Skip to content

Commit

Permalink
Add MarketplaceKit URL handler
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=268260
rdar://121812886

Reviewed by Brady Eidson.

WebKit needs to intercept navigations to MarketplaceKit URL schemes and call the MarketplaceKit
installation API with the appropriate referrer.

* Source/WTF/wtf/PlatformEnableCocoa.h:
* Source/WTF/wtf/PlatformHave.h:
* Source/WebKit/Configurations/WebKitSwift.xcconfig:
* Source/WebKit/DerivedSources.make:
* Source/WebKit/UIProcess/Cocoa/MarketplaceKitWrapper.swift: Added.
(MarketplaceKitWrapper.requestAppInstallation(_:url:)):
* Source/WebKit/UIProcess/Cocoa/NavigationState.mm:
(WebKit::isMarketplaceKitURL):
(WebKit::interceptMarketplaceKitNavigation):
(WebKit::tryInterceptNavigation):
(WebKit::NavigationState::NavigationClient::decidePolicyForNavigationAction):
* Source/WebKit/WebKit.xcodeproj/project.pbxproj:

Canonical link: https://commits.webkit.org/273672@main
  • Loading branch information
bnham committed Jan 29, 2024
1 parent 6a61625 commit 515a0ef
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 26 deletions.
5 changes: 5 additions & 0 deletions Source/WTF/wtf/PlatformHave.h
Original file line number Diff line number Diff line change
Expand Up @@ -1693,3 +1693,8 @@
|| PLATFORM(VISION))
#define HAVE_STRICT_DECODABLE_PKPAYMENTPASS 1
#endif

#if !defined(HAVE_MARKETPLACE_KIT) \
&& (PLATFORM(IOS) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 170400)
#define HAVE_MARKETPLACE_KIT 1
#endif
12 changes: 6 additions & 6 deletions Source/WebKit/Configurations/WebKitSwift.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ WK_EXCLUDED_COORDINATOR_FILES_ = WKGroupSession.swift
WK_EXCLUDED_COORDINATOR_FILES_YES = $(WK_EXCLUDED_COORDINATOR_FILES_YES_$(WK_NOT_$(WK_HAVE_COORDINATOR)));
WK_EXCLUDED_COORDINATOR_FILES_YES_YES = WKGroupSession.swift

WK_HAVE_APPLICATION_UTILITIES = $(WK_HAVE_APPLICATION_UTILITIES_$(WK_PLATFORM_NAME));
WK_HAVE_APPLICATION_UTILITIES_iphoneos = $(WK_HAVE_APPLICATION_UTILITIES$(WK_IOS_17));
WK_HAVE_APPLICATION_UTILITIES_IOS_SINCE_17 = YES;
WK_HAVE_MARKETPLACE_KIT = $(WK_HAVE_MARKETPLACE_KIT_$(WK_PLATFORM_NAME));
WK_HAVE_MARKETPLACE_KIT_iphoneos = $(WK_HAVE_MARKETPLACE_KIT$(WK_IOS_17));
WK_HAVE_MARKETPLACE_KIT_IOS_SINCE_17 = YES;

WK_EXCLUDED_APPLICATION_UTILITIES_FILES = $(WK_EXCLUDED_APPLICATION_UTILITIES_FILES_$(WK_AND_$(USE_INTERNAL_SDK)_$(WK_HAVE_APPLICATION_UTILITIES)));
WK_EXCLUDED_APPLICATION_UTILITIES_FILES_NO = WKApplicationUtilities.swift;
WK_EXCLUDED_MARKETPLACE_KIT_WRAPPER_FILES = $(WK_EXCLUDED_MARKETPLACE_KIT_WRAPPER_FILES_$(WK_AND_$(USE_INTERNAL_SDK)_$(WK_HAVE_MARKETPLACE_KIT)));
WK_EXCLUDED_MARKETPLACE_KIT_WRAPPER_FILES_NO = MarketplaceKitWrapper.swift;

EXCLUDED_SOURCE_FILE_NAMES = $(WK_EXCLUDED_COORDINATOR_FILES) $(WK_EXCLUDED_APPLICATION_UTILITIES_FILES) $(EXCLUDED_IOS_RESOURCE_FILE_NAMES) $(EXCLUDED_MACOS_PLUGIN_FILE_NAMES)
EXCLUDED_SOURCE_FILE_NAMES = $(WK_EXCLUDED_COORDINATOR_FILES) $(WK_EXCLUDED_MARKETPLACE_KIT_WRAPPER_FILES) $(EXCLUDED_IOS_RESOURCE_FILE_NAMES) $(EXCLUDED_MACOS_PLUGIN_FILE_NAMES)
SWIFT_INSTALL_OBJC_HEADER = NO
SWIFT_VERSION = 5.0;

