| // 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/kill.h" |
| #include "base/process/process.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "content/child/appcache/appcache_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_network_provider.h" |
| #include "content/child/service_worker/web_service_worker_provider_impl.h" |
| #include "content/child/web_socket_stream_handle_impl.h" |
| #include "content/child/webmessageportchannel_impl.h" |
| #include "content/child/websocket_bridge.h" |
| #include "content/common/clipboard_messages.h" |
| #include "content/common/frame_messages.h" |
| #include "content/common/input_messages.h" |
| #include "content/common/service_worker/service_worker_types.h" |
| #include "content/common/socket_stream_handle_data.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/url_constants.h" |
| #include "content/public/common/url_utils.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/renderer/accessibility/renderer_accessibility.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/geolocation_dispatcher.h" |
| #include "content/renderer/history_controller.h" |
| #include "content/renderer/history_serialization.h" |
| #include "content/renderer/image_loading_helper.h" |
| #include "content/renderer/ime_event_guard.h" |
| #include "content/renderer/internal_document_state_data.h" |
| #include "content/renderer/media/audio_renderer_mixer_manager.h" |
| #include "content/renderer/media/media_stream_dispatcher.h" |
| #include "content/renderer/media/media_stream_impl.h" |
| #include "content/renderer/media/media_stream_renderer_factory.h" |
| #include "content/renderer/media/midi_dispatcher.h" |
| #include "content/renderer/media/render_media_log.h" |
| #include "content/renderer/media/webcontentdecryptionmodule_impl.h" |
| #include "content/renderer/media/webmediaplayer_impl.h" |
| #include "content/renderer/media/webmediaplayer_ms.h" |
| #include "content/renderer/media/webmediaplayer_params.h" |
| #include "content/renderer/notification_provider.h" |
| #include "content/renderer/npapi/plugin_channel_host.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/v8_value_converter_impl.h" |
| #include "content/renderer/websharedworker_proxy.h" |
| #include "media/base/audio_renderer_mixer_input.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/web/WebColorSuggestion.h" |
| #include "third_party/WebKit/public/web/WebDocument.h" |
| #include "third_party/WebKit/public/web/WebGlyphCache.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/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/WebSurroundingText.h" |
| #include "third_party/WebKit/public/web/WebUserGestureIndicator.h" |
| #include "third_party/WebKit/public/web/WebView.h" |
| #include "webkit/child/weburlresponse_extradata_impl.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" |
| #endif |
| |
| #if defined(ENABLE_BROWSER_CDMS) |
| #include "content/renderer/media/crypto/renderer_cdm_manager.h" |
| #endif |
| |
| using blink::WebContextMenuData; |
| using blink::WebData; |
| using blink::WebDataSource; |
| using blink::WebDocument; |
| using blink::WebElement; |
| using blink::WebFrame; |
| using blink::WebHistoryItem; |
| using blink::WebHTTPBody; |
| using blink::WebLocalFrame; |
| using blink::WebMediaPlayer; |
| using blink::WebMediaPlayerClient; |
| using blink::WebNavigationPolicy; |
| using blink::WebNavigationType; |
| using blink::WebNode; |
| using blink::WebPluginParams; |
| using blink::WebRange; |
| using blink::WebReferrerPolicy; |
| using blink::WebScriptSource; |
| using blink::WebSearchableFormData; |
| using blink::WebSecurityOrigin; |
| using blink::WebSecurityPolicy; |
| using blink::WebServiceWorkerProvider; |
| 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; |
| using webkit_glue::WebURLResponseExtraDataImpl; |
| |
| 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(const WebHistoryItem& item) { |
| if (item.isNull()) |
| return -1; |
| |
| if (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() |
| static 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 static 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; |
| } |
| |
| #if defined(ADDRESS_SANITIZER) || defined(SYZYASAN) |
| NOINLINE static 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 |
| const int kArraySize = 5; |
| |
| if (!url.DomainIs(kCrashDomain, sizeof(kCrashDomain) - 1)) |
| 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 |
| |
| static void MaybeHandleDebugURL(const GURL& url) { |
| if (!url.SchemeIs(kChromeUIScheme)) |
| return; |
| 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::KillProcess(base::GetCurrentProcessHandle(), 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. |
| static bool IsTopLevelNavigation(WebFrame* frame) { |
| return frame->parent() == NULL; |
| } |
| |
| // Returns false unless this is a top-level navigation that crosses origins. |
| static 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; |
| } |
| |
| } // namespace |
| |
| static RenderFrameImpl* (*g_create_render_frame_impl)(RenderViewImpl*, int32) = |
| NULL; |
| |
| // static |
| RenderFrameImpl* RenderFrameImpl::Create(RenderViewImpl* render_view, |
| int32 routing_id) { |
| DCHECK(routing_id != MSG_ROUTING_NONE); |
| |
| if (g_create_render_frame_impl) |
| return g_create_render_frame_impl(render_view, routing_id); |
| else |
| return new RenderFrameImpl(render_view, routing_id); |
| } |
| |
| // static |
| RenderFrameImpl* RenderFrameImpl::FromRoutingID(int32 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 |
| RenderFrame* RenderFrame::FromWebFrame(blink::WebFrame* web_frame) { |
| return RenderFrameImpl::FromWebFrame(web_frame); |
| } |
| |
| 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( |
| RenderFrameImpl* (*create_render_frame_impl)(RenderViewImpl*, int32)) { |
| CHECK(!g_create_render_frame_impl); |
| g_create_render_frame_impl = create_render_frame_impl; |
| } |
| |
| // RenderFrameImpl ---------------------------------------------------------- |
| RenderFrameImpl::RenderFrameImpl(RenderViewImpl* render_view, int routing_id) |
| : frame_(NULL), |
| render_view_(render_view->AsWeakPtr()), |
| routing_id_(routing_id), |
| is_swapped_out_(false), |
| render_frame_proxy_(NULL), |
| is_detaching_(false), |
| cookie_jar_(this), |
| selection_text_offset_(0), |
| selection_range_(gfx::Range::InvalidRange()), |
| handling_select_range_(false), |
| notification_provider_(NULL), |
| web_user_media_client_(NULL), |
| midi_dispatcher_(NULL), |
| #if defined(OS_ANDROID) |
| media_player_manager_(NULL), |
| #endif |
| #if defined(ENABLE_BROWSER_CDMS) |
| cdm_manager_(NULL), |
| #endif |
| geolocation_dispatcher_(NULL), |
| screen_orientation_dispatcher_(NULL), |
| weak_factory_(this) { |
| RenderThread::Get()->AddRoute(routing_id_, 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."; |
| |
| render_view_->RegisterRenderFrame(this); |
| |
| #if defined(OS_ANDROID) |
| new GinJavaBridgeDispatcher(this); |
| #endif |
| |
| #if defined(ENABLE_NOTIFICATIONS) |
| notification_provider_ = new NotificationProvider(this); |
| #endif |
| } |
| |
| RenderFrameImpl::~RenderFrameImpl() { |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, RenderFrameGone()); |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, OnDestruct()); |
| |
| #if defined(OS_ANDROID) && defined(VIDEO_HOLE) |
| if (media_player_manager_) |
| render_view_->UnregisterVideoHoleFrame(this); |
| #endif |
| |
| 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() { |
| #if defined(ENABLE_PLUGINS) |
| new PepperBrowserConnection(this); |
| #endif |
| new SharedWorkerRepository(this); |
| |
| if (!frame_->parent()) |
| new ImageLoadingHelper(this); |
| |
| // 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)); |
| } |
| |
| 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); |
| if (render_view_->renderer_accessibility()) |
| render_view_->renderer_accessibility()->FocusedNodeChanged(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 ViewHostMsg_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(), 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 // ENABLE_PLUGINS |
| |
| bool RenderFrameImpl::Send(IPC::Message* message) { |
| if (is_detaching_) { |
| delete message; |
| return false; |
| } |
| if (is_swapped_out_ || render_view_->is_swapped_out()) { |
| if (!SwappedOutMessages::CanSendWhileSwappedOut(message)) { |
| delete message; |
| return false; |
| } |
| // In most cases, send IPCs through the proxy when swapped out. In some |
| // calls the associated RenderViewImpl routing id is used to send |
| // messages, so don't use the proxy. |
| if (render_frame_proxy_ && message->routing_id() == routing_id_) |
| return render_frame_proxy_->Send(message); |
| } |
| |
| return RenderThread::Get()->Send(message); |
| } |
| |
| bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) { |
| GetContentClient()->SetActiveURL(frame_->document().url()); |
| |
| 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_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_Unselect, OnUnselect) |
| IPC_MESSAGE_HANDLER(InputMsg_Replace, OnReplace) |
| IPC_MESSAGE_HANDLER(InputMsg_ReplaceMisspelling, OnReplaceMisspelling) |
| IPC_MESSAGE_HANDLER(FrameMsg_CSSInsertRequest, OnCSSInsertRequest) |
| IPC_MESSAGE_HANDLER(FrameMsg_JavaScriptExecuteRequest, |
| OnJavaScriptExecuteRequest) |
| IPC_MESSAGE_HANDLER(FrameMsg_SetEditableSelectionOffsets, |
| OnSetEditableSelectionOffsets) |
| IPC_MESSAGE_HANDLER(FrameMsg_SetCompositionFromExistingText, |
| OnSetCompositionFromExistingText) |
| IPC_MESSAGE_HANDLER(FrameMsg_ExtendSelectionAndDelete, |
| OnExtendSelectionAndDelete) |
| IPC_MESSAGE_HANDLER(FrameMsg_Reload, OnReload) |
| IPC_MESSAGE_HANDLER(FrameMsg_TextSurroundingSelectionRequest, |
| OnTextSurroundingSelectionRequest) |
| IPC_MESSAGE_HANDLER(FrameMsg_AddStyleSheetByURL, |
| OnAddStyleSheetByURL) |
| #if defined(OS_MACOSX) |
| IPC_MESSAGE_HANDLER(InputMsg_CopyToFindPboard, OnCopyToFindPboard) |
| #endif |
| IPC_END_MESSAGE_MAP() |
| |
| return handled; |
| } |
| |
| void RenderFrameImpl::OnNavigate(const FrameMsg_Navigate_Params& params) { |
| MaybeHandleDebugURL(params.url); |
| if (!render_view_->webview()) |
| return; |
| |
| FOR_EACH_OBSERVER( |
| RenderViewObserver, render_view_->observers_, Navigate(params.url)); |
| |
| bool is_reload = RenderViewImpl::IsReload(params); |
| WebURLRequest::CachePolicy cache_policy = |
| WebURLRequest::UseProtocolCachePolicy; |
| |
| // If this is a stale back/forward (due to a recent navigation the browser |
| // didn't know about), ignore it. |
| if (render_view_->IsBackForwardToStaleEntry(params, is_reload)) |
| return; |
| |
| // Swap this renderer back in if necessary. |
| if (render_view_->is_swapped_out_) { |
| // We marked the view as hidden when swapping the view out, so be sure to |
| // reset the visibility state before navigating to the new URL. |
| render_view_->webview()->setVisibilityState( |
| render_view_->visibilityState(), false); |
| |
| // If this is an attempt to reload while we are swapped out, we should not |
| // reload swappedout://, but the previous page, which is stored in |
| // params.state. Setting is_reload to false will treat this like a back |
| // navigation to accomplish that. |
| is_reload = false; |
| cache_policy = WebURLRequest::ReloadIgnoringCacheData; |
| |
| // We refresh timezone when a view is swapped in since timezone |
| // can get out of sync when the system timezone is updated while |
| // the view is swapped out. |
| RenderThreadImpl::NotifyTimezoneChange(); |
| |
| render_view_->SetSwappedOut(false); |
| is_swapped_out_ = false; |
| } |
| |
| if (params.should_clear_history_list) { |
| CHECK_EQ(params.pending_history_list_offset, -1); |
| CHECK_EQ(params.current_history_list_offset, -1); |
| CHECK_EQ(params.current_history_list_length, 0); |
| } |
| render_view_->history_list_offset_ = params.current_history_list_offset; |
| render_view_->history_list_length_ = params.current_history_list_length; |
| if (render_view_->history_list_length_ >= 0) { |
| render_view_->history_page_ids_.resize( |
| render_view_->history_list_length_, -1); |
| } |
| if (params.pending_history_list_offset >= 0 && |
| params.pending_history_list_offset < render_view_->history_list_length_) { |
| render_view_->history_page_ids_[params.pending_history_list_offset] = |
| params.page_id; |
| } |
| |
| GetContentClient()->SetActiveURL(params.url); |
| |
| WebFrame* frame = frame_; |
| if (!params.frame_to_navigate.empty()) { |
| // TODO(nasko): Move this lookup to the browser process. |
| frame = render_view_->webview()->findFrameByName( |
| WebString::fromUTF8(params.frame_to_navigate)); |
| CHECK(frame) << "Invalid frame name passed: " << params.frame_to_navigate; |
| } |
| |
| if (is_reload && !render_view_->history_controller()->GetCurrentEntry()) { |
| // We cannot reload if we do not have any history state. This happens, for |
| // example, when recovering from a crash. |
| is_reload = false; |
| cache_policy = WebURLRequest::ReloadIgnoringCacheData; |
| } |
| |
| render_view_->pending_navigation_params_.reset( |
| new FrameMsg_Navigate_Params(params)); |
| |
| // If we are reloading, then WebKit will use the history state of the current |
| // page, so we should just ignore any given history state. Otherwise, if we |
| // have history state, then we need to navigate to it, which corresponds to a |
| // back/forward navigation event. |
| if (is_reload) { |
| bool reload_original_url = |
| (params.navigation_type == |
| FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL); |
| bool ignore_cache = (params.navigation_type == |
| FrameMsg_Navigate_Type::RELOAD_IGNORING_CACHE); |
| |
| if (reload_original_url) |
| frame->reloadWithOverrideURL(params.url, true); |
| else |
| frame->reload(ignore_cache); |
| } else if (params.page_state.IsValid()) { |
| // We must know the page ID of the page we are navigating back to. |
| DCHECK_NE(params.page_id, -1); |
| scoped_ptr<HistoryEntry> entry = |
| PageStateToHistoryEntry(params.page_state); |
| if (entry) { |
| // Ensure we didn't save the swapped out URL in UpdateState, since the |
| // browser should never be telling us to navigate to swappedout://. |
| CHECK(entry->root().urlString() != WebString::fromUTF8(kSwappedOutURL)); |
| render_view_->history_controller()->GoToEntry(entry.Pass(), cache_policy); |
| } |
| } else if (!params.base_url_for_data_url.is_empty()) { |
| // A loadData request with a specified base URL. |
| std::string mime_type, charset, data; |
| if (net::DataURL::Parse(params.url, &mime_type, &charset, &data)) { |
| frame->loadData( |
| WebData(data.c_str(), data.length()), |
| WebString::fromUTF8(mime_type), |
| WebString::fromUTF8(charset), |
| params.base_url_for_data_url, |
| params.history_url_for_data_url, |
| false); |
| } else { |
| CHECK(false) << |
| "Invalid URL passed: " << params.url.possibly_invalid_spec(); |
| } |
| } else { |
| // Navigate to the given URL. |
| WebURLRequest request(params.url); |
| |
| // A session history navigation should have been accompanied by state. |
| CHECK_EQ(params.page_id, -1); |
| |
| if (frame->isViewSourceModeEnabled()) |
| request.setCachePolicy(WebURLRequest::ReturnCacheDataElseLoad); |
| |
| if (params.referrer.url.is_valid()) { |
| WebString referrer = WebSecurityPolicy::generateReferrerHeader( |
| params.referrer.policy, |
| params.url, |
| WebString::fromUTF8(params.referrer.url.spec())); |
| if (!referrer.isEmpty()) |
| request.setHTTPReferrer(referrer, params.referrer.policy); |
| } |
| |
| if (!params.extra_headers.empty()) { |
| for (net::HttpUtil::HeadersIterator i(params.extra_headers.begin(), |
| params.extra_headers.end(), "\n"); |
| i.GetNext(); ) { |
| request.addHTTPHeaderField(WebString::fromUTF8(i.name()), |
| WebString::fromUTF8(i.values())); |
| } |
| } |
| |
| if (params.is_post) { |
| request.setHTTPMethod(WebString::fromUTF8("POST")); |
| |
| // Set post data. |
| WebHTTPBody http_body; |
| http_body.initialize(); |
| const char* data = NULL; |
| if (params.browser_initiated_post_data.size()) { |
| data = reinterpret_cast<const char*>( |
| ¶ms.browser_initiated_post_data.front()); |
| } |
| http_body.appendData( |
| WebData(data, params.browser_initiated_post_data.size())); |
| request.setHTTPBody(http_body); |
| } |
| |
| frame->loadRequest(request); |
| |
| // If this is a cross-process navigation, the browser process will send |
| // along the proper navigation start value. |
| if (!params.browser_navigation_start.is_null() && |
| frame->provisionalDataSource()) { |
| // browser_navigation_start is likely before this process existed, so we |
| // can't use InterProcessTimeTicksConverter. Instead, the best we can do |
| // is just ensure we don't report a bogus value in the future. |
| base::TimeTicks navigation_start = std::min( |
| base::TimeTicks::Now(), params.browser_navigation_start); |
| double navigation_start_seconds = |
| (navigation_start - base::TimeTicks()).InSecondsF(); |
| frame->provisionalDataSource()->setNavigationStartTime( |
| navigation_start_seconds); |
| } |
| } |
| |
| // In case LoadRequest failed before DidCreateDataSource was called. |
| render_view_->pending_navigation_params_.reset(); |
| } |
| |
| void RenderFrameImpl::OnBeforeUnload() { |
| // 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) { |
| RenderFrameProxy* proxy = NULL; |
| |
| // Only run unload if we're not swapped out yet, but send the ack either way. |
| if (!is_swapped_out_ || !render_view_->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. Once WasSwappedOut is called, 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. Create the |
| // RenderFrameProxy as well so its routing id is registered for receiving |
| // IPC messages. |
| render_view_->SyncNavigationState(); |
| proxy = RenderFrameProxy::CreateFrameProxy(proxy_routing_id, routing_id_); |
| |
| // Synchronously run the unload handler before sending the ACK. |
| // TODO(creis): Call dispatchUnloadEvent unconditionally here to support |
| // unload on subframes as well. |
| if (!frame_->parent()) |
| frame_->dispatchUnloadEvent(); |
| |
| // Swap out and stop sending any IPC messages that are not ACKs. |
| if (!frame_->parent()) |
| render_view_->SetSwappedOut(true); |
| is_swapped_out_ = true; |
| |
| // 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 (!frame_->parent()) |
| render_view_->OnStop(); |
| else |
| frame_->stopLoading(); |
| |
| // Let subframes know that the frame is now rendered remotely, for the |
| // purposes of compositing and input events. |
| if (frame_->parent()) |
| frame_->setIsRemote(true); |
| |
| // 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. |
| render_view_->NavigateToSwappedOutURL(frame_); |
| |
| // 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 (!frame_->parent()) { |
| 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 (!frame_->parent()) |
| render_view_->suppress_dialogs_until_swap_out_ = false; |
| |
| Send(new FrameHostMsg_SwapOut_ACK(routing_id_)); |
| |
| // Now that all of the cleanup is complete and the browser side is notified, |
| // start using the RenderFrameProxy, if one is created. |
| if (proxy) |
| set_render_frame_proxy(proxy); |
| } |
| |
| 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 { |
| // 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& start, |
| const gfx::Point& end) { |
| // This IPC is dispatched by RenderWidgetHost, so use its routing id. |
| Send(new ViewHostMsg_SelectRange_ACK(GetRenderWidget()->routing_id())); |
| |
| base::AutoReset<bool> handling_select_range(&handling_select_range_, true); |
| frame_->selectRange(start, end); |
| } |
| |
| void RenderFrameImpl::OnUnselect() { |
| base::AutoReset<bool> handling_select_range(&handling_select_range_, true); |
| frame_->executeCommand(WebString::fromUTF8("Unselect"), GetFocusedElement()); |
| } |
| |
| 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::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::Handle<v8::Value> result = |
| frame_->executeScriptAndReturnValue(WebScriptSource(jscript)); |
| 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); |
| base::Value* result_value = converter.FromV8Value(result, context); |
| list.Set(0, result_value ? result_value : base::Value::CreateNullValue()); |
| } else { |
| list.Set(0, base::Value::CreateNullValue()); |
| } |
| Send(new FrameHostMsg_JavaScriptExecuteResponse(routing_id_, id, list)); |
| } |
| } |
| |
| 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::OnExtendSelectionAndDelete(int before, int after) { |
| if (!GetRenderWidget()->ShouldHandleImeEvent()) |
| return; |
| ImeEventGuard guard(GetRenderWidget()); |
| frame_->extendSelectionAndDelete(before, after); |
| } |
| |
| 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())); |
| } |
| |
| void RenderFrameImpl::OnAddStyleSheetByURL(const std::string& url) { |
| frame_->addStyleSheetByURL(WebString::fromUTF8(url)); |
| } |
| |
| bool RenderFrameImpl::ShouldUpdateSelectionTextFromContextMenuParams( |
| const base::string16& selection_text, |
| size_t selection_text_offset, |
| const gfx::Range& selection_range, |
| const ContextMenuParams& params) { |
| base::string16 trimmed_selection_text; |
| if (!selection_text.empty() && !selection_range.is_empty()) { |
| const int start = selection_range.GetMin() - selection_text_offset; |
| const size_t length = selection_range.length(); |
| if (start >= 0 && start + length <= selection_text.length()) { |
| base::TrimWhitespace(selection_text.substr(start, length), base::TRIM_ALL, |
| &trimmed_selection_text); |
| } |
| } |
| base::string16 trimmed_params_text; |
| base::TrimWhitespace(params.selection_text, base::TRIM_ALL, |
| &trimmed_params_text); |
| return trimmed_params_text != trimmed_selection_text; |
| } |
| |
| 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() { |
| FOR_EACH_OBSERVER( |
| RenderFrameObserver, observers_, DidCommitCompositorFrame()); |
| } |
| |
| RenderView* RenderFrameImpl::GetRenderView() { |
| return render_view_.get(); |
| } |
| |
| int RenderFrameImpl::GetRoutingID() { |
| return routing_id_; |
| } |
| |
| blink::WebFrame* RenderFrameImpl::GetWebFrame() { |
| DCHECK(frame_); |
| return frame_; |
| } |
| |
| 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) { |
| DCHECK_EQ(frame_, frame); |
| #if defined(ENABLE_PLUGINS) |
| 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); |
| } |
| } |
| #if defined(OS_CHROMEOS) |
| LOG(WARNING) << "Pepper module/plugin creation failed."; |
| return NULL; |
| #else |
| // TODO(jam): change to take RenderFrame. |
| return new WebPluginImpl(frame, params, info.path, render_view_, this); |
| #endif |
| #else |
| return NULL; |
| #endif |
| } |
| |
| 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); |
| } |
| |
| // 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::UTF16ToASCII(params.mimeType) == kBrowserPluginMimeType) { |
| return render_view_->GetBrowserPluginManager()->CreateBrowserPlugin( |
| render_view_.get(), frame, false); |
| } |
| |
| #if defined(ENABLE_PLUGINS) |
| WebPluginInfo info; |
| std::string mime_type; |
| bool found = false; |
| Send(new FrameHostMsg_GetPluginInfo( |
| routing_id_, params.url, frame->top()->document().url(), |
| params.mimeType.utf8(), &found, &info, &mime_type)); |
| if (!found) |
| return NULL; |
| |
| if (info.type == content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN) { |
| return render_view_->GetBrowserPluginManager()->CreateBrowserPlugin( |
| render_view_.get(), frame, true); |
| } |
| |
| |
| WebPluginParams params_to_use = params; |
| params_to_use.mimeType = WebString::fromUTF8(mime_type); |
| return CreatePlugin(frame, info, params_to_use); |
| #else |
| return NULL; |
| #endif // defined(ENABLE_PLUGINS) |
| } |
| |
| blink::WebMediaPlayer* RenderFrameImpl::createMediaPlayer( |
| blink::WebLocalFrame* frame, |
| const blink::WebURL& url, |
| blink::WebMediaPlayerClient* client) { |
| blink::WebMediaStream web_stream( |
| blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url)); |
| if (!web_stream.isNull()) |
| return CreateWebMediaPlayerForMediaStream(url, client); |
| |
| #if defined(OS_ANDROID) |
| return CreateAndroidWebMediaPlayer(url, client); |
| #else |
| WebMediaPlayerParams params( |
| base::Bind(&ContentRendererClient::DeferMediaLoad, |
| base::Unretained(GetContentClient()->renderer()), |
| static_cast<RenderFrame*>(this)), |
| RenderThreadImpl::current()->GetAudioRendererMixerManager()->CreateInput( |
| render_view_->routing_id_, routing_id_)); |
| return new WebMediaPlayerImpl(frame, client, weak_factory_.GetWeakPtr(), |
| params); |
| #endif // defined(OS_ANDROID) |
| } |
| |
| blink::WebContentDecryptionModule* |
| RenderFrameImpl::createContentDecryptionModule( |
| blink::WebLocalFrame* frame, |
| const blink::WebSecurityOrigin& security_origin, |
| const blink::WebString& key_system) { |
| DCHECK(!frame_ || frame_ == frame); |
| return WebContentDecryptionModuleImpl::Create( |
| #if defined(ENABLE_PEPPER_CDMS) |
| frame, |
| #elif defined(ENABLE_BROWSER_CDMS) |
| GetCdmManager(), |
| #endif |
| security_origin, |
| key_system); |
| } |
| |
| 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::WebWorkerPermissionClientProxy* |
| RenderFrameImpl::createWorkerPermissionClientProxy( |
| blink::WebLocalFrame* frame) { |
| if (!frame || !frame->view()) |
| return NULL; |
| DCHECK(!frame_ || frame_ == frame); |
| return GetContentClient()->renderer()->CreateWorkerPermissionClientProxy( |
| this, frame); |
| } |
| |
| 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 (!ChildThread::current()) |
| return NULL; // May be null in some tests. |
| ServiceWorkerNetworkProvider* provider = |
| ServiceWorkerNetworkProvider::FromDocumentState( |
| DocumentState::FromDataSource(frame->dataSource())); |
| return new WebServiceWorkerProviderImpl( |
| ChildThread::current()->thread_safe_sender(), |
| provider ? provider->context() : NULL); |
| } |
| |
| void RenderFrameImpl::didAccessInitialDocument(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| // 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 (!frame->parent() && render_view_->page_id_ == -1) |
| Send(new FrameHostMsg_DidAccessInitialDocument(routing_id_)); |
| } |
| |
| blink::WebFrame* RenderFrameImpl::createChildFrame( |
| blink::WebLocalFrame* parent, |
| const blink::WebString& name) { |
| // 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_, |
| base::UTF16ToUTF8(name), |
| &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. |
| if (child_routing_id == MSG_ROUTING_NONE) { |
| #if !defined(OS_LINUX) |
| // DumpWithoutCrashing() crashes on Linux in renderer processes when |
| // breakpad and sandboxing are enabled: crbug.com/349600 |
| base::debug::Alias(parent); |
| base::debug::Alias(&routing_id_); |
| bool render_view_is_swapped_out = GetRenderWidget()->is_swapped_out(); |
| base::debug::Alias(&render_view_is_swapped_out); |
| bool render_view_is_closing = GetRenderWidget()->closing(); |
| base::debug::Alias(&render_view_is_closing); |
| base::debug::Alias(&is_swapped_out_); |
| base::debug::DumpWithoutCrashing(); |
| #endif |
| return NULL; |
| } |
| |
| // 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(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::didDisownOpener(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| // We only need to notify the browser if the active, top-level frame clears |
| // its opener. We can ignore cases where a swapped out frame clears its |
| // opener after hearing about it from the browser, and the browser does not |
| // (yet) care about subframe openers. |
| if (render_view_->is_swapped_out_ || frame->parent()) |
| return; |
| |
| // Notify WebContents and all its swapped out RenderViews. |
| Send(new FrameHostMsg_DidDisownOpener(routing_id_)); |
| } |
| |
| void RenderFrameImpl::frameDetached(blink::WebFrame* frame) { |
| // 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); |
| |
| bool is_subframe = !!frame->parent(); |
| |
| Send(new FrameHostMsg_Detach(routing_id_)); |
| |
| // The |is_detaching_| flag disables Send(). FrameHostMsg_Detach must be |
| // sent before setting |is_detaching_| to true. In contrast, Observers |
| // should only be notified afterwards so they cannot call back into here and |
| // have IPCs fired off. |
| is_detaching_ = true; |
| |
| FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(), |
| FrameDetached(frame)); |
| |
| // 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); |
| |
| if (is_subframe) |
| frame->parent()->removeChild(frame); |
| |
| // |frame| is invalid after here. |
| frame->close(); |
| |
| if (is_subframe) { |
| 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(RenderViewObserver, render_view_->observers(), |
| FrameWillClose(frame)); |
| } |
| |
| void RenderFrameImpl::didChangeName(blink::WebLocalFrame* frame, |
| const blink::WebString& name) { |
| DCHECK(!frame_ || frame_ == frame); |
| if (!render_view_->renderer_preferences_.report_frame_name_changes) |
| return; |
| |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidChangeName(name)); |
| } |
| |
| 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(RenderViewObserver, render_view_->observers(), |
| DidMatchCSS(frame, |
| 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: |
| NOTREACHED(); |
| } |
| |
| 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(), |
| request.url(), referrer, |
| suggested_name, false)); |
| } else if (policy == blink::WebNavigationPolicyDownloadTo) { |
| render_view_->Send(new ViewHostMsg_DownloadUrl(render_view_->GetRoutingID(), |
| request.url(), referrer, |
| suggested_name, true)); |
| } else { |
| OpenURL(frame, request.url(), referrer, policy); |
| } |
| } |
| |
| blink::WebNavigationPolicy RenderFrameImpl::decidePolicyForNavigation( |
| blink::WebLocalFrame* frame, |
| blink::WebDataSource::ExtraData* extra_data, |
| const blink::WebURLRequest& request, |
| blink::WebNavigationType type, |
| blink::WebNavigationPolicy default_policy, |
| bool is_redirect) { |
| DCHECK(!frame_ || frame_ == frame); |
| return DecidePolicyForNavigation( |
| this, frame, extra_data, request, type, default_policy, is_redirect); |
| } |
| |
| 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(RenderViewObserver, render_view_->observers(), |
| WillSendSubmitEvent(frame, form)); |
| } |
| |
| void RenderFrameImpl::willSubmitForm(blink::WebLocalFrame* frame, |
| const blink::WebFormElement& form) { |
| DCHECK(!frame_ || frame_ == frame); |
| DocumentState* document_state = |
| DocumentState::FromDataSource(frame->provisionalDataSource()); |
| NavigationState* navigation_state = document_state->navigation_state(); |
| InternalDocumentStateData* internal_data = |
| InternalDocumentStateData::FromDocumentState(document_state); |
| |
| if (PageTransitionCoreTypeIs(navigation_state->transition_type(), |
| PAGE_TRANSITION_LINK)) { |
| navigation_state->set_transition_type(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(RenderViewObserver, render_view_->observers(), |
| WillSubmitForm(frame, form)); |
| } |
| |
| void RenderFrameImpl::didCreateDataSource(blink::WebLocalFrame* frame, |
| blink::WebDataSource* datasource) { |
| DCHECK(!frame_ || frame_ == frame); |
| |
| // TODO(nasko): Move implementation here. Needed state: |
| // * pending_navigation_params_ |
| // * webview |
| // Needed methods: |
| // * PopulateDocumentStateFromPending |
| // * CreateNavigationStateFromPending |
| render_view_->didCreateDataSource(frame, datasource); |
| |
| // Create the serviceworker's per-document network observing object. |
| scoped_ptr<ServiceWorkerNetworkProvider> |
| network_provider(new ServiceWorkerNetworkProvider()); |
| ServiceWorkerNetworkProvider::AttachToDocumentState( |
| DocumentState::FromDataSource(datasource), |
| network_provider.Pass()); |
| } |
| |
| void RenderFrameImpl::didStartProvisionalLoad(blink::WebLocalFrame* frame) { |
| 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; |
| |
| DocumentState* document_state = DocumentState::FromDataSource(ds); |
| |
| // We should only navigate to swappedout:// when is_swapped_out_ is true. |
| CHECK((ds->request().url() != GURL(kSwappedOutURL)) || |
| is_swapped_out_ || |
| render_view_->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()) { |
| double event_time = ds->triggeringEventTime(); |
| if (event_time != 0.0) |
| document_state->set_request_time(Time::FromDoubleT(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. |
| document_state->navigation_state()->set_transition_type( |
| PAGE_TRANSITION_AUTO_SUBFRAME); |
| } |
| |
| FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(), |
| DidStartProvisionalLoad(frame)); |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidStartProvisionalLoad()); |
| |
| int parent_routing_id = frame->parent() ? |
| FromWebFrame(frame->parent())->GetRoutingID() : -1; |
| Send(new FrameHostMsg_DidStartProvisionalLoadForFrame( |
| routing_id_, parent_routing_id, ds->request().url())); |
| } |
| |
| void RenderFrameImpl::didReceiveServerRedirectForProvisionalLoad( |
| blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| render_view_->history_controller()->RemoveChildrenForRedirect(this); |
| if (frame->parent()) |
| return; |
| // Received a redirect on the main frame. |
| WebDataSource* data_source = frame->provisionalDataSource(); |
| if (!data_source) { |
| // Should only be invoked when we have a data source. |
| NOTREACHED(); |
| return; |
| } |
| std::vector<GURL> redirects; |
| GetRedirectChain(data_source, &redirects); |
| if (redirects.size() >= 2) { |
| Send(new FrameHostMsg_DidRedirectProvisionalLoad( |
| routing_id_, |
| render_view_->page_id_, |
| redirects[redirects.size() - 2], |
| redirects.back())); |
| } |
| } |
| |
| void RenderFrameImpl::didFailProvisionalLoad(blink::WebLocalFrame* frame, |
| const blink::WebURLError& error) { |
| 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)); |
| |
| bool show_repost_interstitial = |
| (error.reason == net::ERR_CACHE_MISS && |
| EqualsASCII(failed_request.httpMethod(), "POST")); |
| |
| FrameHostMsg_DidFailProvisionalLoadWithError_Params params; |
| params.frame_unique_name = frame->uniqueName(); |
| params.error_code = error.reason; |
| GetContentClient()->renderer()->GetNavigationErrorStrings( |
| render_view_.get(), |
| frame, |
| failed_request, |
| error, |
| NULL, |
| ¶ms.error_description); |
| params.url = error.unreachableURL; |
| params.showing_repost_interstitial = show_repost_interstitial; |
| Send(new FrameHostMsg_DidFailProvisionalLoadWithError( |
| routing_id_, params)); |
| |
| // Don't display an error page if this is simply a cancelled load. Aside |
| // from being dumb, WebCore doesn't expect it and it will cause a crash. |
| if (error.reason == net::ERR_ABORTED) |
| return; |
| |
| // Don't display "client blocked" error page if browser has asked us not to. |
| if (error.reason == net::ERR_BLOCKED_BY_CLIENT && |
| render_view_->renderer_preferences_.disable_client_blocked_error_page) { |
| return; |
| } |
| |
| // Allow the embedder to suppress an error page. |
| if (GetContentClient()->renderer()->ShouldSuppressErrorPage(this, |
| error.unreachableURL)) { |
| return; |
| } |
| |
| if (RenderThreadImpl::current() && |
| RenderThreadImpl::current()->layout_test_mode()) { |
| return; |
| } |
| |
| // Make sure we never show errors in view source mode. |
| frame->enableViewSourceMode(false); |
| |
| DocumentState* document_state = DocumentState::FromDataSource(ds); |
| NavigationState* navigation_state = 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. |
| // |
| // AUTO_SUBFRAME loads should always be treated as loads that do not advance |
| // the page id. |
| // |
| // TODO(davidben): This should also take the failed navigation's replacement |
| // state into account, if a location.replace() failed. |
| bool replace = |
| navigation_state->pending_page_id() != -1 || |
| PageTransitionCoreTypeIs(navigation_state->transition_type(), |
| PAGE_TRANSITION_AUTO_SUBFRAME); |
| |
| // If we failed on a browser initiated request, then make sure that our error |
| // page load is regarded as the same browser initiated request. |
| if (!navigation_state->is_content_initiated()) { |
| render_view_->pending_navigation_params_.reset( |
| new FrameMsg_Navigate_Params); |
| render_view_->pending_navigation_params_->page_id = |
| navigation_state->pending_page_id(); |
| render_view_->pending_navigation_params_->pending_history_list_offset = |
| navigation_state->pending_history_list_offset(); |
| render_view_->pending_navigation_params_->should_clear_history_list = |
| navigation_state->history_list_was_cleared(); |
| render_view_->pending_navigation_params_->transition = |
| navigation_state->transition_type(); |
| render_view_->pending_navigation_params_->request_time = |
| document_state->request_time(); |
| render_view_->pending_navigation_params_->should_replace_current_entry = |
| replace; |
| } |
| |
| // Load an error page. |
| LoadNavigationErrorPage(failed_request, error, replace); |
| } |
| |
| void RenderFrameImpl::didCommitProvisionalLoad( |
| blink::WebLocalFrame* frame, |
| const blink::WebHistoryItem& item, |
| blink::WebHistoryCommitType commit_type) { |
| DCHECK(!frame_ || frame_ == frame); |
| DocumentState* document_state = |
| DocumentState::FromDataSource(frame->dataSource()); |
| NavigationState* navigation_state = document_state->navigation_state(); |
| |
| // When we perform a new navigation, we need to update the last committed |
| // session history entry with state for the page we are leaving. Do this |
| // before updating the HistoryController state. |
| render_view_->UpdateSessionHistory(frame); |
| |
| render_view_->history_controller()->UpdateForCommit(this, item, commit_type, |
| navigation_state->was_within_same_page()); |
| |
| InternalDocumentStateData* internal_data = |
| InternalDocumentStateData::FromDocumentState(document_state); |
| |
| if (document_state->commit_load_time().is_null()) |
| document_state->set_commit_load_time(Time::Now()); |
| |
| if (internal_data->must_reset_scroll_and_scale_state()) { |
| render_view_->webview()->resetScrollAndScaleState(); |
| internal_data->set_must_reset_scroll_and_scale_state(false); |
| } |
| internal_data->set_use_error_page(false); |
| |
| bool is_new_navigation = commit_type == blink::WebStandardCommit; |
| if (is_new_navigation) { |
| // We bump our Page ID to correspond with the new session history entry. |
| render_view_->page_id_ = render_view_->next_page_id_++; |
| |
| // Don't update history_page_ids_ (etc) for kSwappedOutURL, since |
| // we don't want to forget the entry that was there, and since we will |
| // never come back to kSwappedOutURL. Note that we have to call |
| // UpdateSessionHistory and update page_id_ even in this case, so that |
| // the current entry gets a state update and so that we don't send a |
| // state update to the wrong entry when we swap back in. |
| if (GetLoadingUrl() != GURL(kSwappedOutURL)) { |
| // Advance our offset in session history, applying the length limit. |
| // There is now no forward history. |
| render_view_->history_list_offset_++; |
| if (render_view_->history_list_offset_ >= kMaxSessionHistoryEntries) |
| render_view_->history_list_offset_ = kMaxSessionHistoryEntries - 1; |
| render_view_->history_list_length_ = |
| render_view_->history_list_offset_ + 1; |
| render_view_->history_page_ids_.resize( |
| render_view_->history_list_length_, -1); |
| render_view_->history_page_ids_[render_view_->history_list_offset_] = |
| render_view_->page_id_; |
| } |
| } else { |
| // Inspect the navigation_state on this frame to see if the navigation |
| // corresponds to a session history navigation... Note: |frame| may or |
| // may not be the toplevel frame, but for the case of capturing session |
| // history, the first committed frame suffices. We keep track of whether |
| // we've seen this commit before so that only capture session history once |
| // per navigation. |
| // |
| // Note that we need to check if the page ID changed. In the case of a |
| // reload, the page ID doesn't change, and UpdateSessionHistory gets the |
| // previous URL and the current page ID, which would be wrong. |
| if (navigation_state->pending_page_id() != -1 && |
| navigation_state->pending_page_id() != render_view_->page_id_ && |
| !navigation_state->request_committed()) { |
| // This is a successful session history navigation! |
| render_view_->page_id_ = navigation_state->pending_page_id(); |
| |
| render_view_->history_list_offset_ = |
| navigation_state->pending_history_list_offset(); |
| |
| // If the history list is valid, our list of page IDs should be correct. |
| DCHECK(render_view_->history_list_length_ <= 0 || |
| render_view_->history_list_offset_ < 0 || |
| render_view_->history_list_offset_ >= |
| render_view_->history_list_length_ || |
| render_view_->history_page_ids_[render_view_->history_list_offset_] |
| == render_view_->page_id_); |
| } |
| } |
| |
| FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers_, |
| DidCommitProvisionalLoad(frame, is_new_navigation)); |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, |
| DidCommitProvisionalLoad(is_new_navigation)); |
| |
| if (!frame->parent()) { // Only for top frames. |
| RenderThreadImpl* render_thread_impl = RenderThreadImpl::current(); |
| if (render_thread_impl) { // Can be NULL in tests. |
| render_thread_impl->histogram_customizer()-> |
| RenderViewNavigatedToHost(GURL(GetLoadingUrl()).host(), |
| RenderViewImpl::GetRenderViewCount()); |
| } |
| } |
| |
| // Remember that we've already processed this request, so we don't update |
| // the session history again. We do this regardless of whether this is |
| // a session history navigation, because if we attempted a session history |
| // navigation without valid HistoryItem state, WebCore will think it is a |
| // new navigation. |
| navigation_state->set_request_committed(true); |
| |
| UpdateURL(frame); |
| |
| // Check whether we have new encoding name. |
| UpdateEncoding(frame, frame->view()->pageEncoding().utf8()); |
| } |
| |
| void RenderFrameImpl::didClearWindowObject(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| // TODO(nasko): Move implementation here. Needed state: |
| // * enabled_bindings_ |
| // * dom_automation_controller_ |
| // * stats_collection_controller_ |
| |
| render_view_->didClearWindowObject(frame); |
| |
| if (render_view_->GetEnabledBindings() & BINDINGS_POLICY_DOM_AUTOMATION) |
| DomAutomationController::Install(this, frame); |
| |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidClearWindowObject()); |
| } |
| |
| void RenderFrameImpl::didCreateDocumentElement(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| |
| // Notify the browser about non-blank documents loading in the top frame. |
| GURL url = frame->document().url(); |
| if (url.is_valid() && url.spec() != url::kAboutBlankURL) { |
| // TODO(nasko): Check if webview()->mainFrame() is the same as the |
| // frame->tree()->top(). |
| blink::WebFrame* main_frame = render_view_->webview()->mainFrame(); |
| if (frame == main_frame) { |
| // For now, don't remember plugin zoom values. We don't want to mix them |
| // with normal web content (i.e. a fixed layout plugin would usually want |
| // them different). |
| render_view_->Send(new ViewHostMsg_DocumentAvailableInMainFrame( |
| render_view_->GetRoutingID(), |
| main_frame->document().isPluginDocument())); |
| } |
| } |
| |
| FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(), |
| DidCreateDocumentElement(frame)); |
| } |
| |
| void RenderFrameImpl::didReceiveTitle(blink::WebLocalFrame* frame, |
| const blink::WebString& title, |
| blink::WebTextDirection direction) { |
| DCHECK(!frame_ || frame_ == frame); |
| // Ignore all but top level navigations. |
| if (!frame->parent()) { |
| base::string16 title16 = title; |
| base::debug::TraceLog::GetInstance()->UpdateProcessLabel( |
| routing_id_, base::UTF16ToUTF8(title16)); |
| |
| base::string16 shortened_title = title16.substr(0, kMaxTitleChars); |
| Send(new FrameHostMsg_UpdateTitle(routing_id_, |
| render_view_->page_id_, |
| shortened_title, direction)); |
| } |
| |
| // Also check whether we have new encoding name. |
| UpdateEncoding(frame, frame->view()->pageEncoding().utf8()); |
| } |
| |
| void RenderFrameImpl::didChangeIcon(blink::WebLocalFrame* frame, |
| blink::WebIconURL::Type icon_type) { |
| DCHECK(!frame_ || frame_ == frame); |
| // TODO(nasko): Investigate wheather implementation should move here. |
| render_view_->didChangeIcon(frame, icon_type); |
| } |
| |
| void RenderFrameImpl::didFinishDocumentLoad(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| WebDataSource* ds = frame->dataSource(); |
| DocumentState* document_state = DocumentState::FromDataSource(ds); |
| document_state->set_finish_document_load_time(Time::Now()); |
| |
| Send(new FrameHostMsg_DidFinishDocumentLoad(routing_id_)); |
| |
| FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(), |
| DidFinishDocumentLoad(frame)); |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidFinishDocumentLoad()); |
| |
| // Check whether we have new encoding name. |
| UpdateEncoding(frame, frame->view()->pageEncoding().utf8()); |
| } |
| |
| void RenderFrameImpl::didHandleOnloadEvents(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| if (!frame->parent()) |
| Send(new FrameHostMsg_DocumentOnLoadCompleted(routing_id_)); |
| } |
| |
| void RenderFrameImpl::didFailLoad(blink::WebLocalFrame* frame, |
| const blink::WebURLError& error) { |
| DCHECK(!frame_ || frame_ == frame); |
| // TODO(nasko): Move implementation here. No state needed. |
| WebDataSource* ds = frame->dataSource(); |
| DCHECK(ds); |
| |
| FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(), |
| DidFailLoad(frame, error)); |
| |
| const WebURLRequest& failed_request = ds->request(); |
| base::string16 error_description; |
| GetContentClient()->renderer()->GetNavigationErrorStrings( |
| render_view_.get(), |
| frame, |
| failed_request, |
| error, |
| NULL, |
| &error_description); |
| Send(new FrameHostMsg_DidFailLoadWithError(routing_id_, |
| failed_request.url(), |
| error.reason, |
| error_description)); |
| } |
| |
| void RenderFrameImpl::didFinishLoad(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| WebDataSource* ds = frame->dataSource(); |
| DocumentState* document_state = DocumentState::FromDataSource(ds); |
| if (document_state->finish_load_time().is_null()) { |
| if (!frame->parent()) { |
| TRACE_EVENT_INSTANT0("WebCore", "LoadFinished", |
| TRACE_EVENT_SCOPE_PROCESS); |
| } |
| document_state->set_finish_load_time(Time::Now()); |
| } |
| |
| FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(), |
| DidFinishLoad(frame)); |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidFinishLoad()); |
| |
| // Don't send this message while the frame is swapped out. |
| if (is_swapped_out()) |
| return; |
| |
| Send(new FrameHostMsg_DidFinishLoad(routing_id_, |
| ds->request().url())); |
| } |
| |
| void RenderFrameImpl::didNavigateWithinPage(blink::WebLocalFrame* frame, |
| const blink::WebHistoryItem& item, |
| blink::WebHistoryCommitType commit_type) { |
| DCHECK(!frame_ || frame_ == frame); |
| // If this was a reference fragment navigation that we initiated, then we |
| // could end up having a non-null pending navigation params. We just need to |
| // update the ExtraData on the datasource so that others who read the |
| // ExtraData will get the new NavigationState. Similarly, if we did not |
| // initiate this navigation, then we need to take care to reset any pre- |
| // existing navigation state to a content-initiated navigation state. |
| // DidCreateDataSource conveniently takes care of this for us. |
| didCreateDataSource(frame, frame->dataSource()); |
| |
| DocumentState* document_state = |
| DocumentState::FromDataSource(frame->dataSource()); |
| NavigationState* new_state = document_state->navigation_state(); |
| new_state->set_was_within_same_page(true); |
| |
| didCommitProvisionalLoad(frame, item, commit_type); |
| } |
| |
| void RenderFrameImpl::didUpdateCurrentHistoryItem(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| // TODO(nasko): Move implementation here. Needed methods: |
| // * StartNavStateSyncTimerIfNecessary |
| render_view_->didUpdateCurrentHistoryItem(frame); |
| } |
| |
| void RenderFrameImpl::didChangeThemeColor() { |
| if (frame_->parent()) |
| return; |
| |
| Send(new FrameHostMsg_DidChangeThemeColor( |
| routing_id_, frame_->document().themeColor())); |
| } |
| |
| blink::WebNotificationPresenter* RenderFrameImpl::notificationPresenter() { |
| return notification_provider_; |
| } |
| |
| void RenderFrameImpl::didChangeSelection(bool is_empty_selection) { |
| if (!GetRenderWidget()->handling_input_event() && !handling_select_range_) |
| return; |
| |
| if (is_empty_selection) |
| selection_text_.clear(); |
| |
| // UpdateTextInputState should be called before SyncSelectionIfRequired. |
| // UpdateTextInputState may send TextInputStateChanged to notify the focus |
| // was changed, and SyncSelectionIfRequired may send SelectionChanged |
| // to notify the selection was changed. Focus change should be notified |
| // before selection change. |
| GetRenderWidget()->UpdateTextInputState( |
| RenderWidget::NO_SHOW_IME, RenderWidget::FROM_NON_IME); |
| SyncSelectionIfRequired(); |
| } |
| |
| blink::WebColorChooser* RenderFrameImpl::createColorChooser( |
| blink::WebColorChooserClient* client, |
| const blink::WebColor& initial_color, |
| const blink::WebVector<blink::WebColorSuggestion>& suggestions) { |
| RendererWebColorChooserImpl* color_chooser = |
| new RendererWebColorChooserImpl(this, client); |
| std::vector<content::ColorSuggestion> color_suggestions; |
| for (size_t i = 0; i < suggestions.size(); i++) { |
| color_suggestions.push_back(content::ColorSuggestion(suggestions[i])); |
| } |
| color_chooser->Open(static_cast<SkColor>(initial_color), color_suggestions); |
| return color_chooser; |
| } |
| |
| void RenderFrameImpl::runModalAlertDialog(const blink::WebString& message) { |
| RunJavaScriptMessage(JAVASCRIPT_MESSAGE_TYPE_ALERT, |
| message, |
| base::string16(), |
| frame_->document().url(), |
| NULL); |
| } |
| |
| bool RenderFrameImpl::runModalConfirmDialog(const blink::WebString& message) { |
| return RunJavaScriptMessage(JAVASCRIPT_MESSAGE_TYPE_CONFIRM, |
| message, |
| base::string16(), |
| frame_->document().url(), |
| NULL); |
| } |
| |
| bool RenderFrameImpl::runModalPromptDialog( |
| const blink::WebString& message, |
| const blink::WebString& default_value, |
| blink::WebString* actual_value) { |
| base::string16 result; |
| bool ok = RunJavaScriptMessage(JAVASCRIPT_MESSAGE_TYPE_PROMPT, |
| message, |
| default_value, |
| frame_->document().url(), |
| &result); |
| if (ok) |
| actual_value->assign(result); |
| return ok; |
| } |
| |
| bool RenderFrameImpl::runModalBeforeUnloadDialog( |
| bool is_reload, |
| const blink::WebString& message) { |
| // If we are swapping out, we have already run the beforeunload handler. |
| // TODO(creis): Fix OnSwapOut to clear the frame without running beforeunload |
| // at all, to avoid running it twice. |
| if (render_view()->is_swapped_out_) |
| return true; |
| |
| // 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; |
| // This is an ignored return value, but is included so we can accept the same |
| // response as RunJavaScriptMessage. |
| base::string16 ignored_result; |
| render_view()->SendAndRunNestedMessageLoop( |
| new FrameHostMsg_RunBeforeUnloadConfirm( |
| routing_id_, frame_->document().url(), message, is_reload, |
| &success, &ignored_result)); |
| return success; |
| } |
| |
| void RenderFrameImpl::showContextMenu(const blink::WebContextMenuData& data) { |
| ContextMenuParams params = ContextMenuParamsBuilder::Build(data); |
| params.source_type = GetRenderWidget()->context_menu_source_type(); |
| GetRenderWidget()->OnShowHostContextMenu(¶ms); |
| if (GetRenderWidget()->has_host_context_menu_location()) { |
| params.x = GetRenderWidget()->host_context_menu_location().x(); |
| params.y = GetRenderWidget()->host_context_menu_location().y(); |
| } |
| |
| // Plugins, e.g. PDF, don't currently update the render view when their |
| // selected text changes, but the context menu params do contain the updated |
| // selection. If that's the case, update the render view's state just prior |
| // to showing the context menu. |
| // TODO(asvitkine): http://crbug.com/152432 |
| if (ShouldUpdateSelectionTextFromContextMenuParams( |
| selection_text_, selection_text_offset_, selection_range_, params)) { |
| selection_text_ = params.selection_text; |
| // TODO(asvitkine): Text offset and range is not available in this case. |
| selection_text_offset_ = 0; |
| selection_range_ = gfx::Range(0, selection_text_.length()); |
| // This IPC is dispatched by RenderWidetHost, so use its routing ID. |
| Send(new ViewHostMsg_SelectionChanged( |
| GetRenderWidget()->routing_id(), selection_text_, |
| selection_text_offset_, selection_range_)); |
| } |
| |
| // Serializing a GURL longer than kMaxURLChars will fail, so don't do |
| // it. We replace it with an empty GURL so the appropriate items are disabled |
| // in the context menu. |
| // TODO(jcivelli): http://crbug.com/45160 This prevents us from saving large |
| // data encoded images. We should have a way to save them. |
| if (params.src_url.spec().size() > GetMaxURLChars()) |
| params.src_url = GURL(); |
| context_menu_node_ = data.node; |
| |
| #if defined(OS_ANDROID) |
| gfx::Rect start_rect; |
| gfx::Rect end_rect; |
| GetRenderWidget()->GetSelectionBounds(&start_rect, &end_rect); |
| params.selection_start = gfx::Point(start_rect.x(), start_rect.bottom()); |
| params.selection_end = gfx::Point(end_rect.right(), end_rect.bottom()); |
| #endif |
| |
| Send(new FrameHostMsg_ContextMenu(routing_id_, params)); |
| } |
| |
| void RenderFrameImpl::clearContextMenu() { |
| context_menu_node_.reset(); |
| } |
| |
| void RenderFrameImpl::willRequestAfterPreconnect( |
| blink::WebLocalFrame* frame, |
| blink::WebURLRequest& request) { |
| DCHECK(!frame_ || frame_ == frame); |
| // FIXME(kohei): This will never be set. |
| WebString custom_user_agent; |
| |
| DCHECK(!request.extraData()); |
| |
| bool was_after_preconnect_request = true; |
| // The args after |was_after_preconnect_request| are not used, and set to |
| // correct values at |willSendRequest|. |
| RequestExtraData* extra_data = new RequestExtraData(); |
| extra_data->set_custom_user_agent(custom_user_agent); |
| extra_data->set_was_after_preconnect_request(was_after_preconnect_request); |
| request.setExtraData(extra_data); |
| } |
| |
| void RenderFrameImpl::willSendRequest( |
| blink::WebLocalFrame* frame, |
| unsigned identifier, |
| blink::WebURLRequest& request, |
| const blink::WebURLResponse& redirect_response) { |
| DCHECK(!frame_ || frame_ == frame); |
| // The request my be empty during tests. |
| if (request.url().isEmpty()) |
| return; |
| |
| // Set the first party for cookies url if it has not been set yet (new |
| // requests). For redirects, it is updated by WebURLLoaderImpl. |
| if (request.firstPartyForCookies().isEmpty()) { |
| if (request.targetType() == blink::WebURLRequest::TargetIsMainFrame) { |
| request.setFirstPartyForCookies(request.url()); |
| } else { |
| request.setFirstPartyForCookies( |
| frame->top()->document().firstPartyForCookies()); |
| } |
| } |
| |
| WebFrame* top_frame = frame->top(); |
| if (!top_frame) |
| top_frame = frame; |
| WebDataSource* provisional_data_source = top_frame->provisionalDataSource(); |
| WebDataSource* top_data_source = top_frame->dataSource(); |
| WebDataSource* data_source = |
| provisional_data_source ? provisional_data_source : top_data_source; |
| |
| PageTransition transition_type = PAGE_TRANSITION_LINK; |
| DocumentState* document_state = DocumentState::FromDataSource(data_source); |
| DCHECK(document_state); |
| InternalDocumentStateData* internal_data = |
| InternalDocumentStateData::FromDocumentState(document_state); |
| NavigationState* navigation_state = document_state->navigation_state(); |
| transition_type = navigation_state->transition_type(); |
| |
| GURL request_url(request.url()); |
| GURL new_url; |
| if (GetContentClient()->renderer()->WillSendRequest( |
| frame, |
| transition_type, |
| request_url, |
| request.firstPartyForCookies(), |
| &new_url)) { |
| request.setURL(WebURL(new_url)); |
| } |
| |
| if (internal_data->is_cache_policy_override_set()) |
| request.setCachePolicy(internal_data->cache_policy_override()); |
| |
| // The request's extra data may indicate that we should set a custom user |
| // agent. This needs to be done here, after WebKit is through with setting the |
| // user agent on its own. |
| WebString custom_user_agent; |
| bool was_after_preconnect_request = false; |
| if (request.extraData()) { |
| RequestExtraData* old_extra_data = |
| static_cast<RequestExtraData*>( |
| request.extraData()); |
| custom_user_agent = old_extra_data->custom_user_agent(); |
| was_after_preconnect_request = |
| old_extra_data->was_after_preconnect_request(); |
| |
| if (!custom_user_agent.isNull()) { |
| if (custom_user_agent.isEmpty()) |
| request.clearHTTPHeaderField("User-Agent"); |
| else |
| request.setHTTPHeaderField("User-Agent", custom_user_agent); |
| } |
| } |
| |
| // Add the default accept header for frame request if it has not been set |
| // already. |
| if ((request.targetType() == blink::WebURLRequest::TargetIsMainFrame || |
| request.targetType() == blink::WebURLRequest::TargetIsSubframe) && |
| request.httpHeaderField(WebString::fromUTF8(kAcceptHeader)).isEmpty()) { |
| request.setHTTPHeaderField(WebString::fromUTF8(kAcceptHeader), |
| WebString::fromUTF8(kDefaultAcceptHeader)); |
| } |
| |
| // Attach |should_replace_current_entry| state to requests so that, should |
| // this navigation later require a request transfer, all state is preserved |
| // when it is re-created in the new process. |
| bool should_replace_current_entry = false; |
| if (navigation_state->is_content_initiated()) { |
| should_replace_current_entry = data_source->replacesCurrentHistoryItem(); |
| } else { |
| // If the navigation is browser-initiated, the NavigationState contains the |
| // correct value instead of the WebDataSource. |
| // |
| // TODO(davidben): Avoid this awkward duplication of state. See comment on |
| // NavigationState::should_replace_current_entry(). |
| should_replace_current_entry = |
| navigation_state->should_replace_current_entry(); |
| } |
| |
| int provider_id = kInvalidServiceWorkerProviderId; |
| if (request.targetType() == blink::WebURLRequest::TargetIsMainFrame || |
| request.targetType() == blink::WebURLRequest::TargetIsSubframe) { |
| // |provisionalDataSource| may be null in some content::ResourceFetcher |
| // use cases, we don't hook those requests. |
| if (frame->provisionalDataSource()) { |
| ServiceWorkerNetworkProvider* provider = |
| ServiceWorkerNetworkProvider::FromDocumentState( |
| DocumentState::FromDataSource(frame->provisionalDataSource())); |
| provider_id = provider->provider_id(); |
| } |
| } else if (frame->dataSource()) { |
| ServiceWorkerNetworkProvider* provider = |
| ServiceWorkerNetworkProvider::FromDocumentState( |
| DocumentState::FromDataSource(frame->dataSource())); |
| provider_id = provider->provider_id(); |
| } |
| |
| int parent_routing_id = frame->parent() ? |
| FromWebFrame(frame->parent())->GetRoutingID() : -1; |
| RequestExtraData* extra_data = new RequestExtraData(); |
| extra_data->set_visibility_state(render_view_->visibilityState()); |
| extra_data->set_custom_user_agent(custom_user_agent); |
| extra_data->set_was_after_preconnect_request(was_after_preconnect_request); |
| extra_data->set_render_frame_id(routing_id_); |
| extra_data->set_is_main_frame(frame == top_frame); |
| extra_data->set_frame_origin( |
| GURL(frame->document().securityOrigin().toString())); |
| extra_data->set_parent_is_main_frame(frame->parent() == top_frame); |
| extra_data->set_parent_render_frame_id(parent_routing_id); |
| extra_data->set_allow_download(navigation_state->allow_download()); |
| extra_data->set_transition_type(transition_type); |
| extra_data->set_should_replace_current_entry(should_replace_current_entry); |
| extra_data->set_transferred_request_child_id( |
| navigation_state->transferred_request_child_id()); |
| extra_data->set_transferred_request_request_id( |
| navigation_state->transferred_request_request_id()); |
| extra_data->set_service_worker_provider_id(provider_id); |
| request.setExtraData(extra_data); |
| |
| DocumentState* top_document_state = |
| DocumentState::FromDataSource(top_data_source); |
| if (top_document_state) { |
| // TODO(gavinp): separate out prefetching and prerender field trials |
| // if the rel=prerender rel type is sticking around. |
| if (request.targetType() == WebURLRequest::TargetIsPrefetch) |
| top_document_state->set_was_prefetcher(true); |
| |
| if (was_after_preconnect_request) |
| top_document_state->set_was_after_preconnect_request(true); |
| } |
| |
| // This is an instance where we embed a copy of the routing id |
| // into the data portion of the message. This can cause problems if we |
| // don't register this id on the browser side, since the download manager |
| // expects to find a RenderViewHost based off the id. |
| request.setRequestorID(render_view_->GetRoutingID()); |
| request.setHasUserGesture(WebUserGestureIndicator::isProcessingUserGesture()); |
| |
| if (!navigation_state->extra_headers().empty()) { |
| for (net::HttpUtil::HeadersIterator i( |
| navigation_state->extra_headers().begin(), |
| navigation_state->extra_headers().end(), "\n"); |
| i.GetNext(); ) { |
| if (LowerCaseEqualsASCII(i.name(), "referer")) { |
| WebString referrer = WebSecurityPolicy::generateReferrerHeader( |
| blink::WebReferrerPolicyDefault, |
| request.url(), |
| WebString::fromUTF8(i.values())); |
| request.setHTTPReferrer(referrer, blink::WebReferrerPolicyDefault); |
| } else { |
| request.setHTTPHeaderField(WebString::fromUTF8(i.name()), |
| WebString::fromUTF8(i.values())); |
| } |
| } |
| } |
| |
| if (!render_view_->renderer_preferences_.enable_referrers) |
| request.setHTTPReferrer(WebString(), blink::WebReferrerPolicyDefault); |
| } |
| |
| void RenderFrameImpl::didReceiveResponse( |
| blink::WebLocalFrame* frame, |
| unsigned identifier, |
| const blink::WebURLResponse& response) { |
| DCHECK(!frame_ || frame_ == frame); |
| // Only do this for responses that correspond to a provisional data source |
| // of the top-most frame. If we have a provisional data source, then we |
| // can't have any sub-resources yet, so we know that this response must |
| // correspond to a frame load. |
| if (!frame->provisionalDataSource() || frame->parent()) |
| return; |
| |
| // If we are in view source mode, then just let the user see the source of |
| // the server's error page. |
| if (frame->isViewSourceModeEnabled()) |
| return; |
| |
| DocumentState* document_state = |
| DocumentState::FromDataSource(frame->provisionalDataSource()); |
| int http_status_code = response.httpStatusCode(); |
| |
| // Record page load flags. |
| WebURLResponseExtraDataImpl* extra_data = |
| GetExtraDataFromResponse(response); |
| if (extra_data) { |
| document_state->set_was_fetched_via_spdy( |
| extra_data->was_fetched_via_spdy()); |
| document_state->set_was_npn_negotiated( |
| extra_data->was_npn_negotiated()); |
| document_state->set_npn_negotiated_protocol( |
| extra_data->npn_negotiated_protocol()); |
| document_state->set_was_alternate_protocol_available( |
| extra_data->was_alternate_protocol_available()); |
| document_state->set_connection_info( |
| extra_data->connection_info()); |
| document_state->set_was_fetched_via_proxy( |
| extra_data->was_fetched_via_proxy()); |
| } |
| InternalDocumentStateData* internal_data = |
| InternalDocumentStateData::FromDocumentState(document_state); |
| internal_data->set_http_status_code(http_status_code); |
| // Whether or not the http status code actually corresponds to an error is |
| // only checked when the page is done loading, if |use_error_page| is |
| // still true. |
| internal_data->set_use_error_page(true); |
| } |
| |
| void RenderFrameImpl::didFinishResourceLoad(blink::WebLocalFrame* frame, |
| unsigned identifier) { |
| DCHECK(!frame_ || frame_ == frame); |
| InternalDocumentStateData* internal_data = |
| InternalDocumentStateData::FromDataSource(frame->dataSource()); |
| if (!internal_data->use_error_page()) |
| return; |
| |
| // Do not show error page when DevTools is attached. |
| if (render_view_->devtools_agent_->IsAttached()) |
| return; |
| |
| // Display error page, if appropriate. |
| std::string error_domain = "http"; |
| int http_status_code = internal_data->http_status_code(); |
| if (GetContentClient()->renderer()->HasErrorPage( |
| http_status_code, &error_domain)) { |
| WebURLError error; |
| error.unreachableURL = frame->document().url(); |
| error.domain = WebString::fromUTF8(error_domain); |
| error.reason = http_status_code; |
| LoadNavigationErrorPage(frame->dataSource()->request(), error, true); |
| } |
| } |
| |
| void RenderFrameImpl::didLoadResourceFromMemoryCache( |
| blink::WebLocalFrame* frame, |
| const blink::WebURLRequest& request, |
| const blink::WebURLResponse& response) { |
| DCHECK(!frame_ || frame_ == frame); |
| // The recipients of this message have no use for data: URLs: they don't |
| // affect the page's insecure content list and are not in the disk cache. To |
| // prevent large (1M+) data: URLs from crashing in the IPC system, we simply |
| // filter them out here. |
| GURL url(request.url()); |
| if (url.SchemeIs("data")) |
| return; |
| |
| // Let the browser know we loaded a resource from the memory cache. This |
| // message is needed to display the correct SSL indicators. |
| render_view_->Send(new ViewHostMsg_DidLoadResourceFromMemoryCache( |
| render_view_->GetRoutingID(), |
| url, |
| response.securityInfo(), |
| request.httpMethod().utf8(), |
| response.mimeType().utf8(), |
| ResourceType::FromTargetType(request.targetType()))); |
| } |
| |
| void RenderFrameImpl::didDisplayInsecureContent(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| render_view_->Send(new ViewHostMsg_DidDisplayInsecureContent( |
| render_view_->GetRoutingID())); |
| } |
| |
| void RenderFrameImpl::didRunInsecureContent( |
| blink::WebLocalFrame* frame, |
| const blink::WebSecurityOrigin& origin, |
| const blink::WebURL& target) { |
| DCHECK(!frame_ || frame_ == frame); |
| render_view_->Send(new ViewHostMsg_DidRunInsecureContent( |
| render_view_->GetRoutingID(), |
| origin.toString().utf8(), |
| target)); |
| } |
| |
| void RenderFrameImpl::didAbortLoading(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| #if defined(ENABLE_PLUGINS) |
| if (frame != render_view_->webview()->mainFrame()) |
| return; |
| PluginChannelHost::Broadcast( |
| new PluginHostMsg_DidAbortLoading(render_view_->GetRoutingID())); |
| #endif |
| } |
| |
| void RenderFrameImpl::didCreateScriptContext(blink::WebLocalFrame* frame, |
| v8::Handle<v8::Context> context, |
| int extension_group, |
| int world_id) { |
| DCHECK(!frame_ || frame_ == frame); |
| GetContentClient()->renderer()->DidCreateScriptContext( |
| frame, context, extension_group, world_id); |
| } |
| |
| void RenderFrameImpl::willReleaseScriptContext(blink::WebLocalFrame* frame, |
| v8::Handle<v8::Context> context, |
| int world_id) { |
| DCHECK(!frame_ || frame_ == frame); |
| |
| FOR_EACH_OBSERVER(RenderFrameObserver, |
| observers_, |
| WillReleaseScriptContext(context, world_id)); |
| } |
| |
| void RenderFrameImpl::didFirstVisuallyNonEmptyLayout( |
| blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| if (frame->parent()) |
| return; |
| |
| InternalDocumentStateData* data = |
| InternalDocumentStateData::FromDataSource(frame->dataSource()); |
| data->set_did_first_visually_non_empty_layout(true); |
| |
| #if defined(OS_ANDROID) |
| GetRenderWidget()->DidChangeBodyBackgroundColor( |
| render_view_->webwidget_->backgroundColor()); |
| #endif |
| } |
| |
| void RenderFrameImpl::didChangeScrollOffset(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| // TODO(nasko): Move implementation here. Needed methods: |
| // * StartNavStateSyncTimerIfNecessary |
| render_view_->didChangeScrollOffset(frame); |
| } |
| |
| void RenderFrameImpl::willInsertBody(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| if (!frame->parent()) { |
| render_view_->Send(new ViewHostMsg_WillInsertBody( |
| render_view_->GetRoutingID())); |
| } |
| } |
| |
| void RenderFrameImpl::reportFindInPageMatchCount(int request_id, |
| int count, |
| bool final_update) { |
| int active_match_ordinal = -1; // -1 = don't update active match ordinal |
| if (!count) |
| active_match_ordinal = 0; |
| |
| render_view_->Send(new ViewHostMsg_Find_Reply( |
| render_view_->GetRoutingID(), request_id, count, |
| gfx::Rect(), active_match_ordinal, final_update)); |
| } |
| |
| void RenderFrameImpl::reportFindInPageSelection( |
| int request_id, |
| int active_match_ordinal, |
| const blink::WebRect& selection_rect) { |
| render_view_->Send(new ViewHostMsg_Find_Reply( |
| render_view_->GetRoutingID(), request_id, -1, selection_rect, |
| active_match_ordinal, false)); |
| } |
| |
| void RenderFrameImpl::requestStorageQuota( |
| blink::WebLocalFrame* frame, |
| blink::WebStorageQuotaType type, |
| unsigned long long requested_size, |
| blink::WebStorageQuotaCallbacks callbacks) { |
| DCHECK(!frame_ || frame_ == frame); |
| WebSecurityOrigin origin = frame->document().securityOrigin(); |
| if (origin.isUnique()) { |
| // Unique origins cannot store persistent state. |
| callbacks.didFail(blink::WebStorageQuotaErrorAbort); |
| return; |
| } |
| ChildThread::current()->quota_dispatcher()->RequestStorageQuota( |
| render_view_->GetRoutingID(), GURL(origin.toString()), |
| static_cast<quota::StorageType>(type), requested_size, |
| QuotaDispatcher::CreateWebStorageQuotaCallbacksWrapper(callbacks)); |
| } |
| |
| void RenderFrameImpl::willOpenSocketStream( |
| blink::WebSocketStreamHandle* handle) { |
| WebSocketStreamHandleImpl* impl = |
| static_cast<WebSocketStreamHandleImpl*>(handle); |
| impl->SetUserData(handle, new SocketStreamHandleData(routing_id_)); |
| } |
| |
| void RenderFrameImpl::willOpenWebSocket(blink::WebSocketHandle* handle) { |
| WebSocketBridge* impl = static_cast<WebSocketBridge*>(handle); |
| impl->set_render_frame_id(routing_id_); |
| } |
| |
| blink::WebGeolocationClient* RenderFrameImpl::geolocationClient() { |
| if (!geolocation_dispatcher_) |
| geolocation_dispatcher_ = new GeolocationDispatcher(this); |
| return geolocation_dispatcher_; |
| } |
| |
| void RenderFrameImpl::willStartUsingPeerConnectionHandler( |
| blink::WebLocalFrame* frame, |
| blink::WebRTCPeerConnectionHandler* handler) { |
| DCHECK(!frame_ || frame_ == frame); |
| #if defined(ENABLE_WEBRTC) |
| static_cast<RTCPeerConnectionHandler*>(handler)->associateWithFrame(frame); |
| #endif |
| } |
| |
| blink::WebUserMediaClient* RenderFrameImpl::userMediaClient() { |
| // This can happen in tests, in which case it's OK to return NULL. |
| if (!InitializeUserMediaClient()) |
| return NULL; |
| |
| return web_user_media_client_; |
| } |
| |
| blink::WebMIDIClient* RenderFrameImpl::webMIDIClient() { |
| if (!midi_dispatcher_) |
| midi_dispatcher_ = new MidiDispatcher(this); |
| return midi_dispatcher_; |
| } |
| |
| bool RenderFrameImpl::willCheckAndDispatchMessageEvent( |
| blink::WebLocalFrame* source_frame, |
| blink::WebFrame* target_frame, |
| blink::WebSecurityOrigin target_origin, |
| blink::WebDOMMessageEvent event) { |
| DCHECK(!frame_ || frame_ == target_frame); |
| |
| if (!render_view_->is_swapped_out_) |
| return false; |
| |
| ViewMsg_PostMessage_Params params; |
| params.data = event.data().toString(); |
| params.source_origin = event.origin(); |
| if (!target_origin.isNull()) |
| params.target_origin = target_origin.toString(); |
| |
| blink::WebMessagePortChannelArray channels = event.releaseChannels(); |
| if (!channels.isEmpty()) { |
| std::vector<int> message_port_ids(channels.size()); |
| // Extract the port IDs from the channel array. |
| for (size_t i = 0; i < channels.size(); ++i) { |
| WebMessagePortChannelImpl* webchannel = |
| static_cast<WebMessagePortChannelImpl*>(channels[i]); |
| message_port_ids[i] = webchannel->message_port_id(); |
| webchannel->QueueMessages(); |
| DCHECK_NE(message_port_ids[i], MSG_ROUTING_NONE); |
| } |
| params.message_port_ids = message_port_ids; |
| } |
| |
| // Include the routing ID for the source frame (if one exists), which the |
| // browser process will translate into the routing ID for the equivalent |
| // frame in the target process. |
| params.source_routing_id = MSG_ROUTING_NONE; |
| if (source_frame) { |
| RenderViewImpl* source_view = |
| RenderViewImpl::FromWebView(source_frame->view()); |
| if (source_view) |
| params.source_routing_id = source_view->routing_id(); |
| } |
| |
| Send(new ViewHostMsg_RouteMessageEvent(render_view_->routing_id_, params)); |
| return true; |
| } |
| |
| blink::WebString RenderFrameImpl::userAgentOverride(blink::WebLocalFrame* frame, |
| const blink::WebURL& url) { |
| DCHECK(!frame_ || frame_ == frame); |
| if (!render_view_->webview() || !render_view_->webview()->mainFrame() || |
| render_view_->renderer_preferences_.user_agent_override.empty()) { |
| return blink::WebString(); |
| } |
| |
| // If we're in the middle of committing a load, the data source we need |
| // will still be provisional. |
| WebFrame* main_frame = render_view_->webview()->mainFrame(); |
| WebDataSource* data_source = NULL; |
| if (main_frame->provisionalDataSource()) |
| data_source = main_frame->provisionalDataSource(); |
| else |
| data_source = main_frame->dataSource(); |
| |
| InternalDocumentStateData* internal_data = data_source ? |
| InternalDocumentStateData::FromDataSource(data_source) : NULL; |
| if (internal_data && internal_data->is_overriding_user_agent()) |
| return WebString::fromUTF8( |
| render_view_->renderer_preferences_.user_agent_override); |
| return blink::WebString(); |
| } |
| |
| blink::WebString RenderFrameImpl::doNotTrackValue(blink::WebLocalFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| if (render_view_->renderer_preferences_.enable_do_not_track) |
| return WebString::fromUTF8("1"); |
| return WebString(); |
| } |
| |
| bool RenderFrameImpl::allowWebGL(blink::WebLocalFrame* frame, |
| bool default_value) { |
| DCHECK(!frame_ || frame_ == frame); |
| if (!default_value) |
| return false; |
| |
| bool blocked = true; |
| render_view_->Send(new ViewHostMsg_Are3DAPIsBlocked( |
| render_view_->GetRoutingID(), |
| GURL(frame->top()->document().securityOrigin().toString()), |
| THREE_D_API_TYPE_WEBGL, |
| &blocked)); |
| return !blocked; |
| } |
| |
| void RenderFrameImpl::didLoseWebGLContext(blink::WebLocalFrame* frame, |
| int arb_robustness_status_code) { |
| DCHECK(!frame_ || frame_ == frame); |
| render_view_->Send(new ViewHostMsg_DidLose3DContext( |
| GURL(frame->top()->document().securityOrigin().toString()), |
| THREE_D_API_TYPE_WEBGL, |
| arb_robustness_status_code)); |
| } |
| |
| void RenderFrameImpl::forwardInputEvent(const blink::WebInputEvent* event) { |
| Send(new FrameHostMsg_ForwardInputEvent(routing_id_, event)); |
| } |
| |
| void RenderFrameImpl::initializeChildFrame(const blink::WebRect& frame_rect, |
| float scale_factor) { |
| Send(new FrameHostMsg_InitializeChildFrame( |
| routing_id_, frame_rect, scale_factor)); |
| } |
| |
| blink::WebScreenOrientationClient* |
| RenderFrameImpl::webScreenOrientationClient() { |
| if (!screen_orientation_dispatcher_) |
| screen_orientation_dispatcher_ = new ScreenOrientationDispatcher(this); |
| return screen_orientation_dispatcher_; |
| } |
| |
| void RenderFrameImpl::DidPlay(blink::WebMediaPlayer* player) { |
| Send(new FrameHostMsg_MediaPlayingNotification( |
| routing_id_, reinterpret_cast<int64>(player), player->hasVideo(), |
| player->hasAudio())); |
| } |
| |
| void RenderFrameImpl::DidPause(blink::WebMediaPlayer* player) { |
| Send(new FrameHostMsg_MediaPausedNotification( |
| routing_id_, reinterpret_cast<int64>(player))); |
| } |
| |
| void RenderFrameImpl::PlayerGone(blink::WebMediaPlayer* player) { |
| DidPause(player); |
| } |
| |
| void RenderFrameImpl::AddObserver(RenderFrameObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void RenderFrameImpl::RemoveObserver(RenderFrameObserver* observer) { |
| observer->RenderFrameGone(); |
| observers_.RemoveObserver(observer); |
| } |
| |
| void RenderFrameImpl::OnStop() { |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, OnStop()); |
| } |
| |
| void RenderFrameImpl::WasHidden() { |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, WasHidden()); |
| } |
| |
| void RenderFrameImpl::WasShown() { |
| FOR_EACH_OBSERVER(RenderFrameObserver, observers_, WasShown()); |
| } |
| |
| bool RenderFrameImpl::IsHidden() { |
| return GetRenderWidget()->is_hidden(); |
| } |
| |
| // Tell the embedding application that the URL of the active page has changed. |
| void RenderFrameImpl::UpdateURL(blink::WebFrame* frame) { |
| DCHECK(!frame_ || frame_ == frame); |
| WebDataSource* ds = frame->dataSource(); |
| DCHECK(ds); |
| |
| const WebURLRequest& request = ds->request(); |
| const WebURLResponse& response = ds->response(); |
| |
| DocumentState* document_state = DocumentState::FromDataSource(ds); |
| NavigationState* navigation_state = document_state->navigation_state(); |
| InternalDocumentStateData* internal_data = |
| InternalDocumentStateData::FromDocumentState(document_state); |
| |
| FrameHostMsg_DidCommitProvisionalLoad_Params params; |
| params.http_status_code = response.httpStatusCode(); |
| params.is_post = false; |
| params.post_id = -1; |
| params.page_id = render_view_->page_id_; |
| params.frame_unique_name = frame->uniqueName(); |
| params.socket_address.set_host(response.remoteIPAddress().utf8()); |
| params.socket_address.set_port(response.remotePort()); |
| WebURLResponseExtraDataImpl* extra_data = GetExtraDataFromResponse(response); |
| if (extra_data) |
| params.was_fetched_via_proxy = extra_data->was_fetched_via_proxy(); |
| params.was_within_same_page = navigation_state->was_within_same_page(); |
| params.security_info = response.securityInfo(); |
| |
| // Set the URL to be displayed in the browser UI to the user. |
| params.url = GetLoadingUrl(); |
| DCHECK(!is_swapped_out_ || params.url == GURL(kSwappedOutURL)); |
| |
| if (frame->document().baseURL() != params.url) |
| params.base_url = frame->document().baseURL(); |
| |
| GetRedirectChain(ds, ¶ms.redirects); |
| params.should_update_history = !ds->hasUnreachableURL() && |
| !response.isMultipartPayload() && (response.httpStatusCode() != 404); |
| |
| params.searchable_form_url = internal_data->searchable_form_url(); |
| params.searchable_form_encoding = internal_data->searchable_form_encoding(); |
| |
| params.gesture = render_view_->navigation_gesture_; |
| render_view_->navigation_gesture_ = NavigationGestureUnknown; |
| |
| // Make navigation state a part of the DidCommitProvisionalLoad message so |
| // that commited entry has it at all times. |
| HistoryEntry* entry = render_view_->history_controller()->GetCurrentEntry(); |
| if (entry) |
| params.page_state = HistoryEntryToPageState(entry); |
| else |
| params.page_state = PageState::CreateFromURL(request.url()); |
| |
| if (!frame->parent()) { |
| // Top-level navigation. |
| |
| // Reset the zoom limits in case a plugin had changed them previously. This |
| // will also call us back which will cause us to send a message to |
| // update WebContentsImpl. |
| render_view_->webview()->zoomLimitsChanged( |
| ZoomFactorToZoomLevel(kMinimumZoomFactor), |
| ZoomFactorToZoomLevel(kMaximumZoomFactor)); |
| |
| // Set zoom level, but don't do it for full-page plugin since they don't use |
| // the same zoom settings. |
| HostZoomLevels::iterator host_zoom = |
| render_view_->host_zoom_levels_.find(GURL(request.url())); |
| if (render_view_->webview()->mainFrame()->document().isPluginDocument()) { |
| // Reset the zoom levels for plugins. |
| render_view_->webview()->setZoomLevel(0); |
| } else { |
| if (host_zoom != render_view_->host_zoom_levels_.end()) |
| render_view_->webview()->setZoomLevel(host_zoom->second); |
| } |
| |
| if (host_zoom != render_view_->host_zoom_levels_.end()) { |
| // This zoom level was merely recorded transiently for this load. We can |
| // erase it now. If at some point we reload this page, the browser will |
| // send us a new, up-to-date zoom level. |
| render_view_->host_zoom_levels_.erase(host_zoom); |
| } |
| |
| // Update contents MIME type for main frame. |
| params.contents_mime_type = ds->response().mimeType().utf8(); |
| |
| params.transition = navigation_state->transition_type(); |
| if (!PageTransitionIsMainFrame(params.transition)) { |
| // If the main frame does a load, it should not be reported as a subframe |
| // navigation. This can occur in the following case: |
| // 1. You're on a site with frames. |
| // 2. You do a subframe navigation. This is stored with transition type |
| // MANUAL_SUBFRAME. |
| // 3. You navigate to some non-frame site, say, google.com. |
| // 4. You navigate back to the page from step 2. Since it was initially |
| // MANUAL_SUBFRAME, it will be that same transition type here. |
| // We don't want that, because any navigation that changes the toplevel |
| // frame should be tracked as a toplevel navigation (this allows us to |
| // update the URL bar, etc). |
| params.transition = PAGE_TRANSITION_LINK; |
| } |
| |
| // If the page contained a client redirect (meta refresh, document.loc...), |
| // set the referrer and transition appropriately. |
| if (ds->isClientRedirect()) { |
| params.referrer = |
| Referrer(params.redirects[0], ds->request().referrerPolicy()); |
| params.transition = static_cast<PageTransition>( |
| params.transition | PAGE_TRANSITION_CLIENT_REDIRECT); |
| } else { |
| params.referrer = RenderViewImpl::GetReferrerFromRequest( |
| frame, ds->request()); |
| } |
| |
| base::string16 method = request.httpMethod(); |
| if (EqualsASCII(method, "POST")) { |
| params.is_post = true; |
| params.post_id = ExtractPostId(entry->root()); |
| } |
| |
| // Send the user agent override back. |
| params.is_overriding_user_agent = internal_data->is_overriding_user_agent(); |
| |
| // Track the URL of the original request. We use the first entry of the |
| // redirect chain if it exists because the chain may have started in another |
| // process. |
| params.original_request_url = GetOriginalRequestURL(ds); |
| |
| params.history_list_was_cleared = |
| navigation_state->history_list_was_cleared(); |
| |
| // Save some histogram data so we can compute the average memory used per |
| // page load of the glyphs. |
| UMA_HISTOGRAM_COUNTS_10000("Memory.GlyphPagesPerLoad", |
| blink::WebGlyphCache::pageCount()); |
| |
| // This message needs to be sent before any of allowScripts(), |
| // allowImages(), allowPlugins() is called for the new page, so that when |
| // these functions send a ViewHostMsg_ContentBlocked message, it arrives |
| // after the FrameHostMsg_DidCommitProvisionalLoad message. |
| Send(new FrameHostMsg_DidCommitProvisionalLoad(routing_id_, params)); |
| } else { |
| // Subframe navigation: the type depends on whether this navigation |
| // generated a new session history entry. When they do generate a session |
| // history entry, it means the user initiated the navigation and we should |
| // mark it as such. This test checks if this is the first time UpdateURL |
| // has been called since WillNavigateToURL was called to initiate the load. |
| if (render_view_->page_id_ > render_view_->last_page_id_sent_to_browser_) |
| params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME; |
| else |
| params.transition = PAGE_TRANSITION_AUTO_SUBFRAME; |
| |
| DCHECK(!navigation_state->history_list_was_cleared()); |
| params.history_list_was_cleared = false; |
| |
| // Don't send this message while the subframe is swapped out. |
| if (!is_swapped_out()) |
| Send(new FrameHostMsg_DidCommitProvisionalLoad(routing_id_, params)); |
| } |
| |
| render_view_->last_page_id_sent_to_browser_ = |
| std::max(render_view_->last_page_id_sent_to_browser_, |
| render_view_->page_id_); |
| |
| // If we end up reusing this WebRequest (for example, due to a #ref click), |
| // we don't want the transition type to persist. Just clear it. |
| navigation_state->set_transition_type(PAGE_TRANSITION_LINK); |
| } |
| |
| WebElement RenderFrameImpl::GetFocusedElement() { |
| WebDocument doc = frame_->document(); |
| if (!doc.isNull()) |
| return doc.focusedElement(); |
| |
| return WebElement(); |
| } |
| |
| void RenderFrameImpl::didStartLoading(bool to_different_document) { |
| render_view_->FrameDidStartLoading(frame_); |
| Send(new FrameHostMsg_DidStartLoading(routing_id_, to_different_document)); |
| } |
| |
| void RenderFrameImpl::didStopLoading() { |
| render_view_->FrameDidStopLoading(frame_); |
| Send(new FrameHostMsg_DidStopLoading(routing_id_)); |
| } |
| |
| void RenderFrameImpl::didChangeLoadProgress(double load_progress) { |
| Send(new FrameHostMsg_DidChangeLoadProgress(routing_id_, load_progress)); |
| } |
| |
| WebNavigationPolicy RenderFrameImpl::DecidePolicyForNavigation( |
| RenderFrame* render_frame, |
| WebFrame* frame, |
| WebDataSource::ExtraData* extraData, |
| const WebURLRequest& request, |
| WebNavigationType type, |
| WebNavigationPolicy default_policy, |
| bool is_redirect) { |
| #ifdef OS_ANDROID |
| // The handlenavigation API is deprecated and will be removed once |
| // crbug.com/325351 is resolved. |
| if (request.url() != GURL(kSwappedOutURL) && |
| GetContentClient()->renderer()->HandleNavigation( |
| render_frame, |
| static_cast<DocumentState*>(extraData), |
| render_view_->opener_id_, |
| frame, |
| request, |
| type, |
| default_policy, |
| is_redirect)) { |
| return blink::WebNavigationPolicyIgnore; |
| } |
| #endif |
| |
| Referrer referrer(RenderViewImpl::GetReferrerFromRequest(frame, request)); |
| |
| if (is_swapped_out_ || render_view_->is_swapped_out()) { |
| if (request.url() != GURL(kSwappedOutURL)) { |
| // Targeted links may try to navigate a swapped out frame. Allow the |
| // browser process to navigate the tab instead. Note that it is also |
| // possible for non-targeted navigations (from this view) to arrive |
| // here just after we are swapped out. It's ok to send them to the |
| // browser, as long as they're for the top level frame. |
| // TODO(creis): Ensure this supports targeted form submissions when |
| // fixing http://crbug.com/101395. |
| if (frame->parent() == NULL) { |
| OpenURL(frame, request.url(), referrer, default_policy); |
| return blink::WebNavigationPolicyIgnore; // Suppress the load here. |
| } |
| |
| // We should otherwise ignore in-process iframe navigations, if they |
| // arrive just after we are swapped out. |
| return blink::WebNavigationPolicyIgnore; |
| } |
| |
| // Allow kSwappedOutURL to complete. |
| return default_policy; |
| } |
| |
| // Webkit is asking whether to navigate to a new URL. |
| // This is fine normally, except if we're showing UI from one security |
| // context and they're trying to navigate to a different context. |
| const GURL& url = request.url(); |
| |
| // A content initiated navigation may have originated from a link-click, |
| // script, drag-n-drop operation, etc. |
| bool is_content_initiated = static_cast<DocumentState*>(extraData)-> |
| navigation_state()->is_content_initiated(); |
| |
| // Experimental: |
| // If --enable-strict-site-isolation or --site-per-process is enabled, send |
| // all top-level navigations to the browser to let it swap processes when |
| // crossing site boundaries. This is currently expected to break some script |
| // calls and navigations, such as form submissions. |
| const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
| bool force_swap_due_to_flag = |
| command_line.HasSwitch(switches::kEnableStrictSiteIsolation) || |
| command_line.HasSwitch(switches::kSitePerProcess); |
| if (force_swap_due_to_flag && |
| !frame->parent() && (is_content_initiated || is_redirect)) { |
| WebString origin_str = frame->document().securityOrigin().toString(); |
| GURL frame_url(origin_str.utf8().data()); |
| // TODO(cevans): revisit whether this site check is still necessary once |
| // crbug.com/101395 is fixed. |
| bool same_domain_or_host = |
| net::registry_controlled_domains::SameDomainOrHost( |
| frame_url, |
| url, |
| net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); |
| if (!same_domain_or_host || frame_url.scheme() != url.scheme()) { |
| OpenURL(frame, url, referrer, default_policy); |
| return blink::WebNavigationPolicyIgnore; |
| } |
| } |
| |
| // If the browser is interested, then give it a chance to look at the request. |
| if (is_content_initiated) { |
| bool is_form_post = ((type == blink::WebNavigationTypeFormSubmitted) || |
| (type == blink::WebNavigationTypeFormResubmitted)) && |
| EqualsASCII(request.httpMethod(), "POST"); |
| bool browser_handles_request = |
| render_view_->renderer_preferences_ |
| .browser_handles_non_local_top_level_requests |
| && IsNonLocalTopLevelNavigation(url, frame, type, is_form_post); |
| if (!browser_handles_request) { |
| browser_handles_request = IsTopLevelNavigation(frame) && |
| render_view_->renderer_preferences_ |
| .browser_handles_all_top_level_requests; |
| } |
| |
| if (browser_handles_request) { |
| // Reset these counters as the RenderView could be reused for the next |
| // navigation. |
| render_view_->page_id_ = -1; |
| render_view_->last_page_id_sent_to_browser_ = -1; |
| OpenURL(frame, url, referrer, default_policy); |
| return blink::WebNavigationPolicyIgnore; // Suppress the load here. |
| } |
| } |
| |
| // Use the frame's original request's URL rather than the document's URL for |
| // subsequent checks. For a popup, the document's URL may become the opener |
| // window's URL if the opener has called document.write(). |
| // See http://crbug.com/93517. |
| GURL old_url(frame->dataSource()->request().url()); |
| |
| // Detect when we're crossing a permission-based boundary (e.g. into or out of |
| // an extension or app origin, leaving a WebUI page, etc). We only care about |
| // top-level navigations (not iframes). But we sometimes navigate to |
| // about:blank to clear a tab, and we want to still allow that. |
| // |
| // Note: this is known to break POST submissions when crossing process |
| // boundaries until http://crbug.com/101395 is fixed. This is better for |
| // security than loading a WebUI, extension or app page in the wrong process. |
| // POST requests don't work because this mechanism does not preserve form |
| // POST data. We will need to send the request's httpBody data up to the |
| // browser process, and issue a special POST navigation in WebKit (via |
| // FrameLoader::loadFrameRequest). See ResourceDispatcher and WebURLLoaderImpl |
| // for examples of how to send the httpBody data. |
| if (!frame->parent() && is_content_initiated && |
| !url.SchemeIs(url::kAboutScheme)) { |
| bool send_referrer = false; |
| |
| // All navigations to or from WebUI URLs or within WebUI-enabled |
| // RenderProcesses must be handled by the browser process so that the |
| // correct bindings and data sources can be registered. |
| // Similarly, navigations to view-source URLs or within ViewSource mode |
| // must be handled by the browser process (except for reloads - those are |
| // safe to leave within the renderer). |
| // Lastly, access to file:// URLs from non-file:// URL pages must be |
| // handled by the browser so that ordinary renderer processes don't get |
| // blessed with file permissions. |
| int cumulative_bindings = RenderProcess::current()->GetEnabledBindings(); |
| bool is_initial_navigation = render_view_->page_id_ == -1; |
| bool should_fork = HasWebUIScheme(url) || HasWebUIScheme(old_url) || |
| (cumulative_bindings & BINDINGS_POLICY_WEB_UI) || |
| url.SchemeIs(kViewSourceScheme) || |
| (frame->isViewSourceModeEnabled() && |
| type != blink::WebNavigationTypeReload); |
| |
| if (!should_fork && url.SchemeIs(url::kFileScheme)) { |
| // Fork non-file to file opens. Check the opener URL if this is the |
| // initial navigation in a newly opened window. |
| GURL source_url(old_url); |
| if (is_initial_navigation && source_url.is_empty() && frame->opener()) |
| source_url = frame->opener()->top()->document().url(); |
| DCHECK(!source_url.is_empty()); |
| should_fork = !source_url.SchemeIs(url::kFileScheme); |
| } |
| |
| if (!should_fork) { |
| // Give the embedder a chance. |
| should_fork = GetContentClient()->renderer()->ShouldFork( |
| frame, url, request.httpMethod().utf8(), is_initial_navigation, |
| is_redirect, &send_referrer); |
| } |
| |
| if (should_fork) { |
| OpenURL( |
| frame, url, send_referrer ? referrer : Referrer(), default_policy); |
| return blink::WebNavigationPolicyIgnore; // Suppress the load here. |
| } |
| } |
| |
| // Detect when a page is "forking" a new tab that can be safely rendered in |
| // its own process. This is done by sites like Gmail that try to open links |
| // in new windows without script connections back to the original page. We |
| // treat such cases as browser navigations (in which we will create a new |
| // renderer for a cross-site navigation), rather than WebKit navigations. |
| // |
| // We use the following heuristic to decide whether to fork a new page in its |
| // own process: |
| // The parent page must open a new tab to about:blank, set the new tab's |
| // window.opener to null, and then redirect the tab to a cross-site URL using |
| // JavaScript. |
| // |
| // TODO(creis): Deprecate this logic once we can rely on rel=noreferrer |
| // (see below). |
| bool is_fork = |
| // Must start from a tab showing about:blank, which is later redirected. |
| old_url == GURL(url::kAboutBlankURL) && |
| // Must be the first real navigation of the tab. |
| render_view_->historyBackListCount() < 1 && |
| render_view_->historyForwardListCount() < 1 && |
| // The parent page must have set the child's window.opener to null before |
| // redirecting to the desired URL. |
| frame->opener() == NULL && |
| // Must be a top-level frame. |
| frame->parent() == NULL && |
| // Must not have issued the request from this page. |
| is_content_initiated && |
| // Must be targeted at the current tab. |
| default_policy == blink::WebNavigationPolicyCurrentTab && |
| // Must be a JavaScript navigation, which appears as "other". |
| type == blink::WebNavigationTypeOther; |
| |
| if (is_fork) { |
| // Open the URL via the browser, not via WebKit. |
| OpenURL(frame, url, Referrer(), default_policy); |
| return blink::WebNavigationPolicyIgnore; |
| } |
| |
| return default_policy; |
| } |
| |
| void RenderFrameImpl::OpenURL(WebFrame* frame, |
| const GURL& url, |
| const Referrer& referrer, |
| WebNavigationPolicy policy) { |
| DCHECK_EQ(frame_, frame); |
| |
| FrameHostMsg_OpenURL_Params params; |
| params.url = url; |
| params.referrer = referrer; |
| params.disposition = RenderViewImpl::NavigationPolicyToDisposition(policy); |
| WebDataSource* ds = frame->provisionalDataSource(); |
| if (ds) { |
| DocumentState* document_state = DocumentState::FromDataSource(ds); |
| NavigationState* navigation_state = document_state->navigation_state(); |
| if (navigation_state->is_content_initiated()) { |
| params.should_replace_current_entry = ds->replacesCurrentHistoryItem(); |
| } else { |
| // This is necessary to preserve the should_replace_current_entry value on |
| // cross-process redirects, in the event it was set by a previous process. |
| // |
| // TODO(davidben): Avoid this awkward duplication of state. See comment on |
| // NavigationState::should_replace_current_entry(). |
| params.should_replace_current_entry = |
| navigation_state->should_replace_current_entry(); |
| } |
| } else { |
| params.should_replace_current_entry = false; |
| } |
| params.user_gesture = WebUserGestureIndicator::isProcessingUserGesture(); |
| if (GetContentClient()->renderer()->AllowPopup()) |
| params.user_gesture = true; |
| |
| if (policy == blink::WebNavigationPolicyNewBackgroundTab || |
| policy == blink::WebNavigationPolicyNewForegroundTab || |
| policy == blink::WebNavigationPolicyNewWindow || |
| policy == blink::WebNavigationPolicyNewPopup) { |
| WebUserGestureIndicator::consumeUserGesture(); |
| } |
| |
| Send(new FrameHostMsg_OpenURL(routing_id_, params)); |
| } |
| |
| void RenderFrameImpl::UpdateEncoding(WebFrame* frame, |
| const std::string& encoding_name) { |
| // Only update main frame's encoding_name. |
| if (!frame->parent()) |
| Send(new FrameHostMsg_UpdateEncoding(routing_id_, encoding_name)); |
| } |
| |
| void RenderFrameImpl::SyncSelectionIfRequired() { |
| base::string16 text; |
| size_t offset; |
| gfx::Range range; |
| #if defined(ENABLE_PLUGINS) |
| if (render_view_->focused_pepper_plugin_) { |
| render_view_->focused_pepper_plugin_->GetSurroundingText(&text, &range); |
| offset = 0; // Pepper API does not support offset reporting. |
| // TODO(kinaba): cut as needed. |
| } else |
| #endif |
| { |
| size_t location, length; |
| if (!GetRenderWidget()->webwidget()->caretOrSelectionRange( |
| &location, &length)) { |
| return; |
| } |
| |
| range = gfx::Range(location, location + length); |
| |
| if (GetRenderWidget()->webwidget()->textInputInfo().type != |
| blink::WebTextInputTypeNone) { |
| // If current focused element is editable, we will send 100 more chars |
| // before and after selection. It is for input method surrounding text |
| // feature. |
| if (location > kExtraCharsBeforeAndAfterSelection) |
| offset = location - kExtraCharsBeforeAndAfterSelection; |
| else |
| offset = 0; |
| length = location + length - offset + kExtraCharsBeforeAndAfterSelection; |
| WebRange webrange = WebRange::fromDocumentRange(frame_, offset, length); |
| if (!webrange.isNull()) |
| text = WebRange::fromDocumentRange( |
| frame_, offset, length).toPlainText(); |
| } else { |
| offset = location; |
| text = frame_->selectionAsText(); |
| // http://crbug.com/101435 |
| // In some case, frame->selectionAsText() returned text's length is not |
| // equal to the length returned from webwidget()->caretOrSelectionRange(). |
| // So we have to set the range according to text.length(). |
| range.set_end(range.start() + text.length()); |
| } |
| } |
| |
| // Sometimes we get repeated didChangeSelection calls from webkit when |
| // the selection hasn't actually changed. We don't want to report these |
| // because it will cause us to continually claim the X clipboard. |
| if (selection_text_offset_ != offset || |
| selection_range_ != range || |
| selection_text_ != text) { |
| selection_text_ = text; |
| selection_text_offset_ = offset; |
| selection_range_ = range; |
| // This IPC is dispatched by RenderWidetHost, so use its routing ID. |
| Send(new ViewHostMsg_SelectionChanged( |
| GetRenderWidget()->routing_id(), text, offset, range)); |
| } |
| GetRenderWidget()->UpdateSelectionBounds(); |
| } |
| |
| bool RenderFrameImpl::InitializeUserMediaClient() { |
| if (web_user_media_client_) |
| return true; |
| |
| if (!RenderThreadImpl::current()) // Will be NULL during unit tests. |
| return false; |
| |
| #if defined(OS_ANDROID) |
| if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableWebRTC)) |
| return false; |
| #endif |
| |
| #if defined(ENABLE_WEBRTC) |
| if (!render_view_->media_stream_dispatcher_) { |
| render_view_->media_stream_dispatcher_ = |
| new MediaStreamDispatcher(render_view_.get()); |
| } |
| |
| MediaStreamImpl* media_stream_impl = new MediaStreamImpl( |
| render_view_.get(), |
| render_view_->media_stream_dispatcher_, |
| RenderThreadImpl::current()->GetPeerConnectionDependencyFactory()); |
| web_user_media_client_ = media_stream_impl; |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| WebMediaPlayer* RenderFrameImpl::CreateWebMediaPlayerForMediaStream( |
| const blink::WebURL& url, |
| WebMediaPlayerClient* client) { |
| #if defined(ENABLE_WEBRTC) |
| #if defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL) |
| bool found_neon = |
| (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; |
| UMA_HISTOGRAM_BOOLEAN("Platform.WebRtcNEONFound", found_neon); |
| #endif // defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL) |
| return new WebMediaPlayerMS(frame_, client, weak_factory_.GetWeakPtr(), |
| new RenderMediaLog(), |
| CreateRendererFactory()); |
| #else |
| return NULL; |
| #endif // defined(ENABLE_WEBRTC) |
| } |
| |
| scoped_ptr<MediaStreamRendererFactory> |
| RenderFrameImpl::CreateRendererFactory() { |
| #if defined(ENABLE_WEBRTC) |
| return scoped_ptr<MediaStreamRendererFactory>( |
| new MediaStreamRendererFactory()); |
| #else |
| return scoped_ptr<MediaStreamRendererFactory>( |
| static_cast<MediaStreamRendererFactory*>(NULL)); |
| #endif |
| } |
| |
| GURL RenderFrameImpl::GetLoadingUrl() const { |
| WebDataSource* ds = frame_->dataSource(); |
| if (ds->hasUnreachableURL()) |
| return ds->unreachableURL(); |
| |
| const WebURLRequest& request = ds->request(); |
| return request.url(); |
| } |
| |
| #if defined(OS_ANDROID) |
| |
| WebMediaPlayer* RenderFrameImpl::CreateAndroidWebMediaPlayer( |
| const blink::WebURL& url, |
| WebMediaPlayerClient* client) { |
| GpuChannelHost* gpu_channel_host = |
| RenderThreadImpl::current()->EstablishGpuChannelSync( |
| CAUSE_FOR_GPU_LAUNCH_VIDEODECODEACCELERATOR_INITIALIZE); |
| if (!gpu_channel_host) { |
| LOG(ERROR) << "Failed to establish GPU channel for media player"; |
| return NULL; |
| } |
| |
| scoped_refptr<StreamTextureFactory> stream_texture_factory; |
| if (SynchronousCompositorFactory* factory = |
| SynchronousCompositorFactory::GetInstance()) { |
| stream_texture_factory = factory->CreateStreamTextureFactory(routing_id_); |
| } else { |
| scoped_refptr<webkit::gpu::ContextProviderWebContext> context_provider = |
| RenderThreadImpl::current()->SharedMainThreadContextProvider(); |
| |
| if (!context_provider.get()) { |
| LOG(ERROR) << "Failed to get context3d for media player"; |
| return NULL; |
| } |
| |
| stream_texture_factory = StreamTextureFactoryImpl::Create( |
| context_provider, gpu_channel_host, routing_id_); |
| } |
| |
| return new WebMediaPlayerAndroid( |
| frame_, |
| client, |
| weak_factory_.GetWeakPtr(), |
| GetMediaPlayerManager(), |
| GetCdmManager(), |
| stream_texture_factory, |
| RenderThreadImpl::current()->GetMediaThreadMessageLoopProxy(), |
| new RenderMediaLog()); |
| } |
| |
| RendererMediaPlayerManager* RenderFrameImpl::GetMediaPlayerManager() { |
| if (!media_player_manager_) { |
| media_player_manager_ = new RendererMediaPlayerManager(this); |
| #if defined(VIDEO_HOLE) |
| render_view_->RegisterVideoHoleFrame(this); |
| #endif // defined(VIDEO_HOLE) |
| } |
| return media_player_manager_; |
| } |
| |
| #endif // defined(OS_ANDROID) |
| |
| #if defined(ENABLE_BROWSER_CDMS) |
| RendererCdmManager* RenderFrameImpl::GetCdmManager() { |
| if (!cdm_manager_) |
| cdm_manager_ = new RendererCdmManager(this); |
| return cdm_manager_; |
| } |
| #endif // defined(ENABLE_BROWSER_CDMS) |
| |
| } // namespace content |