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