gfx/src/nsRenderingContext.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsRenderingContext.h"
     7 #include <string.h>                     // for strlen
     8 #include <algorithm>                    // for min
     9 #include "gfxColor.h"                   // for gfxRGBA
    10 #include "gfxMatrix.h"                  // for gfxMatrix
    11 #include "gfxPoint.h"                   // for gfxPoint, gfxSize
    12 #include "gfxRect.h"                    // for gfxRect
    13 #include "gfxTypes.h"                   // for gfxFloat
    14 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
    15 #include "mozilla/mozalloc.h"           // for operator delete[], etc
    16 #include "nsBoundingMetrics.h"          // for nsBoundingMetrics
    17 #include "nsCharTraits.h"               // for NS_IS_LOW_SURROGATE
    18 #include "nsDebug.h"                    // for NS_ERROR
    19 #include "nsPoint.h"                    // for nsPoint
    20 #include "nsRect.h"                     // for nsRect, nsIntRect
    21 #include "nsRegion.h"                   // for nsIntRegionRectIterator, etc
    23 class gfxASurface;
    25 // XXXTodo: rename FORM_TWIPS to FROM_APPUNITS
    26 #define FROM_TWIPS(_x)  ((gfxFloat)((_x)/(mP2A)))
    27 #define FROM_TWIPS_INT(_x)  (NSToIntRound((gfxFloat)((_x)/(mP2A))))
    28 #define TO_TWIPS(_x)    ((nscoord)((_x)*(mP2A)))
    29 #define GFX_RECT_FROM_TWIPS_RECT(_r)   (gfxRect(FROM_TWIPS((_r).x), FROM_TWIPS((_r).y), FROM_TWIPS((_r).width), FROM_TWIPS((_r).height)))
    31 // Hard limit substring lengths to 8000 characters ... this lets us statically
    32 // size the cluster buffer array in FindSafeLength
    33 #define MAX_GFX_TEXT_BUF_SIZE 8000
    35 static int32_t FindSafeLength(const char16_t *aString, uint32_t aLength,
    36                               uint32_t aMaxChunkLength)
    37 {
    38     if (aLength <= aMaxChunkLength)
    39         return aLength;
    41     int32_t len = aMaxChunkLength;
    43     // Ensure that we don't break inside a surrogate pair
    44     while (len > 0 && NS_IS_LOW_SURROGATE(aString[len])) {
    45         len--;
    46     }
    47     if (len == 0) {
    48         // We don't want our caller to go into an infinite loop, so don't
    49         // return zero. It's hard to imagine how we could actually get here
    50         // unless there are languages that allow clusters of arbitrary size.
    51         // If there are and someone feeds us a 500+ character cluster, too
    52         // bad.
    53         return aMaxChunkLength;
    54     }
    55     return len;
    56 }
    58 static int32_t FindSafeLength(const char *aString, uint32_t aLength,
    59                               uint32_t aMaxChunkLength)
    60 {
    61     // Since it's ASCII, we don't need to worry about clusters or RTL
    62     return std::min(aLength, aMaxChunkLength);
    63 }
    65 //////////////////////////////////////////////////////////////////////
    66 //// nsRenderingContext
    68 void
    69 nsRenderingContext::Init(nsDeviceContext* aContext,
    70                          gfxASurface *aThebesSurface)
    71 {
    72     Init(aContext, new gfxContext(aThebesSurface));
    73 }
    75 void
    76 nsRenderingContext::Init(nsDeviceContext* aContext,
    77                          gfxContext *aThebesContext)
    78 {
    79     mDeviceContext = aContext;
    80     mThebes = aThebesContext;
    82     mThebes->SetLineWidth(1.0);
    83     mP2A = mDeviceContext->AppUnitsPerDevPixel();
    84 }
    86 void
    87 nsRenderingContext::Init(nsDeviceContext* aContext,
    88                          DrawTarget *aDrawTarget)
    89 {
    90     Init(aContext, new gfxContext(aDrawTarget));
    91 }
    93 //
    94 // graphics state
    95 //
    97 void
    98 nsRenderingContext::PushState()
    99 {
   100     mThebes->Save();
   101 }
   103 void
   104 nsRenderingContext::PopState()
   105 {
   106     mThebes->Restore();
   107 }
   109 void
   110 nsRenderingContext::IntersectClip(const nsRect& aRect)
   111 {
   112     mThebes->NewPath();
   113     gfxRect clipRect(GFX_RECT_FROM_TWIPS_RECT(aRect));
   114     if (mThebes->UserToDevicePixelSnapped(clipRect, true)) {
   115         gfxMatrix mat(mThebes->CurrentMatrix());
   116         mat.Invert();
   117         clipRect = mat.Transform(clipRect);
   118         mThebes->Rectangle(clipRect);
   119     } else {
   120         mThebes->Rectangle(clipRect);
   121     }
   123     mThebes->Clip();
   124 }
   126 void
   127 nsRenderingContext::SetClip(const nsIntRegion& aRegion)
   128 {
   129     // Region is in device coords, no transformation.  This should
   130     // only be called when there is no transform in place, when we we
   131     // just start painting a widget. The region is set by the platform
   132     // paint routine.  Therefore, there is no option to intersect with
   133     // an existing clip.
   135     gfxMatrix mat = mThebes->CurrentMatrix();
   136     mThebes->IdentityMatrix();
   138     mThebes->ResetClip();
   140     mThebes->NewPath();
   141     nsIntRegionRectIterator iter(aRegion);
   142     const nsIntRect* rect;
   143     while ((rect = iter.Next())) {
   144         mThebes->Rectangle(gfxRect(rect->x, rect->y, rect->width, rect->height),
   145                            true);
   146     }
   147     mThebes->Clip();
   148     mThebes->SetMatrix(mat);
   149 }
   151 void
   152 nsRenderingContext::SetLineStyle(nsLineStyle aLineStyle)
   153 {
   154     switch (aLineStyle) {
   155         case nsLineStyle_kSolid:
   156             mThebes->SetDash(gfxContext::gfxLineSolid);
   157             break;
   158         case nsLineStyle_kDashed:
   159             mThebes->SetDash(gfxContext::gfxLineDashed);
   160             break;
   161         case nsLineStyle_kDotted:
   162             mThebes->SetDash(gfxContext::gfxLineDotted);
   163             break;
   164         case nsLineStyle_kNone:
   165         default:
   166             // nothing uses kNone
   167             NS_ERROR("SetLineStyle: Invalid line style");
   168             break;
   169     }
   170 }
   173 void
   174 nsRenderingContext::SetColor(nscolor aColor)
   175 {
   176     /* This sets the color assuming the sRGB color space, since that's
   177      * what all CSS colors are defined to be in by the spec.
   178      */
   179     mThebes->SetColor(gfxRGBA(aColor));
   180 }
   182 void
   183 nsRenderingContext::Translate(const nsPoint& aPt)
   184 {
   185     mThebes->Translate(gfxPoint(FROM_TWIPS(aPt.x), FROM_TWIPS(aPt.y)));
   186 }
   188 void
   189 nsRenderingContext::Scale(float aSx, float aSy)
   190 {
   191     mThebes->Scale(aSx, aSy);
   192 }
   194 //
   195 // shapes
   196 //
   198 void
   199 nsRenderingContext::DrawLine(const nsPoint& aStartPt, const nsPoint& aEndPt)
   200 {
   201     DrawLine(aStartPt.x, aStartPt.y, aEndPt.x, aEndPt.y);
   202 }
   204 void
   205 nsRenderingContext::DrawLine(nscoord aX0, nscoord aY0,
   206                              nscoord aX1, nscoord aY1)
   207 {
   208     gfxPoint p0 = gfxPoint(FROM_TWIPS(aX0), FROM_TWIPS(aY0));
   209     gfxPoint p1 = gfxPoint(FROM_TWIPS(aX1), FROM_TWIPS(aY1));
   211     // we can't draw thick lines with gfx, so we always assume we want
   212     // pixel-aligned lines if the rendering context is at 1.0 scale
   213     gfxMatrix savedMatrix = mThebes->CurrentMatrix();
   214     if (!savedMatrix.HasNonTranslation()) {
   215         p0 = mThebes->UserToDevice(p0);
   216         p1 = mThebes->UserToDevice(p1);
   218         p0.Round();
   219         p1.Round();
   221         mThebes->IdentityMatrix();
   223         mThebes->NewPath();
   225         // snap straight lines
   226         if (p0.x == p1.x) {
   227             mThebes->Line(p0 + gfxPoint(0.5, 0),
   228                           p1 + gfxPoint(0.5, 0));
   229         } else if (p0.y == p1.y) {
   230             mThebes->Line(p0 + gfxPoint(0, 0.5),
   231                           p1 + gfxPoint(0, 0.5));
   232         } else {
   233             mThebes->Line(p0, p1);
   234         }
   236         mThebes->Stroke();
   238         mThebes->SetMatrix(savedMatrix);
   239     } else {
   240         mThebes->NewPath();
   241         mThebes->Line(p0, p1);
   242         mThebes->Stroke();
   243     }
   244 }
   246 void
   247 nsRenderingContext::DrawRect(const nsRect& aRect)
   248 {
   249     mThebes->NewPath();
   250     mThebes->Rectangle(GFX_RECT_FROM_TWIPS_RECT(aRect), true);
   251     mThebes->Stroke();
   252 }
   254 void
   255 nsRenderingContext::DrawRect(nscoord aX, nscoord aY,
   256                              nscoord aWidth, nscoord aHeight)
   257 {
   258     DrawRect(nsRect(aX, aY, aWidth, aHeight));
   259 }
   262 /* Clamp r to (0,0) (2^23,2^23)
   263  * these are to be device coordinates.
   264  *
   265  * Returns false if the rectangle is completely out of bounds,
   266  * true otherwise.
   267  *
   268  * This function assumes that it will be called with a rectangle being
   269  * drawn into a surface with an identity transformation matrix; that
   270  * is, anything above or to the left of (0,0) will be offscreen.
   271  *
   272  * First it checks if the rectangle is entirely beyond
   273  * CAIRO_COORD_MAX; if so, it can't ever appear on the screen --
   274  * false is returned.
   275  *
   276  * Then it shifts any rectangles with x/y < 0 so that x and y are = 0,
   277  * and adjusts the width and height appropriately.  For example, a
   278  * rectangle from (0,-5) with dimensions (5,10) will become a
   279  * rectangle from (0,0) with dimensions (5,5).
   280  *
   281  * If after negative x/y adjustment to 0, either the width or height
   282  * is negative, then the rectangle is completely offscreen, and
   283  * nothing is drawn -- false is returned.
   284  *
   285  * Finally, if x+width or y+height are greater than CAIRO_COORD_MAX,
   286  * the width and height are clamped such x+width or y+height are equal
   287  * to CAIRO_COORD_MAX, and true is returned.
   288  */
   289 #define CAIRO_COORD_MAX (double(0x7fffff))
   291 static bool
   292 ConditionRect(gfxRect& r) {
   293     // if either x or y is way out of bounds;
   294     // note that we don't handle negative w/h here
   295     if (r.X() > CAIRO_COORD_MAX || r.Y() > CAIRO_COORD_MAX)
   296         return false;
   298     if (r.X() < 0.0) {
   299         r.width += r.X();
   300         if (r.width < 0.0)
   301             return false;
   302         r.x = 0.0;
   303     }
   305     if (r.XMost() > CAIRO_COORD_MAX) {
   306         r.width = CAIRO_COORD_MAX - r.X();
   307     }
   309     if (r.Y() < 0.0) {
   310         r.height += r.Y();
   311         if (r.Height() < 0.0)
   312             return false;
   314         r.y = 0.0;
   315     }
   317     if (r.YMost() > CAIRO_COORD_MAX) {
   318         r.height = CAIRO_COORD_MAX - r.Y();
   319     }
   320     return true;
   321 }
   323 void
   324 nsRenderingContext::FillRect(const nsRect& aRect)
   325 {
   326     gfxRect r(GFX_RECT_FROM_TWIPS_RECT(aRect));
   328     /* Clamp coordinates to work around a design bug in cairo */
   329     nscoord bigval = (nscoord)(CAIRO_COORD_MAX*mP2A);
   330     if (aRect.width > bigval ||
   331         aRect.height > bigval ||
   332         aRect.x < -bigval ||
   333         aRect.x > bigval ||
   334         aRect.y < -bigval ||
   335         aRect.y > bigval)
   336     {
   337         gfxMatrix mat = mThebes->CurrentMatrix();
   339         r = mat.Transform(r);
   341         if (!ConditionRect(r))
   342             return;
   344         mThebes->IdentityMatrix();
   345         mThebes->NewPath();
   347         mThebes->Rectangle(r, true);
   348         mThebes->Fill();
   349         mThebes->SetMatrix(mat);
   350     }
   352     mThebes->NewPath();
   353     mThebes->Rectangle(r, true);
   354     mThebes->Fill();
   355 }
   357 void
   358 nsRenderingContext::FillRect(nscoord aX, nscoord aY,
   359                              nscoord aWidth, nscoord aHeight)
   360 {
   361     FillRect(nsRect(aX, aY, aWidth, aHeight));
   362 }
   364 void
   365 nsRenderingContext::InvertRect(const nsRect& aRect)
   366 {
   367     gfxContext::GraphicsOperator lastOp = mThebes->CurrentOperator();
   369     mThebes->SetOperator(gfxContext::OPERATOR_XOR);
   370     FillRect(aRect);
   371     mThebes->SetOperator(lastOp);
   372 }
   374 void
   375 nsRenderingContext::DrawEllipse(nscoord aX, nscoord aY,
   376                                 nscoord aWidth, nscoord aHeight)
   377 {
   378     mThebes->NewPath();
   379     mThebes->Ellipse(gfxPoint(FROM_TWIPS(aX) + FROM_TWIPS(aWidth)/2.0,
   380                               FROM_TWIPS(aY) + FROM_TWIPS(aHeight)/2.0),
   381                      gfxSize(FROM_TWIPS(aWidth),
   382                              FROM_TWIPS(aHeight)));
   383     mThebes->Stroke();
   384 }
   386 void
   387 nsRenderingContext::FillEllipse(const nsRect& aRect)
   388 {
   389     FillEllipse(aRect.x, aRect.y, aRect.width, aRect.height);
   390 }
   392 void
   393 nsRenderingContext::FillEllipse(nscoord aX, nscoord aY,
   394                                 nscoord aWidth, nscoord aHeight)
   395 {
   396     mThebes->NewPath();
   397     mThebes->Ellipse(gfxPoint(FROM_TWIPS(aX) + FROM_TWIPS(aWidth)/2.0,
   398                               FROM_TWIPS(aY) + FROM_TWIPS(aHeight)/2.0),
   399                      gfxSize(FROM_TWIPS(aWidth),
   400                              FROM_TWIPS(aHeight)));
   401     mThebes->Fill();
   402 }
   404 void
   405 nsRenderingContext::FillPolygon(const nsPoint twPoints[], int32_t aNumPoints)
   406 {
   407     if (aNumPoints == 0)
   408         return;
   410     nsAutoArrayPtr<gfxPoint> pxPoints(new gfxPoint[aNumPoints]);
   412     for (int i = 0; i < aNumPoints; i++) {
   413         pxPoints[i].x = FROM_TWIPS(twPoints[i].x);
   414         pxPoints[i].y = FROM_TWIPS(twPoints[i].y);
   415     }
   417     mThebes->NewPath();
   418     mThebes->Polygon(pxPoints, aNumPoints);
   419     mThebes->Fill();
   420 }
   422 //
   423 // text
   424 //
   426 void
   427 nsRenderingContext::SetTextRunRTL(bool aIsRTL)
   428 {
   429     mFontMetrics->SetTextRunRTL(aIsRTL);
   430 }
   432 void
   433 nsRenderingContext::SetFont(nsFontMetrics *aFontMetrics)
   434 {
   435     mFontMetrics = aFontMetrics;
   436 }
   438 int32_t
   439 nsRenderingContext::GetMaxChunkLength()
   440 {
   441     if (!mFontMetrics)
   442         return 1;
   443     return std::min(mFontMetrics->GetMaxStringLength(), MAX_GFX_TEXT_BUF_SIZE);
   444 }
   446 nscoord
   447 nsRenderingContext::GetWidth(char aC)
   448 {
   449     if (aC == ' ' && mFontMetrics) {
   450         return mFontMetrics->SpaceWidth();
   451     }
   453     return GetWidth(&aC, 1);
   454 }
   456 nscoord
   457 nsRenderingContext::GetWidth(char16_t aC)
   458 {
   459     return GetWidth(&aC, 1);
   460 }
   462 nscoord
   463 nsRenderingContext::GetWidth(const nsString& aString)
   464 {
   465     return GetWidth(aString.get(), aString.Length());
   466 }
   468 nscoord
   469 nsRenderingContext::GetWidth(const char* aString)
   470 {
   471     return GetWidth(aString, strlen(aString));
   472 }
   474 nscoord
   475 nsRenderingContext::GetWidth(const char* aString, uint32_t aLength)
   476 {
   477     uint32_t maxChunkLength = GetMaxChunkLength();
   478     nscoord width = 0;
   479     while (aLength > 0) {
   480         int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
   481         width += mFontMetrics->GetWidth(aString, len, this);
   482         aLength -= len;
   483         aString += len;
   484     }
   485     return width;
   486 }
   488 nscoord
   489 nsRenderingContext::GetWidth(const char16_t *aString, uint32_t aLength)
   490 {
   491     uint32_t maxChunkLength = GetMaxChunkLength();
   492     nscoord width = 0;
   493     while (aLength > 0) {
   494         int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
   495         width += mFontMetrics->GetWidth(aString, len, this);
   496         aLength -= len;
   497         aString += len;
   498     }
   499     return width;
   500 }
   502 nsBoundingMetrics
   503 nsRenderingContext::GetBoundingMetrics(const char16_t* aString,
   504                                        uint32_t aLength)
   505 {
   506     uint32_t maxChunkLength = GetMaxChunkLength();
   507     int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
   508     // Assign directly in the first iteration. This ensures that
   509     // negative ascent/descent can be returned and the left bearing
   510     // is properly initialized.
   511     nsBoundingMetrics totalMetrics
   512         = mFontMetrics->GetBoundingMetrics(aString, len, this);
   513     aLength -= len;
   514     aString += len;
   516     while (aLength > 0) {
   517         len = FindSafeLength(aString, aLength, maxChunkLength);
   518         nsBoundingMetrics metrics
   519             = mFontMetrics->GetBoundingMetrics(aString, len, this);
   520         totalMetrics += metrics;
   521         aLength -= len;
   522         aString += len;
   523     }
   524     return totalMetrics;
   525 }
   527 void
   528 nsRenderingContext::DrawString(const char *aString, uint32_t aLength,
   529                                nscoord aX, nscoord aY)
   530 {
   531     uint32_t maxChunkLength = GetMaxChunkLength();
   532     while (aLength > 0) {
   533         int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
   534         mFontMetrics->DrawString(aString, len, aX, aY, this);
   535         aLength -= len;
   537         if (aLength > 0) {
   538             nscoord width = mFontMetrics->GetWidth(aString, len, this);
   539             aX += width;
   540             aString += len;
   541         }
   542     }
   543 }
   545 void
   546 nsRenderingContext::DrawString(const nsString& aString, nscoord aX, nscoord aY)
   547 {
   548     DrawString(aString.get(), aString.Length(), aX, aY);
   549 }
   551 void
   552 nsRenderingContext::DrawString(const char16_t *aString, uint32_t aLength,
   553                                nscoord aX, nscoord aY)
   554 {
   555     uint32_t maxChunkLength = GetMaxChunkLength();
   556     if (aLength <= maxChunkLength) {
   557         mFontMetrics->DrawString(aString, aLength, aX, aY, this, this);
   558         return;
   559     }
   561     bool isRTL = mFontMetrics->GetTextRunRTL();
   563     // If we're drawing right to left, we must start at the end.
   564     if (isRTL) {
   565         aX += GetWidth(aString, aLength);
   566     }
   568     while (aLength > 0) {
   569         int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
   570         nscoord width = mFontMetrics->GetWidth(aString, len, this);
   571         if (isRTL) {
   572             aX -= width;
   573         }
   574         mFontMetrics->DrawString(aString, len, aX, aY, this, this);
   575         if (!isRTL) {
   576             aX += width;
   577         }
   578         aLength -= len;
   579         aString += len;
   580     }
   581 }

mercurial