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