blob: 5e22328fd84548ad23f1b59e61defec41b6cecdc [file] [log] [blame]
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
#import "HostedNetscapePluginStream.h"
#import "NetscapePluginHostProxy.h"
#import "NetscapePluginInstanceProxy.h"
#import "WebFrameInternal.h"
#import "WebHostedNetscapePluginView.h"
#import "WebKitErrorsPrivate.h"
#import "WebKitPluginHost.h"
#import "WebNSURLExtras.h"
#import "WebNSURLRequestExtras.h"
#import "WebResourceLoadScheduler.h"
#import <WebCore/Document.h>
#import <WebCore/DocumentLoader.h>
#import <WebCore/Frame.h>
#import <WebCore/FrameLoader.h>
#import <WebCore/PlatformStrategies.h>
#import <WebCore/SecurityOrigin.h>
#import <WebCore/SecurityPolicy.h>
#import <WebCore/WebCoreURLResponse.h>
#import <pal/spi/cf/CFNetworkSPI.h>
#import <wtf/CompletionHandler.h>
#import <wtf/RefCountedLeakCounter.h>
using namespace WebCore;
namespace WebKit {
DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, hostedNetscapePluginStreamCounter, ("HostedNetscapePluginStream"));
HostedNetscapePluginStream::HostedNetscapePluginStream(NetscapePluginInstanceProxy* instance, uint32_t streamID, NSURLRequest *request)
: m_instance(instance)
, m_streamID(streamID)
, m_request(adoptNS([request mutableCopy]))
, m_requestURL([request URL])
, m_frameLoader(0)
{
String referrer = SecurityPolicy::generateReferrerHeader(core([instance->pluginView() webFrame])->document()->referrerPolicy(), [request URL], core([instance->pluginView() webFrame])->loader().outgoingReferrer());
if (referrer.isEmpty())
[m_request.get() _web_setHTTPReferrer:nil];
else
[m_request.get() _web_setHTTPReferrer:referrer];
#ifndef NDEBUG
hostedNetscapePluginStreamCounter.increment();
#endif
}
HostedNetscapePluginStream::HostedNetscapePluginStream(NetscapePluginInstanceProxy* instance, WebCore::FrameLoader* frameLoader)
: m_instance(instance)
, m_streamID(1)
, m_frameLoader(frameLoader)
{
#ifndef NDEBUG
hostedNetscapePluginStreamCounter.increment();
#endif
}
HostedNetscapePluginStream::~HostedNetscapePluginStream()
{
#ifndef NDEBUG
hostedNetscapePluginStreamCounter.decrement();
#endif
}
void HostedNetscapePluginStream::startStreamWithResponse(NSURLResponse *response)
{
didReceiveResponse(0, response);
}
void HostedNetscapePluginStream::startStream(NSURL *responseURL, long long expectedContentLength, NSDate *lastModifiedDate, NSString *mimeType, NSData *headers)
{
m_responseURL = responseURL;
m_mimeType = mimeType;
char* mimeTypeUTF8 = const_cast<char*>([mimeType UTF8String]);
int mimeTypeUTF8Length = mimeTypeUTF8 ? strlen (mimeTypeUTF8) + 1 : 0;
const char *url = [responseURL _web_URLCString];
int urlLength = url ? strlen(url) + 1 : 0;
_WKPHStartStream(m_instance->hostProxy()->port(),
m_instance->pluginID(),
m_streamID,
const_cast<char*>(url), urlLength,
expectedContentLength,
[lastModifiedDate timeIntervalSince1970],
mimeTypeUTF8, mimeTypeUTF8Length,
const_cast<char*>(reinterpret_cast<const char*>([headers bytes])), [headers length]);
}
void HostedNetscapePluginStream::didReceiveData(WebCore::NetscapePlugInStreamLoader*, const char* bytes, int length)
{
_WKPHStreamDidReceiveData(m_instance->hostProxy()->port(),
m_instance->pluginID(),
m_streamID,
const_cast<char*>(bytes), length);
}
void HostedNetscapePluginStream::didFinishLoading(WebCore::NetscapePlugInStreamLoader*)
{
_WKPHStreamDidFinishLoading(m_instance->hostProxy()->port(),
m_instance->pluginID(),
m_streamID);
m_instance->disconnectStream(this);
}
void HostedNetscapePluginStream::willSendRequest(NetscapePlugInStreamLoader*, ResourceRequest&& request, const ResourceResponse&, CompletionHandler<void(WebCore::ResourceRequest&&)>&& callback)
{
// FIXME: We should notify the plug-in with NPP_URLRedirectNotify here.
callback(WTFMove(request));
}
void HostedNetscapePluginStream::didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse& response)
{
NSURLResponse *r = response.nsURLResponse();
NSMutableData *theHeaders = nil;
long long expectedContentLength = [r expectedContentLength];
if ([r isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)r;
theHeaders = [NSMutableData dataWithCapacity:1024];
// FIXME: it would be nice to be able to get the raw HTTP header block.
// This includes the HTTP version, the real status text,
// all headers in their original order and including duplicates,
// and all original bytes verbatim, rather than sent through Unicode translation.
// Unfortunately NSHTTPURLResponse doesn't provide access at that low a level.
[theHeaders appendBytes:"HTTP " length:5];
char statusStr[10];
long statusCode = [httpResponse statusCode];
snprintf(statusStr, sizeof(statusStr), "%ld", statusCode);
[theHeaders appendBytes:statusStr length:strlen(statusStr)];
[theHeaders appendBytes:" OK\n" length:4];
// HACK: pass the headers through as UTF-8.
// This is not the intended behavior; we're supposed to pass original bytes verbatim.
// But we don't have the original bytes, we have NSStrings built by the URL loading system.
// It hopefully shouldn't matter, since RFC2616/RFC822 require ASCII-only headers,
// but surely someone out there is using non-ASCII characters, and hopefully UTF-8 is adequate here.
// It seems better than NSASCIIStringEncoding, which will lose information if non-ASCII is used.
NSDictionary *headerDict = [httpResponse allHeaderFields];
NSArray *keys = [[headerDict allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
NSEnumerator *i = [keys objectEnumerator];
NSString *k;
while ((k = [i nextObject]) != nil) {
NSString *v = [headerDict objectForKey:k];
[theHeaders appendData:[k dataUsingEncoding:NSUTF8StringEncoding]];
[theHeaders appendBytes:": " length:2];
[theHeaders appendData:[v dataUsingEncoding:NSUTF8StringEncoding]];
[theHeaders appendBytes:"\n" length:1];
}
// If the content is encoded (most likely compressed), then don't send its length to the plugin,
// which is only interested in the decoded length, not yet known at the moment.
// <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic.
NSString *contentEncoding = (NSString *)[[(NSHTTPURLResponse *)r allHeaderFields] objectForKey:@"Content-Encoding"];
if (contentEncoding && ![contentEncoding isEqualToString:@"identity"])
expectedContentLength = -1;
[theHeaders appendBytes:"\0" length:1];
}
startStream([r URL], expectedContentLength, [r _lastModifiedDate], [r MIMEType], theHeaders);
}
NPReason HostedNetscapePluginStream::reasonForError(NSError *error)
{
if (!error)
return NPRES_DONE;
if ([[error domain] isEqualToString:NSURLErrorDomain] && [error code] == NSURLErrorCancelled)
return NPRES_USER_BREAK;
return NPRES_NETWORK_ERR;
}
void HostedNetscapePluginStream::didFail(WebCore::NetscapePlugInStreamLoader*, const WebCore::ResourceError& error)
{
if (NetscapePluginHostProxy* hostProxy = m_instance->hostProxy())
_WKPHStreamDidFail(hostProxy->port(), m_instance->pluginID(), m_streamID, reasonForError(error));
m_instance->disconnectStream(this);
}
bool HostedNetscapePluginStream::wantsAllStreams() const
{
// FIXME: Implement.
return false;
}
void HostedNetscapePluginStream::start()
{
ASSERT(m_request);
ASSERT(!m_frameLoader);
ASSERT(!m_loader);
webResourceLoadScheduler().schedulePluginStreamLoad(*core([m_instance->pluginView() webFrame]), *this, m_request.get(), [this, protectedThis = makeRef(*this)] (RefPtr<WebCore::NetscapePlugInStreamLoader>&& loader) {
m_loader = WTFMove(loader);
});
}
void HostedNetscapePluginStream::stop()
{
ASSERT(!m_frameLoader);
if (!m_loader->isDone())
m_loader->cancel(m_loader->cancelledError());
}
void HostedNetscapePluginStream::cancelLoad(NPReason reason)
{
cancelLoad(errorForReason(reason));
}
void HostedNetscapePluginStream::cancelLoad(NSError *error)
{
if (m_frameLoader) {
ASSERT(!m_loader);
DocumentLoader* documentLoader = m_frameLoader->activeDocumentLoader();
if (documentLoader && documentLoader->isLoadingMainResource())
documentLoader->cancelMainResourceLoad(error);
return;
}
if (!m_loader->isDone()) {
// Cancelling the load will disconnect the stream so there's no need to do it explicitly.
m_loader->cancel(error);
} else
m_instance->disconnectStream(this);
}
NSError *HostedNetscapePluginStream::pluginCancelledConnectionError() const
{
return [[[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInCancelledConnection
contentURL:m_responseURL ? m_responseURL.get() : m_requestURL.get()
pluginPageURL:nil
pluginName:[[m_instance->pluginView() pluginPackage] pluginInfo].name
MIMEType:m_mimeType.get()] autorelease];
}
NSError *HostedNetscapePluginStream::errorForReason(NPReason reason) const
{
if (reason == NPRES_DONE)
return nil;
if (reason == NPRES_USER_BREAK)
return [NSError _webKitErrorWithDomain:NSURLErrorDomain
code:NSURLErrorCancelled
URL:m_responseURL ? m_responseURL.get() : m_requestURL.get()];
return pluginCancelledConnectionError();
}
} // namespace WebKit
#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)