| // 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 <map> |
| #include <string> |
| |
| #include "base/auto_reset.h" |
| #include "base/command_line.h" |
| #include "base/debug/alias.h" |
| #include "base/debug/asan_invalid_access.h" |
| #include "base/debug/dump_without_crashing.h" |
| #include "base/i18n/char_iterator.h" |
| #include "base/metrics/histogram.h" |
| #include "base/process/process.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "cc/base/switches.h" |
| #include "components/scheduler/renderer/renderer_scheduler.h" |
| #include "content/child/appcache/appcache_dispatcher.h" |
| #include "content/child/permissions/permission_dispatcher.h" |
| #include "content/child/plugin_messages.h" |
| #include "content/child/quota_dispatcher.h" |
| #include "content/child/request_extra_data.h" |
| #include "content/child/service_worker/service_worker_handle_reference.h" |
| #include "content/child/service_worker/service_worker_network_provider.h" |
| #include "content/child/service_worker/service_worker_provider_context.h" |
| #include "content/child/service_worker/web_service_worker_provider_impl.h" |
| #include "content/child/v8_value_converter_impl.h" |
| #include "content/child/web_url_loader_impl.h" |
| #include "content/child/web_url_request_util.h" |
| #include "content/child/webmessageportchannel_impl.h" |
| #include "content/child/websocket_bridge.h" |
| #include "content/child/weburlresponse_extradata_impl.h" |
| #include "content/common/accessibility_messages.h" |
| #include "content/common/clipboard_messages.h" |
| #include "content/common/frame_messages.h" |
| #include "content/common/frame_replication_state.h" |
| #include "content/common/input_messages.h" |
| #include "content/common/navigation_params.h" |
| #include "content/common/service_worker/service_worker_types.h" |
| #include "content/common/site_isolation_policy.h" |
| #include "content/common/swapped_out_messages.h" |
| #include "content/common/view_messages.h" |
| #include "content/public/common/bindings_policy.h" |
| #include "content/public/common/content_constants.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/common/context_menu_params.h" |
| #include "content/public/common/isolated_world_ids.h" |
| #include "content/public/common/page_state.h" |
| #include "content/public/common/resource_response.h" |
| #include "content/public/common/url_constants.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/navigation_state.h" |
| #include "content/public/renderer/render_frame_observer.h" |
| #include "content/public/renderer/renderer_ppapi_host.h" |
| #include "content/renderer/accessibility/renderer_accessibility.h" |
| #include "content/renderer/bluetooth/web_bluetooth_impl.h" |
| #include "content/renderer/browser_plugin/browser_plugin.h" |
| #include "content/renderer/browser_plugin/browser_plugin_manager.h" |
| #include "content/renderer/child_frame_compositing_helper.h" |
| #include "content/renderer/context_menu_params_builder.h" |
| #include "content/renderer/devtools/devtools_agent.h" |
| #include "content/renderer/dom_automation_controller.h" |
| #include "content/renderer/external_popup_menu.h" |
| #include "content/renderer/geolocation_dispatcher.h" |
| #include "content/renderer/gpu/gpu_benchmarking_extension.h" |
| #include "content/renderer/history_controller.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/internal_document_state_data.h" |
| #include "content/renderer/manifest/manifest_manager.h" |
| #include "content/renderer/media/audio_renderer_mixer_manager.h" |
| #include "content/renderer/media/crypto/render_cdm_factory.h" |
| #include "content/renderer/media/media_permission_dispatcher.h" |
| #include "content/renderer/media/media_stream_dispatcher.h" |
| #include "content/renderer/media/media_stream_renderer_factory_impl.h" |
| #include "content/renderer/media/midi_dispatcher.h" |
| #include "content/renderer/media/render_media_log.h" |
| #include "content/renderer/media/user_media_client_impl.h" |
| #include "content/renderer/media/webmediaplayer_ms.h" |
| #include "content/renderer/memory_benchmarking_extension.h" |
| #include "content/renderer/mojo/service_registry_js_wrapper.h" |
| #include "content/renderer/navigation_state_impl.h" |
| #include "content/renderer/notification_permission_dispatcher.h" |
| #include "content/renderer/npapi/plugin_channel_host.h" |
| #include "content/renderer/pepper/plugin_instance_throttler_impl.h" |
| #include "content/renderer/presentation/presentation_dispatcher.h" |
| #include "content/renderer/push_messaging/push_messaging_dispatcher.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_webapplicationcachehost_impl.h" |
| #include "content/renderer/renderer_webcolorchooser_impl.h" |
| #include "content/renderer/screen_orientation/screen_orientation_dispatcher.h" |
| #include "content/renderer/shared_worker_repository.h" |
| #include "content/renderer/skia_benchmarking_extension.h" |
| #include "content/renderer/stats_collection_controller.h" |
| #include "content/renderer/web_ui_extension.h" |
| #include "content/renderer/websharedworker_proxy.h" |
| #include "gin/modules/module_registry.h" |
| #include "media/base/audio_renderer_mixer_input.h" |
| #include "media/base/media_log.h" |
| #include "media/blink/webencryptedmediaclient_impl.h" |
| #include "media/blink/webmediaplayer_impl.h" |
| #include "media/renderers/gpu_video_accelerator_factories.h" |
| #include "mojo/common/url_type_converters.h" |
| #include "net/base/data_url.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| #include "net/http/http_util.h" |
| #include "third_party/WebKit/public/platform/WebStorageQuotaCallbacks.h" |
| #include "third_party/WebKit/public/platform/WebString.h" |
| #include "third_party/WebKit/public/platform/WebURL.h" |
| #include "third_party/WebKit/public/platform/WebURLError.h" |
| #include "third_party/WebKit/public/platform/WebURLResponse.h" |
| #include "third_party/WebKit/public/platform/WebVector.h" |
| #include "third_party/WebKit/public/platform/modules/webusb/WebUSBClient.h" |
| #include "third_party/WebKit/public/web/WebColorSuggestion.h" |
| #include "third_party/WebKit/public/web/WebDocument.h" |
| #include "third_party/WebKit/public/web/WebFrameWidget.h" |
| #include "third_party/WebKit/public/web/WebGlyphCache.h" |
| #include "third_party/WebKit/public/web/WebKit.h" |
| #include "third_party/WebKit/public/web/WebLocalFrame.h" |
| #include "third_party/WebKit/public/web/WebMediaStreamRegistry.h" |
| #include "third_party/WebKit/public/web/WebNavigationPolicy.h" |
| #include "third_party/WebKit/public/web/WebPlugin.h" |
| #include "third_party/WebKit/public/web/WebPluginParams.h" |
| #include "third_party/WebKit/public/web/WebRange.h" |
| #include "third_party/WebKit/public/web/WebScopedUserGesture.h" |
| #include "third_party/WebKit/public/web/WebScriptSource.h" |
| #include "third_party/WebKit/public/web/WebSearchableFormData.h" |
| #include "third_party/WebKit/public/web/WebSecurityOrigin.h" |
| #include "third_party/WebKit/public/web/WebSecurityPolicy.h" |
| #include "third_party/WebKit/public/web/WebSerializedScriptValue.h" |
| #include "third_party/WebKit/public/web/WebSettings.h" |
| #include "third_party/WebKit/public/web/WebSurroundingText.h" |
| #include "third_party/WebKit/public/web/WebUserGestureIndicator.h" |
| #include "third_party/WebKit/public/web/WebView.h" |
| #include "third_party/mojo/src/mojo/edk/js/core.h" |
| #include "third_party/mojo/src/mojo/edk/js/support.h" |
| #include "url/url_util.h" |
| |
| #if defined(ENABLE_PLUGINS) |
| #include "content/renderer/npapi/webplugin_impl.h" |
| #include "content/renderer/pepper/pepper_browser_connection.h" |
| #include "content/renderer/pepper/pepper_plugin_instance_impl.h" |
| #include "content/renderer/pepper/pepper_webplugin_impl.h" |
| #include "content/renderer/pepper/plugin_module.h" |
| #endif |
| |
| #if defined(ENABLE_WEBRTC) |
| #include "content/renderer/media/rtc_peer_connection_handler.h" |
| #endif |
| |
| #if defined(OS_ANDROID) |
| #include <cpu-features.h> |
| |
| #include "content/common/gpu/client/context_provider_command_buffer.h" |
| #include "content/renderer/android/synchronous_compositor_factory.h" |
| #include "content/renderer/java/gin_java_bridge_dispatcher.h" |
| #include "content/renderer/media/android/renderer_media_player_manager.h" |
| #include "content/renderer/media/android/stream_texture_factory_impl.h" |
| #include "content/renderer/media/android/webmediaplayer_android.h" |
| #else |
| #include "cc/blink/context_provider_web_context.h" |
| #include "content/renderer/usb/web_usb_client_impl.h" |
| #include "device/devices_app/public/cpp/constants.h" |
| #endif |
| |
| #if defined(ENABLE_PEPPER_CDMS) |
| #include "content/renderer/media/crypto/pepper_cdm_wrapper_impl.h" |
| #elif defined(ENABLE_BROWSER_CDMS) |
| #include "content/renderer/media/crypto/renderer_cdm_manager.h" |
| #endif |
| |
| #if defined(ENABLE_MOJO_MEDIA) |
| #include "media/mojo/services/mojo_cdm_factory.h" |
| #include "media/mojo/services/mojo_renderer_factory.h" |
| #include "mojo/application/public/cpp/connect.h" |
| #include "mojo/application/public/interfaces/shell.mojom.h" |
| #include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" |
| #else |
| #include "media/renderers/default_renderer_factory.h" |
| #endif |
| |
| #if defined(ENABLE_WEBVR) |
| #include "content/renderer/vr/vr_dispatcher.h" |
| #endif |
| |
| using blink::WebContentDecryptionModule; |
| using blink::WebContextMenuData; |
| using blink::WebData; |
| using blink::WebDataSource; |
| using blink::WebDocument; |
| using blink::WebDOMEvent; |
| using blink::WebDOMMessageEvent; |
| using blink::WebElement; |
| using blink::WebExternalPopupMenu; |
| using blink::WebExternalPopupMenuClient; |
| using blink::WebFrame; |
| using blink::WebHistoryItem; |
| using blink::WebHTTPBody; |
| using blink::WebLocalFrame; |
| using blink::WebMediaPlayer; |
| using blink::WebMediaPlayerClient; |
| using blink::WebMediaPlayerEncryptedMediaClient; |
| using blink::WebNavigationPolicy; |
| using blink::WebNavigationType; |
| using blink::WebNode; |
| using blink::WebPluginParams; |
| using blink::WebPopupMenuInfo; |
| using blink::WebRange; |
| using blink::WebReferrerPolicy; |
| using blink::WebScriptSource; |
| using blink::WebSearchableFormData; |
| using blink::WebSecurityOrigin; |
| using blink::WebSecurityPolicy; |
| using blink::WebSerializedScriptValue; |
| using blink::WebServiceWorkerProvider; |
| using blink::WebSettings; |
| using blink::WebStorageQuotaCallbacks; |
| using blink::WebString; |
| using blink::WebURL; |
| using blink::WebURLError; |
| using blink::WebURLRequest; |
| using blink::WebURLResponse; |
| using blink::WebUserGestureIndicator; |
| using blink::WebVector; |
| using blink::WebView; |
| using base::Time; |
| using base::TimeDelta; |
| |
| namespace content { |
| |
| namespace { |
| |
| const char kDefaultAcceptHeader[] = |
| "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/" |
| "*;q=0.8"; |
| const char kAcceptHeader[] = "Accept"; |
| |
| const size_t kExtraCharsBeforeAndAfterSelection = 100; |
| |
| typedef std::map<int, RenderFrameImpl*> RoutingIDFrameMap; |
| static base::LazyInstance<RoutingIDFrameMap> g_routing_id_frame_map = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| typedef std::map<blink::WebFrame*, RenderFrameImpl*> FrameMap; |
| base::LazyInstance<FrameMap> g_frame_map = LAZY_INSTANCE_INITIALIZER; |
| |
| int64 ExtractPostId(HistoryEntry* entry) { |
| if (!entry) |
| return -1; |
| |
| const WebHistoryItem& item = entry->root(); |
| if (item.isNull() || item.httpBody().isNull()) |
| return -1; |
| |
| return item.httpBody().identifier(); |
| } |
| |
| WebURLResponseExtraDataImpl* GetExtraDataFromResponse( |
| const WebURLResponse& response) { |
| return static_cast<WebURLResponseExtraDataImpl*>(response.extraData()); |
| } |
| |
| void GetRedirectChain(WebDataSource* ds, std::vector<GURL>* result) { |
| // Replace any occurrences of swappedout:// with about:blank. |
| const WebURL& blank_url = GURL(url::kAboutBlankURL); |
| WebVector<WebURL> urls; |
| ds->redirectChain(urls); |
| result->reserve(urls.size()); |
| for (size_t i = 0; i < urls.size(); ++i) { |
| if (urls[i] != GURL(kSwappedOutURL)) |
| result->push_back(urls[i]); |
| else |
| result->push_back(blank_url); |
| } |
| } |
| |
| // Returns the original request url. If there is no redirect, the original |
| // url is the same as ds->request()->url(). If the WebDataSource belongs to a |
| // frame was loaded by loadData, the original url will be ds->unreachableURL() |
| GURL GetOriginalRequestURL(WebDataSource* ds) { |
| // WebDataSource 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 (ds->hasUnreachableURL()) |
| return ds->unreachableURL(); |
| |
| std::vector<GURL> redirects; |
| GetRedirectChain(ds, &redirects); |
| if (!redirects.empty()) |
| return redirects.at(0); |
| |
| return ds->originalRequest().url(); |
| } |
| |
| NOINLINE void CrashIntentionally() { |
| // NOTE(shess): Crash directly rather than using NOTREACHED() so |
| // that the signature is easier to triage in crash reports. |
| volatile int* zero = NULL; |
| *zero = 0; |
| } |
| |
| NOINLINE void BadCastCrashIntentionally() { |
| class A { |
| virtual void f() {} |
| }; |
| |
| class B { |
| virtual void f() {} |
| }; |
| |
| A a; |
| (void)(B*)&a; |
| } |
| |
| #if defined(ADDRESS_SANITIZER) || defined(SYZYASAN) |
| 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. |
| const char kCrashDomain[] = "crash"; |
| const char kHeapOverflow[] = "/heap-overflow"; |
| const char kHeapUnderflow[] = "/heap-underflow"; |
| const char kUseAfterFree[] = "/use-after-free"; |
| #if defined(SYZYASAN) |
| const char kCorruptHeapBlock[] = "/corrupt-heap-block"; |
| const char kCorruptHeap[] = "/corrupt-heap"; |
| #endif |
| |
| if (!url.DomainIs(kCrashDomain)) |
| return; |
| |
| if (!url.has_path()) |
| return; |
| |
| std::string crash_type(url.path()); |
| if (crash_type == kHeapOverflow) { |
| base::debug::AsanHeapOverflow(); |
| } else if (crash_type == kHeapUnderflow ) { |
| base::debug::AsanHeapUnderflow(); |
| } else if (crash_type == kUseAfterFree) { |
| base::debug::AsanHeapUseAfterFree(); |
| #if defined(SYZYASAN) |
| } else if (crash_type == kCorruptHeapBlock) { |
| base::debug::AsanCorruptHeapBlock(); |
| } else if (crash_type == kCorruptHeap) { |
| base::debug::AsanCorruptHeap(); |
| #endif |
| } |
| } |
| #endif // ADDRESS_SANITIZER || SYZYASAN |
| |
| void MaybeHandleDebugURL(const GURL& url) { |
| if (!url.SchemeIs(kChromeUIScheme)) |
| return; |
| if (url == GURL(kChromeUIBadCastCrashURL)) { |
| BadCastCrashIntentionally(); |
| } else if (url == GURL(kChromeUICrashURL)) { |
| CrashIntentionally(); |
| } else if (url == GURL(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(); |
| } else if (url == GURL(kChromeUIKillURL)) { |
| base::Process::Current().Terminate(1, false); |
| } else if (url == GURL(kChromeUIHangURL)) { |
| for (;;) { |
| base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); |
| } |
| } else if (url == GURL(kChromeUIShorthangURL)) { |
| base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(20)); |
| } |
| |
| #if defined(ADDRESS_SANITIZER) || defined(SYZYASAN) |
| MaybeTriggerAsanError(url); |
| #endif // ADDRESS_SANITIZER || SYZYASAN |
| } |
| |
| // Returns false unless this is a top-level navigation. |
| bool IsTopLevelNavigation(WebFrame* frame) { |
| return frame->parent() == NULL; |
| } |
| |
| // Returns false unless this is a top-level navigation that crosses origins. |
| bool IsNonLocalTopLevelNavigation(const GURL& url, |
| WebFrame* frame, |
| WebNavigationType type, |
| bool is_form_post) { |
| if (!IsTopLevelNavigation(frame)) |
| return false; |
| |
| // Navigations initiated within Webkit are not sent out to the external host |
| // in the following cases. |
| // 1. The url scheme is not http/https |
| // 2. The origin of the url and the opener is the same in which case the |
| // opener relationship is maintained. |
| // 3. Reloads/form submits/back forward navigations |
| if (!url.SchemeIs(url::kHttpScheme) && !url.SchemeIs(url::kHttpsScheme)) |
| return false; |
| |
| if (type != blink::WebNavigationTypeReload && |
| type != blink::WebNavigationTypeBackForward && !is_form_post) { |
| // The opener relationship between the new window and the parent allows the |
| // new window to script the parent and vice versa. This is not allowed if |
| // the origins of the two domains are different. This can be treated as a |
| // top level navigation and routed back to the host. |
| blink::WebFrame* opener = frame->opener(); |
| if (!opener) |
| return true; |
| |
| if (url.GetOrigin() != GURL(opener->document().url()).GetOrigin()) |
| return true; |
| } |
| return false; |
| } |
| |
| WebURLRequest CreateURLRequestForNavigation( |
| const CommonNavigationParams& common_params, |
| scoped_ptr<StreamOverrideParameters> stream_override, |
| bool is_view_source_mode_enabled) { |
| WebURLRequest request(common_params.url); |
| if (is_view_source_mode_enabled) |
| request.setCachePolicy(WebURLRequest::ReturnCacheDataElseLoad); |
| |
| if (common_params.referrer.url.is_valid()) { |
| WebString web_referrer = WebSecurityPolicy::generateReferrerHeader( |
| common_params.referrer.policy, |
| common_params.url, |
| WebString::fromUTF8(common_params.referrer.url.spec())); |
| if (!web_referrer.isEmpty()) |
| request.setHTTPReferrer(web_referrer, common_params.referrer.policy); |
| } |
| |
| RequestExtraData* extra_data = new RequestExtraData(); |
| extra_data->set_stream_override(stream_override.Pass()); |
| request.setExtraData(extra_data); |
| |
| // Set the ui timestamp for this navigation. Currently the timestamp here is |
| // only non empty when the navigation was triggered by an Android intent. The |
| // timestamp is converted to a double version supported by blink. It will be |
| // passed back to the browser in the DidCommitProvisionalLoad and the |
| // DocumentLoadComplete IPCs. |
| base::TimeDelta ui_timestamp = common_params.ui_timestamp - base::TimeTicks(); |
| request.setUiStartTime(ui_timestamp.InSecondsF()); |
| request.setInputPerfMetricReportPolicy( |
| static_cast<WebURLRequest::InputToLoadPerfMetricReportPolicy>( |
| common_params.report_type)); |
| return request; |
| } |
| |
| void UpdateFrameNavigationTiming(WebFrame* frame, |
| base::TimeTicks browser_navigation_start, |
| base::TimeTicks renderer_navigation_start) { |
| // The browser provides the navigation_start time to bootstrap the |
| // Navigation Timing information for the browser-initiated navigations. In |
| // case of cross-process navigations, this carries over the time of |
| // finishing the onbeforeunload handler of the previous page. |
| DCHECK(!browser_navigation_start.is_null()); |
| if (frame->provisionalDataSource()) { |
| // |browser_navigation_start| is likely before this process existed, so we |
| // can't use InterProcessTimeTicksConverter. We need at least to ensure |
| // that the browser-side navigation start we set is not later than the one |
| // on the renderer side. |
| base::TimeTicks navigation_start = std::min( |
| browser_navigation_start, renderer_navigation_start); |
| double navigation_start_seconds = |
| (navigation_start - base::TimeTicks()).InSecondsF(); |
| frame->provisionalDataSource()->setNavigationStartTime( |
| navigation_start_seconds); |
| // TODO(clamy): We need to provide additional timing values for the |
| // Navigation Timing API to work with browser-side navigations. |
| } |
| } |
| |
| // PlzNavigate |
| CommonNavigationParams MakeCommonNavigationParams( |
| blink::WebURLRequest* request, |
| bool should_replace_current_entry) { |
| const RequestExtraData kEmptyData; |
| const RequestExtraData* extra_data = |
| static_cast<RequestExtraData*>(request->extraData()); |
| if (!extra_data) |
| extra_data = &kEmptyData; |
| Referrer referrer( |
| GURL(request->httpHeaderField(WebString::fromUTF8("Referer")).latin1()), |
| request->referrerPolicy()); |
| |
| // Set the ui timestamp for this navigation. Currently the timestamp here is |
| // only non empty when the navigation was triggered by an Android intent, or |
| // by the user clicking on a link. The timestamp is converted from a double |
| // version supported by blink. It will be passed back to the renderer in the |
| // CommitNavigation IPC, and then back to the browser again in the |
| // DidCommitProvisionalLoad and the DocumentLoadComplete IPCs. |
| base::TimeTicks ui_timestamp = |
| base::TimeTicks() + base::TimeDelta::FromSecondsD(request->uiStartTime()); |
| FrameMsg_UILoadMetricsReportType::Value report_type = |
| static_cast<FrameMsg_UILoadMetricsReportType::Value>( |
| request->inputPerfMetricReportPolicy()); |
| return CommonNavigationParams( |
| request->url(), referrer, extra_data->transition_type(), |
| FrameMsg_Navigate_Type::NORMAL, true, should_replace_current_entry, |
| ui_timestamp, report_type, GURL(), GURL()); |
| } |
| |
| #if !defined(OS_ANDROID) || defined(ENABLE_MEDIA_PIPELINE_ON_ANDROID) |
| media::Context3D GetSharedMainThreadContext3D() { |
| cc::ContextProvider* provider = |
| RenderThreadImpl::current()->SharedMainThreadContextProvider().get(); |
| if (!provider) |
| return media::Context3D(); |
| return media::Context3D(provider->ContextGL(), provider->GrContext()); |
| } |
| #endif |
| |
| bool IsReload(FrameMsg_Navigate_Type::Value navigation_type) { |
| return navigation_type == FrameMsg_Navigate_Type::RELOAD || |
| navigation_type == FrameMsg_Navigate_Type::RELOAD_IGNORING_CACHE || |
| navigation_type == FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL; |
| } |
| |
| RenderFrameImpl::CreateRenderFrameImplFunction g_create_render_frame_impl = |
| nullptr; |
| |
| void OnGotContentHandlerID(uint32_t content_handler_id) {} |
| |
| } // namespace |
| |
| // static |
| RenderFrameImpl* RenderFrameImpl::Create(RenderViewImpl* render_view, |
| int32 routing_id) { |
| DCHECK(routing_id != MSG_ROUTING_NONE); |
| CreateParams params(render_view, routing_id); |
| |
| if (g_create_render_frame_impl) |
| return g_create_render_frame_impl(params); |
| else |
| return new RenderFrameImpl(params); |
| } |
| |
| // static |
| RenderFrame* RenderFrame::FromRoutingID(int routing_id) { |
| return RenderFrameImpl::FromRoutingID(routing_id); |
| } |
| |
| // static |
| RenderFrameImpl* RenderFrameImpl::FromRoutingID(int routing_id) { |
| RoutingIDFrameMap::iterator iter = |
| g_routing_id_frame_map.Get().find(routing_id); |
| if (iter != g_routing_id_frame_map.Get().end()) |
| return iter->second; |
| return NULL; |
| } |
| |
| // static |
| RenderFrameImpl* RenderFrameImpl::CreateMainFrame(RenderViewImpl* render_view, |
| int32 routing_id) { |
| RenderFrameImpl* render_frame = |
| RenderFrameImpl::Create(render_view, routing_id); |
| WebLocalFrame* web_frame = |
| WebLocalFrame::create(blink::WebTreeScopeType::Document, render_frame); |
| render_frame->SetWebFrame(web_frame); |
| render_view->webview()->setMainFrame(web_frame); |
| return render_frame; |
| } |
| |
| // static |
| void RenderFrameImpl::CreateFrame( |
| int routing_id, |
| int proxy_routing_id, |
| int opener_routing_id, |
| int parent_routing_id, |
| int previous_sibling_routing_id, |
| const FrameReplicationState& replicated_state, |
| CompositorDependencies* compositor_deps, |
| const FrameMsg_NewFrame_WidgetParams& widget_params) { |
| blink::WebLocalFrame* web_frame; |
| RenderFrameImpl* render_frame; |
| if (proxy_routing_id == MSG_ROUTING_NONE) { |
| RenderFrameProxy* parent_proxy = |
| RenderFrameProxy::FromRoutingID(parent_routing_id); |
| // If the browser is sending a valid parent routing id, it should already |
| // be created and registered. |
| CHECK(parent_proxy); |
| blink::WebRemoteFrame* parent_web_frame = parent_proxy->web_frame(); |
| |
| blink::WebFrame* previous_sibling_web_frame = nullptr; |
| RenderFrameProxy* previous_sibling_proxy = |
| RenderFrameProxy::FromRoutingID(previous_sibling_routing_id); |
| if (previous_sibling_proxy) |
| previous_sibling_web_frame = previous_sibling_proxy->web_frame(); |
| |
| // Create the RenderFrame and WebLocalFrame, linking the two. |
| render_frame = |
| RenderFrameImpl::Create(parent_proxy->render_view(), routing_id); |
| web_frame = parent_web_frame->createLocalChild( |
| replicated_state.scope, WebString::fromUTF8(replicated_state.name), |
| replicated_state.sandbox_flags, render_frame, |
| previous_sibling_web_frame); |
| } else { |
| RenderFrameProxy* proxy = |
| RenderFrameProxy::FromRoutingID(proxy_routing_id); |
| CHECK(proxy); |
| render_frame = RenderFrameImpl::Create(proxy->render_view(), routing_id); |
| web_frame = |
| blink::WebLocalFrame::create(replicated_state.scope, render_frame); |
| render_frame->proxy_routing_id_ = proxy_routing_id; |
| web_frame->initializeToReplaceRemoteFrame( |
| proxy->web_frame(), WebString::fromUTF8(replicated_state.name), |
| replicated_state.sandbox_flags); |
| } |
| render_frame->SetWebFrame(web_frame); |
| CHECK_IMPLIES(parent_routing_id == MSG_ROUTING_NONE, !web_frame->parent()); |
| |
| WebFrame* opener = ResolveOpener(opener_routing_id, nullptr); |
| web_frame->setOpener(opener); |
| |
| if (widget_params.routing_id != MSG_ROUTING_NONE) { |
| CHECK(SiteIsolationPolicy::AreCrossProcessFramesPossible()); |
| render_frame->render_widget_ = RenderWidget::CreateForFrame( |
| widget_params.routing_id, widget_params.surface_id, |
| widget_params.hidden, render_frame->render_view_->screen_info(), |
| compositor_deps, web_frame); |
| // TODO(kenrb): Observing shouldn't be necessary when we sort out |
| // WasShown and WasHidden, separating page-level visibility from |
| // frame-level visibility. |
| render_frame->render_widget_->RegisterRenderFrame(render_frame); |
| } |
| |
| render_frame->Initialize(); |
| } |
| |
| // static |
| RenderFrame* RenderFrame::FromWebFrame(blink::WebFrame* web_frame) { |
| return RenderFrameImpl::FromWebFrame(web_frame); |
| } |
| |
| // static |
| RenderFrameImpl* RenderFrameImpl::FromWebFrame(blink::WebFrame* web_frame) { |
| FrameMap::iterator iter = g_frame_map.Get().find(web_frame); |
| if (iter != g_frame_map.Get().end()) |
| return iter->second; |
| return NULL; |
| } |
| |
| // static |
| void RenderFrameImpl::InstallCreateHook( |
| CreateRenderFrameImplFunction create_render_frame_impl) { |
| CHECK(!g_create_render_frame_impl); |
| g_create_render_frame_impl = create_render_frame_impl; |
| } |
| |
| // static |
| blink::WebFrame* RenderFrameImpl::ResolveOpener(int opener_frame_routing_id, |
| int* opener_view_routing_id) { |
| if (opener_view_routing_id) |
| *opener_view_routing_id = MSG_ROUTING_NONE; |
| |
| 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) { |
| if (opener_view_routing_id) |
| *opener_view_routing_id = opener_proxy->render_view()->GetRoutingID(); |
| |
| // TODO(nasko,alexmos): This check won't be needed once swappedout:// is |
| // gone. |
| if (opener_proxy->IsMainFrameDetachedFromTree()) { |
| DCHECK(!SiteIsolationPolicy::IsSwappedOutStateForbidden()); |
| return opener_proxy->render_view()->webview()->mainFrame(); |
| } else { |
| return opener_proxy->web_frame(); |
| } |
| } |
| |
| RenderFrameImpl* opener_frame = |
| RenderFrameImpl::FromRoutingID(opener_frame_routing_id); |
| if (opener_frame) { |
| if (opener_view_routing_id) |
| *opener_view_routing_id = opener_frame->render_view()->GetRoutingID(); |
| return opener_frame->GetWebFrame(); |
| } |
| |
| return nullptr; |
| } |
| |
| // RenderFrameImpl ---------------------------------------------------------- |
| RenderFrameImpl::RenderFrameImpl(const CreateParams& params) |
| : frame_(NULL), |
| is_subframe_(false), |
| is_local_root_(false), |
| render_view_(params.render_view->AsWeakPtr()), |
| routing_id_(params.routing_id), |
| is_swapped_out_(false), |
| render_frame_proxy_(NULL), |
| is_detaching_(false), |
| proxy_routing_id_(MSG_ROUTING_NONE), |
| #if defined(ENABLE_PLUGINS) |
| plugin_power_saver_helper_(NULL), |
| #endif |
| cookie_jar_(this), |
| selection_text_offset_(0), |
| selection_range_(gfx::Range::InvalidRange()), |
| handling_select_range_(false), |
| notification_permission_dispatcher_(NULL), |
| web_user_media_client_(NULL), |
| media_permission_dispatcher_(NULL), |
| midi_dispatcher_(NULL), |
| #if defined(OS_ANDROID) |
| media_player_manager_(NULL), |
| #endif |
| #if defined(ENABLE_BROWSER_CDMS) |
| cdm_manager_(NULL), |
| #endif |
| #if defined(VIDEO_HOLE) |
| contains_media_player_(false), |
| #endif |
| has_played_media_(false), |
| devtools_agent_(nullptr), |
| geolocation_dispatcher_(NULL), |
| push_messaging_dispatcher_(NULL), |
| presentation_dispatcher_(NULL), |
| screen_orientation_dispatcher_(NULL), |
| manifest_manager_(NULL), |
| accessibility_mode_(AccessibilityModeOff), |
| renderer_accessibility_(NULL), |
| weak_factory_(this) { |
| 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."; |
| |
| RenderThread::Get()->AddRoute(routing_id_, this); |
| |
| render_view_->RegisterRenderFrame(this); |
| |
| // Everything below subclasses RenderFrameObserver and is automatically |
| // deleted when the RenderFrame gets deleted. |
| #if defined(OS_ANDROID) |
| new GinJavaBridgeDispatcher(this); |
| #endif |
| |
| #if defined(ENABLE_PLUGINS) |
| plugin_power_saver_helper_ = new PluginPowerSaverHelper(this); |
| #endif |
| |
| manifest_manager_ = new ManifestManager(this); |
| |
| GetServiceRegistry()->ConnectToRemoteService(mojo::GetProxy(&mojo_shell_)); |
| } |
| |
| RenderFrameImpl::~RenderFrameImpl() { |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, RenderFrameGone()); |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, OnDestruct()); |
| |
| base::trace_event::TraceLog::GetInstance()->RemoveProcessLabel(routing_id_); |
| |
| #if defined(VIDEO_HOLE) |
| if (contains_media_player_) |
| render_view_->UnregisterVideoHoleFrame(this); |
| #endif |
| |
| if (!is_subframe_) { |
| // When using swapped out frames, RenderFrameProxy is owned by |
| // RenderFrameImpl in the case it is the main frame. Ensure it is deleted |
| // along with this object. |
| if (render_frame_proxy_ && |
| !SiteIsolationPolicy::IsSwappedOutStateForbidden()) { |
| // The following method calls back into this object and clears |
| // |render_frame_proxy_|. |
| render_frame_proxy_->frameDetached( |
| blink::WebRemoteFrameClient::DetachType::Remove); |
| } |
| |
| // 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; |
| } |
| |
| render_view_->UnregisterRenderFrame(this); |
| g_routing_id_frame_map.Get().erase(routing_id_); |
| RenderThread::Get()->RemoveRoute(routing_id_); |
| } |
| |
| void RenderFrameImpl::SetWebFrame(blink::WebLocalFrame* web_frame) { |
| DCHECK(!frame_); |
| |
| std::pair<FrameMap::iterator, bool> result = g_frame_map.Get().insert( |
| std::make_pair(web_frame, this)); |
| CHECK(result.second) << "Inserting a duplicate item."; |
| |
| frame_ = web_frame; |
| } |
| |
| void RenderFrameImpl::Initialize() { |
| is_subframe_ = !!frame_->parent(); |
| is_local_root_ = !frame_->parent() || frame_->parent()->isWebRemoteFrame(); |
| |
| #if defined(ENABLE_PLUGINS) |
| new PepperBrowserConnection(this); |
| #endif |
| new SharedWorkerRepository(this); |
| |
| if (is_local_root_ && !render_frame_proxy_) { |
| // DevToolsAgent is a RenderFrameObserver, and will destruct itself |
| // when |this| is deleted. |
| devtools_agent_ = new DevToolsAgent(this); |
| } |
| |
| RegisterMojoServices(); |
| |
| // 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); |
| } |
| |
| RenderWidget* RenderFrameImpl::GetRenderWidget() { |
| return render_view_.get(); |
| } |
| |
| #if defined(ENABLE_PLUGINS) |
| void RenderFrameImpl::PepperPluginCreated(RendererPpapiHost* host) { |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, |
| DidCreatePepperPlugin(host)); |
| if (host->GetPluginName() == kFlashPluginName) { |
| RenderThread::Get()->RecordAction( |
| base::UserMetricsAction("FrameLoadWithFlash")); |
| } |
| } |
| |
| 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 == render_view_->pepper_last_mouse_event_target()) |
| GetRenderWidget()->didChangeCursor(cursor); |
| } |
| |
| void RenderFrameImpl::PepperDidReceiveMouseEvent( |
| PepperPluginInstanceImpl* instance) { |
| render_view_->set_pepper_last_mouse_event_target(instance); |
| } |
| |
| void RenderFrameImpl::PepperTextInputTypeChanged( |
| PepperPluginInstanceImpl* instance) { |
| if (instance != render_view_->focused_pepper_plugin()) |
| return; |
| |
| GetRenderWidget()->UpdateTextInputState( |
| RenderWidget::NO_SHOW_IME, RenderWidget::FROM_NON_IME); |
| |
| FocusedNodeChangedForAccessibility(WebNode()); |
| } |
| |
| void RenderFrameImpl::PepperCaretPositionChanged( |
| PepperPluginInstanceImpl* instance) { |
| if (instance != render_view_->focused_pepper_plugin()) |
| return; |
| GetRenderWidget()->UpdateSelectionBounds(); |
| } |
| |
| void RenderFrameImpl::PepperCancelComposition( |
| PepperPluginInstanceImpl* instance) { |
| if (instance != render_view_->focused_pepper_plugin()) |
| return; |
| Send(new InputHostMsg_ImeCancelComposition(render_view_->GetRoutingID()));; |
| #if defined(OS_MACOSX) || defined(USE_AURA) |
| GetRenderWidget()->UpdateCompositionInfo(true); |
| #endif |
| } |
| |
| void RenderFrameImpl::PepperSelectionChanged( |
| PepperPluginInstanceImpl* instance) { |
| if (instance != render_view_->focused_pepper_plugin()) |
| return; |
| SyncSelectionIfRequired(); |
| } |
| |
| RenderWidgetFullscreenPepper* RenderFrameImpl::CreatePepperFullscreenContainer( |
| PepperPluginInstanceImpl* plugin) { |
| GURL active_url; |
| if (render_view_->webview() && render_view_->webview()->mainFrame()) |
| active_url = GURL(render_view_->webview()->mainFrame()->document().url()); |
| RenderWidgetFullscreenPepper* widget = RenderWidgetFullscreenPepper::Create( |
| GetRenderWidget()->routing_id(), GetRenderWidget()->compositor_deps(), |
| plugin, active_url, GetRenderWidget()->screenInfo()); |
| widget->show(blink::WebNavigationPolicyIgnore); |
| return widget; |
| } |
| |
| bool RenderFrameImpl::IsPepperAcceptingCompositionEvents() const { |
| if (!render_view_->focused_pepper_plugin()) |
| return false; |
| return render_view_->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::WebCompositionUnderline>& underlines, |
| int selection_start, |
| int selection_end) { |
| render_view_->OnImeSetComposition( |
| text, underlines, selection_start, selection_end); |
| } |
| |
| void RenderFrameImpl::SimulateImeConfirmComposition( |
| const base::string16& text, |
| const gfx::Range& replacement_range) { |
| render_view_->OnImeConfirmComposition(text, replacement_range, false); |
| } |
| |
| void RenderFrameImpl::OnImeSetComposition( |
| const base::string16& text, |
| const std::vector<blink::WebCompositionUnderline>& underlines, |
| 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()) { |
| render_view_->focused_pepper_plugin()->HandleCompositionStart( |
| base::string16()); |
| } |
| // Nonempty -> empty: composition canceled. |
| if (!pepper_composition_text_.empty() && text.empty()) { |
| render_view_->focused_pepper_plugin()->HandleCompositionEnd( |
| base::string16()); |
| } |
| pepper_composition_text_ = text; |
| // Nonempty: composition is ongoing. |
| if (!pepper_composition_text_.empty()) { |
| render_view_->focused_pepper_plugin()->HandleCompositionUpdate( |
| pepper_composition_text_, underlines, selection_start, |
| selection_end); |
| } |
| } |
| } |
| |
| void RenderFrameImpl::OnImeConfirmComposition( |
| const base::string16& text, |
| const gfx::Range& replacement_range, |
| bool keep_selection) { |
| // When a PPAPI plugin has focus, we bypass WebKit. |
| // Here, text.empty() has a special meaning. It means to commit the last |
| // update of composition text (see |
| // RenderWidgetHost::ImeConfirmComposition()). |
| const base::string16& last_text = text.empty() ? pepper_composition_text_ |
| : text; |
| |
| // last_text is empty only when both text and pepper_composition_text_ is. |
| // Ignore it. |
| if (last_text.empty()) |
| return; |
| |
| if (!IsPepperAcceptingCompositionEvents()) { |
| base::i18n::UTF16CharIterator iterator(&last_text); |
| int32 i = 0; |
| while (iterator.Advance()) { |
| blink::WebKeyboardEvent char_event; |
| char_event.type = blink::WebInputEvent::Char; |
| char_event.timeStampSeconds = base::Time::Now().ToDoubleT(); |
| char_event.modifiers = 0; |
| char_event.windowsKeyCode = last_text[i]; |
| char_event.nativeKeyCode = last_text[i]; |
| |
| const int32 char_start = i; |
| for (; i < iterator.array_pos(); ++i) { |
| char_event.text[i - char_start] = last_text[i]; |
| char_event.unmodifiedText[i - char_start] = last_text[i]; |
| } |
| |
| if (GetRenderWidget()->webwidget()) |
| GetRenderWidget()->webwidget()->handleInputEvent(char_event); |
| } |
| } else { |
| // Mimics the order of events sent by WebKit. |
| // See WebCore::Editor::setComposition() for the corresponding code. |
| render_view_->focused_pepper_plugin()->HandleCompositionEnd(last_text); |
| render_view_->focused_pepper_plugin()->HandleTextInput(last_text); |
| } |
| pepper_composition_text_.clear(); |
| } |
| #endif // defined(ENABLE_PLUGINS) |
| |
| MediaStreamDispatcher* RenderFrameImpl::GetMediaStreamDispatcher() { |
| if (!web_user_media_client_) |
| InitializeUserMediaClient(); |
| return web_user_media_client_ ? |
| web_user_media_client_->media_stream_dispatcher() : NULL; |
| } |
| |
| bool RenderFrameImpl::Send(IPC::Message* message) { |
| if (is_detaching_) { |
| delete message; |
| return false; |
| } |
| if (is_swapped_out_) { |
| if (!SwappedOutMessages::CanSendWhileSwappedOut(message)) { |
| delete message; |
| return false; |
| } |
| } |
| |
| return RenderThread::Get()->Send(message); |
| } |
| |
| #if defined(OS_MACOSX) || defined(OS_ANDROID) |
| 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) { |
| // We may get here while detaching, when the WebFrame has been deleted. Do |
| // not process any messages in this state. |
| if (!frame_) |
| return false; |
| |
| // TODO(kenrb): document() should not be null, but as a transitional step |
| // we have RenderFrameProxy 'wrapping' a RenderFrameImpl, passing messages |
| // to this method. This happens for a top-level remote frame, where a |
| // document-less RenderFrame is replaced by a RenderFrameProxy but kept |
| // around and is still able to receive messages. |
| if (!frame_->document().isNull()) |
| GetContentClient()->SetActiveURL(frame_->document().url()); |
| |
| base::ObserverListBase<RenderFrameObserver>::Iterator it(&observers_); |
| RenderFrameObserver* observer; |
| while ((observer = it.GetNext()) != NULL) { |
| if (observer->OnMessageReceived(msg)) |
| return true; |
| } |
| |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(RenderFrameImpl, msg) |
| IPC_MESSAGE_HANDLER(FrameMsg_Navigate, OnNavigate) |
| IPC_MESSAGE_HANDLER(FrameMsg_BeforeUnload, OnBeforeUnload) |
| IPC_MESSAGE_HANDLER(FrameMsg_SwapOut, OnSwapOut) |
| IPC_MESSAGE_HANDLER(FrameMsg_Stop, OnStop) |
| IPC_MESSAGE_HANDLER(FrameMsg_ContextMenuClosed, OnContextMenuClosed) |
| IPC_MESSAGE_HANDLER(FrameMsg_CustomContextMenuAction, |
| OnCustomContextMenuAction) |
| IPC_MESSAGE_HANDLER(InputMsg_Undo, OnUndo) |
| IPC_MESSAGE_HANDLER(InputMsg_Redo, OnRedo) |
| IPC_MESSAGE_HANDLER(InputMsg_Cut, OnCut) |
| IPC_MESSAGE_HANDLER(InputMsg_Copy, OnCopy) |
| IPC_MESSAGE_HANDLER(InputMsg_Paste, OnPaste) |
| IPC_MESSAGE_HANDLER(InputMsg_PasteAndMatchStyle, OnPasteAndMatchStyle) |
| IPC_MESSAGE_HANDLER(InputMsg_Delete, OnDelete) |
| IPC_MESSAGE_HANDLER(InputMsg_SelectAll, OnSelectAll) |
| IPC_MESSAGE_HANDLER(InputMsg_SelectRange, OnSelectRange) |
| IPC_MESSAGE_HANDLER(InputMsg_AdjustSelectionByCharacterOffset, |
| OnAdjustSelectionByCharacterOffset) |
| IPC_MESSAGE_HANDLER(InputMsg_Unselect, OnUnselect) |
| IPC_MESSAGE_HANDLER(InputMsg_MoveRangeSelectionExtent, |
| OnMoveRangeSelectionExtent) |
| IPC_MESSAGE_HANDLER(InputMsg_Replace, OnReplace) |
| IPC_MESSAGE_HANDLER(InputMsg_ReplaceMisspelling, OnReplaceMisspelling) |
| IPC_MESSAGE_HANDLER(InputMsg_ExtendSelectionAndDelete, |
| OnExtendSelectionAndDelete) |
| IPC_MESSAGE_HANDLER(InputMsg_SetCompositionFromExistingText, |
| OnSetCompositionFromExistingText) |
| IPC_MESSAGE_HANDLER(InputMsg_ExecuteNoValueEditCommand, |
| OnExecuteNoValueEditCommand) |
| IPC_MESSAGE_HANDLER(FrameMsg_CSSInsertRequest, OnCSSInsertRequest) |
| 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_SetEditableSelectionOffsets, |
| OnSetEditableSelectionOffsets) |
| IPC_MESSAGE_HANDLER(FrameMsg_Reload, OnReload) |
| 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_CommitNavigation, OnCommitNavigation) |
| IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateSandboxFlags, OnDidUpdateSandboxFlags) |
| IPC_MESSAGE_HANDLER(FrameMsg_SetTextTrackSettings, |
| OnTextTrackSettingsChanged) |
| IPC_MESSAGE_HANDLER(FrameMsg_PostMessageEvent, OnPostMessageEvent) |
| IPC_MESSAGE_HANDLER(FrameMsg_FailedNavigation, OnFailedNavigation) |
| #if defined(OS_ANDROID) |
| IPC_MESSAGE_HANDLER(FrameMsg_SelectPopupMenuItems, OnSelectPopupMenuItems) |
| #elif defined(OS_MACOSX) |
| IPC_MESSAGE_HANDLER(FrameMsg_SelectPopupMenuItem, OnSelectPopupMenuItem) |
| IPC_MESSAGE_HANDLER(InputMsg_CopyToFindPboard, OnCopyToFindPboard) |
| #endif |
| IPC_END_MESSAGE_MAP() |
| |
| return handled; |
| } |
| |
| void RenderFrameImpl::OnNavigate( |
| const CommonNavigationParams& common_params, |
| const StartNavigationParams& start_params, |
| const RequestNavigationParams& request_params) { |
| RenderThreadImpl* render_thread_impl = RenderThreadImpl::current(); |
| // Can be NULL in tests. |
| if (render_thread_impl) |
| render_thread_impl->GetRendererScheduler()->OnPageLoadStarted(); |
| DCHECK(!base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kEnableBrowserSideNavigation)); |
| TRACE_EVENT2("navigation", "RenderFrameImpl::OnNavigate", "id", routing_id_, |
| "url", common_params.url.possibly_invalid_spec()); |
| NavigateInternal(common_params, start_params, request_params, |
| scoped_ptr<StreamOverrideParameters>()); |
| } |
| |
| void RenderFrameImpl::NavigateToSwappedOutURL() { |
| // We use loadRequest instead of loadHTMLString because the former commits |
| // synchronously. Otherwise a new navigation can interrupt the navigation |
| // to kSwappedOutURL. If that happens to be to the page we had been |
| // showing, then WebKit will never send a commit and we'll be left spinning. |
| // Set the is_swapped_out_ bit to true, so IPC filtering is in effect and |
| // the navigation to swappedout:// is not announced to the browser side. |
| is_swapped_out_ = true; |
| GURL swappedOutURL(kSwappedOutURL); |
| WebURLRequest request(swappedOutURL); |
| frame_->loadRequest(request); |
| } |
| |
| void RenderFrameImpl::BindServiceRegistry( |
| mojo::InterfaceRequest<mojo::ServiceProvider> services, |
| mojo::ServiceProviderPtr exposed_services) { |
| service_registry_.Bind(services.Pass()); |
| service_registry_.BindRemoteServiceProvider(exposed_services.Pass()); |
| } |
| |
| ManifestManager* RenderFrameImpl::manifest_manager() { |
| return manifest_manager_; |
| } |
| |
| void RenderFrameImpl::SetPendingNavigationParams( |
| scoped_ptr<NavigationParams> navigation_params) { |
| pending_navigation_params_ = navigation_params.Pass(); |
| } |
| |
| void RenderFrameImpl::OnBeforeUnload() { |
| TRACE_EVENT1("navigation", "RenderFrameImpl::OnBeforeUnload", |
| "id", routing_id_); |
| // TODO(creis): Right now, this is only called on the main frame. Make the |
| // browser process send dispatchBeforeUnloadEvent to every frame that needs |
| // it. |
| CHECK(!frame_->parent()); |
| |
| base::TimeTicks before_unload_start_time = base::TimeTicks::Now(); |
| bool proceed = frame_->dispatchBeforeUnloadEvent(); |
| base::TimeTicks before_unload_end_time = base::TimeTicks::Now(); |
| Send(new FrameHostMsg_BeforeUnload_ACK(routing_id_, proceed, |
| before_unload_start_time, |
| before_unload_end_time)); |
| } |
| |
| void RenderFrameImpl::OnSwapOut( |
| int proxy_routing_id, |
| bool is_loading, |
| const FrameReplicationState& replicated_frame_state) { |
| TRACE_EVENT1("navigation", "RenderFrameImpl::OnSwapOut", "id", routing_id_); |
| RenderFrameProxy* proxy = NULL; |
| bool swapped_out_forbidden = |
| SiteIsolationPolicy::IsSwappedOutStateForbidden(); |
| bool is_main_frame = !frame_->parent(); |
| |
| // This codepath should only be hit for subframes when in --site-per-process. |
| CHECK_IMPLIES(!is_main_frame, |
| SiteIsolationPolicy::AreCrossProcessFramesPossible()); |
| |
| // Only run unload if we're not swapped out yet, but send the ack either way. |
| if (!is_swapped_out_) { |
| // Swap this RenderFrame out so the frame can navigate to a page rendered by |
| // a different process. This involves running the unload handler and |
| // clearing the page. We also allow this process to exit if there are no |
| // other active RenderFrames in it. |
| |
| // Send an UpdateState message before we get swapped out. |
| render_view_->SendUpdateState(); |
| |
| // If we need a proxy to replace this, create it now so its routing id is |
| // registered for receiving IPC messages. |
| if (proxy_routing_id != MSG_ROUTING_NONE) { |
| proxy = RenderFrameProxy::CreateProxyToReplaceFrame( |
| this, proxy_routing_id, replicated_frame_state.scope); |
| } |
| |
| // Synchronously run the unload handler before sending the ACK. |
| // TODO(creis): Call dispatchUnloadEvent unconditionally here to support |
| // unload on subframes as well. |
| if (is_main_frame) |
| frame_->dispatchUnloadEvent(); |
| |
| // Swap out and stop sending any IPC messages that are not ACKs. |
| if (is_main_frame) |
| render_view_->SetSwappedOut(true); |
| is_swapped_out_ = true; |
| |
| // Set the proxy here, since OnStop() below could cause an onload event |
| // handler to execute, which could trigger code such as |
| // willCheckAndDispatchMessageEvent() that needs the proxy. |
| if (proxy) |
| set_render_frame_proxy(proxy); |
| |
| // Now that we're swapped out and filtering IPC messages, stop loading to |
| // ensure that no other in-progress navigation continues. We do this here |
| // to avoid sending a DidStopLoading message to the browser process. |
| // TODO(creis): Should we be stopping all frames here and using |
| // StopAltErrorPageFetcher with RenderView::OnStop, or just stopping this |
| // frame? |
| if (!swapped_out_forbidden) |
| OnStop(); |
| |
| // Transfer settings such as initial drawing parameters to the remote frame, |
| // if one is created, that will replace this frame. |
| if (!is_main_frame && proxy) |
| proxy->web_frame()->initializeFromFrame(frame_); |
| |
| // Replace the page with a blank dummy URL. The unload handler will not be |
| // run a second time, thanks to a check in FrameLoader::stopLoading. |
| // TODO(creis): Need to add a better way to do this that avoids running the |
| // beforeunload handler. For now, we just run it a second time silently. |
| if (!swapped_out_forbidden) |
| NavigateToSwappedOutURL(); |
| |
| // Let WebKit know that this view is hidden so it can drop resources and |
| // stop compositing. |
| // TODO(creis): Support this for subframes as well. |
| if (is_main_frame) { |
| render_view_->webview()->setVisibilityState( |
| blink::WebPageVisibilityStateHidden, false); |
| } |
| } |
| |
| // It is now safe to show modal dialogs again. |
| // TODO(creis): Deal with modal dialogs from subframes. |
| if (is_main_frame) |
| render_view_->suppress_dialogs_until_swap_out_ = false; |
| |
| Send(new FrameHostMsg_SwapOut_ACK(routing_id_)); |
| |
| RenderViewImpl* render_view = render_view_.get(); |
| |
| // Now that all of the cleanup is complete and the browser side is notified, |
| // start using the RenderFrameProxy, if one is created. |
| if (proxy && swapped_out_forbidden) { |
| frame_->swap(proxy->web_frame()); |
| |
| if (is_loading) |
| proxy->OnDidStartLoading(); |
| } |
| |
| // In --site-per-process, initialize the WebRemoteFrame with the replication |
| // state passed by the process that is now rendering the frame. |
| // TODO(alexmos): We cannot yet do this for swapped-out main frames, because |
| // in that case we leave the LocalFrame as the main frame visible to Blink |
| // and don't call swap() above. Because swap() is what creates a RemoteFrame |
| // in proxy->web_frame(), the RemoteFrame will not exist for main frames. |
| // When we do an unconditional swap for all frames, we can remove |
| // !is_main_frame below. |
| if (proxy && swapped_out_forbidden) |
| proxy->SetReplicatedState(replicated_frame_state); |
| |
| // Safe to exit if no one else is using the process. |
| // TODO(nasko): Remove the dependency on RenderViewImpl here and ref count |
| // the process based on the lifetime of this RenderFrameImpl object. |
| if (is_main_frame) { |
| render_view->WasSwappedOut(); |
| } |
| } |
| |
| 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(context_menu_node_, custom_context.link_followed); |
| // Internal request, forward to WebKit. |
| context_menu_node_.reset(); |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| void RenderFrameImpl::OnUndo() { |
| frame_->executeCommand(WebString::fromUTF8("Undo"), GetFocusedElement()); |
| } |
| |
| void RenderFrameImpl::OnRedo() { |
| frame_->executeCommand(WebString::fromUTF8("Redo"), GetFocusedElement()); |
| } |
| |
| void RenderFrameImpl::OnCut() { |
| base::AutoReset<bool> handling_select_range(&handling_select_range_, true); |
| frame_->executeCommand(WebString::fromUTF8("Cut"), GetFocusedElement()); |
| } |
| |
| void RenderFrameImpl::OnCopy() { |
| base::AutoReset<bool> handling_select_range(&handling_select_range_, true); |
| WebNode current_node = context_menu_node_.isNull() ? |
| GetFocusedElement() : context_menu_node_; |
| frame_->executeCommand(WebString::fromUTF8("Copy"), current_node); |
| } |
| |
| void RenderFrameImpl::OnPaste() { |
| base::AutoReset<bool> handling_select_range(&handling_select_range_, true); |
| frame_->executeCommand(WebString::fromUTF8("Paste"), GetFocusedElement()); |
| } |
| |
| void RenderFrameImpl::OnPasteAndMatchStyle() { |
| base::AutoReset<bool> handling_select_range(&handling_select_range_, true); |
| frame_->executeCommand( |
| WebString::fromUTF8("PasteAndMatchStyle"), GetFocusedElement()); |
| } |
| |
| #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()) { |
| base::string16 selection = frame_->selectionAsText(); |
| RenderThread::Get()->Send( |
| new ClipboardHostMsg_FindPboardWriteStringAsync(selection)); |
| } |
| } |
| #endif |
| |
| void RenderFrameImpl::OnDelete() { |
| frame_->executeCommand(WebString::fromUTF8("Delete"), GetFocusedElement()); |
| } |
| |
| void RenderFrameImpl::OnSelectAll() { |
| base::AutoReset<bool> handling_select_range(&handling_select_range_, true); |
| frame_->executeCommand(WebString::fromUTF8("SelectAll"), GetFocusedElement()); |
| } |
| |
| void RenderFrameImpl::OnSelectRange(const gfx::Point& base, |
| const gfx::Point& extent) { |
| // This IPC is dispatched by RenderWidgetHost, so use its routing id. |
| Send(new InputHostMsg_SelectRange_ACK(GetRenderWidget()->routing_id())); |
| |
| base::AutoReset<bool> handling_select_range(&handling_select_range_, true); |
| frame_->selectRange(base, extent); |
| } |
| |
| void RenderFrameImpl::OnAdjustSelectionByCharacterOffset(int start_adjust, |
| int end_adjust) { |
| size_t start, length; |
| if (!GetRenderWidget()->webwidget()->caretOrSelectionRange( |
| &start, &length)) { |
| return; |
| } |
| |
| // Sanity checks to disallow empty and out of range selections. |
| if (start_adjust - end_adjust > static_cast<int>(length) |
| || static_cast<int>(start) + start_adjust < 0) { |
| return; |
| } |
| |
| // A negative adjust amount moves the selection towards the beginning of |
| // the document, a positive amount moves the selection towards the end of |
| // the document. |
| start += start_adjust; |
| length += end_adjust - start_adjust; |
| |
| frame_->selectRange(WebRange::fromDocumentRange(frame_, start, length)); |
| } |
| |
| void RenderFrameImpl::OnUnselect() { |
| base::AutoReset<bool> handling_select_range(&handling_select_range_, true); |
| frame_->executeCommand(WebString::fromUTF8("Unselect"), GetFocusedElement()); |
| } |
| |
| void RenderFrameImpl::OnMoveRangeSelectionExtent(const gfx::Point& point) { |
| // This IPC is dispatched by RenderWidgetHost, so use its routing id. |
| Send(new InputHostMsg_MoveRangeSelectionExtent_ACK( |
| GetRenderWidget()->routing_id())); |
| |
| base::AutoReset<bool> handling_select_range(&handling_select_range_, true); |
| frame_->moveRangeSelectionExtent(point); |
| } |
| |
| void RenderFrameImpl::OnReplace(const base::string16& text) { |
| if (!frame_->hasSelection()) |
| frame_->selectWordAroundCaret(); |
| |
| frame_->replaceSelection(text); |
| } |
| |
| void RenderFrameImpl::OnReplaceMisspelling(const base::string16& text) { |
| if (!frame_->hasSelection()) |
| return; |
| |
| frame_->replaceMisspelledRange(text); |
| } |
| |
| void RenderFrameImpl::OnCSSInsertRequest(const std::string& css) { |
| frame_->document().insertStyleSheet(WebString::fromUTF8(css)); |
| } |
| |
| 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(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). |
| scoped_ptr<blink::WebScopedUserGesture> gesture( |
| has_user_gesture ? new blink::WebScopedUserGesture : nullptr); |
| v8::HandleScope handle_scope(blink::mainThreadIsolate()); |
| v8::Local<v8::Value> result = |
| frame_->executeScriptAndReturnValue(WebScriptSource(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(jscript); |
| JavaScriptIsolatedWorldRequest* request = new JavaScriptIsolatedWorldRequest( |
| id, notify_result, routing_id_, weak_factory_.GetWeakPtr()); |
| frame_->requestExecuteScriptInIsolatedWorld(world_id, &script, 1, 0, false, |
| 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) { |
| scoped_ptr<base::Value> result_value( |
| converter.FromV8Value(value, context)); |
| list.Append(result_value ? result_value.Pass() |
| : base::Value::CreateNullValue()); |
| } |
| } else { |
| list.Set(0, base::Value::CreateNullValue()); |
| } |
| 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); |
| scoped_ptr<base::Value> result_value( |
| converter.FromV8Value(result, context)); |
| list.Set(0, result_value ? result_value.Pass() |
| : base::Value::CreateNullValue()); |
| } else { |
| list.Set(0, base::Value::CreateNullValue()); |
| } |
| Send(new FrameHostMsg_JavaScriptExecuteResponse(routing_id_, id, list)); |
| } |
| } |
| |
| void RenderFrameImpl::OnVisualStateRequest(uint64 id) { |
| GetRenderWidget()->QueueMessage( |
| new FrameHostMsg_VisualStateResponse(routing_id_, id), |
| MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE); |
| } |
| |
| void RenderFrameImpl::OnSetEditableSelectionOffsets(int start, int end) { |
| base::AutoReset<bool> handling_select_range(&handling_select_range_, true); |
| if (!GetRenderWidget()->ShouldHandleImeEvent()) |
| return; |
| ImeEventGuard guard(GetRenderWidget()); |
| frame_->setEditableSelectionOffsets(start, end); |
| } |
| |
| void RenderFrameImpl::OnSetCompositionFromExistingText( |
| int start, int end, |
| const std::vector<blink::WebCompositionUnderline>& underlines) { |
| if (!GetRenderWidget()->ShouldHandleImeEvent()) |
| return; |
| ImeEventGuard guard(GetRenderWidget()); |
| frame_->setCompositionFromExistingText(start, end, underlines); |
| } |
| |
| void RenderFrameImpl::OnExecuteNoValueEditCommand(const std::string& name) { |
| frame_->executeCommand(WebString::fromUTF8(name), GetFocusedElement()); |
| } |
| |
| void RenderFrameImpl::OnExtendSelectionAndDelete(int before, int after) { |
| if (!GetRenderWidget()->ShouldHandleImeEvent()) |
| return; |
| |
| DCHECK(!WebUserGestureIndicator::isProcessingUserGesture()); |
| |
| ImeEventGuard guard(GetRenderWidget()); |
| blink::WebScopedUserGesture gesture_indicator; |
| frame_->extendSelectionAndDelete(before, after); |
| } |
| |
| void RenderFrameImpl::OnSetAccessibilityMode(AccessibilityMode new_mode) { |
| if (accessibility_mode_ == new_mode) |
| return; |
| accessibility_mode_ = new_mode; |
| if (renderer_accessibility_) { |
| // Note: this isn't called automatically by the destructor because |
| // there'd be no point in calling it in frame teardown, only if there's |
| // an accessibility mode change but the frame is persisting. |
| renderer_accessibility_->DisableAccessibility(); |
| |
| delete renderer_accessibility_; |
| renderer_accessibility_ = NULL; |
| } |
| if (accessibility_mode_ == AccessibilityModeOff) |
| return; |
| |
| if (accessibility_mode_ & AccessibilityModeFlagFullTree) |
| renderer_accessibility_ = new RendererAccessibility(this); |
| } |
| |
| void RenderFrameImpl::OnSnapshotAccessibilityTree(int callback_id) { |
| ui::AXTreeUpdate<AXContentNodeData> response; |
| RendererAccessibility::SnapshotAccessibilityTree(this, &response); |
| Send(new AccessibilityHostMsg_SnapshotResponse( |
| routing_id_, callback_id, response)); |
| } |
| |
| void RenderFrameImpl::OnUpdateOpener(int opener_routing_id) { |
| WebFrame* opener = ResolveOpener(opener_routing_id, nullptr); |
| frame_->setOpener(opener); |
| } |
| |
| void RenderFrameImpl::OnDidUpdateSandboxFlags(blink::WebSandboxFlags flags) { |
| frame_->setFrameOwnerSandboxFlags(flags); |
| } |
| |
| void RenderFrameImpl::OnTextTrackSettingsChanged( |
| const FrameMsg_TextTrackSettings_Params& params) { |
| DCHECK(!frame_->parent()); |
| if (!render_view_->webview()) |
| return; |
| |
| if (params.text_tracks_enabled) { |
| render_view_->webview()->settings()->setTextTrackKindUserPreference( |
| WebSettings::TextTrackKindUserPreference::Captions); |
| } else { |
| render_view_->webview()->settings()->setTextTrackKindUserPreference( |
| WebSettings::TextTrackKindUserPreference::Default); |
| } |
| render_view_->webview()->settings()->setTextTrackBackgroundColor( |
| WebString::fromUTF8(params.text_track_background_color)); |
| render_view_->webview()->settings()->setTextTrackFontFamily( |
| WebString::fromUTF8(params.text_track_font_family)); |
| render_view_->webview()->settings()->setTextTrackFontStyle( |
| WebString::fromUTF8(params.text_track_font_style)); |
| render_view_->webview()->settings()->setTextTrackFontVariant( |
| WebString::fromUTF8(params.text_track_font_variant)); |
| render_view_->webview()->settings()->setTextTrackTextColor( |
| WebString::fromUTF8(params.text_track_text_color)); |
| render_view_->webview()->settings()->setTextTrackTextShadow( |
| WebString::fromUTF8(params.text_track_text_shadow)); |
| render_view_->webview()->settings()->setTextTrackTextSize( |
| WebString::fromUTF8(params.text_track_text_size)); |
| } |
| |
| void RenderFrameImpl::OnPostMessageEvent( |
| const FrameMsg_PostMessage_Params& params) { |
| // Find the source frame if it exists. |
| WebFrame* source_frame = NULL; |
| if (params.source_routing_id != MSG_ROUTING_NONE) { |
| RenderFrameProxy* source_proxy = |
| RenderFrameProxy::FromRoutingID(params.source_routing_id); |
| if (source_proxy) { |
| // Currently, navigating a top-level frame cross-process does not swap |
| // the WebLocalFrame for a WebRemoteFrame in the frame tree, and the |
| // WebRemoteFrame will not have an associated blink::Frame. If this is |
| // the case for |source_proxy|, use the corresponding (swapped-out) |
| // WebLocalFrame instead, so that event.source for this message can be |
| // set and used properly. |
| if (source_proxy->IsMainFrameDetachedFromTree()) |
| source_frame = source_proxy->render_view()->webview()->mainFrame(); |
| else |
| source_frame = source_proxy->web_frame(); |
| } |
| } |
| |
| // If the message contained MessagePorts, create the corresponding endpoints. |
| blink::WebMessagePortChannelArray channels = |
| WebMessagePortChannelImpl::CreatePorts( |
| params.message_ports, params.new_routing_ids, |
| base::ThreadTaskRunnerHandle::Get().get()); |
| |
| WebSerializedScriptValue serialized_script_value; |
| if (params.is_data_raw_string) { |
| v8::HandleScope handle_scope(blink::mainThreadIsolate()); |
| v8::Local<v8::Context> context = frame_->mainWorldScriptContext(); |
| v8::Context::Scope context_scope(context); |
| V8ValueConverterImpl converter; |
| converter.SetDateAllowed(true); |
| converter.SetRegExpAllowed(true); |
| scoped_ptr<base::Value> value(new base::StringValue(params.data)); |
| v8::Local<v8::Value> result_value = converter.ToV8Value(value.get(), |
| context); |
| serialized_script_value = WebSerializedScriptValue::serialize(result_value); |
| } else { |
| serialized_script_value = WebSerializedScriptValue::fromString(params.data); |
| } |
| |
| // Create an event with the message. The next-to-last parameter to |
| // initMessageEvent is the last event ID, which is not used with postMessage. |
| WebDOMEvent event = frame_->document().createEvent("MessageEvent"); |
| WebDOMMessageEvent msg_event = event.to<WebDOMMessageEvent>(); |
| msg_event.initMessageEvent("message", |
| // |canBubble| and |cancellable| are always false |
| false, false, |
| serialized_script_value, |
| params.source_origin, source_frame, |
| frame_->document(), "", channels); |
| |
| // We must pass in the target_origin to do the security check on this side, |
| // since it may have changed since the original postMessage call was made. |
| WebSecurityOrigin target_origin; |
| if (!params.target_origin.empty()) { |
| target_origin = |
| WebSecurityOrigin::createFromString(WebString(params.target_origin)); |
| } |
| frame_->dispatchMessageEventWithOriginCheck(target_origin, msg_event); |
| } |
| |
| #if defined(OS_ANDROID) |
| void RenderFrameImpl::OnSelectPopupMenuItems( |
| bool canceled, |
| const std::vector<int>& selected_indices) { |
| // It is possible to receive more than one of these calls if the user presses |
| // a select faster than it takes for the show-select-popup IPC message to make |
| // it to the browser UI thread. Ignore the extra-messages. |
| // TODO(jcivelli): http:/b/5793321 Implement a better fix, as detailed in bug. |
| if (!external_popup_menu_) |
| return; |
| |
| external_popup_menu_->DidSelectItems(canceled, selected_indices); |
| external_popup_menu_.reset(); |
| } |
| #endif |
| |
| #if defined(OS_MACOSX) |
| void RenderFrameImpl::OnSelectPopupMenuItem(int selected_index) { |
| if (external_popup_menu_ == NULL) |
| return; |
| external_popup_menu_->DidSelectItem(selected_index); |
| external_popup_menu_.reset(); |
| } |
| #endif |
| |
| void RenderFrameImpl::OnReload(bool ignore_cache) { |
| frame_->reload(ignore_cache); |
| } |
| |
| void RenderFrameImpl::OnTextSurroundingSelectionRequest(size_t max_length) { |
| blink::WebSurroundingText surroundingText; |
| surroundingText.initialize(frame_->selectionRange(), max_length); |
| |
| if (surroundingText.isNull()) { |
| // |surroundingText| might not be correctly initialized, for example if |
| // |frame_->selectionRange().isNull()|, in other words, if there was no |
| // selection. |
| Send(new FrameHostMsg_TextSurroundingSelectionResponse( |
| routing_id_, base::string16(), 0, 0)); |
| return; |
| } |
| |
| Send(new FrameHostMsg_TextSurroundingSelectionResponse( |
| routing_id_, |
| surroundingText.textContent(), |
| surroundingText.startOffsetInTextContent(), |
| surroundingText.endOffsetInTextContent())); |
| } |
| |
| bool RenderFrameImpl::RunJavaScriptMessage(JavaScriptMessageType type, |
| const base::string16& message, |
| const base::string16& default_value, |
| const GURL& frame_url, |
| base::string16* result) { |
| // Don't allow further dialogs if we are waiting to swap out, since the |
| // PageGroupLoadDeferrer in our stack prevents it. |
| if (render_view()->suppress_dialogs_until_swap_out_) |
| return false; |
| |
| bool success = false; |
| base::string16 result_temp; |
| if (!result) |
| result = &result_temp; |
| |
| render_view()->SendAndRunNestedMessageLoop( |
| new FrameHostMsg_RunJavaScriptMessage( |
| routing_id_, message, default_value, frame_url, type, &success, |
| result)); |
| return success; |
| } |
| |
| void RenderFrameImpl::LoadNavigationErrorPage( |
| const WebURLRequest& failed_request, |
| const WebURLError& error, |
| bool replace) { |
| std::string error_html; |
| GetContentClient()->renderer()->GetNavigationErrorStrings( |
| render_view(), frame_, failed_request, error, &error_html, NULL); |
| |
| frame_->loadHTMLString(error_html, |
| GURL(kUnreachableWebDataURL), |
| error.unreachableURL, |
| replace); |
| } |
| |
| void RenderFrameImpl::DidCommitCompositorFrame() { |
| if (BrowserPluginManager::Get()) |
| BrowserPluginManager::Get()->DidCommitCompositorFrame(GetRoutingID()); |
| FOR_EACH_OBSERVER( |
| RenderFrameObserver, observers_, DidCommitCompositorFrame()); |
| } |
| |
| RenderView* RenderFrameImpl::GetRenderView() { |
| return render_view_.get(); |
| } |
| |
| int RenderFrameImpl::GetRoutingID() { |
| return routing_id_; |
| } |
| |
| blink::WebLocalFrame* RenderFrameImpl::GetWebFrame() { |
| DCHECK(frame_); |
| return frame_; |
| } |
| |
| WebElement RenderFrameImpl::GetFocusedElement() const { |
| WebDocument doc = frame_->document(); |
| if (!doc.isNull()) |
| return doc.focusedElement(); |
| |
| return WebElement(); |
| } |
| |
| WebPreferences& RenderFrameImpl::GetWebkitPreferences() { |
| return render_view_->GetWebkitPreferences(); |
| } |
| |
| int RenderFrameImpl::ShowContextMenu(ContextMenuClient* client, |
| const ContextMenuParams& params) { |
| DCHECK(client); // A null client means "internal" when we issue callbacks. |
| ContextMenuParams our_params(params); |
| our_params.custom_context.request_id = pending_context_menus_.Add(client); |
| Send(new FrameHostMsg_ContextMenu(routing_id_, our_params)); |
| return our_params.custom_context.request_id; |
| } |
| |
| void RenderFrameImpl::CancelContextMenu(int request_id) { |
| DCHECK(pending_context_menus_.Lookup(request_id)); |
| pending_context_menus_.Remove(request_id); |
| } |
| |
| blink::WebNode RenderFrameImpl::GetContextMenuNode() const { |
| return context_menu_node_; |
| } |
| |
| blink::WebPlugin* RenderFrameImpl::CreatePlugin( |
| blink::WebFrame* frame, |
| const WebPluginInfo& info, |
| const blink::WebPluginParams& params, |
| scoped_ptr<content::PluginInstanceThrottler> throttler) { |
| DCHECK_EQ(frame_, frame); |
| #if defined(ENABLE_PLUGINS) |
| if (info.type == WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN) { |
| return BrowserPluginManager::Get()->CreateBrowserPlugin( |
| this, GetContentClient() |
| ->renderer() |
| ->CreateBrowserPluginDelegate(this, params.mimeType.utf8(), |
| GURL(params.url)) |
| ->GetWeakPtr()); |
| } |
| |
| bool pepper_plugin_was_registered = false; |
| scoped_refptr<PluginModule> pepper_module(PluginModule::Create( |
| this, info, &pepper_plugin_was_registered)); |
| if (pepper_plugin_was_registered) { |
| if (pepper_module.get()) { |
| return new PepperWebPluginImpl( |
| pepper_module.get(), params, this, |
| make_scoped_ptr( |
| static_cast<PluginInstanceThrottlerImpl*>(throttler.release()))); |
| } |
| } |
| #if defined(OS_CHROMEOS) |
| LOG(WARNING) << "Pepper module/plugin creation failed."; |
| #else |
| if (info.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) { |
| // TODO(jam): change to take RenderFrame. |
| return new WebPluginImpl(frame, params, info.path, render_view_, this); |
| } |
| #endif |
| #endif |
| return NULL; |
| } |
| |
| void RenderFrameImpl::LoadURLExternally(blink::WebLocalFrame* frame, |
| const blink::WebURLRequest& request, |
| blink::WebNavigationPolicy policy) { |
| DCHECK(!frame_ || frame_ == frame); |
| loadURLExternally(frame, request, policy, WebString()); |
| } |
| |
| void RenderFrameImpl::ExecuteJavaScript(const base::string16& javascript) { |
| OnJavaScriptExecuteRequest(javascript, 0, false); |
| } |
| |
| ServiceRegistry* RenderFrameImpl::GetServiceRegistry() { |
| return &service_registry_; |
| } |
| |
| #if defined(ENABLE_PLUGINS) |
| void RenderFrameImpl::RegisterPeripheralPlugin( |
| const GURL& content_origin, |
| const base::Closure& unthrottle_callback) { |
| return plugin_power_saver_helper_->RegisterPeripheralPlugin( |
| content_origin, unthrottle_callback); |
| } |
| #endif // defined(ENABLE_PLUGINS) |
| |
| bool RenderFrameImpl::IsFTPDirectoryListing() { |
| WebURLResponseExtraDataImpl* extra_data = |
| GetExtraDataFromResponse(frame_->dataSource()->response()); |
| return extra_data ? extra_data->is_ftp_directory_listing() : false; |
| } |
| |
| void RenderFrameImpl::AttachGuest(int element_instance_id) { |
| BrowserPluginManager::Get()->Attach(element_instance_id); |
| } |
| |
| void RenderFrameImpl::DetachGuest(int element_instance_id) { |
| BrowserPluginManager::Get()->Detach(element_instance_id); |
| } |
| |
| void RenderFrameImpl::SetSelectedText(const base::string16& selection_text, |
| size_t offset, |
| const gfx::Range& range) { |
| // Use the routing id of Render Widget Host. |
| Send(new ViewHostMsg_SelectionChanged(GetRenderWidget()->routing_id(), |
| selection_text, |
| offset, |
| range)); |
| } |
| |
| void RenderFrameImpl::EnsureMojoBuiltinsAreAvailable( |
| v8::Isolate* isolate, |
| v8::Local<v8::Context> context) { |
| gin::ModuleRegistry* registry = gin::ModuleRegistry::From(context); |
| if (registry->available_modules().count(mojo::js::Core::kModuleName)) |
| return; |
| |
| v8::HandleScope handle_scope(isolate); |
| registry->AddBuiltinModule( |
| isolate, mojo::js::Core::kModuleName, mojo::js::Core::GetModule(isolate)); |
| registry->AddBuiltinModule(isolate, |
| mojo::js::Support::kModuleName, |
| mojo::js::Support::GetModule(isolate)); |
| registry->AddBuiltinModule( |
| isolate, |
| ServiceRegistryJsWrapper::kModuleName, |
| ServiceRegistryJsWrapper::Create(isolate, &service_registry_).ToV8()); |
| } |
| |
| void RenderFrameImpl::AddMessageToConsole(ConsoleMessageLevel level, |
| const std::string& message) { |
| if (devtools_agent_) |
| devtools_agent_->AddMessageToConsole(level, message); |
| } |
| |
| // blink::WebFrameClient implementation ---------------------------------------- |
| |
| blink::WebPlugin* RenderFrameImpl::createPlugin( |
| blink::WebLocalFrame* frame, |
| const blink::WebPluginParams& params) { |
| DCHECK_EQ(frame_, frame); |
| blink::WebPlugin* plugin = NULL; |
| if (GetContentClient()->renderer()->OverrideCreatePlugin( |
| this, frame, params, &plugin)) { |
| return plugin; |
| } |
| |
| if (base::UTF16ToUTF8(base::StringPiece16(params.mimeType)) == |
| kBrowserPluginMimeType) { |
| return BrowserPluginManager::Get()->CreateBrowserPlugin( |
| this, GetContentClient() |
| ->renderer() |
| ->CreateBrowserPluginDelegate(this, kBrowserPluginMimeType, |
| GURL(params.url)) |
| ->GetWeakPtr()); |
| } |
| |
| #if defined(ENABLE_PLUGINS) |
| WebPluginInfo info; |
| std::string mime_type; |
| bool found = false; |
| WebString top_origin = frame->top()->securityOrigin().toString(); |
| Send(new FrameHostMsg_GetPluginInfo(routing_id_, params.url, GURL(top_origin), |
| params.mimeType.utf8(), &found, &info, |
| &mime_type)); |
| if (!found) |
| return NULL; |
| |
| WebPluginParams params_to_use = params; |
| params_to_use.mimeType = WebString::fromUTF8(mime_type); |
| return CreatePlugin(frame, info, params_to_use, nullptr /* throttler */); |
| #else |
| return NULL; |
| #endif // defined(ENABLE_PLUGINS) |
| } |
| |
| blink::WebMediaPlayer* RenderFrameImpl::createMediaPlayer( |
| blink::WebLocalFrame* frame, |
| const blink::WebURL& url, |
| WebMediaPlayerClient* client, |
| WebMediaPlayerEncryptedMediaClient* encrypted_client, |
| WebContentDecryptionModule* initial_cdm) { |
| #if defined(VIDEO_HOLE) |
| if (!contains_media_player_) { |
| render_view_->RegisterVideoHoleFrame(this); |
| contains_media_player_ = true; |
| } |
| #endif // defined(VIDEO_HOLE) |
| |
| blink::WebMediaStream web_stream( |
| blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url)); |
| if (!web_stream.isNull()) |
| return CreateWebMediaPlayerForMediaStream(client); |
| |
| RenderThreadImpl* render_thread = RenderThreadImpl::current(); |
| |
| #if defined(OS_ANDROID) && !defined(ENABLE_MEDIA_PIPELINE_ON_ANDROID) |
| scoped_refptr<media::AudioRendererSink> audio_renderer_sink; |
| media::WebMediaPlayerParams::Context3DCB context_3d_cb; |
| #else |
| scoped_refptr<media::AudioRendererSink> audio_renderer_sink = |
| render_thread->GetAudioRendererMixerManager()->CreateInput(routing_id_); |
| media::WebMediaPlayerParams::Context3DCB context_3d_cb = |
| base::Bind(&GetSharedMainThreadContext3D); |
| #endif // defined(OS_ANDROID) && !defined(ENABLE_MEDIA_PIPELINE_ON_ANDROID) |
| |
| scoped_refptr<media::MediaLog> media_log(new RenderMediaLog()); |
| media::WebMediaPlayerParams params( |
| base::Bind(&ContentRendererClient::DeferMediaLoad, |
| base::Unretained(GetContentClient()->renderer()), |
| static_cast<RenderFrame*>(this), has_played_media_), |
| audio_renderer_sink, media_log, render_thread->GetMediaThreadTaskRunner(), |
| render_thread->GetWorkerTaskRunner(), |
| render_thread->compositor_task_runner(), context_3d_cb, |
| GetMediaPermission(), initial_cdm); |
| |
| #if defined(OS_ANDROID) && !defined(ENABLE_MEDIA_PIPELINE_ON_ANDROID) |
| return CreateAndroidWebMediaPlayer(client, encrypted_client, params); |
| #else |
| #if defined(ENABLE_MOJO_MEDIA) |
| scoped_ptr<media::RendererFactory> media_renderer_factory( |
| new media::MojoRendererFactory(GetMediaServiceFactory())); |
| #else |
| scoped_ptr<media::RendererFactory> media_renderer_factory = |
| GetContentClient()->renderer()->CreateMediaRendererFactory( |
| this, render_thread->GetGpuFactories(), media_log); |
| |
| if (!media_renderer_factory.get()) { |
| media_renderer_factory.reset(new media::DefaultRendererFactory( |
| media_log, render_thread->GetGpuFactories(), |
| *render_thread->GetAudioHardwareConfig())); |
| } |
| #endif // defined(ENABLE_MOJO_MEDIA) |
| |
| return new media::WebMediaPlayerImpl( |
| frame, client, encrypted_client, weak_factory_.GetWeakPtr(), |
| media_renderer_factory.Pass(), GetCdmFactory(), params); |
| #endif // defined(OS_ANDROID) && !defined(ENABLE_MEDIA_PIPELINE_ON_ANDROID) |
| } |
| |
| blink::WebApplicationCacheHost* RenderFrameImpl::createApplicationCacheHost( |
| blink::WebLocalFrame* frame, |
| blink::WebApplicationCacheHostClient* client) { |
| if (!frame || !frame->view()) |
| return NULL; |
| DCHECK(!frame_ || frame_ == frame); |
| return new RendererWebApplicationCacheHostImpl( |
| RenderViewImpl::FromWebView(frame->view()), client, |
| RenderThreadImpl::current()->appcache_dispatcher()->backend_proxy()); |
| } |
| |
| blink::WebWorkerContentSettingsClientProxy* |
| RenderFrameImpl::createWorkerContentSettingsClientProxy( |
| blink::WebLocalFrame* frame) { |
| if (!frame || !frame->view()) |
| return NULL; |
| DCHECK(!frame_ || frame_ == frame); |
| return GetContentClient()->renderer()->CreateWorkerContentSettingsClientProxy( |
| this, frame); |
| } |
| |
| WebExternalPopupMenu* RenderFrameImpl::createExternalPopupMenu( |
| const WebPopupMenuInfo& popup_menu_info, |
| WebExternalPopupMenuClient* popup_menu_client) { |
| #if defined(OS_MACOSX) || defined(OS_ANDROID) |
| // An IPC message is sent to the browser to build and display the actual |
| // popup. The user could have time to click a different select by the time |
| // the popup is shown. In that case external_popup_menu_ is non NULL. |
| // By returning NULL in that case, we instruct Blink to cancel that new |
| // popup. So from the user perspective, only the first one will show, and |
| // will have to close the first one before another one can be shown. |
| if (external_popup_menu_) |
| return NULL; |
| external_popup_menu_.reset( |
| new ExternalPopupMenu(this, popup_menu_info, popup_menu_client)); |
| if (render_view_->screen_metrics_emulator_) { |
| render_view_->SetExternalPopupOriginAdjustmentsForEmulation( |
| external_popup_menu_.get(), |
| render_view_->screen_metrics_emulator_.get()); |
| } |
| return external_popup_menu_.get(); |
| #else |
| return NULL; |
| #endif |
| } |
| |
| blink::WebCookieJar* RenderFrameImpl::cookieJar(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| return &cookie_jar_; |
| } |
| |
| blink::WebServiceWorkerProvider* RenderFrameImpl::createServiceWorkerProvider( |
| blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| // At this point we should have non-null data source. |
| DCHECK(frame->dataSource()); |
| if (!ChildThreadImpl::current()) |
| return NULL; // May be null in some tests. |
| ServiceWorkerNetworkProvider* provider = |
| ServiceWorkerNetworkProvider::FromDocumentState( |
| DocumentState::FromDataSource(frame->dataSource())); |
| DCHECK(provider); |
| return new WebServiceWorkerProviderImpl( |
| ChildThreadImpl::current()->thread_safe_sender(), |
| provider->context()); |
| } |
| |
| void RenderFrameImpl::didAccessInitialDocument(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| // If the request hasn't yet committed, notify the browser process that it is |
| // no longer safe to show the pending URL of the main frame, since a URL spoof |
| // is now possible. (If the request has committed, the browser already knows.) |
| if (!frame->parent()) { |
| DocumentState* document_state = |
| DocumentState::FromDataSource(frame->dataSource()); |
| NavigationStateImpl* navigation_state = |
| static_cast<NavigationStateImpl*>(document_state->navigation_state()); |
| |
| if (!navigation_state->request_committed()) { |
| Send(new FrameHostMsg_DidAccessInitialDocument(routing_id_)); |
| } |
| } |
| } |
| |
| blink::WebFrame* RenderFrameImpl::createChildFrame( |
| blink::WebLocalFrame* parent, |
| blink::WebTreeScopeType scope, |
| const blink::WebString& name, |
| blink::WebSandboxFlags sandbox_flags) { |
| // Synchronously notify the browser of a child frame creation to get the |
| // routing_id for the RenderFrame. |
| int child_routing_id = MSG_ROUTING_NONE; |
| Send(new FrameHostMsg_CreateChildFrame( |
| routing_id_, scope, |
| base::UTF16ToUTF8(base::StringPiece16(name)), sandbox_flags, |
| &child_routing_id)); |
| |
| // Allocation of routing id failed, so we can't create a child frame. This can |
| // happen if this RenderFrameImpl's IPCs are being filtered when in swapped |
| // out state or synchronous IPC message above has failed. |
| if (child_routing_id == MSG_ROUTING_NONE) { |
| NOTREACHED() << "Failed to allocate routing id for child frame."; |
| return nullptr; |
| } |
| |
| // Create the RenderFrame and WebLocalFrame, linking the two. |
| RenderFrameImpl* child_render_frame = RenderFrameImpl::Create( |
| render_view_.get(), child_routing_id); |
| blink::WebLocalFrame* web_frame = |
| WebLocalFrame::create(scope, child_render_frame); |
| child_render_frame->SetWebFrame(web_frame); |
| |
| // Add the frame to the frame tree and initialize it. |
| parent->appendChild(web_frame); |
| child_render_frame->Initialize(); |
| |
| return web_frame; |
| } |
| |
| void RenderFrameImpl::didChangeOpener(blink::WebFrame* opener) { |
| // Only active frames are able to disown their opener. |
| if (!opener && is_swapped_out_) |
| return; |
| |
| // Only a local frame should be able to update another frame's opener. |
| DCHECK(!opener || opener->isWebLocalFrame()); |
| |
| int opener_routing_id = opener ? |
| RenderFrameImpl::FromWebFrame(opener->toWebLocalFrame())->GetRoutingID() : |
| MSG_ROUTING_NONE; |
| Send(new FrameHostMsg_DidChangeOpener(routing_id_, opener_routing_id)); |
| } |
| |
| void RenderFrameImpl::frameDetached(blink::WebFrame* frame, DetachType type) { |
| // NOTE: This function is called on the frame that is being detached and not |
| // the parent frame. This is different from createChildFrame() which is |
| // called on the parent frame. |
| CHECK(!is_detaching_); |
| DCHECK(!frame_ || frame_ == frame); |
| |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, FrameDetached()); |
| FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(), |
| FrameDetached(frame)); |
| |
| // We only notify the browser process when the frame is being detached for |
| // removal. If the frame is being detached for swap, we don't need to do this |
| // since we are not modifiying the frame tree. |
| if (type == DetachType::Remove) |
| Send(new FrameHostMsg_Detach(routing_id_)); |
| |
| // The |is_detaching_| flag disables Send(). FrameHostMsg_Detach must be |
| // sent before setting |is_detaching_| to true. |
| is_detaching_ = true; |
| |
| // Clean up the associated RenderWidget for the frame, if there is one. |
| if (render_widget_) { |
| render_widget_->UnregisterRenderFrame(this); |
| render_widget_->CloseForFrame(); |
| } |
| |
| // We need to clean up subframes by removing them from the map and deleting |
| // the RenderFrameImpl. In contrast, the main frame is owned by its |
| // containing RenderViewHost (so that they have the same lifetime), so only |
| // removal from the map is needed and no deletion. |
| FrameMap::iterator it = g_frame_map.Get().find(frame); |
| CHECK(it != g_frame_map.Get().end()); |
| CHECK_EQ(it->second, this); |
| g_frame_map.Get().erase(it); |
| |
| // Only remove the frame from the renderer's frame tree if the frame is |
| // being detached for removal. In the case of a swap, the frame needs to |
| // remain in the tree so WebFrame::swap() can replace it with the new frame. |
| if (is_subframe_ && type == DetachType::Remove) |
| frame->parent()->removeChild(frame); |
| |
| // |frame| is invalid after here. Be sure to clear frame_ as well, since this |
| // object may not be deleted immediately and other methods may try to access |
| // it. |
| frame->close(); |
| frame_ = nullptr; |
| |
| delete this; |
| // Object is invalid after this point. |
| } |
| |
| void RenderFrameImpl::frameFocused() { |
| Send(new FrameHostMsg_FrameFocused(routing_id_)); |
| } |
| |
| void RenderFrameImpl::willClose(blink::WebFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, FrameWillClose()); |
| FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(), |
| FrameWillClose(frame)); |
| } |
| |
| void RenderFrameImpl::didChangeName(blink::WebLocalFrame* frame, |
| const blink::WebString& name) { |
| DCHECK(!frame_ || frame_ == frame); |
| |
| // TODO(alexmos): According to https://crbug.com/169110, sending window.name |
| // updates may have performance implications for benchmarks like SunSpider. |
| // For now, send these updates only for --site-per-process, which needs to |
| // replicate frame names to frame proxies, and when |
| // |report_frame_name_changes| is set (used by <webview>). If needed, this |
| // can be optimized further by only sending the update if there are any |
| // remote frames in the frame tree, or delaying and batching up IPCs if |
| // updates are happening too frequently. |
| if (SiteIsolationPolicy::AreCrossProcessFramesPossible() || |
| render_view_->renderer_preferences_.report_frame_name_changes) { |
| Send(new FrameHostMsg_DidChangeName( |
| routing_id_, base::UTF16ToUTF8(base::StringPiece16(name)))); |
| } |
| } |
| |
| void RenderFrameImpl::didChangeSandboxFlags(blink::WebFrame* child_frame, |
| blink::WebSandboxFlags flags) { |
| int frame_routing_id = MSG_ROUTING_NONE; |
| if (child_frame->isWebRemoteFrame()) { |
| frame_routing_id = |
| RenderFrameProxy::FromWebFrame(child_frame)->routing_id(); |
| } else { |
| frame_routing_id = |
| RenderFrameImpl::FromWebFrame(child_frame)->GetRoutingID(); |
| } |
| |
| Send(new FrameHostMsg_DidChangeSandboxFlags(routing_id_, frame_routing_id, |
| flags)); |
| } |
| |
| void RenderFrameImpl::didMatchCSS( |
| blink::WebLocalFrame* frame, |
| const blink::WebVector<blink::WebString>& newly_matching_selectors, |
| const blink::WebVector<blink::WebString>& stopped_matching_selectors) { |
| DCHECK(!frame_ || frame_ == frame); |
| |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, |
| DidMatchCSS(newly_matching_selectors, |
| stopped_matching_selectors)); |
| } |
| |
| bool RenderFrameImpl::shouldReportDetailedMessageForSource( |
| const blink::WebString& source) { |
| return GetContentClient()->renderer()->ShouldReportDetailedMessageForSource( |
| source); |
| } |
| |
| void RenderFrameImpl::didAddMessageToConsole( |
| const blink::WebConsoleMessage& message, |
| const blink::WebString& source_name, |
| unsigned source_line, |
| const blink::WebString& stack_trace) { |
| logging::LogSeverity log_severity = logging::LOG_VERBOSE; |
| switch (message.level) { |
| case blink::WebConsoleMessage::LevelDebug: |
| log_severity = logging::LOG_VERBOSE; |
| break; |
| case blink::WebConsoleMessage::LevelLog: |
| case blink::WebConsoleMessage::LevelInfo: |
| log_severity = logging::LOG_INFO; |
| break; |
| case blink::WebConsoleMessage::LevelWarning: |
| log_severity = logging::LOG_WARNING; |
| break; |
| case blink::WebConsoleMessage::LevelError: |
| log_severity = logging::LOG_ERROR; |
| break; |
| default: |
| log_severity = logging::LOG_VERBOSE; |
| } |
| |
| if (shouldReportDetailedMessageForSource(source_name)) { |
| FOR_EACH_OBSERVER( |
| RenderFrameObserver, observers_, |
| DetailedConsoleMessageAdded(message.text, |
| source_name, |
| stack_trace, |
| source_line, |
| static_cast<int32>(log_severity))); |
| } |
| |
| Send(new FrameHostMsg_AddMessageToConsole(routing_id_, |
| static_cast<int32>(log_severity), |
| message.text, |
| static_cast<int32>(source_line), |
| source_name)); |
| } |
| |
| void RenderFrameImpl::loadURLExternally( |
| blink::WebLocalFrame* frame, |
| const blink::WebURLRequest& request, |
| blink::WebNavigationPolicy policy, |
| const blink::WebString& suggested_name) { |
| DCHECK(!frame_ || frame_ == frame); |
| Referrer referrer(RenderViewImpl::GetReferrerFromRequest(frame, request)); |
| if (policy == blink::WebNavigationPolicyDownload) { |
| render_view_->Send(new ViewHostMsg_DownloadUrl(render_view_->GetRoutingID(), |
| GetRoutingID(), |
| request.url(), referrer, |
| suggested_name)); |
| } else { |
| OpenURL(frame, request.url(), referrer, policy); |
| } |
| } |
| |
| blink::WebNavigationPolicy RenderFrameImpl::decidePolicyForNavigation( |
| const NavigationPolicyInfo& info) { |
| DCHECK(!frame_ || frame_ == info.frame); |
| return DecidePolicyForNavigation(this, info); |
| } |
| |
| blink::WebHistoryItem RenderFrameImpl::historyItemForNewChildFrame( |
| blink::WebFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| return render_view_->history_controller()->GetItemForNewChildFrame(this); |
| } |
| |
| void RenderFrameImpl::willSendSubmitEvent(blink::WebLocalFrame* frame, |
| const blink::WebFormElement& form) { |
| DCHECK(!frame_ || frame_ == frame); |
| |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, WillSendSubmitEvent(form)); |
| } |
| |
| void RenderFrameImpl::willSubmitForm(blink::WebLocalFrame* frame, |
| const blink::WebFormElement& form) { |
| DCHECK(!frame_ || frame_ == frame); |
| DocumentState* document_state = |
| DocumentState::FromDataSource(frame->provisionalDataSource()); |
| NavigationStateImpl* navigation_state = |
| static_cast<NavigationStateImpl*>(document_state->navigation_state()); |
| InternalDocumentStateData* internal_data = |
| InternalDocumentStateData::FromDocumentState(document_state); |
| |
| if (ui::PageTransitionCoreTypeIs(navigation_state->GetTransitionType(), |
| ui::PAGE_TRANSITION_LINK)) { |
| navigation_state->set_transition_type(ui::PAGE_TRANSITION_FORM_SUBMIT); |
| } |
| |
| // Save these to be processed when the ensuing navigation is committed. |
| WebSearchableFormData web_searchable_form_data(form); |
| internal_data->set_searchable_form_url(web_searchable_form_data.url()); |
| internal_data->set_searchable_form_encoding( |
| web_searchable_form_data.encoding().utf8()); |
| |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, WillSubmitForm(form)); |
| } |
| |
| void RenderFrameImpl::didCreateDataSource(blink::WebLocalFrame* frame, |
| blink::WebDataSource* datasource) { |
| DCHECK(!frame_ || frame_ == frame); |
| |
| bool content_initiated = !pending_navigation_params_.get(); |
| |
| // Make sure any previous redirect URLs end up in our new data source. |
| if (pending_navigation_params_.get()) { |
| for (const auto& i : |
| pending_navigation_params_->request_params.redirects) { |
| datasource->appendRedirect(i); |
| } |
| } |
| |
| DocumentState* document_state = DocumentState::FromDataSource(datasource); |
| if (!document_state) { |
| document_state = new DocumentState; |
| datasource->setExtraData(document_state); |
| if (!content_initiated) |
| PopulateDocumentStateFromPending(document_state); |
| } |
| |
| // Carry over the user agent override flag, if it exists. |
| blink::WebView* webview = render_view_->webview(); |
| if (content_initiated && webview && webview->mainFrame() && |
| webview->mainFrame()->isWebLocalFrame() && |
| webview->mainFrame()->dataSource()) { |
| DocumentState* old_document_state = |
| DocumentState::FromDataSource(webview->mainFrame()->dataSource()); |
| if (old_document_state) { |
| InternalDocumentStateData* internal_data = |
| InternalDocumentStateData::FromDocumentState(document_state); |
| InternalDocumentStateData* old_internal_data = |
| InternalDocumentStateData::FromDocumentState(old_document_state); |
| internal_data->set_is_overriding_user_agent( |
| old_internal_data->is_overriding_user_agent()); |
| } |
| } |
| |
| // The rest of RenderView assumes that a WebDataSource will always have a |
| // non-null NavigationState. |
| if (content_initiated) { |
| document_state->set_navigation_state( |
| NavigationStateImpl::CreateContentInitiated()); |
| } else { |
| document_state->set_navigation_state(CreateNavigationStateFromPending()); |
| pending_navigation_params_.reset(); |
| } |
| |
| // DocumentState::referred_by_prefetcher_ is true if we are |
| // navigating from a page that used prefetching using a link on that |
| // page. We are early enough in the request process here that we |
| // can still see the DocumentState of the previous page and set |
| // this value appropriately. |
| // TODO(gavinp): catch the important case of navigation in a new |
| // renderer process. |
| if (webview) { |
| if (WebFrame* old_frame = webview->mainFrame()) { |
| const WebURLRequest& original_request = datasource->originalRequest(); |
| const GURL referrer( |
| original_request.httpHeaderField(WebString::fromUTF8("Referer"))); |
| if (!referrer.is_empty() && old_frame->isWebLocalFrame() && |
| DocumentState::FromDataSource(old_frame->dataSource()) |
| ->was_prefetcher()) { |
| for (; old_frame; old_frame = old_frame->traverseNext(false)) { |
| WebDataSource* old_frame_datasource = old_frame->dataSource(); |
| if (old_frame_datasource && |
| referrer == GURL(old_frame_datasource->request().url())) { |
| document_state->set_was_referred_by_prefetcher(true); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| if (content_initiated) { |
| const WebURLRequest& request = datasource->request(); |
| switch (request.cachePolicy()) { |
| case WebURLRequest::UseProtocolCachePolicy: // normal load. |
| document_state->set_load_type(DocumentState::LINK_LOAD_NORMAL); |
| break; |
| case WebURLRequest::ReloadIgnoringCacheData: // reload. |
| case WebURLRequest::ReloadBypassingCache: // end-to-end reload. |
| document_state->set_load_type(DocumentState::LINK_LOAD_RELOAD); |
| break; |
| case WebURLRequest::ReturnCacheDataElseLoad: // allow stale data. |
| document_state->set_load_type(DocumentState::LINK_LOAD_CACHE_STALE_OK); |
| break; |
| case WebURLRequest::ReturnCacheDataDontLoad: // Don't re-post. |
| document_state->set_load_type(DocumentState::LINK_LOAD_CACHE_ONLY); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| // Create the serviceworker's per-document network observing object if it |
| // does not exist (When navigation happens within a page, the provider already |
| // exists). |
| if (!ServiceWorkerNetworkProvider::FromDocumentState( |
| DocumentState::FromDataSource(datasource))) { |
| ServiceWorkerProviderType provider_type = |
| SERVICE_WORKER_PROVIDER_FOR_WINDOW; |
| if ((frame->effectiveSandboxFlags() & blink::WebSandboxFlags::Origin) == |
| blink::WebSandboxFlags::Origin) { |
| provider_type = SERVICE_WORKER_PROVIDER_FOR_SANDBOXED_FRAME; |
| } |
| scoped_ptr<ServiceWorkerNetworkProvider> network_provider( |
| new ServiceWorkerNetworkProvider(routing_id_, provider_type)); |
| ServiceWorkerNetworkProvider::AttachToDocumentState( |
| DocumentState::FromDataSource(datasource), |
| network_provider.Pass()); |
| } |
| } |
| |
| void RenderFrameImpl::didStartProvisionalLoad(blink::WebLocalFrame* frame, |
| double triggering_event_time) { |
| DCHECK(!frame_ || frame_ == frame); |
| WebDataSource* ds = frame->provisionalDataSource(); |
| |
| // In fast/loader/stop-provisional-loads.html, we abort the load before this |
| // callback is invoked. |
| if (!ds) |
| return; |
| |
| TRACE_EVENT2("navigation", "RenderFrameImpl::didStartProvisionalLoad", |
| "id", routing_id_, "url", ds->request().url().string().utf8()); |
| DocumentState* document_state = DocumentState::FromDataSource(ds); |
| |
| // We should only navigate to swappedout:// when is_swapped_out_ is true. |
| CHECK_IMPLIES(ds->request().url() == GURL(kSwappedOutURL), is_swapped_out_) |
| << "Heard swappedout:// when not swapped out."; |
| |
| // Update the request time if WebKit has better knowledge of it. |
| if (document_state->request_time().is_null() && |
| triggering_event_time != 0.0) { |
| document_state->set_request_time(Time::FromDoubleT(triggering_event_time)); |
| } |
| |
| // Start time is only set after request time. |
| document_state->set_start_load_time(Time::Now()); |
| |
| bool is_top_most = !frame->parent(); |
| if (is_top_most) { |
| render_view_->set_navigation_gesture( |
| WebUserGestureIndicator::isProcessingUserGesture() ? |
| NavigationGestureUser : NavigationGestureAuto); |
| } else if (ds->replacesCurrentHistoryItem()) { |
| // 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. |
| static_cast<NavigationStateImpl*>(document_state->navigation_state()) |
| ->set_transition_type(ui::PAGE_TRANSITION_AUTO_SUBFRAME); |
| } |
| |
| FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(), |
| DidStartProvisionalLoad(frame)); |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidStartProvisionalLoad()); |
| |
| Send(new FrameHostMsg_DidStartProvisionalLoadForFrame( |
| routing_id_, ds->request().url())); |
| } |
| |
| void RenderFrameImpl::didReceiveServerRedirectForProvisionalLoad( |
| blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| render_view_->history_controller()->RemoveChildrenForRedirect(this); |
| } |
| |
| void RenderFrameImpl::didFailProvisionalLoad( |
| blink::WebLocalFrame* frame, |
| const blink::WebURLError& error, |
| blink::WebHistoryCommitType commit_type) { |
| TRACE_EVENT1("navigation", "RenderFrameImpl::didFailProvisionalLoad", |
| "id", routing_id_); |
| DCHECK(!frame_ || frame_ == frame); |
| WebDataSource* ds = frame->provisionalDataSource(); |
| DCHECK(ds); |
| |
| const WebURLRequest& failed_request = ds->request(); |
| |
| // Notify the browser that we failed a provisional load with an error. |
| // |
| // Note: It is important this notification occur before DidStopLoading so the |
| // SSL manager can react to the provisional load failure before being |
| // notified the load stopped. |
| // |
| FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(), |
| DidFailProvisionalLoad(frame, error)); |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, |
| DidFailProvisionalLoad(error)); |
| |
| SendFailedProvisionalLoad(failed_request, error, frame); |
| |
| if (!ShouldDisplayErrorPageForFailedLoad(error.reason, error.unreachableURL)) |
| return; |
| |
| // Make sure we never show errors in view source mode. |
| frame->enableViewSourceMode(false); |
| |
| DocumentState* document_state = DocumentState::FromDataSource(ds); |
| NavigationStateImpl* navigation_state = |
| static_cast<NavigationStateImpl*>(document_state->navigation_state()); |
| |
| // If this is a failed back/forward/reload navigation, then we need to do a |
| // 'replace' load. This is necessary to avoid messing up session history. |
| // Otherwise, we do a normal load, which simulates a 'go' navigation as far |
| // as session history is concerned. |
| bool replace = commit_type != blink::WebStandardCommit |