[Extensions] Don't show the pending URL for chrome.tabs API navigations

For the pdf extension, treat navigations as renderer-initiated. Ideally,
we want this to become default for all extensions, but that risks
breakage.

Based on creis@'s patch at https://codereview.chromium.org/2475033002/.

BUG=660498
TEST=See bug for repro steps.

Review-Url: https://codereview.chromium.org/2492863003
Cr-Commit-Position: refs/heads/master@{#431726}
(cherry picked from commit 2097de33a1f3d04e78d93e9e2f16aad4b97e47d7)

Review URL: https://codereview.chromium.org/2506463003 .

Cr-Commit-Position: refs/branch-heads/2883@{#573}
Cr-Branched-From: 614d31daee2f61b0180df403a8ad43f20b9f6dd7-refs/heads/master@{#423768}
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index b0bc10e7..28d77b0 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -1323,13 +1323,25 @@
     return true;
   }
 
-  web_contents_->GetController().LoadURL(
-      url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
+  bool use_renderer_initiated = false;
+  // For the PDF extension, treat it as renderer-initiated so that it does not
+  // show in the omnibox until it commits.  This avoids URL spoofs since urls
+  // can be opened on behalf of untrusted content.
+  // TODO(devlin|nasko): Make this the default for all extensions.
+  if (extension() && extension()->id() == extension_misc::kPdfExtensionId)
+    use_renderer_initiated = true;
+  NavigationController::LoadURLParams load_params(url);
+  load_params.is_renderer_initiated = use_renderer_initiated;
+  web_contents_->GetController().LoadURLWithParams(load_params);
 
   // The URL of a tab contents never actually changes to a JavaScript URL, so
   // this check only makes sense in other cases.
-  if (!url.SchemeIs(url::kJavaScriptScheme))
-    DCHECK_EQ(url.spec(), web_contents_->GetURL().spec());
+  if (!url.SchemeIs(url::kJavaScriptScheme)) {
+    // The URL should be present in the pending entry, though it may not be
+    // visible in the omnibox until it commits.
+    DCHECK_EQ(
+        url, web_contents_->GetController().GetPendingEntry()->GetVirtualURL());
+  }
 
   return true;
 }
diff --git a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
index 7a5cc4bc..5b8193f 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
@@ -2,11 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/api/tabs/tabs_api.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/test_browser_window.h"
@@ -14,6 +16,7 @@
 #include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/test/browser_side_navigation_test_utils.h"
 #include "content/public/test/web_contents_tester.h"
+#include "extensions/browser/api_test_utils.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/test_util.h"
 
@@ -216,4 +219,54 @@
   }
 }
 
+// Test that using the PDF extension for tab updates is treated as a
+// renderer-initiated navigation. crbug.com/660498
+TEST_F(TabsApiUnitTest, PDFExtensionNavigation) {
+  DictionaryBuilder manifest;
+  manifest.Set("name", "pdfext")
+      .Set("description", "desc")
+      .Set("version", "0.1")
+      .Set("manifest_version", 2)
+      .Set("permissions", ListBuilder().Append("tabs").Build());
+  scoped_refptr<const Extension> extension =
+      ExtensionBuilder()
+          .SetManifest(manifest.Build())
+          .SetID(extension_misc::kPdfExtensionId)
+          .Build();
+  ASSERT_TRUE(extension);
+
+  content::WebContents* web_contents =
+      content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+  ASSERT_TRUE(web_contents);
+  content::WebContentsTester* web_contents_tester =
+      content::WebContentsTester::For(web_contents);
+  const GURL kGoogle("http://www.google.com");
+  web_contents_tester->NavigateAndCommit(kGoogle);
+  EXPECT_EQ(kGoogle, web_contents->GetLastCommittedURL());
+  EXPECT_EQ(kGoogle, web_contents->GetVisibleURL());
+
+  SessionTabHelper::CreateForWebContents(web_contents);
+  int tab_id = SessionTabHelper::IdForTab(web_contents);
+  browser()->tab_strip_model()->AppendWebContents(web_contents, true);
+
+  scoped_refptr<TabsUpdateFunction> function = new TabsUpdateFunction();
+  function->set_extension(extension.get());
+  function->set_browser_context(profile());
+  std::unique_ptr<base::ListValue> args(
+      extension_function_test_utils::ParseList(base::StringPrintf(
+          "[%d, {\"url\":\"http://example.com\"}]", tab_id)));
+  function->SetArgs(args.get());
+  api_test_utils::SendResponseHelper response_helper(function.get());
+  function->RunWithValidation()->Execute();
+
+  EXPECT_EQ(kGoogle, web_contents->GetLastCommittedURL());
+  EXPECT_EQ(kGoogle, web_contents->GetVisibleURL());
+
+  // Clean up.
+  response_helper.WaitForResponse();
+  while (!browser()->tab_strip_model()->empty())
+    browser()->tab_strip_model()->CloseWebContentsAt(0, 0);
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/tabs/tabs_test.cc b/chrome/browser/extensions/api/tabs/tabs_test.cc
index 13f9547..311b01e 100644
--- a/chrome/browser/extensions/api/tabs/tabs_test.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_test.cc
@@ -41,6 +41,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/page_zoom.h"
 #include "content/public/common/url_constants.h"
+#include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
@@ -50,6 +51,7 @@
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "ui/base/window_open_disposition.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
@@ -2114,4 +2116,33 @@
       base::MatchPattern(error, manifest_errors::kCannotAccessChromeUrl));
 }
 
