Bug 225333 - Add SPI to suspend / resume a WKWebView
Summary: Add SPI to suspend / resume a WKWebView
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebKit2 (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Chris Dumez
URL:
Keywords: InRadar
Depends on: 225685
Blocks:
  Show dependency treegraph
 
Reported: 2021-05-03 16:15 PDT by Chris Dumez
Modified: 2021-05-11 22:53 PDT (History)
14 users (show)

See Also:


Attachments
WIP Patch (10.83 KB, patch)
2021-05-03 16:17 PDT, Chris Dumez
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
WIP Patch (13.87 KB, patch)
2021-05-03 16:45 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
WIP Patch (with test) (22.22 KB, patch)
2021-05-03 17:11 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (29.51 KB, patch)
2021-05-04 08:17 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (33.88 KB, patch)
2021-05-05 10:05 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (37.36 KB, patch)
2021-05-05 14:43 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (38.00 KB, patch)
2021-05-05 15:42 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (132.01 KB, patch)
2021-05-10 15:40 PDT, Chris Dumez
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
Patch (132.04 KB, patch)
2021-05-10 16:28 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (132.49 KB, patch)
2021-05-11 11:11 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (129.20 KB, patch)
2021-05-11 11:15 PDT, Chris Dumez
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
Patch (129.30 KB, patch)
2021-05-11 11:41 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (129.29 KB, patch)
2021-05-11 21:02 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (129.20 KB, patch)
2021-05-11 21:05 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (129.20 KB, patch)
2021-05-11 21:26 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Chris Dumez 2021-05-03 16:15:41 PDT
Add SPI to suspend / resume a WKWebView.
Comment 1 Chris Dumez 2021-05-03 16:15:58 PDT
<rdar://77462543>
Comment 2 Chris Dumez 2021-05-03 16:17:58 PDT
Created attachment 427613 [details]
WIP Patch
Comment 3 Chris Dumez 2021-05-03 16:45:53 PDT
Created attachment 427619 [details]
WIP Patch
Comment 4 Chris Dumez 2021-05-03 17:11:33 PDT
Created attachment 427623 [details]
WIP Patch (with test)
Comment 5 Chris Dumez 2021-05-04 08:17:49 PDT
Created attachment 427670 [details]
Patch
Comment 6 Sam Weinig 2021-05-04 09:00:24 PDT
Comment on attachment 427670 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=427670&action=review

> Source/WebKit/ChangeLog:12
> +        Add SPI to suspend / resume a WKWebView. This suspends the page as if it was in
> +        the back/forward cache but the suspension happens in-place, no need to navigate.
> +        The suspended page is also not part of the Back/Forward cache so its cap on the
> +        number of suspended pages does not apply here.

What are the rules for what you can do with a suspended WKWebView? Can you call loadRequest:?  Will that resume it?
Comment 7 Geoffrey Garen 2021-05-04 11:07:52 PDT
> What are the rules for what you can do with a suspended WKWebView? Can you
> call loadRequest:?  Will that resume it?

This question makes me wonder if we really want suspension to be a state on the WebView, or if it would be better to model it more explicitly like a navigation to about:blank. (Not that we would require the client to actually navigate to about:blank -- just that the implementation of suspend would navigate to about:blank or about:suspended or whatever, and the implementation of resume would navigate back. Perhaps we would also hide the actual about:blank or about:suspended entry from history and/or back/forward list.)
Comment 8 Chris Dumez 2021-05-04 11:39:26 PDT
(In reply to Sam Weinig from comment #6)
> Comment on attachment 427670 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=427670&action=review
> 
> > Source/WebKit/ChangeLog:12
> > +        Add SPI to suspend / resume a WKWebView. This suspends the page as if it was in
> > +        the back/forward cache but the suspension happens in-place, no need to navigate.
> > +        The suspended page is also not part of the Back/Forward cache so its cap on the
> > +        number of suspended pages does not apply here.
> 
> What are the rules for what you can do with a suspended WKWebView? Can you
> call loadRequest:?  Will that resume it?

With the current implementation, it isn't safe to do much with a suspended view besides resuming it or destroying it. We could support loadRequest if we wanted. This is not a use case we have currently though.
Comment 9 Chris Dumez 2021-05-04 11:42:15 PDT
(In reply to Geoffrey Garen from comment #7)
> > What are the rules for what you can do with a suspended WKWebView? Can you
> > call loadRequest:?  Will that resume it?
> 
> This question makes me wonder if we really want suspension to be a state on
> the WebView, or if it would be better to model it more explicitly like a
> navigation to about:blank. (Not that we would require the client to actually
> navigate to about:blank -- just that the implementation of suspend would
> navigate to about:blank or about:suspended or whatever, and the
> implementation of resume would navigate back. Perhaps we would also hide the
> actual about:blank or about:suspended entry from history and/or back/forward
> list.)

2 issues:
a. Yes, we'd need logic to hide the about:blank / about:suspended from the back/forward list. This was a major point to introducing this SPI vs having the client navigate.
b. Your statement assumes we have a history item to go back to. Unlike back/forward cache, this new suspension mechanism does not require a HistoryItem. Note that calling loadHTMLString or loadData on a WKWebView does NOT create a HistoryItem. Yet I think we should be able to suspend it.
Comment 10 Chris Dumez 2021-05-04 11:49:00 PDT
(In reply to Sam Weinig from comment #6)
> Comment on attachment 427670 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=427670&action=review
> 
> > Source/WebKit/ChangeLog:12
> > +        Add SPI to suspend / resume a WKWebView. This suspends the page as if it was in
> > +        the back/forward cache but the suspension happens in-place, no need to navigate.
> > +        The suspended page is also not part of the Back/Forward cache so its cap on the
> > +        number of suspended pages does not apply here.
> 
> What are the rules for what you can do with a suspended WKWebView? Can you
> call loadRequest:?  Will that resume it?

I did not focus on all these cases because this is not API yet, just SPI for now used in a very specific way. If it were API, I agree we'd need to go through each WKWebView API and decide what their behavior should be when suspended.

Since I think the SPI would eventually be useful as API, I do think I should follow-up and figure those cases out though. I was hoping to land this ASAP to unblock the client app that needs it but if people think we need a better story for what each WKWebView API does when suspended before landing, I can prioritize it.
Comment 11 Chris Dumez 2021-05-05 10:05:41 PDT
Created attachment 427775 [details]
Patch
Comment 12 Sam Weinig 2021-05-05 14:07:38 PDT
(In reply to Chris Dumez from comment #10)
> (In reply to Sam Weinig from comment #6)
> > Comment on attachment 427670 [details]
> > Patch
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=427670&action=review
> > 
> > > Source/WebKit/ChangeLog:12
> > > +        Add SPI to suspend / resume a WKWebView. This suspends the page as if it was in
> > > +        the back/forward cache but the suspension happens in-place, no need to navigate.
> > > +        The suspended page is also not part of the Back/Forward cache so its cap on the
> > > +        number of suspended pages does not apply here.
> > 
> > What are the rules for what you can do with a suspended WKWebView? Can you
> > call loadRequest:?  Will that resume it?
> 
> I did not focus on all these cases because this is not API yet, just SPI for
> now used in a very specific way. If it were API, I agree we'd need to go
> through each WKWebView API and decide what their behavior should be when
> suspended.
> 
> Since I think the SPI would eventually be useful as API, I do think I should
> follow-up and figure those cases out though. I was hoping to land this ASAP
> to unblock the client app that needs it but if people think we need a better
> story for what each WKWebView API does when suspended before landing, I can
> prioritize it.

I generally think we should be very careful with any new SPI as they can be quite hard to remove once adopted.

For something as big as this, I think we need the functionality to be fleshed out and tested before we would want anyone using it.
Comment 13 Chris Dumez 2021-05-05 14:13:07 PDT
(In reply to Sam Weinig from comment #12)
> (In reply to Chris Dumez from comment #10)
> > (In reply to Sam Weinig from comment #6)
> > > Comment on attachment 427670 [details]
> > > Patch
> > > 
> > > View in context:
> > > https://bugs.webkit.org/attachment.cgi?id=427670&action=review
> > > 
> > > > Source/WebKit/ChangeLog:12
> > > > +        Add SPI to suspend / resume a WKWebView. This suspends the page as if it was in
> > > > +        the back/forward cache but the suspension happens in-place, no need to navigate.
> > > > +        The suspended page is also not part of the Back/Forward cache so its cap on the
> > > > +        number of suspended pages does not apply here.
> > > 
> > > What are the rules for what you can do with a suspended WKWebView? Can you
> > > call loadRequest:?  Will that resume it?
> > 
> > I did not focus on all these cases because this is not API yet, just SPI for
> > now used in a very specific way. If it were API, I agree we'd need to go
> > through each WKWebView API and decide what their behavior should be when
> > suspended.
> > 
> > Since I think the SPI would eventually be useful as API, I do think I should
> > follow-up and figure those cases out though. I was hoping to land this ASAP
> > to unblock the client app that needs it but if people think we need a better
> > story for what each WKWebView API does when suspended before landing, I can
> > prioritize it.
> 
> I generally think we should be very careful with any new SPI as they can be
> quite hard to remove once adopted.
> 
> For something as big as this, I think we need the functionality to be
> fleshed out and tested before we would want anyone using it.

I did flesh it out more in my latest iteration. Requests to navigate or run JS are now handled in particular.
Comment 14 Chris Dumez 2021-05-05 14:43:01 PDT
Created attachment 427810 [details]
Patch
Comment 15 Chris Dumez 2021-05-05 15:42:58 PDT
Created attachment 427815 [details]
Patch
Comment 16 Sam Weinig 2021-05-06 17:51:52 PDT
(In reply to Chris Dumez from comment #13)
> (In reply to Sam Weinig from comment #12)
> > (In reply to Chris Dumez from comment #10)
> > > (In reply to Sam Weinig from comment #6)
> > > > Comment on attachment 427670 [details]
> > > > Patch
> > > > 
> > > > View in context:
> > > > https://bugs.webkit.org/attachment.cgi?id=427670&action=review
> > > > 
> > > > > Source/WebKit/ChangeLog:12
> > > > > +        Add SPI to suspend / resume a WKWebView. This suspends the page as if it was in
> > > > > +        the back/forward cache but the suspension happens in-place, no need to navigate.
> > > > > +        The suspended page is also not part of the Back/Forward cache so its cap on the
> > > > > +        number of suspended pages does not apply here.
> > > > 
> > > > What are the rules for what you can do with a suspended WKWebView? Can you
> > > > call loadRequest:?  Will that resume it?
> > > 
> > > I did not focus on all these cases because this is not API yet, just SPI for
> > > now used in a very specific way. If it were API, I agree we'd need to go
> > > through each WKWebView API and decide what their behavior should be when
> > > suspended.
> > > 
> > > Since I think the SPI would eventually be useful as API, I do think I should
> > > follow-up and figure those cases out though. I was hoping to land this ASAP
> > > to unblock the client app that needs it but if people think we need a better
> > > story for what each WKWebView API does when suspended before landing, I can
> > > prioritize it.
> > 
> > I generally think we should be very careful with any new SPI as they can be
> > quite hard to remove once adopted.
> > 
> > For something as big as this, I think we need the functionality to be
> > fleshed out and tested before we would want anyone using it.
> 
> I did flesh it out more in my latest iteration. Requests to navigate or run
> JS are now handled in particular.

Is the rule then that any use of API that would ordinarily work should work when suspended, resuming the page? Do some functions not resume the webview?
Comment 17 Sam Weinig 2021-05-06 17:54:26 PDT
I should add, my personal opinion would be that all WKWebView API/SPI throw or crash if the page is suspended (except resume of course).
Comment 18 Chris Dumez 2021-05-06 18:19:51 PDT
(In reply to Sam Weinig from comment #17)
> I should add, my personal opinion would be that all WKWebView API/SPI throw
> or crash if the page is suspended (except resume of course).

What I implemented is so far is my personal preference (but I am happy to change if others feel differently):
1. Navigating the views unsuspends the page before navigating
2. Operations that do not require the WebProcess keep working
2. Operations that need the WebProcess (e.g. evaluateJS(), find(), takeSnapshot(), createWebArchive, createPDF, ...) fail (as in I am calling their completion handler with an error). I have no covered everything in this case but the top ones.

Throwing an exception for every SPI/API call besides resume() seems super aggressive but then again, that's pretty much the contract we have with the client. That said, there are a lot of API/SPIs that do not need the WebProcess in any way (e.g. WKWebView.URL, WKWebView.isLoading, WKWebView.title, WKWebView.canGoBack) and could keep working just fine while suspended I think.
Comment 19 Sam Weinig 2021-05-07 09:12:27 PDT
(In reply to Chris Dumez from comment #18)
> (In reply to Sam Weinig from comment #17)
> > I should add, my personal opinion would be that all WKWebView API/SPI throw
> > or crash if the page is suspended (except resume of course).
> 
> What I implemented is so far is my personal preference (but I am happy to
> change if others feel differently):
> 1. Navigating the views unsuspends the page before navigating
> 2. Operations that do not require the WebProcess keep working
> 2. Operations that need the WebProcess (e.g. evaluateJS(), find(),
> takeSnapshot(), createWebArchive, createPDF, ...) fail (as in I am calling
> their completion handler with an error). I have no covered everything in
> this case but the top ones.
> 
> Throwing an exception for every SPI/API call besides resume() seems super
> aggressive but then again, that's pretty much the contract we have with the
> client. That said, there are a lot of API/SPIs that do not need the
> WebProcess in any way (e.g. WKWebView.URL, WKWebView.isLoading,
> WKWebView.title, WKWebView.canGoBack) and could keep working just fine while
> suspended I think.

I realize I didn't really add my reasons for my opinion. There are a few reasons I think being heavy handed makes sense.

1) It is much easier to relax things in the future than it is to make them stricter. For instance, if we want to make it valid to call WKWebView.canGoBack when suspended, we can remove the check and document in the header that WKWebView.canGoBack can be called when suspended.
2) Side effects for methods like loadRequest: resuming the view are hard to teach, especially when it is no 100% clear which methods will have the side effects and which won't.
Comment 20 Geoffrey Garen 2021-05-10 10:42:34 PDT
I think I agree that throwing an exception when an operation obviously can't work because we're suspended is a helpful approach -- and more helpful than auto-unsuspending, since that might put the app in an invalid state.

Why don't we compromise and throw an exception in APIs that (a) can't work while suspended or (b) it's not obvious whether they can work or not while suspended. But for the APIs where it is obvious that they can work while suspended, let's not throw. It just doesn't make sense. And I worry that we'll create a long tail of trivial exception noise that doesn't help anyone -- for example, just because Interface Builder has the WebView's URL or title hooked up to some view, which seems like an obviously valid thing to do even while suspended.
Comment 21 Geoffrey Garen 2021-05-10 10:46:48 PDT
Side note: This discussion reminds me of the setDefersLoading() API, and the numerous costly "blank WebView" / "load failed" bugs it caused. Hopefully throwing an exception in APIs that obviously won't work, including loading APIs, will help avoid that kind of problem this time around.
Comment 22 Chris Dumez 2021-05-10 15:40:22 PDT
Created attachment 428213 [details]
Patch
Comment 23 Chris Dumez 2021-05-10 15:41:07 PDT
(In reply to Geoffrey Garen from comment #20)
> I think I agree that throwing an exception when an operation obviously can't
> work because we're suspended is a helpful approach -- and more helpful than
> auto-unsuspending, since that might put the app in an invalid state.
> 
> Why don't we compromise and throw an exception in APIs that (a) can't work
> while suspended or (b) it's not obvious whether they can work or not while
> suspended. But for the APIs where it is obvious that they can work while
> suspended, let's not throw. It just doesn't make sense. And I worry that
> we'll create a long tail of trivial exception noise that doesn't help anyone
> -- for example, just because Interface Builder has the WebView's URL or
> title hooked up to some view, which seems like an obviously valid thing to
> do even while suspended.

Ok, I have implemented this proposal and extended API test coverage accordingly.
Comment 24 Chris Dumez 2021-05-10 16:28:40 PDT
Created attachment 428218 [details]
Patch
Comment 25 Geoffrey Garen 2021-05-11 10:46:06 PDT
iOS EWS is angry: TestWebKitAPI.ProcessSuspension.WKWebViewSuspendPage
Comment 26 Chris Dumez 2021-05-11 10:54:25 PDT
(In reply to Geoffrey Garen from comment #25)
> iOS EWS is angry: TestWebKitAPI.ProcessSuspension.WKWebViewSuspendPage

It is minor, will fix.

   TestWebKitAPI.ProcessSuspension.WKWebViewSuspendPage
        
        /Volumes/Data/worker/iOS-14-Simulator-Build-EWS/build/Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSuspension.mm:192
        Value of: [webView allowsLinkPreview]
          Actual: true
        Expected: false

Looks like this property has a different default value on iOS than macOS is all.
Comment 27 Chris Dumez 2021-05-11 11:10:28 PDT
(In reply to Chris Dumez from comment #26)
> (In reply to Geoffrey Garen from comment #25)
> > iOS EWS is angry: TestWebKitAPI.ProcessSuspension.WKWebViewSuspendPage
> 
> It is minor, will fix.
> 
>    TestWebKitAPI.ProcessSuspension.WKWebViewSuspendPage
>         
>        
> /Volumes/Data/worker/iOS-14-Simulator-Build-EWS/build/Tools/TestWebKitAPI/
> Tests/WebKitCocoa/ProcessSuspension.mm:192
>         Value of: [webView allowsLinkPreview]
>           Actual: true
>         Expected: false
> 
> Looks like this property has a different default value on iOS than macOS is
> all.

```
/*! @abstract A Boolean value indicating whether link preview is allowed for any
 links inside this WKWebView.
 @discussion The default value is YES on Mac and iOS.
 */
@property (nonatomic) BOOL allowsLinkPreview WK_API_AVAILABLE(macos(10.11), ios(9.0));
```

That doc seems to be a lie. Turns out it is true on both platforms.
Comment 28 Chris Dumez 2021-05-11 11:11:53 PDT
Created attachment 428296 [details]
Patch
Comment 29 Chris Dumez 2021-05-11 11:15:39 PDT
Created attachment 428298 [details]
Patch
Comment 30 Chris Dumez 2021-05-11 11:16:44 PDT
(In reply to Chris Dumez from comment #27)
> (In reply to Chris Dumez from comment #26)
> > (In reply to Geoffrey Garen from comment #25)
> > > iOS EWS is angry: TestWebKitAPI.ProcessSuspension.WKWebViewSuspendPage
> > 
> > It is minor, will fix.
> > 
> >    TestWebKitAPI.ProcessSuspension.WKWebViewSuspendPage
> >         
> >        
> > /Volumes/Data/worker/iOS-14-Simulator-Build-EWS/build/Tools/TestWebKitAPI/
> > Tests/WebKitCocoa/ProcessSuspension.mm:192
> >         Value of: [webView allowsLinkPreview]
> >           Actual: true
> >         Expected: false
> > 
> > Looks like this property has a different default value on iOS than macOS is
> > all.
> 
> ```
> /*! @abstract A Boolean value indicating whether link preview is allowed for
> any
>  links inside this WKWebView.
>  @discussion The default value is YES on Mac and iOS.
>  */
> @property (nonatomic) BOOL allowsLinkPreview WK_API_AVAILABLE(macos(10.11),
> ios(9.0));
> ```
> 
> That doc seems to be a lie. Turns out it is true on both platforms.

Never mind, looks like I misread the doc...
Comment 31 Chris Dumez 2021-05-11 11:41:10 PDT
Created attachment 428303 [details]
Patch
Comment 32 Geoffrey Garen 2021-05-11 12:36:35 PDT
Comment on attachment 428303 [details]
Patch

r=me
Comment 33 EWS 2021-05-11 14:39:22 PDT
Committed r277341 (237600@main): <https://commits.webkit.org/237600@main>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 428303 [details].
Comment 34 Ryan Haddad 2021-05-11 20:38:35 PDT
This change has caused all WK2 layout tests to exit with an exception. It seems that there is a ton of this logging during test runs: 
Error: Attempt to call WKPageRef API/SPI on a suspended page, this is a client bug.

https://build.webkit.org/#/builders/70/builds/2492
Comment 35 WebKit Commit Bot 2021-05-11 20:48:01 PDT
Re-opened since this is blocked by bug 225685
Comment 36 Chris Dumez 2021-05-11 21:02:48 PDT
Created attachment 428341 [details]
Patch
Comment 37 Chris Dumez 2021-05-11 21:05:44 PDT
Created attachment 428342 [details]
Patch
Comment 38 Chris Dumez 2021-05-11 21:26:39 PDT
Created attachment 428344 [details]
Patch
Comment 39 EWS 2021-05-11 22:53:23 PDT
Committed r277356 (237614@main): <https://commits.webkit.org/237614@main>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 428344 [details].