mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			157 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			157 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: {},
 | |
|       },
 | |
|     },
 | |
|     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, () => {
 | |
|           if (this._silent)
 | |
|             Services.startup.exitLastWindowClosingSurvivalArea();
 | |
|           connection.onclose();
 | |
|           pipe.stop();
 | |
|           pipeStopped = true;
 | |
|         }, () => browserStartupFinishedPromise);
 | |
|         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;
 | |
| };
 | |
| 
 | 
