Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/thebes/gfxSVGGlyphs.cpp
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "gfxSVGGlyphs.h"
6
7
#include "mozilla/SVGContextPaint.h"
8
#include "nsError.h"
9
#include "nsString.h"
10
#include "nsIDocument.h"
11
#include "nsICategoryManager.h"
12
#include "nsIDocumentLoaderFactory.h"
13
#include "nsIContentViewer.h"
14
#include "nsIStreamListener.h"
15
#include "nsServiceManagerUtils.h"
16
#include "nsIPresShell.h"
17
#include "nsNetUtil.h"
18
#include "nsIInputStream.h"
19
#include "nsStringStream.h"
20
#include "nsStreamUtils.h"
21
#include "nsIPrincipal.h"
22
#include "mozilla/BasePrincipal.h"
23
#include "mozilla/dom/Element.h"
24
#include "mozilla/dom/FontTableURIProtocolHandler.h"
25
#include "mozilla/dom/SVGDocument.h"
26
#include "mozilla/LoadInfo.h"
27
#include "mozilla/NullPrincipal.h"
28
#include "nsSVGUtils.h"
29
#include "nsContentUtils.h"
30
#include "gfxFont.h"
31
#include "nsSMILAnimationController.h"
32
#include "gfxContext.h"
33
#include "harfbuzz/hb.h"
34
#include "mozilla/dom/ImageTracker.h"
35
36
0
#define SVG_CONTENT_TYPE NS_LITERAL_CSTRING("image/svg+xml")
37
0
#define UTF8_CHARSET NS_LITERAL_CSTRING("utf-8")
38
39
using namespace mozilla;
40
41
typedef mozilla::dom::Element Element;
42
43
/* static */ const mozilla::gfx::Color SimpleTextContextPaint::sZero;
44
45
gfxSVGGlyphs::gfxSVGGlyphs(hb_blob_t *aSVGTable, gfxFontEntry *aFontEntry)
46
    : mSVGData(aSVGTable)
47
    , mFontEntry(aFontEntry)
48
0
{
49
0
    unsigned int length;
50
0
    const char* svgData = hb_blob_get_data(mSVGData, &length);
51
0
    mHeader = reinterpret_cast<const Header*>(svgData);
52
0
    mDocIndex = nullptr;
53
0
54
0
    if (sizeof(Header) <= length && uint16_t(mHeader->mVersion) == 0 &&
55
0
        uint64_t(mHeader->mDocIndexOffset) + 2 <= length) {
56
0
        const DocIndex* docIndex = reinterpret_cast<const DocIndex*>
57
0
            (svgData + mHeader->mDocIndexOffset);
58
0
        // Limit the number of documents to avoid overflow
59
0
        if (uint64_t(mHeader->mDocIndexOffset) + 2 +
60
0
                uint16_t(docIndex->mNumEntries) * sizeof(IndexEntry) <= length) {
61
0
            mDocIndex = docIndex;
62
0
        }
63
0
    }
64
0
}
65
66
gfxSVGGlyphs::~gfxSVGGlyphs()
67
0
{
68
0
    hb_blob_destroy(mSVGData);
69
0
}
70
71
void
72
gfxSVGGlyphs::DidRefresh()
73
0
{
74
0
    mFontEntry->NotifyGlyphsChanged();
75
0
}
76
77
/*
78
 * Comparison operator for finding a range containing a given glyph ID. Simply
79
 *   checks whether |key| is less (greater) than every element of |range|, in
80
 *   which case return |key| < |range| (|key| > |range|). Otherwise |key| is in
81
 *   |range|, in which case return equality.
82
 * The total ordering here is guaranteed by
83
 *   (1) the index ranges being disjoint; and
84
 *   (2) the (sole) key always being a singleton, so intersection => containment
85
 *       (note that this is wrong if we have more than one intersection or two 
86
 *        sets intersecting of size > 1 -- so... don't do that)
87
 */
