Changeset 114147 in webkit


Ignore:
Timestamp:
Apr 13, 2012 11:06:50 AM (12 years ago)
Author:
shawnsingh@chromium.org
Message:

[chromium] Support CCHeadsUpDisplay in threaded compositing mode
https://bugs.webkit.org/show_bug.cgi?id=67499

Reviewed by James Robinson.

No new tests because this code is debugging code itself.

The last item that was needed to make the CCHeadsUpDisplay work in
threaded compositing mode was to remove the font rendering code
used on the impl-side thread. To solve this, this patch adds a
CCFontAtlas that is initialized on the main thread (where the font
rendering takes place). Then, when the HUD draws text on the impl
thread, it uses the font atlas directly.

  • WebCore.gypi:
  • platform/graphics/chromium/LayerRendererChromium.cpp:

(WebCore::LayerRendererChromium::create):
(WebCore::LayerRendererChromium::initialize):

  • platform/graphics/chromium/LayerRendererChromium.h:

(WebCore):
(LayerRendererChromium):

  • platform/graphics/chromium/cc/CCFontAtlas.cpp: Added.

(WebCore):
(WebCore::CCFontAtlas::CCFontAtlas):
(WebCore::wrapPositionIfNeeded):
(WebCore::CCFontAtlas::generateAtlasForFont):
(WebCore::CCFontAtlas::initialize):
(WebCore::CCFontAtlas::drawText):
(WebCore::CCFontAtlas::drawOneLineOfTextInternal):
(WebCore::CCFontAtlas::drawDebugAtlas):

  • platform/graphics/chromium/cc/CCFontAtlas.h: Added.

(WebCore):
(CCFontAtlas):
(WebCore::CCFontAtlas::create):

  • platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp:

(WebCore::CCHeadsUpDisplay::CCHeadsUpDisplay):
(WebCore):
(WebCore::CCHeadsUpDisplay::showPlatformLayerTree):
(WebCore::CCHeadsUpDisplay::drawHudContents):
(WebCore::CCHeadsUpDisplay::drawFPSCounter):
(WebCore::CCHeadsUpDisplay::drawFPSCounterText):
(WebCore::CCHeadsUpDisplay::drawPlatformLayerTree):

  • platform/graphics/chromium/cc/CCHeadsUpDisplay.h:

(WebCore::CCHeadsUpDisplay::create):
(CCHeadsUpDisplay):

  • platform/graphics/chromium/cc/CCLayerTreeHost.cpp:

(WebCore::CCLayerTreeHost::initialize):

  • platform/graphics/chromium/cc/CCLayerTreeHost.h:

(WebCore):
(WebCore::CCLayerTreeHost::headsUpDisplayFontAtlas):
(CCLayerTreeHost):

  • platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp:

(WebCore::CCLayerTreeHostImpl::initializeLayerRenderer):

  • platform/graphics/chromium/cc/CCLayerTreeHostImpl.h:

(WebCore):
(CCLayerTreeHostImpl):

  • platform/graphics/chromium/cc/CCSingleThreadProxy.cpp:

(WebCore::CCSingleThreadProxy::initializeLayerRenderer):
(WebCore::CCSingleThreadProxy::recreateContext):

  • platform/graphics/chromium/cc/CCThreadProxy.cpp:

(WebCore::CCThreadProxy::initializeLayerRendererOnImplThread):
(WebCore::CCThreadProxy::recreateContextOnImplThread):

