Merge last green PGO from inbound to central

This commit is contained in:
Marco Bonardo 2012-02-16 11:45:18 +01:00
commit b6fdf810ab
191 changed files with 6500 additions and 2699 deletions

View File

@ -1631,6 +1631,9 @@ nsHyperTextAccessible::GetCaretOffset(PRInt32 *aCaretOffset)
NS_ENSURE_ARG_POINTER(aCaretOffset);
*aCaretOffset = -1;
if (IsDefunct())
return NS_ERROR_FAILURE;
// Not focused focusable accessible except document accessible doesn't have
// a caret.
if (!IsDoc() && !FocusMgr()->IsFocused(this) &&

View File

@ -151,6 +151,9 @@
@BINPATH@/components/dom_system_b2g.xpt
#endif
@BINPATH@/components/dom_battery.xpt
#ifdef MOZ_B2G_BT
@BINPATH@/components/dom_bluetooth.xpt
#endif
@BINPATH@/components/dom_canvas.xpt
@BINPATH@/components/dom_core.xpt
@BINPATH@/components/dom_css.xpt

View File

@ -3983,24 +3983,18 @@
<implementation implements="nsIDOMEventListener">
<constructor><![CDATA[
window.addEventListener("findbaropen", this, false);
window.addEventListener("resize", this, false);
]]></constructor>
<destructor><![CDATA[
window.removeEventListener("findbaropen", this, false);
window.removeEventListener("resize", this, false);
MousePosTracker.removeListener(this);
]]></destructor>
<property name="label">
<setter><![CDATA[
if (!this.label) {
if (window.gFindBarInitialized && !window.gFindBar.hidden)
this.setAttribute("mirror", "true");
else
this.removeAttribute("mirror");
}
if (!this.label)
this.removeAttribute("mirror");
this.style.minWidth = this.getAttribute("type") == "status" &&
this.getAttribute("previoustype") == "status"
@ -4048,10 +4042,6 @@
return;
switch (event.type) {
case "findbaropen":
this.setAttribute("mirror", "true");
this._calcMouseTargetRect();
break;
case "resize":
this._calcMouseTargetRect();
break;
@ -4061,7 +4051,7 @@
<method name="_calcMouseTargetRect">
<body><![CDATA[
let alignRight = (window.gFindBarInitialized && !window.gFindBar.hidden);
let alignRight = false;
if (getComputedStyle(document.documentElement).direction == "rtl")
alighRight = !alignRight;

View File

@ -15,24 +15,26 @@ function reallyHandleRequest(request, response) {
// Allow the caller to drive how authentication is processed via the query.
// Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar
var query = request.queryString;
// The extra ? allows the user/pass/realm checks to succeed if the name is
// at the beginning of the query string.
var query = "?" + request.queryString;
var expected_user = "", expected_pass = "", realm = "mochitest";
var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy";
var huge = false, plugin = false;
var huge = false, plugin = false, anonymous = false;
var authHeaderCount = 1;
// user=xxx
match = /user=([^&]*)/.exec(query);
match = /[^_]user=([^&]*)/.exec(query);
if (match)
expected_user = match[1];
// pass=xxx
match = /pass=([^&]*)/.exec(query);
match = /[^_]pass=([^&]*)/.exec(query);
if (match)
expected_pass = match[1];
// realm=xxx
match = /realm=([^&]*)/.exec(query);
match = /[^_]realm=([^&]*)/.exec(query);
if (match)
realm = match[1];
@ -66,6 +68,10 @@ function reallyHandleRequest(request, response) {
if (match)
authHeaderCount = match[1]+0;
// anonymous=1
match = /anonymous=1/.exec(query);
if (match)
anonymous = true;
// Look for an authentication header, if any, in the request.
//
@ -74,8 +80,9 @@ function reallyHandleRequest(request, response) {
// This test only supports Basic auth. The value sent by the client is
// "username:password", obscured with base64 encoding.
var actual_user = "", actual_pass = "", authHeader;
var actual_user = "", actual_pass = "", authHeader, authPresent = false;
if (request.hasHeader("Authorization")) {
authPresent = true;
authHeader = request.getHeader("Authorization");
match = /Basic (.+)/.exec(authHeader);
if (match.length != 2)
@ -115,16 +122,24 @@ function reallyHandleRequest(request, response) {
requestProxyAuth = false;
}
if (requestProxyAuth) {
response.setStatusLine("1.0", 407, "Proxy authentication required");
for (i = 0; i < authHeaderCount; ++i)
response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true);
} else if (requestAuth) {
response.setStatusLine("1.0", 401, "Authentication required");
for (i = 0; i < authHeaderCount; ++i)
response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true);
if (anonymous) {
if (authPresent) {
response.setStatusLine("1.0", 400, "Unexpected authorization header found");
} else {
response.setStatusLine("1.0", 200, "Authorization header not found");
}
} else {
response.setStatusLine("1.0", 200, "OK");
if (requestProxyAuth) {
response.setStatusLine("1.0", 407, "Proxy authentication required");
for (i = 0; i < authHeaderCount; ++i)
response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true);
} else if (requestAuth) {
response.setStatusLine("1.0", 401, "Authentication required");
for (i = 0; i < authHeaderCount; ++i)
response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true);
} else {
response.setStatusLine("1.0", 200, "OK");
}
}
response.setHeader("Content-Type", "application/xhtml+xml", false);

View File

@ -28,14 +28,18 @@ function test() {
executeSoon(function () {
let consoleListener = {
observe: function (m) {
info("m: " + m + "\n");
info("m.message: " + m.message + "\n");
if (m.message.indexOf("NS_ERROR_DOM_BAD_URI") > -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:

View File

@ -150,6 +150,9 @@
@BINPATH@/components/dom_system_b2g.xpt
#endif
@BINPATH@/components/dom_battery.xpt
#ifdef MOZ_B2G_BT
@BINPATH@/components/dom_bluetooth.xpt
#endif
@BINPATH@/components/dom_canvas.xpt
@BINPATH@/components/dom_core.xpt
@BINPATH@/components/dom_css.xpt

View File

@ -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 <code>millis</code> 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 <code>millis</code> 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);
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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<testInfo> mTestList = new LinkedList<testInfo>();
private LinkedList<testInfo> testList = new LinkedList<testInfo>();
// 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?");
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}

View File

@ -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) { }
}

View File

@ -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);
}
}

View File

@ -293,6 +293,7 @@ MOZ_NATIVE_NSPR = @MOZ_NATIVE_NSPR@
MOZ_NATIVE_NSS = @MOZ_NATIVE_NSS@
MOZ_B2G_RIL = @MOZ_B2G_RIL@
MOZ_B2G_BT = @MOZ_B2G_BT@
BUILD_CTYPES = @BUILD_CTYPES@

View File

@ -225,7 +225,14 @@ if test -n "$L10NBASEDIR"; then
if test "$L10NBASEDIR" = "yes" -o "$L10NBASEDIR" = "no"; then
AC_MSG_ERROR([--with-l10n-base must specify a path])
elif test -d "$L10NBASEDIR"; then
L10NBASEDIR=`cd "$L10NBASEDIR" && pwd`
case "$host_os" in
mingw*)
L10NBASEDIR=`cd "$L10NBASEDIR" && pwd -W`
;;
*)
L10NBASEDIR=`cd "$L10NBASEDIR" && pwd`
;;
esac
else
AC_MSG_ERROR([Invalid value --with-l10n-base, $L10NBASEDIR doesn't exist])
fi
@ -4920,6 +4927,7 @@ cairo-gonk)
MOZ_PDF_PRINTING=1
MOZ_B2G_RIL=1
MOZ_TOUCH=1
MOZ_B2G_BT=1
;;
esac
@ -7590,6 +7598,18 @@ if test -n "$MOZ_B2G_RIL"; then
fi
AC_SUBST(MOZ_B2G_RIL)
dnl ========================================================
dnl = Enable Bluetooth Interface for B2G (Gonk usually)
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(b2g-bt,
[ --enable-b2g-bt Set compile flags necessary for compiling Bluetooth API for B2G ],
MOZ_B2G_BT=1,
MOZ_B2G_BT= )
if test -n "$MOZ_B2G_BT"; then
AC_DEFINE(MOZ_B2G_BT)
fi
AC_SUBST(MOZ_B2G_BT)
dnl ========================================================
dnl = Support for demangling undefined symbols
dnl ========================================================

View File

@ -136,6 +136,10 @@ public:
}
PRInt64 SizeOf() const;
bool HasMappedAttrs() const
{
return MappedAttrCount();
}
private:
nsAttrAndChildArray(const nsAttrAndChildArray& aOther) MOZ_DELETE;

View File

@ -54,6 +54,8 @@
namespace css = mozilla::css;
using mozilla::SVGAttrValueWrapper;
#define MISC_STR_PTR(_cont) \
reinterpret_cast<void*>((_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<const nsSVGAngle*>(aValue);
cont->mType = aType;
SetMiscAtomOrString(aSerialized);
}
}
bool
nsAttrValue::EnsureEmptyMiscContainer()
{

View File

@ -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<ValueBaseType>(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)
{

View File

@ -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;

View File

@ -2388,6 +2388,7 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIWEBGLEXTENSION
virtual ~WebGLExtension() {}
};
inline const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const {

View File

@ -42,7 +42,7 @@
#include "nsIConstraintValidation.h"
class nsDOMValidityState : public nsIDOMValidityState
class nsDOMValidityState MOZ_FINAL : public nsIDOMValidityState
{
public:
NS_DECL_ISUPPORTS

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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(); \
} \

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -111,8 +111,6 @@ private:
~DOMSVGStringList();
void DidChangeStringList(PRUint8 aAttrEnum, bool aDoSetAttr);
SVGStringList &InternalList();
// Strong ref to our element to keep it alive.

View File

@ -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<nsSVGElement> 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<nsSVGElement> element = do_QueryInterface(this);
nsIContent* parent = element->GetFlattenedTreeParent();
if (parent &&
parent->NodeInfo()->Equals(nsGkAtoms::svgSwitch, kNameSpaceID_SVG)) {
static_cast<nsSVGSwitchElement*>(parent)->MaybeInvalidate();

View File

@ -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__

View File

@ -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();
}

View File

@ -206,7 +206,8 @@ private:
SVGTransform& Transform() {
return HasOwner() ? InternalItem() : *mTransform;
}
void NotifyElementOfChange();
inline nsAttrValue NotifyElementWillChange();
void NotifyElementDidChange(const nsAttrValue& aEmptyOrOldValue);
nsRefPtr<DOMSVGTransformList> 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

View File

@ -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();
}

View File

@ -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

View File

@ -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();
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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__

View File

@ -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<IsSVGWhitespace>
tokenizer(aValue, ',');

View File

@ -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<nsString> mStrings;
bool mIsSet;
bool mIsCommaSeparated;
};
} // namespace mozilla

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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; }

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -169,24 +169,44 @@ public:
return GetNumberInfo().mNumberInfo[aAttrEnum].mPercentagesAllowed;
}
void SetLength(nsIAtom* aName, const nsSVGLength2 &aLength);
virtual void DidChangeLength(PRUint8 aAttrEnum, bool aDoSetAttr);
virtual void DidChangeNumber(PRUint8 aAttrEnum, bool aDoSetAttr);
virtual void DidChangeNumberPair(PRUint8 aAttrEnum, bool aDoSetAttr);
virtual void DidChangeInteger(PRUint8 aAttrEnum, bool aDoSetAttr);
virtual void DidChangeIntegerPair(PRUint8 aAttrEnum, bool aDoSetAttr);
virtual void DidChangeAngle(PRUint8 aAttrEnum, bool aDoSetAttr);
virtual void DidChangeBoolean(PRUint8 aAttrEnum, bool aDoSetAttr);
virtual void DidChangeEnum(PRUint8 aAttrEnum, bool aDoSetAttr);
virtual void DidChangeViewBox(bool aDoSetAttr);
virtual void DidChangePreserveAspectRatio(bool aDoSetAttr);
virtual void DidChangeNumberList(PRUint8 aAttrEnum, bool aDoSetAttr);
virtual void DidChangeLengthList(PRUint8 aAttrEnum, bool aDoSetAttr);
virtual void DidChangePointList(bool aDoSetAttr);
virtual void DidChangePathSegList(bool aDoSetAttr);
virtual void DidChangeTransformList(bool aDoSetAttr);
virtual void DidChangeString(PRUint8 aAttrEnum) {}
nsAttrValue WillChangeLength(PRUint8 aAttrEnum);
nsAttrValue WillChangeNumberPair(PRUint8 aAttrEnum);
nsAttrValue WillChangeIntegerPair(PRUint8 aAttrEnum);
nsAttrValue WillChangeAngle(PRUint8 aAttrEnum);
nsAttrValue WillChangeViewBox();
nsAttrValue WillChangePreserveAspectRatio();
nsAttrValue WillChangeNumberList(PRUint8 aAttrEnum);
nsAttrValue WillChangeLengthList(PRUint8 aAttrEnum);
nsAttrValue WillChangePointList();
nsAttrValue WillChangePathSegList();
nsAttrValue WillChangeTransformList();
nsAttrValue WillChangeStringList(bool aIsConditionalProcessingAttribute,
PRUint8 aAttrEnum);
void DidChangeLength(PRUint8 aAttrEnum, const nsAttrValue& aEmptyOrOldValue);
void DidChangeNumber(PRUint8 aAttrEnum);
void DidChangeNumberPair(PRUint8 aAttrEnum,
const nsAttrValue& aEmptyOrOldValue);
void DidChangeInteger(PRUint8 aAttrEnum);
void DidChangeIntegerPair(PRUint8 aAttrEnum,
const nsAttrValue& aEmptyOrOldValue);
void DidChangeAngle(PRUint8 aAttrEnum, const nsAttrValue& aEmptyOrOldValue);
void DidChangeBoolean(PRUint8 aAttrEnum);
void DidChangeEnum(PRUint8 aAttrEnum);
void DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue);
void DidChangePreserveAspectRatio(const nsAttrValue& aEmptyOrOldValue);
void DidChangeNumberList(PRUint8 aAttrEnum,
const nsAttrValue& aEmptyOrOldValue);
void DidChangeLengthList(PRUint8 aAttrEnum,
const nsAttrValue& aEmptyOrOldValue);
void DidChangePointList(const nsAttrValue& aEmptyOrOldValue);
void DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue);
void DidChangeTransformList(const nsAttrValue& aEmptyOrOldValue);
void DidChangeString(PRUint8 aAttrEnum) {}
void DidChangeStringList(bool aIsConditionalProcessingAttribute,
PRUint8 aAttrEnum);
PRUint8 aAttrEnum,
const nsAttrValue& aEmptyOrOldValue);
virtual void DidAnimateLength(PRUint8 aAttrEnum);
virtual void DidAnimateNumber(PRUint8 aAttrEnum);
@ -249,6 +269,16 @@ public:
}
protected:
#ifdef DEBUG
// We define BeforeSetAttr here and mark it MOZ_FINAL to ensure it is NOT used
// by SVG elements.
// This is because we're not currently passing the correct value for aValue to
// BeforeSetAttr since it would involve allocating extra SVG value types.
// See the comment in nsSVGElement::WillChangeValue.
virtual nsresult BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue,
bool aNotify) MOZ_FINAL { return NS_OK; }
#endif // DEBUG
virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify);
virtual bool ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute,
@ -264,6 +294,11 @@ protected:
void UpdateAnimatedContentStyleRule();
mozilla::css::StyleRule* GetAnimatedContentStyleRule();
nsAttrValue WillChangeValue(nsIAtom* aName);
void DidChangeValue(nsIAtom* aName, const nsAttrValue& aEmptyOrOldValue,
nsAttrValue& aNewValue);
void MaybeSerializeAttrBeforeRemoval(nsIAtom* aName, bool aNotify);
static nsIAtom* GetEventNameForAttr(nsIAtom* aAttr);
struct LengthInfo {

View File

@ -67,15 +67,12 @@ nsSVGEnum::GetMapping(nsSVGElement *aSVGElement)
}
nsresult
nsSVGEnum::SetBaseValueString(const nsAString& aValue,
nsSVGElement *aSVGElement)
nsSVGEnum::SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement)
{
nsCOMPtr<nsIAtom> valAtom = do_GetAtom(aValue);
nsSVGEnumMapping *mapping = GetMapping(aSVGElement);
while (mapping && mapping->mKey) {
if (valAtom == *(mapping->mKey)) {
if (aValue == *(mapping->mKey)) {
mIsBaseSet = true;
if (mBaseVal != mapping->mVal) {
mBaseVal = mapping->mVal;
@ -99,19 +96,19 @@ nsSVGEnum::SetBaseValueString(const nsAString& aValue,
return NS_ERROR_DOM_SYNTAX_ERR;
}
void
nsSVGEnum::GetBaseValueString(nsAString& aValue, nsSVGElement *aSVGElement)
nsIAtom*
nsSVGEnum::GetBaseValueAtom(nsSVGElement *aSVGElement)
{
nsSVGEnumMapping *mapping = GetMapping(aSVGElement);
while (mapping && mapping->mKey) {
if (mBaseVal == mapping->mVal) {
(*mapping->mKey)->ToString(aValue);
return;
return *mapping->mKey;
}
mapping++;
}
NS_ERROR("unknown enumeration value");
return nsGkAtoms::_empty;
}
nsresult
@ -131,7 +128,7 @@ nsSVGEnum::SetBaseValue(PRUint16 aValue,
else {
aSVGElement->AnimationNeedsResample();
}
aSVGElement->DidChangeEnum(mAttrEnum, true);
aSVGElement->DidChangeEnum(mAttrEnum);
}
return NS_OK;
}

View File

@ -65,11 +65,8 @@ public:
mIsBaseSet = false;
}
nsresult SetBaseValueString(const nsAString& aValue,
nsSVGElement *aSVGElement);
void GetBaseValueString(nsAString& aValue,
nsSVGElement *aSVGElement);
nsresult SetBaseValueAtom(const nsIAtom* aValue, nsSVGElement *aSVGElement);
nsIAtom* GetBaseValueAtom(nsSVGElement *aSVGElement);
nsresult SetBaseValue(PRUint16 aValue,
nsSVGElement *aSVGElement);
PRUint16 GetBaseValue() const

View File

@ -106,9 +106,16 @@ nsSVGInteger::GetBaseValueString(nsAString & aValueAsString)
}
void
nsSVGInteger::SetBaseValue(int aValue,
nsSVGElement *aSVGElement)
nsSVGInteger::SetBaseValue(int aValue, nsSVGElement *aSVGElement)
{
// We can't just rely on SetParsedAttrValue (as called by DidChangeInteger)
// detecting redundant changes since it will compare false if the existing
// attribute value has an associated serialized version (a string value) even
// if the integers match due to the way integers are stored in nsAttrValue.
if (aValue == mBaseVal && mIsBaseSet) {
return;
}
mBaseVal = aValue;
mIsBaseSet = true;
if (!mIsAnimated) {
@ -117,7 +124,7 @@ nsSVGInteger::SetBaseValue(int aValue,
else {
aSVGElement->AnimationNeedsResample();
}
aSVGElement->DidChangeInteger(mAttrEnum, true);
aSVGElement->DidChangeInteger(mAttrEnum);
}
void

View File

@ -128,7 +128,7 @@ nsSVGIntegerPair::SetBaseValueString(const nsAString &aValueAsString,
}
void
nsSVGIntegerPair::GetBaseValueString(nsAString &aValueAsString)
nsSVGIntegerPair::GetBaseValueString(nsAString &aValueAsString) const
{
aValueAsString.Truncate();
aValueAsString.AppendInt(mBaseVal[0]);
@ -143,6 +143,11 @@ nsSVGIntegerPair::SetBaseValue(PRInt32 aValue, PairIndex aPairIndex,
nsSVGElement *aSVGElement)
{
PRUint32 index = (aPairIndex == eFirst ? 0 : 1);
if (mIsBaseSet && mBaseVal[index] == aValue) {
return;
}
nsAttrValue emptyOrOldValue = aSVGElement->WillChangeIntegerPair(mAttrEnum);
mBaseVal[index] = aValue;
mIsBaseSet = true;
if (!mIsAnimated) {
@ -151,13 +156,18 @@ nsSVGIntegerPair::SetBaseValue(PRInt32 aValue, PairIndex aPairIndex,
else {
aSVGElement->AnimationNeedsResample();
}
aSVGElement->DidChangeIntegerPair(mAttrEnum, true);
aSVGElement->DidChangeIntegerPair(mAttrEnum, emptyOrOldValue);
}
void
nsSVGIntegerPair::SetBaseValues(PRInt32 aValue1, PRInt32 aValue2,
nsSVGElement *aSVGElement)
{
if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) {
return;
}
nsAttrValue emptyOrOldValue = aSVGElement->WillChangeIntegerPair(mAttrEnum);
mBaseVal[0] = aValue1;
mBaseVal[1] = aValue2;
mIsBaseSet = true;
@ -168,7 +178,7 @@ nsSVGIntegerPair::SetBaseValues(PRInt32 aValue1, PRInt32 aValue2,
else {
aSVGElement->AnimationNeedsResample();
}
aSVGElement->DidChangeIntegerPair(mAttrEnum, true);
aSVGElement->DidChangeIntegerPair(mAttrEnum, emptyOrOldValue);
}
void

View File

@ -66,7 +66,7 @@ public:
nsresult SetBaseValueString(const nsAString& aValue,
nsSVGElement *aSVGElement);
void GetBaseValueString(nsAString& aValue);
void GetBaseValueString(nsAString& aValue) const;
void SetBaseValue(PRInt32 aValue, PairIndex aIndex, nsSVGElement *aSVGElement);
void SetBaseValues(PRInt32 aValue1, PRInt32 aValue2, nsSVGElement *aSVGElement);

View File

@ -307,8 +307,17 @@ nsSVGLength2::GetUnitScaleFactor(nsIFrame *aFrame, PRUint8 aUnitType) const
void
nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue,
nsSVGElement *aSVGElement)
nsSVGElement *aSVGElement,
bool aDoSetAttr)
{
if (mIsBaseSet && mBaseVal == aValue) {
return;
}
nsAttrValue emptyOrOldValue;
if (aDoSetAttr) {
emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
}
mBaseVal = aValue;
mIsBaseSet = true;
if (!mIsAnimated) {
@ -317,7 +326,9 @@ nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue,
else {
aSVGElement->AnimationNeedsResample();
}
aSVGElement->DidChangeLength(mAttrEnum, true);
if (aDoSetAttr) {
aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
}
}
nsresult
@ -327,10 +338,23 @@ nsSVGLength2::ConvertToSpecifiedUnits(PRUint16 unitType,
if (!IsValidUnitType(unitType))
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
float valueInUserUnits =
if (mIsBaseSet && mSpecifiedUnitType == PRUint8(unitType))
return NS_OK;
// Even though we're not changing the visual effect this length will have
// on the document, we still need to send out notifications in case we have
// mutation listeners, since the actual string value of the attribute will
// change.
nsAttrValue emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
float valueInUserUnits =
mBaseVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType);
mSpecifiedUnitType = 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->DidChangeLength(mAttrEnum, emptyOrOldValue);
return NS_OK;
}
@ -345,6 +369,12 @@ nsSVGLength2::NewValueSpecifiedUnits(PRUint16 unitType,
if (!IsValidUnitType(unitType))
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
if (mIsBaseSet && mBaseVal == valueInSpecifiedUnits &&
mSpecifiedUnitType == PRUint8(unitType)) {
return NS_OK;
}
nsAttrValue emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
mBaseVal = valueInSpecifiedUnits;
mIsBaseSet = true;
mSpecifiedUnitType = PRUint8(unitType);
@ -354,7 +384,7 @@ nsSVGLength2::NewValueSpecifiedUnits(PRUint16 unitType,
else {
aSVGElement->AnimationNeedsResample();
}
aSVGElement->DidChangeLength(mAttrEnum, true);
aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
return NS_OK;
}
@ -407,12 +437,21 @@ nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString,
{
float value;
PRUint16 unitType;
nsresult rv = GetValueFromString(aValueAsString, &value, &unitType);
if (NS_FAILED(rv)) {
return rv;
}
if (mIsBaseSet && mBaseVal == value &&
mSpecifiedUnitType == PRUint8(unitType)) {
return NS_OK;
}
nsAttrValue emptyOrOldValue;
if (aDoSetAttr) {
emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
}
mBaseVal = value;
mIsBaseSet = true;
mSpecifiedUnitType = PRUint8(unitType);
@ -423,28 +462,31 @@ nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString,
aSVGElement->AnimationNeedsResample();
}
aSVGElement->DidChangeLength(mAttrEnum, aDoSetAttr);
if (aDoSetAttr) {
aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
}
return NS_OK;
}
void
nsSVGLength2::GetBaseValueString(nsAString & aValueAsString)
nsSVGLength2::GetBaseValueString(nsAString & aValueAsString) const
{
GetValueString(aValueAsString, mBaseVal, mSpecifiedUnitType);
}
void
nsSVGLength2::GetAnimValueString(nsAString & aValueAsString)
nsSVGLength2::GetAnimValueString(nsAString & aValueAsString) const
{
GetValueString(aValueAsString, mAnimVal, mSpecifiedUnitType);
}
void
nsSVGLength2::SetBaseValue(float aValue, nsSVGElement *aSVGElement)
nsSVGLength2::SetBaseValue(float aValue, nsSVGElement *aSVGElement,
bool aDoSetAttr)
{
SetBaseValueInSpecifiedUnits(aValue * GetUnitScaleFactor(aSVGElement,
mSpecifiedUnitType),
aSVGElement);
aSVGElement, aDoSetAttr);
}
void

View File

@ -82,8 +82,8 @@ 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(nsSVGElement* aSVGElement) const
{ return mBaseVal / GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); }
@ -148,8 +148,9 @@ private:
float GetUnitScaleFactor(nsSVGSVGElement *aCtx, PRUint8 aUnitType) const;
// SetBaseValue and SetAnimValue set the value in user units
void SetBaseValue(float aValue, nsSVGElement *aSVGElement);
void SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement);
void SetBaseValue(float aValue, nsSVGElement *aSVGElement, bool aDoSetAttr);
void SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement,
bool aDoSetAttr);
void SetAnimValue(float aValue, nsSVGElement *aSVGElement);
void SetAnimValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement);
nsresult NewValueSpecifiedUnits(PRUint16 aUnitType, float aValue,
@ -180,7 +181,7 @@ private:
if (!NS_finite(aValue)) {
return NS_ERROR_ILLEGAL_VALUE;
}
mVal->SetBaseValue(aValue, mSVGElement);
mVal->SetBaseValue(aValue, mSVGElement, true);
return NS_OK;
}
@ -191,7 +192,7 @@ private:
if (!NS_finite(aValue)) {
return NS_ERROR_ILLEGAL_VALUE;
}
mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement);
mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement, true);
return NS_OK;
}

