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