From d4db556e392337549f9f321f1b736981c90a87ef Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Tue, 14 Jul 2015 20:55:55 +0300 Subject: [PATCH 01/67] Bug 1183450, properly wrappercache URL object, r=baku --HG-- extra : rebase_source : 74e2c25872a89718c88cbb4e8b58a2a2e8d0f2c2 --- dom/base/URL.cpp | 13 ++----------- dom/base/URL.h | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/dom/base/URL.cpp b/dom/base/URL.cpp index 03141b95011a..44c6ac7f396b 100644 --- a/dom/base/URL.cpp +++ b/dom/base/URL.cpp @@ -23,22 +23,13 @@ namespace mozilla { namespace dom { -NS_IMPL_CYCLE_COLLECTION_CLASS(URL) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(URL) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mSearchParams) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(URL) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearchParams) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URL, mParent, mSearchParams) NS_IMPL_CYCLE_COLLECTING_ADDREF(URL) NS_IMPL_CYCLE_COLLECTING_RELEASE(URL) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END diff --git a/dom/base/URL.h b/dom/base/URL.h index 667018bdc5c3..7c8494dbbc55 100644 --- a/dom/base/URL.h +++ b/dom/base/URL.h @@ -39,7 +39,7 @@ class URL final : public URLSearchParamsObserver public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS(URL) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(URL) URL(nsISupports* aParent, already_AddRefed aURI); From 0b129137a729e28d273360a6ec394e9881804f5c Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Thu, 25 Jun 2015 18:50:24 -0700 Subject: [PATCH 02/67] Bug 1114554 - Patch 1 - Notification ServiceWorker API stubs. r=wchen,baku --HG-- extra : rebase_source : e033641ae9d271d1681c424f533db63671b3e735 --- dom/base/nsGkAtomList.h | 1 + dom/bindings/Bindings.conf | 8 +++++ dom/notification/Notification.cpp | 5 +-- dom/webidl/Notification.webidl | 6 +++- dom/webidl/ServiceWorker.webidl | 2 +- dom/workers/ServiceWorkerRegistration.cpp | 38 +++++++++++++++++++++-- dom/workers/ServiceWorkerRegistration.h | 21 +++++++++++++ dom/workers/WorkerScope.h | 1 + 8 files changed, 74 insertions(+), 8 deletions(-) diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index f1fe11578fb3..5169077128dd 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -823,6 +823,7 @@ GK_ATOM(onMozScrolledAreaChanged, "onMozScrolledAreaChanged") GK_ATOM(onmoznetworkupload, "onmoznetworkupload") GK_ATOM(onmoznetworkdownload, "onmoznetworkdownload") GK_ATOM(onnewrdsgroup, "onnewrdsgroup") +GK_ATOM(onnotificationclick, "onnotificationclick") GK_ATOM(onnoupdate, "onnoupdate") GK_ATOM(onobsolete, "onobsolete") GK_ATOM(ononline, "ononline") diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index e3a4b208c3ca..fc0bfc34bde6 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -869,6 +869,14 @@ DOMInterfaces = { 'nativeType': 'nsINodeList', }, +'NotificationEvent': { + 'headerFile': 'mozilla/dom/NotificationEvent.h', + 'nativeType': 'mozilla::dom::workers::NotificationEvent', + 'binaryNames': { + 'notification': 'notification_' + } +}, + 'OfflineAudioContext': { 'nativeType': 'mozilla::dom::AudioContext', }, diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 12cff00dc524..96ddb1ea1903 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -1414,10 +1414,7 @@ Notification::Get(const GlobalObject& aGlobal, } nsCOMPtr callback = new NotificationStorageCallback(aGlobal, window, promise); - nsString tag = aFilter.mTag.WasPassed() ? - aFilter.mTag.Value() : - EmptyString(); - aRv = notificationStorage->Get(origin, tag, callback); + aRv = notificationStorage->Get(origin, aFilter.mTag, callback); if (aRv.Failed()) { return nullptr; } diff --git a/dom/webidl/Notification.webidl b/dom/webidl/Notification.webidl index a402b43c9dee..7f2aeb827f14 100644 --- a/dom/webidl/Notification.webidl +++ b/dom/webidl/Notification.webidl @@ -68,7 +68,7 @@ dictionary NotificationOptions { }; dictionary GetNotificationOptions { - DOMString tag; + DOMString tag = ""; }; dictionary NotificationBehavior { @@ -93,3 +93,7 @@ enum NotificationDirection { "rtl" }; +partial interface ServiceWorkerRegistration { + Promise showNotification(DOMString title, optional NotificationOptions options); + Promise> getNotifications(optional GetNotificationOptions filter); +}; diff --git a/dom/webidl/ServiceWorker.webidl b/dom/webidl/ServiceWorker.webidl index 3b16bc73f739..8c3749e940b5 100644 --- a/dom/webidl/ServiceWorker.webidl +++ b/dom/webidl/ServiceWorker.webidl @@ -12,7 +12,7 @@ // https://github.com/slightlyoff/ServiceWorker/issues/189 [Func="mozilla::dom::workers::ServiceWorkerVisible", // FIXME(nsm): Bug 1113522. This is exposed to satisfy webidl constraints, but it won't actually work. - Exposed=(Worker,Window)] + Exposed=(Window,Worker)] interface ServiceWorker : EventTarget { readonly attribute USVString scriptURL; readonly attribute ServiceWorkerState state; diff --git a/dom/workers/ServiceWorkerRegistration.cpp b/dom/workers/ServiceWorkerRegistration.cpp index c084895cc85f..0cd114804490 100644 --- a/dom/workers/ServiceWorkerRegistration.cpp +++ b/dom/workers/ServiceWorkerRegistration.cpp @@ -595,6 +595,23 @@ ServiceWorkerRegistrationMainThread::Unregister(ErrorResult& aRv) return promise.forget(); } +// Notification API extension. +already_AddRefed +ServiceWorkerRegistrationMainThread::ShowNotification(JSContext* aCx, + const nsAString& aTitle, + const NotificationOptions& aOptions) +{ + MOZ_ASSERT(false); + return nullptr; +} + +already_AddRefed +ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions) +{ + MOZ_ASSERT(false); + return nullptr; +} + already_AddRefed ServiceWorkerRegistrationMainThread::GetPushManager(ErrorResult& aRv) { @@ -1001,5 +1018,22 @@ WorkerListener::UpdateFound() } } -} // namespace dom -} // namespace mozilla +// Notification API extension. +already_AddRefed +ServiceWorkerRegistrationWorkerThread::ShowNotification(JSContext* aCx, + const nsAString& aTitle, + const NotificationOptions& aOptions) +{ + MOZ_ASSERT(false); + return nullptr; +} + +already_AddRefed +ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOptions& aOptions) +{ + MOZ_ASSERT(false); + return nullptr; +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/workers/ServiceWorkerRegistration.h b/dom/workers/ServiceWorkerRegistration.h index 3b38771ff97f..1124bd16a5b1 100644 --- a/dom/workers/ServiceWorkerRegistration.h +++ b/dom/workers/ServiceWorkerRegistration.h @@ -12,6 +12,9 @@ #include "mozilla/dom/ServiceWorkerCommon.h" #include "mozilla/dom/workers/bindings/WorkerFeature.h" +// Support for Notification API extension. +#include "mozilla/dom/NotificationBinding.h" + class nsPIDOMWindow; namespace mozilla { @@ -111,6 +114,15 @@ public: JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + // Partial interface from Notification API. + already_AddRefed + ShowNotification(JSContext* aCx, + const nsAString& aTitle, + const NotificationOptions& aOptions); + + already_AddRefed + GetNotifications(const GetNotificationOptions& aOptions); + already_AddRefed GetInstalling() override; @@ -190,6 +202,15 @@ public: JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + // Partial interface from Notification API. + already_AddRefed + ShowNotification(JSContext* aCx, + const nsAString& aTitle, + const NotificationOptions& aOptions); + + already_AddRefed + GetNotifications(const GetNotificationOptions& aOptions); + already_AddRefed GetInstalling() override; diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index f75e1c793024..d00b92c6ab77 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -208,6 +208,7 @@ public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) + IMPL_EVENT_HANDLER(notificationclick) ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsACString& aScope); From b3f34da652963c4809e71082add6b9790242ac04 Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Thu, 25 Jun 2015 18:50:24 -0700 Subject: [PATCH 03/67] Bug 1114554 - Patch 2 - ServiceWorkerRegistration.showNotification(). r=wchen,baku Refactor creation and show dispatch so Notification constructor and showNotification can use it. Move persistence to ShowInternal. NotificationStorage calls callback async even when fetching from cache, simply to have similar semantics. Calls to Notification::Get() are performed async since persistence is now async after being moved to ShowInternal(). Both are in accordance with the spec where the "append to list of notifications" operation is performed in the "show steps" which are performed in parallel from API invocations. --HG-- extra : rebase_source : 52d3864fb39aa892d2f70dc2b71f09fb0d2ba533 --- dom/bindings/Errors.msg | 3 + dom/notification/Notification.cpp | 415 +++++++++++++----- dom/notification/Notification.h | 39 +- dom/notification/NotificationStorage.js | 26 +- dom/webidl/Notification.webidl | 2 + dom/workers/ServiceWorkerRegistration.cpp | 56 ++- dom/workers/ServiceWorkerRegistration.h | 10 +- dom/workers/test/serviceworkers/mochitest.ini | 2 + .../notification_constructor_error.js | 1 + .../test_notification_constructor_error.html | 52 +++ 10 files changed, 466 insertions(+), 140 deletions(-) create mode 100644 dom/workers/test/serviceworkers/notification_constructor_error.js create mode 100644 dom/workers/test/serviceworkers/test_notification_constructor_error.html diff --git a/dom/bindings/Errors.msg b/dom/bindings/Errors.msg index f368163eeb70..8e3698a0c80e 100644 --- a/dom/bindings/Errors.msg +++ b/dom/bindings/Errors.msg @@ -73,3 +73,6 @@ MSG_DEF(MSG_INVALID_URL_SCHEME, 2, JSEXN_TYPEERR, "{0} URL {1} must be either ht MSG_DEF(MSG_RESPONSE_URL_IS_NULL, 0, JSEXN_TYPEERR, "Cannot set Response.finalURL when Response.url is null.") MSG_DEF(MSG_RESPONSE_HAS_VARY_STAR, 0, JSEXN_TYPEERR, "Invalid Response object with a 'Vary: *' header.") MSG_DEF(MSG_BAD_FORMDATA, 0, JSEXN_TYPEERR, "Could not parse content as FormData.") +MSG_DEF(MSG_NO_ACTIVE_WORKER, 1, JSEXN_TYPEERR, "No active worker for scope {0}.") +MSG_DEF(MSG_NOTIFICATION_PERMISSION_DENIED, 0, JSEXN_TYPEERR, "Permission to show Notification denied.") +MSG_DEF(MSG_NOTIFICATION_NO_CONSTRUCTOR_IN_SERVICEWORKER, 0, JSEXN_TYPEERR, "Notification constructor cannot be used in ServiceWorkerGlobalScope. Use registration.showNotification() instead.") diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 96ddb1ea1903..d52bb4dc815b 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -11,6 +11,7 @@ #include "mozilla/dom/Promise.h" #include "mozilla/Move.h" #include "mozilla/Preferences.h" +#include "mozilla/unused.h" #include "nsContentUtils.h" #include "nsIAlertsService.h" #include "nsIAppsService.h" @@ -29,7 +30,7 @@ #include "nsIScriptSecurityManager.h" #include "nsIXPConnect.h" #include "mozilla/dom/PermissionMessageUtils.h" -#include "mozilla/dom/Event.h" +#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h" #include "mozilla/Services.h" #include "nsContentPermissionHelper.h" #include "nsILoadContext.h" @@ -37,6 +38,7 @@ #include "nsIDOMDesktopNotification.h" #endif +#include "ServiceWorkerManager.h" #include "WorkerPrivate.h" #include "WorkerRunnable.h" @@ -87,10 +89,10 @@ public: options.mMozbehavior.Init(aBehavior); nsRefPtr notification; nsCOMPtr global = do_QueryInterface(mWindow); - notification = Notification::CreateInternal(global, - aID, + notification = Notification::CreateInternal(aID, aTitle, options); + notification->BindToOwner(mWindow); ErrorResult rv; notification->InitFromBase64(aCx, aData, rv); if (rv.Failed()) { @@ -169,6 +171,35 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationStorageCallback) tmp->DropData(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END +class NotificationGetRunnable final : public nsRunnable +{ + const nsString mOrigin; + const nsString mTag; + nsCOMPtr mCallback; +public: + NotificationGetRunnable(const nsAString& aOrigin, + const nsAString& aTag, + nsINotificationStorageCallback* aCallback) + : mOrigin(aOrigin), mTag(aTag), mCallback(aCallback) + {} + + NS_IMETHOD + Run() override + { + nsresult rv; + nsCOMPtr notificationStorage = + do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = notificationStorage->Get(mOrigin, mTag, mCallback); + //XXXnsm Is it guaranteed mCallback will be called in case of failure? + unused << NS_WARN_IF(NS_FAILED(rv)); + return rv; + } +}; + class NotificationPermissionRequest : public nsIContentPermissionRequest, public nsIRunnable { @@ -275,7 +306,23 @@ public: return NS_OK; } }; -} // namespace + +nsresult +CheckScope(nsIPrincipal* aPrincipal, const nsACString& aScope) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aPrincipal); + + nsCOMPtr scopeURI; + nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return aPrincipal->CheckMayLoad(scopeURI, /* report = */ true, + /* allowIfInheritsPrincipal = */ false); +} +} // anonymous namespace // Subclass that can be directly dispatched to child workers from the main // thread. @@ -417,7 +464,8 @@ public: }; NotificationTask(UniquePtr aRef, NotificationAction aAction) - : mNotificationRef(Move(aRef)), mAction(aAction) {} + : mNotificationRef(Move(aRef)), mAction(aAction) + {} NS_IMETHOD Run() override; @@ -624,28 +672,26 @@ Notification::IsGetEnabled(JSContext* aCx, JSObject* aObj) Notification::Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody, NotificationDirection aDir, const nsAString& aLang, const nsAString& aTag, const nsAString& aIconUrl, - const NotificationBehavior& aBehavior, nsIGlobalObject* aGlobal) + const NotificationBehavior& aBehavior) : DOMEventTargetHelper(), mWorkerPrivate(nullptr), mObserver(nullptr), mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang), mTag(aTag), mIconUrl(aIconUrl), mBehavior(aBehavior), mIsClosed(false), mIsStored(false), mTaskCount(0) { - nsAutoString alertName; - if (NS_IsMainThread()) { - nsCOMPtr window = do_QueryInterface(aGlobal); - MOZ_ASSERT(window); - BindToOwner(window); - - DebugOnly rv = GetOrigin(window, alertName); - MOZ_ASSERT(NS_SUCCEEDED(rv), "GetOrigin should not have failed"); - } else { + if (!NS_IsMainThread()) { mWorkerPrivate = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(mWorkerPrivate); - - DebugOnly rv = GetOriginWorker(alertName); - MOZ_ASSERT(NS_SUCCEEDED(rv), "GetOrigin should not have failed"); } +} + +void +Notification::SetAlertName() +{ + AssertIsOnMainThread(); + nsAutoString alertName; + DebugOnly rv = GetOrigin(GetPrincipal(), alertName); + MOZ_ASSERT(NS_SUCCEEDED(rv), "GetOrigin should not have failed"); // Get the notification name that is unique per origin + tag/ID. // The name of the alert is of the form origin#tag/ID. @@ -669,45 +715,27 @@ Notification::Constructor(const GlobalObject& aGlobal, const NotificationOptions& aOptions, ErrorResult& aRv) { + // FIXME(nsm): If the sticky flag is set, throw an error. + ServiceWorkerGlobalScope* scope = nullptr; + UNWRAP_WORKER_OBJECT(ServiceWorkerGlobalScope, aGlobal.Get(), scope); + if (scope) { + aRv.ThrowTypeError(MSG_NOTIFICATION_NO_CONSTRUCTOR_IN_SERVICEWORKER); + return nullptr; + } + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); - nsRefPtr notification = CreateInternal(global, - EmptyString(), - aTitle, - aOptions); - // Make a structured clone of the aOptions.mData object - JS::Rooted data(aGlobal.Context(), aOptions.mData); - notification->InitFromJSVal(aGlobal.Context(), data, aRv); - if (aRv.Failed()) { + nsRefPtr notification = + CreateAndShow(global, aTitle, aOptions, aRv); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - auto ref = MakeUnique(notification); - if (!ref->Initialized()) { - aRv.Throw(NS_ERROR_DOM_ABORT_ERR); - return nullptr; - } - - // Queue a task to show the notification. - nsCOMPtr showNotificationTask = - new NotificationTask(Move(ref), NotificationTask::eShow); - nsresult rv = NS_DispatchToMainThread(showNotificationTask); - if (NS_FAILED(rv)) { - notification->DispatchTrustedEvent(NS_LITERAL_STRING("error")); - } - - //XXXnsm The persistence service seems like a prime candidate of - //PBackground. - // On workers, persistence is done in the NotificationTask. - if (NS_IsMainThread()) { - nsresult rv = notification->PersistNotification(); - if (NS_FAILED(rv)) { - NS_WARNING("Could not persist main thread Notification"); - } - } - + // This is be ok since we are on the worker thread where this function will + // run to completion before the Notification has a chance to go away. return notification.forget(); } + nsresult Notification::PersistNotification() { @@ -720,17 +748,8 @@ Notification::PersistNotification() } nsString origin; - if (mWorkerPrivate) { - rv = GetOriginWorker(origin); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - } else { - nsCOMPtr window = do_QueryInterface(GetOwner()); - MOZ_ASSERT(window); - rv = GetOrigin(window, origin); - if (NS_FAILED(rv)) { - return rv; - } - } + rv = GetOrigin(GetPrincipal(), origin); + MOZ_ASSERT(NS_SUCCEEDED(rv)); nsString id; GetID(id); @@ -774,12 +793,12 @@ void Notification::UnpersistNotification() { AssertIsOnMainThread(); - if (mIsStored) { + if (IsStored()) { nsCOMPtr notificationStorage = do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID); if (notificationStorage) { nsString origin; - nsresult rv = GetOrigin(GetOwner(), origin); + nsresult rv = GetOrigin(GetPrincipal(), origin); if (NS_SUCCEEDED(rv)) { notificationStorage->Delete(origin, mID); } @@ -789,8 +808,7 @@ Notification::UnpersistNotification() } already_AddRefed -Notification::CreateInternal(nsIGlobalObject* aGlobal, - const nsAString& aID, +Notification::CreateInternal(const nsAString& aID, const nsAString& aTitle, const NotificationOptions& aOptions) { @@ -818,8 +836,7 @@ Notification::CreateInternal(nsIGlobalObject* aGlobal, aOptions.mLang, aOptions.mTag, aOptions.mIcon, - aOptions.mMozbehavior, - aGlobal); + aOptions.mMozbehavior); return notification.forget(); } @@ -1075,13 +1092,9 @@ Notification::ShowInternal() mozilla::Swap(ownership, mTempRef); MOZ_ASSERT(ownership->GetNotification() == this); - if (mWorkerPrivate) { - // Persist the notification now since we couldn't do it on the worker - // thread. - nsresult rv = PersistNotification(); - if (NS_FAILED(rv)) { - NS_WARNING("Could not persist worker Notification"); - } + nsresult rv = PersistNotification(); + if (NS_FAILED(rv)) { + NS_WARNING("Could not persist Notification"); } nsCOMPtr alertService = @@ -1095,8 +1108,6 @@ Notification::ShowInternal() permission = GetPermissionInternal(GetOwner(), result); } if (permission != NotificationPermission::Granted || !alertService) { - // We do not have permission to show a notification or alert service - // is not available. if (mWorkerPrivate) { nsRefPtr r = new NotificationEventWorkerRunnable(this, @@ -1155,7 +1166,7 @@ Notification::ShowInternal() AppNotificationServiceOptions ops; ops.mTextClickable = true; ops.mManifestURL = manifestUrl; - ops.mId = mAlertName; + GetAlertName(ops.mId); ops.mDbId = mID; ops.mDir = DirectionToString(mDir); ops.mLang = mLang; @@ -1197,8 +1208,11 @@ Notification::ShowInternal() getter_AddRefs(loadContext)); inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing(); } + + nsAutoString alertName; + GetAlertName(alertName); alertService->ShowAlertNotification(iconUrl, mTitle, mBody, true, - uniqueCookie, observer, mAlertName, + uniqueCookie, observer, alertName, DirectionToString(mDir), mLang, dataStr, GetPrincipal(), inPrivateBrowsing); @@ -1238,11 +1252,20 @@ Notification::RequestPermission(const GlobalObject& aGlobal, NS_DispatchToMainThread(request); } +// static NotificationPermission Notification::GetPermission(const GlobalObject& aGlobal, ErrorResult& aRv) +{ + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); + return GetPermission(global, aRv); +} + +// static +NotificationPermission +Notification::GetPermission(nsIGlobalObject* aGlobal, ErrorResult& aRv) { if (NS_IsMainThread()) { - return GetPermissionInternal(aGlobal.GetAsSupports(), aRv); + return GetPermissionInternal(aGlobal, aRv); } else { WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); @@ -1388,34 +1411,32 @@ Notification::Get(const GlobalObject& aGlobal, MOZ_ASSERT(global); nsCOMPtr window = do_QueryInterface(global); MOZ_ASSERT(window); - nsIDocument* doc = window->GetExtantDoc(); + + nsCOMPtr doc = window->GetExtantDoc(); if (!doc) { aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr; } nsString origin; - aRv = GetOrigin(window, origin); + aRv = GetOrigin(doc->NodePrincipal(), origin); if (aRv.Failed()) { return nullptr; } - nsresult rv; - nsCOMPtr notificationStorage = - do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return nullptr; - } - nsRefPtr promise = Promise::Create(global, aRv); if (aRv.Failed()) { return nullptr; } + nsCOMPtr callback = new NotificationStorageCallback(aGlobal, window, promise); - aRv = notificationStorage->Get(origin, aFilter.mTag, callback); - if (aRv.Failed()) { + + nsRefPtr r = + new NotificationGetRunnable(origin, aFilter.mTag, callback); + + aRv = NS_DispatchToMainThread(r); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -1462,30 +1483,25 @@ Notification::CloseInternal() nsCOMPtr alertService = do_GetService(NS_ALERTSERVICE_CONTRACTID); if (alertService) { - alertService->CloseAlert(mAlertName, GetPrincipal()); + nsAutoString alertName; + GetAlertName(alertName); + alertService->CloseAlert(alertName, GetPrincipal()); } } } nsresult -Notification::GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin) +Notification::GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin) { - if (!aWindow) { - return NS_ERROR_FAILURE; - } + MOZ_ASSERT(aPrincipal); + uint16_t appStatus = aPrincipal->GetAppStatus(); + uint32_t appId = aPrincipal->GetAppId(); + nsresult rv; - nsIDocument* doc = aWindow->GetExtantDoc(); - NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); - nsIPrincipal* principal = doc->NodePrincipal(); - NS_ENSURE_TRUE(principal, NS_ERROR_UNEXPECTED); - - uint16_t appStatus = principal->GetAppStatus(); - uint32_t appId = principal->GetAppId(); - if (appStatus == nsIPrincipal::APP_STATUS_NOT_INSTALLED || appId == nsIScriptSecurityManager::NO_APP_ID || appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) { - rv = nsContentUtils::GetUTFOrigin(principal, aOrigin); + rv = nsContentUtils::GetUTFOrigin(aPrincipal, aOrigin); NS_ENSURE_SUCCESS(rv, rv); } else { // If we are in "app code", use manifest URL as unique origin since @@ -1499,14 +1515,6 @@ Notification::GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin) return NS_OK; } -nsresult -Notification::GetOriginWorker(nsString& aOrigin) -{ - MOZ_ASSERT(mWorkerPrivate); - aOrigin = mWorkerPrivate->GetLocationInfo().mOrigin; - return NS_OK; -} - nsIStructuredCloneContainer* Notification::GetDataCloneContainer() { return mDataObjectContainer; @@ -1661,6 +1669,187 @@ Notification::UnregisterFeature() mFeature = nullptr; } +/* + * Checks: + * 1) Is aWorker allowed to show a notification for scope? + * 2) Is aWorker an active worker? + * + * If it is not an active worker, Result() will be NS_ERROR_NOT_AVAILABLE. + */ +class CheckLoadRunnable final : public WorkerMainThreadRunnable +{ + nsresult mRv; + nsCString mScope; + +public: + explicit CheckLoadRunnable(WorkerPrivate* aWorker, const nsACString& aScope) + : WorkerMainThreadRunnable(aWorker) + , mRv(NS_ERROR_DOM_SECURITY_ERR) + , mScope(aScope) + { } + + bool + MainThreadRun() override + { + nsIPrincipal* principal = mWorkerPrivate->GetPrincipal(); + mRv = CheckScope(principal, mScope); + + if (NS_FAILED(mRv)) { + return true; + } + + nsRefPtr swm = ServiceWorkerManager::GetInstance(); + nsRefPtr registration = + swm->GetRegistration(principal, mScope); + + // This is coming from a ServiceWorkerRegistrationWorkerThread. + MOZ_ASSERT(registration); + + if (!registration->mActiveWorker || + registration->mActiveWorker->ID() != mWorkerPrivate->ServiceWorkerID()) { + mRv = NS_ERROR_NOT_AVAILABLE; + } + + return true; + } + + nsresult + Result() + { + return mRv; + } + +}; + +/* static */ +already_AddRefed +Notification::ShowPersistentNotification(nsIGlobalObject *aGlobal, + const nsAString& aScope, + const nsAString& aTitle, + const NotificationOptions& aOptions, + ErrorResult& aRv) +{ + MOZ_ASSERT(aGlobal); + + // Validate scope. + // XXXnsm: This may be slow due to blocking the worker and waiting on the main + // thread. On calls from content, we can be sure the scope is valid since + // ServiceWorkerRegistrations have their scope set correctly. Can this be made + // debug only? The problem is that there would be different semantics in + // debug and non-debug builds in such a case. + if (NS_IsMainThread()) { + nsCOMPtr sop = do_QueryInterface(aGlobal); + if (NS_WARN_IF(!sop)) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + nsIPrincipal* principal = sop->GetPrincipal(); + if (NS_WARN_IF(!principal)) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + aRv = CheckScope(principal, NS_ConvertUTF16toUTF8(aScope)); + if (NS_WARN_IF(aRv.Failed())) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return nullptr; + } + } else { + WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(worker); + worker->AssertIsOnWorkerThread(); + nsRefPtr loadChecker = + new CheckLoadRunnable(worker, NS_ConvertUTF16toUTF8(aScope)); + if (!loadChecker->Dispatch(worker->GetJSContext())) { + aRv.Throw(NS_ERROR_DOM_ABORT_ERR); + return nullptr; + } + + if (NS_WARN_IF(NS_FAILED(loadChecker->Result()))) { + if (loadChecker->Result() == NS_ERROR_NOT_AVAILABLE) { + aRv.ThrowTypeError(MSG_NO_ACTIVE_WORKER); + } else { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + } + return nullptr; + } + } + + + nsRefPtr p = Promise::Create(aGlobal, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + // We check permission here rather than pass the Promise to NotificationTask + // which leads to uglier code. + NotificationPermission permission = GetPermission(aGlobal, aRv); + + // "If permission for notification’s origin is not "granted", reject promise with a TypeError exception, and terminate these substeps." + if (NS_WARN_IF(aRv.Failed()) || permission == NotificationPermission::Denied) { + ErrorResult result; + result.ThrowTypeError(MSG_NOTIFICATION_PERMISSION_DENIED); + p->MaybeReject(result); + return p.forget(); + } + + // "Otherwise, resolve promise with undefined." + // The Notification may still not be shown due to other errors, but the spec + // is not concerned with those. + p->MaybeResolve(JS::UndefinedHandleValue); + + nsRefPtr notification = + CreateAndShow(aGlobal, aTitle, aOptions, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return p.forget(); +} + +/* static */ already_AddRefed +Notification::CreateAndShow(nsIGlobalObject* aGlobal, + const nsAString& aTitle, + const NotificationOptions& aOptions, + ErrorResult& aRv) +{ + MOZ_ASSERT(aGlobal); + + AutoJSAPI jsapi; + jsapi.Init(aGlobal); + JSContext* cx = jsapi.cx(); + + nsRefPtr notification = CreateInternal(EmptyString(), + aTitle, + aOptions); + + notification->BindToOwner(aGlobal); + + // Make a structured clone of the aOptions.mData object + JS::Rooted data(cx, aOptions.mData); + notification->InitFromJSVal(cx, data, aRv); + if (aRv.Failed()) { + return nullptr; + } + + auto ref = MakeUnique(notification); + if (!ref->Initialized()) { + aRv.Throw(NS_ERROR_DOM_ABORT_ERR); + return nullptr; + } + + // Queue a task to show the notification. + nsCOMPtr showNotificationTask = + new NotificationTask(Move(ref), NotificationTask::eShow); + nsresult rv = NS_DispatchToMainThread(showNotificationTask); + if (NS_WARN_IF(NS_FAILED(rv))) { + notification->DispatchTrustedEvent(NS_LITERAL_STRING("error")); + } + + return notification.forget(); +} + } // namespace dom } // namespace mozilla diff --git a/dom/notification/Notification.h b/dom/notification/Notification.h index bacc2429c530..0cf7d649b469 100644 --- a/dom/notification/Notification.h +++ b/dom/notification/Notification.h @@ -193,6 +193,15 @@ public: const GetNotificationOptions& aFilter, ErrorResult& aRv); + // Notification implementation of + // ServiceWorkerRegistration.showNotification. + static already_AddRefed + ShowPersistentNotification(nsIGlobalObject* aGlobal, + const nsAString& aScope, + const nsAString& aTitle, + const NotificationOptions& aOptions, + ErrorResult& aRv); + void Close(); nsPIDOMWindow* GetParentObject() @@ -233,18 +242,22 @@ public: bool AddRefObject(); void ReleaseObject(); + static NotificationPermission GetPermission(nsIGlobalObject* aGlobal, + ErrorResult& aRv); + static NotificationPermission GetPermissionInternal(nsIPrincipal* aPrincipal, ErrorResult& rv); bool DispatchClickEvent(); protected: + // Callers MUST bind the Notification to the correct DOMEventTargetHelper parent using + // BindToOwner(). Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody, NotificationDirection aDir, const nsAString& aLang, const nsAString& aTag, const nsAString& aIconUrl, - const NotificationBehavior& aBehavior, nsIGlobalObject* aGlobal); + const NotificationBehavior& aBehavior); - static already_AddRefed CreateInternal(nsIGlobalObject* aGlobal, - const nsAString& aID, + static already_AddRefed CreateInternal(const nsAString& aID, const nsAString& aTitle, const NotificationOptions& aOptions); @@ -277,11 +290,14 @@ protected: return NotificationDirection::Auto; } - static nsresult GetOrigin(nsPIDOMWindow* aWindow, nsString& aOrigin); - nsresult GetOriginWorker(nsString& aOrigin); + static nsresult GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin); void GetAlertName(nsAString& aRetval) { + workers::AssertIsOnMainThread(); + if (mAlertName.IsEmpty()) { + SetAlertName(); + } aRetval = mAlertName; } @@ -314,11 +330,24 @@ protected: private: virtual ~Notification(); + // Creates a Notification and shows it. Returns a reference to the + // Notification if result is NS_OK. The lifetime of this Notification is tied + // to an underlying NotificationRef. Do not hold a non-stack raw pointer to + // it. Be careful about thread safety if acquiring a strong reference. + static already_AddRefed + CreateAndShow(nsIGlobalObject* aGlobal, + const nsAString& aTitle, + const NotificationOptions& aOptions, + ErrorResult& aRv); + nsIPrincipal* GetPrincipal(); nsresult PersistNotification(); void UnpersistNotification(); + void + SetAlertName(); + bool IsTargetThread() const { return NS_IsMainThread() == !mWorkerPrivate; diff --git a/dom/notification/NotificationStorage.js b/dom/notification/NotificationStorage.js index eec321f76312..85f581d031d3 100644 --- a/dom/notification/NotificationStorage.js +++ b/dom/notification/NotificationStorage.js @@ -211,23 +211,29 @@ NotificationStorage.prototype = { } // Pass each notification back separately. + // The callback is called asynchronously to match the behaviour when + // fetching from the database. notifications.forEach(function(notification) { try { - callback.handle(notification.id, - notification.title, - notification.dir, - notification.lang, - notification.body, - notification.tag, - notification.icon, - notification.data, - notification.mozbehavior); + Services.tm.currentThread.dispatch( + callback.handle.bind(callback, + notification.id, + notification.title, + notification.dir, + notification.lang, + notification.body, + notification.tag, + notification.icon, + notification.data, + notification.mozbehavior), + Ci.nsIThread.DISPATCH_NORMAL); } catch (e) { if (DEBUG) { debug("Error calling callback handle: " + e); } } }); try { - callback.done(); + Services.tm.currentThread.dispatch(callback.done, + Ci.nsIThread.DISPATCH_NORMAL); } catch (e) { if (DEBUG) { debug("Error calling callback done: " + e); } } diff --git a/dom/webidl/Notification.webidl b/dom/webidl/Notification.webidl index 7f2aeb827f14..2f7cd5f62b22 100644 --- a/dom/webidl/Notification.webidl +++ b/dom/webidl/Notification.webidl @@ -94,6 +94,8 @@ enum NotificationDirection { }; partial interface ServiceWorkerRegistration { + [Throws] Promise showNotification(DOMString title, optional NotificationOptions options); + [Throws] Promise> getNotifications(optional GetNotificationOptions filter); }; diff --git a/dom/workers/ServiceWorkerRegistration.cpp b/dom/workers/ServiceWorkerRegistration.cpp index 0cd114804490..95319e110637 100644 --- a/dom/workers/ServiceWorkerRegistration.cpp +++ b/dom/workers/ServiceWorkerRegistration.cpp @@ -6,6 +6,7 @@ #include "ServiceWorkerRegistration.h" +#include "mozilla/dom/Notification.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseWorkerProxy.h" #include "mozilla/dom/ServiceWorkerRegistrationBinding.h" @@ -599,14 +600,42 @@ ServiceWorkerRegistrationMainThread::Unregister(ErrorResult& aRv) already_AddRefed ServiceWorkerRegistrationMainThread::ShowNotification(JSContext* aCx, const nsAString& aTitle, - const NotificationOptions& aOptions) + const NotificationOptions& aOptions, + ErrorResult& aRv) { - MOZ_ASSERT(false); - return nullptr; + AssertIsOnMainThread(); + MOZ_ASSERT(GetOwner()); + nsCOMPtr window = GetOwner(); + if (NS_WARN_IF(!window)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr doc = window->GetExtantDoc(); + if (NS_WARN_IF(!doc)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr worker = GetActive(); + if (!worker) { + aRv.ThrowTypeError(MSG_NO_ACTIVE_WORKER); + return nullptr; + } + + nsCOMPtr global = do_QueryInterface(window); + nsRefPtr p = + Notification::ShowPersistentNotification(global, + mScope, aTitle, aOptions, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return p.forget(); } already_AddRefed -ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions) +ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv) { MOZ_ASSERT(false); return nullptr; @@ -1022,14 +1051,25 @@ WorkerListener::UpdateFound() already_AddRefed ServiceWorkerRegistrationWorkerThread::ShowNotification(JSContext* aCx, const nsAString& aTitle, - const NotificationOptions& aOptions) + const NotificationOptions& aOptions, + ErrorResult& aRv) { - MOZ_ASSERT(false); - return nullptr; + // Until Bug 1131324 exposes ServiceWorkerContainer on workers, + // ShowPersistentNotification() checks for valid active worker while it is + // also verifying scope so that we block the worker on the main thread only + // once. + nsRefPtr p = + Notification::ShowPersistentNotification(mWorkerPrivate->GlobalScope(), + mScope, aTitle, aOptions, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return p.forget(); } already_AddRefed -ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOptions& aOptions) +ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv) { MOZ_ASSERT(false); return nullptr; diff --git a/dom/workers/ServiceWorkerRegistration.h b/dom/workers/ServiceWorkerRegistration.h index 1124bd16a5b1..b696b3d3dc48 100644 --- a/dom/workers/ServiceWorkerRegistration.h +++ b/dom/workers/ServiceWorkerRegistration.h @@ -118,10 +118,11 @@ public: already_AddRefed ShowNotification(JSContext* aCx, const nsAString& aTitle, - const NotificationOptions& aOptions); + const NotificationOptions& aOptions, + ErrorResult& aRv); already_AddRefed - GetNotifications(const GetNotificationOptions& aOptions); + GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv); already_AddRefed GetInstalling() override; @@ -206,10 +207,11 @@ public: already_AddRefed ShowNotification(JSContext* aCx, const nsAString& aTitle, - const NotificationOptions& aOptions); + const NotificationOptions& aOptions, + ErrorResult& aRv); already_AddRefed - GetNotifications(const GetNotificationOptions& aOptions); + GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv); already_AddRefed GetInstalling() override; diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini index 66877b573f41..36c0c91cb2ba 100644 --- a/dom/workers/test/serviceworkers/mochitest.ini +++ b/dom/workers/test/serviceworkers/mochitest.ini @@ -103,6 +103,7 @@ support-files = periodic/register.html periodic/wait_for_update.html periodic/unregister.html + notification_constructor_error.js sanitize/frame.html sanitize/register.html sanitize/example_check_and_unregister.html @@ -217,6 +218,7 @@ skip-if = !debug [test_request_context_xslt.html] [test_scopes.html] [test_sandbox_intercept.html] +[test_notification_constructor_error.html] [test_sanitize.html] [test_sanitize_domain.html] [test_service_worker_allowed.html] diff --git a/dom/workers/test/serviceworkers/notification_constructor_error.js b/dom/workers/test/serviceworkers/notification_constructor_error.js new file mode 100644 index 000000000000..644dba480e7a --- /dev/null +++ b/dom/workers/test/serviceworkers/notification_constructor_error.js @@ -0,0 +1 @@ +new Notification("Hi there"); diff --git a/dom/workers/test/serviceworkers/test_notification_constructor_error.html b/dom/workers/test/serviceworkers/test_notification_constructor_error.html new file mode 100644 index 000000000000..b2fae30c5764 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notification_constructor_error.html @@ -0,0 +1,52 @@ + + + + + Bug XXXXXXX - Check that Notification constructor throws in ServiceWorkerGlobalScope + + + + + + +

+ +

+
+
+
+
+

From f35ee7d850df04e23f4072bb79664b3282762f3c Mon Sep 17 00:00:00 2001
From: Nikhil Marathe 
Date: Thu, 25 Jun 2015 18:50:25 -0700
Subject: [PATCH 04/67] Bug 1114554 - Patch 3 - Fire notificationclick event on
 ServiceWorkerGlobalScope. r=wchen,baku

Bug 1114554 - Patch 3.1 - ServiceWorker principal fixes. r=baku

Bug 1162088 introduced origin attributes that ServiceWorkerManager callers have to use. This patch updates notificationclick events to work.

Folded:
Hide NotificationEvent behind pref

--HG--
extra : rebase_source : e892411834525b4c9b31d96d56ddf42634414040
---
 .../base/nsIServiceWorkerManager.idl          |  13 +-
 .../notification/nsINotificationStorage.idl   |  16 +-
 dom/notification/Notification.cpp             | 256 ++++++++++++++++--
 dom/notification/Notification.h               |  39 +++
 dom/notification/NotificationEvent.cpp        |  26 ++
 dom/notification/NotificationEvent.h          |  74 +++++
 dom/notification/NotificationStorage.js       |  23 +-
 dom/notification/moz.build                    |   2 +
 .../mochitest/notification/MockServices.js    |   5 +-
 dom/webidl/NotificationEvent.webidl           |  26 ++
 dom/webidl/moz.build                          |   1 +
 dom/workers/ServiceWorkerManager.cpp          | 112 ++++++++
 dom/workers/test/serviceworkers/mochitest.ini |   3 +
 .../serviceworkers/notificationclick.html     |  27 ++
 .../test/serviceworkers/notificationclick.js  |  15 +
 .../test_notificationclick.html               |  56 ++++
 .../test_serviceworker_interfaces.js          |   2 +
 dom/workers/test/test_notification.html       |   2 +-
 modules/libpref/init/all.js                   |   2 +-
 19 files changed, 675 insertions(+), 25 deletions(-)
 create mode 100644 dom/notification/NotificationEvent.cpp
 create mode 100644 dom/notification/NotificationEvent.h
 create mode 100644 dom/webidl/NotificationEvent.webidl
 create mode 100644 dom/workers/test/serviceworkers/notificationclick.html
 create mode 100644 dom/workers/test/serviceworkers/notificationclick.js
 create mode 100644 dom/workers/test/serviceworkers/test_notificationclick.html

diff --git a/dom/interfaces/base/nsIServiceWorkerManager.idl b/dom/interfaces/base/nsIServiceWorkerManager.idl
index aa6a1c0afd26..3cd33f846541 100644
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -33,7 +33,7 @@ interface nsIServiceWorkerInfo : nsISupports
   readonly attribute DOMString waitingCacheName;
 };
 
-[scriptable, builtinclass, uuid(e9abb123-0099-4d9e-85db-c8cd0aff19e6)]
+[scriptable, builtinclass, uuid(ed1cbbf2-0400-4caa-8eb2-b09d21a94e20)]
 interface nsIServiceWorkerManager : nsISupports
 {
   /**
@@ -126,6 +126,17 @@ interface nsIServiceWorkerManager : nsISupports
                            in nsIServiceWorkerUnregisterCallback aCallback,
                            in DOMString aScope);
 
+  void sendNotificationClickEvent(in ACString aOriginSuffix,
+                                  in ACString scope,
+                                  in AString aID,
+                                  in AString aTitle,
+                                  in AString aDir,
+                                  in AString aLang,
+                                  in AString aBody,
+                                  in AString aTag,
+                                  in AString aIcon,
+                                  in AString aData,
+                                  in AString aBehavior);
   void sendPushEvent(in ACString aOriginAttributes,
                      in ACString aScope,
                      in DOMString aData);
diff --git a/dom/interfaces/notification/nsINotificationStorage.idl b/dom/interfaces/notification/nsINotificationStorage.idl
index 2362bfbd58b0..d8338f8eeb08 100644
--- a/dom/interfaces/notification/nsINotificationStorage.idl
+++ b/dom/interfaces/notification/nsINotificationStorage.idl
@@ -41,7 +41,7 @@ interface nsINotificationStorageCallback : nsISupports
 /**
  * Interface for notification persistence layer.
  */
-[scriptable, uuid(cac01fb0-c2eb-4252-b2f4-5b1fac933bd4)]
+[scriptable, uuid(2f8f84b7-70b5-4673-98d8-fd3f9f8e0e5c)]
 interface nsINotificationStorage : nsISupports
 {
 
@@ -86,6 +86,20 @@ interface nsINotificationStorage : nsISupports
            in DOMString tag,
            in nsINotificationStorageCallback aCallback);
 
+  /**
+   * Retrieve a notification by ID.
+   *
+   * @param origin: the origin/app for which to fetch notifications.
+   * @param id: the id of the notification.
+   * @param callback: nsINotificationStorageCallback whose Handle method will
+   * be called *at most once* if the notification with that ID is found. Not
+   * called if that ID is not found. Done() will be called right after
+   * Handle().
+   */
+  void getByID(in DOMString origin,
+               in DOMString id,
+               in nsINotificationStorageCallback aCallback);
+
   /**
    * Remove a notification from storage.
    *
diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp
index d52bb4dc815b..a04e04914636 100644
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -19,6 +19,7 @@
 #include "nsIDocument.h"
 #include "nsINotificationStorage.h"
 #include "nsIPermissionManager.h"
+#include "nsIServiceWorkerManager.h"
 #include "nsIUUIDGenerator.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStructuredCloneContainer.h"
@@ -29,8 +30,9 @@
 #include "nsNetUtil.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
-#include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
+#include "mozilla/dom/NotificationEvent.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/Services.h"
 #include "nsContentPermissionHelper.h"
 #include "nsILoadContext.h"
@@ -41,6 +43,7 @@
 #include "ServiceWorkerManager.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
+#include "WorkerScope.h"
 
 namespace mozilla {
 namespace dom {
@@ -735,6 +738,48 @@ Notification::Constructor(const GlobalObject& aGlobal,
   return notification.forget();
 }
 
+// static
+already_AddRefed
+Notification::ConstructFromFields(
+    nsIGlobalObject* aGlobal,
+    const nsAString& aID,
+    const nsAString& aTitle,
+    const nsAString& aDir,
+    const nsAString& aLang,
+    const nsAString& aBody,
+    const nsAString& aTag,
+    const nsAString& aIcon,
+    const nsAString& aData,
+    ErrorResult& aRv)
+{
+  MOZ_ASSERT(aGlobal);
+
+  AutoJSAPI jsapi;
+  DebugOnly ok = jsapi.Init(aGlobal);
+  MOZ_ASSERT(ok);
+
+  RootedDictionary options(jsapi.cx());
+  options.mDir = Notification::StringToDirection(nsString(aDir));
+  options.mLang = aLang;
+  options.mBody = aBody;
+  options.mTag = aTag;
+  options.mIcon = aIcon;
+  nsRefPtr notification;
+  notification = Notification::CreateInternal(aID,
+                                              aTitle,
+                                              options);
+
+  if (NS_IsMainThread()) {
+    notification->BindToOwner(aGlobal);
+  }
+
+  notification->InitFromBase64(jsapi.cx(), aData, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return notification.forget();
+}
 
 nsresult
 Notification::PersistNotification()
@@ -946,6 +991,63 @@ protected:
 
 NS_IMPL_ISUPPORTS_INHERITED0(WorkerNotificationObserver, NotificationObserver)
 
+class ServiceWorkerNotificationObserver final : public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  ServiceWorkerNotificationObserver(const nsAString& aScope,
+                                    nsIPrincipal* aPrincipal,
+                                    const nsAString& aID)
+    : mScope(aScope), mID(aID), mPrincipal(aPrincipal)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aPrincipal);
+  }
+
+private:
+  ~ServiceWorkerNotificationObserver()
+  {}
+
+  const nsString mScope;
+  const nsString mID;
+  nsCOMPtr mPrincipal;
+};
+
+NS_IMPL_ISUPPORTS(ServiceWorkerNotificationObserver, nsIObserver)
+
+// For ServiceWorkers.
+bool
+Notification::DispatchNotificationClickEvent()
+{
+  MOZ_ASSERT(mWorkerPrivate);
+  MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
+  mWorkerPrivate->AssertIsOnWorkerThread();
+
+  NotificationEventInit options;
+  options.mNotification = this;
+
+  ErrorResult result;
+  nsRefPtr target = mWorkerPrivate->GlobalScope();
+  nsRefPtr event =
+    NotificationEvent::Constructor(target,
+                                   NS_LITERAL_STRING("notificationclick"),
+                                   options,
+                                   result);
+  if (NS_WARN_IF(result.Failed())) {
+    return false;
+  }
+
+  event->SetTrusted(true);
+  WantsPopupControlCheck popupControlCheck(event);
+  target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
+  // We always return false since in case of dispatching on the serviceworker,
+  // there is no well defined window to focus. The script may use the
+  // Client.focus() API if it wishes.
+  return false;
+}
+
 bool
 Notification::DispatchClickEvent()
 {
@@ -975,12 +1077,15 @@ public:
     : NotificationWorkerRunnable(aNotification->mWorkerPrivate)
     , mNotification(aNotification)
     , mWindow(aWindow)
-  {}
+  {
+    MOZ_ASSERT_IF(mWorkerPrivate->IsServiceWorker(), !mWindow);
+  }
 
   void
   WorkerRunInternal() override
   {
     bool doDefaultAction = mNotification->DispatchClickEvent();
+    MOZ_ASSERT_IF(mWorkerPrivate->IsServiceWorker(), !doDefaultAction);
     if (doDefaultAction) {
       nsRefPtr r = new FocusWindowRunnable(mWindow);
       NS_DispatchToMainThread(r);
@@ -1042,15 +1147,18 @@ WorkerNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
 
   nsRefPtr r;
   if (!strcmp("alertclickcallback", aTopic)) {
-    WorkerPrivate* top = notification->mWorkerPrivate;
-    while (top->GetParent()) {
-      top = top->GetParent();
-    }
+    nsPIDOMWindow* window = nullptr;
+    if (!notification->mWorkerPrivate->IsServiceWorker()) {
+      WorkerPrivate* top = notification->mWorkerPrivate;
+      while (top->GetParent()) {
+        top = top->GetParent();
+      }
 
-    nsPIDOMWindow* window = top->GetWindow();
-    if (NS_WARN_IF(!window || !window->IsCurrentInnerWindow())) {
-      // Window has been closed, this observer is not valid anymore
-      return NS_ERROR_FAILURE;
+      window = top->GetWindow();
+      if (NS_WARN_IF(!window || !window->IsCurrentInnerWindow())) {
+        // Window has been closed, this observer is not valid anymore
+        return NS_ERROR_FAILURE;
+      }
     }
 
     // Instead of bothering with adding features and other worker lifecycle
@@ -1077,6 +1185,107 @@ WorkerNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
   return NS_OK;
 }
 
+class NotificationClickEventCallback final : public nsINotificationStorageCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  NotificationClickEventCallback(nsIPrincipal* aPrincipal,
+                                 const nsAString& aScope)
+  : mPrincipal(aPrincipal), mScope(aScope)
+  {
+    MOZ_ASSERT(aPrincipal);
+  }
+
+  NS_IMETHOD Handle(const nsAString& aID,
+                    const nsAString& aTitle,
+                    const nsAString& aDir,
+                    const nsAString& aLang,
+                    const nsAString& aBody,
+                    const nsAString& aTag,
+                    const nsAString& aIcon,
+                    const nsAString& aData,
+                    const nsAString& aBehavior,
+                    JSContext* aCx) override
+  {
+    MOZ_ASSERT(!aID.IsEmpty());
+
+    AssertIsOnMainThread();
+
+    nsAutoCString originSuffix;
+    nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsCOMPtr swm =
+      mozilla::services::GetServiceWorkerManager();
+
+    if (swm) {
+      swm->SendNotificationClickEvent(originSuffix,
+                                      NS_ConvertUTF16toUTF8(mScope),
+                                      aID,
+                                      aTitle,
+                                      aDir,
+                                      aLang,
+                                      aBody,
+                                      aTag,
+                                      aIcon,
+                                      aData,
+                                      aBehavior);
+    }
+    return NS_OK;
+  }
+
+  NS_IMETHOD Done(JSContext* aCx) override
+  {
+    return NS_OK;
+  }
+
+private:
+  ~NotificationClickEventCallback()
+  {
+  }
+
+  nsCOMPtr mPrincipal;
+  nsString mScope;
+};
+
+NS_IMPL_ISUPPORTS(NotificationClickEventCallback, nsINotificationStorageCallback)
+
+NS_IMETHODIMP
+ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject,
+                                           const char* aTopic,
+                                           const char16_t* aData)
+{
+  AssertIsOnMainThread();
+  // Persistent notifications only care about the click event.
+  if (!strcmp("alertclickcallback", aTopic)) {
+    nsresult rv;
+    nsCOMPtr notificationStorage =
+      do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsCOMPtr callback =
+      new NotificationClickEventCallback(mPrincipal, mScope);
+
+    nsAutoString origin;
+    rv = Notification::GetOrigin(mPrincipal, origin);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = notificationStorage->GetByID(origin, mID, callback);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
 void
 Notification::ShowInternal()
 {
@@ -1126,16 +1335,27 @@ Notification::ShowInternal()
   nsAutoString soundUrl;
   ResolveIconAndSoundURL(iconUrl, soundUrl);
 
-  // Ownership passed to observer.
   nsCOMPtr observer;
-  if (mWorkerPrivate) {
-    // Keep a pointer so that the feature can tell the observer not to release
-    // the notification.
-    mObserver = new WorkerNotificationObserver(Move(ownership));
-    observer = mObserver;
+  if (mScope.IsEmpty()) {
+    // Ownership passed to observer.
+    if (mWorkerPrivate) {
+      // Scope better be set on ServiceWorker initiated requests.
+      MOZ_ASSERT(!mWorkerPrivate->IsServiceWorker());
+      // Keep a pointer so that the feature can tell the observer not to release
+      // the notification.
+      mObserver = new WorkerNotificationObserver(Move(ownership));
+      observer = mObserver;
+    } else {
+      observer = new NotificationObserver(Move(ownership));
+    }
   } else {
-    observer = new NotificationObserver(Move(ownership));
+    // This observer does not care about the Notification. It will be released
+    // at the end of this function.
+    //
+    // The observer is wholly owned by the alerts service.
+    observer = new ServiceWorkerNotificationObserver(mScope, GetPrincipal(), mID);
   }
+  MOZ_ASSERT(observer);
 
   // mDataObjectContainer might be uninitialized here because the notification
   // was constructed with an undefined data property.
@@ -1805,6 +2025,8 @@ Notification::ShowPersistentNotification(nsIGlobalObject *aGlobal,
     return nullptr;
   }
 
+  notification->SetScope(aScope);
+
   return p.forget();
 }
 
diff --git a/dom/notification/Notification.h b/dom/notification/Notification.h
index 0cf7d649b469..beb0fb41fb2c 100644
--- a/dom/notification/Notification.h
+++ b/dom/notification/Notification.h
@@ -115,6 +115,7 @@ class Notification : public DOMEventTargetHelper
   friend class NotificationPermissionRequest;
   friend class NotificationObserver;
   friend class NotificationStorageCallback;
+  friend class ServiceWorkerNotificationObserver;
   friend class WorkerNotificationObserver;
 
 public:
@@ -134,6 +135,30 @@ public:
                                                     const nsAString& aTitle,
                                                     const NotificationOptions& aOption,
                                                     ErrorResult& aRv);
+
+  /**
+   * Used when dispatching the ServiceWorkerEvent.
+   *
+   * Does not initialize the Notification's behavior.
+   * This is because:
+   * 1) The Notification is not shown to the user and so the behavior
+   *    parameters don't matter.
+   * 2) The default binding requires main thread for parsing the JSON from the
+   *    string behavior.
+   */
+  static already_AddRefed
+  ConstructFromFields(
+    nsIGlobalObject* aGlobal,
+    const nsAString& aID,
+    const nsAString& aTitle,
+    const nsAString& aDir,
+    const nsAString& aLang,
+    const nsAString& aBody,
+    const nsAString& aTag,
+    const nsAString& aIcon,
+    const nsAString& aData,
+    ErrorResult& aRv);
+
   void GetID(nsAString& aRetval) {
     aRetval = mID;
   }
@@ -249,6 +274,7 @@ public:
                                                       ErrorResult& rv);
 
   bool DispatchClickEvent();
+  bool DispatchNotificationClickEvent();
 protected:
   // Callers MUST bind the Notification to the correct DOMEventTargetHelper parent using
   // BindToOwner().
@@ -301,6 +327,18 @@ protected:
     aRetval = mAlertName;
   }
 
+  void GetScope(nsAString& aScope)
+  {
+    aScope = mScope;
+  }
+
+  void
+  SetScope(const nsAString& aScope)
+  {
+    MOZ_ASSERT(mScope.IsEmpty());
+    mScope = aScope;
+  }
+
   const nsString mID;
   const nsString mTitle;
   const nsString mBody;
@@ -315,6 +353,7 @@ protected:
   nsCOMPtr mData;
 
   nsString mAlertName;
+  nsString mScope;
 
   // Main thread only.
   bool mIsClosed;
diff --git a/dom/notification/NotificationEvent.cpp b/dom/notification/NotificationEvent.cpp
new file mode 100644
index 000000000000..765dd68cb184
--- /dev/null
+++ b/dom/notification/NotificationEvent.cpp
@@ -0,0 +1,26 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "NotificationEvent.h"
+
+using namespace mozilla::dom;
+
+BEGIN_WORKERS_NAMESPACE
+
+NotificationEvent::NotificationEvent(EventTarget* aOwner)
+  : ExtendableEvent(aOwner)
+{
+}
+
+NS_IMPL_ADDREF_INHERITED(NotificationEvent, ExtendableEvent)
+NS_IMPL_RELEASE_INHERITED(NotificationEvent, ExtendableEvent)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(NotificationEvent)
+NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent)
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(NotificationEvent, ExtendableEvent, mNotification)
+
+END_WORKERS_NAMESPACE
diff --git a/dom/notification/NotificationEvent.h b/dom/notification/NotificationEvent.h
new file mode 100644
index 000000000000..f49873f763a4
--- /dev/null
+++ b/dom/notification/NotificationEvent.h
@@ -0,0 +1,74 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* 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/. */
+
+#ifndef mozilla_dom_workers_notificationevent_h__
+#define mozilla_dom_workers_notificationevent_h__
+
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/NotificationEventBinding.h"
+#include "mozilla/dom/ServiceWorkerEvents.h"
+#include "mozilla/dom/workers/Workers.h"
+
+BEGIN_WORKERS_NAMESPACE
+
+class ServiceWorker;
+class ServiceWorkerClient;
+
+class NotificationEvent final : public ExtendableEvent
+{
+protected:
+  explicit NotificationEvent(EventTarget* aOwner);
+  ~NotificationEvent()
+  {}
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(NotificationEvent, ExtendableEvent)
+  NS_FORWARD_TO_EVENT
+
+  virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle aGivenProto) override
+  {
+    return NotificationEventBinding::Wrap(aCx, this, aGivenProto);
+  }
+
+  static already_AddRefed
+  Constructor(mozilla::dom::EventTarget* aOwner,
+              const nsAString& aType,
+              const NotificationEventInit& aOptions,
+              ErrorResult& aRv)
+  {
+    nsRefPtr e = new NotificationEvent(aOwner);
+    bool trusted = e->Init(aOwner);
+    e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
+    e->SetTrusted(trusted);
+    e->mNotification = aOptions.mNotification;
+    e->SetWantsPopupControlCheck(e->IsTrusted());
+    return e.forget();
+  }
+
+  static already_AddRefed
+  Constructor(const GlobalObject& aGlobal,
+              const nsAString& aType,
+              const NotificationEventInit& aOptions,
+              ErrorResult& aRv)
+  {
+    nsCOMPtr owner = do_QueryInterface(aGlobal.GetAsSupports());
+    return Constructor(owner, aType, aOptions, aRv);
+  }
+
+  already_AddRefed
+  Notification_()
+  {
+    nsRefPtr n = mNotification;
+    return n.forget();
+  }
+
+private:
+  nsRefPtr mNotification;
+};
+
+END_WORKERS_NAMESPACE
+#endif /* mozilla_dom_workers_notificationevent_h__ */
+
diff --git a/dom/notification/NotificationStorage.js b/dom/notification/NotificationStorage.js
index 85f581d031d3..89452a0897d7 100644
--- a/dom/notification/NotificationStorage.js
+++ b/dom/notification/NotificationStorage.js
@@ -4,7 +4,7 @@
 
 "use strict";
 
-const DEBUG = false;
+const DEBUG = true;
 function debug(s) { dump("-*- NotificationStorage.js: " + s + "\n"); }
 
 const Cc = Components.classes;
@@ -85,7 +85,7 @@ NotificationStorage.prototype = {
 
   put: function(origin, id, title, dir, lang, body, tag, icon, alertName,
                 data, behavior) {
-    if (DEBUG) { debug("PUT: " + id + ": " + title); }
+    if (DEBUG) { debug("PUT: " + origin + " " + id + ": " + title); }
     var notification = {
       id: id,
       title: title,
@@ -134,6 +134,25 @@ NotificationStorage.prototype = {
     }
   },
 
+  getByID: function(origin, id, callback) {
+    if (DEBUG) { debug("GETBYID: " + origin + " " + id); }
+    var GetByIDProxyCallback = function(id, originalCallback) {
+      this.searchID = id;
+      this.originalCallback = originalCallback;
+      var self = this;
+      this.handle = function(id, title, dir, lang, body, tag, icon, data, behavior) {
+        if (id == this.searchID) {
+          self.originalCallback.handle(id, title, dir, lang, body, tag, icon, data, behavior);
+        }
+      };
+      this.done = function() {
+        self.originalCallback.done();
+      };
+    };
+
+    return this.get(origin, "", new GetByIDProxyCallback(id, callback));
+  },
+
   delete: function(origin, id) {
     if (DEBUG) { debug("DELETE: " + id); }
     var notification = this._notifications[id];
diff --git a/dom/notification/moz.build b/dom/notification/moz.build
index 25d219ae3906..6a080126e27f 100644
--- a/dom/notification/moz.build
+++ b/dom/notification/moz.build
@@ -18,11 +18,13 @@ EXTRA_JS_MODULES += [
 EXPORTS.mozilla.dom += [
     'DesktopNotification.h',
     'Notification.h',
+    'NotificationEvent.h',
 ]
 
 UNIFIED_SOURCES += [
     'DesktopNotification.cpp',
     'Notification.cpp',
+    'NotificationEvent.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
diff --git a/dom/tests/mochitest/notification/MockServices.js b/dom/tests/mochitest/notification/MockServices.js
index e8794b8014ba..b47194b5c60a 100644
--- a/dom/tests/mochitest/notification/MockServices.js
+++ b/dom/tests/mochitest/notification/MockServices.js
@@ -43,9 +43,10 @@ var MockServices = (function () {
         setTimeout(function () {
           listener.observe(null, "alertshow", cookie);
         }, 100);
+        setTimeout(function () {
+          listener.observe(null, "alertclickcallback", cookie);
+        }, 100);
       }
-
-      // ?? SpecialPowers.wrap(alertListener).observe(null, "alertclickcallback", cookie);
     },
 
     showAppNotification: function(aImageUrl, aTitle, aText, aAlertListener, aDetails) {
diff --git a/dom/webidl/NotificationEvent.webidl b/dom/webidl/NotificationEvent.webidl
new file mode 100644
index 000000000000..e92e33a19137
--- /dev/null
+++ b/dom/webidl/NotificationEvent.webidl
@@ -0,0 +1,26 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/.
+ *
+ * The origin of this IDL file is
+ * http://notifications.spec.whatwg.org/
+ *
+ * Copyright:
+ * To the extent possible under law, the editors have waived all copyright and
+ * related or neighboring rights to this work.
+ */
+
+[Constructor(DOMString type, optional NotificationEventInit eventInitDict),
+ Exposed=ServiceWorker,Func="mozilla::dom::Notification::PrefEnabled"]
+interface NotificationEvent : ExtendableEvent {
+  readonly attribute Notification notification;
+};
+
+dictionary NotificationEventInit : ExtendableEventInit {
+  required Notification notification;
+};
+
+partial interface ServiceWorkerGlobalScope {
+  attribute EventHandler onnotificationclick;
+};
diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build
index e0c02ee32f09..fc340e7946b2 100644
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -335,6 +335,7 @@ WEBIDL_FILES = [
     'NodeIterator.webidl',
     'NodeList.webidl',
     'Notification.webidl',
+    'NotificationEvent.webidl',
     'NotifyPaintEvent.webidl',
     'OfflineAudioCompletionEvent.webidl',
     'OfflineAudioContext.webidl',
diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
index 4eeaaa3fc173..97e798913db6 100644
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -39,6 +39,7 @@
 #include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/dom/InternalHeaders.h"
 #include "mozilla/dom/Navigator.h"
+#include "mozilla/dom/NotificationEvent.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/RootedDictionary.h"
@@ -2282,6 +2283,117 @@ ServiceWorkerManager::SendPushSubscriptionChangeEvent(const nsACString& aOriginA
 #endif
 }
 
+class SendNotificationClickEventRunnable final : public WorkerRunnable
+{
+  nsMainThreadPtrHandle mServiceWorker;
+  const nsString mID;
+  const nsString mTitle;
+  const nsString mDir;
+  const nsString mLang;
+  const nsString mBody;
+  const nsString mTag;
+  const nsString mIcon;
+  const nsString mData;
+  const nsString mBehavior;
+
+public:
+  SendNotificationClickEventRunnable(
+    WorkerPrivate* aWorkerPrivate,
+    nsMainThreadPtrHandle& aServiceWorker,
+    const nsAString& aID,
+    const nsAString& aTitle,
+    const nsAString& aDir,
+    const nsAString& aLang,
+    const nsAString& aBody,
+    const nsAString& aTag,
+    const nsAString& aIcon,
+    const nsAString& aData,
+    const nsAString& aBehavior)
+      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+      , mServiceWorker(aServiceWorker)
+      , mID(aID)
+      , mTitle(aTitle)
+      , mDir(aDir)
+      , mLang(aLang)
+      , mBody(aBody)
+      , mTag(aTag)
+      , mIcon(aIcon)
+      , mData(aData)
+      , mBehavior(aBehavior)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aWorkerPrivate);
+    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+
+    nsRefPtr target = do_QueryObject(aWorkerPrivate->GlobalScope());
+
+    ErrorResult result;
+    nsRefPtr notification =
+      Notification::ConstructFromFields(aWorkerPrivate->GlobalScope(), mID, mTitle, mDir, mLang, mBody, mTag, mIcon, mData, result);
+    if (NS_WARN_IF(result.Failed())) {
+      return false;
+    }
+
+    NotificationEventInit nei;
+    nei.mNotification = notification;
+    nei.mBubbles = false;
+    nei.mCancelable = true;
+
+    nsRefPtr event =
+      NotificationEvent::Constructor(target, NS_LITERAL_STRING("notificationclick"), nei, result);
+    if (NS_WARN_IF(result.Failed())) {
+      return false;
+    }
+
+    event->SetTrusted(true);
+    target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
+    return true;
+  }
+};
+
+NS_IMETHODIMP
+ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix,
+                                                 const nsACString& aScope,
+                                                 const nsAString& aID,
+                                                 const nsAString& aTitle,
+                                                 const nsAString& aDir,
+                                                 const nsAString& aLang,
+                                                 const nsAString& aBody,
+                                                 const nsAString& aTag,
+                                                 const nsAString& aIcon,
+                                                 const nsAString& aData,
+                                                 const nsAString& aBehavior)
+{
+  OriginAttributes attrs;
+  if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsRefPtr serviceWorker = CreateServiceWorkerForScope(attrs, aScope);
+  if (!serviceWorker) {
+    return NS_ERROR_FAILURE;
+  }
+  nsMainThreadPtrHandle serviceWorkerHandle(
+    new nsMainThreadPtrHolder(serviceWorker));
+
+  nsRefPtr r =
+    new SendNotificationClickEventRunnable(serviceWorker->GetWorkerPrivate(), serviceWorkerHandle, aID, aTitle, aDir, aLang, aBody, aTag, aIcon, aData, aBehavior);
+
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 ServiceWorkerManager::GetReadyPromise(nsIDOMWindow* aWindow,
                                       nsISupports** aPromise)
diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini
index 36c0c91cb2ba..d5e30ab103e3 100644
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -93,6 +93,8 @@ support-files =
   client_focus_worker.js
   bug1151916_worker.js
   bug1151916_driver.html
+  notificationclick.html
+  notificationclick.js
   worker_updatefoundevent.js
   worker_updatefoundevent2.js
   updatefoundevent.html
@@ -218,6 +220,7 @@ skip-if = !debug
 [test_request_context_xslt.html]
 [test_scopes.html]
 [test_sandbox_intercept.html]
+[test_notificationclick.html]
 [test_notification_constructor_error.html]
 [test_sanitize.html]
 [test_sanitize_domain.html]
diff --git a/dom/workers/test/serviceworkers/notificationclick.html b/dom/workers/test/serviceworkers/notificationclick.html
new file mode 100644
index 000000000000..80531b85bb95
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclick.html
@@ -0,0 +1,27 @@
+
+
+
+
+  Bug 1114554 - controlled page
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/notificationclick.js b/dom/workers/test/serviceworkers/notificationclick.js
new file mode 100644
index 000000000000..63ab71c35bd5
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclick.js
@@ -0,0 +1,15 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+//
+onnotificationclick = function(e) {
+  self.clients.matchAll().then(function(clients) {
+    if (clients.length === 0) {
+      dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n");
+      return;
+    }
+
+    clients.forEach(function(client) {
+      client.postMessage("done");
+    });
+  });
+}
diff --git a/dom/workers/test/serviceworkers/test_notificationclick.html b/dom/workers/test/serviceworkers/test_notificationclick.html
new file mode 100644
index 000000000000..258736137f0e
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_notificationclick.html
@@ -0,0 +1,56 @@
+
+
+
+
+  Bug 1114554 - Test ServiceWorkerGlobalScope.notificationclick event.
+  
+  
+  
+  
+
+
+Bug 1114554
+

+ +
+
+ + + diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js index 1b0c4d188b3a..2b7e360c7cd4 100644 --- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js +++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js @@ -158,6 +158,8 @@ var interfaceNamesInGlobalScope = "MessagePort", // IMPORTANT: Do not change this list without review from a DOM peer! "Notification", +// IMPORTANT: Do not change this list without review from a DOM peer! + "NotificationEvent", // IMPORTANT: Do not change this list without review from a DOM peer! "Performance", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/workers/test/test_notification.html b/dom/workers/test/test_notification.html index a680fef67ca1..409d9dfd1414 100644 --- a/dom/workers/test/test_notification.html +++ b/dom/workers/test/test_notification.html @@ -18,7 +18,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=916893
 
+ + + + + diff --git a/dom/workers/test/serviceworkers/notification/register.html b/dom/workers/test/serviceworkers/notification/register.html new file mode 100644 index 000000000000..b7df73bede60 --- /dev/null +++ b/dom/workers/test/serviceworkers/notification/register.html @@ -0,0 +1,11 @@ + + diff --git a/dom/workers/test/serviceworkers/notification_alt/register.html b/dom/workers/test/serviceworkers/notification_alt/register.html new file mode 100644 index 000000000000..b7df73bede60 --- /dev/null +++ b/dom/workers/test/serviceworkers/notification_alt/register.html @@ -0,0 +1,11 @@ + + diff --git a/dom/workers/test/serviceworkers/notification_get_sw.js b/dom/workers/test/serviceworkers/notification_get_sw.js new file mode 100644 index 000000000000..540c9d93cc33 --- /dev/null +++ b/dom/workers/test/serviceworkers/notification_get_sw.js @@ -0,0 +1,49 @@ +function postAll(data) { + self.clients.matchAll().then(function(clients) { + if (clients.length == 0) { + dump("***************** NO CLIENTS FOUND! Test messages are being lost *******************\n"); + } + clients.forEach(function(client) { + client.postMessage(data); + }); + }); +} + +function ok(a, msg) { + postAll({type: 'status', status: !!a, msg: a + ": " + msg }); +} + +function is(a, b, msg) { + postAll({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg }); +} + +function done() { + postAll({type: 'finish'}); +} + +onmessage = function(e) { +dump("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% MESSAGE " + e.data + "\n"); + var start; + if (e.data == 'create') { + start = registration.showNotification("This is a title"); + } else { + start = Promise.resolve(); + } + + start.then(function() { + dump("CALLING getNotification\n"); + registration.getNotifications().then(function(notifications) { + dump("RECD getNotification\n"); + is(notifications.length, 1, "There should be one stored notification"); + var notification = notifications[0]; + if (!notification) { + done(); + return; + } + ok(notification instanceof Notification, "Should be a Notification"); + is(notification.title, "This is a title", "Title should match"); + notification.close(); + done(); + }); + }); +} diff --git a/dom/workers/test/serviceworkers/test_notification_get.html b/dom/workers/test/serviceworkers/test_notification_get.html new file mode 100644 index 000000000000..00a1cfd5d42d --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notification_get.html @@ -0,0 +1,164 @@ + + + + ServiceWorkerRegistration.getNotifications() on main thread and worker thread. + + + + + + +

+ +

+
+
+

From 63f067319a05ffe85f628aaff674fda01b8a4dc3 Mon Sep 17 00:00:00 2001
From: Nikhil Marathe 
Date: Thu, 25 Jun 2015 18:50:25 -0700
Subject: [PATCH 08/67] Bug 1114554 - Patch 7 - Call BindToOwner on all
 threads. r=wchen

--HG--
extra : rebase_source : 044b40f169c8408b669cd8e5f01ae7de989ac310
---
 dom/notification/Notification.cpp | 35 +++++++++++++++----------------
 dom/notification/Notification.h   |  8 +++----
 2 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp
index 056408da4784..4ee3eed53c50 100644
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -690,7 +690,8 @@ Notification::IsGetEnabled(JSContext* aCx, JSObject* aObj)
   return NS_IsMainThread();
 }
 
-Notification::Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
+Notification::Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
+                           const nsAString& aTitle, const nsAString& aBody,
                            NotificationDirection aDir, const nsAString& aLang,
                            const nsAString& aTag, const nsAString& aIconUrl,
                            const NotificationBehavior& aBehavior)
@@ -700,7 +701,14 @@ Notification::Notification(const nsAString& aID, const nsAString& aTitle, const
     mTag(aTag), mIconUrl(aIconUrl), mBehavior(aBehavior), mIsClosed(false),
     mIsStored(false), mTaskCount(0)
 {
-  if (!NS_IsMainThread()) {
+  if (NS_IsMainThread()) {
+    // We can only call this on the main thread because
+    // Event::SetEventType() called down the call chain when dispatching events
+    // using DOMEventTargetHelper::DispatchTrustedEvent() will assume the event
+    // is a main thread event if it has a valid owner. It will then attempt to
+    // fetch the atom for the event name which asserts main thread only.
+    BindToOwner(aGlobal);
+  } else {
     mWorkerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(mWorkerPrivate);
   }
@@ -787,14 +795,8 @@ Notification::ConstructFromFields(
   options.mBody = aBody;
   options.mTag = aTag;
   options.mIcon = aIcon;
-  nsRefPtr notification;
-  notification = Notification::CreateInternal(aID,
-                                              aTitle,
-                                              options);
-
-  if (NS_IsMainThread()) {
-    notification->BindToOwner(aGlobal);
-  }
+  nsRefPtr notification = CreateInternal(aGlobal, aID, aTitle,
+                                                       options);
 
   notification->InitFromBase64(jsapi.cx(), aData, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
@@ -879,7 +881,8 @@ Notification::UnpersistNotification()
 }
 
 already_AddRefed
-Notification::CreateInternal(const nsAString& aID,
+Notification::CreateInternal(nsIGlobalObject* aGlobal,
+                             const nsAString& aID,
                              const nsAString& aTitle,
                              const NotificationOptions& aOptions)
 {
@@ -900,8 +903,7 @@ Notification::CreateInternal(const nsAString& aID,
     id = convertedID;
   }
 
-  nsRefPtr notification = new Notification(id,
-                                                         aTitle,
+  nsRefPtr notification = new Notification(aGlobal, id, aTitle,
                                                          aOptions.mBody,
                                                          aOptions.mDir,
                                                          aOptions.mLang,
@@ -2274,11 +2276,8 @@ Notification::CreateAndShow(nsIGlobalObject* aGlobal,
   jsapi.Init(aGlobal);
   JSContext* cx = jsapi.cx();
 
-  nsRefPtr notification = CreateInternal(EmptyString(),
-                                                       aTitle,
-                                                       aOptions);
-
-  notification->BindToOwner(aGlobal);
+  nsRefPtr notification = CreateInternal(aGlobal, EmptyString(),
+                                                       aTitle, aOptions);
 
   // Make a structured clone of the aOptions.mData object
   JS::Rooted data(cx, aOptions.mData);
diff --git a/dom/notification/Notification.h b/dom/notification/Notification.h
index 41bc90154231..e7a685b71c14 100644
--- a/dom/notification/Notification.h
+++ b/dom/notification/Notification.h
@@ -289,14 +289,14 @@ public:
   bool DispatchClickEvent();
   bool DispatchNotificationClickEvent();
 protected:
-  // Callers MUST bind the Notification to the correct DOMEventTargetHelper parent using
-  // BindToOwner().
-  Notification(const nsAString& aID, const nsAString& aTitle, const nsAString& aBody,
+  Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
+               const nsAString& aTitle, const nsAString& aBody,
                NotificationDirection aDir, const nsAString& aLang,
                const nsAString& aTag, const nsAString& aIconUrl,
                const NotificationBehavior& aBehavior);
 
-  static already_AddRefed CreateInternal(const nsAString& aID,
+  static already_AddRefed CreateInternal(nsIGlobalObject* aGlobal,
+                                                       const nsAString& aID,
                                                        const nsAString& aTitle,
                                                        const NotificationOptions& aOptions);
 

From 16a5e3f9b8aa3ed536d104c2b29beac5114b67e7 Mon Sep 17 00:00:00 2001
From: Nikhil Marathe 
Date: Thu, 25 Jun 2015 18:50:26 -0700
Subject: [PATCH 09/67] Bug 1114554 - Patch 8 - Support waitUntil() on
 notificationclick event. r=baku

This was originally a part of Bug 1160527, separated due to changes in order of landing the patches.

--HG--
extra : rebase_source : d997a510f7a93506e8804a1a84671e58684c60f5
---
 dom/workers/ServiceWorkerManager.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
index fb86820fd113..bb481e377815 100644
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -2357,7 +2357,14 @@ public:
     }
 
     event->SetTrusted(true);
-    target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
+    nsRefPtr waitUntilPromise =
+      DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event);
+
+    if (waitUntilPromise) {
+      nsRefPtr handler = new KeepAliveHandler(mServiceWorker);
+      waitUntilPromise->AppendNativeHandler(handler);
+    }
+
     return true;
   }
 };

From 25dfda5b20ddd365f39a2b0f706497a9acf6dd0a Mon Sep 17 00:00:00 2001
From: Robert Bindar 
Date: Mon, 13 Jul 2015 13:45:17 -0700
Subject: [PATCH 10/67] Bug 1114554 - Patch 9 - Fixed crash in b2g-desktop
 tests. r=nsm

--HG--
extra : rebase_source : 1b59acf04bcc9a8311bc7c6fe163b99c84eabe35
---
 dom/notification/Notification.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp
index 4ee3eed53c50..73454a12473a 100644
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -1148,6 +1148,15 @@ NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
       }
     }
   } else if (!strcmp("alertfinished", aTopic)) {
+    // In b2g-desktop, if the app is closed, closing a notification still
+    // triggers the observer which might be alive even though the owner window
+    // was closed. Keeping this until we remove the close event (Bug 1139363)
+    // from implementation.
+    nsCOMPtr window = notification->GetOwner();
+    if (NS_WARN_IF(!window || !window->IsCurrentInnerWindow())) {
+      return NS_ERROR_FAILURE;
+    }
+
     notification->UnpersistNotification();
     notification->mIsClosed = true;
     notification->DispatchTrustedEvent(NS_LITERAL_STRING("close"));

From 56a5f8273e83fa81119b8fd8e5fcf8513ff00893 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Wed, 10 Jun 2015 12:48:22 -0400
Subject: [PATCH 11/67] Bug 1173523 - Part 1: Expose an nsIPrincipal from
 nsIPermission rather than a host string, appId, and isInBrowserElement,
 r=ehsan

---
 dom/ipc/ContentChild.cpp                      |  18 ++-
 dom/ipc/ContentParent.cpp                     |  15 +--
 dom/storage/DOMStorageObserver.cpp            |  14 +-
 extensions/cookie/nsPermission.cpp            | 123 ++++++++++++++----
 extensions/cookie/nsPermission.h              |   8 +-
 extensions/cookie/nsPermissionManager.cpp     | 122 +++++++++--------
 .../test/unit/test_permmanager_local_files.js |   2 +-
 netwerk/base/nsIPermission.idl                |  44 +++++--
 netwerk/ipc/NeckoMessageUtils.h               |  30 ++---
 9 files changed, 241 insertions(+), 135 deletions(-)

diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp
index b4acda4172d8..ea4e78821f31 100644
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -53,6 +53,7 @@
 #include "mozilla/plugins/PluginModuleParent.h"
 #include "mozilla/widget/WidgetMessageUtils.h"
 #include "mozilla/media/MediaChild.h"
+#include "mozilla/BasePrincipal.h"
 
 #if defined(MOZ_CONTENT_SANDBOX)
 #if defined(XP_WIN)
@@ -2112,19 +2113,16 @@ ContentChild::RecvAddPermission(const IPC::Permission& permission)
     MOZ_ASSERT(permissionManager,
                "We have no permissionManager in the Content process !");
 
+    nsAutoCString originNoSuffix;
+    OriginAttributes attrs;
+    attrs.PopulateFromOrigin(permission.origin, originNoSuffix);
+
     nsCOMPtr uri;
-    NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + nsCString(permission.host));
-    NS_ENSURE_TRUE(uri, true);
-
-    nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-    MOZ_ASSERT(secMan);
-
-    nsCOMPtr principal;
-    nsresult rv = secMan->GetAppCodebasePrincipal(uri, permission.appId,
-                                                permission.isInBrowserElement,
-                                                getter_AddRefs(principal));
+    nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
     NS_ENSURE_SUCCESS(rv, true);
 
+    nsCOMPtr principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+
     // child processes don't care about modification time.
     int64_t modificationTime = 0;
 
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
index c2b484bd3221..a2d750d344eb 100755
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2559,12 +2559,12 @@ ContentParent::RecvReadPermissions(InfallibleTArray* aPermissio
         enumerator->GetNext(getter_AddRefs(supp));
         nsCOMPtr perm = do_QueryInterface(supp);
 
-        nsCString host;
-        perm->GetHost(host);
-        uint32_t appId;
-        perm->GetAppId(&appId);
-        bool isInBrowserElement;
-        perm->GetIsInBrowserElement(&isInBrowserElement);
+        nsCOMPtr principal;
+        perm->GetPrincipal(getter_AddRefs(principal));
+        nsCString origin;
+        if (principal) {
+            principal->GetOrigin(origin);
+        }
         nsCString type;
         perm->GetType(type);
         uint32_t capability;
@@ -2574,8 +2574,7 @@ ContentParent::RecvReadPermissions(InfallibleTArray* aPermissio
         int64_t expireTime;
         perm->GetExpireTime(&expireTime);
 
-        aPermissions->AppendElement(IPC::Permission(host, appId,
-                                                    isInBrowserElement, type,
+        aPermissions->AppendElement(IPC::Permission(origin, type,
                                                     capability, expireType,
                                                     expireTime));
     }
diff --git a/dom/storage/DOMStorageObserver.cpp b/dom/storage/DOMStorageObserver.cpp
index 29a20ed98722..54ca84016398 100644
--- a/dom/storage/DOMStorageObserver.cpp
+++ b/dom/storage/DOMStorageObserver.cpp
@@ -196,8 +196,20 @@ DOMStorageObserver::Observe(nsISupports* aSubject,
       return NS_OK;
     }
 
+    nsCOMPtr principal;
+    perm->GetPrincipal(getter_AddRefs(principal));
+    if (!principal) {
+      return NS_OK;
+    }
+
+    nsCOMPtr origin;
+    principal->GetURI(getter_AddRefs(origin));
+    if (!origin) {
+      return NS_OK;
+    }
+
     nsAutoCString host;
-    perm->GetHost(host);
+    origin->GetHost(host);
     if (host.IsEmpty()) {
       return NS_OK;
     }
diff --git a/extensions/cookie/nsPermission.cpp b/extensions/cookie/nsPermission.cpp
index bdd3abad0e5e..28046fd87147 100644
--- a/extensions/cookie/nsPermission.cpp
+++ b/extensions/cookie/nsPermission.cpp
@@ -4,48 +4,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsPermission.h"
+#include "nsContentUtils.h"
 #include "nsIClassInfoImpl.h"
+#include "nsIEffectiveTLDService.h"
 
 // nsPermission Implementation
 
 NS_IMPL_CLASSINFO(nsPermission, nullptr, 0, {0})
 NS_IMPL_ISUPPORTS_CI(nsPermission, nsIPermission)
 
-nsPermission::nsPermission(const nsACString &aHost,
-                           uint32_t aAppId,
-                           bool aIsInBrowserElement,
+nsPermission::nsPermission(nsIPrincipal*    aPrincipal,
                            const nsACString &aType,
                            uint32_t         aCapability,
                            uint32_t         aExpireType,
                            int64_t          aExpireTime)
- : mHost(aHost)
+ : mPrincipal(aPrincipal)
  , mType(aType)
  , mCapability(aCapability)
  , mExpireType(aExpireType)
  , mExpireTime(aExpireTime)
- , mAppId(aAppId)
- , mIsInBrowserElement(aIsInBrowserElement)
 {
 }
 
 NS_IMETHODIMP
-nsPermission::GetHost(nsACString &aHost)
+nsPermission::GetPrincipal(nsIPrincipal** aPrincipal)
 {
-  aHost = mHost;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsPermission::GetAppId(uint32_t* aAppId)
-{
-  *aAppId = mAppId;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsPermission::GetIsInBrowserElement(bool* aIsInBrowserElement)
-{
-  *aIsInBrowserElement = mIsInBrowserElement;
+  nsCOMPtr copy = mPrincipal;
+  copy.forget(aPrincipal);
   return NS_OK;
 }
 
@@ -76,3 +61,97 @@ nsPermission::GetExpireTime(int64_t *aExpireTime)
   *aExpireTime = mExpireTime;
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsPermission::Matches(nsIPrincipal* aPrincipal, bool aExactHost, bool* aMatches)
+{
+  NS_ENSURE_ARG_POINTER(aPrincipal);
+  NS_ENSURE_ARG_POINTER(aMatches);
+
+  *aMatches = false;
+
+  // If the principals are equal, then they match.
+  if (mPrincipal->Equals(aPrincipal)) {
+    *aMatches = true;
+    return NS_OK;
+  }
+
+  // Make sure that the OriginAttributes of the two entries are the same
+  nsAutoCString theirSuffix;
+  nsresult rv = aPrincipal->GetOriginSuffix(theirSuffix);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString ourSuffix;
+  rv = mPrincipal->GetOriginSuffix(ourSuffix);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (theirSuffix != ourSuffix) {
+    return NS_OK;
+  }
+
+  // Right now, we only care about the hosts
+  nsCOMPtr theirURI;
+  rv = aPrincipal->GetURI(getter_AddRefs(theirURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr ourURI;
+  rv = mPrincipal->GetURI(getter_AddRefs(ourURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Get the hosts so we can compare them
+  nsAutoCString theirHost;
+  rv = theirURI->GetHost(theirHost);
+  if (NS_FAILED(rv) || theirHost.IsEmpty()) {
+    return NS_OK;
+  }
+
+  nsAutoCString ourHost;
+  rv = ourURI->GetHost(ourHost);
+  if (NS_FAILED(rv) || ourHost.IsEmpty()) {
+    return NS_OK;
+  }
+
+  if (aExactHost) { // If we only care about the exact host, we compare them and are done
+    *aMatches = theirHost == ourHost;
+    return NS_OK;
+  }
+
+  nsCOMPtr tldService =
+    do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+  if (!tldService) {
+    NS_ERROR("Should have a tld service!");
+    return NS_ERROR_FAILURE;
+  }
+
+  // Check if the host or any subdomain of the host matches. This loop will
+  // not loop forever, as GetNextSubDomain will eventually fail with
+  // NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS.
+  while (theirHost != ourHost) {
+    rv = tldService->GetNextSubDomain(theirHost, theirHost);
+    if (NS_FAILED(rv)) {
+      if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
+        return NS_OK;
+      } else {
+        return rv;
+      }
+    }
+  }
+
+  *aMatches = true;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPermission::MatchesURI(nsIURI* aURI, bool aExactHost, bool* aMatches)
+{
+  NS_ENSURE_ARG_POINTER(aURI);
+
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
+
+  nsCOMPtr principal;
+  nsresult rv = secMan->GetNoAppCodebasePrincipal(aURI, getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return Matches(principal, aExactHost, aMatches);
+}
diff --git a/extensions/cookie/nsPermission.h b/extensions/cookie/nsPermission.h
index 8e459fb59f71..1e966e4b78b8 100644
--- a/extensions/cookie/nsPermission.h
+++ b/extensions/cookie/nsPermission.h
@@ -18,9 +18,7 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERMISSION
 
-  nsPermission(const nsACString &aHost,
-               uint32_t aAppId,
-               bool aIsInBrowserElement,
+  nsPermission(nsIPrincipal* aPrincipal,
                const nsACString &aType,
                uint32_t aCapability,
                uint32_t aExpireType,
@@ -29,13 +27,11 @@ public:
 protected:
   virtual ~nsPermission() {};
 
-  nsCString mHost;
+  nsCOMPtr mPrincipal;
   nsCString mType;
   uint32_t  mCapability;
   uint32_t  mExpireType;
   int64_t   mExpireTime;
-  uint32_t  mAppId;
-  bool      mIsInBrowserElement;
 };
 
 #endif // nsPermission_h__
diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp
index 1364886c36b5..72ae711641d5 100644
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -8,6 +8,7 @@
 
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/unused.h"
 #include "nsPermissionManager.h"
 #include "nsPermission.h"
@@ -741,6 +742,15 @@ nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal,
     return NS_OK;
   }
 
+  // Null principals can't meaningfully have persisted permissions attached to
+  // them, so we don't allow adding permissions for them.
+  bool isNullPrincipal;
+  nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (isNullPrincipal) {
+    return NS_OK;
+  }
+
   // Permissions may not be added to expanded principals.
   if (IsExpandedPrincipal(aPrincipal)) {
     return NS_ERROR_INVALID_ARG;
@@ -770,16 +780,12 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!IsChildProcess()) {
-    uint32_t appId;
-    rv = aPrincipal->GetAppId(&appId);
+    nsAutoCString origin;
+    rv = aPrincipal->GetOrigin(origin);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    bool isInBrowserElement;
-    rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    IPC::Permission permission(host, appId, isInBrowserElement, aType,
-                               aPermission, aExpireType, aExpireTime);
+    IPC::Permission permission(origin, aType, aPermission,
+                               aExpireType, aExpireTime);
 
     nsTArray cplist;
     ContentParent::GetAll(cplist);
@@ -1098,12 +1104,8 @@ nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal,
 NS_IMETHODIMP
 nsPermissionManager::RemovePermission(nsIPermission* aPerm)
 {
-  nsAutoCString host;
-  nsresult rv = aPerm->GetHost(host);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   nsCOMPtr principal;
-  rv = GetPrincipal(host, getter_AddRefs(principal));
+  nsresult rv = aPerm->GetPrincipal(getter_AddRefs(principal));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString type;
@@ -1301,9 +1303,14 @@ nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal,
   }
 
   PermissionEntry& perm = entry->GetPermissions()[idx];
-  nsCOMPtr r = new nsPermission(entry->GetKey()->mHost,
-                                               entry->GetKey()->mAppId,
-                                               entry->GetKey()->mIsInBrowserElement,
+  nsCOMPtr principal;
+  rv = GetPrincipal(entry->GetKey()->mHost,
+                    entry->GetKey()->mAppId,
+                    entry->GetKey()->mIsInBrowserElement,
+                    getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr r = new nsPermission(principal,
                                                mTypeArray.ElementAt(perm.mType),
                                                perm.mPermission,
                                                perm.mExpireType,
@@ -1444,6 +1451,7 @@ nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
     return GetPermissionHashKey(NS_LITERAL_CSTRING(""), aAppId, aIsInBrowserElement, aType, true);
   }
 
+  // If aExactHostMatch wasn't true, we can check if the base domain has a permission entry.
   if (!aExactHostMatch) {
     nsCString domain = GetNextSubDomainForHost(aHost);
     if (!domain.IsEmpty()) {
@@ -1485,9 +1493,13 @@ AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg)
       continue;
     }
 
-    nsPermission *perm = new nsPermission(entry->GetKey()->mHost,
-                                          entry->GetKey()->mAppId,
-                                          entry->GetKey()->mIsInBrowserElement,
+    nsCOMPtr principal;
+    GetPrincipal(entry->GetKey()->mHost,
+                 entry->GetKey()->mAppId,
+                 entry->GetKey()->mIsInBrowserElement,
+                 getter_AddRefs(principal));
+
+    nsPermission *perm = new nsPermission(principal,
                                           data->types->ElementAt(permEntry.mType),
                                           permEntry.mPermission,
                                           permEntry.mExpireType,
@@ -1542,9 +1554,13 @@ AddPermissionsModifiedSinceToList(
       continue;
     }
 
-    nsPermission* perm = new nsPermission(entry->GetKey()->mHost,
-                                          entry->GetKey()->mAppId,
-                                          entry->GetKey()->mIsInBrowserElement,
+    nsCOMPtr principal;
+    GetPrincipal(entry->GetKey()->mHost,
+                 entry->GetKey()->mAppId,
+                 entry->GetKey()->mIsInBrowserElement,
+                 getter_AddRefs(principal));
+
+    nsPermission* perm = new nsPermission(principal,
                                           data->types->ElementAt(permEntry.mType),
                                           permEntry.mPermission,
                                           permEntry.mExpireType,
@@ -1567,22 +1583,21 @@ nsPermissionManager::RemoveAllModifiedSince(int64_t aModificationTime)
   mPermissionTable.EnumerateEntries(AddPermissionsModifiedSinceToList, &data);
 
   for (int32_t i = 0; iGetHost(host);
-    array[i]->GetIsInBrowserElement(&isInBrowserElement);
-    array[i]->GetType(type);
-    array[i]->GetAppId(&appId);
-
     nsCOMPtr principal;
-    if (NS_FAILED(GetPrincipal(host, appId, isInBrowserElement,
-                               getter_AddRefs(principal)))) {
+    nsAutoCString type;
+
+    nsresult rv = array[i]->GetPrincipal(getter_AddRefs(principal));
+    if (NS_FAILED(rv)) {
       NS_ERROR("GetPrincipal() failed!");
       continue;
     }
+
+    rv = array[i]->GetType(type);
+    if (NS_FAILED(rv)) {
+      NS_ERROR("GetType() failed!");
+      continue;
+    }
+
     // AddInternal handles removal, so let it do the work...
     AddInternal(
       principal,
@@ -1612,9 +1627,13 @@ nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey
       continue;
     }
 
-    data->permissions.AppendObject(new nsPermission(entry->GetKey()->mHost,
-                                                    entry->GetKey()->mAppId,
-                                                    entry->GetKey()->mIsInBrowserElement,
+    nsCOMPtr principal;
+    GetPrincipal(entry->GetKey()->mHost,
+                 entry->GetKey()->mAppId,
+                 entry->GetKey()->mIsInBrowserElement,
+                 getter_AddRefs(principal));
+
+    data->permissions.AppendObject(new nsPermission(principal,
                                                     gPermissionManager->mTypeArray.ElementAt(permEntry.mType),
                                                     permEntry.mPermission,
                                                     permEntry.mExpireType,
@@ -1658,21 +1677,12 @@ nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly)
   mPermissionTable.EnumerateEntries(GetPermissionsForApp, &data);
 
   for (int32_t i=0; i principal;
     nsAutoCString type;
 
-    data.permissions[i]->GetHost(host);
-    data.permissions[i]->GetIsInBrowserElement(&isInBrowserElement);
+    data.permissions[i]->GetPrincipal(getter_AddRefs(principal));
     data.permissions[i]->GetType(type);
 
-    nsCOMPtr principal;
-    if (NS_FAILED(GetPrincipal(host, aAppId, isInBrowserElement,
-                               getter_AddRefs(principal)))) {
-      NS_ERROR("GetPrincipal() failed!");
-      continue;
-    }
-
     AddInternal(principal,
                 type,
                 nsIPermissionManager::UNKNOWN_ACTION,
@@ -1799,8 +1809,11 @@ nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost,
                                                    int64_t           aExpireTime,
                                                    const char16_t  *aData)
 {
+  nsCOMPtr principal;
+  GetPrincipal(aHost, aAppId, aIsInBrowserElement, getter_AddRefs(principal));
+
   nsCOMPtr permission =
-    new nsPermission(aHost, aAppId, aIsInBrowserElement, aType, aPermission,
+    new nsPermission(principal, aType, aPermission,
                      aExpireType, aExpireTime);
   if (permission)
     NotifyObservers(permission, aData);
@@ -2293,11 +2306,16 @@ nsPermissionManager::FetchPermissions() {
   for (uint32_t i = 0; i < perms.Length(); i++) {
     const IPC::Permission &perm = perms[i];
 
-    nsCOMPtr principal;
-    nsresult rv = GetPrincipal(perm.host, perm.appId,
-                               perm.isInBrowserElement, getter_AddRefs(principal));
+    nsAutoCString originNoSuffix;
+    mozilla::OriginAttributes attrs;
+    attrs.PopulateFromOrigin(perm.origin, originNoSuffix);
+
+    nsCOMPtr uri;
+    nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    nsCOMPtr principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+
     // The child process doesn't care about modification times - it neither
     // reads nor writes, nor removes them based on the date - so 0 (which
     // will end up as now()) is fine.
diff --git a/extensions/cookie/test/unit/test_permmanager_local_files.js b/extensions/cookie/test/unit/test_permmanager_local_files.js
index 6de1ce7b063d..32d72dd70b86 100644
--- a/extensions/cookie/test/unit/test_permmanager_local_files.js
+++ b/extensions/cookie/test/unit/test_permmanager_local_files.js
@@ -45,4 +45,4 @@ function run_test() {
   do_check_eq(pm.testPermissionFromPrincipal(principal, "test/local-files"), pm.ALLOW_ACTION);
   do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.ALLOW_ACTION);
   do_check_eq(pm.testPermissionFromPrincipal(fileInDirPrincipal, "test/local-files"), pm.ALLOW_ACTION);
-}
\ No newline at end of file
+}
diff --git a/netwerk/base/nsIPermission.idl b/netwerk/base/nsIPermission.idl
index 4500679905ac..1e8a41b76c10 100644
--- a/netwerk/base/nsIPermission.idl
+++ b/netwerk/base/nsIPermission.idl
@@ -6,7 +6,10 @@
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(cfb08e46-193c-4be7-a467-d7775fb2a31e)]
+interface nsIPrincipal;
+interface nsIURI;
+
+[scriptable, uuid(bb409a51-2371-4fea-9dc9-b7286a458b8c)]
 /**
  * This interface defines a "permission" object,
  * used to specify allowed/blocked objects from
@@ -16,19 +19,9 @@
 interface nsIPermission : nsISupports
 {
     /**
-     * The name of the host for which the permission is set
+     * The principal for which this permission applies.
      */
-    readonly attribute AUTF8String host;
-
-    /**
-     * The id of the app for which the permission is set.
-     */
-    readonly attribute unsigned long appId;
-
-    /**
-     * Whether the permission has been set to a page inside a browser element.
-     */
-    readonly attribute boolean isInBrowserElement;
+    readonly attribute nsIPrincipal principal;
 
     /**
      * a case-sensitive ASCII string, indicating the type of permission
@@ -56,4 +49,29 @@ interface nsIPermission : nsISupports
      * 0:00:00).
      */
     readonly attribute int64_t expireTime;
+
+    /**
+     * Test whether a principal would be affected by this permission.
+     *
+     * @param principal  the principal to test
+     * @param exactHost  If true, only the specific host will be matched,
+     *                   @see nsIPermissionManager::testExactPermission.
+     *                   If false, subdomains will also be searched,
+     *                   @see nsIPermissionManager::testPermission.
+     */
+    boolean matches(in nsIPrincipal principal,
+                    in boolean exactHost);
+
+    /**
+     * Test whether a URI would be affected by this permission.
+     * This performs a matches with a NO_APP_ID identifier.
+     *
+     * @param uri        the uri to test
+     * @param exactHost  If true, only the specific host will be matched,
+     *                   @see nsIPermissionManager::testExactPermission.
+     *                   If false, subdomains will also be searched,
+     *                   @see nsIPermissionManager::testPermission.
+     */
+    boolean matchesURI(in nsIURI uri,
+                       in boolean exactHost);
 };
diff --git a/netwerk/ipc/NeckoMessageUtils.h b/netwerk/ipc/NeckoMessageUtils.h
index 3886a81a8bd7..ce0219d21a23 100644
--- a/netwerk/ipc/NeckoMessageUtils.h
+++ b/netwerk/ipc/NeckoMessageUtils.h
@@ -20,26 +20,20 @@ namespace IPC {
 
 struct Permission
 {
-  nsCString host, type;
+  nsCString origin, type;
   uint32_t capability, expireType;
   int64_t expireTime;
-  uint32_t appId;
-  bool isInBrowserElement;
 
   Permission() { }
-  Permission(const nsCString& aHost,
-             const uint32_t aAppId,
-             const bool aIsInBrowserElement,
+  Permission(const nsCString& aOrigin,
              const nsCString& aType,
              const uint32_t aCapability,
              const uint32_t aExpireType,
-             const int64_t aExpireTime) : host(aHost),
+             const int64_t aExpireTime) : origin(aOrigin),
                                           type(aType),
                                           capability(aCapability),
                                           expireType(aExpireType),
-                                          expireTime(aExpireTime),
-                                          appId(aAppId),
-                                          isInBrowserElement(aIsInBrowserElement)
+                                          expireTime(aExpireTime)
   {}
 };
 
@@ -48,34 +42,26 @@ struct ParamTraits
 {
   static void Write(Message* aMsg, const Permission& aParam)
   {
-    WriteParam(aMsg, aParam.host);
+    WriteParam(aMsg, aParam.origin);
     WriteParam(aMsg, aParam.type);
     WriteParam(aMsg, aParam.capability);
     WriteParam(aMsg, aParam.expireType);
     WriteParam(aMsg, aParam.expireTime);
-    WriteParam(aMsg, aParam.appId);
-    WriteParam(aMsg, aParam.isInBrowserElement);
   }
 
   static bool Read(const Message* aMsg, void** aIter, Permission* aResult)
   {
-    return ReadParam(aMsg, aIter, &aResult->host) &&
+    return ReadParam(aMsg, aIter, &aResult->origin) &&
            ReadParam(aMsg, aIter, &aResult->type) &&
            ReadParam(aMsg, aIter, &aResult->capability) &&
            ReadParam(aMsg, aIter, &aResult->expireType) &&
-           ReadParam(aMsg, aIter, &aResult->expireTime) &&
-           ReadParam(aMsg, aIter, &aResult->appId) &&
-           ReadParam(aMsg, aIter, &aResult->isInBrowserElement);
+           ReadParam(aMsg, aIter, &aResult->expireTime);
   }
 
   static void Log(const Permission& p, std::wstring* l)
   {
     l->append(L"(");
-    LogParam(p.host, l);
-    l->append(L", ");
-    LogParam(p.appId, l);
-    l->append(L", ");
-    LogParam(p.isInBrowserElement, l);
+    LogParam(p.origin, l);
     l->append(L", ");
     LogParam(p.capability, l);
     l->append(L", ");

From 567aeb78e995691016f14ff36303dc87c9345c6e Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Wed, 10 Jun 2015 11:53:25 -0400
Subject: [PATCH 12/67] Bug 1173523 - Part 2: Tests for new methods
 nsIPrincipal::MatchesURI and nsIPrincipal::Matches, r=ehsan

---
 .../test/unit/test_permmanager_matches.js     | 133 ++++++++++++++++
 .../test/unit/test_permmanager_matchesuri.js  | 150 ++++++++++++++++++
 extensions/cookie/test/unit/xpcshell.ini      |   2 +
 3 files changed, 285 insertions(+)
 create mode 100644 extensions/cookie/test/unit/test_permmanager_matches.js
 create mode 100644 extensions/cookie/test/unit/test_permmanager_matchesuri.js

diff --git a/extensions/cookie/test/unit/test_permmanager_matches.js b/extensions/cookie/test/unit/test_permmanager_matches.js
new file mode 100644
index 000000000000..425a12c705b8
--- /dev/null
+++ b/extensions/cookie/test/unit/test_permmanager_matches.js
@@ -0,0 +1,133 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function matches_always(perm, principals) {
+  principals.forEach((principal) => {
+    do_check_true(perm.matches(principal, true), "perm: " + perm.principal.origin + ", princ: " + principal.origin);
+    do_check_true(perm.matches(principal, false), "perm: " + perm.principal.origin + ", princ: " + principal.origin);
+  });
+}
+
+function matches_weak(perm, principals) {
+  principals.forEach((principal) => {
+    do_check_false(perm.matches(principal, true), "perm: " + perm.principal.origin + ", princ: " + principal.origin);
+    do_check_true(perm.matches(principal, false), "perm: " + perm.principal.origin + ", princ: " + principal.origin);
+  });
+}
+
+function matches_never(perm, principals) {
+  principals.forEach((principal) => {
+    do_check_false(perm.matches(principal, true), "perm: " + perm.principal.origin + ", princ: " + principal.origin);
+    do_check_false(perm.matches(principal, false), "perm: " + perm.principal.origin + ", princ: " + principal.origin);
+  });
+}
+
+function run_test() {
+  // initialize the permission manager service
+  let pm = Cc["@mozilla.org/permissionmanager;1"].
+        getService(Ci.nsIPermissionManager);
+
+  let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
+        .getService(Ci.nsIScriptSecurityManager);
+
+  // Add some permissions
+  let uri0 = NetUtil.newURI("http://google.com/search?q=foo#hashtag", null, null);
+  let uri1 = NetUtil.newURI("http://hangouts.google.com/subdir", null, null);
+  let uri2 = NetUtil.newURI("http://google.org/", null, null);
+
+  // NOTE: These entries have different ports/schemes, but will be considered
+  // the same right now (as permissions are based on host only). With bug 1165263
+  // this will be fixed.
+  let uri3 = NetUtil.newURI("https://google.com/some/random/subdirectory", null, null);
+  let uri4 = NetUtil.newURI("https://hangouts.google.com/#!/hangout", null, null);
+  let uri5 = NetUtil.newURI("http://google.com:8096/", null, null);
+
+  let uri0_n_n = secMan.getNoAppCodebasePrincipal(uri0);
+  let uri1_n_n = secMan.getNoAppCodebasePrincipal(uri1);
+  let uri2_n_n = secMan.getNoAppCodebasePrincipal(uri2);
+  let uri3_n_n = secMan.getNoAppCodebasePrincipal(uri3);
+  let uri4_n_n = secMan.getNoAppCodebasePrincipal(uri4);
+  let uri5_n_n = secMan.getNoAppCodebasePrincipal(uri5);
+
+  let uri0_1000_n = secMan.getAppCodebasePrincipal(uri0, 1000, false);
+  let uri1_1000_n = secMan.getAppCodebasePrincipal(uri1, 1000, false);
+  let uri2_1000_n = secMan.getAppCodebasePrincipal(uri2, 1000, false);
+  let uri3_1000_n = secMan.getAppCodebasePrincipal(uri3, 1000, false);
+  let uri4_1000_n = secMan.getAppCodebasePrincipal(uri4, 1000, false);
+  let uri5_1000_n = secMan.getAppCodebasePrincipal(uri5, 1000, false);
+
+  let uri0_1000_y = secMan.getAppCodebasePrincipal(uri0, 1000, true);
+  let uri1_1000_y = secMan.getAppCodebasePrincipal(uri1, 1000, true);
+  let uri2_1000_y = secMan.getAppCodebasePrincipal(uri2, 1000, true);
+  let uri3_1000_y = secMan.getAppCodebasePrincipal(uri3, 1000, true);
+  let uri4_1000_y = secMan.getAppCodebasePrincipal(uri4, 1000, true);
+  let uri5_1000_y = secMan.getAppCodebasePrincipal(uri5, 1000, true);
+
+  let uri0_2000_n = secMan.getAppCodebasePrincipal(uri0, 2000, false);
+  let uri1_2000_n = secMan.getAppCodebasePrincipal(uri1, 2000, false);
+  let uri2_2000_n = secMan.getAppCodebasePrincipal(uri2, 2000, false);
+  let uri3_2000_n = secMan.getAppCodebasePrincipal(uri3, 2000, false);
+  let uri4_2000_n = secMan.getAppCodebasePrincipal(uri4, 2000, false);
+  let uri5_2000_n = secMan.getAppCodebasePrincipal(uri5, 2000, false);
+
+  let uri0_2000_y = secMan.getAppCodebasePrincipal(uri0, 2000, true);
+  let uri1_2000_y = secMan.getAppCodebasePrincipal(uri1, 2000, true);
+  let uri2_2000_y = secMan.getAppCodebasePrincipal(uri2, 2000, true);
+  let uri3_2000_y = secMan.getAppCodebasePrincipal(uri3, 2000, true);
+  let uri4_2000_y = secMan.getAppCodebasePrincipal(uri4, 2000, true);
+  let uri5_2000_y = secMan.getAppCodebasePrincipal(uri5, 2000, true);
+
+  pm.addFromPrincipal(uri0_n_n, "test/matches", pm.ALLOW_ACTION);
+  let perm_n_n = pm.getPermissionObject(uri0_n_n, "test/matches", true);
+  pm.addFromPrincipal(uri0_1000_n, "test/matches", pm.ALLOW_ACTION);
+  let perm_1000_n = pm.getPermissionObject(uri0_1000_n, "test/matches", true);
+  pm.addFromPrincipal(uri0_1000_y, "test/matches", pm.ALLOW_ACTION);
+  let perm_1000_y = pm.getPermissionObject(uri0_1000_y, "test/matches", true);
+  pm.addFromPrincipal(uri0_2000_n, "test/matches", pm.ALLOW_ACTION);
+  let perm_2000_n = pm.getPermissionObject(uri0_2000_n, "test/matches", true);
+  pm.addFromPrincipal(uri0_2000_y, "test/matches", pm.ALLOW_ACTION);
+  let perm_2000_y = pm.getPermissionObject(uri0_2000_y, "test/matches", true);
+
+  matches_always(perm_n_n, [uri0_n_n, uri3_n_n, uri5_n_n]);
+  matches_weak(perm_n_n, [uri1_n_n, uri4_n_n]);
+  matches_never(perm_n_n, [uri2_n_n,
+                           uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
+                           uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
+                           uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
+                           uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y]);
+
+  matches_always(perm_1000_n, [uri0_1000_n, uri3_1000_n, uri5_1000_n]);
+  matches_weak(perm_1000_n, [uri1_1000_n, uri4_1000_n]);
+  matches_never(perm_1000_n, [uri2_1000_n,
+                              uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
+                              uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
+                              uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
+                              uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y]);
+
+  matches_always(perm_1000_y, [uri0_1000_y, uri3_1000_y, uri5_1000_y]);
+  matches_weak(perm_1000_y, [uri1_1000_y, uri4_1000_y]);
+  matches_never(perm_1000_y, [uri2_1000_y,
+                              uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
+                              uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
+                              uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
+                              uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y]);
+
+  matches_always(perm_2000_n, [uri0_2000_n, uri3_2000_n, uri5_2000_n]);
+  matches_weak(perm_2000_n, [uri1_2000_n, uri4_2000_n]);
+  matches_never(perm_2000_n, [uri2_2000_n,
+                              uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
+                              uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
+                              uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
+                              uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y]);
+
+  matches_always(perm_2000_y, [uri0_2000_y, uri3_2000_y, uri5_2000_y]);
+  matches_weak(perm_2000_y, [uri1_2000_y, uri4_2000_y]);
+  matches_never(perm_2000_y, [uri2_2000_y,
+                              uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
+                              uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
+                              uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
+                              uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y]);
+
+  // Clean up!
+  pm.removeAll();
+}
diff --git a/extensions/cookie/test/unit/test_permmanager_matchesuri.js b/extensions/cookie/test/unit/test_permmanager_matchesuri.js
new file mode 100644
index 000000000000..aed8ea529119
--- /dev/null
+++ b/extensions/cookie/test/unit/test_permmanager_matchesuri.js
@@ -0,0 +1,150 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function matches_always(perm, uris) {
+  uris.forEach((uri) => {
+    do_check_true(perm.matchesURI(uri, true), "perm: " + perm.principal.origin + ", URI: " + uri.spec);
+    do_check_true(perm.matchesURI(uri, false), "perm: " + perm.principal.origin + ", URI: " + uri.spec);
+  });
+}
+
+function matches_weak(perm, uris) {
+  uris.forEach((uri) => {
+    do_check_false(perm.matchesURI(uri, true), "perm: " + perm.principal.origin + ", URI: " + uri.spec);
+    do_check_true(perm.matchesURI(uri, false), "perm: " + perm.principal.origin + ", URI: " + uri.spec);
+  });
+}
+
+function matches_never(perm, uris) {
+  uris.forEach((uri) => {
+    do_check_false(perm.matchesURI(uri, true), "perm: " + perm.principal.origin + ", URI: " + uri.spec);
+    do_check_false(perm.matchesURI(uri, false), "perm: " + perm.principal.origin + ", URI: " + uri.spec);
+  });
+}
+
+function mk_permission(uri, isAppPermission = false) {
+  let pm = Cc["@mozilla.org/permissionmanager;1"].
+        getService(Ci.nsIPermissionManager);
+
+  let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
+        .getService(Ci.nsIScriptSecurityManager);
+
+  // Get the permission from the principal!
+  let principal = isAppPermission ?
+        secMan.getAppCodebasePrincipal(uri, 1000, false) :
+        secMan.getNoAppCodebasePrincipal(uri);
+
+  pm.addFromPrincipal(principal, "test/matchesuri", pm.ALLOW_ACTION);
+  let permission = pm.getPermissionObject(principal, "test/matchesuri", true);
+
+  return permission;
+}
+
+function run_test() {
+  // initialize the permission manager service
+  let pm = Cc["@mozilla.org/permissionmanager;1"].
+        getService(Ci.nsIPermissionManager);
+
+  let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
+        .getService(Ci.nsIScriptSecurityManager);
+
+  let fileprefix = "file:///";
+  if (Services.appinfo.OS == "WINNT") {
+    // Windows rejects files if they don't have a drive. See Bug 1180870
+    fileprefix += "c:/";
+  }
+
+  // Add some permissions
+  let uri0 = NetUtil.newURI("http://google.com:9091/just/a/path", null, null);
+  let uri1 = NetUtil.newURI("http://hangouts.google.com:9091/some/path", null, null);
+  let uri2 = NetUtil.newURI("http://google.com:9091/", null, null);
+  let uri3 = NetUtil.newURI("http://google.org:9091/", null, null);
+  let uri4 = NetUtil.newURI("http://deeper.hangouts.google.com:9091/", null, null);
+  let uri5 = NetUtil.newURI("https://google.com/just/a/path", null, null);
+  let uri6 = NetUtil.newURI("https://hangouts.google.com", null, null);
+  let uri7 = NetUtil.newURI("https://google.com/", null, null);
+
+  let fileuri1 = NetUtil.newURI(fileprefix + "a/file/path", null, null);
+  let fileuri2 = NetUtil.newURI(fileprefix + "a/file/path/deeper", null, null);
+  let fileuri3 = NetUtil.newURI(fileprefix + "a/file/otherpath", null, null);
+
+  {
+    let perm = mk_permission(uri0);
+    matches_always(perm, [uri0, uri2, uri5, uri7]);
+    matches_weak(perm, [uri1, uri4, uri6]);
+    matches_never(perm, [uri3, fileuri1, fileuri2, fileuri3]);
+  }
+
+  {
+    let perm = mk_permission(uri1);
+    matches_always(perm, [uri1, uri6]);
+    matches_weak(perm, [uri4]);
+    matches_never(perm, [uri0, uri2, uri3, uri5, uri7, fileuri1, fileuri2, fileuri3]);
+  }
+
+  {
+    let perm = mk_permission(uri2);
+    matches_always(perm, [uri0, uri2, uri5, uri7]);
+    matches_weak(perm, [uri1, uri4, uri6]);
+    matches_never(perm, [uri3, fileuri1, fileuri2, fileuri3]);
+  }
+
+  {
+    let perm = mk_permission(uri3);
+    matches_always(perm, [uri3]);
+    matches_weak(perm, []);
+    matches_never(perm, [uri1, uri2, uri4, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]);
+  }
+
+  {
+    let perm = mk_permission(uri4);
+    matches_always(perm, [uri4]);
+    matches_weak(perm, []);
+    matches_never(perm, [uri1, uri2, uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]);
+  }
+
+  {
+    let perm = mk_permission(uri5);
+    matches_always(perm, [uri0, uri2, uri5, uri7]);
+    matches_weak(perm, [uri1, uri4, uri6]);
+    matches_never(perm, [uri3, fileuri1, fileuri2, fileuri3]);
+  }
+
+  {
+    let perm = mk_permission(uri6);
+    matches_always(perm, [uri1, uri6]);
+    matches_weak(perm, [uri4]);
+    matches_never(perm, [uri0, uri2, uri3, uri5, uri7, fileuri1, fileuri2, fileuri3]);
+  }
+
+  {
+    let perm = mk_permission(uri7);
+    matches_always(perm, [uri0, uri2, uri5, uri7]);
+    matches_weak(perm, [uri1, uri4, uri6]);
+    matches_never(perm, [uri3, fileuri1, fileuri2, fileuri3]);
+  }
+
+  {
+    let perm = mk_permission(fileuri1);
+    matches_always(perm, [fileuri1]);
+    matches_weak(perm, []);
+    matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri6, uri7, fileuri2, fileuri3]);
+  }
+
+  {
+    let perm = mk_permission(fileuri2);
+    matches_always(perm, [fileuri2]);
+    matches_weak(perm, []);
+    matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri6, uri7, fileuri1, fileuri3]);
+  }
+
+  {
+    let perm = mk_permission(fileuri3);
+    matches_always(perm, [fileuri3]);
+    matches_weak(perm, []);
+    matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri6, uri7, fileuri1, fileuri2]);
+  }
+
+  // Clean up!
+  pm.removeAll();
+}
diff --git a/extensions/cookie/test/unit/xpcshell.ini b/extensions/cookie/test/unit/xpcshell.ini
index 902d90a992a9..efd41f7be07e 100644
--- a/extensions/cookie/test/unit/xpcshell.ini
+++ b/extensions/cookie/test/unit/xpcshell.ini
@@ -35,3 +35,5 @@ skip-if = debug == true
 [test_schema_2_migration.js]
 [test_schema_3_migration.js]
 [test_permmanager_removepermission.js]
+[test_permmanager_matchesuri.js]
+[test_permmanager_matches.js]

From 9e4b616796ce03675f2ac73b01d2d0cbf9d9dd9a Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Thu, 4 Jun 2015 10:56:40 -0400
Subject: [PATCH 13/67] Bug 1173523 - Part 3: Update PluginContent to use new
 API for nsIPermission, r=mconley

---
 browser/base/content/browser-plugins.js | 23 ++++++++---------
 browser/base/content/urlbarBindings.xml | 33 ++++++++++++++-----------
 browser/modules/PluginContent.jsm       | 21 ++--------------
 3 files changed, 32 insertions(+), 45 deletions(-)

diff --git a/browser/base/content/browser-plugins.js b/browser/base/content/browser-plugins.js
index dfad5d8965c9..e17cf0f098e8 100644
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -44,14 +44,14 @@ var gPluginHandler = {
     switch (msg.name) {
       case "PluginContent:ShowClickToPlayNotification":
         this.showClickToPlayNotification(msg.target, msg.data.plugins, msg.data.showNow,
-                                         msg.principal, msg.data.host, msg.data.location);
+                                         msg.principal, msg.data.location);
         break;
       case "PluginContent:RemoveNotification":
         this.removeNotification(msg.target, msg.data.name);
         break;
       case "PluginContent:UpdateHiddenPluginUI":
         this.updateHiddenPluginUI(msg.target, msg.data.haveInsecure, msg.data.actions,
-                                  msg.principal, msg.data.host, msg.data.location);
+                                  msg.principal, msg.data.location);
         break;
       case "PluginContent:HideNotificationBar":
         this.hideNotificationBar(msg.target, msg.data.name);
@@ -216,8 +216,8 @@ var gPluginHandler = {
     });
   },
 
-  showClickToPlayNotification: function (browser, plugins, showNow, principal,
-                                         host, location) {
+  showClickToPlayNotification: function (browser, plugins, showNow,
+                                         principal, location) {
     // It is possible that we've received a message from the frame script to show
     // a click to play notification for a principal that no longer matches the one
     // that the browser's content now has assigned (ie, the browser has browsed away
@@ -295,7 +295,6 @@ var gPluginHandler = {
       primaryPlugin: primaryPluginPermission,
       pluginData: pluginData,
       principal: principal,
-      host: host,
     };
     PopupNotifications.show(browser, "click-to-play-plugins",
                             "", "plugins-notification-icon",
@@ -316,8 +315,10 @@ var gPluginHandler = {
       notificationBox.removeNotification(notification, true);
   },
 
-  updateHiddenPluginUI: function (browser, haveInsecure, actions, principal,
-                                  host, location) {
+  updateHiddenPluginUI: function (browser, haveInsecure, actions,
+                                  principal, location) {
+    let origin = principal.originNoSuffix;
+
     // It is possible that we've received a message from the frame script to show
     // the hidden plugin notification for a principal that no longer matches the one
     // that the browser's content now has assigned (ie, the browser has browsed away
@@ -380,22 +381,22 @@ var gPluginHandler = {
           case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
             message = gNavigatorBundle.getFormattedString(
               "pluginActivateNew.message",
-              [pluginName, host]);
+              [pluginName, origin]);
             break;
           case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
             message = gNavigatorBundle.getFormattedString(
               "pluginActivateOutdated.message",
-              [pluginName, host, brand]);
+              [pluginName, origin, brand]);
             break;
           case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
             message = gNavigatorBundle.getFormattedString(
               "pluginActivateVulnerable.message",
-              [pluginName, host, brand]);
+              [pluginName, origin, brand]);
         }
       } else {
         // Multi-plugin
         message = gNavigatorBundle.getFormattedString(
-          "pluginActivateMultiple.message", [host]);
+          "pluginActivateMultiple.message", [origin]);
       }
 
       let buttons = [
diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml
index be7463f4f093..d212b31d04c6 100644
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -2432,9 +2432,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
           let normalizedUrl = Services.io.newURI(
             "https://" + gBrowser.selectedBrowser.currentURI.hostPort,
             null, null);
-          // Add the current host in the 'trackingprotection' consumer of
+          // Add the current host/port combination in the 'trackingprotection' consumer of
           // the permission manager using a normalized URI. This effectively
-          // places this host on the tracking protection allowlist.
+          // places this host/port combination on the tracking protection allowlist.
           Services.perms.add(normalizedUrl,
             "trackingprotection", Services.perms.ALLOW_ACTION);
           BrowserReload();
@@ -2442,10 +2442,13 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
       
       
         
@@ -2561,8 +2564,8 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
             return;
           }
 
-          let host = this.notification.options.host;
-          this._setupDescription("pluginActivateMultiple.message", null, host);
+          let prePath = this.notification.options.principal.URI.prePath;
+          this._setupDescription("pluginActivateMultiple.message", null, prePath);
 
           var showBox = document.getAnonymousElementByAttribute(this, "anonid", "plugin-notification-showbox");
 
@@ -2601,7 +2604,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
       
         
         
          
-        
+        
         
       
diff --git a/browser/modules/PluginContent.jsm b/browser/modules/PluginContent.jsm
index 5d968dab5dd6..a188013a11f5 100644
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -695,20 +695,6 @@ PluginContent.prototype = {
     this._showClickToPlayNotification(null, false);
   },
 
-  // Match the behaviour of nsPermissionManager
-  _getHostFromPrincipal: function (principal) {
-    if (!principal.URI || principal.URI.schemeIs("moz-nullprincipal")) {
-      return "(null)";
-    }
-
-    try {
-      if (principal.URI.host)
-        return principal.URI.host;
-    } catch (e) {}
-
-    return principal.origin;
-  },
-
   /**
    * Activate the plugins that the user has specified.
    */
@@ -776,7 +762,6 @@ PluginContent.prototype = {
     let pluginData = this.pluginData;
 
     let principal = this.content.document.nodePrincipal;
-    let principalHost = this._getHostFromPrincipal(principal);
     let location = this.content.document.location.href;
 
     for (let p of plugins) {
@@ -792,11 +777,11 @@ PluginContent.prototype = {
       let permissionObj = Services.perms.
         getPermissionObject(principal, pluginInfo.permissionString, false);
       if (permissionObj) {
-        pluginInfo.pluginPermissionHost = permissionObj.host;
+        pluginInfo.pluginPermissionPrePath = permissionObj.principal.originNoSuffix;
         pluginInfo.pluginPermissionType = permissionObj.expireType;
       }
       else {
-        pluginInfo.pluginPermissionHost = principalHost;
+        pluginInfo.pluginPermissionPrePath = principal.originNoSuffix;
         pluginInfo.pluginPermissionType = undefined;
       }
 
@@ -806,7 +791,6 @@ PluginContent.prototype = {
     this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", {
       plugins: [... this.pluginData.values()],
       showNow: showNow,
-      host: principalHost,
       location: location,
     }, null, principal);
   },
@@ -889,7 +873,6 @@ PluginContent.prototype = {
     this.global.sendAsyncMessage("PluginContent:UpdateHiddenPluginUI", {
       haveInsecure: haveInsecure,
       actions: [... actions.values()],
-      host: this._getHostFromPrincipal(principal),
       location: location,
     }, null, principal);
   },

From 1af5d66c77ed6638d58d78d4849d5afeeb115ebf Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 2 Jun 2015 11:24:42 -0400
Subject: [PATCH 14/67] Bug 1173523 - Part 4: Update about:permissions to use
 new API for nsIPermission, r=margaret

---
 .../preferences/aboutPermissions.js           | 132 +++++++++---------
 .../tests/browser_chunk_permissions.js        |  16 +--
 .../preferences/tests/browser_permissions.js  |  14 +-
 3 files changed, 82 insertions(+), 80 deletions(-)

diff --git a/browser/components/preferences/aboutPermissions.js b/browser/components/preferences/aboutPermissions.js
index 50175e4493da..9b0bcce885ad 100644
--- a/browser/components/preferences/aboutPermissions.js
+++ b/browser/components/preferences/aboutPermissions.js
@@ -17,6 +17,9 @@ Cu.import("resource://gre/modules/ForgetAboutSite.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
 
+let gSecMan = Cc["@mozilla.org/scriptsecuritymanager;1"].
+              getService(Ci.nsIScriptSecurityManager);
+
 let gFaviconService = Cc["@mozilla.org/browser/favicon-service;1"].
                       getService(Ci.nsIFaviconService);
 
@@ -26,7 +29,7 @@ let gPlacesDatabase = Cc["@mozilla.org/browser/nav-history-service;1"].
                       clone(true);
 
 let gSitesStmt = gPlacesDatabase.createAsyncStatement(
-                  "SELECT get_unreversed_host(rev_host) AS host " +
+                  "SELECT url " +
                   "FROM moz_places " +
                   "WHERE rev_host > '.' " +
                   "AND visit_count > 0 " +
@@ -46,14 +49,11 @@ let gVisitStmt = gPlacesDatabase.createAsyncStatement(
 let TEST_EXACT_PERM_TYPES = ["geo", "camera", "microphone"];
 
 /**
- * Site object represents a single site, uniquely identified by a host.
+ * Site object represents a single site, uniquely identified by a principal.
  */
-function Site(host) {
-  this.host = host;
+function Site(principal) {
+  this.principal = principal;
   this.listitem = null;
-
-  this.httpURI = NetUtil.newURI("http://" + this.host);
-  this.httpsURI = NetUtil.newURI("https://" + this.host);
 }
 
 Site.prototype = {
@@ -75,16 +75,10 @@ Site.prototype = {
       }
     }
 
-    // Try to find favicon for both URIs, but always prefer the https favicon.
-    gFaviconService.getFaviconURLForPage(this.httpsURI, function (aURI) {
+    // Get the favicon for the origin
+    gFaviconService.getFaviconURLForPage(this.principal.URI, function (aURI) {
       if (aURI) {
         invokeCallback(aURI);
-      } else {
-        gFaviconService.getFaviconURLForPage(this.httpURI, function (aURI) {
-          if (aURI) {
-            invokeCallback(aURI);
-          }
-        });
       }
     }.bind(this));
   },
@@ -96,7 +90,9 @@ Site.prototype = {
    *        A function that takes the visit count (a number) as a parameter.
    */
   getVisitCount: function Site_getVisitCount(aCallback) {
-    let rev_host = this.host.split("").reverse().join("") + ".";
+    // XXX This won't be a very reliable system, as it will count both http: and https: visits
+    // Unfortunately, I don't think that there is a much better way to do it right now.
+    let rev_host = this.principal.URI.host.split("").reverse().join("") + ".";
     gVisitStmt.params.rev_host = rev_host;
     gVisitStmt.executeAsync({
       handleResult: function(aResults) {
@@ -139,9 +135,9 @@ Site.prototype = {
 
     let permissionValue;
     if (TEST_EXACT_PERM_TYPES.indexOf(aType) == -1) {
-      permissionValue = Services.perms.testPermission(this.httpURI, aType);
+      permissionValue = Services.perms.testPermissionFromPrincipal(this.principal, aType);
     } else {
-      permissionValue = Services.perms.testExactPermission(this.httpURI, aType);
+      permissionValue = Services.perms.testExactPermissionFromPrincipal(this.principal, aType);
     }
     aResultObj.value = permissionValue;
 
@@ -166,9 +162,7 @@ Site.prototype = {
       return;
     }
 
-    // Using httpURI is kind of bogus, but the permission manager stores the
-    // permission for the host, so the right thing happens in the end.
-    Services.perms.add(this.httpURI, aType, aPerm);
+    Services.perms.addFromPrincipal(this.principal, aType, aPerm);
   },
 
   /**
@@ -179,7 +173,7 @@ Site.prototype = {
    *        e.g. "cookie", "geo", "indexedDB", "popup", "image"
    */
   clearPermission: function Site_clearPermission(aType) {
-    Services.perms.remove(this.httpURI, aType);
+    Services.perms.removeFromPrincipal(this.principal, aType);
   },
 
   /**
@@ -189,13 +183,14 @@ Site.prototype = {
    * @return An array of the cookies set for the site.
    */
   get cookies() {
+    let host = this.principal.URI.host;
     let cookies = [];
-    let enumerator = Services.cookies.getCookiesFromHost(this.host);
+    let enumerator = Services.cookies.getCookiesFromHost(host);
     while (enumerator.hasMoreElements()) {
       let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
       // getCookiesFromHost returns cookies for base domain, but we only want
       // the cookies for the exact domain.
-      if (cookie.rawHost == this.host) {
+      if (cookie.rawHost == host) {
         cookies.push(cookie);
       }
     }
@@ -217,27 +212,27 @@ Site.prototype = {
    * @return An array of the logins stored for the site.
    */
   get logins() {
-    let httpLogins = Services.logins.findLogins({}, this.httpURI.prePath, "", "");
-    let httpsLogins = Services.logins.findLogins({}, this.httpsURI.prePath, "", "");
-    return httpLogins.concat(httpsLogins);
+    let logins = Services.logins.findLogins({}, this.principal.originNoSuffix, "", "");
+    return logins;
   },
 
   get loginSavingEnabled() {
-    // Only say that login saving is blocked if it is blocked for both http and https.
-    return Services.logins.getLoginSavingEnabled(this.httpURI.prePath) &&
-           Services.logins.getLoginSavingEnabled(this.httpsURI.prePath);
+    return Services.logins.getLoginSavingEnabled(this.principal.originNoSuffix);
   },
 
   set loginSavingEnabled(isEnabled) {
-    Services.logins.setLoginSavingEnabled(this.httpURI.prePath, isEnabled);
-    Services.logins.setLoginSavingEnabled(this.httpsURI.prePath, isEnabled);
+    Services.logins.setLoginSavingEnabled(this.principal.originNoSuffix, isEnabled);
   },
 
   /**
    * Removes all data from the browser corresponding to the site.
    */
   forgetSite: function Site_forgetSite() {
-    ForgetAboutSite.removeDataFromDomain(this.host);
+    // XXX This removes data for an entire domain, rather than just
+    // an origin. This may produce confusing results, as data will
+    // be cleared for the http:// as well as the https:// domain
+    // if you try to forget the https:// site.
+    ForgetAboutSite.removeDataFromDomain(this.principal.URI.host);
   }
 }
 
@@ -366,7 +361,7 @@ let AboutPermissions = {
   LIST_BUILD_DELAY: 100, // delay between intervals
 
   /**
-   * Stores a mapping of host strings to Site objects.
+   * Stores a mapping of origin strings to Site objects.
    */
   _sites: {},
 
@@ -472,9 +467,9 @@ let AboutPermissions = {
           break;
         }
         let permission = aSubject.QueryInterface(Ci.nsIPermission);
-        // We can't compare selectedSite.host and permission.host here because
-        // we need to handle the case where a parent domain was changed in a
-        // way that affects the subdomain.
+        // We can't compare selectedSite.principal and permission.principal here
+        // because we need to handle the case where a parent domain was changed
+        // in a way that affects the subdomain.
         if (this._supportedPermissions.indexOf(permission.type) != -1) {
           this.updatePermission(permission.type);
         }
@@ -512,8 +507,11 @@ let AboutPermissions = {
         AboutPermissions.startSitesListBatch();
         let row;
         while (row = aResults.getNextRow()) {
-          let host = row.getResultByName("host");
-          AboutPermissions.addHost(host);
+          let spec = row.getResultByName("url");
+          let uri = NetUtil.newURI(spec);
+          let principal = gSecMan.getNoAppCodebasePrincipal(uri);
+
+          AboutPermissions.addPrincipal(principal);
         }
         AboutPermissions.endSitesListBatch();
       },
@@ -562,7 +560,8 @@ let AboutPermissions = {
       try {
         // aLogin.hostname is a string in origin URL format (e.g. "http://foo.com")
         let uri = NetUtil.newURI(aLogin.hostname);
-        this.addHost(uri.host);
+        let principal = gSecMan.getNoAppCodebasePrincipal(uri);
+        this.addPrincipal(principal);
       } catch (e) {
         // newURI will throw for add-ons logins stored in chrome:// URIs
       }
@@ -577,7 +576,8 @@ let AboutPermissions = {
       try {
         // aHostname is a string in origin URL format (e.g. "http://foo.com")
         let uri = NetUtil.newURI(aHostname);
-        this.addHost(uri.host);
+        let principal = gSecMan.getNoAppCodebasePrincipal(uri);
+        this.addPrincipal(principal);
       } catch (e) {
         // newURI will throw for add-ons logins stored in chrome:// URIs
       }
@@ -592,7 +592,7 @@ let AboutPermissions = {
       let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
       // Only include sites with exceptions set for supported permission types.
       if (this._supportedPermissions.indexOf(permission.type) != -1) {
-        this.addHost(permission.host);
+        this.addPrincipal(permission.principal);
       }
       itemCnt++;
     }
@@ -603,15 +603,15 @@ let AboutPermissions = {
   /**
    * Creates a new Site and adds it to _sites if it's not already there.
    *
-   * @param aHost
-   *        A host string.
+   * @param aPrincipal
+   *        A principal.
    */
-  addHost: function(aHost) {
-    if (aHost in this._sites) {
+  addPrincipal: function(aPrincipal) {
+    if (aPrincipal.origin in this._sites) {
       return;
     }
-    let site = new Site(aHost);
-    this._sites[aHost] = site;
+    let site = new Site(aPrincipal);
+    this._sites[aPrincipal.origin] = site;
     this.addToSitesList(site);
   },
 
@@ -624,7 +624,7 @@ let AboutPermissions = {
   addToSitesList: function(aSite) {
     let item = document.createElement("richlistitem");
     item.setAttribute("class", "site");
-    item.setAttribute("value", aSite.host);
+    item.setAttribute("value", aSite.principal.origin);
 
     aSite.getFavicon(function(aURL) {
       item.setAttribute("favicon", aURL);
@@ -633,7 +633,7 @@ let AboutPermissions = {
 
     // Make sure to only display relevant items when list is filtered
     let filterValue = document.getElementById("sites-filter").value.toLowerCase();
-    item.collapsed = aSite.host.toLowerCase().indexOf(filterValue) == -1;
+    item.collapsed = aSite.principal.origin.toLowerCase().indexOf(filterValue) == -1;
 
     (this._listFragment || this.sitesList).appendChild(item);
   },
@@ -686,16 +686,16 @@ let AboutPermissions = {
    *        The host string corresponding to the site to delete.
    */
   deleteFromSitesList: function(aHost) {
-    for (let host in this._sites) {
-      let site = this._sites[host];
-      if (site.host.hasRootDomain(aHost)) {
+    for (let origin in this._sites) {
+      let site = this._sites[origin];
+      if (site.principal.URI.host.hasRootDomain(aHost)) {
         if (site == this._selectedSite) {
           // Replace site-specific interface with "All Sites" interface.
           this.sitesList.selectedItem = document.getElementById("all-sites-item");
         }
 
         this.sitesList.removeChild(site.listitem);
-        delete this._sites[site.host];
+        delete this._sites[site.principal.origin];
       }
     }
   },
@@ -711,9 +711,9 @@ let AboutPermissions = {
       return;
     }
 
-    let host = event.target.value;
-    let site = this._selectedSite = this._sites[host];
-    document.getElementById("site-label").value = host;
+    let origin = event.target.value;
+    let site = this._selectedSite = this._sites[origin];
+    document.getElementById("site-label").value = origin;
     document.getElementById("header-deck").selectedPanel =
       document.getElementById("site-header");
 
@@ -768,9 +768,9 @@ let AboutPermissions = {
       // If there is no selected site, we are updating the default permissions interface.
       permissionValue = PermissionDefaults[aType];
       if (aType == "cookie")
-	// cookie-9 corresponds to ALLOW_FIRST_PARTY_ONLY, which is reserved
-	// for site-specific preferences only.
-	document.getElementById("cookie-9").hidden = true;
+	      // cookie-9 corresponds to ALLOW_FIRST_PARTY_ONLY, which is reserved
+	      // for site-specific preferences only.
+	      document.getElementById("cookie-9").hidden = true;
     } else {
       if (aType == "cookie")
         document.getElementById("cookie-9").hidden = false;
@@ -825,18 +825,18 @@ let AboutPermissions = {
    * Opens password manager dialog.
    */
   managePasswords: function() {
-    let selectedHost = "";
+    let selectedOrigin = "";
     if (this._selectedSite) {
-      selectedHost = this._selectedSite.host;
+      selectedOrigin = this._selectedSite.principal.URI.prePath;
     }
 
     let win = Services.wm.getMostRecentWindow("Toolkit:PasswordManager");
     if (win) {
-      win.setFilter(selectedHost);
+      win.setFilter(selectedOrigin);
       win.focus();
     } else {
       window.openDialog("chrome://passwordmgr/content/passwordManager.xul",
-                        "Toolkit:PasswordManager", "", {filterString : selectedHost});
+                        "Toolkit:PasswordManager", "", {filterString : selectedOrigin});
     }
   },
 
@@ -877,9 +877,11 @@ let AboutPermissions = {
    * Opens cookie manager dialog.
    */
   manageCookies: function() {
+    // Cookies are stored by-host, and thus we filter the cookie window
+    // using only the host of the selected principal's origin
     let selectedHost = "";
     if (this._selectedSite) {
-      selectedHost = this._selectedSite.host;
+      selectedHost = this._selectedSite.principal.URI.host;
     }
 
     let win = Services.wm.getMostRecentWindow("Browser:Cookies");
diff --git a/browser/components/preferences/tests/browser_chunk_permissions.js b/browser/components/preferences/tests/browser_chunk_permissions.js
index cf88dd329118..4c53c6c7fe20 100644
--- a/browser/components/preferences/tests/browser_chunk_permissions.js
+++ b/browser/components/preferences/tests/browser_chunk_permissions.js
@@ -103,11 +103,11 @@ var tests = [
       sitesFilter.doCommand();
     },
     run: function() {
-      let testSite1 = getSiteItem(TEST_URI_1.host);
+      let testSite1 = getSiteItem(TEST_URI_1.prePath);
       ok(testSite1.collapsed, "test site 1 is collapsed after early filtering");
-      let testSite2 = getSiteItem(TEST_URI_2.host);
+      let testSite2 = getSiteItem(TEST_URI_2.prePath);
       ok(!testSite2.collapsed, "test site 2 is not collapsed after early filtering");
-      let testSite3 = getSiteItem(TEST_URI_3.host);
+      let testSite3 = getSiteItem(TEST_URI_3.prePath);
       ok(testSite3.collapsed, "test site 3 is collapsed after early filtering");
 
       runNextTest();
@@ -119,11 +119,11 @@ var tests = [
       ForgetAboutSite.removeDataFromDomain(TEST_URI_2.host);
     },
     run: function() {
-      let testSite1 = getSiteItem(TEST_URI_1.host);
+      let testSite1 = getSiteItem(TEST_URI_1.prePath);
       ok(testSite1, "test site 1 was not removed from sites list");
-      let testSite2 = getSiteItem(TEST_URI_2.host);
+      let testSite2 = getSiteItem(TEST_URI_2.prePath);
       ok(!testSite2, "test site 2 was pre-removed from sites list");
-      let testSite3 = getSiteItem(TEST_URI_3.host);
+      let testSite3 = getSiteItem(TEST_URI_3.prePath);
       ok(testSite3, "test site 3 was not removed from sites list");
 
       runNextTest();
@@ -131,7 +131,7 @@ var tests = [
   }
 ];
 
-function getSiteItem(aHost) {
+function getSiteItem(aPrePath) {
   return gBrowser.contentDocument.
-                  querySelector(".site[value='" + aHost + "']");
+                  querySelector(".site[value='" + aPrePath + "']");
 }
diff --git a/browser/components/preferences/tests/browser_permissions.js b/browser/components/preferences/tests/browser_permissions.js
index 2a896b3e0dae..84d91e030247 100644
--- a/browser/components/preferences/tests/browser_permissions.js
+++ b/browser/components/preferences/tests/browser_permissions.js
@@ -115,8 +115,8 @@ var tests = [
     is(gSitesList.firstChild.id, "all-sites-item",
        "all sites is the first item in the sites list");
 
-    ok(getSiteItem(TEST_URI_1.host), "site item from places db exists");
-    ok(getSiteItem(TEST_URI_2.host), "site item from enumerating services exists");
+    ok(getSiteItem(TEST_URI_1.prePath), "site item from places db exists");
+    ok(getSiteItem(TEST_URI_2.prePath), "site item from enumerating services exists");
 
     runNextTest();
   },
@@ -128,9 +128,9 @@ var tests = [
     sitesFilter.doCommand();
 
     // make sure correct sites are collapsed/showing
-    let testSite1 = getSiteItem(TEST_URI_1.host);
+    let testSite1 = getSiteItem(TEST_URI_1.prePath);
     ok(!testSite1.collapsed, "test site 1 is not collapsed");
-    let testSite2 = getSiteItem(TEST_URI_2.host);
+    let testSite2 = getSiteItem(TEST_URI_2.prePath);
     ok(testSite2.collapsed, "test site 2 is collapsed");
 
     // clear filter
@@ -202,13 +202,13 @@ var tests = [
 
   function test_select_site() {
     // select the site that has the permissions we set at the beginning of the test
-    let testSiteItem = getSiteItem(TEST_URI_2.host);
+    let testSiteItem = getSiteItem(TEST_URI_2.prePath);
     gSitesList.selectedItem = testSiteItem;
 
     let siteHeader = gBrowser.contentDocument.getElementById("site-header");
     is(siteHeader, gHeaderDeck.selectedPanel,
        "correct header shown for a specific site");
-    is(gSiteLabel.value, TEST_URI_2.host, "header updated for selected site");
+    is(gSiteLabel.value, TEST_URI_2.prePath, "header updated for selected site");
 
     ok(!gBrowser.contentDocument.getElementById("passwords-count").hidden,
        "passwords count is not hidden");
@@ -283,7 +283,7 @@ var tests = [
          "all sites item selected after forgetting selected site");
 
       // check to make sure site is gone from sites list
-      let testSiteItem = getSiteItem(TEST_URI_2.host);
+      let testSiteItem = getSiteItem(TEST_URI_2.prePath);
       ok(!testSiteItem, "site removed from sites list");
 
       // check to make sure we forgot all permissions corresponding to site

From 15518c66d39d0de9b387724803749bb91c8c9e39 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 2 Jun 2015 11:42:39 -0400
Subject: [PATCH 15/67] Bug 1173523 - Part 5: Update the preferences
 permissions UI to use new API for nsIPermission, r=ehsan

---
 browser/components/preferences/permissions.js | 70 ++++++++++---------
 1 file changed, 37 insertions(+), 33 deletions(-)

diff --git a/browser/components/preferences/permissions.js b/browser/components/preferences/permissions.js
index ec55dfb432dd..8a531573f63d 100644
--- a/browser/components/preferences/permissions.js
+++ b/browser/components/preferences/permissions.js
@@ -9,10 +9,10 @@ const nsICookiePermission = Components.interfaces.nsICookiePermission;
 
 const NOTIFICATION_FLUSH_PERMISSIONS = "flush-pending-permissions";
 
-function Permission(host, rawHost, type, capability)
+function Permission(principal, type, capability)
 {
-  this.host = host;
-  this.rawHost = rawHost;
+  this.principal = principal;
+  this.origin = principal.origin;
   this.type = type;
   this.capability = capability;
 }
@@ -35,7 +35,7 @@ var gPermissionManager = {
     getCellText: function (aRow, aColumn)
     {
       if (aColumn.id == "siteCol")
-        return gPermissionManager._permissions[aRow].rawHost;
+        return gPermissionManager._permissions[aRow].origin;
       else if (aColumn.id == "statusCol")
         return gPermissionManager._permissions[aRow].capability;
       return "";
@@ -82,10 +82,17 @@ var gPermissionManager = {
   addPermission: function (aCapability)
   {
     var textbox = document.getElementById("url");
-    var host = textbox.value.replace(/^\s*([-\w]*:\/+)?/, ""); // trim any leading space and scheme
+    var input_url = textbox.value.replace(/^\s*/, ""); // trim any leading space
+    let principal;
     try {
-      var uri = Services.io.newURI("http://"+host, null, null);
-      host = uri.host;
+      // If the uri doesn't successfully parse, try adding a http:// and parsing again
+      let uri;
+      try {
+        let uri = Services.io.newURI(input_url, null, null);
+      } catch(ex) {
+        uri = Services.io.newURI("http://" + input_url, null, null);
+      }
+      principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
     } catch(ex) {
       var message = this._bundle.getString("invalidURI");
       var title = this._bundle.getString("invalidURITitle");
@@ -96,11 +103,11 @@ var gPermissionManager = {
     var capabilityString = this._getCapabilityString(aCapability);
 
     // check whether the permission already exists, if not, add it
-    let hostExists = false;
+    let permissionExists = false;
     let capabilityExists = false;
     for (var i = 0; i < this._permissions.length; ++i) {
-      if (this._permissions[i].rawHost == host) {
-        hostExists = true;
+      if (this._permissions[i].principal.equals(principal)) {
+        permissionExists = true;
         capabilityExists = this._permissions[i].capability == capabilityString;
         if (!capabilityExists) {
           this._permissions[i].capability = capabilityString;
@@ -109,14 +116,14 @@ var gPermissionManager = {
       }
     }
 
-    let permissionParams = {host: host, type: this._type, capability: aCapability};
-    if (!hostExists) {
-      this._permissionsToAdd.set(host, permissionParams);
+    let permissionParams = {principal: principal, type: this._type, capability: aCapability};
+    if (!permissionExists) {
+      this._permissionsToAdd.set(principal.origin, permissionParams);
       this._addPermission(permissionParams);
     }
     else if (!capabilityExists) {
-        this._permissionsToAdd.set(host, permissionParams);
-        this._handleCapabilityChange();
+      this._permissionsToAdd.set(principal.origin, permissionParams);
+      this._handleCapabilityChange();
     }
 
     textbox.value = "";
@@ -131,15 +138,15 @@ var gPermissionManager = {
 
   _removePermission: function(aPermission)
   {
-    this._removePermissionFromList(aPermission.host);
+    this._removePermissionFromList(aPermission.principal);
 
     // If this permission was added during this session, let's remove
     // it from the pending adds list to prevent calls to the
     // permission manager.
-    let isNewPermission = this._permissionsToAdd.delete(aPermission.host);
+    let isNewPermission = this._permissionsToAdd.delete(aPermission.principal.origin);
 
     if (!isNewPermission) {
-      this._permissionsToDelete.set(aPermission.host, aPermission);
+      this._permissionsToDelete.set(aPermission.principal.origin, aPermission);
     }
 
   },
@@ -276,7 +283,7 @@ var gPermissionManager = {
       }
       else if (aData == "changed") {
         for (var i = 0; i < this._permissions.length; ++i) {
-          if (this._permissions[i].host == permission.host) {
+          if (permission.matches(this._permissions[i].principal, true)) {
             this._permissions[i].capability = this._getCapabilityString(permission.capability);
             break;
           }
@@ -363,13 +370,11 @@ var gPermissionManager = {
     this.uninit();
 
     for (let permissionParams of this._permissionsToAdd.values()) {
-      let uri = Services.io.newURI("http://" + permissionParams.host, null, null);
-      Services.perms.add(uri, permissionParams.type, permissionParams.capability);
+      Services.perms.addFromPrincipal(permissionParams.principal, permissionParams.type, permissionParams.capability);
     }
 
     for (let p of this._permissionsToDelete.values()) {
-      let uri = Services.io.newURI("http://" + p.host, null, null);
-      Services.perms.remove(uri, p.type);
+      Services.perms.removeFromPrincipal(p.principal, p.type);
     }
 
     window.close();
@@ -392,7 +397,7 @@ var gPermissionManager = {
 
     // sort and display the table
     this._tree.view = this._view;
-    this.onPermissionSort("rawHost");
+    this.onPermissionSort("origin");
 
     // disable "remove all" button if there are none
     document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
@@ -404,20 +409,19 @@ var gPermissionManager = {
         (!this._manageCapability ||
          (aPermission.capability == this._manageCapability))) {
 
-      var host = aPermission.host;
+      var principal = aPermission.principal;
       var capabilityString = this._getCapabilityString(aPermission.capability);
-      var p = new Permission(host,
-                             (host.charAt(0) == ".") ? host.substring(1,host.length) : host,
+      var p = new Permission(principal,
                              aPermission.type,
                              capabilityString);
       this._permissions.push(p);
     }
   },
 
-  _removePermissionFromList: function (aHost)
+  _removePermissionFromList: function (aPrincipal)
   {
     for (let i = 0; i < this._permissions.length; ++i) {
-      if (this._permissions[i].host == aHost) {
+      if (this._permissions[i].principal.equals(aPrincipal)) {
         this._permissions.splice(i, 1);
         this._view._rowCount--;
         this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1);
@@ -427,15 +431,15 @@ var gPermissionManager = {
     }
   },
 
-  setHost: function (aHost)
+  setOrigin: function (aOrigin)
   {
-    document.getElementById("url").value = aHost;
+    document.getElementById("url").value = aOrigin;
   }
 };
 
-function setHost(aHost)
+function setOrigin(aOrigin)
 {
-  gPermissionManager.setHost(aHost);
+  gPermissionManager.setOrigin(aOrigin);
 }
 
 function initWithParams(aParams)

From d5135fc62c54c3f44a02240c9b98abf5b6678e48 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 2 Jun 2015 11:48:52 -0400
Subject: [PATCH 16/67] Bug 1173523 - Part 6: Update translation to use new API
 for nsIPermission, r=florian

---
 browser/components/preferences/translation.js | 19 ++++++++++---------
 .../test/browser_translation_exceptions.js    |  4 ++--
 2 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/browser/components/preferences/translation.js b/browser/components/preferences/translation.js
index a1ae8988b1b4..4f353c53e2e6 100644
--- a/browser/components/preferences/translation.js
+++ b/browser/components/preferences/translation.js
@@ -9,6 +9,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/BrowserUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gLangBundle", () =>
   Services.strings.createBundle("chrome://global/locale/languageNames.properties"));
@@ -83,7 +84,7 @@ let gTranslationExceptions = {
 
       if (perm.type == kPermissionType &&
           perm.capability == Services.perms.DENY_ACTION) {
-        this._sites.push(perm.host);
+        this._sites.push(perm.principal.origin);
       }
     }
     Services.obs.addObserver(this, "perm-changed", false);
@@ -126,14 +127,14 @@ let gTranslationExceptions = {
         if (aData == "added") {
           if (perm.capability != Services.perms.DENY_ACTION)
             return;
-          this._sites.push(perm.host);
+          this._sites.push(perm.principal.origin);
           this._sites.sort();
           let boxObject = this._siteTree.boxObject;
           boxObject.rowCountChanged(0, 1);
           boxObject.invalidate();
         }
         else if (aData == "deleted") {
-          let index = this._sites.indexOf(perm.host);
+          let index = this._sites.indexOf(perm.principal.origin);
           if (index == -1)
             return;
           this._sites.splice(index, 1);
@@ -188,9 +189,9 @@ let gTranslationExceptions = {
 
   onSiteDeleted: function() {
     let removedSites = this._siteTree.getSelectedItems();
-    for (let host of removedSites) {
-      let uri = Services.io.newURI("http://" + host, null, null);
-      Services.perms.remove(uri, kPermissionType);
+    for (let origin of removedSites) {
+      let principal = BrowserUtils.principalFromOrigin(origin);
+      Services.perms.removeFromPrincipal(principal, kPermissionType);
     }
   },
 
@@ -201,9 +202,9 @@ let gTranslationExceptions = {
     let removedSites = this._sites.splice(0, this._sites.length);
     this._siteTree.boxObject.rowCountChanged(0, -removedSites.length);
 
-    for (let host of removedSites) {
-      let uri = Services.io.newURI("http://" + host, null, null);
-      Services.perms.remove(uri, kPermissionType);
+    for (let origin of removedSites) {
+      let principal = BrowserUtils.principalFromOrigin(origin);
+      Services.perms.removeFromPrincipal(principal, kPermissionType);
     }
 
     this.onSiteSelected();
diff --git a/browser/components/translation/test/browser_translation_exceptions.js b/browser/components/translation/test/browser_translation_exceptions.js
index 25a4b113dff6..086614bfd293 100644
--- a/browser/components/translation/test/browser_translation_exceptions.js
+++ b/browser/components/translation/test/browser_translation_exceptions.js
@@ -51,7 +51,7 @@ function getDomainExceptions() {
 
     if (perm.type == "translate" &&
         perm.capability == Services.perms.DENY_ACTION)
-      results.push(perm.host);
+      results.push(perm.principal);
   }
 
   return results;
@@ -181,7 +181,7 @@ let gTests = [
     // Check this has been saved to the exceptions list.
     let sites = getDomainExceptions();
     is(sites.length, 1, "one site in the exception list");
-    is(sites[0], "example.com", "correct site in the exception list");
+    is(sites[0].origin, "http://example.com", "correct site in the exception list");
     ok(!ui.shouldShowInfoBar(uri, "fr"),
        "the infobar wouldn't be shown anymore");
 

From 15c7779360b8d892e9b28ddf71a57944d05d08ce Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 2 Jun 2015 13:15:10 -0400
Subject: [PATCH 17/67] Bug 1173523 - Part 7: Update advanced.js to use new API
 for nsIPermission, r=jaws

---
 browser/components/preferences/advanced.js    | 32 ++++++++-----------
 .../preferences/in-content/advanced.js        | 31 ++++++++----------
 2 files changed, 27 insertions(+), 36 deletions(-)

diff --git a/browser/components/preferences/advanced.js b/browser/components/preferences/advanced.js
index f2fd2cb23b2a..a1210971da37 100644
--- a/browser/components/preferences/advanced.js
+++ b/browser/components/preferences/advanced.js
@@ -9,6 +9,7 @@ Components.utils.import("resource://gre/modules/ctypes.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/LoadContextInfo.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
 
 var gAdvancedPane = {
   _inited: false,
@@ -471,7 +472,7 @@ var gAdvancedPane = {
     var usage = 0;
     for (var i = 0; i < groups.length; i++) {
       var uri = ios.newURI(groups[i], null, null);
-      if (uri.asciiHost == perm.host) {
+      if (perm.matchesURI(uri, true)) {
         var cache = cacheService.getActiveCache(groups[i]);
         usage += cache.usage;
       }
@@ -508,7 +509,7 @@ var gAdvancedPane = {
         var row = document.createElement("listitem");
         row.id = "";
         row.className = "offlineapp";
-        row.setAttribute("host", perm.host);
+        row.setAttribute("origin", perm.principal.origin);
         var converted = DownloadUtils.
                         convertByteUnits(this._getOfflineAppUsage(perm, groups));
         row.setAttribute("usage",
@@ -534,7 +535,8 @@ var gAdvancedPane = {
   {
     var list = document.getElementById("offlineAppsList");
     var item = list.selectedItem;
-    var host = item.getAttribute("host");
+    var origin = item.getAttribute("origin");
+    var principal = BrowserUtils.principalFromOrigin(origin);
 
     var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                             .getService(Components.interfaces.nsIPromptService);
@@ -543,13 +545,18 @@ var gAdvancedPane = {
 
     var bundle = document.getElementById("bundlePreferences");
     var title = bundle.getString("offlineAppRemoveTitle");
-    var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [host]);
+    var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [principal.URI.prePath]);
     var confirm = bundle.getString("offlineAppRemoveConfirm");
     var result = prompts.confirmEx(window, title, prompt, flags, confirm,
                                    null, null, null, {});
     if (result != 0)
       return;
 
+    // get the permission
+    var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                       .getService(Components.interfaces.nsIPermissionManager);
+    var perm = pm.getPermissionObject(principal, "offline-app");
+
     // clear offline cache entries
     var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
                        getService(Components.interfaces.nsIApplicationCacheService);
@@ -557,25 +564,14 @@ var gAdvancedPane = {
               getService(Components.interfaces.nsIIOService);
     var groups = cacheService.getGroups();
     for (var i = 0; i < groups.length; i++) {
-        let uri = ios.newURI(groups[i], null, null);
-        if (uri.asciiHost == host) {
+        var uri = ios.newURI(groups[i], null, null);
+        if (perm.matchesURI(uri, true)) {
             var cache = cacheService.getActiveCache(groups[i]);
             cache.discard();
         }
     }
 
-    // remove the permission
-    var pm = Components.classes["@mozilla.org/permissionmanager;1"]
-                       .getService(Components.interfaces.nsIPermissionManager);
-    let uri;
-    try {
-      // file:// URIs are stored with their scheme. We try to parse them first, as
-      // URIs like http://file:///foo/bar/baz.html will parse as HTTP URIs.
-      uri = ios.newURI(host, null, null);
-    } catch (e) {
-      uri = ios.newURI("http://" + host, null, null);
-    }
-    pm.remove(uri, "offline-app");
+    pm.removePermission(perm);
 
     list.removeChild(item);
     gAdvancedPane.offlineAppSelected();
diff --git a/browser/components/preferences/in-content/advanced.js b/browser/components/preferences/in-content/advanced.js
index a8f65a9b7f3a..629b167762f9 100644
--- a/browser/components/preferences/in-content/advanced.js
+++ b/browser/components/preferences/in-content/advanced.js
@@ -502,7 +502,7 @@ var gAdvancedPane = {
     var usage = 0;
     for (var i = 0; i < groups.length; i++) {
       var uri = ios.newURI(groups[i], null, null);
-      if (uri.asciiHost == perm.host) {
+      if (perm.matchesURI(uri, true)) {
         var cache = cacheService.getActiveCache(groups[i]);
         usage += cache.usage;
       }
@@ -544,7 +544,7 @@ var gAdvancedPane = {
         var row = document.createElement("listitem");
         row.id = "";
         row.className = "offlineapp";
-        row.setAttribute("host", perm.host);
+        row.setAttribute("origin", perm.principal.origin);
         var converted = DownloadUtils.
                         convertByteUnits(this._getOfflineAppUsage(perm, groups));
         row.setAttribute("usage",
@@ -570,7 +570,8 @@ var gAdvancedPane = {
   {
     var list = document.getElementById("offlineAppsList");
     var item = list.selectedItem;
-    var host = item.getAttribute("host");
+    var origin = item.getAttribute("origin");
+    var principal = BrowserUtils.principalFromOrigin(origin);
 
     var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                             .getService(Components.interfaces.nsIPromptService);
@@ -579,13 +580,18 @@ var gAdvancedPane = {
 
     var bundle = document.getElementById("bundlePreferences");
     var title = bundle.getString("offlineAppRemoveTitle");
-    var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [host]);
+    var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [principal.URI.prePath]);
     var confirm = bundle.getString("offlineAppRemoveConfirm");
     var result = prompts.confirmEx(window, title, prompt, flags, confirm,
                                    null, null, null, {});
     if (result != 0)
       return;
 
+    // get the permission
+    var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                       .getService(Components.interfaces.nsIPermissionManager);
+    var perm = pm.getPermissionObject(principal, "offline-app");
+
     // clear offline cache entries
     try {
       var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"].
@@ -594,26 +600,15 @@ var gAdvancedPane = {
                 getService(Components.interfaces.nsIIOService);
       var groups = cacheService.getGroups();
       for (var i = 0; i < groups.length; i++) {
-          let uri = ios.newURI(groups[i], null, null);
-          if (uri.asciiHost == host) {
+          var uri = ios.newURI(groups[i], null, null);
+          if (perm.matchesURI(uri, true)) {
               var cache = cacheService.getActiveCache(groups[i]);
               cache.discard();
           }
       }
     } catch (e) {}
 
-    // remove the permission
-    var pm = Components.classes["@mozilla.org/permissionmanager;1"]
-                       .getService(Components.interfaces.nsIPermissionManager);
-    let uri;
-    try {
-      // file:// URIs are stored with their scheme. We try to parse them first, as
-      // URIs like http://file:///foo/bar/baz.html will parse as HTTP URIs.
-      uri = ios.newURI(host, null, null);
-    } catch (e) {
-      uri = ios.newURI("http://" + host, null, null);
-    }
-    pm.remove(uri, "offline-app");
+    pm.removePermission(perm);
 
     list.removeChild(item);
     gAdvancedPane.offlineAppSelected();

From bb03fc827ca19f6b940f77043ced22267ce9bb43 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 2 Jun 2015 13:15:45 -0400
Subject: [PATCH 18/67] Bug 1173523 - Part 8: Update ForgetAboutSite.jsm to use
 new API for nsIPermission, r=ehsan

---
 toolkit/forgetaboutsite/ForgetAboutSite.jsm | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/toolkit/forgetaboutsite/ForgetAboutSite.jsm b/toolkit/forgetaboutsite/ForgetAboutSite.jsm
index 9e9e65298d90..ad9f5a46370a 100644
--- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm
+++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm
@@ -140,8 +140,13 @@ this.ForgetAboutSite = {
     enumerator = pm.enumerator;
     while (enumerator.hasMoreElements()) {
       let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
-      if (hasRootDomain(perm.host, aDomain))
-        pm.removePermission(perm);
+      try {
+        if (hasRootDomain(perm.principal.URI.host, aDomain)) {
+          pm.removePermission(perm);
+        }
+      } catch (e) {
+        /* Ignore entry */
+      }
     }
 
     // Offline Storages

From 903877487368c31c6f54f361f059bd28f0753248 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 2 Jun 2015 15:15:25 -0400
Subject: [PATCH 19/67] Bug 1173523 - Part 9: Update pageinfo to use the new
 API for nsIPermission, r=florian

---
 browser/base/content/pageinfo/pageInfo.js    | 3 ++-
 browser/base/content/pageinfo/permissions.js | 4 ++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/browser/base/content/pageinfo/pageInfo.js b/browser/base/content/pageinfo/pageInfo.js
index c7b67c423c08..d216346c2930 100644
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -1117,8 +1117,9 @@ var imagePermissionObserver = {
         var row = getSelectedRow(imageTree);
         var item = gImageView.data[row][COL_IMAGE_NODE];
         var url = gImageView.data[row][COL_IMAGE_ADDRESS];
-        if (makeURI(url).host == permission.host)
+        if (permission.matchesURI(makeURI(url), true)) {
           makeBlockImage(url);
+        }
       }
     }
   }
diff --git a/browser/base/content/pageinfo/permissions.js b/browser/base/content/pageinfo/permissions.js
index a67ecd07b660..eaf5824151d4 100644
--- a/browser/base/content/pageinfo/permissions.js
+++ b/browser/base/content/pageinfo/permissions.js
@@ -18,7 +18,7 @@ var permissionObserver = {
   {
     if (aTopic == "perm-changed") {
       var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission);
-      if (permission.host == gPermURI.host) {
+      if (permission.matchesURI(gPermURI, true)) {
         if (gPermissions.indexOf(permission.type) > -1)
           initRow(permission.type);
         else if (permission.type.startsWith("plugin"))
@@ -35,7 +35,7 @@ function onLoadPermission()
   if (SitePermissions.isSupportedURI(uri)) {
     gPermURI = uri;
     var hostText = document.getElementById("hostText");
-    hostText.value = gPermURI.host;
+    hostText.value = gPermURI.prePath;
 
     for (var i of gPermissions)
       initRow(i);

From 18a0810f4e2284d020f8098a1f2d474e1ae57104 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Thu, 4 Jun 2015 11:00:25 -0400
Subject: [PATCH 20/67] Bug 1173523 - Part 10: Update nsIPermission and
 nsIPermissionManager tests to use new API for nsIPermission, r=ehsan

---
 .../test/test_app_uninstall_permissions.html  |  2 +-
 .../test/unit/test_permmanager_defaults.js    | 22 ++++----
 .../test_permmanager_getPermissionObject.js   | 18 +++----
 .../test/unit/test_permmanager_mailto.js      | 13 ++---
 netwerk/test/unit/test_permmgr.js             | 51 +++++++++----------
 5 files changed, 51 insertions(+), 55 deletions(-)

diff --git a/extensions/cookie/test/test_app_uninstall_permissions.html b/extensions/cookie/test/test_app_uninstall_permissions.html
index afb1f1d46c49..13cf9f7380ba 100644
--- a/extensions/cookie/test/test_app_uninstall_permissions.html
+++ b/extensions/cookie/test/test_app_uninstall_permissions.html
@@ -42,7 +42,7 @@ function getPermissionCountForApp(aAppId) {
   while (enumerator.hasMoreElements()) {
     var permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
 
-    if (permission.appId == aAppId || aAppId == -1) {
+    if (permission.principal.appId == aAppId || aAppId == -1) {
       nbPermissions++;
     }
   }
diff --git a/extensions/cookie/test/unit/test_permmanager_defaults.js b/extensions/cookie/test/unit/test_permmanager_defaults.js
index 7798542edec7..47e18c4fcc51 100644
--- a/extensions/cookie/test/unit/test_permmanager_defaults.js
+++ b/extensions/cookie/test/unit/test_permmanager_defaults.js
@@ -2,8 +2,8 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // The origin we use in most of the tests.
-const TEST_ORIGIN = "example.org";
-const TEST_ORIGIN_2 = "example.com";
+const TEST_ORIGIN = NetUtil.newURI("http://example.org");
+const TEST_ORIGIN_2 = NetUtil.newURI("http://example.com");
 const TEST_PERMISSION = "test-permission";
 Components.utils.import("resource://gre/modules/Promise.jsm");
 
@@ -35,8 +35,8 @@ add_task(function* do_test() {
 
   conv.writeString("# this is a comment\n");
   conv.writeString("\n"); // a blank line!
-  conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN + "\n");
-  conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2 + "\n");
+  conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.spec + "\n");
+  conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2.spec + "\n");
   ostream.close();
 
   // Set the preference used by the permission manager so the file is read.
@@ -47,8 +47,7 @@ add_task(function* do_test() {
            getService(Ci.nsIPermissionManager);
 
   // test the default permission was applied.
-  let permURI = NetUtil.newURI("http://" + TEST_ORIGIN);
-  let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI);
+  let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_ORIGIN);
 
   do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
               pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
@@ -105,8 +104,7 @@ add_task(function* do_test() {
   // check default permissions and removeAllSince work as expected.
   pm.removeAll(); // ensure only defaults are there.
 
-  let permURI2 = NetUtil.newURI("http://" + TEST_ORIGIN_2);
-  let principal2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI2);
+  let principal2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_ORIGIN_2);
 
   // default for both principals is allow.
   do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
@@ -149,12 +147,12 @@ add_task(function* do_test() {
 // use an enumerator to find the requested permission.  Returns the permission
 // value (ie, the "capability" in nsIPermission parlance) or null if it can't
 // be found.
-function findCapabilityViaEnum(host = TEST_ORIGIN, type = TEST_PERMISSION) {
+function findCapabilityViaEnum(origin = TEST_ORIGIN, type = TEST_PERMISSION) {
   let result = undefined;
   let e = Services.perms.enumerator;
   while (e.hasMoreElements()) {
     let perm = e.getNext().QueryInterface(Ci.nsIPermission);
-    if (perm.host == host &&
+    if (perm.matchesURI(origin, true) &&
         perm.type == type) {
       if (result !== undefined) {
         // we've already found one previously - that's bad!
@@ -171,12 +169,12 @@ function findCapabilityViaEnum(host = TEST_ORIGIN, type = TEST_PERMISSION) {
 // distinct possibility exists that our checking of the DB will happen before
 // the permission manager update has completed - so we just retry a few times.
 // Returns a promise.
-function checkCapabilityViaDB(expected, host = TEST_ORIGIN, type = TEST_PERMISSION) {
+function checkCapabilityViaDB(expected, origin = TEST_ORIGIN, type = TEST_PERMISSION) {
   let deferred = Promise.defer();
   let count = 0;
   let max = 20;
   let do_check = () => {
-    let got = findCapabilityViaDB(host, type);
+    let got = findCapabilityViaDB(origin.host, type);
     if (got == expected) {
       // the do_check_eq() below will succeed - which is what we want.
       do_check_eq(got, expected, "The database has the expected value");
diff --git a/extensions/cookie/test/unit/test_permmanager_getPermissionObject.js b/extensions/cookie/test/unit/test_permmanager_getPermissionObject.js
index 9ab4ff21f0f2..f2f13f38e3a0 100644
--- a/extensions/cookie/test/unit/test_permmanager_getPermissionObject.js
+++ b/extensions/cookie/test/unit/test_permmanager_getPermissionObject.js
@@ -29,20 +29,20 @@ function run_test() {
   pm.addFromPrincipal(principal, "test/pobject", pm.ALLOW_ACTION);
   var rootPerm = pm.getPermissionObject(principal, "test/pobject", false);
   do_check_true(rootPerm != null);
-  do_check_eq(rootPerm.host, "example.com");
+  do_check_eq(rootPerm.principal.origin, "http://example.com");
   do_check_eq(rootPerm.type, "test/pobject");
   do_check_eq(rootPerm.capability, pm.ALLOW_ACTION);
   do_check_eq(rootPerm.expireType, pm.EXPIRE_NEVER);
 
   var rootPerm2 = pm.getPermissionObject(principal, "test/pobject", true);
   do_check_true(rootPerm != null);
-  do_check_eq(rootPerm.host, "example.com");
+  do_check_eq(rootPerm.principal.origin, "http://example.com");
 
   var subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", true);
   do_check_null(subPerm);
   subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false);
   do_check_true(subPerm != null);
-  do_check_eq(subPerm.host, "example.com");
+  do_check_eq(subPerm.principal.origin, "http://example.com");
   do_check_eq(subPerm.type, "test/pobject");
   do_check_eq(subPerm.capability, pm.ALLOW_ACTION);
 
@@ -50,7 +50,7 @@ function run_test() {
   do_check_null(subPerm);
   subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", false);
   do_check_true(subPerm != null);
-  do_check_eq(subPerm.host, "example.com");
+  do_check_eq(subPerm.principal.origin, "http://example.com");
 
   pm.addFromPrincipal(principal, "test/pobject", pm.DENY_ACTION, pm.EXPIRE_SESSION);
 
@@ -63,28 +63,28 @@ function run_test() {
   do_check_eq(rootPerm.expireType, pm.EXPIRE_SESSION);
 
   subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false);
-  do_check_eq(subPerm.host, "example.com");
+  do_check_eq(subPerm.principal.origin, "http://example.com");
   do_check_eq(subPerm.capability, pm.DENY_ACTION);
   do_check_eq(subPerm.expireType, pm.EXPIRE_SESSION);
 
   pm.addFromPrincipal(subPrincipal, "test/pobject", pm.PROMPT_ACTION);
   rootPerm = pm.getPermissionObject(principal, "test/pobject", true);
-  do_check_eq(rootPerm.host, "example.com");
+  do_check_eq(rootPerm.principal.origin, "http://example.com");
   do_check_eq(rootPerm.capability, pm.DENY_ACTION);
 
   subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", true);
-  do_check_eq(subPerm.host, "sub.example.com");
+  do_check_eq(subPerm.principal.origin, "http://sub.example.com");
   do_check_eq(subPerm.capability, pm.PROMPT_ACTION);
 
   subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false);
-  do_check_eq(subPerm.host, "sub.example.com");
+  do_check_eq(subPerm.principal.origin, "http://sub.example.com");
   do_check_eq(subPerm.capability, pm.PROMPT_ACTION);
 
   subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", true);
   do_check_null(subPerm);
 
   subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", false);
-  do_check_eq(subPerm.host, "sub.example.com");
+  do_check_eq(subPerm.principal.origin, "http://sub.example.com");
   do_check_eq(subPerm.capability, pm.PROMPT_ACTION);
 
   pm.removeFromPrincipal(principal, "test/pobject");
diff --git a/extensions/cookie/test/unit/test_permmanager_mailto.js b/extensions/cookie/test/unit/test_permmanager_mailto.js
index 21b1f6c60a28..1c111295bec7 100644
--- a/extensions/cookie/test/unit/test_permmanager_mailto.js
+++ b/extensions/cookie/test/unit/test_permmanager_mailto.js
@@ -10,28 +10,29 @@ function run_test() {
   // make a mailto: URI with parameters
   let uri = Services.io.newURI("mailto:" + kTestAddr + "?subject=test", null,
                                null);
+  let origin = "mailto:" + kTestAddr;
 
   // add a permission entry for that URI
   Services.perms.add(uri, kType, kCapability);
-  do_check_true(permission_exists(kTestAddr, kType, kCapability));
+  do_check_true(permission_exists(origin, kType, kCapability));
 
   // remove the permission, and make sure it was removed
   Services.perms.remove(uri, kType);
-  do_check_false(permission_exists(kTestAddr, kType, kCapability));
+  do_check_false(permission_exists(origin, kType, kCapability));
 
   uri = Services.io.newURI("mailto:" + kTestAddr, null, null);
   Services.perms.add(uri, kType, kCapability);
-  do_check_true(permission_exists(kTestAddr, kType, kCapability));
+  do_check_true(permission_exists(origin, kType, kCapability));
 
   Services.perms.remove(uri, kType);
-  do_check_false(permission_exists(kTestAddr, kType, kCapability));
+  do_check_false(permission_exists(origin, kType, kCapability));
 }
 
-function permission_exists(aHost, aType, aCapability) {
+function permission_exists(aOrigin, aType, aCapability) {
   let e = Services.perms.enumerator;
   while (e.hasMoreElements()) {
     let perm = e.getNext().QueryInterface(Ci.nsIPermission);
-    if (perm.host == aHost &&
+    if (perm.principal.origin == aOrigin &&
         perm.type == aType &&
         perm.capability == aCapability) {
       return true;
diff --git a/netwerk/test/unit/test_permmgr.js b/netwerk/test/unit/test_permmgr.js
index f472770a5f7a..5a6ddd8b5a15 100644
--- a/netwerk/test/unit/test_permmgr.js
+++ b/netwerk/test/unit/test_permmgr.js
@@ -2,31 +2,31 @@
 
 var hosts = [
   // format: [host, type, permission]
-  ["mozilla.org", "cookie", 1],
-  ["mozilla.org", "image", 2],
-  ["mozilla.org", "popup", 3],
-  ["mozilla.com", "cookie", 1],
-  ["www.mozilla.com", "cookie", 2],
-  ["dev.mozilla.com", "cookie", 3]
+  ["http://mozilla.org", "cookie", 1],
+  ["http://mozilla.org", "image", 2],
+  ["http://mozilla.org", "popup", 3],
+  ["http://mozilla.com", "cookie", 1],
+  ["http://www.mozilla.com", "cookie", 2],
+  ["http://dev.mozilla.com", "cookie", 3]
 ];
 
 var results = [
   // format: [host, type, testPermission result, testExactPermission result]
   // test defaults
-  ["localhost", "cookie", 0, 0],
-  ["spreadfirefox.com", "cookie", 0, 0],
+  ["http://localhost", "cookie", 0, 0],
+  ["http://spreadfirefox.com", "cookie", 0, 0],
   // test different types
-  ["mozilla.org", "cookie", 1, 1],
-  ["mozilla.org", "image", 2, 2],
-  ["mozilla.org", "popup", 3, 3],
+  ["http://mozilla.org", "cookie", 1, 1],
+  ["http://mozilla.org", "image", 2, 2],
+  ["http://mozilla.org", "popup", 3, 3],
   // test subdomains
-  ["www.mozilla.org", "cookie", 1, 0],
-  ["www.dev.mozilla.org", "cookie", 1, 0],
+  ["http://www.mozilla.org", "cookie", 1, 0],
+  ["http://www.dev.mozilla.org", "cookie", 1, 0],
   // test different permissions on subdomains
-  ["mozilla.com", "cookie", 1, 1],
-  ["www.mozilla.com", "cookie", 2, 2],
-  ["dev.mozilla.com", "cookie", 3, 3],
-  ["www.dev.mozilla.com", "cookie", 3, 0]
+  ["http://mozilla.com", "cookie", 1, 1],
+  ["http://www.mozilla.com", "cookie", 2, 2],
+  ["http://dev.mozilla.com", "cookie", 3, 3],
+  ["http://www.dev.mozilla.com", "cookie", 3, 0]
 ];
 
 function run_test() {
@@ -45,16 +45,16 @@ function run_test() {
 
   // put a few hosts in
   for (var i = 0; i < hosts.length; ++i) {
-    var uri = ioService.newURI("http://" + hosts[i][0], null, null);
-    var principal = secMan.getNoAppCodebasePrincipal(uri);
+    let uri = ioService.newURI(hosts[i][0], null, null);
+    let principal = secMan.getNoAppCodebasePrincipal(uri);
 
     pm.addFromPrincipal(principal, hosts[i][1], hosts[i][2]);
   }
 
   // test the result
   for (var i = 0; i < results.length; ++i) {
-    var uri = ioService.newURI("http://" + results[i][0], null, null);
-    var principal = secMan.getNoAppCodebasePrincipal(uri);
+    let uri = ioService.newURI(results[i][0], null, null);
+    let principal = secMan.getNoAppCodebasePrincipal(uri);
 
     do_check_eq(pm.testPermissionFromPrincipal(principal, results[i][1]), results[i][2]);
     do_check_eq(pm.testExactPermissionFromPrincipal(principal, results[i][1]), results[i][3]);
@@ -72,16 +72,13 @@ function run_test() {
 
   // ... remove all the hosts ...
   for (var j = 0; j < perms.length; ++j) {
-    var uri = ioService.newURI("http://" + perms[j].host, null, null);
-    var principal = secMan.getNoAppCodebasePrincipal(uri);
-
-    pm.removeFromPrincipal(principal, perms[j].type);
+    pm.removePermission(perms[j]);
   }
   
   // ... ensure each and every element is equal ...
   for (var i = 0; i < hosts.length; ++i) {
     for (var j = 0; j < perms.length; ++j) {
-      if (hosts[i][0] == perms[j].host &&
+      if (perms[j].matchesURI(ioService.newURI(hosts[i][0], null, null), true) &&
           hosts[i][1] == perms[j].type &&
           hosts[i][2] == perms[j].capability) {
         perms.splice(j, 1);
@@ -102,7 +99,7 @@ function run_test() {
   var enumerator = pm.enumerator;
   do_check_eq(enumerator.hasMoreElements(), true);
   var ace = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission);
-  do_check_eq(ace.host, aceref);
+  do_check_eq(ace.principal.URI.asciiHost, aceref);
   do_check_eq(enumerator.hasMoreElements(), false);
 
   // test removeAll()

From a7301527ac35d654bf70bb9b2536a52ced1e96c4 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Thu, 4 Jun 2015 11:04:11 -0400
Subject: [PATCH 21/67] Bug 1173523 - Part 11: Update tests for cookie
 permissions to use new API for nsIPermission, r=ehsan

---
 .../tests/browser_cookies_exceptions.js         | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/browser/components/preferences/tests/browser_cookies_exceptions.js b/browser/components/preferences/tests/browser_cookies_exceptions.js
index 52798c43b019..92ed070b75c7 100644
--- a/browser/components/preferences/tests/browser_cookies_exceptions.js
+++ b/browser/components/preferences/tests/browser_cookies_exceptions.js
@@ -20,7 +20,7 @@ var testRunner = {
                                           "permission text should be set correctly");
           params.btnApplyChanges.doCommand();
         },
-        observances: [{ type: "cookie", host: "test.com", data: "added",
+        observances: [{ type: "cookie", origin: "http://test.com", data: "added",
                         capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
       },
       {
@@ -31,7 +31,7 @@ var testRunner = {
                                           "permission should change to deny in UI");
           params.btnApplyChanges.doCommand();
         },
-        observances: [{ type: "cookie", host: "test.com", data: "changed",
+        observances: [{ type: "cookie", origin: "http://test.com", data: "changed",
                         capability: Ci.nsIPermissionManager.DENY_ACTION  }],
       },
       {
@@ -42,7 +42,7 @@ var testRunner = {
                                           "permission should revert back to allow");
           params.btnApplyChanges.doCommand();
         },
-        observances: [{ type: "cookie", host: "test.com", data: "changed",
+        observances: [{ type: "cookie", origin: "http://test.com", data: "changed",
                         capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
       },
       {
@@ -52,7 +52,7 @@ var testRunner = {
           is(params.tree.view.rowCount, 0, "exception should be removed");
           params.btnApplyChanges.doCommand();
         },
-        observances: [{ type: "cookie", host: "test.com", data: "deleted" }],
+        observances: [{ type: "cookie", origin: "http://test.com", data: "deleted" }],
       },
       {
         test: function(params) {
@@ -61,7 +61,7 @@ var testRunner = {
           is(params.tree.view.rowCount, 0, "adding unrelated permission should not change display");
           params.btnApplyChanges.doCommand();
         },
-        observances: [{ type: "popup", host: "test.com", data: "added",
+        observances: [{ type: "popup", origin: "http://test.com", data: "added",
                         capability: Ci.nsIPermissionManager.DENY_ACTION }],
         cleanUp: function(params) {
           let uri = params.ioService.newURI("http://test.com", null, null);
@@ -160,12 +160,17 @@ var testRunner = {
               let expected = testRunner.tests[testRunner._currentTest].observances.shift();
 
               is(aData, expected.data, "type of message should be the same");
-              for each (let prop in ["type", "host", "capability"]) {
+              for each (let prop in ["type", "capability"]) {
                 if (expected[prop])
                   is(permission[prop], expected[prop],
                     "property: \"" + prop  + "\" should be equal");
               }
 
+              if (expected.origin) {
+                is(permission.principal.origin, expected.origin,
+                   "property: \"origin\" should be equal");
+              }
+
               os.removeObserver(permObserver, "perm-changed");
 
               if (testRunner.tests[testRunner._currentTest].cleanup) {

From 5588333939e35d2aa26025bdcda00f85e4171bd7 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Thu, 4 Jun 2015 11:10:02 -0400
Subject: [PATCH 22/67] Bug 1173523 - Part 12: Update tests for plugin
 permissions to use new API for nsIPermission, r=jimm

---
 browser/base/content/test/plugins/head.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/browser/base/content/test/plugins/head.js b/browser/base/content/test/plugins/head.js
index 4c5d8dd382ab..c1290ed19ebf 100644
--- a/browser/base/content/test/plugins/head.js
+++ b/browser/base/content/test/plugins/head.js
@@ -238,7 +238,7 @@ function clearAllPluginPermissions() {
   while (perms.hasMoreElements()) {
     let perm = perms.getNext();
     if (perm.type.startsWith('plugin')) {
-      info("removing permission:" + perm.host + " " + perm.type + "\n");
+      info("removing permission:" + perm.principal.origin + " " + perm.type + "\n");
       Services.perms.removePermission(perm);
     }
   }

From a214a150be3f7708d5f73e8031cb02c0ec19f4d3 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Wed, 10 Jun 2015 11:54:21 -0400
Subject: [PATCH 23/67] Bug 1173523 - Part 13: Update SpecialPowers to use new
 API for nsIPermission, r=jmaher

---
 .../specialpowers/components/SpecialPowersObserver.js | 11 ++++++++++-
 testing/specialpowers/content/specialpowersAPI.js     |  2 +-
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/testing/specialpowers/components/SpecialPowersObserver.js b/testing/specialpowers/components/SpecialPowersObserver.js
index 95f514455409..10703da4a622 100644
--- a/testing/specialpowers/components/SpecialPowersObserver.js
+++ b/testing/specialpowers/components/SpecialPowersObserver.js
@@ -217,7 +217,16 @@ SpecialPowersObserver.prototype = new SpecialPowersObserverAPI();
       switch (aTopic) {
         case "perm-changed":
           var permission = aSubject.QueryInterface(Ci.nsIPermission);
-          msg.permission = { appId: permission.appId, type: permission.type };
+
+          // specialPowersAPI will consume this value, and it is used as a
+          // fake permission, but only type and principal.appId will be used.
+          //
+          // We need to ensure that it looks the same as a real permission,
+          // so we fake these properties.
+          msg.permission = {
+            principal: { appId: permission.principal.appId },
+            type: permission.type
+          };
         default:
           this._self._sendAsyncMessage("specialpowers-" + aTopic, msg);
       }
diff --git a/testing/specialpowers/content/specialpowersAPI.js b/testing/specialpowers/content/specialpowersAPI.js
index 59cd60b415ee..6a01c635c292 100644
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -967,7 +967,7 @@ SpecialPowersAPI.prototype = {
           for (var j = 0; j < undos.length; j++) {
             var undo = undos[j];
             if (undo.op == this._obsDataMap[aData] &&
-                undo.appId == permission.appId &&
+                undo.appId == permission.principal.appId &&
                 undo.type == permission.type) {
               // Remove this undo item if it has been done by others(not
               // specialpowers itself.)

From 369527316f9e135f897c8e46047bd8406a230a7a Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 16 Jun 2015 15:52:57 -0400
Subject: [PATCH 24/67] Bug 817007 - Part 1: Remove  hack from the
 permission manager, r=ehsan

---
 extensions/cookie/nsPermissionManager.cpp           | 13 -------------
 .../test/unit/test_permmanager_local_files.js       |  6 ------
 2 files changed, 19 deletions(-)

diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp
index 72ae711641d5..986686c3baf0 100644
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -1438,19 +1438,6 @@ nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
     return entry;
   }
 
-  // If we haven't found an entry, depending on the host, we could try a bit
-  // harder.
-  // If this is a file:// URI, we can check for the presence of the magic entry
-  //  which gives permission to all file://. This hack might disappear,
-  // see bug 817007. Note that we don't require aExactHostMatch to be true for
-  // that to keep retro-compatibility.
-  // If this is not a file:// URI, and that aExactHostMatch wasn't true, we can
-  // check if the base domain has a permission entry.
-
-  if (StringBeginsWith(aHost, NS_LITERAL_CSTRING("file://"))) {
-    return GetPermissionHashKey(NS_LITERAL_CSTRING(""), aAppId, aIsInBrowserElement, aType, true);
-  }
-
   // If aExactHostMatch wasn't true, we can check if the base domain has a permission entry.
   if (!aExactHostMatch) {
     nsCString domain = GetNextSubDomainForHost(aHost);
diff --git a/extensions/cookie/test/unit/test_permmanager_local_files.js b/extensions/cookie/test/unit/test_permmanager_local_files.js
index 32d72dd70b86..67884cb642cd 100644
--- a/extensions/cookie/test/unit/test_permmanager_local_files.js
+++ b/extensions/cookie/test/unit/test_permmanager_local_files.js
@@ -39,10 +39,4 @@ function run_test() {
   do_check_eq(pm.testPermissionFromPrincipal(principal, "test/local-files"), pm.UNKNOWN_ACTION);
   do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.UNKNOWN_ACTION);
   do_check_eq(pm.testPermissionFromPrincipal(fileInDirPrincipal, "test/local-files"), pm.UNKNOWN_ACTION);
-
-  // Add the magic "" permission and make sure all "file://" now have the permission.
-  pm.addFromPrincipal(getPrincipalFromURIString("http://"), "test/local-files", pm.ALLOW_ACTION, 0, 0);
-  do_check_eq(pm.testPermissionFromPrincipal(principal, "test/local-files"), pm.ALLOW_ACTION);
-  do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.ALLOW_ACTION);
-  do_check_eq(pm.testPermissionFromPrincipal(fileInDirPrincipal, "test/local-files"), pm.ALLOW_ACTION);
 }

From d9e40725853d152725aa7095cd829fb59dc2ca2e Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Fri, 12 Jun 2015 13:08:54 -0400
Subject: [PATCH 25/67] Bug 817007 - Part 2: Update reftests due to removal of
  magic host, r=ehsan

---
 layout/tools/reftest/reftest-preferences.js | 3 +++
 layout/tools/reftest/runreftest.py          | 1 -
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/layout/tools/reftest/reftest-preferences.js b/layout/tools/reftest/reftest-preferences.js
index c1a429e629b2..8709d6d37ab3 100644
--- a/layout/tools/reftest/reftest-preferences.js
+++ b/layout/tools/reftest/reftest-preferences.js
@@ -65,3 +65,6 @@
 
     // Disable periodic updates of service workers.
     branch.setBoolPref("dom.serviceWorkers.periodic-updates.enabled", false);
+
+    // Allow XUL and XBL files to be opened from file:// URIs
+    branch.setBoolPref("dom.allow_XUL_XBL_for_file", true);
diff --git a/layout/tools/reftest/runreftest.py b/layout/tools/reftest/runreftest.py
index 09f7c4ad1f6c..752d0f41a3c8 100644
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -188,7 +188,6 @@ class RefTest(object):
 
         locations = mozprofile.permissions.ServerLocations()
         locations.add_host(server, port=0)
-        locations.add_host('', port=0)
 
         # Set preferences for communication between our command line arguments
         # and the reftest harness.  Preferences that are required for reftest

From c05bef0d229be8b315f83a26e5250ed3f3a6d1e4 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 16 Jun 2015 15:27:45 -0400
Subject: [PATCH 26/67] Bug 1165263 - Part 1: Update nsPermissionManager to use
 origins instead of hosts internally, r=ehsan

---
 browser/app/permissions                   |  18 +-
 extensions/cookie/nsPermission.cpp        |  67 +-
 extensions/cookie/nsPermissionManager.cpp | 760 ++++++++++++++--------
 extensions/cookie/nsPermissionManager.h   |  41 +-
 4 files changed, 557 insertions(+), 329 deletions(-)

diff --git a/browser/app/permissions b/browser/app/permissions
index bc3cddbbf436..cc37101dd377 100644
--- a/browser/app/permissions
+++ b/browser/app/permissions
@@ -1,21 +1,21 @@
 # This file has default permissions for the permission manager.
 # The file-format is strict:
 # * matchtype \t type \t permission \t host
-# * Only "host" is supported for matchtype
+# * "origin" should be used for matchtype, "host" is supported for legacy reasons
 # * type is a string that identifies the type of permission (e.g. "cookie")
 # * permission is an integer between 1 and 15
 # See nsPermissionManager.cpp for more...
 
 # UITour
-host	uitour	1	www.mozilla.org
-host	uitour	1	self-repair.mozilla.org
-host	uitour	1	support.mozilla.org
-host	uitour	1	about:home
+origin	uitour	1	https://www.mozilla.org
+origin	uitour	1	https://self-repair.mozilla.org
+origin	uitour	1	https://support.mozilla.org
+origin	uitour	1	about:home
 
 # XPInstall
-host	install	1	addons.mozilla.org
-host	install	1	marketplace.firefox.com
+origin	install	1	https://addons.mozilla.org
+origin	install	1	https://marketplace.firefox.com
 
 # Remote troubleshooting
-host	remote-troubleshooting	1	input.mozilla.org
-host	remote-troubleshooting	1	support.mozilla.org
+origin	remote-troubleshooting	1	https://input.mozilla.org
+origin	remote-troubleshooting	1	https://support.mozilla.org
diff --git a/extensions/cookie/nsPermission.cpp b/extensions/cookie/nsPermission.cpp
index 28046fd87147..9438b0d6643a 100644
--- a/extensions/cookie/nsPermission.cpp
+++ b/extensions/cookie/nsPermission.cpp
@@ -1,4 +1,4 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
@@ -7,6 +7,7 @@
 #include "nsContentUtils.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIEffectiveTLDService.h"
+#include "mozilla/BasePrincipal.h"
 
 // nsPermission Implementation
 
@@ -76,29 +77,55 @@ nsPermission::Matches(nsIPrincipal* aPrincipal, bool aExactHost, bool* aMatches)
     return NS_OK;
   }
 
-  // Make sure that the OriginAttributes of the two entries are the same
-  nsAutoCString theirSuffix;
-  nsresult rv = aPrincipal->GetOriginSuffix(theirSuffix);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoCString ourSuffix;
-  rv = mPrincipal->GetOriginSuffix(ourSuffix);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (theirSuffix != ourSuffix) {
-    return NS_OK;
+  // If we are matching with an exact host, we're done now - the permissions don't match
+  // otherwise, we need to start comparing subdomains!
+  if (aExactHost) {
+      return NS_OK;
+  }
+
+  // Compare their OriginAttributes
+  const mozilla::OriginAttributes& theirAttrs = mozilla::BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
+  const mozilla::OriginAttributes& ourAttrs = mozilla::BasePrincipal::Cast(mPrincipal)->OriginAttributesRef();
+
+  if (theirAttrs != ourAttrs) {
+      return NS_OK;
   }
 
-  // Right now, we only care about the hosts
   nsCOMPtr theirURI;
-  rv = aPrincipal->GetURI(getter_AddRefs(theirURI));
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(theirURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr ourURI;
   rv = mPrincipal->GetURI(getter_AddRefs(ourURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Get the hosts so we can compare them
+  // Compare schemes
+  nsAutoCString theirScheme;
+  rv = theirURI->GetScheme(theirScheme);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString ourScheme;
+  rv = ourURI->GetScheme(ourScheme);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (theirScheme != ourScheme) {
+    return NS_OK;
+  }
+
+  // Compare ports
+  int32_t theirPort;
+  rv = theirURI->GetPort(&theirPort);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  int32_t ourPort;
+  rv = ourURI->GetPort(&ourPort);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (theirPort != ourPort) {
+    return NS_OK;
+  }
+
+  // Check if the host or any subdomain of their host matches.
   nsAutoCString theirHost;
   rv = theirURI->GetHost(theirHost);
   if (NS_FAILED(rv) || theirHost.IsEmpty()) {
@@ -111,11 +138,6 @@ nsPermission::Matches(nsIPrincipal* aPrincipal, bool aExactHost, bool* aMatches)
     return NS_OK;
   }
 
-  if (aExactHost) { // If we only care about the exact host, we compare them and are done
-    *aMatches = theirHost == ourHost;
-    return NS_OK;
-  }
-
   nsCOMPtr tldService =
     do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
   if (!tldService) {
@@ -123,9 +145,8 @@ nsPermission::Matches(nsIPrincipal* aPrincipal, bool aExactHost, bool* aMatches)
     return NS_ERROR_FAILURE;
   }
 
-  // Check if the host or any subdomain of the host matches. This loop will
-  // not loop forever, as GetNextSubDomain will eventually fail with
-  // NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS.
+  // This loop will not loop forever, as GetNextSubDomain will eventually fail
+  // with NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS.
   while (theirHost != ourHost) {
     rv = tldService->GetNextSubDomain(theirHost, theirHost);
     if (NS_FAILED(rv)) {
diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp
index 986686c3baf0..ded7500c62de 100644
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -40,6 +40,8 @@
 #include "nsReadLine.h"
 #include "mozilla/Telemetry.h"
 #include "nsIConsoleService.h"
+#include "nsINavHistoryService.h"
+#include "nsToolkitCompsCID.h"
 
 static nsPermissionManager *gPermissionManager = nullptr;
 
@@ -102,29 +104,31 @@ LogToConsole(const nsAString& aMsg)
 namespace {
 
 nsresult
-GetPrincipal(const nsACString& aHost, uint32_t aAppId, bool aIsInBrowserElement,
-             nsIPrincipal** aPrincipal)
+GetPrincipalFromOrigin(const nsACString& aOrigin, nsIPrincipal** aPrincipal)
+{
+  nsAutoCString originNoSuffix;
+  mozilla::OriginAttributes attrs;
+  if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr uri;
+  nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+  principal.forget(aPrincipal);
+  return NS_OK;
+}
+
+
+nsresult
+GetPrincipal(nsIURI* aURI, uint32_t aAppId, bool aIsInBrowserElement, nsIPrincipal** aPrincipal)
 {
   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
 
-  nsCOMPtr uri;
-  nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost);
-  if (NS_FAILED(rv)) {
-    // NOTE: most callers will end up here because we don't append "http://" for
-    // hosts. It's fine to arbitrary use "http://" because, for those entries,
-    // we will actually just use the host. If we end up here, but the host looks
-    // like an email address, we use mailto: instead.
-    nsCString scheme;
-    if (aHost.FindChar('@') == -1)
-      scheme = NS_LITERAL_CSTRING("http://");
-    else
-      scheme = NS_LITERAL_CSTRING("mailto:");
-    rv = NS_NewURI(getter_AddRefs(uri), scheme + aHost);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return secMan->GetAppCodebasePrincipal(uri, aAppId, aIsInBrowserElement, aPrincipal);
+  return secMan->GetAppCodebasePrincipal(aURI, aAppId, aIsInBrowserElement, aPrincipal);
 }
 
 nsresult
@@ -136,50 +140,6 @@ GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal)
   return secMan->GetNoAppCodebasePrincipal(aURI, aPrincipal);
 }
 
-nsresult
-GetPrincipal(const nsACString& aHost, nsIPrincipal** aPrincipal)
-{
-  return GetPrincipal(aHost, nsIScriptSecurityManager::NO_APP_ID, false, aPrincipal);
-}
-
-nsresult
-GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost)
-{
-  nsCOMPtr uri;
-  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  uri = NS_GetInnermostURI(uri);
-  NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
-
-  rv = uri->GetAsciiHost(aHost);
-  if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) {
-    return NS_OK;
-  }
-
-  // For the mailto scheme, we use the path of the URI. We have to chop off the
-  // query part if one exists, so we eliminate everything after a ?.
-  bool isMailTo = false;
-  if (NS_SUCCEEDED(uri->SchemeIs("mailto", &isMailTo)) && isMailTo) {
-    rv = uri->GetPath(aHost);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    int32_t spart = aHost.FindChar('?', 0);
-    if (spart >= 0) {
-      aHost.Cut(spart, aHost.Length() - spart);
-    }
-    return NS_OK;
-  }
-
-  // Some entries like "file://" uses the origin.
-  rv = aPrincipal->GetOriginNoSuffix(aHost);
-  if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) {
-    return NS_OK;
-  }
-
-  return NS_ERROR_UNEXPECTED;
-}
-
 nsCString
 GetNextSubDomainForHost(const nsACString& aHost)
 {
@@ -235,6 +195,265 @@ public:
 
 NS_IMPL_ISUPPORTS(AppClearDataObserver, nsIObserver)
 
+class MOZ_STACK_CLASS UpgradeHostToOriginHelper {
+public:
+  virtual nsresult Insert(nsIPrincipal* aPrincipal, const nsAFlatCString& aType,
+                      uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
+                      int64_t aModificationTime) = 0;
+};
+
+class UpgradeHostToOriginDBMigration final : public UpgradeHostToOriginHelper {
+public:
+  UpgradeHostToOriginDBMigration(mozIStorageConnection* aDBConn, int64_t* aID) : mDBConn(aDBConn)
+                                                                               , mID(aID)
+  {
+    mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+      "INSERT INTO moz_hosts_new "
+      "(id, origin, type, permission, expireType, expireTime, modificationTime) "
+      "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), getter_AddRefs(mInsertStmt));
+
+    mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT id FROM moz_hosts_new WHERE origin = ?1"),
+                             getter_AddRefs(mLookupStmt));
+  }
+
+  nsresult
+  Insert(nsIPrincipal* aPrincipal, const nsAFlatCString& aType,
+         uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
+         int64_t aModificationTime) final
+  {
+    nsAutoCString origin;
+    nsresult rv = aPrincipal->GetOrigin(origin);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mLookupStmt->Reset();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mLookupStmt->BindUTF8StringByIndex(0, origin);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Check to see if the origin already exists in the database
+    bool hasResult = false;
+    if (NS_SUCCEEDED(mLookupStmt->ExecuteStep(&hasResult)) && hasResult) {
+      mLookupStmt->Reset();
+      return NS_OK;
+    }
+
+    rv = mInsertStmt->BindInt64ByIndex(0, *mID);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mInsertStmt->BindUTF8StringByIndex(1, origin);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mInsertStmt->BindUTF8StringByIndex(2, aType);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mInsertStmt->BindInt32ByIndex(3, aPermission);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mInsertStmt->BindInt32ByIndex(4, aExpireType);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mInsertStmt->BindInt64ByIndex(5, aExpireTime);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mInsertStmt->BindInt64ByIndex(6, aModificationTime);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Increment the working identifier, as we are about to use this one
+    (*mID)++;
+
+    rv = mInsertStmt->Execute();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr mLookupStmt;
+  nsCOMPtr mInsertStmt;
+  nsCOMPtr mDBConn;
+  int64_t* mID;
+};
+
+class UpgradeHostToOriginHostfileImport final : public UpgradeHostToOriginHelper {
+public:
+  UpgradeHostToOriginHostfileImport(nsPermissionManager* aPm,
+                                    nsPermissionManager::DBOperationType aOperation,
+                                    int64_t aID) : mPm(aPm)
+                                                 , mOperation(aOperation)
+                                                 , mID(aID)
+  {}
+
+  nsresult
+  Insert(nsIPrincipal* aPrincipal, const nsAFlatCString& aType,
+         uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
+         int64_t aModificationTime) final
+  {
+    // AddInternal won't do anything if the permission already exists
+    return mPm->AddInternal(aPrincipal, aType, aPermission, mID,
+                            aExpireType, aExpireTime, aModificationTime,
+                            nsPermissionManager::eDontNotify, mOperation);
+  }
+
+private:
+  nsRefPtr mPm;
+  nsPermissionManager::DBOperationType mOperation;
+  int64_t mID;
+};
+
+nsresult
+UpgradeHostToOriginAndInsert(const nsACString& aHost, const nsAFlatCString& aType,
+                             uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime,
+                             int64_t aModificationTime, uint32_t aAppId, bool aIsInBrowserElement,
+                             UpgradeHostToOriginHelper* aHelper)
+{
+  if (aHost.EqualsLiteral("")) {
+    // We no longer support the magic host 
+    NS_WARNING("The magic host  is no longer supported. "
+               "It is being removed from the permissions database.");
+    return NS_OK;
+  }
+
+  // First, we check to see if the host is a valid URI. If it is, it can be imported directly
+  nsCOMPtr uri;
+  nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost);
+  if (NS_SUCCEEDED(rv)) {
+    // It was previously possible to insert useless entries to your permissions database
+    // for URIs which have a null principal. This acts as a cleanup, getting rid of
+    // these useless database entries
+    bool nullpScheme = false;
+    if (NS_SUCCEEDED(uri->SchemeIs("moz-nullprincipal", &nullpScheme)) && nullpScheme) {
+      NS_WARNING("A moz-nullprincipal: permission is being discarded.");
+      return NS_OK;
+    }
+
+    nsCOMPtr principal;
+    rv = GetPrincipal(uri, aAppId, aIsInBrowserElement, getter_AddRefs(principal));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return aHelper->Insert(principal, aType, aPermission,
+                           aExpireType, aExpireTime, aModificationTime);
+    return NS_OK;
+  }
+
+  // The user may use this host at non-standard ports or protocols, we can use their history
+  // to guess what ports and protocols we want to add permissions for.
+  // We find every URI which they have visited with this host (or a subdomain of this host),
+  // and try to add it as a principal.
+  nsCOMPtr histSrv = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
+
+  nsCOMPtr histQuery;
+  rv = histSrv->GetNewQuery(getter_AddRefs(histQuery));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // We want to only find history items for this particular host, and subdomains
+  rv = histQuery->SetDomain(aHost);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = histQuery->SetDomainIsHost(false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr histQueryOpts;
+  rv = histSrv->GetNewQueryOptions(getter_AddRefs(histQueryOpts));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // We want to get the URIs for every item in the user's history with the given host
+  rv = histQueryOpts->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // We only search history, because searching both bookmarks and history
+  // is not supported, and history tends to be more comprehensive.
+  rv = histQueryOpts->SetQueryType(nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // We include hidden URIs (such as those visited via iFrames) as they may have permissions too
+  rv = histQueryOpts->SetIncludeHidden(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr histResult;
+  rv = histSrv->ExecuteQuery(histQuery, histQueryOpts, getter_AddRefs(histResult));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr histResultContainer;
+  rv = histResult->GetRoot(getter_AddRefs(histResultContainer));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = histResultContainer->SetContainerOpen(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint32_t childCount = 0;
+  rv = histResultContainer->GetChildCount(&childCount);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool foundHistory = false;
+  for (uint32_t i = 0; i < childCount; i++) {
+    nsCOMPtr child;
+    histResultContainer->GetChild(i, getter_AddRefs(child));
+    if (NS_FAILED(rv)) continue;
+
+    uint32_t type;
+    rv = child->GetType(&type);
+    if (NS_FAILED(rv) || type != nsINavHistoryResultNode::RESULT_TYPE_URI) {
+      NS_WARNING("Unexpected non-RESULT_TYPE_URI node in "
+                 "UpgradeHostToOriginAndInsert()");
+      continue;
+    }
+
+    nsAutoCString uriSpec;
+    rv = child->GetUri(uriSpec);
+    if (NS_FAILED(rv)) continue;
+
+    nsCOMPtr uri;
+    rv = NS_NewURI(getter_AddRefs(uri), uriSpec);
+    if (NS_FAILED(rv)) continue;
+
+    // Use the provided host - this URI may be for a subdomain, rather than the host we care about.
+    rv = uri->SetHost(aHost);
+    if (NS_FAILED(rv)) continue;
+
+    // We now have a URI which we can make a nsIPrincipal out of
+    nsCOMPtr principal;
+    rv = GetPrincipal(uri, aAppId, aIsInBrowserElement, getter_AddRefs(principal));
+    if (NS_FAILED(rv)) continue;
+
+    // Insert it! (The backend should be able to deal with us inserting the same origin repeatedly)
+    foundHistory = true;
+    rv = aHelper->Insert(principal, aType, aPermission,
+                         aExpireType, aExpireTime, aModificationTime);
+    NS_WARN_IF(NS_FAILED(rv));
+  }
+
+  rv = histResultContainer->SetContainerOpen(false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // If we didn't find any origins for this host in the poermissions database,
+  // we can insert the default http:// and https:// permissions into the database.
+  // This has a relatively high liklihood of applying the permission to the correct
+  // origin.
+  if (!foundHistory) {
+    rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aHost);
+    if (NS_SUCCEEDED(rv)) {
+      nsCOMPtr principal;
+      rv = GetPrincipal(uri, aAppId, aIsInBrowserElement, getter_AddRefs(principal));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      aHelper->Insert(principal, aType, aPermission,
+                      aExpireType, aExpireTime, aModificationTime);
+    }
+    rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("https://") + aHost);
+    if (NS_SUCCEEDED(rv)) {
+      nsCOMPtr principal;
+      rv = GetPrincipal(uri, aAppId, aIsInBrowserElement, getter_AddRefs(principal));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      aHelper->Insert(principal, aType, aPermission,
+                      aExpireType, aExpireTime, aModificationTime);
+    }
+  }
+
+  return NS_OK;
+}
+
 static bool
 IsExpandedPrincipal(nsIPrincipal* aPrincipal)
 {
@@ -248,9 +467,7 @@ IsExpandedPrincipal(nsIPrincipal* aPrincipal)
 
 nsPermissionManager::PermissionKey::PermissionKey(nsIPrincipal* aPrincipal)
 {
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetHostForPrincipal(aPrincipal, mHost)));
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId)));
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement)));
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetOrigin(mOrigin)));
 }
 
 /**
@@ -373,7 +590,7 @@ nsPermissionManager::AppClearDataObserverInit()
 // nsPermissionManager Implementation
 
 #define PERMISSIONS_FILE_NAME "permissions.sqlite"
-#define HOSTS_SCHEMA_VERSION 4
+#define HOSTS_SCHEMA_VERSION 5
 
 #define HOSTPERM_FILE_NAME "hostperm.1"
 
@@ -616,6 +833,89 @@ nsPermissionManager::InitDB(bool aRemoveFile)
 
       // fall through to the next upgrade
 
+    // Version 4->5 is the merging of host, appId, and isInBrowserElement into origin
+    case 4:
+      {
+        bool tableExists = false;
+        mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts_new"), &tableExists);
+        if (tableExists) {
+          rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts_new"));
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+        rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+          "CREATE TABLE moz_hosts_new ("
+            " id INTEGER PRIMARY KEY"
+            ",origin TEXT"
+            ",type TEXT"
+            ",permission INTEGER"
+            ",expireType INTEGER"
+            ",expireTime INTEGER"
+            ",modificationTime INTEGER"
+          ")"));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        nsCOMPtr stmt;
+        rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+          "SELECT host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement "
+          "FROM moz_hosts"), getter_AddRefs(stmt));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        nsCOMPtr pending;
+
+        int64_t id = 0;
+        nsAutoCString host, type;
+        uint32_t permission;
+        uint32_t expireType;
+        int64_t expireTime;
+        int64_t modificationTime;
+        uint32_t appId;
+        bool isInBrowserElement;
+        bool hasResult;
+
+        while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
+          // Read in the old row
+          rv = stmt->GetUTF8String(0, host);
+          if (NS_FAILED(rv)) {
+            continue;
+          }
+          rv = stmt->GetUTF8String(1, type);
+          if (NS_FAILED(rv)) {
+            continue;
+          }
+          permission = stmt->AsInt32(2);
+          expireType = stmt->AsInt32(3);
+          expireTime = stmt->AsInt64(4);
+          modificationTime = stmt->AsInt64(5);
+          if (stmt->AsInt64(6) < 0) {
+            continue;
+          }
+          appId = static_cast(stmt->AsInt64(6));
+          isInBrowserElement = static_cast(stmt->AsInt32(7));
+
+          UpgradeHostToOriginDBMigration upHelper(mDBConn, &id);
+          rv = UpgradeHostToOriginAndInsert(host, type, permission,
+                                            expireType, expireTime,
+                                            modificationTime, appId,
+                                            isInBrowserElement,
+                                            &upHelper);
+          if (NS_FAILED(rv)) {
+            NS_WARNING("Unexpected failure when upgrading migrating permission from host to origin");
+          }
+        }
+
+        // We rename the old table to moz_hosts_v4 instead of dropping it, such that if
+        // we discover that there was a problem with our migration code in the future, we have information
+        // to roll-back with.
+        rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ALTER TABLE moz_hosts RENAME TO moz_hosts_v4"));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ALTER TABLE moz_hosts_new RENAME TO moz_hosts"));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
     // current version.
     case HOSTS_SCHEMA_VERSION:
       break;
@@ -631,7 +931,7 @@ nsPermissionManager::InitDB(bool aRemoveFile)
         // check if all the expected columns exist
         nsCOMPtr stmt;
         rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
-          "SELECT host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement FROM moz_hosts"),
+          "SELECT origin, type, permission, expireType, expireTime, modificationTime FROM moz_hosts"),
           getter_AddRefs(stmt));
         if (NS_SUCCEEDED(rv))
           break;
@@ -650,8 +950,8 @@ nsPermissionManager::InitDB(bool aRemoveFile)
   // cache frequently used statements (for insertion, deletion, and updating)
   rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
     "INSERT INTO moz_hosts "
-    "(id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) "
-    "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)"), getter_AddRefs(mStmtInsert));
+    "(id, origin, type, permission, expireType, expireTime, modificationTime) "
+    "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), getter_AddRefs(mStmtInsert));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
@@ -688,14 +988,12 @@ nsPermissionManager::CreateTable()
   return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TABLE moz_hosts ("
       " id INTEGER PRIMARY KEY"
-      ",host TEXT"
+      ",origin TEXT"
       ",type TEXT"
       ",permission INTEGER"
       ",expireType INTEGER"
       ",expireTime INTEGER"
       ",modificationTime INTEGER"
-      ",appId INTEGER"
-      ",isInBrowserElement INTEGER"
     ")"));
 }
 
@@ -775,15 +1073,11 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
                                  DBOperationType       aDBOperation,
                                  const bool            aIgnoreSessionPermissions)
 {
-  nsAutoCString host;
-  nsresult rv = GetHostForPrincipal(aPrincipal, host);
+  nsAutoCString origin;
+  nsresult rv = aPrincipal->GetOrigin(origin);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!IsChildProcess()) {
-    nsAutoCString origin;
-    rv = aPrincipal->GetOrigin(origin);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     IPC::Permission permission(origin, aType, aPermission,
                                aExpireType, aExpireTime);
 
@@ -897,21 +1191,11 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
                                                             aModificationTime));
 
       if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
-        uint32_t appId;
-        rv = aPrincipal->GetAppId(&appId);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        bool isInBrowserElement;
-        rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, aModificationTime, appId, isInBrowserElement);
+        UpdateDB(op, mStmtInsert, id, origin, aType, aPermission, aExpireType, aExpireTime, aModificationTime);
       }
 
       if (aNotifyOperation == eNotify) {
-        NotifyObserversWithPermission(host,
-                                      entry->GetKey()->mAppId,
-                                      entry->GetKey()->mIsInBrowserElement,
+        NotifyObserversWithPermission(aPrincipal,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
@@ -932,12 +1216,10 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
         // We care only about the id here so we pass dummy values for all other
         // parameters.
         UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0,
-                 nsIPermissionManager::EXPIRE_NEVER, 0, 0, 0, false);
+                 nsIPermissionManager::EXPIRE_NEVER, 0, 0);
 
       if (aNotifyOperation == eNotify) {
-        NotifyObserversWithPermission(host,
-                                      entry->GetKey()->mAppId,
-                                      entry->GetKey()->mIsInBrowserElement,
+        NotifyObserversWithPermission(aPrincipal,
                                       mTypeArray[typeIndex],
                                       oldPermissionEntry.mPermission,
                                       oldPermissionEntry.mExpireType,
@@ -980,12 +1262,10 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
         // We care only about the id, the permission and expireType/expireTime/modificationTime here.
         // We pass dummy values for all other parameters.
         UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(),
-                 aPermission, aExpireType, aExpireTime, aModificationTime, 0, false);
+                 aPermission, aExpireType, aExpireTime, aModificationTime);
 
       if (aNotifyOperation == eNotify) {
-        NotifyObserversWithPermission(host,
-                                      entry->GetKey()->mAppId,
-                                      entry->GetKey()->mIsInBrowserElement,
+        NotifyObserversWithPermission(aPrincipal,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
@@ -1036,14 +1316,12 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
         rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
         NS_ENSURE_SUCCESS(rv, rv);
 
-        UpdateDB(eOperationAdding, mStmtInsert, id, host, aType, aPermission,
-                 aExpireType, aExpireTime, aModificationTime, appId, isInBrowserElement);
+        UpdateDB(eOperationAdding, mStmtInsert, id, origin, aType, aPermission,
+                 aExpireType, aExpireTime, aModificationTime);
       }
 
       if (aNotifyOperation == eNotify) {
-        NotifyObserversWithPermission(host,
-                                      entry->GetKey()->mAppId,
-                                      entry->GetKey()->mIsInBrowserElement,
+        NotifyObserversWithPermission(aPrincipal,
                                       mTypeArray[typeIndex],
                                       aPermission,
                                       aExpireType,
@@ -1272,25 +1550,12 @@ nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal,
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsAutoCString host;
-  nsresult rv = GetHostForPrincipal(aPrincipal, host);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   int32_t typeIndex = GetTypeIndex(aType, false);
   // If type == -1, the type isn't known,
   // so just return NS_OK
   if (typeIndex == -1) return NS_OK;
 
-  uint32_t appId;
-  rv = aPrincipal->GetAppId(&appId);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool isInBrowserElement;
-  rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
-                                                  typeIndex, aExactHostMatch);
+  PermissionHashKey* entry = GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch);
   if (!entry) {
     return NS_OK;
   }
@@ -1302,14 +1567,11 @@ nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal,
     return NS_OK;
   }
 
-  PermissionEntry& perm = entry->GetPermissions()[idx];
   nsCOMPtr principal;
-  rv = GetPrincipal(entry->GetKey()->mHost,
-                    entry->GetKey()->mAppId,
-                    entry->GetKey()->mIsInBrowserElement,
-                    getter_AddRefs(principal));
+  nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  PermissionEntry& perm = entry->GetPermissions()[idx];
   nsCOMPtr r = new nsPermission(principal,
                                                mTypeArray.ElementAt(perm.mType),
                                                perm.mPermission,
@@ -1362,25 +1624,12 @@ nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
     return NS_OK;
   }
 
-  nsAutoCString host;
-  nsresult rv = GetHostForPrincipal(aPrincipal, host);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   int32_t typeIndex = GetTypeIndex(aType, false);
   // If type == -1, the type isn't known,
   // so just return NS_OK
   if (typeIndex == -1) return NS_OK;
 
-  uint32_t appId;
-  rv = aPrincipal->GetAppId(&appId);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool isInBrowserElement;
-  rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
-                                                  typeIndex, aExactHostMatch);
+  PermissionHashKey* entry = GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch);
   if (!entry ||
       (!aIncludingSession &&
        entry->GetPermission(typeIndex).mNonSessionExpireType ==
@@ -1402,15 +1651,13 @@ nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal,
 // Also accepts host on the format "". This will perform an exact match
 // lookup as the string doesn't contain any dots.
 nsPermissionManager::PermissionHashKey*
-nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
-                                          uint32_t aAppId,
-                                          bool aIsInBrowserElement,
+nsPermissionManager::GetPermissionHashKey(nsIPrincipal* aPrincipal,
                                           uint32_t aType,
                                           bool aExactHostMatch)
 {
   PermissionHashKey* entry = nullptr;
 
-  nsRefPtr key = new PermissionKey(aHost, aAppId, aIsInBrowserElement);
+  nsRefPtr key = new PermissionKey(aPrincipal);
   entry = mPermissionTable.GetEntry(key);
 
   if (entry) {
@@ -1422,13 +1669,8 @@ nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
          (permEntry.mExpireType == nsIPermissionManager::EXPIRE_SESSION &&
           permEntry.mExpireTime != 0)) &&
         permEntry.mExpireTime <= (PR_Now() / 1000)) {
-      nsCOMPtr principal;
-      if (NS_FAILED(GetPrincipal(aHost, aAppId, aIsInBrowserElement, getter_AddRefs(principal)))) {
-        return nullptr;
-      }
-
       entry = nullptr;
-      RemoveFromPrincipal(principal, mTypeArray[aType].get());
+      RemoveFromPrincipal(aPrincipal, mTypeArray[aType].get());
     } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) {
       entry = nullptr;
     }
@@ -1440,10 +1682,40 @@ nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
 
   // If aExactHostMatch wasn't true, we can check if the base domain has a permission entry.
   if (!aExactHostMatch) {
-    nsCString domain = GetNextSubDomainForHost(aHost);
-    if (!domain.IsEmpty()) {
-      return GetPermissionHashKey(domain, aAppId, aIsInBrowserElement, aType, aExactHostMatch);
+    nsCOMPtr uri;
+    nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+    if (NS_FAILED(rv)) {
+      return nullptr;
     }
+
+    nsAutoCString host;
+    rv = uri->GetHost(host);
+    if (NS_FAILED(rv)) {
+      return nullptr;
+    }
+
+    nsCString domain = GetNextSubDomainForHost(host);
+    if (domain.IsEmpty()) {
+      return nullptr;
+    }
+
+    // Create a new principal which is identical to the current one, but with the new host
+    nsCOMPtr newURI;
+    rv = uri->Clone(getter_AddRefs(newURI));
+    if (NS_FAILED(rv)) {
+      return nullptr;
+    }
+
+    rv = newURI->SetHost(domain);
+    if (NS_FAILED(rv)) {
+      return nullptr;
+    }
+
+    // Copy the attributes over
+    mozilla::OriginAttributes attrs = mozilla::BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
+    nsCOMPtr principal = mozilla::BasePrincipal::CreateCodebasePrincipal(newURI, attrs);
+
+    return GetPermissionHashKey(principal, aType, aExactHostMatch);
   }
 
   // No entry, really...
@@ -1481,10 +1753,7 @@ AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg)
     }
 
     nsCOMPtr principal;
-    GetPrincipal(entry->GetKey()->mHost,
-                 entry->GetKey()->mAppId,
-                 entry->GetKey()->mIsInBrowserElement,
-                 getter_AddRefs(principal));
+    GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal));
 
     nsPermission *perm = new nsPermission(principal,
                                           data->types->ElementAt(permEntry.mType),
@@ -1542,10 +1811,7 @@ AddPermissionsModifiedSinceToList(
     }
 
     nsCOMPtr principal;
-    GetPrincipal(entry->GetKey()->mHost,
-                 entry->GetKey()->mAppId,
-                 entry->GetKey()->mIsInBrowserElement,
-                 getter_AddRefs(principal));
+    GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal));
 
     nsPermission* perm = new nsPermission(principal,
                                           data->types->ElementAt(permEntry.mType),
@@ -1609,17 +1875,20 @@ nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey
   for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
     nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
 
-    if (entry->GetKey()->mAppId != data->appId ||
-        (data->browserOnly && !entry->GetKey()->mIsInBrowserElement)) {
+    nsCOMPtr principal;
+    GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal));
+
+    uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
+    principal->GetAppId(&appId);
+
+    bool isInBrowserElement;
+    principal->GetIsInBrowserElement(&isInBrowserElement);
+
+    if (appId != data->appId ||
+        (data->browserOnly && !isInBrowserElement)) {
       continue;
     }
 
-    nsCOMPtr principal;
-    GetPrincipal(entry->GetKey()->mHost,
-                 entry->GetKey()->mAppId,
-                 entry->GetKey()->mIsInBrowserElement,
-                 getter_AddRefs(principal));
-
     data->permissions.AppendObject(new nsPermission(principal,
                                                     gPermissionManager->mTypeArray.ElementAt(permEntry.mType),
                                                     permEntry.mPermission,
@@ -1636,30 +1905,10 @@ nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly)
   ENSURE_NOT_CHILD_PROCESS;
   NS_ENSURE_ARG(aAppId != nsIScriptSecurityManager::NO_APP_ID);
 
-  // We begin by removing all the permissions from the DB.
-  // After clearing the DB, we call AddInternal() to make sure that all
-  // processes are aware of this change and the representation of the DB in
-  // memory is updated.
   // We have to get all permissions associated with an application and then
   // remove those because doing so in EnumerateEntries() would fail because
   // we might happen to actually delete entries from the list.
 
-  nsAutoCString sql;
-  sql.AppendLiteral("DELETE FROM moz_hosts WHERE appId=");
-  sql.AppendInt(aAppId);
-
-  if (aBrowserOnly) {
-    sql.AppendLiteral(" AND isInBrowserElement=1");
-  }
-
-  nsCOMPtr removeStmt;
-  nsresult rv = mDBConn->CreateAsyncStatement(sql, getter_AddRefs(removeStmt));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr pending;
-  rv = removeStmt->ExecuteAsync(nullptr, getter_AddRefs(pending));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   GetPermissionsForAppStruct data(aAppId, aBrowserOnly);
   mPermissionTable.EnumerateEntries(GetPermissionsForApp, &data);
 
@@ -1678,7 +1927,7 @@ nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly)
                 0,
                 0,
                 nsPermissionManager::eNotify,
-                nsPermissionManager::eNoDBOperation);
+                nsPermissionManager::eWriteToDB);
   }
 
   return NS_OK;
@@ -1688,10 +1937,16 @@ PLDHashOperator
 nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator(
   nsPermissionManager::PermissionHashKey* entry, void* arg)
 {
-  uint32_t* appId = static_cast(arg);
+  uint32_t* targetAppId = static_cast(arg);
 
   for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
-    if (entry->GetKey()->mAppId != *appId) {
+    nsCOMPtr principal;
+    GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal));
+
+    uint32_t appId;
+    principal->GetAppId(&appId);
+
+    if (appId != *targetAppId) {
       continue;
     }
 
@@ -1705,9 +1960,7 @@ nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator(
 
       entry->GetPermissions().RemoveElementAt(i);
 
-      gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost,
-                                                        entry->GetKey()->mAppId,
-                                                        entry->GetKey()->mIsInBrowserElement,
+      gPermissionManager->NotifyObserversWithPermission(principal,
                                                         gPermissionManager->mTypeArray.ElementAt(oldPermissionEntry.mType),
                                                         oldPermissionEntry.mPermission,
                                                         oldPermissionEntry.mExpireType,
@@ -1721,9 +1974,7 @@ nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator(
     permEntry.mExpireType = permEntry.mNonSessionExpireType;
     permEntry.mExpireTime = permEntry.mNonSessionExpireTime;
 
-    gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost,
-                                                      entry->GetKey()->mAppId,
-                                                      entry->GetKey()->mIsInBrowserElement,
+    gPermissionManager->NotifyObserversWithPermission(principal,
                                                       gPermissionManager->mTypeArray.ElementAt(permEntry.mType),
                                                       permEntry.mPermission,
                                                       permEntry.mExpireType,
@@ -1787,20 +2038,15 @@ nsPermissionManager::GetTypeIndex(const char *aType,
 // wrapper function for mangling (host,type,perm,expireType,expireTime)
 // set into an nsIPermission.
 void
-nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost,
-                                                   uint32_t          aAppId,
-                                                   bool              aIsInBrowserElement,
+nsPermissionManager::NotifyObserversWithPermission(nsIPrincipal*     aPrincipal,
                                                    const nsCString  &aType,
                                                    uint32_t          aPermission,
                                                    uint32_t          aExpireType,
                                                    int64_t           aExpireTime,
                                                    const char16_t  *aData)
 {
-  nsCOMPtr principal;
-  GetPrincipal(aHost, aAppId, aIsInBrowserElement, getter_AddRefs(principal));
-
   nsCOMPtr permission =
-    new nsPermission(principal, aType, aPermission,
+    new nsPermission(aPrincipal, aType, aPermission,
                      aExpireType, aExpireTime);
   if (permission)
     NotifyObservers(permission, aData);
@@ -1851,18 +2097,16 @@ nsPermissionManager::Read()
 
   nsCOMPtr stmt;
   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
-    "SELECT id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement "
+    "SELECT id, origin, type, permission, expireType, expireTime, modificationTime "
     "FROM moz_hosts"), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
   int64_t id;
-  nsAutoCString host, type;
+  nsAutoCString origin, type;
   uint32_t permission;
   uint32_t expireType;
   int64_t expireTime;
   int64_t modificationTime;
-  uint32_t appId;
-  bool isInBrowserElement;
   bool hasResult;
   bool readError = false;
 
@@ -1873,7 +2117,7 @@ nsPermissionManager::Read()
     if (id > mLargestID)
       mLargestID = id;
 
-    rv = stmt->GetUTF8String(1, host);
+    rv = stmt->GetUTF8String(1, origin);
     if (NS_FAILED(rv)) {
       readError = true;
       continue;
@@ -1892,16 +2136,8 @@ nsPermissionManager::Read()
     expireTime = stmt->AsInt64(5);
     modificationTime = stmt->AsInt64(6);
 
-    if (stmt->AsInt64(7) < 0) {
-      readError = true;
-      continue;
-    }
-
-    appId = static_cast(stmt->AsInt64(7));
-    isInBrowserElement = static_cast(stmt->AsInt32(8));
-
     nsCOMPtr principal;
-    nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal));
+    nsresult rv = GetPrincipalFromOrigin(origin, getter_AddRefs(principal));
     if (NS_FAILED(rv)) {
       readError = true;
       continue;
@@ -1924,6 +2160,7 @@ nsPermissionManager::Read()
 }
 
 static const char kMatchTypeHost[] = "host";
+static const char kMatchTypeOrigin[] = "origin";
 
 // Import() will read a file from the profile directory and add them to the
 // database before deleting the file - ie, this is a one-shot operation that
@@ -2033,32 +2270,48 @@ nsPermissionManager::_DoImport(nsIInputStream *inputStream, mozIStorageConnectio
 
     if (lineArray[0].EqualsLiteral(kMatchTypeHost) &&
         lineArray.Length() == 4) {
-
-      nsresult error;
+      nsresult error = NS_OK;
       uint32_t permission = lineArray[2].ToInteger(&error);
       if (NS_FAILED(error))
         continue;
 
-      // hosts might be encoded in UTF8; switch them to ACE to be consistent
-      if (!IsASCII(lineArray[3])) {
-        rv = NormalizeToACE(lineArray[3]);
-        if (NS_FAILED(rv))
-          continue;
-      }
-
-      nsCOMPtr principal;
-      nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal));
-      NS_ENSURE_SUCCESS(rv, rv);
-
       // the import file format doesn't handle modification times, so we use
       // 0, which AddInternal will convert to now()
       int64_t modificationTime = 0;
 
-      rv = AddInternal(principal, lineArray[1], permission, id,
-                       nsIPermissionManager::EXPIRE_NEVER, 0,
-                       modificationTime,
-                       eDontNotify, operation);
-      NS_ENSURE_SUCCESS(rv, rv);
+      UpgradeHostToOriginHostfileImport upHelper(this, operation, id);
+      error = UpgradeHostToOriginAndInsert(lineArray[3], lineArray[1], permission,
+                                           nsIPermissionManager::EXPIRE_NEVER, 0,
+                                           modificationTime, nsIScriptSecurityManager::NO_APP_ID,
+                                           false, &upHelper);
+      if (NS_FAILED(error)) {
+        NS_WARNING("There was a problem importing a host permission");
+      }
+    } else if (lineArray[0].EqualsLiteral(kMatchTypeOrigin) &&
+               lineArray.Length() == 4) {
+      nsresult error = NS_OK;
+      uint32_t permission = lineArray[2].ToInteger(&error);
+      if (NS_FAILED(error))
+        continue;
+
+      nsCOMPtr principal;
+      error = GetPrincipalFromOrigin(lineArray[3], getter_AddRefs(principal));
+      if (NS_FAILED(error)) {
+        NS_WARNING("Couldn't import an origin permission - malformed origin");
+        continue;
+      }
+
+      // the import file format doesn't handle modification times, so we use
+      // 0, which AddInternal will convert to now()
+      int64_t modificationTime = 0;
+
+      error = AddInternal(principal, lineArray[1], permission, id,
+                          nsIPermissionManager::EXPIRE_NEVER, 0,
+                          modificationTime,
+                          eDontNotify, operation);
+      if (NS_FAILED(error)) {
+        NS_WARNING("There was a problem importing an origin permission");
+      }
     }
 
   } while (isMore);
@@ -2083,14 +2336,12 @@ void
 nsPermissionManager::UpdateDB(OperationType aOp,
                               mozIStorageAsyncStatement* aStmt,
                               int64_t aID,
-                              const nsACString &aHost,
+                              const nsACString &aOrigin,
                               const nsACString &aType,
                               uint32_t aPermission,
                               uint32_t aExpireType,
                               int64_t aExpireTime,
-                              int64_t aModificationTime,
-                              uint32_t aAppId,
-                              bool aIsInBrowserElement)
+                              int64_t aModificationTime)
 {
   ENSURE_NOT_CHILD_PROCESS_NORET;
 
@@ -2106,7 +2357,7 @@ nsPermissionManager::UpdateDB(OperationType aOp,
       rv = aStmt->BindInt64ByIndex(0, aID);
       if (NS_FAILED(rv)) break;
 
-      rv = aStmt->BindUTF8StringByIndex(1, aHost);
+      rv = aStmt->BindUTF8StringByIndex(1, aOrigin);
       if (NS_FAILED(rv)) break;
 
       rv = aStmt->BindUTF8StringByIndex(2, aType);
@@ -2122,12 +2373,6 @@ nsPermissionManager::UpdateDB(OperationType aOp,
       if (NS_FAILED(rv)) break;
 
       rv = aStmt->BindInt64ByIndex(6, aModificationTime);
-      if (NS_FAILED(rv)) break;
-
-      rv = aStmt->BindInt64ByIndex(7, aAppId);
-      if (NS_FAILED(rv)) break;
-
-      rv = aStmt->BindInt64ByIndex(8, aIsInBrowserElement);
       break;
     }
 
@@ -2246,25 +2491,12 @@ nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal,
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsAutoCString host;
-  nsresult rv = GetHostForPrincipal(aPrincipal, host);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   int32_t typeIndex = GetTypeIndex(aType, false);
   // If type == -1, the type isn't known,
   // so just return NS_OK
   if (typeIndex == -1) return NS_OK;
 
-  uint32_t appId;
-  rv = aPrincipal->GetAppId(&appId);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool isInBrowserElement;
-  rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
-                                                  typeIndex, aExactHostMatch);
+  PermissionHashKey* entry = GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch);
   if (!entry) {
     return NS_OK;
   }
@@ -2293,16 +2525,10 @@ nsPermissionManager::FetchPermissions() {
   for (uint32_t i = 0; i < perms.Length(); i++) {
     const IPC::Permission &perm = perms[i];
 
-    nsAutoCString originNoSuffix;
-    mozilla::OriginAttributes attrs;
-    attrs.PopulateFromOrigin(perm.origin, originNoSuffix);
-
-    nsCOMPtr uri;
-    nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
+    nsCOMPtr principal;
+    nsresult rv = GetPrincipalFromOrigin(perm.origin, getter_AddRefs(principal));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
-
     // The child process doesn't care about modification times - it neither
     // reads nor writes, nor removes them based on the date - so 0 (which
     // will end up as now()) is fine.
diff --git a/extensions/cookie/nsPermissionManager.h b/extensions/cookie/nsPermissionManager.h
index 55327005fea3..7bf2c3d146fc 100644
--- a/extensions/cookie/nsPermissionManager.h
+++ b/extensions/cookie/nsPermissionManager.h
@@ -71,35 +71,22 @@ public:
   {
   public:
     explicit PermissionKey(nsIPrincipal* aPrincipal);
-    PermissionKey(const nsACString& aHost,
-                  uint32_t aAppId,
-                  bool aIsInBrowserElement)
-      : mHost(aHost)
-      , mAppId(aAppId)
-      , mIsInBrowserElement(aIsInBrowserElement)
+    explicit PermissionKey(const nsACString& aOrigin)
+      : mOrigin(aOrigin)
     {
     }
 
     bool operator==(const PermissionKey& aKey) const {
-      return mHost.Equals(aKey.mHost) &&
-             mAppId == aKey.mAppId &&
-             mIsInBrowserElement == aKey.mIsInBrowserElement;
+      return mOrigin.Equals(aKey.mOrigin);
     }
 
     PLDHashNumber GetHashCode() const {
-      nsAutoCString str;
-      str.Assign(mHost);
-      str.AppendInt(mAppId);
-      str.AppendInt(static_cast(mIsInBrowserElement));
-
-      return mozilla::HashString(str);
+      return mozilla::HashString(mOrigin);
     }
 
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PermissionKey)
 
-    nsCString mHost;
-    uint32_t  mAppId;
-    bool      mIsInBrowserElement;
+    nsCString mOrigin;
 
   private:
     // Default ctor shouldn't be used.
@@ -222,11 +209,9 @@ private:
   int32_t GetTypeIndex(const char *aTypeString,
                        bool        aAdd);
 
-  PermissionHashKey* GetPermissionHashKey(const nsACString& aHost,
-                                          uint32_t aAppId,
-                                          bool aIsInBrowserElement,
-                                          uint32_t          aType,
-                                          bool              aExactHostMatch);
+  PermissionHashKey* GetPermissionHashKey(nsIPrincipal* aPrincipal,
+                                          uint32_t      aType,
+                                          bool          aExactHostMatch);
 
   nsresult CommonTestPermission(nsIPrincipal* aPrincipal,
                                 const char *aType,
@@ -241,9 +226,7 @@ private:
   nsresult ImportDefaults();
   nsresult _DoImport(nsIInputStream *inputStream, mozIStorageConnection *aConn);
   nsresult Read();
-  void     NotifyObserversWithPermission(const nsACString &aHost,
-                                         uint32_t          aAppId,
-                                         bool              aIsInBrowserElement,
+  void     NotifyObserversWithPermission(nsIPrincipal*     aPrincipal,
                                          const nsCString  &aType,
                                          uint32_t          aPermission,
                                          uint32_t          aExpireType,
@@ -261,14 +244,12 @@ private:
   static void UpdateDB(OperationType aOp,
                        mozIStorageAsyncStatement* aStmt,
                        int64_t aID,
-                       const nsACString& aHost,
+                       const nsACString& aOrigin,
                        const nsACString& aType,
                        uint32_t aPermission,
                        uint32_t aExpireType,
                        int64_t aExpireTime,
-                       int64_t aModificationTime,
-                       uint32_t aAppId,
-                       bool aIsInBrowserElement);
+                       int64_t aModificationTime);
 
   nsresult RemoveExpiredPermissionsForApp(uint32_t aAppId);
 

From 4e4701aa650dd9d27715a9f59e1b78ff6ae54ee9 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 16 Jun 2015 15:28:58 -0400
Subject: [PATCH 27/67] Bug 1165263 - Part 2: Update unit tests for
 nsPermissionManager to reflect new internals, r=ehsan

---
 .../test/unit/test_permmanager_defaults.js    |  44 ++++-
 .../test_permmanager_load_invalid_entries.js  |   2 +-
 .../test/unit/test_permmanager_mailto.js      |  42 -----
 .../test/unit/test_permmanager_matches.js     |  34 ++--
 .../test/unit/test_permmanager_matchesuri.js  |  34 ++--
 .../test/unit/test_permmanager_migrate_4-5.js | 162 ++++++++++++++++++
 extensions/cookie/test/unit/xpcshell.ini      |   2 +-
 7 files changed, 231 insertions(+), 89 deletions(-)
 delete mode 100644 extensions/cookie/test/unit/test_permmanager_mailto.js
 create mode 100644 extensions/cookie/test/unit/test_permmanager_migrate_4-5.js

diff --git a/extensions/cookie/test/unit/test_permmanager_defaults.js b/extensions/cookie/test/unit/test_permmanager_defaults.js
index 47e18c4fcc51..91874a086707 100644
--- a/extensions/cookie/test/unit/test_permmanager_defaults.js
+++ b/extensions/cookie/test/unit/test_permmanager_defaults.js
@@ -3,7 +3,9 @@
 
 // The origin we use in most of the tests.
 const TEST_ORIGIN = NetUtil.newURI("http://example.org");
+const TEST_ORIGIN_HTTPS = NetUtil.newURI("https://example.org");
 const TEST_ORIGIN_2 = NetUtil.newURI("http://example.com");
+const TEST_ORIGIN_3 = NetUtil.newURI("https://example2.com:8080");
 const TEST_PERMISSION = "test-permission";
 Components.utils.import("resource://gre/modules/Promise.jsm");
 
@@ -35,8 +37,10 @@ add_task(function* do_test() {
 
   conv.writeString("# this is a comment\n");
   conv.writeString("\n"); // a blank line!
-  conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.spec + "\n");
-  conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2.spec + "\n");
+  conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.host + "\n");
+  conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2.host + "\n");
+  conv.writeString("origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_3.spec + "\n");
+  conv.writeString("origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.spec + "!appId=1000&inBrowser=1\n");
   ostream.close();
 
   // Set the preference used by the permission manager so the file is read.
@@ -48,12 +52,29 @@ add_task(function* do_test() {
 
   // test the default permission was applied.
   let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_ORIGIN);
+  let principalHttps = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_ORIGIN_HTTPS);
+  let principal2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_ORIGIN_2);
+  let principal3 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_ORIGIN_3);
+  let principal4 = Services.scriptSecurityManager.getAppCodebasePrincipal(TEST_ORIGIN, 1000, true);
+  let principal5 = Services.scriptSecurityManager.getAppCodebasePrincipal(TEST_ORIGIN_3, 1000, true);
 
   do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
               pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
+  do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+              pm.testPermissionFromPrincipal(principalHttps, TEST_PERMISSION));
+  do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+              pm.testPermissionFromPrincipal(principal3, TEST_PERMISSION));
+  do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+              pm.testPermissionFromPrincipal(principal4, TEST_PERMISSION));
+
+  // Didn't add
+  do_check_eq(Ci.nsIPermissionManager.UNKNOWN_ACTION,
+              pm.testPermissionFromPrincipal(principal5, TEST_PERMISSION));
 
   // the permission should exist in the enumerator.
-  do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum());
+  do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum(TEST_ORIGIN));
+  do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum(TEST_ORIGIN_3));
+
   // but should not have been written to the DB
   yield checkCapabilityViaDB(null);
 
@@ -62,6 +83,10 @@ add_task(function* do_test() {
 
   do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
               pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
+  do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+              pm.testPermissionFromPrincipal(principal3, TEST_PERMISSION));
+  do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+              pm.testPermissionFromPrincipal(principal4, TEST_PERMISSION));
 
   // Asking for this permission to be removed should result in that permission
   // having UNKNOWN_ACTION
@@ -104,8 +129,6 @@ add_task(function* do_test() {
   // check default permissions and removeAllSince work as expected.
   pm.removeAll(); // ensure only defaults are there.
 
-  let principal2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_ORIGIN_2);
-
   // default for both principals is allow.
   do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
               pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
@@ -174,7 +197,7 @@ function checkCapabilityViaDB(expected, origin = TEST_ORIGIN, type = TEST_PERMIS
   let count = 0;
   let max = 20;
   let do_check = () => {
-    let got = findCapabilityViaDB(origin.host, type);
+    let got = findCapabilityViaDB(origin, type);
     if (got == expected) {
       // the do_check_eq() below will succeed - which is what we want.
       do_check_eq(got, expected, "The database has the expected value");
@@ -198,7 +221,10 @@ function checkCapabilityViaDB(expected, origin = TEST_ORIGIN, type = TEST_PERMIS
 // use the DB to find the requested permission.   Returns the permission
 // value (ie, the "capability" in nsIPermission parlance) or null if it can't
 // be found.
-function findCapabilityViaDB(host = TEST_ORIGIN, type = TEST_PERMISSION) {
+function findCapabilityViaDB(origin = TEST_ORIGIN, type = TEST_PERMISSION) {
+  let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(origin);
+  let originStr = principal.origin;
+
   let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
   file.append("permissions.sqlite");
 
@@ -208,8 +234,8 @@ function findCapabilityViaDB(host = TEST_ORIGIN, type = TEST_PERMISSION) {
   let connection = storage.openDatabase(file);
 
   let query = connection.createStatement(
-      "SELECT permission FROM moz_hosts WHERE host = :host AND type = :type");
-  query.bindByName("host", host);
+      "SELECT permission FROM moz_hosts WHERE origin = :origin AND type = :type");
+  query.bindByName("origin", originStr);
   query.bindByName("type", type);
 
   if (!query.executeStep()) {
diff --git a/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js b/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js
index b96813b98aa7..4cb1f9604439 100644
--- a/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js
+++ b/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js
@@ -121,7 +121,7 @@ function run_test() {
 
   // The schema should be upgraded to 4, and a 'modificationTime' column should
   // exist with all records having a value of 0.
-  do_check_eq(connection.schemaVersion, 4);
+  do_check_eq(connection.schemaVersion, 5);
 
   let select = connection.createStatement("SELECT modificationTime FROM moz_hosts")
   let numMigrated = 0;
diff --git a/extensions/cookie/test/unit/test_permmanager_mailto.js b/extensions/cookie/test/unit/test_permmanager_mailto.js
deleted file mode 100644
index 1c111295bec7..000000000000
--- a/extensions/cookie/test/unit/test_permmanager_mailto.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-function run_test() {
-  // initialize the permission manager service
-  const kTestAddr = "test@example.org";
-  const kType = "test-mailto";
-  const kCapability = 1;
-
-  // make a mailto: URI with parameters
-  let uri = Services.io.newURI("mailto:" + kTestAddr + "?subject=test", null,
-                               null);
-  let origin = "mailto:" + kTestAddr;
-
-  // add a permission entry for that URI
-  Services.perms.add(uri, kType, kCapability);
-  do_check_true(permission_exists(origin, kType, kCapability));
-
-  // remove the permission, and make sure it was removed
-  Services.perms.remove(uri, kType);
-  do_check_false(permission_exists(origin, kType, kCapability));
-
-  uri = Services.io.newURI("mailto:" + kTestAddr, null, null);
-  Services.perms.add(uri, kType, kCapability);
-  do_check_true(permission_exists(origin, kType, kCapability));
-
-  Services.perms.remove(uri, kType);
-  do_check_false(permission_exists(origin, kType, kCapability));
-}
-
-function permission_exists(aOrigin, aType, aCapability) {
-  let e = Services.perms.enumerator;
-  while (e.hasMoreElements()) {
-    let perm = e.getNext().QueryInterface(Ci.nsIPermission);
-    if (perm.principal.origin == aOrigin &&
-        perm.type == aType &&
-        perm.capability == aCapability) {
-      return true;
-    }
-  }
-  return false;
-}
diff --git a/extensions/cookie/test/unit/test_permmanager_matches.js b/extensions/cookie/test/unit/test_permmanager_matches.js
index 425a12c705b8..475581aeabd7 100644
--- a/extensions/cookie/test/unit/test_permmanager_matches.js
+++ b/extensions/cookie/test/unit/test_permmanager_matches.js
@@ -34,10 +34,6 @@ function run_test() {
   let uri0 = NetUtil.newURI("http://google.com/search?q=foo#hashtag", null, null);
   let uri1 = NetUtil.newURI("http://hangouts.google.com/subdir", null, null);
   let uri2 = NetUtil.newURI("http://google.org/", null, null);
-
-  // NOTE: These entries have different ports/schemes, but will be considered
-  // the same right now (as permissions are based on host only). With bug 1165263
-  // this will be fixed.
   let uri3 = NetUtil.newURI("https://google.com/some/random/subdirectory", null, null);
   let uri4 = NetUtil.newURI("https://hangouts.google.com/#!/hangout", null, null);
   let uri5 = NetUtil.newURI("http://google.com:8096/", null, null);
@@ -88,41 +84,41 @@ function run_test() {
   pm.addFromPrincipal(uri0_2000_y, "test/matches", pm.ALLOW_ACTION);
   let perm_2000_y = pm.getPermissionObject(uri0_2000_y, "test/matches", true);
 
-  matches_always(perm_n_n, [uri0_n_n, uri3_n_n, uri5_n_n]);
-  matches_weak(perm_n_n, [uri1_n_n, uri4_n_n]);
-  matches_never(perm_n_n, [uri2_n_n,
+  matches_always(perm_n_n, [uri0_n_n]);
+  matches_weak(perm_n_n, [uri1_n_n]);
+  matches_never(perm_n_n, [uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
                            uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
                            uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
                            uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
                            uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y]);
 
-  matches_always(perm_1000_n, [uri0_1000_n, uri3_1000_n, uri5_1000_n]);
-  matches_weak(perm_1000_n, [uri1_1000_n, uri4_1000_n]);
-  matches_never(perm_1000_n, [uri2_1000_n,
+  matches_always(perm_1000_n, [uri0_1000_n]);
+  matches_weak(perm_1000_n, [uri1_1000_n]);
+  matches_never(perm_1000_n, [uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
                               uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
                               uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
                               uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
                               uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y]);
 
-  matches_always(perm_1000_y, [uri0_1000_y, uri3_1000_y, uri5_1000_y]);
-  matches_weak(perm_1000_y, [uri1_1000_y, uri4_1000_y]);
-  matches_never(perm_1000_y, [uri2_1000_y,
+  matches_always(perm_1000_y, [uri0_1000_y]);
+  matches_weak(perm_1000_y, [uri1_1000_y]);
+  matches_never(perm_1000_y, [uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y,
                               uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
                               uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
                               uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
                               uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y]);
 
-  matches_always(perm_2000_n, [uri0_2000_n, uri3_2000_n, uri5_2000_n]);
-  matches_weak(perm_2000_n, [uri1_2000_n, uri4_2000_n]);
-  matches_never(perm_2000_n, [uri2_2000_n,
+  matches_always(perm_2000_n, [uri0_2000_n]);
+  matches_weak(perm_2000_n, [uri1_2000_n]);
+  matches_never(perm_2000_n, [uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
                               uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
                               uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
                               uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
                               uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y]);
 
-  matches_always(perm_2000_y, [uri0_2000_y, uri3_2000_y, uri5_2000_y]);
-  matches_weak(perm_2000_y, [uri1_2000_y, uri4_2000_y]);
-  matches_never(perm_2000_y, [uri2_2000_y,
+  matches_always(perm_2000_y, [uri0_2000_y]);
+  matches_weak(perm_2000_y, [uri1_2000_y]);
+  matches_never(perm_2000_y, [uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y,
                               uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n,
                               uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n,
                               uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n,
diff --git a/extensions/cookie/test/unit/test_permmanager_matchesuri.js b/extensions/cookie/test/unit/test_permmanager_matchesuri.js
index aed8ea529119..7317cb35d3ad 100644
--- a/extensions/cookie/test/unit/test_permmanager_matchesuri.js
+++ b/extensions/cookie/test/unit/test_permmanager_matchesuri.js
@@ -70,23 +70,23 @@ function run_test() {
 
   {
     let perm = mk_permission(uri0);
-    matches_always(perm, [uri0, uri2, uri5, uri7]);
-    matches_weak(perm, [uri1, uri4, uri6]);
-    matches_never(perm, [uri3, fileuri1, fileuri2, fileuri3]);
+    matches_always(perm, [uri0, uri2]);
+    matches_weak(perm, [uri1, uri4]);
+    matches_never(perm, [uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]);
   }
 
   {
     let perm = mk_permission(uri1);
-    matches_always(perm, [uri1, uri6]);
+    matches_always(perm, [uri1]);
     matches_weak(perm, [uri4]);
-    matches_never(perm, [uri0, uri2, uri3, uri5, uri7, fileuri1, fileuri2, fileuri3]);
+    matches_never(perm, [uri0, uri2, uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]);
   }
 
   {
     let perm = mk_permission(uri2);
-    matches_always(perm, [uri0, uri2, uri5, uri7]);
-    matches_weak(perm, [uri1, uri4, uri6]);
-    matches_never(perm, [uri3, fileuri1, fileuri2, fileuri3]);
+    matches_always(perm, [uri0, uri2]);
+    matches_weak(perm, [uri1, uri4]);
+    matches_never(perm, [uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]);
   }
 
   {
@@ -105,23 +105,23 @@ function run_test() {
 
   {
     let perm = mk_permission(uri5);
-    matches_always(perm, [uri0, uri2, uri5, uri7]);
-    matches_weak(perm, [uri1, uri4, uri6]);
-    matches_never(perm, [uri3, fileuri1, fileuri2, fileuri3]);
+    matches_always(perm, [uri5, uri7]);
+    matches_weak(perm, [uri6]);
+    matches_never(perm, [uri0, uri1, uri2, uri3, uri4, fileuri1, fileuri2, fileuri3]);
   }
 
   {
     let perm = mk_permission(uri6);
-    matches_always(perm, [uri1, uri6]);
-    matches_weak(perm, [uri4]);
-    matches_never(perm, [uri0, uri2, uri3, uri5, uri7, fileuri1, fileuri2, fileuri3]);
+    matches_always(perm, [uri6]);
+    matches_weak(perm, []);
+    matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri7, fileuri1, fileuri2, fileuri3]);
   }
 
   {
     let perm = mk_permission(uri7);
-    matches_always(perm, [uri0, uri2, uri5, uri7]);
-    matches_weak(perm, [uri1, uri4, uri6]);
-    matches_never(perm, [uri3, fileuri1, fileuri2, fileuri3]);
+    matches_always(perm, [uri5, uri7]);
+    matches_weak(perm, [uri6]);
+    matches_never(perm, [uri0, uri1, uri2, uri3, uri4, fileuri1, fileuri2, fileuri3]);
   }
 
   {
diff --git a/extensions/cookie/test/unit/test_permmanager_migrate_4-5.js b/extensions/cookie/test/unit/test_permmanager_migrate_4-5.js
new file mode 100644
index 000000000000..f3b816817360
--- /dev/null
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_4-5.js
@@ -0,0 +1,162 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
+                                  "resource://testing-common/PlacesTestUtils.jsm");
+
+let PERMISSIONS_FILE_NAME = "permissions.sqlite";
+
+function GetPermissionsFile(profile)
+{
+  let file = profile.clone();
+  file.append(PERMISSIONS_FILE_NAME);
+  return file;
+}
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function test() {
+  /* Create and set up the permissions database */
+  let profile = do_get_profile();
+
+  let db = Services.storage.openDatabase(GetPermissionsFile(profile));
+  db.schemaVersion = 4;
+
+  db.executeSimpleSQL(
+    "CREATE TABLE moz_hosts (" +
+      " id INTEGER PRIMARY KEY" +
+      ",host TEXT" +
+      ",type TEXT" +
+      ",permission INTEGER" +
+      ",expireType INTEGER" +
+      ",expireTime INTEGER" +
+      ",modificationTime INTEGER" +
+      ",appId INTEGER" +
+      ",isInBrowserElement INTEGER" +
+    ")");
+
+  let stmtInsert = db.createStatement(
+    "INSERT INTO moz_hosts (" +
+      "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" +
+    ") VALUES (" +
+      ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" +
+    ")");
+
+  let id = 0;
+
+  function insertHost(host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) {
+    stmtInsert.bindByName("id", id++);
+    stmtInsert.bindByName("host", host);
+    stmtInsert.bindByName("type", type);
+    stmtInsert.bindByName("permission", permission);
+    stmtInsert.bindByName("expireType", expireType);
+    stmtInsert.bindByName("expireTime", expireTime);
+    stmtInsert.bindByName("modificationTime", modificationTime);
+    stmtInsert.bindByName("appId", appId);
+    stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
+
+    try {
+      stmtInsert.executeStep();
+      stmtInsert.reset();
+    } catch (e) {
+      stmtInsert.reset();
+      throw e;
+    }
+  }
+
+  // Add some rows to the database
+  insertHost("foo.com", "A", 1, 0, 0, 0, 0, false);
+  insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false);
+  insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true);
+  insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false);
+  insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false);
+  insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false);
+  insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false);
+  insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true);
+  insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false);
+  insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false);
+  insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false);
+  insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false);
+  insertHost("", "A", 1, 0, 0, 0, 0, false);
+  insertHost("", "B", 1, 0, 0, 0, 0, false);
+
+  // CLose the db connection
+  stmtInsert.finalize();
+  db.close();
+  stmtInsert = null;
+  db = null;
+
+  let expected = [
+    // The http:// entries under foo.com won't be inserted, as there are history entries for foo.com,
+    // and http://foo.com or a subdomain are never visited.
+    // However, permissions for subdomains of foo.com will be present for both http:// and https://,
+    // as they do not apply to any entry in the history
+    // ["http://foo.com", "A", 1, 0, 0],
+    // ["http://foo.com!appId=1000", "A", 1, 0, 0],
+    // ["http://foo.com!appId=2000&inBrowser=1", "A", 1, 0, 0],
+
+    ["http://sub.foo.com", "B", 1, 0, 0],
+    ["http://subber.sub.foo.com", "B", 1, 0, 0],
+
+    ["https://foo.com", "A", 1, 0, 0],
+    ["https://foo.com!appId=1000", "A", 1, 0, 0],
+    ["https://foo.com!appId=2000&inBrowser=1", "A", 1, 0, 0],
+    ["https://sub.foo.com", "B", 1, 0, 0],
+    ["https://subber.sub.foo.com", "B", 1, 0, 0],
+
+    // bar.ca will have both http:// and https:// for all entries, because the foo did the bar a favour
+    ["http://bar.ca", "B", 1, 0, 0],
+    ["https://bar.ca", "B", 1, 0, 0],
+    ["http://bar.ca!appId=1000", "B", 1, 0, 0],
+    ["https://bar.ca!appId=1000", "B", 1, 0, 0],
+    ["http://bar.ca!appId=1000&inBrowser=1", "A", 1, 0, 0],
+    ["https://bar.ca!appId=1000&inBrowser=1", "A", 1, 0, 0],
+    ["file:///some/path/to/file.html", "A", 1, 0, 0],
+    ["file:///another/file.html", "A", 1, 0, 0],
+
+    // Because we put ftp://some.subdomain.of.foo.com:8000/some/subdirectory in the history, we should
+    // also have these entries
+    ["ftp://foo.com:8000", "A", 1, 0, 0],
+    ["ftp://foo.com:8000!appId=1000", "A", 1, 0, 0],
+    ["ftp://foo.com:8000!appId=2000&inBrowser=1", "A", 1, 0, 0],
+  ];
+
+  let found = expected.map((it) => 0);
+
+  // Add some places to the places database
+  yield PlacesTestUtils.addVisits(Services.io.newURI("https://foo.com/some/other/subdirectory", null, null));
+  yield PlacesTestUtils.addVisits(Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory", null, null));
+
+  // Force initialization of the nsPermissionManager
+  let enumerator = Services.perms.enumerator;
+  while (enumerator.hasMoreElements()) {
+    let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+    let isExpected = false;
+
+    expected.forEach((it, i) => {
+      if (permission.principal.origin == it[0] &&
+          permission.type == it[1] &&
+          permission.capability == it[2] &&
+          permission.expireType == it[3] &&
+          permission.expireTime == it[4]) {
+        isExpected = true;
+        found[i]++;
+      }
+    });
+
+    do_check_true(isExpected,
+                  "Permission " + (isExpected ? "should" : "shouldn't") +
+                  " be in permission database: " +
+                  permission.principal.origin + ", " +
+                  permission.type + ", " +
+                  permission.capability + ", " +
+                  permission.expireType + ", " +
+                  permission.expireTime);
+  }
+
+  found.forEach((count, i) => {
+    do_check_true(count == 1, "Expected count = 1, got count = " + count + " for permission " + expected[i]);
+  });
+});
diff --git a/extensions/cookie/test/unit/xpcshell.ini b/extensions/cookie/test/unit/xpcshell.ini
index efd41f7be07e..38ad29f50e12 100644
--- a/extensions/cookie/test/unit/xpcshell.ini
+++ b/extensions/cookie/test/unit/xpcshell.ini
@@ -30,10 +30,10 @@ skip-if = debug == true
 [test_permmanager_idn.js]
 [test_permmanager_subdomains.js]
 [test_permmanager_local_files.js]
-[test_permmanager_mailto.js]
 [test_permmanager_cleardata.js]
 [test_schema_2_migration.js]
 [test_schema_3_migration.js]
 [test_permmanager_removepermission.js]
 [test_permmanager_matchesuri.js]
 [test_permmanager_matches.js]
+[test_permmanager_migrate_4-5.js]

From 5b365a72da75d983bfcdb1327199e425e3880abf Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 16 Jun 2015 15:29:44 -0400
Subject: [PATCH 28/67] Bug 1165263 - Part 3: Update mozprofile to support new
 moz_hosts schema, r=ahal

---
 .../mozprofile/mozprofile/permissions.py      | 44 +++++++++++++++----
 testing/mozbase/mozprofile/tests/bug785146.py |  2 +-
 .../mozbase/mozprofile/tests/permissions.py   | 41 ++++++++++++-----
 3 files changed, 66 insertions(+), 21 deletions(-)

diff --git a/testing/mozbase/mozprofile/mozprofile/permissions.py b/testing/mozbase/mozprofile/mozprofile/permissions.py
index 126d0f91f99b..56bfa18b57a5 100644
--- a/testing/mozbase/mozprofile/mozprofile/permissions.py
+++ b/testing/mozbase/mozprofile/mozprofile/permissions.py
@@ -231,18 +231,26 @@ class Permissions(object):
         # SQL copied from
         # http://mxr.mozilla.org/mozilla-central/source/extensions/cookie/nsPermissionManager.cpp
         cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts (
-           id INTEGER PRIMARY KEY,
-           host TEXT,
-           type TEXT,
-           permission INTEGER,
-           expireType INTEGER,
-           expireTime INTEGER)""")
+              id INTEGER PRIMARY KEY
+             ,origin TEXT
+             ,type TEXT
+             ,permission INTEGER
+             ,expireType INTEGER
+             ,expireTime INTEGER
+             ,modificationTime INTEGER
+           )""")
 
         rows = cursor.execute("PRAGMA table_info(moz_hosts)")
         count = len(rows.fetchall())
 
+        using_origin = False
+        # if the db contains 7 columns, we're using user_version 5
+        if count == 7:
+            statement = "INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0)"
+            cursor.execute("PRAGMA user_version=5;")
+            using_origin = True
         # if the db contains 9 columns, we're using user_version 4
-        if count == 9:
+        elif count == 9:
             statement = "INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0, 0)"
             cursor.execute("PRAGMA user_version=4;")
         # if the db contains 8 columns, we're using user_version 3
@@ -262,8 +270,26 @@ class Permissions(object):
                 else:
                     permission_type = 2
 
-                cursor.execute(statement,
-                               (location.host, perm, permission_type))
+                if using_origin:
+                    # This is a crude approximation of the origin generation logic from
+                    # nsPrincipal and nsStandardURL. It should suffice for the permissions
+                    # which the test runners will want to insert into the system.
+                    origin = location.scheme + "://" + location.host
+                    if (location.scheme != 'http' or location.port != '80') and \
+                       (location.scheme != 'https' or location.port != '443'):
+                        origin += ':' + str(location.port)
+
+                    cursor.execute(statement,
+                                   (origin, perm, permission_type))
+                else:
+                    # The database is still using a legacy system based on hosts
+                    # We can insert the permission as a host
+                    #
+                    # XXX This codepath should not be hit, as tests are run with
+                    # fresh profiles. However, if it was hit, permissions would
+                    # not be added to the database correctly (bug 1183185).
+                    cursor.execute(statement,
+                                   (location.host, perm, permission_type))
 
         # Commit and close
         permDB.commit()
diff --git a/testing/mozbase/mozprofile/tests/bug785146.py b/testing/mozbase/mozprofile/tests/bug785146.py
index 26b98ffc0c46..629a81523aba 100755
--- a/testing/mozbase/mozprofile/tests/bug785146.py
+++ b/testing/mozbase/mozprofile/tests/bug785146.py
@@ -44,7 +44,7 @@ http://127.0.0.1:8888           privileged
         entries = cur.fetchall()
 
         schema_version = entries[0][0]
-        self.assertEqual(schema_version, 2)
+        self.assertEqual(schema_version, 5)
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/testing/mozbase/mozprofile/tests/permissions.py b/testing/mozbase/mozprofile/tests/permissions.py
index b402991d929e..389f14a5c8a0 100755
--- a/testing/mozbase/mozprofile/tests/permissions.py
+++ b/testing/mozbase/mozprofile/tests/permissions.py
@@ -40,7 +40,16 @@ http://127.0.0.1:8888           privileged
 
         cursor.execute("PRAGMA user_version=%d;" % version)
 
-        if version == 4:
+        if version == 5:
+            cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts (
+              id INTEGER PRIMARY KEY,
+              origin TEXT,
+              type TEXT,
+              permission INTEGER,
+              expireType INTEGER,
+              expireTime INTEGER,
+              modificationTime INTEGER)""")
+        elif version == 4:
             cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts (
                id INTEGER PRIMARY KEY,
                host TEXT,
@@ -70,7 +79,7 @@ http://127.0.0.1:8888           privileged
                expireType INTEGER,
                expireTime INTEGER)""")
         else:
-            raise Exception("version must be 2, 3 or 4")
+            raise Exception("version must be 2, 3, 4 or 5")
 
         permDB.commit()
         cursor.close()
@@ -79,7 +88,7 @@ http://127.0.0.1:8888           privileged
         perms = Permissions(self.profile_dir, self.locations_file.name)
         perms_db_filename = os.path.join(self.profile_dir, 'permissions.sqlite')
 
-        select_stmt = 'select host, type, permission from moz_hosts'
+        select_stmt = 'select origin, type, permission from moz_hosts'
 
         con = sqlite3.connect(perms_db_filename)
         cur = con.cursor()
@@ -88,32 +97,32 @@ http://127.0.0.1:8888           privileged
 
         self.assertEqual(len(entries), 3)
 
-        self.assertEqual(entries[0][0], 'mochi.test')
+        self.assertEqual(entries[0][0], 'http://mochi.test:8888')
         self.assertEqual(entries[0][1], 'allowXULXBL')
         self.assertEqual(entries[0][2], 1)
 
-        self.assertEqual(entries[1][0], '127.0.0.1')
+        self.assertEqual(entries[1][0], 'http://127.0.0.1')
         self.assertEqual(entries[1][1], 'allowXULXBL')
         self.assertEqual(entries[1][2], 2)
 
-        self.assertEqual(entries[2][0], '127.0.0.1')
+        self.assertEqual(entries[2][0], 'http://127.0.0.1:8888')
         self.assertEqual(entries[2][1], 'allowXULXBL')
         self.assertEqual(entries[2][2], 1)
 
-        perms._locations.add_host('a.b.c', options='noxul')
+        perms._locations.add_host('a.b.c', port='8081', scheme='https', options='noxul')
 
         cur.execute(select_stmt)
         entries = cur.fetchall()
 
         self.assertEqual(len(entries), 4)
-        self.assertEqual(entries[3][0], 'a.b.c')
+        self.assertEqual(entries[3][0], 'https://a.b.c:8081')
         self.assertEqual(entries[3][1], 'allowXULXBL')
         self.assertEqual(entries[3][2], 2)
 
-        # when creating a DB we should default to user_version==2
+        # when creating a DB we should default to user_version==5
         cur.execute('PRAGMA user_version')
         entries = cur.fetchall()
-        self.assertEqual(entries[0][0], 2)
+        self.assertEqual(entries[0][0], 5)
 
         perms.clean_db()
         # table should be removed
@@ -160,7 +169,14 @@ http://127.0.0.1:8888           privileged
 
         self.assertEqual(len(entries), 3)
 
-        columns = 9 if version == 4 else (8 if version == 3 else 6)
+        columns = {
+            1: 6,
+            2: 6,
+            3: 8,
+            4: 9,
+            5: 7,
+        }[version]
+
         self.assertEqual(len(entries[0]), columns)
         for x in range(4, columns):
             self.assertEqual(entries[0][x], 0)
@@ -174,5 +190,8 @@ http://127.0.0.1:8888           privileged
     def test_existing_permissions_db_v4(self):
         self.verify_user_version(4)
 
+    def test_existing_permissions_db_v5(self):
+        self.verify_user_version(5)
+
 if __name__ == '__main__':
     unittest.main()

From 72b02534d8109e065891632c395038e3a150aa45 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 16 Jun 2015 15:30:13 -0400
Subject: [PATCH 29/67] Bug 1165263 - Part 4: Update reftest runners to support
 new moz_hosts schema, r=dbaron

---
 layout/tools/reftest/reftest.js       | 3 +++
 layout/tools/reftest/remotereftest.py | 2 +-
 layout/tools/reftest/runreftest.py    | 3 ++-
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js
index edd61b9cbf23..27daf26b8db9 100644
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -1177,6 +1177,9 @@ function ServeFiles(manifestPrincipal, depth, aURL, files)
     var testbase = gIOService.newURI("http://localhost:" + gHttpServerPort +
                                      path + dirPath, null, null);
 
+    // Give the testbase URI access to XUL and XBL
+    Services.perms.add(testbase, "allowXULXBL", Services.perms.ALLOW_ACTION);
+
     function FileToURI(file)
     {
         // Only serve relative URIs via the HTTP server, not absolute
diff --git a/layout/tools/reftest/remotereftest.py b/layout/tools/reftest/remotereftest.py
index c0f5dd1092d1..e7430bade751 100644
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -342,7 +342,7 @@ class RemoteReftest(RefTest):
         self.server.stop()
 
     def createReftestProfile(self, options, reftestlist):
-        profile = RefTest.createReftestProfile(self, options, reftestlist, server=options.remoteWebServer)
+        profile = RefTest.createReftestProfile(self, options, reftestlist, server=options.remoteWebServer, port=options.httpPort)
         profileDir = profile.profile
 
         prefs = {}
diff --git a/layout/tools/reftest/runreftest.py b/layout/tools/reftest/runreftest.py
index 752d0f41a3c8..3d7dd67ea01c 100644
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -187,7 +187,8 @@ class RefTest(object):
         """
 
         locations = mozprofile.permissions.ServerLocations()
-        locations.add_host(server, port=0)
+        locations.add_host(server, scheme='http', port=0)
+        locations.add_host(server, scheme='https', port=0)
 
         # Set preferences for communication between our command line arguments
         # and the reftest harness.  Preferences that are required for reftest

From cafed798d4a1daa999a727e86022540e3c149bc4 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 16 Jun 2015 15:33:26 -0400
Subject: [PATCH 30/67] Bug 1165263 - Part 5: Update
 PermissionsUtils.importPrefBranch to use origins instead of hosts, r=ehsan

---
 mobile/android/app/mobile.js                  |  4 +--
 toolkit/modules/PermissionsUtils.jsm          | 34 ++++++++++++------
 .../tests/xpcshell/test_PermissionsUtils.js   | 36 ++++++++++++++-----
 .../test/xpcshell/test_permissions.js         |  6 ++--
 .../test/xpcshell/test_permissions_prefs.js   | 12 +++----
 5 files changed, 61 insertions(+), 31 deletions(-)

diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js
index 8f0e96709e94..9e9fabb2b44c 100644
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -177,8 +177,8 @@ pref("dom.forms.number", true);
 /* extension manager and xpinstall */
 pref("xpinstall.whitelist.directRequest", false);
 pref("xpinstall.whitelist.fileRequest", false);
-pref("xpinstall.whitelist.add", "addons.mozilla.org");
-pref("xpinstall.whitelist.add.180", "marketplace.firefox.com");
+pref("xpinstall.whitelist.add", "https://addons.mozilla.org");
+pref("xpinstall.whitelist.add.180", "https://marketplace.firefox.com");
 
 pref("xpinstall.signatures.required", false);
 
diff --git a/toolkit/modules/PermissionsUtils.jsm b/toolkit/modules/PermissionsUtils.jsm
index 37e7abb5b15e..ec04826959ca 100644
--- a/toolkit/modules/PermissionsUtils.jsm
+++ b/toolkit/modules/PermissionsUtils.jsm
@@ -7,6 +7,7 @@ this.EXPORTED_SYMBOLS = ["PermissionsUtils"];
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/BrowserUtils.jsm")
 
 
 let gImportedPrefBranches = new Set();
@@ -15,29 +16,40 @@ function importPrefBranch(aPrefBranch, aPermission, aAction) {
   let list = Services.prefs.getChildList(aPrefBranch, {});
 
   for (let pref of list) {
-    let hosts = "";
+    let origins = "";
     try {
-      hosts = Services.prefs.getCharPref(pref);
+      origins = Services.prefs.getCharPref(pref);
     } catch (e) {}
 
-    if (!hosts)
+    if (!origins)
       continue;
 
-    hosts = hosts.split(",");
+    origins = origins.split(",");
 
-    for (let host of hosts) {
-      let uri = null;
+    for (let origin of origins) {
+      let principals = [];
       try {
-        uri = Services.io.newURI("http://" + host, null, null);
+        principals = [ BrowserUtils.principalFromOrigin(origin) ];
       } catch (e) {
+        // This preference used to contain a list of hosts. For back-compat
+        // reasons, we convert these hosts into http:// and https:// permissions
+        // on default ports.
         try {
-          uri = Services.io.newURI(host, null, null);
+          let httpURI = Services.io.newURI("http://" + origin, null, null);
+          let httpsURI = Services.io.newURI("https://" + origin, null, null);
+
+          principals = [
+            Services.scriptSecurityManager.getNoAppCodebasePrincipal(httpURI),
+            Services.scriptSecurityManager.getNoAppCodebasePrincipal(httpsURI)
+          ];
         } catch (e2) {}
       }
 
-      try {
-        Services.perms.add(uri, aPermission, aAction);
-      } catch (e) {}
+      for (let principal of principals) {
+        try {
+          Services.perms.addFromPrincipal(principal, aPermission, aAction);
+        } catch (e) {}
+      }
     }
 
     Services.prefs.setCharPref(pref, "");
diff --git a/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js b/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js
index a3bf2fa4afa0..48769f831234 100644
--- a/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js
+++ b/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js
@@ -23,22 +23,36 @@ function test_importfromPrefs() {
   // Create own preferences to test
   Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.EMPTY", "");
   Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.EMPTY2", ",");
-  Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST", "whitelist.example.com");
-  Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST2", "whitelist2-1.example.com,whitelist2-2.example.com,about:home");
+  Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST", "http://whitelist.example.com");
+  Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST2", "https://whitelist2-1.example.com,http://whitelist2-2.example.com:8080,about:home");
+  Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST3", "whitelist3-1.example.com,about:config"); // legacy style - host only
   Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.EMPTY", "");
-  Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST", "blacklist.example.com,");
-  Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST2", ",blacklist2-1.example.com,blacklist2-2.example.com,about:mozilla");
+  Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST", "http://blacklist.example.com,");
+  Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST2", ",https://blacklist2-1.example.com,http://blacklist2-2.example.com:8080,about:mozilla");
+  Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST3", "blacklist3-1.example.com,about:preferences"); // legacy style - host only
 
   // Check they are unknown in the permission manager prior to importing.
   let whitelisted = ["http://whitelist.example.com",
-                     "http://whitelist2-1.example.com",
-                     "http://whitelist2-2.example.com",
+                     "https://whitelist2-1.example.com",
+                     "http://whitelist2-2.example.com:8080",
+                     "http://whitelist3-1.example.com",
+                     "https://whitelist3-1.example.com",
+                     "about:config",
                      "about:home"];
   let blacklisted = ["http://blacklist.example.com",
-                     "http://blacklist2-1.example.com",
-                     "http://blacklist2-2.example.com",
+                     "https://blacklist2-1.example.com",
+                     "http://blacklist2-2.example.com:8080",
+                     "http://blacklist3-1.example.com",
+                     "https://blacklist3-1.example.com",
+                     "about:preferences",
                      "about:mozilla"];
-  let unknown = whitelisted.concat(blacklisted);
+  let untouched = ["https://whitelist.example.com",
+                   "https://blacklist.example.com",
+                   "http://whitelist2-1.example.com",
+                   "http://blacklist2-1.example.com",
+                   "https://whitelist2-2.example.com:8080",
+                   "https://blacklist2-2.example.com:8080"];
+  let unknown = whitelisted.concat(blacklisted).concat(untouched);
   for (let url of unknown) {
     let uri = Services.io.newURI(url, null, null);
     do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.UNKNOWN_ACTION);
@@ -64,4 +78,8 @@ function test_importfromPrefs() {
     let uri = Services.io.newURI(url, null, null);
     do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.DENY_ACTION);
   }
+  for (let url of untouched) {
+    let uri = Services.io.newURI(url, null, null);
+    do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.UNKNOWN_ACTION);
+  }
 }
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_permissions.js b/toolkit/mozapps/extensions/test/xpcshell/test_permissions.js
index 869b7bdc1eed..eb8bc3e5c912 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/test_permissions.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_permissions.js
@@ -12,9 +12,9 @@ const XPI_MIMETYPE = "application/x-xpinstall";
 function run_test() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
 
-  Services.prefs.setCharPref("xpinstall.whitelist.add", "test1.com,test2.com");
-  Services.prefs.setCharPref("xpinstall.whitelist.add.36", "test3.com,www.test4.com");
-  Services.prefs.setCharPref("xpinstall.whitelist.add.test5", "test5.com");
+  Services.prefs.setCharPref("xpinstall.whitelist.add", "https://test1.com,https://test2.com");
+  Services.prefs.setCharPref("xpinstall.whitelist.add.36", "https://test3.com,https://www.test4.com");
+  Services.prefs.setCharPref("xpinstall.whitelist.add.test5", "https://test5.com");
 
   Services.perms.add(NetUtil.newURI("https://www.test9.com"), "install",
                      AM_Ci.nsIPermissionManager.ALLOW_ACTION);
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_permissions_prefs.js b/toolkit/mozapps/extensions/test/xpcshell/test_permissions_prefs.js
index 75c615ace00c..d3eb5ca02f71 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/test_permissions_prefs.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_permissions_prefs.js
@@ -30,9 +30,9 @@ function run_test() {
 
   // Create own preferences to test
   Services.prefs.setCharPref("xpinstall.whitelist.add.EMPTY", "");
-  Services.prefs.setCharPref("xpinstall.whitelist.add.TEST", "whitelist.example.com");
+  Services.prefs.setCharPref("xpinstall.whitelist.add.TEST", "http://whitelist.example.com");
   Services.prefs.setCharPref("xpinstall.blacklist.add.EMPTY", "");
-  Services.prefs.setCharPref("xpinstall.blacklist.add.TEST", "blacklist.example.com");
+  Services.prefs.setCharPref("xpinstall.blacklist.add.TEST", "http://blacklist.example.com");
 
   // Get list of preferences to check
   var whitelistPreferences = Services.prefs.getChildList(PREF_XPI_WHITELIST_PERMISSIONS, {});
@@ -53,19 +53,19 @@ function run_test() {
 
   // First, request to flush all permissions
   clear_imported_preferences_cache();
-  Services.prefs.setCharPref("xpinstall.whitelist.add.TEST2", "whitelist2.example.com");
+  Services.prefs.setCharPref("xpinstall.whitelist.add.TEST2", "https://whitelist2.example.com");
   Services.obs.notifyObservers(null, "flush-pending-permissions", "install");
   do_check_permission_prefs(preferences);
 
   // Then, request to flush just install permissions
   clear_imported_preferences_cache();
-  Services.prefs.setCharPref("xpinstall.whitelist.add.TEST3", "whitelist3.example.com");
+  Services.prefs.setCharPref("xpinstall.whitelist.add.TEST3", "https://whitelist3.example.com");
   Services.obs.notifyObservers(null, "flush-pending-permissions", "");
   do_check_permission_prefs(preferences);
 
   // And a request to flush some other permissions sholdn't flush install permissions
   clear_imported_preferences_cache();
-  Services.prefs.setCharPref("xpinstall.whitelist.add.TEST4", "whitelist4.example.com");
+  Services.prefs.setCharPref("xpinstall.whitelist.add.TEST4", "https://whitelist4.example.com");
   Services.obs.notifyObservers(null, "flush-pending-permissions", "lolcats");
-  do_check_eq(Services.prefs.getCharPref("xpinstall.whitelist.add.TEST4"), "whitelist4.example.com");
+  do_check_eq(Services.prefs.getCharPref("xpinstall.whitelist.add.TEST4"), "https://whitelist4.example.com");
 }

From 41ac99b0273249a5a2780849c62363f39f9dce9a Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 16 Jun 2015 15:34:12 -0400
Subject: [PATCH 31/67] Bug 1165263 - Part 6: Update browser tests to support
 new permissions semantics, r=ehsan

---
 browser/base/content/test/general/browser_bug592338.js | 2 +-
 browser/components/uitour/test/head.js                 | 9 ++++++---
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/browser/base/content/test/general/browser_bug592338.js b/browser/base/content/test/general/browser_bug592338.js
index 99544d87a8b4..015459b0dc54 100644
--- a/browser/base/content/test/general/browser_bug592338.js
+++ b/browser/base/content/test/general/browser_bug592338.js
@@ -48,7 +48,7 @@ function test_install_lwtheme() {
   is(LightweightThemeManager.currentTheme, null, "Should be no lightweight theme selected");
 
   var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+  pm.add(makeURI("https://example.com/"), "install", pm.ALLOW_ACTION);
 
   gBrowser.selectedTab = gBrowser.addTab("https://example.com/browser/browser/base/content/test/general/bug592338.html");
   gBrowser.selectedBrowser.addEventListener("pageshow", function() {
diff --git a/browser/components/uitour/test/head.js b/browser/components/uitour/test/head.js
index 73e5e4c66b2a..330a174c622d 100644
--- a/browser/components/uitour/test/head.js
+++ b/browser/components/uitour/test/head.js
@@ -198,8 +198,10 @@ function loadUITourTestPage(callback, host = "https://example.com/") {
 
 function UITourTest() {
   Services.prefs.setBoolPref("browser.uitour.enabled", true);
-  let testUri = Services.io.newURI("http://example.com", null, null);
-  Services.perms.add(testUri, "uitour", Services.perms.ALLOW_ACTION);
+  let testHttpsUri = Services.io.newURI("https://example.com", null, null);
+  let testHttpUri = Services.io.newURI("http://example.com", null, null);
+  Services.perms.add(testHttpsUri, "uitour", Services.perms.ALLOW_ACTION);
+  Services.perms.add(testHttpUri, "uitour", Services.perms.ALLOW_ACTION);
 
   waitForExplicitFinish();
 
@@ -210,7 +212,8 @@ function UITourTest() {
       gBrowser.removeTab(gTestTab);
     delete window.gTestTab;
     Services.prefs.clearUserPref("browser.uitour.enabled", true);
-    Services.perms.remove(testUri, "uitour");
+    Services.perms.remove(testHttpsUri, "uitour");
+    Services.perms.remove(testHttpUri, "uitour");
   });
 
   function done() {

From 596632a0078ddd5299285a4d029126ba316e9649 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Tue, 7 Jul 2015 14:22:45 -0400
Subject: [PATCH 32/67] Bug 1165263 - Part 7: Update tracking protection to
 account for new nsPermissionManager semantics, r=margaret

---
 browser/base/content/browser-trackingprotection.js |  6 +++++-
 mobile/android/chrome/content/browser.js           | 10 +++++-----
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/browser/base/content/browser-trackingprotection.js b/browser/base/content/browser-trackingprotection.js
index 7c7ed7db5e51..e32bd1c08dfc 100644
--- a/browser/base/content/browser-trackingprotection.js
+++ b/browser/base/content/browser-trackingprotection.js
@@ -99,7 +99,11 @@ let TrackingProtection = {
     // Remove the current host from the 'trackingprotection' consumer
     // of the permission manager. This effectively removes this host
     // from the tracking protection allowlist.
-    Services.perms.remove(gBrowser.selectedBrowser.currentURI,
+    let normalizedUrl = Services.io.newURI(
+      "https://" + gBrowser.selectedBrowser.currentURI.hostPort,
+      null, null);
+
+    Services.perms.remove(normalizedUrl,
       "trackingprotection");
 
     // Telemetry for enable protection.
diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js
index 6df73544b828..5e3537f39f55 100644
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1751,11 +1751,11 @@ var BrowserApp = {
               docShell.mixedContentChannel = null;
             }
           } else if (data.contentType === "tracking") {
+            // Convert document URI into the format used by
+            // nsChannelClassifier::ShouldEnableTrackingProtection
+            // (any scheme turned into https is correct)
+            let normalizedUrl = Services.io.newURI("https://" + browser.currentURI.hostPort, null, null);
             if (data.allowContent) {
-              // Convert document URI into the format used by
-              // nsChannelClassifier::ShouldEnableTrackingProtection
-              // (any scheme turned into https is correct)
-              let normalizedUrl = Services.io.newURI("https://" + browser.currentURI.hostPort, null, null);
               // Add the current host in the 'trackingprotection' consumer of
               // the permission manager using a normalized URI. This effectively
               // places this host on the tracking protection white list.
@@ -1765,7 +1765,7 @@ var BrowserApp = {
               // Remove the current host from the 'trackingprotection' consumer
               // of the permission manager. This effectively removes this host
               // from the tracking protection white list (any list actually).
-              Services.perms.remove(browser.currentURI, "trackingprotection");
+              Services.perms.remove(normalizedUrl, "trackingprotection");
               Telemetry.addData("TRACKING_PROTECTION_EVENTS", 2);
             }
           }

From 4d57018268a116ed61d25e465762df5302c11f77 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Fri, 26 Jun 2015 17:49:41 -0700
Subject: [PATCH 33/67] Bug 1172080 - Part 1: Throw when requesting origin for
 poorly behaved URIs, r=bholley

---
 caps/nsPrincipal.cpp | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/caps/nsPrincipal.cpp b/caps/nsPrincipal.cpp
index ed3b6b017a0a..302e6188cfae 100644
--- a/caps/nsPrincipal.cpp
+++ b/caps/nsPrincipal.cpp
@@ -14,6 +14,7 @@
 #include "pratom.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
+#include "nsIStandardURL.h"
 #include "nsIURIWithPrincipal.h"
 #include "nsJSPrincipals.h"
 #include "nsIEffectiveTLDService.h"
@@ -127,6 +128,31 @@ nsPrincipal::GetOriginForURI(nsIURI* aURI, nsACString& aOrigin)
     }
   }
 
+  // We want the invariant that prinA.origin == prinB.origin i.f.f.
+  // prinA.equals(prinB). However, this requires that we impose certain constraints
+  // on the behavior and origin semantics of principals, and in particular, forbid
+  // creating origin strings for principals whose equality constraints are not
+  // expressible as strings (i.e. object equality). Moreover, we want to forbid URIs
+  // containing the magic "^" we use as a separating character for origin
+  // attributes.
+  //
+  // These constraints can generally be achieved by restricting .origin to
+  // nsIStandardURL-based URIs, but there are a few other URI schemes that we need
+  // to handle.
+  bool isBehaved;
+  if ((NS_SUCCEEDED(origin->SchemeIs("about", &isBehaved)) && isBehaved) ||
+      (NS_SUCCEEDED(origin->SchemeIs("moz-safe-about", &isBehaved)) && isBehaved) ||
+      (NS_SUCCEEDED(origin->SchemeIs("indexeddb", &isBehaved)) && isBehaved)) {
+    rv = origin->GetAsciiSpec(aOrigin);
+    NS_ENSURE_SUCCESS(rv, rv);
+    // These URIs could technically contain a '^', but they never should.
+    if (NS_WARN_IF(aOrigin.FindChar('^', 0) != -1)) {
+      aOrigin.Truncate();
+      return NS_ERROR_FAILURE;
+    }
+    return NS_OK;
+  }
+
   int32_t port;
   if (NS_SUCCEEDED(rv) && !isChrome) {
     rv = origin->GetPort(&port);
@@ -144,6 +170,14 @@ nsPrincipal::GetOriginForURI(nsIURI* aURI, nsACString& aOrigin)
     aOrigin.Append(hostPort);
   }
   else {
+    // If we reached this branch, we can only create an origin if we have a nsIStandardURL.
+    // So, we query to a nsIStandardURL, and fail if we aren't an instance of an nsIStandardURL
+    // nsIStandardURLs have the good property of escaping the '^' character in their specs,
+    // which means that we can be sure that the caret character (which is reserved for delimiting
+    // the end of the spec, and the beginning of the origin attributes) is not present in the
+    // origin string
+    nsCOMPtr standardURL = do_QueryInterface(origin);
+    NS_ENSURE_TRUE(standardURL, NS_ERROR_FAILURE);
     rv = origin->GetAsciiSpec(aOrigin);
     NS_ENSURE_SUCCESS(rv, rv);
   }

From 52f1ea540298d4fce8a71af391f92a40781da265 Mon Sep 17 00:00:00 2001
From: Michael Layzell 
Date: Thu, 2 Jul 2015 16:50:26 -0400
Subject: [PATCH 34/67] Bug 1172080 - Part 2: Use ^ instead of ! to delimit
 originAttributes from the URI in nsIPrincipal.origin, r=bholley

---
 caps/BasePrincipal.cpp                        |  6 ++---
 caps/tests/unit/test_origin.js                | 26 +++++++++----------
 dom/apps/tests/unit/test_moziapplication.js   |  2 +-
 .../test/unit/test_permmanager_defaults.js    |  2 +-
 .../test/unit/test_permmanager_migrate_4-5.js | 20 +++++++-------
 toolkit/modules/BrowserUtils.jsm              |  2 +-
 6 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp
index 3cf84fa26913..2b21258ec9fe 100644
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -51,7 +51,7 @@ OriginAttributes::CreateSuffix(nsACString& aStr) const
 
   params->Serialize(value);
   if (!value.IsEmpty()) {
-    aStr.AppendLiteral("!");
+    aStr.AppendLiteral("^");
     aStr.Append(NS_ConvertUTF16toUTF8(value));
   }
 }
@@ -117,7 +117,7 @@ OriginAttributes::PopulateFromSuffix(const nsACString& aStr)
     return true;
   }
 
-  if (aStr[0] != '!') {
+  if (aStr[0] != '^') {
     return false;
   }
 
@@ -134,7 +134,7 @@ OriginAttributes::PopulateFromOrigin(const nsACString& aOrigin,
 {
   // RFindChar is only available on nsCString.
   nsCString origin(aOrigin);
-  int32_t pos = origin.RFindChar('!');
+  int32_t pos = origin.RFindChar('^');
 
   if (pos == kNotFound) {
     aOriginNoSuffix = origin;
diff --git a/caps/tests/unit/test_origin.js b/caps/tests/unit/test_origin.js
index ef462719e7c4..020ae3a24043 100644
--- a/caps/tests/unit/test_origin.js
+++ b/caps/tests/unit/test_origin.js
@@ -68,33 +68,33 @@ function run_test() {
   // Just app.
   var exampleOrg_app = ssm.createCodebasePrincipal(makeURI('http://example.org'), {appId: 42});
   var nullPrin_app = ssm.createNullPrincipal({appId: 42});
-  checkOriginAttributes(exampleOrg_app, {appId: 42}, '!appId=42');
-  checkOriginAttributes(nullPrin_app, {appId: 42}, '!appId=42');
-  do_check_eq(exampleOrg_app.origin, 'http://example.org!appId=42');
+  checkOriginAttributes(exampleOrg_app, {appId: 42}, '^appId=42');
+  checkOriginAttributes(nullPrin_app, {appId: 42}, '^appId=42');
+  do_check_eq(exampleOrg_app.origin, 'http://example.org^appId=42');
 
   // Just browser.
   var exampleOrg_browser = ssm.createCodebasePrincipal(makeURI('http://example.org'), {inBrowser: true});
   var nullPrin_browser = ssm.createNullPrincipal({inBrowser: true});
-  checkOriginAttributes(exampleOrg_browser, {inBrowser: true}, '!inBrowser=1');
-  checkOriginAttributes(nullPrin_browser, {inBrowser: true}, '!inBrowser=1');
-  do_check_eq(exampleOrg_browser.origin, 'http://example.org!inBrowser=1');
+  checkOriginAttributes(exampleOrg_browser, {inBrowser: true}, '^inBrowser=1');
+  checkOriginAttributes(nullPrin_browser, {inBrowser: true}, '^inBrowser=1');
+  do_check_eq(exampleOrg_browser.origin, 'http://example.org^inBrowser=1');
 
   // App and browser.
   var exampleOrg_appBrowser = ssm.createCodebasePrincipal(makeURI('http://example.org'), {inBrowser: true, appId: 42});
   var nullPrin_appBrowser = ssm.createNullPrincipal({inBrowser: true, appId: 42});
-  checkOriginAttributes(exampleOrg_appBrowser, {appId: 42, inBrowser: true}, '!appId=42&inBrowser=1');
-  checkOriginAttributes(nullPrin_appBrowser, {appId: 42, inBrowser: true}, '!appId=42&inBrowser=1');
-  do_check_eq(exampleOrg_appBrowser.origin, 'http://example.org!appId=42&inBrowser=1');
+  checkOriginAttributes(exampleOrg_appBrowser, {appId: 42, inBrowser: true}, '^appId=42&inBrowser=1');
+  checkOriginAttributes(nullPrin_appBrowser, {appId: 42, inBrowser: true}, '^appId=42&inBrowser=1');
+  do_check_eq(exampleOrg_appBrowser.origin, 'http://example.org^appId=42&inBrowser=1');
 
   // App and browser, different domain.
   var exampleCom_appBrowser = ssm.createCodebasePrincipal(makeURI('https://www.example.com:123'), {appId: 42, inBrowser: true});
-  checkOriginAttributes(exampleCom_appBrowser, {appId: 42, inBrowser: true}, '!appId=42&inBrowser=1');
-  do_check_eq(exampleCom_appBrowser.origin, 'https://www.example.com:123!appId=42&inBrowser=1');
+  checkOriginAttributes(exampleCom_appBrowser, {appId: 42, inBrowser: true}, '^appId=42&inBrowser=1');
+  do_check_eq(exampleCom_appBrowser.origin, 'https://www.example.com:123^appId=42&inBrowser=1');
 
   // Addon.
   var exampleOrg_addon = ssm.createCodebasePrincipal(makeURI('http://example.org'), {addonId: 'dummy'});
-  checkOriginAttributes(exampleOrg_addon, { addonId: "dummy" }, '!addonId=dummy');
-  do_check_eq(exampleOrg_addon.origin, 'http://example.org!addonId=dummy');
+  checkOriginAttributes(exampleOrg_addon, { addonId: "dummy" }, '^addonId=dummy');
+  do_check_eq(exampleOrg_addon.origin, 'http://example.org^addonId=dummy');
 
   // Check that all of the above are cross-origin.
   checkCrossOrigin(exampleOrg_app, exampleOrg);
diff --git a/dom/apps/tests/unit/test_moziapplication.js b/dom/apps/tests/unit/test_moziapplication.js
index b5609d8564c1..b7989f82e04a 100644
--- a/dom/apps/tests/unit/test_moziapplication.js
+++ b/dom/apps/tests/unit/test_moziapplication.js
@@ -51,7 +51,7 @@ add_test(() => {
   });
 
   Assert.ok(mozapp.principal, "app principal should exist");
-  let expectedPrincipalOrigin = app.origin + "!appId=" + app.localId;
+  let expectedPrincipalOrigin = app.origin + "^appId=" + app.localId;
   Assert.equal(mozapp.principal.origin, expectedPrincipalOrigin,
                "app principal origin ok");
   Assert.equal(mozapp.principal.appId, app.localId, "app principal appId ok");
diff --git a/extensions/cookie/test/unit/test_permmanager_defaults.js b/extensions/cookie/test/unit/test_permmanager_defaults.js
index 91874a086707..65ea18f58b24 100644
--- a/extensions/cookie/test/unit/test_permmanager_defaults.js
+++ b/extensions/cookie/test/unit/test_permmanager_defaults.js
@@ -40,7 +40,7 @@ add_task(function* do_test() {
   conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.host + "\n");
   conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2.host + "\n");
   conv.writeString("origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_3.spec + "\n");
-  conv.writeString("origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.spec + "!appId=1000&inBrowser=1\n");
+  conv.writeString("origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.spec + "^appId=1000&inBrowser=1\n");
   ostream.close();
 
   // Set the preference used by the permission manager so the file is read.
diff --git a/extensions/cookie/test/unit/test_permmanager_migrate_4-5.js b/extensions/cookie/test/unit/test_permmanager_migrate_4-5.js
index f3b816817360..253db93dbacc 100644
--- a/extensions/cookie/test/unit/test_permmanager_migrate_4-5.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_4-5.js
@@ -94,33 +94,33 @@ add_task(function test() {
     // However, permissions for subdomains of foo.com will be present for both http:// and https://,
     // as they do not apply to any entry in the history
     // ["http://foo.com", "A", 1, 0, 0],
-    // ["http://foo.com!appId=1000", "A", 1, 0, 0],
-    // ["http://foo.com!appId=2000&inBrowser=1", "A", 1, 0, 0],
+    // ["http://foo.com^appId=1000", "A", 1, 0, 0],
+    // ["http://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0],
 
     ["http://sub.foo.com", "B", 1, 0, 0],
     ["http://subber.sub.foo.com", "B", 1, 0, 0],
 
     ["https://foo.com", "A", 1, 0, 0],
-    ["https://foo.com!appId=1000", "A", 1, 0, 0],
-    ["https://foo.com!appId=2000&inBrowser=1", "A", 1, 0, 0],
+    ["https://foo.com^appId=1000", "A", 1, 0, 0],
+    ["https://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0],
     ["https://sub.foo.com", "B", 1, 0, 0],
     ["https://subber.sub.foo.com", "B", 1, 0, 0],
 
     // bar.ca will have both http:// and https:// for all entries, because the foo did the bar a favour
     ["http://bar.ca", "B", 1, 0, 0],
     ["https://bar.ca", "B", 1, 0, 0],
-    ["http://bar.ca!appId=1000", "B", 1, 0, 0],
-    ["https://bar.ca!appId=1000", "B", 1, 0, 0],
-    ["http://bar.ca!appId=1000&inBrowser=1", "A", 1, 0, 0],
-    ["https://bar.ca!appId=1000&inBrowser=1", "A", 1, 0, 0],
+    ["http://bar.ca^appId=1000", "B", 1, 0, 0],
+    ["https://bar.ca^appId=1000", "B", 1, 0, 0],
+    ["http://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0],
+    ["https://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0],
     ["file:///some/path/to/file.html", "A", 1, 0, 0],
     ["file:///another/file.html", "A", 1, 0, 0],
 
     // Because we put ftp://some.subdomain.of.foo.com:8000/some/subdirectory in the history, we should
     // also have these entries
     ["ftp://foo.com:8000", "A", 1, 0, 0],
-    ["ftp://foo.com:8000!appId=1000", "A", 1, 0, 0],
-    ["ftp://foo.com:8000!appId=2000&inBrowser=1", "A", 1, 0, 0],
+    ["ftp://foo.com:8000^appId=1000", "A", 1, 0, 0],
+    ["ftp://foo.com:8000^appId=2000&inBrowser=1", "A", 1, 0, 0],
   ];
 
   let found = expected.map((it) => 0);
diff --git a/toolkit/modules/BrowserUtils.jsm b/toolkit/modules/BrowserUtils.jsm
index 412e574214ec..4f17f4b879ba 100644
--- a/toolkit/modules/BrowserUtils.jsm
+++ b/toolkit/modules/BrowserUtils.jsm
@@ -105,7 +105,7 @@ this.BrowserUtils = {
       throw new Error("principalFromOrigin does not support nsNullPrincipal");
     }
 
-    var parts = aOriginString.split('!');
+    var parts = aOriginString.split('^');
     if (parts.length > 2) {
       throw new Error("bad origin string: " + aOriginString);
     }

From 43dff6f92f3bad67fc6e236563c407d6e2a8df22 Mon Sep 17 00:00:00 2001
From: Lorien Hu 
Date: Tue, 14 Jul 2015 11:10:30 -0400
Subject: [PATCH 35/67] Bug 1100602 - Fire show/hide events in
 HTMLLIAccessible::UpdateBullet r=tbsaunde

---
 accessible/html/HTMLListAccessible.cpp        |  15 ++-
 .../tests/mochitest/treeupdate/a11y.ini       |   1 +
 .../mochitest/treeupdate/test_bug1100602.html | 114 ++++++++++++++++++
 3 files changed, 127 insertions(+), 3 deletions(-)
 create mode 100644 accessible/tests/mochitest/treeupdate/test_bug1100602.html

diff --git a/accessible/html/HTMLListAccessible.cpp b/accessible/html/HTMLListAccessible.cpp
index fef87525d5a4..3cdb485ef710 100644
--- a/accessible/html/HTMLListAccessible.cpp
+++ b/accessible/html/HTMLListAccessible.cpp
@@ -102,19 +102,28 @@ HTMLLIAccessible::UpdateBullet(bool aHasBullet)
     return;
   }
 
+  nsRefPtr reorderEvent = new AccReorderEvent(this);
+  AutoTreeMutation mut(this);
+
   DocAccessible* document = Document();
   if (aHasBullet) {
     mBullet = new HTMLListBulletAccessible(mContent, mDoc);
     document->BindToDocument(mBullet, nullptr);
     InsertChildAt(0, mBullet);
+
+    nsRefPtr event = new AccShowEvent(mBullet, mBullet->GetContent());
+    mDoc->FireDelayedEvent(event);
+    reorderEvent->AddSubMutationEvent(event);
   } else {
+    nsRefPtr event = new AccHideEvent(mBullet, mBullet->GetContent());
+    mDoc->FireDelayedEvent(event);
+    reorderEvent->AddSubMutationEvent(event);
+
     RemoveChild(mBullet);
-    document->UnbindFromDocument(mBullet);
     mBullet = nullptr;
   }
 
-  // XXXtodo: fire show/hide and reorder events. That's hard to make it
-  // right now because coalescence happens by DOM node.
+  mDoc->FireDelayedEvent(reorderEvent);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/accessible/tests/mochitest/treeupdate/a11y.ini b/accessible/tests/mochitest/treeupdate/a11y.ini
index e6bde363cde7..bdcc2cf6edae 100644
--- a/accessible/tests/mochitest/treeupdate/a11y.ini
+++ b/accessible/tests/mochitest/treeupdate/a11y.ini
@@ -6,6 +6,7 @@
 [test_bug884251.xhtml]
 [test_bug895082.html]
 [test_bug1040735.html]
+[test_bug1100602.html]
 [test_canvas.html]
 [test_colorpicker.xul]
 [test_contextmenu.xul]
diff --git a/accessible/tests/mochitest/treeupdate/test_bug1100602.html b/accessible/tests/mochitest/treeupdate/test_bug1100602.html
new file mode 100644
index 000000000000..1637e50576af
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug1100602.html
@@ -0,0 +1,114 @@
+
+
+
+  Test hide/show events for HTMLListBulletAccessibles on list restyle
+  
+
+  
+
+  
+  
+  
+
+  
+
+
+
+
+
+  
+    Mozilla Bug 1100602
+  
+  

+ +
+  
+ +
    +
  1. list element
  2. +
+ + + From 02e811ecb430266d16c8221383108b11dd3d7683 Mon Sep 17 00:00:00 2001 From: Sean Stangl Date: Mon, 6 Jul 2015 14:41:49 -0700 Subject: [PATCH 36/67] Bug 1181348 - Fix ARM64 toggledCall() under debug mode. r=djvj --- js/src/jit/arm64/MacroAssembler-arm64.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/js/src/jit/arm64/MacroAssembler-arm64.h b/js/src/jit/arm64/MacroAssembler-arm64.h index a82e1e02e2e5..db7aac5913b6 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.h +++ b/js/src/jit/arm64/MacroAssembler-arm64.h @@ -3003,6 +3003,10 @@ class MacroAssemblerCompat : public vixl::MacroAssembler // Emit a BLR or NOP instruction. ToggleCall can be used to patch // this instruction. CodeOffsetLabel toggledCall(JitCode* target, bool enabled) { + // The returned offset must be to the first instruction generated, + // for the debugger to match offset with Baseline's pcMappingEntries_. + BufferOffset offset = nextOffset(); + // TODO: Random pool insertion between instructions below is terrible. // Unfortunately, we can't forbid pool prevention, because we're trying // to add an entry to a pool. So as a temporary fix, just flush the pool @@ -3012,7 +3016,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler syncStackPtr(); - BufferOffset offset = nextOffset(); BufferOffset loadOffset; { vixl::UseScratchRegisterScope temps(this); From 9217beeae7baddd2d375c0ecdbd44042faa271d8 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Tue, 14 Jul 2015 12:44:25 -0700 Subject: [PATCH 37/67] Bumping manifests a=b2g-bump --- b2g/config/aries/sources.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index dddddfd19163..f70dbe9e7518 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -125,7 +125,7 @@ - + From f8df5c9cb56b4b5bfdaca335c02f0c0a364d12b9 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Tue, 14 Jul 2015 13:00:35 -0700 Subject: [PATCH 38/67] Bumping gaia.json for 1 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/d7582982f14e Author: spark-bot Desc: Bump Spark apps --- b2g/config/gaia.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 0ba4a1afa959..f71ebe15647a 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "803d04e3829fd4fe9261211aa0ddca6b79d4e328", + "git_revision": "1d4024dafdd67cf6347e174c3f36620beee41793", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "ad35f158cbd79823e9449f30df5d693613eef17f", + "revision": "d7582982f14eb742b4f466d02b8ce4def96f56f4", "repo_path": "integration/gaia-central" } From 7dd2a8a828a97d0e97e7406e96d2aecaa81c9231 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Tue, 14 Jul 2015 13:02:35 -0700 Subject: [PATCH 39/67] Bumping manifests a=b2g-bump --- b2g/config/aries/sources.xml | 2 +- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator-l/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/nexus-5-l/sources.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index f70dbe9e7518..f3e06ad2e9c5 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index f0d977db50cd..f4aabc4a66de 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index c0e1c9169167..b54a13efb988 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 6f890449b212..04c753c7ae56 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 8ea04ccb0fbc..99905180924d 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index 282273e76492..b7b99062e414 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index c0e1c9169167..b54a13efb988 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index e43980c55c9a..53fb3ed752ff 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index bf545c60f70f..b63e48f2f379 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index a4eb46771529..4db7d666da60 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + From 2b3364a36ed79aad3de710ac03640939074bd02b Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Tue, 14 Jul 2015 13:15:29 -0700 Subject: [PATCH 40/67] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/88a0adc1f40b Author: Martijn Desc: Merge pull request #30957 from mwargers/1183696 Bug 1183696 - Failure in test_browser_lan.py in go_to_url ======== https://hg.mozilla.org/integration/gaia-central/rev/9e97947a5210 Author: Martijn Wargers Desc: Bug 1183696 - Failure in test_browser_lan.py in go_to_url --- b2g/config/gaia.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index f71ebe15647a..8dbffd286c08 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "1d4024dafdd67cf6347e174c3f36620beee41793", + "git_revision": "d600ca447a22b4206b8a0ec08ac382d15997532a", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "d7582982f14eb742b4f466d02b8ce4def96f56f4", + "revision": "88a0adc1f40b077548a8d2d0b036b65c4ff502f4", "repo_path": "integration/gaia-central" } From 3f6d626e51ba9f9a83a64e70f3f5069c72a07f71 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Tue, 14 Jul 2015 13:17:29 -0700 Subject: [PATCH 41/67] Bumping manifests a=b2g-bump --- b2g/config/aries/sources.xml | 2 +- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator-l/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/nexus-5-l/sources.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index f3e06ad2e9c5..6ec66c7e3521 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index f4aabc4a66de..e0d25428afa6 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index b54a13efb988..f597252589ad 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 04c753c7ae56..4da70a650547 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 99905180924d..48ee6cb83478 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index b7b99062e414..1a8d2b1018f7 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index b54a13efb988..f597252589ad 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 53fb3ed752ff..e1ddadbe1f9c 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index b63e48f2f379..4d92924ead8d 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index 4db7d666da60..28c852acca9f 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + From 46e0118691d4b961ab6a3a8ee57c49f85889b836 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Tue, 14 Jul 2015 15:15:10 -0700 Subject: [PATCH 42/67] Bumping gaia.json for 6 gaia revision(s) a=gaia-bump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ======== https://hg.mozilla.org/integration/gaia-central/rev/07dbb05bb163 Author: Andrew Sutherland Desc: Merge pull request #30964 from asutherland/email-bigger-attachments Bug 1183865 - [email] Bump compose attachment size limit on trunk to 22MiB. r=mcav ======== https://hg.mozilla.org/integration/gaia-central/rev/4403d978505d Author: Andrew Sutherland Desc: Bug 1183865 - [email] Bump compose attachment size limit on trunk to 22MiB. r=mcav ======== https://hg.mozilla.org/integration/gaia-central/rev/b0e7f2933dc8 Author: Andrew Sutherland Desc: Merge pull request #30963 from mozilla-b2g/revert-30962-email-bigger-attachments Revert "[email] Bump compose attachment size limit on trunk to 22MiB.… ======== https://hg.mozilla.org/integration/gaia-central/rev/47c5c122bac7 Author: Andrew Sutherland Desc: Revert "[email] Bump compose attachment size limit on trunk to 22MiB. r=mcav" ======== https://hg.mozilla.org/integration/gaia-central/rev/565834843473 Author: Andrew Sutherland Desc: Merge pull request #30962 from asutherland/email-bigger-attachments [email] Bump compose attachment size limit on trunk to 22MiB. r=mcav ======== https://hg.mozilla.org/integration/gaia-central/rev/8ee37f4959db Author: Andrew Sutherland Desc: [email] Bump compose attachment size limit on trunk to 22MiB. r=mcav --- b2g/config/gaia.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 8dbffd286c08..d07937be85ca 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "d600ca447a22b4206b8a0ec08ac382d15997532a", + "git_revision": "46e711792a80e3db03a0bab1d6171154fb90fa57", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "88a0adc1f40b077548a8d2d0b036b65c4ff502f4", + "revision": "07dbb05bb163e5367d5bab0d4a11305b71a55268", "repo_path": "integration/gaia-central" } From df37e2bb1b01118b68a3f73ea8d4e91359daf647 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Tue, 14 Jul 2015 15:17:09 -0700 Subject: [PATCH 43/67] Bumping manifests a=b2g-bump --- b2g/config/aries/sources.xml | 2 +- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator-l/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/nexus-5-l/sources.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index 6ec66c7e3521..9b4d5e2177f6 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index e0d25428afa6..a3fd16ba3414 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index f597252589ad..53ec3535e8fd 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 4da70a650547..05286a387015 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 48ee6cb83478..461f3be73f70 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index 1a8d2b1018f7..e84e35be18bd 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index f597252589ad..53ec3535e8fd 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index e1ddadbe1f9c..a5d6ad790d69 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 4d92924ead8d..8a5896ddc4d7 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index 28c852acca9f..092fcf883c4d 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - + From fb1b6957d406acaa0c2d9faa40466b1888ef5a20 Mon Sep 17 00:00:00 2001 From: Andrew Osmond Date: Wed, 8 Jul 2015 16:24:58 -0300 Subject: [PATCH 44/67] Bug 1043112 - Allow b2g to core dump when signal permits. r=jld --- toolkit/profile/nsProfileLock.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/toolkit/profile/nsProfileLock.cpp b/toolkit/profile/nsProfileLock.cpp index 4aa463e3a6bb..f0e158b1ca03 100644 --- a/toolkit/profile/nsProfileLock.cpp +++ b/toolkit/profile/nsProfileLock.cpp @@ -34,6 +34,10 @@ #include #endif +#if defined(MOZ_B2G) && !defined(MOZ_CRASHREPORTER) +#include +#endif + // ********************************************************************** // class nsProfileLock // @@ -192,6 +196,27 @@ void nsProfileLock::FatalSignalHandler(int signo } } +#ifdef MOZ_B2G + switch (signo) { + case SIGQUIT: + case SIGILL: + case SIGABRT: + case SIGSEGV: +#ifndef MOZ_CRASHREPORTER + // Retrigger the signal for those that can generate a core dump + signal(signo, SIG_DFL); + if (info->si_code <= 0) { + if (syscall(__NR_tgkill, getpid(), syscall(__NR_gettid), signo) < 0) { + break; + } + } +#endif + return; + default: + break; + } +#endif + // Backstop exit call, just in case. _exit(signo); } From 5b759c645a94fe5da153c3e46055db9acfd39861 Mon Sep 17 00:00:00 2001 From: Sean Stangl Date: Mon, 13 Jul 2015 12:51:33 -0700 Subject: [PATCH 45/67] Bug 1183308 - Fix ARM64 bustage from Bug 1141986. r=efaust --- js/src/jit/arm64/MacroAssembler-arm64.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/js/src/jit/arm64/MacroAssembler-arm64.h b/js/src/jit/arm64/MacroAssembler-arm64.h index db7aac5913b6..4b4b9fdda8d0 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.h +++ b/js/src/jit/arm64/MacroAssembler-arm64.h @@ -2829,6 +2829,19 @@ class MacroAssemblerCompat : public vixl::MacroAssembler void compareExchange32(const T& mem, Register oldval, Register newval, Register output) { compareExchange(4, false, mem, oldval, newval, output); } + template + void atomicExchange32(const T& mem, Register value, Register output) { + MOZ_CRASH("atomicExchang32"); + } + + template + void atomicExchange8ZeroExtend(const T& mem, Register value, Register output) { + MOZ_CRASH("atomicExchange8ZeroExtend"); + } + template + void atomicExchange8SignExtend(const T& mem, Register value, Register output) { + MOZ_CRASH("atomicExchange8SignExtend"); + } template void atomicFetchAdd8SignExtend(const S& value, const T& mem, Register temp, Register output) { @@ -2864,6 +2877,15 @@ class MacroAssemblerCompat : public vixl::MacroAssembler atomicEffectOp(4, AtomicFetchAddOp, value, mem); } + template + void atomicExchange16ZeroExtend(const T& mem, Register value, Register output) { + MOZ_CRASH("atomicExchange16ZeroExtend"); + } + template + void atomicExchange16SignExtend(const T& mem, Register value, Register output) { + MOZ_CRASH("atomicExchange16SignExtend"); + } + template void atomicFetchSub8SignExtend(const S& value, const T& mem, Register temp, Register output) { atomicFetchOp(1, true, AtomicFetchSubOp, value, mem, temp, output); From f9686558d695254a7e11536ee272d933b2c3b990 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Tue, 14 Jul 2015 12:16:44 -0700 Subject: [PATCH 46/67] Bug 1182092 - Try harder to filter out template shapes with indexed properties when making unboxed objects, r=jandem. --- js/src/vm/UnboxedObject.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index 320b12872516..38d11bdff389 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -1939,6 +1939,15 @@ js::TryConvertToUnboxedLayout(ExclusiveContext* cx, Shape* templateShape, return true; } + // Make sure that all properties on the template shape are property + // names, and not indexes. + for (Shape::Range r(templateShape); !r.empty(); r.popFront()) { + jsid id = r.front().propid(); + uint32_t dummy; + if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy)) + return true; + } + layoutSize = ComputePlainObjectLayout(cx, templateShape, properties); // The entire object must be allocatable inline. From 64fe037420696795ae98d3f05d3f75105ff907f2 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 14 Jul 2015 15:28:57 -0400 Subject: [PATCH 47/67] Bug 1181762. Remove uses of mozRequestAnimationFrame from toolkit code. r=gijs --- toolkit/content/browser-content.js | 11 ++++------- .../tests/browser/browser_autoscroll_disabled.js | 4 ++-- .../browser_bug295977_autoscroll_overflow.js | 4 ++-- toolkit/content/widgets/progressmeter.xml | 8 ++++---- toolkit/content/widgets/scrollbox.xml | 16 ++++++++-------- 5 files changed, 20 insertions(+), 23 deletions(-) diff --git a/toolkit/content/browser-content.js b/toolkit/content/browser-content.js index 22a7dc62472c..03406fc39c30 100644 --- a/toolkit/content/browser-content.js +++ b/toolkit/content/browser-content.js @@ -29,6 +29,7 @@ let ClickEventHandler = { this._screenX = null; this._screenY = null; this._lastFrame = null; + this.autoscrollLoop = this.autoscrollLoop.bind(this); Services.els.addSystemEventListener(global, "mousedown", this, true); @@ -142,9 +143,9 @@ let ClickEventHandler = { this._screenY = event.screenY; this._scrollErrorX = 0; this._scrollErrorY = 0; - this._lastFrame = content.mozAnimationStartTime; + this._lastFrame = content.performance.now(); - content.mozRequestAnimationFrame(this); + content.requestAnimationFrame(this.autoscrollLoop); }, stopScroll: function() { @@ -211,11 +212,7 @@ let ClickEventHandler = { this._scrollable.scrollLeft += actualScrollX; this._scrollable.scrollTop += actualScrollY; } - content.mozRequestAnimationFrame(this); - }, - - sample: function(timestamp) { - this.autoscrollLoop(timestamp); + content.requestAnimationFrame(this.autoscrollLoop); }, handleEvent: function(event) { diff --git a/toolkit/content/tests/browser/browser_autoscroll_disabled.js b/toolkit/content/tests/browser/browser_autoscroll_disabled.js index 10a25fb1d5d9..507026b09257 100644 --- a/toolkit/content/tests/browser/browser_autoscroll_disabled.js +++ b/toolkit/content/tests/browser/browser_autoscroll_disabled.js @@ -43,7 +43,7 @@ function test() var skipFrames = 1; var checkScroll = function () { if (skipFrames--) { - window.mozRequestAnimationFrame(checkScroll); + window.requestAnimationFrame(checkScroll); return; } ok(elem.scrollTop == 0, "element should not have scrolled vertically"); @@ -70,6 +70,6 @@ function test() * so request and force redraws to get the chance to check for scrolling at * all. */ - window.mozRequestAnimationFrame(checkScroll); + window.requestAnimationFrame(checkScroll); } } diff --git a/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js b/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js index 77963d2d8e2d..1685a976f955 100644 --- a/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js +++ b/toolkit/content/tests/browser/browser_bug295977_autoscroll_overflow.js @@ -117,7 +117,7 @@ function test() // although it also depends on acceleration, which here in this test // should be > 1 due to how it synthesizes mouse events below. if (timeCompensation < 5) { - window.mozRequestAnimationFrame(checkScroll); + window.requestAnimationFrame(checkScroll); return; } @@ -174,7 +174,7 @@ function test() Services.prefs.clearUserPref("middlemouse.paste"); // Start checking for the scroll. - window.mozRequestAnimationFrame(checkScroll); + window.requestAnimationFrame(checkScroll); } waitForExplicitFinish(); diff --git a/toolkit/content/widgets/progressmeter.xml b/toolkit/content/widgets/progressmeter.xml index 912375491c05..4fb236c41e22 100644 --- a/toolkit/content/widgets/progressmeter.xml +++ b/toolkit/content/widgets/progressmeter.xml @@ -74,7 +74,7 @@ document.getAnonymousElementByAttribute(this, "anonid", "spacer"); var isLTR = document.defaultView.getComputedStyle(this, null).direction == "ltr"; - var startTime = window.mozAnimationStartTime; + var startTime = performance.now(); var self = this; function nextStep(t) { @@ -83,7 +83,7 @@ if (!width) { // Maybe we've been removed from the document. if (self._alive) - mozRequestAnimationFrame(nextStep); + requestAnimationFrame(nextStep); return; } @@ -101,11 +101,11 @@ spacer.width = width; spacer.left = width * position; - mozRequestAnimationFrame(nextStep); + requestAnimationFrame(nextStep); } catch (e) { } } - mozRequestAnimationFrame(nextStep); + requestAnimationFrame(nextStep); ]]>
diff --git a/toolkit/content/widgets/scrollbox.xml b/toolkit/content/widgets/scrollbox.xml index 435133f6f817..123b0846db3b 100644 --- a/toolkit/content/widgets/scrollbox.xml +++ b/toolkit/content/widgets/scrollbox.xml @@ -292,13 +292,13 @@ this.distance = distance; this.startPos = this.scrollbox.scrollPosition; this.duration = Math.min(1000, Math.round(50 * Math.sqrt(Math.abs(distance)))); - this.startTime = window.mozAnimationStartTime; + this.startTime = window.performance.now(); if (!this.requestHandle) - this.requestHandle = window.mozRequestAnimationFrame(this); + this.requestHandle = window.requestAnimationFrame(this.sample.bind(this)); }, stop: function scrollAnim_stop() { - window.mozCancelAnimationFrame(this.requestHandle); + window.cancelAnimationFrame(this.requestHandle); this.requestHandle = 0; }, sample: function scrollAnim_handleEvent(timeStamp) { @@ -311,7 +311,7 @@ if (pos == 1) this.scrollbox._stopSmoothScroll(); else - this.requestHandle = window.mozRequestAnimationFrame(this); + this.requestHandle = window.requestAnimationFrame(this.sample.bind(this)); } })]]> @@ -677,12 +677,12 @@ scrollbox: this, requestHandle: 0, /* 0 indicates there is no pending request */ start: function arrowSmoothScroll_start() { - this.lastFrameTime = window.mozAnimationStartTime; + this.lastFrameTime = window.performance.now(); if (!this.requestHandle) - this.requestHandle = window.mozRequestAnimationFrame(this); + this.requestHandle = window.requestAnimationFrame(this.sample.bind(this)); }, stop: function arrowSmoothScroll_stop() { - window.mozCancelAnimationFrame(this.requestHandle); + window.cancelAnimationFrame(this.requestHandle); this.requestHandle = 0; }, sample: function arrowSmoothScroll_handleEvent(timeStamp) { @@ -693,7 +693,7 @@ const scrollDelta = 0.5 * timePassed * scrollIndex; this.scrollbox.scrollPosition += scrollDelta; - this.requestHandle = window.mozRequestAnimationFrame(this); + this.requestHandle = window.requestAnimationFrame(this.sample.bind(this)); } })]]> From e417498f7dfe89b98e7916e6f1020222eee2bda9 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 14 Jul 2015 15:28:57 -0400 Subject: [PATCH 48/67] Bug 1181920. Persist XUL attributes off a scriptrunner. r=peterv --- dom/xul/XULDocument.cpp | 8 ++++---- dom/xul/XULDocument.h | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/dom/xul/XULDocument.cpp b/dom/xul/XULDocument.cpp index 85acf49ad8fb..5b1787bcbf61 100644 --- a/dom/xul/XULDocument.cpp +++ b/dom/xul/XULDocument.cpp @@ -944,8 +944,6 @@ XULDocument::AttributeChanged(nsIDocument* aDocument, AddElementToRefMap(aElement); } - nsresult rv; - // Synchronize broadcast listeners if (mBroadcasterMap && CanBroadcast(aNameSpaceID, aAttribute)) { @@ -1012,8 +1010,10 @@ XULDocument::AttributeChanged(nsIDocument* aDocument, if (!persist.IsEmpty()) { // XXXldb This should check that it's a token, not just a substring. if (persist.Find(nsDependentAtomString(aAttribute)) >= 0) { - rv = Persist(aElement, kNameSpaceID_None, aAttribute); - if (NS_FAILED(rv)) return; + nsContentUtils::AddScriptRunner(NS_NewRunnableMethodWithArgs + + (this, &XULDocument::DoPersist, aElement, kNameSpaceID_None, + aAttribute)); } } } diff --git a/dom/xul/XULDocument.h b/dom/xul/XULDocument.h index 4275a728e843..1c835097123f 100644 --- a/dom/xul/XULDocument.h +++ b/dom/xul/XULDocument.h @@ -298,6 +298,12 @@ protected: nsresult Persist(nsIContent* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute); + // Just like Persist but ignores the return value so we can use it + // as a runnable method. + void DoPersist(nsIContent* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute) + { + Persist(aElement, aNameSpaceID, aAttribute); + } virtual JSObject* WrapNode(JSContext *aCx, JS::Handle aGivenProto) override; From 76bfe95e04b09dea43b4d0378ab404f2ef3fd795 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 14 Jul 2015 15:28:57 -0400 Subject: [PATCH 49/67] Bug 1181916. Notify the debugger's onNewGlobalObject hook off a scriptrunner. r=bholley --- dom/base/nsGlobalWindow.cpp | 20 +++++++++++++++----- dom/base/nsGlobalWindow.h | 5 +++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index ab0aa265ebcf..ee86e3179082 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2709,11 +2709,9 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, // We wait to fire the debugger hook until the window is all set up and hooked // up with the outer. See bug 969156. if (createdInnerWindow) { - // AutoEntryScript required to invoke debugger hook, which is a - // Gecko-specific concept at present. - AutoEntryScript aes(newInnerWindow, "nsGlobalWindow report new global"); - JS::Rooted global(aes.cx(), newInnerWindow->GetWrapper()); - JS_FireOnNewGlobalObject(aes.cx(), global); + nsContentUtils::AddScriptRunner( + NS_NewRunnableMethod(newInnerWindow, + &nsGlobalWindow::FireOnNewGlobalObject)); } if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) { @@ -14167,6 +14165,18 @@ nsGlobalWindow::SetReplaceableWindowCoord(JSContext* aCx, (this->*aSetter)(value, aError); } +void +nsGlobalWindow::FireOnNewGlobalObject() +{ + MOZ_ASSERT(IsInnerWindow()); + + // AutoEntryScript required to invoke debugger hook, which is a + // Gecko-specific concept at present. + AutoEntryScript aes(this, "nsGlobalWindow report new global"); + JS::Rooted global(aes.cx(), GetWrapper()); + JS_FireOnNewGlobalObject(aes.cx(), global); +} + #ifdef _WINDOWS_ #error "Never include windows.h in this file!" #endif diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 9840bc830787..820a9e6abc92 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -1538,6 +1538,11 @@ protected: // show, in that case we show a separate dialog to ask this question. bool ConfirmDialogIfNeeded(); +private: + // Fire the JS engine's onNewGlobalObject hook. Only used on inner windows. + void FireOnNewGlobalObject(); + +protected: // When adding new member variables, be careful not to create cycles // through JavaScript. If there is any chance that a member variable // could own objects that are implemented in JavaScript, then those From f9793d505e295d55f631c729d397e8d09189437e Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 14 Jul 2015 15:28:57 -0400 Subject: [PATCH 50/67] Bug 1181965. Remove uses of mozRequestAnimationFrame from dom and parser tests. r=bkelly --- dom/base/crashtests/706283-1.html | 2 +- dom/base/crashtests/709384.html | 4 +-- dom/base/nsDocument.h | 4 +-- dom/base/test/test_bug647518.html | 8 ++--- dom/base/test/test_bug675121.html | 2 +- dom/base/test/test_bug704063.html | 36 +++++++++---------- .../BrowserElementPanningAPZDisabled.js | 6 ++-- dom/canvas/test/reftest/webgl-utils.js | 2 +- dom/events/test/test_bug607464.html | 4 +-- dom/plugins/test/crashtests/626602-1.html | 4 +-- .../tests/reftest/frame599320-1.html | 2 +- 11 files changed, 37 insertions(+), 37 deletions(-) diff --git a/dom/base/crashtests/706283-1.html b/dom/base/crashtests/706283-1.html index 41e413178fe5..67b618e7fe09 100644 --- a/dom/base/crashtests/706283-1.html +++ b/dom/base/crashtests/706283-1.html @@ -1,6 +1,6 @@ diff --git a/dom/base/crashtests/709384.html b/dom/base/crashtests/709384.html index 9f268a17d7f5..5e99e9c3c6b3 100644 --- a/dom/base/crashtests/709384.html +++ b/dom/base/crashtests/709384.html @@ -1,5 +1,5 @@ diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 98969c54c532..1fe77e78b35e 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -1761,10 +1761,10 @@ private: void EnableStyleSheetsForSetInternal(const nsAString& aSheetSet, bool aUpdateCSSLoader); - // Revoke any pending notifications due to mozRequestAnimationFrame calls + // Revoke any pending notifications due to requestAnimationFrame calls void RevokeAnimationFrameNotifications(); // Reschedule any notifications we need to handle - // mozRequestAnimationFrame, if it's OK to do so. + // requestAnimationFrame, if it's OK to do so. void MaybeRescheduleAnimationFrameNotifications(); // These are not implemented and not supported. diff --git a/dom/base/test/test_bug647518.html b/dom/base/test/test_bug647518.html index 29030d36f713..bb5a6a02a229 100644 --- a/dom/base/test/test_bug647518.html +++ b/dom/base/test/test_bug647518.html @@ -22,7 +22,7 @@ SimpleTest.waitForExplicitFinish(); var counter = 3; var called = false; -var handle1 = window.mozRequestAnimationFrame(function() { +var handle1 = window.requestAnimationFrame(function() { called = true; }); ok(handle1 > 0, "Should get back a nonzero handle"); @@ -33,11 +33,11 @@ function checker() { is(called, false, "Canceled callback should not have been called"); SimpleTest.finish(); } else { - window.mozRequestAnimationFrame(checker); + window.requestAnimationFrame(checker); } } -window.mozRequestAnimationFrame(checker); -window.mozCancelAnimationFrame(handle1); +window.requestAnimationFrame(checker); +window.cancelAnimationFrame(handle1); diff --git a/dom/base/test/test_bug675121.html b/dom/base/test/test_bug675121.html index 2f9638c70b02..a48fea935ec2 100644 --- a/dom/base/test/test_bug675121.html +++ b/dom/base/test/test_bug675121.html @@ -27,7 +27,7 @@ function f() { } } -window.mozRequestAnimationFrame(f); +window.requestAnimationFrame(f); var xhr = new XMLHttpRequest(); xhr.open("GET", "file_bug675121.sjs", false); xhrInProgress = true; diff --git a/dom/base/test/test_bug704063.html b/dom/base/test/test_bug704063.html index 5c966c6b67b0..39759f1543e7 100644 --- a/dom/base/test/test_bug704063.html +++ b/dom/base/test/test_bug704063.html @@ -12,34 +12,34 @@ https://bugzilla.mozilla.org/show_bug.cgi?id= /** Test for Bug **/ SimpleTest.waitForExplicitFinish(); - var unprefixedRan = false; - var prefixedRan = false; - function unprefixed(time) { - is(prefixedRan, true, "We were called second"); - unprefixedRan = true; + var firstRan = false; + var secondRan = false; + function second(time) { + is(firstRan, true, "We were called second"); + secondRan = true; ok(Math.abs(time - performance.now()) < 3600000, "An hour really shouldn't have passed here"); ok(Math.abs(time - Date.now()) > 3600000, "More than an hour should have passed since 1970"); } - function prefixed(time) { - is(unprefixedRan, false, "Prefixed was called first"); - prefixedRan = true; - ok(Math.abs(time - Date.now()) < 3600000, - "Our time should be comparable to Date.now()"); - ok(Math.abs(time - performance.now()) > 3600000, - "Our time should not be comparable to performance.now()"); + function first(time) { + is(secondRan, false, "second() was called first"); + firstRan = true; + ok(Math.abs(time - performance.now()) < 3600000, + "An hour really shouldn't have passed here either"); + ok(Math.abs(time - Date.now()) > 3600000, + "More than an hour should have passed since 1970 here either"); } - function prefixed2() { - ok(prefixedRan, "We should be after the other prefixed call"); - ok(unprefixedRan, "We should be after the unprefixed call"); + function third() { + ok(firstRan, "We should be after the first call"); + ok(secondRan, "We should be after the second call"); SimpleTest.finish(); } window.onload = function() { - window.mozRequestAnimationFrame(prefixed); - window.requestAnimationFrame(unprefixed); - window.mozRequestAnimationFrame(prefixed2); + window.requestAnimationFrame(first); + window.requestAnimationFrame(second); + window.requestAnimationFrame(third); } diff --git a/dom/browser-element/BrowserElementPanningAPZDisabled.js b/dom/browser-element/BrowserElementPanningAPZDisabled.js index 26d5af92fb15..d19757210ac4 100644 --- a/dom/browser-element/BrowserElementPanningAPZDisabled.js +++ b/dom/browser-element/BrowserElementPanningAPZDisabled.js @@ -622,7 +622,7 @@ const KineticPanning = { v.y * Math.exp(-t / c) * -c + a.y * t * t + v.y * c); } - let startTime = content.mozAnimationStartTime; + let startTime = content.performance.now(); let elapsedTime = 0, targetedTime = 0, averageTime = 0; let velocity = this._velocity; @@ -667,9 +667,9 @@ const KineticPanning = { return; } - content.mozRequestAnimationFrame(callback); + content.requestAnimationFrame(callback); }).bind(this); - content.mozRequestAnimationFrame(callback); + content.requestAnimationFrame(callback); } }; diff --git a/dom/canvas/test/reftest/webgl-utils.js b/dom/canvas/test/reftest/webgl-utils.js index a736cc5b36ad..06f34ca91560 100644 --- a/dom/canvas/test/reftest/webgl-utils.js +++ b/dom/canvas/test/reftest/webgl-utils.js @@ -62,7 +62,7 @@ function initGL(canvas) { } function rAF(func) { - var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame; + var raf = window.requestAnimationFrame; raf(func); } diff --git a/dom/events/test/test_bug607464.html b/dom/events/test/test_bug607464.html index 9460d68c505e..9e22e57afc77 100644 --- a/dom/events/test/test_bug607464.html +++ b/dom/events/test/test_bug607464.html @@ -55,10 +55,10 @@ function runTest() { scrollDown15PxWithPixelScrolling(scrollbox); // wait for the next refresh driver run - win.mozRequestAnimationFrame(function() { + win.requestAnimationFrame(function() { // actually, wait for the next one before checking results, since // scrolling might not be flushed until after this code has run - win.mozRequestAnimationFrame(function() { + win.requestAnimationFrame(function() { is(scrollbox.scrollTop, scrollTopBefore + 15, "Pixel scrolling should have finished after one refresh driver iteration. " + "We shouldn't be scrolling smoothly, even though the pref is set."); diff --git a/dom/plugins/test/crashtests/626602-1.html b/dom/plugins/test/crashtests/626602-1.html index 1802cf77f8fd..0a878bbd1d48 100644 --- a/dom/plugins/test/crashtests/626602-1.html +++ b/dom/plugins/test/crashtests/626602-1.html @@ -35,7 +35,7 @@ function start() { canvas = document.getElementById("two"); paintCanvas(); - mozRequestAnimationFrame(moveSomething); + requestAnimationFrame(moveSomething); } function paintCanvas() { @@ -52,7 +52,7 @@ function moveSomething() { return finish(); } - mozRequestAnimationFrame(moveSomething); + requestAnimationFrame(moveSomething); } function finish() { diff --git a/parser/htmlparser/tests/reftest/frame599320-1.html b/parser/htmlparser/tests/reftest/frame599320-1.html index 2b5b3383690e..145ee94ba8e6 100644 --- a/parser/htmlparser/tests/reftest/frame599320-1.html +++ b/parser/htmlparser/tests/reftest/frame599320-1.html @@ -1082,7 +1082,7 @@

Euro sign: €

diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h index 13a42de1990c..e05c9dd70b25 100644 --- a/xpcom/base/ErrorList.h +++ b/xpcom/base/ErrorList.h @@ -331,6 +331,8 @@ ERROR(NS_ERROR_INTERCEPTED_ERROR_RESPONSE, FAILURE(103)), /* Service worker intercepted with a response with bodyUsed set to true */ ERROR(NS_ERROR_INTERCEPTED_USED_RESPONSE, FAILURE(104)), + /* Service worker intercepted a client request with an opaque response */ + ERROR(NS_ERROR_CLIENT_REQUEST_OPAQUE_INTERCEPTION, FAILURE(105)), #undef MODULE From ff2f0d32cf9d45225a122c1cd2ddee34794ab09e Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Tue, 14 Jul 2015 14:00:32 -0700 Subject: [PATCH 60/67] Backed out 24 changesets (bug 1173523, bug 1172080, bug 817007, bug 1165263) for android reftest bustage CLOSED TREE Backed out changeset 84fe04b2e7d1 (bug 1172080) Backed out changeset 0ff004760a1f (bug 1172080) Backed out changeset af147585ad55 (bug 1165263) Backed out changeset c3af8ebb6db0 (bug 1165263) Backed out changeset cd3f33a888fe (bug 1165263) Backed out changeset e5db39044a1e (bug 1165263) Backed out changeset c01c9ed77061 (bug 1165263) Backed out changeset fb723aaa4267 (bug 1165263) Backed out changeset f754e52e74dc (bug 1165263) Backed out changeset c6bda3a0afd6 (bug 817007) Backed out changeset bfa100253349 (bug 817007) Backed out changeset b787b3f9aadc (bug 1173523) Backed out changeset 4a0676b73f77 (bug 1173523) Backed out changeset 82034a4560c5 (bug 1173523) Backed out changeset 4bdb91114c7a (bug 1173523) Backed out changeset 72406261eccc (bug 1173523) Backed out changeset 541b6faf7196 (bug 1173523) Backed out changeset 1caac4569616 (bug 1173523) Backed out changeset 0d4f9f9e1b4e (bug 1173523) Backed out changeset 2d5661eb966c (bug 1173523) Backed out changeset 89833c0bb0cd (bug 1173523) Backed out changeset ea64d70eacfe (bug 1173523) Backed out changeset a8e4f1c0c445 (bug 1173523) Backed out changeset cf498d466b85 (bug 1173523) --- browser/app/permissions | 18 +- browser/base/content/browser-plugins.js | 23 +- .../content/browser-trackingprotection.js | 6 +- browser/base/content/pageinfo/pageInfo.js | 3 +- browser/base/content/pageinfo/permissions.js | 4 +- .../content/test/general/browser_bug592338.js | 2 +- browser/base/content/test/plugins/head.js | 2 +- browser/base/content/urlbarBindings.xml | 33 +- .../preferences/aboutPermissions.js | 132 ++- browser/components/preferences/advanced.js | 32 +- .../preferences/in-content/advanced.js | 31 +- browser/components/preferences/permissions.js | 70 +- .../tests/browser_chunk_permissions.js | 16 +- .../tests/browser_cookies_exceptions.js | 17 +- .../preferences/tests/browser_permissions.js | 14 +- browser/components/preferences/translation.js | 19 +- .../test/browser_translation_exceptions.js | 4 +- browser/components/uitour/test/head.js | 9 +- browser/modules/PluginContent.jsm | 21 +- caps/BasePrincipal.cpp | 6 +- caps/nsPrincipal.cpp | 34 - caps/tests/unit/test_origin.js | 26 +- dom/apps/tests/unit/test_moziapplication.js | 2 +- dom/ipc/ContentChild.cpp | 18 +- dom/ipc/ContentParent.cpp | 15 +- dom/storage/DOMStorageObserver.cpp | 14 +- extensions/cookie/nsPermission.cpp | 146 +--- extensions/cookie/nsPermission.h | 8 +- extensions/cookie/nsPermissionManager.cpp | 817 +++++++----------- extensions/cookie/nsPermissionManager.h | 41 +- .../test/test_app_uninstall_permissions.html | 2 +- .../test/unit/test_permmanager_defaults.js | 58 +- .../test_permmanager_getPermissionObject.js | 18 +- .../test_permmanager_load_invalid_entries.js | 2 +- .../test/unit/test_permmanager_local_files.js | 8 +- .../test/unit/test_permmanager_mailto.js | 41 + .../test/unit/test_permmanager_matches.js | 129 --- .../test/unit/test_permmanager_matchesuri.js | 150 ---- .../test/unit/test_permmanager_migrate_4-5.js | 162 ---- extensions/cookie/test/unit/xpcshell.ini | 4 +- layout/tools/reftest/reftest-preferences.js | 3 - layout/tools/reftest/reftest.js | 3 - layout/tools/reftest/remotereftest.py | 2 +- layout/tools/reftest/runreftest.py | 4 +- mobile/android/app/mobile.js | 4 +- mobile/android/chrome/content/browser.js | 10 +- netwerk/base/nsIPermission.idl | 44 +- netwerk/ipc/NeckoMessageUtils.h | 30 +- netwerk/test/unit/test_permmgr.js | 51 +- .../mozprofile/mozprofile/permissions.py | 44 +- testing/mozbase/mozprofile/tests/bug785146.py | 2 +- .../mozbase/mozprofile/tests/permissions.py | 41 +- .../components/SpecialPowersObserver.js | 11 +- .../specialpowers/content/specialpowersAPI.js | 2 +- toolkit/forgetaboutsite/ForgetAboutSite.jsm | 9 +- toolkit/modules/BrowserUtils.jsm | 2 +- toolkit/modules/PermissionsUtils.jsm | 34 +- .../tests/xpcshell/test_PermissionsUtils.js | 36 +- .../test/xpcshell/test_permissions.js | 6 +- .../test/xpcshell/test_permissions_prefs.js | 12 +- 60 files changed, 821 insertions(+), 1686 deletions(-) create mode 100644 extensions/cookie/test/unit/test_permmanager_mailto.js delete mode 100644 extensions/cookie/test/unit/test_permmanager_matches.js delete mode 100644 extensions/cookie/test/unit/test_permmanager_matchesuri.js delete mode 100644 extensions/cookie/test/unit/test_permmanager_migrate_4-5.js diff --git a/browser/app/permissions b/browser/app/permissions index cc37101dd377..bc3cddbbf436 100644 --- a/browser/app/permissions +++ b/browser/app/permissions @@ -1,21 +1,21 @@ # This file has default permissions for the permission manager. # The file-format is strict: # * matchtype \t type \t permission \t host -# * "origin" should be used for matchtype, "host" is supported for legacy reasons +# * Only "host" is supported for matchtype # * type is a string that identifies the type of permission (e.g. "cookie") # * permission is an integer between 1 and 15 # See nsPermissionManager.cpp for more... # UITour -origin uitour 1 https://www.mozilla.org -origin uitour 1 https://self-repair.mozilla.org -origin uitour 1 https://support.mozilla.org -origin uitour 1 about:home +host uitour 1 www.mozilla.org +host uitour 1 self-repair.mozilla.org +host uitour 1 support.mozilla.org +host uitour 1 about:home # XPInstall -origin install 1 https://addons.mozilla.org -origin install 1 https://marketplace.firefox.com +host install 1 addons.mozilla.org +host install 1 marketplace.firefox.com # Remote troubleshooting -origin remote-troubleshooting 1 https://input.mozilla.org -origin remote-troubleshooting 1 https://support.mozilla.org +host remote-troubleshooting 1 input.mozilla.org +host remote-troubleshooting 1 support.mozilla.org diff --git a/browser/base/content/browser-plugins.js b/browser/base/content/browser-plugins.js index e17cf0f098e8..dfad5d8965c9 100644 --- a/browser/base/content/browser-plugins.js +++ b/browser/base/content/browser-plugins.js @@ -44,14 +44,14 @@ var gPluginHandler = { switch (msg.name) { case "PluginContent:ShowClickToPlayNotification": this.showClickToPlayNotification(msg.target, msg.data.plugins, msg.data.showNow, - msg.principal, msg.data.location); + msg.principal, msg.data.host, msg.data.location); break; case "PluginContent:RemoveNotification": this.removeNotification(msg.target, msg.data.name); break; case "PluginContent:UpdateHiddenPluginUI": this.updateHiddenPluginUI(msg.target, msg.data.haveInsecure, msg.data.actions, - msg.principal, msg.data.location); + msg.principal, msg.data.host, msg.data.location); break; case "PluginContent:HideNotificationBar": this.hideNotificationBar(msg.target, msg.data.name); @@ -216,8 +216,8 @@ var gPluginHandler = { }); }, - showClickToPlayNotification: function (browser, plugins, showNow, - principal, location) { + showClickToPlayNotification: function (browser, plugins, showNow, principal, + host, location) { // It is possible that we've received a message from the frame script to show // a click to play notification for a principal that no longer matches the one // that the browser's content now has assigned (ie, the browser has browsed away @@ -295,6 +295,7 @@ var gPluginHandler = { primaryPlugin: primaryPluginPermission, pluginData: pluginData, principal: principal, + host: host, }; PopupNotifications.show(browser, "click-to-play-plugins", "", "plugins-notification-icon", @@ -315,10 +316,8 @@ var gPluginHandler = { notificationBox.removeNotification(notification, true); }, - updateHiddenPluginUI: function (browser, haveInsecure, actions, - principal, location) { - let origin = principal.originNoSuffix; - + updateHiddenPluginUI: function (browser, haveInsecure, actions, principal, + host, location) { // It is possible that we've received a message from the frame script to show // the hidden plugin notification for a principal that no longer matches the one // that the browser's content now has assigned (ie, the browser has browsed away @@ -381,22 +380,22 @@ var gPluginHandler = { case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY: message = gNavigatorBundle.getFormattedString( "pluginActivateNew.message", - [pluginName, origin]); + [pluginName, host]); break; case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE: message = gNavigatorBundle.getFormattedString( "pluginActivateOutdated.message", - [pluginName, origin, brand]); + [pluginName, host, brand]); break; case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE: message = gNavigatorBundle.getFormattedString( "pluginActivateVulnerable.message", - [pluginName, origin, brand]); + [pluginName, host, brand]); } } else { // Multi-plugin message = gNavigatorBundle.getFormattedString( - "pluginActivateMultiple.message", [origin]); + "pluginActivateMultiple.message", [host]); } let buttons = [ diff --git a/browser/base/content/browser-trackingprotection.js b/browser/base/content/browser-trackingprotection.js index e32bd1c08dfc..7c7ed7db5e51 100644 --- a/browser/base/content/browser-trackingprotection.js +++ b/browser/base/content/browser-trackingprotection.js @@ -99,11 +99,7 @@ let TrackingProtection = { // Remove the current host from the 'trackingprotection' consumer // of the permission manager. This effectively removes this host // from the tracking protection allowlist. - let normalizedUrl = Services.io.newURI( - "https://" + gBrowser.selectedBrowser.currentURI.hostPort, - null, null); - - Services.perms.remove(normalizedUrl, + Services.perms.remove(gBrowser.selectedBrowser.currentURI, "trackingprotection"); // Telemetry for enable protection. diff --git a/browser/base/content/pageinfo/pageInfo.js b/browser/base/content/pageinfo/pageInfo.js index d216346c2930..c7b67c423c08 100644 --- a/browser/base/content/pageinfo/pageInfo.js +++ b/browser/base/content/pageinfo/pageInfo.js @@ -1117,9 +1117,8 @@ var imagePermissionObserver = { var row = getSelectedRow(imageTree); var item = gImageView.data[row][COL_IMAGE_NODE]; var url = gImageView.data[row][COL_IMAGE_ADDRESS]; - if (permission.matchesURI(makeURI(url), true)) { + if (makeURI(url).host == permission.host) makeBlockImage(url); - } } } } diff --git a/browser/base/content/pageinfo/permissions.js b/browser/base/content/pageinfo/permissions.js index eaf5824151d4..a67ecd07b660 100644 --- a/browser/base/content/pageinfo/permissions.js +++ b/browser/base/content/pageinfo/permissions.js @@ -18,7 +18,7 @@ var permissionObserver = { { if (aTopic == "perm-changed") { var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission); - if (permission.matchesURI(gPermURI, true)) { + if (permission.host == gPermURI.host) { if (gPermissions.indexOf(permission.type) > -1) initRow(permission.type); else if (permission.type.startsWith("plugin")) @@ -35,7 +35,7 @@ function onLoadPermission() if (SitePermissions.isSupportedURI(uri)) { gPermURI = uri; var hostText = document.getElementById("hostText"); - hostText.value = gPermURI.prePath; + hostText.value = gPermURI.host; for (var i of gPermissions) initRow(i); diff --git a/browser/base/content/test/general/browser_bug592338.js b/browser/base/content/test/general/browser_bug592338.js index 015459b0dc54..99544d87a8b4 100644 --- a/browser/base/content/test/general/browser_bug592338.js +++ b/browser/base/content/test/general/browser_bug592338.js @@ -48,7 +48,7 @@ function test_install_lwtheme() { is(LightweightThemeManager.currentTheme, null, "Should be no lightweight theme selected"); var pm = Services.perms; - pm.add(makeURI("https://example.com/"), "install", pm.ALLOW_ACTION); + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); gBrowser.selectedTab = gBrowser.addTab("https://example.com/browser/browser/base/content/test/general/bug592338.html"); gBrowser.selectedBrowser.addEventListener("pageshow", function() { diff --git a/browser/base/content/test/plugins/head.js b/browser/base/content/test/plugins/head.js index c1290ed19ebf..4c5d8dd382ab 100644 --- a/browser/base/content/test/plugins/head.js +++ b/browser/base/content/test/plugins/head.js @@ -238,7 +238,7 @@ function clearAllPluginPermissions() { while (perms.hasMoreElements()) { let perm = perms.getNext(); if (perm.type.startsWith('plugin')) { - info("removing permission:" + perm.principal.origin + " " + perm.type + "\n"); + info("removing permission:" + perm.host + " " + perm.type + "\n"); Services.perms.removePermission(perm); } } diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index d212b31d04c6..be7463f4f093 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -2432,9 +2432,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. let normalizedUrl = Services.io.newURI( "https://" + gBrowser.selectedBrowser.currentURI.hostPort, null, null); - // Add the current host/port combination in the 'trackingprotection' consumer of + // Add the current host in the 'trackingprotection' consumer of // the permission manager using a normalized URI. This effectively - // places this host/port combination on the tracking protection allowlist. + // places this host on the tracking protection allowlist. Services.perms.add(normalizedUrl, "trackingprotection", Services.perms.ALLOW_ACTION); BrowserReload(); @@ -2442,13 +2442,10 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. @@ -2564,8 +2561,8 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. return; } - let prePath = this.notification.options.principal.URI.prePath; - this._setupDescription("pluginActivateMultiple.message", null, prePath); + let host = this.notification.options.host; + this._setupDescription("pluginActivateMultiple.message", null, host); var showBox = document.getAnonymousElementByAttribute(this, "anonid", "plugin-notification-showbox"); @@ -2604,7 +2601,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. - + diff --git a/browser/components/preferences/aboutPermissions.js b/browser/components/preferences/aboutPermissions.js index 9b0bcce885ad..50175e4493da 100644 --- a/browser/components/preferences/aboutPermissions.js +++ b/browser/components/preferences/aboutPermissions.js @@ -17,9 +17,6 @@ Cu.import("resource://gre/modules/ForgetAboutSite.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm"); -let gSecMan = Cc["@mozilla.org/scriptsecuritymanager;1"]. - getService(Ci.nsIScriptSecurityManager); - let gFaviconService = Cc["@mozilla.org/browser/favicon-service;1"]. getService(Ci.nsIFaviconService); @@ -29,7 +26,7 @@ let gPlacesDatabase = Cc["@mozilla.org/browser/nav-history-service;1"]. clone(true); let gSitesStmt = gPlacesDatabase.createAsyncStatement( - "SELECT url " + + "SELECT get_unreversed_host(rev_host) AS host " + "FROM moz_places " + "WHERE rev_host > '.' " + "AND visit_count > 0 " + @@ -49,11 +46,14 @@ let gVisitStmt = gPlacesDatabase.createAsyncStatement( let TEST_EXACT_PERM_TYPES = ["geo", "camera", "microphone"]; /** - * Site object represents a single site, uniquely identified by a principal. + * Site object represents a single site, uniquely identified by a host. */ -function Site(principal) { - this.principal = principal; +function Site(host) { + this.host = host; this.listitem = null; + + this.httpURI = NetUtil.newURI("http://" + this.host); + this.httpsURI = NetUtil.newURI("https://" + this.host); } Site.prototype = { @@ -75,10 +75,16 @@ Site.prototype = { } } - // Get the favicon for the origin - gFaviconService.getFaviconURLForPage(this.principal.URI, function (aURI) { + // Try to find favicon for both URIs, but always prefer the https favicon. + gFaviconService.getFaviconURLForPage(this.httpsURI, function (aURI) { if (aURI) { invokeCallback(aURI); + } else { + gFaviconService.getFaviconURLForPage(this.httpURI, function (aURI) { + if (aURI) { + invokeCallback(aURI); + } + }); } }.bind(this)); }, @@ -90,9 +96,7 @@ Site.prototype = { * A function that takes the visit count (a number) as a parameter. */ getVisitCount: function Site_getVisitCount(aCallback) { - // XXX This won't be a very reliable system, as it will count both http: and https: visits - // Unfortunately, I don't think that there is a much better way to do it right now. - let rev_host = this.principal.URI.host.split("").reverse().join("") + "."; + let rev_host = this.host.split("").reverse().join("") + "."; gVisitStmt.params.rev_host = rev_host; gVisitStmt.executeAsync({ handleResult: function(aResults) { @@ -135,9 +139,9 @@ Site.prototype = { let permissionValue; if (TEST_EXACT_PERM_TYPES.indexOf(aType) == -1) { - permissionValue = Services.perms.testPermissionFromPrincipal(this.principal, aType); + permissionValue = Services.perms.testPermission(this.httpURI, aType); } else { - permissionValue = Services.perms.testExactPermissionFromPrincipal(this.principal, aType); + permissionValue = Services.perms.testExactPermission(this.httpURI, aType); } aResultObj.value = permissionValue; @@ -162,7 +166,9 @@ Site.prototype = { return; } - Services.perms.addFromPrincipal(this.principal, aType, aPerm); + // Using httpURI is kind of bogus, but the permission manager stores the + // permission for the host, so the right thing happens in the end. + Services.perms.add(this.httpURI, aType, aPerm); }, /** @@ -173,7 +179,7 @@ Site.prototype = { * e.g. "cookie", "geo", "indexedDB", "popup", "image" */ clearPermission: function Site_clearPermission(aType) { - Services.perms.removeFromPrincipal(this.principal, aType); + Services.perms.remove(this.httpURI, aType); }, /** @@ -183,14 +189,13 @@ Site.prototype = { * @return An array of the cookies set for the site. */ get cookies() { - let host = this.principal.URI.host; let cookies = []; - let enumerator = Services.cookies.getCookiesFromHost(host); + let enumerator = Services.cookies.getCookiesFromHost(this.host); while (enumerator.hasMoreElements()) { let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); // getCookiesFromHost returns cookies for base domain, but we only want // the cookies for the exact domain. - if (cookie.rawHost == host) { + if (cookie.rawHost == this.host) { cookies.push(cookie); } } @@ -212,27 +217,27 @@ Site.prototype = { * @return An array of the logins stored for the site. */ get logins() { - let logins = Services.logins.findLogins({}, this.principal.originNoSuffix, "", ""); - return logins; + let httpLogins = Services.logins.findLogins({}, this.httpURI.prePath, "", ""); + let httpsLogins = Services.logins.findLogins({}, this.httpsURI.prePath, "", ""); + return httpLogins.concat(httpsLogins); }, get loginSavingEnabled() { - return Services.logins.getLoginSavingEnabled(this.principal.originNoSuffix); + // Only say that login saving is blocked if it is blocked for both http and https. + return Services.logins.getLoginSavingEnabled(this.httpURI.prePath) && + Services.logins.getLoginSavingEnabled(this.httpsURI.prePath); }, set loginSavingEnabled(isEnabled) { - Services.logins.setLoginSavingEnabled(this.principal.originNoSuffix, isEnabled); + Services.logins.setLoginSavingEnabled(this.httpURI.prePath, isEnabled); + Services.logins.setLoginSavingEnabled(this.httpsURI.prePath, isEnabled); }, /** * Removes all data from the browser corresponding to the site. */ forgetSite: function Site_forgetSite() { - // XXX This removes data for an entire domain, rather than just - // an origin. This may produce confusing results, as data will - // be cleared for the http:// as well as the https:// domain - // if you try to forget the https:// site. - ForgetAboutSite.removeDataFromDomain(this.principal.URI.host); + ForgetAboutSite.removeDataFromDomain(this.host); } } @@ -361,7 +366,7 @@ let AboutPermissions = { LIST_BUILD_DELAY: 100, // delay between intervals /** - * Stores a mapping of origin strings to Site objects. + * Stores a mapping of host strings to Site objects. */ _sites: {}, @@ -467,9 +472,9 @@ let AboutPermissions = { break; } let permission = aSubject.QueryInterface(Ci.nsIPermission); - // We can't compare selectedSite.principal and permission.principal here - // because we need to handle the case where a parent domain was changed - // in a way that affects the subdomain. + // We can't compare selectedSite.host and permission.host here because + // we need to handle the case where a parent domain was changed in a + // way that affects the subdomain. if (this._supportedPermissions.indexOf(permission.type) != -1) { this.updatePermission(permission.type); } @@ -507,11 +512,8 @@ let AboutPermissions = { AboutPermissions.startSitesListBatch(); let row; while (row = aResults.getNextRow()) { - let spec = row.getResultByName("url"); - let uri = NetUtil.newURI(spec); - let principal = gSecMan.getNoAppCodebasePrincipal(uri); - - AboutPermissions.addPrincipal(principal); + let host = row.getResultByName("host"); + AboutPermissions.addHost(host); } AboutPermissions.endSitesListBatch(); }, @@ -560,8 +562,7 @@ let AboutPermissions = { try { // aLogin.hostname is a string in origin URL format (e.g. "http://foo.com") let uri = NetUtil.newURI(aLogin.hostname); - let principal = gSecMan.getNoAppCodebasePrincipal(uri); - this.addPrincipal(principal); + this.addHost(uri.host); } catch (e) { // newURI will throw for add-ons logins stored in chrome:// URIs } @@ -576,8 +577,7 @@ let AboutPermissions = { try { // aHostname is a string in origin URL format (e.g. "http://foo.com") let uri = NetUtil.newURI(aHostname); - let principal = gSecMan.getNoAppCodebasePrincipal(uri); - this.addPrincipal(principal); + this.addHost(uri.host); } catch (e) { // newURI will throw for add-ons logins stored in chrome:// URIs } @@ -592,7 +592,7 @@ let AboutPermissions = { let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); // Only include sites with exceptions set for supported permission types. if (this._supportedPermissions.indexOf(permission.type) != -1) { - this.addPrincipal(permission.principal); + this.addHost(permission.host); } itemCnt++; } @@ -603,15 +603,15 @@ let AboutPermissions = { /** * Creates a new Site and adds it to _sites if it's not already there. * - * @param aPrincipal - * A principal. + * @param aHost + * A host string. */ - addPrincipal: function(aPrincipal) { - if (aPrincipal.origin in this._sites) { + addHost: function(aHost) { + if (aHost in this._sites) { return; } - let site = new Site(aPrincipal); - this._sites[aPrincipal.origin] = site; + let site = new Site(aHost); + this._sites[aHost] = site; this.addToSitesList(site); }, @@ -624,7 +624,7 @@ let AboutPermissions = { addToSitesList: function(aSite) { let item = document.createElement("richlistitem"); item.setAttribute("class", "site"); - item.setAttribute("value", aSite.principal.origin); + item.setAttribute("value", aSite.host); aSite.getFavicon(function(aURL) { item.setAttribute("favicon", aURL); @@ -633,7 +633,7 @@ let AboutPermissions = { // Make sure to only display relevant items when list is filtered let filterValue = document.getElementById("sites-filter").value.toLowerCase(); - item.collapsed = aSite.principal.origin.toLowerCase().indexOf(filterValue) == -1; + item.collapsed = aSite.host.toLowerCase().indexOf(filterValue) == -1; (this._listFragment || this.sitesList).appendChild(item); }, @@ -686,16 +686,16 @@ let AboutPermissions = { * The host string corresponding to the site to delete. */ deleteFromSitesList: function(aHost) { - for (let origin in this._sites) { - let site = this._sites[origin]; - if (site.principal.URI.host.hasRootDomain(aHost)) { + for (let host in this._sites) { + let site = this._sites[host]; + if (site.host.hasRootDomain(aHost)) { if (site == this._selectedSite) { // Replace site-specific interface with "All Sites" interface. this.sitesList.selectedItem = document.getElementById("all-sites-item"); } this.sitesList.removeChild(site.listitem); - delete this._sites[site.principal.origin]; + delete this._sites[site.host]; } } }, @@ -711,9 +711,9 @@ let AboutPermissions = { return; } - let origin = event.target.value; - let site = this._selectedSite = this._sites[origin]; - document.getElementById("site-label").value = origin; + let host = event.target.value; + let site = this._selectedSite = this._sites[host]; + document.getElementById("site-label").value = host; document.getElementById("header-deck").selectedPanel = document.getElementById("site-header"); @@ -768,9 +768,9 @@ let AboutPermissions = { // If there is no selected site, we are updating the default permissions interface. permissionValue = PermissionDefaults[aType]; if (aType == "cookie") - // cookie-9 corresponds to ALLOW_FIRST_PARTY_ONLY, which is reserved - // for site-specific preferences only. - document.getElementById("cookie-9").hidden = true; + // cookie-9 corresponds to ALLOW_FIRST_PARTY_ONLY, which is reserved + // for site-specific preferences only. + document.getElementById("cookie-9").hidden = true; } else { if (aType == "cookie") document.getElementById("cookie-9").hidden = false; @@ -825,18 +825,18 @@ let AboutPermissions = { * Opens password manager dialog. */ managePasswords: function() { - let selectedOrigin = ""; + let selectedHost = ""; if (this._selectedSite) { - selectedOrigin = this._selectedSite.principal.URI.prePath; + selectedHost = this._selectedSite.host; } let win = Services.wm.getMostRecentWindow("Toolkit:PasswordManager"); if (win) { - win.setFilter(selectedOrigin); + win.setFilter(selectedHost); win.focus(); } else { window.openDialog("chrome://passwordmgr/content/passwordManager.xul", - "Toolkit:PasswordManager", "", {filterString : selectedOrigin}); + "Toolkit:PasswordManager", "", {filterString : selectedHost}); } }, @@ -877,11 +877,9 @@ let AboutPermissions = { * Opens cookie manager dialog. */ manageCookies: function() { - // Cookies are stored by-host, and thus we filter the cookie window - // using only the host of the selected principal's origin let selectedHost = ""; if (this._selectedSite) { - selectedHost = this._selectedSite.principal.URI.host; + selectedHost = this._selectedSite.host; } let win = Services.wm.getMostRecentWindow("Browser:Cookies"); diff --git a/browser/components/preferences/advanced.js b/browser/components/preferences/advanced.js index a1210971da37..f2fd2cb23b2a 100644 --- a/browser/components/preferences/advanced.js +++ b/browser/components/preferences/advanced.js @@ -9,7 +9,6 @@ Components.utils.import("resource://gre/modules/ctypes.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/LoadContextInfo.jsm"); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -Components.utils.import("resource://gre/modules/BrowserUtils.jsm"); var gAdvancedPane = { _inited: false, @@ -472,7 +471,7 @@ var gAdvancedPane = { var usage = 0; for (var i = 0; i < groups.length; i++) { var uri = ios.newURI(groups[i], null, null); - if (perm.matchesURI(uri, true)) { + if (uri.asciiHost == perm.host) { var cache = cacheService.getActiveCache(groups[i]); usage += cache.usage; } @@ -509,7 +508,7 @@ var gAdvancedPane = { var row = document.createElement("listitem"); row.id = ""; row.className = "offlineapp"; - row.setAttribute("origin", perm.principal.origin); + row.setAttribute("host", perm.host); var converted = DownloadUtils. convertByteUnits(this._getOfflineAppUsage(perm, groups)); row.setAttribute("usage", @@ -535,8 +534,7 @@ var gAdvancedPane = { { var list = document.getElementById("offlineAppsList"); var item = list.selectedItem; - var origin = item.getAttribute("origin"); - var principal = BrowserUtils.principalFromOrigin(origin); + var host = item.getAttribute("host"); var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); @@ -545,18 +543,13 @@ var gAdvancedPane = { var bundle = document.getElementById("bundlePreferences"); var title = bundle.getString("offlineAppRemoveTitle"); - var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [principal.URI.prePath]); + var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [host]); var confirm = bundle.getString("offlineAppRemoveConfirm"); var result = prompts.confirmEx(window, title, prompt, flags, confirm, null, null, null, {}); if (result != 0) return; - // get the permission - var pm = Components.classes["@mozilla.org/permissionmanager;1"] - .getService(Components.interfaces.nsIPermissionManager); - var perm = pm.getPermissionObject(principal, "offline-app"); - // clear offline cache entries var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"]. getService(Components.interfaces.nsIApplicationCacheService); @@ -564,14 +557,25 @@ var gAdvancedPane = { getService(Components.interfaces.nsIIOService); var groups = cacheService.getGroups(); for (var i = 0; i < groups.length; i++) { - var uri = ios.newURI(groups[i], null, null); - if (perm.matchesURI(uri, true)) { + let uri = ios.newURI(groups[i], null, null); + if (uri.asciiHost == host) { var cache = cacheService.getActiveCache(groups[i]); cache.discard(); } } - pm.removePermission(perm); + // remove the permission + var pm = Components.classes["@mozilla.org/permissionmanager;1"] + .getService(Components.interfaces.nsIPermissionManager); + let uri; + try { + // file:// URIs are stored with their scheme. We try to parse them first, as + // URIs like http://file:///foo/bar/baz.html will parse as HTTP URIs. + uri = ios.newURI(host, null, null); + } catch (e) { + uri = ios.newURI("http://" + host, null, null); + } + pm.remove(uri, "offline-app"); list.removeChild(item); gAdvancedPane.offlineAppSelected(); diff --git a/browser/components/preferences/in-content/advanced.js b/browser/components/preferences/in-content/advanced.js index 629b167762f9..a8f65a9b7f3a 100644 --- a/browser/components/preferences/in-content/advanced.js +++ b/browser/components/preferences/in-content/advanced.js @@ -502,7 +502,7 @@ var gAdvancedPane = { var usage = 0; for (var i = 0; i < groups.length; i++) { var uri = ios.newURI(groups[i], null, null); - if (perm.matchesURI(uri, true)) { + if (uri.asciiHost == perm.host) { var cache = cacheService.getActiveCache(groups[i]); usage += cache.usage; } @@ -544,7 +544,7 @@ var gAdvancedPane = { var row = document.createElement("listitem"); row.id = ""; row.className = "offlineapp"; - row.setAttribute("origin", perm.principal.origin); + row.setAttribute("host", perm.host); var converted = DownloadUtils. convertByteUnits(this._getOfflineAppUsage(perm, groups)); row.setAttribute("usage", @@ -570,8 +570,7 @@ var gAdvancedPane = { { var list = document.getElementById("offlineAppsList"); var item = list.selectedItem; - var origin = item.getAttribute("origin"); - var principal = BrowserUtils.principalFromOrigin(origin); + var host = item.getAttribute("host"); var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); @@ -580,18 +579,13 @@ var gAdvancedPane = { var bundle = document.getElementById("bundlePreferences"); var title = bundle.getString("offlineAppRemoveTitle"); - var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [principal.URI.prePath]); + var prompt = bundle.getFormattedString("offlineAppRemovePrompt", [host]); var confirm = bundle.getString("offlineAppRemoveConfirm"); var result = prompts.confirmEx(window, title, prompt, flags, confirm, null, null, null, {}); if (result != 0) return; - // get the permission - var pm = Components.classes["@mozilla.org/permissionmanager;1"] - .getService(Components.interfaces.nsIPermissionManager); - var perm = pm.getPermissionObject(principal, "offline-app"); - // clear offline cache entries try { var cacheService = Components.classes["@mozilla.org/network/application-cache-service;1"]. @@ -600,15 +594,26 @@ var gAdvancedPane = { getService(Components.interfaces.nsIIOService); var groups = cacheService.getGroups(); for (var i = 0; i < groups.length; i++) { - var uri = ios.newURI(groups[i], null, null); - if (perm.matchesURI(uri, true)) { + let uri = ios.newURI(groups[i], null, null); + if (uri.asciiHost == host) { var cache = cacheService.getActiveCache(groups[i]); cache.discard(); } } } catch (e) {} - pm.removePermission(perm); + // remove the permission + var pm = Components.classes["@mozilla.org/permissionmanager;1"] + .getService(Components.interfaces.nsIPermissionManager); + let uri; + try { + // file:// URIs are stored with their scheme. We try to parse them first, as + // URIs like http://file:///foo/bar/baz.html will parse as HTTP URIs. + uri = ios.newURI(host, null, null); + } catch (e) { + uri = ios.newURI("http://" + host, null, null); + } + pm.remove(uri, "offline-app"); list.removeChild(item); gAdvancedPane.offlineAppSelected(); diff --git a/browser/components/preferences/permissions.js b/browser/components/preferences/permissions.js index 8a531573f63d..ec55dfb432dd 100644 --- a/browser/components/preferences/permissions.js +++ b/browser/components/preferences/permissions.js @@ -9,10 +9,10 @@ const nsICookiePermission = Components.interfaces.nsICookiePermission; const NOTIFICATION_FLUSH_PERMISSIONS = "flush-pending-permissions"; -function Permission(principal, type, capability) +function Permission(host, rawHost, type, capability) { - this.principal = principal; - this.origin = principal.origin; + this.host = host; + this.rawHost = rawHost; this.type = type; this.capability = capability; } @@ -35,7 +35,7 @@ var gPermissionManager = { getCellText: function (aRow, aColumn) { if (aColumn.id == "siteCol") - return gPermissionManager._permissions[aRow].origin; + return gPermissionManager._permissions[aRow].rawHost; else if (aColumn.id == "statusCol") return gPermissionManager._permissions[aRow].capability; return ""; @@ -82,17 +82,10 @@ var gPermissionManager = { addPermission: function (aCapability) { var textbox = document.getElementById("url"); - var input_url = textbox.value.replace(/^\s*/, ""); // trim any leading space - let principal; + var host = textbox.value.replace(/^\s*([-\w]*:\/+)?/, ""); // trim any leading space and scheme try { - // If the uri doesn't successfully parse, try adding a http:// and parsing again - let uri; - try { - let uri = Services.io.newURI(input_url, null, null); - } catch(ex) { - uri = Services.io.newURI("http://" + input_url, null, null); - } - principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri); + var uri = Services.io.newURI("http://"+host, null, null); + host = uri.host; } catch(ex) { var message = this._bundle.getString("invalidURI"); var title = this._bundle.getString("invalidURITitle"); @@ -103,11 +96,11 @@ var gPermissionManager = { var capabilityString = this._getCapabilityString(aCapability); // check whether the permission already exists, if not, add it - let permissionExists = false; + let hostExists = false; let capabilityExists = false; for (var i = 0; i < this._permissions.length; ++i) { - if (this._permissions[i].principal.equals(principal)) { - permissionExists = true; + if (this._permissions[i].rawHost == host) { + hostExists = true; capabilityExists = this._permissions[i].capability == capabilityString; if (!capabilityExists) { this._permissions[i].capability = capabilityString; @@ -116,14 +109,14 @@ var gPermissionManager = { } } - let permissionParams = {principal: principal, type: this._type, capability: aCapability}; - if (!permissionExists) { - this._permissionsToAdd.set(principal.origin, permissionParams); + let permissionParams = {host: host, type: this._type, capability: aCapability}; + if (!hostExists) { + this._permissionsToAdd.set(host, permissionParams); this._addPermission(permissionParams); } else if (!capabilityExists) { - this._permissionsToAdd.set(principal.origin, permissionParams); - this._handleCapabilityChange(); + this._permissionsToAdd.set(host, permissionParams); + this._handleCapabilityChange(); } textbox.value = ""; @@ -138,15 +131,15 @@ var gPermissionManager = { _removePermission: function(aPermission) { - this._removePermissionFromList(aPermission.principal); + this._removePermissionFromList(aPermission.host); // If this permission was added during this session, let's remove // it from the pending adds list to prevent calls to the // permission manager. - let isNewPermission = this._permissionsToAdd.delete(aPermission.principal.origin); + let isNewPermission = this._permissionsToAdd.delete(aPermission.host); if (!isNewPermission) { - this._permissionsToDelete.set(aPermission.principal.origin, aPermission); + this._permissionsToDelete.set(aPermission.host, aPermission); } }, @@ -283,7 +276,7 @@ var gPermissionManager = { } else if (aData == "changed") { for (var i = 0; i < this._permissions.length; ++i) { - if (permission.matches(this._permissions[i].principal, true)) { + if (this._permissions[i].host == permission.host) { this._permissions[i].capability = this._getCapabilityString(permission.capability); break; } @@ -370,11 +363,13 @@ var gPermissionManager = { this.uninit(); for (let permissionParams of this._permissionsToAdd.values()) { - Services.perms.addFromPrincipal(permissionParams.principal, permissionParams.type, permissionParams.capability); + let uri = Services.io.newURI("http://" + permissionParams.host, null, null); + Services.perms.add(uri, permissionParams.type, permissionParams.capability); } for (let p of this._permissionsToDelete.values()) { - Services.perms.removeFromPrincipal(p.principal, p.type); + let uri = Services.io.newURI("http://" + p.host, null, null); + Services.perms.remove(uri, p.type); } window.close(); @@ -397,7 +392,7 @@ var gPermissionManager = { // sort and display the table this._tree.view = this._view; - this.onPermissionSort("origin"); + this.onPermissionSort("rawHost"); // disable "remove all" button if there are none document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0; @@ -409,19 +404,20 @@ var gPermissionManager = { (!this._manageCapability || (aPermission.capability == this._manageCapability))) { - var principal = aPermission.principal; + var host = aPermission.host; var capabilityString = this._getCapabilityString(aPermission.capability); - var p = new Permission(principal, + var p = new Permission(host, + (host.charAt(0) == ".") ? host.substring(1,host.length) : host, aPermission.type, capabilityString); this._permissions.push(p); } }, - _removePermissionFromList: function (aPrincipal) + _removePermissionFromList: function (aHost) { for (let i = 0; i < this._permissions.length; ++i) { - if (this._permissions[i].principal.equals(aPrincipal)) { + if (this._permissions[i].host == aHost) { this._permissions.splice(i, 1); this._view._rowCount--; this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1); @@ -431,15 +427,15 @@ var gPermissionManager = { } }, - setOrigin: function (aOrigin) + setHost: function (aHost) { - document.getElementById("url").value = aOrigin; + document.getElementById("url").value = aHost; } }; -function setOrigin(aOrigin) +function setHost(aHost) { - gPermissionManager.setOrigin(aOrigin); + gPermissionManager.setHost(aHost); } function initWithParams(aParams) diff --git a/browser/components/preferences/tests/browser_chunk_permissions.js b/browser/components/preferences/tests/browser_chunk_permissions.js index 4c53c6c7fe20..cf88dd329118 100644 --- a/browser/components/preferences/tests/browser_chunk_permissions.js +++ b/browser/components/preferences/tests/browser_chunk_permissions.js @@ -103,11 +103,11 @@ var tests = [ sitesFilter.doCommand(); }, run: function() { - let testSite1 = getSiteItem(TEST_URI_1.prePath); + let testSite1 = getSiteItem(TEST_URI_1.host); ok(testSite1.collapsed, "test site 1 is collapsed after early filtering"); - let testSite2 = getSiteItem(TEST_URI_2.prePath); + let testSite2 = getSiteItem(TEST_URI_2.host); ok(!testSite2.collapsed, "test site 2 is not collapsed after early filtering"); - let testSite3 = getSiteItem(TEST_URI_3.prePath); + let testSite3 = getSiteItem(TEST_URI_3.host); ok(testSite3.collapsed, "test site 3 is collapsed after early filtering"); runNextTest(); @@ -119,11 +119,11 @@ var tests = [ ForgetAboutSite.removeDataFromDomain(TEST_URI_2.host); }, run: function() { - let testSite1 = getSiteItem(TEST_URI_1.prePath); + let testSite1 = getSiteItem(TEST_URI_1.host); ok(testSite1, "test site 1 was not removed from sites list"); - let testSite2 = getSiteItem(TEST_URI_2.prePath); + let testSite2 = getSiteItem(TEST_URI_2.host); ok(!testSite2, "test site 2 was pre-removed from sites list"); - let testSite3 = getSiteItem(TEST_URI_3.prePath); + let testSite3 = getSiteItem(TEST_URI_3.host); ok(testSite3, "test site 3 was not removed from sites list"); runNextTest(); @@ -131,7 +131,7 @@ var tests = [ } ]; -function getSiteItem(aPrePath) { +function getSiteItem(aHost) { return gBrowser.contentDocument. - querySelector(".site[value='" + aPrePath + "']"); + querySelector(".site[value='" + aHost + "']"); } diff --git a/browser/components/preferences/tests/browser_cookies_exceptions.js b/browser/components/preferences/tests/browser_cookies_exceptions.js index 92ed070b75c7..52798c43b019 100644 --- a/browser/components/preferences/tests/browser_cookies_exceptions.js +++ b/browser/components/preferences/tests/browser_cookies_exceptions.js @@ -20,7 +20,7 @@ var testRunner = { "permission text should be set correctly"); params.btnApplyChanges.doCommand(); }, - observances: [{ type: "cookie", origin: "http://test.com", data: "added", + observances: [{ type: "cookie", host: "test.com", data: "added", capability: Ci.nsIPermissionManager.ALLOW_ACTION }], }, { @@ -31,7 +31,7 @@ var testRunner = { "permission should change to deny in UI"); params.btnApplyChanges.doCommand(); }, - observances: [{ type: "cookie", origin: "http://test.com", data: "changed", + observances: [{ type: "cookie", host: "test.com", data: "changed", capability: Ci.nsIPermissionManager.DENY_ACTION }], }, { @@ -42,7 +42,7 @@ var testRunner = { "permission should revert back to allow"); params.btnApplyChanges.doCommand(); }, - observances: [{ type: "cookie", origin: "http://test.com", data: "changed", + observances: [{ type: "cookie", host: "test.com", data: "changed", capability: Ci.nsIPermissionManager.ALLOW_ACTION }], }, { @@ -52,7 +52,7 @@ var testRunner = { is(params.tree.view.rowCount, 0, "exception should be removed"); params.btnApplyChanges.doCommand(); }, - observances: [{ type: "cookie", origin: "http://test.com", data: "deleted" }], + observances: [{ type: "cookie", host: "test.com", data: "deleted" }], }, { test: function(params) { @@ -61,7 +61,7 @@ var testRunner = { is(params.tree.view.rowCount, 0, "adding unrelated permission should not change display"); params.btnApplyChanges.doCommand(); }, - observances: [{ type: "popup", origin: "http://test.com", data: "added", + observances: [{ type: "popup", host: "test.com", data: "added", capability: Ci.nsIPermissionManager.DENY_ACTION }], cleanUp: function(params) { let uri = params.ioService.newURI("http://test.com", null, null); @@ -160,17 +160,12 @@ var testRunner = { let expected = testRunner.tests[testRunner._currentTest].observances.shift(); is(aData, expected.data, "type of message should be the same"); - for each (let prop in ["type", "capability"]) { + for each (let prop in ["type", "host", "capability"]) { if (expected[prop]) is(permission[prop], expected[prop], "property: \"" + prop + "\" should be equal"); } - if (expected.origin) { - is(permission.principal.origin, expected.origin, - "property: \"origin\" should be equal"); - } - os.removeObserver(permObserver, "perm-changed"); if (testRunner.tests[testRunner._currentTest].cleanup) { diff --git a/browser/components/preferences/tests/browser_permissions.js b/browser/components/preferences/tests/browser_permissions.js index 84d91e030247..2a896b3e0dae 100644 --- a/browser/components/preferences/tests/browser_permissions.js +++ b/browser/components/preferences/tests/browser_permissions.js @@ -115,8 +115,8 @@ var tests = [ is(gSitesList.firstChild.id, "all-sites-item", "all sites is the first item in the sites list"); - ok(getSiteItem(TEST_URI_1.prePath), "site item from places db exists"); - ok(getSiteItem(TEST_URI_2.prePath), "site item from enumerating services exists"); + ok(getSiteItem(TEST_URI_1.host), "site item from places db exists"); + ok(getSiteItem(TEST_URI_2.host), "site item from enumerating services exists"); runNextTest(); }, @@ -128,9 +128,9 @@ var tests = [ sitesFilter.doCommand(); // make sure correct sites are collapsed/showing - let testSite1 = getSiteItem(TEST_URI_1.prePath); + let testSite1 = getSiteItem(TEST_URI_1.host); ok(!testSite1.collapsed, "test site 1 is not collapsed"); - let testSite2 = getSiteItem(TEST_URI_2.prePath); + let testSite2 = getSiteItem(TEST_URI_2.host); ok(testSite2.collapsed, "test site 2 is collapsed"); // clear filter @@ -202,13 +202,13 @@ var tests = [ function test_select_site() { // select the site that has the permissions we set at the beginning of the test - let testSiteItem = getSiteItem(TEST_URI_2.prePath); + let testSiteItem = getSiteItem(TEST_URI_2.host); gSitesList.selectedItem = testSiteItem; let siteHeader = gBrowser.contentDocument.getElementById("site-header"); is(siteHeader, gHeaderDeck.selectedPanel, "correct header shown for a specific site"); - is(gSiteLabel.value, TEST_URI_2.prePath, "header updated for selected site"); + is(gSiteLabel.value, TEST_URI_2.host, "header updated for selected site"); ok(!gBrowser.contentDocument.getElementById("passwords-count").hidden, "passwords count is not hidden"); @@ -283,7 +283,7 @@ var tests = [ "all sites item selected after forgetting selected site"); // check to make sure site is gone from sites list - let testSiteItem = getSiteItem(TEST_URI_2.prePath); + let testSiteItem = getSiteItem(TEST_URI_2.host); ok(!testSiteItem, "site removed from sites list"); // check to make sure we forgot all permissions corresponding to site diff --git a/browser/components/preferences/translation.js b/browser/components/preferences/translation.js index 4f353c53e2e6..a1ae8988b1b4 100644 --- a/browser/components/preferences/translation.js +++ b/browser/components/preferences/translation.js @@ -9,7 +9,6 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/BrowserUtils.jsm"); XPCOMUtils.defineLazyGetter(this, "gLangBundle", () => Services.strings.createBundle("chrome://global/locale/languageNames.properties")); @@ -84,7 +83,7 @@ let gTranslationExceptions = { if (perm.type == kPermissionType && perm.capability == Services.perms.DENY_ACTION) { - this._sites.push(perm.principal.origin); + this._sites.push(perm.host); } } Services.obs.addObserver(this, "perm-changed", false); @@ -127,14 +126,14 @@ let gTranslationExceptions = { if (aData == "added") { if (perm.capability != Services.perms.DENY_ACTION) return; - this._sites.push(perm.principal.origin); + this._sites.push(perm.host); this._sites.sort(); let boxObject = this._siteTree.boxObject; boxObject.rowCountChanged(0, 1); boxObject.invalidate(); } else if (aData == "deleted") { - let index = this._sites.indexOf(perm.principal.origin); + let index = this._sites.indexOf(perm.host); if (index == -1) return; this._sites.splice(index, 1); @@ -189,9 +188,9 @@ let gTranslationExceptions = { onSiteDeleted: function() { let removedSites = this._siteTree.getSelectedItems(); - for (let origin of removedSites) { - let principal = BrowserUtils.principalFromOrigin(origin); - Services.perms.removeFromPrincipal(principal, kPermissionType); + for (let host of removedSites) { + let uri = Services.io.newURI("http://" + host, null, null); + Services.perms.remove(uri, kPermissionType); } }, @@ -202,9 +201,9 @@ let gTranslationExceptions = { let removedSites = this._sites.splice(0, this._sites.length); this._siteTree.boxObject.rowCountChanged(0, -removedSites.length); - for (let origin of removedSites) { - let principal = BrowserUtils.principalFromOrigin(origin); - Services.perms.removeFromPrincipal(principal, kPermissionType); + for (let host of removedSites) { + let uri = Services.io.newURI("http://" + host, null, null); + Services.perms.remove(uri, kPermissionType); } this.onSiteSelected(); diff --git a/browser/components/translation/test/browser_translation_exceptions.js b/browser/components/translation/test/browser_translation_exceptions.js index 086614bfd293..25a4b113dff6 100644 --- a/browser/components/translation/test/browser_translation_exceptions.js +++ b/browser/components/translation/test/browser_translation_exceptions.js @@ -51,7 +51,7 @@ function getDomainExceptions() { if (perm.type == "translate" && perm.capability == Services.perms.DENY_ACTION) - results.push(perm.principal); + results.push(perm.host); } return results; @@ -181,7 +181,7 @@ let gTests = [ // Check this has been saved to the exceptions list. let sites = getDomainExceptions(); is(sites.length, 1, "one site in the exception list"); - is(sites[0].origin, "http://example.com", "correct site in the exception list"); + is(sites[0], "example.com", "correct site in the exception list"); ok(!ui.shouldShowInfoBar(uri, "fr"), "the infobar wouldn't be shown anymore"); diff --git a/browser/components/uitour/test/head.js b/browser/components/uitour/test/head.js index 330a174c622d..73e5e4c66b2a 100644 --- a/browser/components/uitour/test/head.js +++ b/browser/components/uitour/test/head.js @@ -198,10 +198,8 @@ function loadUITourTestPage(callback, host = "https://example.com/") { function UITourTest() { Services.prefs.setBoolPref("browser.uitour.enabled", true); - let testHttpsUri = Services.io.newURI("https://example.com", null, null); - let testHttpUri = Services.io.newURI("http://example.com", null, null); - Services.perms.add(testHttpsUri, "uitour", Services.perms.ALLOW_ACTION); - Services.perms.add(testHttpUri, "uitour", Services.perms.ALLOW_ACTION); + let testUri = Services.io.newURI("http://example.com", null, null); + Services.perms.add(testUri, "uitour", Services.perms.ALLOW_ACTION); waitForExplicitFinish(); @@ -212,8 +210,7 @@ function UITourTest() { gBrowser.removeTab(gTestTab); delete window.gTestTab; Services.prefs.clearUserPref("browser.uitour.enabled", true); - Services.perms.remove(testHttpsUri, "uitour"); - Services.perms.remove(testHttpUri, "uitour"); + Services.perms.remove(testUri, "uitour"); }); function done() { diff --git a/browser/modules/PluginContent.jsm b/browser/modules/PluginContent.jsm index a188013a11f5..5d968dab5dd6 100644 --- a/browser/modules/PluginContent.jsm +++ b/browser/modules/PluginContent.jsm @@ -695,6 +695,20 @@ PluginContent.prototype = { this._showClickToPlayNotification(null, false); }, + // Match the behaviour of nsPermissionManager + _getHostFromPrincipal: function (principal) { + if (!principal.URI || principal.URI.schemeIs("moz-nullprincipal")) { + return "(null)"; + } + + try { + if (principal.URI.host) + return principal.URI.host; + } catch (e) {} + + return principal.origin; + }, + /** * Activate the plugins that the user has specified. */ @@ -762,6 +776,7 @@ PluginContent.prototype = { let pluginData = this.pluginData; let principal = this.content.document.nodePrincipal; + let principalHost = this._getHostFromPrincipal(principal); let location = this.content.document.location.href; for (let p of plugins) { @@ -777,11 +792,11 @@ PluginContent.prototype = { let permissionObj = Services.perms. getPermissionObject(principal, pluginInfo.permissionString, false); if (permissionObj) { - pluginInfo.pluginPermissionPrePath = permissionObj.principal.originNoSuffix; + pluginInfo.pluginPermissionHost = permissionObj.host; pluginInfo.pluginPermissionType = permissionObj.expireType; } else { - pluginInfo.pluginPermissionPrePath = principal.originNoSuffix; + pluginInfo.pluginPermissionHost = principalHost; pluginInfo.pluginPermissionType = undefined; } @@ -791,6 +806,7 @@ PluginContent.prototype = { this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", { plugins: [... this.pluginData.values()], showNow: showNow, + host: principalHost, location: location, }, null, principal); }, @@ -873,6 +889,7 @@ PluginContent.prototype = { this.global.sendAsyncMessage("PluginContent:UpdateHiddenPluginUI", { haveInsecure: haveInsecure, actions: [... actions.values()], + host: this._getHostFromPrincipal(principal), location: location, }, null, principal); }, diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp index 2b21258ec9fe..3cf84fa26913 100644 --- a/caps/BasePrincipal.cpp +++ b/caps/BasePrincipal.cpp @@ -51,7 +51,7 @@ OriginAttributes::CreateSuffix(nsACString& aStr) const params->Serialize(value); if (!value.IsEmpty()) { - aStr.AppendLiteral("^"); + aStr.AppendLiteral("!"); aStr.Append(NS_ConvertUTF16toUTF8(value)); } } @@ -117,7 +117,7 @@ OriginAttributes::PopulateFromSuffix(const nsACString& aStr) return true; } - if (aStr[0] != '^') { + if (aStr[0] != '!') { return false; } @@ -134,7 +134,7 @@ OriginAttributes::PopulateFromOrigin(const nsACString& aOrigin, { // RFindChar is only available on nsCString. nsCString origin(aOrigin); - int32_t pos = origin.RFindChar('^'); + int32_t pos = origin.RFindChar('!'); if (pos == kNotFound) { aOriginNoSuffix = origin; diff --git a/caps/nsPrincipal.cpp b/caps/nsPrincipal.cpp index 302e6188cfae..ed3b6b017a0a 100644 --- a/caps/nsPrincipal.cpp +++ b/caps/nsPrincipal.cpp @@ -14,7 +14,6 @@ #include "pratom.h" #include "nsIURI.h" #include "nsIURL.h" -#include "nsIStandardURL.h" #include "nsIURIWithPrincipal.h" #include "nsJSPrincipals.h" #include "nsIEffectiveTLDService.h" @@ -128,31 +127,6 @@ nsPrincipal::GetOriginForURI(nsIURI* aURI, nsACString& aOrigin) } } - // We want the invariant that prinA.origin == prinB.origin i.f.f. - // prinA.equals(prinB). However, this requires that we impose certain constraints - // on the behavior and origin semantics of principals, and in particular, forbid - // creating origin strings for principals whose equality constraints are not - // expressible as strings (i.e. object equality). Moreover, we want to forbid URIs - // containing the magic "^" we use as a separating character for origin - // attributes. - // - // These constraints can generally be achieved by restricting .origin to - // nsIStandardURL-based URIs, but there are a few other URI schemes that we need - // to handle. - bool isBehaved; - if ((NS_SUCCEEDED(origin->SchemeIs("about", &isBehaved)) && isBehaved) || - (NS_SUCCEEDED(origin->SchemeIs("moz-safe-about", &isBehaved)) && isBehaved) || - (NS_SUCCEEDED(origin->SchemeIs("indexeddb", &isBehaved)) && isBehaved)) { - rv = origin->GetAsciiSpec(aOrigin); - NS_ENSURE_SUCCESS(rv, rv); - // These URIs could technically contain a '^', but they never should. - if (NS_WARN_IF(aOrigin.FindChar('^', 0) != -1)) { - aOrigin.Truncate(); - return NS_ERROR_FAILURE; - } - return NS_OK; - } - int32_t port; if (NS_SUCCEEDED(rv) && !isChrome) { rv = origin->GetPort(&port); @@ -170,14 +144,6 @@ nsPrincipal::GetOriginForURI(nsIURI* aURI, nsACString& aOrigin) aOrigin.Append(hostPort); } else { - // If we reached this branch, we can only create an origin if we have a nsIStandardURL. - // So, we query to a nsIStandardURL, and fail if we aren't an instance of an nsIStandardURL - // nsIStandardURLs have the good property of escaping the '^' character in their specs, - // which means that we can be sure that the caret character (which is reserved for delimiting - // the end of the spec, and the beginning of the origin attributes) is not present in the - // origin string - nsCOMPtr standardURL = do_QueryInterface(origin); - NS_ENSURE_TRUE(standardURL, NS_ERROR_FAILURE); rv = origin->GetAsciiSpec(aOrigin); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/caps/tests/unit/test_origin.js b/caps/tests/unit/test_origin.js index 020ae3a24043..ef462719e7c4 100644 --- a/caps/tests/unit/test_origin.js +++ b/caps/tests/unit/test_origin.js @@ -68,33 +68,33 @@ function run_test() { // Just app. var exampleOrg_app = ssm.createCodebasePrincipal(makeURI('http://example.org'), {appId: 42}); var nullPrin_app = ssm.createNullPrincipal({appId: 42}); - checkOriginAttributes(exampleOrg_app, {appId: 42}, '^appId=42'); - checkOriginAttributes(nullPrin_app, {appId: 42}, '^appId=42'); - do_check_eq(exampleOrg_app.origin, 'http://example.org^appId=42'); + checkOriginAttributes(exampleOrg_app, {appId: 42}, '!appId=42'); + checkOriginAttributes(nullPrin_app, {appId: 42}, '!appId=42'); + do_check_eq(exampleOrg_app.origin, 'http://example.org!appId=42'); // Just browser. var exampleOrg_browser = ssm.createCodebasePrincipal(makeURI('http://example.org'), {inBrowser: true}); var nullPrin_browser = ssm.createNullPrincipal({inBrowser: true}); - checkOriginAttributes(exampleOrg_browser, {inBrowser: true}, '^inBrowser=1'); - checkOriginAttributes(nullPrin_browser, {inBrowser: true}, '^inBrowser=1'); - do_check_eq(exampleOrg_browser.origin, 'http://example.org^inBrowser=1'); + checkOriginAttributes(exampleOrg_browser, {inBrowser: true}, '!inBrowser=1'); + checkOriginAttributes(nullPrin_browser, {inBrowser: true}, '!inBrowser=1'); + do_check_eq(exampleOrg_browser.origin, 'http://example.org!inBrowser=1'); // App and browser. var exampleOrg_appBrowser = ssm.createCodebasePrincipal(makeURI('http://example.org'), {inBrowser: true, appId: 42}); var nullPrin_appBrowser = ssm.createNullPrincipal({inBrowser: true, appId: 42}); - checkOriginAttributes(exampleOrg_appBrowser, {appId: 42, inBrowser: true}, '^appId=42&inBrowser=1'); - checkOriginAttributes(nullPrin_appBrowser, {appId: 42, inBrowser: true}, '^appId=42&inBrowser=1'); - do_check_eq(exampleOrg_appBrowser.origin, 'http://example.org^appId=42&inBrowser=1'); + checkOriginAttributes(exampleOrg_appBrowser, {appId: 42, inBrowser: true}, '!appId=42&inBrowser=1'); + checkOriginAttributes(nullPrin_appBrowser, {appId: 42, inBrowser: true}, '!appId=42&inBrowser=1'); + do_check_eq(exampleOrg_appBrowser.origin, 'http://example.org!appId=42&inBrowser=1'); // App and browser, different domain. var exampleCom_appBrowser = ssm.createCodebasePrincipal(makeURI('https://www.example.com:123'), {appId: 42, inBrowser: true}); - checkOriginAttributes(exampleCom_appBrowser, {appId: 42, inBrowser: true}, '^appId=42&inBrowser=1'); - do_check_eq(exampleCom_appBrowser.origin, 'https://www.example.com:123^appId=42&inBrowser=1'); + checkOriginAttributes(exampleCom_appBrowser, {appId: 42, inBrowser: true}, '!appId=42&inBrowser=1'); + do_check_eq(exampleCom_appBrowser.origin, 'https://www.example.com:123!appId=42&inBrowser=1'); // Addon. var exampleOrg_addon = ssm.createCodebasePrincipal(makeURI('http://example.org'), {addonId: 'dummy'}); - checkOriginAttributes(exampleOrg_addon, { addonId: "dummy" }, '^addonId=dummy'); - do_check_eq(exampleOrg_addon.origin, 'http://example.org^addonId=dummy'); + checkOriginAttributes(exampleOrg_addon, { addonId: "dummy" }, '!addonId=dummy'); + do_check_eq(exampleOrg_addon.origin, 'http://example.org!addonId=dummy'); // Check that all of the above are cross-origin. checkCrossOrigin(exampleOrg_app, exampleOrg); diff --git a/dom/apps/tests/unit/test_moziapplication.js b/dom/apps/tests/unit/test_moziapplication.js index b7989f82e04a..b5609d8564c1 100644 --- a/dom/apps/tests/unit/test_moziapplication.js +++ b/dom/apps/tests/unit/test_moziapplication.js @@ -51,7 +51,7 @@ add_test(() => { }); Assert.ok(mozapp.principal, "app principal should exist"); - let expectedPrincipalOrigin = app.origin + "^appId=" + app.localId; + let expectedPrincipalOrigin = app.origin + "!appId=" + app.localId; Assert.equal(mozapp.principal.origin, expectedPrincipalOrigin, "app principal origin ok"); Assert.equal(mozapp.principal.appId, app.localId, "app principal appId ok"); diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index ea4e78821f31..b4acda4172d8 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -53,7 +53,6 @@ #include "mozilla/plugins/PluginModuleParent.h" #include "mozilla/widget/WidgetMessageUtils.h" #include "mozilla/media/MediaChild.h" -#include "mozilla/BasePrincipal.h" #if defined(MOZ_CONTENT_SANDBOX) #if defined(XP_WIN) @@ -2113,15 +2112,18 @@ ContentChild::RecvAddPermission(const IPC::Permission& permission) MOZ_ASSERT(permissionManager, "We have no permissionManager in the Content process !"); - nsAutoCString originNoSuffix; - OriginAttributes attrs; - attrs.PopulateFromOrigin(permission.origin, originNoSuffix); - nsCOMPtr uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix); - NS_ENSURE_SUCCESS(rv, true); + NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + nsCString(permission.host)); + NS_ENSURE_TRUE(uri, true); - nsCOMPtr principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs); + nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); + MOZ_ASSERT(secMan); + + nsCOMPtr principal; + nsresult rv = secMan->GetAppCodebasePrincipal(uri, permission.appId, + permission.isInBrowserElement, + getter_AddRefs(principal)); + NS_ENSURE_SUCCESS(rv, true); // child processes don't care about modification time. int64_t modificationTime = 0; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index a2d750d344eb..c2b484bd3221 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -2559,12 +2559,12 @@ ContentParent::RecvReadPermissions(InfallibleTArray* aPermissio enumerator->GetNext(getter_AddRefs(supp)); nsCOMPtr perm = do_QueryInterface(supp); - nsCOMPtr principal; - perm->GetPrincipal(getter_AddRefs(principal)); - nsCString origin; - if (principal) { - principal->GetOrigin(origin); - } + nsCString host; + perm->GetHost(host); + uint32_t appId; + perm->GetAppId(&appId); + bool isInBrowserElement; + perm->GetIsInBrowserElement(&isInBrowserElement); nsCString type; perm->GetType(type); uint32_t capability; @@ -2574,7 +2574,8 @@ ContentParent::RecvReadPermissions(InfallibleTArray* aPermissio int64_t expireTime; perm->GetExpireTime(&expireTime); - aPermissions->AppendElement(IPC::Permission(origin, type, + aPermissions->AppendElement(IPC::Permission(host, appId, + isInBrowserElement, type, capability, expireType, expireTime)); } diff --git a/dom/storage/DOMStorageObserver.cpp b/dom/storage/DOMStorageObserver.cpp index 54ca84016398..29a20ed98722 100644 --- a/dom/storage/DOMStorageObserver.cpp +++ b/dom/storage/DOMStorageObserver.cpp @@ -196,20 +196,8 @@ DOMStorageObserver::Observe(nsISupports* aSubject, return NS_OK; } - nsCOMPtr principal; - perm->GetPrincipal(getter_AddRefs(principal)); - if (!principal) { - return NS_OK; - } - - nsCOMPtr origin; - principal->GetURI(getter_AddRefs(origin)); - if (!origin) { - return NS_OK; - } - nsAutoCString host; - origin->GetHost(host); + perm->GetHost(host); if (host.IsEmpty()) { return NS_OK; } diff --git a/extensions/cookie/nsPermission.cpp b/extensions/cookie/nsPermission.cpp index 9438b0d6643a..bdd3abad0e5e 100644 --- a/extensions/cookie/nsPermission.cpp +++ b/extensions/cookie/nsPermission.cpp @@ -1,37 +1,51 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* 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/. */ #include "nsPermission.h" -#include "nsContentUtils.h" #include "nsIClassInfoImpl.h" -#include "nsIEffectiveTLDService.h" -#include "mozilla/BasePrincipal.h" // nsPermission Implementation NS_IMPL_CLASSINFO(nsPermission, nullptr, 0, {0}) NS_IMPL_ISUPPORTS_CI(nsPermission, nsIPermission) -nsPermission::nsPermission(nsIPrincipal* aPrincipal, +nsPermission::nsPermission(const nsACString &aHost, + uint32_t aAppId, + bool aIsInBrowserElement, const nsACString &aType, uint32_t aCapability, uint32_t aExpireType, int64_t aExpireTime) - : mPrincipal(aPrincipal) + : mHost(aHost) , mType(aType) , mCapability(aCapability) , mExpireType(aExpireType) , mExpireTime(aExpireTime) + , mAppId(aAppId) + , mIsInBrowserElement(aIsInBrowserElement) { } NS_IMETHODIMP -nsPermission::GetPrincipal(nsIPrincipal** aPrincipal) +nsPermission::GetHost(nsACString &aHost) { - nsCOMPtr copy = mPrincipal; - copy.forget(aPrincipal); + aHost = mHost; + return NS_OK; +} + +NS_IMETHODIMP +nsPermission::GetAppId(uint32_t* aAppId) +{ + *aAppId = mAppId; + return NS_OK; +} + +NS_IMETHODIMP +nsPermission::GetIsInBrowserElement(bool* aIsInBrowserElement) +{ + *aIsInBrowserElement = mIsInBrowserElement; return NS_OK; } @@ -62,117 +76,3 @@ nsPermission::GetExpireTime(int64_t *aExpireTime) *aExpireTime = mExpireTime; return NS_OK; } - -NS_IMETHODIMP -nsPermission::Matches(nsIPrincipal* aPrincipal, bool aExactHost, bool* aMatches) -{ - NS_ENSURE_ARG_POINTER(aPrincipal); - NS_ENSURE_ARG_POINTER(aMatches); - - *aMatches = false; - - // If the principals are equal, then they match. - if (mPrincipal->Equals(aPrincipal)) { - *aMatches = true; - return NS_OK; - } - - // If we are matching with an exact host, we're done now - the permissions don't match - // otherwise, we need to start comparing subdomains! - if (aExactHost) { - return NS_OK; - } - - // Compare their OriginAttributes - const mozilla::OriginAttributes& theirAttrs = mozilla::BasePrincipal::Cast(aPrincipal)->OriginAttributesRef(); - const mozilla::OriginAttributes& ourAttrs = mozilla::BasePrincipal::Cast(mPrincipal)->OriginAttributesRef(); - - if (theirAttrs != ourAttrs) { - return NS_OK; - } - - nsCOMPtr theirURI; - nsresult rv = aPrincipal->GetURI(getter_AddRefs(theirURI)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr ourURI; - rv = mPrincipal->GetURI(getter_AddRefs(ourURI)); - NS_ENSURE_SUCCESS(rv, rv); - - // Compare schemes - nsAutoCString theirScheme; - rv = theirURI->GetScheme(theirScheme); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoCString ourScheme; - rv = ourURI->GetScheme(ourScheme); - NS_ENSURE_SUCCESS(rv, rv); - - if (theirScheme != ourScheme) { - return NS_OK; - } - - // Compare ports - int32_t theirPort; - rv = theirURI->GetPort(&theirPort); - NS_ENSURE_SUCCESS(rv, rv); - - int32_t ourPort; - rv = ourURI->GetPort(&ourPort); - NS_ENSURE_SUCCESS(rv, rv); - - if (theirPort != ourPort) { - return NS_OK; - } - - // Check if the host or any subdomain of their host matches. - nsAutoCString theirHost; - rv = theirURI->GetHost(theirHost); - if (NS_FAILED(rv) || theirHost.IsEmpty()) { - return NS_OK; - } - - nsAutoCString ourHost; - rv = ourURI->GetHost(ourHost); - if (NS_FAILED(rv) || ourHost.IsEmpty()) { - return NS_OK; - } - - nsCOMPtr tldService = - do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); - if (!tldService) { - NS_ERROR("Should have a tld service!"); - return NS_ERROR_FAILURE; - } - - // This loop will not loop forever, as GetNextSubDomain will eventually fail - // with NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS. - while (theirHost != ourHost) { - rv = tldService->GetNextSubDomain(theirHost, theirHost); - if (NS_FAILED(rv)) { - if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) { - return NS_OK; - } else { - return rv; - } - } - } - - *aMatches = true; - return NS_OK; -} - -NS_IMETHODIMP -nsPermission::MatchesURI(nsIURI* aURI, bool aExactHost, bool* aMatches) -{ - NS_ENSURE_ARG_POINTER(aURI); - - nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); - NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE); - - nsCOMPtr principal; - nsresult rv = secMan->GetNoAppCodebasePrincipal(aURI, getter_AddRefs(principal)); - NS_ENSURE_SUCCESS(rv, rv); - - return Matches(principal, aExactHost, aMatches); -} diff --git a/extensions/cookie/nsPermission.h b/extensions/cookie/nsPermission.h index 1e966e4b78b8..8e459fb59f71 100644 --- a/extensions/cookie/nsPermission.h +++ b/extensions/cookie/nsPermission.h @@ -18,7 +18,9 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSIPERMISSION - nsPermission(nsIPrincipal* aPrincipal, + nsPermission(const nsACString &aHost, + uint32_t aAppId, + bool aIsInBrowserElement, const nsACString &aType, uint32_t aCapability, uint32_t aExpireType, @@ -27,11 +29,13 @@ public: protected: virtual ~nsPermission() {}; - nsCOMPtr mPrincipal; + nsCString mHost; nsCString mType; uint32_t mCapability; uint32_t mExpireType; int64_t mExpireTime; + uint32_t mAppId; + bool mIsInBrowserElement; }; #endif // nsPermission_h__ diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp index ded7500c62de..1364886c36b5 100644 --- a/extensions/cookie/nsPermissionManager.cpp +++ b/extensions/cookie/nsPermissionManager.cpp @@ -8,7 +8,6 @@ #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ContentChild.h" -#include "mozilla/BasePrincipal.h" #include "mozilla/unused.h" #include "nsPermissionManager.h" #include "nsPermission.h" @@ -40,8 +39,6 @@ #include "nsReadLine.h" #include "mozilla/Telemetry.h" #include "nsIConsoleService.h" -#include "nsINavHistoryService.h" -#include "nsToolkitCompsCID.h" static nsPermissionManager *gPermissionManager = nullptr; @@ -104,31 +101,29 @@ LogToConsole(const nsAString& aMsg) namespace { nsresult -GetPrincipalFromOrigin(const nsACString& aOrigin, nsIPrincipal** aPrincipal) -{ - nsAutoCString originNoSuffix; - mozilla::OriginAttributes attrs; - if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs); - principal.forget(aPrincipal); - return NS_OK; -} - - -nsresult -GetPrincipal(nsIURI* aURI, uint32_t aAppId, bool aIsInBrowserElement, nsIPrincipal** aPrincipal) +GetPrincipal(const nsACString& aHost, uint32_t aAppId, bool aIsInBrowserElement, + nsIPrincipal** aPrincipal) { nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE); - return secMan->GetAppCodebasePrincipal(aURI, aAppId, aIsInBrowserElement, aPrincipal); + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost); + if (NS_FAILED(rv)) { + // NOTE: most callers will end up here because we don't append "http://" for + // hosts. It's fine to arbitrary use "http://" because, for those entries, + // we will actually just use the host. If we end up here, but the host looks + // like an email address, we use mailto: instead. + nsCString scheme; + if (aHost.FindChar('@') == -1) + scheme = NS_LITERAL_CSTRING("http://"); + else + scheme = NS_LITERAL_CSTRING("mailto:"); + rv = NS_NewURI(getter_AddRefs(uri), scheme + aHost); + NS_ENSURE_SUCCESS(rv, rv); + } + + return secMan->GetAppCodebasePrincipal(uri, aAppId, aIsInBrowserElement, aPrincipal); } nsresult @@ -140,6 +135,50 @@ GetPrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal) return secMan->GetNoAppCodebasePrincipal(aURI, aPrincipal); } +nsresult +GetPrincipal(const nsACString& aHost, nsIPrincipal** aPrincipal) +{ + return GetPrincipal(aHost, nsIScriptSecurityManager::NO_APP_ID, false, aPrincipal); +} + +nsresult +GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost) +{ + nsCOMPtr uri; + nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, rv); + + uri = NS_GetInnermostURI(uri); + NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); + + rv = uri->GetAsciiHost(aHost); + if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) { + return NS_OK; + } + + // For the mailto scheme, we use the path of the URI. We have to chop off the + // query part if one exists, so we eliminate everything after a ?. + bool isMailTo = false; + if (NS_SUCCEEDED(uri->SchemeIs("mailto", &isMailTo)) && isMailTo) { + rv = uri->GetPath(aHost); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t spart = aHost.FindChar('?', 0); + if (spart >= 0) { + aHost.Cut(spart, aHost.Length() - spart); + } + return NS_OK; + } + + // Some entries like "file://" uses the origin. + rv = aPrincipal->GetOriginNoSuffix(aHost); + if (NS_SUCCEEDED(rv) && !aHost.IsEmpty()) { + return NS_OK; + } + + return NS_ERROR_UNEXPECTED; +} + nsCString GetNextSubDomainForHost(const nsACString& aHost) { @@ -195,265 +234,6 @@ public: NS_IMPL_ISUPPORTS(AppClearDataObserver, nsIObserver) -class MOZ_STACK_CLASS UpgradeHostToOriginHelper { -public: - virtual nsresult Insert(nsIPrincipal* aPrincipal, const nsAFlatCString& aType, - uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, - int64_t aModificationTime) = 0; -}; - -class UpgradeHostToOriginDBMigration final : public UpgradeHostToOriginHelper { -public: - UpgradeHostToOriginDBMigration(mozIStorageConnection* aDBConn, int64_t* aID) : mDBConn(aDBConn) - , mID(aID) - { - mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO moz_hosts_new " - "(id, origin, type, permission, expireType, expireTime, modificationTime) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), getter_AddRefs(mInsertStmt)); - - mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT id FROM moz_hosts_new WHERE origin = ?1"), - getter_AddRefs(mLookupStmt)); - } - - nsresult - Insert(nsIPrincipal* aPrincipal, const nsAFlatCString& aType, - uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, - int64_t aModificationTime) final - { - nsAutoCString origin; - nsresult rv = aPrincipal->GetOrigin(origin); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mLookupStmt->Reset(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mLookupStmt->BindUTF8StringByIndex(0, origin); - NS_ENSURE_SUCCESS(rv, rv); - - // Check to see if the origin already exists in the database - bool hasResult = false; - if (NS_SUCCEEDED(mLookupStmt->ExecuteStep(&hasResult)) && hasResult) { - mLookupStmt->Reset(); - return NS_OK; - } - - rv = mInsertStmt->BindInt64ByIndex(0, *mID); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mInsertStmt->BindUTF8StringByIndex(1, origin); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mInsertStmt->BindUTF8StringByIndex(2, aType); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mInsertStmt->BindInt32ByIndex(3, aPermission); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mInsertStmt->BindInt32ByIndex(4, aExpireType); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mInsertStmt->BindInt64ByIndex(5, aExpireTime); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mInsertStmt->BindInt64ByIndex(6, aModificationTime); - NS_ENSURE_SUCCESS(rv, rv); - - // Increment the working identifier, as we are about to use this one - (*mID)++; - - rv = mInsertStmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; - } - -private: - nsCOMPtr mLookupStmt; - nsCOMPtr mInsertStmt; - nsCOMPtr mDBConn; - int64_t* mID; -}; - -class UpgradeHostToOriginHostfileImport final : public UpgradeHostToOriginHelper { -public: - UpgradeHostToOriginHostfileImport(nsPermissionManager* aPm, - nsPermissionManager::DBOperationType aOperation, - int64_t aID) : mPm(aPm) - , mOperation(aOperation) - , mID(aID) - {} - - nsresult - Insert(nsIPrincipal* aPrincipal, const nsAFlatCString& aType, - uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, - int64_t aModificationTime) final - { - // AddInternal won't do anything if the permission already exists - return mPm->AddInternal(aPrincipal, aType, aPermission, mID, - aExpireType, aExpireTime, aModificationTime, - nsPermissionManager::eDontNotify, mOperation); - } - -private: - nsRefPtr mPm; - nsPermissionManager::DBOperationType mOperation; - int64_t mID; -}; - -nsresult -UpgradeHostToOriginAndInsert(const nsACString& aHost, const nsAFlatCString& aType, - uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, - int64_t aModificationTime, uint32_t aAppId, bool aIsInBrowserElement, - UpgradeHostToOriginHelper* aHelper) -{ - if (aHost.EqualsLiteral("")) { - // We no longer support the magic host - NS_WARNING("The magic host is no longer supported. " - "It is being removed from the permissions database."); - return NS_OK; - } - - // First, we check to see if the host is a valid URI. If it is, it can be imported directly - nsCOMPtr uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), aHost); - if (NS_SUCCEEDED(rv)) { - // It was previously possible to insert useless entries to your permissions database - // for URIs which have a null principal. This acts as a cleanup, getting rid of - // these useless database entries - bool nullpScheme = false; - if (NS_SUCCEEDED(uri->SchemeIs("moz-nullprincipal", &nullpScheme)) && nullpScheme) { - NS_WARNING("A moz-nullprincipal: permission is being discarded."); - return NS_OK; - } - - nsCOMPtr principal; - rv = GetPrincipal(uri, aAppId, aIsInBrowserElement, getter_AddRefs(principal)); - NS_ENSURE_SUCCESS(rv, rv); - - return aHelper->Insert(principal, aType, aPermission, - aExpireType, aExpireTime, aModificationTime); - return NS_OK; - } - - // The user may use this host at non-standard ports or protocols, we can use their history - // to guess what ports and protocols we want to add permissions for. - // We find every URI which they have visited with this host (or a subdomain of this host), - // and try to add it as a principal. - nsCOMPtr histSrv = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID); - - nsCOMPtr histQuery; - rv = histSrv->GetNewQuery(getter_AddRefs(histQuery)); - NS_ENSURE_SUCCESS(rv, rv); - - // We want to only find history items for this particular host, and subdomains - rv = histQuery->SetDomain(aHost); - NS_ENSURE_SUCCESS(rv, rv); - - rv = histQuery->SetDomainIsHost(false); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr histQueryOpts; - rv = histSrv->GetNewQueryOptions(getter_AddRefs(histQueryOpts)); - NS_ENSURE_SUCCESS(rv, rv); - - // We want to get the URIs for every item in the user's history with the given host - rv = histQueryOpts->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI); - NS_ENSURE_SUCCESS(rv, rv); - - // We only search history, because searching both bookmarks and history - // is not supported, and history tends to be more comprehensive. - rv = histQueryOpts->SetQueryType(nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY); - NS_ENSURE_SUCCESS(rv, rv); - - // We include hidden URIs (such as those visited via iFrames) as they may have permissions too - rv = histQueryOpts->SetIncludeHidden(true); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr histResult; - rv = histSrv->ExecuteQuery(histQuery, histQueryOpts, getter_AddRefs(histResult)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr histResultContainer; - rv = histResult->GetRoot(getter_AddRefs(histResultContainer)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = histResultContainer->SetContainerOpen(true); - NS_ENSURE_SUCCESS(rv, rv); - - uint32_t childCount = 0; - rv = histResultContainer->GetChildCount(&childCount); - NS_ENSURE_SUCCESS(rv, rv); - - bool foundHistory = false; - for (uint32_t i = 0; i < childCount; i++) { - nsCOMPtr child; - histResultContainer->GetChild(i, getter_AddRefs(child)); - if (NS_FAILED(rv)) continue; - - uint32_t type; - rv = child->GetType(&type); - if (NS_FAILED(rv) || type != nsINavHistoryResultNode::RESULT_TYPE_URI) { - NS_WARNING("Unexpected non-RESULT_TYPE_URI node in " - "UpgradeHostToOriginAndInsert()"); - continue; - } - - nsAutoCString uriSpec; - rv = child->GetUri(uriSpec); - if (NS_FAILED(rv)) continue; - - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), uriSpec); - if (NS_FAILED(rv)) continue; - - // Use the provided host - this URI may be for a subdomain, rather than the host we care about. - rv = uri->SetHost(aHost); - if (NS_FAILED(rv)) continue; - - // We now have a URI which we can make a nsIPrincipal out of - nsCOMPtr principal; - rv = GetPrincipal(uri, aAppId, aIsInBrowserElement, getter_AddRefs(principal)); - if (NS_FAILED(rv)) continue; - - // Insert it! (The backend should be able to deal with us inserting the same origin repeatedly) - foundHistory = true; - rv = aHelper->Insert(principal, aType, aPermission, - aExpireType, aExpireTime, aModificationTime); - NS_WARN_IF(NS_FAILED(rv)); - } - - rv = histResultContainer->SetContainerOpen(false); - NS_ENSURE_SUCCESS(rv, rv); - - // If we didn't find any origins for this host in the poermissions database, - // we can insert the default http:// and https:// permissions into the database. - // This has a relatively high liklihood of applying the permission to the correct - // origin. - if (!foundHistory) { - rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aHost); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr principal; - rv = GetPrincipal(uri, aAppId, aIsInBrowserElement, getter_AddRefs(principal)); - NS_ENSURE_SUCCESS(rv, rv); - - aHelper->Insert(principal, aType, aPermission, - aExpireType, aExpireTime, aModificationTime); - } - rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("https://") + aHost); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr principal; - rv = GetPrincipal(uri, aAppId, aIsInBrowserElement, getter_AddRefs(principal)); - NS_ENSURE_SUCCESS(rv, rv); - - aHelper->Insert(principal, aType, aPermission, - aExpireType, aExpireTime, aModificationTime); - } - } - - return NS_OK; -} - static bool IsExpandedPrincipal(nsIPrincipal* aPrincipal) { @@ -467,7 +247,9 @@ IsExpandedPrincipal(nsIPrincipal* aPrincipal) nsPermissionManager::PermissionKey::PermissionKey(nsIPrincipal* aPrincipal) { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetOrigin(mOrigin))); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetHostForPrincipal(aPrincipal, mHost))); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId))); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement))); } /** @@ -590,7 +372,7 @@ nsPermissionManager::AppClearDataObserverInit() // nsPermissionManager Implementation #define PERMISSIONS_FILE_NAME "permissions.sqlite" -#define HOSTS_SCHEMA_VERSION 5 +#define HOSTS_SCHEMA_VERSION 4 #define HOSTPERM_FILE_NAME "hostperm.1" @@ -833,89 +615,6 @@ nsPermissionManager::InitDB(bool aRemoveFile) // fall through to the next upgrade - // Version 4->5 is the merging of host, appId, and isInBrowserElement into origin - case 4: - { - bool tableExists = false; - mDBConn->TableExists(NS_LITERAL_CSTRING("moz_hosts_new"), &tableExists); - if (tableExists) { - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts_new")); - NS_ENSURE_SUCCESS(rv, rv); - } - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE moz_hosts_new (" - " id INTEGER PRIMARY KEY" - ",origin TEXT" - ",type TEXT" - ",permission INTEGER" - ",expireType INTEGER" - ",expireTime INTEGER" - ",modificationTime INTEGER" - ")")); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr stmt; - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement " - "FROM moz_hosts"), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr pending; - - int64_t id = 0; - nsAutoCString host, type; - uint32_t permission; - uint32_t expireType; - int64_t expireTime; - int64_t modificationTime; - uint32_t appId; - bool isInBrowserElement; - bool hasResult; - - while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { - // Read in the old row - rv = stmt->GetUTF8String(0, host); - if (NS_FAILED(rv)) { - continue; - } - rv = stmt->GetUTF8String(1, type); - if (NS_FAILED(rv)) { - continue; - } - permission = stmt->AsInt32(2); - expireType = stmt->AsInt32(3); - expireTime = stmt->AsInt64(4); - modificationTime = stmt->AsInt64(5); - if (stmt->AsInt64(6) < 0) { - continue; - } - appId = static_cast(stmt->AsInt64(6)); - isInBrowserElement = static_cast(stmt->AsInt32(7)); - - UpgradeHostToOriginDBMigration upHelper(mDBConn, &id); - rv = UpgradeHostToOriginAndInsert(host, type, permission, - expireType, expireTime, - modificationTime, appId, - isInBrowserElement, - &upHelper); - if (NS_FAILED(rv)) { - NS_WARNING("Unexpected failure when upgrading migrating permission from host to origin"); - } - } - - // We rename the old table to moz_hosts_v4 instead of dropping it, such that if - // we discover that there was a problem with our migration code in the future, we have information - // to roll-back with. - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ALTER TABLE moz_hosts RENAME TO moz_hosts_v4")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ALTER TABLE moz_hosts_new RENAME TO moz_hosts")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBConn->SetSchemaVersion(HOSTS_SCHEMA_VERSION); - NS_ENSURE_SUCCESS(rv, rv); - } - // current version. case HOSTS_SCHEMA_VERSION: break; @@ -931,7 +630,7 @@ nsPermissionManager::InitDB(bool aRemoveFile) // check if all the expected columns exist nsCOMPtr stmt; rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT origin, type, permission, expireType, expireTime, modificationTime FROM moz_hosts"), + "SELECT host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement FROM moz_hosts"), getter_AddRefs(stmt)); if (NS_SUCCEEDED(rv)) break; @@ -950,8 +649,8 @@ nsPermissionManager::InitDB(bool aRemoveFile) // cache frequently used statements (for insertion, deletion, and updating) rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( "INSERT INTO moz_hosts " - "(id, origin, type, permission, expireType, expireTime, modificationTime) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), getter_AddRefs(mStmtInsert)); + "(id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) " + "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)"), getter_AddRefs(mStmtInsert)); NS_ENSURE_SUCCESS(rv, rv); rv = mDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( @@ -988,12 +687,14 @@ nsPermissionManager::CreateTable() return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( "CREATE TABLE moz_hosts (" " id INTEGER PRIMARY KEY" - ",origin TEXT" + ",host TEXT" ",type TEXT" ",permission INTEGER" ",expireType INTEGER" ",expireTime INTEGER" ",modificationTime INTEGER" + ",appId INTEGER" + ",isInBrowserElement INTEGER" ")")); } @@ -1040,15 +741,6 @@ nsPermissionManager::AddFromPrincipal(nsIPrincipal* aPrincipal, return NS_OK; } - // Null principals can't meaningfully have persisted permissions attached to - // them, so we don't allow adding permissions for them. - bool isNullPrincipal; - nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal); - NS_ENSURE_SUCCESS(rv, rv); - if (isNullPrincipal) { - return NS_OK; - } - // Permissions may not be added to expanded principals. if (IsExpandedPrincipal(aPrincipal)) { return NS_ERROR_INVALID_ARG; @@ -1073,13 +765,21 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, DBOperationType aDBOperation, const bool aIgnoreSessionPermissions) { - nsAutoCString origin; - nsresult rv = aPrincipal->GetOrigin(origin); + nsAutoCString host; + nsresult rv = GetHostForPrincipal(aPrincipal, host); NS_ENSURE_SUCCESS(rv, rv); if (!IsChildProcess()) { - IPC::Permission permission(origin, aType, aPermission, - aExpireType, aExpireTime); + uint32_t appId; + rv = aPrincipal->GetAppId(&appId); + NS_ENSURE_SUCCESS(rv, rv); + + bool isInBrowserElement; + rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); + NS_ENSURE_SUCCESS(rv, rv); + + IPC::Permission permission(host, appId, isInBrowserElement, aType, + aPermission, aExpireType, aExpireTime); nsTArray cplist; ContentParent::GetAll(cplist); @@ -1191,11 +891,21 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, aModificationTime)); if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) { - UpdateDB(op, mStmtInsert, id, origin, aType, aPermission, aExpireType, aExpireTime, aModificationTime); + uint32_t appId; + rv = aPrincipal->GetAppId(&appId); + NS_ENSURE_SUCCESS(rv, rv); + + bool isInBrowserElement; + rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); + NS_ENSURE_SUCCESS(rv, rv); + + UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime, aModificationTime, appId, isInBrowserElement); } if (aNotifyOperation == eNotify) { - NotifyObserversWithPermission(aPrincipal, + NotifyObserversWithPermission(host, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, mTypeArray[typeIndex], aPermission, aExpireType, @@ -1216,10 +926,12 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, // We care only about the id here so we pass dummy values for all other // parameters. UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0, - nsIPermissionManager::EXPIRE_NEVER, 0, 0); + nsIPermissionManager::EXPIRE_NEVER, 0, 0, 0, false); if (aNotifyOperation == eNotify) { - NotifyObserversWithPermission(aPrincipal, + NotifyObserversWithPermission(host, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, mTypeArray[typeIndex], oldPermissionEntry.mPermission, oldPermissionEntry.mExpireType, @@ -1262,10 +974,12 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, // We care only about the id, the permission and expireType/expireTime/modificationTime here. // We pass dummy values for all other parameters. UpdateDB(op, mStmtUpdate, id, EmptyCString(), EmptyCString(), - aPermission, aExpireType, aExpireTime, aModificationTime); + aPermission, aExpireType, aExpireTime, aModificationTime, 0, false); if (aNotifyOperation == eNotify) { - NotifyObserversWithPermission(aPrincipal, + NotifyObserversWithPermission(host, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, mTypeArray[typeIndex], aPermission, aExpireType, @@ -1316,12 +1030,14 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); NS_ENSURE_SUCCESS(rv, rv); - UpdateDB(eOperationAdding, mStmtInsert, id, origin, aType, aPermission, - aExpireType, aExpireTime, aModificationTime); + UpdateDB(eOperationAdding, mStmtInsert, id, host, aType, aPermission, + aExpireType, aExpireTime, aModificationTime, appId, isInBrowserElement); } if (aNotifyOperation == eNotify) { - NotifyObserversWithPermission(aPrincipal, + NotifyObserversWithPermission(host, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, mTypeArray[typeIndex], aPermission, aExpireType, @@ -1382,8 +1098,12 @@ nsPermissionManager::RemoveFromPrincipal(nsIPrincipal* aPrincipal, NS_IMETHODIMP nsPermissionManager::RemovePermission(nsIPermission* aPerm) { + nsAutoCString host; + nsresult rv = aPerm->GetHost(host); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr principal; - nsresult rv = aPerm->GetPrincipal(getter_AddRefs(principal)); + rv = GetPrincipal(host, getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString type; @@ -1550,12 +1270,25 @@ nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal, return NS_ERROR_INVALID_ARG; } + nsAutoCString host; + nsresult rv = GetHostForPrincipal(aPrincipal, host); + NS_ENSURE_SUCCESS(rv, rv); + int32_t typeIndex = GetTypeIndex(aType, false); // If type == -1, the type isn't known, // so just return NS_OK if (typeIndex == -1) return NS_OK; - PermissionHashKey* entry = GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch); + uint32_t appId; + rv = aPrincipal->GetAppId(&appId); + NS_ENSURE_SUCCESS(rv, rv); + + bool isInBrowserElement; + rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); + NS_ENSURE_SUCCESS(rv, rv); + + PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement, + typeIndex, aExactHostMatch); if (!entry) { return NS_OK; } @@ -1567,12 +1300,10 @@ nsPermissionManager::GetPermissionObject(nsIPrincipal* aPrincipal, return NS_OK; } - nsCOMPtr principal; - nsresult rv = GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal)); - NS_ENSURE_SUCCESS(rv, rv); - PermissionEntry& perm = entry->GetPermissions()[idx]; - nsCOMPtr r = new nsPermission(principal, + nsCOMPtr r = new nsPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, mTypeArray.ElementAt(perm.mType), perm.mPermission, perm.mExpireType, @@ -1624,12 +1355,25 @@ nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal, return NS_OK; } + nsAutoCString host; + nsresult rv = GetHostForPrincipal(aPrincipal, host); + NS_ENSURE_SUCCESS(rv, rv); + int32_t typeIndex = GetTypeIndex(aType, false); // If type == -1, the type isn't known, // so just return NS_OK if (typeIndex == -1) return NS_OK; - PermissionHashKey* entry = GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch); + uint32_t appId; + rv = aPrincipal->GetAppId(&appId); + NS_ENSURE_SUCCESS(rv, rv); + + bool isInBrowserElement; + rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); + NS_ENSURE_SUCCESS(rv, rv); + + PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement, + typeIndex, aExactHostMatch); if (!entry || (!aIncludingSession && entry->GetPermission(typeIndex).mNonSessionExpireType == @@ -1651,13 +1395,15 @@ nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal, // Also accepts host on the format "". This will perform an exact match // lookup as the string doesn't contain any dots. nsPermissionManager::PermissionHashKey* -nsPermissionManager::GetPermissionHashKey(nsIPrincipal* aPrincipal, +nsPermissionManager::GetPermissionHashKey(const nsACString& aHost, + uint32_t aAppId, + bool aIsInBrowserElement, uint32_t aType, bool aExactHostMatch) { PermissionHashKey* entry = nullptr; - nsRefPtr key = new PermissionKey(aPrincipal); + nsRefPtr key = new PermissionKey(aHost, aAppId, aIsInBrowserElement); entry = mPermissionTable.GetEntry(key); if (entry) { @@ -1669,8 +1415,13 @@ nsPermissionManager::GetPermissionHashKey(nsIPrincipal* aPrincipal, (permEntry.mExpireType == nsIPermissionManager::EXPIRE_SESSION && permEntry.mExpireTime != 0)) && permEntry.mExpireTime <= (PR_Now() / 1000)) { + nsCOMPtr principal; + if (NS_FAILED(GetPrincipal(aHost, aAppId, aIsInBrowserElement, getter_AddRefs(principal)))) { + return nullptr; + } + entry = nullptr; - RemoveFromPrincipal(aPrincipal, mTypeArray[aType].get()); + RemoveFromPrincipal(principal, mTypeArray[aType].get()); } else if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) { entry = nullptr; } @@ -1680,42 +1431,24 @@ nsPermissionManager::GetPermissionHashKey(nsIPrincipal* aPrincipal, return entry; } - // If aExactHostMatch wasn't true, we can check if the base domain has a permission entry. + // If we haven't found an entry, depending on the host, we could try a bit + // harder. + // If this is a file:// URI, we can check for the presence of the magic entry + // which gives permission to all file://. This hack might disappear, + // see bug 817007. Note that we don't require aExactHostMatch to be true for + // that to keep retro-compatibility. + // If this is not a file:// URI, and that aExactHostMatch wasn't true, we can + // check if the base domain has a permission entry. + + if (StringBeginsWith(aHost, NS_LITERAL_CSTRING("file://"))) { + return GetPermissionHashKey(NS_LITERAL_CSTRING(""), aAppId, aIsInBrowserElement, aType, true); + } + if (!aExactHostMatch) { - nsCOMPtr uri; - nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); - if (NS_FAILED(rv)) { - return nullptr; + nsCString domain = GetNextSubDomainForHost(aHost); + if (!domain.IsEmpty()) { + return GetPermissionHashKey(domain, aAppId, aIsInBrowserElement, aType, aExactHostMatch); } - - nsAutoCString host; - rv = uri->GetHost(host); - if (NS_FAILED(rv)) { - return nullptr; - } - - nsCString domain = GetNextSubDomainForHost(host); - if (domain.IsEmpty()) { - return nullptr; - } - - // Create a new principal which is identical to the current one, but with the new host - nsCOMPtr newURI; - rv = uri->Clone(getter_AddRefs(newURI)); - if (NS_FAILED(rv)) { - return nullptr; - } - - rv = newURI->SetHost(domain); - if (NS_FAILED(rv)) { - return nullptr; - } - - // Copy the attributes over - mozilla::OriginAttributes attrs = mozilla::BasePrincipal::Cast(aPrincipal)->OriginAttributesRef(); - nsCOMPtr principal = mozilla::BasePrincipal::CreateCodebasePrincipal(newURI, attrs); - - return GetPermissionHashKey(principal, aType, aExactHostMatch); } // No entry, really... @@ -1752,10 +1485,9 @@ AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg) continue; } - nsCOMPtr principal; - GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal)); - - nsPermission *perm = new nsPermission(principal, + nsPermission *perm = new nsPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, data->types->ElementAt(permEntry.mType), permEntry.mPermission, permEntry.mExpireType, @@ -1810,10 +1542,9 @@ AddPermissionsModifiedSinceToList( continue; } - nsCOMPtr principal; - GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal)); - - nsPermission* perm = new nsPermission(principal, + nsPermission* perm = new nsPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, data->types->ElementAt(permEntry.mType), permEntry.mPermission, permEntry.mExpireType, @@ -1836,21 +1567,22 @@ nsPermissionManager::RemoveAllModifiedSince(int64_t aModificationTime) mPermissionTable.EnumerateEntries(AddPermissionsModifiedSinceToList, &data); for (int32_t i = 0; i principal; + nsAutoCString host; + bool isInBrowserElement = false; nsAutoCString type; + uint32_t appId = 0; - nsresult rv = array[i]->GetPrincipal(getter_AddRefs(principal)); - if (NS_FAILED(rv)) { + array[i]->GetHost(host); + array[i]->GetIsInBrowserElement(&isInBrowserElement); + array[i]->GetType(type); + array[i]->GetAppId(&appId); + + nsCOMPtr principal; + if (NS_FAILED(GetPrincipal(host, appId, isInBrowserElement, + getter_AddRefs(principal)))) { NS_ERROR("GetPrincipal() failed!"); continue; } - - rv = array[i]->GetType(type); - if (NS_FAILED(rv)) { - NS_ERROR("GetType() failed!"); - continue; - } - // AddInternal handles removal, so let it do the work... AddInternal( principal, @@ -1875,21 +1607,14 @@ nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; - nsCOMPtr principal; - GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal)); - - uint32_t appId = nsIScriptSecurityManager::NO_APP_ID; - principal->GetAppId(&appId); - - bool isInBrowserElement; - principal->GetIsInBrowserElement(&isInBrowserElement); - - if (appId != data->appId || - (data->browserOnly && !isInBrowserElement)) { + if (entry->GetKey()->mAppId != data->appId || + (data->browserOnly && !entry->GetKey()->mIsInBrowserElement)) { continue; } - data->permissions.AppendObject(new nsPermission(principal, + data->permissions.AppendObject(new nsPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, gPermissionManager->mTypeArray.ElementAt(permEntry.mType), permEntry.mPermission, permEntry.mExpireType, @@ -1905,20 +1630,49 @@ nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly) ENSURE_NOT_CHILD_PROCESS; NS_ENSURE_ARG(aAppId != nsIScriptSecurityManager::NO_APP_ID); + // We begin by removing all the permissions from the DB. + // After clearing the DB, we call AddInternal() to make sure that all + // processes are aware of this change and the representation of the DB in + // memory is updated. // We have to get all permissions associated with an application and then // remove those because doing so in EnumerateEntries() would fail because // we might happen to actually delete entries from the list. + nsAutoCString sql; + sql.AppendLiteral("DELETE FROM moz_hosts WHERE appId="); + sql.AppendInt(aAppId); + + if (aBrowserOnly) { + sql.AppendLiteral(" AND isInBrowserElement=1"); + } + + nsCOMPtr removeStmt; + nsresult rv = mDBConn->CreateAsyncStatement(sql, getter_AddRefs(removeStmt)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr pending; + rv = removeStmt->ExecuteAsync(nullptr, getter_AddRefs(pending)); + NS_ENSURE_SUCCESS(rv, rv); + GetPermissionsForAppStruct data(aAppId, aBrowserOnly); mPermissionTable.EnumerateEntries(GetPermissionsForApp, &data); for (int32_t i=0; i principal; + nsAutoCString host; + bool isInBrowserElement; nsAutoCString type; - data.permissions[i]->GetPrincipal(getter_AddRefs(principal)); + data.permissions[i]->GetHost(host); + data.permissions[i]->GetIsInBrowserElement(&isInBrowserElement); data.permissions[i]->GetType(type); + nsCOMPtr principal; + if (NS_FAILED(GetPrincipal(host, aAppId, isInBrowserElement, + getter_AddRefs(principal)))) { + NS_ERROR("GetPrincipal() failed!"); + continue; + } + AddInternal(principal, type, nsIPermissionManager::UNKNOWN_ACTION, @@ -1927,7 +1681,7 @@ nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly) 0, 0, nsPermissionManager::eNotify, - nsPermissionManager::eWriteToDB); + nsPermissionManager::eNoDBOperation); } return NS_OK; @@ -1937,16 +1691,10 @@ PLDHashOperator nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator( nsPermissionManager::PermissionHashKey* entry, void* arg) { - uint32_t* targetAppId = static_cast(arg); + uint32_t* appId = static_cast(arg); for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { - nsCOMPtr principal; - GetPrincipalFromOrigin(entry->GetKey()->mOrigin, getter_AddRefs(principal)); - - uint32_t appId; - principal->GetAppId(&appId); - - if (appId != *targetAppId) { + if (entry->GetKey()->mAppId != *appId) { continue; } @@ -1960,7 +1708,9 @@ nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator( entry->GetPermissions().RemoveElementAt(i); - gPermissionManager->NotifyObserversWithPermission(principal, + gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, gPermissionManager->mTypeArray.ElementAt(oldPermissionEntry.mType), oldPermissionEntry.mPermission, oldPermissionEntry.mExpireType, @@ -1974,7 +1724,9 @@ nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator( permEntry.mExpireType = permEntry.mNonSessionExpireType; permEntry.mExpireTime = permEntry.mNonSessionExpireTime; - gPermissionManager->NotifyObserversWithPermission(principal, + gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, gPermissionManager->mTypeArray.ElementAt(permEntry.mType), permEntry.mPermission, permEntry.mExpireType, @@ -2038,7 +1790,9 @@ nsPermissionManager::GetTypeIndex(const char *aType, // wrapper function for mangling (host,type,perm,expireType,expireTime) // set into an nsIPermission. void -nsPermissionManager::NotifyObserversWithPermission(nsIPrincipal* aPrincipal, +nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost, + uint32_t aAppId, + bool aIsInBrowserElement, const nsCString &aType, uint32_t aPermission, uint32_t aExpireType, @@ -2046,7 +1800,7 @@ nsPermissionManager::NotifyObserversWithPermission(nsIPrincipal* aPrincipal, const char16_t *aData) { nsCOMPtr permission = - new nsPermission(aPrincipal, aType, aPermission, + new nsPermission(aHost, aAppId, aIsInBrowserElement, aType, aPermission, aExpireType, aExpireTime); if (permission) NotifyObservers(permission, aData); @@ -2097,16 +1851,18 @@ nsPermissionManager::Read() nsCOMPtr stmt; rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id, origin, type, permission, expireType, expireTime, modificationTime " + "SELECT id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement " "FROM moz_hosts"), getter_AddRefs(stmt)); NS_ENSURE_SUCCESS(rv, rv); int64_t id; - nsAutoCString origin, type; + nsAutoCString host, type; uint32_t permission; uint32_t expireType; int64_t expireTime; int64_t modificationTime; + uint32_t appId; + bool isInBrowserElement; bool hasResult; bool readError = false; @@ -2117,7 +1873,7 @@ nsPermissionManager::Read() if (id > mLargestID) mLargestID = id; - rv = stmt->GetUTF8String(1, origin); + rv = stmt->GetUTF8String(1, host); if (NS_FAILED(rv)) { readError = true; continue; @@ -2136,8 +1892,16 @@ nsPermissionManager::Read() expireTime = stmt->AsInt64(5); modificationTime = stmt->AsInt64(6); + if (stmt->AsInt64(7) < 0) { + readError = true; + continue; + } + + appId = static_cast(stmt->AsInt64(7)); + isInBrowserElement = static_cast(stmt->AsInt32(8)); + nsCOMPtr principal; - nsresult rv = GetPrincipalFromOrigin(origin, getter_AddRefs(principal)); + nsresult rv = GetPrincipal(host, appId, isInBrowserElement, getter_AddRefs(principal)); if (NS_FAILED(rv)) { readError = true; continue; @@ -2160,7 +1924,6 @@ nsPermissionManager::Read() } static const char kMatchTypeHost[] = "host"; -static const char kMatchTypeOrigin[] = "origin"; // Import() will read a file from the profile directory and add them to the // database before deleting the file - ie, this is a one-shot operation that @@ -2270,48 +2033,32 @@ nsPermissionManager::_DoImport(nsIInputStream *inputStream, mozIStorageConnectio if (lineArray[0].EqualsLiteral(kMatchTypeHost) && lineArray.Length() == 4) { - nsresult error = NS_OK; + + nsresult error; uint32_t permission = lineArray[2].ToInteger(&error); if (NS_FAILED(error)) continue; - // the import file format doesn't handle modification times, so we use - // 0, which AddInternal will convert to now() - int64_t modificationTime = 0; - - UpgradeHostToOriginHostfileImport upHelper(this, operation, id); - error = UpgradeHostToOriginAndInsert(lineArray[3], lineArray[1], permission, - nsIPermissionManager::EXPIRE_NEVER, 0, - modificationTime, nsIScriptSecurityManager::NO_APP_ID, - false, &upHelper); - if (NS_FAILED(error)) { - NS_WARNING("There was a problem importing a host permission"); + // hosts might be encoded in UTF8; switch them to ACE to be consistent + if (!IsASCII(lineArray[3])) { + rv = NormalizeToACE(lineArray[3]); + if (NS_FAILED(rv)) + continue; } - } else if (lineArray[0].EqualsLiteral(kMatchTypeOrigin) && - lineArray.Length() == 4) { - nsresult error = NS_OK; - uint32_t permission = lineArray[2].ToInteger(&error); - if (NS_FAILED(error)) - continue; nsCOMPtr principal; - error = GetPrincipalFromOrigin(lineArray[3], getter_AddRefs(principal)); - if (NS_FAILED(error)) { - NS_WARNING("Couldn't import an origin permission - malformed origin"); - continue; - } + nsresult rv = GetPrincipal(lineArray[3], getter_AddRefs(principal)); + NS_ENSURE_SUCCESS(rv, rv); // the import file format doesn't handle modification times, so we use // 0, which AddInternal will convert to now() int64_t modificationTime = 0; - error = AddInternal(principal, lineArray[1], permission, id, - nsIPermissionManager::EXPIRE_NEVER, 0, - modificationTime, - eDontNotify, operation); - if (NS_FAILED(error)) { - NS_WARNING("There was a problem importing an origin permission"); - } + rv = AddInternal(principal, lineArray[1], permission, id, + nsIPermissionManager::EXPIRE_NEVER, 0, + modificationTime, + eDontNotify, operation); + NS_ENSURE_SUCCESS(rv, rv); } } while (isMore); @@ -2336,12 +2083,14 @@ void nsPermissionManager::UpdateDB(OperationType aOp, mozIStorageAsyncStatement* aStmt, int64_t aID, - const nsACString &aOrigin, + const nsACString &aHost, const nsACString &aType, uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, - int64_t aModificationTime) + int64_t aModificationTime, + uint32_t aAppId, + bool aIsInBrowserElement) { ENSURE_NOT_CHILD_PROCESS_NORET; @@ -2357,7 +2106,7 @@ nsPermissionManager::UpdateDB(OperationType aOp, rv = aStmt->BindInt64ByIndex(0, aID); if (NS_FAILED(rv)) break; - rv = aStmt->BindUTF8StringByIndex(1, aOrigin); + rv = aStmt->BindUTF8StringByIndex(1, aHost); if (NS_FAILED(rv)) break; rv = aStmt->BindUTF8StringByIndex(2, aType); @@ -2373,6 +2122,12 @@ nsPermissionManager::UpdateDB(OperationType aOp, if (NS_FAILED(rv)) break; rv = aStmt->BindInt64ByIndex(6, aModificationTime); + if (NS_FAILED(rv)) break; + + rv = aStmt->BindInt64ByIndex(7, aAppId); + if (NS_FAILED(rv)) break; + + rv = aStmt->BindInt64ByIndex(8, aIsInBrowserElement); break; } @@ -2491,12 +2246,25 @@ nsPermissionManager::UpdateExpireTime(nsIPrincipal* aPrincipal, return NS_ERROR_INVALID_ARG; } + nsAutoCString host; + nsresult rv = GetHostForPrincipal(aPrincipal, host); + NS_ENSURE_SUCCESS(rv, rv); + int32_t typeIndex = GetTypeIndex(aType, false); // If type == -1, the type isn't known, // so just return NS_OK if (typeIndex == -1) return NS_OK; - PermissionHashKey* entry = GetPermissionHashKey(aPrincipal, typeIndex, aExactHostMatch); + uint32_t appId; + rv = aPrincipal->GetAppId(&appId); + NS_ENSURE_SUCCESS(rv, rv); + + bool isInBrowserElement; + rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement); + NS_ENSURE_SUCCESS(rv, rv); + + PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement, + typeIndex, aExactHostMatch); if (!entry) { return NS_OK; } @@ -2526,7 +2294,8 @@ nsPermissionManager::FetchPermissions() { const IPC::Permission &perm = perms[i]; nsCOMPtr principal; - nsresult rv = GetPrincipalFromOrigin(perm.origin, getter_AddRefs(principal)); + nsresult rv = GetPrincipal(perm.host, perm.appId, + perm.isInBrowserElement, getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); // The child process doesn't care about modification times - it neither diff --git a/extensions/cookie/nsPermissionManager.h b/extensions/cookie/nsPermissionManager.h index 7bf2c3d146fc..55327005fea3 100644 --- a/extensions/cookie/nsPermissionManager.h +++ b/extensions/cookie/nsPermissionManager.h @@ -71,22 +71,35 @@ public: { public: explicit PermissionKey(nsIPrincipal* aPrincipal); - explicit PermissionKey(const nsACString& aOrigin) - : mOrigin(aOrigin) + PermissionKey(const nsACString& aHost, + uint32_t aAppId, + bool aIsInBrowserElement) + : mHost(aHost) + , mAppId(aAppId) + , mIsInBrowserElement(aIsInBrowserElement) { } bool operator==(const PermissionKey& aKey) const { - return mOrigin.Equals(aKey.mOrigin); + return mHost.Equals(aKey.mHost) && + mAppId == aKey.mAppId && + mIsInBrowserElement == aKey.mIsInBrowserElement; } PLDHashNumber GetHashCode() const { - return mozilla::HashString(mOrigin); + nsAutoCString str; + str.Assign(mHost); + str.AppendInt(mAppId); + str.AppendInt(static_cast(mIsInBrowserElement)); + + return mozilla::HashString(str); } NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PermissionKey) - nsCString mOrigin; + nsCString mHost; + uint32_t mAppId; + bool mIsInBrowserElement; private: // Default ctor shouldn't be used. @@ -209,9 +222,11 @@ private: int32_t GetTypeIndex(const char *aTypeString, bool aAdd); - PermissionHashKey* GetPermissionHashKey(nsIPrincipal* aPrincipal, - uint32_t aType, - bool aExactHostMatch); + PermissionHashKey* GetPermissionHashKey(const nsACString& aHost, + uint32_t aAppId, + bool aIsInBrowserElement, + uint32_t aType, + bool aExactHostMatch); nsresult CommonTestPermission(nsIPrincipal* aPrincipal, const char *aType, @@ -226,7 +241,9 @@ private: nsresult ImportDefaults(); nsresult _DoImport(nsIInputStream *inputStream, mozIStorageConnection *aConn); nsresult Read(); - void NotifyObserversWithPermission(nsIPrincipal* aPrincipal, + void NotifyObserversWithPermission(const nsACString &aHost, + uint32_t aAppId, + bool aIsInBrowserElement, const nsCString &aType, uint32_t aPermission, uint32_t aExpireType, @@ -244,12 +261,14 @@ private: static void UpdateDB(OperationType aOp, mozIStorageAsyncStatement* aStmt, int64_t aID, - const nsACString& aOrigin, + const nsACString& aHost, const nsACString& aType, uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, - int64_t aModificationTime); + int64_t aModificationTime, + uint32_t aAppId, + bool aIsInBrowserElement); nsresult RemoveExpiredPermissionsForApp(uint32_t aAppId); diff --git a/extensions/cookie/test/test_app_uninstall_permissions.html b/extensions/cookie/test/test_app_uninstall_permissions.html index 13cf9f7380ba..afb1f1d46c49 100644 --- a/extensions/cookie/test/test_app_uninstall_permissions.html +++ b/extensions/cookie/test/test_app_uninstall_permissions.html @@ -42,7 +42,7 @@ function getPermissionCountForApp(aAppId) { while (enumerator.hasMoreElements()) { var permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); - if (permission.principal.appId == aAppId || aAppId == -1) { + if (permission.appId == aAppId || aAppId == -1) { nbPermissions++; } } diff --git a/extensions/cookie/test/unit/test_permmanager_defaults.js b/extensions/cookie/test/unit/test_permmanager_defaults.js index 65ea18f58b24..7798542edec7 100644 --- a/extensions/cookie/test/unit/test_permmanager_defaults.js +++ b/extensions/cookie/test/unit/test_permmanager_defaults.js @@ -2,10 +2,8 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ // The origin we use in most of the tests. -const TEST_ORIGIN = NetUtil.newURI("http://example.org"); -const TEST_ORIGIN_HTTPS = NetUtil.newURI("https://example.org"); -const TEST_ORIGIN_2 = NetUtil.newURI("http://example.com"); -const TEST_ORIGIN_3 = NetUtil.newURI("https://example2.com:8080"); +const TEST_ORIGIN = "example.org"; +const TEST_ORIGIN_2 = "example.com"; const TEST_PERMISSION = "test-permission"; Components.utils.import("resource://gre/modules/Promise.jsm"); @@ -37,10 +35,8 @@ add_task(function* do_test() { conv.writeString("# this is a comment\n"); conv.writeString("\n"); // a blank line! - conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.host + "\n"); - conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2.host + "\n"); - conv.writeString("origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_3.spec + "\n"); - conv.writeString("origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.spec + "^appId=1000&inBrowser=1\n"); + conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN + "\n"); + conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2 + "\n"); ostream.close(); // Set the preference used by the permission manager so the file is read. @@ -51,30 +47,14 @@ add_task(function* do_test() { getService(Ci.nsIPermissionManager); // test the default permission was applied. - let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_ORIGIN); - let principalHttps = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_ORIGIN_HTTPS); - let principal2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_ORIGIN_2); - let principal3 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(TEST_ORIGIN_3); - let principal4 = Services.scriptSecurityManager.getAppCodebasePrincipal(TEST_ORIGIN, 1000, true); - let principal5 = Services.scriptSecurityManager.getAppCodebasePrincipal(TEST_ORIGIN_3, 1000, true); + let permURI = NetUtil.newURI("http://" + TEST_ORIGIN); + let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI); do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)); - do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, - pm.testPermissionFromPrincipal(principalHttps, TEST_PERMISSION)); - do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, - pm.testPermissionFromPrincipal(principal3, TEST_PERMISSION)); - do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, - pm.testPermissionFromPrincipal(principal4, TEST_PERMISSION)); - - // Didn't add - do_check_eq(Ci.nsIPermissionManager.UNKNOWN_ACTION, - pm.testPermissionFromPrincipal(principal5, TEST_PERMISSION)); // the permission should exist in the enumerator. - do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum(TEST_ORIGIN)); - do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum(TEST_ORIGIN_3)); - + do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum()); // but should not have been written to the DB yield checkCapabilityViaDB(null); @@ -83,10 +63,6 @@ add_task(function* do_test() { do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)); - do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, - pm.testPermissionFromPrincipal(principal3, TEST_PERMISSION)); - do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, - pm.testPermissionFromPrincipal(principal4, TEST_PERMISSION)); // Asking for this permission to be removed should result in that permission // having UNKNOWN_ACTION @@ -129,6 +105,9 @@ add_task(function* do_test() { // check default permissions and removeAllSince work as expected. pm.removeAll(); // ensure only defaults are there. + let permURI2 = NetUtil.newURI("http://" + TEST_ORIGIN_2); + let principal2 = Services.scriptSecurityManager.getNoAppCodebasePrincipal(permURI2); + // default for both principals is allow. do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, pm.testPermissionFromPrincipal(principal, TEST_PERMISSION)); @@ -170,12 +149,12 @@ add_task(function* do_test() { // use an enumerator to find the requested permission. Returns the permission // value (ie, the "capability" in nsIPermission parlance) or null if it can't // be found. -function findCapabilityViaEnum(origin = TEST_ORIGIN, type = TEST_PERMISSION) { +function findCapabilityViaEnum(host = TEST_ORIGIN, type = TEST_PERMISSION) { let result = undefined; let e = Services.perms.enumerator; while (e.hasMoreElements()) { let perm = e.getNext().QueryInterface(Ci.nsIPermission); - if (perm.matchesURI(origin, true) && + if (perm.host == host && perm.type == type) { if (result !== undefined) { // we've already found one previously - that's bad! @@ -192,12 +171,12 @@ function findCapabilityViaEnum(origin = TEST_ORIGIN, type = TEST_PERMISSION) { // distinct possibility exists that our checking of the DB will happen before // the permission manager update has completed - so we just retry a few times. // Returns a promise. -function checkCapabilityViaDB(expected, origin = TEST_ORIGIN, type = TEST_PERMISSION) { +function checkCapabilityViaDB(expected, host = TEST_ORIGIN, type = TEST_PERMISSION) { let deferred = Promise.defer(); let count = 0; let max = 20; let do_check = () => { - let got = findCapabilityViaDB(origin, type); + let got = findCapabilityViaDB(host, type); if (got == expected) { // the do_check_eq() below will succeed - which is what we want. do_check_eq(got, expected, "The database has the expected value"); @@ -221,10 +200,7 @@ function checkCapabilityViaDB(expected, origin = TEST_ORIGIN, type = TEST_PERMIS // use the DB to find the requested permission. Returns the permission // value (ie, the "capability" in nsIPermission parlance) or null if it can't // be found. -function findCapabilityViaDB(origin = TEST_ORIGIN, type = TEST_PERMISSION) { - let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(origin); - let originStr = principal.origin; - +function findCapabilityViaDB(host = TEST_ORIGIN, type = TEST_PERMISSION) { let file = Services.dirsvc.get("ProfD", Ci.nsIFile); file.append("permissions.sqlite"); @@ -234,8 +210,8 @@ function findCapabilityViaDB(origin = TEST_ORIGIN, type = TEST_PERMISSION) { let connection = storage.openDatabase(file); let query = connection.createStatement( - "SELECT permission FROM moz_hosts WHERE origin = :origin AND type = :type"); - query.bindByName("origin", originStr); + "SELECT permission FROM moz_hosts WHERE host = :host AND type = :type"); + query.bindByName("host", host); query.bindByName("type", type); if (!query.executeStep()) { diff --git a/extensions/cookie/test/unit/test_permmanager_getPermissionObject.js b/extensions/cookie/test/unit/test_permmanager_getPermissionObject.js index f2f13f38e3a0..9ab4ff21f0f2 100644 --- a/extensions/cookie/test/unit/test_permmanager_getPermissionObject.js +++ b/extensions/cookie/test/unit/test_permmanager_getPermissionObject.js @@ -29,20 +29,20 @@ function run_test() { pm.addFromPrincipal(principal, "test/pobject", pm.ALLOW_ACTION); var rootPerm = pm.getPermissionObject(principal, "test/pobject", false); do_check_true(rootPerm != null); - do_check_eq(rootPerm.principal.origin, "http://example.com"); + do_check_eq(rootPerm.host, "example.com"); do_check_eq(rootPerm.type, "test/pobject"); do_check_eq(rootPerm.capability, pm.ALLOW_ACTION); do_check_eq(rootPerm.expireType, pm.EXPIRE_NEVER); var rootPerm2 = pm.getPermissionObject(principal, "test/pobject", true); do_check_true(rootPerm != null); - do_check_eq(rootPerm.principal.origin, "http://example.com"); + do_check_eq(rootPerm.host, "example.com"); var subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", true); do_check_null(subPerm); subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false); do_check_true(subPerm != null); - do_check_eq(subPerm.principal.origin, "http://example.com"); + do_check_eq(subPerm.host, "example.com"); do_check_eq(subPerm.type, "test/pobject"); do_check_eq(subPerm.capability, pm.ALLOW_ACTION); @@ -50,7 +50,7 @@ function run_test() { do_check_null(subPerm); subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", false); do_check_true(subPerm != null); - do_check_eq(subPerm.principal.origin, "http://example.com"); + do_check_eq(subPerm.host, "example.com"); pm.addFromPrincipal(principal, "test/pobject", pm.DENY_ACTION, pm.EXPIRE_SESSION); @@ -63,28 +63,28 @@ function run_test() { do_check_eq(rootPerm.expireType, pm.EXPIRE_SESSION); subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false); - do_check_eq(subPerm.principal.origin, "http://example.com"); + do_check_eq(subPerm.host, "example.com"); do_check_eq(subPerm.capability, pm.DENY_ACTION); do_check_eq(subPerm.expireType, pm.EXPIRE_SESSION); pm.addFromPrincipal(subPrincipal, "test/pobject", pm.PROMPT_ACTION); rootPerm = pm.getPermissionObject(principal, "test/pobject", true); - do_check_eq(rootPerm.principal.origin, "http://example.com"); + do_check_eq(rootPerm.host, "example.com"); do_check_eq(rootPerm.capability, pm.DENY_ACTION); subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", true); - do_check_eq(subPerm.principal.origin, "http://sub.example.com"); + do_check_eq(subPerm.host, "sub.example.com"); do_check_eq(subPerm.capability, pm.PROMPT_ACTION); subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false); - do_check_eq(subPerm.principal.origin, "http://sub.example.com"); + do_check_eq(subPerm.host, "sub.example.com"); do_check_eq(subPerm.capability, pm.PROMPT_ACTION); subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", true); do_check_null(subPerm); subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", false); - do_check_eq(subPerm.principal.origin, "http://sub.example.com"); + do_check_eq(subPerm.host, "sub.example.com"); do_check_eq(subPerm.capability, pm.PROMPT_ACTION); pm.removeFromPrincipal(principal, "test/pobject"); diff --git a/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js b/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js index 4cb1f9604439..b96813b98aa7 100644 --- a/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js +++ b/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js @@ -121,7 +121,7 @@ function run_test() { // The schema should be upgraded to 4, and a 'modificationTime' column should // exist with all records having a value of 0. - do_check_eq(connection.schemaVersion, 5); + do_check_eq(connection.schemaVersion, 4); let select = connection.createStatement("SELECT modificationTime FROM moz_hosts") let numMigrated = 0; diff --git a/extensions/cookie/test/unit/test_permmanager_local_files.js b/extensions/cookie/test/unit/test_permmanager_local_files.js index 67884cb642cd..6de1ce7b063d 100644 --- a/extensions/cookie/test/unit/test_permmanager_local_files.js +++ b/extensions/cookie/test/unit/test_permmanager_local_files.js @@ -39,4 +39,10 @@ function run_test() { do_check_eq(pm.testPermissionFromPrincipal(principal, "test/local-files"), pm.UNKNOWN_ACTION); do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.UNKNOWN_ACTION); do_check_eq(pm.testPermissionFromPrincipal(fileInDirPrincipal, "test/local-files"), pm.UNKNOWN_ACTION); -} + + // Add the magic "" permission and make sure all "file://" now have the permission. + pm.addFromPrincipal(getPrincipalFromURIString("http://"), "test/local-files", pm.ALLOW_ACTION, 0, 0); + do_check_eq(pm.testPermissionFromPrincipal(principal, "test/local-files"), pm.ALLOW_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.ALLOW_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(fileInDirPrincipal, "test/local-files"), pm.ALLOW_ACTION); +} \ No newline at end of file diff --git a/extensions/cookie/test/unit/test_permmanager_mailto.js b/extensions/cookie/test/unit/test_permmanager_mailto.js new file mode 100644 index 000000000000..21b1f6c60a28 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_mailto.js @@ -0,0 +1,41 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + // initialize the permission manager service + const kTestAddr = "test@example.org"; + const kType = "test-mailto"; + const kCapability = 1; + + // make a mailto: URI with parameters + let uri = Services.io.newURI("mailto:" + kTestAddr + "?subject=test", null, + null); + + // add a permission entry for that URI + Services.perms.add(uri, kType, kCapability); + do_check_true(permission_exists(kTestAddr, kType, kCapability)); + + // remove the permission, and make sure it was removed + Services.perms.remove(uri, kType); + do_check_false(permission_exists(kTestAddr, kType, kCapability)); + + uri = Services.io.newURI("mailto:" + kTestAddr, null, null); + Services.perms.add(uri, kType, kCapability); + do_check_true(permission_exists(kTestAddr, kType, kCapability)); + + Services.perms.remove(uri, kType); + do_check_false(permission_exists(kTestAddr, kType, kCapability)); +} + +function permission_exists(aHost, aType, aCapability) { + let e = Services.perms.enumerator; + while (e.hasMoreElements()) { + let perm = e.getNext().QueryInterface(Ci.nsIPermission); + if (perm.host == aHost && + perm.type == aType && + perm.capability == aCapability) { + return true; + } + } + return false; +} diff --git a/extensions/cookie/test/unit/test_permmanager_matches.js b/extensions/cookie/test/unit/test_permmanager_matches.js deleted file mode 100644 index 475581aeabd7..000000000000 --- a/extensions/cookie/test/unit/test_permmanager_matches.js +++ /dev/null @@ -1,129 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -function matches_always(perm, principals) { - principals.forEach((principal) => { - do_check_true(perm.matches(principal, true), "perm: " + perm.principal.origin + ", princ: " + principal.origin); - do_check_true(perm.matches(principal, false), "perm: " + perm.principal.origin + ", princ: " + principal.origin); - }); -} - -function matches_weak(perm, principals) { - principals.forEach((principal) => { - do_check_false(perm.matches(principal, true), "perm: " + perm.principal.origin + ", princ: " + principal.origin); - do_check_true(perm.matches(principal, false), "perm: " + perm.principal.origin + ", princ: " + principal.origin); - }); -} - -function matches_never(perm, principals) { - principals.forEach((principal) => { - do_check_false(perm.matches(principal, true), "perm: " + perm.principal.origin + ", princ: " + principal.origin); - do_check_false(perm.matches(principal, false), "perm: " + perm.principal.origin + ", princ: " + principal.origin); - }); -} - -function run_test() { - // initialize the permission manager service - let pm = Cc["@mozilla.org/permissionmanager;1"]. - getService(Ci.nsIPermissionManager); - - let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"] - .getService(Ci.nsIScriptSecurityManager); - - // Add some permissions - let uri0 = NetUtil.newURI("http://google.com/search?q=foo#hashtag", null, null); - let uri1 = NetUtil.newURI("http://hangouts.google.com/subdir", null, null); - let uri2 = NetUtil.newURI("http://google.org/", null, null); - let uri3 = NetUtil.newURI("https://google.com/some/random/subdirectory", null, null); - let uri4 = NetUtil.newURI("https://hangouts.google.com/#!/hangout", null, null); - let uri5 = NetUtil.newURI("http://google.com:8096/", null, null); - - let uri0_n_n = secMan.getNoAppCodebasePrincipal(uri0); - let uri1_n_n = secMan.getNoAppCodebasePrincipal(uri1); - let uri2_n_n = secMan.getNoAppCodebasePrincipal(uri2); - let uri3_n_n = secMan.getNoAppCodebasePrincipal(uri3); - let uri4_n_n = secMan.getNoAppCodebasePrincipal(uri4); - let uri5_n_n = secMan.getNoAppCodebasePrincipal(uri5); - - let uri0_1000_n = secMan.getAppCodebasePrincipal(uri0, 1000, false); - let uri1_1000_n = secMan.getAppCodebasePrincipal(uri1, 1000, false); - let uri2_1000_n = secMan.getAppCodebasePrincipal(uri2, 1000, false); - let uri3_1000_n = secMan.getAppCodebasePrincipal(uri3, 1000, false); - let uri4_1000_n = secMan.getAppCodebasePrincipal(uri4, 1000, false); - let uri5_1000_n = secMan.getAppCodebasePrincipal(uri5, 1000, false); - - let uri0_1000_y = secMan.getAppCodebasePrincipal(uri0, 1000, true); - let uri1_1000_y = secMan.getAppCodebasePrincipal(uri1, 1000, true); - let uri2_1000_y = secMan.getAppCodebasePrincipal(uri2, 1000, true); - let uri3_1000_y = secMan.getAppCodebasePrincipal(uri3, 1000, true); - let uri4_1000_y = secMan.getAppCodebasePrincipal(uri4, 1000, true); - let uri5_1000_y = secMan.getAppCodebasePrincipal(uri5, 1000, true); - - let uri0_2000_n = secMan.getAppCodebasePrincipal(uri0, 2000, false); - let uri1_2000_n = secMan.getAppCodebasePrincipal(uri1, 2000, false); - let uri2_2000_n = secMan.getAppCodebasePrincipal(uri2, 2000, false); - let uri3_2000_n = secMan.getAppCodebasePrincipal(uri3, 2000, false); - let uri4_2000_n = secMan.getAppCodebasePrincipal(uri4, 2000, false); - let uri5_2000_n = secMan.getAppCodebasePrincipal(uri5, 2000, false); - - let uri0_2000_y = secMan.getAppCodebasePrincipal(uri0, 2000, true); - let uri1_2000_y = secMan.getAppCodebasePrincipal(uri1, 2000, true); - let uri2_2000_y = secMan.getAppCodebasePrincipal(uri2, 2000, true); - let uri3_2000_y = secMan.getAppCodebasePrincipal(uri3, 2000, true); - let uri4_2000_y = secMan.getAppCodebasePrincipal(uri4, 2000, true); - let uri5_2000_y = secMan.getAppCodebasePrincipal(uri5, 2000, true); - - pm.addFromPrincipal(uri0_n_n, "test/matches", pm.ALLOW_ACTION); - let perm_n_n = pm.getPermissionObject(uri0_n_n, "test/matches", true); - pm.addFromPrincipal(uri0_1000_n, "test/matches", pm.ALLOW_ACTION); - let perm_1000_n = pm.getPermissionObject(uri0_1000_n, "test/matches", true); - pm.addFromPrincipal(uri0_1000_y, "test/matches", pm.ALLOW_ACTION); - let perm_1000_y = pm.getPermissionObject(uri0_1000_y, "test/matches", true); - pm.addFromPrincipal(uri0_2000_n, "test/matches", pm.ALLOW_ACTION); - let perm_2000_n = pm.getPermissionObject(uri0_2000_n, "test/matches", true); - pm.addFromPrincipal(uri0_2000_y, "test/matches", pm.ALLOW_ACTION); - let perm_2000_y = pm.getPermissionObject(uri0_2000_y, "test/matches", true); - - matches_always(perm_n_n, [uri0_n_n]); - matches_weak(perm_n_n, [uri1_n_n]); - matches_never(perm_n_n, [uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n, - uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n, - uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y, - uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n, - uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y]); - - matches_always(perm_1000_n, [uri0_1000_n]); - matches_weak(perm_1000_n, [uri1_1000_n]); - matches_never(perm_1000_n, [uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n, - uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n, - uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y, - uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n, - uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y]); - - matches_always(perm_1000_y, [uri0_1000_y]); - matches_weak(perm_1000_y, [uri1_1000_y]); - matches_never(perm_1000_y, [uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y, - uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n, - uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n, - uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n, - uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y]); - - matches_always(perm_2000_n, [uri0_2000_n]); - matches_weak(perm_2000_n, [uri1_2000_n]); - matches_never(perm_2000_n, [uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n, - uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n, - uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y, - uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n, - uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y]); - - matches_always(perm_2000_y, [uri0_2000_y]); - matches_weak(perm_2000_y, [uri1_2000_y]); - matches_never(perm_2000_y, [uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y, - uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n, - uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n, - uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n, - uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y]); - - // Clean up! - pm.removeAll(); -} diff --git a/extensions/cookie/test/unit/test_permmanager_matchesuri.js b/extensions/cookie/test/unit/test_permmanager_matchesuri.js deleted file mode 100644 index 7317cb35d3ad..000000000000 --- a/extensions/cookie/test/unit/test_permmanager_matchesuri.js +++ /dev/null @@ -1,150 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -function matches_always(perm, uris) { - uris.forEach((uri) => { - do_check_true(perm.matchesURI(uri, true), "perm: " + perm.principal.origin + ", URI: " + uri.spec); - do_check_true(perm.matchesURI(uri, false), "perm: " + perm.principal.origin + ", URI: " + uri.spec); - }); -} - -function matches_weak(perm, uris) { - uris.forEach((uri) => { - do_check_false(perm.matchesURI(uri, true), "perm: " + perm.principal.origin + ", URI: " + uri.spec); - do_check_true(perm.matchesURI(uri, false), "perm: " + perm.principal.origin + ", URI: " + uri.spec); - }); -} - -function matches_never(perm, uris) { - uris.forEach((uri) => { - do_check_false(perm.matchesURI(uri, true), "perm: " + perm.principal.origin + ", URI: " + uri.spec); - do_check_false(perm.matchesURI(uri, false), "perm: " + perm.principal.origin + ", URI: " + uri.spec); - }); -} - -function mk_permission(uri, isAppPermission = false) { - let pm = Cc["@mozilla.org/permissionmanager;1"]. - getService(Ci.nsIPermissionManager); - - let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"] - .getService(Ci.nsIScriptSecurityManager); - - // Get the permission from the principal! - let principal = isAppPermission ? - secMan.getAppCodebasePrincipal(uri, 1000, false) : - secMan.getNoAppCodebasePrincipal(uri); - - pm.addFromPrincipal(principal, "test/matchesuri", pm.ALLOW_ACTION); - let permission = pm.getPermissionObject(principal, "test/matchesuri", true); - - return permission; -} - -function run_test() { - // initialize the permission manager service - let pm = Cc["@mozilla.org/permissionmanager;1"]. - getService(Ci.nsIPermissionManager); - - let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"] - .getService(Ci.nsIScriptSecurityManager); - - let fileprefix = "file:///"; - if (Services.appinfo.OS == "WINNT") { - // Windows rejects files if they don't have a drive. See Bug 1180870 - fileprefix += "c:/"; - } - - // Add some permissions - let uri0 = NetUtil.newURI("http://google.com:9091/just/a/path", null, null); - let uri1 = NetUtil.newURI("http://hangouts.google.com:9091/some/path", null, null); - let uri2 = NetUtil.newURI("http://google.com:9091/", null, null); - let uri3 = NetUtil.newURI("http://google.org:9091/", null, null); - let uri4 = NetUtil.newURI("http://deeper.hangouts.google.com:9091/", null, null); - let uri5 = NetUtil.newURI("https://google.com/just/a/path", null, null); - let uri6 = NetUtil.newURI("https://hangouts.google.com", null, null); - let uri7 = NetUtil.newURI("https://google.com/", null, null); - - let fileuri1 = NetUtil.newURI(fileprefix + "a/file/path", null, null); - let fileuri2 = NetUtil.newURI(fileprefix + "a/file/path/deeper", null, null); - let fileuri3 = NetUtil.newURI(fileprefix + "a/file/otherpath", null, null); - - { - let perm = mk_permission(uri0); - matches_always(perm, [uri0, uri2]); - matches_weak(perm, [uri1, uri4]); - matches_never(perm, [uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]); - } - - { - let perm = mk_permission(uri1); - matches_always(perm, [uri1]); - matches_weak(perm, [uri4]); - matches_never(perm, [uri0, uri2, uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]); - } - - { - let perm = mk_permission(uri2); - matches_always(perm, [uri0, uri2]); - matches_weak(perm, [uri1, uri4]); - matches_never(perm, [uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]); - } - - { - let perm = mk_permission(uri3); - matches_always(perm, [uri3]); - matches_weak(perm, []); - matches_never(perm, [uri1, uri2, uri4, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]); - } - - { - let perm = mk_permission(uri4); - matches_always(perm, [uri4]); - matches_weak(perm, []); - matches_never(perm, [uri1, uri2, uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]); - } - - { - let perm = mk_permission(uri5); - matches_always(perm, [uri5, uri7]); - matches_weak(perm, [uri6]); - matches_never(perm, [uri0, uri1, uri2, uri3, uri4, fileuri1, fileuri2, fileuri3]); - } - - { - let perm = mk_permission(uri6); - matches_always(perm, [uri6]); - matches_weak(perm, []); - matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri7, fileuri1, fileuri2, fileuri3]); - } - - { - let perm = mk_permission(uri7); - matches_always(perm, [uri5, uri7]); - matches_weak(perm, [uri6]); - matches_never(perm, [uri0, uri1, uri2, uri3, uri4, fileuri1, fileuri2, fileuri3]); - } - - { - let perm = mk_permission(fileuri1); - matches_always(perm, [fileuri1]); - matches_weak(perm, []); - matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri6, uri7, fileuri2, fileuri3]); - } - - { - let perm = mk_permission(fileuri2); - matches_always(perm, [fileuri2]); - matches_weak(perm, []); - matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri6, uri7, fileuri1, fileuri3]); - } - - { - let perm = mk_permission(fileuri3); - matches_always(perm, [fileuri3]); - matches_weak(perm, []); - matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri6, uri7, fileuri1, fileuri2]); - } - - // Clean up! - pm.removeAll(); -} diff --git a/extensions/cookie/test/unit/test_permmanager_migrate_4-5.js b/extensions/cookie/test/unit/test_permmanager_migrate_4-5.js deleted file mode 100644 index 253db93dbacc..000000000000 --- a/extensions/cookie/test/unit/test_permmanager_migrate_4-5.js +++ /dev/null @@ -1,162 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", - "resource://testing-common/PlacesTestUtils.jsm"); - -let PERMISSIONS_FILE_NAME = "permissions.sqlite"; - -function GetPermissionsFile(profile) -{ - let file = profile.clone(); - file.append(PERMISSIONS_FILE_NAME); - return file; -} - -function run_test() { - run_next_test(); -} - -add_task(function test() { - /* Create and set up the permissions database */ - let profile = do_get_profile(); - - let db = Services.storage.openDatabase(GetPermissionsFile(profile)); - db.schemaVersion = 4; - - db.executeSimpleSQL( - "CREATE TABLE moz_hosts (" + - " id INTEGER PRIMARY KEY" + - ",host TEXT" + - ",type TEXT" + - ",permission INTEGER" + - ",expireType INTEGER" + - ",expireTime INTEGER" + - ",modificationTime INTEGER" + - ",appId INTEGER" + - ",isInBrowserElement INTEGER" + - ")"); - - let stmtInsert = db.createStatement( - "INSERT INTO moz_hosts (" + - "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" + - ") VALUES (" + - ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" + - ")"); - - let id = 0; - - function insertHost(host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) { - stmtInsert.bindByName("id", id++); - stmtInsert.bindByName("host", host); - stmtInsert.bindByName("type", type); - stmtInsert.bindByName("permission", permission); - stmtInsert.bindByName("expireType", expireType); - stmtInsert.bindByName("expireTime", expireTime); - stmtInsert.bindByName("modificationTime", modificationTime); - stmtInsert.bindByName("appId", appId); - stmtInsert.bindByName("isInBrowserElement", isInBrowserElement); - - try { - stmtInsert.executeStep(); - stmtInsert.reset(); - } catch (e) { - stmtInsert.reset(); - throw e; - } - } - - // Add some rows to the database - insertHost("foo.com", "A", 1, 0, 0, 0, 0, false); - insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false); - insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true); - insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false); - insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false); - insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false); - insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false); - insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true); - insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false); - insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false); - insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false); - insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false); - insertHost("", "A", 1, 0, 0, 0, 0, false); - insertHost("", "B", 1, 0, 0, 0, 0, false); - - // CLose the db connection - stmtInsert.finalize(); - db.close(); - stmtInsert = null; - db = null; - - let expected = [ - // The http:// entries under foo.com won't be inserted, as there are history entries for foo.com, - // and http://foo.com or a subdomain are never visited. - // However, permissions for subdomains of foo.com will be present for both http:// and https://, - // as they do not apply to any entry in the history - // ["http://foo.com", "A", 1, 0, 0], - // ["http://foo.com^appId=1000", "A", 1, 0, 0], - // ["http://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0], - - ["http://sub.foo.com", "B", 1, 0, 0], - ["http://subber.sub.foo.com", "B", 1, 0, 0], - - ["https://foo.com", "A", 1, 0, 0], - ["https://foo.com^appId=1000", "A", 1, 0, 0], - ["https://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0], - ["https://sub.foo.com", "B", 1, 0, 0], - ["https://subber.sub.foo.com", "B", 1, 0, 0], - - // bar.ca will have both http:// and https:// for all entries, because the foo did the bar a favour - ["http://bar.ca", "B", 1, 0, 0], - ["https://bar.ca", "B", 1, 0, 0], - ["http://bar.ca^appId=1000", "B", 1, 0, 0], - ["https://bar.ca^appId=1000", "B", 1, 0, 0], - ["http://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0], - ["https://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0], - ["file:///some/path/to/file.html", "A", 1, 0, 0], - ["file:///another/file.html", "A", 1, 0, 0], - - // Because we put ftp://some.subdomain.of.foo.com:8000/some/subdirectory in the history, we should - // also have these entries - ["ftp://foo.com:8000", "A", 1, 0, 0], - ["ftp://foo.com:8000^appId=1000", "A", 1, 0, 0], - ["ftp://foo.com:8000^appId=2000&inBrowser=1", "A", 1, 0, 0], - ]; - - let found = expected.map((it) => 0); - - // Add some places to the places database - yield PlacesTestUtils.addVisits(Services.io.newURI("https://foo.com/some/other/subdirectory", null, null)); - yield PlacesTestUtils.addVisits(Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory", null, null)); - - // Force initialization of the nsPermissionManager - let enumerator = Services.perms.enumerator; - while (enumerator.hasMoreElements()) { - let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); - let isExpected = false; - - expected.forEach((it, i) => { - if (permission.principal.origin == it[0] && - permission.type == it[1] && - permission.capability == it[2] && - permission.expireType == it[3] && - permission.expireTime == it[4]) { - isExpected = true; - found[i]++; - } - }); - - do_check_true(isExpected, - "Permission " + (isExpected ? "should" : "shouldn't") + - " be in permission database: " + - permission.principal.origin + ", " + - permission.type + ", " + - permission.capability + ", " + - permission.expireType + ", " + - permission.expireTime); - } - - found.forEach((count, i) => { - do_check_true(count == 1, "Expected count = 1, got count = " + count + " for permission " + expected[i]); - }); -}); diff --git a/extensions/cookie/test/unit/xpcshell.ini b/extensions/cookie/test/unit/xpcshell.ini index 38ad29f50e12..902d90a992a9 100644 --- a/extensions/cookie/test/unit/xpcshell.ini +++ b/extensions/cookie/test/unit/xpcshell.ini @@ -30,10 +30,8 @@ skip-if = debug == true [test_permmanager_idn.js] [test_permmanager_subdomains.js] [test_permmanager_local_files.js] +[test_permmanager_mailto.js] [test_permmanager_cleardata.js] [test_schema_2_migration.js] [test_schema_3_migration.js] [test_permmanager_removepermission.js] -[test_permmanager_matchesuri.js] -[test_permmanager_matches.js] -[test_permmanager_migrate_4-5.js] diff --git a/layout/tools/reftest/reftest-preferences.js b/layout/tools/reftest/reftest-preferences.js index 8709d6d37ab3..c1a429e629b2 100644 --- a/layout/tools/reftest/reftest-preferences.js +++ b/layout/tools/reftest/reftest-preferences.js @@ -65,6 +65,3 @@ // Disable periodic updates of service workers. branch.setBoolPref("dom.serviceWorkers.periodic-updates.enabled", false); - - // Allow XUL and XBL files to be opened from file:// URIs - branch.setBoolPref("dom.allow_XUL_XBL_for_file", true); diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js index 27daf26b8db9..edd61b9cbf23 100644 --- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -1177,9 +1177,6 @@ function ServeFiles(manifestPrincipal, depth, aURL, files) var testbase = gIOService.newURI("http://localhost:" + gHttpServerPort + path + dirPath, null, null); - // Give the testbase URI access to XUL and XBL - Services.perms.add(testbase, "allowXULXBL", Services.perms.ALLOW_ACTION); - function FileToURI(file) { // Only serve relative URIs via the HTTP server, not absolute diff --git a/layout/tools/reftest/remotereftest.py b/layout/tools/reftest/remotereftest.py index e7430bade751..c0f5dd1092d1 100644 --- a/layout/tools/reftest/remotereftest.py +++ b/layout/tools/reftest/remotereftest.py @@ -342,7 +342,7 @@ class RemoteReftest(RefTest): self.server.stop() def createReftestProfile(self, options, reftestlist): - profile = RefTest.createReftestProfile(self, options, reftestlist, server=options.remoteWebServer, port=options.httpPort) + profile = RefTest.createReftestProfile(self, options, reftestlist, server=options.remoteWebServer) profileDir = profile.profile prefs = {} diff --git a/layout/tools/reftest/runreftest.py b/layout/tools/reftest/runreftest.py index 3d7dd67ea01c..09f7c4ad1f6c 100644 --- a/layout/tools/reftest/runreftest.py +++ b/layout/tools/reftest/runreftest.py @@ -187,8 +187,8 @@ class RefTest(object): """ locations = mozprofile.permissions.ServerLocations() - locations.add_host(server, scheme='http', port=0) - locations.add_host(server, scheme='https', port=0) + locations.add_host(server, port=0) + locations.add_host('', port=0) # Set preferences for communication between our command line arguments # and the reftest harness. Preferences that are required for reftest diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index 9e9fabb2b44c..8f0e96709e94 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -177,8 +177,8 @@ pref("dom.forms.number", true); /* extension manager and xpinstall */ pref("xpinstall.whitelist.directRequest", false); pref("xpinstall.whitelist.fileRequest", false); -pref("xpinstall.whitelist.add", "https://addons.mozilla.org"); -pref("xpinstall.whitelist.add.180", "https://marketplace.firefox.com"); +pref("xpinstall.whitelist.add", "addons.mozilla.org"); +pref("xpinstall.whitelist.add.180", "marketplace.firefox.com"); pref("xpinstall.signatures.required", false); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 5e3537f39f55..6df73544b828 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -1751,11 +1751,11 @@ var BrowserApp = { docShell.mixedContentChannel = null; } } else if (data.contentType === "tracking") { - // Convert document URI into the format used by - // nsChannelClassifier::ShouldEnableTrackingProtection - // (any scheme turned into https is correct) - let normalizedUrl = Services.io.newURI("https://" + browser.currentURI.hostPort, null, null); if (data.allowContent) { + // Convert document URI into the format used by + // nsChannelClassifier::ShouldEnableTrackingProtection + // (any scheme turned into https is correct) + let normalizedUrl = Services.io.newURI("https://" + browser.currentURI.hostPort, null, null); // Add the current host in the 'trackingprotection' consumer of // the permission manager using a normalized URI. This effectively // places this host on the tracking protection white list. @@ -1765,7 +1765,7 @@ var BrowserApp = { // Remove the current host from the 'trackingprotection' consumer // of the permission manager. This effectively removes this host // from the tracking protection white list (any list actually). - Services.perms.remove(normalizedUrl, "trackingprotection"); + Services.perms.remove(browser.currentURI, "trackingprotection"); Telemetry.addData("TRACKING_PROTECTION_EVENTS", 2); } } diff --git a/netwerk/base/nsIPermission.idl b/netwerk/base/nsIPermission.idl index 1e8a41b76c10..4500679905ac 100644 --- a/netwerk/base/nsIPermission.idl +++ b/netwerk/base/nsIPermission.idl @@ -6,10 +6,7 @@ #include "nsISupports.idl" -interface nsIPrincipal; -interface nsIURI; - -[scriptable, uuid(bb409a51-2371-4fea-9dc9-b7286a458b8c)] +[scriptable, uuid(cfb08e46-193c-4be7-a467-d7775fb2a31e)] /** * This interface defines a "permission" object, * used to specify allowed/blocked objects from @@ -19,9 +16,19 @@ interface nsIURI; interface nsIPermission : nsISupports { /** - * The principal for which this permission applies. + * The name of the host for which the permission is set */ - readonly attribute nsIPrincipal principal; + readonly attribute AUTF8String host; + + /** + * The id of the app for which the permission is set. + */ + readonly attribute unsigned long appId; + + /** + * Whether the permission has been set to a page inside a browser element. + */ + readonly attribute boolean isInBrowserElement; /** * a case-sensitive ASCII string, indicating the type of permission @@ -49,29 +56,4 @@ interface nsIPermission : nsISupports * 0:00:00). */ readonly attribute int64_t expireTime; - - /** - * Test whether a principal would be affected by this permission. - * - * @param principal the principal to test - * @param exactHost If true, only the specific host will be matched, - * @see nsIPermissionManager::testExactPermission. - * If false, subdomains will also be searched, - * @see nsIPermissionManager::testPermission. - */ - boolean matches(in nsIPrincipal principal, - in boolean exactHost); - - /** - * Test whether a URI would be affected by this permission. - * This performs a matches with a NO_APP_ID identifier. - * - * @param uri the uri to test - * @param exactHost If true, only the specific host will be matched, - * @see nsIPermissionManager::testExactPermission. - * If false, subdomains will also be searched, - * @see nsIPermissionManager::testPermission. - */ - boolean matchesURI(in nsIURI uri, - in boolean exactHost); }; diff --git a/netwerk/ipc/NeckoMessageUtils.h b/netwerk/ipc/NeckoMessageUtils.h index ce0219d21a23..3886a81a8bd7 100644 --- a/netwerk/ipc/NeckoMessageUtils.h +++ b/netwerk/ipc/NeckoMessageUtils.h @@ -20,20 +20,26 @@ namespace IPC { struct Permission { - nsCString origin, type; + nsCString host, type; uint32_t capability, expireType; int64_t expireTime; + uint32_t appId; + bool isInBrowserElement; Permission() { } - Permission(const nsCString& aOrigin, + Permission(const nsCString& aHost, + const uint32_t aAppId, + const bool aIsInBrowserElement, const nsCString& aType, const uint32_t aCapability, const uint32_t aExpireType, - const int64_t aExpireTime) : origin(aOrigin), + const int64_t aExpireTime) : host(aHost), type(aType), capability(aCapability), expireType(aExpireType), - expireTime(aExpireTime) + expireTime(aExpireTime), + appId(aAppId), + isInBrowserElement(aIsInBrowserElement) {} }; @@ -42,26 +48,34 @@ struct ParamTraits { static void Write(Message* aMsg, const Permission& aParam) { - WriteParam(aMsg, aParam.origin); + WriteParam(aMsg, aParam.host); WriteParam(aMsg, aParam.type); WriteParam(aMsg, aParam.capability); WriteParam(aMsg, aParam.expireType); WriteParam(aMsg, aParam.expireTime); + WriteParam(aMsg, aParam.appId); + WriteParam(aMsg, aParam.isInBrowserElement); } static bool Read(const Message* aMsg, void** aIter, Permission* aResult) { - return ReadParam(aMsg, aIter, &aResult->origin) && + return ReadParam(aMsg, aIter, &aResult->host) && ReadParam(aMsg, aIter, &aResult->type) && ReadParam(aMsg, aIter, &aResult->capability) && ReadParam(aMsg, aIter, &aResult->expireType) && - ReadParam(aMsg, aIter, &aResult->expireTime); + ReadParam(aMsg, aIter, &aResult->expireTime) && + ReadParam(aMsg, aIter, &aResult->appId) && + ReadParam(aMsg, aIter, &aResult->isInBrowserElement); } static void Log(const Permission& p, std::wstring* l) { l->append(L"("); - LogParam(p.origin, l); + LogParam(p.host, l); + l->append(L", "); + LogParam(p.appId, l); + l->append(L", "); + LogParam(p.isInBrowserElement, l); l->append(L", "); LogParam(p.capability, l); l->append(L", "); diff --git a/netwerk/test/unit/test_permmgr.js b/netwerk/test/unit/test_permmgr.js index 5a6ddd8b5a15..f472770a5f7a 100644 --- a/netwerk/test/unit/test_permmgr.js +++ b/netwerk/test/unit/test_permmgr.js @@ -2,31 +2,31 @@ var hosts = [ // format: [host, type, permission] - ["http://mozilla.org", "cookie", 1], - ["http://mozilla.org", "image", 2], - ["http://mozilla.org", "popup", 3], - ["http://mozilla.com", "cookie", 1], - ["http://www.mozilla.com", "cookie", 2], - ["http://dev.mozilla.com", "cookie", 3] + ["mozilla.org", "cookie", 1], + ["mozilla.org", "image", 2], + ["mozilla.org", "popup", 3], + ["mozilla.com", "cookie", 1], + ["www.mozilla.com", "cookie", 2], + ["dev.mozilla.com", "cookie", 3] ]; var results = [ // format: [host, type, testPermission result, testExactPermission result] // test defaults - ["http://localhost", "cookie", 0, 0], - ["http://spreadfirefox.com", "cookie", 0, 0], + ["localhost", "cookie", 0, 0], + ["spreadfirefox.com", "cookie", 0, 0], // test different types - ["http://mozilla.org", "cookie", 1, 1], - ["http://mozilla.org", "image", 2, 2], - ["http://mozilla.org", "popup", 3, 3], + ["mozilla.org", "cookie", 1, 1], + ["mozilla.org", "image", 2, 2], + ["mozilla.org", "popup", 3, 3], // test subdomains - ["http://www.mozilla.org", "cookie", 1, 0], - ["http://www.dev.mozilla.org", "cookie", 1, 0], + ["www.mozilla.org", "cookie", 1, 0], + ["www.dev.mozilla.org", "cookie", 1, 0], // test different permissions on subdomains - ["http://mozilla.com", "cookie", 1, 1], - ["http://www.mozilla.com", "cookie", 2, 2], - ["http://dev.mozilla.com", "cookie", 3, 3], - ["http://www.dev.mozilla.com", "cookie", 3, 0] + ["mozilla.com", "cookie", 1, 1], + ["www.mozilla.com", "cookie", 2, 2], + ["dev.mozilla.com", "cookie", 3, 3], + ["www.dev.mozilla.com", "cookie", 3, 0] ]; function run_test() { @@ -45,16 +45,16 @@ function run_test() { // put a few hosts in for (var i = 0; i < hosts.length; ++i) { - let uri = ioService.newURI(hosts[i][0], null, null); - let principal = secMan.getNoAppCodebasePrincipal(uri); + var uri = ioService.newURI("http://" + hosts[i][0], null, null); + var principal = secMan.getNoAppCodebasePrincipal(uri); pm.addFromPrincipal(principal, hosts[i][1], hosts[i][2]); } // test the result for (var i = 0; i < results.length; ++i) { - let uri = ioService.newURI(results[i][0], null, null); - let principal = secMan.getNoAppCodebasePrincipal(uri); + var uri = ioService.newURI("http://" + results[i][0], null, null); + var principal = secMan.getNoAppCodebasePrincipal(uri); do_check_eq(pm.testPermissionFromPrincipal(principal, results[i][1]), results[i][2]); do_check_eq(pm.testExactPermissionFromPrincipal(principal, results[i][1]), results[i][3]); @@ -72,13 +72,16 @@ function run_test() { // ... remove all the hosts ... for (var j = 0; j < perms.length; ++j) { - pm.removePermission(perms[j]); + var uri = ioService.newURI("http://" + perms[j].host, null, null); + var principal = secMan.getNoAppCodebasePrincipal(uri); + + pm.removeFromPrincipal(principal, perms[j].type); } // ... ensure each and every element is equal ... for (var i = 0; i < hosts.length; ++i) { for (var j = 0; j < perms.length; ++j) { - if (perms[j].matchesURI(ioService.newURI(hosts[i][0], null, null), true) && + if (hosts[i][0] == perms[j].host && hosts[i][1] == perms[j].type && hosts[i][2] == perms[j].capability) { perms.splice(j, 1); @@ -99,7 +102,7 @@ function run_test() { var enumerator = pm.enumerator; do_check_eq(enumerator.hasMoreElements(), true); var ace = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission); - do_check_eq(ace.principal.URI.asciiHost, aceref); + do_check_eq(ace.host, aceref); do_check_eq(enumerator.hasMoreElements(), false); // test removeAll() diff --git a/testing/mozbase/mozprofile/mozprofile/permissions.py b/testing/mozbase/mozprofile/mozprofile/permissions.py index 56bfa18b57a5..126d0f91f99b 100644 --- a/testing/mozbase/mozprofile/mozprofile/permissions.py +++ b/testing/mozbase/mozprofile/mozprofile/permissions.py @@ -231,26 +231,18 @@ class Permissions(object): # SQL copied from # http://mxr.mozilla.org/mozilla-central/source/extensions/cookie/nsPermissionManager.cpp cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( - id INTEGER PRIMARY KEY - ,origin TEXT - ,type TEXT - ,permission INTEGER - ,expireType INTEGER - ,expireTime INTEGER - ,modificationTime INTEGER - )""") + id INTEGER PRIMARY KEY, + host TEXT, + type TEXT, + permission INTEGER, + expireType INTEGER, + expireTime INTEGER)""") rows = cursor.execute("PRAGMA table_info(moz_hosts)") count = len(rows.fetchall()) - using_origin = False - # if the db contains 7 columns, we're using user_version 5 - if count == 7: - statement = "INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0)" - cursor.execute("PRAGMA user_version=5;") - using_origin = True # if the db contains 9 columns, we're using user_version 4 - elif count == 9: + if count == 9: statement = "INSERT INTO moz_hosts values(NULL, ?, ?, ?, 0, 0, 0, 0, 0)" cursor.execute("PRAGMA user_version=4;") # if the db contains 8 columns, we're using user_version 3 @@ -270,26 +262,8 @@ class Permissions(object): else: permission_type = 2 - if using_origin: - # This is a crude approximation of the origin generation logic from - # nsPrincipal and nsStandardURL. It should suffice for the permissions - # which the test runners will want to insert into the system. - origin = location.scheme + "://" + location.host - if (location.scheme != 'http' or location.port != '80') and \ - (location.scheme != 'https' or location.port != '443'): - origin += ':' + str(location.port) - - cursor.execute(statement, - (origin, perm, permission_type)) - else: - # The database is still using a legacy system based on hosts - # We can insert the permission as a host - # - # XXX This codepath should not be hit, as tests are run with - # fresh profiles. However, if it was hit, permissions would - # not be added to the database correctly (bug 1183185). - cursor.execute(statement, - (location.host, perm, permission_type)) + cursor.execute(statement, + (location.host, perm, permission_type)) # Commit and close permDB.commit() diff --git a/testing/mozbase/mozprofile/tests/bug785146.py b/testing/mozbase/mozprofile/tests/bug785146.py index 629a81523aba..26b98ffc0c46 100755 --- a/testing/mozbase/mozprofile/tests/bug785146.py +++ b/testing/mozbase/mozprofile/tests/bug785146.py @@ -44,7 +44,7 @@ http://127.0.0.1:8888 privileged entries = cur.fetchall() schema_version = entries[0][0] - self.assertEqual(schema_version, 5) + self.assertEqual(schema_version, 2) if __name__ == '__main__': unittest.main() diff --git a/testing/mozbase/mozprofile/tests/permissions.py b/testing/mozbase/mozprofile/tests/permissions.py index 389f14a5c8a0..b402991d929e 100755 --- a/testing/mozbase/mozprofile/tests/permissions.py +++ b/testing/mozbase/mozprofile/tests/permissions.py @@ -40,16 +40,7 @@ http://127.0.0.1:8888 privileged cursor.execute("PRAGMA user_version=%d;" % version) - if version == 5: - cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( - id INTEGER PRIMARY KEY, - origin TEXT, - type TEXT, - permission INTEGER, - expireType INTEGER, - expireTime INTEGER, - modificationTime INTEGER)""") - elif version == 4: + if version == 4: cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( id INTEGER PRIMARY KEY, host TEXT, @@ -79,7 +70,7 @@ http://127.0.0.1:8888 privileged expireType INTEGER, expireTime INTEGER)""") else: - raise Exception("version must be 2, 3, 4 or 5") + raise Exception("version must be 2, 3 or 4") permDB.commit() cursor.close() @@ -88,7 +79,7 @@ http://127.0.0.1:8888 privileged perms = Permissions(self.profile_dir, self.locations_file.name) perms_db_filename = os.path.join(self.profile_dir, 'permissions.sqlite') - select_stmt = 'select origin, type, permission from moz_hosts' + select_stmt = 'select host, type, permission from moz_hosts' con = sqlite3.connect(perms_db_filename) cur = con.cursor() @@ -97,32 +88,32 @@ http://127.0.0.1:8888 privileged self.assertEqual(len(entries), 3) - self.assertEqual(entries[0][0], 'http://mochi.test:8888') + self.assertEqual(entries[0][0], 'mochi.test') self.assertEqual(entries[0][1], 'allowXULXBL') self.assertEqual(entries[0][2], 1) - self.assertEqual(entries[1][0], 'http://127.0.0.1') + self.assertEqual(entries[1][0], '127.0.0.1') self.assertEqual(entries[1][1], 'allowXULXBL') self.assertEqual(entries[1][2], 2) - self.assertEqual(entries[2][0], 'http://127.0.0.1:8888') + self.assertEqual(entries[2][0], '127.0.0.1') self.assertEqual(entries[2][1], 'allowXULXBL') self.assertEqual(entries[2][2], 1) - perms._locations.add_host('a.b.c', port='8081', scheme='https', options='noxul') + perms._locations.add_host('a.b.c', options='noxul') cur.execute(select_stmt) entries = cur.fetchall() self.assertEqual(len(entries), 4) - self.assertEqual(entries[3][0], 'https://a.b.c:8081') + self.assertEqual(entries[3][0], 'a.b.c') self.assertEqual(entries[3][1], 'allowXULXBL') self.assertEqual(entries[3][2], 2) - # when creating a DB we should default to user_version==5 + # when creating a DB we should default to user_version==2 cur.execute('PRAGMA user_version') entries = cur.fetchall() - self.assertEqual(entries[0][0], 5) + self.assertEqual(entries[0][0], 2) perms.clean_db() # table should be removed @@ -169,14 +160,7 @@ http://127.0.0.1:8888 privileged self.assertEqual(len(entries), 3) - columns = { - 1: 6, - 2: 6, - 3: 8, - 4: 9, - 5: 7, - }[version] - + columns = 9 if version == 4 else (8 if version == 3 else 6) self.assertEqual(len(entries[0]), columns) for x in range(4, columns): self.assertEqual(entries[0][x], 0) @@ -190,8 +174,5 @@ http://127.0.0.1:8888 privileged def test_existing_permissions_db_v4(self): self.verify_user_version(4) - def test_existing_permissions_db_v5(self): - self.verify_user_version(5) - if __name__ == '__main__': unittest.main() diff --git a/testing/specialpowers/components/SpecialPowersObserver.js b/testing/specialpowers/components/SpecialPowersObserver.js index 10703da4a622..95f514455409 100644 --- a/testing/specialpowers/components/SpecialPowersObserver.js +++ b/testing/specialpowers/components/SpecialPowersObserver.js @@ -217,16 +217,7 @@ SpecialPowersObserver.prototype = new SpecialPowersObserverAPI(); switch (aTopic) { case "perm-changed": var permission = aSubject.QueryInterface(Ci.nsIPermission); - - // specialPowersAPI will consume this value, and it is used as a - // fake permission, but only type and principal.appId will be used. - // - // We need to ensure that it looks the same as a real permission, - // so we fake these properties. - msg.permission = { - principal: { appId: permission.principal.appId }, - type: permission.type - }; + msg.permission = { appId: permission.appId, type: permission.type }; default: this._self._sendAsyncMessage("specialpowers-" + aTopic, msg); } diff --git a/testing/specialpowers/content/specialpowersAPI.js b/testing/specialpowers/content/specialpowersAPI.js index 6a01c635c292..59cd60b415ee 100644 --- a/testing/specialpowers/content/specialpowersAPI.js +++ b/testing/specialpowers/content/specialpowersAPI.js @@ -967,7 +967,7 @@ SpecialPowersAPI.prototype = { for (var j = 0; j < undos.length; j++) { var undo = undos[j]; if (undo.op == this._obsDataMap[aData] && - undo.appId == permission.principal.appId && + undo.appId == permission.appId && undo.type == permission.type) { // Remove this undo item if it has been done by others(not // specialpowers itself.) diff --git a/toolkit/forgetaboutsite/ForgetAboutSite.jsm b/toolkit/forgetaboutsite/ForgetAboutSite.jsm index ad9f5a46370a..9e9e65298d90 100644 --- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm +++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm @@ -140,13 +140,8 @@ this.ForgetAboutSite = { enumerator = pm.enumerator; while (enumerator.hasMoreElements()) { let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission); - try { - if (hasRootDomain(perm.principal.URI.host, aDomain)) { - pm.removePermission(perm); - } - } catch (e) { - /* Ignore entry */ - } + if (hasRootDomain(perm.host, aDomain)) + pm.removePermission(perm); } // Offline Storages diff --git a/toolkit/modules/BrowserUtils.jsm b/toolkit/modules/BrowserUtils.jsm index 4f17f4b879ba..412e574214ec 100644 --- a/toolkit/modules/BrowserUtils.jsm +++ b/toolkit/modules/BrowserUtils.jsm @@ -105,7 +105,7 @@ this.BrowserUtils = { throw new Error("principalFromOrigin does not support nsNullPrincipal"); } - var parts = aOriginString.split('^'); + var parts = aOriginString.split('!'); if (parts.length > 2) { throw new Error("bad origin string: " + aOriginString); } diff --git a/toolkit/modules/PermissionsUtils.jsm b/toolkit/modules/PermissionsUtils.jsm index ec04826959ca..37e7abb5b15e 100644 --- a/toolkit/modules/PermissionsUtils.jsm +++ b/toolkit/modules/PermissionsUtils.jsm @@ -7,7 +7,6 @@ this.EXPORTED_SYMBOLS = ["PermissionsUtils"]; const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/BrowserUtils.jsm") let gImportedPrefBranches = new Set(); @@ -16,40 +15,29 @@ function importPrefBranch(aPrefBranch, aPermission, aAction) { let list = Services.prefs.getChildList(aPrefBranch, {}); for (let pref of list) { - let origins = ""; + let hosts = ""; try { - origins = Services.prefs.getCharPref(pref); + hosts = Services.prefs.getCharPref(pref); } catch (e) {} - if (!origins) + if (!hosts) continue; - origins = origins.split(","); + hosts = hosts.split(","); - for (let origin of origins) { - let principals = []; + for (let host of hosts) { + let uri = null; try { - principals = [ BrowserUtils.principalFromOrigin(origin) ]; + uri = Services.io.newURI("http://" + host, null, null); } catch (e) { - // This preference used to contain a list of hosts. For back-compat - // reasons, we convert these hosts into http:// and https:// permissions - // on default ports. try { - let httpURI = Services.io.newURI("http://" + origin, null, null); - let httpsURI = Services.io.newURI("https://" + origin, null, null); - - principals = [ - Services.scriptSecurityManager.getNoAppCodebasePrincipal(httpURI), - Services.scriptSecurityManager.getNoAppCodebasePrincipal(httpsURI) - ]; + uri = Services.io.newURI(host, null, null); } catch (e2) {} } - for (let principal of principals) { - try { - Services.perms.addFromPrincipal(principal, aPermission, aAction); - } catch (e) {} - } + try { + Services.perms.add(uri, aPermission, aAction); + } catch (e) {} } Services.prefs.setCharPref(pref, ""); diff --git a/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js b/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js index 48769f831234..a3bf2fa4afa0 100644 --- a/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js +++ b/toolkit/modules/tests/xpcshell/test_PermissionsUtils.js @@ -23,36 +23,22 @@ function test_importfromPrefs() { // Create own preferences to test Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.EMPTY", ""); Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.EMPTY2", ","); - Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST", "http://whitelist.example.com"); - Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST2", "https://whitelist2-1.example.com,http://whitelist2-2.example.com:8080,about:home"); - Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST3", "whitelist3-1.example.com,about:config"); // legacy style - host only + Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST", "whitelist.example.com"); + Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST2", "whitelist2-1.example.com,whitelist2-2.example.com,about:home"); Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.EMPTY", ""); - Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST", "http://blacklist.example.com,"); - Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST2", ",https://blacklist2-1.example.com,http://blacklist2-2.example.com:8080,about:mozilla"); - Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST3", "blacklist3-1.example.com,about:preferences"); // legacy style - host only + Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST", "blacklist.example.com,"); + Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST2", ",blacklist2-1.example.com,blacklist2-2.example.com,about:mozilla"); // Check they are unknown in the permission manager prior to importing. let whitelisted = ["http://whitelist.example.com", - "https://whitelist2-1.example.com", - "http://whitelist2-2.example.com:8080", - "http://whitelist3-1.example.com", - "https://whitelist3-1.example.com", - "about:config", + "http://whitelist2-1.example.com", + "http://whitelist2-2.example.com", "about:home"]; let blacklisted = ["http://blacklist.example.com", - "https://blacklist2-1.example.com", - "http://blacklist2-2.example.com:8080", - "http://blacklist3-1.example.com", - "https://blacklist3-1.example.com", - "about:preferences", + "http://blacklist2-1.example.com", + "http://blacklist2-2.example.com", "about:mozilla"]; - let untouched = ["https://whitelist.example.com", - "https://blacklist.example.com", - "http://whitelist2-1.example.com", - "http://blacklist2-1.example.com", - "https://whitelist2-2.example.com:8080", - "https://blacklist2-2.example.com:8080"]; - let unknown = whitelisted.concat(blacklisted).concat(untouched); + let unknown = whitelisted.concat(blacklisted); for (let url of unknown) { let uri = Services.io.newURI(url, null, null); do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.UNKNOWN_ACTION); @@ -78,8 +64,4 @@ function test_importfromPrefs() { let uri = Services.io.newURI(url, null, null); do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.DENY_ACTION); } - for (let url of untouched) { - let uri = Services.io.newURI(url, null, null); - do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.UNKNOWN_ACTION); - } } diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_permissions.js b/toolkit/mozapps/extensions/test/xpcshell/test_permissions.js index eb8bc3e5c912..869b7bdc1eed 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_permissions.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_permissions.js @@ -12,9 +12,9 @@ const XPI_MIMETYPE = "application/x-xpinstall"; function run_test() { createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2"); - Services.prefs.setCharPref("xpinstall.whitelist.add", "https://test1.com,https://test2.com"); - Services.prefs.setCharPref("xpinstall.whitelist.add.36", "https://test3.com,https://www.test4.com"); - Services.prefs.setCharPref("xpinstall.whitelist.add.test5", "https://test5.com"); + Services.prefs.setCharPref("xpinstall.whitelist.add", "test1.com,test2.com"); + Services.prefs.setCharPref("xpinstall.whitelist.add.36", "test3.com,www.test4.com"); + Services.prefs.setCharPref("xpinstall.whitelist.add.test5", "test5.com"); Services.perms.add(NetUtil.newURI("https://www.test9.com"), "install", AM_Ci.nsIPermissionManager.ALLOW_ACTION); diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_permissions_prefs.js b/toolkit/mozapps/extensions/test/xpcshell/test_permissions_prefs.js index d3eb5ca02f71..75c615ace00c 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_permissions_prefs.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_permissions_prefs.js @@ -30,9 +30,9 @@ function run_test() { // Create own preferences to test Services.prefs.setCharPref("xpinstall.whitelist.add.EMPTY", ""); - Services.prefs.setCharPref("xpinstall.whitelist.add.TEST", "http://whitelist.example.com"); + Services.prefs.setCharPref("xpinstall.whitelist.add.TEST", "whitelist.example.com"); Services.prefs.setCharPref("xpinstall.blacklist.add.EMPTY", ""); - Services.prefs.setCharPref("xpinstall.blacklist.add.TEST", "http://blacklist.example.com"); + Services.prefs.setCharPref("xpinstall.blacklist.add.TEST", "blacklist.example.com"); // Get list of preferences to check var whitelistPreferences = Services.prefs.getChildList(PREF_XPI_WHITELIST_PERMISSIONS, {}); @@ -53,19 +53,19 @@ function run_test() { // First, request to flush all permissions clear_imported_preferences_cache(); - Services.prefs.setCharPref("xpinstall.whitelist.add.TEST2", "https://whitelist2.example.com"); + Services.prefs.setCharPref("xpinstall.whitelist.add.TEST2", "whitelist2.example.com"); Services.obs.notifyObservers(null, "flush-pending-permissions", "install"); do_check_permission_prefs(preferences); // Then, request to flush just install permissions clear_imported_preferences_cache(); - Services.prefs.setCharPref("xpinstall.whitelist.add.TEST3", "https://whitelist3.example.com"); + Services.prefs.setCharPref("xpinstall.whitelist.add.TEST3", "whitelist3.example.com"); Services.obs.notifyObservers(null, "flush-pending-permissions", ""); do_check_permission_prefs(preferences); // And a request to flush some other permissions sholdn't flush install permissions clear_imported_preferences_cache(); - Services.prefs.setCharPref("xpinstall.whitelist.add.TEST4", "https://whitelist4.example.com"); + Services.prefs.setCharPref("xpinstall.whitelist.add.TEST4", "whitelist4.example.com"); Services.obs.notifyObservers(null, "flush-pending-permissions", "lolcats"); - do_check_eq(Services.prefs.getCharPref("xpinstall.whitelist.add.TEST4"), "https://whitelist4.example.com"); + do_check_eq(Services.prefs.getCharPref("xpinstall.whitelist.add.TEST4"), "whitelist4.example.com"); } From 6aa2608c1cc99fdd54afbedb2071f8b202ded0c5 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Tue, 14 Jul 2015 16:08:36 +0100 Subject: [PATCH 61/67] Bug 1160890 - Part 1: Remove unneeded code from ImportScripts(). r=smaug --- dom/workers/ScriptLoader.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index 026bd09b3655..e22a090491da 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -66,7 +66,6 @@ using mozilla::dom::cache::Cache; using mozilla::dom::cache::CacheStorage; using mozilla::dom::Promise; using mozilla::dom::PromiseNativeHandler; -using mozilla::dom::workers::exceptions::ThrowDOMExceptionForNSResult; using mozilla::ErrorResult; using mozilla::ipc::PrincipalInfo; using mozilla::UniquePtr; @@ -151,13 +150,6 @@ ChannelFromScriptURL(nsIPrincipal* principal, return NS_ERROR_DOM_SECURITY_ERR; } } else if (aIsMainScript) { - // If this script loader is being used to make a new worker then we need - // to do a same-origin check. Otherwise we need to clear the load with the - // security manager. - nsCString scheme; - rv = uri->GetScheme(scheme); - NS_ENSURE_SUCCESS(rv, rv); - // We pass true as the 3rd argument to checkMayLoad here. // This allows workers in sandboxed documents to load data URLs // (and other URLs that inherit their principal from their @@ -1567,7 +1559,7 @@ CacheScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aCont return NS_OK; } - class ChannelGetterRunnable final : public nsRunnable +class ChannelGetterRunnable final : public nsRunnable { WorkerPrivate* mParentWorker; nsCOMPtr mSyncLoopTarget; From 804881c9a1c58fce3776cae682c522dc34a873ce Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Tue, 14 Jul 2015 16:08:38 +0100 Subject: [PATCH 62/67] Bug 1160890 - Part 2: ImportScripts() should return muted errors with 3rd party scripts. r=smaug --- dom/workers/ScriptLoader.cpp | 47 ++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index e22a090491da..56838e7fa965 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -275,6 +275,8 @@ struct ScriptLoadInfo CacheStatus mCacheStatus; + Maybe mMutedErrorFlag; + bool Finished() const { return mLoadingFinished && !mCachePromise && !mChannel; @@ -947,12 +949,33 @@ private: NS_ASSERTION(aString, "This should never be null!"); - // Make sure we're not seeing the result of a 404 or something by checking - // the 'requestSucceeded' attribute on the http channel. nsCOMPtr request; nsresult rv = aLoader->GetRequest(getter_AddRefs(request)); NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr channel = do_QueryInterface(request); + MOZ_ASSERT(channel); + + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + NS_ASSERTION(ssm, "Should never be null!"); + + nsCOMPtr channelPrincipal; + rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsIPrincipal* principal = mWorkerPrivate->GetPrincipal(); + if (!principal) { + WorkerPrivate* parentWorker = mWorkerPrivate->GetParent(); + MOZ_ASSERT(parentWorker, "Must have a parent!"); + principal = parentWorker->GetPrincipal(); + } + + aLoadInfo.mMutedErrorFlag.emplace(principal->Subsumes(channelPrincipal)); + + // Make sure we're not seeing the result of a 404 or something by checking + // the 'requestSucceeded' attribute on the http channel. nsCOMPtr httpChannel = do_QueryInterface(request); if (httpChannel) { bool requestSucceeded; @@ -988,9 +1011,6 @@ private: return NS_ERROR_FAILURE; } - nsCOMPtr channel = do_QueryInterface(request); - NS_ASSERTION(channel, "This should never fail!"); - // Figure out what we actually loaded. nsCOMPtr finalURI; rv = NS_GetFinalChannelURI(channel, getter_AddRefs(finalURI)); @@ -1096,6 +1116,18 @@ private: ScriptLoadInfo& loadInfo = mLoadInfos[aIndex]; MOZ_ASSERT(loadInfo.mCacheStatus == ScriptLoadInfo::Cached); + nsCOMPtr responsePrincipal = + PrincipalInfoToPrincipal(*aPrincipalInfo); + + nsIPrincipal* principal = mWorkerPrivate->GetPrincipal(); + if (!principal) { + WorkerPrivate* parentWorker = mWorkerPrivate->GetParent(); + MOZ_ASSERT(parentWorker, "Must have a parent!"); + principal = parentWorker->GetPrincipal(); + } + + loadInfo.mMutedErrorFlag.emplace(principal->Subsumes(responsePrincipal)); + // May be null. nsIDocument* parentDoc = mWorkerPrivate->GetDocument(); @@ -1118,8 +1150,6 @@ private: nsILoadGroup* loadGroup = mWorkerPrivate->GetLoadGroup(); MOZ_ASSERT(loadGroup); - nsCOMPtr responsePrincipal = - PrincipalInfoToPrincipal(*aPrincipalInfo); mozilla::DebugOnly equal = false; MOZ_ASSERT(responsePrincipal && NS_SUCCEEDED(responsePrincipal->Equals(principal, &equal))); MOZ_ASSERT(equal); @@ -1712,6 +1742,9 @@ ScriptExecutorRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) options.setVersion(JSVERSION_LATEST); } + MOZ_ASSERT(loadInfo.mMutedErrorFlag.isSome()); + options.setMutedErrors(loadInfo.mMutedErrorFlag.valueOr(true)); + JS::SourceBufferHolder srcBuf(loadInfo.mScriptTextBuf, loadInfo.mScriptTextLength, JS::SourceBufferHolder::GiveOwnership); From 50f4ad7609b691e886dccbf8a9a6c1ae1f2c25d7 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Tue, 14 Jul 2015 17:11:46 -0400 Subject: [PATCH 63/67] Bug 1180990 - Add checks for nursery objects when building MIR. r=jandem CLOSED TREE --HG-- extra : amend_source : 8b079e680f9b8fb3e0d36603ff1287b025774e7e --- js/src/jit/IonBuilder.cpp | 16 ++++----- js/src/jit/MCallOptimize.cpp | 10 +++--- js/src/jit/MIR.cpp | 65 ++++++++++++++++++++++++++++++++---- js/src/jit/MIR.h | 6 ++-- js/src/vm/TypeInference.cpp | 51 ---------------------------- js/src/vm/TypeInference.h | 11 ------ 6 files changed, 74 insertions(+), 85 deletions(-) diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 1b541b3cc8be..66866e4f2d04 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -8154,7 +8154,7 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index) // Don't generate a fast path if there have been bounds check failures // and this access might be on a sparse property. - if (ElementAccessHasExtraIndexedProperty(constraints(), obj) && failedBoundsCheck_) { + if (ElementAccessHasExtraIndexedProperty(this, obj) && failedBoundsCheck_) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return true; } @@ -8188,7 +8188,7 @@ IonBuilder::getStaticTypedArrayObject(MDefinition* obj, MDefinition* index) return nullptr; } - if (ElementAccessHasExtraIndexedProperty(constraints(), obj)) { + if (ElementAccessHasExtraIndexedProperty(this, obj)) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return nullptr; } @@ -8536,7 +8536,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType // cannot hit another indexed property on the object or its prototypes. bool readOutOfBounds = types->hasType(TypeSet::UndefinedType()) && - !ElementAccessHasExtraIndexedProperty(constraints(), obj); + !ElementAccessHasExtraIndexedProperty(this, obj); MIRType knownType = MIRType_Value; if (unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier) @@ -9097,7 +9097,7 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, // Don't generate a fast path if there have been bounds check failures // and this access might be on a sparse property. - if (ElementAccessHasExtraIndexedProperty(constraints(), object) && failedBoundsCheck_) { + if (ElementAccessHasExtraIndexedProperty(this, object) && failedBoundsCheck_) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return true; } @@ -9163,7 +9163,7 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object, // from them. If TI can guard that there are no indexed properties on the prototype // chain, we know that we anen't missing any setters by overwriting the hole with // another value. - bool guardHoles = ElementAccessHasExtraIndexedProperty(constraints(), object); + bool guardHoles = ElementAccessHasExtraIndexedProperty(this, object); // Make sure the object being written to doesn't have copy on write elements. const Class* clasp = object->resultTypeSet() ? object->resultTypeSet()->getKnownClass(constraints()) : nullptr; @@ -9199,7 +9199,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, // Writes which are on holes in the object do not have to bail out if they // cannot hit another indexed property on the object or its prototypes. - bool writeOutOfBounds = !ElementAccessHasExtraIndexedProperty(constraints(), obj); + bool writeOutOfBounds = !ElementAccessHasExtraIndexedProperty(this, obj); if (NeedsPostBarrier(info(), value)) current->add(MPostWriteBarrier::New(alloc(), obj, value)); @@ -11068,7 +11068,7 @@ IonBuilder::getPropTryCache(bool* emitted, MDefinition* obj, PropertyName* name, // reflect such possible values. if (barrier != BarrierKind::TypeSet) { BarrierKind protoBarrier = - PropertyReadOnPrototypeNeedsTypeBarrier(this, constraints(), obj, name, types); + PropertyReadOnPrototypeNeedsTypeBarrier(this, obj, name, types); if (protoBarrier != BarrierKind::NoBarrier) { MOZ_ASSERT(barrier <= protoBarrier); barrier = protoBarrier; @@ -12373,7 +12373,7 @@ IonBuilder::jsop_in() break; } - if (ElementAccessHasExtraIndexedProperty(constraints(), obj)) + if (ElementAccessHasExtraIndexedProperty(this, obj)) break; return jsop_in_dense(obj, id, unboxedType); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 7a7bcd67adff..c5a6eeec8a4b 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -649,7 +649,7 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode) return InliningStatus_NotInlined; } - if (ArrayPrototypeHasIndexedProperty(constraints(), script())) { + if (ArrayPrototypeHasIndexedProperty(this, script())) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return InliningStatus_NotInlined; } @@ -791,7 +791,7 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo) return InliningStatus_NotInlined; } - if (ArrayPrototypeHasIndexedProperty(constraints(), script())) { + if (ArrayPrototypeHasIndexedProperty(this, script())) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return InliningStatus_NotInlined; } @@ -890,7 +890,7 @@ IonBuilder::inlineArrayConcat(CallInfo& callInfo) } // Watch out for indexed properties on the prototype. - if (ArrayPrototypeHasIndexedProperty(constraints(), script())) { + if (ArrayPrototypeHasIndexedProperty(this, script())) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return InliningStatus_NotInlined; } @@ -1006,7 +1006,7 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) } // Watch out for indexed properties on the prototype. - if (ArrayPrototypeHasIndexedProperty(constraints(), script())) { + if (ArrayPrototypeHasIndexedProperty(this, script())) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return InliningStatus_NotInlined; } @@ -2127,7 +2127,7 @@ IonBuilder::inlineDefineDataProperty(CallInfo& callInfo) MDefinition* id = callInfo.getArg(1); MDefinition* value = callInfo.getArg(2); - if (ElementAccessHasExtraIndexedProperty(constraints(), obj)) + if (ElementAccessHasExtraIndexedProperty(this, obj)) return InliningStatus_NotInlined; // setElemTryDense will push the value as the result of the define instead diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 6d69e15af32c..e6272a40d7d2 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -4884,15 +4884,14 @@ jit::ElementAccessMightBeCopyOnWrite(CompilerConstraintList* constraints, MDefin } bool -jit::ElementAccessHasExtraIndexedProperty(CompilerConstraintList* constraints, - MDefinition* obj) +jit::ElementAccessHasExtraIndexedProperty(IonBuilder* builder, MDefinition* obj) { TemporaryTypeSet* types = obj->resultTypeSet(); - if (!types || types->hasObjectFlags(constraints, OBJECT_FLAG_LENGTH_OVERFLOW)) + if (!types || types->hasObjectFlags(builder->constraints(), OBJECT_FLAG_LENGTH_OVERFLOW)) return true; - return TypeCanHaveExtraIndexedProperties(constraints, types); + return TypeCanHaveExtraIndexedProperties(builder, types); } MIRType @@ -5056,7 +5055,6 @@ jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx, BarrierKind jit::PropertyReadOnPrototypeNeedsTypeBarrier(IonBuilder* builder, - CompilerConstraintList* constraints, MDefinition* obj, PropertyName* name, TemporaryTypeSet* observed) { @@ -5074,13 +5072,14 @@ jit::PropertyReadOnPrototypeNeedsTypeBarrier(IonBuilder* builder, if (!key) continue; while (true) { - if (!key->hasStableClassAndProto(constraints)) + if (!key->hasStableClassAndProto(builder->constraints())) return BarrierKind::TypeSet; if (!key->proto().isObject()) break; JSObject* proto = builder->checkNurseryObject(key->proto().toObject()); key = TypeSet::ObjectKey::get(proto); - BarrierKind kind = PropertyReadNeedsTypeBarrier(constraints, key, name, observed); + BarrierKind kind = PropertyReadNeedsTypeBarrier(builder->constraints(), + key, name, observed); if (kind == BarrierKind::TypeSet) return BarrierKind::TypeSet; @@ -5164,6 +5163,58 @@ jit::AddObjectsForPropertyRead(MDefinition* obj, PropertyName* name, } } +static bool +PrototypeHasIndexedProperty(IonBuilder* builder, JSObject* obj) +{ + do { + TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(builder->checkNurseryObject(obj)); + if (ClassCanHaveExtraProperties(key->clasp())) + return true; + if (key->unknownProperties()) + return true; + HeapTypeSetKey index = key->property(JSID_VOID); + if (index.nonData(builder->constraints()) || index.isOwnProperty(builder->constraints())) + return true; + obj = obj->getProto(); + } while (obj); + + return false; +} + +// Whether Array.prototype, or an object on its proto chain, has an indexed property. +bool +jit::ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script) +{ + if (JSObject* proto = script->global().maybeGetArrayPrototype()) + return PrototypeHasIndexedProperty(builder, proto); + return true; +} + +// Whether obj or any of its prototypes have an indexed property. +bool +jit::TypeCanHaveExtraIndexedProperties(IonBuilder* builder, TemporaryTypeSet* types) +{ + const Class* clasp = types->getKnownClass(builder->constraints()); + + // Note: typed arrays have indexed properties not accounted for by type + // information, though these are all in bounds and will be accounted for + // by JIT paths. + if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsAnyTypedArrayClass(clasp))) + return true; + + if (types->hasObjectFlags(builder->constraints(), OBJECT_FLAG_SPARSE_INDEXES)) + return true; + + JSObject* proto; + if (!types->getCommonPrototype(builder->constraints(), &proto)) + return true; + + if (!proto) + return false; + + return PrototypeHasIndexedProperty(builder, proto); +} + static bool PropertyTypeIncludes(TempAllocator& alloc, HeapTypeSetKey property, MDefinition* value, MIRType implicitType) diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index d14f06aa5a14..428f1645cb62 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -13719,8 +13719,7 @@ bool ElementAccessIsAnyTypedArray(CompilerConstraintList* constraints, Scalar::Type* arrayType); bool ElementAccessIsPacked(CompilerConstraintList* constraints, MDefinition* obj); bool ElementAccessMightBeCopyOnWrite(CompilerConstraintList* constraints, MDefinition* obj); -bool ElementAccessHasExtraIndexedProperty(CompilerConstraintList* constraints, - MDefinition* obj); +bool ElementAccessHasExtraIndexedProperty(IonBuilder* builder, MDefinition* obj); MIRType DenseNativeElementType(CompilerConstraintList* constraints, MDefinition* obj); BarrierKind PropertyReadNeedsTypeBarrier(JSContext* propertycx, CompilerConstraintList* constraints, @@ -13731,7 +13730,6 @@ BarrierKind PropertyReadNeedsTypeBarrier(JSContext* propertycx, MDefinition* obj, PropertyName* name, TemporaryTypeSet* observed); BarrierKind PropertyReadOnPrototypeNeedsTypeBarrier(IonBuilder* builder, - CompilerConstraintList* constraints, MDefinition* obj, PropertyName* name, TemporaryTypeSet* observed); bool PropertyReadIsIdempotent(CompilerConstraintList* constraints, @@ -13745,6 +13743,8 @@ bool PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* MBasicBlock* current, MDefinition** pobj, PropertyName* name, MDefinition** pvalue, bool canModify, MIRType implicitType = MIRType_None); +bool ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script); +bool TypeCanHaveExtraIndexedProperties(IonBuilder* builder, TemporaryTypeSet* types); } // namespace jit } // namespace js diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index c016bf476c10..fcc2513088c6 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -2449,57 +2449,6 @@ js::ClassCanHaveExtraProperties(const Class* clasp) || IsAnyTypedArrayClass(clasp); } -static bool -PrototypeHasIndexedProperty(CompilerConstraintList* constraints, JSObject* obj) -{ - do { - TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(obj); - if (ClassCanHaveExtraProperties(key->clasp())) - return true; - if (key->unknownProperties()) - return true; - HeapTypeSetKey index = key->property(JSID_VOID); - if (index.nonData(constraints) || index.isOwnProperty(constraints)) - return true; - obj = obj->getProto(); - } while (obj); - - return false; -} - -bool -js::ArrayPrototypeHasIndexedProperty(CompilerConstraintList* constraints, JSScript* script) -{ - if (JSObject* proto = script->global().maybeGetArrayPrototype()) - return PrototypeHasIndexedProperty(constraints, proto); - return true; -} - -bool -js::TypeCanHaveExtraIndexedProperties(CompilerConstraintList* constraints, - TemporaryTypeSet* types) -{ - const Class* clasp = types->getKnownClass(constraints); - - // Note: typed arrays have indexed properties not accounted for by type - // information, though these are all in bounds and will be accounted for - // by JIT paths. - if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsAnyTypedArrayClass(clasp))) - return true; - - if (types->hasObjectFlags(constraints, OBJECT_FLAG_SPARSE_INDEXES)) - return true; - - JSObject* proto; - if (!types->getCommonPrototype(constraints, &proto)) - return true; - - if (!proto) - return false; - - return PrototypeHasIndexedProperty(constraints, proto); -} - void TypeZone::processPendingRecompiles(FreeOp* fop, RecompileInfoVector& recompiles) { diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h index 7bbd49f1609d..977fbce8944b 100644 --- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -974,17 +974,6 @@ inline bool isInlinableCall(jsbytecode* pc); bool ClassCanHaveExtraProperties(const Class* clasp); -/* - * Whether Array.prototype, or an object on its proto chain, has an - * indexed property. - */ -bool -ArrayPrototypeHasIndexedProperty(CompilerConstraintList* constraints, JSScript* script); - -/* Whether obj or any of its prototypes have an indexed property. */ -bool -TypeCanHaveExtraIndexedProperties(CompilerConstraintList* constraints, TemporaryTypeSet* types); - /* Persistent type information for a script, retained across GCs. */ class TypeScript { From 2fddee2d733e642c94f91280c7980f9ff6c007a9 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Tue, 14 Jul 2015 14:35:53 -0700 Subject: [PATCH 64/67] Backed out changeset 5a310370cec1 (bug 1173876) --- accessible/atk/nsMaiInterfaceText.cpp | 76 ++++++++++++--------------- 1 file changed, 34 insertions(+), 42 deletions(-) diff --git a/accessible/atk/nsMaiInterfaceText.cpp b/accessible/atk/nsMaiInterfaceText.cpp index 6e421d27bd8b..56193f66aef7 100644 --- a/accessible/atk/nsMaiInterfaceText.cpp +++ b/accessible/atk/nsMaiInterfaceText.cpp @@ -163,24 +163,22 @@ getTextAfterOffsetCB(AtkText *aText, gint aOffset, AtkTextBoundary aBoundaryType, gint *aStartOffset, gint *aEndOffset) { - nsAutoString autoStr; - int32_t startOffset = 0, endOffset = 0; AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); - if (accWrap) { - HyperTextAccessible* text = accWrap->AsHyperText(); - if (!text || !text->IsTextRole()) - return nullptr; + if (!accWrap) + return nullptr; - text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr); - ConvertTexttoAsterisks(accWrap, autoStr); - } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) { - proxy->GetTextAfterOffset(aOffset, aBoundaryType, autoStr, &startOffset, - &endOffset); - } + HyperTextAccessible* text = accWrap->AsHyperText(); + if (!text || !text->IsTextRole()) + return nullptr; + + nsAutoString autoStr; + int32_t startOffset = 0, endOffset = 0; + text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr); *aStartOffset = startOffset; *aEndOffset = endOffset; + ConvertTexttoAsterisks(accWrap, autoStr); NS_ConvertUTF16toUTF8 cautoStr(autoStr); return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr; } @@ -190,26 +188,23 @@ getTextAtOffsetCB(AtkText *aText, gint aOffset, AtkTextBoundary aBoundaryType, gint *aStartOffset, gint *aEndOffset) { - nsAutoString autoStr; - int32_t startOffset = 0, endOffset = 0; AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); - if (accWrap) { - HyperTextAccessible* text = accWrap->AsHyperText(); - if (!text || !text->IsTextRole()) - return nullptr; + if (!accWrap) + return nullptr; + HyperTextAccessible* text = accWrap->AsHyperText(); + if (!text || !text->IsTextRole()) + return nullptr; + + nsAutoString autoStr; + int32_t startOffset = 0, endOffset = 0; text->TextAtOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr); + *aStartOffset = startOffset; + *aEndOffset = endOffset; + ConvertTexttoAsterisks(accWrap, autoStr); - } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) { - proxy->GetTextAtOffset(aOffset, aBoundaryType, autoStr, &startOffset, - &endOffset); - } - - *aStartOffset = startOffset; - *aEndOffset = endOffset; - - NS_ConvertUTF16toUTF8 cautoStr(autoStr); - return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr; + NS_ConvertUTF16toUTF8 cautoStr(autoStr); + return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr; } static gunichar @@ -238,25 +233,22 @@ getTextBeforeOffsetCB(AtkText *aText, gint aOffset, AtkTextBoundary aBoundaryType, gint *aStartOffset, gint *aEndOffset) { + AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); + if (!accWrap) + return nullptr; + + HyperTextAccessible* text = accWrap->AsHyperText(); + if (!text || !text->IsTextRole()) + return nullptr; + nsAutoString autoStr; int32_t startOffset = 0, endOffset = 0; - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); - if (accWrap) { - HyperTextAccessible* text = accWrap->AsHyperText(); - if (!text || !text->IsTextRole()) - return nullptr; - - text->TextBeforeOffset(aOffset, aBoundaryType, - &startOffset, &endOffset, autoStr); - ConvertTexttoAsterisks(accWrap, autoStr); - } else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) { - proxy->GetTextBeforeOffset(aOffset, aBoundaryType, autoStr, &startOffset, - &endOffset); - } - + text->TextBeforeOffset(aOffset, aBoundaryType, + &startOffset, &endOffset, autoStr); *aStartOffset = startOffset; *aEndOffset = endOffset; + ConvertTexttoAsterisks(accWrap, autoStr); NS_ConvertUTF16toUTF8 cautoStr(autoStr); return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr; } From cb65c5fc0c78c93129672862cde7a29c31af225a Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Tue, 14 Jul 2015 14:36:06 -0700 Subject: [PATCH 65/67] Backed out changeset f4651e7cfb26 (bug 1172516) for mn-e10s orange CLOSED TREE --- accessible/generic/Accessible.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index 118d26585267..75dbcce7f1f8 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -860,9 +860,9 @@ Accessible::HandleAccEvent(AccEvent* aEvent) break; } case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: { - AccCaretMoveEvent* event = downcast_accEvent(aEvent); - ipcDoc->SendCaretMoveEvent(id, event->GetCaretOffset()); - break; + AccCaretMoveEvent* event = downcast_accEvent(aEvent); + ipcDoc->SendEvent(id, event->GetCaretOffset()); + break; } case nsIAccessibleEvent::EVENT_TEXT_INSERTED: case nsIAccessibleEvent::EVENT_TEXT_REMOVED: { From 4415d15e530c84a80a4f5f8ba789d35a5acab776 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Tue, 14 Jul 2015 18:05:35 -0700 Subject: [PATCH 66/67] Bumping gaia.json for 1 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/422a64c4639a Author: Michael Henretty Desc: Revert "Merge pull request #30872 from etiennesegonzac/bug-1179040" This reverts commit 66638d0e65bf58b7f640bcc7bed4a0b23d1356c6, reversing changes made to 7676b68b4d32ed13243eeb719188847121bd5611. --- b2g/config/gaia.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index d07937be85ca..0a1a847cb119 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "46e711792a80e3db03a0bab1d6171154fb90fa57", + "git_revision": "c6ef08964711f461a8e6326eae911789d1ec220c", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "07dbb05bb163e5367d5bab0d4a11305b71a55268", + "revision": "422a64c4639a399fda83fb6ba9af46254a659421", "repo_path": "integration/gaia-central" } From b620afb80fead0f68f5b1efda93a7a6c2fd87ba7 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Tue, 14 Jul 2015 18:07:34 -0700 Subject: [PATCH 67/67] Bumping manifests a=b2g-bump --- b2g/config/aries/sources.xml | 2 +- b2g/config/dolphin/sources.xml | 2 +- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator-kk/sources.xml | 2 +- b2g/config/emulator-l/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/flame-kk/sources.xml | 2 +- b2g/config/nexus-4/sources.xml | 2 +- b2g/config/nexus-5-l/sources.xml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index 9b4d5e2177f6..3209e2b6f62d 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index a3fd16ba3414..2937ab84db81 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 53ec3535e8fd..35b063f1eee4 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 05286a387015..1eb73ea45fb5 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 461f3be73f70..bddec2d3d284 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index e84e35be18bd..9fc52d74216d 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 53ec3535e8fd..35b063f1eee4 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index a5d6ad790d69..f5edfd1cf093 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 8a5896ddc4d7..6bd7133af171 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index 092fcf883c4d..b708c59ee74b 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ - +