diff --git a/toolkit/library/libxul-config.mk b/toolkit/library/libxul-config.mk index f7bb2324a47c..4ee3d8de31ea 100644 --- a/toolkit/library/libxul-config.mk +++ b/toolkit/library/libxul-config.mk @@ -149,7 +149,6 @@ COMPONENT_LIBS += \ nsappshell \ txmgr \ commandlines \ - extensions \ toolkitcomps \ pipboot \ pipnss \ diff --git a/toolkit/library/nsStaticXULComponents.cpp b/toolkit/library/nsStaticXULComponents.cpp index 9ea99194b315..9abab7aa9d19 100644 --- a/toolkit/library/nsStaticXULComponents.cpp +++ b/toolkit/library/nsStaticXULComponents.cpp @@ -265,7 +265,6 @@ STORAGE_MODULE \ PLACES_MODULES \ XULENABLED_MODULES \ - MODULE(AddonsModule) \ MODULE(nsToolkitCompsModule) \ XREMOTE_MODULES \ JSDEBUGGER_MODULES \ diff --git a/toolkit/mozapps/extensions/Makefile.in b/toolkit/mozapps/extensions/Makefile.in index ec8728beed10..28d67f0eaf84 100644 --- a/toolkit/mozapps/extensions/Makefile.in +++ b/toolkit/mozapps/extensions/Makefile.in @@ -41,14 +41,8 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -MODULE = extensions -LIBRARY_NAME = extensions -SHORT_LIBNAME = extnsion -IS_COMPONENT = 1 -MODULE_NAME = AddonsModule -GRE_MODULE = 1 -LIBXUL_LIBRARY = 1 -EXPORT_LIBRARY = 1 +MODULE = extensions +XPIDL_MODULE = extensions XPIDLSRCS = \ amIInstallTrigger.idl \ @@ -56,10 +50,6 @@ XPIDLSRCS = \ amIWebInstaller.idl \ $(NULL) -CPPSRCS = \ - amInstallTrigger.cpp \ - $(NULL) - EXTRA_PP_COMPONENTS = \ extensions.manifest \ nsBlocklistService.js \ diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm index 24db8d37efc2..0652d72a6183 100644 --- a/toolkit/mozapps/extensions/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/XPIProvider.jsm @@ -3836,6 +3836,49 @@ var XPIDatabase = { } }; +/** + * Handles callbacks for HTTP channels of XPI downloads. We support + * prompting for auth dialogs and, optionally, to ignore bad certs. + * + * @param aWindow + * An optional DOM Element related to the request + * @param aNeedBadCertHandling + * Whether we should handle bad certs or not + */ +function XPINotificationCallbacks(aWindow, aNeedBadCertHandling) { + this.window = aWindow; + + // Verify that we don't end up on an insecure channel if we haven't got a + // hash to verify with (see bug 537761 for discussion) + this.needBadCertHandling = aNeedBadCertHandling; + + if (this.needBadCertHandling) { + Components.utils.import("resource://gre/modules/CertUtils.jsm"); + this.badCertHandler = new BadCertHandler(); + } +} + +XPINotificationCallbacks.prototype = { + QueryInterface: function(iid) { + if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIInterfaceRequestor)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + getInterface: function(iid) { + if (iid.equals(Components.interfaces.nsIAuthPrompt2)) { + var factory = Cc["@mozilla.org/prompter;1"]. + getService(Ci.nsIPromptFactory); + return factory.getPrompt(this.window, Ci.nsIAuthPrompt); + } + + if (this.needBadCertHandling) + return this.badCertHandler.getInterface(iid); + + throw Components.results.NS_ERROR_NO_INTERFACE; + }, +}; + /** * Instantiates an AddonInstall and passes the new object to a callback when * it is complete. @@ -4307,8 +4350,6 @@ AddonInstall.prototype = { * Starts downloading the add-on's XPI file. */ startDownload: function AI_startDownload() { - Components.utils.import("resource://gre/modules/CertUtils.jsm"); - this.state = AddonManager.STATE_DOWNLOADING; if (!AddonManagerPrivate.callInstallListeners("onDownloadStarted", this.listeners, this.wrapper)) { @@ -4366,13 +4407,10 @@ AddonInstall.prototype = { listener.init(this, this.stream); try { this.channel = NetUtil.newChannel(this.sourceURI); - if (this.loadGroup) - this.channel.loadGroup = this.loadGroup; - - // Verify that we don't end up on an insecure channel if we haven't got a - // hash to verify with (see bug 537761 for discussion) - if (!this.hash) - this.channel.notificationCallbacks = new BadCertHandler(); + this.channel.notificationCallbacks = + new XPINotificationCallbacks(this.window, !this.hash); + this.channel.QueryInterface(Ci.nsIHttpChannelInternal) + .forceAllowThirdPartyCookie = true; this.channel.asyncOpen(listener, null); Services.obs.addObserver(this, "network:offline-about-to-go-offline", false); @@ -4408,11 +4446,6 @@ AddonInstall.prototype = { * @see nsIStreamListener */ onStartRequest: function AI_onStartRequest(aRequest, aContext) { - // We must remove the request from the load group otherwise if the user - // closes the page that triggered it the download will be cancelled - if (this.loadGroup) - this.loadGroup.removeRequest(aRequest, null, Cr.NS_BINDING_RETARGETED); - this.progress = 0; if (aRequest instanceof Ci.nsIChannel) { try { diff --git a/toolkit/mozapps/extensions/addonManager.js b/toolkit/mozapps/extensions/addonManager.js index 0bbb7b6513dd..4a7e11f68a23 100644 --- a/toolkit/mozapps/extensions/addonManager.js +++ b/toolkit/mozapps/extensions/addonManager.js @@ -57,12 +57,27 @@ const DOWNLOAD_ERROR = -228; const UNSUPPORTED_TYPE = -244; const SUCCESS = 0; +const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled"; +const MSG_INSTALL_ADDONS = "WebInstallerInstallAddonsFromWebpage"; +const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback"; + +const CHILD_SCRIPT = + "chrome://mozapps/content/extensions/extensions-content.js"; + Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +Components.utils.import("resource://gre/modules/Services.jsm"); var gSingleton = null; function amManager() { Components.utils.import("resource://gre/modules/AddonManager.jsm"); + + var messageManager = Cc["@mozilla.org/globalmessagemanager;1"]. + getService(Ci.nsIChromeFrameMessageManager); + + messageManager.addMessageListener(MSG_INSTALL_ENABLED, this); + messageManager.addMessageListener(MSG_INSTALL_ADDONS, this); + messageManager.loadFrameScript(CHILD_SCRIPT, true); } amManager.prototype = { @@ -167,6 +182,52 @@ amManager.prototype = { AddonManagerPrivate.backgroundUpdateCheck(); }, + /** + * messageManager callback function. + * + * Listens to requests from child processes for InstallTrigger + * activity, and sends back callbacks. + */ + receiveMessage: function(aMessage) { + var payload = aMessage.json; + var referer = Services.io.newURI(payload.referer, null, null); + switch (aMessage.name) { + case MSG_INSTALL_ENABLED: + return this.isInstallEnabled(payload.mimetype, referer); + + case MSG_INSTALL_ADDONS: + var callback = null; + if (payload.callbackId != -1) { + callback = { + onInstallEnded: function ITP_callback(url, status) { + // Doing it this way, instead of aMessage.target.messageManager, + // ensures it works in Firefox and not only Fennec. See bug + // 578172. TODO: Clean up this code once that bug is fixed + var flo = aMessage.target.QueryInterface(Ci.nsIFrameLoaderOwner); + var returnMessageManager = flo.frameLoader.messageManager; + returnMessageManager.sendAsyncMessage(MSG_INSTALL_CALLBACK, + { callbackId: payload.callbackId, url: url, status: status } + ); + }, + }; + } + var window; + try { + // Normal approach for single-process mode + window = aMessage.target.docShell + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow).content; + } catch (e) { + // Fallback for multiprocess (e10s) mode. Appears to work but has + // not had a full suite of automated tests run on it. + window = aMessage.target.ownerDocument.defaultView; + } + return this.installAddonsFromWebpage(payload.mimetype, + window, referer, payload.uris, payload.hashes, payload.names, + payload.icons, callback, payload.uris.length); + } + }, + classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"), _xpcom_factory: { createInstance: function(aOuter, aIid) { diff --git a/toolkit/mozapps/extensions/amInstallTrigger.cpp b/toolkit/mozapps/extensions/amInstallTrigger.cpp deleted file mode 100644 index 5e4208e31b6b..000000000000 --- a/toolkit/mozapps/extensions/amInstallTrigger.cpp +++ /dev/null @@ -1,380 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Extension Manager. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dave Townsend - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "amInstallTrigger.h" -#include "nsIClassInfoImpl.h" -#include "nsIComponentManager.h" -#include "nsICategoryManager.h" -#include "nsServiceManagerUtils.h" -#include "nsXPIDLString.h" -#include "nsIScriptNameSpaceManager.h" -#include "nsDOMJSUtils.h" -#include "nsIXPConnect.h" -#include "nsContentUtils.h" -#include "nsIDocument.h" -#include "nsIDOMDocument.h" -#include "nsNetUtil.h" -#include "nsIScriptSecurityManager.h" -#include "mozilla/ModuleUtils.h" - -// -// Helper function for URI verification -// -static nsresult -CheckLoadURIFromScript(JSContext *aCx, const nsACString& aUriStr) -{ - nsresult rv; - nsCOMPtr secman(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv)); - NS_ENSURE_SUCCESS(rv, rv); - - // get the script principal - nsCOMPtr principal; - rv = secman->GetSubjectPrincipal(getter_AddRefs(principal)); - NS_ENSURE_SUCCESS(rv, rv); - if (!principal) - return NS_ERROR_FAILURE; - - // convert the requested URL string to a URI - // Note that we use a null base URI here, since that's what we use when we - // actually convert the string into a URI to load. - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), aUriStr); - NS_ENSURE_SUCCESS(rv, rv); - - // are we allowed to load this one? - rv = secman->CheckLoadURIWithPrincipal(principal, uri, - nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL); - return rv; -} - -NS_IMPL_CLASSINFO(amInstallTrigger, NULL, nsIClassInfo::DOM_OBJECT, - AM_InstallTrigger_CID) -NS_IMPL_ISUPPORTS1_CI(amInstallTrigger, amIInstallTrigger) - -amInstallTrigger::amInstallTrigger() -{ - mManager = do_GetService("@mozilla.org/addons/integration;1"); -} - -amInstallTrigger::~amInstallTrigger() -{ -} - -JSContext* -amInstallTrigger::GetJSContext() -{ - nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID()); - - // get the xpconnect native call context - nsAXPCNativeCallContext *cc = nsnull; - xpc->GetCurrentNativeCallContext(&cc); - if (!cc) - return nsnull; - - // Get JSContext of current call - JSContext* cx; - nsresult rv = cc->GetJSContext(&cx); - if (NS_FAILED(rv)) - return nsnull; - - return cx; -} - -already_AddRefed -amInstallTrigger::GetOriginatingWindow(JSContext* aCx) -{ - nsIScriptGlobalObject *globalObject = nsnull; - nsIScriptContext *scriptContext = GetScriptContextFromJSContext(aCx); - if (!scriptContext) - return nsnull; - - globalObject = scriptContext->GetGlobalObject(); - if (!globalObject) - return nsnull; - - nsCOMPtr window = do_QueryInterface(globalObject); - return window.forget(); -} - -already_AddRefed -amInstallTrigger::GetOriginatingURI(nsIDOMWindowInternal* aWindow) -{ - if (!aWindow) - return nsnull; - - nsCOMPtr domdoc; - aWindow->GetDocument(getter_AddRefs(domdoc)); - nsCOMPtr doc(do_QueryInterface(domdoc)); - nsIURI* uri = doc->GetDocumentURI(); - NS_IF_ADDREF(uri); - return uri; -} - -/* boolean updateEnabled (); */ -NS_IMETHODIMP -amInstallTrigger::UpdateEnabled(PRBool *_retval NS_OUTPARAM) -{ - return Enabled(_retval); -} - -/* boolean enabled (); */ -NS_IMETHODIMP -amInstallTrigger::Enabled(PRBool *_retval NS_OUTPARAM) -{ - nsCOMPtr window = GetOriginatingWindow(GetJSContext()); - nsCOMPtr referer = GetOriginatingURI(window); - - return mManager->IsInstallEnabled(NS_LITERAL_STRING("application/x-xpinstall"), referer, _retval); -} - -/* boolean install (in nsIVariant args, [optional] in amIInstallCallback callback); */ -NS_IMETHODIMP -amInstallTrigger::Install(nsIVariant *aArgs, - amIInstallCallback *aCallback, - PRBool *_retval NS_OUTPARAM) -{ - JSContext *cx = GetJSContext(); - nsCOMPtr window = GetOriginatingWindow(cx); - nsCOMPtr referer = GetOriginatingURI(window); - - jsval params; - nsresult rv = aArgs->GetAsJSVal(¶ms); - NS_ENSURE_SUCCESS(rv, rv); - - if (!JSVAL_IS_OBJECT(params) || !JSVAL_TO_OBJECT(params)) - return NS_ERROR_INVALID_ARG; - - JSIdArray *ida = JS_Enumerate(cx, JSVAL_TO_OBJECT(params)); - if (!ida) - return NS_ERROR_FAILURE; - - PRUint32 count = ida->length; - - nsTArray names; - nsTArray uris; - nsTArray icons; - nsTArray hashes; - - jsval v; - for (PRUint32 i = 0; i < count; i++ ) { - JS_IdToValue(cx, ida->vector[i], &v); - JSString *str = JS_ValueToString(cx, v); - if (!str) { - JS_DestroyIdArray(cx, ida); - return NS_ERROR_FAILURE; - } - - const PRUnichar* name = reinterpret_cast(JS_GetStringChars(str)); - const PRUnichar* uri = nsnull; - const PRUnichar* icon = nsnull; - const PRUnichar* hash = nsnull; - - JS_GetUCProperty(cx, JSVAL_TO_OBJECT(params), reinterpret_cast(name), nsCRT::strlen(name), &v); - if (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v)) { - jsval v2; - if (JS_GetProperty(cx, JSVAL_TO_OBJECT(v), "URL", &v2) && !JSVAL_IS_VOID(v2)) { - JSString *str = JS_ValueToString(cx, v2); - if (!str) { - JS_DestroyIdArray(cx, ida); - return NS_ERROR_FAILURE; - } - uri = reinterpret_cast(JS_GetStringChars(str)); - } - - if (JS_GetProperty(cx, JSVAL_TO_OBJECT(v), "IconURL", &v2) && !JSVAL_IS_VOID(v2)) { - JSString *str = JS_ValueToString(cx, v2); - if (!str) { - JS_DestroyIdArray(cx, ida); - return NS_ERROR_FAILURE; - } - icon = reinterpret_cast(JS_GetStringChars(str)); - } - - if (JS_GetProperty(cx, JSVAL_TO_OBJECT(v), "Hash", &v2) && !JSVAL_IS_VOID(v2)) { - JSString *str = JS_ValueToString(cx, v2); - if (!str) { - JS_DestroyIdArray(cx, ida); - return NS_ERROR_FAILURE; - } - hash = reinterpret_cast(JS_GetStringChars(str)); - } - } - else { - uri = reinterpret_cast(JS_GetStringChars(JS_ValueToString(cx, v))); - } - - if (!uri) { - JS_DestroyIdArray(cx, ida); - return NS_ERROR_FAILURE; - } - - nsCString tmpURI = NS_ConvertUTF16toUTF8(uri); - // Get relative URL to load - if (referer) { - rv = referer->Resolve(tmpURI, tmpURI); - if (NS_FAILED(rv)) { - JS_DestroyIdArray(cx, ida); - return rv; - } - } - - rv = CheckLoadURIFromScript(cx, tmpURI); - if (NS_FAILED(rv)) { - JS_DestroyIdArray(cx, ida); - return rv; - } - uri = UTF8ToNewUnicode(tmpURI); - - if (icon) { - nsCString tmpIcon = NS_ConvertUTF16toUTF8(icon); - if (referer) { - rv = referer->Resolve(tmpIcon, tmpIcon); - if (NS_FAILED(rv)) { - JS_DestroyIdArray(cx, ida); - return rv; - } - } - - // If the page can't load the icon then just ignore it - rv = CheckLoadURIFromScript(cx, tmpIcon); - if (NS_FAILED(rv)) - icon = nsnull; - else - icon = UTF8ToNewUnicode(tmpIcon); - } - - names.AppendElement(name); - uris.AppendElement(uri); - icons.AppendElement(icon); - hashes.AppendElement(hash); - } - - JS_DestroyIdArray(cx, ida); - - rv = mManager->InstallAddonsFromWebpage(NS_LITERAL_STRING("application/x-xpinstall"), - window, referer, uris.Elements(), - hashes.Elements(), names.Elements(), - icons.Elements(), aCallback, count, - _retval); - - for (PRUint32 i = 0; i < uris.Length(); i++) { - NS_Free(const_cast(uris[i])); - if (icons[i]) - NS_Free(const_cast(icons[i])); - } - - return rv; -} - -/* boolean installChrome (in PRUint32 type, in AString url, in AString skin); */ -NS_IMETHODIMP -amInstallTrigger::InstallChrome(PRUint32 aType, - const nsAString & aUrl, - const nsAString & aSkin, - PRBool *_retval NS_OUTPARAM) -{ - return StartSoftwareUpdate(aUrl, 0, _retval); -} - -/* boolean startSoftwareUpdate (in AString url, [optional] in PRInt32 flags); */ -NS_IMETHODIMP -amInstallTrigger::StartSoftwareUpdate(const nsAString & aUrl, - PRInt32 aFlags, - PRBool *_retval NS_OUTPARAM) -{ - nsresult rv; - - JSContext *cx = GetJSContext(); - nsCOMPtr window = GetOriginatingWindow(cx); - nsCOMPtr referer = GetOriginatingURI(window); - - nsTArray names; - nsTArray uris; - nsTArray icons; - nsTArray hashes; - - nsCString tmpURI = NS_ConvertUTF16toUTF8(aUrl); - // Get relative URL to load - if (referer) { - rv = referer->Resolve(tmpURI, tmpURI); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = CheckLoadURIFromScript(cx, tmpURI); - NS_ENSURE_SUCCESS(rv, rv); - - names.AppendElement((PRUnichar*)nsnull); - uris.AppendElement(UTF8ToNewUnicode(tmpURI)); - icons.AppendElement((PRUnichar*)nsnull); - hashes.AppendElement((PRUnichar*)nsnull); - - rv = mManager->InstallAddonsFromWebpage(NS_LITERAL_STRING("application/x-xpinstall"), - window, referer, uris.Elements(), - hashes.Elements(), names.Elements(), - icons.Elements(), nsnull, 1, _retval); - - NS_Free(const_cast(uris[0])); - return rv; -} - -NS_GENERIC_FACTORY_CONSTRUCTOR(amInstallTrigger) - -NS_DEFINE_NAMED_CID(AM_InstallTrigger_CID); - -static const mozilla::Module::CIDEntry kInstallTriggerCIDs[] = { - { &kAM_InstallTrigger_CID, false, NULL, amInstallTriggerConstructor }, - { NULL } -}; - -static const mozilla::Module::ContractIDEntry kInstallTriggerContracts[] = { - { AM_INSTALLTRIGGER_CONTRACTID, &kAM_InstallTrigger_CID }, - { NULL } -}; - -static const mozilla::Module::CategoryEntry kInstallTriggerCategories[] = { - { JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY, "InstallTrigger", AM_INSTALLTRIGGER_CONTRACTID }, - { NULL } -}; - -static const mozilla::Module kInstallTriggerModule = { - mozilla::Module::kVersion, - kInstallTriggerCIDs, - kInstallTriggerContracts, - kInstallTriggerCategories -}; - -NSMODULE_DEFN(AddonsModule) = &kInstallTriggerModule; diff --git a/toolkit/mozapps/extensions/amInstallTrigger.h b/toolkit/mozapps/extensions/amInstallTrigger.h deleted file mode 100644 index 15be01c751d8..000000000000 --- a/toolkit/mozapps/extensions/amInstallTrigger.h +++ /dev/null @@ -1,65 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Extension Manager. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dave Townsend - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "jscntxt.h" -#include "amIInstallTrigger.h" -#include "nsIDOMWindowInternal.h" -#include "nsIURI.h" -#include "amIWebInstaller.h" -#include "nsCOMPtr.h" - -#define AM_InstallTrigger_CID \ - {0xfcfcdf1e, 0xe9ef, 0x4141, {0x90, 0xd8, 0xd5, 0xff, 0x84, 0xc1, 0x7c, 0xce}} -#define AM_INSTALLTRIGGER_CONTRACTID "@mozilla.org/addons/installtrigger;1" - -class amInstallTrigger : public amIInstallTrigger -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_AMIINSTALLTRIGGER - - amInstallTrigger(); - -private: - ~amInstallTrigger(); - - JSContext* GetJSContext(); - already_AddRefed GetOriginatingWindow(JSContext* aCx); - already_AddRefed GetOriginatingURI(nsIDOMWindowInternal* aWindow); - - nsCOMPtr mManager; -}; diff --git a/toolkit/mozapps/extensions/content/extensions-content.js b/toolkit/mozapps/extensions/content/extensions-content.js new file mode 100644 index 000000000000..191d1bffc6e3 --- /dev/null +++ b/toolkit/mozapps/extensions/content/extensions-content.js @@ -0,0 +1,293 @@ +/* +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Extension Manager. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2010 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Alon Zakai +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** +*/ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled"; +const MSG_INSTALL_ADDONS = "WebInstallerInstallAddonsFromWebpage"; +const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback"; + +Cu.import("resource://gre/modules/Services.jsm"); + +/** + * Child part of InstallTrigger e10s handling. + * + * Sets up InstallTriggers on newly-created windows, + * that will relay messages for InstallTrigger + * activity. We also process the parameters for + * the InstallTrigger to proper parameters for + * amIWebInstaller. + */ +function InstallTriggerChild() { + addEventListener("DOMWindowCreated", this, false); +} + +var that = this; + +InstallTriggerChild.prototype = { + handleEvent: function handleEvent(aEvent) { + var window = aEvent.originalTarget.defaultView.content; + + // Need to make sure we are called on what we care about - + // content windows. DOMWindowCreated is called on *all* HTMLDocuments, + // some of which belong to ChromeWindows or lack defaultView.content + // altogether. + // + // Note about the syntax used here: |"wrappedJSObject" in window| + // will silently fail, without even letting us catch it as an + // exception, and checking in the way that we do check in some + // cases still throws an exception; see bug 582108 about both. + try { + if (!window || !window.wrappedJSObject) { + return; + } + } + catch(e) { + return; + } + + // This event happens for each HTMLDocument, so it can happen more than + // once per Window. We only need to work once per Window though. + if (window.wrappedJSObject.InstallTrigger) + return; + + // The public object which web scripts can see (limited by + // the __exposedProps__ defined below) + window.wrappedJSObject.InstallTrigger = { + __exposedProps__: { + SKIN: "r", + LOCALE: "r", + CONTENT: "r", + PACKAGE: "r", + enabled: "r", + updateEnabled: "r", + install: "r", + installChrome: "r", + startSoftwareUpdate: "r", + }, + + // == Public interface == + + SKIN: Ci.amIInstallTrigger.SKIN, + LOCALE: Ci.amIInstallTrigger.LOCALE, + CONTENT: Ci.amIInstallTrigger.CONTENT, + PACKAGE: Ci.amIInstallTrigger.PACKAGE, + + /** + * @see amIInstallTriggerInstaller.idl + */ + enabled: function() { + return sendSyncMessage(MSG_INSTALL_ENABLED, { + mimetype: "application/x-xpinstall", referer: window.location.href + })[0]; + }, + + /** + * @see amIInstallTriggerInstaller.idl + */ + updateEnabled: function() { + return this.enabled(); + }, + + /** + * @see amIInstallTriggerInstaller.idl + */ + install: function(aArgs, aCallback) { + var params = { + mimetype: "application/x-xpinstall", + referer: window.location.href, + uris: [], + hashes: [], + names: [], + icons: [], + }; + + for (var name in aArgs) { + var item = aArgs[name]; + if (typeof item === 'string') { + item = { URL: item }; + } else if (!("URL" in item)) { + throw new Error("Missing URL property for '" + name + "'"); + } + + // Resolve and validate urls + var url = this.resolveURL(item.URL); + if (!this.checkLoadURIFromScript(url)) + throw new Error("insufficient permissions to install: " + url); + + var iconUrl = null; + if ("IconURL" in item) { + iconUrl = this.resolveURL(item.IconURL); + if (!this.checkLoadURIFromScript(iconUrl)) { + iconUrl = null; // If page can't load the icon, just ignore it + } + } + params.uris.push(url.spec); + params.hashes.push("Hash" in item ? item.Hash : null); + params.names.push(name); + params.icons.push(iconUrl ? iconUrl.spec : null); + } + // Add callback Id, done here, so only if we actually got here + params.callbackId = this.addCallback(aCallback, params.uris); + // Send message + return sendSyncMessage(MSG_INSTALL_ADDONS, params)[0]; + }, + + /** + * @see amIInstallTriggerInstaller.idl + */ + startSoftwareUpdate: function(aUrl, aFlags) { + var url = Services.io.newURI(aUrl, null, null) + .QueryInterface(Ci.nsIURL).filename; + var object = {}; + object[url] = { "URL": aUrl }; + return this.install(object); + }, + + /** + * @see amIInstallTriggerInstaller.idl + */ + installChrome: function(aType, aUrl, aSkin) { + return this.startSoftwareUpdate(aUrl); + }, + + // == Internal, hidden machinery == + + callbacks: {}, + + /** + * Adds a callback to the list of callbacks we may receive messages + * about from the parent process. We save them here; only callback IDs + * are sent over IPC. + * + * @param callback + * The callback function + * @param urls + * The urls this callback function will receive responses for. + * After all the callbacks have arrived, we can forget about the + * callback. + * + * @return The callback ID, an integer identifying this callback. + */ + addCallback: function(aCallback, aUrls) { + if (!aCallback) + return -1; + var callbackId = 0; + while (callbackId in this.callbacks) + callbackId++; + this.callbacks[callbackId] = { + callback: aCallback, + urls: aUrls.slice(0), // Clone the urls for our own use (it lets + // us know when no further callbacks will + // occur) + }; + return callbackId; + }, + + /** + * Receives a message about a callback. Performs the actual callback + * (for the callback with the ID we are given). When + * all URLs are exhausted, can free the callbackId and linked stuff. + * + * @param message + * The IPC message. Contains callbackId, the ID of the callback. + * + */ + receiveMessage: function(aMessage) { + var payload = aMessage.json; + var callbackId = payload.callbackId; + var url = payload.url; + var status = payload.status; + var callbackObj = this.callbacks[callbackId]; + if (!callbackObj) + return; + try { + callbackObj.callback(url, status); + } + catch (e) { + dump("InstallTrigger callback threw an exception: " + e + "\n"); + } + callbackObj.urls.splice(callbackObj.urls.indexOf(url), 1); + if (callbackObj.urls.length == 0) + this.callbacks[callbackId] = null; + }, + + /** + * Resolves a URL in the context of our current window. We need to do + * this before sending URLs to the parent process. + * + * @param aUrl + * The url to resolve. + * + * @return A resolved, absolute nsURI object. + */ + resolveURL: function(aUrl) { + return Services.io.newURI(aUrl, null, + window.document.documentURIObject); + }, + + /** + * @see amInstallTrigger.cpp + * TODO: When e10s lands on m-c, consider removing amInstallTrigger.cpp + * See bug 571166 + */ + checkLoadURIFromScript: function(aUri) { + var secman = Cc["@mozilla.org/scriptsecuritymanager;1"]. + getService(Ci.nsIScriptSecurityManager); + var principal = window.content.document.nodePrincipal; + try { + secman.checkLoadURIWithPrincipal(principal, aUri, + Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL); + return true; + } + catch(e) { + return false; + } + }, + }; + + addMessageListener(MSG_INSTALL_CALLBACK, + window.wrappedJSObject.InstallTrigger); + }, +}; + +new InstallTriggerChild(); + diff --git a/toolkit/mozapps/extensions/jar.mn b/toolkit/mozapps/extensions/jar.mn index 0c6df427cf21..b9cb6f1cb246 100644 --- a/toolkit/mozapps/extensions/jar.mn +++ b/toolkit/mozapps/extensions/jar.mn @@ -5,6 +5,7 @@ toolkit.jar: content/mozapps/extensions/extensions.js (content/extensions.js) content/mozapps/extensions/extensions.xml (content/extensions.xml) content/mozapps/extensions/updateinfo.xsl (content/updateinfo.xsl) + content/mozapps/extensions/extensions-content.js (content/extensions-content.js) * content/mozapps/extensions/about.xul (content/about.xul) * content/mozapps/extensions/about.js (content/about.js) * content/mozapps/extensions/list.xul (content/list.xul)