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