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