| |
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
| |
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
| |
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
4 |
| |
5 this.CC = Components.Constructor; |
| |
6 this.Cc = Components.classes; |
| |
7 this.Ci = Components.interfaces; |
| |
8 this.Cu = Components.utils; |
| |
9 |
| |
10 const MARIONETTE_CONTRACTID = "@mozilla.org/marionette;1"; |
| |
11 const MARIONETTE_CID = Components.ID("{786a1369-dca5-4adc-8486-33d23c88010a}"); |
| |
12 const MARIONETTE_ENABLED_PREF = 'marionette.defaultPrefs.enabled'; |
| |
13 const MARIONETTE_FORCELOCAL_PREF = 'marionette.force-local'; |
| |
14 const MARIONETTE_LOG_PREF = 'marionette.logging'; |
| |
15 |
| |
16 this.ServerSocket = CC("@mozilla.org/network/server-socket;1", |
| |
17 "nsIServerSocket", |
| |
18 "initSpecialConnection"); |
| |
19 |
| |
20 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
| |
21 Cu.import("resource://gre/modules/Services.jsm"); |
| |
22 Cu.import("resource://gre/modules/FileUtils.jsm"); |
| |
23 Cu.import("resource://gre/modules/Log.jsm"); |
| |
24 |
| |
25 let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] |
| |
26 .getService(Ci.mozIJSSubScriptLoader); |
| |
27 |
| |
28 function MarionetteComponent() { |
| |
29 this._loaded = false; |
| |
30 this.observerService = Services.obs; |
| |
31 |
| |
32 // set up the logger |
| |
33 this.logger = Log.repository.getLogger("Marionette"); |
| |
34 this.logger.level = Log.Level["Trace"]; |
| |
35 let dumper = false; |
| |
36 #ifdef DEBUG |
| |
37 dumper = true; |
| |
38 #endif |
| |
39 #ifdef MOZ_B2G |
| |
40 dumper = true; |
| |
41 #endif |
| |
42 try { |
| |
43 if (dumper || Services.prefs.getBoolPref(MARIONETTE_LOG_PREF)) { |
| |
44 let formatter = new Log.BasicFormatter(); |
| |
45 this.logger.addAppender(new Log.DumpAppender(formatter)); |
| |
46 } |
| |
47 } |
| |
48 catch(e) {} |
| |
49 } |
| |
50 |
| |
51 MarionetteComponent.prototype = { |
| |
52 classDescription: "Marionette component", |
| |
53 classID: MARIONETTE_CID, |
| |
54 contractID: MARIONETTE_CONTRACTID, |
| |
55 QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler, Ci.nsIObserver]), |
| |
56 _xpcom_categories: [{category: "command-line-handler", entry: "b-marionette"}, |
| |
57 {category: "profile-after-change", service: true}], |
| |
58 appName: Services.appinfo.name, |
| |
59 enabled: false, |
| |
60 finalUiStartup: false, |
| |
61 _marionetteServer: null, |
| |
62 |
| |
63 onSocketAccepted: function mc_onSocketAccepted(aSocket, aTransport) { |
| |
64 this.logger.info("onSocketAccepted for Marionette dummy socket"); |
| |
65 }, |
| |
66 |
| |
67 onStopListening: function mc_onStopListening(aSocket, status) { |
| |
68 this.logger.info("onStopListening for Marionette dummy socket, code " + status); |
| |
69 aSocket.close(); |
| |
70 }, |
| |
71 |
| |
72 // Check cmdLine argument for --marionette |
| |
73 handle: function mc_handle(cmdLine) { |
| |
74 // If the CLI is there then lets do work otherwise nothing to see |
| |
75 if (cmdLine.handleFlag("marionette", false)) { |
| |
76 this.enabled = true; |
| |
77 this.logger.info("marionette enabled via command-line"); |
| |
78 this.init(); |
| |
79 } |
| |
80 }, |
| |
81 |
| |
82 observe: function mc_observe(aSubject, aTopic, aData) { |
| |
83 switch (aTopic) { |
| |
84 case "profile-after-change": |
| |
85 // Using final-ui-startup as the xpcom category doesn't seem to work, |
| |
86 // so we wait for that by adding an observer here. |
| |
87 this.observerService.addObserver(this, "final-ui-startup", false); |
| |
88 #ifdef ENABLE_MARIONETTE |
| |
89 let enabledPref = false; |
| |
90 try { |
| |
91 enabledPref = Services.prefs.getBoolPref(MARIONETTE_ENABLED_PREF); |
| |
92 } catch(e) {} |
| |
93 if (enabledPref) { |
| |
94 this.enabled = true; |
| |
95 this.logger.info("marionette enabled via build flag and pref"); |
| |
96 |
| |
97 // We want to suppress the modal dialog that's shown |
| |
98 // when starting up in safe-mode to enable testing. |
| |
99 if (Services.appinfo.inSafeMode) { |
| |
100 this.observerService.addObserver(this, "domwindowopened", false); |
| |
101 } |
| |
102 } |
| |
103 #endif |
| |
104 break; |
| |
105 case "final-ui-startup": |
| |
106 this.finalUiStartup = true; |
| |
107 this.observerService.removeObserver(this, aTopic); |
| |
108 this.observerService.addObserver(this, "xpcom-shutdown", false); |
| |
109 this.init(); |
| |
110 break; |
| |
111 case "domwindowopened": |
| |
112 this.observerService.removeObserver(this, aTopic); |
| |
113 this._suppressSafeModeDialog(aSubject); |
| |
114 break; |
| |
115 case "xpcom-shutdown": |
| |
116 this.observerService.removeObserver(this, "xpcom-shutdown"); |
| |
117 this.uninit(); |
| |
118 break; |
| |
119 } |
| |
120 }, |
| |
121 |
| |
122 _suppressSafeModeDialog: function mc_suppressSafeModeDialog(aWindow) { |
| |
123 // Wait for the modal dialog to finish loading. |
| |
124 aWindow.addEventListener("load", function onLoad() { |
| |
125 aWindow.removeEventListener("load", onLoad); |
| |
126 |
| |
127 if (aWindow.document.getElementById("safeModeDialog")) { |
| |
128 aWindow.setTimeout(() => { |
| |
129 // Accept the dialog to start in safe-mode. |
| |
130 aWindow.document.documentElement.getButton("accept").click(); |
| |
131 }); |
| |
132 } |
| |
133 }); |
| |
134 }, |
| |
135 |
| |
136 init: function mc_init() { |
| |
137 if (!this._loaded && this.enabled && this.finalUiStartup) { |
| |
138 this._loaded = true; |
| |
139 |
| |
140 let marionette_forcelocal = this.appName == 'B2G' ? false : true; |
| |
141 try { |
| |
142 marionette_forcelocal = Services.prefs.getBoolPref(MARIONETTE_FORCELOCAL_PREF); |
| |
143 } |
| |
144 catch(e) {} |
| |
145 Services.prefs.setBoolPref(MARIONETTE_FORCELOCAL_PREF, marionette_forcelocal); |
| |
146 |
| |
147 if (!marionette_forcelocal) { |
| |
148 // See bug 800138. Because the first socket that opens with |
| |
149 // force-local=false fails, we open a dummy socket that will fail. |
| |
150 // keepWhenOffline=true so that it still work when offline (local). |
| |
151 // This allows the following attempt by Marionette to open a socket |
| |
152 // to succeed. |
| |
153 let insaneSacrificialGoat = new ServerSocket(666, Ci.nsIServerSocket.KeepWhenOffline, 4); |
| |
154 insaneSacrificialGoat.asyncListen(this); |
| |
155 } |
| |
156 |
| |
157 let port; |
| |
158 try { |
| |
159 port = Services.prefs.getIntPref('marionette.defaultPrefs.port'); |
| |
160 } |
| |
161 catch(e) { |
| |
162 port = 2828; |
| |
163 } |
| |
164 try { |
| |
165 loader.loadSubScript("chrome://marionette/content/marionette-server.js"); |
| |
166 let forceLocal = Services.prefs.getBoolPref(MARIONETTE_FORCELOCAL_PREF); |
| |
167 this._marionetteServer = new MarionetteServer(port, forceLocal); |
| |
168 this.logger.info("Marionette server ready"); |
| |
169 } |
| |
170 catch(e) { |
| |
171 this.logger.error('exception: ' + e.name + ', ' + e.message); |
| |
172 } |
| |
173 } |
| |
174 }, |
| |
175 |
| |
176 uninit: function mc_uninit() { |
| |
177 if (this._marionetteServer) { |
| |
178 this._marionetteServer.closeListener(); |
| |
179 } |
| |
180 this._loaded = false; |
| |
181 }, |
| |
182 |
| |
183 }; |
| |
184 |
| |
185 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MarionetteComponent]); |