This is an automated email from the ASF dual-hosted git repository. GitToTheHub pushed a commit to branch pr-fix-load-page-after-clear-data-and-cookies in repository https://gitbox.apache.org/repos/asf/cordova-plugin-inappbrowser.git
commit 4a57b8fdcd4b469b4a59ff8c5c3a4e3bd61644ee Author: Manuel Beck <[email protected]> AuthorDate: Sun May 10 16:54:28 2026 +0200 fix: defer navigation until async cookie or data clearing completes - Fixes #521: Cookies were persisting despite clearcache/clearsessioncache options because navigation started before async clearing operations finished, causing a race condition. --- src/android/InAppBrowser.java | 44 ++++++++++++--- src/ios/CDVWKInAppBrowser.m | 121 ++++++++++++++++++++++++++++++++---------- 2 files changed, 130 insertions(+), 35 deletions(-) diff --git a/src/android/InAppBrowser.java b/src/android/InAppBrowser.java index 6a039c6..f0bff1f 100644 --- a/src/android/InAppBrowser.java +++ b/src/android/InAppBrowser.java @@ -743,6 +743,36 @@ public class InAppBrowser extends CordovaPlugin { return value; } + /** + * Clears cookies according to the active options and invokes completion when finished. + * + * Cookie clearing APIs are asynchronous (API 22+), so loadUrl must run in the callback + * to avoid a race where navigation starts before cookie deletion is complete. + */ + private void clearCookies(final Runnable completion) { + final CookieManager cookieManager = CookieManager.getInstance(); + + if (clearAllCache) { + cookieManager.removeAllCookies(new ValueCallback<Boolean>() { + @Override + public void onReceiveValue(Boolean value) { + cookieManager.flush(); + completion.run(); + } + }); + } else if (clearSessionCache) { + cookieManager.removeSessionCookies(new ValueCallback<Boolean>() { + @Override + public void onReceiveValue(Boolean value) { + cookieManager.flush(); + completion.run(); + } + }); + } else { + completion.run(); + } + } + private View createCloseButton(int id) { View _close; Resources activityRes = cordova.getActivity().getResources(); @@ -1026,16 +1056,16 @@ public class InAppBrowser extends CordovaPlugin { } settings.setDomStorageEnabled(true); - if (clearAllCache) { - CookieManager.getInstance().removeAllCookie(); - } else if (clearSessionCache) { - CookieManager.getInstance().removeSessionCookie(); - } - // Enable Thirdparty Cookies CookieManager.getInstance().setAcceptThirdPartyCookies(inAppWebView,true); - inAppWebView.loadUrl(url); + // Delay navigation until cookie clearing is complete (or skipped). + clearCookies(new Runnable() { + @Override + public void run() { + inAppWebView.loadUrl(url); + } + }); inAppWebView.setId(Integer.valueOf(6)); inAppWebView.getSettings().setLoadWithOverviewMode(true); inAppWebView.getSettings().setUseWideViewPort(useWideViewPort); diff --git a/src/ios/CDVWKInAppBrowser.m b/src/ios/CDVWKInAppBrowser.m index fea8bea..b826c92 100644 --- a/src/ios/CDVWKInAppBrowser.m +++ b/src/ios/CDVWKInAppBrowser.m @@ -120,8 +120,6 @@ { CDVInAppBrowserOptions *browserOptions = [CDVInAppBrowserOptions parseOptions:options]; - [self clearWebsiteDataByOptions:browserOptions]; - if (self.inAppBrowserViewController == nil) { self.inAppBrowserViewController = [[CDVWKInAppBrowserViewController alloc] initWithBrowserOptions: browserOptions andSettings:self.commandDelegate.settings]; self.inAppBrowserViewController.navigationDelegate = self; @@ -176,10 +174,19 @@ } _waitForBeforeload = ![_beforeload isEqualToString:@""]; - [self.inAppBrowserViewController navigateTo:url]; - if (!browserOptions.hidden) { - [self show:nil withNoAnimate:browserOptions.hidden]; - } + __weak CDVWKInAppBrowser *weakSelf = self; + // Delay the initial navigation until requested clearing operations complete. + [self clearWebsiteDataByOptions:browserOptions completionHandler:^{ + __strong __typeof(weakSelf) strongSelf = weakSelf; + if (strongSelf == nil || strongSelf.inAppBrowserViewController == nil) { + return; + } + + [strongSelf.inAppBrowserViewController navigateTo:url]; + if (!browserOptions.hidden) { + [strongSelf show:nil withNoAnimate:browserOptions.hidden]; + } + }]; } /** @@ -187,42 +194,100 @@ * This will clear the data on the Cordova WebView also. * * @param browserOptions The options specifying which types of data to clear. + * @param completionHandler Called after all requested clearing operations are complete. */ -- (void)clearWebsiteDataByOptions:(CDVInAppBrowserOptions *)browserOptions +- (void)clearWebsiteDataByOptions:(CDVInAppBrowserOptions *)browserOptions completionHandler:(void (^)(void))completionHandler { // NOTE: [WKWebsiteDataStore defaultDataStore] will get the default data store of the app, // which is shared between all WKWebViews, which means also the Cordova WebView WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; + // Option precedence: + // 1) cleardata: clears all web data (including cookies), so cookie options are skipped. + // 2) clearcache: clears all cookies (session + persistent). + // 3) clearsessioncache: clears only session cookies. + // 4) no clear options: continue immediately. if (browserOptions.cleardata) { - [dataStore fetchDataRecordsOfTypes:WKWebsiteDataStore.allWebsiteDataTypes completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) { - [dataStore removeDataOfTypes:WKWebsiteDataStore.allWebsiteDataTypes forDataRecords:records completionHandler:^{ - NSLog(@"Removed all WKWebView data"); - // Create new WKProcessPool - if (@available(iOS 15.0, *)) { - // Since iOS 15 WKProcessPool is deprecated and has no effect - } else { + [self clearData:dataStore completionHandler:completionHandler]; + return; + } + + if (browserOptions.clearcache) { + [self clearCookie:dataStore sessionOnly:NO completionHandler:completionHandler]; + return; + } + + if (browserOptions.clearsessioncache) { + [self clearCookie:dataStore sessionOnly:YES completionHandler:completionHandler]; + return; + } + + if (completionHandler != nil) { + completionHandler(); + } +} + +/** + * Clears all WKWebView website data types (cookies, local storage, IndexedDB, cache, etc.). + */ +- (void)clearData:(WKWebsiteDataStore *)dataStore completionHandler:(void (^)(void))completionHandler +{ + [dataStore fetchDataRecordsOfTypes:WKWebsiteDataStore.allWebsiteDataTypes completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) { + [dataStore removeDataOfTypes:WKWebsiteDataStore.allWebsiteDataTypes forDataRecords:records completionHandler:^{ + NSLog(@"Removed all WKWebView data"); + // Create new WKProcessPool + if (@available(iOS 15.0, *)) { + // Since iOS 15 WKProcessPool is deprecated and has no effect + } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - // Set for iOS 14 and below a new process pool - self.inAppBrowserViewController.webView.configuration.processPool = [[WKProcessPool alloc] init]; // create new process pool to flush all data + // Set for iOS 14 and below a new process pool + self.inAppBrowserViewController.webView.configuration.processPool = [[WKProcessPool alloc] init]; // create new process pool to flush all data #pragma clang diagnostic pop } - }]; - }]; - } - // Deletes all cookies or session cookies - if (browserOptions.clearcache || browserOptions.clearsessioncache) { - WKHTTPCookieStore *cookieStore = dataStore.httpCookieStore; - [cookieStore getAllCookies:^(NSArray *cookies) { - for (NSHTTPCookie *cookie in cookies) { - if (browserOptions.clearcache || (browserOptions.clearsessioncache && cookie.sessionOnly)) { - [cookieStore deleteCookie:cookie completionHandler:nil]; - } + if (completionHandler != nil) { + completionHandler(); } }]; - } + }]; +} + +/** + * Clears cookies from the shared cookie store. + * sessionOnly=NO: clear all cookies. + * sessionOnly=YES: clear only session cookies. + */ +- (void)clearCookie:(WKWebsiteDataStore *)dataStore sessionOnly:(BOOL)sessionOnly completionHandler:(void (^)(void))completionHandler +{ + WKHTTPCookieStore *cookieStore = dataStore.httpCookieStore; + [cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> *cookies) { + NSMutableArray<NSHTTPCookie *> *cookiesToDelete = [NSMutableArray array]; + for (NSHTTPCookie *cookie in cookies) { + if (!sessionOnly || cookie.sessionOnly) { + [cookiesToDelete addObject:cookie]; + } + } + + // No cookies available, return + if (cookiesToDelete.count == 0) { + if (completionHandler != nil) { + completionHandler(); + } + return; + } + + // [WKHTTPCookieStore deleteCookie] is async, so complete only after the final callback. + __block NSUInteger pendingCookieDeletes = cookiesToDelete.count; + for (NSHTTPCookie *cookie in cookiesToDelete) { + [cookieStore deleteCookie:cookie completionHandler:^{ + pendingCookieDeletes--; + if (pendingCookieDeletes == 0 && completionHandler != nil) { + completionHandler(); + } + }]; + } + }]; } - (void)show:(CDVInvokedUrlCommand *)command --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
