OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 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 // TODO : Support NP_ASFILEONLY mode | |
6 // TODO : Support NP_SEEK mode | |
7 // TODO : Support SEEKABLE=true in NewStream | |
8 | |
9 #include "webkit/glue/plugins/plugin_stream.h" | |
10 | |
11 #include "base/message_loop.h" | |
12 #include "base/string_util.h" | |
13 #include "base/utf_string_conversions.h" | |
14 #include "net/base/mime_util.h" | |
15 #include "webkit/glue/plugins/plugin_instance.h" | |
16 #include "googleurl/src/gurl.h" | |
17 | |
18 namespace NPAPI { | |
19 | |
20 PluginStream::~PluginStream() { | |
21 // always close our temporary files. | |
22 CloseTempFile(); | |
23 free(const_cast<char*>(stream_.url)); | |
24 } | |
25 | |
26 bool PluginStream::Open(const std::string &mime_type, | |
27 const std::string &headers, | |
28 uint32 length, | |
29 uint32 last_modified, | |
30 bool request_is_seekable) { | |
31 headers_ = headers; | |
32 NPP id = instance_->npp(); | |
33 stream_.end = length; | |
34 stream_.lastmodified = last_modified; | |
35 stream_.pdata = 0; | |
36 stream_.ndata = id->ndata; | |
37 stream_.notifyData = notify_data_; | |
38 if (!headers_.empty()) | |
39 stream_.headers = headers_.c_str(); | |
40 | |
41 bool seekable_stream = false; | |
42 if (request_is_seekable) { | |
43 std::string headers_lc = StringToLowerASCII(headers); | |
44 if (headers_lc.find("accept-ranges: bytes") != std::string::npos) { | |
45 seekable_stream = true; | |
46 } | |
47 } | |
48 | |
49 const char *char_mime_type = "application/x-unknown-content-type"; | |
50 std::string temp_mime_type; | |
51 if (!mime_type.empty()) { | |
52 char_mime_type = mime_type.c_str(); | |
53 } else { | |
54 GURL gurl(stream_.url); | |
55 | |
56 #if defined(OS_WIN) | |
57 FilePath path(UTF8ToWide(gurl.path())); | |
58 #elif defined(OS_POSIX) | |
59 FilePath path(gurl.path()); | |
60 #endif | |
61 if (net::GetMimeTypeFromFile(path, &temp_mime_type)) | |
62 char_mime_type = temp_mime_type.c_str(); | |
63 } | |
64 | |
65 // Silverlight expects a valid mime type | |
66 DCHECK(strlen(char_mime_type) != 0); | |
67 NPError err = instance_->NPP_NewStream((NPMIMEType)char_mime_type, | |
68 &stream_, seekable_stream, | |
69 &requested_plugin_mode_); | |
70 if (err != NPERR_NO_ERROR) { | |
71 Notify(err); | |
72 return false; | |
73 } | |
74 | |
75 opened_ = true; | |
76 | |
77 if (requested_plugin_mode_ == NP_SEEK) { | |
78 seekable_stream_ = true; | |
79 } | |
80 // If the plugin has requested certain modes, then we need a copy | |
81 // of this file on disk. Open it and save it as we go. | |
82 if (requested_plugin_mode_ == NP_ASFILEONLY || | |
83 requested_plugin_mode_ == NP_ASFILE) { | |
84 if (OpenTempFile() == false) | |
85 return false; | |
86 } | |
87 | |
88 mime_type_ = char_mime_type; | |
89 return true; | |
90 } | |
91 | |
92 int PluginStream::Write(const char *buffer, const int length, | |
93 int data_offset) { | |
94 // There may be two streams to write to - the plugin and the file. | |
95 // It is unclear what to do if we cannot write to both. The rules of | |
96 // this function are that the plugin must consume at least as many | |
97 // bytes as returned by the WriteReady call. So, we will attempt to | |
98 // write that many to both streams. If we can't write that many bytes | |
99 // to each stream, we'll return failure. | |
100 | |
101 DCHECK(opened_); | |
102 if (WriteToFile(buffer, length) && | |
103 WriteToPlugin(buffer, length, data_offset)) | |
104 return length; | |
105 | |
106 return -1; | |
107 } | |
108 | |
109 bool PluginStream::WriteToFile(const char *buf, size_t length) { | |
110 // For ASFILEONLY, ASFILE, and SEEK modes, we need to write | |
111 // to the disk | |
112 if (TempFileIsValid() && | |
113 (requested_plugin_mode_ == NP_ASFILE || | |
114 requested_plugin_mode_ == NP_ASFILEONLY) ) { | |
115 size_t totalBytesWritten = 0, bytes; | |
116 do { | |
117 bytes = WriteBytes(buf, length); | |
118 totalBytesWritten += bytes; | |
119 } while (bytes > 0U && totalBytesWritten < length); | |
120 | |
121 if (totalBytesWritten != length) | |
122 return false; | |
123 } | |
124 | |
125 return true; | |
126 } | |
127 | |
128 bool PluginStream::WriteToPlugin(const char *buf, const int length, | |
129 const int data_offset) { | |
130 // For NORMAL and ASFILE modes, we send the data to the plugin now | |
131 if (requested_plugin_mode_ != NP_NORMAL && | |
132 requested_plugin_mode_ != NP_ASFILE && | |
133 requested_plugin_mode_ != NP_SEEK) | |
134 return true; | |
135 | |
136 int written = TryWriteToPlugin(buf, length, data_offset); | |
137 if (written == -1) | |
138 return false; | |
139 | |
140 if (written < length) { | |
141 // Buffer the remaining data. | |
142 size_t remaining = length - written; | |
143 size_t previous_size = delivery_data_.size(); | |
144 delivery_data_.resize(previous_size + remaining); | |
145 data_offset_ = data_offset; | |
146 memcpy(&delivery_data_[previous_size], buf + written, remaining); | |
147 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( | |
148 this, &PluginStream::OnDelayDelivery)); | |
149 } | |
150 | |
151 return true; | |
152 } | |
153 | |
154 void PluginStream::OnDelayDelivery() { | |
155 // It is possible that the plugin stream may have closed before the task | |
156 // was hit. | |
157 if (!opened_) { | |
158 return; | |
159 } | |
160 | |
161 int size = static_cast<int>(delivery_data_.size()); | |
162 int written = TryWriteToPlugin(&delivery_data_.front(), size, | |
163 data_offset_); | |
164 if (written > 0) { | |
165 // Remove the data that we already wrote. | |
166 delivery_data_.erase(delivery_data_.begin(), | |
167 delivery_data_.begin() + written); | |
168 } | |
169 } | |
170 | |
171 int PluginStream::TryWriteToPlugin(const char *buf, const int length, | |
172 const int data_offset) { | |
173 int byte_offset = 0; | |
174 | |
175 if (data_offset > 0) | |
176 data_offset_ = data_offset; | |
177 | |
178 while (byte_offset < length) { | |
179 int bytes_remaining = length - byte_offset; | |
180 int bytes_to_write = instance_->NPP_WriteReady(&stream_); | |
181 if (bytes_to_write > bytes_remaining) | |
182 bytes_to_write = bytes_remaining; | |
183 | |
184 if (bytes_to_write == 0) | |
185 return byte_offset; | |
186 | |
187 int bytes_consumed = instance_->NPP_Write( | |
188 &stream_, data_offset_, bytes_to_write, | |
189 const_cast<char*>(buf + byte_offset)); | |
190 if (bytes_consumed < 0) { | |
191 // The plugin failed, which means that we need to close the stream. | |
192 Close(NPRES_NETWORK_ERR); | |
193 return -1; | |
194 } | |
195 if (bytes_consumed == 0) { | |
196 // The plugin couldn't take all of the data now. | |
197 return byte_offset; | |
198 } | |
199 | |
200 // The plugin might report more that we gave it. | |
201 bytes_consumed = std::min(bytes_consumed, bytes_to_write); | |
202 | |
203 data_offset_ += bytes_consumed; | |
204 byte_offset += bytes_consumed; | |
205 } | |
206 | |
207 if (close_on_write_data_) | |
208 Close(NPRES_DONE); | |
209 | |
210 return length; | |
211 } | |
212 | |
213 bool PluginStream::Close(NPReason reason) { | |
214 if (opened_ == true) { | |
215 opened_ = false; | |
216 | |
217 if (delivery_data_.size()) { | |
218 if (reason == NPRES_DONE) { | |
219 // There is more data to be streamed, don't destroy the stream now. | |
220 close_on_write_data_ = true; | |
221 return true; | |
222 } else { | |
223 // Stop any pending data from being streamed | |
224 delivery_data_.resize(0); | |
225 } | |
226 } | |
227 | |
228 // If we have a temp file, be sure to close it. | |
229 // Also, allow the plugin to access it now. | |
230 if (TempFileIsValid()) { | |
231 CloseTempFile(); | |
232 if (reason == NPRES_DONE) | |
233 WriteAsFile(); | |
234 } | |
235 | |
236 if (stream_.ndata != NULL) { | |
237 // Stream hasn't been closed yet. | |
238 NPError err = instance_->NPP_DestroyStream(&stream_, reason); | |
239 DCHECK(err == NPERR_NO_ERROR); | |
240 } | |
241 } | |
242 | |
243 Notify(reason); | |
244 return true; | |
245 } | |
246 | |
247 webkit_glue::WebPluginResourceClient* PluginStream::AsResourceClient() { | |
248 return NULL; | |
249 } | |
250 | |
251 void PluginStream::Notify(NPReason reason) { | |
252 if (notify_needed_) { | |
253 instance_->NPP_URLNotify(stream_.url, reason, notify_data_); | |
254 notify_needed_ = false; | |
255 } | |
256 } | |
257 | |
258 } // namespace NPAPI | |
OLD | NEW |