Web Inspector: Instrument active pixel memory used by canvases
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 29 Jun 2017 01:34:51 +0000 (01:34 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 29 Jun 2017 01:34:51 +0000 (01:34 +0000)
https://bugs.webkit.org/show_bug.cgi?id=173087
<rdar://problem/32719261>

Reviewed by Joseph Pecoraro.

Source/JavaScriptCore:

* inspector/protocol/Canvas.json:
 - Add optional `memoryCost` attribute to the `Canvas` type.
 - Add `canvasMemoryChanged` event that is dispatched when the `memoryCost` of a canvas changes.

Source/WebCore:

Test: inspector/canvas/memory.html

* html/HTMLCanvasElement.cpp:
(WebCore::HTMLCanvasElement::setImageBuffer):
* inspector/InspectorCanvasAgent.h:
* inspector/InspectorCanvasAgent.cpp:
(WebCore::InspectorCanvasAgent::didChangeCanvasMemory):
(WebCore::InspectorCanvasAgent::buildObjectForCanvas):
* inspector/InspectorInstrumentation.h:
* inspector/InspectorInstrumentation.cpp:
(WebCore::InspectorInstrumentation::didChangeCanvasMemory):
(WebCore::InspectorInstrumentation::didChangeCanvasMemoryImpl):

Source/WebInspectorUI:

* .eslintrc:
* UserInterface/Controllers/CanvasManager.js:
(WebInspector.CanvasManager.prototype.canvasMemoryChanged):
* UserInterface/Models/Canvas.js:
(WebInspector.Canvas.fromPayload):
(WebInspector.Canvas.prototype.get memoryCost):
(WebInspector.Canvas.prototype.set memoryCost):
* UserInterface/Protocol/CanvasObserver.js:
(WebInspector.CanvasObserver.prototype.canvasMemoryChanged):
* UserInterface/Views/CanvasDetailsSidebarPanel.js:
(WebInspector.CanvasDetailsSidebarPanel.prototype.initialLayout):
(WebInspector.CanvasDetailsSidebarPanel.prototype._refreshIdentitySection):
(WebInspector.CanvasDetailsSidebarPanel.prototype._formatMemoryRow):
(WebInspector.CanvasDetailsSidebarPanel.prototype._canvasMemoryChanged):

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Base/Utilities.js:
(Number.bytesToString.value):
Add support for gigabyte numbers.

LayoutTests:

* inspector/canvas/memory-expected.txt: Added.
* inspector/canvas/memory.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@218908 268f45cc-cd09-0410-ab3c-d52691b4dbfc

19 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/canvas/memory-expected.txt [new file with mode: 0644]
LayoutTests/inspector/canvas/memory.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/protocol/Canvas.json
Source/WebCore/ChangeLog
Source/WebCore/html/HTMLCanvasElement.cpp
Source/WebCore/inspector/InspectorCanvasAgent.cpp
Source/WebCore/inspector/InspectorCanvasAgent.h
Source/WebCore/inspector/InspectorInstrumentation.cpp
Source/WebCore/inspector/InspectorInstrumentation.h
Source/WebInspectorUI/.eslintrc
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Base/Utilities.js
Source/WebInspectorUI/UserInterface/Controllers/CanvasManager.js
Source/WebInspectorUI/UserInterface/Models/Canvas.js
Source/WebInspectorUI/UserInterface/Protocol/CanvasObserver.js
Source/WebInspectorUI/UserInterface/Views/CanvasDetailsSidebarPanel.js

index 421cb1a..3e341f9 100644 (file)
@@ -1,3 +1,14 @@
+2017-06-28  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Instrument active pixel memory used by canvases
+        https://bugs.webkit.org/show_bug.cgi?id=173087
+        <rdar://problem/32719261>
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/canvas/memory-expected.txt: Added.
+        * inspector/canvas/memory.html: Added.
+
 2017-06-28  Ryan Haddad  <ryanhaddad@apple.com>
 
         Skip webrtc/video-replace-muted-track.html on iOS.
diff --git a/LayoutTests/inspector/canvas/memory-expected.txt b/LayoutTests/inspector/canvas/memory-expected.txt
new file mode 100644 (file)
index 0000000..6c9606b
--- /dev/null
@@ -0,0 +1,11 @@
+Test that CanvasManager tracks canvas memory costs and is notified of changes.
+
+
+== Running test suite: Canvas.memory
+-- Running test case: Canvas.memory.memoryCost
+Memory cost of canvas is NaN.
+
+-- Running test case: Canvas.memory.canvasMemoryChanged
+Change size of canvas to 200x200.
+Memory cost of canvas updated to 166400.
+
diff --git a/LayoutTests/inspector/canvas/memory.html b/LayoutTests/inspector/canvas/memory.html
new file mode 100644 (file)
index 0000000..76df193
--- /dev/null
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+function load() {
+    window.context = document.body.appendChild(document.createElement("canvas")).getContext("2d");
+
+    runTest();
+}
+
+function changeSize(width, height) {
+    window.context.canvas.width = width;
+    window.context.canvas.height = height;
+
+    // Force the creation of the image buffer, which is used to determine memory cost.
+    window.context.canvas.toDataURL();
+}
+
+function test() {
+    let suite = InspectorTest.createAsyncSuite("Canvas.memory");
+
+    suite.addTestCase({
+        name: "Canvas.memory.memoryCost",
+        description: "Check that memory cost is sent with the canvas when able.",
+        test(resolve, reject) {
+            // NOTE: the memory cost of a canvas will not be retrievable until an operation is
+            // performed on that canvas that requires the image buffer.  A blank canvas that was
+            // just created will not have a buffer, so its memory cost will be 0/NaN.
+
+            let canvases = WebInspector.canvasManager.canvases.filter((canvas) => canvas.contextType === WebInspector.Canvas.ContextType.Canvas2D);
+            if (!canvases.length) {
+                reject("Missing 2D canvas.");
+                return;
+            }
+
+            InspectorTest.assert(canvases.length === 1, "There should only be one canvas-2d.");
+
+            InspectorTest.log(`Memory cost of canvas is ${canvases[0].memoryCost}.`);
+            resolve();
+        }
+    });
+
+    suite.addTestCase({
+        name: "Canvas.memory.canvasMemoryChanged",
+        description: "Check that memory cost is updated when the backend value changes.",
+        test(resolve, reject) {
+            let canvases = WebInspector.canvasManager.canvases.filter((canvas) => canvas.contextType === WebInspector.Canvas.ContextType.Canvas2D);
+            if (!canvases.length) {
+                reject("Missing 2D canvas.");
+                return;
+            }
+
+            InspectorTest.assert(canvases.length === 1, "There should only be one canvas-2d.");
+
+            canvases[0].awaitEvent(WebInspector.Canvas.Event.MemoryChanged)
+            .then((event) => {
+                InspectorTest.log(`Memory cost of canvas updated to ${event.target.memoryCost}.`);
+            })
+            .then(resolve, reject);
+
+            const width = 200;
+            const height = 200;
+            InspectorTest.log(`Change size of canvas to ${width}x${height}.`);
+            InspectorTest.evaluateInPage(`changeSize(${width}, ${height})`);
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="load()">
+    <p>Test that CanvasManager tracks canvas memory costs and is notified of changes.</p>
+</body>
+</html>
index 6f9b046..606623b 100644 (file)
@@ -1,3 +1,15 @@
+2017-06-28  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Instrument active pixel memory used by canvases
+        https://bugs.webkit.org/show_bug.cgi?id=173087
+        <rdar://problem/32719261>
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/protocol/Canvas.json:
+         - Add optional `memoryCost` attribute to the `Canvas` type.
+         - Add `canvasMemoryChanged` event that is dispatched when the `memoryCost` of a canvas changes.
+
 2017-06-28  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: Cleanup Protocol JSON files
index 72f520b..470ad73 100644 (file)
@@ -38,7 +38,8 @@
                 { "name": "frameId", "$ref": "Network.FrameId", "description": "Parent frame identifier." },
                 { "name": "nodeId", "$ref": "DOM.NodeId", "optional": true, "description": "The corresponding DOM node id." },
                 { "name": "cssCanvasName", "type": "string", "optional": true, "description": "The CSS canvas identifier, for canvases created with <code>document.getCSSCanvasContext</code>." },
-                { "name": "contextAttributes", "$ref": "ContextAttributes", "optional": true, "description": "Context attributes for WebGL rendering contexts." }
+                { "name": "contextAttributes", "$ref": "ContextAttributes", "optional": true, "description": "Context attributes for WebGL rendering contexts." },
+                { "name": "memoryCost", "type": "number", "optional": true, "description": "Memory usage of the canvas in bytes." }
             ]
         }
     ],
             "parameters": [
                 { "name": "canvasId", "$ref": "CanvasId", "description": "Removed canvas identifier." }
             ]
+        },
+        {
+            "name": "canvasMemoryChanged",
+            "parameters": [
+                { "name": "canvasId", "$ref": "CanvasId", "description": "Identifier of canvas that changed." },
+                { "name": "memoryCost", "type": "number", "description": "New memory cost value for the canvas in bytes." }
+            ]
         }
     ]
 }
