Devtools renable copy attr context menu for firefox (#17740)
* Use exportFunction() to share clipboard copy with JS running in document/page context. * Remove no-longer-used option to disable copy operation.
Brian Vaughn committed
Dec 29, 2019 at 13:27 UTC
22ef96ae63f40b1b9367bc2d8bdb3db33e6943b0
8 files changed
+37
-23
packages/react-devtools-extensions/firefox/manifest.json
+1
-1
@@ -44,7 +44,7 @@
44
"scripts": ["build/background.js"]
45
},
46
47
- "permissions": ["file:///*", "http://*/*", "https://*/*"],
47
+ "permissions": ["file:///*", "http://*/*", "https://*/*", "clipboardWrite"],
48
49
"content_scripts": [
50
{
packages/react-devtools-extensions/src/injectGlobalHook.js
+16
@@ -97,3 +97,19 @@ if (document.contentType === 'text/html') {
97
detectReact,
98
);
99
}
100
+
101
+if (typeof exportFunction === 'function') {
102
+ // eslint-disable-next-line no-undef
103
+ exportFunction(
104
+ text => {
105
+ // Call clipboard.writeText from the extension content script
106
+ // (as it has the clipboardWrite permission) and return a Promise
107
+ // accessible to the webpage js code.
108
+ return new window.Promise((resolve, reject) =>
109
+ window.navigator.clipboard.writeText(text).then(resolve, reject),
110
+ );
111
+ },
112
+ window.wrappedJSObject.__REACT_DEVTOOLS_GLOBAL_HOOK__,
113
+ {defineAs: 'clipboardCopyText'},
114
+ );
115
+}
packages/react-devtools-extensions/src/main.js
-1
@@ -211,7 +211,6 @@ function createPanelIfReactLoaded() {
211
browserTheme: getBrowserTheme(),
212
componentsPortalContainer,
213
enabledInspectedElementContextMenu: true,
214
- enabledInspectedElementContextMenuCopy: isChrome,
214
overrideTab,
215
profilerPortalContainer,
216
showTabBar: false,
packages/react-devtools-shared/src/backend/utils.js
+13
-1
@@ -40,7 +40,19 @@ export function cleanForBridge(
40
41
export function copyToClipboard(value: any): void {
42
const safeToCopy = serializeToString(value);
43
- copy(safeToCopy === undefined ? 'undefined' : safeToCopy);
43
+ const text = safeToCopy === undefined ? 'undefined' : safeToCopy;
44
+ const {clipboardCopyText} = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
45
+
46
+ // On Firefox navigator.clipboard.writeText has to be called from
47
+ // the content script js code (because it requires the clipboardWrite
48
+ // permission to be allowed out of a "user handling" callback),
49
+ // clipboardCopyText is an helper injected into the page from.
50
+ // injectGlobalHook.
51
+ if (typeof clipboardCopyText === 'function') {
52
+ clipboardCopyText(text).catch(err => {});
53
+ } else {
54
+ copy(text);
55
+ }
56
}
57
58
export function copyWithSet(
packages/react-devtools-shared/src/devtools/views/Components/SelectedElement.js
+6
-9
@@ -303,7 +303,6 @@ function InspectedElementView({
303
304
const {
305
isEnabledForInspectedElement,
306
- supportsCopyOperation,
306
viewAttributeSourceFunction,
307
} = useContext(ContextMenuContext);
308
@@ -445,14 +444,12 @@ function InspectedElementView({
444
<ContextMenu id="SelectedElement">
445
{data => (
446
<Fragment>
448
- {supportsCopyOperation && (
449
- <ContextMenuItem
450
- onClick={() => copyInspectedElementPath(id, data.path)}
451
- title="Copy value to clipboard">
452
- <Icon className={styles.ContextMenuIcon} type="copy" /> Copy
453
- value to clipboard
454
- </ContextMenuItem>
455
- )}
447
+ <ContextMenuItem
448
+ onClick={() => copyInspectedElementPath(id, data.path)}
449
+ title="Copy value to clipboard">
450
+ <Icon className={styles.ContextMenuIcon} type="copy" /> Copy
451
+ value to clipboard
452
+ </ContextMenuItem>
453
<ContextMenuItem
454
onClick={() => storeAsGlobal(id, data.path)}
455
title="Store as global variable">
packages/react-devtools-shared/src/devtools/views/DevTools.js
+1
-8
@@ -54,7 +54,6 @@ export type Props = {|
54
canViewElementSourceFunction?: ?CanViewElementSource,
55
defaultTab?: TabID,
56
enabledInspectedElementContextMenu?: boolean,
57
- enabledInspectedElementContextMenuCopy?: boolean,
57
showTabBar?: boolean,
58
store: Store,
59
warnIfLegacyBackendDetected?: boolean,
@@ -97,7 +96,6 @@ export default function DevTools({
96
componentsPortalContainer,
97
defaultTab = 'components',
98
enabledInspectedElementContextMenu = false,
100
- enabledInspectedElementContextMenuCopy = false,
99
overrideTab,
100
profilerPortalContainer,
101
showTabBar = false,
@@ -123,14 +121,9 @@ export default function DevTools({
121
const contextMenu = useMemo(
122
() => ({
123
isEnabledForInspectedElement: enabledInspectedElementContextMenu,
126
- supportsCopyOperation: enabledInspectedElementContextMenuCopy,
124
viewAttributeSourceFunction: viewAttributeSourceFunction || null,
125
}),
129
- [
130
- enabledInspectedElementContextMenu,
131
- enabledInspectedElementContextMenuCopy,
132
- viewAttributeSourceFunction,
133
- ],
126
+ [enabledInspectedElementContextMenu, viewAttributeSourceFunction],
127
);
128
129
useEffect(
packages/react-devtools-shared/src/devtools/views/context.js
-2
@@ -23,13 +23,11 @@ StoreContext.displayName = 'StoreContext';
23
24
export type ContextMenuContextType = {|
25
isEnabledForInspectedElement: boolean,
26
- supportsCopyOperation: boolean,
26
viewAttributeSourceFunction?: ?ViewAttributeSource,
27
|};
28
29
export const ContextMenuContext = createContext<ContextMenuContextType>({
30
isEnabledForInspectedElement: false,
32
- supportsCopyOperation: false,
31
viewAttributeSourceFunction: null,
32
});
33
ContextMenuContext.displayName = 'ContextMenuContext';
packages/react-devtools-shell/src/devtools.js
-1
@@ -56,7 +56,6 @@ inject('dist/app.js', () => {
56
createElement(DevTools, {
57
browserTheme: 'light',
58
enabledInspectedElementContextMenu: true,
59
- enabledInspectedElementContextMenuCopy: true,
59
showTabBar: true,
60
warnIfLegacyBackendDetected: true,
61
warnIfUnsupportedVersionDetected: true,