diff --git a/mobile/modules/orca-mobile-web-shell/ios/MobileWebShellNavigationPolicy.swift b/mobile/modules/orca-mobile-web-shell/ios/MobileWebShellNavigationPolicy.swift index 7d0005e30e5..5ca4a5f156c 100644 --- a/mobile/modules/orca-mobile-web-shell/ios/MobileWebShellNavigationPolicy.swift +++ b/mobile/modules/orca-mobile-web-shell/ios/MobileWebShellNavigationPolicy.swift @@ -41,9 +41,9 @@ enum MobileWebShellNavigationPolicy { /// WKWebView, off-device: a sandboxed subframe navigating the top frame to the document URL /// arrives as `.other` with no gesture at all, and under a gesture-shaped rule that is an allow /// and a shell reload -- the bridge target cleared, the load state restarted, the page's state - /// gone. `isFromSubframe` is the second discriminator for the same reason: the same probe shows - /// the shell's own load arriving with source and target both the main frame, and a subframe's top - /// navigation arriving with the subframe as its source. + /// gone. The initiating frame is deliberately not an input: before iOS 18.5 WebKit reports no + /// source frame for the view's own `load` (WebKit c28b242), and reading it refused the shell's + /// own document. The one-shot flag already refuses a subframe's top navigation, as on Android. /// /// What is left for the gesture is the only thing an artifact may ask for: a foreign URL, which /// is refused and handed to the opener. A download is not a document load, so it takes that path @@ -56,7 +56,6 @@ enum MobileWebShellNavigationPolicy { static func verdict( url: String?, isMainFrame: Bool, - isFromSubframe: Bool, isDocumentUrl: Bool, isShellLoad: Bool, hasGesture: Bool, @@ -66,7 +65,7 @@ enum MobileWebShellNavigationPolicy { return .cancel } if isDocumentUrl { - return isShellLoad && !isFromSubframe && !isDownload ? .allow : .cancel + return isShellLoad && !isDownload ? .allow : .cancel } guard hasGesture, let offered = offerableUrl(url) else { return .cancel diff --git a/mobile/modules/orca-mobile-web-shell/ios/MobileWebShellView.swift b/mobile/modules/orca-mobile-web-shell/ios/MobileWebShellView.swift index e5575b36e2a..6ed13b44610 100644 --- a/mobile/modules/orca-mobile-web-shell/ios/MobileWebShellView.swift +++ b/mobile/modules/orca-mobile-web-shell/ios/MobileWebShellView.swift @@ -479,7 +479,6 @@ final class OrcaMobileWebShellView: ExpoView, WKNavigationDelegate, WKUIDelegate let verdict = MobileWebShellNavigationPolicy.verdict( url: navigationAction.request.url?.absoluteString, isMainFrame: navigationAction.targetFrame?.isMainFrame == true, - isFromSubframe: !navigationAction.sourceFrame.isMainFrame, isDocumentUrl: isDocumentUrl(navigationAction.request.url), isShellLoad: loadState.isShellLoad, hasGesture: navigationAction.navigationType == .linkActivated, diff --git a/mobile/modules/orca-mobile-web-shell/tests/MobileWebShellChecks.swift b/mobile/modules/orca-mobile-web-shell/tests/MobileWebShellChecks.swift index 81afd6fdfc6..be70ef8d184 100644 --- a/mobile/modules/orca-mobile-web-shell/tests/MobileWebShellChecks.swift +++ b/mobile/modules/orca-mobile-web-shell/tests/MobileWebShellChecks.swift @@ -545,7 +545,6 @@ import Foundation func verdict( _ url: String? = "https://example.com/artifact-link", isMainFrame: Bool = true, - isFromSubframe: Bool = false, isDocumentUrl: Bool = false, isShellLoad: Bool = false, hasGesture: Bool = true, @@ -554,7 +553,6 @@ import Foundation MobileWebShellNavigationPolicy.verdict( url: url, isMainFrame: isMainFrame, - isFromSubframe: isFromSubframe, isDocumentUrl: isDocumentUrl, isShellLoad: isShellLoad, hasGesture: hasGesture, @@ -571,18 +569,14 @@ import Foundation // navigating the top frame to the document URL arrives with no gesture at all. precondition(verdict(document, isDocumentUrl: true, hasGesture: false) == .cancel) precondition(verdict(document, isDocumentUrl: true, hasGesture: true) == .cancel) - precondition( - verdict(document, isFromSubframe: true, isDocumentUrl: true, isShellLoad: true, hasGesture: false) - == .cancel - ) - // The second discriminator, on its own: a load the shell did not start is refused even when the - // initiating frame is the main one, which is the page rewriting its own document away. + // A load the shell did not start is refused whichever frame initiated it: the page rewriting + // its own document away, or the sealed preview navigating the top frame. precondition(verdict(document, isDocumentUrl: true, isShellLoad: false, hasGesture: false) == .cancel) // A top-page meta refresh or a redirect to somewhere else: refused, and never opened. precondition(verdict(hasGesture: false) == .cancel) // A tap inside the sealed preview is exactly a subframe-initiated foreign navigation, and that // is the one thing the artifact is allowed to ask for. - precondition(verdict(isFromSubframe: true) == .cancelAndOffer(foreign)) + precondition(verdict() == .cancelAndOffer(foreign)) // A download is not a document load, so it is refused there rather than allowed; started by a // tap it reaches the opener, which is what makes `` behave as it does natively. precondition( @@ -591,11 +585,8 @@ import Foundation ) precondition(verdict(isDownload: true) == .cancelAndOffer(foreign)) // ``: a download that still names the shell's own document, which is the - // one thing never handed to the opener. Refused from either frame, gesture or not. + // one thing never handed to the opener. Refused, gesture or not. precondition(verdict(document, isDocumentUrl: true, isDownload: true) == .cancel) - precondition( - verdict(document, isFromSubframe: true, isDocumentUrl: true, isDownload: true) == .cancel - ) // A subframe is the sealed preview loading itself, which is not the user leaving the app. precondition(verdict(isMainFrame: false) == .cancel) precondition(verdict(isMainFrame: false, hasGesture: false) == .cancel) @@ -624,7 +615,6 @@ import Foundation MobileWebShellNavigationPolicy.verdict( url: document, isMainFrame: true, - isFromSubframe: false, isDocumentUrl: true, isShellLoad: machine.isShellLoad, hasGesture: false,