/*
 * Copyright (C) 2009, 2012 Google Inc. All rights reserved.
 * Copyright (C) 2011 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "web/FrameLoaderClientImpl.h"

#include "bindings/core/v8/ScriptController.h"
#include "core/HTMLNames.h"
#include "core/dom/Document.h"
#include "core/dom/Fullscreen.h"
#include "core/events/MessageEvent.h"
#include "core/events/MouseEvent.h"
#include "core/events/UIEventWithKeyState.h"
#include "core/frame/FrameView.h"
#include "core/frame/Settings.h"
#include "core/html/HTMLFrameElementBase.h"
#include "core/html/HTMLMediaElement.h"
#include "core/html/HTMLPlugInElement.h"
#include "core/input/EventHandler.h"
#include "core/layout/HitTestResult.h"
#include "core/loader/DocumentLoader.h"
#include "core/loader/FrameLoadRequest.h"
#include "core/loader/FrameLoader.h"
#include "core/loader/HistoryItem.h"
#include "core/origin_trials/OriginTrials.h"
#include "core/page/Page.h"
#include "core/page/WindowFeatures.h"
#include "modules/audio_output_devices/HTMLMediaElementAudioOutputDevice.h"
#include "modules/device_light/DeviceLightController.h"
#include "modules/device_orientation/DeviceMotionController.h"
#include "modules/device_orientation/DeviceOrientationAbsoluteController.h"
#include "modules/device_orientation/DeviceOrientationController.h"
#include "modules/encryptedmedia/HTMLMediaElementEncryptedMedia.h"
#include "modules/gamepad/NavigatorGamepad.h"
#include "modules/remoteplayback/HTMLMediaElementRemotePlayback.h"
#include "modules/remoteplayback/RemotePlayback.h"
#include "modules/serviceworkers/NavigatorServiceWorker.h"
#include "modules/serviceworkers/ServiceWorkerLinkResource.h"
#include "modules/storage/DOMWindowStorageController.h"
#include "modules/vr/NavigatorVR.h"
#include "platform/Histogram.h"
#include "platform/MIMETypeRegistry.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/UserGestureIndicator.h"
#include "platform/exported/WrappedResourceRequest.h"
#include "platform/exported/WrappedResourceResponse.h"
#include "platform/network/HTTPParsers.h"
#include "platform/plugins/PluginData.h"
#include "public/platform/Platform.h"
#include "public/platform/WebApplicationCacheHost.h"
#include "public/platform/WebMediaPlayerSource.h"
#include "public/platform/WebRTCPeerConnectionHandler.h"
#include "public/platform/WebSecurityOrigin.h"
#include "public/platform/WebURL.h"
#include "public/platform/WebURLError.h"
#include "public/platform/WebVector.h"
#include "public/platform/modules/serviceworker/WebServiceWorkerProvider.h"
#include "public/platform/modules/serviceworker/WebServiceWorkerProviderClient.h"
#include "public/web/WebAutofillClient.h"
#include "public/web/WebContentSettingsClient.h"
#include "public/web/WebDOMEvent.h"
#include "public/web/WebDocument.h"
#include "public/web/WebFormElement.h"
#include "public/web/WebFrameClient.h"
#include "public/web/WebNode.h"
#include "public/web/WebPlugin.h"
#include "public/web/WebPluginParams.h"
#include "public/web/WebViewClient.h"
#include "web/DevToolsEmulator.h"
#include "web/SharedWorkerRepositoryClientImpl.h"
#include "web/WebDataSourceImpl.h"
#include "web/WebDevToolsAgentImpl.h"
#include "web/WebDevToolsFrontendImpl.h"
#include "web/WebLocalFrameImpl.h"
#include "web/WebPluginContainerImpl.h"
#include "web/WebViewImpl.h"
#include "wtf/PtrUtil.h"
#include "wtf/StringExtras.h"
#include "wtf/text/CString.h"
#include "wtf/text/WTFString.h"
#include <memory>
#include <v8.h>

namespace blink {

namespace {

// Convenience helper for frame tree helpers in FrameClient to reduce the amount
// of null-checking boilerplate code. Since the frame tree is maintained in the
// web/ layer, the frame tree helpers often have to deal with null WebFrames:
// for example, a frame with no parent will return null for WebFrame::parent().
// TODO(dcheng): Remove duplication between FrameLoaderClientImpl and
// RemoteFrameClientImpl somehow...
Frame* toCoreFrame(WebFrame* frame) {
  return frame ? frame->toImplBase()->frame() : nullptr;
}

}  // namespace

FrameLoaderClientImpl::FrameLoaderClientImpl(WebLocalFrameImpl* frame)
    : m_webFrame(frame) {}

FrameLoaderClientImpl* FrameLoaderClientImpl::create(WebLocalFrameImpl* frame) {
  return new FrameLoaderClientImpl(frame);
}

FrameLoaderClientImpl::~FrameLoaderClientImpl() {}

DEFINE_TRACE(FrameLoaderClientImpl) {
  visitor->trace(m_webFrame);
  FrameLoaderClient::trace(visitor);
}

void FrameLoaderClientImpl::didCreateNewDocument() {
  if (m_webFrame->client())
    m_webFrame->client()->didCreateNewDocument(m_webFrame);
}

void FrameLoaderClientImpl::dispatchDidClearWindowObjectInMainWorld() {
  if (m_webFrame->client()) {
    m_webFrame->client()->didClearWindowObject(m_webFrame);
    Document* document = m_webFrame->frame()->document();
    if (document) {
      DeviceMotionController::from(*document);
      DeviceOrientationController::from(*document);
      DeviceOrientationAbsoluteController::from(*document);
      if (RuntimeEnabledFeatures::deviceLightEnabled())
        DeviceLightController::from(*document);
      NavigatorGamepad::from(*document);
      NavigatorServiceWorker::from(*document);
      DOMWindowStorageController::from(*document);
      if (RuntimeEnabledFeatures::webVREnabled() ||
          OriginTrials::webVREnabled(document->getExecutionContext()))
        NavigatorVR::from(*document);
    }
  }
  // FIXME: when extensions go out of process, this whole concept stops working.
  WebDevToolsFrontendImpl* devToolsFrontend =
      m_webFrame->top()->isWebLocalFrame()
          ? toWebLocalFrameImpl(m_webFrame->top())->devToolsFrontend()
          : nullptr;
  if (devToolsFrontend)
    devToolsFrontend->didClearWindowObject(m_webFrame);
}

void FrameLoaderClientImpl::documentElementAvailable() {
  if (m_webFrame->client())
    m_webFrame->client()->didCreateDocumentElement(m_webFrame);
}

void FrameLoaderClientImpl::runScriptsAtDocumentElementAvailable() {
  if (m_webFrame->client())
    m_webFrame->client()->runScriptsAtDocumentElementAvailable(m_webFrame);
  // The callback might have deleted the frame, do not use |this|!
}

void FrameLoaderClientImpl::runScriptsAtDocumentReady(bool documentIsEmpty) {
  if (m_webFrame->client())
    m_webFrame->client()->runScriptsAtDocumentReady(m_webFrame,
                                                    documentIsEmpty);
  // The callback might have deleted the frame, do not use |this|!
}

void FrameLoaderClientImpl::didCreateScriptContext(
    v8::Local<v8::Context> context,
    int extensionGroup,
    int worldId) {
  if (m_webFrame->client())
    m_webFrame->client()->didCreateScriptContext(m_webFrame, context,
                                                 extensionGroup, worldId);
}

void FrameLoaderClientImpl::willReleaseScriptContext(
    v8::Local<v8::Context> context,
    int worldId) {
  if (m_webFrame->client())
    m_webFrame->client()->willReleaseScriptContext(m_webFrame, context,
                                                   worldId);
}

bool FrameLoaderClientImpl::allowScriptExtension(const String& extensionName,
                                                 int extensionGroup,
                                                 int worldId) {
  if (m_webFrame->contentSettingsClient())
    return m_webFrame->contentSettingsClient()->allowScriptExtension(
        extensionName, extensionGroup, worldId);

  return true;
}

void FrameLoaderClientImpl::didChangeScrollOffset() {
  if (m_webFrame->client())
    m_webFrame->client()->didChangeScrollOffset(m_webFrame);
}

void FrameLoaderClientImpl::didUpdateCurrentHistoryItem() {
  if (m_webFrame->client())
    m_webFrame->client()->didUpdateCurrentHistoryItem();
}

bool FrameLoaderClientImpl::allowScript(bool enabledPerSettings) {
  if (m_webFrame->contentSettingsClient())
    return m_webFrame->contentSettingsClient()->allowScript(enabledPerSettings);

  return enabledPerSettings;
}

bool FrameLoaderClientImpl::allowScriptFromSource(bool enabledPerSettings,
                                                  const KURL& scriptURL) {
  if (m_webFrame->contentSettingsClient())
    return m_webFrame->contentSettingsClient()->allowScriptFromSource(
        enabledPerSettings, scriptURL);

  return enabledPerSettings;
}

bool FrameLoaderClientImpl::allowPlugins(bool enabledPerSettings) {
  if (m_webFrame->contentSettingsClient())
    return m_webFrame->contentSettingsClient()->allowPlugins(
        enabledPerSettings);

  return enabledPerSettings;
}

bool FrameLoaderClientImpl::allowImage(bool enabledPerSettings,
                                       const KURL& imageURL) {
  if (m_webFrame->contentSettingsClient())
    return m_webFrame->contentSettingsClient()->allowImage(enabledPerSettings,
                                                           imageURL);

  return enabledPerSettings;
}

bool FrameLoaderClientImpl::allowRunningInsecureContent(bool enabledPerSettings,
                                                        SecurityOrigin* context,
                                                        const KURL& url) {
  if (m_webFrame->contentSettingsClient())
    return m_webFrame->contentSettingsClient()->allowRunningInsecureContent(
        enabledPerSettings, WebSecurityOrigin(context), WebURL(url));

  return enabledPerSettings;
}

bool FrameLoaderClientImpl::allowAutoplay(bool defaultValue) {
  if (m_webFrame->contentSettingsClient())
    return m_webFrame->contentSettingsClient()->allowAutoplay(defaultValue);

  return defaultValue;
}

void FrameLoaderClientImpl::passiveInsecureContentFound(const KURL& url) {
  if (m_webFrame->contentSettingsClient())
    return m_webFrame->contentSettingsClient()->passiveInsecureContentFound(
        WebURL(url));
}

void FrameLoaderClientImpl::didNotAllowScript() {
  if (m_webFrame->contentSettingsClient())
    m_webFrame->contentSettingsClient()->didNotAllowScript();
}

void FrameLoaderClientImpl::didNotAllowPlugins() {
  if (m_webFrame->contentSettingsClient())
    m_webFrame->contentSettingsClient()->didNotAllowPlugins();
}

void FrameLoaderClientImpl::didUseKeygen() {
  if (m_webFrame->contentSettingsClient())
    m_webFrame->contentSettingsClient()->didUseKeygen();
}

bool FrameLoaderClientImpl::hasWebView() const {
  return m_webFrame->viewImpl();
}

bool FrameLoaderClientImpl::inShadowTree() const {
  return m_webFrame->inShadowTree();
}

Frame* FrameLoaderClientImpl::opener() const {
  return toCoreFrame(m_webFrame->opener());
}

void FrameLoaderClientImpl::setOpener(Frame* opener) {
  WebFrame* openerFrame = WebFrame::fromFrame(opener);
  if (m_webFrame->client() && m_webFrame->opener() != openerFrame)
    m_webFrame->client()->didChangeOpener(openerFrame);
  m_webFrame->setOpener(openerFrame);
}

Frame* FrameLoaderClientImpl::parent() const {
  return toCoreFrame(m_webFrame->parent());
}

Frame* FrameLoaderClientImpl::top() const {
  return toCoreFrame(m_webFrame->top());
}

Frame* FrameLoaderClientImpl::nextSibling() const {
  return toCoreFrame(m_webFrame->nextSibling());
}

Frame* FrameLoaderClientImpl::firstChild() const {
  return toCoreFrame(m_webFrame->firstChild());
}

void FrameLoaderClientImpl::willBeDetached() {
  m_webFrame->willBeDetached();
}

void FrameLoaderClientImpl::detached(FrameDetachType type) {
  // Alert the client that the frame is being detached. This is the last
  // chance we have to communicate with the client.
  WebFrameClient* client = m_webFrame->client();
  if (!client)
    return;

  m_webFrame->willDetachParent();

  // Signal that no further communication with WebFrameClient should take
  // place at this point since we are no longer associated with the Page.
  m_webFrame->setClient(0);

  client->frameDetached(m_webFrame,
                        static_cast<WebFrameClient::DetachType>(type));
  // Clear our reference to LocalFrame at the very end, in case the client
  // refers to it.
  m_webFrame->setCoreFrame(nullptr);
}

void FrameLoaderClientImpl::dispatchWillSendRequest(ResourceRequest& request) {
  // Give the WebFrameClient a crack at the request.
  if (m_webFrame->client()) {
    WrappedResourceRequest webreq(request);
    m_webFrame->client()->willSendRequest(m_webFrame, webreq);
  }
}

void FrameLoaderClientImpl::dispatchDidReceiveResponse(
    const ResourceResponse& response) {
  if (m_webFrame->client()) {
    WrappedResourceResponse webresp(response);
    m_webFrame->client()->didReceiveResponse(webresp);
  }
}

void FrameLoaderClientImpl::dispatchDidFinishDocumentLoad() {
  // TODO(dglazkov): Sadly, workers are WebFrameClients, and they can totally
  // destroy themselves when didFinishDocumentLoad is invoked, and in turn
  // destroy the fake WebLocalFrame that they create, which means that you
  // should not put any code touching `this` after the two lines below.
  if (m_webFrame->client())
    m_webFrame->client()->didFinishDocumentLoad(m_webFrame);
}

void FrameLoaderClientImpl::dispatchDidLoadResourceFromMemoryCache(
    const ResourceRequest& request,
    const ResourceResponse& response) {
  if (m_webFrame->client())
    m_webFrame->client()->didLoadResourceFromMemoryCache(
        WrappedResourceRequest(request), WrappedResourceResponse(response));
}

void FrameLoaderClientImpl::dispatchDidHandleOnloadEvents() {
  if (m_webFrame->client())
    m_webFrame->client()->didHandleOnloadEvents(m_webFrame);
}

void FrameLoaderClientImpl::
    dispatchDidReceiveServerRedirectForProvisionalLoad() {
  if (m_webFrame->client())
    m_webFrame->client()->didReceiveServerRedirectForProvisionalLoad(
        m_webFrame);
}

void FrameLoaderClientImpl::dispatchDidNavigateWithinPage(
    HistoryItem* item,
    HistoryCommitType commitType,
    bool contentInitiated) {
  bool shouldCreateHistoryEntry = commitType == StandardCommit;
  // TODO(dglazkov): Does this need to be called for subframes?
  m_webFrame->viewImpl()->didCommitLoad(shouldCreateHistoryEntry, true);
  if (m_webFrame->client())
    m_webFrame->client()->didNavigateWithinPage(
        m_webFrame, WebHistoryItem(item),
        static_cast<WebHistoryCommitType>(commitType), contentInitiated);
}

void FrameLoaderClientImpl::dispatchWillCommitProvisionalLoad() {
  if (m_webFrame->client())
    m_webFrame->client()->willCommitProvisionalLoad(m_webFrame);
}

void FrameLoaderClientImpl::dispatchDidStartProvisionalLoad() {
  if (m_webFrame->client())
    m_webFrame->client()->didStartProvisionalLoad(m_webFrame);
  if (WebDevToolsAgentImpl* devTools = devToolsAgent())
    devTools->didStartProvisionalLoad(m_webFrame->frame());
}

void FrameLoaderClientImpl::dispatchDidReceiveTitle(const String& title) {
  if (m_webFrame->client())
    m_webFrame->client()->didReceiveTitle(m_webFrame, title,
                                          WebTextDirectionLeftToRight);
}

void FrameLoaderClientImpl::dispatchDidChangeIcons(IconType type) {
  if (m_webFrame->client())
    m_webFrame->client()->didChangeIcon(m_webFrame,
                                        static_cast<WebIconURL::Type>(type));
}

void FrameLoaderClientImpl::dispatchDidCommitLoad(
    HistoryItem* item,
    HistoryCommitType commitType) {
  if (!m_webFrame->parent()) {
    m_webFrame->viewImpl()->didCommitLoad(commitType == StandardCommit, false);
  }

  if (m_webFrame->client())
    m_webFrame->client()->didCommitProvisionalLoad(
        m_webFrame, WebHistoryItem(item),
        static_cast<WebHistoryCommitType>(commitType));
  if (WebDevToolsAgentImpl* devTools = devToolsAgent())
    devTools->didCommitLoadForLocalFrame(m_webFrame->frame());
}

void FrameLoaderClientImpl::dispatchDidFailProvisionalLoad(
    const ResourceError& error,
    HistoryCommitType commitType) {
  m_webFrame->didFail(error, true, commitType);
}

void FrameLoaderClientImpl::dispatchDidFailLoad(const ResourceError& error,
                                                HistoryCommitType commitType) {
  m_webFrame->didFail(error, false, commitType);
}

void FrameLoaderClientImpl::dispatchDidFinishLoad() {
  m_webFrame->didFinish();
}

void FrameLoaderClientImpl::dispatchDidChangeThemeColor() {
  if (m_webFrame->client())
    m_webFrame->client()->didChangeThemeColor();
}

static bool allowCreatingBackgroundTabs() {
  const WebInputEvent* inputEvent = WebViewImpl::currentInputEvent();
  if (!inputEvent || (inputEvent->type != WebInputEvent::MouseUp &&
                      (inputEvent->type != WebInputEvent::RawKeyDown &&
                       inputEvent->type != WebInputEvent::KeyDown) &&
                      inputEvent->type != WebInputEvent::GestureTap))
    return false;

  unsigned short buttonNumber;
  if (WebInputEvent::isMouseEventType(inputEvent->type)) {
    const WebMouseEvent* mouseEvent =
        static_cast<const WebMouseEvent*>(inputEvent);

    switch (mouseEvent->button) {
      case WebMouseEvent::Button::Left:
        buttonNumber = 0;
        break;
      case WebMouseEvent::Button::Middle:
        buttonNumber = 1;
        break;
      case WebMouseEvent::Button::Right:
        buttonNumber = 2;
        break;
      default:
        return false;
    }
  } else {
    // The click is simulated when triggering the keypress event.
    buttonNumber = 0;
  }
  bool ctrl = inputEvent->modifiers & WebMouseEvent::ControlKey;
  bool shift = inputEvent->modifiers & WebMouseEvent::ShiftKey;
  bool alt = inputEvent->modifiers & WebMouseEvent::AltKey;
  bool meta = inputEvent->modifiers & WebMouseEvent::MetaKey;

  NavigationPolicy userPolicy;
  if (!navigationPolicyFromMouseEvent(buttonNumber, ctrl, shift, alt, meta,
                                      &userPolicy))
    return false;
  return userPolicy == NavigationPolicyNewBackgroundTab;
}

NavigationPolicy FrameLoaderClientImpl::decidePolicyForNavigation(
    const ResourceRequest& request,
    DocumentLoader* loader,
    NavigationType type,
    NavigationPolicy policy,
    bool replacesCurrentHistoryItem,
    bool isClientRedirect,
    HTMLFormElement* form) {
  if (!m_webFrame->client())
    return NavigationPolicyIgnore;

  if (policy == NavigationPolicyNewBackgroundTab &&
      !allowCreatingBackgroundTabs() &&
      !UIEventWithKeyState::newTabModifierSetFromIsolatedWorld())
    policy = NavigationPolicyNewForegroundTab;

  WebDataSourceImpl* ds = WebDataSourceImpl::fromDocumentLoader(loader);

  // Newly created child frames may need to be navigated to a history item
  // during a back/forward navigation. This will only happen when the parent
  // is a LocalFrame doing a back/forward navigation that has not completed.
  // (If the load has completed and the parent later adds a frame with script,
  // we do not want to use a history item for it.)
  bool isHistoryNavigationInNewChildFrame =
      m_webFrame->parent() && m_webFrame->parent()->isWebLocalFrame() &&
      isBackForwardLoadType(toWebLocalFrameImpl(m_webFrame->parent())
                                ->frame()
                                ->loader()
                                .loadType()) &&
      !toWebLocalFrameImpl(m_webFrame->parent())
           ->frame()
           ->document()
           ->loadEventFinished();

  WrappedResourceRequest wrappedResourceRequest(request);
  WebFrameClient::NavigationPolicyInfo navigationInfo(wrappedResourceRequest);
  navigationInfo.navigationType = static_cast<WebNavigationType>(type);
  navigationInfo.defaultPolicy = static_cast<WebNavigationPolicy>(policy);
  navigationInfo.extraData = ds ? ds->getExtraData() : nullptr;
  navigationInfo.replacesCurrentHistoryItem = replacesCurrentHistoryItem;
  navigationInfo.isHistoryNavigationInNewChildFrame =
      isHistoryNavigationInNewChildFrame;
  navigationInfo.isClientRedirect = isClientRedirect;
  if (form)
    navigationInfo.form = WebFormElement(form);

  WebNavigationPolicy webPolicy =
      m_webFrame->client()->decidePolicyForNavigation(navigationInfo);
  return static_cast<NavigationPolicy>(webPolicy);
}

void FrameLoaderClientImpl::dispatchWillSendSubmitEvent(HTMLFormElement* form) {
  if (m_webFrame->client())
    m_webFrame->client()->willSendSubmitEvent(WebFormElement(form));
}

void FrameLoaderClientImpl::dispatchWillSubmitForm(HTMLFormElement* form) {
  if (m_webFrame->client())
    m_webFrame->client()->willSubmitForm(WebFormElement(form));
}

void FrameLoaderClientImpl::didStartLoading(LoadStartType loadStartType) {
  if (m_webFrame->client())
    m_webFrame->client()->didStartLoading(loadStartType ==
                                          NavigationToDifferentDocument);
}

void FrameLoaderClientImpl::progressEstimateChanged(double progressEstimate) {
  if (m_webFrame->client())
    m_webFrame->client()->didChangeLoadProgress(progressEstimate);
}

void FrameLoaderClientImpl::didStopLoading() {
  if (m_webFrame->client())
    m_webFrame->client()->didStopLoading();
}

void FrameLoaderClientImpl::loadURLExternally(const ResourceRequest& request,
                                              NavigationPolicy policy,
                                              const String& suggestedName,
                                              bool shouldReplaceCurrentEntry) {
  if (!m_webFrame->client())
    return;
  DCHECK(m_webFrame->frame()->document());
  Fullscreen::fullyExitFullscreen(*m_webFrame->frame()->document());
  m_webFrame->client()->loadURLExternally(
      WrappedResourceRequest(request), static_cast<WebNavigationPolicy>(policy),
      suggestedName, shouldReplaceCurrentEntry);
}

void FrameLoaderClientImpl::loadErrorPage(int reason) {
  if (m_webFrame->client())
    m_webFrame->client()->loadErrorPage(reason);
}

bool FrameLoaderClientImpl::navigateBackForward(int offset) const {
  WebViewImpl* webview = m_webFrame->viewImpl();
  if (!webview->client())
    return false;

  DCHECK(offset);
  if (offset > webview->client()->historyForwardListCount())
    return false;
  if (offset < -webview->client()->historyBackListCount())
    return false;
  webview->client()->navigateBackForwardSoon(offset);
  return true;
}

void FrameLoaderClientImpl::didAccessInitialDocument() {
  if (m_webFrame->client())
    m_webFrame->client()->didAccessInitialDocument();
}

void FrameLoaderClientImpl::didDisplayInsecureContent() {
  if (m_webFrame->client())
    m_webFrame->client()->didDisplayInsecureContent();
}

void FrameLoaderClientImpl::didRunInsecureContent(SecurityOrigin* origin,
                                                  const KURL& insecureURL) {
  if (m_webFrame->client())
    m_webFrame->client()->didRunInsecureContent(WebSecurityOrigin(origin),
                                                insecureURL);
}

void FrameLoaderClientImpl::didDetectXSS(const KURL& insecureURL,
                                         bool didBlockEntirePage) {
  if (m_webFrame->client())
    m_webFrame->client()->didDetectXSS(insecureURL, didBlockEntirePage);
}

void FrameLoaderClientImpl::didDispatchPingLoader(const KURL& url) {
  if (m_webFrame->client())
    m_webFrame->client()->didDispatchPingLoader(url);
}

void FrameLoaderClientImpl::didDisplayContentWithCertificateErrors(
    const KURL& url) {
  if (m_webFrame->client())
    m_webFrame->client()->didDisplayContentWithCertificateErrors(url);
}

void FrameLoaderClientImpl::didRunContentWithCertificateErrors(
    const KURL& url) {
  if (m_webFrame->client())
    m_webFrame->client()->didRunContentWithCertificateErrors(url);
}

void FrameLoaderClientImpl::didChangePerformanceTiming() {
  if (m_webFrame->client())
    m_webFrame->client()->didChangePerformanceTiming();
}

void FrameLoaderClientImpl::didObserveLoadingBehavior(
    WebLoadingBehaviorFlag behavior) {
  if (m_webFrame->client())
    m_webFrame->client()->didObserveLoadingBehavior(behavior);
}

void FrameLoaderClientImpl::selectorMatchChanged(
    const Vector<String>& addedSelectors,
    const Vector<String>& removedSelectors) {
  if (WebFrameClient* client = m_webFrame->client())
    client->didMatchCSS(m_webFrame, WebVector<WebString>(addedSelectors),
                        WebVector<WebString>(removedSelectors));
}

DocumentLoader* FrameLoaderClientImpl::createDocumentLoader(
    LocalFrame* frame,
    const ResourceRequest& request,
    const SubstituteData& data,
    ClientRedirectPolicy clientRedirectPolicy) {
  WebDataSourceImpl* ds =
      WebDataSourceImpl::create(frame, request, data, clientRedirectPolicy);
  if (m_webFrame->client())
    m_webFrame->client()->didCreateDataSource(m_webFrame, ds);
  return ds;
}

String FrameLoaderClientImpl::userAgent() {
  WebString override =
      m_webFrame->client() ? m_webFrame->client()->userAgentOverride() : "";
  if (!override.isEmpty())
    return override;

  if (m_userAgent.isEmpty())
    m_userAgent = Platform::current()->userAgent();
  return m_userAgent;
}

String FrameLoaderClientImpl::doNotTrackValue() {
  WebString doNotTrack = m_webFrame->client()->doNotTrackValue();
  if (!doNotTrack.isEmpty())
    return doNotTrack;
  return String();
}

// Called when the FrameLoader goes into a state in which a new page load
// will occur.
void FrameLoaderClientImpl::transitionToCommittedForNewPage() {
  m_webFrame->createFrameView();
}

LocalFrame* FrameLoaderClientImpl::createFrame(
    const FrameLoadRequest& request,
    const AtomicString& name,
    HTMLFrameOwnerElement* ownerElement) {
  return m_webFrame->createChildFrame(request, name, ownerElement);
}

bool FrameLoaderClientImpl::canCreatePluginWithoutRenderer(
    const String& mimeType) const {
  if (!m_webFrame->client())
    return false;

  return m_webFrame->client()->canCreatePluginWithoutRenderer(mimeType);
}

Widget* FrameLoaderClientImpl::createPlugin(HTMLPlugInElement* element,
                                            const KURL& url,
                                            const Vector<String>& paramNames,
                                            const Vector<String>& paramValues,
                                            const String& mimeType,
                                            bool loadManually,
                                            DetachedPluginPolicy policy) {
  if (!m_webFrame->client())
    return nullptr;

  WebPluginParams params;
  params.url = url;
  params.mimeType = mimeType;
  params.attributeNames = paramNames;
  params.attributeValues = paramValues;
  params.loadManually = loadManually;

  WebPlugin* webPlugin = m_webFrame->client()->createPlugin(m_webFrame, params);
  if (!webPlugin)
    return nullptr;

  // The container takes ownership of the WebPlugin.
  WebPluginContainerImpl* container =
      WebPluginContainerImpl::create(element, webPlugin);

  if (!webPlugin->initialize(container))
    return nullptr;

  if (policy != AllowDetachedPlugin && !element->layoutObject())
    return nullptr;

  return container;
}

std::unique_ptr<WebMediaPlayer> FrameLoaderClientImpl::createWebMediaPlayer(
    HTMLMediaElement& htmlMediaElement,
    const WebMediaPlayerSource& source,
    WebMediaPlayerClient* client) {
  WebLocalFrameImpl* webFrame =
      WebLocalFrameImpl::fromFrame(htmlMediaElement.document().frame());

  if (!webFrame || !webFrame->client())
    return nullptr;

  HTMLMediaElementEncryptedMedia& encryptedMedia =
      HTMLMediaElementEncryptedMedia::from(htmlMediaElement);
  WebString sinkId(HTMLMediaElementAudioOutputDevice::sinkId(htmlMediaElement));
  return wrapUnique(webFrame->client()->createMediaPlayer(
      source, client, &encryptedMedia, encryptedMedia.contentDecryptionModule(),
      sinkId));
}

WebRemotePlaybackClient* FrameLoaderClientImpl::createWebRemotePlaybackClient(
    HTMLMediaElement& htmlMediaElement) {
  return HTMLMediaElementRemotePlayback::remote(htmlMediaElement);
}

ObjectContentType FrameLoaderClientImpl::getObjectContentType(
    const KURL& url,
    const String& explicitMimeType,
    bool shouldPreferPlugInsForImages) {
  // This code is based on Apple's implementation from
  // WebCoreSupport/WebFrameBridge.mm.

  String mimeType = explicitMimeType;
  if (mimeType.isEmpty()) {
    // Try to guess the MIME type based off the extension.
    String filename = url.lastPathComponent();
    int extensionPos = filename.reverseFind('.');
    if (extensionPos >= 0) {
      String extension = filename.substring(extensionPos + 1);
      mimeType = MIMETypeRegistry::getWellKnownMIMETypeForExtension(extension);
    }

    if (mimeType.isEmpty())
      return ObjectContentFrame;
  }

  // If Chrome is started with the --disable-plugins switch, pluginData is 0.
  PluginData* pluginData = m_webFrame->frame()->pluginData();
  bool plugInSupportsMIMEType =
      pluginData && pluginData->supportsMimeType(mimeType);

  if (MIMETypeRegistry::isSupportedImageMIMEType(mimeType))
    return shouldPreferPlugInsForImages && plugInSupportsMIMEType
               ? ObjectContentNetscapePlugin
               : ObjectContentImage;

  if (plugInSupportsMIMEType)
    return ObjectContentNetscapePlugin;

  if (MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType))
    return ObjectContentFrame;

  return ObjectContentNone;
}

WebCookieJar* FrameLoaderClientImpl::cookieJar() const {
  if (!m_webFrame->client())
    return 0;
  return m_webFrame->client()->cookieJar();
}

void FrameLoaderClientImpl::frameFocused() const {
  if (m_webFrame->client())
    m_webFrame->client()->frameFocused();
}

void FrameLoaderClientImpl::didChangeName(const String& name,
                                          const String& uniqueName) {
  if (!m_webFrame->client())
    return;
  m_webFrame->client()->didChangeName(name, uniqueName);
}

void FrameLoaderClientImpl::didEnforceInsecureRequestPolicy(
    WebInsecureRequestPolicy policy) {
  if (!m_webFrame->client())
    return;
  m_webFrame->client()->didEnforceInsecureRequestPolicy(policy);
}

void FrameLoaderClientImpl::didUpdateToUniqueOrigin() {
  if (!m_webFrame->client())
    return;
  DCHECK(m_webFrame->getSecurityOrigin().isUnique());
  m_webFrame->client()->didUpdateToUniqueOrigin(
      m_webFrame->getSecurityOrigin().isPotentiallyTrustworthy());
}

void FrameLoaderClientImpl::didChangeSandboxFlags(Frame* childFrame,
                                                  SandboxFlags flags) {
  if (!m_webFrame->client())
    return;
  m_webFrame->client()->didChangeSandboxFlags(
      WebFrame::fromFrame(childFrame), static_cast<WebSandboxFlags>(flags));
}

void FrameLoaderClientImpl::didAddContentSecurityPolicy(
    const String& headerValue,
    ContentSecurityPolicyHeaderType type,
    ContentSecurityPolicyHeaderSource source) {
  if (m_webFrame->client()) {
    m_webFrame->client()->didAddContentSecurityPolicy(
        headerValue, static_cast<WebContentSecurityPolicyType>(type),
        static_cast<WebContentSecurityPolicySource>(source));
  }
}

void FrameLoaderClientImpl::didChangeFrameOwnerProperties(
    HTMLFrameElementBase* frameElement) {
  if (!m_webFrame->client())
    return;

  m_webFrame->client()->didChangeFrameOwnerProperties(
      WebFrame::fromFrame(frameElement->contentFrame()),
      WebFrameOwnerProperties(
          frameElement->scrollingMode(), frameElement->marginWidth(),
          frameElement->marginHeight(), frameElement->allowFullscreen(),
          frameElement->allowPaymentRequest(), frameElement->csp(),
          frameElement->delegatedPermissions()));
}

void FrameLoaderClientImpl::dispatchWillStartUsingPeerConnectionHandler(
    WebRTCPeerConnectionHandler* handler) {
  m_webFrame->client()->willStartUsingPeerConnectionHandler(handler);
}

bool FrameLoaderClientImpl::allowWebGL(bool enabledPerSettings) {
  if (m_webFrame->client())
    return m_webFrame->client()->allowWebGL(enabledPerSettings);

  return enabledPerSettings;
}

void FrameLoaderClientImpl::dispatchWillInsertBody() {
  if (m_webFrame->client())
    m_webFrame->client()->willInsertBody(m_webFrame);
}

std::unique_ptr<WebServiceWorkerProvider>
FrameLoaderClientImpl::createServiceWorkerProvider() {
  if (!m_webFrame->client())
    return nullptr;
  return wrapUnique(m_webFrame->client()->createServiceWorkerProvider());
}

bool FrameLoaderClientImpl::isControlledByServiceWorker(
    DocumentLoader& loader) {
  return m_webFrame->client() &&
         m_webFrame->client()->isControlledByServiceWorker(
             *WebDataSourceImpl::fromDocumentLoader(&loader));
}

int64_t FrameLoaderClientImpl::serviceWorkerID(DocumentLoader& loader) {
  if (!m_webFrame->client())
    return -1;
  return m_webFrame->client()->serviceWorkerID(
      *WebDataSourceImpl::fromDocumentLoader(&loader));
}

SharedWorkerRepositoryClient*
FrameLoaderClientImpl::sharedWorkerRepositoryClient() {
  return m_webFrame->sharedWorkerRepositoryClient();
}

std::unique_ptr<WebApplicationCacheHost>
FrameLoaderClientImpl::createApplicationCacheHost(
    WebApplicationCacheHostClient* client) {
  if (!m_webFrame->client())
    return nullptr;
  return wrapUnique(m_webFrame->client()->createApplicationCacheHost(client));
}

void FrameLoaderClientImpl::dispatchDidChangeManifest() {
  if (m_webFrame->client())
    m_webFrame->client()->didChangeManifest();
}

unsigned FrameLoaderClientImpl::backForwardLength() {
  WebViewImpl* webview = m_webFrame->viewImpl();
  if (!webview || !webview->client())
    return 0;
  return webview->client()->historyBackListCount() + 1 +
         webview->client()->historyForwardListCount();
}

void FrameLoaderClientImpl::suddenTerminationDisablerChanged(
    bool present,
    SuddenTerminationDisablerType type) {
  if (m_webFrame->client()) {
    m_webFrame->client()->suddenTerminationDisablerChanged(
        present,
        static_cast<WebFrameClient::SuddenTerminationDisablerType>(type));
  }
}

BlameContext* FrameLoaderClientImpl::frameBlameContext() {
  if (!m_webFrame->client())
    return nullptr;
  return m_webFrame->client()->frameBlameContext();
}

LinkResource* FrameLoaderClientImpl::createServiceWorkerLinkResource(
    HTMLLinkElement* owner) {
  return ServiceWorkerLinkResource::create(owner);
}

WebEffectiveConnectionType FrameLoaderClientImpl::getEffectiveConnectionType() {
  if (m_webFrame->client())
    return m_webFrame->client()->getEffectiveConnectionType();
  return WebEffectiveConnectionType::TypeUnknown;
}

WebDevToolsAgentImpl* FrameLoaderClientImpl::devToolsAgent() {
  return WebLocalFrameImpl::fromFrame(m_webFrame->frame()->localFrameRoot())
      ->devToolsAgentImpl();
}

KURL FrameLoaderClientImpl::overrideFlashEmbedWithHTML(const KURL& url) {
  return m_webFrame->client()->overrideFlashEmbedWithHTML(WebURL(url));
}

}  // namespace blink