index 2392231..793d39e 100644 (file)
@@ -1,3 +1,24 @@
+2017-06-28  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Instrument active pixel memory used by canvases
+        https://bugs.webkit.org/show_bug.cgi?id=173087
+        <rdar://problem/32719261>
+
+        Reviewed by Joseph Pecoraro.
+
+        Test: inspector/canvas/memory.html
+
+        * html/HTMLCanvasElement.cpp:
+        (WebCore::HTMLCanvasElement::setImageBuffer):
+        * inspector/InspectorCanvasAgent.h:
+        * inspector/InspectorCanvasAgent.cpp:
+        (WebCore::InspectorCanvasAgent::didChangeCanvasMemory):
+        (WebCore::InspectorCanvasAgent::buildObjectForCanvas):
+        * inspector/InspectorInstrumentation.h:
+        * inspector/InspectorInstrumentation.cpp:
+        (WebCore::InspectorInstrumentation::didChangeCanvasMemory):
+        (WebCore::InspectorInstrumentation::didChangeCanvasMemoryImpl):
+
 2017-06-28  Alex Christensen  <achristensen@webkit.org>
 
         Prevent displaying URLs with small capital letters
index 4572d91..d33527f 100644 (file)
@@ -756,11 +756,16 @@ void HTMLCanvasElement::createImageBuffer() const
 
 void HTMLCanvasElement::setImageBuffer(std::unique_ptr<ImageBuffer> buffer) const
 {
-    removeFromActivePixelMemory(memoryCost());
+    size_t previousMemoryCost = memoryCost();
+    removeFromActivePixelMemory(previousMemoryCost);
 
     m_imageBuffer = WTFMove(buffer);
 
-    activePixelMemory += memoryCost();
+    size_t currentMemoryCost = memoryCost();
+    activePixelMemory += currentMemoryCost;
+
+    if (m_imageBuffer && previousMemoryCost != currentMemoryCost)
+        InspectorInstrumentation::didChangeCanvasMemory(const_cast<HTMLCanvasElement&>(*this));
 }
 
 GraphicsContext* HTMLCanvasElement::drawingContext() const
