blob: 5eb2feb4f35b38c246ca36c055c4664a7a8456ee [file] [log] [blame]
// Copyright 2018 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/web_applications/web_app_tab_helper.h"
#include <memory>
#include <string>
#include "base/unguessable_token.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/components/os_integration_manager.h"
#include "chrome/browser/web_applications/components/web_app_audio_focus_id_map.h"
#include "chrome/browser/web_applications/components/web_app_ui_manager.h"
#include "chrome/browser/web_applications/manifest_update_manager.h"
#include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
#include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "content/public/browser/media_session.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/site_instance.h"
namespace web_app {
void WebAppTabHelper::CreateForWebContents(content::WebContents* contents) {
DCHECK(contents);
if (!FromWebContents(contents)) {
contents->SetUserData(UserDataKey(),
std::make_unique<WebAppTabHelper>(contents));
}
}
WebAppTabHelper::WebAppTabHelper(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
provider_(WebAppProvider::Get(
Profile::FromBrowserContext(web_contents->GetBrowserContext()))) {
DCHECK(provider_);
observation_.Observe(&provider_->registrar());
SetAppId(
FindAppIdWithUrlInScope(web_contents->GetSiteInstance()->GetSiteURL()));
}
WebAppTabHelper::~WebAppTabHelper() = default;
const AppId& WebAppTabHelper::GetAppId() const {
return app_id_;
}
const base::UnguessableToken& WebAppTabHelper::GetAudioFocusGroupIdForTesting()
const {
return audio_focus_group_id_;
}
bool WebAppTabHelper::HasLoadedNonAboutBlankPage() const {
return has_loaded_non_about_blank_page_;
}
void WebAppTabHelper::SetAppId(const AppId& app_id) {
DCHECK(app_id.empty() || provider_->registrar().IsInstalled(app_id));
if (app_id_ == app_id)
return;
AppId previous_app_id = app_id_;
app_id_ = app_id;
OnAssociatedAppChanged(previous_app_id, app_id_);
}
void WebAppTabHelper::ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) {
// TODO(https://crbug.com/1218946): With MPArch there may be multiple main
// frames. This caller was converted automatically to the primary main frame
// to preserve its semantics. Follow up to confirm correctness.
if (navigation_handle->IsInPrimaryMainFrame()) {
const GURL& url = navigation_handle->GetURL();
const AppId app_id = FindAppIdWithUrlInScope(url);
SetAppId(app_id);
}
// If navigating to a System Web App (including navigation in sub frames), let
// SystemWebAppManager perform tab-secific setup for navigations in System Web
// Apps.
if (provider_->system_web_app_manager().IsSystemWebApp(GetAppId())) {
provider_->system_web_app_manager().OnReadyToCommitNavigation(
GetAppId(), navigation_handle);
}
}
void WebAppTabHelper::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
// TODO(https://crbug.com/1218946): With MPArch there may be multiple main
// frames. This caller was converted automatically to the primary main frame
// to preserve its semantics. Follow up to confirm correctness.
if (!navigation_handle->IsInPrimaryMainFrame() ||
!navigation_handle->HasCommitted())
return;
if (!navigation_handle->GetURL().IsAboutBlank())
has_loaded_non_about_blank_page_ = true;
is_error_page_ = navigation_handle->IsErrorPage();
provider_->manifest_update_manager().MaybeUpdate(navigation_handle->GetURL(),
GetAppId(), web_contents());
ReinstallPlaceholderAppIfNecessary(navigation_handle->GetURL());
}
void WebAppTabHelper::DOMContentLoaded(
content::RenderFrameHost* render_frame_host) {
if (render_frame_host != web_contents()->GetMainFrame())
return;
// Don't try and update the expiry time if this is an error page.
if (is_error_page_)
return;
// Don't try and manage file handlers unless this page is for an installed
// app.
if (app_id_.empty())
return;
// There is no way to reliably know if |app_id_| is for a System Web App
// during startup, so we always call MaybeUpdateFileHandlingOriginTrialExpiry.
provider_->os_integration_manager().MaybeUpdateFileHandlingOriginTrialExpiry(
web_contents(), app_id_);
}
void WebAppTabHelper::DidCloneToNewWebContents(
content::WebContents* old_web_contents,
content::WebContents* new_web_contents) {
// When the WebContents that this is attached to is cloned, give the new clone
// a WebAppTabHelper.
CreateForWebContents(new_web_contents);
auto* new_tab_helper = FromWebContents(new_web_contents);
// Clone common state:
new_tab_helper->SetAppId(GetAppId());
}
bool WebAppTabHelper::IsInAppWindow() const {
return provider_->ui_manager().IsInAppWindow(web_contents());
}
void WebAppTabHelper::OnWebAppInstalled(const AppId& installed_app_id) {
// Check if current web_contents url is in scope for the newly installed app.
AppId app_id = FindAppIdWithUrlInScope(web_contents()->GetURL());
if (app_id != installed_app_id)
return;
SetAppId(app_id);
// TODO(crbug.com/1053371): Clean up where we install file handlers.
provider_->os_integration_manager().MaybeUpdateFileHandlingOriginTrialExpiry(
web_contents(), installed_app_id);
}
void WebAppTabHelper::OnWebAppWillBeUninstalled(
const AppId& uninstalled_app_id) {
if (GetAppId() == uninstalled_app_id)
ResetAppId();
}
void WebAppTabHelper::OnAppRegistrarShutdown() {
ResetAppId();
}
void WebAppTabHelper::OnAppRegistrarDestroyed() {
observation_.Reset();
}
void WebAppTabHelper::ResetAppId() {
if (app_id_.empty())
return;
AppId previous_app_id = app_id_;
app_id_.clear();
OnAssociatedAppChanged(previous_app_id, app_id_);
}
void WebAppTabHelper::OnAssociatedAppChanged(const AppId& previous_app_id,
const AppId& new_app_id) {
provider_->ui_manager().NotifyOnAssociatedAppChanged(
web_contents(), previous_app_id, new_app_id);
UpdateAudioFocusGroupId();
}
void WebAppTabHelper::UpdateAudioFocusGroupId() {
if (!app_id_.empty() && IsInAppWindow()) {
audio_focus_group_id_ =
provider_->audio_focus_id_map().CreateOrGetIdForApp(app_id_);
} else {
audio_focus_group_id_ = base::UnguessableToken::Null();
}
content::MediaSession::Get(web_contents())
->SetAudioFocusGroupId(audio_focus_group_id_);
}
void WebAppTabHelper::ReinstallPlaceholderAppIfNecessary(const GURL& url) {
provider_->policy_manager().ReinstallPlaceholderAppIfNecessary(url);
}
AppId WebAppTabHelper::FindAppIdWithUrlInScope(const GURL& url) const {
return provider_->registrar().FindAppWithUrlInScope(url).value_or(AppId());
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(WebAppTabHelper)
} // namespace web_app