1/*
2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1.  Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 * 2.  Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16 *     its contributors may be used to endorse or promote products derived
17 *     from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "PolicyChecker.h"
33
34#include "ContentSecurityPolicy.h"
35#include "DOMWindow.h"
36#include "DocumentLoader.h"
37#include "FormState.h"
38#include "Frame.h"
39#include "FrameLoader.h"
40#include "FrameLoaderClient.h"
41#include "HTMLFormElement.h"
42#include "HTMLFrameOwnerElement.h"
43#include "SecurityOrigin.h"
44
45namespace WebCore {
46
47PolicyChecker::PolicyChecker(Frame* frame)
48    : m_frame(frame)
49    , m_delegateIsDecidingNavigationPolicy(false)
50    , m_delegateIsHandlingUnimplementablePolicy(false)
51    , m_loadType(FrameLoadTypeStandard)
52{
53}
54
55void PolicyChecker::checkNavigationPolicy(const ResourceRequest& newRequest, NavigationPolicyDecisionFunction function, void* argument)
56{
57    checkNavigationPolicy(newRequest, m_frame->loader()->activeDocumentLoader(), 0, function, argument);
58}
59
60void PolicyChecker::checkNavigationPolicy(const ResourceRequest& request, DocumentLoader* loader,
61    PassRefPtr<FormState> formState, NavigationPolicyDecisionFunction function, void* argument)
62{
63    NavigationAction action = loader->triggeringAction();
64    if (action.isEmpty()) {
65        action = NavigationAction(request, NavigationTypeOther);
66        loader->setTriggeringAction(action);
67    }
68
69    // Don't ask more than once for the same request or if we are loading an empty URL.
70    // This avoids confusion on the part of the client.
71    if (equalIgnoringHeaderFields(request, loader->lastCheckedRequest()) || (!request.isNull() && request.url().isEmpty())) {
72        function(argument, request, 0, true);
73        loader->setLastCheckedRequest(request);
74        return;
75    }
76
77    // We are always willing to show alternate content for unreachable URLs;
78    // treat it like a reload so it maintains the right state for b/f list.
79    if (loader->substituteData().isValid() && !loader->substituteData().failingURL().isEmpty()) {
80        if (isBackForwardLoadType(m_loadType))
81            m_loadType = FrameLoadTypeReload;
82        function(argument, request, 0, true);
83        return;
84    }
85
86    // If we're loading content into a subframe, check against the parent's Content Security Policy
87    // and kill the load if that check fails.
88    if (m_frame->ownerElement() && !m_frame->ownerElement()->document()->contentSecurityPolicy()->allowChildFrameFromSource(request.url())) {
89        function(argument, request, 0, false);
90        return;
91    }
92
93    loader->setLastCheckedRequest(request);
94
95    m_callback.set(request, formState.get(), function, argument);
96
97    m_delegateIsDecidingNavigationPolicy = true;
98    m_frame->loader()->client()->dispatchDecidePolicyForNavigationAction(&PolicyChecker::continueAfterNavigationPolicy,
99        action, request, formState);
100    m_delegateIsDecidingNavigationPolicy = false;
101}
102
103void PolicyChecker::checkNewWindowPolicy(const NavigationAction& action, NewWindowPolicyDecisionFunction function,
104    const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, void* argument)
105{
106    if (m_frame->document() && m_frame->document()->isSandboxed(SandboxPopups))
107        return continueAfterNavigationPolicy(PolicyIgnore);
108
109    if (!DOMWindow::allowPopUp(m_frame))
110        return continueAfterNavigationPolicy(PolicyIgnore);
111
112    m_callback.set(request, formState, frameName, action, function, argument);
113    m_frame->loader()->client()->dispatchDecidePolicyForNewWindowAction(&PolicyChecker::continueAfterNewWindowPolicy,
114        action, request, formState, frameName);
115}
116
117void PolicyChecker::checkContentPolicy(const ResourceResponse& response, ContentPolicyDecisionFunction function, void* argument)
118{
119    m_callback.set(function, argument);
120    m_frame->loader()->client()->dispatchDecidePolicyForResponse(&PolicyChecker::continueAfterContentPolicy,
121        response, m_frame->loader()->activeDocumentLoader()->request());
122}
123
124void PolicyChecker::cancelCheck()
125{
126    m_frame->loader()->client()->cancelPolicyCheck();
127    m_callback.clear();
128}
129
130void PolicyChecker::stopCheck()
131{
132    m_frame->loader()->client()->cancelPolicyCheck();
133    PolicyCallback callback = m_callback;
134    m_callback.clear();
135    callback.cancel();
136}
137
138void PolicyChecker::cannotShowMIMEType(const ResourceResponse& response)
139{
140    handleUnimplementablePolicy(m_frame->loader()->client()->cannotShowMIMETypeError(response));
141}
142
143void PolicyChecker::continueLoadAfterWillSubmitForm(PolicyAction)
144{
145    // See header file for an explaination of why this function
146    // isn't like the others.
147    m_frame->loader()->continueLoadAfterWillSubmitForm();
148}
149
150void PolicyChecker::continueAfterNavigationPolicy(PolicyAction policy)
151{
152    PolicyCallback callback = m_callback;
153    m_callback.clear();
154
155    bool shouldContinue = policy == PolicyUse;
156
157    switch (policy) {
158        case PolicyIgnore:
159            callback.clearRequest();
160            break;
161        case PolicyDownload: {
162            ResourceRequest request = callback.request();
163            m_frame->loader()->setOriginalURLForDownloadRequest(request);
164            m_frame->loader()->client()->startDownload(request);
165            callback.clearRequest();
166            break;
167        }
168        case PolicyUse: {
169            ResourceRequest request(callback.request());
170
171            if (!m_frame->loader()->client()->canHandleRequest(request)) {
172                handleUnimplementablePolicy(m_frame->loader()->client()->cannotShowURLError(callback.request()));
173                callback.clearRequest();
174                shouldContinue = false;
175            }
176            break;
177        }
178    }
179
180    callback.call(shouldContinue);
181}
182
183void PolicyChecker::continueAfterNewWindowPolicy(PolicyAction policy)
184{
185    PolicyCallback callback = m_callback;
186    m_callback.clear();
187
188    switch (policy) {
189        case PolicyIgnore:
190            callback.clearRequest();
191            break;
192        case PolicyDownload:
193            m_frame->loader()->client()->startDownload(callback.request());
194            callback.clearRequest();
195            break;
196        case PolicyUse:
197            break;
198    }
199
200    callback.call(policy == PolicyUse);
201}
202
203void PolicyChecker::continueAfterContentPolicy(PolicyAction policy)
204{
205    PolicyCallback callback = m_callback;
206    m_callback.clear();
207    callback.call(policy);
208}
209
210void PolicyChecker::handleUnimplementablePolicy(const ResourceError& error)
211{
212    m_delegateIsHandlingUnimplementablePolicy = true;
213    m_frame->loader()->client()->dispatchUnableToImplementPolicy(error);
214    m_delegateIsHandlingUnimplementablePolicy = false;
215}
216
217} // namespace WebCore
218