michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: let Cc = Components.classes; michael@0: let Ci = Components.interfaces; michael@0: let Cu = Components.utils; michael@0: michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import('resource://gre/modules/XPCOMUtils.jsm'); michael@0: Cu.import("resource://gre/modules/RemoteAddonsChild.jsm"); michael@0: Cu.import("resource://gre/modules/Timer.jsm"); michael@0: michael@0: let SyncHandler = { michael@0: init: function() { michael@0: sendAsyncMessage("SetSyncHandler", {}, {handler: this}); michael@0: }, michael@0: michael@0: getFocusedElementAndWindow: function() { michael@0: let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager); michael@0: michael@0: let focusedWindow = {}; michael@0: let elt = fm.getFocusedElementForWindow(content, true, focusedWindow); michael@0: return [elt, focusedWindow.value]; michael@0: }, michael@0: }; michael@0: michael@0: SyncHandler.init(); michael@0: michael@0: let WebProgressListener = { michael@0: init: function() { michael@0: let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIWebProgress); michael@0: webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL); michael@0: }, michael@0: michael@0: _requestSpec: function (aRequest) { michael@0: if (!aRequest || !(aRequest instanceof Ci.nsIChannel)) michael@0: return null; michael@0: return aRequest.QueryInterface(Ci.nsIChannel).URI.spec; michael@0: }, michael@0: michael@0: _setupJSON: function setupJSON(aWebProgress, aRequest) { michael@0: return { michael@0: isTopLevel: aWebProgress.isTopLevel, michael@0: isLoadingDocument: aWebProgress.isLoadingDocument, michael@0: requestURI: this._requestSpec(aRequest), michael@0: loadType: aWebProgress.loadType, michael@0: documentContentType: content.document && content.document.contentType michael@0: }; michael@0: }, michael@0: michael@0: _setupObjects: function setupObjects(aWebProgress) { michael@0: return { michael@0: contentWindow: content, michael@0: // DOMWindow is not necessarily the content-window with subframes. michael@0: DOMWindow: aWebProgress.DOMWindow michael@0: }; michael@0: }, michael@0: michael@0: onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { michael@0: let json = this._setupJSON(aWebProgress, aRequest); michael@0: let objects = this._setupObjects(aWebProgress); michael@0: michael@0: json.stateFlags = aStateFlags; michael@0: json.status = aStatus; michael@0: michael@0: sendAsyncMessage("Content:StateChange", json, objects); michael@0: }, michael@0: michael@0: onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) { michael@0: }, michael@0: michael@0: onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) { michael@0: let json = this._setupJSON(aWebProgress, aRequest); michael@0: let objects = this._setupObjects(aWebProgress); michael@0: michael@0: json.location = aLocationURI ? aLocationURI.spec : ""; michael@0: json.flags = aFlags; michael@0: michael@0: if (json.isTopLevel) { michael@0: json.canGoBack = docShell.canGoBack; michael@0: json.canGoForward = docShell.canGoForward; michael@0: json.documentURI = content.document.documentURIObject.spec; michael@0: json.charset = content.document.characterSet; michael@0: } michael@0: michael@0: sendAsyncMessage("Content:LocationChange", json, objects); michael@0: }, michael@0: michael@0: onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) { michael@0: let json = this._setupJSON(aWebProgress, aRequest); michael@0: let objects = this._setupObjects(aWebProgress); michael@0: michael@0: json.status = aStatus; michael@0: json.message = aMessage; michael@0: michael@0: sendAsyncMessage("Content:StatusChange", json, objects); michael@0: }, michael@0: michael@0: onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) { michael@0: let json = this._setupJSON(aWebProgress, aRequest); michael@0: let objects = this._setupObjects(aWebProgress); michael@0: michael@0: json.state = aState; michael@0: json.status = SecurityUI.getSSLStatusAsString(); michael@0: michael@0: sendAsyncMessage("Content:SecurityChange", json, objects); michael@0: }, michael@0: michael@0: QueryInterface: function QueryInterface(aIID) { michael@0: if (aIID.equals(Ci.nsIWebProgressListener) || michael@0: aIID.equals(Ci.nsISupportsWeakReference) || michael@0: aIID.equals(Ci.nsISupports)) { michael@0: return this; michael@0: } michael@0: michael@0: throw Components.results.NS_ERROR_NO_INTERFACE; michael@0: } michael@0: }; michael@0: michael@0: WebProgressListener.init(); michael@0: michael@0: let WebNavigation = { michael@0: _webNavigation: docShell.QueryInterface(Ci.nsIWebNavigation), michael@0: michael@0: init: function() { michael@0: addMessageListener("WebNavigation:GoBack", this); michael@0: addMessageListener("WebNavigation:GoForward", this); michael@0: addMessageListener("WebNavigation:GotoIndex", this); michael@0: addMessageListener("WebNavigation:LoadURI", this); michael@0: addMessageListener("WebNavigation:Reload", this); michael@0: addMessageListener("WebNavigation:Stop", this); michael@0: michael@0: // Send a CPOW for the sessionHistory object. michael@0: let history = this._webNavigation.sessionHistory; michael@0: sendAsyncMessage("WebNavigation:setHistory", {}, {history: history}); michael@0: }, michael@0: michael@0: receiveMessage: function(message) { michael@0: switch (message.name) { michael@0: case "WebNavigation:GoBack": michael@0: this.goBack(); michael@0: break; michael@0: case "WebNavigation:GoForward": michael@0: this.goForward(); michael@0: break; michael@0: case "WebNavigation:GotoIndex": michael@0: this.gotoIndex(message.data.index); michael@0: break; michael@0: case "WebNavigation:LoadURI": michael@0: this.loadURI(message.data.uri, message.data.flags); michael@0: break; michael@0: case "WebNavigation:Reload": michael@0: this.reload(message.data.flags); michael@0: break; michael@0: case "WebNavigation:Stop": michael@0: this.stop(message.data.flags); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: goBack: function() { michael@0: if (this._webNavigation.canGoBack) michael@0: this._webNavigation.goBack(); michael@0: }, michael@0: michael@0: goForward: function() { michael@0: if (this._webNavigation.canGoForward) michael@0: this._webNavigation.goForward(); michael@0: }, michael@0: michael@0: gotoIndex: function(index) { michael@0: this._webNavigation.gotoIndex(index); michael@0: }, michael@0: michael@0: loadURI: function(uri, flags) { michael@0: this._webNavigation.loadURI(uri, flags, null, null, null); michael@0: }, michael@0: michael@0: reload: function(flags) { michael@0: this._webNavigation.reload(flags); michael@0: }, michael@0: michael@0: stop: function(flags) { michael@0: this._webNavigation.stop(flags); michael@0: } michael@0: }; michael@0: michael@0: WebNavigation.init(); michael@0: michael@0: let SecurityUI = { michael@0: getSSLStatusAsString: function() { michael@0: let status = docShell.securityUI.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus; michael@0: michael@0: if (status) { michael@0: let helper = Cc["@mozilla.org/network/serialization-helper;1"] michael@0: .getService(Ci.nsISerializationHelper); michael@0: michael@0: status.QueryInterface(Ci.nsISerializable); michael@0: return helper.serializeToString(status); michael@0: } michael@0: michael@0: return null; michael@0: } michael@0: }; michael@0: michael@0: let ControllerCommands = { michael@0: init: function () { michael@0: addMessageListener("ControllerCommands:Do", this); michael@0: }, michael@0: michael@0: receiveMessage: function(message) { michael@0: switch(message.name) { michael@0: case "ControllerCommands:Do": michael@0: if (docShell.isCommandEnabled(message.data)) michael@0: docShell.doCommand(message.data); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: ControllerCommands.init() michael@0: michael@0: addEventListener("DOMTitleChanged", function (aEvent) { michael@0: let document = content.document; michael@0: switch (aEvent.type) { michael@0: case "DOMTitleChanged": michael@0: if (!aEvent.isTrusted || aEvent.target.defaultView != content) michael@0: return; michael@0: michael@0: sendAsyncMessage("DOMTitleChanged", { title: document.title }); michael@0: break; michael@0: } michael@0: }, false); michael@0: michael@0: addEventListener("DOMWindowClose", function (aEvent) { michael@0: if (!aEvent.isTrusted) michael@0: return; michael@0: sendAsyncMessage("DOMWindowClose"); michael@0: aEvent.preventDefault(); michael@0: }, false); michael@0: michael@0: addEventListener("ImageContentLoaded", function (aEvent) { michael@0: if (content.document instanceof Ci.nsIImageDocument) { michael@0: let req = content.document.imageRequest; michael@0: if (!req.image) michael@0: return; michael@0: sendAsyncMessage("ImageDocumentLoaded", { width: req.image.width, michael@0: height: req.image.height }); michael@0: } michael@0: }, false); michael@0: michael@0: let DocumentObserver = { michael@0: init: function() { michael@0: Services.obs.addObserver(this, "document-element-inserted", false); michael@0: addEventListener("unload", () => { michael@0: Services.obs.removeObserver(this, "document-element-inserted"); michael@0: }); michael@0: }, michael@0: michael@0: observe: function(aSubject, aTopic, aData) { michael@0: if (aSubject == content.document) { michael@0: sendAsyncMessage("DocumentInserted", {synthetic: aSubject.mozSyntheticDocument}); michael@0: } michael@0: }, michael@0: }; michael@0: DocumentObserver.init(); michael@0: michael@0: const ZoomManager = { michael@0: get fullZoom() { michael@0: return this._cache.fullZoom; michael@0: }, michael@0: michael@0: get textZoom() { michael@0: return this._cache.textZoom; michael@0: }, michael@0: michael@0: set fullZoom(value) { michael@0: this._cache.fullZoom = value; michael@0: this._markupViewer.fullZoom = value; michael@0: }, michael@0: michael@0: set textZoom(value) { michael@0: this._cache.textZoom = value; michael@0: this._markupViewer.textZoom = value; michael@0: }, michael@0: michael@0: refreshFullZoom: function() { michael@0: return this._refreshZoomValue('fullZoom'); michael@0: }, michael@0: michael@0: refreshTextZoom: function() { michael@0: return this._refreshZoomValue('textZoom'); michael@0: }, michael@0: michael@0: /** michael@0: * Retrieves specified zoom property value from markupViewer and refreshes michael@0: * cache if needed. michael@0: * @param valueName Either 'fullZoom' or 'textZoom'. michael@0: * @returns Returns true if cached value was actually refreshed. michael@0: * @private michael@0: */ michael@0: _refreshZoomValue: function(valueName) { michael@0: let actualZoomValue = this._markupViewer[valueName]; michael@0: if (actualZoomValue != this._cache[valueName]) { michael@0: this._cache[valueName] = actualZoomValue; michael@0: return true; michael@0: } michael@0: return false; michael@0: }, michael@0: michael@0: get _markupViewer() { michael@0: return docShell.contentViewer.QueryInterface(Ci.nsIMarkupDocumentViewer); michael@0: }, michael@0: michael@0: _cache: { michael@0: fullZoom: NaN, michael@0: textZoom: NaN michael@0: } michael@0: }; michael@0: michael@0: addMessageListener("FullZoom", function (aMessage) { michael@0: ZoomManager.fullZoom = aMessage.data.value; michael@0: }); michael@0: michael@0: addMessageListener("TextZoom", function (aMessage) { michael@0: ZoomManager.textZoom = aMessage.data.value; michael@0: }); michael@0: michael@0: addEventListener("FullZoomChange", function () { michael@0: if (ZoomManager.refreshFullZoom()) { michael@0: sendAsyncMessage("FullZoomChange", { value: ZoomManager.fullZoom}); michael@0: } michael@0: }, false); michael@0: michael@0: addEventListener("TextZoomChange", function (aEvent) { michael@0: if (ZoomManager.refreshTextZoom()) { michael@0: sendAsyncMessage("TextZoomChange", { value: ZoomManager.textZoom}); michael@0: } michael@0: }, false); michael@0: michael@0: RemoteAddonsChild.init(this); michael@0: michael@0: addMessageListener("NetworkPrioritizer:AdjustPriority", (msg) => { michael@0: let webNav = docShell.QueryInterface(Ci.nsIWebNavigation); michael@0: let loadGroup = webNav.QueryInterface(Ci.nsIDocumentLoader) michael@0: .loadGroup.QueryInterface(Ci.nsISupportsPriority); michael@0: loadGroup.adjustPriority(msg.data.adjustment); michael@0: }); michael@0: michael@0: let AutoCompletePopup = { michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompletePopup]), michael@0: michael@0: init: function() { michael@0: // Hook up the form fill autocomplete controller. michael@0: let controller = Cc["@mozilla.org/satchel/form-fill-controller;1"] michael@0: .getService(Ci.nsIFormFillController); michael@0: michael@0: controller.attachToBrowser(docShell, this.QueryInterface(Ci.nsIAutoCompletePopup)); michael@0: michael@0: this._input = null; michael@0: this._popupOpen = false; michael@0: michael@0: addMessageListener("FormAutoComplete:HandleEnter", message => { michael@0: this.selectedIndex = message.data.selectedIndex; michael@0: michael@0: let controller = Components.classes["@mozilla.org/autocomplete/controller;1"]. michael@0: getService(Components.interfaces.nsIAutoCompleteController); michael@0: controller.handleEnter(message.data.isPopupSelection); michael@0: }); michael@0: }, michael@0: michael@0: get input () { return this._input; }, michael@0: get overrideValue () { return null; }, michael@0: set selectedIndex (index) { }, michael@0: get selectedIndex () { michael@0: // selectedIndex getter must be synchronous because we need the michael@0: // correct value when the controller is in controller::HandleEnter. michael@0: // We can't easily just let the parent inform us the new value every michael@0: // time it changes because not every action that can change the michael@0: // selectedIndex is trivial to catch (e.g. moving the mouse over the michael@0: // list). michael@0: return sendSyncMessage("FormAutoComplete:GetSelectedIndex", {}); michael@0: }, michael@0: get popupOpen () { michael@0: return this._popupOpen; michael@0: }, michael@0: michael@0: openAutocompletePopup: function (input, element) { michael@0: this._input = input; michael@0: this._popupOpen = true; michael@0: }, michael@0: michael@0: closePopup: function () { michael@0: this._popupOpen = false; michael@0: sendAsyncMessage("FormAutoComplete:ClosePopup", {}); michael@0: }, michael@0: michael@0: invalidate: function () { michael@0: }, michael@0: michael@0: selectBy: function(reverse, page) { michael@0: this._index = sendSyncMessage("FormAutoComplete:SelectBy", { michael@0: reverse: reverse, michael@0: page: page michael@0: }); michael@0: } michael@0: } michael@0: michael@0: let [initData] = sendSyncMessage("Browser:Init"); michael@0: docShell.useGlobalHistory = initData.useGlobalHistory; michael@0: if (initData.initPopup) { michael@0: setTimeout(function() AutoCompletePopup.init(), 0); michael@0: }