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