Home | History | Annotate | Download | only in extensions
      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/feature_switch.h"
     15 #include "extensions/common/permissions/permission_set.h"
     16 #include "extensions/common/permissions/permissions_data.h"
     17 #include "extensions/common/user_script.h"
     18 #include "url/gurl.h"
     19 
     20 using content::RenderProcessHost;
     21 using content::WebContentsObserver;
     22 
     23 namespace extensions {
     24 
     25 ActiveTabPermissionGranter::ActiveTabPermissionGranter(
     26     content::WebContents* web_contents,
     27     int tab_id,
     28     Profile* profile)
     29     : WebContentsObserver(web_contents),
     30       tab_id_(tab_id),
     31       extension_registry_observer_(this) {
     32   extension_registry_observer_.Add(ExtensionRegistry::Get(profile));
     33 }
     34 
     35 ActiveTabPermissionGranter::~ActiveTabPermissionGranter() {}
     36 
     37 void ActiveTabPermissionGranter::GrantIfRequested(const Extension* extension) {
     38   if (granted_extensions_.Contains(extension->id()))
     39     return;
     40 
     41   APIPermissionSet new_apis;
     42   URLPatternSet new_hosts;
     43 
     44   const PermissionsData* permissions_data = extension->permissions_data();
     45 
     46   // If the extension requested all-hosts but has had it withheld, we grant it
     47   // active tab-style permissions, even if it doesn't have the activeTab
     48   // permission in the manifest.
     49   if (permissions_data->HasAPIPermission(APIPermission::kActiveTab) ||
     50       permissions_data->HasWithheldImpliedAllHosts()) {
     51     new_hosts.AddOrigin(UserScript::ValidUserScriptSchemes(),
     52                         web_contents()->GetVisibleURL().GetOrigin());
     53     new_apis.insert(APIPermission::kTab);
     54   }
     55 
     56   if (permissions_data->HasAPIPermission(APIPermission::kTabCapture))
     57     new_apis.insert(APIPermission::kTabCaptureForTab);
     58 
     59   if (!new_apis.empty() || !new_hosts.is_empty()) {
     60     granted_extensions_.Insert(extension);
     61     scoped_refptr<const PermissionSet> new_permissions =
     62         new PermissionSet(new_apis, ManifestPermissionSet(),
     63                           new_hosts, URLPatternSet());
     64     permissions_data->UpdateTabSpecificPermissions(tab_id_, new_permissions);
     65     const content::NavigationEntry* navigation_entry =
     66         web_contents()->GetController().GetVisibleEntry();
     67     if (navigation_entry) {
     68       Send(new ExtensionMsg_UpdateTabSpecificPermissions(
     69           navigation_entry->GetURL(),
     70           tab_id_,
     71           extension->id(),
     72           new_hosts));
     73       // If more things ever need to know about this, we should consider making
     74       // an observer class.
     75       // It's important that this comes after the IPC is sent to the renderer,
     76       // so that any tasks executing in the renderer occur after it has the
     77       // updated permissions.
     78       ActiveScriptController::GetForWebContents(web_contents())
     79           ->OnActiveTabPermissionGranted(extension);
     80     }
     81   }
     82 }
     83 
     84 void ActiveTabPermissionGranter::DidNavigateMainFrame(
     85     const content::LoadCommittedDetails& details,
     86     const content::FrameNavigateParams& params) {
     87   if (details.is_in_page)
     88     return;
     89   DCHECK(details.is_main_frame);  // important: sub-frames don't get granted!
     90 
     91   // Only clear the granted permissions for cross-origin navigations.
     92   //
     93   // See http://crbug.com/404243 for why. Currently we only differentiate
     94   // between same-origin and cross-origin navigations when the
     95   // script-require-action flag is on. It's not clear it's good for general
     96   // activeTab consumption (we likely need to build some UI around it first).
     97   // However, the scripts-require-action feature is all-but unusable without
     98   // this behaviour.
     99   if (FeatureSwitch::scripts_require_action()->IsEnabled()) {
    100     const content::NavigationEntry* navigation_entry =
    101         web_contents()->GetController().GetVisibleEntry();
    102     if (!navigation_entry || (navigation_entry->GetURL().GetOrigin() !=
    103                               details.previous_url.GetOrigin())) {
    104       ClearActiveExtensionsAndNotify();
    105     }
    106   } else {
    107     ClearActiveExtensionsAndNotify();
    108   }
    109 }
    110 
    111 void ActiveTabPermissionGranter::WebContentsDestroyed() {
    112   ClearActiveExtensionsAndNotify();
    113 }
    114 
    115 void ActiveTabPermissionGranter::OnExtensionUnloaded(
    116     content::BrowserContext* browser_context,
    117     const Extension* extension,
    118     UnloadedExtensionInfo::Reason reason) {
    119   // Note: don't need to clear the permissions (nor tell the renderer about it)
    120   // because it's being unloaded anyway.
    121   granted_extensions_.Remove(extension->id());
    122 }
    123 
    124 void ActiveTabPermissionGranter::ClearActiveExtensionsAndNotify() {
    125   if (granted_extensions_.is_empty())
    126     return;
    127 
    128   std::vector<std::string> extension_ids;
    129 
    130   for (ExtensionSet::const_iterator it = granted_extensions_.begin();
    131        it != granted_extensions_.end(); ++it) {
    132     it->get()->permissions_data()->ClearTabSpecificPermissions(tab_id_);
    133     extension_ids.push_back((*it)->id());
    134   }
    135 
    136   Send(new ExtensionMsg_ClearTabSpecificPermissions(tab_id_, extension_ids));
    137   granted_extensions_.Clear();
    138 }
    139 
    140 }  // namespace extensions
    141