Frame scripts were not getting pageshow and pagehide events with the persisted property during frameloader swapping due to the fact that the event target for those events was set to be the chrome event handler. This patch makes the event target be the nsInProcessTabChildGlobal instead, so frame scripts see the events. This still needs to be fixed for the e10s case, but that will have to wait until bug 918634 is fixed.
687 lines
21 KiB
JavaScript
687 lines
21 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
|
|
"resource://gre/modules/BrowserUtils.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "ContentLinkHandler",
|
|
"resource:///modules/ContentLinkHandler.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
|
|
"resource://gre/modules/LoginManagerContent.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "InsecurePasswordUtils",
|
|
"resource://gre/modules/InsecurePasswordUtils.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
|
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
|
|
"resource:///modules/UITour.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "FormSubmitObserver",
|
|
"resource:///modules/FormSubmitObserver.jsm");
|
|
|
|
// TabChildGlobal
|
|
var global = this;
|
|
|
|
// Load the form validation popup handler
|
|
var formSubmitObserver = new FormSubmitObserver(content, this);
|
|
|
|
addMessageListener("Browser:HideSessionRestoreButton", function (message) {
|
|
// Hide session restore button on about:home
|
|
let doc = content.document;
|
|
let container;
|
|
if (doc.documentURI.toLowerCase() == "about:home" &&
|
|
(container = doc.getElementById("sessionRestoreContainer"))) {
|
|
container.hidden = true;
|
|
}
|
|
});
|
|
|
|
addMessageListener("Browser:Reload", function(message) {
|
|
/* First, we'll try to use the session history object to reload so
|
|
* that framesets are handled properly. If we're in a special
|
|
* window (such as view-source) that has no session history, fall
|
|
* back on using the web navigation's reload method.
|
|
*/
|
|
|
|
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
|
|
try {
|
|
let sh = webNav.sessionHistory;
|
|
if (sh)
|
|
webNav = sh.QueryInterface(Ci.nsIWebNavigation);
|
|
} catch (e) {
|
|
}
|
|
|
|
let reloadFlags = message.data.flags;
|
|
let handlingUserInput;
|
|
try {
|
|
handlingUserInput = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindowUtils)
|
|
.setHandlingUserInput(message.data.handlingUserInput);
|
|
webNav.reload(reloadFlags);
|
|
} catch (e) {
|
|
} finally {
|
|
handlingUserInput.destruct();
|
|
}
|
|
});
|
|
|
|
addEventListener("DOMFormHasPassword", function(event) {
|
|
InsecurePasswordUtils.checkForInsecurePasswords(event.target);
|
|
LoginManagerContent.onFormPassword(event);
|
|
});
|
|
addEventListener("DOMAutoComplete", function(event) {
|
|
LoginManagerContent.onUsernameInput(event);
|
|
});
|
|
addEventListener("blur", function(event) {
|
|
LoginManagerContent.onUsernameInput(event);
|
|
});
|
|
|
|
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
|
addEventListener("contextmenu", function (event) {
|
|
let defaultPrevented = event.defaultPrevented;
|
|
if (!Services.prefs.getBoolPref("dom.event.contextmenu.enabled")) {
|
|
let plugin = null;
|
|
try {
|
|
plugin = event.target.QueryInterface(Ci.nsIObjectLoadingContent);
|
|
} catch (e) {}
|
|
if (plugin && plugin.displayedType == Ci.nsIObjectLoadingContent.TYPE_PLUGIN) {
|
|
// Don't open a context menu for plugins.
|
|
return;
|
|
}
|
|
|
|
defaultPrevented = false;
|
|
}
|
|
|
|
if (!defaultPrevented) {
|
|
sendSyncMessage("contextmenu", {}, { event: event });
|
|
}
|
|
}, false);
|
|
} else {
|
|
addEventListener("mozUITour", function(event) {
|
|
if (!Services.prefs.getBoolPref("browser.uitour.enabled"))
|
|
return;
|
|
|
|
let handled = UITour.onPageEvent(event);
|
|
if (handled)
|
|
addEventListener("pagehide", UITour);
|
|
}, false, true);
|
|
}
|
|
|
|
let AboutHomeListener = {
|
|
init: function(chromeGlobal) {
|
|
chromeGlobal.addEventListener('AboutHomeLoad', this, false, true);
|
|
},
|
|
|
|
get isAboutHome() {
|
|
return content.document.documentURI.toLowerCase() == "about:home";
|
|
},
|
|
|
|
handleEvent: function(aEvent) {
|
|
if (!this.isAboutHome) {
|
|
return;
|
|
}
|
|
switch (aEvent.type) {
|
|
case "AboutHomeLoad":
|
|
this.onPageLoad();
|
|
break;
|
|
case "AboutHomeSearchEvent":
|
|
this.onSearch(aEvent);
|
|
break;
|
|
case "click":
|
|
this.onClick(aEvent);
|
|
break;
|
|
case "pagehide":
|
|
this.onPageHide(aEvent);
|
|
break;
|
|
}
|
|
},
|
|
|
|
receiveMessage: function(aMessage) {
|
|
if (!this.isAboutHome) {
|
|
return;
|
|
}
|
|
switch (aMessage.name) {
|
|
case "AboutHome:Update":
|
|
this.onUpdate(aMessage.data);
|
|
break;
|
|
case "AboutHome:FocusInput":
|
|
this.onFocusInput();
|
|
break;
|
|
}
|
|
},
|
|
|
|
onUpdate: function(aData) {
|
|
let doc = content.document;
|
|
if (aData.showRestoreLastSession && !PrivateBrowsingUtils.isWindowPrivate(content))
|
|
doc.getElementById("launcher").setAttribute("session", "true");
|
|
|
|
// Inject search engine and snippets URL.
|
|
let docElt = doc.documentElement;
|
|
// set the following attributes BEFORE searchEngineName, which triggers to
|
|
// show the snippets when it's set.
|
|
docElt.setAttribute("snippetsURL", aData.snippetsURL);
|
|
if (aData.showKnowYourRights)
|
|
docElt.setAttribute("showKnowYourRights", "true");
|
|
docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
|
|
docElt.setAttribute("searchEngineName", aData.defaultEngineName);
|
|
},
|
|
|
|
onPageLoad: function() {
|
|
let doc = content.document;
|
|
if (doc.documentElement.hasAttribute("hasBrowserHandlers")) {
|
|
return;
|
|
}
|
|
|
|
doc.documentElement.setAttribute("hasBrowserHandlers", "true");
|
|
addMessageListener("AboutHome:Update", this);
|
|
addMessageListener("AboutHome:FocusInput", this);
|
|
addEventListener("click", this, true);
|
|
addEventListener("pagehide", this, true);
|
|
|
|
// XXX bug 738646 - when Marketplace is launched, remove this statement and
|
|
// the hidden attribute set on the apps button in aboutHome.xhtml
|
|
if (Services.prefs.getPrefType("browser.aboutHome.apps") == Services.prefs.PREF_BOOL &&
|
|
Services.prefs.getBoolPref("browser.aboutHome.apps"))
|
|
doc.getElementById("apps").removeAttribute("hidden");
|
|
|
|
sendAsyncMessage("AboutHome:RequestUpdate");
|
|
doc.addEventListener("AboutHomeSearchEvent", this, true, true);
|
|
},
|
|
|
|
onClick: function(aEvent) {
|
|
if (!aEvent.isTrusted || // Don't trust synthetic events
|
|
aEvent.button == 2 || aEvent.target.localName != "button") {
|
|
return;
|
|
}
|
|
|
|
let originalTarget = aEvent.originalTarget;
|
|
let ownerDoc = originalTarget.ownerDocument;
|
|
if (ownerDoc.documentURI != "about:home") {
|
|
// This shouldn't happen, but we're being defensive.
|
|
return;
|
|
}
|
|
|
|
let elmId = originalTarget.getAttribute("id");
|
|
|
|
switch (elmId) {
|
|
case "restorePreviousSession":
|
|
sendAsyncMessage("AboutHome:RestorePreviousSession");
|
|
ownerDoc.getElementById("launcher").removeAttribute("session");
|
|
break;
|
|
|
|
case "downloads":
|
|
sendAsyncMessage("AboutHome:Downloads");
|
|
break;
|
|
|
|
case "bookmarks":
|
|
sendAsyncMessage("AboutHome:Bookmarks");
|
|
break;
|
|
|
|
case "history":
|
|
sendAsyncMessage("AboutHome:History");
|
|
break;
|
|
|
|
case "apps":
|
|
sendAsyncMessage("AboutHome:Apps");
|
|
break;
|
|
|
|
case "addons":
|
|
sendAsyncMessage("AboutHome:Addons");
|
|
break;
|
|
|
|
case "sync":
|
|
sendAsyncMessage("AboutHome:Sync");
|
|
break;
|
|
|
|
case "settings":
|
|
sendAsyncMessage("AboutHome:Settings");
|
|
break;
|
|
}
|
|
},
|
|
|
|
onPageHide: function(aEvent) {
|
|
if (aEvent.target.defaultView.frameElement) {
|
|
return;
|
|
}
|
|
removeMessageListener("AboutHome:Update", this);
|
|
removeEventListener("click", this, true);
|
|
removeEventListener("pagehide", this, true);
|
|
if (aEvent.target.documentElement) {
|
|
aEvent.target.documentElement.removeAttribute("hasBrowserHandlers");
|
|
}
|
|
},
|
|
|
|
onSearch: function(aEvent) {
|
|
sendAsyncMessage("AboutHome:Search", { searchData: aEvent.detail });
|
|
},
|
|
|
|
onFocusInput: function () {
|
|
let searchInput = content.document.getElementById("searchText");
|
|
if (searchInput) {
|
|
searchInput.focus();
|
|
}
|
|
},
|
|
};
|
|
AboutHomeListener.init(this);
|
|
|
|
|
|
// An event listener for custom "WebChannelMessageToChrome" events on pages
|
|
addEventListener("WebChannelMessageToChrome", function (e) {
|
|
// if target is window then we want the document principal, otherwise fallback to target itself.
|
|
let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal;
|
|
|
|
if (e.detail) {
|
|
sendAsyncMessage("WebChannelMessageToChrome", e.detail, null, principal);
|
|
} else {
|
|
Cu.reportError("WebChannel message failed. No message detail.");
|
|
}
|
|
}, true, true);
|
|
|
|
// Add message listener for "WebChannelMessageToContent" messages from chrome scripts
|
|
addMessageListener("WebChannelMessageToContent", function (e) {
|
|
if (e.data) {
|
|
content.dispatchEvent(new content.CustomEvent("WebChannelMessageToContent", {
|
|
detail: Cu.cloneInto({
|
|
id: e.data.id,
|
|
message: e.data.message,
|
|
}, content),
|
|
}));
|
|
} else {
|
|
Cu.reportError("WebChannel message failed. No message data.");
|
|
}
|
|
});
|
|
|
|
|
|
let ContentSearchMediator = {
|
|
|
|
whitelist: new Set([
|
|
"about:home",
|
|
"about:newtab",
|
|
]),
|
|
|
|
init: function (chromeGlobal) {
|
|
chromeGlobal.addEventListener("ContentSearchClient", this, true, true);
|
|
addMessageListener("ContentSearch", this);
|
|
},
|
|
|
|
handleEvent: function (event) {
|
|
if (this._contentWhitelisted) {
|
|
this._sendMsg(event.detail.type, event.detail.data);
|
|
}
|
|
},
|
|
|
|
receiveMessage: function (msg) {
|
|
if (msg.data.type == "AddToWhitelist") {
|
|
for (let uri of msg.data.data) {
|
|
this.whitelist.add(uri);
|
|
}
|
|
this._sendMsg("AddToWhitelistAck");
|
|
return;
|
|
}
|
|
if (this._contentWhitelisted) {
|
|
this._fireEvent(msg.data.type, msg.data.data);
|
|
}
|
|
},
|
|
|
|
get _contentWhitelisted() {
|
|
return this.whitelist.has(content.document.documentURI);
|
|
},
|
|
|
|
_sendMsg: function (type, data=null) {
|
|
sendAsyncMessage("ContentSearch", {
|
|
type: type,
|
|
data: data,
|
|
});
|
|
},
|
|
|
|
_fireEvent: function (type, data=null) {
|
|
let event = Cu.cloneInto({
|
|
detail: {
|
|
type: type,
|
|
data: data,
|
|
},
|
|
}, content);
|
|
content.dispatchEvent(new content.CustomEvent("ContentSearchService",
|
|
event));
|
|
},
|
|
};
|
|
ContentSearchMediator.init(this);
|
|
|
|
// Lazily load the finder code
|
|
addMessageListener("Finder:Initialize", function () {
|
|
let {RemoteFinderListener} = Cu.import("resource://gre/modules/RemoteFinder.jsm", {});
|
|
new RemoteFinderListener(global);
|
|
});
|
|
|
|
|
|
let ClickEventHandler = {
|
|
init: function init() {
|
|
Cc["@mozilla.org/eventlistenerservice;1"]
|
|
.getService(Ci.nsIEventListenerService)
|
|
.addSystemEventListener(global, "click", this, true);
|
|
},
|
|
|
|
handleEvent: function(event) {
|
|
if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
|
|
return;
|
|
}
|
|
|
|
let originalTarget = event.originalTarget;
|
|
let ownerDoc = originalTarget.ownerDocument;
|
|
|
|
// Handle click events from about pages
|
|
if (ownerDoc.documentURI.startsWith("about:certerror")) {
|
|
this.onAboutCertError(originalTarget, ownerDoc);
|
|
return;
|
|
} else if (ownerDoc.documentURI.startsWith("about:blocked")) {
|
|
this.onAboutBlocked(originalTarget, ownerDoc);
|
|
return;
|
|
} else if (ownerDoc.documentURI.startsWith("about:neterror")) {
|
|
this.onAboutNetError(originalTarget, ownerDoc);
|
|
}
|
|
|
|
let [href, node] = this._hrefAndLinkNodeForClickEvent(event);
|
|
|
|
let json = { button: event.button, shiftKey: event.shiftKey,
|
|
ctrlKey: event.ctrlKey, metaKey: event.metaKey,
|
|
altKey: event.altKey, href: null, title: null,
|
|
bookmark: false };
|
|
|
|
if (href) {
|
|
json.href = href;
|
|
if (node) {
|
|
json.title = node.getAttribute("title");
|
|
if (event.button == 0 && !event.ctrlKey && !event.shiftKey &&
|
|
!event.altKey && !event.metaKey) {
|
|
json.bookmark = node.getAttribute("rel") == "sidebar";
|
|
if (json.bookmark) {
|
|
event.preventDefault(); // Need to prevent the pageload.
|
|
}
|
|
}
|
|
}
|
|
|
|
sendAsyncMessage("Content:Click", json);
|
|
return;
|
|
}
|
|
|
|
// This might be middle mouse navigation.
|
|
if (event.button == 1) {
|
|
sendAsyncMessage("Content:Click", json);
|
|
}
|
|
},
|
|
|
|
onAboutCertError: function (targetElement, ownerDoc) {
|
|
let docshell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.QueryInterface(Ci.nsIDocShell);
|
|
sendAsyncMessage("Browser:CertExceptionError", {
|
|
location: ownerDoc.location.href,
|
|
elementId: targetElement.getAttribute("id"),
|
|
isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
|
|
}, {
|
|
failedChannel: docshell.failedChannel
|
|
});
|
|
},
|
|
|
|
onAboutBlocked: function (targetElement, ownerDoc) {
|
|
sendAsyncMessage("Browser:SiteBlockedError", {
|
|
location: ownerDoc.location.href,
|
|
isMalware: /e=malwareBlocked/.test(ownerDoc.documentURI),
|
|
elementId: targetElement.getAttribute("id"),
|
|
isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView)
|
|
});
|
|
},
|
|
|
|
onAboutNetError: function (targetElement, ownerDoc) {
|
|
let elmId = targetElement.getAttribute("id");
|
|
if (elmId != "errorTryAgain" || !/e=netOffline/.test(ownerDoc.documentURI)) {
|
|
return;
|
|
}
|
|
sendSyncMessage("Browser:NetworkError", {});
|
|
},
|
|
|
|
/**
|
|
* Extracts linkNode and href for the current click target.
|
|
*
|
|
* @param event
|
|
* The click event.
|
|
* @return [href, linkNode].
|
|
*
|
|
* @note linkNode will be null if the click wasn't on an anchor
|
|
* element (or XLink).
|
|
*/
|
|
_hrefAndLinkNodeForClickEvent: function(event) {
|
|
function isHTMLLink(aNode) {
|
|
// Be consistent with what nsContextMenu.js does.
|
|
return ((aNode instanceof content.HTMLAnchorElement && aNode.href) ||
|
|
(aNode instanceof content.HTMLAreaElement && aNode.href) ||
|
|
aNode instanceof content.HTMLLinkElement);
|
|
}
|
|
|
|
let node = event.target;
|
|
while (node && !isHTMLLink(node)) {
|
|
node = node.parentNode;
|
|
}
|
|
|
|
if (node)
|
|
return [node.href, node];
|
|
|
|
// If there is no linkNode, try simple XLink.
|
|
let href, baseURI;
|
|
node = event.target;
|
|
while (node && !href) {
|
|
if (node.nodeType == content.Node.ELEMENT_NODE) {
|
|
href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
|
|
if (href)
|
|
baseURI = node.ownerDocument.baseURIObject;
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
|
|
// In case of XLink, we don't return the node we got href from since
|
|
// callers expect <a>-like elements.
|
|
// Note: makeURI() will throw if aUri is not a valid URI.
|
|
return [href ? BrowserUtils.makeURI(href, null, baseURI).spec : null, null];
|
|
}
|
|
};
|
|
ClickEventHandler.init();
|
|
|
|
ContentLinkHandler.init(this);
|
|
|
|
addEventListener("DOMWebNotificationClicked", function(event) {
|
|
sendAsyncMessage("DOMWebNotificationClicked", {});
|
|
}, false);
|
|
|
|
let PageStyleHandler = {
|
|
init: function() {
|
|
addMessageListener("PageStyle:Switch", this);
|
|
addMessageListener("PageStyle:Disable", this);
|
|
|
|
// Send a CPOW to the parent so that it can synchronously request
|
|
// the list of style sheets.
|
|
sendSyncMessage("PageStyle:SetSyncHandler", {}, {syncHandler: this});
|
|
},
|
|
|
|
get markupDocumentViewer() {
|
|
return docShell.contentViewer;
|
|
},
|
|
|
|
// Called synchronously via CPOW from the parent.
|
|
getStyleSheetInfo: function() {
|
|
let styleSheets = this._filterStyleSheets(this.getAllStyleSheets());
|
|
return {
|
|
styleSheets: styleSheets,
|
|
authorStyleDisabled: this.markupDocumentViewer.authorStyleDisabled,
|
|
preferredStyleSheetSet: content.document.preferredStyleSheetSet
|
|
};
|
|
},
|
|
|
|
// Called synchronously via CPOW from the parent.
|
|
getAllStyleSheets: function(frameset = content) {
|
|
let selfSheets = Array.slice(frameset.document.styleSheets);
|
|
let subSheets = Array.map(frameset.frames, frame => this.getAllStyleSheets(frame));
|
|
return selfSheets.concat(...subSheets);
|
|
},
|
|
|
|
receiveMessage: function(msg) {
|
|
switch (msg.name) {
|
|
case "PageStyle:Switch":
|
|
this.markupDocumentViewer.authorStyleDisabled = false;
|
|
this._stylesheetSwitchAll(content, msg.data.title);
|
|
break;
|
|
|
|
case "PageStyle:Disable":
|
|
this.markupDocumentViewer.authorStyleDisabled = true;
|
|
break;
|
|
}
|
|
},
|
|
|
|
_stylesheetSwitchAll: function (frameset, title) {
|
|
if (!title || this._stylesheetInFrame(frameset, title)) {
|
|
this._stylesheetSwitchFrame(frameset, title);
|
|
}
|
|
|
|
for (let i = 0; i < frameset.frames.length; i++) {
|
|
// Recurse into sub-frames.
|
|
this._stylesheetSwitchAll(frameset.frames[i], title);
|
|
}
|
|
},
|
|
|
|
_stylesheetSwitchFrame: function (frame, title) {
|
|
var docStyleSheets = frame.document.styleSheets;
|
|
|
|
for (let i = 0; i < docStyleSheets.length; ++i) {
|
|
let docStyleSheet = docStyleSheets[i];
|
|
if (docStyleSheet.title) {
|
|
docStyleSheet.disabled = (docStyleSheet.title != title);
|
|
} else if (docStyleSheet.disabled) {
|
|
docStyleSheet.disabled = false;
|
|
}
|
|
}
|
|
},
|
|
|
|
_stylesheetInFrame: function (frame, title) {
|
|
return Array.some(frame.document.styleSheets, (styleSheet) => styleSheet.title == title);
|
|
},
|
|
|
|
_filterStyleSheets: function(styleSheets) {
|
|
let result = [];
|
|
|
|
for (let currentStyleSheet of styleSheets) {
|
|
if (!currentStyleSheet.title)
|
|
continue;
|
|
|
|
// Skip any stylesheets that don't match the screen media type.
|
|
if (currentStyleSheet.media.length > 0) {
|
|
let mediaQueryList = currentStyleSheet.media.mediaText;
|
|
if (!content.matchMedia(mediaQueryList).matches) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
result.push({title: currentStyleSheet.title,
|
|
disabled: currentStyleSheet.disabled});
|
|
}
|
|
|
|
return result;
|
|
},
|
|
};
|
|
PageStyleHandler.init();
|
|
|
|
// Keep a reference to the translation content handler to avoid it it being GC'ed.
|
|
let trHandler = null;
|
|
if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) {
|
|
Cu.import("resource:///modules/translation/TranslationContentHandler.jsm");
|
|
trHandler = new TranslationContentHandler(global, docShell);
|
|
}
|
|
|
|
let DOMFullscreenHandler = {
|
|
_fullscreenDoc: null,
|
|
|
|
init: function() {
|
|
addMessageListener("DOMFullscreen:Approved", this);
|
|
addMessageListener("DOMFullscreen:CleanUp", this);
|
|
addEventListener("MozEnteredDomFullscreen", this);
|
|
},
|
|
|
|
receiveMessage: function(aMessage) {
|
|
switch(aMessage.name) {
|
|
case "DOMFullscreen:Approved": {
|
|
if (this._fullscreenDoc) {
|
|
Services.obs.notifyObservers(this._fullscreenDoc,
|
|
"fullscreen-approved",
|
|
"");
|
|
}
|
|
break;
|
|
}
|
|
case "DOMFullscreen:CleanUp": {
|
|
this._fullscreenDoc = null;
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
|
|
handleEvent: function(aEvent) {
|
|
if (aEvent.type == "MozEnteredDomFullscreen") {
|
|
this._fullscreenDoc = aEvent.target;
|
|
sendAsyncMessage("MozEnteredDomFullscreen", {
|
|
origin: this._fullscreenDoc.nodePrincipal.origin,
|
|
});
|
|
}
|
|
}
|
|
};
|
|
DOMFullscreenHandler.init();
|
|
|
|
function gKeywordURIFixup(fixupInfo) {
|
|
fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
|
|
|
|
// Ignore info from other docshells
|
|
let parent = fixupInfo.consumer.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeRootTreeItem;
|
|
if (parent != docShell)
|
|
return;
|
|
|
|
let data = {};
|
|
for (let f of Object.keys(fixupInfo)) {
|
|
if (f == "consumer" || typeof fixupInfo[f] == "function")
|
|
continue;
|
|
|
|
if (fixupInfo[f] && fixupInfo[f] instanceof Ci.nsIURI) {
|
|
data[f] = fixupInfo[f].spec;
|
|
} else {
|
|
data[f] = fixupInfo[f];
|
|
}
|
|
}
|
|
|
|
sendAsyncMessage("Browser:URIFixup", data);
|
|
}
|
|
Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup", false);
|
|
addEventListener("unload", () => {
|
|
Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
|
|
}, false);
|
|
|
|
addMessageListener("Browser:AppTab", function(message) {
|
|
docShell.isAppTab = message.data.isAppTab;
|
|
});
|
|
|
|
let WebBrowserChrome = {
|
|
onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
|
|
return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
|
|
},
|
|
};
|
|
|
|
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
|
let tabchild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsITabChild);
|
|
tabchild.webBrowserChrome = WebBrowserChrome;
|
|
}
|
|
|
|
addEventListener("pageshow", function(event) {
|
|
if (event.target == content.document) {
|
|
sendAsyncMessage("PageVisibility:Show", {
|
|
persisted: event.persisted,
|
|
});
|
|
}
|
|
});
|