-1) {
- Services.console.unregisterListener(consoleListener);
ok(true, "drop was blocked");
executeSoon(finish);
}
}
}
Services.console.registerListener(consoleListener);
+ registerCleanupFunction(function () {
+ Services.console.unregisterListener(consoleListener);
+ });
// The drop handler throws an exception when dragging URIs that inherit
// principal, e.g. javascript:
diff --git a/browser/branding/official/configure.sh b/browser/branding/official/configure.sh
index 4d3d2973770..55884683d15 100644
--- a/browser/branding/official/configure.sh
+++ b/browser/branding/official/configure.sh
@@ -1,2 +1 @@
MOZ_APP_DISPLAYNAME=Firefox
-MOZ_UA_BUILDID=20100101
diff --git a/browser/components/sessionstore/src/nsSessionStartup.js b/browser/components/sessionstore/src/nsSessionStartup.js
index d399232b89d..3aed091278c 100644
--- a/browser/components/sessionstore/src/nsSessionStartup.js
+++ b/browser/components/sessionstore/src/nsSessionStartup.js
@@ -72,6 +72,7 @@ const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource:///modules/TelemetryStopwatch.jsm");
const STATE_RUNNING_STR = "running";
const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100 megabytes
@@ -127,23 +128,30 @@ SessionStartup.prototype = {
return;
// parse the session state into a JS object
+ // remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0)
+ if (iniString.charAt(0) == '(')
+ iniString = iniString.slice(1, -1);
+ let corruptFile = false;
try {
- // remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0)
- if (iniString.charAt(0) == '(')
- iniString = iniString.slice(1, -1);
+ this._initialState = JSON.parse(iniString);
+ }
+ catch (ex) {
+ debug("The session file contained un-parse-able JSON: " + ex);
+ // Try to eval.
+ // evalInSandbox will throw if iniString is not parse-able.
try {
- this._initialState = JSON.parse(iniString);
- }
- catch (exJSON) {
var s = new Cu.Sandbox("about:blank", {sandboxName: 'nsSessionStartup'});
this._initialState = Cu.evalInSandbox("(" + iniString + ")", s);
+ } catch(ex) {
+ debug("The session file contained un-eval-able JSON: " + ex);
+ corruptFile = true;
}
-
- // If this is a normal restore then throw away any previous session
- if (!doResumeSessionOnce)
- delete this._initialState.lastSessionState;
}
- catch (ex) { debug("The session file is invalid: " + ex); }
+ Services.telemetry.getHistogramById("FX_SESSION_RESTORE_CORRUPT_FILE").add(corruptFile);
+
+ // If this is a normal restore then throw away any previous session
+ if (!doResumeSessionOnce)
+ delete this._initialState.lastSessionState;
let resumeFromCrash = prefBranch.getBoolPref("sessionstore.resume_from_crash");
let lastSessionCrashed =
@@ -154,8 +162,7 @@ SessionStartup.prototype = {
// Report shutdown success via telemetry. Shortcoming here are
// being-killed-by-OS-shutdown-logic, shutdown freezing after
// session restore was written, etc.
- let Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
- Telemetry.getHistogramById("SHUTDOWN_OK").add(!lastSessionCrashed);
+ Services.telemetry.getHistogramById("SHUTDOWN_OK").add(!lastSessionCrashed);
// set the startup type
if (lastSessionCrashed && resumeFromCrash)
@@ -296,9 +303,11 @@ SessionStartup.prototype = {
* @returns a session state string
*/
_readStateFile: function sss_readStateFile(aFile) {
+ TelemetryStopwatch.start("FX_SESSION_RESTORE_READ_FILE_MS");
var stateString = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
stateString.data = this._readFile(aFile) || "";
+ TelemetryStopwatch.finish("FX_SESSION_RESTORE_READ_FILE_MS");
Services.obs.notifyObservers(stateString, "sessionstore-state-read", "");
diff --git a/browser/components/sessionstore/src/nsSessionStore.js b/browser/components/sessionstore/src/nsSessionStore.js
index d933d4bcb9d..f7599b75b53 100644
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -131,6 +131,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/debug.js");
Cu.import("resource:///modules/TelemetryTimestamps.jsm");
+Cu.import("resource:///modules/TelemetryStopwatch.jsm");
XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
Cu.import("resource://gre/modules/NetUtil.jsm");
@@ -3653,6 +3654,8 @@ SessionStoreService.prototype = {
// if we crash.
let pinnedOnly = this._loadState == STATE_RUNNING && !this._resume_from_crash;
+ TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_DATA_MS");
+
var oState = this._getCurrentState(aUpdateAll, pinnedOnly);
if (!oState)
return;
@@ -3691,6 +3694,8 @@ SessionStoreService.prototype = {
if (this._lastSessionState)
oState.lastSessionState = this._lastSessionState;
+ TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_DATA_MS");
+
this._saveStateObject(oState);
},
@@ -3698,9 +3703,11 @@ SessionStoreService.prototype = {
* write a state object to disk
*/
_saveStateObject: function sss_saveStateObject(aStateObj) {
+ TelemetryStopwatch.start("FX_SESSION_RESTORE_SERIALIZE_DATA_MS");
var stateString = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
stateString.data = this._toJSONString(aStateObj);
+ TelemetryStopwatch.finish("FX_SESSION_RESTORE_SERIALIZE_DATA_MS");
Services.obs.notifyObservers(stateString, "sessionstore-state-write", "");
@@ -3809,7 +3816,7 @@ SessionStoreService.prototype = {
argString.data = "";
// Build feature string
- let features = "chrome,dialog=no,all";
+ let features = "chrome,dialog=no,macsuppressanimation,all";
let winState = aState.windows[0];
WINDOW_ATTRIBUTES.forEach(function(aFeature) {
// Use !isNaN as an easy way to ignore sizemode and check for numbers
@@ -4427,6 +4434,7 @@ SessionStoreService.prototype = {
* String data
*/
_writeFile: function sss_writeFile(aFile, aData) {
+ TelemetryStopwatch.start("FX_SESSION_RESTORE_WRITE_FILE_MS");
// Initialize the file output stream.
var ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
@@ -4442,6 +4450,7 @@ SessionStoreService.prototype = {
var self = this;
NetUtil.asyncCopy(istream, ostream, function(rc) {
if (Components.isSuccessCode(rc)) {
+ TelemetryStopwatch.finish("FX_SESSION_RESTORE_WRITE_FILE_MS");
Services.obs.notifyObservers(null,
"sessionstore-state-write-complete",
"");
diff --git a/browser/config/mozconfigs/macosx-lion-universal/nightly b/browser/config/mozconfigs/macosx-lion-universal/nightly
new file mode 100644
index 00000000000..da575599dbd
--- /dev/null
+++ b/browser/config/mozconfigs/macosx-lion-universal/nightly
@@ -0,0 +1,24 @@
+. $topsrcdir/build/macosx/universal/mozconfig
+
+# Universal builds override the default of browser (bug 575283 comment 29)
+ac_add_options --enable-application=browser
+
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --enable-update-packaging
+ac_add_options --enable-codesighs
+ac_add_options --disable-install-strip
+
+# Nightlies only since this has a cost in performance
+ac_add_options --enable-js-diagnostics
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+export MOZ_TELEMETRY_REPORTING=1
+mk_add_options MOZ_MAKE_FLAGS="-j12"
+
+ac_add_options --with-macbundlename-prefix=Firefox
+
+# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
+ac_add_options --enable-warnings-as-errors
+ac_add_options --with-ccache
diff --git a/browser/config/mozconfigs/macosx-lion-universal/release b/browser/config/mozconfigs/macosx-lion-universal/release
new file mode 100644
index 00000000000..0acefb03ecd
--- /dev/null
+++ b/browser/config/mozconfigs/macosx-lion-universal/release
@@ -0,0 +1,20 @@
+. $topsrcdir/build/macosx/universal/mozconfig
+
+# Universal builds override the default of browser (bug 575283 comment 29)
+ac_add_options --enable-application=browser
+
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --enable-update-packaging
+ac_add_options --enable-official-branding
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+export MOZ_TELEMETRY_REPORTING=1
+
+# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
+ac_add_options --enable-warnings-as-errors
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
+ac_add_options --with-ccache
diff --git a/browser/config/mozconfigs/macosx-lion-universal/shark b/browser/config/mozconfigs/macosx-lion-universal/shark
new file mode 100644
index 00000000000..48f355dbe1b
--- /dev/null
+++ b/browser/config/mozconfigs/macosx-lion-universal/shark
@@ -0,0 +1,26 @@
+# Just like nightlies, but without tests, not on an update channel, and with
+# shark and dtrace enabled
+. $topsrcdir/build/macosx/universal/mozconfig
+
+# Universal builds override the default of browser (bug 575283 comment 29)
+ac_add_options --enable-application=browser
+
+ac_add_options --disable-tests
+ac_add_options --disable-install-strip
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j12"
+
+# shark specific options
+ac_add_options --enable-shark
+ac_add_options --enable-dtrace
+
+# Need this to prevent name conflicts with the normal nightly build packages
+export MOZ_PKG_SPECIAL="shark"
+
+# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
+ac_add_options --enable-warnings-as-errors
+ac_add_options --with-ccache
diff --git a/browser/config/mozconfigs/macosx32-lion/debug b/browser/config/mozconfigs/macosx32-lion/debug
new file mode 100644
index 00000000000..cc7a05b93b5
--- /dev/null
+++ b/browser/config/mozconfigs/macosx32-lion/debug
@@ -0,0 +1,12 @@
+. $topsrcdir/build/macosx/mozconfig.leopard
+ac_add_options --enable-debug
+ac_add_options --enable-trace-malloc
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j12"
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+ac_add_options --with-macbundlename-prefix=Firefox
+ac_add_options --with-ccache
diff --git a/browser/config/mozconfigs/macosx64-lion/debug b/browser/config/mozconfigs/macosx64-lion/debug
new file mode 100644
index 00000000000..73cbd46a179
--- /dev/null
+++ b/browser/config/mozconfigs/macosx64-lion/debug
@@ -0,0 +1,17 @@
+. $topsrcdir/build/macosx/common
+
+ac_add_options --enable-debug
+ac_add_options --enable-trace-malloc
+ac_add_options --enable-accessibility
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j12"
+
+# Needed to enable breakpad in application.ini
+export MOZILLA_OFFICIAL=1
+
+ac_add_options --with-macbundlename-prefix=Firefox
+
+# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
+ac_add_options --enable-warnings-as-errors
+ac_add_options --with-ccache
diff --git a/browser/config/mozconfigs/macosx64-lion/l10n-mozconfig b/browser/config/mozconfigs/macosx64-lion/l10n-mozconfig
new file mode 100644
index 00000000000..304914322ca
--- /dev/null
+++ b/browser/config/mozconfigs/macosx64-lion/l10n-mozconfig
@@ -0,0 +1,5 @@
+ac_add_options --with-l10n-base=../../l10n-central
+ac_add_options --enable-official-branding
+ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
+ac_add_options --enable-update-packaging
+ac_add_options --with-ccache
diff --git a/browser/devtools/debugger/test/Makefile.in b/browser/devtools/debugger/test/Makefile.in
index 5fded0cdd63..d85d6668285 100644
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -72,6 +72,7 @@ _BROWSER_TEST_FILES = \
browser_dbg_pause-resume.js \
browser_dbg_update-editor-mode.js \
browser_dbg_select-line.js \
+ browser_dbg_clean-exit.js \
head.js \
$(NULL)
diff --git a/browser/devtools/debugger/test/browser_dbg_clean-exit.js b/browser/devtools/debugger/test/browser_dbg_clean-exit.js
new file mode 100644
index 00000000000..9530cc8f4cd
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_clean-exit.js
@@ -0,0 +1,42 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test that closing a tab with the debugger in a paused state exits cleanly.
+
+var gPane = null;
+var gTab = null;
+var gDebuggee = null;
+var gDebugger = null;
+
+const DEBUGGER_TAB_URL = "http://example.com/browser/browser/devtools/" +
+ "debugger/test/" +
+ "browser_dbg_debuggerstatement.html";
+
+function test() {
+ debug_tab_pane(DEBUGGER_TAB_URL, function(aTab, aDebuggee, aPane) {
+ gTab = aTab;
+ gDebuggee = aDebuggee;
+ gPane = aPane;
+ gDebugger = gPane.debuggerWindow;
+
+ testCleanExit();
+ });
+}
+
+function testCleanExit() {
+ gPane.activeThread.addOneTimeListener("framesadded", function() {
+ Services.tm.currentThread.dispatch({ run: function() {
+ is(gDebugger.StackFrames.activeThread.paused, true,
+ "Should be paused after the debugger statement.");
+
+ gPane._client.addOneTimeListener("tabDetached", function () {
+ finish();
+ });
+ removeTab(gTab);
+ }}, 0);
+ });
+
+ gTab.linkedBrowser.contentWindow.wrappedJSObject.runDebuggerStatement();
+}
diff --git a/browser/devtools/debugger/test/browser_dbg_select-line.js b/browser/devtools/debugger/test/browser_dbg_select-line.js
index 57e7b8186b9..885e5786c96 100644
--- a/browser/devtools/debugger/test/browser_dbg_select-line.js
+++ b/browser/devtools/debugger/test/browser_dbg_select-line.js
@@ -43,37 +43,41 @@ function testSelectLine() {
ok(gDebugger.editor.getText().search(/debugger/) != -1,
"The correct script was loaded initially.");
- // getCaretPosition is 0-based.
- is(gDebugger.editor.getCaretPosition().line, 5,
- "The correct line is selected.");
+ // Yield control back to the event loop so that the debugger has a
+ // chance to highlight the proper line.
+ executeSoon(function(){
+ // getCaretPosition is 0-based.
+ is(gDebugger.editor.getCaretPosition().line, 5,
+ "The correct line is selected.");
- gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
- function onChange() {
- gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
- onChange);
- ok(gDebugger.editor.getText().search(/debugger/) == -1,
- "The second script is no longer displayed.");
+ gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+ function onChange() {
+ gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+ onChange);
+ ok(gDebugger.editor.getText().search(/debugger/) == -1,
+ "The second script is no longer displayed.");
- ok(gDebugger.editor.getText().search(/firstCall/) != -1,
- "The first script is displayed.");
+ ok(gDebugger.editor.getText().search(/firstCall/) != -1,
+ "The first script is displayed.");
- // Yield control back to the event loop so that the debugger has a
- // chance to highlight the proper line.
- executeSoon(function(){
- // getCaretPosition is 0-based.
- is(gDebugger.editor.getCaretPosition().line, 4,
- "The correct line is selected.");
+ // Yield control back to the event loop so that the debugger has a
+ // chance to highlight the proper line.
+ executeSoon(function(){
+ // getCaretPosition is 0-based.
+ is(gDebugger.editor.getCaretPosition().line, 4,
+ "The correct line is selected.");
- gDebugger.StackFrames.activeThread.resume(function() {
- removeTab(gTab);
- finish();
+ gDebugger.StackFrames.activeThread.resume(function() {
+ removeTab(gTab);
+ finish();
+ });
});
});
- });
- // Click the oldest stack frame.
- let element = gDebugger.document.getElementById("stackframe-3");
- EventUtils.synthesizeMouseAtCenter(element, {}, gDebugger);
+ // Click the oldest stack frame.
+ let element = gDebugger.document.getElementById("stackframe-3");
+ EventUtils.synthesizeMouseAtCenter(element, {}, gDebugger);
+ });
}}, 0);
});
diff --git a/browser/devtools/highlighter/inspector.jsm b/browser/devtools/highlighter/inspector.jsm
index 60e7f5e3b30..8191e658689 100644
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -763,6 +763,9 @@ InspectorUI.prototype = {
this.boundRuleViewChanged = this.ruleViewChanged.bind(this);
this.ruleView.element.addEventListener("CssRuleViewChanged",
this.boundRuleViewChanged);
+ this.cssRuleViewBoundCSSLinkClicked = this.ruleViewCSSLinkClicked.bind(this);
+ this.ruleView.element.addEventListener("CssRuleViewCSSLinkClicked",
+ this.cssRuleViewBoundCSSLinkClicked);
doc.documentElement.appendChild(this.ruleView.element);
this.ruleView.highlight(this.selection);
@@ -800,6 +803,30 @@ InspectorUI.prototype = {
this.nodeChanged(this.ruleViewObject);
},
+ /**
+ * When a css link is clicked this method is called in order to either:
+ * 1. Open the link in view source (for element style attributes)
+ * 2. Open the link in the style editor
+ *
+ * @param aEvent The event containing the style rule to act on
+ */
+ ruleViewCSSLinkClicked: function(aEvent)
+ {
+ if (!this.chromeWin) {
+ return;
+ }
+
+ let rule = aEvent.detail.rule;
+ let styleSheet = rule.sheet;
+
+ if (styleSheet) {
+ this.chromeWin.StyleEditor.openChrome(styleSheet, rule.ruleLine);
+ } else {
+ let href = rule.elementStyle.element.ownerDocument.location.href;
+ this.chromeWin.openUILinkIn("view-source:" + href, "window");
+ }
+ },
+
/**
* Destroy the rule view.
*/
@@ -811,6 +838,8 @@ InspectorUI.prototype = {
if (this.ruleView) {
this.ruleView.element.removeEventListener("CssRuleViewChanged",
this.boundRuleViewChanged);
+ this.ruleView.element.removeEventListener("CssRuleViewCSSLinkClicked",
+ this.cssRuleViewBoundCSSLinkClicked);
delete boundRuleViewChanged;
this.ruleView.clear();
delete this.ruleView;
diff --git a/browser/devtools/scratchpad/scratchpad.xul b/browser/devtools/scratchpad/scratchpad.xul
index c8a4c7e3f7b..eb3c17b08c2 100644
--- a/browser/devtools/scratchpad/scratchpad.xul
+++ b/browser/devtools/scratchpad/scratchpad.xul
@@ -51,6 +51,7 @@
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&window.title;"
windowtype="devtools:scratchpad"
+ macanimationtype="document"
screenX="4" screenY="4"
width="640" height="480"
persist="screenX screenY width height sizemode">
diff --git a/browser/devtools/sourceeditor/source-editor-orion.jsm b/browser/devtools/sourceeditor/source-editor-orion.jsm
index fb6d485072f..5cb4b0693df 100644
--- a/browser/devtools/sourceeditor/source-editor-orion.jsm
+++ b/browser/devtools/sourceeditor/source-editor-orion.jsm
@@ -78,6 +78,9 @@ const ORION_EVENTS = {
Selection: "Selection",
Focus: "Focus",
Blur: "Blur",
+ MouseOver: "MouseOver",
+ MouseOut: "MouseOut",
+ MouseMove: "MouseMove",
};
/**
diff --git a/browser/devtools/sourceeditor/source-editor.jsm b/browser/devtools/sourceeditor/source-editor.jsm
index e38ac0e2ef2..85d07c85955 100644
--- a/browser/devtools/sourceeditor/source-editor.jsm
+++ b/browser/devtools/sourceeditor/source-editor.jsm
@@ -161,6 +161,30 @@ SourceEditor.EVENTS = {
* The blur event is fired when the editor goes out of focus.
*/
BLUR: "Blur",
+
+ /**
+ * The MouseMove event is sent when the user moves the mouse over a line
+ * annotation. The event object properties:
+ * - event - the DOM mousemove event object.
+ * - x and y - the mouse coordinates relative to the document being edited.
+ */
+ MOUSE_MOVE: "MouseMove",
+
+ /**
+ * The MouseOver event is sent when the mouse pointer enters a line
+ * annotation. The event object properties:
+ * - event - the DOM mouseover event object.
+ * - x and y - the mouse coordinates relative to the document being edited.
+ */
+ MOUSE_OVER: "MouseOver",
+
+ /**
+ * This MouseOut event is sent when the mouse pointer exits a line
+ * annotation. The event object properties:
+ * - event - the DOM mouseout event object.
+ * - x and y - the mouse coordinates relative to the document being edited.
+ */
+ MOUSE_OUT: "MouseOut",
};
/**
diff --git a/browser/devtools/sourceeditor/test/Makefile.in b/browser/devtools/sourceeditor/test/Makefile.in
index f8f390b0bee..081dc57dcbf 100644
--- a/browser/devtools/sourceeditor/test/Makefile.in
+++ b/browser/devtools/sourceeditor/test/Makefile.in
@@ -55,6 +55,7 @@ _BROWSER_TEST_FILES = \
browser_bug687160_line_api.js \
browser_bug650345_find.js \
browser_bug703692_focus_blur.js \
+ browser_bug725388_mouse_events.js \
head.js \
libs:: $(_BROWSER_TEST_FILES)
diff --git a/browser/devtools/sourceeditor/test/browser_bug725388_mouse_events.js b/browser/devtools/sourceeditor/test/browser_bug725388_mouse_events.js
new file mode 100644
index 00000000000..07959127161
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_bug725388_mouse_events.js
@@ -0,0 +1,97 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
+
+let testWin;
+let editor;
+
+function test()
+{
+ waitForExplicitFinish();
+
+ const windowUrl = "data:text/xml," +
+ "";
+ const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+
+ testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
+ testWin.addEventListener("load", function onWindowLoad() {
+ testWin.removeEventListener("load", onWindowLoad, false);
+ waitForFocus(initEditor, testWin);
+ }, false);
+}
+
+function initEditor()
+{
+ let hbox = testWin.document.querySelector("hbox");
+
+ editor = new SourceEditor();
+ editor.init(hbox, {}, editorLoaded);
+}
+
+function editorLoaded()
+{
+ let text = "BrowserBug - 725388";
+ editor.setText(text);
+
+ let target = editor.editorElement;
+ let targetWin = target.ownerDocument.defaultView;
+
+ let mMoveHandler = function(aEvent) {
+ editor.removeEventListener(SourceEditor.EVENTS.MOUSE_MOVE, mMoveHandler);
+
+ is(aEvent.event.type, "mousemove", "MouseMove event fired.");
+
+ editor.addEventListener(SourceEditor.EVENTS.MOUSE_OVER, mOverHandler);
+ waitForFocus(function() {
+ EventUtils.synthesizeMouse(target, 10, 10, {type: "mouseover"},
+ targetWin);
+ });
+ };
+
+ let mOverHandler = function(aEvent) {
+ editor.removeEventListener(SourceEditor.EVENTS.MOUSE_OVER, mOverHandler);
+
+ is(aEvent.event.type, "mouseover", "MouseOver event fired.");
+
+ editor.addEventListener(SourceEditor.EVENTS.MOUSE_OUT, mOutHandler);
+ waitForFocus(function() {
+ EventUtils.synthesizeMouse(target, -10, -10, {type: "mouseout"},
+ targetWin);
+ }, targetWin);
+ };
+
+ let mOutHandler = function(aEvent) {
+ editor.removeEventListener(SourceEditor.EVENTS.MOUSE_OUT, mOutHandler);
+
+ is(aEvent.event.type, "mouseout", "MouseOut event fired.");
+ executeSoon(testEnd);
+ };
+
+ editor.addEventListener(SourceEditor.EVENTS.MOUSE_MOVE, mMoveHandler);
+
+ editor.focus();
+ waitForFocus(function() {
+ EventUtils.synthesizeMouse(target, 1, 1, {type: "mousemove"},
+ targetWin);
+ }, targetWin);
+}
+
+function testEnd()
+{
+ if (editor) {
+ editor.destroy();
+ }
+ if (testWin) {
+ testWin.close();
+ }
+ testWin = editor = null;
+
+ waitForFocus(finish, window);
+}
diff --git a/browser/devtools/styleeditor/StyleEditor.jsm b/browser/devtools/styleeditor/StyleEditor.jsm
index 4eb8241eee2..ff492cb056f 100644
--- a/browser/devtools/styleeditor/StyleEditor.jsm
+++ b/browser/devtools/styleeditor/StyleEditor.jsm
@@ -146,7 +146,7 @@ StyleEditor.prototype = {
*/
get styleSheet()
{
- assert(this._styleSheet, "StyleSheet must be loaded first.")
+ assert(this._styleSheet, "StyleSheet must be loaded first.");
return this._styleSheet;
},
@@ -921,9 +921,11 @@ StyleEditor.prototype = {
aArgs.unshift(this);
}
+ // copy the list of listeners to allow adding/removing listeners in handlers
+ let listeners = this._actionListeners.concat();
// trigger all listeners that have this action handler
- for (let i = 0; i < this._actionListeners.length; ++i) {
- let listener = this._actionListeners[i];
+ for (let i = 0; i < listeners.length; ++i) {
+ let listener = listeners[i];
let actionHandler = listener["on" + aName];
if (actionHandler) {
actionHandler.apply(listener, aArgs);
diff --git a/browser/devtools/styleeditor/StyleEditorChrome.jsm b/browser/devtools/styleeditor/StyleEditorChrome.jsm
index ca57f0208ce..0873941cb44 100644
--- a/browser/devtools/styleeditor/StyleEditorChrome.jsm
+++ b/browser/devtools/styleeditor/StyleEditorChrome.jsm
@@ -270,9 +270,11 @@ StyleEditorChrome.prototype = {
aArgs.unshift(this);
}
- // trigger all listeners that have this named handler
- for (let i = 0; i < this._listeners.length; ++i) {
- let listener = this._listeners[i];
+ // copy the list of listeners to allow adding/removing listeners in handlers
+ let listeners = this._listeners.concat();
+ // trigger all listeners that have this named handler.
+ for (let i = 0; i < listeners.length; i++) {
+ let listener = listeners[i];
let handler = listener["on" + aName];
if (handler) {
handler.apply(listener, aArgs);
@@ -329,10 +331,10 @@ StyleEditorChrome.prototype = {
{
this._resetChrome();
- this._document.title = _("chromeWindowTitle",
- this.contentDocument.title || this.contentDocument.location.href);
-
let document = this.contentDocument;
+ this._document.title = _("chromeWindowTitle",
+ document.title || document.location.href);
+
for (let i = 0; i < document.styleSheets.length; ++i) {
let styleSheet = document.styleSheets[i];
@@ -352,6 +354,79 @@ StyleEditorChrome.prototype = {
}, this);
},
+ /**
+ * selects a stylesheet and optionally moves the cursor to a selected line
+ *
+ * @param {CSSStyleSheet} [aSheet]
+ * Stylesheet that should be selected. If a stylesheet is not passed
+ * and the editor is not initialized we focus the first stylesheet. If
+ * a stylesheet is not passed and the editor is initialized we ignore
+ * the call.
+ * @param {Number} [aLine]
+ * Line to which the caret should be moved (one-indexed).
+ * @param {Number} [aCol]
+ * Column to which the caret should be moved (one-indexed).
+ */
+ selectStyleSheet: function SEC_selectSheet(aSheet, aLine, aCol)
+ {
+ let select = function DEC_select(aEditor) {
+ let summary = aSheet ? this.getSummaryElementForEditor(aEditor)
+ : this._view.getSummaryElementByOrdinal(0);
+ let setCaret = false;
+
+ if (aLine || aCol) {
+ aLine = aLine || 1;
+ aCol = aCol || 1;
+ setCaret = true;
+ }
+ if (!aEditor.sourceEditor) {
+ // If a line or column was specified we move the caret appropriately.
+ if (setCaret) {
+ aEditor.addActionListener({
+ onAttach: function SEC_selectSheet_onAttach()
+ {
+ aEditor.removeActionListener(this);
+ aEditor.sourceEditor.setCaretPosition(aLine - 1, aCol - 1);
+ }
+ });
+ }
+ this._view.activeSummary = summary;
+ } else {
+ this._view.activeSummary = summary;
+
+ // If a line or column was specified we move the caret appropriately.
+ if (setCaret) {
+ aEditor.sourceEditor.setCaretPosition(aLine - 1, aCol - 1);
+ }
+ }
+ }.bind(this);
+
+ if (!this.editors.length) {
+ // We are in the main initialization phase so we wait for the editor
+ // containing the target stylesheet to be added and select the target
+ // stylesheet, optionally moving the cursor to a selected line.
+ this.addChromeListener({
+ onEditorAdded: function SEC_selectSheet_onEditorAdded(aChrome, aEditor) {
+ if ((!aSheet && aEditor.styleSheetIndex == 0) ||
+ aEditor.styleSheet == aSheet) {
+ aChrome.removeChromeListener(this);
+ select(aEditor);
+ }
+ }
+ });
+ } else if (aSheet) {
+ // We are already initialized and a stylesheet has been specified. Here
+ // we iterate through the editors and select the one containing the target
+ // stylesheet, optionally moving the cursor to a selected line.
+ for each (let editor in this.editors) {
+ if (editor.styleSheet == aSheet) {
+ select(editor);
+ break;
+ }
+ }
+ }
+ },
+
/**
* Disable all UI, effectively making editors read-only.
* This is automatically called when no content window is attached.
@@ -455,9 +530,8 @@ StyleEditorChrome.prototype = {
}
}, false);
- // autofocus the first or new stylesheet
- if (editor.styleSheetIndex == 0 ||
- editor.hasFlag(StyleEditorFlags.NEW)) {
+ // autofocus new stylesheets
+ if (editor.hasFlag(StyleEditorFlags.NEW)) {
this._view.activeSummary = aSummary;
}
diff --git a/browser/devtools/styleeditor/styleeditor.xul b/browser/devtools/styleeditor/styleeditor.xul
index f10ae6deda2..e84df003c0f 100644
--- a/browser/devtools/styleeditor/styleeditor.xul
+++ b/browser/devtools/styleeditor/styleeditor.xul
@@ -132,8 +132,10 @@
diff --git a/browser/devtools/styleeditor/test/Makefile.in b/browser/devtools/styleeditor/test/Makefile.in
index f8dcecf7e8f..0b411622473 100644
--- a/browser/devtools/styleeditor/test/Makefile.in
+++ b/browser/devtools/styleeditor/test/Makefile.in
@@ -51,6 +51,7 @@ _BROWSER_TEST_FILES = \
browser_styleeditor_init.js \
browser_styleeditor_loading.js \
browser_styleeditor_new.js \
+ browser_styleeditor_passedinsheet.js \
browser_styleeditor_pretty.js \
browser_styleeditor_readonly.js \
browser_styleeditor_reopen.js \
diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_passedinsheet.js b/browser/devtools/styleeditor/test/browser_styleeditor_passedinsheet.js
new file mode 100644
index 00000000000..05b37019e49
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_passedinsheet.js
@@ -0,0 +1,61 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TESTCASE_URI = TEST_BASE + "simple.html";
+const LINE = 6;
+const COL = 2;
+
+let editor = null;
+let sheet = null;
+
+function test()
+{
+ waitForExplicitFinish();
+ gBrowser.selectedBrowser.addEventListener("load", function () {
+ gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+ run();
+ }, true);
+ content.location = TESTCASE_URI;
+}
+
+function run()
+{
+ sheet = content.document.styleSheets[1];
+ launchStyleEditorChrome(function attachListeners(aChrome) {
+ aChrome.addChromeListener({
+ onEditorAdded: checkSourceEditor
+ });
+ }, sheet, LINE, COL);
+}
+
+function checkSourceEditor(aChrome, aEditor)
+{
+ if (!aEditor.sourceEditor) {
+ aEditor.addActionListener({
+ onAttach: function (aEditor) {
+ aEditor.removeActionListener(this);
+ validate(aEditor);
+ }
+ });
+ } else {
+ validate(aEditor);
+ }
+}
+
+function validate(aEditor)
+{
+ info("validating style editor");
+ let sourceEditor = aEditor.sourceEditor;
+ let caretPosition = sourceEditor.getCaretPosition();
+ is(caretPosition.line, LINE - 1, "caret row is correct"); // index based
+ is(caretPosition.col, COL - 1, "caret column is correct");
+ is(aEditor.styleSheet, sheet, "loaded stylesheet matches document stylesheet");
+ finishUp();
+}
+
+function finishUp()
+{
+ editor = sheet = null;
+ finish();
+}
diff --git a/browser/devtools/styleeditor/test/head.js b/browser/devtools/styleeditor/test/head.js
index f1bfbfbacd9..bc4267a6962 100644
--- a/browser/devtools/styleeditor/test/head.js
+++ b/browser/devtools/styleeditor/test/head.js
@@ -19,9 +19,9 @@ function cleanup()
}
}
-function launchStyleEditorChrome(aCallback)
+function launchStyleEditorChrome(aCallback, aSheet, aLine, aCol)
{
- gChromeWindow = StyleEditor.openChrome();
+ gChromeWindow = StyleEditor.openChrome(aSheet, aLine, aCol);
if (gChromeWindow.document.readyState != "complete") {
gChromeWindow.addEventListener("load", function onChromeLoad() {
gChromeWindow.removeEventListener("load", onChromeLoad, true);
@@ -34,12 +34,12 @@ function launchStyleEditorChrome(aCallback)
}
}
-function addTabAndLaunchStyleEditorChromeWhenLoaded(aCallback)
+function addTabAndLaunchStyleEditorChromeWhenLoaded(aCallback, aSheet, aLine, aCol)
{
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
- launchStyleEditorChrome(aCallback);
+ launchStyleEditorChrome(aCallback, aSheet, aLine, aCol);
}, true);
}
diff --git a/browser/devtools/styleinspector/CssHtmlTree.jsm b/browser/devtools/styleinspector/CssHtmlTree.jsm
index 7e121e1fac4..4915b3f2794 100644
--- a/browser/devtools/styleinspector/CssHtmlTree.jsm
+++ b/browser/devtools/styleinspector/CssHtmlTree.jsm
@@ -273,6 +273,7 @@ CssHtmlTree.prototype = {
this._matchedProperties = null;
if (this.htmlComplete) {
+ this.refreshSourceFilter();
this.refreshPanel();
} else {
if (this._refreshProcess) {
@@ -281,6 +282,9 @@ CssHtmlTree.prototype = {
CssHtmlTree.processTemplate(this.templateRoot, this.root, this);
+ // Refresh source filter ... this must be done after templateRoot has been
+ // processed.
+ this.refreshSourceFilter();
this.numVisibleProperties = 0;
let fragment = this.doc.createDocumentFragment();
this._refreshProcess = new UpdateProcess(this.win, CssHtmlTree.propertyNames, {
@@ -362,21 +366,28 @@ CssHtmlTree.prototype = {
},
/**
- * The change event handler for the onlyUserStyles checkbox. When
- * onlyUserStyles.checked is true we do not display properties that have no
- * matched selectors, and we do not display UA styles. If .checked is false we
- * do display even properties with no matched selectors, and we include the UA
- * styles.
+ * The change event handler for the onlyUserStyles checkbox.
*
* @param {Event} aEvent the DOM Event object.
*/
onlyUserStylesChanged: function CssHtmltree_onlyUserStylesChanged(aEvent)
+ {
+ this.refreshSourceFilter();
+ this.refreshPanel();
+ },
+
+ /**
+ * When onlyUserStyles.checked is true we only display properties that have
+ * matched selectors and have been included by the document or one of the
+ * document's stylesheets. If .checked is false we display all properties
+ * including those that come from UA stylesheets.
+ */
+ refreshSourceFilter: function CssHtmlTree_setSourceFilter()
{
this._matchedProperties = null;
this.cssLogic.sourceFilter = this.showOnlyUserStyles ?
CssLogic.FILTER.ALL :
CssLogic.FILTER.UA;
- this.refreshPanel();
},
/**
@@ -974,4 +985,24 @@ SelectorView.prototype = {
return result;
},
+
+ /**
+ * When a css link is clicked this method is called in order to either:
+ * 1. Open the link in view source (for element style attributes).
+ * 2. Open the link in the style editor.
+ *
+ * @param aEvent The click event
+ */
+ openStyleEditor: function(aEvent)
+ {
+ if (this.selectorInfo.selector._cssRule._cssSheet) {
+ let styleSheet = this.selectorInfo.selector._cssRule._cssSheet.domSheet;
+ let line = this.selectorInfo.ruleLine;
+
+ this.tree.win.StyleEditor.openChrome(styleSheet, line);
+ } else {
+ let href = this.selectorInfo.sourceElement.ownerDocument.location.href;
+ this.tree.win.openUILinkIn("view-source:" + href, "window");
+ }
+ },
};
diff --git a/browser/devtools/styleinspector/CssLogic.jsm b/browser/devtools/styleinspector/CssLogic.jsm
index d4b03d1c2a4..67e37810071 100644
--- a/browser/devtools/styleinspector/CssLogic.jsm
+++ b/browser/devtools/styleinspector/CssLogic.jsm
@@ -232,7 +232,7 @@ CssLogic.prototype = {
// Update the CssSheet objects.
this.forEachSheet(function(aSheet) {
aSheet._sheetAllowed = -1;
- if (!aSheet.systemSheet && aSheet.sheetAllowed) {
+ if (aSheet.contentSheet && aSheet.sheetAllowed) {
ruleCount += aSheet.ruleCount;
}
}, this);
@@ -345,7 +345,7 @@ CssLogic.prototype = {
let sheets = [];
this.forEachSheet(function (aSheet) {
- if (!aSheet.systemSheet) {
+ if (aSheet.contentSheet) {
sheets.push(aSheet);
}
}, this);
@@ -395,7 +395,7 @@ CssLogic.prototype = {
}
sheet = new CssSheet(this, aDomSheet, aIndex);
- if (sheet.sheetAllowed && !sheet.systemSheet) {
+ if (sheet.sheetAllowed && sheet.contentSheet) {
this._ruleCount += sheet.ruleCount;
}
@@ -569,7 +569,7 @@ CssLogic.prototype = {
this.forEachSheet(function (aSheet) {
// We do not show unmatched selectors from system stylesheets
- if (aSheet.systemSheet || aSheet.disabled || !aSheet.mediaMatches) {
+ if (!aSheet.contentSheet || aSheet.disabled || !aSheet.mediaMatches) {
return;
}
@@ -664,7 +664,7 @@ CssLogic.prototype = {
sheet._passId = this._passId;
}
- if (filter !== CssLogic.FILTER.UA && sheet.systemSheet) {
+ if (filter === CssLogic.FILTER.ALL && !sheet.contentSheet) {
continue;
}
@@ -710,7 +710,7 @@ CssLogic.prototype = {
let result = {};
this.forSomeSheets(function (aSheet) {
- if (aSheet.systemSheet || aSheet.disabled || !aSheet.mediaMatches) {
+ if (!aSheet.contentSheet || aSheet.disabled || !aSheet.mediaMatches) {
return false;
}
@@ -865,29 +865,23 @@ XPCOMUtils.defineLazyGetter(CssLogic, "_strings", function() Services.strings
.createBundle("chrome://browser/locale/devtools/styleinspector.properties"));
/**
- * Is the given property sheet a system (user agent) stylesheet?
+ * Is the given property sheet a content stylesheet?
*
* @param {CSSStyleSheet} aSheet a stylesheet
- * @return {boolean} true if the given stylesheet is a system stylesheet or
+ * @return {boolean} true if the given stylesheet is a content stylesheet,
* false otherwise.
*/
-CssLogic.isSystemStyleSheet = function CssLogic_isSystemStyleSheet(aSheet)
+CssLogic.isContentStylesheet = function CssLogic_isContentStylesheet(aSheet)
{
- if (!aSheet) {
+ // All sheets with owner nodes have been included by content.
+ if (aSheet.ownerNode) {
return true;
}
- let url = aSheet.href;
-
- if (!url) return false;
- if (url.length === 0) return true;
-
- // Check for http[s]
- if (url[0] === 'h') return false;
- if (url.substr(0, 9) === "resource:") return true;
- if (url.substr(0, 7) === "chrome:") return true;
- if (url === "XPCSafeJSObjectWrapper.cpp") return true;
- if (url.substr(0, 6) === "about:") return true;
+ // If the sheet has a CSSImportRule we need to check the parent stylesheet.
+ if (aSheet.ownerRule instanceof Ci.nsIDOMCSSImportRule) {
+ return CssLogic.isContentStylesheet(aSheet.parentStyleSheet);
+ }
return false;
};
@@ -942,7 +936,7 @@ function CssSheet(aCssLogic, aDomSheet, aIndex)
{
this._cssLogic = aCssLogic;
this.domSheet = aDomSheet;
- this.index = this.systemSheet ? -100 * aIndex : aIndex;
+ this.index = this.contentSheet ? aIndex : -100 * aIndex;
// Cache of the sheets href. Cached by the getter.
this._href = null;
@@ -960,21 +954,21 @@ function CssSheet(aCssLogic, aDomSheet, aIndex)
CssSheet.prototype = {
_passId: null,
- _systemSheet: null,
+ _contentSheet: null,
_mediaMatches: null,
/**
* Tells if the stylesheet is provided by the browser or not.
*
- * @return {boolean} true if this is a browser-provided stylesheet, or false
+ * @return {boolean} false if this is a browser-provided stylesheet, or true
* otherwise.
*/
- get systemSheet()
+ get contentSheet()
{
- if (this._systemSheet === null) {
- this._systemSheet = CssLogic.isSystemStyleSheet(this.domSheet);
+ if (this._contentSheet === null) {
+ this._contentSheet = CssLogic.isContentStylesheet(this.domSheet);
}
- return this._systemSheet;
+ return this._contentSheet;
},
/**
@@ -1048,7 +1042,7 @@ CssSheet.prototype = {
this._sheetAllowed = true;
let filter = this._cssLogic.sourceFilter;
- if (filter === CssLogic.FILTER.ALL && this.systemSheet) {
+ if (filter === CssLogic.FILTER.ALL && !this.contentSheet) {
this._sheetAllowed = false;
}
if (filter !== CssLogic.FILTER.ALL && filter !== CssLogic.FILTER.UA) {
@@ -1202,13 +1196,13 @@ function CssRule(aCssSheet, aDomRule, aElement)
this.line = this._cssSheet._cssLogic.domUtils.getRuleLine(this._domRule);
this.source = this._cssSheet.shortSource + ":" + this.line;
this.href = this._cssSheet.href;
- this.systemRule = this._cssSheet.systemSheet;
+ this.contentRule = this._cssSheet.contentSheet;
} else if (aElement) {
this._selectors = [ new CssSelector(this, "@element.style") ];
this.line = -1;
this.source = CssLogic.l10n("rule.sourceElement");
this.href = "#";
- this.systemRule = false;
+ this.contentRule = true;
this.sourceElement = aElement;
}
}
@@ -1396,12 +1390,12 @@ CssSelector.prototype = {
/**
* Check if the selector comes from a browser-provided stylesheet.
*
- * @return {boolean} true if the selector comes from a browser-provided
+ * @return {boolean} true if the selector comes from a content-provided
* stylesheet, or false otherwise.
*/
- get systemRule()
+ get contentRule()
{
- return this._cssRule.systemRule;
+ return this._cssRule.contentRule;
},
/**
@@ -1794,12 +1788,12 @@ function CssSelectorInfo(aSelector, aProperty, aValue, aStatus)
4 important
5 inline important
*/
- let scorePrefix = this.systemRule ? 0 : 2;
+ let scorePrefix = this.contentRule ? 2 : 0;
if (this.elementStyle) {
scorePrefix++;
}
if (this.important) {
- scorePrefix += this.systemRule ? 1 : 2;
+ scorePrefix += this.contentRule ? 2 : 1;
}
this.specificityScore = "" + scorePrefix + this.specificity.ids +
@@ -1902,9 +1896,9 @@ CssSelectorInfo.prototype = {
* @return {boolean} true if the selector comes from a browser-provided
* stylesheet, or false otherwise.
*/
- get systemRule()
+ get contentRule()
{
- return this.selector.systemRule;
+ return this.selector.contentRule;
},
/**
@@ -1916,8 +1910,8 @@ CssSelectorInfo.prototype = {
*/
compareTo: function CssSelectorInfo_compareTo(aThat)
{
- if (this.systemRule && !aThat.systemRule) return 1;
- if (!this.systemRule && aThat.systemRule) return -1;
+ if (!this.contentRule && aThat.contentRule) return 1;
+ if (this.contentRule && !aThat.contentRule) return -1;
if (this.elementStyle && !aThat.elementStyle) {
if (!this.important && aThat.important) return 1;
diff --git a/browser/devtools/styleinspector/CssRuleView.jsm b/browser/devtools/styleinspector/CssRuleView.jsm
index c12a160e216..f001b49f9d6 100644
--- a/browser/devtools/styleinspector/CssRuleView.jsm
+++ b/browser/devtools/styleinspector/CssRuleView.jsm
@@ -38,7 +38,7 @@
*
* ***** END LICENSE BLOCK ***** */
-"use strict"
+"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
@@ -181,8 +181,8 @@ ElementStyle.prototype = {
let domRule = domRules.GetElementAt(i);
// XXX: Optionally provide access to system sheets.
- let systemSheet = CssLogic.isSystemStyleSheet(domRule.parentStyleSheet);
- if (systemSheet) {
+ let contentSheet = CssLogic.isContentStylesheet(domRule.parentStyleSheet);
+ if (!contentSheet) {
continue;
}
@@ -324,7 +324,7 @@ ElementStyle.prototype = {
aProp.overridden = overridden;
return dirty;
}
-}
+};
/**
* A single style rule or declaration.
@@ -358,11 +358,9 @@ Rule.prototype = {
if (this._title) {
return this._title;
}
- let sheet = this.domRule ? this.domRule.parentStyleSheet : null;
- this._title = CssLogic.shortSource(sheet);
+ this._title = CssLogic.shortSource(this.sheet);
if (this.domRule) {
- let line = this.elementStyle.domUtils.getRuleLine(this.domRule);
- this._title += ":" + line;
+ this._title += ":" + this.ruleLine;
}
if (this.inherited) {
@@ -378,6 +376,26 @@ Rule.prototype = {
return this._title;
},
+ /**
+ * The rule's stylesheet.
+ */
+ get sheet()
+ {
+ return this.domRule ? this.domRule.parentStyleSheet : null;
+ },
+
+ /**
+ * The rule's line within a stylesheet
+ */
+ get ruleLine()
+ {
+ if (!this.sheet) {
+ // No stylesheet, no ruleLine
+ return null;
+ }
+ return this.elementStyle.domUtils.getRuleLine(this.domRule);
+ },
+
/**
* Create a new TextProperty to include in the rule.
*
@@ -530,7 +548,7 @@ Rule.prototype = {
this.textProps.push(textProp);
}
},
-}
+};
/**
* A single property in a rule's cssText.
@@ -618,7 +636,7 @@ TextProperty.prototype = {
{
this.rule.removeProperty(this);
}
-}
+};
/**
@@ -643,7 +661,7 @@ TextProperty.prototype = {
* apply to a given element. After construction, the 'element'
* property will be available with the user interface.
*
- * @param Document aDocument
+ * @param Document aDoc
* The document that will contain the rule view.
* @param object aStore
* The CSS rule view can use this object to store metadata
@@ -655,7 +673,6 @@ function CssRuleView(aDoc, aStore)
{
this.doc = aDoc;
this.store = aStore;
-
this.element = this.doc.createElementNS(XUL_NS, "vbox");
this.element.setAttribute("tabindex", "0");
this.element.classList.add("ruleview");
@@ -768,6 +785,14 @@ RuleEditor.prototype = {
class: "ruleview-rule-source",
textContent: this.rule.title
});
+ source.addEventListener("click", function() {
+ let rule = this.rule;
+ let evt = this.doc.createEvent("CustomEvent");
+ evt.initCustomEvent("CssRuleViewCSSLinkClicked", true, false, {
+ rule: rule,
+ });
+ this.element.dispatchEvent(evt);
+ }.bind(this));
let code = createChild(this.element, "div", {
class: "ruleview-code"
@@ -1094,8 +1119,6 @@ TextPropertyEditor.prototype = {
_parseValue: function TextPropertyEditor_parseValue(aValue)
{
let pieces = aValue.split("!", 2);
- let value = pieces[0];
- let priority = pieces.length > 1 ? pieces[1] : "";
return {
value: pieces[0].trim(),
priority: (pieces.length > 1 ? pieces[1].trim() : "")
diff --git a/browser/devtools/styleinspector/csshtmltree.xul b/browser/devtools/styleinspector/csshtmltree.xul
index e5c3bcc5a4c..1e4eee123ad 100644
--- a/browser/devtools/styleinspector/csshtmltree.xul
+++ b/browser/devtools/styleinspector/csshtmltree.xul
@@ -114,7 +114,7 @@ To visually debug the templates without running firefox, alter the display:none
${selector.humanReadableText(__element)}
- ${selector.selectorInfo.source}
|
diff --git a/browser/devtools/styleinspector/test/Makefile.in b/browser/devtools/styleinspector/test/Makefile.in
index a4665bddc3d..e77fb1a6e2b 100644
--- a/browser/devtools/styleinspector/test/Makefile.in
+++ b/browser/devtools/styleinspector/test/Makefile.in
@@ -59,11 +59,19 @@ _BROWSER_TEST_FILES = \
browser_ruleview_manipulation.js \
browser_ruleview_override.js \
browser_ruleview_ui.js \
+ browser_bug705707_is_content_stylesheet.js \
head.js \
$(NULL)
_BROWSER_TEST_PAGES = \
browser_bug683672.html \
+ browser_bug705707_is_content_stylesheet.html \
+ browser_bug705707_is_content_stylesheet_imported.css \
+ browser_bug705707_is_content_stylesheet_imported2.css \
+ browser_bug705707_is_content_stylesheet_linked.css \
+ browser_bug705707_is_content_stylesheet_script.css \
+ browser_bug705707_is_content_stylesheet.xul \
+ browser_bug705707_is_content_stylesheet_xul.css \
$(NULL)
libs:: $(_BROWSER_TEST_FILES)
diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.html b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.html
new file mode 100644
index 00000000000..96ef5b6e9e9
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.html
@@ -0,0 +1,33 @@
+
+
+ test
+
+
+
+
+
+
+
+
+
+
+
diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.js b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.js
new file mode 100644
index 00000000000..fec06cf5620
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.js
@@ -0,0 +1,100 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the correct stylesheets origins are identified in HTML & XUL
+// stylesheets
+
+let doc;
+
+const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" +
+ "test/browser_bug705707_is_content_stylesheet.html";
+const TEST_URI2 = "http://example.com/browser/browser/devtools/styleinspector/" +
+ "test/browser_bug705707_is_content_stylesheet.xul";
+const XUL_URI = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService)
+ .newURI(TEST_URI2, null, null);
+
+let tempScope = {};
+Cu.import("resource:///modules/devtools/CssLogic.jsm", tempScope);
+let CssLogic = tempScope.CssLogic;
+
+function test()
+{
+ waitForExplicitFinish();
+ addTab(TEST_URI);
+ browser.addEventListener("load", htmlLoaded, true);
+}
+
+function htmlLoaded()
+{
+ browser.removeEventListener("load", htmlLoaded, true);
+ doc = content.document;
+ testFromHTML()
+}
+
+function testFromHTML()
+{
+ let target = doc.querySelector("#target");
+
+ executeSoon(function() {
+ checkSheets(target);
+ gBrowser.removeCurrentTab();
+ openXUL();
+ });
+}
+
+function openXUL()
+{
+ Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager)
+ .add(XUL_URI, 'allowXULXBL', Ci.nsIPermissionManager.ALLOW_ACTION);
+ addTab(TEST_URI2);
+ browser.addEventListener("load", xulLoaded, true);
+}
+
+function xulLoaded()
+{
+ browser.removeEventListener("load", xulLoaded, true);
+ doc = content.document;
+ testFromXUL()
+}
+
+function testFromXUL()
+{
+ let target = doc.querySelector("#target");
+
+ executeSoon(function() {
+ checkSheets(target);
+ finishUp();
+ });
+}
+
+function checkSheets(aTarget)
+{
+ let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(Ci.inIDOMUtils);
+ let domRules = domUtils.getCSSStyleRules(aTarget);
+
+ for (let i = 0, n = domRules.Count(); i < n; i++) {
+ let domRule = domRules.GetElementAt(i);
+ let sheet = domRule.parentStyleSheet;
+ let isContentSheet = CssLogic.isContentStylesheet(sheet);
+
+ if (!sheet.href ||
+ /browser_bug705707_is_content_stylesheet_/.test(sheet.href)) {
+ ok(isContentSheet, sheet.href + " identified as content stylesheet");
+ } else {
+ ok(!isContentSheet, sheet.href + " identified as non-content stylesheet");
+ }
+ }
+}
+
+function finishUp()
+{
+ info("finishing up");
+ Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager)
+ .add(XUL_URI, 'allowXULXBL', Ci.nsIPermissionManager.DENY_ACTION);
+ doc = null;
+ gBrowser.removeCurrentTab();
+ finish();
+}
diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.xul b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.xul
new file mode 100644
index 00000000000..abbd03030e8
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet.xul
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported.css b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported.css
new file mode 100644
index 00000000000..4092afeea39
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported.css
@@ -0,0 +1,5 @@
+@import url("./browser_bug705707_is_content_stylesheet_imported2.css");
+
+#target {
+ text-decoration: underline;
+}
diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported2.css b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported2.css
new file mode 100644
index 00000000000..77c73299ea9
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_imported2.css
@@ -0,0 +1,3 @@
+#target {
+ text-decoration: underline;
+}
diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_linked.css b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_linked.css
new file mode 100644
index 00000000000..712ba78fb6f
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_linked.css
@@ -0,0 +1,3 @@
+table {
+ border-collapse: collapse;
+}
diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_script.css b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_script.css
new file mode 100644
index 00000000000..bf4fc8ddcb8
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_script.css
@@ -0,0 +1,5 @@
+@import url("./browser_bug705707_is_content_stylesheet_imported.css");
+
+table {
+ opacity: 1;
+}
diff --git a/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_xul.css b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_xul.css
new file mode 100644
index 00000000000..a14ae7f6fdb
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug705707_is_content_stylesheet_xul.css
@@ -0,0 +1,3 @@
+#target {
+ font-size: 200px;
+}
diff --git a/browser/devtools/webconsole/GcliCommands.jsm b/browser/devtools/webconsole/GcliCommands.jsm
index 31532b9e0ae..07dbf13d82d 100644
--- a/browser/devtools/webconsole/GcliCommands.jsm
+++ b/browser/devtools/webconsole/GcliCommands.jsm
@@ -126,6 +126,40 @@ gcli.addCommand({
}
});
+/**
+ * 'edit' command
+ */
+gcli.addCommand({
+ name: "edit",
+ description: gcli.lookup("editDesc"),
+ manual: gcli.lookup("editManual"),
+ params: [
+ {
+ name: 'resource',
+ type: {
+ name: 'resource',
+ include: 'text/css'
+ },
+ description: gcli.lookup("editResourceDesc")
+ },
+ {
+ name: "line",
+ defaultValue: 1,
+ type: {
+ name: "number",
+ min: 1,
+ step: 10
+ },
+ description: gcli.lookup("editLineToJumpToDesc")
+ }
+ ],
+ exec: function(args, context) {
+ let hud = HUDService.getHudReferenceById(context.environment.hudId);
+ let StyleEditor = hud.gcliterm.document.defaultView.StyleEditor;
+ StyleEditor.openChrome(args.resource.element, args.line);
+ }
+});
+
let breakpoints = [];
/**
diff --git a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
index bfa6a92669b..f4a1ce01820 100644
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -129,3 +129,23 @@ breakdelRemoved=Breakpoint removed
# 'console close' command. This string is designed to be shown in a menu
# alongside the command name, which is why it should be as short as possible.
consolecloseDesc=Close the console
+
+# LOCALIZATION NOTE (editDesc) A very short description of the 'edit'
+# command. See editManual for a fuller description of what it does. This
+# string is designed to be shown in a menu alongside the command name, which
+# is why it should be as short as possible.
+editDesc=Tweak a page resource
+
+# LOCALIZATION NOTE (editManual) A fuller description of the 'edit' command,
+# displayed when the user asks for help on what it does.
+editManual=Edit one of the resources that is part of this page (or maybe any generic web resource?)
+
+# LOCALIZATION NOTE (editResourceDesc) A very short string to describe the
+# 'resource' parameter to the 'edit' command, which is displayed in a dialog
+# when the user is using this command.
+editResourceDesc=URL to edit
+
+# LOCALIZATION NOTE (editLineToJumpToDesc) A very short string to describe the
+# 'line' parameter to the 'edit' command, which is displayed in a dialog
+# when the user is using this command.
+editLineToJumpToDesc=Line to jump to
diff --git a/browser/makefiles.sh b/browser/makefiles.sh
index e5bc351d0ba..d76045f8cfa 100644
--- a/browser/makefiles.sh
+++ b/browser/makefiles.sh
@@ -88,6 +88,12 @@ browser/themes/Makefile
$MOZ_BRANDING_DIRECTORY/Makefile
$MOZ_BRANDING_DIRECTORY/content/Makefile
$MOZ_BRANDING_DIRECTORY/locales/Makefile
+toolkit/locales/Makefile
+extensions/spellcheck/locales/Makefile
+intl/locales/Makefile
+netwerk/locales/Makefile
+dom/locales/Makefile
+security/manager/locales/Makefile
"
if [ "$MOZ_SAFE_BROWSING" ]; then
diff --git a/browser/themes/gnomestripe/devtools/csshtmltree.css b/browser/themes/gnomestripe/devtools/csshtmltree.css
index d15f6268508..8e19dcf2920 100644
--- a/browser/themes/gnomestripe/devtools/csshtmltree.css
+++ b/browser/themes/gnomestripe/devtools/csshtmltree.css
@@ -68,6 +68,9 @@
.helplink:visited {
text-decoration: none;
}
+.link:hover {
+ text-decoration: underline;
+}
.helplink {
display: block;
@@ -135,6 +138,7 @@
.rule-link {
text-align: end;
-moz-padding-start: 10px;
+ cursor: pointer;
}
/* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
@@ -200,7 +204,13 @@
.ruleview-rule-source {
background-color: -moz-dialog;
+ color: #0091ff;
padding: 2px 5px;
+ cursor: pointer;
+}
+
+.ruleview-rule-source:hover {
+ text-decoration: underline;
}
.ruleview-code {
diff --git a/browser/themes/pinstripe/devtools/csshtmltree.css b/browser/themes/pinstripe/devtools/csshtmltree.css
index d5b4c32b5ae..316b6d0e530 100644
--- a/browser/themes/pinstripe/devtools/csshtmltree.css
+++ b/browser/themes/pinstripe/devtools/csshtmltree.css
@@ -68,6 +68,9 @@
.helplink:visited {
text-decoration: none;
}
+.link:hover {
+ text-decoration: underline;
+}
.helplink {
display: block;
@@ -137,6 +140,7 @@
.rule-link {
text-align: end;
-moz-padding-start: 10px;
+ cursor: pointer;
}
/* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
@@ -202,7 +206,13 @@
.ruleview-rule-source {
background-color: -moz-dialog;
+ color: #0091ff;
padding: 2px 5px;
+ cursor: pointer;
+}
+
+.ruleview-rule-source:hover {
+ text-decoration: underline;
}
.ruleview-code {
diff --git a/browser/themes/winstripe/devtools/csshtmltree.css b/browser/themes/winstripe/devtools/csshtmltree.css
index 1d2b8ea5dae..459a11af4c9 100644
--- a/browser/themes/winstripe/devtools/csshtmltree.css
+++ b/browser/themes/winstripe/devtools/csshtmltree.css
@@ -67,6 +67,9 @@
.helplink:visited {
text-decoration: none;
}
+.link:hover {
+ text-decoration: underline;
+}
.helplink {
display: block;
@@ -135,6 +138,7 @@
.rule-link {
text-align: end;
-moz-padding-start: 10px;
+ cursor: pointer;
}
/* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
@@ -200,7 +204,13 @@
.ruleview-rule-source {
background-color: -moz-dialog;
+ color: #0091ff;
padding: 2px 5px;
+ cursor: pointer;
+}
+
+.ruleview-rule-source:hover {
+ text-decoration: underline;
}
.ruleview-code {
diff --git a/build/Makefile.in b/build/Makefile.in
index 0b592470628..f50833dd8d3 100644
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -84,7 +84,7 @@ DEFINES += -DGRE_MILESTONE=$(GRE_MILESTONE) -DAPP_BUILDID=$(APP_BUILDID)
DEFINES += -DMOZ_APP_VERSION="$(MOZ_APP_VERSION)"
APP_INI_DEPS += $(DEPTH)/config/autoconf.mk
-MOZ_SOURCE_STAMP ?= $(firstword $(shell hg -R $(topsrcdir)/$(MOZ_BUILD_APP)/.. parent --template="{node|short}\n" 2>/dev/null))
+MOZ_SOURCE_STAMP := $(firstword $(shell cd $(topsrcdir)/$(MOZ_BUILD_APP)/.. && hg parent --template="{node|short}\n" 2>/dev/null))
ifdef MOZ_SOURCE_STAMP
DEFINES += -DMOZ_SOURCE_STAMP="$(MOZ_SOURCE_STAMP)"
endif
diff --git a/build/autoconf/compiler-opts.m4 b/build/autoconf/compiler-opts.m4
new file mode 100644
index 00000000000..0c2fd2a0b1a
--- /dev/null
+++ b/build/autoconf/compiler-opts.m4
@@ -0,0 +1,13 @@
+dnl Add compiler specific options
+
+AC_DEFUN([MOZ_COMPILER_OPTS],
+[
+if test "$CLANG_CXX"; then
+ ## We disable return-type-c-linkage because jsval is defined as a C++ type but is
+ ## returned by C functions. This is possible because we use knowledge about the ABI
+ ## to typedef it to a C type with the same layout when the headers are included
+ ## from C.
+ _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-return-type-c-linkage"
+fi
+])
+
diff --git a/build/mobile/robocop/Actions.java.in b/build/mobile/robocop/Actions.java.in
index 7be67df3cf5..0c123016921 100644
--- a/build/mobile/robocop/Actions.java.in
+++ b/build/mobile/robocop/Actions.java.in
@@ -41,43 +41,55 @@ package @ANDROID_PACKAGE_NAME@;
import java.util.List;
public interface Actions {
- public enum SpecialKey {
- DOWN, UP, LEFT, RIGHT, ENTER, MENU, BACK
- }
- public interface EventExpecter {
- /** Blocks until the event has been received. Subsequent calls will return immediately. */
- public void blockForEvent();
- /** Polls to see if the event has been received. Once this returns true, subsequent calls will also return true. */
- public boolean eventReceived();
- }
+ /** Special keys supported by sendSpecialKey() */
+ public enum SpecialKey {
+ DOWN, UP, LEFT, RIGHT, ENTER, MENU, BACK
+ }
- public interface RepeatedEventExpecter extends EventExpecter {
- /** Blocks until at least one event has been received, and no events have been received in the last millis
milliseconds. */
- public void blockUntilClear(long millis);
- }
+ public interface EventExpecter {
+ /** Blocks until the event has been received. Subsequent calls will return immediately. */
+ public void blockForEvent();
- /**
- * Listens for a gecko event to be sent from the Gecko instance.
- * The returned object can be used to test if the event has been
- * received. Note that only one event is listened for.
- *
- * @param geckoEvent The geckoEvent JSONObject's type
- */
- EventExpecter expectGeckoEvent(String geckoEvent);
+ /** Polls to see if the event has been received. Once this returns true, subsequent calls will also return true. */
+ public boolean eventReceived();
+ }
- /**
- * Listens for a paint event. Note that calling expectPaint() will
- * invalidate the event expecters returned from any previous calls
- * to expectPaint(); calling any methods on those invalidated objects
- * will result in undefined behaviour.
- */
- RepeatedEventExpecter expectPaint();
+ public interface RepeatedEventExpecter extends EventExpecter {
+ /** Blocks until at least one event has been received, and no events have been received in the last millis
milliseconds. */
+ public void blockUntilClear(long millis);
+ }
- // Send the string kewsToSend to the application
- void sendKeys(String keysToSend);
- //Send any of the above keys to the element
- void sendSpecialKey(SpecialKey button);
+ /**
+ * Listens for a gecko event to be sent from the Gecko instance.
+ * The returned object can be used to test if the event has been
+ * received. Note that only one event is listened for.
+ *
+ * @param geckoEvent The geckoEvent JSONObject's type
+ */
+ EventExpecter expectGeckoEvent(String geckoEvent);
- void drag(int startingX, int endingX, int startingY, int endingY);
+ /**
+ * Listens for a paint event. Note that calling expectPaint() will
+ * invalidate the event expecters returned from any previous calls
+ * to expectPaint(); calling any methods on those invalidated objects
+ * will result in undefined behaviour.
+ */
+ RepeatedEventExpecter expectPaint();
+
+ /**
+ * Send a string to the application
+ *
+ * @param keysToSend The string to send
+ */
+ void sendKeys(String keysToSend);
+
+ /**
+ * Send a special keycode to the element
+ *
+ * @param key The special key to send
+ */
+ void sendSpecialKey(SpecialKey key);
+
+ void drag(int startingX, int endingX, int startingY, int endingY);
}
diff --git a/build/mobile/robocop/Assert.java.in b/build/mobile/robocop/Assert.java.in
index d3afb7bd31c..28a14dbdb5c 100644
--- a/build/mobile/robocop/Assert.java.in
+++ b/build/mobile/robocop/Assert.java.in
@@ -40,19 +40,19 @@
package @ANDROID_PACKAGE_NAME@;
public interface Assert {
- void dumpLog(String message);
- void setLogFile(String filename);
- void setTestName(String testName);
+ void dumpLog(String message);
+ void setLogFile(String filename);
+ void setTestName(String testName);
- void finalize();
- void ok(boolean condition, String name, String diag);
- void is(Object a, Object b, String name);
- void isnot(Object a, Object b, String name);
- void todo(boolean condition, String name, String diag);
- void todo_is(Object a, Object b, String name);
- void todo_isnot(Object a, Object b, String name);
- void info(String name, String message);
+ void finalize();
+ void ok(boolean condition, String name, String diag);
+ void is(Object a, Object b, String name);
+ void isnot(Object a, Object b, String name);
+ void todo(boolean condition, String name, String diag);
+ void todo_is(Object a, Object b, String name);
+ void todo_isnot(Object a, Object b, String name);
+ void info(String name, String message);
- // robocop-specific asserts
- void ispixel(int actual, int r, int g, int b, String name);
+ // robocop-specific asserts
+ void ispixel(int actual, int r, int g, int b, String name);
}
diff --git a/build/mobile/robocop/Driver.java.in b/build/mobile/robocop/Driver.java.in
index b026fe47a23..e358ec89a23 100644
--- a/build/mobile/robocop/Driver.java.in
+++ b/build/mobile/robocop/Driver.java.in
@@ -43,39 +43,39 @@ import java.util.List;
import android.app.Activity;
public interface Driver {
- /**
- * Find the first Element using the given method.
- *
- * @param activity The activity the element belongs to
- * @param name The name of the element
- * @return The first matching element on the current context
- * @throws RoboCopException If no matching elements are found
- */
- Element findElement(Activity activity, String name);
+ /**
+ * Find the first Element using the given method.
+ *
+ * @param activity The activity the element belongs to
+ * @param name The name of the element
+ * @return The first matching element on the current context
+ * @throws RoboCopException If no matching elements are found
+ */
+ Element findElement(Activity activity, String name);
- /**
- * Sets up scroll handling so that data is received from the extension.
- */
- void setupScrollHandling();
+ /**
+ * Sets up scroll handling so that data is received from the extension.
+ */
+ void setupScrollHandling();
- int getPageHeight();
- int getScrollHeight();
- int getHeight();
- int getGeckoTop();
- int getGeckoLeft();
- int getGeckoWidth();
- int getGeckoHeight();
+ int getPageHeight();
+ int getScrollHeight();
+ int getHeight();
+ int getGeckoTop();
+ int getGeckoLeft();
+ int getGeckoWidth();
+ int getGeckoHeight();
- void startFrameRecording();
- int stopFrameRecording();
+ void startFrameRecording();
+ int stopFrameRecording();
- void startCheckerboardRecording();
- float stopCheckerboardRecording();
+ void startCheckerboardRecording();
+ float stopCheckerboardRecording();
- /**
- * Get a copy of the painted content region.
- * @return A 2-D array of pixels (indexed by y, then x). The pixels
- * are in ARGB-8888 format.
- */
- int[][] getPaintedSurface();
+ /**
+ * Get a copy of the painted content region.
+ * @return A 2-D array of pixels (indexed by y, then x). The pixels
+ * are in ARGB-8888 format.
+ */
+ int[][] getPaintedSurface();
}
diff --git a/build/mobile/robocop/Element.java.in b/build/mobile/robocop/Element.java.in
index 76bbbd71673..2c87bd208f9 100644
--- a/build/mobile/robocop/Element.java.in
+++ b/build/mobile/robocop/Element.java.in
@@ -39,13 +39,21 @@
package @ANDROID_PACKAGE_NAME@;
+/**
+ * Element provides access to a specific UI view (android.view.View).
+ * See also Driver.findElement().
+ */
public interface Element {
- //Click on the element
- void click();
- //Returns true if the element is currently displayed
- boolean isDisplayed();
- //Returns the text currently displayed on the element.
- String getText();
- //Returns view ID.
- Integer getId();
+
+ /** Click on the element */
+ void click();
+
+ /** Returns true if the element is currently displayed */
+ boolean isDisplayed();
+
+ /** Returns the text currently displayed on the element */
+ String getText();
+
+ /** Returns the view ID */
+ Integer getId();
}
diff --git a/build/mobile/robocop/FennecMochitestAssert.java.in b/build/mobile/robocop/FennecMochitestAssert.java.in
index 35a59652242..20199ec5110 100644
--- a/build/mobile/robocop/FennecMochitestAssert.java.in
+++ b/build/mobile/robocop/FennecMochitestAssert.java.in
@@ -38,202 +38,194 @@
package @ANDROID_PACKAGE_NAME@;
import java.util.LinkedList;
-import java.util.List;
-import java.util.Date;
+import android.os.SystemClock;
public class FennecMochitestAssert implements Assert {
- // Objects for reflexive access of fennec classes.
+ private LinkedList mTestList = new LinkedList();
- private LinkedList testList = new LinkedList();
+ // Internal state variables to make logging match up with existing mochitests
+ private int mLineNumber = 0;
+ private int mPassed = 0;
+ private int mFailed = 0;
+ private int mTodo = 0;
+
+ // Used to write the first line of the test file
+ private boolean mLogStarted = false;
- // Internal state variables to make logging match up with existing mochitests
- private int lineNumber = 0;
- private int passed = 0;
- private int failed = 0;
- private int todo = 0;
-
- // Used to write the first line of the test file
- private boolean logStarted = false;
+ // Used to write the test-start/test-end log lines
+ private String mLogTestName = "";
- // Used to write the test-start/test-end log lines
- private String logTestName = "";
+ // Measure the time it takes to run test case
+ private long mStartTime = 0;
- // Measure the time it takes to run test case
- private long startTime = 0;
-
- public FennecMochitestAssert() {
- }
-
- // Write information to a logfile and logcat
- public void dumpLog(String message)
- {
- FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message);
- }
-
- // Set the filename used for dumpLog.
- public void setLogFile(String filename)
- {
- FennecNativeDriver.setLogFile(filename);
-
- String message;
- if (!logStarted) {
- dumpLog(Integer.toString(lineNumber++) + " INFO SimpleTest START");
- logStarted = true;
+ public FennecMochitestAssert() {
}
- if (logTestName != "") {
- long diff = (new Date().getTime()) - startTime;
- message = Integer.toString(lineNumber++) + " INFO TEST-END | " + logTestName;
- message += " | finished in " + diff + "ms";
- dumpLog(message);
- logTestName = "";
- }
- }
-
- public void setTestName(String testName)
- {
- String[] nameParts = testName.split("\\.");
- logTestName = nameParts[nameParts.length - 1];
- startTime = new Date().getTime();
-
- dumpLog(Integer.toString(lineNumber++) + " INFO TEST-START | " + logTestName);
- }
-
- class testInfo {
- public boolean result;
- public String name;
- public String diag;
- public boolean todo;
- public testInfo(boolean r, String n, String d, boolean t) {
- result = r;
- name = n;
- diag = d;
- todo = t;
+ /** Write information to a logfile and logcat */
+ public void dumpLog(String message) {
+ FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message);
}
- }
+ /** Set the filename used for dumpLog. */
+ public void setLogFile(String filename) {
+ FennecNativeDriver.setLogFile(filename);
- private void _logMochitestResult(testInfo test, String passString, String failString)
- {
- boolean isError = true;
- String resultString = failString;
- if (test.result || test.todo) {
- isError = false;
- }
- if (test.result)
- {
- resultString = passString;
- }
- String diag = test.name;
- if (test.diag != null) diag += " - " + test.diag;
+ String message;
+ if (!mLogStarted) {
+ dumpLog(Integer.toString(mLineNumber++) + " INFO SimpleTest START");
+ mLogStarted = true;
+ }
- String message = Integer.toString(lineNumber++) + " INFO " + resultString + " | " + logTestName + " | " + diag;
- dumpLog(message);
-
- if (test.todo) {
- todo++;
- } else if (isError) {
- failed++;
- } else {
- passed++;
- }
- if (isError) {
- junit.framework.Assert.fail(message);
- }
- }
-
- public void finalize()
- {
- // It appears that we call finalize during cleanup, this might be an invalid assertion.
- String message;
-
- if (logTestName != "") {
- long diff = (new Date().getTime()) - startTime;
- message = Integer.toString(lineNumber++) + " INFO TEST-END | " + logTestName;
- message += " | finished in " + diff + "ms";
- dumpLog(message);
- logTestName = "";
+ if (mLogTestName != "") {
+ long diff = SystemClock.uptimeMillis() - mStartTime;
+ message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName;
+ message += " | finished in " + diff + "ms";
+ dumpLog(message);
+ mLogTestName = "";
+ }
}
- message = Integer.toString(lineNumber++) + " INFO TEST-START | Shutdown";
- dumpLog(message);
- message = Integer.toString(lineNumber++) + " INFO Passed: " + Integer.toString(passed);
- dumpLog(message);
- message = Integer.toString(lineNumber++) + " INFO Failed: " + Integer.toString(failed);
- dumpLog(message);
- message = Integer.toString(lineNumber++) + " INFO Todo: " + Integer.toString(todo);
- dumpLog(message);
- message = Integer.toString(lineNumber++) + " INFO SimpleTest FINISHED";
- dumpLog(message);
- }
+ public void setTestName(String testName) {
+ String[] nameParts = testName.split("\\.");
+ mLogTestName = nameParts[nameParts.length - 1];
+ mStartTime = SystemClock.uptimeMillis();
- public void ok(boolean condition, String name, String diag) {
- testInfo test = new testInfo(condition, name, diag, false);
- _logMochitestResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL");
- testList.add(test);
- }
-
- public void is(Object a, Object b, String name) {
- boolean pass = a.equals(b);
- String diag = "got " + a.toString() + ", expected " + b.toString();
- if(pass) {
- diag = a.toString() + " should equal " + b.toString();
+ dumpLog(Integer.toString(mLineNumber++) + " INFO TEST-START | " + mLogTestName);
}
- ok(pass, name, diag);
- }
-
- public void isnot(Object a, Object b, String name) {
- boolean pass = !a.equals(b);
- String diag = "didn't expect " + a.toString() + ", but got it";
- if(pass) {
- diag = a.toString() + " should not equal " + b.toString();
+
+ class testInfo {
+ public boolean mResult;
+ public String mName;
+ public String mDiag;
+ public boolean mTodo;
+ public testInfo(boolean r, String n, String d, boolean t) {
+ mResult = r;
+ mName = n;
+ mDiag = d;
+ mTodo = t;
+ }
+
}
- ok(pass, name, diag);
- }
- public void ispixel(int actual, int r, int g, int b, String name) {
- // When we read GL pixels the GPU has already processed them and they
- // are usually off by a little bit. For example a CSS-color pixel of color #64FFF5
- // was turned into #63FFF7 when it came out of glReadPixels. So in order to compare
- // against the expected value, we use a little fuzz factor. For the alpha we just
- // make sure it is always 0xFF.
- int aAlpha = ((actual >> 24) & 0xFF);
- int aR = ((actual >> 16) & 0xFF);
- int aG = ((actual >> 8) & 0xFF);
- int aB = (actual & 0xFF);
- boolean pass = (aAlpha == 0xFF) /* alpha */
- && (Math.abs(aR - r) < 8) /* red */
- && (Math.abs(aG - g) < 8) /* green */
- && (Math.abs(aB - b) < 8); /* blue */
- ok(pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (pass ? " " : " not") + " close enough to expected rgb(" + r + "," + g + "," + b + ")");
- }
+ private void _logMochitestResult(testInfo test, String passString, String failString) {
+ boolean isError = true;
+ String resultString = failString;
+ if (test.mResult || test.mTodo) {
+ isError = false;
+ }
+ if (test.mResult)
+ {
+ resultString = passString;
+ }
+ String diag = test.mName;
+ if (test.mDiag != null) diag += " - " + test.mDiag;
- public void todo(boolean condition, String name, String diag) {
- testInfo test = new testInfo(condition, name, diag, true);
- _logMochitestResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL");
- testList.add(test);
- }
+ String message = Integer.toString(mLineNumber++) + " INFO " + resultString + " | " + mLogTestName + " | " + diag;
+ dumpLog(message);
- public void todo_is(Object a, Object b, String name) {
- boolean pass = a.equals(b);
- String diag = "got " + a.toString() + ", expected " + b.toString();
- if(pass) {
- diag = a.toString() + " should equal " + b.toString();
+ if (test.mTodo) {
+ mTodo++;
+ } else if (isError) {
+ mFailed++;
+ } else {
+ mPassed++;
+ }
+ if (isError) {
+ junit.framework.Assert.fail(message);
+ }
}
- todo(pass, name, diag);
- }
-
- public void todo_isnot(Object a, Object b, String name) {
- boolean pass = !a.equals(b);
- String diag = "didn't expect " + a.toString() + ", but got it";
- if(pass) {
- diag = a.toString() + " should not equal " + b.toString();
- }
- todo(pass, name, diag);
- }
- public void info(String name, String message) {
- testInfo test = new testInfo(true, name, message, false);
- _logMochitestResult(test, "TEST-INFO", "INFO FAILED?");
- }
+ public void finalize() {
+ // It appears that we call finalize during cleanup, this might be an invalid assertion.
+ String message;
+
+ if (mLogTestName != "") {
+ long diff = SystemClock.uptimeMillis() - mStartTime;
+ message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName;
+ message += " | finished in " + diff + "ms";
+ dumpLog(message);
+ mLogTestName = "";
+ }
+
+ message = Integer.toString(mLineNumber++) + " INFO TEST-START | Shutdown";
+ dumpLog(message);
+ message = Integer.toString(mLineNumber++) + " INFO Passed: " + Integer.toString(mPassed);
+ dumpLog(message);
+ message = Integer.toString(mLineNumber++) + " INFO Failed: " + Integer.toString(mFailed);
+ dumpLog(message);
+ message = Integer.toString(mLineNumber++) + " INFO Todo: " + Integer.toString(mTodo);
+ dumpLog(message);
+ message = Integer.toString(mLineNumber++) + " INFO SimpleTest FINISHED";
+ dumpLog(message);
+ }
+
+ public void ok(boolean condition, String name, String diag) {
+ testInfo test = new testInfo(condition, name, diag, false);
+ _logMochitestResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL");
+ mTestList.add(test);
+ }
+
+ public void is(Object a, Object b, String name) {
+ boolean pass = a.equals(b);
+ String diag = "got " + a.toString() + ", expected " + b.toString();
+ if (pass) {
+ diag = a.toString() + " should equal " + b.toString();
+ }
+ ok(pass, name, diag);
+ }
+
+ public void isnot(Object a, Object b, String name) {
+ boolean pass = !a.equals(b);
+ String diag = "didn't expect " + a.toString() + ", but got it";
+ if (pass) {
+ diag = a.toString() + " should not equal " + b.toString();
+ }
+ ok(pass, name, diag);
+ }
+
+ public void ispixel(int actual, int r, int g, int b, String name) {
+ // When we read GL pixels the GPU has already processed them and they
+ // are usually off by a little bit. For example a CSS-color pixel of color #64FFF5
+ // was turned into #63FFF7 when it came out of glReadPixels. So in order to compare
+ // against the expected value, we use a little fuzz factor. For the alpha we just
+ // make sure it is always 0xFF.
+ int aAlpha = ((actual >> 24) & 0xFF);
+ int aR = ((actual >> 16) & 0xFF);
+ int aG = ((actual >> 8) & 0xFF);
+ int aB = (actual & 0xFF);
+ boolean pass = (aAlpha == 0xFF) /* alpha */
+ && (Math.abs(aR - r) < 8) /* red */
+ && (Math.abs(aG - g) < 8) /* green */
+ && (Math.abs(aB - b) < 8); /* blue */
+ ok(pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (pass ? " " : " not") + " close enough to expected rgb(" + r + "," + g + "," + b + ")");
+ }
+
+ public void todo(boolean condition, String name, String diag) {
+ testInfo test = new testInfo(condition, name, diag, true);
+ _logMochitestResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL");
+ mTestList.add(test);
+ }
+
+ public void todo_is(Object a, Object b, String name) {
+ boolean pass = a.equals(b);
+ String diag = "got " + a.toString() + ", expected " + b.toString();
+ if (pass) {
+ diag = a.toString() + " should equal " + b.toString();
+ }
+ todo(pass, name, diag);
+ }
+
+ public void todo_isnot(Object a, Object b, String name) {
+ boolean pass = !a.equals(b);
+ String diag = "didn't expect " + a.toString() + ", but got it";
+ if (pass) {
+ diag = a.toString() + " should not equal " + b.toString();
+ }
+ todo(pass, name, diag);
+ }
+
+ public void info(String name, String message) {
+ testInfo test = new testInfo(true, name, message, false);
+ _logMochitestResult(test, "TEST-INFO", "INFO FAILED?");
+ }
}
diff --git a/build/mobile/robocop/FennecNativeActions.java.in b/build/mobile/robocop/FennecNativeActions.java.in
index 1992d87c83e..973a3fd70bb 100644
--- a/build/mobile/robocop/FennecNativeActions.java.in
+++ b/build/mobile/robocop/FennecNativeActions.java.in
@@ -59,295 +59,294 @@ import org.json.*;
import com.jayway.android.robotium.solo.Solo;
public class FennecNativeActions implements Actions {
- private Solo solo;
- private Instrumentation instr;
- private Activity geckoApp;
+ private Solo mSolo;
+ private Instrumentation mInstr;
+ private Activity mGeckoApp;
- // Objects for reflexive access of fennec classes.
- private ClassLoader classLoader;
- private Class gel;
- private Class ge;
- private Class gas;
- private Class drawListener;
- private Method registerGEL;
- private Method unregisterGEL;
- private Method sendGE;
- private Method getLayerClient;
- private Method setDrawListener;
+ // Objects for reflexive access of fennec classes.
+ private ClassLoader mClassLoader;
+ private Class mGel;
+ private Class mGe;
+ private Class mGas;
+ private Class mDrawListener;
+ private Method mRegisterGEL;
+ private Method mUnregisterGEL;
+ private Method mSendGE;
+ private Method mGetLayerClient;
+ private Method mSetDrawListener;
- public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation){
- this.solo = robocop;
- this.instr = instrumentation;
- this.geckoApp = activity;
- // Set up reflexive access of java classes and methods.
- try {
- classLoader = activity.getClassLoader();
- gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
- ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent");
- gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
- Class [] parameters = new Class[2];
- parameters[0] = String.class;
- parameters[1] = gel;
- registerGEL = gas.getMethod("registerGeckoEventListener", parameters);
- unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters);
- parameters = new Class[1];
- parameters[0] = ge;
- sendGE = gas.getMethod("sendEventToGecko", parameters);
-
- getLayerClient = activity.getClass().getMethod("getSoftwareLayerClient");
- Class gslc = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient");
- drawListener = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient$DrawListener");
- setDrawListener = gslc.getDeclaredMethod("setDrawListener", drawListener);
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (SecurityException e) {
- e.printStackTrace();
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- }
- }
-
- class wakeInvocationHandler implements InvocationHandler {
- private final GeckoEventExpecter mEventExpecter;
-
- public wakeInvocationHandler(GeckoEventExpecter expecter) {
- mEventExpecter = expecter;
- }
-
- public Object invoke(Object proxy, Method method, Object[] args) {
- String methodName = method.getName();
- //Depending on the method, return a completely different type.
- if(methodName.equals("toString")) {
- return "wakeInvocationHandler";
- }
- if(methodName.equals("equals")) {
- return this == args[0];
- }
- if(methodName.equals("clone")) {
- return this;
- }
- if(methodName.equals("hashCode")) {
- return 314;
- }
- FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
- "Waking up on "+methodName);
- mEventExpecter.notifyOfEvent();
- return null;
- }
- }
-
- class GeckoEventExpecter implements EventExpecter {
- private final String mGeckoEvent;
- private final Object[] mRegistrationParams;
- private boolean mEventReceived;
-
- GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
- mGeckoEvent = geckoEvent;
- mRegistrationParams = registrationParams;
- }
-
- public synchronized void blockForEvent() {
- while (! mEventReceived) {
+ public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation) {
+ mSolo = robocop;
+ mInstr = instrumentation;
+ mGeckoApp = activity;
+ // Set up reflexive access of java classes and methods.
try {
- this.wait();
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- break;
+ mClassLoader = activity.getClassLoader();
+ mGel = mClassLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
+ mGe = mClassLoader.loadClass("org.mozilla.gecko.GeckoEvent");
+ mGas = mClassLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
+ Class [] parameters = new Class[2];
+ parameters[0] = String.class;
+ parameters[1] = mGel;
+ mRegisterGEL = mGas.getMethod("registerGeckoEventListener", parameters);
+ mUnregisterGEL = mGas.getMethod("unregisterGeckoEventListener", parameters);
+ parameters = new Class[1];
+ parameters[0] = mGe;
+ mSendGE = mGas.getMethod("sendEventToGecko", parameters);
+
+ mGetLayerClient = activity.getClass().getMethod("getSoftwareLayerClient");
+ Class gslc = mClassLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient");
+ mDrawListener = mClassLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient$DrawListener");
+ mSetDrawListener = gslc.getDeclaredMethod("setDrawListener", mDrawListener);
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } catch (SecurityException e) {
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ }
+ }
+
+ class wakeInvocationHandler implements InvocationHandler {
+ private final GeckoEventExpecter mEventExpecter;
+
+ public wakeInvocationHandler(GeckoEventExpecter expecter) {
+ mEventExpecter = expecter;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ String methodName = method.getName();
+ //Depending on the method, return a completely different type.
+ if(methodName.equals("toString")) {
+ return "wakeInvocationHandler";
+ }
+ if(methodName.equals("equals")) {
+ return this == args[0];
+ }
+ if(methodName.equals("clone")) {
+ return this;
+ }
+ if(methodName.equals("hashCode")) {
+ return 314;
+ }
+ FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
+ "Waking up on "+methodName);
+ mEventExpecter.notifyOfEvent();
+ return null;
}
- }
- FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
- "unblocked on expecter for " + mGeckoEvent);
}
- public synchronized boolean eventReceived() {
- return mEventReceived;
- }
+ class GeckoEventExpecter implements EventExpecter {
+ private final String mGeckoEvent;
+ private final Object[] mRegistrationParams;
+ private boolean mEventReceived;
- void notifyOfEvent() {
- try {
- unregisterGEL.invoke(null, mRegistrationParams);
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
- "received event " + mGeckoEvent);
- synchronized (this) {
- mEventReceived = true;
- this.notifyAll();
- }
- }
- }
-
- public EventExpecter expectGeckoEvent(String geckoEvent) {
- FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
- "waiting for "+geckoEvent);
- try {
- Class [] interfaces = new Class[1];
- interfaces[0] = gel;
- Object[] finalParams = new Object[2];
- finalParams[0] = geckoEvent;
-
- GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams);
- wakeInvocationHandler wIH = new wakeInvocationHandler(expecter);
- Object proxy = Proxy.newProxyInstance(classLoader, interfaces, wIH);
- finalParams[1] = proxy;
- registerGEL.invoke(null, finalParams);
-
- return expecter;
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- return null;
- }
+ GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
+ mGeckoEvent = geckoEvent;
+ mRegistrationParams = registrationParams;
+ }
- class DrawListenerProxy implements InvocationHandler {
- private final PaintExpecter mPaintExpecter;
+ public synchronized void blockForEvent() {
+ while (! mEventReceived) {
+ try {
+ this.wait();
+ } catch (InterruptedException ie) {
+ ie.printStackTrace();
+ break;
+ }
+ }
+ FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
+ "unblocked on expecter for " + mGeckoEvent);
+ }
- DrawListenerProxy(PaintExpecter paintExpecter) {
- mPaintExpecter = paintExpecter;
+ public synchronized boolean eventReceived() {
+ return mEventReceived;
+ }
+
+ void notifyOfEvent() {
+ try {
+ mUnregisterGEL.invoke(null, mRegistrationParams);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
+ "received event " + mGeckoEvent);
+ synchronized (this) {
+ mEventReceived = true;
+ this.notifyAll();
+ }
+ }
}
-
- public Object invoke(Object proxy, Method method, Object[] args) {
- String methodName = method.getName();
- if ("drawFinished".equals(methodName)) {
+
+ public EventExpecter expectGeckoEvent(String geckoEvent) {
FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
- "Received drawFinished notification");
- mPaintExpecter.notifyOfEvent();
- } else if ("toString".equals(methodName)) {
- return "DrawListenerProxy";
- } else if ("equals".equals(methodName)) {
- return false;
- } else if ("hashCode".equals(methodName)) {
- return 0;
- }
- return null;
- }
- }
-
- class PaintExpecter implements RepeatedEventExpecter {
- private Object mLayerClient;
- private boolean mPaintDone;
-
- PaintExpecter() throws IllegalAccessException, InvocationTargetException {
- mLayerClient = getLayerClient.invoke(geckoApp);
- setDrawListener.invoke(mLayerClient, Proxy.newProxyInstance(classLoader, new Class[] { drawListener }, new DrawListenerProxy(this)));
- }
-
- void notifyOfEvent() {
- synchronized (this) {
- mPaintDone = true;
- this.notifyAll();
- }
- }
-
- public synchronized void blockForEvent() {
- while (! mPaintDone) {
+ "waiting for "+geckoEvent);
try {
- this.wait();
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- break;
+ Class [] interfaces = new Class[1];
+ interfaces[0] = mGel;
+ Object[] finalParams = new Object[2];
+ finalParams[0] = geckoEvent;
+
+ GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams);
+ wakeInvocationHandler wIH = new wakeInvocationHandler(expecter);
+ Object proxy = Proxy.newProxyInstance(mClassLoader, interfaces, wIH);
+ finalParams[1] = proxy;
+ mRegisterGEL.invoke(null, finalParams);
+
+ return expecter;
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
}
- }
- try {
- setDrawListener.invoke(mLayerClient, (Object)null);
- } catch (Exception e) {
- e.printStackTrace();
- }
+ return null;
}
- public synchronized boolean eventReceived() {
- return mPaintDone;
+ class DrawListenerProxy implements InvocationHandler {
+ private final PaintExpecter mPaintExpecter;
+
+ DrawListenerProxy(PaintExpecter paintExpecter) {
+ mPaintExpecter = paintExpecter;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ String methodName = method.getName();
+ if ("drawFinished".equals(methodName)) {
+ FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_DEBUG,
+ "Received drawFinished notification");
+ mPaintExpecter.notifyOfEvent();
+ } else if ("toString".equals(methodName)) {
+ return "DrawListenerProxy";
+ } else if ("equals".equals(methodName)) {
+ return false;
+ } else if ("hashCode".equals(methodName)) {
+ return 0;
+ }
+ return null;
+ }
}
- public synchronized void blockUntilClear(long millis) {
- if (millis <= 0) {
- throw new IllegalArgumentException("millis must be > 0");
- }
- // wait for at least one event
- while (! mPaintDone) {
+ class PaintExpecter implements RepeatedEventExpecter {
+ private Object mLayerClient;
+ private boolean mPaintDone;
+
+ PaintExpecter() throws IllegalAccessException, InvocationTargetException {
+ mLayerClient = mGetLayerClient.invoke(mGeckoApp);
+ mSetDrawListener.invoke(mLayerClient, Proxy.newProxyInstance(mClassLoader, new Class[] { mDrawListener }, new DrawListenerProxy(this)));
+ }
+
+ void notifyOfEvent() {
+ synchronized (this) {
+ mPaintDone = true;
+ this.notifyAll();
+ }
+ }
+
+ public synchronized void blockForEvent() {
+ while (!mPaintDone) {
+ try {
+ this.wait();
+ } catch (InterruptedException ie) {
+ ie.printStackTrace();
+ break;
+ }
+ }
+ try {
+ mSetDrawListener.invoke(mLayerClient, (Object)null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public synchronized boolean eventReceived() {
+ return mPaintDone;
+ }
+
+ public synchronized void blockUntilClear(long millis) {
+ if (millis <= 0) {
+ throw new IllegalArgumentException("millis must be > 0");
+ }
+ // wait for at least one event
+ while (!mPaintDone) {
+ try {
+ this.wait();
+ } catch (InterruptedException ie) {
+ ie.printStackTrace();
+ break;
+ }
+ }
+ // now wait for a period of millis where we don't get an event
+ long startTime = SystemClock.uptimeMillis();
+ while (true) {
+ try {
+ this.wait(millis);
+ } catch (InterruptedException ie) {
+ ie.printStackTrace();
+ break;
+ }
+ long endTime = SystemClock.uptimeMillis();
+ if (endTime - startTime >= millis) {
+ // success
+ break;
+ }
+ // we got a notify() before we could wait long enough, so we need to start over
+ startTime = endTime;
+ }
+ try {
+ mSetDrawListener.invoke(mLayerClient, (Object)null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public RepeatedEventExpecter expectPaint() {
try {
- this.wait();
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- break;
+ return new PaintExpecter();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
}
- }
- // now wait for a period of millis where we don't get an event
- long startTime = SystemClock.uptimeMillis();
- while (true) {
- try {
- this.wait(millis);
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- break;
+ }
+
+ public void sendSpecialKey(SpecialKey button) {
+ switch(button) {
+ case DOWN:
+ mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN);
+ break;
+ case UP:
+ mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_UP);
+ break;
+ case LEFT:
+ mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_LEFT);
+ break;
+ case RIGHT:
+ mInstr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_RIGHT);
+ break;
+ case ENTER:
+ mInstr.sendCharacterSync(KeyEvent.KEYCODE_ENTER);
+ break;
+ case MENU:
+ mInstr.sendCharacterSync(KeyEvent.KEYCODE_MENU);
+ break;
+ case BACK:
+ mInstr.sendCharacterSync(KeyEvent.KEYCODE_BACK);
+ break;
+ default:
+ break;
}
- long endTime = SystemClock.uptimeMillis();
- if (endTime - startTime >= millis) {
- // success
- break;
- }
- // we got a notify() before we could wait long enough, so we need to start over
- startTime = endTime;
- }
- try {
- setDrawListener.invoke(mLayerClient, (Object)null);
- } catch (Exception e) {
- e.printStackTrace();
- }
}
- }
- public RepeatedEventExpecter expectPaint() {
- try {
- return new PaintExpecter();
- } catch (Exception e) {
- e.printStackTrace();
- return null;
+ @Override
+ public void sendKeys(String input) {
+ mInstr.sendStringSync(input);
}
- }
- public void sendSpecialKey(SpecialKey button) {
- switch( button) {
- case DOWN:
- instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN);
- break;
- case UP:
- instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_UP);
- break;
- case LEFT:
- instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_LEFT);
- break;
- case RIGHT:
- instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_RIGHT);
- break;
- case ENTER:
- instr.sendCharacterSync(KeyEvent.KEYCODE_ENTER);
- break;
- case MENU:
- instr.sendCharacterSync(KeyEvent.KEYCODE_MENU);
- break;
- case BACK:
- instr.sendCharacterSync(KeyEvent.KEYCODE_BACK);
- break;
- default:
- break;
+ public void drag(int startingX, int endingX, int startingY, int endingY) {
+ mSolo.drag(startingX, endingX, startingY, endingY, 10);
}
- }
-
- @Override
- public void sendKeys(String input) {
- instr.sendStringSync(input);
- }
-
-
- public void drag(int startingX, int endingX, int startingY, int endingY) {
- solo.drag(startingX, endingX, startingY, endingY, 10);
- }
}
diff --git a/build/mobile/robocop/FennecNativeDriver.java.in b/build/mobile/robocop/FennecNativeDriver.java.in
index 76263ca7d78..eb6d048dcb5 100644
--- a/build/mobile/robocop/FennecNativeDriver.java.in
+++ b/build/mobile/robocop/FennecNativeDriver.java.in
@@ -46,8 +46,6 @@ import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.IntBuffer;
-import java.util.ArrayList;
-import java.util.LinkedList;
import java.util.HashMap;
import java.util.List;
@@ -68,396 +66,405 @@ import org.json.*;
import com.jayway.android.robotium.solo.Solo;
public class FennecNativeDriver implements Driver {
- // Map of IDs to element names.
- private HashMap locators = null;
- private Activity activity;
- private Solo solo;
+ // Map of IDs to element names.
+ private HashMap mLocators = null;
+ private Activity mActivity;
+ private Solo mSolo;
- private static String mLogFile = null;
- private static LogLevel mLogLevel = LogLevel.LOG_LEVEL_INFO;
+ private static String mLogFile = null;
+ private static LogLevel mLogLevel = LogLevel.LOG_LEVEL_INFO;
- // Objects for reflexive access of fennec classes.
- private ClassLoader classLoader;
- private Class gel;
- private Class ge;
- private Class gas;
- private Method registerGEL;
- private Method unregisterGEL;
- private Method sendGE;
- private Method _startFrameRecording;
- private Method _stopFrameRecording;
- private Method _startCheckerboardRecording;
- private Method _stopCheckerboardRecording;
- private Method _getPixels;
+ // Objects for reflexive access of fennec classes.
+ private ClassLoader mClassLoader;
+ private Class mGel;
+ private Class mGe;
+ private Class mGas;
+ private Method mRegisterGEL;
+ private Method mUnregisterGEL;
+ private Method mSendGE;
+ private Method _startFrameRecording;
+ private Method _stopFrameRecording;
+ private Method _startCheckerboardRecording;
+ private Method _stopCheckerboardRecording;
+ private Method _getPixels;
- public enum LogLevel {
- LOG_LEVEL_DEBUG(1),
- LOG_LEVEL_INFO(2),
- LOG_LEVEL_WARN(3),
- LOG_LEVEL_ERROR(4);
+ public enum LogLevel {
+ LOG_LEVEL_DEBUG(1),
+ LOG_LEVEL_INFO(2),
+ LOG_LEVEL_WARN(3),
+ LOG_LEVEL_ERROR(4);
- private int mValue;
- LogLevel(int value) {
- mValue = value;
- }
- public boolean isEnabled(LogLevel configuredLevel) {
- return mValue >= configuredLevel.getValue();
- }
- private int getValue() {
- return mValue;
- }
- }
-
- public FennecNativeDriver(Activity activity, Solo robocop){
- this.activity = activity;
- this.solo = robocop;
-
- // Set up table of fennec_ids.
- locators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt"));
-
- // Set up reflexive access of java classes and methods.
- try {
- classLoader = activity.getClassLoader();
- gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
- ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent");
- gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
- Class [] parameters = new Class[2];
- parameters[0] = String.class;
- parameters[1] = gel;
- registerGEL = gas.getMethod("registerGeckoEventListener", parameters);
- unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters);
- parameters = new Class[1];
- parameters[0] = ge;
- sendGE = gas.getMethod("sendEventToGecko", parameters);
-
- Class gfx = classLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI");
- _startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording");
- _stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording");
- _startCheckerboardRecording = gfx.getDeclaredMethod("startCheckerboardRecording");
- _stopCheckerboardRecording = gfx.getDeclaredMethod("stopCheckerboardRecording");
-
- Class layerView = classLoader.loadClass("org.mozilla.gecko.gfx.LayerView");
- _getPixels = layerView.getDeclaredMethod("getPixels");
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (SecurityException e) {
- e.printStackTrace();
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- }
- }
-
- //Information on the location of the Gecko Frame.
- private boolean geckoInfo = false;
- private int geckoTop = 100;
- private int geckoLeft = 0;
- private int geckoHeight= 700;
- private int geckoWidth = 1024;
-
- private void getGeckoInfo() {
- View geckoLayout = activity.findViewById(Integer.decode((String)locators.get("gecko_layout")));
- if (geckoLayout != null) {
- int[] pos = new int[2];
- geckoLayout.getLocationOnScreen(pos);
- geckoTop = pos[1];
- geckoLeft = pos[0];
- geckoWidth = geckoLayout.getWidth();
- geckoHeight = geckoLayout.getHeight();
- geckoInfo = true;
- } else {
- throw new RoboCopException("Unable to find view gecko_layout");
- }
- }
-
- public int getGeckoTop() {
- if(!geckoInfo) {
- getGeckoInfo();
- }
- return geckoTop;
- }
-
- public int getGeckoLeft() {
- if(!geckoInfo) {
- getGeckoInfo();
- }
- return geckoLeft;
- }
-
- public int getGeckoHeight() {
- if(!geckoInfo) {
- getGeckoInfo();
- }
- return geckoHeight;
- }
- public int getGeckoWidth() {
- if(!geckoInfo) {
- getGeckoInfo();
- }
- return geckoWidth;
- }
-
- public Element findElement(Activity activity, String name) {
- if (name == null)
- throw new IllegalArgumentException("Can not findElements when passed a null");
- if (locators.containsKey(name)){
- return new FennecNativeElement(Integer.decode((String)locators.get(name)), activity, solo);
- }
- throw new RoboCopException("Element does not exist in the list");
- }
-
- public void startFrameRecording() {
- try {
- Object [] params = null;
- _startFrameRecording.invoke(null, params);
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- }
-
- public int stopFrameRecording() {
- Class [] parameters = new Class[1];
- parameters[0] = null;
- List frames;
-
- try {
- Object [] params = null;
- frames = (List)_stopFrameRecording.invoke(null, params);
- Object [] framearray = frames.toArray();
- Long last = new Long(0);
- Long threshold = new Long(17);
- int numDelays = 0;
- for (int i=0; i < framearray.length; i++) {
- Long val = (Long)framearray[i];
- if ((val - last) > threshold) {
- numDelays++;
+ private int mValue;
+ LogLevel(int value) {
+ mValue = value;
}
- last = val;
- }
- return numDelays;
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
-
- return 0;
- }
-
- public void startCheckerboardRecording() {
- try {
- Object [] params = null;
- _startCheckerboardRecording.invoke(null, params);
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- }
-
- public float stopCheckerboardRecording() {
- Class [] parameters = new Class[1];
- parameters[0] = null;
- List checkerboard;
-
- try {
- Object [] params = null;
- checkerboard = (List)_stopCheckerboardRecording.invoke(null, params);
- Object [] amountarray = checkerboard.toArray();
- double completeness = 0;
- for (Object obj : amountarray) {
- float val = (Float)obj;
- completeness += (1.0 - (double)val) / (double)amountarray.length;
- }
- return (float)completeness;
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
-
- return 0.0f;
- }
-
- private GLSurfaceView getSurfaceView() {
- for (View v : solo.getCurrentViews()) {
- if (v instanceof GLSurfaceView) {
- return (GLSurfaceView)v;
- }
- }
- return null;
- }
-
- public int[][] getPaintedSurface() {
- GLSurfaceView view = getSurfaceView();
- if (view == null) {
- return null;
- }
- IntBuffer pixelBuffer;
- try {
- pixelBuffer = (IntBuffer)_getPixels.invoke(view);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
-
- // now we need to (1) flip the image, because GL likes to do things up-side-down,
- // and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
- int w = view.getWidth();
- int h = view.getHeight();
- pixelBuffer.position(0);
- int[][] pixels = new int[h][w];
- for (int y = h - 1; y >= 0; y--) {
- for (int x = 0; x < w; x++) {
- int agbr = pixelBuffer.get();
- pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000);
- }
- }
- return pixels;
- }
-
- class scrollHandler implements InvocationHandler {
- public scrollHandler(){};
- public Object invoke(Object proxy, Method method, Object[] args) {
- try{
- //Disect the JSON object into the appropriate variables
- JSONObject jo = ((JSONObject)args[1]);
- scrollHeight = jo.getInt("y");
- height = jo.getInt("cheight");
- //We don't want a height of 0. That means it's a bad response.
- if( height > 0) {
- pageHeight = jo.getInt("height");
+ public boolean isEnabled(LogLevel configuredLevel) {
+ return mValue >= configuredLevel.getValue();
+ }
+ private int getValue() {
+ return mValue;
}
-
- } catch( Throwable e) {
- FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN,
- "WARNING: ScrollReceived, but read wrong!");
- }
- return null;
- }
- }
- public int getScrollHeight() {
- return scrollHeight;
- }
- public int getPageHeight() {
- return pageHeight;
- }
- public int getHeight() {
- return height;
- }
-
- public int height=0;
- public int scrollHeight=0;
- public int pageHeight=10;
- public void setupScrollHandling() {
- //Setup scrollHandler to catch "robocop:scroll" events.
- try {
- Class [] interfaces = new Class[1];
- interfaces[0] = gel;
- Object[] finalParams = new Object[2];
- finalParams[0] = "robocop:scroll";
- finalParams[1] = Proxy.newProxyInstance(classLoader, interfaces, new scrollHandler());
- registerGEL.invoke(null, finalParams);
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
}
- }
+ public FennecNativeDriver(Activity activity, Solo robocop) {
+ mActivity = activity;
+ mSolo = robocop;
- //Takes a filename, loads the file,
- // and returns a string version of the entire file.
- public static String getFile(String filename)
- {
- StringBuilder text = new StringBuilder();
+ // Set up table of fennec_ids.
+ mLocators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt"));
- BufferedReader br = null;
- try {
- br = new BufferedReader(new FileReader(filename));
- String line;
-
- while ((line = br.readLine()) != null) {
- text.append(line);
- text.append('\n');
- }
- } catch(IOException e) {
- e.printStackTrace();
- } finally {
- try {
- br.close();
- } catch (IOException e) {
- }
- }
- return text.toString();
- }
-
- // Takes a string of "key=value" pairs split by \n and creates a hash table.
- public static HashMap convertTextToTable(String data)
- {
- HashMap retVal = new HashMap();
-
- String[] lines = data.split("\n");
- for (int i = 0; i < lines.length; i++) {
- String[] parts = lines[i].split("=");
- retVal.put(parts[0].trim(), parts[1].trim());
- }
- return retVal;
- }
-
- // Set the filename used for logging. If the file already exists, delete it
- // as a safe-guard against accidentally appending to an old log file.
- public static void setLogFile(String filename) {
- mLogFile = filename;
- File file = new File(mLogFile);
- if (file.exists()) {
- file.delete();
- }
- }
-
- public static void setLogLevel(LogLevel level) {
- mLogLevel = level;
- }
-
- public static void log(LogLevel level, String message) {
- if (mLogFile == null) {
- assert(false);
- }
-
- if (level.isEnabled(mLogLevel)) {
- File file = new File(mLogFile);
- BufferedWriter bw = null;
-
- try {
- bw = new BufferedWriter(new FileWriter(mLogFile, true));
- bw.write(message);
- bw.newLine();
- } catch(IOException e) {
- Log.e("Robocop", "exception with file writer on: " + mLogFile);
- } finally {
+ // Set up reflexive access of java classes and methods.
try {
- if (bw != null) {
- bw.flush();
- bw.close();
- }
- } catch (IOException ex) {
- ex.printStackTrace();
- }
- }
+ mClassLoader = activity.getClassLoader();
+ mGel = mClassLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
+ mGe = mClassLoader.loadClass("org.mozilla.gecko.GeckoEvent");
+ mGas = mClassLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
+ Class [] parameters = new Class[2];
+ parameters[0] = String.class;
+ parameters[1] = mGel;
+ mRegisterGEL = mGas.getMethod("registerGeckoEventListener", parameters);
+ mUnregisterGEL = mGas.getMethod("unregisterGeckoEventListener", parameters);
+ parameters = new Class[1];
+ parameters[0] = mGe;
+ mSendGE = mGas.getMethod("sendEventToGecko", parameters);
+
+ Class gfx = mClassLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI");
+ _startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording");
+ _stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording");
+ _startCheckerboardRecording = gfx.getDeclaredMethod("startCheckerboardRecording");
+ _stopCheckerboardRecording = gfx.getDeclaredMethod("stopCheckerboardRecording");
+
+ Class layerView = mClassLoader.loadClass("org.mozilla.gecko.gfx.LayerView");
+ _getPixels = layerView.getDeclaredMethod("getPixels");
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } catch (SecurityException e) {
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ }
}
- if (level == LogLevel.LOG_LEVEL_INFO) {
- Log.i("Robocop", message);
- } else if (level == LogLevel.LOG_LEVEL_DEBUG) {
- Log.d("Robocop", message);
- } else if (level == LogLevel.LOG_LEVEL_WARN) {
- Log.w("Robocop", message);
- } else if (level == LogLevel.LOG_LEVEL_ERROR) {
- Log.e("Robocop", message);
+ //Information on the location of the Gecko Frame.
+ private boolean mGeckoInfo = false;
+ private int mGeckoTop = 100;
+ private int mGeckoLeft = 0;
+ private int mGeckoHeight= 700;
+ private int mGeckoWidth = 1024;
+
+ private void getGeckoInfo() {
+ View geckoLayout = mActivity.findViewById(Integer.decode((String)mLocators.get("gecko_layout")));
+ if (geckoLayout != null) {
+ int[] pos = new int[2];
+ geckoLayout.getLocationOnScreen(pos);
+ mGeckoTop = pos[1];
+ mGeckoLeft = pos[0];
+ mGeckoWidth = geckoLayout.getWidth();
+ mGeckoHeight = geckoLayout.getHeight();
+ mGeckoInfo = true;
+ } else {
+ throw new RoboCopException("Unable to find view gecko_layout");
+ }
+ }
+
+ public int getGeckoTop() {
+ if (!mGeckoInfo) {
+ getGeckoInfo();
+ }
+ return mGeckoTop;
+ }
+
+ public int getGeckoLeft() {
+ if (!mGeckoInfo) {
+ getGeckoInfo();
+ }
+ return mGeckoLeft;
+ }
+
+ public int getGeckoHeight() {
+ if (!mGeckoInfo) {
+ getGeckoInfo();
+ }
+ return mGeckoHeight;
+ }
+
+ public int getGeckoWidth() {
+ if (!mGeckoInfo) {
+ getGeckoInfo();
+ }
+ return mGeckoWidth;
+ }
+
+ public Element findElement(Activity activity, String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Can not findElements when passed a null");
+ }
+ if (mLocators.containsKey(name)) {
+ return new FennecNativeElement(Integer.decode((String)mLocators.get(name)), activity, mSolo);
+ }
+ throw new RoboCopException("Element does not exist in the list");
+ }
+
+ public void startFrameRecording() {
+ try {
+ Object [] params = null;
+ _startFrameRecording.invoke(null, params);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public int stopFrameRecording() {
+ Class [] parameters = new Class[1];
+ parameters[0] = null;
+ List frames;
+
+ try {
+ Object [] params = null;
+ frames = (List)_stopFrameRecording.invoke(null, params);
+ Object [] framearray = frames.toArray();
+ Long last = new Long(0);
+ Long threshold = new Long(17);
+ int numDelays = 0;
+ for (int i=0; i < framearray.length; i++) {
+ Long val = (Long)framearray[i];
+ if ((val - last) > threshold) {
+ numDelays++;
+ }
+ last = val;
+ }
+ return numDelays;
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+
+ return 0;
+ }
+
+ public void startCheckerboardRecording() {
+ try {
+ Object [] params = null;
+ _startCheckerboardRecording.invoke(null, params);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public float stopCheckerboardRecording() {
+ Class [] parameters = new Class[1];
+ parameters[0] = null;
+ List checkerboard;
+
+ try {
+ Object [] params = null;
+ checkerboard = (List)_stopCheckerboardRecording.invoke(null, params);
+ Object [] amountarray = checkerboard.toArray();
+ double completeness = 0;
+ for (Object obj : amountarray) {
+ float val = (Float)obj;
+ completeness += (1.0 - (double)val) / (double)amountarray.length;
+ }
+ return (float)completeness;
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+
+ return 0.0f;
+ }
+
+ private GLSurfaceView getSurfaceView() {
+ for (View v : mSolo.getCurrentViews()) {
+ if (v instanceof GLSurfaceView) {
+ return (GLSurfaceView)v;
+ }
+ }
+ return null;
+ }
+
+ public int[][] getPaintedSurface() {
+ GLSurfaceView view = getSurfaceView();
+ if (view == null) {
+ return null;
+ }
+ IntBuffer pixelBuffer;
+ try {
+ pixelBuffer = (IntBuffer)_getPixels.invoke(view);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+
+ // now we need to (1) flip the image, because GL likes to do things up-side-down,
+ // and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
+ int w = view.getWidth();
+ int h = view.getHeight();
+ pixelBuffer.position(0);
+ int[][] pixels = new int[h][w];
+ for (int y = h - 1; y >= 0; y--) {
+ for (int x = 0; x < w; x++) {
+ int agbr = pixelBuffer.get();
+ pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000);
+ }
+ }
+ return pixels;
+ }
+
+ public int mHeight=0;
+ public int mScrollHeight=0;
+ public int mPageHeight=10;
+
+ class scrollHandler implements InvocationHandler {
+ public scrollHandler(){};
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ try {
+ // Disect the JSON object into the appropriate variables
+ JSONObject jo = ((JSONObject)args[1]);
+ mScrollHeight = jo.getInt("y");
+ mHeight = jo.getInt("cheight");
+ // We don't want a height of 0. That means it's a bad response.
+ if (mHeight > 0) {
+ mPageHeight = jo.getInt("height");
+ }
+
+ } catch( Throwable e) {
+ FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN,
+ "WARNING: ScrollReceived, but read wrong!");
+ }
+ return null;
+ }
+ }
+
+ public int getScrollHeight() {
+ return mScrollHeight;
+ }
+ public int getPageHeight() {
+ return mPageHeight;
+ }
+ public int getHeight() {
+ return mHeight;
+ }
+
+ public void setupScrollHandling() {
+ //Setup scrollHandler to catch "robocop:scroll" events.
+ try {
+ Class [] interfaces = new Class[1];
+ interfaces[0] = mGel;
+ Object[] finalParams = new Object[2];
+ finalParams[0] = "robocop:scroll";
+ finalParams[1] = Proxy.newProxyInstance(mClassLoader, interfaces, new scrollHandler());
+ mRegisterGEL.invoke(null, finalParams);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ /**
+ * Takes a filename, loads the file, and returns a string version of the entire file.
+ */
+ public static String getFile(String filename)
+ {
+ StringBuilder text = new StringBuilder();
+
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader(filename));
+ String line;
+
+ while ((line = br.readLine()) != null) {
+ text.append(line);
+ text.append('\n');
+ }
+ } catch(IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ br.close();
+ } catch (IOException e) {
+ }
+ }
+ return text.toString();
+ }
+
+ /**
+ * Takes a string of "key=value" pairs split by \n and creates a hash table.
+ */
+ public static HashMap convertTextToTable(String data)
+ {
+ HashMap retVal = new HashMap();
+
+ String[] lines = data.split("\n");
+ for (int i = 0; i < lines.length; i++) {
+ String[] parts = lines[i].split("=");
+ retVal.put(parts[0].trim(), parts[1].trim());
+ }
+ return retVal;
+ }
+
+ /**
+ * Set the filename used for logging. If the file already exists, delete it
+ * as a safe-guard against accidentally appending to an old log file.
+ */
+ public static void setLogFile(String filename) {
+ mLogFile = filename;
+ File file = new File(mLogFile);
+ if (file.exists()) {
+ file.delete();
+ }
+ }
+
+ public static void setLogLevel(LogLevel level) {
+ mLogLevel = level;
+ }
+
+ public static void log(LogLevel level, String message) {
+ if (mLogFile == null) {
+ assert(false);
+ }
+
+ if (level.isEnabled(mLogLevel)) {
+ File file = new File(mLogFile);
+ BufferedWriter bw = null;
+
+ try {
+ bw = new BufferedWriter(new FileWriter(mLogFile, true));
+ bw.write(message);
+ bw.newLine();
+ } catch(IOException e) {
+ Log.e("Robocop", "exception with file writer on: " + mLogFile);
+ } finally {
+ try {
+ if (bw != null) {
+ bw.flush();
+ bw.close();
+ }
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ if (level == LogLevel.LOG_LEVEL_INFO) {
+ Log.i("Robocop", message);
+ } else if (level == LogLevel.LOG_LEVEL_DEBUG) {
+ Log.d("Robocop", message);
+ } else if (level == LogLevel.LOG_LEVEL_WARN) {
+ Log.w("Robocop", message);
+ } else if (level == LogLevel.LOG_LEVEL_ERROR) {
+ Log.e("Robocop", message);
+ }
}
- }
}
diff --git a/build/mobile/robocop/FennecNativeElement.java.in b/build/mobile/robocop/FennecNativeElement.java.in
index bbbe0430404..0785a0de2bd 100644
--- a/build/mobile/robocop/FennecNativeElement.java.in
+++ b/build/mobile/robocop/FennecNativeElement.java.in
@@ -42,7 +42,6 @@ package @ANDROID_PACKAGE_NAME@;
import java.util.List;
import android.app.Activity;
-import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
@@ -55,110 +54,110 @@ import com.jayway.android.robotium.solo.Solo;
import java.util.concurrent.SynchronousQueue;
public class FennecNativeElement implements Element {
- private final Activity mActivity;
- private Integer id;
- private Solo robocop;
+ private final Activity mActivity;
+ private Integer mId;
+ private Solo mSolo;
- public FennecNativeElement(Integer id, Activity activity, Solo solo){
- this.id = id;
- mActivity = activity;
- robocop = solo;
- }
-
- public Integer getId() {
- return id;
- }
-
- public void click() {
- final SynchronousQueue syncQueue = new SynchronousQueue();
- mActivity.runOnUiThread(
- new Runnable() {
- public void run() {
- View view = (View)mActivity.findViewById(id);
- if(view != null) {
- if (!view.performClick()) {
- FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN,
- "Robocop called click on an element with no listener");
- }
- } else {
- throw new RoboCopException("click: unable to find view "+id);
- }
- syncQueue.offer(new Object());
- }
- });
- try {
- syncQueue.take();
- } catch (InterruptedException e) {
- e.printStackTrace();
+ public FennecNativeElement(Integer id, Activity activity, Solo solo) {
+ mId = id;
+ mActivity = activity;
+ mSolo = solo;
}
- }
- private Object text;
+ public Integer getId() {
+ return mId;
+ }
- public String getText() {
- final SynchronousQueue syncQueue = new SynchronousQueue();
- mActivity.runOnUiThread(
- new Runnable() {
- public void run() {
- View v = mActivity.findViewById(id);
- if(v instanceof EditText) {
- EditText et = (EditText)v;
- text = et.getEditableText();
- }else if(v instanceof TextSwitcher) {
- TextSwitcher ts = (TextSwitcher)v;
- ts.getNextView();
- text = ((TextView)ts.getCurrentView()).getText();
- }else if(v instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup)v;
- for(int i = 0; i < vg.getChildCount(); i++) {
- if(vg.getChildAt(i) instanceof TextView) {
- text = ((TextView)vg.getChildAt(i)).getText();
+ public void click() {
+ final SynchronousQueue syncQueue = new SynchronousQueue();
+ mActivity.runOnUiThread(
+ new Runnable() {
+ public void run() {
+ View view = (View)mActivity.findViewById(mId);
+ if(view != null) {
+ if (!view.performClick()) {
+ FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_WARN,
+ "Robocop called click on an element with no listener");
+ }
+ } else {
+ throw new RoboCopException("click: unable to find view "+mId);
+ }
+ syncQueue.offer(new Object());
}
- } //end of for
- } else if(v instanceof TextView) {
- text = ((TextView)v).getText();
- } else if(v == null) {
- throw new RoboCopException("getText: unable to find view "+id);
- } else {
- throw new RoboCopException("getText: unhandled type for view "+id);
- }
- syncQueue.offer(new Object());
- } // end of run() method definition
- } // end of anonymous Runnable object instantiation
- );
- try {
- //Wait for the UiThread code to finish running
- syncQueue.take();
- } catch (InterruptedException e) {
- e.printStackTrace();
+ });
+ try {
+ syncQueue.take();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
}
- if(text == null) {
- throw new RoboCopException("getText: Text is null for view "+id);
- }
- return text.toString();
- }
- private boolean displayed;
+ private Object mText;
- public boolean isDisplayed() {
- final SynchronousQueue syncQueue = new SynchronousQueue();
- displayed = false;
- mActivity.runOnUiThread(
- new Runnable() {
- public void run() {
- View view = (View)mActivity.findViewById(id);
- if(view != null) {
- displayed = true;
- }
- syncQueue.offer(new Object());
- }
- });
- try {
- syncQueue.take();
- } catch (InterruptedException e) {
- e.printStackTrace();
+ public String getText() {
+ final SynchronousQueue syncQueue = new SynchronousQueue();
+ mActivity.runOnUiThread(
+ new Runnable() {
+ public void run() {
+ View v = mActivity.findViewById(mId);
+ if (v instanceof EditText) {
+ EditText et = (EditText)v;
+ mText = et.getEditableText();
+ } else if (v instanceof TextSwitcher) {
+ TextSwitcher ts = (TextSwitcher)v;
+ ts.getNextView();
+ mText = ((TextView)ts.getCurrentView()).getText();
+ } else if (v instanceof ViewGroup) {
+ ViewGroup vg = (ViewGroup)v;
+ for (int i = 0; i < vg.getChildCount(); i++) {
+ if (vg.getChildAt(i) instanceof TextView) {
+ mText = ((TextView)vg.getChildAt(i)).getText();
+ }
+ }
+ } else if (v instanceof TextView) {
+ mText = ((TextView)v).getText();
+ } else if (v == null) {
+ throw new RoboCopException("getText: unable to find view "+mId);
+ } else {
+ throw new RoboCopException("getText: unhandled type for view "+mId);
+ }
+ syncQueue.offer(new Object());
+ } // end of run() method definition
+ } // end of anonymous Runnable object instantiation
+ );
+ try {
+ // Wait for the UiThread code to finish running
+ syncQueue.take();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (mText == null) {
+ throw new RoboCopException("getText: Text is null for view "+mId);
+ }
+ return mText.toString();
+ }
+
+ private boolean mDisplayed;
+
+ public boolean isDisplayed() {
+ final SynchronousQueue syncQueue = new SynchronousQueue();
+ mDisplayed = false;
+ mActivity.runOnUiThread(
+ new Runnable() {
+ public void run() {
+ View view = (View)mActivity.findViewById(mId);
+ if (view != null) {
+ mDisplayed = true;
+ }
+ syncQueue.offer(new Object());
+ }
+ });
+ try {
+ syncQueue.take();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return mDisplayed;
}
- return displayed;
- }
}
diff --git a/build/mobile/robocop/FennecTalosAssert.java.in b/build/mobile/robocop/FennecTalosAssert.java.in
index 5fa85a4405f..9a9d75511d4 100644
--- a/build/mobile/robocop/FennecTalosAssert.java.in
+++ b/build/mobile/robocop/FennecTalosAssert.java.in
@@ -39,52 +39,40 @@ package @ANDROID_PACKAGE_NAME@;
public class FennecTalosAssert implements Assert {
-
- public FennecTalosAssert() {
- }
+
+ public FennecTalosAssert() { }
- // Write information to a logfile and logcat
- public void dumpLog(String message)
- {
- FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message);
- }
+ /**
+ * Write information to a logfile and logcat
+ */
+ public void dumpLog(String message) {
+ FennecNativeDriver.log(FennecNativeDriver.LogLevel.LOG_LEVEL_INFO, message);
+ }
- // Set the filename used for dumpLog.
- public void setLogFile(String filename)
- {
- FennecNativeDriver.setLogFile(filename);
- }
+ /**
+ * Set the filename used for dumpLog.
+ */
+ public void setLogFile(String filename) {
+ FennecNativeDriver.setLogFile(filename);
+ }
- public void setTestName(String testName)
- {
- }
+ public void setTestName(String testName) { }
+ public void finalize() { }
- public void finalize()
- {
- }
+ public void ok(boolean condition, String name, String diag) { }
- public void ok(boolean condition, String name, String diag) {
- }
+ public void is(Object a, Object b, String name) { }
+
+ public void isnot(Object a, Object b, String name) { }
- public void is(Object a, Object b, String name) {
- }
-
- public void isnot(Object a, Object b, String name) {
- }
+ public void ispixel(int actual, int r, int g, int b, String name) { }
- public void ispixel(int actual, int r, int g, int b, String name) {
- }
+ public void todo(boolean condition, String name, String diag) { }
- public void todo(boolean condition, String name, String diag) {
- }
+ public void todo_is(Object a, Object b, String name) { }
+
+ public void todo_isnot(Object a, Object b, String name) { }
- public void todo_is(Object a, Object b, String name) {
- }
-
- public void todo_isnot(Object a, Object b, String name) {
- }
-
- public void info(String name, String message) {
- }
+ public void info(String name, String message) { }
}
diff --git a/build/mobile/robocop/RoboCopException.java.in b/build/mobile/robocop/RoboCopException.java.in
index de908cc3213..adc5b6af519 100644
--- a/build/mobile/robocop/RoboCopException.java.in
+++ b/build/mobile/robocop/RoboCopException.java.in
@@ -40,20 +40,20 @@
package @ANDROID_PACKAGE_NAME@;
public class RoboCopException extends RuntimeException {
-
- public RoboCopException(){
- super();
- }
-
- public RoboCopException(String message){
- super(message);
- }
-
- public RoboCopException(Throwable cause){
- super(cause);
- }
-
- public RoboCopException(String message, Throwable cause){
- super(message, cause);
- }
+
+ public RoboCopException() {
+ super();
+ }
+
+ public RoboCopException(String message) {
+ super(message);
+ }
+
+ public RoboCopException(Throwable cause) {
+ super(cause);
+ }
+
+ public RoboCopException(String message, Throwable cause) {
+ super(message, cause);
+ }
}
diff --git a/build/unix/build-toolchain/build-gcc.py b/build/unix/build-toolchain/build-gcc.py
index 3b46b6742e3..496e6bce939 100755
--- a/build/unix/build-toolchain/build-gcc.py
+++ b/build/unix/build-toolchain/build-gcc.py
@@ -1,5 +1,18 @@
#!/usr/bin/python
+# The directories end up in the debug info, so the easy way of getting
+# a reproducible build is to run it in a know absolute directory.
+# We use a directory in /builds/slave because the mozilla infrastructure
+# cleans it up automatically.
+base_dir = "/builds/slave/moz-toolschain"
+
+source_dir = base_dir + "/src"
+build_dir = base_dir + "/build"
+aux_inst_dir = build_dir + '/aux_inst'
+old_make = aux_inst_dir + '/bin/make'
+
+##############################################
+
import urllib
import os
import os.path
@@ -33,17 +46,21 @@ def patch(patch, plevel, srcdir):
check_run(['patch', '-d', srcdir, '-p%s' % plevel, '-i', patch, '--fuzz=0',
'-s'])
-def build_package(package_source_dir, package_build_dir, configure_args):
+def build_package(package_source_dir, package_build_dir, configure_args,
+ make = old_make):
os.mkdir(package_build_dir)
run_in(package_build_dir,
["%s/configure" % package_source_dir] + configure_args)
- run_in(package_build_dir, ["make", "-j8"])
- run_in(package_build_dir, ["make", "install"])
+ run_in(package_build_dir, [make, "-j8"])
+ run_in(package_build_dir, [make, "install"])
-def build_tar(base_dir, tar_inst_dir):
+def build_aux_tools(base_dir):
+ make_build_dir = base_dir + '/make_build'
+ build_package(make_source_dir, make_build_dir,
+ ["--prefix=%s" % aux_inst_dir], "make")
tar_build_dir = base_dir + '/tar_build'
build_package(tar_source_dir, tar_build_dir,
- ["--prefix=%s" % tar_inst_dir])
+ ["--prefix=%s" % aux_inst_dir])
def with_env(env, f):
old_env = os.environ.copy()
@@ -133,21 +150,13 @@ def build_tar_package(tar, name, base, directory):
##############################################
-# The directories end up in the debug info, so the easy way of getting
-# a reproducible build is to run it in a know absolute directory.
-# We use a directory in /builds/slave because the mozilla infrastructure
-# cleans it up automatically.
-base_dir = "/builds/slave/moz-toolschain"
-
-source_dir = base_dir + "/src"
-build_dir = base_dir + "/build"
-
def build_source_dir(prefix, version):
return source_dir + '/' + prefix + version
binutils_version = "2.21.1"
-glibc_version = "2.12.2" #FIXME: should probably use 2.5.1
+glibc_version = "2.5.1"
tar_version = "1.26"
+make_version = "3.81"
gcc_version = "4.5.2"
mpfr_version = "2.4.2"
gmp_version = "5.0.1"
@@ -159,6 +168,8 @@ glibc_source_uri = "http://ftp.gnu.org/gnu/glibc/glibc-%s.tar.bz2" % \
glibc_version
tar_source_uri = "http://ftp.gnu.org/gnu/tar/tar-%s.tar.bz2" % \
tar_version
+make_source_uri = "http://ftp.gnu.org/gnu/make/make-%s.tar.bz2" % \
+ make_version
gcc_source_uri = "http://ftp.gnu.org/gnu/gcc/gcc-%s/gcc-%s.tar.bz2" % \
(gcc_version, gcc_version)
mpfr_source_uri = "http://www.mpfr.org/mpfr-%s/mpfr-%s.tar.bz2" % \
@@ -170,6 +181,7 @@ mpc_source_uri = "http://www.multiprecision.org/mpc/download/mpc-%s.tar.gz" % \
binutils_source_tar = download_uri(binutils_source_uri)
glibc_source_tar = download_uri(glibc_source_uri)
tar_source_tar = download_uri(tar_source_uri)
+make_source_tar = download_uri(make_source_uri)
mpc_source_tar = download_uri(mpc_source_uri)
mpfr_source_tar = download_uri(mpfr_source_uri)
gmp_source_tar = download_uri(gmp_source_uri)
@@ -178,6 +190,7 @@ gcc_source_tar = download_uri(gcc_source_uri)
binutils_source_dir = build_source_dir('binutils-', binutils_version)
glibc_source_dir = build_source_dir('glibc-', glibc_version)
tar_source_dir = build_source_dir('tar-', tar_version)
+make_source_dir = build_source_dir('make-', make_version)
mpc_source_dir = build_source_dir('mpc-', mpc_version)
mpfr_source_dir = build_source_dir('mpfr-', mpfr_version)
gmp_source_dir = build_source_dir('gmp-', gmp_version)
@@ -191,6 +204,7 @@ if not os.path.exists(source_dir):
patch('glibc-deterministic.patch', 1, glibc_source_dir)
run_in(glibc_source_dir, ["autoconf"])
extract(tar_source_tar, source_dir)
+ extract(make_source_tar, source_dir)
extract(mpc_source_tar, source_dir)
extract(mpfr_source_tar, source_dir)
extract(gmp_source_tar, source_dir)
@@ -203,19 +217,18 @@ if os.path.exists(build_dir):
shutil.rmtree(build_dir)
os.makedirs(build_dir)
-tar_inst_dir = build_dir + '/tar_inst'
-build_tar(build_dir, tar_inst_dir)
+build_aux_tools(build_dir)
stage1_dir = build_dir + '/stage1'
build_one_stage({"CC": "gcc", "CXX" : "g++"}, stage1_dir, True)
stage1_tool_inst_dir = stage1_dir + '/inst'
stage2_dir = build_dir + '/stage2'
-build_one_stage({"CC" : stage1_tool_inst_dir + "/bin/gcc",
+build_one_stage({"CC" : stage1_tool_inst_dir + "/bin/gcc -fgnu89-inline",
"CXX" : stage1_tool_inst_dir + "/bin/g++",
"AR" : stage1_tool_inst_dir + "/bin/ar",
"RANLIB" : "true" },
stage2_dir, False)
-build_tar_package(tar_inst_dir + "/bin/tar",
+build_tar_package(aux_inst_dir + "/bin/tar",
"toolchain.tar", stage2_dir, "inst")
diff --git a/build/unix/build-toolchain/glibc-deterministic.patch b/build/unix/build-toolchain/glibc-deterministic.patch
index 016fbf1ea72..bc5a11f0920 100644
--- a/build/unix/build-toolchain/glibc-deterministic.patch
+++ b/build/unix/build-toolchain/glibc-deterministic.patch
@@ -1,7 +1,22 @@
diff -ru a/configure.in b/configure.in
--- a/configure.in 2011-01-17 23:34:07.000000000 -0500
+++ b/configure.in 2012-01-25 20:40:27.919485606 -0500
-@@ -2230,6 +2230,7 @@
+@@ -841,14 +841,6 @@
+ LIBC_PROG_BINUTILS
+ AC_SUBST(MIG)dnl Needed by sysdeps/mach/configure.in
+
+-# Accept binutils 2.13 or newer.
+-AC_CHECK_PROG_VER(AS, $AS, --version,
+- [GNU assembler.* \([0-9]*\.[0-9.]*\)],
+- [2.1[3-9]*], AS=: critic_missing="$critic_missing as")
+-AC_CHECK_PROG_VER(LD, $LD, --version,
+- [GNU ld.* \([0-9][0-9]*\.[0-9.]*\)],
+- [2.1[3-9]*], LD=: critic_missing="$critic_missing ld")
+-
+ # We need the physical current working directory. We cannot use the
+ # "pwd -P" shell builtin since that's not portable. Instead we try to
+ # find a pwd binary. Note that assigning to the PWD environment
+@@ -2175,6 +2167,7 @@
fi
AC_SUBST(old_glibc_headers)
@@ -12,7 +27,7 @@ diff -ru a/configure.in b/configure.in
diff -ru a/csu/Makefile b/csu/Makefile
--- a/csu/Makefile 2011-01-17 23:34:07.000000000 -0500
+++ b/csu/Makefile 2012-01-23 13:58:28.957792633 -0500
-@@ -234,8 +234,7 @@
+@@ -223,8 +223,7 @@
if [ -z "$$os" ]; then \
os=Linux; \
fi; \
@@ -22,10 +37,58 @@ diff -ru a/csu/Makefile b/csu/Makefile
*) ;; \
esac; \
files="$(all-Banner-files)"; \
+diff -ru a/elf/Makefile b/elf/Makefile
+--- a/elf/Makefile 2008-10-31 16:35:11.000000000 -0400
++++ b/elf/Makefile 2012-02-16 12:20:00.038593752 -0500
+@@ -295,18 +295,11 @@
+ z-now-yes = -Wl,-z,now
+
+ $(objpfx)ld.so: $(objpfx)librtld.os $(ld-map)
+- @rm -f $@.lds
+- $(LINK.o) -nostdlib -nostartfiles -shared $(z-now-$(bind-now)) \
+- $(LDFLAGS-rtld) -Wl,-z,defs -Wl,--verbose 2>&1 | \
+- LC_ALL=C \
+- sed -e '/^=========/,/^=========/!d;/^=========/d' \
+- -e 's/\. = 0 + SIZEOF_HEADERS;/& _begin = . - SIZEOF_HEADERS;/' \
+- > $@.lds
+ $(LINK.o) -nostdlib -nostartfiles -shared -o $@ \
+ $(LDFLAGS-rtld) -Wl,-z,defs $(z-now-$(bind-now)) \
+ $(filter-out $(map-file),$^) $(load-map-file) \
+- -Wl,-soname=$(rtld-installed-name) -T $@.lds
+- rm -f $@.lds
++ -Wl,-soname=$(rtld-installed-name) \
++ -Wl,-defsym=_begin=0
+
+ # interp.c exists just to get this string into the libraries.
+ CFLAGS-interp.c = -D'RUNTIME_LINKER="$(slibdir)/$(rtld-installed-name)"' \
+diff -ru a/localedata/Makefile b/localedata/Makefile
+--- a/localedata/Makefile 2006-04-26 01:14:03.000000000 -0400
++++ b/localedata/Makefile 2012-02-17 10:31:24.592345047 -0500
+@@ -113,7 +113,7 @@
+ $(make-target-directory)
+ rm -f $(@:.gz=) $@
+ $(INSTALL_DATA) $< $(@:.gz=)
+- gzip -9 $(@:.gz=)
++ gzip -9n $(@:.gz=)
+
+ # Install the locale source files in the appropriate directory.
+ $(inst_i18ndir)/locales/%: locales/% $(+force); $(do-install)
+diff -ru a/Makeconfig b/Makeconfig
+--- a/Makeconfig 2006-07-10 17:42:27.000000000 -0400
++++ b/Makeconfig 2012-02-17 08:28:31.859584817 -0500
+@@ -674,7 +674,7 @@
+ $(foreach lib,$(libof-$(basename $(@F))) \
+ $(libof-$(= r6b
+dnl creates some (http://code.google.com/p/android/issues/detail?id=23203)
+dnl We however want to avoid these text relocations, and this can be done
+dnl by making gcc not link crtbegin and crtend. In the broken NDKs, crtend
+dnl doesn't contain anything at all, beside placeholders for some sections,
+dnl and crtbegin only contains a finalizer function that calls
+dnl __cxa_finalize. The custom linker actually takes care of calling
+dnl __cxa_finalize when the library doesn't call it itself, which makes it
+dnl safe not to link crtbegin. Besides, previous versions of the NDK didn't
+dnl link crtbegin and crtend at all.
+if test -n "$MOZ_LINKER" -a -z "$MOZ_OLD_LINKER" -a "$OS_TARGET" = "Android"; then
+ AC_CACHE_CHECK([whether the CRT objects have text relocations],
+ ac_cv_crt_has_text_relocations,
+ [echo 'int foo() { return 0; }' > conftest.cpp
+ if AC_TRY_COMMAND(${CXX-g++} -o conftest${DLL_SUFFIX} $CXXFLAGS $DSO_LDOPTS $LDFLAGS conftest.cpp $LIBS 1>&5) &&
+ test -s conftest${DLL_SUFFIX}; then
+ if readelf -d conftest${DLL_SUFFIX} | grep TEXTREL > /dev/null; then
+ ac_cv_crt_has_text_relocations=yes
+ else
+ ac_cv_crt_has_text_relocations=no
+ fi
+ else
+ AC_ERROR([couldn't compile a simple C file])
+ fi
+ rm -rf conftest*])
+ if test "$ac_cv_crt_has_text_relocations" = yes; then
+ dnl While we want libraries to skip the CRT files, we don't want
+ dnl executables to be treated the same way. We thus set the flag
+ dnl in DSO_LDOPTS and not LDFLAGS. However, to pass it to nspr,
+ dnl we need to use LDFLAGS because nspr doesn't inherit DSO_LDOPTS.
+ dnl Using LDFLAGS in nspr is safe, since we only really build
+ dnl libraries there.
+ DSO_LDOPTS="$DSO_LDOPTS -nostartfiles"
+ NSPR_LDFLAGS=-nostartfiles
+ fi
+fi
+
dnl Check for the existence of various allocation headers/functions
MALLOC_H=
@@ -4605,6 +4663,7 @@ LIBJPEG_TURBO_AS=
LIBJPEG_TURBO_ASFLAGS=
LIBJPEG_TURBO_X86_ASM=
LIBJPEG_TURBO_X64_ASM=
+LIBJPEG_TURBO_ARM_ASM=
MOZ_PANGO=1
MOZ_PERMISSIONS=1
MOZ_PLACES=1
@@ -6163,38 +6222,51 @@ if test -n "$MOZ_LIBJPEG_TURBO"; then
LIBJPEG_TURBO_ASFLAGS="-f win64 -rnasm -pnasm -D__x86_64__ -DPIC -DWIN64 -DMSVC"
LIBJPEG_TURBO_X64_ASM=1
;;
+ *:arm*)
+ LIBJPEG_TURBO_ASFLAGS="-march=armv7-a -mfpu=neon"
+ LIBJPEG_TURBO_ARM_ASM=1
+ ;;
esac
fi
-dnl If we're on a system which supports libjpeg-turbo's asm routines and
-dnl --disable-libjpeg-turbo wasn't passed, check for yasm, and error out if it
-dnl doesn't exist or we have too old of a version.
+dnl If we're on an x86 or x64 system which supports libjpeg-turbo's asm routines
+dnl and --disable-libjpeg-turbo wasn't passed, check for Yasm, and error out if
+dnl it doesn't exist or we have too old of a version.
if test -n "$LIBJPEG_TURBO_X86_ASM" -o -n "$LIBJPEG_TURBO_X64_ASM" ; then
- AC_MSG_CHECKING([for YASM assembler])
+ AC_MSG_CHECKING([for Yasm assembler])
AC_CHECK_PROGS(LIBJPEG_TURBO_AS, yasm, "")
if test -z "$LIBJPEG_TURBO_AS" ; then
- AC_MSG_ERROR([yasm is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you do not appear to have yasm installed. Either install it or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder. See https://developer.mozilla.org/en/YASM for more details.])
+ AC_MSG_ERROR([Yasm is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you do not appear to have Yasm installed. Either install it or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder. See https://developer.mozilla.org/en/YASM for more details.])
fi
dnl Check that we have the right yasm version. We require 1.0.1 or newer
dnl on Linux and 1.1 or newer everywhere else.
if test "$OS_ARCH" = "Linux" ; then
if test "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -eq "0" -a "$_YASM_RELEASE" -lt "1" \) ; then
- AC_MSG_ERROR([yasm 1.0.1 or greater is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION.$_YASM_RELEASE. Upgrade to the newest version or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder. See https://developer.mozilla.org/en/YASM for more details.])
+ AC_MSG_ERROR([Yasm 1.0.1 or greater is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION.$_YASM_RELEASE. Upgrade to the newest version or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder. See https://developer.mozilla.org/en/YASM for more details.])
fi
else
if test "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -lt "1" \) ; then
- AC_MSG_ERROR([yasm 1.1 or greater is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION. Upgrade to the newest version or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder. See https://developer.mozilla.org/en/YASM for more details.])
+ AC_MSG_ERROR([Yasm 1.1 or greater is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION. Upgrade to the newest version or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder. See https://developer.mozilla.org/en/YASM for more details.])
fi
fi
fi
+dnl If we're on an ARM system which supports libjpeg-turbo's asm routines and
+dnl --disable-libjpeg-turbo wasn't passed, use the C compiler as the assembler.
+if test -n "$LIBJPEG_TURBO_ARM_ASM" ; then
+ echo "Using $AS as the assembler for ARM code."
+ LIBJPEG_TURBO_AS=$AS
+fi
+
if test -n "$LIBJPEG_TURBO_X86_ASM"; then
AC_DEFINE(LIBJPEG_TURBO_X86_ASM)
elif test -n "$LIBJPEG_TURBO_X64_ASM"; then
AC_DEFINE(LIBJPEG_TURBO_X64_ASM)
+elif test -n "$LIBJPEG_TURBO_ARM_ASM"; then
+ AC_DEFINE(LIBJPEG_TURBO_ARM_ASM)
elif test -n "$MOZ_LIBJPEG_TURBO"; then
dnl Warn if we're not building the optimized routines, even though the user
dnl didn't specify --disable-libjpeg-turbo.
@@ -8565,8 +8637,6 @@ AC_SUBST(MOZ_APP_VERSION)
AC_DEFINE_UNQUOTED(MOZ_UA_FIREFOX_VERSION, "$FIREFOX_VERSION")
AC_DEFINE_UNQUOTED(FIREFOX_VERSION,$FIREFOX_VERSION)
AC_SUBST(FIREFOX_VERSION)
-AC_DEFINE_UNQUOTED(MOZ_UA_BUILDID, "$MOZ_UA_BUILDID")
-AC_SUBST(MOZ_UA_BUILDID)
# We can't use the static application.ini data when building against
# a libxul SDK.
@@ -8736,6 +8806,7 @@ AC_SUBST(LIBJPEG_TURBO_AS)
AC_SUBST(LIBJPEG_TURBO_ASFLAGS)
AC_SUBST(LIBJPEG_TURBO_X86_ASM)
AC_SUBST(LIBJPEG_TURBO_X64_ASM)
+AC_SUBST(LIBJPEG_TURBO_ARM_ASM)
AC_MSG_CHECKING([for posix_fallocate])
AC_TRY_LINK([#define _XOPEN_SOURCE 600
@@ -9070,7 +9141,11 @@ if test -z "$MOZ_NATIVE_NSPR"; then
_SAVE_CPPFLAGS="$CPPFLAGS"
export CPPFLAGS="-include $_topsrcdir/mozglue/linker/dladdr.h $CPPFLAGS"
fi
+ _SAVE_LDFLAGS="$LDFLAGS"
+ export LDFLAGS="$LDFLAGS $NSPR_LDFLAGS"
AC_OUTPUT_SUBDIRS(nsprpub)
+ unset LDFLAGS
+ LDFLAGS="$_SAVE_LDFLAGS"
if test -n "$MOZ_LINKER" -a -z "$MOZ_OLD_LINKER" -a "$ac_cv_func_dladdr" = no; then
unset CPPFLAGS
CPPFLAGS="$_SAVE_CFLAGS"
diff --git a/content/base/crashtests/700512-worker.js b/content/base/crashtests/700512-worker.js
index 83b732ab570..fcb558fcf45 100644
--- a/content/base/crashtests/700512-worker.js
+++ b/content/base/crashtests/700512-worker.js
@@ -1,7 +1,7 @@
onmessage = function(event) {
var blob = event.data;
- blob.mozSlice(1, 5);
+ blob.slice(1, 5);
postMessage("done");
}
diff --git a/content/base/public/nsIDOMFile.idl b/content/base/public/nsIDOMFile.idl
index 6d47a5fc5b8..7c9913fe203 100644
--- a/content/base/public/nsIDOMFile.idl
+++ b/content/base/public/nsIDOMFile.idl
@@ -71,9 +71,9 @@ interface nsIDOMBlob : nsISupports
// blob: protocol handler
[noscript] DOMString getInternalUrl(in nsIPrincipal principal);
- [optional_argc] nsIDOMBlob mozSlice([optional] in long long start,
- [optional] in long long end,
- [optional] in DOMString contentType);
+ [optional_argc] nsIDOMBlob slice([optional] in long long start,
+ [optional] in long long end,
+ [optional] in DOMString contentType);
// Get internal id of stored file. Returns -1 if it is not a stored file.
// Intended only for testing. It can be called on any thread.
diff --git a/content/base/src/nsAttrAndChildArray.h b/content/base/src/nsAttrAndChildArray.h
index a3e66c1e401..2e56ac5ddfe 100644
--- a/content/base/src/nsAttrAndChildArray.h
+++ b/content/base/src/nsAttrAndChildArray.h
@@ -136,6 +136,10 @@ public:
}
PRInt64 SizeOf() const;
+ bool HasMappedAttrs() const
+ {
+ return MappedAttrCount();
+ }
private:
nsAttrAndChildArray(const nsAttrAndChildArray& aOther) MOZ_DELETE;
diff --git a/content/base/src/nsAttrValue.cpp b/content/base/src/nsAttrValue.cpp
index f0791f2b99a..dd9b995358f 100644
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -54,6 +54,8 @@
namespace css = mozilla::css;
+using mozilla::SVGAttrValueWrapper;
+
#define MISC_STR_PTR(_cont) \
reinterpret_cast((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK)
@@ -76,6 +78,12 @@ nsAttrValue::nsAttrValue(const nsAString& aValue)
SetTo(aValue);
}
+nsAttrValue::nsAttrValue(nsIAtom* aValue)
+ : mBits(0)
+{
+ SetTo(aValue);
+}
+
nsAttrValue::nsAttrValue(css::StyleRule* aValue, const nsAString* aSerialized)
: mBits(0)
{
@@ -260,7 +268,13 @@ nsAttrValue::SetTo(const nsAttrValue& aOther)
}
default:
{
- NS_NOTREACHED("unknown type stored in MiscContainer");
+ if (IsSVGType(otherCont->mType)) {
+ // All SVG types are just pointers to classes and will therefore have
+ // the same size so it doesn't really matter which one we assign
+ cont->mSVGAngle = otherCont->mSVGAngle;
+ } else {
+ NS_NOTREACHED("unknown type stored in MiscContainer");
+ }
break;
}
}
@@ -290,6 +304,16 @@ nsAttrValue::SetTo(const nsAString& aValue)
}
}
+void
+nsAttrValue::SetTo(nsIAtom* aValue)
+{
+ ResetIfSet();
+ if (aValue) {
+ NS_ADDREF(aValue);
+ SetPtrValueAndType(aValue, eAtomBase);
+ }
+}
+
void
nsAttrValue::SetTo(PRInt16 aInt)
{
@@ -297,6 +321,24 @@ nsAttrValue::SetTo(PRInt16 aInt)
SetIntValueAndType(aInt, eInteger, nsnull);
}
+void
+nsAttrValue::SetTo(PRInt32 aInt, const nsAString* aSerialized)
+{
+ ResetIfSet();
+ SetIntValueAndType(aInt, eInteger, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(double aValue, const nsAString* aSerialized)
+{
+ if (EnsureEmptyMiscContainer()) {
+ MiscContainer* cont = GetMiscContainer();
+ cont->mDoubleValue = aValue;
+ cont->mType = eDoubleValue;
+ SetMiscAtomOrString(aSerialized);
+ }
+}
+
void
nsAttrValue::SetTo(css::StyleRule* aValue, const nsAString* aSerialized)
{
@@ -331,6 +373,115 @@ nsAttrValue::SetToSerialized(const nsAttrValue& aOther)
}
}
+void
+nsAttrValue::SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized)
+{
+ SetSVGType(eSVGAngle, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const nsSVGIntegerPair& aValue, const nsAString* aSerialized)
+{
+ SetSVGType(eSVGIntegerPair, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const nsSVGLength2& aValue, const nsAString* aSerialized)
+{
+ SetSVGType(eSVGLength, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGLengthList& aValue,
+ const nsAString* aSerialized)
+{
+ // While an empty string will parse as a length list, there's no need to store
+ // it (and SetMiscAtomOrString will assert if we try)
+ if (aSerialized && aSerialized->IsEmpty()) {
+ aSerialized = nsnull;
+ }
+ SetSVGType(eSVGLengthList, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGNumberList& aValue,
+ const nsAString* aSerialized)
+{
+ // While an empty string will parse as a number list, there's no need to store
+ // it (and SetMiscAtomOrString will assert if we try)
+ if (aSerialized && aSerialized->IsEmpty()) {
+ aSerialized = nsnull;
+ }
+ SetSVGType(eSVGNumberList, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const nsSVGNumberPair& aValue, const nsAString* aSerialized)
+{
+ SetSVGType(eSVGNumberPair, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGPathData& aValue,
+ const nsAString* aSerialized)
+{
+ // While an empty string will parse as path data, there's no need to store it
+ // (and SetMiscAtomOrString will assert if we try)
+ if (aSerialized && aSerialized->IsEmpty()) {
+ aSerialized = nsnull;
+ }
+ SetSVGType(eSVGPathData, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGPointList& aValue,
+ const nsAString* aSerialized)
+{
+ // While an empty string will parse as a point list, there's no need to store
+ // it (and SetMiscAtomOrString will assert if we try)
+ if (aSerialized && aSerialized->IsEmpty()) {
+ aSerialized = nsnull;
+ }
+ SetSVGType(eSVGPointList, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGAnimatedPreserveAspectRatio& aValue,
+ const nsAString* aSerialized)
+{
+ SetSVGType(eSVGPreserveAspectRatio, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGStringList& aValue,
+ const nsAString* aSerialized)
+{
+ // While an empty string will parse as a string list, there's no need to store
+ // it (and SetMiscAtomOrString will assert if we try)
+ if (aSerialized && aSerialized->IsEmpty()) {
+ aSerialized = nsnull;
+ }
+ SetSVGType(eSVGStringList, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const mozilla::SVGTransformList& aValue,
+ const nsAString* aSerialized)
+{
+ // While an empty string will parse as a transform list, there's no need to
+ // store it (and SetMiscAtomOrString will assert if we try)
+ if (aSerialized && aSerialized->IsEmpty()) {
+ aSerialized = nsnull;
+ }
+ SetSVGType(eSVGTransformList, &aValue, aSerialized);
+}
+
+void
+nsAttrValue::SetTo(const nsSVGViewBox& aValue, const nsAString* aSerialized)
+{
+ SetSVGType(eSVGViewBox, &aValue, aSerialized);
+}
+
void
nsAttrValue::SwapValueWith(nsAttrValue& aOther)
{
@@ -428,6 +579,73 @@ nsAttrValue::ToString(nsAString& aResult) const
aResult.AppendFloat(GetDoubleValue());
break;
}
+ case eSVGAngle:
+ {
+ SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGAngle, aResult);
+ break;
+ }
+ case eSVGIntegerPair:
+ {
+ SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGIntegerPair,
+ aResult);
+ break;
+ }
+ case eSVGLength:
+ {
+ SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGLength, aResult);
+ break;
+ }
+ case eSVGLengthList:
+ {
+ SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGLengthList,
+ aResult);
+ break;
+ }
+ case eSVGNumberList:
+ {
+ SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGNumberList,
+ aResult);
+ break;
+ }
+ case eSVGNumberPair:
+ {
+ SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGNumberPair,
+ aResult);
+ break;
+ }
+ case eSVGPathData:
+ {
+ SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGPathData, aResult);
+ break;
+ }
+ case eSVGPointList:
+ {
+ SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGPointList, aResult);
+ break;
+ }
+ case eSVGPreserveAspectRatio:
+ {
+ SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGPreserveAspectRatio,
+ aResult);
+ break;
+ }
+ case eSVGStringList:
+ {
+ SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGStringList,
+ aResult);
+ break;
+ }
+ case eSVGTransformList:
+ {
+ SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGTransformList,
+ aResult);
+ break;
+ }
+ case eSVGViewBox:
+ {
+ SVGAttrValueWrapper::ToString(GetMiscContainer()->mSVGViewBox, aResult);
+ break;
+ }
default:
{
aResult.Truncate();
@@ -614,6 +832,10 @@ nsAttrValue::HashValue() const
}
default:
{
+ if (IsSVGType(cont->mType)) {
+ // All SVG types are just pointers to classes so we can treat them alike
+ return NS_PTR_TO_INT32(cont->mSVGAngle);
+ }
NS_NOTREACHED("unknown type stored in MiscContainer");
return 0;
}
@@ -706,6 +928,16 @@ nsAttrValue::Equals(const nsAttrValue& aOther) const
}
default:
{
+ if (IsSVGType(thisCont->mType)) {
+ // Currently this method is never called for nsAttrValue objects that
+ // point to SVG data types.
+ // If that changes then we probably want to add methods to the
+ // corresponding SVG types to compare their base values.
+ // As a shortcut, however, we can begin by comparing the pointers.
+ NS_ABORT_IF_FALSE(false, "Comparing nsAttrValues that point to SVG "
+ "data");
+ return false;
+ }
NS_NOTREACHED("unknown type stored in MiscContainer");
return false;
}
@@ -1351,6 +1583,22 @@ nsAttrValue::ResetMiscAtomOrString()
}
}
+void
+nsAttrValue::SetSVGType(ValueType aType, const void* aValue,
+ const nsAString* aSerialized) {
+ NS_ABORT_IF_FALSE(IsSVGType(aType), "Not an SVG type");
+ if (EnsureEmptyMiscContainer()) {
+ MiscContainer* cont = GetMiscContainer();
+ // All SVG types are just pointers to classes so just setting any of them
+ // will do. We'll lose type-safety but the signature of the calling
+ // function should ensure we don't get anything unexpected, and once we
+ // stick aValue in a union we lose type information anyway.
+ cont->mSVGAngle = static_cast(aValue);
+ cont->mType = aType;
+ SetMiscAtomOrString(aSerialized);
+ }
+}
+
bool
nsAttrValue::EnsureEmptyMiscContainer()
{
diff --git a/content/base/src/nsAttrValue.h b/content/base/src/nsAttrValue.h
index 8b797248f11..7e90f880750 100644
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -51,6 +51,7 @@
#include "nsCaseTreatment.h"
#include "nsMargin.h"
#include "nsCOMPtr.h"
+#include "SVGAttrValueWrapper.h"
typedef PRUptrdiff PtrBits;
class nsAString;
@@ -102,6 +103,7 @@ public:
nsAttrValue();
nsAttrValue(const nsAttrValue& aOther);
explicit nsAttrValue(const nsAString& aValue);
+ explicit nsAttrValue(nsIAtom* aValue);
nsAttrValue(mozilla::css::StyleRule* aValue, const nsAString* aSerialized);
explicit nsAttrValue(const nsIntMargin& aValue);
~nsAttrValue();
@@ -123,9 +125,23 @@ public:
// Values below here won't matter, they'll be always stored in the 'misc'
// struct.
eCSSStyleRule = 0x10
- ,eAtomArray = 0x11
+ ,eAtomArray = 0x11
,eDoubleValue = 0x12
,eIntMarginValue = 0x13
+ ,eSVGTypesBegin = 0x14
+ ,eSVGAngle = eSVGTypesBegin
+ ,eSVGIntegerPair = 0x15
+ ,eSVGLength = 0x16
+ ,eSVGLengthList = 0x17
+ ,eSVGNumberList = 0x18
+ ,eSVGNumberPair = 0x19
+ ,eSVGPathData = 0x20
+ ,eSVGPointList = 0x21
+ ,eSVGPreserveAspectRatio = 0x22
+ ,eSVGStringList = 0x23
+ ,eSVGTransformList = 0x24
+ ,eSVGViewBox = 0x25
+ ,eSVGTypesEnd = 0x34
};
ValueType Type() const;
@@ -134,9 +150,29 @@ public:
void SetTo(const nsAttrValue& aOther);
void SetTo(const nsAString& aValue);
+ void SetTo(nsIAtom* aValue);
void SetTo(PRInt16 aInt);
+ void SetTo(PRInt32 aInt, const nsAString* aSerialized);
+ void SetTo(double aValue, const nsAString* aSerialized);
void SetTo(mozilla::css::StyleRule* aValue, const nsAString* aSerialized);
void SetTo(const nsIntMargin& aValue);
+ void SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized);
+ void SetTo(const nsSVGIntegerPair& aValue, const nsAString* aSerialized);
+ void SetTo(const nsSVGLength2& aValue, const nsAString* aSerialized);
+ void SetTo(const mozilla::SVGLengthList& aValue,
+ const nsAString* aSerialized);
+ void SetTo(const mozilla::SVGNumberList& aValue,
+ const nsAString* aSerialized);
+ void SetTo(const nsSVGNumberPair& aValue, const nsAString* aSerialized);
+ void SetTo(const mozilla::SVGPathData& aValue, const nsAString* aSerialized);
+ void SetTo(const mozilla::SVGPointList& aValue, const nsAString* aSerialized);
+ void SetTo(const mozilla::SVGAnimatedPreserveAspectRatio& aValue,
+ const nsAString* aSerialized);
+ void SetTo(const mozilla::SVGStringList& aValue,
+ const nsAString* aSerialized);
+ void SetTo(const mozilla::SVGTransformList& aValue,
+ const nsAString* aSerialized);
+ void SetTo(const nsSVGViewBox& aValue, const nsAString* aSerialized);
/**
* Sets this object with the string or atom representation of aValue.
@@ -368,10 +404,23 @@ private:
AtomArray* mAtomArray;
double mDoubleValue;
nsIntMargin* mIntMargin;
+ const nsSVGAngle* mSVGAngle;
+ const nsSVGIntegerPair* mSVGIntegerPair;
+ const nsSVGLength2* mSVGLength;
+ const mozilla::SVGLengthList* mSVGLengthList;
+ const mozilla::SVGNumberList* mSVGNumberList;
+ const nsSVGNumberPair* mSVGNumberPair;
+ const mozilla::SVGPathData* mSVGPathData;
+ const mozilla::SVGPointList* mSVGPointList;
+ const mozilla::SVGAnimatedPreserveAspectRatio* mSVGPreserveAspectRatio;
+ const mozilla::SVGStringList* mSVGStringList;
+ const mozilla::SVGTransformList* mSVGTransformList;
+ const nsSVGViewBox* mSVGViewBox;
};
};
inline ValueBaseType BaseType() const;
+ inline bool IsSVGType(ValueType aType) const;
/**
* Get the index of an EnumTable in the sEnumTableArray.
@@ -388,6 +437,8 @@ private:
void SetColorValue(nscolor aColor, const nsAString& aString);
void SetMiscAtomOrString(const nsAString* aValue);
void ResetMiscAtomOrString();
+ void SetSVGType(ValueType aType, const void* aValue,
+ const nsAString* aSerialized);
inline void ResetIfSet();
inline void* GetPtr() const;
@@ -502,6 +553,12 @@ nsAttrValue::BaseType() const
return static_cast(mBits & NS_ATTRVALUE_BASETYPE_MASK);
}
+inline bool
+nsAttrValue::IsSVGType(ValueType aType) const
+{
+ return aType >= eSVGTypesBegin && aType <= eSVGTypesEnd;
+}
+
inline void
nsAttrValue::SetPtrValueAndType(void* aValue, ValueBaseType aType)
{
diff --git a/content/base/src/nsContentAreaDragDrop.cpp b/content/base/src/nsContentAreaDragDrop.cpp
index 7190bb4fd37..ec8b803137b 100644
--- a/content/base/src/nsContentAreaDragDrop.cpp
+++ b/content/base/src/nsContentAreaDragDrop.cpp
@@ -48,6 +48,7 @@
#include "nsCopySupport.h"
#include "nsIDOMUIEvent.h"
#include "nsISelection.h"
+#include "nsISelectionController.h"
#include "nsIDOMNode.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMEvent.h"
@@ -70,6 +71,7 @@
#include "nsIDocShell.h"
#include "nsIContent.h"
#include "nsIImageLoadingContent.h"
+#include "nsITextControlElement.h"
#include "nsUnicharUtils.h"
#include "nsIURL.h"
#include "nsIDocument.h"
@@ -84,37 +86,6 @@
#include "imgIRequest.h"
#include "nsDOMDataTransfer.h"
-// private clipboard data flavors for html copy, used by editor when pasting
-#define kHTMLContext "text/_moz_htmlcontext"
-#define kHTMLInfo "text/_moz_htmlinfo"
-
-// if aNode is null, use the selection from the window
-static nsresult
-GetTransferableForNodeOrSelection(nsIDOMWindow* aWindow,
- nsIContent* aNode,
- nsITransferable** aTransferable)
-{
- NS_ENSURE_ARG_POINTER(aWindow);
-
- nsCOMPtr domDoc;
- aWindow->GetDocument(getter_AddRefs(domDoc));
- NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
- nsCOMPtr doc = do_QueryInterface(domDoc);
-
- nsresult rv;
- if (aNode) {
- rv = nsCopySupport::GetTransferableForNode(aNode, doc, aTransferable);
- } else {
- nsCOMPtr selection;
- aWindow->GetSelection(getter_AddRefs(selection));
- rv = nsCopySupport::GetTransferableForSelection(selection, doc,
- aTransferable);
- }
-
- NS_ENSURE_SUCCESS(rv, rv);
- return rv;
-}
-
class NS_STACK_CLASS DragDataProducer
{
public:
@@ -124,7 +95,7 @@ public:
bool aIsAltKeyPressed);
nsresult Produce(nsDOMDataTransfer* aDataTransfer,
bool* aCanDrag,
- bool* aDragSelection,
+ nsISelection** aSelection,
nsIContent** aDragNode);
private:
@@ -172,7 +143,7 @@ nsContentAreaDragDrop::GetDragData(nsIDOMWindow* aWindow,
bool aIsAltKeyPressed,
nsDOMDataTransfer* aDataTransfer,
bool* aCanDrag,
- bool* aDragSelection,
+ nsISelection** aSelection,
nsIContent** aDragNode)
{
NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG);
@@ -181,7 +152,7 @@ nsContentAreaDragDrop::GetDragData(nsIDOMWindow* aWindow,
DragDataProducer
provider(aWindow, aTarget, aSelectionTargetNode, aIsAltKeyPressed);
- return provider.Produce(aDataTransfer, aCanDrag, aDragSelection, aDragNode);
+ return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode);
}
@@ -412,10 +383,10 @@ DragDataProducer::GetNodeString(nsIContent* inNode,
nsresult
DragDataProducer::Produce(nsDOMDataTransfer* aDataTransfer,
bool* aCanDrag,
- bool* aDragSelection,
+ nsISelection** aSelection,
nsIContent** aDragNode)
{
- NS_PRECONDITION(aCanDrag && aDragSelection && aDataTransfer && aDragNode,
+ NS_PRECONDITION(aCanDrag && aSelection && aDataTransfer && aDragNode,
"null pointer passed to Produce");
NS_ASSERTION(mWindow, "window not set");
NS_ASSERTION(mSelectionTargetNode, "selection target node should have been set");
@@ -424,33 +395,72 @@ DragDataProducer::Produce(nsDOMDataTransfer* aDataTransfer,
nsresult rv;
nsIContent* dragNode = nsnull;
+ *aSelection = nsnull;
- // find the selection to see what we could be dragging and if
- // what we're dragging is in what is selected.
+ // Find the selection to see what we could be dragging and if what we're
+ // dragging is in what is selected. If this is an editable textbox, use
+ // the textbox's selection, otherwise use the window's selection.
nsCOMPtr selection;
- mWindow->GetSelection(getter_AddRefs(selection));
- if (!selection) {
- return NS_OK;
- }
-
- // check if the node is inside a form control. If so, dragging will be
- // handled in editor code (nsPlaintextDataTransfer::DoDrag). Don't set
- // aCanDrag to false however, as we still want to allow the drag.
- nsCOMPtr findFormNode = mSelectionTargetNode;
- nsIContent* findFormParent = findFormNode->GetParent();
- while (findFormParent) {
- nsCOMPtr form(do_QueryInterface(findFormParent));
- if (form && !form->AllowDraggableChildren()) {
- return NS_OK;
+ nsIContent* editingElement = mSelectionTargetNode->IsEditable() ?
+ mSelectionTargetNode->GetEditingHost() : nsnull;
+ nsCOMPtr textControl(do_QueryInterface(editingElement));
+ if (textControl) {
+ nsISelectionController* selcon = textControl->GetSelectionController();
+ if (selcon) {
+ selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
+ if (!selection)
+ return NS_OK;
+ }
+ }
+ else {
+ mWindow->GetSelection(getter_AddRefs(selection));
+ if (!selection)
+ return NS_OK;
+
+ // Check if the node is inside a form control. Don't set aCanDrag to false
+ //however, as we still want to allow the drag.
+ nsCOMPtr findFormNode = mSelectionTargetNode;
+ nsIContent* findFormParent = findFormNode->GetParent();
+ while (findFormParent) {
+ nsCOMPtr form(do_QueryInterface(findFormParent));
+ if (form && !form->AllowDraggableChildren()) {
+ return NS_OK;
+ }
+ findFormParent = findFormParent->GetParent();
}
- findFormParent = findFormParent->GetParent();
}
// if set, serialize the content under this node
nsCOMPtr nodeToSerialize;
- *aDragSelection = false;
- {
+ bool isChromeShell = false;
+ nsCOMPtr webnav = do_GetInterface(mWindow);
+ nsCOMPtr dsti = do_QueryInterface(webnav);
+ if (dsti) {
+ PRInt32 type = -1;
+ if (NS_SUCCEEDED(dsti->GetItemType(&type)) &&
+ type == nsIDocShellTreeItem::typeChrome) {
+ isChromeShell = true;
+ }
+ }
+
+ // In chrome shells, only allow dragging inside editable areas.
+ if (isChromeShell && !editingElement)
+ return NS_OK;
+
+ if (isChromeShell && textControl) {
+ // Only use the selection if it isn't collapsed.
+ bool isCollapsed = false;
+ selection->GetIsCollapsed(&isCollapsed);
+ if (!isCollapsed)
+ selection.swap(*aSelection);
+ }
+ else {
+ // In content shells, a number of checks are made below to determine
+ // whether an image or a link is being dragged. If so, add additional
+ // data to the data transfer. This is also done for chrome shells, but
+ // only when in a non-textbox editor.
+
bool haveSelectedContent = false;
// possible parent link node
@@ -490,7 +500,7 @@ DragDataProducer::Produce(nsDOMDataTransfer* aDataTransfer,
return NS_OK;
}
- *aDragSelection = true;
+ selection.swap(*aSelection);
} else if (selectedImageOrLinkNode) {
// an image is selected
image = do_QueryInterface(selectedImageOrLinkNode);
@@ -660,20 +670,28 @@ DragDataProducer::Produce(nsDOMDataTransfer* aDataTransfer,
}
}
- if (nodeToSerialize || *aDragSelection) {
- // if we have selected text, use it in preference to the node
- if (*aDragSelection) {
- nodeToSerialize = nsnull;
- }
-
+ if (nodeToSerialize || *aSelection) {
mHtmlString.Truncate();
mContextString.Truncate();
mInfoString.Truncate();
mTitleString.Truncate();
+
+ nsCOMPtr domDoc;
+ mWindow->GetDocument(getter_AddRefs(domDoc));
+ nsCOMPtr doc = do_QueryInterface(domDoc);
+ NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+
+ // if we have selected text, use it in preference to the node
nsCOMPtr transferable;
- rv = ::GetTransferableForNodeOrSelection(mWindow, nodeToSerialize,
- getter_AddRefs(transferable));
- NS_ENSURE_SUCCESS(rv, rv);
+ if (*aSelection) {
+ rv = nsCopySupport::GetTransferableForSelection(*aSelection, doc,
+ getter_AddRefs(transferable));
+ }
+ else {
+ rv = nsCopySupport::GetTransferableForNode(nodeToSerialize, doc,
+ getter_AddRefs(transferable));
+ }
+
nsCOMPtr data;
PRUint32 dataSize;
rv = transferable->GetTransferData(kHTMLMime, getter_AddRefs(data), &dataSize);
@@ -747,15 +765,17 @@ DragDataProducer::AddStringsToDataTransfer(nsIContent* aDragNode,
AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal);
}
- // add a special flavor, even if we don't have html context data
- AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString, principal);
+ // add a special flavor for the html context data
+ if (!mContextString.IsEmpty())
+ AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString, principal);
// add a special flavor if we have html info data
if (!mInfoString.IsEmpty())
AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), mInfoString, principal);
// add the full html
- AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString, principal);
+ if (!mHtmlString.IsEmpty())
+ AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString, principal);
// add the plain text. we use the url for text/plain data if an anchor is
// being dragged, rather than the title text of the link or the alt text for
diff --git a/content/base/src/nsContentAreaDragDrop.h b/content/base/src/nsContentAreaDragDrop.h
index 64a1ad4ba80..06482876dcf 100644
--- a/content/base/src/nsContentAreaDragDrop.h
+++ b/content/base/src/nsContentAreaDragDrop.h
@@ -78,8 +78,8 @@ public:
* aDataTransfer - the dataTransfer for the drag event.
* aCanDrag - [out] set to true if the drag may proceed, false to stop the
* drag entirely
- * aDragSelection - [out] set to true to indicate that a selection is being
- * dragged, rather than a specific node
+ * aSelection - [out] set to the selection being dragged, or null if no
+ * selection is being dragged.
* aDragNode - [out] the link, image or area being dragged, or null if the
* drag occurred on another element.
*/
@@ -89,7 +89,7 @@ public:
bool aIsAltKeyPressed,
nsDOMDataTransfer* aDataTransfer,
bool* aCanDrag,
- bool* aDragSelection,
+ nsISelection** aSelection,
nsIContent** aDragNode);
};
diff --git a/content/base/src/nsCopySupport.cpp b/content/base/src/nsCopySupport.cpp
index 6054a56603e..f6c32d25dce 100644
--- a/content/base/src/nsCopySupport.cpp
+++ b/content/base/src/nsCopySupport.cpp
@@ -91,10 +91,6 @@ static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID);
static NS_DEFINE_CID(kHTMLConverterCID, NS_HTMLFORMATCONVERTER_CID);
-// private clipboard data flavors for html copy, used by editor when pasting
-#define kHTMLContext "text/_moz_htmlcontext"
-#define kHTMLInfo "text/_moz_htmlinfo"
-
// copy string data onto the transferable
static nsresult AppendString(nsITransferable *aTransferable,
const nsAString& aString,
diff --git a/content/base/src/nsDOMBlobBuilder.cpp b/content/base/src/nsDOMBlobBuilder.cpp
index b1dab01e18f..402edddd103 100644
--- a/content/base/src/nsDOMBlobBuilder.cpp
+++ b/content/base/src/nsDOMBlobBuilder.cpp
@@ -122,9 +122,9 @@ nsDOMMultipartFile::CreateSlice(PRUint64 aStart, PRUint64 aLength,
PRUint64 upperBound = NS_MIN(l - skipStart, length);
nsCOMPtr firstBlob;
- rv = blob->MozSlice(skipStart, skipStart + upperBound,
- aContentType, 3,
- getter_AddRefs(firstBlob));
+ rv = blob->Slice(skipStart, skipStart + upperBound,
+ aContentType, 3,
+ getter_AddRefs(firstBlob));
NS_ENSURE_SUCCESS(rv, nsnull);
// Avoid wrapping a single blob inside an nsDOMMultipartFile
@@ -150,8 +150,8 @@ nsDOMMultipartFile::CreateSlice(PRUint64 aStart, PRUint64 aLength,
if (length < l) {
nsCOMPtr lastBlob;
- rv = blob->MozSlice(0, length, aContentType, 3,
- getter_AddRefs(lastBlob));
+ rv = blob->Slice(0, length, aContentType, 3,
+ getter_AddRefs(lastBlob));
NS_ENSURE_SUCCESS(rv, nsnull);
blobs.AppendElement(lastBlob);
diff --git a/content/base/src/nsDOMFile.cpp b/content/base/src/nsDOMFile.cpp
index 9c1d56d08d6..1df7d3863e0 100644
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -238,9 +238,9 @@ ParseSize(PRInt64 aSize, PRInt64& aStart, PRInt64& aEnd)
}
NS_IMETHODIMP
-nsDOMFileBase::MozSlice(PRInt64 aStart, PRInt64 aEnd,
- const nsAString& aContentType, PRUint8 optional_argc,
- nsIDOMBlob **aBlob)
+nsDOMFileBase::Slice(PRInt64 aStart, PRInt64 aEnd,
+ const nsAString& aContentType, PRUint8 optional_argc,
+ nsIDOMBlob **aBlob)
{
*aBlob = nsnull;
diff --git a/content/base/src/nsEventSource.cpp b/content/base/src/nsEventSource.cpp
index cb91fea9bdb..b0860e28151 100644
--- a/content/base/src/nsEventSource.cpp
+++ b/content/base/src/nsEventSource.cpp
@@ -89,6 +89,7 @@ nsEventSource::nsEventSource() :
mErrorLoadOnRedirect(false),
mGoingToDispatchAllMessages(false),
mWithCredentials(false),
+ mWaitingForOnStopRequest(false),
mLastConvertionResult(NS_OK),
mReadyState(nsIEventSource::CONNECTING),
mScriptLine(0),
@@ -108,13 +109,19 @@ nsEventSource::~nsEventSource()
NS_IMPL_CYCLE_COLLECTION_CLASS(nsEventSource)
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsEventSource)
- if (tmp->IsBlack()) {
+ bool isBlack = tmp->IsBlack();
+ if (isBlack || tmp->mWaitingForOnStopRequest) {
if (tmp->mListenerManager) {
tmp->mListenerManager->UnmarkGrayJSListeners();
NS_UNMARK_LISTENER_WRAPPER(Open)
NS_UNMARK_LISTENER_WRAPPER(Message)
NS_UNMARK_LISTENER_WRAPPER(Error)
}
+ if (!isBlack) {
+ xpc_UnmarkGrayObject(tmp->PreservingWrapper() ?
+ tmp->GetWrapperPreserveColor() :
+ tmp->GetExpandoObjectPreserveColor());
+ }
return true;
}
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
@@ -599,6 +606,8 @@ nsEventSource::OnStopRequest(nsIRequest *aRequest,
nsISupports *aContext,
nsresult aStatusCode)
{
+ mWaitingForOnStopRequest = false;
+
if (mReadyState == nsIEventSource::CLOSED) {
return NS_ERROR_ABORT;
}
@@ -949,7 +958,11 @@ nsEventSource::InitChannelAndRequestEventSource()
NS_ENSURE_SUCCESS(rv, rv);
// Start reading from the channel
- return mHttpChannel->AsyncOpen(listener, nsnull);
+ rv = mHttpChannel->AsyncOpen(listener, nsnull);
+ if (NS_SUCCEEDED(rv)) {
+ mWaitingForOnStopRequest = true;
+ }
+ return rv;
}
void
diff --git a/content/base/src/nsEventSource.h b/content/base/src/nsEventSource.h
index 4ee1c32b004..0b04bce34e5 100644
--- a/content/base/src/nsEventSource.h
+++ b/content/base/src/nsEventSource.h
@@ -216,6 +216,7 @@ protected:
bool mErrorLoadOnRedirect;
bool mGoingToDispatchAllMessages;
bool mWithCredentials;
+ bool mWaitingForOnStopRequest;
// used while reading the input streams
nsCOMPtr mUnicodeDecoder;
diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp
index 7c7f74107b2..e26bf228eae 100644
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -1700,9 +1700,29 @@ nsINode::SetExplicitBaseURI(nsIURI* aURI)
//----------------------------------------------------------------------
+static JSObject*
+GetJSObjectChild(nsWrapperCache* aCache)
+{
+ if (aCache->PreservingWrapper()) {
+ return aCache->GetWrapperPreserveColor();
+ }
+ return aCache->GetExpandoObjectPreserveColor();
+}
+
+static bool
+NeedsScriptTraverse(nsWrapperCache* aCache)
+{
+ JSObject* o = GetJSObjectChild(aCache);
+ return o && xpc_IsGrayGCThing(o);
+}
+
+//----------------------------------------------------------------------
+
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsChildContentList)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsChildContentList)
+// If nsChildContentList is changed so that any additional fields are
+// traversed by the cycle collector, then CAN_SKIP must be updated.
NS_IMPL_CYCLE_COLLECTION_CLASS(nsChildContentList)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsChildContentList)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -1714,6 +1734,20 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsChildContentList)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
+// nsChildContentList only ever has a single child, its wrapper, so if
+// the wrapper is black, the list can't be part of a garbage cycle.
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsChildContentList)
+ return !NeedsScriptTraverse(tmp);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsChildContentList)
+ return !NeedsScriptTraverse(tmp);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+// CanSkipThis returns false to avoid problems with incomplete unlinking.
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsChildContentList)
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
NS_INTERFACE_TABLE_HEAD(nsChildContentList)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_NODELIST_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsChildContentList)
@@ -4405,22 +4439,6 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGenericElement)
nsINode::Trace(tmp, aCallback, aClosure);
NS_IMPL_CYCLE_COLLECTION_TRACE_END
-static JSObject*
-GetJSObjectChild(nsINode* aNode)
-{
- if (aNode->PreservingWrapper()) {
- return aNode->GetWrapperPreserveColor();
- }
- return aNode->GetExpandoObjectPreserveColor();
-}
-
-static bool
-NeedsScriptTraverse(nsINode* aNode)
-{
- JSObject* o = GetJSObjectChild(aNode);
- return o && xpc_IsGrayGCThing(o);
-}
-
void
nsGenericElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
void* aData)
diff --git a/content/base/src/nsGenericElement.h b/content/base/src/nsGenericElement.h
index bfe8869d236..1417a46ce64 100644
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -102,7 +102,7 @@ public:
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsChildContentList)
+ NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsChildContentList)
// nsWrapperCache
virtual JSObject* WrapObject(JSContext *cx, XPCWrappedNativeScope *scope,
diff --git a/content/base/src/nsWebSocket.cpp b/content/base/src/nsWebSocket.cpp
index 1a6f1457120..b5b53594db3 100644
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -439,7 +439,8 @@ nsWebSocket::~nsWebSocket()
NS_IMPL_CYCLE_COLLECTION_CLASS(nsWebSocket)
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsWebSocket)
- if (tmp->IsBlack()) {
+ bool isBlack = tmp->IsBlack();
+ if (isBlack|| tmp->mKeepingAlive) {
if (tmp->mListenerManager) {
tmp->mListenerManager->UnmarkGrayJSListeners();
NS_UNMARK_LISTENER_WRAPPER(Open)
@@ -447,6 +448,11 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsWebSocket)
NS_UNMARK_LISTENER_WRAPPER(Message)
NS_UNMARK_LISTENER_WRAPPER(Close)
}
+ if (!isBlack) {
+ xpc_UnmarkGrayObject(tmp->PreservingWrapper() ?
+ tmp->GetWrapperPreserveColor() :
+ tmp->GetExpandoObjectPreserveColor());
+ }
return true;
}
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp
index 170b68fa459..bb86e90fece 100644
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -445,8 +445,8 @@ nsXMLHttpRequest::nsXMLHttpRequest()
mProgressSinceLastProgressEvent(false),
mUploadProgress(0), mUploadProgressMax(0),
mRequestSentTime(0), mTimeoutMilliseconds(0),
- mErrorLoad(false), mProgressTimerIsActive(false),
- mProgressEventWasDelayed(false),
+ mErrorLoad(false), mWaitingForOnStopRequest(false),
+ mProgressTimerIsActive(false), mProgressEventWasDelayed(false),
mIsHtml(false),
mWarnAboutMultipartHtml(false),
mWarnAboutSyncHtml(false),
@@ -602,7 +602,8 @@ nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver)
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest)
- if (tmp->IsBlack()) {
+ bool isBlack = tmp->IsBlack();
+ if (isBlack || tmp->mWaitingForOnStopRequest) {
if (tmp->mListenerManager) {
tmp->mListenerManager->UnmarkGrayJSListeners();
NS_UNMARK_LISTENER_WRAPPER(Load)
@@ -614,6 +615,11 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest)
NS_UNMARK_LISTENER_WRAPPER(UploadProgress)
NS_UNMARK_LISTENER_WRAPPER(Readystatechange)
}
+ if (!isBlack) {
+ xpc_UnmarkGrayObject(tmp->PreservingWrapper() ?
+ tmp->GetWrapperPreserveColor() :
+ tmp->GetExpandoObjectPreserveColor());
+ }
return true;
}
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
@@ -2140,6 +2146,8 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
return NS_OK;
}
+ mWaitingForOnStopRequest = false;
+
nsresult rv = NS_OK;
// If we're loading a multipart stream of XML documents, we'll get
@@ -2774,6 +2782,9 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
return rv;
}
+ // Either AsyncOpen was called, or CORS will open the channel later.
+ mWaitingForOnStopRequest = true;
+
// If we're synchronous, spin an event loop here and wait
if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
mState |= XML_HTTP_REQUEST_SYNCLOOPING;
diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h
index 3d5a1ab8424..f25c953d6a6 100644
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -365,7 +365,7 @@ protected:
void HandleTimeoutCallback();
bool mErrorLoad;
-
+ bool mWaitingForOnStopRequest;
bool mProgressTimerIsActive;
bool mProgressEventWasDelayed;
bool mIsHtml;
diff --git a/content/base/test/fileutils.js b/content/base/test/fileutils.js
index fbe6cbaa1c3..67ba18989b4 100644
--- a/content/base/test/fileutils.js
+++ b/content/base/test/fileutils.js
@@ -201,24 +201,24 @@ function testSlice(file, size, type, contents, fileType) {
ok(file instanceof File, fileType + " file is a File");
ok(file instanceof Blob, fileType + " file is also a Blob");
- var slice = file.mozSlice(0, size);
+ var slice = file.slice(0, size);
ok(slice instanceof Blob, fileType + " fullsize slice is a Blob");
ok(!(slice instanceof File), fileType + " fullsize slice is not a File");
- slice = file.mozSlice(0, 1234);
+ slice = file.slice(0, 1234);
ok(slice instanceof Blob, fileType + " sized slice is a Blob");
ok(!(slice instanceof File), fileType + " sized slice is not a File");
- slice = file.mozSlice(0, size, "foo/bar");
+ slice = file.slice(0, size, "foo/bar");
is(slice.type, "foo/bar", fileType + " fullsize slice foo/bar type");
- slice = file.mozSlice(0, 5432, "foo/bar");
+ slice = file.slice(0, 5432, "foo/bar");
is(slice.type, "foo/bar", fileType + " sized slice foo/bar type");
- is(slice.mozSlice(0, 10).type, "", fileType + " slice-slice type");
- is(slice.mozSlice(0, 10).size, 10, fileType + " slice-slice size");
- is(slice.mozSlice(0, 10, "hello/world").type, "hello/world", fileType + " slice-slice hello/world type");
- is(slice.mozSlice(0, 10, "hello/world").size, 10, fileType + " slice-slice hello/world size");
+ is(slice.slice(0, 10).type, "", fileType + " slice-slice type");
+ is(slice.slice(0, 10).size, 10, fileType + " slice-slice size");
+ is(slice.slice(0, 10, "hello/world").type, "hello/world", fileType + " slice-slice hello/world type");
+ is(slice.slice(0, 10, "hello/world").size, 10, fileType + " slice-slice hello/world size");
// Start, end, expected size
var indexes = [[0, size, size],
@@ -247,17 +247,17 @@ function testSlice(file, size, type, contents, fileType) {
var sliceContents;
var testName;
if (indexes[i][0] == undefined) {
- slice = file.mozSlice();
+ slice = file.slice();
sliceContents = contents.slice();
testName = fileType + " slice()";
}
else if (indexes[i][1] == undefined) {
- slice = file.mozSlice(indexes[i][0]);
+ slice = file.slice(indexes[i][0]);
sliceContents = contents.slice(indexes[i][0]);
testName = fileType + " slice(" + indexes[i][0] + ")";
}
else {
- slice = file.mozSlice(indexes[i][0], indexes[i][1]);
+ slice = file.slice(indexes[i][0], indexes[i][1]);
sliceContents = contents.slice(indexes[i][0], indexes[i][1]);
testName = fileType + " slice(" + indexes[i][0] + ", " + indexes[i][1] + ")";
}
@@ -268,11 +268,11 @@ function testSlice(file, size, type, contents, fileType) {
}
// Slice of slice
- var slice = file.mozSlice(0, 40000);
- testFile(slice.mozSlice(5000, 42000), contents.slice(5000, 40000), "file slice slice");
+ var slice = file.slice(0, 40000);
+ testFile(slice.slice(5000, 42000), contents.slice(5000, 40000), "file slice slice");
// ...of slice of slice
- slice = slice.mozSlice(5000, 42000).mozSlice(400, 700);
+ slice = slice.slice(5000, 42000).slice(400, 700);
SpecialPowers.gc();
testFile(slice, contents.slice(5400, 5700), "file slice slice slice");
}
diff --git a/content/base/test/test_blobbuilder.html b/content/base/test/test_blobbuilder.html
index 7b767c78245..6addb2f75f5 100644
--- a/content/base/test/test_blobbuilder.html
+++ b/content/base/test/test_blobbuilder.html
@@ -151,7 +151,7 @@ function doTest(data) {
ok(blob instanceof Blob, "Test " + testCounter + " blob is a Blob");
ok(!(blob instanceof File), "Test " + testCounter + " blob is not a File");
- let slice = blob.mozSlice(test.start, test.start + test.length);
+ let slice = blob.slice(test.start, test.start + test.length);
ok(slice, "Test " + testCounter + " got slice");
ok(slice instanceof Blob, "Test " + testCounter + " slice is a Blob");
ok(!(slice instanceof File), "Test " + testCounter + " slice is not a File");
diff --git a/content/base/test/test_bug338679.html b/content/base/test/test_bug338679.html
index cea297736a2..ac34bfc0156 100644
--- a/content/base/test/test_bug338679.html
+++ b/content/base/test/test_bug338679.html
@@ -49,7 +49,7 @@ function attr_modified(ev) {
e_prev = e_new;
if (!recursive) {
recursive = true;
- e_new = "width: 0pt;";
+ e_new = "width: 0px;";
testDiv.style.width = "0";
} else {
recursive = false;
diff --git a/content/base/test/test_fileapi_slice.html b/content/base/test/test_fileapi_slice.html
index 2b5594ce93e..9bfc6e0315c 100644
--- a/content/base/test/test_fileapi_slice.html
+++ b/content/base/test/test_fileapi_slice.html
@@ -104,7 +104,7 @@ function imageLoadHandler(event) {
var imgfile = createFileWithData(testBinaryData + fileData + testBinaryData);
is(imgfile.size, size + testBinaryData.length * 2, "correct file size (middle)");
var img = new Image;
-img.src = URL.createObjectURL(imgfile.mozSlice(testBinaryData.length, testBinaryData.length + size));
+img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, testBinaryData.length + size));
img.onload = imageLoadHandler;
expectedTestCount++;
@@ -112,7 +112,7 @@ expectedTestCount++;
var imgfile = createFileWithData(fileData + testBinaryData);
is(imgfile.size, size + testBinaryData.length, "correct file size (start)");
var img = new Image;
-img.src = URL.createObjectURL(imgfile.mozSlice(0, size));
+img.src = URL.createObjectURL(imgfile.slice(0, size));
img.onload = imageLoadHandler;
expectedTestCount++;
@@ -120,7 +120,7 @@ expectedTestCount++;
var imgfile = createFileWithData(testBinaryData + fileData);
is(imgfile.size, size + testBinaryData.length, "correct file size (end)");
var img = new Image;
-img.src = URL.createObjectURL(imgfile.mozSlice(testBinaryData.length, testBinaryData.length + size));
+img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, testBinaryData.length + size));
img.onload = imageLoadHandler;
expectedTestCount++;
@@ -128,7 +128,7 @@ expectedTestCount++;
var imgfile = createFileWithData(testBinaryData + fileData);
is(imgfile.size, size + testBinaryData.length, "correct file size (past end)");
var img = new Image;
-img.src = URL.createObjectURL(imgfile.mozSlice(testBinaryData.length, testBinaryData.length + size + 1000));
+img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, testBinaryData.length + size + 1000));
img.onload = imageLoadHandler;
expectedTestCount++;
diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h
index 5f1d14181b1..1b99c250bf5 100644
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -1024,7 +1024,7 @@ struct WebGLVertexAttribData {
}
};
-class WebGLBuffer
+class WebGLBuffer MOZ_FINAL
: public nsIWebGLBuffer
, public WebGLRefCountedObject
, public WebGLContextBoundObject
@@ -1158,7 +1158,7 @@ protected:
void* mData; // in the case of an Element Array Buffer, we keep a copy.
};
-class WebGLTexture
+class WebGLTexture MOZ_FINAL
: public nsIWebGLTexture
, public WebGLRefCountedObject
, public WebGLContextBoundObject
@@ -1608,7 +1608,7 @@ public:
}
};
-class WebGLShader
+class WebGLShader MOZ_FINAL
: public nsIWebGLShader
, public WebGLRefCountedObject
, public WebGLContextBoundObject
@@ -1673,7 +1673,7 @@ protected:
WebGLMonotonicHandle mMonotonicHandle;
};
-class WebGLProgram
+class WebGLProgram MOZ_FINAL
: public nsIWebGLProgram
, public WebGLRefCountedObject
, public WebGLContextBoundObject
@@ -1795,7 +1795,7 @@ protected:
WebGLMonotonicHandle mMonotonicHandle;
};
-class WebGLRenderbuffer
+class WebGLRenderbuffer MOZ_FINAL
: public nsIWebGLRenderbuffer
, public WebGLRefCountedObject
, public WebGLRectangleObject
@@ -2001,7 +2001,7 @@ public:
}
};
-class WebGLFramebuffer
+class WebGLFramebuffer MOZ_FINAL
: public nsIWebGLFramebuffer
, public WebGLRefCountedObject
, public WebGLContextBoundObject
@@ -2296,7 +2296,7 @@ public:
WebGLMonotonicHandle mMonotonicHandle;
};
-class WebGLUniformLocation
+class WebGLUniformLocation MOZ_FINAL
: public nsIWebGLUniformLocation
, public WebGLContextBoundObject
, public WebGLRefCountedObject
@@ -2337,7 +2337,7 @@ protected:
friend class WebGLProgram;
};
-class WebGLActiveInfo
+class WebGLActiveInfo MOZ_FINAL
: public nsIWebGLActiveInfo
{
public:
@@ -2356,7 +2356,7 @@ protected:
nsString mName;
};
-class WebGLShaderPrecisionFormat
+class WebGLShaderPrecisionFormat MOZ_FINAL
: public nsIWebGLShaderPrecisionFormat
{
public:
@@ -2388,6 +2388,7 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIWEBGLEXTENSION
+ virtual ~WebGLExtension() {}
};
inline const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const {
diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp
index c0a8fed9c8e..fd5b6fce070 100644
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -4377,7 +4377,7 @@ WebGLContext::CompileShader(nsIWebGLShader *sobj)
// ESSL backend
compiler = ShConstructCompiler((ShShaderType) shader->ShaderType(),
SH_WEBGL_SPEC,
-#ifdef MOZ_WIDGET_ANDROID
+#ifdef ANDROID
SH_GLSL_OUTPUT,
#else
gl->IsGLES2() ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT,
diff --git a/content/canvas/src/nsCanvasRenderingContext2D.cpp b/content/canvas/src/nsCanvasRenderingContext2D.cpp
index b159d7efbf7..7b4fa8b7f3a 100644
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -180,7 +180,7 @@ CopyContext(gfxContext* dest, gfxContext* src)
**/
#define NS_CANVASGRADIENT_PRIVATE_IID \
{ 0x491d39d8, 0x4058, 0x42bd, { 0xac, 0x76, 0x70, 0xd5, 0x62, 0x7f, 0x02, 0x10 } }
-class nsCanvasGradient : public nsIDOMCanvasGradient
+class nsCanvasGradient MOZ_FINAL : public nsIDOMCanvasGradient
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASGRADIENT_PRIVATE_IID)
@@ -238,7 +238,7 @@ NS_INTERFACE_MAP_END
**/
#define NS_CANVASPATTERN_PRIVATE_IID \
{ 0xb85c6c8a, 0x0624, 0x4530, { 0xb8, 0xee, 0xff, 0xdf, 0x42, 0xe8, 0x21, 0x6d } }
-class nsCanvasPattern : public nsIDOMCanvasPattern
+class nsCanvasPattern MOZ_FINAL : public nsIDOMCanvasPattern
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASPATTERN_PRIVATE_IID)
diff --git a/content/canvas/test/webgl/failing_tests_mac.txt b/content/canvas/test/webgl/failing_tests_mac.txt
index 5bc2bc7c82d..eaabd7f6f1e 100644
--- a/content/canvas/test/webgl/failing_tests_mac.txt
+++ b/content/canvas/test/webgl/failing_tests_mac.txt
@@ -7,3 +7,5 @@ conformance/more/conformance/quickCheckAPI-S_V.html
conformance/glsl/misc/attrib-location-length-limits.html
conformance/glsl/misc/uniform-location-length-limits.html
conformance/programs/program-test.html
+conformance/textures/texture-mips.html
+conformance/textures/texture-npot.html
diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp
index 1e36b45f0b6..a80cd3159f6 100644
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -180,8 +180,7 @@ nsDOMEvent::InitPresContextData(nsPresContext* aPresContext)
// Get the explicit original target (if it's anonymous make it null)
{
nsCOMPtr content = GetTargetFromFrame();
- mTmpRealOriginalTarget = do_QueryInterface(content);
- mExplicitOriginalTarget = mTmpRealOriginalTarget;
+ mExplicitOriginalTarget = do_QueryInterface(content);
if (content && content->IsInAnonymousSubtree()) {
mExplicitOriginalTarget = nsnull;
}
@@ -237,10 +236,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMEvent)
}
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPresContext);
- NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTmpRealOriginalTarget)
- // Always set mExplicitOriginalTarget to null, when
- // mTmpRealOriginalTarget doesn't point to any object!
- tmp->mExplicitOriginalTarget = nsnull;
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mExplicitOriginalTarget);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMEvent)
@@ -275,7 +271,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMEvent)
}
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mPresContext.get(), nsPresContext)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTmpRealOriginalTarget)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mExplicitOriginalTarget)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
// nsIDOMEventInterface
@@ -355,18 +351,6 @@ nsDOMEvent::GetExplicitOriginalTarget(nsIDOMEventTarget** aRealEventTarget)
return GetTarget(aRealEventTarget);
}
-NS_IMETHODIMP
-nsDOMEvent::GetTmpRealOriginalTarget(nsIDOMEventTarget** aRealEventTarget)
-{
- if (mTmpRealOriginalTarget) {
- *aRealEventTarget = mTmpRealOriginalTarget;
- NS_ADDREF(*aRealEventTarget);
- return NS_OK;
- }
-
- return GetOriginalTarget(aRealEventTarget);
-}
-
NS_IMETHODIMP
nsDOMEvent::GetOriginalTarget(nsIDOMEventTarget** aOriginalTarget)
{
diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h
index f3b53c04890..e2c2017cd58 100644
--- a/content/events/src/nsDOMEvent.h
+++ b/content/events/src/nsDOMEvent.h
@@ -267,8 +267,7 @@ protected:
nsEvent* mEvent;
nsRefPtr mPresContext;
- nsCOMPtr mTmpRealOriginalTarget;
- nsIDOMEventTarget* mExplicitOriginalTarget;
+ nsCOMPtr mExplicitOriginalTarget;
nsString mCachedType;
bool mEventIsInternal;
bool mPrivateDataDuplicated;
diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp
index 7978989e7d2..2143c8d266b 100644
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -2081,14 +2081,12 @@ nsEventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
if (!dataTransfer)
return;
- bool isInEditor = false;
- bool isSelection = false;
+ nsCOMPtr selection;
nsCOMPtr eventContent, targetContent;
mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(eventContent));
if (eventContent)
DetermineDragTarget(aPresContext, eventContent, dataTransfer,
- &isSelection, &isInEditor,
- getter_AddRefs(targetContent));
+ getter_AddRefs(selection), getter_AddRefs(targetContent));
// Stop tracking the drag gesture now. This should stop us from
// reentering GenerateDragGesture inside DOM event processing.
@@ -2129,9 +2127,8 @@ nsEventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
// elements in an editor, only fire the draggesture event so that the
// editor code can handle it but content doesn't see a dragstart.
nsEventStatus status = nsEventStatus_eIgnore;
- if (!isInEditor)
- nsEventDispatcher::Dispatch(targetContent, aPresContext, &startEvent, nsnull,
- &status);
+ nsEventDispatcher::Dispatch(targetContent, aPresContext, &startEvent, nsnull,
+ &status);
nsDragEvent* event = &startEvent;
if (status != nsEventStatus_eConsumeNoDefault) {
@@ -2148,7 +2145,7 @@ nsEventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
if (status != nsEventStatus_eConsumeNoDefault) {
bool dragStarted = DoDefaultDragStart(aPresContext, event, dataTransfer,
- targetContent, isSelection);
+ targetContent, selection);
if (dragStarted) {
sActiveESM = nsnull;
aEvent->flags |= NS_EVENT_FLAG_STOP_DISPATCH;
@@ -2173,37 +2170,27 @@ void
nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext,
nsIContent* aSelectionTarget,
nsDOMDataTransfer* aDataTransfer,
- bool* aIsSelection,
- bool* aIsInEditor,
+ nsISelection** aSelection,
nsIContent** aTargetNode)
{
*aTargetNode = nsnull;
- *aIsInEditor = false;
nsCOMPtr container = aPresContext->GetContainer();
nsCOMPtr window = do_GetInterface(container);
// GetDragData determines if a selection, link or image in the content
// should be dragged, and places the data associated with the drag in the
- // data transfer. Skip this check for chrome shells.
+ // data transfer.
+ // mGestureDownContent is the node where the mousedown event for the drag
+ // occurred, and aSelectionTarget is the node to use when a selection is used
bool canDrag;
nsCOMPtr dragDataNode;
- nsCOMPtr dsti = do_QueryInterface(container);
- if (dsti) {
- PRInt32 type = -1;
- if (NS_SUCCEEDED(dsti->GetItemType(&type)) &&
- type != nsIDocShellTreeItem::typeChrome) {
- // mGestureDownContent is the node where the mousedown event for the drag
- // occurred, and aSelectionTarget is the node to use when a selection is used
- nsresult rv =
- nsContentAreaDragDrop::GetDragData(window, mGestureDownContent,
- aSelectionTarget, mGestureDownAlt,
- aDataTransfer, &canDrag, aIsSelection,
- getter_AddRefs(dragDataNode));
- if (NS_FAILED(rv) || !canDrag)
- return;
- }
- }
+ nsresult rv = nsContentAreaDragDrop::GetDragData(window, mGestureDownContent,
+ aSelectionTarget, mGestureDownAlt,
+ aDataTransfer, &canDrag, aSelection,
+ getter_AddRefs(dragDataNode));
+ if (NS_FAILED(rv) || !canDrag)
+ return;
// if GetDragData returned a node, use that as the node being dragged.
// Otherwise, if a selection is being dragged, use the node within the
@@ -2211,7 +2198,7 @@ nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext,
nsIContent* dragContent = mGestureDownContent;
if (dragDataNode)
dragContent = dragDataNode;
- else if (*aIsSelection)
+ else if (*aSelection)
dragContent = aSelectionTarget;
nsIContent* originalDragContent = dragContent;
@@ -2220,7 +2207,7 @@ nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext,
// draggable property set. If one is found, use that as the target of the
// drag instead of the node that was clicked on. If a draggable node wasn't
// found, just use the clicked node.
- if (!*aIsSelection) {
+ if (!*aSelection) {
while (dragContent) {
nsCOMPtr htmlElement = do_QueryInterface(dragContent);
if (htmlElement) {
@@ -2245,17 +2232,6 @@ nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext,
// otherwise, it's not an HTML or XUL element, so just keep looking
}
dragContent = dragContent->GetParent();
-
- // if an editable parent is encountered, then we don't look at any
- // ancestors. This is used because the editor attaches a draggesture
- // listener to the editable element and we want to call it without
- // making the editable element draggable. This should be removed once
- // the editor is switched over to using the proper drag and drop api.
- nsCOMPtr editableElement = do_QueryInterface(dragContent);
- if (editableElement) {
- *aIsInEditor = true;
- break;
- }
}
}
@@ -2279,7 +2255,7 @@ nsEventStateManager::DoDefaultDragStart(nsPresContext* aPresContext,
nsDragEvent* aDragEvent,
nsDOMDataTransfer* aDataTransfer,
nsIContent* aDragTarget,
- bool aIsSelection)
+ nsISelection* aSelection)
{
nsCOMPtr dragService =
do_GetService("@mozilla.org/widget/dragservice;1");
@@ -2333,22 +2309,6 @@ nsEventStateManager::DoDefaultDragStart(nsPresContext* aPresContext,
PRInt32 imageX, imageY;
nsIDOMElement* dragImage = aDataTransfer->GetDragImage(&imageX, &imageY);
- // If a selection is being dragged, and no custom drag image was
- // set, get the selection so that the drag region can be created
- // from the selection area. If a custom image was set, it doesn't
- // matter what the selection is since the image will be used instead.
- nsISelection* selection = nsnull;
- if (aIsSelection && !dragImage) {
- nsIDocument* doc = aDragTarget->GetCurrentDoc();
- if (doc) {
- nsIPresShell* presShell = doc->GetShell();
- if (presShell) {
- selection = presShell->GetCurrentSelection(
- nsISelectionController::SELECTION_NORMAL);
- }
- }
- }
-
nsCOMPtr transArray;
aDataTransfer->GetTransferables(getter_AddRefs(transArray));
if (!transArray)
@@ -2363,8 +2323,13 @@ nsEventStateManager::DoDefaultDragStart(nsPresContext* aPresContext,
nsCOMPtr domDragEvent = do_QueryInterface(domEvent);
// if creating a drag event failed, starting a drag session will
// just fail.
- if (selection) {
- dragService->InvokeDragSessionWithSelection(selection, transArray,
+
+ // Use InvokeDragSessionWithSelection if a selection is being dragged,
+ // such that the image can be generated from the selected text. However,
+ // use InvokeDragSessionWithImage if a custom image was set or something
+ // other than a selection is being dragged.
+ if (!dragImage && aSelection) {
+ dragService->InvokeDragSessionWithSelection(aSelection, transArray,
action, domDragEvent,
aDataTransfer);
}
diff --git a/content/events/src/nsEventStateManager.h b/content/events/src/nsEventStateManager.h
index b6839292d8e..f831f587205 100644
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -417,15 +417,13 @@ protected:
*
* aSelectionTarget - target to check for selection
* aDataTransfer - data transfer object that will contain the data to drag
- * aIsSelection - [out] set to true if a selection is being dragged
- * aIsInEditor - [out] set to true if the content is in an editor field
+ * aSelection - [out] set to the selection to be dragged
* aTargetNode - [out] the draggable node, or null if there isn't one
*/
void DetermineDragTarget(nsPresContext* aPresContext,
nsIContent* aSelectionTarget,
nsDOMDataTransfer* aDataTransfer,
- bool* aIsSelection,
- bool* aIsInEditor,
+ nsISelection** aSelection,
nsIContent** aTargetNode);
/*
@@ -436,13 +434,13 @@ protected:
* aDragEvent - the dragstart/draggesture event
* aDataTransfer - the data transfer that holds the data to be dragged
* aDragTarget - the target of the drag
- * aIsSelection - true if a selection is being dragged
+ * aSelection - the selection to be dragged
*/
bool DoDefaultDragStart(nsPresContext* aPresContext,
nsDragEvent* aDragEvent,
nsDOMDataTransfer* aDataTransfer,
nsIContent* aDragTarget,
- bool aIsSelection);
+ nsISelection* aSelection);
bool IsTrackingDragGesture ( ) const { return mGestureDownContent != nsnull; }
/**
diff --git a/content/html/content/src/nsDOMValidityState.h b/content/html/content/src/nsDOMValidityState.h
index 8370b45b9a2..5980757f234 100644
--- a/content/html/content/src/nsDOMValidityState.h
+++ b/content/html/content/src/nsDOMValidityState.h
@@ -42,7 +42,7 @@
#include "nsIConstraintValidation.h"
-class nsDOMValidityState : public nsIDOMValidityState
+class nsDOMValidityState MOZ_FINAL : public nsIDOMValidityState
{
public:
NS_DECL_ISUPPORTS
diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp
index a17eb86e732..0c862a57b98 100644
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -185,7 +185,7 @@ static const nsAttrValue::EnumTable* kInputDefaultAutocomplete = &kInputAutocomp
{0xb5, 0x13, 0x7b, 0x36, 0x93, 0x43, 0xe3, 0xa0} \
}
-class nsHTMLInputElementState : public nsISupports
+class nsHTMLInputElementState MOZ_FINAL : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_INPUT_ELEMENT_STATE_IID)
diff --git a/content/html/content/src/nsHTMLInputElement.h b/content/html/content/src/nsHTMLInputElement.h
index c462c85b6ea..453b8f7a06f 100644
--- a/content/html/content/src/nsHTMLInputElement.h
+++ b/content/html/content/src/nsHTMLInputElement.h
@@ -57,7 +57,7 @@ class nsIRadioGroupContainer;
class nsIRadioGroupVisitor;
class nsIRadioVisitor;
-class UploadLastDir : public nsIObserver, public nsSupportsWeakReference {
+class UploadLastDir MOZ_FINAL : public nsIObserver, public nsSupportsWeakReference {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp
index c4cc3d966c2..fcb41e0946c 100644
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -243,10 +243,10 @@ public:
* to an nsIChannel, which holds a reference to this listener.
* We break the reference cycle in OnStartRequest by clearing mElement.
*/
-class nsHTMLMediaElement::MediaLoadListener : public nsIStreamListener,
- public nsIChannelEventSink,
- public nsIInterfaceRequestor,
- public nsIObserver
+class nsHTMLMediaElement::MediaLoadListener MOZ_FINAL : public nsIStreamListener,
+ public nsIChannelEventSink,
+ public nsIInterfaceRequestor,
+ public nsIObserver
{
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
diff --git a/content/html/content/src/nsMediaError.h b/content/html/content/src/nsMediaError.h
index 33d657f650c..c540afd9233 100644
--- a/content/html/content/src/nsMediaError.h
+++ b/content/html/content/src/nsMediaError.h
@@ -37,8 +37,9 @@
* ***** END LICENSE BLOCK ***** */
#include "nsIDOMMediaError.h"
#include "nsISupports.h"
+#include "mozilla/Attributes.h"
-class nsMediaError : public nsIDOMMediaError
+class nsMediaError MOZ_FINAL : public nsIDOMMediaError
{
public:
nsMediaError(PRUint16 aCode);
diff --git a/content/html/content/src/nsTimeRanges.h b/content/html/content/src/nsTimeRanges.h
index 172cadb5b85..8359b2378de 100644
--- a/content/html/content/src/nsTimeRanges.h
+++ b/content/html/content/src/nsTimeRanges.h
@@ -45,7 +45,7 @@
// Implements media TimeRanges:
// http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#timeranges
-class nsTimeRanges : public nsIDOMTimeRanges {
+class nsTimeRanges MOZ_FINAL : public nsIDOMTimeRanges {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMTIMERANGES
diff --git a/content/media/nsBuiltinDecoder.cpp b/content/media/nsBuiltinDecoder.cpp
index c4b0c294b2e..a0f40e5802f 100644
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -216,6 +216,7 @@ nsresult nsBuiltinDecoder::Load(MediaResource* aResource,
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mDecoderStateMachine->SetSeekable(mSeekable);
mDecoderStateMachine->SetDuration(mDuration);
+ mDecoderStateMachine->SetVolume(mInitialVolume);
if (mFrameBufferLength > 0) {
// The valid mFrameBufferLength value was specified earlier
diff --git a/content/svg/content/src/DOMSVGLength.cpp b/content/svg/content/src/DOMSVGLength.cpp
index 8e0de116e52..ada2143a204 100644
--- a/content/svg/content/src/DOMSVGLength.cpp
+++ b/content/svg/content/src/DOMSVGLength.cpp
@@ -156,8 +156,14 @@ DOMSVGLength::SetValue(float aUserUnitValue)
// unit as it is.
if (HasOwner()) {
- if (InternalItem().SetFromUserUnitValue(aUserUnitValue, Element(), Axis())) {
- Element()->DidChangeLengthList(mAttrEnum, true);
+ if (InternalItem().GetValueInUserUnits(Element(), Axis()) ==
+ aUserUnitValue) {
+ return NS_OK;
+ }
+ nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum);
+ if (InternalItem().SetFromUserUnitValue(aUserUnitValue, Element(), Axis()))
+ {
+ Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue);
if (mList->mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -195,8 +201,12 @@ DOMSVGLength::SetValueInSpecifiedUnits(float aValue)
}
if (HasOwner()) {
+ if (InternalItem().GetValueInCurrentUnits() == aValue) {
+ return NS_OK;
+ }
+ nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum);
InternalItem().SetValueInCurrentUnits(aValue);
- Element()->DidChangeLengthList(mAttrEnum, true);
+ Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue);
if (mList->mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -218,8 +228,12 @@ DOMSVGLength::SetValueAsString(const nsAString& aValue)
return NS_ERROR_DOM_SYNTAX_ERR;
}
if (HasOwner()) {
+ if (InternalItem() == value) {
+ return NS_OK;
+ }
+ nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum);
InternalItem() = value;
- Element()->DidChangeLengthList(mAttrEnum, true);
+ Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue);
if (mList->mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -259,8 +273,13 @@ DOMSVGLength::NewValueSpecifiedUnits(PRUint16 aUnit, float aValue)
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
if (HasOwner()) {
+ if (InternalItem().GetUnit() == aUnit &&
+ InternalItem().GetValueInCurrentUnits() == aValue) {
+ return NS_OK;
+ }
+ nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum);
InternalItem().SetValueAndUnit(aValue, PRUint8(aUnit));
- Element()->DidChangeLengthList(mAttrEnum, true);
+ Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue);
if (mList->mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -282,7 +301,12 @@ DOMSVGLength::ConvertToSpecifiedUnits(PRUint16 aUnit)
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
if (HasOwner()) {
+ if (InternalItem().GetUnit() == aUnit) {
+ return NS_OK;
+ }
+ nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(mAttrEnum);
if (InternalItem().ConvertToUnit(PRUint8(aUnit), Element(), Axis())) {
+ Element()->DidChangeLengthList(mAttrEnum, emptyOrOldValue);
return NS_OK;
}
} else {
diff --git a/content/svg/content/src/DOMSVGLengthList.cpp b/content/svg/content/src/DOMSVGLengthList.cpp
index b59930d9bdd..f5b46c46e0a 100644
--- a/content/svg/content/src/DOMSVGLengthList.cpp
+++ b/content/svg/content/src/DOMSVGLengthList.cpp
@@ -171,6 +171,7 @@ DOMSVGLengthList::Clear()
}
if (Length() > 0) {
+ nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(AttrEnum());
// Notify any existing DOM items of removal *before* truncating the lists
// so that they can find their SVGLength internal counterparts and copy
// their values. This also notifies the animVal list:
@@ -178,7 +179,7 @@ DOMSVGLengthList::Clear()
mItems.Clear();
InternalList().Clear();
- Element()->DidChangeLengthList(AttrEnum(), true);
+ Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue);
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -256,6 +257,7 @@ DOMSVGLengthList::InsertItemBefore(nsIDOMSVGLength *newItem,
return NS_ERROR_OUT_OF_MEMORY;
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(AttrEnum());
// Now that we know we're inserting, keep animVal list in sync as necessary.
MaybeInsertNullInAnimValListAt(index);
@@ -269,7 +271,7 @@ DOMSVGLengthList::InsertItemBefore(nsIDOMSVGLength *newItem,
UpdateListIndicesFromIndex(mItems, index + 1);
- Element()->DidChangeLengthList(AttrEnum(), true);
+ Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue);
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -298,6 +300,7 @@ DOMSVGLengthList::ReplaceItem(nsIDOMSVGLength *newItem,
domItem = domItem->Copy(); // must do this before changing anything!
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(AttrEnum());
if (mItems[index]) {
// Notify any existing DOM item of removal *before* modifying the lists so
// that the DOM item can copy the *old* value at its index:
@@ -311,7 +314,7 @@ DOMSVGLengthList::ReplaceItem(nsIDOMSVGLength *newItem,
// would end up reading bad data from InternalList()!
domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
- Element()->DidChangeLengthList(AttrEnum(), true);
+ Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue);
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -332,6 +335,7 @@ DOMSVGLengthList::RemoveItem(PRUint32 index,
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangeLengthList(AttrEnum());
// Now that we know we're removing, keep animVal list in sync as necessary.
// Do this *before* touching InternalList() so the removed item can get its
// internal value.
@@ -350,7 +354,7 @@ DOMSVGLengthList::RemoveItem(PRUint32 index,
UpdateListIndicesFromIndex(mItems, index);
- Element()->DidChangeLengthList(AttrEnum(), true);
+ Element()->DidChangeLengthList(AttrEnum(), emptyOrOldValue);
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
diff --git a/content/svg/content/src/DOMSVGNumber.cpp b/content/svg/content/src/DOMSVGNumber.cpp
index 2648f32b1d4..a9f815964a2 100644
--- a/content/svg/content/src/DOMSVGNumber.cpp
+++ b/content/svg/content/src/DOMSVGNumber.cpp
@@ -123,8 +123,12 @@ DOMSVGNumber::SetValue(float aValue)
NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
if (HasOwner()) {
+ if (InternalItem() == aValue) {
+ return NS_OK;
+ }
+ nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(mAttrEnum);
InternalItem() = aValue;
- Element()->DidChangeNumberList(mAttrEnum, true);
+ Element()->DidChangeNumberList(mAttrEnum, emptyOrOldValue);
if (mList->mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
diff --git a/content/svg/content/src/DOMSVGNumberList.cpp b/content/svg/content/src/DOMSVGNumberList.cpp
index 6e9d8b80458..1f458180f52 100644
--- a/content/svg/content/src/DOMSVGNumberList.cpp
+++ b/content/svg/content/src/DOMSVGNumberList.cpp
@@ -171,6 +171,7 @@ DOMSVGNumberList::Clear()
}
if (Length() > 0) {
+ nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(AttrEnum());
// Notify any existing DOM items of removal *before* truncating the lists
// so that they can find their SVGNumber internal counterparts and copy
// their values. This also notifies the animVal list:
@@ -178,7 +179,7 @@ DOMSVGNumberList::Clear()
mItems.Clear();
InternalList().Clear();
- Element()->DidChangeNumberList(AttrEnum(), true);
+ Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue);
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -256,6 +257,7 @@ DOMSVGNumberList::InsertItemBefore(nsIDOMSVGNumber *newItem,
return NS_ERROR_OUT_OF_MEMORY;
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(AttrEnum());
// Now that we know we're inserting, keep animVal list in sync as necessary.
MaybeInsertNullInAnimValListAt(index);
@@ -269,7 +271,7 @@ DOMSVGNumberList::InsertItemBefore(nsIDOMSVGNumber *newItem,
UpdateListIndicesFromIndex(mItems, index + 1);
- Element()->DidChangeNumberList(AttrEnum(), true);
+ Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue);
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -298,6 +300,7 @@ DOMSVGNumberList::ReplaceItem(nsIDOMSVGNumber *newItem,
domItem = domItem->Clone(); // must do this before changing anything!
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(AttrEnum());
if (mItems[index]) {
// Notify any existing DOM item of removal *before* modifying the lists so
// that the DOM item can copy the *old* value at its index:
@@ -311,7 +314,7 @@ DOMSVGNumberList::ReplaceItem(nsIDOMSVGNumber *newItem,
// would end up reading bad data from InternalList()!
domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
- Element()->DidChangeNumberList(AttrEnum(), true);
+ Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue);
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -340,6 +343,7 @@ DOMSVGNumberList::RemoveItem(PRUint32 index,
// We have to return the removed item, so make sure it exists:
EnsureItemAt(index);
+ nsAttrValue emptyOrOldValue = Element()->WillChangeNumberList(AttrEnum());
// Notify the DOM item of removal *before* modifying the lists so that the
// DOM item can copy its *old* value:
mItems[index]->RemovingFromList();
@@ -350,7 +354,7 @@ DOMSVGNumberList::RemoveItem(PRUint32 index,
UpdateListIndicesFromIndex(mItems, index);
- Element()->DidChangeNumberList(AttrEnum(), true);
+ Element()->DidChangeNumberList(AttrEnum(), emptyOrOldValue);
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
diff --git a/content/svg/content/src/DOMSVGPathSeg.cpp b/content/svg/content/src/DOMSVGPathSeg.cpp
index 53c5680923f..36c90033217 100644
--- a/content/svg/content/src/DOMSVGPathSeg.cpp
+++ b/content/svg/content/src/DOMSVGPathSeg.cpp
@@ -245,9 +245,13 @@ DOMSVGPathSeg::IndexIsValid()
} \
NS_ENSURE_FINITE(float(a##propName), NS_ERROR_ILLEGAL_VALUE); \
if (HasOwner()) { \
+ if (InternalItem()[1+index] == float(a##propName)) { \
+ return NS_OK; \
+ } \
+ NS_ABORT_IF_FALSE(IsInList(), "Will/DidChangePathSegList() is wrong"); \
+ nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList(); \
InternalItem()[1+index] = float(a##propName); \
- NS_ABORT_IF_FALSE(IsInList(), "DidChangePathSegList() is wrong"); \
- Element()->DidChangePathSegList(true); \
+ Element()->DidChangePathSegList(emptyOrOldValue); \
if (mList->AttrIsAnimating()) { \
Element()->AnimationNeedsResample(); \
} \
diff --git a/content/svg/content/src/DOMSVGPathSegList.cpp b/content/svg/content/src/DOMSVGPathSegList.cpp
index ff0c9ad645d..b2ae7ab3ecb 100644
--- a/content/svg/content/src/DOMSVGPathSegList.cpp
+++ b/content/svg/content/src/DOMSVGPathSegList.cpp
@@ -269,6 +269,7 @@ DOMSVGPathSegList::Clear()
}
if (Length() > 0) {
+ nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList();
// DOM list items that are to be removed must be removed before we change
// the internal list, otherwise they wouldn't be able to copy their
// internal counterparts' values!
@@ -285,7 +286,7 @@ DOMSVGPathSegList::Clear()
}
InternalList().Clear();
- Element()->DidChangePathSegList(true);
+ Element()->DidChangePathSegList(emptyOrOldValue);
if (AttrIsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -371,6 +372,7 @@ DOMSVGPathSegList::InsertItemBefore(nsIDOMSVGPathSeg *aNewItem,
return NS_ERROR_OUT_OF_MEMORY;
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList();
// Now that we know we're inserting, keep animVal list in sync as necessary.
MaybeInsertNullInAnimValListAt(aIndex, internalIndex, argCount);
@@ -387,7 +389,7 @@ DOMSVGPathSegList::InsertItemBefore(nsIDOMSVGPathSeg *aNewItem,
UpdateListIndicesFromIndex(aIndex + 1, argCount + 1);
- Element()->DidChangePathSegList(true);
+ Element()->DidChangePathSegList(emptyOrOldValue);
if (AttrIsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -416,6 +418,7 @@ DOMSVGPathSegList::ReplaceItem(nsIDOMSVGPathSeg *aNewItem,
domItem = domItem->Clone(); // must do this before changing anything!
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList();
if (ItemAt(aIndex)) {
// Notify any existing DOM item of removal *before* modifying the lists so
// that the DOM item can copy the *old* value at its index:
@@ -451,7 +454,7 @@ DOMSVGPathSegList::ReplaceItem(nsIDOMSVGPathSeg *aNewItem,
}
}
- Element()->DidChangePathSegList(true);
+ Element()->DidChangePathSegList(emptyOrOldValue);
if (AttrIsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -474,6 +477,7 @@ DOMSVGPathSegList::RemoveItem(PRUint32 aIndex,
// We have to return the removed item, so make sure it exists:
EnsureItemAt(aIndex);
+ nsAttrValue emptyOrOldValue = Element()->WillChangePathSegList();
// Notify the DOM item of removal *before* modifying the lists so that the
// DOM item can copy its *old* value:
ItemAt(aIndex)->RemovingFromList();
@@ -493,7 +497,7 @@ DOMSVGPathSegList::RemoveItem(PRUint32 aIndex,
UpdateListIndicesFromIndex(aIndex, -(argCount + 1));
- Element()->DidChangePathSegList(true);
+ Element()->DidChangePathSegList(emptyOrOldValue);
if (AttrIsAnimating()) {
Element()->AnimationNeedsResample();
}
diff --git a/content/svg/content/src/DOMSVGPoint.cpp b/content/svg/content/src/DOMSVGPoint.cpp
index ba3d6ef946b..dbe96022c94 100644
--- a/content/svg/content/src/DOMSVGPoint.cpp
+++ b/content/svg/content/src/DOMSVGPoint.cpp
@@ -98,8 +98,12 @@ DOMSVGPoint::SetX(float aX)
NS_ENSURE_FINITE(aX, NS_ERROR_ILLEGAL_VALUE);
if (HasOwner()) {
+ if (InternalItem().mX == aX) {
+ return NS_OK;
+ }
+ nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
InternalItem().mX = aX;
- Element()->DidChangePointList(true);
+ Element()->DidChangePointList(emptyOrOldValue);
if (mList->AttrIsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -129,8 +133,12 @@ DOMSVGPoint::SetY(float aY)
NS_ENSURE_FINITE(aY, NS_ERROR_ILLEGAL_VALUE);
if (HasOwner()) {
+ if (InternalItem().mY == aY) {
+ return NS_OK;
+ }
+ nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
InternalItem().mY = aY;
- Element()->DidChangePointList(true);
+ Element()->DidChangePointList(emptyOrOldValue);
if (mList->AttrIsAnimating()) {
Element()->AnimationNeedsResample();
}
diff --git a/content/svg/content/src/DOMSVGPointList.cpp b/content/svg/content/src/DOMSVGPointList.cpp
index 18baadb595c..aa660fe4187 100644
--- a/content/svg/content/src/DOMSVGPointList.cpp
+++ b/content/svg/content/src/DOMSVGPointList.cpp
@@ -213,6 +213,7 @@ DOMSVGPointList::Clear()
}
if (Length() > 0) {
+ nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
// DOM list items that are to be removed must be removed before we change
// the internal list, otherwise they wouldn't be able to copy their
// internal counterparts' values!
@@ -229,7 +230,7 @@ DOMSVGPointList::Clear()
}
InternalList().Clear();
- Element()->DidChangePointList(true);
+ Element()->DidChangePointList(emptyOrOldValue);
if (AttrIsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -307,6 +308,7 @@ DOMSVGPointList::InsertItemBefore(nsIDOMSVGPoint *aNewItem,
return NS_ERROR_OUT_OF_MEMORY;
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
// Now that we know we're inserting, keep animVal list in sync as necessary.
MaybeInsertNullInAnimValListAt(aIndex);
@@ -320,7 +322,7 @@ DOMSVGPointList::InsertItemBefore(nsIDOMSVGPoint *aNewItem,
UpdateListIndicesFromIndex(mItems, aIndex + 1);
- Element()->DidChangePointList(true);
+ Element()->DidChangePointList(emptyOrOldValue);
if (AttrIsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -349,6 +351,7 @@ DOMSVGPointList::ReplaceItem(nsIDOMSVGPoint *aNewItem,
domItem = domItem->Clone(); // must do this before changing anything!
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
if (mItems[aIndex]) {
// Notify any existing DOM item of removal *before* modifying the lists so
// that the DOM item can copy the *old* value at its index:
@@ -362,7 +365,7 @@ DOMSVGPointList::ReplaceItem(nsIDOMSVGPoint *aNewItem,
// would end up reading bad data from InternalList()!
domItem->InsertingIntoList(this, aIndex, IsAnimValList());
- Element()->DidChangePointList(true);
+ Element()->DidChangePointList(emptyOrOldValue);
if (AttrIsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -383,6 +386,7 @@ DOMSVGPointList::RemoveItem(PRUint32 aIndex,
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
// Now that we know we're removing, keep animVal list in sync as necessary.
// Do this *before* touching InternalList() so the removed item can get its
// internal value.
@@ -401,7 +405,7 @@ DOMSVGPointList::RemoveItem(PRUint32 aIndex,
UpdateListIndicesFromIndex(mItems, aIndex);
- Element()->DidChangePointList(true);
+ Element()->DidChangePointList(emptyOrOldValue);
if (AttrIsAnimating()) {
Element()->AnimationNeedsResample();
}
diff --git a/content/svg/content/src/DOMSVGStringList.cpp b/content/svg/content/src/DOMSVGStringList.cpp
index 8fd2a53d605..7f0966b1b55 100644
--- a/content/svg/content/src/DOMSVGStringList.cpp
+++ b/content/svg/content/src/DOMSVGStringList.cpp
@@ -109,9 +109,12 @@ NS_IMETHODIMP
DOMSVGStringList::Clear()
{
if (InternalList().IsExplicitlySet()) {
+ nsAttrValue emptyOrOldValue =
+ mElement->WillChangeStringList(mIsConditionalProcessingAttribute,
+ mAttrEnum);
InternalList().Clear();
mElement->DidChangeStringList(mIsConditionalProcessingAttribute,
- mAttrEnum);
+ mAttrEnum, emptyOrOldValue);
}
return NS_OK;
}
@@ -151,9 +154,13 @@ DOMSVGStringList::InsertItemBefore(const nsAString & newItem,
return NS_ERROR_OUT_OF_MEMORY;
}
+ nsAttrValue emptyOrOldValue =
+ mElement->WillChangeStringList(mIsConditionalProcessingAttribute,
+ mAttrEnum);
InternalList().InsertItem(index, newItem);
- mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum);
+ mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum,
+ emptyOrOldValue);
_retval = newItem;
return NS_OK;
}
@@ -171,9 +178,13 @@ DOMSVGStringList::ReplaceItem(const nsAString & newItem,
}
_retval = InternalList()[index];
+ nsAttrValue emptyOrOldValue =
+ mElement->WillChangeStringList(mIsConditionalProcessingAttribute,
+ mAttrEnum);
InternalList().ReplaceItem(index, newItem);
- mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum);
+ mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum,
+ emptyOrOldValue);
return NS_OK;
}
@@ -185,9 +196,13 @@ DOMSVGStringList::RemoveItem(PRUint32 index,
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
+ nsAttrValue emptyOrOldValue =
+ mElement->WillChangeStringList(mIsConditionalProcessingAttribute,
+ mAttrEnum);
InternalList().RemoveItem(index);
- mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum);
+ mElement->DidChangeStringList(mIsConditionalProcessingAttribute, mAttrEnum,
+ emptyOrOldValue);
return NS_OK;
}
diff --git a/content/svg/content/src/DOMSVGStringList.h b/content/svg/content/src/DOMSVGStringList.h
index 6bb74f3c562..1437687db75 100644
--- a/content/svg/content/src/DOMSVGStringList.h
+++ b/content/svg/content/src/DOMSVGStringList.h
@@ -111,8 +111,6 @@ private:
~DOMSVGStringList();
- void DidChangeStringList(PRUint8 aAttrEnum, bool aDoSetAttr);
-
SVGStringList &InternalList();
// Strong ref to our element to keep it alive.
diff --git a/content/svg/content/src/DOMSVGTests.cpp b/content/svg/content/src/DOMSVGTests.cpp
index cdfa3585214..77e4bdcddaf 100644
--- a/content/svg/content/src/DOMSVGTests.cpp
+++ b/content/svg/content/src/DOMSVGTests.cpp
@@ -48,13 +48,18 @@ using namespace mozilla;
NS_IMPL_ISUPPORTS1(DOMSVGTests, nsIDOMSVGTests)
-DOMSVGTests::StringListInfo DOMSVGTests::sStringListInfo[3] =
+nsIAtom** DOMSVGTests::sStringListNames[3] =
{
- { &nsGkAtoms::requiredFeatures, false },
- { &nsGkAtoms::requiredExtensions, false },
- { &nsGkAtoms::systemLanguage, true }
+ &nsGkAtoms::requiredFeatures,
+ &nsGkAtoms::requiredExtensions,
+ &nsGkAtoms::systemLanguage,
};
+DOMSVGTests::DOMSVGTests()
+{
+ mStringListAttributes[LANGUAGE].SetIsCommaSeparated(true);
+}
+
/* readonly attribute nsIDOMSVGStringList requiredFeatures; */
NS_IMETHODIMP
DOMSVGTests::GetRequiredFeatures(nsIDOMSVGStringList * *aRequiredFeatures)
@@ -96,8 +101,8 @@ DOMSVGTests::HasExtension(const nsAString & extension, bool *_retval)
bool
DOMSVGTests::IsConditionalProcessingAttribute(const nsIAtom* aAttribute) const
{
- for (PRUint32 i = 0; i < ArrayLength(sStringListInfo); i++) {
- if (aAttribute == *sStringListInfo[i].mName) {
+ for (PRUint32 i = 0; i < ArrayLength(sStringListNames); i++) {
+ if (aAttribute == *sStringListNames[i]) {
return true;
}
}
@@ -222,10 +227,9 @@ DOMSVGTests::ParseConditionalProcessingAttribute(nsIAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult)
{
- for (PRUint32 i = 0; i < ArrayLength(sStringListInfo); i++) {
- if (aAttribute == *sStringListInfo[i].mName) {
- nsresult rv = mStringListAttributes[i].SetValue(
- aValue, sStringListInfo[i].mIsCommaSeparated);
+ for (PRUint32 i = 0; i < ArrayLength(sStringListNames); i++) {
+ if (aAttribute == *sStringListNames[i]) {
+ nsresult rv = mStringListAttributes[i].SetValue(aValue);
if (NS_FAILED(rv)) {
mStringListAttributes[i].Clear();
}
@@ -236,20 +240,11 @@ DOMSVGTests::ParseConditionalProcessingAttribute(nsIAtom* aAttribute,
return false;
}
-void
-DOMSVGTests::GetValue(PRUint8 aAttrEnum, nsAString& aValue) const
-{
- NS_ABORT_IF_FALSE(aAttrEnum >= 0 && aAttrEnum < ArrayLength(sStringListInfo),
- "aAttrEnum out of range");
- mStringListAttributes[aAttrEnum].GetValue(
- aValue, sStringListInfo[aAttrEnum].mIsCommaSeparated);
-}
-
void
DOMSVGTests::UnsetAttr(const nsIAtom* aAttribute)
{
- for (PRUint32 i = 0; i < ArrayLength(sStringListInfo); i++) {
- if (aAttribute == *sStringListInfo[i].mName) {
+ for (PRUint32 i = 0; i < ArrayLength(sStringListNames); i++) {
+ if (aAttribute == *sStringListNames[i]) {
mStringListAttributes[i].Clear();
MaybeInvalidate();
return;
@@ -257,22 +252,18 @@ DOMSVGTests::UnsetAttr(const nsIAtom* aAttribute)
}
}
-void
-DOMSVGTests::DidChangeStringList(PRUint8 aAttrEnum)
+nsIAtom*
+DOMSVGTests::GetAttrName(PRUint8 aAttrEnum) const
{
- NS_ASSERTION(aAttrEnum < ArrayLength(sStringListInfo), "aAttrEnum out of range");
+ return *sStringListNames[aAttrEnum];
+}
- nsCOMPtr element = do_QueryInterface(this);
-
- nsAutoString serializedValue;
- GetValue(aAttrEnum, serializedValue);
-
- nsAttrValue attrValue(serializedValue);
- element->SetParsedAttr(kNameSpaceID_None,
- *sStringListInfo[aAttrEnum].mName,
- nsnull, attrValue, true);
-
- MaybeInvalidate();
+void
+DOMSVGTests::GetAttrValue(PRUint8 aAttrEnum, nsAttrValue& aValue) const
+{
+ NS_ABORT_IF_FALSE(aAttrEnum >= 0 && aAttrEnum < ArrayLength(sStringListNames),
+ "aAttrEnum out of range");
+ aValue.SetTo(mStringListAttributes[aAttrEnum], nsnull);
}
void
@@ -281,7 +272,7 @@ DOMSVGTests::MaybeInvalidate()
nsCOMPtr element = do_QueryInterface(this);
nsIContent* parent = element->GetFlattenedTreeParent();
-
+
if (parent &&
parent->NodeInfo()->Equals(nsGkAtoms::svgSwitch, kNameSpaceID_SVG)) {
static_cast(parent)->MaybeInvalidate();
diff --git a/content/svg/content/src/DOMSVGTests.h b/content/svg/content/src/DOMSVGTests.h
index f1332a8ac22..7fc3cb232c3 100644
--- a/content/svg/content/src/DOMSVGTests.h
+++ b/content/svg/content/src/DOMSVGTests.h
@@ -59,6 +59,8 @@ public:
friend class mozilla::DOMSVGStringList;
typedef mozilla::SVGStringList SVGStringList;
+ DOMSVGTests();
+
/**
* Compare the language name(s) in a systemLanguage attribute to the
* user's language preferences, as defined in
@@ -103,30 +105,20 @@ public:
const nsAString& aValue,
nsAttrValue& aResult);
- /**
- * Serialises the conditional processing attribute.
- */
- void GetValue(PRUint8 aAttrEnum, nsAString& aValue) const;
-
/**
* Unsets a conditional processing attribute.
*/
void UnsetAttr(const nsIAtom* aAttribute);
- void DidChangeStringList(PRUint8 aAttrEnum);
+ nsIAtom* GetAttrName(PRUint8 aAttrEnum) const;
+ void GetAttrValue(PRUint8 aAttrEnum, nsAttrValue &aValue) const;
void MaybeInvalidate();
private:
-
- struct StringListInfo {
- nsIAtom** mName;
- bool mIsCommaSeparated;
- };
-
enum { FEATURES, EXTENSIONS, LANGUAGE };
SVGStringList mStringListAttributes[3];
- static StringListInfo sStringListInfo[3];
+ static nsIAtom** sStringListNames[3];
};
#endif // MOZILLA_DOMSVGTESTS_H__
diff --git a/content/svg/content/src/DOMSVGTransform.cpp b/content/svg/content/src/DOMSVGTransform.cpp
index 94d36007c25..8f8956464c3 100644
--- a/content/svg/content/src/DOMSVGTransform.cpp
+++ b/content/svg/content/src/DOMSVGTransform.cpp
@@ -173,9 +173,7 @@ DOMSVGTransform::SetMatrix(nsIDOMSVGMatrix *matrix)
if (!domMatrix)
return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
- Transform().SetMatrix(domMatrix->Matrix());
- NotifyElementOfChange();
-
+ SetMatrix(domMatrix->Matrix());
return NS_OK;
}
@@ -188,8 +186,14 @@ DOMSVGTransform::SetTranslate(float tx, float ty)
}
NS_ENSURE_FINITE2(tx, ty, NS_ERROR_ILLEGAL_VALUE);
+ if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_TRANSLATE &&
+ Matrix().x0 == tx && Matrix().y0 == ty) {
+ return NS_OK;
+ }
+
+ nsAttrValue emptyOrOldValue = NotifyElementWillChange();
Transform().SetTranslate(tx, ty);
- NotifyElementOfChange();
+ NotifyElementDidChange(emptyOrOldValue);
return NS_OK;
}
@@ -203,8 +207,14 @@ DOMSVGTransform::SetScale(float sx, float sy)
}
NS_ENSURE_FINITE2(sx, sy, NS_ERROR_ILLEGAL_VALUE);
+ if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_SCALE &&
+ Matrix().xx == sx && Matrix().yy == sy) {
+ return NS_OK;
+ }
+
+ nsAttrValue emptyOrOldValue = NotifyElementWillChange();
Transform().SetScale(sx, sy);
- NotifyElementOfChange();
+ NotifyElementDidChange(emptyOrOldValue);
return NS_OK;
}
@@ -218,8 +228,17 @@ DOMSVGTransform::SetRotate(float angle, float cx, float cy)
}
NS_ENSURE_FINITE3(angle, cx, cy, NS_ERROR_ILLEGAL_VALUE);
+ if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_ROTATE) {
+ float currentCx, currentCy;
+ Transform().GetRotationOrigin(currentCx, currentCy);
+ if (Transform().Angle() == angle && currentCx == cx && currentCy == cy) {
+ return NS_OK;
+ }
+ }
+
+ nsAttrValue emptyOrOldValue = NotifyElementWillChange();
Transform().SetRotate(angle, cx, cy);
- NotifyElementOfChange();
+ NotifyElementDidChange(emptyOrOldValue);
return NS_OK;
}
@@ -233,10 +252,16 @@ DOMSVGTransform::SetSkewX(float angle)
}
NS_ENSURE_FINITE(angle, NS_ERROR_ILLEGAL_VALUE);
+ if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_SKEWX &&
+ Transform().Angle() == angle) {
+ return NS_OK;
+ }
+
+ nsAttrValue emptyOrOldValue = NotifyElementWillChange();
nsresult rv = Transform().SetSkewX(angle);
if (NS_FAILED(rv))
return rv;
- NotifyElementOfChange();
+ NotifyElementDidChange(emptyOrOldValue);
return NS_OK;
}
@@ -250,10 +275,16 @@ DOMSVGTransform::SetSkewY(float angle)
}
NS_ENSURE_FINITE(angle, NS_ERROR_ILLEGAL_VALUE);
+ if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_SKEWY &&
+ Transform().Angle() == angle) {
+ return NS_OK;
+ }
+
+ nsAttrValue emptyOrOldValue = NotifyElementWillChange();
nsresult rv = Transform().SetSkewY(angle);
if (NS_FAILED(rv))
return rv;
- NotifyElementOfChange();
+ NotifyElementDidChange(emptyOrOldValue);
return NS_OK;
}
@@ -324,8 +355,15 @@ DOMSVGTransform::SetMatrix(const gfxMatrix& aMatrix)
{
NS_ABORT_IF_FALSE(!mIsAnimValItem,
"Attempting to modify read-only transform");
+
+ if (Transform().Type() == nsIDOMSVGTransform::SVG_TRANSFORM_MATRIX &&
+ SVGTransform::MatricesEqual(Matrix(), aMatrix)) {
+ return;
+ }
+
+ nsAttrValue emptyOrOldValue = NotifyElementWillChange();
Transform().SetMatrix(aMatrix);
- NotifyElementOfChange();
+ NotifyElementDidChange(emptyOrOldValue);
}
void
@@ -341,10 +379,10 @@ DOMSVGTransform::ClearMatrixTearoff(DOMSVGMatrix* aMatrix)
// Implementation helpers
void
-DOMSVGTransform::NotifyElementOfChange()
+DOMSVGTransform::NotifyElementDidChange(const nsAttrValue& aEmptyOrOldValue)
{
if (HasOwner()) {
- Element()->DidChangeTransformList(true);
+ Element()->DidChangeTransformList(aEmptyOrOldValue);
if (mList->mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
diff --git a/content/svg/content/src/DOMSVGTransform.h b/content/svg/content/src/DOMSVGTransform.h
index 58654e0b213..7f1f6ec6fa8 100644
--- a/content/svg/content/src/DOMSVGTransform.h
+++ b/content/svg/content/src/DOMSVGTransform.h
@@ -206,7 +206,8 @@ private:
SVGTransform& Transform() {
return HasOwner() ? InternalItem() : *mTransform;
}
- void NotifyElementOfChange();
+ inline nsAttrValue NotifyElementWillChange();
+ void NotifyElementDidChange(const nsAttrValue& aEmptyOrOldValue);
nsRefPtr mList;
@@ -235,6 +236,16 @@ private:
NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGTransform, MOZILLA_DOMSVGTRANSFORM_IID)
+nsAttrValue
+DOMSVGTransform::NotifyElementWillChange()
+{
+ nsAttrValue result;
+ if (HasOwner()) {
+ result = Element()->WillChangeTransformList();
+ }
+ return result;
+}
+
} // namespace mozilla
#undef MOZ_SVG_LIST_INDEX_BIT_COUNT
diff --git a/content/svg/content/src/DOMSVGTransformList.cpp b/content/svg/content/src/DOMSVGTransformList.cpp
index 6484e15b5c5..c8c89f18c02 100644
--- a/content/svg/content/src/DOMSVGTransformList.cpp
+++ b/content/svg/content/src/DOMSVGTransformList.cpp
@@ -184,6 +184,7 @@ DOMSVGTransformList::Clear()
}
if (Length() > 0) {
+ nsAttrValue emptyOrOldValue = Element()->WillChangeTransformList();
// Notify any existing DOM items of removal *before* truncating the lists
// so that they can find their SVGTransform internal counterparts and copy
// their values. This also notifies the animVal list:
@@ -191,7 +192,7 @@ DOMSVGTransformList::Clear()
mItems.Clear();
InternalList().Clear();
- Element()->DidChangeTransformList(true);
+ Element()->DidChangeTransformList(emptyOrOldValue);
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -272,6 +273,7 @@ DOMSVGTransformList::InsertItemBefore(nsIDOMSVGTransform *newItem,
return NS_ERROR_OUT_OF_MEMORY;
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangeTransformList();
// Now that we know we're inserting, keep animVal list in sync as necessary.
MaybeInsertNullInAnimValListAt(index);
@@ -285,7 +287,7 @@ DOMSVGTransformList::InsertItemBefore(nsIDOMSVGTransform *newItem,
UpdateListIndicesFromIndex(mItems, index + 1);
- Element()->DidChangeTransformList(true);
+ Element()->DidChangeTransformList(emptyOrOldValue);
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -316,6 +318,7 @@ DOMSVGTransformList::ReplaceItem(nsIDOMSVGTransform *newItem,
domItem = domItem->Clone(); // must do this before changing anything!
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangeTransformList();
if (mItems[index]) {
// Notify any existing DOM item of removal *before* modifying the lists so
// that the DOM item can copy the *old* value at its index:
@@ -329,7 +332,7 @@ DOMSVGTransformList::ReplaceItem(nsIDOMSVGTransform *newItem,
// would end up reading bad data from InternalList()!
domItem->InsertingIntoList(this, index, IsAnimValList());
- Element()->DidChangeTransformList(true);
+ Element()->DidChangeTransformList(emptyOrOldValue);
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
@@ -350,6 +353,7 @@ DOMSVGTransformList::RemoveItem(PRUint32 index, nsIDOMSVGTransform **_retval)
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
+ nsAttrValue emptyOrOldValue = Element()->WillChangeTransformList();
// Now that we know we're removing, keep animVal list in sync as necessary.
// Do this *before* touching InternalList() so the removed item can get its
// internal value.
@@ -368,7 +372,7 @@ DOMSVGTransformList::RemoveItem(PRUint32 index, nsIDOMSVGTransform **_retval)
UpdateListIndicesFromIndex(mItems, index);
- Element()->DidChangeTransformList(true);
+ Element()->DidChangeTransformList(emptyOrOldValue);
if (mAList->IsAnimating()) {
Element()->AnimationNeedsResample();
}
diff --git a/content/svg/content/src/Makefile.in b/content/svg/content/src/Makefile.in
index f27cb4d4517..601a5e30d1c 100644
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -146,6 +146,7 @@ CPPSRCS = \
nsSVGAnimationElement.cpp \
nsSVGMpathElement.cpp \
nsSVGSetElement.cpp \
+ SVGAttrValueWrapper.cpp \
SVGIntegerPairSMILType.cpp \
SVGLengthListSMILType.cpp \
SVGMotionSMILType.cpp \
@@ -169,6 +170,7 @@ FORCE_STATIC_LIB = 1
EXPORTS = \
nsSVGFeatures.h \
nsSVGRect.h \
+ SVGAttrValueWrapper.h \
$(NULL)
include $(topsrcdir)/config/rules.mk
diff --git a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp
index 9905e3a4831..429de200e13 100644
--- a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp
+++ b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.cpp
@@ -250,7 +250,8 @@ SVGAnimatedPreserveAspectRatio::SetBaseValueString(
}
void
-SVGAnimatedPreserveAspectRatio::GetBaseValueString(nsAString & aValueAsString)
+SVGAnimatedPreserveAspectRatio::GetBaseValueString(
+ nsAString& aValueAsString) const
{
nsAutoString tmpString;
@@ -276,12 +277,17 @@ nsresult
SVGAnimatedPreserveAspectRatio::SetBaseAlign(PRUint16 aAlign,
nsSVGElement *aSVGElement)
{
+ if (mIsBaseSet && mBaseVal.GetAlign() == aAlign) {
+ return NS_OK;
+ }
+
+ nsAttrValue emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio();
nsresult rv = mBaseVal.SetAlign(aAlign);
NS_ENSURE_SUCCESS(rv, rv);
mIsBaseSet = true;
mAnimVal.mAlign = mBaseVal.mAlign;
- aSVGElement->DidChangePreserveAspectRatio(true);
+ aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue);
if (mIsAnimated) {
aSVGElement->AnimationNeedsResample();
}
@@ -293,12 +299,17 @@ nsresult
SVGAnimatedPreserveAspectRatio::SetBaseMeetOrSlice(PRUint16 aMeetOrSlice,
nsSVGElement *aSVGElement)
{
+ if (mIsBaseSet && mBaseVal.GetMeetOrSlice() == aMeetOrSlice) {
+ return NS_OK;
+ }
+
+ nsAttrValue emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio();
nsresult rv = mBaseVal.SetMeetOrSlice(aMeetOrSlice);
NS_ENSURE_SUCCESS(rv, rv);
mIsBaseSet = true;
mAnimVal.mMeetOrSlice = mBaseVal.mMeetOrSlice;
- aSVGElement->DidChangePreserveAspectRatio(true);
+ aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue);
if (mIsAnimated) {
aSVGElement->AnimationNeedsResample();
}
diff --git a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
index b8c353481d0..5b2e8396aef 100644
--- a/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
+++ b/content/svg/content/src/SVGAnimatedPreserveAspectRatio.h
@@ -118,7 +118,7 @@ public:
nsresult SetBaseValueString(const nsAString& aValue,
nsSVGElement *aSVGElement);
- void GetBaseValueString(nsAString& aValue);
+ void GetBaseValueString(nsAString& aValue) const;
nsresult SetBaseAlign(PRUint16 aAlign, nsSVGElement *aSVGElement);
nsresult SetBaseMeetOrSlice(PRUint16 aMeetOrSlice, nsSVGElement *aSVGElement);
diff --git a/content/svg/content/src/SVGAttrValueWrapper.cpp b/content/svg/content/src/SVGAttrValueWrapper.cpp
new file mode 100644
index 00000000000..af3f5455c1c
--- /dev/null
+++ b/content/svg/content/src/SVGAttrValueWrapper.cpp
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** 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 the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is Mozilla Japan.
+ * 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 "SVGAttrValueWrapper.h"
+#include "nsSVGAngle.h"
+#include "nsSVGIntegerPair.h"
+#include "nsSVGLength2.h"
+#include "nsSVGNumberPair.h"
+#include "nsSVGViewBox.h"
+#include "SVGAnimatedPreserveAspectRatio.h"
+#include "SVGLengthList.h"
+#include "SVGNumberList.h"
+#include "SVGPathData.h"
+#include "SVGPointList.h"
+#include "SVGStringList.h"
+#include "SVGTransformList.h"
+
+using namespace mozilla;
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const nsSVGAngle* aAngle, nsAString& aResult)
+{
+ aAngle->GetBaseValueString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const nsSVGIntegerPair* aIntegerPair,
+ nsAString& aResult)
+{
+ aIntegerPair->GetBaseValueString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const nsSVGLength2* aLength, nsAString& aResult)
+{
+ aLength->GetBaseValueString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const SVGLengthList* aLengthList,
+ nsAString& aResult)
+{
+ aLengthList->GetValueAsString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const SVGNumberList* aNumberList,
+ nsAString& aResult)
+{
+ aNumberList->GetValueAsString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const nsSVGNumberPair* aNumberPair,
+ nsAString& aResult)
+{
+ aNumberPair->GetBaseValueString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const SVGPathData* aPathData, nsAString& aResult)
+{
+ aPathData->GetValueAsString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const SVGPointList* aPointList,
+ nsAString& aResult)
+{
+ aPointList->GetValueAsString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(
+ const SVGAnimatedPreserveAspectRatio* aPreserveAspectRatio,
+ nsAString& aResult)
+{
+ aPreserveAspectRatio->GetBaseValueString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const SVGStringList* aStringList,
+ nsAString& aResult)
+{
+ aStringList->GetValue(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const SVGTransformList* aTransformList,
+ nsAString& aResult)
+{
+ aTransformList->GetValueAsString(aResult);
+}
+
+/*static*/ void
+SVGAttrValueWrapper::ToString(const nsSVGViewBox* aViewBox, nsAString& aResult)
+{
+ aViewBox->GetBaseValueString(aResult);
+}
diff --git a/content/svg/content/src/SVGAttrValueWrapper.h b/content/svg/content/src/SVGAttrValueWrapper.h
new file mode 100644
index 00000000000..b0419d57099
--- /dev/null
+++ b/content/svg/content/src/SVGAttrValueWrapper.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** 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 the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is Mozilla Japan.
+ * 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_SVGATTRVALUEWRAPPER_H__
+#define MOZILLA_SVGATTRVALUEWRAPPER_H__
+
+/**
+ * Utility wrapper for handling SVG types used inside nsAttrValue so that these
+ * types don't need to be exported outside the SVG module.
+ */
+
+#include "nsString.h"
+
+class nsSVGAngle;
+class nsSVGIntegerPair;
+class nsSVGLength2;
+class nsSVGNumberPair;
+class nsSVGViewBox;
+
+namespace mozilla {
+class SVGLengthList;
+class SVGNumberList;
+class SVGPathData;
+class SVGPointList;
+class SVGAnimatedPreserveAspectRatio;
+class SVGStringList;
+class SVGTransformList;
+}
+
+namespace mozilla {
+
+class SVGAttrValueWrapper
+{
+public:
+ static void ToString(const nsSVGAngle* aAngle, nsAString& aResult);
+ static void ToString(const nsSVGIntegerPair* aIntegerPair,
+ nsAString& aResult);
+ static void ToString(const nsSVGLength2* aLength, nsAString& aResult);
+ static void ToString(const mozilla::SVGLengthList* aLengthList,
+ nsAString& aResult);
+ static void ToString(const mozilla::SVGNumberList* aNumberList,
+ nsAString& aResult);
+ static void ToString(const nsSVGNumberPair* aNumberPair, nsAString& aResult);
+ static void ToString(const mozilla::SVGPathData* aPathData,
+ nsAString& aResult);
+ static void ToString(const mozilla::SVGPointList* aPointList,
+ nsAString& aResult);
+ static void ToString(
+ const mozilla::SVGAnimatedPreserveAspectRatio* aPreserveAspectRatio,
+ nsAString& aResult);
+ static void ToString(const mozilla::SVGStringList* aStringList,
+ nsAString& aResult);
+ static void ToString(const mozilla::SVGTransformList* aTransformList,
+ nsAString& aResult);
+ static void ToString(const nsSVGViewBox* aViewBox, nsAString& aResult);
+};
+
+} /* namespace mozilla */
+
+#endif // MOZILLA_SVGATTRVALUEWRAPPER_H__
diff --git a/content/svg/content/src/SVGStringList.cpp b/content/svg/content/src/SVGStringList.cpp
index 6cb8a69fd1b..a8a608135da 100644
--- a/content/svg/content/src/SVGStringList.cpp
+++ b/content/svg/content/src/SVGStringList.cpp
@@ -62,14 +62,14 @@ SVGStringList::CopyFrom(const SVGStringList& rhs)
}
void
-SVGStringList::GetValue(nsAString& aValue, bool aIsCommaSeparated) const
+SVGStringList::GetValue(nsAString& aValue) const
{
aValue.Truncate();
PRUint32 last = mStrings.Length() - 1;
for (PRUint32 i = 0; i < mStrings.Length(); ++i) {
aValue.Append(mStrings[i]);
if (i != last) {
- if (aIsCommaSeparated) {
+ if (mIsCommaSeparated) {
aValue.Append(',');
}
aValue.Append(' ');
@@ -78,11 +78,11 @@ SVGStringList::GetValue(nsAString& aValue, bool aIsCommaSeparated) const
}
nsresult
-SVGStringList::SetValue(const nsAString& aValue, bool aIsCommaSeparated)
+SVGStringList::SetValue(const nsAString& aValue)
{
SVGStringList temp;
- if (aIsCommaSeparated) {
+ if (mIsCommaSeparated) {
nsCharSeparatedTokenizerTemplate
tokenizer(aValue, ',');
diff --git a/content/svg/content/src/SVGStringList.h b/content/svg/content/src/SVGStringList.h
index 4548564c95d..5452d944f8d 100644
--- a/content/svg/content/src/SVGStringList.h
+++ b/content/svg/content/src/SVGStringList.h
@@ -54,10 +54,13 @@ class SVGStringList
public:
- SVGStringList() : mIsSet(false) {}
+ SVGStringList() : mIsSet(false), mIsCommaSeparated(false) {}
~SVGStringList(){}
- nsresult SetValue(const nsAString& aValue, bool aIsCommaSeparated);
+ void SetIsCommaSeparated(bool aIsCommaSeparated) {
+ mIsCommaSeparated = aIsCommaSeparated;
+ }
+ nsresult SetValue(const nsAString& aValue);
void Clear() {
mStrings.Clear();
@@ -65,7 +68,7 @@ public:
}
/// This may return an incomplete string on OOM, but that's acceptable.
- void GetValue(nsAString& aValue, bool aIsCommaSeparated) const;
+ void GetValue(nsAString& aValue) const;
bool IsEmpty() const {
return mStrings.IsEmpty();
@@ -91,9 +94,8 @@ public:
mStrings.Compact();
}
- // Returns true if the animated value of this stringlist has been explicitly
- // set by taking on the base value which has been explicitly set by markup
- // or a DOM call, false otherwise.
+ // Returns true if the value of this stringlist has been explicitly
+ // set by markup or a DOM call, false otherwise.
bool IsExplicitlySet() const
{ return mIsSet; }
@@ -168,6 +170,7 @@ protected:
*/
nsTArray mStrings;
bool mIsSet;
+ bool mIsCommaSeparated;
};
} // namespace mozilla
diff --git a/content/svg/content/src/SVGTransform.h b/content/svg/content/src/SVGTransform.h
index b4d661e9e91..51bf7c66034 100644
--- a/content/svg/content/src/SVGTransform.h
+++ b/content/svg/content/src/SVGTransform.h
@@ -97,7 +97,6 @@ public:
nsresult SetSkewX(float aAngle);
nsresult SetSkewY(float aAngle);
-protected:
static bool MatricesEqual(const gfxMatrix& a, const gfxMatrix& b)
{
return a.xx == b.xx &&
@@ -108,6 +107,7 @@ protected:
a.y0 == b.y0;
}
+protected:
gfxMatrix mMatrix;
float mAngle, mOriginX, mOriginY;
PRUint16 mType;
diff --git a/content/svg/content/src/nsSVGAngle.cpp b/content/svg/content/src/nsSVGAngle.cpp
index 087d00f2ee8..68979970836 100644
--- a/content/svg/content/src/nsSVGAngle.cpp
+++ b/content/svg/content/src/nsSVGAngle.cpp
@@ -72,7 +72,7 @@ public:
NS_IMETHOD SetValue(float aValue)
{
NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
- mVal.SetBaseValue(aValue, nsnull);
+ mVal.SetBaseValue(aValue, nsnull, true);
return NS_OK;
}
@@ -258,6 +258,11 @@ void
nsSVGAngle::SetBaseValueInSpecifiedUnits(float aValue,
nsSVGElement *aSVGElement)
{
+ if (mBaseVal == aValue) {
+ return;
+ }
+
+ nsAttrValue emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
mBaseVal = aValue;
if (!mIsAnimated) {
mAnimVal = mBaseVal;
@@ -265,7 +270,7 @@ nsSVGAngle::SetBaseValueInSpecifiedUnits(float aValue,
else {
aSVGElement->AnimationNeedsResample();
}
- aSVGElement->DidChangeAngle(mAttrEnum, true);
+ aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
}
nsresult
@@ -275,9 +280,19 @@ nsSVGAngle::ConvertToSpecifiedUnits(PRUint16 unitType,
if (!IsValidUnitType(unitType))
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+ if (mBaseValUnit == PRUint8(unitType))
+ return NS_OK;
+
+ nsAttrValue emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
+
float valueInUserUnits = mBaseVal * GetDegreesPerUnit(mBaseValUnit);
mBaseValUnit = PRUint8(unitType);
- SetBaseValue(valueInUserUnits, aSVGElement);
+ // Setting aDoSetAttr to false here will ensure we don't call
+ // Will/DidChangeAngle a second time (and dispatch duplicate notifications).
+ SetBaseValue(valueInUserUnits, aSVGElement, false);
+
+ aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
+
return NS_OK;
}
@@ -291,6 +306,13 @@ nsSVGAngle::NewValueSpecifiedUnits(PRUint16 unitType,
if (!IsValidUnitType(unitType))
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+ if (mBaseVal == valueInSpecifiedUnits && mBaseValUnit == PRUint8(unitType))
+ return NS_OK;
+
+ nsAttrValue emptyOrOldValue;
+ if (aSVGElement) {
+ emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
+ }
mBaseVal = valueInSpecifiedUnits;
mBaseValUnit = PRUint8(unitType);
if (!mIsAnimated) {
@@ -301,7 +323,7 @@ nsSVGAngle::NewValueSpecifiedUnits(PRUint16 unitType,
aSVGElement->AnimationNeedsResample();
}
if (aSVGElement) {
- aSVGElement->DidChangeAngle(mAttrEnum, true);
+ aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
}
return NS_OK;
}
@@ -342,7 +364,14 @@ nsSVGAngle::SetBaseValueString(const nsAString &aValueAsString,
if (NS_FAILED(rv)) {
return rv;
}
+ if (mBaseVal == value && mBaseValUnit == PRUint8(unitType)) {
+ return NS_OK;
+ }
+ nsAttrValue emptyOrOldValue;
+ if (aDoSetAttr) {
+ emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
+ }
mBaseVal = value;
mBaseValUnit = PRUint8(unitType);
if (!mIsAnimated) {
@@ -353,27 +382,36 @@ nsSVGAngle::SetBaseValueString(const nsAString &aValueAsString,
aSVGElement->AnimationNeedsResample();
}
- // We don't need to call DidChange* here - we're only called by
- // nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
- // which takes care of notifying.
+ if (aDoSetAttr) {
+ aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
+ }
return NS_OK;
}
void
-nsSVGAngle::GetBaseValueString(nsAString & aValueAsString)
+nsSVGAngle::GetBaseValueString(nsAString & aValueAsString) const
{
GetValueString(aValueAsString, mBaseVal, mBaseValUnit);
}
void
-nsSVGAngle::GetAnimValueString(nsAString & aValueAsString)
+nsSVGAngle::GetAnimValueString(nsAString & aValueAsString) const
{
GetValueString(aValueAsString, mAnimVal, mAnimValUnit);
}
void
-nsSVGAngle::SetBaseValue(float aValue, nsSVGElement *aSVGElement)
+nsSVGAngle::SetBaseValue(float aValue, nsSVGElement *aSVGElement,
+ bool aDoSetAttr)
{
+ if (mBaseVal == aValue * GetDegreesPerUnit(mBaseValUnit)) {
+ return;
+ }
+ nsAttrValue emptyOrOldValue;
+ if (aSVGElement && aDoSetAttr) {
+ emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
+ }
+
mBaseVal = aValue / GetDegreesPerUnit(mBaseValUnit);
if (!mIsAnimated) {
mAnimVal = mBaseVal;
@@ -381,8 +419,8 @@ nsSVGAngle::SetBaseValue(float aValue, nsSVGElement *aSVGElement)
else {
aSVGElement->AnimationNeedsResample();
}
- if (aSVGElement) {
- aSVGElement->DidChangeAngle(mAttrEnum, true);
+ if (aSVGElement && aDoSetAttr) {
+ aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
}
}
diff --git a/content/svg/content/src/nsSVGAngle.h b/content/svg/content/src/nsSVGAngle.h
index ff518f1834c..10ab5d3e7b0 100644
--- a/content/svg/content/src/nsSVGAngle.h
+++ b/content/svg/content/src/nsSVGAngle.h
@@ -67,15 +67,15 @@ public:
nsresult SetBaseValueString(const nsAString& aValue,
nsSVGElement *aSVGElement,
bool aDoSetAttr);
- void GetBaseValueString(nsAString& aValue);
- void GetAnimValueString(nsAString& aValue);
+ void GetBaseValueString(nsAString& aValue) const;
+ void GetAnimValueString(nsAString& aValue) const;
float GetBaseValue() const
{ return mBaseVal * GetDegreesPerUnit(mBaseValUnit); }
float GetAnimValue() const
{ return mAnimVal * GetDegreesPerUnit(mAnimValUnit); }
- void SetBaseValue(float aValue, nsSVGElement *aSVGElement);
+ void SetBaseValue(float aValue, nsSVGElement *aSVGElement, bool aDoSetAttr);
void SetAnimValue(float aValue, PRUint8 aUnit, nsSVGElement *aSVGElement);
PRUint8 GetBaseValueUnit() const { return mBaseValUnit; }
@@ -125,7 +125,7 @@ public:
NS_IMETHOD GetValue(float* aResult)
{ *aResult = mVal->GetBaseValue(); return NS_OK; }
NS_IMETHOD SetValue(float aValue)
- { mVal->SetBaseValue(aValue, mSVGElement); return NS_OK; }
+ { mVal->SetBaseValue(aValue, mSVGElement, true); return NS_OK; }
NS_IMETHOD GetValueInSpecifiedUnits(float* aResult)
{ *aResult = mVal->mBaseVal; return NS_OK; }
diff --git a/content/svg/content/src/nsSVGBoolean.cpp b/content/svg/content/src/nsSVGBoolean.cpp
index 2b7db25d221..c31fca96e4d 100644
--- a/content/svg/content/src/nsSVGBoolean.cpp
+++ b/content/svg/content/src/nsSVGBoolean.cpp
@@ -70,13 +70,26 @@ GetValueFromString(const nsAString &aValueAsString,
return NS_ERROR_DOM_SYNTAX_ERR;
}
+static nsresult
+GetValueFromAtom(const nsIAtom* aValueAsAtom, bool *aValue)
+{
+ if (aValueAsAtom == nsGkAtoms::_true) {
+ *aValue = true;
+ return NS_OK;
+ }
+ if (aValueAsAtom == nsGkAtoms::_false) {
+ *aValue = false;
+ return NS_OK;
+ }
+ return NS_ERROR_DOM_SYNTAX_ERR;
+}
+
nsresult
-nsSVGBoolean::SetBaseValueString(const nsAString &aValueAsString,
- nsSVGElement *aSVGElement)
+nsSVGBoolean::SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement)
{
bool val;
- nsresult rv = GetValueFromString(aValueAsString, &val);
+ nsresult rv = GetValueFromAtom(aValue, &val);
if (NS_FAILED(rv)) {
return rv;
}
@@ -95,30 +108,28 @@ nsSVGBoolean::SetBaseValueString(const nsAString &aValueAsString,
return NS_OK;
}
-void
-nsSVGBoolean::GetBaseValueString(nsAString & aValueAsString)
+nsIAtom*
+nsSVGBoolean::GetBaseValueAtom() const
{
- aValueAsString.Assign(mBaseVal
- ? NS_LITERAL_STRING("true")
- : NS_LITERAL_STRING("false"));
+ return mBaseVal ? nsGkAtoms::_true : nsGkAtoms::_false;
}
void
-nsSVGBoolean::SetBaseValue(bool aValue,
- nsSVGElement *aSVGElement)
+nsSVGBoolean::SetBaseValue(bool aValue, nsSVGElement *aSVGElement)
{
NS_PRECONDITION(aValue == true || aValue == false, "Boolean out of range");
- if (aValue != mBaseVal) {
- mBaseVal = aValue;
- if (!mIsAnimated) {
- mAnimVal = mBaseVal;
- }
- else {
- aSVGElement->AnimationNeedsResample();
- }
- aSVGElement->DidChangeBoolean(mAttrEnum, true);
+ if (aValue == mBaseVal) {
+ return;
}
+
+ mBaseVal = aValue;
+ if (!mIsAnimated) {
+ mAnimVal = mBaseVal;
+ } else {
+ aSVGElement->AnimationNeedsResample();
+ }
+ aSVGElement->DidChangeBoolean(mAttrEnum);
}
void
diff --git a/content/svg/content/src/nsSVGBoolean.h b/content/svg/content/src/nsSVGBoolean.h
index 4ca77411d0a..f649c37ce47 100644
--- a/content/svg/content/src/nsSVGBoolean.h
+++ b/content/svg/content/src/nsSVGBoolean.h
@@ -58,9 +58,8 @@ public:
mIsAnimated = false;
}
- nsresult SetBaseValueString(const nsAString& aValue,
- nsSVGElement *aSVGElement);
- void GetBaseValueString(nsAString& aValue);
+ nsresult SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement);
+ nsIAtom* GetBaseValueAtom() const;
void SetBaseValue(bool aValue, nsSVGElement *aSVGElement);
bool GetBaseValue() const
diff --git a/content/svg/content/src/nsSVGElement.cpp b/content/svg/content/src/nsSVGElement.cpp
index 61c693aa4dd..2e6ef2b09ab 100644
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -89,6 +89,7 @@
#include
#include "nsSMILMappedAttribute.h"
#include "SVGMotionSMILAttr.h"
+#include "nsAttrValueOrString.h"
using namespace mozilla;
@@ -262,6 +263,15 @@ nsresult
nsSVGElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify)
{
+ // We don't currently use nsMappedAttributes within SVG. If this changes, we
+ // need to be very careful because some nsAttrValues used by SVG point to
+ // member data of SVG elements and if an nsAttrValue outlives the SVG element
+ // whose data it points to (by virtue of being stored in
+ // mAttrsAndChildren->mMappedAttributes, meaning it's shared between
+ // elements), the pointer will dangle. See bug 724680.
+ NS_ABORT_IF_FALSE(!mAttrsAndChildren.HasMappedAttrs(),
+ "Unexpected use of nsMappedAttributes within SVG");
+
// If this is an svg presentation attribute we need to map it into
// the content stylerule.
// XXX For some reason incremental mapping doesn't work, so for now
@@ -290,6 +300,8 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
{
nsresult rv = NS_OK;
bool foundMatch = false;
+ bool didSetResult = false;
+
if (aNamespaceID == kNameSpaceID_None) {
// Check for nsSVGLength2 attribute
@@ -301,6 +313,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
rv = lengthInfo.mLengths[i].SetBaseValueString(aValue, this, false);
if (NS_FAILED(rv)) {
lengthInfo.Reset(i);
+ } else {
+ aResult.SetTo(lengthInfo.mLengths[i], &aValue);
+ didSetResult = true;
}
foundMatch = true;
break;
@@ -315,6 +330,10 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
rv = lengthListInfo.mLengthLists[i].SetBaseValueString(aValue);
if (NS_FAILED(rv)) {
lengthListInfo.Reset(i);
+ } else {
+ aResult.SetTo(lengthListInfo.mLengthLists[i].GetBaseValue(),
+ &aValue);
+ didSetResult = true;
}
foundMatch = true;
break;
@@ -330,6 +349,10 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
rv = numberListInfo.mNumberLists[i].SetBaseValueString(aValue);
if (NS_FAILED(rv)) {
numberListInfo.Reset(i);
+ } else {
+ aResult.SetTo(numberListInfo.mNumberLists[i].GetBaseValue(),
+ &aValue);
+ didSetResult = true;
}
foundMatch = true;
break;
@@ -342,11 +365,12 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
if (GetPointListAttrName() == aAttribute) {
SVGAnimatedPointList* pointList = GetAnimatedPointList();
if (pointList) {
- rv = pointList->SetBaseValueString(aValue);
- if (NS_FAILED(rv)) {
- // The spec says we parse everything up to the failure, so we don't
- // call pointList->ClearBaseValue()
- }
+ pointList->SetBaseValueString(aValue);
+ // The spec says we parse everything up to the failure, so we DON'T
+ // need to check the result of SetBaseValueString or call
+ // pointList->ClearBaseValue() if it fails
+ aResult.SetTo(pointList->GetBaseValue(), &aValue);
+ didSetResult = true;
foundMatch = true;
}
}
@@ -357,11 +381,12 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
if (GetPathDataAttrName() == aAttribute) {
SVGAnimatedPathSegList* segList = GetAnimPathSegList();
if (segList) {
- rv = segList->SetBaseValueString(aValue);
- if (NS_FAILED(rv)) {
- // The spec says we parse everything up to the failure, so we don't
- // call segList->ClearBaseValue()
- }
+ segList->SetBaseValueString(aValue);
+ // The spec says we parse everything up to the failure, so we DON'T
+ // need to check the result of SetBaseValueString or call
+ // segList->ClearBaseValue() if it fails
+ aResult.SetTo(segList->GetBaseValue(), &aValue);
+ didSetResult = true;
foundMatch = true;
}
}
@@ -375,6 +400,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
rv = numberInfo.mNumbers[i].SetBaseValueString(aValue, this);
if (NS_FAILED(rv)) {
numberInfo.Reset(i);
+ } else {
+ aResult.SetTo(numberInfo.mNumbers[i].GetBaseValue(), &aValue);
+ didSetResult = true;
}
foundMatch = true;
break;
@@ -390,6 +418,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
rv = numberPairInfo.mNumberPairs[i].SetBaseValueString(aValue, this);
if (NS_FAILED(rv)) {
numberPairInfo.Reset(i);
+ } else {
+ aResult.SetTo(numberPairInfo.mNumberPairs[i], &aValue);
+ didSetResult = true;
}
foundMatch = true;
break;
@@ -405,6 +436,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
rv = integerInfo.mIntegers[i].SetBaseValueString(aValue, this);
if (NS_FAILED(rv)) {
integerInfo.Reset(i);
+ } else {
+ aResult.SetTo(integerInfo.mIntegers[i].GetBaseValue(), &aValue);
+ didSetResult = true;
}
foundMatch = true;
break;
@@ -417,9 +451,13 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo();
for (i = 0; i < integerPairInfo.mIntegerPairCount; i++) {
if (aAttribute == *integerPairInfo.mIntegerPairInfo[i].mName) {
- rv = integerPairInfo.mIntegerPairs[i].SetBaseValueString(aValue, this);
+ rv =
+ integerPairInfo.mIntegerPairs[i].SetBaseValueString(aValue, this);
if (NS_FAILED(rv)) {
integerPairInfo.Reset(i);
+ } else {
+ aResult.SetTo(integerPairInfo.mIntegerPairs[i], &aValue);
+ didSetResult = true;
}
foundMatch = true;
break;
@@ -435,6 +473,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
rv = angleInfo.mAngles[i].SetBaseValueString(aValue, this, false);
if (NS_FAILED(rv)) {
angleInfo.Reset(i);
+ } else {
+ aResult.SetTo(angleInfo.mAngles[i], &aValue);
+ didSetResult = true;
}
foundMatch = true;
break;
@@ -447,9 +488,13 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
BooleanAttributesInfo booleanInfo = GetBooleanInfo();
for (i = 0; i < booleanInfo.mBooleanCount; i++) {
if (aAttribute == *booleanInfo.mBooleanInfo[i].mName) {
- rv = booleanInfo.mBooleans[i].SetBaseValueString(aValue, this);
+ nsCOMPtr valAtom = do_GetAtom(aValue);
+ rv = booleanInfo.mBooleans[i].SetBaseValueAtom(valAtom, this);
if (NS_FAILED(rv)) {
booleanInfo.Reset(i);
+ } else {
+ aResult.SetTo(valAtom);
+ didSetResult = true;
}
foundMatch = true;
break;
@@ -462,9 +507,13 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
EnumAttributesInfo enumInfo = GetEnumInfo();
for (i = 0; i < enumInfo.mEnumCount; i++) {
if (aAttribute == *enumInfo.mEnumInfo[i].mName) {
- rv = enumInfo.mEnums[i].SetBaseValueString(aValue, this);
+ nsCOMPtr valAtom = do_GetAtom(aValue);
+ rv = enumInfo.mEnums[i].SetBaseValueAtom(valAtom, this);
if (NS_FAILED(rv)) {
enumInfo.Reset(i);
+ } else {
+ aResult.SetTo(valAtom);
+ didSetResult = true;
}
foundMatch = true;
break;
@@ -486,9 +535,12 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
StringListAttributesInfo stringListInfo = GetStringListInfo();
for (i = 0; i < stringListInfo.mStringListCount; i++) {
if (aAttribute == *stringListInfo.mStringListInfo[i].mName) {
- rv = stringListInfo.mStringLists[i].SetValue(aValue, false);
+ rv = stringListInfo.mStringLists[i].SetValue(aValue);
if (NS_FAILED(rv)) {
stringListInfo.Reset(i);
+ } else {
+ aResult.SetTo(stringListInfo.mStringLists[i], &aValue);
+ didSetResult = true;
}
foundMatch = true;
break;
@@ -504,6 +556,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
rv = viewBox->SetBaseValueString(aValue, this);
if (NS_FAILED(rv)) {
viewBox->Init();
+ } else {
+ aResult.SetTo(*viewBox, &aValue);
+ didSetResult = true;
}
foundMatch = true;
}
@@ -515,6 +570,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
rv = preserveAspectRatio->SetBaseValueString(aValue, this);
if (NS_FAILED(rv)) {
preserveAspectRatio->Init();
+ } else {
+ aResult.SetTo(*preserveAspectRatio, &aValue);
+ didSetResult = true;
}
foundMatch = true;
}
@@ -525,6 +583,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
rv = transformList->SetBaseValueString(aValue);
if (NS_FAILED(rv)) {
transformList->ClearBaseValue();
+ } else {
+ aResult.SetTo(transformList->GetBaseValue(), &aValue);
+ didSetResult = true;
}
foundMatch = true;
}
@@ -550,7 +611,9 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue);
return false;
}
- aResult.SetTo(aValue);
+ if (!didSetResult) {
+ aResult.SetTo(aValue);
+ }
return true;
}
@@ -584,8 +647,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
for (PRUint32 i = 0; i < lenInfo.mLengthCount; i++) {
if (aName == *lenInfo.mLengthInfo[i].mName) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
lenInfo.Reset(i);
- DidChangeLength(i, false);
return;
}
}
@@ -595,8 +658,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
for (PRUint32 i = 0; i < lengthListInfo.mLengthListCount; i++) {
if (aName == *lengthListInfo.mLengthListInfo[i].mName) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
lengthListInfo.Reset(i);
- DidChangeLengthList(i, false);
return;
}
}
@@ -606,8 +669,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
for (PRUint32 i = 0; i < numberListInfo.mNumberListCount; i++) {
if (aName == *numberListInfo.mNumberListInfo[i].mName) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
numberListInfo.Reset(i);
- DidChangeNumberList(i, false);
return;
}
}
@@ -616,6 +679,7 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
if (GetPointListAttrName() == aName) {
SVGAnimatedPointList *pointList = GetAnimatedPointList();
if (pointList) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
pointList->ClearBaseValue();
return;
}
@@ -625,8 +689,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
if (GetPathDataAttrName() == aName) {
SVGAnimatedPathSegList *segList = GetAnimPathSegList();
if (segList) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
segList->ClearBaseValue();
- DidChangePathSegList(false);
return;
}
}
@@ -637,7 +701,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
for (PRUint32 i = 0; i < numInfo.mNumberCount; i++) {
if (aName == *numInfo.mNumberInfo[i].mName) {
numInfo.Reset(i);
- DidChangeNumber(i, false);
return;
}
}
@@ -647,8 +710,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
for (PRUint32 i = 0; i < numPairInfo.mNumberPairCount; i++) {
if (aName == *numPairInfo.mNumberPairInfo[i].mName) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
numPairInfo.Reset(i);
- DidChangeNumberPair(i, false);
return;
}
}
@@ -659,7 +722,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
for (PRUint32 i = 0; i < intInfo.mIntegerCount; i++) {
if (aName == *intInfo.mIntegerInfo[i].mName) {
intInfo.Reset(i);
- DidChangeInteger(i, false);
return;
}
}
@@ -669,8 +731,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
for (PRUint32 i = 0; i < intPairInfo.mIntegerPairCount; i++) {
if (aName == *intPairInfo.mIntegerPairInfo[i].mName) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
intPairInfo.Reset(i);
- DidChangeIntegerPair(i, false);
return;
}
}
@@ -680,8 +742,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
for (PRUint32 i = 0; i < angleInfo.mAngleCount; i++) {
if (aName == *angleInfo.mAngleInfo[i].mName) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
angleInfo.Reset(i);
- DidChangeAngle(i, false);
return;
}
}
@@ -692,7 +754,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
for (PRUint32 i = 0; i < boolInfo.mBooleanCount; i++) {
if (aName == *boolInfo.mBooleanInfo[i].mName) {
boolInfo.Reset(i);
- DidChangeBoolean(i, false);
return;
}
}
@@ -703,7 +764,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
for (PRUint32 i = 0; i < enumInfo.mEnumCount; i++) {
if (aName == *enumInfo.mEnumInfo[i].mName) {
enumInfo.Reset(i);
- DidChangeEnum(i, false);
return;
}
}
@@ -712,8 +772,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
if (aName == nsGkAtoms::viewBox) {
nsSVGViewBox* viewBox = GetViewBox();
if (viewBox) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
viewBox->Init();
- DidChangeViewBox(false);
return;
}
}
@@ -722,10 +782,9 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
if (aName == nsGkAtoms::preserveAspectRatio) {
SVGAnimatedPreserveAspectRatio *preserveAspectRatio =
GetPreserveAspectRatio();
-
if (preserveAspectRatio) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
preserveAspectRatio->Init();
- DidChangePreserveAspectRatio(false);
return;
}
}
@@ -734,8 +793,8 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
if (GetTransformListAttrName() == aName) {
SVGAnimatedTransformList *transformList = GetAnimatedTransformList();
if (transformList) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
transformList->ClearBaseValue();
- DidChangeTransformList(false);
return;
}
}
@@ -743,6 +802,7 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
// Check for conditional processing attributes
nsCOMPtr tests(do_QueryInterface(this));
if (tests && tests->IsConditionalProcessingAttribute(aName)) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
tests->UnsetAttr(aName);
return;
}
@@ -752,6 +812,7 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
for (PRUint32 i = 0; i < stringListInfo.mStringListCount; i++) {
if (aName == *stringListInfo.mStringListInfo[i].mName) {
+ MaybeSerializeAttrBeforeRemoval(aName, aNotify);
stringListInfo.Reset(i);
return;
}
@@ -765,7 +826,6 @@ nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID &&
aName == *stringInfo.mStringInfo[i].mName) {
stringInfo.Reset(i);
- DidChangeString(i);
return;
}
}
@@ -1247,6 +1307,137 @@ nsSVGElement::GetAnimatedContentStyleRule()
nsnull));
}
+/**
+ * Helper methods for the type-specific WillChangeXXX methods.
+ *
+ * This method sends out appropriate pre-change notifications so that selector
+ * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
+ * matching) work, and it returns an nsAttrValue that _may_ contain the
+ * attribute's pre-change value.
+ *
+ * The nsAttrValue returned by this method depends on whether there are
+ * mutation event listeners listening for changes to this element's attributes.
+ * If not, then the object returned is empty. If there are, then the
+ * nsAttrValue returned contains a serialized copy of the attribute's value
+ * prior to the change, and this object should be passed to the corresponding
+ * DidChangeXXX method call (assuming a WillChangeXXX call is required for the
+ * SVG type - see comment below). This is necessary so that the 'prevValue'
+ * property of the mutation event that is dispatched will correctly contain the
+ * old value.
+ *
+ * The reason we need to serialize the old value if there are mutation
+ * event listeners is because the underlying nsAttrValue for the attribute
+ * points directly to a parsed representation of the attribute (e.g. an
+ * SVGAnimatedLengthList*) that is a member of the SVG element. That object
+ * will have changed by the time DidChangeXXX has been called, so without the
+ * serialization of the old attribute value that we provide, DidChangeXXX
+ * would have no way to get the old value to pass to SetAttrAndNotify.
+ *
+ * We only return the old value when there are mutation event listeners because
+ * it's not needed otherwise, and because it's expensive to serialize the old
+ * value. This is especially true for list type attributes, which may be built
+ * up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
+ * before the script finally finishes setting the attribute.
+ *
+ * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
+ * and filter out redundant changes. Before calling WillChangeXXX, the caller
+ * should check whether the new and old values are actually the same, and skip
+ * calling Will/DidChangeXXX if they are.
+ *
+ * Also note that not all SVG types use this scheme. For types that can be
+ * represented by an nsAttrValue without pointing back to an SVG object (e.g.
+ * enums, booleans, integers) we can simply use SetParsedAttr which will do all
+ * of the above for us. For such types there is no matching WillChangeXXX
+ * method, only DidChangeXXX which calls SetParsedAttr.
+ */
+nsAttrValue
+nsSVGElement::WillChangeValue(nsIAtom* aName)
+{
+ // We need an empty attr value:
+ // a) to pass to BeforeSetAttr when GetParsedAttr returns nsnull
+ // b) to store the old value in the case we have mutation listeners
+ // We can use the same value for both purposes since (a) happens before (b).
+ // Also, we should be careful to always return this value to benefit from
+ // return value optimization.
+ nsAttrValue emptyOrOldAttrValue;
+ const nsAttrValue* attrValue = GetParsedAttr(aName);
+
+ // This is not strictly correct--the attribute value parameter for
+ // BeforeSetAttr should reflect the value that *will* be set but that implies
+ // allocating, e.g. an extra nsSVGLength2, and isn't necessary at the moment
+ // since no SVG elements overload BeforeSetAttr. For now we just pass the
+ // current value.
+ nsAttrValueOrString attrStringOrValue(attrValue ? *attrValue
+ : emptyOrOldAttrValue);
+ DebugOnly rv =
+ BeforeSetAttr(kNameSpaceID_None, aName, &attrStringOrValue,
+ kNotifyDocumentObservers);
+ // SVG elements aren't expected to overload BeforeSetAttr in such a way that
+ // it may fail. So long as this is the case we don't need to check and pass on
+ // the return value which simplifies the calling code significantly.
+ NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Unexpected failure from BeforeSetAttr");
+
+ // We only need to set the old value if we have listeners since otherwise it
+ // isn't used.
+ if (attrValue &&
+ nsContentUtils::HasMutationListeners(this,
+ NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
+ this)) {
+ emptyOrOldAttrValue.SetToSerialized(*attrValue);
+ }
+
+ PRUint8 modType = attrValue
+ ? static_cast(nsIDOMMutationEvent::MODIFICATION)
+ : static_cast(nsIDOMMutationEvent::ADDITION);
+ nsNodeUtils::AttributeWillChange(this, kNameSpaceID_None, aName, modType);
+
+ return emptyOrOldAttrValue;
+}
+
+/**
+ * Helper methods for the type-specific DidChangeXXX methods.
+ *
+ * aEmptyOrOldValue will normally be the object returned from the corresponding
+ * WillChangeXXX call. This is because:
+ * a) WillChangeXXX will ensure the object is set when we have mutation
+ * listeners, and
+ * b) WillChangeXXX will ensure the object represents a serialized version of
+ * the old attribute value so that the value doesn't change when the
+ * underlying SVG type is updated.
+ */
+void
+nsSVGElement::DidChangeValue(nsIAtom* aName,
+ const nsAttrValue& aEmptyOrOldValue,
+ nsAttrValue& aNewValue)
+{
+ bool hasListeners =
+ nsContentUtils::HasMutationListeners(this,
+ NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
+ this);
+ PRUint8 modType = HasAttr(kNameSpaceID_None, aName)
+ ? static_cast(nsIDOMMutationEvent::MODIFICATION)
+ : static_cast(nsIDOMMutationEvent::ADDITION);
+ SetAttrAndNotify(kNameSpaceID_None, aName, nsnull, aEmptyOrOldValue,
+ aNewValue, modType, hasListeners, kNotifyDocumentObservers,
+ kCallAfterSetAttr);
+}
+
+void
+nsSVGElement::MaybeSerializeAttrBeforeRemoval(nsIAtom* aName, bool aNotify)
+{
+ if (!aNotify ||
+ !nsContentUtils::HasMutationListeners(this,
+ NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
+ this)) {
+ return;
+ }
+
+ nsAutoString serializedValue;
+ mAttrsAndChildren.GetAttr(aName)->ToString(serializedValue);
+ nsAttrValue attrValue(serializedValue);
+ mAttrsAndChildren.SetAndTakeAttr(aName, attrValue);
+}
+
/* static */
nsIAtom* nsSVGElement::GetEventNameForAttr(nsIAtom* aAttr)
{
@@ -1295,7 +1486,8 @@ nsSVGElement::GetCtx() const
}
/* virtual */ gfxMatrix
-nsSVGElement::PrependLocalTransformTo(const gfxMatrix &aMatrix) const
+nsSVGElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix,
+ TransformTypes aWhich) const
{
return aMatrix;
}
@@ -1329,25 +1521,27 @@ nsSVGElement::SetLength(nsIAtom* aName, const nsSVGLength2 &aLength)
NS_ABORT_IF_FALSE(false, "no length found to set");
}
-void
-nsSVGElement::DidChangeLength(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeLength(PRUint8 aAttrEnum)
{
- if (!aDoSetAttr)
- return;
+ return WillChangeValue(*GetLengthInfo().mLengthInfo[aAttrEnum].mName);
+}
+void
+nsSVGElement::DidChangeLength(PRUint8 aAttrEnum,
+ const nsAttrValue& aEmptyOrOldValue)
+{
LengthAttributesInfo info = GetLengthInfo();
NS_ASSERTION(info.mLengthCount > 0,
"DidChangeLength on element with no length attribs");
-
NS_ASSERTION(aAttrEnum < info.mLengthCount, "aAttrEnum out of range");
- nsAutoString serializedValue;
- info.mLengths[aAttrEnum].GetBaseValueString(serializedValue);
+ nsAttrValue newValue;
+ newValue.SetTo(info.mLengths[aAttrEnum], nsnull);
- nsAttrValue attrValue(serializedValue);
- SetParsedAttr(kNameSpaceID_None, *info.mLengthInfo[aAttrEnum].mName, nsnull,
- attrValue, true);
+ DidChangeValue(*info.mLengthInfo[aAttrEnum].mName, aEmptyOrOldValue,
+ newValue);
}
void
@@ -1424,24 +1618,27 @@ nsSVGElement::LengthListAttributesInfo::Reset(PRUint8 aAttrEnum)
// caller notifies
}
-void
-nsSVGElement::DidChangeLengthList(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeLengthList(PRUint8 aAttrEnum)
{
- if (!aDoSetAttr)
- return;
+ return WillChangeValue(*GetLengthListInfo().mLengthListInfo[aAttrEnum].mName);
+}
+void
+nsSVGElement::DidChangeLengthList(PRUint8 aAttrEnum,
+ const nsAttrValue& aEmptyOrOldValue)
+{
LengthListAttributesInfo info = GetLengthListInfo();
NS_ASSERTION(info.mLengthListCount > 0,
"DidChangeLengthList on element with no length list attribs");
NS_ASSERTION(aAttrEnum < info.mLengthListCount, "aAttrEnum out of range");
- nsAutoString serializedValue;
- info.mLengthLists[aAttrEnum].GetBaseValue().GetValueAsString(serializedValue);
+ nsAttrValue newValue;
+ newValue.SetTo(info.mLengthLists[aAttrEnum].GetBaseValue(), nsnull);
- nsAttrValue attrValue(serializedValue);
- SetParsedAttr(kNameSpaceID_None, *info.mLengthListInfo[aAttrEnum].mName,
- nsnull, attrValue, true);
+ DidChangeValue(*info.mLengthListInfo[aAttrEnum].mName, aEmptyOrOldValue,
+ newValue);
}
void
@@ -1506,24 +1703,28 @@ nsSVGElement::NumberListAttributesInfo::Reset(PRUint8 aAttrEnum)
// caller notifies
}
-void
-nsSVGElement::DidChangeNumberList(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeNumberList(PRUint8 aAttrEnum)
{
- if (!aDoSetAttr)
- return;
+ return WillChangeValue(*GetNumberListInfo().mNumberListInfo[aAttrEnum].mName);
+}
+void
+nsSVGElement::DidChangeNumberList(PRUint8 aAttrEnum,
+ const nsAttrValue& aEmptyOrOldValue)
+{
NumberListAttributesInfo info = GetNumberListInfo();
NS_ABORT_IF_FALSE(info.mNumberListCount > 0,
- "DidChangeNumberList on element with no number list attribs");
- NS_ABORT_IF_FALSE(aAttrEnum < info.mNumberListCount, "aAttrEnum out of range");
+ "DidChangeNumberList on element with no number list attribs");
+ NS_ABORT_IF_FALSE(aAttrEnum < info.mNumberListCount,
+ "aAttrEnum out of range");
- nsAutoString serializedValue;
- info.mNumberLists[aAttrEnum].GetBaseValue().GetValueAsString(serializedValue);
+ nsAttrValue newValue;
+ newValue.SetTo(info.mNumberLists[aAttrEnum].GetBaseValue(), nsnull);
- nsAttrValue attrValue(serializedValue);
- SetParsedAttr(kNameSpaceID_None, *info.mNumberListInfo[aAttrEnum].mName,
- nsnull, attrValue, true);
+ DidChangeValue(*info.mNumberListInfo[aAttrEnum].mName, aEmptyOrOldValue,
+ newValue);
}
void
@@ -1565,20 +1766,24 @@ nsSVGElement::GetAnimatedNumberList(nsIAtom *aAttrName)
return nsnull;
}
-void
-nsSVGElement::DidChangePointList(bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangePointList()
{
- NS_ABORT_IF_FALSE(GetPointListAttrName(), "Changing non-existent point list?");
+ NS_ABORT_IF_FALSE(GetPointListAttrName(),
+ "Changing non-existent point list?");
+ return WillChangeValue(GetPointListAttrName());
+}
- if (!aDoSetAttr)
- return;
+void
+nsSVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue)
+{
+ NS_ABORT_IF_FALSE(GetPointListAttrName(),
+ "Changing non-existent point list?");
- nsAutoString serializedValue;
- GetAnimatedPointList()->GetBaseValue().GetValueAsString(serializedValue);
+ nsAttrValue newValue;
+ newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nsnull);
- nsAttrValue attrValue(serializedValue);
- SetParsedAttr(kNameSpaceID_None, GetPointListAttrName(), nsnull,
- attrValue, true);
+ DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue);
}
void
@@ -1596,18 +1801,24 @@ nsSVGElement::DidAnimatePointList()
}
}
-void
-nsSVGElement::DidChangePathSegList(bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangePathSegList()
{
- if (!aDoSetAttr)
- return;
+ NS_ABORT_IF_FALSE(GetPathDataAttrName(),
+ "Changing non-existent path seg list?");
+ return WillChangeValue(GetPathDataAttrName());
+}
- nsAutoString serializedValue;
- GetAnimPathSegList()->GetBaseValue().GetValueAsString(serializedValue);
+void
+nsSVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue)
+{
+ NS_ABORT_IF_FALSE(GetPathDataAttrName(),
+ "Changing non-existent path seg list?");
- nsAttrValue attrValue(serializedValue);
- SetParsedAttr(kNameSpaceID_None, GetPathDataAttrName(), nsnull,
- attrValue, true);
+ nsAttrValue newValue;
+ newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nsnull);
+
+ DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue, newValue);
}
void
@@ -1638,22 +1849,17 @@ void nsSVGElement::NumberAttributesInfo::Reset(PRUint8 aAttrEnum)
}
void
-nsSVGElement::DidChangeNumber(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsSVGElement::DidChangeNumber(PRUint8 aAttrEnum)
{
- if (!aDoSetAttr)
- return;
-
NumberAttributesInfo info = GetNumberInfo();
NS_ASSERTION(info.mNumberCount > 0,
"DidChangeNumber on element with no number attribs");
-
NS_ASSERTION(aAttrEnum < info.mNumberCount, "aAttrEnum out of range");
- nsAutoString serializedValue;
- info.mNumbers[aAttrEnum].GetBaseValueString(serializedValue);
+ nsAttrValue attrValue;
+ attrValue.SetTo(info.mNumbers[aAttrEnum].GetBaseValue(), nsnull);
- nsAttrValue attrValue(serializedValue);
SetParsedAttr(kNameSpaceID_None, *info.mNumberInfo[aAttrEnum].mName, nsnull,
attrValue, true);
}
@@ -1705,25 +1911,27 @@ void nsSVGElement::NumberPairAttributesInfo::Reset(PRUint8 aAttrEnum)
mNumberPairInfo[aAttrEnum].mDefaultValue2);
}
-void
-nsSVGElement::DidChangeNumberPair(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeNumberPair(PRUint8 aAttrEnum)
{
- if (!aDoSetAttr)
- return;
+ return WillChangeValue(*GetNumberPairInfo().mNumberPairInfo[aAttrEnum].mName);
+}
+void
+nsSVGElement::DidChangeNumberPair(PRUint8 aAttrEnum,
+ const nsAttrValue& aEmptyOrOldValue)
+{
NumberPairAttributesInfo info = GetNumberPairInfo();
NS_ASSERTION(info.mNumberPairCount > 0,
"DidChangePairNumber on element with no number pair attribs");
-
NS_ASSERTION(aAttrEnum < info.mNumberPairCount, "aAttrEnum out of range");
- nsAutoString serializedValue;
- info.mNumberPairs[aAttrEnum].GetBaseValueString(serializedValue);
+ nsAttrValue newValue;
+ newValue.SetTo(info.mNumberPairs[aAttrEnum], nsnull);
- nsAttrValue attrValue(serializedValue);
- SetParsedAttr(kNameSpaceID_None, *info.mNumberPairInfo[aAttrEnum].mName, nsnull,
- attrValue, true);
+ DidChangeValue(*info.mNumberPairInfo[aAttrEnum].mName, aEmptyOrOldValue,
+ newValue);
}
void
@@ -1752,22 +1960,17 @@ void nsSVGElement::IntegerAttributesInfo::Reset(PRUint8 aAttrEnum)
}
void
-nsSVGElement::DidChangeInteger(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsSVGElement::DidChangeInteger(PRUint8 aAttrEnum)
{
- if (!aDoSetAttr)
- return;
-
IntegerAttributesInfo info = GetIntegerInfo();
NS_ASSERTION(info.mIntegerCount > 0,
"DidChangeInteger on element with no integer attribs");
-
NS_ASSERTION(aAttrEnum < info.mIntegerCount, "aAttrEnum out of range");
- nsAutoString serializedValue;
- info.mIntegers[aAttrEnum].GetBaseValueString(serializedValue);
+ nsAttrValue attrValue;
+ attrValue.SetTo(info.mIntegers[aAttrEnum].GetBaseValue(), nsnull);
- nsAttrValue attrValue(serializedValue);
SetParsedAttr(kNameSpaceID_None, *info.mIntegerInfo[aAttrEnum].mName, nsnull,
attrValue, true);
}
@@ -1819,25 +2022,28 @@ void nsSVGElement::IntegerPairAttributesInfo::Reset(PRUint8 aAttrEnum)
mIntegerPairInfo[aAttrEnum].mDefaultValue2);
}
-void
-nsSVGElement::DidChangeIntegerPair(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeIntegerPair(PRUint8 aAttrEnum)
{
- if (!aDoSetAttr)
- return;
+ return WillChangeValue(
+ *GetIntegerPairInfo().mIntegerPairInfo[aAttrEnum].mName);
+}
+void
+nsSVGElement::DidChangeIntegerPair(PRUint8 aAttrEnum,
+ const nsAttrValue& aEmptyOrOldValue)
+{
IntegerPairAttributesInfo info = GetIntegerPairInfo();
NS_ASSERTION(info.mIntegerPairCount > 0,
"DidChangeIntegerPair on element with no integer pair attribs");
-
NS_ASSERTION(aAttrEnum < info.mIntegerPairCount, "aAttrEnum out of range");
- nsAutoString serializedValue;
- info.mIntegerPairs[aAttrEnum].GetBaseValueString(serializedValue);
+ nsAttrValue newValue;
+ newValue.SetTo(info.mIntegerPairs[aAttrEnum], nsnull);
- nsAttrValue attrValue(serializedValue);
- SetParsedAttr(kNameSpaceID_None, *info.mIntegerPairInfo[aAttrEnum].mName, nsnull,
- attrValue, true);
+ DidChangeValue(*info.mIntegerPairInfo[aAttrEnum].mName, aEmptyOrOldValue,
+ newValue);
}
void
@@ -1866,25 +2072,26 @@ void nsSVGElement::AngleAttributesInfo::Reset(PRUint8 aAttrEnum)
mAngleInfo[aAttrEnum].mDefaultUnitType);
}
-void
-nsSVGElement::DidChangeAngle(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeAngle(PRUint8 aAttrEnum)
{
- if (!aDoSetAttr)
- return;
+ return WillChangeValue(*GetAngleInfo().mAngleInfo[aAttrEnum].mName);
+}
+void
+nsSVGElement::DidChangeAngle(PRUint8 aAttrEnum,
+ const nsAttrValue& aEmptyOrOldValue)
+{
AngleAttributesInfo info = GetAngleInfo();
NS_ASSERTION(info.mAngleCount > 0,
"DidChangeAngle on element with no angle attribs");
-
NS_ASSERTION(aAttrEnum < info.mAngleCount, "aAttrEnum out of range");
- nsAutoString serializedValue;
- info.mAngles[aAttrEnum].GetBaseValueString(serializedValue);
+ nsAttrValue newValue;
+ newValue.SetTo(info.mAngles[aAttrEnum], nsnull);
- nsAttrValue attrValue(serializedValue);
- SetParsedAttr(kNameSpaceID_None, *info.mAngleInfo[aAttrEnum].mName, nsnull,
- attrValue, true);
+ DidChangeValue(*info.mAngleInfo[aAttrEnum].mName, aEmptyOrOldValue, newValue);
}
void
@@ -1913,22 +2120,15 @@ void nsSVGElement::BooleanAttributesInfo::Reset(PRUint8 aAttrEnum)
}
void
-nsSVGElement::DidChangeBoolean(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsSVGElement::DidChangeBoolean(PRUint8 aAttrEnum)
{
- if (!aDoSetAttr)
- return;
-
BooleanAttributesInfo info = GetBooleanInfo();
NS_ASSERTION(info.mBooleanCount > 0,
"DidChangeBoolean on element with no boolean attribs");
-
NS_ASSERTION(aAttrEnum < info.mBooleanCount, "aAttrEnum out of range");
- nsAutoString serializedValue;
- info.mBooleans[aAttrEnum].GetBaseValueString(serializedValue);
-
- nsAttrValue attrValue(serializedValue);
+ nsAttrValue attrValue(info.mBooleans[aAttrEnum].GetBaseValueAtom());
SetParsedAttr(kNameSpaceID_None, *info.mBooleanInfo[aAttrEnum].mName, nsnull,
attrValue, true);
}
@@ -1959,22 +2159,15 @@ void nsSVGElement::EnumAttributesInfo::Reset(PRUint8 aAttrEnum)
}
void
-nsSVGElement::DidChangeEnum(PRUint8 aAttrEnum, bool aDoSetAttr)
+nsSVGElement::DidChangeEnum(PRUint8 aAttrEnum)
{
- if (!aDoSetAttr)
- return;
-
EnumAttributesInfo info = GetEnumInfo();
NS_ASSERTION(info.mEnumCount > 0,
"DidChangeEnum on element with no enum attribs");
-
NS_ASSERTION(aAttrEnum < info.mEnumCount, "aAttrEnum out of range");
- nsAutoString serializedValue;
- info.mEnums[aAttrEnum].GetBaseValueString(serializedValue, this);
-
- nsAttrValue attrValue(serializedValue);
+ nsAttrValue attrValue(info.mEnums[aAttrEnum].GetBaseValueAtom(this));
SetParsedAttr(kNameSpaceID_None, *info.mEnumInfo[aAttrEnum].mName, nsnull,
attrValue, true);
}
@@ -1998,22 +2191,23 @@ nsSVGElement::GetViewBox()
return nsnull;
}
-void
-nsSVGElement::DidChangeViewBox(bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeViewBox()
{
- if (!aDoSetAttr)
- return;
+ return WillChangeValue(nsGkAtoms::viewBox);
+}
+void
+nsSVGElement::DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue)
+{
nsSVGViewBox *viewBox = GetViewBox();
NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib");
- nsAutoString serializedValue;
- viewBox->GetBaseValueString(serializedValue);
+ nsAttrValue newValue;
+ newValue.SetTo(*viewBox, nsnull);
- nsAttrValue attrValue(serializedValue);
- SetParsedAttr(kNameSpaceID_None, nsGkAtoms::viewBox, nsnull,
- attrValue, true);
+ DidChangeValue(nsGkAtoms::viewBox, aEmptyOrOldValue, newValue);
}
void
@@ -2034,24 +2228,26 @@ nsSVGElement::GetPreserveAspectRatio()
return nsnull;
}
-void
-nsSVGElement::DidChangePreserveAspectRatio(bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangePreserveAspectRatio()
{
- if (!aDoSetAttr)
- return;
+ return WillChangeValue(nsGkAtoms::preserveAspectRatio);
+}
+void
+nsSVGElement::DidChangePreserveAspectRatio(const nsAttrValue& aEmptyOrOldValue)
+{
SVGAnimatedPreserveAspectRatio *preserveAspectRatio =
GetPreserveAspectRatio();
NS_ASSERTION(preserveAspectRatio,
- "DidChangePreserveAspectRatio on element with no preserveAspectRatio attrib");
+ "DidChangePreserveAspectRatio on element with no "
+ "preserveAspectRatio attrib");
- nsAutoString serializedValue;
- preserveAspectRatio->GetBaseValueString(serializedValue);
+ nsAttrValue newValue;
+ newValue.SetTo(*preserveAspectRatio, nsnull);
- nsAttrValue attrValue(serializedValue);
- SetParsedAttr(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio, nsnull,
- attrValue, true);
+ DidChangeValue(nsGkAtoms::preserveAspectRatio, aEmptyOrOldValue, newValue);
}
void
@@ -2066,22 +2262,22 @@ nsSVGElement::DidAnimatePreserveAspectRatio()
}
}
-void
-nsSVGElement::DidChangeTransformList(bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangeTransformList()
{
- if (!aDoSetAttr)
- return;
+ return WillChangeValue(GetTransformListAttrName());
+}
- SVGAnimatedTransformList* transformList = GetAnimatedTransformList();
- NS_ABORT_IF_FALSE(transformList,
- "DidChangeTransformList on element with no transform list");
+void
+nsSVGElement::DidChangeTransformList(const nsAttrValue& aEmptyOrOldValue)
+{
+ NS_ABORT_IF_FALSE(GetTransformListAttrName(),
+ "Changing non-existent transform list?");
- nsAutoString serializedValue;
- transformList->GetBaseValue().GetValueAsString(serializedValue);
+ nsAttrValue newValue;
+ newValue.SetTo(GetAnimatedTransformList()->GetBaseValue(), nsnull);
- nsAttrValue attrValue(serializedValue);
- SetParsedAttr(kNameSpaceID_None, GetTransformListAttrName(), nsnull,
- attrValue, true);
+ DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue, newValue);
}
void
@@ -2155,29 +2351,49 @@ nsSVGElement::GetStringListInfo()
return StringListAttributesInfo(nsnull, nsnull, 0);
}
-void
-nsSVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
- PRUint8 aAttrEnum)
+nsAttrValue
+nsSVGElement::WillChangeStringList(bool aIsConditionalProcessingAttribute,
+ PRUint8 aAttrEnum)
{
+ nsIAtom* name;
if (aIsConditionalProcessingAttribute) {
nsCOMPtr tests(do_QueryInterface(this));
- tests->DidChangeStringList(aAttrEnum);
- return;
+ name = tests->GetAttrName(aAttrEnum);
+ } else {
+ name = *GetStringListInfo().mStringListInfo[aAttrEnum].mName;
+ }
+ return WillChangeValue(name);
+}
+
+void
+nsSVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
+ PRUint8 aAttrEnum,
+ const nsAttrValue& aEmptyOrOldValue)
+{
+ nsIAtom* name;
+ nsAttrValue newValue;
+ nsCOMPtr tests;
+
+ if (aIsConditionalProcessingAttribute) {
+ tests = do_QueryInterface(this);
+ name = tests->GetAttrName(aAttrEnum);
+ tests->GetAttrValue(aAttrEnum, newValue);
+ } else {
+ StringListAttributesInfo info = GetStringListInfo();
+
+ NS_ASSERTION(info.mStringListCount > 0,
+ "DidChangeStringList on element with no string list attribs");
+ NS_ASSERTION(aAttrEnum < info.mStringListCount, "aAttrEnum out of range");
+
+ name = *info.mStringListInfo[aAttrEnum].mName;
+ newValue.SetTo(info.mStringLists[aAttrEnum], nsnull);
}
- StringListAttributesInfo info = GetStringListInfo();
+ DidChangeValue(name, aEmptyOrOldValue, newValue);
- NS_ASSERTION(info.mStringListCount > 0,
- "DidChangeStringList on element with no string list attribs");
-
- NS_ASSERTION(aAttrEnum < info.mStringListCount, "aAttrEnum out of range");
-
- nsAutoString serializedValue;
- info.mStringLists[aAttrEnum].GetValue(serializedValue, this);
-
- nsAttrValue attrValue(serializedValue);
- SetParsedAttr(kNameSpaceID_None, *info.mStringListInfo[aAttrEnum].mName,
- nsnull, attrValue, true);
+ if (aIsConditionalProcessingAttribute) {
+ tests->MaybeInvalidate();
+ }
}
void
diff --git a/content/svg/content/src/nsSVGElement.h b/content/svg/content/src/nsSVGElement.h
index 53c9c4914bf..44a63c87abd 100644
--- a/content/svg/content/src/nsSVGElement.h
+++ b/content/svg/content/src/nsSVGElement.h
@@ -151,11 +151,36 @@ public:
// nsnull for outer