2014-06-25 09:12:07 +04:00
|
|
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
2012-08-11 00:20:25 +04:00
|
|
|
/* 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/. */
|
2011-07-13 23:17:31 +04:00
|
|
|
|
2015-04-13 23:23:51 +03:00
|
|
|
/* This content script should work in any browser or iframe and should not
|
|
|
|
* depend on the frame being contained in tabbrowser. */
|
|
|
|
|
2014-05-15 16:56:37 +04:00
|
|
|
let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
2011-07-13 23:17:31 +04:00
|
|
|
|
2013-06-20 03:30:53 +04:00
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
2014-03-14 22:45:53 +04:00
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
2014-09-22 22:39:57 +04:00
|
|
|
Cu.import("resource:///modules/ContentWebRTC.jsm");
|
2015-02-04 20:13:38 +03:00
|
|
|
Cu.import("resource:///modules/ContentObservers.jsm");
|
2014-10-03 18:52:37 +04:00
|
|
|
Cu.import("resource://gre/modules/InlineSpellChecker.jsm");
|
|
|
|
Cu.import("resource://gre/modules/InlineSpellCheckerContent.jsm");
|
2013-06-20 03:30:53 +04:00
|
|
|
|
2014-08-21 19:42:42 +04:00
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
|
|
|
|
"resource://gre/modules/BrowserUtils.jsm");
|
2014-02-09 05:41:34 +04:00
|
|
|
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");
|
2014-09-18 00:06:58 +04:00
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PluginContent",
|
|
|
|
"resource:///modules/PluginContent.jsm");
|
2013-07-06 00:55:00 +04:00
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
|
|
|
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
2014-08-21 19:42:42 +04:00
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "FormSubmitObserver",
|
|
|
|
"resource:///modules/FormSubmitObserver.jsm");
|
2015-03-06 01:43:05 +03:00
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",
|
|
|
|
"resource://gre/modules/PageMetadata.jsm");
|
2014-12-16 19:21:11 +03:00
|
|
|
XPCOMUtils.defineLazyGetter(this, "PageMenuChild", function() {
|
|
|
|
let tmp = {};
|
|
|
|
Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
|
|
|
|
return new tmp.PageMenuChild();
|
|
|
|
});
|
2013-06-20 03:30:53 +04:00
|
|
|
|
2014-08-21 19:42:42 +04:00
|
|
|
// TabChildGlobal
|
|
|
|
var global = this;
|
|
|
|
|
|
|
|
// Load the form validation popup handler
|
|
|
|
var formSubmitObserver = new FormSubmitObserver(content, this);
|
2013-09-30 11:57:26 +04:00
|
|
|
|
2014-12-16 19:21:11 +03:00
|
|
|
addMessageListener("ContextMenu:DoCustomCommand", function(message) {
|
|
|
|
PageMenuChild.executeMenu(message.data);
|
|
|
|
});
|
|
|
|
|
2014-06-28 22:09:45 +04:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
|
2014-11-17 21:58:13 +03:00
|
|
|
let handleContentContextMenu = 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;
|
2014-08-26 22:44:10 +04:00
|
|
|
}
|
|
|
|
|
2014-11-17 21:58:13 +03:00
|
|
|
defaultPrevented = false;
|
2014-11-11 23:52:13 +03:00
|
|
|
}
|
2013-07-06 00:55:00 +04:00
|
|
|
|
2014-11-17 21:58:13 +03:00
|
|
|
if (defaultPrevented)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let addonInfo = {};
|
|
|
|
let subject = {
|
|
|
|
event: event,
|
|
|
|
addonInfo: addonInfo,
|
|
|
|
};
|
|
|
|
subject.wrappedJSObject = subject;
|
|
|
|
Services.obs.notifyObservers(subject, "content-contextmenu", null);
|
|
|
|
|
2015-02-21 03:22:39 +03:00
|
|
|
let doc = event.target.ownerDocument;
|
|
|
|
let docLocation = doc.location.href;
|
|
|
|
let charSet = doc.characterSet;
|
|
|
|
let baseURI = doc.baseURI;
|
|
|
|
let referrer = doc.referrer;
|
2015-03-05 10:29:55 +03:00
|
|
|
let referrerPolicy = doc.referrerPolicy;
|
2015-04-01 20:38:24 +03:00
|
|
|
let frameOuterWindowID = doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIDOMWindowUtils)
|
|
|
|
.outerWindowID;
|
2015-02-21 03:22:39 +03:00
|
|
|
|
2015-03-31 20:59:06 +03:00
|
|
|
// Media related cache info parent needs for saving
|
|
|
|
let contentType = null;
|
|
|
|
let contentDisposition = null;
|
|
|
|
if (event.target.nodeType == Ci.nsIDOMNode.ELEMENT_NODE &&
|
|
|
|
event.target instanceof Ci.nsIImageLoadingContent &&
|
|
|
|
event.target.currentURI) {
|
|
|
|
try {
|
|
|
|
let imageCache = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
|
|
|
.getImgCacheForDocument(doc);
|
|
|
|
let props =
|
|
|
|
imageCache.findEntryProperties(event.target.currentURI);
|
|
|
|
if (props) {
|
|
|
|
contentType = props.get("type", Ci.nsISupportsCString).data;
|
|
|
|
contentDisposition = props.get("content-disposition", Ci.nsISupportsCString).data;
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
Cu.reportError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-17 21:58:13 +03:00
|
|
|
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
|
|
|
let editFlags = SpellCheckHelper.isEditable(event.target, content);
|
|
|
|
let spellInfo;
|
|
|
|
if (editFlags &
|
|
|
|
(SpellCheckHelper.EDITABLE | SpellCheckHelper.CONTENTEDITABLE)) {
|
|
|
|
spellInfo =
|
|
|
|
InlineSpellCheckerContent.initContextMenu(event, editFlags, this);
|
|
|
|
}
|
2014-11-12 01:27:17 +03:00
|
|
|
|
2014-12-16 19:21:11 +03:00
|
|
|
let customMenuItems = PageMenuChild.build(event.target);
|
2015-02-21 03:22:39 +03:00
|
|
|
let principal = doc.nodePrincipal;
|
2015-01-29 22:28:01 +03:00
|
|
|
sendSyncMessage("contextmenu",
|
2015-02-21 03:22:39 +03:00
|
|
|
{ editFlags, spellInfo, customMenuItems, addonInfo,
|
2015-03-05 10:29:55 +03:00
|
|
|
principal, docLocation, charSet, baseURI, referrer,
|
2015-04-01 20:38:24 +03:00
|
|
|
referrerPolicy, contentType, contentDisposition,
|
|
|
|
frameOuterWindowID },
|
2015-01-29 22:28:01 +03:00
|
|
|
{ event, popupNode: event.target });
|
2014-11-17 21:58:13 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Break out to the parent window and pass the add-on info along
|
|
|
|
let browser = docShell.chromeEventHandler;
|
|
|
|
let mainWin = browser.ownerDocument.defaultView;
|
|
|
|
mainWin.gContextMenuContentData = {
|
|
|
|
isRemote: false,
|
|
|
|
event: event,
|
|
|
|
popupNode: event.target,
|
|
|
|
browser: browser,
|
|
|
|
addonInfo: addonInfo,
|
2015-02-21 03:22:39 +03:00
|
|
|
documentURIObject: doc.documentURIObject,
|
|
|
|
docLocation: docLocation,
|
|
|
|
charSet: charSet,
|
|
|
|
referrer: referrer,
|
2015-03-05 10:29:55 +03:00
|
|
|
referrerPolicy: referrerPolicy,
|
2015-03-31 20:59:06 +03:00
|
|
|
contentType: contentType,
|
|
|
|
contentDisposition: contentDisposition,
|
2014-11-17 21:58:13 +03:00
|
|
|
};
|
|
|
|
}
|
2014-11-12 01:27:17 +03:00
|
|
|
}
|
2014-11-11 23:52:13 +03:00
|
|
|
|
2014-11-17 21:58:13 +03:00
|
|
|
Cc["@mozilla.org/eventlistenerservice;1"]
|
|
|
|
.getService(Ci.nsIEventListenerService)
|
|
|
|
.addSystemEventListener(global, "contextmenu", handleContentContextMenu, false);
|
|
|
|
|
2014-10-30 14:52:00 +03:00
|
|
|
let AboutNetErrorListener = {
|
|
|
|
init: function(chromeGlobal) {
|
|
|
|
chromeGlobal.addEventListener('AboutNetErrorLoad', this, false, true);
|
|
|
|
chromeGlobal.addEventListener('AboutNetErrorSetAutomatic', this, false, true);
|
|
|
|
chromeGlobal.addEventListener('AboutNetErrorSendReport', this, false, true);
|
|
|
|
},
|
|
|
|
|
|
|
|
get isAboutNetError() {
|
|
|
|
return content.document.documentURI.startsWith("about:neterror");
|
|
|
|
},
|
|
|
|
|
|
|
|
handleEvent: function(aEvent) {
|
|
|
|
if (!this.isAboutNetError) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (aEvent.type) {
|
|
|
|
case "AboutNetErrorLoad":
|
|
|
|
this.onPageLoad(aEvent);
|
|
|
|
break;
|
|
|
|
case "AboutNetErrorSetAutomatic":
|
|
|
|
this.onSetAutomatic(aEvent);
|
|
|
|
break;
|
|
|
|
case "AboutNetErrorSendReport":
|
|
|
|
this.onSendReport(aEvent);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onPageLoad: function(evt) {
|
|
|
|
let automatic = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic");
|
|
|
|
content.dispatchEvent(new content.CustomEvent("AboutNetErrorOptions", {
|
|
|
|
detail: JSON.stringify({
|
|
|
|
enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"),
|
|
|
|
automatic: automatic
|
|
|
|
})
|
|
|
|
}
|
|
|
|
));
|
|
|
|
if (automatic) {
|
|
|
|
this.onSendReport(evt);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onSetAutomatic: function(evt) {
|
|
|
|
sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
|
|
|
|
automatic: evt.detail
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
onSendReport: function(evt) {
|
|
|
|
let contentDoc = content.document;
|
|
|
|
|
|
|
|
let reportSendingMsg = contentDoc.getElementById("reportSendingMessage");
|
|
|
|
let reportSentMsg = contentDoc.getElementById("reportSentMessage");
|
|
|
|
let reportBtn = contentDoc.getElementById("reportCertificateError");
|
|
|
|
let retryBtn = contentDoc.getElementById("reportCertificateErrorRetry");
|
|
|
|
|
|
|
|
addMessageListener("Browser:SSLErrorReportStatus", function(message) {
|
|
|
|
// show and hide bits - but only if this is a message for the right
|
|
|
|
// document - we'll compare on document URI
|
|
|
|
if (contentDoc.documentURI === message.data.documentURI) {
|
|
|
|
switch(message.data.reportStatus) {
|
|
|
|
case "activity":
|
|
|
|
// Hide the button that was just clicked
|
|
|
|
reportBtn.style.display = "none";
|
|
|
|
retryBtn.style.display = "none";
|
|
|
|
reportSentMsg.style.display = "none";
|
|
|
|
reportSendingMsg.style.display = "inline";
|
|
|
|
break;
|
|
|
|
case "error":
|
|
|
|
// show the retry button
|
|
|
|
retryBtn.style.display = "inline";
|
|
|
|
reportSendingMsg.style.display = "none";
|
|
|
|
break;
|
|
|
|
case "complete":
|
|
|
|
// Show a success indicator
|
|
|
|
reportSentMsg.style.display = "inline";
|
|
|
|
reportSendingMsg.style.display = "none";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
let failedChannel = docShell.failedChannel;
|
|
|
|
let location = contentDoc.location.href;
|
|
|
|
|
|
|
|
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
|
|
|
|
.getService(Ci.nsISerializationHelper);
|
|
|
|
|
|
|
|
let serializable = docShell.failedChannel.securityInfo
|
|
|
|
.QueryInterface(Ci.nsITransportSecurityInfo)
|
|
|
|
.QueryInterface(Ci.nsISerializable);
|
|
|
|
|
|
|
|
let serializedSecurityInfo = serhelper.serializeToString(serializable);
|
|
|
|
|
|
|
|
sendAsyncMessage("Browser:SendSSLErrorReport", {
|
|
|
|
elementId: evt.target.id,
|
|
|
|
documentURI: contentDoc.documentURI,
|
|
|
|
location: contentDoc.location,
|
|
|
|
securityInfo: serializedSecurityInfo
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
AboutNetErrorListener.init(this);
|
|
|
|
|
2014-08-01 09:42:00 +04:00
|
|
|
// 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.");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2013-08-09 04:16:47 +04:00
|
|
|
let ClickEventHandler = {
|
|
|
|
init: function init() {
|
|
|
|
Cc["@mozilla.org/eventlistenerservice;1"]
|
|
|
|
.getService(Ci.nsIEventListenerService)
|
|
|
|
.addSystemEventListener(global, "click", this, true);
|
|
|
|
},
|
|
|
|
|
|
|
|
handleEvent: function(event) {
|
2014-07-02 23:27:35 +04:00
|
|
|
if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
|
2013-08-09 04:16:47 +04:00
|
|
|
return;
|
2014-07-02 23:27:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
let originalTarget = event.originalTarget;
|
|
|
|
let ownerDoc = originalTarget.ownerDocument;
|
2014-09-09 00:36:22 +04:00
|
|
|
if (!ownerDoc) {
|
|
|
|
return;
|
|
|
|
}
|
2014-07-02 23:27:35 +04:00
|
|
|
|
|
|
|
// 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")) {
|
2015-01-14 13:46:55 +03:00
|
|
|
this.onAboutNetError(event, ownerDoc.documentURI);
|
|
|
|
return;
|
2014-07-02 23:27:35 +04:00
|
|
|
}
|
2013-08-09 04:16:47 +04:00
|
|
|
|
|
|
|
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,
|
2015-03-05 10:29:55 +03:00
|
|
|
bookmark: false, referrerPolicy: ownerDoc.referrerPolicy };
|
2013-08-09 04:16:47 +04:00
|
|
|
|
|
|
|
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";
|
2014-07-02 23:27:35 +04:00
|
|
|
if (json.bookmark) {
|
2013-08-09 04:16:47 +04:00
|
|
|
event.preventDefault(); // Need to prevent the pageload.
|
2014-07-02 23:27:35 +04:00
|
|
|
}
|
2013-08-09 04:16:47 +04:00
|
|
|
}
|
|
|
|
}
|
2015-02-18 00:17:06 +03:00
|
|
|
json.noReferrer = BrowserUtils.linkHasNoReferrer(node)
|
2013-08-09 04:16:47 +04:00
|
|
|
|
|
|
|
sendAsyncMessage("Content:Click", json);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This might be middle mouse navigation.
|
2014-07-02 23:27:35 +04:00
|
|
|
if (event.button == 1) {
|
2013-08-09 04:16:47 +04:00
|
|
|
sendAsyncMessage("Content:Click", json);
|
2014-07-02 23:27:35 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onAboutCertError: function (targetElement, ownerDoc) {
|
2014-07-08 02:33:07 +04:00
|
|
|
let docshell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
|
|
.QueryInterface(Ci.nsIDocShell);
|
2014-10-02 08:17:13 +04:00
|
|
|
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
|
|
|
|
.getService(Ci.nsISerializationHelper);
|
|
|
|
let serializedSSLStatus = "";
|
|
|
|
|
|
|
|
try {
|
|
|
|
let serializable = docShell.failedChannel.securityInfo
|
|
|
|
.QueryInterface(Ci.nsISSLStatusProvider)
|
|
|
|
.SSLStatus
|
|
|
|
.QueryInterface(Ci.nsISerializable);
|
|
|
|
serializedSSLStatus = serhelper.serializeToString(serializable);
|
|
|
|
} catch (e) { }
|
|
|
|
|
2014-07-02 23:27:35 +04:00
|
|
|
sendAsyncMessage("Browser:CertExceptionError", {
|
|
|
|
location: ownerDoc.location.href,
|
|
|
|
elementId: targetElement.getAttribute("id"),
|
2014-07-08 02:33:07 +04:00
|
|
|
isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
|
2014-10-02 08:17:13 +04:00
|
|
|
sslStatusAsString: serializedSSLStatus
|
2014-07-02 23:27:35 +04:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
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)
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2015-01-14 13:46:55 +03:00
|
|
|
onAboutNetError: function (event, documentURI) {
|
|
|
|
let elmId = event.originalTarget.getAttribute("id");
|
|
|
|
if (elmId != "errorTryAgain" || !/e=netOffline/.test(documentURI)) {
|
2014-07-02 23:27:35 +04:00
|
|
|
return;
|
|
|
|
}
|
2015-01-14 13:46:55 +03:00
|
|
|
// browser front end will handle clearing offline mode and refreshing
|
|
|
|
// the page *if* we're in offline mode now. Otherwise let the error page
|
|
|
|
// handle the click.
|
|
|
|
if (Services.io.offline) {
|
|
|
|
event.preventDefault();
|
|
|
|
sendAsyncMessage("Browser:EnableOnlineMode", {});
|
|
|
|
}
|
2013-08-09 04:16:47 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
2013-09-30 11:57:26 +04:00
|
|
|
baseURI = node.ownerDocument.baseURIObject;
|
2013-08-09 04:16:47 +04:00
|
|
|
}
|
|
|
|
node = node.parentNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
// In case of XLink, we don't return the node we got href from since
|
|
|
|
// callers expect <a>-like elements.
|
2013-09-30 11:57:26 +04:00
|
|
|
// Note: makeURI() will throw if aUri is not a valid URI.
|
2014-08-21 19:42:42 +04:00
|
|
|
return [href ? BrowserUtils.makeURI(href, null, baseURI).spec : null, null];
|
2013-08-09 04:16:47 +04:00
|
|
|
}
|
|
|
|
};
|
2013-08-30 20:20:22 +04:00
|
|
|
ClickEventHandler.init();
|
2014-01-13 16:56:28 +04:00
|
|
|
|
2014-02-09 05:41:34 +04:00
|
|
|
ContentLinkHandler.init(this);
|
|
|
|
|
2014-09-18 00:06:58 +04:00
|
|
|
// TODO: Load this lazily so the JSM is run only if a relevant event/message fires.
|
|
|
|
let pluginContent = new PluginContent(global);
|
|
|
|
|
2014-01-13 16:56:28 +04:00
|
|
|
addEventListener("DOMWebNotificationClicked", function(event) {
|
|
|
|
sendAsyncMessage("DOMWebNotificationClicked", {});
|
|
|
|
}, false);
|
2014-03-12 07:13:45 +04:00
|
|
|
|
2014-09-22 22:39:57 +04:00
|
|
|
ContentWebRTC.init();
|
2014-09-22 22:39:58 +04:00
|
|
|
addMessageListener("webrtc:Allow", ContentWebRTC);
|
|
|
|
addMessageListener("webrtc:Deny", ContentWebRTC);
|
2014-09-22 22:39:57 +04:00
|
|
|
addMessageListener("webrtc:StopSharing", ContentWebRTC);
|
2015-02-24 19:16:27 +03:00
|
|
|
addMessageListener("webrtc:StartBrowserSharing", () => {
|
|
|
|
let windowID = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
|
|
|
|
sendAsyncMessage("webrtc:response:StartBrowserSharing", {
|
|
|
|
windowID: windowID
|
|
|
|
});
|
|
|
|
});
|
2014-09-22 22:39:57 +04:00
|
|
|
|
2014-09-03 07:09:24 +04:00
|
|
|
addEventListener("pageshow", function(event) {
|
|
|
|
if (event.target == content.document) {
|
|
|
|
sendAsyncMessage("PageVisibility:Show", {
|
|
|
|
persisted: event.persisted,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2014-10-30 22:21:47 +03:00
|
|
|
|
2015-03-06 01:43:05 +03:00
|
|
|
let PageMetadataMessenger = {
|
2015-03-17 22:49:07 +03:00
|
|
|
init() {
|
2015-03-06 01:43:05 +03:00
|
|
|
addMessageListener("PageMetadata:GetPageData", this);
|
|
|
|
addMessageListener("PageMetadata:GetMicrodata", this);
|
2014-11-06 04:26:36 +03:00
|
|
|
},
|
2015-03-17 22:49:07 +03:00
|
|
|
receiveMessage(message) {
|
|
|
|
switch(message.name) {
|
2015-03-06 01:43:05 +03:00
|
|
|
case "PageMetadata:GetPageData": {
|
|
|
|
let result = PageMetadata.getData(content.document);
|
|
|
|
sendAsyncMessage("PageMetadata:PageDataResult", result);
|
2014-11-06 04:26:36 +03:00
|
|
|
break;
|
2015-03-06 01:43:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
case "PageMetadata:GetMicrodata": {
|
2015-03-12 00:19:30 +03:00
|
|
|
let target = message.objects.target;
|
2015-03-06 01:43:05 +03:00
|
|
|
let result = PageMetadata.getMicrodata(content.document, target);
|
|
|
|
sendAsyncMessage("PageMetadata:MicrodataResult", result);
|
2014-11-06 04:26:36 +03:00
|
|
|
break;
|
2015-03-06 01:43:05 +03:00
|
|
|
}
|
2014-11-06 04:26:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-03-06 01:43:05 +03:00
|
|
|
PageMetadataMessenger.init();
|
2014-11-06 04:26:36 +03:00
|
|
|
|
2014-11-06 04:22:27 +03:00
|
|
|
addEventListener("ActivateSocialFeature", function (aEvent) {
|
|
|
|
let document = content.document;
|
|
|
|
if (PrivateBrowsingUtils.isContentWindowPrivate(content)) {
|
|
|
|
Cu.reportError("cannot use social providers in private windows");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let dwu = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIDOMWindowUtils);
|
|
|
|
if (!dwu.isHandlingUserInput) {
|
|
|
|
Cu.reportError("attempt to activate provider without user input from " + document.nodePrincipal.origin);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let node = aEvent.target;
|
|
|
|
let ownerDocument = node.ownerDocument;
|
|
|
|
let data = node.getAttribute("data-service");
|
|
|
|
if (data) {
|
|
|
|
try {
|
|
|
|
data = JSON.parse(data);
|
|
|
|
} catch(e) {
|
|
|
|
Cu.reportError("Social Service manifest parse error: " + e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Cu.reportError("Social Service manifest not available");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sendAsyncMessage("Social:Activation", {
|
|
|
|
url: ownerDocument.location.href,
|
|
|
|
origin: ownerDocument.nodePrincipal.origin,
|
|
|
|
manifest: data
|
|
|
|
});
|
|
|
|
}, true, true);
|
|
|
|
|
2014-10-30 22:21:47 +03:00
|
|
|
addMessageListener("ContextMenu:SaveVideoFrameAsImage", (message) => {
|
|
|
|
let video = message.objects.target;
|
|
|
|
let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
|
|
|
canvas.width = video.videoWidth;
|
|
|
|
canvas.height = video.videoHeight;
|
|
|
|
|
|
|
|
let ctxDraw = canvas.getContext("2d");
|
|
|
|
ctxDraw.drawImage(video, 0, 0);
|
|
|
|
sendAsyncMessage("ContextMenu:SaveVideoFrameAsImage:Result", {
|
|
|
|
dataURL: canvas.toDataURL("image/jpeg", ""),
|
|
|
|
});
|
|
|
|
});
|
2015-02-19 09:37:00 +03:00
|
|
|
|
|
|
|
addMessageListener("ContextMenu:MediaCommand", (message) => {
|
|
|
|
let media = message.objects.element;
|
|
|
|
|
|
|
|
switch (message.data.command) {
|
|
|
|
case "play":
|
|
|
|
media.play();
|
|
|
|
break;
|
|
|
|
case "pause":
|
|
|
|
media.pause();
|
|
|
|
break;
|
|
|
|
case "mute":
|
|
|
|
media.muted = true;
|
|
|
|
break;
|
|
|
|
case "unmute":
|
|
|
|
media.muted = false;
|
|
|
|
break;
|
|
|
|
case "playbackRate":
|
|
|
|
media.playbackRate = message.data.data;
|
|
|
|
break;
|
|
|
|
case "hidecontrols":
|
|
|
|
media.removeAttribute("controls");
|
|
|
|
break;
|
|
|
|
case "showcontrols":
|
|
|
|
media.setAttribute("controls", "true");
|
|
|
|
break;
|
|
|
|
case "hidestats":
|
|
|
|
case "showstats":
|
|
|
|
let event = media.ownerDocument.createEvent("CustomEvent");
|
|
|
|
event.initCustomEvent("media-showStatistics", false, true,
|
|
|
|
message.data.command == "showstats");
|
|
|
|
media.dispatchEvent(event);
|
|
|
|
break;
|
2015-02-19 09:39:00 +03:00
|
|
|
case "fullscreen":
|
|
|
|
if (content.document.mozFullScreenEnabled)
|
|
|
|
media.mozRequestFullScreen();
|
|
|
|
break;
|
2015-02-19 09:37:00 +03:00
|
|
|
}
|
|
|
|
});
|
2015-03-05 00:27:35 +03:00
|
|
|
|
|
|
|
addMessageListener("ContextMenu:Canvas:ToDataURL", (message) => {
|
|
|
|
let dataURL = message.objects.target.toDataURL();
|
|
|
|
sendAsyncMessage("ContextMenu:Canvas:ToDataURL:Result", { dataURL });
|
|
|
|
});
|
2015-03-21 03:13:57 +03:00
|
|
|
|
|
|
|
addMessageListener("ContextMenu:ReloadFrame", (message) => {
|
|
|
|
message.objects.target.ownerDocument.location.reload();
|
|
|
|
});
|