Update ownership semantics for WebContentsDelegate::AddWebContents.

This CL is a refactor and has no intended behavior change.

This CL updates AddWebContents to use explicit ownership semantics. This CL
fixes some of the subclasses to have proper ownership semantics, and leaves
behind some TODOs for other subclasses.

Bug: 832879, 674318
Change-Id: I6294e591e9166dfb0c029eb78648c17216075dab
TBR: [email protected], [email protected], [email protected]
Reviewed-on: https://chromium-review.googlesource.com/1028631
Reviewed-by: Richard Coles <[email protected]>
Reviewed-by: Antoine Labour <[email protected]>
Reviewed-by: Nico Weber <[email protected]>
Reviewed-by: Ken Rockot <[email protected]>
Commit-Queue: Erik Chen <[email protected]>
Cr-Commit-Position: refs/heads/master@{#554450}
diff --git a/android_webview/browser/aw_web_contents_delegate.cc b/android_webview/browser/aw_web_contents_delegate.cc
index 3ce5a0f..3431d263 100644
--- a/android_webview/browser/aw_web_contents_delegate.cc
+++ b/android_webview/browser/aw_web_contents_delegate.cc
@@ -121,12 +121,13 @@
       params.capture);
 }
 
-void AwWebContentsDelegate::AddNewContents(WebContents* source,
-                                           WebContents* new_contents,
-                                           WindowOpenDisposition disposition,
-                                           const gfx::Rect& initial_rect,
-                                           bool user_gesture,
-                                           bool* was_blocked) {
+void AwWebContentsDelegate::AddNewContents(
+    WebContents* source,
+    std::unique_ptr<WebContents> new_contents,
+    WindowOpenDisposition disposition,
+    const gfx::Rect& initial_rect,
+    bool user_gesture,
+    bool* was_blocked) {
   JNIEnv* env = AttachCurrentThread();
 
   bool is_dialog = disposition == WindowOpenDisposition::NEW_POPUP;
@@ -144,17 +145,22 @@
     // it. The source AwContents takes ownership of the new WebContents
     // until then, and when the callback is made we will swap the WebContents
     // out into the new AwContents.
+    WebContents* raw_new_contents = new_contents.get();
     AwContents::FromWebContents(source)->SetPendingWebContentsForPopup(
-        base::WrapUnique(new_contents));
+        std::move(new_contents));
+    // It's possible that SetPendingWebContentsForPopup deletes |new_contents|,
+    // but it only does so asynchronously, so it's safe to use a raw pointer
+    // here.
     // Hide the WebContents for the pop up now, we will show it again
     // when the user calls us back with an AwContents to use to show it.
-    new_contents->WasHidden();
+    raw_new_contents->WasHidden();
   } else {
     // The embedder has forgone their chance to display this popup
     // window, so we're done with the WebContents now. We use
     // DeleteSoon as WebContentsImpl may call methods on |new_contents|
     // after this method returns.
-    base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, new_contents);
+    base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
+                                                    std::move(new_contents));
   }
 
   if (was_blocked) {
diff --git a/android_webview/browser/aw_web_contents_delegate.h b/android_webview/browser/aw_web_contents_delegate.h
index 86be13e..b6b66384 100644
--- a/android_webview/browser/aw_web_contents_delegate.h
+++ b/android_webview/browser/aw_web_contents_delegate.h
@@ -31,7 +31,7 @@
   void RunFileChooser(content::RenderFrameHost* render_frame_host,
                       const content::FileChooserParams& params) override;
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
diff --git a/chrome/browser/android/document/document_web_contents_delegate.cc b/chrome/browser/android/document/document_web_contents_delegate.cc
index 410c125..bd91615 100644
--- a/chrome/browser/android/document/document_web_contents_delegate.cc
+++ b/chrome/browser/android/document/document_web_contents_delegate.cc
@@ -29,7 +29,7 @@
 
 void DocumentWebContentsDelegate::AddNewContents(
     content::WebContents* source,
-    content::WebContents* new_contents,
+    std::unique_ptr<content::WebContents> new_contents,
     WindowOpenDisposition disposition,
     const gfx::Rect& initial_pos,
     bool user_gesture,
diff --git a/chrome/browser/android/document/document_web_contents_delegate.h b/chrome/browser/android/document/document_web_contents_delegate.h
index bc989ce..3537cb06 100644
--- a/chrome/browser/android/document/document_web_contents_delegate.h
+++ b/chrome/browser/android/document/document_web_contents_delegate.h
@@ -30,7 +30,7 @@
 
   // Overridden from WebContentsDelegate.
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_pos,
                       bool user_gesture,
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.cc b/chrome/browser/android/tab_web_contents_delegate_android.cc
index 378ef958f..0463d7c 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.cc
+++ b/chrome/browser/android/tab_web_contents_delegate_android.cc
@@ -398,7 +398,7 @@
 
 void TabWebContentsDelegateAndroid::AddNewContents(
     WebContents* source,
-    WebContents* new_contents,
+    std::unique_ptr<WebContents> new_contents,
     WindowOpenDisposition disposition,
     const gfx::Rect& initial_rect,
     bool user_gesture,
@@ -411,9 +411,9 @@
   // At this point the |new_contents| is beyond the popup blocker, but we use
   // the same logic for determining if the popup tracker needs to be attached.
   if (source && PopupBlockerTabHelper::ConsiderForPopupBlocking(disposition))
-    PopupTracker::CreateForWebContents(new_contents, source);
+    PopupTracker::CreateForWebContents(new_contents.get(), source);
 
-  TabHelpers::AttachTabHelpers(new_contents);
+  TabHelpers::AttachTabHelpers(new_contents.get());
 
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
@@ -433,8 +433,11 @@
 
   if (was_blocked)
     *was_blocked = !handled;
-  if (!handled)
-    delete new_contents;
+
+  // When handled is |true|, ownership has been passed to java, which in turn
+  // creates a new TabAndroid instance to own the WebContents.
+  if (handled)
+    new_contents.release();
 }
 
 blink::WebSecurityStyle TabWebContentsDelegateAndroid::GetSecurityStyle(
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.h b/chrome/browser/android/tab_web_contents_delegate_android.h
index efffb9d..892ed29 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.h
+++ b/chrome/browser/android/tab_web_contents_delegate_android.h
@@ -77,7 +77,7 @@
       const content::OpenURLParams& params) override;
   bool ShouldResumeRequestsForCreatedWindow() override;
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
diff --git a/chrome/browser/background/background_contents.cc b/chrome/browser/background/background_contents.cc
index 49a8ac07..1eca1ed 100644
--- a/chrome/browser/background/background_contents.cc
+++ b/chrome/browser/background/background_contents.cc
@@ -150,14 +150,15 @@
 }
 
 // Forward requests to add a new WebContents to our delegate.
-void BackgroundContents::AddNewContents(WebContents* source,
-                                        WebContents* new_contents,
-                                        WindowOpenDisposition disposition,
-                                        const gfx::Rect& initial_rect,
-                                        bool user_gesture,
-                                        bool* was_blocked) {
-  delegate_->AddWebContents(
-      new_contents, disposition, initial_rect, user_gesture, was_blocked);
+void BackgroundContents::AddNewContents(
+    WebContents* source,
+    std::unique_ptr<WebContents> new_contents,
+    WindowOpenDisposition disposition,
+    const gfx::Rect& initial_rect,
+    bool user_gesture,
+    bool* was_blocked) {
+  delegate_->AddWebContents(std::move(new_contents), disposition, initial_rect,
+                            user_gesture, was_blocked);
 }
 
 bool BackgroundContents::IsNeverVisible(content::WebContents* web_contents) {
diff --git a/chrome/browser/background/background_contents.h b/chrome/browser/background/background_contents.h
index 29db768..2048d19 100644
--- a/chrome/browser/background/background_contents.h
+++ b/chrome/browser/background/background_contents.h
@@ -45,11 +45,12 @@
     // WebContents to a suitable container (e.g. browser) or to show it if it's
     // a popup window. If |was_blocked| is non-NULL, then |*was_blocked| will be
     // set to true if the popup gets blocked, and left unchanged otherwise.
-    virtual void AddWebContents(content::WebContents* new_contents,
-                                WindowOpenDisposition disposition,
-                                const gfx::Rect& initial_rect,
-                                bool user_gesture,
-                                bool* was_blocked) = 0;
+    virtual void AddWebContents(
+        std::unique_ptr<content::WebContents> new_contents,
+        WindowOpenDisposition disposition,
+        const gfx::Rect& initial_rect,
+        bool user_gesture,
+        bool* was_blocked) = 0;
 
    protected:
     virtual ~Delegate() {}
@@ -77,7 +78,7 @@
   bool ShouldSuppressDialogs(content::WebContents* source) override;
   void DidNavigateMainFramePostCommit(content::WebContents* tab) override;
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
diff --git a/chrome/browser/background/background_contents_service.cc b/chrome/browser/background/background_contents_service.cc
index 7bd71ae..901f2d5 100644
--- a/chrome/browser/background/background_contents_service.cc
+++ b/chrome/browser/background/background_contents_service.cc
@@ -815,7 +815,7 @@
 }
 
 void BackgroundContentsService::AddWebContents(
-    WebContents* new_contents,
+    std::unique_ptr<WebContents> new_contents,
     WindowOpenDisposition disposition,
     const gfx::Rect& initial_rect,
     bool user_gesture,
@@ -823,7 +823,7 @@
   Browser* browser = chrome::FindLastActiveWithProfile(
       Profile::FromBrowserContext(new_contents->GetBrowserContext()));
   if (browser) {
-    chrome::AddWebContents(browser, nullptr, new_contents, disposition,
-                           initial_rect, user_gesture);
+    chrome::AddWebContents(browser, nullptr, std::move(new_contents),
+                           disposition, initial_rect, user_gesture);
   }
 }
diff --git a/chrome/browser/background/background_contents_service.h b/chrome/browser/background/background_contents_service.h
index 7fe46083..d71b548 100644
--- a/chrome/browser/background/background_contents_service.h
+++ b/chrome/browser/background/background_contents_service.h
@@ -97,7 +97,7 @@
   std::vector<BackgroundContents*> GetBackgroundContents() const;
 
   // BackgroundContents::Delegate implementation.
-  void AddWebContents(content::WebContents* new_contents,
+  void AddWebContents(std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index 4f2b9681..8140cbd 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -1138,12 +1138,16 @@
 }
 
 void DevToolsWindow::AddNewContents(WebContents* source,
-                                    WebContents* new_contents,
+                                    std::unique_ptr<WebContents> new_contents,
                                     WindowOpenDisposition disposition,
                                     const gfx::Rect& initial_rect,
                                     bool user_gesture,
                                     bool* was_blocked) {
-  if (new_contents == toolbox_web_contents_) {
+  if (new_contents.get() == toolbox_web_contents_) {
+    // TODO(erikchen): Fix ownership semantics for WebContents.
+    // https://crbug.com/832879.
+    new_contents.release();
+
     toolbox_web_contents_->SetDelegate(
         new DevToolsToolboxDelegate(toolbox_web_contents_,
                                     inspected_contents_observer_.get()));
@@ -1160,8 +1164,8 @@
   WebContents* inspected_web_contents = GetInspectedWebContents();
   if (inspected_web_contents) {
     inspected_web_contents->GetDelegate()->AddNewContents(
-        source, new_contents, disposition, initial_rect, user_gesture,
-        was_blocked);
+        source, std::move(new_contents), disposition, initial_rect,
+        user_gesture, was_blocked);
   }
 }
 
diff --git a/chrome/browser/devtools/devtools_window.h b/chrome/browser/devtools/devtools_window.h
index c8cb1bed..ad48848 100644
--- a/chrome/browser/devtools/devtools_window.h
+++ b/chrome/browser/devtools/devtools_window.h
@@ -293,7 +293,7 @@
   // content::WebContentsDelegate:
   void ActivateContents(content::WebContents* contents) override;
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 5535fb5..15654c8 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -5472,19 +5472,21 @@
   restored_entry->SetPageState(entry->GetPageState());
 
   WebContents::CreateParams params(tab->GetBrowserContext());
-  WebContents* tab2 = WebContents::Create(params);
-  tab->GetDelegate()->AddNewContents(nullptr, tab2,
+  std::unique_ptr<WebContents> tab2 =
+      base::WrapUnique(WebContents::Create(params));
+  WebContents* raw_tab2 = tab2.get();
+  tab->GetDelegate()->AddNewContents(nullptr, std::move(tab2),
                                      WindowOpenDisposition::NEW_FOREGROUND_TAB,
                                      gfx::Rect(), false, nullptr);
   std::vector<std::unique_ptr<NavigationEntry>> entries;
   entries.push_back(std::move(restored_entry));
-  content::TestNavigationObserver observer(tab2);
-  tab2->GetController().Restore(
+  content::TestNavigationObserver observer(raw_tab2);
+  raw_tab2->GetController().Restore(
       entries.size() - 1, content::RestoreType::LAST_SESSION_EXITED_CLEANLY,
       &entries);
-  tab2->GetController().LoadIfNecessary();
+  raw_tab2->GetController().LoadIfNecessary();
   observer.Wait();
-  CheckAuthenticatedState(tab2, AuthState::NONE);
+  CheckAuthenticatedState(raw_tab2, AuthState::NONE);
 }
 
 void SetupRestoredTabWithNavigation(
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.cc b/chrome/browser/ui/apps/chrome_app_delegate.cc
index a3fd28a..8fedda5e 100644
--- a/chrome/browser/ui/apps/chrome_app_delegate.cc
+++ b/chrome/browser/ui/apps/chrome_app_delegate.cc
@@ -125,11 +125,18 @@
   NewWindowContentsDelegate() {}
   ~NewWindowContentsDelegate() override {}
 
+  void BecomeOwningDeletageOf(
+      std::unique_ptr<content::WebContents> web_contents) {
+    web_contents->SetDelegate(this);
+    owned_contents_.push_back(std::move(web_contents));
+  }
+
   content::WebContents* OpenURLFromTab(
       content::WebContents* source,
       const content::OpenURLParams& params) override;
 
  private:
+  std::vector<std::unique_ptr<content::WebContents>> owned_contents_;
   DISALLOW_COPY_AND_ASSIGN(NewWindowContentsDelegate);
 };
 
@@ -144,7 +151,16 @@
     // NewWindowContentsDelegate actually sees the WebContents. Here ownership
     // is captured and passed to OpenURLAfterCheckIsDefaultBrowser(), which
     // destroys it after the default browser worker completes.
-    std::unique_ptr<content::WebContents> source_ptr(source);
+    std::unique_ptr<content::WebContents> owned_source;
+    for (auto it = owned_contents_.begin(); it != owned_contents_.end(); ++it) {
+      if (it->get() == source) {
+        owned_source = std::move(*it);
+        owned_contents_.erase(it);
+        break;
+      }
+    }
+    DCHECK(owned_source);
+
     // Object lifetime notes: StartCheckIsDefault() takes lifetime ownership of
     // check_if_default_browser_worker and will clean up after the asynchronous
     // tasks.
@@ -152,7 +168,7 @@
         check_if_default_browser_worker =
             new shell_integration::DefaultBrowserWorker(
                 base::Bind(&OpenURLAfterCheckIsDefaultBrowser,
-                           base::Passed(&source_ptr), params));
+                           base::Passed(&owned_source), params));
     check_if_default_browser_worker->StartCheckIsDefault();
   }
   return NULL;
@@ -226,19 +242,22 @@
   return OpenURLFromTabInternal(context, params);
 }
 
-void ChromeAppDelegate::AddNewContents(content::BrowserContext* context,
-                                       content::WebContents* new_contents,
-                                       WindowOpenDisposition disposition,
-                                       const gfx::Rect& initial_rect,
-                                       bool user_gesture) {
+void ChromeAppDelegate::AddNewContents(
+    content::BrowserContext* context,
+    std::unique_ptr<content::WebContents> new_contents,
+    WindowOpenDisposition disposition,
+    const gfx::Rect& initial_rect,
+    bool user_gesture) {
   if (!disable_external_open_for_testing_) {
     // We don't really want to open a window for |new_contents|, but we need to
     // capture its intended navigation. Here we give ownership to the
     // NewWindowContentsDelegate, which will dispose of the contents once
     // a navigation is captured.
-    new_contents->SetDelegate(new_window_contents_delegate_.get());
+    new_window_contents_delegate_->BecomeOwningDeletageOf(
+        std::move(new_contents));
     return;
   }
+
   chrome::ScopedTabbedBrowserDisplayer displayer(
       Profile::FromBrowserContext(context));
   // Force all links to open in a new tab, even if they were trying to open a
@@ -246,8 +265,8 @@
   disposition = disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB
                     ? disposition
                     : WindowOpenDisposition::NEW_FOREGROUND_TAB;
-  chrome::AddWebContents(displayer.browser(), NULL, new_contents, disposition,
-                         initial_rect, user_gesture);
+  chrome::AddWebContents(displayer.browser(), NULL, std::move(new_contents),
+                         disposition, initial_rect, user_gesture);
 }
 
 content::ColorChooser* ChromeAppDelegate::ShowColorChooser(
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.h b/chrome/browser/ui/apps/chrome_app_delegate.h
index 830c910c..bdebbac9 100644
--- a/chrome/browser/ui/apps/chrome_app_delegate.h
+++ b/chrome/browser/ui/apps/chrome_app_delegate.h
@@ -48,7 +48,7 @@
       content::WebContents* source,
       const content::OpenURLParams& params) override;
   void AddNewContents(content::BrowserContext* context,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture) override;
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index b0727a2..8e59ba2 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1512,7 +1512,7 @@
 }
 
 void Browser::AddNewContents(WebContents* source,
-                             WebContents* new_contents,
+                             std::unique_ptr<WebContents> new_contents,
                              WindowOpenDisposition disposition,
                              const gfx::Rect& initial_rect,
                              bool user_gesture,
@@ -1520,9 +1520,9 @@
   // At this point the |new_contents| is beyond the popup blocker, but we use
   // the same logic for determining if the popup tracker needs to be attached.
   if (source && PopupBlockerTabHelper::ConsiderForPopupBlocking(disposition))
-    PopupTracker::CreateForWebContents(new_contents, source);
-  chrome::AddWebContents(this, source, new_contents, disposition, initial_rect,
-                         user_gesture);
+    PopupTracker::CreateForWebContents(new_contents.get(), source);
+  chrome::AddWebContents(this, source, std::move(new_contents), disposition,
+                         initial_rect, user_gesture);
 }
 
 void Browser::ActivateContents(WebContents* contents) {
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 7e7c49b9..ea03a29 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -587,7 +587,7 @@
                               content::InvalidateTypes changed_flags) override;
   void VisibleSecurityStateChanged(content::WebContents* source) override;
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
diff --git a/chrome/browser/ui/browser_tabstrip.cc b/chrome/browser/ui/browser_tabstrip.cc
index 5d3c9a44..2db923f 100644
--- a/chrome/browser/ui/browser_tabstrip.cc
+++ b/chrome/browser/ui/browser_tabstrip.cc
@@ -48,7 +48,7 @@
 
 void AddWebContents(Browser* browser,
                     content::WebContents* source_contents,
-                    content::WebContents* new_contents,
+                    std::unique_ptr<content::WebContents> new_contents,
                     WindowOpenDisposition disposition,
                     const gfx::Rect& initial_rect,
                     bool user_gesture) {
@@ -57,8 +57,7 @@
   // Can't create a new contents for the current tab - invalid case.
   DCHECK(disposition != WindowOpenDisposition::CURRENT_TAB);
 
-  // TODO(erikchen): Fix ownership semantics. https://crbug.com/832879.
-  NavigateParams params(browser, base::WrapUnique(new_contents));
+  NavigateParams params(browser, std::move(new_contents));
   params.source_contents = source_contents;
   params.disposition = disposition;
   params.window_bounds = initial_rect;
diff --git a/chrome/browser/ui/browser_tabstrip.h b/chrome/browser/ui/browser_tabstrip.h
index adc1090..c8d1a35 100644
--- a/chrome/browser/ui/browser_tabstrip.h
+++ b/chrome/browser/ui/browser_tabstrip.h
@@ -36,7 +36,7 @@
 // the initial position and size.
 void AddWebContents(Browser* browser,
                     content::WebContents* source_contents,
-                    content::WebContents* new_contents,
+                    std::unique_ptr<content::WebContents> new_contents,
                     WindowOpenDisposition disposition,
                     const gfx::Rect& initial_rect,
                     bool user_gesture);
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac_browsertest.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac_browsertest.mm
index 0965d5fa..3b65983 100644
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac_browsertest.mm
+++ b/chrome/browser/ui/cocoa/constrained_window/constrained_window_mac_browsertest.mm
@@ -98,7 +98,7 @@
   create_params.initially_hidden = true;
   std::unique_ptr<content::WebContents> web_contents(
       content::WebContents::Create(create_params));
-  chrome::AddWebContents(browser(), NULL, web_contents.release(),
+  chrome::AddWebContents(browser(), NULL, std::move(web_contents),
                          WindowOpenDisposition::NEW_BACKGROUND_TAB, gfx::Rect(),
                          false);
   content::WebContents* tab2 =
diff --git a/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc b/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc
index 58ee37f..b3d3f6a 100644
--- a/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc
+++ b/chrome/browser/ui/webui/web_dialog_web_contents_delegate_unittest.cc
@@ -89,11 +89,11 @@
 }
 
 TEST_F(WebDialogWebContentsDelegateTest, AddNewContentsForegroundTabTest) {
-  WebContents* contents =
-      WebContentsTester::CreateTestWebContents(profile(), NULL);
+  std::unique_ptr<WebContents> contents = base::WrapUnique(
+      WebContentsTester::CreateTestWebContents(profile(), NULL));
   test_web_contents_delegate_->AddNewContents(
-      NULL, contents, WindowOpenDisposition::NEW_FOREGROUND_TAB, gfx::Rect(),
-      false, NULL);
+      NULL, std::move(contents), WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      gfx::Rect(), false, NULL);
   // This should create a new foreground tab in the existing browser.
   EXPECT_EQ(1, browser()->tab_strip_model()->count());
   EXPECT_EQ(1U, chrome::GetTotalBrowserCount());
diff --git a/components/offline_pages/content/background_loader/background_loader_contents.cc b/components/offline_pages/content/background_loader/background_loader_contents.cc
index 6f8f80cc..0e66117 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents.cc
+++ b/components/offline_pages/content/background_loader/background_loader_contents.cc
@@ -88,7 +88,7 @@
 
 void BackgroundLoaderContents::AddNewContents(
     content::WebContents* source,
-    content::WebContents* new_contents,
+    std::unique_ptr<content::WebContents> new_contents,
     WindowOpenDisposition disposition,
     const gfx::Rect& initial_rect,
     bool user_gesture,
diff --git a/components/offline_pages/content/background_loader/background_loader_contents.h b/components/offline_pages/content/background_loader/background_loader_contents.h
index 53863a7..0cfdf25 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents.h
+++ b/components/offline_pages/content/background_loader/background_loader_contents.h
@@ -71,7 +71,7 @@
       content::SessionStorageNamespace* session_storage_namespace) override;
 
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
diff --git a/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc b/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
index 645ecd19..6910403 100644
--- a/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
+++ b/components/offline_pages/content/background_loader/background_loader_contents_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/offline_pages/content/background_loader/background_loader_contents.h"
 
 #include "base/synchronization/waitable_event.h"
+#include "content/public/browser/web_contents.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -139,7 +140,8 @@
 TEST_F(BackgroundLoaderContentsTest, ShouldNotAddNewContents) {
   bool blocked;
   contents()->AddNewContents(
-      nullptr /* source */, nullptr /* new_contents */,
+      nullptr /* source */,
+      std::unique_ptr<content::WebContents>() /* new_contents */,
       WindowOpenDisposition::CURRENT_TAB /* disposition */,
       gfx::Rect() /* initial_rect */, false /* user_gesture */,
       &blocked /* was_blocked */);
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index d17d8d5..271a81a 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1870,8 +1870,12 @@
   // Clear a pending contents that has been closed before being shown.
   for (auto iter = pending_contents_.begin(); iter != pending_contents_.end();
        ++iter) {
-    if (iter->second != web_contents)
+    if (iter->second.get() != web_contents)
       continue;
+
+    // Someone else has deleted the WebContents. That should never happen!
+    // TODO(erikchen): Fix semantics here. https://crbug.com/832879.
+    iter->second.release();
     pending_contents_.erase(iter);
     return;
   }
@@ -2409,31 +2413,33 @@
   create_params.renderer_initiated_creation =
       main_frame_route_id != MSG_ROUTING_NONE;
 
-  WebContentsImpl* new_contents = nullptr;
+  std::unique_ptr<WebContents> new_contents;
   if (!is_guest) {
     create_params.context = view_->GetNativeView();
     create_params.initial_size = GetContainerBounds().size();
-    new_contents = static_cast<WebContentsImpl*>(
-        WebContents::Create(create_params));
+    new_contents.reset(
+        static_cast<WebContentsImpl*>(WebContents::Create(create_params)));
   }  else {
-    new_contents = GetBrowserPluginGuest()->CreateNewGuestWindow(create_params);
+    new_contents = base::WrapUnique(
+        GetBrowserPluginGuest()->CreateNewGuestWindow(create_params));
   }
-  new_contents->GetController().SetSessionStorageNamespace(
-      partition_id,
-      session_storage_namespace);
+  WebContentsImpl* raw_new_contents =
+      static_cast<WebContentsImpl*>(new_contents.get());
+  raw_new_contents->GetController().SetSessionStorageNamespace(
+      partition_id, session_storage_namespace);
 
   // If the new frame has a name, make sure any SiteInstances that can find
   // this named frame have proxies for it.  Must be called after
   // SetSessionStorageNamespace, since this calls CreateRenderView, which uses
   // GetSessionStorageNamespace.
   if (!params.frame_name.empty())
-    new_contents->GetRenderManager()->CreateProxiesForNewNamedFrame();
+    raw_new_contents->GetRenderManager()->CreateProxiesForNewNamedFrame();
 
   // Save the window for later if we're not suppressing the opener (since it
   // will be shown immediately).
   if (!params.opener_suppressed) {
     if (!is_guest) {
-      WebContentsView* new_view = new_contents->view_.get();
+      WebContentsView* new_view = raw_new_contents->view_.get();
 
       // TODO(brettw): It seems bogus that we have to call this function on the
       // newly created object and give it one of its own member variables.
@@ -2443,20 +2449,21 @@
     // Save the created window associated with the route so we can show it
     // later.
     DCHECK_NE(MSG_ROUTING_NONE, main_frame_widget_route_id);
-    pending_contents_[std::make_pair(
-        render_process_id, main_frame_widget_route_id)] = new_contents;
-    AddDestructionObserver(new_contents);
+    pending_contents_[std::make_pair(render_process_id,
+                                     main_frame_widget_route_id)] =
+        std::move(new_contents);
+    AddDestructionObserver(raw_new_contents);
   }
 
   if (delegate_) {
     delegate_->WebContentsCreated(this, render_process_id,
                                   opener->GetRoutingID(), params.frame_name,
-                                  params.target_url, new_contents);
+                                  params.target_url, raw_new_contents);
   }
 
   if (opener) {
     for (auto& observer : observers_) {
-      observer.DidOpenRequestedURL(new_contents, opener, params.target_url,
+      observer.DidOpenRequestedURL(raw_new_contents, opener, params.target_url,
                                    params.referrer, params.disposition,
                                    ui::PAGE_TRANSITION_LINK,
                                    false,  // started_from_context_menu
@@ -2473,18 +2480,19 @@
     // When the opener is suppressed, the original renderer cannot access the
     // new window.  As a result, we need to show and navigate the window here.
     bool was_blocked = false;
+
+    base::WeakPtr<WebContentsImpl> weak_new_contents =
+        raw_new_contents->weak_factory_.GetWeakPtr();
     if (delegate_) {
       gfx::Rect initial_rect;
-      base::WeakPtr<WebContentsImpl> weak_new_contents =
-          new_contents->weak_factory_.GetWeakPtr();
 
-      delegate_->AddNewContents(
-          this, new_contents, params.disposition, initial_rect,
-          params.user_gesture, &was_blocked);
-
+      delegate_->AddNewContents(this, std::move(new_contents),
+                                params.disposition, initial_rect,
+                                params.user_gesture, &was_blocked);
       if (!weak_new_contents)
         return;  // The delegate deleted |new_contents| during AddNewContents().
     }
+
     if (!was_blocked) {
       OpenURLParams open_params(params.target_url, params.referrer,
                                 WindowOpenDisposition::CURRENT_TAB,
@@ -2494,11 +2502,12 @@
 
       if (delegate_ && !is_guest &&
           !delegate_->ShouldResumeRequestsForCreatedWindow()) {
+        DCHECK(weak_new_contents);
         // We are in asynchronous add new contents path, delay opening url
-        new_contents->delayed_open_url_params_.reset(
+        weak_new_contents->delayed_open_url_params_.reset(
             new OpenURLParams(open_params));
       } else {
-        new_contents->OpenURL(open_params);
+        weak_new_contents->OpenURL(open_params);
       }
     }
   }
@@ -2555,24 +2564,26 @@
                                         WindowOpenDisposition disposition,
                                         const gfx::Rect& initial_rect,
                                         bool user_gesture) {
-  WebContentsImpl* popup =
+  std::unique_ptr<WebContents> popup =
       GetCreatedWindow(process_id, main_frame_widget_route_id);
   if (popup) {
+    WebContentsImpl* raw_popup = static_cast<WebContentsImpl*>(popup.get());
     WebContentsDelegate* delegate = GetDelegate();
-    popup->is_resume_pending_ = true;
+    raw_popup->is_resume_pending_ = true;
     if (!delegate || delegate->ShouldResumeRequestsForCreatedWindow())
-      popup->ResumeLoadingCreatedWebContents();
+      raw_popup->ResumeLoadingCreatedWebContents();
 
+    base::WeakPtr<WebContentsImpl> weak_popup =
+        raw_popup->weak_factory_.GetWeakPtr();
     if (delegate) {
-      base::WeakPtr<WebContentsImpl> weak_popup =
-          popup->weak_factory_.GetWeakPtr();
-      delegate->AddNewContents(this, popup, disposition, initial_rect,
-                               user_gesture, nullptr);
+      delegate->AddNewContents(this, std::move(popup), disposition,
+                               initial_rect, user_gesture, nullptr);
       if (!weak_popup)
         return;  // The delegate deleted |popup| during AddNewContents().
     }
 
-    RenderWidgetHostImpl* rwh = popup->GetMainFrame()->GetRenderWidgetHost();
+    RenderWidgetHostImpl* rwh =
+        weak_popup->GetMainFrame()->GetRenderWidgetHost();
     DCHECK_EQ(main_frame_widget_route_id, rwh->GetRoutingID());
     rwh->Send(new ViewMsg_Move_ACK(rwh->GetRoutingID()));
   }
@@ -2633,7 +2644,7 @@
   render_widget_host_impl->set_allow_privileged_mouse_lock(is_fullscreen);
 }
 
-WebContentsImpl* WebContentsImpl::GetCreatedWindow(
+std::unique_ptr<WebContents> WebContentsImpl::GetCreatedWindow(
     int process_id,
     int main_frame_widget_route_id) {
   auto key = std::make_pair(process_id, main_frame_widget_route_id);
@@ -2644,17 +2655,18 @@
   if (iter == pending_contents_.end())
     return nullptr;
 
-  WebContentsImpl* new_contents = iter->second;
+  std::unique_ptr<WebContents> new_contents = std::move(iter->second);
   pending_contents_.erase(key);
-  RemoveDestructionObserver(new_contents);
+  WebContentsImpl* raw_new_contents =
+      static_cast<WebContentsImpl*>(new_contents.get());
+  RemoveDestructionObserver(raw_new_contents);
 
   // Don't initialize the guest WebContents immediately.
-  if (BrowserPluginGuest::IsGuest(new_contents))
+  if (BrowserPluginGuest::IsGuest(raw_new_contents))
     return new_contents;
 
   if (!new_contents->GetMainFrame()->GetProcess()->HasConnection() ||
       !new_contents->GetMainFrame()->GetView()) {
-    // TODO(nick): http://crbug.com/674318 -- Who deletes |new_contents|?
     return nullptr;
   }
 
@@ -4120,8 +4132,8 @@
   new_frame_entry->SetPageState(new_page_state);
 
   // Create a new WebContents, which is used to display the source code.
-  WebContentsImpl* view_source_contents =
-      static_cast<WebContentsImpl*>(Create(CreateParams(GetBrowserContext())));
+  std::unique_ptr<WebContents> view_source_contents =
+      base::WrapUnique(Create(CreateParams(GetBrowserContext())));
 
   // Restore the previously created NavigationEntry.
   std::vector<std::unique_ptr<NavigationEntry>> navigation_entries;
@@ -4133,7 +4145,7 @@
   gfx::Rect initial_rect;
   constexpr bool kUserGesture = true;
   bool ignored_was_blocked;
-  delegate_->AddNewContents(this, view_source_contents,
+  delegate_->AddNewContents(this, std::move(view_source_contents),
                             WindowOpenDisposition::NEW_FOREGROUND_TAB,
                             initial_rect, kUserGesture, &ignored_was_blocked);
   // Note that the |delegate_| could have deleted |view_source_contents| during
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index a00271b..828b877e 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -1261,8 +1261,8 @@
   // Finds the new WebContentsImpl by |main_frame_widget_route_id|, initializes
   // it for renderer-initiated creation, and returns it. Note that this can only
   // be called once as this call also removes it from the internal map.
-  WebContentsImpl* GetCreatedWindow(int process_id,
-                                    int main_frame_widget_route_id);
+  std::unique_ptr<WebContents> GetCreatedWindow(int process_id,
+                                                int main_frame_widget_route_id);
 
   // Sends a Page message IPC.
   void SendPageMessage(IPC::Message* msg);
@@ -1381,7 +1381,8 @@
   // Tracks created WebContentsImpl objects that have not been shown yet. They
   // are identified by the process ID and routing ID passed to CreateNewWindow.
   typedef std::pair<int, int> ProcessRoutingIdPair;
-  std::map<ProcessRoutingIdPair, WebContentsImpl*> pending_contents_;
+  std::map<ProcessRoutingIdPair, std::unique_ptr<WebContents>>
+      pending_contents_;
 
   // This map holds widgets that were created on behalf of the renderer but
   // haven't been shown yet.
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index d808e44d..619bbf9 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -1481,12 +1481,12 @@
   }
 
   void AddNewContents(WebContents* source,
-                      WebContents* new_contents,
+                      std::unique_ptr<WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
                       bool* was_blocked) override {
-    popup_.reset(new_contents);
+    popup_ = std::move(new_contents);
 
     if (waiting_for_ == kNewContents)
       run_loop_->Quit();
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index f517013..17f86a5 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -2667,29 +2667,38 @@
 // Test that if a pending contents is deleted before it is shown, we don't
 // crash.
 TEST_F(WebContentsImplTest, PendingContentsDestroyed) {
-  std::unique_ptr<TestWebContents> other_contents(
-      static_cast<TestWebContents*>(CreateTestWebContents()));
-  contents()->AddPendingContents(other_contents.get());
+  std::unique_ptr<WebContentsImpl> other_contents(
+      static_cast<WebContentsImpl*>(CreateTestWebContents()));
+  content::TestWebContents* raw_other_contents =
+      static_cast<TestWebContents*>(other_contents.get());
+  contents()->AddPendingContents(std::move(other_contents));
   RenderWidgetHost* widget =
-      other_contents->GetMainFrame()->GetRenderWidgetHost();
+      raw_other_contents->GetMainFrame()->GetRenderWidgetHost();
   int process_id = widget->GetProcess()->GetID();
   int widget_id = widget->GetRoutingID();
-  other_contents.reset();
+
+  // TODO(erikchen): Fix ownership semantics of WebContents. Nothing should be
+  // able to delete it beside from the owner. https://crbug.com/832879.
+  delete raw_other_contents;
   EXPECT_EQ(nullptr, contents()->GetCreatedWindow(process_id, widget_id));
 }
 
 TEST_F(WebContentsImplTest, PendingContentsShown) {
-  std::unique_ptr<TestWebContents> other_contents(
-      static_cast<TestWebContents*>(CreateTestWebContents()));
-  contents()->AddPendingContents(other_contents.get());
+  std::unique_ptr<WebContents> other_contents(
+      static_cast<WebContents*>(CreateTestWebContents()));
+  content::WebContents* raw_other_contents = other_contents.get();
+  content::TestWebContents* test_web_contents =
+      static_cast<content::TestWebContents*>(other_contents.get());
+  contents()->AddPendingContents(std::move(other_contents));
+
   RenderWidgetHost* widget =
-      other_contents->GetMainFrame()->GetRenderWidgetHost();
+      test_web_contents->GetMainFrame()->GetRenderWidgetHost();
   int process_id = widget->GetProcess()->GetID();
   int widget_id = widget->GetRoutingID();
 
   // The first call to GetCreatedWindow pops it off the pending list.
-  EXPECT_EQ(other_contents.get(),
-            contents()->GetCreatedWindow(process_id, widget_id));
+  EXPECT_EQ(raw_other_contents,
+            contents()->GetCreatedWindow(process_id, widget_id).get());
   // A second call should return nullptr, verifying that it's been forgotten.
   EXPECT_EQ(nullptr, contents()->GetCreatedWindow(process_id, widget_id));
 }
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 28cf408..0111890e 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -18,6 +18,7 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/bluetooth_chooser.h"
 #include "content/public/browser/invalidate_type.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/common/media_stream_request.h"
 #include "content/public/common/previews_state.h"
 #include "content/public/common/window_container_type.mojom.h"
@@ -48,7 +49,6 @@
 class RenderWidgetHost;
 class SessionStorageNamespace;
 class SiteInstance;
-class WebContents;
 class WebContentsImpl;
 struct ContextMenuParams;
 struct DropData;
@@ -121,7 +121,7 @@
   // |*was_blocked| will be set to true if the popup gets blocked, and left
   // unchanged otherwise.
   virtual void AddNewContents(WebContents* source,
-                              WebContents* new_contents,
+                              std::unique_ptr<WebContents> new_contents,
                               WindowOpenDisposition disposition,
                               const gfx::Rect& initial_rect,
                               bool user_gesture,
diff --git a/content/shell/browser/shell.cc b/content/shell/browser/shell.cc
index 4366ed9..f963e02 100644
--- a/content/shell/browser/shell.cc
+++ b/content/shell/browser/shell.cc
@@ -73,9 +73,9 @@
   DISALLOW_COPY_AND_ASSIGN(DevToolsWebContentsObserver);
 };
 
-Shell::Shell(WebContents* web_contents)
-    : WebContentsObserver(web_contents),
-      web_contents_(web_contents),
+Shell::Shell(std::unique_ptr<WebContents> web_contents)
+    : WebContentsObserver(web_contents.get()),
+      web_contents_(std::move(web_contents)),
       devtools_frontend_(nullptr),
       is_fullscreen_(false),
       window_(nullptr),
@@ -131,9 +131,10 @@
   web_contents_->SetDelegate(nullptr);
 }
 
-Shell* Shell::CreateShell(WebContents* web_contents,
+Shell* Shell::CreateShell(std::unique_ptr<WebContents> web_contents,
                           const gfx::Size& initial_size) {
-  Shell* shell = new Shell(web_contents);
+  WebContents* raw_web_contents = web_contents.get();
+  Shell* shell = new Shell(std::move(web_contents));
   shell->PlatformCreateWindow(initial_size.width(), initial_size.height());
 
   shell->PlatformSetContents();
@@ -144,14 +145,14 @@
   // here, because they will be forgotten after a cross-process navigation. Use
   // RenderFrameCreated or RenderViewCreated instead.
   if (switches::IsRunLayoutTestSwitchPresent()) {
-    web_contents->GetMutableRendererPrefs()->use_custom_colors = false;
-    web_contents->GetRenderViewHost()->SyncRendererPrefs();
+    raw_web_contents->GetMutableRendererPrefs()->use_custom_colors = false;
+    raw_web_contents->GetRenderViewHost()->SyncRendererPrefs();
   }
 
 #if BUILDFLAG(ENABLE_WEBRTC)
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kForceWebRtcIPHandlingPolicy)) {
-    web_contents->GetMutableRendererPrefs()->webrtc_ip_handling_policy =
+    raw_web_contents->GetMutableRendererPrefs()->webrtc_ip_handling_policy =
         command_line->GetSwitchValueASCII(
             switches::kForceWebRtcIPHandlingPolicy);
   }
@@ -208,8 +209,10 @@
         blink::kPresentationReceiverSandboxFlags;
   }
   create_params.initial_size = AdjustWindowSize(initial_size);
-  WebContents* web_contents = WebContents::Create(create_params);
-  Shell* shell = CreateShell(web_contents, create_params.initial_size);
+  std::unique_ptr<WebContents> web_contents =
+      base::WrapUnique(WebContents::Create(create_params));
+  Shell* shell =
+      CreateShell(std::move(web_contents), create_params.initial_size);
   if (!url.is_empty())
     shell->LoadURL(url);
   return shell;
@@ -277,14 +280,15 @@
 }
 
 void Shell::AddNewContents(WebContents* source,
-                           WebContents* new_contents,
+                           std::unique_ptr<WebContents> new_contents,
                            WindowOpenDisposition disposition,
                            const gfx::Rect& initial_rect,
                            bool user_gesture,
                            bool* was_blocked) {
-  CreateShell(new_contents, AdjustWindowSize(initial_rect.size()));
+  WebContents* raw_new_contents = new_contents.get();
+  CreateShell(std::move(new_contents), AdjustWindowSize(initial_rect.size()));
   if (switches::IsRunLayoutTestSwitchPresent())
-    SecondaryTestWindowObserver::CreateForWebContents(new_contents);
+    SecondaryTestWindowObserver::CreateForWebContents(raw_new_contents);
 }
 
 void Shell::GoBackOrForward(int offset) {
diff --git a/content/shell/browser/shell.h b/content/shell/browser/shell.h
index 65af9a78..2efc6bd 100644
--- a/content/shell/browser/shell.h
+++ b/content/shell/browser/shell.h
@@ -124,7 +124,7 @@
   WebContents* OpenURLFromTab(WebContents* source,
                               const OpenURLParams& params) override;
   void AddNewContents(WebContents* source,
-                      WebContents* new_contents,
+                      std::unique_ptr<WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
@@ -183,10 +183,10 @@
 
   class DevToolsWebContentsObserver;
 
-  explicit Shell(WebContents* web_contents);
+  explicit Shell(std::unique_ptr<WebContents> web_contents);
 
   // Helper to create a new Shell given a newly created WebContents.
-  static Shell* CreateShell(WebContents* web_contents,
+  static Shell* CreateShell(std::unique_ptr<WebContents> web_contents,
                             const gfx::Size& initial_size);
 
   // Helper for one time initialization of application
diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc
index 594d8ab..9b60472 100644
--- a/content/test/test_web_contents.cc
+++ b/content/test/test_web_contents.cc
@@ -347,12 +347,14 @@
       static_cast<WebContentsImpl*>(opener)->GetFrameTree()->root());
 }
 
-void TestWebContents::AddPendingContents(TestWebContents* contents) {
+void TestWebContents::AddPendingContents(
+    std::unique_ptr<WebContents> contents) {
   // This is normally only done in WebContentsImpl::CreateNewWindow.
   ProcessRoutingIdPair key(contents->GetRenderViewHost()->GetProcess()->GetID(),
                            contents->GetRenderViewHost()->GetRoutingID());
-  pending_contents_[key] = contents;
-  AddDestructionObserver(contents);
+  WebContentsImpl* raw_contents = static_cast<WebContentsImpl*>(contents.get());
+  AddDestructionObserver(raw_contents);
+  pending_contents_[key] = std::move(contents);
 }
 
 void TestWebContents::ExpectSetHistoryOffsetAndLength(int history_offset,
diff --git a/content/test/test_web_contents.h b/content/test/test_web_contents.h
index ce72d69..72eaa3a 100644
--- a/content/test/test_web_contents.h
+++ b/content/test/test_web_contents.h
@@ -126,7 +126,7 @@
   }
 
   // Allows us to simulate that a contents was created via CreateNewWindow.
-  void AddPendingContents(TestWebContents* contents);
+  void AddPendingContents(std::unique_ptr<WebContents> contents);
 
   // Establish expected arguments for |SetHistoryOffsetAndLength()|. When
   // |SetHistoryOffsetAndLength()| is called, the arguments are compared
diff --git a/extensions/browser/app_window/app_delegate.h b/extensions/browser/app_window/app_delegate.h
index a513cba..30c8fb38 100644
--- a/extensions/browser/app_window/app_delegate.h
+++ b/extensions/browser/app_window/app_delegate.h
@@ -48,11 +48,12 @@
       content::BrowserContext* context,
       content::WebContents* source,
       const content::OpenURLParams& params) = 0;
-  virtual void AddNewContents(content::BrowserContext* context,
-                              content::WebContents* new_contents,
-                              WindowOpenDisposition disposition,
-                              const gfx::Rect& initial_rect,
-                              bool user_gesture) = 0;
+  virtual void AddNewContents(
+      content::BrowserContext* context,
+      std::unique_ptr<content::WebContents> new_contents,
+      WindowOpenDisposition disposition,
+      const gfx::Rect& initial_rect,
+      bool user_gesture) = 0;
 
   // Feature support.
   virtual content::ColorChooser* ShowColorChooser(
diff --git a/extensions/browser/app_window/app_window.cc b/extensions/browser/app_window/app_window.cc
index 5b090151..e377ba4 100644
--- a/extensions/browser/app_window/app_window.cc
+++ b/extensions/browser/app_window/app_window.cc
@@ -366,14 +366,14 @@
 }
 
 void AppWindow::AddNewContents(WebContents* source,
-                               WebContents* new_contents,
+                               std::unique_ptr<WebContents> new_contents,
                                WindowOpenDisposition disposition,
                                const gfx::Rect& initial_rect,
                                bool user_gesture,
                                bool* was_blocked) {
   DCHECK(new_contents->GetBrowserContext() == browser_context_);
-  app_delegate_->AddNewContents(browser_context_, new_contents, disposition,
-                                initial_rect, user_gesture);
+  app_delegate_->AddNewContents(browser_context_, std::move(new_contents),
+                                disposition, initial_rect, user_gesture);
 }
 
 content::KeyboardEventProcessingResult AppWindow::PreHandleKeyboardEvent(
diff --git a/extensions/browser/app_window/app_window.h b/extensions/browser/app_window/app_window.h
index 84026c0..e753f89 100644
--- a/extensions/browser/app_window/app_window.h
+++ b/extensions/browser/app_window/app_window.h
@@ -425,7 +425,7 @@
       content::WebContents* source,
       const content::OpenURLParams& params) override;
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
diff --git a/extensions/browser/extension_host.cc b/extensions/browser/extension_host.cc
index 1ef809a..0a8a8f1 100644
--- a/extensions/browser/extension_host.cc
+++ b/extensions/browser/extension_host.cc
@@ -389,7 +389,7 @@
 }
 
 void ExtensionHost::AddNewContents(WebContents* source,
-                                   WebContents* new_contents,
+                                   std::unique_ptr<WebContents> new_contents,
                                    WindowOpenDisposition disposition,
                                    const gfx::Rect& initial_rect,
                                    bool user_gesture,
@@ -409,18 +409,16 @@
             new_contents->GetBrowserContext()) {
       WebContentsDelegate* delegate = associated_contents->GetDelegate();
       if (delegate) {
-        delegate->AddNewContents(
-            associated_contents, new_contents, disposition, initial_rect,
-            user_gesture, was_blocked);
+        delegate->AddNewContents(associated_contents, std::move(new_contents),
+                                 disposition, initial_rect, user_gesture,
+                                 was_blocked);
         return;
       }
     }
   }
 
-  // TODO(erikchen): Refactor AddNewContents to take strong ownership semantics.
-  // https://crbug.com/832879.
-  delegate_->CreateTab(base::WrapUnique(new_contents), extension_id_,
-                       disposition, initial_rect, user_gesture);
+  delegate_->CreateTab(std::move(new_contents), extension_id_, disposition,
+                       initial_rect, user_gesture);
 }
 
 void ExtensionHost::RenderViewReady() {
diff --git a/extensions/browser/extension_host.h b/extensions/browser/extension_host.h
index c93e3a4..8c7a96fd 100644
--- a/extensions/browser/extension_host.h
+++ b/extensions/browser/extension_host.h
@@ -113,7 +113,7 @@
   content::JavaScriptDialogManager* GetJavaScriptDialogManager(
       content::WebContents* source) override;
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
diff --git a/extensions/browser/guest_view/extension_options/extension_options_guest.cc b/extensions/browser/guest_view/extension_options/extension_options_guest.cc
index 1a74872..8ad547f 100644
--- a/extensions/browser/guest_view/extension_options/extension_options_guest.cc
+++ b/extensions/browser/guest_view/extension_options/extension_options_guest.cc
@@ -152,17 +152,18 @@
       options.ToValue()));
 }
 
-void ExtensionOptionsGuest::AddNewContents(WebContents* source,
-                                           WebContents* new_contents,
-                                           WindowOpenDisposition disposition,
-                                           const gfx::Rect& initial_rect,
-                                           bool user_gesture,
-                                           bool* was_blocked) {
+void ExtensionOptionsGuest::AddNewContents(
+    WebContents* source,
+    std::unique_ptr<WebContents> new_contents,
+    WindowOpenDisposition disposition,
+    const gfx::Rect& initial_rect,
+    bool user_gesture,
+    bool* was_blocked) {
   if (!attached() || !embedder_web_contents()->GetDelegate())
     return;
 
   embedder_web_contents()->GetDelegate()->AddNewContents(
-      source, new_contents, disposition, initial_rect, user_gesture,
+      source, std::move(new_contents), disposition, initial_rect, user_gesture,
       was_blocked);
 }
 
diff --git a/extensions/browser/guest_view/extension_options/extension_options_guest.h b/extensions/browser/guest_view/extension_options/extension_options_guest.h
index 12250fc1..9ff1ba65b 100644
--- a/extensions/browser/guest_view/extension_options/extension_options_guest.h
+++ b/extensions/browser/guest_view/extension_options/extension_options_guest.h
@@ -39,7 +39,7 @@
 
   // content::WebContentsDelegate implementation.
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.cc b/extensions/browser/guest_view/web_view/web_view_guest.cc
index d0561f07..d9880065 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_guest.cc
@@ -1245,17 +1245,17 @@
 }
 
 void WebViewGuest::AddNewContents(WebContents* source,
-                                  WebContents* new_contents,
+                                  std::unique_ptr<WebContents> new_contents,
                                   WindowOpenDisposition disposition,
                                   const gfx::Rect& initial_rect,
                                   bool user_gesture,
                                   bool* was_blocked) {
+  // TODO(erikchen): Fix ownership semantics for WebContents inside this class.
+  // https://crbug.com/832879.
   if (was_blocked)
     *was_blocked = false;
-  RequestNewWindowPermission(disposition,
-                             initial_rect,
-                             user_gesture,
-                             new_contents);
+  RequestNewWindowPermission(disposition, initial_rect, user_gesture,
+                             new_contents.release());
 }
 
 WebContents* WebViewGuest::OpenURLFromTab(
diff --git a/extensions/browser/guest_view/web_view/web_view_guest.h b/extensions/browser/guest_view/web_view/web_view_guest.h
index 5c06f378..4d6730dd 100644
--- a/extensions/browser/guest_view/web_view/web_view_guest.h
+++ b/extensions/browser/guest_view/web_view/web_view_guest.h
@@ -232,7 +232,7 @@
   content::JavaScriptDialogManager* GetJavaScriptDialogManager(
       content::WebContents* source) final;
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
diff --git a/extensions/shell/browser/shell_app_delegate.cc b/extensions/shell/browser/shell_app_delegate.cc
index 8d9afef1..ee1cf108 100644
--- a/extensions/shell/browser/shell_app_delegate.cc
+++ b/extensions/shell/browser/shell_app_delegate.cc
@@ -41,11 +41,12 @@
   return NULL;
 }
 
-void ShellAppDelegate::AddNewContents(content::BrowserContext* context,
-                                      content::WebContents* new_contents,
-                                      WindowOpenDisposition disposition,
-                                      const gfx::Rect& initial_rect,
-                                      bool user_gesture) {
+void ShellAppDelegate::AddNewContents(
+    content::BrowserContext* context,
+    std::unique_ptr<content::WebContents> new_contents,
+    WindowOpenDisposition disposition,
+    const gfx::Rect& initial_rect,
+    bool user_gesture) {
   NOTIMPLEMENTED();
 }
 
diff --git a/extensions/shell/browser/shell_app_delegate.h b/extensions/shell/browser/shell_app_delegate.h
index c07400ae..79cc6615 100644
--- a/extensions/shell/browser/shell_app_delegate.h
+++ b/extensions/shell/browser/shell_app_delegate.h
@@ -28,7 +28,7 @@
       content::WebContents* source,
       const content::OpenURLParams& params) override;
   void AddNewContents(content::BrowserContext* context,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture) override;
diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc
index 8c1cdd65..81b8e8e 100644
--- a/headless/lib/browser/headless_web_contents_impl.cc
+++ b/headless/lib/browser/headless_web_contents_impl.cc
@@ -118,7 +118,7 @@
   }
 
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
@@ -126,8 +126,10 @@
     const gfx::Rect default_rect(
         headless_web_contents_->browser()->options()->window_size);
     const gfx::Rect rect = initial_rect.IsEmpty() ? default_rect : initial_rect;
+    // TODO(erikchen): Refactor this class to use strong ownership semantics.
+    // https://crbug.com/832879.
     auto* const headless_contents =
-        HeadlessWebContentsImpl::From(browser(), new_contents);
+        HeadlessWebContentsImpl::From(browser(), new_contents.release());
     DCHECK(headless_contents);
     headless_contents->SetBounds(rect);
   }
diff --git a/ui/views/controls/webview/web_dialog_view.cc b/ui/views/controls/webview/web_dialog_view.cc
index ef8b793..323d9f8 100644
--- a/ui/views/controls/webview/web_dialog_view.cc
+++ b/ui/views/controls/webview/web_dialog_view.cc
@@ -307,19 +307,16 @@
   return WebDialogWebContentsDelegate::OpenURLFromTab(source, params);
 }
 
-void WebDialogView::AddNewContents(content::WebContents* source,
-                                   content::WebContents* new_contents,
-                                   WindowOpenDisposition disposition,
-                                   const gfx::Rect& initial_rect,
-                                   bool user_gesture,
-                                   bool* was_blocked) {
-  if (delegate_ && delegate_->HandleAddNewContents(
-          source, new_contents, disposition, initial_rect, user_gesture)) {
-    return;
-  }
-  WebDialogWebContentsDelegate::AddNewContents(
-      source, new_contents, disposition, initial_rect, user_gesture,
-      was_blocked);
+void WebDialogView::AddNewContents(
+    content::WebContents* source,
+    std::unique_ptr<content::WebContents> new_contents,
+    WindowOpenDisposition disposition,
+    const gfx::Rect& initial_rect,
+    bool user_gesture,
+    bool* was_blocked) {
+  WebDialogWebContentsDelegate::AddNewContents(source, std::move(new_contents),
+                                               disposition, initial_rect,
+                                               user_gesture, was_blocked);
 }
 
 void WebDialogView::LoadingStateChanged(content::WebContents* source,
diff --git a/ui/views/controls/webview/web_dialog_view.h b/ui/views/controls/webview/web_dialog_view.h
index 3a91e99..bfba061 100644
--- a/ui/views/controls/webview/web_dialog_view.h
+++ b/ui/views/controls/webview/web_dialog_view.h
@@ -104,7 +104,7 @@
       content::WebContents* source,
       const content::OpenURLParams& params) override;
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,
diff --git a/ui/web_dialogs/web_dialog_delegate.cc b/ui/web_dialogs/web_dialog_delegate.cc
index 7ef4cbe..f5ae082 100644
--- a/ui/web_dialogs/web_dialog_delegate.cc
+++ b/ui/web_dialogs/web_dialog_delegate.cc
@@ -41,15 +41,6 @@
   return false;
 }
 
-bool WebDialogDelegate::HandleAddNewContents(
-    content::WebContents* source,
-    content::WebContents* new_contents,
-    WindowOpenDisposition disposition,
-    const gfx::Rect& initial_rect,
-    bool user_gesture) {
-  return false;
-}
-
 bool WebDialogDelegate::HandleShouldCreateWebContents() {
   return true;
 }
diff --git a/ui/web_dialogs/web_dialog_delegate.h b/ui/web_dialogs/web_dialog_delegate.h
index eb2db20..b966e1f 100644
--- a/ui/web_dialogs/web_dialog_delegate.h
+++ b/ui/web_dialogs/web_dialog_delegate.h
@@ -26,7 +26,6 @@
 }
 
 namespace gfx {
-class Rect;
 class Size;
 }
 
@@ -123,17 +122,6 @@
                                     const content::OpenURLParams& params,
                                     content::WebContents** out_new_contents);
 
-  // A callback to create a new tab with |new_contents|. |source| is the
-  // WebContent where the operation originated. |disposition| controls how the
-  // new tab should be opened. |initial_rect| is the position and size of the
-  // window if a new window is created. |user_gesture| is true if the operation
-  // was started by a user gesture. Return false to use the default handler.
-  virtual bool HandleAddNewContents(content::WebContents* source,
-                                    content::WebContents* new_contents,
-                                    WindowOpenDisposition disposition,
-                                    const gfx::Rect& initial_rect,
-                                    bool user_gesture);
-
   // A callback to control whether a WebContents will be created. Returns
   // false to disallow the creation. Return true to use the default handler.
   virtual bool HandleShouldCreateWebContents();