View File

@ -224,7 +224,7 @@ NS_IMETHODIMP nsSVGMarkerElement::SetOrientToAngle(nsIDOMSVGAngle *angle)
nsresult rv = angle->GetValue(&f);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_FINITE(f, NS_ERROR_DOM_SVG_WRONG_TYPE_ERR);
mAngleAttributes[ORIENT].SetBaseValue(f, this);
mAngleAttributes[ORIENT].SetBaseValue(f, this, true);
return NS_OK;
}

View File

@ -149,9 +149,12 @@ nsSVGNumber2::GetBaseValueString(nsAString & aValueAsString)
}
void
nsSVGNumber2::SetBaseValue(float aValue,
nsSVGElement *aSVGElement)
nsSVGNumber2::SetBaseValue(float aValue, nsSVGElement *aSVGElement)
{
if (mIsBaseSet && aValue == mBaseVal) {
return;
}
mBaseVal = aValue;
mIsBaseSet = true;
if (!mIsAnimated) {
@ -160,7 +163,7 @@ nsSVGNumber2::SetBaseValue(float aValue,
else {
aSVGElement->AnimationNeedsResample();
}
aSVGElement->DidChangeNumber(mAttrEnum, true);
aSVGElement->DidChangeNumber(mAttrEnum);
}
void

View File

@ -121,14 +121,14 @@ nsSVGNumberPair::SetBaseValueString(const nsAString &aValueAsString,
aSVGElement->AnimationNeedsResample();
}
// We don't need to call DidChange* here - we're only called by
// We don't need to call Will/DidChange* here - we're only called by
// nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
// which takes care of notifying.
return NS_OK;
}
void
nsSVGNumberPair::GetBaseValueString(nsAString &aValueAsString)
nsSVGNumberPair::GetBaseValueString(nsAString &aValueAsString) const
{
aValueAsString.Truncate();
aValueAsString.AppendFloat(mBaseVal[0]);
@ -143,6 +143,10 @@ nsSVGNumberPair::SetBaseValue(float aValue, PairIndex aPairIndex,
nsSVGElement *aSVGElement)
{
PRUint32 index = (aPairIndex == eFirst ? 0 : 1);
if (mIsBaseSet && mBaseVal[index] == aValue) {
return;
}
nsAttrValue emptyOrOldValue = aSVGElement->WillChangeNumberPair(mAttrEnum);
mBaseVal[index] = aValue;
mIsBaseSet = true;
if (!mIsAnimated) {
@ -151,13 +155,17 @@ nsSVGNumberPair::SetBaseValue(float aValue, PairIndex aPairIndex,
else {
aSVGElement->AnimationNeedsResample();
}
aSVGElement->DidChangeNumberPair(mAttrEnum, true);
aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue);
}
void
nsSVGNumberPair::SetBaseValues(float aValue1, float aValue2,
nsSVGElement *aSVGElement)
{
if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) {
return;
}
nsAttrValue emptyOrOldValue = aSVGElement->WillChangeNumberPair(mAttrEnum);
mBaseVal[0] = aValue1;
mBaseVal[1] = aValue2;
mIsBaseSet = true;
@ -168,7 +176,7 @@ nsSVGNumberPair::SetBaseValues(float aValue1, float aValue2,
else {
aSVGElement->AnimationNeedsResample();
}
aSVGElement->DidChangeNumberPair(mAttrEnum, true);
aSVGElement->DidChangeNumberPair(mAttrEnum, emptyOrOldValue);
}
void

View File

@ -67,7 +67,7 @@ public:
nsresult SetBaseValueString(const nsAString& aValue,
nsSVGElement *aSVGElement);
void GetBaseValueString(nsAString& aValue);
void GetBaseValueString(nsAString& aValue) const;
void SetBaseValue(float aValue, PairIndex aIndex, nsSVGElement *aSVGElement);
void SetBaseValues(float aValue1, float aValue2, nsSVGElement *aSVGElement);

View File

@ -127,10 +127,16 @@ void
nsSVGViewBox::SetBaseValue(float aX, float aY, float aWidth, float aHeight,
nsSVGElement *aSVGElement)
{
if (mHasBaseVal && mBaseVal == nsSVGViewBoxRect(aX, aY, aWidth, aHeight)) {
return;
}
nsAttrValue emptyOrOldValue = aSVGElement->WillChangeViewBox();
mBaseVal = nsSVGViewBoxRect(aX, aY, aWidth, aHeight);
mHasBaseVal = true;
aSVGElement->DidChangeViewBox(true);
aSVGElement->DidChangeViewBox(emptyOrOldValue);
if (mAnimVal) {
aSVGElement->AnimationNeedsResample();
}
@ -185,7 +191,7 @@ nsSVGViewBox::SetBaseValueString(const nsAString& aValue,
if (mAnimVal) {
aSVGElement->AnimationNeedsResample();
}
// We don't need to call DidChange* here - we're only called by
// We don't need to call Will/DidChange* here - we're only called by
// nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
// which takes care of notifying.
}

View File

@ -50,6 +50,7 @@ include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
matrixUtils.js \
MutationEventChecker.js \
test_a_href_01.xhtml \
test_a_href_02.xhtml \
a_href_destination.svg \
@ -65,6 +66,7 @@ _TEST_FILES = \
bbox-helper.svg \
bounds-helper.svg \
test_dataTypes.html \
test_dataTypesModEvents.html \
dataTypes-helper.svg \
getCTM-helper.svg \
test_getCTM.html \
@ -88,7 +90,9 @@ _TEST_FILES = \
test_SVGLengthList.xhtml \
test_SVGLengthList-2.xhtml \
test_SVGMatrix.xhtml \
test_SVGNumberList.xhtml \
test_SVGPathSegList.xhtml \
test_SVGPointList.xhtml \
test_SVGStyleElement.xhtml \
test_SVGStringList.xhtml \
test_SVGTransformList.xhtml \

View File

@ -0,0 +1,245 @@
// Helper class to check DOM MutationEvents
//
// Usage:
//
// * Create a new event checker:
// var eventChecker = new MutationEventChecker;
// * Set the attribute to watch
// eventChecker.watchAttr(<DOM element>, "<attribute name>");
// * Set the events to expect (0..n)
// eventChecker.expect("add", "modify");
// OR
// eventChecker.expect("add modify");
// OR
// eventChecker.expect(MutationEvent.ADDITION, MutationEvent.MODIFICATION);
//
// An empty string or empty set of arguments is also fine as a way of checking
// that all expected events have been received and indicating no events are
// expected from the following code, e.g.
//
// eventChecker.expect("");
// // changes that are not expected to generate events
// eventChecker.expect("modify");
// // change that is expected to generate an event
// ...
//
// * Either finish listening or set the next attribute to watch
// eventChecker.finish();
// eventChecker.watchAttr(element, "nextAttribute");
//
// In either case a check is performed that all expected events have been
// received.
//
// * Event checking can be temporarily disabled with ignoreEvents(). The next
// call to expect() will cause it to resume.
function MutationEventChecker()
{
this.expectedEvents = [];
this.watchAttr = function(element, attr)
{
if (this.attr) {
this.finish();
}
this.expectedEvents = [];
this.element = element;
this.attr = attr;
this.oldValue = element.getAttribute(attr);
this.giveUp = false;
this.ignore = false;
this.element.addEventListener('DOMAttrModified', this._listener, false);
}
this.expect = function()
{
if (this.giveUp) {
return;
}
ok(this.expectedEvents.length == 0,
"Expecting new events for " + this.attr +
" but the following previously expected events have still not been " +
"received: " + this._stillExpecting());
if (this.expectedEvents.length != 0) {
this.giveUp = true;
return;
}
this.ignore = false;
if (arguments.length == 0 ||
arguments.length == 1 && arguments[0] == "") {
return;
}
// Turn arguments object into an array
var args = Array.prototype.slice.call(arguments);
// Check for whitespace separated keywords
if (args.length == 1 && typeof args[0] === 'string' &&
args[0].indexOf(' ') > 0) {
args = args[0].split(' ');
}
// Convert strings to event Ids
this.expectedEvents = args.map(this._argToEventId);
}
// Temporarily disable event checking
this.ignoreEvents = function()
{
// Check all events have been received
ok(this.giveUp || this.expectedEvents.length == 0,
"Going to ignore subsequent events on " + this.attr +
" attribute, but we're still expecting the following events: " +
this._stillExpecting());
this.ignore = true;
}
this.finish = function()
{
// Check all events have been received
ok(this.giveUp || this.expectedEvents.length == 0,
"Finishing listening to " + this.attr +
" attribute, but we're still expecting the following events: " +
this._stillExpecting());
this.element.removeEventListener('DOMAttrModified', this._listener, false);
this.attr = "";
}
this._receiveEvent = function(e)
{
if (this.giveUp || this.ignore) {
this.oldValue = e.newValue;
return;
}
// Make sure we're expecting something at all
if (this.expectedEvents.length == 0) {
ok(false, 'Unexpected ' + this._eventToName(e.attrChange) +
' event when none expected on ' + this.attr + ' attribute.');
return;
}
var expectedEvent = this.expectedEvents.shift();
// Make sure we got the event we expected
if (e.attrChange != expectedEvent) {
ok(false, 'Unexpected ' + this._eventToName(e.attrChange) +
' on ' + this.attr + ' attribute. Expected ' +
this._eventToName(expectedEvent) + ' (followed by: ' +
this._stillExpecting() + ")");
// If we get events out of sequence, it doesn't make sense to do any
// further testing since we don't really know what to expect
this.giveUp = true;
return;
}
// Common param checking
is(e.target, this.element,
'Unexpected node for mutation event on ' + this.attr + ' attribute');
is(e.attrName, this.attr, 'Unexpected attribute name for mutation event');
// Don't bother testing e.relatedNode since Attr nodes are on the way
// out anyway (but then, so are mutation events...)
// Event-specific checking
if (e.attrChange == MutationEvent.MODIFICATION) {
ok(this.element.hasAttribute(this.attr),
'Attribute not set after modification');
is(e.prevValue, this.oldValue,
'Unexpected old value for modification to ' + this.attr +
' attribute');
isnot(e.newValue, this.oldValue,
'Unexpected new value for modification to ' + this.attr +
' attribute');
} else if (e.attrChange == MutationEvent.REMOVAL) {
ok(!this.element.hasAttribute(this.attr), 'Attribute set after removal');
is(e.prevValue, this.oldValue,
'Unexpected old value for removal of ' + this.attr +
' attribute');
// DOM 3 Events doesn't say what value newValue will be for a removal
// event but generally empty strings are used for other events when an
// attribute isn't relevant
ok(e.newValue === "",
'Unexpected new value for removal of ' + this.attr +
' attribute');
} else if (e.attrChange == MutationEvent.ADDITION) {
ok(this.element.hasAttribute(this.attr),
'Attribute not set after addition');
// DOM 3 Events doesn't say what value prevValue will be for an addition
// event but generally empty strings are used for other events when an
// attribute isn't relevant
ok(e.prevValue === "",
'Unexpected old value for addition of ' + this.attr +
' attribute');
ok(typeof(e.newValue) == 'string' && e.newValue !== "",
'Unexpected new value for addition of ' + this.attr +
' attribute');
} else {
ok(false, 'Unexpected mutation event type: ' + e.attrChange);
this.giveUp = true;
}
this.oldValue = e.newValue;
}
this._listener = this._receiveEvent.bind(this);
this._stillExpecting = function()
{
if (this.expectedEvents.length == 0) {
return "(nothing)";
}
var eventNames = [];
for (var i=0; i < this.expectedEvents.length; i++) {
eventNames.push(this._eventToName(this.expectedEvents[i]));
}
return eventNames.join(", ");
}
this._eventToName = function(evtId)
{
switch (evtId)
{
case MutationEvent.MODIFICATION:
return "modification";
case MutationEvent.ADDITION:
return "addition";
case MutationEvent.REMOVAL:
return "removal";
}
}
this._argToEventId = function(arg)
{
if (typeof arg === 'number')
return arg;
if (typeof arg !== 'string') {
ok(false, "Unexpected event type: " + arg);
return 0;
}
switch (arg.toLowerCase())
{
case "mod":
case "modify":
case "modification":
return MutationEvent.MODIFICATION;
case "add":
case "addition":
return MutationEvent.ADDITION;
case "removal":
case "remove":
return MutationEvent.REMOVAL;
default:
ok(false, "Unexpected event name: " + arg);
return 0;
}
}
}

View File

@ -5,6 +5,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=515116
<head>
<title>Tests specific to SVGLengthList</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="MutationEventChecker.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
@ -36,11 +37,31 @@ function run_tests()
is(lengths.numberOfItems, 0, 'Checking numberOfItems');
/*
is(lengths.numberOfItems, 2, 'Checking numberOfItems');
is(lengths.getItem(1).valueInSpecifiedUnits == 20, 'Checking the value of the second length');
is(lengths.getItem(1).unitType == SVGLength.SVG_LENGTHTYPE_CM, 'Checking the unit of the second length');
*/
// Test mutation events
// --- Initialization
eventChecker = new MutationEventChecker;
eventChecker.watchAttr(text, "x");
eventChecker.expect("modify");
text.textContent = "abc";
text.setAttribute("x", "10 20 30");
is(lengths.numberOfItems, 3, 'Checking numberOfItems');
// -- Actual changes
eventChecker.expect("modify modify modify modify modify");
lengths[0].value = 8;
lengths[0].valueInSpecifiedUnits = 9;
lengths[0].valueAsString = "10";
lengths[0].convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_CM);
lengths[0].newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_MM, 11);
// -- Redundant changes
eventChecker.expect("modify");
lengths[0].valueAsString = "10";
eventChecker.expect("");
lengths[0].value = 10;
lengths[0].valueInSpecifiedUnits = 10;
lengths[0].valueAsString = "10";
lengths[0].convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER);
lengths[0].newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER, 10);
eventChecker.finish();
SimpleTest.finish();
}

