1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1.  Redistributions of source code must retain the above copyright
8 *     notice, this list of conditions and the following disclaimer.
9 * 2.  Redistributions in binary form must reproduce the above copyright
10 *     notice, this list of conditions and the following disclaimer in the
11 *     documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "config.h"
26
27#include "platform/graphics/Canvas2DLayerBridge.h"
28
29#include "SkDeferredCanvas.h"
30#include "SkSurface.h"
31#include "platform/graphics/ImageBuffer.h"
32#include "platform/graphics/test/MockWebGraphicsContext3D.h"
33#include "public/platform/Platform.h"
34#include "public/platform/WebExternalBitmap.h"
35#include "public/platform/WebGraphicsContext3DProvider.h"
36#include "public/platform/WebThread.h"
37#include "third_party/skia/include/core/SkDevice.h"
38#include "wtf/RefPtr.h"
39
40#include <gmock/gmock.h>
41#include <gtest/gtest.h>
42
43using namespace blink;
44using testing::InSequence;
45using testing::Return;
46using testing::Test;
47
48namespace {
49
50class MockCanvasContext : public MockWebGraphicsContext3D {
51public:
52    MOCK_METHOD0(flush, void(void));
53    MOCK_METHOD0(createTexture, unsigned(void));
54    MOCK_METHOD1(deleteTexture, void(unsigned));
55};
56
57class MockWebGraphicsContext3DProvider : public WebGraphicsContext3DProvider {
58public:
59    MockWebGraphicsContext3DProvider(WebGraphicsContext3D* context3d)
60        : m_context3d(context3d) { }
61
62    WebGraphicsContext3D* context3d()
63    {
64        return m_context3d;
65    }
66
67    GrContext* grContext()
68    {
69        return 0;
70    }
71
72private:
73    WebGraphicsContext3D* m_context3d;
74};
75
76class Canvas2DLayerBridgePtr {
77public:
78    Canvas2DLayerBridgePtr(PassRefPtr<Canvas2DLayerBridge> layerBridge)
79        : m_layerBridge(layerBridge) { }
80
81    ~Canvas2DLayerBridgePtr()
82    {
83        m_layerBridge->beginDestruction();
84    }
85
86    Canvas2DLayerBridge* operator->() { return m_layerBridge.get(); }
87    Canvas2DLayerBridge* get() { return m_layerBridge.get(); }
88
89private:
90    RefPtr<Canvas2DLayerBridge> m_layerBridge;
91};
92
93class NullWebExternalBitmap : public WebExternalBitmap {
94public:
95    virtual WebSize size()
96    {
97        return WebSize();
98    }
99
100    virtual void setSize(WebSize)
101    {
102    }
103
104    virtual uint8* pixels()
105    {
106        return 0;
107    }
108};
109
110} // namespace
111
112class Canvas2DLayerBridgeTest : public Test {
113protected:
114    void fullLifecycleTest()
115    {
116        MockCanvasContext mainMock;
117        OwnPtr<MockWebGraphicsContext3DProvider> mainMockProvider = adoptPtr(new MockWebGraphicsContext3DProvider(&mainMock));
118        RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterPMColor(300, 150));
119        OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
120
121        ::testing::Mock::VerifyAndClearExpectations(&mainMock);
122
123        {
124            Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(mainMockProvider.release(), canvas.release(), surface, 0, NonOpaque)));
125
126            ::testing::Mock::VerifyAndClearExpectations(&mainMock);
127
128            EXPECT_CALL(mainMock, flush());
129            unsigned textureId = bridge->getBackingTexture();
130            EXPECT_EQ(textureId, 0u);
131
132            ::testing::Mock::VerifyAndClearExpectations(&mainMock);
133        } // bridge goes out of scope here
134
135        ::testing::Mock::VerifyAndClearExpectations(&mainMock);
136    }
137
138    void noDrawOnContextLostTest()
139    {
140        MockCanvasContext mainMock;
141        OwnPtr<MockWebGraphicsContext3DProvider> mainMockProvider = adoptPtr(new MockWebGraphicsContext3DProvider(&mainMock));
142        RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterPMColor(300, 150));
143        OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
144
145        ::testing::Mock::VerifyAndClearExpectations(&mainMock);
146
147        {
148            Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(mainMockProvider.release(), canvas.release(), surface, 0, NonOpaque)));
149            ::testing::Mock::VerifyAndClearExpectations(&mainMock);
150            EXPECT_TRUE(bridge->checkSurfaceValid());
151            SkPaint paint;
152            uint32_t genID = surface->generationID();
153            bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
154            EXPECT_EQ(genID, surface->generationID());
155            mainMock.fakeContextLost();
156            EXPECT_EQ(genID, surface->generationID());
157            bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
158            EXPECT_EQ(genID, surface->generationID());
159            EXPECT_FALSE(bridge->checkSurfaceValid());
160            EXPECT_EQ(genID, surface->generationID());
161            bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
162            EXPECT_EQ(genID, surface->generationID());
163            bridge->freeTransientResources();
164            EXPECT_EQ(genID, surface->generationID());
165            ::testing::Mock::VerifyAndClearExpectations(&mainMock);
166        }
167
168        ::testing::Mock::VerifyAndClearExpectations(&mainMock);
169    }
170
171    void prepareMailboxWithBitmapTest()
172    {
173        MockCanvasContext mainMock;
174        RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterPMColor(300, 150));
175        OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
176        OwnPtr<MockWebGraphicsContext3DProvider> mainMockProvider = adoptPtr(new MockWebGraphicsContext3DProvider(&mainMock));
177        Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(mainMockProvider.release(), canvas.release(), surface, 0, NonOpaque)));
178        bridge->m_lastImageId = 1;
179
180        NullWebExternalBitmap bitmap;
181        bridge->prepareMailbox(0, &bitmap);
182        EXPECT_EQ(0u, bridge->m_lastImageId);
183    }
184
185    void prepareMailboxAndLoseResourceTest()
186    {
187        MockCanvasContext mainMock;
188        RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterPMColor(300, 150));
189        bool lostResource = true;
190
191        // Prepare a mailbox, then report the resource as lost.
192        // This test passes by not crashing and not triggering assertions.
193        {
194            WebExternalTextureMailbox mailbox;
195            OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
196            OwnPtr<MockWebGraphicsContext3DProvider> mainMockProvider = adoptPtr(new MockWebGraphicsContext3DProvider(&mainMock));
197            Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(mainMockProvider.release(), canvas.release(), surface, 0, NonOpaque)));
198            bridge->prepareMailbox(&mailbox, 0);
199            bridge->mailboxReleased(mailbox, lostResource);
200        }
201
202        // Retry with mailbox released while bridge destruction is in progress
203        {
204            OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
205            OwnPtr<MockWebGraphicsContext3DProvider> mainMockProvider = adoptPtr(new MockWebGraphicsContext3DProvider(&mainMock));
206            WebExternalTextureMailbox mailbox;
207            Canvas2DLayerBridge* rawBridge;
208            {
209                Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(mainMockProvider.release(), canvas.release(), surface, 0, NonOpaque)));
210                bridge->prepareMailbox(&mailbox, 0);
211                rawBridge = bridge.get();
212            } // bridge goes out of scope, but object is kept alive by self references
213            // before fixing crbug.com/411864, the following line you cause a memory use after free
214            // that sometimes causes a crash in normal builds and crashes consistently with ASAN.
215            rawBridge->mailboxReleased(mailbox, lostResource); // This should self-destruct the bridge.
216        }
217    }
218};
219
220namespace {
221
222TEST_F(Canvas2DLayerBridgeTest, testFullLifecycleSingleThreaded)
223{
224    fullLifecycleTest();
225}
226
227TEST_F(Canvas2DLayerBridgeTest, testNoDrawOnContextLost)
228{
229    noDrawOnContextLostTest();
230}
231
232TEST_F(Canvas2DLayerBridgeTest, testPrepareMailboxWithBitmap)
233{
234    prepareMailboxWithBitmapTest();
235}
236
237TEST_F(Canvas2DLayerBridgeTest, testPrepareMailboxAndLoseResource)
238{
239    prepareMailboxAndLoseResourceTest();
240}
241
242} // namespace
243