Skip to content

Commit

Permalink
Add support for Web Extension windows events.
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=261015
rdar://problem/114806907

Reviewed by Brian Weinstein.

Implements the browser.windows.onCreated, onRemoved, and onFocusChanged events.

* Source/WebKit/UIProcess/Extensions/Cocoa/API/WebExtensionContextAPIWindowsCocoa.mm:
(WebKit::WebExtensionContext::fireWindowsEventIfNeeded):
* Source/WebKit/UIProcess/Extensions/Cocoa/WebExtensionContextCocoa.mm:
(WebKit::WebExtensionContext::didOpenWindow):
(WebKit::WebExtensionContext::didCloseWindow):
(WebKit::WebExtensionContext::didFocusWindow):
* Source/WebKit/UIProcess/Extensions/Cocoa/WebExtensionWindowCocoa.mm:
(WebKit::WebExtensionWindow::parameters const):
(WebKit::WebExtensionWindow::minimalParameters const):
* Source/WebKit/UIProcess/Extensions/WebExtensionContext.h:
* Source/WebKit/UIProcess/Extensions/WebExtensionWindow.h:
* Source/WebKit/WebProcess/Extensions/API/Cocoa/WebExtensionAPIWindowsCocoa.mm:
(WebKit::toWindowTypeFilter):
(WebKit::WebExtensionContextProxy::dispatchWindowsEvent):
* Source/WebKit/WebProcess/Extensions/WebExtensionContextProxy.h:
* Source/WebKit/WebProcess/Extensions/WebExtensionContextProxy.messages.in:
* Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebExtensionAPIWindows.mm:
(TestWebKitAPI::TEST): Added CreatedEvent, RemovedEvent, and FocusChangedEvent tests.

Canonical link: https://commits.webkit.org/267559@main
  • Loading branch information
xeenon committed Sep 1, 2023
1 parent ade70bd commit 79b9a6a
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,13 @@ static inline bool matchesFilter(const WebExtensionWindow& window, OptionSet<Web
window->close(WTFMove(completionHandler));
}

void WebExtensionContext::fireWindowsEventIfNeeded(WebExtensionEventListenerType type, std::optional<WebExtensionWindowParameters> windowParameters)
{
wakeUpBackgroundContentIfNecessaryToFireEvents({ type }, [&] {
sendToProcessesForEvent(type, Messages::WebExtensionContextProxy::DispatchWindowsEvent(type, windowParameters));
});
}

} // namespace WebKit

#endif // ENABLE(WK_WEB_EXTENSIONS)
Original file line number Diff line number Diff line change
Expand Up @@ -1307,7 +1307,7 @@ static _WKWebExtensionContextError toAPI(WebExtensionContext::Error error)
if (!isLoaded())
return;

// FIXME: Fire event here.
fireWindowsEventIfNeeded(WebExtensionEventListenerType::WindowsOnCreated, window.parameters());
}

void WebExtensionContext::didCloseWindow(const WebExtensionWindow& window)
Expand All @@ -1332,7 +1332,7 @@ static _WKWebExtensionContextError toAPI(WebExtensionContext::Error error)
if (!isLoaded())
return;

// FIXME: Fire event here.
fireWindowsEventIfNeeded(WebExtensionEventListenerType::WindowsOnRemoved, window.minimalParameters());
}

void WebExtensionContext::didFocusWindow(WebExtensionWindow* window)
Expand All @@ -1355,7 +1355,7 @@ static _WKWebExtensionContextError toAPI(WebExtensionContext::Error error)
if (!isLoaded())
return;

// FIXME: Fire event here.
fireWindowsEventIfNeeded(WebExtensionEventListenerType::WindowsOnFocusChanged, window ? std::optional(window->minimalParameters()) : std::nullopt);
}

void WebExtensionContext::didOpenTab(const WebExtensionTab& tab)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@

if (populate == PopulateTabs::Yes) {
auto tabs = this->tabs();

tabParameters.reserveInitialCapacity(tabs.size());

for (auto& tab : tabs)
Expand All @@ -95,6 +94,19 @@
};
}

WebExtensionWindowParameters WebExtensionWindow::minimalParameters() const
{
return {
identifier(),
std::nullopt,
type(),
std::nullopt,
std::nullopt,
std::nullopt,
std::nullopt
};
}

