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 47using namespace WebCore; 48 49namespace WebKit { 50 51#ifndef NDEBUG 52static WTF::RefCountedLeakCounter hostedNetscapePluginStreamCounter("HostedNetscapePluginStream"); 53#endif 54 55HostedNetscapePluginStream::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 71HostedNetscapePluginStream::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 82HostedNetscapePluginStream::~HostedNetscapePluginStream() 83{ 84#ifndef NDEBUG 85 hostedNetscapePluginStreamCounter.decrement(); 86#endif 87} 88 89void HostedNetscapePluginStream::startStreamWithResponse(NSURLResponse *response) 90{ 91 didReceiveResponse(0, response); 92} 93 94void 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 115void 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 123void 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 131void 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 187NPReason 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 198void 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 205bool HostedNetscapePluginStream::wantsAllStreams() const 206{ 207 // FIXME: Implement. 208 return false; 209} 210 211void 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 220void HostedNetscapePluginStream::stop() 221{ 222 ASSERT(!m_frameLoader); 223 224 if (!m_loader->isDone()) 225 m_loader->cancel(m_loader->cancelledError()); 226} 227 228void HostedNetscapePluginStream::cancelLoad(NPReason reason) 229{ 230 cancelLoad(errorForReason(reason)); 231} 232 233void 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 251NSError *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 260NSError *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