Chromium Code Reviews| Index: android_webview/javatests/src/org/chromium/android_webview/test/AwShouldIgnoreNavigationTest.java |
| diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwShouldIgnoreNavigationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwShouldIgnoreNavigationTest.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7d53b0f98b60736560a144e494ca8e4b0bd6dfe2 |
| --- /dev/null |
| +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwShouldIgnoreNavigationTest.java |
| @@ -0,0 +1,470 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package org.chromium.android_webview.test; |
|
mkosiba (inactive)
2012/10/03 17:14:48
Please ignore the file for now - I need to move th
mkosiba (inactive)
2012/10/08 15:09:12
new iteration of test is good to go
|
| + |
| +import android.os.SystemClock; |
| +import android.test.suitebuilder.annotation.SmallTest; |
| +import android.view.MotionEvent; |
| + |
| +import org.chromium.base.test.util.Feature; |
| +import org.chromium.base.test.util.UrlUtils; |
| +import org.chromium.chrome.browser.component.web_contents_delegate_android.WebContentsDelegateAndroid; |
| +import org.chromium.content.browser.ContentViewCore; |
| +import org.chromium.content.browser.test.util.CallbackHelper; |
| +import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper; |
| + |
| +import java.net.URLEncoder; |
| + |
| +/** |
| + * Tests for the WebViewClient.shouldOverrideUrlLoading() method. |
| + */ |
| +public class AwShouldIgnoreNavigationTest extends AndroidWebViewTestBase { |
| + final private String FULL_PAGE_CONTENT_LINK_TO_SELF_URL = |
| + UrlUtils.getTestFileUrl("webview/full_page_content_link_to_self.html"); |
| + final private String REDIRECT_30X_URL = UrlUtils.getTestFileUrl( |
| + "webview/redirect/30x.cgi"); |
| + final private String REDIRECT_META_REFRESH_URL = UrlUtils.getTestFileUrl( |
| + "webview/redirect/meta_refresh.cgi"); |
| + final private String REDIRECT_JS_URL = UrlUtils.getTestFileUrl( |
| + "webview/redirect/js_redirect.cgi"); |
| + final private String REDIRECT_TARGET_URL = UrlUtils.getTestFileUrl( |
| + "webview/redirect/redirect_target.html"); |
| + |
| + private static class TestAwContentsClient |
| + extends org.chromium.android_webview.test.TestAwContentsClient { |
| + |
| + public class ShouldIgnoreNavigationHelper extends CallbackHelper { |
| + private String mShouldIgnoreNavigationUrl; |
| + private String mPreviousShouldIgnoreNavigationUrl; |
| + private boolean mShouldIgnoreNavigationReturnValue = false; |
| + void setShouldIgnoreNavigationUrl(String url) { |
| + mShouldIgnoreNavigationUrl = url; |
| + } |
| + void setPreviousShouldIgnoreNavigationUrl(String url) { |
| + mPreviousShouldIgnoreNavigationUrl = url; |
| + } |
| + void setShouldIgnoreNavigationReturnValue(boolean value) { |
| + mShouldIgnoreNavigationReturnValue = value; |
| + } |
| + public String getShouldIgnoreNavigationUrl() { |
| + assert getCallCount() > 0; |
| + return mShouldIgnoreNavigationUrl; |
| + } |
| + public String getPreviousShouldIgnoreNavigationUrl() { |
| + assert getCallCount() > 1; |
| + return mPreviousShouldIgnoreNavigationUrl; |
| + } |
| + public boolean getShouldIgnoreNavigationReturnValue() { |
| + return mShouldIgnoreNavigationReturnValue; |
| + } |
| + public void notifyCalled(String url) { |
| + mPreviousShouldIgnoreNavigationUrl = mShouldIgnoreNavigationUrl; |
| + mShouldIgnoreNavigationUrl = url; |
| + notifyCalled(); |
| + } |
| + } |
| + |
| + @Override |
| + public boolean shouldIgnoreNavigation(String url) { |
| + super.shouldIgnoreNavigation(url); |
| + boolean returnValue = |
| + mShouldIgnoreNavigationHelper.getShouldIgnoreNavigationReturnValue(); |
| + mShouldIgnoreNavigationHelper.notifyCalled(url); |
| + return returnValue; |
| + } |
| + |
| + private ShouldIgnoreNavigationHelper mShouldIgnoreNavigationHelper; |
| + |
| + public TestAwContentsClient() { |
| + super(); |
| + mShouldIgnoreNavigationHelper = new ShouldIgnoreNavigationHelper(); |
| + } |
| + |
| + public ShouldIgnoreNavigationHelper getShouldIgnoreNavigationHelper() { |
| + return mShouldIgnoreNavigationHelper; |
| + } |
| + } |
| + |
| + /** |
| + * Sends (synchronously) a single click to an absolute screen coordinates. |
| + * |
| + * @param x screen absolute |
| + * @param y screen absolute |
| + * @see TouchUtils |
| + */ |
| + public void singleClick(final ContentViewCore contentViewCore, float x, |
| + float y) throws Throwable { |
| + |
| + long downTime = SystemClock.uptimeMillis(); |
| + long eventTime = SystemClock.uptimeMillis(); |
| + |
| + final MotionEvent event_down = MotionEvent.obtain(downTime, eventTime, |
| + MotionEvent.ACTION_DOWN, x, y, 0); |
| + runTestOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + contentViewCore.onTouchEvent(event_down); |
| + } |
| + }); |
| + |
| + eventTime = SystemClock.uptimeMillis(); |
| + final MotionEvent event_up = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, |
| + x, y, 0); |
| + runTestOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + contentViewCore.onTouchEvent(event_up); |
| + } |
| + }); |
| + } |
| + |
| + void clickOnCenterOfView(ContentViewCore contentViewCore) throws Throwable { |
| + singleClick(contentViewCore, |
| + contentViewCore.getWidth() / 2, contentViewCore.getHeight() / 2); |
| + } |
| + |
| + private String makeHtmlPageFrom(String headers, String body) { |
| + return "<html>" + |
| + "<head>" + |
| + "<title>Title</title>" + |
| + "<style type=\"text/css\">" + |
| + // Make the image take up all of the page so that we don't have to do |
| + // any fancy hit target calculations when synthesizing the touch event |
| + // to click it. |
| + "img.big { width:100%; height:100%; background-color:blue; }" + |
| + "</style>" + |
| + headers + |
| + "</head>" + |
| + "<body>" + |
| + body + |
| + "</body>" + |
| + "</html>"; |
| + } |
| + |
| + private String getHtmlForPageWithSimpleLinkTo(String destination) { |
| + return makeHtmlPageFrom("", |
| + "<a href=\"" + destination + "\">" + |
| + "<img class=\"big\" />" + |
| + "</a>"); |
| + } |
| + |
| + private String getHtmlForPageWithSimpleLink() { |
| + return getHtmlForPageWithSimpleLinkTo(REDIRECT_TARGET_URL); |
| + } |
| + |
| + private String getHtmlForPageWithJsAssignLink() { |
| + return makeHtmlPageFrom("", |
| + "<img onclick=\"location.href='" + REDIRECT_TARGET_URL + "'\" class=\"big\" />"); |
| + } |
| + |
| + private String getHtmlForPageWithJsReplaceLink() { |
| + return makeHtmlPageFrom("", |
| + "<img onclick=\"location.replace('" + REDIRECT_TARGET_URL + |
| + "');\" class=\"big\" />"); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationNotCalledOnLoadUrl() throws Throwable { |
| + final TestAwContentsClient contentsClient = new TestAwContentsClient(); |
| + final ContentViewCore contentViewCore = |
| + createAwTestContainerViewOnMainSync(contentsClient).getContentViewCore(); |
| + TestAwContentsClient.ShouldIgnoreNavigationHelper shouldIgnoreNavigationHelper = |
| + contentsClient.getShouldIgnoreNavigationHelper(); |
| + loadDataSync(contentViewCore, contentsClient.getOnPageFinishedHelper(), |
| + getHtmlForPageWithSimpleLink(), "text/html", false); |
| + |
| + assertEquals(0, shouldIgnoreNavigationHelper.getCallCount()); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCalledWhenLinkClicked() throws Throwable { |
| + final TestAwContentsClient contentsClient = new TestAwContentsClient(); |
| + final ContentViewCore contentViewCore = |
| + createAwTestContainerViewOnMainSync(contentsClient).getContentViewCore(); |
| + TestAwContentsClient.ShouldIgnoreNavigationHelper shouldIgnoreNavigationHelper = |
| + contentsClient.getShouldIgnoreNavigationHelper(); |
| + int callCount = shouldIgnoreNavigationHelper.getCallCount(); |
| + loadDataSync(contentViewCore, contentsClient.getOnPageFinishedHelper(), |
| + getHtmlForPageWithSimpleLink(), "text/html", false); |
| + |
| + clickOnCenterOfView(contentViewCore); |
| + |
| + shouldIgnoreNavigationHelper.waitForCallback(callCount); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCalledWhenSelfLinkClicked() throws Throwable { |
| + final TestAwContentsClient contentsClient = new TestAwContentsClient(); |
| + final ContentViewCore contentViewCore = |
| + createAwTestContainerViewOnMainSync(contentsClient).getContentViewCore(); |
| + TestAwContentsClient.ShouldIgnoreNavigationHelper shouldIgnoreNavigationHelper = |
| + contentsClient.getShouldIgnoreNavigationHelper(); |
| + int callCount = shouldIgnoreNavigationHelper.getCallCount(); |
| + loadUrlSync(contentViewCore, contentsClient.getOnPageFinishedHelper(), |
| + FULL_PAGE_CONTENT_LINK_TO_SELF_URL); |
| + |
| + clickOnCenterOfView(contentViewCore); |
| + |
| + shouldIgnoreNavigationHelper.waitForCallback(callCount); |
| + assertEquals(FULL_PAGE_CONTENT_LINK_TO_SELF_URL, |
| + shouldIgnoreNavigationHelper.getShouldIgnoreNavigationUrl()); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCalledWhenNavigatingFromJavaScriptUsingAssign() |
| + throws Throwable { |
| + final TestAwContentsClient contentsClient = new TestAwContentsClient(); |
| + final ContentViewCore contentViewCore = |
| + createAwTestContainerViewOnMainSync(contentsClient).getContentViewCore(); |
| + enableJavaScriptOnUiThread(contentViewCore); |
| + TestAwContentsClient.ShouldIgnoreNavigationHelper shouldIgnoreNavigationHelper = |
| + contentsClient.getShouldIgnoreNavigationHelper(); |
| + int callCount = shouldIgnoreNavigationHelper.getCallCount(); |
| + loadDataSync(contentViewCore, contentsClient.getOnPageFinishedHelper(), |
| + getHtmlForPageWithJsAssignLink(), "text/html", false); |
| + |
| + clickOnCenterOfView(contentViewCore); |
| + |
| + shouldIgnoreNavigationHelper.waitForCallback(callCount); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCalledWhenNavigatingFromJavaScriptUsingReplace() |
| + throws Throwable { |
| + final TestAwContentsClient contentsClient = new TestAwContentsClient(); |
| + final ContentViewCore contentViewCore = |
| + createAwTestContainerViewOnMainSync(contentsClient).getContentViewCore(); |
| + enableJavaScriptOnUiThread(contentViewCore); |
| + TestAwContentsClient.ShouldIgnoreNavigationHelper shouldIgnoreNavigationHelper = |
| + contentsClient.getShouldIgnoreNavigationHelper(); |
| + int callCount = shouldIgnoreNavigationHelper.getCallCount(); |
| + loadDataSync(contentViewCore, contentsClient.getOnPageFinishedHelper(), |
| + getHtmlForPageWithJsReplaceLink(), "text/html", false); |
| + |
| + clickOnCenterOfView(contentViewCore); |
| + |
| + shouldIgnoreNavigationHelper.waitForCallback(callCount); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationPassesCorrectUrl() throws Throwable { |
| + final TestAwContentsClient contentsClient = new TestAwContentsClient(); |
| + final ContentViewCore contentViewCore = |
| + createAwTestContainerViewOnMainSync(contentsClient).getContentViewCore(); |
| + TestAwContentsClient.ShouldIgnoreNavigationHelper shouldIgnoreNavigationHelper = |
| + contentsClient.getShouldIgnoreNavigationHelper(); |
| + int callCount = shouldIgnoreNavigationHelper.getCallCount(); |
| + loadDataSync(contentViewCore, contentsClient.getOnPageFinishedHelper(), |
| + getHtmlForPageWithSimpleLink(), "text/html", false); |
| + |
| + clickOnCenterOfView(contentViewCore); |
| + |
| + shouldIgnoreNavigationHelper.waitForCallback(callCount); |
| + assertEquals(REDIRECT_TARGET_URL, |
| + shouldIgnoreNavigationHelper.getShouldIgnoreNavigationUrl()); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCanOverrideLoading() throws Throwable { |
| + final TestAwContentsClient contentsClient = new TestAwContentsClient(); |
| + final ContentViewCore contentViewCore = |
| + createAwTestContainerViewOnMainSync(contentsClient).getContentViewCore(); |
| + TestAwContentsClient.ShouldIgnoreNavigationHelper shouldIgnoreNavigationHelper = |
| + contentsClient.getShouldIgnoreNavigationHelper(); |
| + // The callback function is only called for link transitions, so setting it to return true |
| + // here will not impact loadUrlSync calls. |
| + shouldIgnoreNavigationHelper.setShouldIgnoreNavigationReturnValue(true); |
| + int callCount = shouldIgnoreNavigationHelper.getCallCount(); |
| + |
| + loadDataSync(contentViewCore, contentsClient.getOnPageFinishedHelper(), |
| + getHtmlForPageWithSimpleLink(), "text/html", false); |
| + |
| + OnPageFinishedHelper onPageFinishedHelper = contentsClient.getOnPageFinishedHelper(); |
| + int onPageFinishedCountBeforeClickingOnLink = onPageFinishedHelper.getCallCount(); |
| + clickOnCenterOfView(contentViewCore); |
| + // Some time around here true should be returned from the shouldIgnoreNavigation callback |
| + // causing the navigation caused by calling clickOnCenterOfView to be ignored. We validate |
| + // this by indirectly checking that an onPageFinished callback was not issued after this |
| + // point. |
| + shouldIgnoreNavigationHelper.waitForCallback(callCount); |
| + |
| + String synchronizationUrl = getTestHttpUrl("chrome/test/data/android/test.html"); |
| + loadUrlSync(contentViewCore, onPageFinishedHelper, |
| + synchronizationUrl); |
| + |
| + assertEquals(synchronizationUrl, onPageFinishedHelper.getUrl()); |
| + assertEquals(onPageFinishedHelper.getCallCount(), |
| + onPageFinishedCountBeforeClickingOnLink + 1); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCalledForDataUrl() throws Throwable { |
| + final String dataUrl = |
| + "data:text/html;base64," + |
| + "PGh0bWw+PGhlYWQ+PHRpdGxlPmRhdGFVcmxUZXN0QmFzZTY0PC90aXRsZT48" + |
| + "L2hlYWQ+PC9odG1sPg=="; |
| + final TestAwContentsClient contentsClient = new TestAwContentsClient(); |
| + final ContentViewCore contentViewCore = |
| + createAwTestContainerViewOnMainSync(contentsClient).getContentViewCore(); |
| + TestAwContentsClient.ShouldIgnoreNavigationHelper shouldIgnoreNavigationHelper = |
| + contentsClient.getShouldIgnoreNavigationHelper(); |
| + int callCount = shouldIgnoreNavigationHelper.getCallCount(); |
| + loadDataSync(contentViewCore, contentsClient.getOnPageFinishedHelper(), |
| + getHtmlForPageWithSimpleLinkTo(dataUrl), "text/html", false); |
| + |
| + clickOnCenterOfView(contentViewCore); |
| + |
| + shouldIgnoreNavigationHelper.waitForCallback(callCount); |
| + assertTrue("Expected URL that starts with 'data:' but got: <" + |
| + shouldIgnoreNavigationHelper.getShouldIgnoreNavigationUrl() + "> instead.", |
| + shouldIgnoreNavigationHelper.getShouldIgnoreNavigationUrl().startsWith( |
| + "data:")); |
| + } |
| + |
| + /** |
| + * Worker method for the various redirect tests. |
| + * |
| + * Calling this will first load the redirect URL built from redirectFilePath, query and |
| + * locationFilePath and assert that we get a override callback for the destination. |
| + * The second part of the test loads a page that contains a link which points at the redirect |
| + * URL. We expect two callback - one for the redirect link and another for the destination. |
| + * |
| + * These tests depend on CGI scripts which enable our test server to perform the redirects. |
| + * All of them take a parameter called location which specifies where to redirect the request |
| + * to. A short summary of the scripts and their parameters: |
| + * - redirect/30x.cgi: does a 30x redirect |
| + * code : redirect code (301, 302 or 303) |
| + * - redirect/js_redirect.cgi: an interstitial page with an onLoad JavaScript which sets up |
| + * a timer with the supplied delay. When the timer fires we |
| + * initiate a navigation. |
| + * kind : how the page will initiate a navigation. Valid options are 'assign' or 'replace' |
| + * which will result in either 'location.href = locationFilePath;' or |
| + * 'location.replace(locationFilePath);' |
| + * timeout : how long to wait (in milliseconds) before initiating a navigation (0 means |
| + * an immediate redirect. |
| + * - redirect/meta_refresh.cgi : an interstitial page with a meta http-equiv="refresh" header |
| + * timeout : how long to wait (in seconds) before initiating the redirect |
| + */ |
| + private void doTestShouldIgnoreNavigationCalledOnRedirect(String redirectFilePath, |
| + String query, |
| + String redirectLocation) |
| + throws Throwable { |
| + final TestAwContentsClient contentsClient = new TestAwContentsClient(); |
| + final ContentViewCore contentViewCore = |
| + createAwTestContainerViewOnMainSync(contentsClient).getContentViewCore(); |
| + enableJavaScriptOnUiThread(contentViewCore); |
| + String redirectUrlWithEncodedLocation = redirectFilePath + "?" + query + |
| + "&location=" + URLEncoder.encode(redirectLocation, "UTF-8"); |
| + String redirectUrl = redirectFilePath + "?" + query + |
| + "&location=" + redirectLocation; |
| + |
| + TestAwContentsClient.ShouldIgnoreNavigationHelper shouldIgnoreNavigationHelper = |
| + contentsClient.getShouldIgnoreNavigationHelper(); |
| + int directLoadCallCount = shouldIgnoreNavigationHelper.getCallCount(); |
| + loadUrlSync(contentViewCore, contentsClient.getOnPageFinishedHelper(), redirectUrl); |
| + |
| + shouldIgnoreNavigationHelper.waitForCallback(directLoadCallCount); |
| + assertEquals(redirectLocation, |
| + shouldIgnoreNavigationHelper.getShouldIgnoreNavigationUrl()); |
| + |
| + // There is a slight difference between navigations caused by calling load and navigations |
| + // caused by clicking on a link: |
| + // * when using load the navigation is treated as if it came from the URL bar (has the |
| + // navigation type TYPED and doesn't have the has_user_gesture flag) |
| + // * when clicking on a link the navigation has the LINK type and has_user_gesture is |
| + // true. |
| + // Both of these should yield the same result which is what we're verifying here. |
| + int indirectLoadCallCount = shouldIgnoreNavigationHelper.getCallCount(); |
| + loadDataSync(contentViewCore, |
| + contentsClient.getOnPageFinishedHelper(), |
| + getHtmlForPageWithSimpleLinkTo(redirectUrlWithEncodedLocation), |
| + "text/html", false); |
| + assertEquals(indirectLoadCallCount, shouldIgnoreNavigationHelper.getCallCount()); |
| + |
| + clickOnCenterOfView(contentViewCore); |
| + |
| + shouldIgnoreNavigationHelper.waitForCallback(indirectLoadCallCount, 2); |
| + |
| + assertEquals(redirectLocation, |
| + shouldIgnoreNavigationHelper.getShouldIgnoreNavigationUrl()); |
| + assertEquals(redirectUrl, |
| + shouldIgnoreNavigationHelper.getPreviousShouldIgnoreNavigationUrl()); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCalledOn301Redirect() throws Throwable { |
| + doTestShouldIgnoreNavigationCalledOnRedirect( |
| + REDIRECT_30X_URL, |
| + "code=301", |
| + REDIRECT_TARGET_URL); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCalledOn302Redirect() throws Throwable { |
| + doTestShouldIgnoreNavigationCalledOnRedirect( |
| + REDIRECT_30X_URL, |
| + "code=302", |
| + REDIRECT_TARGET_URL); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCalledOnMetaRefreshRedirect() throws Throwable { |
| + doTestShouldIgnoreNavigationCalledOnRedirect( |
| + REDIRECT_META_REFRESH_URL, |
| + "timeout=0", |
| + REDIRECT_TARGET_URL); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCalledOnJavaScriptLocationImmediateAssignRedirect() |
| + throws Throwable { |
| + doTestShouldIgnoreNavigationCalledOnRedirect( |
| + REDIRECT_JS_URL, |
| + "kind=assign&timeout=0", |
| + REDIRECT_TARGET_URL); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCalledOnJavaScriptLocationImmediateReplaceRedirect() |
| + throws Throwable { |
| + doTestShouldIgnoreNavigationCalledOnRedirect( |
| + REDIRECT_JS_URL, |
| + "kind=replace&timeout=0", |
| + REDIRECT_TARGET_URL); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCalledOnJavaScriptLocationDelayedAssignRedirect() |
| + throws Throwable { |
| + doTestShouldIgnoreNavigationCalledOnRedirect( |
| + REDIRECT_JS_URL, |
| + "kind=assign&timeout=100", |
| + REDIRECT_TARGET_URL); |
| + } |
| + |
| + @SmallTest |
| + @Feature({"Android-WebView", "Navigation"}) |
| + public void testShouldIgnoreNavigationCalledOnJavaScriptLocationDelayedReplaceRedirect() |
| + throws Throwable { |
| + doTestShouldIgnoreNavigationCalledOnRedirect( |
| + REDIRECT_JS_URL, |
| + "kind=replace&timeout=100", |
| + REDIRECT_TARGET_URL); |
| + } |
| +} |