| Index: extensions/browser/extension_navigation_throttle.cc | 
| diff --git a/extensions/browser/extension_navigation_throttle.cc b/extensions/browser/extension_navigation_throttle.cc | 
| index 5d026c5d7d649db24e2476ee38bb38b7353f9b26..37ead6b0182a5c7869ac4802805a5671b8542f09 100644 | 
| --- a/extensions/browser/extension_navigation_throttle.cc | 
| +++ b/extensions/browser/extension_navigation_throttle.cc | 
| @@ -34,114 +34,123 @@ ExtensionNavigationThrottle::~ExtensionNavigationThrottle() {} | 
| content::NavigationThrottle::ThrottleCheckResult | 
| ExtensionNavigationThrottle::WillStartRequest() { | 
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
| -  GURL url(navigation_handle()->GetURL()); | 
| content::WebContents* web_contents = navigation_handle()->GetWebContents(); | 
| ExtensionRegistry* registry = | 
| ExtensionRegistry::Get(web_contents->GetBrowserContext()); | 
|  | 
| +  // Is this navigation targeting an extension resource? | 
| +  const GURL& url = navigation_handle()->GetURL(); | 
| +  bool url_has_extension_scheme = url.SchemeIs(kExtensionScheme); | 
| +  url::Origin target_origin(url); | 
| +  const Extension* target_extension = nullptr; | 
| +  if (url_has_extension_scheme) { | 
| +    // "chrome-extension://" URL. | 
| +    target_extension = | 
| +        registry->enabled_extensions().GetExtensionOrAppByURL(url); | 
| +  } else if (target_origin.scheme() == kExtensionScheme) { | 
| +    // "blob:chrome-extension://" or "filesystem:chrome-extension://" URL. | 
| +    DCHECK(url.SchemeIsFileSystem() || url.SchemeIsBlob()); | 
| +    target_extension = | 
| +        registry->enabled_extensions().GetByID(target_origin.host()); | 
| +  } else { | 
| +    // If the navigation is not to a chrome-extension resource, no need to | 
| +    // perform any more checks; it's outside of the purview of this throttle. | 
| +    return content::NavigationThrottle::PROCEED; | 
| +  } | 
| + | 
| +  // If the navigation is to an unknown or disabled extension, block it. | 
| +  if (!target_extension) { | 
| +    // TODO(nick): This yields an unsatisfying error page; use a different error | 
| +    // code once that's supported. https://crbug.com/649869 | 
| +    return content::NavigationThrottle::BLOCK_REQUEST; | 
| +  } | 
| + | 
| if (navigation_handle()->IsInMainFrame()) { | 
| // Block top-level navigations to blob: or filesystem: URLs with extension | 
| // origin from non-extension processes.  See https://crbug.com/645028. | 
| -    bool is_nested_url = url.SchemeIsFileSystem() || url.SchemeIsBlob(); | 
| -    bool is_extension = false; | 
| -    if (registry) { | 
| -      is_extension = !!registry->enabled_extensions().GetExtensionOrAppByURL( | 
| -          navigation_handle()->GetStartingSiteInstance()->GetSiteURL()); | 
| -    } | 
| +    bool current_frame_is_extension_process = | 
| +        !!registry->enabled_extensions().GetExtensionOrAppByURL( | 
| +            navigation_handle()->GetStartingSiteInstance()->GetSiteURL()); | 
|  | 
| -    url::Origin origin(url); | 
| -    if (is_nested_url && origin.scheme() == extensions::kExtensionScheme && | 
| -        !is_extension) { | 
| +    if (!url_has_extension_scheme && !current_frame_is_extension_process) { | 
| // Relax this restriction for apps that use <webview>.  See | 
| // https://crbug.com/652077. | 
| -      const extensions::Extension* extension = | 
| -          registry->enabled_extensions().GetByID(origin.host()); | 
| bool has_webview_permission = | 
| -          extension && | 
| -          extension->permissions_data()->HasAPIPermission( | 
| -              extensions::APIPermission::kWebView); | 
| +          target_extension->permissions_data()->HasAPIPermission( | 
| +              APIPermission::kWebView); | 
| if (!has_webview_permission) | 
| return content::NavigationThrottle::CANCEL; | 
| } | 
|  | 
| -    if (content::IsBrowserSideNavigationEnabled() && | 
| -        url.scheme() == extensions::kExtensionScheme) { | 
| -      // This logic is performed for PlzNavigate sub-resources and for | 
| -      // non-PlzNavigate in | 
| -      // extensions::url_request_util::AllowCrossRendererResourceLoad. | 
| -      const Extension* extension = | 
| -          registry->enabled_extensions().GetExtensionOrAppByURL(url); | 
| -      guest_view::GuestViewBase* guest = | 
| -          guest_view::GuestViewBase::FromWebContents(web_contents); | 
| -      if (guest) { | 
| -        std::string owner_extension_id = guest->owner_host(); | 
| -        const Extension* owner_extension = | 
| -            registry->enabled_extensions().GetByID(owner_extension_id); | 
| - | 
| -        std::string partition_domain, partition_id; | 
| -        bool in_memory; | 
| -        std::string resource_path = url.path(); | 
| -        bool is_guest = WebViewGuest::GetGuestPartitionConfigForSite( | 
| -            navigation_handle()->GetStartingSiteInstance()->GetSiteURL(), | 
| -            &partition_domain, &partition_id, &in_memory); | 
| - | 
| -        bool allowed = true; | 
| -        url_request_util::AllowCrossRendererResourceLoadHelper( | 
| -            is_guest, extension, owner_extension, partition_id, resource_path, | 
| -            navigation_handle()->GetPageTransition(), &allowed); | 
| -        if (!allowed) | 
| -          return content::NavigationThrottle::BLOCK_REQUEST; | 
| -      } | 
| +    guest_view::GuestViewBase* guest = | 
| +        guest_view::GuestViewBase::FromWebContents(web_contents); | 
| +    if (content::IsBrowserSideNavigationEnabled() && url_has_extension_scheme && | 
| +        guest) { | 
| +      // This variant of this logic applies to PlzNavigate top-level | 
| +      // navigations. It is performed for subresources, and for non-PlzNavigate | 
| +      // top navigations, in url_request_util::AllowCrossRendererResourceLoad. | 
| +      const std::string& owner_extension_id = guest->owner_host(); | 
| +      const Extension* owner_extension = | 
| +          registry->enabled_extensions().GetByID(owner_extension_id); | 
| + | 
| +      std::string partition_domain; | 
| +      std::string partition_id; | 
| +      bool in_memory = false; | 
| +      bool is_guest = WebViewGuest::GetGuestPartitionConfigForSite( | 
| +          navigation_handle()->GetStartingSiteInstance()->GetSiteURL(), | 
| +          &partition_domain, &partition_id, &in_memory); | 
| + | 
| +      bool allowed = true; | 
| +      url_request_util::AllowCrossRendererResourceLoadHelper( | 
| +          is_guest, target_extension, owner_extension, partition_id, url.path(), | 
| +          navigation_handle()->GetPageTransition(), &allowed); | 
| +      if (!allowed) | 
| +        return content::NavigationThrottle::BLOCK_REQUEST; | 
| } | 
|  | 
| return content::NavigationThrottle::PROCEED; | 
| } | 
|  | 
| -  // Now enforce web_accessible_resources for navigations. Top-level navigations | 
| -  // should always be allowed. | 
| - | 
| -  // If the navigation is not to a chrome-extension:// URL, no need to perform | 
| -  // any more checks. | 
| -  if (!url.SchemeIs(extensions::kExtensionScheme)) | 
| -    return content::NavigationThrottle::PROCEED; | 
| +  // This is a subframe navigation to a |target_extension| resource. | 
| +  // Enforce the web_accessible_resources restriction. | 
| +  content::RenderFrameHost* parent = navigation_handle()->GetParentFrame(); | 
|  | 
| -  // The subframe which is navigated needs to have all of its ancestors be | 
| -  // at the same origin, otherwise the resource needs to be explicitly listed | 
| -  // in web_accessible_resources. | 
| -  content::RenderFrameHost* ancestor = navigation_handle()->GetParentFrame(); | 
| +  // Look to see if all ancestors belong to |target_extension|. If not, | 
| +  // then the web_accessible_resource restriction applies. | 
| bool external_ancestor = false; | 
| -  while (ancestor) { | 
| -    if (ancestor->GetLastCommittedURL().GetOrigin() != url.GetOrigin()) { | 
| -      // Ignore DevTools, as it is allowed to embed extension pages. | 
| -      if (!ancestor->GetLastCommittedURL().SchemeIs( | 
| -              content::kChromeDevToolsScheme)) { | 
| -        external_ancestor = true; | 
| -        break; | 
| -      } | 
| -    } | 
| -    ancestor = ancestor->GetParent(); | 
| +  for (auto* ancestor = parent; ancestor; ancestor = ancestor->GetParent()) { | 
| +    // Look for a match on the last committed origin. This handles the | 
| +    // common case, and the about:blank case. | 
| +    if (ancestor->GetLastCommittedOrigin() == target_origin) | 
| +      continue; | 
| +    // Look for an origin match with the last committed URL. This handles the | 
| +    // case of sandboxed extension resources, which commit with a null origin, | 
| +    // but are permitted to load non-webaccessible extension resources in | 
| +    // subframes. | 
| +    if (url::Origin(ancestor->GetLastCommittedURL()) == target_origin) | 
| +      continue; | 
| +    // Ignore DevTools, as it is allowed to embed extension pages. | 
| +    if (ancestor->GetLastCommittedURL().SchemeIs( | 
| +            content::kChromeDevToolsScheme)) | 
| +      continue; | 
| + | 
| +    // Otherwise, we have an external ancestor. | 
| +    external_ancestor = true; | 
| +    break; | 
| } | 
|  | 
| -  if (!external_ancestor) | 
| -    return content::NavigationThrottle::PROCEED; | 
| - | 
| -  // Since there was at least one origin different than the navigation URL, | 
| -  // explicitly check for the resource in web_accessible_resources. | 
| -  std::string resource_path = url.path(); | 
| -  if (!registry) | 
| -    return content::NavigationThrottle::BLOCK_REQUEST; | 
| - | 
| -  const extensions::Extension* extension = | 
| -      registry->enabled_extensions().GetByID(url.host()); | 
| -  if (!extension) | 
| -    return content::NavigationThrottle::BLOCK_REQUEST; | 
| +  if (external_ancestor) { | 
| +    // Cancel navigations to nested URLs, to match the main frame behavior. | 
| +    if (!url_has_extension_scheme) | 
| +      return content::NavigationThrottle::CANCEL; | 
|  | 
| -  if (WebAccessibleResourcesInfo::IsResourceWebAccessible(extension, | 
| -                                                          resource_path)) { | 
| -    return content::NavigationThrottle::PROCEED; | 
| +    // |url| must be in the manifest's "web_accessible_resources" section. | 
| +    if (!WebAccessibleResourcesInfo::IsResourceWebAccessible(target_extension, | 
| +                                                             url.path())) | 
| +      return content::NavigationThrottle::BLOCK_REQUEST; | 
| } | 
|  | 
| -  return content::NavigationThrottle::BLOCK_REQUEST; | 
| +  return content::NavigationThrottle::PROCEED; | 
| } | 
|  | 
| const char* ExtensionNavigationThrottle::GetNameForLogging() { | 
|  |