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