| OLD | NEW |
| 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/downloads/downloads_api.h" | 5 #include "chrome/browser/extensions/api/downloads/downloads_api.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <cctype> | 8 #include <cctype> |
| 9 #include <iterator> | 9 #include <iterator> |
| 10 #include <set> | 10 #include <set> |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 #include "chrome/browser/download/download_service_factory.h" | 37 #include "chrome/browser/download/download_service_factory.h" |
| 38 #include "chrome/browser/download/download_shelf.h" | 38 #include "chrome/browser/download/download_shelf.h" |
| 39 #include "chrome/browser/download/download_util.h" | 39 #include "chrome/browser/download/download_util.h" |
| 40 #include "chrome/browser/extensions/event_names.h" | 40 #include "chrome/browser/extensions/event_names.h" |
| 41 #include "chrome/browser/extensions/event_router.h" | 41 #include "chrome/browser/extensions/event_router.h" |
| 42 #include "chrome/browser/extensions/extension_function_dispatcher.h" | 42 #include "chrome/browser/extensions/extension_function_dispatcher.h" |
| 43 #include "chrome/browser/extensions/extension_info_map.h" | 43 #include "chrome/browser/extensions/extension_info_map.h" |
| 44 #include "chrome/browser/extensions/extension_prefs.h" | 44 #include "chrome/browser/extensions/extension_prefs.h" |
| 45 #include "chrome/browser/extensions/extension_service.h" | 45 #include "chrome/browser/extensions/extension_service.h" |
| 46 #include "chrome/browser/extensions/extension_system.h" | 46 #include "chrome/browser/extensions/extension_system.h" |
| 47 #include "chrome/browser/extensions/extension_warning_service.h" |
| 48 #include "chrome/browser/extensions/extension_warning_set.h" |
| 47 #include "chrome/browser/icon_loader.h" | 49 #include "chrome/browser/icon_loader.h" |
| 48 #include "chrome/browser/icon_manager.h" | 50 #include "chrome/browser/icon_manager.h" |
| 49 #include "chrome/browser/platform_util.h" | 51 #include "chrome/browser/platform_util.h" |
| 50 #include "chrome/browser/profiles/profile.h" | 52 #include "chrome/browser/profiles/profile.h" |
| 51 #include "chrome/browser/renderer_host/chrome_render_message_filter.h" | 53 #include "chrome/browser/renderer_host/chrome_render_message_filter.h" |
| 52 #include "chrome/browser/ui/browser.h" | 54 #include "chrome/browser/ui/browser.h" |
| 53 #include "chrome/browser/ui/browser_list.h" | 55 #include "chrome/browser/ui/browser_list.h" |
| 54 #include "chrome/browser/ui/browser_window.h" | 56 #include "chrome/browser/ui/browser_window.h" |
| 55 #include "chrome/common/cancelable_task_tracker.h" | 57 #include "chrome/common/cancelable_task_tracker.h" |
| 56 #include "chrome/common/extensions/api/downloads.h" | 58 #include "chrome/common/extensions/api/downloads.h" |
| (...skipping 528 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 585 explicit ExtensionDownloadsEventRouterData( | 587 explicit ExtensionDownloadsEventRouterData( |
| 586 DownloadItem* download_item, | 588 DownloadItem* download_item, |
| 587 scoped_ptr<base::DictionaryValue> json_item) | 589 scoped_ptr<base::DictionaryValue> json_item) |
| 588 : updated_(0), | 590 : updated_(0), |
| 589 changed_fired_(0), | 591 changed_fired_(0), |
| 590 json_(json_item.Pass()), | 592 json_(json_item.Pass()), |
| 591 creator_conflict_action_( | 593 creator_conflict_action_( |
| 592 extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY), | 594 extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY), |
| 593 determined_conflict_action_( | 595 determined_conflict_action_( |
| 594 extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY) { | 596 extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY) { |
| 597 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 595 download_item->SetUserData(kKey, this); | 598 download_item->SetUserData(kKey, this); |
| 596 } | 599 } |
| 597 | 600 |
| 598 virtual ~ExtensionDownloadsEventRouterData() { | 601 virtual ~ExtensionDownloadsEventRouterData() { |
| 599 if (updated_ > 0) { | 602 if (updated_ > 0) { |
| 600 UMA_HISTOGRAM_PERCENTAGE("Download.OnChanged", | 603 UMA_HISTOGRAM_PERCENTAGE("Download.OnChanged", |
| 601 (changed_fired_ * 100 / updated_)); | 604 (changed_fired_ * 100 / updated_)); |
| 602 } | 605 } |
| 603 } | 606 } |
| 604 | 607 |
| 605 const base::DictionaryValue& json() const { return *json_.get(); } | 608 const base::DictionaryValue& json() const { return *json_.get(); } |
| 606 void set_json(scoped_ptr<base::DictionaryValue> json_item) { | 609 void set_json(scoped_ptr<base::DictionaryValue> json_item) { |
| 607 json_ = json_item.Pass(); | 610 json_ = json_item.Pass(); |
| 608 } | 611 } |
| 609 | 612 |
| 610 void OnItemUpdated() { ++updated_; } | 613 void OnItemUpdated() { ++updated_; } |
| 611 void OnChangedFired() { ++changed_fired_; } | 614 void OnChangedFired() { ++changed_fired_; } |
| 612 | 615 |
| 613 void set_filename_change_callbacks( | 616 void set_filename_change_callbacks( |
| 614 const base::Closure& no_change, | 617 const base::Closure& no_change, |
| 615 const ExtensionDownloadsEventRouter::FilenameChangedCallback& change) { | 618 const ExtensionDownloadsEventRouter::FilenameChangedCallback& change) { |
| 619 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 616 filename_no_change_ = no_change; | 620 filename_no_change_ = no_change; |
| 617 filename_change_ = change; | 621 filename_change_ = change; |
| 618 determined_filename_ = creator_suggested_filename_; | 622 determined_filename_ = creator_suggested_filename_; |
| 619 determined_conflict_action_ = creator_conflict_action_; | 623 determined_conflict_action_ = creator_conflict_action_; |
| 620 // determiner_.install_time should default to 0 so that creator suggestions | 624 // determiner_.install_time should default to 0 so that creator suggestions |
| 621 // should be lower priority than any actual onDeterminingFilename listeners. | 625 // should be lower priority than any actual onDeterminingFilename listeners. |
| 622 } | 626 } |
| 623 | 627 |
| 624 void ClearPendingDeterminers() { | 628 void ClearPendingDeterminers() { |
| 629 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 625 determined_filename_.clear(); | 630 determined_filename_.clear(); |
| 626 determined_conflict_action_ = | 631 determined_conflict_action_ = |
| 627 extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY; | 632 extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY; |
| 628 determiner_ = DeterminerInfo(); | 633 determiner_ = DeterminerInfo(); |
| 629 filename_no_change_ = base::Closure(); | 634 filename_no_change_ = base::Closure(); |
| 630 filename_change_ = ExtensionDownloadsEventRouter::FilenameChangedCallback(); | 635 filename_change_ = ExtensionDownloadsEventRouter::FilenameChangedCallback(); |
| 631 weak_ptr_factory_.reset(); | 636 weak_ptr_factory_.reset(); |
| 632 determiners_.clear(); | 637 determiners_.clear(); |
| 633 } | 638 } |
| 634 | 639 |
| 635 void DeterminerRemoved(const std::string& extension_id) { | 640 void DeterminerRemoved(const std::string& extension_id) { |
| 641 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 636 for (DeterminerInfoVector::iterator iter = determiners_.begin(); | 642 for (DeterminerInfoVector::iterator iter = determiners_.begin(); |
| 637 iter != determiners_.end();) { | 643 iter != determiners_.end();) { |
| 638 if (iter->extension_id == extension_id) { | 644 if (iter->extension_id == extension_id) { |
| 639 iter = determiners_.erase(iter); | 645 iter = determiners_.erase(iter); |
| 640 } else { | 646 } else { |
| 641 ++iter; | 647 ++iter; |
| 642 } | 648 } |
| 643 } | 649 } |
| 644 // If we just removed the last unreported determiner, then we need to call a | 650 // If we just removed the last unreported determiner, then we need to call a |
| 645 // callback. | 651 // callback. |
| 646 CheckAllDeterminersCalled(); | 652 CheckAllDeterminersCalled(); |
| 647 } | 653 } |
| 648 | 654 |
| 649 void AddPendingDeterminer(const std::string& extension_id, | 655 void AddPendingDeterminer(const std::string& extension_id, |
| 650 const base::Time& installed) { | 656 const base::Time& installed) { |
| 657 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 651 for (size_t index = 0; index < determiners_.size(); ++index) { | 658 for (size_t index = 0; index < determiners_.size(); ++index) { |
| 652 if (determiners_[index].extension_id == extension_id) { | 659 if (determiners_[index].extension_id == extension_id) { |
| 653 DCHECK(false) << extension_id; | 660 DCHECK(false) << extension_id; |
| 654 return; | 661 return; |
| 655 } | 662 } |
| 656 } | 663 } |
| 657 determiners_.push_back(DeterminerInfo(extension_id, installed)); | 664 determiners_.push_back(DeterminerInfo(extension_id, installed)); |
| 658 } | 665 } |
| 659 | 666 |
| 660 bool DeterminerAlreadyReported(const std::string& extension_id) { | 667 bool DeterminerAlreadyReported(const std::string& extension_id) { |
| 668 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 661 for (size_t index = 0; index < determiners_.size(); ++index) { | 669 for (size_t index = 0; index < determiners_.size(); ++index) { |
| 662 if (determiners_[index].extension_id == extension_id) { | 670 if (determiners_[index].extension_id == extension_id) { |
| 663 return determiners_[index].reported; | 671 return determiners_[index].reported; |
| 664 } | 672 } |
| 665 } | 673 } |
| 666 return false; | 674 return false; |
| 667 } | 675 } |
| 668 | 676 |
| 669 void CreatorSuggestedFilename( | 677 void CreatorSuggestedFilename( |
| 670 const base::FilePath& filename, | 678 const base::FilePath& filename, |
| 671 extensions::api::downloads::FilenameConflictAction conflict_action) { | 679 extensions::api::downloads::FilenameConflictAction conflict_action) { |
| 680 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 672 creator_suggested_filename_ = filename; | 681 creator_suggested_filename_ = filename; |
| 673 creator_conflict_action_ = conflict_action; | 682 creator_conflict_action_ = conflict_action; |
| 674 } | 683 } |
| 675 | 684 |
| 676 base::FilePath creator_suggested_filename() const { | 685 base::FilePath creator_suggested_filename() const { |
| 677 return creator_suggested_filename_; | 686 return creator_suggested_filename_; |
| 678 } | 687 } |
| 679 | 688 |
| 680 extensions::api::downloads::FilenameConflictAction | 689 extensions::api::downloads::FilenameConflictAction |
| 681 creator_conflict_action() const { | 690 creator_conflict_action() const { |
| 682 return creator_conflict_action_; | 691 return creator_conflict_action_; |
| 683 } | 692 } |
| 684 | 693 |
| 685 void ResetCreatorSuggestion() { | 694 void ResetCreatorSuggestion() { |
| 695 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 686 creator_suggested_filename_.clear(); | 696 creator_suggested_filename_.clear(); |
| 687 creator_conflict_action_ = | 697 creator_conflict_action_ = |
| 688 extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY; | 698 extensions::api::downloads::FILENAME_CONFLICT_ACTION_UNIQUIFY; |
| 689 } | 699 } |
| 690 | 700 |
| 691 // Returns false if this |extension_id| was not expected or if this | 701 // Returns false if this |extension_id| was not expected or if this |
| 692 // |extension_id| has already reported. The caller is responsible for | 702 // |extension_id| has already reported. The caller is responsible for |
| 693 // validating |filename|. | 703 // validating |filename|. |
| 694 bool DeterminerCallback( | 704 bool DeterminerCallback( |
| 705 Profile* profile, |
| 695 const std::string& extension_id, | 706 const std::string& extension_id, |
| 696 const base::FilePath& filename, | 707 const base::FilePath& filename, |
| 697 extensions::api::downloads::FilenameConflictAction conflict_action) { | 708 extensions::api::downloads::FilenameConflictAction conflict_action) { |
| 709 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 698 bool found_info = false; | 710 bool found_info = false; |
| 699 for (size_t index = 0; index < determiners_.size(); ++index) { | 711 for (size_t index = 0; index < determiners_.size(); ++index) { |
| 700 if (determiners_[index].extension_id == extension_id) { | 712 if (determiners_[index].extension_id == extension_id) { |
| 701 found_info = true; | 713 found_info = true; |
| 702 if (determiners_[index].reported) | 714 if (determiners_[index].reported) |
| 703 return false; | 715 return false; |
| 704 determiners_[index].reported = true; | 716 determiners_[index].reported = true; |
| 705 // Do not use filename if another determiner has already overridden the | 717 // Do not use filename if another determiner has already overridden the |
| 706 // filename and they take precedence. Extensions that were installed | 718 // filename and they take precedence. Extensions that were installed |
| 707 // later take precedence over previous extensions. | 719 // later take precedence over previous extensions. |
| 708 if (!filename.empty() && | 720 if (!filename.empty()) { |
| 709 (determiner_.extension_id.empty() || | 721 extensions::ExtensionWarningSet warnings; |
| 710 (determiners_[index].install_time > determiner_.install_time))) { | 722 std::string winner_extension_id; |
| 711 determined_filename_ = filename; | 723 ExtensionDownloadsEventRouter::DetermineFilenameInternal( |
| 712 determined_conflict_action_ = conflict_action; | 724 filename, |
| 713 determiner_ = determiners_[index]; | 725 conflict_action, |
| 726 determiners_[index].extension_id, |
| 727 determiners_[index].install_time, |
| 728 determiner_.extension_id, |
| 729 determiner_.install_time, |
| 730 &winner_extension_id, |
| 731 &determined_filename_, |
| 732 &determined_conflict_action_, |
| 733 &warnings); |
| 734 if (!warnings.empty()) |
| 735 extensions::ExtensionWarningService::NotifyWarningsOnUI( |
| 736 profile, warnings); |
| 737 if (winner_extension_id == determiners_[index].extension_id) |
| 738 determiner_ = determiners_[index]; |
| 714 } | 739 } |
| 715 break; | 740 break; |
| 716 } | 741 } |
| 717 } | 742 } |
| 718 if (!found_info) | 743 if (!found_info) |
| 719 return false; | 744 return false; |
| 720 CheckAllDeterminersCalled(); | 745 CheckAllDeterminersCalled(); |
| 721 return true; | 746 return true; |
| 722 } | 747 } |
| 723 | 748 |
| (...skipping 827 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1551 change.Run(data->creator_suggested_filename(), | 1576 change.Run(data->creator_suggested_filename(), |
| 1552 ConvertConflictAction(data->creator_conflict_action())); | 1577 ConvertConflictAction(data->creator_conflict_action())); |
| 1553 // If all listeners are removed, don't keep |data| around. | 1578 // If all listeners are removed, don't keep |data| around. |
| 1554 data->ResetCreatorSuggestion(); | 1579 data->ResetCreatorSuggestion(); |
| 1555 } else { | 1580 } else { |
| 1556 no_change.Run(); | 1581 no_change.Run(); |
| 1557 } | 1582 } |
| 1558 } | 1583 } |
| 1559 } | 1584 } |
| 1560 | 1585 |
| 1586 void ExtensionDownloadsEventRouter::DetermineFilenameInternal( |
| 1587 const base::FilePath& filename, |
| 1588 extensions::api::downloads::FilenameConflictAction conflict_action, |
| 1589 const std::string& suggesting_extension_id, |
| 1590 const base::Time& suggesting_install_time, |
| 1591 const std::string& incumbent_extension_id, |
| 1592 const base::Time& incumbent_install_time, |
| 1593 std::string* winner_extension_id, |
| 1594 base::FilePath* determined_filename, |
| 1595 extensions::api::downloads::FilenameConflictAction* |
| 1596 determined_conflict_action, |
| 1597 extensions::ExtensionWarningSet* warnings) { |
| 1598 DCHECK(!filename.empty()); |
| 1599 DCHECK(!suggesting_extension_id.empty()); |
| 1600 |
| 1601 if (incumbent_extension_id.empty()) { |
| 1602 *winner_extension_id = suggesting_extension_id; |
| 1603 *determined_filename = filename; |
| 1604 *determined_conflict_action = conflict_action; |
| 1605 return; |
| 1606 } |
| 1607 |
| 1608 if (suggesting_install_time < incumbent_install_time) { |
| 1609 *winner_extension_id = incumbent_extension_id; |
| 1610 warnings->insert( |
| 1611 extensions::ExtensionWarning::CreateDownloadFilenameConflictWarning( |
| 1612 suggesting_extension_id, |
| 1613 incumbent_extension_id, |
| 1614 filename, |
| 1615 *determined_filename)); |
| 1616 return; |
| 1617 } |
| 1618 |
| 1619 *winner_extension_id = suggesting_extension_id; |
| 1620 warnings->insert( |
| 1621 extensions::ExtensionWarning::CreateDownloadFilenameConflictWarning( |
| 1622 incumbent_extension_id, |
| 1623 suggesting_extension_id, |
| 1624 *determined_filename, |
| 1625 filename)); |
| 1626 *determined_filename = filename; |
| 1627 *determined_conflict_action = conflict_action; |
| 1628 } |
| 1629 |
| 1561 bool ExtensionDownloadsEventRouter::DetermineFilename( | 1630 bool ExtensionDownloadsEventRouter::DetermineFilename( |
| 1562 Profile* profile, | 1631 Profile* profile, |
| 1563 bool include_incognito, | 1632 bool include_incognito, |
| 1564 const std::string& ext_id, | 1633 const std::string& ext_id, |
| 1565 int download_id, | 1634 int download_id, |
| 1566 const base::FilePath& const_filename, | 1635 const base::FilePath& const_filename, |
| 1567 extensions::api::downloads::FilenameConflictAction conflict_action, | 1636 extensions::api::downloads::FilenameConflictAction conflict_action, |
| 1568 std::string* error) { | 1637 std::string* error) { |
| 1569 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 1638 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 1570 DownloadItem* item = GetDownload(profile, include_incognito, download_id); | 1639 DownloadItem* item = GetDownload(profile, include_incognito, download_id); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1584 base::FilePath::StringType filename_str(const_filename.value()); | 1653 base::FilePath::StringType filename_str(const_filename.value()); |
| 1585 // Allow windows-style directory separators on all platforms. | 1654 // Allow windows-style directory separators on all platforms. |
| 1586 std::replace(filename_str.begin(), filename_str.end(), | 1655 std::replace(filename_str.begin(), filename_str.end(), |
| 1587 FILE_PATH_LITERAL('\\'), FILE_PATH_LITERAL('/')); | 1656 FILE_PATH_LITERAL('\\'), FILE_PATH_LITERAL('/')); |
| 1588 base::FilePath filename(filename_str); | 1657 base::FilePath filename(filename_str); |
| 1589 bool valid_filename = net::IsSafePortableRelativePath(filename); | 1658 bool valid_filename = net::IsSafePortableRelativePath(filename); |
| 1590 filename = (valid_filename ? filename.NormalizePathSeparators() : | 1659 filename = (valid_filename ? filename.NormalizePathSeparators() : |
| 1591 base::FilePath()); | 1660 base::FilePath()); |
| 1592 // If the invalid filename check is moved to before DeterminerCallback(), then | 1661 // If the invalid filename check is moved to before DeterminerCallback(), then |
| 1593 // it will block forever waiting for this ext_id to report. | 1662 // it will block forever waiting for this ext_id to report. |
| 1594 if (Fault(!data->DeterminerCallback(ext_id, filename, conflict_action), | 1663 if (Fault(!data->DeterminerCallback( |
| 1664 profile, ext_id, filename, conflict_action), |
| 1595 errors::kUnexpectedDeterminer, error) || | 1665 errors::kUnexpectedDeterminer, error) || |
| 1596 Fault((!const_filename.empty() && !valid_filename), | 1666 Fault((!const_filename.empty() && !valid_filename), |
| 1597 errors::kInvalidFilename, error)) | 1667 errors::kInvalidFilename, error)) |
| 1598 return false; | 1668 return false; |
| 1599 return true; | 1669 return true; |
| 1600 } | 1670 } |
| 1601 | 1671 |
| 1602 void ExtensionDownloadsEventRouter::OnListenerRemoved( | 1672 void ExtensionDownloadsEventRouter::OnListenerRemoved( |
| 1603 const extensions::EventListenerInfo& details) { | 1673 const extensions::EventListenerInfo& details) { |
| 1604 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 1674 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1783 content::NotificationService::current()->Notify( | 1853 content::NotificationService::current()->Notify( |
| 1784 chrome::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT, | 1854 chrome::NOTIFICATION_EXTENSION_DOWNLOADS_EVENT, |
| 1785 content_source, | 1855 content_source, |
| 1786 content::Details<std::string>(&json_args)); | 1856 content::Details<std::string>(&json_args)); |
| 1787 } | 1857 } |
| 1788 | 1858 |
| 1789 void ExtensionDownloadsEventRouter::Observe( | 1859 void ExtensionDownloadsEventRouter::Observe( |
| 1790 int type, | 1860 int type, |
| 1791 const content::NotificationSource& source, | 1861 const content::NotificationSource& source, |
| 1792 const content::NotificationDetails& details) { | 1862 const content::NotificationDetails& details) { |
| 1863 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 1793 switch (type) { | 1864 switch (type) { |
| 1794 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { | 1865 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { |
| 1795 extensions::UnloadedExtensionInfo* unloaded = | 1866 extensions::UnloadedExtensionInfo* unloaded = |
| 1796 content::Details<extensions::UnloadedExtensionInfo>(details).ptr(); | 1867 content::Details<extensions::UnloadedExtensionInfo>(details).ptr(); |
| 1797 std::set<const extensions::Extension*>::iterator iter = | 1868 std::set<const extensions::Extension*>::iterator iter = |
| 1798 shelf_disabling_extensions_.find(unloaded->extension); | 1869 shelf_disabling_extensions_.find(unloaded->extension); |
| 1799 if (iter != shelf_disabling_extensions_.end()) | 1870 if (iter != shelf_disabling_extensions_.end()) |
| 1800 shelf_disabling_extensions_.erase(iter); | 1871 shelf_disabling_extensions_.erase(iter); |
| 1801 break; | 1872 break; |
| 1802 } | 1873 } |
| 1803 } | 1874 } |
| 1804 } | 1875 } |
| OLD | NEW |