Skip to content

Commit

Permalink
Expose hasInjectedContent property on _WKWebExtension and _WKWebExten…
Browse files Browse the repository at this point in the history
…sionContext.

https://webkit.org/b/264762
rdar://problem/118347650

Reviewed by Brian Weinstein.

Drop the previous _hasStaticInjectedContentForURL: method that was only used for testing
and add hasInjectedContent to _WKWebExtension and _WKWebExtensionContext.

Added new tests for _WKWebExtensionContext that uses hasInjectedContentForURL: there
instead of the previous private _WKWebExtension method.

* Source/WebKit/UIProcess/API/Cocoa/_WKWebExtension.h:
* Source/WebKit/UIProcess/API/Cocoa/_WKWebExtension.mm:
(-[_WKWebExtension hasInjectedContent]): Added.
(-[_WKWebExtension _hasStaticInjectedContentForURL:]): Deleted.
* Source/WebKit/UIProcess/API/Cocoa/_WKWebExtensionContext.h:
* Source/WebKit/UIProcess/API/Cocoa/_WKWebExtensionContext.mm:
(-[_WKWebExtensionContext hasInjectedContent]): Added.
* Source/WebKit/UIProcess/API/Cocoa/_WKWebExtensionPrivate.h:
* Source/WebKit/UIProcess/Extensions/Cocoa/WebExtensionCocoa.mm:
(WebKit::WebExtension::hasStaticInjectedContent):
* Source/WebKit/UIProcess/Extensions/Cocoa/WebExtensionContextCocoa.mm:
(WebKit::WebExtensionContext::hasInjectedContentForURL): Use URL type.
(WebKit::WebExtensionContext::hasInjectedContent): Added.
* Source/WebKit/UIProcess/Extensions/WebExtension.h:
* Source/WebKit/UIProcess/Extensions/WebExtensionContext.h:
* Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebExtension.mm:
(TestWebKitAPI::TEST):
* Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebExtensionContext.mm:
(TestWebKitAPI::TEST):

Canonical link: https://commits.webkit.org/270675@main
  • Loading branch information
xeenon committed Nov 13, 2023
1 parent 65081c6 commit 38bdd66
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 45 deletions.
9 changes: 8 additions & 1 deletion Source/WebKit/UIProcess/API/Cocoa/_WKWebExtension.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ WK_CLASS_AVAILABLE(macos(13.3), ios(16.4))

/*!
@abstract A Boolean value indicating whether the extension has background content that can run when needed.
@discussion If this property is `YES`, the extension can run in the background even when no web pages are open.
@discussion If this property is `YES`, the extension can run in the background even when no webpages are open.
*/
@property (nonatomic, readonly) BOOL hasBackgroundContent;

Expand All @@ -218,6 +218,13 @@ WK_CLASS_AVAILABLE(macos(13.3), ios(16.4))
*/
@property (nonatomic, readonly) BOOL backgroundContentIsPersistent;

/*!
@abstract A Boolean value indicating whether the extension has script or stylesheet content that can be injected into webpages.
@discussion If this property is `YES`, the extension has content that can be injected by matching against the extension's requested match patterns.
@note Once the extension is loaded, use the `hasInjectedContent` property on the extension context, as the injectable content can change after the extension is loaded.
*/
@property (nonatomic, readonly) BOOL hasInjectedContent;

