1/*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "FrameLoaderClientImpl.h"
34
35#include "BackForwardListChromium.h"
36#include "Chrome.h"
37#include "Document.h"
38#include "DocumentLoader.h"
39#include "FormState.h"
40#include "FrameLoader.h"
41#include "FrameLoadRequest.h"
42#include "FrameNetworkingContextImpl.h"
43#include "FrameView.h"
44#include "HTTPParsers.h"
45#include "HistoryItem.h"
46#include "HitTestResult.h"
47#include "HTMLAppletElement.h"
48#include "HTMLFormElement.h"  // needed by FormState.h
49#include "HTMLNames.h"
50#include "MIMETypeRegistry.h"
51#include "MouseEvent.h"
52#include "Page.h"
53#include "PlatformString.h"
54#include "PluginData.h"
55#include "PluginDataChromium.h"
56#include "ProgressTracker.h"
57#include "Settings.h"
58#include "StringExtras.h"
59#include "WebDataSourceImpl.h"
60#include "WebDevToolsAgentPrivate.h"
61#include "WebFormElement.h"
62#include "WebFrameClient.h"
63#include "WebFrameImpl.h"
64#include "WebKit.h"
65#include "WebKitClient.h"
66#include "WebMimeRegistry.h"
67#include "WebNode.h"
68#include "WebPlugin.h"
69#include "WebPluginContainerImpl.h"
70#include "WebPluginLoadObserver.h"
71#include "WebPluginParams.h"
72#include "WebSecurityOrigin.h"
73#include "WebURL.h"
74#include "WebURLError.h"
75#include "WebVector.h"
76#include "WebViewClient.h"
77#include "WebViewImpl.h"
78#include "WindowFeatures.h"
79#include "WrappedResourceRequest.h"
80#include "WrappedResourceResponse.h"
81#include <wtf/text/CString.h>
82
83using namespace WebCore;
84
85namespace WebKit {
86
87// Domain for internal error codes.
88static const char internalErrorDomain[] = "WebKit";
89
90// An internal error code.  Used to note a policy change error resulting from
91// dispatchDecidePolicyForMIMEType not passing the PolicyUse option.
92enum {
93    PolicyChangeError = -10000,
94};
95
96FrameLoaderClientImpl::FrameLoaderClientImpl(WebFrameImpl* frame)
97    : m_webFrame(frame)
98    , m_hasRepresentation(false)
99    , m_sentInitialResponseToPlugin(false)
100    , m_nextNavigationPolicy(WebNavigationPolicyIgnore)
101{
102}
103
104FrameLoaderClientImpl::~FrameLoaderClientImpl()
105{
106}
107
108void FrameLoaderClientImpl::frameLoaderDestroyed()
109{
110    // When the WebFrame was created, it had an extra reference given to it on
111    // behalf of the Frame.  Since the WebFrame owns us, this extra ref also
112    // serves to keep us alive until the FrameLoader is done with us.  The
113    // FrameLoader calls this method when it's going away.  Therefore, we balance
114    // out that extra reference, which may cause 'this' to be deleted.
115    m_webFrame->closing();
116    m_webFrame->deref();
117}
118
119void FrameLoaderClientImpl::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld*)
120{
121    if (m_webFrame->client())
122        m_webFrame->client()->didClearWindowObject(m_webFrame);
123
124    WebViewImpl* webview = m_webFrame->viewImpl();
125    if (webview->devToolsAgentPrivate())
126        webview->devToolsAgentPrivate()->didClearWindowObject(m_webFrame);
127}
128
129void FrameLoaderClientImpl::documentElementAvailable()
130{
131    if (m_webFrame->client())
132        m_webFrame->client()->didCreateDocumentElement(m_webFrame);
133}
134
135void FrameLoaderClientImpl::didCreateScriptContextForFrame()
136{
137    if (m_webFrame->client())
138        m_webFrame->client()->didCreateScriptContext(m_webFrame);
139}
140
141void FrameLoaderClientImpl::didDestroyScriptContextForFrame()
142{
143    if (m_webFrame->client())
144        m_webFrame->client()->didDestroyScriptContext(m_webFrame);
145}
146
147void FrameLoaderClientImpl::didCreateIsolatedScriptContext()
148{
149    if (m_webFrame->client())
150        m_webFrame->client()->didCreateIsolatedScriptContext(m_webFrame);
151}
152
153bool FrameLoaderClientImpl::allowScriptExtension(const String& extensionName,
154                                                 int extensionGroup)
155{
156    if (m_webFrame->client())
157        return m_webFrame->client()->allowScriptExtension(m_webFrame, extensionName, extensionGroup);
158    return false;
159}
160
161void FrameLoaderClientImpl::didPerformFirstNavigation() const
162{
163}
164
165void FrameLoaderClientImpl::registerForIconNotification(bool)
166{
167}
168
169void FrameLoaderClientImpl::didChangeScrollOffset()
170{
171    if (m_webFrame->client())
172        m_webFrame->client()->didChangeScrollOffset(m_webFrame);
173}
174
175bool FrameLoaderClientImpl::allowJavaScript(bool enabledPerSettings)
176{
177    if (m_webFrame->client())
178        return m_webFrame->client()->allowScript(m_webFrame, enabledPerSettings);
179
180    return enabledPerSettings;
181}
182
183bool FrameLoaderClientImpl::allowPlugins(bool enabledPerSettings)
184{
185    if (m_webFrame->client())
186        return m_webFrame->client()->allowPlugins(m_webFrame, enabledPerSettings);
187
188    return enabledPerSettings;
189}
190
191bool FrameLoaderClientImpl::allowImages(bool enabledPerSettings)
192{
193    if (m_webFrame->client())
194        return m_webFrame->client()->allowImages(m_webFrame, enabledPerSettings);
195
196    return enabledPerSettings;
197}
198
199void FrameLoaderClientImpl::didNotAllowScript()
200{
201    if (m_webFrame->client())
202        m_webFrame->client()->didNotAllowScript(m_webFrame);
203}
204
205void FrameLoaderClientImpl::didNotAllowPlugins()
206{
207    if (m_webFrame->client())
208        m_webFrame->client()->didNotAllowPlugins(m_webFrame);
209}
210
211bool FrameLoaderClientImpl::hasWebView() const
212{
213    return m_webFrame->viewImpl();
214}
215
216bool FrameLoaderClientImpl::hasFrameView() const
217{
218    // The Mac port has this notion of a WebFrameView, which seems to be
219    // some wrapper around an NSView.  Since our equivalent is HWND, I guess
220    // we have a "frameview" whenever we have the toplevel HWND.
221    return m_webFrame->viewImpl();
222}
223
224void FrameLoaderClientImpl::makeDocumentView()
225{
226    m_webFrame->createFrameView();
227}
228
229void FrameLoaderClientImpl::makeRepresentation(DocumentLoader*)
230{
231    m_hasRepresentation = true;
232}
233
234void FrameLoaderClientImpl::forceLayout()
235{
236    // FIXME
237}
238
239void FrameLoaderClientImpl::forceLayoutForNonHTML()
240{
241    // FIXME
242}
243
244void FrameLoaderClientImpl::setCopiesOnScroll()
245{
246    // FIXME
247}
248
249void FrameLoaderClientImpl::detachedFromParent2()
250{
251    // Nothing to do here.
252}
253
254void FrameLoaderClientImpl::detachedFromParent3()
255{
256    // Close down the proxy.  The purpose of this change is to make the
257    // call to ScriptController::clearWindowShell a no-op when called from
258    // Frame::pageDestroyed.  Without this change, this call to clearWindowShell
259    // will cause a crash.  If you remove/modify this, just ensure that you can
260    // go to a page and then navigate to a new page without getting any asserts
261    // or crashes.
262    m_webFrame->frame()->script()->proxy()->clearForClose();
263
264    // Alert the client that the frame is being detached. This is the last
265    // chance we have to communicate with the client.
266    if (m_webFrame->client())
267        m_webFrame->client()->frameDetached(m_webFrame);
268
269    // Stop communicating with the WebFrameClient at this point since we are no
270    // longer associated with the Page.
271    m_webFrame->setClient(0);
272}
273
274// This function is responsible for associating the |identifier| with a given
275// subresource load.  The following functions that accept an |identifier| are
276// called for each subresource, so they should not be dispatched to the
277// WebFrame.
278void FrameLoaderClientImpl::assignIdentifierToInitialRequest(
279    unsigned long identifier, DocumentLoader* loader,
280    const ResourceRequest& request)
281{
282    if (m_webFrame->client()) {
283        WrappedResourceRequest webreq(request);
284        m_webFrame->client()->assignIdentifierToRequest(
285            m_webFrame, identifier, webreq);
286    }
287}
288
289// If the request being loaded by |loader| is a frame, update the ResourceType.
290// A subresource in this context is anything other than a frame --
291// this includes images and xmlhttp requests.  It is important to note that a
292// subresource is NOT limited to stuff loaded through the frame's subresource
293// loader. Synchronous xmlhttp requests for example, do not go through the
294// subresource loader, but we still label them as TargetIsSubresource.
295//
296// The important edge cases to consider when modifying this function are
297// how synchronous resource loads are treated during load/unload threshold.
298static void setTargetTypeFromLoader(ResourceRequest& request, DocumentLoader* loader)
299{
300    if (loader == loader->frameLoader()->provisionalDocumentLoader()) {
301        ResourceRequest::TargetType type;
302        if (loader->frameLoader()->isLoadingMainFrame())
303            type = ResourceRequest::TargetIsMainFrame;
304        else
305            type = ResourceRequest::TargetIsSubframe;
306        request.setTargetType(type);
307    }
308}
309
310void FrameLoaderClientImpl::dispatchWillSendRequest(
311    DocumentLoader* loader, unsigned long identifier, ResourceRequest& request,
312    const ResourceResponse& redirectResponse)
313{
314    if (loader) {
315        // We want to distinguish between a request for a document to be loaded into
316        // the main frame, a sub-frame, or the sub-objects in that document.
317        setTargetTypeFromLoader(request, loader);
318
319        // Avoid repeating a form submission when navigating back or forward.
320        if (loader == loader->frameLoader()->provisionalDocumentLoader()
321            && request.httpMethod() == "POST"
322            && isBackForwardLoadType(loader->frameLoader()->loadType()))
323            request.setCachePolicy(ReturnCacheDataDontLoad);
324    }
325
326    // FrameLoader::loadEmptyDocumentSynchronously() creates an empty document
327    // with no URL.  We don't like that, so we'll rename it to about:blank.
328    if (request.url().isEmpty())
329        request.setURL(KURL(ParsedURLString, "about:blank"));
330    if (request.firstPartyForCookies().isEmpty())
331        request.setFirstPartyForCookies(KURL(ParsedURLString, "about:blank"));
332
333    // Give the WebFrameClient a crack at the request.
334    if (m_webFrame->client()) {
335        WrappedResourceRequest webreq(request);
336        WrappedResourceResponse webresp(redirectResponse);
337        m_webFrame->client()->willSendRequest(
338            m_webFrame, identifier, webreq, webresp);
339    }
340}
341
342bool FrameLoaderClientImpl::shouldUseCredentialStorage(
343    DocumentLoader*, unsigned long identifier)
344{
345    // FIXME
346    // Intended to pass through to a method on the resource load delegate.
347    // If implemented, that method controls whether the browser should ask the
348    // networking layer for a stored default credential for the page (say from
349    // the Mac OS keychain). If the method returns false, the user should be
350    // presented with an authentication challenge whether or not the networking
351    // layer has a credential stored.
352    // This returns true for backward compatibility: the ability to override the
353    // system credential store is new. (Actually, not yet fully implemented in
354    // WebKit, as of this writing.)
355    return true;
356}
357
358void FrameLoaderClientImpl::dispatchDidReceiveAuthenticationChallenge(
359    DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&)
360{
361    // FIXME
362}
363
364void FrameLoaderClientImpl::dispatchDidCancelAuthenticationChallenge(
365    DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&)
366{
367    // FIXME
368}
369
370void FrameLoaderClientImpl::dispatchDidReceiveResponse(DocumentLoader* loader,
371                                                       unsigned long identifier,
372                                                       const ResourceResponse& response)
373{
374    if (m_webFrame->client()) {
375        WrappedResourceResponse webresp(response);
376        m_webFrame->client()->didReceiveResponse(m_webFrame, identifier, webresp);
377    }
378}
379
380void FrameLoaderClientImpl::dispatchDidReceiveContentLength(
381    DocumentLoader* loader,
382    unsigned long identifier,
383    int dataLength)
384{
385}
386
387// Called when a particular resource load completes
388void FrameLoaderClientImpl::dispatchDidFinishLoading(DocumentLoader* loader,
389                                                    unsigned long identifier)
390{
391    if (m_webFrame->client())
392        m_webFrame->client()->didFinishResourceLoad(m_webFrame, identifier);
393}
394
395void FrameLoaderClientImpl::dispatchDidFailLoading(DocumentLoader* loader,
396                                                  unsigned long identifier,
397                                                  const ResourceError& error)
398{
399    if (m_webFrame->client())
400        m_webFrame->client()->didFailResourceLoad(m_webFrame, identifier, error);
401}
402
403void FrameLoaderClientImpl::dispatchDidFinishDocumentLoad()
404{
405    // A frame may be reused.  This call ensures we don't hold on to our password
406    // listeners and their associated HTMLInputElements.
407    m_webFrame->clearPasswordListeners();
408
409    if (m_webFrame->client())
410        m_webFrame->client()->didFinishDocumentLoad(m_webFrame);
411}
412
413bool FrameLoaderClientImpl::dispatchDidLoadResourceFromMemoryCache(
414    DocumentLoader* loader,
415    const ResourceRequest& request,
416    const ResourceResponse& response,
417    int length)
418{
419    if (m_webFrame->client()) {
420        WrappedResourceRequest webreq(request);
421        WrappedResourceResponse webresp(response);
422        m_webFrame->client()->didLoadResourceFromMemoryCache(
423            m_webFrame, webreq, webresp);
424    }
425    return false;  // Do not suppress remaining notifications
426}
427
428void FrameLoaderClientImpl::dispatchDidHandleOnloadEvents()
429{
430    if (m_webFrame->client())
431        m_webFrame->client()->didHandleOnloadEvents(m_webFrame);
432}
433
434// Redirect Tracking
435// =================
436// We want to keep track of the chain of redirects that occur during page
437// loading. There are two types of redirects, server redirects which are HTTP
438// response codes, and client redirects which are document.location= and meta
439// refreshes.
440//
441// This outlines the callbacks that we get in different redirect situations,
442// and how each call modifies the redirect chain.
443//
444// Normal page load
445// ----------------
446//   dispatchDidStartProvisionalLoad() -> adds URL to the redirect list
447//   dispatchDidCommitLoad()           -> DISPATCHES & clears list
448//
449// Server redirect (success)
450// -------------------------
451//   dispatchDidStartProvisionalLoad()                    -> adds source URL
452//   dispatchDidReceiveServerRedirectForProvisionalLoad() -> adds dest URL
453//   dispatchDidCommitLoad()                              -> DISPATCHES
454//
455// Client redirect (success)
456// -------------------------
457//   (on page)
458//   dispatchWillPerformClientRedirect() -> saves expected redirect
459//   dispatchDidStartProvisionalLoad()   -> appends redirect source (since
460//                                          it matches the expected redirect)
461//                                          and the current page as the dest)
462//   dispatchDidCancelClientRedirect()   -> clears expected redirect
463//   dispatchDidCommitLoad()             -> DISPATCHES
464//
465// Client redirect (cancelled)
466// (e.g meta-refresh trumped by manual doc.location change, or just cancelled
467// because a link was clicked that requires the meta refresh to be rescheduled
468// (the SOURCE URL may have changed).
469// ---------------------------
470//   dispatchDidCancelClientRedirect()                 -> clears expected redirect
471//   dispatchDidStartProvisionalLoad()                 -> adds only URL to redirect list
472//   dispatchDidCommitLoad()                           -> DISPATCHES & clears list
473//   rescheduled ? dispatchWillPerformClientRedirect() -> saves expected redirect
474//               : nothing
475
476// Client redirect (failure)
477// -------------------------
478//   (on page)
479//   dispatchWillPerformClientRedirect() -> saves expected redirect
480//   dispatchDidStartProvisionalLoad()   -> appends redirect source (since
481//                                          it matches the expected redirect)
482//                                          and the current page as the dest)
483//   dispatchDidCancelClientRedirect()
484//   dispatchDidFailProvisionalLoad()
485//
486// Load 1 -> Server redirect to 2 -> client redirect to 3 -> server redirect to 4
487// ------------------------------------------------------------------------------
488//   dispatchDidStartProvisionalLoad()                    -> adds source URL 1
489//   dispatchDidReceiveServerRedirectForProvisionalLoad() -> adds dest URL 2
490//   dispatchDidCommitLoad()                              -> DISPATCHES 1+2
491//    -- begin client redirect and NEW DATA SOURCE
492//   dispatchWillPerformClientRedirect()                  -> saves expected redirect
493//   dispatchDidStartProvisionalLoad()                    -> appends URL 2 and URL 3
494//   dispatchDidReceiveServerRedirectForProvisionalLoad() -> appends destination URL 4
495//   dispatchDidCancelClientRedirect()                    -> clears expected redirect
496//   dispatchDidCommitLoad()                              -> DISPATCHES
497//
498// Interesting case with multiple location changes involving anchors.
499// Load page 1 containing future client-redirect (back to 1, e.g meta refresh) > Click
500// on a link back to the same page (i.e an anchor href) >
501// client-redirect finally fires (with new source, set to 1#anchor)
502// -----------------------------------------------------------------------------
503//   dispatchWillPerformClientRedirect(non-zero 'interval' param) -> saves expected redirect
504//   -- click on anchor href
505//   dispatchDidCancelClientRedirect()                            -> clears expected redirect
506//   dispatchDidStartProvisionalLoad()                            -> adds 1#anchor source
507//   dispatchDidCommitLoad()                                      -> DISPATCHES 1#anchor
508//   dispatchWillPerformClientRedirect()                          -> saves exp. source (1#anchor)
509//   -- redirect timer fires
510//   dispatchDidStartProvisionalLoad()                            -> appends 1#anchor (src) and 1 (dest)
511//   dispatchDidCancelClientRedirect()                            -> clears expected redirect
512//   dispatchDidCommitLoad()                                      -> DISPATCHES 1#anchor + 1
513//
514void FrameLoaderClientImpl::dispatchDidReceiveServerRedirectForProvisionalLoad()
515{
516    WebDataSourceImpl* ds = m_webFrame->provisionalDataSourceImpl();
517    if (!ds) {
518        // Got a server redirect when there is no provisional DS!
519        ASSERT_NOT_REACHED();
520        return;
521    }
522
523    // The server redirect may have been blocked.
524    if (ds->request().isNull())
525        return;
526
527    // A provisional load should have started already, which should have put an
528    // entry in our redirect chain.
529    ASSERT(ds->hasRedirectChain());
530
531    // The URL of the destination is on the provisional data source. We also need
532    // to update the redirect chain to account for this addition (we do this
533    // before the callback so the callback can look at the redirect chain to see
534    // what happened).
535    ds->appendRedirect(ds->request().url());
536
537    if (m_webFrame->client())
538        m_webFrame->client()->didReceiveServerRedirectForProvisionalLoad(m_webFrame);
539}
540
541// Called on both success and failure of a client redirect.
542void FrameLoaderClientImpl::dispatchDidCancelClientRedirect()
543{
544    // No longer expecting a client redirect.
545    if (m_webFrame->client()) {
546        m_expectedClientRedirectSrc = KURL();
547        m_expectedClientRedirectDest = KURL();
548        m_webFrame->client()->didCancelClientRedirect(m_webFrame);
549    }
550
551    // No need to clear the redirect chain, since that data source has already
552    // been deleted by the time this function is called.
553}
554
555void FrameLoaderClientImpl::dispatchWillPerformClientRedirect(
556    const KURL& url,
557    double interval,
558    double fireDate)
559{
560    // Tells dispatchDidStartProvisionalLoad that if it sees this item it is a
561    // redirect and the source item should be added as the start of the chain.
562    m_expectedClientRedirectSrc = m_webFrame->url();
563    m_expectedClientRedirectDest = url;
564
565    // FIXME: bug 1135512. Webkit does not properly notify us of cancelling
566    // http > file client redirects. Since the FrameLoader's policy is to never
567    // carry out such a navigation anyway, the best thing we can do for now to
568    // not get confused is ignore this notification.
569    if (m_expectedClientRedirectDest.isLocalFile()
570        && m_expectedClientRedirectSrc.protocolInHTTPFamily()) {
571        m_expectedClientRedirectSrc = KURL();
572        m_expectedClientRedirectDest = KURL();
573        return;
574    }
575
576    if (m_webFrame->client()) {
577        m_webFrame->client()->willPerformClientRedirect(
578            m_webFrame,
579            m_expectedClientRedirectSrc,
580            m_expectedClientRedirectDest,
581            static_cast<unsigned int>(interval),
582            static_cast<unsigned int>(fireDate));
583    }
584}
585
586void FrameLoaderClientImpl::dispatchDidNavigateWithinPage()
587{
588    // Anchor fragment navigations are not normal loads, so we need to synthesize
589    // some events for our delegate.
590    WebViewImpl* webView = m_webFrame->viewImpl();
591
592    // Flag of whether frame loader is completed. Generate didStartLoading and
593    // didStopLoading only when loader is completed so that we don't fire
594    // them for fragment redirection that happens in window.onload handler.
595    // See https://bugs.webkit.org/show_bug.cgi?id=31838
596    bool loaderCompleted =
597        !webView->page()->mainFrame()->loader()->activeDocumentLoader()->isLoadingInAPISense();
598
599    // Generate didStartLoading if loader is completed.
600    if (webView->client() && loaderCompleted)
601        webView->client()->didStartLoading();
602
603    // We need to classify some hash changes as client redirects.
604    // FIXME: It seems wrong that the currentItem can sometimes be null.
605    HistoryItem* currentItem = m_webFrame->frame()->loader()->history()->currentItem();
606    bool isHashChange = !currentItem || !currentItem->stateObject();
607
608    WebDataSourceImpl* ds = m_webFrame->dataSourceImpl();
609    ASSERT(ds);  // Should not be null when navigating to a reference fragment!
610    if (ds) {
611        KURL url = ds->request().url();
612        KURL chainEnd;
613        if (ds->hasRedirectChain()) {
614            chainEnd = ds->endOfRedirectChain();
615            ds->clearRedirectChain();
616        }
617
618        if (isHashChange) {
619            // Figure out if this location change is because of a JS-initiated
620            // client redirect (e.g onload/setTimeout document.location.href=).
621            // FIXME: (b/1085325, b/1046841) We don't get proper redirect
622            // performed/cancelled notifications across anchor navigations, so the
623            // other redirect-tracking code in this class (see
624            // dispatch*ClientRedirect() and dispatchDidStartProvisionalLoad) is
625            // insufficient to catch and properly flag these transitions. Once a
626            // proper fix for this bug is identified and applied the following
627            // block may no longer be required.
628            bool wasClientRedirect =
629                (url == m_expectedClientRedirectDest && chainEnd == m_expectedClientRedirectSrc)
630                || !m_webFrame->isProcessingUserGesture();
631
632            if (wasClientRedirect) {
633                if (m_webFrame->client())
634                    m_webFrame->client()->didCompleteClientRedirect(m_webFrame, chainEnd);
635                ds->appendRedirect(chainEnd);
636                // Make sure we clear the expected redirect since we just effectively
637                // completed it.
638                m_expectedClientRedirectSrc = KURL();
639                m_expectedClientRedirectDest = KURL();
640            }
641        }
642
643        // Regardless of how we got here, we are navigating to a URL so we need to
644        // add it to the redirect chain.
645        ds->appendRedirect(url);
646    }
647
648    bool isNewNavigation;
649    webView->didCommitLoad(&isNewNavigation);
650    if (m_webFrame->client())
651        m_webFrame->client()->didNavigateWithinPage(m_webFrame, isNewNavigation);
652
653    // Generate didStopLoading if loader is completed.
654    if (webView->client() && loaderCompleted)
655        webView->client()->didStopLoading();
656}
657
658void FrameLoaderClientImpl::dispatchDidChangeLocationWithinPage()
659{
660    if (m_webFrame)
661        m_webFrame->client()->didChangeLocationWithinPage(m_webFrame);
662}
663
664void FrameLoaderClientImpl::dispatchDidPushStateWithinPage()
665{
666    dispatchDidNavigateWithinPage();
667}
668
669void FrameLoaderClientImpl::dispatchDidReplaceStateWithinPage()
670{
671    dispatchDidNavigateWithinPage();
672}
673
674void FrameLoaderClientImpl::dispatchDidPopStateWithinPage()
675{
676    // Ignored since dispatchDidNavigateWithinPage was already called.
677}
678
679void FrameLoaderClientImpl::dispatchWillClose()
680{
681    if (m_webFrame->client())
682        m_webFrame->client()->willClose(m_webFrame);
683}
684
685void FrameLoaderClientImpl::dispatchDidReceiveIcon()
686{
687    // The icon database is disabled, so this should never be called.
688    ASSERT_NOT_REACHED();
689}
690
691void FrameLoaderClientImpl::dispatchDidStartProvisionalLoad()
692{
693    // In case a redirect occurs, we need this to be set so that the redirect
694    // handling code can tell where the redirect came from. Server redirects
695    // will occur on the provisional load, so we need to keep track of the most
696    // recent provisional load URL.
697    // See dispatchDidReceiveServerRedirectForProvisionalLoad.
698    WebDataSourceImpl* ds = m_webFrame->provisionalDataSourceImpl();
699    if (!ds) {
700        ASSERT_NOT_REACHED();
701        return;
702    }
703    KURL url = ds->request().url();
704
705    // Since the provisional load just started, we should have not gotten
706    // any redirects yet.
707    ASSERT(!ds->hasRedirectChain());
708
709    // If this load is what we expected from a client redirect, treat it as a
710    // redirect from that original page. The expected redirect urls will be
711    // cleared by DidCancelClientRedirect.
712    bool completingClientRedirect = false;
713    if (m_expectedClientRedirectSrc.isValid()) {
714        // m_expectedClientRedirectDest could be something like
715        // "javascript:history.go(-1)" thus we need to exclude url starts with
716        // "javascript:". See bug: 1080873
717        if (m_expectedClientRedirectDest.protocolIs("javascript")
718            || m_expectedClientRedirectDest == url) {
719            ds->appendRedirect(m_expectedClientRedirectSrc);
720            completingClientRedirect = true;
721        } else {
722            // Any pending redirect is no longer in progress. This can happen
723            // if the navigation was canceled with PolicyIgnore, or if the
724            // redirect was scheduled on the wrong frame (e.g., due to a form
725            // submission targeted to _blank, as in http://webkit.org/b/44079).
726            m_expectedClientRedirectSrc = KURL();
727            m_expectedClientRedirectDest = KURL();
728        }
729    }
730    ds->appendRedirect(url);
731
732    if (m_webFrame->client()) {
733        // Whatever information didCompleteClientRedirect contains should only
734        // be considered relevant until the next provisional load has started.
735        // So we first tell the client that the load started, and then tell it
736        // about the client redirect the load is responsible for completing.
737        m_webFrame->client()->didStartProvisionalLoad(m_webFrame);
738        if (completingClientRedirect) {
739            m_webFrame->client()->didCompleteClientRedirect(
740                m_webFrame, m_expectedClientRedirectSrc);
741        }
742    }
743}
744
745void FrameLoaderClientImpl::dispatchDidReceiveTitle(const StringWithDirection& title)
746{
747    if (m_webFrame->client())
748        m_webFrame->client()->didReceiveTitle(m_webFrame, title.string(), title.direction() == LTR ? WebTextDirectionLeftToRight : WebTextDirectionRightToLeft);
749}
750
751void FrameLoaderClientImpl::dispatchDidChangeIcons()
752{
753    if (m_webFrame->client())
754        m_webFrame->client()->didChangeIcons(m_webFrame);
755}
756
757void FrameLoaderClientImpl::dispatchDidCommitLoad()
758{
759    WebViewImpl* webview = m_webFrame->viewImpl();
760    bool isNewNavigation;
761    webview->didCommitLoad(&isNewNavigation);
762
763    if (m_webFrame->client())
764        m_webFrame->client()->didCommitProvisionalLoad(m_webFrame, isNewNavigation);
765}
766
767void FrameLoaderClientImpl::dispatchDidFailProvisionalLoad(
768    const ResourceError& error)
769{
770
771    // If a policy change occured, then we do not want to inform the plugin
772    // delegate.  See http://b/907789 for details.  FIXME: This means the
773    // plugin won't receive NPP_URLNotify, which seems like it could result in
774    // a memory leak in the plugin!!
775    if (error.domain() == internalErrorDomain
776        && error.errorCode() == PolicyChangeError) {
777        m_webFrame->didFail(cancelledError(error.failingURL()), true);
778        return;
779    }
780
781    OwnPtr<WebPluginLoadObserver> observer = pluginLoadObserver();
782    m_webFrame->didFail(error, true);
783    if (observer)
784        observer->didFailLoading(error);
785}
786
787void FrameLoaderClientImpl::dispatchDidFailLoad(const ResourceError& error)
788{
789    OwnPtr<WebPluginLoadObserver> observer = pluginLoadObserver();
790    m_webFrame->didFail(error, false);
791    if (observer)
792        observer->didFailLoading(error);
793
794    // Don't clear the redirect chain, this will happen in the middle of client
795    // redirects, and we need the context. The chain will be cleared when the
796    // provisional load succeeds or fails, not the "real" one.
797}
798
799void FrameLoaderClientImpl::dispatchDidFinishLoad()
800{
801    OwnPtr<WebPluginLoadObserver> observer = pluginLoadObserver();
802
803    if (m_webFrame->client())
804        m_webFrame->client()->didFinishLoad(m_webFrame);
805
806    if (observer)
807        observer->didFinishLoading();
808
809    // Don't clear the redirect chain, this will happen in the middle of client
810    // redirects, and we need the context. The chain will be cleared when the
811    // provisional load succeeds or fails, not the "real" one.
812}
813
814void FrameLoaderClientImpl::dispatchDidFirstLayout()
815{
816    if (m_webFrame->client())
817        m_webFrame->client()->didFirstLayout(m_webFrame);
818}
819
820void FrameLoaderClientImpl::dispatchDidFirstVisuallyNonEmptyLayout()
821{
822    if (m_webFrame->client())
823        m_webFrame->client()->didFirstVisuallyNonEmptyLayout(m_webFrame);
824}
825
826Frame* FrameLoaderClientImpl::dispatchCreatePage(const NavigationAction& action)
827{
828    struct WindowFeatures features;
829    Page* newPage = m_webFrame->frame()->page()->chrome()->createWindow(
830        m_webFrame->frame(), FrameLoadRequest(m_webFrame->frame()->document()->securityOrigin()),
831        features, action);
832
833    // Make sure that we have a valid disposition.  This should have been set in
834    // the preceeding call to dispatchDecidePolicyForNewWindowAction.
835    ASSERT(m_nextNavigationPolicy != WebNavigationPolicyIgnore);
836    WebNavigationPolicy policy = m_nextNavigationPolicy;
837    m_nextNavigationPolicy = WebNavigationPolicyIgnore;
838
839    // createWindow can return null (e.g., popup blocker denies the window).
840    if (!newPage)
841        return 0;
842
843    WebViewImpl::fromPage(newPage)->setInitialNavigationPolicy(policy);
844    return newPage->mainFrame();
845}
846
847void FrameLoaderClientImpl::dispatchShow()
848{
849    WebViewImpl* webView = m_webFrame->viewImpl();
850    if (webView && webView->client())
851        webView->client()->show(webView->initialNavigationPolicy());
852}
853
854void FrameLoaderClientImpl::dispatchDecidePolicyForResponse(
855     FramePolicyFunction function,
856     const ResourceResponse& response,
857     const ResourceRequest&)
858{
859    PolicyAction action;
860
861    int statusCode = response.httpStatusCode();
862    if (statusCode == 204 || statusCode == 205) {
863        // The server does not want us to replace the page contents.
864        action = PolicyIgnore;
865    } else if (WebCore::contentDispositionType(response.httpHeaderField("Content-Disposition")) == WebCore::ContentDispositionAttachment) {
866        // The server wants us to download instead of replacing the page contents.
867        // Downloading is handled by the embedder, but we still get the initial
868        // response so that we can ignore it and clean up properly.
869        action = PolicyIgnore;
870    } else if (!canShowMIMEType(response.mimeType())) {
871        // Make sure that we can actually handle this type internally.
872        action = PolicyIgnore;
873    } else {
874        // OK, we will render this page.
875        action = PolicyUse;
876    }
877
878    // NOTE: PolicyChangeError will be generated when action is not PolicyUse.
879    (m_webFrame->frame()->loader()->policyChecker()->*function)(action);
880}
881
882void FrameLoaderClientImpl::dispatchDecidePolicyForNewWindowAction(
883    FramePolicyFunction function,
884    const NavigationAction& action,
885    const ResourceRequest& request,
886    PassRefPtr<FormState> formState,
887    const String& frameName)
888{
889    WebNavigationPolicy navigationPolicy;
890    if (!actionSpecifiesNavigationPolicy(action, &navigationPolicy))
891        navigationPolicy = WebNavigationPolicyNewForegroundTab;
892
893    PolicyAction policyAction;
894    if (navigationPolicy == WebNavigationPolicyDownload)
895        policyAction = PolicyDownload;
896    else {
897        policyAction = PolicyUse;
898
899        // Remember the disposition for when dispatchCreatePage is called.  It is
900        // unfortunate that WebCore does not provide us with any context when
901        // creating or showing the new window that would allow us to avoid having
902        // to keep this state.
903        m_nextNavigationPolicy = navigationPolicy;
904    }
905    (m_webFrame->frame()->loader()->policyChecker()->*function)(policyAction);
906}
907
908void FrameLoaderClientImpl::dispatchDecidePolicyForNavigationAction(
909    FramePolicyFunction function,
910    const NavigationAction& action,
911    const ResourceRequest& request,
912    PassRefPtr<FormState> formState) {
913    PolicyAction policyAction = PolicyIgnore;
914
915    // It is valid for this function to be invoked in code paths where the
916    // the webview is closed.
917    // The null check here is to fix a crash that seems strange
918    // (see - https://bugs.webkit.org/show_bug.cgi?id=23554).
919    if (m_webFrame->client() && !request.url().isNull()) {
920        WebNavigationPolicy navigationPolicy = WebNavigationPolicyCurrentTab;
921        actionSpecifiesNavigationPolicy(action, &navigationPolicy);
922
923        // Give the delegate a chance to change the navigation policy.
924        const WebDataSourceImpl* ds = m_webFrame->provisionalDataSourceImpl();
925        if (ds) {
926            KURL url = ds->request().url();
927            ASSERT(!url.protocolIs(backForwardNavigationScheme));
928
929            bool isRedirect = ds->hasRedirectChain();
930
931            WebNavigationType webnavType =
932                WebDataSourceImpl::toWebNavigationType(action.type());
933
934            RefPtr<Node> node;
935            for (const Event* event = action.event(); event; event = event->underlyingEvent()) {
936                if (event->isMouseEvent()) {
937                    const MouseEvent* mouseEvent =
938                        static_cast<const MouseEvent*>(event);
939                    node = m_webFrame->frame()->eventHandler()->hitTestResultAtPoint(
940                        mouseEvent->absoluteLocation(), false).innerNonSharedNode();
941                    break;
942                }
943            }
944            WebNode originatingNode(node);
945
946            navigationPolicy = m_webFrame->client()->decidePolicyForNavigation(
947                m_webFrame, ds->request(), webnavType, originatingNode,
948                navigationPolicy, isRedirect);
949        }
950
951        if (navigationPolicy == WebNavigationPolicyCurrentTab)
952            policyAction = PolicyUse;
953        else if (navigationPolicy == WebNavigationPolicyDownload)
954            policyAction = PolicyDownload;
955        else {
956            if (navigationPolicy != WebNavigationPolicyIgnore) {
957                WrappedResourceRequest webreq(request);
958                m_webFrame->client()->loadURLExternally(m_webFrame, webreq, navigationPolicy);
959            }
960            policyAction = PolicyIgnore;
961        }
962    }
963
964    (m_webFrame->frame()->loader()->policyChecker()->*function)(policyAction);
965}
966
967void FrameLoaderClientImpl::cancelPolicyCheck()
968{
969    // FIXME
970}
971
972void FrameLoaderClientImpl::dispatchUnableToImplementPolicy(const ResourceError& error)
973{
974    m_webFrame->client()->unableToImplementPolicyWithError(m_webFrame, error);
975}
976
977void FrameLoaderClientImpl::dispatchWillSendSubmitEvent(HTMLFormElement* form)
978{
979    if (m_webFrame->client())
980        m_webFrame->client()->willSendSubmitEvent(m_webFrame, WebFormElement(form));
981}
982
983void FrameLoaderClientImpl::dispatchWillSubmitForm(FramePolicyFunction function,
984    PassRefPtr<FormState> formState)
985{
986    if (m_webFrame->client())
987        m_webFrame->client()->willSubmitForm(m_webFrame, WebFormElement(formState->form()));
988    (m_webFrame->frame()->loader()->policyChecker()->*function)(PolicyUse);
989}
990
991void FrameLoaderClientImpl::dispatchDidLoadMainResource(DocumentLoader*)
992{
993    // FIXME
994}
995
996void FrameLoaderClientImpl::revertToProvisionalState(DocumentLoader*)
997{
998    m_hasRepresentation = true;
999}
1000
1001void FrameLoaderClientImpl::setMainDocumentError(DocumentLoader*,
1002                                                 const ResourceError& error)
1003{
1004    if (m_pluginWidget.get()) {
1005        if (m_sentInitialResponseToPlugin) {
1006            m_pluginWidget->didFailLoading(error);
1007            m_sentInitialResponseToPlugin = false;
1008        }
1009        m_pluginWidget = 0;
1010    }
1011}
1012
1013void FrameLoaderClientImpl::postProgressStartedNotification()
1014{
1015    WebViewImpl* webview = m_webFrame->viewImpl();
1016    if (webview && webview->client())
1017        webview->client()->didStartLoading();
1018}
1019
1020void FrameLoaderClientImpl::postProgressEstimateChangedNotification()
1021{
1022    WebViewImpl* webview = m_webFrame->viewImpl();
1023    if (webview && webview->client()) {
1024        webview->client()->didChangeLoadProgress(
1025            m_webFrame, m_webFrame->frame()->page()->progress()->estimatedProgress());
1026    }
1027
1028}
1029
1030void FrameLoaderClientImpl::postProgressFinishedNotification()
1031{
1032    // FIXME: why might the webview be null?  http://b/1234461
1033    WebViewImpl* webview = m_webFrame->viewImpl();
1034    if (webview && webview->client())
1035        webview->client()->didStopLoading();
1036}
1037
1038void FrameLoaderClientImpl::setMainFrameDocumentReady(bool ready)
1039{
1040    // FIXME
1041}
1042
1043// Creates a new connection and begins downloading from that (contrast this
1044// with |download|).
1045void FrameLoaderClientImpl::startDownload(const ResourceRequest& request)
1046{
1047    if (m_webFrame->client()) {
1048        WrappedResourceRequest webreq(request);
1049        m_webFrame->client()->loadURLExternally(
1050            m_webFrame, webreq, WebNavigationPolicyDownload);
1051    }
1052}
1053
1054void FrameLoaderClientImpl::willChangeTitle(DocumentLoader*)
1055{
1056    // FIXME
1057}
1058
1059void FrameLoaderClientImpl::didChangeTitle(DocumentLoader*)
1060{
1061    // FIXME
1062}
1063
1064// Called whenever data is received.
1065void FrameLoaderClientImpl::committedLoad(DocumentLoader* loader, const char* data, int length)
1066{
1067    if (!m_pluginWidget.get()) {
1068        if (m_webFrame->client()) {
1069            bool preventDefault = false;
1070            m_webFrame->client()->didReceiveDocumentData(m_webFrame, data, length, preventDefault);
1071            if (!preventDefault)
1072                m_webFrame->commitDocumentData(data, length);
1073        }
1074    }
1075
1076    // If we are sending data to MediaDocument, we should stop here
1077    // and cancel the request.
1078    if (m_webFrame->frame()->document()->isMediaDocument())
1079        loader->cancelMainResourceLoad(pluginWillHandleLoadError(loader->response()));
1080
1081    // The plugin widget could have been created in the m_webFrame->DidReceiveData
1082    // function.
1083    if (m_pluginWidget.get()) {
1084        if (!m_sentInitialResponseToPlugin) {
1085            m_sentInitialResponseToPlugin = true;
1086            m_pluginWidget->didReceiveResponse(
1087                m_webFrame->frame()->loader()->activeDocumentLoader()->response());
1088        }
1089
1090        // It's possible that the above call removed the pointer to the plugin, so
1091        // check before calling it.
1092        if (m_pluginWidget.get())
1093            m_pluginWidget->didReceiveData(data, length);
1094    }
1095}
1096
1097void FrameLoaderClientImpl::finishedLoading(DocumentLoader* dl)
1098{
1099    if (m_pluginWidget.get()) {
1100        m_pluginWidget->didFinishLoading();
1101        m_pluginWidget = 0;
1102        m_sentInitialResponseToPlugin = false;
1103    } else {
1104        // This is necessary to create an empty document. See bug 634004.
1105        // However, we only want to do this if makeRepresentation has been called, to
1106        // match the behavior on the Mac.
1107        if (m_hasRepresentation)
1108            dl->writer()->setEncoding("", false);
1109    }
1110}
1111
1112void FrameLoaderClientImpl::updateGlobalHistory()
1113{
1114}
1115
1116void FrameLoaderClientImpl::updateGlobalHistoryRedirectLinks()
1117{
1118}
1119
1120bool FrameLoaderClientImpl::shouldGoToHistoryItem(HistoryItem* item) const
1121{
1122    const KURL& url = item->url();
1123    if (!url.protocolIs(backForwardNavigationScheme))
1124        return true;
1125
1126    // Else, we'll punt this history navigation to the embedder.  It is
1127    // necessary that we intercept this here, well before the FrameLoader
1128    // has made any state changes for this history traversal.
1129
1130    bool ok;
1131    int offset = url.lastPathComponent().toIntStrict(&ok);
1132    if (!ok) {
1133        ASSERT_NOT_REACHED();
1134        return false;
1135    }
1136
1137    WebViewImpl* webview = m_webFrame->viewImpl();
1138    if (webview->client())
1139        webview->client()->navigateBackForwardSoon(offset);
1140
1141    return false;
1142}
1143
1144bool FrameLoaderClientImpl::shouldStopLoadingForHistoryItem(HistoryItem* targetItem) const
1145{
1146    // Don't stop loading for pseudo-back-forward URLs, since they will get
1147    // translated and then pass through again.
1148    const KURL& url = targetItem->url();
1149    return !url.protocolIs(backForwardNavigationScheme);
1150}
1151
1152void FrameLoaderClientImpl::dispatchDidAddBackForwardItem(HistoryItem*) const
1153{
1154}
1155
1156void FrameLoaderClientImpl::dispatchDidRemoveBackForwardItem(HistoryItem*) const
1157{
1158}
1159
1160void FrameLoaderClientImpl::dispatchDidChangeBackForwardIndex() const
1161{
1162}
1163
1164void FrameLoaderClientImpl::didDisplayInsecureContent()
1165{
1166    if (m_webFrame->client())
1167        m_webFrame->client()->didDisplayInsecureContent(m_webFrame);
1168}
1169
1170void FrameLoaderClientImpl::didRunInsecureContent(SecurityOrigin* origin, const KURL& insecureURL)
1171{
1172    if (m_webFrame->client())
1173        m_webFrame->client()->didRunInsecureContent(m_webFrame, WebSecurityOrigin(origin), insecureURL);
1174}
1175
1176ResourceError FrameLoaderClientImpl::blockedError(const ResourceRequest&)
1177{
1178    // FIXME
1179    return ResourceError();
1180}
1181
1182ResourceError FrameLoaderClientImpl::cancelledError(const ResourceRequest& request)
1183{
1184    if (!m_webFrame->client())
1185        return ResourceError();
1186
1187    return m_webFrame->client()->cancelledError(
1188        m_webFrame, WrappedResourceRequest(request));
1189}
1190
1191ResourceError FrameLoaderClientImpl::cannotShowURLError(const ResourceRequest& request)
1192{
1193    if (!m_webFrame->client())
1194        return ResourceError();
1195
1196    return m_webFrame->client()->cannotHandleRequestError(
1197        m_webFrame, WrappedResourceRequest(request));
1198}
1199
1200ResourceError FrameLoaderClientImpl::interruptForPolicyChangeError(
1201    const ResourceRequest& request)
1202{
1203    return ResourceError(internalErrorDomain, PolicyChangeError,
1204                         request.url().string(), String());
1205}
1206
1207ResourceError FrameLoaderClientImpl::cannotShowMIMETypeError(const ResourceResponse&)
1208{
1209    // FIXME
1210    return ResourceError();
1211}
1212
1213ResourceError FrameLoaderClientImpl::fileDoesNotExistError(const ResourceResponse&)
1214{
1215    // FIXME
1216    return ResourceError();
1217}
1218
1219ResourceError FrameLoaderClientImpl::pluginWillHandleLoadError(const ResourceResponse&)
1220{
1221    // FIXME
1222    return ResourceError();
1223}
1224
1225bool FrameLoaderClientImpl::shouldFallBack(const ResourceError& error)
1226{
1227    // This method is called when we fail to load the URL for an <object> tag
1228    // that has fallback content (child elements) and is being loaded as a frame.
1229    // The error parameter indicates the reason for the load failure.
1230    // We should let the fallback content load only if this wasn't a cancelled
1231    // request.
1232    // Note: The mac version also has a case for "WebKitErrorPluginWillHandleLoad"
1233    ResourceError c = cancelledError(ResourceRequest());
1234    return error.errorCode() != c.errorCode() || error.domain() != c.domain();
1235}
1236
1237bool FrameLoaderClientImpl::canHandleRequest(const ResourceRequest& request) const
1238{
1239    return m_webFrame->client()->canHandleRequest(
1240        m_webFrame, WrappedResourceRequest(request));
1241}
1242
1243bool FrameLoaderClientImpl::canShowMIMETypeAsHTML(const String& MIMEType) const
1244{
1245    notImplemented();
1246    return false;
1247}
1248
1249bool FrameLoaderClientImpl::canShowMIMEType(const String& mimeType) const
1250{
1251    // This method is called to determine if the media type can be shown
1252    // "internally" (i.e. inside the browser) regardless of whether or not the
1253    // browser or a plugin is doing the rendering.
1254
1255    // mimeType strings are supposed to be ASCII, but if they are not for some
1256    // reason, then it just means that the mime type will fail all of these "is
1257    // supported" checks and go down the path of an unhandled mime type.
1258    if (webKitClient()->mimeRegistry()->supportsMIMEType(mimeType) == WebMimeRegistry::IsSupported)
1259        return true;
1260
1261    // If Chrome is started with the --disable-plugins switch, pluginData is null.
1262    PluginData* pluginData = m_webFrame->frame()->page()->pluginData();
1263
1264    // See if the type is handled by an installed plugin, if so, we can show it.
1265    // FIXME: (http://b/1085524) This is the place to stick a preference to
1266    //        disable full page plugins (optionally for certain types!)
1267    return !mimeType.isEmpty() && pluginData && pluginData->supportsMimeType(mimeType);
1268}
1269
1270bool FrameLoaderClientImpl::representationExistsForURLScheme(const String&) const
1271{
1272    // FIXME
1273    return false;
1274}
1275
1276String FrameLoaderClientImpl::generatedMIMETypeForURLScheme(const String& scheme) const
1277{
1278    // This appears to generate MIME types for protocol handlers that are handled
1279    // internally. The only place I can find in the WebKit code that uses this
1280    // function is WebView::registerViewClass, where it is used as part of the
1281    // process by which custom view classes for certain document representations
1282    // are registered.
1283    String mimeType("x-apple-web-kit/");
1284    mimeType.append(scheme.lower());
1285    return mimeType;
1286}
1287
1288void FrameLoaderClientImpl::frameLoadCompleted()
1289{
1290    // FIXME: the mac port also conditionally calls setDrawsBackground:YES on
1291    // it's ScrollView here.
1292
1293    // This comment from the Mac port:
1294    // Note: Can be called multiple times.
1295    // Even if already complete, we might have set a previous item on a frame that
1296    // didn't do any data loading on the past transaction. Make sure to clear these out.
1297
1298    // FIXME: setPreviousHistoryItem() no longer exists. http://crbug.com/8566
1299    // m_webFrame->frame()->loader()->setPreviousHistoryItem(0);
1300}
1301
1302void FrameLoaderClientImpl::saveViewStateToItem(HistoryItem*)
1303{
1304    // FIXME
1305}
1306
1307void FrameLoaderClientImpl::restoreViewState()
1308{
1309    // FIXME: probably scrolls to last position when you go back or forward
1310}
1311
1312void FrameLoaderClientImpl::provisionalLoadStarted()
1313{
1314    // FIXME: On mac, this does various caching stuff
1315}
1316
1317void FrameLoaderClientImpl::didFinishLoad()
1318{
1319    OwnPtr<WebPluginLoadObserver> observer = pluginLoadObserver();
1320    if (observer)
1321        observer->didFinishLoading();
1322}
1323
1324void FrameLoaderClientImpl::prepareForDataSourceReplacement()
1325{
1326    // FIXME
1327}
1328
1329PassRefPtr<DocumentLoader> FrameLoaderClientImpl::createDocumentLoader(
1330    const ResourceRequest& request,
1331    const SubstituteData& data)
1332{
1333    RefPtr<WebDataSourceImpl> ds = WebDataSourceImpl::create(request, data);
1334    if (m_webFrame->client())
1335        m_webFrame->client()->didCreateDataSource(m_webFrame, ds.get());
1336    return ds.release();
1337}
1338
1339void FrameLoaderClientImpl::setTitle(const StringWithDirection& title, const KURL& url)
1340{
1341    // FIXME: inform consumer of changes to the title.
1342}
1343
1344String FrameLoaderClientImpl::userAgent(const KURL& url)
1345{
1346    return webKitClient()->userAgent(url);
1347}
1348
1349void FrameLoaderClientImpl::savePlatformDataToCachedFrame(CachedFrame*)
1350{
1351    // The page cache should be disabled.
1352    ASSERT_NOT_REACHED();
1353}
1354
1355void FrameLoaderClientImpl::transitionToCommittedFromCachedFrame(CachedFrame*)
1356{
1357    ASSERT_NOT_REACHED();
1358}
1359
1360// Called when the FrameLoader goes into a state in which a new page load
1361// will occur.
1362void FrameLoaderClientImpl::transitionToCommittedForNewPage()
1363{
1364    makeDocumentView();
1365}
1366
1367void FrameLoaderClientImpl::didSaveToPageCache()
1368{
1369}
1370
1371void FrameLoaderClientImpl::didRestoreFromPageCache()
1372{
1373}
1374
1375void FrameLoaderClientImpl::dispatchDidBecomeFrameset(bool)
1376{
1377}
1378
1379bool FrameLoaderClientImpl::canCachePage() const
1380{
1381    // Since we manage the cache, always report this page as non-cacheable to
1382    // FrameLoader.
1383    return false;
1384}
1385
1386// Downloading is handled in the browser process, not WebKit. If we get to this
1387// point, our download detection code in the ResourceDispatcherHost is broken!
1388void FrameLoaderClientImpl::download(ResourceHandle* handle,
1389                                     const ResourceRequest& request,
1390                                     const ResourceRequest& initialRequest,
1391                                     const ResourceResponse& response)
1392{
1393    ASSERT_NOT_REACHED();
1394}
1395
1396PassRefPtr<Frame> FrameLoaderClientImpl::createFrame(
1397    const KURL& url,
1398    const String& name,
1399    HTMLFrameOwnerElement* ownerElement,
1400    const String& referrer,
1401    bool allowsScrolling,
1402    int marginWidth,
1403    int marginHeight)
1404{
1405    FrameLoadRequest frameRequest(m_webFrame->frame()->document()->securityOrigin(),
1406        ResourceRequest(url, referrer), name);
1407    return m_webFrame->createChildFrame(frameRequest, ownerElement);
1408}
1409
1410void FrameLoaderClientImpl::didTransferChildFrameToNewDocument(Page*)
1411{
1412    ASSERT(m_webFrame->frame()->ownerElement());
1413
1414    WebFrameImpl* newParent = static_cast<WebFrameImpl*>(m_webFrame->parent());
1415    if (!newParent || !newParent->client())
1416        return;
1417
1418    // Replace the client since the old client may be destroyed when the
1419    // previous page is closed.
1420    m_webFrame->setClient(newParent->client());
1421}
1422
1423void FrameLoaderClientImpl::transferLoadingResourceFromPage(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request, Page* oldPage)
1424{
1425    assignIdentifierToInitialRequest(identifier, loader, request);
1426
1427    WebFrameImpl* oldWebFrame = WebFrameImpl::fromFrame(oldPage->mainFrame());
1428    if (oldWebFrame && oldWebFrame->client())
1429        oldWebFrame->client()->removeIdentifierForRequest(identifier);
1430}
1431
1432PassRefPtr<Widget> FrameLoaderClientImpl::createPlugin(
1433    const IntSize& size, // FIXME: how do we use this?
1434    HTMLPlugInElement* element,
1435    const KURL& url,
1436    const Vector<String>& paramNames,
1437    const Vector<String>& paramValues,
1438    const String& mimeType,
1439    bool loadManually)
1440{
1441    if (!m_webFrame->client())
1442        return 0;
1443
1444    WebPluginParams params;
1445    params.url = url;
1446    params.mimeType = mimeType;
1447    params.attributeNames = paramNames;
1448    params.attributeValues = paramValues;
1449    params.loadManually = loadManually;
1450
1451    WebPlugin* webPlugin = m_webFrame->client()->createPlugin(m_webFrame, params);
1452    if (!webPlugin)
1453        return 0;
1454
1455    // The container takes ownership of the WebPlugin.
1456    RefPtr<WebPluginContainerImpl> container =
1457        WebPluginContainerImpl::create(element, webPlugin);
1458
1459    if (!webPlugin->initialize(container.get()))
1460        return 0;
1461
1462    // The element might have been removed during plugin initialization!
1463    if (!element->renderer())
1464        return 0;
1465
1466    return container;
1467}
1468
1469// This method gets called when a plugin is put in place of html content
1470// (e.g., acrobat reader).
1471void FrameLoaderClientImpl::redirectDataToPlugin(Widget* pluginWidget)
1472{
1473    if (pluginWidget->isPluginContainer())
1474        m_pluginWidget = static_cast<WebPluginContainerImpl*>(pluginWidget);
1475    ASSERT(m_pluginWidget.get());
1476}
1477
1478PassRefPtr<Widget> FrameLoaderClientImpl::createJavaAppletWidget(
1479    const IntSize& size,
1480    HTMLAppletElement* element,
1481    const KURL& /* baseURL */,
1482    const Vector<String>& paramNames,
1483    const Vector<String>& paramValues)
1484{
1485    return createPlugin(size, element, KURL(), paramNames, paramValues,
1486        "application/x-java-applet", false);
1487}
1488
1489ObjectContentType FrameLoaderClientImpl::objectContentType(
1490    const KURL& url,
1491    const String& explicitMimeType,
1492    bool shouldPreferPlugInsForImages)
1493{
1494    // This code is based on Apple's implementation from
1495    // WebCoreSupport/WebFrameBridge.mm.
1496
1497    String mimeType = explicitMimeType;
1498    if (mimeType.isEmpty()) {
1499        // Try to guess the MIME type based off the extension.
1500        String filename = url.lastPathComponent();
1501        int extensionPos = filename.reverseFind('.');
1502        if (extensionPos >= 0) {
1503            String extension = filename.substring(extensionPos + 1);
1504            mimeType = MIMETypeRegistry::getMIMETypeForExtension(extension);
1505            if (mimeType.isEmpty()) {
1506                // If there's no mimetype registered for the extension, check to see
1507                // if a plugin can handle the extension.
1508                mimeType = getPluginMimeTypeFromExtension(extension);
1509            }
1510        }
1511
1512        if (mimeType.isEmpty())
1513            return ObjectContentFrame;
1514    }
1515
1516    // If Chrome is started with the --disable-plugins switch, pluginData is 0.
1517    PluginData* pluginData = m_webFrame->frame()->page()->pluginData();
1518    bool plugInSupportsMIMEType = pluginData && pluginData->supportsMimeType(mimeType);
1519
1520    if (MIMETypeRegistry::isSupportedImageMIMEType(mimeType))
1521        return shouldPreferPlugInsForImages && plugInSupportsMIMEType ? ObjectContentNetscapePlugin : ObjectContentImage;
1522
1523    if (plugInSupportsMIMEType)
1524        return ObjectContentNetscapePlugin;
1525
1526    if (MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType))
1527        return ObjectContentFrame;
1528
1529    return ObjectContentNone;
1530}
1531
1532String FrameLoaderClientImpl::overrideMediaType() const
1533{
1534    // FIXME
1535    return String();
1536}
1537
1538bool FrameLoaderClientImpl::actionSpecifiesNavigationPolicy(
1539    const NavigationAction& action,
1540    WebNavigationPolicy* policy)
1541{
1542    const MouseEvent* event = 0;
1543    if (action.type() == NavigationTypeLinkClicked
1544        && action.event()->isMouseEvent())
1545        event = static_cast<const MouseEvent*>(action.event());
1546    else if (action.type() == NavigationTypeFormSubmitted
1547             && action.event()
1548             && action.event()->underlyingEvent()
1549             && action.event()->underlyingEvent()->isMouseEvent())
1550        event = static_cast<const MouseEvent*>(action.event()->underlyingEvent());
1551
1552    if (!event)
1553        return false;
1554
1555    return WebViewImpl::navigationPolicyFromMouseEvent(
1556        event->button(), event->ctrlKey(), event->shiftKey(), event->altKey(),
1557        event->metaKey(), policy);
1558}
1559
1560PassOwnPtr<WebPluginLoadObserver> FrameLoaderClientImpl::pluginLoadObserver()
1561{
1562    WebDataSourceImpl* ds = WebDataSourceImpl::fromDocumentLoader(
1563        m_webFrame->frame()->loader()->activeDocumentLoader());
1564    if (!ds) {
1565        // We can arrive here if a popstate event handler detaches this frame.
1566        // FIXME: Remove this code once http://webkit.org/b/36202 is fixed.
1567        ASSERT(!m_webFrame->frame()->page());
1568        return 0;
1569    }
1570    return ds->releasePluginLoadObserver();
1571}
1572
1573PassRefPtr<FrameNetworkingContext> FrameLoaderClientImpl::createNetworkingContext()
1574{
1575    return FrameNetworkingContextImpl::create(m_webFrame->frame());
1576}
1577
1578} // namespace WebKit
1579