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/script_badge_controller.h"
7 #include "base/logging.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
12 #include "chrome/browser/extensions/extension_action.h"
13 #include "chrome/browser/extensions/extension_action_manager.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/extensions/extension_system.h"
16 #include "chrome/browser/extensions/tab_helper.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/sessions/session_id.h"
19 #include "chrome/common/extensions/extension_messages.h"
20 #include "content/public/browser/navigation_controller.h"
21 #include "content/public/browser/navigation_details.h"
22 #include "content/public/browser/navigation_entry.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/web_contents.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/extension_set.h"
27 #include "ipc/ipc_message.h"
28 #include "ipc/ipc_message_macros.h"
31 namespace extensions
{
33 ScriptBadgeController::ScriptBadgeController(content::WebContents
* web_contents
,
34 TabHelper
* tab_helper
)
35 : TabHelper::ScriptExecutionObserver(tab_helper
),
36 content::WebContentsObserver(web_contents
) {
38 chrome::NOTIFICATION_EXTENSION_UNLOADED
,
39 content::Source
<Profile
>(profile()));
42 ScriptBadgeController::~ScriptBadgeController() {}
44 std::vector
<ExtensionAction
*> ScriptBadgeController::GetCurrentActions() const {
45 std::vector
<ExtensionAction
*> result
;
46 ExtensionService
* service
= GetExtensionService();
50 ExtensionActionManager
* extension_action_manager
=
51 ExtensionActionManager::Get(profile());
52 const ExtensionSet
* extensions
= service
->extensions();
53 for (std::set
<std::string
>::const_iterator
54 it
= extensions_in_current_actions_
.begin();
55 it
!= extensions_in_current_actions_
.end(); ++it
) {
56 const Extension
* extension
= extensions
->GetByID(*it
);
59 ExtensionAction
* script_badge
=
60 extension_action_manager
->GetScriptBadge(*extension
);
62 result
.push_back(script_badge
);
67 void ScriptBadgeController::GetAttentionFor(
68 const std::string
& extension_id
) {
69 ExtensionAction
* script_badge
= AddExtensionToCurrentActions(extension_id
);
73 // TODO(jyasskin): Modify the icon's appearance to indicate that the
74 // extension is merely asking for permission to run:
75 // http://crbug.com/133142
76 script_badge
->SetAppearance(SessionID::IdForTab(web_contents()),
77 ExtensionAction::WANTS_ATTENTION
);
82 LocationBarController::Action
ScriptBadgeController::OnClicked(
83 const std::string
& extension_id
, int mouse_button
) {
84 ExtensionService
* service
= GetExtensionService();
88 const Extension
* extension
= service
->extensions()->GetByID(extension_id
);
90 ExtensionAction
* script_badge
=
91 ExtensionActionManager::Get(profile())->GetScriptBadge(*extension
);
94 switch (mouse_button
) {
97 extensions::TabHelper::FromWebContents(web_contents())->
98 active_tab_permission_granter()->GrantIfRequested(extension
);
100 // Even if clicking the badge doesn't immediately cause the extension to
101 // run script on the page, we want to help users associate clicking with
102 // the extension having permission to modify the page, so we make the icon
103 // full-colored immediately.
104 if (script_badge
->SetAppearance(SessionID::IdForTab(web_contents()),
105 ExtensionAction::ACTIVE
))
108 // Fire the scriptBadge.onClicked event.
109 ExtensionActionAPI::ScriptBadgeExecuted(
110 profile(), *script_badge
, SessionID::IdForTab(web_contents()));
112 // TODO(jyasskin): The fallback order should be user-defined popup ->
113 // onClicked handler -> default popup.
114 return ACTION_SHOW_SCRIPT_POPUP
;
117 // Don't grant access on right clicks, so users can investigate
118 // the extension without danger.
120 return extension
->ShowConfigureContextMenus() ?
121 ACTION_SHOW_CONTEXT_MENU
: ACTION_NONE
;
128 std::string
JoinExtensionIDs(const ExecutingScriptsMap
& ids
) {
129 std::vector
<std::string
> as_vector
;
130 for (ExecutingScriptsMap::const_iterator iter
= ids
.begin();
131 iter
!= ids
.end(); ++iter
) {
132 as_vector
.push_back(iter
->first
);
134 return "[" + JoinString(as_vector
, ',') + "]";
138 void ScriptBadgeController::OnScriptsExecuted(
139 const content::WebContents
* web_contents
,
140 const ExecutingScriptsMap
& extension_ids
,
142 const GURL
& on_url
) {
143 int32 current_page_id
= GetPageID();
144 if (on_page_id
!= current_page_id
)
147 if (current_page_id
< 0) {
148 // Tracking down http://crbug.com/138323.
149 std::string message
= base::StringPrintf(
150 "Expected a page ID of %d but there was no navigation entry. "
151 "Extension IDs are %s.",
153 JoinExtensionIDs(extension_ids
).c_str());
155 base::snprintf(buf
, arraysize(buf
), "%s", message
.c_str());
156 LOG(ERROR
) << message
;
160 bool changed
= false;
161 for (ExecutingScriptsMap::const_iterator it
= extension_ids
.begin();
162 it
!= extension_ids
.end(); ++it
) {
163 changed
|= MarkExtensionExecuting(it
->first
);
169 Profile
* ScriptBadgeController::profile() const {
170 content::WebContents
* web_contents
= this->web_contents();
172 return Profile::FromBrowserContext(web_contents
->GetBrowserContext());
177 ExtensionService
* ScriptBadgeController::GetExtensionService() const {
178 Profile
* profile
= this->profile();
180 return ExtensionSystem::Get(profile
)->extension_service();
185 int32
ScriptBadgeController::GetPageID() {
186 content::NavigationEntry
* nav_entry
=
187 web_contents()->GetController().GetVisibleEntry();
188 return nav_entry
? nav_entry
->GetPageID() : -1;
191 void ScriptBadgeController::NotifyChange() {
192 content::NotificationService::current()->Notify(
193 chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED
,
194 content::Source
<Profile
>(profile()),
195 content::Details
<content::WebContents
>(web_contents()));
198 void ScriptBadgeController::DidNavigateMainFrame(
199 const content::LoadCommittedDetails
& details
,
200 const content::FrameNavigateParams
& params
) {
201 if (details
.is_in_page
)
203 extensions_in_current_actions_
.clear();
206 void ScriptBadgeController::Observe(
208 const content::NotificationSource
& source
,
209 const content::NotificationDetails
& details
) {
210 DCHECK_EQ(type
, chrome::NOTIFICATION_EXTENSION_UNLOADED
);
211 const Extension
* extension
=
212 content::Details
<UnloadedExtensionInfo
>(details
)->extension
;
213 if (extensions_in_current_actions_
.erase(extension
->id()))
217 ExtensionAction
* ScriptBadgeController::AddExtensionToCurrentActions(
218 const std::string
& extension_id
) {
219 if (!extensions_in_current_actions_
.insert(extension_id
).second
)
222 ExtensionService
* service
= GetExtensionService();
226 const Extension
* extension
= service
->extensions()->GetByID(extension_id
);
230 return ExtensionActionManager::Get(profile())->GetScriptBadge(*extension
);
233 bool ScriptBadgeController::MarkExtensionExecuting(
234 const std::string
& extension_id
) {
235 ExtensionAction
* script_badge
= AddExtensionToCurrentActions(extension_id
);
239 script_badge
->SetAppearance(SessionID::IdForTab(web_contents()),
240 ExtensionAction::ACTIVE
);
244 } // namespace extensions