2 * Copyright 2006, The Android Open Source Project
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #define LOG_TAG "webcoreglue"
29 #include "WebViewCore.h"
31 #include "AccessibilityObject.h"
32 #include "BaseLayerAndroid.h"
33 #include "CachedNode.h"
34 #include "CachedRoot.h"
36 #include "ChromeClientAndroid.h"
37 #include "ChromiumIncludes.h"
39 #include "DatabaseTracker.h"
41 #include "DOMWindow.h"
42 #include "DOMSelection.h"
45 #include "EditorClientAndroid.h"
46 #include "EventHandler.h"
47 #include "EventNames.h"
48 #include "ExceptionCode.h"
49 #include "FocusController.h"
52 #include "FrameLoader.h"
53 #include "FrameLoaderClientAndroid.h"
54 #include "FrameTree.h"
55 #include "FrameView.h"
56 #include "Geolocation.h"
57 #include "GraphicsContext.h"
58 #include "GraphicsJNI.h"
59 #include "HTMLAnchorElement.h"
60 #include "HTMLAreaElement.h"
61 #include "HTMLElement.h"
62 #include "HTMLFormControlElement.h"
63 #include "HTMLImageElement.h"
64 #include "HTMLInputElement.h"
65 #include "HTMLLabelElement.h"
66 #include "HTMLMapElement.h"
67 #include "HTMLNames.h"
68 #include "HTMLOptGroupElement.h"
69 #include "HTMLOptionElement.h"
70 #include "HTMLSelectElement.h"
71 #include "HTMLTextAreaElement.h"
72 #include "HistoryItem.h"
73 #include "HitTestRequest.h"
74 #include "HitTestResult.h"
75 #include "InlineTextBox.h"
76 #include "Navigator.h"
80 #include "PageGroup.h"
81 #include "PlatformKeyboardEvent.h"
82 #include "PlatformString.h"
83 #include "PluginWidgetAndroid.h"
84 #include "PluginView.h"
86 #include "ProgressTracker.h"
88 #include "RenderBox.h"
89 #include "RenderInline.h"
90 #include "RenderLayer.h"
91 #include "RenderPart.h"
92 #include "RenderText.h"
93 #include "RenderTextControl.h"
94 #include "RenderThemeAndroid.h"
95 #include "RenderView.h"
96 #include "ResourceRequest.h"
97 #include "SchemeRegistry.h"
98 #include "SelectionController.h"
101 #include "SkTemplates.h"
102 #include "SkTDArray.h"
104 #include "SkCanvas.h"
105 #include "SkPicture.h"
108 #include "TypingCommand.h"
109 #include "WebCoreFrameBridge.h"
110 #include "WebFrameView.h"
111 #include "WindowsKeyboardCodes.h"
112 #include "android_graphics.h"
113 #include "autofill/WebAutoFill.h"
117 #include <JNIUtility.h>
118 #include <ui/KeycodeLabels.h>
119 #include <wtf/CurrentTime.h>
120 #include <wtf/text/AtomicString.h>
121 #include <wtf/text/StringImpl.h>
124 #include "ScriptController.h"
125 #include "V8Counters.h"
126 #include <wtf/text/CString.h>
133 #if ENABLE(TOUCH_EVENTS) // Android
134 #include "PlatformTouchEvent.h"
137 #ifdef ANDROID_DOM_LOGGING
138 #include "AndroidLog.h"
139 #include "RenderTreeAsText.h"
140 #include <wtf/text/CString.h>
142 FILE* gDomTreeFile = 0;
143 FILE* gRenderTreeFile = 0;
146 #ifdef ANDROID_INSTRUMENT
147 #include "TimeCounter.h"
150 #if USE(ACCELERATED_COMPOSITING)
151 #include "GraphicsLayerAndroid.h"
152 #include "RenderLayerCompositor.h"
155 /* We pass this flag when recording the actual content, so that we don't spend
156 time actually regionizing complex path clips, when all we really want to do
159 #define PICT_RECORD_FLAGS SkPicture::kUsePathBoundsForClip_RecordingFlag
161 ////////////////////////////////////////////////////////////////////////////////////////////////
165 static SkTDArray<WebViewCore*> gInstanceList;
167 void WebViewCore::addInstance(WebViewCore* inst) {
168 *gInstanceList.append() = inst;
171 void WebViewCore::removeInstance(WebViewCore* inst) {
172 int index = gInstanceList.find(inst);
173 LOG_ASSERT(index >= 0, "RemoveInstance inst not found");
175 gInstanceList.removeShuffle(index);
179 bool WebViewCore::isInstance(WebViewCore* inst) {
180 return gInstanceList.find(inst) >= 0;
183 jobject WebViewCore::getApplicationContext() {
185 // check to see if there is a valid webviewcore object
186 if (gInstanceList.isEmpty())
189 // get the context from the webview
190 jobject context = gInstanceList[0]->getContext();
195 // get the application context using JNI
196 JNIEnv* env = JSC::Bindings::getJNIEnv();
197 jclass contextClass = env->GetObjectClass(context);
198 jmethodID appContextMethod = env->GetMethodID(contextClass, "getApplicationContext", "()Landroid/content/Context;");
199 env->DeleteLocalRef(contextClass);
200 jobject result = env->CallObjectMethod(context, appContextMethod);
206 struct WebViewCoreStaticMethods {
207 jmethodID m_isSupportedMediaMimeType;
208 } gWebViewCoreStaticMethods;
210 // Check whether a media mimeType is supported in Android media framework.
211 bool WebViewCore::isSupportedMediaMimeType(const WTF::String& mimeType) {
212 JNIEnv* env = JSC::Bindings::getJNIEnv();
213 jstring jMimeType = WtfStringToJstring(env, mimeType);
214 jclass webViewCore = env->FindClass("android/webkit/WebViewCore");
215 bool val = env->CallStaticBooleanMethod(webViewCore,
216 gWebViewCoreStaticMethods.m_isSupportedMediaMimeType, jMimeType);
218 env->DeleteLocalRef(webViewCore);
219 env->DeleteLocalRef(jMimeType);
224 // ----------------------------------------------------------------------------
226 #define GET_NATIVE_VIEW(env, obj) ((WebViewCore*)env->GetIntField(obj, gWebViewCoreFields.m_nativeClass))
228 // Field ids for WebViewCore
229 struct WebViewCoreFields {
230 jfieldID m_nativeClass;
231 jfieldID m_viewportWidth;
232 jfieldID m_viewportHeight;
233 jfieldID m_viewportInitialScale;
234 jfieldID m_viewportMinimumScale;
235 jfieldID m_viewportMaximumScale;
236 jfieldID m_viewportUserScalable;
237 jfieldID m_viewportDensityDpi;
239 jfieldID m_drawIsPaused;
240 } gWebViewCoreFields;
242 // ----------------------------------------------------------------------------
244 struct WebViewCore::JavaGlue {
246 jmethodID m_spawnScrollTo;
247 jmethodID m_scrollTo;
248 jmethodID m_scrollBy;
249 jmethodID m_contentDraw;
250 jmethodID m_layersDraw;
251 jmethodID m_requestListBox;
252 jmethodID m_openFileChooser;
253 jmethodID m_requestSingleListBox;
255 jmethodID m_jsConfirm;
256 jmethodID m_jsPrompt;
257 jmethodID m_jsUnload;
258 jmethodID m_jsInterrupt;
259 jmethodID m_didFirstLayout;
260 jmethodID m_updateViewport;
261 jmethodID m_sendNotifyProgressFinished;
262 jmethodID m_sendViewInvalidate;
263 jmethodID m_updateTextfield;
264 jmethodID m_updateTextSelection;
265 jmethodID m_clearTextEntry;
266 jmethodID m_restoreScale;
267 jmethodID m_needTouchEvents;
268 jmethodID m_requestKeyboard;
269 jmethodID m_requestKeyboardWithSelection;
270 jmethodID m_exceededDatabaseQuota;
271 jmethodID m_reachedMaxAppCacheSize;
272 jmethodID m_populateVisitedLinks;
273 jmethodID m_geolocationPermissionsShowPrompt;
274 jmethodID m_geolocationPermissionsHidePrompt;
275 jmethodID m_getDeviceMotionService;
276 jmethodID m_getDeviceOrientationService;
277 jmethodID m_addMessageToConsole;
278 jmethodID m_formDidBlur;
279 jmethodID m_getPluginClass;
280 jmethodID m_showFullScreenPlugin;
281 jmethodID m_hideFullScreenPlugin;
282 jmethodID m_addSurface;
283 jmethodID m_updateSurface;
284 jmethodID m_destroySurface;
285 jmethodID m_getContext;
286 jmethodID m_sendFindAgain;
287 jmethodID m_showRect;
288 jmethodID m_centerFitRect;
289 jmethodID m_setScrollbarModes;
290 jmethodID m_setInstallableWebApp;
291 jmethodID m_setWebTextViewAutoFillable;
292 AutoJObject object(JNIEnv* env) {
293 return getRealObject(env, m_obj);
298 * WebViewCore Implementation
301 static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[])
303 jmethodID m = env->GetMethodID(clazz, name, signature);
304 LOG_ASSERT(m, "Could not find method %s", name);
308 Mutex WebViewCore::gFrameCacheMutex;
309 Mutex WebViewCore::gButtonMutex;
310 Mutex WebViewCore::gCursorBoundsMutex;
312 WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe)
313 : m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired)
314 , m_deviceMotionAndOrientationManager(this)
316 m_mainFrame = mainframe;
319 m_moveGeneration = 0;
320 m_lastGeneration = 0;
321 m_touchGeneration = 0;
322 m_blockTextfieldUpdates = false;
323 // just initial values. These should be set by client
324 m_maxXScroll = 320/4;
325 m_maxYScroll = 240/4;
326 m_textGeneration = 0;
328 m_textWrapWidth = 320;
330 #if ENABLE(TOUCH_EVENTS)
331 m_forwardingTouchEvents = false;
335 LOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!");
337 jclass clazz = env->GetObjectClass(javaWebViewCore);
338 m_javaGlue = new JavaGlue;
339 m_javaGlue->m_obj = env->NewWeakGlobalRef(javaWebViewCore);
340 m_javaGlue->m_spawnScrollTo = GetJMethod(env, clazz, "contentSpawnScrollTo", "(II)V");
341 m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(II)V");
342 m_javaGlue->m_scrollBy = GetJMethod(env, clazz, "contentScrollBy", "(IIZ)V");
343 m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V");
344 m_javaGlue->m_layersDraw = GetJMethod(env, clazz, "layersDraw", "()V");
345 m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[I[I)V");
346 m_javaGlue->m_openFileChooser = GetJMethod(env, clazz, "openFileChooser", "(Ljava/lang/String;)Ljava/lang/String;");
347 m_javaGlue->m_requestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[II)V");
348 m_javaGlue->m_jsAlert = GetJMethod(env, clazz, "jsAlert", "(Ljava/lang/String;Ljava/lang/String;)V");
349 m_javaGlue->m_jsConfirm = GetJMethod(env, clazz, "jsConfirm", "(Ljava/lang/String;Ljava/lang/String;)Z");
350 m_javaGlue->m_jsPrompt = GetJMethod(env, clazz, "jsPrompt", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
351 m_javaGlue->m_jsUnload = GetJMethod(env, clazz, "jsUnload", "(Ljava/lang/String;Ljava/lang/String;)Z");
352 m_javaGlue->m_jsInterrupt = GetJMethod(env, clazz, "jsInterrupt", "()Z");
353 m_javaGlue->m_didFirstLayout = GetJMethod(env, clazz, "didFirstLayout", "(Z)V");
354 m_javaGlue->m_updateViewport = GetJMethod(env, clazz, "updateViewport", "()V");
355 m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V");
356 m_javaGlue->m_sendViewInvalidate = GetJMethod(env, clazz, "sendViewInvalidate", "(IIII)V");
357 m_javaGlue->m_updateTextfield = GetJMethod(env, clazz, "updateTextfield", "(IZLjava/lang/String;I)V");
358 m_javaGlue->m_updateTextSelection = GetJMethod(env, clazz, "updateTextSelection", "(IIII)V");
359 m_javaGlue->m_clearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "()V");
360 m_javaGlue->m_restoreScale = GetJMethod(env, clazz, "restoreScale", "(II)V");
361 m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V");
362 m_javaGlue->m_requestKeyboard = GetJMethod(env, clazz, "requestKeyboard", "(Z)V");
363 m_javaGlue->m_requestKeyboardWithSelection = GetJMethod(env, clazz, "requestKeyboardWithSelection", "(IIII)V");
364 m_javaGlue->m_exceededDatabaseQuota = GetJMethod(env, clazz, "exceededDatabaseQuota", "(Ljava/lang/String;Ljava/lang/String;JJ)V");
365 m_javaGlue->m_reachedMaxAppCacheSize = GetJMethod(env, clazz, "reachedMaxAppCacheSize", "(J)V");
366 m_javaGlue->m_populateVisitedLinks = GetJMethod(env, clazz, "populateVisitedLinks", "()V");
367 m_javaGlue->m_geolocationPermissionsShowPrompt = GetJMethod(env, clazz, "geolocationPermissionsShowPrompt", "(Ljava/lang/String;)V");
368 m_javaGlue->m_geolocationPermissionsHidePrompt = GetJMethod(env, clazz, "geolocationPermissionsHidePrompt", "()V");
369 m_javaGlue->m_getDeviceMotionService = GetJMethod(env, clazz, "getDeviceMotionService", "()Landroid/webkit/DeviceMotionService;");
370 m_javaGlue->m_getDeviceOrientationService = GetJMethod(env, clazz, "getDeviceOrientationService", "()Landroid/webkit/DeviceOrientationService;");
371 m_javaGlue->m_addMessageToConsole = GetJMethod(env, clazz, "addMessageToConsole", "(Ljava/lang/String;ILjava/lang/String;I)V");
372 m_javaGlue->m_formDidBlur = GetJMethod(env, clazz, "formDidBlur", "(I)V");
373 m_javaGlue->m_getPluginClass = GetJMethod(env, clazz, "getPluginClass", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Class;");
374 m_javaGlue->m_showFullScreenPlugin = GetJMethod(env, clazz, "showFullScreenPlugin", "(Landroid/webkit/ViewManager$ChildView;I)V");
375 m_javaGlue->m_hideFullScreenPlugin = GetJMethod(env, clazz, "hideFullScreenPlugin", "()V");
376 m_javaGlue->m_addSurface = GetJMethod(env, clazz, "addSurface", "(Landroid/view/View;IIII)Landroid/webkit/ViewManager$ChildView;");
377 m_javaGlue->m_updateSurface = GetJMethod(env, clazz, "updateSurface", "(Landroid/webkit/ViewManager$ChildView;IIII)V");
378 m_javaGlue->m_destroySurface = GetJMethod(env, clazz, "destroySurface", "(Landroid/webkit/ViewManager$ChildView;)V");
379 m_javaGlue->m_getContext = GetJMethod(env, clazz, "getContext", "()Landroid/content/Context;");
380 m_javaGlue->m_sendFindAgain = GetJMethod(env, clazz, "sendFindAgain", "()V");
381 m_javaGlue->m_showRect = GetJMethod(env, clazz, "showRect", "(IIIIIIFFFF)V");
382 m_javaGlue->m_centerFitRect = GetJMethod(env, clazz, "centerFitRect", "(IIII)V");
383 m_javaGlue->m_setScrollbarModes = GetJMethod(env, clazz, "setScrollbarModes", "(II)V");
384 m_javaGlue->m_setInstallableWebApp = GetJMethod(env, clazz, "setInstallableWebApp", "()V");
385 m_javaGlue->m_setWebTextViewAutoFillable = GetJMethod(env, clazz, "setWebTextViewAutoFillable", "(ILjava/lang/String;)V");
386 env->DeleteLocalRef(clazz);
388 env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this);
390 m_scrollOffsetX = m_scrollOffsetY = 0;
392 PageGroup::setShouldTrackVisitedLinks(true);
396 WebViewCore::addInstance(this);
398 #if USE(CHROME_NETWORK_STACK)
399 AndroidNetworkLibraryImpl::InitWithApplicationContext(env, 0);
403 WebViewCore::~WebViewCore()
405 WebViewCore::removeInstance(this);
407 // Release the focused view
408 Release(m_popupReply);
410 if (m_javaGlue->m_obj) {
411 JNIEnv* env = JSC::Bindings::getJNIEnv();
412 env->DeleteWeakGlobalRef(m_javaGlue->m_obj);
413 m_javaGlue->m_obj = 0;
416 delete m_frameCacheKit;
417 delete m_navPictureKit;
420 WebViewCore* WebViewCore::getWebViewCore(const WebCore::FrameView* view)
422 return getWebViewCore(static_cast<const WebCore::ScrollView*>(view));
425 WebViewCore* WebViewCore::getWebViewCore(const WebCore::ScrollView* view)
430 WebFrameView* webFrameView = static_cast<WebFrameView*>(view->platformWidget());
433 return webFrameView->webViewCore();
436 void WebViewCore::reset(bool fromConstructor)
439 if (fromConstructor) {
443 gFrameCacheMutex.lock();
444 delete m_frameCacheKit;
445 delete m_navPictureKit;
448 gFrameCacheMutex.unlock();
452 m_lastFocusedBounds = WebCore::IntRect(0,0,0,0);
453 m_focusBoundsChanged = false;
454 m_lastFocusedSelStart = 0;
455 m_lastFocusedSelEnd = 0;
457 m_updatedFrameCache = true;
458 m_frameCacheOutOfDate = true;
459 m_skipContentDraw = false;
461 m_domtree_version = 0;
462 m_check_domtree_version = true;
463 m_progressDone = false;
464 m_hasCursorBounds = false;
470 m_groupForVisitedLinks = NULL;
471 m_currentNodeDomNavigationAxis = 0;
474 static bool layoutIfNeededRecursive(WebCore::Frame* f)
479 WebCore::FrameView* v = f->view();
483 if (v->needsLayout())
484 v->layout(f->tree()->parent());
486 WebCore::Frame* child = f->tree()->firstChild();
489 success &= layoutIfNeededRecursive(child);
490 child = child->tree()->nextSibling();
493 return success && !v->needsLayout();
496 CacheBuilder& WebViewCore::cacheBuilder()
498 return FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder();
501 WebCore::Node* WebViewCore::currentFocus()
503 return cacheBuilder().currentFocus();
506 void WebViewCore::recordPicture(SkPicture* picture)
508 // if there is no document yet, just return
509 if (!m_mainFrame->document()) {
510 DBG_NAV_LOG("no document");
513 // Call layout to ensure that the contentWidth and contentHeight are correct
514 if (!layoutIfNeededRecursive(m_mainFrame)) {
515 DBG_NAV_LOG("layout failed");
518 // draw into the picture's recording canvas
519 WebCore::FrameView* view = m_mainFrame->view();
520 DBG_NAV_LOGD("view=(w=%d,h=%d)", view->contentsWidth(),
521 view->contentsHeight());
522 SkAutoPictureRecord arp(picture, view->contentsWidth(),
523 view->contentsHeight(), PICT_RECORD_FLAGS);
524 SkAutoMemoryUsageProbe mup(__FUNCTION__);
526 // Copy m_buttons so we can pass it to our graphics context.
528 WTF::Vector<Container> buttons(m_buttons);
529 gButtonMutex.unlock();
531 WebCore::PlatformGraphicsContext pgc(arp.getRecordingCanvas(), &buttons);
532 WebCore::GraphicsContext gc(&pgc);
533 view->platformWidget()->draw(&gc, WebCore::IntRect(0, 0,
534 view->contentsWidth(), view->contentsHeight()));
537 updateButtonList(&buttons);
538 gButtonMutex.unlock();
541 void WebViewCore::recordPictureSet(PictureSet* content)
543 // if there is no document yet, just return
544 if (!m_mainFrame->document()) {
545 DBG_SET_LOG("!m_mainFrame->document()");
548 if (m_addInval.isEmpty()) {
549 DBG_SET_LOG("m_addInval.isEmpty()");
552 // Call layout to ensure that the contentWidth and contentHeight are correct
553 // it's fine for layout to gather invalidates, but defeat sending a message
554 // back to java to call webkitDraw, since we're already in the middle of
556 m_skipContentDraw = true;
557 bool success = layoutIfNeededRecursive(m_mainFrame);
558 m_skipContentDraw = false;
560 // We may be mid-layout and thus cannot draw.
564 { // collect WebViewCoreRecordTimeCounter after layoutIfNeededRecursive
565 #ifdef ANDROID_INSTRUMENT
566 TimeCounterAuto counter(TimeCounter::WebViewCoreRecordTimeCounter);
569 // if the webkit page dimensions changed, discard the pictureset and redraw.
570 WebCore::FrameView* view = m_mainFrame->view();
571 int width = view->contentsWidth();
572 int height = view->contentsHeight();
574 // Use the contents width and height as a starting point.
576 contentRect.set(0, 0, width, height);
577 SkIRect total(contentRect);
579 // Traverse all the frames and add their sizes if they are in the visible
581 for (WebCore::Frame* frame = m_mainFrame->tree()->traverseNext(); frame;
582 frame = frame->tree()->traverseNext()) {
583 // If the frame doesn't have an owner then it is the top frame and the
584 // view size is the frame size.
585 WebCore::RenderPart* owner = frame->ownerRenderer();
586 if (owner && owner->style()->visibility() == VISIBLE) {
590 // Traverse the tree up to the parent to find the absolute position
592 WebCore::Frame* parent = frame->tree()->parent();
594 WebCore::RenderPart* parentOwner = parent->ownerRenderer();
596 x += parentOwner->x();
597 y += parentOwner->y();
599 parent = parent->tree()->parent();
601 // Use the owner dimensions so that padding and border are
603 int right = x + owner->width();
604 int bottom = y + owner->height();
605 SkIRect frameRect = {x, y, right, bottom};
606 // Ignore a width or height that is smaller than 1. Some iframes
607 // have small dimensions in order to be hidden. The iframe
608 // expansion code does not expand in that case so we should ignore
610 if (frameRect.width() > 1 && frameRect.height() > 1
611 && SkIRect::Intersects(total, frameRect))
612 total.join(x, y, right, bottom);
616 // If the new total is larger than the content, resize the view to include
618 if (!contentRect.contains(total)) {
619 // Resize the view to change the overflow clip.
620 view->resize(total.fRight, total.fBottom);
622 // We have to force a layout in order for the clip to change.
623 m_mainFrame->contentRenderer()->setNeedsLayoutAndPrefWidthsRecalc();
626 // Relayout similar to above
627 m_skipContentDraw = true;
628 bool success = layoutIfNeededRecursive(m_mainFrame);
629 m_skipContentDraw = false;
633 // Set the computed content width
634 width = view->contentsWidth();
635 height = view->contentsHeight();
638 if (cacheBuilder().pictureSetDisabled())
641 content->checkDimensions(width, height, &m_addInval);
643 // The inval region may replace existing pictures. The existing pictures
644 // may have already been split into pieces. If reuseSubdivided() returns
645 // true, the split pieces are the last entries in the picture already. They
646 // are marked as invalid, and are rebuilt by rebuildPictureSet().
648 // If the new region doesn't match a set of split pieces, add it to the end.
649 if (!content->reuseSubdivided(m_addInval)) {
650 const SkIRect& inval = m_addInval.getBounds();
651 SkPicture* picture = rebuildPicture(inval);
652 DBG_SET_LOGD("{%d,%d,w=%d,h=%d}", inval.fLeft,
653 inval.fTop, inval.width(), inval.height());
654 content->add(m_addInval, picture, 0, false);
655 picture->safeUnref();
657 // Remove any pictures already in the set that are obscured by the new one,
658 // and check to see if any already split pieces need to be redrawn.
659 if (content->build())
660 rebuildPictureSet(content);
661 } // WebViewCoreRecordTimeCounter
662 WebCore::Node* oldFocusNode = currentFocus();
663 m_frameCacheOutOfDate = true;
664 WebCore::IntRect oldBounds;
668 oldBounds = oldFocusNode->getRect();
669 RenderObject* renderer = oldFocusNode->renderer();
670 if (renderer && (renderer->isTextArea() || renderer->isTextField())) {
671 WebCore::RenderTextControl* rtc =
672 static_cast<WebCore::RenderTextControl*>(renderer);
673 oldSelStart = rtc->selectionStart();
674 oldSelEnd = rtc->selectionEnd();
677 oldBounds = WebCore::IntRect(0,0,0,0);
678 unsigned latestVersion = 0;
679 if (m_check_domtree_version) {
680 // as domTreeVersion only increment, we can just check the sum to see
681 // whether we need to update the frame cache
682 for (Frame* frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) {
683 const Document* doc = frame->document();
684 latestVersion += doc->domTreeVersion() + doc->styleVersion();
687 DBG_NAV_LOGD("m_lastFocused=%p oldFocusNode=%p"
688 " m_lastFocusedBounds={%d,%d,%d,%d} oldBounds={%d,%d,%d,%d}"
689 " m_lastFocusedSelection={%d,%d} oldSelection={%d,%d}"
690 " m_check_domtree_version=%s latestVersion=%d m_domtree_version=%d",
691 m_lastFocused, oldFocusNode,
692 m_lastFocusedBounds.x(), m_lastFocusedBounds.y(),
693 m_lastFocusedBounds.width(), m_lastFocusedBounds.height(),
694 oldBounds.x(), oldBounds.y(), oldBounds.width(), oldBounds.height(),
695 m_lastFocusedSelStart, m_lastFocusedSelEnd, oldSelStart, oldSelEnd,
696 m_check_domtree_version ? "true" : "false",
697 latestVersion, m_domtree_version);
698 if (m_lastFocused == oldFocusNode && m_lastFocusedBounds == oldBounds
699 && m_lastFocusedSelStart == oldSelStart
700 && m_lastFocusedSelEnd == oldSelEnd
702 && (!m_check_domtree_version || latestVersion == m_domtree_version))
706 m_focusBoundsChanged |= m_lastFocused == oldFocusNode
707 && m_lastFocusedBounds != oldBounds;
708 m_lastFocused = oldFocusNode;
709 m_lastFocusedBounds = oldBounds;
710 m_lastFocusedSelStart = oldSelStart;
711 m_lastFocusedSelEnd = oldSelEnd;
712 m_domtree_version = latestVersion;
713 DBG_NAV_LOG("call updateFrameCache");
716 LOG_ASSERT(m_javaGlue->m_obj,
717 "A Java widget was not associated with this view bridge!");
718 JNIEnv* env = JSC::Bindings::getJNIEnv();
719 env->CallVoidMethod(m_javaGlue->object(env).get(),
720 m_javaGlue->m_sendFindAgain);
725 void WebViewCore::updateButtonList(WTF::Vector<Container>* buttons)
727 // All the entries in buttons are either updates of previous entries in
728 // m_buttons or they need to be added to it.
729 Container* end = buttons->end();
730 for (Container* updatedContainer = buttons->begin();
731 updatedContainer != end; updatedContainer++) {
732 bool updated = false;
733 // Search for a previous entry that references the same node as our new
735 Container* lastPossibleMatch = m_buttons.end();
736 for (Container* possibleMatch = m_buttons.begin();
737 possibleMatch != lastPossibleMatch; possibleMatch++) {
738 if (updatedContainer->matches(possibleMatch->node())) {
739 // Update our record, and skip to the next one.
740 possibleMatch->setRect(updatedContainer->rect());
746 // This is a brand new button, so append it to m_buttons
747 m_buttons.append(*updatedContainer);
751 // count will decrease each time one is removed, so check count each time.
752 while (i < m_buttons.size()) {
753 if (m_buttons[i].canBeRemoved()) {
754 m_buttons[i] = m_buttons.last();
755 m_buttons.removeLast();
762 // note: updateCursorBounds is called directly by the WebView thread
763 // This needs to be called each time we call CachedRoot::setCursor() with
764 // non-null CachedNode/CachedFrame, since otherwise the WebViewCore's data
765 // about the cursor is incorrect. When we call setCursor(0,0), we need
766 // to set hasCursorBounds to false.
767 void WebViewCore::updateCursorBounds(const CachedRoot* root,
768 const CachedFrame* cachedFrame, const CachedNode* cachedNode)
770 LOG_ASSERT(root, "updateCursorBounds: root cannot be null");
771 LOG_ASSERT(cachedNode, "updateCursorBounds: cachedNode cannot be null");
772 LOG_ASSERT(cachedFrame, "updateCursorBounds: cachedFrame cannot be null");
773 gCursorBoundsMutex.lock();
774 m_hasCursorBounds = !cachedNode->isHidden();
775 // If m_hasCursorBounds is false, we never look at the other
776 // values, so do not bother setting them.
777 if (m_hasCursorBounds) {
778 WebCore::IntRect bounds = cachedNode->bounds(cachedFrame);
779 if (m_cursorBounds != bounds)
780 DBG_NAV_LOGD("new cursor bounds=(%d,%d,w=%d,h=%d)",
781 bounds.x(), bounds.y(), bounds.width(), bounds.height());
782 m_cursorBounds = bounds;
783 m_cursorHitBounds = cachedNode->hitBounds(cachedFrame);
784 m_cursorFrame = cachedFrame->framePointer();
785 root->getSimulatedMousePosition(&m_cursorLocation);
786 m_cursorNode = cachedNode->nodePointer();
788 gCursorBoundsMutex.unlock();
791 void WebViewCore::clearContent()
795 m_addInval.setEmpty();
796 m_rebuildInval.setEmpty();
799 bool WebViewCore::focusBoundsChanged()
801 bool result = m_focusBoundsChanged;
802 m_focusBoundsChanged = false;
806 SkPicture* WebViewCore::rebuildPicture(const SkIRect& inval)
808 WebCore::FrameView* view = m_mainFrame->view();
809 int width = view->contentsWidth();
810 int height = view->contentsHeight();
811 SkPicture* picture = new SkPicture();
812 SkAutoPictureRecord arp(picture, width, height, PICT_RECORD_FLAGS);
813 SkAutoMemoryUsageProbe mup(__FUNCTION__);
814 SkCanvas* recordingCanvas = arp.getRecordingCanvas();
817 WTF::Vector<Container> buttons(m_buttons);
818 gButtonMutex.unlock();
820 WebCore::PlatformGraphicsContext pgc(recordingCanvas, &buttons);
821 WebCore::GraphicsContext gc(&pgc);
822 recordingCanvas->translate(-inval.fLeft, -inval.fTop);
823 recordingCanvas->save();
824 view->platformWidget()->draw(&gc, WebCore::IntRect(inval.fLeft,
825 inval.fTop, inval.width(), inval.height()));
826 m_rebuildInval.op(inval, SkRegion::kUnion_Op);
827 DBG_SET_LOGD("m_rebuildInval={%d,%d,r=%d,b=%d}",
828 m_rebuildInval.getBounds().fLeft, m_rebuildInval.getBounds().fTop,
829 m_rebuildInval.getBounds().fRight, m_rebuildInval.getBounds().fBottom);
832 updateButtonList(&buttons);
833 gButtonMutex.unlock();
838 void WebViewCore::rebuildPictureSet(PictureSet* pictureSet)
840 WebCore::FrameView* view = m_mainFrame->view();
841 size_t size = pictureSet->size();
842 for (size_t index = 0; index < size; index++) {
843 if (pictureSet->upToDate(index))
845 const SkIRect& inval = pictureSet->bounds(index);
846 DBG_SET_LOGD("pictSet=%p [%d] {%d,%d,w=%d,h=%d}", pictureSet, index,
847 inval.fLeft, inval.fTop, inval.width(), inval.height());
848 pictureSet->setPicture(index, rebuildPicture(inval));
850 pictureSet->validate(__FUNCTION__);
853 BaseLayerAndroid* WebViewCore::createBaseLayer()
855 BaseLayerAndroid* base = new BaseLayerAndroid();
856 base->setContent(m_content);
858 #if USE(ACCELERATED_COMPOSITING)
859 // We set the background color
860 if (m_mainFrame && m_mainFrame->document()
861 && m_mainFrame->document()->body()) {
862 Document* document = m_mainFrame->document();
863 RefPtr<RenderStyle> style = document->styleForElementIgnoringPendingStylesheets(document->body());
864 if (style->hasBackground()) {
865 Color color = style->visitedDependentColor(CSSPropertyBackgroundColor);
866 base->setBackgroundColor(color);
870 // We update the layers
871 ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
872 GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
874 root->notifyClientAnimationStarted();
875 LayerAndroid* copyLayer = new LayerAndroid(*root->contentLayer());
876 base->addChild(copyLayer);
884 BaseLayerAndroid* WebViewCore::recordContent(SkRegion* region, SkIPoint* point)
886 DBG_SET_LOG("start");
887 float progress = (float) m_mainFrame->page()->progress()->estimatedProgress();
888 m_progressDone = progress <= 0.0f || progress >= 1.0f;
889 recordPictureSet(&m_content);
890 if (!m_progressDone && m_content.isEmpty()) {
891 DBG_SET_LOGD("empty (progress=%g)", progress);
894 region->set(m_addInval);
895 m_addInval.setEmpty();
896 region->op(m_rebuildInval, SkRegion::kUnion_Op);
897 m_rebuildInval.setEmpty();
898 point->fX = m_content.width();
899 point->fY = m_content.height();
900 DBG_SET_LOGD("region={%d,%d,r=%d,b=%d}", region->getBounds().fLeft,
901 region->getBounds().fTop, region->getBounds().fRight,
902 region->getBounds().fBottom);
905 return createBaseLayer();
908 void WebViewCore::splitContent(PictureSet* content)
910 bool layoutSuceeded = layoutIfNeededRecursive(m_mainFrame);
911 LOG_ASSERT(layoutSuceeded, "Can never be called recursively");
912 content->split(&m_content);
913 rebuildPictureSet(&m_content);
914 content->set(m_content);
917 void WebViewCore::scrollTo(int x, int y, bool animate)
919 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
921 // LOGD("WebViewCore::scrollTo(%d %d)\n", x, y);
923 JNIEnv* env = JSC::Bindings::getJNIEnv();
924 env->CallVoidMethod(m_javaGlue->object(env).get(),
925 animate ? m_javaGlue->m_spawnScrollTo : m_javaGlue->m_scrollTo,
930 void WebViewCore::sendNotifyProgressFinished()
932 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
933 JNIEnv* env = JSC::Bindings::getJNIEnv();
934 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendNotifyProgressFinished);
938 void WebViewCore::viewInvalidate(const WebCore::IntRect& rect)
940 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
941 JNIEnv* env = JSC::Bindings::getJNIEnv();
942 env->CallVoidMethod(m_javaGlue->object(env).get(),
943 m_javaGlue->m_sendViewInvalidate,
944 rect.x(), rect.y(), rect.right(), rect.bottom());
948 void WebViewCore::scrollBy(int dx, int dy, bool animate)
952 JNIEnv* env = JSC::Bindings::getJNIEnv();
953 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_scrollBy,
958 void WebViewCore::contentDraw()
960 JNIEnv* env = JSC::Bindings::getJNIEnv();
961 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_contentDraw);
965 void WebViewCore::layersDraw()
967 JNIEnv* env = JSC::Bindings::getJNIEnv();
968 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_layersDraw);
972 void WebViewCore::contentInvalidate(const WebCore::IntRect &r)
974 DBG_SET_LOGD("rect={%d,%d,w=%d,h=%d}", r.x(), r.y(), r.width(), r.height());
976 if (!rect.intersect(0, 0, INT_MAX, INT_MAX))
978 m_addInval.op(rect, SkRegion::kUnion_Op);
979 DBG_SET_LOGD("m_addInval={%d,%d,r=%d,b=%d}",
980 m_addInval.getBounds().fLeft, m_addInval.getBounds().fTop,
981 m_addInval.getBounds().fRight, m_addInval.getBounds().fBottom);
982 if (!m_skipContentDraw)
986 void WebViewCore::contentInvalidateAll()
988 WebCore::FrameView* view = m_mainFrame->view();
989 contentInvalidate(WebCore::IntRect(0, 0,
990 view->contentsWidth(), view->contentsHeight()));
993 void WebViewCore::offInvalidate(const WebCore::IntRect &r)
995 // FIXME: these invalidates are offscreen, and can be throttled or
996 // deferred until the area is visible. For now, treat them as
997 // regular invals so that drawing happens (inefficiently) for now.
998 contentInvalidate(r);
1001 static int pin_pos(int x, int width, int targetWidth)
1003 if (x + width > targetWidth)
1004 x = targetWidth - width;
1010 void WebViewCore::didFirstLayout()
1012 DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1013 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1015 WebCore::FrameLoader* loader = m_mainFrame->loader();
1016 const WebCore::KURL& url = loader->url();
1019 LOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data());
1021 WebCore::FrameLoadType loadType = loader->loadType();
1023 JNIEnv* env = JSC::Bindings::getJNIEnv();
1024 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_didFirstLayout,
1025 loadType == WebCore::FrameLoadTypeStandard
1026 // When redirect with locked history, we would like to reset the
1027 // scale factor. This is important for www.yahoo.com as it is
1028 // redirected to www.yahoo.com/?rs=1 on load.
1029 || loadType == WebCore::FrameLoadTypeRedirectWithLockedBackForwardList);
1030 checkException(env);
1032 DBG_NAV_LOG("call updateFrameCache");
1033 m_check_domtree_version = false;
1035 m_history.setDidFirstLayout(true);
1038 void WebViewCore::updateViewport()
1040 DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1041 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1043 JNIEnv* env = JSC::Bindings::getJNIEnv();
1044 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateViewport);
1045 checkException(env);
1048 void WebViewCore::restoreScale(int scale, int textWrapScale)
1050 DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1051 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1053 JNIEnv* env = JSC::Bindings::getJNIEnv();
1054 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_restoreScale, scale, textWrapScale);
1055 checkException(env);
1058 void WebViewCore::needTouchEvents(bool need)
1060 DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1061 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1063 #if ENABLE(TOUCH_EVENTS)
1064 if (m_forwardingTouchEvents == need)
1067 JNIEnv* env = JSC::Bindings::getJNIEnv();
1068 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_needTouchEvents, need);
1069 checkException(env);
1071 m_forwardingTouchEvents = need;
1075 void WebViewCore::requestKeyboardWithSelection(const WebCore::Node* node,
1076 int selStart, int selEnd)
1078 DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1079 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1081 JNIEnv* env = JSC::Bindings::getJNIEnv();
1082 env->CallVoidMethod(m_javaGlue->object(env).get(),
1083 m_javaGlue->m_requestKeyboardWithSelection,
1084 reinterpret_cast<int>(node), selStart, selEnd, m_textGeneration);
1085 checkException(env);
1088 void WebViewCore::requestKeyboard(bool showKeyboard)
1090 DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1091 LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1093 JNIEnv* env = JSC::Bindings::getJNIEnv();
1094 env->CallVoidMethod(m_javaGlue->object(env).get(),
1095 m_javaGlue->m_requestKeyboard, showKeyboard);
1096 checkException(env);
1099 void WebViewCore::notifyProgressFinished()
1101 DBG_NAV_LOG("call updateFrameCache");
1102 m_check_domtree_version = true;
1104 sendNotifyProgressFinished();
1107 void WebViewCore::doMaxScroll(CacheBuilder::Direction dir)
1112 case CacheBuilder::LEFT:
1115 case CacheBuilder::UP:
1118 case CacheBuilder::RIGHT:
1121 case CacheBuilder::DOWN:
1124 case CacheBuilder::UNINITIALIZED:
1126 LOG_ASSERT(0, "unexpected focus selector");
1128 this->scrollBy(dx, dy, true);
1131 void WebViewCore::setScrollOffset(int moveGeneration, int userScrolled, int dx, int dy)
1133 DBG_NAV_LOGD("{%d,%d} m_scrollOffset=(%d,%d), userScrolled=%d", dx, dy,
1134 m_scrollOffsetX, m_scrollOffsetY, userScrolled);
1135 if (m_scrollOffsetX != dx || m_scrollOffsetY != dy) {
1136 m_scrollOffsetX = dx;
1137 m_scrollOffsetY = dy;
1138 // The visible rect is located within our coordinate space so it
1139 // contains the actual scroll position. Setting the location makes hit
1140 // testing work correctly.
1141 m_mainFrame->view()->platformWidget()->setLocation(m_scrollOffsetX,
1144 m_mainFrame->eventHandler()->sendScrollEvent();
1147 // update the currently visible screen
1148 sendPluginVisibleScreen();
1150 gCursorBoundsMutex.lock();
1151 bool hasCursorBounds = m_hasCursorBounds;
1152 Frame* frame = (Frame*) m_cursorFrame;
1153 IntPoint location = m_cursorLocation;
1154 gCursorBoundsMutex.unlock();
1155 if (!hasCursorBounds)
1157 moveMouseIfLatest(moveGeneration, frame, location.x(), location.y());
1160 void WebViewCore::setGlobalBounds(int x, int y, int h, int v)
1162 DBG_NAV_LOGD("{%d,%d}", x, y);
1163 m_mainFrame->view()->platformWidget()->setWindowBounds(x, y, h, v);
1166 void WebViewCore::setSizeScreenWidthAndScale(int width, int height,
1167 int textWrapWidth, float scale, int screenWidth, int screenHeight,
1168 int anchorX, int anchorY, bool ignoreHeight)
1170 WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
1171 int ow = window->width();
1172 int oh = window->height();
1173 window->setSize(width, height);
1174 window->setVisibleSize(screenWidth, screenHeight);
1175 if (width != screenWidth) {
1176 m_mainFrame->view()->setUseFixedLayout(true);
1177 m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height));
1179 m_mainFrame->view()->setUseFixedLayout(false);
1181 int osw = m_screenWidth;
1182 int osh = m_screenHeight;
1183 int otw = m_textWrapWidth;
1184 DBG_NAV_LOGD("old:(w=%d,h=%d,sw=%d,scale=%g) new:(w=%d,h=%d,sw=%d,scale=%g)",
1185 ow, oh, osw, m_scale, width, height, screenWidth, scale);
1186 m_screenWidth = screenWidth;
1187 m_screenHeight = screenHeight;
1188 m_textWrapWidth = textWrapWidth;
1189 if (scale >= 0) // negative means keep the current scale
1191 m_maxXScroll = screenWidth >> 2;
1192 m_maxYScroll = m_maxXScroll * height / width;
1193 if (ow != width || (!ignoreHeight && oh != height) || otw != textWrapWidth) {
1194 WebCore::RenderObject *r = m_mainFrame->contentRenderer();
1195 DBG_NAV_LOGD("renderer=%p view=(w=%d,h=%d)", r,
1196 screenWidth, screenHeight);
1198 WebCore::IntPoint anchorPoint = WebCore::IntPoint(anchorX, anchorY);
1199 DBG_NAV_LOGD("anchorX=%d anchorY=%d", anchorX, anchorY);
1200 RefPtr<WebCore::Node> node;
1201 WebCore::IntRect bounds;
1202 WebCore::IntPoint offset;
1203 // If the text wrap changed, it is probably zoom change or
1204 // orientation change. Try to keep the anchor at the same place.
1205 if (otw && textWrapWidth && otw != textWrapWidth) {
1206 WebCore::HitTestResult hitTestResult =
1207 m_mainFrame->eventHandler()-> hitTestResultAtPoint(
1208 anchorPoint, false);
1209 node = hitTestResult.innerNode();
1212 bounds = node->getRect();
1213 DBG_NAV_LOGD("ob:(x=%d,y=%d,w=%d,h=%d)",
1214 bounds.x(), bounds.y(), bounds.width(), bounds.height());
1215 // sites like nytimes.com insert a non-standard tag <nyt_text>
1216 // in the html. If it is the HitTestResult, it may have zero
1217 // width and height. In this case, use its parent node.
1218 if (bounds.width() == 0) {
1219 node = node->parent();
1221 bounds = node->getRect();
1222 DBG_NAV_LOGD("found a zero width node and use its parent, whose ob:(x=%d,y=%d,w=%d,h=%d)",
1223 bounds.x(), bounds.y(), bounds.width(), bounds.height());
1227 r->setNeedsLayoutAndPrefWidthsRecalc();
1228 m_mainFrame->view()->forceLayout();
1229 // scroll to restore current screen center
1231 const WebCore::IntRect& newBounds = node->getRect();
1232 DBG_NAV_LOGD("nb:(x=%d,y=%d,w=%d,"
1233 "h=%d)", newBounds.x(), newBounds.y(),
1234 newBounds.width(), newBounds.height());
1235 if ((osw && osh && bounds.width() && bounds.height())
1236 && (bounds != newBounds)) {
1237 WebCore::FrameView* view = m_mainFrame->view();
1238 // force left align if width is not changed while height changed.
1239 // the anchorPoint is probably at some white space in the node
1240 // which is affected by text wrap around the screen width.
1241 const bool leftAlign = (otw != textWrapWidth)
1242 && (bounds.width() == newBounds.width())
1243 && (bounds.height() != newBounds.height());
1244 const float xPercentInDoc =
1245 leftAlign ? 0.0 : (float) (anchorX - bounds.x()) / bounds.width();
1246 const float xPercentInView =
1247 leftAlign ? 0.0 : (float) (anchorX - m_scrollOffsetX) / osw;
1248 const float yPercentInDoc = (float) (anchorY - bounds.y()) / bounds.height();
1249 const float yPercentInView = (float) (anchorY - m_scrollOffsetY) / osh;
1250 showRect(newBounds.x(), newBounds.y(), newBounds.width(),
1251 newBounds.height(), view->contentsWidth(),
1252 view->contentsHeight(),
1253 xPercentInDoc, xPercentInView,
1254 yPercentInDoc, yPercentInView);
1260 // update the currently visible screen as perceived by the plugin
1261 sendPluginVisibleScreen();
1264 void WebViewCore::dumpDomTree(bool useFile)
1266 #ifdef ANDROID_DOM_LOGGING
1268 gDomTreeFile = fopen(DOM_TREE_LOG_FILE, "w");
1269 m_mainFrame->document()->showTreeForThis();
1271 fclose(gDomTreeFile);
1277 void WebViewCore::dumpRenderTree(bool useFile)
1279 #ifdef ANDROID_DOM_LOGGING
1280 WTF::CString renderDump = WebCore::externalRepresentation(m_mainFrame).utf8();
1281 const char* data = renderDump.data();
1283 gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w");
1284 DUMP_RENDER_LOGD("%s", data);
1285 fclose(gRenderTreeFile);
1286 gRenderTreeFile = 0;
1288 // adb log can only output 1024 characters, so write out line by line.
1289 // exclude '\n' as adb log adds it for each output.
1290 int length = renderDump.length();
1291 for (int i = 0, last = 0; i < length; i++) {
1292 if (data[i] == '\n') {
1294 DUMP_RENDER_LOGD("%.*s", (i - last), &(data[last]));
1302 void WebViewCore::dumpNavTree()
1305 cacheBuilder().mDebug.print();
1309 WebCore::HTMLAnchorElement* WebViewCore::retrieveAnchorElement(WebCore::Frame* frame, WebCore::Node* node)
1311 if (!CacheBuilder::validNode(m_mainFrame, frame, node))
1313 if (!node->hasTagName(WebCore::HTMLNames::aTag))
1315 return static_cast<WebCore::HTMLAnchorElement*>(node);
1318 WTF::String WebViewCore::retrieveHref(WebCore::Frame* frame, WebCore::Node* node)
1320 WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(frame, node);
1321 return anchor ? anchor->href() : WTF::String();
1324 WTF::String WebViewCore::retrieveAnchorText(WebCore::Frame* frame, WebCore::Node* node)
1326 WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(frame, node);
1327 return anchor ? anchor->text() : WTF::String();
1330 WTF::String WebViewCore::requestLabel(WebCore::Frame* frame,
1331 WebCore::Node* node)
1333 if (node && CacheBuilder::validNode(m_mainFrame, frame, node)) {
1334 RefPtr<WebCore::NodeList> list = node->document()->getElementsByTagName("label");
1335 unsigned length = list->length();
1336 for (unsigned i = 0; i < length; i++) {
1337 WebCore::HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>(
1339 if (label->control() == node) {
1342 while ((node = node->traverseNextNode(label))) {
1343 if (node->isTextNode()) {
1344 Text* textNode = static_cast<Text*>(node);
1345 result += textNode->dataImpl();
1352 return WTF::String();
1355 static bool isContentEditable(WebCore::Node* node)
1357 if (!node) return false;
1358 return node->document()->frame()->selection()->isContentEditable();
1361 void WebViewCore::revealSelection()
1363 WebCore::Node* focus = currentFocus();
1366 WebCore::RenderObject* renderer = focus->renderer();
1367 if ((!renderer || (!renderer->isTextField() && !renderer->isTextArea()))
1368 && !isContentEditable(focus))
1370 WebCore::Frame* focusedFrame = focus->document()->frame();
1371 WebFrame* webFrame = WebFrame::getWebFrame(focusedFrame);
1372 webFrame->setUserInitiatedAction(true);
1373 focusedFrame->selection()->revealSelection();
1374 webFrame->setUserInitiatedAction(false);
1377 void WebViewCore::updateCacheOnNodeChange()
1379 gCursorBoundsMutex.lock();
1380 bool hasCursorBounds = m_hasCursorBounds;
1381 Frame* frame = (Frame*) m_cursorFrame;
1382 Node* node = (Node*) m_cursorNode;
1383 IntRect bounds = m_cursorHitBounds;
1384 gCursorBoundsMutex.unlock();
1385 if (!hasCursorBounds || !node)
1387 if (CacheBuilder::validNode(m_mainFrame, frame, node)) {
1388 RenderObject* renderer = node->renderer();
1389 if (renderer && renderer->style()->visibility() != HIDDEN) {
1390 IntRect absBox = renderer->absoluteBoundingBoxRect();
1391 int globalX, globalY;
1392 CacheBuilder::GetGlobalOffset(frame, &globalX, &globalY);
1393 absBox.move(globalX, globalY);
1394 if (absBox == bounds)
1396 DBG_NAV_LOGD("absBox=(%d,%d,%d,%d) bounds=(%d,%d,%d,%d)",
1397 absBox.x(), absBox.y(), absBox.width(), absBox.height(),
1398 bounds.x(), bounds.y(), bounds.width(), bounds.height());
1401 DBG_NAV_LOGD("updateFrameCache node=%p", node);
1405 void WebViewCore::updateFrameCache()
1407 if (!m_frameCacheOutOfDate) {
1408 DBG_NAV_LOG("!m_frameCacheOutOfDate");
1411 #ifdef ANDROID_INSTRUMENT
1412 TimeCounterAuto counter(TimeCounter::WebViewCoreBuildNavTimeCounter);
1414 m_frameCacheOutOfDate = false;
1416 m_now = SkTime::GetMSecs();
1418 m_temp = new CachedRoot();
1419 m_temp->init(m_mainFrame, &m_history);
1420 #if USE(ACCELERATED_COMPOSITING)
1421 GraphicsLayerAndroid* graphicsLayer = graphicsRootLayer();
1423 m_temp->setRootLayer(graphicsLayer->contentLayer());
1425 CacheBuilder& builder = cacheBuilder();
1426 WebCore::Settings* settings = m_mainFrame->page()->settings();
1427 builder.allowAllTextDetection();
1428 #ifdef ANDROID_META_SUPPORT
1430 if (!settings->formatDetectionAddress())
1431 builder.disallowAddressDetection();
1432 if (!settings->formatDetectionEmail())
1433 builder.disallowEmailDetection();
1434 if (!settings->formatDetectionTelephone())
1435 builder.disallowPhoneDetection();
1438 builder.buildCache(m_temp);
1439 m_tempPict = new SkPicture();
1440 recordPicture(m_tempPict);
1441 m_temp->setPicture(m_tempPict);
1442 m_temp->setTextGeneration(m_textGeneration);
1443 WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
1444 m_temp->setVisibleRect(WebCore::IntRect(m_scrollOffsetX,
1445 m_scrollOffsetY, window->width(), window->height()));
1446 gFrameCacheMutex.lock();
1447 delete m_frameCacheKit;
1448 delete m_navPictureKit;
1449 m_frameCacheKit = m_temp;
1450 m_navPictureKit = m_tempPict;
1451 m_updatedFrameCache = true;
1453 const CachedNode* cachedFocusNode = m_frameCacheKit->currentFocus();
1454 DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p)",
1455 cachedFocusNode ? cachedFocusNode->index() : 0,
1456 cachedFocusNode ? cachedFocusNode->nodePointer() : 0);
1458 gFrameCacheMutex.unlock();
1461 void WebViewCore::updateFrameCacheIfLoading()
1463 if (!m_check_domtree_version)
1467 struct TouchNodeData {
1472 // get the bounding box of the Node
1473 static IntRect getAbsoluteBoundingBox(Node* node) {
1475 RenderObject* render = node->renderer();
1476 if (render->isRenderInline())
1477 rect = toRenderInline(render)->linesVisibleOverflowBoundingBox();
1478 else if (render->isBox())
1479 rect = toRenderBox(render)->visualOverflowRect();
1480 else if (render->isText())
1481 rect = toRenderText(render)->linesBoundingBox();
1483 LOGE("getAbsoluteBoundingBox failed for node %p, name %s", node, render->renderName());
1484 FloatPoint absPos = render->localToAbsolute();
1485 rect.move(absPos.x(), absPos.y());
1489 // get the highlight rectangles for the touch point (x, y) with the slop
1490 Vector<IntRect> WebViewCore::getTouchHighlightRects(int x, int y, int slop)
1492 Vector<IntRect> rects;
1493 m_mousePos = IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY);
1494 HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y),
1495 false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(slop, slop));
1496 if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) {
1497 LOGE("Should not happen: no in document Node found");
1500 const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult();
1501 if (list.isEmpty()) {
1502 LOGE("Should not happen: no rect-based-test nodes found");
1505 Frame* frame = hitTestResult.innerNode()->document()->frame();
1506 Vector<TouchNodeData> nodeDataList;
1507 ListHashSet<RefPtr<Node> >::const_iterator last = list.end();
1508 for (ListHashSet<RefPtr<Node> >::const_iterator it = list.begin(); it != last; ++it) {
1509 // TODO: it seems reasonable to not search across the frame. Isn't it?
1510 // if the node is not in the same frame as the innerNode, skip it
1511 if (it->get()->document()->frame() != frame)
1513 // traverse up the tree to find the first node that needs highlight
1515 Node* eventNode = it->get();
1517 RenderObject* render = eventNode->renderer();
1518 if (render->isBody() || render->isRenderView())
1520 if (eventNode->supportsFocus()
1521 || eventNode->hasEventListeners(eventNames().clickEvent)
1522 || eventNode->hasEventListeners(eventNames().mousedownEvent)
1523 || eventNode->hasEventListeners(eventNames().mouseupEvent)) {
1527 // the nodes in the rectBasedTestResult() are ordered based on z-index during hit testing.
1528 // so do not search for the eventNode across explicit z-index border.
1529 // TODO: this is a hard one to call. z-index is quite complicated as its value only
1530 // matters when you compare two RenderLayer in the same hierarchy level. e.g. in
1531 // the following example, "b" is on the top as its z level is the highest. even "c"
1532 // has 100 as z-index, it is still below "d" as its parent has the same z-index as
1533 // "d" and logically before "d". Of course "a" is the lowest in the z level.
1541 // If the fat point touches everyone, the order in the list should be "b", "d", "c"
1542 // and "a". When we search for the event node for "b", we really don't want "a" as
1543 // in the z-order it is behind everything else.
1544 if (!render->style()->hasAutoZIndex())
1546 eventNode = eventNode->parentNode();
1548 // didn't find any eventNode, skip it
1551 // first quick check whether it is a duplicated node before computing bounding box
1552 Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end();
1553 for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) {
1554 // found the same node, skip it
1555 if (eventNode == n->mNode) {
1562 // next check whether the node is fully covered by or fully covering another node.
1564 IntRect rect = getAbsoluteBoundingBox(eventNode);
1565 if (rect.isEmpty()) {
1566 // if the node's bounds is empty and it is not a ContainerNode, skip it.
1567 if (!eventNode->isContainerNode())
1569 // if the node's children are all positioned objects, its bounds can be empty.
1570 // Walk through the children to find the bounding box.
1571 Node* child = static_cast<const ContainerNode*>(eventNode)->firstChild();
1574 if (child->renderer())
1575 childrect = getAbsoluteBoundingBox(child);
1576 if (!childrect.isEmpty()) {
1577 rect.unite(childrect);
1578 child = child->traverseNextSibling(eventNode);
1580 child = child->traverseNextNode(eventNode);
1583 for (int i = nodeDataList.size() - 1; i >= 0; i--) {
1584 TouchNodeData n = nodeDataList.at(i);
1585 // the new node is enclosing an existing node, skip it
1586 if (rect.contains(n.mBounds)) {
1590 // the new node is fully inside an existing node, remove the existing node
1591 if (n.mBounds.contains(rect))
1592 nodeDataList.remove(i);
1595 TouchNodeData newNode;
1596 newNode.mNode = eventNode;
1597 newNode.mBounds = rect;
1598 nodeDataList.append(newNode);
1601 if (!nodeDataList.size())
1603 // finally select the node with the largest overlap with the fat point
1604 TouchNodeData final;
1606 IntPoint docPos = frame->view()->windowToContents(m_mousePos);
1607 IntRect testRect(docPos.x() - slop, docPos.y() - slop, 2 * slop + 1, 2 * slop + 1);
1609 Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end();
1610 for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) {
1611 IntRect rect = n->mBounds;
1612 rect.intersect(testRect);
1613 int a = rect.width() * rect.height();
1619 // now get the node's highlight rectangles in the page coordinate system
1621 IntPoint frameAdjust;
1622 if (frame != m_mainFrame) {
1623 frameAdjust = frame->view()->contentsToWindow(IntPoint());
1624 frameAdjust.move(m_scrollOffsetX, m_scrollOffsetY);
1626 if (final.mNode->isLink()) {
1627 // most of the links are inline instead of box style. So the bounding box is not
1628 // a good representation for the highlights. Get the list of rectangles instead.
1629 RenderObject* render = final.mNode->renderer();
1630 IntPoint offset = roundedIntPoint(render->localToAbsolute());
1631 render->absoluteRects(rects, offset.x() + frameAdjust.x(), offset.y() + frameAdjust.y());
1632 bool inside = false;
1633 int distance = INT_MAX;
1634 int newx = x, newy = y;
1635 int i = rects.size();
1637 if (rects[i].isEmpty()) {
1641 // check whether the point (x, y) is inside one of the rectangles.
1644 if (rects[i].contains(x, y)) {
1648 if (x >= rects[i].x() && x < rects[i].right()) {
1649 if (y < rects[i].y()) {
1650 if (rects[i].y() - y < distance) {
1652 newy = rects[i].y();
1653 distance = rects[i].y() - y;
1655 } else if (y >= rects[i].bottom()) {
1656 if (y - rects[i].bottom() + 1 < distance) {
1658 newy = rects[i].bottom() - 1;
1659 distance = y - rects[i].bottom() + 1;
1662 } else if (y >= rects[i].y() && y < rects[i].bottom()) {
1663 if (x < rects[i].x()) {
1664 if (rects[i].x() - x < distance) {
1665 newx = rects[i].x();
1667 distance = rects[i].x() - x;
1669 } else if (x >= rects[i].right()) {
1670 if (x - rects[i].right() + 1 < distance) {
1671 newx = rects[i].right() - 1;
1673 distance = x - rects[i].right() + 1;
1678 if (!rects.isEmpty()) {
1680 // if neither x nor y has overlap, just pick the top/left of the first rectangle
1681 if (newx == x && newy == y) {
1682 newx = rects[0].x();
1683 newy = rects[0].y();
1685 m_mousePos.setX(newx - m_scrollOffsetX);
1686 m_mousePos.setY(newy - m_scrollOffsetY);
1687 DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)",
1688 x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY,
1689 m_scrollOffsetX, m_scrollOffsetY);
1694 IntRect rect = final.mBounds;
1695 rect.move(frameAdjust.x(), frameAdjust.y());
1697 // adjust m_mousePos if it is not inside the returned highlight rectangle
1698 testRect.move(frameAdjust.x(), frameAdjust.y());
1699 testRect.intersect(rect);
1700 if (!testRect.contains(x, y)) {
1701 m_mousePos = testRect.center();
1702 m_mousePos.move(-m_scrollOffsetX, -m_scrollOffsetY);
1703 DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)",
1704 x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY,
1705 m_scrollOffsetX, m_scrollOffsetY);
1711 ///////////////////////////////////////////////////////////////////////////////
1713 void WebViewCore::addPlugin(PluginWidgetAndroid* w)
1715 // SkDebugf("----------- addPlugin %p", w);
1716 /* The plugin must be appended to the end of the array. This ensures that if
1717 the plugin is added while iterating through the array (e.g. sendEvent(...))
1718 that the iteration process is not corrupted.
1720 *m_plugins.append() = w;
1723 void WebViewCore::removePlugin(PluginWidgetAndroid* w)
1725 // SkDebugf("----------- removePlugin %p", w);
1726 int index = m_plugins.find(w);
1728 SkDebugf("--------------- pluginwindow not found! %p\n", w);
1730 m_plugins.removeShuffle(index);
1734 bool WebViewCore::isPlugin(PluginWidgetAndroid* w) const
1736 return m_plugins.find(w) >= 0;
1739 void WebViewCore::invalPlugin(PluginWidgetAndroid* w)
1741 const double PLUGIN_INVAL_DELAY = 1.0 / 60;
1743 if (!m_pluginInvalTimer.isActive()) {
1744 m_pluginInvalTimer.startOneShot(PLUGIN_INVAL_DELAY);
1748 void WebViewCore::drawPlugins()
1750 SkRegion inval; // accumualte what needs to be redrawn
1751 PluginWidgetAndroid** iter = m_plugins.begin();
1752 PluginWidgetAndroid** stop = m_plugins.end();
1754 for (; iter < stop; ++iter) {
1755 PluginWidgetAndroid* w = *iter;
1757 if (w->isDirty(&dirty)) {
1759 inval.op(dirty, SkRegion::kUnion_Op);
1763 if (!inval.isEmpty()) {
1764 // inval.getBounds() is our rectangle
1765 const SkIRect& bounds = inval.getBounds();
1766 WebCore::IntRect r(bounds.fLeft, bounds.fTop,
1767 bounds.width(), bounds.height());
1768 this->viewInvalidate(r);
1772 void WebViewCore::notifyPluginsOnFrameLoad(const Frame* frame) {
1773 // if frame is the parent then notify all plugins
1774 if (!frame->tree()->parent()) {
1775 // trigger an event notifying the plugins that the page has loaded
1777 SkANP::InitEvent(&event, kLifecycle_ANPEventType);
1778 event.data.lifecycle.action = kOnLoad_ANPLifecycleAction;
1779 sendPluginEvent(event);
1781 // else if frame's parent has completed
1782 else if (!frame->tree()->parent()->loader()->isLoading()) {
1783 // send to all plugins who have this frame in their heirarchy
1784 PluginWidgetAndroid** iter = m_plugins.begin();
1785 PluginWidgetAndroid** stop = m_plugins.end();
1786 for (; iter < stop; ++iter) {
1787 Frame* currentFrame = (*iter)->pluginView()->parentFrame();
1788 while (currentFrame) {
1789 if (frame == currentFrame) {
1791 SkANP::InitEvent(&event, kLifecycle_ANPEventType);
1792 event.data.lifecycle.action = kOnLoad_ANPLifecycleAction;
1793 (*iter)->sendEvent(event);
1796 currentFrame = currentFrame->tree()->parent();
1802 void WebViewCore::sendPluginVisibleScreen()
1804 /* We may want to cache the previous values and only send the notification
1805 to the plugin in the event that one of the values has changed.
1808 ANPRectI visibleRect;
1809 visibleRect.left = m_scrollOffsetX;
1810 visibleRect.top = m_scrollOffsetY;
1811 visibleRect.right = m_scrollOffsetX + m_screenWidth;
1812 visibleRect.bottom = m_scrollOffsetY + m_screenHeight;
1814 PluginWidgetAndroid** iter = m_plugins.begin();
1815 PluginWidgetAndroid** stop = m_plugins.end();
1816 for (; iter < stop; ++iter) {
1817 (*iter)->setVisibleScreen(visibleRect, m_scale);
1821 void WebViewCore::sendPluginEvent(const ANPEvent& evt)
1823 /* The list of plugins may be manipulated as we iterate through the list.
1824 This implementation allows for the addition of new plugins during an
1825 iteration, but may fail if a plugin is removed. Currently, there are not
1826 any use cases where a plugin is deleted while processing this loop, but
1827 if it does occur we will have to use an alternate data structure and/or
1828 iteration mechanism.
1830 for (int x = 0; x < m_plugins.count(); x++) {
1831 m_plugins[x]->sendEvent(evt);
1835 PluginWidgetAndroid* WebViewCore::getPluginWidget(NPP npp)
1837 PluginWidgetAndroid** iter = m_plugins.begin();
1838 PluginWidgetAndroid** stop = m_plugins.end();
1839 for (; iter < stop; ++iter) {
1840 if ((*iter)->pluginView()->instance() == npp) {
1847 static PluginView* nodeIsPlugin(Node* node) {
1848 RenderObject* renderer = node->renderer();
1849 if (renderer && renderer->isWidget()) {
1850 Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
1851 if (widget && widget->isPluginView())
1852 return static_cast<PluginView*>(widget);
1857 Node* WebViewCore::cursorNodeIsPlugin() {
1858 gCursorBoundsMutex.lock();
1859 bool hasCursorBounds = m_hasCursorBounds;
1860 Frame* frame = (Frame*) m_cursorFrame;
1861 Node* node = (Node*) m_cursorNode;
1862 gCursorBoundsMutex.unlock();
1863 if (hasCursorBounds && CacheBuilder::validNode(m_mainFrame, frame, node)
1864 && nodeIsPlugin(node)) {
1870 ///////////////////////////////////////////////////////////////////////////////
1871 void WebViewCore::moveMouseIfLatest(int moveGeneration,
1872 WebCore::Frame* frame, int x, int y)
1874 DBG_NAV_LOGD("m_moveGeneration=%d moveGeneration=%d"
1875 " frame=%p x=%d y=%d",
1876 m_moveGeneration, moveGeneration, frame, x, y);
1877 if (m_moveGeneration > moveGeneration) {
1878 DBG_NAV_LOGD("m_moveGeneration=%d > moveGeneration=%d",
1879 m_moveGeneration, moveGeneration);
1880 return; // short-circuit if a newer move has already been generated
1882 m_lastGeneration = moveGeneration;
1883 moveMouse(frame, x, y);
1886 void WebViewCore::moveFocus(WebCore::Frame* frame, WebCore::Node* node)
1888 DBG_NAV_LOGD("frame=%p node=%p", frame, node);
1889 if (!node || !CacheBuilder::validNode(m_mainFrame, frame, node)
1890 || !node->isElementNode())
1892 // Code borrowed from FocusController::advanceFocus
1893 WebCore::FocusController* focusController
1894 = m_mainFrame->page()->focusController();
1895 WebCore::Document* oldDoc
1896 = focusController->focusedOrMainFrame()->document();
1897 if (oldDoc->focusedNode() == node)
1899 if (node->document() != oldDoc)
1900 oldDoc->setFocusedNode(0);
1901 focusController->setFocusedFrame(frame);
1902 static_cast<WebCore::Element*>(node)->focus(false);
1905 // Update mouse position
1906 void WebViewCore::moveMouse(WebCore::Frame* frame, int x, int y)
1908 DBG_NAV_LOGD("frame=%p x=%d y=%d scrollOffset=(%d,%d)", frame,
1909 x, y, m_scrollOffsetX, m_scrollOffsetY);
1910 if (!frame || CacheBuilder::validNode(m_mainFrame, frame, NULL) == false)
1911 frame = m_mainFrame;
1912 // mouse event expects the position in the window coordinate
1913 m_mousePos = WebCore::IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY);
1914 // validNode will still return true if the node is null, as long as we have
1915 // a valid frame. Do not want to make a call on frame unless it is valid.
1916 WebCore::PlatformMouseEvent mouseEvent(m_mousePos, m_mousePos,
1917 WebCore::NoButton, WebCore::MouseEventMoved, 1, false, false, false,
1918 false, WTF::currentTime());
1919 frame->eventHandler()->handleMouseMoveEvent(mouseEvent);
1920 updateCacheOnNodeChange();
1923 void WebViewCore::setSelection(int start, int end)
1925 WebCore::Node* focus = currentFocus();
1928 WebCore::RenderObject* renderer = focus->renderer();
1929 if (!renderer || (!renderer->isTextField() && !renderer->isTextArea()))
1936 // Tell our EditorClient that this change was generated from the UI, so it
1937 // does not need to echo it to the UI.
1938 EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
1939 m_mainFrame->editor()->client());
1940 client->setUiGeneratedSelectionChange(true);
1941 setSelectionRange(focus, start, end);
1942 client->setUiGeneratedSelectionChange(false);
1943 WebCore::Frame* focusedFrame = focus->document()->frame();
1944 bool isPasswordField = false;
1945 if (focus->isElementNode()) {
1946 WebCore::Element* element = static_cast<WebCore::Element*>(focus);
1947 if (WebCore::InputElement* inputElement = WebCore::toInputElement(element))
1948 isPasswordField = static_cast<WebCore::HTMLInputElement*>(inputElement)->isPasswordField();
1950 // For password fields, this is done in the UI side via
1951 // bringPointIntoView, since the UI does the drawing.
1952 if (renderer->isTextArea() || !isPasswordField)
1953 focusedFrame->selection()->revealSelection();
1956 String WebViewCore::modifySelection(const int direction, const int axis)
1958 DOMSelection* selection = m_mainFrame->domWindow()->getSelection();
1959 if (selection->rangeCount() > 1)
1960 selection->removeAllRanges();
1962 case AXIS_CHARACTER:
1965 return modifySelectionTextNavigationAxis(selection, direction, axis);
1968 case AXIS_PARENT_FIRST_CHILD:
1970 return modifySelectionDomNavigationAxis(selection, direction, axis);
1972 LOGE("Invalid navigation axis: %d", axis);
1977 String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int axis)
1979 String directionString;
1980 if (direction == DIRECTION_FORWARD)
1981 directionString = "forward";
1982 else if (direction == DIRECTION_BACKWARD)
1983 directionString = "backward";
1985 LOGE("Invalid direction: %d", direction);
1989 if (axis == AXIS_CHARACTER)
1990 axisString = "character";
1991 else if (axis == AXIS_WORD)
1992 axisString = "word";
1993 else // axis == AXIS_SENTENCE
1994 axisString = "sentence";
1996 // TODO: Add support of IFrames.
1997 HTMLElement* body = m_mainFrame->document()->body();
1999 Node* focusNode = 0;
2000 if (m_currentNodeDomNavigationAxis
2001 && CacheBuilder::validNode(m_mainFrame, m_mainFrame, m_currentNodeDomNavigationAxis)) {
2002 focusNode = m_currentNodeDomNavigationAxis;
2003 m_currentNodeDomNavigationAxis = 0;
2005 focusNode = (direction == DIRECTION_FORWARD) ?
2006 focusNode->traverseNextNode(body) :
2007 focusNode->traversePreviousNode(body);
2008 } while (focusNode && focusNode->isTextNode());
2010 focusNode = (selection->focusNode()) ? selection->focusNode() : currentFocus();
2012 Text* currentNode = 0;
2014 // we have no selection so start from the body or its recursively last child
2015 focusNode = (direction == DIRECTION_FORWARD) ? body : body->lastDescendant();
2016 if (focusNode->isTextNode())
2017 currentNode = static_cast<Text*>(focusNode);
2019 currentNode = traverseNonEmptyNonWhitespaceTextNode(focusNode, body, direction);
2020 if (!setSelection(selection, currentNode, direction))
2022 } else if (focusNode->isElementNode()) {
2023 // find a non-empty text node in the current direction
2024 currentNode = traverseNonEmptyNonWhitespaceTextNode(focusNode, body, direction);
2025 if (!setSelection(selection, currentNode, direction))
2028 currentNode = static_cast<Text*>(focusNode);
2029 if (direction == DIRECTION_FORWARD) {
2030 // if end of non-whitespace text go to the next non-empty text node
2031 int higherIndex = (selection->focusOffset() > selection->anchorOffset()) ?
2032 selection->focusOffset() : selection->anchorOffset();
2033 String suffix = currentNode->data().substring(higherIndex, currentNode->length());
2034 if (suffix.isEmpty() || suffix.stripWhiteSpace().isEmpty()) {
2035 currentNode = traverseNonEmptyNonWhitespaceTextNode(currentNode, body, direction);
2036 if (!setSelection(selection, currentNode, direction))
2039 ExceptionCode ec = 0;
2040 selection->collapseToEnd(ec);
2042 LOGE("Error while collapsing selection. Error code: %d", ec);
2045 // if beginning of non-whitespace text go to the previous non-empty text node
2046 int lowerIndex = (selection->focusOffset() > selection->anchorOffset()) ?
2047 selection->anchorOffset() : selection->focusOffset();
2048 String prefix = currentNode->data().substring(0, lowerIndex);
2049 if (prefix.isEmpty() || prefix.stripWhiteSpace().isEmpty()) {
2050 currentNode = traverseNonEmptyNonWhitespaceTextNode(currentNode, body, direction);
2051 if (!setSelection(selection, currentNode, direction))
2054 ExceptionCode ec = 0;
2055 selection->collapseToStart(ec);
2057 LOGE("Error while collapsing selection. Error code: %d", ec);
2062 // extend the selection - loop as an insurance it does not get stuck
2063 currentNode = static_cast<Text*>(selection->focusNode());
2064 int focusOffset = selection->focusOffset();
2066 selection->modify("extend", directionString, axisString);
2067 if (selection->focusNode() != currentNode || selection->focusOffset() != focusOffset)
2069 currentNode = traverseNonEmptyNonWhitespaceTextNode(currentNode, body, direction);
2070 focusOffset = (direction == DIRECTION_FORWARD) ? 0 : currentNode->data().length();
2071 // setSelection returns false if currentNode is 0 => the loop always terminates
2072 if (!setSelection(selection, currentNode, direction))
2076 if (direction == DIRECTION_FORWARD) {
2077 // enforce the anchor node is a text node
2078 if (selection->anchorNode()->isElementNode()) {
2079 if (!setSelection(selection, selection->focusNode(), selection->focusNode(), 0,
2080 selection->focusOffset()))
2083 // enforce the focus node is a text node
2084 if (selection->focusNode()->isElementNode()) {
2085 int endOffset = static_cast<Text*>(selection->anchorNode())->length(); // cast is safe
2086 if (!setSelection(selection, selection->anchorNode(), selection->anchorNode(),
2087 selection->anchorOffset(), endOffset))
2091 // enforce the focus node is a text node
2092 if (selection->focusNode()->isElementNode()) {
2093 if (!setSelection(selection, selection->anchorNode(), selection->anchorNode(), 0,
2094 selection->anchorOffset()))
2097 // enforce the anchor node is a text node
2098 if (selection->anchorNode()->isElementNode()) {
2099 int endOffset = static_cast<Text*>(selection->focusNode())->length(); // cast is safe
2100 if (!setSelection(selection, selection->focusNode(), selection->focusNode(),
2101 selection->focusOffset(), endOffset))
2106 tryFocusInlineSelectionElement(selection);
2107 // TODO (svetoslavganov): Draw the selected text in the WebView - a-la-Android
2108 String markup = formatMarkup(selection).stripWhiteSpace();
2109 LOGD("Selection markup: %s", markup.utf8().data());
2113 bool WebViewCore::setSelection(DOMSelection* selection, Text* textNode, int direction)
2117 int offset = (direction == DIRECTION_FORWARD) ? 0 : textNode->length();
2118 if (!setSelection(selection, textNode, textNode, offset, offset))
2123 bool WebViewCore::setSelection(DOMSelection* selection, Node* startNode, Node* endNode, int startOffset, int endOffset)
2125 if (!selection || (!startNode && !endNode))
2127 ExceptionCode ec = 0;
2128 PassRefPtr<Range> rangeRef = selection->getRangeAt(0, ec);
2131 rangeRef = m_mainFrame->document()->createRange();
2134 rangeRef->setStart(PassRefPtr<Node>(startNode), startOffset, ec);
2138 rangeRef->setEnd(PassRefPtr<Node>(endNode), endOffset, ec);
2141 selection->removeAllRanges();
2142 selection->addRange(rangeRef.get());
2146 Text* WebViewCore::traverseNonEmptyNonWhitespaceTextNode(Node* fromNode, Node* toNode, int direction)
2148 Node* currentNode = fromNode;
2150 if (direction == DIRECTION_FORWARD)
2151 currentNode = currentNode->traverseNextNode(toNode);
2153 currentNode = currentNode->traversePreviousNode(toNode);
2154 } while (currentNode && (!currentNode->isTextNode()
2155 || isEmptyOrOnlyWhitespaceTextNode(currentNode)));
2156 return static_cast<Text*>(currentNode);
2159 bool WebViewCore::isEmptyOrOnlyWhitespaceTextNode(Node* node)
2161 return (node->isTextNode()
2162 && (static_cast<Text*>(node)->length() == 0
2163 || static_cast<Text*>(node)->containsOnlyWhitespace()));
2166 String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int axis)
2168 // TODO: Add support of IFrames.
2169 HTMLElement* body = m_mainFrame->document()->body();
2170 if (!m_currentNodeDomNavigationAxis && selection->focusNode()) {
2171 m_currentNodeDomNavigationAxis = selection->focusNode();
2173 if (m_currentNodeDomNavigationAxis->isTextNode())
2174 m_currentNodeDomNavigationAxis = m_currentNodeDomNavigationAxis->parentNode();
2176 if (!m_currentNodeDomNavigationAxis)
2177 m_currentNodeDomNavigationAxis = currentFocus();
2178 if (!m_currentNodeDomNavigationAxis
2179 || !CacheBuilder::validNode(m_mainFrame, m_mainFrame, m_currentNodeDomNavigationAxis))
2180 m_currentNodeDomNavigationAxis = body;
2181 Node* currentNode = m_currentNodeDomNavigationAxis;
2182 if (axis == AXIS_HEADING) {
2183 if (currentNode == body && direction == DIRECTION_BACKWARD)
2184 currentNode = currentNode->lastDescendant();
2186 if (direction == DIRECTION_FORWARD)
2187 currentNode = currentNode->traverseNextNode(body);
2189 currentNode = currentNode->traversePreviousNode(body);
2190 } while (currentNode && (currentNode->isTextNode() || !isVisible(currentNode)
2191 || !isHeading(currentNode)));
2192 } else if (axis == AXIS_PARENT_FIRST_CHILD) {
2193 if (direction == DIRECTION_FORWARD) {
2194 currentNode = currentNode->firstChild();
2195 while (currentNode && (currentNode->isTextNode() || !isVisible(currentNode)))
2196 currentNode = currentNode->nextSibling();
2199 if (currentNode == body)
2201 currentNode = currentNode->parentNode();
2202 } while (currentNode && (currentNode->isTextNode() || !isVisible(currentNode)));
2204 } else if (axis == AXIS_SIBLING) {
2206 if (direction == DIRECTION_FORWARD)
2207 currentNode = currentNode->nextSibling();
2209 if (currentNode == body)
2211 currentNode = currentNode->previousSibling();
2213 } while (currentNode && (currentNode->isTextNode() || !isVisible(currentNode)));
2214 } else if (axis == AXIS_DOCUMENT) {
2216 if (direction == DIRECTION_FORWARD)
2217 currentNode = currentNode->lastDescendant();
2219 LOGE("Invalid axis: %d", axis);
2223 m_currentNodeDomNavigationAxis = currentNode;
2224 focusIfFocusableAndNotTextInput(selection, currentNode);
2225 // TODO (svetoslavganov): Draw the selected text in the WebView - a-la-Android
2226 String selectionString = createMarkup(currentNode);
2227 LOGD("Selection markup: %s", selectionString.utf8().data());
2228 return selectionString;
2233 bool WebViewCore::isHeading(Node* node)
2235 if (node->hasTagName(WebCore::HTMLNames::h1Tag)
2236 || node->hasTagName(WebCore::HTMLNames::h2Tag)
2237 || node->hasTagName(WebCore::HTMLNames::h3Tag)
2238 || node->hasTagName(WebCore::HTMLNames::h4Tag)
2239 || node->hasTagName(WebCore::HTMLNames::h5Tag)
2240 || node->hasTagName(WebCore::HTMLNames::h6Tag)) {
2244 if (node->isElementNode()) {
2245 Element* element = static_cast<Element*>(node);
2246 String roleAttribute = element->getAttribute(WebCore::HTMLNames::roleAttr).string();
2247 if (equalIgnoringCase(roleAttribute, "heading"))
2254 bool WebViewCore::isVisible(Node* node)
2256 if (!node->isStyledElement())
2258 RenderStyle* style = node->computedStyle();
2259 return (style->display() != NONE && style->visibility() != HIDDEN);
2262 String WebViewCore::formatMarkup(DOMSelection* selection)
2264 ExceptionCode ec = 0;
2265 PassRefPtr<Range> rangeRef = selection->getRangeAt(0, ec);
2267 LOGE("Error accessing the first selection range. Error code: %d", ec);
2270 // TODO: This breaks in certain cases - WebKit bug. Figure out and work around it
2271 String markup = createMarkup(rangeRef.get());
2272 int fromIdx = markup.find("<span class=\"Apple-style-span\"");
2273 while (fromIdx > -1) {
2274 unsigned toIdx = markup.find(">");
2275 markup = markup.replace(fromIdx, toIdx - fromIdx + 1, "");
2276 markup = markup.replace("</span>", "");
2277 fromIdx = markup.find("<span class=\"Apple-style-span\"");
2282 void WebViewCore::tryFocusInlineSelectionElement(DOMSelection* selection)
2284 Node* currentNode = selection->anchorNode();
2285 Node* endNode = selection->focusNode();
2286 while (currentNode) {
2287 if (focusIfFocusableAndNotTextInput(selection, currentNode))
2289 currentNode = currentNode->traverseNextNode(endNode);
2293 bool WebViewCore::focusIfFocusableAndNotTextInput(DOMSelection* selection, Node* node)
2295 // TODO (svetoslavganov): Synchronize Android and WebKit focus
2296 if (node->isFocusable()) {
2297 WebCore::RenderObject* renderer = node->renderer();
2298 if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
2299 // restore the selection after focus workaround for
2300 // the FIXME in Element.cpp#updateFocusAppearance
2301 ExceptionCode ec = 0;
2302 PassRefPtr<Range> rangeRef = selection->getRangeAt(0, ec);
2303 moveFocus(m_mainFrame, node);
2305 selection->addRange(rangeRef.get());
2312 void WebViewCore::deleteSelection(int start, int end, int textGeneration)
2314 setSelection(start, end);
2317 WebCore::Node* focus = currentFocus();
2320 // Prevent our editor client from passing a message to change the
2322 EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
2323 m_mainFrame->editor()->client());
2324 client->setUiGeneratedSelectionChange(true);
2325 PlatformKeyboardEvent down(AKEYCODE_DEL, 0, 0, true, false, false, false);
2326 PlatformKeyboardEvent up(AKEYCODE_DEL, 0, 0, false, false, false, false);
2329 client->setUiGeneratedSelectionChange(false);
2330 m_textGeneration = textGeneration;
2333 void WebViewCore::replaceTextfieldText(int oldStart,
2334 int oldEnd, const WTF::String& replace, int start, int end,
2337 WebCore::Node* focus = currentFocus();
2340 setSelection(oldStart, oldEnd);
2341 // Prevent our editor client from passing a message to change the
2343 EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
2344 m_mainFrame->editor()->client());
2345 client->setUiGeneratedSelectionChange(true);
2346 WebCore::TypingCommand::insertText(focus->document(), replace,
2348 client->setUiGeneratedSelectionChange(false);
2349 // setSelection calls revealSelection, so there is no need to do it here.
2350 setSelection(start, end);
2351 m_textGeneration = textGeneration;
2354 void WebViewCore::passToJs(int generation, const WTF::String& current,
2355 const PlatformKeyboardEvent& event)
2357 WebCore::Node* focus = currentFocus();
2359 DBG_NAV_LOG("!focus");
2363 WebCore::RenderObject* renderer = focus->renderer();
2364 if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
2365 DBG_NAV_LOGD("renderer==%p || not text", renderer);
2369 // Block text field updates during a key press.
2370 m_blockTextfieldUpdates = true;
2371 // Also prevent our editor client from passing a message to change the
2373 EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
2374 m_mainFrame->editor()->client());
2375 client->setUiGeneratedSelectionChange(true);
2377 client->setUiGeneratedSelectionChange(false);
2378 m_blockTextfieldUpdates = false;
2379 m_textGeneration = generation;
2380 WebCore::RenderTextControl* renderText =
2381 static_cast<WebCore::RenderTextControl*>(renderer);
2382 WTF::String test = renderText->text();
2383 if (test != current) {
2384 // If the text changed during the key event, update the UI text field.
2385 updateTextfield(focus, false, test);
2387 DBG_NAV_LOG("test == current");
2389 // Now that the selection has settled down, send it.
2390 updateTextSelection();
2393 void WebViewCore::scrollFocusedTextInput(float xPercent, int y)
2395 WebCore::Node* focus = currentFocus();
2397 DBG_NAV_LOG("!focus");
2401 WebCore::RenderObject* renderer = focus->renderer();
2402 if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
2403 DBG_NAV_LOGD("renderer==%p || not text", renderer);
2407 WebCore::RenderTextControl* renderText =
2408 static_cast<WebCore::RenderTextControl*>(renderer);
2409 int x = (int) (xPercent * (renderText->scrollWidth() -
2410 renderText->clientWidth()));
2411 DBG_NAV_LOGD("x=%d y=%d xPercent=%g scrollW=%d clientW=%d", x, y,
2412 xPercent, renderText->scrollWidth(), renderText->clientWidth());
2413 renderText->setScrollLeft(x);
2414 renderText->setScrollTop(y);
2417 void WebViewCore::setFocusControllerActive(bool active)
2419 m_mainFrame->page()->focusController()->setActive(active);
2422 void WebViewCore::saveDocumentState(WebCore::Frame* frame)
2424 if (!CacheBuilder::validNode(m_mainFrame, frame, 0))
2425 frame = m_mainFrame;
2426 WebCore::HistoryItem *item = frame->loader()->history()->currentItem();
2428 // item can be null when there is no offical URL for the current page. This happens
2429 // when the content is loaded using with WebCoreFrameBridge::LoadData() and there
2430 // is no failing URL (common case is when content is loaded using data: scheme)
2432 item->setDocumentState(frame->document()->formElementsState());
2436 // Convert a WTF::String into an array of characters where the first
2437 // character represents the length, for easy conversion to java.
2438 static uint16_t* stringConverter(const WTF::String& text)
2440 size_t length = text.length();
2441 uint16_t* itemName = new uint16_t[length+1];
2442 itemName[0] = (uint16_t)length;
2443 uint16_t* firstChar = &(itemName[1]);
2444 memcpy((void*)firstChar, text.characters(), sizeof(UChar)*length);
2448 // Response to dropdown created for a listbox.
2449 class ListBoxReply : public WebCoreReply {
2451 ListBoxReply(WebCore::HTMLSelectElement* select, WebCore::Frame* frame, WebViewCore* view)
2457 // Response used if the listbox only allows single selection.
2458 // index is listIndex of the selected item, or -1 if nothing is selected.
2459 virtual void replyInt(int index)
2462 // Special value for cancel. Do nothing.
2465 // If the select element no longer exists, due to a page change, etc,
2467 if (!m_select || !CacheBuilder::validNode(m_viewImpl->m_mainFrame,
2470 // Use a pointer to HTMLSelectElement's superclass, where
2471 // listToOptionIndex is public.
2472 SelectElement* selectElement = m_select;
2473 int optionIndex = selectElement->listToOptionIndex(index);
2474 m_select->setSelectedIndex(optionIndex, true);
2475 m_select->dispatchFormControlChangeEvent();
2476 m_viewImpl->contentInvalidate(m_select->getRect());
2479 // Response if the listbox allows multiple selection. array stores the listIndices
2480 // of selected positions.
2481 virtual void replyIntArray(const int* array, int count)
2483 // If the select element no longer exists, due to a page change, etc,
2485 if (!m_select || !CacheBuilder::validNode(m_viewImpl->m_mainFrame,
2489 // If count is 1 or 0, use replyInt.
2490 SkASSERT(count > 1);
2492 const WTF::Vector<Element*>& items = m_select->listItems();
2493 int totalItems = static_cast<int>(items.size());
2494 // Keep track of the position of the value we are comparing against.
2496 // The value we are comparing against.
2497 int selection = array[arrayIndex];
2498 WebCore::HTMLOptionElement* option;
2499 for (int listIndex = 0; listIndex < totalItems; listIndex++) {
2500 if (items[listIndex]->hasLocalName(WebCore::HTMLNames::optionTag)) {
2501 option = static_cast<WebCore::HTMLOptionElement*>(
2503 if (listIndex == selection) {
2504 option->setSelectedState(true);
2506 if (arrayIndex == count)
2509 selection = array[arrayIndex];
2511 option->setSelectedState(false);
2514 m_select->dispatchFormControlChangeEvent();
2515 m_viewImpl->contentInvalidate(m_select->getRect());
2518 // The select element associated with this listbox.
2519 WebCore::HTMLSelectElement* m_select;
2520 // The frame of this select element, to verify that it is valid.
2521 WebCore::Frame* m_frame;
2522 // For calling invalidate and checking the select element's validity
2523 WebViewCore* m_viewImpl;
2526 // Create an array of java Strings.
2527 static jobjectArray makeLabelArray(JNIEnv* env, const uint16_t** labels, size_t count)
2529 jclass stringClass = env->FindClass("java/lang/String");
2530 LOG_ASSERT(stringClass, "Could not find java/lang/String");
2531 jobjectArray array = env->NewObjectArray(count, stringClass, 0);
2532 LOG_ASSERT(array, "Could not create new string array");
2534 for (size_t i = 0; i < count; i++) {
2535 jobject newString = env->NewString(&labels[i][1], labels[i][0]);
2536 env->SetObjectArrayElement(array, i, newString);
2537 env->DeleteLocalRef(newString);
2538 checkException(env);
2540 env->DeleteLocalRef(stringClass);
2544 void WebViewCore::openFileChooser(PassRefPtr<WebCore::FileChooser> chooser) {
2547 JNIEnv* env = JSC::Bindings::getJNIEnv();
2549 WTF::String acceptType = chooser->acceptTypes();
2550 jstring jAcceptType = WtfStringToJstring(env, acceptType);
2551 jstring jName = (jstring) env->CallObjectMethod(
2552 m_javaGlue->object(env).get(), m_javaGlue->m_openFileChooser, jAcceptType);
2553 checkException(env);
2554 env->DeleteLocalRef(jAcceptType);
2556 const UChar* string = static_cast<const UChar*>(env->GetStringChars(jName, NULL));
2561 WTF::String webcoreString = jstringToWtfString(env, jName);
2562 env->ReleaseStringChars(jName, string);
2564 if (webcoreString.length())
2565 chooser->chooseFile(webcoreString);
2568 void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount,
2569 bool multiple, const int selected[], size_t selectedCountOrSelection)
2571 // If m_popupReply is not null, then we already have a list showing.
2572 if (m_popupReply != 0)
2575 LOG_ASSERT(m_javaGlue->m_obj, "No java widget associated with this view!");
2577 // Create an array of java Strings for the drop down.
2578 JNIEnv* env = JSC::Bindings::getJNIEnv();
2579 jobjectArray labelArray = makeLabelArray(env, labels, count);
2581 // Create an array determining whether each item is enabled.
2582 jintArray enabledArray = env->NewIntArray(enabledCount);
2583 checkException(env);
2584 jint* ptrArray = env->GetIntArrayElements(enabledArray, 0);
2585 checkException(env);
2586 for (size_t i = 0; i < enabledCount; i++) {
2587 ptrArray[i] = enabled[i];
2589 env->ReleaseIntArrayElements(enabledArray, ptrArray, 0);
2590 checkException(env);
2593 // Pass up an array representing which items are selected.
2594 jintArray selectedArray = env->NewIntArray(selectedCountOrSelection);
2595 checkException(env);
2596 jint* selArray = env->GetIntArrayElements(selectedArray, 0);
2597 checkException(env);
2598 for (size_t i = 0; i < selectedCountOrSelection; i++) {
2599 selArray[i] = selected[i];
2601 env->ReleaseIntArrayElements(selectedArray, selArray, 0);
2603 env->CallVoidMethod(m_javaGlue->object(env).get(),
2604 m_javaGlue->m_requestListBox, labelArray, enabledArray,
2606 env->DeleteLocalRef(selectedArray);
2608 // Pass up the single selection.
2609 env->CallVoidMethod(m_javaGlue->object(env).get(),
2610 m_javaGlue->m_requestSingleListBox, labelArray, enabledArray,
2611 selectedCountOrSelection);
2614 env->DeleteLocalRef(labelArray);
2615 env->DeleteLocalRef(enabledArray);
2616 checkException(env);
2619 m_popupReply = reply;
2622 bool WebViewCore::key(const PlatformKeyboardEvent& event)
2624 WebCore::EventHandler* eventHandler;
2625 WebCore::Node* focusNode = currentFocus();
2626 DBG_NAV_LOGD("keyCode=%s unichar=%d focusNode=%p",
2627 event.keyIdentifier().utf8().data(), event.unichar(), focusNode);
2629 WebCore::Frame* frame = focusNode->document()->frame();
2630 WebFrame* webFrame = WebFrame::getWebFrame(frame);
2631 eventHandler = frame->eventHandler();
2632 VisibleSelection old = frame->selection()->selection();
2633 bool handled = eventHandler->keyEvent(event);
2634 if (isContentEditable(focusNode)) {
2635 // keyEvent will return true even if the contentEditable did not
2636 // change its selection. In the case that it does not, we want to
2637 // return false so that the key will be sent back to our navigation
2639 handled = frame->selection()->selection() != old;
2643 eventHandler = m_mainFrame->eventHandler();
2645 return eventHandler->keyEvent(event);
2648 // For when the user clicks the trackball
2649 void WebViewCore::click(WebCore::Frame* frame, WebCore::Node* node) {
2651 WebCore::IntPoint pt = m_mousePos;
2652 pt.move(m_scrollOffsetX, m_scrollOffsetY);
2653 WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->
2654 hitTestResultAtPoint(pt, false);
2655 node = hitTestResult.innerNode();
2656 frame = node->document()->frame();
2657 DBG_NAV_LOGD("m_mousePos=(%d,%d) m_scrollOffset=(%d,%d) pt=(%d,%d)"
2658 " node=%p", m_mousePos.x(), m_mousePos.y(),
2659 m_scrollOffsetX, m_scrollOffsetY, pt.x(), pt.y(), node);
2662 EditorClientAndroid* client
2663 = static_cast<EditorClientAndroid*>(
2664 m_mainFrame->editor()->client());
2665 client->setShouldChangeSelectedRange(false);
2666 handleMouseClick(frame, node);
2667 client->setShouldChangeSelectedRange(true);
2671 #if USE(ACCELERATED_COMPOSITING)
2672 GraphicsLayerAndroid* WebViewCore::graphicsRootLayer() const
2674 RenderView* contentRenderer = m_mainFrame->contentRenderer();
2675 if (!contentRenderer)
2677 return static_cast<GraphicsLayerAndroid*>(
2678 contentRenderer->compositor()->rootPlatformLayer());
2682 bool WebViewCore::handleTouchEvent(int action, Vector<IntPoint>& points, int metaState)
2684 bool preventDefault = false;
2686 #if USE(ACCELERATED_COMPOSITING)
2687 GraphicsLayerAndroid* rootLayer = graphicsRootLayer();
2689 rootLayer->pauseDisplay(true);
2692 #if ENABLE(TOUCH_EVENTS) // Android
2693 WebCore::TouchEventType type = WebCore::TouchStart;
2694 WebCore::PlatformTouchPoint::State touchState = WebCore::PlatformTouchPoint::TouchPressed;
2696 case 0: // MotionEvent.ACTION_DOWN
2697 case 5: // MotionEvent.ACTION_POINTER_DOWN
2698 type = WebCore::TouchStart;
2700 case 1: // MotionEvent.ACTION_UP
2701 case 6: // MotionEvent.ACTION_POINTER_UP
2702 type = WebCore::TouchEnd;
2703 touchState = WebCore::PlatformTouchPoint::TouchReleased;
2705 case 2: // MotionEvent.ACTION_MOVE
2706 type = WebCore::TouchMove;
2707 touchState = WebCore::PlatformTouchPoint::TouchMoved;
2709 case 3: // MotionEvent.ACTION_CANCEL
2710 type = WebCore::TouchCancel;
2711 touchState = WebCore::PlatformTouchPoint::TouchCancelled;
2713 case 0x100: // WebViewCore.ACTION_LONGPRESS
2714 type = WebCore::TouchLongPress;
2715 touchState = WebCore::PlatformTouchPoint::TouchPressed;
2717 case 0x200: // WebViewCore.ACTION_DOUBLETAP
2718 type = WebCore::TouchDoubleTap;
2719 touchState = WebCore::PlatformTouchPoint::TouchPressed;
2722 // We do not support other kinds of touch event inside WebCore
2724 LOGW("Java passed a touch event type that we do not support in WebCore: %d", action);
2728 // Track previous touch and if stationary set the state.
2729 for (unsigned c = 0; c < points.size(); c++) {
2730 points[c].setX(points[c].x() - m_scrollOffsetX);
2731 points[c].setY(points[c].y() - m_scrollOffsetY);
2733 WebCore::PlatformTouchEvent te(points, type, touchState, metaState);
2734 preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te);
2737 #if USE(ACCELERATED_COMPOSITING)
2739 rootLayer->pauseDisplay(false);
2741 return preventDefault;
2744 void WebViewCore::touchUp(int touchGeneration,
2745 WebCore::Frame* frame, WebCore::Node* node, int x, int y)
2747 if (touchGeneration == 0) {
2748 // m_mousePos should be set in getTouchHighlightRects()
2749 WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(m_mousePos, false);
2750 node = hitTestResult.innerNode();
2752 frame = node->document()->frame();
2755 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);
2757 if (m_touchGeneration > touchGeneration) {
2758 DBG_NAV_LOGD("m_touchGeneration=%d > touchGeneration=%d"
2759 " x=%d y=%d", m_touchGeneration, touchGeneration, x, y);
2760 return; // short circuit if a newer touch has been generated
2762 // This moves m_mousePos to the correct place, and handleMouseClick uses
2763 // m_mousePos to determine where the click happens.
2764 moveMouse(frame, x, y);
2765 m_lastGeneration = touchGeneration;
2767 if (frame && CacheBuilder::validNode(m_mainFrame, frame, 0)) {
2768 frame->loader()->resetMultipleFormSubmissionProtection();
2770 DBG_NAV_LOGD("touchGeneration=%d handleMouseClick frame=%p node=%p"
2771 " x=%d y=%d", touchGeneration, frame, node, x, y);
2772 handleMouseClick(frame, node);
2775 // Return the RenderLayer for the given RenderObject only if the layer is
2776 // composited and it contains a scrollable content layer.
2777 static WebCore::RenderLayer* getLayerFromRenderer(
2778 WebCore::RenderObject* renderer, LayerAndroid** aLayer)
2782 WebCore::RenderLayer* layer = renderer->enclosingSelfPaintingLayer();
2783 if (!layer || !layer->backing())
2785 GraphicsLayerAndroid* graphicsLayer =
2786 static_cast<GraphicsLayerAndroid*>(layer->backing()->graphicsLayer());
2789 LayerAndroid* layerAndroid = graphicsLayer->contentLayer();
2790 if (!layerAndroid || !layerAndroid->contentIsScrollable())
2792 *aLayer = layerAndroid;
2796 // Scroll the RenderLayer associated with a scrollable div element. This is
2797 // done so that the node is visible when it is clicked.
2798 static void scrollLayer(WebCore::RenderObject* renderer, WebCore::IntPoint* pos)
2800 LayerAndroid* aLayer;
2801 WebCore::RenderLayer* layer = getLayerFromRenderer(renderer, &aLayer);
2804 WebCore::IntRect absBounds = renderer->absoluteBoundingBoxRect();
2805 // Do not include the outline when moving the node's bounds.
2806 WebCore::IntRect layerBounds = layer->renderer()->absoluteBoundingBoxRect();
2808 // Move the node's bounds into the layer's coordinates.
2809 absBounds.move(-layerBounds.x(), -layerBounds.y());
2811 int diffX = layer->scrollXOffset();
2812 int diffY = layer->scrollYOffset();
2813 // Scroll the layer to the node's position. The false parameters tell the
2814 // layer not to invalidate.
2815 layer->scrollToOffset(absBounds.x(), absBounds.y(), false, true);
2816 diffX = layer->scrollXOffset() - diffX;
2817 diffY = layer->scrollYOffset() - diffY;
2819 // Update the mouse position to the layer offset.
2820 pos->move(-diffX, -diffY);
2823 // Common code for both clicking with the trackball and touchUp
2824 bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr)
2826 m_lastClickWasOnTextInput = false;
2827 bool valid = framePtr == NULL
2828 || CacheBuilder::validNode(m_mainFrame, framePtr, nodePtr);
2829 WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame);
2830 if (valid && nodePtr) {
2831 // Need to special case area tags because an image map could have an area element in the middle
2832 // so when attempting to get the default, the point chosen would be follow the wrong link.
2833 if (nodePtr->hasTagName(WebCore::HTMLNames::areaTag)) {
2834 webFrame->setUserInitiatedAction(true);
2835 nodePtr->dispatchSimulatedClick(0, true, true);
2836 webFrame->setUserInitiatedAction(false);
2837 DBG_NAV_LOG("area");
2841 WebCore::RenderObject* renderer = nodePtr->renderer();
2842 if (renderer && (renderer->isMenuList() || renderer->isListBox())) {
2843 WebCore::HTMLSelectElement* select = static_cast<WebCore::HTMLSelectElement*>(nodePtr);
2844 const WTF::Vector<WebCore::Element*>& listItems = select->listItems();
2845 SkTDArray<const uint16_t*> names;
2846 // Possible values for enabledArray. Keep in Sync with values in
2847 // InvokeListBox.Container in WebView.java
2850 OPTION_DISABLED = 0,
2853 SkTDArray<int> enabledArray;
2854 SkTDArray<int> selectedArray;
2855 int size = listItems.size();
2856 bool multiple = select->multiple();
2857 for (int i = 0; i < size; i++) {
2858 if (listItems[i]->hasTagName(WebCore::HTMLNames::optionTag)) {
2859 WebCore::HTMLOptionElement* option = static_cast<WebCore::HTMLOptionElement*>(listItems[i]);
2860 *names.append() = stringConverter(option->textIndentedToRespectGroupLabel());
2861 *enabledArray.append() = option->disabled() ? OPTION_DISABLED : OPTION_ENABLED;
2862 if (multiple && option->selected())
2863 *selectedArray.append() = i;
2864 } else if (listItems[i]->hasTagName(WebCore::HTMLNames::optgroupTag)) {
2865 WebCore::HTMLOptGroupElement* optGroup = static_cast<WebCore::HTMLOptGroupElement*>(listItems[i]);
2866 *names.append() = stringConverter(optGroup->groupLabelText());
2867 *enabledArray.append() = OPTGROUP;
2870 WebCoreReply* reply = new ListBoxReply(select, select->document()->frame(), this);
2871 // Use a pointer to HTMLSelectElement's superclass, where
2872 // optionToListIndex is public.
2873 SelectElement* selectElement = select;
2874 listBoxRequest(reply, names.begin(), size, enabledArray.begin(), enabledArray.count(),
2875 multiple, selectedArray.begin(), multiple ? selectedArray.count() :
2876 selectElement->optionToListIndex(select->selectedIndex()));
2877 DBG_NAV_LOG("menu list");
2881 if (!valid || !framePtr)
2882 framePtr = m_mainFrame;
2883 if (nodePtr && valid) {
2884 scrollLayer(nodePtr->renderer(), &m_mousePos);
2885 if (isContentEditable(nodePtr) || (nodePtr->renderer()
2886 && (nodePtr->renderer()-> isTextArea() || nodePtr->renderer()->isTextField()))) {
2887 // The user clicked on a text input field. If this causes a blur event
2888 // on a different text input, do not hide the keyboard in formDidBlur
2889 m_lastClickWasOnTextInput = true;
2892 webFrame->setUserInitiatedAction(true);
2893 WebCore::PlatformMouseEvent mouseDown(m_mousePos, m_mousePos, WebCore::LeftButton,
2894 WebCore::MouseEventPressed, 1, false, false, false, false,
2895 WTF::currentTime());
2896 // ignore the return from as it will return true if the hit point can trigger selection change
2897 framePtr->eventHandler()->handleMousePressEvent(mouseDown);
2898 WebCore::PlatformMouseEvent mouseUp(m_mousePos, m_mousePos, WebCore::LeftButton,
2899 WebCore::MouseEventReleased, 1, false, false, false, false,
2900 WTF::currentTime());
2901 bool handled = framePtr->eventHandler()->handleMouseReleaseEvent(mouseUp);
2902 webFrame->setUserInitiatedAction(false);
2904 m_lastClickWasOnTextInput = false;
2906 // If the user clicked on a textfield, make the focusController active
2907 // so we show the blinking cursor.
2908 WebCore::Node* focusNode = currentFocus();
2909 DBG_NAV_LOGD("m_mousePos={%d,%d} focusNode=%p handled=%s", m_mousePos.x(),
2910 m_mousePos.y(), focusNode, handled ? "true" : "false");
2912 WebCore::RenderObject* renderer = focusNode->renderer();
2913 if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
2914 bool ime = !(static_cast<WebCore::HTMLInputElement*>(focusNode))
2917 RenderTextControl* rtc
2918 = static_cast<RenderTextControl*> (renderer);
2919 requestKeyboardWithSelection(focusNode, rtc->selectionStart(),
2920 rtc->selectionEnd());
2922 requestKeyboard(false);
2925 // If the selection is contentEditable, show the keyboard so the
2926 // user can type. Otherwise hide the keyboard because no text
2928 if (isContentEditable(focusNode)) {
2929 requestKeyboard(true);
2935 // There is no focusNode, so the keyboard is not needed.
2941 void WebViewCore::popupReply(int index)
2944 m_popupReply->replyInt(index);
2945 Release(m_popupReply);
2950 void WebViewCore::popupReply(const int* array, int count)
2953 m_popupReply->replyIntArray(array, count);
2954 Release(m_popupReply);
2955 m_popupReply = NULL;
2959 void WebViewCore::formDidBlur(const WebCore::Node* node)
2961 // This blur is the result of clicking on a different input. Do not hide
2962 // the keyboard, since it will just be opened again.
2963 if (m_lastClickWasOnTextInput) return;
2965 JNIEnv* env = JSC::Bindings::getJNIEnv();
2966 env->CallVoidMethod(m_javaGlue->object(env).get(),
2967 m_javaGlue->m_formDidBlur, reinterpret_cast<int>(node));
2968 checkException(env);
2971 void WebViewCore::addMessageToConsole(const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceID, int msgLevel) {
2972 JNIEnv* env = JSC::Bindings::getJNIEnv();
2973 jstring jMessageStr = WtfStringToJstring(env, message);
2974 jstring jSourceIDStr = WtfStringToJstring(env, sourceID);
2975 env->CallVoidMethod(m_javaGlue->object(env).get(),
2976 m_javaGlue->m_addMessageToConsole, jMessageStr, lineNumber,
2977 jSourceIDStr, msgLevel);
2978 env->DeleteLocalRef(jMessageStr);
2979 env->DeleteLocalRef(jSourceIDStr);
2980 checkException(env);
2983 void WebViewCore::jsAlert(const WTF::String& url, const WTF::String& text)
2985 JNIEnv* env = JSC::Bindings::getJNIEnv();
2986 jstring jInputStr = WtfStringToJstring(env, text);
2987 jstring jUrlStr = WtfStringToJstring(env, url);
2988 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsAlert, jUrlStr, jInputStr);
2989 env->DeleteLocalRef(jInputStr);
2990 env->DeleteLocalRef(jUrlStr);
2991 checkException(env);
2994 void WebViewCore::exceededDatabaseQuota(const WTF::String& url, const WTF::String& databaseIdentifier, const unsigned long long currentQuota, unsigned long long estimatedSize)
2996 #if ENABLE(DATABASE)
2997 JNIEnv* env = JSC::Bindings::getJNIEnv();
2998 jstring jDatabaseIdentifierStr = WtfStringToJstring(env, databaseIdentifier);
2999 jstring jUrlStr = WtfStringToJstring(env, url);
3000 env->CallVoidMethod(m_javaGlue->object(env).get(),
3001 m_javaGlue->m_exceededDatabaseQuota, jUrlStr,
3002 jDatabaseIdentifierStr, currentQuota, estimatedSize);
3003 env->DeleteLocalRef(jDatabaseIdentifierStr);
3004 env->DeleteLocalRef(jUrlStr);
3005 checkException(env);
3009 void WebViewCore::reachedMaxAppCacheSize(const unsigned long long spaceNeeded)
3011 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
3012 JNIEnv* env = JSC::Bindings::getJNIEnv();
3013 env->CallVoidMethod(m_javaGlue->object(env).get(),
3014 m_javaGlue->m_reachedMaxAppCacheSize, spaceNeeded);
3015 checkException(env);
3019 void WebViewCore::populateVisitedLinks(WebCore::PageGroup* group)
3021 m_groupForVisitedLinks = group;
3022 JNIEnv* env = JSC::Bindings::getJNIEnv();
3023 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_populateVisitedLinks);
3024 checkException(env);
3027 void WebViewCore::geolocationPermissionsShowPrompt(const WTF::String& origin)
3029 JNIEnv* env = JSC::Bindings::getJNIEnv();
3030 jstring originString = WtfStringToJstring(env, origin);
3031 env->CallVoidMethod(m_javaGlue->object(env).get(),
3032 m_javaGlue->m_geolocationPermissionsShowPrompt,
3034 env->DeleteLocalRef(originString);
3035 checkException(env);
3038 void WebViewCore::geolocationPermissionsHidePrompt()
3040 JNIEnv* env = JSC::Bindings::getJNIEnv();
3041 env->CallVoidMethod(m_javaGlue->object(env).get(),
3042 m_javaGlue->m_geolocationPermissionsHidePrompt);
3043 checkException(env);
3046 jobject WebViewCore::getDeviceMotionService()
3048 JNIEnv* env = JSC::Bindings::getJNIEnv();
3049 jobject object = env->CallObjectMethod(m_javaGlue->object(env).get(),
3050 m_javaGlue->m_getDeviceMotionService);
3051 checkException(env);
3055 jobject WebViewCore::getDeviceOrientationService()
3057 JNIEnv* env = JSC::Bindings::getJNIEnv();
3058 jobject object = env->CallObjectMethod(m_javaGlue->object(env).get(),
3059 m_javaGlue->m_getDeviceOrientationService);
3060 checkException(env);
3064 bool WebViewCore::jsConfirm(const WTF::String& url, const WTF::String& text)
3066 JNIEnv* env = JSC::Bindings::getJNIEnv();
3067 jstring jInputStr = WtfStringToJstring(env, text);
3068 jstring jUrlStr = WtfStringToJstring(env, url);
3069 jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsConfirm, jUrlStr, jInputStr);
3070 env->DeleteLocalRef(jInputStr);
3071 env->DeleteLocalRef(jUrlStr);
3072 checkException(env);
3076 bool WebViewCore::jsPrompt(const WTF::String& url, const WTF::String& text, const WTF::String& defaultValue, WTF::String& result)
3078 JNIEnv* env = JSC::Bindings::getJNIEnv();
3079 jstring jInputStr = WtfStringToJstring(env, text);
3080 jstring jDefaultStr = WtfStringToJstring(env, defaultValue);
3081 jstring jUrlStr = WtfStringToJstring(env, url);
3082 jstring returnVal = static_cast<jstring>(env->CallObjectMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsPrompt, jUrlStr, jInputStr, jDefaultStr));
3083 env->DeleteLocalRef(jInputStr);
3084 env->DeleteLocalRef(jDefaultStr);
3085 env->DeleteLocalRef(jUrlStr);
3086 checkException(env);
3088 // If returnVal is null, it means that the user cancelled the dialog.
3092 result = jstringToWtfString(env, returnVal);
3096 bool WebViewCore::jsUnload(const WTF::String& url, const WTF::String& message)
3098 JNIEnv* env = JSC::Bindings::getJNIEnv();
3099 jstring jInputStr = WtfStringToJstring(env, message);
3100 jstring jUrlStr = WtfStringToJstring(env, url);
3101 jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsUnload, jUrlStr, jInputStr);
3102 env->DeleteLocalRef(jInputStr);
3103 env->DeleteLocalRef(jUrlStr);
3104 checkException(env);
3108 bool WebViewCore::jsInterrupt()
3110 JNIEnv* env = JSC::Bindings::getJNIEnv();
3111 jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsInterrupt);
3112 checkException(env);
3117 WebViewCore::getJavaObject()
3119 return getRealObject(JSC::Bindings::getJNIEnv(), m_javaGlue->m_obj);
3123 WebViewCore::getWebViewJavaObject()
3125 JNIEnv* env = JSC::Bindings::getJNIEnv();
3126 return env->GetObjectField(m_javaGlue->object(env).get(), gWebViewCoreFields.m_webView);
3129 void WebViewCore::updateTextSelection() {
3130 WebCore::Node* focusNode = currentFocus();
3133 RenderObject* renderer = focusNode->renderer();
3134 if (!renderer || (!renderer->isTextArea() && !renderer->isTextField()))
3136 RenderTextControl* rtc = static_cast<RenderTextControl*>(renderer);
3137 JNIEnv* env = JSC::Bindings::getJNIEnv();
3138 env->CallVoidMethod(m_javaGlue->object(env).get(),
3139 m_javaGlue->m_updateTextSelection, reinterpret_cast<int>(focusNode),
3140 rtc->selectionStart(), rtc->selectionEnd(), m_textGeneration);
3141 checkException(env);
3144 void WebViewCore::updateTextfield(WebCore::Node* ptr, bool changeToPassword,
3145 const WTF::String& text)
3147 if (m_blockTextfieldUpdates)
3149 JNIEnv* env = JSC::Bindings::getJNIEnv();
3150 if (changeToPassword) {
3151 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
3152 (int) ptr, true, 0, m_textGeneration);
3153 checkException(env);
3156 jstring string = WtfStringToJstring(env, text);
3157 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
3158 (int) ptr, false, string, m_textGeneration);
3159 env->DeleteLocalRef(string);
3160 checkException(env);
3163 void WebViewCore::clearTextEntry()
3165 JNIEnv* env = JSC::Bindings::getJNIEnv();
3166 env->CallVoidMethod(m_javaGlue->object(env).get(),
3167 m_javaGlue->m_clearTextEntry);
3170 void WebViewCore::setBackgroundColor(SkColor c)
3172 WebCore::FrameView* view = m_mainFrame->view();
3176 // need (int) cast to find the right constructor
3177 WebCore::Color bcolor((int)SkColorGetR(c), (int)SkColorGetG(c),
3178 (int)SkColorGetB(c), (int)SkColorGetA(c));
3179 view->setBaseBackgroundColor(bcolor);
3181 // Background color of 0 indicates we want a transparent background
3183 view->setTransparent(true);
3186 jclass WebViewCore::getPluginClass(const WTF::String& libName, const char* className)
3188 JNIEnv* env = JSC::Bindings::getJNIEnv();
3190 jstring libString = WtfStringToJstring(env, libName);
3191 jstring classString = env->NewStringUTF(className);
3192 jobject pluginClass = env->CallObjectMethod(m_javaGlue->object(env).get(),
3193 m_javaGlue->m_getPluginClass,
3194 libString, classString);
3195 checkException(env);
3197 // cleanup unneeded local JNI references
3198 env->DeleteLocalRef(libString);
3199 env->DeleteLocalRef(classString);
3201 if (pluginClass != NULL) {
3202 return static_cast<jclass>(pluginClass);
3208 void WebViewCore::showFullScreenPlugin(jobject childView, NPP npp)
3210 JNIEnv* env = JSC::Bindings::getJNIEnv();
3211 AutoJObject obj = m_javaGlue->object(env);
3213 env->CallVoidMethod(obj.get(),
3214 m_javaGlue->m_showFullScreenPlugin, childView, (int)npp);
3215 checkException(env);
3218 void WebViewCore::hideFullScreenPlugin()
3220 JNIEnv* env = JSC::Bindings::getJNIEnv();
3221 env->CallVoidMethod(m_javaGlue->object(env).get(),
3222 m_javaGlue->m_hideFullScreenPlugin);
3223 checkException(env);
3226 jobject WebViewCore::addSurface(jobject view, int x, int y, int width, int height)
3228 JNIEnv* env = JSC::Bindings::getJNIEnv();
3229 jobject result = env->CallObjectMethod(m_javaGlue->object(env).get(),
3230 m_javaGlue->m_addSurface,
3231 view, x, y, width, height);
3232 checkException(env);
3236 void WebViewCore::updateSurface(jobject childView, int x, int y, int width, int height)
3238 JNIEnv* env = JSC::Bindings::getJNIEnv();
3239 env->CallVoidMethod(m_javaGlue->object(env).get(),
3240 m_javaGlue->m_updateSurface, childView,
3241 x, y, width, height);
3242 checkException(env);
3245 void WebViewCore::destroySurface(jobject childView)
3247 JNIEnv* env = JSC::Bindings::getJNIEnv();
3248 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_destroySurface, childView);
3249 checkException(env);
3252 jobject WebViewCore::getContext()
3254 JNIEnv* env = JSC::Bindings::getJNIEnv();
3255 AutoJObject obj = m_javaGlue->object(env);
3257 jobject result = env->CallObjectMethod(obj.get(), m_javaGlue->m_getContext);
3258 checkException(env);
3262 bool WebViewCore::validNodeAndBounds(Frame* frame, Node* node,
3263 const IntRect& originalAbsoluteBounds)
3265 bool valid = CacheBuilder::validNode(m_mainFrame, frame, node);
3268 RenderObject* renderer = node->renderer();
3271 IntRect absBounds = node->hasTagName(HTMLNames::areaTag)
3272 ? CacheBuilder::getAreaRect(static_cast<HTMLAreaElement*>(node))
3273 : renderer->absoluteBoundingBoxRect();
3274 return absBounds == originalAbsoluteBounds;
3277 void WebViewCore::showRect(int left, int top, int width, int height,
3278 int contentWidth, int contentHeight, float xPercentInDoc,
3279 float xPercentInView, float yPercentInDoc, float yPercentInView)
3281 JNIEnv* env = JSC::Bindings::getJNIEnv();
3282 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_showRect,
3283 left, top, width, height, contentWidth, contentHeight,
3284 xPercentInDoc, xPercentInView, yPercentInDoc, yPercentInView);
3285 checkException(env);
3288 void WebViewCore::centerFitRect(int x, int y, int width, int height)
3290 JNIEnv* env = JSC::Bindings::getJNIEnv();
3291 env->CallVoidMethod(m_javaGlue->object(env).get(),
3292 m_javaGlue->m_centerFitRect, x, y, width, height);
3293 checkException(env);
3297 void WebViewCore::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode)
3299 JNIEnv* env = JSC::Bindings::getJNIEnv();
3300 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setScrollbarModes,
3301 horizontalMode, verticalMode);
3302 checkException(env);
3305 void WebViewCore::notifyWebAppCanBeInstalled()
3307 JNIEnv* env = JSC::Bindings::getJNIEnv();
3308 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setInstallableWebApp);
3309 checkException(env);
3312 void WebViewCore::setWebTextViewAutoFillable(int queryId, const string16& previewSummary)
3314 JNIEnv* env = JSC::Bindings::getJNIEnv();
3315 jstring preview = env->NewString(previewSummary.data(), previewSummary.length());
3316 env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setWebTextViewAutoFillable, queryId, preview);
3317 env->DeleteLocalRef(preview);
3320 bool WebViewCore::drawIsPaused() const
3322 JNIEnv* env = JSC::Bindings::getJNIEnv();
3323 return env->GetBooleanField(m_javaGlue->object(env).get(),
3324 gWebViewCoreFields.m_drawIsPaused);
3327 //----------------------------------------------------------------------
3328 // Native JNI methods
3329 //----------------------------------------------------------------------
3330 static void RevealSelection(JNIEnv *env, jobject obj)
3332 GET_NATIVE_VIEW(env, obj)->revealSelection();
3335 static jstring RequestLabel(JNIEnv *env, jobject obj, int framePointer,
3338 return WtfStringToJstring(env, GET_NATIVE_VIEW(env, obj)->requestLabel(
3339 (WebCore::Frame*) framePointer, (WebCore::Node*) nodePointer));
3342 static void ClearContent(JNIEnv *env, jobject obj)
3344 #ifdef ANDROID_INSTRUMENT
3345 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3347 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3348 viewImpl->clearContent();
3351 static void UpdateFrameCacheIfLoading(JNIEnv *env, jobject obj)
3353 GET_NATIVE_VIEW(env, obj)->updateFrameCacheIfLoading();
3356 static void SetSize(JNIEnv *env, jobject obj, jint width, jint height,
3357 jint textWrapWidth, jfloat scale, jint screenWidth, jint screenHeight,
3358 jint anchorX, jint anchorY, jboolean ignoreHeight)
3360 #ifdef ANDROID_INSTRUMENT
3361 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3363 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3364 LOGV("webviewcore::nativeSetSize(%u %u)\n viewImpl: %p", (unsigned)width, (unsigned)height, viewImpl);
3365 LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetSize");
3366 viewImpl->setSizeScreenWidthAndScale(width, height, textWrapWidth, scale,
3367 screenWidth, screenHeight, anchorX, anchorY, ignoreHeight);
3370 static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jint userScrolled, jint x, jint y)
3372 #ifdef ANDROID_INSTRUMENT
3373 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3375 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3376 LOG_ASSERT(viewImpl, "need viewImpl");
3378 viewImpl->setScrollOffset(gen, userScrolled, x, y);
3381 static void SetGlobalBounds(JNIEnv *env, jobject obj, jint x, jint y, jint h,
3384 #ifdef ANDROID_INSTRUMENT
3385 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3387 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3388 LOG_ASSERT(viewImpl, "need viewImpl");
3390 viewImpl->setGlobalBounds(x, y, h, v);
3393 static jboolean Key(JNIEnv *env, jobject obj, jint keyCode, jint unichar,
3394 jint repeatCount, jboolean isShift, jboolean isAlt, jboolean isSym,
3397 #ifdef ANDROID_INSTRUMENT
3398 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3400 return GET_NATIVE_VIEW(env, obj)->key(PlatformKeyboardEvent(keyCode,
3401 unichar, repeatCount, isDown, isShift, isAlt, isSym));
3404 static void Click(JNIEnv *env, jobject obj, int framePtr, int nodePtr)
3406 #ifdef ANDROID_INSTRUMENT
3407 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3409 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3410 LOG_ASSERT(viewImpl, "viewImpl not set in Click");
3412 viewImpl->click(reinterpret_cast<WebCore::Frame*>(framePtr),
3413 reinterpret_cast<WebCore::Node*>(nodePtr));
3416 static void ContentInvalidateAll(JNIEnv *env, jobject obj)
3418 GET_NATIVE_VIEW(env, obj)->contentInvalidateAll();
3421 static void DeleteSelection(JNIEnv *env, jobject obj, jint start, jint end,
3422 jint textGeneration)
3424 #ifdef ANDROID_INSTRUMENT
3425 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3427 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3428 viewImpl->deleteSelection(start, end, textGeneration);
3431 static void SetSelection(JNIEnv *env, jobject obj, jint start, jint end)
3433 #ifdef ANDROID_INSTRUMENT
3434 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3436 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3437 viewImpl->setSelection(start, end);
3440 static jstring ModifySelection(JNIEnv *env, jobject obj, jint direction, jint granularity)
3442 #ifdef ANDROID_INSTRUMENT
3443 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3445 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3446 String selectionString = viewImpl->modifySelection(direction, granularity);
3447 return WtfStringToJstring(env, selectionString);
3450 static void ReplaceTextfieldText(JNIEnv *env, jobject obj,
3451 jint oldStart, jint oldEnd, jstring replace, jint start, jint end,
3452 jint textGeneration)
3454 #ifdef ANDROID_INSTRUMENT
3455 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3457 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3458 WTF::String webcoreString = jstringToWtfString(env, replace);
3459 viewImpl->replaceTextfieldText(oldStart,
3460 oldEnd, webcoreString, start, end, textGeneration);
3463 static void PassToJs(JNIEnv *env, jobject obj,
3464 jint generation, jstring currentText, jint keyCode,
3465 jint keyValue, jboolean down, jboolean cap, jboolean fn, jboolean sym)
3467 #ifdef ANDROID_INSTRUMENT
3468 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3470 WTF::String current = jstringToWtfString(env, currentText);
3471 GET_NATIVE_VIEW(env, obj)->passToJs(generation, current,
3472 PlatformKeyboardEvent(keyCode, keyValue, 0, down, cap, fn, sym));
3475 static void ScrollFocusedTextInput(JNIEnv *env, jobject obj, jfloat xPercent,
3478 #ifdef ANDROID_INSTRUMENT
3479 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3481 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3482 viewImpl->scrollFocusedTextInput(xPercent, y);
3485 static void SetFocusControllerActive(JNIEnv *env, jobject obj, jboolean active)
3487 #ifdef ANDROID_INSTRUMENT
3488 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3490 LOGV("webviewcore::nativeSetFocusControllerActive()\n");
3491 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3492 LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetFocusControllerActive");
3493 viewImpl->setFocusControllerActive(active);
3496 static void SaveDocumentState(JNIEnv *env, jobject obj, jint frame)
3498 #ifdef ANDROID_INSTRUMENT
3499 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3501 LOGV("webviewcore::nativeSaveDocumentState()\n");
3502 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3503 LOG_ASSERT(viewImpl, "viewImpl not set in nativeSaveDocumentState");
3504 viewImpl->saveDocumentState((WebCore::Frame*) frame);
3507 void WebViewCore::addVisitedLink(const UChar* string, int length)
3509 if (m_groupForVisitedLinks)
3510 m_groupForVisitedLinks->addVisitedLink(string, length);
3513 static jint UpdateLayers(JNIEnv *env, jobject obj)
3515 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3516 BaseLayerAndroid* result = viewImpl->createBaseLayer();
3517 return reinterpret_cast<jint>(result);
3520 static jint RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt)
3522 #ifdef ANDROID_INSTRUMENT
3523 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3525 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3526 SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region);
3528 BaseLayerAndroid* result = viewImpl->recordContent(nativeRegion, &nativePt);
3529 GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt);
3530 return reinterpret_cast<jint>(result);
3533 static void SplitContent(JNIEnv *env, jobject obj, jint content)
3535 #ifdef ANDROID_INSTRUMENT
3536 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3538 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3539 viewImpl->splitContent(reinterpret_cast<PictureSet*>(content));
3542 static void SendListBoxChoice(JNIEnv* env, jobject obj, jint choice)
3544 #ifdef ANDROID_INSTRUMENT
3545 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3547 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3548 LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoice");
3549 viewImpl->popupReply(choice);
3552 // Set aside a predetermined amount of space in which to place the listbox
3553 // choices, to avoid unnecessary allocations.
3554 // The size here is arbitrary. We want the size to be at least as great as the
3555 // number of items in the average multiple-select listbox.
3556 #define PREPARED_LISTBOX_STORAGE 10
3558 static void SendListBoxChoices(JNIEnv* env, jobject obj, jbooleanArray jArray,
3561 #ifdef ANDROID_INSTRUMENT
3562 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3564 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3565 LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoices");
3566 jboolean* ptrArray = env->GetBooleanArrayElements(jArray, 0);
3567 SkAutoSTMalloc<PREPARED_LISTBOX_STORAGE, int> storage(size);
3568 int* array = storage.get();
3570 for (int i = 0; i < size; i++) {
3575 env->ReleaseBooleanArrayElements(jArray, ptrArray, JNI_ABORT);
3576 viewImpl->popupReply(array, count);
3579 static jstring FindAddress(JNIEnv *env, jobject obj, jstring addr,
3580 jboolean caseInsensitive)
3582 #ifdef ANDROID_INSTRUMENT
3583 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3587 int length = env->GetStringLength(addr);
3590 const jchar* addrChars = env->GetStringChars(addr, 0);
3592 bool success = CacheBuilder::FindAddress(addrChars, length,
3593 &start, &end, caseInsensitive) == CacheBuilder::FOUND_COMPLETE;
3596 ret = env->NewString(addrChars + start, end - start);
3597 env->DeleteLocalRef(ret);
3599 env->ReleaseStringChars(addr, addrChars);
3603 static jboolean HandleTouchEvent(JNIEnv *env, jobject obj, jint action,
3604 jintArray xArray, jintArray yArray, jint count, jint metaState)
3606 #ifdef ANDROID_INSTRUMENT
3607 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3609 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3610 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3611 jint* ptrXArray = env->GetIntArrayElements(xArray, 0);
3612 jint* ptrYArray = env->GetIntArrayElements(yArray, 0);
3613 Vector<IntPoint> points(count);
3614 for (int c = 0; c < count; c++) {
3615 points[c].setX(ptrXArray[c]);
3616 points[c].setY(ptrYArray[c]);
3618 env->ReleaseIntArrayElements(xArray, ptrXArray, JNI_ABORT);
3619 env->ReleaseIntArrayElements(yArray, ptrYArray, JNI_ABORT);
3621 return viewImpl->handleTouchEvent(action, points, metaState);
3624 static void TouchUp(JNIEnv *env, jobject obj, jint touchGeneration,
3625 jint frame, jint node, jint x, jint y)
3627 #ifdef ANDROID_INSTRUMENT
3628 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3630 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3631 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3632 viewImpl->touchUp(touchGeneration,
3633 (WebCore::Frame*) frame, (WebCore::Node*) node, x, y);
3636 static jstring RetrieveHref(JNIEnv *env, jobject obj, jint frame,
3639 #ifdef ANDROID_INSTRUMENT
3640 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3642 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3643 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3644 WTF::String result = viewImpl->retrieveHref((WebCore::Frame*) frame,
3645 (WebCore::Node*) node);
3646 if (!result.isEmpty())
3647 return WtfStringToJstring(env, result);
3651 static jstring RetrieveAnchorText(JNIEnv *env, jobject obj, jint frame,
3654 #ifdef ANDROID_INSTRUMENT
3655 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3657 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3658 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3659 WTF::String result = viewImpl->retrieveAnchorText((WebCore::Frame*) frame,
3660 (WebCore::Node*) node);
3661 if (!result.isEmpty())
3662 return WtfStringToJstring(env, result);
3667 static void MoveFocus(JNIEnv *env, jobject obj, jint framePtr, jint nodePtr)
3669 #ifdef ANDROID_INSTRUMENT
3670 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3672 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3673 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3674 viewImpl->moveFocus((WebCore::Frame*) framePtr, (WebCore::Node*) nodePtr);
3677 static void MoveMouse(JNIEnv *env, jobject obj, jint frame,
3680 #ifdef ANDROID_INSTRUMENT
3681 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3683 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3684 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3685 viewImpl->moveMouse((WebCore::Frame*) frame, x, y);
3688 static void MoveMouseIfLatest(JNIEnv *env, jobject obj, jint moveGeneration,
3689 jint frame, jint x, jint y)
3691 #ifdef ANDROID_INSTRUMENT
3692 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3694 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3695 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3696 viewImpl->moveMouseIfLatest(moveGeneration,
3697 (WebCore::Frame*) frame, x, y);
3700 static void UpdateFrameCache(JNIEnv *env, jobject obj)
3702 #ifdef ANDROID_INSTRUMENT
3703 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3705 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3706 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3707 viewImpl->updateFrameCache();
3710 static jint GetContentMinPrefWidth(JNIEnv *env, jobject obj)
3712 #ifdef ANDROID_INSTRUMENT
3713 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3715 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3716 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3718 WebCore::Frame* frame = viewImpl->mainFrame();
3720 WebCore::Document* document = frame->document();
3722 WebCore::RenderObject* renderer = document->renderer();
3723 if (renderer && renderer->isRenderView()) {
3724 return renderer->minPreferredLogicalWidth();
3731 static void SetViewportSettingsFromNative(JNIEnv *env, jobject obj)
3733 #ifdef ANDROID_INSTRUMENT
3734 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3736 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3737 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3739 WebCore::Settings* s = viewImpl->mainFrame()->page()->settings();
3743 #ifdef ANDROID_META_SUPPORT
3744 env->SetIntField(obj, gWebViewCoreFields.m_viewportWidth, s->viewportWidth());
3745 env->SetIntField(obj, gWebViewCoreFields.m_viewportHeight, s->viewportHeight());
3746 env->SetIntField(obj, gWebViewCoreFields.m_viewportInitialScale, s->viewportInitialScale());
3747 env->SetIntField(obj, gWebViewCoreFields.m_viewportMinimumScale, s->viewportMinimumScale());
3748 env->SetIntField(obj, gWebViewCoreFields.m_viewportMaximumScale, s->viewportMaximumScale());
3749 env->SetBooleanField(obj, gWebViewCoreFields.m_viewportUserScalable, s->viewportUserScalable());
3750 env->SetIntField(obj, gWebViewCoreFields.m_viewportDensityDpi, s->viewportTargetDensityDpi());
3754 static void SetBackgroundColor(JNIEnv *env, jobject obj, jint color)
3756 #ifdef ANDROID_INSTRUMENT
3757 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3759 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3760 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3762 viewImpl->setBackgroundColor((SkColor) color);
3765 static void DumpDomTree(JNIEnv *env, jobject obj, jboolean useFile)
3767 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3768 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3770 viewImpl->dumpDomTree(useFile);
3773 static void DumpRenderTree(JNIEnv *env, jobject obj, jboolean useFile)
3775 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3776 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3778 viewImpl->dumpRenderTree(useFile);
3781 static void DumpNavTree(JNIEnv *env, jobject obj)
3783 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3784 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3786 viewImpl->dumpNavTree();
3789 static void DumpV8Counters(JNIEnv*, jobject)
3792 #ifdef ANDROID_INSTRUMENT
3793 V8Counters::dumpCounters();
3798 static void SetJsFlags(JNIEnv *env, jobject obj, jstring flags)
3801 WTF::String flagsString = jstringToWtfString(env, flags);
3802 WTF::CString utf8String = flagsString.utf8();
3803 WebCore::ScriptController::setFlags(utf8String.data(), utf8String.length());
3808 // Called from the Java side to set a new quota for the origin or new appcache
3809 // max size in response to a notification that the original quota was exceeded or
3810 // that the appcache has reached its maximum size.
3811 static void SetNewStorageLimit(JNIEnv* env, jobject obj, jlong quota) {
3812 #if ENABLE(DATABASE) || ENABLE(OFFLINE_WEB_APPLICATIONS)
3813 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3814 Frame* frame = viewImpl->mainFrame();
3816 // The main thread is blocked awaiting this response, so now we can wake it
3818 ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client());
3819 chromeC->wakeUpMainThreadWithNewQuota(quota);
3823 // Called from Java to provide a Geolocation permission state for the specified origin.
3824 static void GeolocationPermissionsProvide(JNIEnv* env, jobject obj, jstring origin, jboolean allow, jboolean remember) {
3825 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3826 Frame* frame = viewImpl->mainFrame();
3828 ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client());
3829 chromeClient->provideGeolocationPermissions(jstringToWtfString(env, origin), allow, remember);
3832 static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jstring scheme) {
3833 #ifdef ANDROID_INSTRUMENT
3834 TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3836 WebCore::SchemeRegistry::registerURLSchemeAsLocal(jstringToWtfString(env, scheme));
3839 static bool FocusBoundsChanged(JNIEnv* env, jobject obj)
3841 return GET_NATIVE_VIEW(env, obj)->focusBoundsChanged();
3844 static void Pause(JNIEnv* env, jobject obj)
3846 // This is called for the foreground tab when the browser is put to the
3847 // background (and also for any tab when it is put to the background of the
3848 // browser). The browser can only be killed by the system when it is in the
3849 // background, so saving the Geolocation permission state now ensures that
3850 // is maintained when the browser is killed.
3851 ChromeClient* chromeClient = GET_NATIVE_VIEW(env, obj)->mainFrame()->page()->chrome()->client();
3852 ChromeClientAndroid* chromeClientAndroid = static_cast<ChromeClientAndroid*>(chromeClient);
3853 chromeClientAndroid->storeGeolocationPermissions();
3855 Frame* mainFrame = GET_NATIVE_VIEW(env, obj)->mainFrame();
3856 for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) {
3857 Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
3859 geolocation->suspend();
3862 GET_NATIVE_VIEW(env, obj)->deviceMotionAndOrientationManager()->maybeSuspendClients();
3865 SkANP::InitEvent(&event, kLifecycle_ANPEventType);
3866 event.data.lifecycle.action = kPause_ANPLifecycleAction;
3867 GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
3869 GET_NATIVE_VIEW(env, obj)->setIsPaused(true);
3872 static void Resume(JNIEnv* env, jobject obj)
3874 Frame* mainFrame = GET_NATIVE_VIEW(env, obj)->mainFrame();
3875 for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) {
3876 Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
3878 geolocation->resume();
3881 GET_NATIVE_VIEW(env, obj)->deviceMotionAndOrientationManager()->maybeResumeClients();
3884 SkANP::InitEvent(&event, kLifecycle_ANPEventType);
3885 event.data.lifecycle.action = kResume_ANPLifecycleAction;
3886 GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
3888 GET_NATIVE_VIEW(env, obj)->setIsPaused(false);
3891 static void FreeMemory(JNIEnv* env, jobject obj)
3894 SkANP::InitEvent(&event, kLifecycle_ANPEventType);
3895 event.data.lifecycle.action = kFreeMemory_ANPLifecycleAction;
3896 GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
3899 static void ProvideVisitedHistory(JNIEnv *env, jobject obj, jobject hist)
3901 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3902 LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3904 jobjectArray array = static_cast<jobjectArray>(hist);
3906 jsize len = env->GetArrayLength(array);
3907 for (jsize i = 0; i < len; i++) {
3908 jstring item = static_cast<jstring>(env->GetObjectArrayElement(array, i));
3909 const UChar* str = static_cast<const UChar*>(env->GetStringChars(item, NULL));
3910 jsize len = env->GetStringLength(item);
3911 viewImpl->addVisitedLink(str, len);
3912 env->ReleaseStringChars(item, str);
3913 env->DeleteLocalRef(item);
3917 // Notification from the UI thread that the plugin's full-screen surface has been discarded
3918 static void FullScreenPluginHidden(JNIEnv* env, jobject obj, jint npp)
3920 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3921 PluginWidgetAndroid* plugin = viewImpl->getPluginWidget((NPP)npp);
3923 plugin->exitFullScreen(false);
3926 static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj)
3929 GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B);
3930 return WebCore::IntRect(L, T, R - L, B - T);
3933 static bool ValidNodeAndBounds(JNIEnv *env, jobject obj, int frame, int node,
3936 IntRect nativeRect = jrect_to_webrect(env, rect);
3937 return GET_NATIVE_VIEW(env, obj)->validNodeAndBounds(
3938 reinterpret_cast<Frame*>(frame),
3939 reinterpret_cast<Node*>(node), nativeRect);
3942 static jobject GetTouchHighlightRects(JNIEnv* env, jobject obj, jint x, jint y, jint slop)
3944 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3947 Vector<IntRect> rects = viewImpl->getTouchHighlightRects(x, y, slop);
3948 if (rects.isEmpty())
3951 jclass arrayClass = env->FindClass("java/util/ArrayList");
3952 LOG_ASSERT(arrayClass, "Could not find java/util/ArrayList");
3953 jmethodID init = env->GetMethodID(arrayClass, "<init>", "(I)V");
3954 LOG_ASSERT(init, "Could not find constructor for ArrayList");
3955 jobject array = env->NewObject(arrayClass, init, rects.size());
3956 LOG_ASSERT(array, "Could not create a new ArrayList");
3957 jmethodID add = env->GetMethodID(arrayClass, "add", "(Ljava/lang/Object;)Z");
3958 LOG_ASSERT(add, "Could not find add method on ArrayList");
3959 jclass rectClass = env->FindClass("android/graphics/Rect");
3960 LOG_ASSERT(rectClass, "Could not find android/graphics/Rect");
3961 jmethodID rectinit = env->GetMethodID(rectClass, "<init>", "(IIII)V");
3962 LOG_ASSERT(rectinit, "Could not find init method on Rect");
3964 for (size_t i = 0; i < rects.size(); i++) {
3965 jobject rect = env->NewObject(rectClass, rectinit, rects[i].x(),
3966 rects[i].y(), rects[i].right(), rects[i].bottom());
3968 env->CallBooleanMethod(array, add, rect);
3969 env->DeleteLocalRef(rect);
3973 env->DeleteLocalRef(rectClass);
3974 env->DeleteLocalRef(arrayClass);
3978 static void AutoFillForm(JNIEnv* env, jobject obj, jint queryId)
3980 #if ENABLE(WEB_AUTOFILL)
3981 WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3985 WebCore::Frame* frame = viewImpl->mainFrame();
3987 EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(frame->page()->editorClient());
3988 WebAutoFill* autoFill = editorC->getAutoFill();
3989 autoFill->fillFormFields(queryId);
3994 // ----------------------------------------------------------------------------
3999 static JNINativeMethod gJavaWebViewCoreMethods[] = {
4000 { "nativeClearContent", "()V",
4001 (void*) ClearContent },
4002 { "nativeFocusBoundsChanged", "()Z",
4003 (void*) FocusBoundsChanged } ,
4004 { "nativeKey", "(IIIZZZZ)Z",
4006 { "nativeClick", "(II)V",
4008 { "nativeContentInvalidateAll", "()V",
4009 (void*) ContentInvalidateAll },
4010 { "nativeSendListBoxChoices", "([ZI)V",
4011 (void*) SendListBoxChoices },
4012 { "nativeSendListBoxChoice", "(I)V",
4013 (void*) SendListBoxChoice },
4014 { "nativeSetSize", "(IIIFIIIIZ)V",
4016 { "nativeSetScrollOffset", "(IIII)V",
4017 (void*) SetScrollOffset },
4018 { "nativeSetGlobalBounds", "(IIII)V",
4019 (void*) SetGlobalBounds },
4020 { "nativeSetSelection", "(II)V",
4021 (void*) SetSelection } ,
4022 { "nativeModifySelection", "(II)Ljava/lang/String;",
4023 (void*) ModifySelection },
4024 { "nativeDeleteSelection", "(III)V",
4025 (void*) DeleteSelection } ,
4026 { "nativeReplaceTextfieldText", "(IILjava/lang/String;III)V",
4027 (void*) ReplaceTextfieldText } ,
4028 { "nativeMoveFocus", "(II)V",
4029 (void*) MoveFocus },
4030 { "nativeMoveMouse", "(III)V",
4031 (void*) MoveMouse },
4032 { "nativeMoveMouseIfLatest", "(IIII)V",
4033 (void*) MoveMouseIfLatest },
4034 { "passToJs", "(ILjava/lang/String;IIZZZZ)V",
4036 { "nativeScrollFocusedTextInput", "(FI)V",
4037 (void*) ScrollFocusedTextInput },
4038 { "nativeSetFocusControllerActive", "(Z)V",
4039 (void*) SetFocusControllerActive },
4040 { "nativeSaveDocumentState", "(I)V",
4041 (void*) SaveDocumentState },
4042 { "nativeFindAddress", "(Ljava/lang/String;Z)Ljava/lang/String;",
4043 (void*) FindAddress },
4044 { "nativeHandleTouchEvent", "(I[I[III)Z",
4045 (void*) HandleTouchEvent },
4046 { "nativeTouchUp", "(IIIII)V",
4048 { "nativeRetrieveHref", "(II)Ljava/lang/String;",
4049 (void*) RetrieveHref },
4050 { "nativeRetrieveAnchorText", "(II)Ljava/lang/String;",
4051 (void*) RetrieveAnchorText },
4052 { "nativeUpdateFrameCache", "()V",
4053 (void*) UpdateFrameCache },
4054 { "nativeGetContentMinPrefWidth", "()I",
4055 (void*) GetContentMinPrefWidth },
4056 { "nativeUpdateLayers", "()I",
4057 (void*) UpdateLayers },
4058 { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)I",
4059 (void*) RecordContent },
4060 { "setViewportSettingsFromNative", "()V",
4061 (void*) SetViewportSettingsFromNative },
4062 { "nativeSplitContent", "(I)V",
4063 (void*) SplitContent },
4064 { "nativeSetBackgroundColor", "(I)V",
4065 (void*) SetBackgroundColor },
4066 { "nativeRegisterURLSchemeAsLocal", "(Ljava/lang/String;)V",
4067 (void*) RegisterURLSchemeAsLocal },
4068 { "nativeDumpDomTree", "(Z)V",
4069 (void*) DumpDomTree },
4070 { "nativeDumpRenderTree", "(Z)V",
4071 (void*) DumpRenderTree },
4072 { "nativeDumpNavTree", "()V",
4073 (void*) DumpNavTree },
4074 { "nativeDumpV8Counters", "()V",
4075 (void*) DumpV8Counters },
4076 { "nativeSetNewStorageLimit", "(J)V",
4077 (void*) SetNewStorageLimit },
4078 { "nativeGeolocationPermissionsProvide", "(Ljava/lang/String;ZZ)V",
4079 (void*) GeolocationPermissionsProvide },
4080 { "nativePause", "()V", (void*) Pause },
4081 { "nativeResume", "()V", (void*) Resume },
4082 { "nativeFreeMemory", "()V", (void*) FreeMemory },
4083 { "nativeSetJsFlags", "(Ljava/lang/String;)V", (void*) SetJsFlags },
4084 { "nativeRequestLabel", "(II)Ljava/lang/String;",
4085 (void*) RequestLabel },
4086 { "nativeRevealSelection", "()V", (void*) RevealSelection },
4087 { "nativeUpdateFrameCacheIfLoading", "()V",
4088 (void*) UpdateFrameCacheIfLoading },
4089 { "nativeProvideVisitedHistory", "([Ljava/lang/String;)V",
4090 (void*) ProvideVisitedHistory },
4091 { "nativeFullScreenPluginHidden", "(I)V",
4092 (void*) FullScreenPluginHidden },
4093 { "nativeValidNodeAndBounds", "(IILandroid/graphics/Rect;)Z",
4094 (void*) ValidNodeAndBounds },
4095 { "nativeGetTouchHighlightRects", "(III)Ljava/util/ArrayList;",
4096 (void*) GetTouchHighlightRects },
4097 { "nativeAutoFillForm", "(I)V",
4098 (void*) AutoFillForm },
4101 int registerWebViewCore(JNIEnv* env)
4103 jclass widget = env->FindClass("android/webkit/WebViewCore");
4105 "Unable to find class android/webkit/WebViewCore");
4106 gWebViewCoreFields.m_nativeClass = env->GetFieldID(widget, "mNativeClass",
4108 LOG_ASSERT(gWebViewCoreFields.m_nativeClass,
4109 "Unable to find android/webkit/WebViewCore.mNativeClass");
4110 gWebViewCoreFields.m_viewportWidth = env->GetFieldID(widget,
4111 "mViewportWidth", "I");
4112 LOG_ASSERT(gWebViewCoreFields.m_viewportWidth,
4113 "Unable to find android/webkit/WebViewCore.mViewportWidth");
4114 gWebViewCoreFields.m_viewportHeight = env->GetFieldID(widget,
4115 "mViewportHeight", "I");
4116 LOG_ASSERT(gWebViewCoreFields.m_viewportHeight,
4117 "Unable to find android/webkit/WebViewCore.mViewportHeight");
4118 gWebViewCoreFields.m_viewportInitialScale = env->GetFieldID(widget,
4119 "mViewportInitialScale", "I");
4120 LOG_ASSERT(gWebViewCoreFields.m_viewportInitialScale,
4121 "Unable to find android/webkit/WebViewCore.mViewportInitialScale");
4122 gWebViewCoreFields.m_viewportMinimumScale = env->GetFieldID(widget,
4123 "mViewportMinimumScale", "I");
4124 LOG_ASSERT(gWebViewCoreFields.m_viewportMinimumScale,
4125 "Unable to find android/webkit/WebViewCore.mViewportMinimumScale");
4126 gWebViewCoreFields.m_viewportMaximumScale = env->GetFieldID(widget,
4127 "mViewportMaximumScale", "I");
4128 LOG_ASSERT(gWebViewCoreFields.m_viewportMaximumScale,
4129 "Unable to find android/webkit/WebViewCore.mViewportMaximumScale");
4130 gWebViewCoreFields.m_viewportUserScalable = env->GetFieldID(widget,
4131 "mViewportUserScalable", "Z");
4132 LOG_ASSERT(gWebViewCoreFields.m_viewportUserScalable,
4133 "Unable to find android/webkit/WebViewCore.mViewportUserScalable");
4134 gWebViewCoreFields.m_viewportDensityDpi = env->GetFieldID(widget,
4135 "mViewportDensityDpi", "I");
4136 LOG_ASSERT(gWebViewCoreFields.m_viewportDensityDpi,
4137 "Unable to find android/webkit/WebViewCore.mViewportDensityDpi");
4138 gWebViewCoreFields.m_webView = env->GetFieldID(widget,
4139 "mWebView", "Landroid/webkit/WebView;");
4140 LOG_ASSERT(gWebViewCoreFields.m_webView,
4141 "Unable to find android/webkit/WebViewCore.mWebView");
4142 gWebViewCoreFields.m_drawIsPaused = env->GetFieldID(widget,
4143 "mDrawIsPaused", "Z");
4144 LOG_ASSERT(gWebViewCoreFields.m_drawIsPaused,
4145 "Unable to find android/webkit/WebViewCore.mDrawIsPaused");
4147 gWebViewCoreStaticMethods.m_isSupportedMediaMimeType =
4148 env->GetStaticMethodID(widget, "isSupportedMediaMimeType", "(Ljava/lang/String;)Z");
4149 LOG_FATAL_IF(gWebViewCoreStaticMethods.m_isSupportedMediaMimeType == NULL,
4150 "Could not find static method isSupportedMediaMimeType from WebViewCore");
4152 env->DeleteLocalRef(widget);
4154 return jniRegisterNativeMethods(env, "android/webkit/WebViewCore",
4155 gJavaWebViewCoreMethods, NELEM(gJavaWebViewCoreMethods));
4158 } /* namespace android */