Set Referrer header for media downloads
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 21 Feb 2012 21:08:43 +0000 (21:08 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 21 Feb 2012 21:08:43 +0000 (21:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=78614

Reviewed by Alexey Proskuryakov.

Source/WebCore:

Test: http/tests/media/video-referer.html was modified to test this change.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::mediaPlayerReferrer): New, return the document's referer.
* html/HTMLMediaElement.h:

* platform/graphics/MediaPlayer.cpp:
(WebCore::MediaPlayer::referrer): New, return the client's mediaPlayerReferrer.
* platform/graphics/MediaPlayer.h:
(WebCore::MediaPlayerClient::mediaPlayerReferrer):

* platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL): Tell AVFoundation to add
    a referer header.

LayoutTests:

* http/tests/media/resources/video-referer-check-referer.php: Get the test file and content-type
    from parameters on the url. Fail if referrer is anything but video-referer.html. Add support for
    byte-range requests so it will work with AVFoundation.
* http/tests/media/video-referer.html: Pass video file name and content-type as url parameters
    instead of having another cgi set a cookie. Set the php script as the 'src' on a <source> element
    instead of on the <video> element so we can also add a 'type' attribute.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@108387 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/http/tests/media/resources/setCookieAndReferer.cgi [deleted file]
LayoutTests/http/tests/media/resources/video-referer-check-referer.php
LayoutTests/http/tests/media/video-referer.html
Source/WebCore/ChangeLog
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/platform/graphics/MediaPlayer.cpp
Source/WebCore/platform/graphics/MediaPlayer.h
Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm

index 3b5b944..b8bb96e 100644 (file)
@@ -1,3 +1,17 @@
+2012-02-21  Eric Carlson  <eric.carlson@apple.com>
+
+        Set Referrer header for media downloads
+        https://bugs.webkit.org/show_bug.cgi?id=78614
+
+        Reviewed by Alexey Proskuryakov.
+
+        * http/tests/media/resources/video-referer-check-referer.php: Get the test file and content-type
+            from parameters on the url. Fail if referrer is anything but video-referer.html. Add support for
+            byte-range requests so it will work with AVFoundation.
+        * http/tests/media/video-referer.html: Pass video file name and content-type as url parameters
+            instead of having another cgi set a cookie. Set the php script as the 'src' on a <source> element 
+            instead of on the <video> element so we can also add a 'type' attribute. 
+
 2012-02-21  Chris Fleizach  <cfleizach@apple.com>
 
         AX: move aria-invalid test to top level, so other platforms can use it
diff --git a/LayoutTests/http/tests/media/resources/setCookieAndReferer.cgi b/LayoutTests/http/tests/media/resources/setCookieAndReferer.cgi
deleted file mode 100755 (executable)
index fc96efd..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/perl -wT
-use CGI;
-
-$query = new CGI;
-$name = $query->param('name');
-$referer = $query->param('referer');
-
-print "Content-Type: text/plain\n";
-print "Cache-Control: no-store\n";
-print 'Cache-Control: no-cache="set-cookie"' . "\n";
-
-print "Referer: ${referer}\n";
-
-# We only map the SET_COOKIE request header to "Set-Cookie"
-print "Set-Cookie: TEST=${name}\n\n";
index d888044..63dd279 100644 (file)
@@ -1,24 +1,52 @@
 <?php
-    if($_SERVER['HTTP_REFERER'])
+
+    $refer = $_SERVER["HTTP_REFERER"];
+    if (!isset($refer) || stripos($refer, "video-referer.html") === false)
+        die;
+
+    $fileName = $_GET["name"];
+    $type = $_GET["type"];
+
+    $fileSize = filesize($fileName);
+    $start = 0;
+    $end = $fileSize - 1;
+    $contentRange = $_SERVER["HTTP_RANGE"];
+    if (isset($contentRange))
     {
-        $extension = substr($_COOKIE["TEST"], -3);
-
-        if ($extension == 'mp4') {
-               header("Content-Type: video/mp4");
-               $fileName = "test.mp4";
-        } else if ($extension == 'ogv') {
-               header("Content-Type: video/ogg");
-               $fileName = "test.ogv";
-        } else
-               die;
-
-        header("Cache-Control: no-store");
-        header("Connection: close");
-
-        $fn = fopen($fileName, "r");
-        fpassthru($fn);
-        fclose($fn);
-        exit;
+        $range = explode("-", substr($contentRange, 6)); 
+        $start = intval($range[0]); 
+        if (!empty($range[1]))
+            $end = intval($range[1]);
+        $httpStatus = "HTTP/1.1 206 Partial Content";
     } else
