Skip to content

Conversation

@mochiya98
Copy link

@mochiya98 mochiya98 commented Nov 25, 2025

Summary

fix #34268, close #29647 (duplicated)

The current implementation uses chrome.devtools.inspectedWindow.eval() , which can be blocked by a page's Content Security Policy (CSP). To handle this, I implemented a fallback mechanism that, if eval fails, attempts to evaluate the code in a Content Script.

Because Content Scripts do not have access to the Developer Tools Console API (e.g., inspect(), $0), some features (e.g. focusing a specified DOM node in the Elements panel) will remain unavailable.
However, this change still improves a situation where developer tools were completely unusable and will make many features available.

How to test

Please follow the reproduction steps from the original issue (#34268) to test behavior.

Note: when you reload the extension, tabs that were already open may not behave correctly. After loading the extension, open a new tab to test.

Detailed flow of operations

sequenceDiagram
    participant DevTools as DevTools Page<br>(main/index.js)
    participant Background as Background Script<br>(background/messageHandlers.js)
    participant CS as Content Script<br>[ExecutionWorld.ISOLATE]<br>(contentScripts/proxy.js)
    participant Page as Content Script<br>[ExecutionWorld.MAIN]<br>(contentScripts/fallbackEvalContext.js)
    
    alt First, attempt the regular processing
        DevTools->>DevTools: chrome.devtools.inspectedWindow.eval()
    else If it fails due to an error, run the fallback process below. (e.g. CSP Blocked)
        
        Note right of DevTools: Message:<br>{ source: 'devtools-page', payload: {<br>type: 'eval-in-inspected-window',<br>tabId, requestId, scriptId, args, } }
        DevTools->>Background: chrome.runtime.sendMessage()
        
        Note right of Background: Message:<br>{source: 'devtools-page-eval',<br>payload: { scriptId, args, } }
        Background->>CS: chrome.tabs.sendMessage()
        
        Note right of CS: Message:<br>{ source: 'react-devtools-<br>content-script-eval',<br>payload: { requestId, scriptId, args, } }
        CS->>Page: window.postMessage()
        
        Note over Page: Eval in Content Script<br>evalScripts[scriptId].apply(null, args);
        
        Note right of CS: Message:<br>{ source: 'react-devtools-<br>content-script-eval-response',<br>payload: { requestId, response, } }
        Page-->>CS: window.postMessage(Response)
        Note right of Background: Message:<br> response ( {result, error} )
        CS-->>Background: sendResponse()
        Note right of DevTools: { source: 'react-devtools-background',<br>payload: { type: 'eval-in-inspected-<br>window-response',<br>requestId, result, error, } }
        Background-->>DevTools: chrome.runtime.sendMessage()
    end
Loading

@meta-cla meta-cla bot added the CLA Signed label Nov 25, 2025
@hoxyq hoxyq self-requested a review November 25, 2025 12:43
Copy link
Collaborator

@eps1lon eps1lon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Overall approach looks sound.

Can you make sure all new files have the license header and @flow marker like the other files?

We should also make sure the script ID in fallbackEvalInInspectedWindow is typed to a literal to avoid typos while also adding runtime type-checking.

I'm leaning towards consolidating the source-as-string and source-as-js in a single file and let the bundler do the rest. That way it's harder to have both drift apart unintentionally. Does that make sense to you?

Comment on lines 150 to 153
if (callback) {
callback(response);
evalRequestCallbacks.delete(requestId);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this error if there's no callback registered with that request ID?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If no callback is provided, do nothing. Logging an error here would show up in the user's page console and could cause unnecessary confusion. This is a rare edge case that shouldn't occur in normal operation, so this is sufficient, I think.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also log errors in the user console for other backend failures so that's not a concern.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Honestly, I still have concerns about logging this to the user's page console, but I respect your opinion and will add the error-logging.

Comment on lines 151 to 152
callback(response);
evalRequestCallbacks.delete(requestId);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe wrap the callback(response) in try-finally so that we don't leak callbacks if they error.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, this is definitely a problem. I’ll fix it. Thank you.

@mochiya98
Copy link
Author

Thank you.

That certainly seems like a good improvement.
I consolidated all eval processing into src/evalScripts.js, updated the callers, and added the license header and Flow definitions.

@mochiya98 mochiya98 requested a review from eps1lon November 27, 2025 08:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DevTools Bug]: devtools does not works on null origin(sandbox) [DevTools Bug]: devtools does not works on null origin(sandbox)

2 participants