mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
browser(firefox-stable): cherry pick recent changes from browser_patches/firefox (#6409)
This commit is contained in:
parent
14ebcfdf0b
commit
5c5196104e
@ -1,2 +1,2 @@
|
|||||||
1244
|
1245
|
||||||
Changed: lushnikov@chromium.org Tue 20 Apr 2021 04:48:52 PM PDT
|
Changed: dgozman@gmail.com Tue May 4 16:09:13 PDT 2021
|
||||||
|
@ -504,7 +504,8 @@ class PageTarget {
|
|||||||
// Exclude address bar and navigation control from the video.
|
// Exclude address bar and navigation control from the video.
|
||||||
const rect = this.linkedBrowser().getBoundingClientRect();
|
const rect = this.linkedBrowser().getBoundingClientRect();
|
||||||
const devicePixelRatio = this._window.devicePixelRatio;
|
const devicePixelRatio = this._window.devicePixelRatio;
|
||||||
const videoSessionId = screencast.startVideoRecording(docShell, file, width, height, scale || 0, devicePixelRatio * rect.top);
|
const viewport = this._viewportSize || this._browserContext.defaultViewportSize || {width: 0, height: 0};
|
||||||
|
const videoSessionId = screencast.startVideoRecording(docShell, file, width, height, viewport.width, viewport.height, scale || 0, devicePixelRatio * rect.top);
|
||||||
this._screencastInfo = { videoSessionId, file };
|
this._screencastInfo = { videoSessionId, file };
|
||||||
this.emit(PageTarget.Events.ScreencastStarted);
|
this.emit(PageTarget.Events.ScreencastStarted);
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ class FrameTree {
|
|||||||
this._browsingContextGroup.__jugglerFrameTrees = new Set();
|
this._browsingContextGroup.__jugglerFrameTrees = new Set();
|
||||||
this._browsingContextGroup.__jugglerFrameTrees.add(this);
|
this._browsingContextGroup.__jugglerFrameTrees.add(this);
|
||||||
this._scriptsToEvaluateOnNewDocument = new Map();
|
this._scriptsToEvaluateOnNewDocument = new Map();
|
||||||
|
this._isolatedWorlds = new Map();
|
||||||
|
|
||||||
this._webSocketEventService = Cc[
|
this._webSocketEventService = Cc[
|
||||||
"@mozilla.org/websocketevent/service;1"
|
"@mozilla.org/websocketevent/service;1"
|
||||||
@ -72,6 +73,25 @@ class FrameTree {
|
|||||||
return this._runtime;
|
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) {
|
_frameForWorker(workerDebugger) {
|
||||||
if (workerDebugger.type !== Ci.nsIWorkerDebugger.TYPE_DEDICATED)
|
if (workerDebugger.type !== Ci.nsIWorkerDebugger.TYPE_DEDICATED)
|
||||||
return null;
|
return null;
|
||||||
@ -86,7 +106,6 @@ class FrameTree {
|
|||||||
if (!frame)
|
if (!frame)
|
||||||
return;
|
return;
|
||||||
frame._onGlobalObjectCleared();
|
frame._onGlobalObjectCleared();
|
||||||
this.emit(FrameTree.Events.GlobalObjectCreated, { frame, window });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onWorkerCreated(workerDebugger) {
|
_onWorkerCreated(workerDebugger) {
|
||||||
@ -129,16 +148,6 @@ class FrameTree {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
addScriptToEvaluateOnNewDocument(script) {
|
|
||||||
const scriptId = helper.generateId();
|
|
||||||
this._scriptsToEvaluateOnNewDocument.set(scriptId, script);
|
|
||||||
return scriptId;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeScriptToEvaluateOnNewDocument(scriptId) {
|
|
||||||
this._scriptsToEvaluateOnNewDocument.delete(scriptId);
|
|
||||||
}
|
|
||||||
|
|
||||||
addBinding(name, script) {
|
addBinding(name, script) {
|
||||||
this._bindings.set(name, script);
|
this._bindings.set(name, script);
|
||||||
for (const frame of this.frames())
|
for (const frame of this.frames())
|
||||||
@ -291,7 +300,6 @@ FrameTree.Events = {
|
|||||||
BindingCalled: 'bindingcalled',
|
BindingCalled: 'bindingcalled',
|
||||||
FrameAttached: 'frameattached',
|
FrameAttached: 'frameattached',
|
||||||
FrameDetached: 'framedetached',
|
FrameDetached: 'framedetached',
|
||||||
GlobalObjectCreated: 'globalobjectcreated',
|
|
||||||
WorkerCreated: 'workercreated',
|
WorkerCreated: 'workercreated',
|
||||||
WorkerDestroyed: 'workerdestroyed',
|
WorkerDestroyed: 'workerdestroyed',
|
||||||
WebSocketCreated: 'websocketcreated',
|
WebSocketCreated: 'websocketcreated',
|
||||||
@ -330,6 +338,9 @@ class Frame {
|
|||||||
this._textInputProcessor = null;
|
this._textInputProcessor = null;
|
||||||
this._executionContext = null;
|
this._executionContext = null;
|
||||||
|
|
||||||
|
this._isolatedWorlds = new Map();
|
||||||
|
this._initialNavigationDone = false;
|
||||||
|
|
||||||
this._webSocketListenerInnerWindowId = 0;
|
this._webSocketListenerInnerWindowId = 0;
|
||||||
// WebSocketListener calls frameReceived event before webSocketOpened.
|
// WebSocketListener calls frameReceived event before webSocketOpened.
|
||||||
// To avoid this, serialize event reporting.
|
// To avoid this, serialize event reporting.
|
||||||
@ -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() {
|
dispose() {
|
||||||
|
for (const world of this._isolatedWorlds.values())
|
||||||
|
this._runtime.destroyExecutionContext(world);
|
||||||
|
this._isolatedWorlds.clear();
|
||||||
if (this._executionContext)
|
if (this._executionContext)
|
||||||
this._runtime.destroyExecutionContext(this._executionContext);
|
this._runtime.destroyExecutionContext(this._executionContext);
|
||||||
this._executionContext = null;
|
this._executionContext = null;
|
||||||
@ -458,6 +498,19 @@ class Frame {
|
|||||||
dump(`ERROR: ${e.message}\n${e.stack}\n`);
|
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() {
|
executionContext() {
|
||||||
|
@ -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 {
|
class PageAgent {
|
||||||
constructor(messageManager, browserChannel, frameTree) {
|
constructor(messageManager, browserChannel, frameTree) {
|
||||||
this._messageManager = messageManager;
|
this._messageManager = messageManager;
|
||||||
@ -116,10 +57,7 @@ class PageAgent {
|
|||||||
this._frameTree = frameTree;
|
this._frameTree = frameTree;
|
||||||
this._runtime = frameTree.runtime();
|
this._runtime = frameTree.runtime();
|
||||||
|
|
||||||
this._frameData = new Map();
|
|
||||||
this._workerData = new Map();
|
this._workerData = new Map();
|
||||||
this._scriptsToEvaluateOnNewDocument = new Map();
|
|
||||||
this._isolatedWorlds = new Map();
|
|
||||||
|
|
||||||
const docShell = frameTree.mainFrame().docShell();
|
const docShell = frameTree.mainFrame().docShell();
|
||||||
this._docShell = docShell;
|
this._docShell = docShell;
|
||||||
@ -169,7 +107,6 @@ class PageAgent {
|
|||||||
helper.on(this._frameTree, 'bindingcalled', this._onBindingCalled.bind(this)),
|
helper.on(this._frameTree, 'bindingcalled', this._onBindingCalled.bind(this)),
|
||||||
helper.on(this._frameTree, 'frameattached', this._onFrameAttached.bind(this)),
|
helper.on(this._frameTree, 'frameattached', this._onFrameAttached.bind(this)),
|
||||||
helper.on(this._frameTree, 'framedetached', this._onFrameDetached.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, 'navigationstarted', this._onNavigationStarted.bind(this)),
|
||||||
helper.on(this._frameTree, 'navigationcommitted', this._onNavigationCommitted.bind(this)),
|
helper.on(this._frameTree, 'navigationcommitted', this._onNavigationCommitted.bind(this)),
|
||||||
helper.on(this._frameTree, 'navigationaborted', this._onNavigationAborted.bind(this)),
|
helper.on(this._frameTree, 'navigationaborted', this._onNavigationAborted.bind(this)),
|
||||||
@ -198,7 +135,7 @@ class PageAgent {
|
|||||||
this._runtime.events.onExecutionContextDestroyed(this._onExecutionContextDestroyed.bind(this)),
|
this._runtime.events.onExecutionContextDestroyed(this._onExecutionContextDestroyed.bind(this)),
|
||||||
browserChannel.register('page', {
|
browserChannel.register('page', {
|
||||||
addBinding: ({ name, script }) => this._frameTree.addBinding(name, script),
|
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),
|
adoptNode: this._adoptNode.bind(this),
|
||||||
crash: this._crash.bind(this),
|
crash: this._crash.bind(this),
|
||||||
describeNode: this._describeNode.bind(this),
|
describeNode: this._describeNode.bind(this),
|
||||||
@ -213,7 +150,7 @@ class PageAgent {
|
|||||||
insertText: this._insertText.bind(this),
|
insertText: this._insertText.bind(this),
|
||||||
navigate: this._navigate.bind(this),
|
navigate: this._navigate.bind(this),
|
||||||
reload: this._reload.bind(this),
|
reload: this._reload.bind(this),
|
||||||
removeScriptToEvaluateOnNewDocument: this._removeScriptToEvaluateOnNewDocument.bind(this),
|
removeScriptToEvaluateOnNewDocument: ({scriptId}) => this._frameTree.removeScriptToEvaluateOnNewDocument(scriptId),
|
||||||
screenshot: this._screenshot.bind(this),
|
screenshot: this._screenshot.bind(this),
|
||||||
scrollIntoViewIfNeeded: this._scrollIntoViewIfNeeded.bind(this),
|
scrollIntoViewIfNeeded: this._scrollIntoViewIfNeeded.bind(this),
|
||||||
setCacheDisabled: this._setCacheDisabled.bind(this),
|
setCacheDisabled: this._setCacheDisabled.bind(this),
|
||||||
@ -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}) {
|
_setCacheDisabled({cacheDisabled}) {
|
||||||
const enable = Ci.nsIRequest.LOAD_NORMAL;
|
const enable = Ci.nsIRequest.LOAD_NORMAL;
|
||||||
const disable = Ci.nsIRequest.LOAD_BYPASS_CACHE |
|
const disable = Ci.nsIRequest.LOAD_BYPASS_CACHE |
|
||||||
@ -333,16 +249,16 @@ class PageAgent {
|
|||||||
_filePickerShown(inputElement) {
|
_filePickerShown(inputElement) {
|
||||||
if (inputElement.ownerGlobal.docShell !== this._docShell)
|
if (inputElement.ownerGlobal.docShell !== this._docShell)
|
||||||
return;
|
return;
|
||||||
const frameData = this._findFrameForNode(inputElement);
|
const frame = this._findFrameForNode(inputElement);
|
||||||
this._browserPage.emit('pageFileChooserOpened', {
|
this._browserPage.emit('pageFileChooserOpened', {
|
||||||
executionContextId: frameData._frame.executionContext().id(),
|
executionContextId: frame.executionContext().id(),
|
||||||
element: frameData._frame.executionContext().rawValueToRemoteObject(inputElement)
|
element: frame.executionContext().rawValueToRemoteObject(inputElement)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_findFrameForNode(node) {
|
_findFrameForNode(node) {
|
||||||
return Array.from(this._frameData.values()).find(data => {
|
return this._frameTree.frames().find(frame => {
|
||||||
const doc = data._frame.domWindow().document;
|
const doc = frame.domWindow().document;
|
||||||
return node === doc || node.ownerDocument === doc;
|
return node === doc || node.ownerDocument === doc;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -404,10 +320,9 @@ class PageAgent {
|
|||||||
navigationId,
|
navigationId,
|
||||||
errorText,
|
errorText,
|
||||||
});
|
});
|
||||||
const frameData = this._frameData.get(frame);
|
if (!frame._initialNavigationDone && frame !== this._frameTree.mainFrame())
|
||||||
if (!frameData._initialNavigationDone && frame !== this._frameTree.mainFrame())
|
|
||||||
this._emitAllEvents(frame);
|
this._emitAllEvents(frame);
|
||||||
frameData._initialNavigationDone = true;
|
frame._initialNavigationDone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onSameDocumentNavigation(frame) {
|
_onSameDocumentNavigation(frame) {
|
||||||
@ -424,11 +339,7 @@ class PageAgent {
|
|||||||
url: frame.url(),
|
url: frame.url(),
|
||||||
name: frame.name(),
|
name: frame.name(),
|
||||||
});
|
});
|
||||||
this._frameData.get(frame)._initialNavigationDone = true;
|
frame._initialNavigationDone = true;
|
||||||
}
|
|
||||||
|
|
||||||
_onGlobalObjectCreated({ frame }) {
|
|
||||||
this._frameData.get(frame).reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onFrameAttached(frame) {
|
_onFrameAttached(frame) {
|
||||||
@ -436,11 +347,9 @@ class PageAgent {
|
|||||||
frameId: frame.id(),
|
frameId: frame.id(),
|
||||||
parentFrameId: frame.parentFrame() ? frame.parentFrame().id() : undefined,
|
parentFrameId: frame.parentFrame() ? frame.parentFrame().id() : undefined,
|
||||||
});
|
});
|
||||||
this._frameData.set(frame, new FrameData(this, this._runtime, frame));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onFrameDetached(frame) {
|
_onFrameDetached(frame) {
|
||||||
this._frameData.delete(frame);
|
|
||||||
this._browserPage.emit('pageFrameDetached', {
|
this._browserPage.emit('pageFrameDetached', {
|
||||||
frameId: frame.id(),
|
frameId: frame.id(),
|
||||||
});
|
});
|
||||||
@ -458,9 +367,6 @@ class PageAgent {
|
|||||||
for (const workerData of this._workerData.values())
|
for (const workerData of this._workerData.values())
|
||||||
workerData.dispose();
|
workerData.dispose();
|
||||||
this._workerData.clear();
|
this._workerData.clear();
|
||||||
for (const frameData of this._frameData.values())
|
|
||||||
frameData.dispose();
|
|
||||||
this._frameData.clear();
|
|
||||||
helper.removeListeners(this._eventListeners);
|
helper.removeListeners(this._eventListeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,7 +431,7 @@ class PageAgent {
|
|||||||
const frame = this._frameTree.frame(frameId);
|
const frame = this._frameTree.frame(frameId);
|
||||||
if (!frame)
|
if (!frame)
|
||||||
throw new Error('Failed to find frame with id = ' + frameId);
|
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 context = this._runtime.findExecutionContext(executionContextId);
|
||||||
const fromPrincipal = unsafeObject.nodePrincipal;
|
const fromPrincipal = unsafeObject.nodePrincipal;
|
||||||
const toFrame = this._frameTree.frame(context.auxData().frameId);
|
const toFrame = this._frameTree.frame(context.auxData().frameId);
|
||||||
@ -539,7 +445,7 @@ class PageAgent {
|
|||||||
const frame = this._frameTree.frame(frameId);
|
const frame = this._frameTree.frame(frameId);
|
||||||
if (!frame)
|
if (!frame)
|
||||||
throw new Error('Failed to find frame with id = ' + frameId);
|
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)
|
if (!unsafeObject)
|
||||||
throw new Error('Object is not input!');
|
throw new Error('Object is not input!');
|
||||||
const nsFiles = await Promise.all(files.map(filePath => File.createFromFileName(filePath)));
|
const nsFiles = await Promise.all(files.map(filePath => File.createFromFileName(filePath)));
|
||||||
@ -550,7 +456,7 @@ class PageAgent {
|
|||||||
const frame = this._frameTree.frame(frameId);
|
const frame = this._frameTree.frame(frameId);
|
||||||
if (!frame)
|
if (!frame)
|
||||||
throw new Error('Failed to find frame with id = ' + frameId);
|
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)
|
if (!unsafeObject.getBoxQuads)
|
||||||
throw new Error('RemoteObject is not a node');
|
throw new Error('RemoteObject is not a node');
|
||||||
const quads = unsafeObject.getBoxQuads({relativeTo: this._frameTree.mainFrame().domWindow().document}).map(quad => {
|
const quads = unsafeObject.getBoxQuads({relativeTo: this._frameTree.mainFrame().domWindow().document}).map(quad => {
|
||||||
@ -568,7 +474,7 @@ class PageAgent {
|
|||||||
const frame = this._frameTree.frame(frameId);
|
const frame = this._frameTree.frame(frameId);
|
||||||
if (!frame)
|
if (!frame)
|
||||||
throw new Error('Failed to find frame with id = ' + frameId);
|
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 browsingContextGroup = frame.docShell().browsingContext.group;
|
||||||
const frames = this._frameTree.allFramesInBrowsingContextGroup(browsingContextGroup);
|
const frames = this._frameTree.allFramesInBrowsingContextGroup(browsingContextGroup);
|
||||||
let contentFrame;
|
let contentFrame;
|
||||||
@ -590,7 +496,7 @@ class PageAgent {
|
|||||||
const frame = this._frameTree.frame(frameId);
|
const frame = this._frameTree.frame(frameId);
|
||||||
if (!frame)
|
if (!frame)
|
||||||
throw new Error('Failed to find frame with id = ' + frameId);
|
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)
|
if (!unsafeObject.isConnected)
|
||||||
throw new Error('Node is detached from document');
|
throw new Error('Node is detached from document');
|
||||||
if (!rect)
|
if (!rect)
|
||||||
@ -877,7 +783,7 @@ class PageAgent {
|
|||||||
async _getFullAXTree({objectId}) {
|
async _getFullAXTree({objectId}) {
|
||||||
let unsafeObject = null;
|
let unsafeObject = null;
|
||||||
if (objectId) {
|
if (objectId) {
|
||||||
unsafeObject = this._frameData.get(this._frameTree.mainFrame()).unsafeObject(objectId);
|
unsafeObject = this._frameTree.mainFrame().unsafeObject(objectId);
|
||||||
if (!unsafeObject)
|
if (!unsafeObject)
|
||||||
throw new Error(`No object found for id "${objectId}"`);
|
throw new Error(`No object found for id "${objectId}"`);
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,12 @@ class BrowserHandler {
|
|||||||
"navigator:browser"
|
"navigator:browser"
|
||||||
);
|
);
|
||||||
if (browserWindow && browserWindow.gBrowserInit) {
|
if (browserWindow && browserWindow.gBrowserInit) {
|
||||||
await browserWindow.gBrowserInit.idleTasksFinishedPromise;
|
// idleTasksFinishedPromise does not resolve when the window
|
||||||
|
// is closed early enough, so we race against window closure.
|
||||||
|
await Promise.race([
|
||||||
|
browserWindow.gBrowserInit.idleTasksFinishedPromise,
|
||||||
|
waitForWindowClosed(browserWindow),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
// Try to fully initialize browser before closing.
|
// Try to fully initialize browser before closing.
|
||||||
// See comment in `Browser.enable`.
|
// See comment in `Browser.enable`.
|
||||||
@ -281,6 +286,22 @@ async function waitForAddonManager() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function waitForWindowClosed(browserWindow) {
|
||||||
|
if (browserWindow.closed)
|
||||||
|
return;
|
||||||
|
await new Promise((resolve => {
|
||||||
|
const listener = {
|
||||||
|
onCloseWindow: window => {
|
||||||
|
if (window === browserWindow) {
|
||||||
|
Services.wm.removeListener(listener);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Services.wm.addListener(listener);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
function nullToUndefined(value) {
|
function nullToUndefined(value) {
|
||||||
return value === null ? undefined : value;
|
return value === null ? undefined : value;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ interface nsIDocShell;
|
|||||||
[scriptable, uuid(d8c4d9e0-9462-445e-9e43-68d3872ad1de)]
|
[scriptable, uuid(d8c4d9e0-9462-445e-9e43-68d3872ad1de)]
|
||||||
interface nsIScreencastService : nsISupports
|
interface nsIScreencastService : nsISupports
|
||||||
{
|
{
|
||||||
AString startVideoRecording(in nsIDocShell docShell, in ACString fileName, in uint32_t width, in uint32_t height, in double scale, in int32_t offset_top);
|
AString startVideoRecording(in nsIDocShell docShell, in ACString fileName, in uint32_t width, in uint32_t height, in uint32_t viewportWidth, in uint32_t viewportHeight, in double scale, in int32_t offset_top);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will emit 'juggler-screencast-stopped' when the video file is saved.
|
* Will emit 'juggler-screencast-stopped' when the video file is saved.
|
||||||
|
@ -140,7 +140,7 @@ nsScreencastService::nsScreencastService() = default;
|
|||||||
nsScreencastService::~nsScreencastService() {
|
nsScreencastService::~nsScreencastService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult nsScreencastService::StartVideoRecording(nsIDocShell* aDocShell, const nsACString& aFileName, uint32_t width, uint32_t height, double scale, int32_t offsetTop, nsAString& sessionId) {
|
nsresult nsScreencastService::StartVideoRecording(nsIDocShell* aDocShell, const nsACString& aFileName, uint32_t width, uint32_t height, uint32_t viewportWidth, uint32_t viewportHeight, double scale, int32_t offsetTop, nsAString& sessionId) {
|
||||||
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "Screencast service must be started on the Main thread.");
|
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "Screencast service must be started on the Main thread.");
|
||||||
|
|
||||||
PresShell* presShell = aDocShell->GetPresShell();
|
PresShell* presShell = aDocShell->GetPresShell();
|
||||||
@ -164,15 +164,13 @@ nsresult nsScreencastService::StartVideoRecording(nsIDocShell* aDocShell, const
|
|||||||
maybeScale = Some(scale);
|
maybeScale = Some(scale);
|
||||||
|
|
||||||
gfx::IntMargin margin;
|
gfx::IntMargin margin;
|
||||||
// On GTK the bottom of the client rect is below the bounds and
|
|
||||||
// client size is actually equal to the size of the bounds so
|
|
||||||
// we don't need an adjustment.
|
|
||||||
#ifndef MOZ_WIDGET_GTK
|
|
||||||
auto bounds = widget->GetScreenBounds().ToUnknownRect();
|
auto bounds = widget->GetScreenBounds().ToUnknownRect();
|
||||||
auto clientBounds = widget->GetClientBounds().ToUnknownRect();
|
auto clientBounds = widget->GetClientBounds().ToUnknownRect();
|
||||||
|
// The browser window has a minimum size, so it might be larger than the viewport size.
|
||||||
|
clientBounds.width = std::min((int)viewportWidth, clientBounds.width);
|
||||||
|
clientBounds.height = std::min((int)viewportHeight, clientBounds.height);
|
||||||
// Crop the image to exclude frame (if any).
|
// Crop the image to exclude frame (if any).
|
||||||
margin = bounds - clientBounds;
|
margin = bounds - clientBounds;
|
||||||
#endif
|
|
||||||
// Crop the image to exclude controls.
|
// Crop the image to exclude controls.
|
||||||
margin.top += offsetTop;
|
margin.top += offsetTop;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user