Expand Down
1 change: 0 additions & 1 deletion Source/WebKit/DerivedSources.make
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,6 @@ all : module.private.modulemap

ifeq ($(USE_INTERNAL_SDK),YES)
WEBKIT_ADDITIONS_SWIFT_FILES = \
WKApplicationUtilities.swift \
#

$(WEBKIT_ADDITIONS_SWIFT_FILES): %.swift : %.swift.in
Expand Down
51 changes: 51 additions & 0 deletions Source/WebKit/UIProcess/Cocoa/MarketplaceKitWrapper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (C) 2024 Apple Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.

#if canImport(MarketplaceKit)

import Foundation
import OSLog

import MarketplaceKit

@objc(WKMarketplaceKit)
@available(iOS 17.4, *)
public final class MarketplaceKitWrapper : NSObject {
private static let logger = Logger(subsystem: "com.apple.WebKit", category: "Loading")

@objc
@available(iOS 17.4, *)
public static func requestAppInstallation(topOrigin: URL, url: URL) {
let metadata = LinkMetadata(referrer: topOrigin, url: url)
Task { @MainActor in
do {
try await AppLibrary.current.requestAppInstallation(with: metadata)
logger.debug("WKMarketplaceKit.requestAppInstallation with top origin \(topOrigin, privacy: .sensitive) for \(url, privacy: .sensitive) succeeded")
} catch {
logger.error("WKMarketplaceKit.requestAppInstallation with top origin \(topOrigin, privacy: .sensitive) for \(url, privacy: .sensitive) failed: \(error, privacy: .public)")
}
}
}
}

#endif
62 changes: 47 additions & 15 deletions Source/WebKit/UIProcess/Cocoa/NavigationState.mm
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
#import <WebCore/SerializedCryptoKeyWrap.h>
#import <wtf/BlockPtr.h>
#import <wtf/NeverDestroyed.h>
#import <wtf/Scope.h>
#import <wtf/URL.h>
#import <wtf/WeakHashMap.h>
#import <wtf/cocoa/VectorCocoa.h>
Expand All @@ -95,20 +96,15 @@
#import <pal/spi/cocoa/LaunchServicesSPI.h>
#endif

#if USE(APPLE_INTERNAL_SDK) && __has_include(<WebKitAdditions/NavigationStateAdditions.mm>)
#import <WebKitAdditions/NavigationStateAdditions.mm>
#endif

#ifndef DEFINE_NAVIGATION_STATE_APPLICATION_UTILITIES_LINK_HANDLER
#define DEFINE_NAVIGATION_STATE_APPLICATION_UTILITIES_LINK_HANDLER
#endif
#if HAVE(MARKETPLACE_KIT)
#import "WebKitSwiftSoftLink.h"

#ifndef NAVIGATION_STATE_TRY_INTERCEPT_NAVIGATION
#define NAVIGATION_STATE_TRY_INTERCEPT_NAVIGATION
#endif
SOFT_LINK_CLASS_FOR_HEADER(WebKit, WKMarketplaceKit)
SOFT_LINK_CLASS_FOR_SOURCE_OPTIONAL(WebKit, WebKitSwift, WKMarketplaceKit)

#ifndef NAVIGATION_STATE_DECIDE_POLICY_FOR_NAVIGATION_ACTION_ALLOW_WITHOUT_APP_LINK
#define NAVIGATION_STATE_DECIDE_POLICY_FOR_NAVIGATION_ACTION_ALLOW_WITHOUT_APP_LINK
@interface WKMarketplaceKit : NSObject
+ (void)requestAppInstallationWithTopOrigin:(NSURL *)topOrigin url:(NSURL *)url;
@end
#endif

