Skip to content

Commit

Permalink
browser(firefox): merge FrameData into Frame (#6365)
Browse files Browse the repository at this point in the history
  • Loading branch information
yury-s committed Apr 29, 2021
1 parent 1c40c94 commit b0fae0f
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 124 deletions.
4 changes: 2 additions & 2 deletions browser_patches/firefox/BUILD_NUMBER
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
1248
Changed: aslushnikov@gmail.com Mon Apr 26 12:48:04 -05 2021
1249
Changed: yurys@chromium.org Thu 29 Apr 2021 02:45:14 PM PDT
77 changes: 65 additions & 12 deletions browser_patches/firefox/juggler/content/FrameTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class FrameTree {
this._browsingContextGroup.__jugglerFrameTrees = new Set();
this._browsingContextGroup.__jugglerFrameTrees.add(this);
this._scriptsToEvaluateOnNewDocument = new Map();
this._isolatedWorlds = new Map();

this._webSocketEventService = Cc[
"@mozilla.org/websocketevent/service;1"
Expand Down Expand Up @@ -72,6 +73,25 @@ class FrameTree {
return this._runtime;
}

addScriptToEvaluateOnNewDocument(script, worldName) {
const scriptId = helper.generateId();
if (worldName) {
this._isolatedWorlds.set(scriptId, {script, worldName});
for (const frame of this.frames())
frame.createIsolatedWorld(worldName);
} else {
this._scriptsToEvaluateOnNewDocument.set(scriptId, script);
}
return {scriptId};
}

removeScriptToEvaluateOnNewDocument(scriptId) {
if (this._isolatedWorlds.has(scriptId))
this._isolatedWorlds.delete(scriptId);
else
this._scriptsToEvaluateOnNewDocument.delete(scriptId);
}

_frameForWorker(workerDebugger) {
if (workerDebugger.type !== Ci.nsIWorkerDebugger.TYPE_DEDICATED)
return null;
Expand All @@ -86,7 +106,6 @@ class FrameTree {
if (!frame)
return;
frame._onGlobalObjectCleared();
this.emit(FrameTree.Events.GlobalObjectCreated, { frame, window });
}

_onWorkerCreated(workerDebugger) {
Expand Down Expand Up @@ -129,16 +148,6 @@ class FrameTree {
return true;
}

addScriptToEvaluateOnNewDocument(script) {
const scriptId = helper.generateId();
this._scriptsToEvaluateOnNewDocument.set(scriptId, script);
return scriptId;
}

removeScriptToEvaluateOnNewDocument(scriptId) {
this._scriptsToEvaluateOnNewDocument.delete(scriptId);
}

addBinding(name, script) {
this._bindings.set(name, script);
for (const frame of this.frames())
Expand Down Expand Up @@ -291,7 +300,6 @@ FrameTree.Events = {
BindingCalled: 'bindingcalled',
FrameAttached: 'frameattached',
FrameDetached: 'framedetached',
GlobalObjectCreated: 'globalobjectcreated',
WorkerCreated: 'workercreated',
WorkerDestroyed: 'workerdestroyed',
WebSocketCreated: 'websocketcreated',
Expand Down Expand Up @@ -330,6 +338,9 @@ class Frame {
this._textInputProcessor = null;
this._executionContext = null;

this._isolatedWorlds = new Map();
this._initialNavigationDone = false;

this._webSocketListenerInnerWindowId = 0;
// WebSocketListener calls frameReceived event before webSocketOpened.
// To avoid this, serialize event reporting.
Expand Down Expand Up @@ -415,7 +426,36 @@ class Frame {
};
}

createIsolatedWorld(name) {
const principal = [this.domWindow()]; // extended principal
const sandbox = Cu.Sandbox(principal, {
sandboxPrototype: this.domWindow(),
wantComponents: false,
wantExportHelpers: false,
wantXrays: true,
});
const world = this._runtime.createExecutionContext(this.domWindow(), sandbox, {
frameId: this.id(),
name,
});
this._isolatedWorlds.set(world.id(), world);
return world;
}

unsafeObject(objectId) {
const contexts = [this.executionContext(), ...this._isolatedWorlds.values()];
for (const context of contexts) {
const result = context.unsafeObject(objectId);
if (result)
return result.object;
}
throw new Error('Cannot find object with id = ' + objectId);
}

dispose() {
for (const world of this._isolatedWorlds.values())
this._runtime.destroyExecutionContext(world);
this._isolatedWorlds.clear();
if (this._executionContext)
this._runtime.destroyExecutionContext(this._executionContext);
this._executionContext = null;
Expand Down Expand Up @@ -458,6 +498,19 @@ class Frame {
dump(`ERROR: ${e.message}\n${e.stack}\n`);
}
}

for (const world of this._isolatedWorlds.values())
this._runtime.destroyExecutionContext(world);
this._isolatedWorlds.clear();
for (const {script, worldName} of this._frameTree._isolatedWorlds.values()) {
const context = worldName ? this.createIsolatedWorld(worldName) : this.executionContext();
try {
let result = context.evaluateScript(script);
if (result && result.objectId)
context.disposeObject(result.objectId);
} catch (e) {
}
}
}

executionContext() {
Expand Down
126 changes: 16 additions & 110 deletions browser_patches/firefox/juggler/content/PageAgent.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,65 +49,6 @@ class WorkerData {
}
}

class FrameData {
constructor(agent, runtime, frame) {
this._agent = agent;
this._runtime = runtime;
this._frame = frame;
this._isolatedWorlds = new Map();
this._initialNavigationDone = false;
this.reset();
}

reset() {
for (const world of this._isolatedWorlds.values())
this._runtime.destroyExecutionContext(world);
this._isolatedWorlds.clear();

for (const {script, worldName} of this._agent._isolatedWorlds.values()) {
const context = worldName ? this.createIsolatedWorld(worldName) : this._frame.executionContext();
try {
let result = context.evaluateScript(script);
if (result && result.objectId)
context.disposeObject(result.objectId);
} catch (e) {
}
}
}

createIsolatedWorld(name) {
const principal = [this._frame.domWindow()]; // extended principal
const sandbox = Cu.Sandbox(principal, {
sandboxPrototype: this._frame.domWindow(),
wantComponents: false,
wantExportHelpers: false,
wantXrays: true,
});
const world = this._runtime.createExecutionContext(this._frame.domWindow(), sandbox, {
frameId: this._frame.id(),
name,
});
this._isolatedWorlds.set(world.id(), world);
return world;
}

unsafeObject(objectId) {
const contexts = [this._frame.executionContext(), ...this._isolatedWorlds.values()];
for (const context of contexts) {
const result = context.unsafeObject(objectId);
if (result)
return result.object;
}
throw new Error('Cannot find object with id = ' + objectId);
}

dispose() {
for (const world of this._isolatedWorlds.values())
this._runtime.destroyExecutionContext(world);
this._isolatedWorlds.clear();
}
}

class PageAgent {
constructor(messageManager, browserChannel, frameTree) {
this._messageManager = messageManager;
Expand All @@ -116,10 +57,7 @@ class PageAgent {
this._frameTree = frameTree;
this._runtime = frameTree.runtime();

this._frameData = new Map();
this._workerData = new Map();
this._scriptsToEvaluateOnNewDocument = new Map();
this._isolatedWorlds = new Map();

const docShell = frameTree.mainFrame().docShell();
this._docShell = docShell;
Expand Down Expand Up @@ -169,7 +107,6 @@ class PageAgent {
helper.on(this._frameTree, 'bindingcalled', this._onBindingCalled.bind(this)),
helper.on(this._frameTree, 'frameattached', this._onFrameAttached.bind(this)),
helper.on(this._frameTree, 'framedetached', this._onFrameDetached.bind(this)),
helper.on(this._frameTree, 'globalobjectcreated', this._onGlobalObjectCreated.bind(this)),
helper.on(this._frameTree, 'navigationstarted', this._onNavigationStarted.bind(this)),
helper.on(this._frameTree, 'navigationcommitted', this._onNavigationCommitted.bind(this)),
helper.on(this._frameTree, 'navigationaborted', this._onNavigationAborted.bind(this)),
Expand Down Expand Up @@ -198,7 +135,7 @@ class PageAgent {
this._runtime.events.onExecutionContextDestroyed(this._onExecutionContextDestroyed.bind(this)),
browserChannel.register('page', {
addBinding: ({ name, script }) => this._frameTree.addBinding(name, script),
addScriptToEvaluateOnNewDocument: this._addScriptToEvaluateOnNewDocument.bind(this),
addScriptToEvaluateOnNewDocument: ({script, worldName}) => this._frameTree.addScriptToEvaluateOnNewDocument(script, worldName),
adoptNode: this._adoptNode.bind(this),
crash: this._crash.bind(this),
describeNode: this._describeNode.bind(this),
Expand All @@ -213,7 +150,7 @@ class PageAgent {
insertText: this._insertText.bind(this),
navigate: this._navigate.bind(this),
reload: this._reload.bind(this),
removeScriptToEvaluateOnNewDocument: this._removeScriptToEvaluateOnNewDocument.bind(this),
removeScriptToEvaluateOnNewDocument: ({scriptId}) => this._frameTree.removeScriptToEvaluateOnNewDocument(scriptId),
screenshot: this._screenshot.bind(this),
scrollIntoViewIfNeeded: this._scrollIntoViewIfNeeded.bind(this),
setCacheDisabled: this._setCacheDisabled.bind(this),
Expand All @@ -227,27 +164,6 @@ class PageAgent {
];
}

_addScriptToEvaluateOnNewDocument({script, worldName}) {
if (worldName)
return this._createIsolatedWorld({script, worldName});
return {scriptId: this._frameTree.addScriptToEvaluateOnNewDocument(script)};
}

_createIsolatedWorld({script, worldName}) {
const scriptId = helper.generateId();
this._isolatedWorlds.set(scriptId, {script, worldName});
for (const frameData of this._frameData.values())
frameData.createIsolatedWorld(worldName);
return {scriptId};
}

_removeScriptToEvaluateOnNewDocument({scriptId}) {
if (this._isolatedWorlds.has(scriptId))
this._isolatedWorlds.delete(scriptId);
else
this._frameTree.removeScriptToEvaluateOnNewDocument(scriptId);
}

_setCacheDisabled({cacheDisabled}) {
const enable = Ci.nsIRequest.LOAD_NORMAL;
const disable = Ci.nsIRequest.LOAD_BYPASS_CACHE |
Expand Down Expand Up @@ -333,16 +249,16 @@ class PageAgent {
_filePickerShown(inputElement) {
if (inputElement.ownerGlobal.docShell !== this._docShell)
return;
const frameData = this._findFrameForNode(inputElement);
const frame = this._findFrameForNode(inputElement);
this._browserPage.emit('pageFileChooserOpened', {
executionContextId: frameData._frame.executionContext().id(),
element: frameData._frame.executionContext().rawValueToRemoteObject(inputElement)
executionContextId: frame.executionContext().id(),
element: frame.executionContext().rawValueToRemoteObject(inputElement)
});
}

_findFrameForNode(node) {
return Array.from(this._frameData.values()).find(data => {
const doc = data._frame.domWindow().document;
return this._frameTree.frames().find(frame => {
const doc = frame.domWindow().document;
return node === doc || node.ownerDocument === doc;
});
}
Expand Down Expand Up @@ -404,10 +320,9 @@ class PageAgent {
navigationId,
errorText,
});
const frameData = this._frameData.get(frame);
if (!frameData._initialNavigationDone && frame !== this._frameTree.mainFrame())
if (!frame._initialNavigationDone && frame !== this._frameTree.mainFrame())
this._emitAllEvents(frame);
frameData._initialNavigationDone = true;
frame._initialNavigationDone = true;
}

_onSameDocumentNavigation(frame) {
Expand All @@ -424,23 +339,17 @@ class PageAgent {
url: frame.url(),
name: frame.name(),
});
this._frameData.get(frame)._initialNavigationDone = true;
}

_onGlobalObjectCreated({ frame }) {
this._frameData.get(frame).reset();
frame._initialNavigationDone = true;
}

_onFrameAttached(frame) {
this._browserPage.emit('pageFrameAttached', {
frameId: frame.id(),
parentFrameId: frame.parentFrame() ? frame.parentFrame().id() : undefined,
});
this._frameData.set(frame, new FrameData(this, this._runtime, frame));
}

_onFrameDetached(frame) {
this._frameData.delete(frame);
this._browserPage.emit('pageFrameDetached', {
frameId: frame.id(),
});
Expand All @@ -458,9 +367,6 @@ class PageAgent {
for (const workerData of this._workerData.values())
workerData.dispose();
this._workerData.clear();
for (const frameData of this._frameData.values())
frameData.dispose();
this._frameData.clear();
helper.removeListeners(this._eventListeners);
}

Expand Down Expand Up @@ -525,7 +431,7 @@ class PageAgent {
const frame = this._frameTree.frame(frameId);
if (!frame)
throw new Error('Failed to find frame with id = ' + frameId);
const unsafeObject = this._frameData.get(frame).unsafeObject(objectId);
const unsafeObject = frame.unsafeObject(objectId);
const context = this._runtime.findExecutionContext(executionContextId);
const fromPrincipal = unsafeObject.nodePrincipal;
const toFrame = this._frameTree.frame(context.auxData().frameId);
Expand All @@ -539,7 +445,7 @@ class PageAgent {
const frame = this._frameTree.frame(frameId);
if (!frame)
throw new Error('Failed to find frame with id = ' + frameId);
const unsafeObject = this._frameData.get(frame).unsafeObject(objectId);
const unsafeObject = frame.unsafeObject(objectId);
if (!unsafeObject)
throw new Error('Object is not input!');
const nsFiles = await Promise.all(files.map(filePath => File.createFromFileName(filePath)));
Expand All @@ -550,7 +456,7 @@ class PageAgent {
const frame = this._frameTree.frame(frameId);
if (!frame)
throw new Error('Failed to find frame with id = ' + frameId);
const unsafeObject = this._frameData.get(frame).unsafeObject(objectId);
const unsafeObject = frame.unsafeObject(objectId);
if (!unsafeObject.getBoxQuads)
throw new Error('RemoteObject is not a node');
const quads = unsafeObject.getBoxQuads({relativeTo: this._frameTree.mainFrame().domWindow().document}).map(quad => {
Expand All @@ -568,7 +474,7 @@ class PageAgent {
const frame = this._frameTree.frame(frameId);
if (!frame)
throw new Error('Failed to find frame with id = ' + frameId);
const unsafeObject = this._frameData.get(frame).unsafeObject(objectId);
const unsafeObject = frame.unsafeObject(objectId);
const browsingContextGroup = frame.docShell().browsingContext.group;
const frames = this._frameTree.allFramesInBrowsingContextGroup(browsingContextGroup);
let contentFrame;
Expand All @@ -590,7 +496,7 @@ class PageAgent {
const frame = this._frameTree.frame(frameId);
if (!frame)
throw new Error('Failed to find frame with id = ' + frameId);
const unsafeObject = this._frameData.get(frame).unsafeObject(objectId);
const unsafeObject = frame.unsafeObject(objectId);
if (!unsafeObject.isConnected)
throw new Error('Node is detached from document');
if (!rect)
Expand Down Expand Up @@ -877,7 +783,7 @@ class PageAgent {
async _getFullAXTree({objectId}) {
let unsafeObject = null;
if (objectId) {
unsafeObject = this._frameData.get(this._frameTree.mainFrame()).unsafeObject(objectId);
unsafeObject = this._frameTree.mainFrame().unsafeObject(objectId);
if (!unsafeObject)
throw new Error(`No object found for id "${objectId}"`);
}
Expand Down

0 comments on commit b0fae0f

Please sign in to comment.