blob: a8e1730397b748455e21474f0507c812cf6f4e3a [file] [log] [blame]
/*
* Copyright (C) 2011, 2012 Google 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 "third_party/blink/renderer/core/exported/web_view_impl.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "cc/layers/picture_layer.h"
#include "media/base/media_switches.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "third_party/blink/public/common/input/web_menu_source_type.h"
#include "third_party/blink/public/common/page/page_zoom.h"
#include "third_party/blink/public/common/renderer_preferences/renderer_preferences.h"
#include "third_party/blink/public/common/switches.h"
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
#include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h"
#include "third_party/blink/public/platform/interface_registry.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "third_party/blink/public/platform/web_media_player.h"
#include "third_party/blink/public/platform/web_network_state_notifier.h"
#include "third_party/blink/public/platform/web_runtime_features.h"
#include "third_party/blink/public/platform/web_text_input_info.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/public/web/web_autofill_client.h"
#include "third_party/blink/public/web/web_console_message.h"
#include "third_party/blink/public/web/web_element.h"
#include "third_party/blink/public/web/web_frame.h"
#include "third_party/blink/public/web/web_hit_test_result.h"
#include "third_party/blink/public/web/web_input_element.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
#include "third_party/blink/public/web/web_meaningful_layout.h"
#include "third_party/blink/public/web/web_node.h"
#include "third_party/blink/public/web/web_plugin.h"
#include "third_party/blink/public/web/web_range.h"
#include "third_party/blink/public/web/web_render_theme.h"
#include "third_party/blink/public/web/web_view_client.h"
#include "third_party/blink/public/web/web_window_features.h"
#include "third_party/blink/renderer/core/clipboard/data_object.h"
#include "third_party/blink/renderer/core/content_capture/content_capture_manager.h"
#include "third_party/blink/renderer/core/core_initializer.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
#include "third_party/blink/renderer/core/dom/context_features_client_impl.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
#include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
#include "third_party/blink/renderer/core/dom/text.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/editor.h"
#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/ime/input_method_controller.h"
#include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
#include "third_party/blink/renderer/core/editing/selection_template.h"
#include "third_party/blink/renderer/core/editing/serializers/html_interchange.h"
#include "third_party/blink/renderer/core/editing/serializers/serialization.h"
#include "third_party/blink/renderer/core/events/current_input_event.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
#include "third_party/blink/renderer/core/events/ui_event_with_key_state.h"
#include "third_party/blink/renderer/core/events/web_input_event_conversion.h"
#include "third_party/blink/renderer/core/events/wheel_event.h"
#include "third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h"
#include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
#include "third_party/blink/renderer/core/exported/web_settings_impl.h"
#include "third_party/blink/renderer/core/frame/browser_controls.h"
#include "third_party/blink/renderer/core/frame/event_handler_registry.h"
#include "third_party/blink/renderer/core/frame/fullscreen_controller.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_client.h"
#include "third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/page_scale_constraints_set.h"
#include "third_party/blink/renderer/core/frame/remote_frame.h"
#include "third_party/blink/renderer/core/frame/resize_viewport_anchor.h"
#include "third_party/blink/renderer/core/frame/rotation_viewport_anchor.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/viewport_data.h"
#include "third_party/blink/renderer/core/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/frame/web_frame_widget_impl.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/frame/web_remote_frame_impl.h"
#include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
#include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
#include "third_party/blink/renderer/core/html/html_plugin_element.h"
#include "third_party/blink/renderer/core/html/plugin_document.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/input/context_menu_allowed_scope.h"
#include "third_party/blink/renderer/core/input/event_handler.h"
#include "third_party/blink/renderer/core/input/touch_action_util.h"
#include "third_party/blink/renderer/core/inspector/dev_tools_emulator.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/text_autosizer.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/loader/frame_load_request.h"
#include "third_party/blink/renderer/core/loader/frame_loader.h"
#include "third_party/blink/renderer/core/loader/interactive_detector.h"
#include "third_party/blink/renderer/core/loader/no_state_prefetch_client.h"
#include "third_party/blink/renderer/core/page/chrome_client_impl.h"
#include "third_party/blink/renderer/core/page/context_menu_controller.h"
#include "third_party/blink/renderer/core/page/context_menu_provider.h"
#include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/page/frame_tree.h"
#include "third_party/blink/renderer/core/page/link_highlight.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/page/page_popup_client.h"
#include "third_party/blink/renderer/core/page/pointer_lock_controller.h"
#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
#include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
#include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/paint/paint_timing.h"
#include "third_party/blink/renderer/core/paint/paint_timing_detector.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
#include "third_party/blink/renderer/core/timing/dom_window_performance.h"
#include "third_party/blink/renderer/core/timing/window_performance.h"
#include "third_party/blink/renderer/platform/fonts/font_cache.h"
#include "third_party/blink/renderer/platform/graphics/image.h"
#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/keyboard_codes.h"
#include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/public/page_lifecycle_state.h"
#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
#include "third_party/blink/renderer/platform/widget/widget_base.h"
#include "third_party/icu/source/common/unicode/uscript.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/skia_util.h"
#if defined(OS_ANDROID)
#include "components/viz/common/features.h"
#endif
#if !defined(OS_MAC)
#include "skia/ext/legacy_display_globals.h"
#include "third_party/blink/public/platform/web_font_render_style.h"
#include "ui/gfx/font_render_params.h"
#endif
#if defined(OS_WIN)
#include "third_party/blink/public/web/win/web_font_rendering.h"
#endif
// Get rid of WTF's pow define so we can use std::pow.
#undef pow
#include <cmath> // for std::pow
#include "build/chromeos_buildflags.h"
// The following constants control parameters for automated scaling of webpages
// (such as due to a double tap gesture or find in page etc.). These are
// experimentally determined.
static const int touchPointPadding = 32;
static const int nonUserInitiatedPointPadding = 11;
static const float minScaleDifference = 0.01f;
static const float doubleTapZoomContentDefaultMargin = 5;
static const float doubleTapZoomContentMinimumMargin = 2;
static constexpr base::TimeDelta kDoubleTapZoomAnimationDuration =
base::TimeDelta::FromMilliseconds(250);
static const float doubleTapZoomAlreadyLegibleRatio = 1.2f;
static constexpr base::TimeDelta kFindInPageAnimationDuration;
// Constants for viewport anchoring on resize.
static const float viewportAnchorCoordX = 0.5f;
static const float viewportAnchorCoordY = 0;
// Constants for zooming in on a focused text field.
static constexpr base::TimeDelta kScrollAndScaleAnimationDuration =
base::TimeDelta::FromMicroseconds(200);
static const int minReadableCaretHeight = 16;
static const int minReadableCaretHeightForTextArea = 13;
static const float minScaleChangeToTriggerZoom = 1.5f;
static const float leftBoxRatio = 0.3f;
static const int caretPadding = 10;
namespace blink {
using mojom::blink::EffectiveConnectionType;
// Historically, these values came from Webkit in
// WebKitLegacy/mac/WebView/WebView.mm (named MinimumZoomMultiplier and
// MaximumZoomMultiplier there).
const double WebView::kMinTextSizeMultiplier = 0.5;
const double WebView::kMaxTextSizeMultiplier = 3.0;
// static
HashSet<WebViewImpl*>& WebViewImpl::AllInstances() {
DEFINE_STATIC_LOCAL(HashSet<WebViewImpl*>, all_instances, ());
return all_instances;
}
static bool g_should_use_external_popup_menus = false;
void WebView::SetUseExternalPopupMenus(bool use_external_popup_menus) {
g_should_use_external_popup_menus = use_external_popup_menus;
}
bool WebViewImpl::UseExternalPopupMenus() {
return g_should_use_external_popup_menus;
}
namespace {
class EmptyEventListener final : public NativeEventListener {
public:
void Invoke(ExecutionContext* execution_context, Event*) override {}
};
typedef void (*SetFontFamilyWrapper)(blink::WebSettings*,
const base::string16&,
UScriptCode);
void SetStandardFontFamilyWrapper(WebSettings* settings,
const base::string16& font,
UScriptCode script) {
settings->SetStandardFontFamily(WebString::FromUTF16(font), script);
}
void SetFixedFontFamilyWrapper(WebSettings* settings,
const base::string16& font,
UScriptCode script) {
settings->SetFixedFontFamily(WebString::FromUTF16(font), script);
}
void SetSerifFontFamilyWrapper(WebSettings* settings,
const base::string16& font,
UScriptCode script) {
settings->SetSerifFontFamily(WebString::FromUTF16(font), script);
}
void SetSansSerifFontFamilyWrapper(WebSettings* settings,
const base::string16& font,
UScriptCode script) {
settings->SetSansSerifFontFamily(WebString::FromUTF16(font), script);
}
void SetCursiveFontFamilyWrapper(WebSettings* settings,
const base::string16& font,
UScriptCode script) {
settings->SetCursiveFontFamily(WebString::FromUTF16(font), script);
}
void SetFantasyFontFamilyWrapper(WebSettings* settings,
const base::string16& font,
UScriptCode script) {
settings->SetFantasyFontFamily(WebString::FromUTF16(font), script);
}
void SetPictographFontFamilyWrapper(WebSettings* settings,
const base::string16& font,
UScriptCode script) {
settings->SetPictographFontFamily(WebString::FromUTF16(font), script);
}
// If |scriptCode| is a member of a family of "similar" script codes, returns
// the script code in that family that is used by WebKit for font selection
// purposes. For example, USCRIPT_KATAKANA_OR_HIRAGANA and USCRIPT_JAPANESE are
// considered equivalent for the purposes of font selection. WebKit uses the
// script code USCRIPT_KATAKANA_OR_HIRAGANA. So, if |scriptCode| is
// USCRIPT_JAPANESE, the function returns USCRIPT_KATAKANA_OR_HIRAGANA. WebKit
// uses different scripts than the ones in Chrome pref names because the version
// of ICU included on certain ports does not have some of the newer scripts. If
// |scriptCode| is not a member of such a family, returns |scriptCode|.
UScriptCode GetScriptForWebSettings(UScriptCode scriptCode) {
switch (scriptCode) {
case USCRIPT_HIRAGANA:
case USCRIPT_KATAKANA:
case USCRIPT_JAPANESE:
return USCRIPT_KATAKANA_OR_HIRAGANA;
case USCRIPT_KOREAN:
return USCRIPT_HANGUL;
default:
return scriptCode;
}
}
void ApplyFontsFromMap(const web_pref::ScriptFontFamilyMap& map,
SetFontFamilyWrapper setter,
WebSettings* settings) {
for (auto& it : map) {
int32_t script = u_getPropertyValueEnum(UCHAR_SCRIPT, (it.first).c_str());
if (script >= 0 && script < USCRIPT_CODE_LIMIT) {
UScriptCode code = static_cast<UScriptCode>(script);
(*setter)(settings, it.second, GetScriptForWebSettings(code));
}
}
}
void ApplyCommandLineToSettings(WebSettings* settings) {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
WebSettings::SelectionStrategyType selection_strategy;
if (command_line.GetSwitchValueASCII(switches::kTouchTextSelectionStrategy) ==
"direction")
selection_strategy = WebSettings::SelectionStrategyType::kDirection;
else
selection_strategy = WebSettings::SelectionStrategyType::kCharacter;
settings->SetSelectionStrategy(selection_strategy);
WebString passive_listeners_default = WebString::FromUTF8(
command_line.GetSwitchValueASCII(switches::kPassiveListenersDefault));
if (!passive_listeners_default.IsEmpty()) {
WebSettings::PassiveEventListenerDefault passive_default =
WebSettings::PassiveEventListenerDefault::kFalse;
if (passive_listeners_default == "true")
passive_default = WebSettings::PassiveEventListenerDefault::kTrue;
else if (passive_listeners_default == "forcealltrue")
passive_default = WebSettings::PassiveEventListenerDefault::kForceAllTrue;
settings->SetPassiveEventListenerDefault(passive_default);
}
WebString network_quiet_timeout = WebString::FromUTF8(
command_line.GetSwitchValueASCII(switches::kNetworkQuietTimeout));
if (!network_quiet_timeout.IsEmpty()) {
bool ok;
double network_quiet_timeout_seconds =
String(network_quiet_timeout).ToDouble(&ok);
if (ok)
settings->SetNetworkQuietTimeout(network_quiet_timeout_seconds);
}
if (command_line.HasSwitch(switches::kBlinkSettings)) {
Vector<String> blink_settings;
String command_line_settings =
command_line.GetSwitchValueASCII(switches::kBlinkSettings).c_str();
command_line_settings.Split(",", blink_settings);
for (const String& setting : blink_settings) {
wtf_size_t pos = setting.find('=');
settings->SetFromStrings(
WebString(setting.Substring(0, pos)),
WebString(pos == kNotFound ? "" : setting.Substring(pos + 1)));
}
}
}
WebMediaPlayer::SurfaceLayerMode GetVideoSurfaceLayerMode() {
#if defined(OS_ANDROID)
if (base::FeatureList::IsEnabled(media::kDisableSurfaceLayerForVideo) &&
!::features::IsUsingVizForWebView())
return WebMediaPlayer::SurfaceLayerMode::kNever;
#endif // OS_ANDROID
return WebMediaPlayer::SurfaceLayerMode::kAlways;
}
ui::mojom::blink::WindowOpenDisposition NavigationPolicyToDisposition(
NavigationPolicy policy) {
switch (policy) {
case kNavigationPolicyDownload:
return ui::mojom::blink::WindowOpenDisposition::SAVE_TO_DISK;
case kNavigationPolicyCurrentTab:
return ui::mojom::blink::WindowOpenDisposition::CURRENT_TAB;
case kNavigationPolicyNewBackgroundTab:
return ui::mojom::blink::WindowOpenDisposition::NEW_BACKGROUND_TAB;
case kNavigationPolicyNewForegroundTab:
return ui::mojom::blink::WindowOpenDisposition::NEW_FOREGROUND_TAB;
case kNavigationPolicyNewWindow:
return ui::mojom::blink::WindowOpenDisposition::NEW_WINDOW;
case kNavigationPolicyNewPopup:
return ui::mojom::blink::WindowOpenDisposition::NEW_POPUP;
}
NOTREACHED() << "Unexpected NavigationPolicy";
return ui::mojom::blink::WindowOpenDisposition::IGNORE_ACTION;
}
#if !defined(OS_MAC) && !defined(OS_WIN)
SkFontHinting RendererPreferencesToSkiaHinting(
const blink::RendererPreferences& prefs) {
// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
// complete.
#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
if (!prefs.should_antialias_text) {
// When anti-aliasing is off, GTK maps all non-zero hinting settings to
// 'Normal' hinting so we do the same. Otherwise, folks who have 'Slight'
// hinting selected will see readable text in everything expect Chromium.
switch (prefs.hinting) {
case gfx::FontRenderParams::HINTING_NONE:
return SkFontHinting::kNone;
case gfx::FontRenderParams::HINTING_SLIGHT:
case gfx::FontRenderParams::HINTING_MEDIUM:
case gfx::FontRenderParams::HINTING_FULL:
return SkFontHinting::kNormal;
default:
NOTREACHED();
return SkFontHinting::kNormal;
}
}
#endif
switch (prefs.hinting) {
case gfx::FontRenderParams::HINTING_NONE:
return SkFontHinting::kNone;
case gfx::FontRenderParams::HINTING_SLIGHT:
return SkFontHinting::kSlight;
case gfx::FontRenderParams::HINTING_MEDIUM:
return SkFontHinting::kNormal;
case gfx::FontRenderParams::HINTING_FULL:
return SkFontHinting::kFull;
default:
NOTREACHED();
return SkFontHinting::kNormal;
}
}
#endif // OS_MAC
} // namespace
// WebView ----------------------------------------------------------------
WebView* WebView::Create(
WebViewClient* client,
bool is_hidden,
bool is_inside_portal,
bool compositing_enabled,
WebView* opener,
CrossVariantMojoAssociatedReceiver<mojom::PageBroadcastInterfaceBase>
page_handle,
scheduler::WebAgentGroupScheduler& agent_group_scheduler) {
return WebViewImpl::Create(
client,
is_hidden ? mojom::blink::PageVisibilityState::kHidden
: mojom::blink::PageVisibilityState::kVisible,
is_inside_portal, compositing_enabled, static_cast<WebViewImpl*>(opener),
std::move(page_handle), agent_group_scheduler);
}
WebViewImpl* WebViewImpl::Create(
WebViewClient* client,
mojom::blink::PageVisibilityState visibility,
bool is_inside_portal,
bool compositing_enabled,
WebViewImpl* opener,
mojo::PendingAssociatedReceiver<mojom::blink::PageBroadcast> page_handle,
blink::scheduler::WebAgentGroupScheduler& agent_group_scheduler) {
// Take a self-reference for WebViewImpl that is released by calling Close(),
// then return a raw pointer to the caller.
auto web_view = base::AdoptRef(
new WebViewImpl(client, visibility, is_inside_portal, compositing_enabled,
opener, std::move(page_handle), agent_group_scheduler));
web_view->AddRef();
return web_view.get();
}
void WebView::UpdateVisitedLinkState(uint64_t link_hash) {
Page::VisitedStateChanged(link_hash);
}
void WebView::ResetVisitedLinkState(bool invalidate_visited_link_hashes) {
Page::AllVisitedStateChanged(invalidate_visited_link_hashes);
}
void WebViewImpl::SetNoStatePrefetchClient(
WebNoStatePrefetchClient* no_state_prefetch_client) {
DCHECK(page_);
ProvideNoStatePrefetchClientTo(*page_,
MakeGarbageCollected<NoStatePrefetchClient>(
*page_, no_state_prefetch_client));
}
void WebViewImpl::CloseWindowSoon() {
// Ask the RenderViewHost with a local main frame to initiate close. We
// could be called from deep in Javascript. If we ask the RenderViewHost to
// close now, the window could be closed before the JS finishes executing,
// thanks to nested message loops running and handling the resulting
// DestroyView IPC. So instead, post a message back to the message loop, which
// won't run until the JS is complete, and then the
// RouteCloseEvent/RequestClose request can be sent.
GetPage()
->GetPageScheduler()
->GetAgentGroupScheduler()
.DefaultTaskRunner()
->PostTask(FROM_HERE, WTF::Bind(&WebViewImpl::DoDeferredCloseWindowSoon,
weak_ptr_factory_.GetWeakPtr()));
}
void WebViewImpl::DoDeferredCloseWindowSoon() {
// Have the browser process a close request. We should have either a
// |local_main_frame_host_remote_| or |remote_main_frame_host_remote_|.
// This method will not execute if Close has been called as WeakPtrs
// will be invalidated in Close.
if (GetPage()->MainFrame()->IsLocalFrame()) {
DCHECK(local_main_frame_host_remote_);
local_main_frame_host_remote_->RequestClose();
} else {
DCHECK(remote_main_frame_host_remote_);
remote_main_frame_host_remote_->RouteCloseEvent();
}
}
WebViewImpl::WebViewImpl(
WebViewClient* client,
mojom::blink::PageVisibilityState visibility,
bool is_inside_portal,
bool does_composite,
WebViewImpl* opener,
mojo::PendingAssociatedReceiver<mojom::blink::PageBroadcast> page_handle,
blink::scheduler::WebAgentGroupScheduler& agent_group_scheduler)
: web_view_client_(client),
chrome_client_(MakeGarbageCollected<ChromeClientImpl>(this)),
minimum_zoom_level_(PageZoomFactorToZoomLevel(kMinimumPageZoomFactor)),
maximum_zoom_level_(PageZoomFactorToZoomLevel(kMaximumPageZoomFactor)),
does_composite_(does_composite),
fullscreen_controller_(std::make_unique<FullscreenController>(this)),
receiver_(this,
std::move(page_handle),
agent_group_scheduler.DefaultTaskRunner()) {
if (!web_view_client_)
DCHECK(!does_composite_);
Page::PageClients page_clients;
page_clients.chrome_client = chrome_client_.Get();
page_ =
Page::CreateOrdinary(page_clients, opener ? opener->GetPage() : nullptr,
agent_group_scheduler);
CoreInitializer::GetInstance().ProvideModulesToPage(*page_, web_view_client_);
SetVisibilityState(visibility, /*is_initial_state=*/true);
// We pass this state to Page, but it's only used by the main frame in the
// page.
SetInsidePortal(is_inside_portal);
// When not compositing, keep the Page in the loop so that it will paint all
// content into the root layer, as multiple layers can only be used when
// compositing them together later.
if (does_composite_)
page_->GetSettings().SetAcceleratedCompositingEnabled(true);
dev_tools_emulator_ = MakeGarbageCollected<DevToolsEmulator>(this);
AllInstances().insert(this);
resize_viewport_anchor_ = MakeGarbageCollected<ResizeViewportAnchor>(*page_);
}
WebViewImpl::~WebViewImpl() {
DCHECK(!page_);
}
WebDevToolsAgentImpl* WebViewImpl::MainFrameDevToolsAgentImpl() {
WebLocalFrameImpl* main_frame = MainFrameImpl();
return main_frame ? main_frame->DevToolsAgentImpl() : nullptr;
}
void WebViewImpl::SetTabKeyCyclesThroughElements(bool value) {
if (page_)
page_->SetTabKeyCyclesThroughElements(value);
}
bool WebViewImpl::StartPageScaleAnimation(const IntPoint& target_position,
bool use_anchor,
float new_scale,
base::TimeDelta duration) {
// PageScaleFactor is a property of the main frame only, and only exists when
// compositing.
DCHECK(MainFrameImpl());
DCHECK(does_composite_);
VisualViewport& visual_viewport = GetPage()->GetVisualViewport();
gfx::Point clamped_point = target_position;
if (!use_anchor) {
clamped_point =
visual_viewport.ClampDocumentOffsetAtScale(target_position, new_scale);
if (duration.is_zero()) {
SetPageScaleFactor(new_scale);
LocalFrameView* view = MainFrameImpl()->GetFrameView();
if (view && view->GetScrollableArea()) {
view->GetScrollableArea()->SetScrollOffset(
ScrollOffset(clamped_point.x(), clamped_point.y()),
mojom::blink::ScrollType::kProgrammatic);
}
return false;
}
}
if (use_anchor && new_scale == PageScaleFactor())
return false;
if (enable_fake_page_scale_animation_for_testing_) {
fake_page_scale_animation_target_position_ = target_position;
fake_page_scale_animation_use_anchor_ = use_anchor;
fake_page_scale_animation_page_scale_factor_ = new_scale;
} else {
MainFrameImpl()->FrameWidgetImpl()->StartPageScaleAnimation(
static_cast<gfx::Vector2d>(target_position), use_anchor, new_scale,
duration);
}
return true;
}
void WebViewImpl::EnableFakePageScaleAnimationForTesting(bool enable) {
enable_fake_page_scale_animation_for_testing_ = enable;
fake_page_scale_animation_target_position_ = IntPoint();
fake_page_scale_animation_use_anchor_ = false;
fake_page_scale_animation_page_scale_factor_ = 0;
}
void WebViewImpl::AcceptLanguagesChanged() {
FontCache::AcceptLanguagesChanged(
String::FromUTF8(renderer_preferences_.accept_languages));
if (!GetPage())
return;
GetPage()->AcceptLanguagesChanged();
}
gfx::Rect WebViewImpl::WidenRectWithinPageBounds(const gfx::Rect& source,
int target_margin,
int minimum_margin) {
// Caller should guarantee that the main frame exists and is local.
DCHECK(MainFrame());
DCHECK(MainFrame()->IsWebLocalFrame());
WebSize max_size = MainFrame()->ToWebLocalFrame()->DocumentSize();
IntSize scroll_offset = MainFrame()->ToWebLocalFrame()->GetScrollOffset();
int left_margin = target_margin;
int right_margin = target_margin;
const int absolute_source_x = source.x() + scroll_offset.Width();
if (left_margin > absolute_source_x) {
left_margin = absolute_source_x;
right_margin = std::max(left_margin, minimum_margin);
}
const int maximum_right_margin =
max_size.width - (source.width() + absolute_source_x);
if (right_margin > maximum_right_margin) {
right_margin = maximum_right_margin;
left_margin = std::min(left_margin, std::max(right_margin, minimum_margin));
}
const int new_width = source.width() + left_margin + right_margin;
const int new_x = source.x() - left_margin;
DCHECK_GE(new_width, 0);
DCHECK_LE(scroll_offset.Width() + new_x + new_width, max_size.width);
return gfx::Rect(new_x, source.y(), new_width, source.height());
}
float WebViewImpl::MaximumLegiblePageScale() const {
// Pages should be as legible as on desktop when at dpi scale, so no
// need to zoom in further when automatically determining zoom level
// (after double tap, find in page, etc), though the user should still
// be allowed to manually pinch zoom in further if they desire.
if (GetPage()) {
return maximum_legible_scale_ *
GetPage()->GetSettings().GetAccessibilityFontScaleFactor();
}
return maximum_legible_scale_;
}
void WebViewImpl::ComputeScaleAndScrollForBlockRect(
const gfx::Point& hit_point_in_root_frame,
const gfx::Rect& block_rect_in_root_frame,
float padding,
float default_scale_when_already_legible,
float& scale,
IntPoint& scroll) {
scale = PageScaleFactor();
scroll = IntPoint();
gfx::Rect rect = block_rect_in_root_frame;
if (!rect.IsEmpty()) {
float default_margin = doubleTapZoomContentDefaultMargin;
float minimum_margin = doubleTapZoomContentMinimumMargin;
// We want the margins to have the same physical size, which means we
// need to express them in post-scale size. To do that we'd need to know
// the scale we're scaling to, but that depends on the margins. Instead
// we express them as a fraction of the target rectangle: this will be
// correct if we end up fully zooming to it, and won't matter if we
// don't.
rect = WidenRectWithinPageBounds(
rect, static_cast<int>(default_margin * rect.width() / size_.width()),
static_cast<int>(minimum_margin * rect.width() / size_.width()));
// Fit block to screen, respecting limits.
scale = static_cast<float>(size_.width()) / rect.width();
scale = std::min(scale, MaximumLegiblePageScale());
if (PageScaleFactor() < default_scale_when_already_legible)
scale = std::max(scale, default_scale_when_already_legible);
scale = ClampPageScaleFactorToLimits(scale);
}
// FIXME: If this is being called for auto zoom during find in page,
// then if the user manually zooms in it'd be nice to preserve the
// relative increase in zoom they caused (if they zoom out then it's ok
// to zoom them back in again). This isn't compatible with our current
// double-tap zoom strategy (fitting the containing block to the screen)
// though.
float screen_width = size_.width() / scale;
float screen_height = size_.height() / scale;
// Scroll to vertically align the block.
if (rect.height() < screen_height) {
// Vertically center short blocks.
rect.Offset(0, -0.5 * (screen_height - rect.height()));
} else {
// Ensure position we're zooming to (+ padding) isn't off the bottom of
// the screen.
rect.set_y(std::max<float>(
rect.y(), hit_point_in_root_frame.y() + padding - screen_height));
} // Otherwise top align the block.
// Do the same thing for horizontal alignment.
if (rect.width() < screen_width) {
rect.Offset(-0.5 * (screen_width - rect.width()), 0);
} else {
rect.set_x(std::max<float>(
rect.x(), hit_point_in_root_frame.x() + padding - screen_width));
}
scroll.SetX(rect.x());
scroll.SetY(rect.y());
scale = ClampPageScaleFactorToLimits(scale);
scroll = MainFrameImpl()->GetFrameView()->RootFrameToDocument(scroll);
scroll =
GetPage()->GetVisualViewport().ClampDocumentOffsetAtScale(scroll, scale);
}
static Node* FindCursorDefiningAncestor(Node* node, LocalFrame* frame) {
// Go up the tree to find the node that defines a mouse cursor style
while (node) {
if (node->GetLayoutObject()) {
ECursor cursor = node->GetLayoutObject()->Style()->Cursor();
if (cursor != ECursor::kAuto ||
frame->GetEventHandler().UseHandCursor(node, node->IsLink()))
break;
}
node = LayoutTreeBuilderTraversal::Parent(*node);
}
return node;
}
static bool ShowsHandCursor(Node* node, LocalFrame* frame) {
if (!node || !node->GetLayoutObject())
return false;
ECursor cursor = node->GetLayoutObject()->Style()->Cursor();
return cursor == ECursor::kPointer ||
(cursor == ECursor::kAuto &&
frame->GetEventHandler().UseHandCursor(node, node->IsLink()));
}
// This is for tap (link) highlight and is tested in
// link_highlight_impl_test.cc.
Node* WebViewImpl::BestTapNode(
const GestureEventWithHitTestResults& targeted_tap_event) {
TRACE_EVENT0("input", "WebViewImpl::bestTapNode");
Page* page = page_.Get();
if (!page || !page->MainFrame())
return nullptr;
Node* best_touch_node = targeted_tap_event.GetHitTestResult().InnerNode();
if (!best_touch_node)
return nullptr;
// We might hit something like an image map that has no layoutObject on it
// Walk up the tree until we have a node with an attached layoutObject
while (!best_touch_node->GetLayoutObject()) {
best_touch_node = LayoutTreeBuilderTraversal::Parent(*best_touch_node);
if (!best_touch_node)
return nullptr;
}
// Editable nodes should not be highlighted (e.g., <input>)
if (HasEditableStyle(*best_touch_node))
return nullptr;
Node* cursor_defining_ancestor = FindCursorDefiningAncestor(
best_touch_node, page->DeprecatedLocalMainFrame());
// We show a highlight on tap only when the current node shows a hand cursor
if (!cursor_defining_ancestor ||
!ShowsHandCursor(cursor_defining_ancestor,
page->DeprecatedLocalMainFrame())) {
return nullptr;
}
// We should pick the largest enclosing node with hand cursor set. We do this
// by first jumping up to cursorDefiningAncestor (which is already known to
// have hand cursor set). Then we locate the next cursor-defining ancestor up
// in the the tree and repeat the jumps as long as the node has hand cursor
// set.
do {
best_touch_node = cursor_defining_ancestor;
cursor_defining_ancestor = FindCursorDefiningAncestor(
LayoutTreeBuilderTraversal::Parent(*best_touch_node),
page->DeprecatedLocalMainFrame());
} while (cursor_defining_ancestor &&
ShowsHandCursor(cursor_defining_ancestor,
page->DeprecatedLocalMainFrame()));
// This happens in cases like:
// <div style="display: contents; cursor: pointer">Text</div>.
// The text node inherits cursor: pointer and the div doesn't have a
// LayoutObject, so |best_touch_node| is the text node here. We should not
// return the text node because it can't have touch actions.
if (best_touch_node->IsTextNode())
return nullptr;
return best_touch_node;
}
void WebViewImpl::EnableTapHighlightAtPoint(
const GestureEventWithHitTestResults& targeted_tap_event) {
DCHECK(MainFrameImpl());
Node* touch_node = BestTapNode(targeted_tap_event);
GetPage()->GetLinkHighlight().SetTapHighlight(touch_node);
MainFrameWidget()->UpdateLifecycle(WebLifecycleUpdate::kAll,
DocumentUpdateReason::kTapHighlight);
}
void WebViewImpl::AnimateDoubleTapZoom(const gfx::Point& point_in_root_frame,
const gfx::Rect& rect_to_zoom) {
DCHECK(MainFrameImpl());
float scale;
IntPoint scroll;
ComputeScaleAndScrollForBlockRect(
point_in_root_frame, rect_to_zoom, touchPointPadding,
MinimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio, scale,
scroll);
bool still_at_previous_double_tap_scale =
(PageScaleFactor() == double_tap_zoom_page_scale_factor_ &&
double_tap_zoom_page_scale_factor_ != MinimumPageScaleFactor()) ||
double_tap_zoom_pending_;
bool scale_unchanged = fabs(PageScaleFactor() - scale) < minScaleDifference;
bool should_zoom_out = rect_to_zoom.IsEmpty() || scale_unchanged ||
still_at_previous_double_tap_scale;
bool is_animating;
if (should_zoom_out) {
scale = MinimumPageScaleFactor();
IntPoint target_position =
MainFrameImpl()->GetFrameView()->RootFrameToDocument(
IntPoint(point_in_root_frame.x(), point_in_root_frame.y()));
is_animating = StartPageScaleAnimation(target_position, true, scale,
kDoubleTapZoomAnimationDuration);
} else {
is_animating = StartPageScaleAnimation(scroll, false, scale,
kDoubleTapZoomAnimationDuration);
}
// TODO(dglazkov): The only reason why we're using isAnimating and not just
// checking for layer_tree_view_->HasPendingPageScaleAnimation() is because of
// fake page scale animation plumbing for testing, which doesn't actually
// initiate a page scale animation.
if (is_animating) {
double_tap_zoom_page_scale_factor_ = scale;
double_tap_zoom_pending_ = true;
}
}
void WebViewImpl::ZoomToFindInPageRect(const gfx::Rect& rect_in_root_frame) {
DCHECK(MainFrameImpl());
gfx::Rect block_bounds =
MainFrameImpl()->FrameWidgetImpl()->ComputeBlockBound(
gfx::Point(rect_in_root_frame.x() + rect_in_root_frame.width() / 2,
rect_in_root_frame.y() + rect_in_root_frame.height() / 2),
true);
if (block_bounds.IsEmpty()) {
// Keep current scale (no need to scroll as x,y will normally already
// be visible). FIXME: Revisit this if it isn't always true.
return;
}
float scale;
IntPoint scroll;
ComputeScaleAndScrollForBlockRect(rect_in_root_frame.origin(), block_bounds,
nonUserInitiatedPointPadding,
MinimumPageScaleFactor(), scale, scroll);
StartPageScaleAnimation(scroll, false, scale, kFindInPageAnimationDuration);
}
#if !defined(OS_MAC)
// Mac has no way to open a context menu based on a keyboard event.
WebInputEventResult WebViewImpl::SendContextMenuEvent() {
// The contextMenuController() holds onto the last context menu that was
// popped up on the page until a new one is created. We need to clear
// this menu before propagating the event through the DOM so that we can
// detect if we create a new menu for this event, since we won't create
// a new menu if the DOM swallows the event and the defaultEventHandler does
// not run.
GetPage()->GetContextMenuController().ClearContextMenu();
{
ContextMenuAllowedScope scope;
Frame* focused_frame = GetPage()->GetFocusController().FocusedOrMainFrame();
auto* focused_local_frame = DynamicTo<LocalFrame>(focused_frame);
if (!focused_local_frame)
return WebInputEventResult::kNotHandled;
// Firefox reveal focus based on "keydown" event but not "contextmenu"
// event, we match FF.
if (Element* focused_element =
focused_local_frame->GetDocument()->FocusedElement())
focused_element->scrollIntoViewIfNeeded();
return focused_local_frame->GetEventHandler().ShowNonLocatedContextMenu(
nullptr, kMenuSourceKeyboard);
}
}
#else
WebInputEventResult WebViewImpl::SendContextMenuEvent() {
return WebInputEventResult::kNotHandled;
}
#endif
WebPagePopupImpl* WebViewImpl::OpenPagePopup(PagePopupClient* client) {
DCHECK(client);
// This guarantees there is never more than 1 PagePopup active at a time.
CancelPagePopup();
DCHECK(!page_popup_);
WebLocalFrameImpl* frame = WebLocalFrameImpl::FromFrame(
client->OwnerElement().GetDocument().GetFrame()->LocalFrameRoot());
WebPagePopup* popup_widget = web_view_client_->CreatePopup(frame);
// CreatePopup returns nullptr if this renderer process is about to die.
if (!popup_widget)
return nullptr;
page_popup_ = To<WebPagePopupImpl>(popup_widget);
page_popup_->Initialize(this, client);
EnablePopupMouseWheelEventListener(frame);
return page_popup_.get();
}
void WebViewImpl::CancelPagePopup() {
if (page_popup_)
page_popup_->Cancel();
}
void WebViewImpl::ClosePagePopup(PagePopup* popup) {
DCHECK(popup);
auto* popup_impl = To<WebPagePopupImpl>(popup);
DCHECK_EQ(page_popup_.get(), popup_impl);
if (page_popup_.get() != popup_impl)
return;
page_popup_->ClosePopup();
}
void WebViewImpl::CleanupPagePopup() {
page_popup_ = nullptr;
DisablePopupMouseWheelEventListener();
}
void WebViewImpl::UpdatePagePopup() {
if (page_popup_)
page_popup_->Update();
}
void WebViewImpl::EnablePopupMouseWheelEventListener(
WebLocalFrameImpl* local_root) {
DCHECK(!popup_mouse_wheel_event_listener_);
Document* document = local_root->GetDocument();
DCHECK(document);
// We register an empty event listener, EmptyEventListener, so that mouse
// wheel events get sent to the WebView.
popup_mouse_wheel_event_listener_ =
MakeGarbageCollected<EmptyEventListener>();
document->addEventListener(event_type_names::kMousewheel,
popup_mouse_wheel_event_listener_, false);
local_root_with_empty_mouse_wheel_listener_ = local_root;
}
void WebViewImpl::DisablePopupMouseWheelEventListener() {
// TODO(kenrb): Concerns the same as in enablePopupMouseWheelEventListener.
// See https://crbug.com/566130
DCHECK(popup_mouse_wheel_event_listener_);
Document* document =
local_root_with_empty_mouse_wheel_listener_->GetDocument();
DCHECK(document);
// Document may have already removed the event listener, for instance, due
// to a navigation, but remove it anyway.
document->removeEventListener(event_type_names::kMousewheel,
popup_mouse_wheel_event_listener_.Release(),
false);
local_root_with_empty_mouse_wheel_listener_ = nullptr;
}
LocalDOMWindow* WebViewImpl::PagePopupWindow() const {
return page_popup_ ? page_popup_->Window() : nullptr;
}
Frame* WebViewImpl::FocusedCoreFrame() const {
Page* page = page_.Get();
return page ? page->GetFocusController().FocusedOrMainFrame() : nullptr;
}
// WebWidget ------------------------------------------------------------------
void WebViewImpl::Close() {
// Closership is a single relationship, so only 1 call to Close() should
// occur.
CHECK(page_);
DCHECK(AllInstances().Contains(this));
AllInstances().erase(this);
// Invalidate any weak ptrs as we are starting to shutdown.
weak_ptr_factory_.InvalidateWeakPtrs();
// Initiate shutdown for the entire frameset. This will cause a lot of
// notifications to be sent. This will detach all frames in this WebView's
// frame tree.
page_->WillBeDestroyed();
page_.Clear();
// Reset the delegate to prevent notifications being sent as we're being
// deleted.
web_view_client_ = nullptr;
for (auto& observer : observers_)
observer.WebViewDestroyed();
Release(); // Balances a reference acquired in WebView::Create
}
gfx::Size WebViewImpl::Size() {
return size_;
}
void WebViewImpl::ResizeVisualViewport(const gfx::Size& new_size) {
GetPage()->GetVisualViewport().SetSize(WebSize(new_size));
GetPage()->GetVisualViewport().ClampToBoundaries();
}
void WebViewImpl::UpdateICBAndResizeViewport(
const IntSize& visible_viewport_size) {
// We'll keep the initial containing block size from changing when the top
// controls hide so that the ICB will always be the same size as the
// viewport with the browser controls shown.
IntSize icb_size = IntSize(size_);
if (GetBrowserControls().PermittedState() ==
cc::BrowserControlsState::kBoth &&
!GetBrowserControls().ShrinkViewport()) {
icb_size.Expand(0, -(GetBrowserControls().TotalHeight() -
GetBrowserControls().TotalMinHeight()));
}
GetPageScaleConstraintsSet().DidChangeInitialContainingBlockSize(icb_size);
UpdatePageDefinedViewportConstraints(MainFrameImpl()
->GetFrame()
->GetDocument()
->GetViewportData()
.GetViewportDescription());
UpdateMainFrameLayoutSize();
GetPage()->GetVisualViewport().SetSize(visible_viewport_size);
if (MainFrameImpl()->GetFrameView()) {
MainFrameImpl()->GetFrameView()->SetInitialViewportSize(icb_size);
if (!MainFrameImpl()->GetFrameView()->NeedsLayout())
resize_viewport_anchor_->ResizeFrameView(MainFrameSize());
}
// The boundaries are not properly established until after the frame view is
// also resized, as demonstrated by
// VisualViewportTest.TestBrowserControlsAdjustmentAndResize.
GetPage()->GetVisualViewport().ClampToBoundaries();
}
void WebViewImpl::UpdateBrowserControlsConstraint(
cc::BrowserControlsState constraint) {
cc::BrowserControlsState old_permitted_state =
GetBrowserControls().PermittedState();
GetBrowserControls().UpdateConstraintsAndState(
constraint, cc::BrowserControlsState::kBoth);
// If the controls are going from a locked hidden to unlocked state, or vice
// versa, the ICB size needs to change but we can't rely on getting a
// WebViewImpl::resize since the top controls shown state may not have
// changed.
if ((old_permitted_state == cc::BrowserControlsState::kHidden &&
constraint == cc::BrowserControlsState::kBoth) ||
(old_permitted_state == cc::BrowserControlsState::kBoth &&
constraint == cc::BrowserControlsState::kHidden)) {
UpdateICBAndResizeViewport(GetPage()->GetVisualViewport().Size());
}
}
void WebViewImpl::DidUpdateBrowserControls() {
// BrowserControls are a feature whereby the browser can introduce an
// interactable element [e.g. search box] that grows/shrinks in height as the
// user scrolls the web contents.
//
// This method is called by the BrowserControls class to let the compositor
// know that the browser controls have been updated. This is only relevant if
// the main frame is local because BrowserControls only affects the main
// frame's viewport, and are only affected by main frame scrolling.
//
// The relevant state is stored on the BrowserControls object even if the main
// frame is remote. If the main frame becomes local, the state will be
// restored by the first commit, since the state is checked in every call to
// ApplyScrollAndScale().
WebLocalFrameImpl* main_frame = MainFrameImpl();
if (!main_frame)
return;
WebFrameWidgetImpl* widget = main_frame->LocalRootFrameWidget();
widget->SetBrowserControlsShownRatio(GetBrowserControls().TopShownRatio(),
GetBrowserControls().BottomShownRatio());
widget->SetBrowserControlsParams(GetBrowserControls().Params());
VisualViewport& visual_viewport = GetPage()->GetVisualViewport();
{
// This object will save the current visual viewport offset w.r.t. the
// document and restore it when the object goes out of scope. It's
// needed since the browser controls adjustment will change the maximum
// scroll offset and we may need to reposition them to keep the user's
// apparent position unchanged.
ResizeViewportAnchor::ResizeScope resize_scope(*resize_viewport_anchor_);
visual_viewport.SetBrowserControlsAdjustment(
GetBrowserControls().UnreportedSizeAdjustment());
}
}
BrowserControls& WebViewImpl::GetBrowserControls() {
return GetPage()->GetBrowserControls();
}
void WebViewImpl::ResizeViewWhileAnchored(
cc::BrowserControlsParams params,
const IntSize& visible_viewport_size) {
DCHECK(MainFrameImpl());
GetBrowserControls().SetParams(params);
{
// Avoids unnecessary invalidations while various bits of state in
// TextAutosizer are updated.
TextAutosizer::DeferUpdatePageInfo defer_update_page_info(GetPage());
LocalFrameView* frame_view = MainFrameImpl()->GetFrameView();
IntSize old_size = frame_view->Size();
UpdateICBAndResizeViewport(visible_viewport_size);
IntSize new_size = frame_view->Size();
frame_view->MarkViewportConstrainedObjectsForLayout(
old_size.Width() != new_size.Width(),
old_size.Height() != new_size.Height());
}
fullscreen_controller_->UpdateSize();
// Update lifecycle phases immediately to recalculate the minimum scale limit
// for rotation anchoring, and to make sure that no lifecycle states are
// stale if this WebView is embedded in another one.
MainFrameWidget()->UpdateLifecycle(WebLifecycleUpdate::kAll,
DocumentUpdateReason::kSizeChange);
}
void WebViewImpl::ResizeWithBrowserControls(
const gfx::Size& new_size,
float top_controls_height,
float bottom_controls_height,
bool browser_controls_shrink_layout) {
ResizeWithBrowserControls(
new_size, new_size,
{top_controls_height, GetBrowserControls().TopMinHeight(),
bottom_controls_height, GetBrowserControls().BottomMinHeight(),
GetBrowserControls().AnimateHeightChanges(),
browser_controls_shrink_layout});
}
void WebViewImpl::ResizeWithBrowserControls(
const gfx::Size& main_frame_widget_size,
const gfx::Size& visible_viewport_size,
cc::BrowserControlsParams browser_controls_params) {
if (should_auto_resize_) {
// When auto-resizing only the viewport size comes from the browser, while
// the widget size is determined in the renderer.
ResizeVisualViewport(visible_viewport_size);
return;
}
if (size_ == main_frame_widget_size &&
GetPage()->GetVisualViewport().Size() == IntSize(visible_viewport_size) &&
GetBrowserControls().Params() == browser_controls_params)
return;
if (GetPage()->MainFrame() && !GetPage()->MainFrame()->IsLocalFrame()) {
// Viewport resize for a remote main frame does not require any
// particular action, but the state needs to reflect the correct size
// so that it can be used for initialization if the main frame gets
// swapped to a LocalFrame at a later time.
size_ = main_frame_widget_size;
GetPageScaleConstraintsSet().DidChangeInitialContainingBlockSize(
IntSize(size_));
GetPage()->GetVisualViewport().SetSize(IntSize(size_));
GetPage()->GetBrowserControls().SetParams(browser_controls_params);
return;
}
WebLocalFrameImpl* main_frame = MainFrameImpl();
if (!main_frame)
return;
LocalFrameView* view = main_frame->GetFrameView();
if (!view)
return;
VisualViewport& visual_viewport = GetPage()->GetVisualViewport();
bool is_rotation =
GetPage()->GetSettings().GetMainFrameResizesAreOrientationChanges() &&
size_.width() && ContentsSize().Width() &&
main_frame_widget_size.width() != size_.width() &&
!fullscreen_controller_->IsFullscreenOrTransitioning();
size_ = main_frame_widget_size;
FloatSize viewport_anchor_coords(viewportAnchorCoordX, viewportAnchorCoordY);
if (is_rotation) {
RotationViewportAnchor anchor(*view, visual_viewport,
viewport_anchor_coords,
GetPageScaleConstraintsSet());
ResizeViewWhileAnchored(browser_controls_params,
IntSize(visible_viewport_size));
} else {
ResizeViewportAnchor::ResizeScope resize_scope(*resize_viewport_anchor_);
ResizeViewWhileAnchored(browser_controls_params,
IntSize(visible_viewport_size));
}
SendResizeEventForMainFrame();
}
void WebViewImpl::Resize(const gfx::Size& new_size) {
if (should_auto_resize_ || size_ == new_size)
return;
ResizeWithBrowserControls(new_size, GetBrowserControls().TopHeight(),
GetBrowserControls().BottomHeight(),
GetBrowserControls().ShrinkViewport());
}
void WebViewImpl::SetScreenOrientationOverrideForTesting(
base::Optional<blink::mojom::ScreenOrientation> orientation) {
screen_orientation_override_ = orientation;
// Since we updated the override value, notify all widgets.
for (WebFrame* frame = MainFrame(); frame; frame = frame->TraverseNext()) {
if (frame->IsWebLocalFrame()) {
if (WebFrameWidgetImpl* widget = static_cast<WebFrameWidgetImpl*>(
frame->ToWebLocalFrame()->FrameWidget()))
widget->UpdateScreenInfo(widget->GetScreenInfo());
}
}
}
void WebViewImpl::UseSynchronousResizeModeForTesting(bool enable) {
web_widget_->UseSynchronousResizeModeForTesting(enable);
}
void WebViewImpl::SetWindowRectSynchronouslyForTesting(
const gfx::Rect& new_window_rect) {
web_widget_->SetWindowRectSynchronouslyForTesting(new_window_rect);
}
base::Optional<mojom::blink::ScreenOrientation>
WebViewImpl::ScreenOrientationOverride() {
return screen_orientation_override_;
}
void WebViewImpl::DidEnterFullscreen() {
fullscreen_controller_->DidEnterFullscreen();
}
void WebViewImpl::DidExitFullscreen() {
fullscreen_controller_->DidExitFullscreen();
}
void WebViewImpl::SetMainFrameViewWidget(WebFrameWidgetImpl* widget) {
DCHECK(!widget || widget->ForMainFrame());
web_widget_ = widget;
}
void WebViewImpl::SetMouseOverURL(const KURL& url) {
mouse_over_url_ = url;
UpdateTargetURL(mouse_over_url_, focus_url_);
}
void WebViewImpl::SetKeyboardFocusURL(const KURL& url) {
focus_url_ = url;
UpdateTargetURL(focus_url_, mouse_over_url_);
}
WebFrameWidgetImpl* WebViewImpl::MainFrameViewWidget() {
return web_widget_;
}
void WebViewImpl::PaintContent(cc::PaintCanvas* canvas, const gfx::Rect& rect) {
// This should only be used when compositing is not being used for this
// WebView, and it is painting into the recording of its parent.
DCHECK(!does_composite_);
// Non-composited WebViews always have a local main frame.
DCHECK(MainFrameImpl());
if (rect.IsEmpty())
return;
LocalFrameView& main_view = *MainFrameImpl()->GetFrame()->View();
DCHECK(main_view.GetLayoutView()->GetDocument().Lifecycle().GetState() ==
DocumentLifecycle::kPaintClean);
PaintRecordBuilder builder;
main_view.PaintOutsideOfLifecycle(builder.Context(), kGlobalPaintNormalPhase,
CullRect(IntRect(rect)));
// Don't bother to save/restore here as the caller is expecting the canvas
// to be modified and take care of it.
canvas->clipRect(gfx::RectToSkRect(rect));
builder.EndRecording(*canvas, main_view.GetLayoutView()
->FirstFragment()
.LocalBorderBoxProperties()
.Unalias());
}
// static
void WebView::ApplyWebPreferences(const web_pref::WebPreferences& prefs,
WebView* web_view) {
WebViewImpl* web_view_impl = static_cast<WebViewImpl*>(web_view);
WebSettings* settings = web_view->GetSettings();
ApplyFontsFromMap(prefs.standard_font_family_map,
SetStandardFontFamilyWrapper, settings);
ApplyFontsFromMap(prefs.fixed_font_family_map, SetFixedFontFamilyWrapper,
settings);
ApplyFontsFromMap(prefs.serif_font_family_map, SetSerifFontFamilyWrapper,
settings);
ApplyFontsFromMap(prefs.sans_serif_font_family_map,
SetSansSerifFontFamilyWrapper, settings);
ApplyFontsFromMap(prefs.cursive_font_family_map, SetCursiveFontFamilyWrapper,
settings);
ApplyFontsFromMap(prefs.fantasy_font_family_map, SetFantasyFontFamilyWrapper,
settings);
ApplyFontsFromMap(prefs.pictograph_font_family_map,
SetPictographFontFamilyWrapper, settings);
settings->SetDefaultFontSize(prefs.default_font_size);
settings->SetDefaultFixedFontSize(prefs.default_fixed_font_size);
settings->SetMinimumFontSize(prefs.minimum_font_size);
settings->SetMinimumLogicalFontSize(prefs.minimum_logical_font_size);
settings->SetDefaultTextEncodingName(
WebString::FromASCII(prefs.default_encoding));
settings->SetJavaScriptEnabled(prefs.javascript_enabled);
settings->SetWebSecurityEnabled(prefs.web_security_enabled);
settings->SetLoadsImagesAutomatically(prefs.loads_images_automatically);
settings->SetImagesEnabled(prefs.images_enabled);
settings->SetPluginsEnabled(prefs.plugins_enabled);
settings->SetDOMPasteAllowed(prefs.dom_paste_enabled);
settings->SetTextAreasAreResizable(prefs.text_areas_are_resizable);
settings->SetAllowScriptsToCloseWindows(prefs.allow_scripts_to_close_windows);
settings->SetDownloadableBinaryFontsEnabled(prefs.remote_fonts_enabled);
settings->SetJavaScriptCanAccessClipboard(
prefs.javascript_can_access_clipboard);
RuntimeEnabledFeatures::SetXSLTEnabled(prefs.xslt_enabled);
settings->SetDNSPrefetchingEnabled(prefs.dns_prefetching_enabled);
blink::WebNetworkStateNotifier::SetSaveDataEnabled(prefs.data_saver_enabled);
settings->SetLocalStorageEnabled(prefs.local_storage_enabled);
settings->SetSyncXHRInDocumentsEnabled(prefs.sync_xhr_in_documents_enabled);
settings->SetTargetBlankImpliesNoOpenerEnabledWillBeRemoved(
prefs.target_blank_implies_no_opener_enabled_will_be_removed);
settings->SetAllowNonEmptyNavigatorPlugins(
prefs.allow_non_empty_navigator_plugins);
RuntimeEnabledFeatures::SetDatabaseEnabled(prefs.databases_enabled);
settings->SetOfflineWebApplicationCacheEnabled(
prefs.application_cache_enabled);
settings->SetShouldProtectAgainstIpcFlooding(
!prefs.disable_ipc_flooding_protection);
settings->SetHyperlinkAuditingEnabled(prefs.hyperlink_auditing_enabled);
settings->SetCookieEnabled(prefs.cookie_enabled);
settings->SetNavigateOnDragDrop(prefs.navigate_on_drag_drop);
settings->SetThreadedScrollingEnabled(prefs.threaded_scrolling_enabled);
// By default, allow_universal_access_from_file_urls is set to false and thus
// we mitigate attacks from local HTML files by not granting file:// URLs
// universal access. Only test shell will enable this.
settings->SetAllowUniversalAccessFromFileURLs(
prefs.allow_universal_access_from_file_urls);
settings->SetAllowFileAccessFromFileURLs(
prefs.allow_file_access_from_file_urls);
settings->SetWebGL1Enabled(prefs.webgl1_enabled);
settings->SetWebGL2Enabled(prefs.webgl2_enabled);
// Enable WebGL errors to the JS console if requested.
settings->SetWebGLErrorsToConsoleEnabled(
prefs.webgl_errors_to_console_enabled);
settings->SetHideScrollbars(prefs.hide_scrollbars);
// Enable gpu-accelerated 2d canvas if requested on the command line.
RuntimeEnabledFeatures::SetAccelerated2dCanvasEnabled(
prefs.accelerated_2d_canvas_enabled);
// Enable new canvas 2d api features
RuntimeEnabledFeatures::SetNewCanvas2DAPIEnabled(
prefs.new_canvas_2d_api_enabled);
// Disable antialiasing for 2d canvas if requested on the command line.
settings->SetAntialiased2dCanvasEnabled(
!prefs.antialiased_2d_canvas_disabled);
// Disable antialiasing of clips for 2d canvas if requested on the command
// line.
settings->SetAntialiasedClips2dCanvasEnabled(
prefs.antialiased_clips_2d_canvas_enabled);
// Tabs to link is not part of the settings. WebCore calls
// ChromeClient::tabsToLinks which is part of the glue code.
web_view_impl->SetTabsToLinks(prefs.tabs_to_links);
settings->SetAllowRunningOfInsecureContent(
prefs.allow_running_insecure_content);
settings->SetDisableReadingFromCanvas(prefs.disable_reading_from_canvas);
settings->SetStrictMixedContentChecking(prefs.strict_mixed_content_checking);
settings->SetStrictlyBlockBlockableMixedContent(
prefs.strictly_block_blockable_mixed_content);
settings->SetStrictMixedContentCheckingForPlugin(
prefs.block_mixed_plugin_content);
settings->SetStrictPowerfulFeatureRestrictions(
prefs.strict_powerful_feature_restrictions);
settings->SetAllowGeolocationOnInsecureOrigins(
prefs.allow_geolocation_on_insecure_origins);
settings->SetPasswordEchoEnabled(prefs.password_echo_enabled);
settings->SetShouldPrintBackgrounds(prefs.should_print_backgrounds);
settings->SetShouldClearDocumentBackground(
prefs.should_clear_document_background);
settings->SetEnableScrollAnimator(prefs.enable_scroll_animator);
settings->SetPrefersReducedMotion(prefs.prefers_reduced_motion);
RuntimeEnabledFeatures::SetTouchEventFeatureDetectionEnabled(
prefs.touch_event_feature_detection_enabled);
settings->SetMaxTouchPoints(prefs.pointer_events_max_touch_points);
settings->SetAvailablePointerTypes(prefs.available_pointer_types);
settings->SetPrimaryPointerType(prefs.primary_pointer_type);
settings->SetAvailableHoverTypes(prefs.available_hover_types);
settings->SetPrimaryHoverType(prefs.primary_hover_type);
settings->SetBarrelButtonForDragEnabled(prefs.barrel_button_for_drag_enabled);
settings->SetEditingBehavior(prefs.editing_behavior);
settings->SetSupportsMultipleWindows(prefs.supports_multiple_windows);
settings->SetMainFrameClipsContent(!prefs.record_whole_document);
settings->SetSmartInsertDeleteEnabled(prefs.smart_insert_delete_enabled);
settings->SetSpatialNavigationEnabled(prefs.spatial_navigation_enabled);
// Spatnav depends on KeyboardFocusableScrollers. The WebUI team has
// disabled KFS because they need more time to update their custom elements,
// crbug.com/907284. Meanwhile, we pre-ship KFS to spatnav users.
if (prefs.spatial_navigation_enabled)
RuntimeEnabledFeatures::SetKeyboardFocusableScrollersEnabled(true);
settings->SetSelectionIncludesAltImageText(true);
settings->SetV8CacheOptions(prefs.v8_cache_options);
settings->SetImageAnimationPolicy(prefs.animation_policy);
settings->SetPresentationRequiresUserGesture(
prefs.user_gesture_required_for_presentation);
if (prefs.text_tracks_enabled) {
settings->SetTextTrackKindUserPreference(
WebSettings::TextTrackKindUserPreference::kCaptions);
} else {
settings->SetTextTrackKindUserPreference(
WebSettings::TextTrackKindUserPreference::kDefault);
}
settings->SetTextTrackBackgroundColor(
WebString::FromASCII(prefs.text_track_background_color));
settings->SetTextTrackTextColor(
WebString::FromASCII(prefs.text_track_text_color));
settings->SetTextTrackTextSize(
WebString::FromASCII(prefs.text_track_text_size));
settings->SetTextTrackTextShadow(
WebString::FromASCII(prefs.text_track_text_shadow));
settings->SetTextTrackFontFamily(
WebString::FromASCII(prefs.text_track_font_family));
settings->SetTextTrackFontStyle(
WebString::FromASCII(prefs.text_track_font_style));
settings->SetTextTrackFontVariant(
WebString::FromASCII(prefs.text_track_font_variant));
settings->SetTextTrackMarginPercentage(prefs.text_track_margin_percentage);
settings->SetTextTrackWindowColor(
WebString::FromASCII(prefs.text_track_window_color));
settings->SetTextTrackWindowPadding(
WebString::FromASCII(prefs.text_track_window_padding));
settings->SetTextTrackWindowRadius(
WebString::FromASCII(prefs.text_track_window_radius));
// Needs to happen before SetDefaultPageScaleLimits below since that'll
// recalculate the final page scale limits and that depends on this setting.
settings->SetShrinksViewportContentToFit(
prefs.shrinks_viewport_contents_to_fit);
// Needs to happen before SetIgnoreViewportTagScaleLimits below.
web_view->SetDefaultPageScaleLimits(prefs.default_minimum_page_scale_factor,
prefs.default_maximum_page_scale_factor);
settings->SetFullscreenSupported(prefs.fullscreen_supported);
settings->SetTextAutosizingEnabled(prefs.text_autosizing_enabled);
settings->SetDoubleTapToZoomEnabled(prefs.double_tap_to_zoom_enabled);
blink::WebNetworkStateNotifier::SetNetworkQualityWebHoldback(
static_cast<blink::WebEffectiveConnectionType>(
prefs.network_quality_estimator_web_holdback));
settings->SetDontSendKeyEventsToJavascript(
prefs.dont_send_key_events_to_javascript);
settings->SetWebAppScope(WebString::FromASCII(prefs.web_app_scope.spec()));
#if defined(OS_ANDROID)
settings->SetAllowCustomScrollbarInMainFrame(false);
settings->SetAccessibilityFontScaleFactor(prefs.font_scale_factor);
settings->SetDeviceScaleAdjustment(prefs.device_scale_adjustment);
web_view_impl->SetIgnoreViewportTagScaleLimits(prefs.force_enable_zoom);
settings->SetAutoZoomFocusedNodeToLegibleScale(true);
settings->SetDefaultVideoPosterURL(
WebString::FromASCII(prefs.default_video_poster_url.spec()));
settings->SetSupportDeprecatedTargetDensityDPI(
prefs.support_deprecated_target_density_dpi);
settings->SetUseLegacyBackgroundSizeShorthandBehavior(
prefs.use_legacy_background_size_shorthand_behavior);
settings->SetWideViewportQuirkEnabled(prefs.wide_viewport_quirk);
settings->SetUseWideViewport(prefs.use_wide_viewport);
settings->SetForceZeroLayoutHeight(prefs.force_zero_layout_height);
settings->SetViewportMetaMergeContentQuirk(
prefs.viewport_meta_merge_content_quirk);
settings->SetViewportMetaNonUserScalableQuirk(
prefs.viewport_meta_non_user_scalable_quirk);
settings->SetViewportMetaZeroValuesQuirk(
prefs.viewport_meta_zero_values_quirk);
settings->SetClobberUserAgentInitialScaleQuirk(
prefs.clobber_user_agent_initial_scale_quirk);
settings->SetIgnoreMainFrameOverflowHiddenQuirk(
prefs.ignore_main_frame_overflow_hidden_quirk);
settings->SetReportScreenSizeInPhysicalPixelsQuirk(
prefs.report_screen_size_in_physical_pixels_quirk);
settings->SetShouldReuseGlobalForUnownedMainFrame(
prefs.reuse_global_for_unowned_main_frame);
settings->SetPreferHiddenVolumeControls(true);
settings->SetSpellCheckEnabledByDefault(prefs.spellcheck_enabled_by_default);
RuntimeEnabledFeatures::SetVideoFullscreenOrientationLockEnabled(
prefs.video_fullscreen_orientation_lock_enabled);
RuntimeEnabledFeatures::SetVideoRotateToFullscreenEnabled(
prefs.video_rotate_to_fullscreen_enabled);
settings->SetEmbeddedMediaExperienceEnabled(
prefs.embedded_media_experience_enabled);
settings->SetImmersiveModeEnabled(prefs.immersive_mode_enabled);
settings->SetDoNotUpdateSelectionOnMutatingSelectionRange(
prefs.do_not_update_selection_on_mutating_selection_range);
RuntimeEnabledFeatures::SetCSSHexAlphaColorEnabled(
prefs.css_hex_alpha_color_enabled);
RuntimeEnabledFeatures::SetScrollTopLeftInteropEnabled(
prefs.scroll_top_left_interop_enabled);
RuntimeEnabledFeatures::SetSurfaceEmbeddingFeaturesEnabled(
!prefs.disable_features_depending_on_viz);
RuntimeEnabledFeatures::SetAcceleratedSmallCanvasesEnabled(
!prefs.disable_accelerated_small_canvases);
#endif // defined(OS_ANDROID)
settings->SetForceDarkModeEnabled(prefs.force_dark_mode_enabled);
settings->SetAccessibilityAlwaysShowFocus(prefs.always_show_focus);
settings->SetAutoplayPolicy(prefs.autoplay_policy);
settings->SetViewportEnabled(prefs.viewport_enabled);
settings->SetViewportMetaEnabled(prefs.viewport_meta_enabled);
settings->SetViewportStyle(prefs.viewport_style);
settings->SetLoadWithOverviewMode(prefs.initialize_at_minimum_page_scale);
settings->SetMainFrameResizesAreOrientationChanges(
prefs.main_frame_resizes_are_orientation_changes);
settings->SetShowContextMenuOnMouseUp(prefs.context_menu_on_mouse_up);
settings->SetAlwaysShowContextMenuOnTouch(
prefs.always_show_context_menu_on_touch);
settings->SetSmoothScrollForFindEnabled(prefs.smooth_scroll_for_find_enabled);
settings->SetHideDownloadUI(prefs.hide_download_ui);
settings->SetPresentationReceiver(prefs.presentation_receiver);
settings->SetMediaControlsEnabled(prefs.media_controls_enabled);
settings->SetLowPriorityIframesThreshold(
static_cast<blink::WebEffectiveConnectionType>(
prefs.low_priority_iframes_threshold));
settings->SetPictureInPictureEnabled(
prefs.picture_in_picture_enabled &&
GetVideoSurfaceLayerMode() !=
blink::WebMediaPlayer::SurfaceLayerMode::kNever);
settings->SetDataSaverHoldbackWebApi(
prefs.data_saver_holdback_web_api_enabled);
settings->SetLazyLoadEnabled(prefs.lazy_load_enabled);
settings->SetPreferredColorScheme(prefs.preferred_color_scheme);
settings->SetPreferredContrast(prefs.preferred_contrast);
for (const auto& ect_distance_pair :
prefs.lazy_frame_loading_distance_thresholds_px) {
switch (ect_distance_pair.first) {
case EffectiveConnectionType::kEffectiveConnectionUnknownType:
settings->SetLazyFrameLoadingDistanceThresholdPxUnknown(
ect_distance_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnectionOfflineType:
settings->SetLazyFrameLoadingDistanceThresholdPxOffline(
ect_distance_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnectionSlow2GType:
settings->SetLazyFrameLoadingDistanceThresholdPxSlow2G(
ect_distance_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnection2GType:
settings->SetLazyFrameLoadingDistanceThresholdPx2G(
ect_distance_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnection3GType:
settings->SetLazyFrameLoadingDistanceThresholdPx3G(
ect_distance_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnection4GType:
settings->SetLazyFrameLoadingDistanceThresholdPx4G(
ect_distance_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnectionTypeLast:
continue;
}
NOTREACHED();
}
for (const auto& ect_distance_pair :
prefs.lazy_image_loading_distance_thresholds_px) {
switch (ect_distance_pair.first) {
case EffectiveConnectionType::kEffectiveConnectionUnknownType:
settings->SetLazyImageLoadingDistanceThresholdPxUnknown(
ect_distance_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnectionOfflineType:
settings->SetLazyImageLoadingDistanceThresholdPxOffline(
ect_distance_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnectionSlow2GType:
settings->SetLazyImageLoadingDistanceThresholdPxSlow2G(
ect_distance_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnection2GType:
settings->SetLazyImageLoadingDistanceThresholdPx2G(
ect_distance_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnection3GType:
settings->SetLazyImageLoadingDistanceThresholdPx3G(
ect_distance_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnection4GType:
settings->SetLazyImageLoadingDistanceThresholdPx4G(
ect_distance_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnectionTypeLast:
continue;
}
NOTREACHED();
}
for (const auto& fully_load_k_pair : prefs.lazy_image_first_k_fully_load) {
switch (fully_load_k_pair.first) {
case EffectiveConnectionType::kEffectiveConnectionOfflineType:
continue;
case EffectiveConnectionType::kEffectiveConnectionUnknownType:
settings->SetLazyImageFirstKFullyLoadUnknown(fully_load_k_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnectionSlow2GType:
settings->SetLazyImageFirstKFullyLoadSlow2G(fully_load_k_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnection2GType:
settings->SetLazyImageFirstKFullyLoad2G(fully_load_k_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnection3GType:
settings->SetLazyImageFirstKFullyLoad3G(fully_load_k_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnection4GType:
settings->SetLazyImageFirstKFullyLoad4G(fully_load_k_pair.second);
continue;
case EffectiveConnectionType::kEffectiveConnectionTypeLast:
continue;
}
NOTREACHED();
}
settings->SetTouchDragDropEnabled(prefs.touch_drag_drop_enabled);
settings->SetTouchDragEndContextMenu(prefs.touch_dragend_context_menu);
settings->SetWebXRImmersiveArAllowed(prefs.webxr_immersive_ar_allowed);
#if defined(OS_MAC)
web_view_impl->SetMaximumLegibleScale(
prefs.default_maximum_page_scale_factor);
#endif
#if defined(OS_WIN)
RuntimeEnabledFeatures::SetMiddleClickAutoscrollEnabled(true);
#endif
RuntimeEnabledFeatures::SetTranslateServiceEnabled(
prefs.translate_service_available);
}
void WebViewImpl::ThemeChanged() {
if (!GetPage())
return;
if (!GetPage()->MainFrame()->IsLocalFrame())
return;
LocalFrameView* view = GetPage()->DeprecatedLocalMainFrame()->View();
IntRect damaged_rect(0, 0, size_.width(), size_.height());
view->InvalidateRect(damaged_rect);
}
void WebViewImpl::EnterFullscreen(LocalFrame& frame,
const FullscreenOptions* options,
FullscreenRequestType request_type) {
fullscreen_controller_->EnterFullscreen(frame, options, request_type);
}
void WebViewImpl::ExitFullscreen(LocalFrame& frame) {
fullscreen_controller_->ExitFullscreen(frame);
}
void WebViewImpl::FullscreenElementChanged(Element* old_element,
Element* new_element,
const FullscreenOptions* options,
FullscreenRequestType request_type) {
fullscreen_controller_->FullscreenElementChanged(old_element, new_element,
options, request_type);
}
bool WebViewImpl::HasHorizontalScrollbar() {
return MainFrameImpl()
->GetFrameView()
->LayoutViewport()
->HorizontalScrollbar();
}
bool WebViewImpl::HasVerticalScrollbar() {
return MainFrameImpl()->GetFrameView()->LayoutViewport()->VerticalScrollbar();
}
void WebViewImpl::SetPageFocus(bool enable) {
if (enable)
page_->GetFocusController().SetActive(true);
page_->GetFocusController().SetFocused(enable);
if (enable) {
LocalFrame* focused_frame = page_->GetFocusController().FocusedFrame();
if (focused_frame) {
Element* element = focused_frame->GetDocument()->FocusedElement();
if (element && focused_frame->Selection()
.ComputeVisibleSelectionInDOMTreeDeprecated()
.IsNone()) {
// If the selection was cleared while the WebView was not
// focused, then the focus element shows with a focus ring but
// no caret and does respond to keyboard inputs.
focused_frame->GetDocument()->UpdateStyleAndLayoutTree();
if (element->IsTextControl()) {
element->UpdateFocusAppearance(SelectionBehaviorOnFocus::kRestore);
} else if (HasEditableStyle(*element)) {
// updateFocusAppearance() selects all the text of
// contentseditable DIVs. So we set the selection explicitly
// instead. Note that this has the side effect of moving the
// caret back to the beginning of the text.
Position position(element, 0);
focused_frame->Selection().SetSelectionAndEndTyping(
SelectionInDOMTree::Builder().Collapse(position).Build());
}
}
}
} else {
CancelPagePopup();
LocalFrame* focused_frame = page_->GetFocusController().FocusedFrame();
if (focused_frame) {
// Finish an ongoing composition to delete the composition node.
if (focused_frame->GetInputMethodController().HasComposition()) {
// TODO(editing-dev): The use of
// UpdateStyleAndLayout needs to be audited.
// See http://crbug.com/590369 for more details.
focused_frame->GetDocument()->UpdateStyleAndLayout(
DocumentUpdateReason::kFocus);
focused_frame->GetInputMethodController().FinishComposingText(
InputMethodController::kKeepSelection);
}
}
}
}
// WebView --------------------------------------------------------------------
WebSettingsImpl* WebViewImpl::SettingsImpl() {
if (!web_settings_) {
web_settings_ = std::make_unique<WebSettingsImpl>(
&page_->GetSettings(), dev_tools_emulator_.Get());
}
DCHECK(web_settings_);
return web_settings_.get();
}
WebSettings* WebViewImpl::GetSettings() {
return SettingsImpl();
}
WebString WebViewImpl::PageEncoding() const {
if (!page_)
return WebString();
auto* main_frame = DynamicTo<LocalFrame>(page_->MainFrame());
if (!main_frame)
return WebString();
// FIXME: Is this check needed?
if (!main_frame->GetDocument()->Loader())
return WebString();
return main_frame->GetDocument()->EncodingName();
}
WebFrame* WebViewImpl::MainFrame() {
Page* page = page_.Get();
return WebFrame::FromCoreFrame(page ? page->MainFrame() : nullptr);
}
WebLocalFrameImpl* WebViewImpl::MainFrameImpl() const {
Page* page = page_.Get();
if (!page)
return nullptr;
return WebLocalFrameImpl::FromFrame(DynamicTo<LocalFrame>(page->MainFrame()));
}
std::string WebViewImpl::GetNullFrameReasonForBug1139104() const {
Page* page = page_.Get();
if (!page)
return "WebViewImpl::page";
if (!page->MainFrame())
return "WebViewImpl::page->MainFrame";
LocalFrame* local_frame = DynamicTo<LocalFrame>(page->MainFrame());
if (!local_frame)
return "WebViewImpl::local_frame";
return WebLocalFrameImpl::GetNullFrameReasonForBug1139104(local_frame);
}
void WebViewImpl::DidAttachLocalMainFrame() {
DCHECK(MainFrameImpl());
LocalFrame* local_frame = MainFrameImpl()->GetFrame();
local_frame->WasAttachedAsLocalMainFrame();
local_frame->GetRemoteNavigationAssociatedInterfaces()->GetInterface(
local_main_frame_host_remote_.BindNewEndpointAndPassReceiver(
GetPage()
->GetPageScheduler()
->GetAgentGroupScheduler()
.DefaultTaskRunner()));
if (does_composite_) {
// When attaching a local main frame, set up any state on the compositor.
MainFrameImpl()->FrameWidgetImpl()->SetBackgroundColor(BackgroundColor());
auto& viewport = GetPage()->GetVisualViewport();
MainFrameImpl()->FrameWidgetImpl()->SetPageScaleStateAndLimits(
viewport.Scale(), viewport.IsPinchGestureActive(),
MinimumPageScaleFactor(), MaximumPageScaleFactor());
// Prevent main frame updates while the main frame is loading until enough
// progress is made and BeginMainFrames are explicitly asked for.
scoped_defer_main_frame_update_ =
MainFrameImpl()->FrameWidgetImpl()->DeferMainFrameUpdate();
}
}
void WebViewImpl::DidAttachRemoteMainFrame() {
DCHECK(!MainFrameImpl());
RemoteFrame* remote_frame = DynamicTo<RemoteFrame>(GetPage()->MainFrame());
remote_frame->WasAttachedAsRemoteMainFrame();
remote_frame->GetRemoteAssociatedInterfaces()->GetInterface(
remote_main_frame_host_remote_.BindNewEndpointAndPassReceiver(
GetPage()
->GetPageScheduler()
->GetAgentGroupScheduler()
.DefaultTaskRunner()));
auto& viewport = GetPage()->GetVisualViewport();
viewport.Reset();
}
void WebViewImpl::DidDetachLocalMainFrame() {
// The WebFrameWidget that generated the |scoped_defer_main_frame_update_|
// for a local main frame is going away.
scoped_defer_main_frame_update_ = nullptr;
local_main_frame_host_remote_.reset();
}
void WebViewImpl::DidDetachRemoteMainFrame() {
remote_main_frame_host_remote_.reset();
}
WebLocalFrame* WebViewImpl::FocusedFrame() {
Frame* frame = FocusedCoreFrame();
// TODO(yabinh): focusedCoreFrame() should always return a local frame, and
// the following check should be unnecessary.
// See crbug.com/625068
return WebLocalFrameImpl::FromFrame(DynamicTo<LocalFrame>(frame));
}
void WebViewImpl::SetFocusedFrame(WebFrame* frame) {
if (!frame) {
// Clears the focused frame if any.
Frame* focused_frame = FocusedCoreFrame();
if (auto* focused_local_frame = DynamicTo<LocalFrame>(focused_frame))
focused_local_frame->Selection().SetFrameIsFocused(false);
return;
}
LocalFrame* core_frame = To<WebLocalFrameImpl>(frame)->GetFrame();
core_frame->GetPage()->GetFocusController().SetFocusedFrame(core_frame);
}
bool WebViewImpl::ShouldZoomToLegibleScale(const Element& element) {
bool zoom_into_legible_scale =
web_settings_->AutoZoomFocusedNodeToLegibleScale() &&
!GetPage()->GetVisualViewport().ShouldDisableDesktopWorkarounds();
if (zoom_into_legible_scale) {
// When deciding whether to zoom in on a focused text box, we should
// decide not to zoom in if the user won't be able to zoom out. e.g if the
// textbox is within a touch-action: none container the user can't zoom
// back out.
TouchAction action =
touch_action_util::ComputeEffectiveTouchAction(element);
if (!(static_cast<int>(action) & static_cast<int>(TouchAction::kPinchZoom)))
zoom_into_legible_scale = false;
}
return zoom_into_legible_scale;
}
void WebViewImpl::ZoomAndScrollToFocusedEditableElementRect(
const IntRect& element_bounds_in_document,
const IntRect& caret_bounds_in_document,
bool zoom_into_legible_scale) {
float scale;
IntPoint scroll;
bool need_animation = false;
ComputeScaleAndScrollForEditableElementRects(
element_bounds_in_document, caret_bounds_in_document,
zoom_into_legible_scale, scale, scroll, need_animation);
if (need_animation) {
StartPageScaleAnimation(scroll, false, scale,
kScrollAndScaleAnimationDuration);
}
}
void WebViewImpl::SmoothScroll(int target_x,
int target_y,
base::TimeDelta duration) {
IntPoint target_position(target_x, target_y);
StartPageScaleAnimation(target_position, false, PageScaleFactor(), duration);
}
void WebViewImpl::ComputeScaleAndScrollForEditableElementRects(
const IntRect& element_bounds_in_document,
const IntRect& caret_bounds_in_document,
bool zoom_into_legible_scale,
float& new_scale,
IntPoint& new_scroll,
bool& need_animation) {
VisualViewport& visual_viewport = GetPage()->GetVisualViewport();
TopDocumentRootScrollerController& controller =
GetPage()->GlobalRootScrollerController();
Node* root_scroller = controller.GlobalRootScroller();
IntRect element_bounds_in_content = element_bounds_in_document;
IntRect caret_bounds_in_content = caret_bounds_in_document;
// If the page has a non-default root scroller then we need to scroll that
// rather than the "real" viewport. However, the given coordinates are in the
// real viewport's document space rather than the root scroller's so we
// perform the conversion here. TODO(bokan): Convert this function to take
// coordinates in absolute/root-frame coordinates to make this more
// consistent. https://crbug.com/931447.
if (root_scroller != MainFrameImpl()->GetFrame()->GetDocument() &&
controller.RootScrollerArea()) {
ScrollOffset offset = controller.RootScrollerArea()->GetScrollOffset();
element_bounds_in_content.Move(FlooredIntSize(offset));
caret_bounds_in_content.Move(FlooredIntSize(offset));
}
if (!zoom_into_legible_scale) {
new_scale = PageScaleFactor();
} else {
// Pick a scale which is reasonably readable. This is the scale at which
// the caret height will become minReadableCaretHeightForNode (adjusted
// for dpi and font scale factor).
const int min_readable_caret_height_for_node =
(element_bounds_in_content.Height() >=
2 * caret_bounds_in_content.Height()
? minReadableCaretHeightForTextArea
: minReadableCaretHeight) *
MainFrameImpl()->GetFrame()->PageZoomFactor();
new_scale = ClampPageScaleFactorToLimits(
MaximumLegiblePageScale() * min_readable_caret_height_for_node /
caret_bounds_in_content.Height());
new_scale = std::max(new_scale, PageScaleFactor());
}
const float delta_scale = new_scale / PageScaleFactor();
need_animation = false;
// If we are at less than the target zoom level, zoom in.
if (delta_scale > minScaleChangeToTriggerZoom)
need_animation = true;
else
new_scale = PageScaleFactor();
ScrollableArea* root_viewport =
MainFrameImpl()->GetFrame()->View()->GetScrollableArea();
// If the caret is offscreen, then animate.
if (!root_viewport->VisibleContentRect().Contains(caret_bounds_in_content))
need_animation = true;
// If the box is partially offscreen and it's possible to bring it fully
// onscreen, then animate.
if (visual_viewport.VisibleRect().Width() >=
element_bounds_in_content.Width() &&
visual_viewport.VisibleRect().Height() >=
element_bounds_in_content.Height() &&
!root_viewport->VisibleContentRect().Contains(element_bounds_in_content))
need_animation = true;
if (!need_animation)
return;
FloatSize target_viewport_size(visual_viewport.Size());
target_viewport_size.Scale(1 / new_scale);
if (element_bounds_in_content.Width() <= target_viewport_size.Width()) {
// Field is narrower than screen. Try to leave padding on left so field's
// label is visible, but it's more important to ensure entire field is
// onscreen.
int ideal_left_padding = target_viewport_size.Width() * leftBoxRatio;
int max_left_padding_keeping_box_onscreen =
target_viewport_size.Width() - element_bounds_in_content.Width();
new_scroll.SetX(element_bounds_in_content.X() -
std::min<int>(ideal_left_padding,
max_left_padding_keeping_box_onscreen));
} else {
// Field is wider than screen. Try to left-align field, unless caret would
// be offscreen, in which case right-align the caret.
new_scroll.SetX(std::max<int>(
element_bounds_in_content.X(),
caret_bounds_in_content.X() + caret_bounds_in_content.Width() +
caretPadding - target_viewport_size.Width()));
}
if (element_bounds_in_content.Height() <= target_viewport_size.Height()) {
// Field is shorter than screen. Vertically center it.
new_scroll.SetY(
element_bounds_in_content.Y() -
(target_viewport_size.Height() - element_bounds_in_content.Height()) /
2);
} else {
// Field is taller than screen. Try to top align field, unless caret would
// be offscreen, in which case bottom-align the caret.
new_scroll.SetY(std::max<int>(
element_bounds_in_content.Y(),
caret_bounds_in_content.Y() + caret_bounds_in_content.Height() +
caretPadding - target_viewport_size.Height()));
}
}
void WebViewImpl::AdvanceFocus(bool reverse) {
GetPage()->GetFocusController().AdvanceFocus(
reverse ? mojom::blink::FocusType::kBackward
: mojom::blink::FocusType::kForward);
}
double WebViewImpl::ZoomLevel() {
return zoom_level_;
}
void WebViewImpl::PropagateZoomFactorToLocalFrameRoots(Frame* frame,
float zoom_factor) {
auto* local_frame = DynamicTo<LocalFrame>(frame);
if (local_frame && local_frame->IsLocalRoot()) {
if (Document* document = local_frame->GetDocument()) {
auto* plugin_document = DynamicTo<PluginDocument>(document);
if (!plugin_document || !plugin_document->GetPluginView()) {
local_frame->SetPageZoomFactor(zoom_factor);
}
}
}
for (Frame* child = frame->Tree().FirstChild(); child;
child = child->Tree().NextSibling())
PropagateZoomFactorToLocalFrameRoots(child, zoom_factor);
}
double WebViewImpl::SetZoomLevel(double zoom_level) {
double old_zoom_level = zoom_level_;
if (zoom_level < minimum_zoom_level_)
zoom_level_ = minimum_zoom_level_;
else if (zoom_level > maximum_zoom_level_)
zoom_level_ = maximum_zoom_level_;
else
zoom_level_ = zoom_level;
float zoom_factor =
zoom_factor_override_
? zoom_factor_override_
: static_cast<float>(PageZoomLevelToZoomFactor(zoom_level_));
if (zoom_factor_for_device_scale_factor_) {
if (compositor_device_scale_factor_override_) {
// Adjust the page's DSF so that DevicePixelRatio becomes
// |zoom_factor_for_device_scale_factor_|.
page_->SetDeviceScaleFactorDeprecated(
zoom_factor_for_device_scale_factor_ /
compositor_device_scale_factor_override_);
zoom_factor *= compositor_device_scale_factor_override_;
} else {
page_->SetDeviceScaleFactorDeprecated(1.f);
zoom_factor *= zoom_factor_for_device_scale_factor_;
}
}
PropagateZoomFactorToLocalFrameRoots(page_->MainFrame(), zoom_factor);
if (old_zoom_level != zoom_level_) {
for (auto& observer : observers_)
observer.OnZoomLevelChanged();
CancelPagePopup();
}
return zoom_level_;
}
float WebViewImpl::PageScaleFactor() const {
if (!GetPage())
return 1;
return GetPage()->GetVisualViewport().Scale();
}
float WebViewImpl::ClampPageScaleFactorToLimits(float scale_factor) const {
return GetPageScaleConstraintsSet().FinalConstraints().ClampToConstraints(
scale_factor);
}
void WebViewImpl::SetVisualViewportOffset(const gfx::PointF& offset) {
DCHECK(GetPage());
GetPage()->GetVisualViewport().SetLocation(FloatPoint(offset));
}
gfx::PointF WebViewImpl::VisualViewportOffset() const {
DCHECK(GetPage());
return GetPage()->GetVisualViewport().VisibleRect().Location();
}
gfx::SizeF WebViewImpl::VisualViewportSize() const {
DCHECK(GetPage());
return gfx::SizeF(GetPage()->GetVisualViewport().VisibleRect().Size());
}
void WebViewImpl::SetPageScaleFactorAndLocation(float scale_factor,
bool is_pinch_gesture_active,
const FloatPoint& location) {
DCHECK(GetPage());
GetPage()->GetVisualViewport().SetScaleAndLocation(
ClampPageScaleFactorToLimits(scale_factor), is_pinch_gesture_active,
location);
}
void WebViewImpl::SetPageScaleFactor(float scale_factor) {
DCHECK(GetPage());
DCHECK(MainFrameImpl());
MainFrameImpl()->GetFrame()->SetScaleFactor(scale_factor);
}
void WebViewImpl::SetDeviceScaleFactor(float scale_factor) {
if (!GetPage())
return;
if (GetPage()->DeviceScaleFactorDeprecated() == scale_factor)
return;
GetPage()->SetDeviceScaleFactorDeprecated(scale_factor);
}
void WebViewImpl::SetZoomFactorForDeviceScaleFactor(
float zoom_factor_for_device_scale_factor) {
DCHECK(does_composite_);
// We can't early-return here if these are already equal, because we may
// need to propagate the correct zoom factor to newly navigated frames.
zoom_factor_for_device_scale_factor_ = zoom_factor_for_device_scale_factor;
SetZoomLevel(zoom_level_);
}
void WebViewImpl::SetPageLifecycleStateFromNewPageCommit(
mojom::blink::PageVisibilityState visibility,
mojom::blink::PagehideDispatch pagehide_dispatch) {
mojom::blink::PageLifecycleStatePtr state =
GetPage()->GetPageLifecycleState().Clone();
state->visibility = visibility;
state->pagehide_dispatch = pagehide_dispatch;
SetPageLifecycleStateInternal(std::move(state),
/*page_restore_params=*/nullptr);
}
void WebViewImpl::SetPageLifecycleState(
mojom::blink::PageLifecycleStatePtr state,
mojom::blink::PageRestoreParamsPtr page_restore_params,
SetPageLifecycleStateCallback callback) {
SetPageLifecycleStateInternal(std::move(state),
std::move(page_restore_params));
// Tell the browser that the lifecycle update was successful.
std::move(callback).Run();
}
void WebViewImpl::SetPageLifecycleStateInternal(
mojom::blink::PageLifecycleStatePtr new_state,
mojom::blink::PageRestoreParamsPtr page_restore_params) {
Page* page = GetPage();
if (!page)
return;
auto& old_state = page->GetPageLifecycleState();
bool storing_in_bfcache = new_state->is_in_back_forward_cache &&
!old_state->is_in_back_forward_cache;
bool restoring_from_bfcache = !new_state->is_in_back_forward_cache &&
old_state->is_in_back_forward_cache;
bool hiding_page =
(new_state->visibility != mojom::blink::PageVisibilityState::kVisible) &&
(old_state->visibility == mojom::blink::PageVisibilityState::kVisible);
bool showing_page =
(new_state->visibility == mojom::blink::PageVisibilityState::kVisible) &&
(old_state->visibility != mojom::blink::PageVisibilityState::kVisible);
bool freezing_page = new_state->is_frozen && !old_state->is_frozen;
bool resuming_page = !new_state->is_frozen && old_state->is_frozen;
bool dispatching_pagehide =
(new_state->pagehide_dispatch !=
mojom::blink::PagehideDispatch::kNotDispatched) &&
!GetPage()->DispatchedPagehideAndStillHidden();
bool dispatching_pageshow =
(new_state->pagehide_dispatch ==
mojom::blink::PagehideDispatch::kNotDispatched) &&
GetPage()->DispatchedPagehideAndStillHidden();
bool eviction_changed =
new_state->eviction_enabled != old_state->eviction_enabled;
if (dispatching_pagehide) {
RemoveFocusAndTextInputState();
}
if (dispatching_pagehide) {
// Note that |dispatching_pagehide| is different than |hiding_page|.
// |dispatching_pagehide| will only be true when we're navigating away from
// a page, while |hiding_page| might be true in other cases too such as when
// the tab containing a page is backgrounded, and might be false even when
// we're navigating away from a page, if the page is already hidden.
DispatchPagehide(new_state->pagehide_dispatch);
}
if (hiding_page) {
SetVisibilityState(new_state->visibility, /*is_initial_state=*/false);
}
if (storing_in_bfcache) {
Scheduler()->SetPageBackForwardCached(new_state->is_in_back_forward_cache);
}
if (freezing_page)
SetPageFrozen(true);
if (restoring_from_bfcache) {
DCHECK(page_restore_params);
// Update the history offset and length value saved in RenderViewImpl, as
// pages that are kept in the back-forward cache do not get notified about
// updates on these values, so the currently saved value might be stale.
web_view_client_->OnSetHistoryOffsetAndLength(
page_restore_params->pending_history_list_offset,
page_restore_params->current_history_list_length);
}
if (eviction_changed)
HookBackForwardCacheEviction(new_state->eviction_enabled);
if (resuming_page)
SetPageFrozen(false);
if (showing_page) {
SetVisibilityState(new_state->visibility, /*is_initial_state=*/false);
}
if (dispatching_pageshow) {
DCHECK(restoring_from_bfcache);
DispatchPageshow(page_restore_params->navigation_start);
}
if (restoring_from_bfcache) {
DCHECK(dispatching_pageshow);
DCHECK(page_restore_params);
Scheduler()->SetPageBackForwardCached(new_state->is_in_back_forward_cache);
}
// Make sure no TrackedFeaturesUpdate message is sent after the ACK
// TODO(carlscab): Do we really need to go through LocalFrame =>
// platform/scheduler/ => LocalFrame to report the features? We can probably
// move SchedulerTrackedFeatures to core/ and remove the back and forth.
ReportActiveSchedulerTrackedFeatures();
GetPage()->SetPageLifecycleState(std::move(new_state));
// Notify all local frames that we've updated the page lifecycle state.
for (WebFrame* frame = MainFrame(); frame; frame = frame->TraverseNext()) {
if (frame->IsWebLocalFrame()) {
frame->ToWebLocalFrame()->Client()->DidSetPageLifecycleState();
}
}
}
void WebViewImpl::ReportActiveSchedulerTrackedFeatures() {
Page* page = GetPage();
if (!page)
return;
for (Frame* frame = page->MainFrame(); frame;
frame = frame->Tree().TraverseNext()) {
if (!frame->IsLocalFrame())
continue;
auto* local_frame = DynamicTo<LocalFrame>(frame);
if (!local_frame->GetFrameScheduler())
continue;
local_frame->GetFrameScheduler()->ReportActiveSchedulerTrackedFeatures();
}
}
void WebViewImpl::AudioStateChanged(bool is_audio_playing) {
GetPage()->GetPageScheduler()->AudioStateChanged(is_audio_playing);
}
void WebViewImpl::RemoveFocusAndTextInputState() {
auto& focus_controller = GetPage()->GetFocusController();
auto* focused_frame = focus_controller.FocusedFrame();
if (!focused_frame)
return;
// Remove focus from the currently focused element and frame.
focus_controller.SetFocusedElement(nullptr, nullptr);
// Clear composing state, and make sure we send a TextInputState update.
// Note that the TextInputState itself is cleared when we clear the focus,
// but no updates to the browser will be triggered until the next animation
// frame, which won't happen if we're freezing the page.
if (auto* widget = static_cast<WebFrameWidgetImpl*>(
focused_frame->GetWidgetForLocalRoot())) {
widget->FinishComposingText(false /* keep_selection */);
widget->UpdateTextInputState();
}
}
void WebViewImpl::DispatchPagehide(
mojom::blink::PagehideDispatch pagehide_dispatch) {
DCHECK_NE(pagehide_dispatch, mojom::blink::PagehideDispatch::kNotDispatched);
bool persisted = (pagehide_dispatch ==
mojom::blink::PagehideDispatch::kDispatchedPersisted);
// Dispatch pagehide on all frames.
for (Frame* frame = GetPage()->MainFrame(); frame;
frame = frame->Tree().TraverseNext()) {
if (frame->DomWindow() && frame->DomWindow()->IsLocalDOMWindow()) {
frame->DomWindow()->ToLocalDOMWindow()->DispatchPagehideEvent(
persisted
? PageTransitionEventPersistence::kPageTransitionEventPersisted
: PageTransitionEventPersistence::
kPageTransitionEventNotPersisted);
}
}
}
void WebViewImpl::DispatchPageshow(base::TimeTicks navigation_start) {
for (Frame* frame = GetPage()->MainFrame(); frame;
frame = frame->Tree().TraverseNext()) {
auto* local_frame = DynamicTo<LocalFrame>(frame);
// Record the metics.
if (local_frame && local_frame->View()) {
Document* document = local_frame->GetDocument();
if (document) {
PaintTiming::From(*document).OnRestoredFromBackForwardCache();
InteractiveDetector::From(*document)->OnRestoredFromBackForwardCache();
}
DocumentLoader* loader = local_frame->Loader().GetDocumentLoader();
if (loader) {
loader->GetTiming().MarkBackForwardCacheRestoreNavigationStart(
navigation_start);
}
}
if (frame->DomWindow() && frame->DomWindow()->IsLocalDOMWindow()) {
frame->DomWindow()->ToLocalDOMWindow()->DispatchPersistedPageshowEvent(
navigation_start);
if (frame->IsMainFrame()) {
UMA_HISTOGRAM_BOOLEAN(
"BackForwardCache.MainFrameHasPageshowListenersOnRestore",
frame->DomWindow()->ToLocalDOMWindow()->HasEventListeners(
event_type_names::kPageshow));
}
}
}
}
void WebViewImpl::HookBackForwardCacheEviction(bool hook) {
DCHECK(GetPage());
for (Frame* frame = GetPage()->MainFrame(); frame;
frame = frame->Tree().TraverseNext()) {
auto* local_frame = DynamicTo<LocalFrame>(frame);
if (!local_frame)
continue;
if (hook)
local_frame->HookBackForwardCacheEviction();
else
local_frame->RemoveBackForwardCacheEviction();
}
}
void WebViewImpl::EnableAutoResizeMode(const gfx::Size& min_size,
const gfx::Size& max_size) {
should_auto_resize_ = true;
min_auto_size_ = IntSize(min_size);
max_auto_size_ = IntSize(max_size);
ConfigureAutoResizeMode();
}
void WebViewImpl::DisableAutoResizeMode() {
should_auto_resize_ = false;
ConfigureAutoResizeMode();
}
bool WebViewImpl::AutoResizeMode() {
return should_auto_resize_;
}
void WebViewImpl::EnableAutoResizeForTesting(const gfx::Size& min_window_size,
const gfx::Size& max_window_size) {
EnableAutoResizeMode(web_widget_->DIPsToCeiledBlinkSpace(min_window_size),
web_widget_->DIPsToCeiledBlinkSpace(max_window_size));
}
void WebViewImpl::DisableAutoResizeForTesting(
const gfx::Size& new_window_size) {
if (!should_auto_resize_)
return;
DisableAutoResizeMode();
// The |new_size| is empty when resetting auto resize in between tests. In
// this case the current size should just be preserved.
if (!new_window_size.IsEmpty()) {
web_widget_->Resize(web_widget_->DIPsToCeiledBlinkSpace(new_window_size));
}
}
void WebViewImpl::SetDefaultPageScaleLimits(float min_scale, float max_scale) {
dev_tools_emulator_->SetDefaultPageScaleLimits(min_scale, max_scale);
}
void WebViewImpl::SetInitialPageScaleOverride(
float initial_page_scale_factor_override) {
PageScaleConstraints constraints =
GetPageScaleConstraintsSet().UserAgentConstraints();
constraints.initial_scale = initial_page_scale_factor_override;
if (constraints == GetPageScaleConstraintsSet().UserAgentConstraints())
return;
GetPageScaleConstraintsSet().SetNeedsReset(true);
GetPage()->SetUserAgentPageScaleConstraints(constraints);
}
void WebViewImpl::SetMaximumLegibleScale(float maximum_legible_scale) {
maximum_legible_scale_ = maximum_legible_scale;
}
void WebViewImpl::SetIgnoreViewportTagScaleLimits(bool ignore) {
PageScaleConstraints constraints =
GetPageScaleConstraintsSet().UserAgentConstraints();
if (ignore) {
// Don't ignore the minimum limits in touchless mode to prevent wide
// loading elements from causing us to zoom pages out beyond their layout
// which is fairly common.
if (!RuntimeEnabledFeatures::FocuslessSpatialNavigationEnabled()) {
constraints.minimum_scale =
GetPageScaleConstraintsSet().DefaultConstraints().minimum_scale;
}
constraints.maximum_scale =
GetPageScaleConstraintsSet().DefaultConstraints().maximum_scale;
} else {
if (!RuntimeEnabledFeatures::FocuslessSpatialNavigationEnabled()) {
constraints.minimum_scale = -1;
}
constraints.maximum_scale = -1;
}
GetPage()->SetUserAgentPageScaleConstraints(constraints);
}
IntSize WebViewImpl::MainFrameSize() {
// The frame size should match the viewport size at minimum scale, since the
// viewport must always be contained by the frame.
return IntSize(gfx::ScaleToCeiledSize(size_, 1 / MinimumPageScaleFactor()));
}
PageScaleConstraintsSet& WebViewImpl::GetPageScaleConstraintsSet() const {
return GetPage()->GetPageScaleConstraintsSet();
}
void WebViewImpl::RefreshPageScaleFactor() {
if (!MainFrame() || !GetPage() || !GetPage()->MainFrame() ||
!GetPage()->MainFrame()->IsLocalFrame() ||
!GetPage()->DeprecatedLocalMainFrame()->View())
return;
UpdatePageDefinedViewportConstraints(MainFrameImpl()
->GetFrame()
->GetDocument()
->GetViewportData()
.GetViewportDescription());
GetPageScaleConstraintsSet().ComputeFinalConstraints();
float new_page_scale_factor = PageScaleFactor();
if (GetPageScaleConstraintsSet().NeedsReset() &&
GetPageScaleConstraintsSet().FinalConstraints().initial_scale != -1) {
new_page_scale_factor =
GetPageScaleConstraintsSet().FinalConstraints().initial_scale;
GetPageScaleConstraintsSet().SetNeedsReset(false);
}
SetPageScaleFactor(new_page_scale_factor);
// The constraints may have changed above which affects the page scale limits,
// so we must update those even though SetPageScaleFactor() may do the same if
// the scale factor is changed.
if (does_composite_) {
auto& viewport = GetPage()->GetVisualViewport();
MainFrameImpl()->FrameWidgetImpl()->SetPageScaleStateAndLimits(
viewport.Scale(), viewport.IsPinchGestureActive(),
MinimumPageScaleFactor(), MaximumPageScaleFactor());
}
}
void WebViewImpl::UpdatePageDefinedViewportConstraints(
const ViewportDescription& description) {
if (!GetPage() || (!size_.width() && !size_.height()))
return;
// The viewport is a property of the main frame and its widget, so ignore it
// when the main frame is remote.
// TODO(danakj): Remove calls to this method from ChromeClient and DCHECK this
// instead.
if (!GetPage()->MainFrame()->IsLocalFrame())
return;
if (!GetSettings()->ViewportEnabled()) {
GetPageScaleConstraintsSet().ClearPageDefinedConstraints();
UpdateMainFrameLayoutSize();
return;
}
Document* document = GetPage()->DeprecatedLocalMainFrame()->GetDocument();
Length default_min_width =
document->GetViewportData().ViewportDefaultMinWidth();
if (default_min_width.IsAuto())
default_min_width = Length::ExtendToZoom();
float old_initial_scale =
GetPageScaleConstraintsSet().PageDefinedConstraints().initial_scale;
GetPageScaleConstraintsSet().UpdatePageDefinedConstraints(description,
default_min_width);
if (SettingsImpl()->ClobberUserAgentInitialScaleQuirk() &&
GetPageScaleConstraintsSet().UserAgentConstraints().initial_scale != -1 &&
GetPageScaleConstraintsSet().UserAgentConstraints().initial_scale *
DeviceScaleFactor() <=
1) {
if (description.max_width == Length::DeviceWidth() ||
(description.max_width.IsAuto() &&
GetPageScaleConstraintsSet().PageDefinedConstraints().initial_scale ==
1.0f))
SetInitialPageScaleOverride(-1);
}
Settings& page_settings = GetPage()->GetSettings();
GetPageScaleConstraintsSet().AdjustForAndroidWebViewQuirks(
description, default_min_width.IntValue(), DeviceScaleFactor(),
SettingsImpl()->SupportDeprecatedTargetDensityDPI(),
page_settings.GetWideViewportQuirkEnabled(),
page_settings.GetUseWideViewport(),
page_settings.GetLoadWithOverviewMode(),
SettingsImpl()->ViewportMetaNonUserScalableQuirk());
float new_initial_scale =
GetPageScaleConstraintsSet().PageDefinedConstraints().initial_scale;
if (old_initial_scale != new_initial_scale && new_initial_scale != -1) {
GetPageScaleConstraintsSet().SetNeedsReset(true);
if (MainFrameImpl() && MainFrameImpl()->GetFrameView())
MainFrameImpl()->GetFrameView()->SetNeedsLayout();
}
UpdateMainFrameLayoutSize();
}
void WebViewImpl::UpdateMainFrameLayoutSize() {
if (should_auto_resize_ || !MainFrameImpl())
return;
LocalFrameView* view = MainFrameImpl()->GetFrameView();
if (!view)
return;
gfx::Size layout_size = size_;
if (GetSettings()->ViewportEnabled())
layout_size = gfx::Size(GetPageScaleConstraintsSet().GetLayoutSize());
if (GetPage()->GetSettings().GetForceZeroLayoutHeight())
layout_size.set_height(0);
view->SetLayoutSize(IntSize(layout_size));
}
IntSize WebViewImpl::ContentsSize() const {
if (!GetPage()->MainFrame()->IsLocalFrame())
return IntSize();
auto* layout_view =
GetPage()->DeprecatedLocalMainFrame()->ContentLayoutObject();
if (!layout_view)
return IntSize();
return PixelSnappedIntRect(layout_view->DocumentRect()).Size();
}
gfx::Size WebViewImpl::ContentsPreferredMinimumSize() {
DCHECK(page_->MainFrame()->IsLocalFrame());
auto* main_local_frame = DynamicTo<LocalFrame>(page_->MainFrame());
Document* document = main_local_frame->GetDocument();
if (!document || !document->GetLayoutView() || !document->documentElement() ||
!document->documentElement()->GetLayoutBox())
return gfx::Size();
// The preferred size requires an up-to-date layout tree.
DCHECK(!document->NeedsLayoutTreeUpdate() &&
!document->View()->NeedsLayout());
// Needed for computing MinPreferredWidth.
FontCachePurgePreventer fontCachePurgePreventer;
int width_scaled = document->GetLayoutView()
->PreferredLogicalWidths()
.min_size.Round(); // Already accounts for zoom.
int height_scaled =
document->documentElement()->GetLayoutBox()->ScrollHeight().Round();
return gfx::Size(width_scaled, height_scaled);
}
void WebViewImpl::UpdatePreferredSize() {
// We don't always want to send the change messages over IPC, only if we've
// been put in that mode by getting a |ViewMsg_EnablePreferredSizeChangedMode|
// message.
if (!send_preferred_size_changes_ || !MainFrameImpl())
return;
if (!needs_preferred_size_update_)
return;
needs_preferred_size_update_ = false;
gfx::Size size_in_dips =
MainFrameImpl()->LocalRootFrameWidget()->BlinkSpaceToFlooredDIPs(
gfx::Size(ContentsPreferredMinimumSize()));
if (size_in_dips != preferred_size_in_dips_) {
preferred_size_in_dips_ = size_in_dips;
local_main_frame_host_remote_->ContentsPreferredSizeChanged(size_in_dips);
}
}
void WebViewImpl::EnablePreferredSizeChangedMode() {
if (send_preferred_size_changes_)
return;
send_preferred_size_changes_ = true;
needs_preferred_size_update_ = true;
// We need to ensure |UpdatePreferredSize| gets called. If a layout is needed,
// force an update here which will call |DidUpdateMainFrameLayout|.
if (MainFrameWidget()) {
MainFrameWidget()->UpdateLifecycle(WebLifecycleUpdate::kLayout,
DocumentUpdateReason::kSizeChange);
}
// If a layout was not needed, |DidUpdateMainFrameLayout| will not be called.
// We explicitly update the preferred size here to ensure the preferred size
// notification is sent.
UpdatePreferredSize();
}
void WebViewImpl::Focus() {
if (GetPage()->MainFrame()->IsLocalFrame()) {
DCHECK(local_main_frame_host_remote_);
local_main_frame_host_remote_->FocusPage();
} else {
DCHECK(remote_main_frame_host_remote_);
remote_main_frame_host_remote_->FocusPage();
}
}
void WebViewImpl::TakeFocus(bool reverse) {
if (GetPage()->MainFrame()->IsLocalFrame()) {
DCHECK(local_main_frame_host_remote_);
local_main_frame_host_remote_->TakeFocus(reverse);
} else {
DCHECK(remote_main_frame_host_remote_);
remote_main_frame_host_remote_->TakeFocus(reverse);
}
}
void WebViewImpl::Show(const LocalFrameToken& opener_frame_token,
NavigationPolicy policy,
const gfx::Rect& rect,
bool opened_by_user_gesture) {
// This is only called on local main frames.
DCHECK(local_main_frame_host_remote_);
DCHECK(web_widget_);
web_widget_->SetPendingWindowRect(rect);
local_main_frame_host_remote_->ShowCreatedWindow(
opener_frame_token, NavigationPolicyToDisposition(policy), rect,
opened_by_user_gesture,
WTF::Bind(&WebViewImpl::DidShowCreatedWindow, WTF::Unretained(this)));
MainFrameDevToolsAgentImpl()->DidShowNewWindow();
}
void WebViewImpl::DidShowCreatedWindow() {
web_widget_->AckPendingWindowRect();
}
void WebViewImpl::SendWindowRectToMainFrameHost(
const gfx::Rect& bounds,
base::OnceClosure ack_callback) {
DCHECK(local_main_frame_host_remote_);
local_main_frame_host_remote_->SetWindowRect(bounds, std::move(ack_callback));
}
void WebViewImpl::UpdateTargetURL(const WebURL& url,
const WebURL& fallback_url) {
KURL latest_url = KURL(url.IsEmpty() ? fallback_url : url);
if (latest_url == target_url_)
return;
// Tell the browser to display a destination link.
if (target_url_status_ == TARGET_INFLIGHT ||
target_url_status_ == TARGET_PENDING) {
// If we have a request in-flight, save the URL to be sent when we
// receive an ACK to the in-flight request. We can happily overwrite
// any existing pending sends.
pending_target_url_ = latest_url;
target_url_status_ = TARGET_PENDING;
} else {
// URLs larger than |kMaxURLChars| cannot be sent through IPC -
// see |ParamTraits<GURL>|.
if (latest_url.GetString().length() > url::kMaxURLChars)
latest_url = KURL();
SendUpdatedTargetURLToBrowser(latest_url);
target_url_ = latest_url;
target_url_status_ = TARGET_INFLIGHT;
}
}
void WebViewImpl::SendUpdatedTargetURLToBrowser(const KURL& target_url) {
// Note: WTF::Unretained() usage below is safe, since `this` owns both
// `mojo::Remote` objects.
if (GetPage()->MainFrame()->IsLocalFrame()) {
DCHECK(local_main_frame_host_remote_);
local_main_frame_host_remote_->UpdateTargetURL(
target_url, WTF::Bind(&WebViewImpl::TargetURLUpdatedInBrowser,
WTF::Unretained(this)));
} else {
DCHECK(remote_main_frame_host_remote_);
remote_main_frame_host_remote_->UpdateTargetURL(
target_url, WTF::Bind(&WebViewImpl::TargetURLUpdatedInBrowser,
WTF::Unretained(this)));
}
}
void WebViewImpl::TargetURLUpdatedInBrowser() {
// Check if there is a targeturl waiting to be sent.
if (target_url_status_ == TARGET_PENDING)
SendUpdatedTargetURLToBrowser(pending_target_url_);
target_url_status_ = TARGET_NONE;
}
float WebViewImpl::DefaultMinimumPageScaleFactor() const {
return GetPageScaleConstraintsSet().DefaultConstraints().minimum_scale;
}
float WebViewImpl::DefaultMaximumPageScaleFactor() const {
return GetPageScaleConstraintsSet().DefaultConstraints().maximum_scale;
}
float WebViewImpl::MinimumPageScaleFactor() const {
return GetPageScaleConstraintsSet().FinalConstraints().minimum_scale;
}
float WebViewImpl::MaximumPageScaleFactor() const {
return GetPageScaleConstraintsSet().FinalConstraints().maximum_scale;
}
void WebViewImpl::ResetScaleStateImmediately() {
GetPageScaleConstraintsSet().SetNeedsReset(true);
}
void WebViewImpl::ResetScrollAndScaleState() {
GetPage()->GetVisualViewport().Reset();
auto* main_local_frame = DynamicTo<LocalFrame>(GetPage()->MainFrame());
if (!main_local_frame)
return;
if (LocalFrameView* frame_view = main_local_frame->View()) {
ScrollableArea* scrollable_area = frame_view->LayoutViewport();
if (!scrollable_area->GetScrollOffset().IsZero()) {
scrollable_area->SetScrollOffset(ScrollOffset(),
mojom::blink::ScrollType::kProgrammatic);
}
}
if (Document* document = main_local_frame->GetDocument()) {
if (DocumentLoader* loader = document->Loader()) {
if (HistoryItem* item = loader->GetHistoryItem())
item->ClearViewState();
}
}
GetPageScaleConstraintsSet().SetNeedsReset(true);
}
void WebViewImpl::SendResizeEventForMainFrame() {
// FIXME: This is wrong. The LocalFrameView is responsible sending a
// resizeEvent as part of layout. Layout is also responsible for sending
// invalidations to the embedder. This method and all callers may be wrong. --
// eseidel.
if (MainFrameImpl()->GetFrameView()) {
// Enqueues the resize event.
MainFrameImpl()->GetFrame()->GetDocument()->EnqueueResizeEvent();
}
// A resized main frame can change the page scale limits.
if (does_composite_) {
auto& viewport = GetPage()->GetVisualViewport();
MainFrameImpl()->FrameWidgetImpl()->SetPageScaleStateAndLimits(
viewport.Scale(), viewport.IsPinchGestureActive(),
MinimumPageScaleFactor(), MaximumPageScaleFactor());
}
}
void WebViewImpl::ConfigureAutoResizeMode() {
if (!MainFrameImpl() || !MainFrameImpl()->GetFrame() ||
!MainFrameImpl()->GetFrame()->View())
return;
if (should_auto_resize_) {
MainFrameImpl()->GetFrame()->View()->EnableAutoSizeMode(min_auto_size_,
max_auto_size_);
} else {
MainFrameImpl()->GetFrame()->View()->DisableAutoSizeMode();
}
}
void WebViewImpl::SetCompositorDeviceScaleFactorOverride(
float device_scale_factor) {
if (compositor_device_scale_factor_override_ == device_scale_factor)
return;
compositor_device_scale_factor_override_ = device_scale_factor;
if (zoom_factor_for_device_scale_factor_) {
SetZoomLevel(ZoomLevel());
return;
}
}
void WebViewImpl::SetDeviceEmulationTransform(
const TransformationMatrix& transform) {
if (transform == device_emulation_transform_)
return;
device_emulation_transform_ = transform;
UpdateDeviceEmulationTransform();
}
TransformationMatrix WebViewImpl::GetDeviceEmulationTransform() const {
return device_emulation_transform_;
}
void WebViewImpl::EnableDeviceEmulation(const DeviceEmulationParams& params) {
web_widget_->EnableDeviceEmulation(params);
}
void WebViewImpl::ActivateDevToolsTransform(
const DeviceEmulationParams& params) {
TransformationMatrix device_emulation_transform =
dev_tools_emulator_->EnableDeviceEmulation(params);
SetDeviceEmulationTransform(device_emulation_transform);
}
void WebViewImpl::DisableDeviceEmulation() {
web_widget_->DisableDeviceEmulation();
}
void WebViewImpl::DeactivateDevToolsTransform() {
dev_tools_emulator_->DisableDeviceEmulation();
SetDeviceEmulationTransform(TransformationMatrix());
}
void WebViewImpl::PerformCustomContextMenuAction(unsigned action) {
if (page_) {
page_->GetContextMenuController().CustomContextMenuItemSelected(action);
}
}
void WebViewImpl::DidCloseContextMenu() {
LocalFrame* frame = page_->GetFocusController().FocusedFrame();
if (frame)
frame->Selection().SetCaretBlinkingSuspended(false);
}
SkColor WebViewImpl::BackgroundColor() const {
if (background_color_override_enabled_)
return background_color_override_;
Page* page = page_.Get();
if (!page)
return BaseBackgroundColor().Rgb();
if (auto* main_local_frame = DynamicTo<LocalFrame>(page->MainFrame())) {
LocalFrameView* view = main_local_frame->View();
if (view)
return view->DocumentBackgroundColor().Rgb();
}
return BaseBackgroundColor().Rgb();
}
Color WebViewImpl::BaseBackgroundColor() const {
return base_background_color_override_enabled_
? base_background_color_override_
: base_background_color_;
}
void WebViewImpl::SetBaseBackgroundColor(SkColor color) {
if (base_background_color_ == color)
return;
base_background_color_ = color;
UpdateBaseBackgroundColor();
}
void WebViewImpl::SetBaseBackgroundColorOverride(SkColor color) {
if (base_background_color_override_enabled_ &&
base_background_color_override_ == color) {
return;
}
base_background_color_override_enabled_ = true;
base_background_color_override_ = color;
if (MainFrameImpl()) {
// Force lifecycle update to ensure we're good to call
// LocalFrameView::setBaseBackgroundColor().
MainFrameImpl()
->GetFrame()
->View()
->UpdateLifecycleToCompositingCleanPlusScrolling(
DocumentUpdateReason::kBaseColor);
}
UpdateBaseBackgroundColor();
}
void WebViewImpl::ClearBaseBackgroundColorOverride() {
if (!base_background_color_override_enabled_)
return;
base_background_color_override_enabled_ = false;
if (MainFrameImpl()) {
// Force lifecycle update to ensure we're good to call
// LocalFrameView::setBaseBackgroundColor().
MainFrameImpl()
->GetFrame()
->View()
->UpdateLifecycleToCompositingCleanPlusScrolling(
DocumentUpdateReason::kBaseColor);
}
UpdateBaseBackgroundColor();
}
void WebViewImpl::UpdateBaseBackgroundColor() {
Color color = BaseBackgroundColor();
if (auto* local_frame = DynamicTo<LocalFrame>(page_->MainFrame())) {
LocalFrameView* view = local_frame->View();
view->UpdateBaseBackgroundColorRecursively(color);
}
}
void WebViewImpl::UpdateFontRenderingFromRendererPrefs() {
#if !defined(OS_MAC)
skia::LegacyDisplayGlobals::SetCachedPixelGeometry(
gfx::FontRenderParams::SubpixelRenderingToSkiaPixelGeometry(
renderer_preferences_.subpixel_rendering));
#if defined(OS_WIN)
// Cache the system font metrics in blink.
WebFontRendering::SetMenuFontMetrics(
WebString::FromUTF16(renderer_preferences_.menu_font_family_name),
renderer_preferences_.menu_font_height);
WebFontRendering::SetSmallCaptionFontMetrics(
WebString::FromUTF16(
renderer_preferences_.small_caption_font_family_name),
renderer_preferences_.small_caption_font_height);
WebFontRendering::SetStatusFontMetrics(
WebString::FromUTF16(renderer_preferences_.status_font_family_name),
renderer_preferences_.status_font_height);
WebFontRendering::SetAntialiasedTextEnabled(
renderer_preferences_.should_antialias_text);
WebFontRendering::SetLCDTextEnabled(
renderer_preferences_.subpixel_rendering !=
gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE);
#else
WebFontRenderStyle::SetHinting(
RendererPreferencesToSkiaHinting(renderer_preferences_));
WebFontRenderStyle::SetAutoHint(renderer_preferences_.use_autohinter);
WebFontRenderStyle::SetUseBitmaps(renderer_preferences_.use_bitmaps);
WebFontRenderStyle::SetAntiAlias(renderer_preferences_.should_antialias_text);
WebFontRenderStyle::SetSubpixelRendering(
renderer_preferences_.subpixel_rendering !=
gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE);
WebFontRenderStyle::SetSubpixelPositioning(
renderer_preferences_.use_subpixel_positioning);
// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
// complete.
#if (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && !defined(OS_ANDROID)
if (!renderer_preferences_.system_font_family_name.empty()) {
WebFontRenderStyle::SetSystemFontFamily(blink::WebString::FromUTF8(
renderer_preferences_.system_font_family_name));
}
#endif // (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) &&
// !defined(OS_ANDROID)
#endif // defined(OS_WIN)
#endif // !defined(OS_MAC)
}
void WebViewImpl::SetInsidePortal(bool inside_portal) {
GetPage()->SetInsidePortal(inside_portal);
// We may not have created the frame widget yet but that's ok because it'll
// be created with this value correctly initialized. This can also be null if
// the main frame is remote.
if (web_widget_)
web_widget_->SetIsNestedMainFrameWidget(inside_portal);
}
void WebViewImpl::RegisterRendererPreferenceWatcher(
CrossVariantMojoRemote<mojom::RendererPreferenceWatcherInterfaceBase>
watcher) {
renderer_preference_watchers_.Add(std::move(watcher));
}
void WebViewImpl::SetRendererPreferences(
const RendererPreferences& preferences) {
UpdateRendererPreferences(preferences);
}
const RendererPreferences& WebViewImpl::GetRendererPreferences() {
return renderer_preferences_;
}
void WebViewImpl::UpdateRendererPreferences(
const RendererPreferences& preferences) {
std::string old_accept_languages = renderer_preferences_.accept_languages;
renderer_preferences_ = preferences;
for (auto& watcher : renderer_preference_watchers_)
watcher->NotifyUpdate(renderer_preferences_);
// TODO(crbug.com/1102442): Remove once we no longer need to update theme
// preferences on Windows via content::WebThemeEngineDefault.
web_view_client_->DidUpdateRendererPreferences();
UpdateFontRenderingFromRendererPrefs();
blink::SetCaretBlinkInterval(
renderer_preferences_.caret_blink_interval.has_value()
? renderer_preferences_.caret_blink_interval.value()
: base::TimeDelta::FromMilliseconds(
mojom::blink::kDefaultCaretBlinkIntervalInMilliseconds));
#if defined(USE_AURA)
if (renderer_preferences_.use_custom_colors) {
SetFocusRingColor(renderer_preferences_.focus_ring_color);
SetSelectionColors(renderer_preferences_.active_selection_bg_color,
renderer_preferences_.active_selection_fg_color,
renderer_preferences_.inactive_selection_bg_color,
renderer_preferences_.inactive_selection_fg_color);
ThemeChanged();
}
#endif
if (::features::IsFormControlsRefreshEnabled() &&
renderer_preferences_.use_custom_colors) {
SetFocusRingColor(renderer_preferences_.focus_ring_color);
}
if (old_accept_languages != renderer_preferences_.accept_languages)
AcceptLanguagesChanged();
GetSettings()->SetCaretBrowsingEnabled(
renderer_preferences_.caret_browsing_enabled);
#if defined(USE_X11) || defined(USE_OZONE)
GetSettings()->SetSelectionClipboardBufferAvailable(
renderer_preferences_.selection_clipboard_buffer_available);
#endif // defined(USE_X11) || defined(USE_OZONE)
}
void WebViewImpl::SetHistoryOffsetAndLength(int32_t history_offset,
int32_t history_length) {
DCHECK(web_view_client_);
web_view_client_->OnSetHistoryOffsetAndLength(history_offset, history_length);
}
void WebViewImpl::SetWebPreferences(
const web_pref::WebPreferences& preferences) {
UpdateWebPreferences(preferences);
}
const web_pref::WebPreferences& WebViewImpl::GetWebPreferences() {
return web_preferences_;
}
void WebViewImpl::UpdateWebPreferences(
const blink::web_pref::WebPreferences& preferences) {
web_preferences_ = preferences;
ApplyWebPreferences(preferences, this);
ApplyCommandLineToSettings(SettingsImpl());
}
void WebViewImpl::AddObserver(WebViewObserver* observer) {
observers_.AddObserver(observer);
}
void WebViewImpl::RemoveObserver(WebViewObserver* observer) {
observers_.RemoveObserver(observer);
}
void WebViewImpl::SetIsActive(bool active) {
if (GetPage())
GetPage()->GetFocusController().SetActive(active);
}
bool WebViewImpl::IsActive() const {
return GetPage() ? GetPage()->GetFocusController().IsActive() : false;
}
void WebViewImpl::SetWindowFeatures(const WebWindowFeatures& features) {
page_->SetWindowFeatures(features);
}
void WebViewImpl::SetOpenedByDOM() {
page_->SetOpenedByDOM();
}
void WebViewImpl::DidCommitLoad(bool is_new_navigation,
bool is_navigation_within_page) {
if (!is_navigation_within_page) {
if (web_widget_)
web_widget_->ResetMeaningfulLayoutStateForMainFrame();
if (is_new_navigation)
GetPageScaleConstraintsSet().SetNeedsReset(true);
}
// Give the visual viewport's scroll layer its initial size.
GetPage()->GetVisualViewport().MainFrameDidChangeSize();
}
void WebViewImpl::DidCommitCompositorFrameForLocalMainFrame() {
for (auto& observer : observers_)
observer.DidCommitCompositorFrame();
}
void WebViewImpl::ResizeAfterLayout() {
DCHECK(MainFrameImpl());
if (!web_view_client_ || !web_view_client_->CanUpdateLayout())
return;
if (should_auto_resize_) {
LocalFrameView* view = MainFrameImpl()->GetFrame()->View();
gfx::Size frame_size = gfx::Size(view->Size());
if (frame_size != size_) {
size_ = frame_size;
GetPage()->GetVisualViewport().SetSize(IntSize(size_));
GetPageScaleConstraintsSet().DidChangeInitialContainingBlockSize(
IntSize(size_));
view->SetInitialViewportSize(IntSize(size_));
web_view_client_->DidAutoResize(size_);
web_widget_->DidAutoResize(size_);
SendResizeEventForMainFrame();
}
}
if (does_composite_ && GetPageScaleConstraintsSet().ConstraintsDirty())
RefreshPageScaleFactor();
resize_viewport_anchor_->ResizeFrameView(MainFrameSize());
}
void WebViewImpl::MainFrameLayoutUpdated() {
DCHECK(MainFrameImpl());
if (!web_view_client_)
return;
for (auto& observer : observers_)
observer.DidUpdateMainFrameLayout();
needs_preferred_size_update_ = true;
}
void WebViewImpl::DidChangeContentsSize() {
auto* local_frame = DynamicTo<LocalFrame>(GetPage()->MainFrame());
if (!local_frame)
return;
LocalFrameView* view = local_frame->View();
int vertical_scrollbar_width = 0;
if (view && view->LayoutViewport()) {
Scrollbar* vertical_scrollbar = view->LayoutViewport()->VerticalScrollbar();
if (vertical_scrollbar && !vertical_scrollbar->IsOverlayScrollbar())
vertical_scrollbar_width = vertical_scrollbar->Width();
}
GetPageScaleConstraintsSet().DidChangeContentsSize(
ContentsSize(), vertical_scrollbar_width, PageScaleFactor());
}
void WebViewImpl::PageScaleFactorChanged() {
// This is called from the VisualViewport which only is used to control the
// page scale/scroll viewport for a local main frame, and only when
// compositing as PageScaleFactor doesn't exist otherwise.
DCHECK(MainFrameImpl());
DCHECK(does_composite_);
GetPageScaleConstraintsSet().SetNeedsReset(false);
// Set up the compositor and inform the browser of the PageScaleFactor,
// which is tracked per-view.
auto& viewport = GetPage()->GetVisualViewport();
MainFrameImpl()->FrameWidgetImpl()->SetPageScaleStateAndLimits(
viewport.Scale(), viewport.IsPinchGestureActive(),
MinimumPageScaleFactor(), MaximumPageScaleFactor());
local_main_frame_host_remote_->ScaleFactorChanged(viewport.Scale());
if (dev_tools_emulator_->HasViewportOverride()) {
TransformationMatrix device_emulation_transform =
dev_tools_emulator_->MainFrameScrollOrScaleChanged();
SetDeviceEmulationTransform(device_emulation_transform);
}
}
void WebViewImpl::MainFrameScrollOffsetChanged() {
DCHECK(MainFrameImpl());
if (dev_tools_emulator_->HasViewportOverride()) {
TransformationMatrix device_emulation_transform =
dev_tools_emulator_->MainFrameScrollOrScaleChanged();
SetDeviceEmulationTransform(device_emulation_transform);
}
}
void WebViewImpl::TextAutosizerPageInfoChanged(
const mojom::blink::TextAutosizerPageInfo& page_info) {
DCHECK(MainFrameImpl());
local_main_frame_host_remote_->TextAutosizerPageInfoChanged(
page_info.Clone());
}
void WebViewImpl::SetBackgroundColorOverride(SkColor color) {
DCHECK(does_composite_);
background_color_override_enabled_ = true;
background_color_override_ = color;
if (MainFrameImpl()) {
MainFrameImpl()->FrameWidgetImpl()->SetBackgroundColor(BackgroundColor());
}
}
void WebViewImpl::ClearBackgroundColorOverride() {
DCHECK(does_composite_);
background_color_override_enabled_ = false;
if (MainFrameImpl()) {
MainFrameImpl()->FrameWidgetImpl()->SetBackgroundColor(BackgroundColor());
}
}
void WebViewImpl::SetZoomFactorOverride(float zoom_factor) {
zoom_factor_override_ = zoom_factor;
SetZoomLevel(ZoomLevel());
}
Element* WebViewImpl::FocusedElement() const {
LocalFrame* frame = page_->GetFocusController().FocusedFrame();
if (!frame)
return nullptr;
Document* document = frame->GetDocument();
if (!document)
return nullptr;
return document->FocusedElement();
}
WebHitTestResult WebViewImpl::HitTestResultForTap(
const gfx::Point& tap_point_window_pos,
const gfx::Size& tap_area) {
auto* main_frame = DynamicTo<LocalFrame>(page_->MainFrame());
if (!main_frame)
return HitTestResult();
WebGestureEvent tap_event(WebInputEvent::Type::kGestureTap,
WebInputEvent::kNoModifiers, base::TimeTicks::Now(),
WebGestureDevice::kTouchscreen);
// GestureTap is only ever from a touchscreen.
tap_event.SetPositionInWidget(FloatPoint(IntPoint(tap_point_window_pos)));
tap_event.data.tap.tap_count = 1;
tap_event.data.tap.width = tap_area.width();
tap_event.data.tap.height = tap_area.height();
WebGestureEvent scaled_event =
TransformWebGestureEvent(MainFrameImpl()->GetFrameView(), tap_event);
HitTestResult result =
main_frame->GetEventHandler()
.HitTestResultForGestureEvent(
scaled_event, HitTestRequest::kReadOnly | HitTestRequest::kActive)
.GetHitTestResult();
result.SetToShadowHostIfInRestrictedShadowRoot();
return result;
}
void WebViewImpl::SetTabsToLinks(bool enable) {
tabs_to_links_ = enable;
}
bool WebViewImpl::TabsToLinks() const {
return tabs_to_links_;
}
void WebViewImpl::DidChangeRootLayer(bool root_layer_exists) {
// The Layer is removed when the main frame's `Document` changes. It also is
// removed when the whole `LocalFrame` goes away, in which case we don't
// need to DeferMainFrameUpdate() as we will do so if a local MainFrame is
// attached in the future.
if (!MainFrameImpl()) {
DCHECK(!root_layer_exists);
return;
}
if (root_layer_exists) {
if (!device_emulation_transform_.IsIdentity())
UpdateDeviceEmulationTransform();
} else {
// When the document in an already-attached main frame is being replaced by
// a navigation then DidChangeRootLayer(false) will be called. Since we are
// navigating, defer BeginMainFrames until the new document is ready for
// them.
//
// TODO(crbug.com/936696): This should not be needed once we always swap
// frames when swapping documents.
scoped_defer_main_frame_update_ =
MainFrameImpl()->FrameWidgetImpl()->DeferMainFrameUpdate();
}
}
void WebViewImpl::InvalidateRect(const IntRect& rect) {
// This is only for WebViewPlugin.
if (!does_composite_ && web_view_client_)
web_view_client_->DidInvalidateRect(rect);
}
void WebViewImpl::ApplyViewportChanges(const ApplyViewportChangesArgs& args) {
// TODO(https://crbug.com/1160652): Figure out if Page is null.
CHECK(page_);
VisualViewport& visual_viewport = GetPage()->GetVisualViewport();
// Store the desired offsets the visual viewport before setting the top
// controls ratio since doing so will change the bounds and move the
// viewports to keep the offsets valid. The compositor may have already
// done that so we don't want to double apply the deltas here.
FloatPoint visual_viewport_offset = visual_viewport.VisibleRect().Location();
visual_viewport_offset.Move(args.inner_delta.x(), args.inner_delta.y());
GetBrowserControls().SetShownRatio(
GetBrowserControls().TopShownRatio() + args.top_controls_delta,
GetBrowserControls().BottomShownRatio() + args.bottom_controls_delta);
SetPageScaleFactorAndLocation(PageScaleFactor() * args.page_scale_delta,
args.is_pinch_gesture_active,
visual_viewport_offset);
if (args.page_scale_delta != 1) {
double_tap_zoom_pending_ = false;
visual_viewport.UserDidChangeScale();
}
elastic_overscroll_ += FloatSize(args.elastic_overscroll_delta.x(),
args.elastic_overscroll_delta.y());
UpdateBrowserControlsConstraint(args.browser_controls_constraint);
if (args.scroll_gesture_did_end) {
// TODO(https://crbug.com/1160652): Figure out if MainFrameImpl is null.
CHECK(MainFrameImpl());
MainFrameImpl()->GetFrame()->GetEventHandler().MarkHoverStateDirty();
}
}
Node* WebViewImpl::FindNodeFromScrollableCompositorElementId(
cc::ElementId element_id) const {
if (!GetPage())
return nullptr;
if (element_id == GetPage()->GetVisualViewport().GetScrollElementId()) {
// Return the Document in this case since the window.visualViewport DOM
// object is not a node.
if (MainFrameImpl())
return MainFrameImpl()->GetDocument();
}
if (!GetPage()->GetScrollingCoordinator())
return nullptr;
ScrollableArea* scrollable_area =
GetPage()
->GetScrollingCoordinator()
->ScrollableAreaWithElementIdInAllLocalFrames(element_id);
if (!scrollable_area || !scrollable_area->GetLayoutBox())
return nullptr;
return scrollable_area->GetLayoutBox()->GetNode();
}
void WebViewImpl::UpdateDeviceEmulationTransform() {
GetPage()->GetVisualViewport().SetNeedsPaintPropertyUpdate();
if (auto* main_frame = MainFrameImpl()) {
// When the device emulation transform is updated, to avoid incorrect
// scales and fuzzy raster from the compositor, force all content to
// pick ideal raster scales.
// TODO(wjmaclean): This is only done on the main frame's widget currently,
// it should update all local frames.
main_frame->FrameWidgetImpl()->SetNeedsRecalculateRasterScales();
// Device emulation transform also affects the overriding visible rect
// which is used as the overflow rect of the main frame layout view.
if (auto* view = main_frame->GetFrameView())
view->SetNeedsPaintPropertyUpdate();
}
}
PageScheduler* WebViewImpl::Scheduler() const {
DCHECK(GetPage());
return GetPage()->GetPageScheduler();
}
void WebViewImpl::SetVisibilityState(
mojom::blink::PageVisibilityState visibility_state,
bool is_initial_state) {
DCHECK(GetPage());
if (!is_initial_state) {
// Preserve the side effects of visibility change.
web_view_client_->OnPageVisibilityChanged(visibility_state);
for (auto& observer : observers_)
observer.OnPageVisibilityChanged(visibility_state);
}
GetPage()->SetVisibilityState(visibility_state, is_initial_state);
GetPage()->GetPageScheduler()->SetPageVisible(
visibility_state == mojom::blink::PageVisibilityState::kVisible);
}
mojom::blink::PageVisibilityState WebViewImpl::GetVisibilityState() {
DCHECK(GetPage());
return GetPage()->GetVisibilityState();
}
float WebViewImpl::DeviceScaleFactor() const {
// TODO(oshima): Investigate if this should return the ScreenInfo's scale
// factor rather than page's scale factor, which can be 1 in use-zoom-for-dsf
// mode.
if (!GetPage())
return 1;
return GetPage()->DeviceScaleFactorDeprecated();
}
LocalFrame* WebViewImpl::FocusedLocalFrameInWidget() const {
if (!MainFrameImpl())
return nullptr;
auto* focused_frame = To<LocalFrame>(FocusedCoreFrame());
if (focused_frame->LocalFrameRoot() != MainFrameImpl()->GetFrame())
return nullptr;
return focused_frame;
}
void WebViewImpl::SetPageFrozen(bool frozen) {
Scheduler()->SetPageFrozen(frozen);
web_view_client_->OnPageFrozenChanged(frozen);
}
WebFrameWidget* WebViewImpl::MainFrameWidget() {
return web_widget_;
}
void WebViewImpl::AddAutoplayFlags(int32_t value) {
page_->AddAutoplayFlags(value);
}
void WebViewImpl::ClearAutoplayFlags() {
page_->ClearAutoplayFlags();
}
int32_t WebViewImpl::AutoplayFlagsForTest() {
return page_->AutoplayFlags();
}
gfx::Size WebViewImpl::GetPreferredSizeForTest() {
return preferred_size_in_dips_;
}
void WebViewImpl::StopDeferringMainFrameUpdate() {
DCHECK(MainFrameImpl());
scoped_defer_main_frame_update_ = nullptr;
}
void WebViewImpl::SetDeviceColorSpaceForTesting(
const gfx::ColorSpace& color_space) {
web_widget_->SetDeviceColorSpaceForTesting(color_space);
}
} // namespace blink