Bug 1532287 - P1 Saving the loading document/worker's COEP in InternalRequest. r=dom-workers-and-storage-reviewers,perry
authorEden Chuang <echuang@mozilla.com>
Tue, 19 May 2020 12:50:36 +0000
changeset 530781 9217f5aa798c5cdc52c2c0cccf65a8b5034cd518
parent 530780 cd0c41151346f7a3feea66205ca60efbe4bc4a6e
child 530782 7c9708ccec416e2d85bc83860e7ff24ae004e553
push id37433
push userdluca@mozilla.com
push dateWed, 20 May 2020 03:39:31 +0000
treeherdermozilla-central@855249e545c3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdom-workers-and-storage-reviewers, perry
bugs1532287, 1603168
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1532287 - P1 Saving the loading document/worker's COEP in InternalRequest. r=dom-workers-and-storage-reviewers,perry Currently, the worker's COEP value is saved in WorkerPrivate and it is not respected for fetch/cache API in workers. This patch saving the COEP value which fetch/cache API should be respected when using in workers. Notice that for the dedicated workers, it is not only respected to worker's COEP but also its owner's. For fetch in workers, P2 will propagate the COEP value through nsILoadInfo to HttpChannels, such that COEP can be respected in parent process when calling ProcessCrossOriginResourcePolicyHeader() and ProcessCrossOriginEmbedderPolicyHeader(). For cache in workers. We handle it in bug 1603168. COEP will be propagated through CacheRequest to the parent process and respected in CacheOpParent::OnOpComplete(). Differential Revision: https://phabricator.services.mozilla.com/D73689
dom/fetch/InternalRequest.h
dom/fetch/Request.cpp
dom/workers/ScriptLoader.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -335,16 +335,24 @@ class InternalRequest final : public Ato
   void SetPreferredAlternativeDataType(const nsACString& aDataType) {
     mPreferredAlternativeDataType = aDataType;
   }
 
   ~InternalRequest();
 
   InternalRequest(const InternalRequest& aOther) = delete;
 
+  void SetEmbedderPolicy(nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) {
+    mEmbedderPolicy = aPolicy;
+  }
+
+  nsILoadInfo::CrossOriginEmbedderPolicy GetEmbedderPolicy() const {
+    return mEmbedderPolicy;
+  }
+
  private:
   struct ConstructorGuard {};
 
  public:
   // Does not copy mBodyStream.  Use fallible Clone() for complete copy.
   InternalRequest(const InternalRequest& aOther, ConstructorGuard);
 
  private:
@@ -407,16 +415,18 @@ class InternalRequest final : public Ato
   bool mSkipServiceWorker = false;
   bool mSynchronous = false;
   bool mUnsafeRequest = false;
   bool mUseURLCredentials = false;
   // This is only set when Request.overrideContentPolicyType() has been set.
   // It is illegal to pass such a Request object to a fetch() method unless
   // if the caller has chrome privileges.
   bool mContentPolicyTypeOverridden = false;
+  nsILoadInfo::CrossOriginEmbedderPolicy mEmbedderPolicy =
+      nsILoadInfo::EMBEDDER_POLICY_NULL;
 
   UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_InternalRequest_h
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -424,16 +424,18 @@ SafeRefPtr<Request> Request::Constructor
     request->SetReferrerPolicy(aInit.mReferrerPolicy.Value());
   }
 
   if (aInit.mSignal.WasPassed()) {
     signal = aInit.mSignal.Value();
   }
 
   UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo;
+  nsILoadInfo::CrossOriginEmbedderPolicy coep =
+      nsILoadInfo::EMBEDDER_POLICY_NULL;
 
   if (NS_IsMainThread()) {
     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
     if (window) {
       nsCOMPtr<Document> doc;
       doc = window->GetExtantDoc();
       if (doc) {
         request->SetEnvironmentReferrerPolicy(doc->GetReferrerPolicy());
@@ -441,28 +443,38 @@ SafeRefPtr<Request> Request::Constructor
         principalInfo.reset(new mozilla::ipc::PrincipalInfo());
         nsresult rv =
             PrincipalToPrincipalInfo(doc->NodePrincipal(), principalInfo.get());
         if (NS_WARN_IF(NS_FAILED(rv))) {
           aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
           return nullptr;
         }
       }
+      if (window->GetWindowContext()) {
+        coep = window->GetWindowContext()->GetEmbedderPolicy();
+      }
     }
   } else {
     WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
     if (worker) {
       worker->AssertIsOnWorkerThread();
       request->SetEnvironmentReferrerPolicy(worker->GetReferrerPolicy());
       principalInfo =
           MakeUnique<mozilla::ipc::PrincipalInfo>(worker->GetPrincipalInfo());
+      coep = worker->GetEmbedderPolicy();
+      // For dedicated worker, the response must respect the owner's COEP.
+      if (coep == nsILoadInfo::EMBEDDER_POLICY_NULL &&
+          worker->IsDedicatedWorker()) {
+        coep = worker->GetOwnerEmbedderPolicy();
+      }
     }
   }
 
   request->SetPrincipalInfo(std::move(principalInfo));
