blob: e37b6aaaf280d6cc8384c77200f55c2b5c2f219a [file] [log] [blame]
// Copyright (c) 2012 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 "content/browser/browser_plugin/browser_plugin_guest.h"
#include <stddef.h>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/metrics/user_metrics.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "content/browser/browser_plugin/browser_plugin_embedder.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/guest_host.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents_observer.h"
#include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
#include "third_party/blink/public/mojom/input/focus_type.mojom.h"
#if defined(OS_MAC)
#include "content/browser/browser_plugin/browser_plugin_popup_menu_helper_mac.h"
#endif
namespace content {
BrowserPluginGuest::BrowserPluginGuest(WebContentsImpl* web_contents,
BrowserPluginGuestDelegate* delegate)
: WebContentsObserver(web_contents),
owner_web_contents_(nullptr),
initialized_(false),
delegate_(delegate) {
DCHECK(web_contents);
DCHECK(delegate);
RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.Create"));
}
void BrowserPluginGuest::WillDestroy() {
// It is important that the WebContents is notified before detaching.
GetWebContents()->BrowserPluginGuestWillDetach();
owner_web_contents_ = nullptr;
}
void BrowserPluginGuest::Init() {
if (initialized_)
return;
initialized_ = true;
WebContentsImpl* owner_web_contents = static_cast<WebContentsImpl*>(
delegate_->GetOwnerWebContents());
owner_web_contents->CreateBrowserPluginEmbedderIfNecessary();
InitInternal(owner_web_contents);
}
void BrowserPluginGuest::SetFocus(bool focused,
blink::mojom::FocusType focus_type) {
RenderWidgetHostView* rwhv = web_contents()->GetRenderWidgetHostView();
RenderWidgetHost* rwh = rwhv ? rwhv->GetRenderWidgetHost() : nullptr;
if (!rwh)
return;
if ((focus_type == blink::mojom::FocusType::kForward) ||
(focus_type == blink::mojom::FocusType::kBackward)) {
static_cast<RenderViewHostImpl*>(RenderViewHost::From(rwh))
->SetInitialFocus(focus_type == blink::mojom::FocusType::kBackward);
}
RenderWidgetHostImpl::From(rwh)->GetWidgetInputHandler()->SetFocus(focused);
// Restore the last seen state of text input to the view.
SendTextInputTypeChangedToView(static_cast<RenderWidgetHostViewBase*>(rwhv));
}
WebContentsImpl* BrowserPluginGuest::CreateNewGuestWindow(
const WebContents::CreateParams& params) {
WebContentsImpl* new_contents =
static_cast<WebContentsImpl*>(delegate_->CreateNewGuestWindow(params));
DCHECK(new_contents);
return new_contents;
}
void BrowserPluginGuest::InitInternal(WebContentsImpl* owner_web_contents) {
SetFocus(false, blink::mojom::FocusType::kNone);
if (owner_web_contents_ != owner_web_contents) {
// Once a BrowserPluginGuest has an embedder WebContents, it's considered to
// be attached.
owner_web_contents_ = owner_web_contents;
}
blink::RendererPreferences* renderer_prefs =
GetWebContents()->GetMutableRendererPrefs();
blink::UserAgentOverride guest_user_agent_override =
renderer_prefs->user_agent_override;
// Copy renderer preferences (and nothing else) from the embedder's
// WebContents to the guest.
//
// For GTK and Aura this is necessary to get proper renderer configuration
// values for caret blinking interval, colors related to selection and
// focus.
*renderer_prefs = *owner_web_contents_->GetMutableRendererPrefs();
renderer_prefs->user_agent_override = std::move(guest_user_agent_override);
// Navigation is disabled in Chrome Apps. We want to make sure guest-initiated
// navigations still continue to function inside the app.
renderer_prefs->browser_handles_all_top_level_requests = false;
DCHECK(GetWebContents()->GetRenderViewHost());
// TODO(chrishtr): this code is wrong. The navigate_on_drag_drop field will
// be reset again the next time preferences are updated.
blink::web_pref::WebPreferences prefs =
GetWebContents()->GetOrCreateWebPreferences();
prefs.navigate_on_drag_drop = false;
GetWebContents()->SetWebPreferences(prefs);
}
BrowserPluginGuest::~BrowserPluginGuest() = default;
// static
void BrowserPluginGuest::CreateInWebContents(
WebContentsImpl* web_contents,
BrowserPluginGuestDelegate* delegate) {
auto guest = base::WrapUnique(new BrowserPluginGuest(web_contents, delegate));
delegate->SetGuestHost(guest.get());
web_contents->SetBrowserPluginGuest(std::move(guest));
}
WebContentsImpl* BrowserPluginGuest::GetWebContents() const {
return static_cast<WebContentsImpl*>(web_contents());
}
void BrowserPluginGuest::SendTextInputTypeChangedToView(
RenderWidgetHostViewBase* guest_rwhv) {
if (!guest_rwhv)
return;
if (!owner_web_contents_) {
// If we were showing an interstitial, then we can end up here during
// embedder shutdown or when the embedder navigates to a different page.
// The call stack is roughly:
// BrowserPluginGuest::SetFocus()
// content::InterstitialPageImpl::Hide()
// content::InterstitialPageImpl::DontProceed().
//
// TODO(lazyboy): Write a WebUI test once http://crbug.com/463674 is fixed.
return;
}
if (last_text_input_state_.get()) {
guest_rwhv->TextInputStateChanged(*last_text_input_state_);
if (auto* rwh = guest_rwhv->host()) {
// We need composition range information for some IMEs. To get the
// updates, we need to explicitly ask the renderer to monitor and send the
// composition information changes. RenderWidgetHostView of the page will
// send the request to its process but the machinery for forwarding it to
// BrowserPlugin is not there. Therefore, we send a direct request to the
// guest process to start monitoring the state (see
// https://crbug.com/714771).
rwh->RequestCompositionUpdates(
false, last_text_input_state_->type != ui::TEXT_INPUT_TYPE_NONE);
}
}
}
void BrowserPluginGuest::DidStartNavigation(
NavigationHandle* navigation_handle) {
// Originally added to suppress the error page when a navigation is blocked
// using the webrequest API in a <webview> guest: https://crbug.com/284741.
//
// TODO(https://crbug.com/1127132): net::ERR_BLOCKED_BY_CLIENT is used for
// many other errors. Figure out what suppression policy is desirable here.
//
// TODO(mcnee): Investigate moving this out to WebViewGuest.
NavigationRequest::From(navigation_handle)
->SetSilentlyIgnoreBlockedByClient();
}
void BrowserPluginGuest::DidFinishNavigation(
NavigationHandle* navigation_handle) {
if (navigation_handle->HasCommitted())
RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.DidNavigate"));
}
void BrowserPluginGuest::RenderProcessGone(base::TerminationStatus status) {
switch (status) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
#endif
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.Killed"));
break;
case base::TERMINATION_STATUS_PROCESS_CRASHED:
RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.Crashed"));
break;
case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
RecordAction(
base::UserMetricsAction("BrowserPlugin.Guest.AbnormalDeath"));
break;
case base::TERMINATION_STATUS_LAUNCH_FAILED:
RecordAction(base::UserMetricsAction("BrowserPlugin.Guest.LaunchFailed"));
break;
default:
break;
}
}
#if defined(OS_MAC)
void BrowserPluginGuest::ShowPopupMenu(
RenderFrameHost* render_frame_host,
mojo::PendingRemote<blink::mojom::PopupMenuClient>* popup_client,
const gfx::Rect& bounds,
int32_t item_height,
double font_size,
int32_t selected_item,
std::vector<blink::mojom::MenuItemPtr>* menu_items,
bool right_aligned,
bool allow_multiple_selection) {
gfx::Rect translated_bounds(bounds);
WebContents* guest = web_contents();
translated_bounds.set_origin(
guest->GetRenderWidgetHostView()->TransformPointToRootCoordSpace(
translated_bounds.origin()));
BrowserPluginPopupMenuHelper popup_menu_helper(
owner_web_contents_->GetMainFrame(), render_frame_host,
std::move(*popup_client));
popup_menu_helper.ShowPopupMenu(translated_bounds, item_height, font_size,
selected_item, std::move(*menu_items),
right_aligned, allow_multiple_selection);
}
#endif
} // namespace content