88
/* static */ int
89
gfxSVGGlyphs::CompareIndexEntries(const void *aKey, const void *aEntry)
90
0
{
91
0
    const uint32_t key = *(uint32_t*)aKey;
92
0
    const IndexEntry *entry = (const IndexEntry*)aEntry;
93
0
94
0
    if (key < uint16_t(entry->mStartGlyph)) {
95
0
        return -1;
96
0
    }
97
0
    if (key > uint16_t(entry->mEndGlyph)) {
98
0
        return 1;
99
0
    }
100
0
    return 0;
101
0
}
102
103
gfxSVGGlyphsDocument *
104
gfxSVGGlyphs::FindOrCreateGlyphsDocument(uint32_t aGlyphId)
105
0
{
106
0
    if (!mDocIndex) {
107
0
        // Invalid table
108
0
        return nullptr;
109
0
    }
110
0
111
0
    IndexEntry *entry = (IndexEntry*)bsearch(&aGlyphId, mDocIndex->mEntries,
112
0
                                             uint16_t(mDocIndex->mNumEntries),
113
0
                                             sizeof(IndexEntry),
114
0
                                             CompareIndexEntries);
115
0
    if (!entry) {
116
0
        return nullptr;
117
0
    }
118
0
119
0
    gfxSVGGlyphsDocument *result = mGlyphDocs.Get(entry->mDocOffset);
120
0
121
0
    if (!result) {
122
0
        unsigned int length;
123
0
        const uint8_t *data = (const uint8_t*)hb_blob_get_data(mSVGData, &length);
124
0
        if (entry->mDocOffset > 0 &&
125
0
            uint64_t(mHeader->mDocIndexOffset) + entry->mDocOffset + entry->mDocLength <= length) {
126
0
            result = new gfxSVGGlyphsDocument(data + mHeader->mDocIndexOffset + entry->mDocOffset,
127
0
                                              entry->mDocLength, this);
128
0
            mGlyphDocs.Put(entry->mDocOffset, result);
129
0
        }
130
0
    }
131
0
132
0
    return result;
133
0
}
134
135
nsresult
136
gfxSVGGlyphsDocument::SetupPresentation()
137
0
{
138
0
    nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
139
0
    nsCString contractId;
140
0
    nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "image/svg+xml", contractId);
141
0
    NS_ENSURE_SUCCESS(rv, rv);
142
0
143
0
    nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
144
0
      do_GetService(contractId.get());
145
0
    NS_ASSERTION(docLoaderFactory, "Couldn't get DocumentLoaderFactory");
146
0
147
0
    nsCOMPtr<nsIContentViewer> viewer;
148
0
    rv = docLoaderFactory->CreateInstanceForDocument(nullptr, mDocument, nullptr, getter_AddRefs(viewer));
149
0
    NS_ENSURE_SUCCESS(rv, rv);
150
0
151
0
    rv = viewer->Init(nullptr, gfx::IntRect(0, 0, 1000, 1000));
152
0
    if (NS_SUCCEEDED(rv)) {
153
0
        rv = viewer->Open(nullptr, nullptr);
154
0
        NS_ENSURE_SUCCESS(rv, rv);
155
0
    }
156
0
157
0
    nsCOMPtr<nsIPresShell> presShell;
158
0
    rv = viewer->GetPresShell(getter_AddRefs(presShell));
159
0
    NS_ENSURE_SUCCESS(rv, rv);
160
0
    if (!presShell->DidInitialize()) {
161
0
        rv = presShell->Initialize();
162
0
        NS_ENSURE_SUCCESS(rv, rv);
163
0
    }
164
0
165
0
    mDocument->FlushPendingNotifications(FlushType::Layout);
166
0
167
0
    if (mDocument->HasAnimationController()) {
168
0
      mDocument->GetAnimationController()
169
0
               ->Resume(nsSMILTimeContainer::PAUSE_IMAGE);
170
0
    }
171
0
    mDocument->ImageTracker()->SetAnimatingState(true);
172
0
173
0
    mViewer = viewer;
174
0
    mPresShell = presShell;
