Changeset 109748 in webkit


Ignore:
Timestamp:
Mar 5, 2012 7:52:16 AM (12 years ago)
Author:
noam.rosenthal@nokia.com
Message:

[Qt] [WK2] Support threaded renderer in WK2
https://bugs.webkit.org/show_bug.cgi?id=76661

Source/WebKit2:

Made the appropriate fixes in the UI process code to make rendering thread-safe.

  • Separated the scenegraph node code to QtWebPageSGNode. QtWebPageSGNode has direct access to LayerTreeHostProxy.
  • Each function in LayerTreeHostProxy can be either called from the main thread (handling messages from the web process), or from the renderer thread (handling the GL context). The render-queue is locked with a mutex, and messages back to the web process are sent via callOnMainThread.
  • LayerTreeHostProxy is now ThreadSafeRefCounted. That is done to make sure that the GL resources it creates are only freed when the QtWebPageSGNode is deleted, which can be before or after the owning DrawingAreaProxy is deleted. This ensures that the class is deleted only after its GL resources are freed, otherwise those resources may leak.

Based on a patch by Viatcheslav Ostapenko.

Reviewed by Kenneth Rohde Christiansen.

  • Target.pri: Added new files.
  • UIProcess/API/qt/qquickwebpage.cpp: Moved QtWebPageSGNode out.

(QQuickWebPage::updatePaintNode): Call QtWebPageSGNode
(QQuickWebPagePrivate::updateSize): Call QtWebPageSGNode
(QQuickWebPagePrivate::didDeleteSGWebPageNode): Override QtWebPageSGNode::Client
(QQuickWebPagePrivate::~QQuickWebPagePrivate):

  • UIProcess/API/qt/qquickwebpage_p_p.h:

(QQuickWebPagePrivate):

  • UIProcess/DrawingAreaProxy.h:

(WebKit):
(WebKit::DrawingAreaProxy::layerTreeHostProxy): Made LayerTreeHostProxy ref-counted.
(DrawingAreaProxy):

  • UIProcess/DrawingAreaProxyImpl.cpp:

(WebKit::DrawingAreaProxyImpl::DrawingAreaProxyImpl):
(WebKit::DrawingAreaProxyImpl::enterAcceleratedCompositingMode):

  • UIProcess/LayerTreeHostProxy.h:

(WebKit):
(WebKit::LayerTreeHostProxy::create):
(LayerTreeHostProxy):

  • UIProcess/qt/LayerTreeHostProxyQt.cpp:

(WebKit::LayerTreeHostProxy::paintToCurrentGLContext):
(WebKit):
(MainThreadGuardedInvoker):

A class that allows invoking functions in the main thread, while guarding a ref-
counted object.

(WebKit::MainThreadGuardedInvoker::call):
(WebKit::MainThreadGuardedInvoker::MainThreadGuardedInvoker):
(WebKit::MainThreadGuardedInvoker::invoke):
(WebKit::LayerTreeHostProxy::syncAnimations):
(WebKit::LayerTreeHostProxy::updateViewport):
(WebKit::LayerTreeHostProxy::detachDrawingArea):
(WebKit::LayerTreeHostProxy::syncLayerParameters):
(WebKit::LayerTreeHostProxy::setShouldRenderNextFrame):
(WebKit::LayerTreeHostProxy::flushLayerChanges):
(WebKit::LayerTreeHostProxy::ensureRootLayer):
(WebKit::LayerTreeHostProxy::syncRemoteContent):
(WebKit::LayerTreeHostProxy::dispatchUpdate):
(WebKit::LayerTreeHostProxy::createDirectlyCompositedImage):
(WebKit::LayerTreeHostProxy::purgeGLResources):

  • UIProcess/qt/QtWebPageSGNode.cpp: Added.
  • UIProcess/qt/QtWebPageSGNode.h: Added.

Tools:

Remove the QML_NO_THREADED_RENDERER environment variable from MiniBrowser.

Reviewed by Kenneth Rohde Christiansen.

  • MiniBrowser/qt/main.cpp:

(main):

