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

Side by Side Diff: chrome/browser/intents/web_intents_registry.cc

Issue 12225076: Delete most web intents code. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 7 years, 10 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/intents/web_intents_registry.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/callback.h"
12 #include "base/string_util.h"
13 #include "base/string16.h"
14 #include "base/utf_string_conversions.h"
15 #include "chrome/browser/intents/default_web_intent_service.h"
16 #include "chrome/browser/intents/native_services.h"
17 #include "chrome/browser/intents/web_intents_util.h"
18 #include "chrome/browser/webdata/web_data_service.h"
19 #include "chrome/common/extensions/extension.h"
20 #include "chrome/common/extensions/extension_set.h"
21 #include "chrome/common/extensions/web_intents_handler.h"
22 #include "googleurl/src/gurl.h"
23 #include "net/base/mime_util.h"
24
25 using extensions::Extension;
26 using net::IsMimeType;
27
28 namespace {
29
30 // TODO(hshi): Temporary workaround for http://crbug.com/134197.
31 // If no user-set default service is found, use built-in QuickOffice Viewer as
32 // default for MS office files. Remove this once full defaults is in place.
33 const char* kQuickOfficeViewerMimeType[] = {
34 "application/msword",
35 "application/vnd.ms-powerpoint",
36 "application/vnd.ms-excel",
37 "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
38 "application/vnd.openxmlformats-officedocument.presentationml.presentation",
39 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
40 };
41
42 typedef base::Callback<void(const WDTypedResult* result)> ResultsHandler;
43 typedef WebIntentsRegistry::IntentServiceList IntentServiceList;
44
45 // Compares two web intents type specifiers to see if there is a match.
46 // First checks if both are MIME types. If so, uses MatchesMimeType.
47 // If not, uses exact string equality.
48 bool WebIntentsTypesMatch(const string16& type1, const string16& type2) {
49 return (IsMimeType(UTF16ToUTF8(type1)) && IsMimeType(UTF16ToUTF8(type2)))
50 ? web_intents::MimeTypesMatch(type1, type2)
51 : type1 == type2;
52 }
53
54 // Adds any intent services of |extension| that match |action| to
55 // |matching_services|.
56 void AddMatchingServicesForExtension(const Extension& extension,
57 const string16& action,
58 IntentServiceList* matching_services) {
59 const IntentServiceList& services =
60 extensions::WebIntentsInfo::GetIntentsServices(&extension);
61 for (IntentServiceList::const_iterator i = services.begin();
62 i != services.end(); ++i) {
63 if (action.empty() || action == i->action)
64 matching_services->push_back(*i);
65 }
66 }
67
68 // Removes all services from |matching_services| that do not match |type|.
69 // Wildcards are supported, of the form '<type>/*' or '*'.
70 void FilterServicesByType(const string16& type,
71 IntentServiceList* matching_services) {
72 // Filter out all services not matching the query type.
73 IntentServiceList::iterator iter(matching_services->begin());
74 while (iter != matching_services->end()) {
75 if (WebIntentsTypesMatch(iter->type, type))
76 ++iter;
77 else
78 iter = matching_services->erase(iter);
79 }
80 }
81
82 // Callback for existence checks. Converts a callback for a list of services
83 // into a callback that returns true if the list contains a specific service.
84 void ExistenceCallback(const webkit_glue::WebIntentServiceData& service,
85 const base::Callback<void(bool)>& callback,
86 const WDTypedResult* result) {
87
88 WebIntentsRegistry::IntentServiceList list = static_cast<
89 const WDResult<IntentServiceList>*>(result)->GetValue();
90
91 for (WebIntentsRegistry::IntentServiceList::const_iterator i = list.begin();
92 i != list.end(); ++i) {
93 if (*i == service) {
94 callback.Run(true);
95 return;
96 }
97 }
98
99 callback.Run(false);
100 }
101
102 // Functor object for intent ordering.
103 struct IntentOrdering {
104 // Implements StrictWeakOrdering for intents, based on intent-equivalence.
105 // Order by |service_url|, |action|, |title|, and |disposition| in order.
106 bool operator()(const webkit_glue::WebIntentServiceData& lhs,
107 const webkit_glue::WebIntentServiceData& rhs) {
108 if (lhs.service_url != rhs.service_url)
109 return lhs.service_url < rhs.service_url;
110
111 if (lhs.action != rhs.action)
112 return lhs.action < rhs.action;
113
114 if (lhs.title != rhs.title)
115 return lhs.title < rhs.title;
116
117 if (lhs.disposition != rhs.disposition)
118 return lhs.disposition < rhs.disposition;
119
120 // At this point, we consider intents to be equal, even if |type| differs.
121 return false;
122 }
123 };
124
125 // Two intents are equivalent iff all fields except |type| are equal.
126 bool IntentsAreEquivalent(const webkit_glue::WebIntentServiceData& lhs,
127 const webkit_glue::WebIntentServiceData& rhs) {
128 return !((lhs.service_url != rhs.service_url) ||
129 (lhs.action != rhs.action) ||
130 (lhs.title != rhs.title) ||
131 (lhs.disposition != rhs.disposition));
132 }
133
134 } // namespace
135
136 using webkit_glue::WebIntentServiceData;
137
138 // Internal object containing arguments to be used in post processing
139 // WDS results.
140 struct WebIntentsRegistry::QueryParams {
141
142 // The particular action to filter for while searching through extensions.
143 // If |action_| is empty, return all extension-provided services.
144 string16 action_;
145
146 // The MIME type that was requested for this service query.
147 // Suppports wild cards.
148 string16 type_;
149
150 // The url of the invoking page.
151 GURL url_;
152
153 // Create a new QueryParams for all intent services or for existence checks.
154 QueryParams() : type_(ASCIIToUTF16("*")) {}
155
156 QueryParams(const string16& action, const string16& type)
157 : action_(action), type_(type) {}
158
159 // Create a new QueryParams for default services.
160 QueryParams(const string16& action, const string16& type, const GURL& url)
161 : action_(action), type_(type), url_(url) {}
162 };
163
164 // Internal object adapting the WDS consumer interface to base::Bind
165 // callback way of doing business.
166 class WebIntentsRegistry::QueryAdapter : public WebDataServiceConsumer {
167
168 public:
169 // Underlying data query.
170 WebDataService::Handle query_handle_;
171
172 // Create a new QueryAdapter that delegates results to |handler|.
173 QueryAdapter(WebIntentsRegistry* registry, const ResultsHandler& handler)
174 : registry_(registry), handler_(handler) {
175 registry_->TrackQuery(this);
176 }
177
178 void OnWebDataServiceRequestDone(
179 WebDataService::Handle h,
180 const WDTypedResult* result) OVERRIDE {
181
182 handler_.Run(result);
183 registry_->ReleaseQuery(this);
184 }
185
186 private:
187 // Handle so we can call back into the WebIntentsRegistry when
188 // processing query results. The registry is guaranteed to be
189 // valid for the life of this object. We do not own this object.
190 WebIntentsRegistry* registry_;
191
192 // The callback for this particular query.
193 ResultsHandler handler_;
194 };
195
196 WebIntentsRegistry::WebIntentsRegistry() {
197 native_services_.reset(new web_intents::NativeServiceRegistry());
198 }
199
200 WebIntentsRegistry::~WebIntentsRegistry() {
201
202 // Cancel all pending queries, since we can't handle them any more.
203 for (QueryVector::iterator it = pending_queries_.begin();
204 it != pending_queries_.end(); ++it) {
205 QueryAdapter* query = *it;
206 wds_->CancelRequest(query->query_handle_);
207 delete query;
208 }
209 }
210
211 void WebIntentsRegistry::Initialize(
212 scoped_refptr<WebDataService> wds,
213 ExtensionServiceInterface* extension_service) {
214 wds_ = wds;
215 extension_service_ = extension_service;
216 }
217
218 void WebIntentsRegistry::OnWebIntentsResultReceived(
219 const QueryParams& params,
220 const QueryCallback& callback,
221 const WDTypedResult* result) {
222 DCHECK(result);
223 DCHECK(result->GetType() == WEB_INTENTS_RESULT);
224
225 IntentServiceList matching_services = static_cast<
226 const WDResult<IntentServiceList>*>(result)->GetValue();
227
228 // Loop over all services in all extensions, collect ones
229 // matching the query.
230 if (extension_service_) {
231 const ExtensionSet* extensions = extension_service_->extensions();
232 if (extensions) {
233 for (ExtensionSet::const_iterator i(extensions->begin());
234 i != extensions->end(); ++i) {
235 AddMatchingServicesForExtension(**i, params.action_,
236 &matching_services);
237 }
238 }
239 }
240
241 // add native services.
242 native_services_->GetSupportedServices(params.action_, &matching_services);
243
244 // Filter out all services not matching the query type.
245 FilterServicesByType(params.type_, &matching_services);
246
247 // Collapse intents that are equivalent for all but |type|.
248 CollapseIntents(&matching_services);
249
250 callback.Run(matching_services);
251 }
252
253 void WebIntentsRegistry::OnAllDefaultIntentServicesReceived(
254 const DefaultIntentServicesCallback& callback,
255 const WDTypedResult* result) {
256 DCHECK(result);
257 DCHECK(result->GetType() == WEB_INTENTS_DEFAULTS_RESULT);
258
259 const std::vector<DefaultWebIntentService> services = static_cast<
260 const WDResult<std::vector<DefaultWebIntentService> >*>(result)->
261 GetValue();
262
263 callback.Run(services);
264 }
265
266 void WebIntentsRegistry::OnWebIntentsDefaultsResultReceived(
267 const QueryParams& params,
268 const DefaultQueryCallback& callback,
269 const WDTypedResult* result) {
270 DCHECK(result);
271 DCHECK(result->GetType() == WEB_INTENTS_DEFAULTS_RESULT);
272
273 std::vector<DefaultWebIntentService> services = static_cast<
274 const WDResult<std::vector<DefaultWebIntentService> >*>(result)->
275 GetValue();
276
277 DefaultWebIntentService default_service;
278 std::vector<DefaultWebIntentService>::iterator iter(services.begin());
279 for (; iter != services.end(); ++iter) {
280 if (!WebIntentsTypesMatch(iter->type, params.type_)) {
281 continue;
282 }
283 if (!iter->url_pattern.MatchesURL(params.url_)) {
284 continue;
285 }
286 const Extension* extension = ExtensionForURL(iter->service_url);
287 if (extension != NULL &&
288 !extension_service_->IsExtensionEnabled(extension->id())) {
289 continue;
290 }
291
292 // Found a match. If it is better than default_service, use it.
293 // Currently the metric is that if the new value is user-set,
294 // prefer it. If the new value has a more specific pattern, prefer it.
295 // If the new value has a more recent date, prefer it.
296 if (default_service.user_date <= 0 && iter->user_date >= 0) {
297 default_service = *iter;
298 } else if (default_service.url_pattern.match_all_urls() &&
299 !iter->url_pattern.match_all_urls()) {
300 default_service = *iter;
301 } else if (iter->url_pattern < default_service.url_pattern) {
302 default_service = *iter;
303 } else if (default_service.user_date < iter->user_date) {
304 default_service = *iter;
305 }
306 }
307
308 // If QuickOffice is the default for this, recompute defaults.
309 if (default_service.service_url
310 == web_intents::kQuickOfficeViewerServiceURL ||
311 default_service.service_url
312 == web_intents::kQuickOfficeViewerDevServiceURL) {
313 default_service.user_date = -1;
314 }
315
316 // TODO(hshi): Temporary workaround for http://crbug.com/134197.
317 // If no user-set default service is found, use built-in QuickOffice Viewer as
318 // default for MS office files. Remove this once full defaults is in place.
319
320 if (default_service.user_date <= 0) {
321 const char kQuickOfficeDevExtensionId[] =
322 "ionpfmkccalenbmnddpbmocokhaknphg";
323 std::string service_url = web_intents::kQuickOfficeViewerServiceURL;
324 if (extension_service_->GetInstalledExtension(kQuickOfficeDevExtensionId) &&
325 extension_service_->IsExtensionEnabled(kQuickOfficeDevExtensionId)) {
326 service_url = web_intents::kQuickOfficeViewerDevServiceURL;
327 }
328
329 for (size_t i = 0; i < sizeof(kQuickOfficeViewerMimeType) / sizeof(char*);
330 ++i) {
331 DefaultWebIntentService qoviewer_service;
332 qoviewer_service.action = ASCIIToUTF16(web_intents::kActionView);
333 qoviewer_service.type = ASCIIToUTF16(kQuickOfficeViewerMimeType[i]);
334 qoviewer_service.service_url = service_url;
335 if (WebIntentsTypesMatch(qoviewer_service.type, params.type_)) {
336 default_service = qoviewer_service;
337 break;
338 }
339 }
340 }
341
342 callback.Run(default_service);
343 }
344
345 void WebIntentsRegistry::GetIntentServices(
346 const string16& action, const string16& type,
347 const QueryCallback& callback) {
348 DCHECK(wds_.get());
349 DCHECK(!callback.is_null());
350
351 const QueryParams params(action, type);
352 const ResultsHandler handler = base::Bind(
353 &WebIntentsRegistry::OnWebIntentsResultReceived,
354 base::Unretained(this),
355 params,
356 callback);
357
358 QueryAdapter* query = new QueryAdapter(this, handler);
359 query->query_handle_ = wds_->GetWebIntentServicesForAction(action, query);
360 }
361
362 void WebIntentsRegistry::GetAllIntentServices(
363 const QueryCallback& callback) {
364 DCHECK(wds_.get());
365 DCHECK(!callback.is_null());
366
367 const QueryParams params;
368 const ResultsHandler handler = base::Bind(
369 &WebIntentsRegistry::OnWebIntentsResultReceived,
370 base::Unretained(this),
371 params,
372 callback);
373
374 QueryAdapter* query = new QueryAdapter(this, handler);
375 query->query_handle_ = wds_->GetAllWebIntentServices(query);
376 }
377
378 void WebIntentsRegistry::GetAllDefaultIntentServices(
379 const DefaultIntentServicesCallback& callback) {
380 DCHECK(!callback.is_null());
381
382 ResultsHandler handler = base::Bind(
383 &WebIntentsRegistry::OnAllDefaultIntentServicesReceived,
384 base::Unretained(this),
385 callback);
386
387 QueryAdapter* query = new QueryAdapter(this, handler);
388 query->query_handle_ =
389 wds_->GetAllDefaultWebIntentServices(query);
390 }
391
392 void WebIntentsRegistry::IntentServiceExists(
393 const WebIntentServiceData& service,
394 const base::Callback<void(bool)>& callback) {
395 DCHECK(!callback.is_null());
396
397 ResultsHandler handler = base::Bind(
398 &ExistenceCallback,
399 service,
400 callback);
401
402 QueryAdapter* query = new QueryAdapter(this, handler);
403 query->query_handle_ = wds_->GetWebIntentServicesForURL(
404 UTF8ToUTF16(service.service_url.spec()), query);
405 }
406
407 void WebIntentsRegistry::GetIntentServicesForExtensionFilter(
408 const string16& action,
409 const string16& type,
410 const std::string& extension_id,
411 IntentServiceList* services) {
412 if (extension_service_) {
413 const Extension* extension =
414 extension_service_->GetExtensionById(extension_id, false);
415 AddMatchingServicesForExtension(*extension,
416 action,
417 services);
418 FilterServicesByType(type, services);
419 }
420 }
421
422 void WebIntentsRegistry::RegisterDefaultIntentService(
423 const DefaultWebIntentService& default_service) {
424 DCHECK(wds_.get());
425 wds_->AddDefaultWebIntentService(default_service);
426 }
427
428 void WebIntentsRegistry::UnregisterDefaultIntentService(
429 const DefaultWebIntentService& default_service) {
430 DCHECK(wds_.get());
431 wds_->RemoveDefaultWebIntentService(default_service);
432 }
433
434 void WebIntentsRegistry::UnregisterServiceDefaults(const GURL& service_url) {
435 DCHECK(wds_.get());
436 wds_->RemoveWebIntentServiceDefaults(service_url);
437 }
438
439 void WebIntentsRegistry::GetDefaultIntentService(
440 const string16& action,
441 const string16& type,
442 const GURL& invoking_url,
443 const DefaultQueryCallback& callback) {
444 DCHECK(!callback.is_null());
445
446 const QueryParams params(action, type);
447
448 ResultsHandler handler = base::Bind(
449 &WebIntentsRegistry::OnWebIntentsDefaultsResultReceived,
450 base::Unretained(this),
451 params,
452 callback);
453
454 QueryAdapter* query = new QueryAdapter(this, handler);
455 query->query_handle_ =
456 wds_->GetDefaultWebIntentServicesForAction(action, query);
457 }
458
459 void WebIntentsRegistry::RegisterIntentService(
460 const WebIntentServiceData& service) {
461 DCHECK(wds_.get());
462 wds_->AddWebIntentService(service);
463 }
464
465 void WebIntentsRegistry::UnregisterIntentService(
466 const WebIntentServiceData& service) {
467 DCHECK(wds_.get());
468 wds_->RemoveWebIntentService(service);
469 }
470
471 void WebIntentsRegistry::CollapseIntents(IntentServiceList* services) {
472 DCHECK(services);
473
474 // No need to do anything for no services/single service.
475 if (services->size() < 2)
476 return;
477
478 // Sort so that intents that can be collapsed must be adjacent.
479 std::sort(services->begin(), services->end(), IntentOrdering());
480
481 // Combine adjacent services if they are equivalent.
482 IntentServiceList::iterator write_iter = services->begin();
483 IntentServiceList::iterator read_iter = write_iter + 1;
484 while (read_iter != services->end()) {
485 if (IntentsAreEquivalent(*write_iter, *read_iter)) {
486 // If the two intents are equivalent, join types and collapse.
487 write_iter->type += ASCIIToUTF16(",") + read_iter->type;
488 } else {
489 // Otherwise, keep both intents.
490 ++write_iter;
491 if (write_iter != read_iter)
492 *write_iter = *read_iter;
493 }
494 ++read_iter;
495 }
496
497 // Cut off everything after the last intent copied to the list.
498 if (++write_iter != services->end())
499 services->erase(write_iter, services->end());
500 }
501
502 const Extension* WebIntentsRegistry::ExtensionForURL(const std::string& url) {
503 const ExtensionSet* extensions = extension_service_->extensions();
504 if (!extensions)
505 return NULL;
506
507 // Use the unsafe ExtensionURLInfo constructor: we don't care if the extension
508 // is running or not.
509 GURL gurl(url);
510 ExtensionURLInfo info(gurl);
511 return extensions->GetExtensionOrAppByURL(info);
512 }
513
514 void WebIntentsRegistry::TrackQuery(QueryAdapter* query) {
515 DCHECK(query);
516 pending_queries_.push_back(query);
517 }
518
519 void WebIntentsRegistry::ReleaseQuery(QueryAdapter* query) {
520 QueryVector::iterator it = std::find(
521 pending_queries_.begin(), pending_queries_.end(), query);
522 if (it != pending_queries_.end()) {
523 pending_queries_.erase(it);
524 delete query;
525 } else {
526 NOTREACHED();
527 }
528 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698