View File

@ -0,0 +1,64 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=629200
-->
<head>
<title>Tests specific to SVGNumberList</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="MutationEventChecker.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=629200">Mozilla Bug 629200</a>
<p id="display"></p>
<div id="content" style="display:none;">
<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<text id="text" rotate="10 20 30">abc</text>
</svg>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
<![CDATA[
SimpleTest.waitForExplicitFinish();
/*
This file runs a series of SVGNumberList specific tests. Generic SVGXxxList
tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized
to other list types belongs there.
*/
function run_tests()
{
document.getElementById('svg').pauseAnimations();
var text = document.getElementById("text");
var numbers = text.rotate.baseVal;
is(numbers.numberOfItems, 3, 'Checking numberOfItems');
// Test mutation events
// --- Initialization
eventChecker = new MutationEventChecker;
eventChecker.watchAttr(text, "rotate");
// -- Actual changes
eventChecker.expect("modify modify");
numbers[0].value = 15;
text.setAttribute("rotate", "17 20 30");
// -- Redundant changes
eventChecker.expect("");
numbers[0].value = 17;
numbers[1].value = 20;
text.setAttribute("rotate", "17 20 30");
eventChecker.finish();
SimpleTest.finish();
}
window.addEventListener("load", run_tests, false);
]]>
</script>
</pre>
</body>
</html>

