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