blob: 1a8f521d273354d0a7ee7aec15b0e7f4fb867743 [file] [log] [blame]
// Copyright 2017 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/ui/ash/tablet_mode_client.h"
#include <utility>
#include "ash/public/cpp/tablet_mode.h"
#include "ash/public/interfaces/constants.mojom.h"
#include "base/bind.h"
#include "chrome/browser/ui/ash/tablet_mode_client_observer.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_tab_strip_tracker.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/service_manager_connection.h"
#include "services/service_manager/public/cpp/connector.h"
#include "ui/base/material_design/material_design_controller.h"
#include "ui/base/ui_base_features.h"
namespace {
TabletModeClient* g_tablet_mode_client_instance = nullptr;
} // namespace
TabletModeClient::TabletModeClient() : binding_(this) {
DCHECK(!g_tablet_mode_client_instance);
g_tablet_mode_client_instance = this;
if (features::IsMultiProcessMash()) {
ash::TabletMode::SetCallback(base::BindRepeating(
&TabletModeClient::tablet_mode_enabled, base::Unretained(this)));
}
}
TabletModeClient::~TabletModeClient() {
DCHECK_EQ(this, g_tablet_mode_client_instance);
g_tablet_mode_client_instance = nullptr;
if (features::IsMultiProcessMash())
ash::TabletMode::SetCallback({});
}
void TabletModeClient::Init() {
content::ServiceManagerConnection::GetForProcess()
->GetConnector()
->BindInterface(ash::mojom::kServiceName, &tablet_mode_controller_);
BindAndSetClient();
}
void TabletModeClient::InitForTesting(
ash::mojom::TabletModeControllerPtr controller) {
tablet_mode_controller_ = std::move(controller);
BindAndSetClient();
}
// static
TabletModeClient* TabletModeClient::Get() {
return g_tablet_mode_client_instance;
}
void TabletModeClient::SetTabletModeEnabledForTesting(
bool enabled,
ash::mojom::TabletModeController::SetTabletModeEnabledForTestingCallback
callback) {
tablet_mode_controller_->SetTabletModeEnabledForTesting(enabled,
std::move(callback));
}
void TabletModeClient::AddObserver(TabletModeClientObserver* observer) {
observers_.AddObserver(observer);
}
void TabletModeClient::RemoveObserver(TabletModeClientObserver* observer) {
observers_.RemoveObserver(observer);
}
void TabletModeClient::OnTabletModeToggled(bool enabled) {
if (tablet_mode_enabled_ == enabled)
return;
tablet_mode_enabled_ = enabled;
SetMobileLikeBehaviorEnabled(enabled);
ui::MaterialDesignController::OnTabletModeToggled(enabled);
for (auto& observer : observers_)
observer.OnTabletModeToggled(enabled);
}
bool TabletModeClient::ShouldTrackBrowser(Browser* browser) {
return tablet_mode_enabled_;
}
void TabletModeClient::OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) {
if (change.type() != TabStripModelChange::kInserted)
return;
// We limit the mobile-like behavior to webcontents in tabstrips since many
// apps and extensions draw their own caption buttons and header frames. We
// don't want those to shrink down and resize to fit the width of their
// windows like webpages on mobile do. So this behavior is limited to webpages
// in tabs and packaged apps.
for (const auto& delta : change.deltas())
delta.insert.contents->NotifyPreferencesChanged();
}
void TabletModeClient::FlushForTesting() {
tablet_mode_controller_.FlushForTesting();
}
void TabletModeClient::BindAndSetClient() {
ash::mojom::TabletModeClientPtr client;
binding_.Bind(mojo::MakeRequest(&client));
tablet_mode_controller_->SetClient(std::move(client));
}
void TabletModeClient::SetMobileLikeBehaviorEnabled(bool enabled) {
// Toggling tablet mode on/off should trigger refreshing the WebKit
// preferences, since in tablet mode, we enable certain mobile-like features
// such as "double tap to zoom", "shrink page contents to fit", ... etc.
// Do this only for webpages that belong to existing browsers as well as
// future browsers and webcontents.
if (enabled) {
// On calling Init() of the |tab_strip_tracker_|, we will get a call to
// TabInsertedAt() for all the existing webcontents, upon which we will
// trigger a refresh of their WebKit preferences.
tab_strip_tracker_ =
std::make_unique<BrowserTabStripTracker>(this, this, nullptr);
tab_strip_tracker_->Init();
} else {
// Manually trigger a refresh for the existing webcontents' preferences.
for (Browser* browser : *BrowserList::GetInstance()) {
TabStripModel* tab_strip_model = browser->tab_strip_model();
for (int i = 0; i < tab_strip_model->count(); ++i) {
content::WebContents* web_contents =
tab_strip_model->GetWebContentsAt(i);
DCHECK(web_contents);
web_contents->NotifyPreferencesChanged();
}
}
tab_strip_tracker_ = nullptr;
}
}