simple_data_source.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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 "base/message_loop.h"
6#include "base/process_util.h"
7#include "media/base/filter_host.h"
8#include "net/base/load_flags.h"
9#include "net/base/data_url.h"
10#include "net/http/http_response_headers.h"
11#include "net/url_request/url_request_status.h"
12#include "webkit/glue/media/simple_data_source.h"
13#include "webkit/glue/resource_loader_bridge.h"
14#include "webkit/glue/webkit_glue.h"
15
16namespace {
17
18const char kHttpScheme[] = "http";
19const char kHttpsScheme[] = "https";
20const char kDataScheme[] = "data";
21
22// A helper method that accepts only HTTP, HTTPS and FILE protocol.
23bool IsDataProtocol(const GURL& url) {
24  return url.SchemeIs(kDataScheme);
25}
26
27}  // namespace
28
29namespace webkit_glue {
30
31SimpleDataSource::SimpleDataSource(
32    MessageLoop* render_loop,
33    webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory)
34    : render_loop_(render_loop),
35      bridge_factory_(bridge_factory),
36      size_(-1),
37      single_origin_(true),
38      state_(UNINITIALIZED) {
39  DCHECK(render_loop);
40}
41
42SimpleDataSource::~SimpleDataSource() {
43  AutoLock auto_lock(lock_);
44  DCHECK(state_ == UNINITIALIZED || state_ == STOPPED);
45}
46
47void SimpleDataSource::Stop(media::FilterCallback* callback) {
48  AutoLock auto_lock(lock_);
49  state_ = STOPPED;
50  if (callback) {
51    callback->Run();
52    delete callback;
53  }
54
55  // Post a task to the render thread to cancel loading the resource.
56  render_loop_->PostTask(FROM_HERE,
57      NewRunnableMethod(this, &SimpleDataSource::CancelTask));
58}
59
60void SimpleDataSource::Initialize(const std::string& url,
61                                  media::FilterCallback* callback) {
62  AutoLock auto_lock(lock_);
63  DCHECK_EQ(state_, UNINITIALIZED);
64  DCHECK(callback);
65  state_ = INITIALIZING;
66  initialize_callback_.reset(callback);
67
68  // Validate the URL.
69  SetURL(GURL(url));
70  if (!url_.is_valid() || !IsProtocolSupportedForMedia(url_)) {
71    host()->SetError(media::PIPELINE_ERROR_NETWORK);
72    initialize_callback_->Run();
73    initialize_callback_.reset();
74    return;
75  }
76
77  // Post a task to the render thread to start loading the resource.
78  render_loop_->PostTask(FROM_HERE,
79      NewRunnableMethod(this, &SimpleDataSource::StartTask));
80}
81
82const media::MediaFormat& SimpleDataSource::media_format() {
83  return media_format_;
84}
85
86void SimpleDataSource::Read(int64 position,
87                            size_t size,
88                            uint8* data,
89                            ReadCallback* read_callback) {
90  DCHECK_GE(size_, 0);
91  if (position >= size_) {
92    read_callback->RunWithParams(Tuple1<size_t>(0));
93    delete read_callback;
94  } else {
95    size_t copied = std::min(size, static_cast<size_t>(size_ - position));
96    memcpy(data, data_.c_str() + position, copied);
97    read_callback->RunWithParams(Tuple1<size_t>(copied));
98    delete read_callback;
99  }
100}
101
102bool SimpleDataSource::GetSize(int64* size_out) {
103  *size_out = size_;
104  return true;
105}
106
107bool SimpleDataSource::IsStreaming() {
108  return false;
109}
110
111bool SimpleDataSource::OnReceivedRedirect(
112    const GURL& new_url,
113    const webkit_glue::ResourceResponseInfo& info,
114    bool* has_new_first_party_for_cookies,
115    GURL* new_first_party_for_cookies) {
116  DCHECK(MessageLoop::current() == render_loop_);
117  single_origin_ = url_.GetOrigin() == new_url.GetOrigin();
118
119  // TODO(wtc): should we return a new first party for cookies URL?
120  *has_new_first_party_for_cookies = false;
121  return true;
122}
123
124void SimpleDataSource::OnReceivedResponse(
125    const webkit_glue::ResourceResponseInfo& info,
126    bool content_filtered) {
127  DCHECK(MessageLoop::current() == render_loop_);
128  size_ = info.content_length;
129}
130
131void SimpleDataSource::OnReceivedData(const char* data, int len) {
132  DCHECK(MessageLoop::current() == render_loop_);
133  data_.append(data, len);
134}
135
136void SimpleDataSource::OnCompletedRequest(const URLRequestStatus& status,
137                                          const std::string& security_info,
138                                          const base::Time& completion_time) {
139  DCHECK(MessageLoop::current() == render_loop_);
140  AutoLock auto_lock(lock_);
141  // It's possible this gets called after Stop(), in which case |host_| is no
142  // longer valid.
143  if (state_ == STOPPED)
144    return;
145
146  // Otherwise we should be initializing and have created a bridge.
147  DCHECK_EQ(state_, INITIALIZING);
148  DCHECK(bridge_.get());
149  bridge_.reset();
150
151  // If we don't get a content length or the request has failed, report it
152  // as a network error.
153  if (size_ == -1)
154    size_ = data_.length();
155  DCHECK(static_cast<size_t>(size_) == data_.length());
156
157  DoneInitialization_Locked(status.is_success());
158}
159
160bool SimpleDataSource::HasSingleOrigin() {
161  DCHECK(MessageLoop::current() == render_loop_);
162  return single_origin_;
163}
164
165void SimpleDataSource::Abort() {
166  DCHECK(MessageLoop::current() == render_loop_);
167  NOTIMPLEMENTED();
168}
169
170void SimpleDataSource::SetURL(const GURL& url) {
171  url_ = url;
172  media_format_.Clear();
173  media_format_.SetAsString(media::MediaFormat::kMimeType,
174                            media::mime_type::kApplicationOctetStream);
175  media_format_.SetAsString(media::MediaFormat::kURL, url.spec());
176}
177
178void SimpleDataSource::StartTask() {
179  DCHECK(MessageLoop::current() == render_loop_);
180  AutoLock auto_lock(lock_);
181
182  // We may have stopped.
183  if (state_ == STOPPED)
184    return;
185
186  DCHECK_EQ(state_, INITIALIZING);
187
188  if (IsDataProtocol(url_)) {
189    // If this using data protocol, we just need to decode it.
190    std::string mime_type, charset;
191    bool success = net::DataURL::Parse(url_, &mime_type, &charset, &data_);
192
193    // Don't care about the mime-type just proceed if decoding was successful.
194    size_ = data_.length();
195    DoneInitialization_Locked(success);
196  } else {
197    // Create our bridge and start loading the resource.
198    bridge_.reset(bridge_factory_->CreateBridge(
199        url_, net::LOAD_BYPASS_CACHE, -1, -1));
200    bridge_->Start(this);
201  }
202}
203
204void SimpleDataSource::CancelTask() {
205  DCHECK(MessageLoop::current() == render_loop_);
206  AutoLock auto_lock(lock_);
207  DCHECK_EQ(state_, STOPPED);
208
209  // Cancel any pending requests.
210  if (bridge_.get()) {
211    bridge_->Cancel();
212    bridge_.reset();
213  }
214}
215
216void SimpleDataSource::DoneInitialization_Locked(bool success) {
217  lock_.AssertAcquired();
218  if (success) {
219    state_ = INITIALIZED;
220    host()->SetTotalBytes(size_);
221    host()->SetBufferedBytes(size_);
222    // If scheme is file or data, say we are loaded.
223    host()->SetLoaded(url_.SchemeIsFile() || IsDataProtocol(url_));
224  } else {
225    host()->SetError(media::PIPELINE_ERROR_NETWORK);
226  }
227  initialize_callback_->Run();
228  initialize_callback_.reset();
229}
230
231}  // namespace webkit_glue
232