1 // Copyright 2014 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/common/extensions/manifest_handlers/extension_action_handler.h"
6 
7 #include <memory>
8 
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "chrome/common/extensions/api/extension_action/action_info.h"
12 #include "chrome/common/extensions/extension_constants.h"
13 #include "extensions/common/extension.h"
14 #include "extensions/common/file_util.h"
15 #include "extensions/common/image_util.h"
16 #include "extensions/common/manifest_constants.h"
17 
18 namespace extensions {
19 
ExtensionActionHandler()20 ExtensionActionHandler::ExtensionActionHandler() {
21 }
22 
~ExtensionActionHandler()23 ExtensionActionHandler::~ExtensionActionHandler() {
24 }
25 
Parse(Extension * extension,base::string16 * error)26 bool ExtensionActionHandler::Parse(Extension* extension,
27                                    base::string16* error) {
28   const char* key = nullptr;
29   const char* error_key = nullptr;
30   ActionInfo::Type type = ActionInfo::TYPE_ACTION;
31   if (extension->manifest()->HasKey(manifest_keys::kAction)) {
32     key = manifest_keys::kAction;
33     error_key = manifest_errors::kInvalidAction;
34     // type ACTION is correct.
35   }
36 
37   if (extension->manifest()->HasKey(manifest_keys::kPageAction)) {
38     if (key != nullptr) {
39       // An extension can only have one action.
40       *error = base::ASCIIToUTF16(manifest_errors::kOneUISurfaceOnly);
41       return false;
42     }
43     key = manifest_keys::kPageAction;
44     error_key = manifest_errors::kInvalidPageAction;
45     type = ActionInfo::TYPE_PAGE;
46   }
47 
48   if (extension->manifest()->HasKey(manifest_keys::kBrowserAction)) {
49     if (key != nullptr) {
50       // An extension can only have one action.
51       *error = base::ASCIIToUTF16(manifest_errors::kOneUISurfaceOnly);
52       return false;
53     }
54     key = manifest_keys::kBrowserAction;
55     error_key = manifest_errors::kInvalidBrowserAction;
56     type = ActionInfo::TYPE_BROWSER;
57   }
58 
59   if (key) {
60     const base::DictionaryValue* dict = nullptr;
61     if (!extension->manifest()->GetDictionary(key, &dict)) {
62       *error = base::ASCIIToUTF16(error_key);
63       return false;
64     }
65 
66     std::unique_ptr<ActionInfo> action_info =
67         ActionInfo::Load(extension, type, dict, error);
68     if (!action_info)
69       return false;  // Failed to parse extension action definition.
70 
71     switch (type) {
72       case ActionInfo::TYPE_ACTION:
73         ActionInfo::SetExtensionActionInfo(extension, std::move(action_info));
74         break;
75       case ActionInfo::TYPE_PAGE:
76         ActionInfo::SetPageActionInfo(extension, std::move(action_info));
77         break;
78       case ActionInfo::TYPE_BROWSER:
79         ActionInfo::SetBrowserActionInfo(extension, std::move(action_info));
80         break;
81     }
82   } else {  // No key, used for synthesizing an action for extensions with none.
83     if (Manifest::IsComponentLocation(extension->location()))
84       return true;  // Don't synthesize actions for component extensions.
85     if (extension->was_installed_by_default())
86       return true;  // Don't synthesize actions for default extensions.
87 
88     // Set an empty page action. We use a page action (instead of a browser
89     // action) because the action should not be seen as enabled on every page.
90     auto action_info = std::make_unique<ActionInfo>(ActionInfo::TYPE_PAGE);
91     action_info->synthesized = true;
92     ActionInfo::SetPageActionInfo(extension, std::move(action_info));
93   }
94 
95   return true;
96 }
97 
Validate(const Extension * extension,std::string * error,std::vector<InstallWarning> * warnings) const98 bool ExtensionActionHandler::Validate(
99     const Extension* extension,
100     std::string* error,
101     std::vector<InstallWarning>* warnings) const {
102   const ActionInfo* action = ActionInfo::GetAnyActionInfo(extension);
103   if (!action || action->default_icon.empty())
104     return true;
105 
106   const char* manifest_key = nullptr;
107   switch (action->type) {
108     case ActionInfo::TYPE_ACTION:
109       manifest_key = manifest_keys::kAction;
110       break;
111     case ActionInfo::TYPE_BROWSER:
112       manifest_key = manifest_keys::kBrowserAction;
113       break;
114     case ActionInfo::TYPE_PAGE:
115       manifest_key = manifest_keys::kPageAction;
116       break;
117   }
118   DCHECK(manifest_key);
119 
120   // Analyze the icons for visibility using the default toolbar color, since
121   // the majority of Chrome users don't modify their theme.
122   return file_util::ValidateExtensionIconSet(
123       action->default_icon, extension, manifest_key,
124       image_util::kDefaultToolbarColor, error);
125 }
126 
AlwaysParseForType(Manifest::Type type) const127 bool ExtensionActionHandler::AlwaysParseForType(Manifest::Type type) const {
128   return type == Manifest::TYPE_EXTENSION || type == Manifest::TYPE_USER_SCRIPT;
129 }
130 
Keys() const131 base::span<const char* const> ExtensionActionHandler::Keys() const {
132   static constexpr const char* kKeys[] = {
133       manifest_keys::kPageAction,
134       manifest_keys::kBrowserAction,
135   };
136   return kKeys;
137 }
138 
139 }  // namespace extensions
140