+// Regression test for crbug.com/660498.
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Foo) {
+  ASSERT_TRUE(StartEmbeddedTestServer());
+  content::WebContents* first_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(first_web_contents);
+  chrome::NewTab(browser());
+  content::WebContents* second_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_NE(first_web_contents, second_web_contents);
+  GURL url = embedded_test_server()->GetURL(
+      "/extensions/api_test/tabs/pdf_extension_test.html");
+  content::TestNavigationManager navigation_manager(
+      second_web_contents, GURL("http://www.facebook.com:83"));
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url, WindowOpenDisposition::CURRENT_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  EXPECT_TRUE(navigation_manager.WaitForRequestStart());
+
+  browser()->tab_strip_model()->ActivateTabAt(0, true);
+  EXPECT_EQ(first_web_contents,
+            browser()->tab_strip_model()->GetActiveWebContents());
+  browser()->tab_strip_model()->ActivateTabAt(1, true);
+  EXPECT_EQ(second_web_contents,
+            browser()->tab_strip_model()->GetActiveWebContents());
+
+  EXPECT_EQ(url, second_web_contents->GetVisibleURL());
+}
+
 }  // namespace extensions
diff --git a/chrome/test/data/extensions/api_test/tabs/pdf_extension_test.html b/chrome/test/data/extensions/api_test/tabs/pdf_extension_test.html
new file mode 100644
index 0000000..bc235f8
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/tabs/pdf_extension_test.html
@@ -0,0 +1,41 @@
+<script>
+onbeforeunload=function(){
+        document.write('<b>Welcome to {Some Website}</b>');
+        document.title='OtherWebsite.ltd';
+};
+</script>
+<embed type="application/pdf" src="data:application/pdf;base64,JVBERi0xLjcKIAp0cmFpbGVyCjw8Ci9Sb290IDEgMCBSCj4+CiAKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIKL09wZW5BY3Rpb24gMiAwIFIKPj4KZW5kb2JqCiAKMiAwIG9iago8PAovVHlwZSAvQWN0aW9uCi9TIC9VUkkKL1VSSSAoaHR0cDovL3d3dy5mYWNlYm9vay5jb206ODMpCj4+CmVuZG9iagogCiUlRU9G" width="640" height="480">
+
+
+<!--
+
+Source of the embeded PDF.
+
+-------------------------------------
+%PDF-1.7
+
+trailer
+<<
+/Root 1 0 R
+>>
+
+1 0 obj
+<<
+/Type /Catalog
+/Pages 2 0 R
+/OpenAction 2 0 R
+>>
+endobj
+
+2 0 obj
+<<
+/Type /Action
+/S /URI
+/URI (http://www.facebook.com:83)
+>>
+endobj
+
+%%EOF
+------------------------------------------
+
+-->