View File

@ -5,6 +5,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=611138
<head>
<title>Generic tests for SVG animated length lists</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="MutationEventChecker.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
@ -86,17 +87,25 @@ function run_tests()
ok(list.numberOfItems == 1 && list.getItem(0) == seg,
'initialize should be able initialize an invalid path with a non-moveto item');
// Test mutation events
eventChecker = new MutationEventChecker;
d = 'M0,0 L12,34'
path.setAttribute('d', d);
function check_old_value(e) {
is(e.target, path, 'check mutation event is for expected node');
is(e.attrName, 'd', 'check mutation event is for expected attribute');
is(e.prevValue, d, 'check old attribute value is correctly reported');
isnot(e.newValue, d, 'check attribute value has changed');
}
path.addEventListener('DOMAttrModified', check_old_value, false);
list.getItem(1).y = 35;
path.removeEventListener('DOMAttrModified', check_old_value, false);
eventChecker.watchAttr(path, "d");
// -- Actual changes
eventChecker.expect("modify modify modify");
list[0].x = 10;
list[0].y = 5;
path.setAttribute("d", "M20,5 L12,34");
// -- Redundant changes
eventChecker.expect("");
list[0].x = 20;
list[1].y = 34;
path.setAttribute("d", "M20,5 L12,34");
eventChecker.finish();
SimpleTest.finish();
}

