gfx/thebes/gfxSVGGlyphs.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial