LCOV - code coverage report
Current view: directory - content/html/content/src - nsHTMLCanvasElement.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 362 2 0.6 %
Date: 2012-06-02 Functions: 50 2 4.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  *   Vladimir Vukicevic <vladimir@pobox.com>
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2005
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      26                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "nsHTMLCanvasElement.h"
      39                 : 
      40                 : #include "mozilla/Base64.h"
      41                 : #include "nsNetUtil.h"
      42                 : #include "prmem.h"
      43                 : #include "nsDOMFile.h"
      44                 : #include "CheckedInt.h"
      45                 : 
      46                 : #include "nsIScriptSecurityManager.h"
      47                 : #include "nsIXPConnect.h"
      48                 : #include "jsapi.h"
      49                 : #include "nsContentUtils.h"
      50                 : #include "nsJSUtils.h"
      51                 : #include "nsMathUtils.h"
      52                 : #include "nsStreamUtils.h"
      53                 : #include "mozilla/Preferences.h"
      54                 : #include "mozilla/Telemetry.h"
      55                 : 
      56                 : #include "nsFrameManager.h"
      57                 : #include "nsDisplayList.h"
      58                 : #include "ImageLayers.h"
      59                 : #include "BasicLayers.h"
      60                 : #include "imgIEncoder.h"
      61                 : 
      62                 : #include "nsIWritablePropertyBag2.h"
      63                 : 
      64                 : #define DEFAULT_CANVAS_WIDTH 300
      65                 : #define DEFAULT_CANVAS_HEIGHT 150
      66                 : 
      67                 : using namespace mozilla;
      68                 : using namespace mozilla::dom;
      69                 : using namespace mozilla::layers;
      70                 : 
      71                 : nsGenericHTMLElement*
      72               0 : NS_NewHTMLCanvasElement(already_AddRefed<nsINodeInfo> aNodeInfo,
      73                 :                         FromParser aFromParser)
      74                 : {
      75               0 :   return new nsHTMLCanvasElement(aNodeInfo);
      76                 : }
      77                 : 
      78               0 : nsHTMLCanvasElement::nsHTMLCanvasElement(already_AddRefed<nsINodeInfo> aNodeInfo)
      79               0 :   : nsGenericHTMLElement(aNodeInfo), mWriteOnly(false)
      80                 : {
      81               0 : }
      82                 : 
      83               0 : nsHTMLCanvasElement::~nsHTMLCanvasElement()
      84                 : {
      85               0 : }
      86                 : 
      87            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLCanvasElement)
      88               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLCanvasElement,
      89                 :                                                   nsGenericHTMLElement)
      90               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCurrentContext)
      91               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
      92                 : 
      93               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLCanvasElement,
      94                 :                                                 nsGenericHTMLElement)
      95               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCurrentContext)
      96               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      97                 : 
      98               0 : NS_IMPL_ADDREF_INHERITED(nsHTMLCanvasElement, nsGenericElement)
      99               0 : NS_IMPL_RELEASE_INHERITED(nsHTMLCanvasElement, nsGenericElement)
     100                 : 
     101               0 : DOMCI_NODE_DATA(HTMLCanvasElement, nsHTMLCanvasElement)
     102                 : 
     103               0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLCanvasElement)
     104               0 :   NS_HTML_CONTENT_INTERFACE_TABLE2(nsHTMLCanvasElement,
     105                 :                                    nsIDOMHTMLCanvasElement,
     106                 :                                    nsICanvasElementExternal)
     107               0 :   NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLCanvasElement,
     108                 :                                                nsGenericHTMLElement)
     109               0 : NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLCanvasElement)
     110                 : 
     111               0 : NS_IMPL_ELEMENT_CLONE(nsHTMLCanvasElement)
     112                 : 
     113                 : nsIntSize
     114               0 : nsHTMLCanvasElement::GetWidthHeight()
     115                 : {
     116               0 :   nsIntSize size(DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT);
     117                 :   const nsAttrValue* value;
     118                 : 
     119               0 :   if ((value = GetParsedAttr(nsGkAtoms::width)) &&
     120               0 :       value->Type() == nsAttrValue::eInteger)
     121                 :   {
     122               0 :       size.width = value->GetIntegerValue();
     123                 :   }
     124                 : 
     125               0 :   if ((value = GetParsedAttr(nsGkAtoms::height)) &&
     126               0 :       value->Type() == nsAttrValue::eInteger)
     127                 :   {
     128               0 :       size.height = value->GetIntegerValue();
     129                 :   }
     130                 : 
     131                 :   return size;
     132                 : }
     133                 : 
     134               0 : NS_IMPL_UINT_ATTR_DEFAULT_VALUE(nsHTMLCanvasElement, Width, width, DEFAULT_CANVAS_WIDTH)
     135               0 : NS_IMPL_UINT_ATTR_DEFAULT_VALUE(nsHTMLCanvasElement, Height, height, DEFAULT_CANVAS_HEIGHT)
     136               0 : NS_IMPL_BOOL_ATTR(nsHTMLCanvasElement, MozOpaque, moz_opaque)
     137                 : 
     138                 : nsresult
     139               0 : nsHTMLCanvasElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
     140                 :                              nsIAtom* aPrefix, const nsAString& aValue,
     141                 :                              bool aNotify)
     142                 : {
     143                 :   nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
     144               0 :                                               aNotify);
     145               0 :   if (NS_SUCCEEDED(rv) && mCurrentContext &&
     146                 :       (aName == nsGkAtoms::width || aName == nsGkAtoms::height || aName == nsGkAtoms::moz_opaque))
     147                 :   {
     148               0 :     rv = UpdateContext();
     149               0 :     NS_ENSURE_SUCCESS(rv, rv);
     150                 :   }
     151                 : 
     152               0 :   return rv;
     153                 : }
     154                 : 
     155                 : nsresult
     156               0 : nsHTMLCanvasElement::CopyInnerTo(nsGenericElement* aDest) const
     157                 : {
     158               0 :   nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
     159               0 :   NS_ENSURE_SUCCESS(rv, rv);
     160               0 :   if (aDest->OwnerDoc()->IsStaticDocument()) {
     161               0 :     nsHTMLCanvasElement* dest = static_cast<nsHTMLCanvasElement*>(aDest);
     162               0 :     nsCOMPtr<nsISupports> cxt;
     163               0 :     dest->GetContext(NS_LITERAL_STRING("2d"), JSVAL_VOID, getter_AddRefs(cxt));
     164               0 :     nsCOMPtr<nsIDOMCanvasRenderingContext2D> context2d = do_QueryInterface(cxt);
     165               0 :     if (context2d) {
     166               0 :       context2d->DrawImage(const_cast<nsHTMLCanvasElement*>(this),
     167               0 :                            0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0);
     168                 :     }
     169                 :   }
     170               0 :   return rv;
     171                 : }
     172                 : 
     173                 : nsChangeHint
     174               0 : nsHTMLCanvasElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
     175                 :                                             PRInt32 aModType) const
     176                 : {
     177                 :   nsChangeHint retval =
     178               0 :     nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
     179               0 :   if (aAttribute == nsGkAtoms::width ||
     180                 :       aAttribute == nsGkAtoms::height)
     181                 :   {
     182               0 :     NS_UpdateHint(retval, NS_STYLE_HINT_REFLOW);
     183               0 :   } else if (aAttribute == nsGkAtoms::moz_opaque)
     184                 :   {
     185               0 :     NS_UpdateHint(retval, NS_STYLE_HINT_VISUAL);
     186                 :   }
     187               0 :   return retval;
     188                 : }
     189                 : 
     190                 : bool
     191               0 : nsHTMLCanvasElement::ParseAttribute(PRInt32 aNamespaceID,
     192                 :                                     nsIAtom* aAttribute,
     193                 :                                     const nsAString& aValue,
     194                 :                                     nsAttrValue& aResult)
     195                 : {
     196               0 :   if (aNamespaceID == kNameSpaceID_None &&
     197                 :       (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height)) {
     198               0 :     return aResult.ParseNonNegativeIntValue(aValue);
     199                 :   }
     200                 : 
     201                 :   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
     202               0 :                                               aResult);
     203                 : }
     204                 : 
     205                 : 
     206                 : // nsHTMLCanvasElement::toDataURL
     207                 : 
     208                 : NS_IMETHODIMP
     209               0 : nsHTMLCanvasElement::ToDataURL(const nsAString& aType, nsIVariant* aParams,
     210                 :                                PRUint8 optional_argc, nsAString& aDataURL)
     211                 : {
     212                 :   // do a trust check if this is a write-only canvas
     213               0 :   if (mWriteOnly && !nsContentUtils::IsCallerTrustedForRead()) {
     214               0 :     return NS_ERROR_DOM_SECURITY_ERR;
     215                 :   }
     216                 : 
     217               0 :   return ToDataURLImpl(aType, aParams, aDataURL);
     218                 : }
     219                 : 
     220                 : // nsHTMLCanvasElement::mozFetchAsStream
     221                 : 
     222                 : NS_IMETHODIMP
     223               0 : nsHTMLCanvasElement::MozFetchAsStream(nsIInputStreamCallback *aCallback,
     224                 :                                       const nsAString& aType)
     225                 : {
     226               0 :   if (!nsContentUtils::IsCallerChrome())
     227               0 :     return NS_ERROR_FAILURE;
     228                 : 
     229                 :   nsresult rv;
     230               0 :   bool fellBackToPNG = false;
     231               0 :   nsCOMPtr<nsIInputStream> inputData;
     232                 : 
     233               0 :   rv = ExtractData(aType, EmptyString(), getter_AddRefs(inputData), fellBackToPNG);
     234               0 :   NS_ENSURE_SUCCESS(rv, rv);
     235                 : 
     236               0 :   nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv);
     237               0 :   NS_ENSURE_SUCCESS(rv, rv);
     238                 : 
     239               0 :   nsCOMPtr<nsIThread> mainThread;
     240               0 :   rv = NS_GetMainThread(getter_AddRefs(mainThread));
     241               0 :   NS_ENSURE_SUCCESS(rv, rv);
     242                 : 
     243               0 :   nsCOMPtr<nsIInputStreamCallback> asyncCallback;
     244               0 :   rv = NS_NewInputStreamReadyEvent(getter_AddRefs(asyncCallback), aCallback, mainThread);
     245               0 :   NS_ENSURE_SUCCESS(rv, rv);
     246                 : 
     247               0 :   return asyncCallback->OnInputStreamReady(asyncData);
     248                 : }
     249                 : 
     250                 : nsresult
     251               0 : nsHTMLCanvasElement::ExtractData(const nsAString& aType,
     252                 :                                  const nsAString& aOptions,
     253                 :                                  nsIInputStream** aStream,
     254                 :                                  bool& aFellBackToPNG)
     255                 : {
     256                 :   // note that if we don't have a current context, the spec says we're
     257                 :   // supposed to just return transparent black pixels of the canvas
     258                 :   // dimensions.
     259               0 :   nsRefPtr<gfxImageSurface> emptyCanvas;
     260               0 :   nsIntSize size = GetWidthHeight();
     261               0 :   if (!mCurrentContext) {
     262               0 :     emptyCanvas = new gfxImageSurface(gfxIntSize(size.width, size.height), gfxASurface::ImageFormatARGB32);
     263               0 :     if (emptyCanvas->CairoStatus()) {
     264               0 :       return NS_ERROR_INVALID_ARG;
     265                 :     }
     266                 :   }
     267                 : 
     268                 :   nsresult rv;
     269                 : 
     270                 :   // get image bytes
     271               0 :   nsCOMPtr<nsIInputStream> imgStream;
     272               0 :   NS_ConvertUTF16toUTF8 encoderType(aType);
     273                 : 
     274                 :  try_again:
     275               0 :   if (mCurrentContext) {
     276               0 :     rv = mCurrentContext->GetInputStream(encoderType.get(),
     277               0 :                                          nsPromiseFlatString(aOptions).get(),
     278               0 :                                          getter_AddRefs(imgStream));
     279                 :   } else {
     280                 :     // no context, so we have to encode the empty image we created above
     281               0 :     nsCString enccid("@mozilla.org/image/encoder;2?type=");
     282               0 :     enccid += encoderType;
     283                 : 
     284               0 :     nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get(), &rv);
     285               0 :     if (NS_SUCCEEDED(rv) && encoder) {
     286               0 :       rv = encoder->InitFromData(emptyCanvas->Data(),
     287                 :                                  size.width * size.height * 4,
     288                 :                                  size.width,
     289                 :                                  size.height,
     290                 :                                  size.width * 4,
     291                 :                                  imgIEncoder::INPUT_FORMAT_HOSTARGB,
     292               0 :                                  aOptions);
     293               0 :       if (NS_SUCCEEDED(rv)) {
     294               0 :         imgStream = do_QueryInterface(encoder);
     295                 :       }
     296                 :     } else {
     297               0 :       rv = NS_ERROR_FAILURE;
     298                 :     }
     299                 :   }
     300                 : 
     301               0 :   if (NS_FAILED(rv) && !aFellBackToPNG) {
     302                 :     // Try image/png instead.
     303                 :     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
     304               0 :     aFellBackToPNG = true;
     305               0 :     encoderType.AssignLiteral("image/png");
     306               0 :     goto try_again;
     307                 :   }
     308                 : 
     309               0 :   NS_ENSURE_SUCCESS(rv, rv);
     310                 : 
     311               0 :   imgStream.forget(aStream);
     312               0 :   return NS_OK;
     313                 : }
     314                 : 
     315                 : nsresult
     316               0 : nsHTMLCanvasElement::ToDataURLImpl(const nsAString& aMimeType,
     317                 :                                    nsIVariant* aEncoderOptions,
     318                 :                                    nsAString& aDataURL)
     319                 : {
     320               0 :   bool fallbackToPNG = false;
     321                 : 
     322               0 :   nsIntSize size = GetWidthHeight();
     323               0 :   if (size.height == 0 || size.width == 0) {
     324               0 :     aDataURL = NS_LITERAL_STRING("data:,");
     325               0 :     return NS_OK;
     326                 :   }
     327                 : 
     328               0 :   nsAutoString type;
     329               0 :   nsresult rv = nsContentUtils::ASCIIToLower(aMimeType, type);
     330               0 :   if (NS_FAILED(rv)) {
     331               0 :     return rv;
     332                 :   }
     333                 : 
     334               0 :   nsAutoString params;
     335                 : 
     336                 :   // Quality parameter is only valid for the image/jpeg MIME type
     337               0 :   if (type.EqualsLiteral("image/jpeg")) {
     338                 :     PRUint16 vartype;
     339                 : 
     340               0 :     if (aEncoderOptions &&
     341               0 :         NS_SUCCEEDED(aEncoderOptions->GetDataType(&vartype)) &&
     342                 :         vartype <= nsIDataType::VTYPE_DOUBLE) {
     343                 : 
     344                 :       double quality;
     345                 :       // Quality must be between 0.0 and 1.0, inclusive
     346               0 :       if (NS_SUCCEEDED(aEncoderOptions->GetAsDouble(&quality)) &&
     347                 :           quality >= 0.0 && quality <= 1.0) {
     348               0 :         params.AppendLiteral("quality=");
     349               0 :         params.AppendInt(NS_lround(quality * 100.0));
     350                 :       }
     351                 :     }
     352                 :   }
     353                 : 
     354                 :   // If we haven't parsed the params check for proprietary options.
     355                 :   // The proprietary option -moz-parse-options will take a image lib encoder
     356                 :   // parse options string as is and pass it to the encoder.
     357               0 :   bool usingCustomParseOptions = false;
     358               0 :   if (params.Length() == 0) {
     359               0 :     NS_NAMED_LITERAL_STRING(mozParseOptions, "-moz-parse-options:");
     360               0 :     nsAutoString paramString;
     361               0 :     if (NS_SUCCEEDED(aEncoderOptions->GetAsAString(paramString)) && 
     362               0 :         StringBeginsWith(paramString, mozParseOptions)) {
     363                 :       nsDependentSubstring parseOptions = Substring(paramString, 
     364                 :                                                     mozParseOptions.Length(), 
     365               0 :                                                     paramString.Length() - 
     366               0 :                                                     mozParseOptions.Length());
     367               0 :       params.Append(parseOptions);
     368               0 :       usingCustomParseOptions = true;
     369                 :     }
     370                 :   }
     371                 : 
     372               0 :   nsCOMPtr<nsIInputStream> stream;
     373               0 :   rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
     374                 : 
     375                 :   // If there are unrecognized custom parse options, we should fall back to 
     376                 :   // the default values for the encoder without any options at all.
     377               0 :   if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
     378               0 :     fallbackToPNG = false;
     379               0 :     rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
     380                 :   }
     381                 : 
     382               0 :   NS_ENSURE_SUCCESS(rv, rv);
     383                 : 
     384                 :   // build data URL string
     385               0 :   if (fallbackToPNG)
     386               0 :     aDataURL = NS_LITERAL_STRING("data:image/png;base64,");
     387                 :   else
     388               0 :     aDataURL = NS_LITERAL_STRING("data:") + type +
     389               0 :       NS_LITERAL_STRING(";base64,");
     390                 : 
     391                 :   PRUint32 count;
     392               0 :   rv = stream->Available(&count);
     393               0 :   NS_ENSURE_SUCCESS(rv, rv);
     394                 : 
     395               0 :   return Base64EncodeInputStream(stream, aDataURL, count, aDataURL.Length());
     396                 : }
     397                 : 
     398                 : NS_IMETHODIMP
     399               0 : nsHTMLCanvasElement::MozGetAsFile(const nsAString& aName,
     400                 :                                   const nsAString& aType,
     401                 :                                   PRUint8 optional_argc,
     402                 :                                   nsIDOMFile** aResult)
     403                 : {
     404                 :   // do a trust check if this is a write-only canvas
     405               0 :   if ((mWriteOnly) &&
     406               0 :       !nsContentUtils::IsCallerTrustedForRead()) {
     407               0 :     return NS_ERROR_DOM_SECURITY_ERR;
     408                 :   }
     409                 : 
     410               0 :   return MozGetAsFileImpl(aName, aType, aResult);
     411                 : }
     412                 : 
     413                 : nsresult
     414               0 : nsHTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
     415                 :                                       const nsAString& aType,
     416                 :                                       nsIDOMFile** aResult)
     417                 : {
     418               0 :   bool fallbackToPNG = false;
     419                 : 
     420               0 :   nsCOMPtr<nsIInputStream> stream;
     421               0 :   nsresult rv = ExtractData(aType, EmptyString(), getter_AddRefs(stream),
     422               0 :                             fallbackToPNG);
     423               0 :   NS_ENSURE_SUCCESS(rv, rv);
     424                 : 
     425               0 :   nsAutoString type(aType);
     426               0 :   if (fallbackToPNG) {
     427               0 :     type.AssignLiteral("image/png");
     428                 :   }
     429                 : 
     430                 :   PRUint32 imgSize;
     431               0 :   rv = stream->Available(&imgSize);
     432               0 :   NS_ENSURE_SUCCESS(rv, rv);
     433                 : 
     434               0 :   void* imgData = nsnull;
     435               0 :   rv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
     436               0 :   NS_ENSURE_SUCCESS(rv, rv);
     437                 : 
     438                 :   // The DOMFile takes ownership of the buffer
     439                 :   nsRefPtr<nsDOMMemoryFile> file =
     440               0 :     new nsDOMMemoryFile(imgData, imgSize, aName, type);
     441                 : 
     442               0 :   file.forget(aResult);
     443               0 :   return NS_OK;
     444                 : }
     445                 : 
     446                 : nsresult
     447               0 : nsHTMLCanvasElement::GetContextHelper(const nsAString& aContextId,
     448                 :                                       bool aForceThebes,
     449                 :                                       nsICanvasRenderingContextInternal **aContext)
     450                 : {
     451               0 :   NS_ENSURE_ARG(aContext);
     452                 : 
     453               0 :   NS_ConvertUTF16toUTF8 ctxId(aContextId);
     454                 : 
     455                 :   // check that ctxId is clamped to A-Za-z0-9_-
     456               0 :   for (PRUint32 i = 0; i < ctxId.Length(); i++) {
     457               0 :     if ((ctxId[i] < 'A' || ctxId[i] > 'Z') &&
     458               0 :         (ctxId[i] < 'a' || ctxId[i] > 'z') &&
     459               0 :         (ctxId[i] < '0' || ctxId[i] > '9') &&
     460               0 :         (ctxId[i] != '-') &&
     461               0 :         (ctxId[i] != '_'))
     462                 :     {
     463                 :       // XXX ERRMSG we need to report an error to developers here! (bug 329026)
     464               0 :       return NS_OK;
     465                 :     }
     466                 :   }
     467                 : 
     468               0 :   nsCString ctxString("@mozilla.org/content/canvas-rendering-context;1?id=");
     469               0 :   ctxString.Append(ctxId);
     470                 : 
     471               0 :   if (aForceThebes && ctxId.EqualsASCII("2d")) {
     472               0 :     ctxString.AssignASCII("@mozilla.org/content/2dthebes-canvas-rendering-context;1");
     473                 :   }
     474                 : 
     475                 :   nsresult rv;
     476                 :   nsCOMPtr<nsICanvasRenderingContextInternal> ctx =
     477               0 :     do_CreateInstance(ctxString.get(), &rv);
     478               0 :   if (rv == NS_ERROR_OUT_OF_MEMORY) {
     479               0 :     *aContext = nsnull;
     480               0 :     return NS_ERROR_OUT_OF_MEMORY;
     481                 :   }
     482               0 :   if (NS_FAILED(rv)) {
     483               0 :     *aContext = nsnull;
     484                 :     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
     485               0 :     return NS_OK;
     486                 :   }
     487                 : 
     488               0 :   rv = ctx->SetCanvasElement(this);
     489               0 :   if (NS_FAILED(rv)) {
     490               0 :     *aContext = nsnull;
     491               0 :     return rv;
     492                 :   }
     493                 : 
     494               0 :   ctx.forget(aContext);
     495                 : 
     496               0 :   return rv;
     497                 : }
     498                 : 
     499                 : NS_IMETHODIMP
     500               0 : nsHTMLCanvasElement::GetContext(const nsAString& aContextId,
     501                 :                                 const jsval& aContextOptions,
     502                 :                                 nsISupports **aContext)
     503                 : {
     504                 :   nsresult rv;
     505                 : 
     506               0 :   bool forceThebes = false;
     507                 : 
     508               0 :   while (mCurrentContextId.IsEmpty()) {
     509               0 :     rv = GetContextHelper(aContextId, forceThebes, getter_AddRefs(mCurrentContext));
     510               0 :     NS_ENSURE_SUCCESS(rv, rv);
     511               0 :     if (!mCurrentContext) {
     512               0 :       return NS_OK;
     513                 :     }
     514                 : 
     515                 :     // Ensure that the context participates in CC.  Note that returning a
     516                 :     // CC participant from QI doesn't addref.
     517               0 :     nsXPCOMCycleCollectionParticipant *cp = nsnull;
     518               0 :     CallQueryInterface(mCurrentContext, &cp);
     519               0 :     if (!cp) {
     520               0 :       mCurrentContext = nsnull;
     521               0 :       return NS_ERROR_FAILURE;
     522                 :     }
     523                 : 
     524               0 :     nsCOMPtr<nsIWritablePropertyBag2> contextProps;
     525               0 :     if (!JSVAL_IS_NULL(aContextOptions) &&
     526               0 :         !JSVAL_IS_VOID(aContextOptions))
     527                 :     {
     528               0 :       JSContext *cx = nsContentUtils::GetCurrentJSContext();
     529                 : 
     530                 :       // note: if any contexts end up supporting something other
     531                 :       // than objects, e.g. plain strings, then we'll need to expand
     532                 :       // this to know how to create nsISupportsStrings etc.
     533               0 :       if (JSVAL_IS_OBJECT(aContextOptions)) {
     534               0 :         contextProps = do_CreateInstance("@mozilla.org/hash-property-bag;1");
     535                 : 
     536               0 :         JSObject *opts = JSVAL_TO_OBJECT(aContextOptions);
     537               0 :         JS::AutoIdArray props(cx, JS_Enumerate(cx, opts));
     538               0 :         for (size_t i = 0; !!props && i < props.length(); ++i) {
     539               0 :           jsid propid = props[i];
     540                 :           jsval propname, propval;
     541               0 :           if (!JS_IdToValue(cx, propid, &propname) ||
     542               0 :               !JS_GetPropertyById(cx, opts, propid, &propval)) {
     543               0 :             continue;
     544                 :           }
     545                 : 
     546               0 :           JSString *propnameString = JS_ValueToString(cx, propname);
     547               0 :           nsDependentJSString pstr;
     548               0 :           if (!propnameString || !pstr.init(cx, propnameString)) {
     549               0 :             mCurrentContext = nsnull;
     550               0 :             return NS_ERROR_FAILURE;
     551                 :           }
     552                 : 
     553               0 :           if (JSVAL_IS_BOOLEAN(propval)) {
     554               0 :             contextProps->SetPropertyAsBool(pstr, JSVAL_TO_BOOLEAN(propval));
     555               0 :           } else if (JSVAL_IS_INT(propval)) {
     556               0 :             contextProps->SetPropertyAsInt32(pstr, JSVAL_TO_INT(propval));
     557               0 :           } else if (JSVAL_IS_DOUBLE(propval)) {
     558               0 :             contextProps->SetPropertyAsDouble(pstr, JSVAL_TO_DOUBLE(propval));
     559               0 :           } else if (JSVAL_IS_STRING(propval)) {
     560               0 :             JSString *propvalString = JS_ValueToString(cx, propval);
     561               0 :             nsDependentJSString vstr;
     562               0 :             if (!propvalString || !vstr.init(cx, propvalString)) {
     563               0 :               mCurrentContext = nsnull;
     564               0 :               return NS_ERROR_FAILURE;
     565                 :             }
     566                 : 
     567               0 :             contextProps->SetPropertyAsAString(pstr, vstr);
     568                 :           }
     569                 :         }
     570                 :       }
     571                 :     }
     572                 : 
     573               0 :     rv = UpdateContext(contextProps);
     574               0 :     if (NS_FAILED(rv)) {
     575               0 :       if (!forceThebes) {
     576                 :         // Try again with a Thebes context
     577               0 :         forceThebes = true;
     578               0 :         continue;
     579                 :       }
     580               0 :       return rv;
     581                 :     }
     582                 : 
     583               0 :     mCurrentContextId.Assign(aContextId);
     584               0 :     break;
     585                 :   }
     586               0 :   if (!mCurrentContextId.Equals(aContextId)) {
     587                 :     //XXX eventually allow for more than one active context on a given canvas
     588               0 :     return NS_OK;
     589                 :   }
     590                 : 
     591               0 :   NS_ADDREF (*aContext = mCurrentContext);
     592               0 :   return NS_OK;
     593                 : }
     594                 : 
     595                 : NS_IMETHODIMP
     596               0 : nsHTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
     597                 :                                       nsISupports **aContext)
     598                 : {
     599               0 :   if(!nsContentUtils::IsCallerTrustedForRead()) {
     600                 :     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
     601               0 :     return NS_ERROR_DOM_SECURITY_ERR;
     602                 :   }
     603                 : 
     604                 :   // We only support 2d shmem contexts for now.
     605               0 :   if (!aContextId.Equals(NS_LITERAL_STRING("2d")))
     606               0 :     return NS_ERROR_INVALID_ARG;
     607                 : 
     608               0 :   if (mCurrentContextId.IsEmpty()) {
     609               0 :     nsresult rv = GetContextHelper(aContextId, false, getter_AddRefs(mCurrentContext));
     610               0 :     NS_ENSURE_SUCCESS(rv, rv);
     611               0 :     if (!mCurrentContext) {
     612               0 :       return NS_OK;
     613                 :     }
     614                 : 
     615               0 :     mCurrentContext->SetIsIPC(true);
     616                 : 
     617               0 :     rv = UpdateContext();
     618               0 :     NS_ENSURE_SUCCESS(rv, rv);
     619                 : 
     620               0 :     mCurrentContextId.Assign(aContextId);
     621               0 :   } else if (!mCurrentContextId.Equals(aContextId)) {
     622                 :     //XXX eventually allow for more than one active context on a given canvas
     623               0 :     return NS_ERROR_INVALID_ARG;
     624                 :   }
     625                 : 
     626               0 :   NS_ADDREF (*aContext = mCurrentContext);
     627               0 :   return NS_OK;
     628                 : }
     629                 : 
     630                 : nsresult
     631               0 : nsHTMLCanvasElement::UpdateContext(nsIPropertyBag *aNewContextOptions)
     632                 : {
     633               0 :   if (!mCurrentContext)
     634               0 :     return NS_OK;
     635                 : 
     636               0 :   nsIntSize sz = GetWidthHeight();
     637                 : 
     638               0 :   nsresult rv = mCurrentContext->SetIsOpaque(GetIsOpaque());
     639               0 :   if (NS_FAILED(rv)) {
     640               0 :     mCurrentContext = nsnull;
     641               0 :     mCurrentContextId.Truncate();
     642               0 :     return rv;
     643                 :   }
     644                 : 
     645               0 :   rv = mCurrentContext->SetContextOptions(aNewContextOptions);
     646               0 :   if (NS_FAILED(rv)) {
     647               0 :     mCurrentContext = nsnull;
     648               0 :     mCurrentContextId.Truncate();
     649               0 :     return rv;
     650                 :   }
     651                 : 
     652               0 :   rv = mCurrentContext->SetDimensions(sz.width, sz.height);
     653               0 :   if (NS_FAILED(rv)) {
     654               0 :     mCurrentContext = nsnull;
     655               0 :     mCurrentContextId.Truncate();
     656               0 :     return rv;
     657                 :   }
     658                 : 
     659               0 :   return rv;
     660                 : }
     661                 : 
     662                 : nsIFrame *
     663               0 : nsHTMLCanvasElement::GetPrimaryCanvasFrame()
     664                 : {
     665               0 :   return GetPrimaryFrame(Flush_Frames);
     666                 : }
     667                 : 
     668                 : nsIntSize
     669               0 : nsHTMLCanvasElement::GetSize()
     670                 : {
     671               0 :   return GetWidthHeight();
     672                 : }
     673                 : 
     674                 : bool
     675               0 : nsHTMLCanvasElement::IsWriteOnly()
     676                 : {
     677               0 :   return mWriteOnly;
     678                 : }
     679                 : 
     680                 : void
     681               0 : nsHTMLCanvasElement::SetWriteOnly()
     682                 : {
     683               0 :   mWriteOnly = true;
     684               0 : }
     685                 : 
     686                 : void
     687               0 : nsHTMLCanvasElement::InvalidateCanvasContent(const gfxRect* damageRect)
     688                 : {
     689                 :   // We don't need to flush anything here; if there's no frame or if
     690                 :   // we plan to reframe we don't need to invalidate it anyway.
     691               0 :   nsIFrame *frame = GetPrimaryFrame();
     692               0 :   if (!frame)
     693               0 :     return;
     694                 : 
     695               0 :   frame->MarkLayersActive(nsChangeHint(0));
     696                 : 
     697               0 :   nsRect invalRect;
     698               0 :   nsRect contentArea = frame->GetContentRect();
     699               0 :   if (damageRect) {
     700               0 :     nsIntSize size = GetWidthHeight();
     701               0 :     if (size.width != 0 && size.height != 0) {
     702                 : 
     703                 :       // damageRect and size are in CSS pixels; contentArea is in appunits
     704                 :       // We want a rect in appunits; so avoid doing pixels-to-appunits and
     705                 :       // vice versa conversion here.
     706               0 :       gfxRect realRect(*damageRect);
     707                 :       realRect.Scale(contentArea.width / gfxFloat(size.width),
     708               0 :                      contentArea.height / gfxFloat(size.height));
     709               0 :       realRect.RoundOut();
     710                 : 
     711                 :       // then make it a nsRect
     712               0 :       invalRect = nsRect(realRect.X(), realRect.Y(),
     713               0 :                          realRect.Width(), realRect.Height());
     714                 : 
     715               0 :       invalRect = invalRect.Intersect(nsRect(nsPoint(0,0), contentArea.Size()));
     716                 :     }
     717                 :   } else {
     718               0 :     invalRect = nsRect(nsPoint(0, 0), contentArea.Size());
     719                 :   }
     720               0 :   invalRect.MoveBy(contentArea.TopLeft() - frame->GetPosition());
     721                 : 
     722               0 :   Layer* layer = frame->InvalidateLayer(invalRect, nsDisplayItem::TYPE_CANVAS);
     723               0 :   if (layer) {
     724               0 :     static_cast<CanvasLayer*>(layer)->Updated();
     725                 :   }
     726                 : }
     727                 : 
     728                 : void
     729               0 : nsHTMLCanvasElement::InvalidateCanvas()
     730                 : {
     731                 :   // We don't need to flush anything here; if there's no frame or if
     732                 :   // we plan to reframe we don't need to invalidate it anyway.
     733               0 :   nsIFrame *frame = GetPrimaryFrame();
     734               0 :   if (!frame)
     735               0 :     return;
     736                 : 
     737               0 :   frame->Invalidate(frame->GetContentRect() - frame->GetPosition());
     738                 : }
     739                 : 
     740                 : PRInt32
     741               0 : nsHTMLCanvasElement::CountContexts()
     742                 : {
     743               0 :   if (mCurrentContext)
     744               0 :     return 1;
     745                 : 
     746               0 :   return 0;
     747                 : }
     748                 : 
     749                 : nsICanvasRenderingContextInternal *
     750               0 : nsHTMLCanvasElement::GetContextAtIndex(PRInt32 index)
     751                 : {
     752               0 :   if (mCurrentContext && index == 0)
     753               0 :     return mCurrentContext;
     754                 : 
     755               0 :   return NULL;
     756                 : }
     757                 : 
     758                 : bool
     759               0 : nsHTMLCanvasElement::GetIsOpaque()
     760                 : {
     761               0 :   return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
     762                 : }
     763                 : 
     764                 : already_AddRefed<CanvasLayer>
     765               0 : nsHTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
     766                 :                                     CanvasLayer *aOldLayer,
     767                 :                                     LayerManager *aManager)
     768                 : {
     769               0 :   if (!mCurrentContext)
     770               0 :     return nsnull;
     771                 : 
     772               0 :   return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
     773                 : }
     774                 : 
     775                 : bool
     776               0 : nsHTMLCanvasElement::ShouldForceInactiveLayer(LayerManager *aManager)
     777                 : {
     778               0 :   return !mCurrentContext || mCurrentContext->ShouldForceInactiveLayer(aManager);
     779                 : }
     780                 : 
     781                 : void
     782               0 : nsHTMLCanvasElement::MarkContextClean()
     783                 : {
     784               0 :   if (!mCurrentContext)
     785               0 :     return;
     786                 : 
     787               0 :   mCurrentContext->MarkContextClean();
     788                 : }
     789                 : 
     790                 : NS_IMETHODIMP_(nsIntSize)
     791               0 : nsHTMLCanvasElement::GetSizeExternal()
     792                 : {
     793               0 :   return GetWidthHeight();
     794                 : }
     795                 : 
     796                 : NS_IMETHODIMP
     797               0 : nsHTMLCanvasElement::RenderContextsExternal(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter)
     798                 : {
     799               0 :   if (!mCurrentContext)
     800               0 :     return NS_OK;
     801                 : 
     802               0 :   return mCurrentContext->Render(aContext, aFilter);
     803                 : }
     804                 : 
     805                 : nsresult NS_NewCanvasRenderingContext2DThebes(nsIDOMCanvasRenderingContext2D** aResult);
     806                 : nsresult NS_NewCanvasRenderingContext2DAzure(nsIDOMCanvasRenderingContext2D** aResult);
     807                 : 
     808                 : nsresult
     809               0 : NS_NewCanvasRenderingContext2D(nsIDOMCanvasRenderingContext2D** aResult)
     810                 : {
     811               0 :   Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
     812               0 :   if (Preferences::GetBool("gfx.canvas.azure.enabled", false)) {
     813               0 :     nsresult rv = NS_NewCanvasRenderingContext2DAzure(aResult);
     814                 :     // If Azure fails, fall back to a classic canvas.
     815               0 :     if (NS_SUCCEEDED(rv)) {
     816               0 :       return rv;
     817                 :     }
     818                 :   }
     819                 : 
     820               0 :   return NS_NewCanvasRenderingContext2DThebes(aResult);
     821            4392 : }

Generated by: LCOV version 1.7