175
0
    mPresShell->AddPostRefreshObserver(this);
176
0
177
0
    return NS_OK;
178
0
}
179
180
void
181
gfxSVGGlyphsDocument::DidRefresh()
182
0
{
183
0
    mOwner->DidRefresh();
184
0
}
185
186
/**
187
 * Walk the DOM tree to find all glyph elements and insert them into the lookup
188
 * table
189
 * @param aElem The element to search from
190
 */
191
void
192
gfxSVGGlyphsDocument::FindGlyphElements(Element *aElem)
193
0
{
194
0
    for (nsIContent *child = aElem->GetLastChild(); child;
195
0
            child = child->GetPreviousSibling()) {
196
0
        if (!child->IsElement()) {
197
0
            continue;
198
0
        }
199
0
        FindGlyphElements(child->AsElement());
200
0
    }
201
0
202
0
    InsertGlyphId(aElem);
203
0
}
204
205
/**
206
 * If there exists an SVG glyph with the specified glyph id, render it and return true
207
 * If no such glyph exists, or in the case of an error return false
208
 * @param aContext The thebes aContext to draw to
209
 * @param aGlyphId The glyph id
210
 * @return true iff rendering succeeded
211
 */
212
void
213
gfxSVGGlyphs::RenderGlyph(gfxContext *aContext, uint32_t aGlyphId,
214
                          SVGContextPaint* aContextPaint)
