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/web_app_tab_helper.h"
6 
7 #include <memory>
8 #include <string>
9 
10 #include "base/unguessable_token.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/web_applications/components/os_integration_manager.h"
13 #include "chrome/browser/web_applications/components/policy/web_app_policy_manager.h"
14 #include "chrome/browser/web_applications/components/web_app_audio_focus_id_map.h"
15 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
16 #include "chrome/browser/web_applications/components/web_app_ui_manager.h"
17 #include "chrome/browser/web_applications/manifest_update_manager.h"
18 #include "chrome/browser/web_applications/system_web_app_manager.h"
19 #include "content/public/browser/media_session.h"
20 #include "content/public/browser/navigation_handle.h"
21 #include "content/public/browser/site_instance.h"
22 
23 namespace web_app {
24 
CreateForWebContents(content::WebContents * contents)25 void WebAppTabHelper::CreateForWebContents(content::WebContents* contents) {
26   DCHECK(contents);
27   if (!FromWebContents(contents)) {
28     contents->SetUserData(UserDataKey(),
29                           std::make_unique<WebAppTabHelper>(contents));
30   }
31 }
32 
WebAppTabHelper(content::WebContents * web_contents)33 WebAppTabHelper::WebAppTabHelper(content::WebContents* web_contents)
34     : content::WebContentsObserver(web_contents),
35       provider_(WebAppProviderBase::GetProviderBase(
36           Profile::FromBrowserContext(web_contents->GetBrowserContext()))) {
37   DCHECK(provider_);
38   observer_.Add(&provider_->registrar());
39   SetAppId(
40       FindAppIdWithUrlInScope(web_contents->GetSiteInstance()->GetSiteURL()));
41 }
42 
43 WebAppTabHelper::~WebAppTabHelper() = default;
44 
GetAppId() const45 const AppId& WebAppTabHelper::GetAppId() const {
46   return app_id_;
47 }
48 
GetAudioFocusGroupIdForTesting() const49 const base::UnguessableToken& WebAppTabHelper::GetAudioFocusGroupIdForTesting()
50     const {
51   return audio_focus_group_id_;
52 }
53 
HasLoadedNonAboutBlankPage() const54 bool WebAppTabHelper::HasLoadedNonAboutBlankPage() const {
55   return has_loaded_non_about_blank_page_;
56 }
57 
SetAppId(const AppId & app_id)58 void WebAppTabHelper::SetAppId(const AppId& app_id) {
59   DCHECK(app_id.empty() || provider_->registrar().IsInstalled(app_id));
60   if (app_id_ == app_id)
61     return;
62 
63   AppId previous_app_id = app_id_;
64   app_id_ = app_id;
65 
66   OnAssociatedAppChanged(previous_app_id, app_id_);
67 }
68 
ReadyToCommitNavigation(content::NavigationHandle * navigation_handle)69 void WebAppTabHelper::ReadyToCommitNavigation(
70     content::NavigationHandle* navigation_handle) {
71   if (navigation_handle->IsInMainFrame()) {
72     const GURL& url = navigation_handle->GetURL();
73     const AppId app_id = FindAppIdWithUrlInScope(url);
74     SetAppId(app_id);
75   }
76 
77   // If navigating to a System Web App (including navigation in sub frames), let
78   // SystemWebAppManager perform tab-secific setup for navigations in System Web
79   // Apps.
80   if (provider_->system_web_app_manager().IsSystemWebApp(GetAppId())) {
81     provider_->system_web_app_manager().OnReadyToCommitNavigation(
82         GetAppId(), navigation_handle);
83   }
84 }
85 
DidFinishNavigation(content::NavigationHandle * navigation_handle)86 void WebAppTabHelper::DidFinishNavigation(
87     content::NavigationHandle* navigation_handle) {
88   if (!navigation_handle->IsInMainFrame() || !navigation_handle->HasCommitted())
89     return;
90 
91   if (!navigation_handle->GetURL().IsAboutBlank())
92     has_loaded_non_about_blank_page_ = true;
93 
94   is_error_page_ = navigation_handle->IsErrorPage();
95 
96   provider_->manifest_update_manager().MaybeUpdate(navigation_handle->GetURL(),
97                                                    GetAppId(), web_contents());
98 
99   ReinstallPlaceholderAppIfNecessary(navigation_handle->GetURL());
100 }
101 
DOMContentLoaded(content::RenderFrameHost * render_frame_host)102 void WebAppTabHelper::DOMContentLoaded(
103     content::RenderFrameHost* render_frame_host) {
104   if (render_frame_host != web_contents()->GetMainFrame())
105     return;
106 
107   // Don't try and update the expiry time if this is an error page.
108   if (is_error_page_)
109     return;
110 
111   // Don't try and manage file handlers unless this page is for an installed
112   // app.
113   if (app_id_.empty())
114     return;
115 
116   // There is no way to reliably know if |app_id_| is for a System Web App
117   // during startup, so we always call MaybeUpdateFileHandlingOriginTrialExpiry.
118   provider_->os_integration_manager().MaybeUpdateFileHandlingOriginTrialExpiry(
119       web_contents(), app_id_);
120 }
121 
DidCloneToNewWebContents(content::WebContents * old_web_contents,content::WebContents * new_web_contents)122 void WebAppTabHelper::DidCloneToNewWebContents(
123     content::WebContents* old_web_contents,
124     content::WebContents* new_web_contents) {
125   // When the WebContents that this is attached to is cloned, give the new clone
126   // a WebAppTabHelper.
127   CreateForWebContents(new_web_contents);
128   auto* new_tab_helper = FromWebContents(new_web_contents);
129 
130   // Clone common state:
131   new_tab_helper->SetAppId(GetAppId());
132 }
133 
IsInAppWindow() const134 bool WebAppTabHelper::IsInAppWindow() const {
135   return provider_->ui_manager().IsInAppWindow(web_contents());
136 }
137 
OnWebAppInstalled(const AppId & installed_app_id)138 void WebAppTabHelper::OnWebAppInstalled(const AppId& installed_app_id) {
139   // Check if current web_contents url is in scope for the newly installed app.
140   AppId app_id = FindAppIdWithUrlInScope(web_contents()->GetURL());
141   if (app_id != installed_app_id)
142     return;
143 
144   SetAppId(app_id);
145 
146   // TODO(crbug.com/1053371): Clean up where we install file handlers.
147   provider_->os_integration_manager().MaybeUpdateFileHandlingOriginTrialExpiry(
148       web_contents(), installed_app_id);
149 }
150 
OnWebAppUninstalled(const AppId & uninstalled_app_id)151 void WebAppTabHelper::OnWebAppUninstalled(const AppId& uninstalled_app_id) {
152   if (GetAppId() == uninstalled_app_id)
153     ResetAppId();
154 }
155 
OnAppRegistrarShutdown()156 void WebAppTabHelper::OnAppRegistrarShutdown() {
157   ResetAppId();
158 }
159 
OnAppRegistrarDestroyed()160 void WebAppTabHelper::OnAppRegistrarDestroyed() {
161   observer_.RemoveAll();
162 }
163 
ResetAppId()164 void WebAppTabHelper::ResetAppId() {
165   if (app_id_.empty())
166     return;
167 
168   AppId previous_app_id = app_id_;
169   app_id_.clear();
170 
171   OnAssociatedAppChanged(previous_app_id, app_id_);
172 }
173 
OnAssociatedAppChanged(const AppId & previous_app_id,const AppId & new_app_id)174 void WebAppTabHelper::OnAssociatedAppChanged(const AppId& previous_app_id,
175                                              const AppId& new_app_id) {
176   provider_->ui_manager().NotifyOnAssociatedAppChanged(
177       web_contents(), previous_app_id, new_app_id);
178   UpdateAudioFocusGroupId();
179 }
180 
UpdateAudioFocusGroupId()181 void WebAppTabHelper::UpdateAudioFocusGroupId() {
182   if (!app_id_.empty() && IsInAppWindow()) {
183     audio_focus_group_id_ =
184         provider_->audio_focus_id_map().CreateOrGetIdForApp(app_id_);
185   } else {
186     audio_focus_group_id_ = base::UnguessableToken::Null();
187   }
188 
189   content::MediaSession::Get(web_contents())
190       ->SetAudioFocusGroupId(audio_focus_group_id_);
191 }
192 
ReinstallPlaceholderAppIfNecessary(const GURL & url)193 void WebAppTabHelper::ReinstallPlaceholderAppIfNecessary(const GURL& url) {
194   provider_->policy_manager().ReinstallPlaceholderAppIfNecessary(url);
195 }
196 
FindAppIdWithUrlInScope(const GURL & url) const197 AppId WebAppTabHelper::FindAppIdWithUrlInScope(const GURL& url) const {
198   return provider_->registrar().FindAppWithUrlInScope(url).value_or(AppId());
199 }
200 
201 }  // namespace web_app
202