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