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 5 package org.chromium.android_webview; 6 7 import android.graphics.Rect; 8 import android.os.Handler; 9 import android.os.Looper; 10 import android.os.Message; 11 import android.util.Log; 12 import android.view.KeyEvent; 13 import android.view.View; 14 import android.webkit.ConsoleMessage; 15 import android.webkit.ValueCallback; 16 17 import org.chromium.base.ThreadUtils; 18 import 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 */ 25 class 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