diff --git a/ui/web_dialogs/web_dialog_web_contents_delegate.cc b/ui/web_dialogs/web_dialog_web_contents_delegate.cc
index 08bf98a..306bf2b 100644
--- a/ui/web_dialogs/web_dialog_web_contents_delegate.cc
+++ b/ui/web_dialogs/web_dialog_web_contents_delegate.cc
@@ -41,15 +41,16 @@
 }
 
 void WebDialogWebContentsDelegate::AddNewContents(
-    WebContents* source, WebContents* new_contents,
-    WindowOpenDisposition disposition, const gfx::Rect& initial_rect,
+    WebContents* source,
+    std::unique_ptr<WebContents> new_contents,
+    WindowOpenDisposition disposition,
+    const gfx::Rect& initial_rect,
     bool user_gesture,
     bool* was_blocked) {
   // TODO(erikchen): Refactor AddNewContents to take strong ownership semantics.
   // https://crbug.com/832879.
-  handler_->AddNewContents(browser_context_, source,
-                           base::WrapUnique(new_contents), disposition,
-                           initial_rect, user_gesture);
+  handler_->AddNewContents(browser_context_, source, std::move(new_contents),
+                           disposition, initial_rect, user_gesture);
 }
 
 bool WebDialogWebContentsDelegate::IsPopupOrPanel(
diff --git a/ui/web_dialogs/web_dialog_web_contents_delegate.h b/ui/web_dialogs/web_dialog_web_contents_delegate.h
index a660e33..d1cb93d 100644
--- a/ui/web_dialogs/web_dialog_web_contents_delegate.h
+++ b/ui/web_dialogs/web_dialog_web_contents_delegate.h
@@ -63,7 +63,7 @@
       content::WebContents* source,
       const content::OpenURLParams& params) override;
   void AddNewContents(content::WebContents* source,
-                      content::WebContents* new_contents,
+                      std::unique_ptr<content::WebContents> new_contents,
                       WindowOpenDisposition disposition,
                       const gfx::Rect& initial_rect,
                       bool user_gesture,