0
Files
src/content/renderer/render_frame_impl.cc
Sandor Major 07aa8b0689 Move PermissionsPolicy to the network service
The network service needs access to the permissions policy. Previous CLs
moved all the dependencies so it is now safe to move this class.

The following files need to include permissions_policy.h because
otherwise they want to have access into incomplete `PermissionsPolicy`:
* components/permissions/permission_uma_util.cc
* third_party/blink/common/manifest/manifest_util.cc
* third_party/blink/renderer/core/html/client_hints_util.cc
* third_party/blink/renderer/modules/payments/payment_app_service_worker_registration.cc
* third_party/blink/renderer/modules/payments/payment_instruments.cc

Bug: 382291442
Low-Coverage-Reason: LARGE_SCALE_REFACTOR
Change-Id: Ifcf52df3bc5c035e8f6a45df02cf93f304b748c9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6310821
Auto-Submit: Sandor «Alex» Major <sandormajor@chromium.org>
Owners-Override: Rick Byers <rbyers@chromium.org>
Commit-Queue: Sandor «Alex» Major <sandormajor@chromium.org>
Reviewed-by: Ari Chivukula <arichiv@chromium.org>
Reviewed-by: Rick Byers <rbyers@chromium.org>
Reviewed-by: Maks Orlovich <morlovich@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1426391}
2025-02-28 09:24:35 -08:00

7047 lines
295 KiB
C++

