content_favicon_driver.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. // Copyright 2015 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/favicon/content/content_favicon_driver.h"
  5. #include "base/bind.h"
  6. #include "base/memory/ptr_util.h"
  7. #include "components/favicon/content/favicon_url_util.h"
  8. #include "components/favicon/core/favicon_service.h"
  9. #include "components/favicon/core/favicon_url.h"
  10. #include "content/public/browser/browser_context.h"
  11. #include "content/public/browser/favicon_status.h"
  12. #include "content/public/browser/navigation_controller.h"
  13. #include "content/public/browser/navigation_details.h"
  14. #include "content/public/browser/navigation_entry.h"
  15. #include "content/public/browser/navigation_handle.h"
  16. #include "content/public/browser/page.h"
  17. #include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
  18. #include "ui/gfx/image/image.h"
  19. namespace favicon {
  20. gfx::Image ContentFaviconDriver::GetFavicon() const {
  21. // Like GetTitle(), we also want to use the favicon for the last committed
  22. // entry rather than a pending navigation entry.
  23. content::NavigationController& controller = web_contents()->GetController();
  24. content::NavigationEntry* entry = controller.GetLastCommittedEntry();
  25. if (entry)
  26. return entry->GetFavicon().image;
  27. return gfx::Image();
  28. }
  29. bool ContentFaviconDriver::FaviconIsValid() const {
  30. content::NavigationController& controller = web_contents()->GetController();
  31. content::NavigationEntry* entry = controller.GetLastCommittedEntry();
  32. if (entry)
  33. return entry->GetFavicon().valid;
  34. return false;
  35. }
  36. GURL ContentFaviconDriver::GetActiveURL() {
  37. content::NavigationEntry* entry =
  38. web_contents()->GetController().GetLastCommittedEntry();
  39. return entry ? entry->GetURL() : GURL();
  40. }
  41. GURL ContentFaviconDriver::GetManifestURL(content::RenderFrameHost* rfh) {
  42. DocumentManifestData* document_data =
  43. DocumentManifestData::GetOrCreateForCurrentDocument(rfh);
  44. return document_data->has_manifest_url
  45. ? rfh->GetPage().GetManifestUrl().value_or(GURL())
  46. : GURL();
  47. }
  48. ContentFaviconDriver::ContentFaviconDriver(content::WebContents* web_contents,
  49. CoreFaviconService* favicon_service)
  50. : content::WebContentsObserver(web_contents),
  51. content::WebContentsUserData<ContentFaviconDriver>(*web_contents),
  52. FaviconDriverImpl(favicon_service) {}
  53. ContentFaviconDriver::~ContentFaviconDriver() = default;
  54. ContentFaviconDriver::DocumentManifestData::DocumentManifestData(
  55. content::RenderFrameHost* rfh)
  56. : content::DocumentUserData<DocumentManifestData>(rfh) {}
  57. ContentFaviconDriver::DocumentManifestData::~DocumentManifestData() = default;
  58. ContentFaviconDriver::NavigationManifestData::NavigationManifestData(
  59. content::NavigationHandle& navigation_handle) {}
  60. ContentFaviconDriver::NavigationManifestData::~NavigationManifestData() =
  61. default;
  62. void ContentFaviconDriver::OnDidDownloadManifest(
  63. ManifestDownloadCallback callback,
  64. const GURL& manifest_url,
  65. blink::mojom::ManifestPtr manifest) {
  66. // ~WebContentsImpl triggers running any pending callbacks for manifests.
  67. // As we're about to be destroyed ignore the request. To do otherwise may
  68. // result in calling back to this and attempting to use the WebContents, which
  69. // will crash.
  70. if (!web_contents())
  71. return;
  72. std::vector<FaviconURL> candidates;
  73. if (manifest) {
  74. for (const auto& icon : manifest->icons) {
  75. candidates.emplace_back(
  76. icon.src, favicon_base::IconType::kWebManifestIcon, icon.sizes);
  77. }
  78. }
  79. std::move(callback).Run(candidates);
  80. }
  81. int ContentFaviconDriver::DownloadImage(const GURL& url,
  82. int max_image_size,
  83. ImageDownloadCallback callback) {
  84. bool bypass_cache = (bypass_cache_page_url_ == GetActiveURL());
  85. bypass_cache_page_url_ = GURL();
  86. const gfx::Size preferred_size(max_image_size, max_image_size);
  87. return web_contents()->DownloadImage(url, true, preferred_size,
  88. /*max_bitmap_size=*/max_image_size,
  89. bypass_cache, std::move(callback));
  90. }
  91. void ContentFaviconDriver::DownloadManifest(const GURL& url,
  92. ManifestDownloadCallback callback) {
  93. // TODO(crbug.com/1201237): This appears to be reachable from pages other
  94. // than the primary page. This code should likely be refactored so that either
  95. // this is unreachable from other pages, or the correct page is plumbed in
  96. // here.
  97. web_contents()->GetPrimaryPage().GetManifest(
  98. base::BindOnce(&ContentFaviconDriver::OnDidDownloadManifest,
  99. base::Unretained(this), std::move(callback)));
  100. }
  101. bool ContentFaviconDriver::IsOffTheRecord() {
  102. DCHECK(web_contents());
  103. return web_contents()->GetBrowserContext()->IsOffTheRecord();
  104. }
  105. void ContentFaviconDriver::OnFaviconUpdated(
  106. const GURL& page_url,
  107. FaviconDriverObserver::NotificationIconType notification_icon_type,
  108. const GURL& icon_url,
  109. bool icon_url_changed,
  110. const gfx::Image& image) {
  111. content::NavigationEntry* entry =
  112. web_contents()->GetController().GetLastCommittedEntry();
  113. DCHECK(entry);
  114. DCHECK_EQ(entry->GetURL(), page_url);
  115. if (notification_icon_type == FaviconDriverObserver::NON_TOUCH_16_DIP) {
  116. entry->GetFavicon().valid = true;
  117. entry->GetFavicon().url = icon_url;
  118. entry->GetFavicon().image = image;
  119. web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
  120. }
  121. NotifyFaviconUpdatedObservers(notification_icon_type, icon_url,
  122. icon_url_changed, image);
  123. }
  124. void ContentFaviconDriver::OnFaviconDeleted(
  125. const GURL& page_url,
  126. FaviconDriverObserver::NotificationIconType notification_icon_type) {
  127. content::NavigationEntry* entry =
  128. web_contents()->GetController().GetLastCommittedEntry();
  129. DCHECK(entry && entry->GetURL() == page_url);
  130. if (notification_icon_type == FaviconDriverObserver::NON_TOUCH_16_DIP) {
  131. entry->GetFavicon() = content::FaviconStatus();
  132. web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
  133. }
  134. NotifyFaviconUpdatedObservers(notification_icon_type, /*icon_url=*/GURL(),
  135. /*icon_url_changed=*/true,
  136. content::FaviconStatus().image);
  137. }
  138. void ContentFaviconDriver::DidUpdateFaviconURL(
  139. content::RenderFrameHost* rfh,
  140. const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
  141. // Ignore the update if there is no last committed navigation entry. This can
  142. // occur when loading an initially blank page.
  143. content::NavigationEntry* entry =
  144. web_contents()->GetController().GetLastCommittedEntry();
  145. if (!entry)
  146. return;
  147. if (!rfh->IsDocumentOnLoadCompletedInMainFrame())
  148. return;
  149. OnUpdateCandidates(rfh->GetLastCommittedURL(),
  150. FaviconURLsFromContentFaviconURLs(candidates),
  151. GetManifestURL(rfh));
  152. }
  153. void ContentFaviconDriver::DidUpdateWebManifestURL(
  154. content::RenderFrameHost* rfh,
  155. const GURL& manifest_url) {
  156. // Ignore the update if there is no last committed navigation entry. This can
  157. // occur when loading an initially blank page.
  158. content::NavigationEntry* entry =
  159. web_contents()->GetController().GetLastCommittedEntry();
  160. if (!entry || !rfh->IsDocumentOnLoadCompletedInMainFrame())
  161. return;
  162. DocumentManifestData* document_data =
  163. DocumentManifestData::GetOrCreateForCurrentDocument(rfh);
  164. document_data->has_manifest_url = true;
  165. // On regular page loads, DidUpdateManifestURL() is guaranteed to be called
  166. // before DidUpdateFaviconURL(). However, a page can update the favicons via
  167. // javascript.
  168. if (!rfh->FaviconURLs().empty()) {
  169. OnUpdateCandidates(rfh->GetLastCommittedURL(),
  170. FaviconURLsFromContentFaviconURLs(rfh->FaviconURLs()),
  171. GetManifestURL(rfh));
  172. }
  173. }
  174. void ContentFaviconDriver::DidStartNavigation(
  175. content::NavigationHandle* navigation_handle) {
  176. if (!navigation_handle->IsInPrimaryMainFrame())
  177. return;
  178. content::ReloadType reload_type = navigation_handle->GetReloadType();
  179. if (reload_type == content::ReloadType::NONE || IsOffTheRecord())
  180. return;
  181. if (!navigation_handle->IsSameDocument()) {
  182. NavigationManifestData* navigation_data =
  183. NavigationManifestData::GetOrCreateForNavigationHandle(
  184. *navigation_handle);
  185. navigation_data->has_manifest_url = false;
  186. }
  187. if (reload_type == content::ReloadType::BYPASSING_CACHE)
  188. bypass_cache_page_url_ = navigation_handle->GetURL();
  189. SetFaviconOutOfDateForPage(
  190. navigation_handle->GetURL(),
  191. reload_type == content::ReloadType::BYPASSING_CACHE);
  192. }
  193. void ContentFaviconDriver::DidFinishNavigation(
  194. content::NavigationHandle* navigation_handle) {
  195. if (!navigation_handle->IsInPrimaryMainFrame() ||
  196. !navigation_handle->HasCommitted() || navigation_handle->IsErrorPage()) {
  197. return;
  198. }
  199. // Transfer in-flight navigation data to the document user data.
  200. NavigationManifestData* navigation_data =
  201. NavigationManifestData::GetOrCreateForNavigationHandle(
  202. *navigation_handle);
  203. DocumentManifestData* document_data =
  204. DocumentManifestData::GetOrCreateForCurrentDocument(
  205. navigation_handle->GetRenderFrameHost());
  206. document_data->has_manifest_url = navigation_data->has_manifest_url;
  207. // Wait till the user navigates to a new URL to start checking the cache
  208. // again. The cache may be ignored for non-reload navigations (e.g.
  209. // history.replace() in-page navigation). This is allowed to increase the
  210. // likelihood that "reloading a page ignoring the cache" redownloads the
  211. // favicon. In particular, a page may do an in-page navigation before
  212. // FaviconHandler has the time to determine that the favicon needs to be
  213. // redownloaded.
  214. GURL url = navigation_handle->GetURL();
  215. if (url != bypass_cache_page_url_)
  216. bypass_cache_page_url_ = GURL();
  217. // Get the favicon, either from history or request it from the net.
  218. FetchFavicon(url, navigation_handle->IsSameDocument());
  219. }
  220. NAVIGATION_HANDLE_USER_DATA_KEY_IMPL(
  221. ContentFaviconDriver::NavigationManifestData);
  222. DOCUMENT_USER_DATA_KEY_IMPL(ContentFaviconDriver::DocumentManifestData);
  223. WEB_CONTENTS_USER_DATA_KEY_IMPL(ContentFaviconDriver);
  224. } // namespace favicon