1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/specialpowers/content/specialpowersAPI.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1751 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +/* This code is loaded in every child process that is started by mochitest in 1.8 + * order to be used as a replacement for UniversalXPConnect 1.9 + */ 1.10 + 1.11 +var Ci = Components.interfaces; 1.12 +var Cc = Components.classes; 1.13 +var Cu = Components.utils; 1.14 + 1.15 +Cu.import("resource://specialpowers/MockFilePicker.jsm"); 1.16 +Cu.import("resource://specialpowers/MockColorPicker.jsm"); 1.17 +Cu.import("resource://specialpowers/MockPermissionPrompt.jsm"); 1.18 +Cu.import("resource://gre/modules/Services.jsm"); 1.19 +Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); 1.20 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.21 + 1.22 +function SpecialPowersAPI() { 1.23 + this._consoleListeners = []; 1.24 + this._encounteredCrashDumpFiles = []; 1.25 + this._unexpectedCrashDumpFiles = { }; 1.26 + this._crashDumpDir = null; 1.27 + this._mfl = null; 1.28 + this._prefEnvUndoStack = []; 1.29 + this._pendingPrefs = []; 1.30 + this._applyingPrefs = false; 1.31 + this._permissionsUndoStack = []; 1.32 + this._pendingPermissions = []; 1.33 + this._applyingPermissions = false; 1.34 + this._fm = null; 1.35 + this._cb = null; 1.36 +} 1.37 + 1.38 +function bindDOMWindowUtils(aWindow) { 1.39 + if (!aWindow) 1.40 + return 1.41 + 1.42 + var util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) 1.43 + .getInterface(Ci.nsIDOMWindowUtils); 1.44 + return wrapPrivileged(util); 1.45 +} 1.46 + 1.47 +function getRawComponents(aWindow) { 1.48 + // If we're running in automation that supports enablePrivilege, then we also 1.49 + // provided access to the privileged Components. 1.50 + try { 1.51 + let win = Cu.waiveXrays(aWindow); 1.52 + if (typeof win.netscape.security.PrivilegeManager == 'object') 1.53 + Cu.forcePrivilegedComponentsForScope(aWindow); 1.54 + } catch (e) {} 1.55 + return Cu.getComponentsForScope(aWindow); 1.56 +} 1.57 + 1.58 +function isWrappable(x) { 1.59 + if (typeof x === "object") 1.60 + return x !== null; 1.61 + return typeof x === "function"; 1.62 +}; 1.63 + 1.64 +function isWrapper(x) { 1.65 + return isWrappable(x) && (typeof x.SpecialPowers_wrappedObject !== "undefined"); 1.66 +}; 1.67 + 1.68 +function unwrapIfWrapped(x) { 1.69 + return isWrapper(x) ? unwrapPrivileged(x) : x; 1.70 +}; 1.71 + 1.72 +function wrapIfUnwrapped(x) { 1.73 + return isWrapper(x) ? x : wrapPrivileged(x); 1.74 +} 1.75 + 1.76 +function isXrayWrapper(x) { 1.77 + return Cu.isXrayWrapper(x); 1.78 +} 1.79 + 1.80 +function callGetOwnPropertyDescriptor(obj, name) { 1.81 + // Quickstubbed getters and setters are propertyOps, and don't get reified 1.82 + // until someone calls __lookupGetter__ or __lookupSetter__ on them (note 1.83 + // that there are special version of those functions for quickstubs, so 1.84 + // apply()ing Object.prototype.__lookupGetter__ isn't good enough). Try to 1.85 + // trigger reification before calling Object.getOwnPropertyDescriptor. 1.86 + // 1.87 + // See bug 764315. 1.88 + try { 1.89 + obj.__lookupGetter__(name); 1.90 + obj.__lookupSetter__(name); 1.91 + } catch(e) { } 1.92 + return Object.getOwnPropertyDescriptor(obj, name); 1.93 +} 1.94 + 1.95 +// We can't call apply() directy on Xray-wrapped functions, so we have to be 1.96 +// clever. 1.97 +function doApply(fun, invocant, args) { 1.98 + return Function.prototype.apply.call(fun, invocant, args); 1.99 +} 1.100 + 1.101 +function wrapPrivileged(obj) { 1.102 + 1.103 + // Primitives pass straight through. 1.104 + if (!isWrappable(obj)) 1.105 + return obj; 1.106 + 1.107 + // No double wrapping. 1.108 + if (isWrapper(obj)) 1.109 + throw "Trying to double-wrap object!"; 1.110 + 1.111 + // Make our core wrapper object. 1.112 + var handler = new SpecialPowersHandler(obj); 1.113 + 1.114 + // If the object is callable, make a function proxy. 1.115 + if (typeof obj === "function") { 1.116 + var callTrap = function() { 1.117 + // The invocant and arguments may or may not be wrappers. Unwrap them if necessary. 1.118 + var invocant = unwrapIfWrapped(this); 1.119 + var unwrappedArgs = Array.prototype.slice.call(arguments).map(unwrapIfWrapped); 1.120 + 1.121 + try { 1.122 + return wrapPrivileged(doApply(obj, invocant, unwrappedArgs)); 1.123 + } catch (e) { 1.124 + // Wrap exceptions and re-throw them. 1.125 + throw wrapIfUnwrapped(e); 1.126 + } 1.127 + }; 1.128 + var constructTrap = function() { 1.129 + // The arguments may or may not be wrappers. Unwrap them if necessary. 1.130 + var unwrappedArgs = Array.prototype.slice.call(arguments).map(unwrapIfWrapped); 1.131 + 1.132 + // We want to invoke "obj" as a constructor, but using unwrappedArgs as 1.133 + // the arguments. Make sure to wrap and re-throw exceptions! 1.134 + try { 1.135 + return wrapPrivileged(new obj(...unwrappedArgs)); 1.136 + } catch (e) { 1.137 + throw wrapIfUnwrapped(e); 1.138 + } 1.139 + }; 1.140 + 1.141 + return Proxy.createFunction(handler, callTrap, constructTrap); 1.142 + } 1.143 + 1.144 + // Otherwise, just make a regular object proxy. 1.145 + return Proxy.create(handler); 1.146 +}; 1.147 + 1.148 +function unwrapPrivileged(x) { 1.149 + 1.150 + // We don't wrap primitives, so sometimes we have a primitive where we'd 1.151 + // expect to have a wrapper. The proxy pretends to be the type that it's 1.152 + // emulating, so we can just as easily check isWrappable() on a proxy as 1.153 + // we can on an unwrapped object. 1.154 + if (!isWrappable(x)) 1.155 + return x; 1.156 + 1.157 + // If we have a wrappable type, make sure it's wrapped. 1.158 + if (!isWrapper(x)) 1.159 + throw "Trying to unwrap a non-wrapped object!"; 1.160 + 1.161 + // Unwrap. 1.162 + return x.SpecialPowers_wrappedObject; 1.163 +}; 1.164 + 1.165 +function crawlProtoChain(obj, fn) { 1.166 + var rv = fn(obj); 1.167 + if (rv !== undefined) 1.168 + return rv; 1.169 + if (Object.getPrototypeOf(obj)) 1.170 + return crawlProtoChain(Object.getPrototypeOf(obj), fn); 1.171 +}; 1.172 + 1.173 +/* 1.174 + * We want to waive the __exposedProps__ security check for SpecialPowers-wrapped 1.175 + * objects. We do this by creating a proxy singleton that just always returns 'rw' 1.176 + * for any property name. 1.177 + */ 1.178 +function ExposedPropsWaiverHandler() { 1.179 + // NB: XPConnect denies access if the relevant member of __exposedProps__ is not 1.180 + // enumerable. 1.181 + var _permit = { value: 'rw', writable: false, configurable: false, enumerable: true }; 1.182 + return { 1.183 + getOwnPropertyDescriptor: function(name) { return _permit; }, 1.184 + getPropertyDescriptor: function(name) { return _permit; }, 1.185 + getOwnPropertyNames: function() { throw Error("Can't enumerate ExposedPropsWaiver"); }, 1.186 + getPropertyNames: function() { throw Error("Can't enumerate ExposedPropsWaiver"); }, 1.187 + enumerate: function() { throw Error("Can't enumerate ExposedPropsWaiver"); }, 1.188 + defineProperty: function(name) { throw Error("Can't define props on ExposedPropsWaiver"); }, 1.189 + delete: function(name) { throw Error("Can't delete props from ExposedPropsWaiver"); } 1.190 + }; 1.191 +}; 1.192 +ExposedPropsWaiver = Proxy.create(ExposedPropsWaiverHandler()); 1.193 + 1.194 +function SpecialPowersHandler(obj) { 1.195 + this.wrappedObject = obj; 1.196 +}; 1.197 + 1.198 +// Allow us to transitively maintain the membrane by wrapping descriptors 1.199 +// we return. 1.200 +SpecialPowersHandler.prototype.doGetPropertyDescriptor = function(name, own) { 1.201 + 1.202 + // Handle our special API. 1.203 + if (name == "SpecialPowers_wrappedObject") 1.204 + return { value: this.wrappedObject, writeable: false, configurable: false, enumerable: false }; 1.205 + 1.206 + // Handle __exposedProps__. 1.207 + if (name == "__exposedProps__") 1.208 + return { value: ExposedPropsWaiver, writable: false, configurable: false, enumerable: false }; 1.209 + 1.210 + // In general, we want Xray wrappers for content DOM objects, because waiving 1.211 + // Xray gives us Xray waiver wrappers that clamp the principal when we cross 1.212 + // compartment boundaries. However, Xray adds some gunk to toString(), which 1.213 + // has the potential to confuse consumers that aren't expecting Xray wrappers. 1.214 + // Since toString() is a non-privileged method that returns only strings, we 1.215 + // can just waive Xray for that case. 1.216 + var obj = name == 'toString' ? XPCNativeWrapper.unwrap(this.wrappedObject) 1.217 + : this.wrappedObject; 1.218 + 1.219 + // 1.220 + // Call through to the wrapped object. 1.221 + // 1.222 + // Note that we have several cases here, each of which requires special handling. 1.223 + // 1.224 + var desc; 1.225 + 1.226 + // Case 1: Own Properties. 1.227 + // 1.228 + // This one is easy, thanks to Object.getOwnPropertyDescriptor(). 1.229 + if (own) 1.230 + desc = callGetOwnPropertyDescriptor(obj, name); 1.231 + 1.232 + // Case 2: Not own, not Xray-wrapped. 1.233 + // 1.234 + // Here, we can just crawl the prototype chain, calling 1.235 + // Object.getOwnPropertyDescriptor until we find what we want. 1.236 + // 1.237 + // NB: Make sure to check this.wrappedObject here, rather than obj, because 1.238 + // we may have waived Xray on obj above. 1.239 + else if (!isXrayWrapper(this.wrappedObject)) 1.240 + desc = crawlProtoChain(obj, function(o) {return callGetOwnPropertyDescriptor(o, name);}); 1.241 + 1.242 + // Case 3: Not own, Xray-wrapped. 1.243 + // 1.244 + // This one is harder, because we Xray wrappers are flattened and don't have 1.245 + // a prototype. Xray wrappers are proxies themselves, so we'd love to just call 1.246 + // through to XrayWrapper<Base>::getPropertyDescriptor(). Unfortunately though, 1.247 + // we don't have any way to do that. :-( 1.248 + // 1.249 + // So we first try with a call to getOwnPropertyDescriptor(). If that fails, 1.250 + // we make up a descriptor, using some assumptions about what kinds of things 1.251 + // tend to live on the prototypes of Xray-wrapped objects. 1.252 + else { 1.253 + desc = Object.getOwnPropertyDescriptor(obj, name); 1.254 + if (!desc) { 1.255 + var getter = Object.prototype.__lookupGetter__.call(obj, name); 1.256 + var setter = Object.prototype.__lookupSetter__.call(obj, name); 1.257 + if (getter || setter) 1.258 + desc = {get: getter, set: setter, configurable: true, enumerable: true}; 1.259 + else if (name in obj) 1.260 + desc = {value: obj[name], writable: false, configurable: true, enumerable: true}; 1.261 + } 1.262 + } 1.263 + 1.264 + // Bail if we've got nothing. 1.265 + if (typeof desc === 'undefined') 1.266 + return undefined; 1.267 + 1.268 + // When accessors are implemented as JSPropertyOps rather than JSNatives (ie, 1.269 + // QuickStubs), the js engine does the wrong thing and treats it as a value 1.270 + // descriptor rather than an accessor descriptor. Jorendorff suggested this 1.271 + // little hack to work around it. See bug 520882. 1.272 + if (desc && 'value' in desc && desc.value === undefined) 1.273 + desc.value = obj[name]; 1.274 + 1.275 + // A trapping proxy's properties must always be configurable, but sometimes 1.276 + // this we get non-configurable properties from Object.getOwnPropertyDescriptor(). 1.277 + // Tell a white lie. 1.278 + desc.configurable = true; 1.279 + 1.280 + // Transitively maintain the wrapper membrane. 1.281 + function wrapIfExists(key) { if (key in desc) desc[key] = wrapPrivileged(desc[key]); }; 1.282 + wrapIfExists('value'); 1.283 + wrapIfExists('get'); 1.284 + wrapIfExists('set'); 1.285 + 1.286 + return desc; 1.287 +}; 1.288 + 1.289 +SpecialPowersHandler.prototype.getOwnPropertyDescriptor = function(name) { 1.290 + return this.doGetPropertyDescriptor(name, true); 1.291 +}; 1.292 + 1.293 +SpecialPowersHandler.prototype.getPropertyDescriptor = function(name) { 1.294 + return this.doGetPropertyDescriptor(name, false); 1.295 +}; 1.296 + 1.297 +function doGetOwnPropertyNames(obj, props) { 1.298 + 1.299 + // Insert our special API. It's not enumerable, but getPropertyNames() 1.300 + // includes non-enumerable properties. 1.301 + var specialAPI = 'SpecialPowers_wrappedObject'; 1.302 + if (props.indexOf(specialAPI) == -1) 1.303 + props.push(specialAPI); 1.304 + 1.305 + // Do the normal thing. 1.306 + var flt = function(a) { return props.indexOf(a) == -1; }; 1.307 + props = props.concat(Object.getOwnPropertyNames(obj).filter(flt)); 1.308 + 1.309 + // If we've got an Xray wrapper, include the expandos as well. 1.310 + if ('wrappedJSObject' in obj) 1.311 + props = props.concat(Object.getOwnPropertyNames(obj.wrappedJSObject) 1.312 + .filter(flt)); 1.313 + 1.314 + return props; 1.315 +} 1.316 + 1.317 +SpecialPowersHandler.prototype.getOwnPropertyNames = function() { 1.318 + return doGetOwnPropertyNames(this.wrappedObject, []); 1.319 +}; 1.320 + 1.321 +SpecialPowersHandler.prototype.getPropertyNames = function() { 1.322 + 1.323 + // Manually walk the prototype chain, making sure to add only property names 1.324 + // that haven't been overridden. 1.325 + // 1.326 + // There's some trickiness here with Xray wrappers. Xray wrappers don't have 1.327 + // a prototype, so we need to unwrap them if we want to get all of the names 1.328 + // with Object.getOwnPropertyNames(). But we don't really want to unwrap the 1.329 + // base object, because that will include expandos that are inaccessible via 1.330 + // our implementation of get{,Own}PropertyDescriptor(). So we unwrap just 1.331 + // before accessing the prototype. This ensures that we get Xray vision on 1.332 + // the base object, and no Xray vision for the rest of the way up. 1.333 + var obj = this.wrappedObject; 1.334 + var props = []; 1.335 + while (obj) { 1.336 + props = doGetOwnPropertyNames(obj, props); 1.337 + obj = Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj)); 1.338 + } 1.339 + return props; 1.340 +}; 1.341 + 1.342 +SpecialPowersHandler.prototype.defineProperty = function(name, desc) { 1.343 + return Object.defineProperty(this.wrappedObject, name, desc); 1.344 +}; 1.345 + 1.346 +SpecialPowersHandler.prototype.delete = function(name) { 1.347 + return delete this.wrappedObject[name]; 1.348 +}; 1.349 + 1.350 +SpecialPowersHandler.prototype.fix = function() { return undefined; /* Throws a TypeError. */ }; 1.351 + 1.352 +// Per the ES5 spec this is a derived trap, but it's fundamental in spidermonkey 1.353 +// for some reason. See bug 665198. 1.354 +SpecialPowersHandler.prototype.enumerate = function() { 1.355 + var t = this; 1.356 + var filt = function(name) { return t.getPropertyDescriptor(name).enumerable; }; 1.357 + return this.getPropertyNames().filter(filt); 1.358 +}; 1.359 + 1.360 +// SPConsoleListener reflects nsIConsoleMessage objects into JS in a 1.361 +// tidy, XPCOM-hiding way. Messages that are nsIScriptError objects 1.362 +// have their properties exposed in detail. It also auto-unregisters 1.363 +// itself when it receives a "sentinel" message. 1.364 +function SPConsoleListener(callback) { 1.365 + this.callback = callback; 1.366 +} 1.367 + 1.368 +SPConsoleListener.prototype = { 1.369 + observe: function(msg) { 1.370 + let m = { message: msg.message, 1.371 + errorMessage: null, 1.372 + sourceName: null, 1.373 + sourceLine: null, 1.374 + lineNumber: null, 1.375 + columnNumber: null, 1.376 + category: null, 1.377 + windowID: null, 1.378 + isScriptError: false, 1.379 + isWarning: false, 1.380 + isException: false, 1.381 + isStrict: false }; 1.382 + if (msg instanceof Ci.nsIScriptError) { 1.383 + m.errorMessage = msg.errorMessage; 1.384 + m.sourceName = msg.sourceName; 1.385 + m.sourceLine = msg.sourceLine; 1.386 + m.lineNumber = msg.lineNumber; 1.387 + m.columnNumber = msg.columnNumber; 1.388 + m.category = msg.category; 1.389 + m.windowID = msg.outerWindowID; 1.390 + m.isScriptError = true; 1.391 + m.isWarning = ((msg.flags & Ci.nsIScriptError.warningFlag) === 1); 1.392 + m.isException = ((msg.flags & Ci.nsIScriptError.exceptionFlag) === 1); 1.393 + m.isStrict = ((msg.flags & Ci.nsIScriptError.strictFlag) === 1); 1.394 + } 1.395 + 1.396 + // expose all props of 'm' as read-only 1.397 + let expose = {}; 1.398 + for (let prop in m) 1.399 + expose[prop] = 'r'; 1.400 + m.__exposedProps__ = expose; 1.401 + Object.freeze(m); 1.402 + 1.403 + this.callback.call(undefined, m); 1.404 + 1.405 + if (!m.isScriptError && m.message === "SENTINEL") 1.406 + Services.console.unregisterListener(this); 1.407 + }, 1.408 + 1.409 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleListener]) 1.410 +}; 1.411 + 1.412 +function wrapCallback(cb) { 1.413 + return function SpecialPowersCallbackWrapper() { 1.414 + args = Array.prototype.map.call(arguments, wrapIfUnwrapped); 1.415 + return cb.apply(this, args); 1.416 + } 1.417 +} 1.418 + 1.419 +function wrapCallbackObject(obj) { 1.420 + wrapper = { __exposedProps__: ExposedPropsWaiver }; 1.421 + for (var i in obj) { 1.422 + if (typeof obj[i] == 'function') 1.423 + wrapper[i] = wrapCallback(obj[i]); 1.424 + else 1.425 + wrapper[i] = obj[i]; 1.426 + } 1.427 + return wrapper; 1.428 +} 1.429 + 1.430 +SpecialPowersAPI.prototype = { 1.431 + 1.432 + /* 1.433 + * Privileged object wrapping API 1.434 + * 1.435 + * Usage: 1.436 + * var wrapper = SpecialPowers.wrap(obj); 1.437 + * wrapper.privilegedMethod(); wrapper.privilegedProperty; 1.438 + * obj === SpecialPowers.unwrap(wrapper); 1.439 + * 1.440 + * These functions provide transparent access to privileged objects using 1.441 + * various pieces of deep SpiderMagic. Conceptually, a wrapper is just an 1.442 + * object containing a reference to the underlying object, where all method 1.443 + * calls and property accesses are transparently performed with the System 1.444 + * Principal. Moreover, objects obtained from the wrapper (including properties 1.445 + * and method return values) are wrapped automatically. Thus, after a single 1.446 + * call to SpecialPowers.wrap(), the wrapper layer is transitively maintained. 1.447 + * 1.448 + * Known Issues: 1.449 + * 1.450 + * - The wrapping function does not preserve identity, so 1.451 + * SpecialPowers.wrap(foo) !== SpecialPowers.wrap(foo). See bug 718543. 1.452 + * 1.453 + * - The wrapper cannot see expando properties on unprivileged DOM objects. 1.454 + * That is to say, the wrapper uses Xray delegation. 1.455 + * 1.456 + * - The wrapper sometimes guesses certain ES5 attributes for returned 1.457 + * properties. This is explained in a comment in the wrapper code above, 1.458 + * and shouldn't be a problem. 1.459 + */ 1.460 + wrap: wrapIfUnwrapped, 1.461 + unwrap: unwrapIfWrapped, 1.462 + isWrapper: isWrapper, 1.463 + 1.464 + /* 1.465 + * When content needs to pass a callback or a callback object to an API 1.466 + * accessed over SpecialPowers, that API may sometimes receive arguments for 1.467 + * whom it is forbidden to create a wrapper in content scopes. As such, we 1.468 + * need a layer to wrap the values in SpecialPowers wrappers before they ever 1.469 + * reach content. 1.470 + */ 1.471 + wrapCallback: wrapCallback, 1.472 + wrapCallbackObject: wrapCallbackObject, 1.473 + 1.474 + /* 1.475 + * Create blank privileged objects to use as out-params for privileged functions. 1.476 + */ 1.477 + createBlankObject: function () { 1.478 + var obj = new Object; 1.479 + obj.__exposedProps__ = ExposedPropsWaiver; 1.480 + return obj; 1.481 + }, 1.482 + 1.483 + /* 1.484 + * Because SpecialPowers wrappers don't preserve identity, comparing with == 1.485 + * can be hazardous. Sometimes we can just unwrap to compare, but sometimes 1.486 + * wrapping the underlying object into a content scope is forbidden. This 1.487 + * function strips any wrappers if they exist and compare the underlying 1.488 + * values. 1.489 + */ 1.490 + compare: function(a, b) { 1.491 + return unwrapIfWrapped(a) === unwrapIfWrapped(b); 1.492 + }, 1.493 + 1.494 + get MockFilePicker() { 1.495 + return MockFilePicker 1.496 + }, 1.497 + 1.498 + get MockColorPicker() { 1.499 + return MockColorPicker 1.500 + }, 1.501 + 1.502 + get MockPermissionPrompt() { 1.503 + return MockPermissionPrompt 1.504 + }, 1.505 + 1.506 + loadChromeScript: function (url) { 1.507 + // Create a unique id for this chrome script 1.508 + let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] 1.509 + .getService(Ci.nsIUUIDGenerator); 1.510 + let id = uuidGenerator.generateUUID().toString(); 1.511 + 1.512 + // Tells chrome code to evaluate this chrome script 1.513 + this._sendSyncMessage("SPLoadChromeScript", 1.514 + { url: url, id: id }); 1.515 + 1.516 + // Returns a MessageManager like API in order to be 1.517 + // able to communicate with this chrome script 1.518 + let listeners = []; 1.519 + let chromeScript = { 1.520 + addMessageListener: (name, listener) => { 1.521 + listeners.push({ name: name, listener: listener }); 1.522 + }, 1.523 + 1.524 + removeMessageListener: (name, listener) => { 1.525 + listeners = listeners.filter( 1.526 + o => (o.name != name || o.listener != listener) 1.527 + ); 1.528 + }, 1.529 + 1.530 + sendAsyncMessage: (name, message) => { 1.531 + this._sendSyncMessage("SPChromeScriptMessage", 1.532 + { id: id, name: name, message: message }); 1.533 + }, 1.534 + 1.535 + destroy: () => { 1.536 + listeners = []; 1.537 + this._removeMessageListener("SPChromeScriptMessage", chromeScript); 1.538 + this._removeMessageListener("SPChromeScriptAssert", chromeScript); 1.539 + }, 1.540 + 1.541 + receiveMessage: (aMessage) => { 1.542 + let messageId = aMessage.json.id; 1.543 + let name = aMessage.json.name; 1.544 + let message = aMessage.json.message; 1.545 + // Ignore message from other chrome script 1.546 + if (messageId != id) 1.547 + return; 1.548 + 1.549 + if (aMessage.name == "SPChromeScriptMessage") { 1.550 + listeners.filter(o => (o.name == name)) 1.551 + .forEach(o => o.listener(this.wrap(message))); 1.552 + } else if (aMessage.name == "SPChromeScriptAssert") { 1.553 + assert(aMessage.json); 1.554 + } 1.555 + } 1.556 + }; 1.557 + this._addMessageListener("SPChromeScriptMessage", chromeScript); 1.558 + this._addMessageListener("SPChromeScriptAssert", chromeScript); 1.559 + 1.560 + let assert = json => { 1.561 + // An assertion has been done in a mochitest chrome script 1.562 + let {url, err, message, stack} = json; 1.563 + 1.564 + // Try to fetch a test runner from the mochitest 1.565 + // in order to properly log these assertions and notify 1.566 + // all usefull log observers 1.567 + let window = this.window.get(); 1.568 + let parentRunner, repr = function (o) o; 1.569 + if (window) { 1.570 + window = window.wrappedJSObject; 1.571 + parentRunner = window.TestRunner; 1.572 + if (window.repr) { 1.573 + repr = window.repr; 1.574 + } 1.575 + } 1.576 + 1.577 + // Craft a mochitest-like report string 1.578 + var resultString = err ? "TEST-UNEXPECTED-FAIL" : "TEST-PASS"; 1.579 + var diagnostic = 1.580 + message ? message : 1.581 + ("assertion @ " + stack.filename + ":" + stack.lineNumber); 1.582 + if (err) { 1.583 + diagnostic += 1.584 + " - got " + repr(err.actual) + 1.585 + ", expected " + repr(err.expected) + 1.586 + " (operator " + err.operator + ")"; 1.587 + } 1.588 + var msg = [resultString, url, diagnostic].join(" | "); 1.589 + if (parentRunner) { 1.590 + if (err) { 1.591 + parentRunner.addFailedTest(url); 1.592 + parentRunner.error(msg); 1.593 + } else { 1.594 + parentRunner.log(msg); 1.595 + } 1.596 + } else { 1.597 + // When we are running only a single mochitest, there is no test runner 1.598 + dump(msg + "\n"); 1.599 + } 1.600 + }; 1.601 + 1.602 + return this.wrap(chromeScript); 1.603 + }, 1.604 + 1.605 + get Services() { 1.606 + return wrapPrivileged(Services); 1.607 + }, 1.608 + 1.609 + /* 1.610 + * In general, any Components object created for unprivileged scopes is 1.611 + * neutered (it implements nsIXPCComponentsBase, but not nsIXPCComponents). 1.612 + * We override this in certain legacy automation configurations (see the 1.613 + * implementation of getRawComponents() above), but don't want to support 1.614 + * it in cases where it isn't already required. 1.615 + * 1.616 + * In scopes with neutered Components, we don't have a natural referent for 1.617 + * things like SpecialPowers.Cc. So in those cases, we fall back to the 1.618 + * Components object from the SpecialPowers scope. This doesn't quite behave 1.619 + * the same way (in particular, SpecialPowers.Cc[foo].createInstance() will 1.620 + * create an instance in the SpecialPowers scope), but SpecialPowers wrapping 1.621 + * is already a YMMV / Whatever-It-Takes-To-Get-TBPL-Green sort of thing. 1.622 + * 1.623 + * It probably wouldn't be too much work to just make SpecialPowers.Components 1.624 + * unconditionally point to the Components object in the SpecialPowers scope. 1.625 + * Try will tell what needs to be fixed up. 1.626 + */ 1.627 + getFullComponents: function() { 1.628 + return typeof this.Components.classes == 'object' ? this.Components 1.629 + : Components; 1.630 + }, 1.631 + 1.632 + /* 1.633 + * Convenient shortcuts to the standard Components abbreviations. Note that 1.634 + * we don't SpecialPowers-wrap Components.interfaces, because it's available 1.635 + * to untrusted content, and wrapping it confuses QI and identity checks. 1.636 + */ 1.637 + get Cc() { return wrapPrivileged(this.getFullComponents()).classes; }, 1.638 + get Ci() { return this.Components.interfaces; }, 1.639 + get Cu() { return wrapPrivileged(this.getFullComponents()).utils; }, 1.640 + get Cr() { return wrapPrivileged(this.Components).results; }, 1.641 + 1.642 + /* 1.643 + * SpecialPowers.getRawComponents() allows content to get a reference to a 1.644 + * naked (and, in certain automation configurations, privileged) Components 1.645 + * object for its scope. 1.646 + * 1.647 + * SpecialPowers.getRawComponents(window) is defined as the global property 1.648 + * window.SpecialPowers.Components for convenience. 1.649 + */ 1.650 + getRawComponents: getRawComponents, 1.651 + 1.652 + getDOMWindowUtils: function(aWindow) { 1.653 + if (aWindow == this.window.get() && this.DOMWindowUtils != null) 1.654 + return this.DOMWindowUtils; 1.655 + 1.656 + return bindDOMWindowUtils(aWindow); 1.657 + }, 1.658 + 1.659 + removeExpectedCrashDumpFiles: function(aExpectingProcessCrash) { 1.660 + var success = true; 1.661 + if (aExpectingProcessCrash) { 1.662 + var message = { 1.663 + op: "delete-crash-dump-files", 1.664 + filenames: this._encounteredCrashDumpFiles 1.665 + }; 1.666 + if (!this._sendSyncMessage("SPProcessCrashService", message)[0]) { 1.667 + success = false; 1.668 + } 1.669 + } 1.670 + this._encounteredCrashDumpFiles.length = 0; 1.671 + return success; 1.672 + }, 1.673 + 1.674 + findUnexpectedCrashDumpFiles: function() { 1.675 + var self = this; 1.676 + var message = { 1.677 + op: "find-crash-dump-files", 1.678 + crashDumpFilesToIgnore: this._unexpectedCrashDumpFiles 1.679 + }; 1.680 + var crashDumpFiles = this._sendSyncMessage("SPProcessCrashService", message)[0]; 1.681 + crashDumpFiles.forEach(function(aFilename) { 1.682 + self._unexpectedCrashDumpFiles[aFilename] = true; 1.683 + }); 1.684 + return crashDumpFiles; 1.685 + }, 1.686 + 1.687 + _delayCallbackTwice: function(callback) { 1.688 + function delayedCallback() { 1.689 + function delayAgain() { 1.690 + content.window.setTimeout(callback, 0); 1.691 + } 1.692 + content.window.setTimeout(delayAgain, 0); 1.693 + } 1.694 + return delayedCallback; 1.695 + }, 1.696 + 1.697 + /* apply permissions to the system and when the test case is finished (SimpleTest.finish()) 1.698 + we will revert the permission back to the original. 1.699 + 1.700 + inPermissions is an array of objects where each object has a type, action, context, ex: 1.701 + [{'type': 'SystemXHR', 'allow': 1, 'context': document}, 1.702 + {'type': 'SystemXHR', 'allow': Ci.nsIPermissionManager.PROMPT_ACTION, 'context': document}] 1.703 + 1.704 + Allow can be a boolean value of true/false or ALLOW_ACTION/DENY_ACTION/PROMPT_ACTION/UNKNOWN_ACTION 1.705 + */ 1.706 + pushPermissions: function(inPermissions, callback) { 1.707 + var pendingPermissions = []; 1.708 + var cleanupPermissions = []; 1.709 + 1.710 + for (var p in inPermissions) { 1.711 + var permission = inPermissions[p]; 1.712 + var originalValue = Ci.nsIPermissionManager.UNKNOWN_ACTION; 1.713 + if (this.testPermission(permission.type, Ci.nsIPermissionManager.ALLOW_ACTION, permission.context)) { 1.714 + originalValue = Ci.nsIPermissionManager.ALLOW_ACTION; 1.715 + } else if (this.testPermission(permission.type, Ci.nsIPermissionManager.DENY_ACTION, permission.context)) { 1.716 + originalValue = Ci.nsIPermissionManager.DENY_ACTION; 1.717 + } else if (this.testPermission(permission.type, Ci.nsIPermissionManager.PROMPT_ACTION, permission.context)) { 1.718 + originalValue = Ci.nsIPermissionManager.PROMPT_ACTION; 1.719 + } else if (this.testPermission(permission.type, Ci.nsICookiePermission.ACCESS_SESSION, permission.context)) { 1.720 + originalValue = Ci.nsICookiePermission.ACCESS_SESSION; 1.721 + } else if (this.testPermission(permission.type, Ci.nsICookiePermission.ACCESS_ALLOW_FIRST_PARTY_ONLY, permission.context)) { 1.722 + originalValue = Ci.nsICookiePermission.ACCESS_ALLOW_FIRST_PARTY_ONLY; 1.723 + } else if (this.testPermission(permission.type, Ci.nsICookiePermission.ACCESS_LIMIT_THIRD_PARTY, permission.context)) { 1.724 + originalValue = Ci.nsICookiePermission.ACCESS_LIMIT_THIRD_PARTY; 1.725 + } 1.726 + 1.727 + let [url, appId, isInBrowserElement] = this._getInfoFromPermissionArg(permission.context); 1.728 + 1.729 + let perm; 1.730 + if (typeof permission.allow !== 'boolean') { 1.731 + perm = permission.allow; 1.732 + } else { 1.733 + perm = permission.allow ? Ci.nsIPermissionManager.ALLOW_ACTION 1.734 + : Ci.nsIPermissionManager.DENY_ACTION; 1.735 + } 1.736 + 1.737 + if (permission.remove == true) 1.738 + perm = Ci.nsIPermissionManager.UNKNOWN_ACTION; 1.739 + 1.740 + if (originalValue == perm) { 1.741 + continue; 1.742 + } 1.743 + 1.744 + var todo = {'op': 'add', 'type': permission.type, 'permission': perm, 'value': perm, 'url': url, 'appId': appId, 'isInBrowserElement': isInBrowserElement}; 1.745 + if (permission.remove == true) 1.746 + todo.op = 'remove'; 1.747 + 1.748 + pendingPermissions.push(todo); 1.749 + 1.750 + /* Push original permissions value or clear into cleanup array */ 1.751 + var cleanupTodo = {'op': 'add', 'type': permission.type, 'permission': perm, 'value': perm, 'url': url, 'appId': appId, 'isInBrowserElement': isInBrowserElement}; 1.752 + if (originalValue == Ci.nsIPermissionManager.UNKNOWN_ACTION) { 1.753 + cleanupTodo.op = 'remove'; 1.754 + } else { 1.755 + cleanupTodo.value = originalValue; 1.756 + cleanupTodo.permission = originalValue; 1.757 + } 1.758 + cleanupPermissions.push(cleanupTodo); 1.759 + } 1.760 + 1.761 + if (pendingPermissions.length > 0) { 1.762 + // The callback needs to be delayed twice. One delay is because the pref 1.763 + // service doesn't guarantee the order it calls its observers in, so it 1.764 + // may notify the observer holding the callback before the other 1.765 + // observers have been notified and given a chance to make the changes 1.766 + // that the callback checks for. The second delay is because pref 1.767 + // observers often defer making their changes by posting an event to the 1.768 + // event loop. 1.769 + this._permissionsUndoStack.push(cleanupPermissions); 1.770 + this._pendingPermissions.push([pendingPermissions, 1.771 + this._delayCallbackTwice(callback)]); 1.772 + this._applyPermissions(); 1.773 + } else { 1.774 + content.window.setTimeout(callback, 0); 1.775 + } 1.776 + }, 1.777 + 1.778 + popPermissions: function(callback) { 1.779 + if (this._permissionsUndoStack.length > 0) { 1.780 + // See pushPermissions comment regarding delay. 1.781 + let cb = callback ? this._delayCallbackTwice(callback) : null; 1.782 + /* Each pop from the stack will yield an object {op/type/permission/value/url/appid/isInBrowserElement} or null */ 1.783 + this._pendingPermissions.push([this._permissionsUndoStack.pop(), cb]); 1.784 + this._applyPermissions(); 1.785 + } else { 1.786 + content.window.setTimeout(callback, 0); 1.787 + } 1.788 + }, 1.789 + 1.790 + flushPermissions: function(callback) { 1.791 + while (this._permissionsUndoStack.length > 1) 1.792 + this.popPermissions(null); 1.793 + 1.794 + this.popPermissions(callback); 1.795 + }, 1.796 + 1.797 + 1.798 + _permissionObserver: { 1.799 + _lastPermission: {}, 1.800 + _callBack: null, 1.801 + _nextCallback: null, 1.802 + 1.803 + observe: function (aSubject, aTopic, aData) 1.804 + { 1.805 + if (aTopic == "perm-changed") { 1.806 + var permission = aSubject.QueryInterface(Ci.nsIPermission); 1.807 + if (permission.type == this._lastPermission.type) { 1.808 + var os = Components.classes["@mozilla.org/observer-service;1"] 1.809 + .getService(Components.interfaces.nsIObserverService); 1.810 + os.removeObserver(this, "perm-changed"); 1.811 + content.window.setTimeout(this._callback, 0); 1.812 + content.window.setTimeout(this._nextCallback, 0); 1.813 + } 1.814 + } 1.815 + } 1.816 + }, 1.817 + 1.818 + /* 1.819 + Iterate through one atomic set of permissions actions and perform allow/deny as appropriate. 1.820 + All actions performed must modify the relevant permission. 1.821 + */ 1.822 + _applyPermissions: function() { 1.823 + if (this._applyingPermissions || this._pendingPermissions.length <= 0) { 1.824 + return; 1.825 + } 1.826 + 1.827 + /* Set lock and get prefs from the _pendingPrefs queue */ 1.828 + this._applyingPermissions = true; 1.829 + var transaction = this._pendingPermissions.shift(); 1.830 + var pendingActions = transaction[0]; 1.831 + var callback = transaction[1]; 1.832 + var lastPermission = pendingActions[pendingActions.length-1]; 1.833 + 1.834 + var self = this; 1.835 + var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService); 1.836 + this._permissionObserver._lastPermission = lastPermission; 1.837 + this._permissionObserver._callback = callback; 1.838 + this._permissionObserver._nextCallback = function () { 1.839 + self._applyingPermissions = false; 1.840 + // Now apply any permissions that may have been queued while we were applying 1.841 + self._applyPermissions(); 1.842 + } 1.843 + 1.844 + os.addObserver(this._permissionObserver, "perm-changed", false); 1.845 + 1.846 + for (var idx in pendingActions) { 1.847 + var perm = pendingActions[idx]; 1.848 + this._sendSyncMessage('SPPermissionManager', perm)[0]; 1.849 + } 1.850 + }, 1.851 + 1.852 + /* 1.853 + * Take in a list of pref changes to make, and invoke |callback| once those 1.854 + * changes have taken effect. When the test finishes, these changes are 1.855 + * reverted. 1.856 + * 1.857 + * |inPrefs| must be an object with up to two properties: "set" and "clear". 1.858 + * pushPrefEnv will set prefs as indicated in |inPrefs.set| and will unset 1.859 + * the prefs indicated in |inPrefs.clear|. 1.860 + * 1.861 + * For example, you might pass |inPrefs| as: 1.862 + * 1.863 + * inPrefs = {'set': [['foo.bar', 2], ['magic.pref', 'baz']], 1.864 + * 'clear': [['clear.this'], ['also.this']] }; 1.865 + * 1.866 + * Notice that |set| and |clear| are both an array of arrays. In |set|, each 1.867 + * of the inner arrays must have the form [pref_name, value] or [pref_name, 1.868 + * value, iid]. (The latter form is used for prefs with "complex" values.) 1.869 + * 1.870 + * In |clear|, each inner array should have the form [pref_name]. 1.871 + * 1.872 + * If you set the same pref more than once (or both set and clear a pref), 1.873 + * the behavior of this method is undefined. 1.874 + * 1.875 + * (Implementation note: _prefEnvUndoStack is a stack of values to revert to, 1.876 + * not values which have been set!) 1.877 + * 1.878 + * TODO: complex values for original cleanup? 1.879 + * 1.880 + */ 1.881 + pushPrefEnv: function(inPrefs, callback) { 1.882 + var prefs = Components.classes["@mozilla.org/preferences-service;1"]. 1.883 + getService(Components.interfaces.nsIPrefBranch); 1.884 + 1.885 + var pref_string = []; 1.886 + pref_string[prefs.PREF_INT] = "INT"; 1.887 + pref_string[prefs.PREF_BOOL] = "BOOL"; 1.888 + pref_string[prefs.PREF_STRING] = "CHAR"; 1.889 + 1.890 + var pendingActions = []; 1.891 + var cleanupActions = []; 1.892 + 1.893 + for (var action in inPrefs) { /* set|clear */ 1.894 + for (var idx in inPrefs[action]) { 1.895 + var aPref = inPrefs[action][idx]; 1.896 + var prefName = aPref[0]; 1.897 + var prefValue = null; 1.898 + var prefIid = null; 1.899 + var prefType = prefs.PREF_INVALID; 1.900 + var originalValue = null; 1.901 + 1.902 + if (aPref.length == 3) { 1.903 + prefValue = aPref[1]; 1.904 + prefIid = aPref[2]; 1.905 + } else if (aPref.length == 2) { 1.906 + prefValue = aPref[1]; 1.907 + } 1.908 + 1.909 + /* If pref is not found or invalid it doesn't exist. */ 1.910 + if (prefs.getPrefType(prefName) != prefs.PREF_INVALID) { 1.911 + prefType = pref_string[prefs.getPrefType(prefName)]; 1.912 + if ((prefs.prefHasUserValue(prefName) && action == 'clear') || 1.913 + (action == 'set')) 1.914 + originalValue = this._getPref(prefName, prefType); 1.915 + } else if (action == 'set') { 1.916 + /* prefName doesn't exist, so 'clear' is pointless */ 1.917 + if (aPref.length == 3) { 1.918 + prefType = "COMPLEX"; 1.919 + } else if (aPref.length == 2) { 1.920 + if (typeof(prefValue) == "boolean") 1.921 + prefType = "BOOL"; 1.922 + else if (typeof(prefValue) == "number") 1.923 + prefType = "INT"; 1.924 + else if (typeof(prefValue) == "string") 1.925 + prefType = "CHAR"; 1.926 + } 1.927 + } 1.928 + 1.929 + /* PREF_INVALID: A non existing pref which we are clearing or invalid values for a set */ 1.930 + if (prefType == prefs.PREF_INVALID) 1.931 + continue; 1.932 + 1.933 + /* We are not going to set a pref if the value is the same */ 1.934 + if (originalValue == prefValue) 1.935 + continue; 1.936 + 1.937 + pendingActions.push({'action': action, 'type': prefType, 'name': prefName, 'value': prefValue, 'Iid': prefIid}); 1.938 + 1.939 + /* Push original preference value or clear into cleanup array */ 1.940 + var cleanupTodo = {'action': action, 'type': prefType, 'name': prefName, 'value': originalValue, 'Iid': prefIid}; 1.941 + if (originalValue == null) { 1.942 + cleanupTodo.action = 'clear'; 1.943 + } else { 1.944 + cleanupTodo.action = 'set'; 1.945 + } 1.946 + cleanupActions.push(cleanupTodo); 1.947 + } 1.948 + } 1.949 + 1.950 + if (pendingActions.length > 0) { 1.951 + // The callback needs to be delayed twice. One delay is because the pref 1.952 + // service doesn't guarantee the order it calls its observers in, so it 1.953 + // may notify the observer holding the callback before the other 1.954 + // observers have been notified and given a chance to make the changes 1.955 + // that the callback checks for. The second delay is because pref 1.956 + // observers often defer making their changes by posting an event to the 1.957 + // event loop. 1.958 + this._prefEnvUndoStack.push(cleanupActions); 1.959 + this._pendingPrefs.push([pendingActions, 1.960 + this._delayCallbackTwice(callback)]); 1.961 + this._applyPrefs(); 1.962 + } else { 1.963 + content.window.setTimeout(callback, 0); 1.964 + } 1.965 + }, 1.966 + 1.967 + popPrefEnv: function(callback) { 1.968 + if (this._prefEnvUndoStack.length > 0) { 1.969 + // See pushPrefEnv comment regarding delay. 1.970 + let cb = callback ? this._delayCallbackTwice(callback) : null; 1.971 + /* Each pop will have a valid block of preferences */ 1.972 + this._pendingPrefs.push([this._prefEnvUndoStack.pop(), cb]); 1.973 + this._applyPrefs(); 1.974 + } else { 1.975 + content.window.setTimeout(callback, 0); 1.976 + } 1.977 + }, 1.978 + 1.979 + flushPrefEnv: function(callback) { 1.980 + while (this._prefEnvUndoStack.length > 1) 1.981 + this.popPrefEnv(null); 1.982 + 1.983 + this.popPrefEnv(callback); 1.984 + }, 1.985 + 1.986 + /* 1.987 + Iterate through one atomic set of pref actions and perform sets/clears as appropriate. 1.988 + All actions performed must modify the relevant pref. 1.989 + */ 1.990 + _applyPrefs: function() { 1.991 + if (this._applyingPrefs || this._pendingPrefs.length <= 0) { 1.992 + return; 1.993 + } 1.994 + 1.995 + /* Set lock and get prefs from the _pendingPrefs queue */ 1.996 + this._applyingPrefs = true; 1.997 + var transaction = this._pendingPrefs.shift(); 1.998 + var pendingActions = transaction[0]; 1.999 + var callback = transaction[1]; 1.1000 + 1.1001 + var lastPref = pendingActions[pendingActions.length-1]; 1.1002 + 1.1003 + var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); 1.1004 + var self = this; 1.1005 + pb.addObserver(lastPref.name, function prefObs(subject, topic, data) { 1.1006 + pb.removeObserver(lastPref.name, prefObs); 1.1007 + 1.1008 + content.window.setTimeout(callback, 0); 1.1009 + content.window.setTimeout(function () { 1.1010 + self._applyingPrefs = false; 1.1011 + // Now apply any prefs that may have been queued while we were applying 1.1012 + self._applyPrefs(); 1.1013 + }, 0); 1.1014 + }, false); 1.1015 + 1.1016 + for (var idx in pendingActions) { 1.1017 + var pref = pendingActions[idx]; 1.1018 + if (pref.action == 'set') { 1.1019 + this._setPref(pref.name, pref.type, pref.value, pref.Iid); 1.1020 + } else if (pref.action == 'clear') { 1.1021 + this.clearUserPref(pref.name); 1.1022 + } 1.1023 + } 1.1024 + }, 1.1025 + 1.1026 + // Disables the app install prompt for the duration of this test. There is 1.1027 + // no need to re-enable the prompt at the end of the test. 1.1028 + // 1.1029 + // The provided callback is invoked once the prompt is disabled. 1.1030 + autoConfirmAppInstall: function(cb) { 1.1031 + this.pushPrefEnv({set: [['dom.mozApps.auto_confirm_install', true]]}, cb); 1.1032 + }, 1.1033 + 1.1034 + // Allow tests to disable the per platform app validity checks so we can 1.1035 + // test higher level WebApp functionality without full platform support. 1.1036 + setAllAppsLaunchable: function(launchable) { 1.1037 + this._sendSyncMessage("SPWebAppService", { 1.1038 + op: "set-launchable", 1.1039 + launchable: launchable 1.1040 + }); 1.1041 + }, 1.1042 + 1.1043 + // Restore the launchable property to its default value. 1.1044 + flushAllAppsLaunchable: function() { 1.1045 + this._sendSyncMessage("SPWebAppService", { 1.1046 + op: "set-launchable", 1.1047 + launchable: false 1.1048 + }); 1.1049 + }, 1.1050 + 1.1051 + _proxiedObservers: { 1.1052 + "specialpowers-http-notify-request": function(aMessage) { 1.1053 + let uri = aMessage.json.uri; 1.1054 + Services.obs.notifyObservers(null, "specialpowers-http-notify-request", uri); 1.1055 + }, 1.1056 + }, 1.1057 + 1.1058 + _addObserverProxy: function(notification) { 1.1059 + if (notification in this._proxiedObservers) { 1.1060 + this._addMessageListener(notification, this._proxiedObservers[notification]); 1.1061 + } 1.1062 + }, 1.1063 + 1.1064 + _removeObserverProxy: function(notification) { 1.1065 + if (notification in this._proxiedObservers) { 1.1066 + this._removeMessageListener(notification, this._proxiedObservers[notification]); 1.1067 + } 1.1068 + }, 1.1069 + 1.1070 + addObserver: function(obs, notification, weak) { 1.1071 + this._addObserverProxy(notification); 1.1072 + if (typeof obs == 'object' && obs.observe.name != 'SpecialPowersCallbackWrapper') 1.1073 + obs.observe = wrapCallback(obs.observe); 1.1074 + var obsvc = Cc['@mozilla.org/observer-service;1'] 1.1075 + .getService(Ci.nsIObserverService); 1.1076 + obsvc.addObserver(obs, notification, weak); 1.1077 + }, 1.1078 + removeObserver: function(obs, notification) { 1.1079 + this._removeObserverProxy(notification); 1.1080 + var obsvc = Cc['@mozilla.org/observer-service;1'] 1.1081 + .getService(Ci.nsIObserverService); 1.1082 + obsvc.removeObserver(obs, notification); 1.1083 + }, 1.1084 + notifyObservers: function(subject, topic, data) { 1.1085 + var obsvc = Cc['@mozilla.org/observer-service;1'] 1.1086 + .getService(Ci.nsIObserverService); 1.1087 + obsvc.notifyObservers(subject, topic, data); 1.1088 + }, 1.1089 + 1.1090 + can_QI: function(obj) { 1.1091 + return obj.QueryInterface !== undefined; 1.1092 + }, 1.1093 + do_QueryInterface: function(obj, iface) { 1.1094 + return obj.QueryInterface(Ci[iface]); 1.1095 + }, 1.1096 + 1.1097 + call_Instanceof: function (obj1, obj2) { 1.1098 + obj1=unwrapIfWrapped(obj1); 1.1099 + obj2=unwrapIfWrapped(obj2); 1.1100 + return obj1 instanceof obj2; 1.1101 + }, 1.1102 + 1.1103 + // Returns a privileged getter from an object. GetOwnPropertyDescriptor does 1.1104 + // not work here because xray wrappers don't properly implement it. 1.1105 + // 1.1106 + // This terribleness is used by content/base/test/test_object.html because 1.1107 + // <object> and <embed> tags will spawn plugins if their prototype is touched, 1.1108 + // so we need to get and cache the getter of |hasRunningPlugin| if we want to 1.1109 + // call it without paradoxically spawning the plugin. 1.1110 + do_lookupGetter: function(obj, name) { 1.1111 + return Object.prototype.__lookupGetter__.call(obj, name); 1.1112 + }, 1.1113 + 1.1114 + // Mimic the get*Pref API 1.1115 + getBoolPref: function(aPrefName) { 1.1116 + return (this._getPref(aPrefName, 'BOOL')); 1.1117 + }, 1.1118 + getIntPref: function(aPrefName) { 1.1119 + return (this._getPref(aPrefName, 'INT')); 1.1120 + }, 1.1121 + getCharPref: function(aPrefName) { 1.1122 + return (this._getPref(aPrefName, 'CHAR')); 1.1123 + }, 1.1124 + getComplexValue: function(aPrefName, aIid) { 1.1125 + return (this._getPref(aPrefName, 'COMPLEX', aIid)); 1.1126 + }, 1.1127 + 1.1128 + // Mimic the set*Pref API 1.1129 + setBoolPref: function(aPrefName, aValue) { 1.1130 + return (this._setPref(aPrefName, 'BOOL', aValue)); 1.1131 + }, 1.1132 + setIntPref: function(aPrefName, aValue) { 1.1133 + return (this._setPref(aPrefName, 'INT', aValue)); 1.1134 + }, 1.1135 + setCharPref: function(aPrefName, aValue) { 1.1136 + return (this._setPref(aPrefName, 'CHAR', aValue)); 1.1137 + }, 1.1138 + setComplexValue: function(aPrefName, aIid, aValue) { 1.1139 + return (this._setPref(aPrefName, 'COMPLEX', aValue, aIid)); 1.1140 + }, 1.1141 + 1.1142 + // Mimic the clearUserPref API 1.1143 + clearUserPref: function(aPrefName) { 1.1144 + var msg = {'op':'clear', 'prefName': aPrefName, 'prefType': ""}; 1.1145 + this._sendSyncMessage('SPPrefService', msg); 1.1146 + }, 1.1147 + 1.1148 + // Private pref functions to communicate to chrome 1.1149 + _getPref: function(aPrefName, aPrefType, aIid) { 1.1150 + var msg = {}; 1.1151 + if (aIid) { 1.1152 + // Overloading prefValue to handle complex prefs 1.1153 + msg = {'op':'get', 'prefName': aPrefName, 'prefType':aPrefType, 'prefValue':[aIid]}; 1.1154 + } else { 1.1155 + msg = {'op':'get', 'prefName': aPrefName,'prefType': aPrefType}; 1.1156 + } 1.1157 + var val = this._sendSyncMessage('SPPrefService', msg); 1.1158 + 1.1159 + if (val == null || val[0] == null) 1.1160 + throw "Error getting pref"; 1.1161 + return val[0]; 1.1162 + }, 1.1163 + _setPref: function(aPrefName, aPrefType, aValue, aIid) { 1.1164 + var msg = {}; 1.1165 + if (aIid) { 1.1166 + msg = {'op':'set','prefName':aPrefName, 'prefType': aPrefType, 'prefValue': [aIid,aValue]}; 1.1167 + } else { 1.1168 + msg = {'op':'set', 'prefName': aPrefName, 'prefType': aPrefType, 'prefValue': aValue}; 1.1169 + } 1.1170 + return(this._sendSyncMessage('SPPrefService', msg)[0]); 1.1171 + }, 1.1172 + 1.1173 + _getDocShell: function(window) { 1.1174 + return window.QueryInterface(Ci.nsIInterfaceRequestor) 1.1175 + .getInterface(Ci.nsIWebNavigation) 1.1176 + .QueryInterface(Ci.nsIDocShell); 1.1177 + }, 1.1178 + _getMUDV: function(window) { 1.1179 + return this._getDocShell(window).contentViewer 1.1180 + .QueryInterface(Ci.nsIMarkupDocumentViewer); 1.1181 + }, 1.1182 + //XXX: these APIs really ought to be removed, they're not e10s-safe. 1.1183 + // (also they're pretty Firefox-specific) 1.1184 + _getTopChromeWindow: function(window) { 1.1185 + return window.QueryInterface(Ci.nsIInterfaceRequestor) 1.1186 + .getInterface(Ci.nsIWebNavigation) 1.1187 + .QueryInterface(Ci.nsIDocShellTreeItem) 1.1188 + .rootTreeItem 1.1189 + .QueryInterface(Ci.nsIInterfaceRequestor) 1.1190 + .getInterface(Ci.nsIDOMWindow) 1.1191 + .QueryInterface(Ci.nsIDOMChromeWindow); 1.1192 + }, 1.1193 + _getAutoCompletePopup: function(window) { 1.1194 + return this._getTopChromeWindow(window).document 1.1195 + .getElementById("PopupAutoComplete"); 1.1196 + }, 1.1197 + addAutoCompletePopupEventListener: function(window, eventname, listener) { 1.1198 + this._getAutoCompletePopup(window).addEventListener(eventname, 1.1199 + listener, 1.1200 + false); 1.1201 + }, 1.1202 + removeAutoCompletePopupEventListener: function(window, eventname, listener) { 1.1203 + this._getAutoCompletePopup(window).removeEventListener(eventname, 1.1204 + listener, 1.1205 + false); 1.1206 + }, 1.1207 + get formHistory() { 1.1208 + let tmp = {}; 1.1209 + Cu.import("resource://gre/modules/FormHistory.jsm", tmp); 1.1210 + return wrapPrivileged(tmp.FormHistory); 1.1211 + }, 1.1212 + getFormFillController: function(window) { 1.1213 + return Components.classes["@mozilla.org/satchel/form-fill-controller;1"] 1.1214 + .getService(Components.interfaces.nsIFormFillController); 1.1215 + }, 1.1216 + attachFormFillControllerTo: function(window) { 1.1217 + this.getFormFillController() 1.1218 + .attachToBrowser(this._getDocShell(window), 1.1219 + this._getAutoCompletePopup(window)); 1.1220 + }, 1.1221 + detachFormFillControllerFrom: function(window) { 1.1222 + this.getFormFillController().detachFromBrowser(this._getDocShell(window)); 1.1223 + }, 1.1224 + isBackButtonEnabled: function(window) { 1.1225 + return !this._getTopChromeWindow(window).document 1.1226 + .getElementById("Browser:Back") 1.1227 + .hasAttribute("disabled"); 1.1228 + }, 1.1229 + //XXX end of problematic APIs 1.1230 + 1.1231 + addChromeEventListener: function(type, listener, capture, allowUntrusted) { 1.1232 + addEventListener(type, listener, capture, allowUntrusted); 1.1233 + }, 1.1234 + removeChromeEventListener: function(type, listener, capture) { 1.1235 + removeEventListener(type, listener, capture); 1.1236 + }, 1.1237 + 1.1238 + // Note: each call to registerConsoleListener MUST be paired with a 1.1239 + // call to postConsoleSentinel; when the callback receives the 1.1240 + // sentinel it will unregister itself (_after_ calling the 1.1241 + // callback). SimpleTest.expectConsoleMessages does this for you. 1.1242 + // If you register more than one console listener, a call to 1.1243 + // postConsoleSentinel will zap all of them. 1.1244 + registerConsoleListener: function(callback) { 1.1245 + let listener = new SPConsoleListener(callback); 1.1246 + Services.console.registerListener(listener); 1.1247 + }, 1.1248 + postConsoleSentinel: function() { 1.1249 + Services.console.logStringMessage("SENTINEL"); 1.1250 + }, 1.1251 + resetConsole: function() { 1.1252 + Services.console.reset(); 1.1253 + }, 1.1254 + 1.1255 + getMaxLineBoxWidth: function(window) { 1.1256 + return this._getMUDV(window).maxLineBoxWidth; 1.1257 + }, 1.1258 + 1.1259 + setMaxLineBoxWidth: function(window, width) { 1.1260 + this._getMUDV(window).changeMaxLineBoxWidth(width); 1.1261 + }, 1.1262 + 1.1263 + getFullZoom: function(window) { 1.1264 + return this._getMUDV(window).fullZoom; 1.1265 + }, 1.1266 + setFullZoom: function(window, zoom) { 1.1267 + this._getMUDV(window).fullZoom = zoom; 1.1268 + }, 1.1269 + getTextZoom: function(window) { 1.1270 + return this._getMUDV(window).textZoom; 1.1271 + }, 1.1272 + setTextZoom: function(window, zoom) { 1.1273 + this._getMUDV(window).textZoom = zoom; 1.1274 + }, 1.1275 + 1.1276 + emulateMedium: function(window, mediaType) { 1.1277 + this._getMUDV(window).emulateMedium(mediaType); 1.1278 + }, 1.1279 + stopEmulatingMedium: function(window) { 1.1280 + this._getMUDV(window).stopEmulatingMedium(); 1.1281 + }, 1.1282 + 1.1283 + snapshotWindowWithOptions: function (win, rect, bgcolor, options) { 1.1284 + var el = this.window.get().document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); 1.1285 + if (rect === undefined) { 1.1286 + rect = { top: win.scrollY, left: win.scrollX, 1.1287 + width: win.innerWidth, height: win.innerHeight }; 1.1288 + } 1.1289 + if (bgcolor === undefined) { 1.1290 + bgcolor = "rgb(255,255,255)"; 1.1291 + } 1.1292 + if (options === undefined) { 1.1293 + options = { }; 1.1294 + } 1.1295 + 1.1296 + el.width = rect.width; 1.1297 + el.height = rect.height; 1.1298 + var ctx = el.getContext("2d"); 1.1299 + var flags = 0; 1.1300 + 1.1301 + for (var option in options) { 1.1302 + flags |= options[option] && ctx[option]; 1.1303 + } 1.1304 + 1.1305 + ctx.drawWindow(win, 1.1306 + rect.left, rect.top, rect.width, rect.height, 1.1307 + bgcolor, 1.1308 + flags); 1.1309 + return el; 1.1310 + }, 1.1311 + 1.1312 + snapshotWindow: function (win, withCaret, rect, bgcolor) { 1.1313 + return this.snapshotWindowWithOptions(win, rect, bgcolor, 1.1314 + { DRAWWINDOW_DRAW_CARET: withCaret }); 1.1315 + }, 1.1316 + 1.1317 + snapshotRect: function (win, rect, bgcolor) { 1.1318 + return this.snapshotWindowWithOptions(win, rect, bgcolor); 1.1319 + }, 1.1320 + 1.1321 + gc: function() { 1.1322 + this.DOMWindowUtils.garbageCollect(); 1.1323 + }, 1.1324 + 1.1325 + forceGC: function() { 1.1326 + Cu.forceGC(); 1.1327 + }, 1.1328 + 1.1329 + forceCC: function() { 1.1330 + Cu.forceCC(); 1.1331 + }, 1.1332 + 1.1333 + // Due to various dependencies between JS objects and C++ objects, an ordinary 1.1334 + // forceGC doesn't necessarily clear all unused objects, thus the GC and CC 1.1335 + // needs to run several times and when no other JS is running. 1.1336 + // The current number of iterations has been determined according to massive 1.1337 + // cross platform testing. 1.1338 + exactGC: function(win, callback) { 1.1339 + var self = this; 1.1340 + let count = 0; 1.1341 + 1.1342 + function doPreciseGCandCC() { 1.1343 + function scheduledGCCallback() { 1.1344 + self.getDOMWindowUtils(win).cycleCollect(); 1.1345 + 1.1346 + if (++count < 2) { 1.1347 + doPreciseGCandCC(); 1.1348 + } else { 1.1349 + callback(); 1.1350 + } 1.1351 + } 1.1352 + 1.1353 + Cu.schedulePreciseGC(scheduledGCCallback); 1.1354 + } 1.1355 + 1.1356 + doPreciseGCandCC(); 1.1357 + }, 1.1358 + 1.1359 + setGCZeal: function(zeal) { 1.1360 + Cu.setGCZeal(zeal); 1.1361 + }, 1.1362 + 1.1363 + isMainProcess: function() { 1.1364 + try { 1.1365 + return Cc["@mozilla.org/xre/app-info;1"]. 1.1366 + getService(Ci.nsIXULRuntime). 1.1367 + processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; 1.1368 + } catch (e) { } 1.1369 + return true; 1.1370 + }, 1.1371 + 1.1372 + _xpcomabi: null, 1.1373 + 1.1374 + get XPCOMABI() { 1.1375 + if (this._xpcomabi != null) 1.1376 + return this._xpcomabi; 1.1377 + 1.1378 + var xulRuntime = Cc["@mozilla.org/xre/app-info;1"] 1.1379 + .getService(Components.interfaces.nsIXULAppInfo) 1.1380 + .QueryInterface(Components.interfaces.nsIXULRuntime); 1.1381 + 1.1382 + this._xpcomabi = xulRuntime.XPCOMABI; 1.1383 + return this._xpcomabi; 1.1384 + }, 1.1385 + 1.1386 + // The optional aWin parameter allows the caller to specify a given window in 1.1387 + // whose scope the runnable should be dispatched. If aFun throws, the 1.1388 + // exception will be reported to aWin. 1.1389 + executeSoon: function(aFun, aWin) { 1.1390 + // Create the runnable in the scope of aWin to avoid running into COWs. 1.1391 + var runnable = {}; 1.1392 + if (aWin) 1.1393 + runnable = Cu.createObjectIn(aWin); 1.1394 + runnable.run = aFun; 1.1395 + Cu.dispatch(runnable, aWin); 1.1396 + }, 1.1397 + 1.1398 + _os: null, 1.1399 + 1.1400 + get OS() { 1.1401 + if (this._os != null) 1.1402 + return this._os; 1.1403 + 1.1404 + var xulRuntime = Cc["@mozilla.org/xre/app-info;1"] 1.1405 + .getService(Components.interfaces.nsIXULAppInfo) 1.1406 + .QueryInterface(Components.interfaces.nsIXULRuntime); 1.1407 + 1.1408 + this._os = xulRuntime.OS; 1.1409 + return this._os; 1.1410 + }, 1.1411 + 1.1412 + addSystemEventListener: function(target, type, listener, useCapture) { 1.1413 + Cc["@mozilla.org/eventlistenerservice;1"]. 1.1414 + getService(Ci.nsIEventListenerService). 1.1415 + addSystemEventListener(target, type, listener, useCapture); 1.1416 + }, 1.1417 + removeSystemEventListener: function(target, type, listener, useCapture) { 1.1418 + Cc["@mozilla.org/eventlistenerservice;1"]. 1.1419 + getService(Ci.nsIEventListenerService). 1.1420 + removeSystemEventListener(target, type, listener, useCapture); 1.1421 + }, 1.1422 + 1.1423 + getDOMRequestService: function() { 1.1424 + var serv = Cc["@mozilla.org/dom/dom-request-service;1"]. 1.1425 + getService(Ci.nsIDOMRequestService); 1.1426 + var res = { __exposedProps__: {} }; 1.1427 + var props = ["createRequest", "createCursor", "fireError", "fireSuccess", 1.1428 + "fireDone", "fireDetailedError"]; 1.1429 + for (i in props) { 1.1430 + let prop = props[i]; 1.1431 + res[prop] = function() { return serv[prop].apply(serv, arguments) }; 1.1432 + res.__exposedProps__[prop] = "r"; 1.1433 + } 1.1434 + return res; 1.1435 + }, 1.1436 + 1.1437 + setLogFile: function(path) { 1.1438 + this._mfl = new MozillaFileLogger(path); 1.1439 + }, 1.1440 + 1.1441 + log: function(data) { 1.1442 + this._mfl.log(data); 1.1443 + }, 1.1444 + 1.1445 + closeLogFile: function() { 1.1446 + this._mfl.close(); 1.1447 + }, 1.1448 + 1.1449 + addCategoryEntry: function(category, entry, value, persists, replace) { 1.1450 + Components.classes["@mozilla.org/categorymanager;1"]. 1.1451 + getService(Components.interfaces.nsICategoryManager). 1.1452 + addCategoryEntry(category, entry, value, persists, replace); 1.1453 + }, 1.1454 + 1.1455 + deleteCategoryEntry: function(category, entry, persists) { 1.1456 + Components.classes["@mozilla.org/categorymanager;1"]. 1.1457 + getService(Components.interfaces.nsICategoryManager). 1.1458 + deleteCategoryEntry(category, entry, persists); 1.1459 + }, 1.1460 + 1.1461 + copyString: function(str, doc) { 1.1462 + Components.classes["@mozilla.org/widget/clipboardhelper;1"]. 1.1463 + getService(Components.interfaces.nsIClipboardHelper). 1.1464 + copyString(str, doc); 1.1465 + }, 1.1466 + 1.1467 + openDialog: function(win, args) { 1.1468 + return win.openDialog.apply(win, args); 1.1469 + }, 1.1470 + 1.1471 + // :jdm gets credit for this. ex: getPrivilegedProps(window, 'location.href'); 1.1472 + getPrivilegedProps: function(obj, props) { 1.1473 + var parts = props.split('.'); 1.1474 + 1.1475 + for (var i = 0; i < parts.length; i++) { 1.1476 + var p = parts[i]; 1.1477 + if (obj[p]) { 1.1478 + obj = obj[p]; 1.1479 + } else { 1.1480 + return null; 1.1481 + } 1.1482 + } 1.1483 + return obj; 1.1484 + }, 1.1485 + 1.1486 + get focusManager() { 1.1487 + if (this._fm != null) 1.1488 + return this._fm; 1.1489 + 1.1490 + this._fm = Components.classes["@mozilla.org/focus-manager;1"]. 1.1491 + getService(Components.interfaces.nsIFocusManager); 1.1492 + 1.1493 + return this._fm; 1.1494 + }, 1.1495 + 1.1496 + getFocusedElementForWindow: function(targetWindow, aDeep, childTargetWindow) { 1.1497 + return this.focusManager.getFocusedElementForWindow(targetWindow, aDeep, childTargetWindow); 1.1498 + }, 1.1499 + 1.1500 + activeWindow: function() { 1.1501 + return this.focusManager.activeWindow; 1.1502 + }, 1.1503 + 1.1504 + focusedWindow: function() { 1.1505 + return this.focusManager.focusedWindow; 1.1506 + }, 1.1507 + 1.1508 + focus: function(aWindow) { 1.1509 + // This is called inside TestRunner._makeIframe without aWindow, because of assertions in oop mochitests 1.1510 + // With aWindow, it is called in SimpleTest.waitForFocus to allow popup window opener focus switching 1.1511 + if (aWindow) 1.1512 + aWindow.focus(); 1.1513 + sendAsyncMessage("SpecialPowers.Focus", {}); 1.1514 + }, 1.1515 + 1.1516 + getClipboardData: function(flavor, whichClipboard) { 1.1517 + if (this._cb == null) 1.1518 + this._cb = Components.classes["@mozilla.org/widget/clipboard;1"]. 1.1519 + getService(Components.interfaces.nsIClipboard); 1.1520 + if (whichClipboard === undefined) 1.1521 + whichClipboard = this._cb.kGlobalClipboard; 1.1522 + 1.1523 + var xferable = Components.classes["@mozilla.org/widget/transferable;1"]. 1.1524 + createInstance(Components.interfaces.nsITransferable); 1.1525 + xferable.init(this._getDocShell(content.window) 1.1526 + .QueryInterface(Components.interfaces.nsILoadContext)); 1.1527 + xferable.addDataFlavor(flavor); 1.1528 + this._cb.getData(xferable, whichClipboard); 1.1529 + var data = {}; 1.1530 + try { 1.1531 + xferable.getTransferData(flavor, data, {}); 1.1532 + } catch (e) {} 1.1533 + data = data.value || null; 1.1534 + if (data == null) 1.1535 + return ""; 1.1536 + 1.1537 + return data.QueryInterface(Components.interfaces.nsISupportsString).data; 1.1538 + }, 1.1539 + 1.1540 + clipboardCopyString: function(preExpectedVal, doc) { 1.1541 + var cbHelperSvc = Components.classes["@mozilla.org/widget/clipboardhelper;1"]. 1.1542 + getService(Components.interfaces.nsIClipboardHelper); 1.1543 + cbHelperSvc.copyString(preExpectedVal, doc); 1.1544 + }, 1.1545 + 1.1546 + supportsSelectionClipboard: function() { 1.1547 + if (this._cb == null) { 1.1548 + this._cb = Components.classes["@mozilla.org/widget/clipboard;1"]. 1.1549 + getService(Components.interfaces.nsIClipboard); 1.1550 + } 1.1551 + return this._cb.supportsSelectionClipboard(); 1.1552 + }, 1.1553 + 1.1554 + swapFactoryRegistration: function(cid, contractID, newFactory, oldFactory) { 1.1555 + var componentRegistrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar); 1.1556 + 1.1557 + var unregisterFactory = newFactory; 1.1558 + var registerFactory = oldFactory; 1.1559 + 1.1560 + if (cid == null) { 1.1561 + if (contractID != null) { 1.1562 + cid = componentRegistrar.contractIDToCID(contractID); 1.1563 + oldFactory = Components.manager.getClassObject(Components.classes[contractID], 1.1564 + Components.interfaces.nsIFactory); 1.1565 + } else { 1.1566 + return {'error': "trying to register a new contract ID: Missing contractID"}; 1.1567 + } 1.1568 + 1.1569 + unregisterFactory = oldFactory; 1.1570 + registerFactory = newFactory; 1.1571 + } 1.1572 + componentRegistrar.unregisterFactory(cid, 1.1573 + unregisterFactory); 1.1574 + 1.1575 + // Restore the original factory. 1.1576 + componentRegistrar.registerFactory(cid, 1.1577 + "", 1.1578 + contractID, 1.1579 + registerFactory); 1.1580 + return {'cid':cid, 'originalFactory':oldFactory}; 1.1581 + }, 1.1582 + 1.1583 + _getElement: function(aWindow, id) { 1.1584 + return ((typeof(id) == "string") ? 1.1585 + aWindow.document.getElementById(id) : id); 1.1586 + }, 1.1587 + 1.1588 + dispatchEvent: function(aWindow, target, event) { 1.1589 + var el = this._getElement(aWindow, target); 1.1590 + return el.dispatchEvent(event); 1.1591 + }, 1.1592 + 1.1593 + get isDebugBuild() { 1.1594 + delete this.isDebugBuild; 1.1595 + var debug = Cc["@mozilla.org/xpcom/debug;1"].getService(Ci.nsIDebug2); 1.1596 + return this.isDebugBuild = debug.isDebugBuild; 1.1597 + }, 1.1598 + assertionCount: function() { 1.1599 + var debugsvc = Cc['@mozilla.org/xpcom/debug;1'].getService(Ci.nsIDebug2); 1.1600 + return debugsvc.assertionCount; 1.1601 + }, 1.1602 + 1.1603 + /** 1.1604 + * Get the message manager associated with an <iframe mozbrowser>. 1.1605 + */ 1.1606 + getBrowserFrameMessageManager: function(aFrameElement) { 1.1607 + return this.wrap(aFrameElement.QueryInterface(Ci.nsIFrameLoaderOwner) 1.1608 + .frameLoader 1.1609 + .messageManager); 1.1610 + }, 1.1611 + 1.1612 + setFullscreenAllowed: function(document) { 1.1613 + var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager); 1.1614 + pm.addFromPrincipal(document.nodePrincipal, "fullscreen", Ci.nsIPermissionManager.ALLOW_ACTION); 1.1615 + var obsvc = Cc['@mozilla.org/observer-service;1'] 1.1616 + .getService(Ci.nsIObserverService); 1.1617 + obsvc.notifyObservers(document, "fullscreen-approved", null); 1.1618 + }, 1.1619 + 1.1620 + removeFullscreenAllowed: function(document) { 1.1621 + var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager); 1.1622 + pm.removeFromPrincipal(document.nodePrincipal, "fullscreen"); 1.1623 + }, 1.1624 + 1.1625 + _getInfoFromPermissionArg: function(arg) { 1.1626 + let url = ""; 1.1627 + let appId = Ci.nsIScriptSecurityManager.NO_APP_ID; 1.1628 + let isInBrowserElement = false; 1.1629 + 1.1630 + if (typeof(arg) == "string") { 1.1631 + // It's an URL. 1.1632 + url = Cc["@mozilla.org/network/io-service;1"] 1.1633 + .getService(Ci.nsIIOService) 1.1634 + .newURI(arg, null, null) 1.1635 + .spec; 1.1636 + } else if (arg.manifestURL) { 1.1637 + // It's a thing representing an app. 1.1638 + let appsSvc = Cc["@mozilla.org/AppsService;1"] 1.1639 + .getService(Ci.nsIAppsService) 1.1640 + let app = appsSvc.getAppByManifestURL(arg.manifestURL); 1.1641 + 1.1642 + if (!app) { 1.1643 + throw "No app for this manifest!"; 1.1644 + } 1.1645 + 1.1646 + appId = appsSvc.getAppLocalIdByManifestURL(arg.manifestURL); 1.1647 + url = app.origin; 1.1648 + isInBrowserElement = arg.isInBrowserElement || false; 1.1649 + } else if (arg.nodePrincipal) { 1.1650 + // It's a document. 1.1651 + url = arg.nodePrincipal.URI.spec; 1.1652 + appId = arg.nodePrincipal.appId; 1.1653 + isInBrowserElement = arg.nodePrincipal.isInBrowserElement; 1.1654 + } else { 1.1655 + url = arg.url; 1.1656 + appId = arg.appId; 1.1657 + isInBrowserElement = arg.isInBrowserElement; 1.1658 + } 1.1659 + 1.1660 + return [ url, appId, isInBrowserElement ]; 1.1661 + }, 1.1662 + 1.1663 + addPermission: function(type, allow, arg) { 1.1664 + let [url, appId, isInBrowserElement] = this._getInfoFromPermissionArg(arg); 1.1665 + 1.1666 + let permission; 1.1667 + if (typeof allow !== 'boolean') { 1.1668 + permission = allow; 1.1669 + } else { 1.1670 + permission = allow ? Ci.nsIPermissionManager.ALLOW_ACTION 1.1671 + : Ci.nsIPermissionManager.DENY_ACTION; 1.1672 + } 1.1673 + 1.1674 + var msg = { 1.1675 + 'op': 'add', 1.1676 + 'type': type, 1.1677 + 'permission': permission, 1.1678 + 'url': url, 1.1679 + 'appId': appId, 1.1680 + 'isInBrowserElement': isInBrowserElement 1.1681 + }; 1.1682 + 1.1683 + this._sendSyncMessage('SPPermissionManager', msg); 1.1684 + }, 1.1685 + 1.1686 + removePermission: function(type, arg) { 1.1687 + let [url, appId, isInBrowserElement] = this._getInfoFromPermissionArg(arg); 1.1688 + 1.1689 + var msg = { 1.1690 + 'op': 'remove', 1.1691 + 'type': type, 1.1692 + 'url': url, 1.1693 + 'appId': appId, 1.1694 + 'isInBrowserElement': isInBrowserElement 1.1695 + }; 1.1696 + 1.1697 + this._sendSyncMessage('SPPermissionManager', msg); 1.1698 + }, 1.1699 + 1.1700 + hasPermission: function (type, arg) { 1.1701 + let [url, appId, isInBrowserElement] = this._getInfoFromPermissionArg(arg); 1.1702 + 1.1703 + var msg = { 1.1704 + 'op': 'has', 1.1705 + 'type': type, 1.1706 + 'url': url, 1.1707 + 'appId': appId, 1.1708 + 'isInBrowserElement': isInBrowserElement 1.1709 + }; 1.1710 + 1.1711 + return this._sendSyncMessage('SPPermissionManager', msg)[0]; 1.1712 + }, 1.1713 + testPermission: function (type, value, arg) { 1.1714 + let [url, appId, isInBrowserElement] = this._getInfoFromPermissionArg(arg); 1.1715 + 1.1716 + var msg = { 1.1717 + 'op': 'test', 1.1718 + 'type': type, 1.1719 + 'value': value, 1.1720 + 'url': url, 1.1721 + 'appId': appId, 1.1722 + 'isInBrowserElement': isInBrowserElement 1.1723 + }; 1.1724 + return this._sendSyncMessage('SPPermissionManager', msg)[0]; 1.1725 + }, 1.1726 + 1.1727 + getMozFullPath: function(file) { 1.1728 + return file.mozFullPath; 1.1729 + }, 1.1730 + 1.1731 + isWindowPrivate: function(win) { 1.1732 + return PrivateBrowsingUtils.isWindowPrivate(win); 1.1733 + }, 1.1734 + 1.1735 + notifyObserversInParentProcess: function(subject, topic, data) { 1.1736 + if (subject) { 1.1737 + throw new Error("Can't send subject to another process!"); 1.1738 + } 1.1739 + if (this.isMainProcess()) { 1.1740 + this.notifyObservers(subject, topic, data); 1.1741 + return; 1.1742 + } 1.1743 + var msg = { 1.1744 + 'op': 'notify', 1.1745 + 'observerTopic': topic, 1.1746 + 'observerData': data 1.1747 + }; 1.1748 + this._sendSyncMessage('SPObserverService', msg); 1.1749 + }, 1.1750 +}; 1.1751 + 1.1752 +this.SpecialPowersAPI = SpecialPowersAPI; 1.1753 +this.bindDOMWindowUtils = bindDOMWindowUtils; 1.1754 +this.getRawComponents = getRawComponents;