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