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