215
0
{
216
0
    gfxContextAutoSaveRestore aContextRestorer(aContext);
217
0
218
0
    Element* glyph = mGlyphIdMap.Get(aGlyphId);
219
0
    MOZ_ASSERT(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
220
0
221
0
    AutoSetRestoreSVGContextPaint autoSetRestore(
222
0
        *aContextPaint, *glyph->OwnerDoc()->AsSVGDocument());
223
0
224
0
    nsSVGUtils::PaintSVGGlyph(glyph, aContext);
225
0
}
226
227
bool
228
gfxSVGGlyphs::GetGlyphExtents(uint32_t aGlyphId, const gfxMatrix& aSVGToAppSpace,
229
                              gfxRect *aResult)
230
0
{
231
0
    Element *glyph = mGlyphIdMap.Get(aGlyphId);
232
0
    NS_ASSERTION(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
233
0
234
0
    return nsSVGUtils::GetSVGGlyphExtents(glyph, aSVGToAppSpace, aResult);
235
0
}
236
237
Element *
238
gfxSVGGlyphs::GetGlyphElement(uint32_t aGlyphId)
239
0
{
240
0
    Element *elem;
241
0
242
0
    if (!mGlyphIdMap.Get(aGlyphId, &elem)) {
243
0
        elem = nullptr;
244
0
        if (gfxSVGGlyphsDocument *set = FindOrCreateGlyphsDocument(aGlyphId)) {
245
0
            elem = set->GetGlyphElement(aGlyphId);
246
0
        }
247
0
        mGlyphIdMap.Put(aGlyphId, elem);
248
0
    }
249
0
250
0
    return elem;
251
0
}
252
253
bool
254
gfxSVGGlyphs::HasSVGGlyph(uint32_t aGlyphId)
255
0
{
256
0
    return !!GetGlyphElement(aGlyphId);
257
0
}
258
259
size_t
260
gfxSVGGlyphs::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
261
0
{
262
0
    // We don't include the size of mSVGData here, because (depending on the
263
0
    // font backend implementation) it will either wrap a block of data owned
264
0
    // by the system (and potentially shared), or a table that's in our font
265
0
    // table cache and therefore already counted.
266
0
    size_t result = aMallocSizeOf(this)
267
0
                    + mGlyphDocs.ShallowSizeOfExcludingThis(aMallocSizeOf)
268
0
                    + mGlyphIdMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
269
0
    for (auto iter = mGlyphDocs.ConstIter(); !iter.Done(); iter.Next()) {
270
0
        result += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
271
0
    }
272
0
    return result;
273
0
}
274
275
Element *
276
gfxSVGGlyphsDocument::GetGlyphElement(uint32_t aGlyphId)
277
0
{
278
0
    return mGlyphIdMap.Get(aGlyphId);
279
0
}
280
281
gfxSVGGlyphsDocument::gfxSVGGlyphsDocument(const uint8_t *aBuffer,
282
                                           uint32_t aBufLen,
283
                                           gfxSVGGlyphs *aSVGGlyphs)
284
    : mOwner(aSVGGlyphs)
285
0
{
286
0
    ParseDocument(aBuffer, aBufLen);
287
0
    if (!mDocument) {
288
0
        NS_WARNING("Could not parse SVG glyphs document");
289
0
        return;
290
0
    }
291
0
292
0
    Element *root = mDocument->GetRootElement();
293
0
    if (!root) {
294
0
        NS_WARNING("Could not parse SVG glyphs document");
295
0
        return;
296
0
    }
297
0
298
0
    nsresult rv = SetupPresentation();
299
0
    if (NS_FAILED(rv)) {
300
0
        NS_WARNING("Couldn't setup presentation for SVG glyphs document");
301
0
        return;
302
0
    }
303
0
304
0
    FindGlyphElements(root);
305
0
}
306
307
gfxSVGGlyphsDocument::~gfxSVGGlyphsDocument()
308
0
{
309
0
    if (mDocument) {
310
0
        mDocument->OnPageHide(false, nullptr);
311
0
    }
312
0
    if (mPresShell) {
313
0
        mPresShell->RemovePostRefreshObserver(this);
314
0
    }
315
0
    if (mViewer) {
316
0
        mViewer->Close(nullptr);
317
0
        mViewer->Destroy();
318
0
    }
319
0
}
320
321
static nsresult
322
CreateBufferedStream(const uint8_t *aBuffer, uint32_t aBufLen,
323
                     nsCOMPtr<nsIInputStream> &aResult)
324
0
{
325
0
    nsCOMPtr<nsIInputStream> stream;
326
0
    nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
327
0
                                        reinterpret_cast<const char *>(aBuffer),
328
0
                                        aBufLen, NS_ASSIGNMENT_DEPEND);
329
0
    NS_ENSURE_SUCCESS(rv, rv);
330
0
331
0
    nsCOMPtr<nsIInputStream> aBufferedStream;
332
0
    if (!NS_InputStreamIsBuffered(stream)) {
333
0
        rv = NS_NewBufferedInputStream(getter_AddRefs(aBufferedStream),
334
0
                                       stream.forget(), 4096);
335
0
        NS_ENSURE_SUCCESS(rv, rv);
336
0
        stream = aBufferedStream;
337
0
    }
338
0
339
0
    aResult = stream;
340
0
341
0
    return NS_OK;
342
0
}
343
344
nsresult
345
gfxSVGGlyphsDocument::ParseDocument(const uint8_t *aBuffer, uint32_t aBufLen)
346
0
{
347
0
    // Mostly pulled from nsDOMParser::ParseFromStream
348
0
349
0
    nsCOMPtr<nsIInputStream> stream;
350
0
    nsresult rv = CreateBufferedStream(aBuffer, aBufLen, stream);
351
0
    NS_ENSURE_SUCCESS(rv, rv);
352
0
353
0
    nsCOMPtr<nsIURI> uri;
354
0
    mozilla::dom::FontTableURIProtocolHandler::GenerateURIString(mSVGGlyphsDocumentURI);
355
0
356
0
    rv = NS_NewURI(getter_AddRefs(uri), mSVGGlyphsDocumentURI);
357
0
    NS_ENSURE_SUCCESS(rv, rv);
358
0
359
0
    nsCOMPtr<nsIPrincipal> principal = NullPrincipal::CreateWithoutOriginAttributes();
360
0
361
0
    nsCOMPtr<nsIDocument> document;
362
0
    rv = NS_NewDOMDocument(getter_AddRefs(document),
363
0
                           EmptyString(),   // aNamespaceURI
364
0
                           EmptyString(),   // aQualifiedName
365
0
                           nullptr,          // aDoctype
366
0
                           uri, uri, principal,
367
0
                           false,           // aLoadedAsData
368
0
                           nullptr,          // aEventObject
369
0
                           DocumentFlavorSVG);
370
0
    NS_ENSURE_SUCCESS(rv, rv);
371
0
372
0
    nsCOMPtr<nsIChannel> channel;
373
0
    rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
374
0
                                  uri,
375
0
                                  nullptr, //aStream
376
0
                                  principal,
377
0
                                  nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
378
0
                                  nsIContentPolicy::TYPE_OTHER,
379
0
                                  SVG_CONTENT_TYPE,
380
0
                                  UTF8_CHARSET);
381
0
    NS_ENSURE_SUCCESS(rv, rv);
382
0
383
0
    // Set this early because various decisions during page-load depend on it.
384
0
    document->SetIsBeingUsedAsImage();
385
0
    document->SetIsSVGGlyphsDocument();
386
0
    document->SetReadyStateInternal(nsIDocument::READYSTATE_UNINITIALIZED);
387
0
388
0
    nsCOMPtr<nsIStreamListener> listener;
389
0
    rv = document->StartDocumentLoad("external-resource", channel,
390
0
                                     nullptr,    // aLoadGroup
391
0
                                     nullptr,    // aContainer
392
0
                                     getter_AddRefs(listener),
393
0
                                     true /* aReset */);
394
0
    if (NS_FAILED(rv) || !listener) {
395
0
        return NS_ERROR_FAILURE;
396
0
    }
397
0
398
0
    rv = listener->OnStartRequest(channel, nullptr /* aContext */);
399
0
    if (NS_FAILED(rv)) {
400
0
        channel->Cancel(rv);
401
0
    }
402
0
403
0
    nsresult status;
404
0
    channel->GetStatus(&status);
405
0
    if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) {
406
0
        rv = listener->OnDataAvailable(channel, nullptr /* aContext */, stream, 0, aBufLen);
407
0
        if (NS_FAILED(rv)) {
408
0
            channel->Cancel(rv);
409
0
        }
410
0
        channel->GetStatus(&status);
411
0
    }