// Copyright 2013 The Chromium Authors
// 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 <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/check_deref.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
#include "base/containers/to_vector.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/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.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/notreached.h"
#include "base/observer_list.h"
#include "base/process/process.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "base/trace_event/trace_event.h"
#include "base/types/optional_util.h"
#include "base/unguessable_token.h"
#include "base/uuid.h"
#include "base/values.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "content/common/associated_interfaces.mojom.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/content_switches_internal.h"
#include "content/common/debug_utils.h"
#include "content/common/features.h"
#include "content/common/frame.mojom.h"
#include "content/common/frame_messages.mojom.h"
#include "content/common/main_frame_counter.h"
#include "content/common/navigation_client.mojom.h"
#include "content/common/navigation_gesture.h"
#include "content/common/navigation_params_utils.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/extra_mojo_js_features.mojom.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/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/public/renderer/window_features_converter.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/background_resource_fetch_assets.h"
#include "content/renderer/content_security_policy_util.h"
#include "content/renderer/document_state.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/local_resource_url_loader_factory.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/policy_container_util.h"
#include "content/renderer/render_process.h"
#include "content/renderer/render_thread_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 "media/mojo/mojom/audio_processing.mojom.h"
#include "mojo/public/cpp/bindings/associated_remote.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/remote.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/fetch_api.mojom.h"
#include "services/network/public/mojom/restricted_cookie_manager.mojom.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#include "services/tracing/public/cpp/perfetto/track_name_recorder.h"
#include "services/viz/public/cpp/gpu/context_provider_command_buffer.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/interest_group/ad_auction_constants.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/navigation/impression.h"
#include "third_party/blink/public/common/navigation/navigation_params.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/renderer_preferences/renderer_preferences.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/dom_storage/storage_area.mojom.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
#include "third_party/blink/public/mojom/frame/frame.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/frame/view_transition_state.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/fetch_later.mojom.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/mojom/render_accessibility.mojom.h"
#include "third_party/blink/public/mojom/renderer_preference_watcher.mojom.h"
#include "third_party/blink/public/mojom/widget/platform_widget.mojom.h"
#include "third_party/blink/public/platform/file_path_conversion.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_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_navigation_body_loader.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_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/web/blink.h"
#include "third_party/blink/public/web/modules/media/audio/audio_device_factory.h"
#include "third_party/blink/public/web/modules/media/audio/audio_output_ipc_factory.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_input_method_controller.h"
#include "third_party/blink/public/web/web_link_preview_triggerer.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_navigation_type.h"
#include "third_party/blink/public/web/web_performance_metrics_for_nested_contexts.h"
#include "third_party/blink/public/web/web_picture_in_picture_window_options.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_remote_frame.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_v8_features.h"
#include "third_party/blink/public/web/web_view.h"
#include "third_party/blink/public/web/web_widget.h"
#include "third_party/blink/public/web/web_window_features.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-isolate.h"
#include "v8/include/v8-local-handle.h"
#include "v8/include/v8-microtask-queue.h"
#if BUILDFLAG(ENABLE_PPAPI)
#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 BUILDFLAG(IS_ANDROID)
#include <cpu-features.h>
#include "content/renderer/java/gin_java_bridge_dispatcher.h"
#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
#endif
using base::Time;
using blink::ContextMenuData;
using blink::WebContentDecryptionModule;
using blink::WebData;
using blink::WebDocument;
using blink::WebDocumentLoader;
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::WebString;
using blink::WebThreadSafeData;
using blink::WebURL;
using blink::WebURLError;
using blink::WebURLRequest;
using blink::WebURLResponse;
using blink::WebView;
using blink::mojom::SelectionMenuBehavior;
using network::mojom::ReferrerPolicy;
namespace content {
namespace {
const int kExtraCharsBeforeAndAfterSelection = 100;
const size_t kMaxURLLogChars = 1024;
const char kCommitRenderFrame[] = "Navigation.CommitRenderFrame";
// 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::Seconds(5);
constexpr base::TimeDelta kDelaySecondsForContentStateSync = base::Seconds(1);
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
typedef std::map<int, RenderFrameImpl*> RoutingIDFrameMap;
static base::LazyInstance<RoutingIDFrameMap>::DestructorAtExit
g_routing_id_frame_map = LAZY_INSTANCE_INITIALIZER;
#endif
typedef std::map<blink::WebFrame*, RenderFrameImpl*> FrameMap;
base::LazyInstance<FrameMap>::DestructorAtExit g_frame_map =
LAZY_INSTANCE_INITIALIZER;
// Please keep in sync with "RendererBlockedURLReason" in
// tools/metrics/histograms/metadata/navigation/enums.xml. These values are
// persisted to logs. Entries should not be renumbered and numeric values should
// never be reused.
enum class RendererBlockedURLReason {
kInvalidURL = 0,
kTooLongURL = 1,
kBadAboutURL = 2,
kMaxValue = kBadAboutURL
};
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,
bool is_in_fenced_frame_tree,
WebNavigationType navigation_type) {
if (is_in_fenced_frame_tree) {
// Navigations inside fenced frame trees do not add session history items
// and must be marked with PAGE_TRANSITION_AUTO_SUBFRAME. This is set
// regardless of the `is_main_frame` value since this is inside a fenced
// frame tree and should behave the same as iframes.
return ui::PAGE_TRANSITION_AUTO_SUBFRAME;
}
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::kWebNavigationTypeFormResubmittedBackForward ||
navigation_type == blink::kWebNavigationTypeFormResubmittedReload;
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,
bool is_in_fenced_frame_tree) {
NavigationState* navigation_state =
DocumentState::FromDocumentLoader(document_loader)->navigation_state();
ui::PageTransition default_transition =
navigation_state->IsForSynchronousCommit()
? ui::PAGE_TRANSITION_LINK
: ui::PageTransitionFromInt(
navigation_state->common_params().transition);
if (!is_in_fenced_frame_tree && navigation_state->WasWithinSameDocument())
return default_transition;
return GetTransitionType(default_transition,
document_loader->ReplacesCurrentHistoryItem(),
is_main_frame, is_in_fenced_frame_tree,
document_loader->GetNavigationType());
}
// Ensure that the renderer does not send commit URLs to the browser process
// that are known to be unsupported. Ideally these would be caught earlier in
// Blink and not get this far. Histograms are reported (similar to those in
// RenderProcessHostImpl::FilterURL) to track the cases that should be handled
// earlier. See https://crbug.com/40066983.
bool IsValidCommitUrl(const GURL& url) {
// Invalid URLs are not accepted by the browser process.
if (!url.is_valid()) {
base::UmaHistogramEnumeration("Navigation.Renderer.BlockedForFilterURL",
RendererBlockedURLReason::kInvalidURL);
return false;
}
// Do not send a URL longer than Mojo will serialize.
if (url.possibly_invalid_spec().length() > url::kMaxURLChars) {
base::UmaHistogramEnumeration("Navigation.Renderer.BlockedForFilterURL",
RendererBlockedURLReason::kTooLongURL);
return false;
}
// Any about: URLs must be either about:blank or about:srcdoc (optionally with
// fragments).
if (url.SchemeIs(url::kAboutScheme) &&
(!url.IsAboutBlank() && !url.IsAboutSrcdoc())) {
base::UmaHistogramEnumeration("Navigation.Renderer.BlockedForFilterURL",
RendererBlockedURLReason::kBadAboutURL);
return false;
}
return true;
}
// 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);
// `document_state` may be null if it was taken from the loader, e.g. when
// committing the result of evaluating a javascript: URL,
// `FrameLoader::CommitNavigation()` takes the `DocumentState`. Early
// returning here means the answer may be inaccurate, but this can only
// happen when the replaced `Document` is being detached and about to go
// away.
if (!document_state) {
return false;
}
// 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(crbug.com/40187600): 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->UnreachableWebURL();
return true;
}
return false;
}
// Returns false unless this is a top-level navigation.
bool IsTopLevelNavigation(WebFrame* frame) {
return frame->Parent() == nullptr && !frame->View()->IsFencedFrameRoot();
}
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);
}
}
// 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 `blink::RemoteFrame` 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;
navigation_params->prefetched_signed_exchanges = base::ToVector(
commit_params.prefetched_signed_exchanges, [](const auto& exchange) {
blink::WebURLResponse web_response = blink::WebURLResponse::Create(
exchange->inner_url, *exchange->inner_response,
false /* report_security_info*/, -1 /* request_id */);
return 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->force_enabled_origin_trials = base::ToVector(
commit_params.force_enabled_origin_trials, &WebString::FromASCII);
navigation_params->early_hints_preloaded_resources = base::ToVector(
commit_params.early_hints_preloaded_resources, blink::ToWebURL);
// Pass on the `initiator_base_url` sent via the common_params for srcdoc and
// about:blank documents. This will be picked up in DocumentLoader.
// Note: It's possible for initiator_base_url to be empty if this is an
// error srcdoc page. See test
// NavigationRequestBrowserTest.OriginForSrcdocErrorPageInSubframe.
if (common_params.initiator_base_url) {
CHECK(common_params.url.IsAboutSrcdoc() ||
common_params.url.IsAboutBlank());
navigation_params->fallback_base_url =
common_params.initiator_base_url.value();
} else {
navigation_params->fallback_base_url = WebURL();
}
}
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,
network::mojom::RequestDestination request_destination) {
// 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);
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, from_ad);
std::optional<GURL> initiator_base_url;
GURL requestor_base_url(info->requestor_base_url);
// Make sure the url length doesn't exceed the limit enforced by Mojo when
// it's sent to the browser process.
if (requestor_base_url.is_valid() &&
requestor_base_url.possibly_invalid_spec().length() <=
url::kMaxURLChars) {
initiator_base_url = requestor_base_url;
}
return blink::mojom::CommonNavigationParams::New(
info->url_request.Url(), info->url_request.RequestorOrigin(),
initiator_base_url, std::move(referrer),
url_request_extra_data->transition_type(), navigation_type,
download_policy,
info->frame_load_type == WebFrameLoadType::kReplaceCurrentItem, GURL(),
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,
info->initiator_origin_trial_features, info->href_translate.Latin1(),
is_history_navigation_in_new_child_frame, info->input_start,
request_destination);
}
WebFrameLoadType NavigationTypeToLoadType(
blink::mojom::NavigationType navigation_type,
bool should_replace_current_entry) {
switch (navigation_type) {
case blink::mojom::NavigationType::RELOAD:
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:
return WebFrameLoadType::kRestore;
case blink::mojom::NavigationType::SAME_DOCUMENT:
case blink::mojom::NavigationType::DIFFERENT_DOCUMENT:
return should_replace_current_entry
? WebFrameLoadType::kReplaceCurrentItem
: WebFrameLoadType::kStandard;
default:
NOTREACHED();
}
}
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 std::vector<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 raw_ref<const base::flat_map<GURL, base::FilePath>> url_to_local_path_;
const raw_ref<const base::flat_map<blink::FrameToken, base::FilePath>>
frame_token_to_local_path_;
};
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_ = std::make_unique<MHTMLFileHandleWriter>(
std::move(main_thread_task_runner), std::move(callback),
std::move(params.output_handle->get_file_handle()));
} else {
handle_ = std::make_unique<MHTMLProducerHandleWriter>(
std::move(main_thread_task_runner), std::move(callback),
std::move(params.output_handle->get_producer_handle()));
}
}
MHTMLHandleWriterDelegate(const MHTMLHandleWriterDelegate&) = delete;
MHTMLHandleWriterDelegate& operator=(const MHTMLHandleWriterDelegate&) =
delete;
void WriteContents(std::vector<WebThreadSafeData> mhtml_contents) {
// MHTMLHandleWriter::WriteContents calls MHTMLHandleWriter::Finish
// eventually.
base::ThreadPool::PostTask(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&MHTMLHandleWriter::WriteContents, std::move(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,
std::move(handle_), save_status));
}
private:
std::unique_ptr<MHTMLHandleWriter> handle_;
};
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;
}
// 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>();
document_state->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());
DCHECK(!common_params.navigation_start.is_null());
DCHECK(!common_params.url.SchemeIs(url::kJavaScriptScheme));
document_state->set_is_overriding_user_agent(
commit_params.is_overriding_user_agent);
document_state->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()).
document_state->set_was_load_data_with_base_url_request(
commit_params.is_load_data_with_base_url);
if (commit_params.is_load_data_with_base_url)
document_state->set_data_url(common_params.url);
document_state->set_navigation_state(
NavigationState::CreateForCrossDocumentCommit(
common_params.Clone(), commit_params.Clone(),
std::move(commit_callback), std::move(navigation_client),
was_initiated_in_this_frame));
return document_state;
}
std::optional<WebURL> ApplyFilePathAlias(const WebURL& target) {
const base::CommandLine::StringType file_url_path_alias =
base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
switches::kFileUrlPathAlias);
if (file_url_path_alias.empty()) {
return std::nullopt;
}
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 std::nullopt;
}
#if BUILDFLAG(IS_WIN)
std::wstring path = base::UTF16ToWide(target.GetString().Utf16());
const std::wstring file_prefix =
base::ASCIIToWide(url::kFileScheme) +
base::ASCIIToWide(url::kStandardSchemeSeparator);
#else
std::string path = target.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 std::nullopt;
}
base::ReplaceFirstSubstringAfterOffset(&path, 0, alias_mapping[0],
alias_mapping[1]);
#if BUILDFLAG(IS_WIN)
return blink::WebURL(GURL(base::WideToUTF8(path)));
#else
return 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;
renderer_navigation_timings.parent_resource_timing_access =
browser_navigation_timings.parent_resource_timing_access;
renderer_navigation_timings.system_entropy_at_navigation_start =
browser_navigation_timings.system_entropy_at_navigation_start;
return renderer_navigation_timings;
}
WebHistoryItem NavigationApiHistoryEntryPtrToWebHistoryItem(
const blink::mojom::NavigationApiHistoryEntry& entry) {
return WebHistoryItem(
WebString::FromUTF16(entry.url), WebString::FromUTF16(entry.key),
WebString::FromUTF16(entry.id), entry.item_sequence_number,
entry.document_sequence_number, WebString::FromUTF16(entry.state));
}
// Fills navigation data sent by the browser to a blink understandable
// format, blink::WebNavigationParams.
void FillMiscNavigationParams(
const blink::mojom::CommonNavigationParams& common_params,
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);
if (!commit_params.redirect_infos.empty()) {
navigation_params->navigation_timings.critical_ch_restart =
commit_params.redirect_infos.back().critical_ch_restart_time;
}
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_cross_site_cross_browsing_context_group =
commit_params.is_cross_site_cross_browsing_context_group;
navigation_params->should_have_sticky_user_activation =
commit_params.should_have_sticky_user_activation;
#if BUILDFLAG(IS_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->storage_key = std::move(commit_params.storage_key);
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->origin_agent_cluster_left_as_default =
commit_params.origin_agent_cluster_left_as_default;
navigation_params->reduced_accept_language =
WebString::FromASCII(commit_params.reduced_accept_language);
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;
// Copy the modified runtime features from `commit_params` to send to the
// Blink renderer class WebLocalFrameImpl.
navigation_params->modified_runtime_features =
commit_params.modified_runtime_features;
// Populate the arrays of non-current entries for the window.navigation API.
auto& entry_arrays = commit_params.navigation_api_history_entry_arrays;
navigation_params->navigation_api_back_entries.reserve(
entry_arrays->back_entries.size());
for (const auto& entry : entry_arrays->back_entries) {
navigation_params->navigation_api_back_entries.emplace_back(
NavigationApiHistoryEntryPtrToWebHistoryItem(*entry));
}
navigation_params->navigation_api_forward_entries.reserve(
entry_arrays->forward_entries.size());
for (const auto& entry : entry_arrays->forward_entries) {
navigation_params->navigation_api_forward_entries.emplace_back(
NavigationApiHistoryEntryPtrToWebHistoryItem(*entry));
}
if (entry_arrays->previous_entry) {
navigation_params->navigation_api_previous_entry =
NavigationApiHistoryEntryPtrToWebHistoryItem(
*entry_arrays->previous_entry);
}
if (commit_params.fenced_frame_properties) {
navigation_params->fenced_frame_properties =
commit_params.fenced_frame_properties;
if (commit_params.fenced_frame_properties->nested_urn_config_pairs() &&
commit_params.fenced_frame_properties->nested_urn_config_pairs()
->potentially_opaque_value.has_value()) {
const auto& nested_urn_config_pairs_value =
commit_params.fenced_frame_properties->nested_urn_config_pairs()
->potentially_opaque_value.value();
DCHECK_EQ(blink::MaxAdAuctionAdComponents(),
nested_urn_config_pairs_value.size());
navigation_params->ad_auction_components.emplace();
for (const auto& nested_urn_config_pair : nested_urn_config_pairs_value) {
const GURL& urn = nested_urn_config_pair.first;
DCHECK(urn.SchemeIs(url::kUrnScheme));
navigation_params->ad_auction_components->push_back(blink::WebURL(urn));
}
}
}
navigation_params->ancestor_or_self_has_cspee =
commit_params.ancestor_or_self_has_cspee;
navigation_params->browsing_context_group_info =
commit_params.browsing_context_group_info;
navigation_params->content_settings =
std::move(commit_params.content_settings);
if (commit_params.cookie_deprecation_label.has_value()) {
navigation_params->cookie_deprecation_label =
WebString::FromASCII(*commit_params.cookie_deprecation_label);
}
navigation_params->initial_permission_statuses =
std::move(commit_params.initial_permission_statuses);
navigation_params->force_new_document_sequence_number =
commit_params.force_new_document_sequence_number;
}
std::string GetUniqueNameOfWebFrame(WebFrame* web_frame) {
if (web_frame->IsWebLocalFrame())
return RenderFrameImpl::FromWebFrame(web_frame)->unique_name();
return web_frame->ToWebRemoteFrame()->UniqueName().Utf8();
}
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();
}
void CallClientDeferMediaLoad(base::WeakPtr<RenderFrameImpl> frame,
bool has_played_media_before,
base::OnceClosure closure) {
if (!frame)
return;
GetContentClient()->renderer()->DeferMediaLoad(
frame.get(), has_played_media_before, std::move(closure));
}
void LogCommitHistograms(base::TimeTicks commit_sent,
bool is_main_frame,
const GURL& new_page_url) {
if (!base::TimeTicks::IsConsistentAcrossProcesses())
return;
const char* frame_type = is_main_frame ? "MainFrame" : "Subframe";
auto now = base::TimeTicks::Now();
base::UmaHistogramTimes(
base::StrCat({"Navigation.RendererCommitDelay.", frame_type}),
now - commit_sent);
if (auto* task = base::TaskAnnotator::CurrentTaskForThread()) {
base::UmaHistogramTimes(
base::StrCat({"Navigation.RendererCommitQueueTime.", frame_type}),
now - task->queue_time);
}
// Some tests don't set the render thread.
if (!RenderThreadImpl::current())
return;
base::TimeTicks run_loop_start_time =
RenderThreadImpl::current()->run_loop_start_time();
// If the commit was sent before the run loop was started for this process,
// the navigation was likely delayed while waiting for the process to start.
if (commit_sent < run_loop_start_time) {
base::UmaHistogramTimes(
base::StrCat({"Navigation.RendererCommitProcessWaitTime.", frame_type}),
run_loop_start_time - commit_sent);
}
// We want to record the following metric just one time per process.
static bool is_first_commit = true;
if (is_first_commit) {
is_first_commit = false;
if (run_loop_start_time <= now && new_page_url.is_valid() &&
new_page_url.SchemeIsHTTPOrHTTPS()) {
const char* const name =
is_main_frame
? "Navigation.RendererRunLoopStartToFirstCommitNavigation2."
"MainFrame"
: "Navigation.RendererRunLoopStartToFirstCommitNavigation2."
"Subframe";
const auto trace_id = TRACE_ID_WITH_SCOPE(
name, TRACE_ID_LOCAL(RenderThreadImpl::current()));
TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(
"navigation", name, trace_id, run_loop_start_time, "url",
new_page_url);
TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0("navigation", name,
trace_id, now);
base::UmaHistogramLongTimes(name, now - run_loop_start_time);
}
}
}
content::mojom::WindowContainerType WindowFeaturesToContainerType(
const blink::WebWindowFeatures& window_features) {
if (window_features.background) {
if (window_features.persistent)
return content::mojom::WindowContainerType::PERSISTENT;
else
return content::mojom::WindowContainerType::BACKGROUND;
} else {
return content::mojom::WindowContainerType::NORMAL;
}
}
WindowOpenDisposition NavigationPolicyToDisposition(
WebNavigationPolicy policy) {
switch (policy) {
case blink::kWebNavigationPolicyDownload:
return WindowOpenDisposition::SAVE_TO_DISK;
case blink::kWebNavigationPolicyCurrentTab:
return WindowOpenDisposition::CURRENT_TAB;
case blink::kWebNavigationPolicyNewBackgroundTab:
return WindowOpenDisposition::NEW_BACKGROUND_TAB;
case blink::kWebNavigationPolicyNewForegroundTab:
return WindowOpenDisposition::NEW_FOREGROUND_TAB;
case blink::kWebNavigationPolicyNewWindow:
return WindowOpenDisposition::NEW_WINDOW;
case blink::kWebNavigationPolicyNewPopup:
return WindowOpenDisposition::NEW_POPUP;
case blink::kWebNavigationPolicyPictureInPicture:
return WindowOpenDisposition::NEW_PICTURE_IN_PICTURE;
}
NOTREACHED() << "Unexpected WebNavigationPolicy";
}
bool ShouldNotifySubresourceResponseStarted(
const blink::RendererPreferences& pref) {
if (!base::FeatureList::IsEnabled(
features::kReduceSubresourceResponseStartedIPC)) {
return true;
}
return pref.send_subresource_notification;
}
// Initialize the WebFrameWidget with compositing. Only local root frames
// create a widget.
// `previous_widget` indicates whether the compositor for the frame which
// is being replaced by this frame should be used instead of creating a new
// compositor instance.
void InitializeFrameWidgetForFrame(
WebLocalFrame& frame,
blink::WebFrameWidget* previous_widget,
mojom::CreateFrameWidgetParamsPtr widget_params,
const bool is_for_nested_main_frame) {
CHECK(widget_params);
const bool is_main_frame = !frame.Parent();
CHECK(is_main_frame || !is_for_nested_main_frame);
const bool is_for_scalable_page =
is_main_frame && !frame.View()->IsFencedFrameRoot();
const auto frame_sink_id =
previous_widget ? previous_widget->GetFrameSinkId()
: viz::FrameSinkId(RenderThread::Get()->GetClientId(),
widget_params->routing_id);
auto* web_frame_widget = 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),
frame_sink_id, is_for_nested_main_frame, is_for_scalable_page,
/*hidden=*/true);
if (previous_widget) {
web_frame_widget->InitializeCompositingFromPreviousWidget(
widget_params->visual_properties.screen_infos,
/*settings=*/nullptr, *previous_widget);
} else {
web_frame_widget->InitializeCompositing(
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/40387047): This could become part of WebFrameWidget Init.
web_frame_widget->ApplyVisualProperties(widget_params->visual_properties);
}
} // namespace
// 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 MHTMLPartsGenerationDelegateImpl final
: public WebFrameSerializer::MHTMLPartsGenerationDelegate {
public:
explicit MHTMLPartsGenerationDelegateImpl(
mojom::SerializeAsMHTMLParamsPtr params)
: params_(std::move(params)) {
// 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::ranges::adjacent_find(params_->digests_of_uris_to_skip) ==
params_->digests_of_uris_to_skip.end());
}
MHTMLPartsGenerationDelegateImpl(const MHTMLPartsGenerationDelegateImpl&) =
delete;
MHTMLPartsGenerationDelegateImpl& operator=(
const MHTMLPartsGenerationDelegateImpl&) = delete;
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;
}
std::unordered_set<std::string> TakeSerializedResourcesUriDigests() {
return std::move(serialized_resources_uri_digests_);
}
mojom::SerializeAsMHTMLParamsPtr TakeParams() { return std::move(params_); }
private:
std::vector<std::string> digests_of_uris_to_skip_;
mojom::SerializeAsMHTMLParamsPtr params_;
std::unordered_set<std::string> serialized_resources_uri_digests_;
};
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);
}
MHTMLBodyLoaderClient(const MHTMLBodyLoaderClient&) = delete;
MHTMLBodyLoaderClient& operator=(const MHTMLBodyLoaderClient&) = delete;
~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 BodyDataReceived(base::span<const char> data) override {
data_.Append(base::as_bytes(data));
}
void BodyLoadingFinished(base::TimeTicks completion_time,
int64_t total_encoded_data_length,
int64_t total_encoded_body_length,
int64_t total_decoded_body_length,
const std::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.
raw_ptr<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_;
};
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(
std::string_view 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 (GetUniqueNameOfWebFrame(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)(std::string_view)) const {
std::vector<std::string> result;
for (blink::WebFrame* frame = begin_point == BeginPoint::kParentFrame
? GetWebFrame()->Parent()
: GetWebFrame();
frame; frame = frame->Parent()) {
result.push_back(GetUniqueNameOfWebFrame(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,
const blink::LocalFrameToken& frame_token,
int32_t routing_id,
mojo::PendingAssociatedReceiver<mojom::Frame> frame_receiver,
mojo::PendingAssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
associated_interface_provider,
const base::UnguessableToken& devtools_frame_token,
bool is_for_nested_main_frame) {
DCHECK(routing_id != MSG_ROUTING_NONE);
CreateParams params(agent_scheduling_group, frame_token, routing_id,
std::move(frame_receiver),
std::move(associated_interface_provider),
devtools_frame_token, is_for_nested_main_frame);
if (g_create_render_frame_impl)
return g_create_render_frame_impl(std::move(params));
else
return new RenderFrameImpl(std::move(params));
}
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
// 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;
}
#endif
// static
RenderFrameImpl* RenderFrameImpl::CreateMainFrame(
AgentSchedulingGroup& agent_scheduling_group,
blink::WebView* web_view,
blink::WebFrame* opener,
bool is_for_nested_main_frame,
bool is_for_scalable_page,
blink::mojom::FrameReplicationStatePtr replication_state,
const base::UnguessableToken& devtools_frame_token,
mojom::CreateLocalMainFrameParamsPtr params,
const blink::WebURL& base_url) {
// 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, params->frame_token, params->routing_id,
std::move(params->frame),
std::move(params->associated_interface_provider_remote),
devtools_frame_token, is_for_nested_main_frame);
WebLocalFrame* web_frame = WebLocalFrame::CreateMainFrame(
web_view, render_frame, render_frame->blink_interface_registry_.get(),
std::move(params->interface_broker), params->frame_token,
params->document_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, base_url);
if (!params->is_on_initial_empty_document)
render_frame->frame_->SetIsNotOnInitialEmptyDocument();
CHECK(!params->widget_params->reuse_compositor);
// 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, is_for_scalable_page,
/*hidden=*/true);
web_frame_widget->InitializeCompositing(
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/40387047): 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->SetLoaderFactoryBundle(
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->SetLoaderFactoryBundle(
render_frame->CreateLoaderFactoryBundle(
std::move(params->subresource_loader_factories),
/*subresource_overrides=*/std::nullopt,
/*subresource_proxying_loader_factory=*/mojo::NullRemote(),
/*keep_alive_loader_factory=*/mojo::NullRemote(),
/*fetch_later_loader_factory=*/mojo::NullAssociatedRemote()));
}
return render_frame;
}
// static
void RenderFrameImpl::CreateFrame(
AgentSchedulingGroup& agent_scheduling_group,
const blink::LocalFrameToken& frame_token,
int routing_id,
mojo::PendingAssociatedReceiver<mojom::Frame> frame_receiver,
mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker>
browser_interface_broker,
mojo::PendingAssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
associated_interface_provider,
blink::WebView* web_view,
base::optional_ref<const blink::FrameToken> previous_frame_token,
base::optional_ref<const blink::FrameToken> opener_frame_token,
base::optional_ref<const blink::FrameToken> parent_frame_token,
base::optional_ref<const blink::FrameToken> previous_sibling_frame_token,
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 is_on_initial_empty_document,
const blink::DocumentToken& document_token,
blink::mojom::PolicyContainerPtr policy_container,
bool is_for_nested_main_frame) {
// TODO(danakj): Split this method into two pieces. The first block makes a
// WebLocalFrame and collects the `blink::WebView` and RenderFrame for it. The
// second block uses that to make a RenderWidget, if needed.
RenderFrameImpl* render_frame = nullptr;
blink::WebLocalFrame* web_frame = nullptr;
if (!previous_frame_token) {
// TODO(alexmos): This path is currently used only:
// 1) When recreating a non-main RenderFrame after a crash.
// 2) In tests that issue this IPC directly.
// These two cases should be cleaned up to also pass a previous_frame_token,
// which would allow removing this branch altogether. See
// https://crbug.com/756790.
CHECK(parent_frame_token);
WebFrame* parent_web_frame =
WebFrame::FromFrameToken(parent_frame_token.value());
// If the browser is sending a valid parent routing id, it should already
// be created and registered.
CHECK(parent_web_frame);
CHECK(parent_web_frame->IsWebRemoteFrame());
blink::WebFrame* previous_sibling_web_frame = nullptr;
if (previous_sibling_frame_token) {
previous_sibling_web_frame =
blink::WebFrame::FromFrameToken(previous_sibling_frame_token.value());
}
// `web_view` would only be set by the function caller when creating a
// provisional local main frame for a new WebView, which is handled in the
// other branch of this if clause. Meanwhile, this branch handles subframe
// creation case only, and we should use the parent's WebView in that case.
CHECK(!web_view);
web_view = parent_web_frame->View();
// Create the RenderFrame and WebLocalFrame, linking the two.
render_frame = RenderFrameImpl::Create(
agent_scheduling_group, frame_token, routing_id,
std::move(frame_receiver), std::move(associated_interface_provider),
devtools_frame_token, is_for_nested_main_frame);
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->ToWebRemoteFrame()->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>(),
frame_token, opener, document_token,
std::move(browser_interface_broker),
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 {
WebFrame* previous_web_frame =
WebFrame::FromFrameToken(previous_frame_token.value());
// 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_web_frame)
return;
// This path is creating a local frame. It may or may not be a local root,
// depending on whether the frame's parent is local or remote. It may also
// be the main frame, which will be a provisional frame that can either
// replace a remote main frame in the same WebView or a local main frame in
// a different WebView.
if (web_view) {
// When a `web_view` is set by the caller, it must be for a provisional
// main frame that will do a local frame swap. In this case, the WebView
// must be different from the previous frame's WebView.
CHECK(!previous_web_frame->Parent());
CHECK_NE(web_view, previous_web_frame->View());
} else {
// When not set explicitly, reuse the previous frame's WebView.
web_view = previous_web_frame->View();
}
render_frame = RenderFrameImpl::Create(
agent_scheduling_group, frame_token, routing_id,
std::move(frame_receiver), std::move(associated_interface_provider),
devtools_frame_token, is_for_nested_main_frame);
web_frame = blink::WebLocalFrame::CreateProvisional(
render_frame, render_frame->blink_interface_registry_.get(),
std::move(browser_interface_broker), frame_token, previous_web_frame,
replicated_state->frame_policy,
WebString::FromUTF8(replicated_state->name), web_view);
// 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(web_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_frame_token|
// is not given, which happens in some corner cases.
if (!is_main_frame)
DCHECK(parent_frame_token);
// 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.
// TODO(crbug.com/40387047): Can we merge this `is_main_frame` block with
// RenderFrameImpl::CreateMainFrame()?
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);
render_frame->MaybeInitializeWidget(std::move(widget_params));
// 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.
render_frame->MaybeInitializeWidget(std::move(widget_params));
}
if (!is_on_initial_empty_document) {
render_frame->frame_->SetIsNotOnInitialEmptyDocument();
}
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
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;
}
blink::WebURL RenderFrameImpl::OverrideFlashEmbedWithHTML(
const blink::WebURL& url) {
return GetContentClient()->renderer()->OverrideFlashEmbedWithHTML(url);
}
// RenderFrameImpl::CreateParams --------------------------------------------
RenderFrameImpl::CreateParams::CreateParams(
AgentSchedulingGroup& agent_scheduling_group,
const blink::LocalFrameToken& frame_token,
int32_t routing_id,
mojo::PendingAssociatedReceiver<mojom::Frame> frame_receiver,
mojo::PendingAssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
associated_interface_provider,
const base::UnguessableToken& devtools_frame_token,
bool is_for_nested_main_frame)
: agent_scheduling_group(&agent_scheduling_group),
frame_token(frame_token),
routing_id(routing_id),
frame_receiver(std::move(frame_receiver)),
associated_interface_provider(std::move(associated_interface_provider)),
devtools_frame_token(devtools_frame_token),
is_for_nested_main_frame(is_for_nested_main_frame) {}
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),
frame_token_(params.frame_token),
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
routing_id_(params.routing_id),
#endif
process_label_id_(
tracing::TrackNameRecorder::GetInstance()->GetNewProcessLabelId()),
selection_text_offset_(0),
selection_range_(gfx::Range::InvalidRange()),
render_accessibility_manager_(
std::make_unique<RenderAccessibilityManager>(this)),
weak_wrapper_resource_load_info_notifier_(
std::make_unique<blink::WeakWrapperResourceLoadInfoNotifier>(this)),
#if BUILDFLAG(ENABLE_PPAPI)
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),
is_for_nested_main_frame_(params.is_for_nested_main_frame) {
TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameImpl::RenderFrameImpl",
TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_OUT);
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);
// Save the pending remote for lazy binding in
// `GetRemoteAssociatedInterfaces().
DCHECK(params.associated_interface_provider.is_valid());
pending_associated_interface_provider_remote_ =
std::move(params.associated_interface_provider);
delayed_state_sync_timer_.SetTaskRunner(
agent_scheduling_group_->agent_group_scheduler().DefaultTaskRunner());
// Must call after binding our own remote interfaces.
media_factory_.SetupMojo();
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
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.";
#endif
}
mojom::FrameHost* RenderFrameImpl::GetFrameHost() {
if (!frame_host_remote_.is_bound())
GetRemoteAssociatedInterfaces()->GetInterface(&frame_host_remote_);
return frame_host_remote_.get();
}
RenderFrameImpl::~RenderFrameImpl() {
TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameImpl::~RenderFrameImpl",
TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN);
for (auto& observer : observers_)
observer.OnDestruct();
for (auto& observer : observers_)
observer.RenderFrameGone();
web_media_stream_device_observer_.reset();
if (initialized_ && is_main_frame_)
MainFrameCounter::DecrementCount();
tracing::TrackNameRecorder::GetInstance()->RemoveProcessLabel(
process_label_id_);
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
g_routing_id_frame_map.Get().erase(routing_id_);
#endif
agent_scheduling_group_->RemoveFrameRoute(frame_token_
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
,
routing_id_
#endif
);
}
void RenderFrameImpl::Initialize(blink::WebFrame* parent) {
initialized_ = true;
is_main_frame_ = !parent;
if (is_main_frame_)
MainFrameCounter::IncrementCount();
TRACE_EVENT1("navigation,rail", "RenderFrameImpl::Initialize", "frame_token",
frame_token_);
#if BUILDFLAG(ENABLE_PPAPI)
new PepperBrowserConnection(this);
#endif
RegisterMojoInterfaces();
{
TRACE_EVENT("navigation", "ContentRendererClient::RenderFrameCreated");
// 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::AudioOutputIPCFactory::io_task_runner_ may be null in tests.
auto& factory = blink::AudioOutputIPCFactory::GetInstance();
if (factory.io_task_runner()) {
factory.RegisterRemoteFactory(GetWebFrame()->GetLocalFrameToken(),
GetBrowserInterfaceBroker());
}
// 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(
frame_token_,
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
routing_id_,
#endif
this, GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
}
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_PPAPI)
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_PPAPI)
void RenderFrameImpl::ScriptedPrint() {
bool user_initiated = GetLocalRootWebFrameWidget()->HandlingInputEvent();
for (auto& observer : observers_)
observer.ScriptedPrint(user_initiated);
}
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
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;
}
#endif
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::SetUpSharedMemoryForUkms(
base::ReadOnlySharedMemoryRegion smoothness_memory,
base::ReadOnlySharedMemoryRegion dropped_frames_memory) {
TRACE_EVENT_WITH_FLOW0("navigation",
"RenderFrameImpl::SetUpSharedMemoryForUkms",
TRACE_ID_LOCAL(this),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DCHECK(smoothness_memory.IsValid() && dropped_frames_memory.IsValid());
for (auto& observer : observers_) {
DCHECK(smoothness_memory.IsValid() && dropped_frames_memory.IsValid());
if (observer.SetUpUkmReporting(smoothness_memory, dropped_frames_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) {
// If the provisional owner frame is a local frame and it has a non-null
// `navigation_client_impl_`, it began the navigation. Some properties need
// to be copied over from the original `NavigationClient` to preserve
// behaviour.
NavigationClient* initiator_navigation_client = nullptr;
WebFrame* initiator_frame = GetWebFrame()->GetProvisionalOwnerFrame();
if (initiator_frame && initiator_frame->IsWebLocalFrame()) {
initiator_navigation_client = RenderFrameImpl::FromWebFrame(initiator_frame)
->navigation_client_impl_.get();
}
navigation_client_impl_ =
std::make_unique<NavigationClient>(this, initiator_navigation_client);
navigation_client_impl_->Bind(std::move(receiver));
}
void RenderFrameImpl::BindNavigationClientWithParams(
mojo::PendingAssociatedReceiver<mojom::NavigationClient> receiver,
blink::mojom::BeginNavigationParamsPtr begin_params,
blink::mojom::CommonNavigationParamsPtr common_params,
bool is_duplicate_navigation) {
if (navigation_client_impl_) {
navigation_client_impl_->ResetForNewNavigation(is_duplicate_navigation);
}
navigation_client_impl_ = std::make_unique<NavigationClient>(
this, std::move(begin_params), std::move(common_params));
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(
bool is_loading,
blink::mojom::FrameReplicationStatePtr replicated_frame_state,
const blink::RemoteFrameToken& proxy_frame_token,
blink::mojom::RemoteFrameInterfacesFromBrowserPtr remote_frame_interfaces,
blink::mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces,
const std::optional<base::UnguessableToken>& devtools_frame_token) {
TRACE_EVENT1("navigation,rail", "RenderFrameImpl::UnloadFrame", "frame_token",
frame_token_);
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 acknowledgement
// 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::kInternalPostMessageForwarding);
// Important: |this| is deleted after this call!
if (!SwapOutAndDeleteThis(
is_loading, std::move(replicated_frame_state), proxy_frame_token,
std::move(remote_frame_interfaces),
std::move(remote_main_frame_interfaces), devtools_frame_token)) {
// 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));
});
base::ScopedUmaHistogramTimer histogram_timer("Navigation.RenderFrameDelete");
// 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 `blink::WebView` 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 `blink::WebView` 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 !BUILDFLAG(IS_ANDROID)
CHECK(!in_frame_tree_);
#else
// Previously this CHECK() was disabled on Android because it was much
// easier to hit the race there.
CHECK(!in_frame_tree_, base::NotFatalUntil::M135);
#endif // !BUILDFLAG(IS_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(
bool is_loading,
blink::mojom::FrameReplicationStatePtr replicated_frame_state,
const blink::RemoteFrameToken& proxy_frame_token,
blink::mojom::RemoteFrameInterfacesFromBrowserPtr remote_frame_interfaces,
blink::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(is_loading, std::move(replicated_frame_state),
proxy_frame_token, std::move(remote_frame_interfaces),
std::move(remote_main_frame_interfaces),
/*devtools_frame_token=*/std::nullopt);
}
void RenderFrameImpl::SnapshotAccessibilityTree(
mojom::SnapshotAccessibilityTreeParamsPtr params,
SnapshotAccessibilityTreeCallback callback) {
ui::AXTreeUpdate response;
AXTreeSnapshotterImpl snapshotter(this, ui::AXMode(params->ax_mode));
snapshotter.Snapshot(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;
GetAgentGroupScheduler().Isolate()->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_PPAPI)
// 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 (PepperPluginInstanceImpl* plugin : plugins) {
if (base::Contains(active_pepper_instances_, plugin)) {
plugin->ViewInitiatedPaint();
}
}
#endif
}
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);
}
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
int RenderFrameImpl::GetRoutingID() {
return routing_id_;
}
#endif
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_PPAPI)
std::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)
LOG(WARNING) << "Pepper module/plugin creation failed.";
#endif
#endif // BUILDFLAG(ENABLE_PPAPI)
return nullptr;
}
void RenderFrameImpl::ExecuteJavaScript(const std::u16string& javascript) {
v8::HandleScope handle_scope(GetAgentGroupScheduler().Isolate());
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_) {
DCHECK(pending_associated_interface_provider_remote_);
remote_associated_interfaces_ =
std::make_unique<blink::AssociatedInterfaceProvider>(
std::move(pending_associated_interface_provider_remote_),
GetTaskRunner(blink::TaskType::kInternalNavigationAssociated));
}
return remote_associated_interfaces_.get();
}
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 */);
}
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 BUILDFLAG(IS_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 url::SchemeHostPort& final_response_url,
network::mojom::URLResponseHeadPtr response_head,
network::mojom::RequestDestination request_destination,
bool is_ad_resource) {
if (!blink::IsRequestDestinationFrame(request_destination)) {
bool notify = ShouldNotifySubresourceResponseStarted(
GetWebView()->GetRendererPreferences());
UMA_HISTOGRAM_BOOLEAN(
"Renderer.ReduceSubresourceResponseIPC.DidNotifyBrowser", notify);
if (notify) {
GetFrameHost()->SubresourceResponseStarted(final_response_url,
response_head->cert_status);
}
}
DidStartResponse(final_response_url, request_id, std::move(response_head),
request_destination, is_ad_resource);
}
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::AllowBindings(int64_t enabled_bindings_flags) {
auto new_bindings =
BindingsPolicySet::FromEnumBitmask(enabled_bindings_flags);
enabled_bindings_.PutAll(new_bindings);
if (new_bindings.Has(BindingsPolicyValue::kMojoWebUi)) {
// If mojo web UI is being enabled, update the protected memory bool to
// allow MojoJS binding in this process.
blink::WebV8Features::AllowMojoJSForProcess();
}
}
void RenderFrameImpl::EnableMojoJsBindings(
content::mojom::ExtraMojoJsFeaturesPtr features) {
enable_mojo_js_bindings_ = true;
mojo_js_features_ = std::move(features);
// Update the protected memory bool to allow MojoJS binding in this process.
blink::WebV8Features::AllowMojoJSForProcess();
}
void RenderFrameImpl::EnableMojoJsBindingsWithBroker(
mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker> broker) {
mojo_js_interface_broker_ = std::move(broker);
// Update the protected memory bool to allow MojoJS binding in this process.
blink::WebV8Features::AllowMojoJSForProcess();
}
void RenderFrameImpl::BindWebUI(
mojo::PendingAssociatedReceiver<mojom::WebUI> receiver,
mojo::PendingAssociatedRemote<mojom::WebUIHost> remote) {
DCHECK(enabled_bindings_.Has(BindingsPolicyValue::kWebUi));
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;
WebLocalFrame* old_main_web_frame = WebLocalFrame::FromFrameToken(
old_page_info->frame_token_for_old_main_frame);
if (!old_main_web_frame) {
// Even if we sent a valid `frame_token_for_old_main_frame`, it might have
// already been destroyed by the time we try to get the WebLocalFrame, so
// we should check if it still exists.
return;
}
RenderFrameImpl* old_main_render_frame =
RenderFrameImpl::FromWebFrame(old_main_web_frame);
if (!old_main_render_frame) {
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_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 ==
blink::mojom::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);
return;
}
DCHECK_EQ(old_page_info->new_lifecycle_state_for_old_page->visibility,
blink::mojom::PageVisibilityState::kHidden);
DCHECK_NE(old_page_info->new_lifecycle_state_for_old_page->pagehide_dispatch,
blink::mojom::PagehideDispatch::kNotDispatched);
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,
std::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>
subresource_proxying_loader_factory,
mojo::PendingRemote<network::mojom::URLLoaderFactory>
keep_alive_loader_factory,
mojo::PendingAssociatedRemote<blink::mojom::FetchLaterLoaderFactory>
fetch_later_loader_factory,
const blink::DocumentToken& document_token,
const base::UnguessableToken& devtools_navigation_token,
const base::Uuid& base_auction_nonce,
const std::optional<network::ParsedPermissionsPolicy>& permissions_policy,
blink::mojom::PolicyContainerPtr policy_container,
mojo::PendingRemote<blink::mojom::CodeCacheHost> code_cache_host,
mojo::PendingRemote<blink::mojom::CodeCacheHost>
code_cache_host_for_background,
mojom::CookieManagerInfoPtr cookie_manager_info,
mojom::StorageInfoPtr storage_info,
mojom::NavigationClient::CommitNavigationCallback commit_callback) {
base::ElapsedTimer timer;
base::ScopedUmaHistogramTimer histogram_timer(kCommitRenderFrame);
base::ScopedUmaHistogramTimer histogram_timer_frame(base::StrCat(
{kCommitRenderFrame, IsMainFrame() ? ".MainFrame" : ".Subframe"}));
TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameImpl::CommitNavigation",
TRACE_ID_LOCAL(this),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DCHECK(navigation_client_impl_);
DCHECK(!blink::IsRendererDebugURL(common_params->url));
DCHECK(!NavigationTypeUtils::IsSameDocument(common_params->navigation_type));
// `origin_to_commit` must only be set on failed navigations or data: URL
// navigations, except when kUseBrowserCalculatedOrigin is enabled.
CHECK(!commit_params->origin_to_commit ||
common_params->url.SchemeIs(url::kDataScheme) ||
base::FeatureList::IsEnabled(features::kUseBrowserCalculatedOrigin));
LogCommitHistograms(commit_params->commit_sent, is_main_frame_,
common_params->url);
bool is_new_navigation_in_outermost_main_frame_with_http_or_https = false;
if (frame_->IsOutermostMainFrame() &&
common_params->url.SchemeIsHTTPOrHTTPS()) {
switch (common_params->navigation_type) {
case blink::mojom::NavigationType::DIFFERENT_DOCUMENT:
is_new_navigation_in_outermost_main_frame_with_http_or_https = true;
break;
case blink::mojom::NavigationType::RELOAD:
case blink::mojom::NavigationType::RELOAD_BYPASSING_CACHE:
case blink::mojom::NavigationType::RESTORE:
case blink::mojom::NavigationType::RESTORE_WITH_POST:
case blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT:
case blink::mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT:
case blink::mojom::NavigationType::SAME_DOCUMENT:
break;
}
}
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) ||
subresource_loader_factories);
int request_id = blink::GenerateRequestId();
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>(
document_token, devtools_navigation_token, base_auction_nonce);
navigation_params->navigation_delivery_type =
commit_params->navigation_delivery_type;
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));
navigation_params->view_transition_state =
std::move(commit_params->view_transition_state);
if (frame_->IsOutermostMainFrame() && permissions_policy) {
navigation_params->permissions_policy_override = permissions_policy;
}
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(subresource_proxying_loader_factory),
std::move(keep_alive_loader_factory),
std::move(fetch_later_loader_factory), std::move(code_cache_host),
std::move(code_cache_host_for_background), std::move(cookie_manager_info),
std::move(storage_info), std::move(document_state));
// Handle a navigation that has a non-empty `data_url_as_string`, or perform
// a "loadDataWithBaseURL" navigation, which 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 actual data: URL will be saved in the document's DocumentState to
// later be returned as the `url` in DidCommitProvisionalLoadParams.
bool should_handle_data_url_as_string = false;
#if BUILDFLAG(IS_ANDROID)
should_handle_data_url_as_string |=
is_main_frame_ && !commit_params->data_url_as_string.empty();
#endif
if (should_handle_data_url_as_string ||
commit_params->is_load_data_with_base_url) {
std::string mime_type, charset, data;
// `base_url` will be set to `base_url_for_data_url` from `common_params`,
// unless it's empty (the `data_url_as_string` handling case), 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);
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)) {
NOTREACHED() << "Invalid URL passed: "
<< common_params->url.possibly_invalid_spec();
}
WebNavigationParams::FillStaticResponse(navigation_params.get(),
WebString::FromUTF8(mime_type),
WebString::FromUTF8(charset), data);
} else {
blink::WebNavigationBodyLoader::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),
CreateResourceLoadInfoNotifierWrapper(), !frame_->Parent(),
navigation_params.get(), frame_->IsAdFrame());
}
// 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::EqualsCaseInsensitiveASCII(
response_head->mime_type, "multipart/related") ||
base::EqualsCaseInsensitiveASCII(
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));
if (is_new_navigation_in_outermost_main_frame_with_http_or_https) {
base::UmaHistogramTimes(
base::StrCat({kCommitRenderFrame,
".OutermostMainFrame.NewNavigation.IsHTTPOrHTTPS"}),
timer.Elapsed());
}
}
void RenderFrameImpl::CommitNavigationWithParams(
blink::mojom::CommonNavigationParamsPtr common_params,
blink::mojom::CommitNavigationParamsPtr commit_params,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
subresource_loader_factories,
std::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>
subresource_proxying_loader_factory,
mojo::PendingRemote<network::mojom::URLLoaderFactory>
keep_alive_loader_factory,
mojo::PendingAssociatedRemote<blink::mojom::FetchLaterLoaderFactory>
fetch_later_loader_factory,
mojo::PendingRemote<blink::mojom::CodeCacheHost> code_cache_host,
mojo::PendingRemote<blink::mojom::CodeCacheHost>
code_cache_host_for_background,
mojom::CookieManagerInfoPtr cookie_manager_info,
mojom::StorageInfoPtr storage_info,
std::unique_ptr<DocumentState> document_state,
std::unique_ptr<WebNavigationParams> navigation_params) {
TRACE_EVENT_WITH_FLOW0("navigation",
"RenderFrameImpl::CommitNavigationWithParams",
TRACE_ID_LOCAL(this),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
base::ElapsedTimer timer;
if (common_params->url.IsAboutSrcdoc()) {
WebNavigationParams::FillStaticResponse(navigation_params.get(),
"text/html", "UTF-8",
commit_params->srcdoc_value);
}
scoped_refptr<blink::ChildURLLoaderFactoryBundle> new_loader_factories =
CreateLoaderFactoryBundle(std::move(subresource_loader_factories),
std::move(subresource_overrides),
std::move(subresource_proxying_loader_factory),
std::move(keep_alive_loader_factory),
std::move(fetch_later_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);
if (frame_->IsOutermostMainFrame()) {
// Save the Back/Forward Cache NotRestoredReasons struct to WebLocalFrame to
// report for PerformanceNavigationTiming API.
frame_->SetNotRestoredReasons(
std::move(commit_params->not_restored_reasons));
} else {
// NotRestoredReasons are only set for the outermost main frame.
CHECK(!commit_params->not_restored_reasons);
}
// |lcpp_hint| is set only when the frame is eligible (e.g. it's an outer
// most main frame), which is checked in the browser process. Otherwise
// nullptr.
//
// When there's a pre-existing LCPP hint on frame, we want to remove the
// existing hint. Hence calling SetLCPPHint() is always required.
CHECK(!commit_params->lcpp_hint || frame_->IsOutermostMainFrame());
frame_->SetLCPPHint(std::move(commit_params->lcpp_hint));
// 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);
WebHistoryItem item_for_history_navigation;
blink::mojom::CommitResult commit_status = blink::mojom::CommitResult::Ok;
if (load_type == WebFrameLoadType::kBackForward ||
load_type == WebFrameLoadType::kRestore) {
// 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;
navigation_params->load_with_storage_access =
commit_params->load_with_storage_access;
navigation_params->visited_link_salt = commit_params->visited_link_salt;
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->Clone()));
}
DCHECK(!pending_loader_factories_);
pending_loader_factories_ = std::move(new_loader_factories);
pending_code_cache_host_ = std::move(code_cache_host);
pending_code_cache_host_for_background_ =
std::move(code_cache_host_for_background);
pending_cookie_manager_info_ = std::move(cookie_manager_info);
pending_storage_info_ = std::move(storage_info);
original_storage_key_ = navigation_params->storage_key;
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;
if (commit_params->local_surface_id) {
CHECK(frame_->FrameWidget())
<< "Only local roots should get a SurfaceID update";
frame_->FrameWidget()->ApplyLocalSurfaceIdUpdate(
*commit_params->local_surface_id);
}
if (load_type == WebFrameLoadType::kStandard &&
common_params->url.SchemeIsHTTPOrHTTPS()) {
base::UmaHistogramMicrosecondsTimes(
"Navigation.CommitNavigationWithParams.Time.IsStandardLoadType."
"IsHTTPOrHTTPS",
timer.Elapsed());
}
ResetMembersUsedForDurationOfCommit();
}
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 std::optional<std::string>& error_page_content,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
subresource_loader_factories,
const blink::DocumentToken& document_token,
blink::mojom::PolicyContainerPtr policy_container,
mojom::AlternativeErrorPageOverrideInfoPtr alternative_error_page_info,
mojom::NavigationClient::CommitFailedNavigationCallback callback) {
TRACE_EVENT1("navigation,benchmark,rail",
"RenderFrameImpl::CommitFailedNavigation", "frame_token",
frame_token_);
DCHECK(navigation_client_impl_);
DCHECK(!NavigationTypeUtils::IsSameDocument(common_params->navigation_type));
// `origin_to_commit` must be set on failed navigations.
CHECK(commit_params->origin_to_commit);
// The browser process should not send us an initiator_base_url in a failed
// navigation.
DCHECK(!common_params->initiator_base_url);
AssertNavigationCommits assert_navigation_commits(
this, kMayReplaceInitialEmptyDocument);
GetWebView()->SetHistoryListFromNavigation(
commit_params->current_history_list_index,
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),
std::nullopt /* subresource_overrides */,
mojo::NullRemote() /* subresource_proxying_loader_factory */,
mojo::NullRemote() /* keep_alive_loader_factory */,
mojo::NullAssociatedRemote() /* fetch_later_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);
// Since the URL will be set to kUnreachableWebDataURL, use default content
// settings.
commit_params->content_settings =
blink::CreateDefaultRendererContentSettings();
auto navigation_params = std::make_unique<WebNavigationParams>(
document_token,
/*devtools_navigation_token=*/base::UnguessableToken::Create(),
/*base_auction_nonce=*/base::Uuid::GenerateRandomV4());
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();
}
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,
std::move(alternative_error_page_info), 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(),
std::move(alternative_error_page_info), error_html_ptr);
}
// Make sure we never show errors in view source mode.
frame_->EnableViewSourceMode(false);
auto page_state =
blink::PageState::CreateFromEncodedData(commit_params->page_state);
if (page_state.IsValid())
navigation_params->history_item = WebHistoryItem(page_state);
if (!navigation_params->history_item.IsNull()) {
if (common_params->navigation_type ==
blink::mojom::NavigationType::RESTORE ||
common_params->navigation_type ==
blink::mojom::NavigationType::RESTORE_WITH_POST) {
navigation_params->frame_load_type = WebFrameLoadType::kRestore;
} else {
navigation_params->frame_load_type = WebFrameLoadType::kBackForward;
}
} else if (common_params->should_replace_current_entry) {
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(crbug.com/40150370): 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));
navigation_params->view_transition_state =
std::move(commit_params->view_transition_state);
// 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::GenerateRequestId(),
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;
ResetMembersUsedForDurationOfCommit();
}
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);
DocumentState* document_state =
DocumentState::FromDocumentLoader(frame_->GetDocumentLoader());
document_state->set_navigation_state(
NavigationState::CreateForSameDocumentCommitFromBrowser(
std::move(common_params), std::move(commit_params),
std::move(callback)));
NavigationState* navigation_state = document_state->navigation_state();
blink::mojom::CommitResult commit_status = blink::mojom::CommitResult::Ok;
WebHistoryItem item_for_history_navigation;
if (navigation_state->common_params().navigation_type ==
blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT) {
DCHECK(blink::PageState::CreateFromEncodedData(
navigation_state->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, navigation_state->commit_params().nav_entry_id);
DCHECK(!navigation_state->common_params()
.is_history_navigation_in_new_child_frame);
commit_status = PrepareForHistoryNavigationCommit(
navigation_state->common_params(), navigation_state->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 =
!navigation_state->common_params().base_url_for_data_url.is_empty();
#if BUILDFLAG(IS_ANDROID)
use_base_url_for_data_url |=
!navigation_state->commit_params().data_url_as_string.empty();
#endif
GURL url;
if (is_main_frame_ && use_base_url_for_data_url) {
url = navigation_state->common_params().base_url_for_data_url;
} else {
url = navigation_state->common_params().url;
}
bool is_client_redirect = !!(navigation_state->common_params().transition &
ui::PAGE_TRANSITION_CLIENT_REDIRECT);
bool started_with_transient_activation =
navigation_state->common_params().has_user_gesture;
bool is_browser_initiated =
navigation_state->commit_params().is_browser_initiated;
bool has_ua_visual_transition =
navigation_state->commit_params().has_ua_visual_transition;
std::optional<blink::scheduler::TaskAttributionId>
soft_navigation_heuristics_task_id =
navigation_state->commit_params()
.soft_navigation_heuristics_task_id;
WebSecurityOrigin initiator_origin;
if (navigation_state->common_params().initiator_origin) {
initiator_origin =
navigation_state->common_params().initiator_origin.value();
}
bool should_skip_screenshot =
navigation_state->commit_params().should_skip_screenshot;
// Load the request.
commit_status = frame_->CommitSameDocumentNavigation(
url, load_type, item_for_history_navigation, is_client_redirect,
started_with_transient_activation, initiator_origin,
is_browser_initiated, has_ua_visual_transition,
soft_navigation_heuristics_task_id, should_skip_screenshot);
// If `commit_status` is Ok, RunCommitSameDocumentNavigationCallback() was
// called in DidCommitNavigationInternal() or the NavigationApi deferred the
// commit and will call DidCommitNavigationInternal() when the commit is
// undeferred. Either way, no further work is needed here.
if (commit_status == blink::mojom::CommitResult::Ok) {
return;
}
// The load of the URL 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.
// Similarly, check whether `navigation_state` is still the state associated
// with the WebDocumentLoader. It may have been preempted by a navigation
// started by an event handler.
if (!weak_this || document_state->navigation_state() != navigation_state) {
return;
}
}
DCHECK_NE(commit_status, blink::mojom::CommitResult::Ok);
navigation_state->RunCommitSameDocumentNavigationCallback(commit_status);
document_state->clear_navigation_state();
// The browser expects the frame to be loading this navigation. Inform it
// that the load stopped if needed.
if (frame_ && !frame_->IsLoading()) {
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 {
// RFHI::recreate_default_url_loader_factory_after_network_service_crash_ is
// only set for frames that directly get their factory bundle from the
// browser (HostChildURLLoaderFactoryBundle) rather than inheriting the
// bundle from another frame (TrackedChildURLLoaderFactoryBundle).
// Therefore the default factory *may* be present in the `if`/true branch
// and *must* be missing in the `else` branch.
DCHECK(!subresource_loader_factories->pending_default_factory().is_valid());
// If there is no `pending_default_factory` (see the DCHECK above) and no
// empty-payload IPCs are sent, then the only way to get here should be
// because of `pending_isolated_world_factories`.
//
// TODO(crbug.com/40158699): Remove the whole `else` branch once
// Chrome Platform Apps and `pending_isolated_world_factories` are gone.
DCHECK(!subresource_loader_factories->pending_isolated_world_factories()
.empty());
// `!IsHostChildURLLoaderFactoryBundle` should only happen if the frame
// hosts a document that isn't related to a real navigation (i.e. if an
// initial empty document should "inherit" the factories from its
// opener/parent).
DCHECK_EQ(NavigationCommitState::kInitialEmptyDocument,
navigation_commit_state_);
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());
}
// Resetting `background_resource_fetch_context_` here so it will be recreated
// when MaybeGetBackgroundResourceFetchAssets() is called.
background_resource_fetch_context_.reset();
}
// content::RenderFrame implementation
// ----------------------------------------
const blink::BrowserInterfaceBrokerProxy&
RenderFrameImpl::GetBrowserInterfaceBroker() {
return frame_->GetBrowserInterfaceBroker();
}
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
}
bool RenderFrameImpl::IsDomStorageDisabled() const {
return GetContentClient()->renderer()->IsDomStorageDisabled();
}
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::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_PPAPI)
WebPluginInfo info;
std::string mime_type;
bool found = false;
GetPepperHost()->GetPluginInfo(params.url, 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_PPAPI)
}
std::unique_ptr<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,
scoped_refptr<base::TaskRunner> compositor_worker_task_runner) {
// `settings` should be non-null since the WebView created for
// RenderFrameImpl always composites.
DCHECK(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::move(compositor_worker_task_runner));
}
std::unique_ptr<blink::WebContentSettingsClient>
RenderFrameImpl::CreateWorkerContentSettingsClient() {
if (!frame_ || !frame_->View())
return nullptr;
return GetContentClient()->renderer()->CreateWorkerContentSettingsClient(
this);
}
#if !BUILDFLAG(IS_ANDROID)
std::unique_ptr<media::SpeechRecognitionClient>
RenderFrameImpl::CreateSpeechRecognitionClient() {
if (!frame_ || !frame_->View())
return nullptr;
return GetContentClient()->renderer()->CreateSpeechRecognitionClient(this);
}
#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();
std::vector<blink::WebString> web_cors_exempt_header_list(
cors_exempt_header_list.size());
std::ranges::transform(
cors_exempt_header_list, web_cors_exempt_header_list.begin(),
[](const auto& header) { return blink::WebString::FromLatin1(header); });
// |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()->Clone(),
/*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->SetAncestorFrameToken(
frame_->GetLocalFrameToken());
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->SetAncestorFrameToken(
frame_->GetLocalFrameToken());
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(),
GetTaskRunner(blink::TaskType::kNetworking));
}
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();
}
namespace {
// Emit the trace event using a helper as we:
// a) want to ensure that the trace event covers the entire function.
// b) we want to emit the new child routing id as an argument.
// c) child routing id becomes available only after a sync call.
struct CreateChildFrameTraceEvent {
explicit CreateChildFrameTraceEvent(
const blink::LocalFrameToken& frame_token) {
TRACE_EVENT_BEGIN("navigation,rail", "RenderFrameImpl::createChildFrame",
"frame_token", frame_token);
}
~CreateChildFrameTraceEvent() {
TRACE_EVENT_END("navigation,rail", "child_frame_token", child_frame_token);
}
blink::LocalFrameToken child_frame_token;
};
} // namespace
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::FrameOwnerElementType frame_owner_element_type,
blink::WebPolicyContainerBindParams policy_container_bind_params,
ukm::SourceId document_ukm_source_id,
FinishChildFrameCreationFn finish_creation) {
// Tracing analysis uses this to find main frames when this value is
// MSG_ROUTING_NONE, and build the frame tree otherwise.
CreateChildFrameTraceEvent trace_event(frame_token_);
// Allocate child routing ID. This is a synchronous call.
int child_routing_id;
blink::LocalFrameToken frame_token;
base::UnguessableToken devtools_frame_token;
blink::DocumentToken document_token;
if (!RenderThread::Get()->GenerateFrameRoutingID(
child_routing_id, frame_token, devtools_frame_token,
document_token)) {
return nullptr;
}
trace_event.child_frame_token = frame_token;
// 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 browsing 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 = GetAgentGroupScheduler().Isolate()->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;
mojo::PendingAssociatedRemote<blink::mojom::AssociatedInterfaceProvider>
associated_interface_provider;
// Now create the child frame in the browser via an asynchronous call.
GetFrameHost()->CreateChildFrame(
frame_token, pending_frame_receiver.InitWithNewEndpointAndPassRemote(),
browser_interface_broker.InitWithNewPipeAndPassReceiver(),
blink::mojom::PolicyContainerBindParams::New(
std::move(policy_container_bind_params.receiver)),
associated_interface_provider.InitWithNewEndpointAndPassReceiver(), scope,
name.Utf8(), frame_unique_name, is_created_by_script, frame_policy,
blink::mojom::FrameOwnerProperties::From(frame_owner_properties),
frame_owner_element_type, document_ukm_source_id);
// Create the RenderFrame and WebLocalFrame, linking the two.
RenderFrameImpl* child_render_frame = RenderFrameImpl::Create(
*agent_scheduling_group_, frame_token, child_routing_id,
std::move(pending_frame_receiver),
std::move(associated_interface_provider), devtools_frame_token,
/*is_for_nested_main_frame=*/false);
child_render_frame->SetLoaderFactoryBundle(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();
blink::WebLocalFrame* web_frame = frame_->CreateLocalChild(
scope, child_render_frame,
child_render_frame->blink_interface_registry_.get(), frame_token);
finish_creation(web_frame, document_token,
std::move(browser_interface_broker));
child_render_frame->in_frame_tree_ = true;
child_render_frame->Initialize(/*parent=*/GetWebFrame());
return web_frame;
}
void RenderFrameImpl::DidCreateFencedFrame(
const blink::RemoteFrameToken& frame_token) {
for (auto& observer : observers_)
observer.DidCreateFencedFrame(frame_token);
}
blink::WebFrame* RenderFrameImpl::FindFrame(const blink::WebString& name) {
if (GetBlinkPreferences().renderer_wide_named_frame_lookup) {
for (const auto& it : g_frame_map.Get()) {
WebLocalFrame* frame = it.second->GetWebFrame();
if (frame->AssignedName() == name)
return frame;
}
}
return GetContentClient()->renderer()->FindFrame(this->GetWebFrame(),
name.Utf8());
}
void RenderFrameImpl::MaybeInitializeWidget(
mojom::CreateFrameWidgetParamsPtr widget_params) {
if (widget_params->reuse_compositor) {
// Initializing the widget is deferred until commit if this RenderFrame
// will be replacing a previous RenderFrame. This enables reuse of the
// compositing setup which is expensive.
// This step must be deferred until commit since this RenderFrame could be
// speculative and the previous RenderFrame will continue to be visible
// and animating until commit.
//
// TODO(khushalsagar): Ideal would be to move the widget initialization to
// the commit stage for all cases. This shouldn't have any perf impact
// since the expensive parts of compositing (setting up a connection to
// the GPU process) is not done until the frame is made visible, which
// happens at commit.
widget_params_for_lazy_widget_creation_ = std::move(widget_params);
return;
}
InitializeFrameWidgetForFrame(*frame_, /*previous_widget=*/nullptr,
std::move(widget_params),
is_for_nested_main_frame_);
}
void RenderFrameImpl::InitializeWidgetAtSwap(
blink::WebLocalFrame& frame_for_compositor_reuse) {
CHECK(widget_params_for_lazy_widget_creation_);
DCHECK(widget_params_for_lazy_widget_creation_->reuse_compositor);
InitializeFrameWidgetForFrame(
*frame_, frame_for_compositor_reuse.FrameWidget(),
std::move(widget_params_for_lazy_widget_creation_),
is_for_nested_main_frame_);
}
void RenderFrameImpl::WillDetach(blink::DetachReason detach_reason) {
if (detach_reason == blink::DetachReason::kNavigation) {
if (navigation_client_impl_ &&
ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
navigation_client_impl_->ResetWithoutCancelling();
}
// If `provisional_frame_for_local_root_swap_` is set by `Swap()`, the
// widget for the frame being swapped in needs to be initialized now. At
// this point, the compositor can be passed off safely since the `Document`
// has already been torn down.
//
// TODO(dcheng): This mechanism could probably be used for passing off the
// unique name as well...
if (provisional_frame_for_local_root_swap_) {
CHECK_EQ(
provisional_frame_for_local_root_swap_->is_for_nested_main_frame_,
is_for_nested_main_frame_);
provisional_frame_for_local_root_swap_->InitializeWidgetAtSwap(
CHECK_DEREF(GetWebFrame()));
provisional_frame_for_local_root_swap_ = nullptr;
}
}
for (auto& observer : observers_)
observer.WillDetach(detach_reason);
// blink::AudioOutputIPCFactory::io_task_runner_ may be null in tests.
auto& factory = blink::AudioOutputIPCFactory::GetInstance();
if (factory.io_task_runner())
factory.MaybeDeregisterRemoteFactory(GetWebFrame()->GetLocalFrameToken());
// Send a state update before the frame is detached.
SendUpdateState();
}
void RenderFrameImpl::FrameDetached(blink::DetachReason detach_reason) {
TRACE_EVENT0("navigation", "RenderFrameImpl::FrameDetached");
base::ScopedUmaHistogramTimer histogram_timer(
"Navigation.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(detach_reason);
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 std::vector<blink::WebString>& newly_matching_selectors,
const std::vector<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, message.level);
}
}
}
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());
}
// Set the code cache host earlier to allow fetching the code cache as soon as
// possible.
document_loader->SetCodeCacheHost(
std::move(pending_code_cache_host_),
std::move(pending_code_cache_host_for_background_));
}
void RenderFrameImpl::DidCommitNavigation(
blink::WebHistoryCommitType commit_type,
bool should_reset_browser_interface_broker,
const network::ParsedPermissionsPolicy& permissions_policy_header,
const blink::DocumentPolicyFeatureState& document_policy_header) {
TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameImpl::DidCommitNavigation",
TRACE_ID_LOCAL(this),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
CHECK_EQ(NavigationCommitState::kWillCommit, navigation_commit_state_);
navigation_commit_state_ = NavigationCommitState::kDidCommit;
WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
DocumentState* document_state =
DocumentState::FromDocumentLoader(document_loader);
NavigationState* navigation_state = document_state->navigation_state();
DCHECK(!navigation_state->WasWithinSameDocument());
TRACE_EVENT2("navigation,benchmark,rail",
"RenderFrameImpl::didStartProvisionalLoad", "frame_token",
frame_token_, "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_|.
SetLoaderFactoryBundle(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(), std::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",
"frame_token", frame_token_, "url",
GetLoadingUrl().possibly_invalid_spec());
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kWaitForDebuggerOnNavigation)) {
std::string renderer =
base::StrCat({"Renderer url=\"",
TrimURL(GetLoadingUrl().possibly_invalid_spec()), "\""});
content::WaitForDebugger(renderer);
}
// 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 =
frame_->GetBrowserInterfaceBroker().Reset(
agent_scheduling_group_->agent_group_scheduler()
.DefaultTaskRunner());
// blink::AudioOutputIPCFactory::io_task_runner_ may be null in tests.
auto& factory = blink::AudioOutputIPCFactory::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(crbug.com/40495144): 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(),
frame_->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(),
GetWebView()->IsFencedFrameRoot());
// TODO(crbug.com/40092527): Turn this into a DCHECK for origin equality when
// the linked bug is fixed. Currently sometimes the browser and renderer
// disagree on the origin during commit navigation.
if (pending_cookie_manager_info_ &&
pending_cookie_manager_info_->origin ==
url::Origin(frame_->GetDocument().GetSecurityOrigin())) {
frame_->GetDocument().SetCookieManager(
std::move(pending_cookie_manager_info_->cookie_manager));
}
// TODO(crbug.com/40092527): Turn this into a DCHECK for origin equality when
// the linked bug is fixed. Currently sometimes the browser and renderer
// disagree on the origin during commit navigation.
if (pending_storage_info_ &&
original_storage_key_.origin() ==
url::Origin(frame_->GetDocument().GetSecurityOrigin())) {
if (pending_storage_info_->local_storage_area) {
frame_->SetLocalStorageArea(
std::move(pending_storage_info_->local_storage_area));
}
if (pending_storage_info_->session_storage_area) {
frame_->SetSessionStorageArea(
std::move(pending_storage_info_->session_storage_area));
}
}
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);
document_state->clear_navigation_state();
ResetMembersUsedForDurationOfCommit();
}
void RenderFrameImpl::DidCommitDocumentReplacementNavigation(
blink::WebDocumentLoader* document_loader) {
DocumentState::FromDocumentLoader(document_loader)
->set_navigation_state(NavigationState::CreateForSynchronousCommit());
// TODO(crbug.com/40581836): figure out which of the following observer
// calls are necessary, if any.
for (auto& observer : observers_)
observer.DidStartNavigation(document_loader->GetUrl(), std::nullopt);
for (auto& observer : observers_)
observer.ReadyToCommitNavigation(document_loader);
for (auto& observer : observers_)
observer.DidCreateNewDocument();
ui::PageTransition transition = GetTransitionType(
document_loader, IsMainFrame(), GetWebView()->IsFencedFrameRoot());
NotifyObserversOfNavigationCommit(transition);
}
void RenderFrameImpl::DidClearWindowObject() {
TRACE_EVENT_WITH_FLOW0("navigation", "RenderFrameImpl::DidClearWindowObject",
TRACE_ID_LOCAL(this),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
if (enabled_bindings_.Has(BindingsPolicyValue::kWebUi)) {
WebUIExtension::Install(frame_);
}
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
// DOM automation bindings that allows the JS content to send JSON-encoded
// data back to automation in the browser process. By default this isn't
// allowed unless the process has been started up with the --dom-automation
// switch.
if (command_line.HasSwitch(switches::kDomAutomationController))
DomAutomationController::Install(this, frame_);
// Bindings that allows the JS content to retrieve a variety of internal
// metrics. By default this isn't allowed unless the process has been started
// with the --enable-stats-collection-bindings switch.
if (command_line.HasSwitch(switches::kStatsCollectionController))
StatsCollectionController::Install(frame_);
if (command_line.HasSwitch(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() && !title.IsEmpty()) {
tracing::TrackNameRecorder::GetInstance()->UpdateProcessLabel(
process_label_id_, title.Utf8());
} else {
// Set process title for sub-frames and title-less frames in traces.
GURL loading_url = GetLoadingUrl();
if (!loading_url.host().empty() &&
loading_url.scheme() != url::kFileScheme) {
std::string frame_title;
if (frame_->Parent()) {
frame_title += "Subframe: ";
}
frame_title += loading_url.DeprecatedGetOriginAsURL().spec();
tracing::TrackNameRecorder::GetInstance()->UpdateProcessLabel(
process_label_id_, frame_title);
}
}
// Also check whether we have new encoding name.
UpdateEncoding(frame_, frame_->View()->PageEncoding().Utf8());
}
void RenderFrameImpl::DidDispatchDOMContentLoadedEvent() {
TRACE_EVENT1("navigation,benchmark,rail",
"RenderFrameImpl::DidDispatchDOMContentLoadedEvent",
"frame_token", frame_token_);
for (auto& observer : observers_)
observer.DidDispatchDOMContentLoadedEvent();
// 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",
"frame_token", frame_token_);
if (!frame_->Parent()) {
TRACE_EVENT_INSTANT1("WebCore,benchmark,rail", "LoadFinished",
TRACE_EVENT_SCOPE_PROCESS, "isOutermostMainFrame",
frame_->IsOutermostMainFrame());
}
for (auto& observer : observers_)
observer.DidFinishLoad();
}
void RenderFrameImpl::DidFinishLoadForPrinting() {
for (auto& observer : observers_)
observer.DidFinishLoadForPrinting();
}
void RenderFrameImpl::DidFinishSameDocumentNavigation(
blink::WebHistoryCommitType commit_type,
bool is_synchronously_committed,
blink::mojom::SameDocumentNavigationType same_document_navigation_type,
bool is_client_redirect,
const std::optional<blink::SameDocNavigationScreenshotDestinationToken>&
screenshot_destination) {
TRACE_EVENT1("navigation,rail",
"RenderFrameImpl::didFinishSameDocumentNavigation",
"frame_token", frame_token_);
WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
DocumentState* document_state =
DocumentState::FromDocumentLoader(document_loader);
if (is_synchronously_committed) {
document_state->set_navigation_state(
NavigationState::CreateForSynchronousCommit());
}
document_state->navigation_state()->set_was_within_same_document(true);
ui::PageTransition transition = GetTransitionType(
document_loader, IsMainFrame(), GetWebView()->IsFencedFrameRoot());
auto same_document_params =
mojom::DidCommitSameDocumentNavigationParams::New();
same_document_params->same_document_navigation_type =
same_document_navigation_type;
same_document_params->is_client_redirect = is_client_redirect;
same_document_params->started_with_transient_activation =
document_loader->LastNavigationHadTransientUserActivation();
same_document_params->should_replace_current_entry =
document_loader->ReplacesCurrentHistoryItem();
same_document_params->navigation_entry_screenshot_destination =
screenshot_destination;
DidCommitNavigationInternal(
commit_type, transition,
network::ParsedPermissionsPolicy(), // permissions_policy_header
blink::DocumentPolicyFeatureState(), // document_policy_header
nullptr, // interface_params
std::move(same_document_params),
std::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.
document_state->navigation_state()->set_transition_type(
ui::PAGE_TRANSITION_LINK);
for (auto& observer : observers_)
observer.DidFinishSameDocumentNavigation();
document_state->clear_navigation_state();
}
void RenderFrameImpl::DidFailAsyncSameDocumentCommit() {
// This is called when the Navigation API deferred a same-document commit,
// then fails the navigation without committing, so that we can run the
// callback if this commit was browser-initiated. If the commit is aborted
// due to frame detach or another navigation preempting it, NavigationState's
// destructor will run the callback instead.
DocumentState* document_state =
DocumentState::FromDocumentLoader(frame_->GetDocumentLoader());
if (NavigationState* navigation_state = document_state->navigation_state()) {
navigation_state->RunCommitSameDocumentNavigationCallback(
blink::mojom::CommitResult::Aborted);
document_state->clear_navigation_state();
}
}
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) {
GURL filtered_url(url);
if (!IsValidCommitUrl(filtered_url)) {
filtered_url = GURL(kBlockedURL);
}
GetFrameHost()->DidOpenDocumentInputStream(filtered_url);
}
void RenderFrameImpl::DidSetPageLifecycleState(bool restoring_from_bfcache) {
for (auto& observer : observers_)
observer.DidSetPageLifecycleState(restoring_from_bfcache);
}
void RenderFrameImpl::NotifyCurrentHistoryItemChanged() {
SendUpdateState();
}
void RenderFrameImpl::DidUpdateCurrentHistoryItem() {
StartDelayedSyncTimer();
}
void RenderFrameImpl::StartDelayedSyncTimer() {
base::TimeDelta delay;
if (send_content_state_immediately_) {
SendUpdateState();
return;
} else if (GetWebView()->GetVisibilityState() !=
blink::mojom::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(
bool is_loading,
blink::mojom::FrameReplicationStatePtr replicated_frame_state,
const blink::RemoteFrameToken& proxy_frame_token,
blink::mojom::RemoteFrameInterfacesFromBrowserPtr remote_frame_interfaces,
blink::mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces,
const std::optional<base::UnguessableToken>& devtools_frame_token) {
TRACE_EVENT1("navigation,rail", "RenderFrameImpl::SwapOutAndDeleteThis",
"frame_token", frame_token_);
DCHECK(!base::RunLoop::IsNestedOnCurrentThread());
// Create a WebRemoteFrame so we can pass it into `Swap`.
blink::WebRemoteFrame* remote_frame = blink::WebRemoteFrame::Create(
frame_->GetTreeScopeType(), proxy_frame_token);
blink::WebView* web_view = GetWebView();
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(remote_frame, std::move(remote_frame_interfaces->frame_host),
std::move(remote_frame_interfaces->frame_receiver),
std::move(replicated_frame_state), devtools_frame_token);
// 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 `blink::RemoteFrame` 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().
web_view->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.
remote_frame->Close(blink::DetachReason::kFrameDeletion);
return false;
}
if (is_loading)
remote_frame->DidStartLoading();
return true;
}
base::UnguessableToken RenderFrameImpl::GetDevToolsFrameToken() {
return devtools_frame_token_;
}
void RenderFrameImpl::AbortClientNavigation(bool for_new_navigation) {
CHECK(in_frame_tree_);
is_requesting_navigation_ = 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.
// Note: This might not actually cancel the navigation if the navigation is
// already in the process of committing to a different RenderFrame.
if (for_new_navigation) {
navigation_client_impl_->ResetForNewNavigation(
/*is_duplicate_navigation=*/false);
} else {
navigation_client_impl_->ResetForAbort();
}
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& main_frame_intersection_rect) {
if (main_frame_intersection_rect != main_frame_intersection_rect_) {
main_frame_intersection_rect_ = main_frame_intersection_rect;
for (auto& observer : observers_) {
observer.OnMainFrameIntersectionChanged(main_frame_intersection_rect);
}
}
}
void RenderFrameImpl::OnMainFrameViewportRectangleChanged(
const gfx::Rect& main_frame_viewport_rect) {
if (main_frame_viewport_rect != main_frame_viewport_rect_) {
main_frame_viewport_rect_ = main_frame_viewport_rect;
for (auto& observer : observers_) {
observer.OnMainFrameViewportRectangleChanged(main_frame_viewport_rect);
}
}
}
void RenderFrameImpl::OnMainFrameImageAdRectangleChanged(
int element_id,
const gfx::Rect& image_ad_rect) {
for (auto& observer : observers_) {
observer.OnMainFrameImageAdRectangleChanged(element_id, image_ad_rect);
}
}
void RenderFrameImpl::OnOverlayPopupAdDetected() {
for (auto& observer : observers_) {
observer.OnOverlayPopupAdDetected();
}
}
void RenderFrameImpl::OnLargeStickyAdDetected() {
for (auto& observer : observers_) {
observer.OnLargeStickyAdDetected();
}
}
void RenderFrameImpl::FinalizeRequest(blink::WebURLRequest& request) {
// This method is called for subresources, while transition type is
// a navigation concept. We pass ui::PAGE_TRANSITION_LINK as default one.
FinalizeRequestInternal(request, /*for_outermost_main_frame=*/false,
ui::PAGE_TRANSITION_LINK);
for (auto& observer : observers_) {
// TODO(sky): rename to FinalizeRequest.
observer.WillSendRequest(request);
}
}
std::optional<blink::WebURL> RenderFrameImpl::WillSendRequest(
const blink::WebURL& target,
const blink::WebSecurityOrigin& security_origin,
const net::SiteForCookies& site_for_cookies,
ForRedirect for_redirect,
const blink::WebURL& upstream_url) {
return WillSendRequestInternal(target, security_origin, site_for_cookies,
for_redirect, upstream_url,
ui::PAGE_TRANSITION_LINK);
}
std::optional<blink::WebURL> RenderFrameImpl::WillSendRequestInternal(
const blink::WebURL& target,
const blink::WebSecurityOrigin& security_origin,
const net::SiteForCookies& site_for_cookies,
ForRedirect for_redirect,
const blink::WebURL& upstream_url,
ui::PageTransition transition_type) {
std::optional<blink::WebURL> adjusted = ApplyFilePathAlias(target);
GURL new_url;
std::optional<url::Origin> initiator_origin =
security_origin.IsNull() ? std::optional<url::Origin>()
: std::optional<url::Origin>(security_origin);
GetContentClient()->renderer()->WillSendRequest(
frame_, transition_type, upstream_url,
adjusted.has_value() ? *adjusted : target, site_for_cookies,
base::OptionalToPtr(initiator_origin), &new_url);
if (!new_url.is_empty()) {
return WebURL(new_url);
}
return adjusted;
}
void RenderFrameImpl::FinalizeRequestInternal(
blink::WebURLRequest& request,
bool for_outermost_main_frame,
ui::PageTransition transition_type) {
if (GetWebView()->GetRendererPreferences().enable_do_not_track) {
request.SetHttpHeaderField(
blink::WebString::FromUTF8(blink::kDoNotTrackHeader), "1");
}
// 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);
}
}
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_outermost_main_frame(IsMainFrame() &&
!IsInFencedFrameTree());
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_allow_cross_origin_auth_prompt(
GetWebView()->GetRendererPreferences().allow_cross_origin_auth_prompt);
request.SetDownloadToNetworkCacheOnly(is_for_no_state_prefetch &&
!for_outermost_main_frame);
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 url::SchemeHostPort& final_response_url,
int request_id,
network::mojom::URLResponseHeadPtr response_head,
network::mojom::RequestDestination request_destination,
bool is_ad_resource) {
for (auto& observer : observers_) {
observer.DidStartResponse(final_response_url, request_id, *response_head,
request_destination, is_ad_resource);
}
}
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::DidObserveUserInteraction(
base::TimeTicks max_event_start,
base::TimeTicks max_event_queued_main_thread,
base::TimeTicks max_event_commit_finish,
base::TimeTicks max_event_end,
uint64_t interaction_offset) {
for (auto& observer : observers_) {
observer.DidObserveUserInteraction(
max_event_start, max_event_queued_main_thread, max_event_commit_finish,
max_event_end, interaction_offset);
}
}
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::DidObserveJavaScriptFrameworks(
const blink::JavaScriptFrameworkDetectionResult& result) {
for (auto& observer : observers_) {
observer.DidObserveJavaScriptFrameworks(result);
}
}
void RenderFrameImpl::DidObserveSubresourceLoad(
const blink::SubresourceLoadMetrics& subresource_load_metrics) {
for (auto& observer : observers_)
observer.DidObserveSubresourceLoad(subresource_load_metrics);
}
void RenderFrameImpl::DidObserveNewFeatureUsage(
const blink::UseCounterFeature& feature) {
TRACE_EVENT_WITH_FLOW0("navigation",
"RenderFrameImpl::DidObserveNewFeatureUsage",
TRACE_ID_LOCAL(this),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
for (auto& observer : observers_)
observer.DidObserveNewFeatureUsage(feature);
}
void RenderFrameImpl::DidObserveSoftNavigation(
blink::SoftNavigationMetrics metrics) {
for (auto& observer : observers_) {
observer.DidObserveSoftNavigation(metrics);
}
}
void RenderFrameImpl::DidObserveLayoutShift(double score,
bool after_input_or_scroll) {
for (auto& observer : observers_)
observer.DidObserveLayoutShift(score, after_input_or_scroll);
}
void RenderFrameImpl::DidCreateScriptContext(v8::Local<v8::Context> context,
int world_id) {
TRACE_EVENT_WITH_FLOW0("navigation",
"RenderFrameImpl::DidCreateScriptContext",
TRACE_ID_LOCAL(this),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
v8::MicrotasksScope microtasks(GetAgentGroupScheduler().Isolate(),
context->GetMicrotaskQueue(),
v8::MicrotasksScope::kDoNotRunMicrotasks);
if (((enabled_bindings_.Has(BindingsPolicyValue::kMojoWebUi)) ||
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);
if (mojo_js_features_) {
if (mojo_js_features_->file_system_access)
blink::WebV8Features::EnableMojoJSFileSystemAccessHelper(context, true);
}
}
if (world_id == ISOLATED_WORLD_ID_GLOBAL &&
mojo_js_interface_broker_.is_valid()) {
// MojoJS interface broker can be enabled on subframes, and will limit the
// interfaces JavaScript can request to those provided in the broker.
blink::WebV8Features::EnableMojoJSAndUseBroker(
context, std::move(mojo_js_interface_broker_));
}
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();
}
blink::WebMediaStreamDeviceObserver*
RenderFrameImpl::MediaStreamDeviceObserver() {
if (!web_media_stream_device_observer_)
InitializeMediaStreamDeviceObserver();
return web_media_stream_device_observer_.get();
}
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);
}
return blink::WebString();
}
std::optional<blink::UserAgentMetadata>
RenderFrameImpl::UserAgentMetadataOverride() {
if (ShouldUseUserAgentOverride()) {
return GetWebView()
->GetRendererPreferences()
.user_agent_override.ua_metadata_override;
}
return std::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();
DocumentState* document_state =
document_loader ? DocumentState::FromDocumentLoader(document_loader)
: nullptr;
return document_state && document_state->is_overriding_user_agent();
}
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()->HandleAXEvent(
event);
}
bool RenderFrameImpl::SendAccessibilitySerialization(
std::vector<ui::AXTreeUpdate> updates,
std::vector<ui::AXEvent> events,
ui::AXLocationAndScrollUpdates location_and_scroll_updates,
bool had_load_complete_messages) {
// This function should never be called from a11y unless it's enabled.
CHECK(IsAccessibilityEnabled());
return render_accessibility_manager_->GetRenderAccessibilityImpl()
->SendAccessibilitySerialization(std::move(updates), std::move(events),
std::move(location_and_scroll_updates),
had_load_complete_messages);
}
void RenderFrameImpl::AddObserver(RenderFrameObserver* observer) {
observers_.AddObserver(observer);
}
void RenderFrameImpl::RemoveObserver(RenderFrameObserver* observer) {
observer->RenderFrameGone();
observers_.RemoveObserver(observer);
}
void RenderFrameImpl::OnDroppedNavigation() {
is_requesting_navigation_ = false;
frame_->DidDropNavigation();
}
void RenderFrameImpl::WasHidden() {
frame_->WasHidden();
for (auto& observer : observers_)
observer.WasHidden();
#if BUILDFLAG(ENABLE_PPAPI)
for (PepperPluginInstanceImpl* plugin : active_pepper_instances_) {
plugin->PageVisibilityChanged(false);
}
#endif // BUILDFLAG(ENABLE_PPAPI)
}
void RenderFrameImpl::WasShown() {
frame_->WasShown();
for (auto& observer : observers_)
observer.WasShown();
#if BUILDFLAG(ENABLE_PPAPI)
for (PepperPluginInstanceImpl* plugin : active_pepper_instances_) {
plugin->PageVisibilityChanged(true);
}
#endif // BUILDFLAG(ENABLE_PPAPI)
}
void RenderFrameImpl::OnFrameVisibilityChanged(
blink::mojom::FrameVisibility render_status) {
for (auto& observer : observers_) {
observer.OnFrameVisibilityChanged(render_status);
}
}
bool RenderFrameImpl::IsMainFrame() {
return is_main_frame_;
}
bool RenderFrameImpl::IsInFencedFrameTree() const {
return GetWebFrame()->IsInFencedFrameTree();
}
bool RenderFrameImpl::IsHidden() {
CHECK(GetWebFrame()->IsProvisional() || GetLocalRootWebFrameWidget())
<< "Only provisional frames are created with no widget";
if (!GetLocalRootWebFrameWidget()) {
return true;
}
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());
}
base::WeakPtr<RenderFrameImpl> RenderFrameImpl::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
mojom::DidCommitProvisionalLoadParamsPtr
RenderFrameImpl::MakeDidCommitProvisionalLoadParams(
blink::WebHistoryCommitType commit_type,
ui::PageTransition transition,
const network::ParsedPermissionsPolicy& permissions_policy_header,
const blink::DocumentPolicyFeatureState& document_policy_header,
const std::optional<base::UnguessableToken>& embedding_token) {
WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
const WebURLResponse& response = document_loader->GetWebResponse();
DocumentState* document_state =
DocumentState::FromDocumentLoader(frame_->GetDocumentLoader());
NavigationState* navigation_state = document_state->navigation_state();
auto params = mojom::DidCommitProvisionalLoadParams::New();
params->http_status_code = response.HttpStatusCode();
params->url_is_unreachable = document_loader->HasUnreachableURL();
params->method = "GET";
params->post_id = -1;
params->embedding_token = embedding_token;
params->origin_calculation_debug_info =
document_loader->OriginCalculationDebugInfo().Utf8();
// 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() &&
document_loader->ReplacesCurrentHistoryItem() &&
!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();
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();
// Note: since we get the security origin from the `frame_document`, we also
// get the base url from it too.
if (params->url.IsAboutBlank() || params->url.IsAboutSrcdoc()) {
GURL base_url = frame_document.BaseURL();
// Only pass the base URL if it is valid and can be serialized by Mojo.
if (base_url.is_valid() &&
base_url.possibly_invalid_spec().length() <= url::kMaxURLChars) {
params->initiator_base_url = base_url;
}
}
// Don't send commit URLs to the browser that are known to be unsupported
// (e.g., would not pass RenderProcessHostImpl::FilterURL). This is applied
// after the initiator_base_url check above to avoid passing a base URL when
// the URL was not about:blank but was rewritten to about:blank#blocked.
if (!IsValidCommitUrl(params->url)) {
params->url = GURL(kBlockedURL);
}
// TODO(crbug.com/40161149): Reconsider how we calculate
// should_update_history.
params->should_update_history =
!document_loader->HasUnreachableURL() && response.HttpStatusCode() != 404;
// 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->navigation_api_key = item.GetNavigationApiKey().Utf8();
// Note that the value of `referrer` will be overwritten in the browser with a
// browser-calculated value in most cases. The exceptions are
// renderer-initated same-document navigations and the synchronous about:blank
// commit (because the browser doesn't know anything about those navigations).
// In those cases, the referrer policy component will still be overwritten in
// the browser, because this navigation won't change it and the browser
// already had access to the previous one. Send ReferrerPolicy::kDefault as a
// placeholder.
// TODO(crbug.com/40150370): Remove `referrer` from
// DidCommitProvisionalLoadParams.
params->referrer = blink::mojom::Referrer::New(
blink::WebStringToGURL(document_loader->Referrer()),
network::mojom::ReferrerPolicy::kDefault);
if (!frame_->Parent()) {
// Top-level navigation.
// Update contents MIME type for main frame.
params->contents_mime_type =
document_loader->GetWebResponse().MimeType().Utf8();
params->transition = transition;
// Check that if we are in a fenced frame tree then we must have
// PAGE_TRANSITION_AUTO_SUBFRAME. Otherwise we are a main frame
// and should have valid main frame values.
if (GetWebView()->IsFencedFrameRoot()) {
DCHECK(ui::PageTransitionCoreTypeIs(params->transition,
ui::PAGE_TRANSITION_AUTO_SUBFRAME));
} else {
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 =
document_state->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(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());
NOTREACHED() << " 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 = document_state->request_id();
params->unload_start =
GetWebFrame()->PerformanceMetricsForNestedContexts().UnloadStart();
params->unload_end =
GetWebFrame()->PerformanceMetricsForNestedContexts().UnloadEnd();
params->commit_navigation_start = navigation_state->commit_start_time();
params->commit_navigation_end = GetWebFrame()
->PerformanceMetricsForNestedContexts()
.CommitNavigationEnd();
// Note: this value should be recorded close to sending the DidCommit IPC.
params->commit_reply_sent = base::TimeTicks().Now();
return params;
}
void RenderFrameImpl::UpdateNavigationHistory(
blink::WebHistoryCommitType commit_type) {
NavigationState* navigation_state =
DocumentState::FromDocumentLoader(frame_->GetDocumentLoader())
->navigation_state();
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_index*/ 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_index, {});
}
}
void RenderFrameImpl::NotifyObserversOfNavigationCommit(
ui::PageTransition transition) {
TRACE_EVENT_WITH_FLOW0("navigation",
"RenderFrameImpl::NotifyObserversOfNavigationCommit",
TRACE_ID_LOCAL(this),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
for (auto& observer : observers_)
observer.DidCommitProvisionalLoad(transition);
}
void RenderFrameImpl::UpdateStateForCommit(
blink::WebHistoryCommitType commit_type,
ui::PageTransition transition) {
DocumentState* document_state =
DocumentState::FromDocumentLoader(frame_->GetDocumentLoader());
NavigationState* navigation_state = document_state->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 (!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 forces zoom factor to be propagated to the blink core frame.
auto& widget = CHECK_DEREF(GetLocalRootWebFrameWidget());
widget.SetZoomLevel(widget.GetZoomLevel());
}
// 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 network::ParsedPermissionsPolicy& permissions_policy_header,
const blink::DocumentPolicyFeatureState& document_policy_header,
mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params,
mojom::DidCommitSameDocumentNavigationParamsPtr same_document_params,
const std::optional<base::UnguessableToken>& embedding_token) {
DCHECK(!(same_document_params && interface_params));
UpdateStateForCommit(commit_type, transition);
if (GetBlinkPreferences().renderer_wide_named_frame_lookup)
GetWebFrame()->SetAllowsCrossBrowsingInstanceFrameLookup();
auto params = MakeDidCommitProvisionalLoadParams(
commit_type, transition, permissions_policy_header,
document_policy_header, embedding_token);
NavigationState* navigation_state =
DocumentState::FromDocumentLoader(frame_->GetDocumentLoader())
->navigation_state();
// 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.
if (same_document_params) {
GetFrameHost()->DidCommitSameDocumentNavigation(
std::move(params), std::move(same_document_params));
// This will be a noop if this same document navigation is a synchronous
// renderer-initiated commit.
navigation_state->RunCommitSameDocumentNavigationCallback(
blink::mojom::CommitResult::Ok);
} else {
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 the main frame and viewport rect when the main
// frame commits even if the rect does not change across navigations.
if (IsMainFrame()) {
main_frame_intersection_rect_.reset();
main_frame_viewport_rect_.reset();
}
}
void RenderFrameImpl::PrepareFrameForCommit(
const GURL& url,
const blink::mojom::CommitNavigationParams& commit_params) {
is_requesting_navigation_ = false;
GetContentClient()->SetActiveURL(
url, frame_->Top()->GetSecurityOrigin().ToString().Utf8());
GetWebView()->SetHistoryListFromNavigation(
commit_params.current_history_list_index,
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);
*item_for_history_navigation = WebHistoryItem(
blink::PageState::CreateFromEncodedData(commit_params.page_state));
if (item_for_history_navigation->IsNull())
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.
if (navigation_type == blink::mojom::NavigationType::HISTORY_SAME_DOCUMENT ||
navigation_type ==
blink::mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT) {
*load_type = blink::WebFrameLoadType::kBackForward;
} else {
*load_type = blink::WebFrameLoadType::kRestore;
}
// 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.
// TODO(crbug.com/41489044): A same-document history navigation was
// performed but the renderer does not have a history item. Diagnose this,
// make it a CHECK again, and drop the Restart.
DCHECK(!GetWebFrame()->GetCurrentHistoryItem().IsNull());
if (GetWebFrame()->GetCurrentHistoryItem().IsNull()) {
SCOPED_CRASH_KEY_BOOL("history_no_item", "is_main_frame", IsMainFrame());
SCOPED_CRASH_KEY_NUMBER("history_no_item", "renderer_commit_state",
(int)navigation_commit_state_);
SCOPED_CRASH_KEY_NUMBER("history_no_item", "browser_history_index",
commit_params.current_history_list_index);
SCOPED_CRASH_KEY_NUMBER("history_no_item", "browser_history_len",
commit_params.current_history_list_length);
SCOPED_CRASH_KEY_NUMBER("history_no_item", "renderer_history_len",
GetWebView()->HistoryBackListCount() +
GetWebView()->HistoryForwardListCount() + 1);
base::debug::DumpWithoutCrashing();
return blink::mojom::CommitResult::RestartCrossDocument;
}
// 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/40055210): 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 (auto* render_frame = RenderFrameImpl::FromWebFrame(previous_web_frame);
render_frame && widget_params_for_lazy_widget_creation_) {
// For local -> local swaps that lazily initialize the widget, tell the
// previous RenderFrame (i.e. the frame being swapped out) about `this`
// (i.e. the frame being swapped in), so `WillDetach()` on the previous
// RenderFrame can pass off its compositor to `this` for reuse.
//
// TODO(dcheng): Blink should already know the provisional frame internally;
// consider exposing that through the public API to avoid having to plumb
// state at a distance like this.
render_frame->provisional_frame_for_local_root_swap_ = GetWeakPtr();
}
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;
}
// For local roots, whether the frame widget is created eagerly or lazily, it
// must be initialized by this point.
//
// For non local roots, they must have a local root ancestor which already has
// a frame widget.
CHECK(GetLocalRootWebFrameWidget());
// `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",
"frame_token", frame_token_);
}
void RenderFrameImpl::DidStopLoading() {
TRACE_EVENT1("navigation,rail", "RenderFrameImpl::didStopLoading",
"frame_token", frame_token_);
// 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.
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));
// 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()));
#if BUILDFLAG(IS_ANDROID)
// 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, frame_, info->url_request, info->navigation_type,
info->navigation_policy, false /* is_redirect */)) {
return;
}
#endif
// TODO(crbug.com/40221940): Refactor _unfencedTop handling.
if (info->is_unfenced_top_navigation) {
OpenURL(std::move(info));
return;
}
// 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 (IsTopLevelNavigation(frame_) && !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_.HasAny(kWebUIBindingsPolicySet);
if (should_fork) {
OpenURL(std::move(info));
return; // Suppress the load here.
}
}
if (frame_->IsOutermostMainFrame() && url.is_valid() &&
url.SchemeIsHTTPOrHTTPS() &&
(base::FeatureList::IsEnabled(
blink::features::kHttpDiskCachePrewarming) ||
base::FeatureList::IsEnabled(
blink::features::kSpeculativeServiceWorkerWarmUp))) {
frame_->MaybeStartOutermostMainFrameNavigation(std::vector<WebURL>({url}));
}
// Depending on navigation policy, send one of three IPCs to the browser
// process: DownloadURL handles downloads, OpenURL handles all navigations
// that will end up in a different tab/window, and BeginNavigation handles
// everything else.
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));
return;
}
if (info->navigation_policy != blink::kWebNavigationPolicyCurrentTab) {
OpenURL(std::move(info));
return;
}
// 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(crbug.com/40184245): 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(crbug.com/40184245): Tighten the condition to only accept
// about:blank or an empty URL which defaults to about:blank, per the
// spec:
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element: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
// IsOnInitialEmptyDocument() 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,
// IsOnInitialEmptyDocument() 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_->IsOnInitialEmptyDocument() && 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 &&
// Synchronous about:blank commits on iframes should only be triggered
// when first creating the iframe with an unset/about:blank URL, which
// means the origin should inherit from the parent.
(IsMainFrame() || info->url_request.RequestorOrigin().IsSameOriginWith(
static_cast<WebLocalFrame*>(frame_->Parent())
->GetDocument()
.GetSecurityOrigin()));
if (should_do_synchronous_about_blank_navigation) {
for (auto& observer : observers_)
observer.DidStartNavigation(url, info->navigation_type);
SynchronouslyCommitAboutBlankForBug778318(std::move(info));
return;
}
if (!frame_->WillStartNavigation(*info)) {
return;
}
for (auto& observer : observers_) {
observer.DidStartNavigation(info->url_request.Url(), info->navigation_type);
}
is_requesting_navigation_ = true;
// Everything else is handled asynchronously by the browser process through
// BeginNavigation.
// base::Unretained() is safe to use here because if the callback
// is retained, it's by the member of this class.
auto do_begin_navigation = base::BindOnce(
&RenderFrameImpl::BeginNavigationInternal, base::Unretained(this),
std::move(info), is_history_navigation_in_new_child_frame,
renderer_before_unload_start, renderer_before_unload_end);
client_navigation_throttler_.DispatchOrScheduleNavigation(
std::move(do_begin_navigation));
}
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);
// This quirk is internal to the renderer, so just reuse the previous
// DocumentToken.
navigation_params->document_token = frame_->GetDocument().Token();
navigation_params->is_synchronous_commit_for_bug_778318 = true;
// 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(crbug.com/40184245): Ensure main frame cases always do
// replacement too.
DCHECK(IsMainFrame() || navigation_params->frame_load_type ==
WebFrameLoadType::kReplaceCurrentItem);
// This corresponds to steps 3 and 20 of
// https://html.spec.whatwg.org/multipage/browsers.html#creating-a-new-browsing-context,
// which sets the new Document's `referrer` member to the initiator frame's
// full unredacted URL, in the case of new browsing context creation.
//
// The initiator might no longer exist however, in which case we cannot get
// its document's full URL to use as the referrer.
if (info->initiator_frame_token.has_value() &&
WebFrame::FromFrameToken(info->initiator_frame_token.value())) {
WebFrame* initiator =
WebFrame::FromFrameToken(info->initiator_frame_token.value());
DCHECK(initiator->IsWebLocalFrame());
navigation_params->referrer =
initiator->ToWebLocalFrame()->GetDocument().Url().GetString();
}
// To prevent pages from being able to abuse window.open() to determine the
// system entropy, always set a fixed value of 'normal', for consistency with
// other top-level navigations.
if (IsMainFrame()) {
navigation_params->navigation_timings.system_entropy_at_navigation_start =
blink::mojom::SystemEntropy::kNormal;
} else {
// Sub frames always have an empty entropy state since they are generally
// renderer-initiated. See
// https://docs.google.com/document/d/1D6DqptsCEd3wPRsZ0q1iwVBAXXmhxZuLV-KKFI0ptCg/edit?usp=sharing
// for background.
DCHECK_EQ(blink::mojom::SystemEntropy::kEmpty,
navigation_params->navigation_timings
.system_entropy_at_navigation_start);
}
frame_->CommitNavigation(std::move(navigation_params), BuildDocumentState());
}
// mojom::MhtmlFileWriter implementation
// ----------------------------------------
void RenderFrameImpl::SerializeAsMHTML(mojom::SerializeAsMHTMLParamsPtr params,
SerializeAsMHTMLCallback callback) {
TRACE_EVENT0("page-serialization", "RenderFrameImpl::SerializeAsMHTML");
// 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;
auto delegate =
std::make_unique<MHTMLPartsGenerationDelegateImpl>(std::move(params));
// 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.get()));
}
// 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.
TRACE_EVENT0("page-serialization",
"RenderFrameImpl::SerializeAsMHTML parts serialization");
MHTMLPartsGenerationDelegateImpl* delegate_ptr = delegate.get();
WebFrameSerializer::GenerateMHTMLParts(
mhtml_boundary, GetWebFrame(), delegate_ptr,
base::BindOnce(&RenderFrameImpl::OnSerializeMHTMLComplete,
weak_factory_.GetWeakPtr(), std::move(delegate),
std::move(callback), std::move(mhtml_contents)));
}
void RenderFrameImpl::OnSerializeMHTMLComplete(
std::unique_ptr<MHTMLPartsGenerationDelegateImpl> delegate,
SerializeAsMHTMLCallback callback,
std::vector<blink::WebThreadSafeData> mhtml_contents,
blink::WebThreadSafeData frame_mhtml_data) {
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(frame_mhtml_data);
bool has_some_data = false;
for (const auto& c : mhtml_contents) {
if (!c.IsEmpty()) {
has_some_data = true;
break;
}
}
// Note: the MHTML footer is written by the browser process, after the last
// frame is serialized by a renderer process.
MHTMLHandleWriterDelegate handle_delegate(
*delegate->TakeParams(),
base::BindOnce(&RenderFrameImpl::OnWriteMHTMLComplete,
weak_factory_.GetWeakPtr(), std::move(callback),
delegate->TakeSerializedResourcesUriDigests()),
GetTaskRunner(blink::TaskType::kInternalDefault));
if (has_some_data) {
handle_delegate.WriteContents(mhtml_contents);
} else {
handle_delegate.Finish(mojom::MhtmlSaveStatus::kSuccess);
}
}
void RenderFrameImpl::OnWriteMHTMLComplete(
SerializeAsMHTMLCallback callback,
std::unordered_set<std::string> serialized_resources_uri_digests,
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.
std::move(callback).Run(save_status, std::move(digests_of_new_parts));
}
#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();
if (info->requestor_base_url.IsValid()) {
params->initiator_base_url = info->requestor_base_url;
}
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 = 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->is_unfenced_top_navigation = info->is_unfenced_top_navigation;
params->initiator_navigation_state_keep_alive_handle =
std::move(info->initiator_navigation_state_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();
params->impression = 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 ||
policy == blink::kWebNavigationPolicyPictureInPicture)) {
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_->IsAdFrame();
params->download_policy.ApplyDownloadFramePolicy(
info->is_opener_navigation, info->url_request.HasUserGesture(),
info->url_request.RequestorOrigin().CanAccess(
frame_->GetSecurityOrigin()),
has_download_sandbox_flag, from_ad);
params->initiator_activation_and_ad_status =
blink::GetNavigationInitiatorActivationAndAdStatus(
info->url_request.HasUserGesture(), info->initiator_frame_is_ad,
info->is_ad_script_in_stack);
params->has_rel_opener = info->has_rel_opener;
GetFrameHost()->OpenURL(std::move(params));
}
void RenderFrameImpl::SetLoaderFactoryBundle(
scoped_refptr<blink::ChildURLLoaderFactoryBundle> loader_factories) {
// `background_resource_fetch_context_` will be lazy initialized the first
// time MaybeGetBackgroundResourceFetchAssets() is called if
// BackgroundResourceFetch feature is enabled.
background_resource_fetch_context_.reset();
loader_factories_ = std::move(loader_factories);
}
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,
std::optional<std::vector<blink::mojom::TransferrableURLLoaderPtr>>
subresource_overrides,
mojo::PendingRemote<network::mojom::URLLoaderFactory>
subresource_proxying_loader_factory,
mojo::PendingRemote<network::mojom::URLLoaderFactory>
keep_alive_loader_factory,
mojo::PendingAssociatedRemote<blink::mojom::FetchLaterLoaderFactory>
fetch_later_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.
// If resource metadata is present, construct an in-process resource loader
// and have it intercept requests it may be able to service.
info = MaybeSetUpLocalResourceLoader(std::move(info));
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 (subresource_proxying_loader_factory) {
loader_factories->SetSubresourceProxyingLoaderFactory(
std::move(subresource_proxying_loader_factory));
}
if (keep_alive_loader_factory) {
loader_factories->SetKeepAliveLoaderFactory(
std::move(keep_alive_loader_factory));
}
if (base::FeatureList::IsEnabled(blink::features::kFetchLaterAPI) &&
fetch_later_loader_factory) {
loader_factories->SetFetchLaterLoaderFactory(
std::move(fetch_later_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()) {
// TODO(crbug.com/40188381): Move `UpdateEncoding()` to the
// `LocalMainFrameHost` interface where it makes sense. That is not a simple
// as just migrating the method, since upon main frame creation we attempt
// to send the encoding information before
// `WebViewImpl::local_main_frame_host_remote_` is set-up, which breaks
// things.`
GetFrameHost()->UpdateEncoding(encoding_name);
}
}
void RenderFrameImpl::SyncSelectionIfRequired(blink::SyncCondition force_sync) {
std::u16string text;
size_t offset;
gfx::Range range;
#if BUILDFLAG(ENABLE_PPAPI)
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,
blink::CrossVariantMojoReceiver<
media::mojom::AudioProcessorControlsInterfaceBase> controls_receiver,
const media::AudioProcessingSettings* settings) {
DCHECK_EQ(!!settings, !!controls_receiver);
media::mojom::AudioProcessingConfigPtr processing_config;
if (controls_receiver) {
DCHECK(settings);
processing_config = media::mojom::AudioProcessingConfig::New(
std::move(controls_receiver), *settings);
}
GetAudioInputStreamFactory()->CreateStream(
std::move(client), session_id, params, automatic_gain_control,
shared_memory_count, std::move(processing_config));
}
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::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) {
// Provisional frames shouldn't initiate navigations.
CHECK(!GetWebFrame()->IsProvisional());
// Set SiteForCookies.
WebDocument frame_document = frame_->GetDocument();
if (info->frame_type == blink::mojom::RequestContextFrameType::kTopLevel) {
info->url_request.SetSiteForCookies(
net::SiteForCookies::FromUrl(info->url_request.Url()));
} else {
info->url_request.SetSiteForCookies(frame_document.SiteForCookies());
}
ui::PageTransition transition_type = GetTransitionType(
ui::PAGE_TRANSITION_LINK,
info->frame_load_type == WebFrameLoadType::kReplaceCurrentItem,
IsMainFrame(), GetWebView()->IsFencedFrameRoot(), 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_outermost_main_frame = frame_->IsOutermostMainFrame();
{
// upstream_url can be empty here because ForRedirect(false) implies that
// this isn't a browser initiated navigation.
std::optional<blink::WebURL> adjusted_request_url = WillSendRequestInternal(
info->url_request.Url(), info->url_request.RequestorOrigin(),
info->url_request.SiteForCookies(), ForRedirect(false),
/*upstream_url=*/GURL(), transition_type);
if (adjusted_request_url.has_value()) {
info->url_request.SetUrl(adjusted_request_url.value());
}
}
FinalizeRequestInternal(info->url_request, for_outermost_main_frame,
transition_type);
// The extra data was created in WillSendRequestInternal if it didn't exist.
DCHECK(info->url_request.GetURLRequestExtraData());
// 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::kWebNavigationTypeFormResubmittedBackForward ||
info->navigation_type == blink::kWebNavigationTypeFormResubmittedReload);
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::optional<base::Value::Dict> devtools_initiator;
if (!info->devtools_initiator_info.IsNull()) {
std::optional<base::Value> devtools_initiator_value =
base::JSONReader::Read(info->devtools_initiator_info.Utf8());
if (devtools_initiator_value && devtools_initiator_value->is_dict()) {
devtools_initiator = std::move(*devtools_initiator_value).TakeDict();
}
}
blink::mojom::NavigationInitiatorActivationAndAdStatus
initiator_activation_and_ad_status =
blink::GetNavigationInitiatorActivationAndAdStatus(
info->url_request.HasUserGesture(), info->initiator_frame_is_ad,
info->is_ad_script_in_stack);
blink::mojom::BeginNavigationParamsPtr begin_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::GetMixedContentContextTypeForWebURLRequest(info->url_request),
is_form_submission, was_initiated_by_link_click,
info->force_history_push, searchable_form_url,
searchable_form_encoding, client_side_redirect_url,
std::move(devtools_initiator),
info->url_request.TrustTokenParams()
? info->url_request.TrustTokenParams()->Clone()
: nullptr,
info->impression, renderer_before_unload_start,
renderer_before_unload_end, initiator_activation_and_ad_status,
info->is_container_initiated, info->storage_access_api_status,
info->has_rel_opener);
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_->IsAdFrame();
mojo::PendingRemote<blink::mojom::NavigationStateKeepAliveHandle>
initiator_navigation_state_keep_alive_handle =
std::move(info->initiator_navigation_state_keep_alive_handle);
network::mojom::RequestDestination request_destination =
blink::GetRequestDestinationForWebURLRequest(info->url_request);
blink::mojom::CommonNavigationParamsPtr common_params =
MakeCommonNavigationParams(frame_->GetSecurityOrigin(), std::move(info),
load_flags, has_download_sandbox_flag, from_ad,
is_history_navigation_in_new_child_frame,
request_destination);
bool is_duplicate_navigation = false;
base::TimeDelta nav_start_diff;
if (navigation_client_impl_ &&
navigation_client_impl_->HasBeginNavigationParams()) {
// We ignore navigations that are identical to the ongoing navigation. This
// is because the navigation is likely to be accidental (e.g. user clicked
// the same link multiple times, etc).
auto& prev_begin_params = navigation_client_impl_->begin_params();
auto& prev_common_params = navigation_client_impl_->common_params();
if (begin_params->was_initiated_by_link_click ==
prev_begin_params.was_initiated_by_link_click &&
common_params->url == prev_common_params.url &&
common_params->method == "GET" && prev_common_params.method == "GET" &&
common_params->initiator_origin ==
prev_common_params.initiator_origin &&
common_params->has_user_gesture ==
prev_common_params.has_user_gesture &&
common_params->referrer == prev_common_params.referrer &&
common_params->transition == prev_common_params.transition &&
common_params->should_replace_current_entry ==
prev_common_params.should_replace_current_entry &&
begin_params->headers == prev_begin_params.headers &&
begin_params->has_rel_opener == prev_begin_params.has_rel_opener) {
is_duplicate_navigation = true;
nav_start_diff = (common_params->navigation_start -
prev_common_params.navigation_start);
}
}
base::UmaHistogramBoolean(
"Navigation.RendererInitiated.IsDuplicateWithoutThresholdCheck",
is_duplicate_navigation);
if (is_duplicate_navigation) {
// The navigation is similar to a previous navigation. Check if it's started
// close enough to the start of the previous navigation, in which case we
// can just ignore the new navigation and keep the previous navigation.
bool start_diff_under_threshold =
(nav_start_diff <= features::kDuplicateNavThreshold.Get());
base::UmaHistogramBoolean(
"Navigation.RendererInitiated.DuplicateNavIsUnderThreshold",
start_diff_under_threshold);
base::UmaHistogramTimes(
"Navigation.RendererInitiated.DuplicateNavStartTimeDiff",
nav_start_diff);
if (start_diff_under_threshold &&
base::FeatureList::IsEnabled(features::kIgnoreDuplicateNavs)) {
return;
}
}
mojo::PendingAssociatedRemote<mojom::NavigationClient>
navigation_client_remote;
BindNavigationClientWithParams(
navigation_client_remote.InitWithNewEndpointAndPassReceiver(),
begin_params.Clone(), common_params.Clone(), is_duplicate_navigation);
mojo::PendingReceiver<mojom::NavigationRendererCancellationListener>
renderer_cancellation_listener_receiver;
navigation_client_impl_->SetUpRendererInitiatedNavigation(
renderer_cancellation_listener_receiver.InitWithNewPipeAndPassRemote());
GetFrameHost()->BeginNavigation(
std::move(common_params), std::move(begin_params),
std::move(blob_url_token), std::move(navigation_client_remote),
std::move(initiator_navigation_state_keep_alive_handle),
std::move(renderer_cancellation_listener_receiver));
}
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 BUILDFLAG(IS_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(crbug.com/40187599): 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 {
NOTREACHED() << "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());
}
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
RenderFrameImpl::MaybeSetUpLocalResourceLoader(
std::unique_ptr<blink::PendingURLLoaderFactoryBundle> factory_bundle) {
if (!factory_bundle->local_resource_loader_config()) {
return factory_bundle;
}
// The pipe to the browser-side WebUIURLLoaderFactory could be in one of two
// places:
//
// 1. factory_bundle->pending_default_factory()
// 2. factory_bundle->scheme_specific_factories
//
// (This may change in the future, see https://crbug.com/40091019)
//
// The browser process only sends the config in the first case (see call to
// PendingURLLoaderFactoryBundle::set_local_resource_loader_config in
// RenderFrameHostImpl::CommitNavigation), so if we're constructing a new
// in-process resource loader we always take that as the fallback.
// Take the existing PendingRemote and make it the fallback for a new
// in-process resource loader.
local_resource_loader_.emplace(
base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::USER_BLOCKING, base::MayBlock(),
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}),
std::move(factory_bundle->local_resource_loader_config()),
std::move(factory_bundle->pending_default_factory()));
// Create a pipe and pass the receiving end to the new in-process loader.
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote;
local_resource_loader_
.AsyncCall(&content::LocalResourceURLLoaderFactory::Clone)
.WithArgs(pending_remote.InitWithNewPipeAndPassReceiver());
// Swap in the new PendingRemote.
factory_bundle->pending_default_factory() = std::move(pending_remote);
return factory_bundle;
}
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 loadDataWithBaseURL() navigations, it will return the data: URL
// used to commit, instead of the "base URL" used as the document URL in the
// DocumentLoader. See comments in BuildDocumentStateFromParams() and
// in navigation_params.mojom's `is_load_data_with_base_url` 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<blink::mojom::AutoplayConfigurationClient>(
base::BindRepeating(&RenderFrameImpl::BindAutoplayConfiguration,
weak_factory_.GetWeakPtr()));
GetAssociatedInterfaceRegistry()->AddInterface<mojom::FrameBindingsControl>(
base::BindRepeating(&RenderFrameImpl::BindFrameBindingsControl,
weak_factory_.GetWeakPtr()));
GetAssociatedInterfaceRegistry()->AddInterface<mojom::NavigationClient>(
base::BindRepeating(&RenderFrameImpl::BindNavigationClient,
weak_factory_.GetWeakPtr()));
// TODO(dcheng): Fold this interface into mojom::Frame.
GetAssociatedInterfaceRegistry()->AddInterface<mojom::MhtmlFileWriter>(
base::BindRepeating(&RenderFrameImpl::BindMhtmlFileWriter,
base::Unretained(this)));
GetAssociatedInterfaceRegistry()
->AddInterface<blink::mojom::RenderAccessibility>(base::BindRepeating(
&RenderAccessibilityManager::BindReceiver,
base::Unretained(render_accessibility_manager_.get())));
#if BUILDFLAG(IS_ANDROID)
GetAssociatedInterfaceRegistry()->AddInterface<mojom::GinJavaBridge>(
base::BindRepeating(&RenderFrameImpl::BindGinJavaBridge,
weak_factory_.GetWeakPtr()));
#endif
}
#if BUILDFLAG(IS_ANDROID)
void RenderFrameImpl::BindGinJavaBridge(
mojo::PendingAssociatedReceiver<mojom::GinJavaBridge> receiver) {
mojo::MakeSelfOwnedAssociatedReceiver(
std::make_unique<GinJavaBridgeDispatcher>(this), std::move(receiver));
}
#endif
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));
}
scoped_refptr<network::SharedURLLoaderFactory>
RenderFrameImpl::GetURLLoaderFactory() {
if (!RenderThreadImpl::current()) {
// Some tests (e.g. RenderViewTests) do not have RenderThreadImpl,
// and must create a factory override instead.
if (url_loader_factory_override_for_test_) {
return url_loader_factory_override_for_test_;
}
// 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->url_loader_factory_override_for_test_) {
return ancestor_frame->url_loader_factory_override_for_test_;
}
}
// At this point we can't create anything.
NOTREACHED();
}
return GetLoaderFactoryBundle();
}
blink::URLLoaderThrottleProvider*
RenderFrameImpl::GetURLLoaderThrottleProvider() {
RenderThreadImpl* render_thread = RenderThreadImpl::current();
// The RenderThreadImpl may not be valid in some tests.
return render_thread ? render_thread->url_loader_throttle_provider()
: nullptr;
}
scoped_refptr<blink::WebBackgroundResourceFetchAssets>
RenderFrameImpl::MaybeGetBackgroundResourceFetchAssets() {
if (!base::FeatureList::IsEnabled(
blink::features::kBackgroundResourceFetch)) {
return nullptr;
}
if (!background_resource_fetch_context_) {
if (!background_resource_fetch_task_runner_) {
background_resource_fetch_task_runner_ =
base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::USER_BLOCKING});
}
background_resource_fetch_context_ =
base::MakeRefCounted<BackgroundResourceFetchAssets>(
GetLoaderFactoryBundle()->Clone(),
GetURLLoaderThrottleProvider()
? GetURLLoaderThrottleProvider()->Clone()
: nullptr,
background_resource_fetch_task_runner_,
frame_->GetLocalFrameToken());
}
return background_resource_fetch_context_;
}
void RenderFrameImpl::OnStopLoading() {
for (auto& observer : observers_)
observer.OnStop();
}
bool RenderFrameImpl::IsRequestingNavigation() {
return is_requesting_navigation_;
}
void RenderFrameImpl::LoadHTMLStringForTesting(std::string_view html,
const GURL& base_url,
const std::string& text_encoding,
const GURL& unreachable_url,
bool replace_current_item) {
AssertNavigationCommits assert_navigation_commits(
this, kMayReplaceInitialEmptyDocument);
mojo::PendingRemote<network::mojom::URLLoaderFactory>
url_loader_factory_remote;
if (url_loader_factory_override_for_test_) {
url_loader_factory_override_for_test_->Clone(
url_loader_factory_remote.InitWithNewPipeAndPassReceiver());
} else {
url_loader_factory_remote =
network::NotImplementedURLLoaderFactory::Create();
}
pending_loader_factories_ = CreateLoaderFactoryBundle(
blink::ChildPendingURLLoaderFactoryBundle::CreateFromDefaultFactoryImpl(
std::move(url_loader_factory_remote)),
/*subresource_overrides=*/std::nullopt,
/*subresource_proxying_loader_factory=*/{},
/*keep_alive_loader_factory=*/{},
/*fetch_later_loader_factory=*/{});
auto navigation_params = std::make_unique<WebNavigationParams>();
navigation_params->url = base_url;
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);
}
BindingsPolicySet RenderFrameImpl::GetEnabledBindings() {
return enabled_bindings_;
}
void RenderFrameImpl::SetAccessibilityModeForTest(ui::AXMode new_mode) {
render_accessibility_manager_->SetMode(new_mode, 1);
}
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_PPAPI)
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 // BUILDFLAG(ENABLE_PPAPI)
std::unique_ptr<blink::WebSocketHandshakeThrottle>
RenderFrameImpl::CreateWebSocketHandshakeThrottle() {
// 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(
frame_->GetLocalFrameToken(),
GetTaskRunner(blink::TaskType::kInternalDefault));
}
bool RenderFrameImpl::GetCaretBoundsFromFocusedPlugin(gfx::Rect& rect) {
#if BUILDFLAG(ENABLE_PPAPI)
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::SetURLLoaderFactoryOverrideForTest(
scoped_refptr<network::SharedURLLoaderFactory> factory) {
url_loader_factory_override_for_test_ = std::move(factory);
}
scoped_refptr<blink::ChildURLLoaderFactoryBundle>
RenderFrameImpl::CloneLoaderFactories() {
auto pending_bundle = base::WrapUnique(
static_cast<blink::TrackedChildPendingURLLoaderFactoryBundle*>(
GetLoaderFactoryBundle()->Clone().release()));
return base::MakeRefCounted<blink::TrackedChildURLLoaderFactoryBundle>(
std::move(pending_bundle));
}
blink::scheduler::WebAgentGroupScheduler&
RenderFrameImpl::GetAgentGroupScheduler() {
return agent_scheduling_group_->agent_group_scheduler();
}
url::Origin RenderFrameImpl::GetSecurityOriginOfTopFrame() {
return frame_->Top()->GetSecurityOrigin();
}
base::WeakPtr<media::DecoderFactory> RenderFrameImpl::GetMediaDecoderFactory() {
return media_factory_.GetDecoderFactory();
}
gfx::Rect RenderFrameImpl::ConvertViewportToWindow(const gfx::Rect& rect) {
return GetLocalRootWebFrameWidget()->BlinkSpaceToEnclosedDIPs(rect);
}
float RenderFrameImpl::GetDeviceScaleFactor() {
return GetLocalRootWebFrameWidget()->GetScreenInfo().device_scale_factor;
}
bool RenderFrameImpl::DeferMediaLoad(bool has_played_media_before,
base::OnceClosure closure) {
if (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));
}
WebView* RenderFrameImpl::CreateNewWindow(
const WebURLRequest& request,
const blink::WebWindowFeatures& features,
const WebString& frame_name,
WebNavigationPolicy policy,
network::mojom::WebSandboxFlags sandbox_flags,
const blink::SessionStorageNamespaceId& session_storage_namespace_id,
bool& consumed_user_gesture,
const std::optional<blink::Impression>& impression,
const std::optional<blink::WebPictureInPictureWindowOptions>& pip_options,
const blink::WebURL& base_url) {
consumed_user_gesture = false;
mojom::CreateNewWindowParamsPtr params = mojom::CreateNewWindowParams::New();
// The user activation check is done at the browser process through
// |frame_host->CreateNewWindow()| call below. But the extensions case
// handled through the following |if| is an exception.
params->allow_popup = false;
if (GetContentClient()->renderer()->AllowPopup())
params->allow_popup = true;
params->window_container_type = WindowFeaturesToContainerType(features);
params->session_storage_namespace_id = session_storage_namespace_id;
if (!features.noopener) {
params->clone_from_session_storage_namespace_id =
GetWebView()->GetSessionStorageNamespaceId();
}
const std::string& frame_name_utf8 = frame_name.Utf8(
WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD);
params->frame_name = frame_name_utf8;
params->opener_suppressed = features.noopener;
params->disposition = NavigationPolicyToDisposition(policy);
if (!request.IsNull()) {
params->target_url = request.Url();
// The browser process does not consider empty URLs as valid (partly due to
// a risk of treating them as a navigation to the privileged NTP in some
// cases), so treat an attempt to create a window with an empty URL as
// opening about:blank.
//
// Similarly, javascript: URLs should not be sent to the browser process,
// since they are either handled within the renderer process (if a window is
// created within the same browsing context group) or ignored (in the
// noopener case). Use about:blank for the URL in that case as well, to
// reduce the risk of running them incorrectly.
if (params->target_url.is_empty() ||
params->target_url.SchemeIs(url::kJavaScriptScheme)) {
params->target_url = GURL(url::kAboutBlankURL);
}
params->referrer = blink::mojom::Referrer::New(
blink::WebStringToGURL(request.ReferrerString()),
request.GetReferrerPolicy());
}
params->features = ConvertWebWindowFeaturesToMojoWindowFeatures(features);
params->is_form_submission = request.IsFormSubmission();
params->form_submission_post_data =
blink::GetRequestBodyForWebURLRequest(request);
params->form_submission_post_content_type = request.HttpContentType().Utf8();
params->impression = impression;
if (pip_options) {
CHECK_EQ(policy, blink::kWebNavigationPolicyPictureInPicture);
auto pip_mojom_opts = blink::mojom::PictureInPictureWindowOptions::New();
pip_mojom_opts->width = pip_options->width;
pip_mojom_opts->height = pip_options->height;
pip_mojom_opts->disallow_return_to_opener =
pip_options->disallow_return_to_opener;
pip_mojom_opts->prefer_initial_window_placement =
pip_options->prefer_initial_window_placement;
params->pip_options = std::move(pip_mojom_opts);
}
params->download_policy.ApplyDownloadFramePolicy(
/*is_opener_navigation=*/false, request.HasUserGesture(),
// `openee_can_access_opener_origin` only matters for opener navigations,
// so its value here is irrelevant.
/*openee_can_access_opener_origin=*/true,
!GetWebFrame()->IsAllowedToDownload(), GetWebFrame()->IsAdFrame());
params->initiator_activation_and_ad_status =
blink::GetNavigationInitiatorActivationAndAdStatus(
request.HasUserGesture(), GetWebFrame()->IsAdFrame(),
GetWebFrame()->IsAdScriptInStack());
// We preserve this information before sending the message since |params| is
// moved on send.
bool is_background_tab =
params->disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB;
mojom::CreateNewWindowStatus status;
mojom::CreateNewWindowReplyPtr reply;
auto* frame_host = GetFrameHost();
if (!frame_host->CreateNewWindow(std::move(params), &status, &reply)) {
// The sync IPC failed, e.g. maybe the render process is in the middle of
// shutting down. Can't create a new window without the browser process,
// so just bail out.
return nullptr;
}
// If creation of the window was blocked (e.g. because this frame doesn't
// have user activation), return before consuming user activation. A frame
// that isn't allowed to open a window shouldn't be able to consume the
// activation for the rest of the frame tree.
if (status == mojom::CreateNewWindowStatus::kBlocked)
return nullptr;
// For Android WebView, we support a pop-up like behavior for window.open()
// even if the embedding app doesn't support multiple windows. In this case,
// window.open() will return "window" and navigate it to whatever URL was
// passed. We also don't need to consume user gestures to protect against
// multiple windows being opened, because, well, the app doesn't support
// multiple windows.
// TODO(dcheng): It's awkward that this is plumbed into Blink but not really
// used much in Blink, except to enable web testing... perhaps this should
// be checked directly in the browser side.
if (status == mojom::CreateNewWindowStatus::kReuse) {
// In this case, treat javascript: URLs as blocked rather than running them
// in a reused main frame in Android WebView. See https://crbug.com/1083819.
if (!request.IsNull() && request.Url().ProtocolIs(url::kJavaScriptScheme)) {
return nullptr;
}
return GetWebView();
}
// Consume the transient user activation in the current renderer.
consumed_user_gesture = GetWebFrame()->ConsumeTransientUserActivation(
blink::UserActivationUpdateSource::kBrowser);
// If we should ignore the new window (e.g. because of `noopener`), return
// now that user activation was consumed.
if (status == mojom::CreateNewWindowStatus::kIgnore)
return nullptr;
DCHECK(reply);
DCHECK_NE(MSG_ROUTING_NONE, reply->main_frame_route_id);
DCHECK_NE(MSG_ROUTING_NONE, reply->widget_params->routing_id);
// While this view may be a background extension page, it can spawn a visible
// render view. So we just assume that the new one is not another background
// page instead of passing on our own value.
// TODO(vangelis): Can we tell if the new view will be a background page?
bool never_composited = false;
// The initial hidden state for the RenderViewImpl here has to match what the
// browser will eventually decide for the given disposition. Since we have to
// return from this call synchronously, we just have to make our best guess
// and rely on the browser sending a WasHidden / WasShown message if it
// disagrees.
mojom::CreateViewParamsPtr view_params = mojom::CreateViewParams::New();
view_params->opener_frame_token = GetWebFrame()->GetFrameToken();
view_params->window_was_opened_by_another_window = true;
view_params->renderer_preferences = GetWebView()->GetRendererPreferences();
view_params->web_preferences = GetWebView()->GetWebPreferences();
view_params->replication_state = blink::mojom::FrameReplicationState::New();
view_params->replication_state->frame_policy.sandbox_flags = sandbox_flags;
view_params->replication_state->name = frame_name_utf8;
view_params->devtools_main_frame_token = reply->devtools_main_frame_token;
view_params->browsing_context_group_info = reply->browsing_context_group_info;
view_params->color_provider_colors = reply->color_provider_colors;
auto main_frame_params = mojom::CreateLocalMainFrameParams::New();
main_frame_params->frame_token = reply->main_frame_token;
main_frame_params->routing_id = reply->main_frame_route_id;
main_frame_params->frame = std::move(reply->frame);
main_frame_params->interface_broker =
std::move(reply->main_frame_interface_broker);
main_frame_params->document_token = reply->document_token;
main_frame_params->policy_container = std::move(reply->policy_container);
main_frame_params->associated_interface_provider_remote =
std::move(reply->associated_interface_provider);
main_frame_params->widget_params = std::move(reply->widget_params);
main_frame_params->subresource_loader_factories =
base::WrapUnique(static_cast<blink::PendingURLLoaderFactoryBundle*>(
CloneLoaderFactories()->Clone().release()));
view_params->main_frame =
mojom::CreateMainFrameUnion::NewLocalParams(std::move(main_frame_params));
view_params->blink_page_broadcast = std::move(reply->page_broadcast);
view_params->session_storage_namespace_id =
reply->cloned_session_storage_namespace_id;
DCHECK(!view_params->session_storage_namespace_id.empty())
<< "Session storage namespace must be populated.";
view_params->hidden = is_background_tab;
view_params->never_composited = never_composited;
view_params->partitioned_popin_params =
std::move(reply->partitioned_popin_params);
WebView* web_view = agent_scheduling_group_->CreateWebView(
std::move(view_params),
/*was_created_by_renderer=*/true, base_url);
if (reply->wait_for_debugger) {
blink::WebFrameWidget* frame_widget =
web_view->MainFrame()->ToWebLocalFrame()->LocalRoot()->FrameWidget();
frame_widget->WaitForDebuggerWhenShown();
}
return web_view;
}
std::unique_ptr<blink::WebLinkPreviewTriggerer>
RenderFrameImpl::CreateLinkPreviewTriggerer() {
return GetContentClient()->renderer()->CreateLinkPreviewTriggerer();
}
base::ScopedClosureRunner
RenderFrameImpl::CreateScopedClientNavigationThrottler() {
return client_navigation_throttler_.DeferNavigations();
}
void RenderFrameImpl::ResetMembersUsedForDurationOfCommit() {
pending_loader_factories_ = nullptr;
pending_code_cache_host_.reset();
pending_cookie_manager_info_.reset();
pending_storage_info_.reset();
is_requesting_navigation_ = false;
}
} // namespace content