namespace WebKit {
Expand Down Expand Up @@ -415,11 +411,41 @@ static void trySOAuthorization(Ref<API::NavigationAction>&& navigationAction, We
#endif
}

DEFINE_NAVIGATION_STATE_APPLICATION_UTILITIES_LINK_HANDLER
#if HAVE(MARKETPLACE_KIT)

static bool isMarketplaceKitURL(const URL& url)
{
return url.protocolIs("app-distribution"_s) || url.protocolIs("marketplace-kit"_s);
}

static void interceptMarketplaceKitNavigation(Ref<API::NavigationAction>&& action, WebPageProxy& page)
{
if (!action->shouldOpenExternalSchemes() || !action->isProcessingUserGesture() || action->isRedirect() || action->data().requesterTopOrigin.isNull()) {
RELEASE_LOG_ERROR(Loading, "NavigationState: can't handle MarketplaceKit navigation with shouldOpenExternalSchemes: %d, isProcessingUserGesture: %d, isRedirect: %d, requesterTopOriginIsNull: %d", action->shouldOpenExternalSchemes(), action->isProcessingUserGesture(), action->isRedirect(), action->data().requesterTopOrigin.isNull());
return;
}

RetainPtr<NSURL> requesterTopOriginURL = static_cast<NSURL *>(action->data().requesterTopOrigin.toURL());
RetainPtr<NSURL> url = static_cast<NSURL *>(action->request().url());

if (!requesterTopOriginURL || !url) {
RELEASE_LOG_ERROR(Loading, "NavigationState: can't handle MarketplaceKit navigation with requesterTopOriginURL: %d url: %d", static_cast<bool>(requesterTopOriginURL), static_cast<bool>(url));
return;
}

[getWKMarketplaceKitClass() requestAppInstallationWithTopOrigin:requesterTopOriginURL.get() url:url.get()];
}

#endif // HAVE(MARKETPLACE_KIT)

static void tryInterceptNavigation(Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, WTF::Function<void(bool)>&& completionHandler)
{
NAVIGATION_STATE_TRY_INTERCEPT_NAVIGATION
#if HAVE(MARKETPLACE_KIT)
if (isMarketplaceKitURL(navigationAction->request().url())) {
interceptMarketplaceKitNavigation(WTFMove(navigationAction), page);
return completionHandler(true /* interceptedNavigation */);
}
#endif

#if HAVE(APP_LINKS)
if (navigationAction->shouldOpenAppLinks()) {
Expand Down Expand Up @@ -593,7 +619,13 @@ static bool isUnsupportedWebExtensionNavigation(API::NavigationAction& navigatio
break;

case _WKNavigationActionPolicyAllowWithoutTryingAppLink:
NAVIGATION_STATE_DECIDE_POLICY_FOR_NAVIGATION_ACTION_ALLOW_WITHOUT_APP_LINK
#if HAVE(MARKETPLACE_KIT)
if (isMarketplaceKitURL(navigationAction->request().url())) {
interceptMarketplaceKitNavigation(WTFMove(navigationAction), webPageProxy);
localListener->ignore();
return;
}
#endif

trySOAuthorization(WTFMove(navigationAction), webPageProxy, [localListener = WTFMove(localListener), websitePolicies = WTFMove(apiWebsitePolicies)] (bool optimizedLoad) {
if (optimizedLoad) {
Expand Down
8 changes: 4 additions & 4 deletions Source/WebKit/WebKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2378,11 +2378,11 @@
E5CBA76727A318E100DF7858 /* UnifiedSource119.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E5CBA76027A3187900DF7858 /* UnifiedSource119.cpp */; };
E5CBA76827A318E100DF7858 /* UnifiedSource117.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E5CBA76227A3187900DF7858 /* UnifiedSource117.cpp */; };
E5DEFA6826F8F42600AB68DB /* PhotosUISPI.h in Headers */ = {isa = PBXBuildFile; fileRef = E5DEFA6726F8F42600AB68DB /* PhotosUISPI.h */; };
EB0FBFA72B66C61E00269CC1 /* MarketplaceKitWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB0FBFA62B66C61E00269CC1 /* MarketplaceKitWrapper.swift */; };
EB36B16827A7B4500050E00D /* PushService.h in Headers */ = {isa = PBXBuildFile; fileRef = EB36B16627A7B4500050E00D /* PushService.h */; };
EB36B16927A7B4500050E00D /* PushService.mm in Sources */ = {isa = PBXBuildFile; fileRef = EB36B16727A7B4500050E00D /* PushService.mm */; };
EB450E0F2996C7B6009724B1 /* WKWebsiteDataStoreRefPrivateMac.h in Headers */ = {isa = PBXBuildFile; fileRef = EB450E0D2996C7A1009724B1 /* WKWebsiteDataStoreRefPrivateMac.h */; settings = {ATTRIBUTES = (Private, ); }; };
EB579C3729AEBD0800894C1C /* EarlyHintsResourceLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = EB579C3629AEBCF100894C1C /* EarlyHintsResourceLoader.h */; };
EB7169D32B05D8B2005F305D /* WKApplicationUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7169D22B05D8B2005F305D /* WKApplicationUtilities.swift */; };
EB7D252B27B31B77009CB586 /* com.apple.WebKit.webpushd.mac.sb in Resources */ = {isa = PBXBuildFile; fileRef = EB7D252A27B31B3F009CB586 /* com.apple.WebKit.webpushd.mac.sb */; };
EBA8D3AB27A5E31300CB7900 /* ApplePushServiceSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = EBA8D3AA27A5E31300CB7900 /* ApplePushServiceSPI.h */; };
EBA8D3B227A5E33F00CB7900 /* ApplePushServiceConnection.mm in Sources */ = {isa = PBXBuildFile; fileRef = EBA8D3AC27A5E33E00CB7900 /* ApplePushServiceConnection.mm */; };
Expand Down Expand Up @@ -7763,13 +7763,13 @@
E5DEFA6726F8F42600AB68DB /* PhotosUISPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PhotosUISPI.h; sourceTree = "<group>"; };
EB0D312D275AE13300863D8F /* com.apple.webkit.webpushd.mac.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.webkit.webpushd.mac.plist; sourceTree = "<group>"; };
EB0D312E275AE13300863D8F /* com.apple.webkit.webpushd.ios.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.webkit.webpushd.ios.plist; sourceTree = "<group>"; };
EB0FBFA62B66C61E00269CC1 /* MarketplaceKitWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarketplaceKitWrapper.swift; sourceTree = "<group>"; };
EB36B16627A7B4500050E00D /* PushService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PushService.h; sourceTree = "<group>"; };
EB36B16727A7B4500050E00D /* PushService.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PushService.mm; sourceTree = "<group>"; };
EB450E0D2996C7A1009724B1 /* WKWebsiteDataStoreRefPrivateMac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WKWebsiteDataStoreRefPrivateMac.h; path = mac/WKWebsiteDataStoreRefPrivateMac.h; sourceTree = "<group>"; };
EB450E0E2996C7A1009724B1 /* WKWebsiteDataStoreRefPrivateMac.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = WKWebsiteDataStoreRefPrivateMac.mm; path = mac/WKWebsiteDataStoreRefPrivateMac.mm; sourceTree = "<group>"; };
EB579C3529AEBCF100894C1C /* EarlyHintsResourceLoader.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = EarlyHintsResourceLoader.cpp; sourceTree = "<group>"; };
EB579C3629AEBCF100894C1C /* EarlyHintsResourceLoader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EarlyHintsResourceLoader.h; sourceTree = "<group>"; };
EB7169D22B05D8B2005F305D /* WKApplicationUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKApplicationUtilities.swift; sourceTree = "<group>"; };
EB7D252927B316A6009CB586 /* com.apple.WebKit.webpushd.mac.sb.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = com.apple.WebKit.webpushd.mac.sb.in; sourceTree = "<group>"; };
EB7D252A27B31B3F009CB586 /* com.apple.WebKit.webpushd.mac.sb */ = {isa = PBXFileReference; lastKnownFileType = file; path = com.apple.WebKit.webpushd.mac.sb; sourceTree = "<group>"; };
EBA8D3AA27A5E31300CB7900 /* ApplePushServiceSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ApplePushServiceSPI.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -9081,6 +9081,7 @@
7A821F4D1E2F679E00604577 /* LegacyCustomProtocolManagerClient.mm */,
A1DF631118E0B7C8003A3E2A /* LegacyDownloadClient.h */,
A1DF631018E0B7C8003A3E2A /* LegacyDownloadClient.mm */,
EB0FBFA62B66C61E00269CC1 /* MarketplaceKitWrapper.swift */,
9342588F2555DCA50059EEDD /* MediaPermissionUtilities.mm */,
411286EF21C8A90C003A8550 /* MediaUtilities.h */,
411286F021C8A90D003A8550 /* MediaUtilities.mm */,
Expand Down Expand Up @@ -14687,7 +14688,6 @@
1AAF08B619269E6D00B6390C /* WebUserContentControllerMessages.h */,
7C361D76192803BD0036A59D /* WebUserContentControllerProxyMessageReceiver.cpp */,
7C361D77192803BD0036A59D /* WebUserContentControllerProxyMessages.h */,
EB7169D22B05D8B2005F305D /* WKApplicationUtilities.swift */,
);
name = "Derived Sources";
path = DerivedSources/WebKit;
Expand Down Expand Up @@ -18924,7 +18924,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
EB7169D32B05D8B2005F305D /* WKApplicationUtilities.swift in Sources */,
EB0FBFA72B66C61E00269CC1 /* MarketplaceKitWrapper.swift in Sources */,
CDF1B915266F396A0007EC10 /* WKGroupSession.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down

0 comments on commit 515a0ef

Please sign in to comment.