// 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/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/permissions_policy/permissions_policy.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_( base::trace_event::TraceLog::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(); base::trace_event::TraceLog::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()) { base::trace_event::TraceLog::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(); base::trace_event::TraceLog::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; } // Everything else is handled asynchronously by the browser process through // BeginNavigation. BeginNavigationInternal( std::move(info), is_history_navigation_in_new_child_frame, renderer_before_unload_start, renderer_before_unload_end); } 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()); if (!frame_->WillStartNavigation(*info)) return; for (auto& observer : observers_) observer.DidStartNavigation(info->url_request.Url(), info->navigation_type); is_requesting_navigation_ = true; // 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(); } 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