New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.

Issue 883550 link

Starred by 2 users

Issue metadata

Status: Fixed
Owner:
Closed: Sep 21
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: ----
Pri: 3
Type: Bug



Sign in to add a comment

Navigation fails on window.open() with empty URL from a hosted app

Project Member Reported by alex...@chromium.org, Sep 12

Issue description

What steps will reproduce the problem?

(1) Go to a URL covered by an installed hosted app.  I used https://drive.google.com/drive/my-drive with the Drive app installed (apdfllckaahabafndbhieahigkjlhalf)

(2) Execute:
  var w = window.open('','_blank','width=500,height=500');
  setTimeout(() => w.location = 'http://www.asdf.com', 0);

What is the expected result?
The popup opens and navigates to http://www.asdf.com/

What happens instead?
Most of the time on a recent Linux release build, the popup opens and stays at about:blank.

This was discovered internally as b/113914893.

The problem here is that RenderFrameImpl::DecidePolicyForNavigation unnecessarily triggers an OpenURL IPC to the browser process.  That's due to the should_fork logic in ChromeExtensionsRendererClient::ShouldFork().  Interestingly, it's not because of CrossesExtensionExtents(), which returns false due to the hosted apps workaround (should_consider_workaround) in CrossesExtensionProcessBoundary() (where the old_url corresponds to the hosted app and the new empty URL does not).  Instead, it's due to this subsequent check:

  // If this is a reload, check whether it has the wrong process type.  We
  // should send it to the browser if it's an extension URL (e.g., hosted app)
  // in a normal process, or if it's a process for an extension that has been
  // uninstalled.  Without --site-per-process mode, we never fork processes for
  // subframes, so this check only makes sense for top-level frames.
  // TODO(alexmos,nasko): Figure out how this check should work when reloading
  // subframes in --site-per-process mode.
  if (!frame->Parent() && GURL(frame->GetDocument().Url()) == url) {
    if (is_extension_url != IsStandaloneExtensionProcess())
      return true;
  }

where the two URLs are the same (empty).

Later on, in RFHI::OnOpenURL, the blank URL gets translated to "about:blank" by FilterURL.

Note that if the repro had window.open('about:blank') instead, this wouldn't apply, due to this check in DecidePolicyForNavigation which skips the whole should_fork computation for target URLs that have the about: scheme:

  // Detect when we're crossing a permission-based boundary (e.g. into or out of
  // an extension or app origin, leaving a WebUI page, etc). We only care about
  // top-level navigations (not iframes). But we sometimes navigate to
  // about:blank to clear a tab, and we want to still allow that.
  if (!frame_->Parent() && !url.SchemeIs(url::kAboutScheme)) {
    ... compute should_fork ...
  }

So, an 'about:blank' URL doesn't trigger OpenURL, but an empty URL does.

Having the OpenURL IPC by itself shouldn't really be harmful, but it turns out that it creates a race with the subsequent navigation to asdf.com.  Quoting nasko@'s description (with example.com being asdf.com in the repro above):

"In the cases where it works correctly, the about:blank navigation started by OpenURL is waiting for the renderer to run the beforeunload handler and that takes longer than the start of the navigation to example.com. The start of example.com navigation cancels the existing about:blank navigation and it no longer commits and lets example.com finish navigating.
In the failing case, the beforeunload handler executes quickly enough that it sends an ack to the browser process before the example.com navigation has started. This causes the browser process to send a commit IPC to the renderer for about:blank and while waiting for the commit IPC we start the navigation to example.com. When the commit for about:blank arrives, the example.com navigation is cancelled."

A trivial fix would be to exclude empty URLs from the should_fork branch similarly to about: URLs, or find a way to translate the empty URL to about:blank sooner, but the better way is to clean up and remove ShouldFork altogether - see issue 883549.

 
Cc: piatek@google.com
Cc: -alex...@chromium.org
Owner: alex...@chromium.org
Status: Started (was: Available)
I've got a CL in progress which should fix this: https://chromium-review.googlesource.com/c/chromium/src/+/1226075
Project Member

Comment 3 by bugdroid1@chromium.org, Sep 14

The following revision refers to this bug:
  https://chromium.googlesource.com/chromium/src.git/+/d97d105fb1575487cfce16135bbc1faf9642e14b

commit d97d105fb1575487cfce16135bbc1faf9642e14b
Author: Alex Moshchuk <alexmos@chromium.org>
Date: Fri Sep 14 23:24:25 2018

Remove hosted app reload logic from ShouldFork.

This CL removes the logic that triggered process swaps in
ChromeExtensionsRendererClient::ShouldFork() for the case when the
main frame reloads a URL for which a hosted app was just installed or
uninstalled.  This logic isn't needed with --site-per-process, as that
mode will already force a process transfer.  This is covered by
AppApiTest.ReloadIntoAppProcess and
AppApiTest.ReloadIntoAppProcessWithJavaScript, which still pass with
that logic removed.

This CL also makes a fix on the browser side to keep this logic
working even without --site-per-process, by unconditionally checking
HasWrongProcessForURL() and forcing a process transfer if needed in
IsRendererTransferNeededForNavigation().  This is possible because
with PlzNavigate we will always go to the browser process and check
for transfers, even without --site-per-process.  With that fix, the
above two tests also pass without --site-per-process.

The above tweak also has a side effect of swapping processes when an
app opens a cross-site, non-app popup (also made possible by work on
 issue 794315 ) in non-site-per-process mode, which seems acceptable and
desirable - no reason for the cross-site popup to be in a process with
app permissions.

Bug:  883550 , 883549, 718516
Test: AppApiTest.ReloadIntoAppProcess and AppApiTest.ReloadIntoAppProcessWithJavaScript keep working, with and without --site-per-process.
Change-Id: Ibac63fff3a36318fabceb593ca7ae7967eefad89
Reviewed-on: https://chromium-review.googlesource.com/1226075
Reviewed-by: Nasko Oskov <nasko@chromium.org>
Reviewed-by: Devlin <rdevlin.cronin@chromium.org>
Commit-Queue: Alex Moshchuk <alexmos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#591521}
[modify] https://crrev.com/d97d105fb1575487cfce16135bbc1faf9642e14b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
[modify] https://crrev.com/d97d105fb1575487cfce16135bbc1faf9642e14b/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
[modify] https://crrev.com/d97d105fb1575487cfce16135bbc1faf9642e14b/content/browser/frame_host/render_frame_host_manager.cc

Status: Fixed (was: Started)
I've confirmed that the repro in #0 is fixed on Mac Canary with r591521, and blois@ also confirmed that this fixes the internal repro, so marking as fixed.

Sign in to add a comment