| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/search/instant_service.h" |
| |
| #include <stddef.h> |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/files/file_util.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/observer_list.h" |
| #include "base/path_service.h" |
| #include "base/scoped_observation.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/thread_pool.h" |
| #include "base/time/clock.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/search/instant_service_factory.h" |
| #include "chrome/browser/search/instant_service_observer.h" |
| #include "chrome/browser/search/most_visited_iframe_source.h" |
| #include "chrome/browser/search/search.h" |
| #include "chrome/browser/themes/theme_properties.h" |
| #include "chrome/browser/themes/theme_service.h" |
| #include "chrome/browser/themes/theme_service_factory.h" |
| #include "chrome/browser/ui/webui/favicon_source.h" |
| #include "chrome/browser/ui/webui/theme_source.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/search/search.mojom.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/grit/theme_resources.h" |
| #include "components/favicon_base/favicon_url_parser.h" |
| #include "components/ntp_tiles/constants.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/search/ntp_features.h" |
| #include "components/search/search_provider_observer.h" |
| #include "components/sync_preferences/pref_service_syncable.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/notification_types.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/url_data_source.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/common/extension.h" |
| #include "mojo/public/cpp/bindings/associated_remote.h" |
| #include "ui/gfx/color_palette.h" |
| #include "ui/gfx/color_utils.h" |
| |
| InstantService::InstantService(Profile* profile) |
| : profile_(profile), |
| most_visited_info_(std::make_unique<InstantMostVisitedInfo>()), |
| pref_service_(profile_->GetPrefs()), |
| native_theme_(ui::NativeTheme::GetInstanceForNativeUi()), |
| background_updated_timestamp_(base::TimeTicks::Now()) { |
| // The initialization below depends on a typical set of browser threads. Skip |
| // it if we are running in a unit test without the full suite. |
| if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) |
| return; |
| |
| registrar_.Add(this, |
| content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
| content::NotificationService::AllSources()); |
| |
| most_visited_sites_ = ChromeMostVisitedSitesFactory::NewForProfile(profile_); |
| if (most_visited_sites_) { |
| most_visited_sites_->EnableCustomLinks(false); |
| most_visited_sites_->AddMostVisitedURLsObserver( |
| this, ntp_tiles::kMaxNumMostVisited); |
| } |
| |
| // Listen for theme installation. |
| ThemeServiceFactory::GetForProfile(profile_)->AddObserver(this); |
| |
| // TODO(crbug.com/1192394): multiple WebUI pages depend on the theme source |
| // without adding it themselves. This is not causing an issue because the |
| // theme source is being added here. The source should be added where it is |
| // used and then the following can be removed. |
| content::URLDataSource::Add(profile_, |
| std::make_unique<ThemeSource>(profile_)); |
| |
| // Set up the data sources that Instant uses on the NTP. |
| content::URLDataSource::Add( |
| profile_, std::make_unique<FaviconSource>( |
| profile_, chrome::FaviconUrlFormat::kFaviconLegacy)); |
| content::URLDataSource::Add(profile_, |
| std::make_unique<MostVisitedIframeSource>()); |
| |
| theme_observation_.Observe(native_theme_.get()); |
| } |
| |
| InstantService::~InstantService() = default; |
| |
| void InstantService::AddInstantProcess(int process_id) { |
| process_ids_.insert(process_id); |
| } |
| |
| bool InstantService::IsInstantProcess(int process_id) const { |
| return process_ids_.find(process_id) != process_ids_.end(); |
| } |
| |
| void InstantService::AddObserver(InstantServiceObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void InstantService::RemoveObserver(InstantServiceObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| void InstantService::OnNewTabPageOpened() { |
| if (most_visited_sites_) { |
| most_visited_sites_->Refresh(); |
| } |
| } |
| |
| void InstantService::OnThemeChanged() { |
| theme_ = nullptr; |
| UpdateNtpTheme(); |
| } |
| |
| void InstantService::DeleteMostVisitedItem(const GURL& url) { |
| if (most_visited_sites_) { |
| most_visited_sites_->AddOrRemoveBlockedUrl(url, true); |
| } |
| } |
| |
| void InstantService::UndoMostVisitedDeletion(const GURL& url) { |
| if (most_visited_sites_) { |
| most_visited_sites_->AddOrRemoveBlockedUrl(url, false); |
| } |
| } |
| |
| void InstantService::UndoAllMostVisitedDeletions() { |
| if (most_visited_sites_) { |
| most_visited_sites_->ClearBlockedUrls(); |
| } |
| } |
| |
| void InstantService::UpdateNtpTheme() { |
| SetNtpElementsNtpTheme(); |
| |
| NotifyAboutNtpTheme(); |
| } |
| |
| void InstantService::UpdateMostVisitedInfo() { |
| NotifyAboutMostVisitedInfo(); |
| } |
| |
| NtpTheme* InstantService::GetInitializedNtpTheme() { |
| if (!theme_) |
| BuildNtpTheme(); |
| return theme_.get(); |
| } |
| |
| void InstantService::SetNativeThemeForTesting(ui::NativeTheme* theme) { |
| theme_observation_.Reset(); |
| native_theme_ = theme; |
| theme_observation_.Observe(native_theme_.get()); |
| } |
| |
| void InstantService::Shutdown() { |
| process_ids_.clear(); |
| |
| if (most_visited_sites_) { |
| most_visited_sites_.reset(); |
| } |
| |
| ThemeServiceFactory::GetForProfile(profile_)->RemoveObserver(this); |
| } |
| |
| void InstantService::Observe(int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| switch (type) { |
| case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: { |
| content::RenderProcessHost* rph = |
| content::Source<content::RenderProcessHost>(source).ptr(); |
| Profile* renderer_profile = |
| static_cast<Profile*>(rph->GetBrowserContext()); |
| if (profile_ == renderer_profile) |
| OnRendererProcessTerminated(rph->GetID()); |
| break; |
| } |
| default: |
| NOTREACHED() << "Unexpected notification type in InstantService."; |
| } |
| } |
| |
| void InstantService::OnRendererProcessTerminated(int process_id) { |
| process_ids_.erase(process_id); |
| } |
| |
| void InstantService::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) { |
| DCHECK_EQ(observed_theme, native_theme_); |
| // Force the theme information to rebuild so the correct using_dark_colors |
| // value is sent to the renderer. |
| BuildNtpTheme(); |
| UpdateNtpTheme(); |
| } |
| |
| void InstantService::OnURLsAvailable( |
| const std::map<ntp_tiles::SectionType, ntp_tiles::NTPTilesVector>& |
| sections) { |
| DCHECK(most_visited_sites_); |
| most_visited_info_->items.clear(); |
| // Use only personalized tiles for instant service. |
| const ntp_tiles::NTPTilesVector& tiles = |
| sections.at(ntp_tiles::SectionType::PERSONALIZED); |
| for (const ntp_tiles::NTPTile& tile : tiles) { |
| InstantMostVisitedItem item; |
| item.url = tile.url; |
| item.title = tile.title; |
| item.favicon = tile.favicon_url; |
| most_visited_info_->items.push_back(item); |
| } |
| |
| NotifyAboutMostVisitedInfo(); |
| } |
| |
| void InstantService::OnIconMadeAvailable(const GURL& site_url) {} |
| |
| void InstantService::NotifyAboutMostVisitedInfo() { |
| for (InstantServiceObserver& observer : observers_) |
| observer.MostVisitedInfoChanged(*most_visited_info_); |
| } |
| |
| void InstantService::NotifyAboutNtpTheme() { |
| for (InstantServiceObserver& observer : observers_) |
| observer.NtpThemeChanged(*theme_); |
| } |
| |
| void InstantService::BuildNtpTheme() { |
| // Get theme information from theme service. |
| theme_ = std::make_unique<NtpTheme>(); |
| |
| // Get if the current theme is the default theme. |
| ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile_); |
| theme_->using_default_theme = theme_service->UsingDefaultTheme(); |
| |
| SetNtpElementsNtpTheme(); |
| |
| if (theme_service->UsingExtensionTheme()) { |
| const extensions::Extension* extension = |
| extensions::ExtensionRegistry::Get(profile_) |
| ->enabled_extensions() |
| .GetByID(theme_service->GetThemeID()); |
| if (extension) { |
| theme_->theme_id = theme_service->GetThemeID(); |
| |
| const ui::ThemeProvider& theme_provider = |
| ThemeService::GetThemeProviderForProfile(profile_); |
| if (theme_provider.HasCustomImage(IDR_THEME_NTP_BACKGROUND)) { |
| theme_->has_theme_image = true; |
| |
| // Set theme background image horizontal alignment. |
| int alignment = theme_provider.GetDisplayProperty( |
| ThemeProperties::NTP_BACKGROUND_ALIGNMENT); |
| if (alignment & ThemeProperties::ALIGN_LEFT) |
| theme_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_LEFT; |
| else if (alignment & ThemeProperties::ALIGN_RIGHT) |
| theme_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_RIGHT; |
| else |
| theme_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER; |
| |
| // Set theme background image vertical alignment. |
| if (alignment & ThemeProperties::ALIGN_TOP) |
| theme_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_TOP; |
| else if (alignment & ThemeProperties::ALIGN_BOTTOM) |
| theme_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_BOTTOM; |
| else |
| theme_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER; |
| |
| // Set theme background image tiling. |
| int tiling = theme_provider.GetDisplayProperty( |
| ThemeProperties::NTP_BACKGROUND_TILING); |
| switch (tiling) { |
| case ThemeProperties::NO_REPEAT: |
| theme_->image_tiling = THEME_BKGRND_IMAGE_NO_REPEAT; |
| break; |
| case ThemeProperties::REPEAT_X: |
| theme_->image_tiling = THEME_BKGRND_IMAGE_REPEAT_X; |
| break; |
| case ThemeProperties::REPEAT_Y: |
| theme_->image_tiling = THEME_BKGRND_IMAGE_REPEAT_Y; |
| break; |
| case ThemeProperties::REPEAT: |
| theme_->image_tiling = THEME_BKGRND_IMAGE_REPEAT; |
| break; |
| } |
| |
| theme_->has_attribution = |
| theme_provider.HasCustomImage(IDR_THEME_NTP_ATTRIBUTION); |
| } |
| } |
| } |
| } |
| |
| // static |
| bool InstantService::ShouldServiceRequest( |
| const GURL& url, |
| content::BrowserContext* browser_context, |
| int render_process_id) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| auto* instant_service = InstantServiceFactory::GetForProfile( |
| static_cast<Profile*>(browser_context)); |
| |
| if (!instant_service) |
| return false; |
| |
| // The process_id for the navigation request will be -1. If |
| // so, allow this request since it's not going to another renderer. |
| return render_process_id == -1 || |
| instant_service->IsInstantProcess(render_process_id); |
| } |
| |
| void InstantService::SetNtpElementsNtpTheme() { |
| NtpTheme* theme = GetInitializedNtpTheme(); |
| const ui::ThemeProvider& theme_provider = |
| ThemeService::GetThemeProviderForProfile(profile_); |
| theme->logo_alternate = theme_provider.GetDisplayProperty( |
| ThemeProperties::NTP_LOGO_ALTERNATE) == 1; |
| } |