diff --git a/packages/react-devtools-extensions/firefox/manifest.json b/packages/react-devtools-extensions/firefox/manifest.json index 038fe627b1bef..de33cac87251e 100644 --- a/packages/react-devtools-extensions/firefox/manifest.json +++ b/packages/react-devtools-extensions/firefox/manifest.json @@ -44,7 +44,7 @@ "scripts": ["build/background.js"] }, - "permissions": ["file:///*", "http://*/*", "https://*/*"], + "permissions": ["file:///*", "http://*/*", "https://*/*", "clipboardWrite"], "content_scripts": [ { diff --git a/packages/react-devtools-extensions/src/injectGlobalHook.js b/packages/react-devtools-extensions/src/injectGlobalHook.js index dd38c8ce1b587..0aaa4810d02db 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/injectGlobalHook.js @@ -97,3 +97,19 @@ if (document.contentType === 'text/html') { detectReact, ); } + +if (typeof exportFunction === 'function') { + // eslint-disable-next-line no-undef + exportFunction( + text => { + // Call clipboard.writeText from the extension content script + // (as it has the clipboardWrite permission) and return a Promise + // accessible to the webpage js code. + return new window.Promise((resolve, reject) => + window.navigator.clipboard.writeText(text).then(resolve, reject), + ); + }, + window.wrappedJSObject.__REACT_DEVTOOLS_GLOBAL_HOOK__, + {defineAs: 'clipboardCopyText'}, + ); +} diff --git a/packages/react-devtools-extensions/src/main.js b/packages/react-devtools-extensions/src/main.js index 98ae0714f0109..0b2f5b1c31cbd 100644 --- a/packages/react-devtools-extensions/src/main.js +++ b/packages/react-devtools-extensions/src/main.js @@ -211,7 +211,6 @@ function createPanelIfReactLoaded() { browserTheme: getBrowserTheme(), componentsPortalContainer, enabledInspectedElementContextMenu: true, - enabledInspectedElementContextMenuCopy: isChrome, overrideTab, profilerPortalContainer, showTabBar: false, diff --git a/packages/react-devtools-shared/src/backend/utils.js b/packages/react-devtools-shared/src/backend/utils.js index 47355f2072a7c..c08daac1d7584 100644 --- a/packages/react-devtools-shared/src/backend/utils.js +++ b/packages/react-devtools-shared/src/backend/utils.js @@ -40,7 +40,19 @@ export function cleanForBridge( export function copyToClipboard(value: any): void { const safeToCopy = serializeToString(value); - copy(safeToCopy === undefined ? 'undefined' : safeToCopy); + const text = safeToCopy === undefined ? 'undefined' : safeToCopy; + const {clipboardCopyText} = window.__REACT_DEVTOOLS_GLOBAL_HOOK__; + + // On Firefox navigator.clipboard.writeText has to be called from + // the content script js code (because it requires the clipboardWrite + // permission to be allowed out of a "user handling" callback), + // clipboardCopyText is an helper injected into the page from. + // injectGlobalHook. + if (typeof clipboardCopyText === 'function') { + clipboardCopyText(text).catch(err => {}); + } else { + copy(text); + } } export function copyWithSet( diff --git a/packages/react-devtools-shared/src/devtools/views/Components/SelectedElement.js b/packages/react-devtools-shared/src/devtools/views/Components/SelectedElement.js index 07f168214c643..055378efca79f 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/SelectedElement.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/SelectedElement.js @@ -303,7 +303,6 @@ function InspectedElementView({ const { isEnabledForInspectedElement, - supportsCopyOperation, viewAttributeSourceFunction, } = useContext(ContextMenuContext); @@ -445,14 +444,12 @@ function InspectedElementView({ {data => ( - {supportsCopyOperation && ( - copyInspectedElementPath(id, data.path)} - title="Copy value to clipboard"> - Copy - value to clipboard - - )} + copyInspectedElementPath(id, data.path)} + title="Copy value to clipboard"> + Copy + value to clipboard + storeAsGlobal(id, data.path)} title="Store as global variable"> diff --git a/packages/react-devtools-shared/src/devtools/views/DevTools.js b/packages/react-devtools-shared/src/devtools/views/DevTools.js index 42e194e70ccfc..f05cdd54e7c19 100644 --- a/packages/react-devtools-shared/src/devtools/views/DevTools.js +++ b/packages/react-devtools-shared/src/devtools/views/DevTools.js @@ -54,7 +54,6 @@ export type Props = {| canViewElementSourceFunction?: ?CanViewElementSource, defaultTab?: TabID, enabledInspectedElementContextMenu?: boolean, - enabledInspectedElementContextMenuCopy?: boolean, showTabBar?: boolean, store: Store, warnIfLegacyBackendDetected?: boolean, @@ -97,7 +96,6 @@ export default function DevTools({ componentsPortalContainer, defaultTab = 'components', enabledInspectedElementContextMenu = false, - enabledInspectedElementContextMenuCopy = false, overrideTab, profilerPortalContainer, showTabBar = false, @@ -123,14 +121,9 @@ export default function DevTools({ const contextMenu = useMemo( () => ({ isEnabledForInspectedElement: enabledInspectedElementContextMenu, - supportsCopyOperation: enabledInspectedElementContextMenuCopy, viewAttributeSourceFunction: viewAttributeSourceFunction || null, }), - [ - enabledInspectedElementContextMenu, - enabledInspectedElementContextMenuCopy, - viewAttributeSourceFunction, - ], + [enabledInspectedElementContextMenu, viewAttributeSourceFunction], ); useEffect( diff --git a/packages/react-devtools-shared/src/devtools/views/context.js b/packages/react-devtools-shared/src/devtools/views/context.js index 92fec05d7d2bf..fcc59935d4551 100644 --- a/packages/react-devtools-shared/src/devtools/views/context.js +++ b/packages/react-devtools-shared/src/devtools/views/context.js @@ -23,13 +23,11 @@ StoreContext.displayName = 'StoreContext'; export type ContextMenuContextType = {| isEnabledForInspectedElement: boolean, - supportsCopyOperation: boolean, viewAttributeSourceFunction?: ?ViewAttributeSource, |}; export const ContextMenuContext = createContext({ isEnabledForInspectedElement: false, - supportsCopyOperation: false, viewAttributeSourceFunction: null, }); ContextMenuContext.displayName = 'ContextMenuContext'; diff --git a/packages/react-devtools-shell/src/devtools.js b/packages/react-devtools-shell/src/devtools.js index 41926dbbca3ba..4879e719c880a 100644 --- a/packages/react-devtools-shell/src/devtools.js +++ b/packages/react-devtools-shell/src/devtools.js @@ -56,7 +56,6 @@ inject('dist/app.js', () => { createElement(DevTools, { browserTheme: 'light', enabledInspectedElementContextMenu: true, - enabledInspectedElementContextMenuCopy: true, showTabBar: true, warnIfLegacyBackendDetected: true, warnIfUnsupportedVersionDetected: true,