Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: 7c0df54a0620b167c0f028b36ee28563a9f7897f
      
https://github.com/WebKit/WebKit/commit/7c0df54a0620b167c0f028b36ee28563a9f7897f
  Author: Qianlang Chen <[email protected]>
  Date:   2026-09-25 (Fri, 25 Sep 2026)

  Changed paths:
    A LayoutTests/inspector/script-profiler/tracking-with-samples-expected.txt
    A LayoutTests/inspector/script-profiler/tracking-with-samples.html
    M Source/JavaScriptCore/debugger/Debugger.h
    M Source/JavaScriptCore/inspector/InspectorEnvironment.h
    M Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.cpp
    M Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.h
    M Source/WebCore/bindings/js/ScriptController.cpp
    M Source/WebCore/inspector/FrameDebugger.cpp
    M Source/WebCore/inspector/FrameDebugger.h
    M Source/WebCore/inspector/FrameInspectorController.cpp
    M Source/WebCore/inspector/FrameInspectorController.h
    M Source/WebCore/inspector/PageInspectorController.cpp
    M Source/WebCore/inspector/PageInspectorController.h
    M Source/WebCore/page/LocalFrame.cpp
    M Source/WebCore/page/LocalFrame.h

  Log Message:
  -----------
  [Site Isolation] Web Inspector: JavaScript profiling reports no execution 
events; timeline durations are wrong
rdar://186702735
https://bugs.webkit.org/show_bug.cgi?id=323477

Reviewed by BJ Burg.

With site isolation enabled, a ScriptProfiler tracking session produces
trackingStart and trackingComplete but never a single trackingUpdate.
Two independent defects needed fixing.

InspectorScriptProfilerAgent installs its profiling client on exactly
one debugger, the one InspectorEnvironment::debugger() returns, which
for a page is the PageDebugger. Under site isolation that debugger is
not attached, since per-frame debugging is handled by FrameDebugger
instead. However, ScriptProfilingScope still reads the debugger off the
global object being evaluated (the FrameDebugger under site isolation),
so it never saw a profiling client and trackingUpdate never fired.

(trackingStart/trackingComplete kept working because the agent
dispatches those directly, without consulting a debugger.)

Fix this by adding a forEachDebugger primitive to InspectorEnvironment,
overridden in PageInspectorController to also visit each in-process
FrameDebugger. Leave WorkerInspectorController and
JSGlobalObjectInspectorController unchanged, since each covers exactly
one debugger. A single shared client preserves the pre-frame-target
semantics: one isAlreadyProfiling flag for the whole page and one
sampling profiler per VM, so same-process frames are not double-counted.

Clearing the client walks VM::forEachDebugger, a list every Debugger
adds itself to for exactly its own lifetime, and clears only the ones
whose client is this agent, identified by the new
Debugger::hasProfilingClient overload so that debuggers owned by
another page or JSContext in the same VM are left alone. Clearing
must not depend on the frame tree: a frame can leave it mid-recording
(a removed iframe still kept alive by a reference), and its
FrameDebugger would then keep a raw pointer to an agent that dies with
the page's inspector controller, since ScriptProfilingScope only
rechecks that a debugger has some profiling client, not which one.

The second problem is script run in an isolated world was never
profiled. An isolated world's window proxy is created lazily, after
FrameDebugger has already attached to the proxies that existed at the
time, and ScriptController::initScriptForWindowProxy gives new proxies
Page::debugger(), which is always null under site isolation. That call
site now uses the new LocalFrame::debugger(), which mirrors
Page::debugger() and falls back to it when the frame has none.

FrameDebugger records whether it is attached in
attachDebugger/detachDebugger, and FrameInspectorController exposes
that as attachedDebugger(). LocalFrame::debugger() reads it from there
rather than caching a pointer of its own. That accessor is deliberately
narrower than FrameInspectorController::debugger(), which returns the
frame's debugger even while nothing is debugging the frame: a newly
created global object must only be handed a debugger that is already
attached. Without site isolation no FrameDebugger is ever created, so
this call site is unchanged.

Frame debuggers are created lazily, on the first frontend connection to
that frame target, so a frame that starts being inspected mid-recording
would otherwise go unrecorded for the rest of it. Creating the debugger
now also installs the page's active profiling client if a recording is
already in progress.

(This patch does not fix profiling for frames in other processes: the
page target's agents only run in the process hosting the main frame, so
a cross-origin out-of-process iframe still records nothing. That needs
the ScriptProfiler domain on frame targets, which is part of the
ongoing site isolation work.)

(Two other call sites still hand new global objects Page::debugger():
WindowProxy::setDOMWindow on navigation and
ScriptCachedFrameData::restore on back/forward cache restore. Under site
isolation those global objects are not attached to the frame's debugger
either. Converting them to LocalFrame::debugger() is also tracked
separately, in Bug 325208.)

Test: inspector/script-profiler/tracking-with-samples.html

* LayoutTests/inspector/script-profiler/tracking-with-samples-expected.txt: 
Added.
* LayoutTests/inspector/script-profiler/tracking-with-samples.html: Added.
* Source/JavaScriptCore/debugger/Debugger.h:
(JSC::Debugger::hasProfilingClient const):
* Source/JavaScriptCore/inspector/InspectorEnvironment.h:
(Inspector::InspectorEnvironment::forEachDebugger):
* Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.cpp:
(Inspector::InspectorScriptProfilerAgent::willDestroyFrontendAndBackend):
(Inspector::InspectorScriptProfilerAgent::installProfilingClientOnCoveredDebuggers):
(Inspector::InspectorScriptProfilerAgent::installProfilingClient):
(Inspector::InspectorScriptProfilerAgent::installProfilingClientIfTracking):
(Inspector::InspectorScriptProfilerAgent::clearProfilingClientFromAllDebuggers):
(Inspector::InspectorScriptProfilerAgent::startTracking):
(Inspector::InspectorScriptProfilerAgent::stopTracking):
* Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.h:
* Source/WebCore/bindings/js/ScriptController.cpp:
(WebCore::ScriptController::initScriptForWindowProxy):
* Source/WebCore/inspector/FrameDebugger.cpp:
(WebCore::FrameDebugger::attachDebugger):
(WebCore::FrameDebugger::detachDebugger):
* Source/WebCore/inspector/FrameDebugger.h:
* Source/WebCore/inspector/FrameInspectorController.cpp:
(WebCore::FrameInspectorController::createLazyAgents):
(WebCore::FrameInspectorController::attachedDebugger const):
* Source/WebCore/inspector/FrameInspectorController.h:
* Source/WebCore/inspector/PageInspectorController.cpp:
(WebCore::PageInspectorController::forEachDebugger):
* Source/WebCore/inspector/PageInspectorController.h:
* Source/WebCore/page/LocalFrame.cpp:
(WebCore::LocalFrame::debugger const):
* Source/WebCore/page/LocalFrame.h:

Canonical link: https://commits.webkit.org/321935@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to