browser(firefox): start screencast in existing pages upon setScreencastOptions (#4045)

This makes it work in persistent context.

To achieve this, we have to move screencast logic into PageTarget and
make PageHandler listen to an event.
This commit is contained in:
Dmitry Gozman 2020-10-02 17:16:49 -07:00 committed by GitHub
parent 1f16ce2632
commit 133de10a47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 65 additions and 46 deletions

View File

@ -1,2 +1,2 @@
1179
Changed: lushnikov@chromium.org Fri Oct 2 03:14:15 PDT 2020
1180
Changed: dgozman@gmail.com Fri Oct 2 09:36:06 PDT 2020

View File

@ -10,6 +10,7 @@ const {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm
const {ContextualIdentityService} = ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm");
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
const helper = new Helper();
@ -350,6 +351,7 @@ class PageTarget {
this._openerId = opener ? opener.id() : undefined;
this._channel = SimpleChannel.createForMessageManager(`browser::page[${this._targetId}]`, this._linkedBrowser.messageManager);
this._channelIds = new Set();
this._screencastInfo = undefined;
const navigationListener = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
@ -454,6 +456,50 @@ class PageTarget {
return await this._channel.connect('').send('hasFailedToOverrideTimezone').catch(e => true);
}
async startVideoRecording({width, height, scale, dir}) {
// On Mac the window may not yet be visible when TargetCreated and its
// NSWindow.windowNumber may be -1, so we wait until the window is known
// to be initialized and visible.
await this.windowReady();
const file = OS.Path.join(dir, helper.generateId() + '.webm');
if (width < 10 || width > 10000 || height < 10 || height > 10000)
throw new Error("Invalid size");
if (scale && (scale <= 0 || scale > 1))
throw new Error("Unsupported scale");
const screencast = Cc['@mozilla.org/juggler/screencast;1'].getService(Ci.nsIScreencastService);
const docShell = this._gBrowser.ownerGlobal.docShell;
// Exclude address bar and navigation control from the video.
const rect = this.linkedBrowser().getBoundingClientRect();
const devicePixelRatio = this._window.devicePixelRatio;
const videoSessionId = screencast.startVideoRecording(docShell, file, width, height, scale || 0, devicePixelRatio * rect.top);
this._screencastInfo = { videoSessionId, file };
this.emit('screencastStarted');
}
async stopVideoRecording() {
if (!this._screencastInfo)
throw new Error('No video recording in progress');
const screencastInfo = this._screencastInfo;
this._screencastInfo = undefined;
const screencast = Cc['@mozilla.org/juggler/screencast;1'].getService(Ci.nsIScreencastService);
const result = new Promise(resolve =>
Services.obs.addObserver(function onStopped(subject, topic, data) {
if (screencastInfo.videoSessionId != data)
return;
Services.obs.removeObserver(onStopped, 'juggler-screencast-stopped');
resolve();
}, 'juggler-screencast-stopped')
);
screencast.stopVideoRecording(screencastInfo.videoSessionId);
return result;
}
screencastInfo() {
return this._screencastInfo;
}
dispose() {
this._disposed = true;
this._browserContext.pages.delete(this);
@ -673,8 +719,14 @@ class BrowserContext {
return result;
}
setScreencastOptions(options) {
async setScreencastOptions(options) {
this.screencastOptions = options;
if (!options)
return;
const promises = [];
for (const page of this.pages)
promises.push(page.startVideoRecording(options));
await Promise.all(promises);
}
}

View File

@ -6,7 +6,6 @@
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
const Cc = Components.classes;
const Ci = Components.interfaces;
@ -91,7 +90,6 @@ class PageHandler {
this._dialogs = new Map();
this._enabled = false;
this._videoSessionId = -1;
}
_onWorkerCreated({workerId, frameId, url}) {
@ -132,24 +130,23 @@ class PageHandler {
helper.on(this._pageTarget, 'crashed', () => {
this._session.emitEvent('Page.crashed', {});
}),
helper.on(this._pageTarget, 'screencastStarted', () => {
const info = this._pageTarget.screencastInfo();
this._session.emitEvent('Page.screencastStarted', { screencastId: '' + info.videoSessionId, file: info.file });
}),
]);
const options = this._pageTarget.browserContext().screencastOptions;
if (options) {
const file = OS.Path.join(options.dir, helper.generateId() + '.webm');
// On Mac the window may not yet be visible when TargetCreated and its
// NSWindow.windowNumber may be -1, so we wait until the window is known
// to be initialized and visible.
await this._pageTarget.windowReady();
await this.startVideoRecording(Object.assign({file}, options));
}
if (options)
await this._pageTarget.startVideoRecording(options);
}
async dispose() {
this._contentPage.dispose();
helper.removeListeners(this._eventListeners);
if (this._videoSessionId !== -1)
await this.stopVideoRecording().catch(e => dump(`stopVideoRecording failed:\n${e}\n`));
if (this._pageTarget.screencastInfo())
await this._pageTarget.stopVideoRecording().catch(e => dump(`stopVideoRecording failed:\n${e}\n`));
}
async setViewportSize({viewportSize}) {
@ -303,38 +300,8 @@ class PageHandler {
return await worker.sendMessage(JSON.parse(message));
}
startVideoRecording({file, width, height, scale}) {
if (width < 10 || width > 10000 || height < 10 || height > 10000)
throw new Error("Invalid size");
if (scale && (scale <= 0 || scale > 1))
throw new Error("Unsupported scale");
const screencast = Cc['@mozilla.org/juggler/screencast;1'].getService(Ci.nsIScreencastService);
const docShell = this._pageTarget._gBrowser.ownerGlobal.docShell;
// Exclude address bar and navigation control from the video.
const rect = this._pageTarget.linkedBrowser().getBoundingClientRect();
const devicePixelRatio = this._pageTarget._window.devicePixelRatio;
this._videoSessionId = screencast.startVideoRecording(docShell, file, width, height, scale || 0, devicePixelRatio * rect.top);
this._session.emitEvent('Page.screencastStarted', {screencastId: '' + this._videoSessionId, file});
}
async stopVideoRecording() {
if (this._videoSessionId === -1)
throw new Error('No video recording in progress');
const videoSessionId = this._videoSessionId;
this._videoSessionId = -1;
const screencast = Cc['@mozilla.org/juggler/screencast;1'].getService(Ci.nsIScreencastService);
const result = new Promise(resolve =>
Services.obs.addObserver(function onStopped(subject, topic, data) {
if (videoSessionId != data)
return;
Services.obs.removeObserver(onStopped, 'juggler-screencast-stopped');
resolve();
}, 'juggler-screencast-stopped')
);
screencast.stopVideoRecording(videoSessionId);
return result;
await this._pageTarget.stopVideoRecording();
}
}