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