1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_TAB_HELPER_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_TAB_HELPER_H_
#include <optional>
#include <vector>
#include "base/callback_list.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/unguessable_token.h"
#include "chrome/browser/web_applications/web_app_install_manager.h"
#include "chrome/browser/web_applications/web_app_install_manager_observer.h"
#include "components/tabs/public/tab_interface.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
namespace content {
class WebContents;
}
namespace webapps {
class LaunchQueue;
}
namespace web_app {
class WebAppProvider;
// Per-tab web app helper. Allows to associate a tab (web page) with a web app.
class WebAppTabHelper : public content::WebContentsUserData<WebAppTabHelper>,
public content::WebContentsObserver,
public WebAppInstallManagerObserver {
public:
// `contents` can be different from `tab->GetContents()` during tab discard.
// TODO(https://crbug.com/347770670): This method can be simplified to not
// take `contents`.
static void Create(tabs::TabInterface* tab, content::WebContents* contents);
// Retrieves the WebAppTabHelper's app ID off |web_contents|, returns
// nullptr if there is no tab helper or app ID.
static const webapps::AppId* GetAppId(
const content::WebContents* web_contents);
#if BUILDFLAG(IS_MAC)
// Like the above method, but also checks if notification attribution should
// apply to the app in the web contents. This checks the base::Feature as well
// as makes sure the app is installed.
static std::optional<webapps::AppId> GetAppIdForNotificationAttribution(
content::WebContents* web_contents);
#endif
WebAppTabHelper(tabs::TabInterface* tab, content::WebContents* contents);
WebAppTabHelper(const WebAppTabHelper&) = delete;
WebAppTabHelper& operator=(const WebAppTabHelper&) = delete;
~WebAppTabHelper() override;
// Sets the app id for this web contents. Ideally the app id would always be
// equal to the id of whatever app the last committed primary main frame URL
// is in scope for (and WebAppTabHelper resets it to that any time a
// navigation commits), but for legacy reasons sometimes the app id is set
// explicitly from elsewhere.
void SetAppId(std::optional<webapps::AppId> app_id);
// Called by `WebAppBrowserController` and `WebKioskBrowserControllerBase`'s
// `OnTabInserted` and `OnTabRemoved` methods to indicate if this web contents
// is currently being displayed inside an app window. `window_app_id` is the
// id of the app.
void SetIsInAppWindow(std::optional<webapps::AppId> window_app_id);
void SetCallbackToRunOnTabChanges(base::OnceClosure callback);
// Used to listen to the tab entering the background via the `TabInterface`.
void OnTabBackgrounded(tabs::TabInterface* tab_interface);
// Used to listen to the tab being detached from the tab strip via the
// `TabInterface`. The tab will either be destroyed, or is in the middle of
// being put in a different window.
void OnTabDetached(tabs::TabInterface* tab_interface,
tabs::TabInterface::DetachReason detach_reason);
const base::UnguessableToken& GetAudioFocusGroupIdForTesting() const;
// Returns the installed web app that 'controls' the last committed url of
// this tab. This is populated for this tab no matter where it is, whether in
// a browser window, or in a standalone app window.
// - 'controls' means it's the web app who's scope contains the last committed
// url. If there are multiple web apps that satisfy this constraint, then
// it chooses the one with the longest (aka most specific) scope prefix.
// - 'installed' means the web app is
// InstallState::INSTALLED_WITH_OS_INTEGRATION or
// InstallState::INSTALLED_WITHOUT_OS_INTEGRATION (which is usually only
// preinstalled apps). And thus this excludes the
// InstallState::SUGGESTED_FROM_ANOTHER_DEVICE state.
//
// Note: This is populated on construction from the current tab's
// `GetLastCommittedURL()`, and afterwards only after navigation is committed.
//
// Note: If we are in an app window, this is not guaranteed to match
// `window_app_id()` - for example, if the web contents of an app navigates
// out of scope of the app, this will be std::nullopt.
const std::optional<webapps::AppId>& app_id() const { return app_id_; }
// Returns the installed web app window that contains this tab, or
// std::nullopt if this tab is in a normal browser window. This is not
// guaranteed to match `app_id()`, because app windows can display content
// that is out of scope of the app (and even in scope of another app).
const std::optional<webapps::AppId>& window_app_id() const {
return window_app_id_;
}
// True when this web contents is currently being displayed inside an app
// window instead of in a browser tab.
bool is_in_app_window() const { return window_app_id_.has_value(); }
bool is_pinned_home_tab() const { return is_pinned_home_tab_; }
void set_is_pinned_home_tab(bool is_pinned_home_tab) {
is_pinned_home_tab_ = is_pinned_home_tab;
}
webapps::LaunchQueue& EnsureLaunchQueue();
// content::WebContentsObserver:
void ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) override;
void PrimaryPageChanged(content::Page& page) override;
void DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) override;
// Because the launch queue is communicated via a dedicated mojo pipe,
// ordering can be tricky in tests. This method allows tests to call
// `FlushForTesting()` on the launch queue mojo connection to ensure that all
// launch queue messages have been sent to the renderer.
void FlushLaunchQueueForTesting() const;
// Returns if the current web contents can be used for the 'focus-existing'
// behavior of navigation capturing, where the tab is focused and a
// 'LaunchParams' is given to a javascript 'launch consumer' on the page. This
// returns if the current page can feasibly run javascript to actually set
// this launch consumer, as without that, any captured links would simply do
// nothing.
// Specifically, this turns `true` if the current page's mime-type is html or
// xhtml.
bool CanBeUsedForFocusExisting() const;
private:
friend class WebAppAudioFocusBrowserTest;
friend class content::WebContentsUserData<WebAppTabHelper>;
// WebAppInstallManagerObserver:
void OnWebAppInstalled(const webapps::AppId& installed_app_id) override;
void OnWebAppWillBeUninstalled(
const webapps::AppId& uninstalled_app_id) override;
void OnWebAppInstallManagerDestroyed() override;
void ResetTabSubscriptions(tabs::TabInterface* tab);
// Sets the state of this tab helper. This will call
// `WebAppUiManager::OnAssociatedAppChanged` if the id has changed, and
// `UpdateAudioFocusGroupId()` if either has changed.
void SetState(std::optional<webapps::AppId> app_id,
std::optional<webapps::AppId> window_app_id);
// Runs any logic when the associated app is added, changed or removed.
void OnAssociatedAppChanged(
const std::optional<webapps::AppId>& previous_app_id,
const std::optional<webapps::AppId>& new_app_id);
// Updates the audio focus group id based on the current web app.
void UpdateAudioFocusGroupId();
// Triggers a reinstall of a placeholder app for |url|.
void ReinstallPlaceholderAppIfNecessary(const GURL& url);
// When a `TabInterface` is updated on being detached and attached to a new
// window, update the subscriptions as needed.
void SubscribeToTabState(tabs::TabInterface* tab_interface);
// Asynchronously run `on_tab_details_changed_callback_` after tab states have
// changed.
void MaybeNotifyTabChanged();
// Cache the information that an app launch has happened and a WebFeature use
// counter needs to be measured. Trigger measurement which may or may not
// happen depending on whether page load has finished.
void ScheduleManifestAppliedUseCounter();
// Record the `UseCounter` for an app launch after page loading has
// completed. Resets all flags post `UseCounter` measurement so that this
// happens only once.
void MaybeRecordManifestAppliedUseCounter();
std::optional<webapps::AppId> app_id_;
std::optional<webapps::AppId> window_app_id_;
// True when this tab is the pinned home tab of a tabbed web app.
bool is_pinned_home_tab_ = false;
// The audio focus group id is used to group media sessions together for apps.
// We store the applied group id locally on the helper for testing.
base::UnguessableToken audio_focus_group_id_ = base::UnguessableToken::Null();
// Use unique_ptr for lazy instantiation as most browser tabs have no need to
// incur this memory overhead.
std::unique_ptr<webapps::LaunchQueue> launch_queue_;
// A callback that runs whenever the `tab` is destroyed, navigates or goes to
// the background.
base::OnceClosure on_tab_details_changed_callback_;
// Used to subscribe to various changes happening in the current tab from the
// `TabInterface`.
std::vector<base::CallbackListSubscription> tab_subscriptions_;
// Listen to whether page load has completed in the web contents to measure
// the `UseCounter` of launching a web app.
bool can_record_manifest_applied_ = false;
// Cache the information that an app launch `UseCounter` needs to be measured.
bool meaure_manifest_applied_use_counter_ = false;
base::ScopedObservation<WebAppInstallManager, WebAppInstallManagerObserver>
observation_{this};
raw_ptr<WebAppProvider> provider_ = nullptr;
base::WeakPtrFactory<WebAppTabHelper> weak_factory_{this};
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
} // namespace web_app
#endif // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_TAB_HELPER_H_
|