View File

@ -0,0 +1,64 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=629200
-->
<head>
<title>Tests specific to SVGPointList</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="MutationEventChecker.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=629200">Mozilla Bug 629200</a>
<p id="display"></p>
<div id="content" style="display:none;">
<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<polyline id="polyline" points="50,375 150,380"/>
</svg>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
<![CDATA[
SimpleTest.waitForExplicitFinish();
/*
This file runs a series of SVGPointList specific tests. Generic SVGXxxList
tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized
to other list types belongs there.
*/
function run_tests()
{
document.getElementById('svg').pauseAnimations();
var polyline = document.getElementById("polyline");
var points = polyline.points;
is(points.numberOfItems, 2, 'Checking numberOfItems');
// Test mutation events
// --- Initialization
eventChecker = new MutationEventChecker;
eventChecker.watchAttr(polyline, "points");
// -- Actual changes
eventChecker.expect("modify modify");
points[0].x = 40;
polyline.setAttribute("points", "30,375 150,380");
// -- Redundant changes
eventChecker.expect("");
points[0].x = 30;
points[1].y = 380;
polyline.setAttribute("points", "30,375 150,380");
eventChecker.finish();
SimpleTest.finish();
}
window.addEventListener("load", run_tests, false);
]]>
</script>
</pre>
</body>
</html>

View File

@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=602759
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="matrixUtils.js"></script>
<script type="text/javascript" src="MutationEventChecker.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
@ -41,7 +42,8 @@ function main()
testCreateSVGTransformFromMatrix,
testReadOnly,
testOrphan,
testFailedSet
testFailedSet,
testMutationEvents
];
for (var i = 0; i < tests.length; i++) {
tests[i](g);
@ -355,6 +357,76 @@ function testFailedSet(g)
"Animated transform list should also be empty after setting bad value");
}
function testMutationEvents(g)
{
// Check mutation events
// Set initial value
g.setAttribute("transform", "translate(50 90)");
var list = g.transform.baseVal;
is(list.numberOfItems, 1, "Unexpected initial length of list");
eventChecker = new MutationEventChecker;
eventChecker.watchAttr(g, "transform");
// consolidate
//
// Consolidate happens to generate two modification events in our
// implementation--it's not ideal but it's better than none
eventChecker.expect("modify modify modify");
g.setAttribute("transform", "translate(10 10) translate(10 10)");
list.consolidate();
// In the following, each of the operations is performed twice but only one
// mutation event is expected. This is to check that redundant mutation
// events are not sent.
// transform.setMatrix
eventChecker.expect("modify");
var mx = $('svg').createSVGMatrix();
list[0].setMatrix(mx);
list[0].setMatrix(mx);
// transform.setTranslate
eventChecker.expect("modify");
list[0].setTranslate(10, 10);
list[0].setTranslate(10, 10);
// transform.setScale
eventChecker.expect("modify");
list[0].setScale(2, 2);
list[0].setScale(2, 2);
// transform.setRotate
eventChecker.expect("modify");
list[0].setRotate(45, 1, 2);
list[0].setRotate(45, 1, 2);
// transform.setSkewX
eventChecker.expect("modify");
list[0].setSkewX(45);
list[0].setSkewX(45);
// transform.setSkewY
eventChecker.expect("modify");
list[0].setSkewY(25);
list[0].setSkewY(25);
// transform.matrix
eventChecker.expect("modify modify");
list[0].matrix.a = 1;
list[0].matrix.a = 1;
list[0].matrix.e = 5;
list[0].matrix.e = 5;
// setAttribute interaction
eventChecker.expect("modify");
list[0].setMatrix(mx);
eventChecker.expect("");
g.setAttribute("transform", "matrix(1, 0, 0, 1, 0, 0)");
list[0].setMatrix(mx);
eventChecker.finish();
}
window.addEventListener("load", main, false);
]]>

View File

