1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/metro/base/content/bindings/browser.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,775 @@ 1.4 +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +let Cc = Components.classes; 1.10 +let Ci = Components.interfaces; 1.11 +let Cu = Components.utils; 1.12 + 1.13 +Cu.import("resource://gre/modules/Services.jsm"); 1.14 +Cu.import("resource://gre/modules/FormData.jsm"); 1.15 +Cu.import("resource://gre/modules/ScrollPosition.jsm"); 1.16 +Cu.import("resource://gre/modules/Timer.jsm", this); 1.17 + 1.18 +let WebProgressListener = { 1.19 + _lastLocation: null, 1.20 + _firstPaint: false, 1.21 + 1.22 + init: function() { 1.23 + let flags = Ci.nsIWebProgress.NOTIFY_LOCATION | 1.24 + Ci.nsIWebProgress.NOTIFY_SECURITY | 1.25 + Ci.nsIWebProgress.NOTIFY_STATE_WINDOW | 1.26 + Ci.nsIWebProgress.NOTIFY_STATE_NETWORK | 1.27 + Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT; 1.28 + 1.29 + let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress); 1.30 + webProgress.addProgressListener(this, flags); 1.31 + }, 1.32 + 1.33 + onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { 1.34 + if (content != aWebProgress.DOMWindow) 1.35 + return; 1.36 + 1.37 + sendAsyncMessage("Content:StateChange", { 1.38 + contentWindowId: this.contentWindowId, 1.39 + stateFlags: aStateFlags, 1.40 + status: aStatus 1.41 + }); 1.42 + }, 1.43 + 1.44 + onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) { 1.45 + if (content != aWebProgress.DOMWindow) 1.46 + return; 1.47 + 1.48 + let spec = aLocationURI ? aLocationURI.spec : ""; 1.49 + let location = spec.split("#")[0]; 1.50 + 1.51 + let charset = content.document.characterSet; 1.52 + 1.53 + sendAsyncMessage("Content:LocationChange", { 1.54 + contentWindowId: this.contentWindowId, 1.55 + documentURI: aWebProgress.DOMWindow.document.documentURIObject.spec, 1.56 + location: spec, 1.57 + canGoBack: docShell.canGoBack, 1.58 + canGoForward: docShell.canGoForward, 1.59 + charset: charset.toString() 1.60 + }); 1.61 + 1.62 + this._firstPaint = false; 1.63 + let self = this; 1.64 + 1.65 + // Keep track of hash changes 1.66 + this.hashChanged = (location == this._lastLocation); 1.67 + this._lastLocation = location; 1.68 + 1.69 + // When a new page is loaded fire a message for the first paint 1.70 + addEventListener("MozAfterPaint", function(aEvent) { 1.71 + removeEventListener("MozAfterPaint", arguments.callee, true); 1.72 + 1.73 + self._firstPaint = true; 1.74 + let scrollOffset = ContentScroll.getScrollOffset(content); 1.75 + sendAsyncMessage("Browser:FirstPaint", scrollOffset); 1.76 + }, true); 1.77 + }, 1.78 + 1.79 + onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) { 1.80 + }, 1.81 + 1.82 + onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) { 1.83 + if (content != aWebProgress.DOMWindow) 1.84 + return; 1.85 + 1.86 + let serialization = SecurityUI.getSSLStatusAsString(); 1.87 + 1.88 + sendAsyncMessage("Content:SecurityChange", { 1.89 + contentWindowId: this.contentWindowId, 1.90 + SSLStatusAsString: serialization, 1.91 + state: aState 1.92 + }); 1.93 + }, 1.94 + 1.95 + get contentWindowId() { 1.96 + return content.QueryInterface(Ci.nsIInterfaceRequestor) 1.97 + .getInterface(Ci.nsIDOMWindowUtils) 1.98 + .currentInnerWindowID; 1.99 + }, 1.100 + 1.101 + QueryInterface: function QueryInterface(aIID) { 1.102 + if (aIID.equals(Ci.nsIWebProgressListener) || 1.103 + aIID.equals(Ci.nsISupportsWeakReference) || 1.104 + aIID.equals(Ci.nsISupports)) { 1.105 + return this; 1.106 + } 1.107 + 1.108 + throw Components.results.NS_ERROR_NO_INTERFACE; 1.109 + } 1.110 +}; 1.111 + 1.112 +WebProgressListener.init(); 1.113 + 1.114 + 1.115 +let SecurityUI = { 1.116 + getSSLStatusAsString: function() { 1.117 + let status = docShell.securityUI.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus; 1.118 + 1.119 + if (status) { 1.120 + let serhelper = Cc["@mozilla.org/network/serialization-helper;1"] 1.121 + .getService(Ci.nsISerializationHelper); 1.122 + 1.123 + status.QueryInterface(Ci.nsISerializable); 1.124 + return serhelper.serializeToString(status); 1.125 + } 1.126 + 1.127 + return null; 1.128 + } 1.129 +}; 1.130 + 1.131 +let WebNavigation = { 1.132 + _webNavigation: docShell.QueryInterface(Ci.nsIWebNavigation), 1.133 + _timer: null, 1.134 + 1.135 + init: function() { 1.136 + addMessageListener("WebNavigation:GoBack", this); 1.137 + addMessageListener("WebNavigation:GoForward", this); 1.138 + addMessageListener("WebNavigation:GotoIndex", this); 1.139 + addMessageListener("WebNavigation:LoadURI", this); 1.140 + addMessageListener("WebNavigation:Reload", this); 1.141 + addMessageListener("WebNavigation:Stop", this); 1.142 + }, 1.143 + 1.144 + receiveMessage: function(message) { 1.145 + switch (message.name) { 1.146 + case "WebNavigation:GoBack": 1.147 + this.goBack(); 1.148 + break; 1.149 + case "WebNavigation:GoForward": 1.150 + this.goForward(); 1.151 + break; 1.152 + case "WebNavigation:GotoIndex": 1.153 + this.gotoIndex(message); 1.154 + break; 1.155 + case "WebNavigation:LoadURI": 1.156 + this.loadURI(message); 1.157 + break; 1.158 + case "WebNavigation:Reload": 1.159 + this.reload(message); 1.160 + break; 1.161 + case "WebNavigation:Stop": 1.162 + this.stop(message); 1.163 + break; 1.164 + } 1.165 + }, 1.166 + 1.167 + goBack: function() { 1.168 + if (this._webNavigation.canGoBack) 1.169 + this._webNavigation.goBack(); 1.170 + }, 1.171 + 1.172 + goForward: function() { 1.173 + if (this._webNavigation.canGoForward) 1.174 + this._webNavigation.goForward(); 1.175 + }, 1.176 + 1.177 + gotoIndex: function(message) { 1.178 + this._webNavigation.gotoIndex(message.index); 1.179 + }, 1.180 + 1.181 + loadURI: function(message) { 1.182 + let flags = message.json.flags || this._webNavigation.LOAD_FLAGS_NONE; 1.183 + this._webNavigation.loadURI(message.json.uri, flags, null, null, null); 1.184 + 1.185 + let tabData = message.json; 1.186 + if (tabData.entries) { 1.187 + // We are going to load from history so kill the current load. We do not 1.188 + // want the load added to the history anyway. We reload after resetting history 1.189 + this._webNavigation.stop(this._webNavigation.STOP_ALL); 1.190 + this._restoreHistory(tabData, 0); 1.191 + } 1.192 + }, 1.193 + 1.194 + reload: function(message) { 1.195 + let flags = message.json.flags || this._webNavigation.LOAD_FLAGS_NONE; 1.196 + this._webNavigation.reload(flags); 1.197 + }, 1.198 + 1.199 + stop: function(message) { 1.200 + let flags = message.json.flags || this._webNavigation.STOP_ALL; 1.201 + this._webNavigation.stop(flags); 1.202 + }, 1.203 + 1.204 + _restoreHistory: function _restoreHistory(aTabData, aCount) { 1.205 + // We need to wait for the sessionHistory to be initialized and there 1.206 + // is no good way to do this. We'll try a wait loop like desktop 1.207 + try { 1.208 + if (!this._webNavigation.sessionHistory) 1.209 + throw new Error(); 1.210 + } catch (ex) { 1.211 + if (aCount < 10) { 1.212 + let self = this; 1.213 + this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); 1.214 + this._timer.initWithCallback(function(aTimer) { 1.215 + self._timer = null; 1.216 + self._restoreHistory(aTabData, aCount + 1); 1.217 + }, 100, Ci.nsITimer.TYPE_ONE_SHOT); 1.218 + return; 1.219 + } 1.220 + } 1.221 + 1.222 + let history = this._webNavigation.sessionHistory; 1.223 + if (history.count > 0) 1.224 + history.PurgeHistory(history.count); 1.225 + history.QueryInterface(Ci.nsISHistoryInternal); 1.226 + 1.227 + // helper hashes for ensuring unique frame IDs and unique document 1.228 + // identifiers. 1.229 + let idMap = { used: {} }; 1.230 + let docIdentMap = {}; 1.231 + 1.232 + for (let i = 0; i < aTabData.entries.length; i++) { 1.233 + if (!aTabData.entries[i].url) 1.234 + continue; 1.235 + history.addEntry(this._deserializeHistoryEntry(aTabData.entries[i], idMap, docIdentMap), true); 1.236 + } 1.237 + 1.238 + // We need to force set the active history item and cause it to reload since 1.239 + // we stop the load above 1.240 + let activeIndex = (aTabData.index || aTabData.entries.length) - 1; 1.241 + history.getEntryAtIndex(activeIndex, true); 1.242 + history.QueryInterface(Ci.nsISHistory).reloadCurrentEntry(); 1.243 + }, 1.244 + 1.245 + _deserializeHistoryEntry: function _deserializeHistoryEntry(aEntry, aIdMap, aDocIdentMap) { 1.246 + let shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].createInstance(Ci.nsISHEntry); 1.247 + 1.248 + shEntry.setURI(Services.io.newURI(aEntry.url, null, null)); 1.249 + shEntry.setTitle(aEntry.title || aEntry.url); 1.250 + if (aEntry.subframe) 1.251 + shEntry.setIsSubFrame(aEntry.subframe || false); 1.252 + shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory; 1.253 + if (aEntry.contentType) 1.254 + shEntry.contentType = aEntry.contentType; 1.255 + if (aEntry.referrer) 1.256 + shEntry.referrerURI = Services.io.newURI(aEntry.referrer, null, null); 1.257 + 1.258 + if (aEntry.cacheKey) { 1.259 + let cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].createInstance(Ci.nsISupportsPRUint32); 1.260 + cacheKey.data = aEntry.cacheKey; 1.261 + shEntry.cacheKey = cacheKey; 1.262 + } 1.263 + 1.264 + if (aEntry.ID) { 1.265 + // get a new unique ID for this frame (since the one from the last 1.266 + // start might already be in use) 1.267 + let id = aIdMap[aEntry.ID] || 0; 1.268 + if (!id) { 1.269 + for (id = Date.now(); id in aIdMap.used; id++); 1.270 + aIdMap[aEntry.ID] = id; 1.271 + aIdMap.used[id] = true; 1.272 + } 1.273 + shEntry.ID = id; 1.274 + } 1.275 + 1.276 + if (aEntry.docshellID) 1.277 + shEntry.docshellID = aEntry.docshellID; 1.278 + 1.279 + if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) { 1.280 + shEntry.stateData = 1.281 + Cc["@mozilla.org/docshell/structured-clone-container;1"]. 1.282 + createInstance(Ci.nsIStructuredCloneContainer); 1.283 + 1.284 + shEntry.stateData.initFromBase64(aEntry.structuredCloneState, aEntry.structuredCloneVersion); 1.285 + } 1.286 + 1.287 + if (aEntry.scroll) { 1.288 + let scrollPos = aEntry.scroll.split(","); 1.289 + scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0]; 1.290 + shEntry.setScrollPosition(scrollPos[0], scrollPos[1]); 1.291 + } 1.292 + 1.293 + let childDocIdents = {}; 1.294 + if (aEntry.docIdentifier) { 1.295 + // If we have a serialized document identifier, try to find an SHEntry 1.296 + // which matches that doc identifier and adopt that SHEntry's 1.297 + // BFCacheEntry. If we don't find a match, insert shEntry as the match 1.298 + // for the document identifier. 1.299 + let matchingEntry = aDocIdentMap[aEntry.docIdentifier]; 1.300 + if (!matchingEntry) { 1.301 + matchingEntry = {shEntry: shEntry, childDocIdents: childDocIdents}; 1.302 + aDocIdentMap[aEntry.docIdentifier] = matchingEntry; 1.303 + } else { 1.304 + shEntry.adoptBFCacheEntry(matchingEntry.shEntry); 1.305 + childDocIdents = matchingEntry.childDocIdents; 1.306 + } 1.307 + } 1.308 + 1.309 + if (aEntry.owner_b64) { 1.310 + let ownerInput = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream); 1.311 + let binaryData = atob(aEntry.owner_b64); 1.312 + ownerInput.setData(binaryData, binaryData.length); 1.313 + let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIObjectInputStream); 1.314 + binaryStream.setInputStream(ownerInput); 1.315 + try { // Catch possible deserialization exceptions 1.316 + shEntry.owner = binaryStream.readObject(true); 1.317 + } catch (ex) { dump(ex); } 1.318 + } 1.319 + 1.320 + if (aEntry.children && shEntry instanceof Ci.nsISHContainer) { 1.321 + for (let i = 0; i < aEntry.children.length; i++) { 1.322 + if (!aEntry.children[i].url) 1.323 + continue; 1.324 + 1.325 + // We're getting sessionrestore.js files with a cycle in the 1.326 + // doc-identifier graph, likely due to bug 698656. (That is, we have 1.327 + // an entry where doc identifier A is an ancestor of doc identifier B, 1.328 + // and another entry where doc identifier B is an ancestor of A.) 1.329 + // 1.330 + // If we were to respect these doc identifiers, we'd create a cycle in 1.331 + // the SHEntries themselves, which causes the docshell to loop forever 1.332 + // when it looks for the root SHEntry. 1.333 + // 1.334 + // So as a hack to fix this, we restrict the scope of a doc identifier 1.335 + // to be a node's siblings and cousins, and pass childDocIdents, not 1.336 + // aDocIdents, to _deserializeHistoryEntry. That is, we say that two 1.337 + // SHEntries with the same doc identifier have the same document iff 1.338 + // they have the same parent or their parents have the same document. 1.339 + 1.340 + shEntry.AddChild(this._deserializeHistoryEntry(aEntry.children[i], aIdMap, childDocIdents), i); 1.341 + } 1.342 + } 1.343 + 1.344 + return shEntry; 1.345 + }, 1.346 + 1.347 + sendHistory: function sendHistory() { 1.348 + // We need to package up the session history and send it to the sessionstore 1.349 + let entries = []; 1.350 + let history = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory; 1.351 + for (let i = 0; i < history.count; i++) { 1.352 + let entry = this._serializeHistoryEntry(history.getEntryAtIndex(i, false)); 1.353 + 1.354 + // If someone directly navigates to one of these URLs and they switch to Desktop, 1.355 + // we need to make the page load-able. 1.356 + if (entry.url == "about:home" || entry.url == "about:start") { 1.357 + entry.url = "about:newtab"; 1.358 + } 1.359 + entries.push(entry); 1.360 + } 1.361 + let index = history.index + 1; 1.362 + sendAsyncMessage("Content:SessionHistory", { entries: entries, index: index }); 1.363 + }, 1.364 + 1.365 + _serializeHistoryEntry: function _serializeHistoryEntry(aEntry) { 1.366 + let entry = { url: aEntry.URI.spec }; 1.367 + 1.368 + if (Util.isURLEmpty(entry.url)) { 1.369 + entry.title = Util.getEmptyURLTabTitle(); 1.370 + } else { 1.371 + entry.title = aEntry.title; 1.372 + } 1.373 + 1.374 + if (!(aEntry instanceof Ci.nsISHEntry)) 1.375 + return entry; 1.376 + 1.377 + let cacheKey = aEntry.cacheKey; 1.378 + if (cacheKey && cacheKey instanceof Ci.nsISupportsPRUint32 && cacheKey.data != 0) 1.379 + entry.cacheKey = cacheKey.data; 1.380 + 1.381 + entry.ID = aEntry.ID; 1.382 + entry.docshellID = aEntry.docshellID; 1.383 + 1.384 + if (aEntry.referrerURI) 1.385 + entry.referrer = aEntry.referrerURI.spec; 1.386 + 1.387 + if (aEntry.contentType) 1.388 + entry.contentType = aEntry.contentType; 1.389 + 1.390 + let x = {}, y = {}; 1.391 + aEntry.getScrollPosition(x, y); 1.392 + if (x.value != 0 || y.value != 0) 1.393 + entry.scroll = x.value + "," + y.value; 1.394 + 1.395 + if (aEntry.owner) { 1.396 + try { 1.397 + let binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIObjectOutputStream); 1.398 + let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe); 1.399 + pipe.init(false, false, 0, 0xffffffff, null); 1.400 + binaryStream.setOutputStream(pipe.outputStream); 1.401 + binaryStream.writeCompoundObject(aEntry.owner, Ci.nsISupports, true); 1.402 + binaryStream.close(); 1.403 + 1.404 + // Now we want to read the data from the pipe's input end and encode it. 1.405 + let scriptableStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream); 1.406 + scriptableStream.setInputStream(pipe.inputStream); 1.407 + let ownerBytes = scriptableStream.readByteArray(scriptableStream.available()); 1.408 + // We can stop doing base64 encoding once our serialization into JSON 1.409 + // is guaranteed to handle all chars in strings, including embedded 1.410 + // nulls. 1.411 + entry.owner_b64 = btoa(String.fromCharCode.apply(null, ownerBytes)); 1.412 + } catch (e) { dump(e); } 1.413 + } 1.414 + 1.415 + entry.docIdentifier = aEntry.BFCacheEntry.ID; 1.416 + 1.417 + if (aEntry.stateData != null) { 1.418 + entry.structuredCloneState = aEntry.stateData.getDataAsBase64(); 1.419 + entry.structuredCloneVersion = aEntry.stateData.formatVersion; 1.420 + } 1.421 + 1.422 + if (!(aEntry instanceof Ci.nsISHContainer)) 1.423 + return entry; 1.424 + 1.425 + if (aEntry.childCount > 0) { 1.426 + entry.children = []; 1.427 + for (let i = 0; i < aEntry.childCount; i++) { 1.428 + let child = aEntry.GetChildAt(i); 1.429 + if (child) 1.430 + entry.children.push(this._serializeHistoryEntry(child)); 1.431 + else // to maintain the correct frame order, insert a dummy entry 1.432 + entry.children.push({ url: "about:blank" }); 1.433 + 1.434 + // don't try to restore framesets containing wyciwyg URLs (cf. bug 424689 and bug 450595) 1.435 + if (/^wyciwyg:\/\//.test(entry.children[i].url)) { 1.436 + delete entry.children; 1.437 + break; 1.438 + } 1.439 + } 1.440 + } 1.441 + 1.442 + return entry; 1.443 + } 1.444 +}; 1.445 + 1.446 +WebNavigation.init(); 1.447 + 1.448 + 1.449 +let DOMEvents = { 1.450 + _timeout: null, 1.451 + _sessionEvents: new Set(), 1.452 + _sessionEventMap: {"SessionStore:collectFormdata" : FormData.collect, 1.453 + "SessionStore:collectScrollPosition" : ScrollPosition.collect}, 1.454 + 1.455 + init: function() { 1.456 + addEventListener("DOMContentLoaded", this, false); 1.457 + addEventListener("DOMTitleChanged", this, false); 1.458 + addEventListener("DOMLinkAdded", this, false); 1.459 + addEventListener("DOMWillOpenModalDialog", this, false); 1.460 + addEventListener("DOMModalDialogClosed", this, true); 1.461 + addEventListener("DOMWindowClose", this, false); 1.462 + addEventListener("DOMPopupBlocked", this, false); 1.463 + addEventListener("pageshow", this, false); 1.464 + addEventListener("pagehide", this, false); 1.465 + 1.466 + addEventListener("input", this, true); 1.467 + addEventListener("change", this, true); 1.468 + addEventListener("scroll", this, true); 1.469 + addMessageListener("SessionStore:restoreSessionTabData", this); 1.470 + }, 1.471 + 1.472 + receiveMessage: function(message) { 1.473 + switch (message.name) { 1.474 + case "SessionStore:restoreSessionTabData": 1.475 + if (message.json.formdata) 1.476 + FormData.restore(content, message.json.formdata); 1.477 + if (message.json.scroll) 1.478 + ScrollPosition.restore(content, message.json.scroll.scroll); 1.479 + break; 1.480 + } 1.481 + }, 1.482 + 1.483 + handleEvent: function(aEvent) { 1.484 + let document = content.document; 1.485 + switch (aEvent.type) { 1.486 + case "DOMContentLoaded": 1.487 + if (document.documentURIObject.spec == "about:blank") 1.488 + return; 1.489 + 1.490 + sendAsyncMessage("DOMContentLoaded", { }); 1.491 + 1.492 + // Send the session history now too 1.493 + WebNavigation.sendHistory(); 1.494 + break; 1.495 + 1.496 + case "pageshow": 1.497 + case "pagehide": { 1.498 + if (aEvent.target.defaultView != content) 1.499 + break; 1.500 + 1.501 + let util = aEvent.target.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) 1.502 + .getInterface(Ci.nsIDOMWindowUtils); 1.503 + 1.504 + let json = { 1.505 + contentWindowWidth: content.innerWidth, 1.506 + contentWindowHeight: content.innerHeight, 1.507 + windowId: util.outerWindowID, 1.508 + persisted: aEvent.persisted 1.509 + }; 1.510 + 1.511 + // Clear onload focus to prevent the VKB to be shown unexpectingly 1.512 + // but only if the location has really changed and not only the 1.513 + // fragment identifier 1.514 + let contentWindowID = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID; 1.515 + if (!WebProgressListener.hashChanged && contentWindowID == util.currentInnerWindowID) { 1.516 + let focusManager = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager); 1.517 + focusManager.clearFocus(content); 1.518 + } 1.519 + 1.520 + sendAsyncMessage(aEvent.type, json); 1.521 + break; 1.522 + } 1.523 + 1.524 + case "DOMPopupBlocked": { 1.525 + let util = aEvent.requestingWindow.QueryInterface(Ci.nsIInterfaceRequestor) 1.526 + .getInterface(Ci.nsIDOMWindowUtils); 1.527 + let json = { 1.528 + windowId: util.outerWindowID, 1.529 + popupWindowURI: { 1.530 + spec: aEvent.popupWindowURI.spec, 1.531 + charset: aEvent.popupWindowURI.originCharset 1.532 + }, 1.533 + popupWindowFeatures: aEvent.popupWindowFeatures, 1.534 + popupWindowName: aEvent.popupWindowName 1.535 + }; 1.536 + 1.537 + sendAsyncMessage("DOMPopupBlocked", json); 1.538 + break; 1.539 + } 1.540 + 1.541 + case "DOMTitleChanged": 1.542 + sendAsyncMessage("DOMTitleChanged", { title: document.title }); 1.543 + break; 1.544 + 1.545 + case "DOMLinkAdded": 1.546 + let target = aEvent.originalTarget; 1.547 + if (!target.href || target.disabled) 1.548 + return; 1.549 + 1.550 + let json = { 1.551 + windowId: target.ownerDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID, 1.552 + href: target.href, 1.553 + charset: document.characterSet, 1.554 + title: target.title, 1.555 + rel: target.rel, 1.556 + type: target.type 1.557 + }; 1.558 + 1.559 + // rel=icon can also have a sizes attribute 1.560 + if (target.hasAttribute("sizes")) 1.561 + json.sizes = target.getAttribute("sizes"); 1.562 + 1.563 + sendAsyncMessage("DOMLinkAdded", json); 1.564 + break; 1.565 + 1.566 + case "DOMWillOpenModalDialog": 1.567 + case "DOMModalDialogClosed": 1.568 + case "DOMWindowClose": 1.569 + let retvals = sendSyncMessage(aEvent.type, { }); 1.570 + for (let i in retvals) { 1.571 + if (retvals[i].preventDefault) { 1.572 + aEvent.preventDefault(); 1.573 + break; 1.574 + } 1.575 + } 1.576 + break; 1.577 + case "input": 1.578 + case "change": 1.579 + this._sessionEvents.add("SessionStore:collectFormdata"); 1.580 + this._sendUpdates(); 1.581 + break; 1.582 + case "scroll": 1.583 + this._sessionEvents.add("SessionStore:collectScrollPosition"); 1.584 + this._sendUpdates(); 1.585 + break; 1.586 + } 1.587 + }, 1.588 + 1.589 + _sendUpdates: function() { 1.590 + if (!this._timeout) { 1.591 + // Wait a little before sending the message to batch multiple changes. 1.592 + this._timeout = setTimeout(function() { 1.593 + for (let eventType of this._sessionEvents) { 1.594 + sendAsyncMessage(eventType, { 1.595 + data: this._sessionEventMap[eventType](content) 1.596 + }); 1.597 + } 1.598 + this._sessionEvents.clear(); 1.599 + clearTimeout(this._timeout); 1.600 + this._timeout = null; 1.601 + }.bind(this), 1000); 1.602 + } 1.603 + } 1.604 +}; 1.605 + 1.606 +DOMEvents.init(); 1.607 + 1.608 +let ContentScroll = { 1.609 + // The most recent offset set by APZC for the root scroll frame 1.610 + _scrollOffset: { x: 0, y: 0 }, 1.611 + 1.612 + init: function() { 1.613 + addMessageListener("Content:SetWindowSize", this); 1.614 + 1.615 + addEventListener("pagehide", this, false); 1.616 + addEventListener("MozScrolledAreaChanged", this, false); 1.617 + }, 1.618 + 1.619 + getScrollOffset: function(aWindow) { 1.620 + let cwu = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); 1.621 + let scrollX = {}, scrollY = {}; 1.622 + cwu.getScrollXY(false, scrollX, scrollY); 1.623 + return { x: scrollX.value, y: scrollY.value }; 1.624 + }, 1.625 + 1.626 + getScrollOffsetForElement: function(aElement) { 1.627 + if (aElement.parentNode == aElement.ownerDocument) 1.628 + return this.getScrollOffset(aElement.ownerDocument.defaultView); 1.629 + return { x: aElement.scrollLeft, y: aElement.scrollTop }; 1.630 + }, 1.631 + 1.632 + receiveMessage: function(aMessage) { 1.633 + let json = aMessage.json; 1.634 + switch (aMessage.name) { 1.635 + case "Content:SetWindowSize": { 1.636 + let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); 1.637 + cwu.setCSSViewport(json.width, json.height); 1.638 + sendAsyncMessage("Content:SetWindowSize:Complete", {}); 1.639 + break; 1.640 + } 1.641 + } 1.642 + }, 1.643 + 1.644 + handleEvent: function(aEvent) { 1.645 + switch (aEvent.type) { 1.646 + case "pagehide": 1.647 + this._scrollOffset = { x: 0, y: 0 }; 1.648 + break; 1.649 + 1.650 + case "MozScrolledAreaChanged": { 1.651 + let doc = aEvent.originalTarget; 1.652 + if (content != doc.defaultView) // We are only interested in root scroll pane changes 1.653 + return; 1.654 + 1.655 + sendAsyncMessage("MozScrolledAreaChanged", { 1.656 + width: aEvent.width, 1.657 + height: aEvent.height, 1.658 + left: aEvent.x + content.scrollX 1.659 + }); 1.660 + 1.661 + // Send event only after painting to make sure content views in the parent process have 1.662 + // been updated. 1.663 + addEventListener("MozAfterPaint", function afterPaint() { 1.664 + removeEventListener("MozAfterPaint", afterPaint, false); 1.665 + sendAsyncMessage("Content:UpdateDisplayPort"); 1.666 + }, false); 1.667 + 1.668 + break; 1.669 + } 1.670 + } 1.671 + } 1.672 +}; 1.673 +this.ContentScroll = ContentScroll; 1.674 + 1.675 +ContentScroll.init(); 1.676 + 1.677 +let ContentActive = { 1.678 + init: function() { 1.679 + addMessageListener("Content:Activate", this); 1.680 + addMessageListener("Content:Deactivate", this); 1.681 + }, 1.682 + 1.683 + receiveMessage: function(aMessage) { 1.684 + let json = aMessage.json; 1.685 + switch (aMessage.name) { 1.686 + case "Content:Deactivate": 1.687 + docShell.isActive = false; 1.688 + let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); 1.689 + if (json.keepviewport) 1.690 + break; 1.691 + cwu.setDisplayPortForElement(0, 0, 0, 0, content.document.documentElement, 0); 1.692 + break; 1.693 + 1.694 + case "Content:Activate": 1.695 + docShell.isActive = true; 1.696 + break; 1.697 + } 1.698 + } 1.699 +}; 1.700 + 1.701 +ContentActive.init(); 1.702 + 1.703 +/** 1.704 + * Helper class for IndexedDB, child part. Listens using 1.705 + * the observer service for events regarding IndexedDB 1.706 + * prompts, and sends messages to the parent to actually 1.707 + * show the prompts. 1.708 + */ 1.709 +let IndexedDB = { 1.710 + _permissionsPrompt: "indexedDB-permissions-prompt", 1.711 + _permissionsResponse: "indexedDB-permissions-response", 1.712 + 1.713 + _quotaPrompt: "indexedDB-quota-prompt", 1.714 + _quotaResponse: "indexedDB-quota-response", 1.715 + _quotaCancel: "indexedDB-quota-cancel", 1.716 + 1.717 + waitingObservers: [], 1.718 + 1.719 + init: function IndexedDBPromptHelper_init() { 1.720 + let os = Services.obs; 1.721 + os.addObserver(this, this._permissionsPrompt, false); 1.722 + os.addObserver(this, this._quotaPrompt, false); 1.723 + os.addObserver(this, this._quotaCancel, false); 1.724 + addMessageListener("IndexedDB:Response", this); 1.725 + }, 1.726 + 1.727 + observe: function IndexedDBPromptHelper_observe(aSubject, aTopic, aData) { 1.728 + if (aTopic != this._permissionsPrompt && aTopic != this._quotaPrompt && aTopic != this._quotaCancel) { 1.729 + throw new Error("Unexpected topic!"); 1.730 + } 1.731 + 1.732 + let requestor = aSubject.QueryInterface(Ci.nsIInterfaceRequestor); 1.733 + let observer = requestor.getInterface(Ci.nsIObserver); 1.734 + 1.735 + let contentWindow = requestor.getInterface(Ci.nsIDOMWindow); 1.736 + let contentDocument = contentWindow.document; 1.737 + 1.738 + if (aTopic == this._quotaCancel) { 1.739 + observer.observe(null, this._quotaResponse, Ci.nsIPermissionManager.UNKNOWN_ACTION); 1.740 + return; 1.741 + } 1.742 + 1.743 + // Remote to parent 1.744 + sendAsyncMessage("IndexedDB:Prompt", { 1.745 + topic: aTopic, 1.746 + host: contentDocument.documentURIObject.asciiHost, 1.747 + location: contentDocument.location.toString(), 1.748 + data: aData, 1.749 + observerId: this.addWaitingObserver(observer) 1.750 + }); 1.751 + }, 1.752 + 1.753 + receiveMessage: function(aMessage) { 1.754 + let payload = aMessage.json; 1.755 + switch (aMessage.name) { 1.756 + case "IndexedDB:Response": 1.757 + let observer = this.getAndRemoveWaitingObserver(payload.observerId); 1.758 + observer.observe(null, payload.responseTopic, payload.permission); 1.759 + } 1.760 + }, 1.761 + 1.762 + addWaitingObserver: function(aObserver) { 1.763 + let observerId = 0; 1.764 + while (observerId in this.waitingObservers) 1.765 + observerId++; 1.766 + this.waitingObservers[observerId] = aObserver; 1.767 + return observerId; 1.768 + }, 1.769 + 1.770 + getAndRemoveWaitingObserver: function(aObserverId) { 1.771 + let observer = this.waitingObservers[aObserverId]; 1.772 + delete this.waitingObservers[aObserverId]; 1.773 + return observer; 1.774 + } 1.775 +}; 1.776 + 1.777 +IndexedDB.init(); 1.778 +