Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

browser(firefox): follow-up with assorted simplifications #4066

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
1182
Changed: lushnikov@chromium.org Mon Oct 5 23:55:54 PDT 2020
1183
Changed: lushnikov@chromium.org Tue Oct 6 01:20:41 PDT 2020
26 changes: 2 additions & 24 deletions browser_patches/firefox/juggler/NetworkObserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,36 +50,14 @@ class PageNetwork {
constructor(target) {
EventEmitter.decorate(this);
this._target = target;
this._sessionCount = 0;
this._extraHTTPHeaders = null;
this._responseStorage = null;
this._responseStorage = new ResponseStorage(MAX_RESPONSE_STORAGE_SIZE, MAX_RESPONSE_STORAGE_SIZE / 10);
this._requestInterceptionEnabled = false;
// This is requestId => NetworkRequest map, only contains requests that are
// awaiting interception action (abort, resume, fulfill) over the protocol.
this._interceptedRequests = new Map();
}

addSession() {
if (this._sessionCount === 0)
this._responseStorage = new ResponseStorage(MAX_RESPONSE_STORAGE_SIZE, MAX_RESPONSE_STORAGE_SIZE / 10);
++this._sessionCount;
return () => this._stopTracking();
}

_stopTracking() {
--this._sessionCount;
if (this._sessionCount === 0) {
this._extraHTTPHeaders = null;
this._responseStorage = null;
this._requestInterceptionEnabled = false;
this._interceptedRequests.clear();
}
}

_isActive() {
return this._sessionCount > 0;
}

setExtraHTTPHeaders(headers) {
this._extraHTTPHeaders = headers;
}
Expand Down Expand Up @@ -479,7 +457,7 @@ class NetworkRequest {
}

_activePageNetwork() {
if (!this._maybeInactivePageNetwork || !this._maybeInactivePageNetwork._isActive())
if (!this._maybeInactivePageNetwork)
return undefined;
return this._maybeInactivePageNetwork;
}
Expand Down
12 changes: 5 additions & 7 deletions browser_patches/firefox/juggler/SimpleChannel.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,11 @@ class SimpleChannel {
throw new Error('ERROR: double-register for namespace ' + namespace);
this._handlers.set(namespace, handler);
// Try to re-deliver all pending messages.
Promise.resolve().then(() => {
const bufferedRequests = this._bufferedRequests;
this._bufferedRequests = [];
for (const data of bufferedRequests) {
this._onMessage(data);
}
});
const bufferedRequests = this._bufferedRequests;
this._bufferedRequests = [];
for (const data of bufferedRequests) {
this._onMessage(data);
}
return () => this.unregister(namespace);
}

Expand Down
114 changes: 108 additions & 6 deletions browser_patches/firefox/juggler/TargetRegistry.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class TargetRegistry {
const target = this._browserToTarget.get(browser);
if (!target)
return;
target.emit('crashed');
target.emit(PageTarget.Events.Crashed);
target.dispose();
}
}, 'oop-frameloader-crashed');
Expand Down Expand Up @@ -157,6 +157,8 @@ class TargetRegistry {
target.updateUserAgent();
if (!hasExplicitSize)
target.updateViewportSize();
if (browserContext.screencastOptions)
target._startVideoRecording(browserContext.screencastOptions);
};

