1 /* 2 * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.webkit; 27 28 import com.sun.glass.utils.NativeLibLoader; 29 import com.sun.webkit.event.WCFocusEvent; 30 import com.sun.webkit.event.WCInputMethodEvent; 31 import com.sun.webkit.event.WCKeyEvent; 32 import com.sun.webkit.event.WCMouseEvent; 33 import com.sun.webkit.event.WCMouseWheelEvent; 34 import com.sun.webkit.graphics.*; 35 import com.sun.webkit.network.CookieManager; 36 import static com.sun.webkit.network.URLs.newURL; 37 import java.net.CookieHandler; 38 import java.net.MalformedURLException; 39 import java.net.URL; 40 import java.nio.ByteBuffer; 41 import java.nio.ByteOrder; 42 import java.security.AccessControlContext; 43 import java.security.AccessController; 44 import java.security.PrivilegedAction; 45 import java.util.ArrayList; 46 import java.util.HashMap; 47 import java.util.HashSet; 48 import java.util.Iterator; 49 import java.util.LinkedList; 50 import java.util.List; 51 import java.util.Map; 52 import java.util.Queue; 53 import java.util.Set; 54 import java.util.concurrent.CountDownLatch; 55 import java.util.concurrent.ExecutionException; 56 import java.util.concurrent.FutureTask; 57 import java.util.concurrent.atomic.AtomicReference; 58 import java.util.concurrent.locks.ReentrantLock; 59 import java.util.logging.Level; 60 import java.util.logging.Logger; 61 import netscape.javascript.JSException; 62 import org.w3c.dom.Document; 63 import org.w3c.dom.Element; 64 65 /** 66 * This class provides two-side interaction between native webkit core and 67 * number of clients representing different subsystems of the WebPane component 68 * such as 69 * <ul> 70 * <li>webpage rendering({@link WebPageClient}) 71 * <li>creating/disposing web frames ({@link WebFrameClient}) 72 * <li>creating new windows, alert dialogues ... ({@link UIClient}) 73 * <li>handling menus {@link MenuClient} 74 * <li>supporting policy checking {@link PolicyClient} 75 * </ul> 76 */ 77 78 public final class WebPage { 79 private final static Logger log = Logger.getLogger(WebPage.class.getName()); 80 private final static Logger paintLog = Logger.getLogger(WebPage.class.getName() + ".paint"); 81 82 private static final int MAX_FRAME_QUEUE_SIZE = 10; 83 84 // Native WebPage* pointer 85 private long pPage = 0; 86 87 // A flag to distinguish whether the web page hasn't been created 88 // yet or had been already disposed - in both cases pPage is 0 89 private boolean isDisposed = false; 90 91 private int width, height; 92 93 private int fontSmoothingType; 94 95 private final WCFrameView hostWindow; 96 97 // List of created frames 98 private final Set<Long> frames = new HashSet<Long>(); 99 100 // The access control context associated with this object 101 private final AccessControlContext accessControlContext; 102 103 // Maps load request identifiers to URLs 104 private final Map<Integer, String> requestURLs = 105 new HashMap<Integer, String>(); 106 107 // There may be several RESOURCE_STARTED events for a resource, 108 // so this map is used to convert them to RESOURCE_REDIRECTED 109 private final Set<Integer> requestStarted = new HashSet<Integer>(); 110 111 // PAGE_LOCK is used to synchronize the following operations b/w Event & Main threads: 112 // - rendering of the page (Main thread) 113 // - native calls & other manipulations on the page (Event & Main threads) 114 // - timer invocations (Event thread) 115 private static final ReentrantLock PAGE_LOCK = new ReentrantLock(); 116 117 // The queue of render frames awaiting rendering. 118 // Access to this object is synchronized on its monitor. 119 // Accessed on: Event thread and Main thread. 120 private final Queue<RenderFrame> frameQueue = new LinkedList<RenderFrame>(); 121 122 // The current frame being generated. 123 // Accessed on: Event thread only. 124 private RenderFrame currentFrame = new RenderFrame(); 125 126 // An ID of the current updateContent cycle associated with an updateContent call. 127 private int updateContentCycleID; 128 129 static { 130 AccessController.doPrivileged((PrivilegedAction<Void>) () -> { 131 NativeLibLoader.loadLibrary("jfxwebkit"); 132 log.finer("jfxwebkit loaded"); 133 134 if (CookieHandler.getDefault() == null) { 135 boolean setDefault = Boolean.valueOf(System.getProperty( 136 "com.sun.webkit.setDefaultCookieHandler", 137 "true")); 138 if (setDefault) { 139 CookieHandler.setDefault(new CookieManager()); 140 } 141 } 142 143 final boolean useJIT = Boolean.valueOf(System.getProperty( 144 "com.sun.webkit.useJIT", "true")); 145 final boolean useDFGJIT = Boolean.valueOf(System.getProperty( 146 "com.sun.webkit.useDFGJIT", "true")); 147 148 // Initialize WTF, WebCore and JavaScriptCore. 149 twkInitWebCore(useJIT, useDFGJIT); 150 return null; 151 }); 152 153 } 154 155 private static boolean firstWebPageCreated = false; 156 157 private static void collectJSCGarbages() { 158 Invoker.getInvoker().checkEventThread(); 159 // Add dummy object to get notification as soon as it is collected 160 // by the JVM GC. 161 Disposer.addRecord(new Object(), WebPage::collectJSCGarbages); 162 // Invoke JavaScriptCore GC. 163 twkDoJSCGarbageCollection(); 164 } 165 166 public WebPage(WebPageClient pageClient, 167 UIClient uiClient, 168 PolicyClient policyClient, 169 InspectorClient inspectorClient, 170 ThemeClient themeClient, 171 boolean editable) 172 { 173 Invoker.getInvoker().checkEventThread(); 174 175 this.pageClient = pageClient; 176 this.uiClient = uiClient; 177 this.policyClient = policyClient; 178 this.inspectorClient = inspectorClient; 179 if (themeClient != null) { 180 this.renderTheme = themeClient.createRenderTheme(); 181 this.scrollbarTheme = themeClient.createScrollBarTheme(); 182 } else { 183 this.renderTheme = null; 184 this.scrollbarTheme = null; 185 } 186 187 accessControlContext = AccessController.getContext(); 188 189 hostWindow = new WCFrameView(this); 190 pPage = twkCreatePage(editable); 191 192 twkInit(pPage, false, WCGraphicsManager.getGraphicsManager().getDevicePixelScale()); 193 194 if (pageClient != null && pageClient.isBackBufferSupported()) { 195 backbuffer = pageClient.createBackBuffer(); 196 backbuffer.ref(); 197 } 198 199 if (!firstWebPageCreated) { 200 // Add dummy object to get notification as soon as it is collected 201 // by the JVM GC. 202 Disposer.addRecord(new Object(), WebPage::collectJSCGarbages); 203 firstWebPageCreated = true; 204 } 205 } 206 207 long getPage() { 208 return pPage; 209 } 210 211 // Called from the native code 212 private WCWidget getHostWindow() { 213 return hostWindow; 214 } 215 216 /** 217 * Returns the access control context associated with this object. 218 * May be called on any thread. 219 * @return the access control context associated with this object 220 */ 221 public AccessControlContext getAccessControlContext() { 222 return accessControlContext; 223 } 224 225 static boolean lockPage() { 226 return Invoker.getInvoker().lock(PAGE_LOCK); 227 } 228 229 static boolean unlockPage() { 230 return Invoker.getInvoker().unlock(PAGE_LOCK); 231 } 232 233 // ************************************************************************* 234 // Backbuffer support 235 // ************************************************************************* 236 237 private WCPageBackBuffer backbuffer; 238 private List<WCRectangle> dirtyRects = new LinkedList<WCRectangle>(); 239 240 private void addDirtyRect(WCRectangle toPaint) { 241 if (toPaint.getWidth() <= 0 || toPaint.getHeight() <= 0) { 242 return; 243 } 244 for (Iterator<WCRectangle> it = dirtyRects.iterator(); it.hasNext();) { 245 WCRectangle rect = it.next(); 246 // if already covered 247 if (rect.contains(toPaint)) { 248 return; 249 } 250 // if covers an existing one 251 if (toPaint.contains(rect)) { 252 it.remove(); 253 continue; 254 } 255 WCRectangle u = rect.createUnion(toPaint); 256 // if squre of union is less than summary of squares 257 if (u.getIntWidth() * u.getIntHeight() < 258 rect.getIntWidth() * rect.getIntHeight() + 259 toPaint.getIntWidth() * toPaint.getIntHeight()) 260 { 261 it.remove(); 262 toPaint = u; // replace both the rects with their union 263 continue; 264 } 265 } 266 dirtyRects.add(toPaint); 267 } 268 269 public boolean isDirty() { 270 lockPage(); 271 try { 272 return !dirtyRects.isEmpty(); 273 } finally { 274 unlockPage(); 275 } 276 } 277 278 private void updateDirty(WCRectangle clip) { 279 if (paintLog.isLoggable(Level.FINEST)) { 280 paintLog.log(Level.FINEST, "Entering, " 281 + "dirtyRects: {0}, currentFrame: {1}", 282 new Object[] {dirtyRects, currentFrame}); 283 } 284 285 if (isDisposed || width <= 0 || height <= 0) { 286 // If there're any dirty rects left, they are invalid. 287 // Clear the list so that the platform doesn't consider 288 // the page dirty. 289 dirtyRects.clear(); 290 return; 291 } 292 if (clip == null) { 293 clip = new WCRectangle(0, 0, width, height); 294 } 295 List<WCRectangle> oldDirtyRects = dirtyRects; 296 dirtyRects = new LinkedList<WCRectangle>(); 297 twkPrePaint(getPage()); 298 while (!oldDirtyRects.isEmpty()) { 299 WCRectangle r = oldDirtyRects.remove(0).intersection(clip); 300 if (r.getWidth() <= 0 || r.getHeight() <= 0) { 301 continue; 302 } 303 paintLog.log(Level.FINEST, "Updating: {0}", r); 304 WCRenderQueue rq = WCGraphicsManager.getGraphicsManager() 305 .createRenderQueue(r, true); 306 twkUpdateContent(getPage(), rq, r.getIntX() - 1, r.getIntY() - 1, 307 r.getIntWidth() + 2, r.getIntHeight() + 2); 308 currentFrame.addRenderQueue(rq); 309 } 310 { 311 WCRenderQueue rq = WCGraphicsManager.getGraphicsManager() 312 .createRenderQueue(clip, false); 313 twkPostPaint(getPage(), rq, 314 clip.getIntX(), clip.getIntY(), 315 clip.getIntWidth(), clip.getIntHeight()); 316 currentFrame.addRenderQueue(rq); 317 } 318 319 if (paintLog.isLoggable(Level.FINEST)) { 320 paintLog.log(Level.FINEST, "Dirty rects processed, " 321 + "dirtyRects: {0}, currentFrame: {1}", 322 new Object[] {dirtyRects, currentFrame}); 323 } 324 325 if (currentFrame.getRQList().size() > 0) { 326 synchronized (frameQueue) { 327 paintLog.log(Level.FINEST, "About to update frame queue, " 328 + "frameQueue: {0}", frameQueue); 329 330 Iterator<RenderFrame> it = frameQueue.iterator(); 331 while (it.hasNext()) { 332 RenderFrame frame = it.next(); 333 for (WCRenderQueue rq : currentFrame.getRQList()) { 334 WCRectangle rqRect = rq.getClip(); 335 if (rq.isOpaque() 336 && rqRect.contains(frame.getEnclosingRect())) 337 { 338 paintLog.log(Level.FINEST, "Dropping: {0}", frame); 339 frame.drop(); 340 it.remove(); 341 break; 342 } 343 } 344 } 345 346 frameQueue.add(currentFrame); 347 currentFrame = new RenderFrame(); 348 349 if (frameQueue.size() > MAX_FRAME_QUEUE_SIZE) { 350 paintLog.log(Level.FINEST, "Frame queue exceeded maximum " 351 + "size, clearing and requesting full repaint"); 352 dropRenderFrames(); 353 repaintAll(); 354 } 355 356 paintLog.log(Level.FINEST, "Frame queue updated, " 357 + "frameQueue: {0}", frameQueue); 358 } 359 } 360 361 if (paintLog.isLoggable(Level.FINEST)) { 362 paintLog.log(Level.FINEST, 363 "Exiting, dirtyRects: {0}, currentFrame: {1}", 364 new Object[] {dirtyRects, currentFrame}); 365 } 366 } 367 368 private void scroll(int x, int y, int w, int h, int dx, int dy) { 369 if (paintLog.isLoggable(Level.FINEST)) { 370 paintLog.finest("rect=[" + x + ", " + y + " " + w + "x" + h + 371 "] delta=[" + dx + ", " + dy + "]"); 372 } 373 dx += currentFrame.scrollDx; 374 dy += currentFrame.scrollDy; 375 376 if (Math.abs(dx) < w && Math.abs(dy) < h) { 377 int cx = (dx >= 0) ? x : x - dx; 378 int cy = (dy >= 0) ? y : y - dy; 379 int cw = (dx == 0) ? w : w - Math.abs(dx); 380 int ch = (dy == 0) ? h : h - Math.abs(dy); 381 382 WCRenderQueue rq = WCGraphicsManager.getGraphicsManager() 383 .createRenderQueue( 384 new WCRectangle(0, 0, width, height), false); 385 ByteBuffer buffer = ByteBuffer.allocate(32) 386 .order(ByteOrder.nativeOrder()) 387 .putInt(GraphicsDecoder.COPYREGION) 388 .putInt(backbuffer.getID()) 389 .putInt(cx).putInt(cy).putInt(cw).putInt(ch) 390 .putInt(dx).putInt(dy); 391 buffer.flip(); 392 rq.addBuffer(buffer); 393 // Ignore previous COPYREGION 394 currentFrame.drop(); 395 currentFrame.addRenderQueue(rq); 396 currentFrame.scrollDx = dx; 397 currentFrame.scrollDy = dy; 398 // Now we have to translate "old" dirty rects that fit to the frame's 399 // content as the content is already scrolled at the moment by webkit. 400 if (!dirtyRects.isEmpty()) { 401 WCRectangle scrollRect = new WCRectangle(x, y, w, h); 402 for (WCRectangle r: dirtyRects) { 403 if (scrollRect.contains(r)) { 404 if (paintLog.isLoggable(Level.FINEST)) { 405 paintLog.log(Level.FINEST, "translating old dirty rect by the delta: " + r); 406 } 407 r.translate(dx, dy); 408 } 409 } 410 } 411 } 412 413 // Add the dirty (not copied) rects 414 addDirtyRect(new WCRectangle(x, dy >= 0 ? y : y + h + dy, 415 w, Math.abs(dy))); 416 addDirtyRect(new WCRectangle(dx >= 0 ? x : x + w + dx, y, 417 Math.abs(dx), h - Math.abs(dy))); 418 } 419 420 // Instances of this class may not be accessed and modified concurrently 421 // by multiple threads 422 private static final class RenderFrame { 423 private final List<WCRenderQueue> rqList = 424 new LinkedList<WCRenderQueue>(); 425 private int scrollDx, scrollDy; 426 private final WCRectangle enclosingRect = new WCRectangle(); 427 428 // Called on: Event thread only 429 private void addRenderQueue(WCRenderQueue rq) { 430 if (rq.isEmpty()) { 431 return; 432 } 433 rqList.add(rq); 434 WCRectangle rqRect = rq.getClip(); 435 if (enclosingRect.isEmpty()) { 436 enclosingRect.setFrame(rqRect.getX(), rqRect.getY(), 437 rqRect.getWidth(), rqRect.getHeight()); 438 } else if (rqRect.isEmpty()) { 439 // do nothing 440 } else { 441 WCRectangle.union(enclosingRect, rqRect, enclosingRect); 442 } 443 } 444 445 // Called on: Event thread and Main thread 446 private List<WCRenderQueue> getRQList() { 447 return rqList; 448 } 449 450 // Called on: Event thread only 451 private WCRectangle getEnclosingRect() { 452 return enclosingRect; 453 } 454 455 // Called on: Event thread only 456 private void drop() { 457 for (WCRenderQueue rq : rqList) { 458 rq.dispose(); 459 } 460 rqList.clear(); 461 enclosingRect.setFrame(0, 0, 0, 0); 462 scrollDx = 0; 463 scrollDy = 0; 464 } 465 466 @Override 467 public String toString() { 468 return "RenderFrame{" 469 + "rqList=" + rqList + ", " 470 + "enclosingRect=" + enclosingRect 471 + "}"; 472 } 473 } 474 475 // ************************************************************************* 476 // Callback API 477 // ************************************************************************* 478 479 private final WebPageClient pageClient; 480 private final UIClient uiClient; 481 private final PolicyClient policyClient; 482 private InputMethodClient imClient; 483 private final List<LoadListenerClient> loadListenerClients = 484 new LinkedList<LoadListenerClient>(); 485 private final InspectorClient inspectorClient; 486 private final RenderTheme renderTheme; 487 private final ScrollBarTheme scrollbarTheme; 488 489 public WebPageClient getPageClient() { 490 return pageClient; 491 } 492 493 public void setInputMethodClient(InputMethodClient imClient) { 494 this.imClient = imClient; 495 } 496 497 public void setInputMethodState(boolean state) { 498 if (imClient != null) { 499 // A web page containing multiple clients is a single client from Java 500 // Input Method Framework's viewpoint. We need to control activation and 501 // deactivation for each text field/area here. Also, we need to control 502 // enabling and disabling input methods here so that input method events 503 // won't get delivered to wrong places (e.g., background). 504 imClient.activateInputMethods(state); 505 } 506 } 507 508 public void addLoadListenerClient(LoadListenerClient l) { 509 if (!loadListenerClients.contains(l)) { 510 loadListenerClients.add(l); 511 } 512 } 513 514 private RenderTheme getRenderTheme() { 515 return renderTheme; 516 } 517 518 private static RenderTheme fwkGetDefaultRenderTheme() { 519 return ThemeClient.getDefaultRenderTheme(); 520 } 521 522 private ScrollBarTheme getScrollBarTheme() { 523 return scrollbarTheme; 524 } 525 526 // ************************************************************************* 527 // UI stuff API 528 // ************************************************************************* 529 530 public void setBounds(int x, int y, int w, int h) { 531 lockPage(); 532 try { 533 log.log(Level.FINE, "setBounds: " + x + " " + y + " " + w + " " + h); 534 if (isDisposed) { 535 log.log(Level.FINE, "setBounds() request for a disposed web page."); 536 return; 537 } 538 width = w; 539 height = h; 540 twkSetBounds(getPage(), 0, 0, w, h); 541 // In response to the above call, WebKit will issue many 542 // repaint requests, one of which will be meant to invalidate 543 // the entire visible area. However, if the current scroll 544 // offset is non-zero, that repaint request will contain 545 // incorrect coordinates. 546 // As of time of writing this, this problem exists in both 547 // MiniBrowser and WinLauncher. 548 // MiniBrowser is based on WebKit2, and WebKit2 workarounds 549 // this problem by calling m_drawingArea->setNeedsDisplay() 550 // for the entire visible area from within the WebKit2's 551 // WebPage::setSize(). 552 // WinLauncher workarounds this problem by setting the main 553 // window class style to CS_HREDRAW | CS_VREDRAW and calling 554 // MoveWindow() with bRepaint = TRUE when resizing the web 555 // view. 556 // We workaround this problem by invalidating the entire 557 // visible area here. 558 repaintAll(); 559 560 } finally { 561 unlockPage(); 562 } 563 } 564 565 public void setOpaque(long frameID, boolean isOpaque) { 566 lockPage(); 567 try { 568 log.log(Level.FINE, "setOpaque: " + isOpaque); 569 if (isDisposed) { 570 log.log(Level.FINE, "setOpaque() request for a disposed web page."); 571 return; 572 } 573 if (!frames.contains(frameID)) { 574 return; 575 } 576 twkSetTransparent(frameID, !isOpaque); 577 578 } finally { 579 unlockPage(); 580 } 581 } 582 583 public void setBackgroundColor(long frameID, int backgroundColor) { 584 lockPage(); 585 try { 586 log.log(Level.FINE, "setBackgroundColor: " + backgroundColor); 587 if (isDisposed) { 588 log.log(Level.FINE, "setBackgroundColor() request for a disposed web page."); 589 return; 590 } 591 if (!frames.contains(frameID)) { 592 return; 593 } 594 twkSetBackgroundColor(frameID, backgroundColor); 595 596 } finally { 597 unlockPage(); 598 } 599 } 600 601 public void setBackgroundColor(int backgroundColor) { 602 lockPage(); 603 try { 604 log.log(Level.FINE, "setBackgroundColor: " + backgroundColor + 605 " for all frames"); 606 if (isDisposed) { 607 log.log(Level.FINE, "setBackgroundColor() request for a disposed web page."); 608 return; 609 } 610 611 for (long frameID: frames) { 612 twkSetBackgroundColor(frameID, backgroundColor); 613 } 614 615 } finally { 616 unlockPage(); 617 } 618 } 619 620 /* 621 * Executed on the Event Thread. 622 */ 623 public void updateContent(WCRectangle toPaint) { 624 lockPage(); 625 try { 626 ++updateContentCycleID; 627 628 paintLog.log(Level.FINEST, "toPaint: {0}", toPaint); 629 if (isDisposed) { 630 paintLog.fine("updateContent() request for a disposed web page."); 631 return; 632 } 633 updateDirty(toPaint); 634 635 } finally { 636 unlockPage(); 637 } 638 } 639 640 public int getUpdateContentCycleID() { 641 return updateContentCycleID; 642 } 643 644 public boolean isRepaintPending() { 645 lockPage(); 646 try { 647 synchronized (frameQueue) { 648 return !frameQueue.isEmpty(); 649 } 650 } finally { 651 unlockPage(); 652 } 653 } 654 655 /* 656 * Executed on printing thread. 657 */ 658 public void print(WCGraphicsContext gc, 659 final int x, final int y, final int w, final int h) 660 { 661 lockPage(); 662 try { 663 final WCRenderQueue rq = WCGraphicsManager.getGraphicsManager(). 664 createRenderQueue(new WCRectangle(x, y, w, h), true); 665 FutureTask<Void> f = new FutureTask<Void>(() -> { 666 twkUpdateContent(getPage(), rq, x, y, w, h); 667 }, null); 668 Invoker.getInvoker().invokeOnEventThread(f); 669 670 try { 671 // block until job is complete 672 f.get(); 673 } catch (ExecutionException ex) { 674 throw new AssertionError(ex); 675 } catch (InterruptedException ex) { 676 // ignore; recovery is impossible 677 } 678 679 rq.decode(gc); 680 } finally { 681 unlockPage(); 682 } 683 } 684 685 /* 686 * Executed on the Render Thread. 687 */ 688 public void paint(WCGraphicsContext gc, int x, int y, int w, int h) { 689 lockPage(); 690 try { 691 if (pageClient != null && pageClient.isBackBufferSupported()) { 692 if (!backbuffer.validate(width, height)) { 693 // We need to repaint the whole page on the next turn 694 Invoker.getInvoker().invokeOnEventThread(() -> { 695 repaintAll(); 696 }); 697 return; 698 } 699 WCGraphicsContext bgc = backbuffer.createGraphics(); 700 try { 701 paint2GC(bgc); 702 bgc.flush(); 703 } finally { 704 backbuffer.disposeGraphics(bgc); 705 } 706 backbuffer.flush(gc, x, y, w, h); 707 } else { 708 paint2GC(gc); 709 } 710 } finally { 711 unlockPage(); 712 } 713 } 714 715 private void paint2GC(WCGraphicsContext gc) { 716 paintLog.finest("Entering"); 717 gc.setFontSmoothingType(this.fontSmoothingType); 718 719 List<RenderFrame> framesToRender; 720 synchronized (frameQueue) { 721 framesToRender = new ArrayList(frameQueue); 722 frameQueue.clear(); 723 } 724 725 paintLog.log(Level.FINEST, "Frames to render: {0}", framesToRender); 726 727 for (RenderFrame frame : framesToRender) { 728 paintLog.log(Level.FINEST, "Rendering: {0}", frame); 729 for (WCRenderQueue rq : frame.getRQList()) { 730 gc.saveState(); 731 if (rq.getClip() != null) { 732 gc.setClip(rq.getClip()); 733 } 734 rq.decode(gc); 735 gc.restoreState(); 736 } 737 } 738 paintLog.finest("Exiting"); 739 } 740 741 /* 742 * Executed on the Event Thread. 743 */ 744 public void dropRenderFrames() { 745 lockPage(); 746 try { 747 currentFrame.drop(); 748 synchronized (frameQueue) { 749 for (RenderFrame frame = frameQueue.poll(); frame != null; frame = frameQueue.poll()) { 750 frame.drop(); 751 } 752 } 753 } finally { 754 unlockPage(); 755 } 756 } 757 758 public void dispatchFocusEvent(WCFocusEvent fe) { 759 lockPage(); 760 try { 761 log.log(Level.FINEST, "dispatchFocusEvent: " + fe); 762 if (isDisposed) { 763 log.log(Level.FINE, "Focus event for a disposed web page."); 764 return; 765 } 766 twkProcessFocusEvent(getPage(), fe.getID(), fe.getDirection()); 767 768 } finally { 769 unlockPage(); 770 } 771 } 772 773 public boolean dispatchKeyEvent(WCKeyEvent ke) { 774 lockPage(); 775 try { 776 log.log(Level.FINEST, "dispatchKeyEvent: " + ke); 777 if (isDisposed) { 778 log.log(Level.FINE, "Key event for a disposed web page."); 779 return false; 780 } 781 if (WCKeyEvent.filterEvent(ke)) { 782 log.log(Level.FINEST, "filtered"); 783 return false; 784 } 785 return twkProcessKeyEvent(getPage(), ke.getType(), ke.getText(), 786 ke.getKeyIdentifier(), 787 ke.getWindowsVirtualKeyCode(), 788 ke.isShiftDown(), ke.isCtrlDown(), 789 ke.isAltDown(), ke.isMetaDown(), ke.getWhen() / 1000.0); 790 } finally { 791 unlockPage(); 792 } 793 } 794 795 public boolean dispatchMouseEvent(WCMouseEvent me) { 796 lockPage(); 797 try { 798 log.log(Level.FINEST, "dispatchMouseEvent: " + me.getX() + "," + me.getY()); 799 if (isDisposed) { 800 log.log(Level.FINE, "Mouse event for a disposed web page."); 801 return false; 802 } 803 804 return !isDragConfirmed() //When Webkit informes FX about drag start, it waits 805 //for system DnD loop and not intereasted in 806 //intermediate mouse events that can change text selection. 807 && twkProcessMouseEvent(getPage(), me.getID(), 808 me.getButton(), me.getClickCount(), 809 me.getX(), me.getY(), me.getScreenX(), me.getScreenY(), 810 me.isShiftDown(), me.isControlDown(), me.isAltDown(), me.isMetaDown(), me.isPopupTrigger(), 811 me.getWhen() / 1000.0); 812 } finally { 813 unlockPage(); 814 } 815 } 816 817 public boolean dispatchMouseWheelEvent(WCMouseWheelEvent me) { 818 lockPage(); 819 try { 820 log.log(Level.FINEST, "dispatchMouseWheelEvent: " + me); 821 if (isDisposed) { 822 log.log(Level.FINE, "MouseWheel event for a disposed web page."); 823 return false; 824 } 825 return twkProcessMouseWheelEvent(getPage(), 826 me.getX(), me.getY(), me.getScreenX(), me.getScreenY(), 827 me.getDeltaX(), me.getDeltaY(), 828 me.isShiftDown(), me.isControlDown(), me.isAltDown(), me.isMetaDown(), 829 me.getWhen() / 1000.0); 830 } finally { 831 unlockPage(); 832 } 833 } 834 835 public boolean dispatchInputMethodEvent(WCInputMethodEvent ie) { 836 lockPage(); 837 try { 838 log.log(Level.FINEST, "dispatchInputMethodEvent: " + ie); 839 if (isDisposed) { 840 log.log(Level.FINE, "InputMethod event for a disposed web page."); 841 return false; 842 } 843 switch (ie.getID()) { 844 case WCInputMethodEvent.INPUT_METHOD_TEXT_CHANGED: 845 return twkProcessInputTextChange(getPage(), 846 ie.getComposed(), ie.getCommitted(), 847 ie.getAttributes(), ie.getCaretPosition()); 848 849 case WCInputMethodEvent.CARET_POSITION_CHANGED: 850 return twkProcessCaretPositionChange(getPage(), 851 ie.getCaretPosition()); 852 } 853 return false; 854 855 } finally { 856 unlockPage(); 857 } 858 } 859 860 public final static int DND_DST_ENTER = 0; 861 public final static int DND_DST_OVER = 1; 862 public final static int DND_DST_CHANGE = 2; 863 public final static int DND_DST_EXIT = 3; 864 public final static int DND_DST_DROP = 4; 865 866 public final static int DND_SRC_ENTER = 100; 867 public final static int DND_SRC_OVER = 101; 868 public final static int DND_SRC_CHANGE = 102; 869 public final static int DND_SRC_EXIT = 103; 870 public final static int DND_SRC_DROP = 104; 871 872 public int dispatchDragOperation( 873 int commandId, 874 String[] mimeTypes, String[] values, 875 int x, int y, 876 int screenX, int screenY, 877 int dndActionId) 878 { 879 lockPage(); 880 try { 881 log.log(Level.FINEST, "dispatchDragOperation: " + x + "," + y 882 + " dndCommand:" + commandId 883 + " dndAction" + dndActionId); 884 if (isDisposed) { 885 log.log(Level.FINE, "DnD event for a disposed web page."); 886 return 0; 887 } 888 return twkProcessDrag(getPage(), 889 commandId, 890 mimeTypes, values, 891 x, y, 892 screenX, screenY, 893 dndActionId); 894 } finally { 895 unlockPage(); 896 } 897 } 898 899 public void confirmStartDrag() { 900 if (uiClient != null) 901 uiClient.confirmStartDrag(); 902 } 903 904 public boolean isDragConfirmed(){ 905 return (uiClient != null) 906 ? uiClient.isDragConfirmed() 907 : false; 908 } 909 910 // ************************************************************************* 911 // Input methods 912 // ************************************************************************* 913 914 public int[] getClientTextLocation(int index) { 915 lockPage(); 916 try { 917 if (isDisposed) { 918 log.log(Level.FINE, "getClientTextLocation() request for a disposed web page."); 919 return new int[] { 0, 0, 0, 0 }; 920 } 921 Invoker.getInvoker().checkEventThread(); 922 return twkGetTextLocation(getPage(), index); 923 924 } finally { 925 unlockPage(); 926 } 927 } 928 929 public int getClientLocationOffset(int x, int y) { 930 lockPage(); 931 try { 932 if (isDisposed) { 933 log.log(Level.FINE, "getClientLocationOffset() request for a disposed web page."); 934 return 0; 935 } 936 Invoker.getInvoker().checkEventThread(); 937 return twkGetInsertPositionOffset(getPage()); 938 939 } finally { 940 unlockPage(); 941 } 942 } 943 944 public int getClientInsertPositionOffset() { 945 lockPage(); 946 try { 947 if (isDisposed) { 948 log.log(Level.FINE, "getClientInsertPositionOffset() request for a disposed web page."); 949 return 0; 950 } 951 return twkGetInsertPositionOffset(getPage()); 952 953 } finally { 954 unlockPage(); 955 } 956 } 957 958 public int getClientCommittedTextLength() { 959 lockPage(); 960 try { 961 if (isDisposed) { 962 log.log(Level.FINE, "getClientCommittedTextOffset() request for a disposed web page."); 963 return 0; 964 } 965 return twkGetCommittedTextLength(getPage()); 966 967 } finally { 968 unlockPage(); 969 } 970 } 971 972 public String getClientCommittedText() { 973 lockPage(); 974 try { 975 if (isDisposed) { 976 log.log(Level.FINE, "getClientCommittedText() request for a disposed web page."); 977 return ""; 978 } 979 return twkGetCommittedText(getPage()); 980 981 } finally { 982 unlockPage(); 983 } 984 } 985 986 public String getClientSelectedText() { 987 lockPage(); 988 try { 989 if (isDisposed) { 990 log.log(Level.FINE, "getClientSelectedText() request for a disposed web page."); 991 return ""; 992 } 993 return twkGetSelectedText(getPage()); 994 995 } finally { 996 unlockPage(); 997 } 998 } 999 1000 // ************************************************************************* 1001 // Browser API 1002 // ************************************************************************* 1003 1004 public void dispose() { 1005 lockPage(); 1006 try { 1007 log.log(Level.FINER, "dispose"); 1008 1009 stop(); 1010 dropRenderFrames(); 1011 isDisposed = true; 1012 1013 twkDestroyPage(pPage); 1014 pPage = 0; 1015 1016 for (long frameID : frames) { 1017 log.log(Level.FINE, "Undestroyed frame view: " + frameID); 1018 } 1019 frames.clear(); 1020 1021 if (backbuffer != null) { 1022 backbuffer.deref(); 1023 backbuffer = null; 1024 } 1025 } finally { 1026 unlockPage(); 1027 } 1028 } 1029 1030 public String getName(long frameID) { 1031 lockPage(); 1032 try { 1033 log.log(Level.FINE, "Get Name: frame = " + frameID); 1034 if (isDisposed) { 1035 log.log(Level.FINE, "getName() request for a disposed web page."); 1036 return null; 1037 } 1038 if (!frames.contains(frameID)) { 1039 return null; 1040 } 1041 return twkGetName(frameID); 1042 1043 } finally { 1044 unlockPage(); 1045 } 1046 } 1047 1048 public String getURL(long frameID) { 1049 lockPage(); 1050 try { 1051 log.log(Level.FINE, "Get URL: frame = " + frameID); 1052 if (isDisposed) { 1053 log.log(Level.FINE, "getURL() request for a disposed web page."); 1054 return null; 1055 } 1056 if (!frames.contains(frameID)) { 1057 return null; 1058 } 1059 return twkGetURL(frameID); 1060 1061 } finally { 1062 unlockPage(); 1063 } 1064 } 1065 1066 public String getEncoding() { 1067 lockPage(); 1068 try { 1069 log.log(Level.FINE, "Get encoding"); 1070 if (isDisposed) { 1071 log.log(Level.FINE, "getEncoding() request for a disposed web page."); 1072 return null; 1073 } 1074 return twkGetEncoding(getPage()); 1075 1076 } finally { 1077 unlockPage(); 1078 } 1079 } 1080 1081 public void setEncoding(String encoding) { 1082 lockPage(); 1083 try { 1084 log.log(Level.FINE, "Set encoding: encoding = " + encoding); 1085 if (isDisposed) { 1086 log.log(Level.FINE, "setEncoding() request for a disposed web page."); 1087 return; 1088 } 1089 if (encoding != null && !encoding.isEmpty()) { 1090 twkSetEncoding(getPage(), encoding); 1091 } 1092 1093 } finally { 1094 unlockPage(); 1095 } 1096 } 1097 1098 // DRT support 1099 public String getInnerText(long frameID) { 1100 lockPage(); 1101 try { 1102 log.log(Level.FINE, "Get inner text: frame = " + frameID); 1103 if (isDisposed) { 1104 log.log(Level.FINE, "getInnerText() request for a disposed web page."); 1105 return null; 1106 } 1107 if (!frames.contains(frameID)) { 1108 return null; 1109 } 1110 return twkGetInnerText(frameID); 1111 1112 } finally { 1113 unlockPage(); 1114 } 1115 } 1116 1117 // DRT support 1118 public String getRenderTree(long frameID) { 1119 lockPage(); 1120 try { 1121 log.log(Level.FINE, "Get render tree: frame = " + frameID); 1122 if (isDisposed) { 1123 log.log(Level.FINE, "getRenderTree() request for a disposed web page."); 1124 return null; 1125 } 1126 if (!frames.contains(frameID)) { 1127 return null; 1128 } 1129 return twkGetRenderTree(frameID); 1130 1131 } finally { 1132 unlockPage(); 1133 } 1134 } 1135 1136 // DRT support 1137 public int getUnloadEventListenersCount(long frameID) { 1138 lockPage(); 1139 try { 1140 log.log(Level.FINE, "frame: " + frameID); 1141 if (isDisposed) { 1142 log.log(Level.FINE, "request for a disposed web page."); 1143 return 0; 1144 } 1145 if (!frames.contains(frameID)) { 1146 return 0; 1147 } 1148 return twkGetUnloadEventListenersCount(frameID); 1149 1150 } finally { 1151 unlockPage(); 1152 } 1153 } 1154 1155 public String getContentType(long frameID) { 1156 lockPage(); 1157 try { 1158 log.log(Level.FINE, "Get content type: frame = " + frameID); 1159 if (isDisposed) { 1160 log.log(Level.FINE, "getContentType() request for a disposed web page."); 1161 return null; 1162 } 1163 if (!frames.contains(frameID)) { 1164 return null; 1165 } 1166 return twkGetContentType(frameID); 1167 1168 } finally { 1169 unlockPage(); 1170 } 1171 } 1172 1173 public String getTitle(long frameID) { 1174 lockPage(); 1175 try { 1176 log.log(Level.FINE, "Get title: frame = " + frameID); 1177 if (isDisposed) { 1178 log.log(Level.FINE, "getTitle() request for a disposed web page."); 1179 return null; 1180 } 1181 if (!frames.contains(frameID)) { 1182 return null; 1183 } 1184 return twkGetTitle(frameID); 1185 1186 } finally { 1187 unlockPage(); 1188 } 1189 } 1190 1191 public WCImage getIcon(long frameID) { 1192 lockPage(); 1193 try { 1194 log.log(Level.FINE, "Get icon: frame = " + frameID); 1195 if (isDisposed) { 1196 log.log(Level.FINE, "getIcon() request for a disposed web page."); 1197 return null; 1198 } 1199 if (!frames.contains(frameID)) { 1200 return null; 1201 } 1202 String iconURL = twkGetIconURL(frameID); 1203 // do we need any cache for icons here? 1204 if (iconURL != null && !iconURL.isEmpty()) { 1205 return WCGraphicsManager.getGraphicsManager().getIconImage(iconURL); 1206 } 1207 return null; 1208 1209 } finally { 1210 unlockPage(); 1211 } 1212 } 1213 1214 public void open(final long frameID, final String url) { 1215 lockPage(); 1216 try { 1217 log.log(Level.FINE, "Open URL: " + url); 1218 if (isDisposed) { 1219 log.log(Level.FINE, "open() request for a disposed web page."); 1220 return; 1221 } 1222 if (!frames.contains(frameID)) { 1223 return; 1224 } 1225 if (twkIsLoading(frameID)) { 1226 Invoker.getInvoker().postOnEventThread(() -> { 1227 // Postpone new load request while webkit is 1228 // about to commit the DocumentLoader from 1229 // provisional state to committed state 1230 twkOpen(frameID, url); 1231 }); 1232 } else { 1233 twkOpen(frameID, url); 1234 } 1235 } finally { 1236 unlockPage(); 1237 } 1238 } 1239 1240 public void load(final long frameID, final String text, final String contentType) { 1241 lockPage(); 1242 try { 1243 log.log(Level.FINE, "Load text: " + text); 1244 if (text == null) { 1245 return; 1246 } 1247 if (isDisposed) { 1248 log.log(Level.FINE, "load() request for a disposed web page."); 1249 return; 1250 } 1251 if (!frames.contains(frameID)) { 1252 return; 1253 } 1254 // TODO: handle contentType 1255 if (twkIsLoading(frameID)) { 1256 // Postpone loading new content while webkit is 1257 // about to commit the DocumentLoader from 1258 // provisional state to committed state 1259 Invoker.getInvoker().postOnEventThread(() -> { 1260 twkLoad(frameID, text, contentType); 1261 }); 1262 } else { 1263 twkLoad(frameID, text, contentType); 1264 } 1265 } finally { 1266 unlockPage(); 1267 } 1268 } 1269 1270 public void stop(final long frameID) { 1271 lockPage(); 1272 try { 1273 log.log(Level.FINE, "Stop loading: frame = " + frameID); 1274 1275 String url; 1276 String contentType; 1277 if (isDisposed) { 1278 log.log(Level.FINE, "cancel() request for a disposed web page."); 1279 return; 1280 } 1281 if (!frames.contains(frameID)) { 1282 return; 1283 } 1284 url = twkGetURL(frameID); 1285 contentType = twkGetContentType(frameID); 1286 twkStop(frameID); 1287 // WebKit doesn't send any notifications about loading stopped, 1288 // so sending it here 1289 fireLoadEvent(frameID, LoadListenerClient.LOAD_STOPPED, url, contentType, 1.0, 0); 1290 1291 } finally { 1292 unlockPage(); 1293 } 1294 } 1295 1296 // stops all loading synchronously 1297 public void stop() { 1298 lockPage(); 1299 try { 1300 log.log(Level.FINE, "Stop loading sync"); 1301 if (isDisposed) { 1302 log.log(Level.FINE, "stopAll() request for a disposed web page."); 1303 return; 1304 } 1305 twkStopAll(getPage()); 1306 1307 } finally { 1308 unlockPage(); 1309 } 1310 } 1311 1312 public void refresh(final long frameID) { 1313 lockPage(); 1314 try { 1315 log.log(Level.FINE, "Refresh: frame = " + frameID); 1316 if (isDisposed) { 1317 log.log(Level.FINE, "refresh() request for a disposed web page."); 1318 return; 1319 } 1320 if (!frames.contains(frameID)) { 1321 return; 1322 } 1323 twkRefresh(frameID); 1324 1325 } finally { 1326 unlockPage(); 1327 } 1328 } 1329 1330 public BackForwardList createBackForwardList() { 1331 return new BackForwardList(this); 1332 } 1333 1334 public boolean goBack() { 1335 lockPage(); 1336 try { 1337 log.log(Level.FINE, "Go back"); 1338 if (isDisposed) { 1339 log.log(Level.FINE, "goBack() request for a disposed web page."); 1340 return false; 1341 } 1342 return twkGoBackForward(getPage(), -1); 1343 1344 } finally { 1345 unlockPage(); 1346 } 1347 } 1348 1349 public boolean goForward() { 1350 lockPage(); 1351 try { 1352 log.log(Level.FINE, "Go forward"); 1353 if (isDisposed) { 1354 log.log(Level.FINE, "goForward() request for a disposed web page."); 1355 return false; 1356 } 1357 return twkGoBackForward(getPage(), 1); 1358 1359 } finally { 1360 unlockPage(); 1361 } 1362 } 1363 1364 public boolean copy() { 1365 lockPage(); 1366 try { 1367 log.log(Level.FINE, "Copy"); 1368 if (isDisposed) { 1369 log.log(Level.FINE, "copy() request for a disposed web page."); 1370 return false; 1371 } 1372 long frameID = getMainFrame(); 1373 if (!frames.contains(frameID)) { 1374 return false; 1375 } 1376 return twkCopy(frameID); 1377 1378 } finally { 1379 unlockPage(); 1380 } 1381 } 1382 1383 // Find in page 1384 public boolean find(String stringToFind, boolean forward, boolean wrap, boolean matchCase) { 1385 lockPage(); 1386 try { 1387 log.log(Level.FINE, "Find in page: stringToFind = " + stringToFind + ", " + 1388 (forward ? "forward" : "backward") + (wrap ? ", wrap" : "") + (matchCase ? ", matchCase" : "")); 1389 if (isDisposed) { 1390 log.log(Level.FINE, "find() request for a disposed web page."); 1391 return false; 1392 } 1393 return twkFindInPage(getPage(), stringToFind, forward, wrap, matchCase); 1394 1395 } finally { 1396 unlockPage(); 1397 } 1398 } 1399 1400 // Find in frame 1401 public boolean find(long frameID, 1402 String stringToFind, boolean forward, boolean wrap, boolean matchCase) 1403 { 1404 lockPage(); 1405 try { 1406 log.log(Level.FINE, "Find in frame: stringToFind = " + stringToFind + ", " + 1407 (forward ? "forward" : "backward") + (wrap ? ", wrap" : "") + (matchCase ? ", matchCase" : "")); 1408 if (isDisposed) { 1409 log.log(Level.FINE, "find() request for a disposed web page."); 1410 return false; 1411 } 1412 if (!frames.contains(frameID)) { 1413 return false; 1414 } 1415 return twkFindInFrame(frameID, stringToFind, forward, wrap, matchCase); 1416 1417 } finally { 1418 unlockPage(); 1419 } 1420 } 1421 1422 public void overridePreference(String key, String value) { 1423 lockPage(); 1424 try { 1425 twkOverridePreference(getPage(), key, value); 1426 } finally { 1427 unlockPage(); 1428 } 1429 } 1430 1431 public void resetToConsistentStateBeforeTesting() { 1432 lockPage(); 1433 try { 1434 twkResetToConsistentStateBeforeTesting(getPage()); 1435 } finally { 1436 unlockPage(); 1437 } 1438 } 1439 1440 public float getZoomFactor(boolean textOnly) { 1441 lockPage(); 1442 try { 1443 log.log(Level.FINE, "Get zoom factor, textOnly=" + textOnly); 1444 if (isDisposed) { 1445 log.log(Level.FINE, "getZoomFactor() request for a disposed web page."); 1446 return 1.0f; 1447 } 1448 long frameID = getMainFrame(); 1449 if (!frames.contains(frameID)) { 1450 return 1.0f; 1451 } 1452 return twkGetZoomFactor(frameID, textOnly); 1453 } finally { 1454 unlockPage(); 1455 } 1456 } 1457 1458 public void setZoomFactor(float zoomFactor, boolean textOnly) { 1459 lockPage(); 1460 try { 1461 log.fine(String.format("Set zoom factor %.2f, textOnly=%b", zoomFactor, textOnly)); 1462 if (isDisposed) { 1463 log.log(Level.FINE, "setZoomFactor() request for a disposed web page."); 1464 return; 1465 } 1466 long frameID = getMainFrame(); 1467 if ((frameID == 0) || !frames.contains(frameID)) { 1468 return; 1469 } 1470 twkSetZoomFactor(frameID, zoomFactor, textOnly); 1471 } finally { 1472 unlockPage(); 1473 } 1474 } 1475 1476 public void setFontSmoothingType(int fontSmoothingType) { 1477 this.fontSmoothingType = fontSmoothingType; 1478 repaintAll(); 1479 } 1480 1481 // DRT support 1482 public void reset(long frameID) { 1483 lockPage(); 1484 try { 1485 log.log(Level.FINE, "Reset: frame = " + frameID); 1486 if (isDisposed) { 1487 log.log(Level.FINE, "reset() request for a disposed web page."); 1488 return; 1489 } 1490 if ((frameID == 0) || !frames.contains(frameID)) { 1491 return; 1492 } 1493 twkReset(frameID); 1494 1495 } finally { 1496 unlockPage(); 1497 } 1498 } 1499 1500 public Object executeScript(long frameID, String script) throws JSException { 1501 lockPage(); 1502 try { 1503 log.log(Level.FINE, "execute script: \"" + script + "\" in frame = " + frameID); 1504 if (isDisposed) { 1505 log.log(Level.FINE, "executeScript() request for a disposed web page."); 1506 return null; 1507 } 1508 if ((frameID == 0) || !frames.contains(frameID)) { 1509 return null; 1510 } 1511 return twkExecuteScript(frameID, script); 1512 1513 } finally { 1514 unlockPage(); 1515 } 1516 } 1517 1518 public long getMainFrame() { 1519 lockPage(); 1520 try { 1521 log.log(Level.FINER, "getMainFrame: page = " + pPage); 1522 if (isDisposed) { 1523 log.log(Level.FINE, "getMainFrame() request for a disposed web page."); 1524 return 0L; 1525 } 1526 long mainFrameID = twkGetMainFrame(getPage()); 1527 log.log(Level.FINER, "Main frame = " + mainFrameID); 1528 frames.add(mainFrameID); 1529 return mainFrameID; 1530 } finally { 1531 unlockPage(); 1532 } 1533 } 1534 1535 public long getParentFrame(long childID) { 1536 lockPage(); 1537 try { 1538 log.log(Level.FINE, "getParentFrame: child = " + childID); 1539 if (isDisposed) { 1540 log.log(Level.FINE, "getParentFrame() request for a disposed web page."); 1541 return 0L; 1542 } 1543 if (!frames.contains(childID)) { 1544 return 0L; 1545 } 1546 return twkGetParentFrame(childID); 1547 } finally { 1548 unlockPage(); 1549 } 1550 } 1551 1552 public List<Long> getChildFrames(long parentID) { 1553 lockPage(); 1554 try { 1555 log.log(Level.FINE, "getChildFrames: parent = " + parentID); 1556 if (isDisposed) { 1557 log.log(Level.FINE, "getChildFrames() request for a disposed web page."); 1558 return null; 1559 } 1560 if (!frames.contains(parentID)) { 1561 return null; 1562 } 1563 long[] children = twkGetChildFrames(parentID); 1564 List<Long> childrenList = new LinkedList<Long>(); 1565 for (long child : children) { 1566 childrenList.add(Long.valueOf(child)); 1567 } 1568 return childrenList; 1569 } finally { 1570 unlockPage(); 1571 } 1572 } 1573 1574 public WCRectangle getVisibleRect(long frameID) { 1575 lockPage(); 1576 try { 1577 if (!frames.contains(frameID)) { 1578 return null; 1579 } 1580 int[] arr = twkGetVisibleRect(frameID); 1581 if (arr != null) { 1582 return new WCRectangle(arr[0], arr[1], arr[2], arr[3]); 1583 } 1584 return null; 1585 } finally { 1586 unlockPage(); 1587 } 1588 } 1589 1590 public void scrollToPosition(long frameID, WCPoint p) { 1591 lockPage(); 1592 try { 1593 if (!frames.contains(frameID)) { 1594 return; 1595 } 1596 twkScrollToPosition(frameID, p.getIntX(), p.getIntY()); 1597 } finally { 1598 unlockPage(); 1599 } 1600 } 1601 1602 public WCSize getContentSize(long frameID) { 1603 lockPage(); 1604 try { 1605 if (!frames.contains(frameID)) { 1606 return null; 1607 } 1608 int[] arr = twkGetContentSize(frameID); 1609 if (arr != null) { 1610 return new WCSize(arr[0], arr[1]); 1611 } 1612 return null; 1613 } finally { 1614 unlockPage(); 1615 } 1616 } 1617 1618 // ---- DOM ---- // 1619 1620 public Document getDocument(long frameID) { 1621 lockPage(); 1622 try { 1623 log.log(Level.FINE, "getDocument"); 1624 if (isDisposed) { 1625 log.log(Level.FINE, "getDocument() request for a disposed web page."); 1626 return null; 1627 } 1628 1629 if (!frames.contains(frameID)) { 1630 return null; 1631 } 1632 return twkGetDocument(frameID); 1633 } finally { 1634 unlockPage(); 1635 } 1636 } 1637 1638 public Element getOwnerElement(long frameID) { 1639 lockPage(); 1640 try { 1641 log.log(Level.FINE, "getOwnerElement"); 1642 if (isDisposed) { 1643 log.log(Level.FINE, "getOwnerElement() request for a disposed web page."); 1644 return null; 1645 } 1646 1647 if (!frames.contains(frameID)) { 1648 return null; 1649 } 1650 return twkGetOwnerElement(frameID); 1651 } finally { 1652 unlockPage(); 1653 } 1654 } 1655 1656 // ---- EDITING SUPPORT ---- // 1657 1658 public boolean executeCommand(String command, String value) { 1659 lockPage(); 1660 try { 1661 if (log.isLoggable(Level.FINE)) { 1662 log.log(Level.FINE, "command: [{0}], value: [{1}]", 1663 new Object[] {command, value}); 1664 } 1665 if (isDisposed) { 1666 log.log(Level.FINE, "Web page is already disposed"); 1667 return false; 1668 } 1669 1670 boolean result = twkExecuteCommand(getPage(), command, value); 1671 1672 log.log(Level.FINE, "result: [{0}]", result); 1673 return result; 1674 } finally { 1675 unlockPage(); 1676 } 1677 } 1678 1679 public boolean queryCommandEnabled(String command) { 1680 lockPage(); 1681 try { 1682 log.log(Level.FINE, "command: [{0}]", command); 1683 if (isDisposed) { 1684 log.log(Level.FINE, "Web page is already disposed"); 1685 return false; 1686 } 1687 1688 boolean result = twkQueryCommandEnabled(getPage(), command); 1689 1690 log.log(Level.FINE, "result: [{0}]", result); 1691 return result; 1692 } finally { 1693 unlockPage(); 1694 } 1695 } 1696 1697 public boolean queryCommandState(String command) { 1698 lockPage(); 1699 try { 1700 log.log(Level.FINE, "command: [{0}]", command); 1701 if (isDisposed) { 1702 log.log(Level.FINE, "Web page is already disposed"); 1703 return false; 1704 } 1705 1706 boolean result = twkQueryCommandState(getPage(), command); 1707 1708 log.log(Level.FINE, "result: [{0}]", result); 1709 return result; 1710 } finally { 1711 unlockPage(); 1712 } 1713 } 1714 1715 public String queryCommandValue(String command) { 1716 lockPage(); 1717 try { 1718 log.log(Level.FINE, "command: [{0}]", command); 1719 if (isDisposed) { 1720 log.log(Level.FINE, "Web page is already disposed"); 1721 return null; 1722 } 1723 1724 String result = twkQueryCommandValue(getPage(), command); 1725 1726 log.log(Level.FINE, "result: [{0}]", result); 1727 return result; 1728 } finally { 1729 unlockPage(); 1730 } 1731 } 1732 1733 public boolean isEditable() { 1734 lockPage(); 1735 try { 1736 log.log(Level.FINE, "isEditable"); 1737 if (isDisposed) { 1738 log.log(Level.FINE, "isEditable() request for a disposed web page."); 1739 return false; 1740 } 1741 1742 return twkIsEditable(getPage()); 1743 } finally { 1744 unlockPage(); 1745 } 1746 } 1747 1748 public void setEditable(boolean editable) { 1749 lockPage(); 1750 try { 1751 log.log(Level.FINE, "setEditable"); 1752 if (isDisposed) { 1753 log.log(Level.FINE, "setEditable() request for a disposed web page."); 1754 return; 1755 } 1756 1757 twkSetEditable(getPage(), editable); 1758 } finally { 1759 unlockPage(); 1760 } 1761 } 1762 1763 /** 1764 * @return HTML content of the frame, 1765 * or null if frame document is absent or non-HTML. 1766 */ 1767 public String getHtml(long frameID) { 1768 lockPage(); 1769 try { 1770 log.log(Level.FINE, "getHtml"); 1771 if (isDisposed) { 1772 log.log(Level.FINE, "getHtml() request for a disposed web page."); 1773 return null; 1774 } 1775 if (!frames.contains(frameID)) { 1776 return null; 1777 } 1778 return twkGetHtml(frameID); 1779 } finally { 1780 unlockPage(); 1781 } 1782 } 1783 1784 // ---- PRINTING SUPPORT ---- // 1785 1786 public int beginPrinting(float width, float height) { 1787 lockPage(); 1788 try { 1789 if (isDisposed) { 1790 log.warning("beginPrinting() called for a disposed web page."); 1791 return 0; 1792 } 1793 AtomicReference<Integer> retVal = new AtomicReference<>(0); 1794 final CountDownLatch l = new CountDownLatch(1); 1795 Invoker.getInvoker().invokeOnEventThread(() -> { 1796 try { 1797 int nPages = twkBeginPrinting(getPage(), width, height); 1798 retVal.set(nPages); 1799 } finally { 1800 l.countDown(); 1801 } 1802 }); 1803 1804 try { 1805 l.await(); 1806 } catch (InterruptedException e) { 1807 throw new RuntimeException(e); 1808 } 1809 return retVal.get(); 1810 } finally { 1811 unlockPage(); 1812 } 1813 } 1814 1815 public void endPrinting() { 1816 lockPage(); 1817 try { 1818 if (isDisposed) { 1819 log.warning("endPrinting() called for a disposed web page."); 1820 return; 1821 } 1822 final CountDownLatch l = new CountDownLatch(1); 1823 Invoker.getInvoker().invokeOnEventThread(() -> { 1824 try { 1825 twkEndPrinting(getPage()); 1826 } finally { 1827 l.countDown(); 1828 } 1829 }); 1830 1831 try { 1832 l.await(); 1833 } catch (InterruptedException e) { 1834 throw new RuntimeException(e); 1835 } 1836 } finally { 1837 unlockPage(); 1838 } 1839 } 1840 1841 public void print(final WCGraphicsContext gc, final int pageNumber, final float width) { 1842 lockPage(); 1843 try { 1844 if (isDisposed) { 1845 log.warning("print() called for a disposed web page."); 1846 return; 1847 } 1848 final WCRenderQueue rq = WCGraphicsManager.getGraphicsManager(). 1849 createRenderQueue(null, true); 1850 final CountDownLatch l = new CountDownLatch(1); 1851 Invoker.getInvoker().invokeOnEventThread(() -> { 1852 try { 1853 twkPrint(getPage(), rq, pageNumber, width); 1854 } finally { 1855 l.countDown(); 1856 } 1857 }); 1858 1859 try { 1860 l.await(); 1861 } catch (InterruptedException e) { 1862 rq.dispose(); 1863 return; 1864 } 1865 rq.decode(gc); 1866 } finally { 1867 unlockPage(); 1868 } 1869 } 1870 1871 public int getPageHeight() { 1872 return getFrameHeight(getMainFrame()); 1873 } 1874 1875 public int getFrameHeight(long frameID) { 1876 lockPage(); 1877 try { 1878 log.log(Level.FINE, "Get page height"); 1879 if (isDisposed) { 1880 log.log(Level.FINE, "getFrameHeight() request for a disposed web page."); 1881 return 0; 1882 } 1883 if (!frames.contains(frameID)) { 1884 return 0; 1885 } 1886 int height = twkGetFrameHeight(frameID); 1887 log.log(Level.FINE, "Height = " + height); 1888 return height; 1889 } finally { 1890 unlockPage(); 1891 } 1892 } 1893 1894 public float adjustFrameHeight(long frameID, 1895 float oldTop, float oldBottom, float bottomLimit) 1896 { 1897 lockPage(); 1898 try { 1899 log.log(Level.FINE, "Adjust page height"); 1900 if (isDisposed) { 1901 log.log(Level.FINE, "adjustFrameHeight() request for a disposed web page."); 1902 return 0; 1903 } 1904 if (!frames.contains(frameID)) { 1905 return 0; 1906 } 1907 return twkAdjustFrameHeight(frameID, oldTop, oldBottom, bottomLimit); 1908 } finally { 1909 unlockPage(); 1910 } 1911 } 1912 1913 // ---- SETTINGS ---- // 1914 1915 /** 1916 * Returns the usePageCache settings field. 1917 * @return {@code true} if this object uses the page cache, 1918 * {@code false} otherwise. 1919 */ 1920 public boolean getUsePageCache() { 1921 lockPage(); 1922 try { 1923 return twkGetUsePageCache(getPage()); 1924 } finally { 1925 unlockPage(); 1926 } 1927 } 1928 1929 /** 1930 * Sets the usePageCache settings field. 1931 * @param usePageCache {@code true} to use the page cache, 1932 * {@code false} to not use the page cache. 1933 */ 1934 public void setUsePageCache(boolean usePageCache) { 1935 lockPage(); 1936 try { 1937 twkSetUsePageCache(getPage(), usePageCache); 1938 } finally { 1939 unlockPage(); 1940 } 1941 } 1942 1943 public boolean getDeveloperExtrasEnabled() { 1944 lockPage(); 1945 try { 1946 boolean result = twkGetDeveloperExtrasEnabled(getPage()); 1947 log.log(Level.FINE, 1948 "Getting developerExtrasEnabled, result: [{0}]", 1949 result); 1950 return result; 1951 } finally { 1952 unlockPage(); 1953 } 1954 } 1955 1956 public void setDeveloperExtrasEnabled(boolean enabled) { 1957 lockPage(); 1958 try { 1959 log.log(Level.FINE, 1960 "Setting developerExtrasEnabled, value: [{0}]", 1961 enabled); 1962 twkSetDeveloperExtrasEnabled(getPage(), enabled); 1963 } finally { 1964 unlockPage(); 1965 } 1966 } 1967 1968 public boolean isJavaScriptEnabled() { 1969 lockPage(); 1970 try { 1971 return twkIsJavaScriptEnabled(getPage()); 1972 } finally { 1973 unlockPage(); 1974 } 1975 } 1976 1977 public void setJavaScriptEnabled(boolean enable) { 1978 lockPage(); 1979 try { 1980 twkSetJavaScriptEnabled(getPage(), enable); 1981 } finally { 1982 unlockPage(); 1983 } 1984 } 1985 1986 public boolean isContextMenuEnabled() { 1987 lockPage(); 1988 try { 1989 return twkIsContextMenuEnabled(getPage()); 1990 } finally { 1991 unlockPage(); 1992 } 1993 } 1994 1995 public void setContextMenuEnabled(boolean enable) { 1996 lockPage(); 1997 try { 1998 twkSetContextMenuEnabled(getPage(), enable); 1999 } finally { 2000 unlockPage(); 2001 } 2002 } 2003 2004 public void setUserStyleSheetLocation(String url) { 2005 lockPage(); 2006 try { 2007 twkSetUserStyleSheetLocation(getPage(), url); 2008 } finally { 2009 unlockPage(); 2010 } 2011 } 2012 2013 public String getUserAgent() { 2014 lockPage(); 2015 try { 2016 return twkGetUserAgent(getPage()); 2017 } finally { 2018 unlockPage(); 2019 } 2020 } 2021 2022 public void setUserAgent(String userAgent) { 2023 lockPage(); 2024 try { 2025 twkSetUserAgent(getPage(), userAgent); 2026 } finally { 2027 unlockPage(); 2028 } 2029 } 2030 2031 public void setLocalStorageDatabasePath(String path) { 2032 lockPage(); 2033 try { 2034 twkSetLocalStorageDatabasePath(getPage(), path); 2035 } finally { 2036 unlockPage(); 2037 } 2038 } 2039 2040 public void setLocalStorageEnabled(boolean enabled) { 2041 lockPage(); 2042 try { 2043 twkSetLocalStorageEnabled(getPage(), enabled); 2044 } finally { 2045 unlockPage(); 2046 } 2047 } 2048 2049 // ---- INSPECTOR SUPPORT ---- // 2050 2051 public void connectInspectorFrontend() { 2052 lockPage(); 2053 try { 2054 log.log(Level.FINE, "Connecting inspector frontend"); 2055 twkConnectInspectorFrontend(getPage()); 2056 } finally { 2057 unlockPage(); 2058 } 2059 } 2060 2061 public void disconnectInspectorFrontend() { 2062 lockPage(); 2063 try { 2064 log.log(Level.FINE, "Disconnecting inspector frontend"); 2065 twkDisconnectInspectorFrontend(getPage()); 2066 } finally { 2067 unlockPage(); 2068 } 2069 } 2070 2071 public void dispatchInspectorMessageFromFrontend(String message) { 2072 lockPage(); 2073 try { 2074 if (log.isLoggable(Level.FINE)) { 2075 log.log(Level.FINE, 2076 "Dispatching inspector message from frontend, " 2077 + "message: [{0}]", 2078 message); 2079 } 2080 twkDispatchInspectorMessageFromFrontend(getPage(), message); 2081 } finally { 2082 unlockPage(); 2083 } 2084 } 2085 2086 // ************************************************************************* 2087 // Native callbacks 2088 // ************************************************************************* 2089 2090 private void fwkFrameCreated(long frameID) { 2091 log.log(Level.FINE, "Frame created: frame = " + frameID); 2092 if (frames.contains(frameID)) { 2093 log.log(Level.FINE, "Error in fwkFrameCreated: frame is already in frames"); 2094 return; 2095 } 2096 frames.add(frameID); 2097 } 2098 2099 private void fwkFrameDestroyed(long frameID) { 2100 log.log(Level.FINE, "Frame destroyed: frame = " + frameID); 2101 if (!frames.contains(frameID)) { 2102 log.log(Level.FINE, "Error in fwkFrameDestroyed: frame is not found in frames"); 2103 return; 2104 } 2105 frames.remove(frameID); 2106 } 2107 2108 private void fwkRepaint(int x, int y, int w, int h) { 2109 lockPage(); 2110 try { 2111 if (paintLog.isLoggable(Level.FINEST)) { 2112 paintLog.log(Level.FINEST, "x: {0}, y: {1}, w: {2}, h: {3}", 2113 new Object[] {x, y, w, h}); 2114 } 2115 addDirtyRect(new WCRectangle(x, y, w, h)); 2116 } finally { 2117 unlockPage(); 2118 } 2119 } 2120 2121 private void fwkScroll(int x, int y, int w, int h, int deltaX, int deltaY) { 2122 if (paintLog.isLoggable(Level.FINEST)) { 2123 paintLog.finest("Scroll: " + x + " " + y + " " + w + " " + h + " " + deltaX + " " + deltaY); 2124 } 2125 if (pageClient == null || !pageClient.isBackBufferSupported()) { 2126 paintLog.finest("blit scrolling is switched off"); 2127 // TODO: check why we return void, not boolean (see ScrollView::m_canBlitOnScroll) 2128 return; 2129 } 2130 scroll(x, y, w, h, deltaX, deltaY); 2131 } 2132 2133 private void fwkTransferFocus(boolean forward) { 2134 log.log(Level.FINER, "Transfer focus " + (forward ? "forward" : "backward")); 2135 2136 if (pageClient != null) { 2137 pageClient.transferFocus(forward); 2138 } 2139 } 2140 2141 private void fwkSetCursor(long id) { 2142 log.log(Level.FINER, "Set cursor: " + id); 2143 2144 if (pageClient != null) { 2145 pageClient.setCursor(id); 2146 } 2147 } 2148 2149 private void fwkSetFocus(boolean focus) { 2150 log.log(Level.FINER, "Set focus: " + (focus ? "true" : "false")); 2151 2152 if (pageClient != null) { 2153 pageClient.setFocus(focus); 2154 } 2155 } 2156 2157 private void fwkSetTooltip(String tooltip) { 2158 log.log(Level.FINER, "Set tooltip: " + tooltip); 2159 2160 if (pageClient != null) { 2161 pageClient.setTooltip(tooltip); 2162 } 2163 } 2164 2165 private void fwkPrint() { 2166 log.log(Level.FINER, "Print"); 2167 2168 if (uiClient != null) { 2169 uiClient.print(); 2170 } 2171 } 2172 2173 private void fwkSetRequestURL(long pFrame, int id, String url) { 2174 log.log(Level.FINER, "Set request URL: id = " + id + ", url = " + url); 2175 2176 synchronized (requestURLs) { 2177 requestURLs.put(id, url); 2178 } 2179 } 2180 2181 private void fwkRemoveRequestURL(long pFrame, int id) { 2182 log.log(Level.FINER, "Set request URL: id = " + id); 2183 2184 synchronized (requestURLs) { 2185 requestURLs.remove(id); 2186 requestStarted.remove(id); 2187 } 2188 } 2189 2190 private WebPage fwkCreateWindow( 2191 boolean menu, boolean status, boolean toolbar, boolean resizable) { 2192 log.log(Level.FINER, "Create window"); 2193 2194 if (uiClient != null) { 2195 return uiClient.createPage(menu, status, toolbar, resizable); 2196 } 2197 return null; 2198 } 2199 2200 private void fwkShowWindow() { 2201 log.log(Level.FINER, "Show window"); 2202 2203 if (uiClient != null) { 2204 uiClient.showView(); 2205 } 2206 } 2207 2208 private void fwkCloseWindow() { 2209 log.log(Level.FINER, "Close window"); 2210 2211 if (permitCloseWindowAction()) { 2212 if (uiClient != null) { 2213 uiClient.closePage(); 2214 } 2215 } 2216 } 2217 2218 private WCRectangle fwkGetWindowBounds() { 2219 log.log(Level.FINE, "Get window bounds"); 2220 2221 if (uiClient != null) { 2222 WCRectangle bounds = uiClient.getViewBounds(); 2223 if (bounds != null) { 2224 return bounds; 2225 } 2226 } 2227 return fwkGetPageBounds(); 2228 } 2229 2230 private void fwkSetWindowBounds(int x, int y, int w, int h) { 2231 log.log(Level.FINER, "Set window bounds: " + x + " " + y + " " + w + " " + h); 2232 2233 if (uiClient != null) { 2234 uiClient.setViewBounds(new WCRectangle(x, y, w, h)); 2235 } 2236 } 2237 2238 private WCRectangle fwkGetPageBounds() { 2239 log.log(Level.FINER, "Get page bounds"); 2240 return new WCRectangle(0, 0, width, height); 2241 } 2242 2243 private void fwkSetScrollbarsVisible(boolean visible) { 2244 // TODO: handle this request internally 2245 } 2246 2247 private void fwkSetStatusbarText(String text) { 2248 log.log(Level.FINER, "Set statusbar text: " + text); 2249 2250 if (uiClient != null) { 2251 uiClient.setStatusbarText(text); 2252 } 2253 } 2254 2255 private String[] fwkChooseFile(String initialFileName, boolean multiple, String mimeFilters) { 2256 log.log(Level.FINER, "Choose file, initial=" + initialFileName); 2257 2258 return uiClient != null 2259 ? uiClient.chooseFile(initialFileName, multiple, mimeFilters) 2260 : null; 2261 } 2262 2263 private void fwkStartDrag( 2264 Object image, 2265 int imageOffsetX, int imageOffsetY, 2266 int eventPosX, int eventPosY, 2267 String[] mimeTypes, Object[] values, 2268 boolean isImageSource) 2269 { 2270 log.log(Level.FINER, "Start drag: "); 2271 if (uiClient != null) { 2272 uiClient.startDrag( 2273 WCImage.getImage(image), 2274 imageOffsetX, imageOffsetY, 2275 eventPosX, eventPosY, 2276 mimeTypes, values, 2277 isImageSource); 2278 } 2279 } 2280 2281 private WCPoint fwkScreenToWindow(WCPoint ptScreen) { 2282 log.log(Level.FINER, "fwkScreenToWindow"); 2283 2284 if (pageClient != null) { 2285 return pageClient.screenToWindow(ptScreen); 2286 } 2287 return ptScreen; 2288 } 2289 2290 private WCPoint fwkWindowToScreen(WCPoint ptWindow) { 2291 log.log(Level.FINER, "fwkWindowToScreen"); 2292 2293 if (pageClient != null) { 2294 return pageClient.windowToScreen(ptWindow); 2295 } 2296 return ptWindow; 2297 } 2298 2299 2300 private void fwkAlert(String text) { 2301 log.log(Level.FINE, "JavaScript alert(): text = " + text); 2302 2303 if (uiClient != null) { 2304 uiClient.alert(text); 2305 } 2306 } 2307 2308 private boolean fwkConfirm(String text) { 2309 log.log(Level.FINE, "JavaScript confirm(): text = " + text); 2310 2311 if (uiClient != null) { 2312 return uiClient.confirm(text); 2313 } 2314 return false; 2315 } 2316 2317 private String fwkPrompt(String text, String defaultValue) { 2318 log.log(Level.FINE, "JavaScript prompt(): text = " + text + ", default = " + defaultValue); 2319 2320 if (uiClient != null) { 2321 return uiClient.prompt(text, defaultValue); 2322 } 2323 return null; 2324 } 2325 2326 private boolean fwkCanRunBeforeUnloadConfirmPanel() { 2327 log.log(Level.FINE, "JavaScript canRunBeforeUnloadConfirmPanel()"); 2328 2329 if (uiClient != null) { 2330 return uiClient.canRunBeforeUnloadConfirmPanel(); 2331 } 2332 return false; 2333 } 2334 2335 private boolean fwkRunBeforeUnloadConfirmPanel(String message) { 2336 log.log(Level.FINE, "JavaScript runBeforeUnloadConfirmPanel(): message = " + message); 2337 2338 if (uiClient != null) { 2339 return uiClient.runBeforeUnloadConfirmPanel(message); 2340 } 2341 return false; 2342 } 2343 2344 private void fwkAddMessageToConsole(String message, int lineNumber, 2345 String sourceId) 2346 { 2347 log.log(Level.FINE, "fwkAddMessageToConsole(): message = " + message 2348 + ", lineNumber = " + lineNumber + ", sourceId = " + sourceId); 2349 if (pageClient != null) { 2350 pageClient.addMessageToConsole(message, lineNumber, sourceId); 2351 } 2352 } 2353 2354 private void fwkFireLoadEvent(long frameID, int state, 2355 String url, String contentType, 2356 double progress, int errorCode) 2357 { 2358 log.log(Level.FINER, "Load event: pFrame = " + frameID + ", state = " + state + 2359 ", url = " + url + ", contenttype=" + contentType + 2360 ", progress = " + progress + ", error = " + errorCode); 2361 2362 fireLoadEvent(frameID, state, url, contentType, progress, errorCode); 2363 } 2364 2365 private void fwkFireResourceLoadEvent(long frameID, int state, 2366 int id, String contentType, 2367 double progress, int errorCode) 2368 { 2369 log.log(Level.FINER, "Resource load event: pFrame = " + frameID + ", state = " + state + 2370 ", id = " + id + ", contenttype=" + contentType + 2371 ", progress = " + progress + ", error = " + errorCode); 2372 2373 String url = requestURLs.get(id); 2374 if (url == null) { 2375 log.log(Level.FINE, "Error in fwkFireResourceLoadEvent: unknown request id " + id); 2376 return; 2377 } 2378 2379 int eventState = state; 2380 // convert second and all subsequent STARTED into REDIRECTED 2381 if (state == LoadListenerClient.RESOURCE_STARTED) { 2382 if (requestStarted.contains(id)) { 2383 eventState = LoadListenerClient.RESOURCE_REDIRECTED; 2384 } else { 2385 requestStarted.add(id); 2386 } 2387 } 2388 2389 fireResourceLoadEvent(frameID, eventState, url, contentType, progress, errorCode); 2390 } 2391 2392 private boolean fwkPermitNavigateAction(long pFrame, String url) { 2393 log.log(Level.FINE, "Policy: permit NAVIGATE: pFrame = " + pFrame + ", url = " + url); 2394 2395 if (policyClient != null) { 2396 return policyClient.permitNavigateAction(pFrame, str2url(url)); 2397 } 2398 return true; 2399 } 2400 2401 private boolean fwkPermitRedirectAction(long pFrame, String url) { 2402 log.log(Level.FINE, "Policy: permit REDIRECT: pFrame = " + pFrame + ", url = " + url); 2403 2404 if (policyClient != null) { 2405 return policyClient.permitRedirectAction(pFrame, str2url(url)); 2406 } 2407 return true; 2408 } 2409 2410 private boolean fwkPermitAcceptResourceAction(long pFrame, String url) { 2411 log.log(Level.FINE, "Policy: permit ACCEPT_RESOURCE: pFrame + " + pFrame + ", url = " + url); 2412 2413 if (policyClient != null) { 2414 return policyClient.permitAcceptResourceAction(pFrame, str2url(url)); 2415 } 2416 return true; 2417 } 2418 2419 private boolean fwkPermitSubmitDataAction(long pFrame, String url, 2420 String httpMethod, boolean isSubmit) 2421 { 2422 log.log(Level.FINE, "Policy: permit " + (isSubmit ? "" : "RE") + "SUBMIT_DATA: pFrame = " + 2423 pFrame + ", url = " + url + ", httpMethod = " + httpMethod); 2424 2425 if (policyClient != null) { 2426 if (isSubmit) { 2427 return policyClient.permitSubmitDataAction(pFrame, str2url(url), httpMethod); 2428 } else { 2429 return policyClient.permitResubmitDataAction(pFrame, str2url(url), httpMethod); 2430 } 2431 } 2432 return true; 2433 } 2434 2435 private boolean fwkPermitEnableScriptsAction(long pFrame, String url) { 2436 log.log(Level.FINE, "Policy: permit ENABLE_SCRIPTS: pFrame + " + pFrame + ", url = " + url); 2437 2438 if (policyClient != null) { 2439 return policyClient.permitEnableScriptsAction(pFrame, str2url(url)); 2440 } 2441 return true; 2442 } 2443 2444 private boolean fwkPermitNewWindowAction(long pFrame, String url) { 2445 log.log(Level.FINE, "Policy: permit NEW_PAGE: pFrame = " + pFrame + ", url = " + url); 2446 2447 if (policyClient != null) { 2448 return policyClient.permitNewPageAction(pFrame, str2url(url)); 2449 } 2450 return true; 2451 } 2452 2453 // Called from fwkCloseWindow, that's why no "fwk" prefix 2454 private boolean permitCloseWindowAction() { 2455 log.log(Level.FINE, "Policy: permit CLOSE_PAGE"); 2456 2457 if (policyClient != null) { 2458 // Unfortunately, webkit doesn't provide an information about what 2459 // web frame initiated close window request, so using main frame here 2460 return policyClient.permitClosePageAction(getMainFrame()); 2461 } 2462 return true; 2463 } 2464 2465 private void fwkRepaintAll() { 2466 log.log(Level.FINE, "Repainting the entire page"); 2467 repaintAll(); 2468 } 2469 2470 private boolean fwkSendInspectorMessageToFrontend(String message) { 2471 if (log.isLoggable(Level.FINE)) { 2472 log.log(Level.FINE, 2473 "Sending inspector message to frontend, message: [{0}]", 2474 message); 2475 } 2476 boolean result = false; 2477 if (inspectorClient != null) { 2478 log.log(Level.FINE, "Invoking inspector client"); 2479 result = inspectorClient.sendMessageToFrontend(message); 2480 } 2481 if (log.isLoggable(Level.FINE)) { 2482 log.log(Level.FINE, "Result: [{0}]", result); 2483 } 2484 return result; 2485 } 2486 2487 // ---- DumpRenderTree support ---- // 2488 2489 public static int getWorkerThreadCount() { 2490 return twkWorkerThreadCount(); 2491 } 2492 2493 private static native int twkWorkerThreadCount(); 2494 2495 private void fwkDidClearWindowObject(long pContext, long pWindowObject) { 2496 if (pageClient != null) { 2497 pageClient.didClearWindowObject(pContext, pWindowObject); 2498 } 2499 } 2500 2501 // ************************************************************************* 2502 // Private methods 2503 // ************************************************************************* 2504 2505 private URL str2url(String url) { 2506 try { 2507 return newURL(url); 2508 } catch (MalformedURLException ex) { 2509 log.log(Level.FINE, "Exception while converting \"" + url + "\" to URL", ex); 2510 } 2511 return null; 2512 } 2513 2514 private void fireLoadEvent(long frameID, int state, String url, 2515 String contentType, double progress, int errorCode) 2516 { 2517 for (LoadListenerClient l : loadListenerClients) { 2518 l.dispatchLoadEvent(frameID, state, url, contentType, progress, errorCode); 2519 } 2520 } 2521 2522 private void fireResourceLoadEvent(long frameID, int state, String url, 2523 String contentType, double progress, int errorCode) 2524 { 2525 for (LoadListenerClient l : loadListenerClients) { 2526 l.dispatchResourceLoadEvent(frameID, state, url, contentType, progress, errorCode); 2527 } 2528 } 2529 2530 private void repaintAll() { 2531 dirtyRects.clear(); 2532 addDirtyRect(new WCRectangle(0, 0, width, height)); 2533 } 2534 2535 // Package scope method for testing 2536 int test_getFramesCount() { 2537 return frames.size(); 2538 } 2539 2540 // ************************************************************************* 2541 // Native methods 2542 // ************************************************************************* 2543 2544 private static native void twkInitWebCore(boolean useJIT, boolean useDFGJIT); 2545 private native long twkCreatePage(boolean editable); 2546 private native void twkInit(long pPage, boolean usePlugins, float devicePixelScale); 2547 private native void twkDestroyPage(long pPage); 2548 2549 private native long twkGetMainFrame(long pPage); 2550 private native long twkGetParentFrame(long pFrame); 2551 private native long[] twkGetChildFrames(long pFrame); 2552 2553 private native String twkGetName(long pFrame); 2554 private native String twkGetURL(long pFrame); 2555 private native String twkGetInnerText(long pFrame); 2556 private native String twkGetRenderTree(long pFrame); 2557 private native String twkGetContentType(long pFrame); 2558 private native String twkGetTitle(long pFrame); 2559 private native String twkGetIconURL(long pFrame); 2560 private native static Document twkGetDocument(long pFrame); 2561 private native static Element twkGetOwnerElement(long pFrame); 2562 2563 private native void twkOpen(long pFrame, String url); 2564 private native void twkOverridePreference(long pPage, String key, String value); 2565 private native void twkResetToConsistentStateBeforeTesting(long pPage); 2566 private native void twkLoad(long pFrame, String text, String contentType); 2567 private native boolean twkIsLoading(long pFrame); 2568 private native void twkStop(long pFrame); 2569 private native void twkStopAll(long pPage); // sync 2570 private native void twkRefresh(long pFrame); 2571 2572 private native boolean twkGoBackForward(long pPage, int distance); 2573 2574 private native boolean twkCopy(long pFrame); 2575 private native boolean twkFindInPage(long pPage, 2576 String stringToFind, boolean forward, 2577 boolean wrap, boolean matchCase); 2578 private native boolean twkFindInFrame(long pFrame, 2579 String stringToFind, boolean forward, 2580 boolean wrap, boolean matchCase); 2581 2582 private native float twkGetZoomFactor(long pFrame, boolean textOnly); 2583 private native void twkSetZoomFactor(long pFrame, float zoomFactor, boolean textOnly); 2584 2585 private native Object twkExecuteScript(long pFrame, String script); 2586 2587 private native void twkReset(long pFrame); 2588 2589 private native int twkGetFrameHeight(long pFrame); 2590 private native int twkBeginPrinting(long pPage, float width, float height); 2591 private native void twkEndPrinting(long pPage); 2592 private native void twkPrint(long pPage, WCRenderQueue gc, int pageNumber, float width); 2593 private native float twkAdjustFrameHeight(long pFrame, float oldTop, float oldBottom, float bottomLimit); 2594 2595 private native int[] twkGetVisibleRect(long pFrame); 2596 private native void twkScrollToPosition(long pFrame, int x, int y); 2597 private native int[] twkGetContentSize(long pFrame); 2598 private native void twkSetTransparent(long pFrame, boolean isTransparent); 2599 private native void twkSetBackgroundColor(long pFrame, int backgroundColor); 2600 2601 private native void twkSetBounds(long pPage, int x, int y, int w, int h); 2602 private native void twkPrePaint(long pPage); 2603 private native void twkUpdateContent(long pPage, WCRenderQueue rq, int x, int y, int w, int h); 2604 private native void twkPostPaint(long pPage, WCRenderQueue rq, 2605 int x, int y, int w, int h); 2606 2607 private native String twkGetEncoding(long pPage); 2608 private native void twkSetEncoding(long pPage, String encoding); 2609 2610 private native void twkProcessFocusEvent(long pPage, int id, int direction); 2611 private native boolean twkProcessKeyEvent(long pPage, int type, String text, 2612 String keyIdentifier, 2613 int windowsVirtualKeyCode, 2614 boolean shift, boolean ctrl, 2615 boolean alt, boolean meta, double when); 2616 private native boolean twkProcessMouseEvent(long pPage, int id, 2617 int button, int clickCount, 2618 int x, int y, int sx, int sy, 2619 boolean shift, boolean control, boolean alt, boolean meta, 2620 boolean popupTrigger, double when); 2621 private native boolean twkProcessMouseWheelEvent(long pPage, 2622 int x, int y, int sx, int sy, 2623 float dx, float dy, 2624 boolean shift, boolean control, boolean alt, boolean meta, 2625 double when); 2626 private native boolean twkProcessInputTextChange(long pPage, String committed, String composed, 2627 int[] attributes, int caretPosition); 2628 private native boolean twkProcessCaretPositionChange(long pPage, int caretPosition); 2629 private native int[] twkGetTextLocation(long pPage, int charIndex); 2630 private native int twkGetInsertPositionOffset(long pPage); 2631 private native int twkGetCommittedTextLength(long pPage); 2632 private native String twkGetCommittedText(long pPage); 2633 private native String twkGetSelectedText(long pPage); 2634 2635 private native int twkProcessDrag(long page, 2636 int commandId, 2637 String[] mimeTypes, String[] values, 2638 int x, int y, 2639 int screenX, int screenY, 2640 int dndActionId); 2641 2642 private native boolean twkExecuteCommand(long page, String command, 2643 String value); 2644 private native boolean twkQueryCommandEnabled(long page, String command); 2645 private native boolean twkQueryCommandState(long page, String command); 2646 private native String twkQueryCommandValue(long page, String command); 2647 private native boolean twkIsEditable(long page); 2648 private native void twkSetEditable(long page, boolean editable); 2649 private native String twkGetHtml(long pFrame); 2650 2651 private native boolean twkGetUsePageCache(long page); 2652 private native void twkSetUsePageCache(long page, boolean usePageCache); 2653 private native boolean twkGetDeveloperExtrasEnabled(long page); 2654 private native void twkSetDeveloperExtrasEnabled(long page, 2655 boolean enabled); 2656 private native boolean twkIsJavaScriptEnabled(long page); 2657 private native void twkSetJavaScriptEnabled(long page, boolean enable); 2658 private native boolean twkIsContextMenuEnabled(long page); 2659 private native void twkSetContextMenuEnabled(long page, boolean enable); 2660 private native void twkSetUserStyleSheetLocation(long page, String url); 2661 private native String twkGetUserAgent(long page); 2662 private native void twkSetUserAgent(long page, String userAgent); 2663 private native void twkSetLocalStorageDatabasePath(long page, String path); 2664 private native void twkSetLocalStorageEnabled(long page, boolean enabled); 2665 2666 private native int twkGetUnloadEventListenersCount(long pFrame); 2667 2668 private native void twkConnectInspectorFrontend(long pPage); 2669 private native void twkDisconnectInspectorFrontend(long pPage); 2670 private native void twkDispatchInspectorMessageFromFrontend(long pPage, 2671 String message); 2672 private static native void twkDoJSCGarbageCollection(); 2673 }