+  request->SetEmbedderPolicy(coep);
 
   if (mode != RequestMode::EndGuard_) {
     request->SetMode(mode);
   }
 
   if (credentials != RequestCredentials::EndGuard_) {
     request->SetCredentialsMode(credentials);
   }
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -588,19 +588,16 @@ class ScriptResponseHeaderProcessor fina
       aRequest->Cancel(rv);
     }
 
     return rv;
   }
 
   NS_IMETHOD OnStopRequest(nsIRequest* aRequest,
                            nsresult aStatusCode) override {
-    MOZ_DIAGNOSTIC_ASSERT_IF(NS_SUCCEEDED(aStatusCode),
-                             mWorkerPrivate->GetEmbedderPolicy().isSome());
-
     return NS_OK;
   }
 
   static nsresult ProcessCrossOriginEmbedderPolicyHeader(
       WorkerPrivate* aWorkerPrivate,
       nsILoadInfo::CrossOriginEmbedderPolicy aPolicy, bool aIsMainScript) {
     MOZ_ASSERT(aWorkerPrivate);
 
@@ -616,18 +613,16 @@ class ScriptResponseHeaderProcessor fina
 
     return NS_OK;
   }
 
  private:
   ~ScriptResponseHeaderProcessor() = default;
 
   nsresult ProcessCrossOriginEmbedderPolicyHeader(nsIRequest* aRequest) {
-    MOZ_ASSERT_IF(!mIsMainScript, mWorkerPrivate->GetEmbedderPolicy().isSome());
-
     nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aRequest);
 
     // NOTE: the spec doesn't say what to do with non-HTTP workers.
     // See: https://github.com/whatwg/html/issues/4916
     if (!httpChannel) {
       if (mIsMainScript) {
         mWorkerPrivate->InheritOwnerEmbedderPolicyOrNull(aRequest);
       }
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -5227,100 +5227,106 @@ bool WorkerPrivate::CrossOriginIsolated(
   if (!StaticPrefs::dom_postMessage_sharedArrayBuffer_withCOOP_COEP()) {
     return false;
   }
 
   return mAgentClusterOpenerPolicy ==
          nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
 }
 
-Maybe<nsILoadInfo::CrossOriginEmbedderPolicy> WorkerPrivate::GetEmbedderPolicy()
+nsILoadInfo::CrossOriginEmbedderPolicy WorkerPrivate::GetEmbedderPolicy()
     const {
-  MOZ_ASSERT(NS_IsMainThread());
-
   if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
-    return Some(nsILoadInfo::EMBEDDER_POLICY_NULL);
-  }
-
-  return mEmbedderPolicy;
+    return nsILoadInfo::EMBEDDER_POLICY_NULL;
+  }
+
+  return mEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL);
 }
 
 Result<Ok, nsresult> WorkerPrivate::SetEmbedderPolicy(
     nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mEmbedderPolicy.isNothing());
 
   if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
     return Ok();
   }
 
   // If owner's emebedder policy is corp_reqired, aPolicy must also be
   // corp_reqired. But if owner's embedder policy is null, aPolicy needs not
   // match owner's value.
   // https://wicg.github.io/cross-origin-embedder-policy/#cascade-vs-require
-  auto ownerEmbedderPolicy = GetOwnerEmbedderPolicy();
-  if (ownerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL) !=
+  EnsureOwnerEmbedderPolicy();
+  if (mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL) !=
       nsILoadInfo::EMBEDDER_POLICY_NULL) {
-    if (ownerEmbedderPolicy.valueOr(aPolicy) != aPolicy) {
+    if (mOwnerEmbedderPolicy.valueOr(aPolicy) != aPolicy) {
       return Err(NS_ERROR_BLOCKED_BY_POLICY);
     }
   }
 
   mEmbedderPolicy.emplace(aPolicy);
 
   return Ok();
 }
 
 void WorkerPrivate::InheritOwnerEmbedderPolicyOrNull(nsIRequest* aRequest) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRequest);
 
-  auto coep = GetOwnerEmbedderPolicy();
-
-  if (coep.isSome()) {
+  EnsureOwnerEmbedderPolicy();
+
+  if (mOwnerEmbedderPolicy.isSome()) {
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     MOZ_ASSERT(channel);
 
     nsCOMPtr<nsIURI> scriptURI;
     MOZ_ALWAYS_SUCCEEDS(channel->GetURI(getter_AddRefs(scriptURI)));
 
     bool isLocalScriptURI = false;
     MOZ_ALWAYS_SUCCEEDS(NS_URIChainHasFlags(
         scriptURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
         &isLocalScriptURI));
 
     MOZ_RELEASE_ASSERT(isLocalScriptURI);
   }
 
-  mEmbedderPolicy.emplace(coep.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL));
+  mEmbedderPolicy.emplace(
+      mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL));
 }
 
 bool WorkerPrivate::MatchEmbedderPolicy(
     nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) const {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
     return true;
   }
 
   return mEmbedderPolicy.value() == aPolicy;
 }
 
-Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>
-WorkerPrivate::GetOwnerEmbedderPolicy() const {
+nsILoadInfo::CrossOriginEmbedderPolicy WorkerPrivate::GetOwnerEmbedderPolicy()
+    const {
+  if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
+    return nsILoadInfo::EMBEDDER_POLICY_NULL;
+  }
+
+  return mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL);
+}
+
+void WorkerPrivate::EnsureOwnerEmbedderPolicy() {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mOwnerEmbedderPolicy.isNothing());
 
   if (GetParent()) {
-    return GetParent()->GetEmbedderPolicy();
-  }
-
-  if (GetWindow() && GetWindow()->GetWindowContext()) {
-    return Some(GetWindow()->GetWindowContext()->GetEmbedderPolicy());
-  }
-
-  return Nothing();
+    mOwnerEmbedderPolicy.emplace(GetParent()->GetEmbedderPolicy());
+  } else if (GetWindow() && GetWindow()->GetWindowContext()) {
+    mOwnerEmbedderPolicy.emplace(
+        GetWindow()->GetWindowContext()->GetEmbedderPolicy());
+  }
 }
 
 NS_IMPL_ADDREF(WorkerPrivate::EventTarget)
 NS_IMPL_RELEASE(WorkerPrivate::EventTarget)
 
 NS_INTERFACE_MAP_BEGIN(WorkerPrivate::EventTarget)
   NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -928,18 +928,17 @@ class WorkerPrivate : public RelativeTim
 
   /**
    * COEP Methods
    *
    * If browser.tabs.remote.useCrossOriginEmbedderPolicy=false, these methods
    * will, depending on the return type, return a value that will avoid
    * assertion failures or a value that won't block loads.
    */
-
-  Maybe<nsILoadInfo::CrossOriginEmbedderPolicy> GetEmbedderPolicy() const;
+  nsILoadInfo::CrossOriginEmbedderPolicy GetEmbedderPolicy() const;
 
   // Fails if a policy has already been set or if `aPolicy` violates the owner's
   // policy, if an owner exists.
   mozilla::Result<Ok, nsresult> SetEmbedderPolicy(
       nsILoadInfo::CrossOriginEmbedderPolicy aPolicy);
 
   // `aRequest` is the request loading the worker and must be QI-able to
   // `nsIChannel*`. It's used to verify that the worker can indeed inherit its
@@ -949,16 +948,18 @@ class WorkerPrivate : public RelativeTim
   // always know its final, resolved script URL or have access internally to
   // `aRequest`.
   void InheritOwnerEmbedderPolicyOrNull(nsIRequest* aRequest);
 
   // Requires a policy to already have been set.
   bool MatchEmbedderPolicy(
       nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) const;
 
+  nsILoadInfo::CrossOriginEmbedderPolicy GetOwnerEmbedderPolicy() const;
+
  private:
   WorkerPrivate(
       WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker,
       WorkerType aWorkerType, const nsAString& aWorkerName,
       const nsACString& aServiceWorkerScope, WorkerLoadInfo& aLoadInfo,
       nsString&& aId, const nsID& aAgentClusterId,
       const nsILoadInfo::CrossOriginOpenerPolicy aAgentClusterOpenerPolicy);
 
@@ -1054,17 +1055,23 @@ class WorkerPrivate : public RelativeTim
     AssertIsOnWorkerThread();
     return mUseCounters[static_cast<size_t>(aUseCounter)];
   }
 
   void ReportUseCounters();
 
   UniquePtr<ClientSource> CreateClientSource();
 
-  Maybe<nsILoadInfo::CrossOriginEmbedderPolicy> GetOwnerEmbedderPolicy() const;
+  // This method is called when corresponding script loader processes the COEP
+  // header for the worker.
+  // This method should be called only once in the main thread.
+  // After this method is called the COEP value owner(window/parent worker) is
+  // cached in mOwnerEmbedderPolicy such that it can be accessed in other
+  // threads, i.e. WorkerThread.
+  void EnsureOwnerEmbedderPolicy();
 
   class EventTarget;
   friend class EventTarget;
   friend class AutoSyncLoopHolder;
 
   struct TimeoutInfo;
 
   class MemoryReporter;
@@ -1303,16 +1310,17 @@ class WorkerPrivate : public RelativeTim
 
   // Member variable of this class rather than the worker global scope because
   // it's received on the main thread, but the global scope is thread-bound
   // to the worker thread, so storing the value in the global scope would
   // involve sacrificing the thread-bound-ness or using a WorkerRunnable, and
   // there isn't a strong reason to store it on the global scope other than
   // better consistency with the COEP spec.
   Maybe<nsILoadInfo::CrossOriginEmbedderPolicy> mEmbedderPolicy;
+  Maybe<nsILoadInfo::CrossOriginEmbedderPolicy> mOwnerEmbedderPolicy;
 };
 
 class AutoSyncLoopHolder {
   WorkerPrivate* mWorkerPrivate;
   nsCOMPtr<nsIEventTarget> mTarget;
   uint32_t mIndex;
 
  public: