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 "Attribute.h" 33 #include "BaseLayerAndroid.h" 34 #include "CachedNode.h" 35 #include "CachedRoot.h" 36 #include "Chrome.h" 37 #include "ChromeClientAndroid.h" 38 #include "ChromiumIncludes.h" 39 #include "ClientRect.h" 40 #include "ClientRectList.h" 41 #include "Color.h" 42 #include "CSSPropertyNames.h" 43 #include "CSSValueKeywords.h" 44 #include "DatabaseTracker.h" 45 #include "Document.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 "Frame.h" 57 #include "FrameLoader.h" 58 #include "FrameLoaderClientAndroid.h" 59 #include "FrameTree.h" 60 #include "FrameView.h" 61 #include "Geolocation.h" 62 #include "GraphicsContext.h" 63 #include "GraphicsJNI.h" 64 #include "HTMLAnchorElement.h" 65 #include "HTMLAreaElement.h" 66 #include "HTMLElement.h" 67 #include "HTMLFormControlElement.h" 68 #include "HTMLImageElement.h" 69 #include "HTMLInputElement.h" 70 #include "HTMLLabelElement.h" 71 #include "HTMLMapElement.h" 72 #include "HTMLNames.h" 73 #include "HTMLOptGroupElement.h" 74 #include "HTMLOptionElement.h" 75 #include "HTMLSelectElement.h" 76 #include "HTMLTextAreaElement.h" 77 #include "HistoryItem.h" 78 #include "HitTestRequest.h" 79 #include "HitTestResult.h" 80 #include "InlineTextBox.h" 81 #include "MemoryUsage.h" 82 #include "NamedNodeMap.h" 83 #include "Navigator.h" 84 #include "Node.h" 85 #include "NodeList.h" 86 #include "Page.h" 87 #include "PageGroup.h" 88 #include "PlatformKeyboardEvent.h" 89 #include "PlatformString.h" 90 #include "PluginWidgetAndroid.h" 91 #include "PluginView.h" 92 #include "Position.h" 93 #include "ProgressTracker.h" 94 #include "Range.h" 95 #include "RenderBox.h" 96 #include "RenderInline.h" 97 #include "RenderLayer.h" 98 #include "RenderPart.h" 99 #include "RenderText.h" 100 #include "RenderTextControl.h" 101 #include "RenderThemeAndroid.h" 102 #include "RenderView.h" 103 #include "ResourceRequest.h" 104 #include "SchemeRegistry.h" 105 #include "SelectionController.h" 106 #include "Settings.h" 107 #include "SkANP.h" 108 #include "SkTemplates.h" 109 #include "SkTDArray.h" 110 #include "SkTypes.h" 111 #include "SkCanvas.h" 112 #include "SkPicture.h" 113 #include "SkUtils.h" 114 #include "Text.h" 115 #include "TypingCommand.h" 116 #include "WebCache.h" 117 #include "WebCoreFrameBridge.h" 118 #include "WebFrameView.h" 119 #include "WindowsKeyboardCodes.h" 120 #include "android_graphics.h" 121 #include "autofill/WebAutofill.h" 122 #include "htmlediting.h" 123 #include "markup.h" 124 125 #include <JNIHelp.h> 126 #include <JNIUtility.h> 127 #include <ui/KeycodeLabels.h> 128 #include <wtf/CurrentTime.h> 129 #include <wtf/text/AtomicString.h> 130 #include <wtf/text/StringImpl.h> 131 132 #if USE(V8) 133 #include "ScriptController.h" 134 #include "V8Counters.h" 135 #include <wtf/text/CString.h> 136 #endif 137 138 #if DEBUG_NAV_UI 139 #include "SkTime.h" 140 #endif 141 142 #if ENABLE(TOUCH_EVENTS) // Android 143 #include "PlatformTouchEvent.h" 144 #endif 145 146 #ifdef ANDROID_DOM_LOGGING 147 #include "AndroidLog.h" 148 #include "RenderTreeAsText.h" 149 #include <wtf/text/CString.h> 150 151 FILE* gDomTreeFile = 0; 152 FILE* gRenderTreeFile = 0; 153 #endif 154 155 #ifdef ANDROID_INSTRUMENT 156 #include "TimeCounter.h" 157 #endif 158 159 #if USE(ACCELERATED_COMPOSITING) 160 #include "GraphicsLayerAndroid.h" 161 #include "RenderLayerCompositor.h" 162 #endif 163 164 #if USE(V8) 165 #include <v8.h> 166 #endif 167 168 // In some cases, too many invalidations passed to the UI will slow us down. 169 // Limit ourselves to 32 rectangles, past this just send the area bounds to the UI. 170 // see WebViewCore::recordPictureSet(). 171 #define MAX_INVALIDATIONS 32 172 173 /* We pass this flag when recording the actual content, so that we don't spend 174 time actually regionizing complex path clips, when all we really want to do 175 is record them. 176 */ 177 #define PICT_RECORD_FLAGS SkPicture::kUsePathBoundsForClip_RecordingFlag 178 179 //////////////////////////////////////////////////////////////////////////////////////////////// 180 181 namespace android { 182 183 static SkTDArray<WebViewCore*> gInstanceList; 184 185 void WebViewCore::addInstance(WebViewCore* inst) { 186 *gInstanceList.append() = inst; 187 } 188 189 void WebViewCore::removeInstance(WebViewCore* inst) { 190 int index = gInstanceList.find(inst); 191 LOG_ASSERT(index >= 0, "RemoveInstance inst not found"); 192 if (index >= 0) { 193 gInstanceList.removeShuffle(index); 194 } 195 } 196 197 bool WebViewCore::isInstance(WebViewCore* inst) { 198 return gInstanceList.find(inst) >= 0; 199 } 200 201 jobject WebViewCore::getApplicationContext() { 202 203 // check to see if there is a valid webviewcore object 204 if (gInstanceList.isEmpty()) 205 return 0; 206 207 // get the context from the webview 208 jobject context = gInstanceList[0]->getContext(); 209 210 if (!context) 211 return 0; 212 213 // get the application context using JNI 214 JNIEnv* env = JSC::Bindings::getJNIEnv(); 215 jclass contextClass = env->GetObjectClass(context); 216 jmethodID appContextMethod = env->GetMethodID(contextClass, "getApplicationContext", "()Landroid/content/Context;"); 217 env->DeleteLocalRef(contextClass); 218 jobject result = env->CallObjectMethod(context, appContextMethod); 219 checkException(env); 220 return result; 221 } 222 223 224 struct WebViewCoreStaticMethods { 225 jmethodID m_isSupportedMediaMimeType; 226 } gWebViewCoreStaticMethods; 227 228 // Check whether a media mimeType is supported in Android media framework. 229 bool WebViewCore::isSupportedMediaMimeType(const WTF::String& mimeType) { 230 JNIEnv* env = JSC::Bindings::getJNIEnv(); 231 jstring jMimeType = wtfStringToJstring(env, mimeType); 232 jclass webViewCore = env->FindClass("android/webkit/WebViewCore"); 233 bool val = env->CallStaticBooleanMethod(webViewCore, 234 gWebViewCoreStaticMethods.m_isSupportedMediaMimeType, jMimeType); 235 checkException(env); 236 env->DeleteLocalRef(webViewCore); 237 env->DeleteLocalRef(jMimeType); 238 239 return val; 240 } 241 242 // ---------------------------------------------------------------------------- 243 244 #define GET_NATIVE_VIEW(env, obj) ((WebViewCore*)env->GetIntField(obj, gWebViewCoreFields.m_nativeClass)) 245 246 // Field ids for WebViewCore 247 struct WebViewCoreFields { 248 jfieldID m_nativeClass; 249 jfieldID m_viewportWidth; 250 jfieldID m_viewportHeight; 251 jfieldID m_viewportInitialScale; 252 jfieldID m_viewportMinimumScale; 253 jfieldID m_viewportMaximumScale; 254 jfieldID m_viewportUserScalable; 255 jfieldID m_viewportDensityDpi; 256 jfieldID m_webView; 257 jfieldID m_drawIsPaused; 258 jfieldID m_lowMemoryUsageMb; 259 jfieldID m_highMemoryUsageMb; 260 jfieldID m_highUsageDeltaMb; 261 } gWebViewCoreFields; 262 263 // ---------------------------------------------------------------------------- 264 265 struct WebViewCore::JavaGlue { 266 jweak m_obj; 267 jmethodID m_scrollTo; 268 jmethodID m_contentDraw; 269 jmethodID m_layersDraw; 270 jmethodID m_requestListBox; 271 jmethodID m_openFileChooser; 272 jmethodID m_requestSingleListBox; 273 jmethodID m_jsAlert; 274 jmethodID m_jsConfirm; 275 jmethodID m_jsPrompt; 276 jmethodID m_jsUnload; 277 jmethodID m_jsInterrupt; 278 jmethodID m_didFirstLayout; 279 jmethodID m_updateViewport; 280 jmethodID m_sendNotifyProgressFinished; 281 jmethodID m_sendViewInvalidate; 282 jmethodID m_updateTextfield; 283 jmethodID m_updateTextSelection; 284 jmethodID m_clearTextEntry; 285 jmethodID m_restoreScale; 286 jmethodID m_needTouchEvents; 287 jmethodID m_requestKeyboard; 288 jmethodID m_requestKeyboardWithSelection; 289 jmethodID m_exceededDatabaseQuota; 290 jmethodID m_reachedMaxAppCacheSize; 291 jmethodID m_populateVisitedLinks; 292 jmethodID m_geolocationPermissionsShowPrompt; 293 jmethodID m_geolocationPermissionsHidePrompt; 294 jmethodID m_getDeviceMotionService; 295 jmethodID m_getDeviceOrientationService; 296 jmethodID m_addMessageToConsole; 297 jmethodID m_formDidBlur; 298 jmethodID m_getPluginClass; 299 jmethodID m_showFullScreenPlugin; 300 jmethodID m_hideFullScreenPlugin; 301 jmethodID m_createSurface; 302 jmethodID m_addSurface; 303 jmethodID m_updateSurface; 304 jmethodID m_destroySurface; 305 jmethodID m_getContext; 306 jmethodID m_keepScreenOn; 307 jmethodID m_sendFindAgain; 308 jmethodID m_showRect; 309 jmethodID m_centerFitRect; 310 jmethodID m_setScrollbarModes; 311 jmethodID m_setInstallableWebApp; 312 jmethodID m_enterFullscreenForVideoLayer; 313 jmethodID m_setWebTextViewAutoFillable; 314 jmethodID m_selectAt; 315 AutoJObject object(JNIEnv* env) { 316 // We hold a weak reference to the Java WebViewCore to avoid memeory 317 // leaks due to circular references when WebView.destroy() is not 318 // called manually. The WebView and hence the WebViewCore could become 319 // weakly reachable at any time, after which the GC could null our weak 320 // reference, so we have to check the return value of this method at 321 // every use. Note that our weak reference will be nulled before the 322 // WebViewCore is finalized. 323 return getRealObject(env, m_obj); 324 } 325 }; 326 327 /* 328 * WebViewCore Implementation 329 */ 330 331 static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[]) 332 { 333 jmethodID m = env->GetMethodID(clazz, name, signature); 334 LOG_ASSERT(m, "Could not find method %s", name); 335 return m; 336 } 337 338 Mutex WebViewCore::gFrameCacheMutex; 339 Mutex WebViewCore::gButtonMutex; 340 Mutex WebViewCore::gCursorBoundsMutex; 341 342 WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe) 343 : m_frameCacheKit(0) 344 , m_navPictureKit(0) 345 , m_moveGeneration(0) 346 , m_touchGeneration(0) 347 , m_lastGeneration(0) 348 , m_updatedFrameCache(true) 349 , m_findIsUp(false) 350 , m_hasCursorBounds(false) 351 , m_cursorBounds(WebCore::IntRect(0, 0, 0, 0)) 352 , m_cursorHitBounds(WebCore::IntRect(0, 0, 0, 0)) 353 , m_cursorFrame(0) 354 , m_cursorLocation(WebCore::IntPoint(0, 0)) 355 , m_cursorNode(0) 356 , m_javaGlue(new JavaGlue) 357 , m_mainFrame(mainframe) 358 , m_popupReply(0) 359 , m_lastFocused(0) 360 , m_lastFocusedBounds(WebCore::IntRect(0,0,0,0)) 361 , m_blurringNodePointer(0) 362 , m_lastFocusedSelStart(0) 363 , m_lastFocusedSelEnd(0) 364 , m_blockTextfieldUpdates(false) 365 , m_focusBoundsChanged(false) 366 , m_skipContentDraw(false) 367 , m_textGeneration(0) 368 , m_temp(0) 369 , m_tempPict(0) 370 , m_maxXScroll(320/4) 371 , m_maxYScroll(240/4) 372 , m_scrollOffsetX(0) 373 , m_scrollOffsetY(0) 374 , m_mousePos(WebCore::IntPoint(0,0)) 375 , m_frameCacheOutOfDate(true) 376 , m_progressDone(false) 377 , m_screenWidth(320) 378 , m_screenHeight(240) 379 , m_textWrapWidth(320) 380 , m_scale(1.0f) 381 , m_domtree_version(0) 382 , m_check_domtree_version(true) 383 , m_groupForVisitedLinks(0) 384 , m_isPaused(false) 385 , m_cacheMode(0) 386 , m_shouldPaintCaret(true) 387 , m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired) 388 , m_screenOnCounter(0) 389 , m_currentNodeDomNavigationAxis(0) 390 , m_deviceMotionAndOrientationManager(this) 391 #if ENABLE(TOUCH_EVENTS) 392 , m_forwardingTouchEvents(false) 393 #endif 394 #if USE(CHROME_NETWORK_STACK) 395 , m_webRequestContext(0) 396 #endif 397 { 398 LOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!"); 399 400 jclass clazz = env->GetObjectClass(javaWebViewCore); 401 m_javaGlue->m_obj = env->NewWeakGlobalRef(javaWebViewCore); 402 m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(IIZZ)V"); 403 m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V"); 404 m_javaGlue->m_layersDraw = GetJMethod(env, clazz, "layersDraw", "()V"); 405 m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[I[I)V"); 406 m_javaGlue->m_openFileChooser = GetJMethod(env, clazz, "openFileChooser", "(Ljava/lang/String;)Ljava/lang/String;"); 407 m_javaGlue->m_requestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[II)V"); 408 m_javaGlue->m_jsAlert = GetJMethod(env, clazz, "jsAlert", "(Ljava/lang/String;Ljava/lang/String;)V"); 409 m_javaGlue->m_jsConfirm = GetJMethod(env, clazz, "jsConfirm", "(Ljava/lang/String;Ljava/lang/String;)Z"); 410 m_javaGlue->m_jsPrompt = GetJMethod(env, clazz, "jsPrompt", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); 411 m_javaGlue->m_jsUnload = GetJMethod(env, clazz, "jsUnload", "(Ljava/lang/String;Ljava/lang/String;)Z"); 412 m_javaGlue->m_jsInterrupt = GetJMethod(env, clazz, "jsInterrupt", "()Z"); 413 m_javaGlue->m_didFirstLayout = GetJMethod(env, clazz, "didFirstLayout", "(Z)V"); 414 m_javaGlue->m_updateViewport = GetJMethod(env, clazz, "updateViewport", "()V"); 415 m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V"); 416 m_javaGlue->m_sendViewInvalidate = GetJMethod(env, clazz, "sendViewInvalidate", "(IIII)V"); 417 m_javaGlue->m_updateTextfield = GetJMethod(env, clazz, "updateTextfield", "(IZLjava/lang/String;I)V"); 418 m_javaGlue->m_updateTextSelection = GetJMethod(env, clazz, "updateTextSelection", "(IIII)V"); 419 m_javaGlue->m_clearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "()V"); 420 m_javaGlue->m_restoreScale = GetJMethod(env, clazz, "restoreScale", "(FF)V"); 421 m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V"); 422 m_javaGlue->m_requestKeyboard = GetJMethod(env, clazz, "requestKeyboard", "(Z)V"); 423 m_javaGlue->m_requestKeyboardWithSelection = GetJMethod(env, clazz, "requestKeyboardWithSelection", "(IIII)V"); 424 m_javaGlue->m_exceededDatabaseQuota = GetJMethod(env, clazz, "exceededDatabaseQuota", "(Ljava/lang/String;Ljava/lang/String;JJ)V"); 425 m_javaGlue->m_reachedMaxAppCacheSize = GetJMethod(env, clazz, "reachedMaxAppCacheSize", "(J)V"); 426 m_javaGlue->m_populateVisitedLinks = GetJMethod(env, clazz, "populateVisitedLinks", "()V"); 427 m_javaGlue->m_geolocationPermissionsShowPrompt = GetJMethod(env, clazz, "geolocationPermissionsShowPrompt", "(Ljava/lang/String;)V"); 428 m_javaGlue->m_geolocationPermissionsHidePrompt = GetJMethod(env, clazz, "geolocationPermissionsHidePrompt", "()V"); 429 m_javaGlue->m_getDeviceMotionService = GetJMethod(env, clazz, "getDeviceMotionService", "()Landroid/webkit/DeviceMotionService;"); 430 m_javaGlue->m_getDeviceOrientationService = GetJMethod(env, clazz, "getDeviceOrientationService", "()Landroid/webkit/DeviceOrientationService;"); 431 m_javaGlue->m_addMessageToConsole = GetJMethod(env, clazz, "addMessageToConsole", "(Ljava/lang/String;ILjava/lang/String;I)V"); 432 m_javaGlue->m_formDidBlur = GetJMethod(env, clazz, "formDidBlur", "(I)V"); 433 m_javaGlue->m_getPluginClass = GetJMethod(env, clazz, "getPluginClass", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Class;"); 434 m_javaGlue->m_showFullScreenPlugin = GetJMethod(env, clazz, "showFullScreenPlugin", "(Landroid/webkit/ViewManager$ChildView;II)V"); 435 m_javaGlue->m_hideFullScreenPlugin = GetJMethod(env, clazz, "hideFullScreenPlugin", "()V"); 436 m_javaGlue->m_createSurface = GetJMethod(env, clazz, "createSurface", "(Landroid/view/View;)Landroid/webkit/ViewManager$ChildView;"); 437 m_javaGlue->m_addSurface = GetJMethod(env, clazz, "addSurface", "(Landroid/view/View;IIII)Landroid/webkit/ViewManager$ChildView;"); 438 m_javaGlue->m_updateSurface = GetJMethod(env, clazz, "updateSurface", "(Landroid/webkit/ViewManager$ChildView;IIII)V"); 439 m_javaGlue->m_destroySurface = GetJMethod(env, clazz, "destroySurface", "(Landroid/webkit/ViewManager$ChildView;)V"); 440 m_javaGlue->m_getContext = GetJMethod(env, clazz, "getContext", "()Landroid/content/Context;"); 441 m_javaGlue->m_keepScreenOn = GetJMethod(env, clazz, "keepScreenOn", "(Z)V"); 442 m_javaGlue->m_sendFindAgain = GetJMethod(env, clazz, "sendFindAgain", "()V"); 443 m_javaGlue->m_showRect = GetJMethod(env, clazz, "showRect", "(IIIIIIFFFF)V"); 444 m_javaGlue->m_centerFitRect = GetJMethod(env, clazz, "centerFitRect", "(IIII)V"); 445 m_javaGlue->m_setScrollbarModes = GetJMethod(env, clazz, "setScrollbarModes", "(II)V"); 446 m_javaGlue->m_setInstallableWebApp = GetJMethod(env, clazz, "setInstallableWebApp", "()V"); 447 #if ENABLE(VIDEO) 448 m_javaGlue->m_enterFullscreenForVideoLayer = GetJMethod(env, clazz, "enterFullscreenForVideoLayer", "(ILjava/lang/String;)V"); 449 #endif 450 m_javaGlue->m_setWebTextViewAutoFillable = GetJMethod(env, clazz, "setWebTextViewAutoFillable", "(ILjava/lang/String;)V"); 451 m_javaGlue->m_selectAt = GetJMethod(env, clazz, "selectAt", "(II)V"); 452 env->DeleteLocalRef(clazz); 453 454 env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this); 455 456 PageGroup::setShouldTrackVisitedLinks(true); 457 458 clearContent(); 459 460 MemoryUsage::setLowMemoryUsageMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_lowMemoryUsageMb)); 461 MemoryUsage::setHighMemoryUsageMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_highMemoryUsageMb)); 462 MemoryUsage::setHighUsageDeltaMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_highUsageDeltaMb)); 463 464 WebViewCore::addInstance(this); 465 466 #if USE(CHROME_NETWORK_STACK) 467 AndroidNetworkLibraryImpl::InitWithApplicationContext(env, 0); 468 #endif 469 470 #if USE(V8) 471 // Static initialisation of certain important V8 static data gets performed at system startup when 472 // libwebcore gets loaded. We now need to associate the WebCore thread with V8 to complete 473 // initialisation. 474 v8::V8::Initialize(); 475 #endif 476 } 477 478 WebViewCore::~WebViewCore() 479 { 480 WebViewCore::removeInstance(this); 481 482 // Release the focused view 483 Release(m_popupReply); 484 485 if (m_javaGlue->m_obj) { 486 JNIEnv* env = JSC::Bindings::getJNIEnv(); 487 env->DeleteWeakGlobalRef(m_javaGlue->m_obj); 488 m_javaGlue->m_obj = 0; 489 } 490 delete m_javaGlue; 491 delete m_frameCacheKit; 492 delete m_navPictureKit; 493 } 494 495 WebViewCore* WebViewCore::getWebViewCore(const WebCore::FrameView* view) 496 { 497 return getWebViewCore(static_cast<const WebCore::ScrollView*>(view)); 498 } 499 500 WebViewCore* WebViewCore::getWebViewCore(const WebCore::ScrollView* view) 501 { 502 if (!view) 503 return 0; 504 505 WebFrameView* webFrameView = static_cast<WebFrameView*>(view->platformWidget()); 506 if (!webFrameView) 507 return 0; 508 return webFrameView->webViewCore(); 509 } 510 511 static bool layoutIfNeededRecursive(WebCore::Frame* f) 512 { 513 if (!f) 514 return true; 515 516 WebCore::FrameView* v = f->view(); 517 if (!v) 518 return true; 519 520 if (v->needsLayout()) 521 v->layout(f->tree()->parent()); 522 523 WebCore::Frame* child = f->tree()->firstChild(); 524 bool success = true; 525 while (child) { 526 success &= layoutIfNeededRecursive(child); 527 child = child->tree()->nextSibling(); 528 } 529 530 return success && !v->needsLayout(); 531 } 532 533 CacheBuilder& WebViewCore::cacheBuilder() 534 { 535 return FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder(); 536 } 537 538 WebCore::Node* WebViewCore::currentFocus() 539 { 540 return cacheBuilder().currentFocus(); 541 } 542 543 void WebViewCore::recordPicture(SkPicture* picture) 544 { 545 // if there is no document yet, just return 546 if (!m_mainFrame->document()) { 547 DBG_NAV_LOG("no document"); 548 return; 549 } 550 // Call layout to ensure that the contentWidth and contentHeight are correct 551 if (!layoutIfNeededRecursive(m_mainFrame)) { 552 DBG_NAV_LOG("layout failed"); 553 return; 554 } 555 // draw into the picture's recording canvas 556 WebCore::FrameView* view = m_mainFrame->view(); 557 DBG_NAV_LOGD("view=(w=%d,h=%d)", view->contentsWidth(), 558 view->contentsHeight()); 559 SkAutoPictureRecord arp(picture, view->contentsWidth(), 560 view->contentsHeight(), PICT_RECORD_FLAGS); 561 SkAutoMemoryUsageProbe mup(__FUNCTION__); 562 563 // Copy m_buttons so we can pass it to our graphics context. 564 gButtonMutex.lock(); 565 WTF::Vector<Container> buttons(m_buttons); 566 gButtonMutex.unlock(); 567 568 WebCore::PlatformGraphicsContext pgc(arp.getRecordingCanvas(), &buttons); 569 WebCore::GraphicsContext gc(&pgc); 570 view->platformWidget()->draw(&gc, WebCore::IntRect(0, 0, 571 view->contentsWidth(), view->contentsHeight())); 572 573 gButtonMutex.lock(); 574 updateButtonList(&buttons); 575 gButtonMutex.unlock(); 576 } 577 578 void WebViewCore::recordPictureSet(PictureSet* content) 579 { 580 // if there is no document yet, just return 581 if (!m_mainFrame->document()) { 582 DBG_SET_LOG("!m_mainFrame->document()"); 583 return; 584 } 585 if (m_addInval.isEmpty()) { 586 DBG_SET_LOG("m_addInval.isEmpty()"); 587 return; 588 } 589 // Call layout to ensure that the contentWidth and contentHeight are correct 590 // it's fine for layout to gather invalidates, but defeat sending a message 591 // back to java to call webkitDraw, since we're already in the middle of 592 // doing that 593 m_skipContentDraw = true; 594 bool success = layoutIfNeededRecursive(m_mainFrame); 595 m_skipContentDraw = false; 596 597 // We may be mid-layout and thus cannot draw. 598 if (!success) 599 return; 600 601 { // collect WebViewCoreRecordTimeCounter after layoutIfNeededRecursive 602 #ifdef ANDROID_INSTRUMENT 603 TimeCounterAuto counter(TimeCounter::WebViewCoreRecordTimeCounter); 604 #endif 605 606 // if the webkit page dimensions changed, discard the pictureset and redraw. 607 WebCore::FrameView* view = m_mainFrame->view(); 608 int width = view->contentsWidth(); 609 int height = view->contentsHeight(); 610 611 // Use the contents width and height as a starting point. 612 SkIRect contentRect; 613 contentRect.set(0, 0, width, height); 614 SkIRect total(contentRect); 615 616 // Traverse all the frames and add their sizes if they are in the visible 617 // rectangle. 618 for (WebCore::Frame* frame = m_mainFrame->tree()->traverseNext(); frame; 619 frame = frame->tree()->traverseNext()) { 620 // If the frame doesn't have an owner then it is the top frame and the 621 // view size is the frame size. 622 WebCore::RenderPart* owner = frame->ownerRenderer(); 623 if (owner && owner->style()->visibility() == VISIBLE) { 624 int x = owner->x(); 625 int y = owner->y(); 626 627 // Traverse the tree up to the parent to find the absolute position 628 // of this frame. 629 WebCore::Frame* parent = frame->tree()->parent(); 630 while (parent) { 631 WebCore::RenderPart* parentOwner = parent->ownerRenderer(); 632 if (parentOwner) { 633 x += parentOwner->x(); 634 y += parentOwner->y(); 635 } 636 parent = parent->tree()->parent(); 637 } 638 // Use the owner dimensions so that padding and border are 639 // included. 640 int right = x + owner->width(); 641 int bottom = y + owner->height(); 642 SkIRect frameRect = {x, y, right, bottom}; 643 // Ignore a width or height that is smaller than 1. Some iframes 644 // have small dimensions in order to be hidden. The iframe 645 // expansion code does not expand in that case so we should ignore 646 // them here. 647 if (frameRect.width() > 1 && frameRect.height() > 1 648 && SkIRect::Intersects(total, frameRect)) 649 total.join(x, y, right, bottom); 650 } 651 } 652 653 // If the new total is larger than the content, resize the view to include 654 // all the content. 655 if (!contentRect.contains(total)) { 656 // Resize the view to change the overflow clip. 657 view->resize(total.fRight, total.fBottom); 658 659 // We have to force a layout in order for the clip to change. 660 m_mainFrame->contentRenderer()->setNeedsLayoutAndPrefWidthsRecalc(); 661 view->forceLayout(); 662 663 // Relayout similar to above 664 m_skipContentDraw = true; 665 bool success = layoutIfNeededRecursive(m_mainFrame); 666 m_skipContentDraw = false; 667 if (!success) 668 return; 669 670 // Set the computed content width 671 width = view->contentsWidth(); 672 height = view->contentsHeight(); 673 } 674 675 if (cacheBuilder().pictureSetDisabled()) 676 content->clear(); 677 678 #if USE(ACCELERATED_COMPOSITING) 679 // Detects if the content size has changed 680 bool contentSizeChanged = false; 681 if (content->width() != width || content->height() != height) 682 contentSizeChanged = true; 683 #endif 684 685 content->setDimensions(width, height, &m_addInval); 686 687 // Add the current inval rects to the PictureSet, and rebuild it. 688 content->add(m_addInval, 0, 0, false); 689 690 // If we have too many invalidations, just get the area bounds 691 SkRegion::Iterator iterator(m_addInval); 692 int nbInvals = 0; 693 while (!iterator.done()) { 694 iterator.next(); 695 nbInvals++; 696 if (nbInvals > MAX_INVALIDATIONS) 697 break; 698 } 699 if (nbInvals > MAX_INVALIDATIONS) { 700 SkIRect r = m_addInval.getBounds(); 701 m_addInval.setRect(r); 702 } 703 704 // Rebuild the pictureset (webkit repaint) 705 rebuildPictureSet(content); 706 707 #if USE(ACCELERATED_COMPOSITING) 708 // We repainted the pictureset, but the invals are not always correct when 709 // the content size did change. For now, let's just reset the 710 // inval we will pass to the UI so that it invalidates the entire 711 // content -- tiles will be marked dirty and will have to be repainted. 712 // FIXME: the webkit invals ought to have been enough... 713 if (contentSizeChanged) { 714 SkIRect r; 715 r.fLeft = 0; 716 r.fTop = 0; 717 r.fRight = width; 718 r.fBottom = height; 719 m_addInval.setRect(r); 720 } 721 #endif 722 723 } // WebViewCoreRecordTimeCounter 724 725 WebCore::Node* oldFocusNode = currentFocus(); 726 m_frameCacheOutOfDate = true; 727 WebCore::IntRect oldBounds; 728 int oldSelStart = 0; 729 int oldSelEnd = 0; 730 if (oldFocusNode) { 731 oldBounds = oldFocusNode->getRect(); 732 RenderObject* renderer = oldFocusNode->renderer(); 733 if (renderer && (renderer->isTextArea() || renderer->isTextField())) { 734 WebCore::RenderTextControl* rtc = 735 static_cast<WebCore::RenderTextControl*>(renderer); 736 oldSelStart = rtc->selectionStart(); 737 oldSelEnd = rtc->selectionEnd(); 738 } 739 } else 740 oldBounds = WebCore::IntRect(0,0,0,0); 741 unsigned latestVersion = 0; 742 if (m_check_domtree_version) { 743 // as domTreeVersion only increment, we can just check the sum to see 744 // whether we need to update the frame cache 745 for (Frame* frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) { 746 const Document* doc = frame->document(); 747 latestVersion += doc->domTreeVersion() + doc->styleVersion(); 748 } 749 } 750 DBG_NAV_LOGD("m_lastFocused=%p oldFocusNode=%p" 751 " m_lastFocusedBounds={%d,%d,%d,%d} oldBounds={%d,%d,%d,%d}" 752 " m_lastFocusedSelection={%d,%d} oldSelection={%d,%d}" 753 " m_check_domtree_version=%s latestVersion=%d m_domtree_version=%d", 754 m_lastFocused, oldFocusNode, 755 m_lastFocusedBounds.x(), m_lastFocusedBounds.y(), 756 m_lastFocusedBounds.width(), m_lastFocusedBounds.height(), 757 oldBounds.x(), oldBounds.y(), oldBounds.width(), oldBounds.height(), 758 m_lastFocusedSelStart, m_lastFocusedSelEnd, oldSelStart, oldSelEnd, 759 m_check_domtree_version ? "true" : "false", 760 latestVersion, m_domtree_version); 761 if (m_lastFocused == oldFocusNode && m_lastFocusedBounds == oldBounds 762 && m_lastFocusedSelStart == oldSelStart 763 && m_lastFocusedSelEnd == oldSelEnd 764 && !m_findIsUp 765 && (!m_check_domtree_version || latestVersion == m_domtree_version)) 766 { 767 return; 768 } 769 m_focusBoundsChanged |= m_lastFocused == oldFocusNode 770 && m_lastFocusedBounds != oldBounds; 771 m_lastFocused = oldFocusNode; 772 m_lastFocusedBounds = oldBounds; 773 m_lastFocusedSelStart = oldSelStart; 774 m_lastFocusedSelEnd = oldSelEnd; 775 m_domtree_version = latestVersion; 776 DBG_NAV_LOG("call updateFrameCache"); 777 updateFrameCache(); 778 if (m_findIsUp) { 779 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 780 JNIEnv* env = JSC::Bindings::getJNIEnv(); 781 AutoJObject javaObject = m_javaGlue->object(env); 782 if (javaObject.get()) { 783 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_sendFindAgain); 784 checkException(env); 785 } 786 } 787 } 788 789 void WebViewCore::updateButtonList(WTF::Vector<Container>* buttons) 790 { 791 // All the entries in buttons are either updates of previous entries in 792 // m_buttons or they need to be added to it. 793 Container* end = buttons->end(); 794 for (Container* updatedContainer = buttons->begin(); 795 updatedContainer != end; updatedContainer++) { 796 bool updated = false; 797 // Search for a previous entry that references the same node as our new 798 // data 799 Container* lastPossibleMatch = m_buttons.end(); 800 for (Container* possibleMatch = m_buttons.begin(); 801 possibleMatch != lastPossibleMatch; possibleMatch++) { 802 if (updatedContainer->matches(possibleMatch->node())) { 803 // Update our record, and skip to the next one. 804 possibleMatch->setRect(updatedContainer->rect()); 805 updated = true; 806 break; 807 } 808 } 809 if (!updated) { 810 // This is a brand new button, so append it to m_buttons 811 m_buttons.append(*updatedContainer); 812 } 813 } 814 size_t i = 0; 815 // count will decrease each time one is removed, so check count each time. 816 while (i < m_buttons.size()) { 817 if (m_buttons[i].canBeRemoved()) { 818 m_buttons[i] = m_buttons.last(); 819 m_buttons.removeLast(); 820 } else { 821 i++; 822 } 823 } 824 } 825 826 // note: updateCursorBounds is called directly by the WebView thread 827 // This needs to be called each time we call CachedRoot::setCursor() with 828 // non-null CachedNode/CachedFrame, since otherwise the WebViewCore's data 829 // about the cursor is incorrect. When we call setCursor(0,0), we need 830 // to set hasCursorBounds to false. 831 void WebViewCore::updateCursorBounds(const CachedRoot* root, 832 const CachedFrame* cachedFrame, const CachedNode* cachedNode) 833 { 834 LOG_ASSERT(root, "updateCursorBounds: root cannot be null"); 835 LOG_ASSERT(cachedNode, "updateCursorBounds: cachedNode cannot be null"); 836 LOG_ASSERT(cachedFrame, "updateCursorBounds: cachedFrame cannot be null"); 837 gCursorBoundsMutex.lock(); 838 m_hasCursorBounds = !cachedNode->isHidden(); 839 // If m_hasCursorBounds is false, we never look at the other 840 // values, so do not bother setting them. 841 if (m_hasCursorBounds) { 842 WebCore::IntRect bounds = cachedNode->bounds(cachedFrame); 843 if (m_cursorBounds != bounds) 844 DBG_NAV_LOGD("new cursor bounds=(%d,%d,w=%d,h=%d)", 845 bounds.x(), bounds.y(), bounds.width(), bounds.height()); 846 m_cursorBounds = bounds; 847 m_cursorHitBounds = cachedNode->hitBounds(cachedFrame); 848 m_cursorFrame = cachedFrame->framePointer(); 849 root->getSimulatedMousePosition(&m_cursorLocation); 850 m_cursorNode = cachedNode->nodePointer(); 851 } 852 gCursorBoundsMutex.unlock(); 853 } 854 855 void WebViewCore::clearContent() 856 { 857 DBG_SET_LOG(""); 858 m_content.clear(); 859 m_addInval.setEmpty(); 860 m_rebuildInval.setEmpty(); 861 } 862 863 bool WebViewCore::focusBoundsChanged() 864 { 865 bool result = m_focusBoundsChanged; 866 m_focusBoundsChanged = false; 867 return result; 868 } 869 870 SkPicture* WebViewCore::rebuildPicture(const SkIRect& inval) 871 { 872 WebCore::FrameView* view = m_mainFrame->view(); 873 int width = view->contentsWidth(); 874 int height = view->contentsHeight(); 875 SkPicture* picture = new SkPicture(); 876 SkAutoPictureRecord arp(picture, width, height, PICT_RECORD_FLAGS); 877 SkAutoMemoryUsageProbe mup(__FUNCTION__); 878 SkCanvas* recordingCanvas = arp.getRecordingCanvas(); 879 880 gButtonMutex.lock(); 881 WTF::Vector<Container> buttons(m_buttons); 882 gButtonMutex.unlock(); 883 884 WebCore::PlatformGraphicsContext pgc(recordingCanvas, &buttons); 885 WebCore::GraphicsContext gc(&pgc); 886 recordingCanvas->translate(-inval.fLeft, -inval.fTop); 887 recordingCanvas->save(); 888 view->platformWidget()->draw(&gc, WebCore::IntRect(inval.fLeft, 889 inval.fTop, inval.width(), inval.height())); 890 m_rebuildInval.op(inval, SkRegion::kUnion_Op); 891 DBG_SET_LOGD("m_rebuildInval={%d,%d,r=%d,b=%d}", 892 m_rebuildInval.getBounds().fLeft, m_rebuildInval.getBounds().fTop, 893 m_rebuildInval.getBounds().fRight, m_rebuildInval.getBounds().fBottom); 894 895 gButtonMutex.lock(); 896 updateButtonList(&buttons); 897 gButtonMutex.unlock(); 898 899 return picture; 900 } 901 902 void WebViewCore::rebuildPictureSet(PictureSet* pictureSet) 903 { 904 WebCore::FrameView* view = m_mainFrame->view(); 905 906 #ifdef FAST_PICTURESET 907 WTF::Vector<Bucket*>* buckets = pictureSet->bucketsToUpdate(); 908 909 for (unsigned int i = 0; i < buckets->size(); i++) { 910 Bucket* bucket = (*buckets)[i]; 911 for (unsigned int j = 0; j < bucket->size(); j++) { 912 BucketPicture& bucketPicture = (*bucket)[j]; 913 const SkIRect& inval = bucketPicture.mRealArea; 914 SkPicture* picture = rebuildPicture(inval); 915 SkSafeUnref(bucketPicture.mPicture); 916 bucketPicture.mPicture = picture; 917 } 918 } 919 buckets->clear(); 920 #else 921 size_t size = pictureSet->size(); 922 for (size_t index = 0; index < size; index++) { 923 if (pictureSet->upToDate(index)) 924 continue; 925 const SkIRect& inval = pictureSet->bounds(index); 926 DBG_SET_LOGD("pictSet=%p [%d] {%d,%d,w=%d,h=%d}", pictureSet, index, 927 inval.fLeft, inval.fTop, inval.width(), inval.height()); 928 pictureSet->setPicture(index, rebuildPicture(inval)); 929 } 930 931 pictureSet->validate(__FUNCTION__); 932 #endif 933 } 934 935 bool WebViewCore::updateLayers(LayerAndroid* layers) 936 { 937 // We update the layers 938 ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client()); 939 GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync()); 940 if (root) { 941 root->notifyClientAnimationStarted(); 942 LayerAndroid* updatedLayer = root->contentLayer(); 943 return layers->updateWithTree(updatedLayer); 944 } 945 return true; 946 } 947 948 BaseLayerAndroid* WebViewCore::createBaseLayer(SkRegion* region) 949 { 950 BaseLayerAndroid* base = new BaseLayerAndroid(); 951 base->setContent(m_content); 952 953 if (!region->isEmpty()) { 954 m_skipContentDraw = true; 955 bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame); 956 m_skipContentDraw = false; 957 // Layout only fails if called during a layout. 958 LOG_ASSERT(layoutSucceeded, "Can never be called recursively"); 959 } 960 961 #if USE(ACCELERATED_COMPOSITING) 962 // We set the background color 963 if (!region->isEmpty() && m_mainFrame && m_mainFrame->document() 964 && m_mainFrame->document()->body()) { 965 Document* document = m_mainFrame->document(); 966 RefPtr<RenderStyle> style = document->styleForElementIgnoringPendingStylesheets(document->body()); 967 if (style->hasBackground()) { 968 Color color = style->visitedDependentColor(CSSPropertyBackgroundColor); 969 if (color.isValid() && color.alpha() > 0) 970 base->setBackgroundColor(color); 971 } 972 } 973 974 // We update the layers 975 ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client()); 976 GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync()); 977 if (root) { 978 root->notifyClientAnimationStarted(); 979 LayerAndroid* copyLayer = new LayerAndroid(*root->contentLayer()); 980 base->addChild(copyLayer); 981 copyLayer->unref(); 982 root->contentLayer()->clearDirtyRegion(); 983 } 984 #endif 985 986 return base; 987 } 988 989 BaseLayerAndroid* WebViewCore::recordContent(SkRegion* region, SkIPoint* point) 990 { 991 DBG_SET_LOG("start"); 992 // If there is a pending style recalculation, just return. 993 if (m_mainFrame->document()->isPendingStyleRecalc()) { 994 DBG_SET_LOGD("recordContent: pending style recalc, ignoring."); 995 return 0; 996 } 997 float progress = (float) m_mainFrame->page()->progress()->estimatedProgress(); 998 m_progressDone = progress <= 0.0f || progress >= 1.0f; 999 recordPictureSet(&m_content); 1000 if (!m_progressDone && m_content.isEmpty()) { 1001 DBG_SET_LOGD("empty (progress=%g)", progress); 1002 return 0; 1003 } 1004 region->set(m_addInval); 1005 m_addInval.setEmpty(); 1006 #if USE(ACCELERATED_COMPOSITING) 1007 #else 1008 region->op(m_rebuildInval, SkRegion::kUnion_Op); 1009 #endif 1010 m_rebuildInval.setEmpty(); 1011 point->fX = m_content.width(); 1012 point->fY = m_content.height(); 1013 DBG_SET_LOGD("region={%d,%d,r=%d,b=%d}", region->getBounds().fLeft, 1014 region->getBounds().fTop, region->getBounds().fRight, 1015 region->getBounds().fBottom); 1016 DBG_SET_LOG("end"); 1017 1018 return createBaseLayer(region); 1019 } 1020 1021 void WebViewCore::splitContent(PictureSet* content) 1022 { 1023 #ifdef FAST_PICTURESET 1024 #else 1025 bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame); 1026 LOG_ASSERT(layoutSucceeded, "Can never be called recursively"); 1027 content->split(&m_content); 1028 rebuildPictureSet(&m_content); 1029 content->set(m_content); 1030 #endif // FAST_PICTURESET 1031 } 1032 1033 void WebViewCore::scrollTo(int x, int y, bool animate) 1034 { 1035 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1036 1037 // LOGD("WebViewCore::scrollTo(%d %d)\n", x, y); 1038 1039 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1040 AutoJObject javaObject = m_javaGlue->object(env); 1041 if (!javaObject.get()) 1042 return; 1043 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_scrollTo, 1044 x, y, animate, false); 1045 checkException(env); 1046 } 1047 1048 void WebViewCore::sendNotifyProgressFinished() 1049 { 1050 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1051 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1052 AutoJObject javaObject = m_javaGlue->object(env); 1053 if (!javaObject.get()) 1054 return; 1055 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_sendNotifyProgressFinished); 1056 checkException(env); 1057 } 1058 1059 void WebViewCore::viewInvalidate(const WebCore::IntRect& rect) 1060 { 1061 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1062 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1063 AutoJObject javaObject = m_javaGlue->object(env); 1064 if (!javaObject.get()) 1065 return; 1066 env->CallVoidMethod(javaObject.get(), 1067 m_javaGlue->m_sendViewInvalidate, 1068 rect.x(), rect.y(), rect.maxX(), rect.maxY()); 1069 checkException(env); 1070 } 1071 1072 void WebViewCore::contentDraw() 1073 { 1074 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1075 AutoJObject javaObject = m_javaGlue->object(env); 1076 if (!javaObject.get()) 1077 return; 1078 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_contentDraw); 1079 checkException(env); 1080 } 1081 1082 void WebViewCore::layersDraw() 1083 { 1084 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1085 AutoJObject javaObject = m_javaGlue->object(env); 1086 if (!javaObject.get()) 1087 return; 1088 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_layersDraw); 1089 checkException(env); 1090 } 1091 1092 void WebViewCore::contentInvalidate(const WebCore::IntRect &r) 1093 { 1094 DBG_SET_LOGD("rect={%d,%d,w=%d,h=%d}", r.x(), r.y(), r.width(), r.height()); 1095 SkIRect rect(r); 1096 if (!rect.intersect(0, 0, INT_MAX, INT_MAX)) 1097 return; 1098 m_addInval.op(rect, SkRegion::kUnion_Op); 1099 DBG_SET_LOGD("m_addInval={%d,%d,r=%d,b=%d}", 1100 m_addInval.getBounds().fLeft, m_addInval.getBounds().fTop, 1101 m_addInval.getBounds().fRight, m_addInval.getBounds().fBottom); 1102 if (!m_skipContentDraw) 1103 contentDraw(); 1104 } 1105 1106 void WebViewCore::contentInvalidateAll() 1107 { 1108 WebCore::FrameView* view = m_mainFrame->view(); 1109 contentInvalidate(WebCore::IntRect(0, 0, 1110 view->contentsWidth(), view->contentsHeight())); 1111 } 1112 1113 void WebViewCore::offInvalidate(const WebCore::IntRect &r) 1114 { 1115 // FIXME: these invalidates are offscreen, and can be throttled or 1116 // deferred until the area is visible. For now, treat them as 1117 // regular invals so that drawing happens (inefficiently) for now. 1118 contentInvalidate(r); 1119 } 1120 1121 static int pin_pos(int x, int width, int targetWidth) 1122 { 1123 if (x + width > targetWidth) 1124 x = targetWidth - width; 1125 if (x < 0) 1126 x = 0; 1127 return x; 1128 } 1129 1130 void WebViewCore::didFirstLayout() 1131 { 1132 DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); 1133 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1134 1135 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1136 AutoJObject javaObject = m_javaGlue->object(env); 1137 if (!javaObject.get()) 1138 return; 1139 1140 const WebCore::KURL& url = m_mainFrame->document()->url(); 1141 if (url.isEmpty()) 1142 return; 1143 LOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data()); 1144 1145 WebCore::FrameLoadType loadType = m_mainFrame->loader()->loadType(); 1146 1147 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_didFirstLayout, 1148 loadType == WebCore::FrameLoadTypeStandard 1149 // When redirect with locked history, we would like to reset the 1150 // scale factor. This is important for www.yahoo.com as it is 1151 // redirected to www.yahoo.com/?rs=1 on load. 1152 || loadType == WebCore::FrameLoadTypeRedirectWithLockedBackForwardList); 1153 checkException(env); 1154 1155 DBG_NAV_LOG("call updateFrameCache"); 1156 m_check_domtree_version = false; 1157 updateFrameCache(); 1158 m_history.setDidFirstLayout(true); 1159 } 1160 1161 void WebViewCore::updateViewport() 1162 { 1163 DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); 1164 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1165 1166 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1167 AutoJObject javaObject = m_javaGlue->object(env); 1168 if (!javaObject.get()) 1169 return; 1170 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_updateViewport); 1171 checkException(env); 1172 } 1173 1174 void WebViewCore::restoreScale(float scale, float textWrapScale) 1175 { 1176 DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); 1177 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1178 1179 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1180 AutoJObject javaObject = m_javaGlue->object(env); 1181 if (!javaObject.get()) 1182 return; 1183 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_restoreScale, scale, textWrapScale); 1184 checkException(env); 1185 } 1186 1187 void WebViewCore::needTouchEvents(bool need) 1188 { 1189 DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); 1190 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1191 1192 #if ENABLE(TOUCH_EVENTS) 1193 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1194 AutoJObject javaObject = m_javaGlue->object(env); 1195 if (!javaObject.get()) 1196 return; 1197 1198 if (m_forwardingTouchEvents == need) 1199 return; 1200 1201 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_needTouchEvents, need); 1202 checkException(env); 1203 1204 m_forwardingTouchEvents = need; 1205 #endif 1206 } 1207 1208 void WebViewCore::requestKeyboardWithSelection(const WebCore::Node* node, 1209 int selStart, int selEnd) 1210 { 1211 DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); 1212 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1213 1214 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1215 AutoJObject javaObject = m_javaGlue->object(env); 1216 if (!javaObject.get()) 1217 return; 1218 env->CallVoidMethod(javaObject.get(), 1219 m_javaGlue->m_requestKeyboardWithSelection, 1220 reinterpret_cast<int>(node), selStart, selEnd, m_textGeneration); 1221 checkException(env); 1222 } 1223 1224 void WebViewCore::requestKeyboard(bool showKeyboard) 1225 { 1226 DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); 1227 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); 1228 1229 JNIEnv* env = JSC::Bindings::getJNIEnv(); 1230 AutoJObject javaObject = m_javaGlue->object(env); 1231 if (!javaObject.get()) 1232 return; 1233 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_requestKeyboard, showKeyboard); 1234 checkException(env); 1235 } 1236 1237 void WebViewCore::notifyProgressFinished() 1238 { 1239 m_check_domtree_version = true; 1240 sendNotifyProgressFinished(); 1241 } 1242 1243 void WebViewCore::doMaxScroll(CacheBuilder::Direction dir) 1244 { 1245 int dx = 0, dy = 0; 1246 1247 switch (dir) { 1248 case CacheBuilder::LEFT: 1249 dx = -m_maxXScroll; 1250 break; 1251 case CacheBuilder::UP: 1252 dy = -m_maxYScroll; 1253 break; 1254 case CacheBuilder::RIGHT: 1255 dx = m_maxXScroll; 1256 break; 1257 case CacheBuilder::DOWN: 1258 dy = m_maxYScroll; 1259 break; 1260 case CacheBuilder::UNINITIALIZED: 1261 default: 1262 LOG_ASSERT(0, "unexpected focus selector"); 1263 } 1264 WebCore::FrameView* view = m_mainFrame->view(); 1265 this->scrollTo(view->scrollX() + dx, view->scrollY() + dy, true); 1266 } 1267 1268 void WebViewCore::setScrollOffset(int moveGeneration, bool sendScrollEvent, int dx, int dy) 1269 { 1270 DBG_NAV_LOGD("{%d,%d} m_scrollOffset=(%d,%d), sendScrollEvent=%d", dx, dy, 1271 m_scrollOffsetX, m_scrollOffsetY, sendScrollEvent); 1272 if (m_scrollOffsetX != dx || m_scrollOffsetY != dy) { 1273 m_scrollOffsetX = dx; 1274 m_scrollOffsetY = dy; 1275 // The visible rect is located within our coordinate space so it 1276 // contains the actual scroll position. Setting the location makes hit 1277 // testing work correctly. 1278 m_mainFrame->view()->platformWidget()->setLocation(m_scrollOffsetX, 1279 m_scrollOffsetY); 1280 if (sendScrollEvent) { 1281 m_mainFrame->eventHandler()->sendScrollEvent(); 1282 1283 // Only update history position if it's user scrolled. 1284 // Update history item to reflect the new scroll position. 1285 // This also helps save the history information when the browser goes to 1286 // background, so scroll position will be restored if browser gets 1287 // killed while in background. 1288 WebCore::HistoryController* history = m_mainFrame->loader()->history(); 1289 // Because the history item saving could be heavy for large sites and 1290 // scrolling can generate lots of small scroll offset, the following code 1291 // reduces the saving frequency. 1292 static const int MIN_SCROLL_DIFF = 32; 1293 if (history->currentItem()) { 1294 WebCore::IntPoint currentPoint = history->currentItem()->scrollPoint(); 1295 if (std::abs(currentPoint.x() - dx) >= MIN_SCROLL_DIFF || 1296 std::abs(currentPoint.y() - dy) >= MIN_SCROLL_DIFF) { 1297 history->saveScrollPositionAndViewStateToItem(history->currentItem()); 1298 } 1299 } 1300 } 1301 1302 // update the currently visible screen 1303 sendPluginVisibleScreen(); 1304 } 1305 gCursorBoundsMutex.lock(); 1306 bool hasCursorBounds = m_hasCursorBounds; 1307 Frame* frame = (Frame*) m_cursorFrame; 1308 IntPoint location = m_cursorLocation; 1309 gCursorBoundsMutex.unlock(); 1310 if (!hasCursorBounds) 1311 return; 1312 moveMouseIfLatest(moveGeneration, frame, location.x(), location.y()); 1313 } 1314 1315 void WebViewCore::setGlobalBounds(int x, int y, int h, int v) 1316 { 1317 DBG_NAV_LOGD("{%d,%d}", x, y); 1318 m_mainFrame->view()->platformWidget()->setWindowBounds(x, y, h, v); 1319 } 1320 1321 void WebViewCore::setSizeScreenWidthAndScale(int width, int height, 1322 int textWrapWidth, float scale, int screenWidth, int screenHeight, 1323 int anchorX, int anchorY, bool ignoreHeight) 1324 { 1325 // Ignore the initial empty document. 1326 const WebCore::KURL& url = m_mainFrame->document()->url(); 1327 if (url.isEmpty()) 1328 return; 1329 1330 WebCoreViewBridge* window = m_mainFrame->view()->platformWidget(); 1331 int ow = window->width(); 1332 int oh = window->height(); 1333 int osw = m_screenWidth; 1334 int osh = m_screenHeight; 1335 int otw = m_textWrapWidth; 1336 float oldScale = m_scale; 1337 DBG_NAV_LOGD("old:(w=%d,h=%d,sw=%d,scale=%g) new:(w=%d,h=%d,sw=%d,scale=%g)", 1338 ow, oh, osw, m_scale, width, height, screenWidth, scale); 1339 m_screenWidth = screenWidth; 1340 m_screenHeight = screenHeight; 1341 m_textWrapWidth = textWrapWidth; 1342 if (scale >= 0) // negative means keep the current scale 1343 m_scale = scale; 1344 m_maxXScroll = screenWidth >> 2; 1345 m_maxYScroll = m_maxXScroll * height / width; 1346 // Don't reflow if the diff is small. 1347 const bool reflow = otw && textWrapWidth && 1348 ((float) abs(otw - textWrapWidth) / textWrapWidth) >= 0.01f; 1349 1350 // When the screen size change, fixed positioned element should be updated. 1351 // This is supposed to be light weighted operation without a full layout. 1352 if (osh != screenHeight || osw != screenWidth) 1353 m_mainFrame->view()->updatePositionedObjects(); 1354 1355 if (ow != width || (!ignoreHeight && oh != height) || reflow) { 1356 WebCore::RenderObject *r = m_mainFrame->contentRenderer(); 1357 DBG_NAV_LOGD("renderer=%p view=(w=%d,h=%d)", r, 1358 screenWidth, screenHeight); 1359 if (r) { 1360 WebCore::IntPoint anchorPoint = WebCore::IntPoint(anchorX, anchorY); 1361 DBG_NAV_LOGD("anchorX=%d anchorY=%d", anchorX, anchorY); 1362 RefPtr<WebCore::Node> node; 1363 WebCore::IntRect bounds; 1364 WebCore::IntPoint offset; 1365 // If the text wrap changed, it is probably zoom change or 1366 // orientation change. Try to keep the anchor at the same place. 1367 if (otw && textWrapWidth && otw != textWrapWidth && 1368 (anchorX != 0 || anchorY != 0)) { 1369 WebCore::HitTestResult hitTestResult = 1370 m_mainFrame->eventHandler()->hitTestResultAtPoint( 1371 anchorPoint, false); 1372 node = hitTestResult.innerNode(); 1373 } 1374 if (node) { 1375 bounds = node->getRect(); 1376 DBG_NAV_LOGD("ob:(x=%d,y=%d,w=%d,h=%d)", 1377 bounds.x(), bounds.y(), bounds.width(), bounds.height()); 1378 // sites like nytimes.com insert a non-standard tag <nyt_text> 1379 // in the html. If it is the HitTestResult, it may have zero 1380 // width and height. In this case, use its parent node. 1381 if (bounds.width() == 0) { 1382 node = node->parentOrHostNode(); 1383 if (node) { 1384 bounds = node->getRect(); 1385 DBG_NAV_LOGD("found a zero width node and use its parent, whose ob:(x=%d,y=%d,w=%d,h=%d)", 1386 bounds.x(), bounds.y(), bounds.width(), bounds.height()); 1387 } 1388 } 1389 } 1390 1391 // Set the size after finding the old anchor point as 1392 // hitTestResultAtPoint causes a layout. 1393 window->setSize(width, height); 1394 window->setVisibleSize(screenWidth, screenHeight); 1395 if (width != screenWidth) { 1396 m_mainFrame->view()->setUseFixedLayout(true); 1397 m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height)); 1398 } else { 1399 m_mainFrame->view()->setUseFixedLayout(false); 1400 } 1401 r->setNeedsLayoutAndPrefWidthsRecalc(); 1402 m_mainFrame->view()->forceLayout(); 1403 1404 // scroll to restore current screen center 1405 if (node) { 1406 const WebCore::IntRect& newBounds = node->getRect(); 1407 DBG_NAV_LOGD("nb:(x=%d,y=%d,w=%d," 1408 "h=%d)", newBounds.x(), newBounds.y(), 1409 newBounds.width(), newBounds.height()); 1410 if ((osw && osh && bounds.width() && bounds.height()) 1411 && (bounds != newBounds)) { 1412 WebCore::FrameView* view = m_mainFrame->view(); 1413 // force left align if width is not changed while height changed. 1414 // the anchorPoint is probably at some white space in the node 1415 // which is affected by text wrap around the screen width. 1416 const bool leftAlign = (otw != textWrapWidth) 1417 && (bounds.width() == newBounds.width()) 1418 && (bounds.height() != newBounds.height()); 1419 const float xPercentInDoc = 1420 leftAlign ? 0.0 : (float) (anchorX - bounds.x()) / bounds.width(); 1421 const float xPercentInView = 1422 leftAlign ? 0.0 : (float) (anchorX - m_scrollOffsetX) / osw; 1423 const float yPercentInDoc = (float) (anchorY - bounds.y()) / bounds.height(); 1424 const float yPercentInView = (float) (anchorY - m_scrollOffsetY) / osh; 1425 showRect(newBounds.x(), newBounds.y(), newBounds.width(), 1426 newBounds.height(), view->contentsWidth(), 1427 view->contentsHeight(), 1428 xPercentInDoc, xPercentInView, 1429 yPercentInDoc, yPercentInView); 1430 } 1431 } 1432 } 1433 } else { 1434 window->setSize(width, height); 1435 window->setVisibleSize(screenWidth, screenHeight); 1436 m_mainFrame->view()->resize(width, height); 1437 if (width != screenWidth) { 1438 m_mainFrame->view()->setUseFixedLayout(true); 1439 m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height)); 1440 } else { 1441 m_mainFrame->view()->setUseFixedLayout(false); 1442 } 1443 } 1444 1445 // update the currently visible screen as perceived by the plugin 1446 sendPluginVisibleScreen(); 1447 } 1448 1449 void WebViewCore::dumpDomTree(bool useFile) 1450 { 1451 #ifdef ANDROID_DOM_LOGGING 1452 if (useFile) 1453 gDomTreeFile = fopen(DOM_TREE_LOG_FILE, "w"); 1454 m_mainFrame->document()->showTreeForThis(); 1455 if (gDomTreeFile) { 1456 fclose(gDomTreeFile); 1457 gDomTreeFile = 0; 1458 } 1459 #endif 1460 } 1461 1462 void WebViewCore::dumpRenderTree(bool useFile) 1463 { 1464 #ifdef ANDROID_DOM_LOGGING 1465 WTF::CString renderDump = WebCore::externalRepresentation(m_mainFrame).utf8(); 1466 const char* data = renderDump.data(); 1467 if (useFile) { 1468 gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w"); 1469 DUMP_RENDER_LOGD("%s", data); 1470 fclose(gRenderTreeFile); 1471 gRenderTreeFile = 0; 1472 } else { 1473 // adb log can only output 1024 characters, so write out line by line. 1474 // exclude '\n' as adb log adds it for each output. 1475 int length = renderDump.length(); 1476 for (int i = 0, last = 0; i < length; i++) { 1477 if (data[i] == '\n') { 1478 if (i != last) 1479 DUMP_RENDER_LOGD("%.*s", (i - last), &(data[last])); 1480 last = i + 1; 1481 } 1482 } 1483 } 1484 #endif 1485 } 1486 1487 void WebViewCore::dumpNavTree() 1488 { 1489 #if DUMP_NAV_CACHE 1490 cacheBuilder().mDebug.print(); 1491 #endif 1492 } 1493 1494 HTMLElement* WebViewCore::retrieveElement(int x, int y, 1495 const QualifiedName& tagName) 1496 { 1497 HitTestResult hitTestResult = m_mainFrame->eventHandler() 1498 ->hitTestResultAtPoint(IntPoint(x, y), false, false, 1499 DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, 1500 IntSize(1, 1)); 1501 if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) { 1502 LOGE("Should not happen: no in document Node found"); 1503 return 0; 1504 } 1505 const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult(); 1506 if (list.isEmpty()) { 1507 LOGE("Should not happen: no rect-based-test nodes found"); 1508 return 0; 1509 } 1510 Node* node = hitTestResult.innerNode(); 1511 Node* element = node; 1512 while (element && (!element->isElementNode() 1513 || !element->hasTagName(tagName))) { 1514 element = element->parentNode(); 1515 } 1516 DBG_NAV_LOGD("node=%p element=%p x=%d y=%d nodeName=%s tagName=%s", node, 1517 element, x, y, node->nodeName().utf8().data(), 1518 element ? ((Element*) element)->tagName().utf8().data() : "<none>"); 1519 return static_cast<WebCore::HTMLElement*>(element); 1520 } 1521 1522 HTMLAnchorElement* WebViewCore::retrieveAnchorElement(int x, int y) 1523 { 1524 return static_cast<HTMLAnchorElement*> 1525 (retrieveElement(x, y, HTMLNames::aTag)); 1526 } 1527 1528 HTMLImageElement* WebViewCore::retrieveImageElement(int x, int y) 1529 { 1530 return static_cast<HTMLImageElement*> 1531 (retrieveElement(x, y, HTMLNames::imgTag)); 1532 } 1533 1534 WTF::String WebViewCore::retrieveHref(int x, int y) 1535 { 1536 WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(x, y); 1537 return anchor ? anchor->href() : WTF::String(); 1538 } 1539 1540 WTF::String WebViewCore::retrieveAnchorText(int x, int y) 1541 { 1542 WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(x, y); 1543 return anchor ? anchor->text() : WTF::String(); 1544 } 1545 1546 WTF::String WebViewCore::retrieveImageSource(int x, int y) 1547 { 1548 HTMLImageElement* image = retrieveImageElement(x, y); 1549 return image ? image->src().string() : WTF::String(); 1550 } 1551 1552 WTF::String WebViewCore::requestLabel(WebCore::Frame* frame, 1553 WebCore::Node* node) 1554 { 1555 if (node && CacheBuilder::validNode(m_mainFrame, frame, node)) { 1556 RefPtr<WebCore::NodeList> list = node->document()->getElementsByTagName("label"); 1557 unsigned length = list->length(); 1558 for (unsigned i = 0; i < length; i++) { 1559 WebCore::HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>( 1560 list->item(i)); 1561 if (label->control() == node) { 1562 Node* node = label; 1563 String result; 1564 while ((node = node->traverseNextNode(label))) { 1565 if (node->isTextNode()) { 1566 Text* textNode = static_cast<Text*>(node); 1567 result += textNode->dataImpl(); 1568 } 1569 } 1570 return result; 1571 } 1572 } 1573 } 1574 return WTF::String(); 1575 } 1576 1577 static bool isContentEditable(const WebCore::Node* node) 1578 { 1579 if (!node) return false; 1580 return node->document()->frame()->selection()->isContentEditable(); 1581 } 1582 1583 // Returns true if the node is a textfield, textarea, or contentEditable 1584 static bool isTextInput(const WebCore::Node* node) 1585 { 1586 if (isContentEditable(node)) 1587 return true; 1588 if (!node) 1589 return false; 1590 WebCore::RenderObject* renderer = node->renderer(); 1591 return renderer && (renderer->isTextField() || renderer->isTextArea()); 1592 } 1593 1594 void WebViewCore::revealSelection() 1595 { 1596 WebCore::Node* focus = currentFocus(); 1597 if (!focus) 1598 return; 1599 if (!isTextInput(focus)) 1600 return; 1601 WebCore::Frame* focusedFrame = focus->document()->frame(); 1602 if (!focusedFrame->page()->focusController()->isActive()) 1603 return; 1604 focusedFrame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded); 1605 } 1606 1607 void WebViewCore::updateCacheOnNodeChange() 1608 { 1609 gCursorBoundsMutex.lock(); 1610 bool hasCursorBounds = m_hasCursorBounds; 1611 Frame* frame = (Frame*) m_cursorFrame; 1612 Node* node = (Node*) m_cursorNode; 1613 IntRect bounds = m_cursorHitBounds; 1614 gCursorBoundsMutex.unlock(); 1615 if (!hasCursorBounds || !node) 1616 return; 1617 if (CacheBuilder::validNode(m_mainFrame, frame, node)) { 1618 RenderObject* renderer = node->renderer(); 1619 if (renderer && renderer->style()->visibility() != HIDDEN) { 1620 IntRect absBox = renderer->absoluteBoundingBoxRect(); 1621 int globalX, globalY; 1622 CacheBuilder::GetGlobalOffset(frame, &globalX, &globalY); 1623 absBox.move(globalX, globalY); 1624 if (absBox == bounds) 1625 return; 1626 DBG_NAV_LOGD("absBox=(%d,%d,%d,%d) bounds=(%d,%d,%d,%d)", 1627 absBox.x(), absBox.y(), absBox.width(), absBox.height(), 1628 bounds.x(), bounds.y(), bounds.width(), bounds.height()); 1629 } 1630 } 1631 DBG_NAV_LOGD("updateFrameCache node=%p", node); 1632 updateFrameCache(); 1633 } 1634 1635 void WebViewCore::updateFrameCache() 1636 { 1637 if (!m_frameCacheOutOfDate) { 1638 DBG_NAV_LOG("!m_frameCacheOutOfDate"); 1639 return; 1640 } 1641 1642 // If there is a pending style recalculation, do not update the frame cache. 1643 // Until the recalculation is complete, there may be internal objects that 1644 // are in an inconsistent state (such as font pointers). 1645 // In any event, there's not much point to updating the cache while a style 1646 // recalculation is pending, since it will simply have to be updated again 1647 // once the recalculation is complete. 1648 // TODO: Do we need to reschedule an update for after the style is recalculated? 1649 if (m_mainFrame && m_mainFrame->document() && m_mainFrame->document()->isPendingStyleRecalc()) { 1650 LOGW("updateFrameCache: pending style recalc, ignoring."); 1651 return; 1652 } 1653 #ifdef ANDROID_INSTRUMENT 1654 TimeCounterAuto counter(TimeCounter::WebViewCoreBuildNavTimeCounter); 1655 #endif 1656 m_frameCacheOutOfDate = false; 1657 m_temp = new CachedRoot(); 1658 m_temp->init(m_mainFrame, &m_history); 1659 #if USE(ACCELERATED_COMPOSITING) 1660 GraphicsLayerAndroid* graphicsLayer = graphicsRootLayer(); 1661 if (graphicsLayer) 1662 m_temp->setRootLayer(graphicsLayer->contentLayer()); 1663 #endif 1664 CacheBuilder& builder = cacheBuilder(); 1665 WebCore::Settings* settings = m_mainFrame->page()->settings(); 1666 builder.allowAllTextDetection(); 1667 #ifdef ANDROID_META_SUPPORT 1668 if (settings) { 1669 if (!settings->formatDetectionAddress()) 1670 builder.disallowAddressDetection(); 1671 if (!settings->formatDetectionEmail()) 1672 builder.disallowEmailDetection(); 1673 if (!settings->formatDetectionTelephone()) 1674 builder.disallowPhoneDetection(); 1675 } 1676 #endif 1677 builder.buildCache(m_temp); 1678 m_tempPict = new SkPicture(); 1679 recordPicture(m_tempPict); 1680 m_temp->setPicture(m_tempPict); 1681 m_temp->setTextGeneration(m_textGeneration); 1682 WebCoreViewBridge* window = m_mainFrame->view()->platformWidget(); 1683 m_temp->setVisibleRect(WebCore::IntRect(m_scrollOffsetX, 1684 m_scrollOffsetY, window->width(), window->height())); 1685 gFrameCacheMutex.lock(); 1686 delete m_frameCacheKit; 1687 delete m_navPictureKit; 1688 m_frameCacheKit = m_temp; 1689 m_navPictureKit = m_tempPict; 1690 m_updatedFrameCache = true; 1691 #if DEBUG_NAV_UI 1692 const CachedNode* cachedFocusNode = m_frameCacheKit->currentFocus(); 1693 DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p)", 1694 cachedFocusNode ? cachedFocusNode->index() : 0, 1695 cachedFocusNode ? cachedFocusNode->nodePointer() : 0); 1696 #endif 1697 gFrameCacheMutex.unlock(); 1698 } 1699 1700 void WebViewCore::updateFrameCacheIfLoading() 1701 { 1702 if (!m_check_domtree_version) 1703 updateFrameCache(); 1704 } 1705 1706 struct TouchNodeData { 1707 Node* mNode; 1708 IntRect mBounds; 1709 }; 1710 1711 // get the bounding box of the Node 1712 static IntRect getAbsoluteBoundingBox(Node* node) { 1713 IntRect rect; 1714 RenderObject* render = node->renderer(); 1715 if (render->isRenderInline()) 1716 rect = toRenderInline(render)->linesVisualOverflowBoundingBox(); 1717 else if (render->isBox()) 1718 rect = toRenderBox(render)->visualOverflowRect(); 1719 else if (render->isText()) 1720 rect = toRenderText(render)->linesBoundingBox(); 1721 else 1722 LOGE("getAbsoluteBoundingBox failed for node %p, name %s", node, render->renderName()); 1723 FloatPoint absPos = render->localToAbsolute(); 1724 rect.move(absPos.x(), absPos.y()); 1725 return rect; 1726 } 1727 1728 // get the highlight rectangles for the touch point (x, y) with the slop 1729 Vector<IntRect> WebViewCore::getTouchHighlightRects(int x, int y, int slop) 1730 { 1731 Vector<IntRect> rects; 1732 m_mousePos = IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY); 1733 HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y), 1734 false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(slop, slop)); 1735 if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) { 1736 LOGE("Should not happen: no in document Node found"); 1737 return rects; 1738 } 1739 const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult(); 1740 if (list.isEmpty()) { 1741 LOGE("Should not happen: no rect-based-test nodes found"); 1742 return rects; 1743 } 1744 Frame* frame = hitTestResult.innerNode()->document()->frame(); 1745 Vector<TouchNodeData> nodeDataList; 1746 ListHashSet<RefPtr<Node> >::const_iterator last = list.end(); 1747 for (ListHashSet<RefPtr<Node> >::const_iterator it = list.begin(); it != last; ++it) { 1748 // TODO: it seems reasonable to not search across the frame. Isn't it? 1749 // if the node is not in the same frame as the innerNode, skip it 1750 if (it->get()->document()->frame() != frame) 1751 continue; 1752 // traverse up the tree to find the first node that needs highlight 1753 bool found = false; 1754 Node* eventNode = it->get(); 1755 while (eventNode) { 1756 RenderObject* render = eventNode->renderer(); 1757 if (render && (render->isBody() || render->isRenderView())) 1758 break; 1759 if (eventNode->supportsFocus() 1760 || eventNode->hasEventListeners(eventNames().clickEvent) 1761 || eventNode->hasEventListeners(eventNames().mousedownEvent) 1762 || eventNode->hasEventListeners(eventNames().mouseupEvent)) { 1763 found = true; 1764 break; 1765 } 1766 // the nodes in the rectBasedTestResult() are ordered based on z-index during hit testing. 1767 // so do not search for the eventNode across explicit z-index border. 1768 // TODO: this is a hard one to call. z-index is quite complicated as its value only 1769 // matters when you compare two RenderLayer in the same hierarchy level. e.g. in 1770 // the following example, "b" is on the top as its z level is the highest. even "c" 1771 // has 100 as z-index, it is still below "d" as its parent has the same z-index as 1772 // "d" and logically before "d". Of course "a" is the lowest in the z level. 1773 // 1774 // z-index:auto "a" 1775 // z-index:2 "b" 1776 // z-index:1 1777 // z-index:100 "c" 1778 // z-index:1 "d" 1779 // 1780 // If the fat point touches everyone, the order in the list should be "b", "d", "c" 1781 // and "a". When we search for the event node for "b", we really don't want "a" as 1782 // in the z-order it is behind everything else. 1783 if (render && !render->style()->hasAutoZIndex()) 1784 break; 1785 eventNode = eventNode->parentNode(); 1786 } 1787 // didn't find any eventNode, skip it 1788 if (!found) 1789 continue; 1790 // first quick check whether it is a duplicated node before computing bounding box 1791 Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end(); 1792 for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) { 1793 // found the same node, skip it 1794 if (eventNode == n->mNode) { 1795 found = false; 1796 break; 1797 } 1798 } 1799 if (!found) 1800 continue; 1801 // next check whether the node is fully covered by or fully covering another node. 1802 found = false; 1803 IntRect rect = getAbsoluteBoundingBox(eventNode); 1804 if (rect.isEmpty()) { 1805 // if the node's bounds is empty and it is not a ContainerNode, skip it. 1806 if (!eventNode->isContainerNode()) 1807 continue; 1808 // if the node's children are all positioned objects, its bounds can be empty. 1809 // Walk through the children to find the bounding box. 1810 Node* child = static_cast<const ContainerNode*>(eventNode)->firstChild(); 1811 while (child) { 1812 IntRect childrect; 1813 if (child->renderer()) 1814 childrect = getAbsoluteBoundingBox(child); 1815 if (!childrect.isEmpty()) { 1816 rect.unite(childrect); 1817 child = child->traverseNextSibling(eventNode); 1818 } else 1819 child = child->traverseNextNode(eventNode); 1820 } 1821 } 1822 for (int i = nodeDataList.size() - 1; i >= 0; i--) { 1823 TouchNodeData n = nodeDataList.at(i); 1824 // the new node is enclosing an existing node, skip it 1825 if (rect.contains(n.mBounds)) { 1826 found = true; 1827 break; 1828 } 1829 // the new node is fully inside an existing node, remove the existing node 1830 if (n.mBounds.contains(rect)) 1831 nodeDataList.remove(i); 1832 } 1833 if (!found) { 1834 TouchNodeData newNode; 1835 newNode.mNode = eventNode; 1836 newNode.mBounds = rect; 1837 nodeDataList.append(newNode); 1838 } 1839 } 1840 if (!nodeDataList.size()) 1841 return rects; 1842 // finally select the node with the largest overlap with the fat point 1843 TouchNodeData final; 1844 final.mNode = 0; 1845 IntPoint docPos = frame->view()->windowToContents(m_mousePos); 1846 IntRect testRect(docPos.x() - slop, docPos.y() - slop, 2 * slop + 1, 2 * slop + 1); 1847 int area = 0; 1848 Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end(); 1849 for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) { 1850 IntRect rect = n->mBounds; 1851 rect.intersect(testRect); 1852 int a = rect.width() * rect.height(); 1853 if (a > area) { 1854 final = *n; 1855 area = a; 1856 } 1857 } 1858 // now get the node's highlight rectangles in the page coordinate system 1859 if (final.mNode) { 1860 IntPoint frameAdjust; 1861 if (frame != m_mainFrame) { 1862 frameAdjust = frame->view()->contentsToWindow(IntPoint()); 1863 frameAdjust.move(m_scrollOffsetX, m_scrollOffsetY); 1864 } 1865 if (final.mNode->isLink()) { 1866 // most of the links are inline instead of box style. So the bounding box is not 1867 // a good representation for the highlights. Get the list of rectangles instead. 1868 RenderObject* render = final.mNode->renderer(); 1869 IntPoint offset = roundedIntPoint(render->localToAbsolute()); 1870 render->absoluteRects(rects, offset.x() + frameAdjust.x(), offset.y() + frameAdjust.y()); 1871 bool inside = false; 1872 int distance = INT_MAX; 1873 int newx = x, newy = y; 1874 int i = rects.size(); 1875 while (i--) { 1876 if (rects[i].isEmpty()) { 1877 rects.remove(i); 1878 continue; 1879 } 1880 // check whether the point (x, y) is inside one of the rectangles. 1881 if (inside) 1882 continue; 1883 if (rects[i].contains(x, y)) { 1884 inside = true; 1885 continue; 1886 } 1887 if (x >= rects[i].x() && x < rects[i].maxX()) { 1888 if (y < rects[i].y()) { 1889 if (rects[i].y() - y < distance) { 1890 newx = x; 1891 newy = rects[i].y(); 1892 distance = rects[i].y() - y; 1893 } 1894 } else if (y >= rects[i].maxY()) { 1895 if (y - rects[i].maxY() + 1 < distance) { 1896 newx = x; 1897 newy = rects[i].maxY() - 1; 1898 distance = y - rects[i].maxY() + 1; 1899 } 1900 } 1901 } else if (y >= rects[i].y() && y < rects[i].maxY()) { 1902 if (x < rects[i].x()) { 1903 if (rects[i].x() - x < distance) { 1904 newx = rects[i].x(); 1905 newy = y; 1906 distance = rects[i].x() - x; 1907 } 1908 } else if (x >= rects[i].maxX()) { 1909 if (x - rects[i].maxX() + 1 < distance) { 1910 newx = rects[i].maxX() - 1; 1911 newy = y; 1912 distance = x - rects[i].maxX() + 1; 1913 } 1914 } 1915 } 1916 } 1917 if (!rects.isEmpty()) { 1918 if (!inside) { 1919 // if neither x nor y has overlap, just pick the top/left of the first rectangle 1920 if (newx == x && newy == y) { 1921 newx = rects[0].x(); 1922 newy = rects[0].y(); 1923 } 1924 m_mousePos.setX(newx - m_scrollOffsetX); 1925 m_mousePos.setY(newy - m_scrollOffsetY); 1926 DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)", 1927 x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, 1928 m_scrollOffsetX, m_scrollOffsetY); 1929 } 1930 return rects; 1931 } 1932 } 1933 IntRect rect = final.mBounds; 1934 rect.move(frameAdjust.x(), frameAdjust.y()); 1935 rects.append(rect); 1936 // adjust m_mousePos if it is not inside the returned highlight rectangle 1937 testRect.move(frameAdjust.x(), frameAdjust.y()); 1938 testRect.intersect(rect); 1939 if (!testRect.contains(x, y)) { 1940 m_mousePos = testRect.center(); 1941 m_mousePos.move(-m_scrollOffsetX, -m_scrollOffsetY); 1942 DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)", 1943 x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, 1944 m_scrollOffsetX, m_scrollOffsetY); 1945 } 1946 } 1947 return rects; 1948 } 1949 1950 /////////////////////////////////////////////////////////////////////////////// 1951 1952 void WebViewCore::addPlugin(PluginWidgetAndroid* w) 1953 { 1954 // SkDebugf("----------- addPlugin %p", w); 1955 /* The plugin must be appended to the end of the array. This ensures that if 1956 the plugin is added while iterating through the array (e.g. sendEvent(...)) 1957 that the iteration process is not corrupted. 1958 */ 1959 *m_plugins.append() = w; 1960 } 1961 1962 void WebViewCore::removePlugin(PluginWidgetAndroid* w) 1963 { 1964 // SkDebugf("----------- removePlugin %p", w); 1965 int index = m_plugins.find(w); 1966 if (index < 0) { 1967 SkDebugf("--------------- pluginwindow not found! %p\n", w); 1968 } else { 1969 m_plugins.removeShuffle(index); 1970 } 1971 } 1972 1973 bool WebViewCore::isPlugin(PluginWidgetAndroid* w) const 1974 { 1975 return m_plugins.find(w) >= 0; 1976 } 1977 1978 void WebViewCore::invalPlugin(PluginWidgetAndroid* w) 1979 { 1980 const double PLUGIN_INVAL_DELAY = 1.0 / 60; 1981 1982 if (!m_pluginInvalTimer.isActive()) { 1983 m_pluginInvalTimer.startOneShot(PLUGIN_INVAL_DELAY); 1984 } 1985 } 1986 1987 void WebViewCore::drawPlugins() 1988 { 1989 SkRegion inval; // accumualte what needs to be redrawn 1990 PluginWidgetAndroid** iter = m_plugins.begin(); 1991 PluginWidgetAndroid** stop = m_plugins.end(); 1992 1993 for (; iter < stop; ++iter) { 1994 PluginWidgetAndroid* w = *iter; 1995 SkIRect dirty; 1996 if (w->isDirty(&dirty)) { 1997 w->draw(); 1998 inval.op(dirty, SkRegion::kUnion_Op); 1999 } 2000 } 2001 2002 if (!inval.isEmpty()) { 2003 // inval.getBounds() is our rectangle 2004 const SkIRect& bounds = inval.getBounds(); 2005 WebCore::IntRect r(bounds.fLeft, bounds.fTop, 2006 bounds.width(), bounds.height()); 2007 this->viewInvalidate(r); 2008 } 2009 } 2010 2011 void WebViewCore::notifyPluginsOnFrameLoad(const Frame* frame) { 2012 // if frame is the parent then notify all plugins 2013 if (!frame->tree()->parent()) { 2014 // trigger an event notifying the plugins that the page has loaded 2015 ANPEvent event; 2016 SkANP::InitEvent(&event, kLifecycle_ANPEventType); 2017 event.data.lifecycle.action = kOnLoad_ANPLifecycleAction; 2018 sendPluginEvent(event); 2019 // trigger the on/off screen notification if the page was reloaded 2020 sendPluginVisibleScreen(); 2021 } 2022 // else if frame's parent has completed 2023 else if (!frame->tree()->parent()->loader()->isLoading()) { 2024 // send to all plugins who have this frame in their heirarchy 2025 PluginWidgetAndroid** iter = m_plugins.begin(); 2026 PluginWidgetAndroid** stop = m_plugins.end(); 2027 for (; iter < stop; ++iter) { 2028 Frame* currentFrame = (*iter)->pluginView()->parentFrame(); 2029 while (currentFrame) { 2030 if (frame == currentFrame) { 2031 ANPEvent event; 2032 SkANP::InitEvent(&event, kLifecycle_ANPEventType); 2033 event.data.lifecycle.action = kOnLoad_ANPLifecycleAction; 2034 (*iter)->sendEvent(event); 2035 2036 // trigger the on/off screen notification if the page was reloaded 2037 ANPRectI visibleRect; 2038 getVisibleScreen(visibleRect); 2039 (*iter)->setVisibleScreen(visibleRect, m_scale); 2040 2041 break; 2042 } 2043 currentFrame = currentFrame->tree()->parent(); 2044 } 2045 } 2046 } 2047 } 2048 2049 void WebViewCore::getVisibleScreen(ANPRectI& visibleRect) 2050 { 2051 visibleRect.left = m_scrollOffsetX; 2052 visibleRect.top = m_scrollOffsetY; 2053 visibleRect.right = m_scrollOffsetX + m_screenWidth; 2054 visibleRect.bottom = m_scrollOffsetY + m_screenHeight; 2055 } 2056 2057 void WebViewCore::sendPluginVisibleScreen() 2058 { 2059 /* We may want to cache the previous values and only send the notification 2060 to the plugin in the event that one of the values has changed. 2061 */ 2062 2063 ANPRectI visibleRect; 2064 getVisibleScreen(visibleRect); 2065 2066 PluginWidgetAndroid** iter = m_plugins.begin(); 2067 PluginWidgetAndroid** stop = m_plugins.end(); 2068 for (; iter < stop; ++iter) { 2069 (*iter)->setVisibleScreen(visibleRect, m_scale); 2070 } 2071 } 2072 2073 void WebViewCore::sendPluginSurfaceReady() 2074 { 2075 PluginWidgetAndroid** iter = m_plugins.begin(); 2076 PluginWidgetAndroid** stop = m_plugins.end(); 2077 for (; iter < stop; ++iter) { 2078 (*iter)->checkSurfaceReady(); 2079 } 2080 } 2081 2082 void WebViewCore::sendPluginEvent(const ANPEvent& evt) 2083 { 2084 /* The list of plugins may be manipulated as we iterate through the list. 2085 This implementation allows for the addition of new plugins during an 2086 iteration, but may fail if a plugin is removed. Currently, there are not 2087 any use cases where a plugin is deleted while processing this loop, but 2088 if it does occur we will have to use an alternate data structure and/or 2089 iteration mechanism. 2090 */ 2091 for (int x = 0; x < m_plugins.count(); x++) { 2092 m_plugins[x]->sendEvent(evt); 2093 } 2094 } 2095 2096 PluginWidgetAndroid* WebViewCore::getPluginWidget(NPP npp) 2097 { 2098 PluginWidgetAndroid** iter = m_plugins.begin(); 2099 PluginWidgetAndroid** stop = m_plugins.end(); 2100 for (; iter < stop; ++iter) { 2101 if ((*iter)->pluginView()->instance() == npp) { 2102 return (*iter); 2103 } 2104 } 2105 return 0; 2106 } 2107 2108 static PluginView* nodeIsPlugin(Node* node) { 2109 RenderObject* renderer = node->renderer(); 2110 if (renderer && renderer->isWidget()) { 2111 Widget* widget = static_cast<RenderWidget*>(renderer)->widget(); 2112 if (widget && widget->isPluginView()) 2113 return static_cast<PluginView*>(widget); 2114 } 2115 return 0; 2116 } 2117 2118 Node* WebViewCore::cursorNodeIsPlugin() { 2119 gCursorBoundsMutex.lock(); 2120 bool hasCursorBounds = m_hasCursorBounds; 2121 Frame* frame = (Frame*) m_cursorFrame; 2122 Node* node = (Node*) m_cursorNode; 2123 gCursorBoundsMutex.unlock(); 2124 if (hasCursorBounds && CacheBuilder::validNode(m_mainFrame, frame, node) 2125 && nodeIsPlugin(node)) { 2126 return node; 2127 } 2128 return 0; 2129 } 2130 2131 /////////////////////////////////////////////////////////////////////////////// 2132 void WebViewCore::moveMouseIfLatest(int moveGeneration, 2133 WebCore::Frame* frame, int x, int y) 2134 { 2135 DBG_NAV_LOGD("m_moveGeneration=%d moveGeneration=%d" 2136 " frame=%p x=%d y=%d", 2137 m_moveGeneration, moveGeneration, frame, x, y); 2138 if (m_moveGeneration > moveGeneration) { 2139 DBG_NAV_LOGD("m_moveGeneration=%d > moveGeneration=%d", 2140 m_moveGeneration, moveGeneration); 2141 return; // short-circuit if a newer move has already been generated 2142 } 2143 m_lastGeneration = moveGeneration; 2144 moveMouse(frame, x, y); 2145 } 2146 2147 void WebViewCore::moveFocus(WebCore::Frame* frame, WebCore::Node* node) 2148 { 2149 DBG_NAV_LOGD("frame=%p node=%p", frame, node); 2150 if (!node || !CacheBuilder::validNode(m_mainFrame, frame, node) 2151 || !node->isElementNode()) 2152 return; 2153 // Code borrowed from FocusController::advanceFocus 2154 WebCore::FocusController* focusController 2155 = m_mainFrame->page()->focusController(); 2156 WebCore::Document* oldDoc 2157 = focusController->focusedOrMainFrame()->document(); 2158 if (oldDoc->focusedNode() == node) 2159 return; 2160 if (node->document() != oldDoc) 2161 oldDoc->setFocusedNode(0); 2162 focusController->setFocusedFrame(frame); 2163 static_cast<WebCore::Element*>(node)->focus(false); 2164 } 2165 2166 // Update mouse position 2167 void WebViewCore::moveMouse(WebCore::Frame* frame, int x, int y) 2168 { 2169 DBG_NAV_LOGD("frame=%p x=%d y=%d scrollOffset=(%d,%d)", frame, 2170 x, y, m_scrollOffsetX, m_scrollOffsetY); 2171 if (!frame || !CacheBuilder::validNode(m_mainFrame, frame, 0)) 2172 frame = m_mainFrame; 2173 // mouse event expects the position in the window coordinate 2174 m_mousePos = WebCore::IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY); 2175 // validNode will still return true if the node is null, as long as we have 2176 // a valid frame. Do not want to make a call on frame unless it is valid. 2177 WebCore::PlatformMouseEvent mouseEvent(m_mousePos, m_mousePos, 2178 WebCore::NoButton, WebCore::MouseEventMoved, 1, false, false, false, 2179 false, WTF::currentTime()); 2180 frame->eventHandler()->handleMouseMoveEvent(mouseEvent); 2181 updateCacheOnNodeChange(); 2182 } 2183 2184 void WebViewCore::setSelection(int start, int end) 2185 { 2186 WebCore::Node* focus = currentFocus(); 2187 if (!focus) 2188 return; 2189 WebCore::RenderObject* renderer = focus->renderer(); 2190 if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) 2191 return; 2192 if (start > end) { 2193 int temp = start; 2194 start = end; 2195 end = temp; 2196 } 2197 // Tell our EditorClient that this change was generated from the UI, so it 2198 // does not need to echo it to the UI. 2199 EditorClientAndroid* client = static_cast<EditorClientAndroid*>( 2200 m_mainFrame->editor()->client()); 2201 client->setUiGeneratedSelectionChange(true); 2202 setSelectionRange(focus, start, end); 2203 if (start != end) { 2204 // Fire a select event. No event is sent when the selection reduces to 2205 // an insertion point 2206 RenderTextControl* control = toRenderTextControl(renderer); 2207 control->selectionChanged(true); 2208 } 2209 client->setUiGeneratedSelectionChange(false); 2210 WebCore::Frame* focusedFrame = focus->document()->frame(); 2211 bool isPasswordField = false; 2212 if (focus->isElementNode()) { 2213 WebCore::Element* element = static_cast<WebCore::Element*>(focus); 2214 if (WebCore::InputElement* inputElement = element->toInputElement()) 2215 isPasswordField = static_cast<WebCore::HTMLInputElement*>(inputElement)->isPasswordField(); 2216 } 2217 // For password fields, this is done in the UI side via 2218 // bringPointIntoView, since the UI does the drawing. 2219 if (renderer->isTextArea() || !isPasswordField) 2220 revealSelection(); 2221 } 2222 2223 String WebViewCore::modifySelection(const int direction, const int axis) 2224 { 2225 DOMSelection* selection = m_mainFrame->domWindow()->getSelection(); 2226 ASSERT(selection); 2227 // We've seen crashes where selection is null, but we don't know why 2228 // See http://b/5244036 2229 if (!selection) 2230 return String(); 2231 if (selection->rangeCount() > 1) 2232 selection->removeAllRanges(); 2233 switch (axis) { 2234 case AXIS_CHARACTER: 2235 case AXIS_WORD: 2236 case AXIS_SENTENCE: 2237 return modifySelectionTextNavigationAxis(selection, direction, axis); 2238 case AXIS_HEADING: 2239 case AXIS_SIBLING: 2240 case AXIS_PARENT_FIRST_CHILD: 2241 case AXIS_DOCUMENT: 2242 return modifySelectionDomNavigationAxis(selection, direction, axis); 2243 default: 2244 LOGE("Invalid navigation axis: %d", axis); 2245 return String(); 2246 } 2247 } 2248 2249 void WebViewCore::scrollNodeIntoView(Frame* frame, Node* node) 2250 { 2251 if (!frame || !node) 2252 return; 2253 2254 Element* elementNode = 0; 2255 2256 // If not an Element, find a visible predecessor 2257 // Element to scroll into view. 2258 if (!node->isElementNode()) { 2259 HTMLElement* body = frame->document()->body(); 2260 do { 2261 if (node == body) 2262 return; 2263 node = node->parentNode(); 2264 } while (node && !node->isElementNode() && !isVisible(node)); 2265 } 2266 2267 // Couldn't find a visible predecessor. 2268 if (!node) 2269 return; 2270 2271 elementNode = static_cast<Element*>(node); 2272 elementNode->scrollIntoViewIfNeeded(true); 2273 } 2274 2275 String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int axis) 2276 { 2277 Node* body = m_mainFrame->document()->body(); 2278 2279 ExceptionCode ec = 0; 2280 String markup; 2281 2282 // initialize the selection if necessary 2283 if (selection->rangeCount() == 0) { 2284 if (m_currentNodeDomNavigationAxis 2285 && CacheBuilder::validNode(m_mainFrame, 2286 m_mainFrame, m_currentNodeDomNavigationAxis)) { 2287 PassRefPtr<Range> rangeRef = 2288 selection->frame()->document()->createRange(); 2289 rangeRef->selectNode(m_currentNodeDomNavigationAxis, ec); 2290 m_currentNodeDomNavigationAxis = 0; 2291 if (ec) 2292 return String(); 2293 selection->addRange(rangeRef.get()); 2294 } else if (currentFocus()) { 2295 selection->setPosition(currentFocus(), 0, ec); 2296 } else if (m_cursorNode 2297 && CacheBuilder::validNode(m_mainFrame, 2298 m_mainFrame, m_cursorNode)) { 2299 PassRefPtr<Range> rangeRef = 2300 selection->frame()->document()->createRange(); 2301 rangeRef->selectNode(reinterpret_cast<Node*>(m_cursorNode), ec); 2302 if (ec) 2303 return String(); 2304 selection->addRange(rangeRef.get()); 2305 } else { 2306 selection->setPosition(body, 0, ec); 2307 } 2308 if (ec) 2309 return String(); 2310 } 2311 2312 // collapse the selection 2313 if (direction == DIRECTION_FORWARD) 2314 selection->collapseToEnd(ec); 2315 else 2316 selection->collapseToStart(ec); 2317 if (ec) 2318 return String(); 2319 2320 // Make sure the anchor node is a text node since we are generating 2321 // the markup of the selection which includes the anchor, the focus, 2322 // and any crossed nodes. Forcing the condition that the selection 2323 // starts and ends on text nodes guarantees symmetric selection markup. 2324 // Also this way the text content, rather its container, is highlighted. 2325 Node* anchorNode = selection->anchorNode(); 2326 if (anchorNode->isElementNode()) { 2327 // Collapsed selection while moving forward points to the 2328 // next unvisited node and while moving backward to the 2329 // last visited node. 2330 if (direction == DIRECTION_FORWARD) 2331 advanceAnchorNode(selection, direction, markup, false, ec); 2332 else 2333 advanceAnchorNode(selection, direction, markup, true, ec); 2334 if (ec) 2335 return String(); 2336 if (!markup.isEmpty()) 2337 return markup; 2338 } 2339 2340 // If the selection is at the end of a non white space text move 2341 // it to the next visible text node with non white space content. 2342 // This is a workaround for the selection getting stuck. 2343 anchorNode = selection->anchorNode(); 2344 if (anchorNode->isTextNode()) { 2345 if (direction == DIRECTION_FORWARD) { 2346 String suffix = anchorNode->textContent().substring( 2347 selection->anchorOffset(), caretMaxOffset(anchorNode)); 2348 // If at the end of non white space text we advance the 2349 // anchor node to either an input element or non empty text. 2350 if (suffix.stripWhiteSpace().isEmpty()) { 2351 advanceAnchorNode(selection, direction, markup, true, ec); 2352 } 2353 } else { 2354 String prefix = anchorNode->textContent().substring(0, 2355 selection->anchorOffset()); 2356 // If at the end of non white space text we advance the 2357 // anchor node to either an input element or non empty text. 2358 if (prefix.stripWhiteSpace().isEmpty()) { 2359 advanceAnchorNode(selection, direction, markup, true, ec); 2360 } 2361 } 2362 if (ec) 2363 return String(); 2364 if (!markup.isEmpty()) 2365 return markup; 2366 } 2367 2368 // extend the selection 2369 String directionStr; 2370 if (direction == DIRECTION_FORWARD) 2371 directionStr = "forward"; 2372 else 2373 directionStr = "backward"; 2374 2375 String axisStr; 2376 if (axis == AXIS_CHARACTER) 2377 axisStr = "character"; 2378 else if (axis == AXIS_WORD) 2379 axisStr = "word"; 2380 else 2381 axisStr = "sentence"; 2382 2383 selection->modify("extend", directionStr, axisStr); 2384 2385 // Make sure the focus node is a text node in order to have the 2386 // selection generate symmetric markup because the latter 2387 // includes all nodes crossed by the selection. Also this way 2388 // the text content, rather its container, is highlighted. 2389 Node* focusNode = selection->focusNode(); 2390 if (focusNode->isElementNode()) { 2391 focusNode = getImplicitBoundaryNode(selection->focusNode(), 2392 selection->focusOffset(), direction); 2393 if (!focusNode) 2394 return String(); 2395 if (direction == DIRECTION_FORWARD) { 2396 focusNode = focusNode->traversePreviousSiblingPostOrder(body); 2397 if (focusNode && !isContentTextNode(focusNode)) { 2398 Node* textNode = traverseNextContentTextNode(focusNode, 2399 anchorNode, DIRECTION_BACKWARD); 2400 if (textNode) 2401 anchorNode = textNode; 2402 } 2403 if (focusNode && isContentTextNode(focusNode)) { 2404 selection->extend(focusNode, caretMaxOffset(focusNode), ec); 2405 if (ec) 2406 return String(); 2407 } 2408 } else { 2409 focusNode = focusNode->traverseNextSibling(); 2410 if (focusNode && !isContentTextNode(focusNode)) { 2411 Node* textNode = traverseNextContentTextNode(focusNode, 2412 anchorNode, DIRECTION_FORWARD); 2413 if (textNode) 2414 anchorNode = textNode; 2415 } 2416 if (anchorNode && isContentTextNode(anchorNode)) { 2417 selection->extend(focusNode, 0, ec); 2418 if (ec) 2419 return String(); 2420 } 2421 } 2422 } 2423 2424 // Enforce that the selection does not cross anchor boundaries. This is 2425 // a workaround for the asymmetric behavior of WebKit while crossing 2426 // anchors. 2427 anchorNode = getImplicitBoundaryNode(selection->anchorNode(), 2428 selection->anchorOffset(), direction); 2429 focusNode = getImplicitBoundaryNode(selection->focusNode(), 2430 selection->focusOffset(), direction); 2431 if (anchorNode && focusNode && anchorNode != focusNode) { 2432 Node* inputControl = getIntermediaryInputElement(anchorNode, focusNode, 2433 direction); 2434 if (inputControl) { 2435 if (direction == DIRECTION_FORWARD) { 2436 if (isDescendantOf(inputControl, anchorNode)) { 2437 focusNode = inputControl; 2438 } else { 2439 focusNode = inputControl->traversePreviousSiblingPostOrder( 2440 body); 2441 if (!focusNode) 2442 focusNode = inputControl; 2443 } 2444 // We prefer a text node contained in the input element. 2445 if (!isContentTextNode(focusNode)) { 2446 Node* textNode = traverseNextContentTextNode(focusNode, 2447 anchorNode, DIRECTION_BACKWARD); 2448 if (textNode) 2449 focusNode = textNode; 2450 } 2451 // If we found text in the input select it. 2452 // Otherwise, select the input element itself. 2453 if (isContentTextNode(focusNode)) { 2454 selection->extend(focusNode, caretMaxOffset(focusNode), ec); 2455 } else if (anchorNode != focusNode) { 2456 // Note that the focusNode always has parent and that 2457 // the offset can be one more that the index of the last 2458 // element - this is how WebKit selects such elements. 2459 selection->extend(focusNode->parentNode(), 2460 focusNode->nodeIndex() + 1, ec); 2461 } 2462 if (ec) 2463 return String(); 2464 } else { 2465 if (isDescendantOf(inputControl, anchorNode)) { 2466 focusNode = inputControl; 2467 } else { 2468 focusNode = inputControl->traverseNextSibling(); 2469 if (!focusNode) 2470 focusNode = inputControl; 2471 } 2472 // We prefer a text node contained in the input element. 2473 if (!isContentTextNode(focusNode)) { 2474 Node* textNode = traverseNextContentTextNode(focusNode, 2475 anchorNode, DIRECTION_FORWARD); 2476 if (textNode) 2477 focusNode = textNode; 2478 } 2479 // If we found text in the input select it. 2480 // Otherwise, select the input element itself. 2481 if (isContentTextNode(focusNode)) { 2482 selection->extend(focusNode, caretMinOffset(focusNode), ec); 2483 } else if (anchorNode != focusNode) { 2484 // Note that the focusNode always has parent and that 2485 // the offset can be one more that the index of the last 2486 // element - this is how WebKit selects such elements. 2487 selection->extend(focusNode->parentNode(), 2488 focusNode->nodeIndex() + 1, ec); 2489 } 2490 if (ec) 2491 return String(); 2492 } 2493 } 2494 } 2495 2496 // make sure the selection is visible 2497 if (direction == DIRECTION_FORWARD) 2498 scrollNodeIntoView(m_mainFrame, selection->focusNode()); 2499 else 2500 scrollNodeIntoView(m_mainFrame, selection->anchorNode()); 2501 2502 // format markup for the visible content 2503 PassRefPtr<Range> range = selection->getRangeAt(0, ec); 2504 if (ec) 2505 return String(); 2506 IntRect bounds = range->boundingBox(); 2507 selectAt(bounds.center().x(), bounds.center().y()); 2508 markup = formatMarkup(selection); 2509 LOGV("Selection markup: %s", markup.utf8().data()); 2510 2511 return markup; 2512 } 2513 2514 Node* WebViewCore::getImplicitBoundaryNode(Node* node, unsigned offset, int direction) 2515 { 2516 if (node->offsetInCharacters()) 2517 return node; 2518 if (!node->hasChildNodes()) 2519 return node; 2520 if (offset < node->childNodeCount()) 2521 return node->childNode(offset); 2522 else 2523 if (direction == DIRECTION_FORWARD) 2524 return node->traverseNextSibling(); 2525 else 2526 return node->traversePreviousNodePostOrder( 2527 node->document()->body()); 2528 } 2529 2530 Node* WebViewCore::getNextAnchorNode(Node* anchorNode, bool ignoreFirstNode, int direction) 2531 { 2532 Node* body = 0; 2533 Node* currentNode = 0; 2534 if (direction == DIRECTION_FORWARD) { 2535 if (ignoreFirstNode) 2536 currentNode = anchorNode->traverseNextNode(body); 2537 else 2538 currentNode = anchorNode; 2539 } else { 2540 body = anchorNode->document()->body(); 2541 if (ignoreFirstNode) 2542 currentNode = anchorNode->traversePreviousSiblingPostOrder(body); 2543 else 2544 currentNode = anchorNode; 2545 } 2546 while (currentNode) { 2547 if (isContentTextNode(currentNode) 2548 || isContentInputElement(currentNode)) 2549 return currentNode; 2550 if (direction == DIRECTION_FORWARD) 2551 currentNode = currentNode->traverseNextNode(); 2552 else 2553 currentNode = currentNode->traversePreviousNodePostOrder(body); 2554 } 2555 return 0; 2556 } 2557 2558 void WebViewCore::advanceAnchorNode(DOMSelection* selection, int direction, 2559 String& markup, bool ignoreFirstNode, ExceptionCode& ec) 2560 { 2561 Node* anchorNode = getImplicitBoundaryNode(selection->anchorNode(), 2562 selection->anchorOffset(), direction); 2563 if (!anchorNode) { 2564 ec = NOT_FOUND_ERR; 2565 return; 2566 } 2567 // If the anchor offset is invalid i.e. the anchor node has no 2568 // child with that index getImplicitAnchorNode returns the next 2569 // logical node in the current direction. In such a case our 2570 // position in the DOM tree was has already been advanced, 2571 // therefore we there is no need to do that again. 2572 if (selection->anchorNode()->isElementNode()) { 2573 unsigned anchorOffset = selection->anchorOffset(); 2574 unsigned childNodeCount = selection->anchorNode()->childNodeCount(); 2575 if (anchorOffset >= childNodeCount) 2576 ignoreFirstNode = false; 2577 } 2578 // Find the next anchor node given our position in the DOM and 2579 // whether we want the current node to be considered as well. 2580 Node* nextAnchorNode = getNextAnchorNode(anchorNode, ignoreFirstNode, 2581 direction); 2582 if (!nextAnchorNode) { 2583 ec = NOT_FOUND_ERR; 2584 return; 2585 } 2586 if (nextAnchorNode->isElementNode()) { 2587 // If this is an input element tell the WebView thread 2588 // to set the cursor to that control. 2589 if (isContentInputElement(nextAnchorNode)) { 2590 IntRect bounds = nextAnchorNode->getRect(); 2591 selectAt(bounds.center().x(), bounds.center().y()); 2592 } 2593 Node* textNode = 0; 2594 // Treat the text content of links as any other text but 2595 // for the rest input elements select the control itself. 2596 if (nextAnchorNode->hasTagName(WebCore::HTMLNames::aTag)) 2597 textNode = traverseNextContentTextNode(nextAnchorNode, 2598 nextAnchorNode, direction); 2599 // We prefer to select the text content of the link if such, 2600 // otherwise just select the element itself. 2601 if (textNode) { 2602 nextAnchorNode = textNode; 2603 } else { 2604 if (direction == DIRECTION_FORWARD) { 2605 selection->setBaseAndExtent(nextAnchorNode, 2606 caretMinOffset(nextAnchorNode), nextAnchorNode, 2607 caretMaxOffset(nextAnchorNode), ec); 2608 } else { 2609 selection->setBaseAndExtent(nextAnchorNode, 2610 caretMaxOffset(nextAnchorNode), nextAnchorNode, 2611 caretMinOffset(nextAnchorNode), ec); 2612 } 2613 if (!ec) 2614 markup = formatMarkup(selection); 2615 // make sure the selection is visible 2616 scrollNodeIntoView(selection->frame(), nextAnchorNode); 2617 return; 2618 } 2619 } 2620 if (direction == DIRECTION_FORWARD) 2621 selection->setPosition(nextAnchorNode, 2622 caretMinOffset(nextAnchorNode), ec); 2623 else 2624 selection->setPosition(nextAnchorNode, 2625 caretMaxOffset(nextAnchorNode), ec); 2626 } 2627 2628 bool WebViewCore::isContentInputElement(Node* node) 2629 { 2630 return (isVisible(node) 2631 && (node->hasTagName(WebCore::HTMLNames::selectTag) 2632 || node->hasTagName(WebCore::HTMLNames::aTag) 2633 || node->hasTagName(WebCore::HTMLNames::inputTag) 2634 || node->hasTagName(WebCore::HTMLNames::buttonTag))); 2635 } 2636 2637 bool WebViewCore::isContentTextNode(Node* node) 2638 { 2639 if (!node || !node->isTextNode()) 2640 return false; 2641 Text* textNode = static_cast<Text*>(node); 2642 return (isVisible(textNode) && textNode->length() > 0 2643 && !textNode->containsOnlyWhitespace()); 2644 } 2645 2646 Text* WebViewCore::traverseNextContentTextNode(Node* fromNode, Node* toNode, int direction) 2647 { 2648 Node* currentNode = fromNode; 2649 do { 2650 if (direction == DIRECTION_FORWARD) 2651 currentNode = currentNode->traverseNextNode(toNode); 2652 else 2653 currentNode = currentNode->traversePreviousNodePostOrder(toNode); 2654 } while (currentNode && !isContentTextNode(currentNode)); 2655 return static_cast<Text*>(currentNode); 2656 } 2657 2658 Node* WebViewCore::getIntermediaryInputElement(Node* fromNode, Node* toNode, int direction) 2659 { 2660 if (fromNode == toNode) 2661 return 0; 2662 if (direction == DIRECTION_FORWARD) { 2663 Node* currentNode = fromNode; 2664 while (currentNode && currentNode != toNode) { 2665 if (isContentInputElement(currentNode)) 2666 return currentNode; 2667 currentNode = currentNode->traverseNextNodePostOrder(); 2668 } 2669 currentNode = fromNode; 2670 while (currentNode && currentNode != toNode) { 2671 if (isContentInputElement(currentNode)) 2672 return currentNode; 2673 currentNode = currentNode->traverseNextNode(); 2674 } 2675 } else { 2676 Node* currentNode = fromNode->traversePreviousNode(); 2677 while (currentNode && currentNode != toNode) { 2678 if (isContentInputElement(currentNode)) 2679 return currentNode; 2680 currentNode = currentNode->traversePreviousNode(); 2681 } 2682 currentNode = fromNode->traversePreviousNodePostOrder(); 2683 while (currentNode && currentNode != toNode) { 2684 if (isContentInputElement(currentNode)) 2685 return currentNode; 2686 currentNode = currentNode->traversePreviousNodePostOrder(); 2687 } 2688 } 2689 return 0; 2690 } 2691 2692 bool WebViewCore::isDescendantOf(Node* parent, Node* node) 2693 { 2694 Node* currentNode = node; 2695 while (currentNode) { 2696 if (currentNode == parent) { 2697 return true; 2698 } 2699 currentNode = currentNode->parentNode(); 2700 } 2701 return false; 2702 } 2703 2704 String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int axis) 2705 { 2706 HTMLElement* body = m_mainFrame->document()->body(); 2707 if (!m_currentNodeDomNavigationAxis && selection->focusNode()) { 2708 m_currentNodeDomNavigationAxis = selection->focusNode(); 2709 selection->empty(); 2710 if (m_currentNodeDomNavigationAxis->isTextNode()) 2711 m_currentNodeDomNavigationAxis = 2712 m_currentNodeDomNavigationAxis->parentNode(); 2713 } 2714 if (!m_currentNodeDomNavigationAxis) 2715 m_currentNodeDomNavigationAxis = currentFocus(); 2716 if (!m_currentNodeDomNavigationAxis 2717 || !CacheBuilder::validNode(m_mainFrame, m_mainFrame, 2718 m_currentNodeDomNavigationAxis)) 2719 m_currentNodeDomNavigationAxis = body; 2720 Node* currentNode = m_currentNodeDomNavigationAxis; 2721 if (axis == AXIS_HEADING) { 2722 if (currentNode == body && direction == DIRECTION_BACKWARD) 2723 currentNode = currentNode->lastDescendant(); 2724 do { 2725 if (direction == DIRECTION_FORWARD) 2726 currentNode = currentNode->traverseNextNode(body); 2727 else 2728 currentNode = currentNode->traversePreviousNode(body); 2729 } while (currentNode && (currentNode->isTextNode() 2730 || !isVisible(currentNode) || !isHeading(currentNode))); 2731 } else if (axis == AXIS_PARENT_FIRST_CHILD) { 2732 if (direction == DIRECTION_FORWARD) { 2733 currentNode = currentNode->firstChild(); 2734 while (currentNode && (currentNode->isTextNode() 2735 || !isVisible(currentNode))) 2736 currentNode = currentNode->nextSibling(); 2737 } else { 2738 do { 2739 if (currentNode == body) 2740 return String(); 2741 currentNode = currentNode->parentNode(); 2742 } while (currentNode && (currentNode->isTextNode() 2743 || !isVisible(currentNode))); 2744 } 2745 } else if (axis == AXIS_SIBLING) { 2746 do { 2747 if (direction == DIRECTION_FORWARD) 2748 currentNode = currentNode->nextSibling(); 2749 else { 2750 if (currentNode == body) 2751 return String(); 2752 currentNode = currentNode->previousSibling(); 2753 } 2754 } while (currentNode && (currentNode->isTextNode() 2755 || !isVisible(currentNode))); 2756 } else if (axis == AXIS_DOCUMENT) { 2757 currentNode = body; 2758 if (direction == DIRECTION_FORWARD) 2759 currentNode = currentNode->lastDescendant(); 2760 } else { 2761 LOGE("Invalid axis: %d", axis); 2762 return String(); 2763 } 2764 if (currentNode) { 2765 m_currentNodeDomNavigationAxis = currentNode; 2766 scrollNodeIntoView(m_mainFrame, currentNode); 2767 String selectionString = createMarkup(currentNode); 2768 LOGV("Selection markup: %s", selectionString.utf8().data()); 2769 return selectionString; 2770 } 2771 return String(); 2772 } 2773 2774 bool WebViewCore::isHeading(Node* node) 2775 { 2776 if (node->hasTagName(WebCore::HTMLNames::h1Tag) 2777 || node->hasTagName(WebCore::HTMLNames::h2Tag) 2778 || node->hasTagName(WebCore::HTMLNames::h3Tag) 2779 || node->hasTagName(WebCore::HTMLNames::h4Tag) 2780 || node->hasTagName(WebCore::HTMLNames::h5Tag) 2781 || node->hasTagName(WebCore::HTMLNames::h6Tag)) { 2782 return true; 2783 } 2784 2785 if (node->isElementNode()) { 2786 Element* element = static_cast<Element*>(node); 2787 String roleAttribute = 2788 element->getAttribute(WebCore::HTMLNames::roleAttr).string(); 2789 if (equalIgnoringCase(roleAttribute, "heading")) 2790 return true; 2791 } 2792 2793 return false; 2794 } 2795 2796 bool WebViewCore::isVisible(Node* node) 2797 { 2798 // start off an element 2799 Element* element = 0; 2800 if (node->isElementNode()) 2801 element = static_cast<Element*>(node); 2802 else 2803 element = node->parentElement(); 2804 // check renderer 2805 if (!element->renderer()) { 2806 return false; 2807 } 2808 // check size 2809 if (element->offsetHeight() == 0 || element->offsetWidth() == 0) { 2810 return false; 2811 } 2812 // check style 2813 Node* body = m_mainFrame->document()->body(); 2814 Node* currentNode = element; 2815 while (currentNode && currentNode != body) { 2816 RenderStyle* style = currentNode->computedStyle(); 2817 if (style && 2818 (style->display() == NONE || style->visibility() == HIDDEN)) { 2819 return false; 2820 } 2821 currentNode = currentNode->parentNode(); 2822 } 2823 return true; 2824 } 2825 2826 String WebViewCore::formatMarkup(DOMSelection* selection) 2827 { 2828 ExceptionCode ec = 0; 2829 String markup = String(); 2830 PassRefPtr<Range> wholeRange = selection->getRangeAt(0, ec); 2831 if (ec) 2832 return String(); 2833 if (!wholeRange->startContainer() || !wholeRange->startContainer()) 2834 return String(); 2835 // Since formatted markup contains invisible nodes it 2836 // is created from the concatenation of the visible fragments. 2837 Node* firstNode = wholeRange->firstNode(); 2838 Node* pastLastNode = wholeRange->pastLastNode(); 2839 Node* currentNode = firstNode; 2840 PassRefPtr<Range> currentRange; 2841 2842 while (currentNode != pastLastNode) { 2843 Node* nextNode = currentNode->traverseNextNode(); 2844 if (!isVisible(currentNode)) { 2845 if (currentRange) { 2846 markup = markup + currentRange->toHTML().utf8().data(); 2847 currentRange = 0; 2848 } 2849 } else { 2850 if (!currentRange) { 2851 currentRange = selection->frame()->document()->createRange(); 2852 if (ec) 2853 break; 2854 if (currentNode == firstNode) { 2855 currentRange->setStart(wholeRange->startContainer(), 2856 wholeRange->startOffset(), ec); 2857 if (ec) 2858 break; 2859 } else { 2860 currentRange->setStart(currentNode->parentNode(), 2861 currentNode->nodeIndex(), ec); 2862 if (ec) 2863 break; 2864 } 2865 } 2866 if (nextNode == pastLastNode) { 2867 currentRange->setEnd(wholeRange->endContainer(), 2868 wholeRange->endOffset(), ec); 2869 if (ec) 2870 break; 2871 markup = markup + currentRange->toHTML().utf8().data(); 2872 } else { 2873 if (currentNode->offsetInCharacters()) 2874 currentRange->setEnd(currentNode, 2875 currentNode->maxCharacterOffset(), ec); 2876 else 2877 currentRange->setEnd(currentNode->parentNode(), 2878 currentNode->nodeIndex() + 1, ec); 2879 if (ec) 2880 break; 2881 } 2882 } 2883 currentNode = nextNode; 2884 } 2885 return markup.stripWhiteSpace(); 2886 } 2887 2888 void WebViewCore::selectAt(int x, int y) 2889 { 2890 JNIEnv* env = JSC::Bindings::getJNIEnv(); 2891 AutoJObject javaObject = m_javaGlue->object(env); 2892 if (!javaObject.get()) 2893 return; 2894 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_selectAt, x, y); 2895 checkException(env); 2896 } 2897 2898 void WebViewCore::deleteSelection(int start, int end, int textGeneration) 2899 { 2900 setSelection(start, end); 2901 if (start == end) 2902 return; 2903 WebCore::Node* focus = currentFocus(); 2904 if (!focus) 2905 return; 2906 // Prevent our editor client from passing a message to change the 2907 // selection. 2908 EditorClientAndroid* client = static_cast<EditorClientAndroid*>( 2909 m_mainFrame->editor()->client()); 2910 client->setUiGeneratedSelectionChange(true); 2911 PlatformKeyboardEvent down(AKEYCODE_DEL, 0, 0, true, false, false, false); 2912 PlatformKeyboardEvent up(AKEYCODE_DEL, 0, 0, false, false, false, false); 2913 key(down); 2914 key(up); 2915 client->setUiGeneratedSelectionChange(false); 2916 m_textGeneration = textGeneration; 2917 m_shouldPaintCaret = true; 2918 } 2919 2920 void WebViewCore::replaceTextfieldText(int oldStart, 2921 int oldEnd, const WTF::String& replace, int start, int end, 2922 int textGeneration) 2923 { 2924 WebCore::Node* focus = currentFocus(); 2925 if (!focus) 2926 return; 2927 setSelection(oldStart, oldEnd); 2928 // Prevent our editor client from passing a message to change the 2929 // selection. 2930 EditorClientAndroid* client = static_cast<EditorClientAndroid*>( 2931 m_mainFrame->editor()->client()); 2932 client->setUiGeneratedSelectionChange(true); 2933 WebCore::TypingCommand::insertText(focus->document(), replace, 2934 false); 2935 client->setUiGeneratedSelectionChange(false); 2936 // setSelection calls revealSelection, so there is no need to do it here. 2937 setSelection(start, end); 2938 m_textGeneration = textGeneration; 2939 m_shouldPaintCaret = true; 2940 } 2941 2942 void WebViewCore::passToJs(int generation, const WTF::String& current, 2943 const PlatformKeyboardEvent& event) 2944 { 2945 WebCore::Node* focus = currentFocus(); 2946 if (!focus) { 2947 DBG_NAV_LOG("!focus"); 2948 clearTextEntry(); 2949 return; 2950 } 2951 WebCore::RenderObject* renderer = focus->renderer(); 2952 if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) { 2953 DBG_NAV_LOGD("renderer==%p || not text", renderer); 2954 clearTextEntry(); 2955 return; 2956 } 2957 // Block text field updates during a key press. 2958 m_blockTextfieldUpdates = true; 2959 // Also prevent our editor client from passing a message to change the 2960 // selection. 2961 EditorClientAndroid* client = static_cast<EditorClientAndroid*>( 2962 m_mainFrame->editor()->client()); 2963 client->setUiGeneratedSelectionChange(true); 2964 key(event); 2965 client->setUiGeneratedSelectionChange(false); 2966 m_blockTextfieldUpdates = false; 2967 m_textGeneration = generation; 2968 WebCore::RenderTextControl* renderText = 2969 static_cast<WebCore::RenderTextControl*>(renderer); 2970 WTF::String test = renderText->text(); 2971 if (test != current) { 2972 // If the text changed during the key event, update the UI text field. 2973 updateTextfield(focus, false, test); 2974 } else { 2975 DBG_NAV_LOG("test == current"); 2976 } 2977 // Now that the selection has settled down, send it. 2978 updateTextSelection(); 2979 m_shouldPaintCaret = true; 2980 } 2981 2982 void WebViewCore::scrollFocusedTextInput(float xPercent, int y) 2983 { 2984 WebCore::Node* focus = currentFocus(); 2985 if (!focus) { 2986 DBG_NAV_LOG("!focus"); 2987 clearTextEntry(); 2988 return; 2989 } 2990 WebCore::RenderObject* renderer = focus->renderer(); 2991 if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) { 2992 DBG_NAV_LOGD("renderer==%p || not text", renderer); 2993 clearTextEntry(); 2994 return; 2995 } 2996 WebCore::RenderTextControl* renderText = 2997 static_cast<WebCore::RenderTextControl*>(renderer); 2998 int x = (int) (xPercent * (renderText->scrollWidth() - 2999 renderText->clientWidth())); 3000 DBG_NAV_LOGD("x=%d y=%d xPercent=%g scrollW=%d clientW=%d", x, y, 3001 xPercent, renderText->scrollWidth(), renderText->clientWidth()); 3002 renderText->setScrollLeft(x); 3003 renderText->setScrollTop(y); 3004 } 3005 3006 void WebViewCore::setFocusControllerActive(bool active) 3007 { 3008 m_mainFrame->page()->focusController()->setActive(active); 3009 } 3010 3011 void WebViewCore::saveDocumentState(WebCore::Frame* frame) 3012 { 3013 if (!CacheBuilder::validNode(m_mainFrame, frame, 0)) 3014 frame = m_mainFrame; 3015 WebCore::HistoryItem *item = frame->loader()->history()->currentItem(); 3016 3017 // item can be null when there is no offical URL for the current page. This happens 3018 // when the content is loaded using with WebCoreFrameBridge::LoadData() and there 3019 // is no failing URL (common case is when content is loaded using data: scheme) 3020 if (item) { 3021 item->setDocumentState(frame->document()->formElementsState()); 3022 } 3023 } 3024 3025 // Create an array of java Strings. 3026 static jobjectArray makeLabelArray(JNIEnv* env, const uint16_t** labels, size_t count) 3027 { 3028 jclass stringClass = env->FindClass("java/lang/String"); 3029 LOG_ASSERT(stringClass, "Could not find java/lang/String"); 3030 jobjectArray array = env->NewObjectArray(count, stringClass, 0); 3031 LOG_ASSERT(array, "Could not create new string array"); 3032 3033 for (size_t i = 0; i < count; i++) { 3034 jobject newString = env->NewString(&labels[i][1], labels[i][0]); 3035 env->SetObjectArrayElement(array, i, newString); 3036 env->DeleteLocalRef(newString); 3037 checkException(env); 3038 } 3039 env->DeleteLocalRef(stringClass); 3040 return array; 3041 } 3042 3043 void WebViewCore::openFileChooser(PassRefPtr<WebCore::FileChooser> chooser) 3044 { 3045 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3046 AutoJObject javaObject = m_javaGlue->object(env); 3047 if (!javaObject.get()) 3048 return; 3049 3050 if (!chooser) 3051 return; 3052 3053 WTF::String acceptType = chooser->acceptTypes(); 3054 jstring jAcceptType = wtfStringToJstring(env, acceptType, true); 3055 jstring jName = (jstring) env->CallObjectMethod( 3056 javaObject.get(), m_javaGlue->m_openFileChooser, jAcceptType); 3057 checkException(env); 3058 env->DeleteLocalRef(jAcceptType); 3059 3060 WTF::String wtfString = jstringToWtfString(env, jName); 3061 env->DeleteLocalRef(jName); 3062 3063 if (!wtfString.isEmpty()) 3064 chooser->chooseFile(wtfString); 3065 } 3066 3067 void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount, 3068 bool multiple, const int selected[], size_t selectedCountOrSelection) 3069 { 3070 LOG_ASSERT(m_javaGlue->m_obj, "No java widget associated with this view!"); 3071 3072 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3073 AutoJObject javaObject = m_javaGlue->object(env); 3074 if (!javaObject.get()) 3075 return; 3076 3077 // If m_popupReply is not null, then we already have a list showing. 3078 if (m_popupReply != 0) 3079 return; 3080 3081 // Create an array of java Strings for the drop down. 3082 jobjectArray labelArray = makeLabelArray(env, labels, count); 3083 3084 // Create an array determining whether each item is enabled. 3085 jintArray enabledArray = env->NewIntArray(enabledCount); 3086 checkException(env); 3087 jint* ptrArray = env->GetIntArrayElements(enabledArray, 0); 3088 checkException(env); 3089 for (size_t i = 0; i < enabledCount; i++) { 3090 ptrArray[i] = enabled[i]; 3091 } 3092 env->ReleaseIntArrayElements(enabledArray, ptrArray, 0); 3093 checkException(env); 3094 3095 if (multiple) { 3096 // Pass up an array representing which items are selected. 3097 jintArray selectedArray = env->NewIntArray(selectedCountOrSelection); 3098 checkException(env); 3099 jint* selArray = env->GetIntArrayElements(selectedArray, 0); 3100 checkException(env); 3101 for (size_t i = 0; i < selectedCountOrSelection; i++) { 3102 selArray[i] = selected[i]; 3103 } 3104 env->ReleaseIntArrayElements(selectedArray, selArray, 0); 3105 3106 env->CallVoidMethod(javaObject.get(), 3107 m_javaGlue->m_requestListBox, labelArray, enabledArray, 3108 selectedArray); 3109 env->DeleteLocalRef(selectedArray); 3110 } else { 3111 // Pass up the single selection. 3112 env->CallVoidMethod(javaObject.get(), 3113 m_javaGlue->m_requestSingleListBox, labelArray, enabledArray, 3114 selectedCountOrSelection); 3115 } 3116 3117 env->DeleteLocalRef(labelArray); 3118 env->DeleteLocalRef(enabledArray); 3119 checkException(env); 3120 3121 Retain(reply); 3122 m_popupReply = reply; 3123 } 3124 3125 bool WebViewCore::key(const PlatformKeyboardEvent& event) 3126 { 3127 WebCore::EventHandler* eventHandler; 3128 WebCore::Node* focusNode = currentFocus(); 3129 DBG_NAV_LOGD("keyCode=%s unichar=%d focusNode=%p", 3130 event.keyIdentifier().utf8().data(), event.unichar(), focusNode); 3131 if (focusNode) { 3132 WebCore::Frame* frame = focusNode->document()->frame(); 3133 WebFrame* webFrame = WebFrame::getWebFrame(frame); 3134 eventHandler = frame->eventHandler(); 3135 VisibleSelection old = frame->selection()->selection(); 3136 bool handled = eventHandler->keyEvent(event); 3137 if (isContentEditable(focusNode)) { 3138 // keyEvent will return true even if the contentEditable did not 3139 // change its selection. In the case that it does not, we want to 3140 // return false so that the key will be sent back to our navigation 3141 // system. 3142 handled |= frame->selection()->selection() != old; 3143 } 3144 return handled; 3145 } else { 3146 eventHandler = m_mainFrame->eventHandler(); 3147 } 3148 return eventHandler->keyEvent(event); 3149 } 3150 3151 // For when the user clicks the trackball, presses dpad center, or types into an 3152 // unfocused textfield. In the latter case, 'fake' will be true 3153 void WebViewCore::click(WebCore::Frame* frame, WebCore::Node* node, bool fake) { 3154 if (!node) { 3155 WebCore::IntPoint pt = m_mousePos; 3156 pt.move(m_scrollOffsetX, m_scrollOffsetY); 3157 WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()-> 3158 hitTestResultAtPoint(pt, false); 3159 node = hitTestResult.innerNode(); 3160 frame = node->document()->frame(); 3161 DBG_NAV_LOGD("m_mousePos=(%d,%d) m_scrollOffset=(%d,%d) pt=(%d,%d)" 3162 " node=%p", m_mousePos.x(), m_mousePos.y(), 3163 m_scrollOffsetX, m_scrollOffsetY, pt.x(), pt.y(), node); 3164 } 3165 if (node) { 3166 EditorClientAndroid* client 3167 = static_cast<EditorClientAndroid*>( 3168 m_mainFrame->editor()->client()); 3169 client->setShouldChangeSelectedRange(false); 3170 handleMouseClick(frame, node, fake); 3171 client->setShouldChangeSelectedRange(true); 3172 } 3173 } 3174 3175 #if USE(ACCELERATED_COMPOSITING) 3176 GraphicsLayerAndroid* WebViewCore::graphicsRootLayer() const 3177 { 3178 RenderView* contentRenderer = m_mainFrame->contentRenderer(); 3179 if (!contentRenderer) 3180 return 0; 3181 return static_cast<GraphicsLayerAndroid*>( 3182 contentRenderer->compositor()->rootPlatformLayer()); 3183 } 3184 #endif 3185 3186 bool WebViewCore::handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>& points, int actionIndex, int metaState) 3187 { 3188 bool preventDefault = false; 3189 3190 #if USE(ACCELERATED_COMPOSITING) 3191 GraphicsLayerAndroid* rootLayer = graphicsRootLayer(); 3192 if (rootLayer) 3193 rootLayer->pauseDisplay(true); 3194 #endif 3195 3196 #if ENABLE(TOUCH_EVENTS) // Android 3197 #define MOTION_EVENT_ACTION_POINTER_DOWN 5 3198 #define MOTION_EVENT_ACTION_POINTER_UP 6 3199 3200 WebCore::TouchEventType type = WebCore::TouchStart; 3201 WebCore::PlatformTouchPoint::State defaultTouchState; 3202 Vector<WebCore::PlatformTouchPoint::State> touchStates(points.size()); 3203 3204 switch (action) { 3205 case 0: // MotionEvent.ACTION_DOWN 3206 type = WebCore::TouchStart; 3207 defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed; 3208 break; 3209 case 1: // MotionEvent.ACTION_UP 3210 type = WebCore::TouchEnd; 3211 defaultTouchState = WebCore::PlatformTouchPoint::TouchReleased; 3212 break; 3213 case 2: // MotionEvent.ACTION_MOVE 3214 type = WebCore::TouchMove; 3215 defaultTouchState = WebCore::PlatformTouchPoint::TouchMoved; 3216 break; 3217 case 3: // MotionEvent.ACTION_CANCEL 3218 type = WebCore::TouchCancel; 3219 defaultTouchState = WebCore::PlatformTouchPoint::TouchCancelled; 3220 break; 3221 case 5: // MotionEvent.ACTION_POINTER_DOWN 3222 type = WebCore::TouchStart; 3223 defaultTouchState = WebCore::PlatformTouchPoint::TouchStationary; 3224 break; 3225 case 6: // MotionEvent.ACTION_POINTER_UP 3226 type = WebCore::TouchEnd; 3227 defaultTouchState = WebCore::PlatformTouchPoint::TouchStationary; 3228 break; 3229 case 0x100: // WebViewCore.ACTION_LONGPRESS 3230 type = WebCore::TouchLongPress; 3231 defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed; 3232 break; 3233 case 0x200: // WebViewCore.ACTION_DOUBLETAP 3234 type = WebCore::TouchDoubleTap; 3235 defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed; 3236 break; 3237 default: 3238 // We do not support other kinds of touch event inside WebCore 3239 // at the moment. 3240 LOGW("Java passed a touch event type that we do not support in WebCore: %d", action); 3241 return 0; 3242 } 3243 3244 for (int c = 0; c < static_cast<int>(points.size()); c++) { 3245 points[c].setX(points[c].x() - m_scrollOffsetX); 3246 points[c].setY(points[c].y() - m_scrollOffsetY); 3247 3248 // Setting the touch state for each point. 3249 // Note: actionIndex will be 0 for all actions that are not ACTION_POINTER_DOWN/UP. 3250 if (action == MOTION_EVENT_ACTION_POINTER_DOWN && c == actionIndex) { 3251 touchStates[c] = WebCore::PlatformTouchPoint::TouchPressed; 3252 } else if (action == MOTION_EVENT_ACTION_POINTER_UP && c == actionIndex) { 3253 touchStates[c] = WebCore::PlatformTouchPoint::TouchReleased; 3254 } else { 3255 touchStates[c] = defaultTouchState; 3256 }; 3257 } 3258 3259 WebCore::PlatformTouchEvent te(ids, points, type, touchStates, metaState); 3260 preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te); 3261 #endif 3262 3263 #if USE(ACCELERATED_COMPOSITING) 3264 if (rootLayer) 3265 rootLayer->pauseDisplay(false); 3266 #endif 3267 return preventDefault; 3268 } 3269 3270 void WebViewCore::touchUp(int touchGeneration, 3271 WebCore::Frame* frame, WebCore::Node* node, int x, int y) 3272 { 3273 if (touchGeneration == 0) { 3274 // m_mousePos should be set in getTouchHighlightRects() 3275 WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(m_mousePos, false); 3276 node = hitTestResult.innerNode(); 3277 if (node) 3278 frame = node->document()->frame(); 3279 else 3280 frame = 0; 3281 DBG_NAV_LOGD("touch up on (%d, %d), scrollOffset is (%d, %d), node:%p, frame:%p", m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, m_scrollOffsetX, m_scrollOffsetY, node, frame); 3282 } else { 3283 if (m_touchGeneration > touchGeneration) { 3284 DBG_NAV_LOGD("m_touchGeneration=%d > touchGeneration=%d" 3285 " x=%d y=%d", m_touchGeneration, touchGeneration, x, y); 3286 return; // short circuit if a newer touch has been generated 3287 } 3288 // This moves m_mousePos to the correct place, and handleMouseClick uses 3289 // m_mousePos to determine where the click happens. 3290 moveMouse(frame, x, y); 3291 m_lastGeneration = touchGeneration; 3292 } 3293 if (frame && CacheBuilder::validNode(m_mainFrame, frame, 0)) { 3294 frame->loader()->resetMultipleFormSubmissionProtection(); 3295 } 3296 DBG_NAV_LOGD("touchGeneration=%d handleMouseClick frame=%p node=%p" 3297 " x=%d y=%d", touchGeneration, frame, node, x, y); 3298 handleMouseClick(frame, node, false); 3299 } 3300 3301 // Check for the "x-webkit-soft-keyboard" attribute. If it is there and 3302 // set to hidden, do not show the soft keyboard. Node passed as a parameter 3303 // must not be null. 3304 static bool shouldSuppressKeyboard(const WebCore::Node* node) { 3305 LOG_ASSERT(node, "node passed to shouldSuppressKeyboard cannot be null"); 3306 const NamedNodeMap* attributes = node->attributes(); 3307 if (!attributes) return false; 3308 size_t length = attributes->length(); 3309 for (size_t i = 0; i < length; i++) { 3310 const Attribute* a = attributes->attributeItem(i); 3311 if (a->localName() == "x-webkit-soft-keyboard" && a->value() == "hidden") 3312 return true; 3313 } 3314 return false; 3315 } 3316 3317 // Common code for both clicking with the trackball and touchUp 3318 // Also used when typing into a non-focused textfield to give the textfield focus, 3319 // in which case, 'fake' is set to true 3320 bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr, bool fake) 3321 { 3322 bool valid = !framePtr || CacheBuilder::validNode(m_mainFrame, framePtr, nodePtr); 3323 WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame); 3324 if (valid && nodePtr) { 3325 // Need to special case area tags because an image map could have an area element in the middle 3326 // so when attempting to get the default, the point chosen would be follow the wrong link. 3327 if (nodePtr->hasTagName(WebCore::HTMLNames::areaTag)) { 3328 webFrame->setUserInitiatedAction(true); 3329 nodePtr->dispatchSimulatedClick(0, true, true); 3330 webFrame->setUserInitiatedAction(false); 3331 DBG_NAV_LOG("area"); 3332 return true; 3333 } 3334 } 3335 if (!valid || !framePtr) 3336 framePtr = m_mainFrame; 3337 webFrame->setUserInitiatedAction(true); 3338 WebCore::PlatformMouseEvent mouseDown(m_mousePos, m_mousePos, WebCore::LeftButton, 3339 WebCore::MouseEventPressed, 1, false, false, false, false, 3340 WTF::currentTime()); 3341 // ignore the return from as it will return true if the hit point can trigger selection change 3342 framePtr->eventHandler()->handleMousePressEvent(mouseDown); 3343 WebCore::PlatformMouseEvent mouseUp(m_mousePos, m_mousePos, WebCore::LeftButton, 3344 WebCore::MouseEventReleased, 1, false, false, false, false, 3345 WTF::currentTime()); 3346 bool handled = framePtr->eventHandler()->handleMouseReleaseEvent(mouseUp); 3347 webFrame->setUserInitiatedAction(false); 3348 3349 // If the user clicked on a textfield, make the focusController active 3350 // so we show the blinking cursor. 3351 WebCore::Node* focusNode = currentFocus(); 3352 DBG_NAV_LOGD("m_mousePos={%d,%d} focusNode=%p handled=%s", m_mousePos.x(), 3353 m_mousePos.y(), focusNode, handled ? "true" : "false"); 3354 if (focusNode) { 3355 WebCore::RenderObject* renderer = focusNode->renderer(); 3356 if (renderer && (renderer->isTextField() || renderer->isTextArea())) { 3357 bool ime = !shouldSuppressKeyboard(focusNode) 3358 && !(static_cast<WebCore::HTMLInputElement*>(focusNode))->readOnly(); 3359 if (ime) { 3360 #if ENABLE(WEB_AUTOFILL) 3361 if (renderer->isTextField()) { 3362 EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(framePtr->page()->editorClient()); 3363 WebAutofill* autoFill = editorC->getAutofill(); 3364 autoFill->formFieldFocused(static_cast<HTMLFormControlElement*>(focusNode)); 3365 } 3366 #endif 3367 if (!fake) { 3368 RenderTextControl* rtc 3369 = static_cast<RenderTextControl*> (renderer); 3370 // Force an update of the navcache as this will fire off a 3371 // message to WebView that *must* have an updated focus. 3372 m_frameCacheOutOfDate = true; 3373 updateFrameCache(); 3374 requestKeyboardWithSelection(focusNode, rtc->selectionStart(), 3375 rtc->selectionEnd()); 3376 } 3377 } else if (!fake) { 3378 requestKeyboard(false); 3379 } 3380 } else if (!fake){ 3381 // If the selection is contentEditable, show the keyboard so the 3382 // user can type. Otherwise hide the keyboard because no text 3383 // input is needed. 3384 if (isContentEditable(focusNode)) { 3385 requestKeyboard(true); 3386 } else if (!nodeIsPlugin(focusNode)) { 3387 clearTextEntry(); 3388 } 3389 } 3390 } else if (!fake) { 3391 // There is no focusNode, so the keyboard is not needed. 3392 clearTextEntry(); 3393 } 3394 return handled; 3395 } 3396 3397 void WebViewCore::popupReply(int index) 3398 { 3399 if (m_popupReply) { 3400 m_popupReply->replyInt(index); 3401 Release(m_popupReply); 3402 m_popupReply = 0; 3403 } 3404 } 3405 3406 void WebViewCore::popupReply(const int* array, int count) 3407 { 3408 if (m_popupReply) { 3409 m_popupReply->replyIntArray(array, count); 3410 Release(m_popupReply); 3411 m_popupReply = 0; 3412 } 3413 } 3414 3415 void WebViewCore::formDidBlur(const WebCore::Node* node) 3416 { 3417 // If the blur is on a text input, keep track of the node so we can 3418 // hide the soft keyboard when the new focus is set, if it is not a 3419 // text input. 3420 if (isTextInput(node)) 3421 m_blurringNodePointer = reinterpret_cast<int>(node); 3422 } 3423 3424 void WebViewCore::focusNodeChanged(const WebCore::Node* newFocus) 3425 { 3426 if (isTextInput(newFocus)) 3427 m_shouldPaintCaret = true; 3428 else if (m_blurringNodePointer) { 3429 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3430 AutoJObject javaObject = m_javaGlue->object(env); 3431 if (!javaObject.get()) 3432 return; 3433 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_formDidBlur, m_blurringNodePointer); 3434 checkException(env); 3435 m_blurringNodePointer = 0; 3436 } 3437 } 3438 3439 void WebViewCore::addMessageToConsole(const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceID, int msgLevel) { 3440 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3441 AutoJObject javaObject = m_javaGlue->object(env); 3442 if (!javaObject.get()) 3443 return; 3444 jstring jMessageStr = wtfStringToJstring(env, message); 3445 jstring jSourceIDStr = wtfStringToJstring(env, sourceID); 3446 env->CallVoidMethod(javaObject.get(), 3447 m_javaGlue->m_addMessageToConsole, jMessageStr, lineNumber, 3448 jSourceIDStr, msgLevel); 3449 env->DeleteLocalRef(jMessageStr); 3450 env->DeleteLocalRef(jSourceIDStr); 3451 checkException(env); 3452 } 3453 3454 void WebViewCore::jsAlert(const WTF::String& url, const WTF::String& text) 3455 { 3456 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3457 AutoJObject javaObject = m_javaGlue->object(env); 3458 if (!javaObject.get()) 3459 return; 3460 jstring jInputStr = wtfStringToJstring(env, text); 3461 jstring jUrlStr = wtfStringToJstring(env, url); 3462 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_jsAlert, jUrlStr, jInputStr); 3463 env->DeleteLocalRef(jInputStr); 3464 env->DeleteLocalRef(jUrlStr); 3465 checkException(env); 3466 } 3467 3468 bool WebViewCore::exceededDatabaseQuota(const WTF::String& url, const WTF::String& databaseIdentifier, const unsigned long long currentQuota, unsigned long long estimatedSize) 3469 { 3470 #if ENABLE(DATABASE) 3471 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3472 AutoJObject javaObject = m_javaGlue->object(env); 3473 if (!javaObject.get()) 3474 return false; 3475 jstring jDatabaseIdentifierStr = wtfStringToJstring(env, databaseIdentifier); 3476 jstring jUrlStr = wtfStringToJstring(env, url); 3477 env->CallVoidMethod(javaObject.get(), 3478 m_javaGlue->m_exceededDatabaseQuota, jUrlStr, 3479 jDatabaseIdentifierStr, currentQuota, estimatedSize); 3480 env->DeleteLocalRef(jDatabaseIdentifierStr); 3481 env->DeleteLocalRef(jUrlStr); 3482 checkException(env); 3483 return true; 3484 #endif 3485 } 3486 3487 bool WebViewCore::reachedMaxAppCacheSize(const unsigned long long spaceNeeded) 3488 { 3489 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 3490 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3491 AutoJObject javaObject = m_javaGlue->object(env); 3492 if (!javaObject.get()) 3493 return false; 3494 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_reachedMaxAppCacheSize, spaceNeeded); 3495 checkException(env); 3496 return true; 3497 #endif 3498 } 3499 3500 void WebViewCore::populateVisitedLinks(WebCore::PageGroup* group) 3501 { 3502 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3503 AutoJObject javaObject = m_javaGlue->object(env); 3504 if (!javaObject.get()) 3505 return; 3506 m_groupForVisitedLinks = group; 3507 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_populateVisitedLinks); 3508 checkException(env); 3509 } 3510 3511 void WebViewCore::geolocationPermissionsShowPrompt(const WTF::String& origin) 3512 { 3513 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3514 AutoJObject javaObject = m_javaGlue->object(env); 3515 if (!javaObject.get()) 3516 return; 3517 jstring originString = wtfStringToJstring(env, origin); 3518 env->CallVoidMethod(javaObject.get(), 3519 m_javaGlue->m_geolocationPermissionsShowPrompt, 3520 originString); 3521 env->DeleteLocalRef(originString); 3522 checkException(env); 3523 } 3524 3525 void WebViewCore::geolocationPermissionsHidePrompt() 3526 { 3527 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3528 AutoJObject javaObject = m_javaGlue->object(env); 3529 if (!javaObject.get()) 3530 return; 3531 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_geolocationPermissionsHidePrompt); 3532 checkException(env); 3533 } 3534 3535 jobject WebViewCore::getDeviceMotionService() 3536 { 3537 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3538 AutoJObject javaObject = m_javaGlue->object(env); 3539 if (!javaObject.get()) 3540 return 0; 3541 jobject object = env->CallObjectMethod(javaObject.get(), m_javaGlue->m_getDeviceMotionService); 3542 checkException(env); 3543 return object; 3544 } 3545 3546 jobject WebViewCore::getDeviceOrientationService() 3547 { 3548 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3549 AutoJObject javaObject = m_javaGlue->object(env); 3550 if (!javaObject.get()) 3551 return 0; 3552 jobject object = env->CallObjectMethod(javaObject.get(), m_javaGlue->m_getDeviceOrientationService); 3553 checkException(env); 3554 return object; 3555 } 3556 3557 bool WebViewCore::jsConfirm(const WTF::String& url, const WTF::String& text) 3558 { 3559 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3560 AutoJObject javaObject = m_javaGlue->object(env); 3561 if (!javaObject.get()) 3562 return false; 3563 jstring jInputStr = wtfStringToJstring(env, text); 3564 jstring jUrlStr = wtfStringToJstring(env, url); 3565 jboolean result = env->CallBooleanMethod(javaObject.get(), m_javaGlue->m_jsConfirm, jUrlStr, jInputStr); 3566 env->DeleteLocalRef(jInputStr); 3567 env->DeleteLocalRef(jUrlStr); 3568 checkException(env); 3569 return result; 3570 } 3571 3572 bool WebViewCore::jsPrompt(const WTF::String& url, const WTF::String& text, const WTF::String& defaultValue, WTF::String& result) 3573 { 3574 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3575 AutoJObject javaObject = m_javaGlue->object(env); 3576 if (!javaObject.get()) 3577 return false; 3578 jstring jUrlStr = wtfStringToJstring(env, url); 3579 jstring jInputStr = wtfStringToJstring(env, text); 3580 jstring jDefaultStr = wtfStringToJstring(env, defaultValue); 3581 jstring returnVal = static_cast<jstring>(env->CallObjectMethod(javaObject.get(), m_javaGlue->m_jsPrompt, jUrlStr, jInputStr, jDefaultStr)); 3582 env->DeleteLocalRef(jUrlStr); 3583 env->DeleteLocalRef(jInputStr); 3584 env->DeleteLocalRef(jDefaultStr); 3585 checkException(env); 3586 3587 // If returnVal is null, it means that the user cancelled the dialog. 3588 if (!returnVal) 3589 return false; 3590 3591 result = jstringToWtfString(env, returnVal); 3592 env->DeleteLocalRef(returnVal); 3593 return true; 3594 } 3595 3596 bool WebViewCore::jsUnload(const WTF::String& url, const WTF::String& message) 3597 { 3598 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3599 AutoJObject javaObject = m_javaGlue->object(env); 3600 if (!javaObject.get()) 3601 return false; 3602 jstring jInputStr = wtfStringToJstring(env, message); 3603 jstring jUrlStr = wtfStringToJstring(env, url); 3604 jboolean result = env->CallBooleanMethod(javaObject.get(), m_javaGlue->m_jsUnload, jUrlStr, jInputStr); 3605 env->DeleteLocalRef(jInputStr); 3606 env->DeleteLocalRef(jUrlStr); 3607 checkException(env); 3608 return result; 3609 } 3610 3611 bool WebViewCore::jsInterrupt() 3612 { 3613 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3614 AutoJObject javaObject = m_javaGlue->object(env); 3615 if (!javaObject.get()) 3616 return false; 3617 jboolean result = env->CallBooleanMethod(javaObject.get(), m_javaGlue->m_jsInterrupt); 3618 checkException(env); 3619 return result; 3620 } 3621 3622 AutoJObject 3623 WebViewCore::getJavaObject() 3624 { 3625 return m_javaGlue->object(JSC::Bindings::getJNIEnv()); 3626 } 3627 3628 jobject 3629 WebViewCore::getWebViewJavaObject() 3630 { 3631 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3632 AutoJObject javaObject = m_javaGlue->object(env); 3633 if (!javaObject.get()) 3634 return 0; 3635 return env->GetObjectField(javaObject.get(), gWebViewCoreFields.m_webView); 3636 } 3637 3638 void WebViewCore::updateTextSelection() 3639 { 3640 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3641 AutoJObject javaObject = m_javaGlue->object(env); 3642 if (!javaObject.get()) 3643 return; 3644 WebCore::Node* focusNode = currentFocus(); 3645 if (!focusNode) 3646 return; 3647 RenderObject* renderer = focusNode->renderer(); 3648 if (!renderer || (!renderer->isTextArea() && !renderer->isTextField())) 3649 return; 3650 RenderTextControl* rtc = static_cast<RenderTextControl*>(renderer); 3651 env->CallVoidMethod(javaObject.get(), 3652 m_javaGlue->m_updateTextSelection, reinterpret_cast<int>(focusNode), 3653 rtc->selectionStart(), rtc->selectionEnd(), m_textGeneration); 3654 checkException(env); 3655 } 3656 3657 void WebViewCore::updateTextfield(WebCore::Node* ptr, bool changeToPassword, 3658 const WTF::String& text) 3659 { 3660 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3661 AutoJObject javaObject = m_javaGlue->object(env); 3662 if (!javaObject.get()) 3663 return; 3664 if (m_blockTextfieldUpdates) 3665 return; 3666 if (changeToPassword) { 3667 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_updateTextfield, 3668 (int) ptr, true, 0, m_textGeneration); 3669 checkException(env); 3670 return; 3671 } 3672 jstring string = wtfStringToJstring(env, text); 3673 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_updateTextfield, 3674 (int) ptr, false, string, m_textGeneration); 3675 env->DeleteLocalRef(string); 3676 checkException(env); 3677 } 3678 3679 void WebViewCore::clearTextEntry() 3680 { 3681 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3682 AutoJObject javaObject = m_javaGlue->object(env); 3683 if (!javaObject.get()) 3684 return; 3685 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_clearTextEntry); 3686 } 3687 3688 void WebViewCore::setBackgroundColor(SkColor c) 3689 { 3690 WebCore::FrameView* view = m_mainFrame->view(); 3691 if (!view) 3692 return; 3693 3694 // need (int) cast to find the right constructor 3695 WebCore::Color bcolor((int)SkColorGetR(c), (int)SkColorGetG(c), 3696 (int)SkColorGetB(c), (int)SkColorGetA(c)); 3697 view->setBaseBackgroundColor(bcolor); 3698 3699 // Background color of 0 indicates we want a transparent background 3700 if (c == 0) 3701 view->setTransparent(true); 3702 } 3703 3704 jclass WebViewCore::getPluginClass(const WTF::String& libName, const char* className) 3705 { 3706 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3707 AutoJObject javaObject = m_javaGlue->object(env); 3708 if (!javaObject.get()) 3709 return 0; 3710 3711 jstring libString = wtfStringToJstring(env, libName); 3712 jstring classString = env->NewStringUTF(className); 3713 jobject pluginClass = env->CallObjectMethod(javaObject.get(), 3714 m_javaGlue->m_getPluginClass, 3715 libString, classString); 3716 checkException(env); 3717 3718 // cleanup unneeded local JNI references 3719 env->DeleteLocalRef(libString); 3720 env->DeleteLocalRef(classString); 3721 3722 if (pluginClass != 0) { 3723 return static_cast<jclass>(pluginClass); 3724 } else { 3725 return 0; 3726 } 3727 } 3728 3729 void WebViewCore::showFullScreenPlugin(jobject childView, int32_t orientation, NPP npp) 3730 { 3731 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3732 AutoJObject javaObject = m_javaGlue->object(env); 3733 if (!javaObject.get()) 3734 return; 3735 3736 env->CallVoidMethod(javaObject.get(), 3737 m_javaGlue->m_showFullScreenPlugin, 3738 childView, orientation, reinterpret_cast<int>(npp)); 3739 checkException(env); 3740 } 3741 3742 void WebViewCore::hideFullScreenPlugin() 3743 { 3744 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3745 AutoJObject javaObject = m_javaGlue->object(env); 3746 if (!javaObject.get()) 3747 return; 3748 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_hideFullScreenPlugin); 3749 checkException(env); 3750 } 3751 3752 jobject WebViewCore::createSurface(jobject view) 3753 { 3754 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3755 AutoJObject javaObject = m_javaGlue->object(env); 3756 if (!javaObject.get()) 3757 return 0; 3758 jobject result = env->CallObjectMethod(javaObject.get(), m_javaGlue->m_createSurface, view); 3759 checkException(env); 3760 return result; 3761 } 3762 3763 jobject WebViewCore::addSurface(jobject view, int x, int y, int width, int height) 3764 { 3765 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3766 AutoJObject javaObject = m_javaGlue->object(env); 3767 if (!javaObject.get()) 3768 return 0; 3769 jobject result = env->CallObjectMethod(javaObject.get(), 3770 m_javaGlue->m_addSurface, 3771 view, x, y, width, height); 3772 checkException(env); 3773 return result; 3774 } 3775 3776 void WebViewCore::updateSurface(jobject childView, int x, int y, int width, int height) 3777 { 3778 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3779 AutoJObject javaObject = m_javaGlue->object(env); 3780 if (!javaObject.get()) 3781 return; 3782 env->CallVoidMethod(javaObject.get(), 3783 m_javaGlue->m_updateSurface, childView, 3784 x, y, width, height); 3785 checkException(env); 3786 } 3787 3788 void WebViewCore::destroySurface(jobject childView) 3789 { 3790 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3791 AutoJObject javaObject = m_javaGlue->object(env); 3792 if (!javaObject.get()) 3793 return; 3794 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_destroySurface, childView); 3795 checkException(env); 3796 } 3797 3798 jobject WebViewCore::getContext() 3799 { 3800 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3801 AutoJObject javaObject = m_javaGlue->object(env); 3802 if (!javaObject.get()) 3803 return 0; 3804 3805 jobject result = env->CallObjectMethod(javaObject.get(), m_javaGlue->m_getContext); 3806 checkException(env); 3807 return result; 3808 } 3809 3810 void WebViewCore::keepScreenOn(bool screenOn) { 3811 if ((screenOn && m_screenOnCounter == 0) || (!screenOn && m_screenOnCounter == 1)) { 3812 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3813 AutoJObject javaObject = m_javaGlue->object(env); 3814 if (!javaObject.get()) 3815 return; 3816 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_keepScreenOn, screenOn); 3817 checkException(env); 3818 } 3819 3820 // update the counter 3821 if (screenOn) 3822 m_screenOnCounter++; 3823 else if (m_screenOnCounter > 0) 3824 m_screenOnCounter--; 3825 } 3826 3827 bool WebViewCore::validNodeAndBounds(Frame* frame, Node* node, 3828 const IntRect& originalAbsoluteBounds) 3829 { 3830 bool valid = CacheBuilder::validNode(m_mainFrame, frame, node); 3831 if (!valid) 3832 return false; 3833 RenderObject* renderer = node->renderer(); 3834 if (!renderer) 3835 return false; 3836 IntRect absBounds = node->hasTagName(HTMLNames::areaTag) 3837 ? CacheBuilder::getAreaRect(static_cast<HTMLAreaElement*>(node)) 3838 : renderer->absoluteBoundingBoxRect(); 3839 return absBounds == originalAbsoluteBounds; 3840 } 3841 3842 void WebViewCore::showRect(int left, int top, int width, int height, 3843 int contentWidth, int contentHeight, float xPercentInDoc, 3844 float xPercentInView, float yPercentInDoc, float yPercentInView) 3845 { 3846 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3847 AutoJObject javaObject = m_javaGlue->object(env); 3848 if (!javaObject.get()) 3849 return; 3850 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_showRect, 3851 left, top, width, height, contentWidth, contentHeight, 3852 xPercentInDoc, xPercentInView, yPercentInDoc, yPercentInView); 3853 checkException(env); 3854 } 3855 3856 void WebViewCore::centerFitRect(int x, int y, int width, int height) 3857 { 3858 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3859 AutoJObject javaObject = m_javaGlue->object(env); 3860 if (!javaObject.get()) 3861 return; 3862 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_centerFitRect, x, y, width, height); 3863 checkException(env); 3864 } 3865 3866 void WebViewCore::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode) 3867 { 3868 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3869 AutoJObject javaObject = m_javaGlue->object(env); 3870 if (!javaObject.get()) 3871 return; 3872 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_setScrollbarModes, horizontalMode, verticalMode); 3873 checkException(env); 3874 } 3875 3876 void WebViewCore::notifyWebAppCanBeInstalled() 3877 { 3878 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3879 AutoJObject javaObject = m_javaGlue->object(env); 3880 if (!javaObject.get()) 3881 return; 3882 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_setInstallableWebApp); 3883 checkException(env); 3884 } 3885 3886 #if ENABLE(VIDEO) 3887 void WebViewCore::enterFullscreenForVideoLayer(int layerId, const WTF::String& url) 3888 { 3889 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3890 AutoJObject javaObject = m_javaGlue->object(env); 3891 if (!javaObject.get()) 3892 return; 3893 jstring jUrlStr = wtfStringToJstring(env, url); 3894 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_enterFullscreenForVideoLayer, layerId, jUrlStr); 3895 checkException(env); 3896 } 3897 #endif 3898 3899 void WebViewCore::setWebTextViewAutoFillable(int queryId, const string16& previewSummary) 3900 { 3901 #if ENABLE(WEB_AUTOFILL) 3902 JNIEnv* env = JSC::Bindings::getJNIEnv(); 3903 AutoJObject javaObject = m_javaGlue->object(env); 3904 if (!javaObject.get()) 3905 return; 3906 jstring preview = env->NewString(previewSummary.data(), previewSummary.length()); 3907 env->CallVoidMethod(javaObject.get(), m_javaGlue->m_setWebTextViewAutoFillable, queryId, preview); 3908 env->DeleteLocalRef(preview); 3909 #endif 3910 } 3911 3912 bool WebViewCore::drawIsPaused() const 3913 { 3914 // returning true says scrollview should be offscreen, which pauses 3915 // gifs. because this is not again queried when we stop scrolling, we don't 3916 // use the stopping currently. 3917 return false; 3918 } 3919 3920 #if USE(CHROME_NETWORK_STACK) 3921 void WebViewCore::setWebRequestContextUserAgent() 3922 { 3923 // We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet 3924 if (m_webRequestContext) 3925 m_webRequestContext->setUserAgent(WebFrame::getWebFrame(m_mainFrame)->userAgentForURL(0)); // URL not used 3926 } 3927 3928 void WebViewCore::setWebRequestContextCacheMode(int cacheMode) 3929 { 3930 m_cacheMode = cacheMode; 3931 // We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet 3932 if (!m_webRequestContext) 3933 return; 3934 3935 m_webRequestContext->setCacheMode(cacheMode); 3936 } 3937 3938 WebRequestContext* WebViewCore::webRequestContext() 3939 { 3940 if (!m_webRequestContext) { 3941 Settings* settings = mainFrame()->settings(); 3942 m_webRequestContext = new WebRequestContext(settings && settings->privateBrowsingEnabled()); 3943 setWebRequestContextUserAgent(); 3944 setWebRequestContextCacheMode(m_cacheMode); 3945 } 3946 return m_webRequestContext.get(); 3947 } 3948 #endif 3949 3950 void WebViewCore::scrollRenderLayer(int layer, const SkRect& rect) 3951 { 3952 #if USE(ACCELERATED_COMPOSITING) 3953 GraphicsLayerAndroid* root = graphicsRootLayer(); 3954 if (!root) 3955 return; 3956 3957 LayerAndroid* layerAndroid = root->platformLayer(); 3958 if (!layerAndroid) 3959 return; 3960 3961 LayerAndroid* target = layerAndroid->findById(layer); 3962 if (!target) 3963 return; 3964 3965 RenderLayer* owner = target->owningLayer(); 3966 if (!owner) 3967 return; 3968 3969 if (owner->stackingContext()) 3970 owner->scrollToOffset(rect.fLeft, rect.fTop); 3971 #endif 3972 } 3973 3974 //---------------------------------------------------------------------- 3975 // Native JNI methods 3976 //---------------------------------------------------------------------- 3977 static void RevealSelection(JNIEnv *env, jobject obj) 3978 { 3979 GET_NATIVE_VIEW(env, obj)->revealSelection(); 3980 } 3981 3982 static jstring RequestLabel(JNIEnv *env, jobject obj, int framePointer, 3983 int nodePointer) 3984 { 3985 return wtfStringToJstring(env, GET_NATIVE_VIEW(env, obj)->requestLabel( 3986 (WebCore::Frame*) framePointer, (WebCore::Node*) nodePointer)); 3987 } 3988 3989 static void ClearContent(JNIEnv *env, jobject obj) 3990 { 3991 #ifdef ANDROID_INSTRUMENT 3992 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 3993 #endif 3994 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 3995 viewImpl->clearContent(); 3996 } 3997 3998 static void UpdateFrameCacheIfLoading(JNIEnv *env, jobject obj) 3999 { 4000 GET_NATIVE_VIEW(env, obj)->updateFrameCacheIfLoading(); 4001 } 4002 4003 static void SetSize(JNIEnv *env, jobject obj, jint width, jint height, 4004 jint textWrapWidth, jfloat scale, jint screenWidth, jint screenHeight, 4005 jint anchorX, jint anchorY, jboolean ignoreHeight) 4006 { 4007 #ifdef ANDROID_INSTRUMENT 4008 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4009 #endif 4010 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4011 LOGV("webviewcore::nativeSetSize(%u %u)\n viewImpl: %p", (unsigned)width, (unsigned)height, viewImpl); 4012 LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetSize"); 4013 viewImpl->setSizeScreenWidthAndScale(width, height, textWrapWidth, scale, 4014 screenWidth, screenHeight, anchorX, anchorY, ignoreHeight); 4015 } 4016 4017 static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jboolean sendScrollEvent, jint x, jint y) 4018 { 4019 #ifdef ANDROID_INSTRUMENT 4020 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4021 #endif 4022 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4023 LOG_ASSERT(viewImpl, "need viewImpl"); 4024 4025 viewImpl->setScrollOffset(gen, sendScrollEvent, x, y); 4026 } 4027 4028 static void SetGlobalBounds(JNIEnv *env, jobject obj, jint x, jint y, jint h, 4029 jint v) 4030 { 4031 #ifdef ANDROID_INSTRUMENT 4032 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4033 #endif 4034 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4035 LOG_ASSERT(viewImpl, "need viewImpl"); 4036 4037 viewImpl->setGlobalBounds(x, y, h, v); 4038 } 4039 4040 static jboolean Key(JNIEnv *env, jobject obj, jint keyCode, jint unichar, 4041 jint repeatCount, jboolean isShift, jboolean isAlt, jboolean isSym, 4042 jboolean isDown) 4043 { 4044 #ifdef ANDROID_INSTRUMENT 4045 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4046 #endif 4047 return GET_NATIVE_VIEW(env, obj)->key(PlatformKeyboardEvent(keyCode, 4048 unichar, repeatCount, isDown, isShift, isAlt, isSym)); 4049 } 4050 4051 static void Click(JNIEnv *env, jobject obj, int framePtr, int nodePtr, jboolean fake) 4052 { 4053 #ifdef ANDROID_INSTRUMENT 4054 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4055 #endif 4056 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4057 LOG_ASSERT(viewImpl, "viewImpl not set in Click"); 4058 4059 viewImpl->click(reinterpret_cast<WebCore::Frame*>(framePtr), 4060 reinterpret_cast<WebCore::Node*>(nodePtr), fake); 4061 } 4062 4063 static void ContentInvalidateAll(JNIEnv *env, jobject obj) 4064 { 4065 GET_NATIVE_VIEW(env, obj)->contentInvalidateAll(); 4066 } 4067 4068 static void DeleteSelection(JNIEnv *env, jobject obj, jint start, jint end, 4069 jint textGeneration) 4070 { 4071 #ifdef ANDROID_INSTRUMENT 4072 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4073 #endif 4074 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4075 viewImpl->deleteSelection(start, end, textGeneration); 4076 } 4077 4078 static void SetSelection(JNIEnv *env, jobject obj, jint start, jint end) 4079 { 4080 #ifdef ANDROID_INSTRUMENT 4081 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4082 #endif 4083 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4084 viewImpl->setSelection(start, end); 4085 } 4086 4087 static jstring ModifySelection(JNIEnv *env, jobject obj, jint direction, jint granularity) 4088 { 4089 #ifdef ANDROID_INSTRUMENT 4090 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4091 #endif 4092 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4093 String selectionString = viewImpl->modifySelection(direction, granularity); 4094 return wtfStringToJstring(env, selectionString); 4095 } 4096 4097 static void ReplaceTextfieldText(JNIEnv *env, jobject obj, 4098 jint oldStart, jint oldEnd, jstring replace, jint start, jint end, 4099 jint textGeneration) 4100 { 4101 #ifdef ANDROID_INSTRUMENT 4102 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4103 #endif 4104 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4105 WTF::String webcoreString = jstringToWtfString(env, replace); 4106 viewImpl->replaceTextfieldText(oldStart, 4107 oldEnd, webcoreString, start, end, textGeneration); 4108 } 4109 4110 static void PassToJs(JNIEnv *env, jobject obj, 4111 jint generation, jstring currentText, jint keyCode, 4112 jint keyValue, jboolean down, jboolean cap, jboolean fn, jboolean sym) 4113 { 4114 #ifdef ANDROID_INSTRUMENT 4115 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4116 #endif 4117 WTF::String current = jstringToWtfString(env, currentText); 4118 GET_NATIVE_VIEW(env, obj)->passToJs(generation, current, 4119 PlatformKeyboardEvent(keyCode, keyValue, 0, down, cap, fn, sym)); 4120 } 4121 4122 static void ScrollFocusedTextInput(JNIEnv *env, jobject obj, jfloat xPercent, 4123 jint y) 4124 { 4125 #ifdef ANDROID_INSTRUMENT 4126 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4127 #endif 4128 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4129 viewImpl->scrollFocusedTextInput(xPercent, y); 4130 } 4131 4132 static void SetFocusControllerActive(JNIEnv *env, jobject obj, jboolean active) 4133 { 4134 #ifdef ANDROID_INSTRUMENT 4135 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4136 #endif 4137 LOGV("webviewcore::nativeSetFocusControllerActive()\n"); 4138 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4139 LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetFocusControllerActive"); 4140 viewImpl->setFocusControllerActive(active); 4141 } 4142 4143 static void SaveDocumentState(JNIEnv *env, jobject obj, jint frame) 4144 { 4145 #ifdef ANDROID_INSTRUMENT 4146 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4147 #endif 4148 LOGV("webviewcore::nativeSaveDocumentState()\n"); 4149 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4150 LOG_ASSERT(viewImpl, "viewImpl not set in nativeSaveDocumentState"); 4151 viewImpl->saveDocumentState((WebCore::Frame*) frame); 4152 } 4153 4154 void WebViewCore::addVisitedLink(const UChar* string, int length) 4155 { 4156 if (m_groupForVisitedLinks) 4157 m_groupForVisitedLinks->addVisitedLink(string, length); 4158 } 4159 4160 static bool UpdateLayers(JNIEnv *env, jobject obj, jint jbaseLayer) 4161 { 4162 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4163 BaseLayerAndroid* baseLayer = (BaseLayerAndroid*) jbaseLayer; 4164 if (baseLayer) { 4165 LayerAndroid* root = static_cast<LayerAndroid*>(baseLayer->getChild(0)); 4166 if (root) 4167 return viewImpl->updateLayers(root); 4168 } 4169 return true; 4170 } 4171 4172 static jint RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt) 4173 { 4174 #ifdef ANDROID_INSTRUMENT 4175 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4176 #endif 4177 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4178 SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region); 4179 SkIPoint nativePt; 4180 BaseLayerAndroid* result = viewImpl->recordContent(nativeRegion, &nativePt); 4181 GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt); 4182 return reinterpret_cast<jint>(result); 4183 } 4184 4185 static void SplitContent(JNIEnv *env, jobject obj, jint content) 4186 { 4187 #ifdef ANDROID_INSTRUMENT 4188 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4189 #endif 4190 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4191 viewImpl->splitContent(reinterpret_cast<PictureSet*>(content)); 4192 } 4193 4194 static void SendListBoxChoice(JNIEnv* env, jobject obj, jint choice) 4195 { 4196 #ifdef ANDROID_INSTRUMENT 4197 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4198 #endif 4199 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4200 LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoice"); 4201 viewImpl->popupReply(choice); 4202 } 4203 4204 // Set aside a predetermined amount of space in which to place the listbox 4205 // choices, to avoid unnecessary allocations. 4206 // The size here is arbitrary. We want the size to be at least as great as the 4207 // number of items in the average multiple-select listbox. 4208 #define PREPARED_LISTBOX_STORAGE 10 4209 4210 static void SendListBoxChoices(JNIEnv* env, jobject obj, jbooleanArray jArray, 4211 jint size) 4212 { 4213 #ifdef ANDROID_INSTRUMENT 4214 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4215 #endif 4216 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4217 LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoices"); 4218 jboolean* ptrArray = env->GetBooleanArrayElements(jArray, 0); 4219 SkAutoSTMalloc<PREPARED_LISTBOX_STORAGE, int> storage(size); 4220 int* array = storage.get(); 4221 int count = 0; 4222 for (int i = 0; i < size; i++) { 4223 if (ptrArray[i]) { 4224 array[count++] = i; 4225 } 4226 } 4227 env->ReleaseBooleanArrayElements(jArray, ptrArray, JNI_ABORT); 4228 viewImpl->popupReply(array, count); 4229 } 4230 4231 static jstring FindAddress(JNIEnv *env, jobject obj, jstring addr, 4232 jboolean caseInsensitive) 4233 { 4234 #ifdef ANDROID_INSTRUMENT 4235 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4236 #endif 4237 if (!addr) 4238 return 0; 4239 int length = env->GetStringLength(addr); 4240 if (!length) 4241 return 0; 4242 const jchar* addrChars = env->GetStringChars(addr, 0); 4243 int start, end; 4244 bool success = CacheBuilder::FindAddress(addrChars, length, 4245 &start, &end, caseInsensitive) == CacheBuilder::FOUND_COMPLETE; 4246 jstring ret = 0; 4247 if (success) 4248 ret = env->NewString(addrChars + start, end - start); 4249 env->ReleaseStringChars(addr, addrChars); 4250 return ret; 4251 } 4252 4253 static jboolean HandleTouchEvent(JNIEnv *env, jobject obj, jint action, jintArray idArray, 4254 jintArray xArray, jintArray yArray, 4255 jint count, jint actionIndex, jint metaState) 4256 { 4257 #ifdef ANDROID_INSTRUMENT 4258 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4259 #endif 4260 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4261 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4262 jint* ptrIdArray = env->GetIntArrayElements(idArray, 0); 4263 jint* ptrXArray = env->GetIntArrayElements(xArray, 0); 4264 jint* ptrYArray = env->GetIntArrayElements(yArray, 0); 4265 Vector<int> ids(count); 4266 Vector<IntPoint> points(count); 4267 for (int c = 0; c < count; c++) { 4268 ids[c] = ptrIdArray[c]; 4269 points[c].setX(ptrXArray[c]); 4270 points[c].setY(ptrYArray[c]); 4271 } 4272 env->ReleaseIntArrayElements(idArray, ptrIdArray, JNI_ABORT); 4273 env->ReleaseIntArrayElements(xArray, ptrXArray, JNI_ABORT); 4274 env->ReleaseIntArrayElements(yArray, ptrYArray, JNI_ABORT); 4275 4276 return viewImpl->handleTouchEvent(action, ids, points, actionIndex, metaState); 4277 } 4278 4279 static void TouchUp(JNIEnv *env, jobject obj, jint touchGeneration, 4280 jint frame, jint node, jint x, jint y) 4281 { 4282 #ifdef ANDROID_INSTRUMENT 4283 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4284 #endif 4285 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4286 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4287 viewImpl->touchUp(touchGeneration, 4288 (WebCore::Frame*) frame, (WebCore::Node*) node, x, y); 4289 } 4290 4291 static jstring RetrieveHref(JNIEnv *env, jobject obj, jint x, jint y) 4292 { 4293 #ifdef ANDROID_INSTRUMENT 4294 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4295 #endif 4296 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4297 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4298 WTF::String result = viewImpl->retrieveHref(x, y); 4299 if (!result.isEmpty()) 4300 return wtfStringToJstring(env, result); 4301 return 0; 4302 } 4303 4304 static jstring RetrieveAnchorText(JNIEnv *env, jobject obj, jint x, jint y) 4305 { 4306 #ifdef ANDROID_INSTRUMENT 4307 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4308 #endif 4309 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4310 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4311 WTF::String result = viewImpl->retrieveAnchorText(x, y); 4312 if (!result.isEmpty()) 4313 return wtfStringToJstring(env, result); 4314 return 0; 4315 } 4316 4317 static jstring RetrieveImageSource(JNIEnv *env, jobject obj, jint x, jint y) 4318 { 4319 WTF::String result = GET_NATIVE_VIEW(env, obj)->retrieveImageSource(x, y); 4320 return !result.isEmpty() ? wtfStringToJstring(env, result) : 0; 4321 } 4322 4323 static void StopPaintingCaret(JNIEnv *env, jobject obj) 4324 { 4325 GET_NATIVE_VIEW(env, obj)->setShouldPaintCaret(false); 4326 } 4327 4328 static void MoveFocus(JNIEnv *env, jobject obj, jint framePtr, jint nodePtr) 4329 { 4330 #ifdef ANDROID_INSTRUMENT 4331 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4332 #endif 4333 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4334 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4335 viewImpl->moveFocus((WebCore::Frame*) framePtr, (WebCore::Node*) nodePtr); 4336 } 4337 4338 static void MoveMouse(JNIEnv *env, jobject obj, jint frame, 4339 jint x, jint y) 4340 { 4341 #ifdef ANDROID_INSTRUMENT 4342 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4343 #endif 4344 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4345 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4346 viewImpl->moveMouse((WebCore::Frame*) frame, x, y); 4347 } 4348 4349 static void MoveMouseIfLatest(JNIEnv *env, jobject obj, jint moveGeneration, 4350 jint frame, jint x, jint y) 4351 { 4352 #ifdef ANDROID_INSTRUMENT 4353 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4354 #endif 4355 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4356 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4357 viewImpl->moveMouseIfLatest(moveGeneration, 4358 (WebCore::Frame*) frame, x, y); 4359 } 4360 4361 static void UpdateFrameCache(JNIEnv *env, jobject obj) 4362 { 4363 #ifdef ANDROID_INSTRUMENT 4364 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4365 #endif 4366 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4367 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4368 viewImpl->updateFrameCache(); 4369 } 4370 4371 static jint GetContentMinPrefWidth(JNIEnv *env, jobject obj) 4372 { 4373 #ifdef ANDROID_INSTRUMENT 4374 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4375 #endif 4376 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4377 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4378 4379 WebCore::Frame* frame = viewImpl->mainFrame(); 4380 if (frame) { 4381 WebCore::Document* document = frame->document(); 4382 if (document) { 4383 WebCore::RenderObject* renderer = document->renderer(); 4384 if (renderer && renderer->isRenderView()) { 4385 return renderer->minPreferredLogicalWidth(); 4386 } 4387 } 4388 } 4389 return 0; 4390 } 4391 4392 static void SetViewportSettingsFromNative(JNIEnv *env, jobject obj) 4393 { 4394 #ifdef ANDROID_INSTRUMENT 4395 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4396 #endif 4397 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4398 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4399 4400 WebCore::Settings* s = viewImpl->mainFrame()->page()->settings(); 4401 if (!s) 4402 return; 4403 4404 #ifdef ANDROID_META_SUPPORT 4405 env->SetIntField(obj, gWebViewCoreFields.m_viewportWidth, s->viewportWidth()); 4406 env->SetIntField(obj, gWebViewCoreFields.m_viewportHeight, s->viewportHeight()); 4407 env->SetIntField(obj, gWebViewCoreFields.m_viewportInitialScale, s->viewportInitialScale()); 4408 env->SetIntField(obj, gWebViewCoreFields.m_viewportMinimumScale, s->viewportMinimumScale()); 4409 env->SetIntField(obj, gWebViewCoreFields.m_viewportMaximumScale, s->viewportMaximumScale()); 4410 env->SetBooleanField(obj, gWebViewCoreFields.m_viewportUserScalable, s->viewportUserScalable()); 4411 env->SetIntField(obj, gWebViewCoreFields.m_viewportDensityDpi, s->viewportTargetDensityDpi()); 4412 #endif 4413 } 4414 4415 static void SetBackgroundColor(JNIEnv *env, jobject obj, jint color) 4416 { 4417 #ifdef ANDROID_INSTRUMENT 4418 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4419 #endif 4420 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4421 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4422 4423 viewImpl->setBackgroundColor((SkColor) color); 4424 } 4425 4426 static void DumpDomTree(JNIEnv *env, jobject obj, jboolean useFile) 4427 { 4428 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4429 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4430 4431 viewImpl->dumpDomTree(useFile); 4432 } 4433 4434 static void DumpRenderTree(JNIEnv *env, jobject obj, jboolean useFile) 4435 { 4436 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4437 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4438 4439 viewImpl->dumpRenderTree(useFile); 4440 } 4441 4442 static void DumpNavTree(JNIEnv *env, jobject obj) 4443 { 4444 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4445 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4446 4447 viewImpl->dumpNavTree(); 4448 } 4449 4450 static void DumpV8Counters(JNIEnv*, jobject) 4451 { 4452 #if USE(V8) 4453 #ifdef ANDROID_INSTRUMENT 4454 V8Counters::dumpCounters(); 4455 #endif 4456 #endif 4457 } 4458 4459 static void SetJsFlags(JNIEnv *env, jobject obj, jstring flags) 4460 { 4461 #if USE(V8) 4462 WTF::String flagsString = jstringToWtfString(env, flags); 4463 WTF::CString utf8String = flagsString.utf8(); 4464 WebCore::ScriptController::setFlags(utf8String.data(), utf8String.length()); 4465 #endif 4466 } 4467 4468 4469 // Called from the Java side to set a new quota for the origin or new appcache 4470 // max size in response to a notification that the original quota was exceeded or 4471 // that the appcache has reached its maximum size. 4472 static void SetNewStorageLimit(JNIEnv* env, jobject obj, jlong quota) { 4473 #if ENABLE(DATABASE) || ENABLE(OFFLINE_WEB_APPLICATIONS) 4474 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4475 Frame* frame = viewImpl->mainFrame(); 4476 4477 // The main thread is blocked awaiting this response, so now we can wake it 4478 // up. 4479 ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client()); 4480 chromeC->wakeUpMainThreadWithNewQuota(quota); 4481 #endif 4482 } 4483 4484 // Called from Java to provide a Geolocation permission state for the specified origin. 4485 static void GeolocationPermissionsProvide(JNIEnv* env, jobject obj, jstring origin, jboolean allow, jboolean remember) { 4486 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4487 Frame* frame = viewImpl->mainFrame(); 4488 4489 ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client()); 4490 chromeClient->provideGeolocationPermissions(jstringToWtfString(env, origin), allow, remember); 4491 } 4492 4493 static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jstring scheme) { 4494 #ifdef ANDROID_INSTRUMENT 4495 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); 4496 #endif 4497 WebCore::SchemeRegistry::registerURLSchemeAsLocal(jstringToWtfString(env, scheme)); 4498 } 4499 4500 static bool FocusBoundsChanged(JNIEnv* env, jobject obj) 4501 { 4502 return GET_NATIVE_VIEW(env, obj)->focusBoundsChanged(); 4503 } 4504 4505 static void SetIsPaused(JNIEnv* env, jobject obj, jboolean isPaused) 4506 { 4507 // tell the webcore thread to stop thinking while we do other work 4508 // (selection and scrolling). This has nothing to do with the lifecycle 4509 // pause and resume. 4510 GET_NATIVE_VIEW(env, obj)->setIsPaused(isPaused); 4511 } 4512 4513 static void Pause(JNIEnv* env, jobject obj) 4514 { 4515 // This is called for the foreground tab when the browser is put to the 4516 // background (and also for any tab when it is put to the background of the 4517 // browser). The browser can only be killed by the system when it is in the 4518 // background, so saving the Geolocation permission state now ensures that 4519 // is maintained when the browser is killed. 4520 ChromeClient* chromeClient = GET_NATIVE_VIEW(env, obj)->mainFrame()->page()->chrome()->client(); 4521 ChromeClientAndroid* chromeClientAndroid = static_cast<ChromeClientAndroid*>(chromeClient); 4522 chromeClientAndroid->storeGeolocationPermissions(); 4523 4524 Frame* mainFrame = GET_NATIVE_VIEW(env, obj)->mainFrame(); 4525 for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) { 4526 Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation(); 4527 if (geolocation) 4528 geolocation->suspend(); 4529 } 4530 4531 GET_NATIVE_VIEW(env, obj)->deviceMotionAndOrientationManager()->maybeSuspendClients(); 4532 4533 ANPEvent event; 4534 SkANP::InitEvent(&event, kLifecycle_ANPEventType); 4535 event.data.lifecycle.action = kPause_ANPLifecycleAction; 4536 GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event); 4537 4538 GET_NATIVE_VIEW(env, obj)->setIsPaused(true); 4539 } 4540 4541 static void Resume(JNIEnv* env, jobject obj) 4542 { 4543 Frame* mainFrame = GET_NATIVE_VIEW(env, obj)->mainFrame(); 4544 for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) { 4545 Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation(); 4546 if (geolocation) 4547 geolocation->resume(); 4548 } 4549 4550 GET_NATIVE_VIEW(env, obj)->deviceMotionAndOrientationManager()->maybeResumeClients(); 4551 4552 ANPEvent event; 4553 SkANP::InitEvent(&event, kLifecycle_ANPEventType); 4554 event.data.lifecycle.action = kResume_ANPLifecycleAction; 4555 GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event); 4556 4557 GET_NATIVE_VIEW(env, obj)->setIsPaused(false); 4558 } 4559 4560 static void FreeMemory(JNIEnv* env, jobject obj) 4561 { 4562 ANPEvent event; 4563 SkANP::InitEvent(&event, kLifecycle_ANPEventType); 4564 event.data.lifecycle.action = kFreeMemory_ANPLifecycleAction; 4565 GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event); 4566 } 4567 4568 static void ProvideVisitedHistory(JNIEnv *env, jobject obj, jobject hist) 4569 { 4570 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4571 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); 4572 4573 jobjectArray array = static_cast<jobjectArray>(hist); 4574 4575 jsize len = env->GetArrayLength(array); 4576 for (jsize i = 0; i < len; i++) { 4577 jstring item = static_cast<jstring>(env->GetObjectArrayElement(array, i)); 4578 const UChar* str = static_cast<const UChar*>(env->GetStringChars(item, 0)); 4579 jsize len = env->GetStringLength(item); 4580 viewImpl->addVisitedLink(str, len); 4581 env->ReleaseStringChars(item, str); 4582 env->DeleteLocalRef(item); 4583 } 4584 } 4585 4586 static void PluginSurfaceReady(JNIEnv* env, jobject obj) 4587 { 4588 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4589 if (viewImpl) 4590 viewImpl->sendPluginSurfaceReady(); 4591 } 4592 4593 // Notification from the UI thread that the plugin's full-screen surface has been discarded 4594 static void FullScreenPluginHidden(JNIEnv* env, jobject obj, jint npp) 4595 { 4596 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4597 PluginWidgetAndroid* plugin = viewImpl->getPluginWidget((NPP)npp); 4598 if (plugin) 4599 plugin->exitFullScreen(false); 4600 } 4601 4602 static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj) 4603 { 4604 int L, T, R, B; 4605 GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B); 4606 return WebCore::IntRect(L, T, R - L, B - T); 4607 } 4608 4609 static bool ValidNodeAndBounds(JNIEnv *env, jobject obj, int frame, int node, 4610 jobject rect) 4611 { 4612 IntRect nativeRect = jrect_to_webrect(env, rect); 4613 return GET_NATIVE_VIEW(env, obj)->validNodeAndBounds( 4614 reinterpret_cast<Frame*>(frame), 4615 reinterpret_cast<Node*>(node), nativeRect); 4616 } 4617 4618 static jobject GetTouchHighlightRects(JNIEnv* env, jobject obj, jint x, jint y, jint slop) 4619 { 4620 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4621 if (!viewImpl) 4622 return 0; 4623 Vector<IntRect> rects = viewImpl->getTouchHighlightRects(x, y, slop); 4624 if (rects.isEmpty()) 4625 return 0; 4626 4627 jclass arrayClass = env->FindClass("java/util/ArrayList"); 4628 LOG_ASSERT(arrayClass, "Could not find java/util/ArrayList"); 4629 jmethodID init = env->GetMethodID(arrayClass, "<init>", "(I)V"); 4630 LOG_ASSERT(init, "Could not find constructor for ArrayList"); 4631 jobject array = env->NewObject(arrayClass, init, rects.size()); 4632 LOG_ASSERT(array, "Could not create a new ArrayList"); 4633 jmethodID add = env->GetMethodID(arrayClass, "add", "(Ljava/lang/Object;)Z"); 4634 LOG_ASSERT(add, "Could not find add method on ArrayList"); 4635 jclass rectClass = env->FindClass("android/graphics/Rect"); 4636 LOG_ASSERT(rectClass, "Could not find android/graphics/Rect"); 4637 jmethodID rectinit = env->GetMethodID(rectClass, "<init>", "(IIII)V"); 4638 LOG_ASSERT(rectinit, "Could not find init method on Rect"); 4639 4640 for (size_t i = 0; i < rects.size(); i++) { 4641 jobject rect = env->NewObject(rectClass, rectinit, rects[i].x(), 4642 rects[i].y(), rects[i].maxX(), rects[i].maxY()); 4643 if (rect) { 4644 env->CallBooleanMethod(array, add, rect); 4645 env->DeleteLocalRef(rect); 4646 } 4647 } 4648 4649 env->DeleteLocalRef(rectClass); 4650 env->DeleteLocalRef(arrayClass); 4651 return array; 4652 } 4653 4654 static void AutoFillForm(JNIEnv* env, jobject obj, jint queryId) 4655 { 4656 #if ENABLE(WEB_AUTOFILL) 4657 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); 4658 if (!viewImpl) 4659 return; 4660 4661 WebCore::Frame* frame = viewImpl->mainFrame(); 4662 if (frame) { 4663 EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(frame->page()->editorClient()); 4664 WebAutofill* autoFill = editorC->getAutofill(); 4665 autoFill->fillFormFields(queryId); 4666 } 4667 #endif 4668 } 4669 4670 static void CloseIdleConnections(JNIEnv* env, jobject obj) 4671 { 4672 #if USE(CHROME_NETWORK_STACK) 4673 WebCache::get(true)->closeIdleConnections(); 4674 WebCache::get(false)->closeIdleConnections(); 4675 #endif 4676 } 4677 4678 static void ScrollRenderLayer(JNIEnv* env, jobject obj, jint layer, jobject jRect) 4679 { 4680 SkRect rect; 4681 GraphicsJNI::jrect_to_rect(env, jRect, &rect); 4682 GET_NATIVE_VIEW(env, obj)->scrollRenderLayer(layer, rect); 4683 } 4684 4685 // ---------------------------------------------------------------------------- 4686 4687 /* 4688 * JNI registration. 4689 */ 4690 static JNINativeMethod gJavaWebViewCoreMethods[] = { 4691 { "nativeClearContent", "()V", 4692 (void*) ClearContent }, 4693 { "nativeFocusBoundsChanged", "()Z", 4694 (void*) FocusBoundsChanged } , 4695 { "nativeKey", "(IIIZZZZ)Z", 4696 (void*) Key }, 4697 { "nativeClick", "(IIZ)V", 4698 (void*) Click }, 4699 { "nativeContentInvalidateAll", "()V", 4700 (void*) ContentInvalidateAll }, 4701 { "nativeSendListBoxChoices", "([ZI)V", 4702 (void*) SendListBoxChoices }, 4703 { "nativeSendListBoxChoice", "(I)V", 4704 (void*) SendListBoxChoice }, 4705 { "nativeSetSize", "(IIIFIIIIZ)V", 4706 (void*) SetSize }, 4707 { "nativeSetScrollOffset", "(IZII)V", 4708 (void*) SetScrollOffset }, 4709 { "nativeSetGlobalBounds", "(IIII)V", 4710 (void*) SetGlobalBounds }, 4711 { "nativeSetSelection", "(II)V", 4712 (void*) SetSelection } , 4713 { "nativeModifySelection", "(II)Ljava/lang/String;", 4714 (void*) ModifySelection }, 4715 { "nativeDeleteSelection", "(III)V", 4716 (void*) DeleteSelection } , 4717 { "nativeReplaceTextfieldText", "(IILjava/lang/String;III)V", 4718 (void*) ReplaceTextfieldText } , 4719 { "nativeMoveFocus", "(II)V", 4720 (void*) MoveFocus }, 4721 { "nativeMoveMouse", "(III)V", 4722 (void*) MoveMouse }, 4723 { "nativeMoveMouseIfLatest", "(IIII)V", 4724 (void*) MoveMouseIfLatest }, 4725 { "passToJs", "(ILjava/lang/String;IIZZZZ)V", 4726 (void*) PassToJs }, 4727 { "nativeScrollFocusedTextInput", "(FI)V", 4728 (void*) ScrollFocusedTextInput }, 4729 { "nativeSetFocusControllerActive", "(Z)V", 4730 (void*) SetFocusControllerActive }, 4731 { "nativeSaveDocumentState", "(I)V", 4732 (void*) SaveDocumentState }, 4733 { "nativeFindAddress", "(Ljava/lang/String;Z)Ljava/lang/String;", 4734 (void*) FindAddress }, 4735 { "nativeHandleTouchEvent", "(I[I[I[IIII)Z", 4736 (void*) HandleTouchEvent }, 4737 { "nativeTouchUp", "(IIIII)V", 4738 (void*) TouchUp }, 4739 { "nativeRetrieveHref", "(II)Ljava/lang/String;", 4740 (void*) RetrieveHref }, 4741 { "nativeRetrieveAnchorText", "(II)Ljava/lang/String;", 4742 (void*) RetrieveAnchorText }, 4743 { "nativeRetrieveImageSource", "(II)Ljava/lang/String;", 4744 (void*) RetrieveImageSource }, 4745 { "nativeStopPaintingCaret", "()V", 4746 (void*) StopPaintingCaret }, 4747 { "nativeUpdateFrameCache", "()V", 4748 (void*) UpdateFrameCache }, 4749 { "nativeGetContentMinPrefWidth", "()I", 4750 (void*) GetContentMinPrefWidth }, 4751 { "nativeUpdateLayers", "(I)Z", 4752 (void*) UpdateLayers }, 4753 { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)I", 4754 (void*) RecordContent }, 4755 { "setViewportSettingsFromNative", "()V", 4756 (void*) SetViewportSettingsFromNative }, 4757 { "nativeSplitContent", "(I)V", 4758 (void*) SplitContent }, 4759 { "nativeSetBackgroundColor", "(I)V", 4760 (void*) SetBackgroundColor }, 4761 { "nativeRegisterURLSchemeAsLocal", "(Ljava/lang/String;)V", 4762 (void*) RegisterURLSchemeAsLocal }, 4763 { "nativeDumpDomTree", "(Z)V", 4764 (void*) DumpDomTree }, 4765 { "nativeDumpRenderTree", "(Z)V", 4766 (void*) DumpRenderTree }, 4767 { "nativeDumpNavTree", "()V", 4768 (void*) DumpNavTree }, 4769 { "nativeDumpV8Counters", "()V", 4770 (void*) DumpV8Counters }, 4771 { "nativeSetNewStorageLimit", "(J)V", 4772 (void*) SetNewStorageLimit }, 4773 { "nativeGeolocationPermissionsProvide", "(Ljava/lang/String;ZZ)V", 4774 (void*) GeolocationPermissionsProvide }, 4775 { "nativeSetIsPaused", "(Z)V", (void*) SetIsPaused }, 4776 { "nativePause", "()V", (void*) Pause }, 4777 { "nativeResume", "()V", (void*) Resume }, 4778 { "nativeFreeMemory", "()V", (void*) FreeMemory }, 4779 { "nativeSetJsFlags", "(Ljava/lang/String;)V", (void*) SetJsFlags }, 4780 { "nativeRequestLabel", "(II)Ljava/lang/String;", 4781 (void*) RequestLabel }, 4782 { "nativeRevealSelection", "()V", (void*) RevealSelection }, 4783 { "nativeUpdateFrameCacheIfLoading", "()V", 4784 (void*) UpdateFrameCacheIfLoading }, 4785 { "nativeProvideVisitedHistory", "([Ljava/lang/String;)V", 4786 (void*) ProvideVisitedHistory }, 4787 { "nativeFullScreenPluginHidden", "(I)V", 4788 (void*) FullScreenPluginHidden }, 4789 { "nativePluginSurfaceReady", "()V", 4790 (void*) PluginSurfaceReady }, 4791 { "nativeValidNodeAndBounds", "(IILandroid/graphics/Rect;)Z", 4792 (void*) ValidNodeAndBounds }, 4793 { "nativeGetTouchHighlightRects", "(III)Ljava/util/ArrayList;", 4794 (void*) GetTouchHighlightRects }, 4795 { "nativeAutoFillForm", "(I)V", 4796 (void*) AutoFillForm }, 4797 { "nativeScrollLayer", "(ILandroid/graphics/Rect;)V", 4798 (void*) ScrollRenderLayer }, 4799 { "nativeCloseIdleConnections", "()V", 4800 (void*) CloseIdleConnections }, 4801 }; 4802 4803 int registerWebViewCore(JNIEnv* env) 4804 { 4805 jclass widget = env->FindClass("android/webkit/WebViewCore"); 4806 LOG_ASSERT(widget, 4807 "Unable to find class android/webkit/WebViewCore"); 4808 gWebViewCoreFields.m_nativeClass = env->GetFieldID(widget, "mNativeClass", 4809 "I"); 4810 LOG_ASSERT(gWebViewCoreFields.m_nativeClass, 4811 "Unable to find android/webkit/WebViewCore.mNativeClass"); 4812 gWebViewCoreFields.m_viewportWidth = env->GetFieldID(widget, 4813 "mViewportWidth", "I"); 4814 LOG_ASSERT(gWebViewCoreFields.m_viewportWidth, 4815 "Unable to find android/webkit/WebViewCore.mViewportWidth"); 4816 gWebViewCoreFields.m_viewportHeight = env->GetFieldID(widget, 4817 "mViewportHeight", "I"); 4818 LOG_ASSERT(gWebViewCoreFields.m_viewportHeight, 4819 "Unable to find android/webkit/WebViewCore.mViewportHeight"); 4820 gWebViewCoreFields.m_viewportInitialScale = env->GetFieldID(widget, 4821 "mViewportInitialScale", "I"); 4822 LOG_ASSERT(gWebViewCoreFields.m_viewportInitialScale, 4823 "Unable to find android/webkit/WebViewCore.mViewportInitialScale"); 4824 gWebViewCoreFields.m_viewportMinimumScale = env->GetFieldID(widget, 4825 "mViewportMinimumScale", "I"); 4826 LOG_ASSERT(gWebViewCoreFields.m_viewportMinimumScale, 4827 "Unable to find android/webkit/WebViewCore.mViewportMinimumScale"); 4828 gWebViewCoreFields.m_viewportMaximumScale = env->GetFieldID(widget, 4829 "mViewportMaximumScale", "I"); 4830 LOG_ASSERT(gWebViewCoreFields.m_viewportMaximumScale, 4831 "Unable to find android/webkit/WebViewCore.mViewportMaximumScale"); 4832 gWebViewCoreFields.m_viewportUserScalable = env->GetFieldID(widget, 4833 "mViewportUserScalable", "Z"); 4834 LOG_ASSERT(gWebViewCoreFields.m_viewportUserScalable, 4835 "Unable to find android/webkit/WebViewCore.mViewportUserScalable"); 4836 gWebViewCoreFields.m_viewportDensityDpi = env->GetFieldID(widget, 4837 "mViewportDensityDpi", "I"); 4838 LOG_ASSERT(gWebViewCoreFields.m_viewportDensityDpi, 4839 "Unable to find android/webkit/WebViewCore.mViewportDensityDpi"); 4840 gWebViewCoreFields.m_webView = env->GetFieldID(widget, 4841 "mWebView", "Landroid/webkit/WebView;"); 4842 LOG_ASSERT(gWebViewCoreFields.m_webView, 4843 "Unable to find android/webkit/WebViewCore.mWebView"); 4844 gWebViewCoreFields.m_drawIsPaused = env->GetFieldID(widget, 4845 "mDrawIsPaused", "Z"); 4846 LOG_ASSERT(gWebViewCoreFields.m_drawIsPaused, 4847 "Unable to find android/webkit/WebViewCore.mDrawIsPaused"); 4848 gWebViewCoreFields.m_lowMemoryUsageMb = env->GetFieldID(widget, "mLowMemoryUsageThresholdMb", "I"); 4849 gWebViewCoreFields.m_highMemoryUsageMb = env->GetFieldID(widget, "mHighMemoryUsageThresholdMb", "I"); 4850 gWebViewCoreFields.m_highUsageDeltaMb = env->GetFieldID(widget, "mHighUsageDeltaMb", "I"); 4851 4852 gWebViewCoreStaticMethods.m_isSupportedMediaMimeType = 4853 env->GetStaticMethodID(widget, "isSupportedMediaMimeType", "(Ljava/lang/String;)Z"); 4854 LOG_FATAL_IF(!gWebViewCoreStaticMethods.m_isSupportedMediaMimeType, 4855 "Could not find static method isSupportedMediaMimeType from WebViewCore"); 4856 4857 env->DeleteLocalRef(widget); 4858 4859 return jniRegisterNativeMethods(env, "android/webkit/WebViewCore", 4860 gJavaWebViewCoreMethods, NELEM(gJavaWebViewCoreMethods)); 4861 } 4862 4863 } /* namespace android */ 4864