gfxSVGGlyphs.cpp 16 KB

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