1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.content.browser;
6
7import android.test.suitebuilder.annotation.SmallTest;
8import android.text.TextUtils;
9import android.util.Pair;
10
11import org.chromium.base.test.util.UrlUtils;
12import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
13import org.chromium.content_public.browser.LoadUrlParams;
14import org.chromium.content_public.browser.NavigationTransitionDelegate;
15import org.chromium.content_shell_apk.ContentShellActivity;
16import org.chromium.content_shell_apk.ContentShellTestBase;
17import org.chromium.net.test.util.TestWebServer;
18
19import java.util.ArrayList;
20import java.util.List;
21import java.util.concurrent.TimeUnit;
22
23/**
24 * Test suite for navigation transition listeners.
25 */
26public class TransitionTest extends ContentShellTestBase {
27
28    private static final String URL_1 = UrlUtils.encodeHtmlDataUri("<html>1</html>");
29    private static final String URL_2 = "/2.html";
30    private static final String URL_2_DATA = "<html>2</html>";
31    private static final String URL_3 = "/3.html";
32    private static final String URL_3_DATA = "<html>3</html>";
33
34    static class TestNavigationTransitionDelegate implements NavigationTransitionDelegate {
35        private boolean mDidCallDefer = false;
36        private boolean mDidCallWillHandleDefer = false;
37        private boolean mDidCallAddStylesheet = false;
38        private boolean mHandleDefer = false;
39        private ArrayList<String> mTransitionStylesheets;
40        private ContentViewCore mContentViewCore;
41        private String mTransitionEnteringColor;
42
43        TestNavigationTransitionDelegate(ContentViewCore contentViewCore, boolean handleDefer) {
44            mContentViewCore = contentViewCore;
45            mHandleDefer = handleDefer;
46            mTransitionStylesheets = new ArrayList<String>();
47        }
48
49        @Override
50        public void didDeferAfterResponseStarted(String markup, String cssSelector,
51                String enteringColor) {
52            mDidCallDefer = true;
53            mContentViewCore.resumeResponseDeferredAtStart();
54            mTransitionEnteringColor = enteringColor;
55        }
56
57        @Override
58        public boolean willHandleDeferAfterResponseStarted() {
59            return mHandleDefer;
60        }
61
62        @Override
63        public void addEnteringStylesheetToTransition(String stylesheet) {
64            mDidCallAddStylesheet = true;
65            mTransitionStylesheets.add(stylesheet);
66        }
67
68        @Override
69        public void didStartNavigationTransitionForFrame(long frameId) {
70        }
71
72        public boolean getDidCallDefer() {
73            return mDidCallDefer;
74        }
75
76        public boolean getDidCallWillHandlerDefer() {
77            return mDidCallWillHandleDefer;
78        }
79
80        public boolean getDidCallAddStylesheet() {
81            return mDidCallAddStylesheet;
82        }
83
84        public ArrayList<String> getTransitionStylesheets() {
85            return mTransitionStylesheets;
86        }
87
88        public String getTransitionEnteringColor() {
89            return mTransitionEnteringColor;
90        }
91    };
92
93    private static List<Pair<String, String>> createHeadersList(
94        String[] namesAndValues) {
95        List<Pair<String, String>> result =
96            new ArrayList<Pair<String, String>>();
97        for (int i = 0; i < namesAndValues.length; i += 2)
98            result.add(Pair.create(namesAndValues[i], namesAndValues[i + 1]));
99        return result;
100    }
101
102    /**
103     * Tests that the listener recieves DidDeferAfterResponseStarted if we specify that
104     * the transition is handled.
105     */
106    @SmallTest
107    public void testDidDeferAfterResponseStartedCalled() throws Throwable {
108        ContentShellActivity activity = launchContentShellWithUrl(URL_1);
109        waitForActiveShellToBeDoneLoading();
110        ContentViewCore contentViewCore = activity.getActiveContentViewCore();
111        TestCallbackHelperContainer testCallbackHelperContainer =
112                new TestCallbackHelperContainer(contentViewCore);
113
114        contentViewCore.getWebContents().setHasPendingNavigationTransitionForTesting();
115        TestNavigationTransitionDelegate delegate = new TestNavigationTransitionDelegate(
116                contentViewCore,
117                true);
118        contentViewCore.getWebContents().setNavigationTransitionDelegate(delegate);
119
120        loadUrl(contentViewCore, testCallbackHelperContainer, new LoadUrlParams(URL_1));
121
122        assertTrue("didDeferAfterResponseStarted called.", delegate.getDidCallDefer());
123    }
124
125    /**
126     * Tests that the listener does not receive DidDeferAfterResponseStarted if we specify that
127     * the transition is handled.
128     */
129    @SmallTest
130    public void testDidDeferAfterResponseStartedNotCalled() throws Throwable {
131        ContentShellActivity activity = launchContentShellWithUrl(URL_1);
132        waitForActiveShellToBeDoneLoading();
133        ContentViewCore contentViewCore = activity.getActiveContentViewCore();
134        TestCallbackHelperContainer testCallbackHelperContainer =
135                new TestCallbackHelperContainer(contentViewCore);
136
137        contentViewCore.getWebContents().setHasPendingNavigationTransitionForTesting();
138        TestNavigationTransitionDelegate delegate = new TestNavigationTransitionDelegate(
139                contentViewCore,
140                false);
141        contentViewCore.getWebContents().setNavigationTransitionDelegate(delegate);
142
143        loadUrl(contentViewCore, testCallbackHelperContainer, new LoadUrlParams(URL_1));
144
145        assertFalse("didDeferAfterResponseStarted called.", delegate.getDidCallDefer());
146    }
147
148    /**
149     * Tests that the resource handler doesn't query the listener if no transition is pending.
150     */
151    @SmallTest
152    public void testWillHandleDeferAfterResponseStartedNotCalled() throws Throwable {
153        ContentShellActivity activity = launchContentShellWithUrl(URL_1);
154        waitForActiveShellToBeDoneLoading();
155        ContentViewCore contentViewCore = activity.getActiveContentViewCore();
156        TestCallbackHelperContainer testCallbackHelperContainer =
157                new TestCallbackHelperContainer(contentViewCore);
158
159        TestNavigationTransitionDelegate delegate = new TestNavigationTransitionDelegate(
160                contentViewCore,
161                false);
162        contentViewCore.getWebContents().setNavigationTransitionDelegate(delegate);
163
164        loadUrl(contentViewCore, testCallbackHelperContainer, new LoadUrlParams(URL_1));
165
166        assertFalse("didDeferAfterResponseStarted called.", delegate.getDidCallDefer());
167        assertFalse("willHandleDeferAfterResponseStarted called.",
168                delegate.getDidCallWillHandlerDefer());
169    }
170
171    /**
172     * Tests that the listener receives addStylesheetToTransition if we specify
173     * that there are entering transition stylesheet.
174     */
175    @SmallTest
176    public void testAddStylesheetToTransitionCalled() throws Throwable {
177        TestWebServer webServer = null;
178        try {
179          webServer = new TestWebServer(false);
180
181          final String url2 = webServer.setResponse(URL_2, URL_2_DATA, null);
182          ContentShellActivity activity = launchContentShellWithUrl(url2);
183          waitForActiveShellToBeDoneLoading();
184          ContentViewCore contentViewCore = activity.getActiveContentViewCore();
185          TestCallbackHelperContainer testCallbackHelperContainer =
186              new TestCallbackHelperContainer(contentViewCore);
187          contentViewCore.getWebContents().setHasPendingNavigationTransitionForTesting();
188          TestNavigationTransitionDelegate delegate =
189              new TestNavigationTransitionDelegate(contentViewCore, true);
190          contentViewCore.getWebContents().setNavigationTransitionDelegate(delegate);
191
192          int currentCallCount = testCallbackHelperContainer
193              .getOnPageFinishedHelper().getCallCount();
194          String[] headers = {
195              "link",
196              "<transition0.css>;rel=transition-entering-stylesheet;scope=*",
197              "link",
198              "<transition1.css>;rel=transition-entering-stylesheet;scope=*",
199              "link",
200              "<transition2.css>;rel=transition-entering-stylesheet;scope=*"
201          };
202          final String url3 = webServer.setResponse(URL_3,
203              URL_3_DATA,
204              createHeadersList(headers));
205          LoadUrlParams url3_params = new LoadUrlParams(url3);
206          loadUrl(contentViewCore, testCallbackHelperContainer, url3_params);
207          testCallbackHelperContainer.getOnPageFinishedHelper().waitForCallback(
208              currentCallCount,
209              1,
210              10000,
211              TimeUnit.MILLISECONDS);
212
213          assertTrue("addStylesheetToTransition called.",
214              delegate.getDidCallAddStylesheet());
215          assertTrue("Three stylesheets are added",
216              delegate.getTransitionStylesheets().size() == 3);
217        } finally {
218          if (webServer != null)
219            webServer.shutdown();
220        }
221    }
222
223    /**
224     * Tests that the listener receives addStylesheetToTransition if we specify
225     * that there are no entering transition stylesheet.
226     */
227    @SmallTest
228    public void testAddStylesheetToTransitionNotCalled() throws Throwable {
229        TestWebServer webServer = null;
230        try {
231          webServer = new TestWebServer(false);
232
233          final String url2 = webServer.setResponse(URL_2, URL_2_DATA, null);
234          ContentShellActivity activity = launchContentShellWithUrl(url2);
235          waitForActiveShellToBeDoneLoading();
236          ContentViewCore contentViewCore = activity.getActiveContentViewCore();
237          TestCallbackHelperContainer testCallbackHelperContainer =
238              new TestCallbackHelperContainer(contentViewCore);
239          contentViewCore.getWebContents().setHasPendingNavigationTransitionForTesting();
240          TestNavigationTransitionDelegate delegate =
241              new TestNavigationTransitionDelegate(contentViewCore, true);
242          contentViewCore.getWebContents().setNavigationTransitionDelegate(delegate);
243
244          int currentCallCount = testCallbackHelperContainer
245              .getOnPageFinishedHelper().getCallCount();
246          final String url3 = webServer.setResponse(URL_3, URL_3_DATA, null);
247          LoadUrlParams url3_params = new LoadUrlParams(url3);
248          loadUrl(contentViewCore, testCallbackHelperContainer, url3_params);
249          testCallbackHelperContainer.getOnPageFinishedHelper().waitForCallback(
250              currentCallCount,
251              1,
252              10000,
253              TimeUnit.MILLISECONDS);
254
255          assertFalse("addStylesheetToTransition is not called.",
256              delegate.getDidCallAddStylesheet());
257          assertTrue("No stylesheets are added",
258              delegate.getTransitionStylesheets().size() == 0);
259        } finally {
260          if (webServer != null)
261            webServer.shutdown();
262        }
263    }
264
265    /**
266     * Tests that the listener receives the entering color if it's specified in the
267     * response headers.
268     */
269    @SmallTest
270    public void testParseTransitionEnteringColor() throws Throwable {
271        TestWebServer webServer = null;
272        try {
273            webServer = new TestWebServer(false);
274
275            final String url2 = webServer.setResponse(URL_2, URL_2_DATA, null);
276            ContentShellActivity activity = launchContentShellWithUrl(url2);
277            waitForActiveShellToBeDoneLoading();
278            ContentViewCore contentViewCore = activity.getActiveContentViewCore();
279            TestCallbackHelperContainer testCallbackHelperContainer =
280                    new TestCallbackHelperContainer(contentViewCore);
281            contentViewCore.getWebContents().setHasPendingNavigationTransitionForTesting();
282            TestNavigationTransitionDelegate delegate =
283                    new TestNavigationTransitionDelegate(contentViewCore, true);
284            contentViewCore.getWebContents().setNavigationTransitionDelegate(delegate);
285
286            String transitionEnteringColor = "#00FF00";
287
288            int currentCallCount = testCallbackHelperContainer
289                    .getOnPageFinishedHelper().getCallCount();
290            String[] headers = {
291                    "X-Transition-Entering-Color",
292                    transitionEnteringColor,
293            };
294            final String url3 = webServer.setResponse(URL_3,
295                    URL_3_DATA,
296                    createHeadersList(headers));
297            LoadUrlParams url3Params = new LoadUrlParams(url3);
298            loadUrl(contentViewCore, testCallbackHelperContainer, url3Params);
299            testCallbackHelperContainer.getOnPageFinishedHelper().waitForCallback(
300                    currentCallCount,
301                    1,
302                    10000,
303                    TimeUnit.MILLISECONDS);
304
305            assertTrue("X-Transition-Entering-Color parsed correctly.",
306                    TextUtils.equals(
307                            delegate.getTransitionEnteringColor(),
308                            transitionEnteringColor));
309        } finally {
310            if (webServer != null) webServer.shutdown();
311        }
312    }
313}
314