Bug 1406278: Part 5a - Use subject principal as triggering principal in <audio>/<video> "src" attribute. r=bz
authorKris Maglione <maglione.k@gmail.com>
Thu, 05 Oct 2017 14:47:09 -0700
changeset 427839 e894f7b9168b33eebda126c7b4b431a833807710
parent 427838 8e7305ab6283ee4bd9ebf2a352c276b72c93b269
child 427840 ac4afd1e97b7856b0ff604fb7b867d555cecd40c
push id97
push userfmarier@mozilla.com
push dateSat, 14 Oct 2017 01:12:59 +0000
reviewersbz
bugs1406278
milestone58.0a1
Bug 1406278: Part 5a - Use subject principal as triggering principal in <audio>/<video> "src" attribute. r=bz MozReview-Commit-ID: A1JixlTeZGq
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/webidl/HTMLMediaElement.webidl
toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1188,32 +1188,33 @@ public:
     }
 
     MOZ_ASSERT(aElement->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
     nsContentPolicyType contentPolicyType = aElement->IsHTMLElement(nsGkAtoms::audio)
       ? nsIContentPolicy::TYPE_INTERNAL_AUDIO :
         nsIContentPolicy::TYPE_INTERNAL_VIDEO;
 
     // If aElement has 'loadingprincipal' attribute, we will use the value as
-    // loadingPrincipal for the channel, otherwise it will default to use
+    // triggeringPrincipal for the channel, otherwise it will default to use
     // aElement->NodePrincipal().
     // This function returns true when aElement has 'loadingprincipal', so if
     // setAttrs is true we will override the origin attributes on the channel
     // later.
-    nsCOMPtr<nsIPrincipal> loadingPrincipal;
+    nsCOMPtr<nsIPrincipal> triggeringPrincipal;
     bool setAttrs = nsContentUtils::GetLoadingPrincipalForXULNode(aElement,
-                                    getter_AddRefs(loadingPrincipal));
+                                    aElement->mLoadingSrcTriggeringPrincipal,
+                                    getter_AddRefs(triggeringPrincipal));
 
     nsCOMPtr<nsILoadGroup> loadGroup = aElement->GetDocumentLoadGroup();
     nsCOMPtr<nsIChannel> channel;
     nsresult rv =
       NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
                                            aElement->mLoadingSrc,
                                            static_cast<Element*>(aElement),
-                                           loadingPrincipal,
+                                           triggeringPrincipal,
                                            securityFlags,
                                            contentPolicyType,
                                            loadGroup,
                                            nullptr,   // aCallbacks
                                            nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
                                            nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE |
                                            nsIChannel::LOAD_CLASSIFY_URI |
                                            nsIChannel::LOAD_CALL_CONTENT_SNIFFERS);
@@ -1223,17 +1224,17 @@ public:
       aElement->NotifyLoadError();
       return;
     }
 
     if (setAttrs) {
       nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
       if (loadInfo) {
         // The function simply returns NS_OK, so we ignore the return value.
-        Unused << loadInfo->SetOriginAttributes(loadingPrincipal->OriginAttributesRef());
+        Unused << loadInfo->SetOriginAttributes(triggeringPrincipal->OriginAttributesRef());
       }
     }
 
     nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
     if (cos) {
       if (aElement->mUseUrgentStartForChannel) {
         cos->AddClassFlags(nsIClassOfService::UrgentStart);
 
@@ -1788,16 +1789,17 @@ void HTMLMediaElement::AbortExistingLoad
     ShutdownDecoder();
   }
   if (mSrcStream) {
     EndSrcMediaStreamPlayback();
   }
 
   RemoveMediaElementFromURITable();
   mLoadingSrc = nullptr;
+  mLoadingSrcTriggeringPrincipal = nullptr;
   mMediaSource = nullptr;
 
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING ||
       mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE)
   {
     DispatchAsyncEvent(NS_LITERAL_STRING("abort"));
   }
 
