mirror of
https://github.com/microsoft/playwright.git
synced 2025-01-05 19:04:43 +03:00
f3a1058b38
Internal commit reference:
f7e6794188
159 lines
6.4 KiB
JavaScript
159 lines
6.4 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
var EXPORTED_SYMBOLS = ["Juggler", "JugglerFactory"];
|
|
|
|
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
const {ComponentUtils} = ChromeUtils.import("resource://gre/modules/ComponentUtils.jsm");
|
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
const {Dispatcher} = ChromeUtils.import("chrome://juggler/content/protocol/Dispatcher.js");
|
|
const {BrowserHandler} = ChromeUtils.import("chrome://juggler/content/protocol/BrowserHandler.js");
|
|
const {NetworkObserver} = ChromeUtils.import("chrome://juggler/content/NetworkObserver.js");
|
|
const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
|
|
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
|
const {ActorManagerParent} = ChromeUtils.import('resource://gre/modules/ActorManagerParent.jsm');
|
|
const helper = new Helper();
|
|
|
|
const Cc = Components.classes;
|
|
const Ci = Components.interfaces;
|
|
|
|
// Register JSWindowActors that will be instantiated for each frame.
|
|
ActorManagerParent.addJSWindowActors({
|
|
JugglerFrame: {
|
|
parent: {
|
|
moduleURI: 'chrome://juggler/content/JugglerFrameParent.jsm',
|
|
},
|
|
child: {
|
|
moduleURI: 'chrome://juggler/content/content/JugglerFrameChild.jsm',
|
|
events: {
|
|
// Normally, we instantiate an actor when a new window is created.
|
|
DOMWindowCreated: {},
|
|
// However, for same-origin iframes, the navigation from about:blank
|
|
// to the URL will share the same window, so we need to also create
|
|
// an actor for a new document via DOMDocElementInserted.
|
|
DOMDocElementInserted: {},
|
|
// Also, listening to DOMContentLoaded.
|
|
DOMContentLoaded: {},
|
|
DOMWillOpenModalDialog: {},
|
|
DOMModalDialogClosed: {},
|
|
},
|
|
},
|
|
allFrames: true,
|
|
},
|
|
});
|
|
|
|
let browserStartupFinishedCallback;
|
|
let browserStartupFinishedPromise = new Promise(x => browserStartupFinishedCallback = x);
|
|
|
|
class Juggler {
|
|
get classDescription() { return "Sample command-line handler"; }
|
|
get classID() { return Components.ID('{f7a74a33-e2ab-422d-b022-4fb213dd2639}'); }
|
|
get contractID() { return "@mozilla.org/remote/juggler;1" }
|
|
get QueryInterface() {
|
|
return ChromeUtils.generateQI([ Ci.nsICommandLineHandler, Ci.nsIObserver ]);
|
|
}
|
|
get helpInfo() {
|
|
return " --juggler Enable Juggler automation\n";
|
|
}
|
|
|
|
handle(cmdLine) {
|
|
// flag has to be consumed in nsICommandLineHandler:handle
|
|
// to avoid issues on macos. See Marionette.jsm::handle() for more details.
|
|
// TODO: remove after Bug 1724251 is fixed.
|
|
cmdLine.handleFlag("juggler-pipe", false);
|
|
}
|
|
|
|
// This flow is taken from Remote agent and Marionette.
|
|
// See https://github.com/mozilla/gecko-dev/blob/0c1b4921830e6af8bc951da01d7772de2fe60a08/remote/components/RemoteAgent.jsm#L302
|
|
async observe(subject, topic) {
|
|
switch (topic) {
|
|
case "profile-after-change":
|
|
Services.obs.addObserver(this, "command-line-startup");
|
|
Services.obs.addObserver(this, "browser-idle-startup-tasks-finished");
|
|
break;
|
|
case "command-line-startup":
|
|
Services.obs.removeObserver(this, topic);
|
|
const cmdLine = subject;
|
|
const jugglerPipeFlag = cmdLine.handleFlag('juggler-pipe', false);
|
|
if (!jugglerPipeFlag)
|
|
return;
|
|
|
|
this._silent = cmdLine.findFlag('silent', false) >= 0;
|
|
if (this._silent) {
|
|
Services.startup.enterLastWindowClosingSurvivalArea();
|
|
browserStartupFinishedCallback();
|
|
}
|
|
Services.obs.addObserver(this, "final-ui-startup");
|
|
break;
|
|
case "browser-idle-startup-tasks-finished":
|
|
browserStartupFinishedCallback();
|
|
break;
|
|
// Used to wait until the initial application window has been opened.
|
|
case "final-ui-startup":
|
|
Services.obs.removeObserver(this, topic);
|
|
|
|
const targetRegistry = new TargetRegistry();
|
|
new NetworkObserver(targetRegistry);
|
|
|
|
const loadStyleSheet = () => {
|
|
if (Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).isHeadless) {
|
|
const styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Components.interfaces.nsIStyleSheetService);
|
|
const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
|
|
const uri = ioService.newURI('chrome://juggler/content/content/hidden-scrollbars.css', null, null);
|
|
styleSheetService.loadAndRegisterSheet(uri, styleSheetService.AGENT_SHEET);
|
|
}
|
|
};
|
|
|
|
// Force create hidden window here, otherwise its creation later closes the web socket!
|
|
Services.appShell.hiddenDOMWindow;
|
|
|
|
let pipeStopped = false;
|
|
let browserHandler;
|
|
const pipe = Cc['@mozilla.org/juggler/remotedebuggingpipe;1'].getService(Ci.nsIRemoteDebuggingPipe);
|
|
const connection = {
|
|
QueryInterface: ChromeUtils.generateQI([Ci.nsIRemoteDebuggingPipeClient]),
|
|
receiveMessage(message) {
|
|
if (this.onmessage)
|
|
this.onmessage({ data: message });
|
|
},
|
|
disconnected() {
|
|
if (browserHandler)
|
|
browserHandler['Browser.close']();
|
|
},
|
|
send(message) {
|
|
if (pipeStopped) {
|
|
// We are missing the response to Browser.close,
|
|
// but everything works fine. Once we actually need it,
|
|
// we have to stop the pipe after the response is sent.
|
|
return;
|
|
}
|
|
pipe.sendMessage(message);
|
|
},
|
|
};
|
|
pipe.init(connection);
|
|
const dispatcher = new Dispatcher(connection);
|
|
browserHandler = new BrowserHandler(dispatcher.rootSession(), dispatcher, targetRegistry, browserStartupFinishedPromise, () => {
|
|
if (this._silent)
|
|
Services.startup.exitLastWindowClosingSurvivalArea();
|
|
connection.onclose();
|
|
pipe.stop();
|
|
pipeStopped = true;
|
|
});
|
|
dispatcher.rootSession().setHandler(browserHandler);
|
|
loadStyleSheet();
|
|
dump(`\nJuggler listening to the pipe\n`);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
const jugglerInstance = new Juggler();
|
|
|
|
// This is used by the XPCOM codepath which expects a constructor
|
|
var JugglerFactory = function() {
|
|
return jugglerInstance;
|
|
};
|
|
|