1// Copyright 2013 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.android_webview;
6
7import android.graphics.Rect;
8import android.os.Handler;
9import android.os.Looper;
10import android.os.Message;
11import android.util.Log;
12import android.view.KeyEvent;
13import android.view.View;
14import android.webkit.ConsoleMessage;
15import android.webkit.ValueCallback;
16
17import org.chromium.base.ThreadUtils;
18import org.chromium.content.browser.ContentViewCore;
19
20/**
21 * Adapts the AwWebContentsDelegate interface to the AwContentsClient interface.
22 * This class also serves a secondary function of routing certain callbacks from the content layer
23 * to specific listener interfaces.
24 */
25class AwWebContentsDelegateAdapter extends AwWebContentsDelegate {
26    private static final String TAG = "AwWebContentsDelegateAdapter";
27
28    final AwContentsClient mContentsClient;
29    final View mContainerView;
30
31    public AwWebContentsDelegateAdapter(AwContentsClient contentsClient,
32            View containerView) {
33        mContentsClient = contentsClient;
34        mContainerView = containerView;
35    }
36
37    @Override
38    public void onLoadProgressChanged(int progress) {
39        mContentsClient.onProgressChanged(progress);
40    }
41
42    @Override
43    public void handleKeyboardEvent(KeyEvent event) {
44        if (event.getAction() == KeyEvent.ACTION_DOWN) {
45            int direction;
46            switch (event.getKeyCode()) {
47                case KeyEvent.KEYCODE_DPAD_DOWN:
48                    direction = View.FOCUS_DOWN;
49                    break;
50                case KeyEvent.KEYCODE_DPAD_UP:
51                    direction = View.FOCUS_UP;
52                    break;
53                case KeyEvent.KEYCODE_DPAD_LEFT:
54                    direction = View.FOCUS_LEFT;
55                    break;
56                case KeyEvent.KEYCODE_DPAD_RIGHT:
57                    direction = View.FOCUS_RIGHT;
58                    break;
59                default:
60                    direction = 0;
61                    break;
62            }
63            if (direction != 0 && tryToMoveFocus(direction)) return;
64        }
65        mContentsClient.onUnhandledKeyEvent(event);
66    }
67
68    @Override
69    public boolean takeFocus(boolean reverse) {
70        int direction =
71            (reverse == (mContainerView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL)) ?
72            View.FOCUS_RIGHT : View.FOCUS_LEFT;
73        if (tryToMoveFocus(direction)) return true;
74        direction = reverse ? View.FOCUS_UP : View.FOCUS_DOWN;
75        return tryToMoveFocus(direction);
76    }
77
78    private boolean tryToMoveFocus(int direction) {
79        View focus = mContainerView.focusSearch(direction);
80        return focus != null && focus != mContainerView && focus.requestFocus();
81    }
82
83    @Override
84    public boolean addMessageToConsole(int level, String message, int lineNumber,
85            String sourceId) {
86        ConsoleMessage.MessageLevel messageLevel = ConsoleMessage.MessageLevel.DEBUG;
87        switch(level) {
88            case LOG_LEVEL_TIP:
89                messageLevel = ConsoleMessage.MessageLevel.TIP;
90                break;
91            case LOG_LEVEL_LOG:
92                messageLevel = ConsoleMessage.MessageLevel.LOG;
93                break;
94            case LOG_LEVEL_WARNING:
95                messageLevel = ConsoleMessage.MessageLevel.WARNING;
96                break;
97            case LOG_LEVEL_ERROR:
98                messageLevel = ConsoleMessage.MessageLevel.ERROR;
99                break;
100            default:
101                Log.w(TAG, "Unknown message level, defaulting to DEBUG");
102                break;
103        }
104
105        return mContentsClient.onConsoleMessage(
106                new ConsoleMessage(message, sourceId, lineNumber, messageLevel));
107    }
108
109    @Override
110    public void onUpdateUrl(String url) {
111        // TODO: implement
112    }
113
114    @Override
115    public void openNewTab(String url, String extraHeaders, byte[] postData, int disposition) {
116        // This is only called in chrome layers.
117        assert false;
118    }
119
120    @Override
121    public boolean addNewContents(int nativeSourceWebContents, int nativeWebContents,
122            int disposition, Rect initialPosition, boolean userGesture) {
123        // This is overridden native side; see the other addNewContents overload.
124        throw new RuntimeException("Impossible");
125    }
126
127    @Override
128    public void closeContents() {
129        mContentsClient.onCloseWindow();
130    }
131
132    @Override
133    public void showRepostFormWarningDialog(final ContentViewCore contentViewCore) {
134        // TODO(mkosiba) We should be using something akin to the JsResultReceiver as the
135        // callback parameter (instead of ContentViewCore) and implement a way of converting
136        // that to a pair of messages.
137        final int MSG_CONTINUE_PENDING_RELOAD = 1;
138        final int MSG_CANCEL_PENDING_RELOAD = 2;
139
140        // TODO(sgurun) Remember the URL to cancel the reload behavior
141        // if it is different than the most recent NavigationController entry.
142        final Handler handler = new Handler(ThreadUtils.getUiThreadLooper()) {
143            @Override
144            public void handleMessage(Message msg) {
145                switch(msg.what) {
146                    case MSG_CONTINUE_PENDING_RELOAD: {
147                        contentViewCore.continuePendingReload();
148                        break;
149                    }
150                    case MSG_CANCEL_PENDING_RELOAD: {
151                        contentViewCore.cancelPendingReload();
152                        break;
153                    }
154                    default:
155                        throw new IllegalStateException(
156                                "WebContentsDelegateAdapter: unhandled message " + msg.what);
157                }
158            }
159        };
160
161        Message resend = handler.obtainMessage(MSG_CONTINUE_PENDING_RELOAD);
162        Message dontResend = handler.obtainMessage(MSG_CANCEL_PENDING_RELOAD);
163        mContentsClient.onFormResubmission(dontResend, resend);
164    }
165
166    @Override
167    public void runFileChooser(final int processId, final int renderId, final int mode_flags,
168            String acceptTypes, String title, String defaultFilename, boolean capture) {
169        AwContentsClient.FileChooserParams params = new AwContentsClient.FileChooserParams();
170        params.mode = mode_flags;
171        params.acceptTypes = acceptTypes;
172        params.title = title;
173        params.defaultFilename = defaultFilename;
174        params.capture = capture;
175
176        mContentsClient.showFileChooser(new ValueCallback<String[]>() {
177            boolean completed = false;
178            @Override
179            public void onReceiveValue(String[] results) {
180                if (completed) {
181                    throw new IllegalStateException("Duplicate showFileChooser result");
182                }
183                completed = true;
184                nativeFilesSelectedInChooser(processId, renderId, mode_flags, results);
185            }
186        }, params);
187    }
188
189    @Override
190    public boolean addNewContents(boolean isDialog, boolean isUserGesture) {
191        return mContentsClient.onCreateWindow(isDialog, isUserGesture);
192    }
193
194    @Override
195    public void activateContents() {
196        mContentsClient.onRequestFocus();
197    }
198}
199