diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js
index bdee2a60991..bac3e91de04 100644
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -376,8 +376,13 @@ BrowserGlue.prototype = {
// Browser startup complete. All initial windows have opened.
_onBrowserStartup: function BG__onBrowserStartup() {
// Show about:rights notification, if needed.
- if (this._shouldShowRights())
+ if (this._shouldShowRights()) {
this._showRightsNotification();
+ } else {
+ // Only show telemetry notification when about:rights notification is not shown.
+ this._showTelemetryNotification();
+ }
+
// Show update notification, if needed.
if (Services.prefs.prefHasUserValue("app.update.postupdate"))
@@ -735,6 +740,72 @@ BrowserGlue.prototype = {
}
},
+ _showTelemetryNotification: function BG__showTelemetryNotification() {
+ const PREF_TELEMETRY_PROMPTED = "toolkit.telemetry.prompted";
+ const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
+ const PREF_TELEMETRY_INFOURL = "toolkit.telemetry.infoURL";
+ const PREF_TELEMETRY_SERVER_OWNER = "toolkit.telemetry.server_owner";
+
+ try {
+ // If the user hasn't already been prompted, ask if they want to
+ // send telemetry data.
+ if (Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED) ||
+ Services.prefs.getBoolPref(PREF_TELEMETRY_PROMPTED))
+ return;
+ } catch(e) {}
+
+ // Stick the notification onto the selected tab of the active browser window.
+ var win = this.getMostRecentBrowserWindow();
+ var browser = win.gBrowser; // for closure in notification bar callback
+ var notifyBox = browser.getNotificationBox();
+
+ var browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
+ var brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
+
+ var productName = brandBundle.GetStringFromName("brandFullName");
+ var serverOwner = Services.prefs.getCharPref(PREF_TELEMETRY_SERVER_OWNER);
+ var telemetryText = browserBundle.formatStringFromName("telemetryText", [productName, serverOwner], 2);
+
+ var buttons = [
+ {
+ label: browserBundle.GetStringFromName("telemetryYesButtonLabel"),
+ accessKey: browserBundle.GetStringFromName("telemetryYesButtonAccessKey"),
+ popup: null,
+ callback: function(aNotificationBar, aButton) {
+ Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
+ }
+ },
+ {
+ label: browserBundle.GetStringFromName("telemetryNoButtonLabel"),
+ accessKey: browserBundle.GetStringFromName("telemetryNoButtonAccessKey"),
+ popup: null,
+ callback: function(aNotificationBar, aButton) {}
+ }
+ ];
+
+ // Set pref to indicate we've shown the notification.
+ Services.prefs.setBoolPref(PREF_TELEMETRY_PROMPTED, true);
+
+ var notification = notifyBox.appendNotification(telemetryText, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
+ notification.persistence = 3; // arbitrary number, just so bar sticks around for a bit
+
+ let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ let link = notification.ownerDocument.createElementNS(XULNS, "label");
+ link.className = "text-link telemetry-text-link";
+ link.setAttribute("value", browserBundle.GetStringFromName("telemetryLinkLabel"));
+ link.addEventListener('click', function() {
+ // Open the learn more url in a new tab
+ browser.selectedTab = browser.addTab(Services.prefs.getCharPref(PREF_TELEMETRY_INFOURL));
+ // Remove the notification on which the user clicked
+ notification.parentNode.removeNotification(notification, true);
+ // Add a new notification to that tab, with no "Learn more" link
+ var notifyBox = browser.getNotificationBox();
+ notifyBox.appendNotification(telemetryText, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
+ }, false);
+ let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
+ description.appendChild(link);
+ },
+
_showPluginUpdatePage: function BG__showPluginUpdatePage() {
Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false);
diff --git a/browser/components/preferences/advanced.xul b/browser/components/preferences/advanced.xul
index 98a9c47270f..92537f1ea7f 100644
--- a/browser/components/preferences/advanced.xul
+++ b/browser/components/preferences/advanced.xul
@@ -82,6 +82,10 @@
type="bool"/>
#endif
+
+
@@ -193,11 +197,11 @@
preference="layout.spellcheckDefault"/>
-#ifdef HAVE_SHELL_SERVICE
+#ifdef HAVE_SHELL_SERVICE
#endif
-
#endif
+
+
diff --git a/browser/components/sessionstore/src/nsSessionStore.js b/browser/components/sessionstore/src/nsSessionStore.js
index bb78d1e9ff4..e0348a4d66a 100644
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -2933,8 +2933,7 @@ SessionStoreService.prototype = {
// force session history to update its internal index and call reload
// instead of gotoIndex. See bug 597315.
browser.webNavigation.sessionHistory.getEntryAtIndex(activeIndex, true);
- browser.webNavigation.sessionHistory.
- QueryInterface(Ci.nsISHistory).reloadCurrentEntry();
+ browser.webNavigation.sessionHistory.reloadCurrentEntry();
}
catch (ex) {
// ignore page load errors
diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties
index fddc48202a9..da966fb0587 100644
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -325,3 +325,13 @@ syncPromoNotification.bookmarks.description=You can access your bookmarks on all
# The final space separates this text from the Learn More link.
syncPromoNotification.passwords.description=You can access your passwords on all your devices with %S.\u0020
syncPromoNotification.learnMoreLinkText=Learn More
+
+# Telemetry prompt
+# LOCALIZATION NOTE (telemetryText): %1$S will be replaced by brandFullName,
+# and %2$S by the value of the toolkit.telemetry.server_owner preference.
+telemetryText = Would you like to help improve %1$S by automatically reporting memory usage, performance, and responsiveness to %2$S?
+telemetryLinkLabel = Learn More
+telemetryYesButtonLabel = Yes
+telemetryYesButtonAccessKey = Y
+telemetryNoButtonLabel = No
+telemetryNoButtonAccessKey = N
diff --git a/browser/locales/en-US/chrome/browser/preferences/advanced.dtd b/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
index 042f5b4fe7e..cf3a70c71c8 100644
--- a/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
@@ -29,6 +29,8 @@
+
+
diff --git a/browser/themes/pinstripe/browser/browser.css b/browser/themes/pinstripe/browser/browser.css
index 9c71e660b7f..f72b92f74c8 100644
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -2167,6 +2167,10 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
-moz-margin-start: 0; /* override default label margin to match description margin */
}
+.telemetry-text-link {
+ color: #fff;
+}
+
#addons-notification-icon {
list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric-16.png);
}
diff --git a/content/base/public/nsINode.h b/content/base/public/nsINode.h
index b24f389afaa..a327bb10c57 100644
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -979,6 +979,8 @@ public:
return NS_ERROR_NOT_IMPLEMENTED;
}
+ nsresult Normalize();
+
/**
* Get the base URI for any relative URIs within this piece of
* content. Generally, this is the document's base URI, but certain
diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp
index 063901708d9..46a5d54c7cd 100644
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -5362,10 +5362,14 @@ public:
mFlags = WANT_ALL_TRACES;
}
- NS_IMETHOD_(void) DescribeNode(CCNodeType type,
- nsrefcnt refcount,
- size_t objsz,
- const char* objname)
+ NS_IMETHOD_(void) DescribeRefCountedNode(nsrefcnt refCount,
+ size_t objSz,
+ const char *objName)
+ {
+ }
+ NS_IMETHOD_(void) DescribeGCedNode(PRBool isMarked,
+ size_t objSz,
+ const char *objName)
{
}
NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports *root)
diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp
index a7de03c22d5..badcc75d821 100644
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1808,11 +1808,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
else {
PR_snprintf(name, sizeof(name), "nsDocument %s", uri.get());
}
- cb.DescribeNode(RefCounted, tmp->mRefCnt.get(), sizeof(nsDocument), name);
+ cb.DescribeRefCountedNode(tmp->mRefCnt.get(), sizeof(nsDocument), name);
}
else {
- cb.DescribeNode(RefCounted, tmp->mRefCnt.get(), sizeof(nsDocument),
- "nsDocument");
+ NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get())
}
// Always need to traverse script objects, so do that before we check
@@ -5774,12 +5773,7 @@ nsDocument::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
NS_IMETHODIMP
nsDocument::Normalize()
{
- for (PRUint32 i = 0; i < mChildren.ChildCount(); ++i) {
- nsCOMPtr node(do_QueryInterface(mChildren.ChildAt(i)));
- node->Normalize();
- }
-
- return NS_OK;
+ return nsIDocument::Normalize();
}
NS_IMETHODIMP
diff --git a/content/base/src/nsFrameMessageManager.cpp b/content/base/src/nsFrameMessageManager.cpp
index def27a981bb..b7ba0bb00db 100644
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -369,7 +369,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
jsval targetv;
nsContentUtils::WrapNative(ctx,
JS_GetGlobalForObject(ctx, object),
- aTarget, &targetv);
+ aTarget, &targetv, nsnull, PR_TRUE);
// To keep compatibility with e10s message manager,
// define empty objects array.
@@ -382,6 +382,11 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
}
}
+ js::AutoValueRooter objectsv(ctx);
+ objectsv.set(OBJECT_TO_JSVAL(aObjectsArray));
+ if (!JS_WrapValue(ctx, objectsv.jsval_addr()))
+ return NS_ERROR_UNEXPECTED;
+
jsval json = JSVAL_NULL;
if (!aJSON.IsEmpty()) {
if (!JS_ParseJSON(ctx, (jschar*)nsString(aJSON).get(),
@@ -400,8 +405,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
JS_DefineProperty(ctx, param, "sync",
BOOLEAN_TO_JSVAL(aSync), NULL, NULL, JSPROP_ENUMERATE);
JS_DefineProperty(ctx, param, "json", json, NULL, NULL, JSPROP_ENUMERATE);
- JS_DefineProperty(ctx, param, "objects", OBJECT_TO_JSVAL(aObjectsArray),
- NULL, NULL, JSPROP_ENUMERATE);
+ JS_DefineProperty(ctx, param, "objects", objectsv.jsval_value(), NULL, NULL, JSPROP_ENUMERATE);
jsval thisValue = JSVAL_VOID;
@@ -421,7 +425,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
}
nsContentUtils::WrapNative(ctx,
JS_GetGlobalForObject(ctx, object),
- defaultThisValue, &thisValue);
+ defaultThisValue, &thisValue, nsnull, PR_TRUE);
} else {
// If the listener is a JS object which has receiveMessage function:
NS_ENSURE_STATE(JS_GetProperty(ctx, object, "receiveMessage",
diff --git a/content/base/src/nsGenericDOMDataNode.cpp b/content/base/src/nsGenericDOMDataNode.cpp
index a35782277cb..bf3f9624dc7 100644
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -172,12 +172,6 @@ nsGenericDOMDataNode::GetPrefix(nsAString& aPrefix)
return NS_OK;
}
-nsresult
-nsGenericDOMDataNode::Normalize()
-{
- return NS_OK;
-}
-
nsresult
nsGenericDOMDataNode::IsSupported(const nsAString& aFeature,
const nsAString& aVersion,
diff --git a/content/base/src/nsGenericDOMDataNode.h b/content/base/src/nsGenericDOMDataNode.h
index 4d92cf2bd14..d341123431d 100644
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -143,7 +143,6 @@ public:
return NS_OK;
}
nsresult GetPrefix(nsAString& aPrefix);
- nsresult Normalize();
nsresult IsSupported(const nsAString& aFeature,
const nsAString& aVersion,
PRBool* aReturn);
diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp
index 11289342e71..016d1f66eb3 100644
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -541,6 +541,96 @@ nsINode::RemoveChild(nsIDOMNode* aOldChild, nsIDOMNode** aReturn)
return rv;
}
+nsresult
+nsINode::Normalize()
+{
+ // First collect list of nodes to be removed
+ nsAutoTArray, 50> nodes;
+
+ PRBool canMerge = PR_FALSE;
+ for (nsIContent* node = this->GetFirstChild();
+ node;
+ node = node->GetNextNode(this)) {
+ if (node->NodeType() != nsIDOMNode::TEXT_NODE) {
+ canMerge = PR_FALSE;
+ continue;
+ }
+
+ if (canMerge || node->TextLength() == 0) {
+ // No need to touch canMerge. That way we can merge across empty
+ // textnodes if and only if the node before is a textnode
+ nodes.AppendElement(node);
+ }
+ else {
+ canMerge = PR_TRUE;
+ }
+
+ // If there's no following sibling, then we need to ensure that we don't
+ // collect following siblings of our (grand)parent as to-be-removed
+ canMerge = canMerge && !!node->GetNextSibling();
+ }
+
+ if (nodes.IsEmpty()) {
+ return NS_OK;
+ }
+
+ // We're relying on mozAutoSubtreeModified to keep the doc alive here.
+ nsIDocument* doc = GetOwnerDoc();
+
+ // Batch possible DOMSubtreeModified events.
+ mozAutoSubtreeModified subtree(doc, nsnull);
+
+ // Fire all DOMNodeRemoved events. Optimize the common case of there being
+ // no listeners
+ PRBool hasRemoveListeners = nsContentUtils::
+ HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED);
+ if (hasRemoveListeners) {
+ for (PRUint32 i = 0; i < nodes.Length(); ++i) {
+ nsContentUtils::MaybeFireNodeRemoved(nodes[i], nodes[i]->GetNodeParent(),
+ doc);
+ }
+ }
+
+ mozAutoDocUpdate batch(doc, UPDATE_CONTENT_MODEL, PR_TRUE);
+
+ // Merge and remove all nodes
+ nsAutoString tmpStr;
+ for (PRUint32 i = 0; i < nodes.Length(); ++i) {
+ nsIContent* node = nodes[i];
+ // Merge with previous node unless empty
+ const nsTextFragment* text = node->GetText();
+ if (text->GetLength()) {
+ nsIContent* target = node->GetPreviousSibling();
+ NS_ASSERTION((target && target->NodeType() == nsIDOMNode::TEXT_NODE) ||
+ hasRemoveListeners,
+ "Should always have a previous text sibling unless "
+ "mutation events messed us up");
+ if (!hasRemoveListeners ||
+ (target && target->NodeType() == nsIDOMNode::TEXT_NODE)) {
+ if (text->Is2b()) {
+ target->AppendText(text->Get2b(), text->GetLength(), PR_TRUE);
+ }
+ else {
+ tmpStr.Truncate();
+ text->AppendTo(tmpStr);
+ target->AppendText(tmpStr.get(), tmpStr.Length(), PR_TRUE);
+ }
+ }
+ }
+
+ // Remove node
+ nsINode* parent = node->GetNodeParent();
+ NS_ASSERTION(parent || hasRemoveListeners,
+ "Should always have a parent unless "
+ "mutation events messed us up");
+ if (parent) {
+ parent->RemoveChildAt(parent->IndexOf(node), PR_TRUE);
+ }
+ }
+
+ return NS_OK;
+}
+
nsresult
nsINode::GetDOMBaseURI(nsAString &aURI) const
{
@@ -2619,103 +2709,6 @@ nsGenericElement::HasAttributeNS(const nsAString& aNamespaceURI,
return NS_OK;
}
-nsresult
-nsGenericElement::JoinTextNodes(nsIContent* aFirst,
- nsIContent* aSecond)
-{
- nsresult rv = NS_OK;
- nsCOMPtr firstText(do_QueryInterface(aFirst, &rv));
-
- if (NS_SUCCEEDED(rv)) {
- nsCOMPtr secondText(do_QueryInterface(aSecond, &rv));
-
- if (NS_SUCCEEDED(rv)) {
- nsAutoString str;
-
- rv = secondText->GetData(str);
- if (NS_SUCCEEDED(rv)) {
- rv = firstText->AppendData(str);
- }
- }
- }
-
- return rv;
-}
-
-nsresult
-nsGenericElement::Normalize()
-{
- // We're relying on mozAutoSubtreeModified to keep the doc alive here.
- nsIDocument* doc = GetOwnerDoc();
-
- // Batch possible DOMSubtreeModified events.
- mozAutoSubtreeModified subtree(doc, nsnull);
-
- bool hasRemoveListeners = nsContentUtils::
- HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED);
-
- nsresult result = NS_OK;
- PRUint32 index, count = GetChildCount();
-
- for (index = 0; (index < count) && (NS_OK == result); index++) {
- nsIContent *child = GetChildAt(index);
-
- switch (child->NodeType()) {
- case nsIDOMNode::TEXT_NODE:
-
- // ensure that if the text node is empty, it is removed
- if (0 == child->TextLength()) {
- if (hasRemoveListeners) {
- nsContentUtils::MaybeFireNodeRemoved(child, this, doc);
- }
- result = RemoveChildAt(index, PR_TRUE);
- if (NS_FAILED(result)) {
- return result;
- }
-
- count--;
- index--;
- break;
- }
-
- if (index+1 < count) {
- // Get the sibling. If it's also a text node, then
- // remove it from the tree and join the two text
- // nodes.
- nsCOMPtr sibling = GetChildAt(index + 1);
-
- if (sibling->NodeType() == nsIDOMNode::TEXT_NODE) {
- if (hasRemoveListeners) {
- nsContentUtils::MaybeFireNodeRemoved(sibling, this, doc);
- }
- result = RemoveChildAt(index+1, PR_TRUE);
- if (NS_FAILED(result)) {
- return result;
- }
-
- result = JoinTextNodes(child, sibling);
- if (NS_FAILED(result)) {
- return result;
- }
- count--;
- index--;
- }
- }
- break;
-
- case nsIDOMNode::ELEMENT_NODE:
- nsCOMPtr element = do_QueryInterface(child);
-
- if (element) {
- result = element->Normalize();
- }
- break;
- }
- }
-
- return result;
-}
-
static nsXBLBinding*
GetFirstBindingWithContent(nsBindingManager* aBmgr, nsIContent* aBoundElem)
{
@@ -4182,12 +4175,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGenericElement)
else {
PR_snprintf(name, sizeof(name), "nsGenericElement %s", localName.get());
}
- cb.DescribeNode(RefCounted, tmp->mRefCnt.get(), sizeof(nsGenericElement),
- name);
+ cb.DescribeRefCountedNode(tmp->mRefCnt.get(), sizeof(nsGenericElement),
+ name);
}
else {
- cb.DescribeNode(RefCounted, tmp->mRefCnt.get(), sizeof(nsGenericElement),
- "nsGenericElement");
+ NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGenericElement, tmp->mRefCnt.get())
}
// Always need to traverse script objects, so do that before we check
diff --git a/content/base/src/nsGenericElement.h b/content/base/src/nsGenericElement.h
index 48494a530f3..fb3d2f144d1 100644
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -400,7 +400,6 @@ public:
NS_IMETHOD GetAttributes(nsIDOMNamedNodeMap** aAttributes);
NS_IMETHOD GetNamespaceURI(nsAString& aNamespaceURI);
NS_IMETHOD GetPrefix(nsAString& aPrefix);
- NS_IMETHOD Normalize();
NS_IMETHOD IsSupported(const nsAString& aFeature,
const nsAString& aVersion, PRBool* aReturn);
NS_IMETHOD HasAttributes(PRBool* aHasAttributes);
@@ -479,14 +478,6 @@ public:
*/
nsresult LeaveLink(nsPresContext* aPresContext);
- /**
- * Take two text nodes and append the second to the first.
- * @param aFirst the node which will contain first + second [INOUT]
- * @param aSecond the node which will be appended
- */
- nsresult JoinTextNodes(nsIContent* aFirst,
- nsIContent* aSecond);
-
/**
* Check whether a spec feature/version is supported.
* @param aObject the object, which should support the feature,
diff --git a/content/base/src/nsNodeInfo.cpp b/content/base/src/nsNodeInfo.cpp
index e9e82ad4b3a..4a05d20f558 100644
--- a/content/base/src/nsNodeInfo.cpp
+++ b/content/base/src/nsNodeInfo.cpp
@@ -198,11 +198,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsNodeInfo)
PR_snprintf(name, sizeof(name), "nsNodeInfo %s", localName.get());
}
- cb.DescribeNode(RefCounted, tmp->mRefCnt.get(), sizeof(nsNodeInfo), name);
+ cb.DescribeRefCountedNode(tmp->mRefCnt.get(), sizeof(nsNodeInfo), name);
}
else {
- cb.DescribeNode(RefCounted, tmp->mRefCnt.get(), sizeof(nsNodeInfo),
- "nsNodeInfo");
+ NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsNodeInfo, tmp->mRefCnt.get())
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mOwnerManager,
diff --git a/content/canvas/public/nsICanvasRenderingContextInternal.h b/content/canvas/public/nsICanvasRenderingContextInternal.h
index b8831bc68ff..dd9823ded8a 100644
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -42,6 +42,7 @@
#include "nsIInputStream.h"
#include "nsIDocShell.h"
#include "gfxPattern.h"
+#include "mozilla/RefPtr.h"
#define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
{ 0xffb42d3c, 0x8281, 0x44c8, \
@@ -61,6 +62,9 @@ class LayerManager;
namespace ipc {
class Shmem;
}
+namespace gfx {
+class SourceSurface;
+}
}
class nsICanvasRenderingContextInternal : public nsISupports {
@@ -96,6 +100,11 @@ public:
// If this canvas context can be represented with a simple Thebes surface,
// return the surface. Otherwise returns an error.
NS_IMETHOD GetThebesSurface(gfxASurface **surface) = 0;
+
+ // This gets an Azure SourceSurface for the canvas, this will be a snapshot
+ // of the canvas at the time it was called. This will return null for a
+ // non-azure canvas.
+ virtual mozilla::TemporaryRef GetSurfaceSnapshot() = 0;
// If this context is opaque, the backing store of the canvas should
// be created as opaque; all compositing operators should assume the
diff --git a/content/canvas/src/Makefile.in b/content/canvas/src/Makefile.in
index f98c7f4699d..c2738b68912 100644
--- a/content/canvas/src/Makefile.in
+++ b/content/canvas/src/Makefile.in
@@ -55,6 +55,7 @@ CPPSRCS = \
CanvasImageCache.cpp \
CanvasUtils.cpp \
nsCanvasRenderingContext2D.cpp \
+ nsCanvasRenderingContext2DAzure.cpp \
DocumentRendererParent.cpp \
DocumentRendererChild.cpp \
$(NULL)
diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h
index 84db7299fcc..949bd8bb959 100644
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -342,6 +342,9 @@ public:
const PRUnichar* aEncoderOptions,
nsIInputStream **aStream);
NS_IMETHOD GetThebesSurface(gfxASurface **surface);
+ mozilla::TemporaryRef GetSurfaceSnapshot()
+ { return nsnull; }
+
NS_IMETHOD SetIsOpaque(PRBool b) { return NS_OK; };
NS_IMETHOD SetContextOptions(nsIPropertyBag *aOptions);
diff --git a/content/canvas/src/nsCanvasRenderingContext2D.cpp b/content/canvas/src/nsCanvasRenderingContext2D.cpp
index 97abaa73c44..b57aea6d378 100644
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -373,6 +373,9 @@ public:
const PRUnichar* aEncoderOptions,
nsIInputStream **aStream);
NS_IMETHOD GetThebesSurface(gfxASurface **surface);
+ mozilla::TemporaryRef GetSurfaceSnapshot()
+ { return nsnull; }
+
NS_IMETHOD SetIsOpaque(PRBool isOpaque);
NS_IMETHOD Reset();
already_AddRefed GetCanvasLayer(nsDisplayListBuilder* aBuilder,
@@ -813,7 +816,7 @@ PRUint8 (*nsCanvasRenderingContext2D::sUnpremultiplyTable)[256] = nsnull;
PRUint8 (*nsCanvasRenderingContext2D::sPremultiplyTable)[256] = nsnull;
nsresult
-NS_NewCanvasRenderingContext2D(nsIDOMCanvasRenderingContext2D** aResult)
+NS_NewCanvasRenderingContext2DThebes(nsIDOMCanvasRenderingContext2D** aResult)
{
nsRefPtr ctx = new nsCanvasRenderingContext2D();
if (!ctx)
diff --git a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
new file mode 100644
index 00000000000..39a0bdae45b
--- /dev/null
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -0,0 +1,4352 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bas Schouten
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "base/basictypes.h"
+
+#include "nsIDOMXULElement.h"
+
+#include "prmem.h"
+#include "prenv.h"
+
+#include "nsIServiceManager.h"
+#include "nsMathUtils.h"
+
+#include "nsContentUtils.h"
+
+#include "nsIDOMDocument.h"
+#include "nsIDocument.h"
+#include "nsIDOMCanvasRenderingContext2D.h"
+#include "nsICanvasRenderingContextInternal.h"
+#include "nsHTMLCanvasElement.h"
+#include "nsSVGEffects.h"
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
+#include "nsIVariant.h"
+
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIFrame.h"
+#include "nsDOMError.h"
+#include "nsIScriptError.h"
+
+#include "nsCSSParser.h"
+#include "mozilla/css/StyleRule.h"
+#include "mozilla/css/Declaration.h"
+#include "nsComputedDOMStyle.h"
+#include "nsStyleSet.h"
+
+#include "nsPrintfCString.h"
+
+#include "nsReadableUtils.h"
+
+#include "nsColor.h"
+#include "nsGfxCIID.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIDocShell.h"
+#include "nsIDOMWindow.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDocShellTreeNode.h"
+#include "nsIXPConnect.h"
+#include "jsapi.h"
+#include "nsDisplayList.h"
+
+#include "nsTArray.h"
+
+#include "imgIEncoder.h"
+
+#include "gfxContext.h"
+#include "gfxASurface.h"
+#include "gfxImageSurface.h"
+#include "gfxPlatform.h"
+#include "gfxFont.h"
+#include "gfxTextRunCache.h"
+#include "gfxBlur.h"
+#include "gfxUtils.h"
+
+#include "nsFrameManager.h"
+#include "nsFrameLoader.h"
+#include "nsBidiPresUtils.h"
+#include "Layers.h"
+#include "CanvasUtils.h"
+#include "nsIMemoryReporter.h"
+#include "nsStyleUtil.h"
+#include "CanvasImageCache.h"
+
+#include
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/ipc/PDocumentRendererParent.h"
+#include "mozilla/dom/PBrowserParent.h"
+#include "mozilla/ipc/DocumentRendererParent.h"
+
+#include "mozilla/gfx/2D.h"
+
+#ifdef XP_WIN
+#include "gfxWindowsPlatform.h"
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+// windows.h (included by chromium code) defines this, in its infinite wisdom
+#undef DrawText
+
+using namespace mozilla;
+using namespace mozilla::layers;
+using namespace mozilla::dom;
+using namespace mozilla::gfx;
+using namespace mozilla::ipc;
+using namespace mozilla::css;
+
+namespace mgfx = mozilla::gfx;
+
+static float kDefaultFontSize = 10.0;
+static NS_NAMED_LITERAL_STRING(kDefaultFontName, "sans-serif");
+static NS_NAMED_LITERAL_STRING(kDefaultFontStyle, "10px sans-serif");
+
+/* Float validation stuff */
+#define VALIDATE(_f) if (!NS_finite(_f)) return PR_FALSE
+
+static PRBool FloatValidate (double f1) {
+ VALIDATE(f1);
+ return PR_TRUE;
+}
+
+static PRBool FloatValidate (double f1, double f2) {
+ VALIDATE(f1); VALIDATE(f2);
+ return PR_TRUE;
+}
+
+static PRBool FloatValidate (double f1, double f2, double f3) {
+ VALIDATE(f1); VALIDATE(f2); VALIDATE(f3);
+ return PR_TRUE;
+}
+
+static PRBool FloatValidate (double f1, double f2, double f3, double f4) {
+ VALIDATE(f1); VALIDATE(f2); VALIDATE(f3); VALIDATE(f4);
+ return PR_TRUE;
+}
+
+static PRBool FloatValidate (double f1, double f2, double f3, double f4, double f5) {
+ VALIDATE(f1); VALIDATE(f2); VALIDATE(f3); VALIDATE(f4); VALIDATE(f5);
+ return PR_TRUE;
+}
+
+static PRBool FloatValidate (double f1, double f2, double f3, double f4, double f5, double f6) {
+ VALIDATE(f1); VALIDATE(f2); VALIDATE(f3); VALIDATE(f4); VALIDATE(f5); VALIDATE(f6);
+ return PR_TRUE;
+}
+
+#undef VALIDATE
+
+/* Memory reporter stuff */
+static nsIMemoryReporter *gCanvasAzureMemoryReporter = nsnull;
+static PRInt64 gCanvasAzureMemoryUsed = 0;
+
+static PRInt64 GetCanvasAzureMemoryUsed(void *) {
+ return gCanvasAzureMemoryUsed;
+}
+
+// This is MR_OTHER because it's not always clear where in memory the pixels of
+// a canvas are stored. Furthermore, this memory will be tracked by the
+// underlying surface implementations. See bug 655638 for details.
+NS_MEMORY_REPORTER_IMPLEMENT(CanvasAzureMemory,
+ "canvas-2d-pixel-bytes",
+ MR_OTHER,
+ "Memory used by 2D canvases. Each canvas requires (width * height * 4) "
+ "bytes.",
+ GetCanvasAzureMemoryUsed,
+ nsnull)
+
+/**
+ ** nsCanvasGradientAzure
+ **/
+#define NS_CANVASGRADIENTAZURE_PRIVATE_IID \
+ {0x28425a6a, 0x90e0, 0x4d42, {0x9c, 0x75, 0xff, 0x60, 0x09, 0xb3, 0x10, 0xa8}}
+class nsCanvasGradientAzure : public nsIDOMCanvasGradient
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASGRADIENTAZURE_PRIVATE_IID)
+
+ enum Type
+ {
+ LINEAR = 0,
+ RADIAL
+ };
+
+ Type GetType()
+ {
+ return mType;
+ }
+
+
+ GradientStops *GetGradientStopsForTarget(DrawTarget *aRT)
+ {
+ if (mStops && mStops->GetBackendType() == aRT->GetType()) {
+ return mStops;
+ }
+
+ mStops = aRT->CreateGradientStops(mRawStops.Elements(), mRawStops.Length());
+
+ return mStops;
+ }
+
+ NS_DECL_ISUPPORTS
+
+protected:
+ nsCanvasGradientAzure(Type aType) : mType(aType)
+ {}
+
+ nsTArray mRawStops;
+ RefPtr mStops;
+ Type mType;
+};
+
+class nsCanvasRadialGradientAzure : public nsCanvasGradientAzure
+{
+public:
+ nsCanvasRadialGradientAzure(const Point &aBeginOrigin, Float aBeginRadius,
+ const Point &aEndOrigin, Float aEndRadius)
+ : nsCanvasGradientAzure(RADIAL)
+ , mCenter(aEndOrigin)
+ , mRadius(aEndRadius)
+ {
+ mOffsetStart = aBeginRadius / mRadius;
+
+ mOffsetRatio = 1 - mOffsetStart;
+ mOrigin = ((mCenter * aBeginRadius) - (aBeginOrigin * mRadius)) /
+ (aBeginRadius - mRadius);
+ }
+
+
+ /* nsIDOMCanvasGradient */
+ NS_IMETHOD AddColorStop (float offset,
+ const nsAString& colorstr)
+ {
+ if (!FloatValidate(offset) || offset < 0.0 || offset > 1.0) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+
+ nscolor color;
+ nsCSSParser parser;
+ nsresult rv = parser.ParseColorString(nsString(colorstr),
+ nsnull, 0, &color);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_DOM_SYNTAX_ERR;
+ }
+
+ mStops = nsnull;
+
+ GradientStop newStop;
+
+ newStop.offset = offset * mOffsetRatio + mOffsetStart;
+ newStop.color = Color::FromABGR(color);
+
+ mRawStops.AppendElement(newStop);
+
+ return NS_OK;
+ }
+
+ // XXX - Temporary gradient code, this will be fixed soon as per bug 666097
+ Point mCenter;
+ Float mRadius;
+ Point mOrigin;
+
+ Float mOffsetStart;
+ Float mOffsetRatio;
+};
+
+class nsCanvasLinearGradientAzure : public nsCanvasGradientAzure
+{
+public:
+ nsCanvasLinearGradientAzure(const Point &aBegin, const Point &aEnd)
+ : nsCanvasGradientAzure(LINEAR)
+ , mBegin(aBegin)
+ , mEnd(aEnd)
+ {
+ }
+
+ /* nsIDOMCanvasGradient */
+ NS_IMETHOD AddColorStop (float offset,
+ const nsAString& colorstr)
+ {
+ if (!FloatValidate(offset) || offset < 0.0 || offset > 1.0) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+
+ nscolor color;
+ nsCSSParser parser;
+ nsresult rv = parser.ParseColorString(nsString(colorstr),
+ nsnull, 0, &color);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_DOM_SYNTAX_ERR;
+ }
+
+ mStops = nsnull;
+
+ GradientStop newStop;
+
+ newStop.offset = offset;
+ newStop.color = Color::FromABGR(color);
+
+ mRawStops.AppendElement(newStop);
+
+ return NS_OK;
+ }
+
+protected:
+ friend class nsCanvasRenderingContext2DAzure;
+
+ // Beginning of linear gradient.
+ Point mBegin;
+ // End of linear gradient.
+ Point mEnd;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasGradientAzure, NS_CANVASGRADIENTAZURE_PRIVATE_IID)
+
+NS_IMPL_ADDREF(nsCanvasGradientAzure)
+NS_IMPL_RELEASE(nsCanvasGradientAzure)
+
+// XXX
+// DOMCI_DATA(CanvasGradient, nsCanvasGradientAzure)
+
+NS_INTERFACE_MAP_BEGIN(nsCanvasGradientAzure)
+ NS_INTERFACE_MAP_ENTRY(nsCanvasGradientAzure)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasGradient)
+ NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasGradient)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+/**
+ ** nsCanvasPatternAzure
+ **/
+#define NS_CANVASPATTERNAZURE_PRIVATE_IID \
+ {0xc9bacc25, 0x28da, 0x421e, {0x9a, 0x4b, 0xbb, 0xd6, 0x93, 0x05, 0x12, 0xbc}}
+class nsCanvasPatternAzure : public nsIDOMCanvasPattern
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERNAZURE_PRIVATE_IID)
+
+ enum RepeatMode
+ {
+ REPEAT,
+ REPEATX,
+ REPEATY,
+ NOREPEAT
+ };
+
+ nsCanvasPatternAzure(SourceSurface* aSurface,
+ RepeatMode aRepeat,
+ nsIPrincipal* principalForSecurityCheck,
+ PRBool forceWriteOnly)
+ : mSurface(aSurface)
+ , mRepeat(aRepeat)
+ , mPrincipal(principalForSecurityCheck)
+ , mForceWriteOnly(forceWriteOnly)
+ {
+ }
+
+ NS_DECL_ISUPPORTS
+
+ RefPtr mSurface;
+ RepeatMode mRepeat;
+ nsCOMPtr mPrincipal;
+ PRPackedBool mForceWriteOnly;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsCanvasPatternAzure, NS_CANVASPATTERNAZURE_PRIVATE_IID)
+
+NS_IMPL_ADDREF(nsCanvasPatternAzure)
+NS_IMPL_RELEASE(nsCanvasPatternAzure)
+
+// XXX
+// DOMCI_DATA(CanvasPattern, nsCanvasPatternAzure)
+
+NS_INTERFACE_MAP_BEGIN(nsCanvasPatternAzure)
+ NS_INTERFACE_MAP_ENTRY(nsCanvasPatternAzure)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasPattern)
+ NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasPattern)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+/**
+ ** nsTextMetricsAzure
+ **/
+#define NS_TEXTMETRICSAZURE_PRIVATE_IID \
+ {0x9793f9e7, 0x9dc1, 0x4e9c, {0x81, 0xc8, 0xfc, 0xa7, 0x14, 0xf4, 0x30, 0x79}}
+class nsTextMetricsAzure : public nsIDOMTextMetrics
+{
+public:
+ nsTextMetricsAzure(float w) : width(w) { }
+
+ virtual ~nsTextMetricsAzure() { }
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_TEXTMETRICSAZURE_PRIVATE_IID)
+
+ NS_IMETHOD GetWidth(float* w) {
+ *w = width;
+ return NS_OK;
+ }
+
+ NS_DECL_ISUPPORTS
+
+private:
+ float width;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsTextMetricsAzure, NS_TEXTMETRICSAZURE_PRIVATE_IID)
+
+NS_IMPL_ADDREF(nsTextMetricsAzure)
+NS_IMPL_RELEASE(nsTextMetricsAzure)
+
+// XXX
+// DOMCI_DATA(TextMetrics, nsTextMetricsAzure)
+
+NS_INTERFACE_MAP_BEGIN(nsTextMetricsAzure)
+ NS_INTERFACE_MAP_ENTRY(nsTextMetricsAzure)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMTextMetrics)
+ NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TextMetrics)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+struct nsCanvasBidiProcessorAzure;
+
+// Cap sigma to avoid overly large temp surfaces.
+static const Float SIGMA_MAX = 100;
+
+/**
+ ** nsCanvasRenderingContext2DAzure
+ **/
+class nsCanvasRenderingContext2DAzure :
+ public nsIDOMCanvasRenderingContext2D,
+ public nsICanvasRenderingContextInternal
+{
+public:
+ nsCanvasRenderingContext2DAzure();
+ virtual ~nsCanvasRenderingContext2DAzure();
+
+ nsresult Redraw();
+
+ // nsICanvasRenderingContextInternal
+ NS_IMETHOD SetCanvasElement(nsHTMLCanvasElement* aParentCanvas);
+ NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height);
+ NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, PRInt32 width, PRInt32 height)
+ { return NS_ERROR_NOT_IMPLEMENTED; }
+
+ NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter);
+ NS_IMETHOD GetInputStream(const char* aMimeType,
+ const PRUnichar* aEncoderOptions,
+ nsIInputStream **aStream);
+ NS_IMETHOD GetThebesSurface(gfxASurface **surface);
+
+ TemporaryRef GetSurfaceSnapshot()
+ { return mTarget ? mTarget->Snapshot() : nsnull; }
+
+ NS_IMETHOD SetIsOpaque(PRBool isOpaque);
+ NS_IMETHOD Reset();
+ already_AddRefed GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+ CanvasLayer *aOldLayer,
+ LayerManager *aManager);
+ void MarkContextClean();
+ NS_IMETHOD SetIsIPC(PRBool isIPC);
+ // this rect is in canvas device space
+ void Redraw(const mgfx::Rect &r);
+ NS_IMETHOD Redraw(const gfxRect &r) { Redraw(ToRect(r)); return NS_OK; }
+
+ // this rect is in mTarget's current user space
+ nsresult RedrawUser(const gfxRect &r);
+
+ // nsISupports interface + CC
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCanvasRenderingContext2DAzure, nsIDOMCanvasRenderingContext2D)
+
+ // nsIDOMCanvasRenderingContext2D interface
+ NS_DECL_NSIDOMCANVASRENDERINGCONTEXT2D
+
+ enum Style {
+ STYLE_STROKE = 0,
+ STYLE_FILL,
+ STYLE_MAX
+ };
+
+protected:
+ nsresult InitializeWithTarget(DrawTarget *surface, PRInt32 width, PRInt32 height);
+
+ /**
+ * The number of living nsCanvasRenderingContexts. When this goes down to
+ * 0, we free the premultiply and unpremultiply tables, if they exist.
+ */
+ static PRUint32 sNumLivingContexts;
+
+ /**
+ * Lookup table used to speed up GetImageData().
+ */
+ static PRUint8 (*sUnpremultiplyTable)[256];
+
+ /**
+ * Lookup table used to speed up PutImageData().
+ */
+ static PRUint8 (*sPremultiplyTable)[256];
+
+ // Some helpers. Doesn't modify a color on failure.
+ nsresult SetStyleFromStringOrInterface(const nsAString& aStr, nsISupports *aInterface, Style aWhichStyle);
+ nsresult GetStyleAsStringOrInterface(nsAString& aStr, nsISupports **aInterface, PRInt32 *aType, Style aWhichStyle);
+
+ void StyleColorToString(const nscolor& aColor, nsAString& aStr);
+
+ /**
+ * Creates the unpremultiply lookup table, if it doesn't exist.
+ */
+ void EnsureUnpremultiplyTable();
+
+ /**
+ * Creates the premultiply lookup table, if it doesn't exist.
+ */
+ void EnsurePremultiplyTable();
+
+ /* This function ensures there is a writable pathbuilder available, this
+ * pathbuilder may be working in user space or in device space or
+ * device space.
+ */
+ void EnsureWritablePath();
+
+ // Ensures a path in UserSpace is available.
+ void EnsureUserSpacePath();
+
+ void TransformWillUpdate();
+
+ // Report the fillRule has changed.
+ void FillRuleChanged();
+
+ /**
+ * Returns the surface format this canvas should be allocated using. Takes
+ * into account mOpaque, platform requirements, etc.
+ */
+ SurfaceFormat GetSurfaceFormat() const;
+
+ nsHTMLCanvasElement *HTMLCanvasElement() {
+ return static_cast(mCanvasElement.get());
+ }
+
+ // Member vars
+ PRInt32 mWidth, mHeight;
+
+ // This is true when the canvas is valid, false otherwise, this occurs when
+ // for some reason initialization of the drawtarget fails. If the canvas
+ // is invalid certain behavior is expected.
+ PRPackedBool mValid;
+ // This is true when the canvas is valid, but of zero size, this requires
+ // specific behavior on some operations.
+ PRPackedBool mZero;
+
+ PRPackedBool mOpaque;
+
+ // This is true when the next time our layer is retrieved we need to
+ // recreate it (i.e. our backing surface changed)
+ PRPackedBool mResetLayer;
+ // This is needed for drawing in drawAsyncXULElement
+ PRPackedBool mIPC;
+
+ // the canvas element we're a context of
+ nsCOMPtr mCanvasElement;
+
+ // If mCanvasElement is not provided, then a docshell is
+ nsCOMPtr mDocShell;
+
+ // our drawing surfaces, contexts, and layers
+ RefPtr mTarget;
+
+ /**
+ * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
+ * Redraw is called, reset to false when Render is called.
+ */
+ PRPackedBool mIsEntireFrameInvalid;
+ /**
+ * When this is set, the first call to Redraw(gfxRect) should set
+ * mIsEntireFrameInvalid since we expect it will be followed by
+ * many more Redraw calls.
+ */
+ PRPackedBool mPredictManyRedrawCalls;
+
+ /**
+ * We also have a device space pathbuilder. The reason for this is as
+ * follows, when a path is being built, but the transform changes, we
+ * can no longer keep a single path in userspace, considering there's
+ * several 'user spaces' now. We therefore transform the current path
+ * into device space, and add all operations to this path in device
+ * space.
+ *
+ * When then finally executing a render, the Azure drawing API expects
+ * the path to be in userspace. We could then set an identity transform
+ * on the DrawTarget and do all drawing in device space. This is
+ * undesirable because it requires transforming patterns, gradients,
+ * clips, etc. into device space and it would not work for stroking.
+ * What we do instead is convert the path back to user space when it is
+ * drawn, and draw it with the current transform. This makes all drawing
+ * occur correctly.
+ *
+ * There's never both a device space path builder and a user space path
+ * builder present at the same time. There is also never a path and a
+ * path builder present at the same time. When writing proceeds on an
+ * existing path the Path is cleared and a new builder is created.
+ *
+ * mPath is always in user-space.
+ */
+ RefPtr mPath;
+ RefPtr mDSPathBuilder;
+ RefPtr mPathBuilder;
+ bool mPathTransformWillUpdate;
+ Matrix mPathToDS;
+
+ /**
+ * Number of times we've invalidated before calling redraw
+ */
+ PRUint32 mInvalidateCount;
+ static const PRUint32 kCanvasMaxInvalidateCount = 100;
+
+ /**
+ * Returns true if a shadow should be drawn along with a
+ * drawing operation.
+ */
+ PRBool NeedToDrawShadow()
+ {
+ const ContextState& state = CurrentState();
+
+ // The spec says we should not draw shadows if the operator is OVER.
+ // If it's over and the alpha value is zero, nothing needs to be drawn.
+ return state.op == OP_OVER && NS_GET_A(state.shadowColor) != 0;
+ }
+
+ /**
+ * Gets the pres shell from either the canvas element or the doc shell
+ */
+ nsIPresShell *GetPresShell() {
+ nsCOMPtr content =
+ do_QueryInterface(static_cast(mCanvasElement));
+ if (content) {
+ nsIDocument* ownerDoc = content->GetOwnerDoc();
+ return ownerDoc ? ownerDoc->GetShell() : nsnull;
+ }
+ if (mDocShell) {
+ nsCOMPtr shell;
+ mDocShell->GetPresShell(getter_AddRefs(shell));
+ return shell.get();
+ }
+ return nsnull;
+ }
+
+ // text
+ enum TextAlign {
+ TEXT_ALIGN_START,
+ TEXT_ALIGN_END,
+ TEXT_ALIGN_LEFT,
+ TEXT_ALIGN_RIGHT,
+ TEXT_ALIGN_CENTER
+ };
+
+ enum TextBaseline {
+ TEXT_BASELINE_TOP,
+ TEXT_BASELINE_HANGING,
+ TEXT_BASELINE_MIDDLE,
+ TEXT_BASELINE_ALPHABETIC,
+ TEXT_BASELINE_IDEOGRAPHIC,
+ TEXT_BASELINE_BOTTOM
+ };
+
+ gfxFontGroup *GetCurrentFontStyle();
+
+ enum TextDrawOperation {
+ TEXT_DRAW_OPERATION_FILL,
+ TEXT_DRAW_OPERATION_STROKE,
+ TEXT_DRAW_OPERATION_MEASURE
+ };
+
+ /*
+ * Implementation of the fillText, strokeText, and measure functions with
+ * the operation abstracted to a flag.
+ */
+ nsresult DrawOrMeasureText(const nsAString& text,
+ float x,
+ float y,
+ float maxWidth,
+ TextDrawOperation op,
+ float* aWidth);
+
+ // state stack handling
+ class ContextState {
+ public:
+ ContextState() : textAlign(TEXT_ALIGN_START),
+ textBaseline(TEXT_BASELINE_ALPHABETIC),
+ lineWidth(1.0f),
+ miterLimit(10.0f),
+ globalAlpha(1.0f),
+ shadowBlur(0.0),
+ op(OP_OVER),
+ fillRule(FILL_WINDING),
+ lineCap(CAP_BUTT),
+ lineJoin(JOIN_MITER_OR_BEVEL),
+ imageSmoothingEnabled(PR_TRUE)
+ { }
+
+ ContextState(const ContextState& other)
+ : fontGroup(other.fontGroup),
+ font(other.font),
+ textAlign(other.textAlign),
+ textBaseline(other.textBaseline),
+ shadowColor(other.shadowColor),
+ transform(other.transform),
+ shadowOffset(other.shadowOffset),
+ lineWidth(other.lineWidth),
+ miterLimit(other.miterLimit),
+ globalAlpha(other.globalAlpha),
+ shadowBlur(other.shadowBlur),
+ op(other.op),
+ fillRule(FILL_WINDING),
+ lineCap(other.lineCap),
+ lineJoin(other.lineJoin),
+ imageSmoothingEnabled(other.imageSmoothingEnabled)
+ {
+ for (int i = 0; i < STYLE_MAX; i++) {
+ colorStyles[i] = other.colorStyles[i];
+ gradientStyles[i] = other.gradientStyles[i];
+ patternStyles[i] = other.patternStyles[i];
+ }
+ }
+
+ void SetColorStyle(Style whichStyle, nscolor color) {
+ colorStyles[whichStyle] = color;
+ gradientStyles[whichStyle] = nsnull;
+ patternStyles[whichStyle] = nsnull;
+ }
+
+ void SetPatternStyle(Style whichStyle, nsCanvasPatternAzure* pat) {
+ gradientStyles[whichStyle] = nsnull;
+ patternStyles[whichStyle] = pat;
+ }
+
+ void SetGradientStyle(Style whichStyle, nsCanvasGradientAzure* grad) {
+ gradientStyles[whichStyle] = grad;
+ patternStyles[whichStyle] = nsnull;
+ }
+
+ /**
+ * returns true iff the given style is a solid color.
+ */
+ PRBool StyleIsColor(Style whichStyle) const
+ {
+ return !(patternStyles[whichStyle] ||
+ gradientStyles[whichStyle]);
+ }
+
+
+ std::vector > clipsPushed;
+
+ nsRefPtr fontGroup;
+ nsRefPtr gradientStyles[STYLE_MAX];
+ nsRefPtr patternStyles[STYLE_MAX];
+
+ nsString font;
+ TextAlign textAlign;
+ TextBaseline textBaseline;
+
+ nscolor colorStyles[STYLE_MAX];
+ nscolor shadowColor;
+
+ Matrix transform;
+ Point shadowOffset;
+ Float lineWidth;
+ Float miterLimit;
+ Float globalAlpha;
+ Float shadowBlur;
+
+ CompositionOp op;
+ FillRule fillRule;
+ CapStyle lineCap;
+ JoinStyle lineJoin;
+
+ PRPackedBool imageSmoothingEnabled;
+ };
+
+ class GeneralPattern
+ {
+ public:
+ GeneralPattern() : mPattern(nsnull) {}
+ ~GeneralPattern()
+ {
+ if (mPattern) {
+ mPattern->~Pattern();
+ }
+ }
+
+ Pattern& ForStyle(nsCanvasRenderingContext2DAzure *aCtx,
+ Style aStyle,
+ DrawTarget *aRT)
+ {
+ // This should only be called once or the mPattern destructor will
+ // not be executed.
+ NS_ASSERTION(!mPattern, "ForStyle() should only be called once on GeneralPattern!");
+
+ const nsCanvasRenderingContext2DAzure::ContextState &state = aCtx->CurrentState();
+
+ if (state.StyleIsColor(aStyle)) {
+ mPattern = new (mColorPattern.addr()) ColorPattern(Color::FromABGR(state.colorStyles[aStyle]));
+ } else if (state.gradientStyles[aStyle] &&
+ state.gradientStyles[aStyle]->GetType() == nsCanvasGradientAzure::LINEAR) {
+ nsCanvasLinearGradientAzure *gradient =
+ static_cast(state.gradientStyles[aStyle].get());
+
+ mPattern = new (mLinearGradientPattern.addr())
+ LinearGradientPattern(gradient->mBegin, gradient->mEnd,
+ gradient->GetGradientStopsForTarget(aRT));
+ } else if (state.gradientStyles[aStyle] &&
+ state.gradientStyles[aStyle]->GetType() == nsCanvasGradientAzure::RADIAL) {
+ nsCanvasRadialGradientAzure *gradient =
+ static_cast(state.gradientStyles[aStyle].get());
+
+ mPattern = new (mRadialGradientPattern.addr())
+ RadialGradientPattern(gradient->mCenter, gradient->mOrigin, gradient->mRadius,
+ gradient->GetGradientStopsForTarget(aRT));
+ } else if (state.patternStyles[aStyle]) {
+ if (aCtx->mCanvasElement) {
+ CanvasUtils::DoDrawImageSecurityCheck(aCtx->HTMLCanvasElement(),
+ state.patternStyles[aStyle]->mPrincipal,
+ state.patternStyles[aStyle]->mForceWriteOnly);
+ }
+
+ ExtendMode mode;
+ if (state.patternStyles[aStyle]->mRepeat == nsCanvasPatternAzure::NOREPEAT) {
+ mode = EXTEND_CLAMP;
+ } else {
+ mode = EXTEND_WRAP;
+ }
+ mPattern = new (mSurfacePattern.addr())
+ SurfacePattern(state.patternStyles[aStyle]->mSurface, mode);
+ }
+
+ return *mPattern;
+ }
+
+ union {
+ AlignedStorage2 mColorPattern;
+ AlignedStorage2 mLinearGradientPattern;
+ AlignedStorage2 mRadialGradientPattern;
+ AlignedStorage2 mSurfacePattern;
+ };
+ Pattern *mPattern;
+ };
+
+ /* This is an RAII based class that can be used as a drawtarget for
+ * operations that need a shadow drawn. It will automatically provide a
+ * temporary target when needed, and if so blend it back with a shadow.
+ */
+ class AdjustedTarget
+ {
+ public:
+ AdjustedTarget(nsCanvasRenderingContext2DAzure *ctx,
+ const mgfx::Rect *aBounds = nsnull)
+ : mCtx(nsnull)
+ {
+ if (!ctx->NeedToDrawShadow()) {
+ mTarget = ctx->mTarget;
+ return;
+ }
+ mCtx = ctx;
+
+ const ContextState &state = mCtx->CurrentState();
+
+ mSigma = state.shadowBlur / 2.0f;
+
+ if (mSigma > SIGMA_MAX) {
+ mSigma = SIGMA_MAX;
+ }
+
+ Matrix transform = mCtx->mTarget->GetTransform();
+ if (!aBounds) {
+ mTempSize = IntSize(ctx->mWidth, ctx->mHeight);
+
+ // We need to enlarge an possibly offset our temporary surface
+ // so that things outside of the canvas may cast shadows.
+ if (state.shadowOffset.x > 0) {
+ mTempSize.width += state.shadowOffset.x;
+ mSurfOffset.x = -state.shadowOffset.x;
+ transform._31 += state.shadowOffset.x;
+ } else {
+ mTempSize.width -= state.shadowOffset.x;
+ }
+ if (state.shadowOffset.y > 0) {
+ mTempSize.height += state.shadowOffset.y;
+ mSurfOffset.y = -state.shadowOffset.y;
+ transform._32 += state.shadowOffset.y;
+ } else {
+ mTempSize.height -= state.shadowOffset.y;
+ }
+
+ if (mSigma > 0) {
+ float blurRadius = mSigma * 3;
+ mSurfOffset.x -= blurRadius;
+ mSurfOffset.y -= blurRadius;
+ mTempSize.width += blurRadius;
+ mTempSize.height += blurRadius;
+ transform._31 += blurRadius;
+ transform._32 += blurRadius;
+ }
+ } // XXX - Implement aBounds path! See bug 666452.
+
+ mTarget =
+ mCtx->mTarget->CreateSimilarDrawTarget(mTempSize,
+ FORMAT_B8G8R8A8);
+
+ mTarget->SetTransform(transform);
+
+ if (!mTarget) {
+ // XXX - Deal with the situation where our temp size is too big to
+ // fit in a texture.
+ mTarget = ctx->mTarget;
+ mCtx = nsnull;
+ }
+ }
+
+ ~AdjustedTarget()
+ {
+ if (!mCtx) {
+ return;
+ }
+
+ RefPtr snapshot = mTarget->Snapshot();
+
+ mCtx->mTarget->DrawSurfaceWithShadow(snapshot, mSurfOffset,
+ Color::FromABGR(mCtx->CurrentState().shadowColor),
+ mCtx->CurrentState().shadowOffset, mSigma);
+ }
+
+ DrawTarget* operator->()
+ {
+ return mTarget;
+ }
+
+ private:
+ RefPtr mTarget;
+ nsCanvasRenderingContext2DAzure *mCtx;
+ Float mSigma;
+ IntSize mTempSize;
+ Point mSurfOffset;
+ };
+
+ nsAutoTArray mStyleStack;
+
+ inline ContextState& CurrentState() {
+ return mStyleStack[mStyleStack.Length() - 1];
+ }
+
+ // other helpers
+ void GetAppUnitsValues(PRUint32 *perDevPixel, PRUint32 *perCSSPixel) {
+ // If we don't have a canvas element, we just return something generic.
+ PRUint32 devPixel = 60;
+ PRUint32 cssPixel = 60;
+
+ nsIPresShell *ps = GetPresShell();
+ nsPresContext *pc;
+
+ if (!ps) goto FINISH;
+ pc = ps->GetPresContext();
+ if (!pc) goto FINISH;
+ devPixel = pc->AppUnitsPerDevPixel();
+ cssPixel = pc->AppUnitsPerCSSPixel();
+
+ FINISH:
+ if (perDevPixel)
+ *perDevPixel = devPixel;
+ if (perCSSPixel)
+ *perCSSPixel = cssPixel;
+ }
+
+ friend struct nsCanvasBidiProcessorAzure;
+};
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCanvasRenderingContext2DAzure)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCanvasRenderingContext2DAzure)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsCanvasRenderingContext2DAzure)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCanvasRenderingContext2DAzure)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCanvasElement)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCanvasRenderingContext2DAzure)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCanvasElement)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+// XXX
+// DOMCI_DATA(CanvasRenderingContext2D, nsCanvasRenderingContext2DAzure)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCanvasRenderingContext2DAzure)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMCanvasRenderingContext2D)
+ NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMCanvasRenderingContext2D)
+ NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasRenderingContext2D)
+NS_INTERFACE_MAP_END
+
+/**
+ ** CanvasRenderingContext2D impl
+ **/
+
+
+// Initialize our static variables.
+PRUint32 nsCanvasRenderingContext2DAzure::sNumLivingContexts = 0;
+PRUint8 (*nsCanvasRenderingContext2DAzure::sUnpremultiplyTable)[256] = nsnull;
+PRUint8 (*nsCanvasRenderingContext2DAzure::sPremultiplyTable)[256] = nsnull;
+
+nsresult
+NS_NewCanvasRenderingContext2DAzure(nsIDOMCanvasRenderingContext2D** aResult)
+{
+#ifndef XP_WIN
+ return NS_ERROR_NOT_AVAILABLE;
+#else
+
+ if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() !=
+ gfxWindowsPlatform::RENDER_DIRECT2D ||
+ !gfxWindowsPlatform::GetPlatform()->DWriteEnabled()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsRefPtr ctx = new nsCanvasRenderingContext2DAzure();
+ if (!ctx)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ *aResult = ctx.forget().get();
+ return NS_OK;
+#endif
+}
+
+nsCanvasRenderingContext2DAzure::nsCanvasRenderingContext2DAzure()
+ : mValid(PR_FALSE), mZero(PR_FALSE), mOpaque(PR_FALSE), mResetLayer(PR_TRUE)
+ , mIPC(PR_FALSE)
+ , mCanvasElement(nsnull)
+ , mIsEntireFrameInvalid(PR_FALSE)
+ , mPredictManyRedrawCalls(PR_FALSE), mPathTransformWillUpdate(false)
+ , mInvalidateCount(0)
+{
+ sNumLivingContexts++;
+}
+
+nsCanvasRenderingContext2DAzure::~nsCanvasRenderingContext2DAzure()
+{
+ Reset();
+ sNumLivingContexts--;
+ if (!sNumLivingContexts) {
+ delete[] sUnpremultiplyTable;
+ delete[] sPremultiplyTable;
+ sUnpremultiplyTable = nsnull;
+ sPremultiplyTable = nsnull;
+ }
+}
+
+nsresult
+nsCanvasRenderingContext2DAzure::Reset()
+{
+ if (mCanvasElement) {
+ HTMLCanvasElement()->InvalidateCanvas();
+ }
+
+ // only do this for non-docshell created contexts,
+ // since those are the ones that we created a surface for
+ if (mValid && !mDocShell) {
+ gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
+ }
+
+ mTarget = nsnull;
+ mValid = PR_FALSE;
+ mIsEntireFrameInvalid = PR_FALSE;
+ mPredictManyRedrawCalls = PR_FALSE;
+ return NS_OK;
+}
+
+nsresult
+nsCanvasRenderingContext2DAzure::SetStyleFromStringOrInterface(const nsAString& aStr,
+ nsISupports *aInterface,
+ Style aWhichStyle)
+{
+ nsresult rv;
+ nscolor color;
+
+ if (!aStr.IsVoid()) {
+ nsIDocument* document = mCanvasElement ?
+ HTMLCanvasElement()->GetOwnerDoc() : nsnull;
+
+ // Pass the CSS Loader object to the parser, to allow parser error
+ // reports to include the outer window ID.
+ nsCSSParser parser(document ? document->CSSLoader() : nsnull);
+ rv = parser.ParseColorString(aStr, nsnull, 0, &color);
+ if (NS_FAILED(rv)) {
+ // Error reporting happens inside the CSS parser
+ return NS_OK;
+ }
+
+ CurrentState().SetColorStyle(aWhichStyle, color);
+ return NS_OK;
+ }
+
+ if (aInterface) {
+ nsCOMPtr grad(do_QueryInterface(aInterface));
+ if (grad) {
+ CurrentState().SetGradientStyle(aWhichStyle, grad);
+ return NS_OK;
+ }
+
+ nsCOMPtr pattern(do_QueryInterface(aInterface));
+ if (pattern) {
+ CurrentState().SetPatternStyle(aWhichStyle, pattern);
+ return NS_OK;
+ }
+ }
+
+ nsContentUtils::ReportToConsole(
+ nsContentUtils::eDOM_PROPERTIES,
+ "UnexpectedCanvasVariantStyle",
+ nsnull, 0,
+ nsnull,
+ EmptyString(), 0, 0,
+ nsIScriptError::warningFlag,
+ "Canvas",
+ mCanvasElement ? HTMLCanvasElement()->GetOwnerDoc() : nsnull);
+
+ return NS_OK;
+}
+
+nsresult
+nsCanvasRenderingContext2DAzure::GetStyleAsStringOrInterface(nsAString& aStr,
+ nsISupports **aInterface,
+ PRInt32 *aType,
+ Style aWhichStyle)
+{
+ const ContextState &state = CurrentState();
+
+ if (state.patternStyles[aWhichStyle]) {
+ aStr.SetIsVoid(PR_TRUE);
+ NS_ADDREF(*aInterface = state.patternStyles[aWhichStyle]);
+ *aType = CMG_STYLE_PATTERN;
+ } else if (state.gradientStyles[aWhichStyle]) {
+ aStr.SetIsVoid(PR_TRUE);
+ NS_ADDREF(*aInterface = state.gradientStyles[aWhichStyle]);
+ *aType = CMG_STYLE_GRADIENT;
+ } else {
+ StyleColorToString(state.colorStyles[aWhichStyle], aStr);
+ *aInterface = nsnull;
+ *aType = CMG_STYLE_STRING;
+ }
+
+ return NS_OK;
+}
+
+void
+nsCanvasRenderingContext2DAzure::StyleColorToString(const nscolor& aColor, nsAString& aStr)
+{
+ // We can't reuse the normal CSS color stringification code,
+ // because the spec calls for a different algorithm for canvas.
+ if (NS_GET_A(aColor) == 255) {
+ CopyUTF8toUTF16(nsPrintfCString(100, "#%02x%02x%02x",
+ NS_GET_R(aColor),
+ NS_GET_G(aColor),
+ NS_GET_B(aColor)),
+ aStr);
+ } else {
+ CopyUTF8toUTF16(nsPrintfCString(100, "rgba(%d, %d, %d, ",
+ NS_GET_R(aColor),
+ NS_GET_G(aColor),
+ NS_GET_B(aColor)),
+ aStr);
+ aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
+ aStr.Append(')');
+ }
+}
+
+nsresult
+nsCanvasRenderingContext2DAzure::Redraw()
+{
+ if (mIsEntireFrameInvalid) {
+ return NS_OK;
+ }
+
+ mIsEntireFrameInvalid = PR_TRUE;
+
+ if (!mCanvasElement) {
+ NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
+ return NS_OK;
+ }
+
+ nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
+
+ HTMLCanvasElement()->InvalidateCanvasContent(nsnull);
+
+ return NS_OK;
+}
+
+void
+nsCanvasRenderingContext2DAzure::Redraw(const mgfx::Rect &r)
+{
+ ++mInvalidateCount;
+
+ if (mIsEntireFrameInvalid) {
+ return;
+ }
+
+ if (mPredictManyRedrawCalls ||
+ mInvalidateCount > kCanvasMaxInvalidateCount) {
+ Redraw();
+ return;
+ }
+
+ if (!mCanvasElement) {
+ NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
+ return;
+ }
+
+ nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
+
+ gfxRect tmpR = GFXRect(r);
+ HTMLCanvasElement()->InvalidateCanvasContent(&tmpR);
+
+ return;
+}
+
+nsresult
+nsCanvasRenderingContext2DAzure::RedrawUser(const gfxRect& r)
+{
+ if (mIsEntireFrameInvalid) {
+ ++mInvalidateCount;
+ return NS_OK;
+ }
+
+ mgfx::Rect newr =
+ mTarget->GetTransform().TransformBounds(ToRect(r));
+ Redraw(newr);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetDimensions(PRInt32 width, PRInt32 height)
+{
+ RefPtr target;
+
+ // Zero sized surfaces cause issues, so just go with 1x1.
+ if (height == 0 || width == 0) {
+ mZero = PR_TRUE;
+ height = 1;
+ width = 1;
+ }
+
+ // Check that the dimensions are sane
+ IntSize size(width, height);
+ if (size.width <= 0xFFFF && size.height <= 0xFFFF &&
+ size.width >= 0 && size.height >= 0) {
+ SurfaceFormat format = GetSurfaceFormat();
+ nsCOMPtr content =
+ do_QueryInterface(static_cast(mCanvasElement));
+ nsIDocument* ownerDoc = nsnull;
+ if (content) {
+ ownerDoc = content->GetOwnerDoc();
+ }
+
+ nsRefPtr layerManager = nsnull;
+
+ if (ownerDoc) {
+ layerManager =
+ nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);
+ }
+
+ if (layerManager) {
+ target = layerManager->CreateDrawTarget(size, format);
+ } else {
+ target = Factory::CreateDrawTarget(BACKEND_DIRECT2D, size, format);
+ }
+ }
+
+ if (target) {
+ if (gCanvasAzureMemoryReporter == nsnull) {
+ gCanvasAzureMemoryReporter = new NS_MEMORY_REPORTER_NAME(CanvasAzureMemory);
+ NS_RegisterMemoryReporter(gCanvasAzureMemoryReporter);
+ }
+
+ gCanvasAzureMemoryUsed += width * height * 4;
+ JS_updateMallocCounter(nsContentUtils::GetCurrentJSContext(), width * height * 4);
+ }
+
+ return InitializeWithTarget(target, width, height);
+}
+
+nsresult
+nsCanvasRenderingContext2DAzure::InitializeWithTarget(DrawTarget *target, PRInt32 width, PRInt32 height)
+{
+ Reset();
+
+ NS_ASSERTION(mCanvasElement, "Must have a canvas element!");
+ mDocShell = nsnull;
+
+ mWidth = width;
+ mHeight = height;
+
+ mTarget = target;
+
+ mResetLayer = PR_TRUE;
+
+ /* Create dummy surfaces here - target can be null when a canvas was created
+ * that is too large to support.
+ */
+ if (!target)
+ {
+ mTarget = Factory::CreateDrawTarget(BACKEND_DIRECT2D, IntSize(1, 1), FORMAT_B8G8R8A8);
+ } else {
+ mValid = PR_TRUE;
+ }
+
+ // set up the initial canvas defaults
+ mStyleStack.Clear();
+ mPathBuilder = nsnull;
+ mPath = nsnull;
+ mDSPathBuilder = nsnull;
+
+ ContextState *state = mStyleStack.AppendElement();
+ state->globalAlpha = 1.0;
+
+ state->colorStyles[STYLE_FILL] = NS_RGB(0,0,0);
+ state->colorStyles[STYLE_STROKE] = NS_RGB(0,0,0);
+ state->shadowColor = NS_RGBA(0,0,0,0);
+
+ mTarget->ClearRect(mgfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
+
+ // always force a redraw, because if the surface dimensions were reset
+ // then the surface became cleared, and we need to redraw everything.
+ Redraw();
+
+ return mValid ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetIsOpaque(PRBool isOpaque)
+{
+ if (isOpaque == mOpaque)
+ return NS_OK;
+
+ mOpaque = isOpaque;
+
+ if (mValid) {
+ /* If we've already been created, let SetDimensions take care of
+ * recreating our surface
+ */
+ return SetDimensions(mWidth, mHeight);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetIsIPC(PRBool isIPC)
+{
+ if (isIPC == mIPC)
+ return NS_OK;
+
+ mIPC = isIPC;
+
+ if (mValid) {
+ /* If we've already been created, let SetDimensions take care of
+ * recreating our surface
+ */
+ return SetDimensions(mWidth, mHeight);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter)
+{
+ nsresult rv = NS_OK;
+
+ if (!mValid || !mTarget) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsRefPtr surface;
+
+ if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsRefPtr pat = new gfxPattern(surface);
+
+ pat->SetFilter(aFilter);
+ pat->SetExtend(gfxPattern::EXTEND_PAD);
+
+ gfxContext::GraphicsOperator op = ctx->CurrentOperator();
+ if (mOpaque)
+ ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+
+ // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
+ // pixel alignment for this stuff!
+ ctx->NewPath();
+ ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
+ ctx->Fill();
+
+ if (mOpaque)
+ ctx->SetOperator(op);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetInputStream(const char *aMimeType,
+ const PRUnichar *aEncoderOptions,
+ nsIInputStream **aStream)
+{
+ if (!mValid || !mTarget) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsRefPtr surface;
+
+ if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
+ nsAutoArrayPtr conid(new (std::nothrow) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
+
+ if (!conid) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ strcpy(conid, encoderPrefix);
+ strcat(conid, aMimeType);
+
+ nsCOMPtr encoder = do_CreateInstance(conid);
+ if (!encoder) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoArrayPtr imageBuffer(new (std::nothrow) PRUint8[mWidth * mHeight * 4]);
+ if (!imageBuffer) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsRefPtr imgsurf =
+ new gfxImageSurface(imageBuffer.get(),
+ gfxIntSize(mWidth, mHeight),
+ mWidth * 4,
+ gfxASurface::ImageFormatARGB32);
+
+ if (!imgsurf || imgsurf->CairoStatus()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsRefPtr ctx = new gfxContext(imgsurf);
+
+ if (!ctx || ctx->HasError()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+ ctx->SetSource(surface, gfxPoint(0, 0));
+ ctx->Paint();
+
+ rv = encoder->InitFromData(imageBuffer.get(),
+ mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
+ imgIEncoder::INPUT_FORMAT_HOSTARGB,
+ nsDependentString(aEncoderOptions));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CallQueryInterface(encoder, aStream);
+}
+
+SurfaceFormat
+nsCanvasRenderingContext2DAzure::GetSurfaceFormat() const
+{
+ return mOpaque ? FORMAT_B8G8R8X8 : FORMAT_B8G8R8A8;
+}
+
+//
+// nsCanvasRenderingContext2DAzure impl
+//
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetCanvasElement(nsHTMLCanvasElement* aCanvasElement)
+{
+ mCanvasElement = aCanvasElement;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetCanvas(nsIDOMHTMLCanvasElement **canvas)
+{
+ NS_IF_ADDREF(*canvas = mCanvasElement);
+
+ return NS_OK;
+}
+
+//
+// state
+//
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::Save()
+{
+ mStyleStack[mStyleStack.Length() - 1].transform = mTarget->GetTransform();
+ mStyleStack.SetCapacity(mStyleStack.Length() + 1);
+ mStyleStack.AppendElement(CurrentState());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::Restore()
+{
+ if (mStyleStack.Length() - 1 == 0)
+ return NS_OK;
+
+ for (PRUint32 i = 0; i < CurrentState().clipsPushed.size(); i++) {
+ mTarget->PopClip();
+ }
+
+ mStyleStack.RemoveElementAt(mStyleStack.Length() - 1);
+
+ TransformWillUpdate();
+
+ mTarget->SetTransform(CurrentState().transform);
+
+ return NS_OK;
+}
+
+//
+// transformations
+//
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::Scale(float x, float y)
+{
+ if (!FloatValidate(x,y))
+ return NS_OK;
+
+ TransformWillUpdate();
+
+ Matrix newMatrix = mTarget->GetTransform();
+ mTarget->SetTransform(newMatrix.Scale(x, y));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::Rotate(float angle)
+{
+ if (!FloatValidate(angle))
+ return NS_OK;
+
+ TransformWillUpdate();
+
+ Matrix rotation = Matrix::Rotation(angle);
+ mTarget->SetTransform(rotation * mTarget->GetTransform());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::Translate(float x, float y)
+{
+ if (!FloatValidate(x,y)) {
+ return NS_OK;
+ }
+
+ TransformWillUpdate();
+
+ Matrix newMatrix = mTarget->GetTransform();
+ mTarget->SetTransform(newMatrix.Translate(x, y));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::Transform(float m11, float m12, float m21, float m22, float dx, float dy)
+{
+ if (!FloatValidate(m11,m12,m21,m22,dx,dy)) {
+ return NS_OK;
+ }
+
+ TransformWillUpdate();
+
+ Matrix matrix(m11, m12, m21, m22, dx, dy);
+ mTarget->SetTransform(matrix * mTarget->GetTransform());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetTransform(float m11, float m12, float m21, float m22, float dx, float dy)
+{
+ if (!FloatValidate(m11,m12,m21,m22,dx,dy)) {
+ return NS_OK;
+ }
+
+ TransformWillUpdate();
+
+ Matrix matrix(m11, m12, m21, m22, dx, dy);
+ mTarget->SetTransform(matrix);
+
+ return NS_OK;
+}
+
+//
+// colors
+//
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetGlobalAlpha(float aGlobalAlpha)
+{
+ if (!FloatValidate(aGlobalAlpha) || aGlobalAlpha < 0.0 || aGlobalAlpha > 1.0) {
+ return NS_OK;
+ }
+
+ CurrentState().globalAlpha = aGlobalAlpha;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetGlobalAlpha(float *aGlobalAlpha)
+{
+ *aGlobalAlpha = CurrentState().globalAlpha;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetStrokeStyle(nsIVariant *aValue)
+{
+ if (!aValue)
+ return NS_ERROR_FAILURE;
+
+ nsString str;
+
+ nsresult rv;
+ PRUint16 vtype;
+ rv = aValue->GetDataType(&vtype);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (vtype == nsIDataType::VTYPE_INTERFACE ||
+ vtype == nsIDataType::VTYPE_INTERFACE_IS)
+ {
+ nsIID *iid;
+ nsCOMPtr sup;
+ rv = aValue->GetAsInterface(&iid, getter_AddRefs(sup));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (iid) {
+ NS_Free(iid);
+ }
+
+ str.SetIsVoid(PR_TRUE);
+ return SetStrokeStyle_multi(str, sup);
+ }
+
+ rv = aValue->GetAsAString(str);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return SetStrokeStyle_multi(str, nsnull);
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetStrokeStyle(nsIVariant **aResult)
+{
+ nsCOMPtr wv = do_CreateInstance(NS_VARIANT_CONTRACTID);
+
+ nsCOMPtr sup;
+ nsString str;
+ PRInt32 t;
+ nsresult rv = GetStrokeStyle_multi(str, getter_AddRefs(sup), &t);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (t == CMG_STYLE_STRING) {
+ rv = wv->SetAsAString(str);
+ } else if (t == CMG_STYLE_PATTERN) {
+ rv = wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasPattern),
+ sup);
+ } else if (t == CMG_STYLE_GRADIENT) {
+ rv = wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasGradient),
+ sup);
+ } else {
+ NS_ERROR("Unknown type from GetStroke/FillStyle_multi!");
+ return NS_ERROR_FAILURE;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*aResult = wv.get());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetFillStyle(nsIVariant *aValue)
+{
+ if (!aValue) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsString str;
+ nsresult rv;
+ PRUint16 vtype;
+ rv = aValue->GetDataType(&vtype);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (vtype == nsIDataType::VTYPE_INTERFACE ||
+ vtype == nsIDataType::VTYPE_INTERFACE_IS)
+ {
+ nsIID *iid;
+ nsCOMPtr sup;
+ rv = aValue->GetAsInterface(&iid, getter_AddRefs(sup));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ str.SetIsVoid(PR_TRUE);
+ return SetFillStyle_multi(str, sup);
+ }
+
+ rv = aValue->GetAsAString(str);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return SetFillStyle_multi(str, nsnull);
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetFillStyle(nsIVariant **aResult)
+{
+ nsCOMPtr wv = do_CreateInstance(NS_VARIANT_CONTRACTID);
+
+ nsCOMPtr sup;
+ nsString str;
+ PRInt32 t;
+ nsresult rv = GetFillStyle_multi(str, getter_AddRefs(sup), &t);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (t == CMG_STYLE_STRING) {
+ rv = wv->SetAsAString(str);
+ } else if (t == CMG_STYLE_PATTERN) {
+ rv = wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasPattern),
+ sup);
+ } else if (t == CMG_STYLE_GRADIENT) {
+ rv = wv->SetAsInterface(NS_GET_IID(nsIDOMCanvasGradient),
+ sup);
+ } else {
+ NS_ERROR("Unknown type from GetStroke/FillStyle_multi!");
+ return NS_ERROR_FAILURE;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*aResult = wv.get());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetMozFillRule(const nsAString& aString)
+{
+ FillRule rule;
+
+ if (aString.EqualsLiteral("evenodd"))
+ rule = FILL_EVEN_ODD;
+ else if (aString.EqualsLiteral("nonzero"))
+ rule = FILL_WINDING;
+ else
+ return NS_OK;
+
+ CurrentState().fillRule = rule;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetMozFillRule(nsAString& aString)
+{
+ switch (CurrentState().fillRule) {
+ case FILL_WINDING:
+ aString.AssignLiteral("nonzero"); break;
+ case FILL_EVEN_ODD:
+ aString.AssignLiteral("evenodd"); break;
+ default:
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetStrokeStyle_multi(const nsAString& aStr, nsISupports *aInterface)
+{
+ return SetStyleFromStringOrInterface(aStr, aInterface, STYLE_STROKE);
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetStrokeStyle_multi(nsAString& aStr, nsISupports **aInterface, PRInt32 *aType)
+{
+ return GetStyleAsStringOrInterface(aStr, aInterface, aType, STYLE_STROKE);
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetFillStyle_multi(const nsAString& aStr, nsISupports *aInterface)
+{
+ return SetStyleFromStringOrInterface(aStr, aInterface, STYLE_FILL);
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetFillStyle_multi(nsAString& aStr, nsISupports **aInterface, PRInt32 *aType)
+{
+ return GetStyleAsStringOrInterface(aStr, aInterface, aType, STYLE_FILL);
+}
+
+//
+// gradients and patterns
+//
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::CreateLinearGradient(float x0, float y0, float x1, float y1,
+ nsIDOMCanvasGradient **_retval)
+{
+ if (!FloatValidate(x0,y0,x1,y1)) {
+ return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+ }
+
+ nsRefPtr grad =
+ new nsCanvasLinearGradientAzure(Point(x0, y0), Point(x1, y1));
+
+ *_retval = grad.forget().get();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::CreateRadialGradient(float x0, float y0, float r0,
+ float x1, float y1, float r1,
+ nsIDOMCanvasGradient **_retval)
+{
+ if (!FloatValidate(x0,y0,r0,x1,y1,r1)) {
+ return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+ }
+
+ if (r0 < 0.0 || r1 < 0.0) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+
+ nsRefPtr grad =
+ new nsCanvasRadialGradientAzure(Point(x0, y0), r0, Point(x1, y1), r1);
+
+ *_retval = grad.forget().get();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::CreatePattern(nsIDOMHTMLElement *image,
+ const nsAString& repeat,
+ nsIDOMCanvasPattern **_retval)
+{
+ if (!image) {
+ return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
+ }
+
+ nsCanvasPatternAzure::RepeatMode repeatMode =
+ nsCanvasPatternAzure::NOREPEAT;
+
+ if (repeat.IsEmpty() || repeat.EqualsLiteral("repeat")) {
+ repeatMode = nsCanvasPatternAzure::REPEAT;
+ } else if (repeat.EqualsLiteral("repeat-x")) {
+ repeatMode = nsCanvasPatternAzure::REPEATX;
+ } else if (repeat.EqualsLiteral("repeat-y")) {
+ repeatMode = nsCanvasPatternAzure::REPEATY;
+ } else if (repeat.EqualsLiteral("no-repeat")) {
+ repeatMode = nsCanvasPatternAzure::NOREPEAT;
+ } else {
+ return NS_ERROR_DOM_SYNTAX_ERR;
+ }
+
+ nsCOMPtr content = do_QueryInterface(image);
+ nsHTMLCanvasElement* canvas = nsHTMLCanvasElement::FromContent(content);
+
+ if (canvas) {
+ nsIntSize size = canvas->GetSize();
+ if (size.width == 0 || size.height == 0) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+ }
+
+ // Special case for Canvas, which could be an Azure canvas!
+ nsCOMPtr node = do_QueryInterface(image);
+ if (canvas && node) {
+ if (canvas->CountContexts() == 1) {
+ nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
+
+ // This might not be an Azure canvas!
+ if (srcCanvas) {
+ RefPtr srcSurf = srcCanvas->GetSurfaceSnapshot();
+
+ nsRefPtr pat =
+ new nsCanvasPatternAzure(srcSurf, repeatMode, node->NodePrincipal(), canvas->IsWriteOnly());
+
+ *_retval = pat.forget().get();
+ return NS_OK;
+ }
+ }
+ }
+
+ // The canvas spec says that createPattern should use the first frame
+ // of animated images
+ nsLayoutUtils::SurfaceFromElementResult res =
+ nsLayoutUtils::SurfaceFromElement(image, nsLayoutUtils::SFE_WANT_FIRST_FRAME |
+ nsLayoutUtils::SFE_WANT_NEW_SURFACE);
+
+ if (!res.mSurface) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // Ignore nsnull cairo surfaces! See bug 666312.
+ if (!res.mSurface->CairoSurface()) {
+ return NS_OK;
+ }
+
+ RefPtr srcSurf =
+ gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, res.mSurface);
+
+ nsRefPtr pat =
+ new nsCanvasPatternAzure(srcSurf, repeatMode, res.mPrincipal, res.mIsWriteOnly);
+
+ *_retval = pat.forget().get();
+ return NS_OK;
+}
+
+//
+// shadows
+//
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetShadowOffsetX(float x)
+{
+ if (!FloatValidate(x)) {
+ return NS_OK;
+ }
+
+ CurrentState().shadowOffset.x = x;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetShadowOffsetX(float *x)
+{
+ *x = static_cast(CurrentState().shadowOffset.x);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetShadowOffsetY(float y)
+{
+ if (!FloatValidate(y)) {
+ return NS_OK;
+ }
+
+ CurrentState().shadowOffset.y = y;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetShadowOffsetY(float *y)
+{
+ *y = static_cast(CurrentState().shadowOffset.y);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetShadowBlur(float blur)
+{
+ if (!FloatValidate(blur) || blur < 0.0) {
+ return NS_OK;
+ }
+
+ CurrentState().shadowBlur = blur;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetShadowBlur(float *blur)
+{
+ *blur = CurrentState().shadowBlur;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetShadowColor(const nsAString& colorstr)
+{
+ nsIDocument* document = mCanvasElement ?
+ HTMLCanvasElement()->GetOwnerDoc() : nsnull;
+
+ // Pass the CSS Loader object to the parser, to allow parser error reports
+ // to include the outer window ID.
+ nsCSSParser parser(document ? document->CSSLoader() : nsnull);
+ nscolor color;
+ nsresult rv = parser.ParseColorString(colorstr, nsnull, 0, &color);
+ if (NS_FAILED(rv)) {
+ // Error reporting happens inside the CSS parser
+ return NS_OK;
+ }
+ CurrentState().shadowColor = color;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetShadowColor(nsAString& color)
+{
+ StyleColorToString(CurrentState().shadowColor, color);
+
+ return NS_OK;
+}
+
+//
+// rects
+//
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::ClearRect(float x, float y, float w, float h)
+{
+ if (!FloatValidate(x,y,w,h)) {
+ return NS_OK;
+ }
+
+ mTarget->ClearRect(mgfx::Rect(x, y, w, h));
+
+ return RedrawUser(gfxRect(x, y, w, h));
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::FillRect(float x, float y, float w, float h)
+{
+ if (!FloatValidate(x,y,w,h)) {
+ return NS_OK;
+ }
+
+ bool doDrawShadow = NeedToDrawShadow();
+
+ const ContextState &state = CurrentState();
+
+ if (state.patternStyles[STYLE_FILL]) {
+ nsCanvasPatternAzure::RepeatMode repeat =
+ state.patternStyles[STYLE_FILL]->mRepeat;
+ // In the FillRect case repeat modes are easy to deal with.
+ bool limitx = repeat == nsCanvasPatternAzure::NOREPEAT || repeat == nsCanvasPatternAzure::REPEATY;
+ bool limity = repeat == nsCanvasPatternAzure::NOREPEAT || repeat == nsCanvasPatternAzure::REPEATX;
+
+ IntSize patternSize =
+ state.patternStyles[STYLE_FILL]->mSurface->GetSize();
+
+ // We always need to execute painting for non-over operators, even if
+ // we end up with w/h = 0.
+ if (limitx) {
+ if (x < 0) {
+ w += x;
+ if (w < 0) {
+ w = 0;
+ }
+
+ x = 0;
+ }
+ if (x + w > patternSize.width) {
+ w = patternSize.width - x;
+ if (w < 0) {
+ w = 0;
+ }
+ }
+ }
+ if (limity) {
+ if (y < 0) {
+ h += y;
+ if (h < 0) {
+ h = 0;
+ }
+
+ y = 0;
+ }
+ if (y + h > patternSize.height) {
+ h = patternSize.height - y;
+ if (h < 0) {
+ h = 0;
+ }
+ }
+ }
+ }
+
+ AdjustedTarget(this)->FillRect(mgfx::Rect(x, y, w, h),
+ GeneralPattern().ForStyle(this, STYLE_FILL, mTarget),
+ DrawOptions(state.globalAlpha, state.op));
+
+ return RedrawUser(gfxRect(x, y, w, h));
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::StrokeRect(float x, float y, float w, float h)
+{
+ if (!FloatValidate(x,y,w,h)) {
+ return NS_OK;
+ }
+
+ const ContextState &state = CurrentState();
+
+ if (!w && !h) {
+ return NS_OK;
+ } else if (!h) {
+ CapStyle cap = CAP_BUTT;
+ if (state.lineJoin == JOIN_ROUND) {
+ cap = CAP_ROUND;
+ }
+ AdjustedTarget(this)->
+ StrokeLine(Point(x, y), Point(x + w, y),
+ GeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
+ StrokeOptions(state.lineWidth, state.lineJoin,
+ cap, state.miterLimit),
+ DrawOptions(state.globalAlpha, state.op));
+ return NS_OK;
+ } else if (!w) {
+ CapStyle cap = CAP_BUTT;
+ if (state.lineJoin == JOIN_ROUND) {
+ cap = CAP_ROUND;
+ }
+ AdjustedTarget(this)->
+ StrokeLine(Point(x, y), Point(x, y + h),
+ GeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
+ StrokeOptions(state.lineWidth, state.lineJoin,
+ cap, state.miterLimit),
+ DrawOptions(state.globalAlpha, state.op));
+ return NS_OK;
+ }
+
+ AdjustedTarget(this)->
+ StrokeRect(mgfx::Rect(x, y, w, h),
+ GeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
+ StrokeOptions(state.lineWidth, state.lineJoin,
+ state.lineCap, state.miterLimit),
+ DrawOptions(state.globalAlpha, state.op));
+
+ return Redraw();
+}
+
+//
+// path bits
+//
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::BeginPath()
+{
+ mPath = nsnull;
+ mPathBuilder = nsnull;
+ mDSPathBuilder = nsnull;
+ mPathTransformWillUpdate = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::ClosePath()
+{
+ EnsureWritablePath();
+
+ if (mPathBuilder) {
+ mPathBuilder->Close();
+ } else {
+ mDSPathBuilder->Close();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::Fill()
+{
+ EnsureUserSpacePath();
+
+ if (!mPath) {
+ return NS_OK;
+ }
+
+ AdjustedTarget(this)->
+ Fill(mPath, GeneralPattern().ForStyle(this, STYLE_FILL, mTarget),
+ DrawOptions(CurrentState().globalAlpha, CurrentState().op));
+
+ return Redraw();
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::Stroke()
+{
+ EnsureUserSpacePath();
+
+ if (!mPath) {
+ return NS_OK;
+ }
+
+ const ContextState &state = CurrentState();
+
+ AdjustedTarget(this)->
+ Stroke(mPath, GeneralPattern().ForStyle(this, STYLE_STROKE, mTarget),
+ StrokeOptions(state.lineWidth, state.lineJoin,
+ state.lineCap, state.miterLimit),
+ DrawOptions(state.globalAlpha, state.op));
+
+ return Redraw();
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::Clip()
+{
+ EnsureUserSpacePath();
+
+ if (!mPath) {
+ return NS_OK;
+ }
+
+ mTarget->PushClip(mPath);
+ CurrentState().clipsPushed.push_back(mPath);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::MoveTo(float x, float y)
+{
+ if (!FloatValidate(x,y))
+ return NS_OK;
+
+ EnsureWritablePath();
+
+ if (mPathBuilder) {
+ mPathBuilder->MoveTo(Point(x, y));
+ } else {
+ mDSPathBuilder->MoveTo(mTarget->GetTransform() * Point(x, y));
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::LineTo(float x, float y)
+{
+ if (!FloatValidate(x,y))
+ return NS_OK;
+
+ EnsureWritablePath();
+
+ if (mPathBuilder) {
+ mPathBuilder->LineTo(Point(x, y));
+ } else {
+ mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x, y));
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::QuadraticCurveTo(float cpx, float cpy, float x, float y)
+{
+ if (!FloatValidate(cpx, cpy, x, y)) {
+ return NS_OK;
+ }
+
+ EnsureWritablePath();
+
+ if (mPathBuilder) {
+ mPathBuilder->QuadraticBezierTo(Point(cpx, cpy), Point(x, y));
+ } else {
+ Matrix transform = mTarget->GetTransform();
+ mDSPathBuilder->QuadraticBezierTo(transform * Point(cpx, cpy), transform * Point(cpx, cpy));
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::BezierCurveTo(float cp1x, float cp1y,
+ float cp2x, float cp2y,
+ float x, float y)
+{
+ if (!FloatValidate(cp1x, cp1y, cp2x, cp2y, x, y)) {
+ return NS_OK;
+ }
+
+ EnsureWritablePath();
+
+ if (mPathBuilder) {
+ mPathBuilder->BezierTo(Point(cp1x, cp1y), Point(cp2x, cp2y), Point(x, y));
+ } else {
+ Matrix transform = mTarget->GetTransform();
+ mDSPathBuilder->BezierTo(transform * Point(cp1x, cp1y),
+ transform * Point(cp2x, cp2y),
+ transform * Point(x, y));
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::ArcTo(float x1, float y1, float x2, float y2, float radius)
+{
+ if (!FloatValidate(x1, y1, x2, y2, radius)) {
+ return NS_OK;
+ }
+
+ if (radius < 0) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+
+ EnsureWritablePath();
+
+ // Current point in user space!
+ Point p0;
+ if (mPathBuilder) {
+ p0 = mPathBuilder->CurrentPoint();
+ } else {
+ Matrix invTransform = mTarget->GetTransform();
+ if (!invTransform.Invert()) {
+ return NS_OK;
+ }
+
+ p0 = invTransform * mDSPathBuilder->CurrentPoint();
+ }
+
+ Point p1(x1, y1);
+ Point p2(x2, y2);
+
+ // Execute these calculations in double precision to avoid cumulative
+ // rounding errors.
+ double dir, a2, b2, c2, cosx, sinx, d, anx, any,
+ bnx, bny, x3, y3, x4, y4, cx, cy, angle0, angle1;
+ bool anticlockwise;
+
+ if (p0 == p1 || p1 == p2 || radius == 0) {
+ LineTo(p1.x, p1.y);
+ return NS_OK;
+ }
+
+ // Check for colinearity
+ dir = (p2.x - p1.x) * (p0.y - p1.y) + (p2.y - p1.y) * (p1.x - p0.x);
+ if (dir == 0) {
+ LineTo(p1.x, p1.y);
+ return NS_OK;
+ }
+
+
+ // XXX - Math for this code was already available from the non-azure code
+ // and would be well tested. Perhaps converting to bezier directly might
+ // be more efficient longer run.
+ a2 = (p0.x-x1)*(p0.x-x1) + (p0.y-y1)*(p0.y-y1);
+ b2 = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
+ c2 = (p0.x-x2)*(p0.x-x2) + (p0.y-y2)*(p0.y-y2);
+ cosx = (a2+b2-c2)/(2*sqrt(a2*b2));
+
+ sinx = sqrt(1 - cosx*cosx);
+ d = radius / ((1 - cosx) / sinx);
+
+ anx = (x1-p0.x) / sqrt(a2);
+ any = (y1-p0.y) / sqrt(a2);
+ bnx = (x1-x2) / sqrt(b2);
+ bny = (y1-y2) / sqrt(b2);
+ x3 = x1 - anx*d;
+ y3 = y1 - any*d;
+ x4 = x1 - bnx*d;
+ y4 = y1 - bny*d;
+ anticlockwise = (dir < 0);
+ cx = x3 + any*radius*(anticlockwise ? 1 : -1);
+ cy = y3 - anx*radius*(anticlockwise ? 1 : -1);
+ angle0 = atan2((y3-cy), (x3-cx));
+ angle1 = atan2((y4-cy), (x4-cx));
+
+
+ LineTo(x3, y3);
+
+ Arc(cx, cy, radius, angle0, angle1, anticlockwise);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::Arc(float x, float y,
+ float r,
+ float startAngle, float endAngle,
+ PRBool ccw)
+{
+ if (!FloatValidate(x, y, r, startAngle, endAngle)) {
+ return NS_OK;
+ }
+
+ if (r < 0.0)
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+ EnsureWritablePath();
+
+ // We convert to Bezier curve here, since we need to be able to write in
+ // device space, but a transformed arc is no longer representable by an arc.
+
+ Point startPoint(x + cos(startAngle) * r, y + sin(startAngle) * r);
+
+ if (mPathBuilder) {
+ mPathBuilder->LineTo(startPoint);
+ } else {
+ mDSPathBuilder->LineTo(mTarget->GetTransform() * startPoint);
+ }
+
+ // Clockwise we always sweep from the smaller to the larger angle, ccw
+ // it's vice versa.
+ if (!ccw && (endAngle < startAngle)) {
+ Float correction = ceil((startAngle - endAngle) / (2.0f * M_PI));
+ endAngle += correction * 2.0f * M_PI;
+ } else if (ccw && (startAngle < endAngle)) {
+ Float correction = ceil((endAngle - startAngle) / (2.0f * M_PI));
+ startAngle += correction * 2.0f * M_PI;
+ }
+
+ // Sweeping more than 2 * pi is a full circle.
+ if (!ccw && (endAngle - startAngle > 2 * M_PI)) {
+ endAngle = startAngle + 2.0f * M_PI;
+ } else if (ccw && (startAngle - endAngle > 2.0f * M_PI)) {
+ endAngle = startAngle - 2.0f * M_PI;
+ }
+
+ // Calculate the total arc we're going to sweep.
+ Float arcSweepLeft = abs(endAngle - startAngle);
+ // Calculate the amount of curves needed, 1 per quarter circle.
+ Float curves = ceil(arcSweepLeft / (M_PI / 2.0f));
+
+ Float sweepDirection = ccw ? -1.0f : 1.0f;
+
+ Float currentStartAngle = startAngle;
+
+ while (arcSweepLeft > 0) {
+ // We guarantee here the current point is the start point of the next
+ // curve segment.
+ Float currentEndAngle;
+
+ if (arcSweepLeft > M_PI / 2.0f) {
+ currentEndAngle = currentStartAngle + M_PI / 2.0f * sweepDirection;
+ } else {
+ currentEndAngle = currentStartAngle + arcSweepLeft * sweepDirection;
+ }
+
+ Point currentStartPoint(x + cos(currentStartAngle) * r,
+ y + sin(currentStartAngle) * r);
+ Point currentEndPoint(x + cos(currentEndAngle) * r,
+ y + sin(currentEndAngle) * r);
+
+ // Calculate kappa constant for partial curve. The sign of angle in the
+ // tangent will actually ensure this is negative for a counter clockwise
+ // sweep, so changing signs later isn't needed.
+ Float kappa = (4.0f / 3.0f) * tan((currentEndAngle - currentStartAngle) / 4.0f) * r;
+
+ Point tangentStart(-sin(currentStartAngle), cos(currentStartAngle));
+ Point cp1 = currentStartPoint;
+ cp1 += tangentStart * kappa;
+
+ Point revTangentEnd(sin(currentEndAngle), -cos(currentEndAngle));
+ Point cp2 = currentEndPoint;
+ cp2 += revTangentEnd * kappa;
+
+ if (mPathBuilder) {
+ mPathBuilder->BezierTo(cp1, cp2, currentEndPoint);
+ } else {
+ mDSPathBuilder->BezierTo(mTarget->GetTransform() * cp1,
+ mTarget->GetTransform() * cp2,
+ mTarget->GetTransform() * currentEndPoint);
+ }
+
+ arcSweepLeft -= M_PI / 2.0f;
+ currentStartAngle = currentEndAngle;
+ }
+
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::Rect(float x, float y, float w, float h)
+{
+ if (!FloatValidate(x, y, w, h)) {
+ return NS_OK;
+ }
+
+ EnsureWritablePath();
+
+ if (mPathBuilder) {
+ mPathBuilder->MoveTo(Point(x, y));
+ mPathBuilder->LineTo(Point(x + w, y));
+ mPathBuilder->LineTo(Point(x + w, y + h));
+ mPathBuilder->LineTo(Point(x, y + h));
+ mPathBuilder->Close();
+ } else {
+ mDSPathBuilder->MoveTo(mTarget->GetTransform() * Point(x, y));
+ mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x + w, y));
+ mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x + w, y + h));
+ mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(x, y + h));
+ mDSPathBuilder->Close();
+ }
+
+ return NS_OK;
+}
+
+void
+nsCanvasRenderingContext2DAzure::EnsureWritablePath()
+{
+ if (mDSPathBuilder) {
+ return;
+ }
+
+ FillRule fillRule = CurrentState().fillRule;
+
+ if (mPathBuilder) {
+ if (mPathTransformWillUpdate) {
+ mPath = mPathBuilder->Finish();
+ mDSPathBuilder =
+ mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
+ mPath = nsnull;
+ mPathBuilder = nsnull;
+ }
+ return;
+ }
+
+ if (!mPath) {
+ mPathBuilder = mTarget->CreatePathBuilder(fillRule);
+ } else if (!mPathTransformWillUpdate) {
+ mPathBuilder = mPath->CopyToBuilder(fillRule);
+ } else {
+ mDSPathBuilder =
+ mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
+ }
+}
+
+void
+nsCanvasRenderingContext2DAzure::EnsureUserSpacePath()
+{
+ FillRule fillRule = CurrentState().fillRule;
+
+ if (!mPath && !mPathBuilder && !mDSPathBuilder) {
+ mPathBuilder = mTarget->CreatePathBuilder(fillRule);
+ }
+
+ if (mPathBuilder) {
+ mPath = mPathBuilder->Finish();
+ mPathBuilder = nsnull;
+ }
+
+ if (mPath && mPathTransformWillUpdate) {
+ mDSPathBuilder =
+ mPath->TransformedCopyToBuilder(mPathToDS, fillRule);
+ mPath = nsnull;
+ mPathTransformWillUpdate = false;
+ }
+
+ if (mDSPathBuilder) {
+ RefPtr dsPath;
+ dsPath = mDSPathBuilder->Finish();
+ mDSPathBuilder = nsnull;
+
+ Matrix inverse = mTarget->GetTransform();
+ if (!inverse.Invert()) {
+ return;
+ }
+
+ mPathBuilder =
+ dsPath->TransformedCopyToBuilder(inverse, fillRule);
+ mPath = mPathBuilder->Finish();
+ mPathBuilder = nsnull;
+ }
+
+ if (mPath && mPath->GetFillRule() != fillRule) {
+ mPathBuilder = mPath->CopyToBuilder(fillRule);
+ mPath = mPathBuilder->Finish();
+ }
+}
+
+void
+nsCanvasRenderingContext2DAzure::TransformWillUpdate()
+{
+ // Store the matrix that would transform the current path to device
+ // space.
+ if (mPath || mPathBuilder) {
+ if (!mPathTransformWillUpdate) {
+ // If the transform has already been updated, but a device space builder
+ // has not been created yet mPathToDS contains the right transform to
+ // transform the current mPath into device space.
+ // We should leave it alone.
+ mPathToDS = mTarget->GetTransform();
+ }
+ mPathTransformWillUpdate = true;
+ }
+}
+
+//
+// text
+//
+
+/**
+ * Helper function for SetFont that creates a style rule for the given font.
+ * @param aFont The CSS font string
+ * @param aNode The canvas element
+ * @param aResult Pointer in which to place the new style rule.
+ * @remark Assumes all pointer arguments are non-null.
+ */
+static nsresult
+CreateFontStyleRule(const nsAString& aFont,
+ nsINode* aNode,
+ StyleRule** aResult)
+{
+ nsRefPtr rule;
+ PRBool changed;
+
+ nsIPrincipal* principal = aNode->NodePrincipal();
+ nsIDocument* document = aNode->GetOwnerDoc();
+
+ nsIURI* docURL = document->GetDocumentURI();
+ nsIURI* baseURL = document->GetDocBaseURI();
+
+ // Pass the CSS Loader object to the parser, to allow parser error reports
+ // to include the outer window ID.
+ nsCSSParser parser(document->CSSLoader());
+
+ nsresult rv = parser.ParseStyleAttribute(EmptyString(), docURL, baseURL,
+ principal, getter_AddRefs(rule));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = parser.ParseProperty(eCSSProperty_font, aFont, docURL, baseURL,
+ principal, rule->GetDeclaration(), &changed,
+ PR_FALSE);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = parser.ParseProperty(eCSSProperty_line_height,
+ NS_LITERAL_STRING("normal"), docURL, baseURL,
+ principal, rule->GetDeclaration(), &changed,
+ PR_FALSE);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rule->RuleMatched();
+
+ rule.forget(aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetFont(const nsAString& font)
+{
+ nsresult rv;
+
+ /*
+ * If font is defined with relative units (e.g. ems) and the parent
+ * style context changes in between calls, setting the font to the
+ * same value as previous could result in a different computed value,
+ * so we cannot have the optimization where we check if the new font
+ * string is equal to the old one.
+ */
+
+ nsCOMPtr content = do_QueryInterface(mCanvasElement);
+ if (!content && !mDocShell) {
+ NS_WARNING("Canvas element must be an nsIContent and non-null or a docshell must be provided");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsIPresShell* presShell = GetPresShell();
+ if (!presShell) {
+ return NS_ERROR_FAILURE;
+ }
+ nsIDocument* document = presShell->GetDocument();
+
+ nsCOMArray rules;
+
+ nsRefPtr rule;
+ rv = CreateFontStyleRule(font, document, getter_AddRefs(rule));
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ css::Declaration *declaration = rule->GetDeclaration();
+ // The easiest way to see whether we got a syntax error or whether
+ // we got 'inherit' or 'initial' is to look at font-size-adjust,
+ // which the shorthand resets to either 'none' or
+ // '-moz-system-font'.
+ // We know the declaration is not !important, so we can use
+ // GetNormalBlock().
+ const nsCSSValue *fsaVal =
+ declaration->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust);
+ if (!fsaVal || (fsaVal->GetUnit() != eCSSUnit_None &&
+ fsaVal->GetUnit() != eCSSUnit_System_Font)) {
+ // We got an all-property value or a syntax error. The spec says
+ // this value must be ignored.
+ return NS_OK;
+ }
+
+ rules.AppendObject(rule);
+
+ nsStyleSet* styleSet = presShell->StyleSet();
+
+ // have to get a parent style context for inherit-like relative
+ // values (2em, bolder, etc.)
+ nsRefPtr parentContext;
+
+ if (content && content->IsInDoc()) {
+ // inherit from the canvas element
+ parentContext = nsComputedDOMStyle::GetStyleContextForElement(
+ content->AsElement(),
+ nsnull,
+ presShell);
+ } else {
+ // otherwise inherit from default (10px sans-serif)
+ nsRefPtr parentRule;
+ rv = CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
+ document,
+ getter_AddRefs(parentRule));
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCOMArray parentRules;
+ parentRules.AppendObject(parentRule);
+ parentContext = styleSet->ResolveStyleForRules(nsnull, parentRules);
+ }
+
+ if (!parentContext) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsRefPtr sc =
+ styleSet->ResolveStyleForRules(parentContext, rules);
+ if (!sc) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const nsStyleFont* fontStyle = sc->GetStyleFont();
+
+ NS_ASSERTION(fontStyle, "Could not obtain font style");
+
+ nsIAtom* language = sc->GetStyleVisibility()->mLanguage;
+ if (!language) {
+ language = presShell->GetPresContext()->GetLanguageFromCharset();
+ }
+
+ // use CSS pixels instead of dev pixels to avoid being affected by page zoom
+ const PRUint32 aupcp = nsPresContext::AppUnitsPerCSSPixel();
+ // un-zoom the font size to avoid being affected by text-only zoom
+ const nscoord fontSize = nsStyleFont::UnZoomText(parentContext->PresContext(), fontStyle->mFont.size);
+
+ PRBool printerFont = (presShell->GetPresContext()->Type() == nsPresContext::eContext_PrintPreview ||
+ presShell->GetPresContext()->Type() == nsPresContext::eContext_Print);
+
+ gfxFontStyle style(fontStyle->mFont.style,
+ fontStyle->mFont.weight,
+ fontStyle->mFont.stretch,
+ NSAppUnitsToFloatPixels(fontSize, float(aupcp)),
+ language,
+ fontStyle->mFont.sizeAdjust,
+ fontStyle->mFont.systemFont,
+ printerFont,
+ fontStyle->mFont.featureSettings,
+ fontStyle->mFont.languageOverride);
+
+ CurrentState().fontGroup =
+ gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle->mFont.name,
+ &style,
+ presShell->GetPresContext()->GetUserFontSet());
+ NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
+
+ // The font getter is required to be reserialized based on what we
+ // parsed (including having line-height removed). (Older drafts of
+ // the spec required font sizes be converted to pixels, but that no
+ // longer seems to be required.)
+ declaration->GetValue(eCSSProperty_font, CurrentState().font);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetFont(nsAString& font)
+{
+ /* will initilize the value if not set, else does nothing */
+ GetCurrentFontStyle();
+
+ font = CurrentState().font;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetTextAlign(const nsAString& ta)
+{
+ if (ta.EqualsLiteral("start"))
+ CurrentState().textAlign = TEXT_ALIGN_START;
+ else if (ta.EqualsLiteral("end"))
+ CurrentState().textAlign = TEXT_ALIGN_END;
+ else if (ta.EqualsLiteral("left"))
+ CurrentState().textAlign = TEXT_ALIGN_LEFT;
+ else if (ta.EqualsLiteral("right"))
+ CurrentState().textAlign = TEXT_ALIGN_RIGHT;
+ else if (ta.EqualsLiteral("center"))
+ CurrentState().textAlign = TEXT_ALIGN_CENTER;
+ // spec says to not throw error for invalid arg, but do it anyway
+ else
+ return NS_ERROR_INVALID_ARG;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetTextAlign(nsAString& ta)
+{
+ switch (CurrentState().textAlign)
+ {
+ case TEXT_ALIGN_START:
+ ta.AssignLiteral("start");
+ break;
+ case TEXT_ALIGN_END:
+ ta.AssignLiteral("end");
+ break;
+ case TEXT_ALIGN_LEFT:
+ ta.AssignLiteral("left");
+ break;
+ case TEXT_ALIGN_RIGHT:
+ ta.AssignLiteral("right");
+ break;
+ case TEXT_ALIGN_CENTER:
+ ta.AssignLiteral("center");
+ break;
+ default:
+ NS_ERROR("textAlign holds invalid value");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetTextBaseline(const nsAString& tb)
+{
+ if (tb.EqualsLiteral("top"))
+ CurrentState().textBaseline = TEXT_BASELINE_TOP;
+ else if (tb.EqualsLiteral("hanging"))
+ CurrentState().textBaseline = TEXT_BASELINE_HANGING;
+ else if (tb.EqualsLiteral("middle"))
+ CurrentState().textBaseline = TEXT_BASELINE_MIDDLE;
+ else if (tb.EqualsLiteral("alphabetic"))
+ CurrentState().textBaseline = TEXT_BASELINE_ALPHABETIC;
+ else if (tb.EqualsLiteral("ideographic"))
+ CurrentState().textBaseline = TEXT_BASELINE_IDEOGRAPHIC;
+ else if (tb.EqualsLiteral("bottom"))
+ CurrentState().textBaseline = TEXT_BASELINE_BOTTOM;
+ // spec says to not throw error for invalid arg, but do it anyway
+ else
+ return NS_ERROR_INVALID_ARG;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetTextBaseline(nsAString& tb)
+{
+ switch (CurrentState().textBaseline)
+ {
+ case TEXT_BASELINE_TOP:
+ tb.AssignLiteral("top");
+ break;
+ case TEXT_BASELINE_HANGING:
+ tb.AssignLiteral("hanging");
+ break;
+ case TEXT_BASELINE_MIDDLE:
+ tb.AssignLiteral("middle");
+ break;
+ case TEXT_BASELINE_ALPHABETIC:
+ tb.AssignLiteral("alphabetic");
+ break;
+ case TEXT_BASELINE_IDEOGRAPHIC:
+ tb.AssignLiteral("ideographic");
+ break;
+ case TEXT_BASELINE_BOTTOM:
+ tb.AssignLiteral("bottom");
+ break;
+ default:
+ NS_ERROR("textBaseline holds invalid value");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+/*
+ * Helper function that replaces the whitespace characters in a string
+ * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
+ * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
+ * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
+ * @param str The string whose whitespace characters to replace.
+ */
+static inline void
+TextReplaceWhitespaceCharacters(nsAutoString& str)
+{
+ str.ReplaceChar("\x09\x0A\x0B\x0C\x0D", PRUnichar(' '));
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::FillText(const nsAString& text, float x, float y, float maxWidth)
+{
+ return DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_FILL, nsnull);
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::StrokeText(const nsAString& text, float x, float y, float maxWidth)
+{
+ return DrawOrMeasureText(text, x, y, maxWidth, TEXT_DRAW_OPERATION_STROKE, nsnull);
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::MeasureText(const nsAString& rawText,
+ nsIDOMTextMetrics** _retval)
+{
+ float width;
+
+ nsresult rv = DrawOrMeasureText(rawText, 0, 0, 0, TEXT_DRAW_OPERATION_MEASURE, &width);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsRefPtr textMetrics = new nsTextMetricsAzure(width);
+ if (!textMetrics.get()) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ *_retval = textMetrics.forget().get();
+
+ return NS_OK;
+}
+
+/**
+ * Used for nsBidiPresUtils::ProcessText
+ */
+struct NS_STACK_CLASS nsCanvasBidiProcessorAzure : public nsBidiPresUtils::BidiProcessor
+{
+ virtual void SetText(const PRUnichar* text, PRInt32 length, nsBidiDirection direction)
+ {
+ mTextRun = gfxTextRunCache::MakeTextRun(text,
+ length,
+ mFontgrp,
+ mThebes,
+ mAppUnitsPerDevPixel,
+ direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
+ }
+
+ virtual nscoord GetWidth()
+ {
+ gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
+ mTextRun->GetLength(),
+ mDoMeasureBoundingBox ?
+ gfxFont::TIGHT_INK_EXTENTS :
+ gfxFont::LOOSE_INK_EXTENTS,
+ mThebes,
+ nsnull);
+
+ // this only measures the height; the total width is gotten from the
+ // the return value of ProcessText.
+ if (mDoMeasureBoundingBox) {
+ textRunMetrics.mBoundingBox.Scale(1.0 / mAppUnitsPerDevPixel);
+ mBoundingBox = mBoundingBox.Union(textRunMetrics.mBoundingBox);
+ }
+
+ return static_cast(textRunMetrics.mAdvanceWidth/gfxFloat(mAppUnitsPerDevPixel));
+ }
+
+ virtual void DrawText(nscoord xOffset, nscoord width)
+ {
+ gfxPoint point = mPt;
+ point.x += xOffset * mAppUnitsPerDevPixel;
+
+ // offset is given in terms of left side of string
+ if (mTextRun->IsRightToLeft()) {
+ // Bug 581092 - don't use rounded pixel width to advance to
+ // right-hand end of run, because this will cause different
+ // glyph positioning for LTR vs RTL drawing of the same
+ // glyph string on OS X and DWrite where textrun widths may
+ // involve fractional pixels.
+ gfxTextRun::Metrics textRunMetrics =
+ mTextRun->MeasureText(0,
+ mTextRun->GetLength(),
+ mDoMeasureBoundingBox ?
+ gfxFont::TIGHT_INK_EXTENTS :
+ gfxFont::LOOSE_INK_EXTENTS,
+ mThebes,
+ nsnull);
+ point.x += textRunMetrics.mAdvanceWidth;
+ // old code was:
+ // point.x += width * mAppUnitsPerDevPixel;
+ // TODO: restore this if/when we move to fractional coords
+ // throughout the text layout process
+ }
+
+ PRUint32 numRuns;
+ const gfxTextRun::GlyphRun *runs = mTextRun->GetGlyphRuns(&numRuns);
+ const PRUint32 appUnitsPerDevUnit = mAppUnitsPerDevPixel;
+ const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
+ Point baselineOrigin =
+ Point(point.x * devUnitsPerAppUnit, point.y * devUnitsPerAppUnit);
+
+ for (int c = 0; c < numRuns; c++) {
+ gfxFont *font = runs[c].mFont;
+ PRUint32 endRun = 0;
+ if (c + 1 < numRuns) {
+ endRun = runs[c + 1].mCharacterOffset;
+ } else {
+ endRun = mTextRun->GetLength();
+ }
+
+ const gfxTextRun::CompressedGlyph *glyphs = mTextRun->GetCharacterGlyphs();
+
+ RefPtr scaledFont =
+ gfxPlatform::GetPlatform()->GetScaledFontForFont(font);
+
+ GlyphBuffer buffer;
+
+ std::vector glyphBuf;
+
+ float advanceSum = 0;
+
+ for (int i = runs[c].mCharacterOffset; i < endRun; i++) {
+ Glyph newGlyph;
+ if (glyphs[i].IsSimpleGlyph()) {
+ newGlyph.mIndex = glyphs[i].GetSimpleGlyph();
+ if (mTextRun->IsRightToLeft()) {
+ newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
+ glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
+ } else {
+ newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
+ }
+ newGlyph.mPosition.y = baselineOrigin.y;
+ advanceSum += glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
+ glyphBuf.push_back(newGlyph);
+ continue;
+ }
+
+ if (!glyphs[i].GetGlyphCount()) {
+ continue;
+ }
+
+ gfxTextRun::DetailedGlyph *detailedGlyphs =
+ mTextRun->GetDetailedGlyphs(i);
+
+ for (int c = 0; c < glyphs[i].GetGlyphCount(); c++) {
+ newGlyph.mIndex = detailedGlyphs[c].mGlyphID;
+ if (mTextRun->IsRightToLeft()) {
+ newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit -
+ advanceSum - detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
+ } else {
+ newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit + advanceSum;
+ }
+ newGlyph.mPosition.y = baselineOrigin.y + detailedGlyphs[c].mYOffset * devUnitsPerAppUnit;
+ glyphBuf.push_back(newGlyph);
+ advanceSum += detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
+ }
+ }
+
+ buffer.mGlyphs = &glyphBuf.front();
+ buffer.mNumGlyphs = glyphBuf.size();
+
+ if (mOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_FILL) {
+ nsCanvasRenderingContext2DAzure::AdjustedTarget(mCtx)->
+ FillGlyphs(scaledFont, buffer,
+ nsCanvasRenderingContext2DAzure::GeneralPattern().
+ ForStyle(mCtx, nsCanvasRenderingContext2DAzure::STYLE_FILL, mCtx->mTarget),
+ DrawOptions(mState->globalAlpha, mState->op));
+ } else if (mOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_STROKE) {
+ RefPtr path = scaledFont->GetPathForGlyphs(buffer, mCtx->mTarget);
+
+ Matrix oldTransform = mCtx->mTarget->GetTransform();
+
+ nsCanvasRenderingContext2DAzure::AdjustedTarget(mCtx)->
+ Stroke(path, nsCanvasRenderingContext2DAzure::GeneralPattern().
+ ForStyle(mCtx, nsCanvasRenderingContext2DAzure::STYLE_STROKE, mCtx->mTarget),
+ StrokeOptions(mCtx->CurrentState().lineWidth, mCtx->CurrentState().lineJoin,
+ mCtx->CurrentState().lineCap, mCtx->CurrentState().miterLimit),
+ DrawOptions(mState->globalAlpha, mState->op));
+
+ }
+ }
+ }
+
+ // current text run
+ gfxTextRunCache::AutoTextRun mTextRun;
+
+ // pointer to a screen reference context used to measure text and such
+ nsRefPtr mThebes;
+
+ // Pointer to the draw target we should fill our text to
+ nsCanvasRenderingContext2DAzure *mCtx;
+
+ // position of the left side of the string, alphabetic baseline
+ gfxPoint mPt;
+
+ // current font
+ gfxFontGroup* mFontgrp;
+
+ // dev pixel conversion factor
+ PRUint32 mAppUnitsPerDevPixel;
+
+ // operation (fill or stroke)
+ nsCanvasRenderingContext2DAzure::TextDrawOperation mOp;
+
+ // context state
+ nsCanvasRenderingContext2DAzure::ContextState *mState;
+
+ // union of bounding boxes of all runs, needed for shadows
+ gfxRect mBoundingBox;
+
+ // true iff the bounding box should be measured
+ PRBool mDoMeasureBoundingBox;
+};
+
+nsresult
+nsCanvasRenderingContext2DAzure::DrawOrMeasureText(const nsAString& aRawText,
+ float aX,
+ float aY,
+ float aMaxWidth,
+ TextDrawOperation aOp,
+ float* aWidth)
+{
+ nsresult rv;
+
+ if (!FloatValidate(aX, aY, aMaxWidth))
+ return NS_ERROR_DOM_SYNTAX_ERR;
+
+ // spec isn't clear on what should happen if aMaxWidth <= 0, so
+ // treat it as an invalid argument
+ // technically, 0 should be an invalid value as well, but 0 is the default
+ // arg, and there is no way to tell if the default was used
+ if (aMaxWidth < 0)
+ return NS_ERROR_INVALID_ARG;
+
+ nsCOMPtr content = do_QueryInterface(mCanvasElement);
+ if (!content && !mDocShell) {
+ NS_WARNING("Canvas element must be an nsIContent and non-null or a docshell must be provided");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsIPresShell* presShell = GetPresShell();
+ if (!presShell)
+ return NS_ERROR_FAILURE;
+
+ nsIDocument* document = presShell->GetDocument();
+
+ nsBidiPresUtils* bidiUtils = presShell->GetPresContext()->GetBidiUtils();
+ if (!bidiUtils) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // replace all the whitespace characters with U+0020 SPACE
+ nsAutoString textToDraw(aRawText);
+ TextReplaceWhitespaceCharacters(textToDraw);
+
+ // for now, default to ltr if not in doc
+ PRBool isRTL = PR_FALSE;
+
+ if (content && content->IsInDoc()) {
+ // try to find the closest context
+ nsRefPtr canvasStyle =
+ nsComputedDOMStyle::GetStyleContextForElement(content->AsElement(),
+ nsnull,
+ presShell);
+ if (!canvasStyle) {
+ return NS_ERROR_FAILURE;
+ }
+
+ isRTL = canvasStyle->GetStyleVisibility()->mDirection ==
+ NS_STYLE_DIRECTION_RTL;
+ } else {
+ isRTL = GET_BIDI_OPTION_DIRECTION(document->GetBidiOptions()) == IBMBIDI_TEXTDIRECTION_RTL;
+ }
+
+ const ContextState &state = CurrentState();
+
+ // This is only needed to know if we can know the drawing bounding box easily.
+ PRBool doDrawShadow = aOp == TEXT_DRAW_OPERATION_FILL && NeedToDrawShadow();
+
+ nsCanvasBidiProcessorAzure processor;
+
+ GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nsnull);
+ processor.mPt = gfxPoint(aX, aY);
+ processor.mThebes =
+ new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
+ Matrix matrix = mTarget->GetTransform();
+ processor.mThebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32));
+ processor.mCtx = this;
+ processor.mOp = aOp;
+ processor.mBoundingBox = gfxRect(0, 0, 0, 0);
+ processor.mDoMeasureBoundingBox = doDrawShadow || !mIsEntireFrameInvalid;
+ processor.mState = &CurrentState();
+
+
+ processor.mFontgrp = GetCurrentFontStyle();
+ NS_ASSERTION(processor.mFontgrp, "font group is null");
+
+ nscoord totalWidth;
+
+ // calls bidi algo twice since it needs the full text width and the
+ // bounding boxes before rendering anything
+ rv = bidiUtils->ProcessText(textToDraw.get(),
+ textToDraw.Length(),
+ isRTL ? NSBIDI_RTL : NSBIDI_LTR,
+ presShell->GetPresContext(),
+ processor,
+ nsBidiPresUtils::MODE_MEASURE,
+ nsnull,
+ 0,
+ &totalWidth);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (aWidth) {
+ *aWidth = static_cast(totalWidth);
+ }
+
+ // if only measuring, don't need to do any more work
+ if (aOp==TEXT_DRAW_OPERATION_MEASURE) {
+ return NS_OK;
+ }
+
+ // offset pt.x based on text align
+ gfxFloat anchorX;
+
+ if (state.textAlign == TEXT_ALIGN_CENTER) {
+ anchorX = .5;
+ } else if (state.textAlign == TEXT_ALIGN_LEFT ||
+ (!isRTL && state.textAlign == TEXT_ALIGN_START) ||
+ (isRTL && state.textAlign == TEXT_ALIGN_END)) {
+ anchorX = 0;
+ } else {
+ anchorX = 1;
+ }
+
+ processor.mPt.x -= anchorX * totalWidth;
+
+ // offset pt.y based on text baseline
+ NS_ASSERTION(processor.mFontgrp->FontListLength()>0, "font group contains no fonts");
+ const gfxFont::Metrics& fontMetrics = processor.mFontgrp->GetFontAt(0)->GetMetrics();
+
+ gfxFloat anchorY;
+
+ switch (state.textBaseline)
+ {
+ case TEXT_BASELINE_HANGING:
+ // fall through; best we can do with the information available
+ case TEXT_BASELINE_TOP:
+ anchorY = fontMetrics.emAscent;
+ break;
+ break;
+ case TEXT_BASELINE_MIDDLE:
+ anchorY = (fontMetrics.emAscent - fontMetrics.emDescent) * .5f;
+ break;
+ case TEXT_BASELINE_IDEOGRAPHIC:
+ // fall through; best we can do with the information available
+ case TEXT_BASELINE_ALPHABETIC:
+ anchorY = 0;
+ break;
+ case TEXT_BASELINE_BOTTOM:
+ anchorY = -fontMetrics.emDescent;
+ break;
+ default:
+ NS_ERROR("mTextBaseline holds invalid value");
+ return NS_ERROR_FAILURE;
+ }
+
+ processor.mPt.y += anchorY;
+
+ // correct bounding box to get it to be the correct size/position
+ processor.mBoundingBox.width = totalWidth;
+ processor.mBoundingBox.MoveBy(processor.mPt);
+
+ processor.mPt.x *= processor.mAppUnitsPerDevPixel;
+ processor.mPt.y *= processor.mAppUnitsPerDevPixel;
+
+ Matrix oldTransform = mTarget->GetTransform();
+ // if text is over aMaxWidth, then scale the text horizontally such that its
+ // width is precisely aMaxWidth
+ if (aMaxWidth > 0 && totalWidth > aMaxWidth) {
+ Matrix newTransform = oldTransform;
+
+ // Translate so that the anchor point is at 0,0, then scale and then
+ // translate back.
+ newTransform.Translate(aX, 0);
+ newTransform.Scale(aMaxWidth / totalWidth, 1);
+ newTransform.Translate(-aX, 0);
+ /* we do this to avoid an ICE in the android compiler */
+ Matrix androidCompilerBug = newTransform;
+ mTarget->SetTransform(androidCompilerBug);
+ }
+
+ // save the previous bounding box
+ gfxRect boundingBox = processor.mBoundingBox;
+
+ // don't ever need to measure the bounding box twice
+ processor.mDoMeasureBoundingBox = PR_FALSE;
+
+ rv = bidiUtils->ProcessText(textToDraw.get(),
+ textToDraw.Length(),
+ isRTL ? NSBIDI_RTL : NSBIDI_LTR,
+ presShell->GetPresContext(),
+ processor,
+ nsBidiPresUtils::MODE_DRAW,
+ nsnull,
+ 0,
+ nsnull);
+
+
+ mTarget->SetTransform(oldTransform);
+
+ if (aOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_FILL && !doDrawShadow)
+ return RedrawUser(boundingBox);
+
+ return Redraw();
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetMozTextStyle(const nsAString& textStyle)
+{
+ // font and mozTextStyle are the same value
+ return SetFont(textStyle);
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetMozTextStyle(nsAString& textStyle)
+{
+ // font and mozTextStyle are the same value
+ return GetFont(textStyle);
+}
+
+gfxFontGroup *nsCanvasRenderingContext2DAzure::GetCurrentFontStyle()
+{
+ // use lazy initilization for the font group since it's rather expensive
+ if (!CurrentState().fontGroup) {
+ nsresult rv = SetFont(kDefaultFontStyle);
+ if (NS_FAILED(rv)) {
+ gfxFontStyle style;
+ style.size = kDefaultFontSize;
+ CurrentState().fontGroup =
+ gfxPlatform::GetPlatform()->CreateFontGroup(kDefaultFontName,
+ &style,
+ nsnull);
+ if (CurrentState().fontGroup) {
+ CurrentState().font = kDefaultFontStyle;
+ rv = NS_OK;
+ } else {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Default canvas font is invalid");
+ }
+
+ return CurrentState().fontGroup;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::MozDrawText(const nsAString& textToDraw)
+{
+ return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::MozMeasureText(const nsAString& textToMeasure, float *retVal)
+{
+ return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::MozPathText(const nsAString& textToPath)
+{
+ return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::MozTextAlongPath(const nsAString& textToDraw, PRBool stroke)
+{
+ return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+}
+
+//
+// line caps/joins
+//
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetLineWidth(float width)
+{
+ if (!FloatValidate(width) || width <= 0.0) {
+ return NS_OK;
+ }
+
+ CurrentState().lineWidth = width;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetLineWidth(float *width)
+{
+ *width = CurrentState().lineWidth;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetLineCap(const nsAString& capstyle)
+{
+ CapStyle cap;
+
+ if (capstyle.EqualsLiteral("butt")) {
+ cap = CAP_BUTT;
+ } else if (capstyle.EqualsLiteral("round")) {
+ cap = CAP_ROUND;
+ } else if (capstyle.EqualsLiteral("square")) {
+ cap = CAP_SQUARE;
+ } else {
+ // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+ return NS_OK;
+ }
+
+ CurrentState().lineCap = cap;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetLineCap(nsAString& capstyle)
+{
+ switch (CurrentState().lineCap) {
+ case CAP_BUTT:
+ capstyle.AssignLiteral("butt");
+ break;
+ case CAP_ROUND:
+ capstyle.AssignLiteral("round");
+ break;
+ case CAP_SQUARE:
+ capstyle.AssignLiteral("square");
+ break;
+ default:
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetLineJoin(const nsAString& joinstyle)
+{
+ JoinStyle j;
+
+ if (joinstyle.EqualsLiteral("round")) {
+ j = JOIN_ROUND;
+ } else if (joinstyle.EqualsLiteral("bevel")) {
+ j = JOIN_BEVEL;
+ } else if (joinstyle.EqualsLiteral("miter")) {
+ j = JOIN_MITER_OR_BEVEL;
+ } else {
+ // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+ return NS_OK;
+ }
+
+ CurrentState().lineJoin = j;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetLineJoin(nsAString& joinstyle)
+{
+ switch (CurrentState().lineJoin) {
+ case JOIN_ROUND:
+ joinstyle.AssignLiteral("round");
+ break;
+ case JOIN_BEVEL:
+ joinstyle.AssignLiteral("bevel");
+ break;
+ case JOIN_MITER_OR_BEVEL:
+ joinstyle.AssignLiteral("miter");
+ break;
+ default:
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetMiterLimit(float miter)
+{
+ if (!FloatValidate(miter) || miter <= 0.0)
+ return NS_OK;
+
+ CurrentState().miterLimit = miter;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetMiterLimit(float *miter)
+{
+ *miter = CurrentState().miterLimit;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::IsPointInPath(float x, float y, PRBool *retVal)
+{
+ if (!FloatValidate(x,y)) {
+ *retVal = PR_FALSE;
+ return NS_OK;
+ }
+
+ EnsureUserSpacePath();
+
+ *retVal = PR_FALSE;
+
+ if (mPath) {
+ *retVal = mPath->ContainsPoint(Point(x, y), mTarget->GetTransform());
+ }
+
+ return NS_OK;
+}
+
+//
+// image
+//
+
+// drawImage(in HTMLImageElement image, in float dx, in float dy);
+// -- render image from 0,0 at dx,dy top-left coords
+// drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
+// -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
+// drawImage(in HTMLImageElement image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh);
+// -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::DrawImage(nsIDOMElement *imgElt, float a1,
+ float a2, float a3, float a4, float a5,
+ float a6, float a7, float a8,
+ PRUint8 optional_argc)
+{
+ if (!imgElt) {
+ return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
+ }
+
+ if (optional_argc == 0) {
+ if (!FloatValidate(a1, a2)) {
+ return NS_OK;
+ }
+ } else if (optional_argc == 2) {
+ if (!FloatValidate(a1, a2, a3, a4)) {
+ return NS_OK;
+ }
+ } else if (optional_argc == 6) {
+ if (!FloatValidate(a1, a2, a3, a4, a5, a6) || !FloatValidate(a7, a8)) {
+ return NS_OK;
+ }
+ }
+
+ double sx,sy,sw,sh;
+ double dx,dy,dw,dh;
+
+ nsCOMPtr content = do_QueryInterface(imgElt);
+ nsHTMLCanvasElement* canvas = nsHTMLCanvasElement::FromContent(content);
+ if (canvas) {
+ nsIntSize size = canvas->GetSize();
+ if (size.width == 0 || size.height == 0) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+ }
+
+ RefPtr srcSurf;
+
+ gfxIntSize imgSize;
+ nsRefPtr imgsurf =
+ CanvasImageCache::Lookup(imgElt, HTMLCanvasElement(), &imgSize);
+
+ if (imgsurf) {
+ srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, imgsurf);
+ }
+
+ // We know an Azure canvas always has an HTMLCanvasElement!
+ if (canvas == HTMLCanvasElement()) {
+ // Self-copy.
+ srcSurf = mTarget->Snapshot();
+ imgSize = gfxIntSize(mWidth, mHeight);
+ }
+
+ if (!srcSurf) {
+ // The canvas spec says that drawImage should draw the first frame
+ // of animated images
+ PRUint32 sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME;
+ nsLayoutUtils::SurfaceFromElementResult res =
+ nsLayoutUtils::SurfaceFromElement(imgElt, sfeFlags);
+
+ if (!res.mSurface) {
+ // Spec says to silently do nothing if the element is still loading.
+ return res.mIsStillLoading ? NS_OK : NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // Ignore nsnull cairo surfaces! See bug 666312.
+ if (!res.mSurface->CairoSurface()) {
+ return NS_OK;
+ }
+
+ imgsurf = res.mSurface.forget();
+ imgSize = res.mSize;
+
+ if (mCanvasElement) {
+ CanvasUtils::DoDrawImageSecurityCheck(HTMLCanvasElement(),
+ res.mPrincipal, res.mIsWriteOnly);
+ }
+
+ if (res.mImageRequest) {
+ CanvasImageCache::NotifyDrawImage(imgElt, HTMLCanvasElement(),
+ res.mImageRequest, imgsurf, imgSize);
+ }
+
+ srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, imgsurf);
+ }
+
+ if (optional_argc == 0) {
+ dx = a1;
+ dy = a2;
+ sx = sy = 0.0;
+ dw = sw = (double) imgSize.width;
+ dh = sh = (double) imgSize.height;
+ } else if (optional_argc == 2) {
+ dx = a1;
+ dy = a2;
+ dw = a3;
+ dh = a4;
+ sx = sy = 0.0;
+ sw = (double) imgSize.width;
+ sh = (double) imgSize.height;
+ } else if (optional_argc == 6) {
+ sx = a1;
+ sy = a2;
+ sw = a3;
+ sh = a4;
+ dx = a5;
+ dy = a6;
+ dw = a7;
+ dh = a8;
+ } else {
+ // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (sw == 0.0 || sh == 0.0) {
+ // zero-sized source -- failure !?
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+
+ if (dw == 0.0 || dh == 0.0) {
+ // not really failure, but nothing to do --
+ // and noone likes a divide-by-zero
+ return NS_OK;
+ }
+
+ // Handle negative sw, sh, dw and dh by flipping the rectangle over in the
+ // relevant direction.
+ if (sw < 0.0) {
+ sx += sw;
+ sw = -sw;
+ }
+ if (sh < 0.0) {
+ sy += sh;
+ sh = -sh;
+ }
+ if (dw < 0.0) {
+ dx += dw;
+ dw = -dw;
+ }
+ if (dh < 0.0) {
+ dy += dh;
+ dh = -dh;
+ }
+
+ // Checking source image boundaries.
+ if (sx < 0 || sx + sw > (double) imgSize.width ||
+ sy < 0 || sy + sh > (double) imgSize.height) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+
+ Filter filter;
+
+ if (CurrentState().imageSmoothingEnabled)
+ filter = mgfx::FILTER_LINEAR;
+ else
+ filter = mgfx::FILTER_POINT;
+
+ AdjustedTarget(this)->
+ DrawSurface(srcSurf,
+ mgfx::Rect(dx, dy, dw, dh),
+ mgfx::Rect(sx, sy, sw, sh),
+ DrawSurfaceOptions(filter),
+ DrawOptions(CurrentState().globalAlpha, CurrentState().op));
+
+ return RedrawUser(gfxRect(dx, dy, dw, dh));
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetGlobalCompositeOperation(const nsAString& op)
+{
+ CompositionOp comp_op;
+
+#define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
+ if (op.EqualsLiteral(cvsop)) \
+ comp_op = OP_##op2d;
+
+ CANVAS_OP_TO_GFX_OP("copy", SOURCE)
+ else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
+ else CANVAS_OP_TO_GFX_OP("source-in", IN)
+ else CANVAS_OP_TO_GFX_OP("source-out", OUT)
+ else CANVAS_OP_TO_GFX_OP("source-over", OVER)
+ else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
+ else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
+ else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
+ else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
+ else CANVAS_OP_TO_GFX_OP("lighter", ADD)
+ else CANVAS_OP_TO_GFX_OP("xor", XOR)
+ // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+ else return NS_OK;
+
+#undef CANVAS_OP_TO_GFX_OP
+ CurrentState().op = comp_op;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetGlobalCompositeOperation(nsAString& op)
+{
+ CompositionOp comp_op = CurrentState().op;
+
+#define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
+ if (comp_op == OP_##op2d) \
+ op.AssignLiteral(cvsop);
+
+ CANVAS_OP_TO_GFX_OP("copy", SOURCE)
+ else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP)
+ else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN)
+ else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT)
+ else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER)
+ else CANVAS_OP_TO_GFX_OP("lighter", ADD)
+ else CANVAS_OP_TO_GFX_OP("source-atop", ATOP)
+ else CANVAS_OP_TO_GFX_OP("source-in", IN)
+ else CANVAS_OP_TO_GFX_OP("source-out", OUT)
+ else CANVAS_OP_TO_GFX_OP("source-over", OVER)
+ else CANVAS_OP_TO_GFX_OP("xor", XOR)
+ else return NS_ERROR_FAILURE;
+
+#undef CANVAS_OP_TO_GFX_OP
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::DrawWindow(nsIDOMWindow* aWindow, float aX, float aY,
+ float aW, float aH,
+ const nsAString& aBGColor,
+ PRUint32 flags)
+{
+ NS_ENSURE_ARG(aWindow != nsnull);
+
+ // protect against too-large surfaces that will cause allocation
+ // or overflow issues
+ if (!gfxASurface::CheckSurfaceSize(gfxIntSize(PRInt32(aW), PRInt32(aH)),
+ 0xffff))
+ return NS_ERROR_FAILURE;
+
+ nsRefPtr drawSurf;
+ GetThebesSurface(getter_AddRefs(drawSurf));
+
+ nsRefPtr thebes = new gfxContext(drawSurf);
+
+ Matrix matrix = mTarget->GetTransform();
+ thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
+ matrix._22, matrix._31, matrix._32));
+
+ // We can't allow web apps to call this until we fix at least the
+ // following potential security issues:
+ // -- rendering cross-domain IFRAMEs and then extracting the results
+ // -- rendering the user's theme and then extracting the results
+ // -- rendering native anonymous content (e.g., file input paths;
+ // scrollbars should be allowed)
+ if (!nsContentUtils::IsCallerTrustedForRead()) {
+ // not permitted to use DrawWindow
+ // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ // Flush layout updates
+ if (!(flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH))
+ nsContentUtils::FlushLayoutForTree(aWindow);
+
+ nsRefPtr presContext;
+ nsCOMPtr win = do_QueryInterface(aWindow);
+ if (win) {
+ nsIDocShell* docshell = win->GetDocShell();
+ if (docshell) {
+ docshell->GetPresContext(getter_AddRefs(presContext));
+ }
+ }
+ if (!presContext)
+ return NS_ERROR_FAILURE;
+
+ nscolor bgColor;
+
+ nsIDocument* elementDoc = mCanvasElement ?
+ HTMLCanvasElement()->GetOwnerDoc() : nsnull;
+
+ // Pass the CSS Loader object to the parser, to allow parser error reports
+ // to include the outer window ID.
+ nsCSSParser parser(elementDoc ? elementDoc->CSSLoader() : nsnull);
+ nsresult rv = parser.ParseColorString(PromiseFlatString(aBGColor),
+ nsnull, 0, &bgColor);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsIPresShell* presShell = presContext->PresShell();
+ NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
+
+ nsRect r(nsPresContext::CSSPixelsToAppUnits(aX),
+ nsPresContext::CSSPixelsToAppUnits(aY),
+ nsPresContext::CSSPixelsToAppUnits(aW),
+ nsPresContext::CSSPixelsToAppUnits(aH));
+ PRUint32 renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
+ nsIPresShell::RENDER_DOCUMENT_RELATIVE);
+ if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
+ renderDocFlags |= nsIPresShell::RENDER_CARET;
+ }
+ if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
+ renderDocFlags &= ~(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
+ nsIPresShell::RENDER_DOCUMENT_RELATIVE);
+ }
+ if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_USE_WIDGET_LAYERS) {
+ renderDocFlags |= nsIPresShell::RENDER_USE_WIDGET_LAYERS;
+ }
+ if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_ASYNC_DECODE_IMAGES) {
+ renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES;
+ }
+
+ rv = presShell->RenderDocument(r, renderDocFlags, bgColor, thebes);
+
+ // note that aX and aY are coordinates in the document that
+ // we're drawing; aX and aY are drawn to 0,0 in current user
+ // space.
+ RedrawUser(gfxRect(0, 0, aW, aH));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::AsyncDrawXULElement(nsIDOMXULElement* aElem, float aX, float aY,
+ float aW, float aH,
+ const nsAString& aBGColor,
+ PRUint32 flags)
+{
+#if 0
+ NS_ENSURE_ARG(aElem != nsnull);
+
+ // We can't allow web apps to call this until we fix at least the
+ // following potential security issues:
+ // -- rendering cross-domain IFRAMEs and then extracting the results
+ // -- rendering the user's theme and then extracting the results
+ // -- rendering native anonymous content (e.g., file input paths;
+ // scrollbars should be allowed)
+ if (!nsContentUtils::IsCallerTrustedForRead()) {
+ // not permitted to use DrawWindow
+ // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ nsCOMPtr loaderOwner = do_QueryInterface(aElem);
+ if (!loaderOwner)
+ return NS_ERROR_FAILURE;
+
+ nsRefPtr frameloader = loaderOwner->GetFrameLoader();
+ if (!frameloader)
+ return NS_ERROR_FAILURE;
+
+ PBrowserParent *child = frameloader->GetRemoteBrowser();
+ if (!child) {
+ nsCOMPtr window =
+ do_GetInterface(frameloader->GetExistingDocShell());
+ if (!window)
+ return NS_ERROR_FAILURE;
+
+ return DrawWindow(window, aX, aY, aW, aH, aBGColor, flags);
+ }
+
+ // protect against too-large surfaces that will cause allocation
+ // or overflow issues
+ if (!gfxASurface::CheckSurfaceSize(gfxIntSize(aW, aH), 0xffff))
+ return NS_ERROR_FAILURE;
+
+ PRBool flush =
+ (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH) == 0;
+
+ PRUint32 renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
+ if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET) {
+ renderDocFlags |= nsIPresShell::RENDER_CARET;
+ }
+ if (flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW) {
+ renderDocFlags &= ~nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING;
+ }
+
+ nsRect rect(nsPresContext::CSSPixelsToAppUnits(aX),
+ nsPresContext::CSSPixelsToAppUnits(aY),
+ nsPresContext::CSSPixelsToAppUnits(aW),
+ nsPresContext::CSSPixelsToAppUnits(aH));
+ if (mIPC) {
+ PDocumentRendererParent *pdocrender =
+ child->SendPDocumentRendererConstructor(rect,
+ mThebes->CurrentMatrix(),
+ nsString(aBGColor),
+ renderDocFlags, flush,
+ nsIntSize(mWidth, mHeight));
+ if (!pdocrender)
+ return NS_ERROR_FAILURE;
+
+ DocumentRendererParent *docrender =
+ static_cast(pdocrender);
+
+ docrender->SetCanvasContext(this, mThebes);
+ }
+#endif
+ return NS_OK;
+}
+
+//
+// device pixel getting/setting
+//
+
+void
+nsCanvasRenderingContext2DAzure::EnsureUnpremultiplyTable() {
+ if (sUnpremultiplyTable)
+ return;
+
+ // Infallably alloc the unpremultiply table.
+ sUnpremultiplyTable = new PRUint8[256][256];
+
+ // It's important that the array be indexed first by alpha and then by rgb
+ // value. When we unpremultiply a pixel, we're guaranteed to do three
+ // lookups with the same alpha; indexing by alpha first makes it likely that
+ // those three lookups will be close to one another in memory, thus
+ // increasing the chance of a cache hit.
+
+ // a == 0 case
+ for (PRUint32 c = 0; c <= 255; c++) {
+ sUnpremultiplyTable[0][c] = c;
+ }
+
+ for (int a = 1; a <= 255; a++) {
+ for (int c = 0; c <= 255; c++) {
+ sUnpremultiplyTable[a][c] = (PRUint8)((c * 255) / a);
+ }
+ }
+}
+
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetImageData()
+{
+ /* Should never be called -- GetImageData_explicit is the QS entry point */
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h,
+ PRUint8 *aData, PRUint32 aDataLen)
+{
+ if (!mValid)
+ return NS_ERROR_FAILURE;
+
+ if (!mCanvasElement && !mDocShell) {
+ NS_ERROR("No canvas element and no docshell in GetImageData!!!");
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ // Check only if we have a canvas element; if we were created with a docshell,
+ // then it's special internal use.
+ if (mCanvasElement &&
+ HTMLCanvasElement()->IsWriteOnly() &&
+ !nsContentUtils::IsCallerTrustedForRead())
+ {
+ // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ if (w == 0 || h == 0 || aDataLen != w * h * 4)
+ return NS_ERROR_DOM_SYNTAX_ERR;
+
+ CheckedInt32 rightMost = CheckedInt32(x) + w;
+ CheckedInt32 bottomMost = CheckedInt32(y) + h;
+
+ if (!rightMost.valid() || !bottomMost.valid())
+ return NS_ERROR_DOM_SYNTAX_ERR;
+
+ if (mZero) {
+ return NS_OK;
+ }
+
+ IntRect srcRect(0, 0, mWidth, mHeight);
+ IntRect destRect(x, y, w, h);
+
+ if (!srcRect.Contains(destRect)) {
+ // Some data is outside the canvas surface, clear the destination.
+ memset(aData, 0, aDataLen);
+ }
+
+ bool finishedPainting = false;
+
+ IntRect srcReadRect = srcRect.Intersect(destRect);
+ IntRect dstWriteRect = srcReadRect;
+ dstWriteRect.MoveBy(-x, -y);
+
+ PRUint8 *src = aData;
+ PRUint32 srcStride = w * 4;
+
+ if (!srcReadRect.IsEmpty()) {
+ RefPtr snapshot = mTarget->Snapshot();
+
+ RefPtr readback = snapshot->GetDataSurface();
+
+ srcStride = readback->Stride();
+ src = readback->GetData() + srcReadRect.y * srcStride + srcReadRect.x * 4;
+ }
+
+ // make sure sUnpremultiplyTable has been created
+ EnsureUnpremultiplyTable();
+
+ // NOTE! dst is the same as src, and this relies on reading
+ // from src and advancing that ptr before writing to dst.
+ PRUint8 *dst = aData + dstWriteRect.y * (w * 4) + dstWriteRect.x * 4;
+
+ for (PRUint32 j = 0; j < dstWriteRect.height; j++) {
+ for (PRUint32 i = 0; i < dstWriteRect.width; i++) {
+ // XXX Is there some useful swizzle MMX we can use here?
+#ifdef IS_LITTLE_ENDIAN
+ PRUint8 b = *src++;
+ PRUint8 g = *src++;
+ PRUint8 r = *src++;
+ PRUint8 a = *src++;
+#else
+ PRUint8 a = *src++;
+ PRUint8 r = *src++;
+ PRUint8 g = *src++;
+ PRUint8 b = *src++;
+#endif
+ // Convert to non-premultiplied color
+ *dst++ = sUnpremultiplyTable[a][r];
+ *dst++ = sUnpremultiplyTable[a][g];
+ *dst++ = sUnpremultiplyTable[a][b];
+ *dst++ = a;
+ }
+ src += srcStride - (dstWriteRect.width * 4);
+ dst += (w * 4) - (dstWriteRect.width * 4);
+ }
+ return NS_OK;
+}
+
+void
+nsCanvasRenderingContext2DAzure::EnsurePremultiplyTable() {
+ if (sPremultiplyTable)
+ return;
+
+ // Infallably alloc the premultiply table.
+ sPremultiplyTable = new PRUint8[256][256];
+
+ // Like the unpremultiply table, it's important that we index the premultiply
+ // table with the alpha value as the first index to ensure good cache
+ // performance.
+
+ for (int a = 0; a <= 255; a++) {
+ for (int c = 0; c <= 255; c++) {
+ sPremultiplyTable[a][c] = (a * c + 254) / 255;
+ }
+ }
+}
+
+void
+nsCanvasRenderingContext2DAzure::FillRuleChanged()
+{
+ if (mPath) {
+ mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
+ mPath = nsnull;
+ }
+}
+
+// void putImageData (in ImageData d, in float x, in float y);
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::PutImageData()
+{
+ /* Should never be called -- PutImageData_explicit is the QS entry point */
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::PutImageData_explicit(PRInt32 x, PRInt32 y, PRUint32 w, PRUint32 h,
+ unsigned char *aData, PRUint32 aDataLen,
+ PRBool hasDirtyRect, PRInt32 dirtyX, PRInt32 dirtyY,
+ PRInt32 dirtyWidth, PRInt32 dirtyHeight)
+{
+ if (!mValid) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (w == 0 || h == 0) {
+ return NS_ERROR_DOM_SYNTAX_ERR;
+ }
+
+ IntRect dirtyRect;
+ IntRect imageDataRect(0, 0, w, h);
+
+ if (hasDirtyRect) {
+ // fix up negative dimensions
+ if (dirtyWidth < 0) {
+ NS_ENSURE_TRUE(dirtyWidth != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
+
+ CheckedInt32 checkedDirtyX = CheckedInt32(dirtyX) + dirtyWidth;
+
+ if (!checkedDirtyX.valid())
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+ dirtyX = checkedDirtyX.value();
+ dirtyWidth = -(int32)dirtyWidth;
+ }
+
+ if (dirtyHeight < 0) {
+ NS_ENSURE_TRUE(dirtyHeight != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
+
+ CheckedInt32 checkedDirtyY = CheckedInt32(dirtyY) + dirtyHeight;
+
+ if (!checkedDirtyY.valid())
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+ dirtyY = checkedDirtyY.value();
+ dirtyHeight = -(int32)dirtyHeight;
+ }
+
+ // bound the dirty rect within the imageData rectangle
+ dirtyRect = imageDataRect.Intersect(IntRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight));
+
+ if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0)
+ return NS_OK;
+ } else {
+ dirtyRect = imageDataRect;
+ }
+
+ dirtyRect.MoveBy(IntPoint(x, y));
+ dirtyRect = IntRect(0, 0, mWidth, mHeight).Intersect(dirtyRect);
+
+ if (dirtyRect.Width() <= 0 || dirtyRect.Height() <= 0) {
+ return NS_OK;
+ }
+
+ PRUint32 len = w * h * 4;
+ if (aDataLen != len) {
+ return NS_ERROR_DOM_SYNTAX_ERR;
+ }
+
+ nsRefPtr imgsurf = new gfxImageSurface(gfxIntSize(w, h),
+ gfxASurface::ImageFormatARGB32);
+ if (!imgsurf || imgsurf->CairoStatus()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // ensure premultiply table has been created
+ EnsurePremultiplyTable();
+
+ PRUint8 *src = aData;
+ PRUint8 *dst = imgsurf->Data();
+
+ for (PRUint32 j = 0; j < h; j++) {
+ for (PRUint32 i = 0; i < w; i++) {
+ PRUint8 r = *src++;
+ PRUint8 g = *src++;
+ PRUint8 b = *src++;
+ PRUint8 a = *src++;
+
+ // Convert to premultiplied color (losslessly if the input came from getImageData)
+#ifdef IS_LITTLE_ENDIAN
+ *dst++ = sPremultiplyTable[a][b];
+ *dst++ = sPremultiplyTable[a][g];
+ *dst++ = sPremultiplyTable[a][r];
+ *dst++ = a;
+#else
+ *dst++ = a;
+ *dst++ = sPremultiplyTable[a][r];
+ *dst++ = sPremultiplyTable[a][g];
+ *dst++ = sPremultiplyTable[a][b];
+#endif
+ }
+ }
+
+ RefPtr sourceSurface =
+ mTarget->CreateSourceSurfaceFromData(imgsurf->Data(), IntSize(w, h), imgsurf->Stride(), FORMAT_B8G8R8A8);
+
+
+ mTarget->CopySurface(sourceSurface,
+ IntRect(dirtyRect.x - x, dirtyRect.y - y,
+ dirtyRect.width, dirtyRect.height),
+ IntPoint(dirtyRect.x, dirtyRect.y));
+
+ Redraw(mgfx::Rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetThebesSurface(gfxASurface **surface)
+{
+ if (!mTarget) {
+ nsRefPtr tmpSurf =
+ gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(1, 1), gfxASurface::CONTENT_COLOR_ALPHA);
+ *surface = tmpSurf.forget().get();
+ return NS_OK;
+ }
+
+ nsRefPtr newSurf =
+ gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
+
+ *surface = newSurf.forget().get();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::CreateImageData()
+{
+ /* Should never be called; handled entirely in the quickstub */
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::GetMozImageSmoothingEnabled(PRBool *retVal)
+{
+ *retVal = CurrentState().imageSmoothingEnabled;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2DAzure::SetMozImageSmoothingEnabled(PRBool val)
+{
+ if (val != CurrentState().imageSmoothingEnabled) {
+ CurrentState().imageSmoothingEnabled = val;
+ }
+
+ return NS_OK;
+}
+
+static PRUint8 g2DContextLayerUserData;
+
+class CanvasRenderingContext2DUserData : public LayerUserData {
+public:
+ CanvasRenderingContext2DUserData(nsHTMLCanvasElement *aContent)
+ : mContent(aContent) {}
+ static void DidTransactionCallback(void* aData)
+ {
+ static_cast(aData)->mContent->MarkContextClean();
+ }
+
+private:
+ nsRefPtr mContent;
+};
+
+already_AddRefed
+nsCanvasRenderingContext2DAzure::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+ CanvasLayer *aOldLayer,
+ LayerManager *aManager)
+{
+ if (!mValid) {
+ return nsnull;
+ }
+
+ if (mTarget) {
+ mTarget->Flush();
+ }
+
+ if (!mResetLayer && aOldLayer &&
+ aOldLayer->HasUserData(&g2DContextLayerUserData)) {
+ NS_ADDREF(aOldLayer);
+ return aOldLayer;
+ }
+
+ nsRefPtr canvasLayer = aManager->CreateCanvasLayer();
+ if (!canvasLayer) {
+ NS_WARNING("CreateCanvasLayer returned null!");
+ return nsnull;
+ }
+ CanvasRenderingContext2DUserData *userData = nsnull;
+ if (aBuilder->IsPaintingToWindow()) {
+ // Make the layer tell us whenever a transaction finishes (including
+ // the current transaction), so we can clear our invalidation state and
+ // start invalidating again. We need to do this for the layer that is
+ // being painted to a window (there shouldn't be more than one at a time,
+ // and if there is, flushing the invalidation state more often than
+ // necessary is harmless).
+
+ // The layer will be destroyed when we tear down the presentation
+ // (at the latest), at which time this userData will be destroyed,
+ // releasing the reference to the element.
+ // The userData will receive DidTransactionCallbacks, which flush the
+ // the invalidation state to indicate that the canvas is up to date.
+ userData = new CanvasRenderingContext2DUserData(HTMLCanvasElement());
+ canvasLayer->SetDidTransactionCallback(
+ CanvasRenderingContext2DUserData::DidTransactionCallback, userData);
+ }
+ canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
+
+ CanvasLayer::Data data;
+
+ data.mDrawTarget = mTarget;
+ data.mSize = nsIntSize(mWidth, mHeight);
+
+ canvasLayer->Initialize(data);
+ PRUint32 flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
+ canvasLayer->SetContentFlags(flags);
+ canvasLayer->Updated();
+
+ mResetLayer = PR_FALSE;
+
+ return canvasLayer.forget();
+}
+
+void
+nsCanvasRenderingContext2DAzure::MarkContextClean()
+{
+ if (mInvalidateCount > 0) {
+ mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount;
+ }
+ mIsEntireFrameInvalid = PR_FALSE;
+ mInvalidateCount = 0;
+}
+
diff --git a/content/canvas/test/test_2d.gradient.radial.cone.top.html b/content/canvas/test/test_2d.gradient.radial.cone.top.html
index 424df7fa826..04b2fef218a 100644
--- a/content/canvas/test/test_2d.gradient.radial.cone.top.html
+++ b/content/canvas/test/test_2d.gradient.radial.cone.top.html
@@ -6,6 +6,18 @@
@@ -245,7 +256,7 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -1018,8 +1029,8 @@ function todo_isPixel(ctx, x,y, r,g,b,a, d) {
todo(r-d <= pr && pr <= r+d &&
g-d <= pg && pg <= g+d &&
b-d <= pb && pb <= b+d &&
- a-d <= pa && pa <= a+d,
- "pixel "+pos+" is "+pr+","+pg+","+pb+","+pa+"; expected "+colour+" +/- "+d);
+ a-d <= pa && pa <= a+d,
+ "pixel "+pos+" of "+ctx.canvas.id+" is "+pr+","+pg+","+pb+","+pa+" (marked todo); expected "+colour+" +/- " + d);
}
function test_2d_composite_globalAlpha_canvaspattern() {
@@ -1467,7 +1478,7 @@ ok(ctx.globalCompositeOperation == 'xor', "ctx.globalCompositeOperation == 'xor'
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -1573,7 +1584,7 @@ ok(ctx.globalCompositeOperation == 'xor', "ctx.globalCompositeOperation == 'xor'
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -1600,7 +1611,7 @@ ok(ctx.globalCompositeOperation == 'xor', "ctx.globalCompositeOperation == 'xor'
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -1646,7 +1657,7 @@ ok(ctx.globalCompositeOperation == 'xor', "ctx.globalCompositeOperation == 'xor'
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -3141,6 +3152,7 @@ var canvas = document.getElementById('c119');
var ctx = canvas.getContext('2d');
var _thrown_outer = false;
+
try {
ctx.fillStyle = '#0f0';
@@ -3843,7 +3855,7 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -6072,9 +6084,15 @@ g.addColorStop(0.75, '#0f0');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
-ctx.translate(-50, 0);
-ctx.fillRect(50, 0, 100, 50);
-todo_isPixel(ctx, 25,25, 0,255,0,255, 0);
+ctx.translate(-50, 0);
+ctx.fillRect(50, 0, 100, 50);
+
+if (IsAzureEnabled()) {
+ isPixel(ctx, 25,25, 0,255,0,255, 0);
+} else {
+ todo_isPixel(ctx, 25,25, 0,255,0,255, 0);
+}
+
isPixel(ctx, 50,25, 0,255,0,255, 0);
isPixel(ctx, 75,25, 0,255,0,255, 0);
@@ -6379,18 +6397,30 @@ var g = ctx.createRadialGradient(210, 25, 100, 230, 25, 100);
g.addColorStop(0, '#0f0');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
-ctx.fillRect(0, 0, 100, 50);
-
-isPixel(ctx, 1,1, 0,255,0,255, 0);
-isPixel(ctx, 50,1, 0,255,0,255, 0);
-isPixel(ctx, 98,1, 0,255,0,255, 0);
-isPixel(ctx, 1,25, 0,255,0,255, 0);
-isPixel(ctx, 50,25, 0,255,0,255, 0);
-isPixel(ctx, 98,25, 0,255,0,255, 0);
-isPixel(ctx, 1,48, 0,255,0,255, 0);
-isPixel(ctx, 50,48, 0,255,0,255, 0);
-isPixel(ctx, 98,48, 0,255,0,255, 0);
-
+ctx.fillRect(0, 0, 100, 50);
+
+if (IsAzureEnabled()) {
+ // XXX - See Bug 666097.
+ todo_isPixel(ctx, 1, 1, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 50, 1, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 98, 1, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 1, 25, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 50, 25, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 98, 25, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 1, 48, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 50, 48, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 98, 48, 0, 255, 0, 255, 0);
+} else {
+ isPixel(ctx, 1, 1, 0, 255, 0, 255, 0);
+ isPixel(ctx, 50, 1, 0, 255, 0, 255, 0);
+ isPixel(ctx, 98, 1, 0, 255, 0, 255, 0);
+ isPixel(ctx, 1, 25, 0, 255, 0, 255, 0);
+ isPixel(ctx, 50, 25, 0, 255, 0, 255, 0);
+ isPixel(ctx, 98, 25, 0, 255, 0, 255, 0);
+ isPixel(ctx, 1, 48, 0, 255, 0, 255, 0);
+ isPixel(ctx, 50, 48, 0, 255, 0, 255, 0);
+ isPixel(ctx, 98, 48, 0, 255, 0, 255, 0);
+}
}
@@ -6538,18 +6568,31 @@ var g = ctx.createRadialGradient(230, 25, 100, 100, 25, 101);
g.addColorStop(0, '#f00');
g.addColorStop(1, '#0f0');
ctx.fillStyle = g;
-ctx.fillRect(0, 0, 100, 50);
-
-isPixel(ctx, 1,1, 0,255,0,255, 0);
-isPixel(ctx, 50,1, 0,255,0,255, 0);
-isPixel(ctx, 98,1, 0,255,0,255, 0);
-isPixel(ctx, 1,25, 0,255,0,255, 0);
-isPixel(ctx, 50,25, 0,255,0,255, 0);
-isPixel(ctx, 98,25, 0,255,0,255, 0);
-isPixel(ctx, 1,48, 0,255,0,255, 0);
-isPixel(ctx, 50,48, 0,255,0,255, 0);
-isPixel(ctx, 98,48, 0,255,0,255, 0);
-
+ctx.fillRect(0, 0, 100, 50);
+
+
+if (IsAzureEnabled()) {
+ // XXX - See Bug 666097.
+ todo_isPixel(ctx, 1, 1, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 50, 1, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 98, 1, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 1, 25, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 50, 25, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 98, 25, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 1, 48, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 50, 48, 0, 255, 0, 255, 0);
+ todo_isPixel(ctx, 98, 48, 0, 255, 0, 255, 0);
+} else {
+ isPixel(ctx, 1, 1, 0, 255, 0, 255, 0);
+ isPixel(ctx, 50, 1, 0, 255, 0, 255, 0);
+ isPixel(ctx, 98, 1, 0, 255, 0, 255, 0);
+ isPixel(ctx, 1, 25, 0, 255, 0, 255, 0);
+ isPixel(ctx, 50, 25, 0, 255, 0, 255, 0);
+ isPixel(ctx, 98, 25, 0, 255, 0, 255, 0);
+ isPixel(ctx, 1, 48, 0, 255, 0, 255, 0);
+ isPixel(ctx, 50, 48, 0, 255, 0, 255, 0);
+ isPixel(ctx, 98, 48, 0, 255, 0, 255, 0);
+}
}
@@ -7267,10 +7310,16 @@ ctx.fillRect(0, 0, 100, 50);
ctx.translate(50, 25);
ctx.scale(10, 10);
ctx.fillRect(-5, -2.5, 10, 5);
-todo_isPixel(ctx, 25,25, 0,255,0,255, 0);
-todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
-todo_isPixel(ctx, 75,25, 0,255,0,255, 0);
+if (IsAzureEnabled()) {
+ isPixel(ctx, 25,25, 0,255,0,255, 0);
+ isPixel(ctx, 50,25, 0,255,0,255, 0);
+ isPixel(ctx, 75,25, 0,255,0,255, 0);
+} else {
+ todo_isPixel(ctx, 25,25, 0,255,0,255, 0);
+ todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
+ todo_isPixel(ctx, 75,25, 0,255,0,255, 0);
+}
}
@@ -7420,7 +7469,7 @@ ok(imgdata3.data.length == imgdata4.data.length, "imgdata3.data.length == imgdat
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -7536,7 +7585,7 @@ ok(isTransparentBlack, "isTransparentBlack");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -7568,7 +7617,7 @@ ok(imgdata.data.thisImplementsCanvasPixelArray, "imgdata.data.thisImplementsCanv
} catch (e) {
_thrown_outer = true;
}
-todo(!_thrown_outer, 'should not throw exception');
+todo(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -7600,7 +7649,7 @@ todo(imgdata.data.thisImplementsCanvasPixelArray, "imgdata.data.thisImplementsCa
} catch (e) {
_thrown_outer = true;
}
-todo(!_thrown_outer, 'should not throw exception');
+todo(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -8039,7 +8088,7 @@ ok(imgdata2.data[3] === 0, "imgdata2.data[\""+(3)+"\"] === 0");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -8124,7 +8173,7 @@ ok(imgdata7.data[20*4+3] === 0, "imgdata7.data[20*4+3] === 0");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -8174,7 +8223,7 @@ ok(imgdata.height == 1, "imgdata.height == 1");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -8206,7 +8255,7 @@ ok(imgdata.data.thisImplementsCanvasPixelArray, "imgdata.data.thisImplementsCanv
} catch (e) {
_thrown_outer = true;
}
-todo(!_thrown_outer, 'should not throw exception');
+todo(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -8699,7 +8748,7 @@ isPixel(ctx, 50,45, 0,255,0,255, 2);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -8745,7 +8794,7 @@ isPixel(ctx, 1,45, 0,255,0,255, 2);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -8789,7 +8838,7 @@ isPixel(ctx, 50,45, 0,255,0,255, 2);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -8833,7 +8882,7 @@ isPixel(ctx, 50,45, 0,255,0,255, 2);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -9433,7 +9482,7 @@ ok(ctx.lineCap === 'butt', "ctx.lineCap === 'butt'");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -9807,7 +9856,7 @@ ok(ctx.lineJoin === 'bevel', "ctx.lineJoin === 'bevel'");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -10138,7 +10187,7 @@ ok(ctx.miterLimit === 1.5, "ctx.miterLimit === 1.5");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -10428,7 +10477,7 @@ ok(ctx.lineWidth === 1.5, "ctx.lineWidth === 1.5");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -11100,7 +11149,7 @@ isPixel(ctx, 90,45, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -11582,7 +11631,12 @@ ctx.beginPath();
ctx.moveTo(0, 25);
ctx.arc(200, 25, 0, 0, Math.PI, true);
ctx.stroke();
-todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
+
+if (IsAzureEnabled()) {
+ isPixel(ctx, 50,25, 0,255,0,255, 0);
+} else {
+ todo_isPixel(ctx, 50,25, 0,255,0,255, 0);
+}
}
@@ -11880,7 +11934,7 @@ isPixel(ctx, 90,45, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -12170,7 +12224,7 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -12213,7 +12267,7 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -12393,7 +12447,7 @@ isPixel(ctx, 90,45, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -13140,22 +13194,22 @@ ok(ctx.isPointInPath(70, 30) === false, "ctx.isPointInPath(70, 30) === false");
function test_2d_path_isPointInPath_edge() {
var canvas = document.getElementById('c404');
-var ctx = canvas.getContext('2d');
-
-ctx.rect(0, 0, 20, 20);
-ok(ctx.isPointInPath(0, 0) === true, "ctx.isPointInPath(0, 0) === true");
-ok(ctx.isPointInPath(10, 0) === true, "ctx.isPointInPath(10, 0) === true");
-ok(ctx.isPointInPath(20, 0) === true, "ctx.isPointInPath(20, 0) === true");
-ok(ctx.isPointInPath(20, 10) === true, "ctx.isPointInPath(20, 10) === true");
-ok(ctx.isPointInPath(20, 20) === true, "ctx.isPointInPath(20, 20) === true");
-ok(ctx.isPointInPath(10, 20) === true, "ctx.isPointInPath(10, 20) === true");
-ok(ctx.isPointInPath(0, 20) === true, "ctx.isPointInPath(0, 20) === true");
-ok(ctx.isPointInPath(0, 10) === true, "ctx.isPointInPath(0, 10) === true");
-ok(ctx.isPointInPath(10, -0.01) === false, "ctx.isPointInPath(10, -0.01) === false");
-ok(ctx.isPointInPath(10, 20.01) === false, "ctx.isPointInPath(10, 20.01) === false");
-ok(ctx.isPointInPath(-0.01, 10) === false, "ctx.isPointInPath(-0.01, 10) === false");
-ok(ctx.isPointInPath(20.01, 10) === false, "ctx.isPointInPath(20.01, 10) === false");
-
+var ctx = canvas.getContext('2d');
+
+ctx.rect(0, 0, 20, 20);
+
+ok(ctx.isPointInPath(0, 0) === true, "ctx.isPointInPath(0, 0) === true");
+ok(ctx.isPointInPath(10, 0) === true, "ctx.isPointInPath(10, 0) === true");
+ok(ctx.isPointInPath(20, 0) === true, "ctx.isPointInPath(20, 0) === true");
+ok(ctx.isPointInPath(20, 10) === true, "ctx.isPointInPath(20, 10) === true");
+ok(ctx.isPointInPath(20, 20) === true, "ctx.isPointInPath(20, 20) === true");
+ok(ctx.isPointInPath(10, 20) === true, "ctx.isPointInPath(10, 20) === true");
+ok(ctx.isPointInPath(0, 20) === true, "ctx.isPointInPath(0, 20) === true");
+ok(ctx.isPointInPath(0, 10) === true, "ctx.isPointInPath(0, 10) === true");
+ok(ctx.isPointInPath(10, -0.01) === false, "ctx.isPointInPath(10, -0.01) === false");
+ok(ctx.isPointInPath(10, 20.01) === false, "ctx.isPointInPath(10, 20.01) === false");
+ok(ctx.isPointInPath(-0.01, 10) === false, "ctx.isPointInPath(-0.01, 10) === false");
+ok(ctx.isPointInPath(20.01, 10) === false, "ctx.isPointInPath(20.01, 10) === false");
}
@@ -13205,7 +13259,7 @@ ok(ctx.isPointInPath(NaN, NaN) === false, "ctx.isPointInPath(NaN, NaN) === false
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -13506,7 +13560,7 @@ isPixel(ctx, 90,45, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -13621,7 +13675,7 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -13727,7 +13781,7 @@ isPixel(ctx, 90,45, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -13991,7 +14045,7 @@ isPixel(ctx, 90,45, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -14943,30 +14997,41 @@ ok(pattern.thisImplementsCanvasPattern, "pattern.thisImplementsCanvasPattern");
function test_2d_pattern_basic_zerocanvas() {
-var canvas = document.getElementById('c463');
-var ctx = canvas.getContext('2d');
+var _thrown_outer = false;
-var canvas2 = document.createElement('canvas');
-canvas2.width = 0;
-canvas2.height = 0;
-ok(canvas2.width === 0, "canvas2.width === 0");
-ok(canvas2.height === 0, "canvas2.height === 0");
-var ctx2 = canvas2.getContext('2d');
-ctx2.fillStyle = '#f00';
-ctx2.fillRect(0, 0, 100, 50);
-var pattern = ctx.createPattern(canvas2, 'repeat');
+try {
+ var canvas = document.getElementById('c463');
+ var ctx = canvas.getContext('2d');
-ctx.fillStyle = '#0f0';
-ctx.fillRect(0, 0, 100, 50);
-ctx.fillStyle = '#f00';
-ctx.fillStyle = pattern;
-ctx.fillRect(0, 0, 100, 50);
+ var canvas2 = document.createElement('canvas');
+ canvas2.width = 0;
+ canvas2.height = 0;
+ ok(canvas2.width === 0, "canvas2.width === 0");
+ ok(canvas2.height === 0, "canvas2.height === 0");
+ var ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = '#f00';
+ ctx2.fillRect(0, 0, 100, 50);
+ var pattern = ctx.createPattern(canvas2, 'repeat');
-isPixel(ctx, 1,1, 0,255,0,255, 0);
-isPixel(ctx, 98,1, 0,255,0,255, 0);
-isPixel(ctx, 1,48, 0,255,0,255, 0);
-isPixel(ctx, 98,48, 0,255,0,255, 0);
+ ctx.fillStyle = '#0f0';
+ ctx.fillRect(0, 0, 100, 50);
+ ctx.fillStyle = '#f00';
+ ctx.fillStyle = pattern;
+ ctx.fillRect(0, 0, 100, 50);
+ isPixel(ctx, 1,1, 0,255,0,255, 0);
+ isPixel(ctx, 98,1, 0,255,0,255, 0);
+ isPixel(ctx, 1,48, 0,255,0,255, 0);
+ isPixel(ctx, 98,48, 0,255,0,255, 0);
+} catch(e) {
+ _thrown_outer = true;
+}
+
+if (IsAzureEnabled()) {
+ ok(_thrown_outer);
+} else {
+ todo(_thrown_outer, ctx.canvas.is + ' should throw exception INVALID_STATE_ERR');
+}
}
@@ -15594,11 +15659,17 @@ ctx.fillRect(0, 0, 100, 50);
ctx.translate(-128, -78);
ctx.fillRect(128, 78, 100, 50);
-todo_isPixel(ctx, 1,1, 0,255,0,255, 0);
-todo_isPixel(ctx, 98,1, 0,255,0,255, 0);
-todo_isPixel(ctx, 1,48, 0,255,0,255, 0);
-todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
-
+if (IsAzureEnabled()) {
+ isPixel(ctx, 1,1, 0,255,0,255, 0);
+ isPixel(ctx, 98,1, 0,255,0,255, 0);
+ isPixel(ctx, 1,48, 0,255,0,255, 0);
+ isPixel(ctx, 98,48, 0,255,0,255, 0);
+} else {
+ todo_isPixel(ctx, 1,1, 0,255,0,255, 0);
+ todo_isPixel(ctx, 98,1, 0,255,0,255, 0);
+ todo_isPixel(ctx, 1,48, 0,255,0,255, 0);
+ todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
+}
}
@@ -15692,12 +15763,20 @@ ctx.fillRect(0, -16, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 16);
-todo_isPixel(ctx, 1,1, 0,255,0,255, 0);
-todo_isPixel(ctx, 98,1, 0,255,0,255, 0);
+if (IsAzureEnabled()) {
+ isPixel(ctx, 1,1, 0,255,0,255, 0);
+ isPixel(ctx, 98,1, 0,255,0,255, 0);
+ isPixel(ctx, 1,48, 0,255,0,255, 0);
+ isPixel(ctx, 98,48, 0,255,0,255, 0);
+} else {
+ todo_isPixel(ctx, 1,1, 0,255,0,255, 0);
+ todo_isPixel(ctx, 98,1, 0,255,0,255, 0);
+ todo_isPixel(ctx, 1,48, 0,255,0,255, 0);
+ todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
+}
+
isPixel(ctx, 1,25, 0,255,0,255, 0);
isPixel(ctx, 98,25, 0,255,0,255, 0);
-todo_isPixel(ctx, 1,48, 0,255,0,255, 0);
-todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
}
@@ -15730,8 +15809,14 @@ ctx.fillRect(0, 0, 100, 16);
isPixel(ctx, 1,1, 0,255,0,255, 0);
isPixel(ctx, 98,1, 0,255,0,255, 0);
-todo_isPixel(ctx, 1,48, 0,255,0,255, 0);
-todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
+
+if (IsAzureEnabled()) {
+ isPixel(ctx, 1,48, 0,255,0,255, 0);
+ isPixel(ctx, 98,48, 0,255,0,255, 0);
+} else {
+ todo_isPixel(ctx, 1,48, 0,255,0,255, 0);
+ todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
+}
}
@@ -15795,12 +15880,20 @@ ctx.fillRect(-48, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 16, 50);
-todo_isPixel(ctx, 1,1, 0,255,0,255, 0);
isPixel(ctx, 50,1, 0,255,0,255, 0);
-todo_isPixel(ctx, 98,1, 0,255,0,255, 0);
-todo_isPixel(ctx, 1,48, 0,255,0,255, 0);
isPixel(ctx, 50,48, 0,255,0,255, 0);
-todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
+
+if (IsAzureEnabled()) {
+ isPixel(ctx, 1,1, 0,255,0,255, 0);
+ isPixel(ctx, 98,1, 0,255,0,255, 0);
+ isPixel(ctx, 1,48, 0,255,0,255, 0);
+ isPixel(ctx, 98,48, 0,255,0,255, 0);
+} else {
+ todo_isPixel(ctx, 1,1, 0,255,0,255, 0);
+ todo_isPixel(ctx, 98,1, 0,255,0,255, 0);
+ todo_isPixel(ctx, 1,48, 0,255,0,255, 0);
+ todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
+}
}
@@ -15832,10 +15925,15 @@ ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 16, 50);
isPixel(ctx, 1,1, 0,255,0,255, 0);
-todo_isPixel(ctx, 98,1, 0,255,0,255, 0);
isPixel(ctx, 1,48, 0,255,0,255, 0);
-todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
+if (IsAzureEnabled()) {
+ isPixel(ctx, 98,1, 0,255,0,255, 0);
+ isPixel(ctx, 98,48, 0,255,0,255, 0);
+} else {
+ todo_isPixel(ctx, 98,1, 0,255,0,255, 0);
+ todo_isPixel(ctx, 98,48, 0,255,0,255, 0);
+}
}
@@ -16642,10 +16740,9 @@ ctx.globalCompositeOperation = 'xor';
ctx.shadowColor = '#f00';
ctx.shadowOffsetX = 100;
ctx.fillStyle = '#0f0';
-ctx.fillRect(-100, 0, 200, 50);
-
-isPixel(ctx, 50,25, 0,255,0,255, 2);
-
+ctx.fillRect(-100, 0, 200, 50);
+
+isPixel(ctx, 50, 25, 255, 255, 0, 255, 2);
}
@@ -16669,10 +16766,9 @@ ctx.globalCompositeOperation = 'xor';
ctx.shadowColor = '#f00';
ctx.shadowBlur = 1;
ctx.fillStyle = '#0f0';
-ctx.fillRect(-10, -10, 120, 70);
-
-isPixel(ctx, 50,25, 0,255,0,255, 2);
-
+ctx.fillRect(-10, -10, 120, 70);
+
+isPixel(ctx, 50, 25, 255, 255, 0, 255, 2);
}
@@ -18263,7 +18359,7 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -18566,7 +18662,7 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -18825,7 +18921,7 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -18995,7 +19091,7 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -19202,7 +19298,7 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -19316,7 +19412,7 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -19613,7 +19709,7 @@ ok(canvas.getContext('2D') === null, "canvas.getContext('2D') === null");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -19639,7 +19735,7 @@ ok(canvas.getContext("") === null, "canvas.getContext(\"\") === null");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -19665,7 +19761,7 @@ ok(canvas.getContext('This is not an implemented context in any real browser') =
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -19691,7 +19787,7 @@ ok(canvas.getContext("2d#") === null, "canvas.getContext(\"2d#\") === null");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -19717,7 +19813,7 @@ ok(canvas.getContext("2d\0") === null, "canvas.getContext(\"2d\\0\") === null");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -19743,7 +19839,7 @@ ok(canvas.getContext("2\uFF44") === null, "canvas.getContext(\"2\\uFF44\") === n
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -20763,7 +20859,7 @@ ok(/^data:image\/png[;,]/.test(data), "data =~ /^data:image\\/png[;,]/");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -20790,7 +20886,7 @@ ok(/^data:image\/png[;,]/.test(data), "data =~ /^data:image\\/png[;,]/");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -20818,7 +20914,7 @@ ok(/^data:image\/png[;,]/.test(data), "data =~ /^data:image\\/png[;,]/");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -20904,7 +21000,7 @@ ok(/^data:image\/png[;,]/.test(data), "data =~ /^data:image\\/png[;,]/");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -21012,7 +21108,7 @@ ok(/^data:image\/png[;,]/.test(data), "data =~ /^data:image\\/png[;,]/");
} catch (e) {
_thrown_outer = true;
}
-ok(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, ctx.canvas.id + ' should not throw exception');
}
@@ -21407,14 +21503,10 @@ function runTests() {
//test_2d_path_rect_zero_6(); // This test is bogus according to the spec; see bug 407107
- // These tests are bogus according to the spec: shadows should not be
- // drawn if shadowBlur, shadowOffsetX, and shadowOffsetY are all zero, whic
- // they are in these tests
+ // The following tests are disabled due to pending changes in the spec:
+ //
//test_2d_shadow_composite_3();
//test_2d_shadow_composite_4();
- // Shadows should not be drawn if the operator is not source-over (the spec doesn't
- // say this yet, but it should be changed). These tests assume shadows will
- // be drawn with operators other than source-over:
//test_2d_shadow_composite_1();
//test_2d_shadow_composite_2();
try {
diff --git a/content/html/content/src/nsHTMLCanvasElement.cpp b/content/html/content/src/nsHTMLCanvasElement.cpp
index ed47529dc69..e50a96a182f 100644
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -48,6 +48,7 @@
#include "jsapi.h"
#include "nsJSUtils.h"
#include "nsMathUtils.h"
+#include "mozilla/Preferences.h"
#include "nsFrameManager.h"
#include "nsDisplayList.h"
@@ -722,3 +723,23 @@ nsHTMLCanvasElement::RenderContextsExternal(gfxContext *aContext, gfxPattern::Gr
return mCurrentContext->Render(aContext, aFilter);
}
+
+nsresult NS_NewCanvasRenderingContext2DThebes(nsIDOMCanvasRenderingContext2D** aResult);
+nsresult NS_NewCanvasRenderingContext2DAzure(nsIDOMCanvasRenderingContext2D** aResult);
+
+nsresult
+NS_NewCanvasRenderingContext2D(nsIDOMCanvasRenderingContext2D** aResult)
+{
+ PRBool azure = PR_FALSE;
+ nsresult rv = Preferences::GetBool("gfx.canvas.azure.enabled", &azure);
+
+ if (azure) {
+ nsresult rv = NS_NewCanvasRenderingContext2DAzure(aResult);
+ // If Azure fails, fall back to a classic canvas.
+ if (NS_SUCCEEDED(rv)) {
+ return rv;
+ }
+ }
+
+ return NS_NewCanvasRenderingContext2DThebes(aResult);
+}
diff --git a/content/mathml/content/src/nsMathMLElement.cpp b/content/mathml/content/src/nsMathMLElement.cpp
index 30b130391fe..261fa83b22a 100644
--- a/content/mathml/content/src/nsMathMLElement.cpp
+++ b/content/mathml/content/src/nsMathMLElement.cpp
@@ -62,6 +62,8 @@ NS_INTERFACE_TABLE_HEAD(nsMathMLElement)
NS_NODE_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsMathMLElement)
NS_INTERFACE_TABLE_ENTRY(nsMathMLElement, nsIDOMNode)
NS_INTERFACE_TABLE_ENTRY(nsMathMLElement, nsIDOMElement)
+ NS_INTERFACE_TABLE_ENTRY(nsMathMLElement, nsILink)
+ NS_INTERFACE_TABLE_ENTRY(nsMathMLElement, Link)
NS_OFFSET_AND_INTERFACE_TABLE_END
NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MathMLElement)
@@ -77,6 +79,8 @@ nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
{
static const char kMathMLStyleSheetURI[] = "resource://gre-resources/mathml.css";
+ Link::ResetLinkState(false);
+
nsresult rv = nsMathMLElementBase::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
@@ -101,6 +105,16 @@ nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
return rv;
}
+void
+nsMathMLElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
+{
+ // If this link is ever reinserted into a document, it might
+ // be under a different xml:base, so forget the cached state now.
+ Link::ResetLinkState(false);
+
+ nsMathMLElementBase::UnbindFromTree(aDeep, aNullParent);
+}
+
PRBool
nsMathMLElement::ParseAttribute(PRInt32 aNamespaceID,
nsIAtom* aAttribute,
@@ -438,12 +452,27 @@ nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes,
}
}
+nsresult
+nsMathMLElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
+{
+ nsresult rv = nsGenericElement::PreHandleEvent(aVisitor);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return PreHandleEventForLinks(aVisitor);
+}
+
+nsresult
+nsMathMLElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
+{
+ return PostHandleEventForLinks(aVisitor);
+}
+
NS_IMPL_ELEMENT_CLONE(nsMathMLElement)
nsEventStates
nsMathMLElement::IntrinsicState() const
{
- return nsMathMLElementBase::IntrinsicState() |
+ return Link::LinkState() | nsMathMLElementBase::IntrinsicState() |
(mIncrementScriptLevel ? NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL : nsEventStates());
}
@@ -465,3 +494,185 @@ nsMathMLElement::SetIncrementScriptLevel(PRBool aIncrementScriptLevel,
UpdateState(true);
}
+
+PRBool
+nsMathMLElement::IsFocusable(PRInt32 *aTabIndex, PRBool aWithMouse)
+{
+ nsCOMPtr uri;
+ if (IsLink(getter_AddRefs(uri))) {
+ if (aTabIndex) {
+ *aTabIndex = ((sTabFocusModel & eTabFocus_linksMask) == 0 ? -1 : 0);
+ }
+ return PR_TRUE;
+ }
+
+ if (aTabIndex) {
+ *aTabIndex = -1;
+ }
+
+ return PR_FALSE;
+}
+
+PRBool
+nsMathMLElement::IsLink(nsIURI** aURI) const
+{
+ // http://www.w3.org/TR/2010/REC-MathML3-20101021/chapter6.html#interf.link
+ // The REC says that the following elements should not be linking elements:
+ nsIAtom* tag = Tag();
+ if (tag == nsGkAtoms::mprescripts_ ||
+ tag == nsGkAtoms::none ||
+ tag == nsGkAtoms::malignmark_ ||
+ tag == nsGkAtoms::maligngroup_) {
+ *aURI = nsnull;
+ return PR_FALSE;
+ }
+
+ PRBool hasHref = PR_FALSE;
+ const nsAttrValue* href = mAttrsAndChildren.GetAttr(nsGkAtoms::href,
+ kNameSpaceID_None);
+ if (href) {
+ // MathML href
+ // The REC says: "When user agents encounter MathML elements with both href
+ // and xlink:href attributes, the href attribute should take precedence."
+ hasHref = PR_TRUE;
+ } else {
+ // To be a clickable XLink for styling and interaction purposes, we require:
+ //
+ // xlink:href - must be set
+ // xlink:type - must be unset or set to "" or set to "simple"
+ // xlink:show - must be unset or set to "", "new" or "replace"
+ // xlink:actuate - must be unset or set to "" or "onRequest"
+ //
+ // For any other values, we're either not a *clickable* XLink, or the end
+ // result is poorly specified. Either way, we return PR_FALSE.
+
+ static nsIContent::AttrValuesArray sTypeVals[] =
+ { &nsGkAtoms::_empty, &nsGkAtoms::simple, nsnull };
+
+ static nsIContent::AttrValuesArray sShowVals[] =
+ { &nsGkAtoms::_empty, &nsGkAtoms::_new, &nsGkAtoms::replace, nsnull };
+
+ static nsIContent::AttrValuesArray sActuateVals[] =
+ { &nsGkAtoms::_empty, &nsGkAtoms::onRequest, nsnull };
+
+ // Optimization: check for href first for early return
+ href = mAttrsAndChildren.GetAttr(nsGkAtoms::href,
+ kNameSpaceID_XLink);
+ if (href &&
+ FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::type,
+ sTypeVals, eCaseMatters) !=
+ nsIContent::ATTR_VALUE_NO_MATCH &&
+ FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
+ sShowVals, eCaseMatters) !=
+ nsIContent::ATTR_VALUE_NO_MATCH &&
+ FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::actuate,
+ sActuateVals, eCaseMatters) !=
+ nsIContent::ATTR_VALUE_NO_MATCH) {
+ hasHref = PR_TRUE;
+ }
+ }
+
+ if (hasHref) {
+ nsCOMPtr baseURI = GetBaseURI();
+ // Get absolute URI
+ nsAutoString hrefStr;
+ href->ToString(hrefStr);
+ nsContentUtils::NewURIWithDocumentCharset(aURI, hrefStr,
+ GetOwnerDoc(), baseURI);
+ // must promise out param is non-null if we return true
+ return !!*aURI;
+ }
+
+ *aURI = nsnull;
+ return PR_FALSE;
+}
+
+void
+nsMathMLElement::GetLinkTarget(nsAString& aTarget)
+{
+ const nsAttrValue* target = mAttrsAndChildren.GetAttr(nsGkAtoms::target,
+ kNameSpaceID_XLink);
+ if (target) {
+ target->ToString(aTarget);
+ }
+
+ if (aTarget.IsEmpty()) {
+
+ static nsIContent::AttrValuesArray sShowVals[] =
+ { &nsGkAtoms::_new, &nsGkAtoms::replace, nsnull };
+
+ switch (FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
+ sShowVals, eCaseMatters)) {
+ case 0:
+ aTarget.AssignLiteral("_blank");
+ return;
+ case 1:
+ return;
+ }
+ nsIDocument* ownerDoc = GetOwnerDoc();
+ if (ownerDoc) {
+ ownerDoc->GetBaseTarget(aTarget);
+ }
+ }
+}
+
+nsLinkState
+nsMathMLElement::GetLinkState() const
+{
+ return Link::GetLinkState();
+}
+
+void
+nsMathMLElement::RequestLinkStateUpdate()
+{
+ UpdateLinkState(Link::LinkState());
+}
+
+already_AddRefed
+nsMathMLElement::GetHrefURI() const
+{
+ nsCOMPtr hrefURI;
+ return IsLink(getter_AddRefs(hrefURI)) ? hrefURI.forget() : nsnull;
+}
+
+nsresult
+nsMathMLElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+ nsIAtom* aPrefix, const nsAString& aValue,
+ PRBool aNotify)
+{
+ nsresult rv = nsMathMLElementBase::SetAttr(aNameSpaceID, aName, aPrefix,
+ aValue, aNotify);
+
+ // The ordering of the parent class's SetAttr call and Link::ResetLinkState
+ // is important here! The attribute is not set until SetAttr returns, and
+ // we will need the updated attribute value because notifying the document
+ // that content states have changed will call IntrinsicState, which will try
+ // to get updated information about the visitedness from Link.
+ if (aName == nsGkAtoms::href &&
+ (aNameSpaceID == kNameSpaceID_None ||
+ aNameSpaceID == kNameSpaceID_XLink)) {
+ Link::ResetLinkState(!!aNotify);
+ }
+
+ return rv;
+}
+
+nsresult
+nsMathMLElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttr,
+ PRBool aNotify)
+{
+ nsresult rv = nsMathMLElementBase::UnsetAttr(aNameSpaceID, aAttr, aNotify);
+
+ // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
+ // is important here! The attribute is not unset until UnsetAttr returns, and
+ // we will need the updated attribute value because notifying the document
+ // that content states have changed will call IntrinsicState, which will try
+ // to get updated information about the visitedness from Link.
+ if (aAttr == nsGkAtoms::href &&
+ (aNameSpaceID == kNameSpaceID_None ||
+ aNameSpaceID == kNameSpaceID_XLink)) {
+ Link::ResetLinkState(!!aNotify);
+ }
+
+ return rv;
+}
diff --git a/content/mathml/content/src/nsMathMLElement.h b/content/mathml/content/src/nsMathMLElement.h
index 92058f4da12..3e6dd56ba72 100644
--- a/content/mathml/content/src/nsMathMLElement.h
+++ b/content/mathml/content/src/nsMathMLElement.h
@@ -42,6 +42,8 @@
#include "nsMappedAttributeElement.h"
#include "nsIDOMElement.h"
+#include "nsILink.h"
+#include "Link.h"
class nsCSSValue;
@@ -50,12 +52,15 @@ typedef nsMappedAttributeElement nsMathMLElementBase;
/*
* The base class for MathML elements.
*/
-class nsMathMLElement : public nsMathMLElementBase
- , public nsIDOMElement
+class nsMathMLElement : public nsMathMLElementBase,
+ public nsIDOMElement,
+ public nsILink,
+ public mozilla::dom::Link
{
public:
nsMathMLElement(already_AddRefed aNodeInfo)
- : nsMathMLElementBase(aNodeInfo), mIncrementScriptLevel(PR_FALSE)
+ : nsMathMLElementBase(aNodeInfo), Link(this),
+ mIncrementScriptLevel(PR_FALSE)
{}
// Implementation of nsISupports is inherited from nsMathMLElementBase
@@ -69,6 +74,8 @@ public:
nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
PRBool aCompileEventHandlers);
+ virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
+ PRBool aNullParent = PR_TRUE);
virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
nsIAtom* aAttribute,
@@ -89,6 +96,8 @@ public:
static void MapMathMLAttributesInto(const nsMappedAttributes* aAttributes,
nsRuleData* aRuleData);
+ virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
+ virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
nsresult Clone(nsINodeInfo*, nsINode**) const;
virtual nsEventStates IntrinsicState() const;
virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
@@ -100,6 +109,26 @@ public:
return mIncrementScriptLevel;
}
+ NS_IMETHOD LinkAdded() { return NS_OK; }
+ NS_IMETHOD LinkRemoved() { return NS_OK; }
+ virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull,
+ PRBool aWithMouse = PR_FALSE);
+ virtual PRBool IsLink(nsIURI** aURI) const;
+ virtual void GetLinkTarget(nsAString& aTarget);
+ virtual nsLinkState GetLinkState() const;
+ virtual void RequestLinkStateUpdate();
+ virtual already_AddRefed GetHrefURI() const;
+ nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+ const nsAString& aValue, PRBool aNotify)
+ {
+ return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
+ }
+ virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+ nsIAtom* aPrefix, const nsAString& aValue,
+ PRBool aNotify);
+ virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
+ PRBool aNotify);
+
virtual nsXPCClassInfo* GetClassInfo();
private:
PRPackedBool mIncrementScriptLevel;
diff --git a/content/xbl/src/nsXBLService.cpp b/content/xbl/src/nsXBLService.cpp
index 17c1a1fb41c..9bc997ab559 100644
--- a/content/xbl/src/nsXBLService.cpp
+++ b/content/xbl/src/nsXBLService.cpp
@@ -87,7 +87,7 @@
#ifdef MOZ_XUL
#include "nsXULPrototypeCache.h"
#endif
-#include "nsIDOMLoadListener.h"
+#include "nsIDOMEventListener.h"
#include "mozilla/Preferences.h"
using namespace mozilla;
@@ -272,19 +272,13 @@ int nsXBLBindingRequest::gRefCnt = 0;
// nsXBLStreamListener, a helper class used for
// asynchronous parsing of URLs
/* Header file */
-class nsXBLStreamListener : public nsIStreamListener, public nsIDOMLoadListener
+class nsXBLStreamListener : public nsIStreamListener, public nsIDOMEventListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
-
- NS_IMETHOD Load(nsIDOMEvent* aEvent);
- NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent) { return NS_OK; }
- NS_IMETHOD Unload(nsIDOMEvent* aEvent) { return NS_OK; }
- NS_IMETHOD Abort(nsIDOMEvent* aEvent) { return NS_OK; }
- NS_IMETHOD Error(nsIDOMEvent* aEvent) { return NS_OK; }
- NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; }
+ NS_DECL_NSIDOMEVENTLISTENER
nsXBLStreamListener(nsXBLService* aXBLService,
nsIDocument* aBoundDocument,
@@ -307,7 +301,10 @@ private:
};
/* Implementation file */
-NS_IMPL_ISUPPORTS4(nsXBLStreamListener, nsIStreamListener, nsIRequestObserver, nsIDOMLoadListener, nsIDOMEventListener)
+NS_IMPL_ISUPPORTS3(nsXBLStreamListener,
+ nsIStreamListener,
+ nsIRequestObserver,
+ nsIDOMEventListener)
nsXBLStreamListener::nsXBLStreamListener(nsXBLService* aXBLService,
nsIDocument* aBoundDocument,
@@ -401,7 +398,7 @@ nsXBLStreamListener::HasRequest(nsIURI* aURI, nsIContent* aElt)
}
nsresult
-nsXBLStreamListener::Load(nsIDOMEvent* aEvent)
+nsXBLStreamListener::HandleEvent(nsIDOMEvent* aEvent)
{
nsresult rv = NS_OK;
PRUint32 i;
@@ -481,7 +478,7 @@ nsXBLStreamListener::Load(nsIDOMEvent* aEvent)
}
}
- target->RemoveEventListener(NS_LITERAL_STRING("load"), (nsIDOMLoadListener*)this, PR_FALSE);
+ target->RemoveEventListener(NS_LITERAL_STRING("load"), this, PR_FALSE);
return rv;
}
diff --git a/content/xbl/src/nsXBLWindowKeyHandler.cpp b/content/xbl/src/nsXBLWindowKeyHandler.cpp
index 317bc513d7d..868a7381895 100644
--- a/content/xbl/src/nsXBLWindowKeyHandler.cpp
+++ b/content/xbl/src/nsXBLWindowKeyHandler.cpp
@@ -201,8 +201,7 @@ nsXBLWindowKeyHandler::~nsXBLWindowKeyHandler()
}
}
-NS_IMPL_ISUPPORTS2(nsXBLWindowKeyHandler,
- nsIDOMKeyListener,
+NS_IMPL_ISUPPORTS1(nsXBLWindowKeyHandler,
nsIDOMEventListener)
static void
@@ -393,28 +392,20 @@ nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventTy
return NS_OK;
}
-nsresult nsXBLWindowKeyHandler::KeyUp(nsIDOMEvent* aEvent)
+NS_IMETHODIMP
+nsXBLWindowKeyHandler::HandleEvent(nsIDOMEvent* aEvent)
{
nsCOMPtr keyEvent(do_QueryInterface(aEvent));
NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
- return WalkHandlers(keyEvent, nsGkAtoms::keyup);
-}
-nsresult nsXBLWindowKeyHandler::KeyDown(nsIDOMEvent* aEvent)
-{
- nsCOMPtr keyEvent(do_QueryInterface(aEvent));
- NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
- return WalkHandlers(keyEvent, nsGkAtoms::keydown);
-}
+ nsAutoString eventType;
+ aEvent->GetType(eventType);
+ nsCOMPtr eventTypeAtom = do_GetAtom(eventType);
+ NS_ENSURE_TRUE(eventTypeAtom, NS_ERROR_OUT_OF_MEMORY);
-nsresult nsXBLWindowKeyHandler::KeyPress(nsIDOMEvent* aEvent)
-{
- nsCOMPtr keyEvent(do_QueryInterface(aEvent));
- NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
- return WalkHandlers(keyEvent, nsGkAtoms::keypress);
+ return WalkHandlers(keyEvent, eventTypeAtom);
}
-
//
// EventMatched
//
diff --git a/content/xbl/src/nsXBLWindowKeyHandler.h b/content/xbl/src/nsXBLWindowKeyHandler.h
index b4a33a1a4be..820a704506c 100644
--- a/content/xbl/src/nsXBLWindowKeyHandler.h
+++ b/content/xbl/src/nsXBLWindowKeyHandler.h
@@ -41,7 +41,7 @@
#define nsXBLWindowKeyHandler_h__
#include "nsWeakPtr.h"
-#include "nsIDOMKeyListener.h"
+#include "nsIDOMEventListener.h"
class nsIAtom;
class nsIDOMElement;
@@ -52,23 +52,14 @@ class nsIXBLDocumentInfo;
class nsXBLSpecialDocInfo;
class nsXBLPrototypeHandler;
-class nsXBLWindowKeyHandler : public nsIDOMKeyListener
+class nsXBLWindowKeyHandler : public nsIDOMEventListener
{
public:
nsXBLWindowKeyHandler(nsIDOMElement* aElement, nsIDOMEventTarget* aTarget);
virtual ~nsXBLWindowKeyHandler();
-
- // nsIDOMetc.
- NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent)
- {
- return NS_OK;
- }
- NS_IMETHOD KeyUp(nsIDOMEvent* aKeyEvent);
- NS_IMETHOD KeyDown(nsIDOMEvent* aKeyEvent);
- NS_IMETHOD KeyPress(nsIDOMEvent* aKeyEvent);
-
NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMEVENTLISTENER
// release globals
static NS_HIDDEN_(void) ShutDown();
diff --git a/docshell/base/nsDefaultURIFixup.cpp b/docshell/base/nsDefaultURIFixup.cpp
index e46cc81f911..0b43f8028f4 100644
--- a/docshell/base/nsDefaultURIFixup.cpp
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -44,8 +44,6 @@
#include "nsEscape.h"
#include "nsCRT.h"
-#include "nsIPrefService.h"
-#include "nsIPrefLocalizedString.h"
#include "nsIPlatformCharset.h"
#include "nsILocalFile.h"
@@ -55,6 +53,9 @@
#include "nsIURIFixup.h"
#include "nsDefaultURIFixup.h"
+#include "mozilla/Preferences.h"
+
+using namespace mozilla;
/* Implementation file */
NS_IMPL_ISUPPORTS1(nsDefaultURIFixup, nsIURIFixup)
@@ -62,9 +63,6 @@ NS_IMPL_ISUPPORTS1(nsDefaultURIFixup, nsIURIFixup)
nsDefaultURIFixup::nsDefaultURIFixup()
{
/* member initializers and constructor code */
-
- // Try and get the pref service
- mPrefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
}
@@ -134,13 +132,10 @@ nsDefaultURIFixup::CreateExposableURI(nsIURI *aURI, nsIURI **aReturn)
}
// hide user:pass unless overridden by pref
- PRBool hideUserPass = PR_TRUE;
- if (mPrefBranch)
+ if (Preferences::GetBool("browser.fixup.hide_user_pass", PR_TRUE))
{
- mPrefBranch->GetBoolPref("browser.fixup.hide_user_pass", &hideUserPass);
- }
- if (hideUserPass)
uri->SetUserPass(EmptyCString());
+ }
// return the fixed-up URI
*aReturn = uri;
@@ -274,10 +269,8 @@ nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI, PRUint32 aFixupF
// Test whether keywords need to be fixed up
PRBool fixupKeywords = PR_FALSE;
if (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP) {
- if (mPrefBranch)
- {
- NS_ENSURE_SUCCESS(mPrefBranch->GetBoolPref("keyword.enabled", &fixupKeywords), NS_ERROR_FAILURE);
- }
+ nsresult rv = Preferences::GetBool("keyword.enabled", &fixupKeywords);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
if (fixupKeywords)
{
KeywordURIFixup(uriString, aURI);
@@ -358,7 +351,7 @@ NS_IMETHODIMP nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword,
nsIURI **aURI)
{
*aURI = nsnull;
- NS_ENSURE_STATE(mPrefBranch);
+ NS_ENSURE_STATE(Preferences::GetRootBranch());
// Strip leading "?" and leading/trailing spaces from aKeyword
nsCAutoString keyword(aKeyword);
@@ -367,19 +360,10 @@ NS_IMETHODIMP nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword,
}
keyword.Trim(" ");
- nsXPIDLCString url;
- nsCOMPtr keywordURL;
- mPrefBranch->GetComplexValue("keyword.URL",
- NS_GET_IID(nsIPrefLocalizedString),
- getter_AddRefs(keywordURL));
-
- if (keywordURL) {
- nsXPIDLString wurl;
- keywordURL->GetData(getter_Copies(wurl));
- CopyUTF16toUTF8(wurl, url);
- } else {
+ nsAdoptingCString url = Preferences::GetLocalizedCString("keyword.URL");
+ if (!url) {
// Fall back to a non-localized pref, for backwards compat
- mPrefBranch->GetCharPref("keyword.URL", getter_Copies(url));
+ url = Preferences::GetCString("keyword.URL");
}
// If the pref is set and non-empty, use it.
@@ -441,13 +425,11 @@ NS_IMETHODIMP nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword,
PRBool nsDefaultURIFixup::MakeAlternateURI(nsIURI *aURI)
{
- if (!mPrefBranch)
+ if (!Preferences::GetRootBranch())
{
return PR_FALSE;
}
- PRBool makeAlternate = PR_TRUE;
- mPrefBranch->GetBoolPref("browser.fixup.alternate.enabled", &makeAlternate);
- if (!makeAlternate)
+ if (!Preferences::GetBool("browser.fixup.alternate.enabled", PR_TRUE))
{
return PR_FALSE;
}
@@ -489,17 +471,17 @@ PRBool nsDefaultURIFixup::MakeAlternateURI(nsIURI *aURI)
// are www. & .com but they could be any other value, e.g. www. & .org
nsCAutoString prefix("www.");
- nsXPIDLCString prefPrefix;
- rv = mPrefBranch->GetCharPref("browser.fixup.alternate.prefix", getter_Copies(prefPrefix));
- if (NS_SUCCEEDED(rv))
+ nsAdoptingCString prefPrefix =
+ Preferences::GetCString("browser.fixup.alternate.prefix");
+ if (prefPrefix)
{
prefix.Assign(prefPrefix);
}
nsCAutoString suffix(".com");
- nsXPIDLCString prefSuffix;
- rv = mPrefBranch->GetCharPref("browser.fixup.alternate.suffix", getter_Copies(prefSuffix));
- if (NS_SUCCEEDED(rv))
+ nsAdoptingCString prefSuffix =
+ Preferences::GetCString("browser.fixup.alternate.suffix");
+ if (prefSuffix)
{
suffix.Assign(prefSuffix);
}
diff --git a/docshell/base/nsDefaultURIFixup.h b/docshell/base/nsDefaultURIFixup.h
index bc8a5487ef9..388cee5e25c 100644
--- a/docshell/base/nsDefaultURIFixup.h
+++ b/docshell/base/nsDefaultURIFixup.h
@@ -40,7 +40,6 @@
#ifndef NSDEFAULTURIFIXUP_H
#define NSDEFAULTURIFIXUP_H
-#include "nsIPrefBranch.h"
#include "nsIURIFixup.h"
#include "nsCOMPtr.h"
@@ -71,7 +70,6 @@ private:
const char * GetFileSystemCharset();
const char * GetCharsetForUrlBar();
- nsCOMPtr mPrefBranch;
nsCString mFsCharset;
};
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index 968d6bedf88..6674e527f17 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -95,9 +95,6 @@
#include "nsXPCOMCID.h"
#include "nsISeekableStream.h"
#include "nsAutoPtr.h"
-#include "nsIPrefService.h"
-#include "nsIPrefBranch.h"
-#include "nsIPrefBranch2.h"
#include "nsIWritablePropertyBag2.h"
#include "nsIAppShell.h"
#include "nsWidgetsCID.h"
@@ -111,6 +108,7 @@
#include "nsJSON.h"
#include "IHistory.h"
#include "mozilla/Services.h"
+#include "mozilla/Preferences.h"
// we want to explore making the document own the load group
// so we can associate the document URI with the load group.
@@ -307,21 +305,14 @@ FavorPerformanceHint(PRBool perfOverStarvation, PRUint32 starvationDelay)
static PRBool
PingsEnabled(PRInt32 *maxPerLink, PRBool *requireSameHost)
{
- PRBool allow = PR_FALSE;
+ PRBool allow = Preferences::GetBool(PREF_PINGS_ENABLED, PR_FALSE);
*maxPerLink = 1;
*requireSameHost = PR_TRUE;
- nsCOMPtr prefs =
- do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (prefs) {
- PRBool val;
- if (NS_SUCCEEDED(prefs->GetBoolPref(PREF_PINGS_ENABLED, &val)))
- allow = val;
- if (allow) {
- prefs->GetIntPref(PREF_PINGS_MAX_PER_LINK, maxPerLink);
- prefs->GetBoolPref(PREF_PINGS_REQUIRE_SAME_HOST, requireSameHost);
- }
+ if (allow) {
+ Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, maxPerLink);
+ Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, requireSameHost);
}
return allow;
@@ -739,6 +730,7 @@ nsDocShell::nsDocShell():
mPreviousTransIndex(-1),
mLoadType(0),
mLoadedTransIndex(-1),
+ mCreated(PR_FALSE),
mAllowSubframes(PR_TRUE),
mAllowPlugins(PR_TRUE),
mAllowJavascript(PR_TRUE),
@@ -1600,9 +1592,7 @@ nsDocShell::MaybeInitTiming()
return NS_OK;
}
- PRBool enabled;
- nsresult rv = mPrefs->GetBoolPref("dom.enable_performance", &enabled);
- if (NS_SUCCEEDED(rv) && enabled) {
+ if (Preferences::GetBool("dom.enable_performance", PR_FALSE)) {
mTiming = new nsDOMNavigationTiming();
mTiming->NotifyNavigationStart();
}
@@ -2201,11 +2191,8 @@ nsDocShell::SetUseErrorPages(PRBool aUseErrorPages)
{
// If mUseErrorPages is set explicitly, stop observing the pref.
if (mObserveErrorPages) {
- nsCOMPtr prefs(do_QueryInterface(mPrefs));
- if (prefs) {
- prefs->RemoveObserver("browser.xul.error_pages.enabled", this);
- mObserveErrorPages = PR_FALSE;
- }
+ Preferences::RemoveObserver(this, "browser.xul.error_pages.enabled");
+ mObserveErrorPages = PR_FALSE;
}
mUseErrorPages = aUseErrorPages;
return NS_OK;
@@ -3919,17 +3906,15 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
if (isStsHost)
cssClass.AssignLiteral("badStsCert");
- PRBool expert = PR_FALSE;
- mPrefs->GetBoolPref("browser.xul.error_pages.expert_bad_cert",
- &expert);
- if (expert) {
+ if (Preferences::GetBool(
+ "browser.xul.error_pages.expert_bad_cert", PR_FALSE)) {
cssClass.AssignLiteral("expertBadCert");
}
// See if an alternate cert error page is registered
- nsXPIDLCString alternateErrorPage;
- mPrefs->GetCharPref("security.alternate_certificate_error_page",
- getter_Copies(alternateErrorPage));
+ nsAdoptingCString alternateErrorPage =
+ Preferences::GetCString(
+ "security.alternate_certificate_error_page");
if (alternateErrorPage)
errorPage.Assign(alternateErrorPage);
} else {
@@ -3944,9 +3929,8 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
// Malware and phishing detectors may want to use an alternate error
// page, but if the pref's not set, we'll fall back on the standard page
- nsXPIDLCString alternateErrorPage;
- mPrefs->GetCharPref("urlclassifier.alternate_error_page",
- getter_Copies(alternateErrorPage));
+ nsAdoptingCString alternateErrorPage =
+ Preferences::GetCString("urlclassifier.alternate_error_page");
if (alternateErrorPage)
errorPage.Assign(alternateErrorPage);
@@ -4492,7 +4476,7 @@ nsDocShell::InitWindow(nativeWindow parentNativeWindow,
NS_IMETHODIMP
nsDocShell::Create()
{
- if (mPrefs) {
+ if (mCreated) {
// We've already been created
return NS_OK;
}
@@ -4500,34 +4484,24 @@ nsDocShell::Create()
NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
"Unexpected item type in docshell");
- nsresult rv = NS_ERROR_FAILURE;
- mPrefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
+ mCreated = PR_TRUE;
- PRBool tmpbool;
-
- rv = mPrefs->GetBoolPref("browser.frames.enabled", &tmpbool);
- if (NS_SUCCEEDED(rv))
- mAllowSubframes = tmpbool;
+ mAllowSubframes =
+ Preferences::GetBool("browser.frames.enabled", mAllowSubframes);
if (gValidateOrigin == (PRBool)0xffffffff) {
// Check pref to see if we should prevent frameset spoofing
- rv = mPrefs->GetBoolPref("browser.frame.validate_origin", &tmpbool);
- if (NS_SUCCEEDED(rv)) {
- gValidateOrigin = tmpbool;
- } else {
- gValidateOrigin = PR_TRUE;
- }
+ gValidateOrigin =
+ Preferences::GetBool("browser.frame.validate_origin", PR_TRUE);
}
// Should we use XUL error pages instead of alerts if possible?
- rv = mPrefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool);
- if (NS_SUCCEEDED(rv))
- mUseErrorPages = tmpbool;
+ mUseErrorPages =
+ Preferences::GetBool("browser.xul.error_pages.enabled", mUseErrorPages);
- nsCOMPtr prefs(do_QueryInterface(mPrefs, &rv));
- if (NS_SUCCEEDED(rv) && mObserveErrorPages) {
- prefs->AddObserver("browser.xul.error_pages.enabled", this, PR_FALSE);
+ if (mObserveErrorPages) {
+ Preferences::AddStrongObserver(this, "browser.xul.error_pages.enabled");
}
nsCOMPtr serv = do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
@@ -4560,11 +4534,8 @@ nsDocShell::Destroy()
// Remove our pref observers
if (mObserveErrorPages) {
- nsCOMPtr prefs(do_QueryInterface(mPrefs));
- if (prefs) {
- prefs->RemoveObserver("browser.xul.error_pages.enabled", this);
- mObserveErrorPages = PR_FALSE;
- }
+ Preferences::RemoveObserver(this, "browser.xul.error_pages.enabled");
+ mObserveErrorPages = PR_FALSE;
}
// Make sure to blow away our mLoadingURI just in case. No loads
@@ -5971,9 +5942,7 @@ nsDocShell::OnStateChange(nsIWebProgress * aProgress, nsIRequest * aRequest,
if ((aStateFlags & STATE_RESTORING) == 0) {
// Show the progress cursor if the pref is set
- PRBool tmpBool = PR_FALSE;
- if (NS_SUCCEEDED(mPrefs->GetBoolPref("ui.use_activity_cursor", &tmpBool))
- && tmpBool) {
+ if (Preferences::GetBool("ui.use_activity_cursor", PR_FALSE)) {
nsCOMPtr mainWidget;
GetMainWidget(getter_AddRefs(mainWidget));
if (mainWidget) {
@@ -5991,9 +5960,7 @@ nsDocShell::OnStateChange(nsIWebProgress * aProgress, nsIRequest * aRequest,
mBusyFlags = BUSY_FLAGS_NONE;
// Hide the progress cursor if the pref is set
- PRBool tmpBool = PR_FALSE;
- if (NS_SUCCEEDED(mPrefs->GetBoolPref("ui.use_activity_cursor", &tmpBool))
- && tmpBool) {
+ if (Preferences::GetBool("ui.use_activity_cursor", PR_FALSE)) {
nsCOMPtr mainWidget;
GetMainWidget(getter_AddRefs(mainWidget));
if (mainWidget) {
@@ -6240,12 +6207,8 @@ nsDocShell::EndPageLoad(nsIWebProgress * aProgress,
// First try keyword fixup
//
if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup) {
- PRBool keywordsEnabled = PR_FALSE;
-
- if (mPrefs &&
- NS_FAILED(mPrefs->GetBoolPref("keyword.enabled",
- &keywordsEnabled)))
- keywordsEnabled = PR_FALSE;
+ PRBool keywordsEnabled =
+ Preferences::GetBool("keyword.enabled", PR_FALSE);
nsCAutoString host;
url->GetHost(host);
@@ -6700,9 +6663,9 @@ nsDocShell::CanSavePresentation(PRUint32 aLoadType,
// Don't cache the content viewer if we're in a subframe and the subframe
// pref is disabled.
- PRBool cacheFrames = PR_FALSE;
- mPrefs->GetBoolPref("browser.sessionhistory.cache_subframes",
- &cacheFrames);
+ PRBool cacheFrames =
+ Preferences::GetBool("browser.sessionhistory.cache_subframes",
+ PR_FALSE);
if (!cacheFrames) {
nsCOMPtr root;
GetSameTypeParent(getter_AddRefs(root));
@@ -9212,22 +9175,16 @@ nsresult nsDocShell::DoChannelLoad(nsIChannel * aChannel,
case LOAD_NORMAL:
case LOAD_LINK:
// Set cache checking flags
- PRInt32 prefSetting;
- if (NS_SUCCEEDED
- (mPrefs->
- GetIntPref("browser.cache.check_doc_frequency",
- &prefSetting))) {
- switch (prefSetting) {
- case 0:
- loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
- break;
- case 1:
- loadFlags |= nsIRequest::VALIDATE_ALWAYS;
- break;
- case 2:
- loadFlags |= nsIRequest::VALIDATE_NEVER;
- break;
- }
+ switch (Preferences::GetInt("browser.cache.check_doc_frequency", -1)) {
+ case 0:
+ loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
+ break;
+ case 1:
+ loadFlags |= nsIRequest::VALIDATE_ALWAYS;
+ break;
+ case 2:
+ loadFlags |= nsIRequest::VALIDATE_NEVER;
+ break;
}
break;
}
@@ -9700,11 +9657,8 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
// Check that the state object isn't too long.
// Default max length: 640k bytes.
- PRInt32 maxStateObjSize = 0xA0000;
- if (mPrefs) {
- mPrefs->GetIntPref("browser.history.maxStateObjectSize",
- &maxStateObjSize);
- }
+ PRInt32 maxStateObjSize =
+ Preferences::GetInt("browser.history.maxStateObjectSize", 0xA0000);
if (maxStateObjSize < 0) {
maxStateObjSize = 0;
}
@@ -11206,11 +11160,8 @@ nsDocShell::Observe(nsISupports *aSubject, const char *aTopic,
!nsCRT::strcmp(aData,
NS_LITERAL_STRING("browser.xul.error_pages.enabled").get())) {
- nsCOMPtr prefs(do_QueryInterface(aSubject, &rv));
- NS_ENSURE_SUCCESS(rv, rv);
-
PRBool tmpbool;
- rv = prefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool);
+ rv = Preferences::GetBool("browser.xul.error_pages.enabled", &tmpbool);
if (NS_SUCCEEDED(rv))
mUseErrorPages = tmpbool;
diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
index 9dc8f837e11..175d01d1f58 100644
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -44,7 +44,6 @@
#include "nsIDOMNode.h"
#include "nsIDOMNodeList.h"
#include "nsIContentViewer.h"
-#include "nsIPrefBranch.h"
#include "nsInterfaceHashtable.h"
#include "nsIScriptContext.h"
#include "nsITimer.h"
@@ -718,7 +717,6 @@ protected:
nsCOMPtr mContentViewer;
nsCOMPtr mDocumentCharsetInfo;
nsCOMPtr mParentWidget;
- nsCOMPtr mPrefs;
// mCurrentURI should be marked immutable on set if possible.
nsCOMPtr mCurrentURI;
@@ -791,6 +789,7 @@ protected:
PRInt32 mPreviousTransIndex;
PRInt32 mLoadedTransIndex;
+ PRPackedBool mCreated;
PRPackedBool mAllowSubframes;
PRPackedBool mAllowPlugins;
PRPackedBool mAllowJavascript;
diff --git a/docshell/build/nsDocShellModule.cpp b/docshell/build/nsDocShellModule.cpp
index 9df5c0cee7a..c9b190f0173 100644
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -94,6 +94,7 @@ Initialize()
static void
Shutdown()
{
+ nsSHistory::Shutdown();
nsSHEntry::Shutdown();
gInitialized = PR_FALSE;
}
diff --git a/docshell/shistory/src/nsSHistory.cpp b/docshell/shistory/src/nsSHistory.cpp
index 06499a57447..b391407daa4 100644
--- a/docshell/shistory/src/nsSHistory.cpp
+++ b/docshell/shistory/src/nsSHistory.cpp
@@ -43,6 +43,7 @@
// Helper Classes
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
+#include "mozilla/Preferences.h"
// Interfaces Needed
#include "nsILayoutHistoryState.h"
@@ -53,7 +54,6 @@
#include "nsIDocShellTreeNode.h"
#include "nsIDocShellLoadInfo.h"
#include "nsIServiceManager.h"
-#include "nsIPrefService.h"
#include "nsIURI.h"
#include "nsIContentViewer.h"
#include "nsICacheService.h"
@@ -68,10 +68,19 @@
#include "nspr.h"
#include // for log()
+using namespace mozilla;
+
#define PREF_SHISTORY_SIZE "browser.sessionhistory.max_entries"
#define PREF_SHISTORY_MAX_TOTAL_VIEWERS "browser.sessionhistory.max_total_viewers"
#define PREF_SHISTORY_OPTIMIZE_EVICTION "browser.sessionhistory.optimize_eviction"
+static const char* kObservedPrefs[] = {
+ PREF_SHISTORY_SIZE,
+ PREF_SHISTORY_MAX_TOTAL_VIEWERS,
+ PREF_SHISTORY_OPTIMIZE_EVICTION,
+ nsnull
+};
+
static PRInt32 gHistoryMaxSize = 50;
// Max viewers allowed per SHistory objects
static const PRInt32 gHistoryMaxViewers = 3;
@@ -115,6 +124,8 @@ protected:
~nsSHistoryObserver() {}
};
+static nsSHistoryObserver* gObserver = nsnull;
+
NS_IMPL_ISUPPORTS1(nsSHistoryObserver, nsIObserver)
NS_IMETHODIMP
@@ -122,11 +133,8 @@ nsSHistoryObserver::Observe(nsISupports *aSubject, const char *aTopic,
const PRUnichar *aData)
{
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
- nsCOMPtr prefs = do_QueryInterface(aSubject);
- if (prefs) {
- nsSHistory::UpdatePrefs(prefs);
- nsSHistory::EvictGlobalContentViewer();
- }
+ nsSHistory::UpdatePrefs();
+ nsSHistory::EvictGlobalContentViewer();
} else if (!strcmp(aTopic, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID) ||
!strcmp(aTopic, "memory-pressure")) {
nsSHistory::EvictAllContentViewersGlobally();
@@ -227,13 +235,12 @@ nsSHistory::CalcMaxTotalViewers()
// static
void
-nsSHistory::UpdatePrefs(nsIPrefBranch *aPrefBranch)
+nsSHistory::UpdatePrefs()
{
- aPrefBranch->GetIntPref(PREF_SHISTORY_SIZE, &gHistoryMaxSize);
- aPrefBranch->GetIntPref(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
- &sHistoryMaxTotalViewers);
- aPrefBranch->GetBoolPref(PREF_SHISTORY_OPTIMIZE_EVICTION,
- &gOptimizeEviction);
+ Preferences::GetInt(PREF_SHISTORY_SIZE, &gHistoryMaxSize);
+ Preferences::GetInt(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
+ &sHistoryMaxTotalViewers);
+ Preferences::GetBool(PREF_SHISTORY_OPTIMIZE_EVICTION, &gOptimizeEviction);
// If the pref is negative, that means we calculate how many viewers
// we think we should cache, based on total memory
if (sHistoryMaxTotalViewers < 0) {
@@ -245,52 +252,33 @@ nsSHistory::UpdatePrefs(nsIPrefBranch *aPrefBranch)
nsresult
nsSHistory::Startup()
{
- nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (prefs) {
- nsCOMPtr sesHBranch;
- prefs->GetBranch(nsnull, getter_AddRefs(sesHBranch));
- if (sesHBranch) {
- UpdatePrefs(sesHBranch);
- }
+ UpdatePrefs();
- // The goal of this is to unbreak users who have inadvertently set their
- // session history size to less than the default value.
- PRInt32 defaultHistoryMaxSize = 50;
- nsCOMPtr defaultBranch;
- prefs->GetDefaultBranch(nsnull, getter_AddRefs(defaultBranch));
- if (defaultBranch) {
- defaultBranch->GetIntPref(PREF_SHISTORY_SIZE, &defaultHistoryMaxSize);
- }
+ // The goal of this is to unbreak users who have inadvertently set their
+ // session history size to less than the default value.
+ PRInt32 defaultHistoryMaxSize =
+ Preferences::GetDefaultInt(PREF_SHISTORY_SIZE, 50);
+ if (gHistoryMaxSize < defaultHistoryMaxSize) {
+ gHistoryMaxSize = defaultHistoryMaxSize;
+ }
+
+ // Allow the user to override the max total number of cached viewers,
+ // but keep the per SHistory cached viewer limit constant
+ if (!gObserver) {
+ gObserver = new nsSHistoryObserver();
+ NS_ADDREF(gObserver);
+ Preferences::AddStrongObservers(gObserver, kObservedPrefs);
- if (gHistoryMaxSize < defaultHistoryMaxSize) {
- gHistoryMaxSize = defaultHistoryMaxSize;
- }
-
- // Allow the user to override the max total number of cached viewers,
- // but keep the per SHistory cached viewer limit constant
- nsCOMPtr branch = do_QueryInterface(sesHBranch);
- if (branch) {
- nsSHistoryObserver* obs = new nsSHistoryObserver();
- if (!obs) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- branch->AddObserver(PREF_SHISTORY_SIZE, obs, PR_FALSE);
- branch->AddObserver(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
- obs, PR_FALSE);
- branch->AddObserver(PREF_SHISTORY_OPTIMIZE_EVICTION,
- obs, PR_FALSE);
+ nsCOMPtr obsSvc =
+ mozilla::services::GetObserverService();
+ if (obsSvc) {
+ // Observe empty-cache notifications so tahat clearing the disk/memory
+ // cache will also evict all content viewers.
+ obsSvc->AddObserver(gObserver,
+ NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID, PR_FALSE);
- nsCOMPtr obsSvc =
- mozilla::services::GetObserverService();
- if (obsSvc) {
- // Observe empty-cache notifications so tahat clearing the disk/memory
- // cache will also evict all content viewers.
- obsSvc->AddObserver(obs,
- NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID, PR_FALSE);
-
- // Same for memory-pressure notifications
- obsSvc->AddObserver(obs, "memory-pressure", PR_FALSE);
- }
+ // Same for memory-pressure notifications
+ obsSvc->AddObserver(gObserver, "memory-pressure", PR_FALSE);
}
}
@@ -299,6 +287,22 @@ nsSHistory::Startup()
return NS_OK;
}
+// static
+void
+nsSHistory::Shutdown()
+{
+ if (gObserver) {
+ Preferences::RemoveObservers(gObserver, kObservedPrefs);
+ nsCOMPtr obsSvc =
+ mozilla::services::GetObserverService();
+ if (obsSvc) {
+ obsSvc->RemoveObserver(gObserver, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID);
+ obsSvc->RemoveObserver(gObserver, "memory-pressure");
+ }
+ NS_RELEASE(gObserver);
+ }
+}
+
/* Add an entry to the History list at mIndex and
* increment the index to point to the new entry
*/
diff --git a/docshell/shistory/src/nsSHistory.h b/docshell/shistory/src/nsSHistory.h
index 94a9650ef73..01cdf465454 100644
--- a/docshell/shistory/src/nsSHistory.h
+++ b/docshell/shistory/src/nsSHistory.h
@@ -53,7 +53,6 @@
#include "nsISHistoryListener.h"
#include "nsIHistoryEntry.h"
#include "nsIObserver.h"
-#include "nsIPrefBranch2.h"
// Needed to maintain global list of all SHistory objects
#include "prclist.h"
@@ -76,7 +75,8 @@ public:
// One time initialization method called upon docshell module construction
static nsresult Startup();
- static void UpdatePrefs(nsIPrefBranch *aPrefBranch);
+ static void Shutdown();
+ static void UpdatePrefs();
// Max number of total cached content viewers. If the pref
// browser.sessionhistory.max_total_viewers is negative, then
diff --git a/dom/base/nsJSTimeoutHandler.cpp b/dom/base/nsJSTimeoutHandler.cpp
index a7adb74cd63..cca7ec56920 100644
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -142,13 +142,12 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSScriptTimeoutHandler)
}
}
}
- cb.DescribeNode(RefCounted, tmp->mRefCnt.get(),
- sizeof(nsJSScriptTimeoutHandler), foo.get());
+ cb.DescribeRefCountedNode(tmp->mRefCnt.get(),
+ sizeof(nsJSScriptTimeoutHandler), foo.get());
}
else {
- cb.DescribeNode(RefCounted, tmp->mRefCnt.get(),
- sizeof(nsJSScriptTimeoutHandler),
- "nsJSScriptTimeoutHandler");
+ NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSScriptTimeoutHandler,
+ tmp->mRefCnt.get())
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
diff --git a/editor/libeditor/html/nsHTMLEditor.cpp b/editor/libeditor/html/nsHTMLEditor.cpp
index 0a16671b164..3f36166e9df 100644
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -168,11 +168,6 @@ nsHTMLEditor::~nsHTMLEditor()
// free any default style propItems
RemoveAllDefaultProperties();
- while (mStyleSheetURLs.Length())
- {
- RemoveOverrideStyleSheet(mStyleSheetURLs[0]);
- }
-
if (mLinkHandler && mDocWeak)
{
nsCOMPtr ps = GetPresShell();
@@ -359,6 +354,11 @@ nsHTMLEditor::PreDestroy(PRBool aDestroyingFrames)
document->RemoveMutationObserver(this);
}
+ while (mStyleSheetURLs.Length())
+ {
+ RemoveOverrideStyleSheet(mStyleSheetURLs[0]);
+ }
+
return nsPlaintextEditor::PreDestroy(aDestroyingFrames);
}
diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h
new file mode 100644
index 00000000000..da001346e82
--- /dev/null
+++ b/gfx/2d/2D.h
@@ -0,0 +1,668 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bas Schouten
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MOZILLA_GFX_2D_H
+#define _MOZILLA_GFX_2D_H
+
+#include "Point.h"
+#include "Rect.h"
+#include "Matrix.h"
+
+// This RefPtr class isn't ideal for usage in Azure, as it doesn't allow T**
+// outparams using the &-operator. But it will have to do as there's no easy
+// solution.
+#include "mozilla/RefPtr.h"
+
+struct _cairo_surface;
+typedef _cairo_surface cairo_surface_t;
+
+struct ID3D10Device1;
+struct ID3D10Texture2D;
+
+namespace mozilla {
+namespace gfx {
+
+class SourceSurface;
+class DataSourceSurface;
+class DrawTarget;
+
+struct NativeSurface {
+ NativeSurfaceType mType;
+ SurfaceFormat mFormat;
+ void *mSurface;
+};
+
+struct NativeFont {
+ NativeFontType mType;
+ void *mFont;
+};
+
+/*
+ * This structure is used to send draw options that are universal to all drawing
+ * operations. It consists of the following:
+ *
+ * mAlpha - Alpha value by which the mask generated by this operation is
+ * multiplied.
+ * mCompositionOp - The operator that indicates how the source and destination
+ * patterns are blended.
+ * mAntiAliasMode - The AntiAlias mode used for this drawing operation.
+ * mSnapping - Whether this operation is snapped to pixel boundaries.
+ */
+struct DrawOptions {
+ DrawOptions(Float aAlpha = 1.0f,
+ CompositionOp aCompositionOp = OP_OVER,
+ AntialiasMode aAntialiasMode = AA_GRAY,
+ Snapping aSnapping = SNAP_NONE)
+ : mAlpha(aAlpha)
+ , mCompositionOp(aCompositionOp)
+ , mAntialiasMode(aAntialiasMode)
+ , mSnapping(aSnapping)
+ {}
+
+ Float mAlpha;
+ CompositionOp mCompositionOp : 8;
+ AntialiasMode mAntialiasMode : 2;
+ Snapping mSnapping : 1;
+};
+
+/*
+ * This structure is used to send stroke options that are used in stroking
+ * operations. It consists of the following:
+ *
+ * mLineWidth - Width of the stroke in userspace.
+ * mLineJoin - Join style used for joining lines.
+ * mLineCap - Cap style used for capping lines.
+ * mMiterLimit - Miter limit in units of linewidth
+ */
+struct StrokeOptions {
+ StrokeOptions(Float aLineWidth = 1.0f,
+ JoinStyle aLineJoin = JOIN_MITER_OR_BEVEL,
+ CapStyle aLineCap = CAP_BUTT,
+ Float aMiterLimit = 10.0f)
+ : mLineWidth(aLineWidth)
+ , mMiterLimit(aMiterLimit)
+ , mLineJoin(aLineJoin)
+ , mLineCap(aLineCap)
+ {}
+
+ Float mLineWidth;
+ Float mMiterLimit;
+ JoinStyle mLineJoin : 4;
+ CapStyle mLineCap : 3;
+};
+
+/*
+ * This structure supplies additional options for calls to DrawSurface.
+ *
+ * mFilter - Filter used when resampling source surface region to the
+ * destination region.
+ */
+struct DrawSurfaceOptions {
+ DrawSurfaceOptions(Filter aFilter = FILTER_LINEAR)
+ : mFilter(aFilter)
+ { }
+
+ Filter mFilter : 3;
+};
+
+/*
+ * This class is used to store gradient stops, it can only be used with a
+ * matching DrawTarget. Not adhering to this condition will make a draw call
+ * fail.
+ */
+class GradientStops : public RefCounted
+{
+public:
+ virtual ~GradientStops() {}
+
+ virtual BackendType GetBackendType() const = 0;
+
+protected:
+ GradientStops() {}
+};
+
+/*
+ * This is the base class for 'patterns'. Patterns describe the pixels used as
+ * the source for a masked composition operation that is done by the different
+ * drawing commands. These objects are not backend specific, however for
+ * example the gradient stops on a gradient pattern can be backend specific.
+ */
+class Pattern
+{
+public:
+ virtual ~Pattern() {}
+
+ virtual PatternType GetType() const = 0;
+
+protected:
+ Pattern() {}
+};
+
+class ColorPattern : public Pattern
+{
+public:
+ ColorPattern(const Color &aColor)
+ : mColor(aColor)
+ {}
+
+ virtual PatternType GetType() const { return PATTERN_COLOR; }
+
+ Color mColor;
+};
+
+/*
+ * This class is used for Linear Gradient Patterns, the gradient stops are
+ * stored in a separate object and are backend dependent. This class itself
+ * may be used on the stack.
+ */
+class LinearGradientPattern : public Pattern
+{
+public:
+ /*
+ * aBegin Start of the linear gradient
+ * aEnd End of the linear gradient
+ * aStops GradientStops object for this gradient, this should match the
+ * backend type of the draw target this pattern will be used with.
+ */
+ LinearGradientPattern(const Point &aBegin,
+ const Point &aEnd,
+ GradientStops *aStops)
+ : mBegin(aBegin)
+ , mEnd(aEnd)
+ , mStops(aStops)
+ {
+ }
+
+ virtual PatternType GetType() const { return PATTERN_LINEAR_GRADIENT; }
+
+ Point mBegin;
+ Point mEnd;
+ RefPtr mStops;
+};
+
+/*
+ * This class is used for Radial Gradient Patterns, the gradient stops are
+ * stored in a separate object and are backend dependent. This class itself
+ * may be used on the stack.
+ */
+class RadialGradientPattern : public Pattern
+{
+public:
+ /*
+ * aBegin Start of the linear gradient
+ * aEnd End of the linear gradient
+ * aStops GradientStops object for this gradient, this should match the
+ * backend type of the draw target this pattern will be used with.
+ */
+ RadialGradientPattern(const Point &aCenter,
+ const Point &aOrigin,
+ Float aRadius,
+ GradientStops *aStops)
+ : mCenter(aCenter)
+ , mOrigin(aOrigin)
+ , mRadius(aRadius)
+ , mStops(aStops)
+ {
+ }
+
+ virtual PatternType GetType() const { return PATTERN_RADIAL_GRADIENT; }
+
+ Point mCenter;
+ Point mOrigin;
+ Float mRadius;
+ RefPtr mStops;
+};
+
+/*
+ * This class is used for Surface Patterns, they wrap a surface and a
+ * repetition mode for the surface. This may be used on the stack.
+ */
+class SurfacePattern : public Pattern
+{
+public:
+ SurfacePattern(SourceSurface *aSourceSurface, ExtendMode aExtendMode)
+ : mSurface(aSourceSurface)
+ , mExtendMode(aExtendMode)
+ {}
+
+ virtual PatternType GetType() const { return PATTERN_SURFACE; }
+
+ RefPtr mSurface;
+ ExtendMode mExtendMode;
+ Filter mFilter;
+};
+
+/*
+ * This is the base class for source surfaces. These objects are surfaces
+ * which may be used as a source in a SurfacePattern of a DrawSurface call.
+ * They cannot be drawn to directly.
+ */
+class SourceSurface : public RefCounted
+{
+public:
+ virtual ~SourceSurface() {}
+
+ virtual SurfaceType GetType() const = 0;
+ virtual IntSize GetSize() const = 0;
+ virtual SurfaceFormat GetFormat() const = 0;
+
+ /*
+ * This function will get a DataSourceSurface for this surface, a
+ * DataSourceSurface's data can be accessed directly.
+ */
+ virtual TemporaryRef GetDataSurface() = 0;
+};
+
+class DataSourceSurface : public SourceSurface
+{
+public:
+ /* Get the raw bitmap data of the surface */
+ virtual unsigned char *GetData() = 0;
+ /*
+ * Stride of the surface, distance in bytes between the start of the image
+ * data belonging to row y and row y+1. This may be negative.
+ */
+ virtual int32_t Stride() = 0;
+
+ virtual TemporaryRef GetDataSurface() { RefPtr temp = this; return temp.forget(); }
+};
+
+/* This is an abstract object that accepts path segments. */
+class PathSink : public RefCounted
+{
+public:
+ virtual ~PathSink() {}
+
+ /* Move the current point in the path, any figure currently being drawn will
+ * be considered closed during fill operations, however when stroking the
+ * closing line segment will not be drawn.
+ */
+ virtual void MoveTo(const Point &aPoint) = 0;
+ /* Add a linesegment to the current figure */
+ virtual void LineTo(const Point &aPoint) = 0;
+ /* Add a cubic bezier curve to the current figure */
+ virtual void BezierTo(const Point &aCP1,
+ const Point &aCP2,
+ const Point &aCP3) = 0;
+ /* Add a quadratic bezier curve to the current figure */
+ virtual void QuadraticBezierTo(const Point &aCP1,
+ const Point &aCP2) = 0;
+ /* Close the current figure, this will essentially generate a line segment
+ * from the current point to the starting point for the current figure
+ */
+ virtual void Close() = 0;
+ /* Add an arc to the current figure */
+ virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
+ float aEndAngle, bool aAntiClockwise = false) = 0;
+ /* Point the current subpath is at - or where the next subpath will start
+ * if there is no active subpath.
+ */
+ virtual Point CurrentPoint() const = 0;
+};
+
+class PathBuilder;
+
+/* The path class is used to create (sets of) figures of any shape that can be
+ * filled or stroked to a DrawTarget
+ */
+class Path : public RefCounted
+{
+public:
+ virtual ~Path() {}
+
+ virtual BackendType GetBackendType() const = 0;
+
+ /* This returns a PathBuilder object that contains a copy of the contents of
+ * this path and is still writable.
+ */
+ virtual TemporaryRef CopyToBuilder(FillRule aFillRule = FILL_WINDING) const = 0;
+ virtual TemporaryRef TransformedCopyToBuilder(const Matrix &aTransform,
+ FillRule aFillRule = FILL_WINDING) const = 0;
+
+ /* This function checks if a point lies within a path. It allows passing a
+ * transform that will transform the path to the coordinate space in which
+ * aPoint is given.
+ */
+ virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const = 0;
+
+ /* This gets the fillrule this path's builder was created with. This is not
+ * mutable.
+ */
+ virtual FillRule GetFillRule() const = 0;
+};
+
+/* The PathBuilder class allows path creation. Once finish is called on the
+ * pathbuilder it may no longer be written to.
+ */
+class PathBuilder : public PathSink
+{
+public:
+ /* Finish writing to the path and return a Path object that can be used for
+ * drawing. Future use of the builder results in a crash!
+ */
+ virtual TemporaryRef Finish() = 0;
+};
+
+struct Glyph
+{
+ uint32_t mIndex;
+ Point mPosition;
+};
+
+/* This class functions as a glyph buffer that can be drawn to a DrawTarget.
+ * XXX - This should probably contain the guts of gfxTextRun in the future as
+ * roc suggested. But for now it's a simple container for a glyph vector.
+ */
+struct GlyphBuffer
+{
+ // A pointer to a buffer of glyphs. Managed by the caller.
+ const Glyph *mGlyphs;
+ // Number of glyphs mGlyphs points to.
+ uint32_t mNumGlyphs;
+};
+
+/* This class is an abstraction of a backend/platform specific font object
+ * at a particular size. It is passed into text drawing calls to describe
+ * the font used for the drawing call.
+ */
+class ScaledFont : public RefCounted
+{
+public:
+ virtual ~ScaledFont() {}
+
+ virtual FontType GetType() const = 0;
+
+ /* This allows getting a path that describes the outline of a set of glyphs.
+ * A target is passed in so that the guarantee is made the returned path
+ * can be used with any DrawTarget that has the same backend as the one
+ * passed in.
+ */
+ virtual TemporaryRef GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) = 0;
+
+protected:
+ ScaledFont() {}
+};
+
+/* This is the main class used for all the drawing. It is created through the
+ * factory and accepts drawing commands. The results of drawing to a target
+ * may be used either through a Snapshot or by flushing the target and directly
+ * accessing the backing store a DrawTarget was created with.
+ */
+class DrawTarget : public RefCounted
+{
+public:
+ DrawTarget() : mTransformDirty(false) {}
+ virtual ~DrawTarget() {}
+
+ virtual BackendType GetType() const = 0;
+ virtual TemporaryRef Snapshot() = 0;
+ virtual IntSize GetSize() = 0;
+
+ /* Ensure that the DrawTarget backend has flushed all drawing operations to
+ * this draw target. This must be called before using the backing surface of
+ * this draw target outside of GFX 2D code.
+ */
+ virtual void Flush() = 0;
+
+ /*
+ * Draw a surface to the draw target. Possibly doing partial drawing or
+ * applying scaling. No sampling happens outside the source.
+ *
+ * aSurface Source surface to draw
+ * aDest Destination rectangle that this drawing operation should draw to
+ * aSource Source rectangle in aSurface coordinates, this area of aSurface
+ * will be stretched to the size of aDest.
+ * aOptions General draw options that are applied to the operation
+ * aSurfOptions DrawSurface options that are applied
+ */
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /*
+ * Blend a surface to the draw target with a shadow. The shadow is drawn as a
+ * gaussian blur using a specified sigma.
+ * NOTE: This function works in device space!
+ *
+ * aSurface Source surface to draw.
+ * aDest Destination point that this drawing operation should draw to.
+ * aColor Color of the drawn shadow
+ * aOffset Offset of the shadow
+ * aSigma Sigma used for the guassian filter kernel
+ */
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma) = 0;
+
+ /*
+ * Clear a rectangle on the draw target to transparent black. This will
+ * respect the clipping region and transform.
+ *
+ * aRect Rectangle to clear
+ */
+ virtual void ClearRect(const Rect &aRect) = 0;
+
+ /*
+ * This is essentially a 'memcpy' between two surfaces. It moves a pixel
+ * aligned area from the source surface unscaled directly onto the
+ * drawtarget. This ignores both transform and clip.
+ *
+ * aSurface Surface to copy from
+ * aSourceRect Source rectangle to be copied
+ * aDest Destination point to copy the surface to
+ */
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination) = 0;
+
+ /*
+ * Fill a rectangle on the DrawTarget with a certain source pattern.
+ *
+ * aRect Rectangle that forms the mask of this filling operation
+ * aPattern Pattern that forms the source of this filling operation
+ * aOptions Options that are applied to this operation
+ */
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /*
+ * Stroke a rectangle on the DrawTarget with a certain source pattern.
+ *
+ * aRect Rectangle that forms the mask of this stroking operation
+ * aPattern Pattern that forms the source of this stroking operation
+ * aOptions Options that are applied to this operation
+ */
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /*
+ * Stroke a line on the DrawTarget with a certain source pattern.
+ *
+ * aStart Starting point of the line
+ * aEnd End point of the line
+ * aPattern Pattern that forms the source of this stroking operation
+ * aOptions Options that are applied to this operation
+ */
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /*
+ * Stroke a path on the draw target with a certain source pattern.
+ *
+ * aPath Path that is to be stroked
+ * aPattern Pattern that should be used for the stroke
+ * aStrokeOptions Stroke options used for this operation
+ * aOptions Draw options used for this operation
+ */
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /*
+ * Fill a path on the draw target with a certain source pattern.
+ *
+ * aPath Path that is to be filled
+ * aPattern Pattern that should be used for the fill
+ * aOptions Draw options used for this operation
+ */
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /*
+ * Fill a series of clyphs on the draw target with a certain source pattern.
+ */
+ virtual void FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions()) = 0;
+
+ /*
+ * Push a clip to the DrawTarget.
+ *
+ * aPath The path to clip to
+ */
+ virtual void PushClip(const Path *aPath) = 0;
+
+ /* Pop a clip from the DrawTarget. A pop without a corresponding push will
+ * be ignored.
+ */
+ virtual void PopClip() = 0;
+
+ /*
+ * Create a SourceSurface optimized for use with this DrawTarget for
+ * existing bitmap data in memory.
+ */
+ virtual TemporaryRef CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const = 0;
+
+ /*
+ * Create a SourceSurface optimized for use with this DrawTarget from
+ * an arbitrary other SourceSurface. This may return aSourceSurface or some
+ * other existing surface.
+ */
+ virtual TemporaryRef OptimizeSourceSurface(SourceSurface *aSurface) const = 0;
+
+ /*
+ * Create a SourceSurface for a type of NativeSurface. This may fail if the
+ * draw target does not know how to deal with the type of NativeSurface passed
+ * in.
+ */
+ virtual TemporaryRef
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const = 0;
+
+ /*
+ * Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
+ */
+ virtual TemporaryRef
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const = 0;
+
+ /*
+ * Create a path builder with the specified fillmode.
+ */
+ virtual TemporaryRef CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const = 0;
+
+ /*
+ * Create a GradientStops object that holds information about a set of
+ * gradient stops, this object is required for linear or radial gradient
+ * patterns to represent the color stops in the gradient.
+ *
+ * aStops An array of gradient stops
+ * aNumStops Number of stops in the array aStops
+ */
+ virtual TemporaryRef CreateGradientStops(GradientStop *aStops, uint32_t aNumStops) const = 0;
+
+ const Matrix &GetTransform() const { return mTransform; }
+
+ /*
+ * Set a transform on the surface, this transform is applied at drawing time
+ * to both the mask and source of the operation.
+ */
+ virtual void SetTransform(const Matrix &aTransform)
+ { mTransform = aTransform; mTransformDirty = true; }
+
+ SurfaceFormat GetFormat() { return mFormat; }
+
+ /* Tries to get a native surface for a DrawTarget, this may fail if the
+ * draw target cannot convert to this surface type.
+ */
+ virtual void *GetNativeSurface(NativeSurfaceType aType) = 0;
+
+protected:
+ Matrix mTransform;
+ bool mTransformDirty : 1;
+
+ SurfaceFormat mFormat;
+};
+
+class Factory
+{
+public:
+#ifdef USE_CAIRO
+ static TemporaryRef CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface);
+#endif
+
+ static TemporaryRef CreateDrawTarget(BackendType aBackend, const IntSize &aSize, SurfaceFormat aFormat);
+ static TemporaryRef CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize);
+
+#ifdef WIN32
+ static TemporaryRef CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat);
+ static void SetDirect3D10Device(ID3D10Device1 *aDevice);
+ static ID3D10Device1 *GetDirect3D10Device();
+
+private:
+ static ID3D10Device1 *mD3D10Device;
+#endif
+};
+
+}
+}
+
+#endif // _MOZILLA_GFX_2D_H
diff --git a/gfx/src/BaseMargin.h b/gfx/2d/BaseMargin.h
similarity index 93%
rename from gfx/src/BaseMargin.h
rename to gfx/2d/BaseMargin.h
index a25089473e0..1b99482cce7 100644
--- a/gfx/src/BaseMargin.h
+++ b/gfx/2d/BaseMargin.h
@@ -35,12 +35,13 @@
*
* ***** END LICENSE BLOCK ***** */
-#ifndef MOZILLA_BASEMARGIN_H_
-#define MOZILLA_BASEMARGIN_H_
+#ifndef MOZILLA_GFX_BASEMARGIN_H_
+#define MOZILLA_GFX_BASEMARGIN_H_
-#include "gfxCore.h"
+#include "Types.h"
namespace mozilla {
+namespace gfx {
/**
* Do not use this class directly. Subclass it, pass that subclass as the
@@ -68,12 +69,10 @@ struct BaseMargin {
T TopBottom() const { return top + bottom; }
T& Side(SideT aSide) {
- NS_PRECONDITION(aSide <= NS_SIDE_LEFT, "Out of range side");
// This is ugly!
return *(&top + aSide);
}
T Side(SideT aSide) const {
- NS_PRECONDITION(aSide <= NS_SIDE_LEFT, "Out of range side");
// This is ugly!
return *(&top + aSide);
}
@@ -104,6 +103,7 @@ struct BaseMargin {
}
};
+}
}
-#endif /* MOZILLA_BASEMARGIN_H_ */
+#endif /* MOZILLA_GFX_BASEMARGIN_H_ */
diff --git a/gfx/src/BasePoint.h b/gfx/2d/BasePoint.h
similarity index 96%
rename from gfx/src/BasePoint.h
rename to gfx/2d/BasePoint.h
index 21cb0b64251..08f695fd262 100644
--- a/gfx/src/BasePoint.h
+++ b/gfx/2d/BasePoint.h
@@ -35,10 +35,11 @@
*
* ***** END LICENSE BLOCK ***** */
-#ifndef MOZILLA_BASEPOINT_H_
-#define MOZILLA_BASEPOINT_H_
+#ifndef MOZILLA_GFX_BASEPOINT_H_
+#define MOZILLA_GFX_BASEPOINT_H_
namespace mozilla {
+namespace gfx {
/**
* Do not use this class directly. Subclass it, pass that subclass as the
@@ -95,6 +96,7 @@ struct BasePoint {
}
};
+}
}
-#endif /* MOZILLA_BASEPOINT_H_ */
+#endif /* MOZILLA_GFX_BASEPOINT_H_ */
diff --git a/gfx/src/BaseRect.h b/gfx/2d/BaseRect.h
similarity index 88%
rename from gfx/src/BaseRect.h
rename to gfx/2d/BaseRect.h
index cb3be231de2..9359f5a7f2f 100644
--- a/gfx/src/BaseRect.h
+++ b/gfx/2d/BaseRect.h
@@ -35,12 +35,27 @@
*
* ***** END LICENSE BLOCK ***** */
-#ifndef MOZILLA_BASERECT_H_
-#define MOZILLA_BASERECT_H_
+#ifndef MOZILLA_GFX_BASERECT_H_
+#define MOZILLA_GFX_BASERECT_H_
-#include "nsAlgorithm.h"
+#include
namespace mozilla {
+namespace gfx {
+
+// XXX - conflicts with exceptions on 10.6. Define our own gfx_min/gfx_max
+// functions here. Avoid min/max to avoid conflicts with existing #defines on windows.
+template
+T gfx_min(T aVal1, T aVal2)
+{
+ return (aVal1 < aVal2) ? aVal1 : aVal2;
+}
+
+template
+T gfx_max(T aVal1, T aVal2)
+{
+ return (aVal1 > aVal2) ? aVal1 : aVal2;
+}
/**
* Rectangles have two interpretations: a set of (zero-size) points,
@@ -113,15 +128,15 @@ struct BaseRect {
}
// Returns the rectangle containing the intersection of the points
// (including edges) of *this and aRect. If there are no points in that
- // intersection, returns an empty rectangle with x/y set to the max of the x/y
+ // intersection, returns an empty rectangle with x/y set to the gfx_max of the x/y
// of *this and aRect.
Sub Intersect(const Sub& aRect) const
{
Sub result;
- result.x = NS_MAX(x, aRect.x);
- result.y = NS_MAX(y, aRect.y);
- result.width = NS_MIN(XMost(), aRect.XMost()) - result.x;
- result.height = NS_MIN(YMost(), aRect.YMost()) - result.y;
+ result.x = gfx_max(x, aRect.x);
+ result.y = gfx_max(y, aRect.y);
+ result.width = gfx_min(XMost(), aRect.XMost()) - result.x;
+ result.height = gfx_min(YMost(), aRect.YMost()) - result.y;
if (result.width < 0 || result.height < 0) {
result.SizeTo(0, 0);
}
@@ -129,7 +144,7 @@ struct BaseRect {
}
// Sets *this to be the rectangle containing the intersection of the points
// (including edges) of *this and aRect. If there are no points in that
- // intersection, sets *this to be an empty rectangle with x/y set to the max
+ // intersection, sets *this to be an empty rectangle with x/y set to the gfx_max
// of the x/y of *this and aRect.
//
// 'this' can be the same object as either aRect1 or aRect2
@@ -159,10 +174,10 @@ struct BaseRect {
Sub UnionEdges(const Sub& aRect) const
{
Sub result;
- result.x = NS_MIN(x, aRect.x);
- result.y = NS_MIN(y, aRect.y);
- result.width = NS_MAX(XMost(), aRect.XMost()) - result.x;
- result.height = NS_MAX(YMost(), aRect.YMost()) - result.y;
+ result.x = gfx_min(x, aRect.x);
+ result.y = gfx_min(y, aRect.y);
+ result.width = gfx_max(XMost(), aRect.XMost()) - result.x;
+ result.height = gfx_max(YMost(), aRect.YMost()) - result.y;
return result;
}
// Computes the smallest rectangle that contains both the area of both
@@ -223,15 +238,15 @@ struct BaseRect {
{
x += aDx;
y += aDy;
- width = NS_MAX(T(0), width - 2 * aDx);
- height = NS_MAX(T(0), height - 2 * aDy);
+ width = gfx_max(T(0), width - 2 * aDx);
+ height = gfx_max(T(0), height - 2 * aDy);
}
void Deflate(const Margin& aMargin)
{
x += aMargin.left;
y += aMargin.top;
- width = NS_MAX(T(0), width - aMargin.LeftRight());
- height = NS_MAX(T(0), height - aMargin.TopBottom());
+ width = gfx_max(T(0), width - aMargin.LeftRight());
+ height = gfx_max(T(0), height - aMargin.TopBottom());
}
void Deflate(const SizeT& aSize) { Deflate(aSize.width, aSize.height); }
@@ -303,10 +318,10 @@ struct BaseRect {
// Note: this can turn an empty rectangle into a non-empty rectangle
void ScaleRoundOut(double aXScale, double aYScale)
{
- T right = static_cast(NS_ceil(double(XMost()) * aXScale));
- T bottom = static_cast(NS_ceil(double(YMost()) * aYScale));
- x = static_cast(NS_floor(double(x) * aXScale));
- y = static_cast(NS_floor(double(y) * aYScale));
+ T right = static_cast(ceil(double(XMost()) * aXScale));
+ T bottom = static_cast(ceil(double(YMost()) * aYScale));
+ x = static_cast(floor(double(x) * aXScale));
+ y = static_cast(floor(double(y) * aYScale));
width = right - x;
height = bottom - y;
}
@@ -318,12 +333,12 @@ struct BaseRect {
// unrounded result.
void ScaleRoundIn(double aXScale, double aYScale)
{
- T right = static_cast(NS_floor(double(XMost()) * aXScale));
- T bottom = static_cast(NS_floor(double(YMost()) * aYScale));
- x = static_cast(NS_ceil(double(x) * aXScale));
- y = static_cast(NS_ceil(double(y) * aYScale));
- width = NS_MAX(0, right - x);
- height = NS_MAX(0, bottom - y);
+ T right = static_cast(floor(double(XMost()) * aXScale));
+ T bottom = static_cast(floor(double(YMost()) * aYScale));
+ x = static_cast(ceil(double(x) * aXScale));
+ y = static_cast(ceil(double(y) * aYScale));
+ width = gfx_max(0, right - x);
+ height = gfx_max(0, bottom - y);
}
// Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
// the smallest integer-coordinate rectangle containing the unrounded result.
@@ -350,6 +365,7 @@ private:
bool operator!=(const Sub& aRect) const { return false; }
};
+}
}
-#endif /* MOZILLA_BASERECT_H_ */
+#endif /* MOZILLA_GFX_BASERECT_H_ */
diff --git a/gfx/src/BaseSize.h b/gfx/2d/BaseSize.h
similarity index 96%
rename from gfx/src/BaseSize.h
rename to gfx/2d/BaseSize.h
index f995b337261..0751887af53 100644
--- a/gfx/src/BaseSize.h
+++ b/gfx/2d/BaseSize.h
@@ -35,10 +35,11 @@
*
* ***** END LICENSE BLOCK ***** */
-#ifndef MOZILLA_BASESIZE_H_
-#define MOZILLA_BASESIZE_H_
+#ifndef MOZILLA_GFX_BASESIZE_H_
+#define MOZILLA_GFX_BASESIZE_H_
namespace mozilla {
+namespace gfx {
/**
* Do not use this class directly. Subclass it, pass that subclass as the
@@ -96,6 +97,7 @@ struct BaseSize {
}
};
+}
}
-#endif /* MOZILLA_BASESIZE_H_ */
+#endif /* MOZILLA_GFX_BASESIZE_H_ */
diff --git a/gfx/2d/DrawTargetCG.cpp b/gfx/2d/DrawTargetCG.cpp
new file mode 100644
index 00000000000..6e7d91e36b3
--- /dev/null
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bas Schouten
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+#include "DrawTargetCG.h"
+#include "SourceSurfaceCG.h"
+#include "Rect.h"
+
+//CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
+
+namespace mozilla {
+namespace gfx {
+
+static CGRect RectToCGRect(Rect r)
+{
+ return CGRectMake(r.x, r.y, r.width, r.height);
+}
+
+CGBlendMode ToBlendMode(CompositionOp op)
+{
+ CGBlendMode mode;
+ switch (op) {
+ case OP_OVER:
+ mode = kCGBlendModeNormal;
+ break;
+ case OP_SOURCE:
+ mode = kCGBlendModeCopy;
+ break;
+ case OP_CLEAR:
+ mode = kCGBlendModeClear;
+ break;
+ case OP_ADD:
+ mode = kCGBlendModePlusLighter;
+ break;
+ case OP_ATOP:
+ mode = kCGBlendModeSourceAtop;
+ break;
+ default:
+ mode = kCGBlendModeNormal;
+ }
+ return mode;
+}
+
+
+
+DrawTargetCG::DrawTargetCG()
+{
+}
+
+DrawTargetCG::~DrawTargetCG()
+{
+}
+
+TemporaryRef
+DrawTargetCG::Snapshot()
+{
+ return NULL;
+}
+
+TemporaryRef
+DrawTargetCG::CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+{
+ RefPtr newSurf = new SourceSurfaceCG();
+
+ if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
+ return NULL;
+ }
+
+ return newSurf;
+}
+
+TemporaryRef
+DrawTargetCG::OptimizeSourceSurface(SourceSurface *aSurface) const
+{
+ return NULL;
+}
+
+void
+DrawTargetCG::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawOptions &aOptions,
+ const DrawSurfaceOptions &aSurfOptions)
+{
+ CGImageRef image;
+ CGImageRef subimage = NULL;
+ if (aSurface->GetType() == COREGRAPHICS_IMAGE) {
+ image = static_cast(aSurface)->GetImage();
+ /* we have two options here:
+ * - create a subimage -- this is slower
+ * - fancy things with clip and different dest rects */
+ {
+ subimage = CGImageCreateWithImageInRect(image, RectToCGRect(aSource));
+ image = subimage;
+ }
+
+ CGContextDrawImage(mCg, RectToCGRect(aDest), image);
+
+ CGImageRelease(subimage);
+ }
+}
+
+void
+DrawTargetCG::FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ //XXX: it would be nice to hang a CGColor off of the pattern here
+ if (aPattern.GetType() == COLOR) {
+ Color color = static_cast(&aPattern)->mColor;
+ //XXX: the m prefixes are painful here
+ CGContextSetRGBFillColor(mCg, color.mR, color.mG, color.mB, color.mA);
+ }
+
+ CGContextSetBlendMode(mCg, ToBlendMode(aOptions.mCompositionOp));
+ CGContextFillRect(mCg, RectToCGRect(aRect));
+}
+
+
+bool
+DrawTargetCG::Init(const IntSize &aSize)
+{
+ CGColorSpaceRef cgColorspace;
+ cgColorspace = CGColorSpaceCreateDeviceRGB();
+
+ mSize = aSize;
+
+ int bitsPerComponent = 8;
+ int stride = mSize.width;
+
+ CGBitmapInfo bitinfo;
+
+ bitinfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
+
+ // XXX: mWidth is ugly
+ mCg = CGBitmapContextCreate (NULL,
+ mSize.width,
+ mSize.height,
+ bitsPerComponent,
+ stride,
+ cgColorspace,
+ bitinfo);
+
+ CGColorSpaceRelease (cgColorspace);
+
+ return true;
+}
+}
+}
diff --git a/gfx/2d/DrawTargetCG.h b/gfx/2d/DrawTargetCG.h
new file mode 100644
index 00000000000..d6908f91751
--- /dev/null
+++ b/gfx/2d/DrawTargetCG.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jeff Muizelaar
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#pragma once
+
+#include
+
+#include "2D.h"
+#include "Rect.h"
+namespace mozilla {
+namespace gfx {
+
+class DrawTargetCG : public DrawTarget
+{
+public:
+ DrawTargetCG();
+ virtual ~DrawTargetCG();
+
+ virtual BackendType GetType() const { return COREGRAPHICS; }
+ virtual TemporaryRef Snapshot();
+
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawOptions &aOptions = DrawOptions(),
+ const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions());
+
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions());
+
+
+ bool Init(const IntSize &aSize);
+ bool Init(CGContextRef cgContext, const IntSize &aSize);
+
+ /* This is for creating good compatible surfaces */
+ virtual TemporaryRef CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const;
+ virtual TemporaryRef OptimizeSourceSurface(SourceSurface *aSurface) const;
+private:
+ bool InitCGRenderTarget();
+
+ IntSize mSize;
+ CGContextRef mCg;
+
+};
+
+}
+}
diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp
new file mode 100644
index 00000000000..8687e5ba0f0
--- /dev/null
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "DrawTargetCairo.h"
+#include "SourceSurfaceCairo.h"
+
+#include "cairo/cairo.h"
+
+namespace mozilla {
+namespace gfx {
+
+cairo_operator_t
+GfxOpToCairoOp(CompositionOp op)
+{
+ switch (op)
+ {
+ case OP_OVER:
+ return CAIRO_OPERATOR_OVER;
+ case OP_SOURCE:
+ return CAIRO_OPERATOR_SOURCE;
+ case OP_ADD:
+ return CAIRO_OPERATOR_ADD;
+ case OP_ATOP:
+ return CAIRO_OPERATOR_ATOP;
+ case OP_COUNT:
+ break;
+ }
+
+ return CAIRO_OPERATOR_OVER;
+}
+
+cairo_filter_t
+GfxFilterToCairoFilter(Filter filter)
+{
+ switch (filter)
+ {
+ case FILTER_LINEAR:
+ return CAIRO_FILTER_BILINEAR;
+ case FILTER_POINT:
+ return CAIRO_FILTER_NEAREST;
+ }
+
+ return CAIRO_FILTER_BILINEAR;
+}
+
+cairo_format_t
+GfxFormatToCairoFormat(SurfaceFormat format)
+{
+ switch (format)
+ {
+ case FORMAT_B8G8R8A8:
+ return CAIRO_FORMAT_ARGB32;
+ case FORMAT_B8G8R8X8:
+ return CAIRO_FORMAT_RGB24;
+ case FORMAT_A8:
+ return CAIRO_FORMAT_A8;
+ }
+
+ return CAIRO_FORMAT_ARGB32;
+}
+
+void
+GfxMatrixToCairoMatrix(const Matrix& mat, cairo_matrix_t& retval)
+{
+ cairo_matrix_init(&retval, mat._11, mat._12, mat._21, mat._22, mat._31, mat._32);
+}
+
+DrawTargetCairo::DrawTargetCairo()
+ : mContext(NULL)
+{
+}
+
+DrawTargetCairo::~DrawTargetCairo()
+{
+ cairo_destroy(mContext);
+}
+
+TemporaryRef
+DrawTargetCairo::Snapshot()
+{
+ return NULL;
+}
+
+void
+DrawTargetCairo::Flush()
+{
+ cairo_surface_t* surf = cairo_get_target(mContext);
+ cairo_surface_flush(surf);
+}
+
+void
+DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions)
+{
+ float sx = aSource.Width() / aDest.Width();
+ float sy = aSource.Height() / aDest.Height();
+
+ cairo_matrix_t src_mat;
+ cairo_matrix_init_scale(&src_mat, sx, sy);
+ cairo_matrix_translate(&src_mat, -aSource.X(), -aSource.Y());
+
+ cairo_surface_t* surf = NULL;
+ if (aSurface->GetType() == SURFACE_CAIRO) {
+ surf = static_cast(aSurface)->GetSurface();
+ }
+
+ cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf);
+ cairo_pattern_set_matrix(pat, &src_mat);
+ cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(aSurfOptions.mFilter));
+
+ cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
+ cairo_rectangle(mContext, aDest.X(), aDest.Y(),
+ aDest.Width(), aDest.Height());
+ cairo_fill(mContext);
+
+ cairo_pattern_destroy(pat);
+}
+
+void
+DrawTargetCairo::FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+{
+ cairo_new_path(mContext);
+ cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height());
+
+ cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
+
+ if (aPattern.GetType() == PATTERN_COLOR) {
+ Color color = static_cast(aPattern).mColor;
+ cairo_set_source_rgba(mContext, color.r, color.g,
+ color.b, color.a);
+ }
+
+ cairo_fill(mContext);
+}
+
+TemporaryRef
+DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const
+{
+ cairo_surface_t* surf = cairo_image_surface_create_for_data(aData,
+ GfxFormatToCairoFormat(aFormat),
+ aSize.width,
+ aSize.height,
+ aStride);
+ RefPtr source_surf = new SourceSurfaceCairo();
+ source_surf->InitFromSurface(surf, aSize, aFormat);
+ cairo_surface_destroy(surf);
+ return source_surf;
+}
+
+TemporaryRef
+DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
+{
+ return NULL;
+}
+
+TemporaryRef
+DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+{
+ return NULL;
+}
+
+bool
+DrawTargetCairo::Init(cairo_surface_t* aSurface)
+{
+ mContext = cairo_create(aSurface);
+
+ return true;
+}
+
+void
+DrawTargetCairo::SetTransform(const Matrix& aTransform)
+{
+ cairo_matrix_t mat;
+ GfxMatrixToCairoMatrix(aTransform, mat);
+ cairo_set_matrix(mContext, &mat);
+ mTransform = aTransform;
+}
+
+}
+}
diff --git a/gfx/2d/DrawTargetCairo.h b/gfx/2d/DrawTargetCairo.h
new file mode 100644
index 00000000000..3a69fe98183
--- /dev/null
+++ b/gfx/2d/DrawTargetCairo.h
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
+#define _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
+
+#include "2D.h"
+#include "cairo/cairo.h"
+
+namespace mozilla {
+namespace gfx {
+
+class DrawTargetCairo : public DrawTarget
+{
+public:
+ DrawTargetCairo();
+ virtual ~DrawTargetCairo();
+
+ virtual BackendType GetType() const { return BACKEND_CAIRO; }
+ virtual TemporaryRef Snapshot();
+ virtual IntSize GetSize() { return IntSize(); }
+
+ virtual void Flush();
+ virtual void DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma)
+ { }
+
+ virtual void ClearRect(const Rect &aRect)
+ { }
+
+ virtual void CopySurface(SourceSurface *aSurface,
+ const IntRect &aSourceRect,
+ const IntPoint &aDestination)
+ { }
+
+ virtual void FillRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions());
+ virtual void StrokeRect(const Rect &aRect,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions())
+ { return; }
+ virtual void StrokeLine(const Point &aStart,
+ const Point &aEnd,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions())
+ { return; }
+
+ virtual void Stroke(const Path *aPath,
+ const Pattern &aPattern,
+ const StrokeOptions &aStrokeOptions = StrokeOptions(),
+ const DrawOptions &aOptions = DrawOptions())
+ { return; }
+
+ virtual void Fill(const Path *aPath,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions = DrawOptions())
+ { return; }
+
+ virtual void FillGlyphs(ScaledFont *aFont,
+ const GlyphBuffer &aBuffer,
+ const Pattern &aPattern,
+ const DrawOptions &aOptions)
+ { return; }
+
+ virtual void PushClip(const Path *aPath) { }
+ virtual void PopClip() { }
+
+ virtual TemporaryRef CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const { return NULL; }
+
+ virtual TemporaryRef CreateSourceSurfaceFromData(unsigned char *aData,
+ const IntSize &aSize,
+ int32_t aStride,
+ SurfaceFormat aFormat) const;
+ virtual TemporaryRef OptimizeSourceSurface(SourceSurface *aSurface) const;
+ virtual TemporaryRef
+ CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const;
+ virtual TemporaryRef
+ CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+ { return NULL; }
+
+ virtual TemporaryRef CreateGradientStops(GradientStop *aStops, uint32_t aNumStops) const
+ { return NULL; }
+
+ virtual void *GetNativeSurface(NativeSurfaceType aType)
+ { return NULL; }
+
+ virtual void SetTransform(const Matrix& aTransform);
+
+ bool Init(cairo_surface_t* aSurface);
+
+private:
+
+ cairo_t* mContext;
+};
+
+}
+}
+
+#endif // _MOZILLA_GFX_DRAWTARGET_CAIRO_H_
diff --git a/gfx/2d/DrawTargetD2D.cpp b/gfx/2d/DrawTargetD2D.cpp
new file mode 100644
index 00000000000..5d80061f4b4
--- /dev/null
+++ b/gfx/2d/DrawTargetD2D.cpp
@@ -0,0 +1,1679 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bas Schouten
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "DrawTargetD2D.h"
+#include "SourceSurfaceD2D.h"
+#include "SourceSurfaceD2DTarget.h"
+#include "ShadersD2D.h"
+#include "PathD2D.h"
+#include "GradientStopsD2D.h"
+#include "ScaledFontDWrite.h"
+#include "Logging.h"
+#include "Tools.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
+ __in D2D1_FACTORY_TYPE factoryType,
+ __in REFIID iid,
+ __in_opt CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
+ __out void **factory
+);
+
+typedef HRESULT (WINAPI*D3D10CreateEffectFromMemoryFunc)(
+ __in void *pData,
+ __in SIZE_T DataLength,
+ __in UINT FXFlags,
+ __in ID3D10Device *pDevice,
+ __in ID3D10EffectPool *pEffectPool,
+ __out ID3D10Effect **ppEffect
+);
+
+namespace mozilla {
+namespace gfx {
+
+struct Vertex {
+ float x;
+ float y;
+};
+
+ID2D1Factory *DrawTargetD2D::mFactory;
+
+// Helper class to restore surface contents that was clipped out but may have
+// been altered by a drawing call.
+class AutoSaveRestoreClippedOut
+{
+public:
+ AutoSaveRestoreClippedOut(DrawTargetD2D *aDT)
+ : mDT(aDT)
+ {}
+
+ void Save() {
+ if (!mDT->mPushedClips.size()) {
+ return;
+ }
+
+ mDT->Flush();
+
+ RefPtr tmpTexture;
+ IntSize size = mDT->mSize;
+ SurfaceFormat format = mDT->mFormat;
+
+ CD3D10_TEXTURE2D_DESC desc(DXGIFormat(format), size.width, size.height,
+ 1, 1);
+ desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
+
+ HRESULT hr = mDT->mDevice->CreateTexture2D(&desc, NULL, byRef(tmpTexture));
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create temporary texture to hold surface data.";
+ }
+ mDT->mDevice->CopyResource(tmpTexture, mDT->mTexture);
+
+ D2D1_BITMAP_PROPERTIES props =
+ D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(format),
+ AlphaMode(format)));
+
+ RefPtr surf;
+
+ tmpTexture->QueryInterface((IDXGISurface**)byRef(surf));
+
+ hr = mDT->mRT->CreateSharedBitmap(IID_IDXGISurface, surf,
+ &props, byRef(mOldSurfBitmap));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failed to create shared bitmap for old surface.";
+ }
+
+ factory()->CreatePathGeometry(byRef(mClippedArea));
+ RefPtr currentSink;
+ mClippedArea->Open(byRef(currentSink));
+
+ std::vector::iterator iter = mDT->mPushedClips.begin();
+ iter->mPath->GetGeometry()->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+ iter->mTransform, currentSink);
+
+ currentSink->Close();
+
+ iter++;
+ for (;iter != mDT->mPushedClips.end(); iter++) {
+ RefPtr newGeom;
+ factory()->CreatePathGeometry(byRef(newGeom));
+
+ newGeom->Open(byRef(currentSink));
+ mClippedArea->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT,
+ iter->mTransform, currentSink);
+
+ currentSink->Close();
+
+ mClippedArea = newGeom;
+ }
+ }
+
+ ID2D1Factory *factory() { return mDT->factory(); }
+
+ ~AutoSaveRestoreClippedOut()
+ {
+ if (!mOldSurfBitmap) {
+ return;
+ }
+
+ ID2D1RenderTarget *rt = mDT->mRT;
+
+ // Write the area that was clipped out back to the surface. This all
+ // happens in device space.
+ rt->SetTransform(D2D1::IdentityMatrix());
+ mDT->mTransformDirty = true;
+
+ RefPtr rectGeom;
+ factory()->CreateRectangleGeometry(D2D1::InfiniteRect(), byRef(rectGeom));
+
+ RefPtr invClippedArea;
+ factory()->CreatePathGeometry(byRef(invClippedArea));
+ RefPtr sink;
+ invClippedArea->Open(byRef(sink));
+
+ HRESULT hr = rectGeom->CombineWithGeometry(mClippedArea, D2D1_COMBINE_MODE_EXCLUDE,
+ NULL, sink);
+ sink->Close();
+
+ RefPtr brush;
+ rt->CreateBitmapBrush(mOldSurfBitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(), byRef(brush));
+
+ rt->FillGeometry(invClippedArea, brush);
+ }
+
+private:
+
+ DrawTargetD2D *mDT;
+
+ // If we have an operator unbound by the source, this will contain a bitmap
+ // with the old dest surface data.
+ RefPtr mOldSurfBitmap;
+ // This contains the area drawing is clipped to.
+ RefPtr mClippedArea;
+};
+
+DrawTargetD2D::DrawTargetD2D()
+ : mClipsArePushed(false)
+ , mPrivateData(NULL)
+{
+}
+
+DrawTargetD2D::~DrawTargetD2D()
+{
+ if (mRT) {
+ PopAllClips();
+
+ mRT->EndDraw();
+ }
+ if (mTempRT) {
+ mTempRT->EndDraw();
+ }
+}
+
+/*
+ * DrawTarget Implementation
+ */
+TemporaryRef
+DrawTargetD2D::Snapshot()
+{
+ RefPtr newSurf = new SourceSurfaceD2DTarget();
+
+ newSurf->mFormat = mFormat;
+ newSurf->mTexture = mTexture;
+ newSurf->mDrawTarget = this;
+
+ mSnapshots.push_back(newSurf);
+
+ Flush();
+
+ return newSurf;
+}
+
+void
+DrawTargetD2D::Flush()
+{
+ PopAllClips();
+
+ HRESULT hr = mRT->Flush();
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Error reported when trying to flush D2D rendertarget. Code: " << hr;
+ }
+}
+
+void
+DrawTargetD2D::DrawSurface(SourceSurface *aSurface,
+ const Rect &aDest,
+ const Rect &aSource,
+ const DrawSurfaceOptions &aSurfOptions,
+ const DrawOptions &aOptions)
+{
+ RefPtr bitmap;
+
+ ID2D1RenderTarget *rt = GetRTForOperator(aOptions.mCompositionOp);
+
+ PrepareForDrawing(rt);
+
+ Rect srcRect = aSource;
+
+ switch (aSurface->GetType()) {
+
+ case SURFACE_D2D1_BITMAP:
+ {
+ SourceSurfaceD2D *srcSurf = static_cast(aSurface);
+ bitmap = srcSurf->GetBitmap();
+
+ if (!bitmap) {
+ if (aSource.width > rt->GetMaximumBitmapSize() ||
+ aSource.height > rt->GetMaximumBitmapSize()) {
+ gfxDebug() << "Bitmap source larger than texture size specified. DrawBitmap will silently fail.";
+ // Don't know how to deal with this yet.
+ return;
+ }
+
+ int stride = srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat());
+
+ unsigned char *data = &srcSurf->mRawData.front() +
+ (uint32_t)aSource.y * stride +
+ (uint32_t)aSource.x * BytesPerPixel(srcSurf->GetFormat());
+
+ D2D1_BITMAP_PROPERTIES props =
+ D2D1::BitmapProperties(D2D1::PixelFormat(DXGIFormat(srcSurf->GetFormat()), AlphaMode(srcSurf->GetFormat())));
+ mRT->CreateBitmap(D2D1::SizeU(UINT32(aSource.width), UINT32(aSource.height)), data, stride, props, byRef(bitmap));
+
+ srcRect.x -= (uint32_t)aSource.x;
+ srcRect.y -= (uint32_t)aSource.y;
+ }
+ }
+ break;
+ case SURFACE_D2D1_DRAWTARGET:
+ {
+ SourceSurfaceD2DTarget *srcSurf = static_cast(aSurface);
+ bitmap = srcSurf->GetBitmap(mRT);
+
+ if (!srcSurf->IsCopy()) {
+ srcSurf->mDrawTarget->mDependentTargets.push_back(this);
+ }
+ }
+ break;
+ }
+
+ rt->DrawBitmap(bitmap, D2DRect(aDest), aOptions.mAlpha, D2DFilter(aSurfOptions.mFilter), D2DRect(srcRect));
+
+ FinalizeRTForOperator(aOptions.mCompositionOp, aDest);
+}
+
+void
+DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
+ const Point &aDest,
+ const Color &aColor,
+ const Point &aOffset,
+ Float aSigma)
+{
+ RefPtr srView = NULL;
+ if (aSurface->GetType() != SURFACE_D2D1_DRAWTARGET) {
+ return;
+ }
+
+ Flush();
+
+ srView = static_cast(aSurface)->GetSRView();
+
+ EnsureViews();
+
+ if (!mTempRTView) {
+ // This view is only needed in this path.
+ HRESULT hr = mDevice->CreateRenderTargetView(mTempTexture, NULL, byRef(mTempRTView));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failure to create RenderTargetView. Code: " << hr;
+ return;
+ }
+ }
+
+ RefPtr destRTView = mRTView;
+ RefPtr destTexture;
+ HRESULT hr;
+
+ if (mPushedClips.size()) {
+ // We need to take clips into account, draw into a temporary surface, which
+ // we then blend back with the proper clips set, using D2D.
+ CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
+ mSize.width, mSize.height,
+ 1, 1);
+ desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
+
+ hr = mDevice->CreateTexture2D(&desc, NULL, byRef(destTexture));
+ if (FAILED(hr)) {
+ gfxWarning() << "Failure to create temporary texture. Size: " << mSize << " Code: " << hr;
+ return;
+ }
+
+ hr = mDevice->CreateRenderTargetView(destTexture, NULL, byRef(destRTView));
+ if (FAILED(hr)) {
+ gfxWarning() << "Failure to create RenderTargetView. Code: " << hr;
+ return;
+ }
+
+ float color[4] = { 0, 0, 0, 0 };
+ mDevice->ClearRenderTargetView(destRTView, color);
+ }
+
+
+ IntSize srcSurfSize;
+ ID3D10RenderTargetView *rtViews;
+ D3D10_VIEWPORT viewport;
+
+ UINT stride = sizeof(Vertex);
+ UINT offset = 0;
+ ID3D10Buffer *buff = mPrivateData->mVB;
+
+ mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset);
+ mDevice->IASetInputLayout(mPrivateData->mInputLayout);
+
+ mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()->
+ SetFloatVector(ShaderConstantRectD3D10(-1.0f, 1.0f, 2.0f, -2.0f));
+ mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()->
+ SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
+
+ // If we create a downsampled source surface we need to correct aOffset for that.
+ Point correctedOffset = aOffset + aDest;
+
+ // The 'practical' scaling factors.
+ Float dsFactorX = 1.0f;
+ Float dsFactorY = 1.0f;
+
+ if (aSigma > 1.7f) {
+ // In this case 9 samples of our original will not cover it. Generate the
+ // mip levels for the original and create a downsampled version from
+ // them. We generate a version downsampled so that a kernel for a sigma
+ // of 1.7 will produce the right results.
+ float blurWeights[9] = { 0.234671f, 0.197389f, 0.197389f, 0.117465f, 0.117465f, 0.049456f, 0.049456f, 0.014732f, 0.014732f };
+ mPrivateData->mEffect->GetVariableByName("BlurWeights")->SetRawValue(blurWeights, 0, sizeof(blurWeights));
+
+ CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
+ aSurface->GetSize().width,
+ aSurface->GetSize().height);
+ desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
+ desc.MiscFlags = D3D10_RESOURCE_MISC_GENERATE_MIPS;
+
+ RefPtr mipTexture;
+ hr = mDevice->CreateTexture2D(&desc, NULL, byRef(mipTexture));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failure to create temporary texture. Size: " <<
+ aSurface->GetSize() << " Code: " << hr;
+ return;
+ }
+
+ IntSize dsSize = IntSize(int32_t(aSurface->GetSize().width * (1.7f / aSigma)),
+ int32_t(aSurface->GetSize().height * (1.7f / aSigma)));
+
+ if (dsSize.width < 1) {
+ dsSize.width = 1;
+ }
+ if (dsSize.height < 1) {
+ dsSize.height = 1;
+ }
+
+ dsFactorX = dsSize.width / Float(aSurface->GetSize().width);
+ dsFactorY = dsSize.height / Float(aSurface->GetSize().height);
+ correctedOffset.x *= dsFactorX;
+ correctedOffset.y *= dsFactorY;
+
+ desc = CD3D10_TEXTURE2D_DESC(DXGI_FORMAT_B8G8R8A8_UNORM,
+ dsSize.width,
+ dsSize.height, 1, 1);
+ desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
+ RefPtr tmpDSTexture;
+ hr = mDevice->CreateTexture2D(&desc, NULL, byRef(tmpDSTexture));
+
+ if (FAILED(hr)) {
+ gfxWarning() << "Failure to create temporary texture. Size: " << dsSize << " Code: " << hr;
+ return;
+ }
+
+ D3D10_BOX box;
+ box.left = box.top = box.front = 0;
+ box.back = 1;
+ box.right = aSurface->GetSize().width;
+ box.bottom = aSurface->GetSize().height;
+ mDevice->CopySubresourceRegion(mipTexture, 0, 0, 0, 0, static_cast(aSurface)->mTexture, 0, &box);
+
+ mDevice->CreateShaderResourceView(mipTexture, NULL, byRef(srView));
+ mDevice->GenerateMips(srView);
+
+ RefPtr dsRTView;
+ RefPtr dsSRView;
+ mDevice->CreateRenderTargetView(tmpDSTexture, NULL, byRef(dsRTView));
+ mDevice->CreateShaderResourceView(tmpDSTexture, NULL, byRef(dsSRView));
+
+ rtViews = dsRTView;
+ mDevice->OMSetRenderTargets(1, &rtViews, NULL);
+
+ viewport.MaxDepth = 1;
+ viewport.MinDepth = 0;
+ viewport.Height = dsSize.height;
+ viewport.Width = dsSize.width;
+ viewport.TopLeftX = 0;
+ viewport.TopLeftY = 0;
+
+ mDevice->RSSetViewports(1, &viewport);
+ mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView);
+ mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->
+ GetPassByIndex(0)->Apply(0);
+
+ mDevice->OMSetBlendState(GetBlendStateForOperator(OP_OVER), NULL, 0xffffffff);
+
+ mDevice->Draw(4, 0);
+
+ srcSurfSize = dsSize;
+
+ srView = dsSRView;
+ } else {
+ // In this case generate a kernel to draw the blur directly to the temp
+ // surf in one direction and to final in the other.
+ float blurWeights[9];
+
+ float normalizeFactor = 1.0f;
+ if (aSigma != 0) {
+ normalizeFactor = 1.0f / Float(sqrt(2 * M_PI * pow(aSigma, 2)));
+ }
+
+ blurWeights[0] = normalizeFactor;
+
+ // XXX - We should actually optimize for Sigma = 0 here. We could use a
+ // much simpler shader and save a lot of texture lookups.
+ for (int i = 1; i < 9; i += 2) {
+ if (aSigma != 0) {
+ blurWeights[i] = blurWeights[i + 1] = normalizeFactor *
+ exp(-pow(float((i + 1) / 2), 2) / (2 * pow(aSigma, 2)));
+ } else {
+ blurWeights[i] = blurWeights[i + 1] = 0;
+ }
+ }
+
+ mPrivateData->mEffect->GetVariableByName("BlurWeights")->SetRawValue(blurWeights, 0, sizeof(blurWeights));
+
+ viewport.MaxDepth = 1;
+ viewport.MinDepth = 0;
+ viewport.Height = aSurface->GetSize().height;
+ viewport.Width = aSurface->GetSize().width;
+ viewport.TopLeftX = 0;
+ viewport.TopLeftY = 0;
+
+ mDevice->RSSetViewports(1, &viewport);
+
+ srcSurfSize = aSurface->GetSize();
+ }
+
+ // We may need to draw to a different intermediate surface if our temp
+ // texture isn't big enough.
+ bool needBiggerTemp = srcSurfSize.width > mSize.width ||
+ srcSurfSize.height > mSize.height;
+
+ RefPtr tmpRTView;
+ RefPtr tmpSRView;
+ RefPtr