| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/render_frame_impl.h" |
| |
| #include <algorithm> |
| #include <map> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/auto_reset.h" |
| #include "base/bind_helpers.h" |
| #include "base/command_line.h" |
| #include "base/debug/alias.h" |
| #include "base/debug/asan_invalid_access.h" |
| #include "base/debug/crash_logging.h" |
| #include "base/debug/dump_without_crashing.h" |
| #include "base/feature_list.h" |
| #include "base/files/file.h" |
| #include "base/guid.h" |
| #include "base/i18n/char_iterator.h" |
| #include "base/json/json_reader.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/shared_memory.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/optional.h" |
| #include "base/process/process.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/post_task.h" |
| #include "base/task_runner_util.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "base/trace_event/trace_event.h" |
| #include "build/build_config.h" |
| #include "cc/base/switches.h" |
| #include "content/common/accessibility_messages.h" |
| #include "content/common/associated_interfaces.mojom.h" |
| #include "content/common/content_constants_internal.h" |
| #include "content/common/content_security_policy/content_security_policy.h" |
| #include "content/common/content_security_policy_header.h" |
| #include "content/common/download/mhtml_save_status.h" |
| #include "content/common/edit_command.h" |
| #include "content/common/frame_messages.h" |
| #include "content/common/frame_owner_properties.h" |
| #include "content/common/frame_replication_state.h" |
| #include "content/common/input_messages.h" |
| #include "content/common/navigation_gesture.h" |
| #include "content/common/navigation_params.h" |
| #include "content/common/page_messages.h" |
| #include "content/common/possibly_associated_wrapper_shared_url_loader_factory.h" |
| #include "content/common/renderer_host.mojom.h" |
| #include "content/common/savable_subframe.h" |
| #include "content/common/service_worker/service_worker_types.h" |
| #include "content/common/swapped_out_messages.h" |
| #include "content/common/view_messages.h" |
| #include "content/public/common/appcache_info.h" |
| #include "content/public/common/bind_interface_helpers.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/context_menu_params.h" |
| #include "content/public/common/favicon_url.h" |
| #include "content/public/common/isolated_world_ids.h" |
| #include "content/public/common/navigation_policy.h" |
| #include "content/public/common/page_state.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "content/public/common/url_constants.h" |
| #include "content/public/common/url_loader_throttle.h" |
| #include "content/public/common/url_utils.h" |
| #include "content/public/renderer/browser_plugin_delegate.h" |
| #include "content/public/renderer/content_renderer_client.h" |
| #include "content/public/renderer/context_menu_client.h" |
| #include "content/public/renderer/document_state.h" |
| #include "content/public/renderer/render_frame_observer.h" |
| #include "content/public/renderer/render_frame_visitor.h" |
| #include "content/public/renderer/renderer_ppapi_host.h" |
| #include "content/renderer/accessibility/aom_content_ax_tree.h" |
| #include "content/renderer/accessibility/render_accessibility_impl.h" |
| #include "content/renderer/appcache/appcache_frontend_impl.h" |
| #include "content/renderer/browser_plugin/browser_plugin.h" |
| #include "content/renderer/browser_plugin/browser_plugin_manager.h" |
| #include "content/renderer/compositor/layer_tree_view.h" |
| #include "content/renderer/content_security_policy_util.h" |
| #include "content/renderer/context_menu_params_builder.h" |
| #include "content/renderer/crash_helpers.h" |
| #include "content/renderer/dom_automation_controller.h" |
| #include "content/renderer/effective_connection_type_helper.h" |
| #include "content/renderer/external_popup_menu.h" |
| #include "content/renderer/frame_owner_properties.h" |
| #include "content/renderer/gpu_benchmarking_extension.h" |
| #include "content/renderer/history_entry.h" |
| #include "content/renderer/history_serialization.h" |
| #include "content/renderer/image_downloader/image_downloader_impl.h" |
| #include "content/renderer/ime_event_guard.h" |
| #include "content/renderer/input/frame_input_handler_impl.h" |
| #include "content/renderer/input/input_target_client_impl.h" |
| #include "content/renderer/input/widget_input_handler_manager.h" |
| #include "content/renderer/installedapp/related_apps_fetcher.h" |
| #include "content/renderer/internal_document_state_data.h" |
| #include "content/renderer/loader/request_extra_data.h" |
| #include "content/renderer/loader/tracked_child_url_loader_factory_bundle.h" |
| #include "content/renderer/loader/web_url_loader_impl.h" |
| #include "content/renderer/loader/web_url_request_util.h" |
| #include "content/renderer/loader/web_worker_fetch_context_impl.h" |
| #include "content/renderer/low_memory_mode_controller.h" |
| #include "content/renderer/manifest/manifest_change_notifier.h" |
| #include "content/renderer/manifest/manifest_manager.h" |
| #include "content/renderer/media/audio/audio_device_factory.h" |
| #include "content/renderer/media/audio/audio_output_ipc_factory.h" |
| #include "content/renderer/media/audio/audio_renderer_sink_cache.h" |
| #include "content/renderer/media/media_permission_dispatcher.h" |
| #include "content/renderer/media/stream/media_stream_device_observer.h" |
| #include "content/renderer/media/stream/user_media_client_impl.h" |
| #include "content/renderer/media/webrtc/rtc_peer_connection_handler.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/pepper/plugin_instance_throttler_impl.h" |
| #include "content/renderer/push_messaging/push_messaging_client.h" |
| #include "content/renderer/render_frame_proxy.h" |
| #include "content/renderer/render_process.h" |
| #include "content/renderer/render_thread_impl.h" |
| #include "content/renderer/render_view_impl.h" |
| #include "content/renderer/render_widget_fullscreen_pepper.h" |
| #include "content/renderer/renderer_blink_platform_impl.h" |
| #include "content/renderer/renderer_webapplicationcachehost_impl.h" |
| #include "content/renderer/resource_timing_info_conversions.h" |
| #include "content/renderer/savable_resources.h" |
| #include "content/renderer/service_worker/service_worker_network_provider.h" |
| #include "content/renderer/service_worker/service_worker_provider_context.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 "crypto/sha2.h" |
| #include "media/blink/webmediaplayer_util.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/network/public/cpp/features.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/network/public/mojom/request_context_frame_type.mojom.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "services/service_manager/public/cpp/interface_provider.h" |
| #include "services/service_manager/public/mojom/interface_provider.mojom.h" |
| #include "services/ws/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/frame/sandbox_flags.h" |
| #include "third_party/blink/public/common/frame/user_activation_update_type.h" |
| #include "third_party/blink/public/common/service_worker/service_worker_utils.h" |
| #include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h" |
| #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h" |
| #include "third_party/blink/public/platform/file_path_conversion.h" |
| #include "third_party/blink/public/platform/interface_provider.h" |
| #include "third_party/blink/public/platform/modules/permissions/permission.mojom.h" |
| #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h" |
| #include "third_party/blink/public/platform/url_conversion.h" |
| #include "third_party/blink/public/platform/web_data.h" |
| #include "third_party/blink/public/platform/web_focus_type.h" |
| #include "third_party/blink/public/platform/web_http_body.h" |
| #include "third_party/blink/public/platform/web_keyboard_event.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_point.h" |
| #include "third_party/blink/public/platform/web_scroll_into_view_params.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_response.h" |
| #include "third_party/blink/public/platform/web_vector.h" |
| #include "third_party/blink/public/web/blink.h" |
| #include "third_party/blink/public/web/web_autofill_client.h" |
| #include "third_party/blink/public/web/web_console_message.h" |
| #include "third_party/blink/public/web/web_context_features.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_serializer_cache_control_policy.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_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_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_scoped_user_gesture.h" |
| #include "third_party/blink/public/web/web_script_source.h" |
| #include "third_party/blink/public/web/web_searchable_form_data.h" |
| #include "third_party/blink/public/web/web_security_policy.h" |
| #include "third_party/blink/public/web/web_serialized_script_value.h" |
| #include "third_party/blink/public/web/web_settings.h" |
| #include "third_party/blink/public/web/web_surrounding_text.h" |
| #include "third_party/blink/public/web/web_user_gesture_indicator.h" |
| #include "third_party/blink/public/web/web_view.h" |
| #include "third_party/blink/public/web/web_widget.h" |
| #include "ui/events/base_event_utils.h" |
| #include "url/origin.h" |
| #include "url/url_constants.h" |
| #include "url/url_util.h" |
| #include "v8/include/v8.h" |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| #include "content/renderer/pepper/pepper_browser_connection.h" |
| #include "content/renderer/pepper/pepper_plugin_instance_impl.h" |
| #include "content/renderer/pepper/pepper_plugin_registry.h" |
| #include "content/renderer/pepper/pepper_webplugin_impl.h" |
| #include "content/renderer/pepper/plugin_module.h" |
| #endif |
| |
| #if defined(OS_WIN) |
| #include "base/debug/invalid_access_win.h" |
| #include "base/process/kill.h" |
| #elif defined(OS_POSIX) |
| #include <signal.h> |
| #endif |
| |
| #if defined(OS_ANDROID) |
| #include <cpu-features.h> |
| |
| #include "content/renderer/java/gin_java_bridge_dispatcher.h" |
| #include "third_party/blink/public/platform/web_float_point.h" |
| #endif |
| |
| using base::Time; |
| using base::TimeDelta; |
| using blink::WebContentDecryptionModule; |
| using blink::WebContextMenuData; |
| using blink::WebData; |
| using blink::WebDocument; |
| using blink::WebDocumentLoader; |
| using blink::WebDOMEvent; |
| using blink::WebDOMMessageEvent; |
| using blink::WebElement; |
| using blink::WebElementCollection; |
| using blink::WebExternalPopupMenu; |
| using blink::WebExternalPopupMenuClient; |
| using blink::WebFrame; |
| using blink::WebFrameLoadType; |
| using blink::WebFrameSerializer; |
| using blink::WebFrameSerializerClient; |
| using blink::WebHistoryItem; |
| using blink::WebHTTPBody; |
| using blink::WebLayerTreeView; |
| 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::WebPoint; |
| using blink::WebPopupMenuInfo; |
| using blink::WebRange; |
| using blink::WebRect; |
| using blink::WebScriptSource; |
| using blink::WebSearchableFormData; |
| using blink::WebSecurityOrigin; |
| using blink::WebSecurityPolicy; |
| using blink::WebSerializedScriptValue; |
| using blink::WebServiceWorkerProvider; |
| using blink::WebSettings; |
| using blink::WebString; |
| using blink::WebThreadSafeData; |
| using blink::WebURL; |
| using blink::WebURLError; |
| using blink::WebURLRequest; |
| using blink::WebURLResponse; |
| using blink::WebUserGestureIndicator; |
| using blink::WebVector; |
| using blink::WebView; |
| using blink::mojom::SelectionMenuBehavior; |
| using network::mojom::ReferrerPolicy; |
| |
| #if defined(OS_ANDROID) |
| using blink::WebFloatPoint; |
| using blink::WebFloatRect; |
| #endif |
| |
| namespace content { |
| |
| namespace { |
| |
| const int kExtraCharsBeforeAndAfterSelection = 100; |
| |
| // Maximum number of burst download requests allowed. |
| const int kBurstDownloadLimit = 10; |
| |
| const PreviewsState kDisabledPreviewsBits = |
| PREVIEWS_OFF | PREVIEWS_NO_TRANSFORM; |
| |
| // Print up to |kMaxSecurityWarningMessages| console messages per frame about |
| // certificates or TLS versions that will be distrusted in future. |
| const uint32_t kMaxSecurityWarningMessages = 10; |
| |
| typedef std::map<int, RenderFrameImpl*> RoutingIDFrameMap; |
| static base::LazyInstance<RoutingIDFrameMap>::DestructorAtExit |
| g_routing_id_frame_map = LAZY_INSTANCE_INITIALIZER; |
| |
| typedef std::map<blink::WebFrame*, RenderFrameImpl*> FrameMap; |
| base::LazyInstance<FrameMap>::DestructorAtExit g_frame_map = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| int64_t ExtractPostId(const WebHistoryItem& item) { |
| if (item.IsNull() || item.HttpBody().IsNull()) |
| return -1; |
| |
| return item.HttpBody().Identifier(); |
| } |
| |
| ui::PageTransition GetTransitionType(blink::WebDocumentLoader* document_loader, |
| blink::WebLocalFrame* frame, |
| bool loading) { |
| NavigationState* navigation_state = |
| NavigationState::FromDocumentLoader(document_loader); |
| ui::PageTransition default_transition = |
| navigation_state->IsContentInitiated() |
| ? ui::PAGE_TRANSITION_LINK |
| : navigation_state->common_params().transition; |
| if (navigation_state->WasWithinSameDocument()) |
| return default_transition; |
| if (loading || document_loader->GetResponse().IsNull()) { |
| if (document_loader->ReplacesCurrentHistoryItem() && frame->Parent()) { |
| // 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 = document_loader->GetNavigationType() == |
| blink::kWebNavigationTypeFormSubmitted || |
| document_loader->GetNavigationType() == |
| blink::kWebNavigationTypeFormResubmitted; |
| if (ui::PageTransitionCoreTypeIs(default_transition, |
| ui::PAGE_TRANSITION_LINK) && |
| is_form_submit) { |
| return ui::PAGE_TRANSITION_FORM_SUBMIT; |
| } |
| } |
| return default_transition; |
| } |
| |
| void GetRedirectChain(WebDocumentLoader* document_loader, |
| std::vector<GURL>* result) { |
| WebVector<WebURL> urls; |
| document_loader->RedirectChain(urls); |
| result->reserve(urls.size()); |
| for (size_t i = 0; i < urls.size(); ++i) { |
| result->push_back(urls[i]); |
| } |
| } |
| |
| // Gets URL that should override the default getter for this data source |
| // (if any), storing it in |output|. Returns true if there is an override URL. |
| bool MaybeGetOverriddenURL(WebDocumentLoader* document_loader, GURL* output) { |
| DocumentState* document_state = |
| DocumentState::FromDocumentLoader(document_loader); |
| |
| // If load was from a data URL, then the saved data URL, not the history |
| // URL, should be the URL of the data source. |
| if (document_state->was_load_data_with_base_url_request()) { |
| *output = document_state->data_url(); |
| return true; |
| } |
| |
| // WebDocumentLoader has unreachable URL means that the frame is loaded |
| // through blink::WebFrame::loadData(), and the base URL will be in the |
| // redirect chain. However, we never visited the baseURL. So in this case, we |
| // should use the unreachable URL as the original URL. |
| if (document_loader->HasUnreachableURL()) { |
| *output = document_loader->UnreachableURL(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // Returns the original request url. If there is no redirect, the original |
| // url is the same as document loader's OriginalUrl(). If the WebDocumentLoader |
| // belongs to a frame was loaded by loadData, the original url will be |
| // it's UnreachableURL(). |
| GURL GetOriginalRequestURL(WebDocumentLoader* document_loader) { |
| GURL overriden_url; |
| if (MaybeGetOverriddenURL(document_loader, &overriden_url)) |
| return overriden_url; |
| |
| std::vector<GURL> redirects; |
| GetRedirectChain(document_loader, &redirects); |
| if (!redirects.empty()) |
| return redirects.at(0); |
| |
| return document_loader->OriginalUrl(); |
| } |
| |
| // Returns false unless this is a top-level navigation. |
| bool IsTopLevelNavigation(WebFrame* frame) { |
| return frame->Parent() == nullptr; |
| } |
| |
| WebURLRequest CreateURLRequestForNavigation( |
| const CommonNavigationParams& common_params, |
| const CommitNavigationParams& commit_params, |
| std::unique_ptr<NavigationResponseOverrideParameters> response_override, |
| bool is_view_source_mode_enabled) { |
| // Use the original navigation url to construct the WebURLRequest. The |
| // WebURLloaderImpl will replay the redirects afterwards and will eventually |
| // commit the final url. |
| const GURL navigation_url = !commit_params.original_url.is_empty() |
| ? commit_params.original_url |
| : common_params.url; |
| const std::string navigation_method = !commit_params.original_method.empty() |
| ? commit_params.original_method |
| : common_params.method; |
| WebURLRequest request(navigation_url); |
| request.SetHTTPMethod(WebString::FromUTF8(navigation_method)); |
| |
| if (is_view_source_mode_enabled) |
| request.SetCacheMode(blink::mojom::FetchCacheMode::kForceCache); |
| |
| WebString web_referrer; |
| if (common_params.referrer.url.is_valid()) { |
| web_referrer = WebSecurityPolicy::GenerateReferrerHeader( |
| common_params.referrer.policy, common_params.url, |
| WebString::FromUTF8(common_params.referrer.url.spec())); |
| request.SetHTTPReferrer(web_referrer, common_params.referrer.policy); |
| if (!web_referrer.IsEmpty()) { |
| request.SetHTTPOriginIfNeeded( |
| WebSecurityOrigin(url::Origin::Create(common_params.referrer.url))); |
| } |
| } |
| |
| if (common_params.post_data) { |
| request.SetHTTPBody(GetWebHTTPBodyForRequestBody(*common_params.post_data)); |
| if (!commit_params.post_content_type.empty()) { |
| request.AddHTTPHeaderField( |
| WebString::FromASCII(net::HttpRequestHeaders::kContentType), |
| WebString::FromASCII(commit_params.post_content_type)); |
| } |
| } |
| |
| if (!web_referrer.IsEmpty() || common_params.referrer.policy != |
| network::mojom::ReferrerPolicy::kDefault) { |
| request.SetHTTPReferrer(web_referrer, common_params.referrer.policy); |
| } |
| |
| request.SetPreviewsState( |
| static_cast<WebURLRequest::PreviewsState>(common_params.previews_state)); |
| |
| request.SetOriginPolicy(WebString::FromUTF8(common_params.origin_policy)); |
| |
| // Set the request initiator origin, which is supplied by the browser |
| // process. It is present in cases such as navigating a frame in a different |
| // process, which is routed through RenderFrameProxy and the origin is |
| // required to correctly compute the effective origin in which the |
| // navigation will commit. |
| if (common_params.initiator_origin) |
| request.SetRequestorOrigin(common_params.initiator_origin.value()); |
| |
| auto extra_data = std::make_unique<RequestExtraData>(); |
| extra_data->set_navigation_response_override(std::move(response_override)); |
| extra_data->set_navigation_initiated_by_renderer(commit_params.nav_entry_id == |
| 0); |
| request.SetExtraData(std::move(extra_data)); |
| request.SetWasDiscarded(commit_params.was_discarded); |
| |
| return request; |
| } |
| |
| CommonNavigationParams MakeCommonNavigationParams( |
| const WebSecurityOrigin& current_origin, |
| std::unique_ptr<blink::WebNavigationInfo> info, |
| int load_flags, |
| bool prevent_sandboxed_download) { |
| // A valid RequestorOrigin is always expected to be present. |
| DCHECK(!info->url_request.RequestorOrigin().IsNull()); |
| |
| Referrer referrer( |
| GURL(info->url_request.HttpHeaderField(WebString::FromUTF8("Referer")) |
| .Latin1()), |
| 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. |
| FrameMsg_Navigate_Type::Value navigation_type = |
| FrameMsg_Navigate_Type::DIFFERENT_DOCUMENT; |
| if (info->navigation_type == blink::kWebNavigationTypeReload) { |
| if (load_flags & net::LOAD_BYPASS_CACHE) |
| navigation_type = FrameMsg_Navigate_Type::RELOAD_BYPASSING_CACHE; |
| else |
| navigation_type = FrameMsg_Navigate_Type::RELOAD; |
| } |
| |
| base::Optional<SourceLocation> source_location; |
| if (!info->source_location.url.IsNull()) { |
| source_location = SourceLocation(info->source_location.url.Latin1(), |
| info->source_location.line_number, |
| info->source_location.column_number); |
| } |
| |
| CSPDisposition should_check_main_world_csp = |
| info->should_check_main_world_content_security_policy == |
| blink::kWebContentSecurityPolicyDispositionCheck |
| ? CSPDisposition::CHECK |
| : CSPDisposition::DO_NOT_CHECK; |
| |
| const RequestExtraData* extra_data = |
| static_cast<RequestExtraData*>(info->url_request.GetExtraData()); |
| DCHECK(extra_data); |
| NavigationDownloadPolicy download_policy = |
| prevent_sandboxed_download |
| ? NavigationDownloadPolicy::kDisallowSandbox |
| : RenderFrameImpl::GetOpenerDownloadPolicy( |
| info->is_opener_navigation, info->url_request, current_origin); |
| return CommonNavigationParams( |
| info->url_request.Url(), info->url_request.RequestorOrigin(), referrer, |
| extra_data->transition_type(), navigation_type, download_policy, |
| info->frame_load_type == WebFrameLoadType::kReplaceCurrentItem, GURL(), |
| GURL(), static_cast<PreviewsState>(info->url_request.GetPreviewsState()), |
| base::TimeTicks::Now(), info->url_request.HttpMethod().Latin1(), |
| GetRequestBodyForWebURLRequest(info->url_request), source_location, |
| false /* started_from_context_menu */, info->url_request.HasUserGesture(), |
| InitiatorCSPInfo( |
| should_check_main_world_csp, |
| BuildContentSecurityPolicyList(info->url_request.GetInitiatorCSP()), |
| info->url_request.GetInitiatorCSP().self_source.has_value() |
| ? base::Optional<CSPSource>(BuildCSPSource( |
| info->url_request.GetInitiatorCSP().self_source.value())) |
| : base::nullopt), |
| info->href_translate.Latin1(), info->input_start); |
| } |
| |
| WebFrameLoadType NavigationTypeToLoadType( |
| FrameMsg_Navigate_Type::Value navigation_type, |
| bool should_replace_current_entry, |
| bool has_valid_page_state) { |
| switch (navigation_type) { |
| case FrameMsg_Navigate_Type::RELOAD: |
| case FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL: |
| return WebFrameLoadType::kReload; |
| |
| case FrameMsg_Navigate_Type::RELOAD_BYPASSING_CACHE: |
| return WebFrameLoadType::kReloadBypassingCache; |
| |
| case FrameMsg_Navigate_Type::HISTORY_SAME_DOCUMENT: |
| case FrameMsg_Navigate_Type::HISTORY_DIFFERENT_DOCUMENT: |
| return WebFrameLoadType::kBackForward; |
| |
| case FrameMsg_Navigate_Type::RESTORE: |
| case FrameMsg_Navigate_Type::RESTORE_WITH_POST: |
| if (has_valid_page_state) |
| return WebFrameLoadType::kBackForward; |
| // If there is no valid page state, fall through to the default case. |
| FALLTHROUGH; |
| |
| case FrameMsg_Navigate_Type::SAME_DOCUMENT: |
| case FrameMsg_Navigate_Type::DIFFERENT_DOCUMENT: |
| return should_replace_current_entry |
| ? WebFrameLoadType::kReplaceCurrentItem |
| : WebFrameLoadType::kStandard; |
| |
| default: |
| NOTREACHED(); |
| return WebFrameLoadType::kStandard; |
| } |
| } |
| |
| RenderFrameImpl::CreateRenderFrameImplFunction g_create_render_frame_impl = |
| nullptr; |
| RenderFrameImpl::CreateRenderWidgetForChildLocalRootFunction |
| g_create_render_widget = nullptr; |
| RenderFrameImpl::RenderWidgetForChildLocalRootInitializedCallback |
| g_render_widget_initialized = nullptr; |
| |
| WebString ConvertRelativePathToHtmlAttribute(const base::FilePath& path) { |
| DCHECK(!path.IsAbsolute()); |
| return WebString::FromUTF8( |
| std::string("./") + |
| path.NormalizePathSeparatorsTo(FILE_PATH_LITERAL('/')).AsUTF8Unsafe()); |
| } |
| |
| // Implementation of WebFrameSerializer::LinkRewritingDelegate that responds |
| // based on the payload of FrameMsg_GetSerializedHtmlWithLocalLinks. |
| class LinkRewritingDelegate : public WebFrameSerializer::LinkRewritingDelegate { |
| public: |
| LinkRewritingDelegate( |
| const std::map<GURL, base::FilePath>& url_to_local_path, |
| const std::map<int, base::FilePath>& frame_routing_id_to_local_path) |
| : url_to_local_path_(url_to_local_path), |
| frame_routing_id_to_local_path_(frame_routing_id_to_local_path) {} |
| |
| bool RewriteFrameSource(WebFrame* frame, WebString* rewritten_link) override { |
| int routing_id = RenderFrame::GetRoutingIdForWebFrame(frame); |
| auto it = frame_routing_id_to_local_path_.find(routing_id); |
| if (it == frame_routing_id_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(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 std::map<GURL, base::FilePath>& url_to_local_path_; |
| const std::map<int, base::FilePath>& frame_routing_id_to_local_path_; |
| }; |
| |
| // Implementation of WebFrameSerializer::MHTMLPartsGenerationDelegate that |
| // 1. Bases shouldSkipResource and getContentID responses on contents of |
| // FrameMsg_SerializeAsMHTML_Params. |
| // 2. Stores digests of urls of serialized resources (i.e. urls reported via |
| // shouldSkipResource) into |serialized_resources_uri_digests| passed |
| // to the constructor. |
| class MHTMLPartsGenerationDelegate |
| : public WebFrameSerializer::MHTMLPartsGenerationDelegate { |
| public: |
| MHTMLPartsGenerationDelegate( |
| const FrameMsg_SerializeAsMHTML_Params& params, |
| std::set<std::string>* serialized_resources_uri_digests) |
| : params_(params), |
| serialized_resources_uri_digests_(serialized_resources_uri_digests) { |
| DCHECK(serialized_resources_uri_digests_); |
| } |
| |
| 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 (base::ContainsKey(params_.digests_of_uris_to_skip, 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; |
| } |
| |
| blink::WebFrameSerializerCacheControlPolicy CacheControlPolicy() override { |
| return params_.mhtml_cache_control_policy; |
| } |
| |
| bool UseBinaryEncoding() override { return params_.mhtml_binary_encoding; } |
| |
| bool RemovePopupOverlay() override { |
| return params_.mhtml_popup_overlay_removal; |
| } |
| |
| bool UsePageProblemDetectors() override { |
| return params_.mhtml_problem_detection; |
| } |
| |
| private: |
| const FrameMsg_SerializeAsMHTML_Params& params_; |
| std::set<std::string>* serialized_resources_uri_digests_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MHTMLPartsGenerationDelegate); |
| }; |
| |
| bool IsHttpPost(const blink::WebURLRequest& request) { |
| return request.HttpMethod().Utf8() == "POST"; |
| } |
| |
| // Writes to file the serialized and encoded MHTML data from WebThreadSafeData |
| // instances. |
| MhtmlSaveStatus WriteMHTMLToDisk(std::vector<WebThreadSafeData> mhtml_contents, |
| base::File file) { |
| TRACE_EVENT0("page-serialization", "WriteMHTMLToDisk (RenderFrameImpl)"); |
| SCOPED_UMA_HISTOGRAM_TIMER( |
| "PageSerialization.MhtmlGeneration.WriteToDiskTime.SingleFrame"); |
| DCHECK(!RenderThread::Get()) << "Should not run in the main renderer thread"; |
| MhtmlSaveStatus save_status = MhtmlSaveStatus::SUCCESS; |
| for (const WebThreadSafeData& data : mhtml_contents) { |
| if (!data.IsEmpty() && |
| file.WriteAtCurrentPos(data.Data(), data.size()) < 0) { |
| save_status = MhtmlSaveStatus::FILE_WRITTING_ERROR; |
| break; |
| } |
| } |
| // Explicitly close |file| here to make sure to include any flush operations |
| // in the UMA metric. |
| file.Close(); |
| return save_status; |
| } |
| |
| FaviconURL::IconType ToFaviconType(blink::WebIconURL::Type type) { |
| switch (type) { |
| case blink::WebIconURL::kTypeFavicon: |
| return FaviconURL::IconType::kFavicon; |
| case blink::WebIconURL::kTypeTouch: |
| return FaviconURL::IconType::kTouchIcon; |
| case blink::WebIconURL::kTypeTouchPrecomposed: |
| return FaviconURL::IconType::kTouchPrecomposedIcon; |
| case blink::WebIconURL::kTypeInvalid: |
| return FaviconURL::IconType::kInvalid; |
| } |
| NOTREACHED(); |
| return FaviconURL::IconType::kInvalid; |
| } |
| |
| std::vector<gfx::Size> ConvertToFaviconSizes( |
| const blink::WebVector<blink::WebSize>& web_sizes) { |
| std::vector<gfx::Size> result; |
| result.reserve(web_sizes.size()); |
| for (const blink::WebSize& web_size : web_sizes) |
| result.push_back(gfx::Size(web_size)); |
| return result; |
| } |
| |
| // Use this for histograms with dynamically generated names, which otherwise |
| // can't use the UMA_HISTOGRAM_MEMORY_MB macro without code duplication. |
| void RecordSuffixedMemoryMBHistogram(base::StringPiece name, |
| base::StringPiece suffix, |
| int sample_mb) { |
| std::string name_with_suffix; |
| name.CopyToString(&name_with_suffix); |
| suffix.AppendToString(&name_with_suffix); |
| base::UmaHistogramMemoryMB(name_with_suffix, sample_mb); |
| } |
| |
| void RecordSuffixedRendererMemoryMetrics( |
| const RenderThreadImpl::RendererMemoryMetrics& memory_metrics, |
| base::StringPiece suffix) { |
| RecordSuffixedMemoryMBHistogram("Memory.Experimental.Renderer.PartitionAlloc", |
| suffix, |
| memory_metrics.partition_alloc_kb / 1024); |
| RecordSuffixedMemoryMBHistogram("Memory.Experimental.Renderer.BlinkGC", |
| suffix, memory_metrics.blink_gc_kb / 1024); |
| RecordSuffixedMemoryMBHistogram("Memory.Experimental.Renderer.Malloc", suffix, |
| memory_metrics.malloc_mb); |
| RecordSuffixedMemoryMBHistogram("Memory.Experimental.Renderer.Discardable", |
| suffix, memory_metrics.discardable_kb / 1024); |
| RecordSuffixedMemoryMBHistogram( |
| "Memory.Experimental.Renderer.V8MainThreadIsolate", suffix, |
| memory_metrics.v8_main_thread_isolate_mb); |
| RecordSuffixedMemoryMBHistogram("Memory.Experimental.Renderer.TotalAllocated", |
| suffix, memory_metrics.total_allocated_mb); |
| RecordSuffixedMemoryMBHistogram( |
| "Memory.Experimental.Renderer.NonDiscardableTotalAllocated", suffix, |
| memory_metrics.non_discardable_total_allocated_mb); |
| RecordSuffixedMemoryMBHistogram( |
| "Memory.Experimental.Renderer.TotalAllocatedPerRenderView", suffix, |
| memory_metrics.total_allocated_per_render_view_mb); |
| } |
| |
| // See also LOG_NAVIGATION_TIMING_HISTOGRAM in NavigationHandleImpl. |
| void RecordReadyToCommitUntilCommitHistogram(base::TimeDelta delay, |
| ui::PageTransition transition) { |
| UMA_HISTOGRAM_TIMES("Navigation.Renderer.ReadyToCommitUntilCommit", delay); |
| if (transition & ui::PAGE_TRANSITION_FORWARD_BACK) { |
| UMA_HISTOGRAM_TIMES( |
| "Navigation.Renderer.ReadyToCommitUntilCommit.BackForward", delay); |
| } else if (ui::PageTransitionCoreTypeIs(transition, |
| ui::PAGE_TRANSITION_RELOAD)) { |
| UMA_HISTOGRAM_TIMES("Navigation.Renderer.ReadyToCommitUntilCommit.Reload", |
| delay); |
| } else if (ui::PageTransitionIsNewNavigation(transition)) { |
| UMA_HISTOGRAM_TIMES( |
| "Navigation.Renderer.ReadyToCommitUntilCommit.NewNavigation", delay); |
| } else { |
| NOTREACHED() << "Invalid page transition: " << transition; |
| } |
| } |
| |
| blink::mojom::BlobURLTokenPtrInfo CloneBlobURLToken( |
| mojo::MessagePipeHandle handle) { |
| if (!handle.is_valid()) |
| return nullptr; |
| blink::mojom::BlobURLTokenPtrInfo result; |
| blink::mojom::BlobURLTokenPtr token( |
| blink::mojom::BlobURLTokenPtrInfo(mojo::ScopedMessagePipeHandle(handle), |
| blink::mojom::BlobURLToken::Version_)); |
| token->Clone(MakeRequest(&result)); |
| ignore_result(token.PassInterface().PassHandle().release()); |
| return result; |
| } |
| |
| // Creates a fully functional DocumentState in the case where we do not have |
| // navigation parameters available. |
| std::unique_ptr<DocumentState> BuildDocumentState() { |
| std::unique_ptr<DocumentState> document_state = |
| std::make_unique<DocumentState>(); |
| InternalDocumentStateData::FromDocumentState(document_state.get()) |
| ->set_navigation_state(NavigationState::CreateContentInitiated()); |
| 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 CommonNavigationParams& common_params, |
| const CommitNavigationParams& commit_params, |
| base::TimeTicks time_commit_requested, |
| mojom::FrameNavigationControl::CommitNavigationCallback commit_callback, |
| const network::ResourceResponseHead* head, |
| std::unique_ptr<NavigationClient> navigation_client) { |
| std::unique_ptr<DocumentState> document_state(new DocumentState()); |
| InternalDocumentStateData* internal_data = |
| InternalDocumentStateData::FromDocumentState(document_state.get()); |
| |
| DCHECK(!common_params.navigation_start.is_null()); |
| DCHECK(!common_params.url.SchemeIs(url::kJavaScriptScheme)); |
| |
| if (common_params.navigation_type == FrameMsg_Navigate_Type::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". |
| internal_data->set_cache_policy_override( |
| blink::mojom::FetchCacheMode::kDefault); |
| } |
| |
| internal_data->set_is_overriding_user_agent( |
| commit_params.is_overriding_user_agent); |
| internal_data->set_must_reset_scroll_and_scale_state( |
| common_params.navigation_type == |
| FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL); |
| internal_data->set_previews_state(common_params.previews_state); |
| document_state->set_can_load_local_resources( |
| commit_params.can_load_local_resources); |
| |
| if (head) { |
| if (head->headers) |
| internal_data->set_http_status_code(head->headers->response_code()); |
| else if (common_params.url.SchemeIs(url::kDataScheme)) |
| internal_data->set_http_status_code(200); |
| document_state->set_was_fetched_via_spdy(head->was_fetched_via_spdy); |
| document_state->set_was_alpn_negotiated(head->was_alpn_negotiated); |
| document_state->set_alpn_negotiated_protocol( |
| head->alpn_negotiated_protocol); |
| document_state->set_was_alternate_protocol_available( |
| head->was_alternate_protocol_available); |
| document_state->set_connection_info(head->connection_info); |
| internal_data->set_effective_connection_type( |
| head->effective_connection_type); |
| } |
| |
| bool load_data = !common_params.base_url_for_data_url.is_empty() && |
| !common_params.history_url_for_data_url.is_empty() && |
| common_params.url.SchemeIs(url::kDataScheme); |
| document_state->set_was_load_data_with_base_url_request(load_data); |
| if (load_data) |
| document_state->set_data_url(common_params.url); |
| |
| InternalDocumentStateData::FromDocumentState(document_state.get()) |
| ->set_navigation_state(NavigationState::CreateBrowserInitiated( |
| common_params, commit_params, time_commit_requested, |
| std::move(commit_callback), std::move(navigation_client))); |
| return document_state; |
| } |
| |
| void ApplyFilePathAlias(blink::WebURLRequest* request) { |
| const base::CommandLine::StringType file_url_path_alias = |
| base::CommandLine::ForCurrentProcess()->GetSwitchValueNative( |
| switches::kFileUrlPathAlias); |
| if (file_url_path_alias.empty()) |
| return; |
| |
| const auto alias_mapping = |
| base::SplitString(file_url_path_alias, FILE_PATH_LITERAL("="), |
| base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| if (alias_mapping.size() != 2) { |
| LOG(ERROR) << "Invalid file path alias format."; |
| return; |
| } |
| |
| #if defined(OS_WIN) |
| base::string16 path = request->Url().GetString().Utf16(); |
| const base::string16 file_prefix = |
| base::ASCIIToUTF16(url::kFileScheme) + |
| base::ASCIIToUTF16(url::kStandardSchemeSeparator); |
| #else |
| std::string path = request->Url().GetString().Utf8(); |
| const std::string file_prefix = |
| std::string(url::kFileScheme) + url::kStandardSchemeSeparator; |
| #endif |
| if (!base::StartsWith(path, file_prefix + alias_mapping[0], |
| base::CompareCase::SENSITIVE)) { |
| return; |
| } |
| |
| base::ReplaceFirstSubstringAfterOffset(&path, 0, alias_mapping[0], |
| alias_mapping[1]); |
| request->SetURL(blink::WebURL(GURL(path))); |
| } |
| |
| // Packs all navigation timings sent by the browser to a blink understandable |
| // format, blink::WebNavigationTimings. |
| blink::WebNavigationTimings BuildNavigationTimings( |
| base::TimeTicks navigation_start, |
| const NavigationTiming& browser_navigation_timings, |
| base::TimeTicks input_start) { |
| blink::WebNavigationTimings renderer_navigation_timings; |
| |
| // Sanitizes the navigation_start timestamp for browser-initiated navigations, |
| // where the browser possibly has a better notion of start time than the |
| // renderer. In the case of cross-process navigations, this carries over the |
| // time of finishing the onbeforeunload handler of the previous page. |
| // TimeTicks is sometimes not monotonic across processes, and because |
| // |browser_navigation_start| is likely before this process existed, |
| // InterProcessTimeTicksConverter won't help. The timestamp is sanitized by |
| // clamping it to now. |
| DCHECK(!navigation_start.is_null()); |
| renderer_navigation_timings.navigation_start = |
| std::min(navigation_start, base::TimeTicks::Now()); |
| |
| renderer_navigation_timings.redirect_start = |
| browser_navigation_timings.redirect_start; |
| renderer_navigation_timings.redirect_end = |
| browser_navigation_timings.redirect_end; |
| renderer_navigation_timings.fetch_start = |
| browser_navigation_timings.fetch_start; |
| |
| renderer_navigation_timings.input_start = input_start; |
| |
| return renderer_navigation_timings; |
| } |
| |
| // Fills navigation data sent by the browser to a blink understandable |
| // format, blink::WebNavigationParams. |
| void FillNavigationParams(const CommonNavigationParams& common_params, |
| const 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 (common_params.source_location.has_value()) { |
| blink::WebSourceLocation source_location; |
| source_location.url = |
| WebString::FromLatin1(common_params.source_location->url); |
| source_location.line_number = common_params.source_location->line_number; |
| source_location.column_number = |
| common_params.source_location->column_number; |
| navigation_params->source_location = source_location; |
| } |
| |
| navigation_params->is_user_activated = |
| commit_params.was_activated == WasActivatedOption::kYes; |
| |
| if (commit_params.origin_to_commit) { |
| navigation_params->origin_to_commit = |
| commit_params.origin_to_commit.value(); |
| } |
| } |
| |
| } // namespace |
| |
| class RenderFrameImpl::FrameURLLoaderFactory |
| : public blink::WebURLLoaderFactory { |
| public: |
| explicit FrameURLLoaderFactory(base::WeakPtr<RenderFrameImpl> frame) |
| : frame_(std::move(frame)) {} |
| |
| ~FrameURLLoaderFactory() override = default; |
| |
| std::unique_ptr<blink::WebURLLoader> CreateURLLoader( |
| const WebURLRequest& request, |
| std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle> |
| task_runner_handle) override { |
| // This should not be called if the frame is detached. |
| DCHECK(frame_); |
| |
| mojom::KeepAliveHandlePtr keep_alive_handle; |
| if (request.GetKeepalive()) { |
| frame_->GetFrameHost()->IssueKeepAliveHandle( |
| mojo::MakeRequest(&keep_alive_handle)); |
| } |
| return std::make_unique<WebURLLoaderImpl>( |
| RenderThreadImpl::current()->resource_dispatcher(), |
| std::move(task_runner_handle), frame_->GetLoaderFactoryBundle(), |
| std::move(keep_alive_handle)); |
| } |
| |
| private: |
| base::WeakPtr<RenderFrameImpl> frame_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FrameURLLoaderFactory); |
| }; |
| |
| // The following methods are outside of the anonymous namespace to ensure that |
| // the corresponding symbols get emmitted even on symbol_level 1. |
| NOINLINE void ExhaustMemory() { |
| volatile void* ptr = nullptr; |
| do { |
| ptr = malloc(0x10000000); |
| base::debug::Alias(&ptr); |
| } while (ptr); |
| } |
| |
| #if defined(ADDRESS_SANITIZER) |
| NOINLINE void MaybeTriggerAsanError(const GURL& url) { |
| // NOTE(rogerm): We intentionally perform an invalid heap access here in |
| // order to trigger an Address Sanitizer (ASAN) error report. |
| if (url == kChromeUICrashHeapOverflowURL) { |
| LOG(ERROR) << "Intentionally causing ASAN heap overflow" |
| << " because user navigated to " << url.spec(); |
| base::debug::AsanHeapOverflow(); |
| } else if (url == kChromeUICrashHeapUnderflowURL) { |
| LOG(ERROR) << "Intentionally causing ASAN heap underflow" |
| << " because user navigated to " << url.spec(); |
| base::debug::AsanHeapUnderflow(); |
| } else if (url == kChromeUICrashUseAfterFreeURL) { |
| LOG(ERROR) << "Intentionally causing ASAN heap use-after-free" |
| << " because user navigated to " << url.spec(); |
| base::debug::AsanHeapUseAfterFree(); |
| #if defined(OS_WIN) |
| } else if (url == kChromeUICrashCorruptHeapBlockURL) { |
| LOG(ERROR) << "Intentionally causing ASAN corrupt heap block" |
| << " because user navigated to " << url.spec(); |
| base::debug::AsanCorruptHeapBlock(); |
| } else if (url == kChromeUICrashCorruptHeapURL) { |
| LOG(ERROR) << "Intentionally causing ASAN corrupt heap" |
| << " because user navigated to " << url.spec(); |
| base::debug::AsanCorruptHeap(); |
| #endif // OS_WIN |
| } |
| } |
| #endif // ADDRESS_SANITIZER |
| |
| // Returns true if the URL is a debug URL, false otherwise. These URLs do not |
| // commit, though they are intentionally left in the address bar above the |
| // effect they cause (e.g., a sad tab). |
| void HandleChromeDebugURL(const GURL& url) { |
| DCHECK(IsRendererDebugURL(url) && !url.SchemeIs(url::kJavaScriptScheme)); |
| if (url == kChromeUIBadCastCrashURL) { |
| LOG(ERROR) << "Intentionally crashing (with bad cast)" |
| << " because user navigated to " << url.spec(); |
| internal::BadCastCrashIntentionally(); |
| } else if (url == kChromeUICrashURL) { |
| LOG(ERROR) << "Intentionally crashing (with null pointer dereference)" |
| << " because user navigated to " << url.spec(); |
| internal::CrashIntentionally(); |
| } else if (url == kChromeUIDumpURL) { |
| // This URL will only correctly create a crash dump file if content is |
| // hosted in a process that has correctly called |
| // base::debug::SetDumpWithoutCrashingFunction. Refer to the documentation |
| // of base::debug::DumpWithoutCrashing for more details. |
| base::debug::DumpWithoutCrashing(); |
| #if defined(OS_WIN) || defined(OS_POSIX) |
| } else if (url == kChromeUIKillURL) { |
| LOG(ERROR) << "Intentionally terminating current process because user" |
| " navigated to " |
| << url.spec(); |
| // Simulate termination such that the base::GetTerminationStatus() API will |
| // return TERMINATION_STATUS_PROCESS_WAS_KILLED. |
| #if defined(OS_WIN) |
| base::Process::TerminateCurrentProcessImmediately( |
| base::win::kProcessKilledExitCode); |
| #elif defined(OS_POSIX) |
| PCHECK(kill(base::Process::Current().Pid(), SIGTERM) == 0); |
| #endif |
| #endif // defined(OS_WIN) || defined(OS_POSIX) |
| } else if (url == kChromeUIHangURL) { |
| LOG(ERROR) << "Intentionally hanging ourselves with sleep infinite loop" |
| << " because user navigated to " << url.spec(); |
| for (;;) { |
| base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); |
| } |
| } else if (url == kChromeUIShorthangURL) { |
| LOG(ERROR) << "Intentionally sleeping renderer for 20 seconds" |
| << " because user navigated to " << url.spec(); |
| base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(20)); |
| } else if (url == kChromeUIMemoryExhaustURL) { |
| LOG(ERROR) |
| << "Intentionally exhausting renderer memory because user navigated to " |
| << url.spec(); |
| ExhaustMemory(); |
| } else if (url == kChromeUICheckCrashURL) { |
| LOG(ERROR) << "Intentionally causing CHECK because user navigated to " |
| << url.spec(); |
| CHECK(false); |
| } |
| |
| #if defined(OS_WIN) |
| if (url == kChromeUIHeapCorruptionCrashURL) { |
| LOG(ERROR) |
| << "Intentionally causing heap corruption because user navigated to " |
| << url.spec(); |
| base::debug::win::TerminateWithHeapCorruption(); |
| } |
| #endif |
| |
| #if DCHECK_IS_ON() |
| if (url == kChromeUICrashDcheckURL) { |
| LOG(ERROR) << "Intentionally causing DCHECK because user navigated to " |
| << url.spec(); |
| |
| DCHECK(false) << "Intentional DCHECK."; |
| } |
| #endif |
| |
| #if defined(ADDRESS_SANITIZER) |
| MaybeTriggerAsanError(url); |
| #endif // ADDRESS_SANITIZER |
| } |
| |
| const std::string& UniqueNameForWebFrame(blink::WebFrame* frame) { |
| return frame->IsWebLocalFrame() |
| ? RenderFrameImpl::FromWebFrame(frame)->unique_name() |
| : RenderFrameProxy::FromWebFrame(frame->ToWebRemoteFrame()) |
| ->unique_name(); |
| } |
| |
| RenderFrameImpl::UniqueNameFrameAdapter::UniqueNameFrameAdapter( |
| RenderFrameImpl* render_frame) |
| : render_frame_(render_frame) {} |
| |
| RenderFrameImpl::UniqueNameFrameAdapter::~UniqueNameFrameAdapter() {} |
| |
| bool RenderFrameImpl::UniqueNameFrameAdapter::IsMainFrame() const { |
| return render_frame_->IsMainFrame(); |
| } |
| |
| bool RenderFrameImpl::UniqueNameFrameAdapter::IsCandidateUnique( |
| base::StringPiece name) const { |
| // This method is currently O(N), where N = number of frames in the tree. |
| DCHECK(!name.empty()); |
| |
| for (blink::WebFrame* frame = GetWebFrame()->Top(); frame; |
| frame = frame->TraverseNext()) { |
| if (UniqueNameForWebFrame(frame) == name) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| int RenderFrameImpl::UniqueNameFrameAdapter::GetSiblingCount() const { |
| int sibling_count = 0; |
| for (blink::WebFrame* frame = GetWebFrame()->Parent()->FirstChild(); frame; |
| frame = frame->NextSibling()) { |
| if (frame == GetWebFrame()) |
| continue; |
| ++sibling_count; |
| } |
| return sibling_count; |
| } |
| |
| int RenderFrameImpl::UniqueNameFrameAdapter::GetChildCount() const { |
| int child_count = 0; |
| for (blink::WebFrame* frame = GetWebFrame()->FirstChild(); frame; |
| frame = frame->NextSibling()) { |
| ++child_count; |
| } |
| return child_count; |
| } |
| |
| std::vector<base::StringPiece> |
| RenderFrameImpl::UniqueNameFrameAdapter::CollectAncestorNames( |
| BeginPoint begin_point, |
| bool (*should_stop)(base::StringPiece)) const { |
| std::vector<base::StringPiece> result; |
| for (blink::WebFrame* frame = begin_point == BeginPoint::kParentFrame |
| ? GetWebFrame()->Parent() |
| : GetWebFrame(); |
| frame; frame = frame->Parent()) { |
| result.push_back(UniqueNameForWebFrame(frame)); |
| if (should_stop(result.back())) |
| break; |
| } |
| return result; |
| } |
| |
| std::vector<int> RenderFrameImpl::UniqueNameFrameAdapter::GetFramePosition( |
| BeginPoint begin_point) const { |
| std::vector<int> result; |
| blink::WebFrame* parent = begin_point == BeginPoint::kParentFrame |
| ? GetWebFrame()->Parent() |
| : GetWebFrame(); |
| blink::WebFrame* child = |
| begin_point == BeginPoint::kParentFrame ? GetWebFrame() : nullptr; |
| while (parent) { |
| int position_in_parent = 0; |
| blink::WebFrame* sibling = parent->FirstChild(); |
| while (sibling != child) { |
| sibling = sibling->NextSibling(); |
| ++position_in_parent; |
| } |
| result.push_back(position_in_parent); |
| |
| child = parent; |
| parent = parent->Parent(); |
| } |
| return result; |
| } |
| |
| blink::WebLocalFrame* RenderFrameImpl::UniqueNameFrameAdapter::GetWebFrame() |
| const { |
| return render_frame_->frame_; |
| } |
| |
| // static |
| RenderFrameImpl* RenderFrameImpl::Create( |
| RenderViewImpl* render_view, |
| int32_t routing_id, |
| service_manager::mojom::InterfaceProviderPtr interface_provider, |
| blink::mojom::DocumentInterfaceBrokerPtr document_interface_broker_content, |
| const base::UnguessableToken& devtools_frame_token) { |
| DCHECK(routing_id != MSG_ROUTING_NONE); |
| CreateParams params(render_view, routing_id, std::move(interface_provider), |
| std::move(document_interface_broker_content), |
| devtools_frame_token); |
| |
| if (g_create_render_frame_impl) |
| return g_create_render_frame_impl(std::move(params)); |
| else |
| return new RenderFrameImpl(std::move(params)); |
| } |
| |
| // static |
| RenderFrame* RenderFrame::FromRoutingID(int routing_id) { |
| return RenderFrameImpl::FromRoutingID(routing_id); |
| } |
| |
| // static |
| RenderFrameImpl* RenderFrameImpl::FromRoutingID(int routing_id) { |
| auto iter = g_routing_id_frame_map.Get().find(routing_id); |
| if (iter != g_routing_id_frame_map.Get().end()) |
| return iter->second; |
| return nullptr; |
| } |
| |
| // static |
| RenderFrameImpl* RenderFrameImpl::CreateMainFrame( |
| RenderViewImpl* render_view, |
| int32_t routing_id, |
| service_manager::mojom::InterfaceProviderPtr interface_provider, |
| blink::mojom::DocumentInterfaceBrokerPtr document_interface_broker_content, |
| blink::mojom::DocumentInterfaceBrokerPtr document_interface_broker_blink, |
| int32_t widget_routing_id, |
| bool hidden, |
| const ScreenInfo& screen_info, |
| CompositorDependencies* compositor_deps, |
| blink::WebFrame* opener, |
| const base::UnguessableToken& devtools_frame_token, |
| const FrameReplicationState& replicated_state, |
| bool has_committed_real_load) { |
| // A main frame RenderFrame must have a RenderWidget. |
| DCHECK_NE(MSG_ROUTING_NONE, widget_routing_id); |
| |
| RenderFrameImpl* render_frame = RenderFrameImpl::Create( |
| render_view, routing_id, std::move(interface_provider), |
| std::move(document_interface_broker_content), devtools_frame_token); |
| render_frame->InitializeBlameContext(nullptr); |
| WebLocalFrame* web_frame = WebLocalFrame::CreateMainFrame( |
| render_view->webview(), render_frame, |
| render_frame->blink_interface_registry_.get(), |
| document_interface_broker_blink.PassInterface().PassHandle(), opener, |
| // This conversion is a little sad, as this often comes from a |
| // WebString... |
| WebString::FromUTF8(replicated_state.name), |
| replicated_state.frame_policy.sandbox_flags); |
| if (has_committed_real_load) |
| render_frame->frame_->SetCommittedFirstRealLoad(); |
| |
| // The RenderViewImpl and its RenderWidget already exist by the time we get |
| // here. |
| // TODO(crbug.com/419087): We probably want to create the RenderWidget here |
| // though (when we make the WebFrameWidget?). |
| RenderWidget* render_widget = render_view->GetWidget(); |
| |
| // 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. |
| auto* web_frame_widget = blink::WebFrameWidget::CreateForMainFrame( |
| render_view->WidgetClient(), web_frame); |
| render_view->AttachWebFrameWidget(web_frame_widget); |
| // TODO(crbug.com/419087): This was added in 6ccadf770766e89c3 to prevent an |
| // empty ScreenInfo, but the WebView has already been created and initialized |
| // by RenderViewImpl, so this is surely redundant? |
| render_widget->UpdateWebViewWithDeviceScaleFactor(); |
| |
| render_frame->render_widget_ = render_widget; |
| render_frame->in_frame_tree_ = true; |
| render_frame->Initialize(); |
| |
| return render_frame; |
| } |
| |
| // static |
| void RenderFrameImpl::CreateFrame( |
| int routing_id, |
| service_manager::mojom::InterfaceProviderPtr interface_provider, |
| blink::mojom::DocumentInterfaceBrokerPtr document_interface_broker_content, |
| blink::mojom::DocumentInterfaceBrokerPtr document_interface_broker_blink, |
| int proxy_routing_id, |
| int opener_routing_id, |
| int parent_routing_id, |
| int previous_sibling_routing_id, |
| const base::UnguessableToken& devtools_frame_token, |
| const FrameReplicationState& replicated_state, |
| CompositorDependencies* compositor_deps, |
| const mojom::CreateFrameWidgetParams& widget_params, |
| const FrameOwnerProperties& frame_owner_properties, |
| bool has_committed_real_load) { |
| // TODO(danakj): Split this method into two pieces. The first block makes a |
| // WebLocalFrame and collects the RenderView and RenderFrame for it. The |
| // second block uses that to make/setup a RenderWidget, if needed. |
| RenderViewImpl* render_view = nullptr; |
| RenderFrameImpl* render_frame = nullptr; |
| blink::WebLocalFrame* web_frame = nullptr; |
| if (proxy_routing_id == MSG_ROUTING_NONE) { |
| // TODO(alexmos): This path is currently used only: |
| // 1) When recreating a RenderFrame after a crash. |
| // 2) In tests that issue this IPC directly. |
| // These two cases should be cleaned up to also pass a proxy_routing_id, |
| // which would allow removing this branch altogether. See |
| // https://crbug.com/756790. |
| |
| RenderFrameProxy* parent_proxy = |
| RenderFrameProxy::FromRoutingID(parent_routing_id); |
| // If the browser is sending a valid parent routing id, it should already |
| // be created and registered. |
| CHECK(parent_proxy); |
| blink::WebRemoteFrame* parent_web_frame = parent_proxy->web_frame(); |
| |
| blink::WebFrame* previous_sibling_web_frame = nullptr; |
| RenderFrameProxy* previous_sibling_proxy = |
| RenderFrameProxy::FromRoutingID(previous_sibling_routing_id); |
| if (previous_sibling_proxy) |
| previous_sibling_web_frame = previous_sibling_proxy->web_frame(); |
| |
| render_view = parent_proxy->render_view(); |
| // Create the RenderFrame and WebLocalFrame, linking the two. |
| render_frame = RenderFrameImpl::Create( |
| parent_proxy->render_view(), routing_id, std::move(interface_provider), |
| std::move(document_interface_broker_content), devtools_frame_token); |
| render_frame->InitializeBlameContext(FromRoutingID(parent_routing_id)); |
| render_frame->unique_name_helper_.set_propagated_name( |
| replicated_state.unique_name); |
| web_frame = parent_web_frame->CreateLocalChild( |
| replicated_state.scope, WebString::FromUTF8(replicated_state.name), |
| replicated_state.frame_policy.sandbox_flags, render_frame, |
| render_frame->blink_interface_registry_.get(), |
| document_interface_broker_blink.PassInterface().PassHandle(), |
| previous_sibling_web_frame, |
| replicated_state.frame_policy.container_policy, |
| ConvertFrameOwnerPropertiesToWebFrameOwnerProperties( |
| frame_owner_properties), |
| replicated_state.frame_owner_element_type, |
| ResolveOpener(opener_routing_id)); |
| |
| // The RenderFrame is created and inserted into the frame tree in the above |
| // call to createLocalChild. |
| render_frame->in_frame_tree_ = true; |
| } else { |
| RenderFrameProxy* proxy = |
| RenderFrameProxy::FromRoutingID(proxy_routing_id); |
| // The remote frame could've been detached while the remote-to-local |
| // 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 (!proxy) |
| return; |
| |
| // This path is creating a local frame. It may or may not be a local root, |
| // depending if the frame's parent is local or remote. It may also be the |
| // main frame, as in the case where a navigation to the current process' |
| // origin replaces a remote main frame (the proxy's web_frame()) with a |
| // local one. |
| const bool proxy_is_main_frame = !proxy->web_frame()->Parent(); |
| |
| render_view = proxy->render_view(); |
| render_frame = RenderFrameImpl::Create( |
| render_view, routing_id, std::move(interface_provider), |
| std::move(document_interface_broker_content), devtools_frame_token); |
| render_frame->InitializeBlameContext(nullptr); |
| render_frame->proxy_routing_id_ = proxy_routing_id; |
| proxy->set_provisional_frame_routing_id(routing_id); |
| web_frame = blink::WebLocalFrame::CreateProvisional( |
| render_frame, render_frame->blink_interface_registry_.get(), |
| document_interface_broker_blink.PassInterface().PassHandle(), |
| proxy->web_frame(), replicated_state.frame_policy.sandbox_flags, |
| replicated_state.frame_policy.container_policy); |
| // The new |web_frame| is a main frame iff the proxy's frame was. |
| DCHECK_EQ(proxy_is_main_frame, !web_frame->Parent()); |
| } |
| |
| DCHECK(render_view); |
| DCHECK(render_frame); |
| DCHECK(web_frame); |
| |
| const 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 |proxy_routing_id| is |
| // not given, which happens in some corner cases. |
| if (!is_main_frame) |
| DCHECK_NE(parent_routing_id, MSG_ROUTING_NONE); |
| |
| // We now have a WebLocalFrame for the new frame. The next step is to set |
| // up a RenderWidget for it, if it is needed. |
| // |
| // If there is no widget routing id, then the new frame is not a local root, |
| // and does not need a RenderWidget. In that case we'll do nothing. Otherwise |
| // it does. |
| if (is_main_frame) { |
| // For a main frame, we use the RenderWidget already attached to the |
| // RenderView (this is being changed by https://crbug.com/419087). |
| |
| // Main frames are always local roots, so they should always have a routing |
| // id. Surprisingly, this routing id is *not* used though, as the routing id |
| // on the existing RenderWidget is not changed. (I don't know why.) |
| // TODO(crbug.com/888105): It's a bug that the RenderWidget is not using |
| // this routing id. |
| DCHECK_NE(widget_params.routing_id, MSG_ROUTING_NONE); |
| |
| // The RenderViewImpl and its RenderWidget already exist by the time we |
| // get here (we get them from the RenderFrameProxy). |
| // TODO(crbug.com/419087): We probably want to create the RenderWidget |
| // here though (when we make the WebFrameWidget?). |
| RenderWidget* render_widget = render_view->GetWidget(); |
| |
| // 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. |
| auto* web_frame_widget = blink::WebFrameWidget::CreateForMainFrame( |
| render_view->WidgetClient(), web_frame); |
| render_view->AttachWebFrameWidget(web_frame_widget); |
| // TODO(crbug.com/419087): This was added in 6ccadf770766e89c3 to prevent |
| // an empty ScreenInfo, but the WebView has already been created and |
| // initialized by RenderViewImpl, so this is surely redundant? It will be |
| // pulling the device scale factor off the WebView itself. |
| render_widget->UpdateWebViewWithDeviceScaleFactor(); |
| |
| // It may be questionable, since we create un-frozen RenderWidgets at this |
| // point for subframes, but we don't un-freeze the main frame's RenderWidget |
| // here, instead deferring until the non-provisional frame is swapped in. |
| // But we do need to start the creating compositor resources in parallel to |
| // the navigation being done with the provisional frame, so we inform the |
| // frozen RenderWidget to get prepared. We must abort this if we are no |
| // longer planning to un-freeze the RenderWidget (ie in FrameDetached). |
| render_widget->WarmupCompositor(); |
| |
| render_frame->render_widget_ = render_widget; |
| } else if (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. |
| |
| // TODO(crbug.com/419087): This is grabbing something off the view's |
| // widget but if the main frame is remote this widget would not be valid? |
| const ScreenInfo& screen_info_from_main_frame = |
| render_view->GetWidget()->GetWebScreenInfo(); |
| |
| // Makes a new RenderWidget for the child local root. It provides the |
| // local root with a new compositing, painting, and input coordinate |
| // space/context. |
| scoped_refptr<RenderWidget> render_widget; |
| if (g_create_render_widget) { |
| // Web test hooks inject a different type (subclass) for RenderWidget, |
| // allowing it to override the behaviour of the WebWidgetClient which |
| // RenderWidget provides. |
| render_widget = g_create_render_widget( |
| widget_params.routing_id, compositor_deps, |
| screen_info_from_main_frame, blink::kWebDisplayModeUndefined, |
| /*is_frozen=*/false, widget_params.hidden, |
| /*never_visible=*/false); |
| } else { |
| render_widget = base::MakeRefCounted<RenderWidget>( |
| widget_params.routing_id, compositor_deps, |
| screen_info_from_main_frame, blink::kWebDisplayModeUndefined, |
| /*is_frozen=*/false, widget_params.hidden, |
| /*never_visible=*/false); |
| } |
| |
| // Non-owning pointer that is self-referencing and destroyed by calling |
| // Close(). We use the new RenderWidget as the client for this |
| // WebFrameWidget, *not* the RenderWidget of the MainFrame, which is |
| // accessible from the RenderViewImpl. |
| auto* web_frame_widget = blink::WebFrameWidget::CreateForChildLocalRoot( |
| render_widget.get(), web_frame); |
| |
| // Adds a reference on RenderWidget, making it self-referencing. So it |
| // will not be destroyed by scoped_refptr unless Close() has been called |
| // and run. |
| render_widget->InitForChildLocalRoot(web_frame_widget); |
| // TODO(crbug.com/419087): This was added in 6ccadf770766e89c3 to prevent |
| // an empty ScreenInfo, but the WebView has already been created and |
| // initialized by RenderViewImpl, so this is surely redundant? It will be |
| // pulling the device scale factor off the WebView itself. |
| render_widget->UpdateWebViewWithDeviceScaleFactor(); |
| |
| // Web test hooks to set up the injected type for RenderWidget. |
| if (g_render_widget_initialized) |
| g_render_widget_initialized(render_widget.get()); |
| |
| render_frame->render_widget_ = std::move(render_widget); |
| } |
| |
| if (has_committed_real_load) |
| render_frame->frame_->SetCommittedFirstRealLoad(); |
| |
| render_frame->Initialize(); |
| } |
| |
| // static |
| RenderFrame* RenderFrame::FromWebFrame(blink::WebLocalFrame* web_frame) { |
| return RenderFrameImpl::FromWebFrame(web_frame); |
| } |
| |
| // static |
| void RenderFrame::ForEach(RenderFrameVisitor* visitor) { |
| FrameMap* frames = g_frame_map.Pointer(); |
| for (auto it = frames->begin(); it != frames->end(); ++it) { |
| if (!visitor->Visit(it->second)) |
| return; |
| } |
| } |
| |
| // static |
| int RenderFrame::GetRoutingIdForWebFrame(blink::WebFrame* web_frame) { |
| if (!web_frame) |
| return MSG_ROUTING_NONE; |
| if (web_frame->IsWebRemoteFrame()) { |
| return RenderFrameProxy::FromWebFrame(web_frame->ToWebRemoteFrame()) |
| ->routing_id(); |
| } |
| return RenderFrameImpl::FromWebFrame(web_frame)->GetRoutingID(); |
| } |
| |
| // static |
| RenderFrameImpl* RenderFrameImpl::FromWebFrame(blink::WebFrame* web_frame) { |
| 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, |
| CreateRenderWidgetForChildLocalRootFunction create_widget, |
| RenderWidgetForChildLocalRootInitializedCallback widget_initialized) { |
| DCHECK(!g_create_render_frame_impl); |
| DCHECK(!g_create_render_widget); |
| DCHECK(!g_render_widget_initialized); |
| g_create_render_frame_impl = create_frame; |
| g_create_render_widget = create_widget; |
| g_render_widget_initialized = widget_initialized; |
| } |
| |
| // static |
| blink::WebFrame* RenderFrameImpl::ResolveOpener(int opener_frame_routing_id) { |
| if (opener_frame_routing_id == MSG_ROUTING_NONE) |
| return nullptr; |
| |
| // Opener routing ID could refer to either a RenderFrameProxy or a |
| // RenderFrame, so need to check both. |
| RenderFrameProxy* opener_proxy = |
| RenderFrameProxy::FromRoutingID(opener_frame_routing_id); |
| if (opener_proxy) |
| return opener_proxy->web_frame(); |
| |
| RenderFrameImpl* opener_frame = |
| RenderFrameImpl::FromRoutingID(opener_frame_routing_id); |
| if (opener_frame) |
| return opener_frame->GetWebFrame(); |
| |
| return nullptr; |
| } |
| |
| // static |
| NavigationDownloadPolicy RenderFrameImpl::GetOpenerDownloadPolicy( |
| bool is_opener_navigation, |
| const blink::WebURLRequest& request, |
| const WebSecurityOrigin& current_origin) { |
| if (!is_opener_navigation) |
| return NavigationDownloadPolicy::kAllow; |
| bool gesture = request.HasUserGesture(); |
| bool cross_origin = !request.RequestorOrigin().CanAccess(current_origin); |
| if (!gesture && cross_origin) |
| return NavigationDownloadPolicy::kAllowOpenerCrossOriginNoGesture; |
| if (!gesture) |
| return NavigationDownloadPolicy::kAllowOpenerNoGesture; |
| if (cross_origin) |
| return NavigationDownloadPolicy::kAllowOpenerCrossOrigin; |
| return NavigationDownloadPolicy::kAllowOpener; |
| } |
| |
| blink::WebURL RenderFrameImpl::OverrideFlashEmbedWithHTML( |
| const blink::WebURL& url) { |
| return GetContentClient()->renderer()->OverrideFlashEmbedWithHTML(url); |
| } |
| |
| // RenderFrameImpl::CreateParams -------------------------------------------- |
| |
| RenderFrameImpl::CreateParams::CreateParams( |
| RenderViewImpl* render_view, |
| int32_t routing_id, |
| service_manager::mojom::InterfaceProviderPtr interface_provider, |
| blink::mojom::DocumentInterfaceBrokerPtr document_interface_broker_content, |
| const base::UnguessableToken& devtools_frame_token) |
| : render_view(render_view), |
| routing_id(routing_id), |
| interface_provider(std::move(interface_provider)), |
| document_interface_broker_content( |
| std::move(document_interface_broker_content)), |
| devtools_frame_token(devtools_frame_token) {} |
| RenderFrameImpl::CreateParams::~CreateParams() = default; |
| RenderFrameImpl::CreateParams::CreateParams(CreateParams&&) = default; |
| RenderFrameImpl::CreateParams& RenderFrameImpl::CreateParams::operator=( |
| CreateParams&&) = default; |
| |
| // RenderFrameImpl ---------------------------------------------------------- |
| RenderFrameImpl::RenderFrameImpl(CreateParams params) |
| : frame_(nullptr), |
| is_main_frame_(true), |
| unique_name_frame_adapter_(this), |
| unique_name_helper_(&unique_name_frame_adapter_), |
| in_frame_tree_(false), |
| render_view_(params.render_view), |
| routing_id_(params.routing_id), |
| proxy_routing_id_(MSG_ROUTING_NONE), |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| plugin_power_saver_helper_(nullptr), |
| #endif |
| cookie_jar_(this), |
| selection_text_offset_(0), |
| selection_range_(gfx::Range::InvalidRange()), |
| handling_select_range_(false), |
| web_user_media_client_(nullptr), |
| push_messaging_client_(nullptr), |
| render_accessibility_(nullptr), |
| previews_state_(PREVIEWS_UNSPECIFIED), |
| effective_connection_type_( |
| blink::WebEffectiveConnectionType::kTypeUnknown), |
| is_pasting_(false), |
| suppress_further_dialogs_(false), |
| blame_context_(nullptr), |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| focused_pepper_plugin_(nullptr), |
| pepper_last_mouse_event_target_(nullptr), |
| #endif |
| autoplay_configuration_binding_(this), |
| frame_binding_(this), |
| host_zoom_binding_(this), |
| frame_bindings_control_binding_(this), |
| frame_navigation_control_binding_(this), |
| fullscreen_binding_(this), |
| navigation_client_impl_(nullptr), |
| has_accessed_initial_document_(false), |
| media_factory_(this, |
| base::Bind(&RenderFrameImpl::RequestOverlayRoutingToken, |
| base::Unretained(this))), |
| input_target_client_impl_(this), |
| devtools_frame_token_(params.devtools_frame_token), |
| weak_factory_(this) { |
| // The InterfaceProvider to access Mojo services exposed by the RFHI must be |
| // provided at construction time. See: https://crbug.com/729021/. |
| CHECK(params.interface_provider.is_bound()); |
| remote_interfaces_.Bind(std::move(params.interface_provider)); |
| blink_interface_registry_.reset(new BlinkInterfaceRegistryImpl( |
| registry_.GetWeakPtr(), associated_interfaces_.GetWeakPtr())); |
| |
| // The DocumentInterfaceBroker to access Mojo services exposed by the RFHI |
| // must be provided at construction time. See: https://crbug.com/718652/. |
| CHECK(params.document_interface_broker_content.is_bound()); |
| document_interface_broker_ = |
| std::move(params.document_interface_broker_content); |
| |
| // Must call after binding our own remote interfaces. |
| media_factory_.SetupMojo(); |
| |
| std::pair<RoutingIDFrameMap::iterator, bool> result = |
| g_routing_id_frame_map.Get().insert(std::make_pair(routing_id_, this)); |
| CHECK(result.second) << "Inserting a duplicate item."; |
| |
| // Everything below subclasses RenderFrameObserver and is automatically |
| // deleted when the RenderFrame gets deleted. |
| #if defined(OS_ANDROID) |
| new GinJavaBridgeDispatcher(this); |
| #endif |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| // Manages its own lifetime. |
| plugin_power_saver_helper_ = new PluginPowerSaverHelper(this); |
| #endif |
| |
| manifest_manager_ = std::make_unique<ManifestManager>(this); |
| // TODO(ajwong): This always returns true as is_main_frame_ gets initialized |
| // later in RenderFrameImpl::Initialize(). Should the conditional be in |
| // RenderFrameImpl::Initialize()? https://crbug.com/840533 |
| if (IsMainFrame()) { |
| // Manages its own lifetime. |
| new ManifestChangeNotifier(this); |
| } |
| } |
| |
| mojom::FrameHost* RenderFrameImpl::GetFrameHost() { |
| if (!frame_host_ptr_.is_bound()) |
| GetRemoteAssociatedInterfaces()->GetInterface(&frame_host_ptr_); |
| return frame_host_ptr_.get(); |
| } |
| |
| RenderFrameImpl::~RenderFrameImpl() { |
| for (auto& observer : observers_) |
| observer.RenderFrameGone(); |
| for (auto& observer : observers_) |
| observer.OnDestruct(); |
| |
| base::trace_event::TraceLog::GetInstance()->RemoveProcessLabel(routing_id_); |
| |
| if (auto* factory = AudioOutputIPCFactory::get()) |
| factory->MaybeDeregisterRemoteFactory(GetRoutingID()); |
| |
| // |thread| may be null in tests. |
| if (auto* thread = RenderThreadImpl::current()) { |
| if (auto* controller = thread->low_memory_mode_controller()) |
| controller->OnFrameDestroyed(IsMainFrame()); |
| } |
| |
| if (is_main_frame_) { |
| // Ensure the RenderView doesn't point to this object, once it is destroyed. |
| // TODO(nasko): Add a check that the |main_render_frame_| of |render_view_| |
| // is |this|, once the object is no longer leaked. |
| // See https://crbug.com/464764. |
| render_view_->main_render_frame_ = nullptr; |
| } |
| |
| g_routing_id_frame_map.Get().erase(routing_id_); |
| RenderThread::Get()->RemoveRoute(routing_id_); |
| } |
| |
| void RenderFrameImpl::Initialize() { |
| is_main_frame_ = !frame_->Parent(); |
| |
| GetLocalRootRenderWidget()->RegisterRenderFrame(this); |
| |
| RenderFrameImpl* parent_frame = |
| RenderFrameImpl::FromWebFrame(frame_->Parent()); |
| if (parent_frame) { |
| previews_state_ = parent_frame->GetPreviewsState(); |
| effective_connection_type_ = parent_frame->GetEffectiveConnectionType(); |
| } |
| |
| bool is_tracing_rail = false; |
| bool is_tracing_navigation = false; |
| TRACE_EVENT_CATEGORY_GROUP_ENABLED("navigation", &is_tracing_navigation); |
| TRACE_EVENT_CATEGORY_GROUP_ENABLED("rail", &is_tracing_rail); |
| if (is_tracing_rail || is_tracing_navigation) { |
| int parent_id = RenderFrame::GetRoutingIdForWebFrame(frame_->Parent()); |
| TRACE_EVENT2("navigation,rail", "RenderFrameImpl::Initialize", |
| "id", routing_id_, |
| "parent", parent_id); |
| } |
| |
| // |thread| may be null in tests. |
| if (auto* thread = RenderThreadImpl::current()) { |
| if (auto* controller = thread->low_memory_mode_controller()) |
| controller->OnFrameCreated(IsMainFrame()); |
| } |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| new PepperBrowserConnection(this); |
| #endif |
| |
| RegisterMojoInterfaces(); |
| |
| // We delay calling this until we have the WebFrame so that any observer or |
| // embedder can call GetWebFrame on any RenderFrame. |
| GetContentClient()->renderer()->RenderFrameCreated(this); |
| |
| // AudioOutputIPCFactory may be null in tests. |
| if (auto* factory = AudioOutputIPCFactory::get()) |
| factory->RegisterRemoteFactory(GetRoutingID(), GetRemoteInterfaces()); |
| |
| AudioRendererSinkCache::ObserveFrame(this); |
| |
| const base::CommandLine& command_line = |
| *base::CommandLine::ForCurrentProcess(); |
| if (command_line.HasSwitch(switches::kDomAutomationController)) |
| enabled_bindings_ |= BINDINGS_POLICY_DOM_AUTOMATION; |
| if (command_line.HasSwitch(switches::kStatsCollectionController)) |
| enabled_bindings_ |= BINDINGS_POLICY_STATS_COLLECTION; |
| if (base::FeatureList::IsEnabled(network::features::kNetworkService)) |
| frame_request_blocker_ = base::MakeRefCounted<FrameRequestBlocker>(); |
| |
| // Bind this frame and the message router. This must be called after |frame_| |
| // is set since binding requires a per-frame task runner. |
| RenderThread::Get()->AddRoute(routing_id_, this); |
| } |
| |
| void RenderFrameImpl::InitializeBlameContext(RenderFrameImpl* parent_frame) { |
| DCHECK(!blame_context_); |
| blame_context_ = std::make_unique<FrameBlameContext>(this, parent_frame); |
| blame_context_->Initialize(); |
| } |
| |
| void RenderFrameImpl::GetInterface( |
| const std::string& interface_name, |
| mojo::ScopedMessagePipeHandle interface_pipe) { |
| if (registry_.TryBindInterface(interface_name, &interface_pipe)) |
| return; |
| |
| for (auto& observer : observers_) { |
| observer.OnInterfaceRequestForFrame(interface_name, &interface_pipe); |
| if (!interface_pipe.is_valid()) |
| return; |
| } |
| } |
| |
| RenderWidget* RenderFrameImpl::GetLocalRootRenderWidget() { |
| return GetLocalRoot()->render_widget_.get(); |
| } |
| |
| RenderWidget* RenderFrameImpl::GetMainFrameRenderWidget() { |
| return render_view()->GetWidget(); |
| } |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| void RenderFrameImpl::PepperPluginCreated(RendererPpapiHost* host) { |
| for (auto& observer : observers_) |
| observer.DidCreatePepperPlugin(host); |
| } |
| |
| void RenderFrameImpl::PepperDidChangeCursor( |
| PepperPluginInstanceImpl* instance, |
| const blink::WebCursorInfo& cursor) { |
| // Update the cursor appearance immediately if the requesting plugin is the |
| // one which receives the last mouse event. Otherwise, the new cursor won't be |
| // picked up until the plugin gets the next input event. That is bad if, e.g., |
| // the plugin would like to set an invisible cursor when there isn't any user |
| // input for a while. |
| if (instance == pepper_last_mouse_event_target_) |
| GetLocalRootRenderWidget()->DidChangeCursor(cursor); |
| } |
| |
| void RenderFrameImpl::PepperDidReceiveMouseEvent( |
| PepperPluginInstanceImpl* instance) { |
| set_pepper_last_mouse_event_target(instance); |
| } |
| |
| void RenderFrameImpl::PepperTextInputTypeChanged( |
| PepperPluginInstanceImpl* instance) { |
| if (instance != focused_pepper_plugin_) |
| return; |
| |
| GetLocalRootRenderWidget()->UpdateTextInputState(); |
| |
| FocusedNodeChangedForAccessibility(WebNode()); |
| } |
| |
| void RenderFrameImpl::PepperCaretPositionChanged( |
| PepperPluginInstanceImpl* instance) { |
| if (instance != focused_pepper_plugin_) |
| return; |
| GetLocalRootRenderWidget()->UpdateSelectionBounds(); |
| } |
| |
| void RenderFrameImpl::PepperCancelComposition( |
| PepperPluginInstanceImpl* instance) { |
| if (instance != focused_pepper_plugin_) |
| return; |
| if (mojom::WidgetInputHandlerHost* host = GetLocalRootRenderWidget() |
| ->widget_input_handler_manager() |
| ->GetWidgetInputHandlerHost()) { |
| host->ImeCancelComposition(); |
| } |
| #if defined(OS_MACOSX) || defined(USE_AURA) |
| GetLocalRootRenderWidget()->UpdateCompositionInfo( |
| false /* not an immediate request */); |
| #endif |
| } |
| |
| void RenderFrameImpl::PepperSelectionChanged( |
| PepperPluginInstanceImpl* instance) { |
| if (instance != focused_pepper_plugin_) |
| return; |
| SyncSelectionIfRequired(); |
| } |
| |
| RenderWidgetFullscreenPepper* RenderFrameImpl::CreatePepperFullscreenContainer( |
| PepperPluginInstanceImpl* plugin) { |
| // Get the URL of the main frame if possible. |
| blink::WebURL main_frame_url; |
| WebFrame* main_frame = render_view()->webview()->MainFrame(); |
| if (main_frame->IsWebLocalFrame()) |
| main_frame_url = main_frame->ToWebLocalFrame()->GetDocument().Url(); |
| |
| mojom::WidgetPtr widget_channel; |
| mojom::WidgetRequest widget_channel_request = |
| mojo::MakeRequest(&widget_channel); |
| |
| // Synchronous IPC to obtain a routing id for the fullscreen widget. |
| int32_t fullscreen_widget_routing_id = MSG_ROUTING_NONE; |
| if (!RenderThreadImpl::current_render_message_filter() |
| ->CreateFullscreenWidget(render_view()->GetRoutingID(), |
| std::move(widget_channel), |
| &fullscreen_widget_routing_id)) { |
| return nullptr; |
| } |
| RenderWidget::ShowCallback show_callback = |
| base::BindOnce(&RenderViewImpl::ShowCreatedFullscreenWidget, |
| render_view()->GetWeakPtr()); |
| |
| // TODO(fsamuel): It's not clear if we should be passing in the |
| // web ScreenInfo or the original ScreenInfo here. |
| RenderWidgetFullscreenPepper* widget = RenderWidgetFullscreenPepper::Create( |
| fullscreen_widget_routing_id, std::move(show_callback), |
| GetLocalRootRenderWidget()->compositor_deps(), plugin, |
| std::move(main_frame_url), GetLocalRootRenderWidget()->GetWebScreenInfo(), |
| std::move(widget_channel_request)); |
| // TODO(nick): The show() handshake seems like unnecessary complexity here, |
| // since there's no real delay between CreateFullscreenWidget and |
| // ShowCreatedFullscreenWidget. Would it be simpler to have the |
| // CreateFullscreenWidget mojo method implicitly show the window, and skip the |
| // subsequent step? |
| widget->Show(blink::kWebNavigationPolicyCurrentTab); |
| return widget; |
| } |
| |
| bool RenderFrameImpl::IsPepperAcceptingCompositionEvents() const { |
| if (!focused_pepper_plugin_) |
| return false; |
| return focused_pepper_plugin_->IsPluginAcceptingCompositionEvents(); |
| } |
| |
| void RenderFrameImpl::PluginCrashed(const base::FilePath& plugin_path, |
| base::ProcessId plugin_pid) { |
| // TODO(jam): dispatch this IPC in RenderFrameHost and switch to use |
| // routing_id_ as a result. |
| Send(new FrameHostMsg_PluginCrashed(routing_id_, plugin_path, plugin_pid)); |
| } |
| |
| void RenderFrameImpl::SimulateImeSetComposition( |
| const base::string16& text, |
| const std::vector<blink::WebImeTextSpan>& ime_text_spans, |
| int selection_start, |
| int selection_end) { |
| GetMainFrameRenderWidget()->OnImeSetComposition( |
| text, ime_text_spans, gfx::Range::InvalidRange(), selection_start, |
| selection_end); |
| } |
| |
| void RenderFrameImpl::SimulateImeCommitText( |
| const base::string16& text, |
| const std::vector<blink::WebImeTextSpan>& ime_text_spans, |
| const gfx::Range& replacement_range) { |
| GetMainFrameRenderWidget()->OnImeCommitText(text, ime_text_spans, |
| replacement_range, 0); |
| } |
| |
| void RenderFrameImpl::SimulateImeFinishComposingText(bool keep_selection) { |
| GetMainFrameRenderWidget()->OnImeFinishComposingText(keep_selection); |
| } |
| |
| void RenderFrameImpl::OnImeSetComposition( |
| const base::string16& text, |
| const std::vector<blink::WebImeTextSpan>& ime_text_spans, |
| int selection_start, |
| int selection_end) { |
| // When a PPAPI plugin has focus, we bypass WebKit. |
| if (!IsPepperAcceptingCompositionEvents()) { |
| pepper_composition_text_ = text; |
| } else { |
| // TODO(kinaba) currently all composition events are sent directly to |
| // plugins. Use DOM event mechanism after WebKit is made aware about |
| // plugins that support composition. |
| // The code below mimics the behavior of WebCore::Editor::setComposition. |
| |
| // Empty -> nonempty: composition started. |
| if (pepper_composition_text_.empty() && !text.empty()) { |
| focused_pepper_plugin_->HandleCompositionStart(base::string16()); |
| } |
| // Nonempty -> empty: composition canceled. |
| if (!pepper_composition_text_.empty() && text.empty()) { |
| focused_pepper_plugin_->HandleCompositionEnd(base::string16()); |
| } |
| pepper_composition_text_ = text; |
| // Nonempty: composition is ongoing. |
| if (!pepper_composition_text_.empty()) { |
| focused_pepper_plugin_->HandleCompositionUpdate( |
| pepper_composition_text_, ime_text_spans, selection_start, |
| selection_end); |
| } |
| } |
| } |
| |
| void RenderFrameImpl::OnImeCommitText(const base::string16& text, |
| const gfx::Range& replacement_range, |
| int relative_cursor_pos) { |
| HandlePepperImeCommit(text); |
| } |
| |
| void RenderFrameImpl::OnImeFinishComposingText(bool keep_selection) { |
| const base::string16& text = pepper_composition_text_; |
| HandlePepperImeCommit(text); |
| } |
| #endif // BUILDFLAG(ENABLE_PLUGINS) |
| |
| MediaStreamDeviceObserver* RenderFrameImpl::GetMediaStreamDeviceObserver() { |
| if (!web_user_media_client_) |
| InitializeUserMediaClient(); |
| return web_user_media_client_ |
| ? web_user_media_client_->media_stream_device_observer() |
| : nullptr; |
| } |
| |
| void RenderFrameImpl::ScriptedPrint(bool user_initiated) { |
| for (auto& observer : observers_) |
| observer.ScriptedPrint(user_initiated); |
| } |
| |
| bool RenderFrameImpl::Send(IPC::Message* message) { |
| return RenderThread::Get()->Send(message); |
| } |
| |
| #if BUILDFLAG(USE_EXTERNAL_POPUP_MENU) |
| void RenderFrameImpl::DidHideExternalPopupMenu() { |
| // We need to clear external_popup_menu_ as soon as ExternalPopupMenu::close |
| // is called. Otherwise, createExternalPopupMenu() for new popup will fail. |
| external_popup_menu_.reset(); |
| } |
| #endif |
| |
| bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) { |
| // Forward Page IPCs to the RenderView. |
| if ((IPC_MESSAGE_CLASS(msg) == PageMsgStart)) { |
| if (render_view()) |
| return render_view()->OnMessageReceived(msg); |
| |
| return false; |
| } |
| |
| // 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; |
| } |
| |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(RenderFrameImpl, msg) |
| IPC_MESSAGE_HANDLER(FrameMsg_BeforeUnload, OnBeforeUnload) |
| IPC_MESSAGE_HANDLER(FrameMsg_SwapOut, OnSwapOut) |
| IPC_MESSAGE_HANDLER(FrameMsg_SwapIn, OnSwapIn) |
| IPC_MESSAGE_HANDLER(FrameMsg_Delete, OnDeleteFrame) |
| IPC_MESSAGE_HANDLER(FrameMsg_Stop, OnStop) |
| IPC_MESSAGE_HANDLER(FrameMsg_DroppedNavigation, OnDroppedNavigation) |
| IPC_MESSAGE_HANDLER(FrameMsg_Collapse, OnCollapse) |
| IPC_MESSAGE_HANDLER(FrameMsg_ContextMenuClosed, OnContextMenuClosed) |
| IPC_MESSAGE_HANDLER(FrameMsg_CustomContextMenuAction, |
| OnCustomContextMenuAction) |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| IPC_MESSAGE_HANDLER(FrameMsg_SetPepperVolume, OnSetPepperVolume) |
| #endif |
| IPC_MESSAGE_HANDLER(FrameMsg_CopyImageAt, OnCopyImageAt) |
| IPC_MESSAGE_HANDLER(FrameMsg_SaveImageAt, OnSaveImageAt) |
| IPC_MESSAGE_HANDLER(FrameMsg_AddMessageToConsole, OnAddMessageToConsole) |
| IPC_MESSAGE_HANDLER(FrameMsg_JavaScriptExecuteRequest, |
| OnJavaScriptExecuteRequest) |
| IPC_MESSAGE_HANDLER(FrameMsg_JavaScriptExecuteRequestForTests, |
| OnJavaScriptExecuteRequestForTests) |
| IPC_MESSAGE_HANDLER(FrameMsg_JavaScriptExecuteRequestInIsolatedWorld, |
| OnJavaScriptExecuteRequestInIsolatedWorld) |
| IPC_MESSAGE_HANDLER(FrameMsg_VisualStateRequest, |
| OnVisualStateRequest) |
| IPC_MESSAGE_HANDLER(FrameMsg_Reload, OnReload) |
| IPC_MESSAGE_HANDLER(FrameMsg_ReloadLoFiImages, OnReloadLoFiImages) |
| IPC_MESSAGE_HANDLER(FrameMsg_TextSurroundingSelectionRequest, |
| OnTextSurroundingSelectionRequest) |
| IPC_MESSAGE_HANDLER(FrameMsg_SetAccessibilityMode, |
| OnSetAccessibilityMode) |
| IPC_MESSAGE_HANDLER(AccessibilityMsg_SnapshotTree, |
| OnSnapshotAccessibilityTree) |
| IPC_MESSAGE_HANDLER(FrameMsg_UpdateOpener, OnUpdateOpener) |
| IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateFramePolicy, OnDidUpdateFramePolicy) |
| IPC_MESSAGE_HANDLER(FrameMsg_SetFrameOwnerProperties, |
| OnSetFrameOwnerProperties) |
| IPC_MESSAGE_HANDLER(FrameMsg_AdvanceFocus, OnAdvanceFocus) |
| IPC_MESSAGE_HANDLER(FrameMsg_AdvanceFocusInForm, OnAdvanceFocusInForm) |
| IPC_MESSAGE_HANDLER(FrameMsg_SetFocusedFrame, OnSetFocusedFrame) |
| IPC_MESSAGE_HANDLER(FrameMsg_SetTextTrackSettings, |
| OnTextTrackSettingsChanged) |
| IPC_MESSAGE_HANDLER(FrameMsg_CheckCompleted, OnCheckCompleted) |
| IPC_MESSAGE_HANDLER(FrameMsg_PostMessageEvent, OnPostMessageEvent) |
| IPC_MESSAGE_HANDLER(FrameMsg_ReportContentSecurityPolicyViolation, |
| OnReportContentSecurityPolicyViolation) |
| IPC_MESSAGE_HANDLER(FrameMsg_GetSavableResourceLinks, |
| OnGetSavableResourceLinks) |
| IPC_MESSAGE_HANDLER(FrameMsg_GetSerializedHtmlWithLocalLinks, |
| OnGetSerializedHtmlWithLocalLinks) |
| IPC_MESSAGE_HANDLER(FrameMsg_SerializeAsMHTML, OnSerializeAsMHTML) |
| IPC_MESSAGE_HANDLER(FrameMsg_EnableViewSourceMode, OnEnableViewSourceMode) |
| IPC_MESSAGE_HANDLER(FrameMsg_SuppressFurtherDialogs, |
| OnSuppressFurtherDialogs) |
| IPC_MESSAGE_HANDLER(FrameMsg_ClearFocusedElement, OnClearFocusedElement) |
| IPC_MESSAGE_HANDLER(FrameMsg_BlinkFeatureUsageReport, |
| OnBlinkFeatureUsageReport) |
| IPC_MESSAGE_HANDLER(FrameMsg_MixedContentFound, OnMixedContentFound) |
| IPC_MESSAGE_HANDLER(FrameMsg_SetOverlayRoutingToken, |
| OnSetOverlayRoutingToken) |
| IPC_MESSAGE_HANDLER(FrameMsg_NotifyUserActivation, OnNotifyUserActivation) |
| IPC_MESSAGE_HANDLER(FrameMsg_MediaPlayerActionAt, OnMediaPlayerActionAt) |
| IPC_MESSAGE_HANDLER(FrameMsg_RenderFallbackContent, OnRenderFallbackContent) |
| #if BUILDFLAG(USE_EXTERNAL_POPUP_MENU) |
| #if defined(OS_MACOSX) |
| IPC_MESSAGE_HANDLER(FrameMsg_SelectPopupMenuItem, OnSelectPopupMenuItem) |
| #else |
| IPC_MESSAGE_HANDLER(FrameMsg_SelectPopupMenuItems, OnSelectPopupMenuItems) |
| #endif |
| #endif |
| |
| IPC_END_MESSAGE_MAP() |
| |
| return handled; |
| } |
| |
| 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::BindFullscreen( |
| mojom::FullscreenVideoElementHandlerAssociatedRequest request) { |
| fullscreen_binding_.Bind(std::move(request), |
| GetTaskRunner(blink::TaskType::kInternalIPC)); |
| } |
| |
| void RenderFrameImpl::BindAutoplayConfiguration( |
| blink::mojom::AutoplayConfigurationClientAssociatedRequest request) { |
| autoplay_configuration_binding_.Bind( |
| std::move(request), GetTaskRunner(blink::TaskType::kInternalIPC)); |
| } |
| |
| void RenderFrameImpl::BindFrame( |
| const service_manager::BindSourceInfo& browser_info, |
| mojom::FrameRequest request) { |
| browser_info_ = browser_info; |
| frame_binding_.Bind(std::move(request), |
| GetTaskRunner(blink::TaskType::kInternalIPC)); |
| } |
| |
| void RenderFrameImpl::BindFrameBindingsControl( |
| mojom::FrameBindingsControlAssociatedRequest request) { |
| frame_bindings_control_binding_.Bind( |
| std::move(request), GetTaskRunner(blink::TaskType::kInternalIPC)); |
| } |
| |
| void RenderFrameImpl::BindFrameNavigationControl( |
| mojom::FrameNavigationControlAssociatedRequest request) { |
| frame_navigation_control_binding_.Bind( |
| std::move(request), GetTaskRunner(blink::TaskType::kInternalIPC)); |
| } |
| |
| void RenderFrameImpl::BindNavigationClient( |
| mojom::NavigationClientAssociatedRequest request) { |
| navigation_client_impl_ = std::make_unique<NavigationClient>(this); |
| navigation_client_impl_->Bind(std::move(request)); |
| } |
| |
| blink::mojom::ManifestManager& RenderFrameImpl::GetManifestManager() { |
| return *manifest_manager_; |
| } |
| |
| void RenderFrameImpl::OnBeforeUnload(bool is_reload) { |
| TRACE_EVENT1("navigation,rail", "RenderFrameImpl::OnBeforeUnload", |
| "id", routing_id_); |
| // Save the routing_id, as the RenderFrameImpl can be deleted in |
| // dispatchBeforeUnloadEvent. See https://crbug.com/666714 for details. |
| int routing_id = routing_id_; |
| |
| base::TimeTicks before_unload_start_time = base::TimeTicks::Now(); |
| |
| // This will execute the BeforeUnload event in this frame and all of its |
| // local descendant frames, including children of remote frames. The browser |
| // process will send separate IPCs to dispatch beforeunload in any |
| // out-of-process child frames. |
| bool proceed = frame_->DispatchBeforeUnloadEvent(is_reload); |
| |
| base::TimeTicks before_unload_end_time = base::TimeTicks::Now(); |
| RenderThread::Get()->Send(new FrameHostMsg_BeforeUnload_ACK( |
| routing_id, proceed, before_unload_start_time, before_unload_end_time)); |
| } |
| |
| // Swap this RenderFrame out 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::OnSwapOut( |
| int proxy_routing_id, |
| bool is_loading, |
| const FrameReplicationState& replicated_frame_state) { |
| TRACE_EVENT1("navigation,rail", "RenderFrameImpl::OnSwapOut", |
| "id", routing_id_); |
| |
| // Send an UpdateState message before we get deleted. |
| SendUpdateState(); |
| |
| // There should always be a proxy to replace this RenderFrame. Create it now |
| // so its routing id is registered for receiving IPC messages. |
| CHECK_NE(proxy_routing_id, MSG_ROUTING_NONE); |
| RenderFrameProxy* proxy = RenderFrameProxy::CreateProxyToReplaceFrame( |
| this, proxy_routing_id, replicated_frame_state.scope); |
| |
| // Swap out and stop sending any IPC messages that are not ACKs. |
| if (is_main_frame_) { |
| // The RenderWidget isn't actually closed here because we might need to use |
| // it again. It can't be destroyed and recreated later as it is part of |
| // the |render_view_|, which must be kept alive. So instead freeze the |
| // widget. |
| render_view_->GetWidget()->SetIsFrozen(true); |
| } |
| |
| RenderViewImpl* render_view = render_view_; |
| bool is_main_frame = is_main_frame_; |
| int routing_id = GetRoutingID(); |
| |
| // Before |this| is destroyed, grab the TaskRunner to be used for sending the |
| // SwapOut ACK. This will be used to schedule SwapOut ACK to be sent after |
| // any postMessage IPCs scheduled from the unload event above. |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner = |
| GetTaskRunner(blink::TaskType::kPostedMessage); |
| |
| // Now that all of the cleanup is complete and the browser side is notified, |
| // start using the RenderFrameProxy. |
| // |
| // 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. Catch any cases that the |
| // RenderView's main_render_frame_ isn't cleared below (whether swap returns |
| // false or not). |
| // |
| // This executes the unload handlers on this frame and its local descendants. |
| bool success = frame_->Swap(proxy->web_frame()); |
| |
| // For main frames, the swap should have cleared the RenderView's pointer to |
| // this frame. |
| if (is_main_frame) |
| CHECK(!render_view->main_render_frame_); |
| |
| if (!success) { |
| // The swap can fail when the frame is detached during swap (this can |
| // happen while running the unload handlers). When that happens, delete |
| // the proxy. |
| proxy->FrameDetached(blink::WebRemoteFrameClient::DetachType::kSwap); |
| return; |
| } |
| |
| if (is_loading) |
| proxy->OnDidStartLoading(); |
| |
| // Initialize the WebRemoteFrame with the replication state passed by the |
| // process that is now rendering the frame. |
| proxy->SetReplicatedState(replicated_frame_state); |
| |
| // Notify the browser that this frame was swapped. Use the RenderThread |
| // directly 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_swapout_ack = base::BindOnce( |
| [](int routing_id, bool is_main_frame) { |
| RenderThread::Get()->Send(new FrameHostMsg_SwapOut_ACK(routing_id)); |
| }, |
| routing_id, is_main_frame); |
| task_runner->PostTask(FROM_HERE, std::move(send_swapout_ack)); |
| } |
| |
| void RenderFrameImpl::OnSwapIn() { |
| SwapIn(); |
| } |
| |
| void RenderFrameImpl::OnDeleteFrame() { |
| // This will result in a call to RenderFrameImpl::FrameDetached, which |
| // deletes the object. Do not access |this| after detach. |
| frame_->Detach(); |
| } |
| |
| void RenderFrameImpl::OnContextMenuClosed( |
| const CustomContextMenuContext& custom_context) { |
| if (custom_context.request_id) { |
| // External request, should be in our map. |
| ContextMenuClient* client = |
| pending_context_menus_.Lookup(custom_context.request_id); |
| if (client) { |
| client->OnMenuClosed(custom_context.request_id); |
| pending_context_menus_.Remove(custom_context.request_id); |
| } |
| } else { |
| if (custom_context.link_followed.is_valid()) |
| frame_->SendPings(custom_context.link_followed); |
| } |
| |
| render_view()->webview()->DidCloseContextMenu(); |
| } |
| |
| void RenderFrameImpl::OnCustomContextMenuAction( |
| const CustomContextMenuContext& custom_context, |
| unsigned action) { |
| if (custom_context.request_id) { |
| // External context menu request, look in our map. |
| ContextMenuClient* client = |
| pending_context_menus_.Lookup(custom_context.request_id); |
| if (client) |
| client->OnMenuAction(custom_context.request_id, action); |
| } else { |
| // Internal request, forward to WebKit. |
| render_view_->webview()->PerformCustomContextMenuAction(action); |
| } |
| } |
| |
| #if defined(OS_MACOSX) |
| void RenderFrameImpl::OnCopyToFindPboard() { |
| // Since the find pasteboard supports only plain text, this can be simpler |
| // than the |OnCopy()| case. |
| if (frame_->HasSelection()) { |
| if (!clipboard_host_) { |
| auto* platform = RenderThreadImpl::current_blink_platform_impl(); |
| platform->GetConnector()->BindInterface(platform->GetBrowserServiceName(), |
| &clipboard_host_); |
| clipboard_host_.set_connection_error_handler(base::BindOnce( |
| &RenderFrameImpl::OnClipboardHostError, base::Unretained(this))); |
| } |
| base::string16 selection = frame_->SelectionAsText().Utf16(); |
| clipboard_host_->WriteStringToFindPboard(selection); |
| } |
| } |
| |
| void RenderFrameImpl::OnClipboardHostError() { |
| clipboard_host_.reset(); |
| } |
| #endif |
| |
| void RenderFrameImpl::OnCopyImageAt(int x, int y) { |
| blink::WebFloatRect viewport_position(x, y, 0, 0); |
| GetLocalRootRenderWidget()->ConvertWindowToViewport(&viewport_position); |
| frame_->CopyImageAt(WebPoint(viewport_position.x, viewport_position.y)); |
| } |
| |
| void RenderFrameImpl::OnSaveImageAt(int x, int y) { |
| blink::WebFloatRect viewport_position(x, y, 0, 0); |
| GetLocalRootRenderWidget()->ConvertWindowToViewport(&viewport_position); |
| frame_->SaveImageAt(WebPoint(viewport_position.x, viewport_position.y)); |
| } |
| |
| void RenderFrameImpl::OnAddMessageToConsole(ConsoleMessageLevel level, |
| const std::string& message) { |
| AddMessageToConsole(level, message); |
| } |
| |
| void RenderFrameImpl::OnJavaScriptExecuteRequest( |
| const base::string16& jscript, |
| int id, |
| bool notify_result) { |
| TRACE_EVENT_INSTANT0("test_tracing", "OnJavaScriptExecuteRequest", |
| TRACE_EVENT_SCOPE_THREAD); |
| |
| v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); |
| v8::Local<v8::Value> result = frame_->ExecuteScriptAndReturnValue( |
| WebScriptSource(WebString::FromUTF16(jscript))); |
| |
| HandleJavascriptExecutionResult(jscript, id, notify_result, result); |
| } |
| |
| void RenderFrameImpl::OnJavaScriptExecuteRequestForTests( |
| const base::string16& jscript, |
| int id, |
| bool notify_result, |
| bool has_user_gesture) { |
| TRACE_EVENT_INSTANT0("test_tracing", "OnJavaScriptExecuteRequestForTests", |
| TRACE_EVENT_SCOPE_THREAD); |
| |
| // A bunch of tests expect to run code in the context of a user gesture, which |
| // can grant additional privileges (e.g. the ability to create popups). |
| std::unique_ptr<blink::WebScopedUserGesture> gesture( |
| has_user_gesture ? new blink::WebScopedUserGesture(frame_) : nullptr); |
| v8::HandleScope handle_scope(blink::MainThreadIsolate()); |
| v8::Local<v8::Value> result = frame_->ExecuteScriptAndReturnValue( |
| WebScriptSource(WebString::FromUTF16(jscript))); |
| |
| HandleJavascriptExecutionResult(jscript, id, notify_result, result); |
| } |
| |
| void RenderFrameImpl::OnJavaScriptExecuteRequestInIsolatedWorld( |
| const base::string16& jscript, |
| int id, |
| bool notify_result, |
| int world_id) { |
| TRACE_EVENT_INSTANT0("test_tracing", |
| "OnJavaScriptExecuteRequestInIsolatedWorld", |
| TRACE_EVENT_SCOPE_THREAD); |
| |
| if (world_id <= ISOLATED_WORLD_ID_GLOBAL || |
| world_id > ISOLATED_WORLD_ID_MAX) { |
| // Return if the world_id is not valid. world_id is passed as a plain int |
| // over IPC and needs to be verified here, in the IPC endpoint. |
| NOTREACHED(); |
| return; |
| } |
| |
| v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); |
| WebScriptSource script = WebScriptSource(WebString::FromUTF16(jscript)); |
| JavaScriptIsolatedWorldRequest* request = new JavaScriptIsolatedWorldRequest( |
| id, notify_result, routing_id_, weak_factory_.GetWeakPtr()); |
| frame_->RequestExecuteScriptInIsolatedWorld( |
| world_id, &script, 1, false, WebLocalFrame::kSynchronous, request); |
| } |
| |
| RenderFrameImpl::JavaScriptIsolatedWorldRequest::JavaScriptIsolatedWorldRequest( |
| int id, |
| bool notify_result, |
| int routing_id, |
| base::WeakPtr<RenderFrameImpl> render_frame_impl) |
| : id_(id), |
| notify_result_(notify_result), |
| routing_id_(routing_id), |
| render_frame_impl_(render_frame_impl) { |
| } |
| |
| RenderFrameImpl::JavaScriptIsolatedWorldRequest:: |
| ~JavaScriptIsolatedWorldRequest() { |
| } |
| |
| void RenderFrameImpl::JavaScriptIsolatedWorldRequest::Completed( |
| const blink::WebVector<v8::Local<v8::Value>>& result) { |
| if (!render_frame_impl_.get()) { |
| return; |
| } |
| |
| if (notify_result_) { |
| base::ListValue list; |
| if (!result.IsEmpty()) { |
| // It's safe to always use the main world context when converting |
| // here. V8ValueConverterImpl shouldn't actually care about the |
| // context scope, and it switches to v8::Object's creation context |
| // when encountered. (from extensions/renderer/script_injection.cc) |
| v8::Local<v8::Context> context = |
| render_frame_impl_.get()->frame_->MainWorldScriptContext(); |
| v8::Context::Scope context_scope(context); |
| V8ValueConverterImpl converter; |
| converter.SetDateAllowed(true); |
| converter.SetRegExpAllowed(true); |
| for (const auto& value : result) { |
| std::unique_ptr<base::Value> result_value( |
| converter.FromV8Value(value, context)); |
| list.Append(result_value ? std::move(result_value) |
| : std::make_unique<base::Value>()); |
| } |
| } else { |
| list.Set(0, std::make_unique<base::Value>()); |
| } |
| render_frame_impl_.get()->Send( |
| new FrameHostMsg_JavaScriptExecuteResponse(routing_id_, id_, list)); |
| } |
| |
| delete this; |
| } |
| |
| void RenderFrameImpl::HandleJavascriptExecutionResult( |
| const base::string16& jscript, |
| int id, |
| bool notify_result, |
| v8::Local<v8::Value> result) { |
| if (notify_result) { |
| base::ListValue list; |
| if (!result.IsEmpty()) { |
| v8::Local<v8::Context> context = frame_->MainWorldScriptContext(); |
| v8::Context::Scope context_scope(context); |
| V8ValueConverterImpl converter; |
| converter.SetDateAllowed(true); |
| converter.SetRegExpAllowed(true); |
| std::unique_ptr<base::Value> result_value( |
| converter.FromV8Value(result, context)); |
| list.Set(0, result_value ? std::move(result_value) |
| : std::make_unique<base::Value>()); |
| } else { |
| list.Set(0, std::make_unique<base::Value>()); |
| } |
| Send(new FrameHostMsg_JavaScriptExecuteResponse(routing_id_, id, list)); |
| } |
| } |
| |
| void RenderFrameImpl::OnVisualStateRequest(uint64_t id) { |
| GetLocalRootRenderWidget()->QueueMessage( |
| new FrameHostMsg_VisualStateResponse(routing_id_, id)); |
| } |
| |
| void RenderFrameImpl::OnSetAccessibilityMode(ui::AXMode new_mode) { |
| if (accessibility_mode_ == new_mode) |
| return; |
| ui::AXMode old_mode = accessibility_mode_; |
| accessibility_mode_ = new_mode; |
| |
| if (new_mode.has_mode(ui::AXMode::kWebContents) && |
| !old_mode.has_mode(ui::AXMode::kWebContents)) { |
| render_accessibility_ = new RenderAccessibilityImpl(this, new_mode); |
| } else if (!new_mode.has_mode(ui::AXMode::kWebContents) && |
| old_mode.has_mode(ui::AXMode::kWebContents)) { |
| delete render_accessibility_; |
| render_accessibility_ = nullptr; |
| } |
| |
| for (auto& observer : observers_) |
| observer.AccessibilityModeChanged(); |
| } |
| |
| void RenderFrameImpl::OnSnapshotAccessibilityTree(int callback_id, |
| ui::AXMode ax_mode) { |
| AXContentTreeUpdate response; |
| RenderAccessibilityImpl::SnapshotAccessibilityTree(this, &response, ax_mode); |
| Send(new AccessibilityHostMsg_SnapshotResponse( |
| routing_id_, callback_id, response)); |
| } |
| |
| void RenderFrameImpl::OnPortalActivated() { |
| frame_->OnPortalActivated(); |
| } |
| |
| #if defined(OS_ANDROID) |
| void RenderFrameImpl::ExtractSmartClipData( |
| const gfx::Rect& rect, |
| ExtractSmartClipDataCallback callback) { |
| blink::WebString clip_text; |
| blink::WebString clip_html; |
| blink::WebRect clip_rect; |
| GetWebFrame()->ExtractSmartClipData(rect, clip_text, clip_html, clip_rect); |
| std::move(callback).Run(clip_text.Utf16(), clip_html.Utf16(), clip_rect); |
| } |
| #endif // defined(OS_ANDROID) |
| |
| void RenderFrameImpl::OnUpdateOpener(int opener_routing_id) { |
| WebFrame* opener = ResolveOpener(opener_routing_id); |
| frame_->SetOpener(opener); |
| } |
| |
| void RenderFrameImpl::OnDidUpdateFramePolicy( |
| const blink::FramePolicy& frame_policy) { |
| frame_->SetFrameOwnerPolicy(frame_policy.sandbox_flags, |
| frame_policy.container_policy); |
| } |
| |
| void RenderFrameImpl::OnSetFrameOwnerProperties( |
| const FrameOwnerProperties& frame_owner_properties) { |
| DCHECK(frame_); |
| frame_->SetFrameOwnerProperties( |
| ConvertFrameOwnerPropertiesToWebFrameOwnerProperties( |
| frame_owner_properties)); |
| } |
| |
| void RenderFrameImpl::OnAdvanceFocus(blink::WebFocusType type, |
| int32_t source_routing_id) { |
| RenderFrameProxy* source_frame = |
| RenderFrameProxy::FromRoutingID(source_routing_id); |
| if (!source_frame) { |
| render_view_->webview()->SetInitialFocus(type == |
| blink::kWebFocusTypeBackward); |
| return; |
| } |
| |
| render_view_->webview()->AdvanceFocusAcrossFrames( |
| type, source_frame->web_frame(), frame_); |
| } |
| |
| void RenderFrameImpl::OnAdvanceFocusInForm(blink::WebFocusType focus_type) { |
| if (render_view_->webview()->FocusedFrame() != frame_) |
| return; |
| frame_->AdvanceFocusInForm(focus_type); |
| } |
| |
| void RenderFrameImpl::OnSetFocusedFrame() { |
| // This uses focusDocumentView rather than setFocusedFrame so that focus/blur |
| // events are properly dispatched on any currently focused elements. |
| render_view_->webview()->FocusDocumentView(frame_); |
| } |
| |
| void RenderFrameImpl::OnTextTrackSettingsChanged( |
| const FrameMsg_TextTrackSettings_Params& params) { |
| DCHECK(!frame_->Parent()); |
| if (!render_view_->webview()) |
| return; |
| |
| if (params.text_tracks_enabled) { |
| render_view_->webview()->GetSettings()->SetTextTrackKindUserPreference( |
| WebSettings::TextTrackKindUserPreference::kCaptions); |
| } else { |
| render_view_->webview()->GetSettings()->SetTextTrackKindUserPreference( |
| WebSettings::TextTrackKindUserPreference::kDefault); |
| } |
| render_view_->webview()->GetSettings()->SetTextTrackBackgroundColor( |
| WebString::FromUTF8(params.text_track_background_color)); |
| render_view_->webview()->GetSettings()->SetTextTrackFontFamily( |
| WebString::FromUTF8(params.text_track_font_family)); |
| render_view_->webview()->GetSettings()->SetTextTrackFontStyle( |
| WebString::FromUTF8(params.text_track_font_style)); |
| render_view_->webview()->GetSettings()->SetTextTrackFontVariant( |
| WebString::FromUTF8(params.text_track_font_variant)); |
| render_view_->webview()->GetSettings()->SetTextTrackTextColor( |
| WebString::FromUTF8(params.text_track_text_color)); |
| render_view_->webview()->GetSettings()->SetTextTrackTextShadow( |
| WebString::FromUTF8(params.text_track_text_shadow)); |
| render_view_->webview()->GetSettings()->SetTextTrackTextSize( |
| WebString::FromUTF8(params.text_track_text_size)); |
| } |
|