LCOV - code coverage report
Current view: top level - gfx/thebes - gfxSVGGlyphs.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 217 0.5 %
Date: 2017-07-14 16:53:18 Functions: 0 20 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "NullPrincipal.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           3 : /* static */ const Color SimpleTextContextPaint::sZero = Color();
      44             : 
      45           0 : gfxSVGGlyphs::gfxSVGGlyphs(hb_blob_t *aSVGTable, gfxFontEntry *aFontEntry)
      46             :     : mSVGData(aSVGTable)
      47           0 :     , mFontEntry(aFontEntry)
      48             : {
      49             :     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             : 
      54           0 :     if (sizeof(Header) <= length && uint16_t(mHeader->mVersion) == 0 &&
      55           0 :         uint64_t(mHeader->mDocIndexOffset) + 2 <= length) {
      56             :         const DocIndex* docIndex = reinterpret_cast<const DocIndex*>
      57           0 :             (svgData + mHeader->mDocIndexOffset);
      58             :         // 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             :         }
      63             :     }
      64           0 : }
      65             : 
      66           0 : gfxSVGGlyphs::~gfxSVGGlyphs()
      67             : {
      68           0 :     hb_blob_destroy(mSVGData);
      69           0 : }
      70             : 
      71             : void
      72           0 : gfxSVGGlyphs::DidRefresh()
      73             : {
      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           0 : gfxSVGGlyphs::CompareIndexEntries(const void *aKey, const void *aEntry)
      90             : {
      91           0 :     const uint32_t key = *(uint32_t*)aKey;
      92           0 :     const IndexEntry *entry = (const IndexEntry*)aEntry;
      93             : 
      94           0 :     if (key < uint16_t(entry->mStartGlyph)) {
      95           0 :         return -1;
      96             :     }
      97           0 :     if (key > uint16_t(entry->mEndGlyph)) {
      98           0 :         return 1;
      99             :     }
     100           0 :     return 0;
     101             : }
     102             : 
     103             : gfxSVGGlyphsDocument *
     104           0 : gfxSVGGlyphs::FindOrCreateGlyphsDocument(uint32_t aGlyphId)
     105             : {
     106           0 :     if (!mDocIndex) {
     107             :         // Invalid table
     108           0 :         return nullptr;
     109             :     }
     110             : 
     111           0 :     IndexEntry *entry = (IndexEntry*)bsearch(&aGlyphId, mDocIndex->mEntries,
     112           0 :                                              uint16_t(mDocIndex->mNumEntries),
     113             :                                              sizeof(IndexEntry),
     114           0 :                                              CompareIndexEntries);
     115           0 :     if (!entry) {
     116           0 :         return nullptr;
     117             :     }
     118             : 
     119           0 :     gfxSVGGlyphsDocument *result = mGlyphDocs.Get(entry->mDocOffset);
     120             : 
     121           0 :     if (!result) {
     122             :         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             :         }
     130             :     }
     131             : 
     132           0 :     return result;
     133             : }
     134             : 
     135             : nsresult
     136           0 : gfxSVGGlyphsDocument::SetupPresentation()
     137             : {
     138           0 :     nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
     139           0 :     nsXPIDLCString contractId;
     140           0 :     nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "image/svg+xml", getter_Copies(contractId));
     141           0 :     NS_ENSURE_SUCCESS(rv, rv);
     142             : 
     143           0 :     nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory = do_GetService(contractId);
     144           0 :     NS_ASSERTION(docLoaderFactory, "Couldn't get DocumentLoaderFactory");
     145             : 
     146           0 :     nsCOMPtr<nsIContentViewer> viewer;
     147           0 :     rv = docLoaderFactory->CreateInstanceForDocument(nullptr, mDocument, nullptr, getter_AddRefs(viewer));
     148           0 :     NS_ENSURE_SUCCESS(rv, rv);
     149             : 
     150           0 :     rv = viewer->Init(nullptr, gfx::IntRect(0, 0, 1000, 1000));
     151           0 :     if (NS_SUCCEEDED(rv)) {
     152           0 :         rv = viewer->Open(nullptr, nullptr);
     153           0 :         NS_ENSURE_SUCCESS(rv, rv);
     154             :     }
     155             : 
     156           0 :     nsCOMPtr<nsIPresShell> presShell;
     157           0 :     rv = viewer->GetPresShell(getter_AddRefs(presShell));
     158           0 :     NS_ENSURE_SUCCESS(rv, rv);
     159           0 :     nsPresContext* presContext = presShell->GetPresContext();
     160           0 :     presContext->SetIsGlyph(true);
     161             : 
     162           0 :     if (!presShell->DidInitialize()) {
     163           0 :         nsRect rect = presContext->GetVisibleArea();
     164           0 :         rv = presShell->Initialize(rect.width, rect.height);
     165           0 :         NS_ENSURE_SUCCESS(rv, rv);
     166             :     }
     167             : 
     168           0 :     mDocument->FlushPendingNotifications(FlushType::Layout);
     169             : 
     170           0 :     nsSMILAnimationController* controller = mDocument->GetAnimationController();
     171           0 :     if (controller) {
     172           0 :       controller->Resume(nsSMILTimeContainer::PAUSE_IMAGE);
     173             :     }
     174           0 :     mDocument->ImageTracker()->SetAnimatingState(true);
     175             : 
     176           0 :     mViewer = viewer;
     177           0 :     mPresShell = presShell;
     178           0 :     mPresShell->AddPostRefreshObserver(this);
     179             : 
     180           0 :     return NS_OK;
     181             : }
     182             : 
     183             : void
     184           0 : gfxSVGGlyphsDocument::DidRefresh()
     185             : {
     186           0 :     mOwner->DidRefresh();
     187           0 : }
     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
     195           0 : gfxSVGGlyphsDocument::FindGlyphElements(Element *aElem)
     196             : {
     197           0 :     for (nsIContent *child = aElem->GetLastChild(); child;
     198           0 :             child = child->GetPreviousSibling()) {
     199           0 :         if (!child->IsElement()) {
     200           0 :             continue;
     201             :         }
     202           0 :         FindGlyphElements(child->AsElement());
     203             :     }
     204             : 
     205           0 :     InsertGlyphId(aElem);
     206           0 : }
     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             : void
     216           0 : gfxSVGGlyphs::RenderGlyph(gfxContext *aContext, uint32_t aGlyphId,
     217             :                           SVGContextPaint* aContextPaint)
     218             : {
     219           0 :     gfxContextAutoSaveRestore aContextRestorer(aContext);
     220             : 
     221           0 :     Element *glyph = mGlyphIdMap.Get(aGlyphId);
     222           0 :     MOZ_ASSERT(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
     223             : 
     224           0 :     AutoSetRestoreSVGContextPaint autoSetRestore(aContextPaint, glyph->OwnerDoc());
     225             : 
     226           0 :     nsSVGUtils::PaintSVGGlyph(glyph, aContext);
     227           0 : }
     228             : 
     229             : bool
     230           0 : gfxSVGGlyphs::GetGlyphExtents(uint32_t aGlyphId, const gfxMatrix& aSVGToAppSpace,
     231             :                               gfxRect *aResult)
     232             : {
     233           0 :     Element *glyph = mGlyphIdMap.Get(aGlyphId);
     234           0 :     NS_ASSERTION(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
     235             : 
     236           0 :     return nsSVGUtils::GetSVGGlyphExtents(glyph, aSVGToAppSpace, aResult);
     237             : }
     238             : 
     239             : Element *
     240           0 : gfxSVGGlyphs::GetGlyphElement(uint32_t aGlyphId)
     241             : {
     242             :     Element *elem;
     243             : 
     244           0 :     if (!mGlyphIdMap.Get(aGlyphId, &elem)) {
     245           0 :         elem = nullptr;
     246           0 :         if (gfxSVGGlyphsDocument *set = FindOrCreateGlyphsDocument(aGlyphId)) {
     247           0 :             elem = set->GetGlyphElement(aGlyphId);
     248             :         }
     249           0 :         mGlyphIdMap.Put(aGlyphId, elem);
     250             :     }
     251             : 
     252           0 :     return elem;
     253             : }
     254             : 
     255             : bool
     256           0 : gfxSVGGlyphs::HasSVGGlyph(uint32_t aGlyphId)
     257             : {
     258           0 :     return !!GetGlyphElement(aGlyphId);
     259             : }
     260             : 
     261             : size_t
     262           0 : 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           0 :     size_t result = aMallocSizeOf(this)
     269           0 :                     + mGlyphDocs.ShallowSizeOfExcludingThis(aMallocSizeOf)
     270           0 :                     + mGlyphIdMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
     271           0 :     for (auto iter = mGlyphDocs.ConstIter(); !iter.Done(); iter.Next()) {
     272           0 :         result += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
     273             :     }
     274           0 :     return result;
     275             : }
     276             : 
     277             : Element *
     278           0 : gfxSVGGlyphsDocument::GetGlyphElement(uint32_t aGlyphId)
     279             : {
     280           0 :     return mGlyphIdMap.Get(aGlyphId);
     281             : }
     282             : 
     283           0 : gfxSVGGlyphsDocument::gfxSVGGlyphsDocument(const uint8_t *aBuffer,
     284             :                                            uint32_t aBufLen,
     285           0 :                                            gfxSVGGlyphs *aSVGGlyphs)
     286           0 :     : mOwner(aSVGGlyphs)
     287             : {
     288           0 :     ParseDocument(aBuffer, aBufLen);
     289           0 :     if (!mDocument) {
     290           0 :         NS_WARNING("Could not parse SVG glyphs document");
     291           0 :         return;
     292             :     }
     293             : 
     294           0 :     Element *root = mDocument->GetRootElement();
     295           0 :     if (!root) {
     296           0 :         NS_WARNING("Could not parse SVG glyphs document");
     297           0 :         return;
     298             :     }
     299             : 
     300           0 :     nsresult rv = SetupPresentation();
     301           0 :     if (NS_FAILED(rv)) {
     302           0 :         NS_WARNING("Couldn't setup presentation for SVG glyphs document");
     303           0 :         return;
     304             :     }
     305             : 
     306           0 :     FindGlyphElements(root);
     307             : }
     308             : 
     309           0 : gfxSVGGlyphsDocument::~gfxSVGGlyphsDocument()
     310             : {
     311           0 :     if (mDocument) {
     312           0 :         mDocument->OnPageHide(false, nullptr);
     313             :     }
     314           0 :     if (mPresShell) {
     315           0 :         mPresShell->RemovePostRefreshObserver(this);
     316             :     }
     317           0 :     if (mViewer) {
     318           0 :         mViewer->Close(nullptr);
     319           0 :         mViewer->Destroy();
     320             :     }
     321           0 : }
     322             : 
     323             : static nsresult
     324           0 : CreateBufferedStream(const uint8_t *aBuffer, uint32_t aBufLen,
     325             :                      nsCOMPtr<nsIInputStream> &aResult)
     326             : {
     327           0 :     nsCOMPtr<nsIInputStream> stream;
     328           0 :     nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
     329             :                                         reinterpret_cast<const char *>(aBuffer),
     330           0 :                                         aBufLen, NS_ASSIGNMENT_DEPEND);
     331           0 :     NS_ENSURE_SUCCESS(rv, rv);
     332             : 
     333           0 :     nsCOMPtr<nsIInputStream> aBufferedStream;
     334           0 :     if (!NS_InputStreamIsBuffered(stream)) {
     335           0 :         rv = NS_NewBufferedInputStream(getter_AddRefs(aBufferedStream), stream, 4096);
     336           0 :         NS_ENSURE_SUCCESS(rv, rv);
     337           0 :         stream = aBufferedStream;
     338             :     }
     339             : 
     340           0 :     aResult = stream;
     341             : 
     342           0 :     return NS_OK;
     343             : }
     344             : 
     345             : nsresult
     346           0 : gfxSVGGlyphsDocument::ParseDocument(const uint8_t *aBuffer, uint32_t aBufLen)
     347             : {
     348             :     // Mostly pulled from nsDOMParser::ParseFromStream
     349             : 
     350           0 :     nsCOMPtr<nsIInputStream> stream;
     351           0 :     nsresult rv = CreateBufferedStream(aBuffer, aBufLen, stream);
     352           0 :     NS_ENSURE_SUCCESS(rv, rv);
     353             : 
     354           0 :     nsCOMPtr<nsIURI> uri;
     355           0 :     nsHostObjectProtocolHandler::GenerateURIString(NS_LITERAL_CSTRING(FONTTABLEURI_SCHEME),
     356             :                                                    nullptr,
     357           0 :                                                    mSVGGlyphsDocumentURI);
     358             :  
     359           0 :     rv = NS_NewURI(getter_AddRefs(uri), mSVGGlyphsDocumentURI);
     360           0 :     NS_ENSURE_SUCCESS(rv, rv);
     361             : 
     362           0 :     nsCOMPtr<nsIPrincipal> principal = NullPrincipal::Create();
     363             : 
     364           0 :     nsCOMPtr<nsIDOMDocument> domDoc;
     365           0 :     rv = NS_NewDOMDocument(getter_AddRefs(domDoc),
     366           0 :                            EmptyString(),   // aNamespaceURI
     367           0 :                            EmptyString(),   // aQualifiedName
     368             :                            nullptr,          // aDoctype
     369             :                            uri, uri, principal,
     370             :                            false,           // aLoadedAsData
     371             :                            nullptr,          // aEventObject
     372           0 :                            DocumentFlavorSVG);
     373           0 :     NS_ENSURE_SUCCESS(rv, rv);
     374             : 
     375           0 :     nsCOMPtr<nsIDocument> document(do_QueryInterface(domDoc));
     376           0 :     if (!document) {
     377           0 :         return NS_ERROR_FAILURE;
     378             :     }
     379             : 
     380           0 :     nsCOMPtr<nsIChannel> channel;
     381           0 :     rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
     382             :                                   uri,
     383             :                                   nullptr, //aStream
     384             :                                   principal,
     385             :                                   nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
     386             :                                   nsIContentPolicy::TYPE_OTHER,
     387           0 :                                   SVG_CONTENT_TYPE,
     388           0 :                                   UTF8_CHARSET);
     389           0 :     NS_ENSURE_SUCCESS(rv, rv);
     390             : 
     391             :     // Set this early because various decisions during page-load depend on it.
     392           0 :     document->SetIsBeingUsedAsImage();
     393           0 :     document->SetReadyStateInternal(nsIDocument::READYSTATE_UNINITIALIZED);
     394             : 
     395           0 :     nsCOMPtr<nsIStreamListener> listener;
     396           0 :     rv = document->StartDocumentLoad("external-resource", channel,
     397             :                                      nullptr,    // aLoadGroup
     398             :                                      nullptr,    // aContainer
     399           0 :                                      getter_AddRefs(listener),
     400           0 :                                      true /* aReset */);
     401           0 :     if (NS_FAILED(rv) || !listener) {
     402           0 :         return NS_ERROR_FAILURE;
     403             :     }
     404             : 
     405           0 :     rv = listener->OnStartRequest(channel, nullptr /* aContext */);
     406           0 :     if (NS_FAILED(rv)) {
     407           0 :         channel->Cancel(rv);
     408             :     }
     409             : 
     410             :     nsresult status;
     411           0 :     channel->GetStatus(&status);
     412           0 :     if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) {
     413           0 :         rv = listener->OnDataAvailable(channel, nullptr /* aContext */, stream, 0, aBufLen);
     414           0 :         if (NS_FAILED(rv)) {
     415           0 :             channel->Cancel(rv);
     416             :         }
     417           0 :         channel->GetStatus(&status);
     418             :     }
     419             : 
     420           0 :     rv = listener->OnStopRequest(channel, nullptr /* aContext */, status);
     421           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
     422             : 
     423           0 :     document.swap(mDocument);
     424             : 
     425           0 :     return NS_OK;
     426             : }
     427             : 
     428             : void
     429           0 : gfxSVGGlyphsDocument::InsertGlyphId(Element *aGlyphElement)
     430             : {
     431           0 :     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           0 :     if (!aGlyphElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, glyphIdStr) ||
     436           0 :         !StringBeginsWith(glyphIdStr, NS_LITERAL_STRING("glyph")) ||
     437           0 :         glyphIdStr.Length() > glyphPrefixLength + 5) {
     438           0 :         return;
     439             :     }
     440             : 
     441           0 :     uint32_t id = 0;
     442           0 :     for (uint32_t i = glyphPrefixLength; i < glyphIdStr.Length(); ++i) {
     443           0 :       char16_t ch = glyphIdStr.CharAt(i);
     444           0 :       if (ch < '0' || ch > '9') {
     445           0 :         return;
     446             :       }
     447           0 :       if (ch == '0' && i == glyphPrefixLength) {
     448           0 :         return;
     449             :       }
     450           0 :       id = id * 10 + (ch - '0');
     451             :     }
     452             : 
     453           0 :     mGlyphIdMap.Put(id, aGlyphElement);
     454             : }
     455             : 
     456             : size_t
     457           0 : gfxSVGGlyphsDocument::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     458             : {
     459           0 :     return aMallocSizeOf(this)
     460           0 :            + mGlyphIdMap.ShallowSizeOfExcludingThis(aMallocSizeOf)
     461           0 :            + mSVGGlyphsDocumentURI.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
     462             : 
     463             : }

Generated by: LCOV version 1.13