1 /* 2 * Copyright 2006, The Android Open Source Project 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #define LOG_TAG "webcoreglue" 27 28 #include "config.h" 29 #include "WebViewCore.h" 30 31 #include "AccessibilityObject.h" 32 #include "AndroidHitTestResult.h" 33 #include "Attribute.h" 34 #include "content/address_detector.h" 35 #include "Chrome.h" 36 #include "ChromeClientAndroid.h" 37 #include "ChromiumIncludes.h" 38 #include "ClientRect.h" 39 #include "ClientRectList.h" 40 #include "Color.h" 41 #include "CSSPropertyNames.h" 42 #include "CSSValueKeywords.h" 43 #include "DatabaseTracker.h" 44 #include "Document.h" 45 #include "DocumentMarkerController.h" 46 #include "DOMWindow.h" 47 #include "DOMSelection.h" 48 #include "Element.h" 49 #include "Editor.h" 50 #include "EditorClientAndroid.h" 51 #include "EventHandler.h" 52 #include "EventNames.h" 53 #include "ExceptionCode.h" 54 #include "FocusController.h" 55 #include "Font.h" 56 #include "FontCache.h" 57 #include "Frame.h" 58 #include "FrameLoader.h" 59 #include "FrameLoaderClientAndroid.h" 60 #include "FrameTree.h" 61 #include "FrameView.h" 62 #include "Geolocation.h" 63 #include "GraphicsContext.h" 64 #include "GraphicsJNI.h" 65 #include "GraphicsOperationCollection.h" 66 #include "HTMLAnchorElement.h" 67 #include "HTMLAreaElement.h" 68 #include "HTMLElement.h" 69 #include "HTMLFormControlElement.h" 70 #include "HTMLImageElement.h" 71 #include "HTMLInputElement.h" 72 #include "HTMLLabelElement.h" 73 #include "HTMLMapElement.h" 74 #include "HTMLNames.h" 75 #include "HTMLOptGroupElement.h" 76 #include "HTMLOptionElement.h" 77 #include "HTMLSelectElement.h" 78 #include "HTMLTextAreaElement.h" 79 #include "HistoryItem.h" 80 #include "HitTestRequest.h" 81 #include "HitTestResult.h" 82 #include "InlineTextBox.h" 83 #include "KeyboardEvent.h" 84 #include "MemoryUsage.h" 85 #include "NamedNodeMap.h" 86 #include "Navigator.h" 87 #include "Node.h" 88 #include "NodeList.h" 89 #include "Page.h" 90 #include "PageGroup.h" 91 #include "PictureLayerContent.h" 92 #include "PicturePileLayerContent.h" 93 #include "PlatformKeyboardEvent.h" 94 #include "PlatformString.h" 95 #include "PluginWidgetAndroid.h" 96 #include "PluginView.h" 97 #include "Position.h" 98 #include "ProgressTracker.h" 99 #include "Range.h" 100 #include "RenderBox.h" 101 #include "RenderImage.h" 102 #include "RenderInline.h" 103 #include "RenderLayer.h" 104 #include "RenderPart.h" 105 #include "RenderText.h" 106 #include "RenderTextControl.h" 107 #include "RenderThemeAndroid.h" 108 #include "RenderView.h" 109 #include "ResourceRequest.h" 110 #include "RuntimeEnabledFeatures.h" 111 #include "SchemeRegistry.h" 112 #include "ScopedLocalRef.h" 113 #include "ScriptController.h" 114 #include "SelectionController.h" 115 #include "SelectText.h" 116 #include "Settings.h" 117 #include "SkANP.h" 118 #include "SkTemplates.h" 119 #include "SkTDArray.h" 120 #include "SkTypes.h" 121 #include "SkCanvas.h" 122 #include "SkGraphics.h" 123 #include "SkPicture.h" 124 #include "SkUtils.h" 125 #include "Text.h" 126 #include "TextIterator.h" 127 #include "TilesManager.h" 128 #include "TypingCommand.h" 129 #include "WebCache.h" 130 #include "WebCoreFrameBridge.h" 131 #include "WebCoreJni.h" 132 #include "WebFrameView.h" 133 #include "WindowsKeyboardCodes.h" 134 #include "android_graphics.h" 135 #include "autofill/WebAutofill.h" 136 #include "htmlediting.h" 137 #include "markup.h" 138 #include "visible_units.h" 139 140 #include <JNIHelp.h> 141 #include <JNIUtility.h> 142 #include <androidfw/KeycodeLabels.h> 143 #include <cutils/properties.h> 144 #include <v8.h> 145 #include <wtf/CurrentTime.h> 146 #include <wtf/text/AtomicString.h> 147 #include <wtf/text/CString.h> 148 #include <wtf/text/StringImpl.h> 149 150 #if DEBUG_NAV_UI 151 #include "SkTime.h" 152 #endif 153 154 #if ENABLE(TOUCH_EVENTS) // Android 155 #include "PlatformTouchEvent.h" 156 #endif 157 158 #ifdef ANDROID_DOM_LOGGING 159 #include "AndroidLog.h" 160 #include "RenderTreeAsText.h" 161 #include <wtf/text/CString.h> 162 163 FILE* gDomTreeFile = 0; 164 FILE* gRenderTreeFile = 0; 165 #endif 166 167 #include "BaseLayerAndroid.h" 168 169 #if USE(ACCELERATED_COMPOSITING) 170 #include "GraphicsLayerAndroid.h" 171 #include "RenderLayerCompositor.h" 172 #endif 173 174 #define FOREGROUND_TIMER_INTERVAL 0.004 // 4ms 175 #define BACKGROUND_TIMER_INTERVAL 1.0 // 1s 176 177 // How many ms to wait for the scroll to "settle" before we will consider doing 178 // prerenders 179 #define PRERENDER_AFTER_SCROLL_DELAY 750 180 181 #define TOUCH_FLAG_HIT_HANDLER 0x1 182 #define TOUCH_FLAG_PREVENT_DEFAULT 0x2 183 184 //////////////////////////////////////////////////////////////////////////////////////////////// 185 186 namespace android { 187 188 // Copied from CacheBuilder, not sure if this is needed/correct 189 IntRect getAreaRect(const HTMLAreaElement* area) 190 { 191 Node* node = area->document(); 192 while ((node = node->traverseNextNode()) != NULL) { 193 RenderObject* renderer = node->renderer(); 194 if (renderer && renderer->isRenderImage()) { 195 RenderImage* image = static_cast<RenderImage*>(renderer); 196 HTMLMapElement* map = image->imageMap(); 197 if (map) { 198 Node* n; 199 for (n = map->firstChild(); n; 200 n = n->traverseNextNode(map)) { 201 if (n == area) { 202 if (area->isDefault()) 203 return image->absoluteBoundingBoxRect(); 204 return area->computeRect(image); 205 } 206 } 207 } 208 } 209 } 210 return IntRect(); 211 } 212 213 // Copied from CacheBuilder, not sure if this is needed/correct 214 // TODO: See if this is even needed (I suspect not), and if not remove it 215 bool validNode(Frame* startFrame, void* matchFrame, 216 void* matchNode) 217 { 218 if (matchFrame == startFrame) { 219 if (matchNode == NULL) 220 return true; 221 Node* node = startFrame->document(); 222 while (node != NULL) { 223 if (node == matchNode) { 224 const IntRect& rect = node->hasTagName(HTMLNames::areaTag) ? 225 getAreaRect(static_cast<HTMLAreaElement*>(node)) : node->getRect(); 226 // Consider nodes with empty rects that are not at the origin 227 // to be valid, since news.google.com has valid nodes like this 228 if (rect.x() == 0 && rect.y() == 0 && rect.isEmpty()) 229 return false; 230 return true; 231 } 232 node = node->traverseNextNode(); 233 } 234 return false; 235 } 236 Frame* child = startFrame->tree()->firstChild(); 237 while (child) { 238 bool result = validNode(child, matchFrame, matchNode); 239 if (result) 240 return result; 241 child = child->tree()->nextSibling(); 242 } 243 return false; 244 } 245 246 static SkTDArray<WebViewCore*> gInstanceList; 247 248 void WebViewCore::addInstance(WebViewCore* inst) { 249 *gInstanceList.append() = inst; 250 } 251 252 void WebViewCore::removeInstance(WebViewCore* inst) { 253 int index = gInstanceList.find(inst); 254 ALOG_ASSERT(index >= 0, "RemoveInstance inst not found"); 255 if (index >= 0) { 256 gInstanceList.removeShuffle(index); 257 } 258 } 259 260 bool WebViewCore::isInstance(WebViewCore* inst) { 261 return gInstanceList.find(inst) >= 0; 262 } 263 264 jobject WebViewCore::getApplicationContext() { 265 266 // check to see if there is a valid webviewcore object 267 if (gInstanceList.isEmpty()) 268 return 0; 269 270 // get the context from the webview 271 jobject context = gInstanceList[0]->getContext(); 272 273 if (!context) 274 return 0; 275 276 // get the application context using JNI 277 JNIEnv* env = JSC::Bindings::getJNIEnv(); 278 jclass contextClass = env->GetObjectClass(context); 279 jmethodID appContextMethod = env->GetMethodID(contextClass, "getApplicationContext", "()Landroid/content/Context;"); 280 env->DeleteLocalRef(contextClass); 281 jobject result = env->CallObjectMethod(context, appContextMethod); 282 checkException(env); 283 return result; 284 } 285 286 287 struct WebViewCoreStaticMethods { 288 jmethodID m_isSupportedMediaMimeType; 289 } gWebViewCoreStaticMethods; 290 291 // Check whether a media mimeType is supported in Android media framework. 292 bool WebViewCore::isSupportedMediaMimeType(const WTF::String& mimeType) { 293 JNIEnv* env = JSC::Bindings::getJNIEnv(); 294 jstring jMimeType = wtfStringToJstring(env, mimeType); 295 jclass webViewCore = env->FindClass("android/webkit/WebViewCore"); 296 bool val = env->CallStaticBooleanMethod(webViewCore, 297 gWebViewCoreStaticMethods.m_isSupportedMediaMimeType, jMimeType); 298 checkException(env); 299 env->DeleteLocalRef(webViewCore); 300 env->DeleteLocalRef(jMimeType); 301 302 return val; 303 } 304 305 // ---------------------------------------------------------------------------- 306 307 // Field ids for WebViewCore 308 struct WebViewCoreFields { 309 jfieldID m_nativeClass; 310 jfieldID m_viewportWidth; 311 jfieldID m_viewportHeight; 312 jfieldID m_viewportInitialScale; 313 jfieldID m_viewportMinimumScale; 314 jfieldID m_viewportMaximumScale; 315 jfieldID m_viewportUserScalable; 316 jfieldID m_viewportDensityDpi; 317 jfieldID m_drawIsPaused; 318 jfieldID m_lowMemoryUsageMb; 319 jfieldID m_highMemoryUsageMb; 320 jfieldID m_highUsageDeltaMb; 321 } gWebViewCoreFields; 322 323 // ---------------------------------------------------------------------------- 324 325 struct WebViewCore::JavaGlue { 326 jweak m_obj; 327 jmethodID m_scrollTo; 328 jmethodID m_contentDraw; 329 jmethodID m_requestListBox; 330 jmethodID m_openFileChooser; 331 jmethodID m_requestSingleListBox; 332 jmethodID m_jsAlert; 333 jmethodID m_jsConfirm; 334 jmethodID m_jsPrompt; 335 jmethodID m_jsUnload; 336 jmethodID m_jsInterrupt; 337 jmethodID m_getWebView; 338 jmethodID m_didFirstLayout; 339 jmethodID m_updateViewport; 340 jmethodID m_sendNotifyProgressFinished; 341 jmethodID m_sendViewInvalidate; 342 jmethodID m_updateTextfield; 343 jmethodID m_updateTextSelection; 344 jmethodID m_updateTextSizeAndScroll; 345 jmethodID m_clearTextEntry; 346 jmethodID m_restoreScale; 347 jmethodID m_needTouchEvents; 348 jmethodID m_requestKeyboard; 349 jmethodID m_exceededDatabaseQuota; 350 jmethodID m_reachedMaxAppCacheSize; 351 jmethodID m_populateVisitedLinks; 352 jmethodID m_geolocationPermissionsShowPrompt; 353 jmethodID m_geolocationPermissionsHidePrompt; 354 jmethodID m_getDeviceMotionService; 355 jmethodID m_getDeviceOrientationService; 356 jmethodID m_addMessageToConsole; 357 jmethodID m_focusNodeChanged; 358 jmethodID m_getPluginClass; 359 jmethodID m_showFullScreenPlugin; 360 jmethodID m_hideFullScreenPlugin; 361 jmethodID m_createSurface; 362 jmethodID m_addSurface; 363 jmethodID m_updateSurface; 364 jmethodID m_destroySurface; 365 jmethodID m_getContext; 366 jmethodID m_keepScreenOn; 367 jmethodID m_showRect; 368 jmethodID m_centerFitRect; 369 jmethodID m_setScrollbarModes; 370 jmethodID m_setInstallableWebApp; 371 jmethodID m_enterFullscreenForVideoLayer; 372 jmethodID m_exitFullscreenVideo; 373 jmethodID m_setWebTextViewAutoFillable; 374 jmethodID m_selectAt; 375 jmethodID m_initEditField; 376 jmethodID m_chromeCanTakeFocus; 377 jmethodID m_chromeTakeFocus; 378 AutoJObject object(JNIEnv* env) { 379 // We hold a weak reference to the Java WebViewCore to avoid memeory 380 // leaks due to circular references when WebView.destroy() is not 381 // called manually. The WebView and hence the WebViewCore could become 382 // weakly reachable at any time, after which the GC could null our weak 383 // reference, so we have to check the return value of this method at 384 // every use. Note that our weak reference will be nulled before the 385 // WebViewCore is finalized. 386 return getRealObject(env, m_obj); 387 } 388 }; 389 390 struct WebViewCore::TextFieldInitDataGlue { 391 jmethodID m_constructor; 392 jfieldID m_fieldPointer; 393 jfieldID m_text; 394 jfieldID m_type; 395 jfieldID m_isSpellCheckEnabled; 396 jfieldID m_isTextFieldNext; 397 jfieldID m_isTextFieldPrev; 398 jfieldID m_isAutoCompleteEnabled; 399 jfieldID m_name; 400 jfieldID m_label; 401 jfieldID m_maxLength; 402 jfieldID m_contentBounds; 403 jfieldID m_nodeLayerId; 404 jfieldID m_contentRect; 405 }; 406 407 /* 408 * WebViewCore Implementation 409 */ 410 411 static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[]) 412 { 413 jmethodID m = env->GetMethodID(clazz, name, signature); 414 ALOG_ASSERT(m, "Could not find method %s", name); 415 return m; 416 } 417 418 WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe) 419 : m_touchGeneration(0) 420 , m_lastGeneration(0) 421 , m_javaGlue(new JavaGlue) 422 , m_textFieldInitDataGlue(new TextFieldInitDataGlue) 423 , m_mainFrame(mainframe) 424 , m_popupReply(0) 425 , m_blockTextfieldUpdates(false) 426 , m_focusBoundsChanged(false) 427 , m_skipContentDraw(false) 428 , m_textGeneration(0) 429 , m_maxXScroll(320/4) 430 , m_maxYScroll(240/4) 431 , m_scrollOffsetX(0) 432 , m_scrollOffsetY(0) 433 , m_mousePos(WebCore::IntPoint(0,0)) 434 , m_screenWidth(320) 435 , m_screenHeight(240) 436 , m_textWrapWidth(320) 437 , m_scale(1.0f) 438 , m_groupForVisitedLinks(0) 439 , m_isPaused(false) 440 , m_cacheMode(0) 441 , m_fullscreenVideoMode(false) 442 , m_matchCount(0) 443 , m_activeMatchIndex(0) 444 , m_activeMatch(0) 445 , m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired) 446 , m_screenOnCounter(0) 447 , m_currentNodeDomNavigationAxis(0) 448 , m_deviceMotionAndOrientationManager(this) 449 #if ENABLE(TOUCH_EVENTS) 450 , m_forwardingTouchEvents(false) 451 #endif 452 , m_webRequestContext(0) 453 , m_prerenderEnabled(false) 454 { 455 ALOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!"); 456 457 jclass clazz = env->GetObjectClass(javaWebViewCore); 458 m_javaGlue->m_obj = env->NewWeakGlobalRef(javaWebViewCore); 459 m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(IIZZ)V"); 460 m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V"); 461 m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[I[I)V"); 462 m_javaGlue->m_openFileChooser = GetJMethod(env, clazz, "openFileChooser", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); 463 m_javaGlue->m_requestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[II)V"); 464 m_javaGlue->m_jsAlert = GetJMethod(env, clazz, "jsAlert", "(Ljava/lang/String;Ljava/lang/String;)V"); 465 m_javaGlue->m_jsConfirm = GetJMethod(env, clazz, "jsConfirm", "(Ljava/lang/String;Ljava/lang/String;)Z"); 466 m_javaGlue->m_jsPrompt = GetJMethod(env, clazz, "jsPrompt", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); 467 m_javaGlue->m_jsUnload = GetJMethod(env, clazz, "jsUnload", "(Ljava/lang/String;Ljava/lang/String;)Z"); 468 m_javaGlue->m_jsInterrupt = GetJMethod(env, clazz, "jsInterrupt", "()Z"); 469 m_javaGlue->m_getWebView = GetJMethod(env, clazz, "getWebView", "()Landroid/webkit/WebView;"); 470 m_javaGlue->m_didFirstLayout = GetJMethod(env, clazz, "didFirstLayout", "(Z)V"); 471 m_javaGlue->m_updateViewport = GetJMethod(env, clazz, "updateViewport", "()V"); 472 m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V"); 473 m_javaGlue->m_sendViewInvalidate = GetJMethod(env, clazz, "sendViewInvalidate", "(IIII)V"); 474 m_javaGlue->m_updateTextfield = GetJMethod(env, clazz, "updateTextfield", "(IZLjava/lang/String;I)V"); 475 m_javaGlue->m_updateTextSelection = GetJMethod(env, clazz, "updateTextSelection", "(IIIII)V"); 476 m_javaGlue->m_updateTextSizeAndScroll = GetJMethod(env, clazz, "updateTextSizeAndScroll", "(IIIII)V"); 477 m_javaGlue->m_clearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "()V"); 478 m_javaGlue->m_restoreScale = GetJMethod(env, clazz, "restoreScale", "(FF)V"); 479 m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V"); 480 m_javaGlue->m_requestKeyboard = GetJMethod(env, clazz, "requestKeyboard", "(Z)V"); 481 m_javaGlue->m_exceededDatabaseQuota = GetJMethod(env, clazz, "exceededDatabaseQuota", "(Ljava/lang/String;Ljava/lang/String;JJ)V"); 482 m_javaGlue->m_reachedMaxAppCacheSize = GetJMethod(env, clazz, "reachedMaxAppCacheSize", "(J)V"); 483 m_javaGlue->m_populateVisitedLinks = GetJMethod(env, clazz, "populateVisitedLinks", "()V"); 484 m_javaGlue->m_geolocationPermissionsShowPrompt = GetJMethod(env, clazz, "geolocationPermissionsShowPrompt", "(Ljava/lang/String;)V"); 485 m_javaGlue->m_geolocationPermissionsHidePrompt = GetJMethod(env, clazz, "geolocationPermissionsHidePrompt", "()V"); 486 m_javaGlue->m_getDeviceMotionService = GetJMethod(env, clazz, "getDeviceMotionService", "()Landroid/webkit/DeviceMotionService;"); 487 m_javaGlue->m_getDeviceOrientationService = GetJMethod(env, clazz, "getDeviceOrientationService", "()Landroid/webkit/DeviceOrientationService;"); 488 m_javaGlue->m_addMessageToConsole = GetJMethod(env, clazz, "addMessageToConsole", "(Ljava/lang/String;ILjava/lang/String;I)V"); 489 m_javaGlue->m_focusNodeChanged = GetJMethod(env, clazz, "focusNodeChanged", "(ILandroid/webkit/WebViewCore$WebKitHitTest;)V"); 490 m_javaGlue->m_getPluginClass = GetJMethod(env, clazz, "getPluginClass", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Class;"); 491 m_javaGlue->m_showFullScreenPlugin = GetJMethod(env, clazz, "showFullScreenPlugin", "(Landroid/webkit/ViewManager$ChildView;II)V"); 492 m_javaGlue->m_hideFullScreenPlugin = GetJMethod(env, clazz, "hideFullScreenPlugin", "()V"); 493 m_javaGlue->m_createSurface = GetJMethod(env, clazz, "createSurface", "(Landroid/view/View;)Landroid/webkit/ViewManager$ChildView;"); 494 m_javaGlue->m_addSurface = GetJMethod(env, clazz, "addSurface", "(Landroid/view/View;IIII)Landroid/webkit/ViewManager$ChildView;"); 495 m_javaGlue->m_updateSurface = GetJMethod(env, clazz, "updateSurface", "(Landroid/webkit/ViewManager$ChildView;IIII)V"); 496 m_javaGlue->m_destroySurface = GetJMethod(env, clazz, "destroySurface", "(Landroid/webkit/ViewManager$ChildView;)V"); 497 m_javaGlue->m_getContext = GetJMethod(env, clazz, "getContext", "()Landroid/content/Context;"); 498 m_javaGlue->m_keepScreenOn = GetJMethod(env, clazz, "keepScreenOn", "(Z)V"); 499 m_javaGlue->m_showRect = GetJMethod(env, clazz, "showRect", "(IIIIIIFFFF)V"); 500 m_javaGlue->m_centerFitRect = GetJMethod(env, clazz, "centerFitRect", "(IIII)V"); 501 m_javaGlue->m_setScrollbarModes = GetJMethod(env, clazz, "setScrollbarModes", "(II)V"); 502 m_javaGlue->m_setInstallableWebApp = GetJMethod(env, clazz, "setInstallableWebApp", "()V"); 503 #if ENABLE(VIDEO) 504 m_javaGlue->m_enterFullscreenForVideoLayer = GetJMethod(env, clazz, "enterFullscreenForVideoLayer", "(ILjava/lang/String;)V"); 505 m_javaGlue->m_exitFullscreenVideo = GetJMethod(env, clazz, "exitFullscreenVideo", "()V"); 506 #endif 507 m_javaGlue->m_setWebTextViewAutoFillable = GetJMethod(env, clazz, "setWebTextViewAutoFillable", "(ILjava/lang/String;)V"); 508 m_javaGlue->m_selectAt = GetJMethod(env, clazz, "selectAt", "(II)V"); 509 m_javaGlue->m_initEditField = GetJMethod(env, clazz, "initEditField", "(IIILandroid/webkit/WebViewCore$TextFieldInitData;)V"); 510 m_javaGlue->m_chromeCanTakeFocus = GetJMethod(env, clazz, "chromeCanTakeFocus", "(I)Z"); 511 m_javaGlue->m_chromeTakeFocus = GetJMethod(env, clazz, "chromeTakeFocus", "(I)V"); 512 env->DeleteLocalRef(clazz); 513 514 env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this); 515 516 jclass tfidClazz = env->FindClass("android/webkit/WebViewCore$TextFieldInitData"); 517 m_textFieldInitDataGlue->m_fieldPointer = env->GetFieldID(tfidClazz, "mFieldPointer", "I"); 518 m_textFieldInitDataGlue->m_text = env->GetFieldID(tfidClazz, "mText", "Ljava/lang/String;"); 519 m_textFieldInitDataGlue->m_type = env->GetFieldID(tfidClazz, "mType", "I"); 520 m_textFieldInitDataGlue->m_isSpellCheckEnabled = env->GetFieldID(tfidClazz, "mIsSpellCheckEnabled", "Z"); 521 m_textFieldInitDataGlue->m_isTextFieldNext = env->GetFieldID(tfidClazz, "mIsTextFieldNext", "Z"); 522 m_textFieldInitDataGlue->m_isTextFieldPrev = env->GetFieldID(tfidClazz, "mIsTextFieldPrev", "Z"); 523 m_textFieldInitDataGlue->m_isAutoCompleteEnabled = env->GetFieldID(tfidClazz, "mIsAutoCompleteEnabled", "Z"); 524 m_textFieldInitDataGlue->m_name = env->GetFieldID(tfidClazz, "mName", "Ljava/lang/String;"); 525 m_textFieldInitDataGlue->m_label = env->GetFieldID(tfidClazz, "mLabel", "Ljava/lang/String;"); 526 m_textFieldInitDataGlue->m_maxLength = env->GetFieldID(tfidClazz, "mMaxLength", "I"); 527 m_textFieldInitDataGlue->m_contentBounds = env->GetFieldID(tfidClazz, "mContentBounds", "Landroid/graphics/Rect;"); 528 m_textFieldInitDataGlue->m_nodeLayerId = env->GetFieldID(tfidClazz, "mNodeLayerId", "I"); 529 m_textFieldInitDataGlue->m_contentRect = env->GetFieldID(tfidClazz, "mContentRect", "Landroid/graphics/Rect;"); 530 m_textFieldInitDataGlue->m_constructor = GetJMethod(env, tfidClazz, "<init>", "()V"); 531 env->DeleteLocalRef(tfidClazz); 532 533 PageGroup::setShouldTrackVisitedLinks(true); 534 535 clearContent(); 536 537 MemoryUsage::setLowMemoryUsageMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_lowMemoryUsageMb)); 538 MemoryUsage::setHighMemoryUsageMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_highMemoryUsageMb)); 539 MemoryUsage::setHighUsageDeltaMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_highUsageDeltaMb)); 540 541 WebViewCore::addInstance(this); 542 543 AndroidNetworkLibraryImpl::InitWithApplicationContext(env, 0); 544 545 // increase the font cache size beyond the standard system setting 546 SkGraphics::SetFontCacheLimit(1572864); // 1572864 bytes == 1.5 MB 547 548 // Static initialisation of certain important V8 static data gets performed at system startup when 549 // libwebcore gets loaded. We now need to associate the WebCore thread with V8 to complete 550 // initialisation. 551 v8::V8::Initialize(); 552 553 // Configure any RuntimeEnabled features that we need to change from their default now. 554 // See WebCore/bindings/generic/RuntimeEnabledFeatures.h 555 556 // HTML5 History API 557 RuntimeEnabledFeatures::setPushStateEnabled(true); 558 if (m_mainFrame) 559 m_mainFrame->settings()->setMinDOMTimerInterval(FOREGROUND_TIMER_INTERVAL); 560 } 561 562 WebViewCore::~WebViewCore() 563 { 564 WebViewCore::removeInstance(this); 565 566 // Release the focused view 567 Release(m_popupReply); 568 569 if (m_javaGlue->m_obj) { 570 JNIEnv* env = JSC::Bindings::getJNIEnv(); 571 env->DeleteWeakGlobalRef(m_javaGlue->m_obj); 572 m_javaGlue->m_obj = 0; 573 } 574 delete m_javaGlue; 575 } 576 577 WebViewCore* WebViewCore::getWebViewCore(const WebCore::FrameView* view) 578 { 579 if (!view) 580 return 0; 581 if (view->platformWidget()) 582 return static_cast<WebFrameView*>(view->platformWidget())->webViewCore(); 583 Frame* frame = view->frame(); 584 while (Frame* parent = frame->tree()->parent()) 585 frame = parent; 586 WebFrameView* webFrameView = 0; 587 if (frame && frame->view()) 588 webFrameView = static_cast<WebFrameView*>(frame->view()->platformWidget()); 589 if (!webFrameView) 590 return 0; 591 return webFrameView->webViewCore(); 592 } 593 594 WebViewCore* WebViewCore::getWebViewCore(const WebCore::ScrollView* view) 595 { 596 if (!view) 597 return 0; 598 if (view->platformWidget()) 599 return static_cast<WebFrameView*>(view->platformWidget())->webViewCore(); 600 const FrameView* frameView = 0; 601 if (view->isFrameView()) 602 frameView = static_cast<const FrameView*>(view); 603 else { 604 frameView = static_cast<const FrameView*>(view->root()); 605 if (!frameView) 606 return 0; 607 } 608 return getWebViewCore(frameView); 609 } 610 611 static bool layoutIfNeededRecursive(WebCore::Frame* f) 612 { 613 if (!f) 614 return true; 615 616 WebCore::FrameView* v = f->view(); 617 if (!v) 618 return true; 619 v->updateLayoutAndStyleIfNeededRecursive(); 620 return !v->needsLayout(); 621 } 622 623 WebCore::Node* WebViewCore::currentFocus() 624 { 625 return focusedFrame()->document()->focusedNode(); 626 } 627 628 void WebViewCore::layout() 629 { 630 TRACE_METHOD(); 631 632 // if there is no document yet, just return 633 if (!m_mainFrame->document()) { 634 ALOGV("!m_mainFrame->document()"); 635 return; 636 } 637 638 // Call layout to ensure that the contentWidth and contentHeight are correct 639 // it's fine for layout to gather invalidates, but defeat sending a message 640 // back to java to call webkitDraw, since we're already in the middle of 641 // doing that 642 bool success = layoutIfNeededRecursive(m_mainFrame); 643 644 // We may be mid-layout and thus cannot draw. 645 if (!success) 646 return; 647 648 // if the webkit page dimensions changed, discard the pictureset and redraw. 649 WebCore::FrameView* view = m_mainFrame->view(); 650 int width = view->contentsWidth(); 651 int height = view->contentsHeight(); 652 653 // Use the contents width and height as a starting point. 654 SkIRect contentRect; 655 contentRect.set(0, 0, width, height); 656 SkIRect total(contentRect); 657 658 // Traverse all the frames and add their sizes if they are in the visible 659 // rectangle. 660 for (WebCore::Frame* frame = m_mainFrame->tree()->traverseNext(); frame; 661 frame = frame->tree()->traverseNext()) { 662 // If the frame doesn't have an owner then it is the top frame and the 663 // view size is the frame size. 664 WebCore::RenderPart* owner = frame->ownerRenderer(); 665 if (owner && owner->style()->visibility() == VISIBLE) { 666 int x = owner->x(); 667 int y = owner->y(); 668 669 // Traverse the tree up to the parent to find the absolute position 670 // of this frame. 671 WebCore::Frame* parent = frame->tree()->parent(); 672 while (parent) { 673 WebCore::RenderPart* parentOwner = parent->ownerRenderer(); 674 if (parentOwner) { 675 x += parentOwner->x(); 676 y += parentOwner->y(); 677 } 678 parent = parent->tree()->parent(); 679 } 680 // Use the owner dimensions so that padding and border are 681 // included. 682 int right = x + owner->width(); 683 int bottom = y + owner->height(); 684 SkIRect frameRect = {x, y, right, bottom}; 685 // Ignore a width or height that is smaller than 1. Some iframes 686 // have small dimensions in order to be hidden. The iframe 687 // expansion code does not expand in that case so we should ignore 688 // them here. 689 if (frameRect.width() > 1 && frameRect.height() > 1 690 && SkIRect::Intersects(total, frameRect)) 691 total.join(x, y, right, bottom); 692 } 693 } 694 695 // If the new total is larger than the content, resize the view to include 696 // all the content. 697 if (!contentRect.contains(total)) { 698 // TODO: Does this ever happen? Is this needed now that we don't flatten 699 // frames? 700 // Resize the view to change the overflow clip. 701 view->resize(total.fRight, total.fBottom); 702 703 // We have to force a layout in order for the clip to change. 704 m_mainFrame->contentRenderer()->setNeedsLayoutAndPrefWidthsRecalc(); 705 view->forceLayout(); 706 707 // Relayout similar to above 708 layoutIfNeededRecursive(m_mainFrame); 709 } 710 } 711 712 void WebViewCore::recordPicturePile() 713 { 714 // if the webkit page dimensions changed, discard the pictureset and redraw. 715 WebCore::FrameView* view = m_mainFrame->view(); 716 int width = view ? view->contentsWidth() : 0; 717 int height = view ? view->contentsHeight() : 0; 718 719 m_content.setSize(IntSize(width, height)); 720 721 // Rebuild the pictureset (webkit repaint) 722 m_content.updatePicturesIfNeeded(this); 723 } 724 725 void WebViewCore::clearContent() 726 { 727 m_content.reset(); 728 updateLocale(); 729 } 730 731 bool WebViewCore::focusBoundsChanged() 732 { 733 bool result = m_focusBoundsChanged; 734 m_focusBoundsChanged = false; 735 return result; 736 } 737 738 void WebViewCore::paintContents(WebCore::GraphicsContext* gc, WebCore::IntRect& dirty) 739 { 740 WebCore::FrameView* view = m_mainFrame->view(); 741 if (!view) { 742 gc->setFillColor(WebCore::Color::white, WebCore::ColorSpaceDeviceRGB); 743 gc->fillColor(); 744 return; 745 } 746 747 IntPoint origin = view->minimumScrollPosition(); 748 IntRect drawArea = dirty; 749 gc->translate(-origin.x(), -origin.y()); 750 drawArea.move(origin.x(), origin.y()); 751 view->platformWidget()->draw(gc, drawArea); 752 } 753 754 void WebViewCore::setPrerenderingEnabled(bool enable) 755 { 756 MutexLocker locker(m_prerenderLock); 757 m_prerenderEnabled = enable; 758 } 759 760 bool WebViewCore::prerenderingEnabled() 761 { 762 MutexLocker locker(m_prerenderLock); 763 return m_prerenderEnabled; 764 } 765 766 SkCanvas* WebViewCore::createPrerenderCanvas(PrerenderedInval* prerendered) 767 { 768 // Has WebView disabled prerenders (not attached, etc...)? 769 if (!prerenderingEnabled()) 770 return 0; 771 // Does this WebView have focus? 772 if (!m_mainFrame->page()->focusController()->isActive()) 773 return 0; 774 // Are we scrolling? 775 if (currentTimeMS() - m_scrollSetTime < PRERENDER_AFTER_SCROLL_DELAY) 776 return 0; 777 // Do we have anything to render? 778 if (prerendered->area.isEmpty()) 779 return 0; 780 FloatRect scaleTemp(m_scrollOffsetX, m_scrollOffsetY, m_screenWidth, m_screenHeight); 781 scaleTemp.scale(m_scale); 782 IntRect visibleTileClip = enclosingIntRect(scaleTemp); 783 FloatRect scaledArea = prerendered->area; 784 scaledArea.scale(m_scale); 785 IntRect enclosingScaledArea = enclosingIntRect(scaledArea); 786 if (enclosingScaledArea.isEmpty()) 787 return 0; 788 // "round out" the screen to tile boundaries so that we can clip yet still 789 // cover any visible tiles with the prerender 790 int tw = TilesManager::tileWidth(); 791 int th = TilesManager::tileHeight(); 792 float left = tw * (int) (visibleTileClip.x() / tw); 793 float top = th * (int) (visibleTileClip.y() / th); 794 float right = tw * (int) ceilf(visibleTileClip.maxX() / (float) tw); 795 float bottom = th * (int) ceilf(visibleTileClip.maxY() / (float) th); 796 visibleTileClip = IntRect(left, top, right - left, bottom - top); 797 enclosingScaledArea.intersect(visibleTileClip); 798 if (enclosingScaledArea.isEmpty()) 799 return 0; 800 prerendered->screenArea = enclosingScaledArea; 801 FloatRect enclosingDocArea(enclosingScaledArea); 802 enclosingDocArea.scale(1 / m_scale); 803 prerendered->area = enclosingIntRect(enclosingDocArea); 804 if (prerendered->area.isEmpty()) 805 return 0; 806 prerendered->bitmap.setConfig(SkBitmap::kARGB_8888_Config, 807 enclosingScaledArea.width(), 808 enclosingScaledArea.height()); 809 prerendered->bitmap.allocPixels(); 810 SkCanvas* bitmapCanvas = new SkCanvas(prerendered->bitmap); 811 bitmapCanvas->scale(m_scale, m_scale); 812 bitmapCanvas->translate(-enclosingDocArea.x(), -enclosingDocArea.y()); 813 return bitmapCanvas; 814 } 815 816 void WebViewCore::notifyAnimationStarted() 817 { 818 // We notify webkit that the animations have begun 819 // TODO: handle case where not all have begun 820 ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client()); 821 GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync()); 822 if (root) 823 root->notifyClientAnimationStarted(); 824 825 } 826 827 BaseLayerAndroid* WebViewCore::createBaseLayer(GraphicsLayerAndroid* root) 828 { 829 // We set the background color 830 Color background = Color::white; 831 832 bool bodyHasFixedBackgroundImage = false; 833 bool bodyHasCSSBackground = false; 834 835 if (m_mainFrame && m_mainFrame->document() 836 && m_mainFrame->document()->body()) { 837 838 Document* document = m_mainFrame->document(); 839 RefPtr<RenderStyle> style = document->styleForElementIgnoringPendingStylesheets(document->body()); 840 if (style->hasBackground()) { 841 background = style->visitedDependentColor(CSSPropertyBackgroundColor); 842 bodyHasCSSBackground = true; 843 } 844 WebCore::FrameView* view = m_mainFrame->view(); 845 if (view) { 846 Color viewBackground = view->baseBackgroundColor(); 847 background = bodyHasCSSBackground ? viewBackground.blend(background) : viewBackground; 848 } 849 if (style->hasFixedBackgroundImage()) { 850 Image* backgroundImage = FixedBackgroundImageLayerAndroid::GetCachedImage(style); 851 if (backgroundImage && backgroundImage->width() > 1 && backgroundImage->height() > 1) 852 bodyHasFixedBackgroundImage = true; 853 } 854 } 855 856 PicturePileLayerContent* content = new PicturePileLayerContent(m_content); 857 m_content.clearPrerenders(); 858 859 BaseLayerAndroid* realBase = 0; 860 LayerAndroid* base = 0; 861 862 //If we have a fixed background image on the body element, the fixed image 863 // will be contained in the PictureSet (the content object), and the foreground 864 //of the body element will be moved to a layer. 865 //In that case, let's change the hierarchy to obtain: 866 // 867 //BaseLayerAndroid 868 // \- FixedBackgroundBaseLayerAndroid (fixed positioning) 869 // \- ForegroundBaseLayerAndroid 870 // \- root layer (webkit composited tree) 871 872 if (bodyHasFixedBackgroundImage) { 873 base = new ForegroundBaseLayerAndroid(0); 874 base->setSize(content->width(), content->height()); 875 876 Document* document = m_mainFrame->document(); 877 RefPtr<RenderStyle> style = document->styleForElementIgnoringPendingStylesheets(document->body()); 878 879 FixedBackgroundImageLayerAndroid* baseBackground = 880 new FixedBackgroundImageLayerAndroid(style, content->width(), content->height()); 881 882 realBase = new BaseLayerAndroid(0); 883 realBase->setSize(content->width(), content->height()); 884 realBase->addChild(baseBackground); 885 realBase->addChild(base); 886 baseBackground->unref(); 887 base->unref(); 888 } else { 889 realBase = new BaseLayerAndroid(content); 890 base = realBase; 891 } 892 893 realBase->setBackgroundColor(background); 894 895 SkSafeUnref(content); 896 897 // We update the layers 898 if (root) { 899 LayerAndroid* copyLayer = new LayerAndroid(*root->contentLayer()); 900 base->addChild(copyLayer); 901 copyLayer->unref(); 902 root->contentLayer()->clearDirtyRegion(); 903 } 904 905 return realBase; 906 } 907 908 BaseLayerAndroid* WebViewCore::recordContent(SkIPoint* point) 909 { 910 m_skipContentDraw = true; 911 layout(); 912 ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client()); 913 GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync()); 914 m_skipContentDraw = false; 915 recordPicturePile(); 916 917 BaseLayerAndroid* baseLayer = createBaseLayer(root); 918 919 baseLayer->markAsDirty(m_content.dirtyRegion()); 920 m_content.dirtyRegion().setEmpty(); 921 #if USE(ACCELERATED_COMPOSITING) 922 #else 923 baseLayer->markAsDirty(m_rebuildInval); 924 #endif 925 point->fX = m_content.size().width(); 926 point->fY = m_content.size().height(); 927 928 return baseLayer; 929 } 930 931 void WebViewCore::scrollTo(int x, int y, bool animate) 932 { 933 ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 934 935 JNIEnv* env = JSC::Bindings::getJNIEnv(); 936 AutoJObject javaObject = m_javaGlue->object(env); 937 if (!javaObject.get()) 938 return; 939 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_scrollTo, 940 x, y, animate, false); 941 checkException(env); 942 } 943 944 void WebViewCore::sendNotifyProgressFinished() 945 { 946 ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 947 JNIEnv* env = JSC::Bindings::getJNIEnv(); 948 AutoJObject javaObject = m_javaGlue->object(env); 949 if (!javaObject.get()) 950 return; 951 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_sendNotifyProgressFinished); 952 checkException(env); 953 } 954 955 void WebViewCore::viewInvalidate(const WebCore::IntRect& rect) 956 { 957 ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 958 JNIEnv* env = JSC::Bindings::getJNIEnv(); 959 AutoJObject javaObject = m_javaGlue->object(env); 960 if (!javaObject.get()) 961 return; 962 env->CallVoidMethod(javaObject.get(), 963 m_javaGlue->m_sendViewInvalidate, 964 rect.x(), rect.y(), rect.maxX(), rect.maxY()); 965 checkException(env); 966 } 967 968 void WebViewCore::contentDraw() 969 { 970 JNIEnv* env = JSC::Bindings::getJNIEnv(); 971 AutoJObject javaObject = m_javaGlue->object(env); 972 if (!javaObject.get()) 973 return; 974 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_contentDraw); 975 checkException(env); 976 } 977 978 void WebViewCore::contentInvalidate(const WebCore::IntRect &r) 979 { 980 IntPoint origin = m_mainFrame->view()->minimumScrollPosition(); 981 IntRect dirty = r; 982 dirty.move(-origin.x(), -origin.y()); 983 m_content.invalidate(dirty); 984 if (!m_skipContentDraw) 985 contentDraw(); 986 } 987 988 void WebViewCore::contentInvalidateAll() 989 { 990 WebCore::FrameView* view = m_mainFrame->view(); 991 contentInvalidate(WebCore::IntRect(0, 0, 992 view->contentsWidth(), view->contentsHeight())); 993 } 994 995 void WebViewCore::offInvalidate(const WebCore::IntRect &r) 996 { 997 // FIXME: these invalidates are offscreen, and can be throttled or 998 // deferred until the area is visible. For now, treat them as 999 // regular invals so that drawing happens (inefficiently) for now. 1000 contentInvalidate(r); 1001 } 1002 1003 void WebViewCore::didFirstLayout() 1004 { 1005 ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1006 1007 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1008 AutoJObject javaObject = m_javaGlue->object(env); 1009 if (!javaObject.get()) 1010 return; 1011 1012 const WebCore::KURL& url = m_mainFrame->document()->url(); 1013 if (url.isEmpty()) 1014 return; 1015 ALOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data()); 1016 1017 WebCore::FrameLoadType loadType = m_mainFrame->loader()->loadType(); 1018 1019 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_didFirstLayout, 1020 loadType == WebCore::FrameLoadTypeStandard 1021 // When redirect with locked history, we would like to reset the 1022 // scale factor. This is important for www.yahoo.com as it is 1023 // redirected to www.yahoo.com/?rs=1 on load. 1024 || loadType == WebCore::FrameLoadTypeRedirectWithLockedBackForwardList 1025 // When "request desktop page" is used, we want to treat it as 1026 // a newly-loaded page. 1027 || loadType == WebCore::FrameLoadTypeSame); 1028 checkException(env); 1029 } 1030 1031 void WebViewCore::updateViewport() 1032 { 1033 ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1034 1035 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1036 AutoJObject javaObject = m_javaGlue->object(env); 1037 if (!javaObject.get()) 1038 return; 1039 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_updateViewport); 1040 checkException(env); 1041 } 1042 1043 void WebViewCore::restoreScale(float scale, float textWrapScale) 1044 { 1045 ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1046 1047 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1048 AutoJObject javaObject = m_javaGlue->object(env); 1049 if (!javaObject.get()) 1050 return; 1051 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_restoreScale, scale, textWrapScale); 1052 checkException(env); 1053 } 1054 1055 void WebViewCore::needTouchEvents(bool need) 1056 { 1057 ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1058 1059 #if ENABLE(TOUCH_EVENTS) 1060 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1061 AutoJObject javaObject = m_javaGlue->object(env); 1062 if (!javaObject.get()) 1063 return; 1064 1065 if (m_forwardingTouchEvents == need) 1066 return; 1067 1068 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_needTouchEvents, need); 1069 checkException(env); 1070 1071 m_forwardingTouchEvents = need; 1072 #endif 1073 } 1074 1075 void WebViewCore::requestKeyboard(bool showKeyboard) 1076 { 1077 ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1078 1079 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1080 AutoJObject javaObject = m_javaGlue->object(env); 1081 if (!javaObject.get()) 1082 return; 1083 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_requestKeyboard, showKeyboard); 1084 checkException(env); 1085 } 1086 1087 void WebViewCore::notifyProgressFinished() 1088 { 1089 sendNotifyProgressFinished(); 1090 } 1091 1092 void WebViewCore::setScrollOffset(bool sendScrollEvent, int dx, int dy) 1093 { 1094 if (m_scrollOffsetX != dx || m_scrollOffsetY != dy) { 1095 m_scrollOffsetX = dx; 1096 m_scrollOffsetY = dy; 1097 m_scrollSetTime = currentTimeMS(); 1098 // The visible rect is located within our coordinate space so it 1099 // contains the actual scroll position. Setting the location makes hit 1100 // testing work correctly. 1101 m_mainFrame->view()->platformWidget()->setLocation(m_scrollOffsetX, 1102 m_scrollOffsetY); 1103 if (sendScrollEvent) { 1104 m_mainFrame->eventHandler()->sendScrollEvent(); 1105 1106 // Only update history position if it's user scrolled. 1107 // Update history item to reflect the new scroll position. 1108 // This also helps save the history information when the browser goes to 1109 // background, so scroll position will be restored if browser gets 1110 // killed while in background. 1111 WebCore::HistoryController* history = m_mainFrame->loader()->history(); 1112 // Because the history item saving could be heavy for large sites and 1113 // scrolling can generate lots of small scroll offset, the following code 1114 // reduces the saving frequency. 1115 static const int MIN_SCROLL_DIFF = 32; 1116 if (history->currentItem()) { 1117 WebCore::IntPoint currentPoint = history->currentItem()->scrollPoint(); 1118 if (std::abs(currentPoint.x() - dx) >= MIN_SCROLL_DIFF || 1119 std::abs(currentPoint.y() - dy) >= MIN_SCROLL_DIFF) { 1120 history->saveScrollPositionAndViewStateToItem(history->currentItem()); 1121 } 1122 } 1123 } 1124 1125 // update the currently visible screen 1126 sendPluginVisibleScreen(); 1127 } 1128 } 1129 1130 void WebViewCore::setGlobalBounds(int x, int y, int h, int v) 1131 { 1132 m_mainFrame->view()->platformWidget()->setWindowBounds(x, y, h, v); 1133 } 1134 1135 void WebViewCore::setSizeScreenWidthAndScale(int width, int height, 1136 int textWrapWidth, float scale, int screenWidth, int screenHeight, 1137 int anchorX, int anchorY, bool ignoreHeight) 1138 { 1139 // Ignore the initial empty document. 1140 const WebCore::KURL& url = m_mainFrame->document()->url(); 1141 if (url.isEmpty()) 1142 return; 1143 1144 WebCoreViewBridge* window = m_mainFrame->view()->platformWidget(); 1145 int ow = window->width(); 1146 int oh = window->height(); 1147 int osw = m_screenWidth; 1148 int osh = m_screenHeight; 1149 int otw = m_textWrapWidth; 1150 m_screenWidth = screenWidth; 1151 m_screenHeight = screenHeight; 1152 m_textWrapWidth = textWrapWidth; 1153 if (scale >= 0) // negative means keep the current scale 1154 m_scale = scale; 1155 m_maxXScroll = screenWidth >> 2; 1156 m_maxYScroll = m_maxXScroll * height / width; 1157 // Don't reflow if the diff is small. 1158 const bool reflow = otw && textWrapWidth && 1159 ((float) abs(otw - textWrapWidth) / textWrapWidth) >= 0.01f; 1160 1161 // When the screen size change, fixed positioned element should be updated. 1162 // This is supposed to be light weighted operation without a full layout. 1163 if (osh != screenHeight || osw != screenWidth) 1164 m_mainFrame->view()->updatePositionedObjects(); 1165 1166 if (ow != width || (!ignoreHeight && oh != height) || reflow) { 1167 WebCore::RenderObject *r = m_mainFrame->contentRenderer(); 1168 if (r) { 1169 WebCore::IntPoint anchorPoint = WebCore::IntPoint(anchorX, anchorY); 1170 RefPtr<WebCore::Node> node; 1171 WebCore::IntRect bounds; 1172 WebCore::IntPoint offset; 1173 // If the text wrap changed, it is probably zoom change or 1174 // orientation change. Try to keep the anchor at the same place. 1175 if (otw && textWrapWidth && otw != textWrapWidth && 1176 (anchorX != 0 || anchorY != 0)) { 1177 WebCore::HitTestResult hitTestResult = 1178 m_mainFrame->eventHandler()->hitTestResultAtPoint( 1179 anchorPoint, false); 1180 node = hitTestResult.innerNode(); 1181 if (node && !node->isTextNode()) { 1182 // If the hitTestResultAtPoint didn't find a suitable node 1183 // for anchoring, try again with some slop. 1184 static const int HIT_SLOP = 30; 1185 anchorPoint.move(HIT_SLOP, HIT_SLOP); 1186 hitTestResult = 1187 m_mainFrame->eventHandler()->hitTestResultAtPoint( 1188 anchorPoint, false); 1189 node = hitTestResult.innerNode(); 1190 } 1191 } 1192 if (node) { 1193 bounds = node->getRect(); 1194 // sites like nytimes.com insert a non-standard tag <nyt_text> 1195 // in the html. If it is the HitTestResult, it may have zero 1196 // width and height. In this case, use its parent node. 1197 if (bounds.width() == 0) { 1198 node = node->parentOrHostNode(); 1199 if (node) { 1200 bounds = node->getRect(); 1201 } 1202 } 1203 } 1204 1205 // Set the size after finding the old anchor point as 1206 // hitTestResultAtPoint causes a layout. 1207 window->setSize(width, height); 1208 window->setVisibleSize(screenWidth, screenHeight); 1209 if (width != screenWidth) { 1210 m_mainFrame->view()->setUseFixedLayout(true); 1211 m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height)); 1212 } else 1213 m_mainFrame->view()->setUseFixedLayout(false); 1214 r->setNeedsLayoutAndPrefWidthsRecalc(); 1215 if (m_mainFrame->view()->didFirstLayout()) 1216 m_mainFrame->view()->forceLayout(); 1217 1218 // scroll to restore current screen center 1219 if (node) { 1220 const WebCore::IntRect& newBounds = node->getRect(); 1221 if ((osw && osh && bounds.width() && bounds.height()) 1222 && (bounds != newBounds)) { 1223 WebCore::FrameView* view = m_mainFrame->view(); 1224 // force left align if width is not changed while height changed. 1225 // the anchorPoint is probably at some white space in the node 1226 // which is affected by text wrap around the screen width. 1227 const bool leftAlign = (otw != textWrapWidth) 1228 && (bounds.width() == newBounds.width()) 1229 && (bounds.height() != newBounds.height()); 1230 const float xPercentInDoc = 1231 leftAlign ? 0.0 : (float) (anchorX - bounds.x()) / bounds.width(); 1232 const float xPercentInView = 1233 leftAlign ? 0.0 : (float) (anchorX - m_scrollOffsetX) / osw; 1234 const float yPercentInDoc = (float) (anchorY - bounds.y()) / bounds.height(); 1235 const float yPercentInView = (float) (anchorY - m_scrollOffsetY) / osh; 1236 showRect(newBounds.x(), newBounds.y(), newBounds.width(), 1237 newBounds.height(), view->contentsWidth(), 1238 view->contentsHeight(), 1239 xPercentInDoc, xPercentInView, 1240 yPercentInDoc, yPercentInView); 1241 } 1242 } 1243 } 1244 } else { 1245 window->setSize(width, height); 1246 window->setVisibleSize(screenWidth, screenHeight); 1247 m_mainFrame->view()->resize(width, height); 1248 if (width != screenWidth) { 1249 m_mainFrame->view()->setUseFixedLayout(true); 1250 m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height)); 1251 } else 1252 m_mainFrame->view()->setUseFixedLayout(false); 1253 } 1254 1255 // update the currently visible screen as perceived by the plugin 1256 sendPluginVisibleScreen(); 1257 } 1258 1259 void WebViewCore::dumpDomTree(bool useFile) 1260 { 1261 #ifdef ANDROID_DOM_LOGGING 1262 if (useFile) 1263 gDomTreeFile = fopen(DOM_TREE_LOG_FILE, "w"); 1264 m_mainFrame->document()->showTreeForThis(); 1265 if (gDomTreeFile) { 1266 fclose(gDomTreeFile); 1267 gDomTreeFile = 0; 1268 } 1269 #endif 1270 } 1271 1272 void WebViewCore::dumpRenderTree(bool useFile) 1273 { 1274 #ifdef ANDROID_DOM_LOGGING 1275 WTF::CString renderDump = WebCore::externalRepresentation(m_mainFrame).utf8(); 1276 const char* data = renderDump.data(); 1277 if (useFile) { 1278 gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w"); 1279 DUMP_RENDER_LOGD("%s", data); 1280 fclose(gRenderTreeFile); 1281 gRenderTreeFile = 0; 1282 } else { 1283 // adb log can only output 1024 characters, so write out line by line. 1284 // exclude '\n' as adb log adds it for each output. 1285 int length = renderDump.length(); 1286 for (int i = 0, last = 0; i < length; i++) { 1287 if (data[i] == '\n') { 1288 if (i != last) 1289 DUMP_RENDER_LOGD("%.*s", (i - last), &(data[last])); 1290 last = i + 1; 1291 } 1292 } 1293 } 1294 #endif 1295 } 1296 1297 HTMLElement* WebViewCore::retrieveElement(int x, int y, 1298 const QualifiedName& tagName) 1299 { 1300 HitTestResult hitTestResult = m_mainFrame->eventHandler() 1301 ->hitTestResultAtPoint(IntPoint(x, y), false, false, 1302 DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, 1303 IntSize(1, 1)); 1304 if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) { 1305 ALOGE("Should not happen: no in document Node found"); 1306 return 0; 1307 } 1308 const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult(); 1309 if (list.isEmpty()) { 1310 ALOGE("Should not happen: no rect-based-test nodes found"); 1311 return 0; 1312 } 1313 Node* node = hitTestResult.innerNode(); 1314 Node* element = node; 1315 while (element && (!element->isElementNode() 1316 || !element->hasTagName(tagName))) { 1317 element = element->parentNode(); 1318 } 1319 return static_cast<WebCore::HTMLElement*>(element); 1320 } 1321 1322 HTMLAnchorElement* WebViewCore::retrieveAnchorElement(int x, int y) 1323 { 1324 return static_cast<HTMLAnchorElement*> 1325 (retrieveElement(x, y, HTMLNames::aTag)); 1326 } 1327 1328 HTMLImageElement* WebViewCore::retrieveImageElement(int x, int y) 1329 { 1330 return static_cast<HTMLImageElement*> 1331 (retrieveElement(x, y, HTMLNames::imgTag)); 1332 } 1333 1334 WTF::String WebViewCore::retrieveHref(int x, int y) 1335 { 1336 // TODO: This is expensive, cache 1337 HitTestResult result = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y), 1338 false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(1, 1)); 1339 return result.absoluteLinkURL(); 1340 } 1341 1342 WTF::String WebViewCore::retrieveAnchorText(int x, int y) 1343 { 1344 WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(x, y); 1345 return anchor ? anchor->text() : WTF::String(); 1346 } 1347 1348 WTF::String WebViewCore::retrieveImageSource(int x, int y) 1349 { 1350 // TODO: This is expensive, cache 1351 HitTestResult result = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y), 1352 false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(1, 1)); 1353 return result.absoluteImageURL(); 1354 } 1355 1356 WTF::String WebViewCore::requestLabel(WebCore::Frame* frame, 1357 WebCore::Node* node) 1358 { 1359 if (node && validNode(m_mainFrame, frame, node)) { 1360 RefPtr<WebCore::NodeList> list = node->document()->getElementsByTagName("label"); 1361 unsigned length = list->length(); 1362 for (unsigned i = 0; i < length; i++) { 1363 WebCore::HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>( 1364 list->item(i)); 1365 if (label->control() == node) { 1366 Node* node = label; 1367 String result; 1368 while ((node = node->traverseNextNode(label))) { 1369 if (node->isTextNode()) { 1370 Text* textNode = static_cast<Text*>(node); 1371 result += textNode->dataImpl(); 1372 } 1373 } 1374 return result; 1375 } 1376 } 1377 } 1378 return WTF::String(); 1379 } 1380 1381 static bool isContentEditable(const WebCore::Node* node) 1382 { 1383 if (!node) 1384 return false; 1385 return node->isContentEditable(); 1386 } 1387 1388 // Returns true if the node is a textfield, textarea, or contentEditable 1389 static bool isTextInput(const WebCore::Node* node) 1390 { 1391 if (!node) 1392 return false; 1393 if (isContentEditable(node)) 1394 return true; 1395 WebCore::RenderObject* renderer = node->renderer(); 1396 return renderer && (renderer->isTextField() || renderer->isTextArea()); 1397 } 1398 1399 void WebViewCore::revealSelection() 1400 { 1401 WebCore::Node* focus = currentFocus(); 1402 if (!focus) 1403 return; 1404 if (!isTextInput(focus)) 1405 return; 1406 WebCore::Frame* focusedFrame = focus->document()->frame(); 1407 if (!focusedFrame->page()->focusController()->isActive()) 1408 return; 1409 focusedFrame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded); 1410 } 1411 1412 struct TouchNodeData { 1413 Node* mUrlNode; 1414 Node* mInnerNode; 1415 IntRect mBounds; 1416 }; 1417 1418 // get the bounding box of the Node 1419 static IntRect getAbsoluteBoundingBox(Node* node) { 1420 IntRect rect; 1421 RenderObject* render = node->renderer(); 1422 if (!render) 1423 return rect; 1424 if (render->isRenderInline()) 1425 rect = toRenderInline(render)->linesVisualOverflowBoundingBox(); 1426 else if (render->isBox()) 1427 rect = toRenderBox(render)->visualOverflowRect(); 1428 else if (render->isText()) 1429 rect = toRenderText(render)->linesBoundingBox(); 1430 else 1431 ALOGE("getAbsoluteBoundingBox failed for node %p, name %s", node, render->renderName()); 1432 FloatPoint absPos = render->localToAbsolute(FloatPoint(), false, true); 1433 rect.move(absPos.x(), absPos.y()); 1434 return rect; 1435 } 1436 1437 WebCore::Frame* WebViewCore::focusedFrame() const 1438 { 1439 return m_mainFrame->page()->focusController()->focusedOrMainFrame(); 1440 } 1441 1442 VisiblePosition WebViewCore::visiblePositionForContentPoint(int x, int y) 1443 { 1444 return visiblePositionForContentPoint(IntPoint(x, y)); 1445 } 1446 1447 VisiblePosition WebViewCore::visiblePositionForContentPoint(const IntPoint& point) 1448 { 1449 // Hit test of this kind required for this to work inside input fields 1450 HitTestRequest request(HitTestRequest::Active 1451 | HitTestRequest::MouseMove 1452 | HitTestRequest::ReadOnly 1453 | HitTestRequest::IgnoreClipping); 1454 HitTestResult result(point); 1455 focusedFrame()->document()->renderView()->layer()->hitTest(request, result); 1456 1457 // Matching the logic in MouseEventWithHitTestResults::targetNode() 1458 Node* node = result.innerNode(); 1459 if (!node) 1460 return VisiblePosition(); 1461 Element* element = node->parentElement(); 1462 if (!node->inDocument() && element && element->inDocument()) 1463 node = element; 1464 1465 return node->renderer()->positionForPoint(result.localPoint()); 1466 } 1467 1468 bool WebViewCore::selectWordAt(int x, int y) 1469 { 1470 HitTestResult hoverResult; 1471 moveMouse(x, y, &hoverResult); 1472 if (hoverResult.innerNode()) { 1473 Node* node = hoverResult.innerNode(); 1474 Frame* frame = node->document()->frame(); 1475 Page* page = m_mainFrame->document()->page(); 1476 page->focusController()->setFocusedFrame(frame); 1477 } 1478 1479 IntPoint point = convertGlobalContentToFrameContent(IntPoint(x, y)); 1480 1481 // Hit test of this kind required for this to work inside input fields 1482 HitTestRequest request(HitTestRequest::Active); 1483 HitTestResult result(point); 1484 1485 focusedFrame()->document()->renderView()->layer()->hitTest(request, result); 1486 1487 // Matching the logic in MouseEventWithHitTestResults::targetNode() 1488 Node* node = result.innerNode(); 1489 if (!node) 1490 return false; 1491 Element* element = node->parentElement(); 1492 if (!node->inDocument() && element && element->inDocument()) 1493 node = element; 1494 1495 SelectionController* sc = focusedFrame()->selection(); 1496 bool wordSelected = false; 1497 if (!sc->contains(point) && (node->isContentEditable() || node->isTextNode()) && !result.isLiveLink() 1498 && node->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true))) { 1499 VisiblePosition pos(node->renderer()->positionForPoint(result.localPoint())); 1500 wordSelected = selectWordAroundPosition(node->document()->frame(), pos); 1501 } 1502 return wordSelected; 1503 } 1504 1505 bool WebViewCore::selectWordAroundPosition(Frame* frame, VisiblePosition pos) 1506 { 1507 VisibleSelection selection(pos); 1508 selection.expandUsingGranularity(WordGranularity); 1509 SelectionController* selectionController = frame->selection(); 1510 1511 bool wordSelected = false; 1512 if (selectionController->shouldChangeSelection(selection)) { 1513 bool allWhitespaces = true; 1514 RefPtr<Range> firstRange = selection.firstRange(); 1515 String text = firstRange.get() ? firstRange->text() : ""; 1516 for (size_t i = 0; i < text.length(); ++i) { 1517 if (!isSpaceOrNewline(text[i])) { 1518 allWhitespaces = false; 1519 break; 1520 } 1521 } 1522 if (allWhitespaces) { 1523 VisibleSelection emptySelection(pos); 1524 selectionController->setSelection(emptySelection); 1525 } else { 1526 selectionController->setSelection(selection); 1527 wordSelected = true; 1528 } 1529 } 1530 return wordSelected; 1531 } 1532 1533 int WebViewCore::platformLayerIdFromNode(Node* node, LayerAndroid** outLayer) 1534 { 1535 if (!node || !node->renderer()) 1536 return -1; 1537 RenderLayer* renderLayer = node->renderer()->enclosingLayer(); 1538 while (renderLayer && !renderLayer->isComposited()) 1539 renderLayer = renderLayer->parent(); 1540 if (!renderLayer || !renderLayer->isComposited()) 1541 return -1; 1542 GraphicsLayer* graphicsLayer = renderLayer->backing()->graphicsLayer(); 1543 if (!graphicsLayer) 1544 return -1; 1545 GraphicsLayerAndroid* agl = static_cast<GraphicsLayerAndroid*>(graphicsLayer); 1546 LayerAndroid* layer = agl->foregroundLayer(); 1547 if (!layer) 1548 layer = agl->contentLayer(); 1549 if (!layer) 1550 return -1; 1551 if (outLayer) 1552 *outLayer = layer; 1553 return layer->uniqueId(); 1554 } 1555 1556 void WebViewCore::layerToAbsoluteOffset(const LayerAndroid* layer, IntPoint& offset) 1557 { 1558 while (layer) { 1559 const SkPoint& pos = layer->getPosition(); 1560 offset.move(pos.fX, pos.fY); 1561 const IntPoint& scroll = layer->getScrollOffset(); 1562 offset.move(-scroll.x(), -scroll.y()); 1563 layer = static_cast<LayerAndroid*>(layer->getParent()); 1564 } 1565 } 1566 1567 void WebViewCore::setSelectionCaretInfo(SelectText* selectTextContainer, 1568 const WebCore::Position& pos, const IntPoint& frameOffset, 1569 SelectText::HandleId handleId, int caretRectOffset, EAffinity affinity) 1570 { 1571 Node* node = pos.anchorNode(); 1572 LayerAndroid* layer = 0; 1573 int layerId = platformLayerIdFromNode(node, &layer); 1574 selectTextContainer->setCaretLayerId(handleId, layerId); 1575 IntPoint offset = frameOffset; 1576 layerToAbsoluteOffset(layer, offset); 1577 RenderObject* r = node->renderer(); 1578 RenderText* renderText = toRenderText(r); 1579 int caretOffset; 1580 InlineBox* inlineBox; 1581 pos.getInlineBoxAndOffset(affinity, inlineBox, caretOffset); 1582 IntRect caretRect = renderText->localCaretRect(inlineBox, caretOffset); 1583 FloatPoint absoluteOffset = renderText->localToAbsolute(caretRect.location()); 1584 caretRect.setX(absoluteOffset.x() - offset.x() + caretRectOffset); 1585 caretRect.setY(absoluteOffset.y() - offset.y()); 1586 selectTextContainer->setCaretRect(handleId, caretRect); 1587 selectTextContainer->setTextRect(handleId, 1588 positionToTextRect(pos, affinity, offset)); 1589 } 1590 1591 bool WebViewCore::isLtr(const Position& position) 1592 { 1593 InlineBox* inlineBox = 0; 1594 int caretOffset = 0; 1595 position.getInlineBoxAndOffset(DOWNSTREAM, inlineBox, caretOffset); 1596 bool isLtr; 1597 if (inlineBox) 1598 isLtr = inlineBox->isLeftToRightDirection(); 1599 else 1600 isLtr = position.primaryDirection() == LTR; 1601 return isLtr; 1602 } 1603 1604 SelectText* WebViewCore::createSelectText(const VisibleSelection& selection) 1605 { 1606 bool isCaret = selection.isCaret(); 1607 if (selection.isNone() || (!selection.isContentEditable() && isCaret) 1608 || !selection.start().anchorNode() 1609 || !selection.start().anchorNode()->renderer() 1610 || !selection.end().anchorNode() 1611 || !selection.end().anchorNode()->renderer()) 1612 return 0; 1613 1614 RefPtr<Range> range = selection.firstRange(); 1615 Node* startContainer = range->startContainer(); 1616 Node* endContainer = range->endContainer(); 1617 1618 if (!startContainer || !endContainer) 1619 return 0; 1620 if (!isCaret && startContainer == endContainer 1621 && range->startOffset() == range->endOffset()) 1622 return 0; 1623 1624 IntPoint frameOffset = convertGlobalContentToFrameContent(IntPoint()); 1625 SelectText* selectTextContainer = new SelectText(); 1626 if (isCaret) { 1627 setSelectionCaretInfo(selectTextContainer, selection.start(), frameOffset, 1628 SelectText::LeftHandle, 0, selection.affinity()); 1629 setSelectionCaretInfo(selectTextContainer, selection.start(), frameOffset, 1630 SelectText::RightHandle, 0, selection.affinity()); 1631 } else { 1632 bool ltr = isLtr(selection.start()); 1633 Position left = ltr ? selection.start() : selection.end(); 1634 Position right = ltr ? selection.end() : selection.start(); 1635 int leftOffset = isLtr(left) ? 0 : -1; 1636 int rightOffset = isLtr(right) ? 0 : -1; 1637 setSelectionCaretInfo(selectTextContainer, left, frameOffset, 1638 SelectText::LeftHandle, leftOffset, selection.affinity()); 1639 setSelectionCaretInfo(selectTextContainer, right, frameOffset, 1640 SelectText::RightHandle, rightOffset, selection.affinity()); 1641 1642 Node* stopNode = range->pastLastNode(); 1643 for (Node* node = range->firstNode(); node != stopNode; node = node->traverseNextNode()) { 1644 RenderObject* r = node->renderer(); 1645 if (!r || !r->isText() || r->style()->visibility() != VISIBLE) 1646 continue; 1647 RenderText* renderText = toRenderText(r); 1648 int startOffset = node == startContainer ? range->startOffset() : 0; 1649 int endOffset = node == endContainer ? range->endOffset() : numeric_limits<int>::max(); 1650 LayerAndroid* layer = 0; 1651 int layerId = platformLayerIdFromNode(node, &layer); 1652 Vector<IntRect> rects; 1653 renderText->absoluteRectsForRange(rects, startOffset, endOffset, true); 1654 selectTextContainer->addHighlightRegion(layer, rects, frameOffset); 1655 } 1656 } 1657 selectTextContainer->setText(range->text()); 1658 return selectTextContainer; 1659 } 1660 1661 IntRect WebViewCore::positionToTextRect(const Position& position, 1662 EAffinity affinity, const WebCore::IntPoint& offset) 1663 { 1664 IntRect textRect; 1665 InlineBox* inlineBox; 1666 int offsetIndex; 1667 position.getInlineBoxAndOffset(affinity, inlineBox, offsetIndex); 1668 if (inlineBox && inlineBox->isInlineTextBox()) { 1669 InlineTextBox* box = static_cast<InlineTextBox*>(inlineBox); 1670 RootInlineBox* root = box->root(); 1671 RenderText* renderText = box->textRenderer(); 1672 int left = root->logicalLeft(); 1673 int width = root->logicalWidth(); 1674 int top = root->selectionTop(); 1675 int height = root->selectionHeight(); 1676 1677 if (!renderText->style()->isHorizontalWritingMode()) { 1678 swap(left, top); 1679 swap(width, height); 1680 } 1681 FloatPoint origin(left, top); 1682 FloatPoint absoluteOrigin = renderText->localToAbsolute(origin); 1683 1684 textRect.setX(absoluteOrigin.x() - offset.x()); 1685 textRect.setWidth(width); 1686 textRect.setY(absoluteOrigin.y() - offset.y()); 1687 textRect.setHeight(height); 1688 } 1689 return textRect; 1690 } 1691 1692 IntPoint WebViewCore::convertGlobalContentToFrameContent(const IntPoint& point, WebCore::Frame* frame) 1693 { 1694 if (!frame) frame = focusedFrame(); 1695 IntPoint frameOffset(-m_scrollOffsetX, -m_scrollOffsetY); 1696 frameOffset = frame->view()->windowToContents(frameOffset); 1697 return IntPoint(point.x() + frameOffset.x(), point.y() + frameOffset.y()); 1698 } 1699 1700 Position WebViewCore::trimSelectionPosition(const Position &start, const Position& stop) 1701 { 1702 int direction = comparePositions(start, stop); 1703 if (direction == 0) 1704 return start; 1705 bool forward = direction < 0; 1706 EAffinity affinity = forward ? DOWNSTREAM : UPSTREAM; 1707 bool move; 1708 Position pos = start; 1709 bool movedTooFar = false; 1710 do { 1711 move = true; 1712 Node* node = pos.anchorNode(); 1713 if (node && node->isTextNode() && node->renderer()) { 1714 RenderText *textRenderer = toRenderText(node->renderer()); 1715 move = !textRenderer->textLength(); 1716 } 1717 if (move) { 1718 Position nextPos = forward ? pos.next() : pos.previous(); 1719 movedTooFar = nextPos.isNull() || pos == nextPos 1720 || ((comparePositions(nextPos, stop) < 0) != forward); 1721 pos = nextPos; 1722 } 1723 } while (move && !movedTooFar); 1724 if (movedTooFar) 1725 pos = stop; 1726 return pos; 1727 } 1728 1729 void WebViewCore::selectText(int startX, int startY, int endX, int endY) 1730 { 1731 SelectionController* sc = focusedFrame()->selection(); 1732 IntPoint startPoint = convertGlobalContentToFrameContent(IntPoint(startX, startY)); 1733 VisiblePosition startPosition(visiblePositionForContentPoint(startPoint)); 1734 IntPoint endPoint = convertGlobalContentToFrameContent(IntPoint(endX, endY)); 1735 VisiblePosition endPosition(visiblePositionForContentPoint(endPoint)); 1736 1737 if (startPosition.isNull() || endPosition.isNull()) 1738 return; 1739 1740 // Ensure startPosition is before endPosition 1741 if (comparePositions(startPosition, endPosition) > 0) 1742 swap(startPosition, endPosition); 1743 1744 if (sc->isContentEditable()) { 1745 startPosition = sc->selection().visibleStart().honorEditableBoundaryAtOrAfter(startPosition); 1746 endPosition = sc->selection().visibleEnd().honorEditableBoundaryAtOrBefore(endPosition); 1747 if (startPosition.isNull() || endPosition.isNull()) { 1748 return; 1749 } 1750 } 1751 1752 // Ensure startPosition is not at end of block 1753 if (startPosition != endPosition && isEndOfBlock(startPosition)) { 1754 VisiblePosition nextStartPosition(startPosition.next()); 1755 if (!nextStartPosition.isNull()) 1756 startPosition = nextStartPosition; 1757 } 1758 // Ensure endPosition is not at start of block 1759 if (startPosition != endPosition && isStartOfBlock(endPosition)) { 1760 VisiblePosition prevEndPosition(endPosition.previous()); 1761 if (!prevEndPosition.isNull()) 1762 endPosition = prevEndPosition; 1763 } 1764 1765 Position start = startPosition.deepEquivalent(); 1766 Position end = endPosition.deepEquivalent(); 1767 start = trimSelectionPosition(start, end); 1768 end = trimSelectionPosition(end, start); 1769 VisibleSelection selection(start, end); 1770 // Only allow changes between caret positions or to text selection. 1771 bool selectChangeAllowed = (!selection.isCaret() || sc->isCaret()); 1772 if (selectChangeAllowed && sc->shouldChangeSelection(selection)) 1773 sc->setSelection(selection); 1774 } 1775 1776 bool WebViewCore::nodeIsClickableOrFocusable(Node* node) 1777 { 1778 if (!node) 1779 return false; 1780 if (node->disabled()) 1781 return false; 1782 if (!node->inDocument()) 1783 return false; 1784 if (!node->renderer() || node->renderer()->style()->visibility() != VISIBLE) 1785 return false; 1786 return node->supportsFocus() 1787 || node->hasEventListeners(eventNames().clickEvent) 1788 || node->hasEventListeners(eventNames().mousedownEvent) 1789 || node->hasEventListeners(eventNames().mouseupEvent) 1790 || node->hasEventListeners(eventNames().mouseoverEvent); 1791 } 1792 1793 // get the highlight rectangles for the touch point (x, y) with the slop 1794 AndroidHitTestResult WebViewCore::hitTestAtPoint(int x, int y, int slop, bool doMoveMouse) 1795 { 1796 if (doMoveMouse) 1797 moveMouse(x, y, 0, true); 1798 HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y), 1799 false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(slop, slop)); 1800 AndroidHitTestResult androidHitResult(this, hitTestResult); 1801 if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) { 1802 ALOGE("Should not happen: no in document Node found"); 1803 return androidHitResult; 1804 } 1805 const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult(); 1806 if (list.isEmpty()) { 1807 ALOGE("Should not happen: no rect-based-test nodes found"); 1808 return androidHitResult; 1809 } 1810 Frame* frame = hitTestResult.innerNode()->document()->frame(); 1811 Vector<TouchNodeData> nodeDataList; 1812 if (hitTestResult.innerNode() != hitTestResult.innerNonSharedNode() 1813 && hitTestResult.innerNode()->hasTagName(WebCore::HTMLNames::areaTag)) { 1814 HTMLAreaElement* area = static_cast<HTMLAreaElement*>(hitTestResult.innerNode()); 1815 androidHitResult.hitTestResult().setURLElement(area); 1816 androidHitResult.highlightRects().append(area->computeRect( 1817 hitTestResult.innerNonSharedNode()->renderer())); 1818 return androidHitResult; 1819 } 1820 ListHashSet<RefPtr<Node> >::const_iterator last = list.end(); 1821 for (ListHashSet<RefPtr<Node> >::const_iterator it = list.begin(); it != last; ++it) { 1822 // TODO: it seems reasonable to not search across the frame. Isn't it? 1823 // if the node is not in the same frame as the innerNode, skip it 1824 if (it->get()->document()->frame() != frame) 1825 continue; 1826 // traverse up the tree to find the first node that needs highlight 1827 bool found = false; 1828 Node* eventNode = it->get(); 1829 Node* innerNode = eventNode; 1830 while (eventNode) { 1831 RenderObject* render = eventNode->renderer(); 1832 if (render && (render->isBody() || render->isRenderView())) 1833 break; 1834 if (nodeIsClickableOrFocusable(eventNode)) { 1835 found = true; 1836 break; 1837 } 1838 // the nodes in the rectBasedTestResult() are ordered based on z-index during hit testing. 1839 // so do not search for the eventNode across explicit z-index border. 1840 // TODO: this is a hard one to call. z-index is quite complicated as its value only 1841 // matters when you compare two RenderLayer in the same hierarchy level. e.g. in 1842 // the following example, "b" is on the top as its z level is the highest. even "c" 1843 // has 100 as z-index, it is still below "d" as its parent has the same z-index as 1844 // "d" and logically before "d". Of course "a" is the lowest in the z level. 1845 // 1846 // z-index:auto "a" 1847 // z-index:2 "b" 1848 // z-index:1 1849 // z-index:100 "c" 1850 // z-index:1 "d" 1851 // 1852 // If the fat point touches everyone, the order in the list should be "b", "d", "c" 1853 // and "a". When we search for the event node for "b", we really don't want "a" as 1854 // in the z-order it is behind everything else. 1855 if (render && !render->style()->hasAutoZIndex()) 1856 break; 1857 eventNode = eventNode->parentNode(); 1858 } 1859 // didn't find any eventNode, skip it 1860 if (!found) 1861 continue; 1862 // first quick check whether it is a duplicated node before computing bounding box 1863 Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end(); 1864 for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) { 1865 // found the same node, skip it 1866 if (eventNode == n->mUrlNode) { 1867 found = false; 1868 break; 1869 } 1870 } 1871 if (!found) 1872 continue; 1873 // next check whether the node is fully covered by or fully covering another node. 1874 found = false; 1875 IntRect rect = getAbsoluteBoundingBox(eventNode); 1876 if (rect.isEmpty()) { 1877 // if the node's bounds is empty and it is not a ContainerNode, skip it. 1878 if (!eventNode->isContainerNode()) 1879 continue; 1880 // if the node's children are all positioned objects, its bounds can be empty. 1881 // Walk through the children to find the bounding box. 1882 Node* child = static_cast<const ContainerNode*>(eventNode)->firstChild(); 1883 while (child) { 1884 IntRect childrect; 1885 if (child->renderer()) 1886 childrect = getAbsoluteBoundingBox(child); 1887 if (!childrect.isEmpty()) { 1888 rect.unite(childrect); 1889 child = child->traverseNextSibling(eventNode); 1890 } else 1891 child = child->traverseNextNode(eventNode); 1892 } 1893 } 1894 for (int i = nodeDataList.size() - 1; i >= 0; i--) { 1895 TouchNodeData n = nodeDataList.at(i); 1896 // the new node is enclosing an existing node, skip it 1897 if (rect.contains(n.mBounds)) { 1898 found = true; 1899 break; 1900 } 1901 // the new node is fully inside an existing node, remove the existing node 1902 if (n.mBounds.contains(rect)) 1903 nodeDataList.remove(i); 1904 } 1905 if (!found) { 1906 TouchNodeData newNode; 1907 newNode.mUrlNode = eventNode; 1908 newNode.mBounds = rect; 1909 newNode.mInnerNode = innerNode; 1910 nodeDataList.append(newNode); 1911 } 1912 } 1913 if (!nodeDataList.size()) { 1914 androidHitResult.searchContentDetectors(); 1915 return androidHitResult; 1916 } 1917 // finally select the node with the largest overlap with the fat point 1918 TouchNodeData final; 1919 final.mUrlNode = 0; 1920 IntPoint docPos = frame->view()->windowToContents(m_mousePos); 1921 IntRect testRect(docPos.x() - slop, docPos.y() - slop, 2 * slop + 1, 2 * slop + 1); 1922 int area = 0; 1923 Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end(); 1924 for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) { 1925 IntRect rect = n->mBounds; 1926 rect.intersect(testRect); 1927 int a = rect.width() * rect.height(); 1928 if (a > area || !final.mUrlNode) { 1929 final = *n; 1930 area = a; 1931 } 1932 } 1933 // now get the node's highlight rectangles in the page coordinate system 1934 if (final.mUrlNode) { 1935 // Update innerNode and innerNonSharedNode 1936 androidHitResult.hitTestResult().setInnerNode(final.mInnerNode); 1937 androidHitResult.hitTestResult().setInnerNonSharedNode(final.mInnerNode); 1938 if (final.mUrlNode->isElementNode()) { 1939 // We found a URL element. Update the hitTestResult 1940 androidHitResult.setURLElement(static_cast<Element*>(final.mUrlNode)); 1941 } else { 1942 androidHitResult.setURLElement(0); 1943 } 1944 Vector<IntRect>& highlightRects = androidHitResult.highlightRects(); 1945 if (doMoveMouse && highlightRects.size() > 0) { 1946 // adjust m_mousePos if it is not inside the returned highlight 1947 // rectangles 1948 IntRect foundIntersection; 1949 IntRect inputRect = IntRect(x - slop, y - slop, 1950 slop * 2 + 1, slop * 2 + 1); 1951 for (size_t i = 0; i < highlightRects.size(); i++) { 1952 IntRect& hr = highlightRects[i]; 1953 IntRect test = inputRect; 1954 test.intersect(hr); 1955 if (!test.isEmpty()) { 1956 foundIntersection = test; 1957 break; 1958 } 1959 } 1960 if (!foundIntersection.isEmpty() && !foundIntersection.contains(x, y)) { 1961 IntPoint pt = foundIntersection.center(); 1962 moveMouse(pt.x(), pt.y(), 0, true); 1963 } 1964 } 1965 } else { 1966 androidHitResult.searchContentDetectors(); 1967 } 1968 return androidHitResult; 1969 } 1970 1971 /////////////////////////////////////////////////////////////////////////////// 1972 1973 void WebViewCore::addPlugin(PluginWidgetAndroid* w) 1974 { 1975 // SkDebugf("----------- addPlugin %p", w); 1976 /* The plugin must be appended to the end of the array. This ensures that if 1977 the plugin is added while iterating through the array (e.g. sendEvent(...)) 1978 that the iteration process is not corrupted. 1979 */ 1980 *m_plugins.append() = w; 1981 } 1982 1983 void WebViewCore::removePlugin(PluginWidgetAndroid* w) 1984 { 1985 // SkDebugf("----------- removePlugin %p", w); 1986 int index = m_plugins.find(w); 1987 if (index < 0) { 1988 SkDebugf("--------------- pluginwindow not found! %p\n", w); 1989 } else { 1990 m_plugins.removeShuffle(index); 1991 } 1992 } 1993 1994 bool WebViewCore::isPlugin(PluginWidgetAndroid* w) const 1995 { 1996 return m_plugins.find(w) >= 0; 1997 } 1998 1999 void WebViewCore::invalPlugin(PluginWidgetAndroid* w) 2000 { 2001 const double PLUGIN_INVAL_DELAY = 1.0 / 60; 2002 2003 if (!m_pluginInvalTimer.isActive()) { 2004 m_pluginInvalTimer.startOneShot(PLUGIN_INVAL_DELAY); 2005 } 2006 } 2007 2008 void WebViewCore::drawPlugins() 2009 { 2010 SkRegion inval; // accumualte what needs to be redrawn 2011 PluginWidgetAndroid** iter = m_plugins.begin(); 2012 PluginWidgetAndroid** stop = m_plugins.end(); 2013 2014 for (; iter < stop; ++iter) { 2015 PluginWidgetAndroid* w = *iter; 2016 SkIRect dirty; 2017 if (w->isDirty(&dirty)) { 2018 w->draw(); 2019 inval.op(dirty, SkRegion::kUnion_Op); 2020 } 2021 } 2022 2023 if (!inval.isEmpty()) { 2024 // inval.getBounds() is our rectangle 2025 const SkIRect& bounds = inval.getBounds(); 2026 WebCore::IntRect r(bounds.fLeft, bounds.fTop, 2027 bounds.width(), bounds.height()); 2028 this->viewInvalidate(r); 2029 } 2030 } 2031 2032 void WebViewCore::notifyPluginsOnFrameLoad(const Frame* frame) { 2033 // if frame is the parent then notify all plugins 2034 if (!frame->tree()->parent()) { 2035 // trigger an event notifying the plugins that the page has loaded 2036 ANPEvent event; 2037 SkANP::InitEvent(&event, kLifecycle_ANPEventType); 2038 event.data.lifecycle.action = kOnLoad_ANPLifecycleAction; 2039 sendPluginEvent(event); 2040 // trigger the on/off screen notification if the page was reloaded 2041 sendPluginVisibleScreen(); 2042 } 2043 // else if frame's parent has completed 2044 else if (!frame->tree()->parent()->loader()->isLoading()) { 2045 // send to all plugins who have this frame in their heirarchy 2046 PluginWidgetAndroid** iter = m_plugins.begin(); 2047 PluginWidgetAndroid** stop = m_plugins.end(); 2048 for (; iter < stop; ++iter) { 2049 Frame* currentFrame = (*iter)->pluginView()->parentFrame(); 2050 while (currentFrame) { 2051 if (frame == currentFrame) { 2052 ANPEvent event; 2053 SkANP::InitEvent(&event, kLifecycle_ANPEventType); 2054 event.data.lifecycle.action = kOnLoad_ANPLifecycleAction; 2055 (*iter)->sendEvent(event); 2056 2057 // trigger the on/off screen notification if the page was reloaded 2058 ANPRectI visibleRect; 2059 getVisibleScreen(visibleRect); 2060 (*iter)->setVisibleScreen(visibleRect, m_scale); 2061 2062 break; 2063 } 2064 currentFrame = currentFrame->tree()->parent(); 2065 } 2066 } 2067 } 2068 } 2069 2070 void WebViewCore::getVisibleScreen(ANPRectI& visibleRect) 2071 { 2072 visibleRect.left = m_scrollOffsetX; 2073 visibleRect.top = m_scrollOffsetY; 2074 visibleRect.right = m_scrollOffsetX + m_screenWidth; 2075 visibleRect.bottom = m_scrollOffsetY + m_screenHeight; 2076 } 2077 2078 void WebViewCore::sendPluginVisibleScreen() 2079 { 2080 /* We may want to cache the previous values and only send the notification 2081 to the plugin in the event that one of the values has changed. 2082 */ 2083 2084 ANPRectI visibleRect; 2085 getVisibleScreen(visibleRect); 2086 2087 PluginWidgetAndroid** iter = m_plugins.begin(); 2088 PluginWidgetAndroid** stop = m_plugins.end(); 2089 for (; iter < stop; ++iter) { 2090 (*iter)->setVisibleScreen(visibleRect, m_scale); 2091 } 2092 } 2093 2094 void WebViewCore::sendPluginSurfaceReady() 2095 { 2096 PluginWidgetAndroid** iter = m_plugins.begin(); 2097 PluginWidgetAndroid** stop = m_plugins.end(); 2098 for (; iter < stop; ++iter) { 2099 (*iter)->checkSurfaceReady(); 2100 } 2101 } 2102 2103 void WebViewCore::sendPluginEvent(const ANPEvent& evt) 2104 { 2105 /* The list of plugins may be manipulated as we iterate through the list. 2106 This implementation allows for the addition of new plugins during an 2107 iteration, but may fail if a plugin is removed. Currently, there are not 2108 any use cases where a plugin is deleted while processing this loop, but 2109 if it does occur we will have to use an alternate data structure and/or 2110 iteration mechanism. 2111 */ 2112 for (int x = 0; x < m_plugins.count(); x++) { 2113 m_plugins[x]->sendEvent(evt); 2114 } 2115 } 2116 2117 PluginWidgetAndroid* WebViewCore::getPluginWidget(NPP npp) 2118 { 2119 PluginWidgetAndroid** iter = m_plugins.begin(); 2120 PluginWidgetAndroid** stop = m_plugins.end(); 2121 for (; iter < stop; ++iter) { 2122 if ((*iter)->pluginView()->instance() == npp) { 2123 return (*iter); 2124 } 2125 } 2126 return 0; 2127 } 2128 2129 static PluginView* nodeIsPlugin(Node* node) { 2130 RenderObject* renderer = node->renderer(); 2131 if (renderer && renderer->isWidget()) { 2132 Widget* widget = static_cast<RenderWidget*>(renderer)->widget(); 2133 if (widget && widget->isPluginView()) 2134 return static_cast<PluginView*>(widget); 2135 } 2136 return 0; 2137 } 2138 2139 /////////////////////////////////////////////////////////////////////////////// 2140 2141 // Update mouse position 2142 void WebViewCore::moveMouse(int x, int y, HitTestResult* hoveredNode, bool isClickCandidate) 2143 { 2144 // mouse event expects the position in the window coordinate 2145 m_mousePos = WebCore::IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY); 2146 if (isClickCandidate) 2147 m_mouseClickPos = m_mousePos; 2148 // validNode will still return true if the node is null, as long as we have 2149 // a valid frame. Do not want to make a call on frame unless it is valid. 2150 WebCore::PlatformMouseEvent mouseEvent(m_mousePos, m_mousePos, 2151 WebCore::NoButton, WebCore::MouseEventMoved, 1, false, false, false, 2152 false, WTF::currentTime()); 2153 m_mainFrame->eventHandler()->handleMouseMoveEvent(mouseEvent, hoveredNode); 2154 } 2155 2156 Position WebViewCore::getPositionForOffset(Node* node, int offset) 2157 { 2158 Position start = firstPositionInNode(node); 2159 Position end = lastPositionInNode(node); 2160 Document* document = node->document(); 2161 PassRefPtr<Range> range = Range::create(document, start, end); 2162 WebCore::CharacterIterator iterator(range.get()); 2163 iterator.advance(offset); 2164 return iterator.range()->startPosition(); 2165 } 2166 2167 void WebViewCore::setSelection(Node* node, int start, int end) 2168 { 2169 RenderTextControl* control = toRenderTextControl(node); 2170 if (control) 2171 setSelectionRange(node, start, end); 2172 else { 2173 Position startPosition = getPositionForOffset(node, start); 2174 Position endPosition = getPositionForOffset(node, end); 2175 VisibleSelection selection(startPosition, endPosition); 2176 SelectionController* selector = node->document()->frame()->selection(); 2177 selector->setSelection(selection); 2178 } 2179 } 2180 2181 void WebViewCore::setSelection(int start, int end) 2182 { 2183 WebCore::Node* focus = currentFocus(); 2184 if (!focus) 2185 return; 2186 if (start > end) 2187 swap(start, end); 2188 2189 // Tell our EditorClient that this change was generated from the UI, so it 2190 // does not need to echo it to the UI. 2191 EditorClientAndroid* client = static_cast<EditorClientAndroid*>( 2192 m_mainFrame->editor()->client()); 2193 client->setUiGeneratedSelectionChange(true); 2194 setSelection(focus, start, end); 2195 RenderTextControl* control = toRenderTextControl(focus); 2196 if (start != end && control) { 2197 // Fire a select event. No event is sent when the selection reduces to 2198 // an insertion point 2199 control->selectionChanged(true); 2200 } 2201 client->setUiGeneratedSelectionChange(false); 2202 bool isPasswordField = false; 2203 if (focus->isElementNode()) { 2204 WebCore::Element* element = static_cast<WebCore::Element*>(focus); 2205 if (WebCore::InputElement* inputElement = element->toInputElement()) 2206 isPasswordField = static_cast<WebCore::HTMLInputElement*>(inputElement)->isPasswordField(); 2207 } 2208 // For password fields, this is done in the UI side via 2209 // bringPointIntoView, since the UI does the drawing. 2210 if ((control && control->isTextArea()) || !isPasswordField) 2211 revealSelection(); 2212 } 2213 2214 String WebViewCore::modifySelection(const int direction, const int axis) 2215 { 2216 DOMSelection* selection = m_mainFrame->domWindow()->getSelection(); 2217 ASSERT(selection); 2218 // We've seen crashes where selection is null, but we don't know why 2219 // See http://b/5244036 2220 if (!selection) 2221 return String(); 2222 if (selection->rangeCount() > 1) 2223 selection->removeAllRanges(); 2224 switch (axis) { 2225 case AXIS_CHARACTER: 2226 case AXIS_WORD: 2227 case AXIS_SENTENCE: 2228 return modifySelectionTextNavigationAxis(selection, direction, axis); 2229 case AXIS_HEADING: 2230 case AXIS_SIBLING: 2231 case AXIS_PARENT_FIRST_CHILD: 2232 case AXIS_DOCUMENT: 2233 return modifySelectionDomNavigationAxis(selection, direction, axis); 2234 default: 2235 ALOGE("Invalid navigation axis: %d", axis); 2236 return String(); 2237 } 2238 } 2239 2240 void WebViewCore::scrollNodeIntoView(Frame* frame, Node* node) 2241 { 2242 if (!frame || !node) 2243 return; 2244 2245 Element* elementNode = 0; 2246 2247 // If not an Element, find a visible predecessor 2248 // Element to scroll into view. 2249 if (!node->isElementNode()) { 2250 HTMLElement* body = frame->document()->body(); 2251 do { 2252 if (node == body) 2253 return; 2254 node = node->parentNode(); 2255 } while (node && !node->isElementNode() && !isVisible(node)); 2256 } 2257 2258 // Couldn't find a visible predecessor. 2259 if (!node) 2260 return; 2261 2262 elementNode = static_cast<Element*>(node); 2263 elementNode->scrollIntoViewIfNeeded(true); 2264 } 2265 2266 String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int axis) 2267 { 2268 Node* body = m_mainFrame->document()->body(); 2269 2270 ExceptionCode ec = 0; 2271 String markup; 2272 2273 // initialize the selection if necessary 2274 if (selection->rangeCount() == 0) { 2275 if (m_currentNodeDomNavigationAxis 2276 && validNode(m_mainFrame, 2277 m_mainFrame, m_currentNodeDomNavigationAxis)) { 2278 RefPtr<Range> rangeRef = 2279 selection->frame()->document()->createRange(); 2280 rangeRef->selectNode(m_currentNodeDomNavigationAxis, ec); 2281 m_currentNodeDomNavigationAxis = 0; 2282 if (ec) 2283 return String(); 2284 selection->addRange(rangeRef.get()); 2285 } else if (currentFocus()) { 2286 selection->setPosition(currentFocus(), 0, ec); 2287 } else { 2288 selection->setPosition(body, 0, ec); 2289 } 2290 if (ec) 2291 return String(); 2292 } 2293 2294 // collapse the selection 2295 if (direction == DIRECTION_FORWARD) 2296 selection->collapseToEnd(ec); 2297 else 2298 selection->collapseToStart(ec); 2299 if (ec) 2300 return String(); 2301 2302 // Make sure the anchor node is a text node since we are generating 2303 // the markup of the selection which includes the anchor, the focus, 2304 // and any crossed nodes. Forcing the condition that the selection 2305 // starts and ends on text nodes guarantees symmetric selection markup. 2306 // Also this way the text content, rather its container, is highlighted. 2307 Node* anchorNode = selection->anchorNode(); 2308 if (anchorNode->isElementNode()) { 2309 // Collapsed selection while moving forward points to the 2310 // next unvisited node and while moving backward to the 2311 // last visited node. 2312 if (direction == DIRECTION_FORWARD) 2313 advanceAnchorNode(selection, direction, markup, false, ec); 2314 else 2315 advanceAnchorNode(selection, direction, markup, true, ec); 2316 if (ec) 2317 return String(); 2318 if (!markup.isEmpty()) 2319 return markup; 2320 } 2321 2322 // If the selection is at the end of a non white space text move 2323 // it to the next visible text node with non white space content. 2324 // This is a workaround for the selection getting stuck. 2325 anchorNode = selection->anchorNode(); 2326 if (anchorNode->isTextNode()) { 2327 if (direction == DIRECTION_FORWARD) { 2328 String suffix = anchorNode->textContent().substring( 2329 selection->anchorOffset(), caretMaxOffset(anchorNode)); 2330 // If at the end of non white space text we advance the 2331 // anchor node to either an input element or non empty text. 2332 if (suffix.stripWhiteSpace().isEmpty()) { 2333 advanceAnchorNode(selection, direction, markup, true, ec); 2334 } 2335 } else { 2336 String prefix = anchorNode->textContent().substring(0, 2337 selection->anchorOffset()); 2338 // If at the end of non white space text we advance the 2339 // anchor node to either an input element or non empty text. 2340 if (prefix.stripWhiteSpace().isEmpty()) { 2341 advanceAnchorNode(selection, direction, markup, true, ec); 2342 } 2343 } 2344 if (ec) 2345 return String(); 2346 if (!markup.isEmpty()) 2347 return markup; 2348 } 2349 2350 // extend the selection 2351 String directionStr; 2352 if (direction == DIRECTION_FORWARD) 2353 directionStr = "forward"; 2354 else 2355 directionStr = "backward"; 2356 2357 String axisStr; 2358 if (axis == AXIS_CHARACTER) 2359 axisStr = "character"; 2360 else if (axis == AXIS_WORD) 2361 axisStr = "word"; 2362 else 2363 axisStr = "sentence"; 2364 2365 selection->modify("extend", directionStr, axisStr); 2366 2367 // Make sure the focus node is a text node in order to have the 2368 // selection generate symmetric markup because the latter 2369 // includes all nodes crossed by the selection. Also this way 2370 // the text content, rather its container, is highlighted. 2371 Node* focusNode = selection->focusNode(); 2372 if (focusNode->isElementNode()) { 2373 focusNode = getImplicitBoundaryNode(selection->focusNode(), 2374 selection->focusOffset(), direction); 2375 if (!focusNode) 2376 return String(); 2377 if (direction == DIRECTION_FORWARD) { 2378 focusNode = focusNode->traversePreviousSiblingPostOrder(body); 2379 if (focusNode && !isContentTextNode(focusNode)) { 2380 Node* textNode = traverseNextContentTextNode(focusNode, 2381 anchorNode, DIRECTION_BACKWARD); 2382 if (textNode) 2383 anchorNode = textNode; 2384 } 2385 if (focusNode && isContentTextNode(focusNode)) { 2386 selection->extend(focusNode, caretMaxOffset(focusNode), ec); 2387 if (ec) 2388 return String(); 2389 } 2390 } else { 2391 focusNode = focusNode->traverseNextSibling(); 2392 if (focusNode && !isContentTextNode(focusNode)) { 2393 Node* textNode = traverseNextContentTextNode(focusNode, 2394 anchorNode, DIRECTION_FORWARD); 2395 if (textNode) 2396 anchorNode = textNode; 2397 } 2398 if (anchorNode && isContentTextNode(anchorNode)) { 2399 selection->extend(focusNode, 0, ec); 2400 if (ec) 2401 return String(); 2402 } 2403 } 2404 } 2405 2406 // Enforce that the selection does not cross anchor boundaries. This is 2407 // a workaround for the asymmetric behavior of WebKit while crossing 2408 // anchors. 2409 anchorNode = getImplicitBoundaryNode(selection->anchorNode(), 2410 selection->anchorOffset(), direction); 2411 focusNode = getImplicitBoundaryNode(selection->focusNode(), 2412 selection->focusOffset(), direction); 2413 if (anchorNode && focusNode && anchorNode != focusNode) { 2414 Node* inputControl = getIntermediaryInputElement(anchorNode, focusNode, 2415 direction); 2416 if (inputControl) { 2417 if (direction == DIRECTION_FORWARD) { 2418 if (isDescendantOf(inputControl, anchorNode)) { 2419 focusNode = inputControl; 2420 } else { 2421 focusNode = inputControl->traversePreviousSiblingPostOrder( 2422 body); 2423 if (!focusNode) 2424 focusNode = inputControl; 2425 } 2426 // We prefer a text node contained in the input element. 2427 if (!isContentTextNode(focusNode)) { 2428 Node* textNode = traverseNextContentTextNode(focusNode, 2429 anchorNode, DIRECTION_BACKWARD); 2430 if (textNode) 2431 focusNode = textNode; 2432 } 2433 // If we found text in the input select it. 2434 // Otherwise, select the input element itself. 2435 if (isContentTextNode(focusNode)) { 2436 selection->extend(focusNode, caretMaxOffset(focusNode), ec); 2437 } else if (anchorNode != focusNode) { 2438 // Note that the focusNode always has parent and that 2439 // the offset can be one more that the index of the last 2440 // element - this is how WebKit selects such elements. 2441 selection->extend(focusNode->parentNode(), 2442 focusNode->nodeIndex() + 1, ec); 2443 } 2444 if (ec) 2445 return String(); 2446 } else { 2447 if (isDescendantOf(inputControl, anchorNode)) { 2448 focusNode = inputControl; 2449 } else { 2450 focusNode = inputControl->traverseNextSibling(); 2451 if (!focusNode) 2452 focusNode = inputControl; 2453 } 2454 // We prefer a text node contained in the input element. 2455 if (!isContentTextNode(focusNode)) { 2456 Node* textNode = traverseNextContentTextNode(focusNode, 2457 anchorNode, DIRECTION_FORWARD); 2458 if (textNode) 2459 focusNode = textNode; 2460 } 2461 // If we found text in the input select it. 2462 // Otherwise, select the input element itself. 2463 if (isContentTextNode(focusNode)) { 2464 selection->extend(focusNode, caretMinOffset(focusNode), ec); 2465 } else if (anchorNode != focusNode) { 2466 // Note that the focusNode always has parent and that 2467 // the offset can be one more that the index of the last 2468 // element - this is how WebKit selects such elements. 2469 selection->extend(focusNode->parentNode(), 2470 focusNode->nodeIndex() + 1, ec); 2471 } 2472 if (ec) 2473 return String(); 2474 } 2475 } 2476 } 2477 2478 // make sure the selection is visible 2479 if (direction == DIRECTION_FORWARD) 2480 scrollNodeIntoView(m_mainFrame, selection->focusNode()); 2481 else 2482 scrollNodeIntoView(m_mainFrame, selection->anchorNode()); 2483 2484 // format markup for the visible content 2485 RefPtr<Range> range = selection->getRangeAt(0, ec); 2486 if (ec) 2487 return String(); 2488 IntRect bounds = range->boundingBox(); 2489 selectAt(bounds.center().x(), bounds.center().y()); 2490 markup = formatMarkup(selection); 2491 ALOGV("Selection markup: %s", markup.utf8().data()); 2492 2493 return markup; 2494 } 2495 2496 Node* WebViewCore::getImplicitBoundaryNode(Node* node, unsigned offset, int direction) 2497 { 2498 if (node->offsetInCharacters()) 2499 return node; 2500 if (!node->hasChildNodes()) 2501 return node; 2502 if (offset < node->childNodeCount()) 2503 return node->childNode(offset); 2504 else 2505 if (direction == DIRECTION_FORWARD) 2506 return node->traverseNextSibling(); 2507 else 2508 return node->traversePreviousNodePostOrder( 2509 node->document()->body()); 2510 } 2511 2512 Node* WebViewCore::getNextAnchorNode(Node* anchorNode, bool ignoreFirstNode, int direction) 2513 { 2514 Node* body = 0; 2515 Node* currentNode = 0; 2516 if (direction == DIRECTION_FORWARD) { 2517 if (ignoreFirstNode) 2518 currentNode = anchorNode->traverseNextNode(body); 2519 else 2520 currentNode = anchorNode; 2521 } else { 2522 body = anchorNode->document()->body(); 2523 if (ignoreFirstNode) 2524 currentNode = anchorNode->traversePreviousSiblingPostOrder(body); 2525 else 2526 currentNode = anchorNode; 2527 } 2528 while (currentNode) { 2529 if (isContentTextNode(currentNode) 2530 || isContentInputElement(currentNode)) 2531 return currentNode; 2532 if (direction == DIRECTION_FORWARD) 2533 currentNode = currentNode->traverseNextNode(); 2534 else 2535 currentNode = currentNode->traversePreviousNodePostOrder(body); 2536 } 2537 return 0; 2538 } 2539 2540 void WebViewCore::advanceAnchorNode(DOMSelection* selection, int direction, 2541 String& markup, bool ignoreFirstNode, ExceptionCode& ec) 2542 { 2543 Node* anchorNode = getImplicitBoundaryNode(selection->anchorNode(), 2544 selection->anchorOffset(), direction); 2545 if (!anchorNode) { 2546 ec = NOT_FOUND_ERR; 2547 return; 2548 } 2549 // If the anchor offset is invalid i.e. the anchor node has no 2550 // child with that index getImplicitAnchorNode returns the next 2551 // logical node in the current direction. In such a case our 2552 // position in the DOM tree was has already been advanced, 2553 // therefore we there is no need to do that again. 2554 if (selection->anchorNode()->isElementNode()) { 2555 unsigned anchorOffset = selection->anchorOffset(); 2556 unsigned childNodeCount = selection->anchorNode()->childNodeCount(); 2557 if (anchorOffset >= childNodeCount) 2558 ignoreFirstNode = false; 2559 } 2560 // Find the next anchor node given our position in the DOM and 2561 // whether we want the current node to be considered as well. 2562 Node* nextAnchorNode = getNextAnchorNode(anchorNode, ignoreFirstNode, 2563 direction); 2564 if (!nextAnchorNode) { 2565 ec = NOT_FOUND_ERR; 2566 return; 2567 } 2568 if (nextAnchorNode->isElementNode()) { 2569 // If this is an input element tell the WebView thread 2570 // to set the cursor to that control. 2571 if (isContentInputElement(nextAnchorNode)) { 2572 IntRect bounds = nextAnchorNode->getRect(); 2573 selectAt(bounds.center().x(), bounds.center().y()); 2574 } 2575 Node* textNode = 0; 2576 // Treat the text content of links as any other text but 2577 // for the rest input elements select the control itself. 2578 if (nextAnchorNode->hasTagName(WebCore::HTMLNames::aTag)) 2579 textNode = traverseNextContentTextNode(nextAnchorNode, 2580 nextAnchorNode, direction); 2581 // We prefer to select the text content of the link if such, 2582 // otherwise just select the element itself. 2583 if (textNode) { 2584 nextAnchorNode = textNode; 2585 } else { 2586 if (direction == DIRECTION_FORWARD) { 2587 selection->setBaseAndExtent(nextAnchorNode, 2588 caretMinOffset(nextAnchorNode), nextAnchorNode, 2589 caretMaxOffset(nextAnchorNode), ec); 2590 } else { 2591 selection->setBaseAndExtent(nextAnchorNode, 2592 caretMaxOffset(nextAnchorNode), nextAnchorNode, 2593 caretMinOffset(nextAnchorNode), ec); 2594 } 2595 if (!ec) 2596 markup = formatMarkup(selection); 2597 // make sure the selection is visible 2598 scrollNodeIntoView(selection->frame(), nextAnchorNode); 2599 return; 2600 } 2601 } 2602 if (direction == DIRECTION_FORWARD) 2603 selection->setPosition(nextAnchorNode, 2604 caretMinOffset(nextAnchorNode), ec); 2605 else 2606 selection->setPosition(nextAnchorNode, 2607 caretMaxOffset(nextAnchorNode), ec); 2608 } 2609 2610 bool WebViewCore::isContentInputElement(Node* node) 2611 { 2612 return (isVisible(node) 2613 && (node->hasTagName(WebCore::HTMLNames::selectTag) 2614 || node->hasTagName(WebCore::HTMLNames::aTag) 2615 || node->hasTagName(WebCore::HTMLNames::inputTag) 2616 || node->hasTagName(WebCore::HTMLNames::buttonTag))); 2617 } 2618 2619 bool WebViewCore::isContentTextNode(Node* node) 2620 { 2621 if (!node || !node->isTextNode()) 2622 return false; 2623 Text* textNode = static_cast<Text*>(node); 2624 return (isVisible(textNode) && textNode->length() > 0 2625 && !textNode->containsOnlyWhitespace()); 2626 } 2627 2628 Text* WebViewCore::traverseNextContentTextNode(Node* fromNode, Node* toNode, int direction) 2629 { 2630 Node* currentNode = fromNode; 2631 do { 2632 if (direction == DIRECTION_FORWARD) 2633 currentNode = currentNode->traverseNextNode(toNode); 2634 else 2635 currentNode = currentNode->traversePreviousNodePostOrder(toNode); 2636 } while (currentNode && !isContentTextNode(currentNode)); 2637 return static_cast<Text*>(currentNode); 2638 } 2639 2640 Node* WebViewCore::getIntermediaryInputElement(Node* fromNode, Node* toNode, int direction) 2641 { 2642 if (fromNode == toNode) 2643 return 0; 2644 if (direction == DIRECTION_FORWARD) { 2645 Node* currentNode = fromNode; 2646 while (currentNode && currentNode != toNode) { 2647 if (isContentInputElement(currentNode)) 2648 return currentNode; 2649 currentNode = currentNode->traverseNextNodePostOrder(); 2650 } 2651 currentNode = fromNode; 2652 while (currentNode && currentNode != toNode) { 2653 if (isContentInputElement(currentNode)) 2654 return currentNode; 2655 currentNode = currentNode->traverseNextNode(); 2656 } 2657 } else { 2658 Node* currentNode = fromNode->traversePreviousNode(); 2659 while (currentNode && currentNode != toNode) { 2660 if (isContentInputElement(currentNode)) 2661 return currentNode; 2662 currentNode = currentNode->traversePreviousNode(); 2663 } 2664 currentNode = fromNode->traversePreviousNodePostOrder(); 2665 while (currentNode && currentNode != toNode) { 2666 if (isContentInputElement(currentNode)) 2667 return currentNode; 2668 currentNode = currentNode->traversePreviousNodePostOrder(); 2669 } 2670 } 2671 return 0; 2672 } 2673 2674 bool WebViewCore::isDescendantOf(Node* parent, Node* node) 2675 { 2676 Node* currentNode = node; 2677 while (currentNode) { 2678 if (currentNode == parent) { 2679 return true; 2680 } 2681 currentNode = currentNode->parentNode(); 2682 } 2683 return false; 2684 } 2685 2686 String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int axis) 2687 { 2688 HTMLElement* body = m_mainFrame->document()->body(); 2689 if (!m_currentNodeDomNavigationAxis && selection->focusNode()) { 2690 m_currentNodeDomNavigationAxis = selection->focusNode(); 2691 selection->empty(); 2692 if (m_currentNodeDomNavigationAxis->isTextNode()) 2693 m_currentNodeDomNavigationAxis = 2694 m_currentNodeDomNavigationAxis->parentNode(); 2695 } 2696 if (!m_currentNodeDomNavigationAxis) 2697 m_currentNodeDomNavigationAxis = currentFocus(); 2698 if (!m_currentNodeDomNavigationAxis 2699 || !validNode(m_mainFrame, m_mainFrame, 2700 m_currentNodeDomNavigationAxis)) 2701 m_currentNodeDomNavigationAxis = body; 2702 Node* currentNode = m_currentNodeDomNavigationAxis; 2703 if (axis == AXIS_HEADING) { 2704 if (currentNode == body && direction == DIRECTION_BACKWARD) 2705 currentNode = currentNode->lastDescendant(); 2706 do { 2707 if (direction == DIRECTION_FORWARD) 2708 currentNode = currentNode->traverseNextNode(body); 2709 else 2710 currentNode = currentNode->traversePreviousNode(body); 2711 } while (currentNode && (currentNode->isTextNode() 2712 || !isVisible(currentNode) || !isHeading(currentNode))); 2713 } else if (axis == AXIS_PARENT_FIRST_CHILD) { 2714 if (direction == DIRECTION_FORWARD) { 2715 currentNode = currentNode->firstChild(); 2716 while (currentNode && (currentNode->isTextNode() 2717 || !isVisible(currentNode))) 2718 currentNode = currentNode->nextSibling(); 2719 } else { 2720 do { 2721 if (currentNode == body) 2722 return String(); 2723 currentNode = currentNode->parentNode(); 2724 } while (currentNode && (currentNode->isTextNode() 2725 || !isVisible(currentNode))); 2726 } 2727 } else if (axis == AXIS_SIBLING) { 2728 do { 2729 if (direction == DIRECTION_FORWARD) 2730 currentNode = currentNode->nextSibling(); 2731 else { 2732 if (currentNode == body) 2733 return String(); 2734 currentNode = currentNode->previousSibling(); 2735 } 2736 } while (currentNode && (currentNode->isTextNode() 2737 || !isVisible(currentNode))); 2738 } else if (axis == AXIS_DOCUMENT) { 2739 currentNode = body; 2740 if (direction == DIRECTION_FORWARD) 2741 currentNode = currentNode->lastDescendant(); 2742 } else { 2743 ALOGE("Invalid axis: %d", axis); 2744 return String(); 2745 } 2746 if (currentNode) { 2747 m_currentNodeDomNavigationAxis = currentNode; 2748 scrollNodeIntoView(m_mainFrame, currentNode); 2749 String selectionString = createMarkup(currentNode); 2750 ALOGV("Selection markup: %s", selectionString.utf8().data()); 2751 return selectionString; 2752 } 2753 return String(); 2754 } 2755 2756 bool WebViewCore::isHeading(Node* node) 2757 { 2758 if (node->hasTagName(WebCore::HTMLNames::h1Tag) 2759 || node->hasTagName(WebCore::HTMLNames::h2Tag) 2760 || node->hasTagName(WebCore::HTMLNames::h3Tag) 2761 || node->hasTagName(WebCore::HTMLNames::h4Tag) 2762 || node->hasTagName(WebCore::HTMLNames::h5Tag) 2763 || node->hasTagName(WebCore::HTMLNames::h6Tag)) { 2764 return true; 2765 } 2766 2767 if (node->isElementNode()) { 2768 Element* element = static_cast<Element*>(node); 2769 String roleAttribute = 2770 element->getAttribute(WebCore::HTMLNames::roleAttr).string(); 2771 if (equalIgnoringCase(roleAttribute, "heading")) 2772 return true; 2773 } 2774 2775 return false; 2776 } 2777 2778 bool WebViewCore::isVisible(Node* node) 2779 { 2780 // start off an element 2781 Element* element = 0; 2782 if (node->isElementNode()) 2783 element = static_cast<Element*>(node); 2784 else 2785 element = node->parentElement(); 2786 // check renderer 2787 if (!element->renderer()) { 2788 return false; 2789 } 2790 // check size 2791 if (element->offsetHeight() == 0 || element->offsetWidth() == 0) { 2792 return false; 2793 } 2794 // check style 2795 Node* body = m_mainFrame->document()->body(); 2796 Node* currentNode = element; 2797 while (currentNode && currentNode != body) { 2798 RenderStyle* style = currentNode->computedStyle(); 2799 if (style && 2800 (style->display() == WebCore::NONE || style->visibility() == WebCore::HIDDEN)) { 2801 return false; 2802 } 2803 currentNode = currentNode->parentNode(); 2804 } 2805 return true; 2806 } 2807 2808 String WebViewCore::formatMarkup(DOMSelection* selection) 2809 { 2810 ExceptionCode ec = 0; 2811 String markup = String(); 2812 RefPtr<Range> wholeRange = selection->getRangeAt(0, ec); 2813 if (ec) 2814 return String(); 2815 if (!wholeRange->startContainer() || !wholeRange->startContainer()) 2816 return String(); 2817 // Since formatted markup contains invisible nodes it 2818 // is created from the concatenation of the visible fragments. 2819 Node* firstNode = wholeRange->firstNode(); 2820 Node* pastLastNode = wholeRange->pastLastNode(); 2821 Node* currentNode = firstNode; 2822 RefPtr<Range> currentRange; 2823 2824 while (currentNode != pastLastNode) { 2825 Node* nextNode = currentNode->traverseNextNode(); 2826 if (!isVisible(currentNode)) { 2827 if (currentRange) { 2828 markup = markup + currentRange->toHTML().utf8().data(); 2829 currentRange = 0; 2830 } 2831 } else { 2832 if (!currentRange) { 2833 currentRange = selection->frame()->document()->createRange(); 2834 if (ec) 2835 break; 2836 if (currentNode == firstNode) { 2837 currentRange->setStart(wholeRange->startContainer(), 2838 wholeRange->startOffset(), ec); 2839 if (ec) 2840 break; 2841 } else { 2842 currentRange->setStart(currentNode->parentNode(), 2843 currentNode->nodeIndex(), ec); 2844 if (ec) 2845 break; 2846 } 2847 } 2848 if (nextNode == pastLastNode) { 2849 currentRange->setEnd(wholeRange->endContainer(), 2850 wholeRange->endOffset(), ec); 2851 if (ec) 2852 break; 2853 markup = markup + currentRange->toHTML().utf8().data(); 2854 } else { 2855 if (currentNode->offsetInCharacters()) 2856 currentRange->setEnd(currentNode, 2857 currentNode->maxCharacterOffset(), ec); 2858 else 2859 currentRange->setEnd(currentNode->parentNode(), 2860 currentNode->nodeIndex() + 1, ec); 2861 if (ec) 2862 break; 2863 } 2864 } 2865 currentNode = nextNode; 2866 } 2867 return markup.stripWhiteSpace(); 2868 } 2869 2870 void WebViewCore::selectAt(int x, int y) 2871 { 2872 JNIEnv* env = JSC::Bindings::getJNIEnv(); 2873 AutoJObject javaObject = m_javaGlue->object(env); 2874 if (!javaObject.get()) 2875 return; 2876 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_selectAt, x, y); 2877 checkException(env); 2878 } 2879 2880 void WebViewCore::deleteSelection(int start, int end, int textGeneration) 2881 { 2882 setSelection(start, end); 2883 if (start == end) 2884 return; 2885 WebCore::Node* focus = currentFocus(); 2886 if (!focus) 2887 return; 2888 // Prevent our editor client from passing a message to change the 2889 // selection. 2890 EditorClientAndroid* client = static_cast<EditorClientAndroid*>( 2891 m_mainFrame->editor()->client()); 2892 client->setUiGeneratedSelectionChange(true); 2893 PlatformKeyboardEvent down(AKEYCODE_DEL, 0, 0, true, false, false, false); 2894 PlatformKeyboardEvent up(AKEYCODE_DEL, 0, 0, false, false, false, false); 2895 key(down); 2896 key(up); 2897 client->setUiGeneratedSelectionChange(false); 2898 m_textGeneration = textGeneration; 2899 } 2900 2901 void WebViewCore::replaceTextfieldText(int oldStart, 2902 int oldEnd, const WTF::String& replace, int start, int end, 2903 int textGeneration) 2904 { 2905 WebCore::Node* focus = currentFocus(); 2906 if (!focus) 2907 return; 2908 setSelection(oldStart, oldEnd); 2909 // Prevent our editor client from passing a message to change the 2910 // selection. 2911 EditorClientAndroid* client = static_cast<EditorClientAndroid*>( 2912 m_mainFrame->editor()->client()); 2913 client->setUiGeneratedSelectionChange(true); 2914 if (replace.length()) 2915 WebCore::TypingCommand::insertText(focus->document(), replace, 2916 false); 2917 else 2918 WebCore::TypingCommand::deleteSelection(focus->document()); 2919 client->setUiGeneratedSelectionChange(false); 2920 // setSelection calls revealSelection, so there is no need to do it here. 2921 setSelection(start, end); 2922 m_textGeneration = textGeneration; 2923 } 2924 2925 void WebViewCore::passToJs(int generation, const WTF::String& current, 2926 const PlatformKeyboardEvent& event) 2927 { 2928 WebCore::Node* focus = currentFocus(); 2929 if (!focus) { 2930 clearTextEntry(); 2931 return; 2932 } 2933 // Block text field updates during a key press. 2934 m_blockTextfieldUpdates = true; 2935 // Also prevent our editor client from passing a message to change the 2936 // selection. 2937 EditorClientAndroid* client = static_cast<EditorClientAndroid*>( 2938 m_mainFrame->editor()->client()); 2939 client->setUiGeneratedSelectionChange(true); 2940 key(event); 2941 client->setUiGeneratedSelectionChange(false); 2942 m_blockTextfieldUpdates = false; 2943 m_textGeneration = generation; 2944 WTF::String test = getInputText(focus); 2945 if (test != current) { 2946 // If the text changed during the key event, update the UI text field. 2947 updateTextfield(focus, false, test); 2948 } 2949 // Now that the selection has settled down, send it. 2950 updateTextSelection(); 2951 } 2952 2953 WebCore::IntRect WebViewCore::scrollFocusedTextInput(float xPercent, int y) 2954 { 2955 WebCore::Node* focus = currentFocus(); 2956 if (!focus) { 2957 clearTextEntry(); 2958 return WebCore::IntRect(); 2959 } 2960 WebCore::RenderTextControl* renderText = toRenderTextControl(focus); 2961 if (!renderText) { 2962 clearTextEntry(); 2963 return WebCore::IntRect(); 2964 } 2965 2966 int x = (int) (xPercent * (renderText->scrollWidth() - 2967 renderText->clientWidth())); 2968 renderText->setScrollLeft(x); 2969 renderText->setScrollTop(y); 2970 focus->document()->frame()->selection()->recomputeCaretRect(); 2971 LayerAndroid* layer = 0; 2972 platformLayerIdFromNode(focus, &layer); 2973 return absoluteContentRect(focus, layer); 2974 } 2975 2976 void WebViewCore::setFocusControllerActive(bool active) 2977 { 2978 m_mainFrame->page()->focusController()->setActive(active); 2979 } 2980 2981 void WebViewCore::saveDocumentState(WebCore::Frame* frame) 2982 { 2983 if (!validNode(m_mainFrame, frame, 0)) 2984 frame = m_mainFrame; 2985 WebCore::HistoryItem *item = frame->loader()->history()->currentItem(); 2986 2987 // item can be null when there is no offical URL for the current page. This happens 2988 // when the content is loaded using with WebCoreFrameBridge::LoadData() and there 2989 // is no failing URL (common case is when content is loaded using data: scheme) 2990 if (item) { 2991 item->setDocumentState(frame->document()->formElementsState()); 2992 } 2993 } 2994 2995 // Create an array of java Strings. 2996 static jobjectArray makeLabelArray(JNIEnv* env, const uint16_t** labels, size_t count) 2997 { 2998 jclass stringClass = env->FindClass("java/lang/String"); 2999 ALOG_ASSERT(stringClass, "Could not find java/lang/String"); 3000 jobjectArray array = env->NewObjectArray(count, stringClass, 0); 3001 ALOG_ASSERT(array, "Could not create new string array"); 3002 3003 for (size_t i = 0; i < count; i++) { 3004 jobject newString = env->NewString(&labels[i][1], labels[i][0]); 3005 env->SetObjectArrayElement(array, i, newString); 3006 env->DeleteLocalRef(newString); 3007 checkException(env); 3008 } 3009 env->DeleteLocalRef(stringClass); 3010 return array; 3011 } 3012 3013 void WebViewCore::openFileChooser(PassRefPtr<WebCore::FileChooser> chooser) 3014 { 3015 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3016 AutoJObject javaObject = m_javaGlue->object(env); 3017 if (!javaObject.get()) 3018 return; 3019 3020 if (!chooser) 3021 return; 3022 3023 WTF::String acceptType = chooser->acceptTypes(); 3024 WTF::String capture; 3025 3026 #if ENABLE(MEDIA_CAPTURE) 3027 capture = chooser->capture(); 3028 #endif 3029 3030 jstring jAcceptType = wtfStringToJstring(env, acceptType, true); 3031 jstring jCapture = wtfStringToJstring(env, capture, true); 3032 jstring jName = (jstring) env->CallObjectMethod( 3033 javaObject.get(), m_javaGlue->m_openFileChooser, jAcceptType, jCapture); 3034 checkException(env); 3035 env->DeleteLocalRef(jAcceptType); 3036 env->DeleteLocalRef(jCapture); 3037 3038 WTF::String wtfString = jstringToWtfString(env, jName); 3039 env->DeleteLocalRef(jName); 3040 3041 if (!wtfString.isEmpty()) 3042 chooser->chooseFile(wtfString); 3043 } 3044 3045 void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount, 3046 bool multiple, const int selected[], size_t selectedCountOrSelection) 3047 { 3048 ALOG_ASSERT(m_javaGlue->m_obj, "No java widget associated with this view!"); 3049 3050 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3051 AutoJObject javaObject = m_javaGlue->object(env); 3052 if (!javaObject.get()) 3053 return; 3054 3055 // If m_popupReply is not null, then we already have a list showing. 3056 if (m_popupReply != 0) 3057 return; 3058 3059 // Create an array of java Strings for the drop down. 3060 jobjectArray labelArray = makeLabelArray(env, labels, count); 3061 3062 // Create an array determining whether each item is enabled. 3063 jintArray enabledArray = env->NewIntArray(enabledCount); 3064 checkException(env); 3065 jint* ptrArray = env->GetIntArrayElements(enabledArray, 0); 3066 checkException(env); 3067 for (size_t i = 0; i < enabledCount; i++) { 3068 ptrArray[i] = enabled[i]; 3069 } 3070 env->ReleaseIntArrayElements(enabledArray, ptrArray, 0); 3071 checkException(env); 3072 3073 if (multiple) { 3074 // Pass up an array representing which items are selected. 3075 jintArray selectedArray = env->NewIntArray(selectedCountOrSelection); 3076 checkException(env); 3077 jint* selArray = env->GetIntArrayElements(selectedArray, 0); 3078 checkException(env); 3079 for (size_t i = 0; i < selectedCountOrSelection; i++) { 3080 selArray[i] = selected[i]; 3081 } 3082 env->ReleaseIntArrayElements(selectedArray, selArray, 0); 3083 3084 env->CallVoidMethod(javaObject.get(), 3085 m_javaGlue->m_requestListBox, labelArray, enabledArray, 3086 selectedArray); 3087 env->DeleteLocalRef(selectedArray); 3088 } else { 3089 // Pass up the single selection. 3090 env->CallVoidMethod(javaObject.get(), 3091 m_javaGlue->m_requestSingleListBox, labelArray, enabledArray, 3092 selectedCountOrSelection); 3093 } 3094 3095 env->DeleteLocalRef(labelArray); 3096 env->DeleteLocalRef(enabledArray); 3097 checkException(env); 3098 3099 Retain(reply); 3100 m_popupReply = reply; 3101 } 3102 3103 bool WebViewCore::key(const PlatformKeyboardEvent& event) 3104 { 3105 WebCore::EventHandler* eventHandler; 3106 WebCore::Node* focusNode = currentFocus(); 3107 if (focusNode) { 3108 WebCore::Frame* frame = focusNode->document()->frame(); 3109 eventHandler = frame->eventHandler(); 3110 VisibleSelection old = frame->selection()->selection(); 3111 EditorClientAndroid* client = static_cast<EditorClientAndroid*>( 3112 m_mainFrame->editor()->client()); 3113 client->setUiGeneratedSelectionChange(true); 3114 bool handled = eventHandler->keyEvent(event); 3115 client->setUiGeneratedSelectionChange(false); 3116 if (isContentEditable(focusNode)) { 3117 // keyEvent will return true even if the contentEditable did not 3118 // change its selection. In the case that it does not, we want to 3119 // return false so that the key will be sent back to our navigation 3120 // system. 3121 handled |= frame->selection()->selection() != old; 3122 } 3123 return handled; 3124 } else { 3125 eventHandler = focusedFrame()->eventHandler(); 3126 } 3127 return eventHandler->keyEvent(event); 3128 } 3129 3130 bool WebViewCore::chromeCanTakeFocus(FocusDirection direction) 3131 { 3132 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3133 AutoJObject javaObject = m_javaGlue->object(env); 3134 if (!javaObject.get()) 3135 return false; 3136 return env->CallBooleanMethod(javaObject.get(), m_javaGlue->m_chromeCanTakeFocus, direction); 3137 } 3138 3139 void WebViewCore::chromeTakeFocus(FocusDirection direction) 3140 { 3141 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3142 AutoJObject javaObject = m_javaGlue->object(env); 3143 if (!javaObject.get()) 3144 return; 3145 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_chromeTakeFocus, direction); 3146 } 3147 3148 void WebViewCore::setInitialFocus(const WebCore::PlatformKeyboardEvent& platformEvent) 3149 { 3150 Frame* frame = focusedFrame(); 3151 Document* document = frame->document(); 3152 if (document) 3153 document->setFocusedNode(0); 3154 FocusDirection direction; 3155 switch (platformEvent.nativeVirtualKeyCode()) { 3156 case AKEYCODE_DPAD_LEFT: 3157 direction = FocusDirectionLeft; 3158 break; 3159 case AKEYCODE_DPAD_RIGHT: 3160 direction = FocusDirectionRight; 3161 break; 3162 case AKEYCODE_DPAD_UP: 3163 direction = FocusDirectionUp; 3164 break; 3165 default: 3166 direction = FocusDirectionDown; 3167 break; 3168 } 3169 RefPtr<KeyboardEvent> webkitEvent = KeyboardEvent::create(platformEvent, 0); 3170 m_mainFrame->page()->focusController()->setInitialFocus(direction, 3171 webkitEvent.get()); 3172 } 3173 3174 #if USE(ACCELERATED_COMPOSITING) 3175 GraphicsLayerAndroid* WebViewCore::graphicsRootLayer() const 3176 { 3177 RenderView* contentRenderer = m_mainFrame->contentRenderer(); 3178 if (!contentRenderer) 3179 return 0; 3180 return static_cast<GraphicsLayerAndroid*>( 3181 contentRenderer->compositor()->rootPlatformLayer()); 3182 } 3183 #endif 3184 3185 int WebViewCore::handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>& points, int actionIndex, int metaState) 3186 { 3187 int flags = 0; 3188 3189 #if USE(ACCELERATED_COMPOSITING) 3190 GraphicsLayerAndroid* rootLayer = graphicsRootLayer(); 3191 if (rootLayer) 3192 rootLayer->pauseDisplay(true); 3193 #endif 3194 3195 #if ENABLE(TOUCH_EVENTS) // Android 3196 #define MOTION_EVENT_ACTION_POINTER_DOWN 5 3197 #define MOTION_EVENT_ACTION_POINTER_UP 6 3198 3199 WebCore::TouchEventType type = WebCore::TouchStart; 3200 WebCore::PlatformTouchPoint::State defaultTouchState; 3201 Vector<WebCore::PlatformTouchPoint::State> touchStates(points.size()); 3202 3203 switch (action) { 3204 case 0: // MotionEvent.ACTION_DOWN 3205 type = WebCore::TouchStart; 3206 defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed; 3207 break; 3208 case 1: // MotionEvent.ACTION_UP 3209 type = WebCore::TouchEnd; 3210 defaultTouchState = WebCore::PlatformTouchPoint::TouchReleased; 3211 break; 3212 case 2: // MotionEvent.ACTION_MOVE 3213 type = WebCore::TouchMove; 3214 defaultTouchState = WebCore::PlatformTouchPoint::TouchMoved; 3215 break; 3216 case 3: // MotionEvent.ACTION_CANCEL 3217 type = WebCore::TouchCancel; 3218 defaultTouchState = WebCore::PlatformTouchPoint::TouchCancelled; 3219 break; 3220 case 5: // MotionEvent.ACTION_POINTER_DOWN 3221 type = WebCore::TouchStart; 3222 defaultTouchState = WebCore::PlatformTouchPoint::TouchStationary; 3223 break; 3224 case 6: // MotionEvent.ACTION_POINTER_UP 3225 type = WebCore::TouchEnd; 3226 defaultTouchState = WebCore::PlatformTouchPoint::TouchStationary; 3227 break; 3228 default: 3229 // We do not support other kinds of touch event inside WebCore 3230 // at the moment. 3231 ALOGW("Java passed a touch event type that we do not support in WebCore: %d", action); 3232 return 0; 3233 } 3234 3235 for (int c = 0; c < static_cast<int>(points.size()); c++) { 3236 points[c].setX(points[c].x() - m_scrollOffsetX); 3237 points[c].setY(points[c].y() - m_scrollOffsetY); 3238 3239 // Setting the touch state for each point. 3240 // Note: actionIndex will be 0 for all actions that are not ACTION_POINTER_DOWN/UP. 3241 if (action == MOTION_EVENT_ACTION_POINTER_DOWN && c == actionIndex) { 3242 touchStates[c] = WebCore::PlatformTouchPoint::TouchPressed; 3243 } else if (action == MOTION_EVENT_ACTION_POINTER_UP && c == actionIndex) { 3244 touchStates[c] = WebCore::PlatformTouchPoint::TouchReleased; 3245 } else { 3246 touchStates[c] = defaultTouchState; 3247 }; 3248 } 3249 3250 WebCore::PlatformTouchEvent te(ids, points, type, touchStates, metaState); 3251 if (m_mainFrame->eventHandler()->handleTouchEvent(te)) 3252 flags |= TOUCH_FLAG_PREVENT_DEFAULT; 3253 if (te.hitTouchHandler()) 3254 flags |= TOUCH_FLAG_HIT_HANDLER; 3255 #endif 3256 3257 #if USE(ACCELERATED_COMPOSITING) 3258 if (rootLayer) 3259 rootLayer->pauseDisplay(false); 3260 #endif 3261 return flags; 3262 } 3263 3264 bool WebViewCore::performMouseClick() 3265 { 3266 WebCore::PlatformMouseEvent mouseDown(m_mouseClickPos, m_mouseClickPos, WebCore::LeftButton, 3267 WebCore::MouseEventPressed, 1, false, false, false, false, 3268 WTF::currentTime()); 3269 // ignore the return from as it will return true if the hit point can trigger selection change 3270 m_mainFrame->eventHandler()->handleMousePressEvent(mouseDown); 3271 WebCore::PlatformMouseEvent mouseUp(m_mouseClickPos, m_mouseClickPos, WebCore::LeftButton, 3272 WebCore::MouseEventReleased, 1, false, false, false, false, 3273 WTF::currentTime()); 3274 bool handled = m_mainFrame->eventHandler()->handleMouseReleaseEvent(mouseUp); 3275 3276 WebCore::Node* focusNode = currentFocus(); 3277 initializeTextInput(focusNode, false); 3278 return handled; 3279 } 3280 3281 // Check for the "x-webkit-soft-keyboard" attribute. If it is there and 3282 // set to hidden, do not show the soft keyboard. Node passed as a parameter 3283 // must not be null. 3284 static bool shouldSuppressKeyboard(const WebCore::Node* node) { 3285 ALOG_ASSERT(node, "node passed to shouldSuppressKeyboard cannot be null"); 3286 const NamedNodeMap* attributes = node->attributes(); 3287 if (!attributes) return false; 3288 size_t length = attributes->length(); 3289 for (size_t i = 0; i < length; i++) { 3290 const Attribute* a = attributes->attributeItem(i); 3291 if (a->localName() == "x-webkit-soft-keyboard" && a->value() == "hidden") 3292 return true; 3293 } 3294 return false; 3295 } 3296 3297 WebViewCore::InputType WebViewCore::getInputType(Node* node) 3298 { 3299 WebCore::RenderObject* renderer = node->renderer(); 3300 if (!renderer) 3301 return WebViewCore::NONE; 3302 if (renderer->isTextArea()) 3303 return WebViewCore::TEXT_AREA; 3304 3305 if (node->hasTagName(WebCore::HTMLNames::inputTag)) { 3306 HTMLInputElement* htmlInput = static_cast<HTMLInputElement*>(node); 3307 if (htmlInput->isPasswordField()) 3308 return WebViewCore::PASSWORD; 3309 if (htmlInput->isSearchField()) 3310 return WebViewCore::SEARCH; 3311 if (htmlInput->isEmailField()) 3312 return WebViewCore::EMAIL; 3313 if (htmlInput->isNumberField()) 3314 return WebViewCore::NUMBER; 3315 if (htmlInput->isTelephoneField()) 3316 return WebViewCore::TELEPHONE; 3317 if (htmlInput->isTextField()) 3318 return WebViewCore::NORMAL_TEXT_FIELD; 3319 } 3320 3321 if (node->isContentEditable()) 3322 return WebViewCore::TEXT_AREA; 3323 3324 return WebViewCore::NONE; 3325 } 3326 3327 int WebViewCore::getMaxLength(Node* node) 3328 { 3329 int maxLength = -1; 3330 if (node->hasTagName(WebCore::HTMLNames::inputTag)) { 3331 HTMLInputElement* htmlInput = static_cast<HTMLInputElement*>(node); 3332 maxLength = htmlInput->maxLength(); 3333 } 3334 return maxLength; 3335 } 3336 3337 String WebViewCore::getFieldName(Node* node) 3338 { 3339 String name; 3340 if (node->hasTagName(WebCore::HTMLNames::inputTag)) { 3341 HTMLInputElement* htmlInput = static_cast<HTMLInputElement*>(node); 3342 name = htmlInput->name(); 3343 } 3344 return name; 3345 } 3346 3347 bool WebViewCore::isSpellCheckEnabled(Node* node) 3348 { 3349 bool isEnabled = true; 3350 if (node->isElementNode()) { 3351 WebCore::Element* element = static_cast<WebCore::Element*>(node); 3352 isEnabled = element->isSpellCheckingEnabled(); 3353 } 3354 return isEnabled; 3355 } 3356 3357 bool WebViewCore::isAutoCompleteEnabled(Node* node) 3358 { 3359 bool isEnabled = false; 3360 if (node->hasTagName(WebCore::HTMLNames::inputTag)) { 3361 HTMLInputElement* htmlInput = static_cast<HTMLInputElement*>(node); 3362 isEnabled = htmlInput->autoComplete(); 3363 } 3364 return isEnabled; 3365 } 3366 3367 WebCore::IntRect WebViewCore::absoluteContentRect(WebCore::Node* node, 3368 LayerAndroid* layer) 3369 { 3370 IntRect contentRect; 3371 if (node) { 3372 RenderObject* render = node->renderer(); 3373 if (render && render->isBox() && !render->isBody()) { 3374 IntPoint offset = convertGlobalContentToFrameContent(IntPoint(), 3375 node->document()->frame()); 3376 WebViewCore::layerToAbsoluteOffset(layer, offset); 3377 3378 RenderBox* renderBox = toRenderBox(render); 3379 contentRect = renderBox->absoluteContentBox(); 3380 contentRect.move(-offset.x(), -offset.y()); 3381 } 3382 } 3383 return contentRect; 3384 } 3385 3386 jobject WebViewCore::createTextFieldInitData(Node* node) 3387 { 3388 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3389 TextFieldInitDataGlue* classDef = m_textFieldInitDataGlue; 3390 ScopedLocalRef<jclass> clazz(env, 3391 env->FindClass("android/webkit/WebViewCore$TextFieldInitData")); 3392 jobject initData = env->NewObject(clazz.get(), classDef->m_constructor); 3393 env->SetIntField(initData, classDef->m_fieldPointer, 3394 reinterpret_cast<int>(node)); 3395 ScopedLocalRef<jstring> inputText(env, 3396 wtfStringToJstring(env, getInputText(node), true)); 3397 env->SetObjectField(initData, classDef->m_text, inputText.get()); 3398 env->SetIntField(initData, classDef->m_type, getInputType(node)); 3399 env->SetBooleanField(initData, classDef->m_isSpellCheckEnabled, 3400 isSpellCheckEnabled(node)); 3401 Document* document = node->document(); 3402 PlatformKeyboardEvent tab(AKEYCODE_TAB, 0, 0, false, false, false, false); 3403 PassRefPtr<KeyboardEvent> tabEvent = 3404 KeyboardEvent::create(tab, document->defaultView()); 3405 env->SetBooleanField(initData, classDef->m_isTextFieldNext, 3406 isTextInput(document->nextFocusableNode(node, tabEvent.get()))); 3407 env->SetBooleanField(initData, classDef->m_isTextFieldPrev, 3408 isTextInput(document->previousFocusableNode(node, tabEvent.get()))); 3409 env->SetBooleanField(initData, classDef->m_isAutoCompleteEnabled, 3410 isAutoCompleteEnabled(node)); 3411 ScopedLocalRef<jstring> fieldName(env, 3412 wtfStringToJstring(env, getFieldName(node), false)); 3413 env->SetObjectField(initData, classDef->m_name, fieldName.get()); 3414 ScopedLocalRef<jstring> label(env, 3415 wtfStringToJstring(env, requestLabel(document->frame(), node), false)); 3416 env->SetObjectField(initData, classDef->m_label, label.get()); 3417 env->SetIntField(initData, classDef->m_maxLength, getMaxLength(node)); 3418 LayerAndroid* layer = 0; 3419 int layerId = platformLayerIdFromNode(node, &layer); 3420 IntRect bounds = absoluteContentRect(node, layer); 3421 ScopedLocalRef<jobject> jbounds(env, intRectToRect(env, bounds)); 3422 env->SetObjectField(initData, classDef->m_contentBounds, jbounds.get()); 3423 env->SetIntField(initData, classDef->m_nodeLayerId, layerId); 3424 IntRect contentRect; 3425 RenderTextControl* rtc = toRenderTextControl(node); 3426 if (rtc) { 3427 contentRect.setWidth(rtc->scrollWidth()); 3428 contentRect.setHeight(rtc->scrollHeight()); 3429 contentRect.move(-rtc->scrollLeft(), -rtc->scrollTop()); 3430 } 3431 ScopedLocalRef<jobject> jcontentRect(env, intRectToRect(env, contentRect)); 3432 env->SetObjectField(initData, classDef->m_contentRect, jcontentRect.get()); 3433 return initData; 3434 } 3435 3436 void WebViewCore::initEditField(Node* node) 3437 { 3438 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3439 AutoJObject javaObject = m_javaGlue->object(env); 3440 if (!javaObject.get()) 3441 return; 3442 m_textGeneration = 0; 3443 int start = 0; 3444 int end = 0; 3445 getSelectionOffsets(node, start, end); 3446 SelectText* selectText = createSelectText(focusedFrame()->selection()->selection()); 3447 ScopedLocalRef<jobject> initData(env, createTextFieldInitData(node)); 3448 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_initEditField, 3449 start, end, reinterpret_cast<int>(selectText), initData.get()); 3450 checkException(env); 3451 } 3452 3453 void WebViewCore::popupReply(int index) 3454 { 3455 if (m_popupReply) { 3456 m_popupReply->replyInt(index); 3457 Release(m_popupReply); 3458 m_popupReply = 0; 3459 } 3460 } 3461 3462 void WebViewCore::popupReply(const int* array, int count) 3463 { 3464 if (m_popupReply) { 3465 m_popupReply->replyIntArray(array, count); 3466 Release(m_popupReply); 3467 m_popupReply = 0; 3468 } 3469 } 3470 3471 // This is a slightly modified Node::nextNodeConsideringAtomicNodes() with the 3472 // extra constraint of limiting the search to inside a containing parent 3473 WebCore::Node* nextNodeWithinParent(WebCore::Node* parent, WebCore::Node* start) 3474 { 3475 if (!isAtomicNode(start) && start->firstChild()) 3476 return start->firstChild(); 3477 if (start->nextSibling()) 3478 return start->nextSibling(); 3479 const Node *n = start; 3480 while (n && !n->nextSibling()) { 3481 n = n->parentNode(); 3482 if (n == parent) 3483 return 0; 3484 } 3485 if (n) 3486 return n->nextSibling(); 3487 return 0; 3488 } 3489 3490 void WebViewCore::initializeTextInput(WebCore::Node* node, bool fake) 3491 { 3492 if (node) { 3493 if (isTextInput(node)) { 3494 bool showKeyboard = true; 3495 initEditField(node); 3496 WebCore::RenderTextControl* rtc = toRenderTextControl(node); 3497 if (rtc && node->hasTagName(HTMLNames::inputTag)) { 3498 HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(node); 3499 bool ime = !shouldSuppressKeyboard(node) && !inputElement->readOnly(); 3500 if (ime) { 3501 #if ENABLE(WEB_AUTOFILL) 3502 if (rtc->isTextField()) { 3503 Page* page = node->document()->page(); 3504 EditorClient* editorClient = page->editorClient(); 3505 EditorClientAndroid* androidEditor = 3506 static_cast<EditorClientAndroid*>(editorClient); 3507 WebAutofill* autoFill = androidEditor->getAutofill(); 3508 autoFill->formFieldFocused(inputElement); 3509 } 3510 #endif 3511 } else 3512 showKeyboard = false; 3513 } 3514 if (!fake) 3515 requestKeyboard(showKeyboard); 3516 } else if (!fake && !nodeIsPlugin(node)) { 3517 // not a text entry field, put away the keyboard. 3518 clearTextEntry(); 3519 } 3520 } else if (!fake) { 3521 // There is no focusNode, so the keyboard is not needed. 3522 clearTextEntry(); 3523 } 3524 } 3525 3526 void WebViewCore::focusNodeChanged(WebCore::Node* newFocus) 3527 { 3528 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3529 AutoJObject javaObject = m_javaGlue->object(env); 3530 if (!javaObject.get()) 3531 return; 3532 if (isTextInput(newFocus)) 3533 initializeTextInput(newFocus, true); 3534 HitTestResult focusHitResult; 3535 focusHitResult.setInnerNode(newFocus); 3536 focusHitResult.setInnerNonSharedNode(newFocus); 3537 if (newFocus && newFocus->isLink() && newFocus->isElementNode()) { 3538 focusHitResult.setURLElement(static_cast<Element*>(newFocus)); 3539 if (newFocus->hasChildNodes() && !newFocus->hasTagName(HTMLNames::imgTag)) { 3540 // Check to see if any of the children are images, and if so 3541 // set them as the innerNode and innerNonSharedNode 3542 // This will stop when it hits the first image. I'm not sure what 3543 // should be done in the case of multiple images inside one anchor... 3544 Node* nextNode = newFocus->firstChild(); 3545 bool found = false; 3546 while (nextNode) { 3547 if (nextNode->hasTagName(HTMLNames::imgTag)) { 3548 found = true; 3549 break; 3550 } 3551 nextNode = nextNodeWithinParent(newFocus, nextNode); 3552 } 3553 if (found) { 3554 focusHitResult.setInnerNode(nextNode); 3555 focusHitResult.setInnerNonSharedNode(nextNode); 3556 } 3557 } 3558 } 3559 AndroidHitTestResult androidHitTest(this, focusHitResult); 3560 jobject jHitTestObj = androidHitTest.createJavaObject(env); 3561 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_focusNodeChanged, 3562 reinterpret_cast<int>(newFocus), jHitTestObj); 3563 env->DeleteLocalRef(jHitTestObj); 3564 } 3565 3566 void WebViewCore::addMessageToConsole(const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceID, int msgLevel) { 3567 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3568 AutoJObject javaObject = m_javaGlue->object(env); 3569 if (!javaObject.get()) 3570 return; 3571 jstring jMessageStr = wtfStringToJstring(env, message); 3572 jstring jSourceIDStr = wtfStringToJstring(env, sourceID); 3573 env->CallVoidMethod(javaObject.get(), 3574 m_javaGlue->m_addMessageToConsole, jMessageStr, lineNumber, 3575 jSourceIDStr, msgLevel); 3576 env->DeleteLocalRef(jMessageStr); 3577 env->DeleteLocalRef(jSourceIDStr); 3578 checkException(env); 3579 } 3580 3581 void WebViewCore::jsAlert(const WTF::String& url, const WTF::String& text) 3582 { 3583 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3584 AutoJObject javaObject = m_javaGlue->object(env); 3585 if (!javaObject.get()) 3586 return; 3587 jstring jInputStr = wtfStringToJstring(env, text); 3588 jstring jUrlStr = wtfStringToJstring(env, url); 3589 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_jsAlert, jUrlStr, jInputStr); 3590 env->DeleteLocalRef(jInputStr); 3591 env->DeleteLocalRef(jUrlStr); 3592 checkException(env); 3593 } 3594 3595 bool WebViewCore::exceededDatabaseQuota(const WTF::String& url, const WTF::String& databaseIdentifier, const unsigned long long currentQuota, unsigned long long estimatedSize) 3596 { 3597 #if ENABLE(DATABASE) 3598 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3599 AutoJObject javaObject = m_javaGlue->object(env); 3600 if (!javaObject.get()) 3601 return false; 3602 jstring jDatabaseIdentifierStr = wtfStringToJstring(env, databaseIdentifier); 3603 jstring jUrlStr = wtfStringToJstring(env, url); 3604 env->CallVoidMethod(javaObject.get(), 3605 m_javaGlue->m_exceededDatabaseQuota, jUrlStr, 3606 jDatabaseIdentifierStr, currentQuota, estimatedSize); 3607 env->DeleteLocalRef(jDatabaseIdentifierStr); 3608 env->DeleteLocalRef(jUrlStr); 3609 checkException(env); 3610 return true; 3611 #endif 3612 } 3613 3614 bool WebViewCore::reachedMaxAppCacheSize(const unsigned long long spaceNeeded) 3615 { 3616 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 3617 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3618 AutoJObject javaObject = m_javaGlue->object(env); 3619 if (!javaObject.get()) 3620 return false; 3621 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_reachedMaxAppCacheSize, spaceNeeded); 3622 checkException(env); 3623 return true; 3624 #endif 3625 } 3626 3627 void WebViewCore::populateVisitedLinks(WebCore::PageGroup* group) 3628 { 3629 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3630 AutoJObject javaObject = m_javaGlue->object(env); 3631 if (!javaObject.get()) 3632 return; 3633 m_groupForVisitedLinks = group; 3634 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_populateVisitedLinks); 3635 checkException(env); 3636 } 3637 3638 void WebViewCore::geolocationPermissionsShowPrompt(const WTF::String& origin) 3639 { 3640 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3641 AutoJObject javaObject = m_javaGlue->object(env); 3642 if (!javaObject.get()) 3643 return; 3644 jstring originString = wtfStringToJstring(env, origin); 3645 env->CallVoidMethod(javaObject.get(), 3646 m_javaGlue->m_geolocationPermissionsShowPrompt, 3647 originString); 3648 env->DeleteLocalRef(originString); 3649 checkException(env); 3650 } 3651 3652 void WebViewCore::geolocationPermissionsHidePrompt() 3653 { 3654 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3655 AutoJObject javaObject = m_javaGlue->object(env); 3656 if (!javaObject.get()) 3657 return; 3658 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_geolocationPermissionsHidePrompt); 3659 checkException(env); 3660 } 3661 3662 jobject WebViewCore::getDeviceMotionService() 3663 { 3664 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3665 AutoJObject javaObject = m_javaGlue->object(env); 3666 if (!javaObject.get()) 3667 return 0; 3668 jobject object = env->CallObjectMethod(javaObject.get(), m_javaGlue->m_getDeviceMotionService); 3669 checkException(env); 3670 return object; 3671 } 3672 3673 jobject WebViewCore::getDeviceOrientationService() 3674 { 3675 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3676 AutoJObject javaObject = m_javaGlue->object(env); 3677 if (!javaObject.get()) 3678 return 0; 3679 jobject object = env->CallObjectMethod(javaObject.get(), m_javaGlue->m_getDeviceOrientationService); 3680 checkException(env); 3681 return object; 3682 } 3683 3684 bool WebViewCore::jsConfirm(const WTF::String& url, const WTF::String& text) 3685 { 3686 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3687 AutoJObject javaObject = m_javaGlue->object(env); 3688 if (!javaObject.get()) 3689 return false; 3690 jstring jInputStr = wtfStringToJstring(env, text); 3691 jstring jUrlStr = wtfStringToJstring(env, url); 3692 jboolean result = env->CallBooleanMethod(javaObject.get(), m_javaGlue->m_jsConfirm, jUrlStr, jInputStr); 3693 env->DeleteLocalRef(jInputStr); 3694 env->DeleteLocalRef(jUrlStr); 3695 checkException(env); 3696 return result; 3697 } 3698 3699 bool WebViewCore::jsPrompt(const WTF::String& url, const WTF::String& text, const WTF::String& defaultValue, WTF::String& result) 3700 { 3701 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3702 AutoJObject javaObject = m_javaGlue->object(env); 3703 if (!javaObject.get()) 3704 return false; 3705 jstring jUrlStr = wtfStringToJstring(env, url); 3706 jstring jInputStr = wtfStringToJstring(env, text); 3707 jstring jDefaultStr = wtfStringToJstring(env, defaultValue); 3708 jstring returnVal = static_cast<jstring>(env->CallObjectMethod(javaObject.get(), m_javaGlue->m_jsPrompt, jUrlStr, jInputStr, jDefaultStr)); 3709 env->DeleteLocalRef(jUrlStr); 3710 env->DeleteLocalRef(jInputStr); 3711 env->DeleteLocalRef(jDefaultStr); 3712 checkException(env); 3713 3714 // If returnVal is null, it means that the user cancelled the dialog. 3715 if (!returnVal) 3716 return false; 3717 3718 result = jstringToWtfString(env, returnVal); 3719 env->DeleteLocalRef(returnVal); 3720 return true; 3721 } 3722 3723 bool WebViewCore::jsUnload(const WTF::String& url, const WTF::String& message) 3724 { 3725 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3726 AutoJObject javaObject = m_javaGlue->object(env); 3727 if (!javaObject.get()) 3728 return false; 3729 jstring jInputStr = wtfStringToJstring(env, message); 3730 jstring jUrlStr = wtfStringToJstring(env, url); 3731 jboolean result = env->CallBooleanMethod(javaObject.get(), m_javaGlue->m_jsUnload, jUrlStr, jInputStr); 3732 env->DeleteLocalRef(jInputStr); 3733 env->DeleteLocalRef(jUrlStr); 3734 checkException(env); 3735 return result; 3736 } 3737 3738 bool WebViewCore::jsInterrupt() 3739 { 3740 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3741 AutoJObject javaObject = m_javaGlue->object(env); 3742 if (!javaObject.get()) 3743 return false; 3744 jboolean result = env->CallBooleanMethod(javaObject.get(), m_javaGlue->m_jsInterrupt); 3745 checkException(env); 3746 return result; 3747 } 3748 3749 AutoJObject 3750 WebViewCore::getJavaObject() 3751 { 3752 return m_javaGlue->object(JSC::Bindings::getJNIEnv()); 3753 } 3754 3755 jobject 3756 WebViewCore::getWebViewJavaObject() 3757 { 3758 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3759 AutoJObject javaObject = m_javaGlue->object(env); 3760 if (!javaObject.get()) 3761 return 0; 3762 return env->CallObjectMethod(javaObject.get(), m_javaGlue->m_getWebView); 3763 } 3764 3765 RenderTextControl* WebViewCore::toRenderTextControl(Node* node) 3766 { 3767 RenderTextControl* rtc = 0; 3768 RenderObject* renderer = node->renderer(); 3769 if (renderer && renderer->isTextControl()) { 3770 rtc = WebCore::toRenderTextControl(renderer); 3771 } 3772 return rtc; 3773 } 3774 3775 void WebViewCore::getSelectionOffsets(Node* node, int& start, int& end) 3776 { 3777 RenderTextControl* rtc = toRenderTextControl(node); 3778 if (rtc) { 3779 start = rtc->selectionStart(); 3780 end = rtc->selectionEnd(); 3781 } else { 3782 // It must be content editable field. 3783 Document* document = node->document(); 3784 Frame* frame = document->frame(); 3785 SelectionController* selector = frame->selection(); 3786 Position selectionStart = selector->start(); 3787 Position selectionEnd = selector->end(); 3788 Position startOfNode = firstPositionInNode(node); 3789 RefPtr<Range> startRange = Range::create(document, startOfNode, 3790 selectionStart); 3791 start = TextIterator::rangeLength(startRange.get(), true); 3792 RefPtr<Range> endRange = Range::create(document, startOfNode, 3793 selectionEnd); 3794 end = TextIterator::rangeLength(endRange.get(), true); 3795 } 3796 } 3797 3798 String WebViewCore::getInputText(Node* node) 3799 { 3800 String text; 3801 WebCore::RenderTextControl* renderText = toRenderTextControl(node); 3802 if (renderText) 3803 text = renderText->text(); 3804 else { 3805 // It must be content editable field. 3806 Position start = firstPositionInNode(node); 3807 Position end = lastPositionInNode(node); 3808 VisibleSelection allEditableText(start, end); 3809 if (allEditableText.isRange()) 3810 text = allEditableText.firstRange()->text(); 3811 } 3812 return text; 3813 } 3814 3815 void WebViewCore::updateTextSelection() 3816 { 3817 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3818 AutoJObject javaObject = m_javaGlue->object(env); 3819 if (!javaObject.get()) 3820 return; 3821 VisibleSelection selection = focusedFrame()->selection()->selection(); 3822 int start = 0; 3823 int end = 0; 3824 if (selection.isCaretOrRange()) 3825 getSelectionOffsets(selection.start().anchorNode(), start, end); 3826 SelectText* selectText = createSelectText(selection); 3827 env->CallVoidMethod(javaObject.get(), 3828 m_javaGlue->m_updateTextSelection, reinterpret_cast<int>(currentFocus()), 3829 start, end, m_textGeneration, reinterpret_cast<int>(selectText)); 3830 checkException(env); 3831 } 3832 3833 void WebViewCore::updateTextSizeAndScroll(WebCore::Node* node) 3834 { 3835 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3836 AutoJObject javaObject = m_javaGlue->object(env); 3837 if (!javaObject.get()) 3838 return; 3839 RenderTextControl* rtc = toRenderTextControl(node); 3840 if (!rtc) 3841 return; 3842 int width = rtc->scrollWidth(); 3843 int height = rtc->contentHeight(); 3844 int scrollX = rtc->scrollLeft(); 3845 int scrollY = rtc->scrollTop(); 3846 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_updateTextSizeAndScroll, 3847 reinterpret_cast<int>(node), width, height, scrollX, scrollY); 3848 checkException(env); 3849 } 3850 3851 void WebViewCore::updateTextfield(WebCore::Node* ptr, bool changeToPassword, 3852 const WTF::String& text) 3853 { 3854 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3855 AutoJObject javaObject = m_javaGlue->object(env); 3856 if (!javaObject.get()) 3857 return; 3858 if (m_blockTextfieldUpdates) 3859 return; 3860 if (changeToPassword) { 3861 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_updateTextfield, 3862 (int) ptr, true, 0, m_textGeneration); 3863 checkException(env); 3864 return; 3865 } 3866 jstring string = wtfStringToJstring(env, text); 3867 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_updateTextfield, 3868 (int) ptr, false, string, m_textGeneration); 3869 env->DeleteLocalRef(string); 3870 checkException(env); 3871 } 3872 3873 void WebViewCore::clearTextEntry() 3874 { 3875 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3876 AutoJObject javaObject = m_javaGlue->object(env); 3877 if (!javaObject.get()) 3878 return; 3879 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_clearTextEntry); 3880 } 3881 3882 void WebViewCore::setBackgroundColor(SkColor c) 3883 { 3884 WebCore::FrameView* view = m_mainFrame->view(); 3885 if (!view) 3886 return; 3887 3888 // need (int) cast to find the right constructor 3889 WebCore::Color bcolor((int)SkColorGetR(c), (int)SkColorGetG(c), 3890 (int)SkColorGetB(c), (int)SkColorGetA(c)); 3891 3892 if (view->baseBackgroundColor() == bcolor) 3893 return; 3894 3895 view->setBaseBackgroundColor(bcolor); 3896 3897 // Background color of 0 indicates we want a transparent background 3898 if (c == 0) 3899 view->setTransparent(true); 3900 3901 //invalidate so the new color is shown 3902 contentInvalidateAll(); 3903 } 3904 3905 jclass WebViewCore::getPluginClass(const WTF::String& libName, const char* className) 3906 { 3907 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3908 AutoJObject javaObject = m_javaGlue->object(env); 3909 if (!javaObject.get()) 3910 return 0; 3911 3912 jstring libString = wtfStringToJstring(env, libName); 3913 jstring classString = env->NewStringUTF(className); 3914 jobject pluginClass = env->CallObjectMethod(javaObject.get(), 3915 m_javaGlue->m_getPluginClass, 3916 libString, classString); 3917 checkException(env); 3918 3919 // cleanup unneeded local JNI references 3920 env->DeleteLocalRef(libString); 3921 env->DeleteLocalRef(classString); 3922 3923 if (pluginClass != 0) { 3924 return static_cast<jclass>(pluginClass); 3925 } else { 3926 return 0; 3927 } 3928 } 3929 3930 void WebViewCore::showFullScreenPlugin(jobject childView, int32_t orientation, NPP npp) 3931 { 3932 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3933 AutoJObject javaObject = m_javaGlue->object(env); 3934 if (!javaObject.get()) 3935 return; 3936 3937 env->CallVoidMethod(javaObject.get(), 3938 m_javaGlue->m_showFullScreenPlugin, 3939 childView, orientation, reinterpret_cast<int>(npp)); 3940 checkException(env); 3941 } 3942 3943 void WebViewCore::hideFullScreenPlugin() 3944 { 3945 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3946 AutoJObject javaObject = m_javaGlue->object(env); 3947 if (!javaObject.get()) 3948 return; 3949 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_hideFullScreenPlugin); 3950 checkException(env); 3951 } 3952 3953 jobject WebViewCore::createSurface(jobject view) 3954 { 3955 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3956 AutoJObject javaObject = m_javaGlue->object(env); 3957 if (!javaObject.get()) 3958 return 0; 3959 jobject result = env->CallObjectMethod(javaObject.get(), m_javaGlue->m_createSurface, view); 3960 checkException(env); 3961 return result; 3962 } 3963 3964 jobject WebViewCore::addSurface(jobject view, int x, int y, int width, int height) 3965 { 3966 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3967 AutoJObject javaObject = m_javaGlue->object(env); 3968 if (!javaObject.get()) 3969 return 0; 3970 jobject result = env->CallObjectMethod(javaObject.get(), 3971 m_javaGlue->m_addSurface, 3972 view, x, y, width, height); 3973 checkException(env); 3974 return result; 3975 } 3976 3977 void WebViewCore::updateSurface(jobject childView, int x, int y, int width, int height) 3978 { 3979 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3980 AutoJObject javaObject = m_javaGlue->object(env); 3981 if (!javaObject.get()) 3982 return; 3983 env->CallVoidMethod(javaObject.get(), 3984 m_javaGlue->m_updateSurface, childView, 3985 x, y, width, height); 3986 checkException(env); 3987 } 3988 3989 void WebViewCore::destroySurface(jobject childView) 3990 { 3991 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3992 AutoJObject javaObject = m_javaGlue->object(env); 3993 if (!javaObject.get()) 3994 return; 3995 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_destroySurface, childView); 3996 checkException(env); 3997 } 3998 3999 jobject WebViewCore::getContext() 4000 { 4001 JNIEnv* env = JSC::Bindings::getJNIEnv(); 4002 AutoJObject javaObject = m_javaGlue->object(env); 4003 if (!javaObject.get()) 4004 return 0; 4005 4006 jobject result = env->CallObjectMethod(javaObject.get(), m_javaGlue->m_getContext); 4007 checkException(env); 4008 return result; 4009 } 4010 4011 void WebViewCore::keepScreenOn(bool screenOn) { 4012 if ((screenOn && m_screenOnCounter == 0) || (!screenOn && m_screenOnCounter == 1)) { 4013 JNIEnv* env = JSC::Bindings::getJNIEnv(); 4014 AutoJObject javaObject = m_javaGlue->object(env); 4015 if (!javaObject.get()) 4016 return; 4017 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_keepScreenOn, screenOn); 4018 checkException(env); 4019 } 4020 4021 // update the counter 4022 if (screenOn) 4023 m_screenOnCounter++; 4024 else if (m_screenOnCounter > 0) 4025 m_screenOnCounter--; 4026 } 4027 4028 void WebViewCore::showRect(int left, int top, int width, int height, 4029 int contentWidth, int contentHeight, float xPercentInDoc, 4030 float xPercentInView, float yPercentInDoc, float yPercentInView) 4031 { 4032 JNIEnv* env = JSC::Bindings::getJNIEnv(); 4033 AutoJObject javaObject = m_javaGlue->object(env); 4034 if (!javaObject.get()) 4035 return; 4036 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_showRect, 4037 left, top, width, height, contentWidth, contentHeight, 4038 xPercentInDoc, xPercentInView, yPercentInDoc, yPercentInView); 4039 checkException(env); 4040 } 4041 4042 void WebViewCore::centerFitRect(int x, int y, int width, int height) 4043 { 4044 JNIEnv* env = JSC::Bindings::getJNIEnv(); 4045 AutoJObject javaObject = m_javaGlue->object(env); 4046 if (!javaObject.get()) 4047 return; 4048 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_centerFitRect, x, y, width, height); 4049 checkException(env); 4050 } 4051 4052 void WebViewCore::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode) 4053 { 4054 JNIEnv* env = JSC::Bindings::getJNIEnv(); 4055 AutoJObject javaObject = m_javaGlue->object(env); 4056 if (!javaObject.get()) 4057 return; 4058 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_setScrollbarModes, horizontalMode, verticalMode); 4059 checkException(env); 4060 } 4061 4062 void WebViewCore::notifyWebAppCanBeInstalled() 4063 { 4064 JNIEnv* env = JSC::Bindings::getJNIEnv(); 4065 AutoJObject javaObject = m_javaGlue->object(env); 4066 if (!javaObject.get()) 4067 return; 4068 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_setInstallableWebApp); 4069 checkException(env); 4070 } 4071 4072 #if ENABLE(VIDEO) 4073 void WebViewCore::enterFullscreenForVideoLayer(int layerId, const WTF::String& url) 4074 { 4075 JNIEnv* env = JSC::Bindings::getJNIEnv(); 4076 AutoJObject javaObject = m_javaGlue->object(env); 4077 if (!javaObject.get()) 4078 return; 4079 jstring jUrlStr = wtfStringToJstring(env, url); 4080 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_enterFullscreenForVideoLayer, layerId, jUrlStr); 4081 m_fullscreenVideoMode = true; 4082 checkException(env); 4083 } 4084 4085 void WebViewCore::exitFullscreenVideo() 4086 { 4087 JNIEnv* env = JSC::Bindings::getJNIEnv(); 4088 AutoJObject javaObject = m_javaGlue->object(env); 4089 if (!javaObject.get()) 4090 return; 4091 if (m_fullscreenVideoMode) { 4092 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_exitFullscreenVideo); 4093 m_fullscreenVideoMode = false; 4094 } 4095 checkException(env); 4096 } 4097 #endif 4098 4099 void WebViewCore::setWebTextViewAutoFillable(int queryId, const string16& previewSummary) 4100 { 4101 #if ENABLE(WEB_AUTOFILL) 4102 JNIEnv* env = JSC::Bindings::getJNIEnv(); 4103 AutoJObject javaObject = m_javaGlue->object(env); 4104 if (!javaObject.get()) 4105 return; 4106 jstring preview = env->NewString(previewSummary.data(), previewSummary.length()); 4107 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_setWebTextViewAutoFillable, queryId, preview); 4108 env->DeleteLocalRef(preview); 4109 #endif 4110 } 4111 4112 bool WebViewCore::drawIsPaused() const 4113 { 4114 // returning true says scrollview should be offscreen, which pauses 4115 // gifs. because this is not again queried when we stop scrolling, we don't 4116 // use the stopping currently. 4117 return false; 4118 } 4119 4120 void WebViewCore::setWebRequestContextUserAgent() 4121 { 4122 // We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet 4123 if (m_webRequestContext) 4124 m_webRequestContext->setUserAgent(WebFrame::getWebFrame(m_mainFrame)->userAgentForURL(0)); // URL not used 4125 } 4126 4127 void WebViewCore::setWebRequestContextCacheMode(int cacheMode) 4128 { 4129 m_cacheMode = cacheMode; 4130 // We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet 4131 if (!m_webRequestContext) 4132 return; 4133 4134 m_webRequestContext->setCacheMode(cacheMode); 4135 } 4136 4137 WebRequestContext* WebViewCore::webRequestContext() 4138 { 4139 if (!m_webRequestContext) { 4140 Settings* settings = mainFrame()->settings(); 4141 m_webRequestContext = new WebRequestContext(settings && settings->privateBrowsingEnabled()); 4142 setWebRequestContextUserAgent(); 4143 setWebRequestContextCacheMode(m_cacheMode); 4144 } 4145 return m_webRequestContext.get(); 4146 } 4147 4148 void WebViewCore::scrollRenderLayer(int layer, const SkRect& rect) 4149 { 4150 #if USE(ACCELERATED_COMPOSITING) 4151 GraphicsLayerAndroid* root = graphicsRootLayer(); 4152 if (!root) 4153 return; 4154 4155 LayerAndroid* layerAndroid = root->platformLayer(); 4156 if (!layerAndroid) 4157 return; 4158 4159 LayerAndroid* target = layerAndroid->findById(layer); 4160 if (!target) 4161 return; 4162 4163 RenderLayer* owner = target->owningLayer(); 4164 if (!owner) 4165 return; 4166 4167 if (owner->isRootLayer()) { 4168 FrameView* view = owner->renderer()->frame()->view(); 4169 IntPoint pt(rect.fLeft, rect.fTop); 4170 view->setScrollPosition(pt); 4171 } else 4172 owner->scrollToOffset(rect.fLeft, rect.fTop); 4173 #endif 4174 } 4175 4176 Vector<VisibleSelection> WebViewCore::getTextRanges( 4177 int startX, int startY, int endX, int endY) 4178 { 4179 // These are the positions of the selection handles, 4180 // which reside below the line that they are selecting. 4181 // Use the vertical position higher, which will include 4182 // the selected text. 4183 startY--; 4184 endY--; 4185 VisiblePosition startSelect = visiblePositionForContentPoint(startX, startY); 4186 VisiblePosition endSelect = visiblePositionForContentPoint(endX, endY); 4187 Position start = startSelect.deepEquivalent(); 4188 Position end = endSelect.deepEquivalent(); 4189 Vector<VisibleSelection> ranges; 4190 if (!start.isNull() && !end.isNull()) { 4191 if (comparePositions(start, end) > 0) { 4192 swap(start, end); // RTL start/end positions may be swapped 4193 } 4194 Position nextRangeStart = start; 4195 Position previousRangeEnd; 4196 do { 4197 VisibleSelection selection(nextRangeStart, end); 4198 ranges.append(selection); 4199 previousRangeEnd = selection.end(); 4200 nextRangeStart = nextCandidate(previousRangeEnd); 4201 } while (comparePositions(previousRangeEnd, end) < 0); 4202 } 4203 return ranges; 4204 } 4205 4206 void WebViewCore::deleteText(int startX, int startY, int endX, int endY) 4207 { 4208 Vector<VisibleSelection> ranges = 4209 getTextRanges(startX, startY, endX, endY); 4210 4211 EditorClientAndroid* client = static_cast<EditorClientAndroid*>( 4212 m_mainFrame->editor()->client()); 4213 client->setUiGeneratedSelectionChange(true); 4214 4215 SelectionController* selector = m_mainFrame->selection(); 4216 for (size_t i = 0; i < ranges.size(); i++) { 4217 const VisibleSelection& selection = ranges[i]; 4218 if (selection.isContentEditable()) { 4219 selector->setSelection(selection, CharacterGranularity); 4220 Document* document = selection.start().anchorNode()->document(); 4221 WebCore::TypingCommand::deleteSelection(document, 0); 4222 } 4223 } 4224 client->setUiGeneratedSelectionChange(false); 4225 } 4226 4227 void WebViewCore::insertText(const WTF::String &text) 4228 { 4229 WebCore::Node* focus = currentFocus(); 4230 if (!focus || !isTextInput(focus)) 4231 return; 4232 4233 Document* document = focus->document(); 4234 4235 EditorClientAndroid* client = static_cast<EditorClientAndroid*>( 4236 m_mainFrame->editor()->client()); 4237 if (!client) 4238 return; 4239 client->setUiGeneratedSelectionChange(true); 4240 WebCore::TypingCommand::insertText(document, text, 4241 TypingCommand::PreventSpellChecking); 4242 client->setUiGeneratedSelectionChange(false); 4243 } 4244 4245 void WebViewCore::resetFindOnPage() 4246 { 4247 m_searchText.truncate(0); 4248 m_matchCount = 0; 4249 m_activeMatchIndex = 0; 4250 m_activeMatch = 0; 4251 } 4252 4253 int WebViewCore::findTextOnPage(const WTF::String &text) 4254 { 4255 resetFindOnPage(); // reset even if parameters are bad 4256 4257 WebCore::Frame* frame = m_mainFrame; 4258 if (!frame) 4259 return 0; 4260 4261 m_searchText = text; 4262 FindOptions findOptions = WebCore::CaseInsensitive; 4263 4264 do { 4265 frame->document()->markers()->removeMarkers(DocumentMarker::TextMatch); 4266 m_matchCount += frame->editor()->countMatchesForText(text, findOptions, 4267 0, true); 4268 frame->editor()->setMarkedTextMatchesAreHighlighted(true); 4269 frame = frame->tree()->traverseNextWithWrap(false); 4270 } while (frame); 4271 m_activeMatchIndex = m_matchCount - 1; // prime first findNext 4272 return m_matchCount; 4273 } 4274 4275 int WebViewCore::findNextOnPage(bool forward) 4276 { 4277 if (!m_mainFrame) 4278 return -1; 4279 if (!m_matchCount) 4280 return -1; 4281 4282 EditorClientAndroid* client = static_cast<EditorClientAndroid*>( 4283 m_mainFrame->editor()->client()); 4284 client->setUiGeneratedSelectionChange(true); 4285 4286 // Clear previous active match. 4287 if (m_activeMatch) { 4288 m_mainFrame->document()->markers()->setMarkersActive( 4289 m_activeMatch.get(), false); 4290 } 4291 4292 FindOptions findOptions = WebCore::CaseInsensitive 4293 | WebCore::StartInSelection | WebCore::WrapAround; 4294 if (!forward) 4295 findOptions |= WebCore::Backwards; 4296 4297 // Start from the previous active match. 4298 if (m_activeMatch) { 4299 m_mainFrame->selection()->setSelection(m_activeMatch.get()); 4300 } 4301 4302 bool found = m_mainFrame->editor()->findString(m_searchText, findOptions); 4303 if (found) { 4304 VisibleSelection selection(m_mainFrame->selection()->selection()); 4305 if (selection.isNone() || selection.start() == selection.end()) { 4306 // Temporary workaround for findString() refusing to select text 4307 // marked "-webkit-user-select: none". 4308 m_activeMatchIndex = 0; 4309 m_activeMatch = 0; 4310 } else { 4311 // Mark current match "active". 4312 if (forward) { 4313 ++m_activeMatchIndex; 4314 if (m_activeMatchIndex == m_matchCount) 4315 m_activeMatchIndex = 0; 4316 } else { 4317 if (m_activeMatchIndex == 0) 4318 m_activeMatchIndex = m_matchCount; 4319 --m_activeMatchIndex; 4320 } 4321 m_activeMatch = selection.firstRange(); 4322 m_mainFrame->document()->markers()->setMarkersActive( 4323 m_activeMatch.get(), true); 4324 m_mainFrame->selection()->revealSelection( 4325 ScrollAlignment::alignCenterIfNeeded, true); 4326 } 4327 } 4328 4329 // Clear selection so it doesn't display. 4330 m_mainFrame->selection()->clear(); 4331 client->setUiGeneratedSelectionChange(false); 4332 return m_activeMatchIndex; 4333 } 4334 4335 String WebViewCore::getText(int startX, int startY, int endX, int endY) 4336 { 4337 String text; 4338 4339 Vector<VisibleSelection> ranges = 4340 getTextRanges(startX, startY, endX, endY); 4341 4342 for (size_t i = 0; i < ranges.size(); i++) { 4343 const VisibleSelection& selection = ranges[i]; 4344 if (selection.isRange()) { 4345 PassRefPtr<Range> range = selection.firstRange(); 4346 String textInRange = range->text(); 4347 if (textInRange.length() > 0) { 4348 if (text.length() > 0) 4349 text.append('\n'); 4350 text.append(textInRange); 4351 } 4352 } 4353 } 4354 4355 return text; 4356 } 4357 4358 /** 4359 * Read the persistent locale. 4360 */ 4361 void WebViewCore::getLocale(String& language, String& region) 4362 { 4363 char propLang[PROPERTY_VALUE_MAX], propRegn[PROPERTY_VALUE_MAX]; 4364 4365 property_get("persist.sys.language", propLang, ""); 4366 property_get("persist.sys.country", propRegn, ""); 4367 if (*propLang == 0 && *propRegn == 0) { 4368 /* Set to ro properties, default is en_US */ 4369 property_get("ro.product.locale.language", propLang, "en"); 4370 property_get("ro.product.locale.region", propRegn, "US"); 4371 } 4372 language = String(propLang, 2); 4373 region = String(propRegn, 2); 4374 } 4375 4376 void WebViewCore::updateLocale() 4377 { 4378 static String prevLang; 4379 static String prevRegn; 4380 String language; 4381 String region; 4382 4383 getLocale(language, region); 4384 4385 if ((language != prevLang) || (region != prevRegn)) { 4386 prevLang = language; 4387 prevRegn = region; 4388 GlyphPageTreeNode::resetRoots(); 4389 fontCache()->invalidate(); 4390 } 4391 } 4392 4393 //---------------------------------------------------------------------- 4394 // Native JNI methods 4395 //---------------------------------------------------------------------- 4396 static void RevealSelection(JNIEnv* env, jobject obj, jint nativeClass) 4397 { 4398 reinterpret_cast<WebViewCore*>(nativeClass)->revealSelection(); 4399 } 4400 4401 static jstring RequestLabel(JNIEnv* env, jobject obj, jint nativeClass, 4402 int framePointer, int nodePointer) 4403 { 4404 WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass); 4405 return wtfStringToJstring(env, viewImpl->requestLabel( 4406 (WebCore::Frame*) framePointer, (WebCore::Node*) nodePointer)); 4407 } 4408 4409 static void ClearContent(JNIEnv* env, jobject obj, jint nativeClass) 4410 { 4411 WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass); 4412 viewImpl->clearContent(); 4413 } 4414 4415 static void SetSize(JNIEnv* env, jobject obj,