From 33f0551ea52e279e703cb6994e46e56bd1c05928 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Fri, 10 Mar 2023 15:32:42 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1172382 - Enable AccessibleCaret on B2G. r=roc (f5c58c2798) - remove duplicated (8823cc4a43) - Bug 1219310 - part 1 - ask the prefs file for its size directly; r=njn (93073cbc5e) - Bug 1219310 - part 2 - keep track of how much pref file we have read; r=njn (6a2a10a8b1) - Bug 1213123 - Make Preferences::SetString accept char16ptr_t instead of char16_t*. r=froydnj (a895a36861) - Bug 1216901 - Make the FasterMake backend reticulate splines when moz.build or jar.mn files are modified. r=mshal (ed4ec93b18) - Bug 1219122 - Move webapprt.ini definition to moz.build. - Add corresponding webpprt files. (da6bc91b5c) - Bug 1219126 - Move greprefs.js definition in moz.build. r=mshal (52f404c935) - code style (1b1e543834) - Bug 1162690 - Remove malformed uri warning in nsURLParser::ParseURL r=mcmanus (8534fcebb7) - Bug 1163028 - URL: stop escaping [ and ] in path r=mcmanus (f2f3deec40) - Bug 1163030 - URL: stop escaping ` in query r=mcmanus (17d6c07640) - Bug 1191423 - Disallow illegal characters in cookies set via HTTP. r=jduell (b1786d140f) - Bug 1210235 - Skip package verification if pref out or no signature. The package would be treated unsigned. r=valentin (63870dd7ef) - Bug 1216062 - Notify OnStartSignedPackagedRequest with package identifier. r=valentin. (81a14af3db) - Bug 1214079 - Doom the package cache if the signature verification failed. r=valentin (83824c2d5d) - Bug 1178448 - Use imported CA in developer mode. r=keeler,valentin (b9cf64b477) - Bug 1216469 - Bypass verification for signed packages from trust origins. r=valentin (a36d0a6d2f) - Bug 1218284 - Match signed packages' with trust origin without suffix. r=valentin (45529dc7df) - Bug 412457 - should unescape hostname first, then perform IDNA r=mcmanus (23ebe47574) - Bug 1217316 - Remove for-each from netwerk/. r=jduell (8d0ca69e9e) - Bug 1208847 - Add telemetry to measure how often secure cookies are set from non-secure origins r=mcmanus (57ecf3651d) - Bug 1165267 - Part 1: Replace appId and inBrowser by originAttributes v2. r=honzab (7710301407) - Bug 1165267 - Fix downgrading issue by restoring appId and inBrowserElement columns v3. r=honzab (3e8b8e4dfb) - Bug 1221049 - Use originAttributes from TabContext. r=kanru (5eaebe3b28) - Bug 1197944 - Change pref so that the http auth dialog is presented for sub resources as well. r=jduell (e3a7e2a1a7) - Bug 1202421 - Rename the network.auth.allow-subresource-auth pref. r=michal (87e29e1fdf) - Bug 1213577 - Use OriginAttributes in nsHttpAuthManager, r=mcmanus (33d0a25ac4) - Bug 961049 - Part 1: Remove unused members and methods; r=baku (0f3e6de06b) --- b2g/app/b2g.js | 3 + config/faster/rules.mk | 16 +- docshell/base/LoadContext.h | 2 +- dom/indexedDB/ActorsChild.cpp | 17 +- dom/indexedDB/ActorsChild.h | 20 - dom/indexedDB/IDBFactory.cpp | 10 - dom/indexedDB/IDBFactory.h | 3 - dom/ipc/ContentParent.cpp | 3 +- dom/ipc/TabParent.cpp | 4 +- ...ediaElementAudioSourceNodeCrossOrigin.html | 2 +- .../test/unit/test_cookies_sync_failure.js | 4 +- modules/libpref/Preferences.cpp | 11 +- modules/libpref/Preferences.h | 2 +- modules/libpref/greprefs.js | 12 + modules/libpref/init/all.js | 17 +- modules/libpref/moz.build | 3 + mozglue/misc/StackWalk.h | 2 +- netwerk/base/nsFileStreams.cpp | 6 +- netwerk/base/nsIPackagedAppUtils.idl | 5 +- netwerk/base/nsIPackagedAppVerifier.idl | 7 +- netwerk/base/nsStandardURL.cpp | 57 ++- netwerk/base/nsStandardURL.h | 2 +- netwerk/base/nsURLParsers.cpp | 2 - netwerk/cookie/CookieServiceParent.cpp | 51 +- netwerk/cookie/CookieServiceParent.h | 8 +- netwerk/cookie/nsCookieService.cpp | 456 ++++++++++++++---- netwerk/cookie/nsCookieService.h | 43 +- netwerk/cookie/nsICookieManager2.idl | 2 +- netwerk/protocol/http/PackagedAppService.cpp | 54 ++- netwerk/protocol/http/PackagedAppUtils.js | 8 +- netwerk/protocol/http/PackagedAppVerifier.cpp | 70 ++- netwerk/protocol/http/PackagedAppVerifier.h | 13 +- netwerk/protocol/http/nsHttpAuthCache.cpp | 96 ++-- netwerk/protocol/http/nsHttpAuthCache.h | 28 +- netwerk/protocol/http/nsHttpAuthManager.cpp | 20 +- .../http/nsHttpChannelAuthProvider.cpp | 55 +-- .../mochitests/signed_web_packaged_app.sjs | 96 +++- .../test_signed_web_packaged_app.html | 2 +- netwerk/test/unit/socks_client_subprocess.js | 2 +- .../test/unit/test_auth_dialog_permission.js | 14 +- netwerk/test/unit/test_auth_jar.js | 8 +- netwerk/test/unit/test_auth_proxy.js | 2 +- netwerk/test/unit/test_authentication.js | 2 +- netwerk/test/unit/test_bug337744.js | 2 +- netwerk/test/unit/test_bug380994.js | 2 +- netwerk/test/unit/test_bug396389.js | 4 +- netwerk/test/unit/test_bug412457.js | 44 ++ netwerk/test/unit/test_cookie_blacklist.js | 16 + .../unit/test_file_partial_inputstream.js | 16 +- netwerk/test/unit/test_gre_resources.js | 2 +- .../test/unit/test_packaged_app_bug1214079.js | 181 +++++++ .../test/unit/test_packaged_app_channel.js | 132 ++++- .../test/unit/test_packaged_app_service.js | 90 +++- netwerk/test/unit/test_packaged_app_utils.js | 2 +- .../test/unit/test_packaged_app_verifier.js | 112 +++-- netwerk/test/unit/test_readline.js | 4 +- netwerk/test/unit/test_socks.js | 4 +- netwerk/test/unit/test_standardurl.js | 42 +- netwerk/test/unit/xpcshell.ini | 4 + .../mozbuild/mozbuild/backend/fastermake.py | 6 + security/apps/AppTrustDomain.cpp | 59 +++ security/apps/AppTrustDomain.h | 6 + security/manager/ssl/nsIX509CertDB.idl | 10 +- .../passwordmgr/test/test_prompt_async.html | 2 +- toolkit/components/telemetry/Histograms.json | 8 + .../extensions/test/xpinstall/browser_auth.js | 2 +- .../prefetch/OfflineCacheUpdateParent.cpp | 6 +- uriloader/prefetch/OfflineCacheUpdateParent.h | 2 +- webapprt/CommandLineHandler.js | 76 +++ webapprt/ContentPermission.js | 118 +++++ webapprt/DirectoryProvider.js | 56 +++ webapprt/DownloadView.jsm | 35 ++ webapprt/Makefile.in | 16 + webapprt/PaymentUIGlue.js | 148 ++++++ webapprt/Startup.jsm | 169 +++++++ webapprt/WebappManager.jsm | 142 ++++++ webapprt/WebappRT.jsm | 143 ++++++ webapprt/components.manifest | 17 + webapprt/defs.mk | 1 + webapprt/jar.mn | 17 + webapprt/moz.build | 59 +++ webapprt/prefs.js | 97 ++++ webapprt/webapprt.ini | 28 ++ xpcom/io/nsEscape.cpp | 22 +- 84 files changed, 2551 insertions(+), 591 deletions(-) create mode 100644 modules/libpref/greprefs.js create mode 100644 netwerk/test/unit/test_bug412457.js create mode 100644 netwerk/test/unit/test_cookie_blacklist.js create mode 100644 netwerk/test/unit/test_packaged_app_bug1214079.js create mode 100644 webapprt/CommandLineHandler.js create mode 100644 webapprt/ContentPermission.js create mode 100644 webapprt/DirectoryProvider.js create mode 100644 webapprt/DownloadView.jsm create mode 100644 webapprt/Makefile.in create mode 100644 webapprt/PaymentUIGlue.js create mode 100644 webapprt/Startup.jsm create mode 100644 webapprt/WebappManager.jsm create mode 100644 webapprt/WebappRT.jsm create mode 100644 webapprt/components.manifest create mode 100644 webapprt/defs.mk create mode 100644 webapprt/jar.mn create mode 100644 webapprt/moz.build create mode 100644 webapprt/prefs.js create mode 100644 webapprt/webapprt.ini diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 3f3593a18f..edf6979f28 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -1056,6 +1056,9 @@ pref("dom.wakelock.enabled", true); // Enable webapps add-ons pref("dom.apps.customization.enabled", true); +// New implementation to unify touch-caret and selection-carets. +pref("layout.accessiblecaret.enabled", true); + // Enable touch caret by default pref("touchcaret.enabled", true); diff --git a/config/faster/rules.mk b/config/faster/rules.mk index 215cde6126..cbf0f2ca27 100644 --- a/config/faster/rules.mk +++ b/config/faster/rules.mk @@ -24,6 +24,8 @@ # python/mozbuild/mozbuild/backend/fastermake.py is the following: # - TOPSRCDIR/TOPOBJDIR, respectively the top source directory and the top # object directory +# - BACKEND, the path to the file the backend will always update when running +# mach build-backend # - PYTHON, the path to the python executable # - ACDEFINES, which contains a set of -Dvar=name to be used during # preprocessing @@ -41,9 +43,7 @@ default: $(addprefix install-,$(INSTALL_MANIFESTS)) # Explicit files to be built for a default build default: $(addprefix $(TOPOBJDIR)/,$(MANIFEST_TARGETS)) -default: $(TOPOBJDIR)/dist/bin/greprefs.js default: $(TOPOBJDIR)/dist/bin/platform.ini -default: $(TOPOBJDIR)/dist/bin/webapprt/webapprt.ini # Targets from the recursive make backend to be built for a default build default: $(TOPOBJDIR)/config/makefiles/xpidl/xpidl @@ -69,12 +69,22 @@ $(TOPOBJDIR)/%: FORCE # fallback $(TOPOBJDIR)/faster/%: ; +# Files under the python virtualenv, which are dependencies of the BACKEND +# file, are not meant to use the fallback either. +$(TOPOBJDIR)/_virtualenv/%: ; + # And files under dist/ are meant to be copied from their first dependency # if there is no other rule. $(TOPOBJDIR)/dist/%: rm -f $@ cp $< $@ +# Refresh backend +$(BACKEND): + cd $(TOPOBJDIR) && $(PYTHON) config.status --backend FasterMake + +$(MAKEFILE_LIST): $(BACKEND) + # Install files using install manifests # # The list of base directories is given in INSTALL_MANIFESTS. The @@ -115,9 +125,7 @@ $(addprefix $(TOPOBJDIR)/,$(MANIFEST_TARGETS)): FORCE # that are not supported by data in moz.build. # Files to build with the recursive backend and simply copy -$(TOPOBJDIR)/dist/bin/greprefs.js: $(TOPOBJDIR)/modules/libpref/greprefs.js $(TOPOBJDIR)/dist/bin/platform.ini: $(TOPOBJDIR)/toolkit/xre/platform.ini -$(TOPOBJDIR)/dist/bin/webapprt/webapprt.ini: $(TOPOBJDIR)/webapprt/webapprt.ini # The xpidl target in config/makefiles/xpidl requires the install manifest for # dist/idl to have been processed. diff --git a/docshell/base/LoadContext.h b/docshell/base/LoadContext.h index e6afc7ccf9..6caabbde05 100644 --- a/docshell/base/LoadContext.h +++ b/docshell/base/LoadContext.h @@ -77,7 +77,7 @@ public: bool aIsContent, bool aUsePrivateBrowsing, bool aUseRemoteTabs, - OriginAttributes& aAttrs) + const OriginAttributes& aAttrs) : mTopFrameElement(do_GetWeakReference(aTopFrameElement)) , mNestedFrameId(0) , mIsContent(aIsContent) diff --git a/dom/indexedDB/ActorsChild.cpp b/dom/indexedDB/ActorsChild.cpp index 842ebdbb26..06994a4539 100644 --- a/dom/indexedDB/ActorsChild.cpp +++ b/dom/indexedDB/ActorsChild.cpp @@ -1101,7 +1101,6 @@ PermissionRequestChildProcessActor::Recv__delete__( BackgroundRequestChildBase::BackgroundRequestChildBase(IDBRequest* aRequest) : mRequest(aRequest) - , mActorDestroyed(false) { MOZ_ASSERT(aRequest); aRequest->AssertIsOnOwningThread(); @@ -1127,15 +1126,6 @@ BackgroundRequestChildBase::AssertIsOnOwningThread() const #endif // DEBUG -void -BackgroundRequestChildBase::NoteActorDestroyed() -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(!mActorDestroyed); - - mActorDestroyed = true; -} - /******************************************************************************* * BackgroundFactoryChild ******************************************************************************/ @@ -1280,8 +1270,7 @@ BackgroundFactoryRequestChild::GetOpenDBRequest() const { AssertIsOnOwningThread(); - IDBRequest* baseRequest = BackgroundRequestChildBase::GetDOMObject(); - return static_cast(baseRequest); + return static_cast(mRequest.get()); } bool @@ -1362,8 +1351,6 @@ BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy) MaybeCollectGarbageOnIPCMessage(); - NoteActorDestroyed(); - if (aWhy != Deletion) { IDBOpenDBRequest* openRequest = GetOpenDBRequest(); if (openRequest) { @@ -2447,8 +2434,6 @@ BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy) MaybeCollectGarbageOnIPCMessage(); - NoteActorDestroyed(); - if (mTransaction) { mTransaction->AssertIsOnOwningThread(); diff --git a/dom/indexedDB/ActorsChild.h b/dom/indexedDB/ActorsChild.h index 1682c61f14..8055a85f8e 100644 --- a/dom/indexedDB/ActorsChild.h +++ b/dom/indexedDB/ActorsChild.h @@ -218,9 +218,6 @@ class BackgroundRequestChildBase protected: RefPtr mRequest; -private: - bool mActorDestroyed; - public: void AssertIsOnOwningThread() const @@ -230,28 +227,11 @@ public: { } #endif - IDBRequest* - GetDOMObject() const - { - AssertIsOnOwningThread(); - return mRequest; - } - - bool - IsActorDestroyed() const - { - AssertIsOnOwningThread(); - return mActorDestroyed; - } - protected: explicit BackgroundRequestChildBase(IDBRequest* aRequest); virtual ~BackgroundRequestChildBase(); - - void - NoteActorDestroyed(); }; class BackgroundFactoryRequestChild final diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index 028fc40bd2..9507133ca8 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -473,16 +473,6 @@ IDBFactory::IsChrome() const return mPrincipalInfo->type() == PrincipalInfo::TSystemPrincipalInfo; } -void -IDBFactory::SetBackgroundActor(BackgroundFactoryChild* aBackgroundActor) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(aBackgroundActor); - MOZ_ASSERT(!mBackgroundActor); - - mBackgroundActor = aBackgroundActor; -} - void IDBFactory::IncrementParentLoggingRequestSerialNumber() { diff --git a/dom/indexedDB/IDBFactory.h b/dom/indexedDB/IDBFactory.h index 5d1d93d30f..f7614de433 100644 --- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.h @@ -121,9 +121,6 @@ public: { } #endif - void - SetBackgroundActor(BackgroundFactoryChild* aBackgroundActor); - void ClearBackgroundActor() { diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 6151037467..b96181709c 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -5225,8 +5225,7 @@ ContentParent::AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI, } RefPtr update = new mozilla::docshell::OfflineCacheUpdateParent( - tabContext.OwnOrContainingAppId(), - tabContext.IsBrowserElement()); + tabContext.OriginAttributesRef()); // Use this reference as the IPDL reference. return update.forget().take(); } diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 86379274f6..c465d44ea8 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -2821,13 +2821,11 @@ TabParent::GetLoadContext() if (mLoadContext) { loadContext = mLoadContext; } else { - // TODO Bug 1191740 - Add OriginAttributes in TabContext - OriginAttributes attrs = OriginAttributes(OwnOrContainingAppId(), IsBrowserElement()); loadContext = new LoadContext(GetOwnerElement(), true /* aIsContent */, mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW, mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW, - attrs); + OriginAttributesRef()); mLoadContext = loadContext; } return loadContext.forget(); diff --git a/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html b/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html index 7d5b4a206d..558a2d4404 100644 --- a/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html +++ b/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html @@ -12,7 +12,7 @@ SimpleTest.waitForExplicitFinish(); // Turn off the authentication dialog blocking for this test. -SpecialPowers.setIntPref("network.auth.allow-subresource-auth", 2) +SpecialPowers.setIntPref("network.auth.subresource-http-auth-allow", 2) var tests = [ // Not the same origin no CORS asked for, should have silence diff --git a/extensions/cookie/test/unit/test_cookies_sync_failure.js b/extensions/cookie/test/unit/test_cookies_sync_failure.js index e9a9303bb8..2de1de628d 100644 --- a/extensions/cookie/test/unit/test_cookies_sync_failure.js +++ b/extensions/cookie/test/unit/test_cookies_sync_failure.js @@ -18,9 +18,9 @@ // c) Schema 3: the 'creationTime' column already exists; or the // 'moz_uniqueid' index already exists. -let COOKIE_DATABASE_SCHEMA_CURRENT = 5; +var COOKIE_DATABASE_SCHEMA_CURRENT = 7; -let test_generator = do_run_test(); +var test_generator = do_run_test(); function run_test() { do_test_pending(); diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp index 40a1981f31..4bee9f3f5e 100644 --- a/modules/libpref/Preferences.cpp +++ b/modules/libpref/Preferences.cpp @@ -1001,8 +1001,8 @@ static nsresult openPrefFile(nsIFile* aFile) if (NS_FAILED(rv)) return rv; - uint64_t fileSize64; - rv = inStr->Available(&fileSize64); + int64_t fileSize64; + rv = aFile->GetFileSize(&fileSize64); if (NS_FAILED(rv)) return rv; NS_ENSURE_TRUE(fileSize64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG); @@ -1018,6 +1018,7 @@ static nsresult openPrefFile(nsIFile* aFile) // Read is not guaranteed to return a buf the size of fileSize, // but usually will. nsresult rv2 = NS_OK; + uint32_t offset = 0; for (;;) { uint32_t amtRead = 0; rv = inStr->Read((char*)fileBuffer, fileSize, &amtRead); @@ -1025,6 +1026,10 @@ static nsresult openPrefFile(nsIFile* aFile) break; if (!PREF_ParseBuf(&ps, fileBuffer, amtRead)) rv2 = NS_ERROR_FILE_CORRUPTED; + offset += amtRead; + if (offset == fileSize) { + break; + } } PREF_FinalizeParseState(&ps); @@ -1515,7 +1520,7 @@ Preferences::SetCString(const char* aPref, const nsACString &aValue) // static nsresult -Preferences::SetString(const char* aPref, const char16_t* aValue) +Preferences::SetString(const char* aPref, const char16ptr_t aValue) { ENSURE_MAIN_PROCESS("Cannot SetString from content process:", aPref); NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE); diff --git a/modules/libpref/Preferences.h b/modules/libpref/Preferences.h index e686da3f1b..82869a357e 100644 --- a/modules/libpref/Preferences.h +++ b/modules/libpref/Preferences.h @@ -202,7 +202,7 @@ public: static nsresult SetFloat(const char* aPref, float aValue); static nsresult SetCString(const char* aPref, const char* aValue); static nsresult SetCString(const char* aPref, const nsACString &aValue); - static nsresult SetString(const char* aPref, const char16_t* aValue); + static nsresult SetString(const char* aPref, const char16ptr_t aValue); static nsresult SetString(const char* aPref, const nsAString &aValue); static nsresult SetComplex(const char* aPref, const nsIID &aType, diff --git a/modules/libpref/greprefs.js b/modules/libpref/greprefs.js new file mode 100644 index 0000000000..5f0b68c70d --- /dev/null +++ b/modules/libpref/greprefs.js @@ -0,0 +1,12 @@ +#include ../../netwerk/base/security-prefs.js +#include init/all.js +#ifdef MOZ_DATA_REPORTING +#include ../../services/datareporting/datareporting-prefs.js +#endif +#ifdef MOZ_SERVICES_HEALTHREPORT +#if MOZ_WIDGET_TOOLKIT == android +#include ../../mobile/android/chrome/content/healthreport-prefs.js +#else +#include ../../services/healthreport/healthreport-prefs.js +#endif +#endif diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 800e6b792a..9ec8fdd8ba 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1773,10 +1773,9 @@ pref("network.http.enforce-framing.soft", true); // See http://www.w3.org/TR/web-packaging/#streamable-package-format pref("network.http.enable-packaged-apps", false); -// Enable this pref to skip verification process. The packaged app -// will be considered signed no matter the package has a valid/invalid -// signature or no signature. -pref("network.http.packaged-apps-developer-mode", false); +// Enable this to bring in the signature verification if the signature exists. +// Set to false if you don't need the signed packaged web app support (i.e. NSec). +pref("network.http.signed-packages.enabled", false); // default values for FTP // in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594, @@ -2120,7 +2119,7 @@ pref("network.generic-ntlm-auth.workstation", "WORKSTATION"); // 1 - allow sub-resources to open HTTP authentication credentials dialogs, // but don't allow it for cross-origin sub-resources // 2 - allow the cross-origin authentication as well. -pref("network.auth.allow-subresource-auth", 1); +pref("network.auth.subresource-http-auth-allow", 2); pref("permissions.default.image", 1); // 1-Accept, 2-Deny, 3-dontAcceptForeign @@ -4625,10 +4624,6 @@ pref("layers.offmainthreadcomposition.frame-rate", -1); pref("layers.async-pan-zoom.enabled", true); #endif -#ifdef MOZ_WIDGET_UIKIT -pref("layers.async-pan-zoom.enabled", true); -#endif - #ifdef XP_MACOSX pref("layers.enable-tiles", true); pref("layers.tile-width", 512); @@ -5143,7 +5138,7 @@ pref("browser.trackingprotection.gethashURL", "https://tracking.services.mozilla // Turn off Spatial navigation by default. pref("snav.enabled", false); -// Turn off touch caret by default. +// Original caret implementation on collapsed selection. pref("touchcaret.enabled", false); // This will inflate the size of the touch caret frame when checking if user @@ -5155,7 +5150,7 @@ pref("touchcaret.inflatesize.threshold", 40); // In milliseconds. (0 means disable this feature) pref("touchcaret.expiration.time", 3000); -// Turn off selection caret by default +// Original caret implementation on non-collapsed selection. pref("selectioncaret.enabled", false); // This will inflate size of selection caret frame when we checking if diff --git a/modules/libpref/moz.build b/modules/libpref/moz.build index 88bb84caa2..1e36ffcfaf 100644 --- a/modules/libpref/moz.build +++ b/modules/libpref/moz.build @@ -46,3 +46,6 @@ DEFINES['MOZ_WIDGET_TOOLKIT'] = CONFIG['MOZ_WIDGET_TOOLKIT'] if CONFIG['GNU_CXX']: CXXFLAGS += ['-Wshadow'] +DIST_FILES += [ + 'greprefs.js', +] diff --git a/mozglue/misc/StackWalk.h b/mozglue/misc/StackWalk.h index 4546f62d8c..4a7ba91284 100644 --- a/mozglue/misc/StackWalk.h +++ b/mozglue/misc/StackWalk.h @@ -153,7 +153,7 @@ FramePointerStackWalk(MozWalkStackCallback aCallback, uint32_t aSkipFrames, uint32_t aMaxFrames, void* aClosure, void** aBp, void* aStackEnd); -} +} // namespace mozilla /** * Initialize the critical sections for this platform so that we can diff --git a/netwerk/base/nsFileStreams.cpp b/netwerk/base/nsFileStreams.cpp index aead7d933f..b665648aad 100644 --- a/netwerk/base/nsFileStreams.cpp +++ b/netwerk/base/nsFileStreams.cpp @@ -277,7 +277,7 @@ nsFileStreamBase::Write(const char *buf, uint32_t count, uint32_t *result) *result = cnt; return NS_OK; } - + nsresult nsFileStreamBase::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval) { @@ -698,7 +698,7 @@ nsPartialFileInputStream::Init(nsIFile* aFile, uint64_t aStart, nsresult rv = nsFileInputStream::Init(aFile, aIOFlags, aPerm, aBehaviorFlags); NS_ENSURE_SUCCESS(rv, rv); - + return nsFileInputStream::Seek(NS_SEEK_SET, mStart); } @@ -833,7 +833,7 @@ NS_IMPL_ISUPPORTS_INHERITED(nsFileOutputStream, nsFileStreamBase, nsIOutputStream, nsIFileOutputStream) - + nsresult nsFileOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) { diff --git a/netwerk/base/nsIPackagedAppUtils.idl b/netwerk/base/nsIPackagedAppUtils.idl index ca78e9a3ec..cc4c05d6f9 100644 --- a/netwerk/base/nsIPackagedAppUtils.idl +++ b/netwerk/base/nsIPackagedAppUtils.idl @@ -24,7 +24,7 @@ interface nsIVerificationCallback; * https://wiki.mozilla.org/FirefoxOS/New_security_model/Packaging */ -[scriptable, uuid(cc245638-6a38-4f70-8d77-21c55aabd636)] +[scriptable, uuid(edf91fee-ef4a-4479-9136-27eb3b7a6312)] interface nsIPackagedAppUtils : nsISupports { /** @@ -37,7 +37,8 @@ interface nsIPackagedAppUtils : nsISupports */ void verifyManifest(in ACString aHeader, in ACString aManifest, - in nsIVerificationCallback aVerifier); + in nsIVerificationCallback aVerifier, + in boolean aDeveloperMode); /** * @aFileName is the name of a resource in the package diff --git a/netwerk/base/nsIPackagedAppVerifier.idl b/netwerk/base/nsIPackagedAppVerifier.idl index 9f616e5e2b..b087e57d87 100644 --- a/netwerk/base/nsIPackagedAppVerifier.idl +++ b/netwerk/base/nsIPackagedAppVerifier.idl @@ -17,11 +17,12 @@ interface nsIPackagedAppVerifierListener; * onStartRequest/onDataAvailable/onStopRequest. * */ -[scriptable, uuid(16419a80-4cc3-11e5-b970-0800200c9a66)] +[scriptable, uuid(37a5c208-0fce-4ad6-8431-aeb904dfe543)] interface nsIPackagedAppVerifier : nsIStreamListener { - // The package origin of either a signed or unsigned package. - readonly attribute ACString packageOrigin; + // The package identifier of the signed package. For a unsigned package, this + // attribute is empty. + readonly attribute ACString packageIdentifier; // Whether this package is signed. readonly attribute boolean isPackageSigned; diff --git a/netwerk/base/nsStandardURL.cpp b/netwerk/base/nsStandardURL.cpp index 4822e52e05..20324121ca 100644 --- a/netwerk/base/nsStandardURL.cpp +++ b/netwerk/base/nsStandardURL.cpp @@ -383,7 +383,7 @@ nsStandardURL::InvalidateCache(bool invalidateCachedFile) mSpecEncoding = eEncoding_Unknown; } -bool +nsresult nsStandardURL::NormalizeIDN(const nsCSubstring &host, nsCString &result) { // If host is ACE, then convert to UTF-8. Else, if host is already UTF-8, @@ -407,16 +407,16 @@ nsStandardURL::NormalizeIDN(const nsCSubstring &host, nsCString &result) } } - if (gIDN && - NS_SUCCEEDED(gIDN->ConvertToDisplayIDN(host, &isASCII, result))) { - if (!isASCII) + result.Truncate(); + nsresult rv = NS_ERROR_UNEXPECTED; + if (gIDN) { + rv = gIDN->ConvertToDisplayIDN(host, &isASCII, result); + if (NS_SUCCEEDED(rv) && !isASCII) { mHostEncoding = eEncoding_UTF8; - - return true; + } } - result.Truncate(); - return false; + return rv; } bool @@ -446,7 +446,9 @@ nsStandardURL::ValidIPv6orHostname(const char *host, uint32_t length) const char *end = host + length; if (end != net_FindCharInSet(host, end, "\t\n\v\f\r #/:?@[\\]")) { - // % is allowed because we don't do hostname percent decoding yet. + // We still allow % because it is in the ID of addons. + // Any percent encoded ASCII characters that are not allowed in the + // hostname are not percent decoded, and will be parsed just fine. return false; } @@ -583,19 +585,22 @@ nsStandardURL::BuildNormalizedSpec(const char *spec) mHostEncoding = eEncoding_ASCII; // Note that we don't disallow URLs without a host - file:, etc if (mHost.mLen > 0) { - const nsCSubstring& tempHost = - Substring(spec + mHost.mPos, spec + mHost.mPos + mHost.mLen); + nsAutoCString tempHost; + NS_UnescapeURL(spec + mHost.mPos, mHost.mLen, esc_AlwaysCopy | esc_Host, tempHost); if (tempHost.Contains('\0')) return NS_ERROR_MALFORMED_URI; // null embedded in hostname if (tempHost.Contains(' ')) return NS_ERROR_MALFORMED_URI; // don't allow spaces in the hostname - if ((useEncHost = NormalizeIDN(tempHost, encHost))) - approxLen += encHost.Length(); - else - approxLen += mHost.mLen; + nsresult rv = NormalizeIDN(tempHost, encHost); + if (NS_FAILED(rv)) { + return rv; + } - if ((useEncHost && !ValidIPv6orHostname(encHost.BeginReading(), encHost.Length())) || - (!useEncHost && !ValidIPv6orHostname(tempHost.BeginReading(), tempHost.Length()))) { + // NormalizeIDN always copies, if the call was successful. + useEncHost = true; + approxLen += encHost.Length(); + + if (!ValidIPv6orHostname(encHost.BeginReading(), encHost.Length())) { return NS_ERROR_MALFORMED_URI; } } @@ -1663,7 +1668,11 @@ nsStandardURL::SetHost(const nsACString &input) FindHostLimit(start, end); - const nsCString flat(Substring(start, end)); + const nsCString unescapedHost(Substring(start, end)); + // Do percent decoding on the the input. + nsAutoCString flat; + NS_UnescapeURL(unescapedHost.BeginReading(), unescapedHost.Length(), + esc_AlwaysCopy | esc_Host, flat); const char *host = flat.get(); LOG(("nsStandardURL::SetHost [host=%s]\n", host)); @@ -1694,12 +1703,14 @@ nsStandardURL::SetHost(const nsACString &input) uint32_t len; nsAutoCString hostBuf; - if (NormalizeIDN(flat, hostBuf)) { - host = hostBuf.get(); - len = hostBuf.Length(); + nsresult rv = NormalizeIDN(flat, hostBuf); + if (NS_FAILED(rv)) { + return rv; } - else - len = flat.Length(); + + // NormalizeIDN always copies if the call was successful + host = hostBuf.get(); + len = hostBuf.Length(); if (!ValidIPv6orHostname(host, len)) { return NS_ERROR_MALFORMED_URI; diff --git a/netwerk/base/nsStandardURL.h b/netwerk/base/nsStandardURL.h index 820e3ffc83..97ca76a72a 100644 --- a/netwerk/base/nsStandardURL.h +++ b/netwerk/base/nsStandardURL.h @@ -178,7 +178,7 @@ private: void InvalidateCache(bool invalidateCachedFile = true); bool ValidIPv6orHostname(const char *host, uint32_t aLen); - bool NormalizeIDN(const nsCSubstring &host, nsCString &result); + nsresult NormalizeIDN(const nsCSubstring &host, nsCString &result); void CoalescePath(netCoalesceFlags coalesceFlag, char *path); uint32_t AppendSegmentToBuf(char *, uint32_t, const char *, diff --git a/netwerk/base/nsURLParsers.cpp b/netwerk/base/nsURLParsers.cpp index 37f3e722c7..a024174f04 100644 --- a/netwerk/base/nsURLParsers.cpp +++ b/netwerk/base/nsURLParsers.cpp @@ -119,7 +119,6 @@ nsBaseURLParser::ParseURL(const char *spec, int32_t specLen, // spec = : // if (!net_IsValidScheme(spec, colon - spec) || (*(colon+1) == ':')) { - NS_WARNING("malformed uri"); return NS_ERROR_MALFORMED_URI; } SET_RESULT(scheme, offset, colon - spec); @@ -577,7 +576,6 @@ nsAuthURLParser::ParseServerInfo(const char *serverinfo, int32_t serverinfoLen, break; case ' ': // hostname must not contain a space - NS_WARNING("malformed hostname"); return NS_ERROR_MALFORMED_URI; } } diff --git a/netwerk/cookie/CookieServiceParent.cpp b/netwerk/cookie/CookieServiceParent.cpp index c8d4cff2b4..9195458199 100644 --- a/netwerk/cookie/CookieServiceParent.cpp +++ b/netwerk/cookie/CookieServiceParent.cpp @@ -27,15 +27,13 @@ namespace { // Ignore failures from this function, as they only affect whether we do or // don't show a dialog box in private browsing mode if the user sets a pref. void -CreateDummyChannel(nsIURI* aHostURI, uint32_t aAppId, bool aInMozBrowser, - bool aIsPrivate, nsIChannel **aChannel) +CreateDummyChannel(nsIURI* aHostURI, OriginAttributes &aAttrs, bool aIsPrivate, + nsIChannel **aChannel) { - MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID); + MOZ_ASSERT(aAttrs.mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID); - // TODO: Bug 1165267 - Use OriginAttributes for nsCookieService - OriginAttributes attrs(aAppId, aInMozBrowser); nsCOMPtr principal = - BasePrincipal::CreateCodebasePrincipal(aHostURI, attrs); + BasePrincipal::CreateCodebasePrincipal(aHostURI, aAttrs); if (!principal) { return; } @@ -66,28 +64,22 @@ namespace net { MOZ_WARN_UNUSED_RESULT bool -CookieServiceParent::GetAppInfoFromParams(const IPC::SerializedLoadContext &aLoadContext, - uint32_t& aAppId, - bool& aIsInBrowserElement, - bool& aIsPrivate) +CookieServiceParent::GetOriginAttributesFromParams(const IPC::SerializedLoadContext &aLoadContext, + OriginAttributes& aAttrs, + bool& aIsPrivate) { - aAppId = NECKO_NO_APP_ID; - aIsInBrowserElement = false; aIsPrivate = false; - OriginAttributes attrs; const char* error = NeckoParent::GetValidatedAppInfo(aLoadContext, Manager()->Manager(), - attrs); + aAttrs); if (error) { - NS_WARNING(nsPrintfCString("CookieServiceParent: GetAppInfoFromParams: " + NS_WARNING(nsPrintfCString("CookieServiceParent: GetOriginAttributesFromParams: " "FATAL error: %s: KILLING CHILD PROCESS\n", error).get()); return false; } - aAppId = attrs.mAppId; - aIsInBrowserElement = attrs.mInBrowser; if (aLoadContext.IsPrivateBitValid()) { aIsPrivate = aLoadContext.mUsePrivateBrowsing; } @@ -133,16 +125,15 @@ CookieServiceParent::RecvGetCookieString(const URIParams& aHost, if (!hostURI) return false; - uint32_t appId; - bool isInBrowserElement, isPrivate; - bool valid = GetAppInfoFromParams(aLoadContext, appId, - isInBrowserElement, isPrivate); + OriginAttributes attrs; + bool isPrivate; + bool valid = GetOriginAttributesFromParams(aLoadContext, attrs, isPrivate); if (!valid) { return false; } - mCookieService->GetCookieStringInternal(hostURI, aIsForeign, aFromHttp, appId, - isInBrowserElement, isPrivate, *aResult); + mCookieService->GetCookieStringInternal(hostURI, aIsForeign, aFromHttp, attrs, + isPrivate, *aResult); return true; } @@ -164,10 +155,9 @@ CookieServiceParent::RecvSetCookieString(const URIParams& aHost, if (!hostURI) return false; - uint32_t appId; - bool isInBrowserElement, isPrivate; - bool valid = GetAppInfoFromParams(aLoadContext, appId, - isInBrowserElement, isPrivate); + OriginAttributes attrs; + bool isPrivate; + bool valid = GetOriginAttributesFromParams(aLoadContext, attrs, isPrivate); if (!valid) { return false; } @@ -180,14 +170,13 @@ CookieServiceParent::RecvSetCookieString(const URIParams& aHost, // with aIsForeign before we have to worry about nsCookiePermission trying // to use the channel to inspect it. nsCOMPtr dummyChannel; - CreateDummyChannel(hostURI, appId, isInBrowserElement, - isPrivate, getter_AddRefs(dummyChannel)); + CreateDummyChannel(hostURI, attrs, isPrivate, getter_AddRefs(dummyChannel)); // NB: dummyChannel could be null if something failed in CreateDummyChannel. nsDependentCString cookieString(aCookieString, 0); mCookieService->SetCookieStringInternal(hostURI, aIsForeign, cookieString, - aServerTime, aFromHttp, appId, - isInBrowserElement, isPrivate, dummyChannel); + aServerTime, aFromHttp, attrs, + isPrivate, dummyChannel); return true; } diff --git a/netwerk/cookie/CookieServiceParent.h b/netwerk/cookie/CookieServiceParent.h index 43c77d4246..cf71f9f5d7 100644 --- a/netwerk/cookie/CookieServiceParent.h +++ b/netwerk/cookie/CookieServiceParent.h @@ -10,6 +10,7 @@ #include "SerializedLoadContext.h" class nsCookieService; +namespace mozilla { class OriginAttributes; } namespace mozilla { namespace net { @@ -22,10 +23,9 @@ public: protected: MOZ_WARN_UNUSED_RESULT bool - GetAppInfoFromParams(const IPC::SerializedLoadContext &aLoadContext, - uint32_t& aAppId, - bool& aIsInBrowserElement, - bool& aIsPrivate); + GetOriginAttributesFromParams(const IPC::SerializedLoadContext &aLoadContext, + OriginAttributes& aAttrs, + bool& aIsPrivate); virtual void ActorDestroy(ActorDestroyReason aWhy) override; diff --git a/netwerk/cookie/nsCookieService.cpp b/netwerk/cookie/nsCookieService.cpp index 9db399ae05..bdfb29f7cf 100644 --- a/netwerk/cookie/nsCookieService.cpp +++ b/netwerk/cookie/nsCookieService.cpp @@ -50,6 +50,7 @@ #include "mozIApplication.h" #include "mozIApplicationClearPrivateDataParams.h" #include "nsIConsoleService.h" +#include "nsVariant.h" using namespace mozilla; using namespace mozilla::net; @@ -59,7 +60,7 @@ using namespace mozilla::net; // on content processes (see bug 777620), change to use the appropriate app // namespace. For now those IDLs aren't supported on child processes. #define DEFAULT_APP_KEY(baseDomain) \ - nsCookieKey(baseDomain, NECKO_NO_APP_ID, false) + nsCookieKey(baseDomain, OriginAttributes()) /****************************************************************************** * nsCookieService impl: @@ -73,7 +74,7 @@ static nsCookieService *gCookieService; #define HTTP_ONLY_PREFIX "#HttpOnly_" #define COOKIES_FILE "cookies.sqlite" -#define COOKIES_SCHEMA_VERSION 5 +#define COOKIES_SCHEMA_VERSION 7 // parameter indexes; see EnsureReadDomain, EnsureReadComplete and // ReadCookieDBListener::HandleResult @@ -87,8 +88,7 @@ static nsCookieService *gCookieService; #define IDX_SECURE 7 #define IDX_HTTPONLY 8 #define IDX_BASE_DOMAIN 9 -#define IDX_APP_ID 10 -#define IDX_BROWSER_ELEM 11 +#define IDX_ORIGIN_ATTRIBUTES 10 static const int64_t kCookiePurgeAge = int64_t(30 * 24 * 60 * 60) * PR_USEC_PER_SEC; // 30 days in microseconds @@ -485,8 +485,11 @@ public: CookieDomainTuple *tuple = mDBState->hostArray.AppendElement(); row->GetUTF8String(IDX_BASE_DOMAIN, tuple->key.mBaseDomain); - tuple->key.mAppId = static_cast(row->AsInt32(IDX_APP_ID)); - tuple->key.mInBrowserElement = static_cast(row->AsInt32(IDX_BROWSER_ELEM)); + + nsAutoCString suffix; + row->GetUTF8String(IDX_ORIGIN_ATTRIBUTES, suffix); + tuple->key.mOriginAttributes.PopulateFromSuffix(suffix); + tuple->cookie = gCookieService->GetCookieFromRow(row); } @@ -564,20 +567,21 @@ public: // nsIObserver implementation. NS_IMETHODIMP - Observe(nsISupports *aSubject, const char *aTopic, const char16_t *data) override + Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) override { - MOZ_ASSERT(!nsCRT::strcmp(aTopic, TOPIC_WEB_APP_CLEAR_DATA)); + MOZ_ASSERT(!nsCRT::strcmp(aTopic, TOPIC_CLEAR_ORIGIN_DATA)); - uint32_t appId = NECKO_UNKNOWN_APP_ID; - bool browserOnly = false; - nsresult rv = NS_GetAppInfoFromClearDataNotification(aSubject, &appId, - &browserOnly); - NS_ENSURE_SUCCESS(rv, rv); + MOZ_ASSERT(XRE_IsParentProcess()); + OriginAttributes attrs; + MOZ_ALWAYS_TRUE(attrs.Init(nsDependentString(aData))); nsCOMPtr cookieManager = do_GetService(NS_COOKIEMANAGER_CONTRACTID); MOZ_ASSERT(cookieManager); - return cookieManager->RemoveCookiesForApp(appId, browserOnly); + + // TODO: We should add a new interface RemoveCookiesForOriginAttributes in + // nsICookieManager2 and use it instead of RemoveCookiesForApp. + return cookieManager->RemoveCookiesForApp(attrs.mAppId, attrs.mInBrowser); } }; @@ -677,7 +681,7 @@ nsCookieService::AppClearDataObserverInit() { nsCOMPtr observerService = services::GetObserverService(); nsCOMPtr obs = new AppClearDataObserver(); - observerService->AddObserver(obs, TOPIC_WEB_APP_CLEAR_DATA, + observerService->AddObserver(obs, TOPIC_CLEAR_ORIGIN_DATA, /* holdsWeak= */ false); } @@ -802,6 +806,108 @@ nsCookieService::InitDBStates() } } +namespace { + +class ConvertAppIdToOriginAttrsSQLFunction final : public mozIStorageFunction +{ + ~ConvertAppIdToOriginAttrsSQLFunction() {} + + NS_DECL_ISUPPORTS + NS_DECL_MOZISTORAGEFUNCTION +}; + +NS_IMPL_ISUPPORTS(ConvertAppIdToOriginAttrsSQLFunction, mozIStorageFunction); + +NS_IMETHODIMP +ConvertAppIdToOriginAttrsSQLFunction::OnFunctionCall( + mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult) +{ + nsresult rv; + int32_t appId, inBrowser; + + rv = aFunctionArguments->GetInt32(0, &appId); + NS_ENSURE_SUCCESS(rv, rv); + rv = aFunctionArguments->GetInt32(1, &inBrowser); + NS_ENSURE_SUCCESS(rv, rv); + + // Create an originAttributes object by appId and inBrowserElemnt. + // Then create the originSuffix string from this object. + OriginAttributes attrs(appId, (inBrowser ? 1 : 0)); + nsAutoCString suffix; + attrs.CreateSuffix(suffix); + + RefPtr outVar(new nsVariant()); + rv = outVar->SetAsAUTF8String(suffix); + NS_ENSURE_SUCCESS(rv, rv); + + outVar.forget(aResult); + return NS_OK; +} + +class SetAppIdFromOriginAttributesSQLFunction final : public mozIStorageFunction +{ + ~SetAppIdFromOriginAttributesSQLFunction() {} + + NS_DECL_ISUPPORTS + NS_DECL_MOZISTORAGEFUNCTION +}; + +NS_IMPL_ISUPPORTS(SetAppIdFromOriginAttributesSQLFunction, mozIStorageFunction); + +NS_IMETHODIMP +SetAppIdFromOriginAttributesSQLFunction::OnFunctionCall( + mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult) +{ + nsresult rv; + nsAutoCString suffix; + OriginAttributes attrs; + + rv = aFunctionArguments->GetUTF8String(0, suffix); + NS_ENSURE_SUCCESS(rv, rv); + attrs.PopulateFromSuffix(suffix); + + RefPtr outVar(new nsVariant()); + rv = outVar->SetAsInt32(attrs.mAppId); + NS_ENSURE_SUCCESS(rv, rv); + + outVar.forget(aResult); + return NS_OK; +} + +class SetInBrowserFromOriginAttributesSQLFunction final : + public mozIStorageFunction +{ + ~SetInBrowserFromOriginAttributesSQLFunction() {} + + NS_DECL_ISUPPORTS + NS_DECL_MOZISTORAGEFUNCTION +}; + +NS_IMPL_ISUPPORTS(SetInBrowserFromOriginAttributesSQLFunction, + mozIStorageFunction); + +NS_IMETHODIMP +SetInBrowserFromOriginAttributesSQLFunction::OnFunctionCall( + mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult) +{ + nsresult rv; + nsAutoCString suffix; + OriginAttributes attrs; + + rv = aFunctionArguments->GetUTF8String(0, suffix); + NS_ENSURE_SUCCESS(rv, rv); + attrs.PopulateFromSuffix(suffix); + + RefPtr outVar(new nsVariant()); + rv = outVar->SetAsInt32(attrs.mInBrowser); + NS_ENSURE_SUCCESS(rv, rv); + + outVar.forget(aResult); + return NS_OK; +} + +} // namespace + /* Attempt to open and read the database. If 'aRecreateDB' is true, try to * move the existing database file out of the way and create a new one. * @@ -1062,7 +1168,7 @@ nsCookieService::TryInitDB(bool aRecreateDB) NS_ENSURE_SUCCESS(rv, RESULT_RETRY); // Create new table (with new fields and new unique constraint) - rv = CreateTable(); + rv = CreateTableForSchemaVersion5(); NS_ENSURE_SUCCESS(rv, RESULT_RETRY); // Copy data from old table, using appId/inBrowser=0 for existing rows @@ -1080,9 +1186,127 @@ nsCookieService::TryInitDB(bool aRecreateDB) "DROP TABLE moz_cookies_old")); NS_ENSURE_SUCCESS(rv, RESULT_RETRY); - COOKIE_LOGSTRING(LogLevel::Debug, + COOKIE_LOGSTRING(LogLevel::Debug, ("Upgraded database to schema version 5")); } + // Fall through to the next upgrade. + + case 5: + { + // Change in the version: Replace the columns |appId| and + // |inBrowserElement| by a single column |originAttributes|. + // + // Why we made this change: FxOS new security model (NSec) encapsulates + // "appId/inBrowser" in nsIPrincipal::originAttributes to make it easier + // to modify the contents of this structure in the future. + // + // We do the migration in several steps: + // 1. Rename the old table. + // 2. Create a new table. + // 3. Copy data from the old table to the new table; convert appId and + // inBrowserElement to originAttributes in the meantime. + + // Rename existing table. + rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE moz_cookies RENAME TO moz_cookies_old")); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + // Drop existing index (CreateTable will create new one for new table). + rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX moz_basedomain")); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + // Create new table with new fields and new unique constraint. + rv = CreateTable(); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + // Copy data from old table without the two deprecated columns appId and + // inBrowserElement. + nsCOMPtr + convertToOriginAttrs(new ConvertAppIdToOriginAttrsSQLFunction()); + NS_ENSURE_TRUE(convertToOriginAttrs, RESULT_RETRY); + + NS_NAMED_LITERAL_CSTRING(convertToOriginAttrsName, + "CONVERT_TO_ORIGIN_ATTRIBUTES"); + + rv = mDefaultDBState->dbConn->CreateFunction(convertToOriginAttrsName, + 2, convertToOriginAttrs); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO moz_cookies " + "(baseDomain, originAttributes, name, value, host, path, expiry," + " lastAccessed, creationTime, isSecure, isHttpOnly) " + "SELECT baseDomain, " + " CONVERT_TO_ORIGIN_ATTRIBUTES(appId, inBrowserElement)," + " name, value, host, path, expiry, lastAccessed, creationTime, " + " isSecure, isHttpOnly " + "FROM moz_cookies_old")); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + rv = mDefaultDBState->dbConn->RemoveFunction(convertToOriginAttrsName); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + // Drop old table + rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE moz_cookies_old")); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + COOKIE_LOGSTRING(LogLevel::Debug, + ("Upgraded database to schema version 6")); + } + + case 6: + { + // We made a mistake in schema version 6. We cannot remove expected + // columns of any version (checked in the default case) from cookie + // database, because doing this would destroy the possibility of + // downgrading database. + // + // This version simply restores appId and inBrowserElement columns in + // order to fix downgrading issue even though these two columns are no + // longer used in the latest schema. + rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE moz_cookies ADD appId INTEGER DEFAULT 0;")); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE moz_cookies ADD inBrowserElement INTEGER DEFAULT 0;")); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + // Compute and populate the values of appId and inBrwoserElement from + // originAttributes. + nsCOMPtr + setAppId(new SetAppIdFromOriginAttributesSQLFunction()); + NS_ENSURE_TRUE(setAppId, RESULT_RETRY); + + NS_NAMED_LITERAL_CSTRING(setAppIdName, "SET_APP_ID"); + + rv = mDefaultDBState->dbConn->CreateFunction(setAppIdName, 1, setAppId); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + nsCOMPtr + setInBrowser(new SetInBrowserFromOriginAttributesSQLFunction()); + NS_ENSURE_TRUE(setInBrowser, RESULT_RETRY); + + NS_NAMED_LITERAL_CSTRING(setInBrowserName, "SET_IN_BROWSER"); + + rv = mDefaultDBState->dbConn->CreateFunction(setInBrowserName, 1, + setInBrowser); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE moz_cookies SET appId = SET_APP_ID(originAttributes), " + "inBrowserElement = SET_IN_BROWSER(originAttributes);" + )); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + rv = mDefaultDBState->dbConn->RemoveFunction(setAppIdName); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + + rv = mDefaultDBState->dbConn->RemoveFunction(setInBrowserName); + NS_ENSURE_SUCCESS(rv, RESULT_RETRY); + } // No more upgrades. Update the schema version. rv = mDefaultDBState->dbConn->SetSchemaVersion(COOKIES_SCHEMA_VERSION); @@ -1119,8 +1343,7 @@ nsCookieService::TryInitDB(bool aRecreateDB) "SELECT " "id, " "baseDomain, " - "appId, " - "inBrowserElement, " + "originAttributes, " "name, " "value, " "host, " @@ -1161,8 +1384,7 @@ nsCookieService::TryInitDB(bool aRecreateDB) rv = mDefaultDBState->dbConn->CreateAsyncStatement(NS_LITERAL_CSTRING( "INSERT INTO moz_cookies (" "baseDomain, " - "appId, " - "inBrowserElement, " + "originAttributes, " "name, " "value, " "host, " @@ -1174,8 +1396,7 @@ nsCookieService::TryInitDB(bool aRecreateDB) "isHttpOnly" ") VALUES (" ":baseDomain, " - ":appId, " - ":inBrowserElement, " + ":originAttributes, " ":name, " ":value, " ":host, " @@ -1236,7 +1457,43 @@ nsCookieService::CreateTable() COOKIES_SCHEMA_VERSION); if (NS_FAILED(rv)) return rv; - // Create the table. We default appId/inBrowserElement to 0: this is so if + // Create the table. + // We default originAttributes to empty string: this is so if users revert to + // an older Firefox version that doesn't know about this field, any cookies + // set will still work once they upgrade back. + rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE moz_cookies (" + "id INTEGER PRIMARY KEY, " + "baseDomain TEXT, " + "originAttributes TEXT NOT NULL DEFAULT '', " + "name TEXT, " + "value TEXT, " + "host TEXT, " + "path TEXT, " + "expiry INTEGER, " + "lastAccessed INTEGER, " + "creationTime INTEGER, " + "isSecure INTEGER, " + "isHttpOnly INTEGER, " + "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)" + ")")); + if (NS_FAILED(rv)) return rv; + + // Create an index on baseDomain. + return mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain, " + "originAttributes)")); +} + +// Sets the schema version and creates the moz_cookies table. +nsresult +nsCookieService::CreateTableForSchemaVersion5() +{ + // Set the schema version, before creating the table. + nsresult rv = mDefaultDBState->dbConn->SetSchemaVersion(5); + if (NS_FAILED(rv)) return rv; + + // Create the table. We default appId/inBrowserElement to 0: this is so if // users revert to an older Firefox version that doesn't know about these // fields, any cookies set will still work once they upgrade back. rv = mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( @@ -1590,18 +1847,17 @@ nsCookieService::GetCookieStringCommon(nsIURI *aHostURI, bool isForeign = true; mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign); - // Get app info, if channel is present. Else assume default namespace. - uint32_t appId = NECKO_NO_APP_ID; - bool inBrowserElement = false; + // Get originAttributes. + OriginAttributes attrs; if (aChannel) { - NS_GetAppInfo(aChannel, &appId, &inBrowserElement); + NS_GetOriginAttributes(aChannel, attrs); } bool isPrivate = aChannel && NS_UsePrivateBrowsing(aChannel); nsAutoCString result; - GetCookieStringInternal(aHostURI, isForeign, aHttpBound, appId, - inBrowserElement, isPrivate, result); + GetCookieStringInternal(aHostURI, isForeign, aHttpBound, attrs, + isPrivate, result); *aCookie = result.IsEmpty() ? nullptr : ToNewCString(result); return NS_OK; } @@ -1664,11 +1920,10 @@ nsCookieService::SetCookieStringCommon(nsIURI *aHostURI, bool isForeign = true; mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign); - // Get app info, if channel is present. Else assume default namespace. - uint32_t appId = NECKO_NO_APP_ID; - bool inBrowserElement = false; + // Get originAttributes. + OriginAttributes attrs; if (aChannel) { - NS_GetAppInfo(aChannel, &appId, &inBrowserElement); + NS_GetOriginAttributes(aChannel, attrs); } bool isPrivate = aChannel && NS_UsePrivateBrowsing(aChannel); @@ -1676,21 +1931,20 @@ nsCookieService::SetCookieStringCommon(nsIURI *aHostURI, nsDependentCString cookieString(aCookieHeader); nsDependentCString serverTime(aServerTime ? aServerTime : ""); SetCookieStringInternal(aHostURI, isForeign, cookieString, - serverTime, aFromHttp, appId, inBrowserElement, + serverTime, aFromHttp, attrs, isPrivate, aChannel); return NS_OK; } void -nsCookieService::SetCookieStringInternal(nsIURI *aHostURI, - bool aIsForeign, - nsDependentCString &aCookieHeader, - const nsCString &aServerTime, - bool aFromHttp, - uint32_t aAppId, - bool aInBrowserElement, - bool aIsPrivate, - nsIChannel *aChannel) +nsCookieService::SetCookieStringInternal(nsIURI *aHostURI, + bool aIsForeign, + nsDependentCString &aCookieHeader, + const nsCString &aServerTime, + bool aFromHttp, + const OriginAttributes &aOriginAttrs, + bool aIsPrivate, + nsIChannel *aChannel) { NS_ASSERTION(aHostURI, "null host!"); @@ -1711,12 +1965,12 @@ nsCookieService::SetCookieStringInternal(nsIURI *aHostURI, nsAutoCString baseDomain; nsresult rv = GetBaseDomain(aHostURI, baseDomain, requireHostMatch); if (NS_FAILED(rv)) { - COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, + COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "couldn't get base domain from URI"); return; } - nsCookieKey key(baseDomain, aAppId, aInBrowserElement); + nsCookieKey key(baseDomain, aOriginAttrs); // check default prefs CookieStatus cookieStatus = CheckPrefs(aHostURI, aIsForeign, requireHostMatch, @@ -1999,9 +2253,9 @@ nsCookieService::Add(const nsACString &aHost, nsresult -nsCookieService::Remove(const nsACString& aHost, uint32_t aAppId, - bool aInBrowserElement, const nsACString& aName, - const nsACString& aPath, bool aBlocked) +nsCookieService::Remove(const nsACString& aHost, const OriginAttributes& aAttrs, + const nsACString& aName, const nsACString& aPath, + bool aBlocked) { if (!mDBState) { NS_WARNING("No DBState! Profile already closed?"); @@ -2019,7 +2273,7 @@ nsCookieService::Remove(const nsACString& aHost, uint32_t aAppId, nsListIter matchIter; RefPtr cookie; - if (FindCookie(nsCookieKey(baseDomain, aAppId, aInBrowserElement), + if (FindCookie(nsCookieKey(baseDomain, aAttrs), host, PromiseFlatCString(aName), PromiseFlatCString(aPath), @@ -2057,7 +2311,8 @@ nsCookieService::Remove(const nsACString &aHost, const nsACString &aPath, bool aBlocked) { - return Remove(aHost, NECKO_NO_APP_ID, false, aName, aPath, aBlocked); + OriginAttributes attrs; + return Remove(aHost, attrs, aName, aPath, aBlocked); } /****************************************************************************** @@ -2084,8 +2339,7 @@ nsCookieService::Read() "isSecure, " "isHttpOnly, " "baseDomain, " - "appId, " - "inBrowserElement " + "originAttributes " "FROM moz_cookies " "WHERE baseDomain NOTNULL"), getter_AddRefs(stmtRead)); NS_ENSURE_SUCCESS(rv, RESULT_RETRY); @@ -2257,8 +2511,7 @@ nsCookieService::EnsureReadDomain(const nsCookieKey &aKey) "isHttpOnly " "FROM moz_cookies " "WHERE baseDomain = :baseDomain " - " AND appId = :appId " - " AND inBrowserElement = :inBrowserElement"), + " AND originAttributes = :originAttributes"), getter_AddRefs(mDefaultDBState->stmtReadDomain)); if (NS_FAILED(rv)) { @@ -2278,13 +2531,12 @@ nsCookieService::EnsureReadDomain(const nsCookieKey &aKey) rv = mDefaultDBState->stmtReadDomain->BindUTF8StringByName( NS_LITERAL_CSTRING("baseDomain"), aKey.mBaseDomain); NS_ASSERT_SUCCESS(rv); - rv = mDefaultDBState->stmtReadDomain->BindInt32ByName( - NS_LITERAL_CSTRING("appId"), aKey.mAppId); - NS_ASSERT_SUCCESS(rv); - rv = mDefaultDBState->stmtReadDomain->BindInt32ByName( - NS_LITERAL_CSTRING("inBrowserElement"), aKey.mInBrowserElement ? 1 : 0); - NS_ASSERT_SUCCESS(rv); + nsAutoCString suffix; + aKey.mOriginAttributes.CreateSuffix(suffix); + rv = mDefaultDBState->stmtReadDomain->BindUTF8StringByName( + NS_LITERAL_CSTRING("originAttributes"), suffix); + NS_ASSERT_SUCCESS(rv); bool hasResult; nsCString name, value, host, path; @@ -2317,8 +2569,8 @@ nsCookieService::EnsureReadDomain(const nsCookieKey &aKey) COOKIE_LOGSTRING(LogLevel::Debug, ("EnsureReadDomain(): %ld cookies read for base domain %s, " - " appId=%u, inBrowser=%d", array.Length(), aKey.mBaseDomain.get(), - (unsigned)aKey.mAppId, (int)aKey.mInBrowserElement)); + " originAttributes = %s", array.Length(), aKey.mBaseDomain.get(), + suffix.get())); } void @@ -2349,8 +2601,7 @@ nsCookieService::EnsureReadComplete() "isSecure, " "isHttpOnly, " "baseDomain, " - "appId, " - "inBrowserElement " + "originAttributes " "FROM moz_cookies " "WHERE baseDomain NOTNULL"), getter_AddRefs(stmt)); @@ -2364,8 +2615,7 @@ nsCookieService::EnsureReadComplete() } nsCString baseDomain, name, value, host, path; - uint32_t appId; - bool inBrowserElement, hasResult; + bool hasResult; nsAutoTArray array; while (1) { rv = stmt->ExecuteStep(&hasResult); @@ -2383,9 +2633,13 @@ nsCookieService::EnsureReadComplete() // Make sure we haven't already read the data. stmt->GetUTF8String(IDX_BASE_DOMAIN, baseDomain); - appId = static_cast(stmt->AsInt32(IDX_APP_ID)); - inBrowserElement = static_cast(stmt->AsInt32(IDX_BROWSER_ELEM)); - nsCookieKey key(baseDomain, appId, inBrowserElement); + + nsAutoCString suffix; + OriginAttributes attrs; + stmt->GetUTF8String(IDX_ORIGIN_ATTRIBUTES, suffix); + attrs.PopulateFromSuffix(suffix); + + nsCookieKey key(baseDomain, attrs); if (mDefaultDBState->readSet.GetEntry(key)) continue; @@ -2529,7 +2783,8 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile) if (NS_FAILED(rv)) continue; - // pre-existing cookies have appId=0, inBrowser=false + // pre-existing cookies have appId=0, inBrowser=false set by default + // constructor of OriginAttributes(). nsCookieKey key = DEFAULT_APP_KEY(baseDomain); // Create a new nsCookie and assign the data. We don't know the cookie @@ -2620,8 +2875,7 @@ void nsCookieService::GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, - uint32_t aAppId, - bool aInBrowserElement, + const OriginAttributes aOriginAttrs, bool aIsPrivate, nsCString &aCookieString) { @@ -2678,7 +2932,7 @@ nsCookieService::GetCookieStringInternal(nsIURI *aHostURI, int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC; bool stale = false; - nsCookieKey key(baseDomain, aAppId, aInBrowserElement); + nsCookieKey key(baseDomain, aOriginAttrs); EnsureReadDomain(key); // perform the hash lookup @@ -2878,28 +3132,20 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI, return newCookie; } - // Reject cookie if value contains an RFC 6265 disallowed character. - // See RFC 6265 section 4.1.1 - // XXX: For now we allow for web compatibility (see issue #357): - // 0x20 (Space) - // 0x22 (DQUOTE) - // 0x2C (Comma) - // 0x5C (Backslash) - // - // FIXME: Before removing DQUOTE from the exceptions list: - // DQUOTE *cookie-octet DQUOTE is permitted and would fail if just removed. - // This needs better checking for first and last character allowing - // DQUOTE but not in the actual value. - // - // This only applies to cookies set via the Set-Cookie header, since - // document.cookie is defined to be UTF-8. - const char illegalCharacters[] = { - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, - 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, /* 0x20, 0x22, */ - /* 0x2C, */ 0x3B, /* 0x5C, */ 0x7F, 0x00 }; + // reject cookie if value contains an RFC 6265 disallowed character - see + // https://bugzilla.mozilla.org/show_bug.cgi?id=1191423 + // NOTE: this is not the full set of characters disallowed by 6265 - notably + // 0x09, 0x20, 0x22, 0x2C, 0x5C, and 0x7F are missing from this list. This is + // for parity with Chrome. This only applies to cookies set via the Set-Cookie + // header, as document.cookie is defined to be UTF-8. Hooray for + // symmetry! + const char illegalCharacters[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, + 0x1E, 0x1F, 0x3B, 0x00 }; if (aFromHttp && (cookieAttributes.value.FindCharInSet(illegalCharacters, 0) != -1)) { - COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "illegal character in cookie"); + COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "invalid value character"); return newCookie; } @@ -3977,8 +4223,8 @@ nsCookieService::GetCookiesForApp(uint32_t aAppId, bool aOnlyBrowserElement, for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) { nsCookieEntry* entry = iter.Get(); - if (entry->mAppId != aAppId || - (aOnlyBrowserElement && !entry->mInBrowserElement)) { + if (entry->mOriginAttributes.mAppId != aAppId || + (aOnlyBrowserElement && !entry->mOriginAttributes.mInBrowser)) { continue; } @@ -4030,9 +4276,11 @@ nsCookieService::RemoveCookiesForApp(uint32_t aAppId, bool aOnlyBrowserElement) // // NOTE: we could make this better by getting nsCookieEntry objects instead // of plain nsICookie. - Remove(host, aAppId, true, name, path, false); + OriginAttributes attrs(aAppId, true); + Remove(host, attrs, name, path, false); if (!aOnlyBrowserElement) { - Remove(host, aAppId, false, name, path, false); + attrs.mInBrowser = false; + Remove(host, attrs, name, path, false); } } @@ -4146,12 +4394,10 @@ bindCookieParameters(mozIStorageBindingParamsArray *aParamsArray, aKey.mBaseDomain); NS_ASSERT_SUCCESS(rv); - rv = params->BindInt32ByName(NS_LITERAL_CSTRING("appId"), - aKey.mAppId); - NS_ASSERT_SUCCESS(rv); - - rv = params->BindInt32ByName(NS_LITERAL_CSTRING("inBrowserElement"), - aKey.mInBrowserElement ? 1 : 0); + nsAutoCString suffix; + aKey.mOriginAttributes.CreateSuffix(suffix); + rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("originAttributes"), + suffix); NS_ASSERT_SUCCESS(rv); rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("name"), diff --git a/netwerk/cookie/nsCookieService.h b/netwerk/cookie/nsCookieService.h index b644dadaf9..a6e7a306f3 100644 --- a/netwerk/cookie/nsCookieService.h +++ b/netwerk/cookie/nsCookieService.h @@ -25,10 +25,15 @@ #include "mozIStorageRow.h" #include "mozIStorageCompletionCallback.h" #include "mozIStorageStatementCallback.h" +#include "mozIStorageFunction.h" +#include "nsIVariant.h" #include "nsIFile.h" +#include "mozilla/BasePrincipal.h" #include "mozilla/MemoryReporting.h" +using mozilla::OriginAttributes; + class nsICookiePermission; class nsIEffectiveTLDService; class nsIIDNService; @@ -60,22 +65,19 @@ public: nsCookieKey() {} - nsCookieKey(const nsCString &baseDomain, uint32_t appId, bool inBrowser) + nsCookieKey(const nsCString &baseDomain, const OriginAttributes &attrs) : mBaseDomain(baseDomain) - , mAppId(appId) - , mInBrowserElement(inBrowser) + , mOriginAttributes(attrs) {} explicit nsCookieKey(KeyTypePointer other) : mBaseDomain(other->mBaseDomain) - , mAppId(other->mAppId) - , mInBrowserElement(other->mInBrowserElement) + , mOriginAttributes(other->mOriginAttributes) {} nsCookieKey(KeyType other) : mBaseDomain(other.mBaseDomain) - , mAppId(other.mAppId) - , mInBrowserElement(other.mInBrowserElement) + , mOriginAttributes(other.mOriginAttributes) {} ~nsCookieKey() @@ -84,8 +86,7 @@ public: bool KeyEquals(KeyTypePointer other) const { return mBaseDomain == other->mBaseDomain && - mAppId == other->mAppId && - mInBrowserElement == other->mInBrowserElement; + mOriginAttributes == other->mOriginAttributes; } static KeyTypePointer KeyToPointer(KeyType aKey) @@ -98,9 +99,9 @@ public: // TODO: more efficient way to generate hash? nsAutoCString temp(aKey->mBaseDomain); temp.Append('#'); - temp.Append(aKey->mAppId); - temp.Append('#'); - temp.Append(aKey->mInBrowserElement ? 1 : 0); + nsAutoCString suffix; + aKey->mOriginAttributes.CreateSuffix(suffix); + temp.Append(suffix); return mozilla::HashString(temp); } @@ -108,9 +109,8 @@ public: enum { ALLOW_MEMMOVE = true }; - nsCString mBaseDomain; - uint32_t mAppId; - bool mInBrowserElement; + nsCString mBaseDomain; + OriginAttributes mOriginAttributes; }; // Inherit from nsCookieKey so this can be stored in nsTHashTable @@ -274,6 +274,7 @@ class nsCookieService final : public nsICookieService void InitDBStates(); OpenDBResult TryInitDB(bool aDeleteExistingDB); nsresult CreateTable(); + nsresult CreateTableForSchemaVersion5(); void CloseDBStates(); void CleanupCachedStatements(); void CleanupDefaultDBConnection(); @@ -290,9 +291,9 @@ class nsCookieService final : public nsICookieService nsresult GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch); nsresult GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain); nsresult GetCookieStringCommon(nsIURI *aHostURI, nsIChannel *aChannel, bool aHttpBound, char** aCookie); - void GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, uint32_t aAppId, bool aInBrowserElement, bool aIsPrivate, nsCString &aCookie); + void GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, const OriginAttributes aOriginAttrs, bool aIsPrivate, nsCString &aCookie); nsresult SetCookieStringCommon(nsIURI *aHostURI, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, bool aFromHttp); - void SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp, uint32_t aAppId, bool aInBrowserElement, bool aIsPrivate, nsIChannel* aChannel); + void SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp, const OriginAttributes &aOriginAttrs, bool aIsPrivate, nsIChannel* aChannel); bool SetCookieInternal(nsIURI *aHostURI, const nsCookieKey& aKey, bool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, bool aFromHttp, nsIChannel* aChannel); void AddInternal(const nsCookieKey& aKey, nsCookie *aCookie, int64_t aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, bool aFromHttp); void RemoveCookieFromList(const nsListIter &aIter, mozIStorageBindingParamsArray *aParamsArray = nullptr); @@ -318,12 +319,12 @@ class nsCookieService final : public nsICookieService /** * This method is a helper that allows calling nsICookieManager::Remove() - * with appId/inBrowserElement parameters. + * with OriginAttributes parameter. * NOTE: this could be added to a public interface if we happen to need it. */ - nsresult Remove(const nsACString& aHost, uint32_t aAppId, - bool aInBrowserElement, const nsACString& aName, - const nsACString& aPath, bool aBlocked); + nsresult Remove(const nsACString& aHost, const OriginAttributes& aAttrs, + const nsACString& aName, const nsACString& aPath, + bool aBlocked); protected: // cached members. diff --git a/netwerk/cookie/nsICookieManager2.idl b/netwerk/cookie/nsICookieManager2.idl index b12d496c04..95ddefae97 100644 --- a/netwerk/cookie/nsICookieManager2.idl +++ b/netwerk/cookie/nsICookieManager2.idl @@ -8,7 +8,7 @@ interface nsICookie2; interface nsIFile; -/** +/** * Additions to the frozen nsICookieManager */ diff --git a/netwerk/protocol/http/PackagedAppService.cpp b/netwerk/protocol/http/PackagedAppService.cpp index e89d1b7e52..52fbcc7e2a 100644 --- a/netwerk/protocol/http/PackagedAppService.cpp +++ b/netwerk/protocol/http/PackagedAppService.cpp @@ -360,19 +360,19 @@ PackagedAppService::PackagedAppChannelListener::OnStartRequest(nsIRequest *aRequ // to know if it's a signed package. Notify requesters if it's signed. if (isFromCache) { bool isPackageSigned = false; - nsCString signedPackageOrigin; + nsCString signedPackageId; nsCOMPtr packageCacheEntry = GetPackageCacheEntry(aRequest); if (packageCacheEntry) { - const char* key = PackagedAppVerifier::kSignedPakOriginMetadataKey; + const char* key = PackagedAppVerifier::kSignedPakIdMetadataKey; nsXPIDLCString value; nsresult rv = packageCacheEntry->GetMetaDataElement(key, getter_Copies(value)); isPackageSigned = (NS_SUCCEEDED(rv) && !value.IsEmpty()); - signedPackageOrigin = value; + signedPackageId = value; } if (isPackageSigned) { LOG(("The cached package is signed. Notify the requesters.")); - mDownloader->NotifyOnStartSignedPackageRequest(signedPackageOrigin); + mDownloader->NotifyOnStartSignedPackageRequest(signedPackageId); } } @@ -476,6 +476,12 @@ PackagedAppService::PackagedAppDownloader::OnStartRequest(nsIRequest *aRequest, NS_WARN_IF(NS_FAILED(rv)); EnsureVerifier(aRequest); + + if (!mVerifier->WouldVerify()) { + // It means there's no signature or the signed app is disabled. + return NS_OK; + } + mVerifier->OnStartRequest(nullptr, uri); // Since the header is considered as a part of the streaming data, @@ -635,7 +641,7 @@ PackagedAppService::PackagedAppDownloader::OnStopRequest(nsIRequest *aRequest, // Chances to get here: // 1) Very likely the package has been cached or // 2) Less likely the package is malformed. - if (!mVerifier) { + if (!mVerifier || !mVerifier->WouldVerify()) { FinalizeDownload(aStatusCode); } else { // We've got a broken last part and some resources might be still @@ -671,6 +677,12 @@ PackagedAppService::PackagedAppDownloader::OnStopRequest(nsIRequest *aRequest, RefPtr info = new ResourceCacheInfo(uri, entry, aStatusCode, lastPart); + if (!mVerifier->WouldVerify()) { + // No manifest at all. Everything is simply a resource. + OnResourceVerified(info, true); + return NS_OK; + } + mVerifier->OnStopRequest(nullptr, info, aStatusCode); return NS_OK; @@ -699,6 +711,11 @@ PackagedAppService::PackagedAppDownloader::ConsumeData(nsIInputStream *aStream, self->mWriter->ConsumeData(aFromRawSegment, aCount, aWriteCount); + if (!self->mVerifier->WouldVerify()) { + // No signature or signed app support is disabled. + return NS_OK; + } + nsCOMPtr stream = CreateSharedStringStream(aFromRawSegment, aCount); return self->mVerifier->OnDataAvailable(nullptr, nullptr, stream, 0, aCount); } @@ -742,7 +759,7 @@ PackagedAppService::PackagedAppDownloader::AddCallback(nsIURI *aURI, if (mVerifier && mVerifier->GetIsPackageSigned()) { // TODO: Bug 1178526 will deal with the package identifier things. // For now we just use the origin as the identifier. - listener->OnStartSignedPackageRequest(mVerifier->GetPackageOrigin()); + listener->OnStartSignedPackageRequest(mVerifier->GetPackageIdentifier()); listener = nullptr; // So that the request will not be added to the queue. } mCacheStorage->AsyncOpenURI(aURI, EmptyCString(), @@ -876,7 +893,24 @@ PackagedAppService::PackagedAppDownloader::NotifyOnStartSignedPackageRequest(con mRequesters.Clear(); } -void PackagedAppService::PackagedAppDownloader::InstallSignedPackagedApp() +static bool +AddPackageIdToOrigin(nsACString& aOrigin, const nsACString& aPackageId) +{ + nsAutoCString originNoSuffix; + mozilla::OriginAttributes attrs; + if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) { + return false; + } + + attrs.mSignedPkg = NS_ConvertUTF8toUTF16(aPackageId); + nsAutoCString suffixWithPackageId; + attrs.CreateSuffix(suffixWithPackageId); + aOrigin = originNoSuffix + suffixWithPackageId; + return true; +} + +void +PackagedAppService::PackagedAppDownloader::InstallSignedPackagedApp() { // TODO: Bug 1178533 to register permissions, system messages etc on navigation to // signed packages. @@ -935,9 +969,7 @@ PackagedAppService::PackagedAppDownloader::OnManifestVerified(const ResourceCach return; } - nsCString packageOrigin; - mVerifier->GetPackageOrigin(packageOrigin); - NotifyOnStartSignedPackageRequest(packageOrigin); + NotifyOnStartSignedPackageRequest(mVerifier->GetPackageIdentifier()); InstallSignedPackagedApp(); } @@ -955,7 +987,7 @@ PackagedAppService::PackagedAppDownloader::OnResourceVerified(const ResourceCach if (mVerifier->GetIsPackageSigned()) { // TODO: Bug 1178526 will deal with the package identifier things. // For now we just use the origin as the identifier. - NotifyOnStartSignedPackageRequest(mVerifier->GetPackageOrigin()); + NotifyOnStartSignedPackageRequest(mVerifier->GetPackageIdentifier()); } // Serve this resource to all listeners. diff --git a/netwerk/protocol/http/PackagedAppUtils.js b/netwerk/protocol/http/PackagedAppUtils.js index d09354c3e0..0c3ce10023 100644 --- a/netwerk/protocol/http/PackagedAppUtils.js +++ b/netwerk/protocol/http/PackagedAppUtils.js @@ -29,7 +29,7 @@ PackagedAppUtils.prototype = { classDescription: "Packaged App Utils", QueryInterface: XPCOMUtils.generateQI([Ci.nsIPackagedAppUtils]), - verifyManifest: function(aHeader, aManifest, aCallback) { + verifyManifest: function(aHeader, aManifest, aCallback, aDeveloperMode) { debug("Manifest: " + aManifest); // parse signature from header @@ -83,8 +83,10 @@ PackagedAppUtils.prototype = { throw "CERTDB_ERROR"; } - certDb.verifySignedManifestAsync( - Ci.nsIX509CertDB.PrivilegedPackageRoot, manifestStream, signatureStream, + let trustedRoot = aDeveloperMode ? Ci.nsIX509CertDB.DeveloperImportedRoot + : Ci.nsIX509CertDB.PrivilegedPackageRoot; + + certDb.verifySignedManifestAsync(trustedRoot, manifestStream, signatureStream, function(aRv, aCert) { aCallback.fireVerifiedEvent(true, Components.isSuccessCode(aRv)); }); diff --git a/netwerk/protocol/http/PackagedAppVerifier.cpp b/netwerk/protocol/http/PackagedAppVerifier.cpp index b30bb71ea8..0ea422ff66 100644 --- a/netwerk/protocol/http/PackagedAppVerifier.cpp +++ b/netwerk/protocol/http/PackagedAppVerifier.cpp @@ -16,12 +16,13 @@ #include "mozilla/Preferences.h" #include "nsIPackagedAppUtils.h" #include "nsIInputStream.h" +#include "nsComponentManagerUtils.h" +#include "nsIURL.h" +#include "mozilla/BasePrincipal.h" static const short kResourceHashType = nsICryptoHash::SHA256; -// If it's true, all the verification will be skipped and the package will -// be treated signed. -static bool gDeveloperMode = false; +static bool gSignedAppEnabled = false; namespace mozilla { namespace net { @@ -32,7 +33,7 @@ NS_IMPL_ISUPPORTS(PackagedAppVerifier, nsIPackagedAppVerifier, nsIVerificationCa NS_IMPL_ISUPPORTS(PackagedAppVerifier::ResourceCacheInfo, nsISupports) -const char* PackagedAppVerifier::kSignedPakOriginMetadataKey = "signed-pak-origin"; +const char* PackagedAppVerifier::kSignedPakIdMetadataKey = "package-id"; PackagedAppVerifier::PackagedAppVerifier() { @@ -67,18 +68,23 @@ NS_IMETHODIMP PackagedAppVerifier::Init(nsIPackagedAppVerifierListener* aListene { static bool onceThru = false; if (!onceThru) { - Preferences::AddBoolVarCache(&gDeveloperMode, - "network.http.packaged-apps-developer-mode", false); + Preferences::AddBoolVarCache(&gSignedAppEnabled, + "network.http.signed-packages.enabled", false); onceThru = true; } mListener = aListener; mState = STATE_UNKNOWN; - mPackageOrigin = aPackageOrigin; mSignature = aSignature; mIsPackageSigned = false; mPackageCacheEntry = aPackageCacheEntry; mIsFirstResource = true; + mManifest = EmptyCString(); + + nsAutoCString originNoSuffix; + OriginAttributes().PopulateFromOrigin(aPackageOrigin, originNoSuffix); + mBypassVerification = (originNoSuffix == + Preferences::GetCString("network.http.signed-packages.trusted-origin")); nsresult rv; mPackagedAppUtils = do_CreateInstance(NS_PACKAGEDAPPUTILS_CONTRACTID, &rv); @@ -127,7 +133,7 @@ PackagedAppVerifier::WriteManifest(nsIInputStream* aStream, uint32_t* aWriteCount) { LOG(("WriteManifest: length %u", aCount)); - LOG(("%s", aFromRawSegment)); + LOG(("%s", nsCString(aFromRawSegment, aCount).get())); nsCString* manifest = static_cast(aManifest); manifest->AppendASCII(aFromRawSegment, aCount); *aWriteCount = aCount; @@ -268,12 +274,6 @@ PackagedAppVerifier::VerifyManifest(const ResourceCacheInfo* aInfo) mState = STATE_MANIFEST_VERIFYING; - if (gDeveloperMode) { - LOG(("Developer mode! Bypass verification.")); - FireVerifiedEvent(true, true); - return; - } - if (mSignature.IsEmpty()) { LOG(("No signature. No need to do verification.")); FireVerifiedEvent(true, true); @@ -282,7 +282,11 @@ PackagedAppVerifier::VerifyManifest(const ResourceCacheInfo* aInfo) LOG(("Signature: length = %u\n%s", mSignature.Length(), mSignature.get())); LOG(("Manifest: length = %u\n%s", mManifest.Length(), mManifest.get())); - nsresult rv = mPackagedAppUtils->VerifyManifest(mSignature, mManifest, this); + + bool useDeveloperRoot = + !Preferences::GetCString("network.http.signed-packages.developer-root").IsEmpty(); + nsresult rv = mPackagedAppUtils->VerifyManifest(mSignature, mManifest, + this, useDeveloperRoot); if (NS_FAILED(rv)) { LOG(("VerifyManifest FAILED rv = %u", (unsigned)rv)); } @@ -309,8 +313,8 @@ PackagedAppVerifier::VerifyResource(const ResourceCacheInfo* aInfo) MOZ_CRASH(); } - if (gDeveloperMode) { - LOG(("Developer mode! Bypass integrity check.")); + if (mBypassVerification) { + LOG(("Origin is trusted. Bypass integrity check.")); FireVerifiedEvent(false, true); return; } @@ -349,6 +353,12 @@ PackagedAppVerifier::OnManifestVerified(bool aSuccess) return; } + + if (!aSuccess && mBypassVerification) { + aSuccess = true; + LOG(("Developer mode! Treat junk signature valid.")); + } + // Only when the manifest verified and package has signature would we // regard this package is signed. mIsPackageSigned = aSuccess && !mSignature.IsEmpty(); @@ -356,14 +366,24 @@ PackagedAppVerifier::OnManifestVerified(bool aSuccess) mState = aSuccess ? STATE_MANIFEST_VERIFIED_OK : STATE_MANIFEST_VERIFIED_FAILED; - // TODO: Update mPackageOrigin. + // Obtain the package identifier from manifest if the package is signed. + if (mIsPackageSigned) { + mPackagedAppUtils->GetPackageIdentifier(mPackageIdentifer); + LOG(("PackageIdentifer is: %s", mPackageIdentifer.get())); + } + + // If the signature verification failed, doom the package cache to + // make its subresources unavailable in the subsequent requests. + if (!aSuccess && mPackageCacheEntry) { + mPackageCacheEntry->AsyncDoom(nullptr); + } // If the package is signed, add related info to the package cache. if (mIsPackageSigned && mPackageCacheEntry) { LOG(("This package is signed. Add this info to the cache channel.")); if (mPackageCacheEntry) { - mPackageCacheEntry->SetMetaDataElement(kSignedPakOriginMetadataKey, - mPackageOrigin.get()); + mPackageCacheEntry->SetMetaDataElement(kSignedPakIdMetadataKey, + mPackageIdentifer.get()); mPackageCacheEntry = nullptr; // the cache entry is no longer needed. } } @@ -418,14 +438,20 @@ PackagedAppVerifier::SetHasBrokenLastPart(nsresult aStatusCode) mPendingResourceCacheInfoList.insertBack(info); } +bool +PackagedAppVerifier::WouldVerify() const +{ + return gSignedAppEnabled && !mSignature.IsEmpty(); +} + //--------------------------------------------------------------- // nsIPackagedAppVerifier. //--------------------------------------------------------------- NS_IMETHODIMP -PackagedAppVerifier::GetPackageOrigin(nsACString& aPackageOrigin) +PackagedAppVerifier::GetPackageIdentifier(nsACString& aPackageIdentifier) { - aPackageOrigin = mPackageOrigin; + aPackageIdentifier = mPackageIdentifer; return NS_OK; } diff --git a/netwerk/protocol/http/PackagedAppVerifier.h b/netwerk/protocol/http/PackagedAppVerifier.h index b733bc46cb..c56d9d92ff 100644 --- a/netwerk/protocol/http/PackagedAppVerifier.h +++ b/netwerk/protocol/http/PackagedAppVerifier.h @@ -109,12 +109,14 @@ public: return mIsPackageSigned; } - const nsACString& GetPackageOrigin() const + const nsACString& GetPackageIdentifier() const { - return mPackageOrigin; + return mPackageIdentifer; } - static const char* kSignedPakOriginMetadataKey; + bool WouldVerify() const; + + static const char* kSignedPakIdMetadataKey; private: virtual ~PackagedAppVerifier(); @@ -169,6 +171,9 @@ private: // Whether this package app is signed. bool mIsPackageSigned; + // Whether we should bypass verification. + bool mBypassVerification; + // The package cache entry (e.g. http://foo.com/app.pak) used to store // any necessarry signed package information. nsCOMPtr mPackageCacheEntry; @@ -191,6 +196,8 @@ private: // A place to store the computed hashes of each resource. nsClassHashtable mResourceHashStore; + + nsCString mPackageIdentifer; }; // class PackagedAppVerifier } // namespace net diff --git a/netwerk/protocol/http/nsHttpAuthCache.cpp b/netwerk/protocol/http/nsHttpAuthCache.cpp index 780313a5f6..e0a7328284 100644 --- a/netwerk/protocol/http/nsHttpAuthCache.cpp +++ b/netwerk/protocol/http/nsHttpAuthCache.cpp @@ -16,18 +16,17 @@ #include "mozIApplicationClearPrivateDataParams.h" #include "nsIObserverService.h" #include "mozilla/Services.h" +#include "mozilla/DebugOnly.h" #include "nsNetUtil.h" namespace mozilla { namespace net { static inline void -GetAuthKey(const char *scheme, const char *host, int32_t port, uint32_t appId, bool inBrowserElement, nsCString &key) +GetAuthKey(const char *scheme, const char *host, int32_t port, nsACString const &originSuffix, nsCString &key) { key.Truncate(); - key.AppendInt(appId); - key.Append(':'); - key.AppendInt(inBrowserElement); + key.Append(originSuffix); key.Append(':'); key.Append(scheme); key.AppendLiteral("://"); @@ -57,11 +56,11 @@ StrEquivalent(const char16_t *a, const char16_t *b) nsHttpAuthCache::nsHttpAuthCache() : mDB(nullptr) - , mObserver(new AppDataClearObserver(this)) + , mObserver(new OriginClearObserver(this)) { nsCOMPtr obsSvc = services::GetObserverService(); if (obsSvc) { - obsSvc->AddObserver(mObserver, "webapps-clear-data", false); + obsSvc->AddObserver(mObserver, "clear-origin-data", false); } } @@ -71,7 +70,7 @@ nsHttpAuthCache::~nsHttpAuthCache() ClearAll(); nsCOMPtr obsSvc = services::GetObserverService(); if (obsSvc) { - obsSvc->RemoveObserver(mObserver, "webapps-clear-data"); + obsSvc->RemoveObserver(mObserver, "clear-origin-data"); mObserver->mOwner = nullptr; } } @@ -97,15 +96,14 @@ nsHttpAuthCache::GetAuthEntryForPath(const char *scheme, const char *host, int32_t port, const char *path, - uint32_t appId, - bool inBrowserElement, + nsACString const &originSuffix, nsHttpAuthEntry **entry) { LOG(("nsHttpAuthCache::GetAuthEntryForPath [key=%s://%s:%d path=%s]\n", scheme, host, port, path)); nsAutoCString key; - nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key); + nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, originSuffix, key); if (!node) return NS_ERROR_NOT_AVAILABLE; @@ -118,8 +116,7 @@ nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme, const char *host, int32_t port, const char *realm, - uint32_t appId, - bool inBrowserElement, + nsACString const &originSuffix, nsHttpAuthEntry **entry) { @@ -127,7 +124,7 @@ nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme, scheme, host, port, realm)); nsAutoCString key; - nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key); + nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, originSuffix, key); if (!node) return NS_ERROR_NOT_AVAILABLE; @@ -143,8 +140,7 @@ nsHttpAuthCache::SetAuthEntry(const char *scheme, const char *realm, const char *creds, const char *challenge, - uint32_t appId, - bool inBrowserElement, + nsACString const &originSuffix, const nsHttpAuthIdentity *ident, nsISupports *metadata) { @@ -159,7 +155,7 @@ nsHttpAuthCache::SetAuthEntry(const char *scheme, } nsAutoCString key; - nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key); + nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, originSuffix, key); if (!node) { // create a new entry node and set the given entry @@ -182,14 +178,13 @@ nsHttpAuthCache::ClearAuthEntry(const char *scheme, const char *host, int32_t port, const char *realm, - uint32_t appId, - bool inBrowserElement) + nsACString const &originSuffix) { if (!mDB) return; nsAutoCString key; - GetAuthKey(scheme, host, port, appId, inBrowserElement, key); + GetAuthKey(scheme, host, port, originSuffix, key); PL_HashTableRemove(mDB, key.get()); } @@ -213,14 +208,13 @@ nsHttpAuthNode * nsHttpAuthCache::LookupAuthNode(const char *scheme, const char *host, int32_t port, - uint32_t appId, - bool inBrowserElement, + nsACString const &originSuffix, nsCString &key) { if (!mDB) return nullptr; - GetAuthKey(scheme, host, port, appId, inBrowserElement, key); + GetAuthKey(scheme, host, port, originSuffix, key); return (nsHttpAuthNode *) PL_HashTableLookup(mDB, key.get()); } @@ -268,60 +262,56 @@ PLHashAllocOps nsHttpAuthCache::gHashAllocOps = nsHttpAuthCache::FreeEntry }; -NS_IMPL_ISUPPORTS(nsHttpAuthCache::AppDataClearObserver, nsIObserver) +NS_IMPL_ISUPPORTS(nsHttpAuthCache::OriginClearObserver, nsIObserver) NS_IMETHODIMP -nsHttpAuthCache::AppDataClearObserver::Observe(nsISupports *subject, - const char * topic, - const char16_t * data_unicode) +nsHttpAuthCache::OriginClearObserver::Observe(nsISupports *subject, + const char * topic, + const char16_t * data_unicode) { NS_ENSURE_TRUE(mOwner, NS_ERROR_NOT_AVAILABLE); - nsCOMPtr params = - do_QueryInterface(subject); - if (!params) { - NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams"); - return NS_ERROR_UNEXPECTED; + OriginAttributesPattern pattern; + if (!pattern.Init(nsDependentString(data_unicode))) { + NS_ERROR("Cannot parse origin attributes pattern"); + return NS_ERROR_FAILURE; } - uint32_t appId; - bool browserOnly; - - nsresult rv = params->GetAppId(&appId); - NS_ENSURE_SUCCESS(rv, rv); - rv = params->GetBrowserOnly(&browserOnly); - NS_ENSURE_SUCCESS(rv, rv); - - MOZ_ASSERT(appId != NECKO_UNKNOWN_APP_ID); - mOwner->ClearAppData(appId, browserOnly); + mOwner->ClearOriginData(pattern); return NS_OK; } static int -RemoveEntriesForApp(PLHashEntry *entry, int32_t number, void *arg) +RemoveEntriesForPattern(PLHashEntry *entry, int32_t number, void *arg) { nsDependentCString key(static_cast(entry->key)); - nsAutoCString* prefix = static_cast(arg); - if (StringBeginsWith(key, *prefix)) { + + // Extract the origin attributes suffix from the key. + int32_t colon = key.Find(NS_LITERAL_CSTRING(":")); + MOZ_ASSERT(colon != kNotFound); + nsDependentCSubstring oaSuffix; + oaSuffix.Rebind(key.BeginReading(), colon); + + // Build the OriginAttributes object of it... + OriginAttributes oa; + DebugOnly rv = oa.PopulateFromSuffix(oaSuffix); + MOZ_ASSERT(rv); + + // ...and match it against the given pattern. + OriginAttributesPattern const *pattern = static_cast(arg); + if (pattern->Matches(oa)) { return HT_ENUMERATE_NEXT | HT_ENUMERATE_REMOVE; } return HT_ENUMERATE_NEXT; } void -nsHttpAuthCache::ClearAppData(uint32_t appId, bool browserOnly) +nsHttpAuthCache::ClearOriginData(OriginAttributesPattern const &pattern) { if (!mDB) { return; } - nsAutoCString keyPrefix; - keyPrefix.AppendInt(appId); - keyPrefix.Append(':'); - if (browserOnly) { - keyPrefix.AppendInt(browserOnly); - keyPrefix.Append(':'); - } - PL_HashTableEnumerateEntries(mDB, RemoveEntriesForApp, &keyPrefix); + PL_HashTableEnumerateEntries(mDB, RemoveEntriesForPattern, (void*)&pattern); } //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/nsHttpAuthCache.h b/netwerk/protocol/http/nsHttpAuthCache.h index dfc43e3bae..f96623d847 100644 --- a/netwerk/protocol/http/nsHttpAuthCache.h +++ b/netwerk/protocol/http/nsHttpAuthCache.h @@ -16,6 +16,9 @@ class nsCString; namespace mozilla { + +class OriginAttributesPattern; + namespace net { struct nsHttpAuthPath { @@ -181,8 +184,7 @@ public: const char *host, int32_t port, const char *path, - uint32_t appId, - bool inBrowserElement, + nsACString const &originSuffix, nsHttpAuthEntry **entry); // |scheme|, |host|, and |port| are required @@ -192,8 +194,7 @@ public: const char *host, int32_t port, const char *realm, - uint32_t appId, - bool inBrowserElement, + nsACString const &originSuffix, nsHttpAuthEntry **entry); // |scheme|, |host|, and |port| are required @@ -208,8 +209,7 @@ public: const char *realm, const char *credentials, const char *challenge, - uint32_t appId, - bool inBrowserElement, + nsACString const &originSuffix, const nsHttpAuthIdentity *ident, nsISupports *metadata); @@ -217,8 +217,7 @@ public: const char *host, int32_t port, const char *realm, - uint32_t appId, - bool inBrowserElement); + nsACString const &originSuffix); // expire all existing auth list entries including proxy auths. nsresult ClearAll(); @@ -227,8 +226,7 @@ private: nsHttpAuthNode *LookupAuthNode(const char *scheme, const char *host, int32_t port, - uint32_t appId, - bool inBrowserElement, + nsACString const &originSuffix, nsCString &key); // hash table allocation functions @@ -239,20 +237,20 @@ private: static PLHashAllocOps gHashAllocOps; - class AppDataClearObserver : public nsIObserver { - virtual ~AppDataClearObserver() {} + class OriginClearObserver : public nsIObserver { + virtual ~OriginClearObserver() {} public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER - explicit AppDataClearObserver(nsHttpAuthCache* aOwner) : mOwner(aOwner) {} + explicit OriginClearObserver(nsHttpAuthCache* aOwner) : mOwner(aOwner) {} nsHttpAuthCache* mOwner; }; - void ClearAppData(uint32_t appId, bool browserOnly); + void ClearOriginData(OriginAttributesPattern const &pattern); private: PLHashTable *mDB; // "host:port" --> nsHttpAuthNode - RefPtr mObserver; + RefPtr mObserver; }; } // namespace net diff --git a/netwerk/protocol/http/nsHttpAuthManager.cpp b/netwerk/protocol/http/nsHttpAuthManager.cpp index b6f73f1698..a2f1b7c5e7 100644 --- a/netwerk/protocol/http/nsHttpAuthManager.cpp +++ b/netwerk/protocol/http/nsHttpAuthManager.cpp @@ -67,11 +67,10 @@ nsHttpAuthManager::GetAuthIdentity(const nsACString & aScheme, nsHttpAuthCache* auth_cache = aIsPrivate ? mPrivateAuthCache : mAuthCache; nsHttpAuthEntry * entry = nullptr; nsresult rv; - uint32_t appId = NECKO_NO_APP_ID; - bool inBrowserElement = false; + + nsAutoCString originSuffix; if (aPrincipal) { - appId = aPrincipal->GetAppId(); - inBrowserElement = aPrincipal->GetIsInBrowserElement(); + BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(originSuffix); } if (!aPath.IsEmpty()) @@ -79,14 +78,14 @@ nsHttpAuthManager::GetAuthIdentity(const nsACString & aScheme, PromiseFlatCString(aHost).get(), aPort, PromiseFlatCString(aPath).get(), - appId, inBrowserElement, + originSuffix, &entry); else rv = auth_cache->GetAuthEntryForDomain(PromiseFlatCString(aScheme).get(), PromiseFlatCString(aHost).get(), aPort, PromiseFlatCString(aRealm).get(), - appId, inBrowserElement, + originSuffix, &entry); if (NS_FAILED(rv)) @@ -117,13 +116,12 @@ nsHttpAuthManager::SetAuthIdentity(const nsACString & aScheme, PromiseFlatString(aUserName).get(), PromiseFlatString(aUserPassword).get()); - uint32_t appId = NECKO_NO_APP_ID; - bool inBrowserElement = false; + nsAutoCString originSuffix; if (aPrincipal) { - appId = aPrincipal->GetAppId(); - inBrowserElement = aPrincipal->GetIsInBrowserElement(); + BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(originSuffix); } + nsHttpAuthCache* auth_cache = aIsPrivate ? mPrivateAuthCache : mAuthCache; return auth_cache->SetAuthEntry(PromiseFlatCString(aScheme).get(), PromiseFlatCString(aHost).get(), @@ -132,7 +130,7 @@ nsHttpAuthManager::SetAuthIdentity(const nsACString & aScheme, PromiseFlatCString(aRealm).get(), nullptr, // credentials nullptr, // challenge - appId, inBrowserElement, + originSuffix, &ident, nullptr); // metadata } diff --git a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp index bca9321a1d..4573e0b3f0 100644 --- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp +++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp @@ -43,19 +43,16 @@ namespace net { #define MAX_DISPLAYED_HOST_LENGTH 64 static void -GetAppIdAndBrowserStatus(nsIChannel* aChan, uint32_t* aAppId, bool* aInBrowserElem) +GetOriginAttributesSuffix(nsIChannel* aChan, nsACString &aSuffix) { - nsCOMPtr loadContext; + OriginAttributes oa; + + // Deliberately ignoring the result and going with defaults if (aChan) { - NS_QueryNotificationCallbacks(aChan, loadContext); - } - if (!loadContext) { - *aAppId = NECKO_NO_APP_ID; - *aInBrowserElem = false; - } else { - loadContext->GetAppId(aAppId); - loadContext->GetIsInBrowserElement(aInBrowserElem); + NS_GetOriginAttributes(aChan, oa); } + + oa.CreateSuffix(aSuffix); } nsHttpChannelAuthProvider::nsHttpChannelAuthProvider() @@ -77,15 +74,15 @@ nsHttpChannelAuthProvider::~nsHttpChannelAuthProvider() } uint32_t nsHttpChannelAuthProvider::sAuthAllowPref = - SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN; + SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL; void nsHttpChannelAuthProvider::InitializePrefs() { MOZ_ASSERT(NS_IsMainThread()); mozilla::Preferences::AddUintVarCache(&sAuthAllowPref, - "network.auth.allow-subresource-auth", - SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN); + "network.auth.subresource-http-auth-allow", + SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL); } NS_IMETHODIMP @@ -404,9 +401,8 @@ nsHttpChannelAuthProvider::GenCredsAndSetEntry(nsIHttpAuthenticator *auth, nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate); nsCOMPtr chan = do_QueryInterface(mAuthChannel); - uint32_t appId; - bool isInBrowserElement; - GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); + nsAutoCString suffix; + GetOriginAttributesSuffix(chan, suffix); // create a cache entry. we do this even though we don't yet know that // these credentials are valid b/c we need to avoid prompting the user @@ -417,7 +413,7 @@ nsHttpChannelAuthProvider::GenCredsAndSetEntry(nsIHttpAuthenticator *auth, rv = authCache->SetAuthEntry(scheme, host, port, directory, realm, saveCreds ? *result : nullptr, saveChallenge ? challenge : nullptr, - appId, isInBrowserElement, + suffix, saveIdentity ? &ident : nullptr, sessionState); return rv; @@ -678,9 +674,8 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, } nsCOMPtr chan = do_QueryInterface(mAuthChannel); - uint32_t appId; - bool isInBrowserElement; - GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); + nsAutoCString suffix; + GetOriginAttributesSuffix(chan, suffix); // // if we already tried some credentials for this transaction, then @@ -690,8 +685,7 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, // nsHttpAuthEntry *entry = nullptr; authCache->GetAuthEntryForDomain(scheme.get(), host, port, - realm.get(), appId, - isInBrowserElement, &entry); + realm.get(), suffix, &entry); // hold reference to the auth session state (in case we clear our // reference to the entry). @@ -722,7 +716,7 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, // corresponding entry from the auth cache. authCache->ClearAuthEntry(scheme.get(), host, port, realm.get(), - appId, isInBrowserElement); + suffix); entry = nullptr; ident->Clear(); } @@ -1107,15 +1101,13 @@ NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthAvailable(nsISupports *aContext, ParseRealm(mCurrentChallenge.get(), realm); nsCOMPtr chan = do_QueryInterface(mAuthChannel); - uint32_t appId; - bool isInBrowserElement; - GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); + nsAutoCString suffix; + GetOriginAttributesSuffix(chan, suffix); nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate); nsHttpAuthEntry *entry = nullptr; authCache->GetAuthEntryForDomain(scheme.get(), host, port, - realm.get(), appId, - isInBrowserElement, + realm.get(), suffix, &entry); nsCOMPtr sessionStateGrip; @@ -1365,12 +1357,11 @@ nsHttpChannelAuthProvider::SetAuthorizationHeader(nsHttpAuthCache *authCache, } nsCOMPtr chan = do_QueryInterface(mAuthChannel); - uint32_t appId; - bool isInBrowserElement; - GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); + nsAutoCString suffix; + GetOriginAttributesSuffix(chan, suffix); rv = authCache->GetAuthEntryForPath(scheme, host, port, path, - appId, isInBrowserElement, &entry); + suffix, &entry); if (NS_SUCCEEDED(rv)) { // if we are trying to add a header for origin server auth and if the // URL contains an explicit username, then try the given username first. diff --git a/netwerk/test/mochitests/signed_web_packaged_app.sjs b/netwerk/test/mochitests/signed_web_packaged_app.sjs index 1b7256be3a..eac3bfe503 100644 --- a/netwerk/test/mochitests/signed_web_packaged_app.sjs +++ b/netwerk/test/mochitests/signed_web_packaged_app.sjs @@ -5,33 +5,79 @@ var Cu = Components.utils; function handleRequest(request, response) { response.setHeader("Content-Type", "application/package", false); - response.write(octetStreamData.packageHeader + octetStreamData.getData()); + response.write(signedPackage); return; } // The package content // getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format -var octetStreamData = { - packageHeader: 'manifest-signature: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\r\n', - - content: [ - { headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "\r\n \r\n \r\n\r\n Web Packaged App Index\r\n\r\n", type: "text/html" }, - { headers: ["Content-Location: /scripts/app.js", "Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" }, - { headers: ["Content-Location: /scripts/helpers/math.js", "Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" } - ], - token : "gc0pJq0M:08jU534c0p", - getData: function() { - var str = ""; - for (var i in this.content) { - str += "--" + this.token + "\r\n"; - for (var j in this.content[i].headers) { - str += this.content[i].headers[j] + "\r\n"; - } - str += "\r\n"; - str += this.content[i].data + "\r\n"; - } - - str += "--" + this.token + "--"; - return str; - } -} +var signedPackage = [ + "manifest-signature: MIIF1AYJKoZIhvcNAQcCoIIFxTCCBcECAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3DQEHAaCCA54wggOaMIICgqADAgECAgECMA0GCSqGSIb3DQEBCwUAMHMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEkMCIGA1UEChMbRXhhbXBsZSBUcnVzdGVkIENvcnBvcmF0aW9uMRkwFwYDVQQDExBUcnVzdGVkIFZhbGlkIENBMB4XDTE1MDkxMDA4MDQzNVoXDTM1MDkxMDA4MDQzNVowdDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSQwIgYDVQQKExtFeGFtcGxlIFRydXN0ZWQgQ29ycG9yYXRpb24xGjAYBgNVBAMTEVRydXN0ZWQgQ29ycCBDZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAts8whjOzEbn/w1xkFJ67af7F/JPujBK91oyJekh2schIMzFau9pY8S1AiJQoJCulOJCJfUc8hBLKBZiGAkii+4Gpx6cVqMLe6C22MdD806Soxn8Dg4dQqbIvPuI4eeVKu5CEk80PW/BaFMmRvRHO62C7PILuH6yZeGHC4P7dTKpsk4CLxh/jRGXLC8jV2BCW0X+3BMbHBg53NoI9s1Gs7KGYnfOHbBP5wEFAa00RjHnubUaCdEBlC8Kl4X7p0S4RGb3rsB08wgFe9EmSZHIgcIm+SuVo7N4qqbI85qo2ulU6J8NN7ZtgMPHzrMhzgAgf/KnqPqwDIxnNmRNJmHTUYwIDAQABozgwNjAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAukH6cJUUj5faa8CuPCqrEa0PoLY4SYNnff9NI+TTAHkB9l+kOcFl5eo2EQOcWmZKYi7QLlWC4jy/KQYattO9FMaxiOQL4FAc6ZIbNyfwWBzZWyr5syYJTTTnkLq8A9pCKarN49+FqhJseycU+8EhJEJyP5pv5hLvDNTTHOQ6SXhASsiX8cjo3AY4bxA5pWeXuTZ459qDxOnQd+GrOe4dIeqflk0hA2xYKe3SfF+QlK8EO370B8Dj8RX230OATM1E3OtYyALe34KW3wM9Qm9rb0eViDnVyDiCWkhhQnw5yPg/XQfloug2itRYuCnfUoRt8xfeHgwz2Ymz8cUADn3KpTGCAf4wggH6AgEBMHgwczELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSQwIgYDVQQKExtFeGFtcGxlIFRydXN0ZWQgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFRydXN0ZWQgVmFsaWQgQ0ECAQIwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE1MTAwMTIxMTEwNlowIwYJKoZIhvcNAQkEMRYEFHAisUYrrt+gBxYFhZ5KQQusOmN3MA0GCSqGSIb3DQEBAQUABIIBACHW4V0BsPWOvWrGOTRj6mPpNbH/JI1bN2oyqQZrpUQoaBY+BbYxO7TY4Uwe+aeIR/TTPJznOMF/dl3Bna6TPabezU4ylg7TVFI6W7zC5f5DZKp+Xv6uTX6knUzbbW1fkJqMtE8hGUzYXc3/C++Ci6kuOzrpWOhk6DpJHeUO/ioV56H0+QK/oMAjYpEsOohaPqvTPNOBhMQ0OQP3bmuJ6HcjZ/oz96PpzXUPKT1tDe6VykIYkV5NvtC8Tu2lDbYvp9ug3gyDgdyNSV47y5i/iWkzEhsAJB+9Z50wKhplnkxxVHEXkB/6tmfvExvQ28gLd/VbaEGDX2ljCaTSUjhD0o0=\r", + "--7B0MKBI3UH\r", + "Content-Location: manifest.webapp\r", + "Content-Type: application/x-web-app-manifest+json\r", + "\r", + "{", + " \"name\": \"My App\",", + " \"moz-resources\": [", + " {", + " \"src\": \"page2.html\",", + " \"integrity\": \"JREF3JbXGvZ+I1KHtoz3f46ZkeIPrvXtG4VyFQrJ7II=\"", + " },", + " {", + " \"src\": \"index.html\",", + " \"integrity\": \"zEubR310nePwd30NThIuoCxKJdnz7Mf5z+dZHUbH1SE=\"", + " },", + " {", + " \"src\": \"scripts/script.js\",", + " \"integrity\": \"6TqtNArQKrrsXEQWu3D9ZD8xvDRIkhyV6zVdTcmsT5Q=\"", + " },", + " {", + " \"src\": \"scripts/library.js\",", + " \"integrity\": \"TN2ByXZiaBiBCvS4MeZ02UyNi44vED+KjdjLInUl4o8=\"", + " }", + " ],", + " \"moz-permissions\": [", + " {", + " \"systemXHR\": {", + " \"description\": \"Needed to download stuff\"", + " },", + " \"devicestorage:pictures\": {", + " \"description\": \"Need to load pictures\"", + " }", + " }", + " ],", + " \"package-identifier\": \"611FC2FE-491D-4A47-B3B3-43FBDF6F404F\",", + " \"moz-package-location\": \"https://example.com/myapp/app.pak\",", + " \"description\": \"A great app!\"", + "}\r", + "--7B0MKBI3UH\r", + "Content-Location: page2.html\r", + "Content-Type: text/html\r", + "\r", + "", + " page2.html", + "", + "\r", + "--7B0MKBI3UH\r", + "Content-Location: index.html\r", + "Content-Type: text/html\r", + "\r", + "", + " Last updated: 2015/10/01 14:10 PST", + "", + "\r", + "--7B0MKBI3UH\r", + "Content-Location: scripts/script.js\r", + "Content-Type: text/javascript\r", + "\r", + "// script.js", + "\r", + "--7B0MKBI3UH\r", + "Content-Location: scripts/library.js\r", + "Content-Type: text/javascript\r", + "\r", + "// library.js", + "\r", + "--7B0MKBI3UH--" +].join("\n"); diff --git a/netwerk/test/mochitests/test_signed_web_packaged_app.html b/netwerk/test/mochitests/test_signed_web_packaged_app.html index 7eae7b8956..b0856c727c 100644 --- a/netwerk/test/mochitests/test_signed_web_packaged_app.html +++ b/netwerk/test/mochitests/test_signed_web_packaged_app.html @@ -21,8 +21,8 @@ var Cr = SpecialPowers.Cr; SpecialPowers.pushPrefEnv( { "set": [["network.http.enable-packaged-apps", true], - ["network.http.packaged-apps-developer-mode", true], ["dom.ipc.processPriorityManager.testMode", true], + ["network.http.signed-packages.enabled", true], ["dom.ipc.processPriorityManager.enabled", true], ["dom.ipc.tabs.disabled", false], ["dom.ipc.processCount", 3], diff --git a/netwerk/test/unit/socks_client_subprocess.js b/netwerk/test/unit/socks_client_subprocess.js index b3597c0878..0de183033f 100644 --- a/netwerk/test/unit/socks_client_subprocess.js +++ b/netwerk/test/unit/socks_client_subprocess.js @@ -34,7 +34,7 @@ function launchConnection(socks_vers, socks_port, dest_host, dest_port, dns) output.close(); } -for each (var arg in arguments) { +for (var arg of arguments) { print('client: running test', arg); test = arg.split('|'); launchConnection(test[0], parseInt(test[1]), test[2], diff --git a/netwerk/test/unit/test_auth_dialog_permission.js b/netwerk/test/unit/test_auth_dialog_permission.js index 5bfe985372..38ec50c38b 100644 --- a/netwerk/test/unit/test_auth_dialog_permission.js +++ b/netwerk/test/unit/test_auth_dialog_permission.js @@ -1,5 +1,5 @@ // This file tests authentication prompt depending on pref -// network.auth.allow-subresource-auth: +// network.auth.subresource-http-auth-allow: // 0 - don't allow sub-resources to open HTTP authentication credentials // dialogs // 1 - allow sub-resources to open HTTP authentication credentials dialogs, @@ -131,9 +131,9 @@ function makeChan(loadingUrl, url, contentPolicy) { return chan; } -function Test(allow_subresource_auth_pref, loadingUri, uri, contentPolicy, +function Test(subresource_http_auth_allow_pref, loadingUri, uri, contentPolicy, expectedCode) { - this._allow_subresource_auth_pref = allow_subresource_auth_pref; + this._subresource_http_auth_allow_pref = subresource_http_auth_allow_pref; this._loadingUri = loadingUri; this._uri = uri; this._contentPolicy = contentPolicy; @@ -141,7 +141,7 @@ function Test(allow_subresource_auth_pref, loadingUri, uri, contentPolicy, } Test.prototype = { - _allow_subresource_auth_pref: 1, + _subresource_http_auth_allow_pref: 1, _loadingUri: null, _uri: null, _contentPolicy: Ci.nsIContentPolicy.TYPE_OTHER, @@ -184,14 +184,14 @@ Test.prototype = { }, run: function() { - dump("Run test: " + this._allow_subresource_auth_pref + dump("Run test: " + this._subresource_http_auth_allow_pref + this._loadingUri + this._uri + this._contentPolicy + this._expectedCode + " \n"); - prefs.setIntPref("network.auth.allow-subresource-auth", - this._allow_subresource_auth_pref); + prefs.setIntPref("network.auth.subresource-http-auth-allow", + this._subresource_http_auth_allow_pref); let chan = makeChan(this._loadingUri, this._uri, this._contentPolicy); chan.notificationCallbacks = new Requestor(this._expectedCode == 200); chan.asyncOpen(this, null); diff --git a/netwerk/test/unit/test_auth_jar.js b/netwerk/test/unit/test_auth_jar.js index b5e42b5e83..a63f54402d 100644 --- a/netwerk/test/unit/test_auth_jar.js +++ b/netwerk/test/unit/test_auth_jar.js @@ -23,12 +23,8 @@ function run_test() { am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user3", "pass3", false, app1browser); am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user2", "pass2", false, app10); - let subject = { - appId: 1, - browserOnly: true, - QueryInterface: XPCOMUtils.generateQI([Ci.mozIApplicationClearPrivateDataParams]) - }; - Services.obs.notifyObservers(subject, "webapps-clear-data", null); + let attrs_inBrowser = JSON.stringify({ appId:1, inBrowser:true }); + Services.obs.notifyObservers(null, "clear-origin-data", attrs_inBrowser); var domain = {value: ""}, user = {value: ""}, pass = {value: ""}; try { diff --git a/netwerk/test/unit/test_auth_proxy.js b/netwerk/test/unit/test_auth_proxy.js index 4b252dcf50..871f122b56 100644 --- a/netwerk/test/unit/test_auth_proxy.js +++ b/netwerk/test/unit/test_auth_proxy.js @@ -245,7 +245,7 @@ function run_test() { prefs.setIntPref("network.proxy.type", 1); // Turn off the authentication dialog blocking for this test. - prefs.setIntPref("network.auth.allow-subresource-auth", 2); + prefs.setIntPref("network.auth.subresource-http-auth-allow", 2); tests[current_test](); } diff --git a/netwerk/test/unit/test_authentication.js b/netwerk/test/unit/test_authentication.js index 8fbbf31ebb..32ee99660f 100644 --- a/netwerk/test/unit/test_authentication.js +++ b/netwerk/test/unit/test_authentication.js @@ -7,7 +7,7 @@ Cu.import("resource://gre/modules/Services.jsm"); // Turn off the authentication dialog blocking for this test. var prefs = Cc["@mozilla.org/preferences-service;1"]. getService(Ci.nsIPrefBranch); -prefs.setIntPref("network.auth.allow-subresource-auth", 2); +prefs.setIntPref("network.auth.subresource-http-auth-allow", 2); XPCOMUtils.defineLazyGetter(this, "URL", function() { return "http://localhost:" + httpserv.identity.primaryPort; diff --git a/netwerk/test/unit/test_bug337744.js b/netwerk/test/unit/test_bug337744.js index b5e63964b7..a2422a8df6 100644 --- a/netwerk/test/unit/test_bug337744.js +++ b/netwerk/test/unit/test_bug337744.js @@ -35,7 +35,7 @@ function check_for_exception(spec) } function run_test() { - for each (spec in specs) { + for (var spec of specs) { check_for_exception(spec); } } diff --git a/netwerk/test/unit/test_bug380994.js b/netwerk/test/unit/test_bug380994.js index 337276bcac..b9b9d5bd60 100644 --- a/netwerk/test/unit/test_bug380994.js +++ b/netwerk/test/unit/test_bug380994.js @@ -14,7 +14,7 @@ function run_test() { var ios = Cc["@mozilla.org/network/io-service;1"]. getService(Ci.nsIIOService); - for each (spec in specs) { + for (var spec of specs) { var uri = ios.newURI(spec, null, null); if (uri.spec.indexOf("..") != -1) do_throw("resource: traversal remains: '"+spec+"' ==> '"+uri.spec+"'"); diff --git a/netwerk/test/unit/test_bug396389.js b/netwerk/test/unit/test_bug396389.js index 21a42939c2..0bcfa83624 100644 --- a/netwerk/test/unit/test_bug396389.js +++ b/netwerk/test/unit/test_bug396389.js @@ -43,7 +43,7 @@ function run_test() { // Make sure our prefs are set such that this test actually means something var prefs = Cc["@mozilla.org/preferences-service;1"]. getService(Ci.nsIPrefBranch); - for each (var pref in prefData) { + for (var pref of prefData) { try { pref.oldVal = prefs.getBoolPref(pref.name); } catch(e) { @@ -63,7 +63,7 @@ function run_test() { do_check_eq(uri4.host, uri5.host); do_check_eq(uri4.asciiHost, uri5.asciiHost); } finally { - for each (var pref in prefData) { + for (var pref of prefData) { if (prefs.prefHasUserValue(pref.name)) prefs.clearUserPref(pref.name); } diff --git a/netwerk/test/unit/test_bug412457.js b/netwerk/test/unit/test_bug412457.js new file mode 100644 index 0000000000..dd4358413e --- /dev/null +++ b/netwerk/test/unit/test_bug412457.js @@ -0,0 +1,44 @@ +function run_test() { + var ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + + // check if hostname is unescaped before applying IDNA + var newURI = ios.newURI("http://\u5341%2ecom/", null, null); + do_check_eq(newURI.asciiHost, "xn--kkr.com"); + + // escaped UTF8 + newURI.spec = "http://%e5%8d%81.com"; + do_check_eq(newURI.asciiHost, "xn--kkr.com"); + + // There should be only allowed characters in hostname after + // unescaping and attempting to apply IDNA. "\x80" is illegal in + // UTF-8, so IDNA fails, and 0x80 is illegal in DNS too. + Assert.throws(() => { newURI.spec = "http://%80.com"; }, "illegal UTF character"); + + // test parsing URL with all possible host terminators + newURI.spec = "http://example.com?foo"; + do_check_eq(newURI.asciiHost, "example.com"); + + newURI.spec = "http://example.com#foo"; + do_check_eq(newURI.asciiHost, "example.com"); + + newURI.spec = "http://example.com:80"; + do_check_eq(newURI.asciiHost, "example.com"); + + newURI.spec = "http://example.com/foo"; + do_check_eq(newURI.asciiHost, "example.com"); + + // Characters that are invalid in the host, shouldn't be decoded. + newURI.spec = "http://example.com%3ffoo"; + do_check_eq(newURI.asciiHost, "example.com%3ffoo"); + newURI.spec = "http://example.com%23foo"; + do_check_eq(newURI.asciiHost, "example.com%23foo"); + newURI.spec = "http://example.com%3bfoo"; + do_check_eq(newURI.asciiHost, "example.com%3bfoo"); + newURI.spec = "http://example.com%3a80"; + do_check_eq(newURI.asciiHost, "example.com%3a80"); + newURI.spec = "http://example.com%2ffoo"; + do_check_eq(newURI.asciiHost, "example.com%2ffoo"); + newURI.spec = "http://example.com%00"; + do_check_eq(newURI.asciiHost, "example.com%00"); +} \ No newline at end of file diff --git a/netwerk/test/unit/test_cookie_blacklist.js b/netwerk/test/unit/test_cookie_blacklist.js new file mode 100644 index 0000000000..09d581cfbd --- /dev/null +++ b/netwerk/test/unit/test_cookie_blacklist.js @@ -0,0 +1,16 @@ +const GOOD_COOKIE = "GoodCookie=OMNOMNOM"; + +function run_test() { + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + var cookieURI = ios.newURI("http://mozilla.org/test_cookie_blacklist.js", + null, null); + + var cookieService = Cc["@mozilla.org/cookieService;1"] + .getService(Ci.nsICookieService); + cookieService.setCookieStringFromHttp(cookieURI, cookieURI, null, "BadCookie1=\x01", null, null); + cookieService.setCookieStringFromHttp(cookieURI, cookieURI, null, "BadCookie2=\v", null, null); + cookieService.setCookieStringFromHttp(cookieURI, cookieURI, null, GOOD_COOKIE, null, null); + + var storedCookie = cookieService.getCookieString(cookieURI, null); + do_check_eq(storedCookie, GOOD_COOKIE); +} diff --git a/netwerk/test/unit/test_file_partial_inputstream.js b/netwerk/test/unit/test_file_partial_inputstream.js index 83fc858b90..fdbb02da46 100644 --- a/netwerk/test/unit/test_file_partial_inputstream.js +++ b/netwerk/test/unit/test_file_partial_inputstream.js @@ -146,7 +146,7 @@ function test_binary_portion(start, length) { test_seek_then_read, ]; - for each(test in streamTests) { + for (var test of streamTests) { let fileStream = new_file_input_stream(subFile); let partialStream = new_partial_file_input_stream(do_get_file(binary_test_file_name), start, length); @@ -207,7 +207,7 @@ function test_seek(dummy, partialFileStream, size) { partialFileStream.QueryInterface(Ci.nsISeekableStream); - tests = [ + var tests = [ [SET, 0], [SET, 5], [SET, 1000], @@ -259,7 +259,7 @@ function test_seek(dummy, partialFileStream, size) { ]; let pos = 0; - for each(test in tests) { + for (var test of tests) { let didThrow = false; try { partialFileStream.seek(test[0], test[1]); @@ -298,7 +298,7 @@ function test_seek_then_read(fileStreamA, fileStreamB, size) { let read = {}; - tests = [ + var tests = [ [SET, 0], [read, 1000], [read, 1000], @@ -324,7 +324,7 @@ function test_seek_then_read(fileStreamA, fileStreamB, size) { [read, 100], ]; - for each(test in tests) { + for (var test of tests) { if (test[0] === read) { let didThrowA = false; @@ -364,7 +364,7 @@ function test_text_portion(start, length) { test_seek_then_readline, ]; - for each(test in streamTests) { + for (var test of streamTests) { let fileStream = new_file_input_stream(subFile) .QueryInterface(Ci.nsILineInputStream); let partialStream = new_partial_file_input_stream(do_get_file(binary_test_file_name), @@ -400,7 +400,7 @@ function test_seek_then_readline(fileStreamA, fileStreamB, size) { let read = {}; - tests = [ + var tests = [ [SET, 0], [read, 5], [read, 5], @@ -430,7 +430,7 @@ function test_seek_then_readline(fileStreamA, fileStreamB, size) { [read, 1], ]; - for each(test in tests) { + for (var test of tests) { if (test[0] === read) { for (let i = 0; i < test[1]; ++i) { diff --git a/netwerk/test/unit/test_gre_resources.js b/netwerk/test/unit/test_gre_resources.js index df7d0d270a..12126daf5d 100644 --- a/netwerk/test/unit/test_gre_resources.js +++ b/netwerk/test/unit/test_gre_resources.js @@ -30,6 +30,6 @@ function check_file(file) { } function run_test() { - for each(let file in ["ua.css"]) + for (let file of ["ua.css"]) check_file(file) } diff --git a/netwerk/test/unit/test_packaged_app_bug1214079.js b/netwerk/test/unit/test_packaged_app_bug1214079.js new file mode 100644 index 0000000000..b41706c489 --- /dev/null +++ b/netwerk/test/unit/test_packaged_app_bug1214079.js @@ -0,0 +1,181 @@ +// This test is to verify Bug 1214079: when we failed to load a signed packaged +// content due to the bad signature, we are able to load in the next time since +// the cache is not cleaned up after signature verification. +// +// In order to verify it, we do two identical requests in a row, where the +// second request will be loaded from cache. The request is made for a signed +// package with bad signature. If the package cache isn't doomed after the +// first load, the second request will get valid data from the cache. + +Cu.import('resource://gre/modules/LoadContextInfo.jsm'); +Cu.import("resource://testing-common/httpd.js"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); + +var gPrefs = Cc["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + +var badSignature = [ + "manifest-signature: AAAAAAAAAAAAAAAAAAAAA\r", + "--7B0MKBI3UH\r", + "Content-Location: manifest.webapp\r", + "Content-Type: application/x-web-app-manifest+json\r", + "\r", + "{", + " \"name\": \"My App\",", + " \"moz-resources\": [", + " {", + " \"src\": \"page2.html\",", + " \"integrity\": \"JREF3JbXGvZ+I1KHtoz3f46ZkeIPrvXtG4VyFQrJ7II=\"", + " },", + " {", + " \"src\": \"index.html\",", + " \"integrity\": \"zEubR310nePwd30NThIuoCxKJdnz7Mf5z+dZHUbH1SE=\"", + " },", + " {", + " \"src\": \"scripts/script.js\",", + " \"integrity\": \"6TqtNArQKrrsXEQWu3D9ZD8xvDRIkhyV6zVdTcmsT5Q=\"", + " },", + " {", + " \"src\": \"scripts/library.js\",", + " \"integrity\": \"TN2ByXZiaBiBCvS4MeZ02UyNi44vED+KjdjLInUl4o8=\"", + " }", + " ],", + " \"moz-permissions\": [", + " {", + " \"systemXHR\": {", + " \"description\": \"Needed to download stuff\"", + " },", + " \"devicestorage:pictures\": {", + " \"description\": \"Need to load pictures\"", + " }", + " }", + " ],", + " \"package-identifier\": \"611FC2FE-491D-4A47-B3B3-43FBDF6F404F\",", + " \"moz-package-location\": \"https://example.com/myapp/app.pak\",", + " \"description\": \"A great app!\"", + "}\r", + "--7B0MKBI3UH\r", + "Content-Location: page2.html\r", + "Content-Type: text/html\r", + "\r", + "", + " page2.html", + "", + "\r", + "--7B0MKBI3UH\r", + "Content-Location: index.html\r", + "Content-Type: text/html\r", + "\r", + "", + " Last updated: 2015/10/01 14:10 PST", + "", + "\r", + "--7B0MKBI3UH\r", + "Content-Location: scripts/script.js\r", + "Content-Type: text/javascript\r", + "\r", + "// script.js", + "\r", + "--7B0MKBI3UH\r", + "Content-Location: scripts/library.js\r", + "Content-Type: text/javascript\r", + "\r", + "// library.js", + "\r", + "--7B0MKBI3UH--" +].join("\n"); + +XPCOMUtils.defineLazyGetter(this, "uri", function() { + return "http://localhost:" + httpserver.identity.primaryPort; +}); + +// The active http server initialized in run_test +var httpserver = null; +// The packaged app service initialized in run_test +var paservice = null; + +function run_test() +{ + // setup test + httpserver = new HttpServer(); + httpserver.registerPathHandler("/badSignature", badSignatureHandler); + httpserver.start(-1); + + do_register_cleanup(function() { + gPrefs.clearUserPref("network.http.signed-packages.enabled"); + }); + + paservice = Cc["@mozilla.org/network/packaged-app-service;1"] + .getService(Ci.nsIPackagedAppService); + ok(!!paservice, "test service exists"); + + gPrefs.setBoolPref("network.http.signed-packages.enabled", true); + + add_test(test_badSignature_package); + add_test(test_badSignature_package); + + // run tests + run_next_test(); +} + +//----------------------------------------------------------------------------- + +// The number of times this package has been requested +// This number might be reset by tests that use it +var packagedAppRequestsMade = 0; + +function badSignatureHandler(metadata, response) +{ + packagedAppRequestsMade++; + if (packagedAppRequestsMade == 2) { + // The second request returns a 304 not modified response + response.setStatusLine(metadata.httpVersion, 304, "Not Modified"); + response.bodyOutputStream.write("", 0); + return; + } + + response.setHeader("Content-Type", 'application/package'); + var body = badSignature; + response.bodyOutputStream.write(body, body.length); +} + +function getChannelForURL(url) { + let uri = createURI(url); + let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let principal = ssm.createCodebasePrincipal(uri, {}); + let tmpChannel = + NetUtil.newChannel({ + uri: url, + loadingPrincipal: principal, + contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER + }); + + tmpChannel.notificationCallbacks = + new LoadContextCallback(principal.appId, + principal.isInBrowserElement, + false, + false); + + return tmpChannel; +} + +function test_badSignature_package() +{ + let packagePath = "/badSignature"; + let url = uri + packagePath + "!//index.html"; + let channel = getChannelForURL(url); + + paservice.getResource(channel, { + onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; }, + onCacheEntryAvailable: function (entry, isnew, appcache, status) { + // XXX: it returns NS_ERROR_FAILURE for a missing package + // and NS_ERROR_FILE_NOT_FOUND for a missing file from the package. + // Maybe make them both return NS_ERROR_FILE_NOT_FOUND? + notEqual(status, Cr.NS_OK, "NOT FOUND"); + ok(!entry, "There should be no entry"); + run_next_test(); + } + }); +} diff --git a/netwerk/test/unit/test_packaged_app_channel.js b/netwerk/test/unit/test_packaged_app_channel.js index 171f48f814..82fa6aaf2c 100644 --- a/netwerk/test/unit/test_packaged_app_channel.js +++ b/netwerk/test/unit/test_packaged_app_channel.js @@ -51,7 +51,7 @@ Listener.prototype = { do_check_eq(this.available, count); // Need to consume stream to avoid assertion var str = new nsIBinaryInputStream(stream).readBytes(count); - equal(str, "\r\n \r\n \r\n ...\r\n \r\n ...\r\n\r\n", "check proper content"); + equal(str, "\n Last updated: 2015/10/01 14:10 PST\n\n", "check proper content"); } catch (ex) { do_throw(ex); @@ -74,7 +74,7 @@ Listener.prototype = { var testData = { packageHeader: "manifest-signature: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\r\n", content: [ - { headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "\r\n \r\n \r\n ...\r\n \r\n ...\r\n\r\n", type: "text/html" }, + { headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "\n Last updated: 2015/10/01 14:10 PST\n\n", type: "text/html" }, { headers: ["Content-Location: /scripts/app.js", "Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" }, { headers: ["Content-Location: /scripts/helpers/math.js", "Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" } ], @@ -95,6 +95,79 @@ var testData = { } } +var badSignature = "manifest-signature: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\r\n"; +var goodSignature = "manifest-signature: MIIF1AYJKoZIhvcNAQcCoIIFxTCCBcECAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3DQEHAaCCA54wggOaMIICgqADAgECAgECMA0GCSqGSIb3DQEBCwUAMHMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEkMCIGA1UEChMbRXhhbXBsZSBUcnVzdGVkIENvcnBvcmF0aW9uMRkwFwYDVQQDExBUcnVzdGVkIFZhbGlkIENBMB4XDTE1MDkxMDA4MDQzNVoXDTM1MDkxMDA4MDQzNVowdDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSQwIgYDVQQKExtFeGFtcGxlIFRydXN0ZWQgQ29ycG9yYXRpb24xGjAYBgNVBAMTEVRydXN0ZWQgQ29ycCBDZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAts8whjOzEbn/w1xkFJ67af7F/JPujBK91oyJekh2schIMzFau9pY8S1AiJQoJCulOJCJfUc8hBLKBZiGAkii+4Gpx6cVqMLe6C22MdD806Soxn8Dg4dQqbIvPuI4eeVKu5CEk80PW/BaFMmRvRHO62C7PILuH6yZeGHC4P7dTKpsk4CLxh/jRGXLC8jV2BCW0X+3BMbHBg53NoI9s1Gs7KGYnfOHbBP5wEFAa00RjHnubUaCdEBlC8Kl4X7p0S4RGb3rsB08wgFe9EmSZHIgcIm+SuVo7N4qqbI85qo2ulU6J8NN7ZtgMPHzrMhzgAgf/KnqPqwDIxnNmRNJmHTUYwIDAQABozgwNjAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAukH6cJUUj5faa8CuPCqrEa0PoLY4SYNnff9NI+TTAHkB9l+kOcFl5eo2EQOcWmZKYi7QLlWC4jy/KQYattO9FMaxiOQL4FAc6ZIbNyfwWBzZWyr5syYJTTTnkLq8A9pCKarN49+FqhJseycU+8EhJEJyP5pv5hLvDNTTHOQ6SXhASsiX8cjo3AY4bxA5pWeXuTZ459qDxOnQd+GrOe4dIeqflk0hA2xYKe3SfF+QlK8EO370B8Dj8RX230OATM1E3OtYyALe34KW3wM9Qm9rb0eViDnVyDiCWkhhQnw5yPg/XQfloug2itRYuCnfUoRt8xfeHgwz2Ymz8cUADn3KpTGCAf4wggH6AgEBMHgwczELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSQwIgYDVQQKExtFeGFtcGxlIFRydXN0ZWQgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFRydXN0ZWQgVmFsaWQgQ0ECAQIwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE1MTAwMTIxMTEwNlowIwYJKoZIhvcNAQkEMRYEFHAisUYrrt+gBxYFhZ5KQQusOmN3MA0GCSqGSIb3DQEBAQUABIIBACHW4V0BsPWOvWrGOTRj6mPpNbH/JI1bN2oyqQZrpUQoaBY+BbYxO7TY4Uwe+aeIR/TTPJznOMF/dl3Bna6TPabezU4ylg7TVFI6W7zC5f5DZKp+Xv6uTX6knUzbbW1fkJqMtE8hGUzYXc3/C++Ci6kuOzrpWOhk6DpJHeUO/ioV56H0+QK/oMAjYpEsOohaPqvTPNOBhMQ0OQP3bmuJ6HcjZ/oz96PpzXUPKT1tDe6VykIYkV5NvtC8Tu2lDbYvp9ug3gyDgdyNSV47y5i/iWkzEhsAJB+9Z50wKhplnkxxVHEXkB/6tmfvExvQ28gLd/VbaEGDX2ljCaTSUjhD0o0=\r\n"; + +var packageContent = [ + "--7B0MKBI3UH\r", + "Content-Location: manifest.webapp\r", + "Content-Type: application/x-web-app-manifest+json\r", + "\r", + "{", + " \"name\": \"My App\",", + " \"moz-resources\": [", + " {", + " \"src\": \"page2.html\",", + " \"integrity\": \"JREF3JbXGvZ+I1KHtoz3f46ZkeIPrvXtG4VyFQrJ7II=\"", + " },", + " {", + " \"src\": \"index.html\",", + " \"integrity\": \"zEubR310nePwd30NThIuoCxKJdnz7Mf5z+dZHUbH1SE=\"", + " },", + " {", + " \"src\": \"scripts/script.js\",", + " \"integrity\": \"6TqtNArQKrrsXEQWu3D9ZD8xvDRIkhyV6zVdTcmsT5Q=\"", + " },", + " {", + " \"src\": \"scripts/library.js\",", + " \"integrity\": \"TN2ByXZiaBiBCvS4MeZ02UyNi44vED+KjdjLInUl4o8=\"", + " }", + " ],", + " \"moz-permissions\": [", + " {", + " \"systemXHR\": {", + " \"description\": \"Needed to download stuff\"", + " },", + " \"devicestorage:pictures\": {", + " \"description\": \"Need to load pictures\"", + " }", + " }", + " ],", + " \"package-identifier\": \"611FC2FE-491D-4A47-B3B3-43FBDF6F404F\",", + " \"moz-package-location\": \"https://example.com/myapp/app.pak\",", + " \"description\": \"A great app!\"", + "}\r", + "--7B0MKBI3UH\r", + "Content-Location: page2.html\r", + "Content-Type: text/html\r", + "\r", + "", + " page2.html", + "", + "\r", + "--7B0MKBI3UH\r", + "Content-Location: index.html\r", + "Content-Type: text/html\r", + "\r", + "", + " Last updated: 2015/10/01 14:10 PST", + "", + "\r", + "--7B0MKBI3UH\r", + "Content-Location: scripts/script.js\r", + "Content-Type: text/javascript\r", + "\r", + "// script.js", + "\r", + "--7B0MKBI3UH\r", + "Content-Location: scripts/library.js\r", + "Content-Type: text/javascript\r", + "\r", + "// library.js", + "\r", + "--7B0MKBI3UH--" +].join("\n"); + function contentHandler(metadata, response) { response.setHeader("Content-Type", 'application/package'); @@ -108,16 +181,23 @@ function regularContentHandler(metadata, response) response.bodyOutputStream.write(body, body.length); } -function contentHandlerWithSignature(metadata, response) +function contentHandlerWithBadSignature(metadata, response) { response.setHeader("Content-Type", 'application/package'); - var body = testData.packageHeader + testData.getData(); + var body = badSignature + packageContent; + response.bodyOutputStream.write(body, body.length); +} + +function contentHandlerWithGoodSignature(metadata, response) +{ + response.setHeader("Content-Type", 'application/package'); + var body = goodSignature + packageContent; response.bodyOutputStream.write(body, body.length); } var httpserver = null; var originalPref = false; -var originalDevMode = false; +var originalSignedAppEnabled = false; function run_test() { @@ -125,46 +205,58 @@ function run_test() httpserver = new HttpServer(); httpserver.registerPathHandler("/package", contentHandler); httpserver.registerPathHandler("/regular", regularContentHandler); - httpserver.registerPathHandler("/package_with_signature", contentHandlerWithSignature); + httpserver.registerPathHandler("/package_with_good_signature", contentHandlerWithGoodSignature); + httpserver.registerPathHandler("/package_with_bad_signature", contentHandlerWithBadSignature); httpserver.start(-1); // Enable the feature and save the original pref value originalPref = Services.prefs.getBoolPref("network.http.enable-packaged-apps"); - originalDevMode = Services.prefs.getBoolPref("network.http.packaged-apps-developer-mode"); + originalSignedAppEnabled = Services.prefs.getBoolPref("network.http.signed-packages.enabled"); Services.prefs.setBoolPref("network.http.enable-packaged-apps", true); - Services.prefs.setBoolPref("network.http.packaged-apps-developer-mode", false); + Services.prefs.setBoolPref("network.http.signed-packages.enabled", true); do_register_cleanup(reset_pref); add_test(test_channel); add_test(test_channel_no_notificationCallbacks); add_test(test_channel_uris); - add_test(test_channel_with_signature); - add_test(test_channel_with_signature_dev_mode); + add_test(test_channel_with_bad_signature_from_trusted_origin); + add_test(test_channel_with_bad_signature); + add_test(test_channel_with_good_signature); // run tests run_next_test(); } -function test_channel_with_signature() { - var channel = make_channel(uri+"/package_with_signature!//index.html"); +function test_channel_with_bad_signature() { + var channel = make_channel(uri+"/package_with_bad_signature!//index.html"); channel.notificationCallbacks = new LoadContextCallback(1024, false, false, false); channel.asyncOpen(new Listener(function(l) { - // Since the manifest verification is not implemented yet, we should - // get NS_ERROR_FILE_NOT_FOUND if the package has a signature while - // not in developer mode. do_check_true(l.gotFileNotFound); run_next_test(); }), null); } -function test_channel_with_signature_dev_mode() { - Services.prefs.setBoolPref("network.http.packaged-apps-developer-mode", true); - var channel = make_channel(uri+"/package_with_signature!//index.html"); +function test_channel_with_bad_signature_from_trusted_origin() { + let pref = "network.http.signed-packages.trusted-origin"; + ok(!!Ci.nsISupportsString, "Ci.nsISupportsString"); + let origin = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); + origin.data = uri; + Services.prefs.setComplexValue(pref, Ci.nsISupportsString, origin); + var channel = make_channel(uri+"/package_with_bad_signature!//index.html"); + channel.notificationCallbacks = new LoadContextCallback(1024, false, false, false); + channel.asyncOpen(new Listener(function(l) { + do_check_true(l.gotStopRequestOK); + Services.prefs.clearUserPref(pref); + run_next_test(); + }), null); +} + +function test_channel_with_good_signature() { + var channel = make_channel(uri+"/package_with_good_signature!//index.html"); channel.notificationCallbacks = new LoadContextCallback(1024, false, false, false); channel.asyncOpen(new Listener(function(l) { do_check_true(l.gotStopRequestOK); - Services.prefs.setBoolPref("network.http.packaged-apps-developer-mode", false); run_next_test(); }), null); } @@ -205,5 +297,5 @@ function check_regular_response(request, buffer) { function reset_pref() { // Set the pref to its original value Services.prefs.setBoolPref("network.http.enable-packaged-apps", originalPref); - Services.prefs.setBoolPref("network.http.packaged-apps-developer-mode", originalDevMode); + Services.prefs.setBoolPref("network.http.signed-packages.enabled", originalSignedAppEnabled); } diff --git a/netwerk/test/unit/test_packaged_app_service.js b/netwerk/test/unit/test_packaged_app_service.js index 8a7541bbd7..a585616c4d 100644 --- a/netwerk/test/unit/test_packaged_app_service.js +++ b/netwerk/test/unit/test_packaged_app_service.js @@ -113,6 +113,77 @@ var testData = { } } +var signedPackage = [ + "manifest-signature: MIIF1AYJKoZIhvcNAQcCoIIFxTCCBcECAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3DQEHAaCCA54wggOaMIICgqADAgECAgECMA0GCSqGSIb3DQEBCwUAMHMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEkMCIGA1UEChMbRXhhbXBsZSBUcnVzdGVkIENvcnBvcmF0aW9uMRkwFwYDVQQDExBUcnVzdGVkIFZhbGlkIENBMB4XDTE1MDkxMDA4MDQzNVoXDTM1MDkxMDA4MDQzNVowdDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSQwIgYDVQQKExtFeGFtcGxlIFRydXN0ZWQgQ29ycG9yYXRpb24xGjAYBgNVBAMTEVRydXN0ZWQgQ29ycCBDZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAts8whjOzEbn/w1xkFJ67af7F/JPujBK91oyJekh2schIMzFau9pY8S1AiJQoJCulOJCJfUc8hBLKBZiGAkii+4Gpx6cVqMLe6C22MdD806Soxn8Dg4dQqbIvPuI4eeVKu5CEk80PW/BaFMmRvRHO62C7PILuH6yZeGHC4P7dTKpsk4CLxh/jRGXLC8jV2BCW0X+3BMbHBg53NoI9s1Gs7KGYnfOHbBP5wEFAa00RjHnubUaCdEBlC8Kl4X7p0S4RGb3rsB08wgFe9EmSZHIgcIm+SuVo7N4qqbI85qo2ulU6J8NN7ZtgMPHzrMhzgAgf/KnqPqwDIxnNmRNJmHTUYwIDAQABozgwNjAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAukH6cJUUj5faa8CuPCqrEa0PoLY4SYNnff9NI+TTAHkB9l+kOcFl5eo2EQOcWmZKYi7QLlWC4jy/KQYattO9FMaxiOQL4FAc6ZIbNyfwWBzZWyr5syYJTTTnkLq8A9pCKarN49+FqhJseycU+8EhJEJyP5pv5hLvDNTTHOQ6SXhASsiX8cjo3AY4bxA5pWeXuTZ459qDxOnQd+GrOe4dIeqflk0hA2xYKe3SfF+QlK8EO370B8Dj8RX230OATM1E3OtYyALe34KW3wM9Qm9rb0eViDnVyDiCWkhhQnw5yPg/XQfloug2itRYuCnfUoRt8xfeHgwz2Ymz8cUADn3KpTGCAf4wggH6AgEBMHgwczELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSQwIgYDVQQKExtFeGFtcGxlIFRydXN0ZWQgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFRydXN0ZWQgVmFsaWQgQ0ECAQIwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE1MTAwMTIxMTEwNlowIwYJKoZIhvcNAQkEMRYEFHAisUYrrt+gBxYFhZ5KQQusOmN3MA0GCSqGSIb3DQEBAQUABIIBACHW4V0BsPWOvWrGOTRj6mPpNbH/JI1bN2oyqQZrpUQoaBY+BbYxO7TY4Uwe+aeIR/TTPJznOMF/dl3Bna6TPabezU4ylg7TVFI6W7zC5f5DZKp+Xv6uTX6knUzbbW1fkJqMtE8hGUzYXc3/C++Ci6kuOzrpWOhk6DpJHeUO/ioV56H0+QK/oMAjYpEsOohaPqvTPNOBhMQ0OQP3bmuJ6HcjZ/oz96PpzXUPKT1tDe6VykIYkV5NvtC8Tu2lDbYvp9ug3gyDgdyNSV47y5i/iWkzEhsAJB+9Z50wKhplnkxxVHEXkB/6tmfvExvQ28gLd/VbaEGDX2ljCaTSUjhD0o0=\r", + "--7B0MKBI3UH\r", + "Content-Location: manifest.webapp\r", + "Content-Type: application/x-web-app-manifest+json\r", + "\r", + "{", + " \"name\": \"My App\",", + " \"moz-resources\": [", + " {", + " \"src\": \"page2.html\",", + " \"integrity\": \"JREF3JbXGvZ+I1KHtoz3f46ZkeIPrvXtG4VyFQrJ7II=\"", + " },", + " {", + " \"src\": \"index.html\",", + " \"integrity\": \"zEubR310nePwd30NThIuoCxKJdnz7Mf5z+dZHUbH1SE=\"", + " },", + " {", + " \"src\": \"scripts/script.js\",", + " \"integrity\": \"6TqtNArQKrrsXEQWu3D9ZD8xvDRIkhyV6zVdTcmsT5Q=\"", + " },", + " {", + " \"src\": \"scripts/library.js\",", + " \"integrity\": \"TN2ByXZiaBiBCvS4MeZ02UyNi44vED+KjdjLInUl4o8=\"", + " }", + " ],", + " \"moz-permissions\": [", + " {", + " \"systemXHR\": {", + " \"description\": \"Needed to download stuff\"", + " },", + " \"devicestorage:pictures\": {", + " \"description\": \"Need to load pictures\"", + " }", + " }", + " ],", + " \"package-identifier\": \"611FC2FE-491D-4A47-B3B3-43FBDF6F404F\",", + " \"moz-package-location\": \"https://example.com/myapp/app.pak\",", + " \"description\": \"A great app!\"", + "}\r", + "--7B0MKBI3UH\r", + "Content-Location: page2.html\r", + "Content-Type: text/html\r", + "\r", + "", + " page2.html", + "", + "\r", + "--7B0MKBI3UH\r", + "Content-Location: index.html\r", + "Content-Type: text/html\r", + "\r", + "", + " Last updated: 2015/10/01 14:10 PST", + "", + "\r", + "--7B0MKBI3UH\r", + "Content-Location: scripts/script.js\r", + "Content-Type: text/javascript\r", + "\r", + "// script.js", + "\r", + "--7B0MKBI3UH\r", + "Content-Location: scripts/library.js\r", + "Content-Type: text/javascript\r", + "\r", + "// library.js", + "\r", + "--7B0MKBI3UH--" +].join("\n"); + XPCOMUtils.defineLazyGetter(this, "uri", function() { return "http://localhost:" + httpserver.identity.primaryPort; }); @@ -142,18 +213,16 @@ function run_test() httpserver.registerPathHandler("/signedPackage", signedPackagedAppContentHandler); httpserver.start(-1); - // We will enable the developer mode in 'test_signed_package_callback'. - // So restore it after testing. - // - // TODO: To be removed in Bug 1178518. do_register_cleanup(function() { - gPrefs.clearUserPref("network.http.packaged-apps-developer-mode"); + gPrefs.clearUserPref("network.http.signed-packages.enabled"); }); paservice = Cc["@mozilla.org/network/packaged-app-service;1"] .getService(Ci.nsIPackagedAppService); ok(!!paservice, "test service exists"); + gPrefs.setBoolPref("network.http.signed-packages.enabled", true); + add_test(test_bad_args); add_test(test_callback_gets_called); @@ -495,7 +564,7 @@ function test_worse_package_5() { function signedPackagedAppContentHandler(metadata, response) { response.setHeader("Content-Type", 'application/package'); - var body = testData.packageHeader + testData.getData(); + var body = signedPackage; response.bodyOutputStream.write(body, body.length); } @@ -513,9 +582,6 @@ let dummyCacheListener = { function test_signed_package_callback() { - // TODO: To be removed in Bug 1178518. - gPrefs.setBoolPref("network.http.packaged-apps-developer-mode", true); - packagePath = "/signedPackage"; let url = uri + packagePath + "!//index.html"; let channel = getChannelForURL(url, { @@ -534,6 +600,9 @@ function test_signed_package_callback() iid.equals(Ci.nsIPackagedAppChannelListener)) { return this; } + if (iid.equals(Ci.nsILoadContext)) { + return new LoadContextCallback(1024, false, false, false); + } throw Cr.NS_ERROR_NO_INTERFACE; }, }); @@ -560,6 +629,9 @@ function test_unsigned_package_callback() iid.equals(Ci.nsIPackagedAppChannelListener)) { return this; } + if (iid.equals(Ci.nsILoadContext)) { + return new LoadContextCallback(1024, false, false, false); + } throw Cr.NS_ERROR_NO_INTERFACE; }, }); diff --git a/netwerk/test/unit/test_packaged_app_utils.js b/netwerk/test/unit/test_packaged_app_utils.js index b660c97b4b..f7ddc8e937 100644 --- a/netwerk/test/unit/test_packaged_app_utils.js +++ b/netwerk/test/unit/test_packaged_app_utils.js @@ -98,7 +98,7 @@ function test_verify_manifest(aHeader, aManifest, aShouldSucceed) { run_next_test(); } }; - packagedAppUtils.verifyManifest(aHeader, aManifest, fakeVerifier); + packagedAppUtils.verifyManifest(aHeader, aManifest, fakeVerifier, false); } } diff --git a/netwerk/test/unit/test_packaged_app_verifier.js b/netwerk/test/unit/test_packaged_app_verifier.js index dce1ca3ab2..4f351e704e 100644 --- a/netwerk/test/unit/test_packaged_app_verifier.js +++ b/netwerk/test/unit/test_packaged_app_verifier.js @@ -38,19 +38,10 @@ let gLoadContextInfoFactory = const kUriIdx = 0; const kStatusCodeIdx = 1; const kVerificationSuccessIdx = 2; - -function enable_developer_mode() -{ - gPrefs.setBoolPref("network.http.packaged-apps-developer-mode", true); -} - -function reset_developer_mode() -{ - gPrefs.clearUserPref("network.http.packaged-apps-developer-mode"); -} +const kContentIdx = 3; function createVerifierListener(aExpecetedCallbacks, - aExpectedOrigin, + aExpectedPackageId, aExpectedIsSigned, aPackageCacheEntry) { let cnt = 0; @@ -77,33 +68,47 @@ function createVerifierListener(aExpecetedCallbacks, if (isManifest) { // Check if the verifier got the right package info. - equal(gVerifier.packageOrigin, aExpectedOrigin, 'package origin'); + equal(gVerifier.packageIdentifier, aExpectedPackageId, 'package identifier'); equal(gVerifier.isPackageSigned, aExpectedIsSigned, 'is package signed'); // Check if the verifier wrote the signed package origin to the cache. ok(!!aPackageCacheEntry, aPackageCacheEntry.key); - let signePakOriginInCache = aPackageCacheEntry.getMetaDataElement('signed-pak-origin'); - equal(signePakOriginInCache, - (aExpectedIsSigned ? aExpectedOrigin : ''), - 'signed-pak-origin in cache'); + let signePakIdInCache = aPackageCacheEntry.getMetaDataElement('package-id'); + equal(signePakIdInCache, + (aExpectedIsSigned ? aExpectedPackageId : ''), + 'package-id in cache'); } if (isLastPart) { - reset_developer_mode(); run_next_test(); } }, }; }; -function feedResources(aExpectedCallbacks) { +function feedData(aString) { + let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]. + createInstance(Ci.nsIStringInputStream); + stringStream.setData(aString, aString.length); + gVerifier.onDataAvailable(null, null, stringStream, 0, aString.length); +} + +function feedResources(aExpectedCallbacks, aSignature) { for (let i = 0; i < aExpectedCallbacks.length; i++) { let expectedCallback = aExpectedCallbacks[i]; let isLastPart = (i === aExpectedCallbacks.length - 1); + // Start request. let uri = gIoService.newURI(expectedCallback[kUriIdx], null, null); gVerifier.onStartRequest(null, uri); + // Feed data at once. + let contentString = expectedCallback[kContentIdx]; + if (contentString !== undefined) { + feedData(contentString); + } + + // Stop request. let info = gVerifier.createResourceCacheInfo(uri, null, expectedCallback[kStatusCodeIdx], @@ -121,12 +126,12 @@ function createPackageCache(aPackageUriAsAscii, aLoadContextInfo) { return cacheStorage.openTruncate(uri, ''); } -function test_no_signature(aDeveloperMode) { +function test_no_signature(aBypassVerification) { const kOrigin = 'http://foo.com'; - aDeveloperMode = !!aDeveloperMode; + aBypassVerification = !!aBypassVerification; - // If the package has no signature and not in developer mode, the package is unsigned + // If the package has no signature, the package is unsigned // but the verification result is always true. const expectedCallbacks = [ @@ -142,79 +147,80 @@ function test_no_signature(aDeveloperMode) { let isPackageSigned = false; // We only require the package URL to be different in each test case. - let packageUriString = kOrigin + '/pak' + (aDeveloperMode ? '-dev' : ''); + let packageUriString = kOrigin + '/pak' + (aBypassVerification ? '-dev' : ''); let packageCacheEntry = createPackageCache(packageUriString, gLoadContextInfoFactory.default); let verifierListener = createVerifierListener(expectedCallbacks, - kOrigin, + '', isPackageSigned, packageCacheEntry); gVerifier.init(verifierListener, kOrigin, '', packageCacheEntry); - feedResources(expectedCallbacks); + feedResources(expectedCallbacks, ''); } -function test_invalid_signature(aDeveloperMode) { +function test_invalid_signature(aBypassVerification) { const kOrigin = 'http://bar.com'; - aDeveloperMode = !!aDeveloperMode; + aBypassVerification = !!aBypassVerification; // Since we haven't implemented signature verification, the verification always // fails if the signature exists. - let verificationResult = aDeveloperMode; // Verification always success in developer mode. - let isPackageSigned = aDeveloperMode; // Package is always considered as signed in developer mode. + let verificationResult = aBypassVerification; // Verification always success in developer mode. + let isPackageSigned = aBypassVerification; // Package is always considered as signed in developer mode. + + const kPackagedId = '611FC2FE-491D-4A47-B3B3-43FBDF6F404F'; + const kManifestContent = 'Content-Location: manifest.webapp\r\n' + + 'Content-Type: application/x-web-app-manifest+json\r\n' + + '\r\n' + + '{ "package-identifier": "' + kPackagedId + '" }'; const expectedCallbacks = [ - // URL statusCode verificationResult - [kOrigin + '/manifest', Cr.NS_OK, verificationResult], - [kOrigin + '/1.html', Cr.NS_OK, verificationResult], - [kOrigin + '/2.js', Cr.NS_OK, verificationResult], - [kOrigin + '/3.jpg', Cr.NS_OK, verificationResult], - [kOrigin + '/4.html', Cr.NS_OK, verificationResult], - [kOrigin + '/5.css', Cr.NS_OK, verificationResult], + // URL statusCode verificationResult content + [kOrigin + '/manifest', Cr.NS_OK, verificationResult, kManifestContent], + [kOrigin + '/1.html', Cr.NS_OK, verificationResult, 'abc'], + [kOrigin + '/2.js', Cr.NS_OK, verificationResult, 'abc'], + [kOrigin + '/3.jpg', Cr.NS_OK, verificationResult, 'abc'], + [kOrigin + '/4.html', Cr.NS_OK, verificationResult, 'abc'], + [kOrigin + '/5.css', Cr.NS_OK, verificationResult, 'abc'], ]; - let packageUriString = kOrigin + '/pak' + (aDeveloperMode ? '-dev' : ''); + let packageUriString = kOrigin + '/pak' + (aBypassVerification ? '-dev' : ''); let packageCacheEntry = createPackageCache(packageUriString, gLoadContextInfoFactory.private); let verifierListener = createVerifierListener(expectedCallbacks, - kOrigin, + aBypassVerification ? kPackagedId : '', isPackageSigned, packageCacheEntry); - gVerifier.init(verifierListener, kOrigin, 'invalid signature', packageCacheEntry); + let signature = 'manifest-signature: 11111111111111111111111'; + gVerifier.init(verifierListener, kOrigin, signature, packageCacheEntry); - feedResources(expectedCallbacks); + feedResources(expectedCallbacks, signature); } -function test_no_signature_developer_mode() -{ - enable_developer_mode() - test_no_signature(true); -} - -function test_invalid_signature_developer_mode() -{ - enable_developer_mode() +function test_invalid_signature_bypass_verification() { + let pref = "network.http.signed-packages.trusted-origin"; + ok(!!Ci.nsISupportsString, "Ci.nsISupportsString"); + let origin = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); + origin.data = "http://bar.com"; + gPrefs.setComplexValue(pref, Ci.nsISupportsString, origin); test_invalid_signature(true); + gPrefs.clearUserPref(pref); } function run_test() { ok(!!gVerifier); - // Test cases in non-developer mode. add_test(test_no_signature); add_test(test_invalid_signature); - - // Test cases in developer mode. - add_test(test_no_signature_developer_mode); - add_test(test_invalid_signature_developer_mode); + add_test(test_invalid_signature_bypass_verification); // run tests run_next_test(); diff --git a/netwerk/test/unit/test_readline.js b/netwerk/test/unit/test_readline.js index 81aa66ed19..798b2c2a78 100644 --- a/netwerk/test/unit/test_readline.js +++ b/netwerk/test/unit/test_readline.js @@ -34,13 +34,13 @@ function err(file, lineNo, msg) { function run_test() { - for each (var test in test_array) { + for (var test of test_array) { var lineStream = new_line_input_stream(test.file); var lineNo = 0; var more = false; var line = {}; more = lineStream.readLine(line); - for each (var check in test.lines) { + for (var check of test.lines) { ++lineNo; if (lineNo == test.lines.length) { if (more) err(test.file, lineNo, "There should be no more data after the last line"); diff --git a/netwerk/test/unit/test_socks.js b/netwerk/test/unit/test_socks.js index cea5307aa6..cbfd9275e7 100644 --- a/netwerk/test/unit/test_socks.js +++ b/netwerk/test/unit/test_socks.js @@ -428,7 +428,7 @@ SocksTestServer.prototype = { var argv = []; // marshaled: socks_ver|server_port|dest_host|dest_port| - for each (var test in this.test_cases) { + for (var test of this.test_cases) { var arg = test.type + '|' + String(socks_listen_port) + '|' + test.host + '|' + test.port + '|'; @@ -467,7 +467,7 @@ SocksTestServer.prototype = { } this.client_subprocess = null; } - for each (var client in this.client_connections) + for (var client of this.client_connections) client.close(); this.client_connections = []; if (this.listener) { diff --git a/netwerk/test/unit/test_standardurl.js b/netwerk/test/unit/test_standardurl.js index 81de347b1b..e30176c566 100644 --- a/netwerk/test/unit/test_standardurl.js +++ b/netwerk/test/unit/test_standardurl.js @@ -50,7 +50,7 @@ function test_setEmptyPath() ["http://example.com:80/a", "http://example.com/tests/dom/tests"], ].map(pairToURLs); - for each (var [provided, target] in pairs) + for (var [provided, target] of pairs) { symmetricEquality(false, target, provided); @@ -77,7 +77,7 @@ function test_setQuery() ["http://example.com/?f", "http://example.com/?foo"], ].map(pairToURLs); - for each (var [provided, target] in pairs) { + for (var [provided, target] of pairs) { symmetricEquality(false, provided, target); provided.query = "foo"; @@ -135,7 +135,7 @@ function test_setRef() ["http://example.com:80/a", "xxxxxxxxxxxxxx", "http://example.com:80/a#xxxxxxxxxxxxxx"], ]; - for each (var [before, ref, result] in tests) + for (var [before, ref, result] of tests) { /* Test1: starting with empty ref */ var a = stringToURL(before); @@ -220,8 +220,9 @@ function test_clearedSpec() symmetricEquality(true, url, ref); } -function test_escapeQueryBrackets() +function test_escapeBrackets() { + // Query var url = stringToURL("http://example.com/?a[x]=1"); do_check_eq(url.spec, "http://example.com/?a[x]=1"); @@ -233,6 +234,14 @@ function test_escapeQueryBrackets() url = stringToURL("http://[2001::1]/?a%5Bx%5D=1"); do_check_eq(url.spec, "http://[2001::1]/?a%5Bx%5D=1"); + + // Path + url = stringToURL("http://example.com/brackets[x]/test"); + do_check_eq(url.spec, "http://example.com/brackets[x]/test"); + + url = stringToURL("http://example.com/a%5Bx%5D/test"); + do_check_eq(url.spec, "http://example.com/a%5Bx%5D/test"); + } function test_apostropheEncoding() @@ -243,6 +252,27 @@ function test_apostropheEncoding() do_check_eq(url.spec, "http://example.com/dir'/file'.ext'"); } +function test_accentEncoding() +{ + var url = stringToURL("http://example.com/?hello=`"); + do_check_eq(url.spec, "http://example.com/?hello=`"); + do_check_eq(url.query, "hello=`"); + + url = stringToURL("http://example.com/?hello=%2C"); + do_check_eq(url.spec, "http://example.com/?hello=%2C"); + do_check_eq(url.query, "hello=%2C"); +} + +function test_percentDecoding() +{ + var url = stringToURL("http://%70%61%73%74%65%62%69%6E.com"); + do_check_eq(url.spec, "http://pastebin.com/"); + + // We shouldn't unescape characters that are not allowed in the hostname. + url = stringToURL("http://example.com%0a%23.google.com/"); + do_check_eq(url.spec, "http://example.com%0a%23.google.com/"); +} + function run_test() { test_setEmptyPath(); @@ -251,6 +281,8 @@ function run_test() test_ipv6(); test_ipv6_fail(); test_clearedSpec(); - test_escapeQueryBrackets(); + test_escapeBrackets(); test_apostropheEncoding(); + test_accentEncoding(); + test_percentDecoding(); } diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 4b69c32d84..2c4596c7f8 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -340,3 +340,7 @@ firefox-appdir = browser [test_dns_disable_ipv6.js] [test_packaged_app_service_paths.js] [test_bug1195415.js] +[test_cookie_blacklist.js] +[test_packaged_app_bug1214079.js] +[test_bug412457.js] + diff --git a/python/mozbuild/mozbuild/backend/fastermake.py b/python/mozbuild/mozbuild/backend/fastermake.py index 50f41b7bc0..b6f63bdb04 100644 --- a/python/mozbuild/mozbuild/backend/fastermake.py +++ b/python/mozbuild/mozbuild/backend/fastermake.py @@ -207,6 +207,7 @@ class FasterMakeBackend(CommonBackend): ) pp.out = JarManifestParser() pp.do_include(obj.path) + self.backend_input_files |= pp.includes for jarinfo in pp.out: install_target = obj.install_target @@ -301,6 +302,7 @@ class FasterMakeBackend(CommonBackend): mk.create_rule(['default']) mk.add_statement('TOPSRCDIR = %s' % self.environment.topsrcdir) mk.add_statement('TOPOBJDIR = %s' % self.environment.topobjdir) + mk.add_statement('BACKEND = %s' % self._backend_output_list_file) # Add a few necessary variables inherited from configure for var in ( @@ -331,6 +333,10 @@ class FasterMakeBackend(CommonBackend): mk.create_rule([target]).add_dependencies( '$(TOPOBJDIR)/%s' % d for d in deps) + # Add backend dependencies: + mk.create_rule([self._backend_output_list_file]).add_dependencies( + self.backend_input_files) + mk.add_statement('include $(TOPSRCDIR)/config/faster/rules.mk') for base, install_manifest in self._install_manifests.iteritems(): diff --git a/security/apps/AppTrustDomain.cpp b/security/apps/AppTrustDomain.cpp index a74b6da8eb..f85172ad83 100644 --- a/security/apps/AppTrustDomain.cpp +++ b/security/apps/AppTrustDomain.cpp @@ -8,7 +8,13 @@ #include "certdb.h" #include "pkix/pkixnss.h" #include "mozilla/ArrayUtils.h" +#include "MainThreadUtils.h" +#include "mozilla/Preferences.h" +#include "nsComponentManagerUtils.h" +#include "nsIFile.h" +#include "nsIFileStreams.h" #include "nsIX509CertDB.h" +#include "nsNetUtil.h" #include "nsNSSCertificate.h" #include "prerror.h" #include "secerr.h" @@ -34,9 +40,15 @@ using namespace mozilla::pkix; extern PRLogModuleInfo* gPIPNSSLog; static const unsigned int DEFAULT_MIN_RSA_BITS = 2048; +static char kDevImportedDER[] = + "network.http.signed-packages.developer-root"; namespace mozilla { namespace psm { +StaticMutex AppTrustDomain::sMutex; +nsAutoArrayPtr AppTrustDomain::sDevImportedDERData(nullptr); +unsigned int AppTrustDomain::sDevImportedDERLen = 0; + AppTrustDomain::AppTrustDomain(ScopedCERTCertList& certChain, void* pinArg) : mCertChain(certChain) , mPinArg(pinArg) @@ -101,6 +113,53 @@ AppTrustDomain::SetTrustedRoot(AppTrustedRoot trustedRoot) trustedDER.len = mozilla::ArrayLength(privilegedPackageRoot); break; + case nsIX509CertDB::DeveloperImportedRoot: { + StaticMutexAutoLock lock(sMutex); + if (!sDevImportedDERData) { + MOZ_ASSERT(!NS_IsMainThread()); + nsCOMPtr file(do_CreateInstance("@mozilla.org/file/local;1")); + if (!file) { + PR_SetError(SEC_ERROR_IO, 0); + return SECFailure; + } + nsresult rv = file->InitWithNativePath( + Preferences::GetCString(kDevImportedDER)); + if (NS_FAILED(rv)) { + PR_SetError(SEC_ERROR_IO, 0); + return SECFailure; + } + + nsCOMPtr inputStream; + NS_NewLocalFileInputStream(getter_AddRefs(inputStream), file, -1, -1, + nsIFileInputStream::CLOSE_ON_EOF); + if (!inputStream) { + PR_SetError(SEC_ERROR_IO, 0); + return SECFailure; + } + + uint64_t length; + rv = inputStream->Available(&length); + if (NS_FAILED(rv)) { + PR_SetError(SEC_ERROR_IO, 0); + return SECFailure; + } + + char* data = new char[length]; + rv = inputStream->Read(data, length, &sDevImportedDERLen); + if (NS_FAILED(rv)) { + PR_SetError(SEC_ERROR_IO, 0); + return SECFailure; + } + + MOZ_ASSERT(length == sDevImportedDERLen); + sDevImportedDERData = reinterpret_cast(data); + } + + trustedDER.data = sDevImportedDERData; + trustedDER.len = sDevImportedDERLen; + break; + } + default: PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; diff --git a/security/apps/AppTrustDomain.h b/security/apps/AppTrustDomain.h index e494874101..fb742a8b17 100644 --- a/security/apps/AppTrustDomain.h +++ b/security/apps/AppTrustDomain.h @@ -8,6 +8,8 @@ #define mozilla_psm_AppsTrustDomain_h #include "pkix/pkixtypes.h" +#include "mozilla/StaticMutex.h" +#include "nsAutoPtr.h" #include "nsDebug.h" #include "nsIX509CertDB.h" #include "ScopedNSSTypes.h" @@ -69,6 +71,10 @@ private: void* mPinArg; // non-owning! ScopedCERTCertificate mTrustedRoot; unsigned int mMinRSABits; + + static StaticMutex sMutex; + static nsAutoArrayPtr sDevImportedDERData; + static unsigned int sDevImportedDERLen; }; } } // namespace mozilla::psm diff --git a/security/manager/ssl/nsIX509CertDB.idl b/security/manager/ssl/nsIX509CertDB.idl index d1ba93edeb..345290433e 100644 --- a/security/manager/ssl/nsIX509CertDB.idl +++ b/security/manager/ssl/nsIX509CertDB.idl @@ -46,7 +46,7 @@ interface nsIVerifySignedManifestCallback : nsISupports * This represents a service to access and manipulate * X.509 certificates stored in a database. */ -[scriptable, uuid(0a47571d-602c-4b21-9f52-c3d0e681d83a)] +[scriptable, uuid(a36c45fb-f7b5-423e-a0f7-ea1eb4fd60b5)] interface nsIX509CertDB : nsISupports { /** @@ -319,6 +319,14 @@ interface nsIX509CertDB : nsISupports { const AppTrustedRoot AddonsPublicRoot = 7; const AppTrustedRoot AddonsStageRoot = 8; const AppTrustedRoot PrivilegedPackageRoot = 9; + /* + * If DeveloperImportedRoot is set as trusted root, a CA from local file + * system will be imported. Only used when preference + * "network.http.packaged-apps-developer-mode" is set. + * The path of the CA is specified by preference + * "network.http.packaged-apps-developer-trusted-root". + */ + const AppTrustedRoot DeveloperImportedRoot = 10; void openSignedAppFileAsync(in AppTrustedRoot trustedRoot, in nsIFile aJarFile, in nsIOpenSignedAppFileCallback callback); diff --git a/toolkit/components/passwordmgr/test/test_prompt_async.html b/toolkit/components/passwordmgr/test/test_prompt_async.html index 3b9ee7b0ad..04d44b5a88 100644 --- a/toolkit/components/passwordmgr/test/test_prompt_async.html +++ b/toolkit/components/passwordmgr/test/test_prompt_async.html @@ -13,7 +13,7 @@ var prefs = Cc["@mozilla.org/preferences-service;1"]. getService(Ci.nsIPrefBranch); - prefs.setIntPref("network.auth.allow-subresource-auth", 2); + prefs.setIntPref("network.auth.subresource-http-auth-allow", 2); // Class monitoring number of open dialog windows // It checks there is always open just a single dialog per application function dialogMonitor() { diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 54f7986e60..a38c2d990a 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -7223,6 +7223,14 @@ "n_values": 10, "description": "How often would blocked mixed content be allowed if HSTS upgrades were allowed? 0=display/no-HSTS, 1=display/HSTS, 2=active/no-HSTS, 3=active/HSTS" }, + "COOKIE_SCHEME_SECURITY": { + "alert_emails": ["seceng@mozilla.org"], + "expires_in_version": "50", + "kind": "enumerated", + "n_values": 10, + "releaseChannelCollection": "opt-out", + "description": "How often are secure cookies set from non-secure origins, and vice-versa? 0=nonsecure/http, 1=nonsecure/https, 2=secure/http, 3=secure/https" + }, "NTLM_MODULE_USED_2": { "expires_in_version": "never", "kind": "enumerated", diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js b/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js index 1a524336eb..ff01717130 100644 --- a/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js +++ b/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js @@ -10,7 +10,7 @@ function test() { var prefs = Cc["@mozilla.org/preferences-service;1"]. getService(Ci.nsIPrefBranch); - prefs.setIntPref("network.auth.allow-subresource-auth", 2); + prefs.setIntPref("network.auth.subresource-http-auth-allow", 2); var pm = Services.perms; pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); diff --git a/uriloader/prefetch/OfflineCacheUpdateParent.cpp b/uriloader/prefetch/OfflineCacheUpdateParent.cpp index ab54fc39da..e50432e800 100644 --- a/uriloader/prefetch/OfflineCacheUpdateParent.cpp +++ b/uriloader/prefetch/OfflineCacheUpdateParent.cpp @@ -52,11 +52,9 @@ NS_IMPL_ISUPPORTS(OfflineCacheUpdateParent, // OfflineCacheUpdateParent //----------------------------------------------------------------------------- -// TODO: Bug 1191740 - Add OriginAttributes in TabContext -OfflineCacheUpdateParent::OfflineCacheUpdateParent(uint32_t aAppId, - bool aIsInBrowser) +OfflineCacheUpdateParent::OfflineCacheUpdateParent(const OriginAttributes& aAttrs) : mIPCClosed(false) - , mOriginAttributes(aAppId, aIsInBrowser) + , mOriginAttributes(aAttrs) { // Make sure the service has been initialized nsOfflineCacheUpdateService::EnsureService(); diff --git a/uriloader/prefetch/OfflineCacheUpdateParent.h b/uriloader/prefetch/OfflineCacheUpdateParent.h index 60465162bf..c7a83223fa 100644 --- a/uriloader/prefetch/OfflineCacheUpdateParent.h +++ b/uriloader/prefetch/OfflineCacheUpdateParent.h @@ -45,7 +45,7 @@ public: mIPCClosed = true; } - OfflineCacheUpdateParent(uint32_t aAppId, bool aIsInBrowser); + explicit OfflineCacheUpdateParent(const mozilla::OriginAttributes& aAttrs); virtual void ActorDestroy(ActorDestroyReason aWhy) override; private: diff --git a/webapprt/CommandLineHandler.js b/webapprt/CommandLineHandler.js new file mode 100644 index 0000000000..02aad8bf13 --- /dev/null +++ b/webapprt/CommandLineHandler.js @@ -0,0 +1,76 @@ +/* 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/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +function CommandLineHandler() {} + +CommandLineHandler.prototype = { + classID: Components.ID("{6d69c782-40a3-469b-8bfd-3ee366105a4a}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]), + + handle: function handle(cmdLine) { + let args = Cc["@mozilla.org/hash-property-bag;1"]. + createInstance(Ci.nsIWritablePropertyBag); + let inTestMode = this._handleTestMode(cmdLine, args); + + if (inTestMode) { + // Open the mochitest shim window, which configures the runtime for tests. + Services.ww.openWindow(null, + "chrome://webapprt/content/mochitest.xul", + "_blank", + "chrome,dialog=no", + args); + } else { + // We're opening the window here in order to show it as soon as possible. + let window = Services.ww.openWindow(null, + "chrome://webapprt/content/webapp.xul", + "_blank", + "chrome,dialog=no,resizable,scrollbars,centerscreen", + null); + // Load the module to start up the app + Cu.import("resource://webapprt/modules/Startup.jsm"); + startup(window).then(null, function (aError) { + dump("Error: " + aError + "\n"); + Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit); + }); + } + }, + + _handleTestMode: function _handleTestMode(cmdLine, args) { + // -test-mode [url] + let idx = cmdLine.findFlag("test-mode", true); + if (idx < 0) + return false; + let url; + let urlIdx = idx + 1; + if (urlIdx < cmdLine.length) { + let potentialURL = cmdLine.getArgument(urlIdx); + if (potentialURL && potentialURL[0] != "-") { + try { + url = Services.io.newURI(potentialURL, null, null); + } catch (err) { + throw Components.Exception( + "-test-mode argument is not a valid URL: " + potentialURL, + Components.results.NS_ERROR_INVALID_ARG); + } + cmdLine.removeArguments(urlIdx, urlIdx); + args.setProperty("url", url.spec); + } + } + cmdLine.removeArguments(idx, idx); + return true; + }, + + helpInfo : "", +}; + +var components = [CommandLineHandler]; +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); diff --git a/webapprt/ContentPermission.js b/webapprt/ContentPermission.js new file mode 100644 index 0000000000..33056a020d --- /dev/null +++ b/webapprt/ContentPermission.js @@ -0,0 +1,118 @@ +/* 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/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +const UNKNOWN_FAIL = ["geolocation", "desktop-notification"]; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://webapprt/modules/WebappRT.jsm"); + +function ContentPermission() {} + +ContentPermission.prototype = { + classID: Components.ID("{07ef5b2e-88fb-47bd-8cec-d3b0bef11ac4}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]), + + _getChromeWindow: function(aWindow) { + return aWindow + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow) + .QueryInterface(Ci.nsIDOMChromeWindow); + }, + + prompt: function(request) { + // Only allow exactly one permission request here. + let types = request.types.QueryInterface(Ci.nsIArray); + if (types.length != 1) { + request.cancel(); + return; + } + let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); + + // Reuse any remembered permission preferences + let result = + Services.perms.testExactPermissionFromPrincipal(request.principal, + perm.type); + + // We used to use the name "geo" for the geolocation permission, now we're + // using "geolocation". We need to check both to support existing + // installations. + if ((result == Ci.nsIPermissionManager.UNKNOWN_ACTION || + result == Ci.nsIPermissionManager.PROMPT_ACTION) && + perm.type == "geolocation") { + let geoResult = Services.perms.testExactPermission(request.principal.URI, + "geo"); + // We override the result only if the "geo" permission was allowed or + // denied. + if (geoResult == Ci.nsIPermissionManager.ALLOW_ACTION || + geoResult == Ci.nsIPermissionManager.DENY_ACTION) { + result = geoResult; + } + } + + if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { + request.allow(); + return; + } else if (result == Ci.nsIPermissionManager.DENY_ACTION || + (result == Ci.nsIPermissionManager.UNKNOWN_ACTION && + UNKNOWN_FAIL.indexOf(perm.type) >= 0)) { + request.cancel(); + return; + } + + // Display a prompt at the top level + let {name} = WebappRT.localeManifest; + let requestingWindow = request.window.top; + let chromeWin = this._getChromeWindow(requestingWindow); + let bundle = Services.strings.createBundle("chrome://webapprt/locale/webapp.properties"); + + // Construct a prompt with share/don't and remember checkbox + let remember = {value: false}; + let choice = Services.prompt.confirmEx( + chromeWin, + bundle.formatStringFromName(perm.type + ".title", [name], 1), + bundle.GetStringFromName(perm.type + ".description"), + // Set both buttons to strings with the cancel button being default + Ci.nsIPromptService.BUTTON_POS_1_DEFAULT | + Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_0 | + Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_1, + bundle.GetStringFromName(perm.type + ".allow"), + bundle.GetStringFromName(perm.type + ".deny"), + null, + bundle.GetStringFromName(perm.type + ".remember"), + remember); + + let action = Ci.nsIPermissionManager.ALLOW_ACTION; + if (choice != 0) { + action = Ci.nsIPermissionManager.DENY_ACTION; + } + + if (remember.value) { + // Persist the choice if the user wants to remember + Services.perms.addFromPrincipal(request.principal, perm.type, action); + } else { + // Otherwise allow the permission for the current session + Services.perms.addFromPrincipal(request.principal, perm.type, action, + Ci.nsIPermissionManager.EXPIRE_SESSION); + } + + // Trigger the selected choice + if (choice == 0) { + request.allow(); + } + else { + request.cancel(); + } + } +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPermission]); diff --git a/webapprt/DirectoryProvider.js b/webapprt/DirectoryProvider.js new file mode 100644 index 0000000000..9b199863b7 --- /dev/null +++ b/webapprt/DirectoryProvider.js @@ -0,0 +1,56 @@ +/* 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/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +const WEBAPP_REGISTRY_DIR = "WebappRegD"; +const NS_APP_CHROME_DIR_LIST = "AChromDL"; + +Cu.import("resource://gre/modules/FileUtils.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://webapprt/modules/WebappRT.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +function DirectoryProvider() {} + +DirectoryProvider.prototype = { + classID: Components.ID("{e1799fda-4b2f-4457-b671-e0641d95698d}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider, + Ci.nsIDirectoryServiceProvider2]), + + getFile: function(prop, persistent) { + if (prop == WEBAPP_REGISTRY_DIR) { + let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + dir.initWithPath(WebappRT.config.registryDir); + return dir; + } + + // We return null to show failure instead of throwing an error, + // which works with the way the interface is called (per bug 529077). + return null; + }, + + getFiles: function(prop, persistent) { + if (prop == NS_APP_CHROME_DIR_LIST) { + return { + _done: false, + QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]), + hasMoreElements: function() { + return !this._done; + }, + getNext: function() { + this._done = true; + return FileUtils.getDir("AppRegD", ["chrome"], false); + } + }; + } + + return null; + }, +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DirectoryProvider]); diff --git a/webapprt/DownloadView.jsm b/webapprt/DownloadView.jsm new file mode 100644 index 0000000000..8c140f9337 --- /dev/null +++ b/webapprt/DownloadView.jsm @@ -0,0 +1,35 @@ +/* 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/. */ + +"use strict"; + +this.EXPORTED_SYMBOLS = ["DownloadView"]; + +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Downloads.jsm"); + +this.DownloadView = { + init: function() { + Downloads.getList(Downloads.ALL) + .then(list => list.addView(this)) + .catch(Cu.reportError); + }, + + onDownloadAdded: function(aDownload) { + let dmWindow = Services.wm.getMostRecentWindow("Download:Manager"); + if (dmWindow) { + dmWindow.focus(); + } else { + Services.ww.openWindow(null, + "chrome://webapprt/content/downloads/downloads.xul", + "Download:Manager", + "chrome,dialog=no,resizable", + null); + } + }, +}; + +DownloadView.init(); diff --git a/webapprt/Makefile.in b/webapprt/Makefile.in new file mode 100644 index 0000000000..ac3998cd72 --- /dev/null +++ b/webapprt/Makefile.in @@ -0,0 +1,16 @@ +# 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 config.mk explicitly so we can override FINAL_TARGET. +include $(topsrcdir)/config/config.mk + +include $(topsrcdir)/config/rules.mk + +libs:: $(call mkdir_deps,$(FINAL_TARGET)) + $(call py_action,buildlist,$(FINAL_TARGET)/chrome.manifest 'resource webapprt ./') + +MOZ_APP_BUILDID := $(shell cat $(DEPTH)/config/buildid) +DEFINES += -DMOZ_APP_BUILDID=$(MOZ_APP_BUILDID) + +$(FINAL_TARGET)/webapprt.ini: $(DEPTH)/config/buildid $(topsrcdir)/config/milestone.txt diff --git a/webapprt/PaymentUIGlue.js b/webapprt/PaymentUIGlue.js new file mode 100644 index 0000000000..da227142ec --- /dev/null +++ b/webapprt/PaymentUIGlue.js @@ -0,0 +1,148 @@ +/* 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/. */ + +"use strict"; + +const { interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", + "@mozilla.org/childprocessmessagemanager;1", + "nsIMessageSender"); + +function paymentSuccess(aRequestId) { + return function(aResult) { + closePaymentWindow(aRequestId, function() { + cpmm.sendAsyncMessage("Payment:Success", { requestId: aRequestId, + result: aResult }); + }); + }; +} + +function paymentFailed(aRequestId) { + return function(aErrorMsg) { + closePaymentWindow(aRequestId, function() { + cpmm.sendAsyncMessage("Payment:Failed", { requestId: aRequestId, + errorMsg: aErrorMsg }); + }); + }; +} + +var payments = {}; + +function closePaymentWindow(aId, aCallback) { + if (payments[aId]) { + payments[aId].handled = true; + payments[aId].win.close(); + payments[aId] = null; + } + + aCallback(); +} + +function PaymentUI() {} + +PaymentUI.prototype = { + classID: Components.ID("{ede1124f-72e8-4a31-9567-3270d46f21fb}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIGlue]), + + confirmPaymentRequest: function(aRequestId, aRequests, aSuccessCb, aErrorCb) { + // If there's only one payment provider that will work, just move on + // without prompting the user. + if (aRequests.length == 1) { + aSuccessCb.onresult(aRequestId, aRequests[0].type); + return; + } + + let items = []; + + // Otherwise, let the user select a payment provider from a list. + for (let i = 0; i < aRequests.length; i++) { + let request = aRequests[i]; + let requestText = request.providerName; + if (request.productPrice && Array.isArray(request.productPrice)) { + // We should guess the user currency and use that instead. + requestText += " (" + request.productPrice[0].amount + " " + + request.productPrice[0].currency + ")"; + } + items.push(requestText); + } + + let selected = {}; + + let bundle = Services.strings. + createBundle("chrome://webapprt/locale/webapp.properties"); + let result = Services.prompt. + select(null, bundle.GetStringFromName("paymentDialog.title"), + bundle.GetStringFromName("paymentDialog.message"), + items.length, items, selected); + if (result) { + aSuccessCb.onresult(aRequestId, + aRequests[selected.value].type); + } else { + aErrorCb.onresult(aRequestId, "USER_CANCELLED"); + } + }, + + showPaymentFlow: function(aRequestId, aPaymentFlowInfo, aErrorCb) { + let win = Services.ww. + openWindow(null, + "chrome://webapprt/content/webapp.xul", + "_blank", + "chrome,dialog=no,resizable,scrollbars,centerscreen", + null); + + // Store a reference to the window so that we can close it when the payment + // succeeds or fails. + payments[aRequestId] = { win: win, handled: false }; + + // Inject paymentSuccess and paymentFailed methods into the document after + // its loaded. + win.addEventListener("DOMContentLoaded", function onDOMContentLoaded() { + win.removeEventListener("DOMContentLoaded", onDOMContentLoaded); + + let browserElement = win.document.getElementById("content"); + browserElement. + setAttribute("src", aPaymentFlowInfo.uri + aPaymentFlowInfo.jwt); + + browserElement.addEventListener("DOMWindowCreated", + function onDOMWindowCreated() { + browserElement.removeEventListener("DOMWindowCreated", + onDOMWindowCreated); + + + win.document.getElementById("content").contentDocument.defaultView + .wrappedJSObject.mozPaymentProvider = { + __exposedProps__: { + paymentSuccess: 'r', + paymentFailed: 'r' + }, + paymentSuccess: paymentSuccess(aRequestId), + paymentFailed: paymentFailed(aRequestId) + }; + }, true); + }); + + let winObserver = function(aClosedWin, aTopic) { + if (aTopic == "domwindowclosed") { + // Fail the payment if the window is closed. + if (aClosedWin == win) { + Services.ww.unregisterNotification(winObserver); + if (payments[aRequestId] && !payments[aRequestId].handled) { + aErrorCb.onresult(aRequestId, "USER_CANCELLED"); + } + } + } + } + + Services.ww.registerNotification(winObserver); + }, + + cleanup: function() { + }, +} + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentUI]); diff --git a/webapprt/Startup.jsm b/webapprt/Startup.jsm new file mode 100644 index 0000000000..4ae1066228 --- /dev/null +++ b/webapprt/Startup.jsm @@ -0,0 +1,169 @@ +/* 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/. */ + +/* This module is imported at the startup of an application. It takes care of + * permissions installation, application url loading, security settings. Put + * stuff here that you want to happen once on startup before the webapp is + * loaded. */ + +this.EXPORTED_SYMBOLS = ["startup"]; + +const Ci = Components.interfaces; +const Cu = Components.utils; + +/* We load here modules that are needed to perform the application startup. + * We lazily load modules that aren't needed on every startup. + * We load modules that aren't used here but that need to perform some + * initialization steps later in the startup function. */ + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/Promise.jsm"); +Cu.import("resource://gre/modules/osfile.jsm"); + +Cu.import("resource://webapprt/modules/WebappRT.jsm"); + +// Lazily load these modules because we don't need them at every +// startup, but only during first run or runtime update. + +XPCOMUtils.defineLazyModuleGetter(this, "PermissionsInstaller", + "resource://gre/modules/PermissionsInstaller.jsm"); + +const PROFILE_DIR = OS.Constants.Path.profileDir; + +function isFirstRunOrUpdate() { + let savedBuildID = null; + try { + savedBuildID = Services.prefs.getCharPref("webapprt.buildID"); + } catch (e) {} + + let ourBuildID = Services.appinfo.platformBuildID; + + if (ourBuildID != savedBuildID) { + Services.prefs.setCharPref("webapprt.buildID", ourBuildID); + return true; + } + + return false; +} + +function writeFile(aPath, aData) { + return Task.spawn(function() { + let data = new TextEncoder().encode(aData); + yield OS.File.writeAtomic(aPath, data, { tmpPath: aPath + ".tmp" }); + }); +} + +function createBrandingFiles() { + return Task.spawn(function() { + let manifest = WebappRT.localeManifest; + let name = WebappRT.localeManifest.name; + let developer = " "; + if (WebappRT.localeManifest.developer) { + developer = WebappRT.localeManifest.developer.name; + } + + let brandDTDContent = '\n\ + \n\ + \n\ + '; + + yield writeFile(OS.Path.join(PROFILE_DIR, "brand.dtd"), brandDTDContent); + + let brandPropertiesContent = 'brandShortName=' + name + '\n\ + brandFullName=' + name + '\n\ + vendorShortName=' + developer; + + yield writeFile(OS.Path.join(PROFILE_DIR, "brand.properties"), + brandPropertiesContent); + }); +} + +// Observes all the events needed to actually launch an application. +// It waits for XUL window and webapps registry loading. +this.startup = function(window) { + return Task.spawn(function () { + // Observe XUL window loading. + // For tests, it could be already loaded. + let deferredWindowLoad = Promise.defer(); + if (window.document && window.document.getElementById("content")) { + deferredWindowLoad.resolve(); + } else { + window.addEventListener("DOMContentLoaded", function onLoad() { + window.removeEventListener("DOMContentLoaded", onLoad, false); + deferredWindowLoad.resolve(); + }); + } + + let appUpdated = false; + let updatePending = yield WebappRT.isUpdatePending(); + if (updatePending) { + appUpdated = yield WebappRT.applyUpdate(); + } + + yield WebappRT.configPromise; + + let appData = WebappRT.config.app; + + // Initialize DOMApplicationRegistry by importing Webapps.jsm. + Cu.import("resource://gre/modules/Webapps.jsm"); + // Initialize window-independent handling of webapps- notifications. + Cu.import("resource://webapprt/modules/WebappManager.jsm"); + + // Wait for webapps registry loading. + yield DOMApplicationRegistry.registryStarted; + // Add the currently running app to the registry. + yield DOMApplicationRegistry.addInstalledApp(appData, appData.manifest, + appData.updateManifest); + + let manifestURL = appData.manifestURL; + if (manifestURL) { + // On firstrun, set permissions to their default values. + // When the webapp runtime is updated, update the permissions. + if (isFirstRunOrUpdate(Services.prefs) || appUpdated) { + PermissionsInstaller.installPermissions(appData, true); + yield createBrandingFiles(); + } + } + + // Branding substitution + let aliasFile = Components.classes["@mozilla.org/file/local;1"] + .createInstance(Ci.nsIFile); + aliasFile.initWithPath(PROFILE_DIR); + + let aliasURI = Services.io.newFileURI(aliasFile); + + Services.io.getProtocolHandler("resource") + .QueryInterface(Ci.nsIResProtocolHandler) + .setSubstitution("webappbranding", aliasURI); + + // Wait for XUL window loading + yield deferredWindowLoad.promise; + + // Load these modules here because they aren't needed right at startup, + // but they need to be loaded to perform some initialization steps. + Cu.import("resource://gre/modules/Payment.jsm"); + Cu.import("resource://gre/modules/AlarmService.jsm"); + Cu.import("resource://webapprt/modules/WebRTCHandler.jsm"); + Cu.import("resource://webapprt/modules/DownloadView.jsm"); + + // Get the element in the webapp.xul window. + let appBrowser = window.document.getElementById("content"); + + // Set the principal to the correct appID and launch the application. + appBrowser.docShell.setIsApp(WebappRT.appID); + appBrowser.setAttribute("src", WebappRT.launchURI); + + if (appData.manifest.fullscreen) { + appBrowser.addEventListener("load", function onLoad() { + appBrowser.removeEventListener("load", onLoad, true); + appBrowser.contentDocument. + documentElement.mozRequestFullScreen(); + }, true); + } + + WebappRT.startUpdateService(); + }); +} diff --git a/webapprt/WebappManager.jsm b/webapprt/WebappManager.jsm new file mode 100644 index 0000000000..3a8ed912c9 --- /dev/null +++ b/webapprt/WebappManager.jsm @@ -0,0 +1,142 @@ +/* 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/. */ + +"use strict"; + +this.EXPORTED_SYMBOLS = ["WebappManager"]; + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Webapps.jsm"); +Cu.import("resource://gre/modules/AppsUtils.jsm"); +Cu.import("resource://gre/modules/NativeApp.jsm"); +Cu.import("resource://gre/modules/WebappOSUtils.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://webapprt/modules/WebappRT.jsm"); + + +this.WebappManager = { + observe: function(aSubject, aTopic, aData) { + let data = JSON.parse(aData); + data.mm = aSubject; + + let chromeWin; + switch (aTopic) { + case "webapps-ask-install": + chromeWin = Services.wm.getOuterWindowWithId(data.oid); + if (chromeWin) + this.doInstall(data, chromeWin); + break; + case "webapps-ask-uninstall": + chromeWin = Services.wm.getOuterWindowWithId(data.windowId); + if (chromeWin) { + this.doUninstall(data, chromeWin); + } + break; + case "webapps-launch": + WebappOSUtils.launch(data); + break; + case "webapps-uninstall": + WebappOSUtils.uninstall(data).then(null, Cu.reportError); + break; + } + }, + + update: function(aApp, aManifest, aZipPath) { + let nativeApp = new NativeApp(aApp, aManifest, + WebappRT.config.app.categories, + WebappRT.config.registryDir); + nativeApp.prepareUpdate(aApp, aManifest, aZipPath); + }, + + doInstall: function(data, window) { + let jsonManifest = data.isPackage ? data.app.updateManifest : data.app.manifest; + let manifest = + new ManifestHelper(jsonManifest, data.app.origin, data.app.manifestURL); + let name = manifest.name; + let bundle = Services.strings.createBundle("chrome://webapprt/locale/webapp.properties"); + + let choice = Services.prompt.confirmEx( + window, + bundle.formatStringFromName("webapps.install.title", [name], 1), + bundle.formatStringFromName("webapps.install.description", [name], 1), + // Set both buttons to strings with the cancel button being default + Ci.nsIPromptService.BUTTON_POS_1_DEFAULT | + Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_0 | + Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_1, + bundle.GetStringFromName("webapps.install.install"), + bundle.GetStringFromName("webapps.install.dontinstall"), + null, + null, + {}); + + // Perform the install if the user allows it + if (choice == 0) { + let nativeApp = new NativeApp(data.app, jsonManifest, + data.app.categories, + WebappRT.config.registryDir); + let localDir; + try { + localDir = nativeApp.createProfile(); + } catch (ex) { + DOMApplicationRegistry.denyInstall(data); + return; + } + + DOMApplicationRegistry.confirmInstall(data, localDir, + Task.async(function*(aApp, aManifest, aZipPath) { + yield nativeApp.install(aApp, aManifest, aZipPath); + }) + ); + } else { + DOMApplicationRegistry.denyInstall(data); + } + }, + + doUninstall: function(aData, aWindow) { + let jsonManifest = aData.isPackage ? aData.app.updateManifest : aData.app.manifest; + let manifest = new ManifestHelper(jsonManifest, aData.app.origin, + aData.app.manifestURL); + let name = manifest.name; + let bundle = Services.strings.createBundle("chrome://webapprt/locale/webapp.properties"); + + let choice = Services.prompt.confirmEx( + aWindow, + bundle.formatStringFromName("webapps.uninstall.title", [name], 1), + bundle.formatStringFromName("webapps.uninstall.description", [name], 1), + // Set both buttons to strings with the cancel button being default + Ci.nsIPromptService.BUTTON_POS_1_DEFAULT | + Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_0 | + Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_1, + bundle.GetStringFromName("webapps.uninstall.uninstall"), + bundle.GetStringFromName("webapps.uninstall.dontuninstall"), + null, + null, + {}); + + // Perform the uninstall if the user allows it + if (choice == 0) { + DOMApplicationRegistry.confirmUninstall(aData).then((aApp) => { + WebappOSUtils.uninstall(aApp).then(null, Cu.reportError); + }); + } else { + DOMApplicationRegistry.denyUninstall(aData); + } + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, + Ci.nsISupportsWeakReference]) +}; + +Services.obs.addObserver(WebappManager, "webapps-ask-install", false); +Services.obs.addObserver(WebappManager, "webapps-ask-uninstall", false); +Services.obs.addObserver(WebappManager, "webapps-launch", false); +Services.obs.addObserver(WebappManager, "webapps-uninstall", false); +Services.obs.addObserver(WebappManager, "webapps-update", false); + +DOMApplicationRegistry.registerUpdateHandler(WebappManager.update); diff --git a/webapprt/WebappRT.jsm b/webapprt/WebappRT.jsm new file mode 100644 index 0000000000..d4c5f186f9 --- /dev/null +++ b/webapprt/WebappRT.jsm @@ -0,0 +1,143 @@ +/* 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/. */ + +this.EXPORTED_SYMBOLS = ["WebappRT"]; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Promise.jsm"); +Cu.import("resource://gre/modules/AppsUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "OS", + "resource://gre/modules/osfile.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Task", + "resource://gre/modules/Task.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, 'NativeApp', + 'resource://gre/modules/NativeApp.jsm'); + +XPCOMUtils.defineLazyServiceGetter(this, "appsService", + "@mozilla.org/AppsService;1", + "nsIAppsService"); + +this.WebappRT = { + _configPromise: null, + + get configPromise() { + if (!this._configPromise) { + this._configPromise = Task.spawn(function*() { + let webappJson = OS.Path.join(Services.dirsvc.get("AppRegD", Ci.nsIFile).path, + "webapp.json"); + + WebappRT.config = yield AppsUtils.loadJSONAsync(webappJson); + }); + } + + return this._configPromise; + }, + + get launchURI() { + return this.localeManifest.fullLaunchPath(); + }, + + get localeManifest() { + return new ManifestHelper(this.config.app.manifest, + this.config.app.origin, + this.config.app.manifestURL); + }, + + get appID() { + let manifestURL = this.config.app.manifestURL; + if (!manifestURL) { + return Ci.nsIScriptSecurityManager.NO_APP_ID; + } + + return appsService.getAppLocalIdByManifestURL(manifestURL); + }, + + isUpdatePending: Task.async(function*() { + let webappJson = OS.Path.join(Services.dirsvc.get("AppRegD", Ci.nsIFile).path, + "update", "webapp.json"); + + if (!(yield OS.File.exists(webappJson))) { + return false; + } + + return true; + }), + + applyUpdate: Task.async(function*() { + let webappJson = OS.Path.join(Services.dirsvc.get("AppRegD", Ci.nsIFile).path, + "update", "webapp.json"); + let config = yield AppsUtils.loadJSONAsync(webappJson); + + let nativeApp = new NativeApp(config.app, config.app.manifest, + config.app.categories, + config.registryDir); + try { + yield nativeApp.applyUpdate(config.app); + } catch (ex) { + return false; + } + + // The update has been applied successfully, the new config file + // is the config file that was in the update directory. + this.config = config; + this._configPromise = Promise.resolve(); + + return true; + }), + + startUpdateService: function() { + let manifestURL = this.config.app.manifestURL; + // We used to install apps without storing their manifest URL. + // Now we can't update them. + if (!manifestURL) { + return; + } + + // Check for updates once a day. + let timerManager = Cc["@mozilla.org/updates/timer-manager;1"]. + getService(Ci.nsIUpdateTimerManager); + timerManager.registerTimer("updateTimer", () => { + let window = Services.wm.getMostRecentWindow("webapprt:webapp"); + window.navigator.mozApps.mgmt.getAll().onsuccess = function() { + let thisApp = null; + for (let app of this.result) { + if (app.manifestURL == manifestURL) { + thisApp = app; + break; + } + } + + // This shouldn't happen if the app is installed. + if (!thisApp) { + Cu.reportError("Couldn't find the app in the webapps registry"); + return; + } + + thisApp.ondownloadavailable = () => { + // Download available, download it! + thisApp.download(); + }; + + thisApp.ondownloadsuccess = () => { + // Update downloaded, apply it! + window.navigator.mozApps.mgmt.applyDownload(thisApp); + }; + + thisApp.ondownloadapplied = () => { + // Application updated, nothing to do. + }; + + thisApp.checkForUpdate(); + } + }, Services.prefs.getIntPref("webapprt.app_update_interval")); + }, +}; diff --git a/webapprt/components.manifest b/webapprt/components.manifest new file mode 100644 index 0000000000..b11e54f365 --- /dev/null +++ b/webapprt/components.manifest @@ -0,0 +1,17 @@ +# CommandLineHandler.js +component {6d69c782-40a3-469b-8bfd-3ee366105a4a} CommandLineHandler.js +contract @mozilla.org/webapprt/clh;1 {6d69c782-40a3-469b-8bfd-3ee366105a4a} +category command-line-handler x-default @mozilla.org/webapprt/clh;1 + +# ContentPermission.js +component {07ef5b2e-88fb-47bd-8cec-d3b0bef11ac4} ContentPermission.js +contract @mozilla.org/content-permission/prompt;1 {07ef5b2e-88fb-47bd-8cec-d3b0bef11ac4} + +# DirectoryProvider.js +component {e1799fda-4b2f-4457-b671-e0641d95698d} DirectoryProvider.js +contract @mozilla.org/webapprt/directory-provider;1 {e1799fda-4b2f-4457-b671-e0641d95698d} +category xpcom-directory-providers webapprt-directory-provider @mozilla.org/webapprt/directory-provider;1 + +# PaymentUIGlue.js +component {ede1124f-72e8-4a31-9567-3270d46f21fb} PaymentUIGlue.js +contract @mozilla.org/payment/ui-glue;1 {ede1124f-72e8-4a31-9567-3270d46f21fb} diff --git a/webapprt/defs.mk b/webapprt/defs.mk new file mode 100644 index 0000000000..e3ed0d8cbf --- /dev/null +++ b/webapprt/defs.mk @@ -0,0 +1 @@ +XPI_ROOT_APPID=webapprt@mozilla.org diff --git a/webapprt/jar.mn b/webapprt/jar.mn new file mode 100644 index 0000000000..fdf7fef068 --- /dev/null +++ b/webapprt/jar.mn @@ -0,0 +1,17 @@ +# 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/. + +webapprt.jar: +% content webapprt %content/ +* content/webapp.js (content/webapp.js) +* content/webapp.xul (content/webapp.xul) + content/getUserMediaDialog.xul (content/getUserMediaDialog.xul) + content/getUserMediaDialog.js (content/getUserMediaDialog.js) + content/mochitest-shared.js (content/mochitest-shared.js) + content/mochitest.js (content/mochitest.js) + content/mochitest.xul (content/mochitest.xul) +* content/downloads/downloads.xul (content/downloads/downloads.xul) + content/downloads/downloads.js (content/downloads/downloads.js) + content/downloads/downloads.css (content/downloads/downloads.css) + content/downloads/download.xml (content/downloads/download.xml) diff --git a/webapprt/moz.build b/webapprt/moz.build new file mode 100644 index 0000000000..2e10df02dd --- /dev/null +++ b/webapprt/moz.build @@ -0,0 +1,59 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +with Files('**'): + BUG_COMPONENT = ('Firefox', 'Webapp Runtime') + +if CONFIG['OS_ARCH'] == 'WINNT': + DIRS += ['win'] +elif CONFIG['OS_ARCH'] == 'Darwin': + DIRS += ['mac'] +elif CONFIG['MOZ_ENABLE_GTK']: + DIRS += ['gtk'] + +DIRS += [ + 'locales', + 'themes', +] + +EXTRA_COMPONENTS += [ + 'CommandLineHandler.js', + 'components.manifest', + 'ContentPermission.js', + 'DirectoryProvider.js', + 'PaymentUIGlue.js', +] + +EXTRA_JS_MODULES += [ + 'DownloadView.jsm', + 'Startup.jsm', + 'WebappManager.jsm', + 'WebappRT.jsm', + 'WebRTCHandler.jsm', +] + +MOCHITEST_WEBAPPRT_CHROME_MANIFESTS += [ + 'test/chrome/downloads/webapprt.ini', + 'test/chrome/webapprt.ini', +] +MOCHITEST_WEBAPPRT_CONTENT_MANIFESTS += ['test/content/webapprt.ini'] + +# Place webapprt resources in a separate app dir +DIST_SUBDIR = 'webapprt' +export('DIST_SUBDIR') + +DEFINES['GRE_MILESTONE'] = CONFIG['GRE_MILESTONE'] +DEFINES['MOZ_APP_BASENAME'] = CONFIG['MOZ_APP_BASENAME'] + +JAR_MANIFESTS += ['jar.mn'] + +JS_PREFERENCE_FILES += [ + 'prefs.js', +] + +DIST_FILES += [ + 'webapprt.ini', +] diff --git a/webapprt/prefs.js b/webapprt/prefs.js new file mode 100644 index 0000000000..8da1e20d5b --- /dev/null +++ b/webapprt/prefs.js @@ -0,0 +1,97 @@ +/* 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/. */ + +// Check for updates once a day. +pref("webapprt.app_update_interval", 86400); + +pref("browser.chromeURL", "chrome://webapprt/content/webapp.xul"); +pref("browser.download.folderList", 1); + +// Disable all add-on locations other than the profile (which can't be disabled this way) +pref("extensions.enabledScopes", 1); +// Auto-disable any add-ons that are "dropped in" to the profile +pref("extensions.autoDisableScopes", 1); +// Disable add-on installation via the web-exposed APIs +pref("xpinstall.enabled", false); +// Disable installation of distribution add-ons +pref("extensions.installDistroAddons", false); +// Disable the add-on compatibility dialog +pref("extensions.showMismatchUI", false); + +// Set reportURL for crashes +pref("breakpad.reportURL", "https://crash-stats.mozilla.com/report/index/"); + +// Blocklist preferences +pref("extensions.blocklist.enabled", true); +pref("extensions.blocklist.interval", 86400); +// Controls what level the blocklist switches from warning about items to forcibly +// blocking them. +pref("extensions.blocklist.level", 2); +pref("extensions.blocklist.url", "https://blocklist.addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/"); +pref("extensions.blocklist.detailsURL", "https://www.mozilla.com/%LOCALE%/blocklist/"); +pref("extensions.blocklist.itemURL", "https://blocklist.addons.mozilla.org/%LOCALE%/%APP%/blocked/%blockID%"); + +pref("full-screen-api.enabled", true); + +// IndexedDB +pref("dom.indexedDB.enabled", true); + +// Offline cache prefs +pref("browser.offline-apps.notify", false); +pref("browser.cache.offline.enable", true); +pref("offline-apps.allow_by_default", true); + +// TCPSocket +pref("dom.mozTCPSocket.enabled", true); + +// Enable smooth scrolling +pref("general.smoothScroll", true); + +// WebPayment +pref("dom.mozPay.enabled", false); + +// System messages +pref("dom.sysmsg.enabled", true); + +// Alarm API +pref("dom.mozAlarms.enabled", true); + +// Disable slow script dialog for apps +pref("dom.max_script_run_time", 0); +pref("dom.max_chrome_script_run_time", 0); + +// The request URL of the GeoLocation backend +pref("geo.wifi.uri", "https://location.services.mozilla.com/v1/geolocate?key=%MOZILLA_API_KEY%"); + +#ifndef RELEASE_BUILD +// Enable mozPay default provider +pref("dom.payment.provider.0.name", "Firefox Marketplace"); +pref("dom.payment.provider.0.description", "marketplace.firefox.com"); +pref("dom.payment.provider.0.uri", "https://marketplace.firefox.com/mozpay/?req="); +pref("dom.payment.provider.0.type", "mozilla/payments/pay/v1"); +pref("dom.payment.provider.0.requestMethod", "GET"); +#endif + +// Enable window resize and move +pref("dom.always_allow_move_resize_window", true); + +// Disable all plugins. This has to be a non-empty string to disable plugins; +// otherwise, nsPluginHost::IsTypeWhitelisted assumes all plugins are enabled. +pref("plugin.allowed_types", " "); +// Suppress the check for outdated plugins from opening a window. +pref("extensions.blocklist.suppressUI", true); + +// The default for this pref reflects whether the build is capable of IPC. +// (Turning it on in a no-IPC build will have no effect.) +#ifdef XP_MACOSX +// i386 ipc preferences +pref("dom.ipc.plugins.enabled.i386", false); +pref("dom.ipc.plugins.enabled.i386.flash player.plugin", true); +// x86_64 ipc preferences +pref("dom.ipc.plugins.enabled.x86_64", true); +#else +pref("dom.ipc.plugins.enabled", true); +#endif + +pref("places.database.growthIncrementKiB", 0); diff --git a/webapprt/webapprt.ini b/webapprt/webapprt.ini new file mode 100644 index 0000000000..e6cbabe313 --- /dev/null +++ b/webapprt/webapprt.ini @@ -0,0 +1,28 @@ +#if 0 +; 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/. +#endif + +#filter substitution + +[App] +ID=webapprt@mozilla.org +Vendor=Mozilla +; Note: the Windows stub executable sets nsXREAppData::name to the webapp +; origin, overriding the value below, to make the app runner treat webapps +; as distinct products, per bug 747409. +Name=Webapp Runtime +Version=@GRE_MILESTONE@ +BuildID=@MOZ_APP_BUILDID@ +UAName=@MOZ_APP_BASENAME@ + +[Gecko] +MinVersion=@GRE_MILESTONE@ +MaxVersion=@GRE_MILESTONE@ + +[Crash Reporter] +#if MOZILLA_OFFICIAL +Enabled=1 +#endif +ServerURL=https://crash-reports.mozilla.com/submit?id=webapprt@mozilla.org&version=@GRE_MILESTONE@&buildid=@MOZ_APP_BUILDID@ diff --git a/xpcom/io/nsEscape.cpp b/xpcom/io/nsEscape.cpp index 5485dc36d1..0a4f9c289c 100644 --- a/xpcom/io/nsEscape.cpp +++ b/xpcom/io/nsEscape.cpp @@ -363,8 +363,8 @@ static const uint32_t EscapeChars[256] = 0,1023, 0, 512,1023, 0,1023, 112,1023,1023,1023,1023,1023,1023, 953, 784, // 2x !"#$%&'()*+,-./ 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1008,1008, 0,1008, 0, 768, // 3x 0123456789:;<=>? 1008,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, // 4x @ABCDEFGHIJKLMNO - 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, 896, 896, 896, 896,1023, // 5x PQRSTUVWXYZ[\]^_ - 0,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, // 6x `abcdefghijklmno + 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1008, 896,1008, 896,1023, // 5x PQRSTUVWXYZ[\]^_ + 384,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, // 6x `abcdefghijklmno 1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023, 896,1012, 896,1023, 0, // 7x pqrstuvwxyz{|}~ DEL 0 // 80 to FF are zero }; @@ -551,28 +551,26 @@ NS_UnescapeURL(const char* aStr, int32_t aLen, uint32_t aFlags, bool ignoreAscii = !!(aFlags & esc_OnlyNonASCII); bool writing = !!(aFlags & esc_AlwaysCopy); bool skipControl = !!(aFlags & esc_SkipControl); + bool skipInvalidHostChar = !!(aFlags & esc_Host); const char* last = aStr; const char* p = aStr; for (int i = 0; i < aLen; ++i, ++p) { - //printf("%c [i=%d of aLen=%d]\n", *p, i, aLen); if (*p == HEX_ESCAPE && i < aLen - 2) { - unsigned char* p1 = (unsigned char*)p + 1; - unsigned char* p2 = (unsigned char*)p + 2; - if (ISHEX(*p1) && ISHEX(*p2) && - ((*p1 < '8' && !ignoreAscii) || (*p1 >= '8' && !ignoreNonAscii)) && + unsigned char c1 = *((unsigned char*)p + 1); + unsigned char c2 = *((unsigned char*)p + 2); + unsigned char u = (UNHEX(c1) << 4) + UNHEX(c2); + if (ISHEX(c1) && ISHEX(c2) && + (!skipInvalidHostChar || dontNeedEscape(u, aFlags) || c1 >= '8') && + ((c1 < '8' && !ignoreAscii) || (c1 >= '8' && !ignoreNonAscii)) && !(skipControl && - (*p1 < '2' || (*p1 == '7' && (*p2 == 'f' || *p2 == 'F'))))) { - //printf("- p1=%c p2=%c\n", *p1, *p2); + (c1 < '2' || (c1 == '7' && (c2 == 'f' || c2 == 'F'))))) { writing = true; if (p > last) { - //printf("- p=%p, last=%p\n", p, last); aResult.Append(last, p - last); last = p; } - char u = (UNHEX(*p1) << 4) + UNHEX(*p2); - //printf("- u=%c\n", u); aResult.Append(u); i += 2; p += 2;