// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "web/RemoteFrameClientImpl.h"

#include "core/events/KeyboardEvent.h"
#include "core/events/MouseEvent.h"
#include "core/events/WheelEvent.h"
#include "core/frame/RemoteFrame.h"
#include "core/frame/RemoteFrameView.h"
#include "core/layout/api/LayoutItem.h"
#include "core/layout/api/LayoutPartItem.h"
#include "platform/exported/WrappedResourceRequest.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "platform/weborigin/SecurityPolicy.h"
#include "public/web/WebRemoteFrameClient.h"
#include "web/WebInputEventConversion.h"
#include "web/WebLocalFrameImpl.h"
#include "web/WebRemoteFrameImpl.h"
#include "wtf/PtrUtil.h"
#include <memory>

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

RemoteFrameClientImpl::RemoteFrameClientImpl(WebRemoteFrameImpl* webFrame)
    : m_webFrame(webFrame) {}

RemoteFrameClientImpl* RemoteFrameClientImpl::create(
    WebRemoteFrameImpl* webFrame) {
  return new RemoteFrameClientImpl(webFrame);
}

DEFINE_TRACE(RemoteFrameClientImpl) {
  visitor->trace(m_webFrame);
  RemoteFrameClient::trace(visitor);
}

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

void RemoteFrameClientImpl::willBeDetached() {}

void RemoteFrameClientImpl::detached(FrameDetachType type) {
  // Alert the client that the frame is being detached.
  WebRemoteFrameClient* client = m_webFrame->client();
  if (!client)
    return;

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

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

void RemoteFrameClientImpl::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* RemoteFrameClientImpl::parent() const {
  return toCoreFrame(m_webFrame->parent());
}

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

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

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

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

void RemoteFrameClientImpl::navigate(const ResourceRequest& request,
                                     bool shouldReplaceCurrentEntry) {
  if (m_webFrame->client())
    m_webFrame->client()->navigate(WrappedResourceRequest(request),
                                   shouldReplaceCurrentEntry);
}

void RemoteFrameClientImpl::reload(FrameLoadType loadType,
                                   ClientRedirectPolicy clientRedirectPolicy) {
  DCHECK(isReloadLoadType(loadType));
  if (m_webFrame->client())
    m_webFrame->client()->reload(
        static_cast<WebFrameLoadType>(loadType),
        static_cast<WebClientRedirectPolicy>(clientRedirectPolicy));
}

unsigned RemoteFrameClientImpl::backForwardLength() {
  // TODO(creis,japhet): This method should return the real value for the
  // session history length. For now, return static value for the initial
  // navigation and the subsequent one moving the frame out-of-process.
  // See https://crbug.com/501116.
  return 2;
}

void RemoteFrameClientImpl::forwardPostMessage(
    MessageEvent* event,
    PassRefPtr<SecurityOrigin> target,
    LocalFrame* sourceFrame) const {
  if (m_webFrame->client())
    m_webFrame->client()->forwardPostMessage(
        WebLocalFrameImpl::fromFrame(sourceFrame), m_webFrame,
        WebSecurityOrigin(std::move(target)), WebDOMMessageEvent(event));
}

// FIXME: Remove this code once we have input routing in the browser
// process. See http://crbug.com/339659.
void RemoteFrameClientImpl::forwardInputEvent(Event* event) {
  // It is possible for a platform event to cause the remote iframe element
  // to be hidden, which destroys the layout object (for instance, a mouse
  // event that moves between elements will trigger a mouseout on the old
  // element, which might hide the new element). In that case we do not
  // forward. This is divergent behavior from local frames, where the
  // content of the frame can receive events even after the frame is hidden.
  // We might need to revisit this after browser hit testing is fully
  // implemented, since this code path will need to be removed or refactored
  // anyway.
  // See https://crbug.com/520705.
  if (m_webFrame->toImplBase()->frame()->ownerLayoutItem().isNull())
    return;

  // This is only called when we have out-of-process iframes, which
  // need to forward input events across processes.
  // FIXME: Add a check for out-of-process iframes enabled.
  std::unique_ptr<WebInputEvent> webEvent;
  if (event->isKeyboardEvent())
    webEvent = wrapUnique(
        new WebKeyboardEventBuilder(*static_cast<KeyboardEvent*>(event)));
  else if (event->isMouseEvent())
    webEvent = wrapUnique(new WebMouseEventBuilder(
        m_webFrame->frame()->view(),
        m_webFrame->toImplBase()->frame()->ownerLayoutItem(),
        *static_cast<MouseEvent*>(event)));

  // Other or internal Blink events should not be forwarded.
  if (!webEvent || webEvent->type == WebInputEvent::Undefined)
    return;

  m_webFrame->client()->forwardInputEvent(webEvent.get());
}

void RemoteFrameClientImpl::frameRectsChanged(const IntRect& frameRect) {
  m_webFrame->client()->frameRectsChanged(frameRect);
}

void RemoteFrameClientImpl::advanceFocus(WebFocusType type,
                                         LocalFrame* source) {
  m_webFrame->client()->advanceFocus(type,
                                     WebLocalFrameImpl::fromFrame(source));
}

void RemoteFrameClientImpl::visibilityChanged(bool visible) {
  m_webFrame->client()->visibilityChanged(visible);
}

void RemoteFrameClientImpl::setHasReceivedUserGesture() {
  m_webFrame->client()->setHasReceivedUserGesture();
}

}  // namespace blink
