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"
23 namespace extensions
{
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
,
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
,
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));
71 ActiveTabPermissionGranter::ActiveTabPermissionGranter(
72 content::WebContents
* web_contents
,
75 : content::WebContentsObserver(web_contents
),
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()))
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(),
122 SendMessageToProcesses(
123 ProcessManager::Get(web_contents()->GetBrowserContext())->
124 GetRenderFrameHostsForExtension(extension
->id()),
125 web_contents()->GetRenderProcessHost(),
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
)
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
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();
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())
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(),
202 granted_extensions_
.Clear();
205 } // namespace extensions