/*!
@abstract A Boolean value indicating whether the extension has an options page.
@discussion If this property is `YES`, the extension includes a dedicated options page where users can customize settings.
Expand Down
22 changes: 10 additions & 12 deletions Source/WebKit/UIProcess/API/Cocoa/_WKWebExtension.mm
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ - (BOOL)backgroundContentIsPersistent
return _webExtension->backgroundContentIsPersistent();
}

- (BOOL)hasInjectedContent
{
return _webExtension->hasStaticInjectedContent();
}

- (BOOL)hasOptionsPage
{
return _webExtension->hasOptionsPage();
Expand All @@ -280,13 +285,6 @@ - (BOOL)_backgroundContentUsesModules
return _webExtension->backgroundContentUsesModules();
}

- (BOOL)_hasStaticInjectedContentForURL:(NSURL *)url
{
NSParameterAssert([url isKindOfClass:NSURL.class]);

return _webExtension->hasStaticInjectedContentForURL(url);
}

#pragma mark WKObject protocol implementation

- (API::Object&)_apiObject
Expand Down Expand Up @@ -436,6 +434,11 @@ - (BOOL)backgroundContentIsPersistent
return NO;
}

- (BOOL)hasInjectedContent
{
return NO;
}

- (BOOL)hasOptionsPage
{
return NO;
Expand All @@ -461,11 +464,6 @@ - (BOOL)_backgroundContentUsesModules
return NO;
}

- (BOOL)_hasStaticInjectedContentForURL:(NSURL *)url
{
return NO;
}

#endif // ENABLE(WK_WEB_EXTENSIONS)

@end
11 changes: 9 additions & 2 deletions Source/WebKit/UIProcess/API/Cocoa/_WKWebExtensionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,20 +330,27 @@ WK_CLASS_AVAILABLE(macos(13.3), ios(16.4))
- (BOOL)hasAccessToURL:(NSURL *)url inTab:(nullable id <_WKWebExtensionTab>)tab NS_SWIFT_NAME(hasAccess(to:in:));

/*!
@abstract Checks if the currently granted permission match patterns set contains the `<all_urls>` pattern.
@abstract A Boolean value indicating if the currently granted permission match patterns set contains the `<all_urls>` pattern.
@discussion This does not check for any `*` host patterns. In most cases you should use the broader `hasAccessToAllHosts`.
@seealso currentPermissionMatchPatterns
@seealso hasAccessToAllHosts
*/
@property (nonatomic, readonly) BOOL hasAccessToAllURLs;

/*!
@abstract Checks if the currently granted permission match patterns set contains the `<all_urls>` pattern or any `*` host patterns.
@abstract A Boolean value indicating if the currently granted permission match patterns set contains the `<all_urls>` pattern or any `*` host patterns.
@seealso currentPermissionMatchPatterns
@seealso hasAccessToAllURLs
*/
@property (nonatomic, readonly) BOOL hasAccessToAllHosts;

/*!
@abstract A Boolean value indicating whether the extension has script or stylesheet content that can be injected into webpages.
@discussion If this property is `YES`, the extension has content that can be injected by matching against the extension's requested match patterns.
@seealso hasInjectedContentForURL:
*/
@property (nonatomic, readonly) BOOL hasInjectedContent;

/*!
@abstract Checks if the extension has script or stylesheet content that can be injected into the specified URL.
@param url The webpage URL to check.
Expand Down
10 changes: 10 additions & 0 deletions Source/WebKit/UIProcess/API/Cocoa/_WKWebExtensionContext.mm
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,11 @@ - (BOOL)hasAccessToAllHosts
return _webExtensionContext->hasAccessToAllHosts();
}

- (BOOL)hasInjectedContent
{
return _webExtensionContext->hasInjectedContent();
}

- (BOOL)hasInjectedContentForURL:(NSURL *)url
{
NSParameterAssert([url isKindOfClass:NSURL.class]);
Expand Down Expand Up @@ -1007,6 +1012,11 @@ - (BOOL)hasAccessToAllHosts
return NO;
}

- (BOOL)hasInjectedContent
{
return NO;
}

- (BOOL)hasInjectedContentForURL:(NSURL *)url
{
return NO;
Expand Down
8 changes: 0 additions & 8 deletions Source/WebKit/UIProcess/API/Cocoa/_WKWebExtensionPrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,6 @@ NS_ASSUME_NONNULL_BEGIN
/*! @abstract A Boolean value indicating whether the extension use modules for the background content. */
@property (readonly, nonatomic) BOOL _backgroundContentUsesModules;

/*!
@abstract Checks if the extension has script or stylesheet content that can be injected into the specified URL.
@param url The webpage URL to check.
@result Returns `YES` if the extension has content that can be injected by matching the `url` against the extension's requested match patterns.
@discussion The extension will still need to be loaded and have granted website permissions for its content to actually be injected.
*/
- (BOOL)_hasStaticInjectedContentForURL:(NSURL *)url;