-        die;
+        $httpStatus = "200 OK";
+
+    header("Status: " . $httpStatus);
+    header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
+    header("Pragma: no-cache");
+    header("Etag: " . '"' . $fileSize . "-" . filemtime($fileName) . '"');
+    header("Content-Type: " . $type);
+    header("Accept-Ranges: bytes");
+    header("Content-Length: " . ($end - $start) + 1);
+    if ($contentRange)
+               header("Content-Range: bytes " . $start . "-" . $end . "/" . $fileSize); 
+    header("Connection: close");
+
+    $chunkSize = 1024 * 256;
+    $offset = $start;
+
+    $fn = fopen($fileName, "rb");
+    fseek($fn, $offset, 0);
+
+    while (!feof($fn) && $offset <= $end && connection_status() == 0)
+    {
+        $readSize = min($chunkSize, ($end - $offset) + 1);
+        $buffer = fread($fn, $readSize);
+        print($buffer);
+        flush();
+        $offset += $chunkSize;
+    }
+    fclose($fn);
+
+    exit;
 ?>
index 83770a0..69fedb2 100644 (file)
@@ -2,25 +2,25 @@
 <head>
 </head>
 <body onload="loadCookieAndReferer()">
-<video id="video"></video>
+<video id="video">
+    <source id="source">
+</video>
 <script src=../../media-resources/video-test.js></script>
 <script src=../../media-resources/media-file.js></script>
 <script>
-    if (window.layoutTestController) {
-        layoutTestController.setAlwaysAcceptCookies(true);
-    }
-
     function loadCookieAndReferer () {
-        var movie = findMediaFile('video', 'resources/test');
+        var movie = findMediaFile('video', 'test');
+        var type = mimeTypeForExtension(movie.split('.').pop());
         var frame = document.createElement('iframe');
         frame.width = 0;
         frame.height = 0;
-        frame.src = 'http://127.0.0.1:8000/media/resources/setCookieAndReferer.cgi?name=' + movie + '&referer=http://127.0.0.1:8000/media/resources';
-
+        frame.src = "data:text/html,<b>test</b>";
         frame.addEventListener('load', function () {
                 video = document.getElementById('video');
-                video.src='http://127.0.0.1:8000/media/resources/video-referer-check-referer.php';
-                video.play();
+                source = document.getElementById('source');
+                source.src = 'http://127.0.0.1:8000/media/resources/video-referer-check-referer.php?name=' + movie + '&type=' + type;
+                source.type = type;
+                video.load();
         });
 
         document.body.appendChild(frame);
index 5970527..7ef3ac7 100644 (file)
@@ -1,3 +1,26 @@
+2012-02-21  Eric Carlson  <eric.carlson@apple.com>
+
+        Set Referrer header for media downloads
+        https://bugs.webkit.org/show_bug.cgi?id=78614
+
+        Reviewed by Alexey Proskuryakov.
+
+        Test: http/tests/media/video-referer.html was modified to test this change.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::mediaPlayerReferrer): New, return the document's referer.
+        * html/HTMLMediaElement.h:
+
+        * platform/graphics/MediaPlayer.cpp:
+        (WebCore::MediaPlayer::referrer): New, return the client's mediaPlayerReferrer.
+        * platform/graphics/MediaPlayer.h:
+        (WebCore::MediaPlayerClient::mediaPlayerReferrer):
+
+        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL): Tell AVFoundation to add
+            a referer header.
+
 2012-02-21  Antti Koivisto  <antti@apple.com>
 
         Not reviewed.
index a4092e1..d21f7af 100644 (file)
@@ -69,6 +69,7 @@
 #include "ScriptController.h"
 #include "ScriptEventListener.h"
 #include "SecurityOrigin.h"
+#include "SecurityPolicy.h"
 #include "Settings.h"
 #include "ShadowRoot.h"
 #include "ShadowRootList.h"
