@@ -383,10 +383,12 @@ static void didFailLoadWithError (id self, SEL, WebView* sender, NSError* error,
383383 DelegateConnector (WebBrowserComponent& browserIn,
384384 std::function<void (const var&)> handleNativeEventFnIn,
385385 std::function<std::optional<WebBrowserComponent::Resource> (const String&)> handleResourceRequestFnIn,
386+ std::function<void (const String&)> didFinishNavigationCallbackIn,
386387 const WebBrowserComponent::Options& optionsIn)
387388 : browser (browserIn),
388389 handleNativeEventFn (std::move (handleNativeEventFnIn)),
389390 handleResourceRequestFn (std::move (handleResourceRequestFnIn)),
391+ didFinishNavigationCallback (std::move (didFinishNavigationCallbackIn)),
390392 options (optionsIn)
391393 {
392394 }
@@ -408,10 +410,16 @@ auto handleResourceRequest (const String& url)
408410 return options;
409411 }
410412
413+ void didFinishNavigation (const String& url)
414+ {
415+ didFinishNavigationCallback (url);
416+ }
417+
411418private:
412419 WebBrowserComponent& browser;
413420 std::function<void (const var&)> handleNativeEventFn;
414421 std::function<std::optional<WebBrowserComponent::Resource> (const String&)> handleResourceRequestFn;
422+ std::function<void (const String&)> didFinishNavigationCallback;
415423 WebBrowserComponent::Options options;
416424};
417425
@@ -437,7 +445,7 @@ auto handleResourceRequest (const String& url)
437445 [] (id self, SEL , WKWebView * webview, WKNavigation *)
438446 {
439447 if (auto * connector = getConnector (self))
440- connector->getBrowser (). pageFinishedLoading (nsStringToJuce ([[webview URL ] absoluteString ]));
448+ connector->didFinishNavigation (nsStringToJuce ([[webview URL ] absoluteString ]));
441449 });
442450
443451 addMethod (@selector (webView:didFailNavigation:withError: ),
@@ -811,6 +819,7 @@ void evaluateJavascript (const String&, WebBrowserComponent::EvaluationCallback)
811819#endif
812820
813821class WebBrowserComponent ::Impl::Platform::WKWebViewImpl : public WebBrowserComponent::Impl::PlatformInterface,
822+ private AsyncUpdater,
814823 #if JUCE_MAC
815824 public NSViewComponent
816825 #else
@@ -825,6 +834,10 @@ void evaluateJavascript (const String&, WebBrowserComponent::EvaluationCallback)
825834 delegateConnector (implIn.owner,
826835 [this] (const auto & m) { owner.handleNativeEvent (m); },
827836 [this] (const auto & r) { return owner.handleResourceRequest (r); },
837+ [this] (const auto & url) {
838+ lastLoadedUrl = url;
839+ owner.owner .pageFinishedLoading (url);
840+ },
828841 browserOptions),
829842 allowAccessToEnclosingDirectory (browserOptions.getAppleWkWebViewOptions()
830843 .getAllowAccessToEnclosingDirectory())
@@ -917,27 +930,61 @@ void setWebViewSize (int width, int height) override
917930 setSize (width, height);
918931 }
919932
933+ void handleAsyncUpdate () override
934+ {
935+ auto & browser = owner.owner ;
936+
937+ if (! browser.blankPageShown )
938+ return ;
939+
940+ if (lastRequestedUrl != blankPageUrl)
941+ return ;
942+
943+ // According to our logic, a blank page was shown, and now we are trying to go back to the
944+ // page before that.
945+ //
946+ // But WkWebView seems to be doing some asynchronous batching, and if you send loadRequest:
947+ // and goBack in quick succession, loadRequest: will be ignored entirely and goBack will be
948+ // executed on the backForwardList as if it never happened.
949+ //
950+ // Although none of this is documented, it seems we can reliably query the current contents
951+ // of the backForwardList to see, if we would be navigating away from the URL with the
952+ // actual contents if we executed goBack now, and we can wait until loadRequest: has taken
953+ // effect.
954+ //
955+ // This behaviour initially caused a bug in FL Studio, where the plugin window can become
956+ // invisible and visible again in very rapid succession, when using the TAB button.
957+ if (lastLoadedUrl != blankPageUrl)
958+ {
959+ triggerAsyncUpdate ();
960+ return ;
961+ }
962+
963+ browser.goBack ();
964+ }
965+
920966 void checkWindowAssociation () override
921967 {
922968 auto & browser = owner.owner ;
923969
924970 if (browser.isShowing ())
925971 {
926972 browser.reloadLastURL ();
927-
928- if (browser.blankPageShown )
929- browser.goBack ();
973+ handleAsyncUpdate ();
930974 }
931975 else
932976 {
933- if (browser.unloadPageWhenHidden && ! browser.blankPageShown )
977+ if ( browser.unloadPageWhenHidden
978+ && ! browser.blankPageShown
979+ && lastLoadedUrl.isNotEmpty ()
980+ && lastLoadedUrl != blankPageUrl)
934981 {
935982 // when the component becomes invisible, some stuff like flash
936983 // carries on playing audio, so we need to force it onto a blank
937984 // page to avoid this, (and send it back when it's made visible again).
938985
939986 browser.blankPageShown = true ;
940- goToURL (" about:blank " , nullptr , nullptr );
987+ goToURL (blankPageUrl , nullptr , nullptr );
941988 }
942989 }
943990 }
@@ -1030,6 +1077,7 @@ void goToURL (const String& url,
10301077 }
10311078 else if (NSMutableURLRequest * request = getRequestForURL (url, headers, postData))
10321079 {
1080+ lastRequestedUrl = url;
10331081 [webView.get () loadRequest: request];
10341082 }
10351083 }
@@ -1097,12 +1145,15 @@ void evaluateJavascript (const String& script, WebBrowserComponent::EvaluationCa
10971145 }
10981146
10991147private:
1148+ static inline auto blankPageUrl = " about:blank" ;
1149+
11001150 WebBrowserComponent::Impl& owner;
11011151 DelegateConnector delegateConnector;
11021152 bool allowAccessToEnclosingDirectory = false ;
11031153 LastFocusChange lastFocusChange;
11041154 ObjCObjectHandle<WKWebView *> webView;
11051155 ObjCObjectHandle<id > webViewDelegate;
1156+ String lastRequestedUrl, lastLoadedUrl;
11061157};
11071158
11081159// ==============================================================================
0 commit comments