1/*
2 * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2013 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#include "config.h"
27#include "ResourceHandle.h"
28#include "ResourceHandleInternal.h"
29
30#include "Logging.h"
31#include "NetworkingContext.h"
32#include "NotImplemented.h"
33#include "ResourceHandleClient.h"
34#include "Timer.h"
35#include <algorithm>
36#include <wtf/MainThread.h>
37#include <wtf/text/CString.h>
38
39namespace WebCore {
40
41static bool shouldForceContentSniffing;
42
43typedef HashMap<AtomicString, ResourceHandle::BuiltinConstructor> BuiltinResourceHandleConstructorMap;
44static BuiltinResourceHandleConstructorMap& builtinResourceHandleConstructorMap()
45{
46#if PLATFORM(IOS)
47    ASSERT(WebThreadIsLockedOrDisabled());
48#else
49    ASSERT(isMainThread());
50#endif
51    DEPRECATED_DEFINE_STATIC_LOCAL(BuiltinResourceHandleConstructorMap, map, ());
52    return map;
53}
54
55void ResourceHandle::registerBuiltinConstructor(const AtomicString& protocol, ResourceHandle::BuiltinConstructor constructor)
56{
57    builtinResourceHandleConstructorMap().add(protocol, constructor);
58}
59
60typedef HashMap<AtomicString, ResourceHandle::BuiltinSynchronousLoader> BuiltinResourceHandleSynchronousLoaderMap;
61static BuiltinResourceHandleSynchronousLoaderMap& builtinResourceHandleSynchronousLoaderMap()
62{
63    ASSERT(isMainThread());
64    DEPRECATED_DEFINE_STATIC_LOCAL(BuiltinResourceHandleSynchronousLoaderMap, map, ());
65    return map;
66}
67
68void ResourceHandle::registerBuiltinSynchronousLoader(const AtomicString& protocol, ResourceHandle::BuiltinSynchronousLoader loader)
69{
70    builtinResourceHandleSynchronousLoaderMap().add(protocol, loader);
71}
72
73ResourceHandle::ResourceHandle(NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff)
74    : d(adoptPtr(new ResourceHandleInternal(this, context, request, client, defersLoading, shouldContentSniff && shouldContentSniffURL(request.url()))))
75{
76    if (!request.url().isValid()) {
77        scheduleFailure(InvalidURLFailure);
78        return;
79    }
80
81    if (!portAllowed(request.url())) {
82        scheduleFailure(BlockedFailure);
83        return;
84    }
85}
86
87PassRefPtr<ResourceHandle> ResourceHandle::create(NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff)
88{
89    BuiltinResourceHandleConstructorMap::iterator protocolMapItem = builtinResourceHandleConstructorMap().find(request.url().protocol());
90
91    if (protocolMapItem != builtinResourceHandleConstructorMap().end())
92        return protocolMapItem->value(request, client);
93
94    RefPtr<ResourceHandle> newHandle(adoptRef(new ResourceHandle(context, request, client, defersLoading, shouldContentSniff)));
95
96    if (newHandle->d->m_scheduledFailureType != NoFailure)
97        return newHandle.release();
98
99    if (newHandle->start())
100        return newHandle.release();
101
102    return 0;
103}
104
105void ResourceHandle::scheduleFailure(FailureType type)
106{
107    d->m_scheduledFailureType = type;
108    d->m_failureTimer.startOneShot(0);
109}
110
111void ResourceHandle::failureTimerFired(Timer<ResourceHandle>&)
112{
113    if (!client())
114        return;
115
116    switch (d->m_scheduledFailureType) {
117        case NoFailure:
118            ASSERT_NOT_REACHED();
119            return;
120        case BlockedFailure:
121            d->m_scheduledFailureType = NoFailure;
122            client()->wasBlocked(this);
123            return;
124        case InvalidURLFailure:
125            d->m_scheduledFailureType = NoFailure;
126            client()->cannotShowURL(this);
127            return;
128    }
129
130    ASSERT_NOT_REACHED();
131}
132
133void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data)
134{
135    BuiltinResourceHandleSynchronousLoaderMap::iterator protocolMapItem = builtinResourceHandleSynchronousLoaderMap().find(request.url().protocol());
136
137    if (protocolMapItem != builtinResourceHandleSynchronousLoaderMap().end()) {
138        protocolMapItem->value(context, request, storedCredentials, error, response, data);
139        return;
140    }
141
142    platformLoadResourceSynchronously(context, request, storedCredentials, error, response, data);
143}
144
145ResourceHandleClient* ResourceHandle::client() const
146{
147    return d->m_client;
148}
149
150void ResourceHandle::setClient(ResourceHandleClient* client)
151{
152    d->m_client = client;
153}
154
155#if !PLATFORM(COCOA) && !USE(CFNETWORK) && !USE(SOUP)
156// ResourceHandle never uses async client calls on these platforms yet.
157void ResourceHandle::continueWillSendRequest(const ResourceRequest&)
158{
159    notImplemented();
160}
161
162void ResourceHandle::continueDidReceiveResponse()
163{
164    notImplemented();
165}
166
167#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
168void ResourceHandle::continueCanAuthenticateAgainstProtectionSpace(bool)
169{
170    notImplemented();
171}
172#endif
173#endif
174
175ResourceRequest& ResourceHandle::firstRequest()
176{
177    return d->m_firstRequest;
178}
179
180NetworkingContext* ResourceHandle::context() const
181{
182    return d->m_context.get();
183}
184
185const String& ResourceHandle::lastHTTPMethod() const
186{
187    return d->m_lastHTTPMethod;
188}
189
190bool ResourceHandle::hasAuthenticationChallenge() const
191{
192    return !d->m_currentWebChallenge.isNull();
193}
194
195void ResourceHandle::clearAuthentication()
196{
197#if PLATFORM(COCOA)
198    d->m_currentMacChallenge = nil;
199#endif
200    d->m_currentWebChallenge.nullify();
201}
202
203bool ResourceHandle::shouldContentSniff() const
204{
205    return d->m_shouldContentSniff;
206}
207
208bool ResourceHandle::shouldContentSniffURL(const URL& url)
209{
210#if PLATFORM(COCOA)
211    if (shouldForceContentSniffing)
212        return true;
213#endif
214    // We shouldn't content sniff file URLs as their MIME type should be established via their extension.
215    return !url.protocolIs("file");
216}
217
218void ResourceHandle::forceContentSniffing()
219{
220    shouldForceContentSniffing = true;
221}
222
223void ResourceHandle::setDefersLoading(bool defers)
224{
225    LOG(Network, "Handle %p setDefersLoading(%s)", this, defers ? "true" : "false");
226
227    ASSERT(d->m_defersLoading != defers); // Deferring is not counted, so calling setDefersLoading() repeatedly is likely to be in error.
228    d->m_defersLoading = defers;
229
230    if (defers) {
231        ASSERT(d->m_failureTimer.isActive() == (d->m_scheduledFailureType != NoFailure));
232        if (d->m_failureTimer.isActive())
233            d->m_failureTimer.stop();
234    } else if (d->m_scheduledFailureType != NoFailure) {
235        ASSERT(!d->m_failureTimer.isActive());
236        d->m_failureTimer.startOneShot(0);
237    }
238
239    platformSetDefersLoading(defers);
240}
241
242void ResourceHandle::didChangePriority(ResourceLoadPriority)
243{
244    // Optionally implemented by platform.
245}
246
247} // namespace WebCore
248