1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/browser-element/BrowserElementChildPreload.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1150 @@ 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 file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +dump("######################## BrowserElementChildPreload.js loaded\n"); 1.11 + 1.12 +var BrowserElementIsReady = false; 1.13 + 1.14 +let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; 1.15 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.16 +Cu.import("resource://gre/modules/Services.jsm"); 1.17 +Cu.import("resource://gre/modules/BrowserElementPromptService.jsm"); 1.18 + 1.19 +// Event whitelisted for bubbling. 1.20 +let whitelistedEvents = [ 1.21 + Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE, // Back button. 1.22 + Ci.nsIDOMKeyEvent.DOM_VK_SLEEP, // Power button. 1.23 + Ci.nsIDOMKeyEvent.DOM_VK_CONTEXT_MENU, 1.24 + Ci.nsIDOMKeyEvent.DOM_VK_F5, // Search button. 1.25 + Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP, // Volume up. 1.26 + Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN // Volume down. 1.27 +]; 1.28 + 1.29 +function debug(msg) { 1.30 + //dump("BrowserElementChildPreload - " + msg + "\n"); 1.31 +} 1.32 + 1.33 +function sendAsyncMsg(msg, data) { 1.34 + // Ensure that we don't send any messages before BrowserElementChild.js 1.35 + // finishes loading. 1.36 + if (!BrowserElementIsReady) 1.37 + return; 1.38 + 1.39 + if (!data) { 1.40 + data = { }; 1.41 + } 1.42 + 1.43 + data.msg_name = msg; 1.44 + sendAsyncMessage('browser-element-api:call', data); 1.45 +} 1.46 + 1.47 +function sendSyncMsg(msg, data) { 1.48 + // Ensure that we don't send any messages before BrowserElementChild.js 1.49 + // finishes loading. 1.50 + if (!BrowserElementIsReady) 1.51 + return; 1.52 + 1.53 + if (!data) { 1.54 + data = { }; 1.55 + } 1.56 + 1.57 + data.msg_name = msg; 1.58 + return sendSyncMessage('browser-element-api:call', data); 1.59 +} 1.60 + 1.61 +let CERTIFICATE_ERROR_PAGE_PREF = 'security.alternate_certificate_error_page'; 1.62 + 1.63 +let NS_ERROR_MODULE_BASE_OFFSET = 0x45; 1.64 +let NS_ERROR_MODULE_SECURITY= 21; 1.65 +function NS_ERROR_GET_MODULE(err) { 1.66 + return ((((err) >> 16) - NS_ERROR_MODULE_BASE_OFFSET) & 0x1fff); 1.67 +} 1.68 + 1.69 +function NS_ERROR_GET_CODE(err) { 1.70 + return ((err) & 0xffff); 1.71 +} 1.72 + 1.73 +let SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE; 1.74 +let SEC_ERROR_UNKNOWN_ISSUER = (SEC_ERROR_BASE + 13); 1.75 +let SEC_ERROR_CA_CERT_INVALID = (SEC_ERROR_BASE + 36); 1.76 +let SEC_ERROR_UNTRUSTED_ISSUER = (SEC_ERROR_BASE + 20); 1.77 +let SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE = (SEC_ERROR_BASE + 30); 1.78 +let SEC_ERROR_UNTRUSTED_CERT = (SEC_ERROR_BASE + 21); 1.79 +let SEC_ERROR_EXPIRED_CERTIFICATE = (SEC_ERROR_BASE + 11); 1.80 +let SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = (SEC_ERROR_BASE + 176); 1.81 + 1.82 +let SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE; 1.83 +let SSL_ERROR_BAD_CERT_DOMAIN = (SSL_ERROR_BASE + 12); 1.84 + 1.85 +function getErrorClass(errorCode) { 1.86 + let NSPRCode = -1 * NS_ERROR_GET_CODE(errorCode); 1.87 + 1.88 + switch (NSPRCode) { 1.89 + case SEC_ERROR_UNKNOWN_ISSUER: 1.90 + case SEC_ERROR_UNTRUSTED_ISSUER: 1.91 + case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: 1.92 + case SEC_ERROR_UNTRUSTED_CERT: 1.93 + case SSL_ERROR_BAD_CERT_DOMAIN: 1.94 + case SEC_ERROR_EXPIRED_CERTIFICATE: 1.95 + case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: 1.96 + case SEC_ERROR_CA_CERT_INVALID: 1.97 + return Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT; 1.98 + default: 1.99 + return Ci.nsINSSErrorsService.ERROR_CLASS_SSL_PROTOCOL; 1.100 + } 1.101 + 1.102 + return null; 1.103 +} 1.104 + 1.105 +const OBSERVED_EVENTS = [ 1.106 + 'fullscreen-origin-change', 1.107 + 'ask-parent-to-exit-fullscreen', 1.108 + 'ask-parent-to-rollback-fullscreen', 1.109 + 'xpcom-shutdown', 1.110 + 'activity-done' 1.111 +]; 1.112 + 1.113 +/** 1.114 + * The BrowserElementChild implements one half of <iframe mozbrowser>. 1.115 + * (The other half is, unsurprisingly, BrowserElementParent.) 1.116 + * 1.117 + * This script is injected into an <iframe mozbrowser> via 1.118 + * nsIMessageManager::LoadFrameScript(). 1.119 + * 1.120 + * Our job here is to listen for events within this frame and bubble them up to 1.121 + * the parent process. 1.122 + */ 1.123 + 1.124 +var global = this; 1.125 + 1.126 +function BrowserElementChild() { 1.127 + // Maps outer window id --> weak ref to window. Used by modal dialog code. 1.128 + this._windowIDDict = {}; 1.129 + 1.130 + // _forcedVisible corresponds to the visibility state our owner has set on us 1.131 + // (via iframe.setVisible). ownerVisible corresponds to whether the docShell 1.132 + // whose window owns this element is visible. 1.133 + // 1.134 + // Our docShell is visible iff _forcedVisible and _ownerVisible are both 1.135 + // true. 1.136 + this._forcedVisible = true; 1.137 + this._ownerVisible = true; 1.138 + 1.139 + this._nextPaintHandler = null; 1.140 + 1.141 + this._isContentWindowCreated = false; 1.142 + this._pendingSetInputMethodActive = []; 1.143 + 1.144 + this._init(); 1.145 +}; 1.146 + 1.147 +BrowserElementChild.prototype = { 1.148 + 1.149 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, 1.150 + Ci.nsISupportsWeakReference]), 1.151 + 1.152 + _init: function() { 1.153 + debug("Starting up."); 1.154 + 1.155 + BrowserElementPromptService.mapWindowToBrowserElementChild(content, this); 1.156 + 1.157 + docShell.QueryInterface(Ci.nsIWebProgress) 1.158 + .addProgressListener(this._progressListener, 1.159 + Ci.nsIWebProgress.NOTIFY_LOCATION | 1.160 + Ci.nsIWebProgress.NOTIFY_SECURITY | 1.161 + Ci.nsIWebProgress.NOTIFY_STATE_WINDOW); 1.162 + 1.163 + docShell.QueryInterface(Ci.nsIWebNavigation) 1.164 + .sessionHistory = Cc["@mozilla.org/browser/shistory;1"] 1.165 + .createInstance(Ci.nsISHistory); 1.166 + 1.167 + // This is necessary to get security web progress notifications. 1.168 + var securityUI = Cc['@mozilla.org/secure_browser_ui;1'] 1.169 + .createInstance(Ci.nsISecureBrowserUI); 1.170 + securityUI.init(content); 1.171 + 1.172 + // A cache of the menuitem dom objects keyed by the id we generate 1.173 + // and pass to the embedder 1.174 + this._ctxHandlers = {}; 1.175 + // Counter of contextmenu events fired 1.176 + this._ctxCounter = 0; 1.177 + 1.178 + this._shuttingDown = false; 1.179 + 1.180 + addEventListener('DOMTitleChanged', 1.181 + this._titleChangedHandler.bind(this), 1.182 + /* useCapture = */ true, 1.183 + /* wantsUntrusted = */ false); 1.184 + 1.185 + addEventListener('DOMLinkAdded', 1.186 + this._linkAddedHandler.bind(this), 1.187 + /* useCapture = */ true, 1.188 + /* wantsUntrusted = */ false); 1.189 + 1.190 + addEventListener('DOMMetaAdded', 1.191 + this._metaAddedHandler.bind(this), 1.192 + /* useCapture = */ true, 1.193 + /* wantsUntrusted = */ false); 1.194 + 1.195 + // This listens to unload events from our message manager, but /not/ from 1.196 + // the |content| window. That's because the window's unload event doesn't 1.197 + // bubble, and we're not using a capturing listener. If we'd used 1.198 + // useCapture == true, we /would/ hear unload events from the window, which 1.199 + // is not what we want! 1.200 + addEventListener('unload', 1.201 + this._unloadHandler.bind(this), 1.202 + /* useCapture = */ false, 1.203 + /* wantsUntrusted = */ false); 1.204 + 1.205 + // Registers a MozAfterPaint handler for the very first paint. 1.206 + this._addMozAfterPaintHandler(function () { 1.207 + sendAsyncMsg('firstpaint'); 1.208 + }); 1.209 + 1.210 + let self = this; 1.211 + 1.212 + let mmCalls = { 1.213 + "purge-history": this._recvPurgeHistory, 1.214 + "get-screenshot": this._recvGetScreenshot, 1.215 + "set-visible": this._recvSetVisible, 1.216 + "get-visible": this._recvVisible, 1.217 + "send-mouse-event": this._recvSendMouseEvent, 1.218 + "send-touch-event": this._recvSendTouchEvent, 1.219 + "get-can-go-back": this._recvCanGoBack, 1.220 + "get-can-go-forward": this._recvCanGoForward, 1.221 + "go-back": this._recvGoBack, 1.222 + "go-forward": this._recvGoForward, 1.223 + "reload": this._recvReload, 1.224 + "stop": this._recvStop, 1.225 + "unblock-modal-prompt": this._recvStopWaiting, 1.226 + "fire-ctx-callback": this._recvFireCtxCallback, 1.227 + "owner-visibility-change": this._recvOwnerVisibilityChange, 1.228 + "exit-fullscreen": this._recvExitFullscreen.bind(this), 1.229 + "activate-next-paint-listener": this._activateNextPaintListener.bind(this), 1.230 + "set-input-method-active": this._recvSetInputMethodActive.bind(this), 1.231 + "deactivate-next-paint-listener": this._deactivateNextPaintListener.bind(this) 1.232 + } 1.233 + 1.234 + addMessageListener("browser-element-api:call", function(aMessage) { 1.235 + if (aMessage.data.msg_name in mmCalls) { 1.236 + return mmCalls[aMessage.data.msg_name].apply(self, arguments); 1.237 + } 1.238 + }); 1.239 + 1.240 + let els = Cc["@mozilla.org/eventlistenerservice;1"] 1.241 + .getService(Ci.nsIEventListenerService); 1.242 + 1.243 + // We are using the system group for those events so if something in the 1.244 + // content called .stopPropagation() this will still be called. 1.245 + els.addSystemEventListener(global, 'keydown', 1.246 + this._keyEventHandler.bind(this), 1.247 + /* useCapture = */ true); 1.248 + els.addSystemEventListener(global, 'keypress', 1.249 + this._keyEventHandler.bind(this), 1.250 + /* useCapture = */ true); 1.251 + els.addSystemEventListener(global, 'keyup', 1.252 + this._keyEventHandler.bind(this), 1.253 + /* useCapture = */ true); 1.254 + els.addSystemEventListener(global, 'DOMWindowClose', 1.255 + this._windowCloseHandler.bind(this), 1.256 + /* useCapture = */ false); 1.257 + els.addSystemEventListener(global, 'DOMWindowCreated', 1.258 + this._windowCreatedHandler.bind(this), 1.259 + /* useCapture = */ true); 1.260 + els.addSystemEventListener(global, 'DOMWindowResize', 1.261 + this._windowResizeHandler.bind(this), 1.262 + /* useCapture = */ false); 1.263 + els.addSystemEventListener(global, 'contextmenu', 1.264 + this._contextmenuHandler.bind(this), 1.265 + /* useCapture = */ false); 1.266 + els.addSystemEventListener(global, 'scroll', 1.267 + this._scrollEventHandler.bind(this), 1.268 + /* useCapture = */ false); 1.269 + 1.270 + OBSERVED_EVENTS.forEach((aTopic) => { 1.271 + Services.obs.addObserver(this, aTopic, false); 1.272 + }); 1.273 + }, 1.274 + 1.275 + observe: function(subject, topic, data) { 1.276 + // Ignore notifications not about our document. (Note that |content| /can/ 1.277 + // be null; see bug 874900.) 1.278 + if (topic !== 'activity-done' && (!content || subject != content.document)) 1.279 + return; 1.280 + if (topic == 'activity-done' && docShell !== subject) 1.281 + return; 1.282 + switch (topic) { 1.283 + case 'fullscreen-origin-change': 1.284 + sendAsyncMsg('fullscreen-origin-change', { _payload_: data }); 1.285 + break; 1.286 + case 'ask-parent-to-exit-fullscreen': 1.287 + sendAsyncMsg('exit-fullscreen'); 1.288 + break; 1.289 + case 'ask-parent-to-rollback-fullscreen': 1.290 + sendAsyncMsg('rollback-fullscreen'); 1.291 + break; 1.292 + case 'activity-done': 1.293 + sendAsyncMsg('activitydone', { success: (data == 'activity-success') }); 1.294 + break; 1.295 + case 'xpcom-shutdown': 1.296 + this._shuttingDown = true; 1.297 + break; 1.298 + } 1.299 + }, 1.300 + 1.301 + /** 1.302 + * Called when our TabChildGlobal starts to die. This is not called when the 1.303 + * page inside |content| unloads. 1.304 + */ 1.305 + _unloadHandler: function() { 1.306 + this._shuttingDown = true; 1.307 + OBSERVED_EVENTS.forEach((aTopic) => { 1.308 + Services.obs.removeObserver(this, aTopic); 1.309 + }); 1.310 + }, 1.311 + 1.312 + _tryGetInnerWindowID: function(win) { 1.313 + let utils = win.QueryInterface(Ci.nsIInterfaceRequestor) 1.314 + .getInterface(Ci.nsIDOMWindowUtils); 1.315 + try { 1.316 + return utils.currentInnerWindowID; 1.317 + } 1.318 + catch(e) { 1.319 + return null; 1.320 + } 1.321 + }, 1.322 + 1.323 + /** 1.324 + * Show a modal prompt. Called by BrowserElementPromptService. 1.325 + */ 1.326 + showModalPrompt: function(win, args) { 1.327 + let utils = win.QueryInterface(Ci.nsIInterfaceRequestor) 1.328 + .getInterface(Ci.nsIDOMWindowUtils); 1.329 + 1.330 + args.windowID = { outer: utils.outerWindowID, 1.331 + inner: this._tryGetInnerWindowID(win) }; 1.332 + sendAsyncMsg('showmodalprompt', args); 1.333 + 1.334 + let returnValue = this._waitForResult(win); 1.335 + 1.336 + Services.obs.notifyObservers(null, 'BEC:ShownModalPrompt', null); 1.337 + 1.338 + if (args.promptType == 'prompt' || 1.339 + args.promptType == 'confirm' || 1.340 + args.promptType == 'custom-prompt') { 1.341 + return returnValue; 1.342 + } 1.343 + }, 1.344 + 1.345 + /** 1.346 + * Spin in a nested event loop until we receive a unblock-modal-prompt message for 1.347 + * this window. 1.348 + */ 1.349 + _waitForResult: function(win) { 1.350 + debug("_waitForResult(" + win + ")"); 1.351 + let utils = win.QueryInterface(Ci.nsIInterfaceRequestor) 1.352 + .getInterface(Ci.nsIDOMWindowUtils); 1.353 + 1.354 + let outerWindowID = utils.outerWindowID; 1.355 + let innerWindowID = this._tryGetInnerWindowID(win); 1.356 + if (innerWindowID === null) { 1.357 + // I have no idea what waiting for a result means when there's no inner 1.358 + // window, so let's just bail. 1.359 + debug("_waitForResult: No inner window. Bailing."); 1.360 + return; 1.361 + } 1.362 + 1.363 + this._windowIDDict[outerWindowID] = Cu.getWeakReference(win); 1.364 + 1.365 + debug("Entering modal state (outerWindowID=" + outerWindowID + ", " + 1.366 + "innerWindowID=" + innerWindowID + ")"); 1.367 + 1.368 + utils.enterModalState(); 1.369 + 1.370 + // We'll decrement win.modalDepth when we receive a unblock-modal-prompt message 1.371 + // for the window. 1.372 + if (!win.modalDepth) { 1.373 + win.modalDepth = 0; 1.374 + } 1.375 + win.modalDepth++; 1.376 + let origModalDepth = win.modalDepth; 1.377 + 1.378 + let thread = Services.tm.currentThread; 1.379 + debug("Nested event loop - begin"); 1.380 + while (win.modalDepth == origModalDepth && !this._shuttingDown) { 1.381 + // Bail out of the loop if the inner window changed; that means the 1.382 + // window navigated. Bail out when we're shutting down because otherwise 1.383 + // we'll leak our window. 1.384 + if (this._tryGetInnerWindowID(win) !== innerWindowID) { 1.385 + debug("_waitForResult: Inner window ID changed " + 1.386 + "while in nested event loop."); 1.387 + break; 1.388 + } 1.389 + 1.390 + thread.processNextEvent(/* mayWait = */ true); 1.391 + } 1.392 + debug("Nested event loop - finish"); 1.393 + 1.394 + // If we exited the loop because the inner window changed, then bail on the 1.395 + // modal prompt. 1.396 + if (innerWindowID !== this._tryGetInnerWindowID(win)) { 1.397 + throw Components.Exception("Modal state aborted by navigation", 1.398 + Cr.NS_ERROR_NOT_AVAILABLE); 1.399 + } 1.400 + 1.401 + let returnValue = win.modalReturnValue; 1.402 + delete win.modalReturnValue; 1.403 + 1.404 + if (!this._shuttingDown) { 1.405 + utils.leaveModalState(); 1.406 + } 1.407 + 1.408 + debug("Leaving modal state (outerID=" + outerWindowID + ", " + 1.409 + "innerID=" + innerWindowID + ")"); 1.410 + return returnValue; 1.411 + }, 1.412 + 1.413 + _recvStopWaiting: function(msg) { 1.414 + let outerID = msg.json.windowID.outer; 1.415 + let innerID = msg.json.windowID.inner; 1.416 + let returnValue = msg.json.returnValue; 1.417 + debug("recvStopWaiting(outer=" + outerID + ", inner=" + innerID + 1.418 + ", returnValue=" + returnValue + ")"); 1.419 + 1.420 + if (!this._windowIDDict[outerID]) { 1.421 + debug("recvStopWaiting: No record of outer window ID " + outerID); 1.422 + return; 1.423 + } 1.424 + 1.425 + let win = this._windowIDDict[outerID].get(); 1.426 + delete this._windowIDDict[outerID]; 1.427 + 1.428 + if (!win) { 1.429 + debug("recvStopWaiting, but window is gone\n"); 1.430 + return; 1.431 + } 1.432 + 1.433 + if (innerID !== this._tryGetInnerWindowID(win)) { 1.434 + debug("recvStopWaiting, but inner ID has changed\n"); 1.435 + return; 1.436 + } 1.437 + 1.438 + debug("recvStopWaiting " + win); 1.439 + win.modalReturnValue = returnValue; 1.440 + win.modalDepth--; 1.441 + }, 1.442 + 1.443 + _recvExitFullscreen: function() { 1.444 + var utils = content.document.defaultView 1.445 + .QueryInterface(Ci.nsIInterfaceRequestor) 1.446 + .getInterface(Ci.nsIDOMWindowUtils); 1.447 + utils.exitFullscreen(); 1.448 + }, 1.449 + 1.450 + _titleChangedHandler: function(e) { 1.451 + debug("Got titlechanged: (" + e.target.title + ")"); 1.452 + var win = e.target.defaultView; 1.453 + 1.454 + // Ignore titlechanges which don't come from the top-level 1.455 + // <iframe mozbrowser> window. 1.456 + if (win == content) { 1.457 + sendAsyncMsg('titlechange', { _payload_: e.target.title }); 1.458 + } 1.459 + else { 1.460 + debug("Not top level!"); 1.461 + } 1.462 + }, 1.463 + 1.464 + _iconChangedHandler: function(e) { 1.465 + debug('Got iconchanged: (' + e.target.href + ')'); 1.466 + let icon = { href: e.target.href }; 1.467 + if (e.target.getAttribute('sizes')) { 1.468 + icon.sizes = e.target.getAttribute('sizes'); 1.469 + } 1.470 + 1.471 + sendAsyncMsg('iconchange', icon); 1.472 + }, 1.473 + 1.474 + _openSearchHandler: function(e) { 1.475 + debug('Got opensearch: (' + e.target.href + ')'); 1.476 + 1.477 + if (e.target.type !== "application/opensearchdescription+xml") { 1.478 + return; 1.479 + } 1.480 + 1.481 + sendAsyncMsg('opensearch', { title: e.target.title, 1.482 + href: e.target.href }); 1.483 + 1.484 + }, 1.485 + 1.486 + _manifestChangedHandler: function(e) { 1.487 + debug('Got manifestchanged: (' + e.target.href + ')'); 1.488 + let manifest = { href: e.target.href }; 1.489 + sendAsyncMsg('manifestchange', manifest); 1.490 + 1.491 + }, 1.492 + 1.493 + // Processes the "rel" field in <link> tags and forward to specific handlers. 1.494 + _linkAddedHandler: function(e) { 1.495 + let win = e.target.ownerDocument.defaultView; 1.496 + // Ignore links which don't come from the top-level 1.497 + // <iframe mozbrowser> window. 1.498 + if (win != content) { 1.499 + debug('Not top level!'); 1.500 + return; 1.501 + } 1.502 + 1.503 + let handlers = { 1.504 + 'icon': this._iconChangedHandler, 1.505 + 'search': this._openSearchHandler, 1.506 + 'manifest': this._manifestChangedHandler 1.507 + }; 1.508 + 1.509 + debug('Got linkAdded: (' + e.target.href + ') ' + e.target.rel); 1.510 + e.target.rel.split(' ').forEach(function(x) { 1.511 + let token = x.toLowerCase(); 1.512 + if (handlers[token]) { 1.513 + handlers[token](e); 1.514 + } 1.515 + }, this); 1.516 + }, 1.517 + 1.518 + _metaAddedHandler: function(e) { 1.519 + let win = e.target.ownerDocument.defaultView; 1.520 + // Ignore metas which don't come from the top-level 1.521 + // <iframe mozbrowser> window. 1.522 + if (win != content) { 1.523 + debug('Not top level!'); 1.524 + return; 1.525 + } 1.526 + 1.527 + if (!e.target.name) { 1.528 + return; 1.529 + } 1.530 + 1.531 + debug('Got metaAdded: (' + e.target.name + ') ' + e.target.content); 1.532 + if (e.target.name == 'application-name') { 1.533 + let meta = { name: e.target.name, 1.534 + content: e.target.content }; 1.535 + 1.536 + let lang; 1.537 + let elm; 1.538 + 1.539 + for (elm = e.target; 1.540 + !lang && elm && elm.nodeType == e.target.ELEMENT_NODE; 1.541 + elm = elm.parentNode) { 1.542 + if (elm.hasAttribute('lang')) { 1.543 + lang = elm.getAttribute('lang'); 1.544 + continue; 1.545 + } 1.546 + 1.547 + if (elm.hasAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang')) { 1.548 + lang = elm.getAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang'); 1.549 + continue; 1.550 + } 1.551 + } 1.552 + 1.553 + // No lang has been detected. 1.554 + if (!lang && elm.nodeType == e.target.DOCUMENT_NODE) { 1.555 + lang = elm.contentLanguage; 1.556 + } 1.557 + 1.558 + if (lang) { 1.559 + meta.lang = lang; 1.560 + } 1.561 + 1.562 + sendAsyncMsg('metachange', meta); 1.563 + } 1.564 + }, 1.565 + 1.566 + _addMozAfterPaintHandler: function(callback) { 1.567 + function onMozAfterPaint() { 1.568 + let uri = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI; 1.569 + if (uri.spec != "about:blank") { 1.570 + debug("Got afterpaint event: " + uri.spec); 1.571 + removeEventListener('MozAfterPaint', onMozAfterPaint, 1.572 + /* useCapture = */ true); 1.573 + callback(); 1.574 + } 1.575 + } 1.576 + 1.577 + addEventListener('MozAfterPaint', onMozAfterPaint, /* useCapture = */ true); 1.578 + return onMozAfterPaint; 1.579 + }, 1.580 + 1.581 + _removeMozAfterPaintHandler: function(listener) { 1.582 + removeEventListener('MozAfterPaint', listener, 1.583 + /* useCapture = */ true); 1.584 + }, 1.585 + 1.586 + _activateNextPaintListener: function(e) { 1.587 + if (!this._nextPaintHandler) { 1.588 + this._nextPaintHandler = this._addMozAfterPaintHandler(function () { 1.589 + this._nextPaintHandler = null; 1.590 + sendAsyncMsg('nextpaint'); 1.591 + }.bind(this)); 1.592 + } 1.593 + }, 1.594 + 1.595 + _deactivateNextPaintListener: function(e) { 1.596 + if (this._nextPaintHandler) { 1.597 + this._removeMozAfterPaintHandler(this._nextPaintHandler); 1.598 + this._nextPaintHandler = null; 1.599 + } 1.600 + }, 1.601 + 1.602 + _windowCloseHandler: function(e) { 1.603 + let win = e.target; 1.604 + if (win != content || e.defaultPrevented) { 1.605 + return; 1.606 + } 1.607 + 1.608 + debug("Closing window " + win); 1.609 + sendAsyncMsg('close'); 1.610 + 1.611 + // Inform the window implementation that we handled this close ourselves. 1.612 + e.preventDefault(); 1.613 + }, 1.614 + 1.615 + _windowCreatedHandler: function(e) { 1.616 + let targetDocShell = e.target.defaultView 1.617 + .QueryInterface(Ci.nsIInterfaceRequestor) 1.618 + .getInterface(Ci.nsIWebNavigation); 1.619 + if (targetDocShell != docShell) { 1.620 + return; 1.621 + } 1.622 + 1.623 + let uri = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI; 1.624 + debug("Window created: " + uri.spec); 1.625 + if (uri.spec != "about:blank") { 1.626 + this._addMozAfterPaintHandler(function () { 1.627 + sendAsyncMsg('documentfirstpaint'); 1.628 + }); 1.629 + this._isContentWindowCreated = true; 1.630 + // Handle pending SetInputMethodActive request. 1.631 + while (this._pendingSetInputMethodActive.length > 0) { 1.632 + this._recvSetInputMethodActive(this._pendingSetInputMethodActive.shift()); 1.633 + } 1.634 + } 1.635 + }, 1.636 + 1.637 + _windowResizeHandler: function(e) { 1.638 + let win = e.target; 1.639 + if (win != content || e.defaultPrevented) { 1.640 + return; 1.641 + } 1.642 + 1.643 + debug("resizing window " + win); 1.644 + sendAsyncMsg('resize', { width: e.detail.width, height: e.detail.height }); 1.645 + 1.646 + // Inform the window implementation that we handled this resize ourselves. 1.647 + e.preventDefault(); 1.648 + }, 1.649 + 1.650 + _contextmenuHandler: function(e) { 1.651 + debug("Got contextmenu"); 1.652 + 1.653 + if (e.defaultPrevented) { 1.654 + return; 1.655 + } 1.656 + 1.657 + this._ctxCounter++; 1.658 + this._ctxHandlers = {}; 1.659 + 1.660 + var elem = e.target; 1.661 + var menuData = {systemTargets: [], contextmenu: null}; 1.662 + var ctxMenuId = null; 1.663 + 1.664 + while (elem && elem.parentNode) { 1.665 + var ctxData = this._getSystemCtxMenuData(elem); 1.666 + if (ctxData) { 1.667 + menuData.systemTargets.push({ 1.668 + nodeName: elem.nodeName, 1.669 + data: ctxData 1.670 + }); 1.671 + } 1.672 + 1.673 + if (!ctxMenuId && 'hasAttribute' in elem && elem.hasAttribute('contextmenu')) { 1.674 + ctxMenuId = elem.getAttribute('contextmenu'); 1.675 + } 1.676 + elem = elem.parentNode; 1.677 + } 1.678 + 1.679 + if (ctxMenuId) { 1.680 + var menu = e.target.ownerDocument.getElementById(ctxMenuId); 1.681 + if (menu) { 1.682 + menuData.contextmenu = this._buildMenuObj(menu, ''); 1.683 + } 1.684 + } 1.685 + 1.686 + // The value returned by the contextmenu sync call is true iff the embedder 1.687 + // called preventDefault() on its contextmenu event. 1.688 + // 1.689 + // We call preventDefault() on our contextmenu event iff the embedder called 1.690 + // preventDefault() on /its/ contextmenu event. This way, if the embedder 1.691 + // ignored the contextmenu event, TabChild will fire a click. 1.692 + if (sendSyncMsg('contextmenu', menuData)[0]) { 1.693 + e.preventDefault(); 1.694 + } else { 1.695 + this._ctxHandlers = {}; 1.696 + } 1.697 + }, 1.698 + 1.699 + _getSystemCtxMenuData: function(elem) { 1.700 + if ((elem instanceof Ci.nsIDOMHTMLAnchorElement && elem.href) || 1.701 + (elem instanceof Ci.nsIDOMHTMLAreaElement && elem.href)) { 1.702 + return {uri: elem.href}; 1.703 + } 1.704 + if (elem instanceof Ci.nsIImageLoadingContent && elem.currentURI) { 1.705 + return {uri: elem.currentURI.spec}; 1.706 + } 1.707 + if (elem instanceof Ci.nsIDOMHTMLImageElement) { 1.708 + return {uri: elem.src}; 1.709 + } 1.710 + if (elem instanceof Ci.nsIDOMHTMLMediaElement) { 1.711 + let hasVideo = !(elem.readyState >= elem.HAVE_METADATA && 1.712 + (elem.videoWidth == 0 || elem.videoHeight == 0)); 1.713 + return {uri: elem.currentSrc || elem.src, hasVideo: hasVideo}; 1.714 + } 1.715 + return false; 1.716 + }, 1.717 + 1.718 + _scrollEventHandler: function(e) { 1.719 + let win = e.target.defaultView; 1.720 + if (win != content) { 1.721 + return; 1.722 + } 1.723 + 1.724 + debug("scroll event " + win); 1.725 + sendAsyncMsg("scroll", { top: win.scrollY, left: win.scrollX }); 1.726 + }, 1.727 + 1.728 + _recvPurgeHistory: function(data) { 1.729 + debug("Received purgeHistory message: (" + data.json.id + ")"); 1.730 + 1.731 + let history = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory; 1.732 + 1.733 + try { 1.734 + if (history && history.count) { 1.735 + history.PurgeHistory(history.count); 1.736 + } 1.737 + } catch(e) {} 1.738 + 1.739 + sendAsyncMsg('got-purge-history', { id: data.json.id, successRv: true }); 1.740 + }, 1.741 + 1.742 + _recvGetScreenshot: function(data) { 1.743 + debug("Received getScreenshot message: (" + data.json.id + ")"); 1.744 + 1.745 + let self = this; 1.746 + let maxWidth = data.json.args.width; 1.747 + let maxHeight = data.json.args.height; 1.748 + let mimeType = data.json.args.mimeType; 1.749 + let domRequestID = data.json.id; 1.750 + 1.751 + let takeScreenshotClosure = function() { 1.752 + self._takeScreenshot(maxWidth, maxHeight, mimeType, domRequestID); 1.753 + }; 1.754 + 1.755 + let maxDelayMS = 2000; 1.756 + try { 1.757 + maxDelayMS = Services.prefs.getIntPref('dom.browserElement.maxScreenshotDelayMS'); 1.758 + } 1.759 + catch(e) {} 1.760 + 1.761 + // Try to wait for the event loop to go idle before we take the screenshot, 1.762 + // but once we've waited maxDelayMS milliseconds, go ahead and take it 1.763 + // anyway. 1.764 + Cc['@mozilla.org/message-loop;1'].getService(Ci.nsIMessageLoop).postIdleTask( 1.765 + takeScreenshotClosure, maxDelayMS); 1.766 + }, 1.767 + 1.768 + /** 1.769 + * Actually take a screenshot and foward the result up to our parent, given 1.770 + * the desired maxWidth and maxHeight (in CSS pixels), and given the 1.771 + * DOMRequest ID associated with the request from the parent. 1.772 + */ 1.773 + _takeScreenshot: function(maxWidth, maxHeight, mimeType, domRequestID) { 1.774 + // You can think of the screenshotting algorithm as carrying out the 1.775 + // following steps: 1.776 + // 1.777 + // - Calculate maxWidth, maxHeight, and viewport's width and height in the 1.778 + // dimension of device pixels by multiply the numbers with 1.779 + // window.devicePixelRatio. 1.780 + // 1.781 + // - Let scaleWidth be the factor by which we'd need to downscale the 1.782 + // viewport pixel width so it would fit within maxPixelWidth. 1.783 + // (If the viewport's pixel width is less than maxPixelWidth, let 1.784 + // scaleWidth be 1.) Compute scaleHeight the same way. 1.785 + // 1.786 + // - Scale the viewport by max(scaleWidth, scaleHeight). Now either the 1.787 + // viewport's width is no larger than maxWidth, the viewport's height is 1.788 + // no larger than maxHeight, or both. 1.789 + // 1.790 + // - Crop the viewport so its width is no larger than maxWidth and its 1.791 + // height is no larger than maxHeight. 1.792 + // 1.793 + // - Set mozOpaque to true and background color to solid white 1.794 + // if we are taking a JPEG screenshot, keep transparent if otherwise. 1.795 + // 1.796 + // - Return a screenshot of the page's viewport scaled and cropped per 1.797 + // above. 1.798 + debug("Taking a screenshot: maxWidth=" + maxWidth + 1.799 + ", maxHeight=" + maxHeight + 1.800 + ", mimeType=" + mimeType + 1.801 + ", domRequestID=" + domRequestID + "."); 1.802 + 1.803 + if (!content) { 1.804 + // If content is not loaded yet, bail out since even sendAsyncMessage 1.805 + // fails... 1.806 + debug("No content yet!"); 1.807 + return; 1.808 + } 1.809 + 1.810 + let devicePixelRatio = content.devicePixelRatio; 1.811 + 1.812 + let maxPixelWidth = Math.round(maxWidth * devicePixelRatio); 1.813 + let maxPixelHeight = Math.round(maxHeight * devicePixelRatio); 1.814 + 1.815 + let contentPixelWidth = content.innerWidth * devicePixelRatio; 1.816 + let contentPixelHeight = content.innerHeight * devicePixelRatio; 1.817 + 1.818 + let scaleWidth = Math.min(1, maxPixelWidth / contentPixelWidth); 1.819 + let scaleHeight = Math.min(1, maxPixelHeight / contentPixelHeight); 1.820 + 1.821 + let scale = Math.max(scaleWidth, scaleHeight); 1.822 + 1.823 + let canvasWidth = 1.824 + Math.min(maxPixelWidth, Math.round(contentPixelWidth * scale)); 1.825 + let canvasHeight = 1.826 + Math.min(maxPixelHeight, Math.round(contentPixelHeight * scale)); 1.827 + 1.828 + let transparent = (mimeType !== 'image/jpeg'); 1.829 + 1.830 + var canvas = content.document 1.831 + .createElementNS("http://www.w3.org/1999/xhtml", "canvas"); 1.832 + if (!transparent) 1.833 + canvas.mozOpaque = true; 1.834 + canvas.width = canvasWidth; 1.835 + canvas.height = canvasHeight; 1.836 + 1.837 + let ctx = canvas.getContext("2d", { willReadFrequently: true }); 1.838 + ctx.scale(scale * devicePixelRatio, scale * devicePixelRatio); 1.839 + 1.840 + let flags = ctx.DRAWWINDOW_DRAW_VIEW | 1.841 + ctx.DRAWWINDOW_USE_WIDGET_LAYERS | 1.842 + ctx.DRAWWINDOW_DO_NOT_FLUSH | 1.843 + ctx.DRAWWINDOW_ASYNC_DECODE_IMAGES; 1.844 + ctx.drawWindow(content, 0, 0, content.innerWidth, content.innerHeight, 1.845 + transparent ? "rgba(255,255,255,0)" : "rgb(255,255,255)", 1.846 + flags); 1.847 + 1.848 + // Take a JPEG screenshot by default instead of PNG with alpha channel. 1.849 + // This requires us to unpremultiply the alpha channel, which 1.850 + // is expensive on ARM processors because they lack a hardware integer 1.851 + // division instruction. 1.852 + canvas.toBlob(function(blob) { 1.853 + sendAsyncMsg('got-screenshot', { 1.854 + id: domRequestID, 1.855 + successRv: blob 1.856 + }); 1.857 + }, mimeType); 1.858 + }, 1.859 + 1.860 + _recvFireCtxCallback: function(data) { 1.861 + debug("Received fireCtxCallback message: (" + data.json.menuitem + ")"); 1.862 + // We silently ignore if the embedder uses an incorrect id in the callback 1.863 + if (data.json.menuitem in this._ctxHandlers) { 1.864 + this._ctxHandlers[data.json.menuitem].click(); 1.865 + this._ctxHandlers = {}; 1.866 + } else { 1.867 + debug("Ignored invalid contextmenu invocation"); 1.868 + } 1.869 + }, 1.870 + 1.871 + _buildMenuObj: function(menu, idPrefix) { 1.872 + function maybeCopyAttribute(src, target, attribute) { 1.873 + if (src.getAttribute(attribute)) { 1.874 + target[attribute] = src.getAttribute(attribute); 1.875 + } 1.876 + } 1.877 + 1.878 + var menuObj = {type: 'menu', items: []}; 1.879 + maybeCopyAttribute(menu, menuObj, 'label'); 1.880 + 1.881 + for (var i = 0, child; child = menu.children[i++];) { 1.882 + if (child.nodeName === 'MENU') { 1.883 + menuObj.items.push(this._buildMenuObj(child, idPrefix + i + '_')); 1.884 + } else if (child.nodeName === 'MENUITEM') { 1.885 + var id = this._ctxCounter + '_' + idPrefix + i; 1.886 + var menuitem = {id: id, type: 'menuitem'}; 1.887 + maybeCopyAttribute(child, menuitem, 'label'); 1.888 + maybeCopyAttribute(child, menuitem, 'icon'); 1.889 + this._ctxHandlers[id] = child; 1.890 + menuObj.items.push(menuitem); 1.891 + } 1.892 + } 1.893 + return menuObj; 1.894 + }, 1.895 + 1.896 + _recvSetVisible: function(data) { 1.897 + debug("Received setVisible message: (" + data.json.visible + ")"); 1.898 + if (this._forcedVisible == data.json.visible) { 1.899 + return; 1.900 + } 1.901 + 1.902 + this._forcedVisible = data.json.visible; 1.903 + this._updateVisibility(); 1.904 + }, 1.905 + 1.906 + _recvVisible: function(data) { 1.907 + sendAsyncMsg('got-visible', { 1.908 + id: data.json.id, 1.909 + successRv: docShell.isActive 1.910 + }); 1.911 + }, 1.912 + 1.913 + /** 1.914 + * Called when the window which contains this iframe becomes hidden or 1.915 + * visible. 1.916 + */ 1.917 + _recvOwnerVisibilityChange: function(data) { 1.918 + debug("Received ownerVisibilityChange: (" + data.json.visible + ")"); 1.919 + this._ownerVisible = data.json.visible; 1.920 + this._updateVisibility(); 1.921 + }, 1.922 + 1.923 + _updateVisibility: function() { 1.924 + var visible = this._forcedVisible && this._ownerVisible; 1.925 + if (docShell.isActive !== visible) { 1.926 + docShell.isActive = visible; 1.927 + sendAsyncMsg('visibilitychange', {visible: visible}); 1.928 + } 1.929 + }, 1.930 + 1.931 + _recvSendMouseEvent: function(data) { 1.932 + let json = data.json; 1.933 + let utils = content.QueryInterface(Ci.nsIInterfaceRequestor) 1.934 + .getInterface(Ci.nsIDOMWindowUtils); 1.935 + utils.sendMouseEventToWindow(json.type, json.x, json.y, json.button, 1.936 + json.clickCount, json.modifiers); 1.937 + }, 1.938 + 1.939 + _recvSendTouchEvent: function(data) { 1.940 + let json = data.json; 1.941 + let utils = content.QueryInterface(Ci.nsIInterfaceRequestor) 1.942 + .getInterface(Ci.nsIDOMWindowUtils); 1.943 + utils.sendTouchEventToWindow(json.type, json.identifiers, json.touchesX, 1.944 + json.touchesY, json.radiisX, json.radiisY, 1.945 + json.rotationAngles, json.forces, json.count, 1.946 + json.modifiers); 1.947 + }, 1.948 + 1.949 + _recvCanGoBack: function(data) { 1.950 + var webNav = docShell.QueryInterface(Ci.nsIWebNavigation); 1.951 + sendAsyncMsg('got-can-go-back', { 1.952 + id: data.json.id, 1.953 + successRv: webNav.canGoBack 1.954 + }); 1.955 + }, 1.956 + 1.957 + _recvCanGoForward: function(data) { 1.958 + var webNav = docShell.QueryInterface(Ci.nsIWebNavigation); 1.959 + sendAsyncMsg('got-can-go-forward', { 1.960 + id: data.json.id, 1.961 + successRv: webNav.canGoForward 1.962 + }); 1.963 + }, 1.964 + 1.965 + _recvGoBack: function(data) { 1.966 + try { 1.967 + docShell.QueryInterface(Ci.nsIWebNavigation).goBack(); 1.968 + } catch(e) { 1.969 + // Silently swallow errors; these happen when we can't go back. 1.970 + } 1.971 + }, 1.972 + 1.973 + _recvGoForward: function(data) { 1.974 + try { 1.975 + docShell.QueryInterface(Ci.nsIWebNavigation).goForward(); 1.976 + } catch(e) { 1.977 + // Silently swallow errors; these happen when we can't go forward. 1.978 + } 1.979 + }, 1.980 + 1.981 + _recvReload: function(data) { 1.982 + let webNav = docShell.QueryInterface(Ci.nsIWebNavigation); 1.983 + let reloadFlags = data.json.hardReload ? 1.984 + webNav.LOAD_FLAGS_BYPASS_PROXY | webNav.LOAD_FLAGS_BYPASS_CACHE : 1.985 + webNav.LOAD_FLAGS_NONE; 1.986 + try { 1.987 + webNav.reload(reloadFlags); 1.988 + } catch(e) { 1.989 + // Silently swallow errors; these can happen if a used cancels reload 1.990 + } 1.991 + }, 1.992 + 1.993 + _recvStop: function(data) { 1.994 + let webNav = docShell.QueryInterface(Ci.nsIWebNavigation); 1.995 + webNav.stop(webNav.STOP_NETWORK); 1.996 + }, 1.997 + 1.998 + _recvSetInputMethodActive: function(data) { 1.999 + let msgData = { id: data.json.id }; 1.1000 + if (!this._isContentWindowCreated) { 1.1001 + if (data.json.args.isActive) { 1.1002 + // To activate the input method, we should wait before the content 1.1003 + // window is ready. 1.1004 + this._pendingSetInputMethodActive.push(data); 1.1005 + return; 1.1006 + } 1.1007 + msgData.successRv = null; 1.1008 + sendAsyncMsg('got-set-input-method-active', msgData); 1.1009 + return; 1.1010 + } 1.1011 + // Unwrap to access webpage content. 1.1012 + let nav = XPCNativeWrapper.unwrap(content.document.defaultView.navigator); 1.1013 + if (nav.mozInputMethod) { 1.1014 + // Wrap to access the chrome-only attribute setActive. 1.1015 + new XPCNativeWrapper(nav.mozInputMethod).setActive(data.json.args.isActive); 1.1016 + msgData.successRv = null; 1.1017 + } else { 1.1018 + msgData.errorMsg = 'Cannot access mozInputMethod.'; 1.1019 + } 1.1020 + sendAsyncMsg('got-set-input-method-active', msgData); 1.1021 + }, 1.1022 + 1.1023 + _keyEventHandler: function(e) { 1.1024 + if (whitelistedEvents.indexOf(e.keyCode) != -1 && !e.defaultPrevented) { 1.1025 + sendAsyncMsg('keyevent', { 1.1026 + type: e.type, 1.1027 + keyCode: e.keyCode, 1.1028 + charCode: e.charCode, 1.1029 + }); 1.1030 + } 1.1031 + }, 1.1032 + 1.1033 + // The docShell keeps a weak reference to the progress listener, so we need 1.1034 + // to keep a strong ref to it ourselves. 1.1035 + _progressListener: { 1.1036 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, 1.1037 + Ci.nsISupportsWeakReference]), 1.1038 + _seenLoadStart: false, 1.1039 + 1.1040 + onLocationChange: function(webProgress, request, location, flags) { 1.1041 + // We get progress events from subshells here, which is kind of weird. 1.1042 + if (webProgress != docShell) { 1.1043 + return; 1.1044 + } 1.1045 + 1.1046 + // Ignore locationchange events which occur before the first loadstart. 1.1047 + // These are usually about:blank loads we don't care about. 1.1048 + if (!this._seenLoadStart) { 1.1049 + return; 1.1050 + } 1.1051 + 1.1052 + // Remove password and wyciwyg from uri. 1.1053 + location = Cc["@mozilla.org/docshell/urifixup;1"] 1.1054 + .getService(Ci.nsIURIFixup).createExposableURI(location); 1.1055 + 1.1056 + sendAsyncMsg('locationchange', { _payload_: location.spec }); 1.1057 + }, 1.1058 + 1.1059 + onStateChange: function(webProgress, request, stateFlags, status) { 1.1060 + if (webProgress != docShell) { 1.1061 + return; 1.1062 + } 1.1063 + 1.1064 + if (stateFlags & Ci.nsIWebProgressListener.STATE_START) { 1.1065 + this._seenLoadStart = true; 1.1066 + sendAsyncMsg('loadstart'); 1.1067 + } 1.1068 + 1.1069 + if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) { 1.1070 + let bgColor = 'transparent'; 1.1071 + try { 1.1072 + bgColor = content.getComputedStyle(content.document.body) 1.1073 + .getPropertyValue('background-color'); 1.1074 + } catch (e) {} 1.1075 + sendAsyncMsg('loadend', {backgroundColor: bgColor}); 1.1076 + 1.1077 + // Ignoring NS_BINDING_ABORTED, which is set when loading page is 1.1078 + // stopped. 1.1079 + if (status == Cr.NS_OK || 1.1080 + status == Cr.NS_BINDING_ABORTED) { 1.1081 + return; 1.1082 + } 1.1083 + 1.1084 + if (NS_ERROR_GET_MODULE(status) == NS_ERROR_MODULE_SECURITY && 1.1085 + getErrorClass(status) == Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT) { 1.1086 + 1.1087 + // XXX Is there a point firing the event if the error page is not 1.1088 + // certerror? If yes, maybe we should add a property to the 1.1089 + // event to to indicate whether there is a custom page. That would 1.1090 + // let the embedder have more control over the desired behavior. 1.1091 + var errorPage = null; 1.1092 + try { 1.1093 + errorPage = Services.prefs.getCharPref(CERTIFICATE_ERROR_PAGE_PREF); 1.1094 + } catch(e) {} 1.1095 + 1.1096 + if (errorPage == 'certerror') { 1.1097 + sendAsyncMsg('error', { type: 'certerror' }); 1.1098 + return; 1.1099 + } 1.1100 + } 1.1101 + 1.1102 + // TODO See nsDocShell::DisplayLoadError for a list of all the error 1.1103 + // codes (the status param) we should eventually handle here. 1.1104 + sendAsyncMsg('error', { type: 'other' }); 1.1105 + } 1.1106 + }, 1.1107 + 1.1108 + onSecurityChange: function(webProgress, request, state) { 1.1109 + if (webProgress != docShell) { 1.1110 + return; 1.1111 + } 1.1112 + 1.1113 + var stateDesc; 1.1114 + if (state & Ci.nsIWebProgressListener.STATE_IS_SECURE) { 1.1115 + stateDesc = 'secure'; 1.1116 + } 1.1117 + else if (state & Ci.nsIWebProgressListener.STATE_IS_BROKEN) { 1.1118 + stateDesc = 'broken'; 1.1119 + } 1.1120 + else if (state & Ci.nsIWebProgressListener.STATE_IS_INSECURE) { 1.1121 + stateDesc = 'insecure'; 1.1122 + } 1.1123 + else { 1.1124 + debug("Unexpected securitychange state!"); 1.1125 + stateDesc = '???'; 1.1126 + } 1.1127 + 1.1128 + // XXX Until bug 764496 is fixed, this will always return false. 1.1129 + var isEV = !!(state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL); 1.1130 + 1.1131 + sendAsyncMsg('securitychange', { state: stateDesc, extendedValidation: isEV }); 1.1132 + }, 1.1133 + 1.1134 + onStatusChange: function(webProgress, request, status, message) {}, 1.1135 + onProgressChange: function(webProgress, request, curSelfProgress, 1.1136 + maxSelfProgress, curTotalProgress, maxTotalProgress) {}, 1.1137 + }, 1.1138 + 1.1139 + // Expose the message manager for WebApps and others. 1.1140 + _messageManagerPublic: { 1.1141 + sendAsyncMessage: global.sendAsyncMessage.bind(global), 1.1142 + sendSyncMessage: global.sendSyncMessage.bind(global), 1.1143 + addMessageListener: global.addMessageListener.bind(global), 1.1144 + removeMessageListener: global.removeMessageListener.bind(global) 1.1145 + }, 1.1146 + 1.1147 + get messageManager() { 1.1148 + return this._messageManagerPublic; 1.1149 + } 1.1150 +}; 1.1151 + 1.1152 +var api = new BrowserElementChild(); 1.1153 +