@ -6,6 +6,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=515116
<title>Generic tests for SVG animated length lists</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="matrixUtils.js"></script>
<script type="text/javascript" src="MutationEventChecker.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
@ -452,6 +453,7 @@ function get_array_of_list_items(list)
function run_baseVal_API_tests()
{
var res, threw, items;
var eventChecker = new MutationEventChecker;
for each (var t in tests) {
@ -462,6 +464,8 @@ function run_baseVal_API_tests()
is(t.baseVal.numberOfItems, 4,
'The '+t.list_type+' object should contain four list items.');
eventChecker.watchAttr(t.element, t.attr_name);
eventChecker.expect("modify");
res = t.baseVal.clear();
is(t.baseVal.numberOfItems, 0,
@ -469,13 +473,48 @@ function run_baseVal_API_tests()
' object.');
is(res, undefined,
'The method '+t.list_type+'.clear() should not return a value.');
ok(t.element.hasAttribute(t.attr_name),
'The method '+t.list_type+'.clear() should not remove the attribute.');
ok(t.element.getAttribute(t.attr_name) === "",
'Cleared '+t.attr_name+' ('+t.list_type+') but did not get an '+
'empty string back.');
eventChecker.expect("");
t.baseVal.clear();
eventChecker.ignoreEvents();
// Test empty strings
t.element.setAttribute(t.attr_name, "");
ok(t.element.getAttribute(t.attr_name) === "",
'Set an empty attribute value for '+t.attr_name+' ('+t.list_type+
') but did not get an empty string back.');
// Test removed attributes
t.element.removeAttribute(t.attr_name);
ok(t.element.getAttribute(t.attr_name) === null,
'Removed attribute value for '+t.attr_name+' ('+t.list_type+
') but did not get null back.');
ok(!t.element.hasAttribute(t.attr_name),
'Removed attribute value for '+t.attr_name+' ('+t.list_type+
') but hasAttribute still returns true.');
// Test .initialize():
t.element.setAttribute(t.attr_name, t.attr_val_4);
var item = t.item_constructor();
// Our current implementation of 'initialize' for most list types performs
// a 'clear' followed by an 'insertItemBefore'. This results in two
// modification events being dispatched. SVGStringList however avoids the
// additional clear.
var expectedModEvents =
t.item_type == "DOMString" ? "modify" : "modify modify";
eventChecker.expect(expectedModEvents);
var res = t.baseVal.initialize(item);
eventChecker.ignoreEvents();
is(t.baseVal.numberOfItems, 1,
'The '+t.list_type+' object should contain one list item.');
@ -515,6 +554,7 @@ function run_baseVal_API_tests()
'is passed in, even if that object is the only item in that list.');
// [SVGWG issue] not what the spec currently says
eventChecker.expect("");
threw = false;
try {
t.baseVal.initialize({});
@ -524,6 +564,7 @@ function run_baseVal_API_tests()
ok(threw,
'The method '+t.list_type+'.initialize() should throw if passed an '+
'object of the wrong type.');
eventChecker.ignoreEvents();
}
// Test .insertItemBefore():
@ -532,7 +573,9 @@ function run_baseVal_API_tests()
old_items = get_array_of_list_items(t.baseVal);
item = t.item_constructor();
eventChecker.expect("modify");
res = t.baseVal.insertItemBefore(item, 2);
eventChecker.ignoreEvents();
is(t.baseVal.numberOfItems, 5,
'The '+t.list_type+' object should contain five list items.');
@ -587,6 +630,7 @@ function run_baseVal_API_tests()
'the list at the index specified.');
// [SVGWG issue] not what the spec currently says
eventChecker.expect("");
threw = false;
try {
t.baseVal.insertItemBefore({}, 2);
@ -596,6 +640,7 @@ function run_baseVal_API_tests()
ok(threw,
'The method '+t.list_type+'.insertItemBefore() should throw if passed '+
'an object of the wrong type.');
eventChecker.ignoreEvents();
}
// Test .replaceItem():
@ -604,7 +649,9 @@ function run_baseVal_API_tests()
old_items = get_array_of_list_items(t.baseVal);
item = t.item_constructor();
eventChecker.expect("modify");
res = t.baseVal.replaceItem(item, 2);
eventChecker.ignoreEvents();
is(t.baseVal.numberOfItems, 4,
'The '+t.list_type+' object should contain four list items.');
@ -627,6 +674,7 @@ function run_baseVal_API_tests()
item = t.item_constructor();
eventChecker.expect("");
threw = false;
try {
t.baseVal.replaceItem(item, 100);
@ -636,6 +684,7 @@ function run_baseVal_API_tests()
ok(threw,
'The method '+t.list_type+'.replaceItem() should throw if passed '+
'an index that is out of bounds.');
eventChecker.ignoreEvents();
old_items = get_array_of_list_items(t.baseVal);
item = t.baseVal.getItem(3);
@ -683,7 +732,9 @@ function run_baseVal_API_tests()
old_items = get_array_of_list_items(t.baseVal);
item = t.baseVal.getItem(2);
eventChecker.expect("modify");
res = t.baseVal.removeItem(2);
eventChecker.ignoreEvents();
is(t.baseVal.numberOfItems, 3,
'The '+t.list_type+' object should contain three list items.');
@ -701,6 +752,7 @@ function run_baseVal_API_tests()
'the item at index 2 was removed using the '+t.list_type+
'.replaceItem() method.');
eventChecker.expect("");
threw = false;
try {
t.baseVal.removeItem(100);
@ -710,6 +762,7 @@ function run_baseVal_API_tests()
ok(threw,
'The method '+t.list_type+'.removeItem() should throw if passed '+
'an index that is out of bounds.');
eventChecker.ignoreEvents();
// Test .appendItem():
@ -717,7 +770,9 @@ function run_baseVal_API_tests()
old_items = get_array_of_list_items(t.baseVal);
item = t.item_constructor();
eventChecker.expect("modify");
res = t.baseVal.appendItem(item);
eventChecker.ignoreEvents();
is(t.baseVal.numberOfItems, 5,
'The '+t.list_type+' object should contain five list items.');
@ -763,6 +818,7 @@ function run_baseVal_API_tests()
'that list.');
// [SVGWG issue] not what the spec currently says
eventChecker.expect("");
threw = false;
try {
t.baseVal.appendItem({});
@ -772,7 +828,15 @@ function run_baseVal_API_tests()
ok(threw,
'The method '+t.list_type+'.appendItem() should throw if passed '+
'an object of the wrong type.');
eventChecker.ignoreEvents();
}
// Test removal and addition events
eventChecker.expect("remove add");
t.element.removeAttribute(t.attr_name);
res = t.baseVal.appendItem(item);
eventChecker.finish();
}
}
@ -880,7 +944,7 @@ function run_animVal_API_tests()
/**
* This function runs some basic tests to check the effect of setAttribute()
* calls on object identidy, without taking SMIL animation into consideration.
* calls on object identity, without taking SMIL animation into consideration.
*/
function run_basic_setAttribute_tests()
{

View File

@ -38,8 +38,11 @@ function runTests()
is(filter.className.animVal, "bar", "className animVal");
filter.removeAttribute("class");
is(filter.hasAttribute("class"), false, "class attribute");
ok(filter.getAttribute("class") === null, "removed class attribute");
is(filter.className.baseVal, "", "className baseVal");
is(filter.className.animVal, "", "className animVal");
filter.setAttribute("class", "");
ok(filter.getAttribute("class") === "", "empty class attribute");
// length attribute
@ -57,6 +60,11 @@ function runTests()
is(marker.markerWidth.animVal.value, 7.5, "length animVal");
is(marker.getAttribute("markerWidth"), "7.5", "length attribute");
marker.setAttribute("markerWidth", "");
ok(marker.getAttribute("markerWidth") === "", "empty length attribute");
marker.removeAttribute("markerWidth");
ok(marker.getAttribute("markerWidth") === null, "removed length attribute");
// number attribute
convolve.setAttribute("divisor", "12.5");
@ -67,6 +75,11 @@ function runTests()
is(convolve.divisor.animVal, 7.5, "number animVal");
is(convolve.getAttribute("divisor"), "7.5", "number attribute");
convolve.setAttribute("divisor", "");
ok(convolve.getAttribute("divisor") === "", "empty number attribute");
convolve.removeAttribute("divisor");
ok(convolve.getAttribute("divisor") === null, "removed number attribute");
// number-optional-number attribute
blur.setAttribute("stdDeviation", "20.5");
@ -89,6 +102,13 @@ function runTests()
is(blur.stdDeviationY.baseVal, 0.5, "integer-optional-integer second baseVal");
is(blur.stdDeviationY.animVal, 0.5, "integer-optional-integer second animVal");
blur.setAttribute("stdDeviation", "");
ok(blur.getAttribute("stdDeviation") === "",
"empty number-optional-number attribute");
blur.removeAttribute("stdDeviation");
ok(blur.getAttribute("stdDeviation") === null,
"removed number-optional-number attribute");
// integer attribute
convolve.setAttribute("targetX", "12");
@ -97,6 +117,10 @@ function runTests()
convolve.targetX.baseVal = 7;
is(convolve.targetX.animVal, 7, "integer animVal");
is(convolve.getAttribute("targetX"), "7", "integer attribute");
convolve.setAttribute("targetX", "");
ok(convolve.getAttribute("targetX") === "", "empty integer attribute");
convolve.removeAttribute("targetX");
ok(convolve.getAttribute("targetX") === null, "removed integer attribute");
// integer-optional-integer attribute
@ -120,6 +144,13 @@ function runTests()
is(filter.filterResY.baseVal, 90, "integer-optional-integer second baseVal");
is(filter.filterResY.animVal, 90, "integer-optional-integer second animVal");
filter.setAttribute("filterRes", "");
ok(filter.getAttribute("filterRes") === "",
"empty integer-optional-integer attribute");
filter.removeAttribute("filterRes");
ok(filter.getAttribute("filterRes") === null,
"removed integer-optional-integer attribute");
// angle attribute
marker.setAttribute("orient", "90deg");
@ -136,6 +167,11 @@ function runTests()
is(marker.orientAngle.animVal.value, 30, "angle animVal");
is(marker.getAttribute("orient"), "30deg", "angle attribute");
marker.setAttribute("orient", "");
ok(marker.getAttribute("orient") === "", "empty angle attribute");
marker.removeAttribute("orient");
ok(marker.getAttribute("orient") === null, "removed angle attribute");
// boolean attribute
convolve.setAttribute("preserveAlpha", "false");
@ -144,6 +180,11 @@ function runTests()
convolve.preserveAlpha.baseVal = true;
is(convolve.preserveAlpha.animVal, true, "boolean animVal");
is(convolve.getAttribute("preserveAlpha"), "true", "boolean attribute");
convolve.setAttribute("preserveAlpha", "");
ok(convolve.getAttribute("preserveAlpha") === "", "empty boolean attribute");
convolve.removeAttribute("preserveAlpha");
ok(convolve.getAttribute("preserveAlpha") === null,
"removed boolean attribute");
// enum attribute
@ -153,6 +194,10 @@ function runTests()
convolve.edgeMode.baseVal = 1;
is(convolve.edgeMode.animVal, 1, "enum animVal");
is(convolve.getAttribute("edgeMode"), "duplicate", "enum attribute");
convolve.setAttribute("edgeMode", "");
ok(convolve.getAttribute("edgeMode") === "", "empty enum attribute");
convolve.removeAttribute("edgeMode");
ok(convolve.getAttribute("edgeMode") === null, "removed enum attribute");
// string attribute
@ -162,6 +207,10 @@ function runTests()
convolve.result.baseVal = "bar";
is(convolve.result.animVal, "bar", "string animVal");
is(convolve.getAttribute("result"), "bar", "string attribute");
convolve.setAttribute("result", "");
ok(convolve.getAttribute("result") === "", "empty string attribute");
convolve.removeAttribute("result");
ok(convolve.getAttribute("result") === null, "removed string attribute");
// preserveAspectRatio attribute
@ -186,6 +235,13 @@ function runTests()
is(basePreserveAspectRatio.meetOrSlice, 2, "preserveAspectRatio.meetOrSlice baseVal");
is(animPreserveAspectRatio.meetOrSlice, 2, "preserveAspectRatio.meetOrSlice animVal");
marker.setAttribute("preserveAspectRatio", "");
ok(marker.getAttribute("preserveAspectRatio") === "",
"empty preserveAspectRatio attribute");
marker.removeAttribute("preserveAspectRatio");
ok(marker.getAttribute("preserveAspectRatio") === null,
"removed preserveAspectRatio attribute");
// viewBox attribute
var baseViewBox = marker.viewBox.baseVal;
var animViewBox = marker.viewBox.animVal;
@ -223,6 +279,10 @@ function runTests()
is(marker.getAttribute("viewBox"), "5 6 7 8", "viewBox attribute");
marker.removeAttribute("viewBox");
is(marker.hasAttribute("viewBox"), false, "viewBox hasAttribute");
ok(marker.getAttribute("viewBox") === null, "removed viewBox attribute");
marker.setAttribute("viewBox", "");
ok(marker.getAttribute("viewBox") === "", "empty viewBox attribute");
SimpleTest.finish();
}

View File