@end

NS_ASSUME_NONNULL_END
6 changes: 6 additions & 0 deletions Source/WebKit/UIProcess/Extensions/Cocoa/WebExtensionCocoa.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,12 @@ static bool parseCommandShortcut(const String& shortcut, OptionSet<ModifierFlags
return false;
}

bool WebExtension::hasStaticInjectedContent()
{
populateContentScriptPropertiesIfNeeded();
return !m_staticInjectedContents.isEmpty();
}

NSArray *WebExtension::InjectedContentData::expandedIncludeMatchPatternStrings() const
{
NSMutableArray<NSString *> *result = [NSMutableArray array];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,10 +395,8 @@ static _WKWebExtensionContextError toAPI(WebExtensionContext::Error error)
return m_extension->staticInjectedContents();
}

bool WebExtensionContext::hasInjectedContentForURL(NSURL *url)
bool WebExtensionContext::hasInjectedContentForURL(const URL& url)
{
ASSERT(url);

for (auto& injectedContent : injectedContents()) {
// FIXME: <https://webkit.org/b/246492> Add support for exclude globs.
bool isExcluded = false;
Expand All @@ -422,6 +420,11 @@ static _WKWebExtensionContextError toAPI(WebExtensionContext::Error error)
return false;
}

bool WebExtensionContext::hasInjectedContent()
{
return !injectedContents().isEmpty();
}

URL WebExtensionContext::optionsPageURL() const
{
if (!extension().hasOptionsPage())
Expand Down
2 changes: 2 additions & 0 deletions Source/WebKit/UIProcess/Extensions/WebExtension.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ class WebExtension : public API::ObjectImpl<API::Object::Type::WebExtension>, pu
CocoaImage *actionIcon(CGSize idealSize);
NSString *displayActionLabel();
NSString *actionPopupPath();

bool hasAction();
bool hasBrowserAction();
bool hasPageAction();
Expand Down Expand Up @@ -236,6 +237,7 @@ class WebExtension : public API::ObjectImpl<API::Object::Type::WebExtension>, pu

const InjectedContentVector& staticInjectedContents();
bool hasStaticInjectedContentForURL(NSURL *);
bool hasStaticInjectedContent();

// Permissions requested by the extension in their manifest.
// These are not the currently allowed permissions.
Expand Down
3 changes: 2 additions & 1 deletion Source/WebKit/UIProcess/Extensions/WebExtensionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ class WebExtensionContext : public API::ObjectImpl<API::Object::Type::WebExtensi
void setInspectable(bool);

const InjectedContentVector& injectedContents();
bool hasInjectedContentForURL(NSURL *);
bool hasInjectedContentForURL(const URL&);
bool hasInjectedContent();

URL optionsPageURL() const;
URL overrideNewTabPageURL() const;
Expand Down
26 changes: 8 additions & 18 deletions Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebExtension.mm
Original file line number Diff line number Diff line change
Expand Up @@ -206,28 +206,22 @@
NSMutableDictionary *testManifestDictionary = [@{ @"manifest_version": @2, @"name": @"Test", @"description": @"Test", @"version": @"1.0" } mutableCopy];

testManifestDictionary[@"content_scripts"] = @[ @{ @"js": @[ @"test.js", @1, @"" ], @"css": @[ @NO, @"test.css", @"" ], @"matches": @[ @"*://*/" ] } ];
auto testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];

auto webkitURL = [NSURL URLWithString:@"https://webkit.org/"];
auto exampleURL = [NSURL URLWithString:@"https://example.com/"];
auto *testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];

EXPECT_NS_EQUAL(testExtension.errors, @[ ]);
EXPECT_TRUE([testExtension _hasStaticInjectedContentForURL:webkitURL]);
EXPECT_TRUE([testExtension _hasStaticInjectedContentForURL:exampleURL]);
EXPECT_TRUE(testExtension.hasInjectedContent);

testManifestDictionary[@"content_scripts"] = @[ @{ @"js": @[ @"test.js", @1, @"" ], @"css": @[ @NO, @"test.css", @"" ], @"matches": @[ @"*://*/" ], @"exclude_matches": @[ @"*://*.example.com/" ] } ];
testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];

