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