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/content_settings_observer.h"
6
7 #include "base/command_line.h"
8 #include "base/metrics/histogram.h"
9 #include "chrome/common/chrome_switches.h"
10 #include "chrome/common/render_messages.h"
11 #include "chrome/common/url_constants.h"
12 #include "chrome/renderer/extensions/dispatcher.h"
13 #include "content/public/renderer/document_state.h"
14 #include "content/public/renderer/navigation_state.h"
15 #include "content/public/renderer/render_view.h"
16 #include "extensions/common/constants.h"
17 #include "third_party/WebKit/public/platform/WebURL.h"
18 #include "third_party/WebKit/public/web/WebDataSource.h"
19 #include "third_party/WebKit/public/web/WebDocument.h"
20 #include "third_party/WebKit/public/web/WebFrame.h"
21 #include "third_party/WebKit/public/web/WebFrameClient.h"
22 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
23 #include "third_party/WebKit/public/web/WebView.h"
24 #include "webkit/child/weburlresponse_extradata_impl.h"
25
26 using blink::WebDataSource;
27 using blink::WebDocument;
28 using blink::WebFrame;
29 using blink::WebFrameClient;
30 using blink::WebSecurityOrigin;
31 using blink::WebString;
32 using blink::WebURL;
33 using blink::WebView;
34 using content::DocumentState;
35 using content::NavigationState;
36 using extensions::APIPermission;
37
38 namespace {
39
40 enum {
41 INSECURE_CONTENT_DISPLAY = 0,
42 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE,
43 INSECURE_CONTENT_DISPLAY_HOST_WWW_GOOGLE,
44 INSECURE_CONTENT_DISPLAY_HTML,
45 INSECURE_CONTENT_RUN,
46 INSECURE_CONTENT_RUN_HOST_GOOGLE,
47 INSECURE_CONTENT_RUN_HOST_WWW_GOOGLE,
48 INSECURE_CONTENT_RUN_TARGET_YOUTUBE,
49 INSECURE_CONTENT_RUN_JS,
50 INSECURE_CONTENT_RUN_CSS,
51 INSECURE_CONTENT_RUN_SWF,
52 INSECURE_CONTENT_DISPLAY_HOST_YOUTUBE,
53 INSECURE_CONTENT_RUN_HOST_YOUTUBE,
54 INSECURE_CONTENT_RUN_HOST_GOOGLEUSERCONTENT,
55 INSECURE_CONTENT_DISPLAY_HOST_MAIL_GOOGLE,
56 INSECURE_CONTENT_RUN_HOST_MAIL_GOOGLE,
57 INSECURE_CONTENT_DISPLAY_HOST_PLUS_GOOGLE,
58 INSECURE_CONTENT_RUN_HOST_PLUS_GOOGLE,
59 INSECURE_CONTENT_DISPLAY_HOST_DOCS_GOOGLE,
60 INSECURE_CONTENT_RUN_HOST_DOCS_GOOGLE,
61 INSECURE_CONTENT_DISPLAY_HOST_SITES_GOOGLE,
62 INSECURE_CONTENT_RUN_HOST_SITES_GOOGLE,
63 INSECURE_CONTENT_DISPLAY_HOST_PICASAWEB_GOOGLE,
64 INSECURE_CONTENT_RUN_HOST_PICASAWEB_GOOGLE,
65 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_READER,
66 INSECURE_CONTENT_RUN_HOST_GOOGLE_READER,
67 INSECURE_CONTENT_DISPLAY_HOST_CODE_GOOGLE,
68 INSECURE_CONTENT_RUN_HOST_CODE_GOOGLE,
69 INSECURE_CONTENT_DISPLAY_HOST_GROUPS_GOOGLE,
70 INSECURE_CONTENT_RUN_HOST_GROUPS_GOOGLE,
71 INSECURE_CONTENT_DISPLAY_HOST_MAPS_GOOGLE,
72 INSECURE_CONTENT_RUN_HOST_MAPS_GOOGLE,
73 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_SUPPORT,
74 INSECURE_CONTENT_RUN_HOST_GOOGLE_SUPPORT,
75 INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_INTL,
76 INSECURE_CONTENT_RUN_HOST_GOOGLE_INTL,
77 INSECURE_CONTENT_NUM_EVENTS
78 };
79
80 // Constants for UMA statistic collection.
81 static const char kWWWDotGoogleDotCom[] = "www.google.com";
82 static const char kMailDotGoogleDotCom[] = "mail.google.com";
83 static const char kPlusDotGoogleDotCom[] = "plus.google.com";
84 static const char kDocsDotGoogleDotCom[] = "docs.google.com";
85 static const char kSitesDotGoogleDotCom[] = "sites.google.com";
86 static const char kPicasawebDotGoogleDotCom[] = "picasaweb.google.com";
87 static const char kCodeDotGoogleDotCom[] = "code.google.com";
88 static const char kGroupsDotGoogleDotCom[] = "groups.google.com";
89 static const char kMapsDotGoogleDotCom[] = "maps.google.com";
90 static const char kWWWDotYoutubeDotCom[] = "www.youtube.com";
91 static const char kDotGoogleUserContentDotCom[] = ".googleusercontent.com";
92 static const char kGoogleReaderPathPrefix[] = "/reader/";
93 static const char kGoogleSupportPathPrefix[] = "/support/";
94 static const char kGoogleIntlPathPrefix[] = "/intl/";
95 static const char kDotJS[] = ".js";
96 static const char kDotCSS[] = ".css";
97 static const char kDotSWF[] = ".swf";
98 static const char kDotHTML[] = ".html";
99
100 // Constants for mixed-content blocking.
101 static const char kGoogleDotCom[] = "google.com";
102
IsHostInDomain(const std::string & host,const std::string & domain)103 static bool IsHostInDomain(const std::string& host, const std::string& domain) {
104 return (EndsWith(host, domain, false) &&
105 (host.length() == domain.length() ||
106 (host.length() > domain.length() &&
107 host[host.length() - domain.length() - 1] == '.')));
108 }
109
GetOriginOrURL(const WebFrame * frame)110 GURL GetOriginOrURL(const WebFrame* frame) {
111 WebString top_origin = frame->top()->document().securityOrigin().toString();
112 // The the |top_origin| is unique ("null") e.g., for file:// URLs. Use the
113 // document URL as the primary URL in those cases.
114 if (top_origin == "null")
115 return frame->top()->document().url();
116 return GURL(top_origin);
117 }
118
GetContentSettingFromRules(const ContentSettingsForOneType & rules,const WebFrame * frame,const GURL & secondary_url)119 ContentSetting GetContentSettingFromRules(
120 const ContentSettingsForOneType& rules,
121 const WebFrame* frame,
122 const GURL& secondary_url) {
123 ContentSettingsForOneType::const_iterator it;
124 // If there is only one rule, it's the default rule and we don't need to match
125 // the patterns.
126 if (rules.size() == 1) {
127 DCHECK(rules[0].primary_pattern == ContentSettingsPattern::Wildcard());
128 DCHECK(rules[0].secondary_pattern == ContentSettingsPattern::Wildcard());
129 return rules[0].setting;
130 }
131 const GURL& primary_url = GetOriginOrURL(frame);
132 for (it = rules.begin(); it != rules.end(); ++it) {
133 if (it->primary_pattern.Matches(primary_url) &&
134 it->secondary_pattern.Matches(secondary_url)) {
135 return it->setting;
136 }
137 }
138 NOTREACHED();
139 return CONTENT_SETTING_DEFAULT;
140 }
141
142 } // namespace
143
ContentSettingsObserver(content::RenderView * render_view,extensions::Dispatcher * extension_dispatcher)144 ContentSettingsObserver::ContentSettingsObserver(
145 content::RenderView* render_view,
146 extensions::Dispatcher* extension_dispatcher)
147 : content::RenderViewObserver(render_view),
148 content::RenderViewObserverTracker<ContentSettingsObserver>(render_view),
149 extension_dispatcher_(extension_dispatcher),
150 allow_displaying_insecure_content_(false),
151 allow_running_insecure_content_(false),
152 content_setting_rules_(NULL),
153 is_interstitial_page_(false),
154 npapi_plugins_blocked_(false) {
155 ClearBlockedContentSettings();
156 render_view->GetWebView()->setPermissionClient(this);
157 }
158
~ContentSettingsObserver()159 ContentSettingsObserver::~ContentSettingsObserver() {
160 }
161
SetContentSettingRules(const RendererContentSettingRules * content_setting_rules)162 void ContentSettingsObserver::SetContentSettingRules(
163 const RendererContentSettingRules* content_setting_rules) {
164 content_setting_rules_ = content_setting_rules;
165 }
166
IsPluginTemporarilyAllowed(const std::string & identifier)167 bool ContentSettingsObserver::IsPluginTemporarilyAllowed(
168 const std::string& identifier) {
169 // If the empty string is in here, it means all plug-ins are allowed.
170 // TODO(bauerb): Remove this once we only pass in explicit identifiers.
171 return (temporarily_allowed_plugins_.find(identifier) !=
172 temporarily_allowed_plugins_.end()) ||
173 (temporarily_allowed_plugins_.find(std::string()) !=
174 temporarily_allowed_plugins_.end());
175 }
176
DidBlockContentType(ContentSettingsType settings_type)177 void ContentSettingsObserver::DidBlockContentType(
178 ContentSettingsType settings_type) {
179 if (!content_blocked_[settings_type]) {
180 content_blocked_[settings_type] = true;
181 Send(new ChromeViewHostMsg_ContentBlocked(routing_id(), settings_type));
182 }
183 }
184
OnMessageReceived(const IPC::Message & message)185 bool ContentSettingsObserver::OnMessageReceived(const IPC::Message& message) {
186 bool handled = true;
187 IPC_BEGIN_MESSAGE_MAP(ContentSettingsObserver, message)
188 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAsInterstitial, OnSetAsInterstitial)
189 IPC_MESSAGE_HANDLER(ChromeViewMsg_NPAPINotSupported, OnNPAPINotSupported)
190 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAllowDisplayingInsecureContent,
191 OnSetAllowDisplayingInsecureContent)
192 IPC_MESSAGE_HANDLER(ChromeViewMsg_SetAllowRunningInsecureContent,
193 OnSetAllowRunningInsecureContent)
194 IPC_MESSAGE_UNHANDLED(handled = false)
195 IPC_END_MESSAGE_MAP()
196 if (handled)
197 return true;
198
199 // Don't swallow LoadBlockedPlugins messages, as they're sent to every
200 // blocked plugin.
201 IPC_BEGIN_MESSAGE_MAP(ContentSettingsObserver, message)
202 IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins)
203 IPC_END_MESSAGE_MAP()
204
205 return false;
206 }
207
DidCommitProvisionalLoad(WebFrame * frame,bool is_new_navigation)208 void ContentSettingsObserver::DidCommitProvisionalLoad(
209 WebFrame* frame, bool is_new_navigation) {
210 if (frame->parent())
211 return; // Not a top-level navigation.
212
213 DocumentState* document_state = DocumentState::FromDataSource(
214 frame->dataSource());
215 NavigationState* navigation_state = document_state->navigation_state();
216 if (!navigation_state->was_within_same_page()) {
217 // Clear "block" flags for the new page. This needs to happen before any of
218 // |allowScript()|, |allowScriptFromSource()|, |allowImage()|, or
219 // |allowPlugins()| is called for the new page so that these functions can
220 // correctly detect that a piece of content flipped from "not blocked" to
221 // "blocked".
222 ClearBlockedContentSettings();
223 temporarily_allowed_plugins_.clear();
224 }
225
226 GURL url = frame->document().url();
227 // If we start failing this DCHECK, please makes sure we don't regress
228 // this bug: http://code.google.com/p/chromium/issues/detail?id=79304
229 DCHECK(frame->document().securityOrigin().toString() == "null" ||
230 !url.SchemeIs(chrome::kDataScheme));
231 }
232
allowDatabase(WebFrame * frame,const WebString & name,const WebString & display_name,unsigned long estimated_size)233 bool ContentSettingsObserver::allowDatabase(WebFrame* frame,
234 const WebString& name,
235 const WebString& display_name,
236 unsigned long estimated_size) {
237 if (frame->document().securityOrigin().isUnique() ||
238 frame->top()->document().securityOrigin().isUnique())
239 return false;
240
241 bool result = false;
242 Send(new ChromeViewHostMsg_AllowDatabase(
243 routing_id(), GURL(frame->document().securityOrigin().toString()),
244 GURL(frame->top()->document().securityOrigin().toString()),
245 name, display_name, &result));
246 return result;
247 }
248
allowFileSystem(WebFrame * frame)249 bool ContentSettingsObserver::allowFileSystem(WebFrame* frame) {
250 if (frame->document().securityOrigin().isUnique() ||
251 frame->top()->document().securityOrigin().isUnique())
252 return false;
253
254 bool result = false;
255 Send(new ChromeViewHostMsg_AllowFileSystem(
256 routing_id(), GURL(frame->document().securityOrigin().toString()),
257 GURL(frame->top()->document().securityOrigin().toString()), &result));
258 return result;
259 }
260
allowImage(WebFrame * frame,bool enabled_per_settings,const WebURL & image_url)261 bool ContentSettingsObserver::allowImage(WebFrame* frame,
262 bool enabled_per_settings,
263 const WebURL& image_url) {
264 bool allow = enabled_per_settings;
265 if (enabled_per_settings) {
266 if (is_interstitial_page_)
267 return true;
268 if (IsWhitelistedForContentSettings(frame))
269 return true;
270
271 if (content_setting_rules_) {
272 GURL secondary_url(image_url);
273 allow = GetContentSettingFromRules(
274 content_setting_rules_->image_rules,
275 frame, secondary_url) != CONTENT_SETTING_BLOCK;
276 }
277 }
278 if (!allow)
279 DidBlockContentType(CONTENT_SETTINGS_TYPE_IMAGES);
280 return allow;
281 }
282
allowIndexedDB(WebFrame * frame,const WebString & name,const WebSecurityOrigin & origin)283 bool ContentSettingsObserver::allowIndexedDB(WebFrame* frame,
284 const WebString& name,
285 const WebSecurityOrigin& origin) {
286 if (frame->document().securityOrigin().isUnique() ||
287 frame->top()->document().securityOrigin().isUnique())
288 return false;
289
290 bool result = false;
291 Send(new ChromeViewHostMsg_AllowIndexedDB(
292 routing_id(), GURL(frame->document().securityOrigin().toString()),
293 GURL(frame->top()->document().securityOrigin().toString()),
294 name, &result));
295 return result;
296 }
297
allowPlugins(WebFrame * frame,bool enabled_per_settings)298 bool ContentSettingsObserver::allowPlugins(WebFrame* frame,
299 bool enabled_per_settings) {
300 return enabled_per_settings;
301 }
302
allowScript(WebFrame * frame,bool enabled_per_settings)303 bool ContentSettingsObserver::allowScript(WebFrame* frame,
304 bool enabled_per_settings) {
305 if (!enabled_per_settings)
306 return false;
307 if (is_interstitial_page_)
308 return true;
309
310 std::map<WebFrame*, bool>::const_iterator it =
311 cached_script_permissions_.find(frame);
312 if (it != cached_script_permissions_.end())
313 return it->second;
314
315 // Evaluate the content setting rules before
316 // |IsWhitelistedForContentSettings|; if there is only the default rule
317 // allowing all scripts, it's quicker this way.
318 bool allow = true;
319 if (content_setting_rules_) {
320 ContentSetting setting = GetContentSettingFromRules(
321 content_setting_rules_->script_rules,
322 frame,
323 GURL(frame->document().securityOrigin().toString()));
324 allow = setting != CONTENT_SETTING_BLOCK;
325 }
326 allow = allow || IsWhitelistedForContentSettings(frame);
327
328 cached_script_permissions_[frame] = allow;
329 return allow;
330 }
331
allowScriptFromSource(WebFrame * frame,bool enabled_per_settings,const blink::WebURL & script_url)332 bool ContentSettingsObserver::allowScriptFromSource(
333 WebFrame* frame,
334 bool enabled_per_settings,
335 const blink::WebURL& script_url) {
336 if (!enabled_per_settings)
337 return false;
338 if (is_interstitial_page_)
339 return true;
340
341 bool allow = true;
342 if (content_setting_rules_) {
343 ContentSetting setting = GetContentSettingFromRules(
344 content_setting_rules_->script_rules,
345 frame,
346 GURL(script_url));
347 allow = setting != CONTENT_SETTING_BLOCK;
348 }
349 return allow || IsWhitelistedForContentSettings(frame);
350 }
351
allowStorage(WebFrame * frame,bool local)352 bool ContentSettingsObserver::allowStorage(WebFrame* frame, bool local) {
353 if (frame->document().securityOrigin().isUnique() ||
354 frame->top()->document().securityOrigin().isUnique())
355 return false;
356 bool result = false;
357
358 StoragePermissionsKey key(
359 GURL(frame->document().securityOrigin().toString()), local);
360 std::map<StoragePermissionsKey, bool>::const_iterator permissions =
361 cached_storage_permissions_.find(key);
362 if (permissions != cached_storage_permissions_.end())
363 return permissions->second;
364
365 Send(new ChromeViewHostMsg_AllowDOMStorage(
366 routing_id(), GURL(frame->document().securityOrigin().toString()),
367 GURL(frame->top()->document().securityOrigin().toString()),
368 local, &result));
369 cached_storage_permissions_[key] = result;
370 return result;
371 }
372
allowReadFromClipboard(WebFrame * frame,bool default_value)373 bool ContentSettingsObserver::allowReadFromClipboard(WebFrame* frame,
374 bool default_value) {
375 bool allowed = false;
376 // TODO(dcheng): Should we consider a toURL() method on WebSecurityOrigin?
377 Send(new ChromeViewHostMsg_CanTriggerClipboardRead(
378 routing_id(), GURL(frame->document().securityOrigin().toString().utf8()),
379 &allowed));
380 return allowed;
381 }
382
allowWriteToClipboard(WebFrame * frame,bool default_value)383 bool ContentSettingsObserver::allowWriteToClipboard(WebFrame* frame,
384 bool default_value) {
385 bool allowed = false;
386 Send(new ChromeViewHostMsg_CanTriggerClipboardWrite(
387 routing_id(), GURL(frame->document().securityOrigin().toString().utf8()),
388 &allowed));
389 return allowed;
390 }
391
allowWebComponents(WebFrame * frame,bool defaultValue)392 bool ContentSettingsObserver::allowWebComponents(WebFrame* frame,
393 bool defaultValue) {
394 if (defaultValue)
395 return true;
396
397 WebSecurityOrigin origin = frame->document().securityOrigin();
398 if (EqualsASCII(origin.protocol(), chrome::kChromeUIScheme))
399 return true;
400
401 if (const extensions::Extension* extension = GetExtension(origin)) {
402 if (extension->HasAPIPermission(APIPermission::kExperimental))
403 return true;
404 }
405
406 return false;
407 }
408
allowMutationEvents(WebFrame * frame,bool default_value)409 bool ContentSettingsObserver::allowMutationEvents(WebFrame* frame,
410 bool default_value) {
411 WebSecurityOrigin origin = frame->document().securityOrigin();
412 const extensions::Extension* extension = GetExtension(origin);
413 if (extension && extension->is_platform_app())
414 return false;
415 return default_value;
416 }
417
allowPushState(WebFrame * frame)418 bool ContentSettingsObserver::allowPushState(WebFrame* frame) {
419 WebSecurityOrigin origin = frame->document().securityOrigin();
420 const extensions::Extension* extension = GetExtension(origin);
421 return !extension || !extension->is_platform_app();
422 }
423
SendInsecureContentSignal(int signal)424 static void SendInsecureContentSignal(int signal) {
425 UMA_HISTOGRAM_ENUMERATION("SSL.InsecureContent", signal,
426 INSECURE_CONTENT_NUM_EVENTS);
427 }
428
allowDisplayingInsecureContent(blink::WebFrame * frame,bool allowed_per_settings,const blink::WebSecurityOrigin & origin,const blink::WebURL & resource_url)429 bool ContentSettingsObserver::allowDisplayingInsecureContent(
430 blink::WebFrame* frame,
431 bool allowed_per_settings,
432 const blink::WebSecurityOrigin& origin,
433 const blink::WebURL& resource_url) {
434 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY);
435
436 std::string origin_host(origin.host().utf8());
437 GURL frame_gurl(frame->document().url());
438 if (IsHostInDomain(origin_host, kGoogleDotCom)) {
439 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE);
440 if (StartsWithASCII(frame_gurl.path(), kGoogleSupportPathPrefix, false)) {
441 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_SUPPORT);
442 } else if (StartsWithASCII(frame_gurl.path(),
443 kGoogleIntlPathPrefix,
444 false)) {
445 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_INTL);
446 }
447 }
448
449 if (origin_host == kWWWDotGoogleDotCom) {
450 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_WWW_GOOGLE);
451 if (StartsWithASCII(frame_gurl.path(), kGoogleReaderPathPrefix, false))
452 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GOOGLE_READER);
453 } else if (origin_host == kMailDotGoogleDotCom) {
454 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_MAIL_GOOGLE);
455 } else if (origin_host == kPlusDotGoogleDotCom) {
456 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_PLUS_GOOGLE);
457 } else if (origin_host == kDocsDotGoogleDotCom) {
458 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_DOCS_GOOGLE);
459 } else if (origin_host == kSitesDotGoogleDotCom) {
460 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_SITES_GOOGLE);
461 } else if (origin_host == kPicasawebDotGoogleDotCom) {
462 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_PICASAWEB_GOOGLE);
463 } else if (origin_host == kCodeDotGoogleDotCom) {
464 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_CODE_GOOGLE);
465 } else if (origin_host == kGroupsDotGoogleDotCom) {
466 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_GROUPS_GOOGLE);
467 } else if (origin_host == kMapsDotGoogleDotCom) {
468 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_MAPS_GOOGLE);
469 } else if (origin_host == kWWWDotYoutubeDotCom) {
470 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HOST_YOUTUBE);
471 }
472
473 GURL resource_gurl(resource_url);
474 if (EndsWith(resource_gurl.path(), kDotHTML, false))
475 SendInsecureContentSignal(INSECURE_CONTENT_DISPLAY_HTML);
476
477 if (allowed_per_settings || allow_displaying_insecure_content_)
478 return true;
479
480 Send(new ChromeViewHostMsg_DidBlockDisplayingInsecureContent(routing_id()));
481
482 return false;
483 }
484
allowRunningInsecureContent(blink::WebFrame * frame,bool allowed_per_settings,const blink::WebSecurityOrigin & origin,const blink::WebURL & resource_url)485 bool ContentSettingsObserver::allowRunningInsecureContent(
486 blink::WebFrame* frame,
487 bool allowed_per_settings,
488 const blink::WebSecurityOrigin& origin,
489 const blink::WebURL& resource_url) {
490 std::string origin_host(origin.host().utf8());
491 GURL frame_gurl(frame->document().url());
492 DCHECK_EQ(frame_gurl.host(), origin_host);
493
494 bool is_google = IsHostInDomain(origin_host, kGoogleDotCom);
495 if (is_google) {
496 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE);
497 if (StartsWithASCII(frame_gurl.path(), kGoogleSupportPathPrefix, false)) {
498 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_SUPPORT);
499 } else if (StartsWithASCII(frame_gurl.path(),
500 kGoogleIntlPathPrefix,
501 false)) {
502 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_INTL);
503 }
504 }
505
506 if (origin_host == kWWWDotGoogleDotCom) {
507 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_WWW_GOOGLE);
508 if (StartsWithASCII(frame_gurl.path(), kGoogleReaderPathPrefix, false))
509 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLE_READER);
510 } else if (origin_host == kMailDotGoogleDotCom) {
511 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_MAIL_GOOGLE);
512 } else if (origin_host == kPlusDotGoogleDotCom) {
513 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_PLUS_GOOGLE);
514 } else if (origin_host == kDocsDotGoogleDotCom) {
515 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_DOCS_GOOGLE);
516 } else if (origin_host == kSitesDotGoogleDotCom) {
517 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_SITES_GOOGLE);
518 } else if (origin_host == kPicasawebDotGoogleDotCom) {
519 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_PICASAWEB_GOOGLE);
520 } else if (origin_host == kCodeDotGoogleDotCom) {
521 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_CODE_GOOGLE);
522 } else if (origin_host == kGroupsDotGoogleDotCom) {
523 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GROUPS_GOOGLE);
524 } else if (origin_host == kMapsDotGoogleDotCom) {
525 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_MAPS_GOOGLE);
526 } else if (origin_host == kWWWDotYoutubeDotCom) {
527 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_YOUTUBE);
528 } else if (EndsWith(origin_host, kDotGoogleUserContentDotCom, false)) {
529 SendInsecureContentSignal(INSECURE_CONTENT_RUN_HOST_GOOGLEUSERCONTENT);
530 }
531
532 GURL resource_gurl(resource_url);
533 if (resource_gurl.host() == kWWWDotYoutubeDotCom)
534 SendInsecureContentSignal(INSECURE_CONTENT_RUN_TARGET_YOUTUBE);
535
536 if (EndsWith(resource_gurl.path(), kDotJS, false))
537 SendInsecureContentSignal(INSECURE_CONTENT_RUN_JS);
538 else if (EndsWith(resource_gurl.path(), kDotCSS, false))
539 SendInsecureContentSignal(INSECURE_CONTENT_RUN_CSS);
540 else if (EndsWith(resource_gurl.path(), kDotSWF, false))
541 SendInsecureContentSignal(INSECURE_CONTENT_RUN_SWF);
542
543 if (!allow_running_insecure_content_ && !allowed_per_settings) {
544 DidBlockContentType(CONTENT_SETTINGS_TYPE_MIXEDSCRIPT);
545 return false;
546 }
547
548 return true;
549 }
550
allowWebGLDebugRendererInfo(WebFrame * frame)551 bool ContentSettingsObserver::allowWebGLDebugRendererInfo(WebFrame* frame) {
552 bool allowed = false;
553 Send(new ChromeViewHostMsg_IsWebGLDebugRendererInfoAllowed(
554 routing_id(),
555 GURL(frame->top()->document().securityOrigin().toString().utf8()),
556 &allowed));
557 return allowed;
558 }
559
didNotAllowPlugins(WebFrame * frame)560 void ContentSettingsObserver::didNotAllowPlugins(WebFrame* frame) {
561 DidBlockContentType(CONTENT_SETTINGS_TYPE_PLUGINS);
562 }
563
didNotAllowScript(WebFrame * frame)564 void ContentSettingsObserver::didNotAllowScript(WebFrame* frame) {
565 DidBlockContentType(CONTENT_SETTINGS_TYPE_JAVASCRIPT);
566 }
567
AreNPAPIPluginsBlocked() const568 bool ContentSettingsObserver::AreNPAPIPluginsBlocked() const {
569 return npapi_plugins_blocked_;
570 }
571
OnLoadBlockedPlugins(const std::string & identifier)572 void ContentSettingsObserver::OnLoadBlockedPlugins(
573 const std::string& identifier) {
574 temporarily_allowed_plugins_.insert(identifier);
575 }
576
OnSetAsInterstitial()577 void ContentSettingsObserver::OnSetAsInterstitial() {
578 is_interstitial_page_ = true;
579 }
580
OnNPAPINotSupported()581 void ContentSettingsObserver::OnNPAPINotSupported() {
582 npapi_plugins_blocked_ = true;
583 }
584
OnSetAllowDisplayingInsecureContent(bool allow)585 void ContentSettingsObserver::OnSetAllowDisplayingInsecureContent(bool allow) {
586 allow_displaying_insecure_content_ = allow;
587 WebFrame* main_frame = render_view()->GetWebView()->mainFrame();
588 if (main_frame)
589 main_frame->reload();
590 }
591
OnSetAllowRunningInsecureContent(bool allow)592 void ContentSettingsObserver::OnSetAllowRunningInsecureContent(bool allow) {
593 allow_running_insecure_content_ = allow;
594 OnSetAllowDisplayingInsecureContent(allow);
595 }
596
597
ClearBlockedContentSettings()598 void ContentSettingsObserver::ClearBlockedContentSettings() {
599 for (size_t i = 0; i < arraysize(content_blocked_); ++i)
600 content_blocked_[i] = false;
601 cached_storage_permissions_.clear();
602 cached_script_permissions_.clear();
603 }
604
GetExtension(const WebSecurityOrigin & origin) const605 const extensions::Extension* ContentSettingsObserver::GetExtension(
606 const WebSecurityOrigin& origin) const {
607 if (!EqualsASCII(origin.protocol(), extensions::kExtensionScheme))
608 return NULL;
609
610 const std::string extension_id = origin.host().utf8().data();
611 if (!extension_dispatcher_->IsExtensionActive(extension_id))
612 return NULL;
613
614 return extension_dispatcher_->extensions()->GetByID(extension_id);
615 }
616
IsWhitelistedForContentSettings(WebFrame * frame)617 bool ContentSettingsObserver::IsWhitelistedForContentSettings(WebFrame* frame) {
618 // Whitelist Instant processes.
619 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kInstantProcess))
620 return true;
621
622 // Whitelist ftp directory listings, as they require JavaScript to function
623 // properly.
624 webkit_glue::WebURLResponseExtraDataImpl* extra_data =
625 static_cast<webkit_glue::WebURLResponseExtraDataImpl*>(
626 frame->dataSource()->response().extraData());
627 if (extra_data && extra_data->is_ftp_directory_listing())
628 return true;
629 return IsWhitelistedForContentSettings(frame->document().securityOrigin(),
630 frame->document().url());
631 }
632
IsWhitelistedForContentSettings(const WebSecurityOrigin & origin,const GURL & document_url)633 bool ContentSettingsObserver::IsWhitelistedForContentSettings(
634 const WebSecurityOrigin& origin,
635 const GURL& document_url) {
636 if (document_url == GURL(content::kUnreachableWebDataURL))
637 return true;
638
639 if (origin.isUnique())
640 return false; // Uninitialized document?
641
642 if (EqualsASCII(origin.protocol(), chrome::kChromeUIScheme))
643 return true; // Browser UI elements should still work.
644
645 if (EqualsASCII(origin.protocol(), chrome::kChromeDevToolsScheme))
646 return true; // DevTools UI elements should still work.
647
648 if (EqualsASCII(origin.protocol(), extensions::kExtensionScheme))
649 return true;
650
651 // TODO(creis, fsamuel): Remove this once the concept of swapped out
652 // RenderViews goes away.
653 if (document_url == GURL(content::kSwappedOutURL))
654 return true;
655
656 // If the scheme is file:, an empty file name indicates a directory listing,
657 // which requires JavaScript to function properly.
658 if (EqualsASCII(origin.protocol(), chrome::kFileScheme)) {
659 return document_url.SchemeIs(chrome::kFileScheme) &&
660 document_url.ExtractFileName().empty();
661 }
662
663 return false;
664 }
665