diff options
Diffstat (limited to 'webkit/plugins/npapi/plugin_stream.cc')
-rw-r--r-- | webkit/plugins/npapi/plugin_stream.cc | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/webkit/plugins/npapi/plugin_stream.cc b/webkit/plugins/npapi/plugin_stream.cc new file mode 100644 index 0000000..fee63eb --- /dev/null +++ b/webkit/plugins/npapi/plugin_stream.cc @@ -0,0 +1,260 @@ +// Copyright (c) 2010 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 "webkit/plugins/npapi/plugin_stream.h" + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "net/base/mime_util.h" +#include "webkit/plugins/npapi/plugin_instance.h" +#include "googleurl/src/gurl.h" + +namespace webkit { +namespace npapi { + +PluginStream::~PluginStream() { + // always close our temporary files. + CloseTempFile(); + free(const_cast<char*>(stream_.url)); +} + +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); + +#if defined(OS_WIN) + FilePath path(UTF8ToWide(gurl.path())); +#elif defined(OS_POSIX) + FilePath path(gurl.path()); +#endif + if (net::GetMimeTypeFromFile(path, &temp_mime_type)) + char_mime_type = temp_mime_type.c_str(); + } + + // Silverlight expects a valid mime type + DCHECK(strlen(char_mime_type) != 0); + 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 (requested_plugin_mode_ == NP_ASFILEONLY || + requested_plugin_mode_ == NP_ASFILE) { + 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() && + (requested_plugin_mode_ == NP_ASFILE || + requested_plugin_mode_ == NP_ASFILEONLY) ) { + 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); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &PluginStream::OnDelayDelivery)); + } + + 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; + } +} + +} // namespace npapi +} // namespace webkit |