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