diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp index 44ec1087a524..3d844ee1ef19 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/docshell/base/CanonicalBrowsingContext.cpp @@ -178,10 +178,9 @@ void CanonicalBrowsingContext::ReplacedBy( mActiveEntry.swap(aNewContext->mActiveEntry); } -void CanonicalBrowsingContext:: - UpdateSecurityStateForLocationOrMixedContentChange() { +void CanonicalBrowsingContext::UpdateSecurityState() { if (mSecureBrowserUI) { - mSecureBrowserUI->UpdateForLocationOrMixedContentChange(); + mSecureBrowserUI->RecomputeSecurityFlags(); } } diff --git a/docshell/base/CanonicalBrowsingContext.h b/docshell/base/CanonicalBrowsingContext.h index 8d6e5c286563..c781c00e11ae 100644 --- a/docshell/base/CanonicalBrowsingContext.h +++ b/docshell/base/CanonicalBrowsingContext.h @@ -213,8 +213,8 @@ class CanonicalBrowsingContext final : public BrowsingContext { // Called when the current URI changes (from an // nsIWebProgressListener::OnLocationChange event, so that we // can update our security UI for the new location, or when the - // mixed content state for our current window is changed. - void UpdateSecurityStateForLocationOrMixedContentChange(); + // mixed content/https-only state for our current window is changed. + void UpdateSecurityState(); void MaybeAddAsProgressListener(nsIWebProgress* aWebProgress); diff --git a/docshell/base/WindowContext.cpp b/docshell/base/WindowContext.cpp index 6c3ca365d9c4..5b7c4c4fd2d2 100644 --- a/docshell/base/WindowContext.cpp +++ b/docshell/base/WindowContext.cpp @@ -334,21 +334,23 @@ void WindowContext::Discard() { Group()->Unregister(this); } -void WindowContext::AddMixedContentSecurityState(uint32_t aStateFlags) { +void WindowContext::AddSecurityState(uint32_t aStateFlags) { MOZ_ASSERT(TopWindowContext() == this); MOZ_ASSERT((aStateFlags & (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT | - nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT)) == + nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT | + nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED | + nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED)) == aStateFlags, "Invalid flags specified!"); if (XRE_IsParentProcess()) { - Canonical()->AddMixedContentSecurityState(aStateFlags); + Canonical()->AddSecurityState(aStateFlags); } else { ContentChild* child = ContentChild::GetSingleton(); - child->SendAddMixedContentSecurityState(this, aStateFlags); + child->SendAddSecurityState(this, aStateFlags); } } diff --git a/docshell/base/WindowContext.h b/docshell/base/WindowContext.h index a77f54741947..3cde13a16bed 100644 --- a/docshell/base/WindowContext.h +++ b/docshell/base/WindowContext.h @@ -136,11 +136,10 @@ class WindowContext : public nsISupports, public nsWrapperCache { static void CreateFromIPC(IPCInitializer&& aInit); - // Add new mixed content security state flags. - // These should be some of the four nsIWebProgressListener - // 'MIXED' state flags, and should only be called on the - // top window context. - void AddMixedContentSecurityState(uint32_t aStateFlags); + // Add new security state flags. + // These should be some of the nsIWebProgressListener 'HTTPS_ONLY_MODE' or + // 'MIXED' state flags, and should only be called on the top window context. + void AddSecurityState(uint32_t aStateFlags); // This function would be called when its corresponding window is activated // by user gesture. diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index ec40b9f07da6..f6563b339eb7 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -5820,9 +5820,7 @@ nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest, // changes the current window global, but that happens before this and we // have a lot of tests that depend on the specific ordering of messages. if (!(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT)) { - GetBrowsingContext() - ->Canonical() - ->UpdateSecurityStateForLocationOrMixedContentChange(); + GetBrowsingContext()->Canonical()->UpdateSecurityState(); } } return NS_OK; diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 38db94cac54c..c576511ce19c 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -3167,10 +3167,10 @@ nsresult Document::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, mBlockAllMixedContent = loadInfo->GetBlockAllMixedContent(); mBlockAllMixedContentPreloads = mBlockAllMixedContent; + // HTTPS-Only Mode flags // The HTTPS_ONLY_EXEMPT flag of the HTTPS-Only state gets propagated to all // sub-resources and sub-documents. - mHttpsOnlyStatus = - loadInfo->GetHttpsOnlyStatus() & nsILoadInfo::HTTPS_ONLY_EXEMPT; + mHttpsOnlyStatus = loadInfo->GetHttpsOnlyStatus(); nsresult rv = InitReferrerInfo(aChannel); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp index 4907d75ee156..04244b57494d 100644 --- a/dom/ipc/BrowserParent.cpp +++ b/dom/ipc/BrowserParent.cpp @@ -2688,7 +2688,7 @@ mozilla::ipc::IPCResult BrowserParent::RecvOnLocationChange( // the current window global, but that happens before this and we have a lot // of tests that depend on the specific ordering of messages. if (!(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT)) { - GetBrowsingContext()->UpdateSecurityStateForLocationOrMixedContentChange(); + GetBrowsingContext()->UpdateSecurityState(); } return IPC_OK(); } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index c1548b49a56c..b7fa58aadfe4 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -4017,13 +4017,13 @@ bool ContentParent::DeallocPParentToChildStreamParent( return true; } -mozilla::ipc::IPCResult ContentParent::RecvAddMixedContentSecurityState( +mozilla::ipc::IPCResult ContentParent::RecvAddSecurityState( const MaybeDiscarded& aContext, uint32_t aStateFlags) { if (aContext.IsNullOrDiscarded()) { return IPC_OK(); } - aContext.get()->AddMixedContentSecurityState(aStateFlags); + aContext.get()->AddSecurityState(aStateFlags); return IPC_OK(); } diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 109900b97645..f64d14e2a6dd 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -1103,7 +1103,7 @@ class ContentParent final const MaybeDiscarded& aContext, WindowContext::BaseTransaction&& aTransaction, uint64_t aEpoch); - mozilla::ipc::IPCResult RecvAddMixedContentSecurityState( + mozilla::ipc::IPCResult RecvAddSecurityState( const MaybeDiscarded& aContext, uint32_t aStateFlags); mozilla::ipc::IPCResult RecvFirstIdle(); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index c7b7a12c3fa5..00a01a697ca5 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1055,7 +1055,7 @@ parent: async OpenNotificationSettings(Principal principal); - async AddMixedContentSecurityState(MaybeDiscardedWindowContext aContext, uint32_t aStateFlags); + async AddSecurityState(MaybeDiscardedWindowContext aContext, uint32_t aStateFlags); // Request that the ServiceWorkerManager in the parent process create a // notification "click" or "close" event and dispatch it on the relevant diff --git a/dom/ipc/WindowGlobalParent.cpp b/dom/ipc/WindowGlobalParent.cpp index 7115716125f0..3d0605d7916d 100644 --- a/dom/ipc/WindowGlobalParent.cpp +++ b/dom/ipc/WindowGlobalParent.cpp @@ -986,11 +986,11 @@ void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) { }; bool hasMixedDisplay = - mMixedContentSecurityState & + mSecurityState & (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT); bool hasMixedActive = - mMixedContentSecurityState & + mSecurityState & (nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT); @@ -1102,24 +1102,26 @@ bool WindowGlobalParent::ShouldTrackSiteOriginTelemetry() { return DocumentPrincipal()->GetIsContentPrincipal(); } -void WindowGlobalParent::AddMixedContentSecurityState(uint32_t aStateFlags) { +void WindowGlobalParent::AddSecurityState(uint32_t aStateFlags) { MOZ_ASSERT(TopWindowContext() == this); MOZ_ASSERT((aStateFlags & (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT | - nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT)) == + nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT | + nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED | + nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED)) == aStateFlags, "Invalid flags specified!"); - if ((mMixedContentSecurityState & aStateFlags) == aStateFlags) { + if ((mSecurityState & aStateFlags) == aStateFlags) { return; } - mMixedContentSecurityState |= aStateFlags; + mSecurityState |= aStateFlags; if (GetBrowsingContext()->GetCurrentWindowGlobal() == this) { - GetBrowsingContext()->UpdateSecurityStateForLocationOrMixedContentChange(); + GetBrowsingContext()->UpdateSecurityState(); } } diff --git a/dom/ipc/WindowGlobalParent.h b/dom/ipc/WindowGlobalParent.h index 361c2ff64d77..2f37489e7080 100644 --- a/dom/ipc/WindowGlobalParent.h +++ b/dom/ipc/WindowGlobalParent.h @@ -193,8 +193,8 @@ class WindowGlobalParent final : public WindowContext, uint32_t HttpsOnlyStatus() { return mHttpsOnlyStatus; } - void AddMixedContentSecurityState(uint32_t aStateFlags); - uint32_t GetMixedContentSecurityFlags() { return mMixedContentSecurityState; } + void AddSecurityState(uint32_t aStateFlags); + uint32_t GetSecurityFlags() { return mSecurityState; } nsITransportSecurityInfo* GetSecurityInfo() { return mSecurityInfo; } @@ -289,7 +289,7 @@ class WindowGlobalParent final : public WindowContext, // includes the activity log for all of the nested subdocuments as well. ContentBlockingLog mContentBlockingLog; - uint32_t mMixedContentSecurityState = 0; + uint32_t mSecurityState = 0; Maybe mClientInfo; // Fields being mirrored from the corresponding document diff --git a/dom/security/nsHTTPSOnlyStreamListener.cpp b/dom/security/nsHTTPSOnlyStreamListener.cpp index 070bc2782c8e..9425baed90a5 100644 --- a/dom/security/nsHTTPSOnlyStreamListener.cpp +++ b/dom/security/nsHTTPSOnlyStreamListener.cpp @@ -4,13 +4,14 @@ * 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 "nsHTTPSOnlyUtils.h" #include "NSSErrorsService.h" #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" +#include "mozilla/dom/WindowGlobalParent.h" #include "mozpkix/pkixnss.h" #include "nsCOMPtr.h" #include "nsHTTPSOnlyStreamListener.h" +#include "nsHTTPSOnlyUtils.h" #include "nsIChannel.h" #include "nsIRequest.h" #include "nsITransportSecurityInfo.h" @@ -23,8 +24,18 @@ NS_IMPL_ISUPPORTS(nsHTTPSOnlyStreamListener, nsIStreamListener, nsIRequestObserver) nsHTTPSOnlyStreamListener::nsHTTPSOnlyStreamListener( - nsIStreamListener* aListener) - : mListener(aListener), mCreationStart(mozilla::TimeStamp::Now()) {} + nsIStreamListener* aListener, nsILoadInfo* aLoadInfo) + : mListener(aListener), mCreationStart(mozilla::TimeStamp::Now()) { + RefPtr wgp = + mozilla::dom::WindowGlobalParent::GetByInnerWindowId( + aLoadInfo->GetInnerWindowID()); + // For Top-level document loads (which don't have a requesting window-context) + // we compute these flags once we create the Document in nsSecureBrowserUI. + if (wgp) { + wgp->TopWindowContext()->AddSecurityState( + nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED); + } +} NS_IMETHODIMP nsHTTPSOnlyStreamListener::OnDataAvailable(nsIRequest* aRequest, @@ -42,9 +53,28 @@ NS_IMETHODIMP nsHTTPSOnlyStreamListener::OnStopRequest(nsIRequest* request, nsresult aStatus) { nsCOMPtr channel = do_QueryInterface(request); + + // Note: CouldBeHttpsOnlyError also returns true if there was no error if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(channel, aStatus)) { RecordUpgradeTelemetry(request, aStatus); LogUpgradeFailure(request, aStatus); + + // If the request failed and there is a requesting window-context, set + // HTTPS-Only state flag to indicate a failed upgrade. + // For Top-level document loads (which don't have a requesting + // window-context) we simply check in the UI code whether we landed on the + // HTTPS-Only error page. + if (NS_FAILED(aStatus)) { + nsCOMPtr loadInfo = channel->LoadInfo(); + RefPtr wgp = + mozilla::dom::WindowGlobalParent::GetByInnerWindowId( + loadInfo->GetInnerWindowID()); + + if (wgp) { + wgp->TopWindowContext()->AddSecurityState( + nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED); + } + } } return mListener->OnStopRequest(request, aStatus); diff --git a/dom/security/nsHTTPSOnlyStreamListener.h b/dom/security/nsHTTPSOnlyStreamListener.h index d3255c5b2261..85845878205b 100644 --- a/dom/security/nsHTTPSOnlyStreamListener.h +++ b/dom/security/nsHTTPSOnlyStreamListener.h @@ -22,7 +22,8 @@ class nsHTTPSOnlyStreamListener : public nsIStreamListener { NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER - explicit nsHTTPSOnlyStreamListener(nsIStreamListener* aListener); + explicit nsHTTPSOnlyStreamListener(nsIStreamListener* aListener, + nsILoadInfo* aLoadInfo); private: virtual ~nsHTTPSOnlyStreamListener() = default; diff --git a/dom/security/nsHTTPSOnlyUtils.cpp b/dom/security/nsHTTPSOnlyUtils.cpp index 1a7d011679d6..6f8ef272e84d 100644 --- a/dom/security/nsHTTPSOnlyUtils.cpp +++ b/dom/security/nsHTTPSOnlyUtils.cpp @@ -84,7 +84,7 @@ void nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout( // if it's not a GET method, then there is nothing to do here either. nsAutoCString method; - Unused << httpChannel->GetRequestMethod(method); + mozilla::Unused << httpChannel->GetRequestMethod(method); if (!method.EqualsLiteral("GET")) { return; } @@ -495,7 +495,7 @@ TestHTTPAnswerRunnable::Notify(nsITimer* aTimer) { return NS_OK; } - OriginAttributes attrs = origLoadInfo->GetOriginAttributes(); + mozilla::OriginAttributes attrs = origLoadInfo->GetOriginAttributes(); RefPtr nullPrincipal = mozilla::NullPrincipal::CreateWithInheritedAttributes(attrs); diff --git a/dom/security/nsMixedContentBlocker.cpp b/dom/security/nsMixedContentBlocker.cpp index 08cb2174fd19..069a8c005d6f 100644 --- a/dom/security/nsMixedContentBlocker.cpp +++ b/dom/security/nsMixedContentBlocker.cpp @@ -898,7 +898,7 @@ nsresult nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, // Notify the top WindowContext of the flags we've computed, and it // will handle updating any relevant security UI. - topWC->AddMixedContentSecurityState(newState); + topWC->AddSecurityState(newState); return NS_OK; } diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 3883132350b5..dd4ae65efe51 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -634,7 +634,7 @@ nsresult nsHttpChannel::OnBeforeConnect() { if (httpOnlyStatus & nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED) { RefPtr httpsOnlyListener = - new nsHTTPSOnlyStreamListener(mListener); + new nsHTTPSOnlyStreamListener(mListener, mLoadInfo); mListener = httpsOnlyListener; httpOnlyStatus ^= diff --git a/security/manager/ssl/nsSecureBrowserUI.cpp b/security/manager/ssl/nsSecureBrowserUI.cpp index b85f9d3b5bae..b4de1a331ffc 100644 --- a/security/manager/ssl/nsSecureBrowserUI.cpp +++ b/security/manager/ssl/nsSecureBrowserUI.cpp @@ -51,7 +51,7 @@ nsSecureBrowserUI::GetState(uint32_t* aState) { return NS_OK; } -void nsSecureBrowserUI::UpdateForLocationOrMixedContentChange() { +void nsSecureBrowserUI::RecomputeSecurityFlags() { // Our BrowsingContext either has a new WindowGlobalParent, or the // existing one has mutated its security state. // Recompute our security state and fire notifications to listeners @@ -87,9 +87,17 @@ void nsSecureBrowserUI::UpdateForLocationOrMixedContentChange() { } } - // Add the mixed content flags from the window + // Add upgraded-state flags when request has been + // upgraded with HTTPS-Only Mode if (win) { - mState |= win->GetMixedContentSecurityFlags(); + // Check if top-level load has been upgraded + uint32_t httpsOnlyStatus = win->HttpsOnlyStatus(); + if (!(httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) && + !(httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT)) { + mState |= nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED; + } + // Add the secruity flags from the window + mState |= win->GetSecurityFlags(); } // If we have loaded mixed content and this is a secure page, diff --git a/security/manager/ssl/nsSecureBrowserUI.h b/security/manager/ssl/nsSecureBrowserUI.h index 22793af57d47..a71d9ebde443 100644 --- a/security/manager/ssl/nsSecureBrowserUI.h +++ b/security/manager/ssl/nsSecureBrowserUI.h @@ -39,7 +39,7 @@ class nsSecureBrowserUI : public nsISecureBrowserUI, NS_DECL_ISUPPORTS NS_DECL_NSISECUREBROWSERUI - void UpdateForLocationOrMixedContentChange(); + void RecomputeSecurityFlags(); protected: virtual ~nsSecureBrowserUI() = default; diff --git a/uriloader/base/nsIWebProgressListener.idl b/uriloader/base/nsIWebProgressListener.idl index 789b4ae96990..bb979974bf64 100644 --- a/uriloader/base/nsIWebProgressListener.idl +++ b/uriloader/base/nsIWebProgressListener.idl @@ -354,6 +354,18 @@ interface nsIWebProgressListener : nsISupports const unsigned long STATE_LOADED_SOCIALTRACKING_CONTENT = 0x00020000; const unsigned long STATE_UNBLOCKED_UNSAFE_CONTENT = 0x00000010; + /** + * Flag for HTTPS-Only Mode upgrades + * + * STATE_HTTPS_ONLY_MODE_UPGRADED + * When a request has been upgraded by HTTPS-Only Mode + * + * STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED + * When an upgraded request failed. + */ + const unsigned long STATE_HTTPS_ONLY_MODE_UPGRADED = 0x00400000; + const unsigned long STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED = 0x00800000; + /** * Notification indicating the state has changed for one of the requests * associated with aWebProgress.