const onTabCloseListener = event => {
Expand Down Expand Up @@ -329,13 +331,20 @@ class PageTarget {
this._openerId = opener ? opener.id() : undefined;
this._channel = SimpleChannel.createForMessageManager(`browser::page[${this._targetId}]`, this._linkedBrowser.messageManager);
this._screencastInfo = undefined;
this._dialogs = new Map();

const navigationListener = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
onLocationChange: (aWebProgress, aRequest, aLocation) => this._onNavigated(aLocation),
};
this._eventListeners = [
helper.addProgressListener(tab.linkedBrowser, navigationListener, Ci.nsIWebProgress.NOTIFY_LOCATION),
helper.addEventListener(this._linkedBrowser, 'DOMWillOpenModalDialog', async (event) => {
// wait for the dialog to be actually added to DOM.
await Promise.resolve();
this._updateModalDialogs();
}),
helper.addEventListener(this._linkedBrowser, 'DOMModalDialogClosed', event => this._updateModalDialogs()),
];

this._disposed = false;
Expand All @@ -346,6 +355,14 @@ class PageTarget {
this._registry.emit(TargetRegistry.Events.TargetCreated, this);
}

dialog(dialogId) {
return this._dialogs.get(dialogId);
}

dialogs() {
return [...this._dialogs.values()];
}

async windowReady() {
await waitForWindowReady(this._window);
}
Expand All @@ -362,6 +379,25 @@ class PageTarget {
this._linkedBrowser.browsingContext.customUserAgent = this._browserContext.defaultUserAgent;
}

_updateModalDialogs() {
const prompts = new Set(this._linkedBrowser.tabModalPromptBox ? this._linkedBrowser.tabModalPromptBox.listPrompts() : []);
for (const dialog of this._dialogs.values()) {
if (!prompts.has(dialog.prompt())) {
this._dialogs.delete(dialog.id());
this.emit(PageTarget.Events.DialogClosed, dialog);
} else {
prompts.delete(dialog.prompt());
}
}
for (const prompt of prompts) {
const dialog = Dialog.createIfSupported(prompt);
if (!dialog)
continue;
this._dialogs.set(dialog.id(), dialog);
this.emit(PageTarget.Events.DialogOpened, dialog);
}
}

async updateViewportSize() {
// Viewport size is defined by three arguments:
// 1. default size. Could be explicit if set as part of `window.open` call, e.g.
Expand Down Expand Up @@ -433,7 +469,7 @@ class PageTarget {
return await this._channel.connect('').send('hasFailedToOverrideTimezone').catch(e => true);
}

async startVideoRecording({width, height, scale, dir}) {
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.
Expand All @@ -451,10 +487,10 @@ class PageTarget {
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');
this.emit(PageTarget.Events.ScreencastStarted);
}

async stopVideoRecording() {
async _stopVideoRecording() {
if (!this._screencastInfo)
throw new Error('No video recording in progress');
const screencastInfo = this._screencastInfo;
Expand All @@ -479,6 +515,8 @@ class PageTarget {

dispose() {
this._disposed = true;
if (this._screencastInfo)
this._stopVideoRecording().catch(e => dump(`stopVideoRecording failed:\n${e}\n`));
this._browserContext.pages.delete(this);
this._registry._browserToTarget.delete(this._linkedBrowser);
this._registry._browserBrowsingContextToTarget.delete(this._linkedBrowser.browsingContext);
Expand All @@ -487,6 +525,13 @@ class PageTarget {
}
}

PageTarget.Events = {
ScreencastStarted: Symbol('PageTarget.ScreencastStarted'),
Crashed: Symbol('PageTarget.Crashed'),
DialogOpened: Symbol('PageTarget.DialogOpened'),
DialogClosed: Symbol('PageTarget.DialogClosed'),
};

class BrowserContext {
constructor(registry, browserContextId, removeOnDetach) {
this._registry = registry;
Expand Down Expand Up @@ -702,11 +747,67 @@ class BrowserContext {
return;
const promises = [];
for (const page of this.pages)
promises.push(page.startVideoRecording(options));
promises.push(page._startVideoRecording(options));
await Promise.all(promises);
}
}

class Dialog {
static createIfSupported(prompt) {
const type = prompt.args.promptType;
switch (type) {
case 'alert':
case 'prompt':
case 'confirm':
return new Dialog(prompt, type);
case 'confirmEx':
return new Dialog(prompt, 'beforeunload');
default:
return null;
};
}

constructor(prompt, type) {
this._id = helper.generateId();
this._type = type;
this._prompt = prompt;
}

id() {
return this._id;
}

message() {
return this._prompt.ui.infoBody.textContent;
}

type() {
return this._type;
}

prompt() {
return this._prompt;
}

dismiss() {
if (this._prompt.ui.button1)
this._prompt.ui.button1.click();
else
this._prompt.ui.button0.click();
}

defaultValue() {
return this._prompt.ui.loginTextbox.value;
}

accept(promptValue) {
if (typeof promptValue === 'string' && this._type === 'prompt')
this._prompt.ui.loginTextbox.value = promptValue;
this._prompt.ui.button0.click();
}
}


function dirPath(path) {
return path.substring(0, path.lastIndexOf('/') + 1);
}
Expand Down Expand Up @@ -755,5 +856,6 @@ TargetRegistry.Events = {
DownloadFinished: Symbol('TargetRegistry.Events.DownloadFinished'),
};

var EXPORTED_SYMBOLS = ['TargetRegistry'];
var EXPORTED_SYMBOLS = ['TargetRegistry', 'PageTarget'];
this.TargetRegistry = TargetRegistry;
this.PageTarget = PageTarget;
Loading