1 // Copyright 2018 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
5 #include "chrome/browser/web_applications/components/web_app_data_retriever.h"
6
7 #include <memory>
8 #include <set>
9 #include <string>
10 #include <utility>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/installable/installable_data.h"
17 #include "chrome/browser/installable/installable_manager.h"
18 #include "chrome/browser/web_applications/components/web_app_icon_generator.h"
19 #include "chrome/browser/web_applications/components/web_application_info.h"
20 #include "chrome/common/chrome_render_frame.mojom.h"
21 #include "content/public/browser/navigation_entry.h"
22 #include "content/public/browser/render_frame_host.h"
23 #include "content/public/browser/web_contents.h"
24 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
25 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
26 #include "third_party/skia/include/core/SkColor.h"
27
28 namespace web_app {
29
30 WebAppDataRetriever::WebAppDataRetriever() = default;
31
32 WebAppDataRetriever::~WebAppDataRetriever() = default;
33
GetWebApplicationInfo(content::WebContents * web_contents,GetWebApplicationInfoCallback callback)34 void WebAppDataRetriever::GetWebApplicationInfo(
35 content::WebContents* web_contents,
36 GetWebApplicationInfoCallback callback) {
37 Observe(web_contents);
38
39 // Concurrent calls are not allowed.
40 DCHECK(!get_web_app_info_callback_);
41 get_web_app_info_callback_ = std::move(callback);
42
43 content::NavigationEntry* entry =
44 web_contents->GetController().GetLastCommittedEntry();
45 if (!entry) {
46 base::ThreadTaskRunnerHandle::Get()->PostTask(
47 FROM_HERE, base::BindOnce(&WebAppDataRetriever::CallCallbackOnError,
48 weak_ptr_factory_.GetWeakPtr()));
49 return;
50 }
51
52 // Makes a copy of WebContents fields right after Commit but before a mojo
53 // request to the renderer process.
54 default_web_application_info_ = std::make_unique<WebApplicationInfo>();
55 default_web_application_info_->start_url =
56 web_contents->GetLastCommittedURL();
57 default_web_application_info_->title = web_contents->GetTitle();
58 if (default_web_application_info_->title.empty()) {
59 default_web_application_info_->title =
60 base::UTF8ToUTF16(default_web_application_info_->start_url.spec());
61 }
62
63 mojo::AssociatedRemote<chrome::mojom::ChromeRenderFrame> chrome_render_frame;
64 web_contents->GetMainFrame()->GetRemoteAssociatedInterfaces()->GetInterface(
65 &chrome_render_frame);
66
67 // Set the error handler so that we can run |get_web_app_info_callback_| if
68 // the WebContents or the RenderFrameHost are destroyed and the connection
69 // to ChromeRenderFrame is lost.
70 chrome_render_frame.set_disconnect_handler(
71 base::BindOnce(&WebAppDataRetriever::CallCallbackOnError,
72 weak_ptr_factory_.GetWeakPtr()));
73 // Bind the InterfacePtr into the callback so that it's kept alive
74 // until there's either a connection error or a response.
75 auto* web_page_metadata_proxy = chrome_render_frame.get();
76 web_page_metadata_proxy->GetWebPageMetadata(
77 base::BindOnce(&WebAppDataRetriever::OnGetWebPageMetadata,
78 weak_ptr_factory_.GetWeakPtr(),
79 std::move(chrome_render_frame), entry->GetUniqueID()));
80 }
81
CheckInstallabilityAndRetrieveManifest(content::WebContents * web_contents,bool bypass_service_worker_check,CheckInstallabilityCallback callback)82 void WebAppDataRetriever::CheckInstallabilityAndRetrieveManifest(
83 content::WebContents* web_contents,
84 bool bypass_service_worker_check,
85 CheckInstallabilityCallback callback) {
86 InstallableManager* installable_manager =
87 InstallableManager::FromWebContents(web_contents);
88 DCHECK(installable_manager);
89
90 Observe(web_contents);
91
92 // Concurrent calls are not allowed.
93 DCHECK(!check_installability_callback_);
94 check_installability_callback_ = std::move(callback);
95
96 // TODO(crbug.com/829232) Unify with other calls to GetData.
97 InstallableParams params;
98 params.check_eligibility = true;
99 params.valid_primary_icon = true;
100 params.valid_manifest = true;
101 params.check_webapp_manifest_display = false;
102 // Do not wait for a service worker if it doesn't exist.
103 params.has_worker = !bypass_service_worker_check;
104 // Do not wait_for_worker. OnDidPerformInstallableCheck is always invoked.
105 installable_manager->GetData(
106 params, base::BindOnce(&WebAppDataRetriever::OnDidPerformInstallableCheck,
107 weak_ptr_factory_.GetWeakPtr()));
108 }
109
GetIcons(content::WebContents * web_contents,const std::vector<GURL> & icon_urls,bool skip_page_favicons,WebAppIconDownloader::Histogram histogram,GetIconsCallback callback)110 void WebAppDataRetriever::GetIcons(content::WebContents* web_contents,
111 const std::vector<GURL>& icon_urls,
112 bool skip_page_favicons,
113 WebAppIconDownloader::Histogram histogram,
114 GetIconsCallback callback) {
115 Observe(web_contents);
116
117 // Concurrent calls are not allowed.
118 CHECK(!get_icons_callback_);
119 get_icons_callback_ = std::move(callback);
120
121 // TODO(loyso): Refactor WebAppIconDownloader: crbug.com/907296.
122 icon_downloader_ = std::make_unique<WebAppIconDownloader>(
123 web_contents, icon_urls, histogram,
124 base::BindOnce(&WebAppDataRetriever::OnIconsDownloaded,
125 weak_ptr_factory_.GetWeakPtr()));
126
127 if (skip_page_favicons)
128 icon_downloader_->SkipPageFavicons();
129
130 icon_downloader_->Start();
131 }
132
WebContentsDestroyed()133 void WebAppDataRetriever::WebContentsDestroyed() {
134 CallCallbackOnError();
135 }
136
RenderProcessGone(base::TerminationStatus status)137 void WebAppDataRetriever::RenderProcessGone(base::TerminationStatus status) {
138 CallCallbackOnError();
139 }
140
OnGetWebPageMetadata(mojo::AssociatedRemote<chrome::mojom::ChromeRenderFrame> chrome_render_frame,int last_committed_nav_entry_unique_id,chrome::mojom::WebPageMetadataPtr web_page_metadata)141 void WebAppDataRetriever::OnGetWebPageMetadata(
142 mojo::AssociatedRemote<chrome::mojom::ChromeRenderFrame>
143 chrome_render_frame,
144 int last_committed_nav_entry_unique_id,
145 chrome::mojom::WebPageMetadataPtr web_page_metadata) {
146 if (ShouldStopRetrieval())
147 return;
148
149 DCHECK(default_web_application_info_);
150
151 content::WebContents* contents = web_contents();
152 Observe(nullptr);
153
154 std::unique_ptr<WebApplicationInfo> info;
155
156 content::NavigationEntry* entry =
157 contents->GetController().GetLastCommittedEntry();
158
159 if (entry) {
160 if (entry->GetUniqueID() == last_committed_nav_entry_unique_id) {
161 info = std::make_unique<WebApplicationInfo>(*web_page_metadata);
162 if (info->start_url.is_empty())
163 info->start_url = std::move(default_web_application_info_->start_url);
164 if (info->title.empty())
165 info->title = std::move(default_web_application_info_->title);
166 } else {
167 // WebContents navigation state changed during the call. Ignore the mojo
168 // request result. Use default initial info instead.
169 info = std::move(default_web_application_info_);
170 }
171 }
172
173 default_web_application_info_.reset();
174
175 std::move(get_web_app_info_callback_).Run(std::move(info));
176 }
177
OnDidPerformInstallableCheck(const InstallableData & data)178 void WebAppDataRetriever::OnDidPerformInstallableCheck(
179 const InstallableData& data) {
180 if (ShouldStopRetrieval())
181 return;
182
183 Observe(nullptr);
184
185 DCHECK(data.manifest);
186 DCHECK(data.manifest_url.is_valid() || data.manifest->IsEmpty());
187
188 const bool is_installable = data.errors.empty();
189 DCHECK(!is_installable || data.valid_manifest);
190 base::Optional<blink::Manifest> opt_manifest;
191 if (!data.manifest->IsEmpty())
192 opt_manifest = *data.manifest;
193
194 std::move(check_installability_callback_)
195 .Run(std::move(opt_manifest), data.valid_manifest, is_installable);
196 }
197
OnIconsDownloaded(bool success,IconsMap icons_map)198 void WebAppDataRetriever::OnIconsDownloaded(bool success, IconsMap icons_map) {
199 if (ShouldStopRetrieval())
200 return;
201
202 Observe(nullptr);
203 icon_downloader_.reset();
204 std::move(get_icons_callback_).Run(std::move(icons_map));
205 }
206
CallCallbackOnError()207 void WebAppDataRetriever::CallCallbackOnError() {
208 Observe(nullptr);
209 DCHECK(ShouldStopRetrieval());
210
211 default_web_application_info_.reset();
212
213 // Call a callback as a tail call. The callback may destroy |this|.
214 if (get_web_app_info_callback_) {
215 std::move(get_web_app_info_callback_).Run(nullptr);
216 } else if (check_installability_callback_) {
217 std::move(check_installability_callback_)
218 .Run(base::nullopt, /*valid_manifest_for_web_app=*/false,
219 /*is_installable=*/false);
220 } else if (get_icons_callback_) {
221 std::move(get_icons_callback_).Run(IconsMap{});
222 }
223 }
224
ShouldStopRetrieval() const225 bool WebAppDataRetriever::ShouldStopRetrieval() const {
226 return !web_contents();
227 }
228
229 } // namespace web_app
230