@@ -2049,16 +2051,17 @@ void HTMLMediaElement::SelectResource()
     nsresult rv = NewURIFromString(src, getter_AddRefs(uri));
     if (NS_SUCCEEDED(rv)) {
       LOG(LogLevel::Debug, ("%p Trying load from src=%s", this, NS_ConvertUTF16toUTF8(src).get()));
       NS_ASSERTION(!mIsLoadingFromSourceChildren,
         "Should think we're not loading from source children by default");
 
       RemoveMediaElementFromURITable();
       mLoadingSrc = uri;
+      mLoadingSrcTriggeringPrincipal = mSrcAttrTriggeringPrincipal;
       mMediaSource = mSrcMediaSource;
       UpdatePreloadAction();
       if (mPreloadAction == HTMLMediaElement::PRELOAD_NONE &&
           !IsMediaStreamURI(mLoadingSrc) && !mMediaSource) {
         // preload:none media, suspend the load here before we make any
         // network requests.
         SuspendLoad();
         return;
@@ -2369,16 +2372,17 @@ void HTMLMediaElement::LoadFromSourceChi
       const char16_t* params[] = { src.get() };
       ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
       DealWithFailedElement(child);
       return;
     }
 
     RemoveMediaElementFromURITable();
     mLoadingSrc = uri;
+    mLoadingSrcTriggeringPrincipal = nullptr;
     mMediaSource = childSrc->GetSrcMediaSource();
     NS_ASSERTION(mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING,
                  "Network state should be loading");
 
     if (mPreloadAction == HTMLMediaElement::PRELOAD_NONE &&
         !IsMediaStreamURI(mLoadingSrc) && !mMediaSource) {
       // preload:none media, suspend the load here before we make any
       // network requests.
@@ -2593,16 +2597,17 @@ nsresult HTMLMediaElement::LoadWithChann
 
   // Make sure we don't reenter during synchronous abort events.
   if (mIsRunningLoadMethod)
     return NS_OK;
   mIsRunningLoadMethod = true;
   AbortExistingLoads();
   mIsRunningLoadMethod = false;
 
+  mLoadingSrcTriggeringPrincipal = nullptr;
   nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(mLoadingSrc));
   NS_ENSURE_SUCCESS(rv, rv);
 
   ChangeDelayLoadStatus(true);
   rv = InitializeDecoderForChannel(aChannel, aListener);
   if (NS_FAILED(rv)) {
     ChangeDelayLoadStatus(false);
     return rv;
@@ -4451,16 +4456,19 @@ HTMLMediaElement::AfterSetAttr(int32_t a
                                 const nsAttrValue* aValue,
                                 const nsAttrValue* aOldValue,
                                 nsIPrincipal* aMaybeScriptedPrincipal,
                                 bool aNotify)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::src) {
       mSrcMediaSource = nullptr;
+      mSrcAttrTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal(
+          this, aValue ? aValue->GetStringValue() : EmptyString(),
+          aMaybeScriptedPrincipal);
       if (aValue) {
         nsString srcStr = aValue->GetStringValue();
         nsCOMPtr<nsIURI> uri;
         NewURIFromString(srcStr, getter_AddRefs(uri));
         if (uri && IsMediaSourceURI(uri)) {
           nsresult rv =
             NS_GetSourceForMediaSourceURI(uri, getter_AddRefs(mSrcMediaSource));
           if (NS_FAILED(rv)) {
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -430,20 +430,23 @@ public:
    * cycle collection unlinking!
    */
   MediaStream* GetSrcMediaStream() const;
 
   // WebIDL
 
   MediaError* GetError() const;
 
-  // XPCOM GetSrc() is OK
-  void SetSrc(const nsAString& aSrc, ErrorResult& aRv)
+  void GetSrc(nsString& aSrc, nsIPrincipal&)
   {
-    SetHTMLAttr(nsGkAtoms::src, aSrc, aRv);
+    GetSrc(aSrc);
+  }
+  void SetSrc(const nsAString& aSrc, nsIPrincipal& aTriggeringPrincipal, ErrorResult& aRv)
+  {
+    SetHTMLAttr(nsGkAtoms::src, aSrc, aTriggeringPrincipal, aRv);
   }
 
   // XPCOM GetCurrentSrc() is OK
 
   void GetCrossOrigin(nsAString& aResult)
   {
     // Null for both missing and invalid defaults is ok, since we
     // always parse to an enum value, so we don't need an invalid
@@ -1359,16 +1362,19 @@ protected:
   // A reference to the VideoFrameContainer which contains the current frame
   // of video to display.
   RefPtr<VideoFrameContainer> mVideoFrameContainer;
 
   // Holds a reference to the DOM wrapper for the MediaStream that has been
   // set in the src attribute.
   RefPtr<DOMMediaStream> mSrcAttrStream;
 
+  // Holds the triggering principal for the src attribute.
+  nsCOMPtr<nsIPrincipal> mSrcAttrTriggeringPrincipal;
+
   // Holds a reference to the DOM wrapper for the MediaStream that we're
   // actually playing.
   // At most one of mDecoder and mSrcStream can be non-null.
   RefPtr<DOMMediaStream> mSrcStream;
 
   // True once mSrcStream's initial set of tracks are known.
   bool mSrcStreamTracksAvailable;
 
@@ -1470,16 +1476,19 @@ protected:
 
   // URI of the resource we're attempting to load. This stores the value we
   // return in the currentSrc attribute. Use GetCurrentSrc() to access the
   // currentSrc attribute.
   // This is always the original URL we're trying to load --- before
   // redirects etc.
   nsCOMPtr<nsIURI> mLoadingSrc;
 
+  // The triggering principal for the current source.
+  nsCOMPtr<nsIPrincipal> mLoadingSrcTriggeringPrincipal;
+
   // Stores the current preload action for this element. Initially set to
   // PRELOAD_UNDEFINED, its value is changed by calling
   // UpdatePreloadAction().
   PreloadAction mPreloadAction;
 
   // Time that the last timeupdate event was fired. Read/Write from the
   // main thread only.
   TimeStamp mTimeUpdateTime;
--- a/dom/webidl/HTMLMediaElement.webidl
+++ b/dom/webidl/HTMLMediaElement.webidl
@@ -12,17 +12,17 @@
  */
 
 interface HTMLMediaElement : HTMLElement {
 
   // error state
   readonly attribute MediaError? error;
 
   // network state
-  [CEReactions, SetterThrows]
+  [CEReactions, NeedsSubjectPrincipal, SetterThrows]
            attribute DOMString src;
   readonly attribute DOMString currentSrc;
 
   [CEReactions, SetterThrows]
            attribute DOMString? crossOrigin;
   const unsigned short NETWORK_EMPTY = 0;
   const unsigned short NETWORK_IDLE = 1;
   const unsigned short NETWORK_LOADING = 2;
@@ -211,9 +211,9 @@ partial interface HTMLMediaElement {
  *   drawImage().
  */
 partial interface HTMLMediaElement {
   [Pref="media.test.video-suspend"]
   void setVisible(boolean aVisible);
 
   [Pref="media.test.video-suspend"]
   boolean hasSuspendTaint();
-};
\ No newline at end of file
+};
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript_triggeringPrincipal.js
@@ -428,16 +428,20 @@ function awaitLoads(tests, sources, orig
 }
 
 add_task(async function test_contentscript_triggeringPrincipals() {
   /**
    * A list of tests to run in each context, as understood by
    * {@see getElementData}.
    */
   const TESTS = [
+    {
+      element: ["audio", {}],
+      src: "audio.webm",
+    },
     // TODO: <frame> element, which requires a frameset document.
     {
       element: ["iframe", {}],
       src: "iframe.html",
     },
     {
       element: ["img", {}],
       src: "img.png",
@@ -447,16 +451,20 @@ add_task(async function test_contentscri
       src: "imgset.png",
       srcAttr: "srcset",
     },
     {
       element: ["script", {}],
       src: "script.js",
       liveSrc: false,
     },
+    {
+      element: ["video", {}],
+      src: "video.webm",
+    },
   ];
 
   /**
    * A set of sources for which each of the above tests is expected to
    * generate one request, if each of the properties in the value object
    * matches the value of the same property in the test object.
    */
   const SOURCES = {