blob: 4dbdc2d4e7c916ed3fd1ed1214bac673220bbec8 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/render_frame_impl.h"
#include <algorithm>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/debug/alias.h"
#include "base/debug/asan_invalid_access.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/files/file.h"
#include "base/guid.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/process/process.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/strcat.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/trace_event/base_tracing.h"
#include "base/trace_event/trace_event.h"
#include "base/unguessable_token.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "cc/base/switches.h"
#include "cc/trees/ukm_manager.h"
#include "content/common/associated_interfaces.mojom.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/debug_utils.h"
#include "content/common/frame.mojom.h"
#include "content/common/frame_messages.mojom.h"
#include "content/common/navigation_client.mojom.h"
#include "content/common/navigation_gesture.h"
#include "content/common/navigation_params_utils.h"
#include "content/common/render_accessibility.mojom.h"
#include "content/common/renderer_host.mojom.h"
#include "content/common/web_package/signed_exchange_utils.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
#include "content/public/renderer/content_renderer_client.h"
#include "content/public/renderer/document_state.h"
#include "content/public/renderer/render_frame_observer.h"
#include "content/public/renderer/render_frame_visitor.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/renderer_ppapi_host.h"
#include "content/renderer/accessibility/aom_content_ax_tree.h"
#include "content/renderer/accessibility/ax_tree_snapshotter_impl.h"
#include "content/renderer/accessibility/render_accessibility_impl.h"
#include "content/renderer/accessibility/render_accessibility_manager.h"
#include "content/renderer/agent_scheduling_group.h"
#include "content/renderer/content_security_policy_util.h"
#include "content/renderer/dom_automation_controller.h"
#include "content/renderer/effective_connection_type_helper.h"
#include "content/renderer/frame_owner_properties_converter.h"
#include "content/renderer/gpu_benchmarking_extension.h"
#include "content/renderer/internal_document_state_data.h"
#include "content/renderer/loader/navigation_body_loader.h"
#include "content/renderer/media/media_permission_dispatcher.h"
#include "content/renderer/mhtml_handle_writer.h"
#include "content/renderer/mojo/blink_interface_registry_impl.h"
#include "content/renderer/navigation_client.h"
#include "content/renderer/navigation_state.h"
#include "content/renderer/pepper/pepper_audio_controller.h"
#include "content/renderer/render_frame_proxy.h"
#include "content/renderer/render_process.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/render_view_impl.h"
#include "content/renderer/renderer_blink_platform_impl.h"
#include "content/renderer/service_worker/service_worker_network_provider_for_frame.h"
#include "content/renderer/service_worker/web_service_worker_provider_impl.h"
#include "content/renderer/skia_benchmarking_extension.h"
#include "content/renderer/stats_collection_controller.h"
#include "content/renderer/v8_value_converter_impl.h"
#include "content/renderer/web_ui_extension.h"
#include "content/renderer/web_ui_extension_data.h"
#include "content/renderer/worker/dedicated_worker_host_factory_client.h"
#include "crypto/sha2.h"
#include "ipc/ipc_message.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/data_url.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_util.h"
#include "ppapi/buildflags/buildflags.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/not_implemented_url_loader_factory.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/action_after_pagehide.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/chrome_debug_urls.h"
#include "third_party/blink/public/common/context_menu_data/context_menu_data.h"
#include "third_party/blink/public/common/context_menu_data/untrustworthy_context_menu_params.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/input/web_keyboard_event.h"
#include "third_party/blink/public/common/loader/loader_constants.h"
#include "third_party/blink/public/common/loader/record_load_histograms.h"
#include "third_party/blink/public/common/loader/resource_type_util.h"
#include "third_party/blink/public/common/loader/url_loader_throttle.h"
#include "third_party/blink/public/common/logging/logging_utils.h"
#include "third_party/blink/public/common/navigation/impression.h"
#include "third_party/blink/public/common/navigation/navigation_params_mojom_traits.h"
#include "third_party/blink/public/common/navigation/navigation_policy.h"
#include "third_party/blink/public/common/page_state/page_state.h"
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
#include "third_party/blink/public/mojom/blob/blob.mojom.h"
#include "third_party/blink/public/mojom/blob/blob_url_store.mojom.h"
#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
#include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h"
#include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom.h"
#include "third_party/blink/public/mojom/frame/user_activation_update_types.mojom.h"
#include "third_party/blink/public/mojom/input/focus_type.mojom.h"
#include "third_party/blink/public/mojom/input/input_handler.mojom-shared.h"
#include "third_party/blink/public/mojom/loader/referrer.mojom.h"
#include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
#include "third_party/blink/public/mojom/navigation/navigation_params.mojom.h"
#include "third_party/blink/public/mojom/page/widget.mojom.h"
#include "third_party/blink/public/mojom/permissions/permission.mojom.h"
#include "third_party/blink/public/platform/file_path_conversion.h"
#include "third_party/blink/public/platform/impression_conversions.h"
#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
#include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h"
#include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
#include "third_party/blink/public/platform/tracked_child_url_loader_factory_bundle.h"
#include "third_party/blink/public/platform/url_conversion.h"
#include "third_party/blink/public/platform/weak_wrapper_resource_load_info_notifier.h"
#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h"
#include "third_party/blink/public/platform/web_data.h"
#include "third_party/blink/public/platform/web_dedicated_or_shared_worker_fetch_context.h"
#include "third_party/blink/public/platform/web_http_body.h"
#include "third_party/blink/public/platform/web_media_player.h"
#include "third_party/blink/public/platform/web_media_player_source.h"
#include "third_party/blink/public/platform/web_resource_request_sender.h"
#include "third_party/blink/public/platform/web_runtime_features.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_error.h"
#include "third_party/blink/public/platform/web_url_loader.h"
#include "third_party/blink/public/platform/web_url_request_extra_data.h"
#include "third_party/blink/public/platform/web_url_request_util.h"
#include "third_party/blink/public/platform/web_url_response.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/public/web/blink.h"
#include "third_party/blink/public/web/modules/media/audio/web_audio_device_factory.h"
#include "third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h"
#include "third_party/blink/public/web/modules/media/webmediaplayer_util.h"
#include "third_party/blink/public/web/modules/mediastream/web_media_stream_device_observer.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_document.h"
#include "third_party/blink/public/web/web_element_collection.h"
#include "third_party/blink/public/web/web_frame_owner_properties.h"
#include "third_party/blink/public/web/web_frame_serializer.h"
#include "third_party/blink/public/web/web_frame_widget.h"
#include "third_party/blink/public/web/web_history_entry.h"
#include "third_party/blink/public/web/web_input_method_controller.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_navigation_control.h"
#include "third_party/blink/public/web/web_navigation_policy.h"
#include "third_party/blink/public/web/web_navigation_timings.h"
#include "third_party/blink/public/web/web_performance.h"
#include "third_party/blink/public/web/web_plugin.h"
#include "third_party/blink/public/web/web_plugin_container.h"
#include "third_party/blink/public/web/web_plugin_document.h"
#include "third_party/blink/public/web/web_plugin_params.h"
#include "third_party/blink/public/web/web_range.h"
#include "third_party/blink/public/web/web_savable_resources_test_support.h"
#include "third_party/blink/public/web/web_script_source.h"
#include "third_party/blink/public/web/web_searchable_form_data.h"
#include "third_party/blink/public/web/web_security_policy.h"
#include "third_party/blink/public/web/web_serialized_script_value.h"
#include "third_party/blink/public/web/web_settings.h"
#include "third_party/blink/public/web/web_v8_features.h"
#include "third_party/blink/public/web/web_view.h"
#include "third_party/blink/public/web/web_widget.h"
#include "ui/accessibility/ax_tree_update.h"
#include "ui/events/base_event_utils.h"
#include "url/origin.h"
#include "url/url_constants.h"
#include "url/url_util.h"
#include "v8/include/v8.h"
#if BUILDFLAG(ENABLE_PLUGINS)
#include "content/renderer/pepper/pepper_browser_connection.h"
#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
#include "content/renderer/pepper/pepper_plugin_registry.h"
#include "content/renderer/pepper/pepper_webplugin_impl.h"
#include "content/renderer/pepper/plugin_module.h"
#endif
#if defined(OS_ANDROID)
#include <cpu-features.h>
#include "content/renderer/java/gin_java_bridge_dispatcher.h"
#endif
using base::Time;
using base::TimeDelta;
using blink::ContextMenuData;
using blink::WebContentDecryptionModule;
using blink::WebData;
using blink::WebDocument;
using blink::WebDocumentLoader;
using blink::WebDOMEvent;
using blink::WebDOMMessageEvent;
using blink::WebElement;
using blink::WebElementCollection;
using blink::WebFrame;
using blink::WebFrameLoadType;
using blink::WebFrameSerializer;
using blink::WebFrameSerializerClient;
using blink::WebHistoryItem;
using blink::WebHTTPBody;
using blink::WebLocalFrame;
using blink::WebMediaPlayer;
using blink::WebMediaPlayerClient;
using blink::WebMediaPlayerEncryptedMediaClient;
using blink::WebNavigationParams;
using blink::WebNavigationPolicy;
using blink::WebNavigationType;
using blink::WebNode;
using blink::WebPluginDocument;
using blink::WebPluginParams;
using blink::WebRange;
using blink::WebScriptSource;
using blink::WebSearchableFormData;
using blink::WebSecurityOrigin;
using blink::WebSecurityPolicy;
using blink::WebSerializedScriptValue;
using blink::WebServiceWorkerProvider;
using blink::WebSettings;
using blink::WebString;
using blink::WebThreadSafeData;
using blink::WebURL;
using blink::WebURLError;
using blink::WebURLRequest;
using blink::WebURLResponse;
using blink::WebVector;
using blink::WebView;
using blink::mojom::SelectionMenuBehavior;
using network::mojom::ReferrerPolicy;
namespace content {
namespace {
const int kExtraCharsBeforeAndAfterSelection = 100;
const size_t kMaxURLLogChars = 1024;
// Time, in seconds, we delay before sending content state changes (such as form
// state and scroll position) to the browser. We delay sending changes to avoid
// spamming the browser.
// To avoid having tab/session restore require sending a message to get the
// current content state during tab closing we use a shorter timeout for the
// foreground renderer. This means there is a small window of time from which
// content state is modified and not sent to session restore, but this is
// better than having to wake up all renderers during shutdown.
constexpr base::TimeDelta kDelaySecondsForContentStateSyncHidden =
base::TimeDelta::FromSeconds(5);
constexpr base::TimeDelta kDelaySecondsForContentStateSync =
base::TimeDelta::FromSeconds(1);
const blink::PreviewsState kDisabledPreviewsBits =
blink::PreviewsTypes::PREVIEWS_OFF |
blink::PreviewsTypes::PREVIEWS_NO_TRANSFORM;
typedef std::map<int, RenderFrameImpl*> RoutingIDFrameMap;
static base::LazyInstance<RoutingIDFrameMap>::DestructorAtExit
g_routing_id_frame_map = LAZY_INSTANCE_INITIALIZER;
typedef std::map<blink::WebFrame*, RenderFrameImpl*> FrameMap;
base::LazyInstance<FrameMap>::DestructorAtExit g_frame_map =
LAZY_INSTANCE_INITIALIZER;
int64_t ExtractPostId(const WebHistoryItem& item) {
if (item.IsNull() || item.HttpBody().IsNull())
return -1;
return item.HttpBody().Identifier();
}
std::string TrimURL(const std::string& url) {
if (url.length() <= kMaxURLLogChars)
return url;
return url.substr(0, kMaxURLLogChars - 3) + "...";
}
// Calculates transition type based on navigation parameters. Used
// during navigation, before WebDocumentLoader is available.
ui::PageTransition GetTransitionType(ui::PageTransition default_transition,
bool replaces_current_item,
bool is_main_frame,
WebNavigationType navigation_type) {
if (replaces_current_item && !is_main_frame) {
// Subframe navigations that don't add session history items must be
// marked with AUTO_SUBFRAME. See also DidFailProvisionalLoad for how we
// handle loading of error pages.
return ui::PAGE_TRANSITION_AUTO_SUBFRAME;
}
bool is_form_submit =
navigation_type == blink::kWebNavigationTypeFormSubmitted ||
navigation_type == blink::kWebNavigationTypeFormResubmitted;
if (ui::PageTransitionCoreTypeIs(default_transition,
ui::PAGE_TRANSITION_LINK) &&
is_form_submit) {
return ui::PAGE_TRANSITION_FORM_SUBMIT;
}
return default_transition;
}
// Calculates transition type for the specific document loaded using
// WebDocumentLoader. Used while loading subresources.
ui::PageTransition GetTransitionType(blink::WebDocumentLoader* document_loader,
bool is_main_frame) {
NavigationState* navigation_state =
NavigationState::FromDocumentLoader(document_loader);
ui::PageTransition default_transition =
navigation_state->IsForSynchronousCommit()
? ui::PAGE_TRANSITION_LINK
: ui::PageTransitionFromInt(
navigation_state->common_params().transition);
if (navigation_state->WasWithinSameDocument())
return default_transition;
return GetTransitionType(default_transition,
document_loader->ReplacesCurrentHistoryItem(),
is_main_frame, document_loader->GetNavigationType());
}
// Gets URL that should override the default getter for this data source
// (if any), storing it in |output|. Returns true if there is an override URL.
bool MaybeGetOverriddenURL(WebDocumentLoader* document_loader, GURL* output) {
DocumentState* document_state =
DocumentState::FromDocumentLoader(document_loader);
// If this document is loaded by a loadDataWithBaseURL request, then the URLs
// saved in the DocumentLoader will be the user-supplied base URL (used as the
// "document URL") and history URL (used as the "unreachable URL"/"URL for
// history"). However, we want to return the data: URL (the URL originally
// sent by the browser to commit the navigation) here.
// TODO(https://crbug.com/1223408): Since the DocumentState stays as long as
// the Document stays the same, this means the data: URL will be returned even
// after same-document navigations. Investigate whether this is intended or
// not.
if (document_state->was_load_data_with_base_url_request()) {
*output = document_state->data_url();
return true;
}
// The "unreachable URL" is only set in two cases:
// - An error page, where the "unreachable URL" is set to the URL that failed
// to load. We want the URL bar to show that URL, and the session history
// entry should also use that URL (instead of "chrome-error://chromewebdata"
// which is used as the "document URL" for the DocumentLoader).
// - A loadDataWithBaseURL, where the "unreachable URL" is set to the "history
// URL". This case should never reach this point as it's handled above, where
// we return the original data: URL instead.
if (document_loader->HasUnreachableURL()) {
*output = document_loader->UnreachableURL();
return true;
}
return false;
}
// Returns false unless this is a top-level navigation.
bool IsTopLevelNavigation(WebFrame* frame) {
return frame->Parent() == nullptr;
}
void FillNavigationParamsRequest(
const blink::mojom::CommonNavigationParams& common_params,
const blink::mojom::CommitNavigationParams& commit_params,
blink::WebNavigationParams* navigation_params) {
// Use the original navigation url to start with. We'll replay the redirects
// afterwards and will eventually arrive to the final url.
navigation_params->url = !commit_params.original_url.is_empty()
? commit_params.original_url
: common_params.url;
navigation_params->http_method = WebString::FromASCII(
!commit_params.original_method.empty() ? commit_params.original_method
: common_params.method);
if (common_params.referrer->url.is_valid()) {
WebString referrer = WebSecurityPolicy::GenerateReferrerHeader(
common_params.referrer->policy, common_params.url,
WebString::FromUTF8(common_params.referrer->url.spec()));
navigation_params->referrer = referrer;
navigation_params->referrer_policy = common_params.referrer->policy;
}
if (common_params.referrer->policy !=
network::mojom::ReferrerPolicy::kDefault) {
navigation_params->referrer_policy = common_params.referrer->policy;
}
if (common_params.post_data) {
navigation_params->http_body =
blink::GetWebHTTPBodyForRequestBody(*common_params.post_data);
if (!commit_params.post_content_type.empty()) {
navigation_params->http_content_type =
WebString::FromASCII(commit_params.post_content_type);
}
}
if (common_params.previews_state & kDisabledPreviewsBits) {
// Sanity check disabled vs. enabled bits here before passing on.
DCHECK(!(common_params.previews_state & ~kDisabledPreviewsBits))
<< common_params.previews_state;
}
navigation_params->previews_state =
static_cast<blink::PreviewsState>(common_params.previews_state);
// Set the request initiator origin, which is supplied by the browser
// process. It is present in cases such as navigating a frame in a different
// process, which is routed through RenderFrameProxy and the origin is
// required to correctly compute the effective origin in which the
// navigation will commit.
if (common_params.initiator_origin) {
navigation_params->requestor_origin =
common_params.initiator_origin.value();
}
navigation_params->initiator_origin_trial_features = {
common_params.initiator_origin_trial_features.begin(),
common_params.initiator_origin_trial_features.end()};
navigation_params->was_discarded = commit_params.was_discarded;
navigation_params->document_ukm_source_id =
commit_params.document_ukm_source_id;
if (!commit_params.prefetched_signed_exchanges.empty()) {
navigation_params->prefetched_signed_exchanges = WebVector<std::unique_ptr<
blink::WebNavigationParams::PrefetchedSignedExchange>>();
for (const auto& exchange : commit_params.prefetched_signed_exchanges) {
blink::WebURLResponse web_response;
blink::WebURLLoader::PopulateURLResponse(
exchange->inner_url, *exchange->inner_response, &web_response,
false /* report_security_info*/, -1 /* request_id */);
navigation_params->prefetched_signed_exchanges.emplace_back(
std::make_unique<
blink::WebNavigationParams::PrefetchedSignedExchange>(
exchange->outer_url,
WebString::FromLatin1(
signed_exchange_utils::CreateHeaderIntegrityHashString(
exchange->header_integrity)),
exchange->inner_url, web_response,
std::move(exchange->loader_factory_handle)));
}
}
navigation_params->had_transient_user_activation =
common_params.has_user_gesture;
navigation_params->web_bundle_physical_url =
commit_params.web_bundle_physical_url;
navigation_params->web_bundle_claimed_url =
commit_params.web_bundle_claimed_url;
WebVector<WebString> web_origin_trials;
web_origin_trials.reserve(commit_params.force_enabled_origin_trials.size());
for (const auto& trial : commit_params.force_enabled_origin_trials)
web_origin_trials.emplace_back(WebString::FromASCII(trial));
navigation_params->force_enabled_origin_trials = web_origin_trials;
if (!commit_params.early_hints_preloaded_resources.empty()) {
navigation_params->early_hints_preloaded_resources = WebVector<WebURL>();
for (const auto& resource : commit_params.early_hints_preloaded_resources) {
navigation_params->early_hints_preloaded_resources.emplace_back(resource);
}
}
}
blink::mojom::CommonNavigationParamsPtr MakeCommonNavigationParams(
const WebSecurityOrigin& current_origin,
std::unique_ptr<blink::WebNavigationInfo> info,
int load_flags,
bool has_download_sandbox_flag,
bool from_ad,
bool is_history_navigation_in_new_child_frame) {
// A valid RequestorOrigin is always expected to be present.
DCHECK(!info->url_request.RequestorOrigin().IsNull());
blink::mojom::ReferrerPtr referrer = blink::mojom::Referrer::New(
blink::WebStringToGURL(info->url_request.ReferrerString()),
info->url_request.GetReferrerPolicy());
// No history-navigation is expected to happen.
DCHECK(info->navigation_type != blink::kWebNavigationTypeBackForward);
// Determine the navigation type. No same-document navigation is expected
// because it is loaded immediately by the FrameLoader.
blink::mojom::NavigationType navigation_type =
blink::mojom::NavigationType::DIFFERENT_DOCUMENT;
if (info->navigation_type == blink::kWebNavigationTypeReload) {
if (load_flags & net::LOAD_BYPASS_CACHE)
navigation_type = blink::mojom::NavigationType::RELOAD_BYPASSING_CACHE;
else
navigation_type = blink::mojom::NavigationType::RELOAD;
}
auto source_location = network::mojom::SourceLocation::New(
info->source_location.url.Latin1(), info->source_location.line_number,
info->source_location.column_number);
const blink::WebURLRequestExtraData* url_request_extra_data =
static_cast<blink::WebURLRequestExtraData*>(
info->url_request.GetURLRequestExtraData().get());
DCHECK(url_request_extra_data);
// Convert from WebVector<int> to std::vector<int>.
std::vector<int> initiator_origin_trial_features(
info->initiator_origin_trial_features.begin(),
info->initiator_origin_trial_features.end());
blink::NavigationDownloadPolicy download_policy;
download_policy.ApplyDownloadFramePolicy(
info->is_opener_navigation, info->url_request.HasUserGesture(),
info->url_request.RequestorOrigin().CanAccess(current_origin),
has_download_sandbox_flag, info->blocking_downloads_in_sandbox_enabled,
from_ad);
return blink::mojom::CommonNavigationParams::New(
info->url_request.Url(), info->url_request.RequestorOrigin(),
std::move(referrer), url_request_extra_data->transition_type(),
navigation_type, download_policy,
info->frame_load_type == WebFrameLoadType::kReplaceCurrentItem, GURL(),
GURL(),
static_cast<blink::PreviewsState>(info->url_request.GetPreviewsState()),
base::TimeTicks::Now(), info->url_request.HttpMethod().Latin1(),
blink::GetRequestBodyForWebURLRequest(info->url_request),
std::move(source_location), false /* started_from_context_menu */,
info->url_request.HasUserGesture(),
info->url_request.HasTextFragmentToken(),
info->should_check_main_world_content_security_policy,
initiator_origin_trial_features, info->href_translate.Latin1(),
is_history_navigation_in_new_child_frame, info->input_start);
}
WebFrameLoadType NavigationTypeToLoadType(
blink::mojom::NavigationType navigation_type,
bool should_replace_current_entry,
bool has_valid_page_state) {
switch (navigation_type) {
case blink::mojom::NavigationType::RELOAD:
case blink::mojom::NavigationType::RELOAD_ORIGINAL_REQUEST_URL:
return WebFrameLoadType::kReload;
case blink::mojom::NavigationType::RELOAD_BYPASSING_CACHE:
return WebFrameLoadType::kReloadBypassingCache;
case blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT:
case blink::mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT:
return WebFrameLoadType::kBackForward;
case blink::mojom::NavigationType::RESTORE:
case blink::mojom::NavigationType::RESTORE_WITH_POST:
if (has_valid_page_state)
return WebFrameLoadType::kBackForward;
// If there is no valid page state, fall through to the default case.
FALLTHROUGH;
case blink::mojom::NavigationType::SAME_DOCUMENT:
case blink::mojom::NavigationType::DIFFERENT_DOCUMENT:
return should_replace_current_entry
? WebFrameLoadType::kReplaceCurrentItem
: WebFrameLoadType::kStandard;
default:
NOTREACHED();
return WebFrameLoadType::kStandard;
}
}
RenderFrameImpl::CreateRenderFrameImplFunction g_create_render_frame_impl =
nullptr;
WebString ConvertRelativePathToHtmlAttribute(const base::FilePath& path) {
DCHECK(!path.IsAbsolute());
return WebString::FromUTF8(
std::string("./") +
path.NormalizePathSeparatorsTo(FILE_PATH_LITERAL('/')).AsUTF8Unsafe());
}
class RenderFrameWebFrameSerializerClient
: public blink::WebFrameSerializerClient {
public:
explicit RenderFrameWebFrameSerializerClient(
mojo::PendingRemote<mojom::FrameHTMLSerializerHandler> handler_remote)
: handler_remote_(std::move(handler_remote)) {}
// WebFrameSerializerClient implementation:
void DidSerializeDataForFrame(
const WebVector<char>& data,
WebFrameSerializerClient::FrameSerializationStatus status) override {
DCHECK(handler_remote_.is_bound());
handler_remote_->DidReceiveData(std::string(data.Data(), data.size()));
// Make sure to report Done() to the browser process when receiving the last
// chunk of data, and reset the mojo remote so that the DCHECK above ensures
// this method won't be called anymore after this point.
if (status == WebFrameSerializerClient::kCurrentFrameIsFinished) {
handler_remote_->Done();
handler_remote_.reset();
}
}
private:
mojo::Remote<mojom::FrameHTMLSerializerHandler> handler_remote_;
};
// Implementation of WebFrameSerializer::LinkRewritingDelegate that responds
// based on the payload of mojom::Frame::GetSerializedHtmlWithLocalLinks().
class LinkRewritingDelegate : public WebFrameSerializer::LinkRewritingDelegate {
public:
LinkRewritingDelegate(
const base::flat_map<GURL, base::FilePath>& url_to_local_path,
const base::flat_map<blink::FrameToken, base::FilePath>&
frame_token_to_local_path)
: url_to_local_path_(url_to_local_path),
frame_token_to_local_path_(frame_token_to_local_path) {}
bool RewriteFrameSource(WebFrame* frame, WebString* rewritten_link) override {
const blink::FrameToken frame_token = frame->GetFrameToken();
auto it = frame_token_to_local_path_.find(frame_token);
if (it == frame_token_to_local_path_.end())
return false; // This can happen because of https://crbug.com/541354.
const base::FilePath& local_path = it->second;
*rewritten_link = ConvertRelativePathToHtmlAttribute(local_path);
return true;
}
bool RewriteLink(const WebURL& url, WebString* rewritten_link) override {
auto it = url_to_local_path_.find(GURL(url));
if (it == url_to_local_path_.end())
return false;
const base::FilePath& local_path = it->second;
*rewritten_link = ConvertRelativePathToHtmlAttribute(local_path);
return true;
}
private:
const base::flat_map<GURL, base::FilePath>& url_to_local_path_;
const base::flat_map<blink::FrameToken, base::FilePath>&
frame_token_to_local_path_;
};
// Implementation of WebFrameSerializer::MHTMLPartsGenerationDelegate that
// 1. Bases shouldSkipResource and getContentID responses on contents of
// SerializeAsMHTMLParams.
// 2. Stores digests of urls of serialized resources (i.e. urls reported via
// shouldSkipResource) into |serialized_resources_uri_digests| passed
// to the constructor.
class MHTMLPartsGenerationDelegate
: public WebFrameSerializer::MHTMLPartsGenerationDelegate {
public:
MHTMLPartsGenerationDelegate(
const mojom::SerializeAsMHTMLParams& params,
std::unordered_set<std::string>* serialized_resources_uri_digests)
: params_(params),
serialized_resources_uri_digests_(serialized_resources_uri_digests) {
DCHECK(serialized_resources_uri_digests_);
// Digests must be sorted for binary search.
DCHECK(std::is_sorted(params_.digests_of_uris_to_skip.begin(),
params_.digests_of_uris_to_skip.end()));
// URLs are not duplicated.
DCHECK(std::adjacent_find(params_.digests_of_uris_to_skip.begin(),
params_.digests_of_uris_to_skip.end()) ==
params_.digests_of_uris_to_skip.end());
}
bool ShouldSkipResource(const WebURL& url) override {
std::string digest =
crypto::SHA256HashString(params_.salt + GURL(url).spec());
// Skip if the |url| already covered by serialization of an *earlier* frame.
if (std::binary_search(params_.digests_of_uris_to_skip.begin(),
params_.digests_of_uris_to_skip.end(), digest))
return true;
// Let's record |url| as being serialized for the *current* frame.
auto pair = serialized_resources_uri_digests_->insert(digest);
bool insertion_took_place = pair.second;
DCHECK(insertion_took_place); // Blink should dedupe within a frame.
return false;
}
bool UseBinaryEncoding() override { return params_.mhtml_binary_encoding; }
bool RemovePopupOverlay() override {
return params_.mhtml_popup_overlay_removal;
}
bool UsePageProblemDetectors() override {
return params_.mhtml_problem_detection;
}
private:
const mojom::SerializeAsMHTMLParams& params_;
std::unordered_set<std::string>* serialized_resources_uri_digests_;
DISALLOW_COPY_AND_ASSIGN(MHTMLPartsGenerationDelegate);
};
bool IsHttpPost(const blink::WebURLRequest& request) {
return request.HttpMethod().Utf8() == "POST";
}
// Delegate responsible for determining the handle writing implementation by
// instantiating an MHTMLHandleWriter on the heap respective to the passed in
// MHTMLSerializationParams. This transfers ownership of the handle to the
// new MHTMLHandleWriter.
class MHTMLHandleWriterDelegate {
public:
MHTMLHandleWriterDelegate(
const mojom::SerializeAsMHTMLParams& params,
MHTMLHandleWriter::MHTMLWriteCompleteCallback callback,
scoped_refptr<base::TaskRunner> main_thread_task_runner) {
// Handle must be instantiated.
DCHECK(params.output_handle);
if (params.output_handle->is_file_handle()) {
handle_ = new MHTMLFileHandleWriter(
std::move(main_thread_task_runner), std::move(callback),
std::move(params.output_handle->get_file_handle()));
} else {
handle_ = new MHTMLProducerHandleWriter(
std::move(main_thread_task_runner), std::move(callback),
std::move(params.output_handle->get_producer_handle()));
}
}
void WriteContents(std::vector<WebThreadSafeData> mhtml_contents) {
// Using base::Unretained is safe, as calls to WriteContents() always
// deletes |handle| upon Finish().
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&MHTMLHandleWriter::WriteContents,
base::Unretained(handle_), std::move(mhtml_contents)));
}
// Within the context of the delegate, only for premature write finish.
void Finish(mojom::MhtmlSaveStatus save_status) {
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&MHTMLHandleWriter::Finish, base::Unretained(handle_),
save_status));
}
private:
MHTMLHandleWriter* handle_;
DISALLOW_COPY_AND_ASSIGN(MHTMLHandleWriterDelegate);
};
// Use this for histograms with dynamically generated names, which otherwise
// can't use the UMA_HISTOGRAM_MEMORY_MB macro without code duplication.
void RecordSuffixedMemoryMBHistogram(base::StringPiece name,
base::StringPiece suffix,
int sample_mb) {
base::UmaHistogramMemoryMB(base::StrCat({name, suffix}), sample_mb);
}
void RecordSuffixedRendererMemoryMetrics(
const RenderThreadImpl::RendererMemoryMetrics& memory_metrics,
base::StringPiece suffix) {
RecordSuffixedMemoryMBHistogram("Memory.Experimental.Renderer.PartitionAlloc",
suffix,
memory_metrics.partition_alloc_kb / 1024);
RecordSuffixedMemoryMBHistogram("Memory.Experimental.Renderer.BlinkGC",
suffix, memory_metrics.blink_gc_kb / 1024);
RecordSuffixedMemoryMBHistogram("Memory.Experimental.Renderer.Malloc", suffix,
memory_metrics.malloc_mb);
RecordSuffixedMemoryMBHistogram("Memory.Experimental.Renderer.Discardable",
suffix, memory_metrics.discardable_kb / 1024);
RecordSuffixedMemoryMBHistogram(
"Memory.Experimental.Renderer.V8MainThreadIsolate", suffix,
memory_metrics.v8_main_thread_isolate_mb);
RecordSuffixedMemoryMBHistogram("Memory.Experimental.Renderer.TotalAllocated",
suffix, memory_metrics.total_allocated_mb);
RecordSuffixedMemoryMBHistogram(
"Memory.Experimental.Renderer.NonDiscardableTotalAllocated", suffix,
memory_metrics.non_discardable_total_allocated_mb);
RecordSuffixedMemoryMBHistogram(
"Memory.Experimental.Renderer.TotalAllocatedPerRenderView", suffix,
memory_metrics.total_allocated_per_render_view_mb);
}
mojo::PendingRemote<blink::mojom::BlobURLToken> CloneBlobURLToken(
blink::CrossVariantMojoRemote<blink::mojom::BlobURLTokenInterfaceBase>&
blob_url_token) {
if (!blob_url_token)
return mojo::NullRemote();
mojo::PendingRemote<blink::mojom::BlobURLToken> cloned_token;
mojo::Remote<blink::mojom::BlobURLToken> token(std::move(blob_url_token));
token->Clone(cloned_token.InitWithNewPipeAndPassReceiver());
blob_url_token = token.Unbind();
return cloned_token;
}
// Whether the navigation should be treated as a "loadDataWithBaseURL"
// navigation, where the "document URL" is set to the supplied base URL instead
// of the data URL set in CommonNavigationParams' `url` and the "unreachable
// URL" is set to the supplied history URL. If this returns false, the data:
// URL will still be loaded, but we won't try to use the supplied base URL and
// history URL.
bool ShouldLoadDataWithBaseURL(
bool is_main_frame,
const blink::mojom::CommonNavigationParams& common_params,
const blink::mojom::CommitNavigationParams& commit_params) {
if (!is_main_frame)
return false;
// If no base URL is supplied, we should treat this as a normal data: URL
// navigation, except when `data_url_as_string` should be used, as the `url`
// in CommonNavigationParams will just contain the data header with no actual
// data, and the `data_url_as_string` code is only available in the
// loadDataWithBaseURL path.
bool should_load_data_url = !common_params.base_url_for_data_url.is_empty();
#if defined(OS_ANDROID)
should_load_data_url |= !commit_params.data_url_as_string.empty();
#endif
DCHECK(!should_load_data_url || common_params.url.SchemeIs(url::kDataScheme));
return should_load_data_url;
}
// Creates a fully functional DocumentState in the case where we do not have
// navigation parameters available.
std::unique_ptr<DocumentState> BuildDocumentState() {
std::unique_ptr<DocumentState> document_state =
std::make_unique<DocumentState>();
InternalDocumentStateData::FromDocumentState(document_state.get())
->set_navigation_state(NavigationState::CreateForSynchronousCommit());
return document_state;
}
// Creates a fully functional DocumentState in the case where we have
// navigation parameters available in the RenderFrameImpl.
std::unique_ptr<DocumentState> BuildDocumentStateFromParams(
const blink::mojom::CommonNavigationParams& common_params,
const blink::mojom::CommitNavigationParams& commit_params,
mojom::NavigationClient::CommitNavigationCallback commit_callback,
std::unique_ptr<NavigationClient> navigation_client,
int request_id,
bool was_initiated_in_this_frame) {
std::unique_ptr<DocumentState> document_state(new DocumentState());
InternalDocumentStateData* internal_data =
InternalDocumentStateData::FromDocumentState(document_state.get());
DCHECK(!common_params.navigation_start.is_null());
DCHECK(!common_params.url.SchemeIs(url::kJavaScriptScheme));
internal_data->set_is_overriding_user_agent(
commit_params.is_overriding_user_agent);
internal_data->set_must_reset_scroll_and_scale_state(
common_params.navigation_type ==
blink::mojom::NavigationType::RELOAD_ORIGINAL_REQUEST_URL);
internal_data->set_request_id(request_id);
// If this is a loadDataWithBaseURL request, save the commit URL so that we
// can send a DidCommit message with the URL that was originally sent by the
// browser in CommonNavigationParams (See MaybeGetOverriddenURL()).
// Note that this is not calling ShouldLoadDataWithBaseURL() to preserve
// pre-existing behavior, where we would only return the data: URL with the
// DidCommitProvisionalLoadParams under this specific condition (in other
// cases we will return the DocumentLoader's URL - which will use the base URL
// if it's not empty).
// TODO(https://crbug.com/1223403, https://crbug.com/1223408): Make this
// consistent with the other LoadDataWithBaseURL checks.
bool load_data = !common_params.base_url_for_data_url.is_empty() &&
!common_params.history_url_for_data_url.is_empty() &&
common_params.url.SchemeIs(url::kDataScheme);
document_state->set_was_load_data_with_base_url_request(load_data);
if (load_data)
document_state->set_data_url(common_params.url);
InternalDocumentStateData::FromDocumentState(document_state.get())
->set_navigation_state(NavigationState::Create(
common_params.Clone(), commit_params.Clone(),
std::move(commit_callback), std::move(navigation_client),
was_initiated_in_this_frame));
return document_state;
}
void ApplyFilePathAlias(blink::WebURLRequest* request) {
const base::CommandLine::StringType file_url_path_alias =
base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
switches::kFileUrlPathAlias);
if (file_url_path_alias.empty())
return;
const auto alias_mapping =
base::SplitString(file_url_path_alias, FILE_PATH_LITERAL("="),
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (alias_mapping.size() != 2) {
LOG(ERROR) << "Invalid file path alias format.";
return;
}
#if defined(OS_WIN)
std::wstring path = base::UTF16ToWide(request->Url().GetString().Utf16());
const std::wstring file_prefix =
base::ASCIIToWide(url::kFileScheme) +
base::ASCIIToWide(url::kStandardSchemeSeparator);
#else
std::string path = request->Url().GetString().Utf8();
const std::string file_prefix =
std::string(url::kFileScheme) + url::kStandardSchemeSeparator;
#endif
if (!base::StartsWith(path, file_prefix + alias_mapping[0],
base::CompareCase::SENSITIVE)) {
return;
}
base::ReplaceFirstSubstringAfterOffset(&path, 0, alias_mapping[0],
alias_mapping[1]);
#if defined(OS_WIN)
request->SetUrl(blink::WebURL(GURL(base::WideToUTF8(path))));
#else
request->SetUrl(blink::WebURL(GURL(path)));
#endif
}
// Packs all navigation timings sent by the browser to a blink understandable
// format, blink::WebNavigationTimings.
blink::WebNavigationTimings BuildNavigationTimings(
base::TimeTicks navigation_start,
const blink::mojom::NavigationTiming& browser_navigation_timings,
base::TimeTicks input_start) {
blink::WebNavigationTimings renderer_navigation_timings;
// Sanitizes the navigation_start timestamp for browser-initiated navigations,
// where the browser possibly has a better notion of start time than the
// renderer. In the case of cross-process navigations, this carries over the
// time of finishing the onbeforeunload handler of the previous page.
// TimeTicks is sometimes not monotonic across processes, and because
// |browser_navigation_start| is likely before this process existed,
// InterProcessTimeTicksConverter won't help. The timestamp is sanitized by
// clamping it to now.
DCHECK(!navigation_start.is_null());
renderer_navigation_timings.navigation_start =
std::min(navigation_start, base::TimeTicks::Now());
renderer_navigation_timings.redirect_start =
browser_navigation_timings.redirect_start;
renderer_navigation_timings.redirect_end =
browser_navigation_timings.redirect_end;
renderer_navigation_timings.fetch_start =
browser_navigation_timings.fetch_start;
renderer_navigation_timings.input_start = input_start;
return renderer_navigation_timings;
}
// Fills navigation data sent by the browser to a blink understandable
// format, blink::WebNavigationParams.
void FillMiscNavigationParams(
const blink::mojom::CommonNavigationParams& common_params,
const blink::mojom::CommitNavigationParams& commit_params,
blink::WebNavigationParams* navigation_params) {
navigation_params->navigation_timings = BuildNavigationTimings(
common_params.navigation_start, *commit_params.navigation_timing,
common_params.input_start);
navigation_params->is_user_activated =
commit_params.was_activated == blink::mojom::WasActivatedOption::kYes;
navigation_params->has_text_fragment_token =
common_params.text_fragment_token;
navigation_params->is_browser_initiated = commit_params.is_browser_initiated;
navigation_params->is_prerendering = commit_params.is_prerendering;
navigation_params->is_cross_site_cross_browsing_context_group =
commit_params.is_cross_site_cross_browsing_context_group;
#if defined(OS_ANDROID)
// Only android webview uses this.
navigation_params->grant_load_local_resources =
commit_params.can_load_local_resources;
#else
DCHECK(!commit_params.can_load_local_resources);
#endif
if (commit_params.origin_to_commit) {
navigation_params->origin_to_commit =
commit_params.origin_to_commit.value();
}
navigation_params->sandbox_flags = commit_params.sandbox_flags;
navigation_params->appcache_host_id =
commit_params.appcache_host_id.value_or(base::UnguessableToken());
navigation_params->frame_policy = commit_params.frame_policy;
if (common_params.navigation_type == blink::mojom::NavigationType::RESTORE) {
// We're doing a load of a page that was restored from the last session.
// By default this prefers the cache over loading
// (LOAD_SKIP_CACHE_VALIDATION) which can result in stale data for pages
// that are set to expire. We explicitly override that by setting the
// policy here so that as necessary we load from the network.
//
// TODO(davidben): Remove this in favor of passing a cache policy to the
// loadHistoryItem call in OnNavigate. That requires not overloading
// UseProtocolCachePolicy to mean both "normal load" and "determine cache
// policy based on load type, etc".
navigation_params->force_fetch_cache_mode =
blink::mojom::FetchCacheMode::kDefault;
}
navigation_params->origin_agent_cluster = commit_params.origin_agent_cluster;
navigation_params->enabled_client_hints.reserve(
commit_params.enabled_client_hints.size());
for (auto enabled_hint : commit_params.enabled_client_hints)
navigation_params->enabled_client_hints.emplace_back(enabled_hint);
if (commit_params.http_response_code != -1)
navigation_params->http_status_code = commit_params.http_response_code;
navigation_params->app_history_back_entries.reserve(
commit_params.app_history_back_entries.size());
for (const auto& entry : commit_params.app_history_back_entries) {
WebHistoryItem item;
item.Initialize();
item.SetAppHistoryKey(WebString::FromUTF16(entry->key));
item.SetAppHistoryId(WebString::FromUTF16(entry->id));
item.SetURLString(WebString::FromUTF16(entry->url));
navigation_params->app_history_back_entries.emplace_back(item);
}
navigation_params->app_history_forward_entries.reserve(
commit_params.app_history_forward_entries.size());
for (const auto& entry : commit_params.app_history_forward_entries) {
WebHistoryItem item;
item.Initialize();
item.SetAppHistoryKey(WebString::FromUTF16(entry->key));
item.SetAppHistoryId(WebString::FromUTF16(entry->id));
item.SetURLString(WebString::FromUTF16(entry->url));
navigation_params->app_history_forward_entries.emplace_back(item);
}
}
// Fills in the origin policy associated with this response, if any is present.
// Converts it into a format that blink understands: WebOriginPolicy.
void FillNavigationParamsOriginPolicy(
const network::mojom::URLResponseHead& head,
blink::WebNavigationParams* navigation_params) {
if (head.origin_policy.has_value() && head.origin_policy.value().contents) {
navigation_params->origin_policy = blink::WebOriginPolicy();
for (const auto& id : head.origin_policy.value().contents->ids) {
navigation_params->origin_policy->ids.emplace_back(
WebString::FromUTF8(id));
}
const absl::optional<std::string>& permissions_policy =
head.origin_policy.value().contents->permissions_policy;
if (permissions_policy) {
navigation_params->origin_policy->permissions_policy =
WebString::FromUTF8(*permissions_policy);
}
for (const auto& csp :
head.origin_policy.value().contents->content_security_policies) {
navigation_params->origin_policy->content_security_policies.emplace_back(
WebString::FromUTF8(csp));
}
for (const auto& csp_report_only :
head.origin_policy.value()
.contents->content_security_policies_report_only) {
navigation_params->origin_policy->content_security_policies_report_only
.emplace_back(WebString::FromUTF8(csp_report_only));
}
}
}
std::unique_ptr<blink::WebPolicyContainer> ToWebPolicyContainer(
blink::mojom::PolicyContainerPtr in) {
if (!in)
return nullptr;
return std::make_unique<blink::WebPolicyContainer>(
blink::WebPolicyContainerPolicies{
in->policies->referrer_policy,
in->policies->ip_address_space,
ToWebContentSecurityPolicies(
std::move(in->policies->content_security_policies)),
},
std::move(in->remote));
}
std::string GetUniqueNameOfWebFrame(WebFrame* web_frame) {
if (web_frame->IsWebLocalFrame())
return RenderFrameImpl::FromWebFrame(web_frame)->unique_name();
return RenderFrameProxy::FromWebFrame(web_frame->ToWebRemoteFrame())
->unique_name();
}
perfetto::protos::pbzero::FrameDeleteIntention FrameDeleteIntentionToProto(
mojom::FrameDeleteIntention intent) {
using ProtoLevel = perfetto::protos::pbzero::FrameDeleteIntention;
switch (intent) {
case mojom::FrameDeleteIntention::kNotMainFrame:
return ProtoLevel::FRAME_DELETE_INTENTION_NOT_MAIN_FRAME;
case mojom::FrameDeleteIntention::kSpeculativeMainFrameForShutdown:
return ProtoLevel::
FRAME_DELETE_INTENTION_SPECULATIVE_MAIN_FRAME_FOR_SHUTDOWN;
case mojom::FrameDeleteIntention::
kSpeculativeMainFrameForNavigationCancelled:
return ProtoLevel::
FRAME_DELETE_INTENTION_SPECULATIVE_MAIN_FRAME_FOR_NAVIGATION_CANCELLED;
}
// All cases should've been handled by the switch case above.
NOTREACHED();
return ProtoLevel::FRAME_DELETE_INTENTION_NOT_MAIN_FRAME;
}
void PropagatePageZoomToNewlyAttachedFrame(blink::WebView* web_view,
float device_scale_factor) {
if (RenderThread::Get()->IsUseZoomForDSF())
web_view->SetZoomFactorForDeviceScaleFactor(device_scale_factor);
else
web_view->SetZoomLevel(web_view->ZoomLevel());
}
void CallClientDeferMediaLoad(base::WeakPtr<RenderFrameImpl> frame,
bool has_played_media_before,
base::OnceClosure closure) {
DCHECK(blink::features::IsPrerender2Enabled());
if (!frame)
return;
GetContentClient()->renderer()->DeferMediaLoad(
frame.get(), has_played_media_before, std::move(closure));
}
} // namespace
RenderFrameImpl::AssertNavigationCommits::AssertNavigationCommits(
RenderFrameImpl* frame)
: AssertNavigationCommits(frame, false) {}
RenderFrameImpl::AssertNavigationCommits::AssertNavigationCommits(
RenderFrameImpl* frame,
MayReplaceInitialEmptyDocumentTag)
: AssertNavigationCommits(frame, true) {}
RenderFrameImpl::AssertNavigationCommits::~AssertNavigationCommits() {
// Frame might have been synchronously detached when dispatching JS events.
if (frame_) {
CHECK_EQ(NavigationCommitState::kDidCommit,
frame_->navigation_commit_state_);
frame_->navigation_commit_state_ = NavigationCommitState::kNone;
}
}
RenderFrameImpl::AssertNavigationCommits::AssertNavigationCommits(
RenderFrameImpl* frame,
bool allow_transition_from_initial_empty_document)
: frame_(frame->weak_factory_.GetWeakPtr()) {
if (NavigationCommitState::kNone != frame->navigation_commit_state_) {
CHECK(allow_transition_from_initial_empty_document);
CHECK_EQ(NavigationCommitState::kInitialEmptyDocument,
frame->navigation_commit_state_);
}
frame->navigation_commit_state_ = NavigationCommitState::kWillCommit;
}
// This class uses existing WebNavigationBodyLoader to read the whole response
// body into in-memory buffer, and then creates another body loader with static
// data so that we can parse mhtml archive synchronously. This is a workaround
// for the fact that we need the whole archive to determine the document's mime
// type and construct a right document instance.
class RenderFrameImpl::MHTMLBodyLoaderClient
: public blink::WebNavigationBodyLoader::Client {
public:
// Once the body is read, fills |navigation_params| with the new body loader
// and calls |done_callbcak|.
MHTMLBodyLoaderClient(
RenderFrameImpl* frame,
std::unique_ptr<blink::WebNavigationParams> navigation_params,
base::OnceCallback<void(std::unique_ptr<blink::WebNavigationParams>)>
done_callback)
: frame_(frame),
navigation_params_(std::move(navigation_params)),
body_loader_(std::move(navigation_params_->body_loader)),
done_callback_(std::move(done_callback)) {
body_loader_->StartLoadingBody(this, nullptr /*code_cache_host*/);
}
~MHTMLBodyLoaderClient() override {
// MHTMLBodyLoaderClient is reset in several different places. Either:
CHECK(
// - the body load finished and the result is being committed, so
// |BodyLoadingFinished| (see below) should still be on the stack, or
committing_ ||
// - MHTMLBodyLoaderClient is abandoned, either because:
// - a new renderer-initiated navigation began, which explicitly
// detaches any existing MHTMLBodyLoaderClient
// - this renderer began the navigation and cancelled it with
// |AbortClientNavigation|, e.g. JS called window.stop(), which
// explicitly detaches any existing MHTMLBodyLoaderClient
// - the frame is detached and self-deleted, which also explicitly
// detaches any existing MHTMLBodyLoaderClient or,
!frame_ ||
// - the browser requested a different navigation be committed in this
// frame, i.e. the navigation commit state should be |kWillCommit|
NavigationCommitState::kWillCommit == frame_->navigation_commit_state_);
}
// Marks |this|'s pending load as abandoned. There are a number of reasons
// this can happen; see the destructor for more information.
void Detach() {
// Note that the MHTMLBodyLoaderClient might be associated with a
// provisional frame, so this does not assert that `frame_->in_frame_tree_`
// is true.
frame_ = nullptr;
}
// blink::WebNavigationBodyLoader::Client overrides:
void BodyCodeCacheReceived(mojo_base::BigBuffer data) override {}
void BodyDataReceived(base::span<const char> data) override {
data_.Append(data.data(), data.size());
}
void BodyLoadingFinished(base::TimeTicks completion_time,
int64_t total_encoded_data_length,
int64_t total_encoded_body_length,
int64_t total_decoded_body_length,
bool should_report_corb_blocking,
const absl::optional<WebURLError>& error) override {
committing_ = true;
AssertNavigationCommits assert_navigation_commits(frame_);
if (!error.has_value()) {
WebNavigationParams::FillBodyLoader(navigation_params_.get(), data_);
// Clear |is_static_data| flag to avoid the special behavior it triggers,
// e.g. skipping content disposition check. We want this load to be
// regular, just like with an original body loader.
navigation_params_->is_static_data = false;
}
std::move(done_callback_).Run(std::move(navigation_params_));
}
private:
// |RenderFrameImpl| owns |this|, so |frame_| is guaranteed to outlive |this|.
// Will be nulled if |Detach()| has been called.
RenderFrameImpl* frame_;
bool committing_ = false;
WebData data_;
std::unique_ptr<blink::WebNavigationParams> navigation_params_;
std::unique_ptr<blink::WebNavigationBodyLoader> body_loader_;
base::OnceCallback<void(std::unique_ptr<blink::WebNavigationParams>)>
done_callback_;
DISALLOW_COPY_AND_ASSIGN(MHTMLBodyLoaderClient);
};
class RenderFrameImpl::FrameURLLoaderFactory
: public blink::WebURLLoaderFactory {
public:
explicit FrameURLLoaderFactory(base::WeakPtr<RenderFrameImpl> frame)
: frame_(std::move(frame)) {}
~FrameURLLoaderFactory() override = default;
std::unique_ptr<blink::WebURLLoader> CreateURLLoader(
const WebURLRequest& request,
std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>
freezable_task_runner_handle,
std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>
unfreezable_task_runner_handle,
blink::CrossVariantMojoRemote<blink::mojom::KeepAliveHandleInterfaceBase>
keep_alive_handle,
blink::WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper)
override {
// This should not be called if the frame is detached.
DCHECK(frame_);
std::vector<std::string> cors_exempt_header_list =
RenderThreadImpl::current()->cors_exempt_header_list();
blink::WebVector<blink::WebString> web_cors_exempt_header_list(
cors_exempt_header_list.size());
std::transform(
cors_exempt_header_list.begin(), cors_exempt_header_list.end(),
web_cors_exempt_header_list.begin(),
[](const std::string& h) { return blink::WebString::FromLatin1(h); });
return std::make_unique<blink::WebURLLoader>(
web_cors_exempt_header_list,
/*terminate_sync_load_event=*/nullptr,
std::move(freezable_task_runner_handle),
std::move(unfreezable_task_runner_handle),
frame_->GetLoaderFactoryBundle(), std::move(keep_alive_handle),
back_forward_cache_loader_helper);
}
private:
base::WeakPtr<RenderFrameImpl> frame_;
DISALLOW_COPY_AND_ASSIGN(FrameURLLoaderFactory);
};
std::string UniqueNameForWebFrame(blink::WebFrame* frame) {
return frame->IsWebLocalFrame()
? RenderFrameImpl::FromWebFrame(frame)->unique_name()
: RenderFrameProxy::FromWebFrame(frame->ToWebRemoteFrame())
->unique_name();
}
RenderFrameImpl::UniqueNameFrameAdapter::UniqueNameFrameAdapter(
RenderFrameImpl* render_frame)
: render_frame_(render_frame) {}
RenderFrameImpl::UniqueNameFrameAdapter::~UniqueNameFrameAdapter() {}
bool RenderFrameImpl::UniqueNameFrameAdapter::IsMainFrame() const {
return render_frame_->IsMainFrame();
}
bool RenderFrameImpl::UniqueNameFrameAdapter::IsCandidateUnique(
base::StringPiece name) const {
// This method is currently O(N), where N = number of frames in the tree.
DCHECK(!name.empty());
for (blink::WebFrame* frame = GetWebFrame()->Top(); frame;
frame = frame->TraverseNext()) {
if (UniqueNameForWebFrame(frame) == name)
return false;
}
return true;
}
int RenderFrameImpl::UniqueNameFrameAdapter::GetSiblingCount() const {
int sibling_count = 0;
for (blink::WebFrame* frame = GetWebFrame()->Parent()->FirstChild(); frame;
frame = frame->NextSibling()) {
if (frame == GetWebFrame())
continue;
++sibling_count;
}
return sibling_count;
}
int RenderFrameImpl::UniqueNameFrameAdapter::GetChildCount() const {
int child_count = 0;
for (blink::WebFrame* frame = GetWebFrame()->FirstChild(); frame;
frame = frame->NextSibling()) {
++child_count;
}
return child_count;
}
std::vector<std::string>
RenderFrameImpl::UniqueNameFrameAdapter::CollectAncestorNames(
BeginPoint begin_point,
bool (*should_stop)(base::StringPiece)) const {
std::vector<std::string> result;
for (blink::WebFrame* frame = begin_point == BeginPoint::kParentFrame
? GetWebFrame()->Parent()
: GetWebFrame();
frame; frame = frame->Parent()) {
result.push_back(UniqueNameForWebFrame(frame));
if (should_stop(result.back()))
break;
}
return result;
}
std::vector<int> RenderFrameImpl::UniqueNameFrameAdapter::GetFramePosition(
BeginPoint begin_point) const {
std::vector<int> result;
blink::WebFrame* parent = begin_point == BeginPoint::kParentFrame
? GetWebFrame()->Parent()
: GetWebFrame();
blink::WebFrame* child =
begin_point == BeginPoint::kParentFrame ? GetWebFrame() : nullptr;
while (parent) {
int position_in_parent = 0;
blink::WebFrame* sibling = parent->FirstChild();
while (sibling != child) {
sibling = sibling->NextSibling();
++position_in_parent;
}
result.push_back(position_in_parent);
child = parent;
parent = parent->Parent();
}
return result;
}
blink::WebLocalFrame* RenderFrameImpl::UniqueNameFrameAdapter::GetWebFrame()
const {
return render_frame_->frame_;
}
// static
RenderFrameImpl* RenderFrameImpl::Create(
AgentSchedulingGroup& agent_scheduling_group,
RenderViewImpl* render_view,
int32_t routing_id,
mojo::PendingAssociatedReceiver<mojom::Frame> frame_receiver,
mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
browser_interface_broker,
const base::UnguessableToken& devtools_frame_token) {
DCHECK(routing_id != MSG_ROUTING_NONE);
CreateParams params(agent_scheduling_group, render_view, routing_id,
std::move(frame_receiver),
std::move(browser_interface_broker),
devtools_frame_token);
if (g_create_render_frame_impl)
return g_create_render_frame_impl(std::move(params));
else
return new RenderFrameImpl(std::move(params));
}
// static
RenderFrame* RenderFrame::FromRoutingID(int routing_id) {
return RenderFrameImpl::FromRoutingID(routing_id);
}
// static
RenderFrameImpl* RenderFrameImpl::FromRoutingID(int routing_id) {
DCHECK(RenderThread::IsMainThread());
auto iter = g_routing_id_frame_map.Get().find(routing_id);
if (iter != g_routing_id_frame_map.Get().end())
return iter->second;
return nullptr;
}
// static
RenderFrameImpl* RenderFrameImpl::CreateMainFrame(
AgentSchedulingGroup& agent_scheduling_group,
RenderViewImpl* render_view,
blink::WebFrame* opener,
bool is_for_nested_main_frame,
blink::mojom::FrameReplicationStatePtr replication_state,
const base::UnguessableToken& devtools_frame_token,
mojom::CreateLocalMainFrameParamsPtr params) {
// A main frame RenderFrame must have a RenderWidget.
DCHECK_NE(MSG_ROUTING_NONE, params->widget_params->routing_id);
RenderFrameImpl* render_frame = RenderFrameImpl::Create(
agent_scheduling_group, render_view, params->routing_id,
std::move(params->frame), std::move(params->interface_broker),
devtools_frame_token);
render_frame->InitializeBlameContext(nullptr);
WebLocalFrame* web_frame = WebLocalFrame::CreateMainFrame(
render_view->GetWebView(), render_frame,
render_frame->blink_interface_registry_.get(), params->token,
ToWebPolicyContainer(std::move(params->policy_container)), opener,
// This conversion is a little sad, as this often comes from a
// WebString...
WebString::FromUTF8(replication_state->name),
replication_state->frame_policy.sandbox_flags);
if (params->has_committed_real_load)
render_frame->frame_->SetCommittedFirstRealLoad();
// Non-owning pointer that is self-referencing and destroyed by calling
// Close(). The RenderViewImpl has a RenderWidget already, but not a
// WebFrameWidget, which is now attached here.
blink::WebFrameWidget* web_frame_widget = web_frame->InitializeFrameWidget(
std::move(params->widget_params->frame_widget_host),
std::move(params->widget_params->frame_widget),
std::move(params->widget_params->widget_host),
std::move(params->widget_params->widget),
viz::FrameSinkId(RenderThread::Get()->GetClientId(),
params->widget_params->routing_id),
is_for_nested_main_frame,
/*hidden=*/true);
web_frame_widget->InitializeCompositing(
agent_scheduling_group.agent_group_scheduler(),
params->widget_params->visual_properties.screen_infos,
/*settings=*/nullptr);
// The WebFrame created here was already attached to the Page as its main
// frame, and the WebFrameWidget has been initialized, so we can call
// WebView's DidAttachLocalMainFrame().
render_frame->GetWebView()->DidAttachLocalMainFrame();
// The WebFrameWidget should start with valid VisualProperties, including a
// non-zero size. While WebFrameWidget would not normally receive IPCs and
// thus would not get VisualProperty updates while the frame is provisional,
// we need at least one update to them in order to meet expectations in the
// renderer, and that update comes as part of the CreateFrame message.
// TODO(crbug.com/419087): This could become part of WebFrameWidget Init.
web_frame_widget->ApplyVisualProperties(
params->widget_params->visual_properties);
render_frame->in_frame_tree_ = true;
render_frame->Initialize(nullptr);
if (params->subresource_loader_factories
->IsTrackedChildPendingURLLoaderFactoryBundle()) {
// In renderer-initiated creation of a new main frame (e.g. popup without
// rel=noopener), `params->subresource_loader_factories` are inherited from
// the creator frame (e.g. TrackedChildURLLoaderFactoryBundle will be
// updated when the creator's bundle recovers from a NetworkService crash).
// See also https://crbug.com/1194763#c5.
render_frame->loader_factories_ =
base::MakeRefCounted<blink::TrackedChildURLLoaderFactoryBundle>(
base::WrapUnique(
static_cast<blink::TrackedChildPendingURLLoaderFactoryBundle*>(
params->subresource_loader_factories.release())));
} else {
// In browser-initiated creation of a new main frame (e.g. popup with
// rel=noopener, or when creating a new tab) the Browser process provides
// `params->subresource_loader_factories`.
render_frame->loader_factories_ = render_frame->CreateLoaderFactoryBundle(
std::move(params->subresource_loader_factories),
absl::nullopt /* subresource_overrides */,
mojo::NullRemote() /* prefetch_loader_factory */);
}
return render_frame;
}
// static
void RenderFrameImpl::CreateFrame(
AgentSchedulingGroup& agent_scheduling_group,
const blink::LocalFrameToken& token,
int routing_id,
mojo::PendingAssociatedReceiver<mojom::Frame> frame_receiver,
mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
browser_interface_broker,
int previous_routing_id,
const absl::optional<blink::FrameToken>& opener_frame_token,
int parent_routing_id,
int previous_sibling_routing_id,
const base::UnguessableToken& devtools_frame_token,
blink::mojom::TreeScopeType tree_scope_type,
blink::mojom::FrameReplicationStatePtr replicated_state,
mojom::CreateFrameWidgetParamsPtr widget_params,
blink::mojom::FrameOwnerPropertiesPtr frame_owner_properties,
bool has_committed_real_load,
blink::mojom::PolicyContainerPtr policy_container) {
// TODO(danakj): Split this method into two pieces. The first block makes a
// WebLocalFrame and collects the RenderView and RenderFrame for it. The
// second block uses that to make a RenderWidget, if needed.
RenderViewImpl* render_view = nullptr;
RenderFrameImpl* render_frame = nullptr;
blink::WebLocalFrame* web_frame = nullptr;
if (previous_routing_id == MSG_ROUTING_NONE) {
// TODO(alexmos): This path is currently used only:
// 1) When recreating a RenderFrame after a crash.
// 2) In tests that issue this IPC directly.
// These two cases should be cleaned up to also pass a previous_routing_id,
// which would allow removing this branch altogether. See
// https://crbug.com/756790.
CHECK_NE(parent_routing_id, MSG_ROUTING_NONE);
RenderFrameProxy* parent_proxy =
RenderFrameProxy::FromRoutingID(parent_routing_id);
// If the browser is sending a valid parent routing id, it should already
// be created and registered.
CHECK(parent_proxy);
blink::WebRemoteFrame* parent_web_frame = parent_proxy->web_frame();
blink::WebFrame* previous_sibling_web_frame = nullptr;
RenderFrameProxy* previous_sibling_proxy =
RenderFrameProxy::FromRoutingID(previous_sibling_routing_id);
if (previous_sibling_proxy)
previous_sibling_web_frame = previous_sibling_proxy->web_frame();
render_view = parent_proxy->render_view();
// Create the RenderFrame and WebLocalFrame, linking the two.
render_frame = RenderFrameImpl::Create(
agent_scheduling_group, parent_proxy->render_view(), routing_id,
std::move(frame_receiver), std::move(browser_interface_broker),
devtools_frame_token);
render_frame->InitializeBlameContext(FromRoutingID(parent_routing_id));
render_frame->unique_name_helper_.set_propagated_name(
replicated_state->unique_name);
WebFrame* opener = nullptr;
if (opener_frame_token)
opener = WebFrame::FromFrameToken(opener_frame_token.value());
web_frame = parent_web_frame->CreateLocalChild(
tree_scope_type, WebString::FromUTF8(replicated_state->name),
replicated_state->frame_policy, render_frame,
render_frame->blink_interface_registry_.get(),
previous_sibling_web_frame,
frame_owner_properties->To<blink::WebFrameOwnerProperties>(), token,
opener, ToWebPolicyContainer(std::move(policy_container)));
// The RenderFrame is created and inserted into the frame tree in the above
// call to createLocalChild.
render_frame->in_frame_tree_ = true;
} else {
RenderFrameProxy* previous_proxy =
RenderFrameProxy::FromRoutingID(previous_routing_id);
RenderFrameImpl* previous_frame =
RenderFrameImpl::FromRoutingID(previous_routing_id);
WebFrame* previous_web_frame = ResolveWebFrame(previous_routing_id);
// Only one can be found. Either the |previous_proxy| for a remote-to-local
// navigation, or the |previous_frame| for a local-to-local navigation.
DCHECK(!(previous_proxy && previous_frame));
// The previous frame could've been detached while the navigation was being
// initiated in the browser process. Drop the navigation and don't create
// the frame in that case.
// See https://crbug.com/526304.
if (!previous_proxy && !previous_frame)
return;
render_view = previous_frame ? previous_frame->render_view()
: previous_proxy->render_view();
// This path is creating a local frame. It may or may not be a local root,
// depending if the frame's parent is local or remote. It may also be the
// main frame, as in the case where a navigation to the current process'
render_frame = RenderFrameImpl::Create(
agent_scheduling_group, render_view, routing_id,
std::move(frame_receiver), std::move(browser_interface_broker),
devtools_frame_token);
render_frame->InitializeBlameContext(nullptr);
web_frame = blink::WebLocalFrame::CreateProvisional(
render_frame, render_frame->blink_interface_registry_.get(), token,
previous_web_frame, replicated_state->frame_policy,
WebString::FromUTF8(replicated_state->name));
// The new |web_frame| is a main frame iff the previous frame was.
DCHECK_EQ(!previous_web_frame->Parent(), !web_frame->Parent());
// Clone the current unique name so web tests that log frame unique names
// output something meaningful. At `SwapIn()` time, the unique name will be
// updated to the latest value.
render_frame->unique_name_helper_.set_propagated_name(
GetUniqueNameOfWebFrame(previous_web_frame));
}
CHECK(render_view);
CHECK(render_frame);
CHECK(web_frame);
bool is_main_frame = !web_frame->Parent();
// Child frames require there to be a |parent_routing_id| present, for the
// remote parent frame. Though it is only used if the |previous_routing_id|
// is not given, which happens in some corner cases.
if (!is_main_frame)
DCHECK_NE(parent_routing_id, MSG_ROUTING_NONE);
// We now have a WebLocalFrame for the new frame. The next step is to make
// a RenderWidget (aka WebWidgetClient) for it, if it is a local root.
if (is_main_frame) {
// Main frames are always local roots, so they should always have a
// |widget_params| (and it always comes with a routing id).
DCHECK(widget_params);
DCHECK_NE(widget_params->routing_id, MSG_ROUTING_NONE);
// TODO(crbug.com/419087): Can we merge this code with
// RenderFrameImpl::CreateMainFrame()?
// Non-owning pointer that is self-referencing and destroyed by calling
// Close(). The RenderViewImpl has a RenderWidget already, but not a
// WebFrameWidget, which is now attached here.
blink::WebFrameWidget* web_frame_widget = web_frame->InitializeFrameWidget(
std::move(widget_params->frame_widget_host),
std::move(widget_params->frame_widget),
std::move(widget_params->widget_host), std::move(widget_params->widget),
viz::FrameSinkId(RenderThread::Get()->GetClientId(),
widget_params->routing_id),
/*is_for_nested_main_frame=*/false,
/*hidden=*/true);
web_frame_widget->InitializeCompositing(
agent_scheduling_group.agent_group_scheduler(),
widget_params->visual_properties.screen_infos,
/*settings=*/nullptr);
// The WebFrameWidget should start with valid VisualProperties, including a
// non-zero size. While WebFrameWidget would not normally receive IPCs and
// thus would not get VisualProperty updates while the frame is provisional,
// we need at least one update to them in order to meet expectations in the
// renderer, and that update comes as part of the CreateFrame message.
// TODO(crbug.com/419087): This could become part of WebFrameWidget Init.
web_frame_widget->ApplyVisualProperties(widget_params->visual_properties);
// Note that we do *not* call WebView's DidAttachLocalMainFrame() here yet
// because this frame is provisional and not attached to the Page yet. We
// will tell WebViewImpl about it once it is swapped in.
} else if (widget_params) {
DCHECK(widget_params->routing_id != MSG_ROUTING_NONE);
// This frame is a child local root, so we require a separate RenderWidget
// for it from any other frames in the frame tree. Each local root defines
// a separate context/coordinate space/world for compositing, painting,
// input, etc. And each local root has a RenderWidget which provides
// such services independent from other RenderWidgets.
// Notably, we do not attempt to reuse the main frame's RenderWidget (if the
// main frame in this frame tree is local) as that RenderWidget is
// functioning in a different local root. Because this is a child local
// root, it implies there is some remote frame ancestor between this frame
// and the main frame, thus its coordinate space etc is not known relative
// to the main frame.
// Non-owning pointer that is self-referencing and destroyed by calling
// Close(). We use the new RenderWidget as the client for this
// WebFrameWidget, *not* the RenderWidget of the MainFrame, which is
// accessible from the RenderViewImpl.
blink::WebFrameWidget* web_frame_widget = web_frame->InitializeFrameWidget(
std::move(widget_params->frame_widget_host),
std::move(widget_params->frame_widget),
std::move(widget_params->widget_host), std::move(widget_params->widget),
viz::FrameSinkId(RenderThread::Get()->GetClientId(),
widget_params->routing_id),
/*is_for_nested_main_frame=*/false,
/*hidden=*/true);
web_frame_widget->InitializeCompositing(
agent_scheduling_group.agent_group_scheduler(),
widget_params->visual_properties.screen_infos,
/*settings=*/nullptr);
// The WebFrameWidget should start with valid VisualProperties, including a
// non-zero size. While WebFrameWidget would not normally receive IPCs and
// thus would not get VisualProperty updates while the frame is provisional,
// we need at least one update to them in order to meet expectations in the
// renderer, and that update comes as part of the CreateFrame message.
// TODO(crbug.com/419087): This could become part of WebFrameWidget Init.
web_frame_widget->ApplyVisualProperties(widget_params->visual_properties);
}
if (has_committed_real_load)
render_frame->frame_->SetCommittedFirstRealLoad();
render_frame->Initialize(web_frame->Parent());
}
// static
RenderFrame* RenderFrame::FromWebFrame(blink::WebLocalFrame* web_frame) {
return RenderFrameImpl::FromWebFrame(web_frame);
}
// static
void RenderFrame::ForEach(RenderFrameVisitor* visitor) {
DCHECK(RenderThread::IsMainThread());
FrameMap* frames = g_frame_map.Pointer();
for (auto it = frames->begin(); it != frames->end(); ++it) {
if (!visitor->Visit(it->second))
return;
}
}
// static
int RenderFrame::GetRoutingIdForWebFrame(blink::WebFrame* web_frame) {
if (!web_frame)
return MSG_ROUTING_NONE;
if (web_frame->IsWebRemoteFrame()) {
return RenderFrameProxy::FromWebFrame(web_frame->ToWebRemoteFrame())
->routing_id();
}
return RenderFrameImpl::FromWebFrame(web_frame)->GetRoutingID();
}
// static
RenderFrameImpl* RenderFrameImpl::FromWebFrame(blink::WebFrame* web_frame) {
DCHECK(RenderThread::IsMainThread());
auto iter = g_frame_map.Get().find(web_frame);
if (iter != g_frame_map.Get().end())
return iter->second;
return nullptr;
}
// static
void RenderFrameImpl::InstallCreateHook(
CreateRenderFrameImplFunction create_frame) {
DCHECK(!g_create_render_frame_impl);
g_create_render_frame_impl = create_frame;
}
// static
blink::WebFrame* RenderFrameImpl::ResolveWebFrame(int frame_routing_id) {
if (frame_routing_id == MSG_ROUTING_NONE)
return nullptr;
// Opener routing ID could refer to either a RenderFrameProxy or a
// RenderFrame, so need to check both.
RenderFrameProxy* opener_proxy =
RenderFrameProxy::FromRoutingID(frame_routing_id);
if (opener_proxy)
return opener_proxy->web_frame();
RenderFrameImpl* opener_frame =
RenderFrameImpl::FromRoutingID(frame_routing_id);
if (opener_frame)
return opener_frame->GetWebFrame();
return nullptr;
}
blink::WebURL RenderFrameImpl::OverrideFlashEmbedWithHTML(
const blink::WebURL& url) {
return GetContentClient()->renderer()->OverrideFlashEmbedWithHTML(url);
}
// RenderFrameImpl::CreateParams --------------------------------------------
RenderFrameImpl::CreateParams::CreateParams(
AgentSchedulingGroup& agent_scheduling_group,
RenderViewImpl* render_view,
int32_t routing_id,
mojo::PendingAssociatedReceiver<mojom::Frame> frame_receiver,
mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
browser_interface_broker,
const base::UnguessableToken& devtools_frame_token)
: agent_scheduling_group(&agent_scheduling_group),
render_view(render_view),
routing_id(routing_id),
frame_receiver(std::move(frame_receiver)),
browser_interface_broker(std::move(browser_interface_broker)),
devtools_frame_token(devtools_frame_token) {}
RenderFrameImpl::CreateParams::~CreateParams() = default;
RenderFrameImpl::CreateParams::CreateParams(CreateParams&&) = default;
RenderFrameImpl::CreateParams& RenderFrameImpl::CreateParams::operator=(
CreateParams&&) = default;
// RenderFrameImpl ----------------------------------------------------------
RenderFrameImpl::RenderFrameImpl(CreateParams params)
: agent_scheduling_group_(*params.agent_scheduling_group),
is_main_frame_(true),
unique_name_frame_adapter_(this),
unique_name_helper_(&unique_name_frame_adapter_),
in_frame_tree_(false),
render_view_(params.render_view),
routing_id_(params.routing_id),
selection_text_offset_(0),
selection_range_(gfx::Range::InvalidRange()),
render_accessibility_manager_(
std::make_unique<RenderAccessibilityManager>(this)),
blame_context_(nullptr),
weak_wrapper_resource_load_info_notifier_(
std::make_unique<blink::WeakWrapperResourceLoadInfoNotifier>(this)),
#if BUILDFLAG(ENABLE_PLUGINS)
focused_pepper_plugin_(nullptr),
#endif
navigation_client_impl_(nullptr),
media_factory_(
this,
base::BindRepeating(&RenderFrameImpl::RequestOverlayRoutingToken,
base::Unretained(this))),
devtools_frame_token_(params.devtools_frame_token) {
DCHECK(RenderThread::IsMainThread());
blink_interface_registry_ = std::make_unique<BlinkInterfaceRegistryImpl>(
registry_.GetWeakPtr(), associated_interfaces_.GetWeakPtr());
DCHECK(params.frame_receiver.is_valid());
pending_frame_receiver_ = std::move(params.frame_receiver);
DCHECK(params.browser_interface_broker.is_valid());
browser_interface_broker_proxy_.Bind(
std::move(params.browser_interface_broker),
agent_scheduling_group_.agent_group_scheduler().DefaultTaskRunner());
delayed_state_sync_timer_.SetTaskRunner(
agent_scheduling_group_.agent_group_scheduler().DefaultTaskRunner());
// Must call after binding our own remote interfaces.
media_factory_.SetupMojo();
std::pair<RoutingIDFrameMap::iterator, bool> result =
g_routing_id_frame_map.Get().insert(std::make_pair(routing_id_, this));
CHECK(result.second) << "Inserting a duplicate item.";
// Everything below subclasses RenderFrameObserver and is automatically
// deleted when the RenderFrame gets deleted.
#if defined(OS_ANDROID)
new GinJavaBridgeDispatcher(this);
#endif
}
mojom::FrameHost* RenderFrameImpl::GetFrameHost() {
if (!frame_host_remote_.is_bound())
GetRemoteAssociatedInterfaces()->GetInterface(&frame_host_remote_);
return frame_host_remote_.get();
}
RenderFrameImpl::~RenderFrameImpl() {
for (auto& observer : observers_)
observer.RenderFrameGone();
for (auto& observer : observers_)
observer.OnDestruct();
web_media_stream_device_observer_.reset();
base::trace_event::TraceLog::GetInstance()->RemoveProcessLabel(routing_id_);
g_routing_id_frame_map.Get().erase(routing_id_);
agent_scheduling_group_.RemoveRoute(routing_id_);
}
void RenderFrameImpl::Initialize(blink::WebFrame* parent) {
initialized_ = true;
is_main_frame_ = !parent;
bool is_tracing_rail = false;
bool is_tracing_navigation = false;
TRACE_EVENT_CATEGORY_GROUP_ENABLED("navigation", &is_tracing_navigation);
TRACE_EVENT_CATEGORY_GROUP_ENABLED("rail", &is_tracing_rail);
if (is_tracing_rail || is_tracing_navigation) {
int parent_id = RenderFrame::GetRoutingIdForWebFrame(parent);
TRACE_EVENT2("navigation,rail", "RenderFrameImpl::Initialize", "id",
routing_id_, "parent", parent_id);
}
#if BUILDFLAG(ENABLE_PLUGINS)
new PepperBrowserConnection(this);
#endif
RegisterMojoInterfaces();
// We delay calling this until we have the WebFrame so that any observer or
// embedder can call GetWebFrame on any RenderFrame.
GetContentClient()->renderer()->RenderFrameCreated(this);
// blink::WebAudioOutputIPCFactory::io_task_runner_ may be null in tests.
auto& factory = blink::WebAudioOutputIPCFactory::GetInstance();
if (factory.io_task_runner()) {
factory.RegisterRemoteFactory(GetWebFrame()->GetLocalFrameToken(),
GetBrowserInterfaceBroker());
}
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kDomAutomationController))
enabled_bindings_ |= BINDINGS_POLICY_DOM_AUTOMATION;
if (command_line.HasSwitch(switches::kStatsCollectionController))
enabled_bindings_ |= BINDINGS_POLICY_STATS_COLLECTION;
frame_request_blocker_ = blink::WebFrameRequestBlocker::Create();
// Bind this class to mojom::Frame and to the message router for legacy IPC.
// These must be called after |frame_| is set since binding requires a
// per-frame task runner.
frame_receiver_.Bind(
std::move(pending_frame_receiver_),
GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
agent_scheduling_group_.AddFrameRoute(
routing_id_, this,
GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
}
void RenderFrameImpl::InitializeBlameContext(RenderFrameImpl* parent_frame) {
DCHECK(!blame_context_);
blame_context_ = std::make_unique<FrameBlameContext>(this, parent_frame);
blame_context_->Initialize();
}
void RenderFrameImpl::GetInterface(
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
if (registry_.TryBindInterface(interface_name, &interface_pipe))
return;
for (auto& observer : observers_) {
observer.OnInterfaceRequestForFrame(interface_name, &interface_pipe);
if (!interface_pipe.is_valid())
return;
}
}
blink::WebFrameWidget* RenderFrameImpl::GetLocalRootWebFrameWidget() {
return frame_->LocalRoot()->FrameWidget();
}
#if BUILDFLAG(ENABLE_PLUGINS)
void RenderFrameImpl::PepperPluginCreated(RendererPpapiHost* host) {
for (auto& observer : observers_)
observer.DidCreatePepperPlugin(host);
}
void RenderFrameImpl::PepperTextInputTypeChanged(
PepperPluginInstanceImpl* instance) {
if (instance != focused_pepper_plugin_)
return;
GetLocalRootWebFrameWidget()->UpdateTextInputState();
}
void RenderFrameImpl::PepperCaretPositionChanged(
PepperPluginInstanceImpl* instance) {
if (instance != focused_pepper_plugin_)
return;
GetLocalRootWebFrameWidget()->UpdateSelectionBounds();
}
void RenderFrameImpl::PepperCancelComposition(
PepperPluginInstanceImpl* instance) {
if (instance != focused_pepper_plugin_)
return;
GetLocalRootWebFrameWidget()->CancelCompositionForPepper();
}
void RenderFrameImpl::PepperSelectionChanged(
PepperPluginInstanceImpl* instance) {
if (instance != focused_pepper_plugin_)
return;
// We have no reason to believe the locally cached last synced selection is
// invalid so we do not need to force the update if it matches our last synced
// value.
SyncSelectionIfRequired(blink::SyncCondition::kNotForced);
}
#endif // BUILDFLAG(ENABLE_PLUGINS)
void RenderFrameImpl::ScriptedPrint() {
bool user_initiated = GetLocalRootWebFrameWidget()->HandlingInputEvent();
for (auto& observer : observers_)
observer.ScriptedPrint(user_initiated);
}
bool RenderFrameImpl::Send(IPC::Message* message) {
return agent_scheduling_group_.Send(message);
}
bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) {
// We may get here while detaching, when the WebFrame has been deleted. Do
// not process any messages in this state.
if (!frame_)
return false;
DCHECK(!frame_->GetDocument().IsNull());
GetContentClient()->SetActiveURL(
frame_->GetDocument().Url(),
frame_->Top()->GetSecurityOrigin().ToString().Utf8());
for (auto& observer : observers_) {
if (observer.OnMessageReceived(msg))
return true;
}
return false;
}
void RenderFrameImpl::OnAssociatedInterfaceRequest(
const std::string& interface_name,
mojo::ScopedInterfaceEndpointHandle handle) {
if (!associated_interfaces_.TryBindInterface(interface_name, &handle)) {
for (auto& observer : observers_) {
if (observer.OnAssociatedInterfaceRequestForFrame(interface_name,
&handle)) {
return;
}
}
}
}
void RenderFrameImpl::SetUpSharedMemoryForSmoothness(
base::ReadOnlySharedMemoryRegion shared_memory) {
DCHECK(shared_memory.IsValid());
for (auto& observer : observers_) {
DCHECK(shared_memory.IsValid());
if (observer.SetUpSmoothnessReporting(shared_memory))
break;
}
}
void RenderFrameImpl::BindAutoplayConfiguration(
mojo::PendingAssociatedReceiver<blink::mojom::AutoplayConfigurationClient>
receiver) {
autoplay_configuration_receiver_.reset();
autoplay_configuration_receiver_.Bind(
std::move(receiver),
GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
}
void RenderFrameImpl::BindFrameBindingsControl(
mojo::PendingAssociatedReceiver<mojom::FrameBindingsControl> receiver) {
frame_bindings_control_receiver_.Bind(
std::move(receiver),
GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
}
void RenderFrameImpl::BindNavigationClient(
mojo::PendingAssociatedReceiver<mojom::NavigationClient> receiver) {
navigation_client_impl_ = std::make_unique<NavigationClient>(this);
navigation_client_impl_->Bind(std::move(receiver));
}
// Unload this RenderFrame so the frame can navigate to a document rendered by
// a different process. We also allow this process to exit if there are no other
// active RenderFrames in it.
// This executes the unload handlers on this frame and its local descendants.
void RenderFrameImpl::Unload(
int proxy_routing_id,
bool is_loading,
blink::mojom::FrameReplicationStatePtr replicated_frame_state,
const blink::RemoteFrameToken& proxy_frame_token,
mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces) {
TRACE_EVENT1("navigation,rail", "RenderFrameImpl::UnloadFrame", "id",
routing_id_);
DCHECK(!base::RunLoop::IsNestedOnCurrentThread());
// Send an UpdateState message before we get deleted.
// TODO(dcheng): Improve this comment to clarify why it's important to sent
// state updates.
SendUpdateState();
// Before `this` is destroyed, save any fields needed to schedule a call to
// `AgentSchedulingGroupHost::DidUnloadRenderFrame()`. The acknowlegement
// itself is asynchronous to ensure that any postMessage calls (which schedule
// IPCs as well) made from unload handlers are routed to the browser process
// before the corresponding `RenderFrameHostImpl` is torn down.
auto& agent_scheduling_group = agent_scheduling_group_;
blink::LocalFrameToken frame_token = frame_->GetLocalFrameToken();
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
GetTaskRunner(blink::TaskType::kPostedMessage);
// Important: |this| is deleted after this call!
if (!SwapOutAndDeleteThis(
proxy_routing_id, is_loading, std::move(replicated_frame_state),
proxy_frame_token, std::move(remote_main_frame_interfaces))) {
// The swap is cancelled because running the unload handlers ended up
// detaching this frame.
return;
}
// Notify the browser that this frame was swapped out. Use the cached
// `AgentSchedulingGroup` because |this| is deleted. Post a task to send the
// ACK, so that any postMessage IPCs scheduled from the unload handler are
// sent before the ACK (see https://crbug.com/857274).
auto send_unload_ack = base::BindOnce(
[](AgentSchedulingGroup* agent_scheduling_group,
const blink::LocalFrameToken& frame_token) {
agent_scheduling_group->DidUnloadRenderFrame(frame_token);
},
&agent_scheduling_group, frame_token);
task_runner->PostTask(FROM_HERE, std::move(send_unload_ack));
}
void RenderFrameImpl::Delete(mojom::FrameDeleteIntention intent) {
TRACE_EVENT(
"navigation", "RenderFrameImpl::Delete", [&](perfetto::EventContext ctx) {
auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
auto* data = event->set_render_frame_impl_deletion();
data->set_intent(FrameDeleteIntentionToProto(intent));
});
// The main frame (when not provisional) is owned by the renderer's frame tree
// via WebViewImpl. When a provisional main frame is swapped in, the ownership
// moves from the browser to the renderer, but this happens in the renderer
// process and is then the browser is informed.
// If the provisional main frame is swapped in while the browser is destroying
// it, the browser may request to delete |this|, thinking it has ownership
// of it, but the renderer has already taken ownership via SwapIn().
switch (intent) {
case mojom::FrameDeleteIntention::kNotMainFrame:
// The frame was not a main frame, so the browser should always have
// ownership of it and we can just proceed with deleting it on
// request.
DCHECK(!is_main_frame_);
break;
case mojom::FrameDeleteIntention::kSpeculativeMainFrameForShutdown:
// In this case the renderer has taken ownership of the provisional main
// frame but the browser did not know yet and is shutting down. We can
// ignore this request as the frame will be destroyed when the RenderView
// is. This handles the shutdown case of https://crbug.com/957858.
DCHECK(is_main_frame_);
if (in_frame_tree_)
return;
break;
case mojom::FrameDeleteIntention::
kSpeculativeMainFrameForNavigationCancelled:
// In this case the browser was navigating and cancelled the speculative
// navigation. The renderer *should* undo the SwapIn() but the old state
// has already been destroyed. Both ignoring the message or handling it
// would leave the renderer in an inconsistent state now. If we ignore it
// then the browser thinks the RenderView has a remote main frame, but it
// is incorrect. If we handle it, then we are deleting a local main frame
// out from under the RenderView and we will have bad pointers in the
// renderer. So all we can do is crash. We should instead prevent this
// scenario by blocking the browser from dropping the speculative main
// frame when a commit (and ownership transfer) is imminent.
// TODO(dcheng): This is the case of https://crbug.com/838348.
DCHECK(is_main_frame_);
#if !defined(OS_ANDROID)
// This check is not enabled on Android, since it seems like it's much
// easier to trigger data races there.
CHECK(!in_frame_tree_);
#endif // !defined(OS_ANDROID)
break;
}
// This will result in a call to RenderFrameImpl::FrameDetached, which
// deletes the object. Do not access |this| after detach.
frame_->Detach();
}
void RenderFrameImpl::UndoCommitNavigation(
int proxy_routing_id,
bool is_loading,
blink::mojom::FrameReplicationStatePtr replicated_frame_state,
const blink::RemoteFrameToken& proxy_frame_token,
mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces) {
// The browser process asked `this` to commit a navigation but has now decided
// to discard the speculative RenderFrameHostImpl instead, since the
// associated navigation was cancelled or replaced. However, the browser
// process hasn't heard the `DidCommitNavigation()` yet, so pretend that the
// commit never happened by immediately swapping `this` back to a proxy.
//
// This means that any state changes triggered by the already-swapped in
// RenderFrame will simply be ignored, but that can't be helped: the
// browser-side RFH will be gone before any outgoing IPCs from the renderer
// for this RenderFrame (which by definition, are still in-flight) will be
// processed by the browser process (as it has not yet seen the
// `DidCommitNavigation()`).
SwapOutAndDeleteThis(proxy_routing_id, is_loading,
std::move(replicated_frame_state), proxy_frame_token,
std::move(remote_main_frame_interfaces));
}
void RenderFrameImpl::SnapshotAccessibilityTree(
mojom::SnapshotAccessibilityTreeParamsPtr params,
SnapshotAccessibilityTreeCallback callback) {
ui::AXTreeUpdate response;
AXTreeSnapshotterImpl snapshotter(this, ui::AXMode(params->ax_mode));
snapshotter.Snapshot(params->exclude_offscreen, params->max_nodes,
params->timeout, &response);
std::move(callback).Run(response);
}
void RenderFrameImpl::GetSerializedHtmlWithLocalLinks(
const base::flat_map<GURL, base::FilePath>& url_map,
const base::flat_map<blink::FrameToken, base::FilePath>& frame_token_map,
bool save_with_empty_url,
mojo::PendingRemote<mojom::FrameHTMLSerializerHandler> handler_remote) {
// Convert input to the canonical way of passing a map into a Blink API.
LinkRewritingDelegate delegate(url_map, frame_token_map);
RenderFrameWebFrameSerializerClient client(std::move(handler_remote));
// Serialize the frame (without recursing into subframes).
WebFrameSerializer::Serialize(GetWebFrame(), &client, &delegate,
save_with_empty_url);
}
void RenderFrameImpl::SetWantErrorMessageStackTrace() {
want_error_message_stack_trace_ = true;
v8::Isolate::GetCurrent()->SetCaptureStackTraceForUncaughtExceptions(true);
}
void RenderFrameImpl::NotifyObserversOfFailedProvisionalLoad() {
for (auto& observer : observers_)
observer.DidFailProvisionalLoad();
}
void RenderFrameImpl::DidMeaningfulLayout(
blink::WebMeaningfulLayout layout_type) {
for (auto& observer : observers_)
observer.DidMeaningfulLayout(layout_type);
}
void RenderFrameImpl::DidCommitAndDrawCompositorFrame() {
#if BUILDFLAG(ENABLE_PLUGINS)
// Notify all instances that we painted. The same caveats apply as for
// ViewFlushedPaint regarding instances closing themselves, so we take
// similar precautions.
PepperPluginSet plugins = active_pepper_instances_;
for (auto* plugin : plugins) {
if (active_pepper_instances_.find(plugin) != active_pepper_instances_.end())
plugin->ViewInitiatedPaint();
}
#endif
}
RenderView* RenderFrameImpl::GetRenderView() {
return render_view_;
}
RenderFrame* RenderFrameImpl::GetMainRenderFrame() {
WebFrame* main_frame = GetWebView()->MainFrame();
DCHECK(main_frame);
if (!main_frame->IsWebLocalFrame())
return nullptr;
return RenderFrame::FromWebFrame(main_frame->ToWebLocalFrame());
}
RenderAccessibility* RenderFrameImpl::GetRenderAccessibility() {
return render_accessibility_manager_->GetRenderAccessibilityImpl();
}
std::unique_ptr<AXTreeSnapshotter> RenderFrameImpl::CreateAXTreeSnapshotter(
ui::AXMode ax_mode) {
return std::make_unique<AXTreeSnapshotterImpl>(this, ax_mode);
}
int RenderFrameImpl::GetRoutingID() {
return routing_id_;
}
blink::WebLocalFrame* RenderFrameImpl::GetWebFrame() {
DCHECK(frame_);
return frame_;
}
const blink::WebLocalFrame* RenderFrameImpl::GetWebFrame() const {
DCHECK(frame_);
return frame_;
}
blink::WebView* RenderFrameImpl::GetWebView() {
blink::WebView* web_view = GetWebFrame()->View();
DCHECK(web_view);
return web_view;
}
const blink::WebView* RenderFrameImpl::GetWebView() const {
const blink::WebView* web_view = GetWebFrame()->View();
DCHECK(web_view);
return web_view;
}
const blink::web_pref::WebPreferences& RenderFrameImpl::GetBlinkPreferences() {
return GetWebView()->GetWebPreferences();
}
const blink::RendererPreferences& RenderFrameImpl::GetRendererPreferences()
const {
return GetWebView()->GetRendererPreferences();
}
void RenderFrameImpl::ShowVirtualKeyboard() {
GetLocalRootWebFrameWidget()->ShowVirtualKeyboard();
}
blink::WebPlugin* RenderFrameImpl::CreatePlugin(
const WebPluginInfo& info,
const blink::WebPluginParams& params) {
#if BUILDFLAG(ENABLE_PLUGINS)
absl::optional<url::Origin> origin_lock;
if (GetContentClient()->renderer()->IsOriginIsolatedPepperPlugin(info.path)) {
origin_lock = url::Origin::Create(GURL(params.url));
}
bool pepper_plugin_was_registered = false;
scoped_refptr<PluginModule> pepper_module(PluginModule::Create(
this, info, origin_lock, &pepper_plugin_was_registered,
GetTaskRunner(blink::TaskType::kNetworking)));
if (pepper_plugin_was_registered) {
if (pepper_module.get()) {
return new PepperWebPluginImpl(pepper_module.get(), params, this);
}
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
LOG(WARNING) << "Pepper module/plugin creation failed.";
#endif
#endif // BUILDFLAG(ENABLE_PLUGINS)
return nullptr;
}
void RenderFrameImpl::ExecuteJavaScript(const std::u16string& javascript) {
v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
frame_->ExecuteScript(WebScriptSource(WebString::FromUTF16(javascript)));
}
void RenderFrameImpl::BindLocalInterface(
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
GetInterface(interface_name, std::move(interface_pipe));
}
blink::AssociatedInterfaceRegistry*
RenderFrameImpl::GetAssociatedInterfaceRegistry() {
return &associated_interfaces_;
}
blink::AssociatedInterfaceProvider*
RenderFrameImpl::GetRemoteAssociatedInterfaces() {
if (!remote_associated_interfaces_) {
mojo::PendingAssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
remote_interfaces;
agent_scheduling_group_.GetRemoteRouteProvider()->GetRoute(
routing_id_, remote_interfaces.InitWithNewEndpointAndPassReceiver());
remote_associated_interfaces_ =
std::make_unique<blink::AssociatedInterfaceProvider>(
std::move(remote_interfaces),
GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
}
return remote_associated_interfaces_.get();
}
#if BUILDFLAG(ENABLE_PLUGINS)
void RenderFrameImpl::PluginDidStartLoading() {
DidStartLoading();
}
void RenderFrameImpl::PluginDidStopLoading() {
DidStopLoading();
}
#endif // BUILDFLAG(ENABLE_PLUGINS)
bool RenderFrameImpl::IsFTPDirectoryListing() {
return frame_->GetDocumentLoader()->IsListingFtpDirectory();
}
void RenderFrameImpl::SetSelectedText(const std::u16string& selection_text,
size_t offset,
const gfx::Range& range) {
GetWebFrame()->TextSelectionChanged(WebString::FromUTF16(selection_text),
static_cast<uint32_t>(offset), range);
}
void RenderFrameImpl::AddMessageToConsole(
blink::mojom::ConsoleMessageLevel level,
const std::string& message) {
AddMessageToConsoleImpl(level, message, false /* discard_duplicates */);
}
blink::PreviewsState RenderFrameImpl::GetPreviewsState() {
WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
return document_loader ? document_loader->GetPreviewsState()
: blink::PreviewsTypes::PREVIEWS_UNSPECIFIED;
}
bool RenderFrameImpl::IsPasting() {
return GetLocalRootWebFrameWidget()->IsPasting();
}
// blink::mojom::AutoplayConfigurationClient implementation
// --------------------------
void RenderFrameImpl::AddAutoplayFlags(const url::Origin& origin,
const int32_t flags) {
// If the origin is the same as the previously stored flags then we should
// merge the two sets of flags together.
if (autoplay_flags_.first == origin) {
autoplay_flags_.second |= flags;
} else {
autoplay_flags_ = std::make_pair(origin, flags);
}
}
// blink::mojom::ResourceLoadInfoNotifier implementation
// --------------------------
#if defined(OS_ANDROID)
void RenderFrameImpl::NotifyUpdateUserGestureCarryoverInfo() {
GetFrameHost()->UpdateUserGestureCarryoverInfo();
}
#endif
void RenderFrameImpl::NotifyResourceRedirectReceived(
const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr redirect_response) {}
void RenderFrameImpl::NotifyResourceResponseReceived(
int64_t request_id,
const GURL& response_url,
network::mojom::URLResponseHeadPtr response_head,
network::mojom::RequestDestination request_destination,
int32_t previews_state) {
if (!blink::IsRequestDestinationFrame(request_destination)) {
GetFrameHost()->SubresourceResponseStarted(response_url,
response_head->cert_status);
}
DidStartResponse(response_url, request_id, std::move(response_head),
request_destination, previews_state);
}
void RenderFrameImpl::NotifyResourceTransferSizeUpdated(
int64_t request_id,
int32_t transfer_size_diff) {
DidReceiveTransferSizeUpdate(request_id, transfer_size_diff);
}
void RenderFrameImpl::NotifyResourceLoadCompleted(
blink::mojom::ResourceLoadInfoPtr resource_load_info,
const network::URLLoaderCompletionStatus& status) {
DidCompleteResponse(resource_load_info->request_id, status);
GetFrameHost()->ResourceLoadComplete(std::move(resource_load_info));
}
void RenderFrameImpl::NotifyResourceLoadCanceled(int64_t request_id) {
DidCancelResponse(request_id);
}
void RenderFrameImpl::Clone(
mojo::PendingReceiver<blink::mojom::ResourceLoadInfoNotifier>
pending_resource_load_info_notifier) {
resource_load_info_notifier_receivers_.Add(
this, std::move(pending_resource_load_info_notifier),
agent_scheduling_group_.agent_group_scheduler().DefaultTaskRunner());
}
void RenderFrameImpl::GetInterfaceProvider(
mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver) {
auto task_runner = GetTaskRunner(blink::TaskType::kInternalDefault);
DCHECK(task_runner);
interface_provider_receivers_.Add(this, std::move(receiver), task_runner);
}
void RenderFrameImpl::BlockRequests() {
frame_request_blocker_->Block();
}
void RenderFrameImpl::ResumeBlockedRequests() {
frame_request_blocker_->Resume();
}
void RenderFrameImpl::AllowBindings(int32_t enabled_bindings_flags) {
enabled_bindings_ |= enabled_bindings_flags;
}
void RenderFrameImpl::EnableMojoJsBindings() {
enable_mojo_js_bindings_ = true;
}
void RenderFrameImpl::BindWebUI(
mojo::PendingAssociatedReceiver<mojom::WebUI> receiver,
mojo::PendingAssociatedRemote<mojom::WebUIHost> remote) {
DCHECK(enabled_bindings_ & BINDINGS_POLICY_WEB_UI);
WebUIExtensionData::Create(this, std::move(receiver), std::move(remote));
}
void RenderFrameImpl::SetOldPageLifecycleStateFromNewPageCommitIfNeeded(
const blink::mojom::OldPageInfo* old_page_info,
const GURL& new_page_url) {
if (!old_page_info)
return;
RenderFrameImpl* old_main_render_frame = RenderFrameImpl::FromRoutingID(
old_page_info->routing_id_for_old_main_frame);
if (!old_main_render_frame) {
// Even if we sent a valid |routing_id_for_old_main_frame|, it might have
// already been destroyed by the time we try to get the RenderFrame, so
// we should check if it still exists.
return;
}
if (!IsMainFrame() && !old_main_render_frame->IsMainFrame()) {
// This shouldn't happen because `old_page_info` should only be set on
// cross-BrowsingInstance navigations, which can only happen on main frames.
// However, we got some reports of this happening (see
// https://crbug.com/1207271).
SCOPED_CRASH_KEY_BOOL("old_page_info", "new_is_main_frame", IsMainFrame());
SCOPED_CRASH_KEY_STRING256("old_page_info", "new_url", new_page_url.spec());
SCOPED_CRASH_KEY_BOOL("old_page_info", "old_is_main_frame",
old_main_render_frame->IsMainFrame());
SCOPED_CRASH_KEY_STRING256("old_page_info", "old_url",
old_main_render_frame->GetLoadingUrl().spec());
SCOPED_CRASH_KEY_NUMBER("old_page_info", "old_routing_id",
old_page_info->routing_id_for_old_main_frame);
SCOPED_CRASH_KEY_BOOL(
"old_page_info", "old_is_frozen",
old_page_info->new_lifecycle_state_for_old_page->is_frozen);
SCOPED_CRASH_KEY_BOOL("old_page_info", "old_is_in_bfcache",
old_page_info->new_lifecycle_state_for_old_page
->is_in_back_forward_cache);
SCOPED_CRASH_KEY_BOOL(
"old_page_info", "old_is_hidden",
old_page_info->new_lifecycle_state_for_old_page->visibility ==
PageVisibilityState::kHidden);
SCOPED_CRASH_KEY_BOOL(
"old_page_info", "old_pagehide_dispatch",
old_page_info->new_lifecycle_state_for_old_page->pagehide_dispatch ==
blink::mojom::PagehideDispatch::kNotDispatched);
CaptureTraceForNavigationDebugScenario(
DebugScenario::kDebugNonMainFrameWithOldPageInfo);
NOTREACHED();
return;
}
DCHECK_EQ(old_page_info->new_lifecycle_state_for_old_page->visibility,
PageVisibilityState::kHidden);
DCHECK_NE(old_page_info->new_lifecycle_state_for_old_page->pagehide_dispatch,
blink::mojom::PagehideDispatch::kNotDispatched);
WebFrame* old_main_web_frame = old_main_render_frame->GetWebFrame();
old_main_web_frame->View()->SetPageLifecycleStateFromNewPageCommit(
old_page_info->new_lifecycle_state_for_old_page->visibility,
old_page_info->new_lifecycle_state_for_old_page->pagehide_dispatch);
}
void RenderFrameImpl::CommitNavigation(
blink::mojom::CommonNavigationParamsPtr common_params,
blink::mojom::CommitNavigationParamsPtr commit_params,
network::mojom::URLResponseHeadPtr response_head,
mojo::ScopedDataPipeConsumerHandle response_body,
network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
subresource_loader_factories,
absl::optional<std::vector<blink::mojom::TransferrableURLLoaderPtr>>
subresource_overrides,
blink::mojom::ControllerServiceWorkerInfoPtr controller_service_worker_info,
blink::mojom::ServiceWorkerContainerInfoForClientPtr container_info,
mojo::PendingRemote<network::mojom::URLLoaderFactory>
prefetch_loader_factory,
const base::UnguessableToken& devtools_navigation_token,
blink::mojom::PolicyContainerPtr policy_container,
mojom::NavigationClient::CommitNavigationCallback commit_callback) {
DCHECK(navigation_client_impl_);
DCHECK(!blink::IsRendererDebugURL(common_params->url));
DCHECK(!NavigationTypeUtils::IsSameDocument(common_params->navigation_type));
AssertNavigationCommits assert_navigation_commits(
this, kMayReplaceInitialEmptyDocument);
SetOldPageLifecycleStateFromNewPageCommitIfNeeded(
commit_params->old_page_info.get(), common_params->url);
bool was_initiated_in_this_frame =
navigation_client_impl_ &&
navigation_client_impl_->was_initiated_in_this_frame();
// Sanity check that the browser always sends us new loader factories on
// cross-document navigations.
DCHECK(common_params->url.SchemeIs(url::kJavaScriptScheme) ||
common_params->url.IsAboutSrcdoc() || subresource_loader_factories);
int request_id = blink::WebResourceRequestSender::MakeRequestID();
std::unique_ptr<DocumentState> document_state = BuildDocumentStateFromParams(
*common_params, *commit_params, std::move(commit_callback),
std::move(navigation_client_impl_), request_id,
was_initiated_in_this_frame);
// Check if the navigation being committed originated as a client redirect.
bool is_client_redirect =
!!(common_params->transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT);
auto navigation_params =
std::make_unique<WebNavigationParams>(devtools_navigation_token);
navigation_params->is_client_redirect = is_client_redirect;
FillMiscNavigationParams(*common_params, *commit_params,
navigation_params.get());
navigation_params->policy_container =
ToWebPolicyContainer(std::move(policy_container));
auto commit_with_params = base::BindOnce(
&RenderFrameImpl::CommitNavigationWithParams, weak_factory_.GetWeakPtr(),
common_params.Clone(), commit_params.Clone(),
std::move(subresource_loader_factories), std::move(subresource_overrides),
std::move(controller_service_worker_info), std::move(container_info),
std::move(prefetch_loader_factory), std::move(document_state));
// Perform a "loadDataWithBaseURL" navigation. This is different from a normal
// data: URL navigation in various ways:
// - The "document URL" will use the supplied `base_url_for_data_url` if it's
// not empty (otherwise it will fall back to the data: URL).
// - The "unreachable URL" (used for HistoryItem, etc) will use the supplied
// `history_url_for_data_url` if it's not empty (otherwise it will fall back
// to the document URL).
// - The actual data: URL will be saved in the document's DocumentState to
// later be returned as the `url` in DidCommitProvisionalLoadParams.
if (ShouldLoadDataWithBaseURL(is_main_frame_, *common_params,
*commit_params)) {
std::string mime_type, charset, data;
// `base_url` will be set to `base_url_for_data_url` from `common_params`,
// unless it's empty, in which case `url` from `common_params` will be used.
GURL base_url;
DecodeDataURL(*common_params, *commit_params, &mime_type, &charset, &data,
&base_url);
// Note that even though we use the term "base URL", `base_url` is also
// used as the "document URL" (unlike normal "base URL"s set through the
// <base> element or other means, which would only be used to resolve
// relative URLs, etc).
navigation_params->url = base_url;
WebNavigationParams::FillStaticResponse(navigation_params.get(),
WebString::FromUTF8(mime_type),
WebString::FromUTF8(charset), data);
// Even though this is a successful navigation, we set the "unreachable URL"
// to the value of the supplied "history URL". This value will be used for:
// - Setting the HistoryItem URL in
// DocumentLoader::SetHistoryItemStateForCommit(). Note that this is *not*
// used for setting the session history entry's URL in the browser side.
// - Determining whether various states (scroll state, document.state) from
// the previous HistoryItem should be carried over.
// - Determining whether a navigation is a same-URL navigation, and thus
// should result in replacement, in FrameLoader::DetermineFrameLoadType().
// Note that the code there compares the new *document URL* against the old
// *history URL* so if the base URL and the history URL is different, even
// if the loadDataWithBaseURL() navigation is triggered with the exact same
// parameters twice, it will not be classified as "same-URL". This seems to
// be unintended, but the old comment here says this is "Needed so that
// history-url-only changes don't become reloads" so at least it works to
// prevent reloads/replacements when the history URL changes but not
// everything else.
// TODO(https://crbug.com/1223398): Figure out if these behaviors should be
// kept, or even if we should send the history URL to the renderer at all.
navigation_params->unreachable_url =
common_params->history_url_for_data_url;
std::move(commit_with_params).Run(std::move(navigation_params));
return;
}
FillNavigationParamsRequest(*common_params, *commit_params,
navigation_params.get());
if (!url_loader_client_endpoints &&
common_params->url.SchemeIs(url::kDataScheme)) {
// Normally, data urls will have |url_loader_client_endpoints| set.
// However, tests and interstitial pages pass data urls directly,
// without creating url loader.
std::string mime_type, charset, data;
if (!net::DataURL::Parse(common_params->url, &mime_type, &charset, &data)) {
CHECK(false) << "Invalid URL passed: "
<< common_params->url.possibly_invalid_spec();
return;
}
WebNavigationParams::FillStaticResponse(navigation_params.get(),
WebString::FromUTF8(mime_type),
WebString::FromUTF8(charset), data);
} else {
NavigationBodyLoader::FillNavigationParamsResponseAndBodyLoader(
std::move(common_params), std::move(commit_params), request_id,
response_head.Clone(), std::move(response_body),
std::move(url_loader_client_endpoints),
GetTaskRunner(blink::TaskType::kInternalLoading), this,
!frame_->Parent(), navigation_params.get());
}
FillNavigationParamsOriginPolicy(*response_head, navigation_params.get());
// The MHTML mime type should be same as the one we check in the browser
// process's download_utils::MustDownload.
bool is_mhtml_archive =
base::LowerCaseEqualsASCII(response_head->mime_type,
"multipart/related") ||
base::LowerCaseEqualsASCII(response_head->mime_type, "message/rfc822");
if (is_mhtml_archive && navigation_params->body_loader) {
// Load full mhtml archive before committing navigation.
// We need this to retrieve the document mime type prior to committing.
mhtml_body_loader_client_ =
std::make_unique<RenderFrameImpl::MHTMLBodyLoaderClient>(
this, std::move(navigation_params), std::move(commit_with_params));
// The navigation didn't really commit, but lie about it anyway. Why? MHTML
// is a bit special: the renderer process is responsible for parsing the
// archive, but at this point, the response body isn't fully loaded yet.
// Instead, MHTMLBodyLoaderClient will read the entire response body and
// parse the archive to extract the main resource to be committed.
//
// There are two possibilities from this point:
// - |MHTMLBodyLoaderClient::BodyLoadingFinished()| is called. At that
// point, the main resource can be extracted, and the navigation will be
// synchronously committed. If |this| is a provisional frame, it will be
// swapped in and committed. A separate |AssertNavigationCommits| is
// instantiated in |MHTMLBodyLoaderClient::BodyLoadingFinished()| to
// assert the commit actually happens. ✔️
// - Alternatively, the pending archive load may be cancelled. This can only
// happen if the renderer initiates a new navigation, or if the browser
// requests that this frame commit a different navigation.
// - If |this| is already swapped in, the reason for cancelling the
// pending archive load does not matter. There will be no state skew
// between the browser and the renderer, since |this| has already
// committed a navigation. ✔️
// - If |this| is provisional, the pending archive load may only be
// cancelled by the browser requesting |this| to commit a different
// navigation. AssertNavigationCommits ensures the new request ends up
// committing and swapping in |this|, so this is OK. ✔️
navigation_commit_state_ = NavigationCommitState::kDidCommit;
return;
}
// Common case - fill navigation params from provided information and commit.
std::move(commit_with_params).Run(std::move(navigation_params));
}
void RenderFrameImpl::CommitNavigationWithParams(
blink::mojom::CommonNavigationParamsPtr common_params,
blink::mojom::CommitNavigationParamsPtr commit_params,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
subresource_loader_factories,
absl::optional<std::vector<blink::mojom::TransferrableURLLoaderPtr>>
subresource_overrides,
blink::mojom::ControllerServiceWorkerInfoPtr controller_service_worker_info,
blink::mojom::ServiceWorkerContainerInfoForClientPtr container_info,
mojo::PendingRemote<network::mojom::URLLoaderFactory>
prefetch_loader_factory,
std::unique_ptr<DocumentState> document_state,
std::unique_ptr<WebNavigationParams> navigation_params) {
// Here, creator means either the parent frame or the window opener.
bool inherit_loaders_from_creator =
// Iframe with the about:srcdoc URL inherits subresource loaders from
// its parent. If its parent is able to use the FileUrlLoader, then its
// about:srcdoc iframe can use it too.
// TODO(arthursonzogni): Ideally, this decision should be made by the
// browser process. However, giving an iframe the FileUrlLoader mistakenly
// could have terrible consequences (e.g. give access to user's file from
// an unknown website). Inheriting from the parent in the renderer process
// is more conservative and feels more cautious for now.
// TODO(arthursonzogni): Something similar needs to be done for
// about:blank.
common_params->url.IsAboutSrcdoc();
// TODO(lukasza): https://crbug.com/936696: No need to postpone setting the
// |new_loader_factories| once we start swapping RenderFrame^H^H^H
// RenderDocument on every cross-document navigation.
scoped_refptr<blink::ChildURLLoaderFactoryBundle> new_loader_factories;
if (inherit_loaders_from_creator) {
// The browser process didn't provide any way to fetch subresources, it
// expects this document to inherit loaders from its parent.
DCHECK(!subresource_loader_factories);
DCHECK(!subresource_overrides);
DCHECK(!prefetch_loader_factory);
// Presence of the parent is verified by the browser process before
// committing.
//
// TODO(arthursonzogni, dcheng): If `inherit_loaders_from_creator` is ever
// extended to 'about:blank', then `creator` might also need to come from
// the opener.
auto* creator = RenderFrameImpl::FromWebFrame(frame_->Parent());
DCHECK(creator);
new_loader_factories = creator->CloneLoaderFactories();
} else {
new_loader_factories = CreateLoaderFactoryBundle(
std::move(subresource_loader_factories),
std::move(subresource_overrides), std::move(prefetch_loader_factory));
}
DCHECK(new_loader_factories);
DCHECK(new_loader_factories->HasBoundDefaultFactory());
// If the navigation is for "view source", the WebLocalFrame needs to be put
// in a special mode.
if (commit_params->is_view_source)
frame_->EnableViewSourceMode(true);
// Note: this intentionally does not call |Detach()| before |reset()|. If
// there is an active |MHTMLBodyLoaderClient|, the browser-side navigation
// code is explicitly replacing it with a new navigation commit request.
// The check for |kWillCommit| in |~MHTMLBodyLoaderClient| covers this case.
mhtml_body_loader_client_.reset();
PrepareFrameForCommit(common_params->url, *commit_params);
blink::WebFrameLoadType load_type = NavigationTypeToLoadType(
common_params->navigation_type,
common_params->should_replace_current_entry,
blink::PageState::CreateFromEncodedData(commit_params->page_state)
.IsValid());
WebHistoryItem item_for_history_navigation;
blink::mojom::CommitResult commit_status = blink::mojom::CommitResult::Ok;
if (load_type == WebFrameLoadType::kBackForward) {
// We must know the nav entry ID of the page we are navigating back to,
// which should be the case because history navigations are routed via the
// browser.
DCHECK_NE(0, commit_params->nav_entry_id);
// Check that the history navigation can commit.
commit_status = PrepareForHistoryNavigationCommit(
*common_params, *commit_params, &item_for_history_navigation,
&load_type);
}
if (commit_status != blink::mojom::CommitResult::Ok) {
// The browser expects the frame to be loading this navigation. Inform it
// that the load stopped if needed.
if (frame_ && !frame_->IsLoading())
GetFrameHost()->DidStopLoading();
return;
}
navigation_params->frame_load_type = load_type;
navigation_params->history_item = item_for_history_navigation;
if (!container_info) {
// An empty network provider will always be created since it is expected in
// a certain number of places.
navigation_params->service_worker_network_provider =
ServiceWorkerNetworkProviderForFrame::CreateInvalidInstance();
} else {
navigation_params->service_worker_network_provider =
ServiceWorkerNetworkProviderForFrame::Create(
this, std::move(container_info),
std::move(controller_service_worker_info),
network::SharedURLLoaderFactory::Create(
new_loader_factories->CloneWithoutAppCacheFactory()));
}
if (base::FeatureList::IsEnabled(features::kServiceWorkerSubresourceFilter)) {
WebString subresource_filter = navigation_params->response.HttpHeaderField(
WebString::FromUTF8("Service-Worker-Subresource-Filter"));
if (!subresource_filter.IsEmpty()) {
ServiceWorkerNetworkProviderForFrame* provider =
static_cast<ServiceWorkerNetworkProviderForFrame*>(
navigation_params->service_worker_network_provider.get());
DCHECK(provider);
provider->context()->SetSubresourceFilter(subresource_filter.Utf8());
}
}
DCHECK(!pending_loader_factories_);
pending_loader_factories_ = std::move(new_loader_factories);
base::WeakPtr<RenderFrameImpl> weak_self = weak_factory_.GetWeakPtr();
frame_->CommitNavigation(std::move(navigation_params),
std::move(document_state));
// The commit can result in this frame being removed.
if (!weak_self)
return;
pending_loader_factories_ = nullptr;
}
void RenderFrameImpl::CommitFailedNavigation(
blink::mojom::CommonNavigationParamsPtr common_params,
blink::mojom::CommitNavigationParamsPtr commit_params,
bool has_stale_copy_in_cache,
int error_code,
int extended_error_code,
net::ResolveErrorInfo resolve_error_info,
const absl::optional<std::string>& error_page_content,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
subresource_loader_factories,
blink::mojom::PolicyContainerPtr policy_container,
mojom::NavigationClient::CommitFailedNavigationCallback callback) {
TRACE_EVENT1("navigation,benchmark,rail",
"RenderFrameImpl::CommitFailedNavigation", "id", routing_id_);
DCHECK(navigation_client_impl_);
DCHECK(!NavigationTypeUtils::IsSameDocument(common_params->navigation_type));
AssertNavigationCommits assert_navigation_commits(
this, kMayReplaceInitialEmptyDocument);
GetWebView()->SetHistoryListFromNavigation(
commit_params->current_history_list_offset,
commit_params->current_history_list_length);
// Note: this intentionally does not call |Detach()| before |reset()|. If
// there is an active |MHTMLBodyLoaderClient|, the browser-side navigation
// code is explicitly replacing it with a new navigation commit request.
// The check for |kWillCommit| in |~MHTMLBodyLoaderClient| covers this case.
mhtml_body_loader_client_.reset();
GetContentClient()->SetActiveURL(
common_params->url, frame_->Top()->GetSecurityOrigin().ToString().Utf8());
// TODO(lukasza): https://crbug.com/936696: No need to postpone setting the
// |new_loader_factories| once we start swapping RenderFrame^H^H^H
// RenderDocument on every cross-document navigation.
scoped_refptr<blink::ChildURLLoaderFactoryBundle> new_loader_factories =
CreateLoaderFactoryBundle(
std::move(subresource_loader_factories),
absl::nullopt /* subresource_overrides */,
mojo::NullRemote() /* prefetch_loader_factory */);
DCHECK(new_loader_factories->HasBoundDefaultFactory());
// Send the provisional load failure.
WebURLError error(
error_code, extended_error_code, resolve_error_info,
has_stale_copy_in_cache ? WebURLError::HasCopyInCache::kTrue
: WebURLError::HasCopyInCache::kFalse,
WebURLError::IsWebSecurityViolation::kFalse, common_params->url,
WebURLError::ShouldCollapseInitiator::kFalse);
auto navigation_params = std::make_unique<WebNavigationParams>();
FillNavigationParamsRequest(*common_params, *commit_params,
navigation_params.get());
// Use kUnreachableWebDataURL as the document URL (instead of the URL that
// failed to load, which is saved separately as the "unreachable URL" below).
navigation_params->url = GURL(kUnreachableWebDataURL);
// FillNavigationParamsRequest() sets the |navigation_params->http_method| to
// the original method of the request. In successful page loads,
// |navigation_params->redirects| also gets populated and the redirects are
// later replayed to update the method. However, in the case of an error page
// load, the redirects are neither populated nor replayed. Hence |http_method|
// needs to be manually set to the final method.
navigation_params->http_method = WebString::FromASCII(common_params->method);
navigation_params->error_code = error_code;
// This is already checked in `NavigationRequest::OnRequestFailedInternal` and
// `NavigationRequest::OnFailureChecksCompleted` on the browser side, so the
// renderer should never see this.
CHECK_NE(net::ERR_ABORTED, error_code);
if (commit_params->nav_entry_id == 0) {
// For renderer initiated navigations, we send out a
// DidFailProvisionalLoad() notification.
NotifyObserversOfFailedProvisionalLoad();
}
// Replace the current history entry in reloads, and loads of the same url.
// This corresponds to Blink's notion of a standard commit.
// Also replace the current history entry if the browser asked for it
// specifically.
// TODO(clamy): see if initial commits in subframes should be handled
// separately.
bool is_reload_or_history =
NavigationTypeUtils::IsReload(common_params->navigation_type) ||
NavigationTypeUtils::IsHistory(common_params->navigation_type);
bool replace = is_reload_or_history ||
common_params->url == GetLoadingUrl() ||
common_params->should_replace_current_entry;
std::unique_ptr<blink::WebHistoryEntry> history_entry;
auto page_state =
blink::PageState::CreateFromEncodedData(commit_params->page_state);
if (page_state.IsValid())
history_entry = PageStateToHistoryEntry(page_state);
std::string error_html;
std::string* error_html_ptr = &error_html;
if (error_code == net::ERR_HTTP_RESPONSE_CODE_FAILURE) {
DCHECK_NE(commit_params->http_response_code, -1);
GetContentClient()->renderer()->PrepareErrorPageForHttpStatusError(
this, error, navigation_params->http_method.Ascii(),
commit_params->http_response_code, error_html_ptr);
} else {
if (error_page_content) {
error_html = error_page_content.value();
error_html_ptr = nullptr;
}
// Prepare for the error page. Note that even if |error_html_ptr| is set to
// null above, PrepareErrorPage might have other side effects e.g. setting
// some error-related states, so we should still call it.
GetContentClient()->renderer()->PrepareErrorPage(
this, error, navigation_params->http_method.Ascii(), error_html_ptr);
}
// Make sure we never show errors in view source mode.
frame_->EnableViewSourceMode(false);
if (history_entry) {
navigation_params->frame_load_type = WebFrameLoadType::kBackForward;
navigation_params->history_item = history_entry->root();
} else if (replace) {
navigation_params->frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
}
navigation_params->service_worker_network_provider =
ServiceWorkerNetworkProviderForFrame::CreateInvalidInstance();
FillMiscNavigationParams(*common_params, *commit_params,
navigation_params.get());
WebNavigationParams::FillStaticResponse(navigation_params.get(), "text/html",
"UTF-8", error_html);
// Save the URL that failed to load as the "unreachable URL" so that the
// we can use that (instead of kUnreachableWebDataURL) for the HistoryItem for
// this navigation, and also to send back with the DidCommitProvisionalLoad
// message to the browser.
// TODO(https://crbug.com/1131832): Stop sending the URL back with DidCommit.
navigation_params->unreachable_url = error.url();
if (commit_params->redirects.size()) {
navigation_params->pre_redirect_url_for_failed_navigations =
commit_params->redirects[0];
} else {
navigation_params->pre_redirect_url_for_failed_navigations = error.url();
}
navigation_params->policy_container =
ToWebPolicyContainer(std::move(policy_container));
// The error page load (not to confuse with a failed load of original page)
// was not initiated through BeginNavigation, therefore
// |was_initiated_in_this_frame| is false.
std::unique_ptr<DocumentState> document_state = BuildDocumentStateFromParams(
*common_params, *commit_params, std::move(callback),
std::move(navigation_client_impl_),
blink::WebResourceRequestSender::MakeRequestID(),
false /* was_initiated_in_this_frame */);
DCHECK(!pending_loader_factories_);
pending_loader_factories_ = std::move(new_loader_factories);
// The load of the error page can result in this frame being removed.
// Use a WeakPtr as an easy way to detect whether this has occurred. If so,
// this method should return immediately and not touch any part of the object,
// otherwise it will result in a use-after-free bug.
base::WeakPtr<RenderFrameImpl> weak_this = weak_factory_.GetWeakPtr();
frame_->CommitNavigation(std::move(navigation_params),
std::move(document_state));
if (!weak_this)
return;
pending_loader_factories_ = nullptr;
browser_side_navigation_pending_ = false;
}
void RenderFrameImpl::CommitSameDocumentNavigation(
blink::mojom::CommonNavigationParamsPtr common_params,
blink::mojom::CommitNavigationParamsPtr commit_params,
CommitSameDocumentNavigationCallback callback) {
DCHECK(!blink::IsRendererDebugURL(common_params->url));
DCHECK(!NavigationTypeUtils::IsReload(common_params->navigation_type));
DCHECK(!commit_params->is_view_source);
DCHECK(NavigationTypeUtils::IsSameDocument(common_params->navigation_type));
CHECK(in_frame_tree_);
// Unlike a cross-document navigation commit, detach the MHTMLBodyLoaderClient
// before resetting it. In the case of a cross-document navigation, it's
// important to ensure *something* commits, even if the original commit
// request was replaced by a commit request. However, in the case of a
// same-document navigation commit request, |this| must already be committed.
//
// Note that this means a same-document navigation might cancel a
// cross-document navigation, which is a bit strange. In the future, explore
// the idea of allowing the cross-document navigation to continue.
if (mhtml_body_loader_client_) {
mhtml_body_loader_client_->Detach();
mhtml_body_loader_client_.reset();
}
PrepareFrameForCommit(common_params->url, *commit_params);
blink::WebFrameLoadType load_type = NavigationTypeToLoadType(
common_params->navigation_type,
common_params->should_replace_current_entry,
blink::PageState::CreateFromEncodedData(commit_params->page_state)
.IsValid());
blink::mojom::CommitResult commit_status = blink::mojom::CommitResult::Ok;
WebHistoryItem item_for_history_navigation;
if (common_params->navigation_type ==
blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT) {
DCHECK(blink::PageState::CreateFromEncodedData(commit_params->page_state)
.IsValid());
// We must know the nav entry ID of the page we are navigating back to,
// which should be the case because history navigations are routed via the
// browser.
DCHECK_NE(0, commit_params->nav_entry_id);
DCHECK(!common_params->is_history_navigation_in_new_child_frame);
commit_status = PrepareForHistoryNavigationCommit(
*common_params, *commit_params, &item_for_history_navigation,
&load_type);
}
if (commit_status == blink::mojom::CommitResult::Ok) {
base::WeakPtr<RenderFrameImpl> weak_this = weak_factory_.GetWeakPtr();
// Same-document navigations on data URLs loaded with a valid base URL
// should keep the base URL as document URL.
bool use_base_url_for_data_url =
!common_params->base_url_for_data_url.is_empty();
#if defined(OS_ANDROID)
use_base_url_for_data_url |= !commit_params->data_url_as_string.empty();
#endif
GURL url;
if (is_main_frame_ && use_base_url_for_data_url) {
url = common_params->base_url_for_data_url;
} else {
url = common_params->url;
}
bool is_client_redirect =
!!(common_params->transition & ui::PAGE_TRANSITION_CLIENT_REDIRECT);
bool has_transient_activation = common_params->has_user_gesture;
WebSecurityOrigin initiator_origin;
if (common_params->initiator_origin)
initiator_origin = common_params->initiator_origin.value();
DocumentState* original_document_state =
DocumentState::FromDocumentLoader(frame_->GetDocumentLoader());
std::unique_ptr<DocumentState> document_state =
original_document_state->Clone();
InternalDocumentStateData* internal_data =
InternalDocumentStateData::FromDocumentState(document_state.get());
internal_data->CopyFrom(
InternalDocumentStateData::FromDocumentState(original_document_state));
// This is a same-document navigation coming from the browser process (as
// opposed to a fragment link click, which would have been handled
// synchronously in the renderer process), therefore
// |was_initiated_in_this_frame| must be false.
internal_data->set_navigation_state(NavigationState::Create(
std::move(common_params), std::move(commit_params),
mojom::NavigationClient::CommitNavigationCallback(), nullptr,
false /* was_initiated_in_this_frame */));
// Load the request.
commit_status = frame_->CommitSameDocumentNavigation(
url, load_type, item_for_history_navigation, is_client_redirect,
has_transient_activation, initiator_origin, std::move(document_state));
// The load of the URL can result in this frame being removed. Use a
// WeakPtr as an easy way to detect whether this has occured. If so, this
// method should return immediately and not touch any part of the object,
// otherwise it will result in a use-after-free bug.
if (!weak_this)
return;
}
std::move(callback).Run(commit_status);
// The browser expects the frame to be loading this navigation. Inform it
// that the load stopped if needed.
if (frame_ && !frame_->IsLoading() &&
commit_status != blink::mojom::CommitResult::Ok) {
GetFrameHost()->DidStopLoading();
}
}
void RenderFrameImpl::UpdateSubresourceLoaderFactories(
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
subresource_loader_factories) {
if (loader_factories_->IsHostChildURLLoaderFactoryBundle()) {
static_cast<blink::HostChildURLLoaderFactoryBundle*>(
loader_factories_.get())
->UpdateThisAndAllClones(std::move(subresource_loader_factories));
} else {
#if DCHECK_IS_ON()
// This situation should happen only if the frame hosts a document that
// isn't related to a real navigation (i.e. if the frame should "inherit"
// the factories from its opener/parent - for example for about:blank or
// about:srcdoc or about:blank#someHref frames, or for frames with no URL
// - like the initial frame opened by window('', 'popup')).
WebURL url = GetWebFrame()->GetDocument().Url();
if (url.IsValid() && !url.IsEmpty())
DCHECK(url.ProtocolIs(url::kAboutScheme));
#endif
auto partial_bundle =
base::MakeRefCounted<blink::ChildURLLoaderFactoryBundle>();
static_cast<blink::URLLoaderFactoryBundle*>(partial_bundle.get())
->Update(std::move(subresource_loader_factories));
loader_factories_->Update(partial_bundle->PassInterface());
}
}
// blink::WebLocalFrameClient implementation
// ----------------------------------------
blink::BrowserInterfaceBrokerProxy*
RenderFrameImpl::GetBrowserInterfaceBroker() {
return &browser_interface_broker_proxy_;
}
bool RenderFrameImpl::IsPluginHandledExternally(
const blink::WebElement& plugin_element,
const blink::WebURL& url,
const blink::WebString& suggested_mime_type) {
#if BUILDFLAG(ENABLE_PLUGINS)
return GetContentClient()->renderer()->IsPluginHandledExternally(
this, plugin_element, GURL(url), suggested_mime_type.Utf8());
#else
return false;
#endif
}
v8::Local<v8::Object> RenderFrameImpl::GetScriptableObject(
const blink::WebElement& plugin_element,
v8::Isolate* isolate) {
#if BUILDFLAG(ENABLE_PLUGINS)
return GetContentClient()->renderer()->GetScriptableObject(plugin_element,
isolate);
#else
return v8::Local<v8::Object>();
#endif
}
void RenderFrameImpl::UpdateSubresourceFactory(
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> info) {
auto child_info = std::make_unique<blink::ChildPendingURLLoaderFactoryBundle>(
std::move(info));
GetLoaderFactoryBundle()->Update(std::move(child_info));
}
void RenderFrameImpl::BindToFrame(blink::WebNavigationControl* frame) {
DCHECK(!frame_);
std::pair<FrameMap::iterator, bool> result =
g_frame_map.Get().emplace(frame, this);
CHECK(result.second) << "Inserting a duplicate item.";
frame_ = frame;
}
blink::WebPlugin* RenderFrameImpl::CreatePlugin(
const blink::WebPluginParams& params) {
blink::WebPlugin* plugin = nullptr;
if (GetContentClient()->renderer()->OverrideCreatePlugin(this, params,
&plugin)) {
return plugin;
}
#if BUILDFLAG(ENABLE_PLUGINS)
WebPluginInfo info;
std::string mime_type;
bool found = false;
GetPepperHost()->GetPluginInfo(params.url, frame_->Top()->GetSecurityOrigin(),
params.mime_type.Utf8(), &found, &info,
&mime_type);
if (!found)
return nullptr;
WebPluginParams params_to_use = params;
params_to_use.mime_type = WebString::FromUTF8(mime_type);
return CreatePlugin(info, params_to_use);
#else
return nullptr;
#endif // BUILDFLAG(ENABLE_PLUGINS)
}
blink::WebMediaPlayer* RenderFrameImpl::CreateMediaPlayer(
const blink::WebMediaPlayerSource& source,
WebMediaPlayerClient* client,
blink::MediaInspectorContext* inspector_context,
WebMediaPlayerEncryptedMediaClient* encrypted_client,
WebContentDecryptionModule* initial_cdm,
const blink::WebString& sink_id,
const cc::LayerTreeSettings& settings) {
return media_factory_.CreateMediaPlayer(
source, client, inspector_context, encrypted_client, initial_cdm, sink_id,
GetLocalRootWebFrameWidget()->GetFrameSinkId(), settings,
agent_scheduling_group_.agent_group_scheduler().CompositorTaskRunner());
}
std::unique_ptr<blink::WebContentSettingsClient>
RenderFrameImpl::CreateWorkerContentSettingsClient() {
if (!frame_ || !frame_->View())
return nullptr;
return GetContentClient()->renderer()->CreateWorkerContentSettingsClient(
this);
}
#if !defined(OS_ANDROID)
std::unique_ptr<media::SpeechRecognitionClient>
RenderFrameImpl::CreateSpeechRecognitionClient(
media::SpeechRecognitionClient::OnReadyCallback callback) {
if (!frame_ || !frame_->View())
return nullptr;
return GetContentClient()->renderer()->CreateSpeechRecognitionClient(
this, std::move(callback));
}
#endif
scoped_refptr<blink::WebWorkerFetchContext>
RenderFrameImpl::CreateWorkerFetchContext() {
ServiceWorkerNetworkProviderForFrame* provider =
static_cast<ServiceWorkerNetworkProviderForFrame*>(
frame_->GetDocumentLoader()->GetServiceWorkerNetworkProvider());
DCHECK(provider);
mojo::PendingReceiver<blink::mojom::RendererPreferenceWatcher>
watcher_receiver;
GetWebView()->RegisterRendererPreferenceWatcher(
watcher_receiver.InitWithNewPipeAndPassRemote());
mojo::PendingRemote<blink::mojom::ResourceLoadInfoNotifier>
pending_resource_load_info_notifier;
resource_load_info_notifier_receivers_.Add(
this,
pending_resource_load_info_notifier.InitWithNewPipeAndPassReceiver(),
agent_scheduling_group_.agent_group_scheduler().DefaultTaskRunner());
std::vector<std::string> cors_exempt_header_list =
RenderThreadImpl::current()->cors_exempt_header_list();
blink::WebVector<blink::WebString> web_cors_exempt_header_list(
cors_exempt_header_list.size());
std::transform(cors_exempt_header_list.begin(), cors_exempt_header_list.end(),
web_cors_exempt_header_list.begin(), [](const std::string& h) {
return blink::WebString::FromLatin1(h);
});
// |pending_subresource_loader_updater| and
// |pending_resource_load_info_notifier| are not used for
// non-PlzDedicatedWorker and worklets.
scoped_refptr<blink::WebDedicatedOrSharedWorkerFetchContext>
web_dedicated_or_shared_worker_fetch_context =
blink::WebDedicatedOrSharedWorkerFetchContext::Create(
provider->context(), GetWebView()->GetRendererPreferences(),
std::move(watcher_receiver), GetLoaderFactoryBundle()->Clone(),
GetLoaderFactoryBundle()->CloneWithoutAppCacheFactory(),
/*pending_subresource_loader_updater=*/mojo::NullReceiver(),
web_cors_exempt_header_list,
std::move(pending_resource_load_info_notifier));
web_dedicated_or_shared_worker_fetch_context->set_ancestor_frame_id(
routing_id_);
web_dedicated_or_shared_worker_fetch_context->set_frame_request_blocker(
frame_request_blocker_);
web_dedicated_or_shared_worker_fetch_context->set_site_for_cookies(
frame_->GetDocument().SiteForCookies());
web_dedicated_or_shared_worker_fetch_context->set_top_frame_origin(
frame_->GetDocument().TopFrameOrigin());
for (auto& observer : observers_) {
observer.WillCreateWorkerFetchContext(
web_dedicated_or_shared_worker_fetch_context.get());
}
return web_dedicated_or_shared_worker_fetch_context;
}
scoped_refptr<blink::WebWorkerFetchContext>
RenderFrameImpl::CreateWorkerFetchContextForPlzDedicatedWorker(
blink::WebDedicatedWorkerHostFactoryClient* factory_client) {
DCHECK(base::FeatureList::IsEnabled(blink::features::kPlzDedicatedWorker));
DCHECK(factory_client);
mojo::PendingReceiver<blink::mojom::RendererPreferenceWatcher>
watcher_receiver;
GetWebView()->RegisterRendererPreferenceWatcher(
watcher_receiver.InitWithNewPipeAndPassRemote());
mojo::PendingRemote<blink::mojom::ResourceLoadInfoNotifier>
pending_resource_load_info_notifier;
resource_load_info_notifier_receivers_.Add(
this,
pending_resource_load_info_notifier.InitWithNewPipeAndPassReceiver(),
agent_scheduling_group_.agent_group_scheduler().DefaultTaskRunner());
scoped_refptr<blink::WebDedicatedOrSharedWorkerFetchContext>
web_dedicated_or_shared_worker_fetch_context =
static_cast<DedicatedWorkerHostFactoryClient*>(factory_client)
->CreateWorkerFetchContext(
GetWebView()->GetRendererPreferences(),
std::move(watcher_receiver),
std::move(pending_resource_load_info_notifier));
web_dedicated_or_shared_worker_fetch_context->set_ancestor_frame_id(
routing_id_);
web_dedicated_or_shared_worker_fetch_context->set_frame_request_blocker(
frame_request_blocker_);
web_dedicated_or_shared_worker_fetch_context->set_site_for_cookies(
frame_->GetDocument().SiteForCookies());
web_dedicated_or_shared_worker_fetch_context->set_top_frame_origin(
frame_->GetDocument().TopFrameOrigin());
for (auto& observer : observers_) {
observer.WillCreateWorkerFetchContext(
web_dedicated_or_shared_worker_fetch_context.get());
}
return web_dedicated_or_shared_worker_fetch_context;
}
std::unique_ptr<blink::WebPrescientNetworking>
RenderFrameImpl::CreatePrescientNetworking() {
return GetContentClient()->renderer()->CreatePrescientNetworking(this);
}
std::unique_ptr<blink::ResourceLoadInfoNotifierWrapper>
RenderFrameImpl::CreateResourceLoadInfoNotifierWrapper() {
return std::make_unique<blink::ResourceLoadInfoNotifierWrapper>(
weak_wrapper_resource_load_info_notifier_->AsWeakPtr());
}
blink::BlameContext* RenderFrameImpl::GetFrameBlameContext() {
DCHECK(blame_context_);
return blame_context_.get();
}
std::unique_ptr<blink::WebServiceWorkerProvider>
RenderFrameImpl::CreateServiceWorkerProvider() {
// Bail-out if we are about to be navigated away.
// We check that DocumentLoader is attached since:
// - This serves as the signal since the DocumentLoader is detached in
// FrameLoader::PrepareForCommit().
// - Creating ServiceWorkerProvider in
// RenderFrameImpl::CreateServiceWorkerProvider() assumes that there is a
// DocumentLoader attached to the frame.
if (!frame_->GetDocumentLoader())
return nullptr;
// At this point we should have non-null data source.
if (!ChildThreadImpl::current())
return nullptr; // May be null in some tests.
ServiceWorkerNetworkProviderForFrame* provider =
static_cast<ServiceWorkerNetworkProviderForFrame*>(
frame_->GetDocumentLoader()->GetServiceWorkerNetworkProvider());
if (!provider->context()) {
// The context can be null when the frame is sandboxed.
return nullptr;
}
return std::make_unique<WebServiceWorkerProviderImpl>(provider->context());
}
blink::AssociatedInterfaceProvider*
RenderFrameImpl::GetRemoteNavigationAssociatedInterfaces() {
return GetRemoteAssociatedInterfaces();
}
blink::WebLocalFrame* RenderFrameImpl::CreateChildFrame(
blink::mojom::TreeScopeType scope,
const blink::WebString& name,
const blink::WebString& fallback_name,
const blink::FramePolicy& frame_policy,
const blink::WebFrameOwnerProperties& frame_owner_properties,
blink::mojom::FrameOwnerElementType frame_owner_element_type,
blink::WebPolicyContainerBindParams policy_container_bind_params) {
// Allocate child routing ID. This is a synchronous call.
int child_routing_id;
blink::LocalFrameToken frame_token;
base::UnguessableToken devtools_frame_token;
if (!RenderThread::Get()->GenerateFrameRoutingID(
child_routing_id, frame_token, devtools_frame_token)) {
return nullptr;
}
// The unique name generation logic was moved out of Blink, so for historical
// reasons, unique name generation needs to take something called the
// |fallback_name| into account. Normally, unique names are generated based on
// the browing context name. For new frames, the initial browsing context name
// comes from the name attribute of the browsing context container element.
//
// However, when the browsing context name is null, Blink instead uses the
// "fallback name" to derive the unique name. The exact contents of the
// "fallback name" are unspecified, but may contain the value of the
// 'subresource attribute' of the browsing context container element.
//
// Note that Blink can't be changed to just pass |fallback_name| as |name| in
// the case |name| is empty: |fallback_name| should never affect the actual
// browsing context name, only unique name generation.
bool is_created_by_script =
v8::Isolate::GetCurrent() && v8::Isolate::GetCurrent()->InContext();
std::string frame_unique_name =
unique_name_helper_.GenerateNameForNewChildFrame(
name.IsEmpty() ? fallback_name.Utf8() : name.Utf8(),
is_created_by_script);
mojo::PendingAssociatedReceiver<mojom::Frame> pending_frame_receiver;
mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
browser_interface_broker;
// Now create the child frame in the browser via an asynchronous call.
GetFrameHost()->CreateChildFrame(
child_routing_id,
pending_frame_receiver.InitWithNewEndpointAndPassRemote(),
browser_interface_broker.InitWithNewPipeAndPassReceiver(),
blink::mojom::PolicyContainerBindParams::New(
std::move(policy_container_bind_params.receiver)),
scope, name.Utf8(), frame_unique_name, is_created_by_script, frame_policy,
blink::mojom::FrameOwnerProperties::From(frame_owner_properties),
frame_owner_element_type);
// Tracing analysis uses this to find main frames when this value is
// MSG_ROUTING_NONE, and build the frame tree otherwise.
TRACE_EVENT2("navigation,rail", "RenderFrameImpl::createChildFrame", "id",
routing_id_, "child", child_routing_id);
// Create the RenderFrame and WebLocalFrame, linking the two.
RenderFrameImpl* child_render_frame = RenderFrameImpl::Create(
agent_scheduling_group_, render_view_, child_routing_id,
std::move(pending_frame_receiver), std::move(browser_interface_broker),
devtools_frame_token);
child_render_frame->loader_factories_ = CloneLoaderFactories();
child_render_frame->unique_name_helper_.set_propagated_name(
frame_unique_name);
if (is_created_by_script)
child_render_frame->unique_name_helper_.Freeze();
child_render_frame->InitializeBlameContext(this);
blink::WebLocalFrame* web_frame = frame_->CreateLocalChild(
scope, child_render_frame,
child_render_frame->blink_interface_registry_.get(), frame_token);
child_render_frame->in_frame_tree_ = true;
// The WebLocalFrame created here is not fully initialized yet, so we delay
// our Initialize() step until that has completed, at which point
// InitializeAsChildFrame() is called.
return web_frame;
}
void RenderFrameImpl::InitializeAsChildFrame(blink::WebLocalFrame* parent) {
Initialize(parent);
}
std::pair<blink::WebRemoteFrame*, blink::PortalToken>
RenderFrameImpl::CreatePortal(
blink::CrossVariantMojoAssociatedReceiver<blink::mojom::PortalInterfaceBase>
portal_endpoint,
blink::CrossVariantMojoAssociatedRemote<
blink::mojom::PortalClientInterfaceBase> client_endpoint,
const blink::WebElement& portal_element) {
int proxy_routing_id = MSG_ROUTING_NONE;
blink::mojom::FrameReplicationStatePtr initial_replicated_state =
blink::mojom::FrameReplicationState::New();
blink::PortalToken portal_token;
blink::RemoteFrameToken frame_token;
base::UnguessableToken devtools_frame_token;
GetFrameHost()->CreatePortal(std::move(portal_endpoint),
std::move(client_endpoint), &proxy_routing_id,
&initial_replicated_state, &portal_token,
&frame_token, &devtools_frame_token);
RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyForPortalOrFencedFrame(
agent_scheduling_group_, this, proxy_routing_id, frame_token,
devtools_frame_token, portal_element);
proxy->SetReplicatedState(std::move(initial_replicated_state));
return std::make_pair(proxy->web_frame(), portal_token);
}
blink::WebRemoteFrame* RenderFrameImpl::AdoptPortal(
const blink::PortalToken& portal_token,
const blink::WebElement& portal_element) {
int proxy_routing_id = MSG_ROUTING_NONE;
blink::mojom::FrameReplicationStatePtr replicated_state =
blink::mojom::FrameReplicationState::New();
blink::RemoteFrameToken frame_token;
base::UnguessableToken devtools_frame_token;
GetFrameHost()->AdoptPortal(portal_token, &proxy_routing_id,
&replicated_state, &frame_token,
&devtools_frame_token);
RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyForPortalOrFencedFrame(
agent_scheduling_group_, this, proxy_routing_id, frame_token,
devtools_frame_token, portal_element);
proxy->SetReplicatedState(std::move(replicated_state));
return proxy->web_frame();
}
blink::WebRemoteFrame* RenderFrameImpl::CreateFencedFrame(
const blink::WebElement& fenced_frame) {
int proxy_routing_id = MSG_ROUTING_NONE;
blink::mojom::FrameReplicationStatePtr initial_replicated_state =
blink::mojom::FrameReplicationState::New();
blink::RemoteFrameToken frame_token;
base::UnguessableToken devtools_frame_token;
// TODO(crbug.com/1123606): Call mojom::FrameHost::CreateFencedFrame() once we
// introduce it in a subsequent CL.
RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyForPortalOrFencedFrame(
agent_scheduling_group_, this, proxy_routing_id, frame_token,
devtools_frame_token, fenced_frame);
proxy->SetReplicatedState(std::move(initial_replicated_state));
return proxy->web_frame();
}
blink::WebFrame* RenderFrameImpl::FindFrame(const blink::WebString& name) {
if (render_view_->renderer_wide_named_frame_lookup()) {
for (const auto& it : g_routing_id_frame_map.Get()) {
WebLocalFrame* frame = it.second->GetWebFrame();
if (frame->AssignedName() == name)
return frame;
}
}
return GetContentClient()->renderer()->FindFrame(this->GetWebFrame(),
name.Utf8());
}
void RenderFrameImpl::WillDetach() {
for (auto& observer : observers_)
observer.WillDetach();
// blink::WebAudioOutputIPCFactory::io_task_runner_ may be null in tests.
auto& factory = blink::WebAudioOutputIPCFactory::GetInstance();
if (factory.io_task_runner())
factory.MaybeDeregisterRemoteFactory(GetWebFrame()->GetLocalFrameToken());
// Send a state update before the frame is detached.
SendUpdateState();
}
void RenderFrameImpl::FrameDetached() {
// We need to clean up subframes by removing them from the map and deleting
// the RenderFrameImpl. In contrast, the main frame is owned by its
// containing RenderViewHost (so that they have the same lifetime), so only
// removal from the map is needed and no deletion.
auto it = g_frame_map.Get().find(frame_);
CHECK(it != g_frame_map.Get().end());
CHECK_EQ(it->second, this);
g_frame_map.Get().erase(it);
// RenderAccessibilityManager keeps a reference to the RenderFrame that owns
// it, so we need to clear the pointer to prevent invalid access after the
// frame gets closed and deleted.
render_accessibility_manager_.reset();
// |frame_| may not be referenced after this, so clear the pointer since
// the actual WebLocalFrame may not be deleted immediately and other methods
// may try to access it.
frame_->Close();
frame_ = nullptr;
if (mhtml_body_loader_client_) {
mhtml_body_loader_client_->Detach();
mhtml_body_loader_client_.reset();
}
delete this;
// Object is invalid after this point.
}
void RenderFrameImpl::DidChangeName(const blink::WebString& name) {
if (GetWebFrame()->GetCurrentHistoryItem().IsNull()) {
// Once a navigation has committed, the unique name must no longer change to
// avoid breaking back/forward navigations: https://crbug.com/607205
unique_name_helper_.UpdateName(name.Utf8());
}
GetFrameHost()->DidChangeName(name.Utf8(), unique_name_helper_.value());
}
void RenderFrameImpl::DidMatchCSS(
const blink::WebVector<blink::WebString>& newly_matching_selectors,
const blink::WebVector<blink::WebString>& stopped_matching_selectors) {
for (auto& observer : observers_)
observer.DidMatchCSS(newly_matching_selectors, stopped_matching_selectors);
}
bool RenderFrameImpl::ShouldReportDetailedMessageForSourceAndSeverity(
blink::mojom::ConsoleMessageLevel log_level,
const blink::WebString& source) {
if (want_error_message_stack_trace_ &&
log_level == blink::mojom::ConsoleMessageLevel::kError) {
return true;
}
return GetContentClient()->renderer()->ShouldReportDetailedMessageForSource(
source.Utf16());
}
void RenderFrameImpl::DidAddMessageToConsole(
const blink::WebConsoleMessage& message,
const blink::WebString& source_name,
unsigned source_line,
const blink::WebString& stack_trace) {
if (ShouldReportDetailedMessageForSourceAndSeverity(message.level,
source_name)) {
for (auto& observer : observers_) {
observer.DetailedConsoleMessageAdded(
message.text.Utf16(), source_name.Utf16(), stack_trace.Utf16(),
source_line, blink::ConsoleMessageLevelToLogSeverity(message.level));
}
}
}
void RenderFrameImpl::WillSendSubmitEvent(const blink::WebFormElement& form) {
for (auto& observer : observers_)
observer.WillSendSubmitEvent(form);
}
void RenderFrameImpl::DidCreateDocumentLoader(
blink::WebDocumentLoader* document_loader) {
DocumentState* document_state =
DocumentState::FromDocumentLoader(document_loader);
if (!document_state) {
// This must be an initial empty document.
document_loader->SetExtraData(BuildDocumentState());
document_loader->SetServiceWorkerNetworkProvider(
ServiceWorkerNetworkProviderForFrame::CreateInvalidInstance());
}
}
void RenderFrameImpl::DidCommitNavigation(
blink::WebHistoryCommitType commit_type,
bool should_reset_browser_interface_broker,
const blink::ParsedPermissionsPolicy& permissions_policy_header,
const blink::DocumentPolicyFeatureState& document_policy_header) {
CHECK_EQ(NavigationCommitState::kWillCommit, navigation_commit_state_);
navigation_commit_state_ = NavigationCommitState::kDidCommit;
WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
InternalDocumentStateData* internal_data =
InternalDocumentStateData::FromDocumentLoader(document_loader);
NavigationState* navigation_state = internal_data->navigation_state();
DCHECK(!navigation_state->WasWithinSameDocument());
TRACE_EVENT2("navigation,benchmark,rail",
"RenderFrameImpl::didStartProvisionalLoad", "id", routing_id_,
"url", document_loader->GetUrl().GetString().Utf8());
// Install factories as early as possible - it needs to happen before the
// newly committed document starts any subresource fetches. In particular,
// this needs to happen before invoking
// RenderFrameObserver::ReadyToCommitNavigation below.
if (pending_loader_factories_) {
// Commits triggered by the browser process should always provide
// |pending_loader_factories_|.
loader_factories_ = std::move(pending_loader_factories_);
}
DCHECK(loader_factories_);
DCHECK(loader_factories_->HasBoundDefaultFactory());
// TODO(dgozman): call DidStartNavigation in various places where we call
// CommitNavigation() on the frame.
if (!navigation_state->was_initiated_in_this_frame()) {
// Navigation initiated in this frame has been already reported in
// BeginNavigation.
for (auto& observer : observers_)
observer.DidStartNavigation(document_loader->GetUrl(), absl::nullopt);
}
for (auto& observer : observers_)
observer.ReadyToCommitNavigation(document_loader);
for (auto& observer : observers_)
observer.DidCreateNewDocument();
DVLOG(1) << "Committed provisional load: "
<< TrimURL(GetLoadingUrl().possibly_invalid_spec());
TRACE_EVENT2("navigation,rail", "RenderFrameImpl::didCommitProvisionalLoad",
"id", routing_id_, "url",
GetLoadingUrl().possibly_invalid_spec());
// Generate a new embedding token on each document change.
GetWebFrame()->SetEmbeddingToken(base::UnguessableToken::Create());
mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
browser_interface_broker_receiver;
// blink passes true when the new pipe needs to be bound.
if (should_reset_browser_interface_broker) {
// If we're navigating to a new document, bind
// |browser_interface_broker_proxy_| to a new browser interface broker. The
// request end of the new BrowserInterfaceBroker interface will be sent over
// as part of DidCommitProvisionalLoad. After the RFHI receives the commit
// confirmation, it will immediately close the old message pipe to avoid
// GetInterface() calls racing with navigation commit, and bind the request
// end of the message pipe created here. Must initialize
// |browser_interface_broker_proxy_| with a new working pipe *before*
// observers receive DidCommitProvisionalLoad, so they can already request
// remote interfaces. The interface requests will be serviced once the
// BrowserInterfaceBroker interface request is bound by the
// RenderFrameHostImpl.
browser_interface_broker_receiver = browser_interface_broker_proxy_.Reset(
agent_scheduling_group_.agent_group_scheduler().DefaultTaskRunner());
// blink::WebAudioOutputIPCFactory::io_task_runner_ may be null in tests.
auto& factory = blink::WebAudioOutputIPCFactory::GetInstance();
if (factory.io_task_runner()) {
// The RendererAudioOutputStreamFactory must be readily accessible on the
// IO thread when it's needed, because the main thread may block while
// waiting for the factory call to finish on the IO thread, so if we tried
// to lazily initialize it, we could deadlock.
//
// TODO(https://crbug.com/668275): Still, it is odd for one specific
// factory to be registered here, make this a RenderFrameObserver.
// code.
factory.MaybeDeregisterRemoteFactory(GetWebFrame()->GetLocalFrameToken());
factory.RegisterRemoteFactory(GetWebFrame()->GetLocalFrameToken(),
GetBrowserInterfaceBroker());
}
// If the request for |audio_input_stream_factory_| is in flight when
// |browser_interface_broker_proxy_| is reset, it will be silently dropped.
// We reset |audio_input_stream_factory_| to force a new mojo request to be
// sent the next time it's used. See https://crbug.com/795258 for
// implementing a nicer solution.
audio_input_stream_factory_.reset();
render_accessibility_manager_->CloseConnection();
}
// Notify the MediaPermissionDispatcher that its connection will be closed
// due to a navigation to a different document.
if (media_permission_dispatcher_)
media_permission_dispatcher_->OnNavigation();
ui::PageTransition transition =
GetTransitionType(frame_->GetDocumentLoader(), IsMainFrame());
DidCommitNavigationInternal(
commit_type, transition, permissions_policy_header,
document_policy_header,
should_reset_browser_interface_broker
? mojom::DidCommitProvisionalLoadInterfaceParams::New(
std::move(browser_interface_broker_receiver))
: nullptr,
nullptr /* same_document_params */, GetWebFrame()->GetEmbeddingToken());
// If we end up reusing this WebRequest (for example, due to a #ref click),
// we don't want the transition type to persist. Just clear it.
navigation_state->set_transition_type(ui::PAGE_TRANSITION_LINK);
// Check whether we have new encoding name.
UpdateEncoding(frame_, frame_->View()->PageEncoding().Utf8());
NotifyObserversOfNavigationCommit(transition);
}
void RenderFrameImpl::DidCommitDocumentReplacementNavigation(
blink::WebDocumentLoader* document_loader) {
// TODO(https://crbug.com/855189): figure out which of the following observer
// calls are necessary, if any.
for (auto& observer : observers_)
observer.DidStartNavigation(document_loader->GetUrl(), absl::nullopt);
for (auto& observer : observers_)
observer.ReadyToCommitNavigation(document_loader);
for (auto& observer : observers_)
observer.DidCreateNewDocument();
ui::PageTransition transition =
GetTransitionType(document_loader, IsMainFrame());
NotifyObserversOfNavigationCommit(transition);
}
void RenderFrameImpl::DidClearWindowObject() {
v8::MicrotasksScope microtasks(blink::MainThreadIsolate(),
v8::MicrotasksScope::kDoNotRunMicrotasks);
if (enabled_bindings_ & BINDINGS_POLICY_WEB_UI)
WebUIExtension::Install(frame_);
if (enabled_bindings_ & BINDINGS_POLICY_DOM_AUTOMATION)
DomAutomationController::Install(this, frame_);
if (enabled_bindings_ & BINDINGS_POLICY_STATS_COLLECTION)
StatsCollectionController::Install(frame_);
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(cc::switches::kEnableGpuBenchmarking))
GpuBenchmarking::Install(weak_factory_.GetWeakPtr());
if (command_line.HasSwitch(switches::kEnableSkiaBenchmarking))
SkiaBenchmarking::Install(frame_);
for (auto& observer : observers_)
observer.DidClearWindowObject();
}
void RenderFrameImpl::DidCreateDocumentElement() {
for (auto& observer : observers_)
observer.DidCreateDocumentElement();
}
void RenderFrameImpl::RunScriptsAtDocumentElementAvailable() {
// Wait until any RenderFrameObservers for this frame have a chance to be
// constructed.
if (!initialized_)
return;
GetContentClient()->renderer()->RunScriptsAtDocumentStart(this);
// Do not use |this|! ContentClient might have deleted them by now!
}
void RenderFrameImpl::DidReceiveTitle(const blink::WebString& title) {
// Ignore all but top level navigations.
if (!frame_->Parent()) {
base::trace_event::TraceLog::GetInstance()->UpdateProcessLabel(
routing_id_, title.Utf8());
} else {
// Set process title for sub-frames in traces.
GURL loading_url = GetLoadingUrl();
if (!loading_url.host().empty() &&
loading_url.scheme() != url::kFileScheme) {
std::string subframe_title = "Subframe: " + loading_url.scheme() +
url::kStandardSchemeSeparator +
loading_url.host();
base::trace_event::TraceLog::GetInstance()->UpdateProcessLabel(
routing_id_, subframe_title);
}
}
// Also check whether we have new encoding name.
UpdateEncoding(frame_, frame_->View()->PageEncoding().Utf8());
}
void RenderFrameImpl::DidFinishDocumentLoad() {
TRACE_EVENT1("navigation,benchmark,rail",
"RenderFrameImpl::didFinishDocumentLoad", "id", routing_id_);
for (auto& observer : observers_)
observer.DidFinishDocumentLoad();
// Check whether we have new encoding name.
UpdateEncoding(frame_, frame_->View()->PageEncoding().Utf8());
}
void RenderFrameImpl::RunScriptsAtDocumentReady() {
DCHECK(initialized_);
GetContentClient()->renderer()->RunScriptsAtDocumentEnd(this);
}
void RenderFrameImpl::RunScriptsAtDocumentIdle() {
GetContentClient()->renderer()->RunScriptsAtDocumentIdle(this);
// ContentClient might have deleted |this| by now!
}
void RenderFrameImpl::DidHandleOnloadEvents() {
for (auto& observer : observers_)
observer.DidHandleOnloadEvents();
}
void RenderFrameImpl::DidFinishLoad() {
TRACE_EVENT1("navigation,benchmark,rail", "RenderFrameImpl::didFinishLoad",
"id", routing_id_);
if (!frame_->Parent()) {
TRACE_EVENT_INSTANT0("WebCore,benchmark,rail", "LoadFinished",
TRACE_EVENT_SCOPE_PROCESS);
}
for (auto& observer : observers_)
observer.DidFinishLoad();
if (!RenderThreadImpl::current())
return;
RenderThreadImpl::RendererMemoryMetrics memory_metrics;
if (!RenderThreadImpl::current()->GetRendererMemoryMetrics(&memory_metrics))
return;
RecordSuffixedRendererMemoryMetrics(memory_metrics, ".DidFinishLoad");
if (!IsMainFrame())
return;
RecordSuffixedRendererMemoryMetrics(memory_metrics,
".MainFrameDidFinishLoad");
}
void RenderFrameImpl::DidFinishSameDocumentNavigation(
blink::WebHistoryCommitType commit_type,
bool is_synchronously_committed,
bool is_history_api_navigation,
bool is_client_redirect) {
TRACE_EVENT1("navigation,rail",
"RenderFrameImpl::didFinishSameDocumentNavigation", "id",
routing_id_);
InternalDocumentStateData* data =
InternalDocumentStateData::FromDocumentLoader(
frame_->GetDocumentLoader());
if (is_synchronously_committed)
data->set_navigation_state(NavigationState::CreateForSynchronousCommit());
data->navigation_state()->set_was_within_same_document(true);
ui::PageTransition transition =
GetTransitionType(frame_->GetDocumentLoader(), IsMainFrame());
auto same_document_params =
mojom::DidCommitSameDocumentNavigationParams::New();
same_document_params->is_history_api_navigation = is_history_api_navigation;
same_document_params->is_client_redirect = is_client_redirect;
DidCommitNavigationInternal(
commit_type, transition,
blink::ParsedPermissionsPolicy(), // permissions_policy_header
blink::DocumentPolicyFeatureState(), // document_policy_header
nullptr, // interface_params
std::move(same_document_params),
absl::nullopt // embedding_token
);
// If we end up reusing this WebRequest (for example, due to a #ref click),
// we don't want the transition type to persist. Just clear it.
data->navigation_state()->set_transition_type(ui::PAGE_TRANSITION_LINK);
for (auto& observer : observers_)
observer.DidFinishSameDocumentNavigation();
}
void RenderFrameImpl::WillFreezePage() {
// Make sure browser has the latest info before the page is frozen. If the
// page goes into the back-forward cache it could be evicted and some of the
// updates lost.
SendUpdateState();
}
void RenderFrameImpl::DidOpenDocumentInputStream(const blink::WebURL& url) {
GetFrameHost()->DidOpenDocumentInputStream(url);
}
void RenderFrameImpl::DidSetPageLifecycleState() {
for (auto& observer : observers_)
observer.DidSetPageLifecycleState();
}
void RenderFrameImpl::DidUpdateCurrentHistoryItem() {
StartDelayedSyncTimer();
}
void RenderFrameImpl::StartDelayedSyncTimer() {
base::TimeDelta delay;
if (send_content_state_immediately_) {
SendUpdateState();
return;
} else if (GetWebView()->GetVisibilityState() !=
PageVisibilityState::kVisible)
delay = kDelaySecondsForContentStateSyncHidden;
else
delay = kDelaySecondsForContentStateSync;
if (delayed_state_sync_timer_.IsRunning()) {
// The timer is already running. If the delay of the timer matches the
// amount we want to delay by, then return. Otherwise stop the timer so that
// it gets started with the right delay.
if (delayed_state_sync_timer_.GetCurrentDelay() == delay)
return;
delayed_state_sync_timer_.Stop();
}
delayed_state_sync_timer_.Start(FROM_HERE, delay, this,
&RenderFrameImpl::SendUpdateState);
}
bool RenderFrameImpl::SwapOutAndDeleteThis(
int proxy_routing_id,
bool is_loading,
blink::mojom::FrameReplicationStatePtr replicated_frame_state,
const blink::RemoteFrameToken& proxy_frame_token,
mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces) {
TRACE_EVENT1("navigation,rail", "RenderFrameImpl::SwapOutAndDeleteThis", "id",
routing_id_);
DCHECK(!base::RunLoop::IsNestedOnCurrentThread());
// There should always be a proxy to replace this RenderFrame. Create it now
// so its routing id is registered for receiving IPC messages.
CHECK_NE(proxy_routing_id, MSG_ROUTING_NONE);
RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyToReplaceFrame(
agent_scheduling_group_, this, proxy_routing_id,
frame_->GetTreeScopeType(), proxy_frame_token);
RenderViewImpl* render_view = render_view_;
bool is_main_frame = is_main_frame_;
// The swap call deletes this RenderFrame via FrameDetached. Do not access
// any members after this call.
//
// TODO(creis): WebFrame::swap() can return false. Most of those cases
// should be due to the frame being detached during unload (in which case
// the necessary cleanup has happened anyway), but it might be possible for
// it to return false without detaching.
//
// This executes the unload handlers on this frame and its local descendants.
bool success = frame_->Swap(proxy->web_frame());
// WARNING: Do not access 'this' past this point!
if (is_main_frame) {
// Main frames should always swap successfully because there is no parent
// frame to cause them to become detached.
DCHECK(success);
// The RenderFrameProxy being swapped in here has now been attached to the
// Page as its main frame and properly initialized by the WebFrame::Swap()
// call, so we can call WebView's DidAttachRemoteMainFrame().
render_view->GetWebView()->DidAttachRemoteMainFrame(
std::move(remote_main_frame_interfaces->main_frame_host),
std::move(remote_main_frame_interfaces->main_frame));
}
if (!success) {
// The swap can fail when the frame is detached during swap (this can
// happen while running the unload handlers). When that happens, delete
// the proxy.
proxy->FrameDetached(blink::WebRemoteFrameClient::DetachType::kSwap);
return false;
}
if (is_loading)
proxy->DidStartLoading();
// Initialize the WebRemoteFrame with the replication state passed by the
// process that is now rendering the frame.
proxy->SetReplicatedState(std::move(replicated_frame_state));
return true;
}
base::UnguessableToken RenderFrameImpl::GetDevToolsFrameToken() {
return devtools_frame_token_;
}
void RenderFrameImpl::AbortClientNavigation() {
CHECK(in_frame_tree_);
browser_side_navigation_pending_ = false;
if (mhtml_body_loader_client_) {
mhtml_body_loader_client_->Detach();
mhtml_body_loader_client_.reset();
}
NotifyObserversOfFailedProvisionalLoad();
// See comment in header for more information of how navigation cleanup works.
navigation_client_impl_.reset();
}
void RenderFrameImpl::DidChangeSelection(bool is_empty_selection,
blink::SyncCondition force_sync) {
if (!GetLocalRootWebFrameWidget()->HandlingInputEvent() &&
!GetLocalRootWebFrameWidget()->HandlingSelectRange())
return;
if (is_empty_selection)
selection_text_.clear();
// UpdateTextInputState should be called before SyncSelectionIfRequired.
// UpdateTextInputState may send TextInputStateChanged to notify the focus
// was changed, and SyncSelectionIfRequired may send SelectionChanged
// to notify the selection was changed. Focus change should be notified
// before selection change.
GetLocalRootWebFrameWidget()->UpdateTextInputState();
SyncSelectionIfRequired(force_sync);
}
void RenderFrameImpl::OnMainFrameIntersectionChanged(
const gfx::Rect& mainframe_intersection_rect) {
if (!mainframe_intersection_rect_ ||
mainframe_intersection_rect != mainframe_intersection_rect_) {
mainframe_intersection_rect_ = mainframe_intersection_rect;
for (auto& observer : observers_) {
observer.OnMainFrameIntersectionChanged(mainframe_intersection_rect);
}
}
}
void RenderFrameImpl::OnOverlayPopupAdDetected() {
for (auto& observer : observers_) {
observer.OnOverlayPopupAdDetected();
}
}
void RenderFrameImpl::OnLargeStickyAdDetected() {
for (auto& observer : observers_) {
observer.OnLargeStickyAdDetected();
}
}
void RenderFrameImpl::WillSendRequest(blink::WebURLRequest& request,
ForRedirect for_redirect) {
// This method is called for subresources, while transition type is
// a navigation concept. We pass ui::PAGE_TRANSITION_LINK as default one.
WillSendRequestInternal(request, /*for_main_frame=*/false,
ui::PAGE_TRANSITION_LINK, for_redirect);
#if !defined(OS_ANDROID)
for (auto& observer : observers_) {
observer.WillSendRequest(request);
}
#endif
}
void RenderFrameImpl::WillSendRequestInternal(
blink::WebURLRequest& request,
bool for_main_frame,
ui::PageTransition transition_type,
ForRedirect for_redirect) {
if (GetWebView()->GetRendererPreferences().enable_do_not_track) {
request.SetHttpHeaderField(
blink::WebString::FromUTF8(blink::kDoNotTrackHeader), "1");
}
ApplyFilePathAlias(&request);
GURL new_url;
absl::optional<url::Origin> initiator_origin =
request.RequestorOrigin().IsNull()
? absl::optional<url::Origin>()
: absl::optional<url::Origin>(request.RequestorOrigin());
GetContentClient()->renderer()->WillSendRequest(
frame_, transition_type, request.Url(), request.SiteForCookies(),
base::OptionalOrNullptr(initiator_origin), &new_url);
if (!new_url.is_empty())
request.SetUrl(WebURL(new_url));
// The request's extra data may indicate that we should set a custom user
// agent. This needs to be done here, after WebKit is through with setting the
// user agent on its own.
WebString custom_user_agent;
if (request.GetURLRequestExtraData()) {
blink::WebURLRequestExtraData* old_request_extra_data =
static_cast<blink::WebURLRequestExtraData*>(
request.GetURLRequestExtraData().get());
custom_user_agent = old_request_extra_data->custom_user_agent();
if (!custom_user_agent.IsNull()) {
if (custom_user_agent.IsEmpty())
request.ClearHttpHeaderField("User-Agent");
else
request.SetHttpHeaderField("User-Agent", custom_user_agent);
}
}
WebDocument frame_document = frame_->GetDocument();
if (!request.GetURLRequestExtraData())
request.SetURLRequestExtraData(
base::MakeRefCounted<blink::WebURLRequestExtraData>());
auto* url_request_extra_data = static_cast<blink::WebURLRequestExtraData*>(
request.GetURLRequestExtraData().get());
url_request_extra_data->set_custom_user_agent(custom_user_agent);
url_request_extra_data->set_is_main_frame(IsMainFrame());
url_request_extra_data->set_transition_type(transition_type);
bool is_for_no_state_prefetch =
GetContentClient()->renderer()->IsPrefetchOnly(this);
url_request_extra_data->set_is_for_no_state_prefetch(
is_for_no_state_prefetch);
url_request_extra_data->set_frame_request_blocker(frame_request_blocker_);
url_request_extra_data->set_allow_cross_origin_auth_prompt(
GetWebView()->GetRendererPreferences().allow_cross_origin_auth_prompt);
url_request_extra_data->set_top_frame_origin(GetSecurityOriginOfTopFrame());
request.SetDownloadToNetworkCacheOnly(is_for_no_state_prefetch &&
!for_main_frame);
// The RenderThreadImpl or its URLLoaderThrottleProvider member may not be
// valid in some tests.
RenderThreadImpl* render_thread = RenderThreadImpl::current();
if (!for_redirect && render_thread &&
render_thread->url_loader_throttle_provider()) {
url_request_extra_data->set_url_loader_throttles(
render_thread->url_loader_throttle_provider()->CreateThrottles(
routing_id_, request));
}
request.SetHasUserGesture(frame_->HasTransientUserActivation());
if (!GetWebView()->GetRendererPreferences().enable_referrers) {
request.SetReferrerString(WebString());
request.SetReferrerPolicy(network::mojom::ReferrerPolicy::kNever);
}
}
void RenderFrameImpl::DidLoadResourceFromMemoryCache(
const blink::WebURLRequest& request,
const blink::WebURLResponse& response) {
for (auto& observer : observers_) {
observer.DidLoadResourceFromMemoryCache(
request.Url(), response.RequestId(), response.EncodedBodyLength(),
response.MimeType().Utf8(), response.FromArchive());
}
}
void RenderFrameImpl::DidStartResponse(
const GURL& response_url,
int request_id,
network::mojom::URLResponseHeadPtr response_head,
network::mojom::RequestDestination request_destination,
blink::PreviewsState previews_state) {
for (auto& observer : observers_) {
observer.DidStartResponse(response_url, request_id, *response_head,
request_destination, previews_state);
}
}
void RenderFrameImpl::DidCompleteResponse(
int request_id,
const network::URLLoaderCompletionStatus& status) {
for (auto& observer : observers_)
observer.DidCompleteResponse(request_id, status);
}
void RenderFrameImpl::DidCancelResponse(int request_id) {
for (auto& observer : observers_)
observer.DidCancelResponse(request_id);
}
void RenderFrameImpl::DidReceiveTransferSizeUpdate(int resource_id,
int received_data_length) {
for (auto& observer : observers_) {
observer.DidReceiveTransferSizeUpdate(resource_id, received_data_length);
}
}
void RenderFrameImpl::DidChangePerformanceTiming() {
for (auto& observer : observers_)
observer.DidChangePerformanceTiming();
}
void RenderFrameImpl::DidObserveInputDelay(base::TimeDelta input_delay) {
for (auto& observer : observers_)
observer.DidObserveInputDelay(input_delay);
}
void RenderFrameImpl::DidChangeCpuTiming(base::TimeDelta time) {
for (auto& observer : observers_)
observer.DidChangeCpuTiming(time);
}
void RenderFrameImpl::DidObserveLoadingBehavior(
blink::LoadingBehaviorFlag behavior) {
for (auto& observer : observers_)
observer.DidObserveLoadingBehavior(behavior);
}
void RenderFrameImpl::DidObserveNewFeatureUsage(
const blink::UseCounterFeature& feature) {
for (auto& observer : observers_)
observer.DidObserveNewFeatureUsage(feature);
}
void RenderFrameImpl::DidObserveLayoutShift(double score,
bool after_input_or_scroll) {
for (auto& observer : observers_)
observer.DidObserveLayoutShift(score, after_input_or_scroll);
}
void RenderFrameImpl::DidObserveLayoutNg(uint32_t all_block_count,
uint32_t ng_block_count,
uint32_t all_call_count,
uint32_t ng_call_count,
uint32_t flexbox_ng_block_count,
uint32_t grid_ng_block_count) {
for (auto& observer : observers_)
observer.DidObserveLayoutNg(all_block_count, ng_block_count, all_call_count,
ng_call_count, flexbox_ng_block_count,
grid_ng_block_count);
}
void RenderFrameImpl::DidObserveLazyLoadBehavior(
WebLocalFrameClient::LazyLoadBehavior lazy_load_behavior) {
for (auto& observer : observers_)
observer.DidObserveLazyLoadBehavior(lazy_load_behavior);
}
void RenderFrameImpl::DidCreateScriptContext(v8::Local<v8::Context> context,
int world_id) {
v8::MicrotasksScope microtasks(blink::MainThreadIsolate(),
v8::MicrotasksScope::kDoNotRunMicrotasks);
if (((enabled_bindings_ & BINDINGS_POLICY_MOJO_WEB_UI) ||
enable_mojo_js_bindings_) &&
IsMainFrame() && world_id == ISOLATED_WORLD_ID_GLOBAL) {
// We only allow these bindings to be installed when creating the main
// world context of the main frame.
blink::WebV8Features::EnableMojoJS(context, true);
}
for (auto& observer : observers_)
observer.DidCreateScriptContext(context, world_id);
}
void RenderFrameImpl::WillReleaseScriptContext(v8::Local<v8::Context> context,
int world_id) {
for (auto& observer : observers_)
observer.WillReleaseScriptContext(context, world_id);
}
void RenderFrameImpl::DidChangeScrollOffset() {
StartDelayedSyncTimer();
for (auto& observer : observers_)
observer.DidChangeScrollOffset();
}
void RenderFrameImpl::PreloadSubresourceOptimizationsForOrigins(
const std::vector<blink::WebSecurityOrigin>& origins) {
for (auto& observer : observers_)
observer.PreloadSubresourceOptimizationsForOrigins(origins);
}
blink::WebMediaStreamDeviceObserver*
RenderFrameImpl::MediaStreamDeviceObserver() {
if (!web_media_stream_device_observer_)
InitializeMediaStreamDeviceObserver();
return web_media_stream_device_observer_.get();
}
bool RenderFrameImpl::AllowRTCLegacyTLSProtocols() {
return GetRendererPreferences().webrtc_allow_legacy_tls_protocols;
}
blink::WebEncryptedMediaClient* RenderFrameImpl::EncryptedMediaClient() {
return media_factory_.EncryptedMediaClient();
}
blink::WebString RenderFrameImpl::UserAgentOverride() {
if (ShouldUseUserAgentOverride()) {
return WebString::FromUTF8(GetWebView()
->GetRendererPreferences()
.user_agent_override.ua_string_override);
}
#if BUILDFLAG(IS_CHROMEOS_LACROS)
// TODO(https://crbug.com/1114866): Implement proper L3 CDM support for
// Lacros. This is scheduled for Q2 2021. After that we can remove this hack.
WebSecurityOrigin frame_origin = frame_->GetDocument().GetSecurityOrigin();
if (frame_origin.Host().Utf8() == "www.netflix.com") {
WebString user_agent = RenderThreadImpl::current()->GetUserAgent();
std::string user_agent_utf8 =
user_agent.Utf8(WebString::UTF8ConversionMode::kStrict);
base::ReplaceSubstringsAfterOffset(&user_agent_utf8, /*start_offset=*/0,
" CrOS ", " Linux ");
return WebString::FromUTF8(user_agent_utf8);
}
#endif
return blink::WebString();
}
absl::optional<blink::UserAgentMetadata>
RenderFrameImpl::UserAgentMetadataOverride() {
if (ShouldUseUserAgentOverride()) {
return GetWebView()
->GetRendererPreferences()
.user_agent_override.ua_metadata_override;
}
return absl::nullopt;
}
bool RenderFrameImpl::ShouldUseUserAgentOverride() const {
auto* web_view = GetWebView();
// TODO(nasko): When the top-level frame is remote, there is no
// WebDocumentLoader associated with it, so the checks below are not valid.
// Temporarily return early and fix properly as part of
// https://crbug.com/426555.
if (web_view->MainFrame()->IsWebRemoteFrame())
return false;
const WebLocalFrame* main_frame = web_view->MainFrame()->ToWebLocalFrame();
WebDocumentLoader* document_loader = main_frame->GetDocumentLoader();
InternalDocumentStateData* internal_data =
document_loader
? InternalDocumentStateData::FromDocumentLoader(document_loader)
: nullptr;
return internal_data && internal_data->is_overriding_user_agent();
}
blink::WebString RenderFrameImpl::DoNotTrackValue() {
if (GetWebView()->GetRendererPreferences().enable_do_not_track)
return WebString::FromUTF8("1");
return WebString();
}
blink::mojom::RendererAudioInputStreamFactory*
RenderFrameImpl::GetAudioInputStreamFactory() {
if (!audio_input_stream_factory_)
GetBrowserInterfaceBroker()->GetInterface(
audio_input_stream_factory_.BindNewPipeAndPassReceiver(
agent_scheduling_group_.agent_group_scheduler()
.DefaultTaskRunner()));
return audio_input_stream_factory_.get();
}
bool RenderFrameImpl::AllowContentInitiatedDataUrlNavigations(
const blink::WebURL& url) {
// Error pages can navigate to data URLs.
return url.GetString() == kUnreachableWebDataURL;
}
void RenderFrameImpl::PostAccessibilityEvent(const ui::AXEvent& event) {
if (!IsAccessibilityEnabled())
return;
render_accessibility_manager_->GetRenderAccessibilityImpl()
->HandleWebAccessibilityEvent(event);
}
void RenderFrameImpl::MarkWebAXObjectDirty(
const blink::WebAXObject& obj,
bool subtree,
ax::mojom::Action event_from_action) {
if (!IsAccessibilityEnabled())
return;
render_accessibility_manager_->GetRenderAccessibilityImpl()
->MarkWebAXObjectDirty(obj, subtree, event_from_action);
}
void RenderFrameImpl::AddObserver(RenderFrameObserver* observer) {
observers_.AddObserver(observer);
}
void RenderFrameImpl::RemoveObserver(RenderFrameObserver* observer) {
observer->RenderFrameGone();
observers_.RemoveObserver(observer);
}
void RenderFrameImpl::OnDroppedNavigation() {
browser_side_navigation_pending_ = false;
frame_->DidDropNavigation();
}
void RenderFrameImpl::WasHidden() {
frame_->WasHidden();
for (auto& observer : observers_)
observer.WasHidden();
#if BUILDFLAG(ENABLE_PLUGINS)
for (auto* plugin : active_pepper_instances_)
plugin->PageVisibilityChanged(false);
#endif // ENABLE_PLUGINS
}
void RenderFrameImpl::WasShown() {
frame_->WasShown();
for (auto& observer : observers_)
observer.WasShown();
#if BUILDFLAG(ENABLE_PLUGINS)
for (auto* plugin : active_pepper_instances_)
plugin->PageVisibilityChanged(true);
#endif // ENABLE_PLUGINS
}
bool RenderFrameImpl::IsMainFrame() {
return is_main_frame_;
}
bool RenderFrameImpl::IsHidden() {
return GetLocalRootWebFrameWidget()->IsHidden();
}
bool RenderFrameImpl::IsLocalRoot() const {
return !(frame_->Parent() && frame_->Parent()->IsWebLocalFrame());
}
const RenderFrameImpl* RenderFrameImpl::GetLocalRoot() const {
return IsLocalRoot() ? this
: RenderFrameImpl::FromWebFrame(frame_->LocalRoot());
}
mojom::DidCommitProvisionalLoadParamsPtr
RenderFrameImpl::MakeDidCommitProvisionalLoadParams(
blink::WebHistoryCommitType commit_type,
ui::PageTransition transition,
const blink::ParsedPermissionsPolicy& permissions_policy_header,
const blink::DocumentPolicyFeatureState& document_policy_header,
const absl::optional<base::UnguessableToken>& embedding_token) {
WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
const WebURLResponse& response = document_loader->GetResponse();
InternalDocumentStateData* internal_data =
InternalDocumentStateData::FromDocumentLoader(
frame_->GetDocumentLoader());
NavigationState* navigation_state = internal_data->navigation_state();
auto params = mojom::DidCommitProvisionalLoadParams::New();
params->http_status_code = response.HttpStatusCode();
params->url_is_unreachable = document_loader->HasUnreachableURL();
params->method = "GET";
params->intended_as_new_entry =
navigation_state->commit_params().intended_as_new_entry;
params->should_replace_current_entry =
document_loader->ReplacesCurrentHistoryItem();
params->post_id = -1;
params->embedding_token = embedding_token;
// Pass the navigation token back to the browser process, or generate a new
// one if this navigation is committing without the browser process asking for
// it.
// TODO(clamy): We should add checks on navigations that commit without having
// been asked to commit by the browser process.
params->navigation_token = navigation_state->commit_params().navigation_token;
if (params->navigation_token.is_empty())
params->navigation_token = base::UnguessableToken::Create();
// "Standard" commits from Blink create new NavigationEntries. We also treat
// main frame "inert" commits as creating new NavigationEntries if they
// replace the current entry on a cross-document navigation (e.g., client
// redirects, location.replace, navigation to same URL), since this will
// replace all the subframes and could go cross-origin. We don't want to rely
// on updating the existing NavigationEntry in this case, since it could leave
// stale state around.
params->did_create_new_entry =
(commit_type == blink::kWebStandardCommit) ||
(commit_type == blink::kWebHistoryInertCommit && !frame_->Parent() &&
params->should_replace_current_entry &&
!navigation_state->WasWithinSameDocument());
WebDocument frame_document = frame_->GetDocument();
// Set the origin of the frame. This will be replicated to the corresponding
// RenderFrameProxies in other processes.
WebSecurityOrigin frame_origin = frame_document.GetSecurityOrigin();
params->origin = frame_origin;
params->permissions_policy_header = permissions_policy_header;
params->document_policy_header = document_policy_header;
params->insecure_request_policy = frame_->GetInsecureRequestPolicy();
params->insecure_navigations_set =
frame_->GetInsecureRequestToUpgrade().ReleaseVector();
params->has_potentially_trustworthy_unique_origin =
frame_origin.IsOpaque() && frame_origin.IsPotentiallyTrustworthy();
// Set the URL to be displayed in the browser UI to the user. Note this might
// be different than the URL actually used in the DocumentLoader (see comments
// in GetLoadingUrl() and MaybeGetOverriddenURL()). This might not be the URL
// actually shown to the user as well, since the browser has additional logic
// for virtual URLs (e.g. the "history URL" is shown for loadDataWithBaseURL
// instead of this URL).
params->url = GetLoadingUrl();
// TODO(https://crbug.com/1158101): Reconsider how we calculate
// should_update_history.
params->should_update_history =
!document_loader->HasUnreachableURL() && response.HttpStatusCode() != 404;
params->gesture = document_loader->LastNavigationHadTransientUserActivation()
? NavigationGestureUser
: NavigationGestureAuto;
// Make navigation state a part of the DidCommitProvisionalLoad message so
// that committed entry has it at all times. Send a single HistoryItem for
// this frame, rather than the whole tree. It will be stored in the
// corresponding FrameNavigationEntry.
const WebHistoryItem& item = GetWebFrame()->GetCurrentHistoryItem();
params->page_state = GetWebFrame()->CurrentHistoryItemToPageState();
params->method = document_loader->HttpMethod().Latin1();
if (params->method == "POST")
params->post_id = ExtractPostId(item);
params->item_sequence_number = item.ItemSequenceNumber();
params->document_sequence_number = item.DocumentSequenceNumber();
params->app_history_key = item.GetAppHistoryKey().Utf8();
// Note that the value of `referrer` will be overwritten in the browser with a
// browser-calculated value, except for renderer-initated same-document
// navigations and the synchronous about:blank commit (because the browser
// doesn't know anything about those navigations).
// TODO(https://crbug.com/1131832): Remove `referrer` from
// DidCommitProvisionalLoadParams.
params->referrer = blink::mojom::Referrer::New(
blink::WebStringToGURL(document_loader->Referrer()),
document_loader->GetReferrerPolicy());
if (!frame_->Parent()) {
// Top-level navigation.
// Update contents MIME type for main frame.
params->contents_mime_type =
document_loader->GetResponse().MimeType().Utf8();
params->transition = transition;
DCHECK(ui::PageTransitionIsMainFrame(params->transition));
// If the page contained a client redirect (meta refresh, document.loc...),
// set the transition appropriately.
if (document_loader->IsClientRedirect()) {
params->transition = ui::PageTransitionFromInt(
params->transition | ui::PAGE_TRANSITION_CLIENT_REDIRECT);
}
// Send the user agent override back.
params->is_overriding_user_agent =
internal_data->is_overriding_user_agent();
params->history_list_was_cleared =
navigation_state->commit_params().should_clear_history_list;
} else {
// Subframe navigation: the type depends on whether this navigation
// generated a new session history entry. When they do generate a session
// history entry, it means the user initiated the navigation and we should
// mark it as such.
if (commit_type == blink::kWebStandardCommit)
params->transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
else
params->transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
DCHECK(!navigation_state->commit_params().should_clear_history_list);
params->history_list_was_cleared = false;
}
bool requires_universal_access = false;
const bool file_scheme_with_universal_access =
params->origin.scheme() == url::kFileScheme &&
GetBlinkPreferences().allow_universal_access_from_file_urls;
// Standard URLs must match the reported origin, when it is not unique.
// This check is very similar to RenderFrameHostImpl::CanCommitOrigin, but
// adapted to the renderer process side.
if (!params->origin.opaque() && params->url.IsStandard() &&
GetBlinkPreferences().web_security_enabled) {
if (!params->origin.IsSameOriginWith(url::Origin::Create(params->url))) {
// Exclude file: URLs when settings allow them access any origin.
if (!file_scheme_with_universal_access) {
SCOPED_CRASH_KEY_STRING256("MakeDCPLParams", "mismatched_url",
params->url.possibly_invalid_spec());
SCOPED_CRASH_KEY_STRING256("MakeDCPLParams", "mismatched_origin",
params->origin.GetDebugString());
CHECK(false) << " url:" << params->url << " origin:" << params->origin;
} else {
requires_universal_access = true;
}
}
if (file_scheme_with_universal_access) {
base::UmaHistogramBoolean(
"Android.WebView.UniversalAccess.OriginUrlMismatchInRenderFrame",
requires_universal_access);
}
}
params->request_id = internal_data->request_id();
params->unload_start = GetWebFrame()->Performance().UnloadStart();
params->unload_end = GetWebFrame()->Performance().UnloadEnd();
params->commit_navigation_end =
GetWebFrame()->Performance().CommitNavigationEnd();
return params;
}
void RenderFrameImpl::UpdateNavigationHistory(
blink::WebHistoryCommitType commit_type) {
NavigationState* navigation_state =
NavigationState::FromDocumentLoader(frame_->GetDocumentLoader());
const blink::mojom::CommitNavigationParams& commit_params =
navigation_state->commit_params();
GetWebFrame()->UpdateCurrentHistoryItem();
GetWebFrame()->SetTargetToCurrentHistoryItem(
blink::WebString::FromUTF8(unique_name_helper_.value()));
bool is_new_navigation = commit_type == blink::kWebStandardCommit;
blink::WebView* webview = GetWebView();
if (commit_params.should_clear_history_list) {
webview->SetHistoryListFromNavigation(/*history_offset*/ 0,
/*history_length*/ 1);
} else if (is_new_navigation) {
DCHECK(!navigation_state->common_params().should_replace_current_entry ||
(webview->HistoryBackListCount() +
webview->HistoryForwardListCount() + 1) > 0);
if (!navigation_state->common_params().should_replace_current_entry)
webview->IncreaseHistoryListFromNavigation();
} else if (commit_params.nav_entry_id != 0 &&
!commit_params.intended_as_new_entry) {
webview->SetHistoryListFromNavigation(
navigation_state->commit_params().pending_history_list_offset, {});
}
}
void RenderFrameImpl::NotifyObserversOfNavigationCommit(
ui::PageTransition transition) {
for (auto& observer : observers_)
observer.DidCommitProvisionalLoad(transition);
}
void RenderFrameImpl::UpdateStateForCommit(
blink::WebHistoryCommitType commit_type,
ui::PageTransition transition) {
InternalDocumentStateData* internal_data =
InternalDocumentStateData::FromDocumentLoader(
frame_->GetDocumentLoader());
NavigationState* navigation_state = internal_data->navigation_state();
// We need to update the last committed session history entry with state for
// the previous page. Do this before updating the current history item.
SendUpdateState();
UpdateNavigationHistory(commit_type);
if (internal_data->must_reset_scroll_and_scale_state()) {
GetWebView()->ResetScrollAndScaleState();
internal_data->set_must_reset_scroll_and_scale_state(false);
}
if (!frame_->Parent()) { // Only for top frames.
RenderThreadImpl* render_thread_impl = RenderThreadImpl::current();
if (render_thread_impl) { // Can be NULL in tests.
render_thread_impl->histogram_customizer()->RenderViewNavigatedToHost(
GetLoadingUrl().host(), blink::WebView::GetWebViewCount());
}
}
if (IsLocalRoot()) {
// This goes to WebViewImpl and sets the zoom factor which will be
// propagated down to this RenderFrameImpl's LocalFrame in blink.
// Non-local-roots are able to grab the value off their parents but local
// roots can not and this is a huge action-at-a-distance to make up for that
// flaw in how LocalFrame determines the zoom factor.
// TODO(danakj): This should not be needed if the zoom factor/device scale
// factor did not need to be propagated to each frame. Since they are a
// global that should be okay?? The test that fails without this, for
// child frames, is in content_browsertests:
// SitePerProcessHighDPIBrowserTest.
// SubframeLoadsWithCorrectDeviceScaleFactor
// And when UseZoomForDSF is disabled, in content_browsertests:
// IFrameZoomBrowserTest.SubframesDontZoomIndependently (and the whole
// suite).
PropagatePageZoomToNewlyAttachedFrame(
GetWebView(),
GetLocalRootWebFrameWidget()->GetScreenInfo().device_scale_factor);
}
// If we are a top frame navigation to another document we should clear any
// existing autoplay flags on the Page. This is because flags are stored at
// the page level so subframes would only add to them.
if (!frame_->Parent() && !navigation_state->WasWithinSameDocument()) {
GetWebView()->ClearAutoplayFlags();
}
// Set the correct autoplay flags on the Page and wipe the cached origin so
// this will not be used incorrectly.
if (url::Origin(frame_->GetSecurityOrigin()) == autoplay_flags_.first) {
GetWebView()->AddAutoplayFlags(autoplay_flags_.second);
autoplay_flags_.first = url::Origin();
}
}
void RenderFrameImpl::DidCommitNavigationInternal(
blink::WebHistoryCommitType commit_type,
ui::PageTransition transition,
const blink::ParsedPermissionsPolicy& permissions_policy_header,
const blink::DocumentPolicyFeatureState& document_policy_header,
mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params,
mojom::DidCommitSameDocumentNavigationParamsPtr same_document_params,
const absl::optional<base::UnguessableToken>& embedding_token) {
DCHECK(!(same_document_params && interface_params));
UpdateStateForCommit(commit_type, transition);
if (render_view_->renderer_wide_named_frame_lookup())
GetWebFrame()->SetAllowsCrossBrowsingInstanceFrameLookup();
// This invocation must precede any calls to allowScripts(), allowImages(),
// or allowPlugins() for the new page. This ensures that when these functions
// call chrome::ContentSettingsManager::OnContentBlocked, those calls arrive
// after the browser process has already been informed of the provisional
// load committing.
auto params = MakeDidCommitProvisionalLoadParams(
commit_type, transition, permissions_policy_header,
document_policy_header, embedding_token);
if (same_document_params) {
GetFrameHost()->DidCommitSameDocumentNavigation(
std::move(params), std::move(same_document_params));
} else {
NavigationState* navigation_state =
NavigationState::FromDocumentLoader(frame_->GetDocumentLoader());
if (navigation_state->has_navigation_client()) {
navigation_state->RunCommitNavigationCallback(
std::move(params), std::move(interface_params));
} else {
GetFrameHost()->DidCommitProvisionalLoad(std::move(params),
std::move(interface_params));
}
}
// Ensure we will propagate frame intersections when the main frame commits
// even if the intersection does not change across navigations.
if (IsMainFrame()) {
mainframe_intersection_rect_.reset();
}
}
void RenderFrameImpl::PrepareFrameForCommit(
const GURL& url,
const blink::mojom::CommitNavigationParams& commit_params) {
browser_side_navigation_pending_ = false;
GetContentClient()->SetActiveURL(
url, frame_->Top()->GetSecurityOrigin().ToString().Utf8());
GetWebView()->SetHistoryListFromNavigation(
commit_params.current_history_list_offset,
commit_params.current_history_list_length);
}
blink::mojom::CommitResult RenderFrameImpl::PrepareForHistoryNavigationCommit(
const blink::mojom::CommonNavigationParams& common_params,
const blink::mojom::CommitNavigationParams& commit_params,
WebHistoryItem* item_for_history_navigation,
blink::WebFrameLoadType* load_type) {
blink::mojom::NavigationType navigation_type = common_params.navigation_type;
DCHECK(navigation_type ==
blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT ||
navigation_type ==
blink::mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT ||
navigation_type == blink::mojom::NavigationType::RESTORE ||
navigation_type == blink::mojom::NavigationType::RESTORE_WITH_POST);
std::unique_ptr<blink::WebHistoryEntry> entry = PageStateToHistoryEntry(
blink::PageState::CreateFromEncodedData(commit_params.page_state));
if (!entry)
return blink::mojom::CommitResult::Aborted;
// The browser process sends a single WebHistoryItem for this frame.
// TODO(creis): Change PageState to FrameState. In the meantime, we
// store the relevant frame's WebHistoryItem in the root of the
// PageState.
*item_for_history_navigation = entry->root();
*load_type = blink::WebFrameLoadType::kBackForward;
// Keep track of which subframes the browser process has history items
// for during a history navigation.
history_subframe_unique_names_ = commit_params.subframe_unique_names;
if (navigation_type == blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT) {
// If this is marked as a same document load but we haven't committed
// anything, we can't proceed with the load. The browser shouldn't let this
// happen.
CHECK(!GetWebFrame()->GetCurrentHistoryItem().IsNull());
// Additionally, if the current history item's document sequence number
// doesn't match the one sent from the browser, it is possible that this
// renderer has committed a different document. In such case, the navigation
// cannot be loaded as a same-document navigation. The browser shouldn't let
// this happen.
// TODO(crbug.com/1188513): A same document history navigation was performed
// but the renderer thinks there's a different document loaded. Where did
// this bad state of a different document + same-document navigation come
// from? Figure it out, make this a CHECK again, and drop the Restart.
DCHECK_EQ(GetWebFrame()->GetCurrentHistoryItem().DocumentSequenceNumber(),
item_for_history_navigation->DocumentSequenceNumber());
if (GetWebFrame()->GetCurrentHistoryItem().DocumentSequenceNumber() !=
item_for_history_navigation->DocumentSequenceNumber()) {
SCOPED_CRASH_KEY_NUMBER(
"history_bad_seq", "browser_doc_seq_num",
item_for_history_navigation->DocumentSequenceNumber());
SCOPED_CRASH_KEY_NUMBER(
"history_bad_seq", "renderer_doc_seq_num",
GetWebFrame()->GetCurrentHistoryItem().DocumentSequenceNumber());
base::debug::DumpWithoutCrashing();
return blink::mojom::CommitResult::RestartCrossDocument;
}
}
// Note: we used to check that initial history navigation in the child frame
// was not canceled by a client redirect before committing it. However,
// we now destroy the NavigationClient for initial history navigation, and
// commit does not arrive to the renderer in this case.
return blink::mojom::CommitResult::Ok;
}
bool RenderFrameImpl::SwapIn(WebFrame* previous_web_frame) {
CHECK(!in_frame_tree_);
// The unique name can still change in `WebFrame::Swap()` below (due to JS
// changing the browsing context name), but in practice, this seems to be good
// enough.
unique_name_helper_.set_propagated_name(
GetUniqueNameOfWebFrame(previous_web_frame));
// Swapping out a frame can dispatch JS event handlers, causing `this` to be
// deleted.
bool is_main_frame = is_main_frame_;
if (!previous_web_frame->Swap(frame_)) {
// Main frames should always swap successfully because there is no parent
// frame to cause them to become detached.
DCHECK(!is_main_frame);
return false;
}
// `previous_web_frame` is now detached, and should no longer be referenced.
in_frame_tree_ = true;
// If this is the main frame going from a remote frame to a local frame,
// it needs to set RenderViewImpl's pointer for the main frame to itself.
if (is_main_frame_) {
// The WebFrame being swapped in here has now been attached to the Page as
// its main frame, and the WebFrameWidget was previously initialized when
// the frame was created so we can call WebView's DidAttachLocalMainFrame().
GetWebView()->DidAttachLocalMainFrame();
}
return true;
}
void RenderFrameImpl::DidStartLoading() {
// TODO(dgozman): consider removing this callback.
TRACE_EVENT1("navigation,rail", "RenderFrameImpl::didStartLoading", "id",
routing_id_);
}
void RenderFrameImpl::DidStopLoading() {
TRACE_EVENT1("navigation,rail", "RenderFrameImpl::didStopLoading", "id",
routing_id_);
// Any subframes created after this point won't be considered part of the
// current history navigation (if this was one), so we don't need to track
// this state anymore.
history_subframe_unique_names_.clear();
GetFrameHost()->DidStopLoading();
}
void RenderFrameImpl::NotifyAccessibilityModeChange(ui::AXMode new_mode) {
for (auto& observer : observers_)
observer.AccessibilityModeChanged(new_mode);
}
void RenderFrameImpl::FocusedElementChanged(const blink::WebElement& element) {
for (auto& observer : observers_)
observer.FocusedElementChanged(element);
}
void RenderFrameImpl::BeginNavigation(
std::unique_ptr<blink::WebNavigationInfo> info) {
// A provisional frame should never make a renderer-initiated navigation: no
// JS should be running in |this|, and no other frame should have a reference
// to |this|.
CHECK(in_frame_tree_);
// This might be the first navigation in this RenderFrame.
const bool first_navigation_in_render_frame = !had_started_any_navigation_;
had_started_any_navigation_ = true;
// This method is only called for renderer initiated navigations, which
// may have originated from a link-click, script, drag-n-drop operation, etc.
// Note that we don't want to go to browser for a navigation to an empty url,
// which happens for window.open('') call. An example would be embedder
// deciding to fork the process for the empty url, or setting
// |browser_handles_all_top_level_requests| preference.
//
// Doing a browser-side navigation might later trigger unload handlers,
// e.g. when the dom window of the popup has already been touched
// synchronously in this process. We should avoid that.
//
// See the checks for empty url in the cases below.
// TODO(dgozman): if we rewrite empty url to about:blank earlier
// (we currently do that in DocumentLoader), all the empty checks can be
// removed, since they already account for an empty url.
// Blink is asking whether to navigate to a new URL.
// This is fine normally, except if we're showing UI from one security
// context and they're trying to navigate to a different context.
const GURL& url = info->url_request.Url();
TRACE_EVENT2("navigation", "RenderFrameImpl::BeginNavigation", "url",
url.possibly_invalid_spec(), "navigation_type",
static_cast<int>(info->navigation_type));
if (GetWebFrame() && GetWebFrame()->DispatchedPagehideAndStillHidden()) {
// The navigation started after the pagehide event got dispatched. This
// navigation will be ignored by the browser, and we need to track that it's
// happening. Note that this problem is not unique to BackForwardCache/
// same-site BrowsingInstance swap, as navigations started after unload in
// normal scenarios will also be ignored by the browser.
UMA_HISTOGRAM_ENUMERATION("BackForwardCache.SameSite.ActionAfterPagehide2",
blink::ActionAfterPagehide::kNavigation);
}
// When an MHTML Archive is present, it should be used to serve iframe
// content instead of doing a network request. This should never be true for
// the main frame.
bool use_archive = (info->archive_status ==
blink::WebNavigationInfo::ArchiveStatus::Present) &&
!url.SchemeIs(url::kDataScheme);
DCHECK(!(use_archive && IsMainFrame()));
#ifdef OS_ANDROID
bool render_view_was_created_by_renderer =
render_view_->was_created_by_renderer_;
// The handlenavigation API is deprecated and will be removed once
// crbug.com/325351 is resolved.
if (!url.is_empty() && !use_archive && !IsURLHandledByNetworkStack(url) &&
GetContentClient()->renderer()->HandleNavigation(
this, render_view_was_created_by_renderer, frame_, info->url_request,
info->navigation_type, info->navigation_policy,
false /* is_redirect */)) {
return;
}
#endif
// If the browser is interested, then give it a chance to look at the request.
if (IsTopLevelNavigation(frame_) &&
GetWebView()
->GetRendererPreferences()
.browser_handles_all_top_level_requests) {
OpenURL(std::move(info));
return; // Suppress the load here.
}
// Back/forward navigations in newly created subframes should be sent to the
// browser if there is a matching FrameNavigationEntry, and if it isn't just
// staying at about:blank. If this frame isn't in the map of unique names
// that have history items, or if it's staying at the initial about:blank URL,
// fall back to loading the default url. (We remove each name as we encounter
// it, because it will only be used once as the frame is created.)
// Note: Skip this logic for MHTML files (|use_archive|), which should load
// their subframes from the archive and not from history.
bool is_history_navigation_in_new_child_frame = false;
if (info->is_history_navigation_in_new_child_frame && frame_->Parent() &&
!use_archive) {
// Check whether the browser has a history item for this frame that isn't
// just staying at the initial about:blank document.
RenderFrameImpl* parent = RenderFrameImpl::FromWebFrame(frame_->Parent());
auto iter = parent->history_subframe_unique_names_.find(
unique_name_helper_.value());
if (iter != parent->history_subframe_unique_names_.end()) {
bool history_item_is_about_blank = iter->second;
is_history_navigation_in_new_child_frame =
!history_item_is_about_blank || url != url::kAboutBlankURL;
parent->history_subframe_unique_names_.erase(iter);
}
}
if (is_history_navigation_in_new_child_frame) {
// Don't do this if |info| also says it is a client redirect, in which
// case JavaScript on the page is trying to interrupt the history
// navigation.
if (info->is_client_redirect) {
// Client redirects during an initial history load should attempt to
// cancel the history navigation. They will create a provisional
// document loader, causing the history load to be ignored in
// NavigateInternal, and this IPC will try to cancel any cross-process
// history load.
is_history_navigation_in_new_child_frame = false;
GetFrameHost()->CancelInitialHistoryLoad();
}
}
// Use the frame's original request's URL rather than the document's URL for
// subsequent checks. For a popup, the document's URL may become the opener
// window's URL if the opener has called document.write().
// See http://crbug.com/93517.
GURL old_url(frame_->GetDocumentLoader()->GetUrl());
// Detect when we're crossing a permission-based boundary (e.g. into or out of
// an extension or app origin, leaving a WebUI page, etc). We only care about
// top-level navigations (not iframes). But we sometimes navigate to
// about:blank to clear a tab, and we want to still allow that.
if (!frame_->Parent() && !url.SchemeIs(url::kAboutScheme) &&
!url.is_empty()) {
// All navigations to or from WebUI URLs or within WebUI-enabled
// RenderProcesses must be handled by the browser process so that the
// correct bindings and data sources can be registered.
// All frames in a WebUI process must have the same enabled_bindings_, so
// we can do a per-frame check here rather than a process-wide check.
bool should_fork = HasWebUIScheme(url) || HasWebUIScheme(old_url) ||
(enabled_bindings_ & kWebUIBindingsPolicyMask);
if (should_fork) {
OpenURL(std::move(info));
return; // Suppress the load here.
}
}
if (info->navigation_policy == blink::kWebNavigationPolicyCurrentTab) {
// Execute the BeforeUnload event. If asked not to proceed or the frame is
// destroyed, ignore the navigation.
// Keep a WeakPtr to this RenderFrameHost to detect if executing the
// BeforeUnload event destroyed this frame.
base::WeakPtr<RenderFrameImpl> weak_self = weak_factory_.GetWeakPtr();
base::TimeTicks renderer_before_unload_start = base::TimeTicks::Now();
if (!frame_->DispatchBeforeUnloadEvent(info->navigation_type ==
blink::kWebNavigationTypeReload) ||
!weak_self) {
return;
}
base::TimeTicks renderer_before_unload_end = base::TimeTicks::Now();
if (!info->form.IsNull()) {
for (auto& observer : observers_)
observer.WillSubmitForm(info->form);
}
if (mhtml_body_loader_client_) {
mhtml_body_loader_client_->Detach();
mhtml_body_loader_client_.reset();
}
// In certain cases, Blink re-navigates to about:blank when creating a new
// browsing context (when opening a new window or creating an iframe) and
// expects the navigation to complete synchronously.
// TODO(https://crbug.com/1215096): Remove the synchronous about:blank
// navigation.
bool should_do_synchronous_about_blank_navigation =
// Mainly a proxy for checking about:blank, even though it can match
// other things like about:srcdoc (or any empty document schemes that
// are registered).
// TODO(https://crbug.com/1215096): This should be changed to only check
// for about:blank because the navigation triggered by browsing context
// creation will always navigate to about:blank.
WebDocumentLoader::WillLoadUrlAsEmpty(url) &&
// The navigation method must be "GET". This is to avoid issues like
// https://crbug.com/1210653, where a form submits to about:blank
// targeting a new window using a POST. The browser never expects this
// to happen synchronously because it only expects the synchronous
// about:blank navigation to originate from browsing context creation,
// which will always be GET requests.
info->url_request.HttpMethod().Equals("GET") &&
// If the frame has committed or even started a navigation before, this
// navigation can't possibly be triggered by browsing context creation,
// which would have triggered the navigation synchronously as the first
// navigation in this frame. Note that we check both
// HasCommittedFirstRealLoad() and `first_navigation_in_render_frame`
// here because `first_navigation_in_render_frame` only tracks the state
// in this *RenderFrame*, so it will be true even if this navigation
// happens on a frame that has existed before in another process (e.g.
// an <iframe> pointing to a.com being navigated to a cross-origin
// about:blank document that happens in a new frame). Meanwhile,
// HasCommittedFirstRealLoad() tracks the state of the frame, so it will
// be true in the aforementioned case and we would not do a synchronous
// commit here.
!frame_->HasCommittedFirstRealLoad() &&
first_navigation_in_render_frame &&
// If this is a subframe history navigation that should be sent to the
// browser, don't commit it synchronously.
!is_history_navigation_in_new_child_frame;
if (should_do_synchronous_about_blank_navigation) {
for (auto& observer : observers_)
observer.DidStartNavigation(url, info->navigation_type);
SynchronouslyCommitAboutBlankForBug778318(std::move(info));
return;
}
// Everything else is handled asynchronously by the browser process through
// BeginNavigation.
BeginNavigationInternal(
std::move(info), is_history_navigation_in_new_child_frame,
renderer_before_unload_start, renderer_before_unload_end);
return;
}
if (info->navigation_policy == blink::kWebNavigationPolicyDownload) {
mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token =
CloneBlobURLToken(info->blob_url_token);
frame_->DownloadURL(info->url_request,
network::mojom::RedirectMode::kFollow,
std::move(blob_url_token));
} else {
OpenURL(std::move(info));
}
}
void RenderFrameImpl::SynchronouslyCommitAboutBlankForBug778318(
std::unique_ptr<blink::WebNavigationInfo> info) {
CHECK_EQ(NavigationCommitState::kInitialEmptyDocument,
navigation_commit_state_);
navigation_commit_state_ = NavigationCommitState::kNone;
AssertNavigationCommits assert_navigation_commits(this);
// TODO(dgozman): should we follow the RFI::CommitNavigation path instead?
auto navigation_params = WebNavigationParams::CreateFromInfo(*info);
// We need the provider to be non-null, otherwise Blink crashes, even
// though the provider should not be used for any actual networking.
navigation_params->service_worker_network_provider =
ServiceWorkerNetworkProviderForFrame::CreateInvalidInstance();
// The synchronous about:blank commit should only happen when the frame is
// currently showing the initial empty document. For iframes, all navigations
// that happen on the initial empty document should result in replacement, we
// must have set the `frame_load_type` to kReplaceCurrentItem. For main frames
// there are still cases where we will append instead of replace, but the
// browser already expects this case.
// TODO(https://crbug.com/1215096): Ensure main frame cases always do
// replacement too.
DCHECK(IsMainFrame() || navigation_params->frame_load_type ==
WebFrameLoadType::kReplaceCurrentItem);
frame_->CommitNavigation(std::move(navigation_params), BuildDocumentState());
}
// mojom::MhtmlFileWriter implementation
// ----------------------------------------
void RenderFrameImpl::SerializeAsMHTML(mojom::SerializeAsMHTMLParamsPtr params,
SerializeAsMHTMLCallback callback) {
TRACE_EVENT0("page-serialization", "RenderFrameImpl::SerializeAsMHTML");
base::TimeTicks start_time = base::TimeTicks::Now();
// Unpack payload.
const WebString mhtml_boundary =
WebString::FromUTF8(params->mhtml_boundary_marker);
DCHECK(!mhtml_boundary.IsEmpty());
// Holds WebThreadSafeData instances for some or all of header, contents and
// footer.
std::vector<WebThreadSafeData> mhtml_contents;
std::unordered_set<std::string> serialized_resources_uri_digests;
MHTMLPartsGenerationDelegate delegate(*params,
&serialized_resources_uri_digests);
mojom::MhtmlSaveStatus save_status = mojom::MhtmlSaveStatus::kSuccess;
bool has_some_data = false;
// Generate MHTML header if needed.
if (IsMainFrame()) {
TRACE_EVENT0("page-serialization",
"RenderFrameImpl::SerializeAsMHTML header");
// The returned data can be empty if the main frame should be skipped. If
// the main frame is skipped, then the whole archive is bad.
mhtml_contents.emplace_back(WebFrameSerializer::GenerateMHTMLHeader(
mhtml_boundary, GetWebFrame(), &delegate));
has_some_data = true;
}
// Generate MHTML parts. Note that if this is not the main frame, then even
// skipping the whole parts generation step is not an error - it simply
// results in an omitted resource in the final file.
if (save_status == mojom::MhtmlSaveStatus::kSuccess) {
TRACE_EVENT0("page-serialization",
"RenderFrameImpl::SerializeAsMHTML parts serialization");
// The returned data can be empty if the frame should be skipped, but this
// is OK.
mhtml_contents.emplace_back(WebFrameSerializer::GenerateMHTMLParts(
mhtml_boundary, GetWebFrame(), &delegate));
has_some_data |= !mhtml_contents.back().IsEmpty();
}
// Note: the MHTML footer is written by the browser process, after the last
// frame is serialized by a renderer process.
// Note: we assume RenderFrameImpl::OnWriteMHTMLComplete and the rest of
// this function will be fast enough to not need to be accounted for in this
// metric.
base::TimeDelta main_thread_use_time = base::TimeTicks::Now() - start_time;
UMA_HISTOGRAM_TIMES(
"PageSerialization.MhtmlGeneration.RendererMainThreadTime.SingleFrame",
main_thread_use_time);
MHTMLHandleWriterDelegate handle_delegate(
*params,
base::BindOnce(&RenderFrameImpl::OnWriteMHTMLComplete,
weak_factory_.GetWeakPtr(), std::move(callback),
std::move(serialized_resources_uri_digests),
main_thread_use_time),
GetTaskRunner(blink::TaskType::kInternalDefault));
if (save_status == mojom::MhtmlSaveStatus::kSuccess && has_some_data) {
handle_delegate.WriteContents(mhtml_contents);
} else {
handle_delegate.Finish(save_status);
}
}
void RenderFrameImpl::OnWriteMHTMLComplete(
SerializeAsMHTMLCallback callback,
std::unordered_set<std::string> serialized_resources_uri_digests,
base::TimeDelta main_thread_use_time,
mojom::MhtmlSaveStatus save_status) {
TRACE_EVENT1("page-serialization", "RenderFrameImpl::OnWriteMHTMLComplete",
"frame save status", save_status);
DCHECK(RenderThread::IsMainThread())
<< "Must run in the main renderer thread";
// Convert the set into a vector for transport.
std::vector<std::string> digests_of_new_parts(
std::make_move_iterator(serialized_resources_uri_digests.begin()),
std::make_move_iterator(serialized_resources_uri_digests.end()));
// Notify the browser process about completion using the callback.
// Note: we assume this method is fast enough to not need to be accounted for
// in PageSerialization.MhtmlGeneration.RendererMainThreadTime.SingleFrame.
std::move(callback).Run(save_status, std::move(digests_of_new_parts),
main_thread_use_time);
}
#ifndef STATIC_ASSERT_ENUM
#define STATIC_ASSERT_ENUM(a, b) \
static_assert(static_cast<int>(a) == static_cast<int>(b), \
"mismatching enums: " #a)
#undef STATIC_ASSERT_ENUM
#endif
void RenderFrameImpl::RequestOverlayRoutingToken(
media::RoutingTokenCallback callback) {
std::move(callback).Run(GetWebFrame()->GetFrameToken().value());
}
void RenderFrameImpl::OpenURL(std::unique_ptr<blink::WebNavigationInfo> info) {
// A valid RequestorOrigin is always expected to be present.
DCHECK(!info->url_request.RequestorOrigin().IsNull());
WebNavigationPolicy policy = info->navigation_policy;
auto params = blink::mojom::OpenURLParams::New();
params->url = info->url_request.Url();
params->initiator_origin = info->url_request.RequestorOrigin();
params->post_body = blink::GetRequestBodyForWebURLRequest(info->url_request);
DCHECK_EQ(!!params->post_body, IsHttpPost(info->url_request));
params->extra_headers =
blink::GetWebURLRequestHeadersAsString(info->url_request).Latin1();
params->referrer = blink::mojom::Referrer::New(
blink::WebStringToGURL(info->url_request.ReferrerString()),
info->url_request.GetReferrerPolicy());
params->disposition = RenderViewImpl::NavigationPolicyToDisposition(policy);
params->triggering_event_info = info->triggering_event_info;
params->blob_url_token = CloneBlobURLToken(info->blob_url_token);
params->should_replace_current_entry =
info->frame_load_type == WebFrameLoadType::kReplaceCurrentItem &&
GetWebView()->HistoryBackListCount() +
GetWebView()->HistoryForwardListCount() + 1;
params->user_gesture = info->has_transient_user_activation;
params->initiator_policy_container_keep_alive_handle =
std::move(info->initiator_policy_container_keep_alive_handle);
params->initiator_frame_token = info->initiator_frame_token;
// TODO(antoniosartori): Consider plumbing in the source location also for
// navigations performed via OpenURL.
params->source_location = network::mojom::SourceLocation::New();
if (info->impression) {
params->impression =
blink::ConvertWebImpressionToImpression(*info->impression);
}
if (GetContentClient()->renderer()->AllowPopup())
params->user_gesture = true;
// A main frame navigation should already have consumed an activation in
// FrameLoader::StartNavigation.
DCHECK(!is_main_frame_ || !frame_->HasTransientUserActivation());
if (!is_main_frame_ &&
(policy == blink::kWebNavigationPolicyNewBackgroundTab ||
policy == blink::kWebNavigationPolicyNewForegroundTab ||
policy == blink::kWebNavigationPolicyNewWindow ||
policy == blink::kWebNavigationPolicyNewPopup)) {
frame_->ConsumeTransientUserActivation();
}
params->href_translate = info->href_translate.Latin1();
bool current_frame_has_download_sandbox_flag = !frame_->IsAllowedToDownload();
bool has_download_sandbox_flag =
info->initiator_frame_has_download_sandbox_flag ||
current_frame_has_download_sandbox_flag;
bool from_ad = info->initiator_frame_is_ad || frame_->IsAdSubframe();
params->download_policy.ApplyDownloadFramePolicy(
info->is_opener_navigation, info->url_request.HasUserGesture(),
info->url_request.RequestorOrigin().CanAccess(
frame_->GetSecurityOrigin()),
has_download_sandbox_flag, info->blocking_downloads_in_sandbox_enabled,
from_ad);
GetFrameHost()->OpenURL(std::move(params));
}
blink::ChildURLLoaderFactoryBundle* RenderFrameImpl::GetLoaderFactoryBundle() {
// GetLoaderFactoryBundle should not be called before `loader_factories_` have
// been set up - before a document is committed (e.g. before a navigation
// commits or the initial empty document commits) it is not possible to 1)
// trigger subresource loads, or 2) trigger propagation of the factories into
// a new frame.
DCHECK(loader_factories_);
return loader_factories_.get();
}
scoped_refptr<blink::ChildURLLoaderFactoryBundle>
RenderFrameImpl::CreateLoaderFactoryBundle(
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> info,
absl::optional<std::vector<blink::mojom::TransferrableURLLoaderPtr>>
subresource_overrides,
mojo::PendingRemote<network::mojom::URLLoaderFactory>
prefetch_loader_factory) {
DCHECK(info);
// We don't check `DCHECK(info->pending_default_factory())`, because it will
// be missing for speculative frames (and in other cases where no subresource
// loads are expected - e.g. in test frames created via RenderViewTest). See
// also the DCHECK in URLLoaderFactoryBundle::GetFactory.
auto loader_factories =
base::MakeRefCounted<blink::HostChildURLLoaderFactoryBundle>(
GetTaskRunner(blink::TaskType::kInternalLoading));
loader_factories->Update(
std::make_unique<blink::ChildPendingURLLoaderFactoryBundle>(
std::move(info)));
if (subresource_overrides) {
loader_factories->UpdateSubresourceOverrides(&*subresource_overrides);
}
if (prefetch_loader_factory) {
loader_factories->SetPrefetchLoaderFactory(
std::move(prefetch_loader_factory));
}
return loader_factories;
}
void RenderFrameImpl::UpdateEncoding(WebFrame* frame,
const std::string& encoding_name) {
// Only update main frame's encoding_name.
if (!frame->Parent())
GetFrameHost()->UpdateEncoding(encoding_name);
}
void RenderFrameImpl::SyncSelectionIfRequired(blink::SyncCondition force_sync) {
std::u16string text;
size_t offset;
gfx::Range range;
#if BUILDFLAG(ENABLE_PLUGINS)
if (focused_pepper_plugin_) {
focused_pepper_plugin_->GetSurroundingText(&text, &range);
offset = 0; // Pepper API does not support offset reporting.
// TODO(kinaba): cut as needed.
} else
#endif
{
WebRange selection =
frame_->GetInputMethodController()->GetSelectionOffsets();
if (selection.IsNull())
return;
range = gfx::Range(selection.StartOffset(), selection.EndOffset());
if (frame_->GetInputMethodController()->TextInputType() !=
blink::kWebTextInputTypeNone) {
// If current focused element is editable, we will send 100 more chars
// before and after selection. It is for input method surrounding text
// feature.
if (selection.StartOffset() > kExtraCharsBeforeAndAfterSelection)
offset = selection.StartOffset() - kExtraCharsBeforeAndAfterSelection;
else
offset = 0;
size_t length =
selection.EndOffset() - offset + kExtraCharsBeforeAndAfterSelection;
text = frame_->RangeAsText(WebRange(offset, length)).Utf16();
} else {
offset = selection.StartOffset();
text = frame_->SelectionAsText().Utf16();
// http://crbug.com/101435
// In some case, frame->selectionAsText() returned text's length is not
// equal to the length returned from frame_->GetSelectionOffsets(). So we
// have to set the range according to text.length().
range.set_end(range.start() + text.length());
}
}
// TODO(dglazkov): Investigate if and why this would be happening,
// and resolve this. We shouldn't be carrying selection text here.
// http://crbug.com/632920.
// Sometimes we get repeated didChangeSelection calls from webkit when
// the selection hasn't actually changed. We don't want to report these
// because it will cause us to continually claim the X clipboard.
if (force_sync == blink::SyncCondition::kForced ||
selection_text_offset_ != offset || selection_range_ != range ||
selection_text_ != text) {
selection_text_ = text;
selection_text_offset_ = offset;
selection_range_ = range;
SetSelectedText(text, offset, range);
}
GetLocalRootWebFrameWidget()->UpdateSelectionBounds();
}
void RenderFrameImpl::CreateAudioInputStream(
blink::CrossVariantMojoRemote<
blink::mojom::RendererAudioInputStreamFactoryClientInterfaceBase>
client,
const base::UnguessableToken& session_id,
const media::AudioParameters& params,
bool automatic_gain_control,
uint32_t shared_memory_count) {
GetAudioInputStreamFactory()->CreateStream(std::move(client), session_id,
params, automatic_gain_control,
shared_memory_count);
}
void RenderFrameImpl::AssociateInputAndOutputForAec(
const base::UnguessableToken& input_stream_id,
const std::string& output_device_id) {
GetAudioInputStreamFactory()->AssociateInputAndOutputForAec(input_stream_id,
output_device_id);
}
void RenderFrameImpl::DidChangeMobileFriendliness(
const blink::MobileFriendliness& mf) {
for (auto& observer : observers_) {
observer.OnMobileFriendlinessChanged(mf);
}
}
void RenderFrameImpl::InitializeMediaStreamDeviceObserver() {
RenderThreadImpl* render_thread = RenderThreadImpl::current();
if (!render_thread) // Will be NULL during unit tests.
return;
DCHECK(!web_media_stream_device_observer_);
web_media_stream_device_observer_ =
std::make_unique<blink::WebMediaStreamDeviceObserver>(GetWebFrame());
}
void RenderFrameImpl::BeginNavigationInternal(
std::unique_ptr<blink::WebNavigationInfo> info,
bool is_history_navigation_in_new_child_frame,
base::TimeTicks renderer_before_unload_start,
base::TimeTicks renderer_before_unload_end) {
if (!frame_->WillStartNavigation(*info))
return;
for (auto& observer : observers_)
observer.DidStartNavigation(info->url_request.Url(), info->navigation_type);
browser_side_navigation_pending_ = true;
blink::WebURLRequest& request = info->url_request;
// Set SiteForCookies.
WebDocument frame_document = frame_->GetDocument();
if (info->frame_type == blink::mojom::RequestContextFrameType::kTopLevel)
request.SetSiteForCookies(net::SiteForCookies::FromUrl(request.Url()));
else
request.SetSiteForCookies(frame_document.SiteForCookies());
ui::PageTransition transition_type = GetTransitionType(
ui::PAGE_TRANSITION_LINK,
info->frame_load_type == WebFrameLoadType::kReplaceCurrentItem,
IsMainFrame(), info->navigation_type);
if (info->is_client_redirect) {
transition_type = ui::PageTransitionFromInt(
transition_type | ui::PAGE_TRANSITION_CLIENT_REDIRECT);
}
// Note: At this stage, the goal is to apply all the modifications the
// renderer wants to make to the request, and then send it to the browser, so
// that the actual network request can be started. Ideally, all such
// modifications should take place in WillSendRequestInternal, and in the
// implementation of willSendRequest for the various InspectorAgents
// (devtools).
//
// TODO(clamy): Apply devtools override.
// TODO(clamy): Make sure that navigation requests are not modified somewhere
// else in blink.
bool for_main_frame = !frame_->Parent();
WillSendRequestInternal(request, for_main_frame, transition_type,
ForRedirect(false));
if (!info->url_request.GetURLRequestExtraData()) {
info->url_request.SetURLRequestExtraData(
base::MakeRefCounted<blink::WebURLRequestExtraData>());
}
// TODO(clamy): Same-document navigations should not be sent back to the
// browser.
// TODO(clamy): Data urls should not be sent back to the browser either.
// These values are assumed on the browser side for navigations. These checks
// ensure the renderer has the correct values.
DCHECK_EQ(network::mojom::RequestMode::kNavigate,
info->url_request.GetMode());
DCHECK_EQ(network::mojom::CredentialsMode::kInclude,
info->url_request.GetCredentialsMode());
DCHECK_EQ(network::mojom::RedirectMode::kManual,
info->url_request.GetRedirectMode());
DCHECK(frame_->Parent() ||
info->frame_type == blink::mojom::RequestContextFrameType::kTopLevel);
DCHECK(!frame_->Parent() ||
info->frame_type == blink::mojom::RequestContextFrameType::kNested);
bool is_form_submission =
info->navigation_type == blink::kWebNavigationTypeFormSubmitted ||
info->navigation_type == blink::kWebNavigationTypeFormResubmitted;
bool was_initiated_by_link_click =
info->navigation_type == blink::kWebNavigationTypeLinkClicked;
GURL searchable_form_url;
std::string searchable_form_encoding;
if (!info->form.IsNull()) {
WebSearchableFormData web_searchable_form_data(info->form);
searchable_form_url = web_searchable_form_data.Url();
searchable_form_encoding = web_searchable_form_data.Encoding().Utf8();
}
GURL client_side_redirect_url;
if (info->is_client_redirect)
client_side_redirect_url = frame_->GetDocument().Url();
mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token(
CloneBlobURLToken(info->blob_url_token));
int load_flags = info->url_request.GetLoadFlagsForWebUrlRequest();
std::unique_ptr<base::DictionaryValue> initiator;
if (!info->devtools_initiator_info.IsNull()) {
initiator = base::DictionaryValue::From(
base::JSONReader::ReadDeprecated(info->devtools_initiator_info.Utf8()));
}
absl::optional<network::ResourceRequest::WebBundleTokenParams>
web_bundle_token_params;
if (info->url_request.WebBundleToken()) {
web_bundle_token_params =
absl::make_optional(network::ResourceRequest::WebBundleTokenParams(
*info->url_request.WebBundleUrl(),
*info->url_request.WebBundleToken(),
-1 /* render_process_id, to be filled in the browser process */));
}
blink::mojom::BeginNavigationParamsPtr begin_navigation_params =
blink::mojom::BeginNavigationParams::New(
info->initiator_frame_token,
blink::GetWebURLRequestHeadersAsString(info->url_request).Latin1(),
load_flags, info->url_request.GetSkipServiceWorker(),
blink::GetRequestContextTypeForWebURLRequest(info->url_request),
blink::GetRequestDestinationForWebURLRequest(info->url_request),
blink::GetMixedContentContextTypeForWebURLRequest(info->url_request),
is_form_submission, was_initiated_by_link_click, searchable_form_url,
searchable_form_encoding, client_side_redirect_url,
initiator ? absl::make_optional<base::Value>(std::move(*initiator))
: absl::nullopt,
info->url_request.TrustTokenParams()
? info->url_request.TrustTokenParams()->Clone()
: nullptr,
info->impression
? absl::make_optional<blink::Impression>(
blink::ConvertWebImpressionToImpression(*info->impression))
: absl::nullopt,
renderer_before_unload_start, renderer_before_unload_end,
web_bundle_token_params);
mojo::PendingAssociatedRemote<mojom::NavigationClient>
navigation_client_remote;
BindNavigationClient(
navigation_client_remote.InitWithNewEndpointAndPassReceiver());
navigation_client_impl_->MarkWasInitiatedInThisFrame();
bool current_frame_has_download_sandbox_flag = !frame_->IsAllowedToDownload();
bool has_download_sandbox_flag =
info->initiator_frame_has_download_sandbox_flag ||
current_frame_has_download_sandbox_flag;
bool from_ad = info->initiator_frame_is_ad || frame_->IsAdSubframe();
mojo::PendingRemote<blink::mojom::PolicyContainerHostKeepAliveHandle>
initiator_policy_container_keep_alive_handle =
std::move(info->initiator_policy_container_keep_alive_handle);
GetFrameHost()->BeginNavigation(
MakeCommonNavigationParams(frame_->GetSecurityOrigin(), std::move(info),
load_flags, has_download_sandbox_flag, from_ad,
is_history_navigation_in_new_child_frame),
std::move(begin_navigation_params), std::move(blob_url_token),
std::move(navigation_client_remote),
std::move(initiator_policy_container_keep_alive_handle));
}
void RenderFrameImpl::DecodeDataURL(
const blink::mojom::CommonNavigationParams& common_params,
const blink::mojom::CommitNavigationParams& commit_params,
std::string* mime_type,
std::string* charset,
std::string* data,
GURL* base_url) {
// A loadData request with a specified base URL.
GURL data_url = common_params.url;
#if defined(OS_ANDROID)
if (!commit_params.data_url_as_string.empty()) {
#if DCHECK_IS_ON()
{
std::string mime_type_tmp, charset_tmp, data_tmp;
DCHECK(net::DataURL::Parse(data_url, &mime_type_tmp, &charset_tmp,
&data_tmp));
DCHECK(data_tmp.empty());
}
#endif
// If `data_url_as_string` is set, the `url` in CommonNavigationParams will
// only contain the data: URL header and won't contain any actual data (see
// NavigationControllerAndroid::LoadUrl()), so we should use
// `data_url_as_string` if possible.
data_url = GURL(commit_params.data_url_as_string);
if (!data_url.is_valid() || !data_url.SchemeIs(url::kDataScheme)) {
// If the given data URL is invalid, use the data: URL header as a
// fallback.
data_url = common_params.url;
}
}
#endif
// Parse the given data, then set the `base_url`, which is used as the
// document URL.
if (net::DataURL::Parse(data_url, mime_type, charset, data)) {
// Since the base URL will also be used as the document URL, we should not
// use an empty URL. If it's empty, use the data: URL as a fallback.
// TODO(https://crbug.com/1223403): Maybe this should consider
// `data_url_as_string` too. Otherwise, the base URL might be set to the
// data: URL header with empty data, instead of the data: URL that contains
// the actual data.
*base_url = common_params.base_url_for_data_url.is_empty()
? common_params.url
: common_params.base_url_for_data_url;
} else {
CHECK(false) << "Invalid URL passed: "
<< common_params.url.possibly_invalid_spec();
}
}
void RenderFrameImpl::SendUpdateState() {
// Since we are sending immediately we can cancel any pending delayed sync
// timer.
delayed_state_sync_timer_.Stop();
if (GetWebFrame()->GetCurrentHistoryItem().IsNull())
return;
GetFrameHost()->UpdateState(GetWebFrame()->CurrentHistoryItemToPageState());
}
blink::WebURL RenderFrameImpl::LastCommittedUrlForUKM() {
return GetLoadingUrl();
}
// Returns the "loading URL", which might be different than the "document URL"
// used in the DocumentLoader in some cases:
// - For error pages, it will return the URL that failed to load, instead of the
// chrome-error:// URL used as the document URL in the DocumentLoader.
// - For some loadDataWithBaseURL() navigations, it will return the data: URL
// used to commit, instead of the "base URL" used as the document URL in the
// DocumentLoader. Note that this does not cover all loadDataWithBaseURL()
// navigations, the rest will just return the document/base URL. See comments in
// BuildDocumentStateFromParams() and ShouldLoadDataWithBaseURL() for more
// details.
GURL RenderFrameImpl::GetLoadingUrl() const {
WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
GURL overriden_url;
if (MaybeGetOverriddenURL(document_loader, &overriden_url))
return overriden_url;
return document_loader->GetUrl();
}
media::MediaPermission* RenderFrameImpl::GetMediaPermission() {
if (!media_permission_dispatcher_) {
media_permission_dispatcher_ =
std::make_unique<MediaPermissionDispatcher>(this);
}
return media_permission_dispatcher_.get();
}
void RenderFrameImpl::RegisterMojoInterfaces() {
// TODO(dcheng): Fold this interface into mojom::Frame.
GetAssociatedInterfaceRegistry()->AddInterface(base::BindRepeating(
&RenderFrameImpl::BindAutoplayConfiguration, weak_factory_.GetWeakPtr()));
GetAssociatedInterfaceRegistry()->AddInterface(base::BindRepeating(
&RenderFrameImpl::BindFrameBindingsControl, weak_factory_.GetWeakPtr()));
GetAssociatedInterfaceRegistry()->AddInterface(base::BindRepeating(
&RenderFrameImpl::BindNavigationClient, weak_factory_.GetWeakPtr()));
// TODO(dcheng): Fold this interface into mojom::Frame.
GetAssociatedInterfaceRegistry()->AddInterface(base::BindRepeating(
&RenderFrameImpl::BindMhtmlFileWriter, base::Unretained(this)));
GetAssociatedInterfaceRegistry()->AddInterface(base::BindRepeating(
&RenderAccessibilityManager::BindReceiver,
base::Unretained(render_accessibility_manager_.get())));
}
void RenderFrameImpl::BindMhtmlFileWriter(
mojo::PendingAssociatedReceiver<mojom::MhtmlFileWriter> receiver) {
mhtml_file_writer_receiver_.reset();
mhtml_file_writer_receiver_.Bind(
std::move(receiver), GetTaskRunner(blink::TaskType::kInternalDefault));
}
// TODO(https://crbug.com/787252): Move this method to Blink, and eliminate
// the plumbing logic through blink::WebLocalFrameClient.
void RenderFrameImpl::CheckIfAudioSinkExistsAndIsAuthorized(
const blink::WebString& sink_id,
blink::WebSetSinkIdCompleteCallback completion_callback) {
std::move(
blink::ConvertToOutputDeviceStatusCB(std::move(completion_callback)))
.Run(blink::WebAudioDeviceFactory::GetOutputDeviceInfo(
GetWebFrame()->GetLocalFrameToken(),
media::AudioSinkParameters(base::UnguessableToken(),
sink_id.Utf8()))
.device_status());
}
std::unique_ptr<blink::WebURLLoaderFactory>
RenderFrameImpl::CreateURLLoaderFactory() {
if (!RenderThreadImpl::current()) {
// Some tests (e.g. RenderViewTests) do not have RenderThreadImpl,
// and must create a factory override instead.
if (web_url_loader_factory_override_for_test_)
return web_url_loader_factory_override_for_test_->Clone();
// If the override does not exist, try looking in the ancestor chain since
// we might have created child frames and asked them to create a URL loader
// factory.
for (auto* ancestor = GetWebFrame()->Parent(); ancestor;
ancestor = ancestor->Parent()) {
RenderFrameImpl* ancestor_frame = RenderFrameImpl::FromWebFrame(ancestor);
if (ancestor_frame &&
ancestor_frame->web_url_loader_factory_override_for_test_) {
return ancestor_frame->web_url_loader_factory_override_for_test_
->Clone();
}
}
// At this point we can't create anything.
NOTREACHED();
return nullptr;
}
return std::make_unique<FrameURLLoaderFactory>(weak_factory_.GetWeakPtr());
}
void RenderFrameImpl::OnStopLoading() {
for (auto& observer : observers_)
observer.OnStop();
}
void RenderFrameImpl::DraggableRegionsChanged() {
for (auto& observer : observers_)
observer.DraggableRegionsChanged();
}
bool RenderFrameImpl::IsBrowserSideNavigationPending() {
return browser_side_navigation_pending_;
}
void RenderFrameImpl::LoadHTMLStringForTesting(const std::string& html,
const GURL& base_url,
const std::string& text_encoding,
const GURL& unreachable_url,
bool replace_current_item) {
AssertNavigationCommits assert_navigation_commits(
this, kMayReplaceInitialEmptyDocument);
pending_loader_factories_ = CreateLoaderFactoryBundle(
blink::ChildPendingURLLoaderFactoryBundle::CreateFromDefaultFactoryImpl(
network::NotImplementedURLLoaderFactory::Create()),
absl::nullopt, // |subresource_overrides|
{}); // prefetch_loader_factory
auto navigation_params = std::make_unique<WebNavigationParams>();
navigation_params->url = base_url;
navigation_params->sandbox_flags = network::mojom::WebSandboxFlags::kNone;
WebNavigationParams::FillStaticResponse(navigation_params.get(), "text/html",
WebString::FromUTF8(text_encoding),
html);
navigation_params->unreachable_url = unreachable_url;
navigation_params->frame_load_type =
replace_current_item ? blink::WebFrameLoadType::kReplaceCurrentItem
: blink::WebFrameLoadType::kStandard;
navigation_params->service_worker_network_provider =
ServiceWorkerNetworkProviderForFrame::CreateInvalidInstance();
frame_->CommitNavigation(std::move(navigation_params), BuildDocumentState());
}
scoped_refptr<base::SingleThreadTaskRunner> RenderFrameImpl::GetTaskRunner(
blink::TaskType task_type) {
return GetWebFrame()->GetTaskRunner(task_type);
}
int RenderFrameImpl::GetEnabledBindings() {
return enabled_bindings_;
}
void RenderFrameImpl::SetAccessibilityModeForTest(ui::AXMode new_mode) {
render_accessibility_manager_->SetMode(new_mode.mode());
}
scoped_refptr<network::SharedURLLoaderFactory>
RenderFrameImpl::GetURLLoaderFactory() {
return GetLoaderFactoryBundle();
}
const RenderFrameMediaPlaybackOptions&
RenderFrameImpl::GetRenderFrameMediaPlaybackOptions() {
return renderer_media_playback_options_;
}
void RenderFrameImpl::SetRenderFrameMediaPlaybackOptions(
const RenderFrameMediaPlaybackOptions& opts) {
renderer_media_playback_options_ = opts;
}
void RenderFrameImpl::SetAllowsCrossBrowsingInstanceFrameLookup() {
GetWebFrame()->SetAllowsCrossBrowsingInstanceFrameLookup();
}
bool RenderFrameImpl::IsAccessibilityEnabled() const {
return render_accessibility_manager_->GetAccessibilityMode().has_mode(
ui::AXMode::kWebContents);
}
#if BUILDFLAG(ENABLE_PLUGINS)
mojom::PepperHost* RenderFrameImpl::GetPepperHost() {
if (!pepper_host_remote_.is_bound())
GetRemoteAssociatedInterfaces()->GetInterface(&pepper_host_remote_);
return pepper_host_remote_.get();
}
void RenderFrameImpl::PepperInstanceCreated(
PepperPluginInstanceImpl* instance,
mojo::PendingAssociatedRemote<mojom::PepperPluginInstance> mojo_instance,
mojo::PendingAssociatedReceiver<mojom::PepperPluginInstanceHost>
mojo_host) {
active_pepper_instances_.insert(instance);
GetPepperHost()->InstanceCreated(
instance->pp_instance(), std::move(mojo_instance), std::move(mojo_host));
}
void RenderFrameImpl::PepperInstanceDeleted(
PepperPluginInstanceImpl* instance) {
active_pepper_instances_.erase(instance);
if (focused_pepper_plugin_ == instance)
PepperFocusChanged(instance, false);
}
void RenderFrameImpl::PepperFocusChanged(PepperPluginInstanceImpl* instance,
bool focused) {
if (focused)
focused_pepper_plugin_ = instance;
else if (focused_pepper_plugin_ == instance)
focused_pepper_plugin_ = nullptr;
GetLocalRootWebFrameWidget()->UpdateTextInputState();
GetLocalRootWebFrameWidget()->UpdateSelectionBounds();
}
#endif // ENABLE_PLUGINS
blink::WebComputedAXTree* RenderFrameImpl::GetOrCreateWebComputedAXTree() {
if (!computed_ax_tree_)
computed_ax_tree_ = std::make_unique<AomContentAxTree>(this);
return computed_ax_tree_.get();
}
std::unique_ptr<blink::WebSocketHandshakeThrottle>
RenderFrameImpl::CreateWebSocketHandshakeThrottle() {
WebLocalFrame* web_local_frame = GetWebFrame();
if (!web_local_frame)
return nullptr;
auto* render_frame = content::RenderFrame::FromWebFrame(web_local_frame);
if (!render_frame)
return nullptr;
int render_frame_id = render_frame->GetRoutingID();
// Lazily create the provider.
if (!websocket_handshake_throttle_provider_) {
websocket_handshake_throttle_provider_ =
GetContentClient()
->renderer()
->CreateWebSocketHandshakeThrottleProvider();
if (!websocket_handshake_throttle_provider_)
return nullptr;
}
return websocket_handshake_throttle_provider_->CreateThrottle(
render_frame_id,
render_frame->GetTaskRunner(blink::TaskType::kInternalDefault));
}
bool RenderFrameImpl::GetCaretBoundsFromFocusedPlugin(gfx::Rect& rect) {
#if BUILDFLAG(ENABLE_PLUGINS)
if (focused_pepper_plugin_) {
rect = focused_pepper_plugin_->GetCaretBounds();
return true;
}
#endif
return false;
}
void RenderFrameImpl::AddMessageToConsoleImpl(
blink::mojom::ConsoleMessageLevel level,
const std::string& message,
bool discard_duplicates) {
blink::WebConsoleMessage wcm(level, WebString::FromUTF8(message));
frame_->AddMessageToConsole(wcm, discard_duplicates);
}
void RenderFrameImpl::SetWebURLLoaderFactoryOverrideForTest(
std::unique_ptr<blink::WebURLLoaderFactoryForTest> factory) {
web_url_loader_factory_override_for_test_ = std::move(factory);
}
scoped_refptr<blink::ChildURLLoaderFactoryBundle>
RenderFrameImpl::CloneLoaderFactories() {
auto bundle_info = base::WrapUnique(
static_cast<blink::TrackedChildPendingURLLoaderFactoryBundle*>(
GetLoaderFactoryBundle()->Clone().release()));
return base::MakeRefCounted<blink::TrackedChildURLLoaderFactoryBundle>(
std::move(bundle_info));
}
blink::scheduler::WebAgentGroupScheduler&
RenderFrameImpl::GetAgentGroupScheduler() {
return agent_scheduling_group_.agent_group_scheduler();
}
url::Origin RenderFrameImpl::GetSecurityOriginOfTopFrame() {
return frame_->Top()->GetSecurityOrigin();
}
gfx::RectF RenderFrameImpl::ElementBoundsInWindow(
const blink::WebElement& element) {
return gfx::RectF(GetLocalRootWebFrameWidget()->BlinkSpaceToEnclosedDIPs(
element.BoundsInViewport()));
}
void RenderFrameImpl::ConvertViewportToWindow(gfx::Rect* rect) {
*rect = GetLocalRootWebFrameWidget()->BlinkSpaceToEnclosedDIPs(*rect);
}
float RenderFrameImpl::GetDeviceScaleFactor() {
return GetLocalRootWebFrameWidget()->GetScreenInfo().device_scale_factor;
}
bool RenderFrameImpl::DeferMediaLoad(bool has_played_media_before,
base::OnceClosure closure) {
if (blink::features::IsPrerender2Enabled() &&
frame_->GetDocument().IsPrerendering()) {
frame_->GetDocument().AddPostPrerenderingActivationStep(
base::BindOnce(CallClientDeferMediaLoad, weak_factory_.GetWeakPtr(),
has_played_media_before, std::move(closure)));
return true;
}
return GetContentClient()->renderer()->DeferMediaLoad(
this, has_played_media_before, std::move(closure));
}
} // namespace content