| 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/chrome_content_browser_client.h" | 5 #include "chrome/browser/chrome_content_browser_client.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 #include <utility> | 8 #include <utility> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| (...skipping 617 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 628 return ChromeWebUIControllerFactory::GetInstance(); | 628 return ChromeWebUIControllerFactory::GetInstance(); |
| 629 } | 629 } |
| 630 | 630 |
| 631 GURL ChromeContentBrowserClient::GetEffectiveURL( | 631 GURL ChromeContentBrowserClient::GetEffectiveURL( |
| 632 content::BrowserContext* browser_context, const GURL& url) { | 632 content::BrowserContext* browser_context, const GURL& url) { |
| 633 Profile* profile = Profile::FromBrowserContext(browser_context); | 633 Profile* profile = Profile::FromBrowserContext(browser_context); |
| 634 // Get the effective URL for the given actual URL. If the URL is part of an | 634 // Get the effective URL for the given actual URL. If the URL is part of an |
| 635 // installed app, the effective URL is an extension URL with the ID of that | 635 // installed app, the effective URL is an extension URL with the ID of that |
| 636 // extension as the host. This has the effect of grouping apps together in | 636 // extension as the host. This has the effect of grouping apps together in |
| 637 // a common SiteInstance. | 637 // a common SiteInstance. |
| 638 if (!profile || !profile->GetExtensionService()) | 638 ExtensionService* extension_service = |
| 639 extensions::ExtensionSystem::Get(profile)->extension_service(); |
| 640 if (!extension_service) |
| 639 return url; | 641 return url; |
| 640 | 642 |
| 641 const Extension* extension = profile->GetExtensionService()->extensions()-> | 643 const Extension* extension = extension_service->extensions()-> |
| 642 GetHostedAppByURL(ExtensionURLInfo(url)); | 644 GetHostedAppByURL(ExtensionURLInfo(url)); |
| 643 if (!extension) | 645 if (!extension) |
| 644 return url; | 646 return url; |
| 645 | 647 |
| 646 // Bookmark apps do not use the hosted app process model, and should be | 648 // Bookmark apps do not use the hosted app process model, and should be |
| 647 // treated as normal URLs. | 649 // treated as normal URLs. |
| 648 if (extension->from_bookmark()) | 650 if (extension->from_bookmark()) |
| 649 return url; | 651 return url; |
| 650 | 652 |
| 651 // If the URL is part of an extension's web extent, convert it to an | 653 // If the URL is part of an extension's web extent, convert it to an |
| 652 // extension URL. | 654 // extension URL. |
| 653 return extension->GetResourceURL(url.path()); | 655 return extension->GetResourceURL(url.path()); |
| 654 } | 656 } |
| 655 | 657 |
| 656 bool ChromeContentBrowserClient::ShouldUseProcessPerSite( | 658 bool ChromeContentBrowserClient::ShouldUseProcessPerSite( |
| 657 content::BrowserContext* browser_context, const GURL& effective_url) { | 659 content::BrowserContext* browser_context, const GURL& effective_url) { |
| 658 // Non-extension URLs should generally use process-per-site-instance. | 660 // Non-extension URLs should generally use process-per-site-instance. |
| 659 // Because we expect to use the effective URL, URLs for hosted apps (apart | 661 // Because we expect to use the effective URL, URLs for hosted apps (apart |
| 660 // from bookmark apps) should have an extension scheme by now. | 662 // from bookmark apps) should have an extension scheme by now. |
| 661 if (!effective_url.SchemeIs(extensions::kExtensionScheme)) | 663 if (!effective_url.SchemeIs(extensions::kExtensionScheme)) |
| 662 return false; | 664 return false; |
| 663 | 665 |
| 664 Profile* profile = Profile::FromBrowserContext(browser_context); | 666 Profile* profile = Profile::FromBrowserContext(browser_context); |
| 665 if (!profile || !profile->GetExtensionService()) | 667 ExtensionService* extension_service = |
| 668 extensions::ExtensionSystem::Get(profile)->extension_service(); |
| 669 if (!extension_service) |
| 666 return false; | 670 return false; |
| 667 | 671 |
| 668 const Extension* extension = profile->GetExtensionService()->extensions()-> | 672 const Extension* extension = extension_service->extensions()-> |
| 669 GetExtensionOrAppByURL(ExtensionURLInfo(effective_url)); | 673 GetExtensionOrAppByURL(ExtensionURLInfo(effective_url)); |
| 670 if (!extension) | 674 if (!extension) |
| 671 return false; | 675 return false; |
| 672 | 676 |
| 673 // If the URL is part of a hosted app that does not have the background | 677 // If the URL is part of a hosted app that does not have the background |
| 674 // permission, or that does not allow JavaScript access to the background | 678 // permission, or that does not allow JavaScript access to the background |
| 675 // page, we want to give each instance its own process to improve | 679 // page, we want to give each instance its own process to improve |
| 676 // responsiveness. | 680 // responsiveness. |
| 677 if (extension->GetType() == Extension::TYPE_HOSTED_APP) { | 681 if (extension->GetType() == Extension::TYPE_HOSTED_APP) { |
| 678 if (!extension->HasAPIPermission(APIPermission::kBackground) || | 682 if (!extension->HasAPIPermission(APIPermission::kBackground) || |
| (...skipping 10 matching lines...) Expand all Loading... |
| 689 | 693 |
| 690 bool ChromeContentBrowserClient::IsHandledURL(const GURL& url) { | 694 bool ChromeContentBrowserClient::IsHandledURL(const GURL& url) { |
| 691 return ProfileIOData::IsHandledURL(url); | 695 return ProfileIOData::IsHandledURL(url); |
| 692 } | 696 } |
| 693 | 697 |
| 694 bool ChromeContentBrowserClient::IsSuitableHost( | 698 bool ChromeContentBrowserClient::IsSuitableHost( |
| 695 content::RenderProcessHost* process_host, | 699 content::RenderProcessHost* process_host, |
| 696 const GURL& site_url) { | 700 const GURL& site_url) { |
| 697 Profile* profile = | 701 Profile* profile = |
| 698 Profile::FromBrowserContext(process_host->GetBrowserContext()); | 702 Profile::FromBrowserContext(process_host->GetBrowserContext()); |
| 699 ExtensionService* service = profile->GetExtensionService(); | 703 ExtensionService* service = |
| 704 extensions::ExtensionSystem::Get(profile)->extension_service(); |
| 700 extensions::ProcessMap* process_map = service->process_map(); | 705 extensions::ProcessMap* process_map = service->process_map(); |
| 701 | 706 |
| 702 // Don't allow the Task Manager to share a process with anything else. | 707 // Don't allow the Task Manager to share a process with anything else. |
| 703 // Otherwise it can affect the renderers it is observing. | 708 // Otherwise it can affect the renderers it is observing. |
| 704 // Note: we could create another RenderProcessHostPrivilege bucket for | 709 // Note: we could create another RenderProcessHostPrivilege bucket for |
| 705 // this to allow multiple chrome://tasks instances to share, but that's | 710 // this to allow multiple chrome://tasks instances to share, but that's |
| 706 // a very unlikely case without serious consequences. | 711 // a very unlikely case without serious consequences. |
| 707 if (site_url.GetOrigin() == GURL(chrome::kChromeUITaskManagerURL).GetOrigin()) | 712 if (site_url.GetOrigin() == GURL(chrome::kChromeUITaskManagerURL).GetOrigin()) |
| 708 return false; | 713 return false; |
| 709 | 714 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 736 // with background pages. It uses a globally set percentage of processes to | 741 // with background pages. It uses a globally set percentage of processes to |
| 737 // run such extensions and if the limit is exceeded, it returns true, to | 742 // run such extensions and if the limit is exceeded, it returns true, to |
| 738 // indicate to the content module to group extensions together. | 743 // indicate to the content module to group extensions together. |
| 739 bool ChromeContentBrowserClient::ShouldTryToUseExistingProcessHost( | 744 bool ChromeContentBrowserClient::ShouldTryToUseExistingProcessHost( |
| 740 content::BrowserContext* browser_context, const GURL& url) { | 745 content::BrowserContext* browser_context, const GURL& url) { |
| 741 // It has to be a valid URL for us to check for an extension. | 746 // It has to be a valid URL for us to check for an extension. |
| 742 if (!url.is_valid()) | 747 if (!url.is_valid()) |
| 743 return false; | 748 return false; |
| 744 | 749 |
| 745 Profile* profile = Profile::FromBrowserContext(browser_context); | 750 Profile* profile = Profile::FromBrowserContext(browser_context); |
| 746 ExtensionService* service = profile->GetExtensionService(); | 751 ExtensionService* service = |
| 752 extensions::ExtensionSystem::Get(profile)->extension_service(); |
| 747 if (!service) | 753 if (!service) |
| 748 return false; | 754 return false; |
| 749 | 755 |
| 750 // We have to have a valid extension with background page to proceed. | 756 // We have to have a valid extension with background page to proceed. |
| 751 const Extension* extension = | 757 const Extension* extension = |
| 752 service->extensions()->GetExtensionOrAppByURL(ExtensionURLInfo(url)); | 758 service->extensions()->GetExtensionOrAppByURL(ExtensionURLInfo(url)); |
| 753 if (!extension) | 759 if (!extension) |
| 754 return false; | 760 return false; |
| 755 if (!extension->has_background_page()) | 761 if (!extension->has_background_page()) |
| 756 return false; | 762 return false; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 782 | 788 |
| 783 return false; | 789 return false; |
| 784 } | 790 } |
| 785 | 791 |
| 786 void ChromeContentBrowserClient::SiteInstanceGotProcess( | 792 void ChromeContentBrowserClient::SiteInstanceGotProcess( |
| 787 SiteInstance* site_instance) { | 793 SiteInstance* site_instance) { |
| 788 CHECK(site_instance->HasProcess()); | 794 CHECK(site_instance->HasProcess()); |
| 789 | 795 |
| 790 Profile* profile = Profile::FromBrowserContext( | 796 Profile* profile = Profile::FromBrowserContext( |
| 791 site_instance->GetBrowserContext()); | 797 site_instance->GetBrowserContext()); |
| 792 ExtensionService* service = profile->GetExtensionService(); | 798 ExtensionService* service = |
| 799 extensions::ExtensionSystem::Get(profile)->extension_service(); |
| 793 if (!service) | 800 if (!service) |
| 794 return; | 801 return; |
| 795 | 802 |
| 796 const Extension* extension = | 803 const Extension* extension = |
| 797 service->extensions()->GetExtensionOrAppByURL(ExtensionURLInfo( | 804 service->extensions()->GetExtensionOrAppByURL(ExtensionURLInfo( |
| 798 site_instance->GetSiteURL())); | 805 site_instance->GetSiteURL())); |
| 799 if (!extension) | 806 if (!extension) |
| 800 return; | 807 return; |
| 801 | 808 |
| 802 service->process_map()->Insert(extension->id(), | 809 service->process_map()->Insert(extension->id(), |
| 803 site_instance->GetProcess()->GetID(), | 810 site_instance->GetProcess()->GetID(), |
| 804 site_instance->GetId()); | 811 site_instance->GetId()); |
| 805 BrowserThread::PostTask( | 812 BrowserThread::PostTask( |
| 806 BrowserThread::IO, FROM_HERE, | 813 BrowserThread::IO, FROM_HERE, |
| 807 base::Bind(&ExtensionInfoMap::RegisterExtensionProcess, | 814 base::Bind(&ExtensionInfoMap::RegisterExtensionProcess, |
| 808 extensions::ExtensionSystem::Get(profile)->info_map(), | 815 extensions::ExtensionSystem::Get(profile)->info_map(), |
| 809 extension->id(), | 816 extension->id(), |
| 810 site_instance->GetProcess()->GetID(), | 817 site_instance->GetProcess()->GetID(), |
| 811 site_instance->GetId())); | 818 site_instance->GetId())); |
| 812 } | 819 } |
| 813 | 820 |
| 814 void ChromeContentBrowserClient::SiteInstanceDeleting( | 821 void ChromeContentBrowserClient::SiteInstanceDeleting( |
| 815 SiteInstance* site_instance) { | 822 SiteInstance* site_instance) { |
| 816 if (!site_instance->HasProcess()) | 823 if (!site_instance->HasProcess()) |
| 817 return; | 824 return; |
| 818 | 825 |
| 819 Profile* profile = Profile::FromBrowserContext( | 826 Profile* profile = Profile::FromBrowserContext( |
| 820 site_instance->GetBrowserContext()); | 827 site_instance->GetBrowserContext()); |
| 821 ExtensionService* service = profile->GetExtensionService(); | 828 ExtensionService* service = |
| 829 extensions::ExtensionSystem::Get(profile)->extension_service(); |
| 822 if (!service) | 830 if (!service) |
| 823 return; | 831 return; |
| 824 | 832 |
| 825 const Extension* extension = | 833 const Extension* extension = |
| 826 service->extensions()->GetExtensionOrAppByURL( | 834 service->extensions()->GetExtensionOrAppByURL( |
| 827 ExtensionURLInfo(site_instance->GetSiteURL())); | 835 ExtensionURLInfo(site_instance->GetSiteURL())); |
| 828 if (!extension) | 836 if (!extension) |
| 829 return; | 837 return; |
| 830 | 838 |
| 831 service->process_map()->Remove(extension->id(), | 839 service->process_map()->Remove(extension->id(), |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 908 browser_command_line.GetSwitchValueASCII(switches::kLoginProfile); | 916 browser_command_line.GetSwitchValueASCII(switches::kLoginProfile); |
| 909 if (!login_profile.empty()) | 917 if (!login_profile.empty()) |
| 910 command_line->AppendSwitchASCII(switches::kLoginProfile, login_profile); | 918 command_line->AppendSwitchASCII(switches::kLoginProfile, login_profile); |
| 911 #endif | 919 #endif |
| 912 | 920 |
| 913 content::RenderProcessHost* process = | 921 content::RenderProcessHost* process = |
| 914 content::RenderProcessHost::FromID(child_process_id); | 922 content::RenderProcessHost::FromID(child_process_id); |
| 915 if (process) { | 923 if (process) { |
| 916 Profile* profile = Profile::FromBrowserContext( | 924 Profile* profile = Profile::FromBrowserContext( |
| 917 process->GetBrowserContext()); | 925 process->GetBrowserContext()); |
| 918 if (profile->GetExtensionService()) { | 926 ExtensionService* extension_service = |
| 919 extensions::ProcessMap* process_map = | 927 extensions::ExtensionSystem::Get(profile)->extension_service(); |
| 920 profile->GetExtensionService()->process_map(); | 928 if (extension_service) { |
| 929 extensions::ProcessMap* process_map = extension_service->process_map(); |
| 921 if (process_map && process_map->Contains(process->GetID())) | 930 if (process_map && process_map->Contains(process->GetID())) |
| 922 command_line->AppendSwitch(switches::kExtensionProcess); | 931 command_line->AppendSwitch(switches::kExtensionProcess); |
| 923 } | 932 } |
| 924 | 933 |
| 925 PrefService* prefs = profile->GetPrefs(); | 934 PrefService* prefs = profile->GetPrefs(); |
| 926 // Currently this pref is only registered if applied via a policy. | 935 // Currently this pref is only registered if applied via a policy. |
| 927 if (prefs->HasPrefPath(prefs::kDisable3DAPIs) && | 936 if (prefs->HasPrefPath(prefs::kDisable3DAPIs) && |
| 928 prefs->GetBoolean(prefs::kDisable3DAPIs)) { | 937 prefs->GetBoolean(prefs::kDisable3DAPIs)) { |
| 929 // Turn this policy into a command line switch. | 938 // Turn this policy into a command line switch. |
| 930 command_line->AppendSwitch(switches::kDisable3DAPIs); | 939 command_line->AppendSwitch(switches::kDisable3DAPIs); |
| (...skipping 421 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1352 tab_util::GetWebContentsByID(render_process_id, render_view_id); | 1361 tab_util::GetWebContentsByID(render_process_id, render_view_id); |
| 1353 if (!contents) { | 1362 if (!contents) { |
| 1354 NOTREACHED(); | 1363 NOTREACHED(); |
| 1355 return; | 1364 return; |
| 1356 } | 1365 } |
| 1357 | 1366 |
| 1358 // Skip showing the infobar if the request comes from an extension, and that | 1367 // Skip showing the infobar if the request comes from an extension, and that |
| 1359 // extension has the 'notify' permission. (If the extension does not have the | 1368 // extension has the 'notify' permission. (If the extension does not have the |
| 1360 // permission, the user will still be prompted.) | 1369 // permission, the user will still be prompted.) |
| 1361 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); | 1370 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); |
| 1362 ExtensionService* service = profile->GetExtensionService(); | 1371 ExtensionService* service = |
| 1372 extensions::ExtensionSystem::Get(profile)->extension_service(); |
| 1363 const Extension* extension = !service ? NULL : | 1373 const Extension* extension = !service ? NULL : |
| 1364 service->extensions()->GetExtensionOrAppByURL(ExtensionURLInfo( | 1374 service->extensions()->GetExtensionOrAppByURL(ExtensionURLInfo( |
| 1365 source_origin)); | 1375 source_origin)); |
| 1366 if (extension && | 1376 if (extension && |
| 1367 extension->HasAPIPermission(APIPermission::kNotification)) { | 1377 extension->HasAPIPermission(APIPermission::kNotification)) { |
| 1368 RenderViewHost* rvh = | 1378 RenderViewHost* rvh = |
| 1369 RenderViewHost::FromID(render_process_id, render_view_id); | 1379 RenderViewHost::FromID(render_process_id, render_view_id); |
| 1370 if (rvh) | 1380 if (rvh) |
| 1371 rvh->DesktopNotificationPermissionRequestDone(callback_context); | 1381 rvh->DesktopNotificationPermissionRequestDone(callback_context); |
| 1372 return; | 1382 return; |
| (...skipping 269 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1642 CharacterEncoding::GetCanonicalEncodingNameByAliasName( | 1652 CharacterEncoding::GetCanonicalEncodingNameByAliasName( |
| 1643 web_prefs->default_encoding); | 1653 web_prefs->default_encoding); |
| 1644 if (web_prefs->default_encoding.empty()) { | 1654 if (web_prefs->default_encoding.empty()) { |
| 1645 prefs->ClearPref(prefs::kDefaultCharset); | 1655 prefs->ClearPref(prefs::kDefaultCharset); |
| 1646 web_prefs->default_encoding = prefs->GetString(prefs::kDefaultCharset); | 1656 web_prefs->default_encoding = prefs->GetString(prefs::kDefaultCharset); |
| 1647 } | 1657 } |
| 1648 DCHECK(!web_prefs->default_encoding.empty()); | 1658 DCHECK(!web_prefs->default_encoding.empty()); |
| 1649 | 1659 |
| 1650 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); | 1660 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); |
| 1651 chrome::ViewType view_type = chrome::GetViewType(web_contents); | 1661 chrome::ViewType view_type = chrome::GetViewType(web_contents); |
| 1652 ExtensionService* service = profile->GetExtensionService(); | 1662 ExtensionService* service = |
| 1663 extensions::ExtensionSystem::Get(profile)->extension_service(); |
| 1653 if (service) { | 1664 if (service) { |
| 1654 const GURL& url = rvh->GetSiteInstance()->GetSiteURL(); | 1665 const GURL& url = rvh->GetSiteInstance()->GetSiteURL(); |
| 1655 const Extension* extension = service->extensions()->GetByID(url.host()); | 1666 const Extension* extension = service->extensions()->GetByID(url.host()); |
| 1656 // Ensure that we are only granting extension preferences to URLs with | 1667 // Ensure that we are only granting extension preferences to URLs with |
| 1657 // the correct scheme. Without this check, chrome-guest:// schemes used by | 1668 // the correct scheme. Without this check, chrome-guest:// schemes used by |
| 1658 // webview tags as well as hosts that happen to match the id of an | 1669 // webview tags as well as hosts that happen to match the id of an |
| 1659 // installed extension would get the wrong preferences. | 1670 // installed extension would get the wrong preferences. |
| 1660 if (url.SchemeIs(extensions::kExtensionScheme)) { | 1671 if (url.SchemeIs(extensions::kExtensionScheme)) { |
| 1661 extension_webkit_preferences::SetPreferences( | 1672 extension_webkit_preferences::SetPreferences( |
| 1662 extension, view_type, web_prefs); | 1673 extension, view_type, web_prefs); |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1781 return false; | 1792 return false; |
| 1782 | 1793 |
| 1783 std::string host = url.host(); | 1794 std::string host = url.host(); |
| 1784 if (url.SchemeIs(extensions::kExtensionScheme) && | 1795 if (url.SchemeIs(extensions::kExtensionScheme) && |
| 1785 allowed_socket_origins_.count(host)) { | 1796 allowed_socket_origins_.count(host)) { |
| 1786 return true; | 1797 return true; |
| 1787 } | 1798 } |
| 1788 | 1799 |
| 1789 Profile* profile = Profile::FromBrowserContext(browser_context); | 1800 Profile* profile = Profile::FromBrowserContext(browser_context); |
| 1790 const Extension* extension = NULL; | 1801 const Extension* extension = NULL; |
| 1791 if (profile && profile->GetExtensionService()) { | 1802 ExtensionService* extension_service = |
| 1792 extension = profile->GetExtensionService()->extensions()-> | 1803 extensions::ExtensionSystem::Get(profile)->extension_service(); |
| 1804 if (extension_service) { |
| 1805 extension = extension_service->extensions()-> |
| 1793 GetExtensionOrAppByURL(ExtensionURLInfo(url)); | 1806 GetExtensionOrAppByURL(ExtensionURLInfo(url)); |
| 1794 } | 1807 } |
| 1795 | 1808 |
| 1796 // Need to check this now and not on construction because otherwise it won't | 1809 // Need to check this now and not on construction because otherwise it won't |
| 1797 // work with browser_tests. | 1810 // work with browser_tests. |
| 1798 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | 1811 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
| 1799 std::string allowed_list = | 1812 std::string allowed_list = |
| 1800 command_line.GetSwitchValueASCII(switches::kAllowNaClSocketAPI); | 1813 command_line.GetSwitchValueASCII(switches::kAllowNaClSocketAPI); |
| 1801 if (allowed_list == "*") { | 1814 if (allowed_list == "*") { |
| 1802 // The wildcard allows socket API only for packaged and platform apps. | 1815 // The wildcard allows socket API only for packaged and platform apps. |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1930 io_thread_application_locale_ = locale; | 1943 io_thread_application_locale_ = locale; |
| 1931 } | 1944 } |
| 1932 | 1945 |
| 1933 void ChromeContentBrowserClient::SetApplicationLocaleOnIOThread( | 1946 void ChromeContentBrowserClient::SetApplicationLocaleOnIOThread( |
| 1934 const std::string& locale) { | 1947 const std::string& locale) { |
| 1935 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 1948 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 1936 io_thread_application_locale_ = locale; | 1949 io_thread_application_locale_ = locale; |
| 1937 } | 1950 } |
| 1938 | 1951 |
| 1939 } // namespace chrome | 1952 } // namespace chrome |
| OLD | NEW |