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

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 = api::omnibox;
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);
not at google - send to devlin 2013/03/22 17:33:30 if we don't validate that the content field was se
Aaron Jacobs 2013/03/22 19:54:15 I believe the check is no longer needed now that t
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));
256 250
not at google - send to devlin 2013/03/22 17:33:30 likewise need to check somewhere that it isn't set
Aaron Jacobs 2013/03/22 19:54:15 This should no longer be needed with the modificat
257 ExtensionPrefs* prefs = 251 ExtensionPrefs* prefs =
258 ExtensionSystem::Get(profile())->extension_service()->extension_prefs(); 252 ExtensionSystem::Get(profile())->extension_service()->extension_prefs();
259 if (prefs) 253 if (prefs)
260 prefs->SetOmniboxDefaultSuggestion(extension_id(), suggestion); 254 prefs->SetOmniboxDefaultSuggestion(extension_id(), params->suggestion);
261 255
262 content::NotificationService::current()->Notify( 256 content::NotificationService::current()->Notify(
263 chrome::NOTIFICATION_EXTENSION_OMNIBOX_DEFAULT_SUGGESTION_CHANGED, 257 chrome::NOTIFICATION_EXTENSION_OMNIBOX_DEFAULT_SUGGESTION_CHANGED,
264 content::Source<Profile>(profile_->GetOriginalProfile()), 258 content::Source<Profile>(profile_->GetOriginalProfile()),
265 content::NotificationService::NoDetails()); 259 content::NotificationService::NoDetails());
266 260
267 return true; 261 return true;
268 } 262 }
269 263
270 ExtensionOmniboxSuggestion::ExtensionOmniboxSuggestion() {} 264 // This function converts style information populated by the JSON schema
265 // compiler into an ACMatchClassifications object.
266 ACMatchClassifications StyleTypesToACMatchClassifications(
267 const omnibox::SuggestResult &suggestion) {
268 ACMatchClassifications match_classifications;
269 if (suggestion.description_styles) {
270 string16 description = UTF8ToUTF16(suggestion.description);
271 std::vector<int> styles;
272 styles.resize(description.length()); // sets all styles to 0
not at google - send to devlin 2013/03/22 17:33:30 you should be able to give this as an argument to
Aaron Jacobs 2013/03/22 19:54:15 Done.
271 273
272 ExtensionOmniboxSuggestion::~ExtensionOmniboxSuggestion() {} 274 for (std::vector<linked_ptr<omnibox::SuggestResult::DescriptionStylesType> >
not at google - send to devlin 2013/03/22 17:33:30 wow. maybe we should generate typedefs in the sche
275 ::iterator i = suggestion.description_styles->begin();
276 i != suggestion.description_styles->end(); ++i) {
not at google - send to devlin 2013/03/22 17:33:30 super nit: for loop style is to align vertically,
Aaron Jacobs 2013/03/22 19:54:15 Done.
277 int type_class, offset, length;
not at google - send to devlin 2013/03/22 17:33:30 just declare them when they're initialized.
Aaron Jacobs 2013/03/22 19:54:15 Done.
273 278
274 bool ExtensionOmniboxSuggestion::Populate(const base::DictionaryValue& value, 279 if ((*i).get() && (*i)->length)
not at google - send to devlin 2013/03/22 17:33:30 please save a reference to *i (or better: the raw
Aaron Jacobs 2013/03/22 19:54:15 Done.
275 bool require_content) { 280 length = *(*i)->length;
276 if (!value.GetString(kSuggestionContent, &content) && require_content) 281 if (length < 0)
277 return false; 282 length = description.length();
278 283
279 if (!value.GetString(kSuggestionDescription, &description)) 284 offset = (*i)->offset;
280 return false; 285 if (offset < 0)
286 offset = std::max(0, static_cast<int>(description.length()) + offset);
not at google - send to devlin 2013/03/22 17:33:30 if you make this a size_t and ternary-initialize i
Aaron Jacobs 2013/03/22 19:54:15 Done.
281 287
282 description_styles.clear(); 288 switch ((*i)->type) {
283 if (value.HasKey(kSuggestionDescriptionStyles)) { 289 case omnibox::SuggestResult::DescriptionStylesType::TYPE_URL:
284 // This version comes from the extension. 290 type_class = AutocompleteMatch::ACMatchClassification::URL;
285 const ListValue* styles = NULL; 291 break;
286 if (!value.GetList(kSuggestionDescriptionStyles, &styles) || 292 case omnibox::SuggestResult::DescriptionStylesType::TYPE_MATCH:
287 !ReadStylesFromValue(*styles)) { 293 type_class = AutocompleteMatch::ACMatchClassification::MATCH;
288 return false; 294 break;
295 case omnibox::SuggestResult::DescriptionStylesType::TYPE_DIM:
296 type_class = AutocompleteMatch::ACMatchClassification::DIM;
297 break;
298 default:
299 type_class = AutocompleteMatch::ACMatchClassification::NONE;
300 return match_classifications;
301 }
302
303 for (int j = offset;
304 j < offset + length && j < static_cast<int>(styles.size()); ++j)
305 styles[j] |= type_class;
289 } 306 }
290 } else if (value.HasKey(kSuggestionDescriptionStylesRaw)) { 307
291 // This version comes from ToValue(), which we use to persist to disk. 308 for (size_t i = 0; i < styles.size(); ++i) {
292 const ListValue* styles = NULL; 309 if (i ==0 || styles[i] != styles[i-1])
293 if (!value.GetList(kSuggestionDescriptionStylesRaw, &styles) || 310 match_classifications.push_back(
294 styles->empty()) { 311 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 } 312 }
308 } else { 313 } else {
309 description_styles.push_back( 314 match_classifications.push_back(
310 ACMatchClassification(0, ACMatchClassification::NONE)); 315 ACMatchClassification(0, ACMatchClassification::NONE));
311 } 316 }
312 317
313 return true; 318 return match_classifications;
314 } 319 }
315 320
316 bool ExtensionOmniboxSuggestion::ReadStylesFromValue(
317 const ListValue& styles_value) {
318 description_styles.clear();
319
320 // Step 1: Build a vector of styles, 1 per character of description text.
321 std::vector<int> styles;
322 styles.resize(description.length()); // sets all styles to 0
323
324 for (size_t i = 0; i < styles_value.GetSize(); ++i) {
325 const DictionaryValue* style;
326 std::string type;
327 int offset;
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
338 if (offset < 0)
339 offset = std::max(0, static_cast<int>(description.length()) + offset);
340
341 int type_class =
342 (type == "url") ? ACMatchClassification::URL :
343 (type == "match") ? ACMatchClassification::MATCH :
344 (type == "dim") ? ACMatchClassification::DIM : -1;
345 if (type_class == -1)
346 return false;
347
348 for (int j = offset;
349 j < offset + length && j < static_cast<int>(styles.size()); ++j)
350 styles[j] |= type_class;
351 }
352
353 // Step 2: Convert the vector into continuous runs of common styles.
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 }
361
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( 321 void ApplyDefaultSuggestionForExtensionKeyword(
388 Profile* profile, 322 Profile* profile,
389 const TemplateURL* keyword, 323 const TemplateURL* keyword,
390 const string16& remaining_input, 324 const string16& remaining_input,
391 AutocompleteMatch* match) { 325 AutocompleteMatch* match) {
392 DCHECK(keyword->IsExtensionKeyword()); 326 DCHECK(keyword->IsExtensionKeyword());
393 327
394 ExtensionPrefs* prefs = 328 ExtensionPrefs* prefs =
395 ExtensionSystem::Get(profile)->extension_service()->extension_prefs(); 329 ExtensionSystem::Get(profile)->extension_service()->extension_prefs();
396 if (!prefs) 330 if (!prefs)
397 return; 331 return;
398 332
399 ExtensionOmniboxSuggestion suggestion = 333 scoped_ptr<omnibox::SuggestResult> suggestion(
400 prefs->GetOmniboxDefaultSuggestion(keyword->GetExtensionId()); 334 prefs->GetOmniboxDefaultSuggestion(keyword->GetExtensionId()));
401 if (suggestion.description.empty()) 335 if (!suggestion || suggestion->description.empty())
402 return; // fall back to the universal default 336 return; // fall back to the universal default
403 337
404 const string16 kPlaceholderText(ASCIIToUTF16("%s")); 338 const string16 kPlaceholderText(ASCIIToUTF16("%s"));
405 const string16 kReplacementText(ASCIIToUTF16("<input>")); 339 const string16 kReplacementText(ASCIIToUTF16("<input>"));
406 340
407 string16 description = suggestion.description; 341 string16 description = UTF8ToUTF16(suggestion->description);
408 ACMatchClassifications& description_styles = match->contents_class; 342 ACMatchClassifications& description_styles = match->contents_class;
409 description_styles = suggestion.description_styles; 343 description_styles = StyleTypesToACMatchClassifications(*suggestion);
410 344
411 // Replace "%s" with the user's input and adjust the style offsets to the 345 // Replace "%s" with the user's input and adjust the style offsets to the
412 // new length of the description. 346 // new length of the description.
413 size_t placeholder(suggestion.description.find(kPlaceholderText, 0)); 347 size_t placeholder(description.find(kPlaceholderText, 0));
414 if (placeholder != string16::npos) { 348 if (placeholder != string16::npos) {
415 string16 replacement = 349 string16 replacement =
416 remaining_input.empty() ? kReplacementText : remaining_input; 350 remaining_input.empty() ? kReplacementText : remaining_input;
417 description.replace(placeholder, kPlaceholderText.length(), replacement); 351 description.replace(placeholder, kPlaceholderText.length(), replacement);
418 352
419 for (size_t i = 0; i < description_styles.size(); ++i) { 353 for (size_t i = 0; i < description_styles.size(); ++i) {
420 if (description_styles[i].offset > placeholder) 354 if (description_styles[i].offset > placeholder)
421 description_styles[i].offset += replacement.length() - 2; 355 description_styles[i].offset += replacement.length() - 2;
422 } 356 }
423 } 357 }
424 358
425 match->contents.assign(description); 359 match->contents.assign(description);
426 } 360 }
427 361
428 } // namespace extensions 362 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698