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_agent_impl.h"
6 
7 #include <utility>
8 #include <vector>
9 
10 #include "base/bind.h"
11 #include "base/feature_list.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "chrome/common/chrome_features.h"
15 #include "chrome/common/client_hints/client_hints.h"
16 #include "chrome/common/render_messages.h"
17 #include "chrome/common/ssl_insecure_content.h"
18 #include "components/content_settings/core/common/content_settings.h"
19 #include "components/content_settings/core/common/content_settings.mojom.h"
20 #include "components/content_settings/core/common/content_settings_pattern.h"
21 #include "components/content_settings/core/common/content_settings_utils.h"
22 #include "content/public/child/child_thread.h"
23 #include "content/public/common/client_hints.mojom.h"
24 #include "content/public/common/origin_util.h"
25 #include "content/public/common/previews_state.h"
26 #include "content/public/common/url_constants.h"
27 #include "content/public/renderer/document_state.h"
28 #include "content/public/renderer/render_frame.h"
29 #include "content/public/renderer/render_view.h"
30 #include "extensions/buildflags/buildflags.h"
31 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
32 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
33 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
34 #include "third_party/blink/public/platform/url_conversion.h"
35 #include "third_party/blink/public/platform/web_client_hints_type.h"
36 #include "third_party/blink/public/platform/web_security_origin.h"
37 #include "third_party/blink/public/platform/web_url.h"
38 #include "third_party/blink/public/web/web_document.h"
39 #include "third_party/blink/public/web/web_local_frame.h"
40 #include "third_party/blink/public/web/web_local_frame_client.h"
41 #include "third_party/blink/public/web/web_view.h"
42 #include "url/gurl.h"
43 #include "url/origin.h"
44 #include "url/url_constants.h"
45 
46 #if BUILDFLAG(ENABLE_EXTENSIONS)
47 #include "extensions/common/constants.h"
48 #include "extensions/common/extension.h"
49 #include "extensions/common/permissions/api_permission.h"
50 #include "extensions/common/permissions/permissions_data.h"
51 #include "extensions/renderer/dispatcher.h"
52 #include "extensions/renderer/renderer_extension_registry.h"
53 #endif
54 
55 using blink::WebDocument;
56 using blink::WebFrame;
57 using blink::WebLocalFrame;
58 using blink::WebSecurityOrigin;
59 using blink::WebString;
60 using blink::WebURL;
61 using blink::WebView;
62 using content::DocumentState;
63 
64 namespace {
65 
GetOriginOrURL(const WebFrame * frame)66 GURL GetOriginOrURL(const WebFrame* frame) {
67   url::Origin top_origin = url::Origin(frame->Top()->GetSecurityOrigin());
68   // The |top_origin| is unique ("null") e.g., for file:// URLs. Use the
69   // document URL as the primary URL in those cases.
70   // TODO(alexmos): This is broken for --site-per-process, since top() can be a
71   // WebRemoteFrame which does not have a document(), and the WebRemoteFrame's
72   // URL is not replicated.  See https://crbug.com/628759.
73   if (top_origin.opaque() && frame->Top()->IsWebLocalFrame())
74     return frame->Top()->ToWebLocalFrame()->GetDocument().Url();
75   return top_origin.GetURL();
76 }
77 
78 // Allow passing both WebURL and GURL here, so that we can early return without
79 // allocating a new backing string if only the default rule matches.
80 template <typename URL>
GetContentSettingFromRules(const ContentSettingsForOneType & rules,const WebFrame * frame,const URL & secondary_url)81 ContentSetting GetContentSettingFromRules(
82     const ContentSettingsForOneType& rules,
83     const WebFrame* frame,
84     const URL& secondary_url) {
85   // If there is only one rule, it's the default rule and we don't need to match
86   // the patterns.
87   if (rules.size() == 1) {
88     DCHECK(rules[0].primary_pattern == ContentSettingsPattern::Wildcard());
89     DCHECK(rules[0].secondary_pattern == ContentSettingsPattern::Wildcard());
90     return rules[0].GetContentSetting();
91   }
92   const GURL& primary_url = GetOriginOrURL(frame);
93   const GURL& secondary_gurl = secondary_url;
94   for (const auto& rule : rules) {
95     if (rule.primary_pattern.Matches(primary_url) &&
96         rule.secondary_pattern.Matches(secondary_gurl)) {
97       return rule.GetContentSetting();
98     }
99   }
100   NOTREACHED();
101   return CONTENT_SETTING_DEFAULT;
102 }
103 
IsScriptDisabledForPreview(content::RenderFrame * render_frame)104 bool IsScriptDisabledForPreview(content::RenderFrame* render_frame) {
105   return render_frame->GetPreviewsState() & content::NOSCRIPT_ON;
106 }
107 
IsFrameWithOpaqueOrigin(WebFrame * frame)108 bool IsFrameWithOpaqueOrigin(WebFrame* frame) {
109   // Storage access is keyed off the top origin and the frame's origin.
110   // It will be denied any opaque origins so have this method to return early
111   // instead of making a Sync IPC call.
112   return frame->GetSecurityOrigin().IsOpaque() ||
113          frame->Top()->GetSecurityOrigin().IsOpaque();
114 }
115 
116 }  // namespace
117 
ContentSettingsAgentImpl(content::RenderFrame * render_frame,bool should_whitelist,service_manager::BinderRegistry * registry)118 ContentSettingsAgentImpl::ContentSettingsAgentImpl(
119     content::RenderFrame* render_frame,
120     bool should_whitelist,
121     service_manager::BinderRegistry* registry)
122     : content::RenderFrameObserver(render_frame),
123       content::RenderFrameObserverTracker<ContentSettingsAgentImpl>(
124           render_frame),
125       should_whitelist_(should_whitelist) {
126   ClearBlockedContentSettings();
127   render_frame->GetWebFrame()->SetContentSettingsClient(this);
128 
129   render_frame->GetAssociatedInterfaceRegistry()->AddInterface(
130       base::Bind(&ContentSettingsAgentImpl::OnContentSettingsAgentRequest,
131                  base::Unretained(this)));
132 
133   content::RenderFrame* main_frame =
134       render_frame->GetRenderView()->GetMainRenderFrame();
135   // TODO(nasko): The main frame is not guaranteed to be in the same process
136   // with this frame with --site-per-process. This code needs to be updated
137   // to handle this case. See https://crbug.com/496670.
138   if (main_frame && main_frame != render_frame) {
139     // Copy all the settings from the main render frame to avoid race conditions
140     // when initializing this data. See https://crbug.com/333308.
141     ContentSettingsAgentImpl* parent =
142         ContentSettingsAgentImpl::Get(main_frame);
143     allow_running_insecure_content_ = parent->allow_running_insecure_content_;
144     temporarily_allowed_plugins_ = parent->temporarily_allowed_plugins_;
145     is_interstitial_page_ = parent->is_interstitial_page_;
146   }
147 }
148 
~ContentSettingsAgentImpl()149 ContentSettingsAgentImpl::~ContentSettingsAgentImpl() {}
150 
151 chrome::mojom::ContentSettingsManager&
GetContentSettingsManager()152 ContentSettingsAgentImpl::GetContentSettingsManager() {
153   if (!content_settings_manager_)
154     BindContentSettingsManager(&content_settings_manager_);
155   return *content_settings_manager_;
156 }
157 
158 #if BUILDFLAG(ENABLE_EXTENSIONS)
SetExtensionDispatcher(extensions::Dispatcher * extension_dispatcher)159 void ContentSettingsAgentImpl::SetExtensionDispatcher(
160     extensions::Dispatcher* extension_dispatcher) {
161   DCHECK(!extension_dispatcher_)
162       << "SetExtensionDispatcher() should only be called once.";
163   extension_dispatcher_ = extension_dispatcher;
164 }
165 #endif
166 
SetContentSettingRules(const RendererContentSettingRules * content_setting_rules)167 void ContentSettingsAgentImpl::SetContentSettingRules(
168     const RendererContentSettingRules* content_setting_rules) {
169   content_setting_rules_ = content_setting_rules;
170   UMA_HISTOGRAM_COUNTS_1M("ClientHints.CountRulesReceived",
171                           content_setting_rules_->client_hints_rules.size());
172 }
173 
174 const RendererContentSettingRules*
GetContentSettingRules()175 ContentSettingsAgentImpl::GetContentSettingRules() {
176   return content_setting_rules_;
177 }
178 
IsPluginTemporarilyAllowed(const std::string & identifier)179 bool ContentSettingsAgentImpl::IsPluginTemporarilyAllowed(
180     const std::string& identifier) {
181   // If the empty string is in here, it means all plugins are allowed.
182   // TODO(bauerb): Remove this once we only pass in explicit identifiers.
183   return base::Contains(temporarily_allowed_plugins_, identifier) ||
184          base::Contains(temporarily_allowed_plugins_, std::string());
185 }
186 
DidBlockContentType(ContentSettingsType settings_type)187 void ContentSettingsAgentImpl::DidBlockContentType(
188     ContentSettingsType settings_type) {
189   bool newly_blocked = content_blocked_.insert(settings_type).second;
190   if (newly_blocked)
191     GetContentSettingsManager().OnContentBlocked(routing_id(), settings_type);
192 }
193 
BindContentSettingsManager(mojo::Remote<chrome::mojom::ContentSettingsManager> * manager)194 void ContentSettingsAgentImpl::BindContentSettingsManager(
195     mojo::Remote<chrome::mojom::ContentSettingsManager>* manager) {
196   DCHECK(!*manager);
197   content::ChildThread::Get()->BindHostReceiver(
198       manager->BindNewPipeAndPassReceiver());
199 }
200 
OnMessageReceived(const IPC::Message & message)201 bool ContentSettingsAgentImpl::OnMessageReceived(const IPC::Message& message) {
202   // Don't swallow LoadBlockedPlugins messages, as they're sent to every
203   // blocked plugin.
204   IPC_BEGIN_MESSAGE_MAP(ContentSettingsAgentImpl, message)
205     IPC_MESSAGE_HANDLER(ChromeViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins)
206   IPC_END_MESSAGE_MAP()
207   return false;
208 }
209 
DidCommitProvisionalLoad(bool is_same_document_navigation,ui::PageTransition transition)210 void ContentSettingsAgentImpl::DidCommitProvisionalLoad(
211     bool is_same_document_navigation,
212     ui::PageTransition transition) {
213   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
214   if (frame->Parent())
215     return;  // Not a top-level navigation.
216 
217   if (!is_same_document_navigation) {
218     // Clear "block" flags for the new page. This needs to happen before any of
219     // |allowScript()|, |allowScriptFromSource()|, |allowImage()|, or
220     // |allowPlugins()| is called for the new page so that these functions can
221     // correctly detect that a piece of content flipped from "not blocked" to
222     // "blocked".
223     ClearBlockedContentSettings();
224     temporarily_allowed_plugins_.clear();
225 
226     // The BrowserInterfaceBroker is reset on navigation, so we will need to
227     // re-acquire the ContentSettingsManager.
228     content_settings_manager_.reset();
229   }
230 
231   GURL url = frame->GetDocument().Url();
232   // If we start failing this DCHECK, please makes sure we don't regress
233   // this bug: http://code.google.com/p/chromium/issues/detail?id=79304
234   DCHECK(frame->GetDocument().GetSecurityOrigin().ToString() == "null" ||
235          !url.SchemeIs(url::kDataScheme));
236 }
237 
OnDestruct()238 void ContentSettingsAgentImpl::OnDestruct() {
239   delete this;
240 }
241 
SetAllowRunningInsecureContent()242 void ContentSettingsAgentImpl::SetAllowRunningInsecureContent() {
243   allow_running_insecure_content_ = true;
244 
245   // Reload if we are the main frame.
246   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
247   if (!frame->Parent())
248     frame->StartReload(blink::WebFrameLoadType::kReload);
249 }
250 
SetAsInterstitial()251 void ContentSettingsAgentImpl::SetAsInterstitial() {
252   is_interstitial_page_ = true;
253 }
254 
SetDisabledMixedContentUpgrades()255 void ContentSettingsAgentImpl::SetDisabledMixedContentUpgrades() {
256   mixed_content_autoupgrades_disabled_ = true;
257 }
258 
OnContentSettingsAgentRequest(mojo::PendingAssociatedReceiver<chrome::mojom::ContentSettingsAgent> receiver)259 void ContentSettingsAgentImpl::OnContentSettingsAgentRequest(
260     mojo::PendingAssociatedReceiver<chrome::mojom::ContentSettingsAgent>
261         receiver) {
262   receivers_.Add(this, std::move(receiver));
263 }
264 
AllowDatabase()265 bool ContentSettingsAgentImpl::AllowDatabase() {
266   return AllowStorageAccess(
267       chrome::mojom::ContentSettingsManager::StorageType::DATABASE);
268 }
269 
RequestFileSystemAccessAsync(base::OnceCallback<void (bool)> callback)270 void ContentSettingsAgentImpl::RequestFileSystemAccessAsync(
271     base::OnceCallback<void(bool)> callback) {
272   WebLocalFrame* frame = render_frame()->GetWebFrame();
273   if (IsFrameWithOpaqueOrigin(frame)) {
274     std::move(callback).Run(false);
275     return;
276   }
277 
278   GetContentSettingsManager().AllowStorageAccess(
279       routing_id(),
280       chrome::mojom::ContentSettingsManager::StorageType::FILE_SYSTEM,
281       frame->GetSecurityOrigin(),
282       frame->GetDocument().SiteForCookies().RepresentativeUrl(),
283       frame->GetDocument().TopFrameOrigin(), std::move(callback));
284 }
285 
AllowImage(bool enabled_per_settings,const WebURL & image_url)286 bool ContentSettingsAgentImpl::AllowImage(bool enabled_per_settings,
287                                           const WebURL& image_url) {
288   bool allow = enabled_per_settings;
289   if (enabled_per_settings) {
290     if (is_interstitial_page_)
291       return true;
292 
293     if (IsWhitelistedForContentSettings())
294       return true;
295 
296     if (content_setting_rules_) {
297       allow = GetContentSettingFromRules(content_setting_rules_->image_rules,
298                                          render_frame()->GetWebFrame(),
299                                          image_url) != CONTENT_SETTING_BLOCK;
300     }
301   }
302   if (!allow)
303     DidBlockContentType(ContentSettingsType::IMAGES);
304   return allow;
305 }
306 
AllowIndexedDB()307 bool ContentSettingsAgentImpl::AllowIndexedDB() {
308   return AllowStorageAccess(
309       chrome::mojom::ContentSettingsManager::StorageType::INDEXED_DB);
310 }
311 
AllowCacheStorage()312 bool ContentSettingsAgentImpl::AllowCacheStorage() {
313   return AllowStorageAccess(
314       chrome::mojom::ContentSettingsManager::StorageType::CACHE);
315 }
316 
AllowWebLocks()317 bool ContentSettingsAgentImpl::AllowWebLocks() {
318   return AllowStorageAccess(
319       chrome::mojom::ContentSettingsManager::StorageType::WEB_LOCKS);
320 }
321 
AllowScript(bool enabled_per_settings)322 bool ContentSettingsAgentImpl::AllowScript(bool enabled_per_settings) {
323   if (!enabled_per_settings)
324     return false;
325   if (IsScriptDisabledForPreview(render_frame()))
326     return false;
327   if (is_interstitial_page_)
328     return true;
329 
330   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
331   const auto it = cached_script_permissions_.find(frame);
332   if (it != cached_script_permissions_.end())
333     return it->second;
334 
335   // Evaluate the content setting rules before
336   // IsWhitelistedForContentSettings(); if there is only the default rule
337   // allowing all scripts, it's quicker this way.
338   bool allow = true;
339   if (content_setting_rules_) {
340     ContentSetting setting = GetContentSettingFromRules(
341         content_setting_rules_->script_rules, frame,
342         url::Origin(frame->GetDocument().GetSecurityOrigin()).GetURL());
343     allow = setting != CONTENT_SETTING_BLOCK;
344   }
345   allow = allow || IsWhitelistedForContentSettings();
346 
347   cached_script_permissions_[frame] = allow;
348   return allow;
349 }
350 
AllowScriptFromSource(bool enabled_per_settings,const blink::WebURL & script_url)351 bool ContentSettingsAgentImpl::AllowScriptFromSource(
352     bool enabled_per_settings,
353     const blink::WebURL& script_url) {
354   if (!enabled_per_settings)
355     return false;
356   if (IsScriptDisabledForPreview(render_frame()))
357     return false;
358   if (is_interstitial_page_)
359     return true;
360 
361   bool allow = true;
362   if (content_setting_rules_) {
363     ContentSetting setting =
364         GetContentSettingFromRules(content_setting_rules_->script_rules,
365                                    render_frame()->GetWebFrame(), script_url);
366     allow = setting != CONTENT_SETTING_BLOCK;
367   }
368   return allow || IsWhitelistedForContentSettings();
369 }
370 
AllowStorage(bool local)371 bool ContentSettingsAgentImpl::AllowStorage(bool local) {
372   WebLocalFrame* frame = render_frame()->GetWebFrame();
373   if (IsFrameWithOpaqueOrigin(frame))
374     return false;
375 
376   StoragePermissionsKey key(
377       url::Origin(frame->GetDocument().GetSecurityOrigin()).GetURL(), local);
378   const auto permissions = cached_storage_permissions_.find(key);
379   if (permissions != cached_storage_permissions_.end())
380     return permissions->second;
381 
382   bool result = false;
383   GetContentSettingsManager().AllowStorageAccess(
384       routing_id(),
385       local
386           ? chrome::mojom::ContentSettingsManager::StorageType::LOCAL_STORAGE
387           : chrome::mojom::ContentSettingsManager::StorageType::SESSION_STORAGE,
388       frame->GetSecurityOrigin(),
389       frame->GetDocument().SiteForCookies().RepresentativeUrl(),
390       frame->GetDocument().TopFrameOrigin(), &result);
391   cached_storage_permissions_[key] = result;
392   return result;
393 }
394 
AllowReadFromClipboard(bool default_value)395 bool ContentSettingsAgentImpl::AllowReadFromClipboard(bool default_value) {
396   bool allowed = default_value;
397 #if BUILDFLAG(ENABLE_EXTENSIONS)
398   extensions::ScriptContext* current_context =
399       extension_dispatcher_->script_context_set().GetCurrent();
400   if (current_context) {
401     allowed |= current_context->HasAPIPermission(
402         extensions::APIPermission::kClipboardRead);
403   }
404 #endif
405   return allowed;
406 }
407 
AllowWriteToClipboard(bool default_value)408 bool ContentSettingsAgentImpl::AllowWriteToClipboard(bool default_value) {
409   bool allowed = default_value;
410 #if BUILDFLAG(ENABLE_EXTENSIONS)
411   // All blessed extension pages could historically write to the clipboard, so
412   // preserve that for compatibility.
413   extensions::ScriptContext* current_context =
414       extension_dispatcher_->script_context_set().GetCurrent();
415   if (current_context) {
416     if (current_context->effective_context_type() ==
417             extensions::Feature::BLESSED_EXTENSION_CONTEXT &&
418         !current_context->IsForServiceWorker()) {
419       allowed = true;
420     } else {
421       allowed |= current_context->HasAPIPermission(
422           extensions::APIPermission::kClipboardWrite);
423     }
424   }
425 #endif
426   return allowed;
427 }
428 
AllowMutationEvents(bool default_value)429 bool ContentSettingsAgentImpl::AllowMutationEvents(bool default_value) {
430   return IsPlatformApp() ? false : default_value;
431 }
432 
AllowRunningInsecureContent(bool allowed_per_settings,const blink::WebURL & resource_url)433 bool ContentSettingsAgentImpl::AllowRunningInsecureContent(
434     bool allowed_per_settings,
435     const blink::WebURL& resource_url) {
436   bool allow = allowed_per_settings;
437 
438   if (base::FeatureList::IsEnabled(features::kMixedContentSiteSetting)) {
439     if (content_setting_rules_) {
440       auto setting = GetContentSettingFromRules(
441           content_setting_rules_->mixed_content_rules,
442           render_frame()->GetWebFrame(), GURL());
443       allow |= (setting == CONTENT_SETTING_ALLOW);
444     }
445   } else {
446     allow |= allow_running_insecure_content_;
447     if (!allow) {
448       DidBlockContentType(ContentSettingsType::MIXEDSCRIPT);
449     }
450   }
451 
452   // Note: this implementation is a mirror of
453   // Browser::ShouldAllowRunningInsecureContent.
454   FilteredReportInsecureContentRan(GURL(resource_url));
455 
456   return allow;
457 }
458 
AllowPopupsAndRedirects(bool default_value)459 bool ContentSettingsAgentImpl::AllowPopupsAndRedirects(bool default_value) {
460   if (!content_setting_rules_)
461     return default_value;
462   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
463   return GetContentSettingFromRules(
464              content_setting_rules_->popup_redirect_rules, frame,
465              url::Origin(frame->GetDocument().GetSecurityOrigin()).GetURL()) ==
466          CONTENT_SETTING_ALLOW;
467 }
468 
PassiveInsecureContentFound(const blink::WebURL & resource_url)469 void ContentSettingsAgentImpl::PassiveInsecureContentFound(
470     const blink::WebURL& resource_url) {
471   // Note: this implementation is a mirror of
472   // Browser::PassiveInsecureContentFound.
473   ReportInsecureContent(SslInsecureContentType::DISPLAY);
474   FilteredReportInsecureContentDisplayed(GURL(resource_url));
475 }
476 
PersistClientHints(const blink::WebEnabledClientHints & enabled_client_hints,base::TimeDelta duration,const blink::WebURL & url)477 void ContentSettingsAgentImpl::PersistClientHints(
478     const blink::WebEnabledClientHints& enabled_client_hints,
479     base::TimeDelta duration,
480     const blink::WebURL& url) {
481   if (duration <= base::TimeDelta())
482     return;
483 
484   const GURL primary_url(url);
485   const url::Origin primary_origin = url::Origin::Create(primary_url);
486   if (!content::IsOriginSecure(primary_url))
487     return;
488 
489   // TODO(tbansal): crbug.com/735518. Determine if the value should be
490   // merged or overridden. Also, determine if the merger should happen on the
491   // browser side or the renderer. If the value needs to be overridden,
492   // this method should not return early if |update_count| is 0.
493   std::vector<::blink::mojom::WebClientHintsType> client_hints;
494   static constexpr size_t kWebClientHintsCount =
495       static_cast<size_t>(blink::mojom::WebClientHintsType::kMaxValue) + 1;
496   client_hints.reserve(kWebClientHintsCount);
497 
498   for (size_t i = 0; i < kWebClientHintsCount; ++i) {
499     if (enabled_client_hints.IsEnabled(
500             static_cast<blink::mojom::WebClientHintsType>(i))) {
501       client_hints.push_back(static_cast<blink::mojom::WebClientHintsType>(i));
502     }
503   }
504   size_t update_count = client_hints.size();
505   if (update_count == 0)
506     return;
507 
508   UMA_HISTOGRAM_CUSTOM_TIMES(
509       "ClientHints.PersistDuration", duration, base::TimeDelta::FromSeconds(1),
510       // TODO(crbug.com/949034): Rename and fix this histogram to have some
511       // intended max value. We throw away the 32 most-significant bits of the
512       // 64-bit time delta in milliseconds. Before it happened silently in
513       // histogram.cc, now it is explicit here. The previous value of 365 days
514       // effectively turns into roughly 17 days when getting cast to int.
515       base::TimeDelta::FromMilliseconds(
516           static_cast<int>(base::TimeDelta::FromDays(365).InMilliseconds())),
517       100);
518 
519   UMA_HISTOGRAM_COUNTS_100("ClientHints.UpdateSize", update_count);
520 
521   // Notify the embedder.
522   mojo::AssociatedRemote<client_hints::mojom::ClientHints> host_observer;
523   render_frame()->GetRemoteAssociatedInterfaces()->GetInterface(&host_observer);
524   host_observer->PersistClientHints(primary_origin, std::move(client_hints),
525                                     duration);
526 }
527 
GetAllowedClientHintsFromSource(const blink::WebURL & url,blink::WebEnabledClientHints * client_hints) const528 void ContentSettingsAgentImpl::GetAllowedClientHintsFromSource(
529     const blink::WebURL& url,
530     blink::WebEnabledClientHints* client_hints) const {
531   if (!content_setting_rules_)
532     return;
533 
534   if (content_setting_rules_->client_hints_rules.empty())
535     return;
536 
537   client_hints::GetAllowedClientHintsFromSource(
538       url, content_setting_rules_->client_hints_rules, client_hints);
539 }
540 
ShouldAutoupgradeMixedContent()541 bool ContentSettingsAgentImpl::ShouldAutoupgradeMixedContent() {
542   if (mixed_content_autoupgrades_disabled_)
543     return false;
544 
545   if (content_setting_rules_) {
546     auto setting =
547         GetContentSettingFromRules(content_setting_rules_->mixed_content_rules,
548                                    render_frame()->GetWebFrame(), GURL());
549     return setting != CONTENT_SETTING_ALLOW;
550   }
551   return false;
552 }
553 
DidNotAllowPlugins()554 void ContentSettingsAgentImpl::DidNotAllowPlugins() {
555   DidBlockContentType(ContentSettingsType::PLUGINS);
556 }
557 
DidNotAllowScript()558 void ContentSettingsAgentImpl::DidNotAllowScript() {
559   DidBlockContentType(ContentSettingsType::JAVASCRIPT);
560 }
561 
OnLoadBlockedPlugins(const std::string & identifier)562 void ContentSettingsAgentImpl::OnLoadBlockedPlugins(
563     const std::string& identifier) {
564   temporarily_allowed_plugins_.insert(identifier);
565 }
566 
ClearBlockedContentSettings()567 void ContentSettingsAgentImpl::ClearBlockedContentSettings() {
568   content_blocked_.clear();
569   cached_storage_permissions_.clear();
570   cached_script_permissions_.clear();
571 }
572 
IsPlatformApp()573 bool ContentSettingsAgentImpl::IsPlatformApp() {
574 #if BUILDFLAG(ENABLE_EXTENSIONS)
575   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
576   WebSecurityOrigin origin = frame->GetDocument().GetSecurityOrigin();
577   const extensions::Extension* extension = GetExtension(origin);
578   return extension && extension->is_platform_app();
579 #else
580   return false;
581 #endif
582 }
583 
584 #if BUILDFLAG(ENABLE_EXTENSIONS)
GetExtension(const WebSecurityOrigin & origin) const585 const extensions::Extension* ContentSettingsAgentImpl::GetExtension(
586     const WebSecurityOrigin& origin) const {
587   if (origin.Protocol().Ascii() != extensions::kExtensionScheme)
588     return nullptr;
589 
590   const std::string extension_id = origin.Host().Utf8().data();
591   if (!extension_dispatcher_->IsExtensionActive(extension_id))
592     return nullptr;
593 
594   return extensions::RendererExtensionRegistry::Get()->GetByID(extension_id);
595 }
596 #endif
597 
598 // static
IsWhitelistedForContentSettings() const599 bool ContentSettingsAgentImpl::IsWhitelistedForContentSettings() const {
600   if (should_whitelist_)
601     return true;
602 
603   // Whitelist ftp directory listings, as they require JavaScript to function
604   // properly.
605   if (render_frame()->IsFTPDirectoryListing())
606     return true;
607 
608   const WebDocument& document = render_frame()->GetWebFrame()->GetDocument();
609   return IsWhitelistedForContentSettings(document.GetSecurityOrigin(),
610                                          document.Url());
611 }
612 
IsWhitelistedForContentSettings(const WebSecurityOrigin & origin,const WebURL & document_url)613 bool ContentSettingsAgentImpl::IsWhitelistedForContentSettings(
614     const WebSecurityOrigin& origin,
615     const WebURL& document_url) {
616   if (document_url.GetString() == content::kUnreachableWebDataURL)
617     return true;
618 
619   if (origin.IsOpaque())
620     return false;  // Uninitialized document?
621 
622   blink::WebString protocol = origin.Protocol();
623 
624   if (protocol == content::kChromeUIScheme)
625     return true;  // Browser UI elements should still work.
626 
627   if (protocol == content::kChromeDevToolsScheme)
628     return true;  // DevTools UI elements should still work.
629 
630 #if BUILDFLAG(ENABLE_EXTENSIONS)
631   if (protocol == extensions::kExtensionScheme)
632     return true;
633 #endif
634 
635   // If the scheme is file:, an empty file name indicates a directory listing,
636   // which requires JavaScript to function properly.
637   if (protocol == url::kFileScheme &&
638       document_url.ProtocolIs(url::kFileScheme)) {
639     return GURL(document_url).ExtractFileName().empty();
640   }
641   return false;
642 }
643 
AllowStorageAccess(chrome::mojom::ContentSettingsManager::StorageType storage_type)644 bool ContentSettingsAgentImpl::AllowStorageAccess(
645     chrome::mojom::ContentSettingsManager::StorageType storage_type) {
646   WebLocalFrame* frame = render_frame()->GetWebFrame();
647   if (IsFrameWithOpaqueOrigin(frame))
648     return false;
649 
650   bool result = false;
651   GetContentSettingsManager().AllowStorageAccess(
652       routing_id(), storage_type, frame->GetSecurityOrigin(),
653       frame->GetDocument().SiteForCookies().RepresentativeUrl(),
654       frame->GetDocument().TopFrameOrigin(), &result);
655   return result;
656 }
657