Location:
trunk
Files:
2 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit2/ChangeLog

    r109695 r109748  
     12012-03-05  No'am Rosenthal  <noam.rosenthal@nokia.com>
     2
     3        [Qt] [WK2] Support threaded renderer in WK2
     4        https://bugs.webkit.org/show_bug.cgi?id=76661
     5
     6        Made the appropriate fixes in the UI process code to make rendering thread-safe.
     7        - Separated the scenegraph node code to QtWebPageSGNode. QtWebPageSGNode has direct access
     8          to LayerTreeHostProxy.
     9
     10        - Each function in LayerTreeHostProxy can be either called from the main thread (handling
     11          messages from the web process), or from the renderer thread (handling the GL context).
     12          The render-queue is locked with a mutex, and messages back to the web process are sent
     13          via callOnMainThread.
     14
     15        - LayerTreeHostProxy is now ThreadSafeRefCounted. That is done to make sure that the GL
     16          resources it creates are only freed when the QtWebPageSGNode is deleted, which can be
     17          before or after the owning DrawingAreaProxy is deleted. This ensures that the class is
     18          deleted only after its GL resources are freed, otherwise those resources may leak.
     19
     20        Based on a patch by Viatcheslav Ostapenko.
     21
     22        Reviewed by Kenneth Rohde Christiansen.
     23
     24        * Target.pri: Added new files.
     25        * UIProcess/API/qt/qquickwebpage.cpp: Moved QtWebPageSGNode out.
     26        (QQuickWebPage::updatePaintNode): Call QtWebPageSGNode
     27        (QQuickWebPagePrivate::updateSize): Call QtWebPageSGNode
     28        (QQuickWebPagePrivate::didDeleteSGWebPageNode): Override QtWebPageSGNode::Client
     29        (QQuickWebPagePrivate::~QQuickWebPagePrivate):
     30        * UIProcess/API/qt/qquickwebpage_p_p.h:
     31        (QQuickWebPagePrivate):
     32        * UIProcess/DrawingAreaProxy.h:
     33        (WebKit):
     34        (WebKit::DrawingAreaProxy::layerTreeHostProxy): Made LayerTreeHostProxy ref-counted.
     35        (DrawingAreaProxy):
     36        * UIProcess/DrawingAreaProxyImpl.cpp:
     37        (WebKit::DrawingAreaProxyImpl::DrawingAreaProxyImpl):
     38        (WebKit::DrawingAreaProxyImpl::enterAcceleratedCompositingMode):
     39        * UIProcess/LayerTreeHostProxy.h:
     40        (WebKit):
     41        (WebKit::LayerTreeHostProxy::create):
     42        (LayerTreeHostProxy):
     43        * UIProcess/qt/LayerTreeHostProxyQt.cpp:
     44        (WebKit::LayerTreeHostProxy::paintToCurrentGLContext):
     45        (WebKit):
     46        (MainThreadGuardedInvoker):
     47            A class that allows invoking functions in the main thread, while guarding a ref-
     48            counted object.
     49
     50        (WebKit::MainThreadGuardedInvoker::call):
     51        (WebKit::MainThreadGuardedInvoker::MainThreadGuardedInvoker):
     52        (WebKit::MainThreadGuardedInvoker::invoke):
     53        (WebKit::LayerTreeHostProxy::syncAnimations):
     54        (WebKit::LayerTreeHostProxy::updateViewport):
     55        (WebKit::LayerTreeHostProxy::detachDrawingArea):
     56        (WebKit::LayerTreeHostProxy::syncLayerParameters):
     57        (WebKit::LayerTreeHostProxy::setShouldRenderNextFrame):
     58        (WebKit::LayerTreeHostProxy::flushLayerChanges):
     59        (WebKit::LayerTreeHostProxy::ensureRootLayer):
     60        (WebKit::LayerTreeHostProxy::syncRemoteContent):
     61        (WebKit::LayerTreeHostProxy::dispatchUpdate):
     62        (WebKit::LayerTreeHostProxy::createDirectlyCompositedImage):
     63        (WebKit::LayerTreeHostProxy::purgeGLResources):
     64        * UIProcess/qt/QtWebPageSGNode.cpp: Added.
     65        * UIProcess/qt/QtWebPageSGNode.h: Added.
     66
    1672012-03-04  Raphael Kubo da Costa  <kubo@profusion.mobi>
    268
  • trunk/Source/WebKit2/Target.pri

    r109669 r109748  
    262262    UIProcess/qt/QtPanGestureRecognizer.h \
    263263    UIProcess/qt/QtPinchGestureRecognizer.h \
     264    UIProcess/qt/QtWebPageSGNode.h \
    264265    UIProcess/qt/QtTapGestureRecognizer.h \
    265266    UIProcess/qt/QtWebError.h \
     
    593594    UIProcess/qt/QtPanGestureRecognizer.cpp \
    594595    UIProcess/qt/QtPinchGestureRecognizer.cpp \
     596    UIProcess/qt/QtWebPageSGNode.cpp \
    595597    UIProcess/qt/QtTapGestureRecognizer.cpp \
    596598    UIProcess/qt/QtWebError.cpp \
  • trunk/Source/WebKit2/UIProcess/API/qt/qquickwebpage.cpp

    r109664 r109748  
    2424#include "LayerTreeHostProxy.h"
    2525#include "QtWebPageEventHandler.h"
     26#include "QtWebPageSGNode.h"
    2627#include "TransformationMatrix.h"
    2728#include "qquickwebpage_p_p.h"
    2829#include "qquickwebview_p.h"
    29 #include <QtQuick/QQuickCanvas>
    30 #include <QtQuick/QSGGeometryNode>
    31 #include <QtQuick/QSGMaterial>
    32 #include <private/qsgrendernode_p.h>
    3330
    3431QQuickWebPage::QQuickWebPage(QQuickWebView* viewportItem)
     
    7875}
    7976
    80 void QQuickWebPagePrivate::paintToCurrentGLContext(const QTransform& transform, float opacity)
    81 {
    82     if (!q->isVisible())
    83         return;
    84 
    85     QRectF clipRect = viewportItem->mapRectToScene(viewportItem->boundingRect());
    86 
    87     if (!clipRect.isValid())
    88         return;
    89 
    90     DrawingAreaProxy* drawingArea = webPageProxy->drawingArea();
    91     if (!drawingArea)
    92         return;
    93 
    94     drawingArea->paintToCurrentGLContext(QTransform(transform).scale(contentsScale, contentsScale), opacity, clipRect);
    95 }
    96 
    97 struct PageProxyNode : public QSGRenderNode {
    98     PageProxyNode(QQuickWebPagePrivate* page)
    99         : m_pagePrivate(page)
    100     {
    101     }
    102 
    103     virtual StateFlags changedStates()
    104     {
    105         return StateFlags(StencilState) | ColorState | BlendState;
    106     }
    107 
    108     virtual void render(const RenderState&)
    109     {
    110         if (!m_pagePrivate)
    111             return;
    112         QTransform transform = matrix() ? matrix()->toTransform() : QTransform();
    113         m_pagePrivate->paintToCurrentGLContext(transform, inheritedOpacity());
    114     }
    115 
    116     ~PageProxyNode()
    117     {
    118         if (m_pagePrivate)
    119             m_pagePrivate->resetPaintNode();
    120     }
    121 
    122     QQuickWebPagePrivate* m_pagePrivate;
    123 };
    12477
    12578QSGNode* QQuickWebPage::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*)
     
    13184    }
    13285
    133     PageProxyNode* proxyNode = static_cast<PageProxyNode*>(oldNode);
    134     if (!proxyNode) {
    135         proxyNode = new PageProxyNode(d);
    136         d->m_paintNode = proxyNode;
     86    QtWebPageSGNode* sceneNode = static_cast<QtWebPageSGNode*>(oldNode);
     87    if (sceneNode)
     88        return sceneNode;
     89    sceneNode = new QtWebPageSGNode(d->webPageProxy->drawingArea()->layerTreeHostProxy(), d);
     90    {
     91        MutexLocker lock(d->m_paintNodeMutex);
     92        d->m_paintNode = sceneNode;
    13793    }
    138 
    139     return proxyNode;
     94    d->updateSize();
     95    return sceneNode;
    14096}
    14197
     
    189145    q->setSize(scaledSize);
    190146    viewportItem->updateContentsSize(scaledSize);
     147    QRectF clipRect = viewportItem->mapRectToScene(viewportItem->boundingRect());
     148
     149    MutexLocker lock(m_paintNodeMutex);
     150    if (!m_paintNode)
     151        return;
     152    m_paintNode->setClipRect(clipRect);
     153    m_paintNode->setContentsScale(contentsScale);
    191154}
    192155
    193 void QQuickWebPagePrivate::resetPaintNode()
     156void QQuickWebPagePrivate::willDeleteScenegraphNode()
    194157{
     158    MutexLocker lock(m_paintNodeMutex);
    195159    m_paintNode = 0;
    196     DrawingAreaProxy* drawingArea = webPageProxy->drawingArea();
    197     if (drawingArea && drawingArea->layerTreeHostProxy())
    198         drawingArea->layerTreeHostProxy()->purgeGLResources();
    199160}
    200161
    201162QQuickWebPagePrivate::~QQuickWebPagePrivate()
    202163{
    203     if (m_paintNode)
    204         static_cast<PageProxyNode*>(m_paintNode)->m_pagePrivate = 0;
    205164}
    206165
  • trunk/Source/WebKit2/UIProcess/API/qt/qquickwebpage_p_p.h

    r109664 r109748  
    2222#define qquickwebpage_p_p_h
    2323
     24#include "QtWebPageSGNode.h"
    2425#include "qquickwebpage_p.h"
    2526#include <QTransform>
     
    3233class QtWebPageEventHandler;
    3334
    34 class QQuickWebPagePrivate {
     35class QQuickWebPagePrivate : public QtWebPageSGNode::Client {
    3536public:
    3637    QQuickWebPagePrivate(QQuickWebPage* q, QQuickWebView* viewportItem);
    3738    ~QQuickWebPagePrivate();
     39    virtual void willDeleteScenegraphNode();
    3840
    3941    void initialize(WebKit::WebPageProxy*);
     
    4143
    4244    void updateSize();
    43 
    44     void paintToCurrentGLContext(const QTransform&, float opacity);
    4545    void paint(QPainter*);
    4646    void resetPaintNode();
     
    5151    WebKit::WebPageProxy* webPageProxy;
    5252    bool paintingIsInitialized;
    53     QSGNode* m_paintNode;
     53    QtWebPageSGNode* m_paintNode;
     54    Mutex m_paintNodeMutex;
    5455
    5556    QSizeF contentsSize;
  • trunk/Source/WebKit2/UIProcess/DrawingAreaProxy.h

    r109302 r109748  
    3030#include "BackingStore.h"
    3131#include "DrawingAreaInfo.h"
     32#include "LayerTreeHostProxy.h"
    3233#include <WebCore/IntRect.h>
    3334#include <WebCore/IntSize.h>
     
    5556
    5657class LayerTreeContext;
    57 class LayerTreeHostProxy;
    5858class UpdateInfo;
    5959class WebLayerTreeInfo;
     
    9191    virtual void paintToCurrentGLContext(const WebCore::TransformationMatrix&, float, const WebCore::FloatRect&) { }
    9292    virtual void paintLayerTree(BackingStore::PlatformGraphicsContext) { }
    93     LayerTreeHostProxy* layerTreeHostProxy() const { return m_layerTreeHostProxy.get(); }
     93    PassRefPtr<LayerTreeHostProxy> layerTreeHostProxy() const { return m_layerTreeHostProxy; }
    9494    virtual void setVisibleContentsRectForScaling(const WebCore::IntRect& visibleContentsRect, float scale) { }
    9595    virtual void setVisibleContentsRectForPanning(const WebCore::IntRect& visibleContentsRect, const WebCore::FloatPoint& trajectoryVector) { }
     
    111111
    112112#if USE(UI_SIDE_COMPOSITING)
    113     OwnPtr<LayerTreeHostProxy> m_layerTreeHostProxy;
     113    RefPtr<LayerTreeHostProxy> m_layerTreeHostProxy;
    114114#endif
    115115
  • trunk/Source/WebKit2/UIProcess/DrawingAreaProxyImpl.cpp

    r109302 r109748  
    6262    // Construct the proxy early to allow messages to be sent to the web process while AC is entered there.
    6363    if (webPageProxy->pageGroup()->preferences()->forceCompositingMode())
    64         m_layerTreeHostProxy = adoptPtr(new LayerTreeHostProxy(this));
     64        m_layerTreeHostProxy = LayerTreeHostProxy::create(this);
    6565#endif
    6666}
     
    338338#if USE(UI_SIDE_COMPOSITING)
    339339    if (!m_layerTreeHostProxy)
    340         m_layerTreeHostProxy = adoptPtr(new LayerTreeHostProxy(this));
     340        m_layerTreeHostProxy = LayerTreeHostProxy::create(this);
    341341#endif
    342342}
  • trunk/Source/WebKit2/UIProcess/LayerTreeHostProxy.h

    r109302 r109748  
    2424
    2525#include "BackingStore.h"
    26 #include "DrawingAreaProxy.h"
     26#include "Connection.h"
    2727#include "Region.h"
    2828#include "TextureMapper.h"
     
    3737#include <wtf/Functional.h>
    3838#include <wtf/HashSet.h>
    39 
     39#include <wtf/ThreadingPrimitives.h>
    4040
    4141namespace WebKit {
    4242
     43class DrawingAreaProxy;
    4344class LayerBackingStore;
    4445class WebLayerInfo;
    4546class WebLayerUpdateInfo;
    4647
    47 class LayerTreeHostProxy : public WebCore::GraphicsLayerClient {
     48class LayerTreeHostProxy : public ThreadSafeRefCounted<LayerTreeHostProxy>, public WebCore::GraphicsLayerClient {
    4849public:
    49     LayerTreeHostProxy(DrawingAreaProxy*);
    5050    virtual ~LayerTreeHostProxy();
    5151    void syncCompositingLayerState(const WebLayerInfo&);
     
    6969    void updateViewport();
    7070
     71    static PassRefPtr<LayerTreeHostProxy> create(DrawingAreaProxy* drawingArea) { return adoptRef(new LayerTreeHostProxy(drawingArea)); }
     72
    7173protected:
    7274    PassOwnPtr<WebCore::GraphicsLayer> createLayer(WebLayerID);
     
    8991
    9092    Vector<Function<void()> > m_renderQueue;
     93    WTF::Mutex m_renderQueueMutex;
    9194    void dispatchUpdate(const Function<void()>&);
     95    void detachDrawingArea();
     96    void setShouldRenderNextFrame();
    9297
    9398#if USE(TEXTURE_MAPPER)
     
    114119    void swapBuffers();
    115120    void syncAnimations();
     121    void updateViewportOnMainThread();
     122    LayerTreeHostProxy(DrawingAreaProxy*);
    116123
    117124    OwnPtr<WebCore::GraphicsLayer> m_rootLayer;
     
    120127    LayerMap m_layers;
    121128    WebLayerID m_rootLayerID;
    122     int m_id;
    123129};
    124130
  • trunk/Source/WebKit2/UIProcess/qt/LayerTreeHostProxyQt.cpp

    r109302 r109748  
    9696}
    9797
     98template<class T>
     99class MainThreadGuardedInvoker {
     100public:
     101    static void call(PassRefPtr<T> objectToGuard, const Function<void()>& function)
     102    {
     103        MainThreadGuardedInvoker<T>* invoker = new MainThreadGuardedInvoker<T>(objectToGuard, function);
     104        callOnMainThread(invoke, invoker);
     105    }
     106
     107private:
     108    MainThreadGuardedInvoker(PassRefPtr<T> object, const Function<void()>& newFunction)
     109        : objectToGuard(object)
     110        , function(newFunction)
     111    {
     112    }
     113
     114    RefPtr<T> objectToGuard;
     115    Function<void()> function;
     116    static void invoke(void* data)
     117    {
     118        MainThreadGuardedInvoker<T>* invoker = static_cast<MainThreadGuardedInvoker<T>*>(data);
     119        invoker->function();
     120        delete invoker;
     121    }
     122};
     123
    98124void LayerTreeHostProxy::syncAnimations()
    99125{
     
    103129    layer->syncAnimationsRecursively();
    104130    if (layer->descendantsOrSelfHaveRunningAnimations())
    105         updateViewport();
     131        MainThreadGuardedInvoker<LayerTreeHostProxy>::call(this, bind(&LayerTreeHostProxy::updateViewport, this));
    106132}
    107133
     
    128154void LayerTreeHostProxy::updateViewport()
    129155{
    130     m_drawingAreaProxy->updateViewport();
     156    if (m_drawingAreaProxy)
     157        m_drawingAreaProxy->updateViewport();
     158}
     159
     160void LayerTreeHostProxy::detachDrawingArea()
     161{
     162    m_drawingAreaProxy = 0;
    131163}
    132164
     
    186218            layer->removeAnimation(anim.name);
    187219            break;
    188         case WebKit::WebLayerAnimation::PauseAnimation:
     220        case WebKit::WebLayerAnimation::PauseAnimation: {
    189221            double offset = WTF::currentTime() - anim.startTime;
    190222            layer->pauseAnimation(anim.name, offset);
     223            break;
     224        }
     225        default:
    191226            break;
    192227        }
     
    296331}
    297332
     333void LayerTreeHostProxy::setShouldRenderNextFrame()
     334{
     335    // The pending tiles state is on its way to the screen, tell the web process to render the next one.
     336    m_drawingAreaProxy->page()->process()->send(Messages::LayerTreeHost::RenderNextFrame(), m_drawingAreaProxy->page()->pageID());
     337}
     338
    298339void LayerTreeHostProxy::flushLayerChanges()
    299340{
    300341    m_rootLayer->syncCompositingState(FloatRect());
    301342    swapBuffers();
    302 
    303     // The pending tiles state is on its way for the screen, tell the web process to render the next one.
    304     m_drawingAreaProxy->page()->process()->send(Messages::LayerTreeHost::RenderNextFrame(), m_drawingAreaProxy->page()->pageID());
     343    MainThreadGuardedInvoker<LayerTreeHostProxy>::call(this, bind(&LayerTreeHostProxy::setShouldRenderNextFrame, this));
    305344}
    306345
     
    316355    // The root layer should not have zero size, or it would be optimized out.
    317356    m_rootLayer->setSize(FloatSize(1.0, 1.0));
    318     if (!m_textureMapper)
    319         m_textureMapper = TextureMapper::create(TextureMapper::OpenGLMode);
    320357    toTextureMapperLayer(m_rootLayer.get())->setTextureMapper(m_textureMapper.get());
    321358}
     
    324361{
    325362    // We enqueue messages and execute them during paint, as they require an active GL context.
     363    MutexLocker locker(m_renderQueueMutex);
    326364    ensureRootLayer();
    327365
     
    334372void LayerTreeHostProxy::dispatchUpdate(const Function<void()>& function)
    335373{
     374    MutexLocker locker(m_renderQueueMutex);
    336375    m_renderQueue.append(function);
    337376    updateViewport();
     
    358397}
    359398
    360 
    361399void LayerTreeHostProxy::deleteCompositingLayer(WebLayerID id)
    362400{
     
    381419void LayerTreeHostProxy::createDirectlyCompositedImage(int64_t key, const WebKit::ShareableBitmap::Handle& handle)
    382420{
     421    // Even though ShareableBitmap is not ThreadSafeRefCounted, we can safely move it to the renderer thread as the main thread will not touch it anymore.
    383422    RefPtr<ShareableBitmap> bitmap = ShareableBitmap::create(handle);
    384423    dispatchUpdate(bind(&LayerTreeHostProxy::createImage, this, key, bitmap));
     
    404443void LayerTreeHostProxy::purgeGLResources()
    405444{
     445    MutexLocker locker(m_renderQueueMutex);
     446    m_renderQueue.clear();
    406447    TextureMapperLayer* layer = toTextureMapperLayer(rootLayer());
    407448
  • trunk/Tools/ChangeLog

    r109734 r109748  
     12012-03-05  No'am Rosenthal  <noam.rosenthal@nokia.com>
     2
     3        [Qt] [WK2] Support threaded renderer in WK2
     4        https://bugs.webkit.org/show_bug.cgi?id=76661
     5
     6        Remove the QML_NO_THREADED_RENDERER environment variable from MiniBrowser.
     7
     8        Reviewed by Kenneth Rohde Christiansen.
     9
     10        * MiniBrowser/qt/main.cpp:
     11        (main):
     12
    1132012-03-05  Alexander Færøy  <alexander.faeroy@nokia.com>
    214
  • trunk/Tools/MiniBrowser/qt/main.cpp

    r104556 r109748  
    4040int main(int argc, char** argv)
    4141{
    42     // FIXME: We must add support for the threaded rendering as it is the default.
    43     qputenv("QML_NO_THREADED_RENDERER", QByteArray("1"));
    44 
    4542    MiniBrowserApplication app(argc, argv);
    4643
Note: See TracChangeset for help on using the changeset viewer.