WebKit2/WebProcess/Plugins/Netscape/NetscapePluginStream.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2010 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 INC. AND ITS CONTRIBUTORS ``AS IS''
       
    14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
       
    15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
       
    17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
       
    18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
       
    19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
       
    20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
       
    21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
       
    22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
       
    23  * THE POSSIBILITY OF SUCH DAMAGE.
       
    24  */
       
    25 
       
    26 #include "NetscapePluginStream.h"
       
    27 
       
    28 #include "NetscapePlugin.h"
       
    29 #include <utility>
       
    30 
       
    31 using namespace WebCore;
       
    32 using namespace std;
       
    33 
       
    34 namespace WebKit {
       
    35 
       
    36 NetscapePluginStream::NetscapePluginStream(PassRefPtr<NetscapePlugin> plugin, uint64_t streamID, bool sendNotification, void* notificationData)
       
    37     : m_plugin(plugin)
       
    38     , m_streamID(streamID)
       
    39     , m_sendNotification(sendNotification)
       
    40     , m_notificationData(notificationData)
       
    41     , m_npStream()
       
    42     , m_transferMode(NP_NORMAL)
       
    43     , m_offset(0)
       
    44     , m_fileHandle(invalidPlatformFileHandle)
       
    45     , m_isStarted(false)
       
    46 #if !ASSERT_DISABLED
       
    47     , m_urlNotifyHasBeenCalled(false)
       
    48 #endif    
       
    49     , m_deliveryDataTimer(RunLoop::main(), this, &NetscapePluginStream::deliverDataToPlugin)
       
    50     , m_stopStreamWhenDoneDelivering(false)
       
    51 {
       
    52 }
       
    53 
       
    54 NetscapePluginStream::~NetscapePluginStream()
       
    55 {
       
    56     ASSERT(!m_isStarted);
       
    57     ASSERT(!m_sendNotification || m_urlNotifyHasBeenCalled);
       
    58     ASSERT(m_fileHandle == invalidPlatformFileHandle);
       
    59 }
       
    60 
       
    61 void NetscapePluginStream::didReceiveResponse(const KURL& responseURL, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers)
       
    62 {
       
    63     // Starting the stream could cause the plug-in stream to go away so we keep a reference to it here.
       
    64     RefPtr<NetscapePluginStream> protect(this);
       
    65 
       
    66     start(responseURL, streamLength, lastModifiedTime, mimeType, headers);
       
    67 }
       
    68 
       
    69 void NetscapePluginStream::didReceiveData(const char* bytes, int length)
       
    70 {
       
    71     // Delivering the data could cause the plug-in stream to go away so we keep a reference to it here.
       
    72     RefPtr<NetscapePluginStream> protect(this);
       
    73 
       
    74     deliverData(bytes, length);
       
    75 }
       
    76 
       
    77 void NetscapePluginStream::didFinishLoading()
       
    78 {
       
    79     // Stopping the stream could cause the plug-in stream to go away so we keep a reference to it here.
       
    80     RefPtr<NetscapePluginStream> protect(this);
       
    81 
       
    82     stop(NPRES_DONE);
       
    83 }
       
    84 
       
    85 void NetscapePluginStream::didFail(bool wasCancelled)
       
    86 {
       
    87     // Stopping the stream could cause the plug-in stream to go away so we keep a reference to it here.
       
    88     RefPtr<NetscapePluginStream> protect(this);
       
    89 
       
    90     stop(wasCancelled ? NPRES_USER_BREAK : NPRES_NETWORK_ERR);
       
    91 }
       
    92     
       
    93 void NetscapePluginStream::sendJavaScriptStream(const String& requestURLString, const String& result)
       
    94 {
       
    95     // starting the stream or delivering the data to it might cause the plug-in stream to go away, so we keep
       
    96     // a reference to it here.
       
    97     RefPtr<NetscapePluginStream> protect(this);
       
    98 
       
    99     CString resultCString = requestURLString.utf8();
       
   100     if (resultCString.isNull()) {
       
   101         // There was an error evaluating the JavaScript, call NPP_URLNotify if needed and then destroy the stream.
       
   102         notifyAndDestroyStream(NPRES_NETWORK_ERR);
       
   103         return;
       
   104     }
       
   105 
       
   106     if (!start(requestURLString, resultCString.length(), 0, "text/plain", ""))
       
   107         return;
       
   108 
       
   109     deliverData(resultCString.data(), resultCString.length());
       
   110     stop(NPRES_DONE);
       
   111 }
       
   112 
       
   113 NPError NetscapePluginStream::destroy(NPReason reason)
       
   114 {
       
   115     // It doesn't make sense to call NPN_DestroyStream on a stream that hasn't been started yet.
       
   116     if (!m_isStarted)
       
   117         return NPERR_GENERIC_ERROR;
       
   118 
       
   119     // It isn't really valid for a plug-in to call NPN_DestroyStream with NPRES_DONE.
       
   120     // (At least not for browser initiated streams, and we don't support plug-in initiated streams).
       
   121     if (reason == NPRES_DONE)
       
   122         return NPERR_INVALID_PARAM;
       
   123 
       
   124     cancel();
       
   125     stop(reason);
       
   126     return NPERR_NO_ERROR;
       
   127 }
       
   128 
       
   129 static bool isSupportedTransferMode(uint16_t transferMode)
       
   130 {
       
   131     switch (transferMode) {
       
   132     case NP_ASFILEONLY:
       
   133     case NP_ASFILE:
       
   134     case NP_NORMAL:
       
   135         return true;
       
   136     // FIXME: We don't support seekable streams.
       
   137     case NP_SEEK:
       
   138         return false;
       
   139     }
       
   140 
       
   141     ASSERT_NOT_REACHED();
       
   142     return false;
       
   143 }
       
   144     
       
   145 bool NetscapePluginStream::start(const WebCore::String& responseURLString, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers)
       
   146 {
       
   147     m_responseURL = responseURLString.utf8();
       
   148     m_mimeType = mimeType.utf8();
       
   149     m_headers = headers.utf8();
       
   150 
       
   151     m_npStream.ndata = this;
       
   152     m_npStream.url = m_responseURL.data();
       
   153     m_npStream.end = streamLength;
       
   154     m_npStream.lastmodified = lastModifiedTime;
       
   155     m_npStream.notifyData = m_notificationData;
       
   156     m_npStream.headers = m_headers.length() == 0 ? 0 : m_headers.data();
       
   157 
       
   158     NPError error = m_plugin->NPP_NewStream(const_cast<char*>(m_mimeType.data()), &m_npStream, false, &m_transferMode);
       
   159     if (error != NPERR_NO_ERROR) {
       
   160         // We failed to start the stream, cancel the load and destroy it.
       
   161         cancel();
       
   162         notifyAndDestroyStream(NPRES_NETWORK_ERR);
       
   163         return false;
       
   164     }
       
   165 
       
   166     // We successfully started the stream.
       
   167     m_isStarted = true;
       
   168 
       
   169     if (!isSupportedTransferMode(m_transferMode)) {
       
   170         // Cancel the load and stop the stream.
       
   171         cancel();
       
   172         stop(NPRES_NETWORK_ERR);
       
   173         return false;
       
   174     }
       
   175 
       
   176     return true;
       
   177 }
       
   178 
       
   179 void NetscapePluginStream::deliverData(const char* bytes, int length)
       
   180 {
       
   181     ASSERT(m_isStarted);
       
   182 
       
   183     if (m_transferMode != NP_ASFILEONLY) {
       
   184         if (!m_deliveryData)
       
   185             m_deliveryData.set(new Vector<char>);
       
   186 
       
   187         m_deliveryData->reserveCapacity(m_deliveryData->size() + length);
       
   188         m_deliveryData->append(bytes, length);
       
   189         
       
   190         deliverDataToPlugin();
       
   191     }
       
   192 
       
   193     if (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY)
       
   194         deliverDataToFile(bytes, length);
       
   195 }
       
   196 
       
   197 void NetscapePluginStream::deliverDataToPlugin()
       
   198 {
       
   199     ASSERT(m_isStarted);
       
   200 
       
   201     int32_t numBytesToDeliver = m_deliveryData->size();
       
   202     int32_t numBytesDelivered = 0;
       
   203 
       
   204     while (numBytesDelivered < numBytesToDeliver) {
       
   205         int32_t numBytesPluginCanHandle = m_plugin->NPP_WriteReady(&m_npStream);
       
   206         
       
   207         // NPP_WriteReady could call NPN_DestroyStream and destroy the stream.
       
   208         if (!m_isStarted)
       
   209             return;
       
   210 
       
   211         if (numBytesPluginCanHandle <= 0) {
       
   212             // The plug-in can't handle more data, we'll send the rest later
       
   213             m_deliveryDataTimer.startOneShot(0);
       
   214             break;
       
   215         }
       
   216 
       
   217         // Figure out how much data to send to the plug-in.
       
   218         int32_t dataLength = min(numBytesPluginCanHandle, numBytesToDeliver - numBytesDelivered);
       
   219         char* data = m_deliveryData->data() + numBytesDelivered;
       
   220 
       
   221         int32_t numBytesWritten = m_plugin->NPP_Write(&m_npStream, m_offset, dataLength, data);
       
   222         if (numBytesWritten < 0) {
       
   223             stop(NPRES_NETWORK_ERR);
       
   224             return;
       
   225         }
       
   226 
       
   227         // NPP_Write could call NPN_DestroyStream and destroy the stream.
       
   228         if (!m_isStarted)
       
   229             return;
       
   230 
       
   231         numBytesWritten = min(numBytesWritten, dataLength);
       
   232         m_offset += numBytesWritten;
       
   233         numBytesDelivered += numBytesWritten;
       
   234     }
       
   235 
       
   236     // We didn't write anything.
       
   237     if (!numBytesDelivered)
       
   238         return;
       
   239 
       
   240     if (numBytesDelivered < numBytesToDeliver) {
       
   241         // Remove the bytes that we actually delivered.
       
   242         m_deliveryData->remove(0, numBytesDelivered);
       
   243     } else {
       
   244         m_deliveryData->clear();
       
   245 
       
   246         if (m_stopStreamWhenDoneDelivering)
       
   247             stop(NPRES_DONE);
       
   248     }
       
   249 }
       
   250 
       
   251 void NetscapePluginStream::deliverDataToFile(const char* bytes, int length)
       
   252 {
       
   253     if (m_fileHandle == invalidPlatformFileHandle && m_filePath.isNull()) {
       
   254         // Create a temporary file.
       
   255         m_filePath = openTemporaryFile("WebKitPluginStream", m_fileHandle);
       
   256 
       
   257         // We failed to open the file, stop the stream.
       
   258         if (m_fileHandle == invalidPlatformFileHandle) {
       
   259             stop(NPRES_NETWORK_ERR);
       
   260             return;
       
   261         }
       
   262     }
       
   263 
       
   264     if (!length)
       
   265         return;
       
   266 
       
   267     int byteCount = writeToFile(m_fileHandle, bytes, length);
       
   268     if (byteCount != length) {
       
   269         // This happens only rarely, when we are out of disk space or have a disk I/O error.
       
   270         closeFile(m_fileHandle);
       
   271 
       
   272         stop(NPRES_NETWORK_ERR);
       
   273     }
       
   274 }
       
   275 
       
   276 void NetscapePluginStream::stop(NPReason reason)
       
   277 {
       
   278     ASSERT(m_isStarted);
       
   279 
       
   280     if (reason == NPRES_DONE && m_deliveryData && !m_deliveryData->isEmpty()) {
       
   281         // There is still data left that the plug-in hasn't been able to consume yet.
       
   282         ASSERT(m_deliveryDataTimer.isActive());
       
   283         
       
   284         // Set m_stopStreamWhenDoneDelivering to true so that the next time the delivery timer fires
       
   285         // and calls deliverDataToPlugin the stream will be closed if all the remaining data was
       
   286         // successfully delivered.
       
   287         m_stopStreamWhenDoneDelivering = true;
       
   288         return;
       
   289     }
       
   290 
       
   291     m_deliveryData = 0;
       
   292     m_deliveryDataTimer.stop();
       
   293 
       
   294     if (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY) {
       
   295         if (reason == NPRES_DONE) {
       
   296             // Ensure that the file is created.
       
   297             deliverDataToFile(0, 0);
       
   298             if (m_fileHandle != invalidPlatformFileHandle)
       
   299                 closeFile(m_fileHandle);
       
   300             
       
   301             ASSERT(!m_filePath.isNull());
       
   302             
       
   303             m_plugin->NPP_StreamAsFile(&m_npStream, m_filePath.data());
       
   304         } else {
       
   305             // Just close the file.
       
   306             if (m_fileHandle != invalidPlatformFileHandle)
       
   307                 closeFile(m_fileHandle);
       
   308         }
       
   309 
       
   310         // Delete the file after calling NPP_StreamAsFile(), instead of in the destructor.  It should be OK
       
   311         // to delete the file here -- NPP_StreamAsFile() is always called immediately before NPP_DestroyStream()
       
   312         // (the stream destruction function), so there can be no expectation that a plugin will read the stream
       
   313         // file asynchronously after NPP_StreamAsFile() is called.
       
   314         deleteFile(String::fromUTF8(m_filePath.data()));
       
   315         m_filePath = CString();
       
   316 
       
   317         // NPP_StreamAsFile could call NPN_DestroyStream and destroy the stream.
       
   318         if (!m_isStarted)
       
   319             return;
       
   320     }
       
   321 
       
   322     // Set m_isStarted to false before calling NPP_DestroyStream in case NPP_DestroyStream calls NPN_DestroyStream.
       
   323     m_isStarted = false;
       
   324 
       
   325     m_plugin->NPP_DestroyStream(&m_npStream, reason);
       
   326 
       
   327     notifyAndDestroyStream(reason);
       
   328 }
       
   329 
       
   330 void NetscapePluginStream::cancel()
       
   331 {
       
   332     m_plugin->cancelStreamLoad(this);
       
   333 }
       
   334 
       
   335 void NetscapePluginStream::notifyAndDestroyStream(NPReason reason)
       
   336 {
       
   337     ASSERT(!m_isStarted);
       
   338     ASSERT(!m_deliveryDataTimer.isActive());
       
   339     ASSERT(!m_urlNotifyHasBeenCalled);
       
   340     
       
   341     if (m_sendNotification) {
       
   342         m_plugin->NPP_URLNotify(m_responseURL.data(), reason, m_notificationData);
       
   343     
       
   344 #if !ASSERT_DISABLED
       
   345         m_urlNotifyHasBeenCalled = true;
       
   346 #endif    
       
   347     }
       
   348 
       
   349     m_plugin->removePluginStream(this);
       
   350 }
       
   351 
       
   352 } // namespace WebKit