EXPECT_NS_EQUAL(testExtension.errors, @[ ]);
EXPECT_TRUE([testExtension _hasStaticInjectedContentForURL:webkitURL]);
EXPECT_FALSE([testExtension _hasStaticInjectedContentForURL:exampleURL]);
EXPECT_TRUE(testExtension.hasInjectedContent);

testManifestDictionary[@"content_scripts"] = @[ @{ @"js": @[ @"test.js", @1, @"" ], @"css": @[ @NO, @"test.css", @"" ], @"matches": @[ @"*://*.example.com/" ] } ];
testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];

EXPECT_NS_EQUAL(testExtension.errors, @[ ]);
EXPECT_FALSE([testExtension _hasStaticInjectedContentForURL:webkitURL]);
EXPECT_TRUE([testExtension _hasStaticInjectedContentForURL:exampleURL]);
EXPECT_TRUE(testExtension.hasInjectedContent);

// Invalid cases

Expand All @@ -236,32 +230,28 @@

EXPECT_NE(testExtension.errors.count, 0ul);
EXPECT_NOT_NULL(matchingError(testExtension.errors, _WKWebExtensionErrorInvalidManifestEntry));
EXPECT_FALSE([testExtension _hasStaticInjectedContentForURL:webkitURL]);
EXPECT_FALSE([testExtension _hasStaticInjectedContentForURL:exampleURL]);
EXPECT_FALSE(testExtension.hasInjectedContent);

testManifestDictionary[@"content_scripts"] = @{ @"invalid": @YES };
testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];

EXPECT_NE(testExtension.errors.count, 0ul);
EXPECT_NOT_NULL(matchingError(testExtension.errors, _WKWebExtensionErrorInvalidManifestEntry));
EXPECT_FALSE([testExtension _hasStaticInjectedContentForURL:webkitURL]);
EXPECT_FALSE([testExtension _hasStaticInjectedContentForURL:exampleURL]);
EXPECT_FALSE(testExtension.hasInjectedContent);

testManifestDictionary[@"content_scripts"] = @[ @{ @"js": @[ @"test.js" ], @"matches": @[ ] } ];
testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];

EXPECT_NE(testExtension.errors.count, 0ul);
EXPECT_NOT_NULL(matchingError(testExtension.errors, _WKWebExtensionErrorInvalidManifestEntry));
EXPECT_FALSE([testExtension _hasStaticInjectedContentForURL:webkitURL]);
EXPECT_FALSE([testExtension _hasStaticInjectedContentForURL:exampleURL]);
EXPECT_FALSE(testExtension.hasInjectedContent);

testManifestDictionary[@"content_scripts"] = @[ @{ @"js": @[ @"test.js" ], @"matches": @[ @"*://*.example.com/" ], @"run_at": @"invalid" } ];
testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];

EXPECT_NE(testExtension.errors.count, 0ul);
EXPECT_NOT_NULL(matchingError(testExtension.errors, _WKWebExtensionErrorInvalidManifestEntry));
EXPECT_FALSE([testExtension _hasStaticInjectedContentForURL:webkitURL]);
EXPECT_TRUE([testExtension _hasStaticInjectedContentForURL:exampleURL]);
EXPECT_TRUE(testExtension.hasInjectedContent);
}

TEST(WKWebExtension, PermissionsParsing)
Expand Down
73 changes: 73 additions & 0 deletions Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebExtensionContext.mm
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,79 @@
EXPECT_EQ(testContext.grantedPermissions.count, 0ul);
}

TEST(WKWebExtensionContext, ContentScriptsParsing)
{
NSMutableDictionary *testManifestDictionary = [@{ @"manifest_version": @2, @"name": @"Test", @"description": @"Test", @"version": @"1.0" } mutableCopy];

testManifestDictionary[@"content_scripts"] = @[ @{ @"js": @[ @"test.js", @1, @"" ], @"css": @[ @NO, @"test.css", @"" ], @"matches": @[ @"*://*/" ] } ];
auto *testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];
auto *testContext = [[_WKWebExtensionContext alloc] initForExtension:testExtension];