412
0
413
0
    rv = listener->OnStopRequest(channel, nullptr /* aContext */, status);
414
0
    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
415
0
416
0
    document.swap(mDocument);
417
0
418
0
    return NS_OK;
419
0
}
420
421
void
422
gfxSVGGlyphsDocument::InsertGlyphId(Element *aGlyphElement)
423
0
{
424
0
    nsAutoString glyphIdStr;
425
0
    static const uint32_t glyphPrefixLength = 5;
426
0
    // The maximum glyph ID is 65535 so the maximum length of the numeric part
427
0
    // is 5.
428
0
    if (!aGlyphElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, glyphIdStr) ||
429
0
        !StringBeginsWith(glyphIdStr, NS_LITERAL_STRING("glyph")) ||
430
0
        glyphIdStr.Length() > glyphPrefixLength + 5) {
431
0
        return;
432
0
    }
433
0
434
0
    uint32_t id = 0;
435
0
    for (uint32_t i = glyphPrefixLength; i < glyphIdStr.Length(); ++i) {
436
0
      char16_t ch = glyphIdStr.CharAt(i);
437
0
      if (ch < '0' || ch > '9') {
438
0
        return;
439
0
      }
440
0
      if (ch == '0' && i == glyphPrefixLength) {
441
0
        return;
442
0
      }
443
0
      id = id * 10 + (ch - '0');
444
0
    }
445
0
446
0
    mGlyphIdMap.Put(id, aGlyphElement);
447
0
}
448
449
size_t
450
gfxSVGGlyphsDocument::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
451
0
{
452
0
    return aMallocSizeOf(this)
453
0
           + mGlyphIdMap.ShallowSizeOfExcludingThis(aMallocSizeOf)
454
0
           + mSVGGlyphsDocumentURI.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
455
0
456
0
}