WebExtensionWindow::TabVector WebExtensionWindow::tabs() const
{
TabVector result;
Expand Down
1 change: 1 addition & 0 deletions Source/WebKit/UIProcess/Extensions/WebExtensionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ class WebExtensionContext : public API::ObjectImpl<API::Object::Type::WebExtensi
void windowsGetAll(OptionSet<WindowTypeFilter>, PopulateTabs, CompletionHandler<void(Vector<WebExtensionWindowParameters>, WebExtensionWindow::Error)>&&);
void windowsUpdate(WebExtensionWindowIdentifier, WebExtensionWindowParameters, CompletionHandler<void(std::optional<WebExtensionWindowParameters>, WebExtensionWindow::Error)>&&);
void windowsRemove(WebExtensionWindowIdentifier, CompletionHandler<void(WebExtensionWindow::Error)>&&);
void fireWindowsEventIfNeeded(WebExtensionEventListenerType, std::optional<WebExtensionWindowParameters>);

// IPC::MessageReceiver.
void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
Expand Down
1 change: 1 addition & 0 deletions Source/WebKit/UIProcess/Extensions/WebExtensionWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class WebExtensionWindow : public RefCounted<WebExtensionWindow> {

WebExtensionWindowIdentifier identifier() const { return m_identifier; }
WebExtensionWindowParameters parameters(PopulateTabs = PopulateTabs::No) const;
WebExtensionWindowParameters minimalParameters() const;

WebExtensionContext* extensionContext() const;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#import "CocoaHelpers.h"
#import "Logging.h"
#import "MessageSenderInlines.h"
#import "WebExtensionAPINamespace.h"
#import "WebExtensionAPITabs.h"
#import "WebExtensionAPIWindowsEvent.h"
#import "WebExtensionContextMessages.h"
Expand Down Expand Up @@ -562,6 +563,50 @@
return *m_onFocusChanged;
}

inline WebExtensionAPIWindows::WindowTypeFilter toWindowTypeFilter(WebExtensionWindow::Type type)
{
switch (type) {
case WebExtensionWindow::Type::Normal:
return WebExtensionAPIWindows::WindowTypeFilter::Normal;

case WebExtensionWindow::Type::Popup:
return WebExtensionAPIWindows::WindowTypeFilter::Popup;
}
}

void WebExtensionContextProxy::dispatchWindowsEvent(WebExtensionEventListenerType type, std::optional<WebExtensionWindowParameters> windowParameters)
{
auto filter = windowParameters ? toWindowTypeFilter(windowParameters.value().type.value()) : WebExtensionAPIWindows::WindowTypeFilter::All;

enumerateNamespaceObjects([&](auto& namespaceObject) {
auto& windowsObject = namespaceObject.windows();

switch (type) {
case WebExtensionEventListenerType::WindowsOnCreated:
// Documentation: https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/windows/onCreated
ASSERT(windowParameters);
windowsObject.onCreated().invokeListenersWithArgument(toWebAPI(windowParameters.value()), filter);
break;

case WebExtensionEventListenerType::WindowsOnFocusChanged:
// Documentation: https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/windows/onFocusChanged
ASSERT(!windowParameters || windowParameters.value().identifier);
windowsObject.onFocusChanged().invokeListenersWithArgument(@(toWebAPI(windowParameters ? windowParameters.value().identifier.value() : WebExtensionWindowConstants::NoneIdentifier)), filter);
break;

case WebExtensionEventListenerType::WindowsOnRemoved:
// Documentation: https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/windows/onRemoved
ASSERT(windowParameters && windowParameters.value().identifier);
windowsObject.onRemoved().invokeListenersWithArgument(@(toWebAPI(windowParameters.value().identifier.value())), filter);
break;

default:
ASSERT_NOT_REACHED();
break;
}
});
}

} // namespace WebKit

#endif // ENABLE(WK_WEB_EXTENSIONS)
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class WebExtensionAPINamespace;
class WebExtensionMatchPattern;
class WebFrame;
struct WebExtensionAlarmParameters;
struct WebExtensionWindowParameters;

class WebExtensionContextProxy final : public RefCounted<WebExtensionContextProxy>, public IPC::MessageReceiver {
WTF_MAKE_FAST_ALLOCATED;
Expand Down Expand Up @@ -93,6 +94,9 @@ class WebExtensionContextProxy final : public RefCounted<WebExtensionContextProx
// Web Navigation
void dispatchWebNavigationEvent(WebExtensionEventListenerType, WebPageProxyIdentifier, WebCore::FrameIdentifier, URL);

// Windows
void dispatchWindowsEvent(WebExtensionEventListenerType, std::optional<WebExtensionWindowParameters>);

// IPC::MessageReceiver
void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ messages -> WebExtensionContextProxy {

// Web Navigation
DispatchWebNavigationEvent(WebKit::WebExtensionEventListenerType type, WebKit::WebPageProxyIdentifier pageID, WebCore::FrameIdentifier frameID, URL targetURL)

// Windows
DispatchWindowsEvent(WebKit::WebExtensionEventListenerType type, std::optional<WebKit::WebExtensionWindowParameters> windowParameters)
}

#endif // ENABLE(WK_WEB_EXTENSIONS)
113 changes: 113 additions & 0 deletions Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebExtensionAPIWindows.mm
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,119 @@
[manager loadAndRun];
}