index d0da124..149e097 100644 (file)
@@ -180,6 +180,15 @@ void InspectorCanvasAgent::didCreateCanvasRenderingContext(HTMLCanvasElement& ca
     m_frontendDispatcher->canvasAdded(buildObjectForCanvas(newCanvasEntry, canvasElement));
 }
 
+void InspectorCanvasAgent::didChangeCanvasMemory(HTMLCanvasElement& canvasElement)
+{
+    CanvasEntry* canvasEntry = getCanvasEntry(canvasElement);
+    if (!canvasEntry)
+        return;
+
+    m_frontendDispatcher->canvasMemoryChanged(canvasEntry->identifier, canvasElement.memoryCost());
+}
+
 void InspectorCanvasAgent::canvasDestroyed(HTMLCanvasElement& canvasElement)
 {
     auto it = m_canvasEntries.find(&canvasElement);
@@ -295,6 +304,9 @@ Ref<Inspector::Protocol::Canvas::Canvas> InspectorCanvasAgent::buildObjectForCan
     }
 #endif
 
+    if (size_t memoryCost = canvasElement.memoryCost())
+        canvas->setMemoryCost(memoryCost);
+
     return canvas;
 }
 
index fa3f6af..1acf02a 100644 (file)
@@ -64,6 +64,7 @@ public:
     void frameNavigated(Frame&);
     void didCreateCSSCanvas(HTMLCanvasElement&, const String&);
     void didCreateCanvasRenderingContext(HTMLCanvasElement&);
+    void didChangeCanvasMemory(HTMLCanvasElement&);
 
     // CanvasObserver
     void canvasChanged(HTMLCanvasElement&, const FloatRect&) override { }
