• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/webstore_standalone_installer.h"
6 
7 #include "base/values.h"
8 #include "chrome/browser/extensions/crx_installer.h"
9 #include "chrome/browser/extensions/extension_install_prompt.h"
10 #include "chrome/browser/extensions/extension_install_ui.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/webstore_data_fetcher.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "content/public/browser/web_contents.h"
15 #include "extensions/browser/extension_prefs.h"
16 #include "extensions/browser/extension_registry.h"
17 #include "extensions/browser/extension_system.h"
18 #include "extensions/browser/extension_util.h"
19 #include "extensions/common/extension.h"
20 #include "url/gurl.h"
21 
22 using content::WebContents;
23 
24 namespace extensions {
25 
26 const char kInvalidWebstoreItemId[] = "Invalid Chrome Web Store item ID";
27 const char kWebstoreRequestError[] =
28     "Could not fetch data from the Chrome Web Store";
29 const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
30 const char kInvalidManifestError[] = "Invalid manifest";
31 const char kUserCancelledError[] = "User cancelled install";
32 const char kExtensionIsBlacklisted[] = "Extension is blacklisted";
33 
WebstoreStandaloneInstaller(const std::string & webstore_item_id,Profile * profile,const Callback & callback)34 WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
35     const std::string& webstore_item_id,
36     Profile* profile,
37     const Callback& callback)
38     : id_(webstore_item_id),
39       callback_(callback),
40       profile_(profile),
41       install_source_(WebstoreInstaller::INSTALL_SOURCE_INLINE),
42       show_user_count_(true),
43       average_rating_(0.0),
44       rating_count_(0) {
45 }
46 
~WebstoreStandaloneInstaller()47 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {}
48 
49 //
50 // Private interface implementation.
51 //
52 
BeginInstall()53 void WebstoreStandaloneInstaller::BeginInstall() {
54   // Add a ref to keep this alive for WebstoreDataFetcher.
55   // All code paths from here eventually lead to either CompleteInstall or
56   // AbortInstall, which both release this ref.
57   AddRef();
58 
59   if (!Extension::IdIsValid(id_)) {
60     CompleteInstall(kInvalidWebstoreItemId);
61     return;
62   }
63 
64   // Use the requesting page as the referrer both since that is more correct
65   // (it is the page that caused this request to happen) and so that we can
66   // track top sites that trigger inline install requests.
67   webstore_data_fetcher_.reset(new WebstoreDataFetcher(
68       this,
69       profile_->GetRequestContext(),
70       GetRequestorURL(),
71       id_));
72   webstore_data_fetcher_->Start();
73 }
74 
CheckInstallValid(const base::DictionaryValue & manifest,std::string * error)75 bool WebstoreStandaloneInstaller::CheckInstallValid(
76     const base::DictionaryValue& manifest,
77     std::string* error) {
78   return true;
79 }
80 
81 scoped_ptr<ExtensionInstallPrompt>
CreateInstallUI()82 WebstoreStandaloneInstaller::CreateInstallUI() {
83   return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
84 }
85 
86 scoped_ptr<WebstoreInstaller::Approval>
CreateApproval() const87 WebstoreStandaloneInstaller::CreateApproval() const {
88   scoped_ptr<WebstoreInstaller::Approval> approval(
89       WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
90           profile_,
91           id_,
92           scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy()),
93           true));
94   approval->skip_post_install_ui = !ShouldShowPostInstallUI();
95   approval->use_app_installed_bubble = ShouldShowAppInstalledBubble();
96   approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
97   return approval.Pass();
98 }
99 
OnWebstoreRequestFailure()100 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
101   OnWebStoreDataFetcherDone();
102   CompleteInstall(kWebstoreRequestError);
103 }
104 
OnWebstoreResponseParseSuccess(scoped_ptr<base::DictionaryValue> webstore_data)105 void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
106     scoped_ptr<base::DictionaryValue> webstore_data) {
107   OnWebStoreDataFetcherDone();
108 
109   if (!CheckRequestorAlive()) {
110     CompleteInstall(std::string());
111     return;
112   }
113 
114   std::string error;
115 
116   if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
117     CompleteInstall(error);
118     return;
119   }
120 
121   if (!CheckRequestorPermitted(*webstore_data, &error)) {
122     CompleteInstall(error);
123     return;
124   }
125 
126   // Manifest, number of users, average rating and rating count are required.
127   std::string manifest;
128   if (!webstore_data->GetString(kManifestKey, &manifest) ||
129       !webstore_data->GetString(kUsersKey, &localized_user_count_) ||
130       !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) ||
131       !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) {
132     CompleteInstall(kInvalidWebstoreResponseError);
133     return;
134   }
135 
136   // Optional.
137   show_user_count_ = true;
138   webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
139 
140   if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
141       average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
142     CompleteInstall(kInvalidWebstoreResponseError);
143     return;
144   }
145 
146   // Localized name and description are optional.
147   if ((webstore_data->HasKey(kLocalizedNameKey) &&
148       !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) ||
149       (webstore_data->HasKey(kLocalizedDescriptionKey) &&
150       !webstore_data->GetString(
151           kLocalizedDescriptionKey, &localized_description_))) {
152     CompleteInstall(kInvalidWebstoreResponseError);
153     return;
154   }
155 
156   // Icon URL is optional.
157   GURL icon_url;
158   if (webstore_data->HasKey(kIconUrlKey)) {
159     std::string icon_url_string;
160     if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
161       CompleteInstall(kInvalidWebstoreResponseError);
162       return;
163     }
164     icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
165         icon_url_string);
166     if (!icon_url.is_valid()) {
167       CompleteInstall(kInvalidWebstoreResponseError);
168       return;
169     }
170   }
171 
172   // Assume ownership of webstore_data.
173   webstore_data_ = webstore_data.Pass();
174 
175   scoped_refptr<WebstoreInstallHelper> helper =
176       new WebstoreInstallHelper(this,
177                                 id_,
178                                 manifest,
179                                 std::string(),  // We don't have any icon data.
180                                 icon_url,
181                                 profile_->GetRequestContext());
182   // The helper will call us back via OnWebstoreParseSucces or
183   // OnWebstoreParseFailure.
184   helper->Start();
185 }
186 
OnWebstoreResponseParseFailure(const std::string & error)187 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
188     const std::string& error) {
189   OnWebStoreDataFetcherDone();
190   CompleteInstall(error);
191 }
192 
OnWebstoreParseSuccess(const std::string & id,const SkBitmap & icon,base::DictionaryValue * manifest)193 void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
194     const std::string& id,
195     const SkBitmap& icon,
196     base::DictionaryValue* manifest) {
197   CHECK_EQ(id_, id);
198 
199   if (!CheckRequestorAlive()) {
200     CompleteInstall(std::string());
201     return;
202   }
203 
204   manifest_.reset(manifest);
205   icon_ = icon;
206 
207   std::string error;
208   if (!CheckInstallValid(*manifest, &error)) {
209     DCHECK(!error.empty());
210     CompleteInstall(error);
211     return;
212   }
213 
214   install_prompt_ = CreateInstallPrompt();
215   if (install_prompt_) {
216     ShowInstallUI();
217     // Control flow finishes up in InstallUIProceed or InstallUIAbort.
218   } else {
219     InstallUIProceed();
220   }
221 }
222 
OnWebstoreParseFailure(const std::string & id,InstallHelperResultCode result_code,const std::string & error_message)223 void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
224     const std::string& id,
225     InstallHelperResultCode result_code,
226     const std::string& error_message) {
227   CompleteInstall(error_message);
228 }
229 
InstallUIProceed()230 void WebstoreStandaloneInstaller::InstallUIProceed() {
231   if (!CheckRequestorAlive()) {
232     CompleteInstall(std::string());
233     return;
234   }
235 
236   scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
237 
238   ExtensionService* extension_service =
239       ExtensionSystem::Get(profile_)->extension_service();
240   const Extension* extension =
241       extension_service->GetExtensionById(id_, true /* include disabled */);
242   if (extension) {
243     std::string install_result;  // Empty string for install success.
244 
245     if (ExtensionPrefs::Get(profile_)->IsExtensionBlacklisted(id_)) {
246       // Don't install a blacklisted extension.
247       install_result = kExtensionIsBlacklisted;
248     } else if (util::IsEphemeralApp(extension->id(), profile_) &&
249                !approval->is_ephemeral) {
250       // If the target extension has already been installed ephemerally, it can
251       // be promoted to a regular installed extension and downloading from the
252       // Web Store is not necessary.
253       extension_service->PromoteEphemeralApp(extension, false);
254     } else if (!extension_service->IsExtensionEnabled(id_)) {
255       // If the extension is installed but disabled, and not blacklisted,
256       // enable it.
257       extension_service->EnableExtension(id_);
258     }  // else extension is installed and enabled; no work to be done.
259 
260     CompleteInstall(install_result);
261     return;
262   }
263 
264   scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
265       profile_,
266       this,
267       GetWebContents(),
268       id_,
269       approval.Pass(),
270       install_source_);
271   installer->Start();
272 }
273 
InstallUIAbort(bool user_initiated)274 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
275   CompleteInstall(kUserCancelledError);
276 }
277 
OnExtensionInstallSuccess(const std::string & id)278 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
279     const std::string& id) {
280   CHECK_EQ(id_, id);
281   CompleteInstall(std::string());
282 }
283 
OnExtensionInstallFailure(const std::string & id,const std::string & error,WebstoreInstaller::FailureReason cancelled)284 void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
285     const std::string& id,
286     const std::string& error,
287     WebstoreInstaller::FailureReason cancelled) {
288   CHECK_EQ(id_, id);
289   CompleteInstall(error);
290 }
291 
AbortInstall()292 void WebstoreStandaloneInstaller::AbortInstall() {
293   callback_.Reset();
294   // Abort any in-progress fetches.
295   if (webstore_data_fetcher_) {
296     webstore_data_fetcher_.reset();
297     Release();  // Matches the AddRef in BeginInstall.
298   }
299 }
300 
InvokeCallback(const std::string & error)301 void WebstoreStandaloneInstaller::InvokeCallback(const std::string& error) {
302   if (!callback_.is_null())
303     callback_.Run(error.empty(), error);
304 }
305 
CompleteInstall(const std::string & error)306 void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) {
307   InvokeCallback(error);
308   Release();  // Matches the AddRef in BeginInstall.
309 }
310 
ShowInstallUI()311 void WebstoreStandaloneInstaller::ShowInstallUI() {
312   std::string error;
313   localized_extension_for_display_ =
314       ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
315           manifest_.get(),
316           Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
317           id_,
318           localized_name_,
319           localized_description_,
320           &error);
321   if (!localized_extension_for_display_.get()) {
322     CompleteInstall(kInvalidManifestError);
323     return;
324   }
325 
326   install_ui_ = CreateInstallUI();
327   install_ui_->ConfirmStandaloneInstall(
328       this, localized_extension_for_display_.get(), &icon_, install_prompt_);
329 }
330 
OnWebStoreDataFetcherDone()331 void WebstoreStandaloneInstaller::OnWebStoreDataFetcherDone() {
332   // An instance of this class is passed in as a delegate for the
333   // WebstoreInstallHelper, ExtensionInstallPrompt and WebstoreInstaller, and
334   // therefore needs to remain alive until they are done. Clear the webstore
335   // data fetcher to avoid calling Release in AbortInstall while any of these
336   // operations are in progress.
337   webstore_data_fetcher_.reset();
338 }
339 
340 }  // namespace extensions
341