Home | History | Annotate | Download | only in Hosted
      1 /*
      2  * Copyright (C) 2008 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. ``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 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 #if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
     27 
     28 #import "HostedNetscapePluginStream.h"
     29 
     30 #import "NetscapePluginHostProxy.h"
     31 #import "NetscapePluginInstanceProxy.h"
     32 #import "WebFrameInternal.h"
     33 #import "WebHostedNetscapePluginView.h"
     34 #import "WebKitErrorsPrivate.h"
     35 #import "WebKitPluginHost.h"
     36 #import "WebKitSystemInterface.h"
     37 #import "WebNSURLExtras.h"
     38 #import "WebNSURLRequestExtras.h"
     39 #import <WebCore/DocumentLoader.h>
     40 #import <WebCore/Frame.h>
     41 #import <WebCore/FrameLoader.h>
     42 #import <WebCore/ResourceLoadScheduler.h>
     43 #import <WebCore/SecurityOrigin.h>
     44 #import <WebCore/WebCoreURLResponse.h>
     45 #import <wtf/RefCountedLeakCounter.h>
     46 
     47 using namespace WebCore;
     48 
     49 namespace WebKit {
     50 
     51 #ifndef NDEBUG
     52 static WTF::RefCountedLeakCounter hostedNetscapePluginStreamCounter("HostedNetscapePluginStream");
     53 #endif
     54 
     55 HostedNetscapePluginStream::HostedNetscapePluginStream(NetscapePluginInstanceProxy* instance, uint32_t streamID, NSURLRequest *request)
     56     : m_instance(instance)
     57     , m_streamID(streamID)
     58     , m_isTerminated(false)
     59     , m_request(AdoptNS, [request mutableCopy])
     60     , m_requestURL([request URL])
     61     , m_frameLoader(0)
     62 {
     63     if (SecurityOrigin::shouldHideReferrer([request URL], core([instance->pluginView() webFrame])->loader()->outgoingReferrer()))
     64         [m_request.get() _web_setHTTPReferrer:nil];
     65 
     66 #ifndef NDEBUG
     67     hostedNetscapePluginStreamCounter.increment();
     68 #endif
     69 }
     70 
     71 HostedNetscapePluginStream::HostedNetscapePluginStream(NetscapePluginInstanceProxy* instance, WebCore::FrameLoader* frameLoader)
     72     : m_instance(instance)
     73     , m_streamID(1)
     74     , m_isTerminated(false)
     75     , m_frameLoader(frameLoader)
     76 {
     77 #ifndef NDEBUG
     78     hostedNetscapePluginStreamCounter.increment();
     79 #endif
     80 }
     81 
     82 HostedNetscapePluginStream::~HostedNetscapePluginStream()
     83 {
     84 #ifndef NDEBUG
     85     hostedNetscapePluginStreamCounter.decrement();
     86 #endif
     87 }
     88 
     89 void HostedNetscapePluginStream::startStreamWithResponse(NSURLResponse *response)
     90 {
     91     didReceiveResponse(0, response);
     92 }
     93 
     94 void HostedNetscapePluginStream::startStream(NSURL *responseURL, long long expectedContentLength, NSDate *lastModifiedDate, NSString *mimeType, NSData *headers)
     95 {
     96     m_responseURL = responseURL;
     97     m_mimeType = mimeType;
     98 
     99     char* mimeTypeUTF8 = const_cast<char*>([mimeType UTF8String]);
    100     int mimeTypeUTF8Length = mimeTypeUTF8 ? strlen (mimeTypeUTF8) + 1 : 0;
    101 
    102     const char *url = [responseURL _web_URLCString];
    103     int urlLength = url ? strlen(url) + 1 : 0;
    104 
    105     _WKPHStartStream(m_instance->hostProxy()->port(),
    106                      m_instance->pluginID(),
    107                      m_streamID,
    108                      const_cast<char*>(url), urlLength,
    109                      expectedContentLength,
    110                      [lastModifiedDate timeIntervalSince1970],
    111                      mimeTypeUTF8, mimeTypeUTF8Length,
    112                      const_cast<char*>(reinterpret_cast<const char*>([headers bytes])), [headers length]);
    113 }
    114 
    115 void HostedNetscapePluginStream::didReceiveData(WebCore::NetscapePlugInStreamLoader*, const char* bytes, int length)
    116 {
    117     _WKPHStreamDidReceiveData(m_instance->hostProxy()->port(),
    118                               m_instance->pluginID(),
    119                               m_streamID,
    120                               const_cast<char*>(bytes), length);
    121 }
    122 
    123 void HostedNetscapePluginStream::didFinishLoading(WebCore::NetscapePlugInStreamLoader*)
    124 {
    125     _WKPHStreamDidFinishLoading(m_instance->hostProxy()->port(),
    126                                 m_instance->pluginID(),
    127                                 m_streamID);
    128     m_instance->disconnectStream(this);
    129 }
    130 
    131 void HostedNetscapePluginStream::didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse& response)
    132 {
    133     NSURLResponse *r = response.nsURLResponse();
    134 
    135     NSMutableData *theHeaders = nil;
    136     long long expectedContentLength = [r expectedContentLength];
    137 
    138     if ([r isKindOfClass:[NSHTTPURLResponse class]]) {
    139         NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)r;
    140         theHeaders = [NSMutableData dataWithCapacity:1024];
    141 
    142         // FIXME: it would be nice to be able to get the raw HTTP header block.
    143         // This includes the HTTP version, the real status text,
    144         // all headers in their original order and including duplicates,
    145         // and all original bytes verbatim, rather than sent through Unicode translation.
    146         // Unfortunately NSHTTPURLResponse doesn't provide access at that low a level.
    147 
    148         [theHeaders appendBytes:"HTTP " length:5];
    149         char statusStr[10];
    150         long statusCode = [httpResponse statusCode];
    151         snprintf(statusStr, sizeof(statusStr), "%ld", statusCode);
    152         [theHeaders appendBytes:statusStr length:strlen(statusStr)];
    153         [theHeaders appendBytes:" OK\n" length:4];
    154 
    155         // HACK: pass the headers through as UTF-8.
    156         // This is not the intended behavior; we're supposed to pass original bytes verbatim.
    157         // But we don't have the original bytes, we have NSStrings built by the URL loading system.
    158         // It hopefully shouldn't matter, since RFC2616/RFC822 require ASCII-only headers,
    159         // but surely someone out there is using non-ASCII characters, and hopefully UTF-8 is adequate here.
    160         // It seems better than NSASCIIStringEncoding, which will lose information if non-ASCII is used.
    161 
    162         NSDictionary *headerDict = [httpResponse allHeaderFields];
    163         NSArray *keys = [[headerDict allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
    164         NSEnumerator *i = [keys objectEnumerator];
    165         NSString *k;
    166         while ((k = [i nextObject]) != nil) {
    167             NSString *v = [headerDict objectForKey:k];
    168             [theHeaders appendData:[k dataUsingEncoding:NSUTF8StringEncoding]];
    169             [theHeaders appendBytes:": " length:2];
    170             [theHeaders appendData:[v dataUsingEncoding:NSUTF8StringEncoding]];
    171             [theHeaders appendBytes:"\n" length:1];
    172         }
    173 
    174         // If the content is encoded (most likely compressed), then don't send its length to the plugin,
    175         // which is only interested in the decoded length, not yet known at the moment.
    176         // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic.
    177         NSString *contentEncoding = (NSString *)[[(NSHTTPURLResponse *)r allHeaderFields] objectForKey:@"Content-Encoding"];
    178         if (contentEncoding && ![contentEncoding isEqualToString:@"identity"])
    179             expectedContentLength = -1;
    180 
    181         [theHeaders appendBytes:"\0" length:1];
    182     }
    183 
    184     startStream([r URL], expectedContentLength, WKGetNSURLResponseLastModifiedDate(r), [r MIMEType], theHeaders);
    185 }
    186 
    187 NPReason HostedNetscapePluginStream::reasonForError(NSError *error)
    188 {
    189     if (!error)
    190         return NPRES_DONE;
    191 
    192     if ([[error domain] isEqualToString:NSURLErrorDomain] && [error code] == NSURLErrorCancelled)
    193         return NPRES_USER_BREAK;
    194 
    195     return NPRES_NETWORK_ERR;
    196 }
    197 
    198 void HostedNetscapePluginStream::didFail(WebCore::NetscapePlugInStreamLoader*, const WebCore::ResourceError& error)
    199 {
    200     if (NetscapePluginHostProxy* hostProxy = m_instance->hostProxy())
    201         _WKPHStreamDidFail(hostProxy->port(), m_instance->pluginID(), m_streamID, reasonForError(error));
    202     m_instance->disconnectStream(this);
    203 }
    204 
    205 bool HostedNetscapePluginStream::wantsAllStreams() const
    206 {
    207     // FIXME: Implement.
    208     return false;
    209 }
    210 
    211 void HostedNetscapePluginStream::start()
    212 {
    213     ASSERT(m_request);
    214     ASSERT(!m_frameLoader);
    215     ASSERT(!m_loader);
    216 
    217     m_loader = resourceLoadScheduler()->schedulePluginStreamLoad(core([m_instance->pluginView() webFrame]), this, m_request.get());
    218 }
    219 
    220 void HostedNetscapePluginStream::stop()
    221 {
    222     ASSERT(!m_frameLoader);
    223 
    224     if (!m_loader->isDone())
    225         m_loader->cancel(m_loader->cancelledError());
    226 }
    227 
    228 void HostedNetscapePluginStream::cancelLoad(NPReason reason)
    229 {
    230     cancelLoad(errorForReason(reason));
    231 }
    232 
    233 void HostedNetscapePluginStream::cancelLoad(NSError *error)
    234 {
    235     if (m_frameLoader) {
    236         ASSERT(!m_loader);
    237 
    238         DocumentLoader* documentLoader = m_frameLoader->activeDocumentLoader();
    239         if (documentLoader && documentLoader->isLoadingMainResource())
    240             documentLoader->cancelMainResourceLoad(error);
    241         return;
    242     }
    243 
    244     if (!m_loader->isDone()) {
    245         // Cancelling the load will disconnect the stream so there's no need to do it explicitly.
    246         m_loader->cancel(error);
    247     } else
    248         m_instance->disconnectStream(this);
    249 }
    250 
    251 NSError *HostedNetscapePluginStream::pluginCancelledConnectionError() const
    252 {
    253     return [[[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInCancelledConnection
    254                                            contentURL:m_responseURL ? m_responseURL.get() : m_requestURL.get()
    255                                         pluginPageURL:nil
    256                                            pluginName:[[m_instance->pluginView() pluginPackage] pluginInfo].name
    257                                              MIMEType:m_mimeType.get()] autorelease];
    258 }
    259 
    260 NSError *HostedNetscapePluginStream::errorForReason(NPReason reason) const
    261 {
    262     if (reason == NPRES_DONE)
    263         return nil;
    264 
    265     if (reason == NPRES_USER_BREAK)
    266         return [NSError _webKitErrorWithDomain:NSURLErrorDomain
    267                                           code:NSURLErrorCancelled
    268                                            URL:m_responseURL ? m_responseURL.get() : m_requestURL.get()];
    269 
    270     return pluginCancelledConnectionError();
    271 }
    272 
    273 } // namespace WebKit
    274 
    275 #endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
    276 
    277