Commit ffbce71f authored by Scott Violet's avatar Scott Violet Committed by Commit Bot

weblayer: adds NewTabCallback.CloseTab()

This is called when a tab opened by way of window.open() is asked to
close (window.close()).

BUG=none
TEST=covered by test

Change-Id: I0d97069faed7a77edfef2e2932328e271d5d6db1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1913601
Commit-Queue: Scott Violet <sky@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#715086}
parent 22a820c1
...@@ -20,6 +20,11 @@ public class NewTabCallbackImpl extends NewTabCallback { ...@@ -20,6 +20,11 @@ public class NewTabCallbackImpl extends NewTabCallback {
tab.getBrowser().setActiveTab(tab); tab.getBrowser().setActiveTab(tab);
} }
@Override
public void onCloseTab() {
assert false;
}
public void waitForNewTab() { public void waitForNewTab() {
try { try {
// waitForFirst() only handles a single call. If you need more convert from // waitForFirst() only handles a single call. If you need more convert from
......
...@@ -12,7 +12,9 @@ import org.junit.Test; ...@@ -12,7 +12,9 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.weblayer.NewTabCallback;
import org.chromium.weblayer.Tab; import org.chromium.weblayer.Tab;
import org.chromium.weblayer.shell.InstrumentationActivity; import org.chromium.weblayer.shell.InstrumentationActivity;
...@@ -27,6 +29,28 @@ public class NewTabCallbackTest { ...@@ -27,6 +29,28 @@ public class NewTabCallbackTest {
private InstrumentationActivity mActivity; private InstrumentationActivity mActivity;
private static final class CloseTabNewTabCallbackImpl extends NewTabCallback {
private final CallbackHelper mCallbackHelper = new CallbackHelper();
@Override
public void onNewTab(Tab tab, int mode) {}
@Override
public void onCloseTab() {
mCallbackHelper.notifyCalled();
}
public void waitForCloseTab() {
try {
// waitForFirst() only handles a single call. If you need more convert from
// waitForFirst().
mCallbackHelper.waitForFirst();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Test @Test
@SmallTest @SmallTest
public void testNewBrowser() { public void testNewBrowser() {
...@@ -48,4 +72,35 @@ public class NewTabCallbackTest { ...@@ -48,4 +72,35 @@ public class NewTabCallbackTest {
Assert.assertNotSame(firstTab, secondTab); Assert.assertNotSame(firstTab, secondTab);
}); });
} }
@Test
@SmallTest
public void testCloseTab() {
String url = mActivityTestRule.getTestDataURL("new_tab_then_close.html");
mActivity = mActivityTestRule.launchShellWithUrl(url);
Assert.assertNotNull(mActivity);
NewTabCallbackImpl callback = new NewTabCallbackImpl();
Tab firstTab = TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
Tab tab = mActivity.getBrowser().getActiveTab();
tab.setNewTabCallback(callback);
return tab;
});
// Click on the tab to trigger creating a new tab.
EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView());
callback.waitForNewTab();
CloseTabNewTabCallbackImpl closeTabImpl = new CloseTabNewTabCallbackImpl();
TestThreadUtils.runOnUiThreadBlocking(() -> {
Assert.assertEquals(2, mActivity.getBrowser().getTabs().size());
Tab secondTab = mActivity.getBrowser().getActiveTab();
Assert.assertNotSame(firstTab, secondTab);
secondTab.setNewTabCallback(closeTabImpl);
// Switch to the first tab so clicking closes |secondTab|.
secondTab.getBrowser().setActiveTab(firstTab);
});
// Clicking on the tab again to callback to close the tab.
EventUtils.simulateTouchCenterOfView(mActivity.getWindow().getDecorView());
closeTabImpl.waitForCloseTab();
}
} }
...@@ -57,6 +57,11 @@ public final class NewTabCallbackProxy { ...@@ -57,6 +57,11 @@ public final class NewTabCallbackProxy {
mTab.getClient().onNewTab(tab.getId(), mode); mTab.getClient().onNewTab(tab.getId(), mode);
} }
@CalledByNative
private void onCloseTab() throws RemoteException {
mTab.getClient().onCloseTab();
}
@NativeMethods @NativeMethods
interface Natives { interface Natives {
long createNewTabCallbackProxy(NewTabCallbackProxy proxy, long tab); long createNewTabCallbackProxy(NewTabCallbackProxy proxy, long tab);
......
...@@ -14,4 +14,6 @@ interface ITabClient { ...@@ -14,4 +14,6 @@ interface ITabClient {
void onNewTab(in int tabId, in int mode) = 1; void onNewTab(in int tabId, in int mode) = 1;
void onRenderProcessGone() = 2; void onRenderProcessGone() = 2;
void onCloseTab() = 3;
} }
...@@ -33,6 +33,10 @@ void NewTabCallbackProxy::OnNewTab(std::unique_ptr<Tab> tab, NewTabType type) { ...@@ -33,6 +33,10 @@ void NewTabCallbackProxy::OnNewTab(std::unique_ptr<Tab> tab, NewTabType type) {
static_cast<int>(type)); static_cast<int>(type));
} }
void NewTabCallbackProxy::CloseTab() {
Java_NewTabCallbackProxy_onCloseTab(AttachCurrentThread(), java_impl_);
}
static jlong JNI_NewTabCallbackProxy_CreateNewTabCallbackProxy( static jlong JNI_NewTabCallbackProxy_CreateNewTabCallbackProxy(
JNIEnv* env, JNIEnv* env,
const base::android::JavaParamRef<jobject>& proxy, const base::android::JavaParamRef<jobject>& proxy,
......
...@@ -24,6 +24,7 @@ class NewTabCallbackProxy : public NewTabDelegate { ...@@ -24,6 +24,7 @@ class NewTabCallbackProxy : public NewTabDelegate {
// NewTabDelegate: // NewTabDelegate:
void OnNewTab(std::unique_ptr<Tab> tab, NewTabType type) override; void OnNewTab(std::unique_ptr<Tab> tab, NewTabType type) override;
void CloseTab() override;
private: private:
TabImpl* tab_; TabImpl* tab_;
......
...@@ -345,6 +345,11 @@ void TabImpl::AddNewContents(content::WebContents* source, ...@@ -345,6 +345,11 @@ void TabImpl::AddNewContents(content::WebContents* source,
NewTabTypeFromWindowDisposition(disposition)); NewTabTypeFromWindowDisposition(disposition));
} }
void TabImpl::CloseContents(content::WebContents* source) {
if (new_tab_delegate_)
new_tab_delegate_->CloseTab();
}
void TabImpl::DidFinishNavigation( void TabImpl::DidFinishNavigation(
content::NavigationHandle* navigation_handle) { content::NavigationHandle* navigation_handle) {
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
...@@ -363,9 +368,8 @@ void TabImpl::DidFinishNavigation( ...@@ -363,9 +368,8 @@ void TabImpl::DidFinishNavigation(
} }
void TabImpl::RenderProcessGone(base::TerminationStatus status) { void TabImpl::RenderProcessGone(base::TerminationStatus status) {
for (auto& observer : observers_) { for (auto& observer : observers_)
observer.OnRenderProcessGone(); observer.OnRenderProcessGone();
}
} }
void TabImpl::OnExitFullscreen() { void TabImpl::OnExitFullscreen() {
......
...@@ -121,6 +121,7 @@ class TabImpl : public Tab, ...@@ -121,6 +121,7 @@ class TabImpl : public Tab,
const gfx::Rect& initial_rect, const gfx::Rect& initial_rect,
bool user_gesture, bool user_gesture,
bool* was_blocked) override; bool* was_blocked) override;
void CloseContents(content::WebContents* source) override;
// content::WebContentsObserver: // content::WebContentsObserver:
void DidFinishNavigation( void DidFinishNavigation(
......
...@@ -11,5 +11,19 @@ import androidx.annotation.NonNull; ...@@ -11,5 +11,19 @@ import androidx.annotation.NonNull;
* set, popups are disabled. * set, popups are disabled.
*/ */
public abstract class NewTabCallback { public abstract class NewTabCallback {
/**
* Called when a new tab has been created.
*
* @param tab The new tab.
* @param type How the tab should be shown.
*/
public abstract void onNewTab(@NonNull Tab tab, @NewTabType int type); public abstract void onNewTab(@NonNull Tab tab, @NewTabType int type);
/**
* Called when a tab previously opened via onNewTab() was asked to close. Generally this should
* destroy the Tab and/or Browser.
*
* @see Browser#destroyTab
*/
public abstract void onCloseTab();
} }
...@@ -225,6 +225,14 @@ public final class Tab { ...@@ -225,6 +225,14 @@ public final class Tab {
mNewTabCallback.onNewTab(tab, mode); mNewTabCallback.onNewTab(tab, mode);
} }
@Override
public void onCloseTab() {
// This should only be hit if setNewTabCallback() has been called with a non-null
// value.
assert mNewTabCallback != null;
mNewTabCallback.onCloseTab();
}
@Override @Override
public void onRenderProcessGone() { public void onRenderProcessGone() {
for (TabCallback callback : mCallbacks) { for (TabCallback callback : mCallbacks) {
......
...@@ -35,6 +35,11 @@ class NewTabDelegate { ...@@ -35,6 +35,11 @@ class NewTabDelegate {
public: public:
virtual void OnNewTab(std::unique_ptr<Tab> new_tab, NewTabType type) = 0; virtual void OnNewTab(std::unique_ptr<Tab> new_tab, NewTabType type) = 0;
// The page has requested a tab that was created by way of OnNewTab() to be
// closed. This is sent to the NewTabDelegate set on the page created by way
// of OnNewTab().
virtual void CloseTab() = 0;
protected: protected:
virtual ~NewTabDelegate() {} virtual ~NewTabDelegate() {}
}; };
......
<html>
<body>
<p>some content</p>
</body>
<script>
var openedWindow = null;
function openNewWindowOrClose() {
if (openedWindow == null) {
openedWindow = window.open('about:blank', '');
} else {
openedWindow.close();
openedWindow = null;
}
}
document.addEventListener('touchend',
function(e) { openNewWindowOrClose(); }, false);
</script>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment