diff options
author | dfalcantara@chromium.org <dfalcantara@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-20 22:32:22 +0000 |
---|---|---|
committer | dfalcantara@chromium.org <dfalcantara@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-20 22:32:22 +0000 |
commit | bf70edce9937b8e22454d687351db8cc456ba518 (patch) | |
tree | e4f35048094aa3f07162b8602ad3c28b0ff01285 | |
parent | f59a805a41a6cb660bec99b266630ffe38987139 (diff) | |
download | chromium_src-bf70edce9937b8e22454d687351db8cc456ba518.zip chromium_src-bf70edce9937b8e22454d687351db8cc456ba518.tar.gz chromium_src-bf70edce9937b8e22454d687351db8cc456ba518.tar.bz2 |
Transfer user agent info between browser and renderer
Upstreaming "Request desktop site" feature from Android. Previous CLs:
* http://codereview.chromium.org/9999010/
Stores the original request URL in NavigationEntry
* http://codereview.chromium.org/10170016/
Adds info about user agent overrides to WebContents and NavigationEntries
* https://bugs.webkit.org/show_bug.cgi?id=83959
Adds ability to override the user agent string per-WebFrameClient
The previous CLs added the framework to the browser and renderer to support
overriding the default user agent with a different string. This CL connects the
two sides, letting the browser pass information about the user agent override the
renderer should be using for a given navigation.
There's a slightly out of date doc at http://go/chrome_android_rds that's being
adjusted as CLs land.
Changes from previous CLs:
* The user agent override string is now stored in the RendererPreferences instead
of directly inside the RenderViewImpl and WebContentsImpl.
* Setting the user agent override for a WebContents forces a reload if a page is
currently being loaded to avoid giving different parts of a web page different
user agents.
Additions:
* The browser-side lets the renderer know about the user agent override string
through an IPC call that occurs when the user agent is set in WebContentsImpl
and when a RenderViewImpl is created.
* Instant + Prefetch classes have been adjusted to set carry over the override flags
from the WebContents they're being used for.
* A new function is added to the NavigationController to allow setting the override
flag for a particular NavigationEntry before it is loaded.
* The NavigationController is alerted to new navigations by the renderer with an IPC
call and saves the override state in the relevant NavigationEntry.
* DocumentState stores whether the user agent override was used for a given navigation,
and is set in RenderViewImpl::didCreateDataSource(). Both browser- and
renderer-initiated navigations go through here, with
browser-initiated navigations using a ViewMsg_Navigate_Params.
* WebFrameClient::userAgentOverride() is overridden by RenderViewImpl to return the
user agent for the current main frame. If the main frame is provisionally loading
something, we use the override for the provisional load instead.
Internal bugs=6272286,6213026
BUG=112923
TEST=
Review URL: https://chromiumcodereview.appspot.com/10450002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@143279 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/instant/instant_loader.cc | 36 | ||||
-rw-r--r-- | chrome/browser/instant/instant_loader.h | 3 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_config.cc | 3 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_config.h | 6 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_contents.cc | 10 | ||||
-rw-r--r-- | content/browser/web_contents/navigation_controller_impl.cc | 34 | ||||
-rw-r--r-- | content/browser/web_contents/navigation_controller_impl.h | 7 | ||||
-rw-r--r-- | content/browser/web_contents/web_contents_impl.cc | 19 | ||||
-rw-r--r-- | content/browser/web_contents/web_contents_impl.h | 4 | ||||
-rw-r--r-- | content/common/view_messages.h | 7 | ||||
-rw-r--r-- | content/public/browser/navigation_controller.h | 10 | ||||
-rw-r--r-- | content/public/common/renderer_preferences.h | 6 | ||||
-rw-r--r-- | content/public/renderer/document_state.cc | 1 | ||||
-rw-r--r-- | content/public/renderer/document_state.h | 8 | ||||
-rw-r--r-- | content/renderer/render_view_impl.cc | 41 | ||||
-rw-r--r-- | content/renderer/render_view_impl.h | 3 |
16 files changed, 172 insertions, 26 deletions
diff --git a/chrome/browser/instant/instant_loader.cc b/chrome/browser/instant/instant_loader.cc index a3667bb..460f343 100644 --- a/chrome/browser/instant/instant_loader.cc +++ b/chrome/browser/instant/instant_loader.cc @@ -702,6 +702,11 @@ bool InstantLoader::Update(TabContents* tab_contents, if (created_preview_contents) CreatePreviewContents(tab_contents); + // Carry over the user agent override setting to the new entry. + content::NavigationEntry* entry = + tab_contents->web_contents()->GetController().GetActiveEntry(); + bool override_user_agent = entry && entry->GetIsOverridingUserAgent(); + if (template_url) { DCHECK(template_url_id_ == template_url->id()); if (!created_preview_contents) { @@ -733,14 +738,17 @@ bool InstantLoader::Update(TabContents* tab_contents, complete_suggested_text_.substr(user_text_.size()); } } else { - LoadInstantURL(template_url, transition_type, user_text_, verbatim); + LoadInstantURL(template_url, transition_type, user_text_, verbatim, + override_user_agent); } } else { DCHECK(template_url_id_ == 0); preview_tab_contents_delegate_->PrepareForNewLoad(); frame_load_observer_.reset(NULL); - preview_contents_->web_contents()->GetController().LoadURL( - url_, content::Referrer(), transition_type, std::string()); + + preview_contents_->web_contents()->GetController(). + LoadURLWithUserAgentOverride(url_, content::Referrer(), transition_type, + false, std::string(), override_user_agent); } return true; } @@ -864,9 +872,14 @@ void InstantLoader::MaybeLoadInstantURL(TabContents* tab_contents, if (preview_contents_.get()) return; + // Carry over the user agent override setting to the new entry. + content::NavigationEntry* entry = + tab_contents->web_contents()->GetController().GetActiveEntry(); + bool override_user_agent = entry && entry->GetIsOverridingUserAgent(); + CreatePreviewContents(tab_contents); LoadInstantURL(template_url, content::PAGE_TRANSITION_GENERATED, string16(), - true); + true, override_user_agent); } bool InstantLoader::IsNavigationPending() const { @@ -1111,6 +1124,11 @@ void InstantLoader::SetupPreviewContents(TabContents* tab_contents) { gfx::Rect tab_bounds; tab_contents->web_contents()->GetView()->GetContainerBounds(&tab_bounds); preview_contents_->web_contents()->GetView()->SizeContents(tab_bounds.size()); + + // Carry over the user agent override string. + const std::string& override = + tab_contents->web_contents()->GetUserAgentOverride(); + preview_contents_->web_contents()->SetUserAgentOverride(override); } void InstantLoader::CreatePreviewContents(TabContents* tab_contents) { @@ -1128,7 +1146,8 @@ void InstantLoader::CreatePreviewContents(TabContents* tab_contents) { void InstantLoader::LoadInstantURL(const TemplateURL* template_url, content::PageTransition transition_type, const string16& user_text, - bool verbatim) { + bool verbatim, + bool override_user_agent) { preview_tab_contents_delegate_->PrepareForNewLoad(); // Load the instant URL. We don't reflect the url we load in url() as @@ -1144,8 +1163,11 @@ void InstantLoader::LoadInstantURL(const TemplateURL* template_url, CommandLine* cl = CommandLine::ForCurrentProcess(); if (cl->HasSwitch(switches::kInstantURL)) instant_url = GURL(cl->GetSwitchValueASCII(switches::kInstantURL)); - preview_contents_->web_contents()->GetController().LoadURL( - instant_url, content::Referrer(), transition_type, std::string()); + + preview_contents_->web_contents()->GetController(). + LoadURLWithUserAgentOverride(instant_url, content::Referrer(), + transition_type, false, std::string(), override_user_agent); + RenderViewHost* host = preview_contents_->web_contents()->GetRenderViewHost(); preview_contents_->web_contents()->HideContents(); diff --git a/chrome/browser/instant/instant_loader.h b/chrome/browser/instant/instant_loader.h index bec2d1a..12b3c85 100644 --- a/chrome/browser/instant/instant_loader.h +++ b/chrome/browser/instant/instant_loader.h @@ -196,7 +196,8 @@ class InstantLoader : public content::NotificationObserver { void LoadInstantURL(const TemplateURL* template_url, content::PageTransition transition_type, const string16& user_text, - bool verbatim); + bool verbatim, + bool override_user_agent); InstantLoaderDelegate* delegate_; diff --git a/chrome/browser/prerender/prerender_config.cc b/chrome/browser/prerender/prerender_config.cc index 936c105..f409918 100644 --- a/chrome/browser/prerender/prerender_config.cc +++ b/chrome/browser/prerender/prerender_config.cc @@ -11,7 +11,8 @@ Config::Config() : max_bytes(100 * 1024 * 1024), rate_limit_enabled(true), max_age(base::TimeDelta::FromSeconds(30)), https_allowed(true), - default_tab_bounds(640, 480) { + default_tab_bounds(640, 480), + is_overriding_user_agent(false) { } } // namespace prerender diff --git a/chrome/browser/prerender/prerender_config.h b/chrome/browser/prerender/prerender_config.h index 46b83a5..97abcbd 100644 --- a/chrome/browser/prerender/prerender_config.h +++ b/chrome/browser/prerender/prerender_config.h @@ -32,6 +32,12 @@ struct Config { // The default tab bounds used as the prerenderer tab size when the active tab // cannot be accessed. gfx::Rect default_tab_bounds; + + // User agent being used as an override for the WebContents. + std::string user_agent_override; + + // Is the user agent being overridden for this NavigationEntry? + bool is_overriding_user_agent; }; } // namespace prerender diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc index ce7e59a..fa9e06d 100644 --- a/chrome/browser/prerender/prerender_contents.cc +++ b/chrome/browser/prerender/prerender_contents.cc @@ -369,12 +369,18 @@ void PrerenderContents::StartPrerendering( DCHECK(load_start_time_.is_null()); load_start_time_ = base::TimeTicks::Now(); - new_contents->GetController().LoadURL( + // Transfer over the user agent override. + new_contents->SetUserAgentOverride( + prerender_manager_->config().user_agent_override); + + new_contents->GetController().LoadURLWithUserAgentOverride( prerender_url_, referrer_, (origin_ == ORIGIN_OMNIBOX ? content::PAGE_TRANSITION_TYPED : content::PAGE_TRANSITION_LINK), - std::string()); + false, + std::string(), + prerender_manager_->config().is_overriding_user_agent); } bool PrerenderContents::GetChildId(int* child_id) const { diff --git a/content/browser/web_contents/navigation_controller_impl.cc b/content/browser/web_contents/navigation_controller_impl.cc index b24eb5b..5dad661 100644 --- a/content/browser/web_contents/navigation_controller_impl.cc +++ b/content/browser/web_contents/navigation_controller_impl.cc @@ -114,6 +114,12 @@ bool AreURLsInPageNavigation(const GURL& existing_url, const GURL& new_url) { new_url.ReplaceComponents(replacements); } +// Determines whether or not we should be carrying over a user agent override +// between two NavigationEntries. +bool ShouldKeepOverride(const content::NavigationEntry* last_entry) { + return last_entry && last_entry->GetIsOverridingUserAgent(); +} + } // namespace // NavigationControllerImpl ---------------------------------------------------- @@ -577,14 +583,9 @@ void NavigationControllerImpl::LoadURL( if (content::HandleDebugURL(url, transition)) return; - // The user initiated a load, we don't need to reload anymore. - needs_reload_ = false; - - NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry( - CreateNavigationEntry( - url, referrer, transition, false, extra_headers, browser_context_)); - - LoadEntry(entry); + bool override = ShouldKeepOverride(GetLastCommittedEntry()); + LoadURLWithUserAgentOverride(url, referrer, transition, false, extra_headers, + override); } void NavigationControllerImpl::LoadURLFromRenderer( @@ -592,12 +593,26 @@ void NavigationControllerImpl::LoadURLFromRenderer( const content::Referrer& referrer, content::PageTransition transition, const std::string& extra_headers) { + bool override = ShouldKeepOverride(GetLastCommittedEntry()); + LoadURLWithUserAgentOverride(url, referrer, transition, true, extra_headers, + override); +} + +void NavigationControllerImpl::LoadURLWithUserAgentOverride( + const GURL& url, + const content::Referrer& referrer, + content::PageTransition transition, + bool is_renderer_initiated, + const std::string& extra_headers, + bool is_overriding_user_agent) { // The user initiated a load, we don't need to reload anymore. needs_reload_ = false; NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry( CreateNavigationEntry( - url, referrer, transition, true, extra_headers, browser_context_)); + url, referrer, transition, is_renderer_initiated, extra_headers, + browser_context_)); + entry->SetIsOverridingUserAgent(is_overriding_user_agent); LoadEntry(entry); } @@ -868,6 +883,7 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage( static_cast<SiteInstanceImpl*>(web_contents_->GetSiteInstance())); new_entry->SetHasPostData(params.is_post); new_entry->SetPostID(params.post_id); + new_entry->SetIsOverridingUserAgent(params.is_overriding_user_agent); if (params.redirects.size() > 0) new_entry->SetOriginalRequestURL(params.redirects[0]); diff --git a/content/browser/web_contents/navigation_controller_impl.h b/content/browser/web_contents/navigation_controller_impl.h index dfe4dd6..5053475 100644 --- a/content/browser/web_contents/navigation_controller_impl.h +++ b/content/browser/web_contents/navigation_controller_impl.h @@ -63,6 +63,13 @@ class CONTENT_EXPORT NavigationControllerImpl const content::Referrer& referrer, content::PageTransition type, const std::string& extra_headers) OVERRIDE; + virtual void LoadURLWithUserAgentOverride( + const GURL& url, + const content::Referrer& referrer, + content::PageTransition type, + bool is_renderer_initiated, + const std::string& extra_headers, + bool is_overriding_user_agent) OVERRIDE; virtual void TransferURL( const GURL& url, const content::Referrer& referrer, diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 8d21408..7cdfb2b 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -227,6 +227,7 @@ void MakeNavigateParams(const NavigationEntryImpl& entry, entry.transferred_global_request_id().child_id; params->transferred_request_request_id = entry.transferred_global_request_id().request_id; + params->is_overriding_user_agent = entry.GetIsOverridingUserAgent(); // Avoid downloading when in view-source mode. params->allow_download = !entry.IsViewSourceMode(); params->embedder_channel_name = embedder_channel_name; @@ -749,11 +750,25 @@ content::WebUI* WebContentsImpl::GetCommittedWebUI() const { } void WebContentsImpl::SetUserAgentOverride(const std::string& override) { - user_agent_override_ = override; + if (GetUserAgentOverride() == override) + return; + + renderer_preferences_.user_agent_override = override; + + // Send the new override string to the renderer. + RenderViewHost* host = GetRenderViewHost(); + if (host) + host->SyncRendererPrefs(); + + // Reload the page if a load is currently in progress to avoid having + // different parts of the page loaded using different user agents. + NavigationEntry* entry = controller_.GetActiveEntry(); + if (is_loading_ && entry != NULL && entry->GetIsOverridingUserAgent()) + controller_.ReloadIgnoringCache(true); } const std::string& WebContentsImpl::GetUserAgentOverride() const { - return user_agent_override_; + return renderer_preferences_.user_agent_override; } const string16& WebContentsImpl::GetTitle() const { diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index cc18b58..4426f1a 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h @@ -665,10 +665,6 @@ class CONTENT_EXPORT WebContentsImpl // is closed. WebContentsImpl* opener_; - // User agent to use if a NavigationEntry requires that the default user agent - // is overridden. - std::string user_agent_override_; - // Helper classes ------------------------------------------------------------ // Manages creation and swapping of render views. diff --git a/content/common/view_messages.h b/content/common/view_messages.h index 5d4d202..8ffd5ba 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -313,6 +313,7 @@ IPC_STRUCT_TRAITS_BEGIN(content::RendererPreferences) IPC_STRUCT_TRAITS_MEMBER(caret_blink_interval) IPC_STRUCT_TRAITS_MEMBER(enable_referrers) IPC_STRUCT_TRAITS_MEMBER(default_zoom_level) + IPC_STRUCT_TRAITS_MEMBER(user_agent_override) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(content::SSLStatus) @@ -452,6 +453,9 @@ IPC_STRUCT_BEGIN_WITH_PARENT(ViewHostMsg_FrameNavigate_Params, // Serialized history item state to store in the navigation entry. IPC_STRUCT_MEMBER(std::string, content_state) + + // User agent override used to navigate. + IPC_STRUCT_MEMBER(bool, is_overriding_user_agent) IPC_STRUCT_END() // This message is used for supporting popup menus on Mac OS X using native @@ -656,6 +660,9 @@ IPC_STRUCT_BEGIN(ViewMsg_Navigate_Params) // guest RenderView. The embedder_channel_name and embedder_container_id // together uniquely identify a browser plugin instance. IPC_STRUCT_MEMBER(int, embedder_container_id) + + // Whether or not the user agent override string should be used. + IPC_STRUCT_MEMBER(bool, is_overriding_user_agent) IPC_STRUCT_END() IPC_STRUCT_BEGIN(ViewMsg_New_Params) diff --git a/content/public/browser/navigation_controller.h b/content/public/browser/navigation_controller.h index c9c1a01..06e7806 100644 --- a/content/public/browser/navigation_controller.h +++ b/content/public/browser/navigation_controller.h @@ -167,6 +167,16 @@ class NavigationController { PageTransition type, const std::string& extra_headers) = 0; + // Same as LoadURL, but allows overriding the user agent of the + // NavigationEntry before it loads. + // TODO(dfalcantara): Consolidate the LoadURL* interfaces. + virtual void LoadURLWithUserAgentOverride(const GURL& url, + const Referrer& referrer, + PageTransition type, + bool is_renderer_initiated, + const std::string& extra_headers, + bool is_overriding_user_agent) = 0; + // Behaves like LoadURL() and LoadURLFromRenderer() but marks the new // navigation as being transferred from one RVH to another. In this case the // browser can recycle the old request once the new renderer wants to diff --git a/content/public/common/renderer_preferences.h b/content/public/common/renderer_preferences.h index ac7e58c..abb3328 100644 --- a/content/public/common/renderer_preferences.h +++ b/content/public/common/renderer_preferences.h @@ -13,6 +13,8 @@ #define CONTENT_PUBLIC_COMMON_RENDERER_PREFERENCES_H_ #pragma once +#include <string> + #include "content/common/content_export.h" #include "third_party/skia/include/core/SkColor.h" @@ -89,6 +91,10 @@ struct CONTENT_EXPORT RendererPreferences { // Default page zoom level. double default_zoom_level; + + // The user agent given to WebKit when it requests one and the user agent is + // being overridden for the current navigation. + std::string user_agent_override; }; } // namespace content diff --git a/content/public/renderer/document_state.cc b/content/public/renderer/document_state.cc index 1fcb604..9e71d7b 100644 --- a/content/public/renderer/document_state.cc +++ b/content/public/renderer/document_state.cc @@ -19,6 +19,7 @@ DocumentState::DocumentState() was_alternate_protocol_available_(false), was_fetched_via_proxy_(false), use_error_page_(false), + is_overriding_user_agent_(false), was_prefetcher_(false), was_referred_by_prefetcher_(false), load_type_(UNDEFINED_LOAD), diff --git a/content/public/renderer/document_state.h b/content/public/renderer/document_state.h index d39a893..94d10b4 100644 --- a/content/public/renderer/document_state.h +++ b/content/public/renderer/document_state.h @@ -192,6 +192,12 @@ class DocumentState : public WebKit::WebDataSource::ExtraData { use_error_page_ = use_error_page; } + // True if the user agent was overridden for this page. + bool is_overriding_user_agent() const { return is_overriding_user_agent_; } + void set_is_overriding_user_agent(bool state) { + is_overriding_user_agent_ = state; + } + void set_was_prefetcher(bool value) { was_prefetcher_ = value; } bool was_prefetcher() const { return was_prefetcher_; } @@ -273,6 +279,8 @@ class DocumentState : public WebKit::WebDataSource::ExtraData { bool use_error_page_; + bool is_overriding_user_agent_; + // A prefetcher is a page that contains link rel=prefetch elements. bool was_prefetcher_; bool was_referred_by_prefetcher_; diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index 16837b9..8236194 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -1451,6 +1451,10 @@ void RenderViewImpl::UpdateURL(WebFrame* frame) { params.post_id = ExtractPostId(item); } + // Send the user agent override back. + params.is_overriding_user_agent = + document_state->is_overriding_user_agent(); + // Save some histogram data so we can compute the average memory used per // page load of the glyphs. UMA_HISTOGRAM_COUNTS_10000("Memory.GlyphPagesPerLoad", @@ -2736,6 +2740,17 @@ void RenderViewImpl::didCreateDataSource(WebFrame* frame, WebDataSource* ds) { PopulateDocumentStateFromPending(document_state); } + // Carry over the user agent override flag, if it exists. + if (content_initiated && webview() && webview()->mainFrame() && + webview()->mainFrame()->dataSource()) { + DocumentState* old_document_state = + DocumentState::FromDataSource(webview()->mainFrame()->dataSource()); + if (old_document_state) { + document_state->set_is_overriding_user_agent( + old_document_state->is_overriding_user_agent()); + } + } + // The rest of RenderView assumes that a WebDataSource will always have a // non-null NavigationState. if (content_initiated) @@ -2819,6 +2834,7 @@ void RenderViewImpl::PopulateDocumentStateFromPending( document_state->set_load_type(DocumentState::NORMAL_LOAD); document_state->set_referrer_policy(params.referrer.policy); + document_state->set_is_overriding_user_agent(params.is_overriding_user_agent); } NavigationState* RenderViewImpl::CreateNavigationStateFromPending() { @@ -3714,6 +3730,31 @@ void RenderViewImpl::willOpenSocketStream( SocketStreamHandleData::AddToHandle(handle, routing_id_); } +WebKit::WebString RenderViewImpl::userAgentOverride( + WebKit::WebFrame* frame, + const WebKit::WebURL& url) { + if (!webview() || !webview()->mainFrame() || + renderer_preferences_.user_agent_override.empty()) { + return WebKit::WebString(); + } + + // If we're in the middle of committing a load, the data source we need + // will still be provisional. + WebFrame* main_frame = webview()->mainFrame(); + WebDataSource* data_source = NULL; + if (main_frame->provisionalDataSource()) + data_source = main_frame->provisionalDataSource(); + else + data_source = main_frame->dataSource(); + + DocumentState* document_state = + data_source ? DocumentState::FromDataSource(data_source) : NULL; + if (document_state && document_state->is_overriding_user_agent()) + return WebString::fromUTF8(renderer_preferences_.user_agent_override); + else + return WebKit::WebString(); +} + // WebKit::WebPageSerializerClient implementation ------------------------------ void RenderViewImpl::didSerializeDataForFrame( diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index 8468942..f8daf58 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -651,6 +651,9 @@ class RenderViewImpl : public RenderWidget, WebKit::WebFrame* source, WebKit::WebSecurityOrigin targetOrigin, WebKit::WebDOMMessageEvent event) OVERRIDE; + virtual WebKit::WebString userAgentOverride( + WebKit::WebFrame* frame, + const WebKit::WebURL& url) OVERRIDE; // WebKit::WebPageSerializerClient implementation ---------------------------- |