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_install_helper.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/values.h"
11#include "chrome/common/chrome_utility_messages.h"
12#include "chrome/common/extensions/chrome_utility_extensions_messages.h"
13#include "content/public/browser/browser_thread.h"
14#include "content/public/browser/utility_process_host.h"
15#include "net/base/load_flags.h"
16#include "net/url_request/url_fetcher.h"
17#include "net/url_request/url_request_context_getter.h"
18#include "net/url_request/url_request_status.h"
19
20using content::BrowserThread;
21using content::UtilityProcessHost;
22
23namespace {
24
25const char kImageDecodeError[] = "Image decode failed";
26
27}  // namespace
28
29namespace extensions {
30
31WebstoreInstallHelper::WebstoreInstallHelper(
32    Delegate* delegate,
33    const std::string& id,
34    const std::string& manifest,
35    const std::string& icon_data,
36    const GURL& icon_url,
37    net::URLRequestContextGetter* context_getter)
38    : delegate_(delegate),
39      id_(id),
40      manifest_(manifest),
41      icon_base64_data_(icon_data),
42      icon_url_(icon_url),
43      context_getter_(context_getter),
44      icon_decode_complete_(false),
45      manifest_parse_complete_(false),
46      parse_error_(Delegate::UNKNOWN_ERROR) {}
47
48WebstoreInstallHelper::~WebstoreInstallHelper() {}
49
50void WebstoreInstallHelper::Start() {
51  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
52  CHECK(icon_base64_data_.empty() || icon_url_.is_empty());
53
54  if (icon_base64_data_.empty() && icon_url_.is_empty())
55    icon_decode_complete_ = true;
56
57  BrowserThread::PostTask(
58      BrowserThread::IO,
59      FROM_HERE,
60      base::Bind(&WebstoreInstallHelper::StartWorkOnIOThread, this));
61
62  if (!icon_url_.is_empty()) {
63    CHECK(context_getter_);
64    url_fetcher_.reset(net::URLFetcher::Create(
65        icon_url_, net::URLFetcher::GET, this));
66    url_fetcher_->SetRequestContext(context_getter_);
67    url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
68                               net::LOAD_DO_NOT_SEND_COOKIES);
69
70    url_fetcher_->Start();
71    // We'll get called back in OnURLFetchComplete.
72  }
73}
74
75void WebstoreInstallHelper::StartWorkOnIOThread() {
76  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
77  utility_host_ = UtilityProcessHost::Create(
78      this, base::MessageLoopProxy::current().get())->AsWeakPtr();
79  utility_host_->StartBatchMode();
80
81  if (!icon_base64_data_.empty())
82    utility_host_->Send(
83        new ChromeUtilityMsg_DecodeImageBase64(icon_base64_data_));
84
85  utility_host_->Send(new ChromeUtilityMsg_ParseJSON(manifest_));
86}
87
88void WebstoreInstallHelper::OnURLFetchComplete(
89    const net::URLFetcher* source) {
90  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
91  CHECK(source == url_fetcher_.get());
92  int response_code =
93      source->GetStatus().is_success() ? source->GetResponseCode() : 0;
94  if (!source->GetStatus().is_success() ||
95      response_code / 100 == 4 || response_code / 100 == 5) {
96    BrowserThread::PostTask(
97        BrowserThread::IO,
98        FROM_HERE,
99        base::Bind(&WebstoreInstallHelper::OnDecodeImageFailed, this));
100  } else {
101    std::string response_data;
102    source->GetResponseAsString(&response_data);
103    fetched_icon_data_.insert(fetched_icon_data_.begin(),
104                              response_data.begin(),
105                              response_data.end());
106    BrowserThread::PostTask(
107        BrowserThread::IO,
108        FROM_HERE,
109        base::Bind(&WebstoreInstallHelper::StartFetchedImageDecode, this));
110  }
111  url_fetcher_.reset();
112}
113
114void WebstoreInstallHelper::StartFetchedImageDecode() {
115  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
116  CHECK(utility_host_.get());
117  utility_host_->Send(new ChromeUtilityMsg_DecodeImage(fetched_icon_data_,
118                                                       false));
119}
120
121
122bool WebstoreInstallHelper::OnMessageReceived(const IPC::Message& message) {
123  bool handled = true;
124  IPC_BEGIN_MESSAGE_MAP(WebstoreInstallHelper, message)
125    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Succeeded,
126                        OnDecodeImageSucceeded)
127    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Failed,
128                        OnDecodeImageFailed)
129    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Succeeded,
130                        OnJSONParseSucceeded)
131    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Failed,
132                        OnJSONParseFailed)
133    IPC_MESSAGE_UNHANDLED(handled = false)
134  IPC_END_MESSAGE_MAP()
135  return handled;
136}
137
138
139void WebstoreInstallHelper::OnDecodeImageSucceeded(
140    const SkBitmap& decoded_image) {
141  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
142  icon_ = decoded_image;
143  icon_decode_complete_ = true;
144  ReportResultsIfComplete();
145}
146
147void WebstoreInstallHelper::OnDecodeImageFailed() {
148  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
149  icon_decode_complete_ = true;
150  error_ = kImageDecodeError;
151  parse_error_ = Delegate::ICON_ERROR;
152  ReportResultsIfComplete();
153}
154
155void WebstoreInstallHelper::OnJSONParseSucceeded(
156    const base::ListValue& wrapper) {
157  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
158  manifest_parse_complete_ = true;
159  const base::Value* value = NULL;
160  CHECK(wrapper.Get(0, &value));
161  if (value->IsType(base::Value::TYPE_DICTIONARY)) {
162    parsed_manifest_.reset(
163        static_cast<const base::DictionaryValue*>(value)->DeepCopy());
164  } else {
165    parse_error_ = Delegate::MANIFEST_ERROR;
166  }
167  ReportResultsIfComplete();
168}
169
170void WebstoreInstallHelper::OnJSONParseFailed(
171    const std::string& error_message) {
172  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
173  manifest_parse_complete_ = true;
174  error_ = error_message;
175  parse_error_ = Delegate::MANIFEST_ERROR;
176  ReportResultsIfComplete();
177}
178
179void WebstoreInstallHelper::ReportResultsIfComplete() {
180  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
181
182  if (!icon_decode_complete_ || !manifest_parse_complete_)
183    return;
184
185  // The utility_host_ will take care of deleting itself after this call.
186  if (utility_host_.get()) {
187    utility_host_->EndBatchMode();
188    utility_host_.reset();
189  }
190
191  BrowserThread::PostTask(
192      BrowserThread::UI,
193      FROM_HERE,
194      base::Bind(&WebstoreInstallHelper::ReportResultFromUIThread, this));
195}
196
197void WebstoreInstallHelper::ReportResultFromUIThread() {
198  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
199  if (error_.empty() && parsed_manifest_)
200    delegate_->OnWebstoreParseSuccess(id_, icon_, parsed_manifest_.release());
201  else
202    delegate_->OnWebstoreParseFailure(id_, parse_error_, error_);
203}
204
205}  // namespace extensions
206