Location:
trunk/Source/WebCore
Files:
2 added
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r114140 r114147  
     12012-04-12  Shawn Singh  <shawnsingh@chromium.org>
     2
     3        [chromium] Support CCHeadsUpDisplay in threaded compositing mode
     4        https://bugs.webkit.org/show_bug.cgi?id=67499
     5
     6        Reviewed by James Robinson.
     7
     8        No new tests because this code is debugging code itself.
     9
     10        The last item that was needed to make the CCHeadsUpDisplay work in
     11        threaded compositing mode was to remove the font rendering code
     12        used on the impl-side thread. To solve this, this patch adds a
     13        CCFontAtlas that is initialized on the main thread (where the font
     14        rendering takes place). Then, when the HUD draws text on the impl
     15        thread, it uses the font atlas directly.
     16
     17        * WebCore.gypi:
     18        * platform/graphics/chromium/LayerRendererChromium.cpp:
     19        (WebCore::LayerRendererChromium::create):
     20        (WebCore::LayerRendererChromium::initialize):
     21        * platform/graphics/chromium/LayerRendererChromium.h:
     22        (WebCore):
     23        (LayerRendererChromium):
     24        * platform/graphics/chromium/cc/CCFontAtlas.cpp: Added.
     25        (WebCore):
     26        (WebCore::CCFontAtlas::CCFontAtlas):
     27        (WebCore::wrapPositionIfNeeded):
     28        (WebCore::CCFontAtlas::generateAtlasForFont):
     29        (WebCore::CCFontAtlas::initialize):
     30        (WebCore::CCFontAtlas::drawText):
     31        (WebCore::CCFontAtlas::drawOneLineOfTextInternal):
     32        (WebCore::CCFontAtlas::drawDebugAtlas):
     33        * platform/graphics/chromium/cc/CCFontAtlas.h: Added.
     34        (WebCore):
     35        (CCFontAtlas):
     36        (WebCore::CCFontAtlas::create):
     37        * platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp:
     38        (WebCore::CCHeadsUpDisplay::CCHeadsUpDisplay):
     39        (WebCore):
     40        (WebCore::CCHeadsUpDisplay::showPlatformLayerTree):
     41        (WebCore::CCHeadsUpDisplay::drawHudContents):
     42        (WebCore::CCHeadsUpDisplay::drawFPSCounter):
     43        (WebCore::CCHeadsUpDisplay::drawFPSCounterText):
     44        (WebCore::CCHeadsUpDisplay::drawPlatformLayerTree):
     45        * platform/graphics/chromium/cc/CCHeadsUpDisplay.h:
     46        (WebCore::CCHeadsUpDisplay::create):
     47        (CCHeadsUpDisplay):
     48        * platform/graphics/chromium/cc/CCLayerTreeHost.cpp:
     49        (WebCore::CCLayerTreeHost::initialize):
     50        * platform/graphics/chromium/cc/CCLayerTreeHost.h:
     51        (WebCore):
     52        (WebCore::CCLayerTreeHost::headsUpDisplayFontAtlas):
     53        (CCLayerTreeHost):
     54        * platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp:
     55        (WebCore::CCLayerTreeHostImpl::initializeLayerRenderer):
     56        * platform/graphics/chromium/cc/CCLayerTreeHostImpl.h:
     57        (WebCore):
     58        (CCLayerTreeHostImpl):
     59        * platform/graphics/chromium/cc/CCSingleThreadProxy.cpp:
     60        (WebCore::CCSingleThreadProxy::initializeLayerRenderer):
     61        (WebCore::CCSingleThreadProxy::recreateContext):
     62        * platform/graphics/chromium/cc/CCThreadProxy.cpp:
     63        (WebCore::CCThreadProxy::initializeLayerRendererOnImplThread):
     64        (WebCore::CCThreadProxy::recreateContextOnImplThread):
     65
    1662012-04-13  Rob Flack  <flackr@chromium.org>
    267
  • trunk/Source/WebCore/WebCore.gypi

    r114140 r114147  
    36083608            'platform/graphics/chromium/cc/CCDrawQuad.cpp',
    36093609            'platform/graphics/chromium/cc/CCDrawQuad.h',
     3610            'platform/graphics/chromium/cc/CCFontAtlas.cpp',
     3611            'platform/graphics/chromium/cc/CCFontAtlas.h',
    36103612            'platform/graphics/chromium/cc/CCFrameRateController.cpp',
    36113613            'platform/graphics/chromium/cc/CCFrameRateController.h',
  • trunk/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp

    r114081 r114147  
    196196
    197197
    198 PassOwnPtr<LayerRendererChromium> LayerRendererChromium::create(LayerRendererChromiumClient* client, PassRefPtr<GraphicsContext3D> context)
     198PassOwnPtr<LayerRendererChromium> LayerRendererChromium::create(LayerRendererChromiumClient* client, PassRefPtr<GraphicsContext3D> context, CCFontAtlas* headsUpDisplayFontAtlas)
    199199{
    200200    OwnPtr<LayerRendererChromium> layerRenderer(adoptPtr(new LayerRendererChromium(client, context)));
    201     if (!layerRenderer->initialize())
     201    if (!layerRenderer->initialize(headsUpDisplayFontAtlas))
    202202        return nullptr;
    203203
     
    238238};
    239239
    240 bool LayerRendererChromium::initialize()
     240bool LayerRendererChromium::initialize(CCFontAtlas* headsUpDisplayFontAtlas)
    241241{
    242242    if (!m_context->makeContextCurrent())
     
    308308        return false;
    309309
    310     m_headsUpDisplay = CCHeadsUpDisplay::create(this);
     310    m_headsUpDisplay = CCHeadsUpDisplay::create(this, headsUpDisplayFontAtlas);
    311311
    312312    // Make sure the viewport and context gets initialized, even if it is to zero.
  • trunk/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.h

    r114081 r114147  
    5555namespace WebCore {
    5656
     57class CCFontAtlas;
    5758class CCHeadsUpDisplay;
    5859class CCLayerImpl;
     
    8081    WTF_MAKE_NONCOPYABLE(LayerRendererChromium);
    8182public:
    82     static PassOwnPtr<LayerRendererChromium> create(LayerRendererChromiumClient*, PassRefPtr<GraphicsContext3D>);
     83    static PassOwnPtr<LayerRendererChromium> create(LayerRendererChromiumClient*, PassRefPtr<GraphicsContext3D>, CCFontAtlas*);
    8384
    8485    ~LayerRendererChromium();
     
    174175
    175176    LayerRendererChromium(LayerRendererChromiumClient*, PassRefPtr<GraphicsContext3D>);
    176     bool initialize();
     177    bool initialize(CCFontAtlas* headsUpDisplayFontAtlas = 0);
    177178
    178179private:
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.cpp

    r114000 r114147  
    2828#include "CCHeadsUpDisplay.h"
    2929
     30#include "CCFontAtlas.h"
    3031#include "Extensions3DChromium.h"
    31 #include "Font.h"
    32 #include "FontCache.h"
    33 #include "FontDescription.h"
    3432#include "GraphicsContext3D.h"
    3533#include "InspectorController.h"
     
    4947using namespace std;
    5048
    51 CCHeadsUpDisplay::CCHeadsUpDisplay(LayerRendererChromium* owner)
     49CCHeadsUpDisplay::CCHeadsUpDisplay(LayerRendererChromium* owner, CCFontAtlas* headsUpDisplayFontAtlas)
    5250    : m_currentFrameNumber(1)
    5351    , m_layerRenderer(owner)
    5452    , m_useMapSubForUploads(owner->contextSupportsMapSub())
     53    , m_fontAtlas(headsUpDisplayFontAtlas)
    5554{
    5655    m_beginTimeHistoryInSec[0] = currentTime();
     
    5857    for (int i = 2; i < kBeginFrameHistorySize; i++)
    5958        m_beginTimeHistoryInSec[i] = 0;
    60 
    61     // We can't draw text in threaded mode with the current mechanism.
    62     // FIXME: Figure out a way to draw text in threaded mode.
    63     if (!CCProxy::implThread())
    64         initializeFonts();
    6559}
    6660
     
    7266const double CCHeadsUpDisplay::kFrameTooFast = 1.0 / 70;
    7367
    74 void CCHeadsUpDisplay::initializeFonts()
    75 {
    76     ASSERT(!CCProxy::implThread());
    77     FontDescription mediumFontDesc;
    78     mediumFontDesc.setGenericFamily(FontDescription::MonospaceFamily);
    79     mediumFontDesc.setComputedSize(12);
    80 
    81     m_mediumFont = adoptPtr(new Font(mediumFontDesc, 0, 0));
    82     m_mediumFont->update(0);
    83 
    84     FontDescription smallFontDesc;
    85     smallFontDesc.setGenericFamily(FontDescription::MonospaceFamily);
    86     smallFontDesc.setComputedSize(10);
    87 
    88     m_smallFont = adoptPtr(new Font(smallFontDesc, 0, 0));
    89     m_smallFont->update(0);
    90 }
    91 
    9268// safeMod works on -1, returning m-1 in that case.
    9369static inline int safeMod(int number, int modulus)
     
    11894bool CCHeadsUpDisplay::showPlatformLayerTree() const
    11995{
    120     return settings().showPlatformLayerTree && !CCProxy::implThread();
     96    return settings().showPlatformLayerTree;
    12197}
    12298
     
    205181
    206182    int fpsCounterHeight = 40;
    207     if (!CCProxy::implThread())
    208         fpsCounterHeight += m_mediumFont->fontMetrics().floatHeight();
    209 
    210183    int fpsCounterTop = 2;
    211184    int platformLayerTreeTop;
     
    219192
    220193    if (showPlatformLayerTree())
    221         drawPlatformLayerTree(context, platformLayerTreeTop);
     194        drawPlatformLayerTree(context, hudSize, platformLayerTreeTop);
    222195}
    223196
     
    268241void CCHeadsUpDisplay::drawFPSCounter(GraphicsContext* context, int top, int height)
    269242{
    270     float textWidth = 0;
    271     if (!CCProxy::implThread())
    272         textWidth = drawFPSCounterText(context, top, height);
    273 
     243    float textWidth = 170; // so text fits on linux.
    274244    float graphWidth = kBeginFrameHistorySize;
     245
     246    // Draw the FPS text.
     247    drawFPSCounterText(context, top, textWidth, height);
    275248
    276249    // Draw FPS graph.
     
    309282}
    310283
    311 float CCHeadsUpDisplay::drawFPSCounterText(GraphicsContext* context, int top, int height)
    312 {
    313     ASSERT(!CCProxy::implThread());
    314 
    315     FontCachePurgePreventer fontCachePurgePreventer;
     284void CCHeadsUpDisplay::drawFPSCounterText(GraphicsContext* context, int top, int width, int height)
     285{
    316286    double averageFPS, stdDeviation;
    317 
    318287    getAverageFPSAndStandardDeviation(&averageFPS, &stdDeviation);
    319 
    320     String fps(String::format("FPS: %4.1f +/-%3.1f", averageFPS, stdDeviation));
    321     TextRun text(fps);
    322     float textWidth = m_mediumFont->width(text) + 2;
    323288
    324289    // Draw background.
    325290    context->setFillColor(Color(0, 0, 0, 255), ColorSpaceDeviceRGB);
    326     double fontHeight = m_mediumFont->fontMetrics().floatHeight() + 2;
    327     context->fillRect(FloatRect(2, top, textWidth, fontHeight));
     291    context->fillRect(FloatRect(2, top, width, height));
    328292
    329293    // Draw FPS text.
    330     context->setFillColor(Color(200, 200, 200), ColorSpaceDeviceRGB);
    331     context->drawText(*m_mediumFont, text, IntPoint(3, top + fontHeight - 4));
    332 
    333     return textWidth;
    334 }
    335 
    336 void CCHeadsUpDisplay::drawPlatformLayerTree(GraphicsContext* context, int top)
    337 {
    338     ASSERT(!CCProxy::implThread());
    339 
    340     FontCachePurgePreventer fontCachePurgePreventer;
    341 
    342     float smallFontHeight = m_smallFont->fontMetrics().floatHeight();
    343     int y = top + smallFontHeight - 4;
    344     context->setFillColor(Color(255, 0, 0), ColorSpaceDeviceRGB);
    345     Vector<String> lines;
    346     m_layerRenderer->layerTreeAsText().split('\n', lines);
    347     for (size_t i = 0; i < lines.size(); ++i) {
    348         context->drawText(*m_smallFont, TextRun(lines[i]), IntPoint(2, y));
    349         y += smallFontHeight;
    350     }
     294    if (m_fontAtlas)
     295        m_fontAtlas->drawText(context, String::format("FPS: %4.1f +/- %3.1f", averageFPS, stdDeviation), IntPoint(10, height / 3), IntSize(width, height));
     296}
     297
     298void CCHeadsUpDisplay::drawPlatformLayerTree(GraphicsContext* context, const IntSize hudSize, int top)
     299{
     300    if (m_fontAtlas)
     301        m_fontAtlas->drawText(context, m_layerRenderer->layerTreeAsText(), IntPoint(2, top), hudSize);
    351302}
    352303
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCHeadsUpDisplay.h

    r113918 r114147  
    2828#if USE(ACCELERATED_COMPOSITING)
    2929
    30 #include "Font.h"
     30#include "CCFontAtlas.h"
    3131#include "ProgramBinding.h"
    3232#include "ShaderChromium.h"
     
    4444    WTF_MAKE_NONCOPYABLE(CCHeadsUpDisplay);
    4545public:
    46     static PassOwnPtr<CCHeadsUpDisplay> create(LayerRendererChromium* owner)
     46    static PassOwnPtr<CCHeadsUpDisplay> create(LayerRendererChromium* owner, CCFontAtlas* headsUpDisplayFontAtlas)
    4747    {
    48         return adoptPtr(new CCHeadsUpDisplay(owner));
     48        return adoptPtr(new CCHeadsUpDisplay(owner, headsUpDisplayFontAtlas));
    4949    }
    5050
     
    6262
    6363private:
    64     explicit CCHeadsUpDisplay(LayerRendererChromium* owner);
     64    CCHeadsUpDisplay(LayerRendererChromium* owner, CCFontAtlas* headsUpDisplayFontAtlas);
    6565    void drawHudContents(GraphicsContext*, const IntSize& hudSize);
    6666    void drawFPSCounter(GraphicsContext*, int top, int height);
    67     float drawFPSCounterText(GraphicsContext*, int top, int height);
    68     void drawPlatformLayerTree(GraphicsContext*, int top);
     67    void drawFPSCounterText(GraphicsContext*, int top, int width, int height);
     68    void drawPlatformLayerTree(GraphicsContext*, const IntSize hudSize, int top);
    6969    const CCSettings& settings() const;
    7070    bool isBadFrame(int frameNumber) const;
     
    7373
    7474    bool showPlatformLayerTree() const;
    75 
    76     void initializeFonts();
    7775
    7876    int m_currentFrameNumber;
     
    9088    static const int kNumMissedFramesForReset = 5;
    9189
    92     OwnPtr<Font> m_smallFont;
    93     OwnPtr<Font> m_mediumFont;
     90    bool m_useMapSubForUploads;
    9491
    95     bool m_useMapSubForUploads;
     92    CCFontAtlas* m_fontAtlas;
    9693};
    9794
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.cpp

    r113809 r114147  
    3333#include "TraceEvent.h"
    3434#include "TreeSynchronizer.h"
     35#include "cc/CCFontAtlas.h"
    3536#include "cc/CCLayerAnimationController.h"
    3637#include "cc/CCLayerIterator.h"
     
    100101        return false;
    101102
     103    // Only allocate the font atlas if we have reason to use the heads-up display.
     104    if (m_settings.showFPSCounter || m_settings.showPlatformLayerTree) {
     105        m_headsUpDisplayFontAtlas = CCFontAtlas::create();
     106        m_headsUpDisplayFontAtlas->initialize();
     107    }
     108
    102109    m_compositorIdentifier = m_proxy->compositorIdentifier();
    103110    return true;
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.h

    r113819 r114147  
    4444namespace WebCore {
    4545
     46class CCFontAtlas;
    4647class CCLayerTreeHostImpl;
    4748class CCLayerTreeHostImplClient;
     
    221222    void deleteTextureAfterCommit(PassOwnPtr<ManagedTexture>);
    222223
     224    CCFontAtlas* headsUpDisplayFontAtlas() { return m_headsUpDisplayFontAtlas.get(); }
     225
    223226protected:
    224227    CCLayerTreeHost(CCLayerTreeHostClient*, const CCSettings&);
     
    264267    CCSettings m_settings;
    265268
     269    // This is owned by the main layer tree host because it needs to be initialized on the main thread.
     270    OwnPtr<CCFontAtlas> m_headsUpDisplayFontAtlas;
     271
    266272    IntSize m_viewportSize;
    267273    bool m_visible;
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp

    r113871 r114147  
    498498}
    499499
    500 bool CCLayerTreeHostImpl::initializeLayerRenderer(PassRefPtr<GraphicsContext3D> context)
     500bool CCLayerTreeHostImpl::initializeLayerRenderer(PassRefPtr<GraphicsContext3D> context, CCFontAtlas* headsUpDisplayFontAtlas)
    501501{
    502502    OwnPtr<LayerRendererChromium> layerRenderer;
    503     layerRenderer = LayerRendererChromium::create(this, context);
     503    layerRenderer = LayerRendererChromium::create(this, context, headsUpDisplayFontAtlas);
    504504
    505505    // Since we now have a new context/layerRenderer, we cannot continue to use the old
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.h

    r113819 r114147  
    4141class CCActiveGestureAnimation;
    4242class CCCompletionEvent;
     43class CCFontAtlas;
    4344class CCPageScaleAnimation;
    4445class CCLayerImpl;
     
    110111    int frameNumber() const { return m_frameNumber; }
    111112
    112     bool initializeLayerRenderer(PassRefPtr<GraphicsContext3D>);
     113    bool initializeLayerRenderer(PassRefPtr<GraphicsContext3D>, CCFontAtlas* headsUpDisplayFontAtlas = 0);
    113114    bool isContextLost();
    114115    LayerRendererChromium* layerRenderer() { return m_layerRenderer.get(); }
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCSingleThreadProxy.cpp

    r114063 r114147  
    133133    {
    134134        DebugScopedSetImplThread impl;
    135         bool ok = m_layerTreeHostImpl->initializeLayerRenderer(m_contextBeforeInitialization.release());
     135        bool ok = m_layerTreeHostImpl->initializeLayerRenderer(m_contextBeforeInitialization.release(), m_layerTreeHost->headsUpDisplayFontAtlas());
    136136        if (ok) {
    137137            m_layerRendererInitialized = true;
     
    157157        DebugScopedSetImplThread impl;
    158158        m_layerTreeHost->deleteContentsTexturesOnImplThread(m_layerTreeHostImpl->contentsTextureAllocator());
    159         initialized = m_layerTreeHostImpl->initializeLayerRenderer(context);
     159        initialized = m_layerTreeHostImpl->initializeLayerRenderer(context, m_layerTreeHost->headsUpDisplayFontAtlas());
    160160        if (initialized) {
    161161            m_layerRendererCapabilitiesForMainThread = m_layerTreeHostImpl->layerRendererCapabilities();
  • trunk/Source/WebCore/platform/graphics/chromium/cc/CCThreadProxy.cpp

    r113816 r114147  
    717717    ASSERT(isImplThread());
    718718    ASSERT(m_contextBeforeInitializationOnImplThread);
    719     *initializeSucceeded = m_layerTreeHostImpl->initializeLayerRenderer(m_contextBeforeInitializationOnImplThread.release());
     719    *initializeSucceeded = m_layerTreeHostImpl->initializeLayerRenderer(m_contextBeforeInitializationOnImplThread.release(), m_layerTreeHost->headsUpDisplayFontAtlas());
    720720    if (*initializeSucceeded) {
    721721        *capabilities = m_layerTreeHostImpl->layerRendererCapabilities();
     
    754754    ASSERT(isImplThread());
    755755    m_layerTreeHost->deleteContentsTexturesOnImplThread(m_layerTreeHostImpl->contentsTextureAllocator());
    756     *recreateSucceeded = m_layerTreeHostImpl->initializeLayerRenderer(adoptRef(contextPtr));
     756    *recreateSucceeded = m_layerTreeHostImpl->initializeLayerRenderer(adoptRef(contextPtr), m_layerTreeHost->headsUpDisplayFontAtlas());
    757757    if (*recreateSucceeded) {
    758758        *capabilities = m_layerTreeHostImpl->layerRendererCapabilities();
Note: See TracChangeset for help on using the changeset viewer.