auto *webkitURL = [NSURL URLWithString:@"https://webkit.org/"];
auto *exampleURL = [NSURL URLWithString:@"https://example.com/"];

EXPECT_NS_EQUAL(testExtension.errors, @[ ]);
EXPECT_TRUE(testContext.hasInjectedContent);
EXPECT_TRUE([testContext hasInjectedContentForURL:webkitURL]);
EXPECT_TRUE([testContext hasInjectedContentForURL:exampleURL]);

testManifestDictionary[@"content_scripts"] = @[ @{ @"js": @[ @"test.js", @1, @"" ], @"css": @[ @NO, @"test.css", @"" ], @"matches": @[ @"*://*/" ], @"exclude_matches": @[ @"*://*.example.com/" ] } ];
testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];
testContext = [[_WKWebExtensionContext alloc] initForExtension:testExtension];

EXPECT_NS_EQUAL(testExtension.errors, @[ ]);
EXPECT_TRUE(testContext.hasInjectedContent);
EXPECT_TRUE([testContext hasInjectedContentForURL:webkitURL]);
EXPECT_FALSE([testContext hasInjectedContentForURL:exampleURL]);

testManifestDictionary[@"content_scripts"] = @[ @{ @"js": @[ @"test.js", @1, @"" ], @"css": @[ @NO, @"test.css", @"" ], @"matches": @[ @"*://*.example.com/" ] } ];
testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];
testContext = [[_WKWebExtensionContext alloc] initForExtension:testExtension];

EXPECT_NS_EQUAL(testExtension.errors, @[ ]);
EXPECT_TRUE(testContext.hasInjectedContent);
EXPECT_FALSE([testContext hasInjectedContentForURL:webkitURL]);
EXPECT_TRUE([testContext hasInjectedContentForURL:exampleURL]);

// Invalid cases

testManifestDictionary[@"content_scripts"] = @[ ];
testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];
testContext = [[_WKWebExtensionContext alloc] initForExtension:testExtension];

EXPECT_NE(testExtension.errors.count, 0ul);
EXPECT_FALSE(testContext.hasInjectedContent);
EXPECT_FALSE([testContext hasInjectedContentForURL:webkitURL]);
EXPECT_FALSE([testContext hasInjectedContentForURL:exampleURL]);

testManifestDictionary[@"content_scripts"] = @{ @"invalid": @YES };
testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];
testContext = [[_WKWebExtensionContext alloc] initForExtension:testExtension];

EXPECT_NE(testExtension.errors.count, 0ul);
EXPECT_FALSE(testContext.hasInjectedContent);
EXPECT_FALSE([testContext hasInjectedContentForURL:webkitURL]);
EXPECT_FALSE([testContext hasInjectedContentForURL:exampleURL]);

testManifestDictionary[@"content_scripts"] = @[ @{ @"js": @[ @"test.js" ], @"matches": @[ ] } ];
testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];
testContext = [[_WKWebExtensionContext alloc] initForExtension:testExtension];

EXPECT_NE(testExtension.errors.count, 0ul);
EXPECT_FALSE(testContext.hasInjectedContent);
EXPECT_FALSE([testContext hasInjectedContentForURL:webkitURL]);
EXPECT_FALSE([testContext hasInjectedContentForURL:exampleURL]);

testManifestDictionary[@"content_scripts"] = @[ @{ @"js": @[ @"test.js" ], @"matches": @[ @"*://*.example.com/" ], @"run_at": @"invalid" } ];
testExtension = [[_WKWebExtension alloc] _initWithManifestDictionary:testManifestDictionary];
testContext = [[_WKWebExtensionContext alloc] initForExtension:testExtension];

EXPECT_NE(testExtension.errors.count, 0ul);
EXPECT_TRUE(testContext.hasInjectedContent);
EXPECT_FALSE([testContext hasInjectedContentForURL:webkitURL]);
EXPECT_TRUE([testContext hasInjectedContentForURL:exampleURL]);
}

TEST(WKWebExtensionContext, OptionsPageURLParsing)
{
auto *testManifestDictionary = @{
Expand Down

0 comments on commit 38bdd66

Please sign in to comment.