Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(114)

Side by Side Diff: chrome/browser/chrome_to_mobile_service.cc

Issue 9443007: Add Chrome To Mobile Service and Views Page Action. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Put shared CloudPrint consts/helpers in chrome/common/; use CloudPrintURL. Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/chrome_to_mobile_service.h"
6
7 #include "base/bind.h"
8 #include "base/json/json_writer.h"
9 #include "base/stringprintf.h"
10 #include "base/utf_string_conversions.h"
11 #include "base/values.h"
12 #include "chrome/app/chrome_command_ids.h"
13 #include "chrome/common/cloud_print/cloud_print_helpers.h"
14 #include "chrome/browser/printing/cloud_print/cloud_print_url.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/signin/token_service.h"
17 #include "chrome/browser/signin/token_service_factory.h"
18 #include "chrome/browser/ui/browser_list.h"
19 #include "chrome/common/guid.h"
20 #include "chrome/common/net/gaia/gaia_urls.h"
21 #include "chrome/common/net/gaia/oauth2_access_token_fetcher.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/common/url_fetcher.h"
24 #include "net/base/escape.h"
25 #include "net/url_request/url_request_context_getter.h"
26
27 namespace {
28
29 // The URLFetcher max number of retries.
30 size_t kMaxRetries = 10;
31
32 // The request retry delays in seconds.
33 int kAuthRetryDelay = 30;
34 int kAutoSearchRetryDelay = 300;
35
36 const char kOAuth2Scope[] = "https://www.googleapis.com/auth/cloudprint";
37 const char kMobileTypeAndroidChromeSnapshot[] = "ANDROID_CHROME_SNAPSHOT";
38
39 const char kJobTypeURL[] = "url";
40 const char kJobTypeDelayedSnapshot[] = "url_with_delayed_snapshot";
41 const char kJobTypeSnapshot[] = "snapshot";
42
43 std::string GetJobString(const internal::SubmitData& data) {
44 scoped_ptr<DictionaryValue> job(new DictionaryValue());
45 job->SetString("url", data.url.spec());
46 if (data.job_type == internal::URL) {
47 job->SetString("type", kJobTypeURL);
48 } else {
49 job->SetString("snapID", data.snapshot_id);
50 job->SetString("type", (data.job_type == internal::SNAPSHOT) ?
51 kJobTypeSnapshot : kJobTypeDelayedSnapshot);
52 }
53 std::string job_string;
54 base::JSONWriter::Write(job.get(), false, &job_string);
55 return job_string;
56 }
57
58 GURL GetSubmitURL(GURL service_url, const internal::SubmitData& data) {
59 GURL submit_url = cloud_print::GetUrlForSubmit(service_url);
60 if (data.job_type == internal::SNAPSHOT)
61 return submit_url;
62
63 // Append form data to the URL's query for |URL| and |DELAYED_SNAPSHOT| jobs.
64 static const bool kUsePlus = true;
65 std::string tag_string = net::EscapeQueryParamValue(
66 "__c2dm__job_data=" + GetJobString(data), kUsePlus);
67 GURL::Replacements replacements;
68 // Provide dummy content to workaround |errorCode| 412 'Document missing'.
69 std::string query = StringPrintf("printerid=%s&tag=%s&title=%s"
70 "&contentType=text/plain&content=dummy",
71 net::EscapeQueryParamValue(UTF16ToUTF8(data.mobile_id), kUsePlus).c_str(),
72 net::EscapeQueryParamValue(tag_string, kUsePlus).c_str(),
73 net::EscapeQueryParamValue(UTF16ToUTF8(data.title), kUsePlus).c_str());
74 replacements.SetQueryStr(query);
75 return submit_url.ReplaceComponents(replacements);
76 }
77
78 void DeleteFilePath(const FilePath& file_path) {
79 bool success = file_util::Delete(file_path, false);
80 DCHECK(success);
81 }
82
83 void SubmitSnapshot(content::URLFetcher* request,
84 const internal::SubmitData& data) {
85 std::string file;
86 if (file_util::ReadFileToString(data.snapshot_path, &file) && !file.empty()) {
87 std::string post_data, mime_boundary;
88 cloud_print::CreateMimeBoundaryForUpload(&mime_boundary);
89 cloud_print::AddMultipartValueForUpload("printerid",
90 UTF16ToUTF8(data.mobile_id), mime_boundary, std::string(), &post_data);
91 cloud_print::AddMultipartValueForUpload("tag", "__c2dm__job_data=" +
92 GetJobString(data), mime_boundary, std::string(), &post_data);
93 cloud_print::AddMultipartValueForUpload("title", UTF16ToUTF8(data.title),
94 mime_boundary, std::string(), &post_data);
95 cloud_print::AddMultipartValueForUpload("contentType", "multipart/related",
96 mime_boundary, std::string(), &post_data);
97
98 // Append the snapshot MHTML content and terminate the request body.
99 post_data.append("--" + mime_boundary + "\r\n"
100 "Content-Disposition: form-data; "
101 "name=\"content\"; filename=\"blob\"\r\n"
102 "Content-Type: text/mhtml\r\n"
103 "\r\n" + file + "\r\n" "--" + mime_boundary + "--\r\n");
104 std::string content_type = "multipart/form-data; boundary=" + mime_boundary;
105 request->SetUploadData(content_type, post_data);
106 request->Start();
107 }
108
109 content::BrowserThread::PostBlockingPoolTask(FROM_HERE,
110 base::Bind(&DeleteFilePath, data.snapshot_path));
111 }
112
113 } // namespace
114
115 namespace internal {
116
117 SubmitData::SubmitData() {}
118
119 SubmitData::~SubmitData() {}
120
121 }
122
123 ChromeToMobileService::ChromeToMobileService(Profile* profile)
124 : profile_(profile),
125 cloud_print_url_(new CloudPrintURL(profile)) {
126 RequestMobileListUpdate();
127 }
128
129 ChromeToMobileService::~ChromeToMobileService() {}
130
131 void ChromeToMobileService::OnURLFetchComplete(
132 const content::URLFetcher* source) {
133 if (source == search_request_.get())
134 HandleSearchResponse();
135 else
136 HandleSubmitResponse(source);
137 }
138
139 void ChromeToMobileService::OnGetTokenSuccess(
140 const std::string& access_token) {
141 oauth2_request_.reset();
142 request_timer_.Stop();
143 oauth2_token_ = access_token;
144 RequestSearch();
145 }
146
147 void ChromeToMobileService::OnGetTokenFailure(
148 const GoogleServiceAuthError& error) {
149 oauth2_request_.reset();
150 request_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kAuthRetryDelay),
151 this, &ChromeToMobileService::RequestAuth);
152 }
153
154 void ChromeToMobileService::RequestMobileListUpdate() {
155 if (oauth2_token_.empty())
156 RequestAuth();
157 else
158 RequestSearch();
159 }
160
161 void ChromeToMobileService::SendToMobile(const string16& printer_id,
162 const GURL& url,
163 const string16& title,
164 const FilePath& snapshot,
165 base::WeakPtr<Observer> observer) {
166 if (oauth2_token_.empty() || !url.is_valid())
167 return;
168
169 bool send_snapshot = !snapshot.empty();
170 std::string id = send_snapshot ? guid::GenerateGUID() : std::string();
171 internal::SubmitData data;
172 data.mobile_id = printer_id;
173 data.url = url;
174 data.title = title;
175 data.snapshot_path = snapshot;
176 data.snapshot_id = id;
177 data.job_type = send_snapshot ? internal::DELAYED_SNAPSHOT : internal::URL;
178 content::URLFetcher* submit_url = CreateRequest(data);
179 request_observer_map_[submit_url] = observer;
180 submit_url->Start();
181 if (send_snapshot) {
182 data.job_type = internal::SNAPSHOT;
183 content::URLFetcher* submit_snapshot = CreateRequest(data);
184 request_observer_map_[submit_snapshot] = observer;
185 content::BrowserThread::PostBlockingPoolTask(FROM_HERE,
186 base::Bind(&SubmitSnapshot, submit_snapshot, data));
187 }
188 }
189
190 content::URLFetcher* ChromeToMobileService::CreateRequest(
191 const internal::SubmitData& data) {
192 bool get = data.job_type != internal::SNAPSHOT;
193 content::URLFetcher* request = content::URLFetcher::Create(
194 GetSubmitURL(cloud_print_url_->GetCloudPrintServiceURL(), data),
195 get ? content::URLFetcher::GET : content::URLFetcher::POST, this);
196 request->SetRequestContext(profile_->GetRequestContext());
197 request->SetMaxRetries(kMaxRetries);
198 request->SetExtraRequestHeaders("Authorization: OAuth " +
199 oauth2_token_ + "\r\n" + cloud_print::kChromeCloudPrintProxyHeader);
200 return request;
201 }
202
203 void ChromeToMobileService::RequestAuth() {
204 if (oauth2_request_.get() || !oauth2_token_.empty())
205 return;
206
207 GURL auth_url = cloud_print::GetUrlForGetAuthCode(
208 cloud_print_url_->GetCloudPrintServiceURL(),
209 cloud_print::kDefaultCloudPrintOAuthClientId,
210 cloud_print::GenerateProxyId());
211 oauth2_request_.reset(
212 new OAuth2AccessTokenFetcher(this, profile_->GetRequestContext()));
213 std::string token = TokenServiceFactory::GetForProfile(profile_)->
214 GetOAuth2LoginRefreshToken();
215 std::vector<std::string> scopes(1, kOAuth2Scope);
216 oauth2_request_->Start(GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
217 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), token, scopes);
218 }
219
220 void ChromeToMobileService::RequestSearch() {
221 if (search_request_.get() || oauth2_token_.empty())
222 return;
223
224 GURL search_url =
225 cloud_print::GetUrlForSearch(cloud_print_url_->GetCloudPrintServiceURL());
226 search_request_.reset(
227 content::URLFetcher::Create(search_url, content::URLFetcher::GET, this));
228 search_request_->SetRequestContext(profile_->GetRequestContext());
229 search_request_->SetMaxRetries(kMaxRetries);
230 search_request_->SetExtraRequestHeaders("Authorization: OAuth " +
231 oauth2_token_ + "\r\n" + cloud_print::kChromeCloudPrintProxyHeader);
232 search_request_->Start();
233 }
234
235 void ChromeToMobileService::HandleSearchResponse() {
236 std::string data;
237 search_request_->GetResponseAsString(&data);
238 search_request_.reset();
239
240 DictionaryValue* json_data = NULL;
241 ListValue* list = NULL;
242 cloud_print::ParseResponseJSON(data, NULL, &json_data);
243 if (json_data && json_data->GetList(cloud_print::kPrinterListValue, &list)) {
244 mobiles_.clear();
245 for (size_t index = 0; index < list->GetSize(); index++) {
246 DictionaryValue* mobile_data = NULL;
247 if (list->GetDictionary(index, &mobile_data)) {
248 std::string mobile_type;
249 mobile_data->GetString("type", &mobile_type);
250 if (mobile_type.compare(kMobileTypeAndroidChromeSnapshot) == 0)
251 mobiles_.push_back(mobile_data);
252 }
253 }
254 }
255
256 BrowserList::GetLastActiveWithProfile(profile_)->command_updater()->
257 UpdateCommandEnabled(IDC_CHROME_TO_MOBILE_PAGE, !mobiles_.empty());
258
259 if (!request_timer_.IsRunning())
260 request_timer_.Start(FROM_HERE,
261 base::TimeDelta::FromSeconds(kAutoSearchRetryDelay),
262 this, &ChromeToMobileService::RequestSearch);
263 }
264
265 void ChromeToMobileService::HandleSubmitResponse(
266 const content::URLFetcher* source) {
267 // Get the observer for this response; bail if there is none or it is NULL.
268 RequestObserverMap::iterator i = request_observer_map_.find(source);
269 if (i == request_observer_map_.end())
270 return;
271 base::WeakPtr<Observer> observer = i->second;
272 request_observer_map_.erase(i);
273 if (!observer.get())
274 return;
275
276 // Get the success value from the CloudPrint server response data.
277 std::string data;
278 source->GetResponseAsString(&data);
279 DictionaryValue* json_data = NULL;
280 cloud_print::ParseResponseJSON(data, NULL, &json_data);
281 bool success = false;
282 if (json_data)
283 json_data->GetBoolean("success", &success);
284
285 // Check if the observer is waiting on a second response (url and snapshot).
286 RequestObserverMap::iterator other = request_observer_map_.begin();
287 for (; other != request_observer_map_.end(); ++other) {
288 if (other->second == observer) {
289 // Do not call OnSendComplete for observers waiting on a second response.
290 if (success)
291 return;
292
293 // Ensure a second response is not sent after reporting failure below.
294 request_observer_map_.erase(other);
295 break;
296 }
297 }
298
299 observer->OnSendComplete(success);
300 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698