|
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]); |