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

Side by Side Diff: chrome/browser/extensions/api/omnibox/omnibox_api.cc

Issue 12314164: Modified Omnibox extension api to use JSON Schema Compiler (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: kalman's requests Created 7 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
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/api/omnibox/omnibox_api.h" 5 #include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
6 6
7 #include "base/json/json_writer.h" 7 #include "base/json/json_writer.h"
8 #include "base/lazy_instance.h" 8 #include "base/lazy_instance.h"
9 #include "base/metrics/histogram.h" 9 #include "base/metrics/histogram.h"
10 #include "base/string16.h" 10 #include "base/string16.h"
11 #include "base/utf_string_conversions.h" 11 #include "base/utf_string_conversions.h"
12 #include "base/values.h" 12 #include "base/values.h"
13 #include "chrome/browser/extensions/event_router.h" 13 #include "chrome/browser/extensions/event_router.h"
14 #include "chrome/browser/extensions/extension_prefs.h" 14 #include "chrome/browser/extensions/extension_prefs.h"
15 #include "chrome/browser/extensions/extension_service.h" 15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/extension_system.h" 16 #include "chrome/browser/extensions/extension_system.h"
17 #include "chrome/browser/extensions/tab_helper.h" 17 #include "chrome/browser/extensions/tab_helper.h"
18 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/search_engines/template_url.h" 19 #include "chrome/browser/search_engines/template_url.h"
20 #include "chrome/browser/search_engines/template_url_service.h" 20 #include "chrome/browser/search_engines/template_url_service.h"
21 #include "chrome/browser/search_engines/template_url_service_factory.h" 21 #include "chrome/browser/search_engines/template_url_service_factory.h"
22 #include "chrome/common/chrome_notification_types.h" 22 #include "chrome/common/chrome_notification_types.h"
23 #include "chrome/common/extensions/api/omnibox.h"
23 #include "chrome/common/extensions/api/omnibox/omnibox_handler.h" 24 #include "chrome/common/extensions/api/omnibox/omnibox_handler.h"
24 #include "chrome/common/extensions/extension.h" 25 #include "chrome/common/extensions/extension.h"
25 #include "content/public/browser/notification_details.h" 26 #include "content/public/browser/notification_details.h"
26 #include "content/public/browser/notification_service.h" 27 #include "content/public/browser/notification_service.h"
27 #include "ui/gfx/image/image.h" 28 #include "ui/gfx/image/image.h"
28 29
29 namespace events { 30 namespace events {
30 const char kOnInputStarted[] = "omnibox.onInputStarted"; 31 const char kOnInputStarted[] = "omnibox.onInputStarted";
31 const char kOnInputChanged[] = "omnibox.onInputChanged"; 32 const char kOnInputChanged[] = "omnibox.onInputChanged";
32 const char kOnInputEntered[] = "omnibox.onInputEntered"; 33 const char kOnInputEntered[] = "omnibox.onInputEntered";
33 const char kOnInputCancelled[] = "omnibox.onInputCancelled"; 34 const char kOnInputCancelled[] = "omnibox.onInputCancelled";
34 } // namespace events 35 } // namespace events
35 36
36 namespace extensions { 37 namespace extensions {
37 38
39 namespace omnibox = extensions::api::omnibox;
not at google - send to devlin 2013/03/16 00:04:25 fwiw extensions:: is unnecessary because we're alr
Aaron Jacobs 2013/03/21 21:59:55 Done.
40 namespace SendSuggestions = omnibox::SendSuggestions;
41 namespace SetDefaultSuggestion = omnibox::SetDefaultSuggestion;
42
38 namespace { 43 namespace {
39 44
40 const char kSuggestionContent[] = "content"; 45 const char kSuggestionContent[] = "content";
41 const char kSuggestionDescription[] = "description"; 46 const char kSuggestionDescription[] = "description";
42 const char kSuggestionDescriptionStyles[] = "descriptionStyles"; 47 const char kSuggestionDescriptionStyles[] = "descriptionStyles";
43 const char kSuggestionDescriptionStylesRaw[] = "descriptionStylesRaw"; 48 const char kSuggestionDescriptionStylesRaw[] = "descriptionStylesRaw";
44 const char kDescriptionStylesType[] = "type"; 49 const char kDescriptionStylesType[] = "type";
45 const char kDescriptionStylesOffset[] = "offset"; 50 const char kDescriptionStylesOffset[] = "offset";
46 const char kDescriptionStylesLength[] = "length"; 51 const char kDescriptionStylesLength[] = "length";
47 52
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 omnibox_popup_icon_manager_.GetIcon(extension_id)); 224 omnibox_popup_icon_manager_.GetIcon(extension_id));
220 } 225 }
221 226
222 template <> 227 template <>
223 void ProfileKeyedAPIFactory<OmniboxAPI>::DeclareFactoryDependencies() { 228 void ProfileKeyedAPIFactory<OmniboxAPI>::DeclareFactoryDependencies() {
224 DependsOn(ExtensionSystemFactory::GetInstance()); 229 DependsOn(ExtensionSystemFactory::GetInstance());
225 DependsOn(TemplateURLServiceFactory::GetInstance()); 230 DependsOn(TemplateURLServiceFactory::GetInstance());
226 } 231 }
227 232
228 bool OmniboxSendSuggestionsFunction::RunImpl() { 233 bool OmniboxSendSuggestionsFunction::RunImpl() {
229 ExtensionOmniboxSuggestions suggestions; 234 scoped_ptr<SendSuggestions::Params> params(
230 ListValue* suggestions_value; 235 SendSuggestions::Params::Create(*args_));
231 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &suggestions.request_id)); 236 EXTENSION_FUNCTION_VALIDATE(params);
232 EXTENSION_FUNCTION_VALIDATE(args_->GetList(1, &suggestions_value));
233
234 suggestions.suggestions.resize(suggestions_value->GetSize());
235 for (size_t i = 0; i < suggestions_value->GetSize(); ++i) {
236 ExtensionOmniboxSuggestion& suggestion = suggestions.suggestions[i];
237 DictionaryValue* suggestion_value;
238 EXTENSION_FUNCTION_VALIDATE(suggestions_value->GetDictionary(
239 i, &suggestion_value));
240 EXTENSION_FUNCTION_VALIDATE(suggestion.Populate(*suggestion_value, true));
241 }
242 237
243 content::NotificationService::current()->Notify( 238 content::NotificationService::current()->Notify(
244 chrome::NOTIFICATION_EXTENSION_OMNIBOX_SUGGESTIONS_READY, 239 chrome::NOTIFICATION_EXTENSION_OMNIBOX_SUGGESTIONS_READY,
245 content::Source<Profile>(profile_->GetOriginalProfile()), 240 content::Source<Profile>(profile_->GetOriginalProfile()),
246 content::Details<ExtensionOmniboxSuggestions>(&suggestions)); 241 content::Details<SendSuggestions::Params>(params.get()));
247 242
248 return true; 243 return true;
249 } 244 }
250 245
251 bool OmniboxSetDefaultSuggestionFunction::RunImpl() { 246 bool OmniboxSetDefaultSuggestionFunction::RunImpl() {
252 ExtensionOmniboxSuggestion suggestion; 247 scoped_ptr<SetDefaultSuggestion::Params> params(
253 DictionaryValue* suggestion_value; 248 SetDefaultSuggestion::Params::Create(*args_));
254 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &suggestion_value)); 249 EXTENSION_FUNCTION_VALIDATE(params);
255 EXTENSION_FUNCTION_VALIDATE(suggestion.Populate(*suggestion_value, false)); 250
251 if (!params->suggestion.description_styles) {
not at google - send to devlin 2013/03/16 00:04:25 I think it would be better to just do params->sugg
Aaron Jacobs 2013/03/21 21:59:55 This section of code was not actually needed, as i
252 params->suggestion.description_styles.reset(
253 new std::vector<linked_ptr<
254 omnibox::SuggestDefaultResult::DescriptionStylesType> >);
not at google - send to devlin 2013/03/16 00:04:25 I can't remember what SuggestDefaultResult is, but
Aaron Jacobs 2013/03/21 21:59:55 Done.
255 }
256 params->suggestion.description_styles->clear();
256 257
257 ExtensionPrefs* prefs = 258 ExtensionPrefs* prefs =
258 ExtensionSystem::Get(profile())->extension_service()->extension_prefs(); 259 ExtensionSystem::Get(profile())->extension_service()->extension_prefs();
259 if (prefs) 260 if (prefs)
260 prefs->SetOmniboxDefaultSuggestion(extension_id(), suggestion); 261 prefs->SetOmniboxDefaultSuggestion(extension_id(), params->suggestion);
261 262
262 content::NotificationService::current()->Notify( 263 content::NotificationService::current()->Notify(
263 chrome::NOTIFICATION_EXTENSION_OMNIBOX_DEFAULT_SUGGESTION_CHANGED, 264 chrome::NOTIFICATION_EXTENSION_OMNIBOX_DEFAULT_SUGGESTION_CHANGED,
264 content::Source<Profile>(profile_->GetOriginalProfile()), 265 content::Source<Profile>(profile_->GetOriginalProfile()),
265 content::NotificationService::NoDetails()); 266 content::NotificationService::NoDetails());
266 267
267 return true; 268 return true;
268 } 269 }
269 270
270 ExtensionOmniboxSuggestion::ExtensionOmniboxSuggestion() {} 271 // This function converts style information populated by the JSON schema
272 // compiler into an ACMatchClassifications object.
273 ACMatchClassifications StyleTypesToACMatchClassifications(
274 const omnibox::SuggestResult &suggestion) {
275 ACMatchClassifications match_classifications;
276 if (suggestion.description_styles) {
277 const std::vector<linked_ptr<
278 omnibox::SuggestResult::DescriptionStylesType> > &style_types =
279 *suggestion.description_styles;
not at google - send to devlin 2013/03/16 00:04:25 This is pretty unwieldy. Just use an iterator belo
Aaron Jacobs 2013/03/21 21:59:55 Done.
280 string16 description = UTF8ToUTF16(suggestion.description);
281 std::vector<int> styles;
282 styles.resize(description.length()); // sets all styles to 0
271 283
272 ExtensionOmniboxSuggestion::~ExtensionOmniboxSuggestion() {} 284 for (size_t i = 0; i < style_types.size(); ++i) {
285 int type_class, offset, length;
273 286
274 bool ExtensionOmniboxSuggestion::Populate(const base::DictionaryValue& value, 287 if (style_types[i].get() && style_types[i]->length)
not at google - send to devlin 2013/03/16 00:04:25 don't need .get(), use !empty()
Aaron Jacobs 2013/03/21 21:59:55 I can't remove .get(), as it is operating on a lin
275 bool require_content) { 288 length = *style_types[i]->length;
276 if (!value.GetString(kSuggestionContent, &content) && require_content) 289 if (length < 0)
277 return false; 290 length = description.length();
278 291
279 if (!value.GetString(kSuggestionDescription, &description)) 292 offset = style_types[i]->offset;
280 return false; 293 if (offset < 0)
294 offset = std::max(0, static_cast<int>(description.length()) + offset);
281 295
282 description_styles.clear(); 296 if (style_types[i]->type ==
not at google - send to devlin 2013/03/16 00:04:25 use switch
Aaron Jacobs 2013/03/21 21:59:55 Done.
283 if (value.HasKey(kSuggestionDescriptionStyles)) { 297 omnibox::SuggestResult::DescriptionStylesType::TYPE_URL) {
284 // This version comes from the extension. 298 type_class = AutocompleteMatch::ACMatchClassification::URL;
285 const ListValue* styles = NULL; 299 } else if (style_types[i]->type ==
286 if (!value.GetList(kSuggestionDescriptionStyles, &styles) || 300 omnibox::SuggestResult::DescriptionStylesType::TYPE_MATCH) {
287 !ReadStylesFromValue(*styles)) { 301 type_class = AutocompleteMatch::ACMatchClassification::MATCH;
288 return false; 302 } else if (style_types[i]->type ==
303 omnibox::SuggestResult::DescriptionStylesType::TYPE_DIM) {
304 type_class = AutocompleteMatch::ACMatchClassification::DIM;
305 } else {
306 type_class = AutocompleteMatch::ACMatchClassification::NONE;
307 return match_classifications;
308 }
309
310 for (int j = offset;
311 j < offset + length && j < static_cast<int>(styles.size()); ++j)
312 styles[j] |= type_class;
289 } 313 }
290 } else if (value.HasKey(kSuggestionDescriptionStylesRaw)) { 314
291 // This version comes from ToValue(), which we use to persist to disk. 315 for (size_t i = 0; i < styles.size(); ++i) {
292 const ListValue* styles = NULL; 316 if (i ==0 || styles[i] != styles[i-1])
293 if (!value.GetList(kSuggestionDescriptionStylesRaw, &styles) || 317 match_classifications.push_back(
294 styles->empty()) { 318 ACMatchClassification(i, styles[i]));
295 return false;
296 }
297 for (size_t i = 0; i < styles->GetSize(); ++i) {
298 const base::DictionaryValue* style = NULL;
299 int offset, type;
300 if (!styles->GetDictionary(i, &style))
301 return false;
302 if (!style->GetInteger(kDescriptionStylesType, &type))
303 return false;
304 if (!style->GetInteger(kDescriptionStylesOffset, &offset))
305 return false;
306 description_styles.push_back(ACMatchClassification(offset, type));
307 } 319 }
308 } else { 320 } else {
309 description_styles.push_back( 321 match_classifications.push_back(
310 ACMatchClassification(0, ACMatchClassification::NONE)); 322 ACMatchClassification(0, ACMatchClassification::NONE));
311 } 323 }
312 324
313 return true; 325 return match_classifications;
314 } 326 }
315 327
316 bool ExtensionOmniboxSuggestion::ReadStylesFromValue( 328 // This function converts style information populated by the JSON schema
317 const ListValue& styles_value) { 329 // compiler into an ACMatchClassifications object.
318 description_styles.clear(); 330 ACMatchClassifications DefaultStyleTypesToACMatchClassifications(
not at google - send to devlin 2013/03/16 00:04:25 what is the difference between this function and t
Aaron Jacobs 2013/03/21 21:59:55 They served pretty much the same purpose, though w
331 const omnibox::SuggestDefaultResult &suggestion) {
332 ACMatchClassifications match_classifications;
333 if (suggestion.description_styles) {
334 const std::vector<linked_ptr<
335 omnibox::SuggestDefaultResult::DescriptionStylesType> > &style_types =
336 *suggestion.description_styles;
337 string16 description = UTF8ToUTF16(suggestion.description);
338 std::vector<int> styles;
339 styles.resize(description.length()); // sets all styles to 0
319 340
320 // Step 1: Build a vector of styles, 1 per character of description text. 341 for (size_t i = 0; i < style_types.size(); ++i) {
321 std::vector<int> styles; 342 int type_class, offset, length;
322 styles.resize(description.length()); // sets all styles to 0
323 343
324 for (size_t i = 0; i < styles_value.GetSize(); ++i) { 344 if (style_types[i].get() && style_types[i]->length)
325 const DictionaryValue* style; 345 length = *style_types[i]->length;
326 std::string type; 346 if (length < 0)
327 int offset; 347 length = description.length();
328 int length;
329 if (!styles_value.GetDictionary(i, &style))
330 return false;
331 if (!style->GetString(kDescriptionStylesType, &type))
332 return false;
333 if (!style->GetInteger(kDescriptionStylesOffset, &offset))
334 return false;
335 if (!style->GetInteger(kDescriptionStylesLength, &length) || length < 0)
336 length = description.length();
337 348
338 if (offset < 0) 349 offset = style_types[i]->offset;
339 offset = std::max(0, static_cast<int>(description.length()) + offset); 350 if (offset < 0)
351 offset = std::max(0, static_cast<int>(description.length()) + offset);
340 352
341 int type_class = 353 if (style_types[i]->type ==
342 (type == "url") ? ACMatchClassification::URL : 354 omnibox::SuggestDefaultResult::DescriptionStylesType::TYPE_URL) {
343 (type == "match") ? ACMatchClassification::MATCH : 355 type_class = AutocompleteMatch::ACMatchClassification::URL;
344 (type == "dim") ? ACMatchClassification::DIM : -1; 356 } else if (style_types[i]->type ==
345 if (type_class == -1) 357 omnibox::SuggestDefaultResult::DescriptionStylesType::TYPE_MATCH) {
346 return false; 358 type_class = AutocompleteMatch::ACMatchClassification::MATCH;
359 } else if (style_types[i]->type ==
360 omnibox::SuggestDefaultResult::DescriptionStylesType::TYPE_DIM) {
361 type_class = AutocompleteMatch::ACMatchClassification::DIM;
362 } else {
363 type_class = AutocompleteMatch::ACMatchClassification::NONE;
364 return match_classifications;
365 }
347 366
348 for (int j = offset; 367 for (int j = offset;
349 j < offset + length && j < static_cast<int>(styles.size()); ++j) 368 j < offset + length && j < static_cast<int>(styles.size()); ++j)
350 styles[j] |= type_class; 369 styles[j] |= type_class;
370 }
371
372 for (size_t i = 0; i < styles.size(); ++i) {
373 if (i ==0 || styles[i] != styles[i-1])
374 match_classifications.push_back(
375 ACMatchClassification(i, styles[i]));
376 }
377 } else {
378 match_classifications.push_back(
379 ACMatchClassification(0, ACMatchClassification::NONE));
351 } 380 }
352 381
353 // Step 2: Convert the vector into continuous runs of common styles. 382 return match_classifications;
354 for (size_t i = 0; i < styles.size(); ++i) {
355 if (i == 0 || styles[i] != styles[i-1])
356 description_styles.push_back(ACMatchClassification(i, styles[i]));
357 }
358
359 return true;
360 } 383 }
361 384
362 scoped_ptr<base::DictionaryValue> ExtensionOmniboxSuggestion::ToValue() const {
363 scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
364
365 value->SetString(kSuggestionContent, content);
366 value->SetString(kSuggestionDescription, description);
367
368 if (description_styles.size() > 0) {
369 base::ListValue* styles_value = new base::ListValue();
370 for (size_t i = 0; i < description_styles.size(); ++i) {
371 base::DictionaryValue* style = new base::DictionaryValue();
372 style->SetInteger(kDescriptionStylesOffset, description_styles[i].offset);
373 style->SetInteger(kDescriptionStylesType, description_styles[i].style);
374 styles_value->Append(style);
375 }
376
377 value->Set(kSuggestionDescriptionStylesRaw, styles_value);
378 }
379
380 return value.Pass();
381 }
382
383 ExtensionOmniboxSuggestions::ExtensionOmniboxSuggestions() : request_id(0) {}
384
385 ExtensionOmniboxSuggestions::~ExtensionOmniboxSuggestions() {}
386
387 void ApplyDefaultSuggestionForExtensionKeyword( 385 void ApplyDefaultSuggestionForExtensionKeyword(
388 Profile* profile, 386 Profile* profile,
389 const TemplateURL* keyword, 387 const TemplateURL* keyword,
390 const string16& remaining_input, 388 const string16& remaining_input,
391 AutocompleteMatch* match) { 389 AutocompleteMatch* match) {
392 DCHECK(keyword->IsExtensionKeyword()); 390 DCHECK(keyword->IsExtensionKeyword());
393 391
394 ExtensionPrefs* prefs = 392 ExtensionPrefs* prefs =
395 ExtensionSystem::Get(profile)->extension_service()->extension_prefs(); 393 ExtensionSystem::Get(profile)->extension_service()->extension_prefs();
396 if (!prefs) 394 if (!prefs)
397 return; 395 return;
398 396
399 ExtensionOmniboxSuggestion suggestion = 397 scoped_ptr<api::omnibox::SuggestDefaultResult> suggestion(
400 prefs->GetOmniboxDefaultSuggestion(keyword->GetExtensionId()); 398 prefs->GetOmniboxDefaultSuggestion(keyword->GetExtensionId()));
401 if (suggestion.description.empty()) 399 if (!suggestion || suggestion->description.empty())
402 return; // fall back to the universal default 400 return; // fall back to the universal default
403 401
404 const string16 kPlaceholderText(ASCIIToUTF16("%s")); 402 const string16 kPlaceholderText(ASCIIToUTF16("%s"));
405 const string16 kReplacementText(ASCIIToUTF16("<input>")); 403 const string16 kReplacementText(ASCIIToUTF16("<input>"));
406 404
407 string16 description = suggestion.description; 405 string16 description = UTF8ToUTF16(suggestion->description);
408 ACMatchClassifications& description_styles = match->contents_class; 406 ACMatchClassifications& description_styles = match->contents_class;
409 description_styles = suggestion.description_styles; 407 description_styles = DefaultStyleTypesToACMatchClassifications(*suggestion);
410 408
411 // Replace "%s" with the user's input and adjust the style offsets to the 409 // Replace "%s" with the user's input and adjust the style offsets to the
412 // new length of the description. 410 // new length of the description.
413 size_t placeholder(suggestion.description.find(kPlaceholderText, 0)); 411 size_t placeholder(description.find(kPlaceholderText, 0));
414 if (placeholder != string16::npos) { 412 if (placeholder != string16::npos) {
415 string16 replacement = 413 string16 replacement =
416 remaining_input.empty() ? kReplacementText : remaining_input; 414 remaining_input.empty() ? kReplacementText : remaining_input;
417 description.replace(placeholder, kPlaceholderText.length(), replacement); 415 description.replace(placeholder, kPlaceholderText.length(), replacement);
418 416
419 for (size_t i = 0; i < description_styles.size(); ++i) { 417 for (size_t i = 0; i < description_styles.size(); ++i) {
420 if (description_styles[i].offset > placeholder) 418 if (description_styles[i].offset > placeholder)
421 description_styles[i].offset += replacement.length() - 2; 419 description_styles[i].offset += replacement.length() - 2;
422 } 420 }
423 } 421 }
424 422
425 match->contents.assign(description); 423 match->contents.assign(description);
426 } 424 }
427 425
428 } // namespace extensions 426 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698