@ -0,0 +1,245 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=629200
-->
<head>
<title>Test for Bug 629200</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="MutationEventChecker.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=629200">Mozilla
Bug 629200</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<iframe id="svg" src="dataTypes-helper.svg"></iframe>
<pre id="test">
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
function runTests()
{
var doc = $("svg").contentWindow.document;
var filter = doc.getElementById("filter");
var convolve = doc.getElementById("convolve");
var blur = doc.getElementById("blur");
var marker = doc.getElementById("marker");
var eventChecker = new MutationEventChecker;
// class attribute
eventChecker.watchAttr(filter, "class");
eventChecker.expect("add modify remove add");
filter.setAttribute("class", "foo");
filter.className.baseVal = "bar";
filter.removeAttribute("class");
filter.className.baseVal = "foo";
eventChecker.expect("");
filter.className.baseVal = "foo";
filter.setAttribute("class", "foo");
// length attribute
eventChecker.watchAttr(marker, "markerWidth");
eventChecker.expect("add modify modify modify modify modify remove add");
marker.setAttribute("markerWidth", "12.5");
marker.markerWidth.baseVal.value = 8;
marker.markerWidth.baseVal.valueInSpecifiedUnits = 9;
marker.markerWidth.baseVal.valueAsString = "10";
marker.markerWidth.baseVal.convertToSpecifiedUnits(
SVGLength.SVG_LENGTHTYPE_CM);
marker.markerWidth.baseVal.newValueSpecifiedUnits(
SVGLength.SVG_LENGTHTYPE_MM, 11);
marker.removeAttribute("markerWidth");
marker.markerWidth.baseVal.value = 8;
eventChecker.expect("remove add modify");
marker.removeAttribute("markerWidth");
console.log(marker.getAttribute("markerWidth"));
marker.markerWidth.baseVal.convertToSpecifiedUnits(
SVGLength.SVG_LENGTHTYPE_NUMBER);
console.log(marker.getAttribute("markerWidth"));
marker.markerWidth.baseVal.value = 8;
eventChecker.expect("");
marker.markerWidth.baseVal.value = 8;
marker.setAttribute("markerWidth", "8");
marker.markerWidth.baseVal.value = 8;
marker.markerWidth.baseVal.valueAsString = "8";
marker.markerWidth.baseVal.convertToSpecifiedUnits(
SVGLength.SVG_LENGTHTYPE_NUMBER);
marker.markerWidth.baseVal.newValueSpecifiedUnits(
SVGLength.SVG_LENGTHTYPE_NUMBER, 8);
// number attribute
eventChecker.watchAttr(convolve, "divisor");
eventChecker.expect("add modify remove add");
convolve.setAttribute("divisor", "12.5");
convolve.divisor.baseVal = 6;
convolve.removeAttribute("divisor");
convolve.divisor.baseVal = 8;
eventChecker.expect("");
convolve.divisor.baseVal = 8;
convolve.setAttribute("divisor", "8");
// number-optional-number attribute
eventChecker.watchAttr(blur, "stdDeviation");
eventChecker.expect("add modify remove add");
blur.setAttribute("stdDeviation", "5, 6");
blur.stdDeviationX.baseVal = 8;
blur.removeAttribute("stdDeviation");
blur.setAttribute("stdDeviation", "2, 3");
eventChecker.expect("");
blur.stdDeviationX.baseVal = 2;
blur.stdDeviationY.baseVal = 3;
blur.setAttribute("stdDeviation", "2, 3");
// integer attribute
eventChecker.watchAttr(convolve, "targetX");
eventChecker.expect("add modify remove add");
convolve.setAttribute("targetX", "12");
convolve.targetX.baseVal = 6;
convolve.removeAttribute("targetX");
convolve.targetX.baseVal = 8;
// Check redundant change when comparing typed value to attribute value
eventChecker.expect("");
convolve.setAttribute("targetX", "8");
// Check redundant change when comparing attribute value to typed value
eventChecker.expect("remove add");
convolve.removeAttribute("targetX");
convolve.setAttribute("targetX", "8");
convolve.targetX.baseVal = 8;
// integer-optional-integer attribute
eventChecker.watchAttr(filter, "filterRes");
eventChecker.expect("add modify remove add");
filter.setAttribute("filterRes", "60, 70");
filter.filterResX.baseVal = 50;
filter.removeAttribute("filterRes");
filter.setAttribute("filterRes", "50, 60");
eventChecker.expect("");
filter.filterResX.baseVal = 50;
filter.setAttribute("filterRes", "50, 60");
filter.filterResY.baseVal = 60;
// angle attribute
eventChecker.watchAttr(marker, "orient");
eventChecker.expect("add modify modify modify modify modify remove add");
marker.setAttribute("orient", "90deg");
marker.orientAngle.baseVal.value = 12;
marker.orientAngle.baseVal.valueInSpecifiedUnits = 23;
marker.orientAngle.baseVal.valueAsString = "34";
marker.orientAngle.baseVal.newValueSpecifiedUnits(
SVGAngle.SVG_ANGLETYPE_GRAD, 34);
marker.orientAngle.baseVal.newValueSpecifiedUnits(
SVGAngle.SVG_ANGLETYPE_GRAD, 45);
marker.removeAttribute("orient");
marker.orientAngle.baseVal.value = 40;
eventChecker.expect("");
marker.orientAngle.baseVal.value = 40;
marker.orientAngle.baseVal.valueInSpecifiedUnits = 40;
marker.orientAngle.baseVal.valueAsString = "40";
marker.orientAngle.baseVal.convertToSpecifiedUnits(
SVGAngle.SVG_ANGLETYPE_UNSPECIFIED);
marker.orientAngle.baseVal.newValueSpecifiedUnits(
SVGAngle.SVG_ANGLETYPE_UNSPECIFIED, 40);
// boolean attribute
eventChecker.watchAttr(convolve, "preserveAlpha");
eventChecker.expect("add modify remove add");
convolve.setAttribute("preserveAlpha", "true");
convolve.preserveAlpha.baseVal = false;
convolve.removeAttribute("preserveAlpha");
convolve.preserveAlpha.baseVal = true;
eventChecker.expect("");
convolve.preserveAlpha.baseVal = true;
convolve.setAttribute("preserveAlpha", "true");
// enum attribute
eventChecker.watchAttr(convolve, "edgeMode");
eventChecker.expect("add modify remove add");
convolve.setAttribute("edgeMode", "none");
convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP;
convolve.removeAttribute("edgeMode");
convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
eventChecker.expect("");
convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
convolve.setAttribute("edgeMode", "none");
convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
// string attribute
eventChecker.watchAttr(convolve, "result");
eventChecker.expect("add modify remove add");
convolve.setAttribute("result", "bar");
convolve.result.baseVal = "foo";
convolve.removeAttribute("result");
convolve.result.baseVal = "bar";
eventChecker.expect("");
convolve.result.baseVal = "bar";
convolve.setAttribute("result", "bar");
convolve.result.baseVal = "bar";
// preserveAspectRatio attribute
eventChecker.watchAttr(marker, "preserveAspectRatio");
eventChecker.expect("add modify remove add");
marker.setAttribute("preserveAspectRatio", "xMaxYMid slice");
marker.preserveAspectRatio.baseVal.align =
SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMAX;
marker.removeAttribute("preserveAspectRatio");
marker.preserveAspectRatio.baseVal.align =
SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN;
eventChecker.expect("");
marker.preserveAspectRatio.baseVal.meetOrSlice =
SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET;
marker.setAttribute("preserveAspectRatio", "xMidYMin meet");
// viewBox attribute
eventChecker.watchAttr(marker, "viewBox");
eventChecker.expect("add modify remove add");
marker.setAttribute("viewBox", "1 2 3 4");
marker.viewBox.baseVal.height = 5;
marker.removeAttribute("viewBox");
marker.viewBox.baseVal.height = 4;
eventChecker.ignoreEvents();
marker.setAttribute("viewBox", "1 2 3 4");
eventChecker.expect("");
marker.viewBox.baseVal.height = 4;
marker.viewBox.baseVal.x = 1;
marker.setAttribute("viewBox", "1 2 3 4");
eventChecker.finish();
SimpleTest.finish();
}
window.addEventListener("load", runTests, false);
</script>
</pre>
</body>
</html>

View File

@ -4121,7 +4121,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
// Note: For now, display an alert instead of an error page if we have no
// URI object. Missing URI objects are handled badly by session history.
if (mUseErrorPages && aURI && aFailedChannel) {
if (mUseErrorPages && aURI) {
// Display an error page
LoadErrorPage(aURI, aURL, errorPage.get(), error.get(),
messageStr.get(), cssClass.get(), aFailedChannel);

View File

@ -97,6 +97,11 @@ DIRS += \
$(NULL)
endif
ifdef MOZ_B2G_BT
DIRS += \
bluetooth \
$(NULL)
endif
TEST_DIRS += tests
ifneq (,$(filter gtk2 cocoa windows android qt os2,$(MOZ_WIDGET_TOOLKIT)))
TEST_DIRS += plugins/test

View File

@ -80,6 +80,10 @@
#ifdef MOZ_B2G_RIL
#include "TelephonyFactory.h"
#endif
#ifdef MOZ_B2G_BT
#include "nsIDOMBluetoothAdapter.h"
#include "BluetoothAdapter.h"
#endif
// This should not be in the namespace.
DOMCI_DATA(Navigator, mozilla::dom::Navigator)
@ -133,6 +137,9 @@ NS_INTERFACE_MAP_BEGIN(Navigator)
NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorTelephony)
#endif
NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorNetwork)
#ifdef MOZ_B2G_BT
NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorBluetooth)
#endif
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Navigator)
NS_INTERFACE_MAP_END
@ -182,6 +189,12 @@ Navigator::Invalidate()
mConnection->Shutdown();
mConnection = nsnull;
}
#ifdef MOZ_B2G_BT
if (mBluetooth) {
mBluetooth = nsnull;
}
#endif
}
nsPIDOMWindow *
@ -1112,6 +1125,30 @@ Navigator::GetMozConnection(nsIDOMMozConnection** aConnection)
return NS_OK;
}
#ifdef MOZ_B2G_BT
//*****************************************************************************
// nsNavigator::nsIDOMNavigatorBluetooth
//*****************************************************************************
NS_IMETHODIMP
Navigator::GetMozBluetooth(nsIDOMBluetoothAdapter** aBluetooth)
{
nsCOMPtr<nsIDOMBluetoothAdapter> bluetooth = mBluetooth;
if (!bluetooth) {
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
mBluetooth = new bluetooth::BluetoothAdapter();
bluetooth = mBluetooth;
}
bluetooth.forget(aBluetooth);
return NS_OK;
}
#endif //MOZ_B2G_BT
PRInt64
Navigator::SizeOf() const
{

View File

@ -65,6 +65,11 @@ class nsIDOMMozConnection;
class nsIDOMTelephony;
#endif
#ifdef MOZ_B2G_BT
#include "nsIDOMNavigatorBluetooth.h"
#endif
class nsIDOMAdapter;
//*****************************************************************************
// Navigator: Script "navigator" object
//*****************************************************************************
@ -98,6 +103,9 @@ class Navigator : public nsIDOMNavigator
, public nsIDOMNavigatorTelephony
#endif
, public nsIDOMMozNavigatorNetwork
#ifdef MOZ_B2G_BT
, public nsIDOMNavigatorBluetooth
#endif
{
public:
Navigator(nsPIDOMWindow *aInnerWindow);
@ -115,6 +123,10 @@ public:
#endif
NS_DECL_NSIDOMMOZNAVIGATORNETWORK
#ifdef MOZ_B2G_BT
NS_DECL_NSIDOMNAVIGATORBLUETOOTH
#endif
static void Init();
void Invalidate();
@ -146,6 +158,9 @@ private:
nsCOMPtr<nsIDOMTelephony> mTelephony;
#endif
nsRefPtr<network::Connection> mConnection;
#ifdef MOZ_B2G_BT
nsCOMPtr<nsIDOMBluetoothAdapter> mBluetooth;
#endif
nsWeakPtr mWindow;
};

View File

@ -532,6 +532,10 @@ using mozilla::dom::indexedDB::IDBWrapperCache;
#include "CallEvent.h"
#endif
#ifdef MOZ_B2G_BT
#include "BluetoothAdapter.h"
#endif
#include "DOMError.h"
using namespace mozilla;
@ -1627,6 +1631,11 @@ static nsDOMClassInfoData sClassInfoData[] = {
DOM_DEFAULT_SCRIPTABLE_FLAGS)
#endif
#ifdef MOZ_B2G_BT
NS_DEFINE_CLASSINFO_DATA(BluetoothAdapter, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
#endif
NS_DEFINE_CLASSINFO_DATA(DOMError, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
};
@ -2423,6 +2432,9 @@ nsDOMClassInfo::Init()
#endif
DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMMozNavigatorNetwork,
network::IsAPIEnabled())
#ifdef MOZ_B2G_BT
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorBluetooth)
#endif
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(Plugin, nsIDOMPlugin)
@ -4365,6 +4377,12 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_END
#endif
#ifdef MOZ_B2G_BT
DOM_CLASSINFO_MAP_BEGIN(BluetoothAdapter, nsIDOMBluetoothAdapter)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMBluetoothAdapter)
DOM_CLASSINFO_MAP_END
#endif
DOM_CLASSINFO_MAP_BEGIN(DOMError, nsIDOMDOMError)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMError)
DOM_CLASSINFO_MAP_END

View File

@ -540,4 +540,8 @@ DOMCI_CLASS(TelephonyCall)
DOMCI_CLASS(CallEvent)
#endif
#ifdef MOZ_B2G_BT
DOMCI_CLASS(BluetoothAdapter)
#endif
DOMCI_CLASS(DOMError)

View File

@ -0,0 +1,39 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=40: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "BluetoothAdapter.h"
#include "nsDOMClassInfo.h"
USING_BLUETOOTH_NAMESPACE
BluetoothAdapter::BluetoothAdapter() : mPower(false)
{
}
NS_INTERFACE_MAP_BEGIN(BluetoothAdapter)
NS_INTERFACE_MAP_ENTRY(nsIDOMBluetoothAdapter)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BluetoothAdapter)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(BluetoothAdapter)
NS_IMPL_RELEASE(BluetoothAdapter)
DOMCI_DATA(BluetoothAdapter, BluetoothAdapter)
NS_IMETHODIMP
BluetoothAdapter::GetPower(bool* aPower)
{
*aPower = mPower;
return NS_OK;
}
NS_IMETHODIMP
BluetoothAdapter::SetPower(bool aPower)
{
mPower = aPower;
return NS_OK;
}

View File

@ -0,0 +1,28 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=40: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_bluetooth_bluetoothadapter_h__
#define mozilla_dom_bluetooth_bluetoothadapter_h__
#include "BluetoothCommon.h"
#include "nsIDOMBluetoothAdapter.h"
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothAdapter : public nsIDOMBluetoothAdapter
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMBLUETOOTHADAPTER
BluetoothAdapter();
protected:
bool mPower;
};
END_BLUETOOTH_NAMESPACE
#endif

View File

@ -0,0 +1,19 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=40: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_bluetooth_bluetoothcommon_h__
#define mozilla_dom_bluetooth_bluetoothcommon_h__
#define BEGIN_BLUETOOTH_NAMESPACE \
namespace mozilla { namespace dom { namespace bluetooth {
#define END_BLUETOOTH_NAMESPACE \
} /* namespace bluetooth */ } /* namespace dom */ } /* namespace mozilla */
#define USING_BLUETOOTH_NAMESPACE \
using namespace mozilla::dom::bluetooth;
class nsIDOMBluetooth;
#endif // mozilla_dom_bluetooth_bluetoothcommon_h__

30
dom/bluetooth/Makefile.in Normal file
View File

@ -0,0 +1,30 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = dom
LIBRARY_NAME = dombluetooth_s
XPIDL_MODULE = dom_bluetooth
LIBXUL_LIBRARY = 1
FORCE_STATIC_LIB = 1
include $(topsrcdir)/dom/dom-config.mk
CPPSRCS = \
BluetoothAdapter.cpp \
$(NULL)
XPIDLSRCS = \
nsIDOMNavigatorBluetooth.idl \
nsIDOMBluetoothAdapter.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,13 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=40: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
[scriptable, builtinclass, uuid(29689a22-45ff-4ccf-b552-5364ce3a3642)]
interface nsIDOMBluetoothAdapter : nsISupports
{
attribute boolean power;
};

View File

@ -0,0 +1,15 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=40: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIDOMBluetoothAdapter;
[scriptable, uuid(677f2c2d-c4d1-41ea-addc-21d30d0d3858)]
interface nsIDOMNavigatorBluetooth : nsISupports
{
readonly attribute nsIDOMBluetoothAdapter mozBluetooth;
};

View File

@ -31,5 +31,9 @@ DOM_SRCDIRS += \
$(NULL)
endif
ifdef MOZ_B2G_BT
DOM_SRCDIRS += dom/bluetooth
endif
LOCAL_INCLUDES += $(DOM_SRCDIRS:%=-I$(topsrcdir)/%)
DEFINES += -D_IMPL_NS_LAYOUT

View File

@ -52,14 +52,16 @@ using namespace mozilla;
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
#define ASSIGN(obj, name) (obj)->name = anp_native_window_##name
static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) {
static nsresult GetOwner(NPP instance, nsPluginInstanceOwner** owner) {
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
nsPluginInstanceOwner* owner;
if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
return NULL;
}
return pinst->GetOwner((nsIPluginInstanceOwner**)owner);
}
static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) {
nsRefPtr<nsPluginInstanceOwner> owner;
if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner))))
return NULL;
ANPNativeWindow window = owner->Layer()->GetNativeWindowForContent();
owner->Invalidate();
@ -68,12 +70,9 @@ static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) {
}
static void anp_native_window_invertPluginContent(NPP instance, bool isContentInverted) {
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
nsPluginInstanceOwner* owner;
if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
nsRefPtr<nsPluginInstanceOwner> owner;
if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner))))
return;
}
owner->Layer()->SetInverted(isContentInverted);
}

View File

@ -35,22 +35,23 @@
using namespace mozilla;
static AndroidMediaLayer* GetLayerForInstance(NPP instance) {
static nsresult GetOwner(NPP instance, nsPluginInstanceOwner** owner) {
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
nsPluginInstanceOwner* owner;
if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
return NULL;
}
return pinst->GetOwner((nsIPluginInstanceOwner**)owner);
}
static AndroidMediaLayer* GetLayerForInstance(NPP instance) {
nsRefPtr<nsPluginInstanceOwner> owner;
if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner))))
return NULL;
return owner->Layer();
}
static void Invalidate(NPP instance) {
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
nsPluginInstanceOwner* owner;
if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner)))
nsRefPtr<nsPluginInstanceOwner> owner;
if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner))))
return;
owner->Invalidate();

View File

@ -85,6 +85,12 @@ anp_window_requestCenterFitZoom(NPP instance)
NOT_IMPLEMENTED();
}
static nsresult GetOwner(NPP instance, nsPluginInstanceOwner** owner) {
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
return pinst->GetOwner((nsIPluginInstanceOwner**)owner);
}
ANPRectI
anp_window_visibleRect(NPP instance)
{
@ -92,8 +98,8 @@ anp_window_visibleRect(NPP instance)
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
nsPluginInstanceOwner* owner;
if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
nsRefPtr<nsPluginInstanceOwner> owner;
if (NS_FAILED(GetOwner(instance, getter_AddRefs(owner)))) {
return rect;
}

View File

@ -90,6 +90,7 @@ nsNPAPIPluginInstance::nsNPAPIPluginInstance(nsNPAPIPlugin* plugin)
#ifdef MOZ_WIDGET_ANDROID
mSurface(nsnull),
mANPDrawingModel(0),
mOnScreen(true),
#endif
mRunning(NOT_STARTED),
mWindowless(false),
@ -724,6 +725,44 @@ void nsNPAPIPluginInstance::SetEventModel(NPEventModel aModel)
#endif
#if defined(MOZ_WIDGET_ANDROID)
static void SendLifecycleEvent(nsNPAPIPluginInstance* aInstance, PRUint32 aAction)
{
ANPEvent event;
event.inSize = sizeof(ANPEvent);
event.eventType = kLifecycle_ANPEventType;
event.data.lifecycle.action = aAction;
aInstance->HandleEvent(&event, nsnull);
}
void nsNPAPIPluginInstance::NotifyForeground(bool aForeground)
{
PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetForeground this=%p\n foreground=%d",this, aForeground));
if (RUNNING != mRunning)
return;
SendLifecycleEvent(this, aForeground ? kResume_ANPLifecycleAction : kPause_ANPLifecycleAction);
}
void nsNPAPIPluginInstance::NotifyOnScreen(bool aOnScreen)
{
PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetOnScreen this=%p\n onScreen=%d",this, aOnScreen));
if (RUNNING != mRunning || mOnScreen == aOnScreen)
return;
mOnScreen = aOnScreen;
SendLifecycleEvent(this, aOnScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction);
}
void nsNPAPIPluginInstance::MemoryPressure()
{
PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::MemoryPressure this=%p\n",this));
if (RUNNING != mRunning)
return;
SendLifecycleEvent(this, kFreeMemory_ANPLifecycleAction);
}
void nsNPAPIPluginInstance::SetANPDrawingModel(PRUint32 aModel)
{
mANPDrawingModel = aModel;

View File

@ -147,6 +147,14 @@ public:
#endif
#ifdef MOZ_WIDGET_ANDROID
void NotifyForeground(bool aForeground);
void NotifyOnScreen(bool aOnScreen);
void MemoryPressure();
bool IsOnScreen() {
return mOnScreen;
}
PRUint32 GetANPDrawingModel() { return mANPDrawingModel; }
void SetANPDrawingModel(PRUint32 aModel);
@ -282,6 +290,7 @@ private:
bool mUsePluginLayersPref;
#ifdef MOZ_WIDGET_ANDROID
void* mSurface;
bool mOnScreen;
#endif
};

View File

@ -375,6 +375,10 @@ nsPluginHost::nsPluginHost()
if (obsService) {
obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
obsService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, false);
#ifdef MOZ_WIDGET_ANDROID
obsService->AddObserver(this, "application-foreground", false);
obsService->AddObserver(this, "application-background", false);
#endif
}
#ifdef PLUGIN_LOGGING
@ -3381,6 +3385,24 @@ NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
mInstances[i]->PrivateModeStateChanged();
}
}
#ifdef MOZ_WIDGET_ANDROID
if (!nsCRT::strcmp("application-background", aTopic)) {
for(PRUint32 i = 0; i < mInstances.Length(); i++) {
mInstances[i]->NotifyForeground(false);
}
}
if (!nsCRT::strcmp("application-foreground", aTopic)) {
for(PRUint32 i = 0; i < mInstances.Length(); i++) {
if (mInstances[i]->IsOnScreen())
mInstances[i]->NotifyForeground(true);
}
}
if (!nsCRT::strcmp("memory-pressure", aTopic)) {
for(PRUint32 i = 0; i < mInstances.Length(); i++) {
mInstances[i]->MemoryPressure();
}
}
#endif
return NS_OK;
}

Some files were not shown because too many files have changed in this diff Show More