1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/content/browser-child.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,415 @@ 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 + 1.8 +let Cc = Components.classes; 1.9 +let Ci = Components.interfaces; 1.10 +let Cu = Components.utils; 1.11 + 1.12 +Cu.import("resource://gre/modules/Services.jsm"); 1.13 +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); 1.14 +Cu.import("resource://gre/modules/RemoteAddonsChild.jsm"); 1.15 +Cu.import("resource://gre/modules/Timer.jsm"); 1.16 + 1.17 +let SyncHandler = { 1.18 + init: function() { 1.19 + sendAsyncMessage("SetSyncHandler", {}, {handler: this}); 1.20 + }, 1.21 + 1.22 + getFocusedElementAndWindow: function() { 1.23 + let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager); 1.24 + 1.25 + let focusedWindow = {}; 1.26 + let elt = fm.getFocusedElementForWindow(content, true, focusedWindow); 1.27 + return [elt, focusedWindow.value]; 1.28 + }, 1.29 +}; 1.30 + 1.31 +SyncHandler.init(); 1.32 + 1.33 +let WebProgressListener = { 1.34 + init: function() { 1.35 + let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor) 1.36 + .getInterface(Ci.nsIWebProgress); 1.37 + webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL); 1.38 + }, 1.39 + 1.40 + _requestSpec: function (aRequest) { 1.41 + if (!aRequest || !(aRequest instanceof Ci.nsIChannel)) 1.42 + return null; 1.43 + return aRequest.QueryInterface(Ci.nsIChannel).URI.spec; 1.44 + }, 1.45 + 1.46 + _setupJSON: function setupJSON(aWebProgress, aRequest) { 1.47 + return { 1.48 + isTopLevel: aWebProgress.isTopLevel, 1.49 + isLoadingDocument: aWebProgress.isLoadingDocument, 1.50 + requestURI: this._requestSpec(aRequest), 1.51 + loadType: aWebProgress.loadType, 1.52 + documentContentType: content.document && content.document.contentType 1.53 + }; 1.54 + }, 1.55 + 1.56 + _setupObjects: function setupObjects(aWebProgress) { 1.57 + return { 1.58 + contentWindow: content, 1.59 + // DOMWindow is not necessarily the content-window with subframes. 1.60 + DOMWindow: aWebProgress.DOMWindow 1.61 + }; 1.62 + }, 1.63 + 1.64 + onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { 1.65 + let json = this._setupJSON(aWebProgress, aRequest); 1.66 + let objects = this._setupObjects(aWebProgress); 1.67 + 1.68 + json.stateFlags = aStateFlags; 1.69 + json.status = aStatus; 1.70 + 1.71 + sendAsyncMessage("Content:StateChange", json, objects); 1.72 + }, 1.73 + 1.74 + onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) { 1.75 + }, 1.76 + 1.77 + onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) { 1.78 + let json = this._setupJSON(aWebProgress, aRequest); 1.79 + let objects = this._setupObjects(aWebProgress); 1.80 + 1.81 + json.location = aLocationURI ? aLocationURI.spec : ""; 1.82 + json.flags = aFlags; 1.83 + 1.84 + if (json.isTopLevel) { 1.85 + json.canGoBack = docShell.canGoBack; 1.86 + json.canGoForward = docShell.canGoForward; 1.87 + json.documentURI = content.document.documentURIObject.spec; 1.88 + json.charset = content.document.characterSet; 1.89 + } 1.90 + 1.91 + sendAsyncMessage("Content:LocationChange", json, objects); 1.92 + }, 1.93 + 1.94 + onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) { 1.95 + let json = this._setupJSON(aWebProgress, aRequest); 1.96 + let objects = this._setupObjects(aWebProgress); 1.97 + 1.98 + json.status = aStatus; 1.99 + json.message = aMessage; 1.100 + 1.101 + sendAsyncMessage("Content:StatusChange", json, objects); 1.102 + }, 1.103 + 1.104 + onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) { 1.105 + let json = this._setupJSON(aWebProgress, aRequest); 1.106 + let objects = this._setupObjects(aWebProgress); 1.107 + 1.108 + json.state = aState; 1.109 + json.status = SecurityUI.getSSLStatusAsString(); 1.110 + 1.111 + sendAsyncMessage("Content:SecurityChange", json, objects); 1.112 + }, 1.113 + 1.114 + QueryInterface: function QueryInterface(aIID) { 1.115 + if (aIID.equals(Ci.nsIWebProgressListener) || 1.116 + aIID.equals(Ci.nsISupportsWeakReference) || 1.117 + aIID.equals(Ci.nsISupports)) { 1.118 + return this; 1.119 + } 1.120 + 1.121 + throw Components.results.NS_ERROR_NO_INTERFACE; 1.122 + } 1.123 +}; 1.124 + 1.125 +WebProgressListener.init(); 1.126 + 1.127 +let WebNavigation = { 1.128 + _webNavigation: docShell.QueryInterface(Ci.nsIWebNavigation), 1.129 + 1.130 + init: function() { 1.131 + addMessageListener("WebNavigation:GoBack", this); 1.132 + addMessageListener("WebNavigation:GoForward", this); 1.133 + addMessageListener("WebNavigation:GotoIndex", this); 1.134 + addMessageListener("WebNavigation:LoadURI", this); 1.135 + addMessageListener("WebNavigation:Reload", this); 1.136 + addMessageListener("WebNavigation:Stop", this); 1.137 + 1.138 + // Send a CPOW for the sessionHistory object. 1.139 + let history = this._webNavigation.sessionHistory; 1.140 + sendAsyncMessage("WebNavigation:setHistory", {}, {history: history}); 1.141 + }, 1.142 + 1.143 + receiveMessage: function(message) { 1.144 + switch (message.name) { 1.145 + case "WebNavigation:GoBack": 1.146 + this.goBack(); 1.147 + break; 1.148 + case "WebNavigation:GoForward": 1.149 + this.goForward(); 1.150 + break; 1.151 + case "WebNavigation:GotoIndex": 1.152 + this.gotoIndex(message.data.index); 1.153 + break; 1.154 + case "WebNavigation:LoadURI": 1.155 + this.loadURI(message.data.uri, message.data.flags); 1.156 + break; 1.157 + case "WebNavigation:Reload": 1.158 + this.reload(message.data.flags); 1.159 + break; 1.160 + case "WebNavigation:Stop": 1.161 + this.stop(message.data.flags); 1.162 + break; 1.163 + } 1.164 + }, 1.165 + 1.166 + goBack: function() { 1.167 + if (this._webNavigation.canGoBack) 1.168 + this._webNavigation.goBack(); 1.169 + }, 1.170 + 1.171 + goForward: function() { 1.172 + if (this._webNavigation.canGoForward) 1.173 + this._webNavigation.goForward(); 1.174 + }, 1.175 + 1.176 + gotoIndex: function(index) { 1.177 + this._webNavigation.gotoIndex(index); 1.178 + }, 1.179 + 1.180 + loadURI: function(uri, flags) { 1.181 + this._webNavigation.loadURI(uri, flags, null, null, null); 1.182 + }, 1.183 + 1.184 + reload: function(flags) { 1.185 + this._webNavigation.reload(flags); 1.186 + }, 1.187 + 1.188 + stop: function(flags) { 1.189 + this._webNavigation.stop(flags); 1.190 + } 1.191 +}; 1.192 + 1.193 +WebNavigation.init(); 1.194 + 1.195 +let SecurityUI = { 1.196 + getSSLStatusAsString: function() { 1.197 + let status = docShell.securityUI.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus; 1.198 + 1.199 + if (status) { 1.200 + let helper = Cc["@mozilla.org/network/serialization-helper;1"] 1.201 + .getService(Ci.nsISerializationHelper); 1.202 + 1.203 + status.QueryInterface(Ci.nsISerializable); 1.204 + return helper.serializeToString(status); 1.205 + } 1.206 + 1.207 + return null; 1.208 + } 1.209 +}; 1.210 + 1.211 +let ControllerCommands = { 1.212 + init: function () { 1.213 + addMessageListener("ControllerCommands:Do", this); 1.214 + }, 1.215 + 1.216 + receiveMessage: function(message) { 1.217 + switch(message.name) { 1.218 + case "ControllerCommands:Do": 1.219 + if (docShell.isCommandEnabled(message.data)) 1.220 + docShell.doCommand(message.data); 1.221 + break; 1.222 + } 1.223 + } 1.224 +} 1.225 + 1.226 +ControllerCommands.init() 1.227 + 1.228 +addEventListener("DOMTitleChanged", function (aEvent) { 1.229 + let document = content.document; 1.230 + switch (aEvent.type) { 1.231 + case "DOMTitleChanged": 1.232 + if (!aEvent.isTrusted || aEvent.target.defaultView != content) 1.233 + return; 1.234 + 1.235 + sendAsyncMessage("DOMTitleChanged", { title: document.title }); 1.236 + break; 1.237 + } 1.238 +}, false); 1.239 + 1.240 +addEventListener("DOMWindowClose", function (aEvent) { 1.241 + if (!aEvent.isTrusted) 1.242 + return; 1.243 + sendAsyncMessage("DOMWindowClose"); 1.244 + aEvent.preventDefault(); 1.245 +}, false); 1.246 + 1.247 +addEventListener("ImageContentLoaded", function (aEvent) { 1.248 + if (content.document instanceof Ci.nsIImageDocument) { 1.249 + let req = content.document.imageRequest; 1.250 + if (!req.image) 1.251 + return; 1.252 + sendAsyncMessage("ImageDocumentLoaded", { width: req.image.width, 1.253 + height: req.image.height }); 1.254 + } 1.255 +}, false); 1.256 + 1.257 +let DocumentObserver = { 1.258 + init: function() { 1.259 + Services.obs.addObserver(this, "document-element-inserted", false); 1.260 + addEventListener("unload", () => { 1.261 + Services.obs.removeObserver(this, "document-element-inserted"); 1.262 + }); 1.263 + }, 1.264 + 1.265 + observe: function(aSubject, aTopic, aData) { 1.266 + if (aSubject == content.document) { 1.267 + sendAsyncMessage("DocumentInserted", {synthetic: aSubject.mozSyntheticDocument}); 1.268 + } 1.269 + }, 1.270 +}; 1.271 +DocumentObserver.init(); 1.272 + 1.273 +const ZoomManager = { 1.274 + get fullZoom() { 1.275 + return this._cache.fullZoom; 1.276 + }, 1.277 + 1.278 + get textZoom() { 1.279 + return this._cache.textZoom; 1.280 + }, 1.281 + 1.282 + set fullZoom(value) { 1.283 + this._cache.fullZoom = value; 1.284 + this._markupViewer.fullZoom = value; 1.285 + }, 1.286 + 1.287 + set textZoom(value) { 1.288 + this._cache.textZoom = value; 1.289 + this._markupViewer.textZoom = value; 1.290 + }, 1.291 + 1.292 + refreshFullZoom: function() { 1.293 + return this._refreshZoomValue('fullZoom'); 1.294 + }, 1.295 + 1.296 + refreshTextZoom: function() { 1.297 + return this._refreshZoomValue('textZoom'); 1.298 + }, 1.299 + 1.300 + /** 1.301 + * Retrieves specified zoom property value from markupViewer and refreshes 1.302 + * cache if needed. 1.303 + * @param valueName Either 'fullZoom' or 'textZoom'. 1.304 + * @returns Returns true if cached value was actually refreshed. 1.305 + * @private 1.306 + */ 1.307 + _refreshZoomValue: function(valueName) { 1.308 + let actualZoomValue = this._markupViewer[valueName]; 1.309 + if (actualZoomValue != this._cache[valueName]) { 1.310 + this._cache[valueName] = actualZoomValue; 1.311 + return true; 1.312 + } 1.313 + return false; 1.314 + }, 1.315 + 1.316 + get _markupViewer() { 1.317 + return docShell.contentViewer.QueryInterface(Ci.nsIMarkupDocumentViewer); 1.318 + }, 1.319 + 1.320 + _cache: { 1.321 + fullZoom: NaN, 1.322 + textZoom: NaN 1.323 + } 1.324 +}; 1.325 + 1.326 +addMessageListener("FullZoom", function (aMessage) { 1.327 + ZoomManager.fullZoom = aMessage.data.value; 1.328 +}); 1.329 + 1.330 +addMessageListener("TextZoom", function (aMessage) { 1.331 + ZoomManager.textZoom = aMessage.data.value; 1.332 +}); 1.333 + 1.334 +addEventListener("FullZoomChange", function () { 1.335 + if (ZoomManager.refreshFullZoom()) { 1.336 + sendAsyncMessage("FullZoomChange", { value: ZoomManager.fullZoom}); 1.337 + } 1.338 +}, false); 1.339 + 1.340 +addEventListener("TextZoomChange", function (aEvent) { 1.341 + if (ZoomManager.refreshTextZoom()) { 1.342 + sendAsyncMessage("TextZoomChange", { value: ZoomManager.textZoom}); 1.343 + } 1.344 +}, false); 1.345 + 1.346 +RemoteAddonsChild.init(this); 1.347 + 1.348 +addMessageListener("NetworkPrioritizer:AdjustPriority", (msg) => { 1.349 + let webNav = docShell.QueryInterface(Ci.nsIWebNavigation); 1.350 + let loadGroup = webNav.QueryInterface(Ci.nsIDocumentLoader) 1.351 + .loadGroup.QueryInterface(Ci.nsISupportsPriority); 1.352 + loadGroup.adjustPriority(msg.data.adjustment); 1.353 +}); 1.354 + 1.355 +let AutoCompletePopup = { 1.356 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompletePopup]), 1.357 + 1.358 + init: function() { 1.359 + // Hook up the form fill autocomplete controller. 1.360 + let controller = Cc["@mozilla.org/satchel/form-fill-controller;1"] 1.361 + .getService(Ci.nsIFormFillController); 1.362 + 1.363 + controller.attachToBrowser(docShell, this.QueryInterface(Ci.nsIAutoCompletePopup)); 1.364 + 1.365 + this._input = null; 1.366 + this._popupOpen = false; 1.367 + 1.368 + addMessageListener("FormAutoComplete:HandleEnter", message => { 1.369 + this.selectedIndex = message.data.selectedIndex; 1.370 + 1.371 + let controller = Components.classes["@mozilla.org/autocomplete/controller;1"]. 1.372 + getService(Components.interfaces.nsIAutoCompleteController); 1.373 + controller.handleEnter(message.data.isPopupSelection); 1.374 + }); 1.375 + }, 1.376 + 1.377 + get input () { return this._input; }, 1.378 + get overrideValue () { return null; }, 1.379 + set selectedIndex (index) { }, 1.380 + get selectedIndex () { 1.381 + // selectedIndex getter must be synchronous because we need the 1.382 + // correct value when the controller is in controller::HandleEnter. 1.383 + // We can't easily just let the parent inform us the new value every 1.384 + // time it changes because not every action that can change the 1.385 + // selectedIndex is trivial to catch (e.g. moving the mouse over the 1.386 + // list). 1.387 + return sendSyncMessage("FormAutoComplete:GetSelectedIndex", {}); 1.388 + }, 1.389 + get popupOpen () { 1.390 + return this._popupOpen; 1.391 + }, 1.392 + 1.393 + openAutocompletePopup: function (input, element) { 1.394 + this._input = input; 1.395 + this._popupOpen = true; 1.396 + }, 1.397 + 1.398 + closePopup: function () { 1.399 + this._popupOpen = false; 1.400 + sendAsyncMessage("FormAutoComplete:ClosePopup", {}); 1.401 + }, 1.402 + 1.403 + invalidate: function () { 1.404 + }, 1.405 + 1.406 + selectBy: function(reverse, page) { 1.407 + this._index = sendSyncMessage("FormAutoComplete:SelectBy", { 1.408 + reverse: reverse, 1.409 + page: page 1.410 + }); 1.411 + } 1.412 +} 1.413 + 1.414 +let [initData] = sendSyncMessage("Browser:Init"); 1.415 +docShell.useGlobalHistory = initData.useGlobalHistory; 1.416 +if (initData.initPopup) { 1.417 + setTimeout(function() AutoCompletePopup.init(), 0); 1.418 +}