cfe56872fb4a2ebbe487366ccaa8d0008be0cda6
[WebKit-https.git] / WebCore / plugins / win / PluginStreamWin.cpp
1 /*
2  * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "PluginStreamWin.h"
28
29 #include "CString.h"
30 #include "PluginDebug.h"
31 #include "PluginPackageWin.h"
32 #include "PluginViewWin.h"
33 #include "SharedBuffer.h"
34 #include "SubresourceLoader.h"
35
36 #if USE(CFNETWORK)
37 #include <CFNetwork/CFNetwork.h>
38 #include <CFNetwork/CFURLResponsePriv.h>
39 #elif USE(WININET)
40 #include "ResourceHandleWin.h"
41 #else 
42 #error No loader framework defined
43 #endif
44
45 // We use -2 here because some plugins like to return -1 to indicate error
46 // and this way we won't clash with them.
47 static const int WebReasonNone = -2;
48
49 using std::max;
50 using std::min;
51
52 namespace WebCore {
53
54 typedef HashMap<NPStream*, NPP> StreamMap;
55 static StreamMap& streams()
56 {
57     static StreamMap staticStreams;
58     return staticStreams;
59 }
60
61 PluginStreamWin::PluginStreamWin(PluginViewWin* pluginView, Frame* frame, const ResourceRequest& resourceRequest, bool sendNotification, void* notifyData)
62     : m_resourceRequest(resourceRequest)
63     , m_frame(frame)
64     , m_pluginView(pluginView)
65     , m_notifyData(notifyData)
66     , m_sendNotification(sendNotification)
67     , m_streamState(StreamBeforeStarted)
68     , m_delayDeliveryTimer(this, &PluginStreamWin::delayDeliveryTimerFired)
69     , m_deliveryData(0)
70     , m_tempFileHandle(INVALID_HANDLE_VALUE)
71     , m_pluginFuncs(pluginView->plugin()->pluginFuncs())
72     , m_instance(pluginView->instance())
73 {
74     ASSERT(m_pluginView);
75
76     m_stream.url = 0;
77     m_stream.ndata = 0;
78     m_stream.pdata = 0;
79     m_stream.end = 0;
80     m_stream.notifyData = 0;
81     m_stream.lastmodified = 0;
82
83     streams().add(&m_stream, m_instance);
84 }
85
86 PluginStreamWin::~PluginStreamWin()
87 {
88     ASSERT(m_streamState != StreamStarted);
89     ASSERT(!m_loader);
90
91     delete m_deliveryData;
92
93     free((char*)m_stream.url);
94
95     streams().remove(&m_stream);
96 }
97
98 void PluginStreamWin::start()
99 {
100     m_loader = SubresourceLoader::create(m_frame, this, m_resourceRequest);
101     if (m_loader)
102         m_loader->setShouldBufferData(false);
103 }
104
105 void PluginStreamWin::stop()
106 {
107     m_streamState = StreamStopped;
108
109     if (m_loader) {
110         m_loader->cancel();
111         m_loader = 0;
112     }
113 }
114
115 void PluginStreamWin::startStream()
116 {
117     ASSERT(m_streamState == StreamBeforeStarted);
118
119     const KURL& responseURL = m_resourceResponse.url();
120
121     // Some plugins (Flash) expect that javascript URLs are passed back decoded as this is the
122     // format used when requesting the URL.
123     if (responseURL.url().startsWith("javascript:", false))
124         m_stream.url = _strdup(responseURL.decode_string(responseURL.url()).utf8());
125     else
126         m_stream.url = _strdup(responseURL.url().utf8());
127     
128     CString mimeTypeStr = m_resourceResponse.mimeType().utf8();
129     
130     m_stream.pdata = 0;
131     m_stream.ndata = this;
132     m_stream.end = max(m_resourceResponse.expectedContentLength(), static_cast<long long>(0));
133     m_stream.lastmodified = m_resourceResponse.lastModifiedDate();
134     m_stream.notifyData = m_notifyData;
135
136     m_transferMode = NP_NORMAL;
137     m_offset = 0;
138     m_reason = WebReasonNone;
139
140     // Protect the stream if destroystream is called from within the newstream handler
141     RefPtr<PluginStreamWin> protect(this);
142
143     NPError npErr = m_pluginFuncs->newstream(m_instance, (NPMIMEType)mimeTypeStr.data(), &m_stream, false, &m_transferMode);
144     
145     // If the stream was destroyed in the call to newstream we return
146     if (m_reason != WebReasonNone)
147         return;
148         
149     m_streamState = StreamStarted;
150
151     if (npErr != NPERR_NO_ERROR)
152         cancelAndDestroyStream(npErr);
153
154     if (m_transferMode == NP_NORMAL)
155         return;
156     
157     char tempPath[MAX_PATH];
158
159     int result = ::GetTempPathA(_countof(tempPath), tempPath);
160     if (result > 0 && result <= _countof(tempPath)) {
161         char tempFile[MAX_PATH];
162
163         if (::GetTempFileNameA(tempPath, "WKP", 0, tempFile) > 0) {
164             m_tempFileHandle = ::CreateFileA(tempFile, GENERIC_READ | GENERIC_WRITE, 0, 0, 
165                 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
166
167             if (m_tempFileHandle != INVALID_HANDLE_VALUE) {
168                 m_path = tempFile;
169                 return;
170             }
171         }
172     }
173
174     // Something went wrong, cancel loading the stream
175     cancelAndDestroyStream(NPRES_NETWORK_ERR);
176 }
177
178 NPP PluginStreamWin::ownerForStream(NPStream* stream)
179 {
180     return streams().get(stream);
181 }
182
183 void PluginStreamWin::cancelAndDestroyStream(NPReason reason)
184 {
185     RefPtr<PluginStreamWin> protect(this);
186
187     destroyStream(reason);
188     stop();
189 }
190
191 void PluginStreamWin::destroyStream(NPReason reason)
192 {
193     m_reason = reason;
194     if (m_reason != NPRES_DONE) {
195         // Stop any pending data from being streamed
196         if (m_deliveryData)
197             m_deliveryData->resize(0);
198     } else if (m_deliveryData && m_deliveryData->size() > 0) {
199         // There is more data to be streamed, don't destroy the stream now.
200         return;
201     }
202     destroyStream();
203 }
204
205 void PluginStreamWin::destroyStream()
206 {
207     if (m_streamState == StreamStopped)
208         return;
209
210     ASSERT (m_reason != WebReasonNone);
211     ASSERT (!m_deliveryData || m_deliveryData->size() == 0);
212
213     if (m_tempFileHandle != INVALID_HANDLE_VALUE)
214         ::CloseHandle(m_tempFileHandle);
215
216     if (m_stream.ndata != 0) {
217         if (m_reason == NPRES_DONE && (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY)) {
218             ASSERT(!m_path.isNull());
219
220             m_pluginFuncs->asfile(m_instance, &m_stream, m_path.data());
221         }
222
223         NPError npErr;
224         npErr = m_pluginFuncs->destroystream(m_instance, &m_stream, m_reason);
225         LOG_NPERROR(npErr);
226
227         m_stream.ndata = 0;
228     }
229
230     if (m_sendNotification)
231         m_pluginFuncs->urlnotify(m_instance, m_resourceRequest.url().url().utf8(), m_reason, m_notifyData);
232
233     m_streamState = StreamStopped;
234
235     // disconnectStream can cause us to be deleted.
236     RefPtr<PluginStreamWin> protect(this);
237     m_pluginView->disconnectStream(this);
238
239     m_pluginView = 0;
240
241     if (!m_path.isNull())
242         ::DeleteFileA(m_path.data());
243 }
244
245 void PluginStreamWin::delayDeliveryTimerFired(Timer<PluginStreamWin>* timer)
246 {
247     ASSERT(timer == &m_delayDeliveryTimer);
248
249     deliverData();
250 }
251
252 void PluginStreamWin::deliverData()
253 {
254     ASSERT(m_deliveryData);
255     
256     if (m_streamState == StreamStopped)
257         // FIXME: We should cancel our job in the SubresourceLoader on error so we don't reach this case
258         return;
259
260     ASSERT(m_streamState != StreamBeforeStarted);
261
262     if (!m_stream.ndata || m_deliveryData->size() == 0)
263         return;
264
265     int32 totalBytes = m_deliveryData->size();
266     int32 totalBytesDelivered = 0;
267
268     while (totalBytesDelivered < totalBytes) {
269         int32 deliveryBytes = m_pluginFuncs->writeready(m_instance, &m_stream);
270
271         if (deliveryBytes <= 0) {
272             m_delayDeliveryTimer.startOneShot(0);
273             break;
274         } else {
275             deliveryBytes = min(deliveryBytes, totalBytes - totalBytesDelivered);
276             int32 dataLength = deliveryBytes;
277             char* data = m_deliveryData->data() + totalBytesDelivered;
278
279             // Write the data
280             deliveryBytes = m_pluginFuncs->write(m_instance, &m_stream, m_offset, dataLength, (void*)data);
281             if (deliveryBytes < 0) {
282                 LOG_PLUGIN_NET_ERROR();
283                 cancelAndDestroyStream(NPRES_NETWORK_ERR);
284                 return;
285             }
286             deliveryBytes = min(deliveryBytes, dataLength);
287             m_offset += deliveryBytes;
288             totalBytesDelivered += deliveryBytes;
289         }
290     }
291
292     if (totalBytesDelivered > 0) {
293         if (totalBytesDelivered < totalBytes) {
294             int remainingBytes = totalBytes - totalBytesDelivered;
295             memmove(m_deliveryData, m_deliveryData + totalBytesDelivered, remainingBytes);
296             m_deliveryData->resize(remainingBytes);
297         } else {
298             m_deliveryData->resize(0);
299             if (m_reason != WebReasonNone)
300                 destroyStream();
301         }
302     } 
303 }
304
305 void PluginStreamWin::sendJavaScriptStream(const KURL& requestURL, const CString& resultString)
306 {
307     didReceiveResponse(0, ResourceResponse(requestURL, "text/plain", resultString.length(), "", ""));
308
309     if (m_streamState == StreamStopped)
310         return;
311
312     didReceiveData(0, resultString.data(), resultString.length());
313     if (m_streamState == StreamStopped)
314         return;
315
316     didFinishLoading(0);
317 }
318
319 void PluginStreamWin::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)
320 {
321     ASSERT(loader == m_loader);
322     ASSERT(m_streamState == StreamBeforeStarted);
323
324     m_resourceResponse = response;
325
326     startStream();
327 }
328
329 void PluginStreamWin::didReceiveData(SubresourceLoader* loader, const char* data, int length)
330 {
331     ASSERT(loader == m_loader);
332     ASSERT(length > 0);
333     ASSERT(m_streamState == StreamStarted);
334     
335     if (!m_deliveryData)
336         m_deliveryData = new Vector<char>;
337
338     int oldSize = m_deliveryData->size();
339     m_deliveryData->resize(oldSize + length);
340     memcpy(m_deliveryData->data() + oldSize, data, length);
341
342     if (m_transferMode != NP_ASFILEONLY)
343         deliverData();
344
345     if (m_streamState != StreamStopped && m_tempFileHandle != INVALID_HANDLE_VALUE) {
346         DWORD written;
347         bool retval = true;
348
349         retval = WriteFile(m_tempFileHandle, data, length, &written, 0);
350
351         if (!retval || (int)written != length)
352             cancelAndDestroyStream(NPRES_NETWORK_ERR);
353     }
354
355 }
356
357 void PluginStreamWin::didFail(SubresourceLoader* loader, const ResourceError&)
358 {
359     ASSERT(loader == m_loader);
360
361     m_loader = 0;
362
363     LOG_PLUGIN_NET_ERROR();
364     destroyStream(NPRES_NETWORK_ERR);
365 }
366
367 void PluginStreamWin::didFinishLoading(SubresourceLoader* loader)
368 {
369     ASSERT(loader == m_loader);
370     ASSERT(m_streamState == StreamStarted);
371
372     m_loader = 0;
373
374     destroyStream(NPRES_DONE);
375 }
376
377 }