index 9eead09..7236442 100644 (file)
@@ -1005,6 +1005,12 @@ void InspectorInstrumentation::didCreateCanvasRenderingContextImpl(Instrumenting
         canvasAgent->didCreateCanvasRenderingContext(canvasElement);
 }
 
+void InspectorInstrumentation::didChangeCanvasMemoryImpl(InstrumentingAgents* instrumentingAgents, HTMLCanvasElement& canvasElement)
+{
+    if (InspectorCanvasAgent* canvasAgent = instrumentingAgents->inspectorCanvasAgent())
+        canvasAgent->didChangeCanvasMemory(canvasElement);
+}
+
 #if ENABLE(WEB_REPLAY)
 void InspectorInstrumentation::sessionCreatedImpl(InstrumentingAgents& instrumentingAgents, RefPtr<ReplaySession>&& session)
 {
index 3f90e25..9e7cc23 100644 (file)
@@ -247,6 +247,7 @@ public:
 
     static void didCreateCSSCanvas(HTMLCanvasElement&, const String&);
     static void didCreateCanvasRenderingContext(HTMLCanvasElement&);
+    static void didChangeCanvasMemory(HTMLCanvasElement&);
 
     static void networkStateChanged(Page&);
     static void updateApplicationCacheStatus(Frame*);
@@ -422,6 +423,7 @@ private:
 
     static void didCreateCSSCanvasImpl(InstrumentingAgents*, HTMLCanvasElement&, const String&);
     static void didCreateCanvasRenderingContextImpl(InstrumentingAgents*, HTMLCanvasElement&);
+    static void didChangeCanvasMemoryImpl(InstrumentingAgents*, HTMLCanvasElement&);
 
     static void layerTreeDidChangeImpl(InstrumentingAgents&);
     static void renderLayerDestroyedImpl(InstrumentingAgents&, const RenderLayer&);
@@ -1193,6 +1195,13 @@ inline void InspectorInstrumentation::didCreateCanvasRenderingContext(HTMLCanvas
         didCreateCanvasRenderingContextImpl(instrumentingAgents, canvasElement);
 }
 
+inline void InspectorInstrumentation::didChangeCanvasMemory(HTMLCanvasElement& canvasElement)
+{
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+    if (InstrumentingAgents* instrumentingAgents = instrumentingAgentsForDocument(&canvasElement.document()))
+        didChangeCanvasMemoryImpl(instrumentingAgents, canvasElement);
+}
+
 inline void InspectorInstrumentation::networkStateChanged(Page& page)
 {
     FAST_RETURN_IF_NO_FRONTENDS(void());
index 96fc399..3553ed3 100644 (file)
@@ -34,6 +34,7 @@
         // Agents
         "ApplicationCacheAgent": true,
         "CSSAgent": true,
+        "CanvasAgent": true,
         "ConsoleAgent": true,
         "DOMAgent": true,
         "DOMDebuggerAgent": true,
index f2b2a5f..fedc3d7 100644 (file)
@@ -1,3 +1,31 @@
+2017-06-28  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Instrument active pixel memory used by canvases
+        https://bugs.webkit.org/show_bug.cgi?id=173087
+        <rdar://problem/32719261>
+
+        Reviewed by Joseph Pecoraro.
+
+        * .eslintrc:
+        * UserInterface/Controllers/CanvasManager.js:
+        (WebInspector.CanvasManager.prototype.canvasMemoryChanged):
+        * UserInterface/Models/Canvas.js:
+        (WebInspector.Canvas.fromPayload):
+        (WebInspector.Canvas.prototype.get memoryCost):
+        (WebInspector.Canvas.prototype.set memoryCost):
+        * UserInterface/Protocol/CanvasObserver.js:
+        (WebInspector.CanvasObserver.prototype.canvasMemoryChanged):
+        * UserInterface/Views/CanvasDetailsSidebarPanel.js:
+        (WebInspector.CanvasDetailsSidebarPanel.prototype.initialLayout):
+        (WebInspector.CanvasDetailsSidebarPanel.prototype._refreshIdentitySection):
+        (WebInspector.CanvasDetailsSidebarPanel.prototype._formatMemoryRow):
+        (WebInspector.CanvasDetailsSidebarPanel.prototype._canvasMemoryChanged):
+
+        * Localizations/en.lproj/localizedStrings.js:
+        * UserInterface/Base/Utilities.js:
+        (Number.bytesToString.value):
+        Add support for gigabyte numbers.
+
 2017-06-27  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: Remove unused Inspector domain events
index 67bfbb2..9da5794 100644 (file)
@@ -5,6 +5,7 @@ localizedStrings[" (line %s)"] = " (line %s)";
 localizedStrings["${expr} = expression"] = "${expr} = expression";
 localizedStrings["%.0f B"] = "%.0f B";
 localizedStrings["%.0fms"] = "%.0fms";
+localizedStrings["%.1f GB"] = "%.1f GB";
 localizedStrings["%.1f KB"] = "%.1f KB";
 localizedStrings["%.1f MB"] = "%.1f MB";
 localizedStrings["%.1f days"] = "%.1f days";
@@ -14,6 +15,7 @@ localizedStrings["%.1fM"] = "%.1fM";
 localizedStrings["%.1fhrs"] = "%.1fhrs";
 localizedStrings["%.1fmin"] = "%.1fmin";
 localizedStrings["%.1fms"] = "%.1fms";
+localizedStrings["%.2f GB"] = "%.2f GB";
 localizedStrings["%.2f KB"] = "%.2f KB";
 localizedStrings["%.2f MB"] = "%.2f MB";
 localizedStrings["%.2f\u00d7"] = "%.2f\u00d7";
@@ -542,6 +544,7 @@ localizedStrings["Maximum maximum memory size in this recording"] = "Maximum max
 localizedStrings["Media: "] = "Media: ";
 localizedStrings["Medium"] = "Medium";
 localizedStrings["Memory"] = "Memory";
+localizedStrings["Memory usage of this canvas"] = "Memory usage of this canvas";
 localizedStrings["Memory: %s"] = "Memory: %s";
 localizedStrings["Message"] = "Message";
 localizedStrings["Method"] = "Method";
index 4aea877..d6cd034 100644 (file)
@@ -1081,9 +1081,16 @@ Object.defineProperty(Number, "bytesToString",
         }
 
         let megabytes = kilobytes / 1024;
-        if (higherResolution || Math.abs(megabytes) < 10)
-            return WebInspector.UIString("%.2f MB").format(megabytes);
-        return WebInspector.UIString("%.1f MB").format(megabytes);
+        if (Math.abs(megabytes) < 1024) {
+            if (higherResolution || Math.abs(megabytes) < 10)
+                return WebInspector.UIString("%.2f MB").format(megabytes);
+            return WebInspector.UIString("%.1f MB").format(megabytes);
+        }
+
+        let gigabytes = megabytes / 1024;
+        if (higherResolution || Math.abs(gigabytes) < 10)
+            return WebInspector.UIString("%.2f GB").format(gigabytes);
+        return WebInspector.UIString("%.1f GB").format(gigabytes);
     }
 });
 
