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

Side by Side Diff: chrome/browser/extensions/extension_web_ui.cc

Issue 1543923002: [Extensions] Fix chrome url override settings (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years 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/extension_web_ui.h" 5 #include "chrome/browser/extensions/extension_web_ui.h"
6 6
7 #include <set> 7 #include <set>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/command_line.h" 10 #include "base/command_line.h"
(...skipping 29 matching lines...) Expand all
40 #include "ui/gfx/codec/png_codec.h" 40 #include "ui/gfx/codec/png_codec.h"
41 #include "ui/gfx/favicon_size.h" 41 #include "ui/gfx/favicon_size.h"
42 #include "ui/gfx/image/image_skia.h" 42 #include "ui/gfx/image/image_skia.h"
43 43
44 using content::WebContents; 44 using content::WebContents;
45 using extensions::Extension; 45 using extensions::Extension;
46 using extensions::URLOverrides; 46 using extensions::URLOverrides;
47 47
48 namespace { 48 namespace {
49 49
50 // De-dupes the items in |list|. Assumes the values are strings. 50 // The key to the override value for a page.
51 void CleanUpDuplicates(base::ListValue* list) { 51 const char kEntry[] = "entry";
52 std::set<std::string> seen_values; 52 // The key to whether or not the override is active (i.e., can be used).
53 // Overrides may be inactive e.g. when an extension is disabled.
54 const char kActive[] = "active";
53 55
54 // Loop backwards as we may be removing items. 56 // Iterates over |list| and:
55 for (size_t i = list->GetSize() - 1; (i + 1) > 0; --i) { 57 // - Converts any entries of the form <entry> to
56 std::string value; 58 // { 'entry': <entry>, 'active': true }.
57 if (!list->GetString(i, &value)) { 59 // - Removes any duplicate entries.
60 // We do the conversion because we previously stored these values as strings
61 // rather than objects.
62 // TODO(devlin): Remove the conversion once everyone's updated.
63 void InitializeOverridesList(base::ListValue* list) {
Devlin 2015/12/22 23:31:23 Transforming prefs. Yuck. But I can't think of a
64 base::ListValue migrated;
65 std::set<std::string> seen_entries;
66 for (base::Value* val : *list) {
67 scoped_ptr<base::DictionaryValue> new_dict(new base::DictionaryValue());
68 std::string entry_name;
69 base::DictionaryValue* existing_dict = nullptr;
70 if (val->GetAsDictionary(&existing_dict)) {
71 CHECK(existing_dict->GetString(kEntry, &entry_name));
72 new_dict->Swap(existing_dict);
73 } else if (val->GetAsString(&entry_name)) {
74 new_dict->SetString(kEntry, entry_name);
75 new_dict->SetBoolean(kActive, true);
76 } else {
58 NOTREACHED(); 77 NOTREACHED();
59 continue; 78 continue;
60 } 79 }
61 80
62 if (seen_values.find(value) == seen_values.end()) 81 if (seen_entries.count(entry_name) == 0) {
63 seen_values.insert(value); 82 seen_entries.insert(entry_name);
64 else 83 migrated.Append(std::move(new_dict));
65 list->Remove(i, NULL); 84 }
66 } 85 }
86
87 list->Swap(&migrated);
88 }
89
90 // Adds |override| to |list|, or, if there's already an entry for the override,
91 // marks it as active.
92 void AddOverridesToList(base::ListValue* list,
93 const std::string& override) {
94 for (base::Value* val : *list) {
95 base::DictionaryValue* dict = nullptr;
96 std::string entry;
97 if (!val->GetAsDictionary(&dict) || !dict->GetString(kEntry, &entry)) {
98 NOTREACHED();
99 continue;
100 }
101 if (entry == override) {
102 dict->SetBoolean(kActive, true);
103 return; // All done!
104 }
105 }
106
107 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
108 dict->SetString(kEntry, override);
109 dict->SetBoolean(kActive, true);
110 // Add the entry to the front of the list.
111 list->Insert(0, dict.release());
112 }
113
114 // Validates that each entry in |list| contains a valid url and points to an
115 // extension contained in |all_extensions| (and, if not, removes it).
116 void ValidateOverridesList(base::ListValue* list,
117 const extensions::ExtensionSet& all_extensions) {
118 base::ListValue migrated;
119 for (base::Value* val : *list) {
120 base::DictionaryValue* dict = nullptr;
121 std::string entry;
122 if (!val->GetAsDictionary(&dict) || !dict->GetString(kEntry, &entry)) {
123 NOTREACHED();
124 continue;
125 }
126 scoped_ptr<base::DictionaryValue> new_dict(new base::DictionaryValue());
127 new_dict->Swap(dict);
128 GURL override_url(entry);
129 if (!override_url.is_valid())
130 continue;
131
132 if (!all_extensions.GetByID(override_url.host()))
133 continue;
134
135 migrated.Append(std::move(new_dict));
136 }
137
138 list->Swap(&migrated);
67 } 139 }
68 140
69 // Reloads the page in |web_contents| if it uses the same profile as |profile| 141 // Reloads the page in |web_contents| if it uses the same profile as |profile|
70 // and if the current URL is a chrome URL. 142 // and if the current URL is a chrome URL.
71 void UnregisterAndReplaceOverrideForWebContents(const std::string& page, 143 void UnregisterAndReplaceOverrideForWebContents(const std::string& page,
72 Profile* profile, 144 Profile* profile,
73 WebContents* web_contents) { 145 WebContents* web_contents) {
74 if (Profile::FromBrowserContext(web_contents->GetBrowserContext()) != profile) 146 if (Profile::FromBrowserContext(web_contents->GetBrowserContext()) != profile)
75 return; 147 return;
76 148
77 GURL url = web_contents->GetURL(); 149 GURL url = web_contents->GetURL();
78 if (!url.SchemeIs(content::kChromeUIScheme) || url.host_piece() != page) 150 if (!url.SchemeIs(content::kChromeUIScheme) || url.host_piece() != page)
79 return; 151 return;
80 152
81 // Don't use Reload() since |url| isn't the same as the internal URL that 153 // Don't use Reload() since |url| isn't the same as the internal URL that
82 // NavigationController has. 154 // NavigationController has.
83 web_contents->GetController().LoadURL( 155 web_contents->GetController().LoadURL(
84 url, content::Referrer::SanitizeForRequest( 156 url, content::Referrer::SanitizeForRequest(
85 url, content::Referrer(url, blink::WebReferrerPolicyDefault)), 157 url, content::Referrer(url, blink::WebReferrerPolicyDefault)),
86 ui::PAGE_TRANSITION_RELOAD, std::string()); 158 ui::PAGE_TRANSITION_RELOAD, std::string());
87 } 159 }
88 160
161 enum UpdateBehavior {
162 UPDATE_DEACTIVATE, // Mark 'active' as false.
163 UPDATE_REMOVE, // Remove the entry from the list.
164 };
165
166 // Updates the entry (if any) for |override_url| in |overrides_list| according
167 // to |behavior|. Returns true if anything changed.
168 bool UpdateOverridesList(base::ListValue* overrides_list,
169 const std::string& override_url,
170 UpdateBehavior behavior) {
171 base::ListValue::iterator iter =
172 std::find_if(overrides_list->begin(), overrides_list->end(),
173 [&override_url](const base::Value* value) {
174 std::string entry;
175 const base::DictionaryValue* dict = nullptr;
176 return value->GetAsDictionary(&dict) &&
177 dict->GetString(kEntry, &entry) &&
178 entry == override_url;
179 });
180 if (iter != overrides_list->end()) {
181 switch (behavior) {
182 case UPDATE_DEACTIVATE: {
183 base::DictionaryValue* dict = nullptr;
184 CHECK((*iter)->GetAsDictionary(&dict));
Finnur 2015/12/23 13:08:03 I know this isn't a DCHECK, but including producti
Devlin 2015/12/30 03:10:40 Done.
185 dict->SetBoolean(kActive, false);
186 break;
187 }
188 case UPDATE_REMOVE:
189 overrides_list->Erase(iter, nullptr);
190 break;
191 }
192 return true;
193 }
194 return false;
195 }
196
197 // Updates each list referenced in |overrides| according to |behavior|.
198 void UpdateOverridesLists(Profile* profile,
199 const URLOverrides::URLOverrideMap& overrides,
200 UpdateBehavior behavior) {
201 if (overrides.empty())
202 return;
203 PrefService* prefs = profile->GetPrefs();
204 DictionaryPrefUpdate update(prefs, ExtensionWebUI::kExtensionURLOverrides);
205 base::DictionaryValue* all_overrides = update.Get();
206 for (const auto& page_override_pair : overrides) {
207 base::ListValue* page_overrides = nullptr;
208 // If it's being unregistered, it should already be in the list.
209 if (!all_overrides->GetList(page_override_pair.first, &page_overrides)) {
210 NOTREACHED();
211 continue;
212 }
213 if (UpdateOverridesList(page_overrides, page_override_pair.second.spec(),
214 behavior)) {
215 // This is the active override, so we need to find all existing
216 // tabs for this override and get them to reload the original URL.
217 base::Callback<void(WebContents*)> callback =
218 base::Bind(&UnregisterAndReplaceOverrideForWebContents,
219 page_override_pair.first, profile);
220 extensions::ExtensionTabUtil::ForEachTab(callback);
221 }
222 }
223 }
224
89 // Run favicon callbck with image result. If no favicon was available then 225 // Run favicon callbck with image result. If no favicon was available then
90 // |image| will be empty. 226 // |image| will be empty.
91 void RunFaviconCallbackAsync( 227 void RunFaviconCallbackAsync(
92 const favicon_base::FaviconResultsCallback& callback, 228 const favicon_base::FaviconResultsCallback& callback,
93 const gfx::Image& image) { 229 const gfx::Image& image) {
94 std::vector<favicon_base::FaviconRawBitmapResult>* favicon_bitmap_results = 230 std::vector<favicon_base::FaviconRawBitmapResult>* favicon_bitmap_results =
95 new std::vector<favicon_base::FaviconRawBitmapResult>(); 231 new std::vector<favicon_base::FaviconRawBitmapResult>();
96 232
97 const std::vector<gfx::ImageSkiaRep>& image_reps = 233 const std::vector<gfx::ImageSkiaRep>& image_reps =
98 image.AsImageSkia().image_reps(); 234 image.AsImageSkia().image_reps();
(...skipping 21 matching lines...) Expand all
120 FROM_HERE, 256 FROM_HERE,
121 base::Bind(&favicon::FaviconService::FaviconResultsCallbackRunner, 257 base::Bind(&favicon::FaviconService::FaviconResultsCallbackRunner,
122 callback, base::Owned(favicon_bitmap_results))); 258 callback, base::Owned(favicon_bitmap_results)));
123 } 259 }
124 260
125 bool ValidateOverrideURL(const base::Value* override_url_value, 261 bool ValidateOverrideURL(const base::Value* override_url_value,
126 const GURL& source_url, 262 const GURL& source_url,
127 const extensions::ExtensionSet& extensions, 263 const extensions::ExtensionSet& extensions,
128 GURL* override_url, 264 GURL* override_url,
129 const Extension** extension) { 265 const Extension** extension) {
266 const base::DictionaryValue* dict = nullptr;
130 std::string override; 267 std::string override;
131 if (!override_url_value || !override_url_value->GetAsString(&override)) { 268 bool is_active = false;
269 if (!override_url_value || !override_url_value->GetAsDictionary(&dict) ||
270 !dict->GetBoolean(kActive, &is_active) || !is_active ||
271 !dict->GetString(kEntry, &override)) {
132 return false; 272 return false;
133 } 273 }
134 if (!source_url.query().empty()) 274 if (!source_url.query().empty())
135 override += "?" + source_url.query(); 275 override += "?" + source_url.query();
136 if (!source_url.ref().empty()) 276 if (!source_url.ref().empty())
137 override += "#" + source_url.ref(); 277 override += "#" + source_url.ref();
138 *override_url = GURL(override); 278 *override_url = GURL(override);
139 if (!override_url->is_valid()) { 279 if (!override_url->is_valid()) {
140 return false; 280 return false;
141 } 281 }
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
217 // Iterate over the URL list looking for a suitable override. If a 357 // Iterate over the URL list looking for a suitable override. If a
218 // valid non-component override is encountered it is chosen immediately. 358 // valid non-component override is encountered it is chosen immediately.
219 for (size_t i = 0; i < url_list->GetSize(); ++i) { 359 for (size_t i = 0; i < url_list->GetSize(); ++i) {
220 const base::Value* val = NULL; 360 const base::Value* val = NULL;
221 url_list->Get(i, &val); 361 url_list->Get(i, &val);
222 362
223 GURL override_url; 363 GURL override_url;
224 const Extension* extension; 364 const Extension* extension;
225 if (!ValidateOverrideURL( 365 if (!ValidateOverrideURL(
226 val, *url, extensions, &override_url, &extension)) { 366 val, *url, extensions, &override_url, &extension)) {
227 LOG(WARNING) << "Invalid chrome URL override"; 367 // Invalid overrides are cleaned up on startup.
228 UnregisterChromeURLOverride(url_host, profile, val);
229 // The above Unregister call will remove this item from url_list.
230 --i;
231 continue; 368 continue;
232 } 369 }
233 370
234 // We can't handle chrome-extension URLs in incognito mode unless the 371 // We can't handle chrome-extension URLs in incognito mode unless the
235 // extension uses split mode. 372 // extension uses split mode.
236 bool incognito_override_allowed = 373 bool incognito_override_allowed =
237 extensions::IncognitoInfo::IsSplitMode(extension) && 374 extensions::IncognitoInfo::IsSplitMode(extension) &&
238 extensions::util::IsIncognitoEnabled(extension->id(), profile); 375 extensions::util::IsIncognitoEnabled(extension->id(), profile);
239 if (profile->IsOffTheRecord() && !incognito_override_allowed) { 376 if (profile->IsOffTheRecord() && !incognito_override_allowed) {
240 continue; 377 continue;
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
292 *url = original_url; 429 *url = original_url;
293 return true; 430 return true;
294 } 431 }
295 } 432 }
296 } 433 }
297 434
298 return false; 435 return false;
299 } 436 }
300 437
301 // static 438 // static
302 void ExtensionWebUI::RegisterChromeURLOverrides( 439 void ExtensionWebUI::InitializeChromeURLOverrides(Profile* profile) {
303 Profile* profile, const URLOverrides::URLOverrideMap& overrides) { 440 PrefService* prefs = profile->GetPrefs();
304 if (overrides.empty()) 441 DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
305 return; 442 base::DictionaryValue* all_overrides = update.Get();
443
444 // DictionaryValue::Iterator cannot be used to modify the list. Generate the
445 // set of keys instead.
446 std::vector<std::string> keys;
447 for (base::DictionaryValue::Iterator iter(*all_overrides);
448 !iter.IsAtEnd(); iter.Advance()) {
449 keys.push_back(iter.key());
450 }
451 for (const std::string& key : keys) {
452 base::ListValue* list = nullptr;
453 CHECK(all_overrides->GetList(key, &list));
454 InitializeOverridesList(list);
455 }
456 }
457
458 // static
459 void ExtensionWebUI::ValidateChromeURLOverrides(Profile* profile) {
460 scoped_ptr<extensions::ExtensionSet> all_extensions =
461 extensions::ExtensionRegistry::Get(profile)->
462 GenerateInstalledExtensionsSet();
306 463
307 PrefService* prefs = profile->GetPrefs(); 464 PrefService* prefs = profile->GetPrefs();
308 DictionaryPrefUpdate update(prefs, kExtensionURLOverrides); 465 DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
309 base::DictionaryValue* all_overrides = update.Get(); 466 base::DictionaryValue* all_overrides = update.Get();
310 467
311 // For each override provided by the extension, add it to the front of 468 // DictionaryValue::Iterator cannot be used to modify the list. Generate the
312 // the override list if it's not already in the list. 469 // set of keys instead.
313 URLOverrides::URLOverrideMap::const_iterator iter = overrides.begin(); 470 std::vector<std::string> keys;
314 for (; iter != overrides.end(); ++iter) { 471 for (base::DictionaryValue::Iterator iter(*all_overrides);
315 const std::string& key = iter->first; 472 !iter.IsAtEnd(); iter.Advance()) {
316 base::ListValue* page_overrides = NULL; 473 keys.push_back(iter.key());
317 if (!all_overrides->GetList(key, &page_overrides)) { 474 }
318 page_overrides = new base::ListValue(); 475 for (const std::string& key : keys) {
319 all_overrides->Set(key, page_overrides); 476 base::ListValue* list = nullptr;
320 } else { 477 CHECK(all_overrides->GetList(key, &list));
321 CleanUpDuplicates(page_overrides); 478 ValidateOverridesList(list, *all_extensions);
322
323 // Verify that the override isn't already in the list.
324 base::ListValue::iterator i = page_overrides->begin();
325 for (; i != page_overrides->end(); ++i) {
326 std::string override_val;
327 if (!(*i)->GetAsString(&override_val)) {
328 NOTREACHED();
329 continue;
330 }
331 if (override_val == iter->second.spec())
332 break;
333 }
334 // This value is already in the list, leave it alone.
335 if (i != page_overrides->end())
336 continue;
337 }
338 // Insert the override at the front of the list. Last registered override
339 // wins.
340 page_overrides->Insert(0, new base::StringValue(iter->second.spec()));
341 } 479 }
342 } 480 }
343 481
344 // static 482 // static
345 void ExtensionWebUI::UnregisterAndReplaceOverride(const std::string& page, 483 void ExtensionWebUI::RegisterOrActivateChromeURLOverrides(
346 Profile* profile, 484 Profile* profile,
347 base::ListValue* list, 485 const URLOverrides::URLOverrideMap& overrides) {
348 const base::Value* override) {
349 size_t index = 0;
350 bool found = list->Remove(*override, &index);
351 if (found && index == 0) {
352 // This is the active override, so we need to find all existing
353 // tabs for this override and get them to reload the original URL.
354 base::Callback<void(WebContents*)> callback =
355 base::Bind(&UnregisterAndReplaceOverrideForWebContents, page, profile);
356 extensions::ExtensionTabUtil::ForEachTab(callback);
357 }
358 }
359
360 // static
361 void ExtensionWebUI::UnregisterChromeURLOverride(const std::string& page,
362 Profile* profile,
363 const base::Value* override) {
364 if (!override)
365 return;
366 PrefService* prefs = profile->GetPrefs();
367 DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
368 base::DictionaryValue* all_overrides = update.Get();
369 base::ListValue* page_overrides = NULL;
370 if (!all_overrides->GetList(page, &page_overrides)) {
371 // If it's being unregistered, it should already be in the list.
372 NOTREACHED();
373 return;
374 } else {
375 UnregisterAndReplaceOverride(page, profile, page_overrides, override);
376 }
377 }
378
379 // static
380 void ExtensionWebUI::UnregisterChromeURLOverrides(
381 Profile* profile, const URLOverrides::URLOverrideMap& overrides) {
382 if (overrides.empty()) 486 if (overrides.empty())
383 return; 487 return;
384 PrefService* prefs = profile->GetPrefs(); 488 PrefService* prefs = profile->GetPrefs();
385 DictionaryPrefUpdate update(prefs, kExtensionURLOverrides); 489 DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
386 base::DictionaryValue* all_overrides = update.Get(); 490 base::DictionaryValue* all_overrides = update.Get();
387 URLOverrides::URLOverrideMap::const_iterator iter = overrides.begin(); 491 for (const auto& page_override_pair : overrides) {
388 for (; iter != overrides.end(); ++iter) { 492 base::ListValue* page_overrides = nullptr;
389 const std::string& page = iter->first; 493 if (!all_overrides->GetList(page_override_pair.first, &page_overrides)) {
390 base::ListValue* page_overrides = NULL; 494 page_overrides = new base::ListValue();
391 if (!all_overrides->GetList(page, &page_overrides)) { 495 all_overrides->Set(page_override_pair.first, page_overrides);
392 // If it's being unregistered, it should already be in the list.
393 NOTREACHED();
394 continue;
395 } else {
396 base::StringValue override(iter->second.spec());
397 UnregisterAndReplaceOverride(iter->first, profile,
398 page_overrides, &override);
399 } 496 }
497 AddOverridesToList(page_overrides, page_override_pair.second.spec());
400 } 498 }
401 } 499 }
402 500
403 // static 501 // static
502 void ExtensionWebUI::DeactivateChromeURLOverrides(
503 Profile* profile,
504 const URLOverrides::URLOverrideMap& overrides) {
505 UpdateOverridesLists(profile, overrides, UPDATE_DEACTIVATE);
506 }
507
508 // static
509 void ExtensionWebUI::UnregisterChromeURLOverrides(
510 Profile* profile,
511 const URLOverrides::URLOverrideMap& overrides) {
512 UpdateOverridesLists(profile, overrides, UPDATE_REMOVE);
513 }
514
515 // static
404 void ExtensionWebUI::GetFaviconForURL( 516 void ExtensionWebUI::GetFaviconForURL(
405 Profile* profile, 517 Profile* profile,
406 const GURL& page_url, 518 const GURL& page_url,
407 const favicon_base::FaviconResultsCallback& callback) { 519 const favicon_base::FaviconResultsCallback& callback) {
408 const Extension* extension = extensions::ExtensionRegistry::Get( 520 const Extension* extension = extensions::ExtensionRegistry::Get(
409 profile)->enabled_extensions().GetByID(page_url.host()); 521 profile)->enabled_extensions().GetByID(page_url.host());
410 if (!extension) { 522 if (!extension) {
411 RunFaviconCallbackAsync(callback, gfx::Image()); 523 RunFaviconCallbackAsync(callback, gfx::Image());
412 return; 524 return;
413 } 525 }
(...skipping 18 matching lines...) Expand all
432 extensions::ImageLoader::ImageRepresentation::ALWAYS_RESIZE, 544 extensions::ImageLoader::ImageRepresentation::ALWAYS_RESIZE,
433 gfx::Size(pixel_size, pixel_size), 545 gfx::Size(pixel_size, pixel_size),
434 resource_scale_factor)); 546 resource_scale_factor));
435 } 547 }
436 548
437 // LoadImagesAsync actually can run callback synchronously. We want to force 549 // LoadImagesAsync actually can run callback synchronously. We want to force
438 // async. 550 // async.
439 extensions::ImageLoader::Get(profile)->LoadImagesAsync( 551 extensions::ImageLoader::Get(profile)->LoadImagesAsync(
440 extension, info_list, base::Bind(&RunFaviconCallbackAsync, callback)); 552 extension, info_list, base::Bind(&RunFaviconCallbackAsync, callback));
441 } 553 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698