Repository: electron/electron (language: cpp)

Issue:
[Bug]: BrowserWindow#loadURL() throw an error when I load same page with different location hash
### Preflight Checklist

- [X] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/master/CONTRIBUTING.md) for this project.
- [X] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/master/CODE_OF_CONDUCT.md) that this project adheres to.
- [X] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a feature request that matches the one I want to file, without success.

### Electron Version

11.2.3

### What operating system are you using?

Other (specify below)

### Operating System Version

Windows10 Pro Version 1909 (Build 18363.1440), MacOS Big Sur 11.2.3 (20D91), Probably not depend on OS

### What arch are you using?

Other (specify below)

### Last Known Working Electron version

N/A

### Expected Behavior

BrowserWindow#loadURL() must finish without throwing any error

### Actual Behavior

BrowserWindow#loadURL() throw an error as below when load same page with different location hash

```
Error: ERR_FAILED (-2) loading 'asset:///test.html#section2'
    at rejectAndCleanup (electron/js2c/browser_init.js:205:1493)
    at Object.stopLoadingListener (electron/js2c/browser_init.js:205:1868)
    at Object.emit (events.js:327:22) {
  errno: -2,
  code: 'ERR_FAILED',
  url: 'asset:///test.html#section2'
}
```

### Testcase Gist URL

https://gist.github.com/eternalharvest/e6b26f8e20d3ebf6048bd50ef8c3eb63

It is not mentioned in the official document, it seems there is option `reloadIgnoringCache`.
And when I call loadURL method with option which reloadIgnoringCache set to true, It seems this error is not happen.

```javascript
// this code works correct (error is not happen)
mainWindow.loadURL('asset:///test.html#section2', { reloadIgnoringCache: true })
    .then(() => console.log('ok'))
    .catch(console.error);
```

Related Issues
* #24113


Discussion / hints:
Ah, I think this is because the `loadURL` promise handling expects either `did-finish-load` or `did-fail-load` to have been emitted before `did-stop-loading` is emitted, which doesn't happen for in-page navigations. We should be able to detect the in-page nature of the navigation and avoid emitting the error.

Relevant code: https://github.com/electron/electron/blob/da9261497eeaed116d84f460303f1872ae00cc9d/lib/browser/api/web-contents.ts#L452-L462
@nornagon  As an additional case, this error seems to happen if the page code has an `onbeforeunload` event handler that blocks the unload. Then the same 'GUEST_VIEW_MANAGER_CALL': Error: ERR_FAILED (-2) happens and the loadURL fails.
Can this problem be fixed?
I was tearing my hair out today with this bug getting Electron to play nicely with Keycloak. Does anybody have a workaround?
One very crude workaround is to trigger a reload from the main thread after the attempted redirect. This will at least load the intended page with the anchors on it.
This issue has been automatically marked as stale. **If this issue is still affecting you, please leave any comment** (for example, "bump"), and we'll keep it open. If you have any new additional information—in particular, if this is still reproducible in the [latest version of Electron](https://www.electronjs.org/releases/stable) or in the [beta](https://www.electronjs.org/releases/beta)—please include it with your comment!
I've just ran into the same error...
@MarshallOfSound Could you please have a look?

I reproduce it with Electron v16.2.3.

1. Open a page with a webview
2. Fire a `loadURL()` with the same url of the loaded page + an anchor and voila.

At least please make those errors catch-able in the place where `loadURL()` was called.


Fix patch:
diff --git a/lib/browser/api/web-contents.ts b/lib/browser/api/web-contents.ts
index 33d515849b1de..7ca4b09e8b686 100644
--- a/lib/browser/api/web-contents.ts
+++ b/lib/browser/api/web-contents.ts
@@ -450,12 +450,14 @@ WebContents.prototype.loadURL = function (url, options) {
     const removeListeners = () => {
       this.removeListener('did-finish-load', finishListener);
       this.removeListener('did-fail-load', failListener);
+      this.removeListener('did-navigate-in-page', finishListener);
       this.removeListener('did-start-navigation', navigationListener);
       this.removeListener('did-stop-loading', stopLoadingListener);
       this.removeListener('destroyed', stopLoadingListener);
     };
     this.on('did-finish-load', finishListener);
     this.on('did-fail-load', failListener);
+    this.on('did-navigate-in-page', finishListener);
     this.on('did-start-navigation', navigationListener);
     this.on('did-stop-loading', stopLoadingListener);
     this.on('destroyed', stopLoadingListener);
diff --git a/spec/api-web-contents-spec.ts b/spec/api-web-contents-spec.ts
index 1cc8f3790f712..b8d43ae935423 100644
--- a/spec/api-web-contents-spec.ts
+++ b/spec/api-web-contents-spec.ts
@@ -362,6 +362,12 @@ describe('webContents module', () => {
       await expect(w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html'))).to.eventually.be.fulfilled();
     });
 
+    it('resolves when navigating within the page', async () => {
+      await w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html'));
+      await new Promise(resolve => setTimeout(resolve));
+      await expect(w.loadURL(w.getURL() + '#foo')).to.eventually.be.fulfilled();
+    });
+
     it('rejects when failing to load a file URL', async () => {
       await expect(w.loadURL('file:non-existent')).to.eventually.be.rejected()
         .and.have.property('code', 'ERR_FILE_NOT_FOUND');


Test patch: