summaryrefslogtreecommitdiffstats
path: root/content/child/npapi/plugin_stream.cc
diff options
context:
space:
mode:
authorjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-19 01:13:47 +0000
committerjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-19 01:13:47 +0000
commit29e2fb456d3bf933168cf24bccaf05b1bea862fc (patch)
tree43eacb9a2201da4c6eaaa658facf35f9addd2a5b /content/child/npapi/plugin_stream.cc
parent83aa2eda2999b74dab6cb22d677acaaefa044da7 (diff)
downloadchromium_src-29e2fb456d3bf933168cf24bccaf05b1bea862fc.zip
chromium_src-29e2fb456d3bf933168cf24bccaf05b1bea862fc.tar.gz
chromium_src-29e2fb456d3bf933168cf24bccaf05b1bea862fc.tar.bz2
Move NPAPI implementation out of webkit/plugins/npapi and into content.
Notes: -gtk_plugin_container_manager* and gtk_plugin_container* move to content/browser/renderer_host/ since that's all where they're used -plugin_list* and plugin_constants_win* move to content/common as they're used by child processes and the browser -webplugin_impl* and webplugin_page_delegate.h move to content/renderer as that's where they're used. I will remove webplugin_page_delegate.h in a followup change as it's no longer needed. -the rest moves to content/child/npapi -a few constants moved from plugin_constants_win.h to plugin_util.h temporarily BUG=237249 TBR=scottmg Review URL: https://codereview.chromium.org/19761007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@212485 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/child/npapi/plugin_stream.cc')
-rw-r--r--content/child/npapi/plugin_stream.cc287
1 files changed, 287 insertions, 0 deletions
diff --git a/content/child/npapi/plugin_stream.cc b/content/child/npapi/plugin_stream.cc
new file mode 100644
index 0000000..6656678
--- /dev/null
+++ b/content/child/npapi/plugin_stream.cc
@@ -0,0 +1,287 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO : Support NP_ASFILEONLY mode
+// TODO : Support NP_SEEK mode
+// TODO : Support SEEKABLE=true in NewStream
+
+#include "content/child/npapi/plugin_stream.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/child/npapi/plugin_instance.h"
+#include "net/base/mime_util.h"
+#include "url/gurl.h"
+
+namespace content {
+
+PluginStream::PluginStream(
+ PluginInstance* instance,
+ const char* url,
+ bool need_notify,
+ void* notify_data)
+ : instance_(instance),
+ notify_needed_(need_notify),
+ notify_data_(notify_data),
+ close_on_write_data_(false),
+ requested_plugin_mode_(NP_NORMAL),
+ opened_(false),
+ data_offset_(0),
+ seekable_stream_(false) {
+ memset(&stream_, 0, sizeof(stream_));
+ stream_.url = base::strdup(url);
+ ResetTempFileHandle();
+ ResetTempFileName();
+}
+
+PluginStream::~PluginStream() {
+ // always close our temporary files.
+ CloseTempFile();
+ free(const_cast<char*>(stream_.url));
+}
+
+void PluginStream::UpdateUrl(const char* url) {
+ DCHECK(!opened_);
+ free(const_cast<char*>(stream_.url));
+ stream_.url = base::strdup(url);
+ pending_redirect_url_.clear();
+}
+
+bool PluginStream::Open(const std::string& mime_type,
+ const std::string& headers,
+ uint32 length,
+ uint32 last_modified,
+ bool request_is_seekable) {
+ headers_ = headers;
+ NPP id = instance_->npp();
+ stream_.end = length;
+ stream_.lastmodified = last_modified;
+ stream_.pdata = 0;
+ stream_.ndata = id->ndata;
+ stream_.notifyData = notify_data_;
+ if (!headers_.empty())
+ stream_.headers = headers_.c_str();
+
+ bool seekable_stream = false;
+ if (request_is_seekable) {
+ std::string headers_lc = StringToLowerASCII(headers);
+ if (headers_lc.find("accept-ranges: bytes") != std::string::npos) {
+ seekable_stream = true;
+ }
+ }
+
+ const char* char_mime_type = "application/x-unknown-content-type";
+ std::string temp_mime_type;
+ if (!mime_type.empty()) {
+ char_mime_type = mime_type.c_str();
+ } else {
+ GURL gurl(stream_.url);
+
+ base::FilePath path = base::FilePath::FromUTF8Unsafe(gurl.path());
+ if (net::GetMimeTypeFromFile(path, &temp_mime_type))
+ char_mime_type = temp_mime_type.c_str();
+ }
+
+ // Silverlight expects a valid mime type
+ DCHECK_NE(0U, strlen(char_mime_type));
+ NPError err = instance_->NPP_NewStream((NPMIMEType)char_mime_type,
+ &stream_, seekable_stream,
+ &requested_plugin_mode_);
+ if (err != NPERR_NO_ERROR) {
+ Notify(err);
+ return false;
+ }
+
+ opened_ = true;
+
+ if (requested_plugin_mode_ == NP_SEEK) {
+ seekable_stream_ = true;
+ }
+ // If the plugin has requested certain modes, then we need a copy
+ // of this file on disk. Open it and save it as we go.
+ if (RequestedPluginModeIsAsFile()) {
+ if (OpenTempFile() == false) {
+ return false;
+ }
+ }
+
+ mime_type_ = char_mime_type;
+ return true;
+}
+
+int PluginStream::Write(const char* buffer, const int length,
+ int data_offset) {
+ // There may be two streams to write to - the plugin and the file.
+ // It is unclear what to do if we cannot write to both. The rules of
+ // this function are that the plugin must consume at least as many
+ // bytes as returned by the WriteReady call. So, we will attempt to
+ // write that many to both streams. If we can't write that many bytes
+ // to each stream, we'll return failure.
+
+ DCHECK(opened_);
+ if (WriteToFile(buffer, length) &&
+ WriteToPlugin(buffer, length, data_offset)) {
+ return length;
+ }
+
+ return -1;
+}
+
+bool PluginStream::WriteToFile(const char* buf, size_t length) {
+ // For ASFILEONLY, ASFILE, and SEEK modes, we need to write
+ // to the disk
+ if (TempFileIsValid() && RequestedPluginModeIsAsFile()) {
+ size_t totalBytesWritten = 0, bytes;
+ do {
+ bytes = WriteBytes(buf, length);
+ totalBytesWritten += bytes;
+ } while (bytes > 0U && totalBytesWritten < length);
+
+ if (totalBytesWritten != length) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool PluginStream::WriteToPlugin(const char* buf, const int length,
+ const int data_offset) {
+ // For NORMAL and ASFILE modes, we send the data to the plugin now
+ if (requested_plugin_mode_ != NP_NORMAL &&
+ requested_plugin_mode_ != NP_ASFILE &&
+ requested_plugin_mode_ != NP_SEEK) {
+ return true;
+ }
+
+ int written = TryWriteToPlugin(buf, length, data_offset);
+ if (written == -1)
+ return false;
+
+ if (written < length) {
+ // Buffer the remaining data.
+ size_t remaining = length - written;
+ size_t previous_size = delivery_data_.size();
+ delivery_data_.resize(previous_size + remaining);
+ data_offset_ = data_offset;
+ memcpy(&delivery_data_[previous_size], buf + written, remaining);
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&PluginStream::OnDelayDelivery, this));
+ }
+
+ return true;
+}
+
+void PluginStream::OnDelayDelivery() {
+ // It is possible that the plugin stream may have closed before the task
+ // was hit.
+ if (!opened_)
+ return;
+
+ int size = static_cast<int>(delivery_data_.size());
+ int written = TryWriteToPlugin(&delivery_data_.front(), size, data_offset_);
+ if (written > 0) {
+ // Remove the data that we already wrote.
+ delivery_data_.erase(delivery_data_.begin(),
+ delivery_data_.begin() + written);
+ }
+}
+
+int PluginStream::TryWriteToPlugin(const char* buf, const int length,
+ const int data_offset) {
+ int byte_offset = 0;
+
+ if (data_offset > 0)
+ data_offset_ = data_offset;
+
+ while (byte_offset < length) {
+ int bytes_remaining = length - byte_offset;
+ int bytes_to_write = instance_->NPP_WriteReady(&stream_);
+ if (bytes_to_write > bytes_remaining)
+ bytes_to_write = bytes_remaining;
+
+ if (bytes_to_write == 0)
+ return byte_offset;
+
+ int bytes_consumed = instance_->NPP_Write(
+ &stream_, data_offset_, bytes_to_write,
+ const_cast<char*>(buf + byte_offset));
+ if (bytes_consumed < 0) {
+ // The plugin failed, which means that we need to close the stream.
+ Close(NPRES_NETWORK_ERR);
+ return -1;
+ }
+ if (bytes_consumed == 0) {
+ // The plugin couldn't take all of the data now.
+ return byte_offset;
+ }
+
+ // The plugin might report more that we gave it.
+ bytes_consumed = std::min(bytes_consumed, bytes_to_write);
+
+ data_offset_ += bytes_consumed;
+ byte_offset += bytes_consumed;
+ }
+
+ if (close_on_write_data_)
+ Close(NPRES_DONE);
+
+ return length;
+}
+
+bool PluginStream::Close(NPReason reason) {
+ if (opened_ == true) {
+ opened_ = false;
+
+ if (delivery_data_.size()) {
+ if (reason == NPRES_DONE) {
+ // There is more data to be streamed, don't destroy the stream now.
+ close_on_write_data_ = true;
+ return true;
+ } else {
+ // Stop any pending data from being streamed
+ delivery_data_.resize(0);
+ }
+ }
+
+ // If we have a temp file, be sure to close it.
+ // Also, allow the plugin to access it now.
+ if (TempFileIsValid()) {
+ CloseTempFile();
+ if (reason == NPRES_DONE)
+ WriteAsFile();
+ }
+
+ if (stream_.ndata != NULL) {
+ // Stream hasn't been closed yet.
+ NPError err = instance_->NPP_DestroyStream(&stream_, reason);
+ DCHECK(err == NPERR_NO_ERROR);
+ }
+ }
+
+ Notify(reason);
+ return true;
+}
+
+WebPluginResourceClient* PluginStream::AsResourceClient() {
+ return NULL;
+}
+
+void PluginStream::Notify(NPReason reason) {
+ if (notify_needed_) {
+ instance_->NPP_URLNotify(stream_.url, reason, notify_data_);
+ notify_needed_ = false;
+ }
+}
+
+bool PluginStream::RequestedPluginModeIsAsFile() const {
+ return (requested_plugin_mode_ == NP_ASFILE ||
+ requested_plugin_mode_ == NP_ASFILEONLY);
+}
+
+} // namespace content