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/browser/extensions/active_tab_permission_granter.h"
6
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/web_contents.h"
12#include "extensions/browser/extension_registry.h"
13#include "extensions/common/extension_messages.h"
14#include "extensions/common/permissions/permission_set.h"
15#include "extensions/common/permissions/permissions_data.h"
16#include "extensions/common/user_script.h"
17#include "url/gurl.h"
18
19using content::RenderProcessHost;
20using content::WebContentsObserver;
21
22namespace extensions {
23
24ActiveTabPermissionGranter::ActiveTabPermissionGranter(
25    content::WebContents* web_contents,
26    int tab_id,
27    Profile* profile)
28    : WebContentsObserver(web_contents),
29      tab_id_(tab_id),
30      extension_registry_observer_(this) {
31  extension_registry_observer_.Add(ExtensionRegistry::Get(profile));
32}
33
34ActiveTabPermissionGranter::~ActiveTabPermissionGranter() {}
35
36void ActiveTabPermissionGranter::GrantIfRequested(const Extension* extension) {
37  if (granted_extensions_.Contains(extension->id()))
38    return;
39
40  APIPermissionSet new_apis;
41  URLPatternSet new_hosts;
42
43  const PermissionsData* permissions_data = extension->permissions_data();
44
45  // If the extension requires action for script execution, we grant it
46  // active tab-style permissions, even if it doesn't have the activeTab
47  // permission in the manifest.
48  // We don't take tab id into account, because we want to know if the extension
49  // should require active tab in general (not for the current tab).
50  bool requires_action_for_script_execution =
51      permissions_data->RequiresActionForScriptExecution(extension,
52                                                         -1,  // No tab id.
53                                                         GURL());
54
55  if (extension->permissions_data()->HasAPIPermission(
56          APIPermission::kActiveTab) ||
57      requires_action_for_script_execution) {
58    URLPattern pattern(UserScript::ValidUserScriptSchemes());
59    // Pattern parsing could fail if this is an unsupported URL e.g. chrome://.
60    if (pattern.Parse(web_contents()->GetURL().spec()) ==
61            URLPattern::PARSE_SUCCESS) {
62      new_hosts.AddPattern(pattern);
63    }
64    new_apis.insert(APIPermission::kTab);
65  }
66
67  if (extension->permissions_data()->HasAPIPermission(
68          APIPermission::kTabCapture))
69    new_apis.insert(APIPermission::kTabCaptureForTab);
70
71  if (!new_apis.empty() || !new_hosts.is_empty()) {
72    granted_extensions_.Insert(extension);
73    scoped_refptr<const PermissionSet> new_permissions =
74        new PermissionSet(new_apis, ManifestPermissionSet(),
75                          new_hosts, URLPatternSet());
76    permissions_data->UpdateTabSpecificPermissions(tab_id_, new_permissions);
77    const content::NavigationEntry* navigation_entry =
78        web_contents()->GetController().GetVisibleEntry();
79    if (navigation_entry) {
80      Send(new ExtensionMsg_UpdateTabSpecificPermissions(
81          navigation_entry->GetPageID(),
82          tab_id_,
83          extension->id(),
84          new_hosts));
85      // If more things ever need to know about this, we should consider making
86      // an observer class.
87      // It's important that this comes after the IPC is sent to the renderer,
88      // so that any tasks executing in the renderer occur after it has the
89      // updated permissions.
90      ActiveScriptController::GetForWebContents(web_contents())
91          ->OnActiveTabPermissionGranted(extension);
92    }
93  }
94}
95
96void ActiveTabPermissionGranter::DidNavigateMainFrame(
97    const content::LoadCommittedDetails& details,
98    const content::FrameNavigateParams& params) {
99  if (details.is_in_page)
100    return;
101  DCHECK(details.is_main_frame);  // important: sub-frames don't get granted!
102  ClearActiveExtensionsAndNotify();
103}
104
105void ActiveTabPermissionGranter::WebContentsDestroyed() {
106  ClearActiveExtensionsAndNotify();
107}
108
109void ActiveTabPermissionGranter::OnExtensionUnloaded(
110    content::BrowserContext* browser_context,
111    const Extension* extension,
112    UnloadedExtensionInfo::Reason reason) {
113  // Note: don't need to clear the permissions (nor tell the renderer about it)
114  // because it's being unloaded anyway.
115  granted_extensions_.Remove(extension->id());
116}
117
118void ActiveTabPermissionGranter::ClearActiveExtensionsAndNotify() {
119  if (granted_extensions_.is_empty())
120    return;
121
122  std::vector<std::string> extension_ids;
123
124  for (ExtensionSet::const_iterator it = granted_extensions_.begin();
125       it != granted_extensions_.end(); ++it) {
126    it->get()->permissions_data()->ClearTabSpecificPermissions(tab_id_);
127    extension_ids.push_back((*it)->id());
128  }
129
130  Send(new ExtensionMsg_ClearTabSpecificPermissions(tab_id_, extension_ids));
131  granted_extensions_.Clear();
132}
133
134}  // namespace extensions
135