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