1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/renderer/media/android/media_info_loader.h" 6 7 #include "base/bits.h" 8 #include "base/callback_helpers.h" 9 #include "base/metrics/histogram.h" 10 #include "third_party/WebKit/public/platform/WebURLError.h" 11 #include "third_party/WebKit/public/platform/WebURLLoader.h" 12 #include "third_party/WebKit/public/platform/WebURLResponse.h" 13 #include "third_party/WebKit/public/web/WebFrame.h" 14 15 using blink::WebFrame; 16 using blink::WebURLError; 17 using blink::WebURLLoader; 18 using blink::WebURLLoaderOptions; 19 using blink::WebURLRequest; 20 using blink::WebURLResponse; 21 22 namespace content { 23 24 static const int kHttpOK = 200; 25 static const int kHttpPartialContentOK = 206; 26 27 MediaInfoLoader::MediaInfoLoader( 28 const GURL& url, 29 blink::WebMediaPlayer::CORSMode cors_mode, 30 const ReadyCB& ready_cb) 31 : loader_failed_(false), 32 url_(url), 33 allow_stored_credentials_(false), 34 cors_mode_(cors_mode), 35 single_origin_(true), 36 ready_cb_(ready_cb) {} 37 38 MediaInfoLoader::~MediaInfoLoader() {} 39 40 void MediaInfoLoader::Start(blink::WebFrame* frame) { 41 // Make sure we have not started. 42 DCHECK(!ready_cb_.is_null()); 43 CHECK(frame); 44 45 start_time_ = base::TimeTicks::Now(); 46 first_party_url_ = frame->document().firstPartyForCookies(); 47 48 // Prepare the request. 49 WebURLRequest request(url_); 50 // TODO(mkwst): Split this into video/audio. 51 request.setRequestContext(WebURLRequest::RequestContextVideo); 52 frame->setReferrerForRequest(request, blink::WebURL()); 53 54 // Since we don't actually care about the media data at this time, use a two 55 // byte range request to avoid unnecessarily downloading resources. Not all 56 // servers support HEAD unfortunately, so use a range request; which is no 57 // worse than the previous request+cancel code. See http://crbug.com/400788 58 request.addHTTPHeaderField("Range", "bytes=0-1"); 59 60 scoped_ptr<WebURLLoader> loader; 61 if (test_loader_) { 62 loader = test_loader_.Pass(); 63 } else { 64 WebURLLoaderOptions options; 65 if (cors_mode_ == blink::WebMediaPlayer::CORSModeUnspecified) { 66 options.allowCredentials = true; 67 options.crossOriginRequestPolicy = 68 WebURLLoaderOptions::CrossOriginRequestPolicyAllow; 69 allow_stored_credentials_ = true; 70 } else { 71 options.exposeAllResponseHeaders = true; 72 // The author header set is empty, no preflight should go ahead. 73 options.preflightPolicy = WebURLLoaderOptions::PreventPreflight; 74 options.crossOriginRequestPolicy = 75 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; 76 if (cors_mode_ == blink::WebMediaPlayer::CORSModeUseCredentials) { 77 options.allowCredentials = true; 78 allow_stored_credentials_ = true; 79 } 80 } 81 loader.reset(frame->createAssociatedURLLoader(options)); 82 } 83 84 // Start the resource loading. 85 loader->loadAsynchronously(request, this); 86 active_loader_.reset(new media::ActiveLoader(loader.Pass())); 87 } 88 89 ///////////////////////////////////////////////////////////////////////////// 90 // blink::WebURLLoaderClient implementation. 91 void MediaInfoLoader::willSendRequest( 92 WebURLLoader* loader, 93 WebURLRequest& newRequest, 94 const WebURLResponse& redirectResponse) { 95 // The load may have been stopped and |ready_cb| is destroyed. 96 // In this case we shouldn't do anything. 97 if (ready_cb_.is_null()) { 98 // Set the url in the request to an invalid value (empty url). 99 newRequest.setURL(blink::WebURL()); 100 return; 101 } 102 103 // Only allow |single_origin_| if we haven't seen a different origin yet. 104 if (single_origin_) 105 single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin(); 106 107 url_ = newRequest.url(); 108 first_party_url_ = newRequest.firstPartyForCookies(); 109 allow_stored_credentials_ = newRequest.allowStoredCredentials(); 110 } 111 112 void MediaInfoLoader::didSendData( 113 WebURLLoader* loader, 114 unsigned long long bytes_sent, 115 unsigned long long total_bytes_to_be_sent) { 116 NOTIMPLEMENTED(); 117 } 118 119 void MediaInfoLoader::didReceiveResponse( 120 WebURLLoader* loader, 121 const WebURLResponse& response) { 122 DVLOG(1) << "didReceiveResponse: HTTP/" 123 << (response.httpVersion() == WebURLResponse::HTTP_0_9 ? "0.9" : 124 response.httpVersion() == WebURLResponse::HTTP_1_0 ? "1.0" : 125 response.httpVersion() == WebURLResponse::HTTP_1_1 ? "1.1" : 126 "Unknown") 127 << " " << response.httpStatusCode(); 128 DCHECK(active_loader_.get()); 129 if (!url_.SchemeIs(url::kHttpScheme) && !url_.SchemeIs(url::kHttpsScheme)) { 130 DidBecomeReady(kOk); 131 return; 132 } 133 if (response.httpStatusCode() == kHttpOK || 134 response.httpStatusCode() == kHttpPartialContentOK) { 135 DidBecomeReady(kOk); 136 return; 137 } 138 loader_failed_ = true; 139 DidBecomeReady(kFailed); 140 } 141 142 void MediaInfoLoader::didReceiveData( 143 WebURLLoader* loader, 144 const char* data, 145 int data_length, 146 int encoded_data_length) { 147 // Ignored. 148 } 149 150 void MediaInfoLoader::didDownloadData( 151 blink::WebURLLoader* loader, 152 int dataLength, 153 int encodedDataLength) { 154 NOTIMPLEMENTED(); 155 } 156 157 void MediaInfoLoader::didReceiveCachedMetadata( 158 WebURLLoader* loader, 159 const char* data, 160 int data_length) { 161 NOTIMPLEMENTED(); 162 } 163 164 void MediaInfoLoader::didFinishLoading( 165 WebURLLoader* loader, 166 double finishTime, 167 int64_t total_encoded_data_length) { 168 DCHECK(active_loader_.get()); 169 DidBecomeReady(kOk); 170 } 171 172 void MediaInfoLoader::didFail( 173 WebURLLoader* loader, 174 const WebURLError& error) { 175 DVLOG(1) << "didFail: reason=" << error.reason 176 << ", isCancellation=" << error.isCancellation 177 << ", domain=" << error.domain.utf8().data() 178 << ", localizedDescription=" 179 << error.localizedDescription.utf8().data(); 180 DCHECK(active_loader_.get()); 181 loader_failed_ = true; 182 DidBecomeReady(kFailed); 183 } 184 185 bool MediaInfoLoader::HasSingleOrigin() const { 186 DCHECK(ready_cb_.is_null()) 187 << "Must become ready before calling HasSingleOrigin()"; 188 return single_origin_; 189 } 190 191 bool MediaInfoLoader::DidPassCORSAccessCheck() const { 192 DCHECK(ready_cb_.is_null()) 193 << "Must become ready before calling DidPassCORSAccessCheck()"; 194 return !loader_failed_ && 195 cors_mode_ != blink::WebMediaPlayer::CORSModeUnspecified; 196 } 197 198 ///////////////////////////////////////////////////////////////////////////// 199 // Helper methods. 200 201 void MediaInfoLoader::DidBecomeReady(Status status) { 202 UMA_HISTOGRAM_TIMES("Media.InfoLoadDelay", 203 base::TimeTicks::Now() - start_time_); 204 active_loader_.reset(); 205 if (!ready_cb_.is_null()) 206 base::ResetAndReturn(&ready_cb_).Run(status, url_, first_party_url_, 207 allow_stored_credentials_); 208 } 209 210 } // namespace content 211