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