@@ -4022,6 +4023,14 @@ bool HTMLMediaElement::shouldDisableSleep() const
 }
 #endif
 
+String HTMLMediaElement::mediaPlayerReferrer() const
+{
+    Frame* frame = document()->frame();
+    if (!frame)
+        return String();
+
+    return SecurityPolicy::generateReferrerHeader(document()->referrerPolicy(), m_currentSrc, frame->loader()->outgoingReferrer());
 }
 
+}
 #endif
index 68855ae..404b5a4 100644 (file)
@@ -388,6 +388,8 @@ private:
     virtual String mediaPlayerSourceURL() const;
 #endif
 
+    virtual String mediaPlayerReferrer() const OVERRIDE;
+
     void loadTimerFired(Timer<HTMLMediaElement>*);
     void progressEventTimerFired(Timer<HTMLMediaElement>*);
     void playbackProgressTimerFired(Timer<HTMLMediaElement>*);
index 985e27c..9813eee 100644 (file)
@@ -928,6 +928,14 @@ AudioSourceProvider* MediaPlayer::audioSourceProvider()
     return m_private->audioSourceProvider();
 }
 #endif // WEB_AUDIO
+    
+String MediaPlayer::referrer() const
+{
+    if (!m_mediaPlayerClient)
+        return String();
+
+    return m_mediaPlayerClient->mediaPlayerReferrer();
+}
 
 }
 
index 26ca3aa..2ccfb72 100644 (file)
@@ -167,6 +167,8 @@ public:
     virtual void mediaPlayerSourceOpened() { }
     virtual String mediaPlayerSourceURL() const { return "x-media-source-unsupported:"; }
 #endif
+
+    virtual String mediaPlayerReferrer() const { return String(); }
 };
 
 class MediaPlayer {
@@ -286,7 +288,6 @@ public:
     void firstVideoFrameAvailable();
     void characteristicChanged();
 
-
     void repaint();
 
     MediaPlayerClient* mediaPlayerClient() const { return m_mediaPlayerClient; }
@@ -334,6 +335,8 @@ public:
     String sourceURL() const;
 #endif
 
+    String referrer() const;
+
 private:
     MediaPlayer(MediaPlayerClient*);
     void loadWithNextMediaEngine(MediaPlayerFactory*);
index b49791e..7230234 100644 (file)
@@ -242,6 +242,8 @@ protected:
 
     const String& assetURL() const { return m_assetURL; }
 
+    MediaPlayer* player() { return m_player; }
+
 private:
     MediaPlayer* m_player;
 
index bd28dd3..0b30b2e 100644 (file)
@@ -256,11 +256,21 @@ void MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(const String& url)
 
     setDelayCallbacks(true);
 
-    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
-                        [NSNumber numberWithInt:AVAssetReferenceRestrictionForbidRemoteReferenceToLocal | AVAssetReferenceRestrictionForbidLocalReferenceToRemote], AVURLAssetReferenceRestrictionsKey, 
-                        nil];
+    RetainPtr<NSMutableDictionary> options(AdoptNS, [[NSMutableDictionary alloc] init]);    
+
+    [options.get() setObject:[NSNumber numberWithInt:AVAssetReferenceRestrictionForbidRemoteReferenceToLocal | AVAssetReferenceRestrictionForbidLocalReferenceToRemote] forKey:AVURLAssetReferenceRestrictionsKey];
+
+#if !defined(BUILDING_ON_SNOW_LEOPARD) && !defined(BUILDING_ON_LION)
+    String referrer = player()->referrer();
+    if (!referrer.isEmpty()) {
+        RetainPtr<NSMutableDictionary> headerFields(AdoptNS, [[NSMutableDictionary alloc] init]);
+        [headerFields.get() setObject:referrer forKey:@"Referer"];
+        [options.get() setObject:headerFields.get() forKey:@"AVURLAssetHTTPHeaderFieldsKey"];
+    }
+#endif
+
     NSURL *cocoaURL = KURL(ParsedURLString, url);
-    m_avAsset.adoptNS([[AVURLAsset alloc] initWithURL:cocoaURL options:options]);
+    m_avAsset.adoptNS([[AVURLAsset alloc] initWithURL:cocoaURL options:options.get()]);
 
     m_haveCheckedPlayability = false;