index c7fd8a1..542cf25 100644 (file)
@@ -72,6 +72,18 @@ WebInspector.CanvasManager = class CanvasManager extends WebInspector.Object
         this.dispatchEventToListeners(WebInspector.CanvasManager.Event.CanvasWasRemoved, {canvas});
     }
 
+    canvasMemoryChanged(canvasIdentifier, memoryCost)
+    {
+        // Called from WebInspector.CanvasObserver.
+
+        let canvas = this._canvasIdentifierMap.get(canvasIdentifier);
+        console.assert(canvas);
+        if (!canvas)
+            return;
+
+        canvas.memoryCost = memoryCost;
+    }
+
     // Private
 
     _mainResourceDidChange(event)
index d32f2ee..18cca9b 100644 (file)
@@ -25,7 +25,7 @@
 
 WebInspector.Canvas = class Canvas extends WebInspector.Object
 {
-    constructor(identifier, contextType, frame, {domNode, cssCanvasName, contextAttributes} = {})
+    constructor(identifier, contextType, frame, {domNode, cssCanvasName, contextAttributes, memoryCost} = {})
     {
         super();
 
@@ -39,6 +39,7 @@ WebInspector.Canvas = class Canvas extends WebInspector.Object
         this._domNode = domNode || null;
         this._cssCanvasName = cssCanvasName || "";
         this._contextAttributes = contextAttributes || {};
+        this._memoryCost = memoryCost || NaN;
     }
 
     // Static
@@ -62,6 +63,7 @@ WebInspector.Canvas = class Canvas extends WebInspector.Object
             domNode: payload.nodeId ? WebInspector.domTreeManager.nodeForId(payload.nodeId) : null,
             cssCanvasName: payload.cssCanvasName,
             contextAttributes: payload.contextAttributes,
+            memoryCost: payload.memoryCost,
         });
     }
 