TEST(WKWebExtensionAPIWindows, CreatedEvent)
{
auto *backgroundScript = Util::constructScript(@[
@"browser.windows.onCreated.addListener((window) => {",

@" browser.test.assertEq(typeof window, 'object', 'The window should be an object');",
@" browser.test.assertEq(typeof window.id, 'number', 'The window id should be a number');",
@" browser.test.assertEq(window.type, 'normal', 'Window type should be normal');",
@" browser.test.assertEq(window.state, 'normal', 'Window state should be normal');",
@" browser.test.assertTrue(window.focused, 'Window should be focused');",
@" browser.test.assertFalse(window.incognito, 'Window should not be in incognito mode');",
@" browser.test.assertFalse(window.alwaysOnTop, 'Window should not be always on top');",
@" browser.test.assertEq(window.top, 50, 'Window top position should be 50');",
@" browser.test.assertEq(window.left, 100, 'Window left position should be 100');",
@" browser.test.assertEq(window.width, 800, 'Window width should be 800');",
@" browser.test.assertEq(window.height, 600, 'Window height should be 600');",

@" browser.test.notifyPass();",

@"});",

@"browser.test.yield('Open Window');"
]);

auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:windowsManifest resources:@{ @"background.js": backgroundScript }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);

[manager loadAndRun];

EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Open Window");

auto newWindow = adoptNS([[TestWebExtensionWindow alloc] init]);

[manager.get().controller didOpenWindow:newWindow.get()];
[manager run];
}

TEST(WKWebExtensionAPIWindows, FocusChangedEvent)
{
auto *backgroundScript = Util::constructScript(@[
@"let focusedTwice = false;",

@"browser.windows.onFocusChanged.addListener((windowId) => {",
@" if (windowId !== browser.windows.WINDOW_ID_NONE) {",
@" if (!focusedTwice) {",
@" browser.test.assertEq(typeof windowId, 'number', 'The window id should be a number when a window is focused');",
@" browser.test.yield('Focus None');",
@" focusedTwice = true;",
@" } else {",
@" browser.test.assertEq(typeof windowId, 'number', 'The window id should be a number when a window is focused again');",
@" browser.test.notifyPass();",
@" }",
@" } else {",
@" browser.test.assertEq(windowId, browser.windows.WINDOW_ID_NONE, 'The window id should be WINDOW_ID_NONE when nil is focused');",
@" browser.test.yield('Focus Window Again');",
@" }",
@"});",

@"browser.test.yield('Focus Window');"
]);

auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:windowsManifest resources:@{ @"background.js": backgroundScript }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
auto window = adoptNS([[TestWebExtensionWindow alloc] init]);

[manager load];

[manager.get().controller didOpenWindow:window.get()];
[manager run];

EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Focus Window");

[manager.get().controller didFocusWindow:window.get()];
[manager run];

EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Focus None");

[manager.get().controller didFocusWindow:nil];
[manager run];

EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Focus Window Again");

[manager.get().controller didFocusWindow:window.get()];
[manager run];
}

TEST(WKWebExtensionAPIWindows, RemovedEvent)
{
auto *backgroundScript = Util::constructScript(@[
@"browser.windows.onRemoved.addListener((windowId) => {",
@" browser.test.assertEq(typeof windowId, 'number', 'The window id should be a number');",

@" browser.test.notifyPass();",
@"});",

@"browser.test.yield('Close Window');"
]);

auto extension = adoptNS([[_WKWebExtension alloc] _initWithManifestDictionary:windowsManifest resources:@{ @"background.js": backgroundScript }]);
auto manager = adoptNS([[TestWebExtensionManager alloc] initForExtension:extension.get()]);
auto window = adoptNS([[TestWebExtensionWindow alloc] init]);

[manager load];

[manager.get().controller didOpenWindow:window.get()];
[manager run];

EXPECT_NS_EQUAL(manager.get().yieldMessage, @"Close Window");

[manager.get().controller didCloseWindow:window.get()];
[manager run];
}

#if PLATFORM(MAC)

TEST(WKWebExtensionAPIWindows, Create)
Expand Down

0 comments on commit 79b9a6a

Please sign in to comment.