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