Extracted from #23662, https://github.com/electron/electron/issues/23662#issuecomment-632344005, per https://github.com/electron/electron/issues/23662#issuecomment-637077040 (cc @nornagon).

Extensions get loaded for `http://` and `https://` protocols, but don't work for `file://` protocol, which is commonly used.

This also affects devtools extensions.

Appears to be an undocumented regression from 8.x.

Chrome by default behaves the same, but it has an `Allow access to file URLs` switch (the `fileAccess` flag) on extension page at `chrome://extensions/`, which enables the extension to run on `file://` urls (which can be verified on `Content scripts` tab).
Any hint how to do that in Electron?
React-devtools works on `file://` protocol in Chrome with that flag enabled, but does not work in Electron since 9.0.0, as there are no ways to toggle that setting.

### 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 for an issue that matches the one I want to file, without success.

### Issue Details

* **Electron Version:**
  * 9.0.0, 9.0.2
* **Operating System:**
  * `Linux xps 5.6.14-arch1-1 #1 SMP PREEMPT Wed, 20 May 2020 20:43:19 +0000 x86_64 GNU/Linux`
* **Last Known Working Electron version:**
  * 8.3.1

### Expected Behavior

Installed extensions should load for `file://` protocol automatically, or/and there should be an opt-in/out-out for that (like opt-in in Chrome).

Extension content script present in `Content scripts` tab.

### Actual Behavior

Extensions are not loaded if the for pages requested over `file://` protocol.

No extension content script present in `Content scripts` tab.

### To Reproduce

`index.js`:
```js
const { app, session, BrowserWindow } = require('electron');
const path = require('path');

(async () => {
  await app.whenReady()
  await session.defaultSession.loadExtension(path.join(__dirname, 'extensions/test'))
  const bw = new BrowserWindow({webPreferences: {sandbox: true}})
  //await bw.loadURL('about:blank')
  //await bw.loadURL('https://example.org/')
  await bw.loadURL(`file://${path.resolve('simple.html')}`)
  bw.show()
  bw.openDevTools()
})()
```

Put anything in `simple.html`.

Example extension (but you can use any other, e.g. https://github.com/electron/electron/tree/master/spec-main/fixtures/extensions/chrome-api or `react-devtools`)

`extensions/test/main.js`
```js
console.log('extension loaded')
```

`extensions/test/manifest.json`
```json
{
  "name": "test",
  "version": "1.0",
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["main.js"],
      "run_at": "document_start"
    }
  ],
  "permissions": [ "file:///*", "http://*/*", "https://*/*" ],
  "manifest_version": 2
}
```

### Screenshots
`https://`:
![Screenshot_20200608_142638](https://user-images.githubusercontent.com/291301/84025366-18875e00-a994-11ea-8762-259236998f65.png)

`file://`:
![Screenshot_20200608_142102](https://user-images.githubusercontent.com/291301/84024876-52a43000-a993-11ea-9ac5-e44ef60ad616.png)


### Additional Information

This breaks devtools extension and is a part (if not the main reason) behind #23662.

shell/browser/api/electron_api_session.cc
gin_helper::ErrorThrower thrower,
    const std::vector<std::string>& languages) {
  base::ListValue language_codes;
  for (const std::string& lang : languages) {
    std::string code = spellcheck::GetCorrespondingSpellCheckLanguage(lang);
    if (code.empty()) {
      thrower.ThrowError("Invalid language code provided: \"" + lang +
                         "\" is not a valid language code");
      return;
    }
    language_codes.AppendString(code);
  }
  browser_context_->prefs()->Set(spellcheck::prefs::kSpellCheckDictionaries,
                                 language_codes);
  
  browser_context_->prefs()->SetBoolean(spellcheck::prefs::kSpellCheckEnable,
                                        !languages.empty());
}
void SetSpellCheckerDictionaryDownloadURL(gin_helper::ErrorThrower thrower,
                                          const GURL& url) {
  if (!url.is_valid()) {
    thrower.ThrowError(
        "The URL you provided to setSpellCheckerDictionaryDownloadURL is not a "
        "valid URL");
    return;
  }
  SpellcheckHunspellDictionary::SetBaseDownloadURL(url);
}
v8::Local<v8::Promise> Session::ListWordsInSpellCheckerDictionary() {