feat: support chrome.tabs.onZoomChanged

This commit is contained in:
Shelley Vohr
2023-08-04 23:18:57 +02:00
parent 42391fa4af
commit 36ce495886
9 changed files with 336 additions and 5 deletions

View File

@@ -686,6 +686,10 @@ filenames = {
"shell/browser/extensions/api/streams_private/streams_private_api.h",
"shell/browser/extensions/api/tabs/tabs_api.cc",
"shell/browser/extensions/api/tabs/tabs_api.h",
"shell/browser/extensions/api/tabs/tabs_event_router.cc",
"shell/browser/extensions/api/tabs/tabs_event_router.h",
"shell/browser/extensions/api/tabs/tabs_window_api.cc",
"shell/browser/extensions/api/tabs/tabs_window_api.h",
"shell/browser/extensions/electron_browser_context_keyed_service_factories.cc",
"shell/browser/extensions/electron_browser_context_keyed_service_factories.h",
"shell/browser/extensions/electron_component_extension_resource_manager.cc",

View File

@@ -164,6 +164,8 @@
#include "extensions/browser/script_executor.h"
#include "extensions/browser/view_type_utils.h"
#include "extensions/common/mojom/view_type.mojom.h"
#include "shell/browser/extensions/api/tabs/tabs_event_router.h"
#include "shell/browser/extensions/api/tabs/tabs_window_api.h"
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
#endif
@@ -893,6 +895,14 @@ void WebContents::InitZoomController(content::WebContents* web_contents,
if (options.Get(options::kZoomFactor, &zoom_factor))
zoom_controller_->SetDefaultZoomFactor(zoom_factor);
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
auto* tabs_window_api = extensions::TabsWindowsAPI::Get(GetBrowserContext());
if (tabs_window_api) {
tabs_window_api->tabs_event_router()->RegisterForTabNotifications(
web_contents);
}
#endif
// Nothing to do with ZoomController, but this function gets called in all
// init cases!
content::RenderViewHost* host = web_contents->GetRenderViewHost();

View File

@@ -0,0 +1,126 @@
// Copyright (c) 2023 Microsoft, GmbH
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/extensions/api/tabs/tabs_event_router.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "chrome/browser/browser_process.h"
#include "components/zoom/zoom_controller.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "extensions/common/features/feature.h"
#include "extensions/common/mojom/event_dispatcher.mojom-forward.h"
#include "shell/browser/api/electron_api_web_contents.h"
#include "shell/browser/extensions/api/tabs/tabs_window_api.h"
#include "shell/browser/web_contents_zoom_controller.h"
#include "shell/common/extensions/api/tabs.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/page/page_zoom.h"
using base::Value;
using content::WebContents;
using zoom::ZoomController;
using electron::WebContentsZoomController;
namespace extensions {
// namespace {
// void ZoomModeToZoomSettings(ZoomController::ZoomMode zoom_mode,
// api::tabs::ZoomSettings* zoom_settings) {
// DCHECK(zoom_settings);
// switch (zoom_mode) {
// case ZoomController::ZOOM_MODE_DEFAULT:
// zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC;
// zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN;
// break;
// case ZoomController::ZOOM_MODE_ISOLATED:
// zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC;
// zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
// break;
// case ZoomController::ZOOM_MODE_MANUAL:
// zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_MANUAL;
// zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
// break;
// case ZoomController::ZOOM_MODE_DISABLED:
// zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_DISABLED;
// zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB;
// break;
// }
// }
// } // namespace
TabsEventRouter::TabsEventRouter(content::BrowserContext* context)
: context_(context) {}
TabsEventRouter::~TabsEventRouter() = default;
void TabsEventRouter::OnZoomControllerDestroyed(
WebContentsZoomController* zoom_controller) {
if (zoom_scoped_observations_.IsObservingSource(zoom_controller)) {
zoom_scoped_observations_.RemoveObservation(zoom_controller);
}
}
void TabsEventRouter::OnZoomChanged(
const electron::WebContentsZoomController::ZoomChangedEventData& data) {
DCHECK(web_contents);
auto* api_web_contents = electron::api::WebContents::From(data.web_contents);
int tab_id = api_web_contents ? api_web_contents->ID() : -1;
if (tab_id < 0)
return;
// Prepare the zoom change information.
api::tabs::OnZoomChange::ZoomChangeInfo zoom_change_info;
zoom_change_info.tab_id = tab_id;
zoom_change_info.old_zoom_factor =
blink::PageZoomLevelToZoomFactor(data.old_zoom_level);
zoom_change_info.new_zoom_factor =
blink::PageZoomLevelToZoomFactor(data.new_zoom_level);
// ZoomModeToZoomSettings(data.zoom_mode, &zoom_change_info.zoom_settings);
// Dispatch the |onZoomChange| event.
DispatchEvent(data.web_contents->GetBrowserContext(),
events::TABS_ON_ZOOM_CHANGE,
api::tabs::OnZoomChange::kEventName,
api::tabs::OnZoomChange::Create(zoom_change_info),
EventRouter::USER_GESTURE_UNKNOWN);
}
void TabsEventRouter::DispatchEvent(
content::BrowserContext* context,
events::HistogramValue histogram_value,
const std::string& event_name,
base::Value::List args,
EventRouter::UserGestureState user_gesture) {
EventRouter* event_router = EventRouter::Get(context);
if (!event_router)
return;
auto event = std::make_unique<Event>(histogram_value, event_name,
std::move(args), context);
event->user_gesture = user_gesture;
event_router->BroadcastEvent(std::move(event));
}
void TabsEventRouter::RegisterForTabNotifications(WebContents* contents) {
zoom_scoped_observations_.AddObservation(
WebContentsZoomController::FromWebContents(contents));
}
void TabsEventRouter::UnregisterForTabNotifications(WebContents* contents) {
if (auto* zoom_controller =
WebContentsZoomController::FromWebContents(contents);
zoom_scoped_observations_.IsObservingSource(zoom_controller)) {
zoom_scoped_observations_.RemoveObservation(zoom_controller);
}
}
} // namespace extensions

View File

@@ -0,0 +1,74 @@
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_EVENT_ROUTER_H_
#define SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_EVENT_ROUTER_H_
#include <map>
#include <set>
#include <string>
#include "base/memory/raw_ptr.h"
#include "base/scoped_multi_source_observation.h"
#include "base/scoped_observation.h"
#include "content/public/browser/web_contents_observer.h"
#include "extensions/browser/event_router.h"
#include "shell/browser/extensions/api/tabs/tabs_api.h"
#include "shell/browser/web_contents_zoom_controller.h"
#include "shell/browser/web_contents_zoom_observer.h"
namespace content {
class WebContents;
}
namespace extensions {
// The TabsEventRouter listens to tab events and routes them to listeners inside
// extension process renderers.
// TabsEventRouter will only route events from windows/tabs within a context to
// extension processes in the same context.
class TabsEventRouter : public electron::WebContentsZoomObserver {
public:
explicit TabsEventRouter(content::BrowserContext* context);
TabsEventRouter(const TabsEventRouter&) = delete;
TabsEventRouter& operator=(const TabsEventRouter&) = delete;
~TabsEventRouter() override;
// WebContentsZoomController::Observer
void OnZoomChanged(
const electron::WebContentsZoomController::ZoomChangedEventData& data)
override;
void OnZoomControllerDestroyed(
electron::WebContentsZoomController* controller) override;
// Register ourselves to receive the various notifications we are interested
// in for a tab. Also create tab entry to observe web contents notifications.
void RegisterForTabNotifications(content::WebContents* contents);
private:
// The DispatchEvent methods forward events to the |context|'s event router.
// The TabsEventRouter listens to events for all contexts,
// so we avoid duplication by dropping events destined for other contexts.
void DispatchEvent(content::BrowserContext* context,
events::HistogramValue histogram_value,
const std::string& event_name,
base::Value::List args,
EventRouter::UserGestureState user_gesture);
// Removes notifications and tab entry added in RegisterForTabNotifications.
void UnregisterForTabNotifications(content::WebContents* contents);
// The main context that owns this event router.
raw_ptr<content::BrowserContext> context_;
base::ScopedMultiSourceObservation<electron::WebContentsZoomController,
electron::WebContentsZoomObserver>
zoom_scoped_observations_{this};
};
} // namespace extensions
#endif // SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_EVENT_ROUTER_H_

View File

@@ -0,0 +1,57 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shell/browser/extensions/api/tabs/tabs_window_api.h"
#include <memory>
#include "base/lazy_instance.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_system.h"
#include "shell/browser/extensions/api/tabs/tabs_event_router.h"
#include "shell/common/extensions/api/tabs.h"
namespace extensions {
TabsWindowsAPI::TabsWindowsAPI(content::BrowserContext* context)
: browser_context_(context) {
EventRouter* event_router = EventRouter::Get(browser_context_);
// Tabs API Events.
event_router->RegisterObserver(this, api::tabs::OnZoomChange::kEventName);
}
TabsWindowsAPI::~TabsWindowsAPI() = default;
// static
TabsWindowsAPI* TabsWindowsAPI::Get(content::BrowserContext* context) {
return BrowserContextKeyedAPIFactory<TabsWindowsAPI>::Get(context);
}
TabsEventRouter* TabsWindowsAPI::tabs_event_router() {
if (!tabs_event_router_.get()) {
tabs_event_router_ = std::make_unique<TabsEventRouter>(browser_context_);
}
return tabs_event_router_.get();
}
void TabsWindowsAPI::Shutdown() {
EventRouter::Get(browser_context_)->UnregisterObserver(this);
}
static base::LazyInstance<BrowserContextKeyedAPIFactory<TabsWindowsAPI>>::
DestructorAtExit g_tabs_windows_api_factory = LAZY_INSTANCE_INITIALIZER;
BrowserContextKeyedAPIFactory<TabsWindowsAPI>*
TabsWindowsAPI::GetFactoryInstance() {
return g_tabs_windows_api_factory.Pointer();
}
void TabsWindowsAPI::OnListenerAdded(const EventListenerInfo& details) {
// Initialize the event routers.
tabs_event_router();
EventRouter::Get(browser_context_)->UnregisterObserver(this);
}
} // namespace extensions

View File

@@ -0,0 +1,53 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_WINDOWS_API_H_
#define SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_WINDOWS_API_H_
#include <memory>
#include "base/memory/raw_ptr.h"
#include "components/keyed_service/core/keyed_service.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router.h"
namespace extensions {
class TabsEventRouter;
class TabsWindowsAPI : public BrowserContextKeyedAPI,
public EventRouter::Observer {
public:
explicit TabsWindowsAPI(content::BrowserContext* context);
~TabsWindowsAPI() override;
// Convenience method to get the TabsWindowsAPI for a BrowserContext.
static TabsWindowsAPI* Get(content::BrowserContext* context);
TabsEventRouter* tabs_event_router();
// KeyedService implementation.
void Shutdown() override;
// BrowserContextKeyedAPI implementation.
static BrowserContextKeyedAPIFactory<TabsWindowsAPI>* GetFactoryInstance();
// EventRouter::Observer implementation.
void OnListenerAdded(const extensions::EventListenerInfo& details) override;
private:
friend class BrowserContextKeyedAPIFactory<TabsWindowsAPI>;
raw_ptr<content::BrowserContext> browser_context_;
// BrowserContextKeyedAPI implementation.
static const char* service_name() {
return "TabsWindowsAPI";
}
std::unique_ptr<TabsEventRouter> tabs_event_router_;
};
} // namespace extensions
#endif // SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_WINDOWS_API_H_

View File

@@ -5,6 +5,7 @@
#include "shell/browser/extensions/electron_browser_context_keyed_service_factories.h"
#include "extensions/browser/updater/update_service_factory.h"
#include "shell/browser/extensions/api/tabs/tabs_window_api.h"
#include "shell/browser/extensions/electron_extension_system_factory.h"
namespace extensions::electron {
@@ -14,6 +15,8 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() {
// extensions embedders (and namely chrome.)
UpdateServiceFactory::GetInstance();
TabsWindowsAPI::GetFactoryInstance();
ElectronExtensionSystemFactory::GetInstance();
}

View File

@@ -878,7 +878,12 @@ describe('chrome extensions', () => {
const [,, responseString] = await once(w.webContents, 'console-message');
const response = JSON.parse(responseString);
expect(response).to.deep.equal(2);
expect(response).to.deep.equal({
newZoomFactor: 2,
oldZoomFactor: 1.2,
tabId: 1,
zoomSettings: {}
});
});
it('getZoomSettings', async () => {

View File

@@ -12,10 +12,9 @@ const handleRequest = (request, sender, sendResponse) => {
case 'setZoom': {
const [zoom] = args;
chrome.tabs.setZoom(tabId, zoom).then(async () => {
const updatedZoom = await chrome.tabs.getZoom(tabId);
sendResponse(updatedZoom);
});
chrome.tabs.onZoomChange.addListener(sendResponse);
chrome.tabs.setZoom(tabId, zoom);
break;
}