chrome_content_renderer_client.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/renderer/chrome_content_renderer_client.h"
6
7#include "base/command_line.h"
8#include "base/debug/crash_logging.h"
9#include "base/logging.h"
10#include "base/metrics/field_trial.h"
11#include "base/metrics/histogram.h"
12#include "base/metrics/user_metrics_action.h"
13#include "base/path_service.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/strings/string_util.h"
16#include "base/strings/utf_string_conversions.h"
17#include "base/values.h"
18#include "chrome/common/chrome_content_client.h"
19#include "chrome/common/chrome_paths.h"
20#include "chrome/common/chrome_switches.h"
21#include "chrome/common/chrome_version_info.h"
22#include "chrome/common/content_settings_pattern.h"
23#include "chrome/common/crash_keys.h"
24#include "chrome/common/extensions/chrome_extensions_client.h"
25#include "chrome/common/extensions/extension_constants.h"
26#include "chrome/common/extensions/extension_process_policy.h"
27#include "chrome/common/localized_error.h"
28#include "chrome/common/pepper_permission_util.h"
29#include "chrome/common/render_messages.h"
30#include "chrome/common/url_constants.h"
31#include "chrome/renderer/benchmarking_extension.h"
32#include "chrome/renderer/chrome_render_frame_observer.h"
33#include "chrome/renderer/chrome_render_process_observer.h"
34#include "chrome/renderer/chrome_render_view_observer.h"
35#include "chrome/renderer/content_settings_observer.h"
36#include "chrome/renderer/extensions/chrome_extension_helper.h"
37#include "chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.h"
38#include "chrome/renderer/extensions/chrome_extensions_renderer_client.h"
39#include "chrome/renderer/extensions/extension_frame_helper.h"
40#include "chrome/renderer/extensions/renderer_permissions_policy_delegate.h"
41#include "chrome/renderer/extensions/resource_request_policy.h"
42#include "chrome/renderer/external_extension.h"
43#include "chrome/renderer/loadtimes_extension_bindings.h"
44#include "chrome/renderer/media/cast_ipc_dispatcher.h"
45#include "chrome/renderer/media/chrome_key_systems.h"
46#include "chrome/renderer/net/net_error_helper.h"
47#include "chrome/renderer/net/prescient_networking_dispatcher.h"
48#include "chrome/renderer/net/renderer_net_predictor.h"
49#include "chrome/renderer/net_benchmarking_extension.h"
50#include "chrome/renderer/page_load_histograms.h"
51#include "chrome/renderer/pepper/pepper_helper.h"
52#include "chrome/renderer/pepper/ppb_pdf_impl.h"
53#include "chrome/renderer/playback_extension.h"
54#include "chrome/renderer/plugins/chrome_plugin_placeholder.h"
55#include "chrome/renderer/plugins/plugin_uma.h"
56#include "chrome/renderer/prefetch_helper.h"
57#include "chrome/renderer/prerender/prerender_dispatcher.h"
58#include "chrome/renderer/prerender/prerender_helper.h"
59#include "chrome/renderer/prerender/prerender_media_load_deferrer.h"
60#include "chrome/renderer/prerender/prerenderer_client.h"
61#include "chrome/renderer/principals_extension_bindings.h"
62#include "chrome/renderer/printing/print_web_view_helper.h"
63#include "chrome/renderer/safe_browsing/malware_dom_details.h"
64#include "chrome/renderer/safe_browsing/phishing_classifier_delegate.h"
65#include "chrome/renderer/searchbox/search_bouncer.h"
66#include "chrome/renderer/searchbox/searchbox.h"
67#include "chrome/renderer/searchbox/searchbox_extension.h"
68#include "chrome/renderer/tts_dispatcher.h"
69#include "chrome/renderer/worker_permission_client_proxy.h"
70#include "components/autofill/content/renderer/autofill_agent.h"
71#include "components/autofill/content/renderer/password_autofill_agent.h"
72#include "components/autofill/content/renderer/password_generation_agent.h"
73#include "components/dom_distiller/core/url_constants.h"
74#include "components/nacl/renderer/ppb_nacl_private_impl.h"
75#include "components/plugins/renderer/mobile_youtube_plugin.h"
76#include "components/signin/core/common/profile_management_switches.h"
77#include "components/visitedlink/renderer/visitedlink_slave.h"
78#include "content/public/common/content_constants.h"
79#include "content/public/renderer/render_frame.h"
80#include "content/public/renderer/render_thread.h"
81#include "content/public/renderer/render_view.h"
82#include "content/public/renderer/render_view_visitor.h"
83#include "extensions/common/constants.h"
84#include "extensions/common/extension.h"
85#include "extensions/common/extension_set.h"
86#include "extensions/common/extension_urls.h"
87#include "extensions/common/switches.h"
88#include "extensions/renderer/dispatcher.h"
89#include "extensions/renderer/extension_helper.h"
90#include "extensions/renderer/script_context.h"
91#include "grit/generated_resources.h"
92#include "grit/locale_settings.h"
93#include "grit/renderer_resources.h"
94#include "ipc/ipc_sync_channel.h"
95#include "net/base/net_errors.h"
96#include "ppapi/c/private/ppb_nacl_private.h"
97#include "ppapi/c/private/ppb_pdf.h"
98#include "ppapi/shared_impl/ppapi_switches.h"
99#include "third_party/WebKit/public/platform/WebURL.h"
100#include "third_party/WebKit/public/platform/WebURLError.h"
101#include "third_party/WebKit/public/platform/WebURLRequest.h"
102#include "third_party/WebKit/public/web/WebCache.h"
103#include "third_party/WebKit/public/web/WebDataSource.h"
104#include "third_party/WebKit/public/web/WebDocument.h"
105#include "third_party/WebKit/public/web/WebElement.h"
106#include "third_party/WebKit/public/web/WebLocalFrame.h"
107#include "third_party/WebKit/public/web/WebPluginContainer.h"
108#include "third_party/WebKit/public/web/WebPluginParams.h"
109#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
110#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
111#include "ui/base/l10n/l10n_util.h"
112#include "ui/base/layout.h"
113#include "ui/base/resource/resource_bundle.h"
114#include "ui/base/webui/jstemplate_builder.h"
115#include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.
116
117#if !defined(DISABLE_NACL)
118#include "components/nacl/renderer/nacl_helper.h"
119#endif
120
121#if defined(ENABLE_EXTENSIONS)
122#include "chrome/renderer/extensions/chrome_extensions_render_frame_observer.h"
123#endif
124
125#if defined(ENABLE_SPELLCHECK)
126#include "chrome/renderer/spellchecker/spellcheck.h"
127#include "chrome/renderer/spellchecker/spellcheck_provider.h"
128#endif
129
130#if defined(ENABLE_WEBRTC)
131#include "chrome/renderer/media/webrtc_logging_message_filter.h"
132#endif
133
134#if defined(OS_WIN)
135#include "chrome_elf/blacklist/blacklist.h"
136#endif
137
138using autofill::AutofillAgent;
139using autofill::PasswordAutofillAgent;
140using autofill::PasswordGenerationAgent;
141using base::ASCIIToUTF16;
142using base::UserMetricsAction;
143using content::RenderThread;
144using content::WebPluginInfo;
145using extensions::Extension;
146using blink::WebCache;
147using blink::WebConsoleMessage;
148using blink::WebDataSource;
149using blink::WebDocument;
150using blink::WebFrame;
151using blink::WebLocalFrame;
152using blink::WebPlugin;
153using blink::WebPluginParams;
154using blink::WebSecurityOrigin;
155using blink::WebSecurityPolicy;
156using blink::WebString;
157using blink::WebURL;
158using blink::WebURLError;
159using blink::WebURLRequest;
160using blink::WebURLResponse;
161using blink::WebVector;
162
163namespace {
164
165ChromeContentRendererClient* g_current_client;
166
167#if defined(ENABLE_PLUGINS)
168const char* const kPredefinedAllowedCompositorOrigins[] = {
169  "6EAED1924DB611B6EEF2A664BD077BE7EAD33B8F",  // see crbug.com/383937
170  "4EB74897CB187C7633357C2FE832E0AD6A44883A"   // see crbug.com/383937
171};
172
173const char* const kPredefinedAllowedVideoDecodeOrigins[] = {
174  "6EAED1924DB611B6EEF2A664BD077BE7EAD33B8F",  // see crbug.com/383937
175  "4EB74897CB187C7633357C2FE832E0AD6A44883A"   // see crbug.com/383937
176};
177#endif
178
179static void AppendParams(const std::vector<base::string16>& additional_names,
180                         const std::vector<base::string16>& additional_values,
181                         WebVector<WebString>* existing_names,
182                         WebVector<WebString>* existing_values) {
183  DCHECK(additional_names.size() == additional_values.size());
184  DCHECK(existing_names->size() == existing_values->size());
185
186  size_t existing_size = existing_names->size();
187  size_t total_size = existing_size + additional_names.size();
188
189  WebVector<WebString> names(total_size);
190  WebVector<WebString> values(total_size);
191
192  for (size_t i = 0; i < existing_size; ++i) {
193    names[i] = (*existing_names)[i];
194    values[i] = (*existing_values)[i];
195  }
196
197  for (size_t i = 0; i < additional_names.size(); ++i) {
198    names[existing_size + i] = additional_names[i];
199    values[existing_size + i] = additional_values[i];
200  }
201
202  existing_names->swap(names);
203  existing_values->swap(values);
204}
205
206#if defined(ENABLE_SPELLCHECK)
207class SpellCheckReplacer : public content::RenderViewVisitor {
208 public:
209  explicit SpellCheckReplacer(SpellCheck* spellcheck)
210      : spellcheck_(spellcheck) {}
211  virtual bool Visit(content::RenderView* render_view) OVERRIDE;
212
213 private:
214  SpellCheck* spellcheck_;  // New shared spellcheck for all views. Weak Ptr.
215  DISALLOW_COPY_AND_ASSIGN(SpellCheckReplacer);
216};
217
218bool SpellCheckReplacer::Visit(content::RenderView* render_view) {
219  SpellCheckProvider* provider = SpellCheckProvider::Get(render_view);
220  DCHECK(provider);
221  provider->set_spellcheck(spellcheck_);
222  return true;
223}
224#endif
225
226// For certain sandboxed Pepper plugins, use the JavaScript Content Settings.
227bool ShouldUseJavaScriptSettingForPlugin(const WebPluginInfo& plugin) {
228  if (plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS &&
229      plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS) {
230    return false;
231  }
232
233  // Treat Native Client invocations like JavaScript.
234  if (plugin.name == ASCIIToUTF16(ChromeContentClient::kNaClPluginName))
235    return true;
236
237#if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
238  // Treat CDM invocations like JavaScript.
239  if (plugin.name == ASCIIToUTF16(kWidevineCdmDisplayName)) {
240    DCHECK(plugin.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS);
241    return true;
242  }
243#endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
244
245  return false;
246}
247
248}  // namespace
249
250ChromeContentRendererClient::ChromeContentRendererClient() {
251  g_current_client = this;
252
253  extensions::ExtensionsClient::Set(
254      extensions::ChromeExtensionsClient::GetInstance());
255  extensions::ExtensionsRendererClient::Set(
256      ChromeExtensionsRendererClient::GetInstance());
257#if defined(ENABLE_PLUGINS)
258  for (size_t i = 0; i < arraysize(kPredefinedAllowedCompositorOrigins); ++i)
259    allowed_compositor_origins_.insert(kPredefinedAllowedCompositorOrigins[i]);
260  for (size_t i = 0; i < arraysize(kPredefinedAllowedVideoDecodeOrigins); ++i)
261    allowed_video_decode_origins_.insert(
262        kPredefinedAllowedVideoDecodeOrigins[i]);
263#endif
264}
265
266ChromeContentRendererClient::~ChromeContentRendererClient() {
267  g_current_client = NULL;
268}
269
270void ChromeContentRendererClient::RenderThreadStarted() {
271  RenderThread* thread = RenderThread::Get();
272
273  chrome_observer_.reset(new ChromeRenderProcessObserver(this));
274
275  extension_dispatcher_delegate_.reset(
276      new ChromeExtensionsDispatcherDelegate());
277  // ChromeRenderViewTest::SetUp() creates its own ExtensionDispatcher and
278  // injects it using SetExtensionDispatcher(). Don't overwrite it.
279  if (!extension_dispatcher_) {
280    extension_dispatcher_.reset(
281        new extensions::Dispatcher(extension_dispatcher_delegate_.get()));
282  }
283  permissions_policy_delegate_.reset(
284      new extensions::RendererPermissionsPolicyDelegate(
285          extension_dispatcher_.get()));
286  prescient_networking_dispatcher_.reset(new PrescientNetworkingDispatcher());
287  net_predictor_.reset(new RendererNetPredictor());
288#if defined(ENABLE_SPELLCHECK)
289  // ChromeRenderViewTest::SetUp() creates a Spellcheck and injects it using
290  // SetSpellcheck(). Don't overwrite it.
291  if (!spellcheck_) {
292    spellcheck_.reset(new SpellCheck());
293    thread->AddObserver(spellcheck_.get());
294  }
295#endif
296  visited_link_slave_.reset(new visitedlink::VisitedLinkSlave());
297#if defined(FULL_SAFE_BROWSING)
298  phishing_classifier_.reset(safe_browsing::PhishingClassifierFilter::Create());
299#endif
300  prerender_dispatcher_.reset(new prerender::PrerenderDispatcher());
301#if defined(ENABLE_WEBRTC)
302  webrtc_logging_message_filter_ = new WebRtcLoggingMessageFilter(
303      content::RenderThread::Get()->GetIOMessageLoopProxy());
304#endif
305  search_bouncer_.reset(new SearchBouncer());
306
307  thread->AddObserver(chrome_observer_.get());
308  thread->AddObserver(extension_dispatcher_.get());
309#if defined(FULL_SAFE_BROWSING)
310  thread->AddObserver(phishing_classifier_.get());
311#endif
312  thread->AddObserver(visited_link_slave_.get());
313  thread->AddObserver(prerender_dispatcher_.get());
314  thread->AddObserver(search_bouncer_.get());
315
316#if defined(ENABLE_WEBRTC)
317  thread->AddFilter(webrtc_logging_message_filter_.get());
318#endif
319  thread->AddFilter(new CastIPCDispatcher(
320      content::RenderThread::Get()->GetIOMessageLoopProxy()));
321
322  thread->RegisterExtension(extensions_v8::ExternalExtension::Get());
323  thread->RegisterExtension(extensions_v8::LoadTimesExtension::Get());
324
325  CommandLine* command_line = CommandLine::ForCurrentProcess();
326  if (command_line->HasSwitch(switches::kEnableBenchmarking))
327    thread->RegisterExtension(extensions_v8::BenchmarkingExtension::Get());
328  if (command_line->HasSwitch(switches::kEnableNetBenchmarking))
329    thread->RegisterExtension(extensions_v8::NetBenchmarkingExtension::Get());
330  if (command_line->HasSwitch(switches::kInstantProcess))
331    thread->RegisterExtension(extensions_v8::SearchBoxExtension::Get());
332
333  if (command_line->HasSwitch(switches::kPlaybackMode) ||
334      command_line->HasSwitch(switches::kRecordMode)) {
335    thread->RegisterExtension(extensions_v8::PlaybackExtension::Get());
336  }
337
338  // TODO(guohui): needs to forward the new-profile-management switch to
339  // renderer processes.
340  if (switches::IsEnableAccountConsistency())
341    thread->RegisterExtension(extensions_v8::PrincipalsExtension::Get());
342
343  // chrome:, chrome-search:, chrome-devtools:, and chrome-distiller: pages
344  // should not be accessible by normal content, and should also be unable to
345  // script anything but themselves (to help limit the damage that a corrupt
346  // page could cause).
347  WebString chrome_ui_scheme(ASCIIToUTF16(content::kChromeUIScheme));
348  WebSecurityPolicy::registerURLSchemeAsDisplayIsolated(chrome_ui_scheme);
349
350  WebString chrome_search_scheme(ASCIIToUTF16(chrome::kChromeSearchScheme));
351  // The Instant process can only display the content but not read it.  Other
352  // processes can't display it or read it.
353  if (!command_line->HasSwitch(switches::kInstantProcess))
354    WebSecurityPolicy::registerURLSchemeAsDisplayIsolated(chrome_search_scheme);
355
356  WebString dev_tools_scheme(ASCIIToUTF16(content::kChromeDevToolsScheme));
357  WebSecurityPolicy::registerURLSchemeAsDisplayIsolated(dev_tools_scheme);
358
359  WebString dom_distiller_scheme(
360      ASCIIToUTF16(dom_distiller::kDomDistillerScheme));
361  // TODO(nyquist): Add test to ensure this happens when the flag is set.
362  WebSecurityPolicy::registerURLSchemeAsDisplayIsolated(dom_distiller_scheme);
363
364#if defined(OS_CHROMEOS)
365  WebString drive_scheme(ASCIIToUTF16(chrome::kDriveScheme));
366  WebSecurityPolicy::registerURLSchemeAsLocal(drive_scheme);
367#endif
368
369  // chrome: and chrome-search: pages should not be accessible by bookmarklets
370  // or javascript: URLs typed in the omnibox.
371  WebSecurityPolicy::registerURLSchemeAsNotAllowingJavascriptURLs(
372      chrome_ui_scheme);
373  WebSecurityPolicy::registerURLSchemeAsNotAllowingJavascriptURLs(
374      chrome_search_scheme);
375
376  // chrome:, chrome-search:, and chrome-extension: resources shouldn't trigger
377  // insecure content warnings.
378  WebSecurityPolicy::registerURLSchemeAsSecure(chrome_ui_scheme);
379  WebSecurityPolicy::registerURLSchemeAsSecure(chrome_search_scheme);
380
381  WebString extension_scheme(ASCIIToUTF16(extensions::kExtensionScheme));
382  WebSecurityPolicy::registerURLSchemeAsSecure(extension_scheme);
383
384  // chrome-extension: resources should be allowed to receive CORS requests.
385  WebSecurityPolicy::registerURLSchemeAsCORSEnabled(extension_scheme);
386
387  WebString extension_resource_scheme(
388      ASCIIToUTF16(extensions::kExtensionResourceScheme));
389  WebSecurityPolicy::registerURLSchemeAsSecure(extension_resource_scheme);
390
391  // chrome-extension-resource: resources should be allowed to receive CORS
392  // requests.
393  WebSecurityPolicy::registerURLSchemeAsCORSEnabled(extension_resource_scheme);
394
395  // chrome-extension: resources should bypass Content Security Policy checks
396  // when included in protected resources.
397  WebSecurityPolicy::registerURLSchemeAsBypassingContentSecurityPolicy(
398      extension_scheme);
399  WebSecurityPolicy::registerURLSchemeAsBypassingContentSecurityPolicy(
400      extension_resource_scheme);
401
402#if defined(OS_WIN)
403  // Report if the renderer process has been patched by chrome_elf.
404  // TODO(csharp): Remove once the renderer is no longer getting
405  // patched this way.
406  if (blacklist::IsBlacklistInitialized())
407    UMA_HISTOGRAM_BOOLEAN("Blacklist.PatchedInRenderer", true);
408#endif
409}
410
411void ChromeContentRendererClient::RenderFrameCreated(
412    content::RenderFrame* render_frame) {
413  new ChromeRenderFrameObserver(render_frame);
414
415  ContentSettingsObserver* content_settings =
416      new ContentSettingsObserver(render_frame, extension_dispatcher_.get());
417  if (chrome_observer_.get()) {
418    content_settings->SetContentSettingRules(
419        chrome_observer_->content_setting_rules());
420  }
421
422#if defined(ENABLE_EXTENSIONS)
423  new extensions::ChromeExtensionsRenderFrameObserver(render_frame);
424#endif
425  new extensions::ExtensionFrameHelper(render_frame,
426                                       extension_dispatcher_.get());
427
428#if defined(ENABLE_PLUGINS)
429  new PepperHelper(render_frame);
430#endif
431
432#if !defined(DISABLE_NACL)
433  new nacl::NaClHelper(render_frame);
434#endif
435
436  // TODO(jam): when the frame tree moves into content and parent() works at
437  // RenderFrame construction, simplify this by just checking parent().
438  if (render_frame->GetRenderView()->GetMainRenderFrame() != render_frame) {
439    // Avoid any race conditions from having the browser tell subframes that
440    // they're prerendering.
441    if (prerender::PrerenderHelper::IsPrerendering(
442            render_frame->GetRenderView()->GetMainRenderFrame())) {
443      new prerender::PrerenderHelper(render_frame);
444    }
445  }
446
447  if (render_frame->GetRenderView()->GetMainRenderFrame() == render_frame) {
448    // Only attach NetErrorHelper to the main frame, since only the main frame
449    // should get error pages.
450    // PrefetchHelper is also needed only for main frames.
451    new NetErrorHelper(render_frame);
452    new prefetch::PrefetchHelper(render_frame);
453  }
454}
455
456void ChromeContentRendererClient::RenderViewCreated(
457    content::RenderView* render_view) {
458  new extensions::ExtensionHelper(render_view, extension_dispatcher_.get());
459  new extensions::ChromeExtensionHelper(render_view);
460  extension_dispatcher_->OnRenderViewCreated(render_view);
461  new PageLoadHistograms(render_view);
462#if defined(ENABLE_PRINTING)
463  new printing::PrintWebViewHelper(render_view);
464#endif
465#if defined(ENABLE_SPELLCHECK)
466  new SpellCheckProvider(render_view, spellcheck_.get());
467#endif
468  new prerender::PrerendererClient(render_view);
469#if defined(FULL_SAFE_BROWSING)
470  safe_browsing::MalwareDOMDetails::Create(render_view);
471#endif
472
473  PasswordGenerationAgent* password_generation_agent =
474      new PasswordGenerationAgent(render_view);
475  PasswordAutofillAgent* password_autofill_agent =
476      new PasswordAutofillAgent(render_view);
477  new AutofillAgent(render_view,
478                    password_autofill_agent,
479                    password_generation_agent);
480
481  CommandLine* command_line = CommandLine::ForCurrentProcess();
482  if (command_line->HasSwitch(switches::kInstantProcess))
483    new SearchBox(render_view);
484
485  new ChromeRenderViewObserver(render_view, chrome_observer_.get());
486}
487
488void ChromeContentRendererClient::SetNumberOfViews(int number_of_views) {
489  base::debug::SetCrashKeyValue(crash_keys::kNumberOfViews,
490                                base::IntToString(number_of_views));
491}
492
493SkBitmap* ChromeContentRendererClient::GetSadPluginBitmap() {
494  return const_cast<SkBitmap*>(ResourceBundle::GetSharedInstance().
495      GetImageNamed(IDR_SAD_PLUGIN).ToSkBitmap());
496}
497
498SkBitmap* ChromeContentRendererClient::GetSadWebViewBitmap() {
499  return const_cast<SkBitmap*>(ResourceBundle::GetSharedInstance().
500      GetImageNamed(IDR_SAD_WEBVIEW).ToSkBitmap());
501}
502
503std::string ChromeContentRendererClient::GetDefaultEncoding() {
504  return l10n_util::GetStringUTF8(IDS_DEFAULT_ENCODING);
505}
506
507const Extension* ChromeContentRendererClient::GetExtensionByOrigin(
508    const WebSecurityOrigin& origin) const {
509  if (!EqualsASCII(origin.protocol(), extensions::kExtensionScheme))
510    return NULL;
511
512  const std::string extension_id = origin.host().utf8().data();
513  return extension_dispatcher_->extensions()->GetByID(extension_id);
514}
515
516bool ChromeContentRendererClient::OverrideCreatePlugin(
517    content::RenderFrame* render_frame,
518    WebLocalFrame* frame,
519    const WebPluginParams& params,
520    WebPlugin** plugin) {
521  std::string orig_mime_type = params.mimeType.utf8();
522  if (orig_mime_type == content::kBrowserPluginMimeType) {
523    WebDocument document = frame->document();
524    const Extension* extension =
525        GetExtensionByOrigin(document.securityOrigin());
526    if (extension) {
527      const extensions::APIPermission::ID perms[] = {
528          extensions::APIPermission::kAppView,
529          extensions::APIPermission::kEmbeddedExtensionOptions,
530          extensions::APIPermission::kWebView,
531      };
532      for (size_t i = 0; i < arraysize(perms); ++i) {
533        if (extension->permissions_data()->HasAPIPermission(perms[i]))
534          return false;
535      }
536    }
537  }
538
539  ChromeViewHostMsg_GetPluginInfo_Output output;
540#if defined(ENABLE_PLUGINS)
541  render_frame->Send(new ChromeViewHostMsg_GetPluginInfo(
542      render_frame->GetRoutingID(), GURL(params.url),
543      frame->top()->document().url(), orig_mime_type, &output));
544
545  if (output.plugin.type == content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN)
546    return false;
547#else
548  output.status.value = ChromeViewHostMsg_GetPluginInfo_Status::kNotFound;
549#endif
550  *plugin = CreatePlugin(render_frame, frame, params, output);
551  return true;
552}
553
554WebPlugin* ChromeContentRendererClient::CreatePluginReplacement(
555    content::RenderFrame* render_frame,
556    const base::FilePath& plugin_path) {
557  ChromePluginPlaceholder* placeholder =
558      ChromePluginPlaceholder::CreateErrorPlugin(render_frame, plugin_path);
559  return placeholder->plugin();
560}
561
562void ChromeContentRendererClient::DeferMediaLoad(
563    content::RenderFrame* render_frame,
564    const base::Closure& closure) {
565#if defined(OS_ANDROID)
566  // Chromium for Android doesn't support prerender yet.
567  closure.Run();
568  return;
569#else
570  if (!prerender::PrerenderHelper::IsPrerendering(render_frame)) {
571    closure.Run();
572    return;
573  }
574
575  // Lifetime is tied to |render_frame| via content::RenderFrameObserver.
576  new prerender::PrerenderMediaLoadDeferrer(render_frame, closure);
577#endif
578}
579
580WebPlugin* ChromeContentRendererClient::CreatePlugin(
581    content::RenderFrame* render_frame,
582    WebLocalFrame* frame,
583    const WebPluginParams& original_params,
584    const ChromeViewHostMsg_GetPluginInfo_Output& output) {
585  const ChromeViewHostMsg_GetPluginInfo_Status& status = output.status;
586  const WebPluginInfo& plugin = output.plugin;
587  const std::string& actual_mime_type = output.actual_mime_type;
588  const base::string16& group_name = output.group_name;
589  const std::string& identifier = output.group_identifier;
590  ChromeViewHostMsg_GetPluginInfo_Status::Value status_value = status.value;
591  GURL url(original_params.url);
592  std::string orig_mime_type = original_params.mimeType.utf8();
593  ChromePluginPlaceholder* placeholder = NULL;
594
595  // If the browser plugin is to be enabled, this should be handled by the
596  // renderer, so the code won't reach here due to the early exit in
597  // OverrideCreatePlugin.
598  if (status_value == ChromeViewHostMsg_GetPluginInfo_Status::kNotFound ||
599      orig_mime_type == content::kBrowserPluginMimeType) {
600#if defined(OS_ANDROID)
601    if (plugins::MobileYouTubePlugin::IsYouTubeURL(url, orig_mime_type)) {
602      base::StringPiece template_html(
603          ResourceBundle::GetSharedInstance().GetRawDataResource(
604              IDR_MOBILE_YOUTUBE_PLUGIN_HTML));
605      return (new plugins::MobileYouTubePlugin(
606                  render_frame,
607                  frame,
608                  original_params,
609                  template_html,
610                  GURL(ChromePluginPlaceholder::kPluginPlaceholderDataURL)))
611          ->plugin();
612    }
613#endif
614    PluginUMAReporter::GetInstance()->ReportPluginMissing(orig_mime_type, url);
615    placeholder = ChromePluginPlaceholder::CreateMissingPlugin(
616        render_frame, frame, original_params);
617  } else {
618    // TODO(bauerb): This should be in content/.
619    WebPluginParams params(original_params);
620    for (size_t i = 0; i < plugin.mime_types.size(); ++i) {
621      if (plugin.mime_types[i].mime_type == actual_mime_type) {
622        AppendParams(plugin.mime_types[i].additional_param_names,
623                     plugin.mime_types[i].additional_param_values,
624                     &params.attributeNames,
625                     &params.attributeValues);
626        break;
627      }
628    }
629    if (params.mimeType.isNull() && (actual_mime_type.size() > 0)) {
630      // Webkit might say that mime type is null while we already know the
631      // actual mime type via ChromeViewHostMsg_GetPluginInfo. In that case
632      // we should use what we know since WebpluginDelegateProxy does some
633      // specific initializations based on this information.
634      params.mimeType = WebString::fromUTF8(actual_mime_type.c_str());
635    }
636
637    ContentSettingsObserver* observer =
638        ContentSettingsObserver::Get(render_frame);
639
640    const ContentSettingsType content_type =
641        ShouldUseJavaScriptSettingForPlugin(plugin) ?
642            CONTENT_SETTINGS_TYPE_JAVASCRIPT :
643            CONTENT_SETTINGS_TYPE_PLUGINS;
644
645    if ((status_value ==
646             ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized ||
647         status_value == ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay ||
648         status_value == ChromeViewHostMsg_GetPluginInfo_Status::kBlocked) &&
649        observer->IsPluginTemporarilyAllowed(identifier)) {
650      status_value = ChromeViewHostMsg_GetPluginInfo_Status::kAllowed;
651    }
652
653    // Allow full-page plug-ins for click-to-play.
654    if (status_value == ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay &&
655        !frame->parent() &&
656        !frame->opener() &&
657        frame->document().isPluginDocument()) {
658      status_value = ChromeViewHostMsg_GetPluginInfo_Status::kAllowed;
659    }
660
661#if defined(OS_WIN)
662    // In Windows we need to check if we can load NPAPI plugins.
663    // For example, if the render view is in the Ash desktop, we should not.
664    if (status_value == ChromeViewHostMsg_GetPluginInfo_Status::kAllowed &&
665        plugin.type == content::WebPluginInfo::PLUGIN_TYPE_NPAPI) {
666        if (observer->AreNPAPIPluginsBlocked())
667          status_value =
668              ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
669    }
670#endif
671
672    switch (status_value) {
673      case ChromeViewHostMsg_GetPluginInfo_Status::kNotFound: {
674        NOTREACHED();
675        break;
676      }
677      case ChromeViewHostMsg_GetPluginInfo_Status::kAllowed: {
678        const bool is_nacl_plugin =
679            plugin.name == ASCIIToUTF16(ChromeContentClient::kNaClPluginName);
680        const bool is_nacl_mime_type =
681            actual_mime_type == "application/x-nacl";
682        const bool is_pnacl_mime_type =
683            actual_mime_type == "application/x-pnacl";
684        if (is_nacl_plugin || is_nacl_mime_type || is_pnacl_mime_type) {
685          bool is_nacl_unrestricted = false;
686          if (is_nacl_mime_type) {
687            is_nacl_unrestricted =
688                CommandLine::ForCurrentProcess()->HasSwitch(
689                    switches::kEnableNaCl);
690          } else if (is_pnacl_mime_type) {
691            is_nacl_unrestricted = true;
692          }
693          GURL manifest_url;
694          GURL app_url;
695          if (is_nacl_mime_type || is_pnacl_mime_type) {
696            // Normal NaCl/PNaCl embed. The app URL is the page URL.
697            manifest_url = url;
698            app_url = frame->top()->document().url();
699          } else {
700            // NaCl is being invoked as a content handler. Look up the NaCl
701            // module using the MIME type. The app URL is the manifest URL.
702            manifest_url = GetNaClContentHandlerURL(actual_mime_type, plugin);
703            app_url = manifest_url;
704          }
705          const Extension* extension =
706              g_current_client->extension_dispatcher_->extensions()->
707                  GetExtensionOrAppByURL(manifest_url);
708          if (!IsNaClAllowed(manifest_url,
709                             app_url,
710                             is_nacl_unrestricted,
711                             extension,
712                             &params)) {
713            WebString error_message;
714            if (is_nacl_mime_type) {
715              error_message =
716                  "Only unpacked extensions and apps installed from the Chrome "
717                  "Web Store can load NaCl modules without enabling Native "
718                  "Client in about:flags.";
719            } else if (is_pnacl_mime_type) {
720              error_message =
721                  "Portable Native Client must not be disabled in about:flags.";
722            }
723            frame->addMessageToConsole(
724                WebConsoleMessage(WebConsoleMessage::LevelError,
725                                  error_message));
726            placeholder = ChromePluginPlaceholder::CreateBlockedPlugin(
727                render_frame,
728                frame,
729                params,
730                plugin,
731                identifier,
732                group_name,
733                IDR_BLOCKED_PLUGIN_HTML,
734  #if defined(OS_CHROMEOS)
735                l10n_util::GetStringUTF16(IDS_NACL_PLUGIN_BLOCKED));
736  #else
737                l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED, group_name));
738  #endif
739            break;
740          }
741        }
742
743        // Delay loading plugins if prerendering.
744        // TODO(mmenke):  In the case of prerendering, feed into
745        //                ChromeContentRendererClient::CreatePlugin instead, to
746        //                reduce the chance of future regressions.
747        if (prerender::PrerenderHelper::IsPrerendering(render_frame)) {
748          placeholder = ChromePluginPlaceholder::CreateBlockedPlugin(
749              render_frame,
750              frame,
751              params,
752              plugin,
753              identifier,
754              group_name,
755              IDR_CLICK_TO_PLAY_PLUGIN_HTML,
756              l10n_util::GetStringFUTF16(IDS_PLUGIN_LOAD, group_name));
757          placeholder->set_blocked_for_prerendering(true);
758          placeholder->set_allow_loading(true);
759          break;
760        }
761
762        return render_frame->CreatePlugin(frame, plugin, params);
763      }
764      case ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported: {
765        RenderThread::Get()->RecordAction(
766            UserMetricsAction("Plugin_NPAPINotSupported"));
767        placeholder = ChromePluginPlaceholder::CreateBlockedPlugin(
768            render_frame,
769            frame,
770            params,
771            plugin,
772            identifier,
773            group_name,
774            IDR_BLOCKED_PLUGIN_HTML,
775            l10n_util::GetStringUTF16(IDS_PLUGIN_NOT_SUPPORTED_METRO));
776        render_frame->Send(new ChromeViewHostMsg_NPAPINotSupported(
777            render_frame->GetRoutingID(), identifier));
778        break;
779      }
780      case ChromeViewHostMsg_GetPluginInfo_Status::kDisabled: {
781        PluginUMAReporter::GetInstance()->ReportPluginDisabled(orig_mime_type,
782                                                               url);
783        placeholder = ChromePluginPlaceholder::CreateBlockedPlugin(
784            render_frame,
785            frame,
786            params,
787            plugin,
788            identifier,
789            group_name,
790            IDR_DISABLED_PLUGIN_HTML,
791            l10n_util::GetStringFUTF16(IDS_PLUGIN_DISABLED, group_name));
792        break;
793      }
794      case ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked: {
795#if defined(ENABLE_PLUGIN_INSTALLATION)
796        placeholder = ChromePluginPlaceholder::CreateBlockedPlugin(
797            render_frame,
798            frame,
799            params,
800            plugin,
801            identifier,
802            group_name,
803            IDR_BLOCKED_PLUGIN_HTML,
804            l10n_util::GetStringFUTF16(IDS_PLUGIN_OUTDATED, group_name));
805        placeholder->set_allow_loading(true);
806        render_frame->Send(new ChromeViewHostMsg_BlockedOutdatedPlugin(
807            render_frame->GetRoutingID(), placeholder->CreateRoutingId(),
808            identifier));
809#else
810        NOTREACHED();
811#endif
812        break;
813      }
814      case ChromeViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed: {
815        placeholder = ChromePluginPlaceholder::CreateBlockedPlugin(
816            render_frame,
817            frame,
818            params,
819            plugin,
820            identifier,
821            group_name,
822            IDR_BLOCKED_PLUGIN_HTML,
823            l10n_util::GetStringFUTF16(IDS_PLUGIN_OUTDATED, group_name));
824        break;
825      }
826      case ChromeViewHostMsg_GetPluginInfo_Status::kUnauthorized: {
827        placeholder = ChromePluginPlaceholder::CreateBlockedPlugin(
828            render_frame,
829            frame,
830            params,
831            plugin,
832            identifier,
833            group_name,
834            IDR_BLOCKED_PLUGIN_HTML,
835            l10n_util::GetStringFUTF16(IDS_PLUGIN_NOT_AUTHORIZED, group_name));
836        placeholder->set_allow_loading(true);
837        // Check to see if old infobar should be displayed.
838        std::string trial_group =
839            base::FieldTrialList::FindFullName("UnauthorizedPluginInfoBar");
840        if (plugin.type != content::WebPluginInfo::PLUGIN_TYPE_NPAPI ||
841            trial_group == "Enabled") {
842          render_frame->Send(new ChromeViewHostMsg_BlockedUnauthorizedPlugin(
843              render_frame->GetRoutingID(),
844              group_name,
845              identifier));
846        } else {
847          // Send IPC for showing blocked plugins page action.
848          observer->DidBlockContentType(content_type);
849        }
850        break;
851      }
852      case ChromeViewHostMsg_GetPluginInfo_Status::kClickToPlay: {
853        placeholder = ChromePluginPlaceholder::CreateBlockedPlugin(
854            render_frame,
855            frame,
856            params,
857            plugin,
858            identifier,
859            group_name,
860            IDR_CLICK_TO_PLAY_PLUGIN_HTML,
861            l10n_util::GetStringFUTF16(IDS_PLUGIN_LOAD, group_name));
862        placeholder->set_allow_loading(true);
863        RenderThread::Get()->RecordAction(
864            UserMetricsAction("Plugin_ClickToPlay"));
865        observer->DidBlockContentType(content_type);
866        break;
867      }
868      case ChromeViewHostMsg_GetPluginInfo_Status::kBlocked: {
869        placeholder = ChromePluginPlaceholder::CreateBlockedPlugin(
870            render_frame,
871            frame,
872            params,
873            plugin,
874            identifier,
875            group_name,
876            IDR_BLOCKED_PLUGIN_HTML,
877            l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED, group_name));
878        placeholder->set_allow_loading(true);
879        RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Blocked"));
880        observer->DidBlockContentType(content_type);
881        break;
882      }
883      case ChromeViewHostMsg_GetPluginInfo_Status::kBlockedByPolicy: {
884        placeholder = ChromePluginPlaceholder::CreateBlockedPlugin(
885            render_frame,
886            frame,
887            params,
888            plugin,
889            identifier,
890            group_name,
891            IDR_BLOCKED_PLUGIN_HTML,
892            l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED, group_name));
893        placeholder->set_allow_loading(false);
894        RenderThread::Get()->RecordAction(
895            UserMetricsAction("Plugin_BlockedByPolicy"));
896        observer->DidBlockContentType(content_type);
897        break;
898      }
899    }
900  }
901  placeholder->SetStatus(status);
902  return placeholder->plugin();
903}
904
905// For NaCl content handling plugins, the NaCl manifest is stored in an
906// additonal 'nacl' param associated with the MIME type.
907//  static
908GURL ChromeContentRendererClient::GetNaClContentHandlerURL(
909    const std::string& actual_mime_type,
910    const content::WebPluginInfo& plugin) {
911  // Look for the manifest URL among the MIME type's additonal parameters.
912  const char* kNaClPluginManifestAttribute = "nacl";
913  base::string16 nacl_attr = ASCIIToUTF16(kNaClPluginManifestAttribute);
914  for (size_t i = 0; i < plugin.mime_types.size(); ++i) {
915    if (plugin.mime_types[i].mime_type == actual_mime_type) {
916      const content::WebPluginMimeType& content_type = plugin.mime_types[i];
917      for (size_t i = 0; i < content_type.additional_param_names.size(); ++i) {
918        if (content_type.additional_param_names[i] == nacl_attr)
919          return GURL(content_type.additional_param_values[i]);
920      }
921      break;
922    }
923  }
924  return GURL();
925}
926
927//  static
928bool ChromeContentRendererClient::IsNaClAllowed(
929    const GURL& manifest_url,
930    const GURL& app_url,
931    bool is_nacl_unrestricted,
932    const Extension* extension,
933    WebPluginParams* params) {
934  // Temporarily allow these whitelisted apps and WebUIs to use NaCl.
935  std::string app_url_host = app_url.host();
936  std::string manifest_url_path = manifest_url.path();
937
938  bool is_whitelisted_web_ui =
939      app_url.spec() == chrome::kChromeUIAppListStartPageURL;
940
941  bool is_photo_app =
942      // Whitelisted apps must be served over https.
943      app_url.SchemeIs("https") &&
944      manifest_url.SchemeIs("https") &&
945      (EndsWith(app_url_host, "plus.google.com", false) ||
946       EndsWith(app_url_host, "plus.sandbox.google.com", false)) &&
947      manifest_url.DomainIs("ssl.gstatic.com") &&
948      (manifest_url_path.find("s2/oz/nacl/") == 1 ||
949       manifest_url_path.find("photos/nacl/") == 1);
950
951  std::string manifest_fs_host;
952  if (manifest_url.SchemeIsFileSystem() && manifest_url.inner_url()) {
953    manifest_fs_host = manifest_url.inner_url()->host();
954  }
955  bool is_hangouts_app =
956      // Whitelisted apps must be served over secure scheme.
957      app_url.SchemeIs("https") &&
958      manifest_url.SchemeIsSecure() &&
959      manifest_url.SchemeIsFileSystem() &&
960      (EndsWith(app_url_host, "talkgadget.google.com", false) ||
961       EndsWith(app_url_host, "plus.google.com", false) ||
962       EndsWith(app_url_host, "plus.sandbox.google.com", false)) &&
963      // The manifest must be loaded from the host's FileSystem.
964      (manifest_fs_host == app_url_host);
965
966  bool is_whitelisted_app = is_photo_app || is_hangouts_app;
967
968  bool is_extension_from_webstore = extension &&
969      extension->from_webstore();
970
971  bool is_invoked_by_hosted_app = extension &&
972      extension->is_hosted_app() &&
973      extension->web_extent().MatchesURL(app_url);
974
975  // Allow built-in extensions and extensions under development.
976  bool is_extension_unrestricted = extension &&
977      (extension->location() == extensions::Manifest::COMPONENT ||
978       extensions::Manifest::IsUnpackedLocation(extension->location()));
979
980  bool is_invoked_by_extension = app_url.SchemeIs("chrome-extension");
981
982  // The NaCl PDF viewer is always allowed and can use 'Dev' interfaces.
983  bool is_nacl_pdf_viewer =
984      (is_extension_from_webstore &&
985       manifest_url.SchemeIs("chrome-extension") &&
986       manifest_url.host() == "acadkphlmlegjaadjagenfimbpphcgnh");
987
988  // Allow Chrome Web Store extensions, built-in extensions and extensions
989  // under development if the invocation comes from a URL with an extension
990  // scheme. Also allow invocations if they are from whitelisted URLs or
991  // if --enable-nacl is set.
992  bool is_nacl_allowed = is_nacl_unrestricted ||
993                         is_whitelisted_web_ui ||
994                         is_whitelisted_app ||
995                         is_nacl_pdf_viewer ||
996                         is_invoked_by_hosted_app ||
997                         (is_invoked_by_extension &&
998                             (is_extension_from_webstore ||
999                                 is_extension_unrestricted));
1000  if (is_nacl_allowed) {
1001    bool app_can_use_dev_interfaces = is_nacl_pdf_viewer;
1002    // Make sure that PPAPI 'dev' interfaces aren't available for production
1003    // apps unless they're whitelisted.
1004    WebString dev_attribute = WebString::fromUTF8("@dev");
1005    if ((!is_whitelisted_app && !is_extension_from_webstore) ||
1006        app_can_use_dev_interfaces) {
1007      // Add the special '@dev' attribute.
1008      std::vector<base::string16> param_names;
1009      std::vector<base::string16> param_values;
1010      param_names.push_back(dev_attribute);
1011      param_values.push_back(WebString());
1012      AppendParams(
1013          param_names,
1014          param_values,
1015          &params->attributeNames,
1016          &params->attributeValues);
1017    } else {
1018      // If the params somehow contain '@dev', remove it.
1019      size_t attribute_count = params->attributeNames.size();
1020      for (size_t i = 0; i < attribute_count; ++i) {
1021        if (params->attributeNames[i].equals(dev_attribute))
1022          params->attributeNames[i] = WebString();
1023      }
1024    }
1025  }
1026  return is_nacl_allowed;
1027}
1028
1029bool ChromeContentRendererClient::HasErrorPage(int http_status_code,
1030                                               std::string* error_domain) {
1031  // Use an internal error page, if we have one for the status code.
1032  if (!LocalizedError::HasStrings(LocalizedError::kHttpErrorDomain,
1033                                  http_status_code)) {
1034    return false;
1035  }
1036
1037  *error_domain = LocalizedError::kHttpErrorDomain;
1038  return true;
1039}
1040
1041bool ChromeContentRendererClient::ShouldSuppressErrorPage(
1042    content::RenderFrame* render_frame,
1043    const GURL& url) {
1044  // Unit tests for ChromeContentRendererClient pass a NULL RenderFrame here.
1045  // Unfortunately it's very difficult to construct a mock RenderView, so skip
1046  // this functionality in this case.
1047  if (render_frame) {
1048    content::RenderView* render_view = render_frame->GetRenderView();
1049    content::RenderFrame* main_render_frame = render_view->GetMainRenderFrame();
1050    blink::WebFrame* web_frame = render_frame->GetWebFrame();
1051    NetErrorHelper* net_error_helper = NetErrorHelper::Get(main_render_frame);
1052    if (net_error_helper->ShouldSuppressErrorPage(web_frame, url))
1053      return true;
1054  }
1055  // Do not flash an error page if the Instant new tab page fails to load.
1056  return search_bouncer_.get() && search_bouncer_->IsNewTabPage(url);
1057}
1058
1059void ChromeContentRendererClient::GetNavigationErrorStrings(
1060    content::RenderView* render_view,
1061    blink::WebFrame* frame,
1062    const blink::WebURLRequest& failed_request,
1063    const blink::WebURLError& error,
1064    std::string* error_html,
1065    base::string16* error_description) {
1066  const GURL failed_url = error.unreachableURL;
1067  const Extension* extension = NULL;
1068
1069  if (failed_url.is_valid() &&
1070      !failed_url.SchemeIs(extensions::kExtensionScheme)) {
1071    extension = extension_dispatcher_->extensions()->GetExtensionOrAppByURL(
1072        failed_url);
1073  }
1074
1075  bool is_post = EqualsASCII(failed_request.httpMethod(), "POST");
1076
1077  if (error_html) {
1078    // Use a local error page.
1079    if (extension && !extension->from_bookmark()) {
1080      // TODO(erikkay): Should we use a different template for different
1081      // error messages?
1082      int resource_id = IDR_ERROR_APP_HTML;
1083      const base::StringPiece template_html(
1084          ResourceBundle::GetSharedInstance().GetRawDataResource(
1085              resource_id));
1086      if (template_html.empty()) {
1087        NOTREACHED() << "unable to load template. ID: " << resource_id;
1088      } else {
1089        base::DictionaryValue error_strings;
1090        LocalizedError::GetAppErrorStrings(failed_url, extension,
1091                                           &error_strings);
1092        // "t" is the id of the template's root node.
1093        *error_html = webui::GetTemplatesHtml(template_html, &error_strings,
1094                                              "t");
1095      }
1096    } else {
1097      // TODO(ellyjones): change GetNavigationErrorStrings to take a RenderFrame
1098      // instead of a RenderView, then pass that in.
1099      // This is safe for now because we only install the NetErrorHelper on the
1100      // main render frame anyway; see the TODO(ellyjones) in
1101      // RenderFrameCreated.
1102      content::RenderFrame* main_render_frame =
1103          render_view->GetMainRenderFrame();
1104      NetErrorHelper* helper = NetErrorHelper::Get(main_render_frame);
1105      helper->GetErrorHTML(frame, error, is_post, error_html);
1106    }
1107  }
1108
1109  if (error_description) {
1110    if (!extension)
1111      *error_description = LocalizedError::GetErrorDetails(error, is_post);
1112  }
1113}
1114
1115bool ChromeContentRendererClient::RunIdleHandlerWhenWidgetsHidden() {
1116  return !extension_dispatcher_->is_extension_process();
1117}
1118
1119bool ChromeContentRendererClient::AllowPopup() {
1120  extensions::ScriptContext* current_context =
1121      extension_dispatcher_->script_context_set().GetCurrent();
1122  if (!current_context || !current_context->extension())
1123    return false;
1124  // See http://crbug.com/117446 for the subtlety of this check.
1125  switch (current_context->context_type()) {
1126    case extensions::Feature::UNSPECIFIED_CONTEXT:
1127    case extensions::Feature::WEB_PAGE_CONTEXT:
1128    case extensions::Feature::UNBLESSED_EXTENSION_CONTEXT:
1129    case extensions::Feature::WEBUI_CONTEXT:
1130      return false;
1131    case extensions::Feature::BLESSED_EXTENSION_CONTEXT:
1132    case extensions::Feature::CONTENT_SCRIPT_CONTEXT:
1133      return true;
1134    case extensions::Feature::BLESSED_WEB_PAGE_CONTEXT:
1135      return !current_context->web_frame()->parent();
1136  }
1137  NOTREACHED();
1138  return false;
1139}
1140
1141bool ChromeContentRendererClient::ShouldFork(WebFrame* frame,
1142                                             const GURL& url,
1143                                             const std::string& http_method,
1144                                             bool is_initial_navigation,
1145                                             bool is_server_redirect,
1146                                             bool* send_referrer) {
1147  DCHECK(!frame->parent());
1148
1149  // If this is the Instant process, fork all navigations originating from the
1150  // renderer.  The destination page will then be bucketed back to this Instant
1151  // process if it is an Instant url, or to another process if not.  Conversely,
1152  // fork if this is a non-Instant process navigating to an Instant url, so that
1153  // such navigations can also be bucketed into an Instant renderer.
1154  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kInstantProcess) ||
1155      (search_bouncer_.get() && search_bouncer_->ShouldFork(url))) {
1156    *send_referrer = true;
1157    return true;
1158  }
1159
1160  // For now, we skip the rest for POST submissions.  This is because
1161  // http://crbug.com/101395 is more likely to cause compatibility issues
1162  // with hosted apps and extensions than WebUI pages.  We will remove this
1163  // check when cross-process POST submissions are supported.
1164  if (http_method != "GET")
1165    return false;
1166
1167  // If this is the Signin process, fork all navigations originating from the
1168  // renderer.  The destination page will then be bucketed back to this Signin
1169  // process if it is a Signin url, or to another process if not.
1170  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSigninProcess)) {
1171    // We never want to allow non-signin pages to fork-on-POST to a
1172    // signin-related action URL. We'll need to handle this carefully once
1173    // http://crbug.com/101395 is fixed. The CHECK ensures we don't forget.
1174    CHECK_NE(http_method, "POST");
1175    return true;
1176  }
1177
1178  // If |url| matches one of the prerendered URLs, stop this navigation and try
1179  // to swap in the prerendered page on the browser process. If the prerendered
1180  // page no longer exists by the time the OpenURL IPC is handled, a normal
1181  // navigation is attempted.
1182  if (prerender_dispatcher_.get() &&
1183      prerender_dispatcher_->IsPrerenderURL(url)) {
1184    *send_referrer = true;
1185    return true;
1186  }
1187
1188  const extensions::ExtensionSet* extensions =
1189      extension_dispatcher_->extensions();
1190
1191  // Determine if the new URL is an extension (excluding bookmark apps).
1192  const Extension* new_url_extension = extensions::GetNonBookmarkAppExtension(
1193      *extensions, url);
1194  bool is_extension_url = !!new_url_extension;
1195
1196  // If the navigation would cross an app extent boundary, we also need
1197  // to defer to the browser to ensure process isolation.  This is not necessary
1198  // for server redirects, which will be transferred to a new process by the
1199  // browser process when they are ready to commit.  It is necessary for client
1200  // redirects, which won't be transferred in the same way.
1201  if (!is_server_redirect &&
1202      CrossesExtensionExtents(frame, url, *extensions, is_extension_url,
1203          is_initial_navigation)) {
1204    // Include the referrer in this case since we're going from a hosted web
1205    // page. (the packaged case is handled previously by the extension
1206    // navigation test)
1207    *send_referrer = true;
1208
1209    const Extension* extension =
1210        extension_dispatcher_->extensions()->GetExtensionOrAppByURL(url);
1211    if (extension && extension->is_app()) {
1212      UMA_HISTOGRAM_ENUMERATION(
1213          extension->is_platform_app() ?
1214          extension_misc::kPlatformAppLaunchHistogram :
1215          extension_misc::kAppLaunchHistogram,
1216          extension_misc::APP_LAUNCH_CONTENT_NAVIGATION,
1217          extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
1218    }
1219    return true;
1220  }
1221
1222  // If this is a reload, check whether it has the wrong process type.  We
1223  // should send it to the browser if it's an extension URL (e.g., hosted app)
1224  // in a normal process, or if it's a process for an extension that has been
1225  // uninstalled.
1226  if (frame->top()->document().url() == url) {
1227    if (is_extension_url != extension_dispatcher_->is_extension_process())
1228      return true;
1229  }
1230
1231  return false;
1232}
1233
1234bool ChromeContentRendererClient::WillSendRequest(
1235    blink::WebFrame* frame,
1236    content::PageTransition transition_type,
1237    const GURL& url,
1238    const GURL& first_party_for_cookies,
1239    GURL* new_url) {
1240  // Check whether the request should be allowed. If not allowed, we reset the
1241  // URL to something invalid to prevent the request and cause an error.
1242  if (url.SchemeIs(extensions::kExtensionScheme) &&
1243      !extensions::ResourceRequestPolicy::CanRequestResource(
1244          url,
1245          frame,
1246          transition_type,
1247          extension_dispatcher_->extensions())) {
1248    *new_url = GURL(chrome::kExtensionInvalidRequestURL);
1249    return true;
1250  }
1251
1252  if (url.SchemeIs(extensions::kExtensionResourceScheme) &&
1253      !extensions::ResourceRequestPolicy::CanRequestExtensionResourceScheme(
1254          url,
1255          frame)) {
1256    *new_url = GURL(chrome::kExtensionResourceInvalidRequestURL);
1257    return true;
1258  }
1259
1260  const content::RenderView* render_view =
1261      content::RenderView::FromWebView(frame->view());
1262  SearchBox* search_box = SearchBox::Get(render_view);
1263  if (search_box && url.SchemeIs(chrome::kChromeSearchScheme)) {
1264    if (url.host() == chrome::kChromeUIThumbnailHost)
1265      return search_box->GenerateThumbnailURLFromTransientURL(url, new_url);
1266    else if (url.host() == chrome::kChromeUIFaviconHost)
1267      return search_box->GenerateFaviconURLFromTransientURL(url, new_url);
1268  }
1269
1270  return false;
1271}
1272
1273void ChromeContentRendererClient::DidCreateScriptContext(
1274    WebFrame* frame, v8::Handle<v8::Context> context, int extension_group,
1275    int world_id) {
1276  extension_dispatcher_->DidCreateScriptContext(
1277      frame, context, extension_group, world_id);
1278}
1279
1280unsigned long long ChromeContentRendererClient::VisitedLinkHash(
1281    const char* canonical_url, size_t length) {
1282  return visited_link_slave_->ComputeURLFingerprint(canonical_url, length);
1283}
1284
1285bool ChromeContentRendererClient::IsLinkVisited(unsigned long long link_hash) {
1286  return visited_link_slave_->IsVisited(link_hash);
1287}
1288
1289blink::WebPrescientNetworking*
1290ChromeContentRendererClient::GetPrescientNetworking() {
1291  return prescient_networking_dispatcher_.get();
1292}
1293
1294bool ChromeContentRendererClient::ShouldOverridePageVisibilityState(
1295    const content::RenderFrame* render_frame,
1296    blink::WebPageVisibilityState* override_state) {
1297  if (!prerender::PrerenderHelper::IsPrerendering(render_frame))
1298    return false;
1299
1300  *override_state = blink::WebPageVisibilityStatePrerender;
1301  return true;
1302}
1303
1304void ChromeContentRendererClient::SetExtensionDispatcherForTest(
1305    extensions::Dispatcher* extension_dispatcher) {
1306  extension_dispatcher_.reset(extension_dispatcher);
1307  permissions_policy_delegate_.reset(
1308      new extensions::RendererPermissionsPolicyDelegate(
1309          extension_dispatcher_.get()));
1310}
1311
1312extensions::Dispatcher*
1313ChromeContentRendererClient::GetExtensionDispatcherForTest() {
1314  return extension_dispatcher_.get();
1315}
1316
1317bool ChromeContentRendererClient::CrossesExtensionExtents(
1318    WebFrame* frame,
1319    const GURL& new_url,
1320    const extensions::ExtensionSet& extensions,
1321    bool is_extension_url,
1322    bool is_initial_navigation) {
1323  GURL old_url(frame->top()->document().url());
1324
1325  // If old_url is still empty and this is an initial navigation, then this is
1326  // a window.open operation.  We should look at the opener URL.
1327  if (is_initial_navigation && old_url.is_empty() && frame->opener()) {
1328    // If we're about to open a normal web page from a same-origin opener stuck
1329    // in an extension process, we want to keep it in process to allow the
1330    // opener to script it.
1331    WebDocument opener_document = frame->opener()->document();
1332    WebSecurityOrigin opener = frame->opener()->document().securityOrigin();
1333    bool opener_is_extension_url =
1334        !opener.isUnique() && extensions.GetExtensionOrAppByURL(
1335            opener_document.url()) != NULL;
1336    if (!is_extension_url &&
1337        !opener_is_extension_url &&
1338        extension_dispatcher_->is_extension_process() &&
1339        opener.canRequest(WebURL(new_url)))
1340      return false;
1341
1342    // In all other cases, we want to compare against the top frame's URL (as
1343    // opposed to the opener frame's), since that's what determines the type of
1344    // process.  This allows iframes outside an app to open a popup in the app.
1345    old_url = frame->top()->opener()->top()->document().url();
1346  }
1347
1348  // Only consider keeping non-app URLs in an app process if this window
1349  // has an opener (in which case it might be an OAuth popup that tries to
1350  // script an iframe within the app).
1351  bool should_consider_workaround = !!frame->opener();
1352
1353  return extensions::CrossesExtensionProcessBoundary(
1354      extensions, old_url, new_url, should_consider_workaround);
1355}
1356
1357#if defined(ENABLE_SPELLCHECK)
1358void ChromeContentRendererClient::SetSpellcheck(SpellCheck* spellcheck) {
1359  RenderThread* thread = RenderThread::Get();
1360  if (spellcheck_.get() && thread)
1361    thread->RemoveObserver(spellcheck_.get());
1362  spellcheck_.reset(spellcheck);
1363  SpellCheckReplacer replacer(spellcheck_.get());
1364  content::RenderView::ForEach(&replacer);
1365  if (thread)
1366    thread->AddObserver(spellcheck_.get());
1367}
1368#endif
1369
1370// static
1371bool ChromeContentRendererClient::WasWebRequestUsedBySomeExtensions() {
1372  return g_current_client->extension_dispatcher_delegate_
1373      ->WasWebRequestUsedBySomeExtensions();
1374}
1375
1376const void* ChromeContentRendererClient::CreatePPAPIInterface(
1377    const std::string& interface_name) {
1378#if defined(ENABLE_PLUGINS)
1379#if !defined(DISABLE_NACL)
1380  if (interface_name == PPB_NACL_PRIVATE_INTERFACE)
1381    return nacl::GetNaClPrivateInterface();
1382#endif  // DISABLE_NACL
1383  if (interface_name == PPB_PDF_INTERFACE)
1384    return PPB_PDF_Impl::GetInterface();
1385#endif
1386  return NULL;
1387}
1388
1389bool ChromeContentRendererClient::IsExternalPepperPlugin(
1390    const std::string& module_name) {
1391  // TODO(bbudge) remove this when the trusted NaCl plugin has been removed.
1392  // We must defer certain plugin events for NaCl instances since we switch
1393  // from the in-process to the out-of-process proxy after instantiating them.
1394  return module_name == "Native Client";
1395}
1396
1397bool ChromeContentRendererClient::IsExtensionOrSharedModuleWhitelisted(
1398    const GURL& url, const std::set<std::string>& whitelist) {
1399  const extensions::ExtensionSet* extension_set =
1400      g_current_client->extension_dispatcher_->extensions();
1401  return chrome::IsExtensionOrSharedModuleWhitelisted(url, extension_set,
1402      whitelist);
1403}
1404
1405blink::WebSpeechSynthesizer*
1406ChromeContentRendererClient::OverrideSpeechSynthesizer(
1407    blink::WebSpeechSynthesizerClient* client) {
1408  return new TtsDispatcher(client);
1409}
1410
1411bool ChromeContentRendererClient::AllowPepperMediaStreamAPI(
1412    const GURL& url) {
1413#if !defined(OS_ANDROID)
1414  // Allow only the Hangouts app to use the MediaStream APIs. It's OK to check
1415  // the whitelist in the renderer, since we're only preventing access until
1416  // these APIs are public and stable.
1417  std::string url_host = url.host();
1418  if (url.SchemeIs("https") &&
1419      (EndsWith(url_host, "talkgadget.google.com", false) ||
1420       EndsWith(url_host, "plus.google.com", false) ||
1421       EndsWith(url_host, "plus.sandbox.google.com", false)) &&
1422      StartsWithASCII(url.path(), "/hangouts/", false)) {
1423    return true;
1424  }
1425  // Allow access for tests.
1426  if (CommandLine::ForCurrentProcess()->HasSwitch(
1427          switches::kEnablePepperTesting)) {
1428    return true;
1429  }
1430#endif  // !defined(OS_ANDROID)
1431  return false;
1432}
1433
1434void ChromeContentRendererClient::AddKeySystems(
1435    std::vector<content::KeySystemInfo>* key_systems) {
1436  AddChromeKeySystems(key_systems);
1437}
1438
1439bool ChromeContentRendererClient::ShouldReportDetailedMessageForSource(
1440    const base::string16& source) const {
1441  return extensions::IsSourceFromAnExtension(source);
1442}
1443
1444bool ChromeContentRendererClient::ShouldEnableSiteIsolationPolicy() const {
1445  // SiteIsolationPolicy is off by default. We would like to activate cross-site
1446  // document blocking (for UMA data collection) for normal renderer processes
1447  // running a normal web page from the Internet. We only turn on
1448  // SiteIsolationPolicy for a renderer process that does not have the extension
1449  // flag on.
1450  CommandLine* command_line = CommandLine::ForCurrentProcess();
1451  return !command_line->HasSwitch(extensions::switches::kExtensionProcess);
1452}
1453
1454blink::WebWorkerPermissionClientProxy*
1455ChromeContentRendererClient::CreateWorkerPermissionClientProxy(
1456    content::RenderFrame* render_frame,
1457    blink::WebFrame* frame) {
1458  return new WorkerPermissionClientProxy(render_frame, frame);
1459}
1460
1461bool ChromeContentRendererClient::IsPluginAllowedToUseDevChannelAPIs() {
1462#if defined(ENABLE_PLUGINS)
1463  // Allow access for tests.
1464  if (CommandLine::ForCurrentProcess()->HasSwitch(
1465          switches::kEnablePepperTesting)) {
1466    return true;
1467  }
1468
1469  chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
1470  // Allow dev channel APIs to be used on "Canary", "Dev", and "Unknown"
1471  // releases of Chrome. Permitting "Unknown" allows these APIs to be used on
1472  // Chromium builds as well.
1473  return channel <= chrome::VersionInfo::CHANNEL_DEV;
1474#else
1475  return false;
1476#endif
1477}
1478
1479bool ChromeContentRendererClient::IsPluginAllowedToUseCompositorAPI(
1480    const GURL& url) {
1481#if defined(ENABLE_PLUGINS)
1482  if (CommandLine::ForCurrentProcess()->HasSwitch(
1483          switches::kEnablePepperTesting))
1484    return true;
1485  if (IsExtensionOrSharedModuleWhitelisted(url, allowed_compositor_origins_))
1486    return true;
1487
1488  chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
1489  return channel <= chrome::VersionInfo::CHANNEL_DEV;
1490#else
1491  return false;
1492#endif
1493}
1494
1495bool ChromeContentRendererClient::IsPluginAllowedToUseVideoDecodeAPI(
1496    const GURL& url) {
1497#if defined(ENABLE_PLUGINS)
1498  if (CommandLine::ForCurrentProcess()->HasSwitch(
1499          switches::kEnablePepperTesting))
1500    return true;
1501
1502  if (IsExtensionOrSharedModuleWhitelisted(url, allowed_video_decode_origins_))
1503    return true;
1504
1505  chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
1506  return channel <= chrome::VersionInfo::CHANNEL_DEV;
1507#else
1508  return false;
1509#endif
1510}
1511