@@ -90,6 +92,21 @@ WebInspector.Canvas = class Canvas extends WebInspector.Object
     get cssCanvasName() { return this._cssCanvasName; }
     get contextAttributes() { return this._contextAttributes; }
 
+    get memoryCost()
+    {
+        return this._memoryCost;
+    }
+
+    set memoryCost(memoryCost)
+    {
+        if (memoryCost === this._memoryCost)
+            return;
+
+        this._memoryCost = memoryCost;
+
+        this.dispatchEventToListeners(WebInspector.Canvas.Event.MemoryChanged);
+    }
+
     get displayName()
     {
         if (this._cssCanvasName)
@@ -161,3 +178,7 @@ WebInspector.Canvas.ContextType = {
 };
 
 WebInspector.Canvas.ResourceSidebarType = "resource-type-canvas";
+
+WebInspector.Canvas.Event = {
+    MemoryChanged: "canvas-memory-changed",
+};
index 3652001..db38f28 100644 (file)
@@ -36,4 +36,9 @@ WebInspector.CanvasObserver = class CanvasObserver
     {
         WebInspector.canvasManager.canvasRemoved(canvasId);
     }
+
+    canvasMemoryChanged(canvasId, memoryCost)
+    {
+        WebInspector.canvasManager.canvasMemoryChanged(canvasId, memoryCost);
+    }
 };
index 3130b2f..f634401 100644 (file)
@@ -57,8 +57,6 @@ WebInspector.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends
         if (canvas === this._canvas)
             return;
 
-        this._canvas = canvas || null;
-
         if (this._node) {
             this._node.removeEventListener(WebInspector.DOMNode.Event.AttributeModified, this._refreshSourceSection, this);
             this._node.removeEventListener(WebInspector.DOMNode.Event.AttributeRemoved, this._refreshSourceSection, this);
@@ -66,6 +64,14 @@ WebInspector.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends
             this._node = null;
         }
 
+        if (this._canvas)
+            this._canvas.removeEventListener(WebInspector.Canvas.Event.MemoryChanged, this._canvasMemoryChanged, this);
+
+        this._canvas = canvas || null;
+
+        if (this._canvas)
+            this._canvas.addEventListener(WebInspector.Canvas.Event.MemoryChanged, this._canvasMemoryChanged, this);
+
         this.needsLayout();
     }
 
@@ -77,9 +83,10 @@ WebInspector.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends
 
         this._nameRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Name"));
         this._typeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Type"));
+        this._memoryRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Memory"));
 
         let identitySection = new WebInspector.DetailsSection("canvas-details", WebInspector.UIString("Identity"));
-        identitySection.groups = [new WebInspector.DetailsSectionGroup([this._nameRow, this._typeRow])];
+        identitySection.groups = [new WebInspector.DetailsSectionGroup([this._nameRow, this._typeRow, this._memoryRow])];
         this.contentView.element.appendChild(identitySection.element);
 
         this._nodeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Node"));
@@ -128,6 +135,7 @@ WebInspector.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends
 
         this._nameRow.value = this._canvas.displayName;
         this._typeRow.value = WebInspector.Canvas.displayNameForContextType(this._canvas.contextType);
+        this._formatMemoryRow();
     }
 
     _refreshSourceSection()
@@ -230,4 +238,21 @@ WebInspector.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends
 
         dataGrid.updateLayoutIfNeeded();
     }
+
+    _formatMemoryRow()
+    {
+        if (!this._canvas.memoryCost || isNaN(this._canvas.memoryCost)) {
+            this._memoryRow.value = emDash;
+            return;
+        }
+
+        let canvasMemory = Number.bytesToString(this._canvas.memoryCost);
+        this._memoryRow.value = canvasMemory;
+        this._memoryRow.tooltip = WebInspector.UIString("Memory usage of this canvas");
+    }
+
+    _canvasMemoryChanged(event)
+    {
+        this._formatMemoryRow();
+    }
 };