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