specialpowers.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. /* This code is loaded in every child process that is started by mochitest in
  5. * order to be used as a replacement for UniversalXPConnect
  6. */
  7. function SpecialPowers(window) {
  8. this.window = Components.utils.getWeakReference(window);
  9. this._windowID = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  10. .getInterface(Components.interfaces.nsIDOMWindowUtils)
  11. .currentInnerWindowID;
  12. this._encounteredCrashDumpFiles = [];
  13. this._unexpectedCrashDumpFiles = { };
  14. this._crashDumpDir = null;
  15. this.DOMWindowUtils = bindDOMWindowUtils(window);
  16. Object.defineProperty(this, 'Components', {
  17. configurable: true, enumerable: true, get: function() {
  18. var win = this.window.get();
  19. if (!win)
  20. return null;
  21. return getRawComponents(win);
  22. }});
  23. this._pongHandlers = [];
  24. this._messageListener = this._messageReceived.bind(this);
  25. this._grandChildFrameMM = null;
  26. this._createFilesOnError = null;
  27. this._createFilesOnSuccess = null;
  28. this.SP_SYNC_MESSAGES = ["SPChromeScriptMessage",
  29. "SPLoadChromeScript",
  30. "SPImportInMainProcess",
  31. "SPObserverService",
  32. "SPPermissionManager",
  33. "SPPrefService",
  34. "SPProcessCrashService",
  35. "SPSetTestPluginEnabledState",
  36. "SPCleanUpSTSData"];
  37. this.SP_ASYNC_MESSAGES = ["SpecialPowers.Focus",
  38. "SpecialPowers.Quit",
  39. "SpecialPowers.CreateFiles",
  40. "SpecialPowers.RemoveFiles",
  41. "SPPingService",
  42. "SPLoadExtension",
  43. "SPStartupExtension",
  44. "SPUnloadExtension",
  45. "SPExtensionMessage",
  46. "SPClearAppPrivateData"];
  47. addMessageListener("SPPingService", this._messageListener);
  48. addMessageListener("SpecialPowers.FilesCreated", this._messageListener);
  49. addMessageListener("SpecialPowers.FilesError", this._messageListener);
  50. let self = this;
  51. Services.obs.addObserver(function onInnerWindowDestroyed(subject, topic, data) {
  52. var id = subject.QueryInterface(Components.interfaces.nsISupportsPRUint64).data;
  53. if (self._windowID === id) {
  54. Services.obs.removeObserver(onInnerWindowDestroyed, "inner-window-destroyed");
  55. try {
  56. removeMessageListener("SPPingService", self._messageListener);
  57. removeMessageListener("SpecialPowers.FilesCreated", self._messageListener);
  58. removeMessageListener("SpecialPowers.FilesError", self._messageListener);
  59. } catch (e if e.result == Components.results.NS_ERROR_ILLEGAL_VALUE) {
  60. // Ignore the exception which the message manager has been destroyed.
  61. ;
  62. }
  63. }
  64. }, "inner-window-destroyed", false);
  65. }
  66. SpecialPowers.prototype = new SpecialPowersAPI();
  67. SpecialPowers.prototype.toString = function() { return "[SpecialPowers]"; };
  68. SpecialPowers.prototype.sanityCheck = function() { return "foo"; };
  69. // This gets filled in in the constructor.
  70. SpecialPowers.prototype.DOMWindowUtils = undefined;
  71. SpecialPowers.prototype.Components = undefined;
  72. SpecialPowers.prototype.IsInNestedFrame = false;
  73. SpecialPowers.prototype._sendSyncMessage = function(msgname, msg) {
  74. if (this.SP_SYNC_MESSAGES.indexOf(msgname) == -1) {
  75. dump("TEST-INFO | specialpowers.js | Unexpected SP message: " + msgname + "\n");
  76. }
  77. return sendSyncMessage(msgname, msg);
  78. };
  79. SpecialPowers.prototype._sendAsyncMessage = function(msgname, msg) {
  80. if (this.SP_ASYNC_MESSAGES.indexOf(msgname) == -1) {
  81. dump("TEST-INFO | specialpowers.js | Unexpected SP message: " + msgname + "\n");
  82. }
  83. sendAsyncMessage(msgname, msg);
  84. };
  85. SpecialPowers.prototype._addMessageListener = function(msgname, listener) {
  86. addMessageListener(msgname, listener);
  87. sendAsyncMessage("SPPAddNestedMessageListener", { name: msgname });
  88. };
  89. SpecialPowers.prototype._removeMessageListener = function(msgname, listener) {
  90. removeMessageListener(msgname, listener);
  91. };
  92. SpecialPowers.prototype.registerProcessCrashObservers = function() {
  93. addMessageListener("SPProcessCrashService", this._messageListener);
  94. sendSyncMessage("SPProcessCrashService", { op: "register-observer" });
  95. };
  96. SpecialPowers.prototype.unregisterProcessCrashObservers = function() {
  97. removeMessageListener("SPProcessCrashService", this._messageListener);
  98. sendSyncMessage("SPProcessCrashService", { op: "unregister-observer" });
  99. };
  100. SpecialPowers.prototype._messageReceived = function(aMessage) {
  101. switch (aMessage.name) {
  102. case "SPProcessCrashService":
  103. if (aMessage.json.type == "crash-observed") {
  104. for (let e of aMessage.json.dumpIDs) {
  105. this._encounteredCrashDumpFiles.push(e.id + "." + e.extension);
  106. }
  107. }
  108. break;
  109. case "SPPingService":
  110. if (aMessage.json.op == "pong") {
  111. var handler = this._pongHandlers.shift();
  112. if (handler) {
  113. handler();
  114. }
  115. if (this._grandChildFrameMM) {
  116. this._grandChildFrameMM.sendAsyncMessage("SPPingService", { op: "pong" });
  117. }
  118. }
  119. break;
  120. case "SpecialPowers.FilesCreated":
  121. var handler = this._createFilesOnSuccess;
  122. this._createFilesOnSuccess = null;
  123. this._createFilesOnError = null;
  124. if (handler) {
  125. handler(aMessage.data);
  126. }
  127. break;
  128. case "SpecialPowers.FilesError":
  129. var handler = this._createFilesOnError;
  130. this._createFilesOnSuccess = null;
  131. this._createFilesOnError = null;
  132. if (handler) {
  133. handler(aMessage.data);
  134. }
  135. break;
  136. }
  137. return true;
  138. };
  139. SpecialPowers.prototype.quit = function() {
  140. sendAsyncMessage("SpecialPowers.Quit", {});
  141. };
  142. // fileRequests is an array of file requests. Each file request is an object.
  143. // A request must have a field |name|, which gives the base of the name of the
  144. // file to be created in the profile directory. If the request has a |data| field
  145. // then that data will be written to the file.
  146. SpecialPowers.prototype.createFiles = function(fileRequests, onCreation, onError) {
  147. if (this._createFilesOnSuccess || this._createFilesOnError) {
  148. onError("Already waiting for SpecialPowers.createFiles() to finish.");
  149. return;
  150. }
  151. this._createFilesOnSuccess = onCreation;
  152. this._createFilesOnError = onError;
  153. sendAsyncMessage("SpecialPowers.CreateFiles", fileRequests);
  154. };
  155. // Remove the files that were created using |SpecialPowers.createFiles()|.
  156. // This will be automatically called by |SimpleTest.finish()|.
  157. SpecialPowers.prototype.removeFiles = function() {
  158. sendAsyncMessage("SpecialPowers.RemoveFiles", {});
  159. };
  160. SpecialPowers.prototype.executeAfterFlushingMessageQueue = function(aCallback) {
  161. this._pongHandlers.push(aCallback);
  162. sendAsyncMessage("SPPingService", { op: "ping" });
  163. };
  164. SpecialPowers.prototype.nestedFrameSetup = function() {
  165. let self = this;
  166. Services.obs.addObserver(function onRemoteBrowserShown(subject, topic, data) {
  167. let frameLoader = subject;
  168. // get a ref to the app <iframe>
  169. frameLoader.QueryInterface(Components.interfaces.nsIFrameLoader);
  170. let frame = frameLoader.ownerElement;
  171. let frameId = frame.getAttribute('id');
  172. if (frameId === "nested-parent-frame") {
  173. Services.obs.removeObserver(onRemoteBrowserShown, "remote-browser-shown");
  174. let mm = frame.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader.messageManager;
  175. self._grandChildFrameMM = mm;
  176. self.SP_SYNC_MESSAGES.forEach(function (msgname) {
  177. mm.addMessageListener(msgname, function (msg) {
  178. return self._sendSyncMessage(msgname, msg.data)[0];
  179. });
  180. });
  181. self.SP_ASYNC_MESSAGES.forEach(function (msgname) {
  182. mm.addMessageListener(msgname, function (msg) {
  183. self._sendAsyncMessage(msgname, msg.data);
  184. });
  185. });
  186. mm.addMessageListener("SPPAddNestedMessageListener", function(msg) {
  187. self._addMessageListener(msg.json.name, function(aMsg) {
  188. mm.sendAsyncMessage(aMsg.name, aMsg.data);
  189. });
  190. });
  191. mm.loadFrameScript("chrome://specialpowers/content/MozillaLogger.js", false);
  192. mm.loadFrameScript("chrome://specialpowers/content/specialpowersAPI.js", false);
  193. mm.loadFrameScript("chrome://specialpowers/content/specialpowers.js", false);
  194. let frameScript = "SpecialPowers.prototype.IsInNestedFrame=true;";
  195. mm.loadFrameScript("data:," + frameScript, false);
  196. }
  197. }, "remote-browser-shown", false);
  198. };
  199. SpecialPowers.prototype.isServiceWorkerRegistered = function() {
  200. var swm = Components.classes["@mozilla.org/serviceworkers/manager;1"]
  201. .getService(Components.interfaces.nsIServiceWorkerManager);
  202. return swm.getAllRegistrations().length != 0;
  203. };
  204. // Attach our API to the window.
  205. function attachSpecialPowersToWindow(aWindow) {
  206. try {
  207. if ((aWindow !== null) &&
  208. (aWindow !== undefined) &&
  209. (aWindow.wrappedJSObject) &&
  210. !(aWindow.wrappedJSObject.SpecialPowers)) {
  211. let sp = new SpecialPowers(aWindow);
  212. aWindow.wrappedJSObject.SpecialPowers = sp;
  213. if (sp.IsInNestedFrame) {
  214. sp.addPermission("allowXULXBL", true, aWindow.document);
  215. }
  216. }
  217. } catch(ex) {
  218. dump("TEST-INFO | specialpowers.js | Failed to attach specialpowers to window exception: " + ex + "\n");
  219. }
  220. }
  221. // This is a frame script, so it may be running in a content process.
  222. // In any event, it is targeted at a specific "tab", so we listen for
  223. // the DOMWindowCreated event to be notified about content windows
  224. // being created in this context.
  225. function SpecialPowersManager() {
  226. addEventListener("DOMWindowCreated", this, false);
  227. }
  228. SpecialPowersManager.prototype = {
  229. handleEvent: function handleEvent(aEvent) {
  230. var window = aEvent.target.defaultView;
  231. attachSpecialPowersToWindow(window);
  232. }
  233. };
  234. var specialpowersmanager = new SpecialPowersManager();
  235. this.SpecialPowers = SpecialPowers;
  236. this.attachSpecialPowersToWindow = attachSpecialPowersToWindow;
  237. // In the case of Chrome mochitests that inject specialpowers.js as
  238. // a regular content script
  239. if (typeof window != 'undefined') {
  240. window.addMessageListener = function() {}
  241. window.removeMessageListener = function() {}
  242. window.wrappedJSObject.SpecialPowers = new SpecialPowers(window);
  243. }