Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / active_tab_permission_granter.cc
blobcce3f3f9f0974d1729dec1ecb8e45e56d25dbffc
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.
5 #include "chrome/browser/extensions/active_tab_permission_granter.h"
7 #include "chrome/browser/extensions/active_script_controller.h"
8 #include "chrome/browser/profiles/profile.h"
9 #include "content/public/browser/navigation_details.h"
10 #include "content/public/browser/navigation_entry.h"
11 #include "content/public/browser/render_frame_host.h"
12 #include "content/public/browser/render_process_host.h"
13 #include "content/public/browser/web_contents.h"
14 #include "extensions/browser/extension_registry.h"
15 #include "extensions/browser/process_manager.h"
16 #include "extensions/common/extension_messages.h"
17 #include "extensions/common/feature_switch.h"
18 #include "extensions/common/permissions/permission_set.h"
19 #include "extensions/common/permissions/permissions_data.h"
20 #include "extensions/common/user_script.h"
21 #include "url/gurl.h"
23 namespace extensions {
25 namespace {
27 using CreateMessageFunction = base::Callback<IPC::Message*(bool)>;
29 // Creates a new IPC message for updating tab-specific permissions.
30 IPC::Message* CreateUpdateMessage(const GURL& visible_url,
31 const std::string& extension_id,
32 const URLPatternSet& new_hosts,
33 int tab_id,
34 bool update_whitelist) {
35 return new ExtensionMsg_UpdateTabSpecificPermissions(
36 visible_url, extension_id, new_hosts, update_whitelist, tab_id);
39 // Creates a new IPC message for clearing tab-specific permissions.
40 IPC::Message* CreateClearMessage(const std::vector<std::string>& ids,
41 int tab_id,
42 bool update_whitelist) {
43 return new ExtensionMsg_ClearTabSpecificPermissions(
44 ids, update_whitelist, tab_id);
47 // Sends a message exactly once to each render process host owning one of the
48 // given |frame_hosts| and |tab_process|. If |tab_process| doesn't own any of
49 // the |frame_hosts|, it will not be signaled to update its origin whitelist.
50 void SendMessageToProcesses(
51 const std::set<content::RenderFrameHost*>& frame_hosts,
52 content::RenderProcessHost* tab_process,
53 const CreateMessageFunction& create_message) {
54 std::set<content::RenderProcessHost*> sent_to_hosts;
55 for (content::RenderFrameHost* frame_host : frame_hosts) {
56 content::RenderProcessHost* process_host = frame_host->GetProcess();
57 if (sent_to_hosts.count(process_host) == 0) {
58 // Extension processes have to update the origin whitelists.
59 process_host->Send(create_message.Run(true));
60 sent_to_hosts.insert(frame_host->GetProcess());
63 // If the tab wasn't one of those processes already updated (it likely
64 // wasn't), update it. Tabs don't need to update the origin whitelist.
65 if (sent_to_hosts.count(tab_process) == 0)
66 tab_process->Send(create_message.Run(false));
69 } // namespace
71 ActiveTabPermissionGranter::ActiveTabPermissionGranter(
72 content::WebContents* web_contents,
73 int tab_id,
74 Profile* profile)
75 : content::WebContentsObserver(web_contents),
76 tab_id_(tab_id),
77 extension_registry_observer_(this) {
78 extension_registry_observer_.Add(ExtensionRegistry::Get(profile));
81 ActiveTabPermissionGranter::~ActiveTabPermissionGranter() {}
83 void ActiveTabPermissionGranter::GrantIfRequested(const Extension* extension) {
84 if (granted_extensions_.Contains(extension->id()))
85 return;
87 APIPermissionSet new_apis;
88 URLPatternSet new_hosts;
90 const PermissionsData* permissions_data = extension->permissions_data();
92 // If the extension requested all-hosts but has had it withheld, we grant it
93 // active tab-style permissions, even if it doesn't have the activeTab
94 // permission in the manifest.
95 if (permissions_data->HasAPIPermission(APIPermission::kActiveTab) ||
96 permissions_data->HasWithheldImpliedAllHosts()) {
97 new_hosts.AddOrigin(UserScript::ValidUserScriptSchemes(),
98 web_contents()->GetVisibleURL().GetOrigin());
99 new_apis.insert(APIPermission::kTab);
102 if (permissions_data->HasAPIPermission(APIPermission::kTabCapture))
103 new_apis.insert(APIPermission::kTabCaptureForTab);
105 if (!new_apis.empty() || !new_hosts.is_empty()) {
106 granted_extensions_.Insert(extension);
107 scoped_refptr<const PermissionSet> new_permissions =
108 new PermissionSet(new_apis, ManifestPermissionSet(),
109 new_hosts, URLPatternSet());
110 permissions_data->UpdateTabSpecificPermissions(tab_id_, new_permissions);
111 const content::NavigationEntry* navigation_entry =
112 web_contents()->GetController().GetVisibleEntry();
113 if (navigation_entry) {
114 // We update all extension render views with the new tab permissions, and
115 // also the tab itself.
116 CreateMessageFunction update_message =
117 base::Bind(&CreateUpdateMessage,
118 navigation_entry->GetURL(),
119 extension->id(),
120 new_hosts,
121 tab_id_);
122 SendMessageToProcesses(
123 ProcessManager::Get(web_contents()->GetBrowserContext())->
124 GetRenderFrameHostsForExtension(extension->id()),
125 web_contents()->GetRenderProcessHost(),
126 update_message);
128 // If more things ever need to know about this, we should consider making
129 // an observer class.
130 // It's important that this comes after the IPC is sent to the renderer,
131 // so that any tasks executing in the renderer occur after it has the
132 // updated permissions.
133 ActiveScriptController::GetForWebContents(web_contents())
134 ->OnActiveTabPermissionGranted(extension);
139 void ActiveTabPermissionGranter::DidNavigateMainFrame(
140 const content::LoadCommittedDetails& details,
141 const content::FrameNavigateParams& params) {
142 if (details.is_in_page)
143 return;
144 DCHECK(details.is_main_frame); // important: sub-frames don't get granted!
146 // Only clear the granted permissions for cross-origin navigations.
148 // See http://crbug.com/404243 for why. Currently we only differentiate
149 // between same-origin and cross-origin navigations when the
150 // script-require-action flag is on. It's not clear it's good for general
151 // activeTab consumption (we likely need to build some UI around it first).
152 // However, the scripts-require-action feature is all-but unusable without
153 // this behaviour.
154 if (FeatureSwitch::scripts_require_action()->IsEnabled()) {
155 const content::NavigationEntry* navigation_entry =
156 web_contents()->GetController().GetVisibleEntry();
157 if (!navigation_entry || (navigation_entry->GetURL().GetOrigin() !=
158 details.previous_url.GetOrigin())) {
159 ClearActiveExtensionsAndNotify();
161 } else {
162 ClearActiveExtensionsAndNotify();
166 void ActiveTabPermissionGranter::WebContentsDestroyed() {
167 ClearActiveExtensionsAndNotify();
170 void ActiveTabPermissionGranter::OnExtensionUnloaded(
171 content::BrowserContext* browser_context,
172 const Extension* extension,
173 UnloadedExtensionInfo::Reason reason) {
174 // Note: don't need to clear the permissions (nor tell the renderer about it)
175 // because it's being unloaded anyway.
176 granted_extensions_.Remove(extension->id());
179 void ActiveTabPermissionGranter::ClearActiveExtensionsAndNotify() {
180 if (granted_extensions_.is_empty())
181 return;
183 std::set<content::RenderFrameHost*> frame_hosts;
184 std::vector<std::string> extension_ids;
185 ProcessManager* process_manager =
186 ProcessManager::Get(web_contents()->GetBrowserContext());
187 for (const scoped_refptr<const Extension>& extension : granted_extensions_) {
188 extension->permissions_data()->ClearTabSpecificPermissions(tab_id_);
189 extension_ids.push_back(extension->id());
190 std::set<content::RenderFrameHost*> extension_frame_hosts =
191 process_manager->GetRenderFrameHostsForExtension(extension->id());
192 frame_hosts.insert(extension_frame_hosts.begin(),
193 extension_frame_hosts.end());
196 CreateMessageFunction clear_message =
197 base::Bind(&CreateClearMessage, extension_ids, tab_id_);
198 SendMessageToProcesses(frame_hosts,
199 web_contents()->GetRenderProcessHost(),
200 clear_message);
202 granted_extensions_.Clear();
205 } // namespace extensions