Merge mozilla-central to mozilla-inbound
|
@ -52,10 +52,10 @@ TEST_FILES = \
|
||||||
exceptions_in_success_events_iframe.html \
|
exceptions_in_success_events_iframe.html \
|
||||||
helpers.js \
|
helpers.js \
|
||||||
leaving_page_iframe.html \
|
leaving_page_iframe.html \
|
||||||
|
test_add_put.html \
|
||||||
test_add_twice_failure.html \
|
test_add_twice_failure.html \
|
||||||
test_advance.html \
|
test_advance.html \
|
||||||
test_autoIncrement_indexes.html \
|
test_autoIncrement_indexes.html \
|
||||||
test_bad_keypath.html \
|
|
||||||
test_bfcache.html \
|
test_bfcache.html \
|
||||||
test_clear.html \
|
test_clear.html \
|
||||||
test_cmp.html \
|
test_cmp.html \
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
<!--
|
||||||
|
Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Indexed Database Property Test</title>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
|
||||||
|
<script type="text/javascript;version=1.7">
|
||||||
|
function testSteps()
|
||||||
|
{
|
||||||
|
const name = window.location.pathname;
|
||||||
|
let openRequest = mozIndexedDB.open(name, 1);
|
||||||
|
openRequest.onerror = errorHandler;
|
||||||
|
openRequest.onupgradeneeded = grabEventAndContinueHandler;
|
||||||
|
openRequest.onsuccess = unexpectedSuccessHandler;
|
||||||
|
let event = yield;
|
||||||
|
let db = event.target.result;
|
||||||
|
let trans = event.target.transaction;
|
||||||
|
|
||||||
|
for each (let autoincrement in [true, false]) {
|
||||||
|
for each (let keypath in [false, true, "missing", "invalid"]) {
|
||||||
|
for each (let method in ["put", "add"]) {
|
||||||
|
for each (let explicit in [true, false, undefined, "invalid"]) {
|
||||||
|
for each (let existing in [true, false]) {
|
||||||
|
let speccedNoKey = (keypath == false || keypath == "missing") &&
|
||||||
|
!explicit;
|
||||||
|
|
||||||
|
// We can't do 'existing' checks if we use autogenerated key
|
||||||
|
if (speccedNoKey && autoincrement && existing) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create store
|
||||||
|
if (db.objectStoreNames.contains("mystore"))
|
||||||
|
db.deleteObjectStore("mystore");
|
||||||
|
let store = db.createObjectStore("mystore",
|
||||||
|
{ autoIncrement: autoincrement,
|
||||||
|
keyPath: (keypath ? "id" : null) });
|
||||||
|
|
||||||
|
test = " for test " + JSON.stringify({ autoincrement: autoincrement,
|
||||||
|
keypath: keypath,
|
||||||
|
method: method,
|
||||||
|
explicit: explicit === undefined ? "undefined" : explicit,
|
||||||
|
existing: existing });
|
||||||
|
|
||||||
|
// Insert "existing" data if needed
|
||||||
|
if (existing) {
|
||||||
|
if (keypath)
|
||||||
|
store.add({ existing: "data", id: 5 }).onsuccess = grabEventAndContinueHandler;
|
||||||
|
else
|
||||||
|
store.add({ existing: "data" }, 5).onsuccess = grabEventAndContinueHandler;
|
||||||
|
|
||||||
|
let e = yield;
|
||||||
|
is(e.type, "success", "success inserting existing" + test);
|
||||||
|
is(e.target.result, 5, "inserted correct key" + test);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up value to be inserted
|
||||||
|
let value = { theObj: true };
|
||||||
|
if (keypath === true) {
|
||||||
|
value.id = 5;
|
||||||
|
}
|
||||||
|
else if (keypath === "invalid") {
|
||||||
|
value.id = /x/;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Which arguments are passed to function
|
||||||
|
args = [value];
|
||||||
|
if (explicit === true) {
|
||||||
|
args.push(5);
|
||||||
|
}
|
||||||
|
else if (explicit === undefined) {
|
||||||
|
args.push(undefined);
|
||||||
|
}
|
||||||
|
else if (explicit === "invalid") {
|
||||||
|
args.push(/x/);
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected = expectedResult(method, keypath, explicit, autoincrement, existing);
|
||||||
|
|
||||||
|
ok(true, "making call" + test);
|
||||||
|
|
||||||
|
// Make function call for throwing functions
|
||||||
|
if (expected === "throw") {
|
||||||
|
try {
|
||||||
|
store[method].apply(store, args);
|
||||||
|
ok(false, "should have thrown" + test);
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
ok(true, "did throw" + test);
|
||||||
|
ok(ex instanceof IDBDatabaseException, "Got a IDBDatabaseException" + test);
|
||||||
|
is(ex.code, IDBDatabaseException.DATA_ERR, "expect a DATA_ERR" + test);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make non-throwing function call
|
||||||
|
let req = store[method].apply(store, args);
|
||||||
|
req.onsuccess = req.onerror = grabEventAndContinueHandler
|
||||||
|
let e = yield;
|
||||||
|
|
||||||
|
// Figure out what key we used
|
||||||
|
let key = 5;
|
||||||
|
if (autoincrement && speccedNoKey) {
|
||||||
|
key = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust value if expected
|
||||||
|
if (autoincrement && keypath && speccedNoKey) {
|
||||||
|
value.id = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check result
|
||||||
|
if (expected === "error") {
|
||||||
|
is(e.type, "error", "write should fail" + test);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
is(e.type, "success", "write should succeed" + test);
|
||||||
|
if (autoincrement && speccedNoKey) {
|
||||||
|
todo_is(e.target.result, key, "(fix ai) write should return correct key" + test);
|
||||||
|
key = e.target.result;
|
||||||
|
if (keypath) {
|
||||||
|
value.id = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
is(e.target.result, key, "write should return correct key" + test);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.get(key).onsuccess = grabEventAndContinueHandler;
|
||||||
|
e = yield;
|
||||||
|
is(e.type, "success", "read back should succeed" + test);
|
||||||
|
is(JSON.stringify(e.target.result),
|
||||||
|
JSON.stringify(value),
|
||||||
|
"read back should return correct value" + test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function expectedResult(method, keypath, explicit, autoincrement, existing) {
|
||||||
|
if (keypath && explicit)
|
||||||
|
return "throw";
|
||||||
|
if (!keypath && !explicit && !autoincrement)
|
||||||
|
return "throw";
|
||||||
|
if (keypath == "invalid")
|
||||||
|
return "throw";
|
||||||
|
if (keypath == "missing" && !autoincrement)
|
||||||
|
return "throw";
|
||||||
|
if (explicit == "invalid")
|
||||||
|
return "throw";
|
||||||
|
|
||||||
|
if (method == "add" && existing)
|
||||||
|
return "error";
|
||||||
|
|
||||||
|
return "success";
|
||||||
|
}
|
||||||
|
|
||||||
|
openRequest.onsuccess = grabEventAndContinueHandler;
|
||||||
|
yield;
|
||||||
|
|
||||||
|
finishTest();
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="runTest();"></body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -1,49 +0,0 @@
|
||||||
<!--
|
|
||||||
Any copyright is dedicated to the Public Domain.
|
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/
|
|
||||||
-->
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Indexed Database Property Test</title>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
|
||||||
|
|
||||||
<script type="text/javascript;version=1.7">
|
|
||||||
function testSteps()
|
|
||||||
{
|
|
||||||
const name = window.location.pathname;
|
|
||||||
const description = "My Test Database";
|
|
||||||
|
|
||||||
let request = mozIndexedDB.open(name, 1, description);
|
|
||||||
request.onerror = errorHandler;
|
|
||||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
|
||||||
let event = yield;
|
|
||||||
|
|
||||||
let db = request.result;
|
|
||||||
|
|
||||||
let objectStore = db.createObjectStore("foo", { keyPath: "keyPath" });
|
|
||||||
|
|
||||||
request = objectStore.add({keyPath:"foo"});
|
|
||||||
request.onerror = errorHandler;
|
|
||||||
request.onsuccess = grabEventAndContinueHandler;
|
|
||||||
event = yield;
|
|
||||||
|
|
||||||
try {
|
|
||||||
request = objectStore.add({});
|
|
||||||
ok(false, "Shouldn't get here!");
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
is(e.code, IDBDatabaseException.DATA_ERR, "Good error");
|
|
||||||
}
|
|
||||||
|
|
||||||
finishTest();
|
|
||||||
yield;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body onload="runTest();"></body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -174,8 +174,9 @@ NS_IMETHODIMP nsBMPEncoder::StartImageEncode(PRUint32 aWidth,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the image buffer size
|
// Returns the number of bytes in the image buffer used.
|
||||||
NS_IMETHODIMP nsBMPEncoder::GetImageBufferSize(PRUint32 *aOutputSize)
|
// For a BMP file, this is all bytes in the buffer.
|
||||||
|
NS_IMETHODIMP nsBMPEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG_POINTER(aOutputSize);
|
NS_ENSURE_ARG_POINTER(aOutputSize);
|
||||||
*aOutputSize = mImageBufferSize;
|
*aOutputSize = mImageBufferSize;
|
||||||
|
|
|
@ -111,9 +111,10 @@ NS_IMETHODIMP nsICOEncoder::InitFromData(const PRUint8* aData,
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the image buffer size
|
// Returns the number of bytes in the image buffer used
|
||||||
|
// For an ICO file, this is all bytes in the buffer.
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsICOEncoder::GetImageBufferSize(PRUint32 *aOutputSize)
|
nsICOEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG_POINTER(aOutputSize);
|
NS_ENSURE_ARG_POINTER(aOutputSize);
|
||||||
*aOutputSize = mImageBufferSize;
|
*aOutputSize = mImageBufferSize;
|
||||||
|
@ -147,16 +148,16 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
|
||||||
aStride, aInputFormat, noParams);
|
aStride, aInputFormat, noParams);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
PRUint32 imageBufferSize;
|
PRUint32 PNGImageBufferSize;
|
||||||
mContainedEncoder->GetImageBufferSize(&imageBufferSize);
|
mContainedEncoder->GetImageBufferUsed(&PNGImageBufferSize);
|
||||||
mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE +
|
mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE +
|
||||||
imageBufferSize;
|
PNGImageBufferSize;
|
||||||
mImageBufferStart = static_cast<PRUint8*>(moz_malloc(mImageBufferSize));
|
mImageBufferStart = static_cast<PRUint8*>(moz_malloc(mImageBufferSize));
|
||||||
if (!mImageBufferStart) {
|
if (!mImageBufferStart) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
mImageBufferCurr = mImageBufferStart;
|
mImageBufferCurr = mImageBufferStart;
|
||||||
mICODirEntry.mBytesInRes = imageBufferSize;
|
mICODirEntry.mBytesInRes = PNGImageBufferSize;
|
||||||
|
|
||||||
EncodeFileHeader();
|
EncodeFileHeader();
|
||||||
EncodeInfoHeader();
|
EncodeInfoHeader();
|
||||||
|
@ -164,8 +165,8 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
|
||||||
char *imageBuffer;
|
char *imageBuffer;
|
||||||
rv = mContainedEncoder->GetImageBuffer(&imageBuffer);
|
rv = mContainedEncoder->GetImageBuffer(&imageBuffer);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
memcpy(mImageBufferCurr, imageBuffer, imageBufferSize);
|
memcpy(mImageBufferCurr, imageBuffer, PNGImageBufferSize);
|
||||||
mImageBufferCurr += imageBufferSize;
|
mImageBufferCurr += PNGImageBufferSize;
|
||||||
} else {
|
} else {
|
||||||
mContainedEncoder = new nsBMPEncoder();
|
mContainedEncoder = new nsBMPEncoder();
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
@ -181,10 +182,10 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
|
||||||
PRUint32 andMaskSize = ((GetRealWidth() + 31) / 32) * 4 * // row AND mask
|
PRUint32 andMaskSize = ((GetRealWidth() + 31) / 32) * 4 * // row AND mask
|
||||||
GetRealHeight(); // num rows
|
GetRealHeight(); // num rows
|
||||||
|
|
||||||
PRUint32 imageBufferSize;
|
PRUint32 BMPImageBufferSize;
|
||||||
mContainedEncoder->GetImageBufferSize(&imageBufferSize);
|
mContainedEncoder->GetImageBufferUsed(&BMPImageBufferSize);
|
||||||
mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE +
|
mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE +
|
||||||
imageBufferSize + andMaskSize;
|
BMPImageBufferSize + andMaskSize;
|
||||||
mImageBufferStart = static_cast<PRUint8*>(moz_malloc(mImageBufferSize));
|
mImageBufferStart = static_cast<PRUint8*>(moz_malloc(mImageBufferSize));
|
||||||
if (!mImageBufferStart) {
|
if (!mImageBufferStart) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
@ -192,7 +193,7 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
|
||||||
mImageBufferCurr = mImageBufferStart;
|
mImageBufferCurr = mImageBufferStart;
|
||||||
|
|
||||||
// The icon buffer does not include the BFH at all.
|
// The icon buffer does not include the BFH at all.
|
||||||
mICODirEntry.mBytesInRes = imageBufferSize - BFH_LENGTH + andMaskSize;
|
mICODirEntry.mBytesInRes = BMPImageBufferSize - BFH_LENGTH + andMaskSize;
|
||||||
|
|
||||||
// Encode the icon headers
|
// Encode the icon headers
|
||||||
EncodeFileHeader();
|
EncodeFileHeader();
|
||||||
|
@ -202,13 +203,13 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
|
||||||
rv = mContainedEncoder->GetImageBuffer(&imageBuffer);
|
rv = mContainedEncoder->GetImageBuffer(&imageBuffer);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
memcpy(mImageBufferCurr, imageBuffer + BFH_LENGTH,
|
memcpy(mImageBufferCurr, imageBuffer + BFH_LENGTH,
|
||||||
imageBufferSize - BFH_LENGTH);
|
BMPImageBufferSize - BFH_LENGTH);
|
||||||
// We need to fix the BMP height to be *2 for the AND mask
|
// We need to fix the BMP height to be *2 for the AND mask
|
||||||
PRUint32 fixedHeight = GetRealHeight() * 2;
|
PRUint32 fixedHeight = GetRealHeight() * 2;
|
||||||
fixedHeight = NATIVE32_TO_LITTLE(fixedHeight);
|
fixedHeight = NATIVE32_TO_LITTLE(fixedHeight);
|
||||||
// The height is stored at an offset of 8 from the DIB header
|
// The height is stored at an offset of 8 from the DIB header
|
||||||
memcpy(mImageBufferCurr + 8, &fixedHeight, sizeof(fixedHeight));
|
memcpy(mImageBufferCurr + 8, &fixedHeight, sizeof(fixedHeight));
|
||||||
mImageBufferCurr += imageBufferSize - BFH_LENGTH;
|
mImageBufferCurr += BMPImageBufferSize - BFH_LENGTH;
|
||||||
|
|
||||||
// Calculate rowsize in DWORD's
|
// Calculate rowsize in DWORD's
|
||||||
PRUint32 rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
|
PRUint32 rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
|
||||||
|
|
|
@ -221,11 +221,11 @@ NS_IMETHODIMP nsJPEGEncoder::StartImageEncode(PRUint32 aWidth,
|
||||||
return NS_ERROR_NOT_IMPLEMENTED;
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the image buffer size
|
// Returns the number of bytes in the image buffer used.
|
||||||
NS_IMETHODIMP nsJPEGEncoder::GetImageBufferSize(PRUint32 *aOutputSize)
|
NS_IMETHODIMP nsJPEGEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG_POINTER(aOutputSize);
|
NS_ENSURE_ARG_POINTER(aOutputSize);
|
||||||
*aOutputSize = mImageBufferSize;
|
*aOutputSize = mImageBufferUsed;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -206,11 +206,11 @@ NS_IMETHODIMP nsPNGEncoder::StartImageEncode(PRUint32 aWidth,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the image buffer size
|
// Returns the number of bytes in the image buffer used.
|
||||||
NS_IMETHODIMP nsPNGEncoder::GetImageBufferSize(PRUint32 *aOutputSize)
|
NS_IMETHODIMP nsPNGEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG_POINTER(aOutputSize);
|
NS_ENSURE_ARG_POINTER(aOutputSize);
|
||||||
*aOutputSize = mImageBufferSize;
|
*aOutputSize = mImageBufferUsed;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
/**
|
/**
|
||||||
* imgIEncoder interface
|
* imgIEncoder interface
|
||||||
*/
|
*/
|
||||||
[scriptable, uuid(d02e2d95-072d-4b91-8b8f-1300e45fcb2b)]
|
[scriptable, uuid(4baa2d6e-fee7-42df-ae3f-5fbebc0c267c)]
|
||||||
interface imgIEncoder : nsIAsyncInputStream
|
interface imgIEncoder : nsIAsyncInputStream
|
||||||
{
|
{
|
||||||
// Possible values for outputOptions. Multiple values are semicolon-separated.
|
// Possible values for outputOptions. Multiple values are semicolon-separated.
|
||||||
|
@ -159,8 +159,9 @@ interface imgIEncoder : nsIAsyncInputStream
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sometimes an encoder can contain another encoder and direct access
|
* Sometimes an encoder can contain another encoder and direct access
|
||||||
* to its buffer is necessary.
|
* to its buffer is necessary. It is only safe to assume that the buffer
|
||||||
|
* returned from getImageBuffer() is of size equal to getImageBufferUsed().
|
||||||
*/
|
*/
|
||||||
[noscript] unsigned long getImageBufferSize();
|
[noscript] unsigned long getImageBufferUsed();
|
||||||
[noscript] charPtr getImageBuffer();
|
[noscript] charPtr getImageBuffer();
|
||||||
};
|
};
|
||||||
|
|
|
@ -2788,7 +2788,8 @@ EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool callContex
|
||||||
if (op == JSOP_ARGUMENTS || op == JSOP_CALLEE) {
|
if (op == JSOP_ARGUMENTS || op == JSOP_CALLEE) {
|
||||||
if (Emit1(cx, bce, op) < 0)
|
if (Emit1(cx, bce, op) < 0)
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
if (callContext && Emit1(cx, bce, JSOP_PUSH) < 0)
|
/* Need to provide |this| value for call */
|
||||||
|
if (callContext && Emit1(cx, bce, JSOP_UNDEFINED) < 0)
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
} else {
|
} else {
|
||||||
if (!pn->pn_cookie.isFree()) {
|
if (!pn->pn_cookie.isFree()) {
|
||||||
|
@ -6075,7 +6076,8 @@ EmitReturn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||||
if (!EmitTree(cx, bce, pn2))
|
if (!EmitTree(cx, bce, pn2))
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (Emit1(cx, bce, JSOP_PUSH) < 0)
|
/* No explicit return value provided */
|
||||||
|
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6355,9 +6357,9 @@ EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||||
* in jsinterp.cpp for JSOP_LAMBDA followed by JSOP_{SET,INIT}PROP.
|
* in jsinterp.cpp for JSOP_LAMBDA followed by JSOP_{SET,INIT}PROP.
|
||||||
*
|
*
|
||||||
* Then (or in a call case that has no explicit reference-base
|
* Then (or in a call case that has no explicit reference-base
|
||||||
* object) we emit JSOP_PUSH to produce the |this| slot required
|
* object) we emit JSOP_UNDEFINED to produce the undefined |this|
|
||||||
* for calls (which non-strict mode functions will box into the
|
* value required for calls (which non-strict mode functions
|
||||||
* global object).
|
* will box into the global object).
|
||||||
*/
|
*/
|
||||||
ParseNode *pn2 = pn->pn_head;
|
ParseNode *pn2 = pn->pn_head;
|
||||||
switch (pn2->getKind()) {
|
switch (pn2->getKind()) {
|
||||||
|
@ -6379,16 +6381,16 @@ EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||||
JS_ASSERT(pn2->isOp(JSOP_XMLNAME));
|
JS_ASSERT(pn2->isOp(JSOP_XMLNAME));
|
||||||
if (!EmitXMLName(cx, pn2, JSOP_CALLXMLNAME, bce))
|
if (!EmitXMLName(cx, pn2, JSOP_CALLXMLNAME, bce))
|
||||||
return false;
|
return false;
|
||||||
callop = true; /* suppress JSOP_PUSH after */
|
callop = true; /* suppress JSOP_UNDEFINED after */
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
if (!EmitTree(cx, bce, pn2))
|
if (!EmitTree(cx, bce, pn2))
|
||||||
return false;
|
return false;
|
||||||
callop = false; /* trigger JSOP_PUSH after */
|
callop = false; /* trigger JSOP_UNDEFINED after */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!callop && Emit1(cx, bce, JSOP_PUSH) < 0)
|
if (!callop && Emit1(cx, bce, JSOP_UNDEFINED) < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Remember start of callable-object bytecode for decompilation hint. */
|
/* Remember start of callable-object bytecode for decompilation hint. */
|
||||||
|
@ -7027,7 +7029,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||||
if (!EmitTree(cx, bce, pn->pn_kid))
|
if (!EmitTree(cx, bce, pn->pn_kid))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
} else {
|
} else {
|
||||||
if (Emit1(cx, bce, JSOP_PUSH) < 0)
|
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
}
|
}
|
||||||
if (pn->pn_hidden && NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
|
if (pn->pn_hidden && NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/licenses/publicdomain/
|
||||||
|
*/
|
||||||
|
|
||||||
|
load(libdir + 'eqArrayHelper.js');
|
||||||
|
|
||||||
|
assertEqArray(eval('[]'), []);
|
||||||
|
assertEqArray(eval('[,]'), [,]);
|
||||||
|
assertEqArray(eval('[,,]'), [,,]);
|
||||||
|
assertEqArray(eval('[1, 1, ]'), [1,1, ]);
|
||||||
|
assertEqArray(eval('[1, 1, true]'), [1, 1, true]);
|
||||||
|
assertEqArray(eval('[1, false, true]'), [1, false, true]);
|
|
@ -1,4 +1,4 @@
|
||||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
*
|
*
|
||||||
* ***** BEGIN LICENSE BLOCK *****
|
* ***** BEGIN LICENSE BLOCK *****
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
@ -102,8 +102,7 @@ js_InitArrayClass(JSContext *cx, JSObject *obj);
|
||||||
extern bool
|
extern bool
|
||||||
js_InitContextBusyArrayTable(JSContext *cx);
|
js_InitContextBusyArrayTable(JSContext *cx);
|
||||||
|
|
||||||
namespace js
|
namespace js {
|
||||||
{
|
|
||||||
|
|
||||||
/* Create a dense array with no capacity allocated, length set to 0. */
|
/* Create a dense array with no capacity allocated, length set to 0. */
|
||||||
extern JSObject * JS_FASTCALL
|
extern JSObject * JS_FASTCALL
|
||||||
|
@ -136,7 +135,7 @@ NewDenseCopiedArray(JSContext *cx, uint32 length, const Value *vp, JSObject *pro
|
||||||
extern JSObject *
|
extern JSObject *
|
||||||
NewSlowEmptyArray(JSContext *cx);
|
NewSlowEmptyArray(JSContext *cx);
|
||||||
|
|
||||||
}
|
} /* namespace js */
|
||||||
|
|
||||||
extern JSBool
|
extern JSBool
|
||||||
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
|
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
|
||||||
|
@ -162,10 +161,7 @@ array_deleteElement(JSContext *cx, JSObject *obj, uint32 index, Value *rval, JSB
|
||||||
extern bool
|
extern bool
|
||||||
GetElements(JSContext *cx, JSObject *aobj, jsuint length, js::Value *vp);
|
GetElements(JSContext *cx, JSObject *aobj, jsuint length, js::Value *vp);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Natives exposed for optimization by the interpreter and JITs. */
|
/* Natives exposed for optimization by the interpreter and JITs. */
|
||||||
namespace js {
|
|
||||||
|
|
||||||
extern JSBool
|
extern JSBool
|
||||||
array_sort(JSContext *cx, uintN argc, js::Value *vp);
|
array_sort(JSContext *cx, uintN argc, js::Value *vp);
|
||||||
|
@ -213,7 +209,4 @@ js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id,
|
||||||
JSBool
|
JSBool
|
||||||
js_Array(JSContext *cx, uintN argc, js::Value *vp);
|
js_Array(JSContext *cx, uintN argc, js::Value *vp);
|
||||||
|
|
||||||
extern JSBool JS_FASTCALL
|
|
||||||
js_EnsureDenseArrayCapacity(JSContext *cx, JSObject *obj, jsint i);
|
|
||||||
|
|
||||||
#endif /* jsarray_h___ */
|
#endif /* jsarray_h___ */
|
||||||
|
|
|
@ -697,15 +697,6 @@ CreateEvalCallObject(JSContext *cx, StackFrame *fp)
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
JSObject * JS_FASTCALL
|
|
||||||
js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain)
|
|
||||||
{
|
|
||||||
JS_ASSERT(!js_IsNamedLambda(fun));
|
|
||||||
JS_ASSERT(scopeChain);
|
|
||||||
JS_ASSERT(callee);
|
|
||||||
return CallObject::create(cx, fun->script(), *scopeChain, callee);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
js_PutCallObject(StackFrame *fp)
|
js_PutCallObject(StackFrame *fp)
|
||||||
{
|
{
|
||||||
|
|
|
@ -312,9 +312,6 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
|
||||||
uintN flags, JSObject *parent, JSAtom *atom,
|
uintN flags, JSObject *parent, JSAtom *atom,
|
||||||
js::gc::AllocKind kind = JSFunction::FinalizeKind);
|
js::gc::AllocKind kind = JSFunction::FinalizeKind);
|
||||||
|
|
||||||
extern void
|
|
||||||
js_FinalizeFunction(JSContext *cx, JSFunction *fun);
|
|
||||||
|
|
||||||
extern JSFunction * JS_FASTCALL
|
extern JSFunction * JS_FASTCALL
|
||||||
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto,
|
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto,
|
||||||
js::gc::AllocKind kind = JSFunction::FinalizeKind);
|
js::gc::AllocKind kind = JSFunction::FinalizeKind);
|
||||||
|
@ -345,16 +342,9 @@ js_ValueToCallableObject(JSContext *cx, js::Value *vp, uintN flags);
|
||||||
extern void
|
extern void
|
||||||
js_ReportIsNotFunction(JSContext *cx, const js::Value *vp, uintN flags);
|
js_ReportIsNotFunction(JSContext *cx, const js::Value *vp, uintN flags);
|
||||||
|
|
||||||
extern JSObject * JS_FASTCALL
|
|
||||||
js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain);
|
|
||||||
|
|
||||||
extern void
|
extern void
|
||||||
js_PutCallObject(js::StackFrame *fp);
|
js_PutCallObject(js::StackFrame *fp);
|
||||||
|
|
||||||
extern JSBool JS_FASTCALL
|
|
||||||
js_PutCallObjectOnTrace(JSObject *scopeChain, uint32 nargs, js::Value *argv,
|
|
||||||
uint32 nvars, js::Value *slots);
|
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
CallObject *
|
CallObject *
|
||||||
|
|
|
@ -3421,7 +3421,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
||||||
|
|
||||||
/* Bytecodes pushing values of known type. */
|
/* Bytecodes pushing values of known type. */
|
||||||
case JSOP_VOID:
|
case JSOP_VOID:
|
||||||
case JSOP_PUSH:
|
case JSOP_UNDEFINED:
|
||||||
pushed[0].addType(cx, Type::UndefinedType());
|
pushed[0].addType(cx, Type::UndefinedType());
|
||||||
break;
|
break;
|
||||||
case JSOP_ZERO:
|
case JSOP_ZERO:
|
||||||
|
|
|
@ -1995,9 +1995,9 @@ END_CASE(JSOP_LINENO)
|
||||||
BEGIN_CASE(JSOP_BLOCKCHAIN)
|
BEGIN_CASE(JSOP_BLOCKCHAIN)
|
||||||
END_CASE(JSOP_BLOCKCHAIN)
|
END_CASE(JSOP_BLOCKCHAIN)
|
||||||
|
|
||||||
BEGIN_CASE(JSOP_PUSH)
|
BEGIN_CASE(JSOP_UNDEFINED)
|
||||||
PUSH_UNDEFINED();
|
PUSH_UNDEFINED();
|
||||||
END_CASE(JSOP_PUSH)
|
END_CASE(JSOP_UNDEFINED)
|
||||||
|
|
||||||
BEGIN_CASE(JSOP_POP)
|
BEGIN_CASE(JSOP_POP)
|
||||||
regs.sp--;
|
regs.sp--;
|
||||||
|
|
|
@ -1191,7 +1191,7 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
|
||||||
size_t length = linearStr->length();
|
size_t length = linearStr->length();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the eval string starts with '(' and ends with ')', it may be JSON.
|
* If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
|
||||||
* Try the JSON parser first because it's much faster. If the eval string
|
* Try the JSON parser first because it's much faster. If the eval string
|
||||||
* isn't JSON, JSON parsing will probably fail quickly, so little time
|
* isn't JSON, JSON parsing will probably fail quickly, so little time
|
||||||
* will be lost.
|
* will be lost.
|
||||||
|
@ -1202,8 +1202,9 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
|
||||||
* JSON with eval and using strict mode, you deserve to be slow.
|
* JSON with eval and using strict mode, you deserve to be slow.
|
||||||
*/
|
*/
|
||||||
if (length > 2 &&
|
if (length > 2 &&
|
||||||
chars[0] == '(' && chars[length - 1] == ')' &&
|
((chars[0] == '[' && chars[length - 1] == ']') ||
|
||||||
(!caller || !caller->script()->strictModeCode))
|
(chars[0] == '(' && chars[length - 1] == ')')) &&
|
||||||
|
(!caller || !caller->script()->strictModeCode))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Remarkably, JavaScript syntax is not a superset of JSON syntax:
|
* Remarkably, JavaScript syntax is not a superset of JSON syntax:
|
||||||
|
@ -1218,7 +1219,8 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (cp == end) {
|
if (cp == end) {
|
||||||
JSONParser parser(cx, chars + 1, length - 2,
|
bool isArray = (chars[0] == '[');
|
||||||
|
JSONParser parser(cx, isArray ? chars : chars + 1, isArray ? length : length - 2,
|
||||||
JSONParser::StrictJSON, JSONParser::NoError);
|
JSONParser::StrictJSON, JSONParser::NoError);
|
||||||
Value tmp;
|
Value tmp;
|
||||||
if (!parser.parse(&tmp))
|
if (!parser.parse(&tmp))
|
||||||
|
|
|
@ -1917,7 +1917,7 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
|
||||||
const char *rval;
|
const char *rval;
|
||||||
|
|
||||||
LOAD_OP_DATA(pc);
|
LOAD_OP_DATA(pc);
|
||||||
LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL);
|
LOCAL_ASSERT(op == JSOP_GETLOCAL);
|
||||||
|
|
||||||
todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
|
todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
|
||||||
if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
|
if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
|
||||||
|
@ -1934,7 +1934,7 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
|
||||||
if (pc == endpc)
|
if (pc == endpc)
|
||||||
return pc;
|
return pc;
|
||||||
LOAD_OP_DATA(pc);
|
LOAD_OP_DATA(pc);
|
||||||
if (op != JSOP_PUSH && op != JSOP_GETLOCAL)
|
if (op != JSOP_GETLOCAL)
|
||||||
break;
|
break;
|
||||||
if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
|
if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2548,20 +2548,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSOP_PUSH:
|
|
||||||
#if JS_HAS_DESTRUCTURING
|
|
||||||
sn = js_GetSrcNote(jp->script, pc);
|
|
||||||
if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
|
|
||||||
pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
|
|
||||||
if (!pc)
|
|
||||||
return NULL;
|
|
||||||
LOCAL_ASSERT(*pc == JSOP_POPN);
|
|
||||||
len = oplen = JSOP_POPN_LENGTH;
|
|
||||||
goto end_groupassignment;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/* FALL THROUGH */
|
|
||||||
|
|
||||||
case JSOP_BINDNAME:
|
case JSOP_BINDNAME:
|
||||||
case JSOP_BINDGNAME:
|
case JSOP_BINDGNAME:
|
||||||
todo = Sprint(&ss->sprinter, "");
|
todo = Sprint(&ss->sprinter, "");
|
||||||
|
@ -4193,8 +4179,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||||
LOCAL_ASSERT(*pc == JSOP_NULLBLOCKCHAIN);
|
LOCAL_ASSERT(*pc == JSOP_NULLBLOCKCHAIN);
|
||||||
pc += JSOP_NULLBLOCKCHAIN_LENGTH;
|
pc += JSOP_NULLBLOCKCHAIN_LENGTH;
|
||||||
}
|
}
|
||||||
LOCAL_ASSERT(*pc == JSOP_PUSH);
|
LOCAL_ASSERT(*pc == JSOP_UNDEFINED);
|
||||||
pc += JSOP_PUSH_LENGTH;
|
pc += JSOP_UNDEFINED_LENGTH;
|
||||||
LOCAL_ASSERT(*pc == JSOP_CALL);
|
LOCAL_ASSERT(*pc == JSOP_CALL);
|
||||||
LOCAL_ASSERT(GET_ARGC(pc) == 0);
|
LOCAL_ASSERT(GET_ARGC(pc) == 0);
|
||||||
len = JSOP_CALL_LENGTH;
|
len = JSOP_CALL_LENGTH;
|
||||||
|
@ -5225,10 +5211,6 @@ DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
|
||||||
JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX &&
|
JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX &&
|
||||||
op != JSOP_DUP && op != JSOP_DUP2);
|
op != JSOP_DUP && op != JSOP_DUP2);
|
||||||
|
|
||||||
/* JSOP_PUSH is used to generate undefined for group assignment holes. */
|
|
||||||
if (op == JSOP_PUSH)
|
|
||||||
return JS_strdup(cx, js_undefined_str);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* |this| could convert to a very long object initialiser, so cite it by
|
* |this| could convert to a very long object initialiser, so cite it by
|
||||||
* its keyword name instead.
|
* its keyword name instead.
|
||||||
|
|
|
@ -113,7 +113,7 @@
|
||||||
OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE)
|
OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||||
|
|
||||||
/* Long-standing JavaScript bytecodes. */
|
/* Long-standing JavaScript bytecodes. */
|
||||||
OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE)
|
OPDEF(JSOP_UNDEFINED, 1, js_undefined_str, "", 1, 0, 1, 0, JOF_BYTE)
|
||||||
OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE)
|
OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE)
|
||||||
OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE|JOF_PARENHEAD)
|
OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE|JOF_PARENHEAD)
|
||||||
OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE)
|
OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE)
|
||||||
|
|
|
@ -1681,9 +1681,9 @@ mjit::Compiler::generateMethod()
|
||||||
BEGIN_CASE(JSOP_NOP)
|
BEGIN_CASE(JSOP_NOP)
|
||||||
END_CASE(JSOP_NOP)
|
END_CASE(JSOP_NOP)
|
||||||
|
|
||||||
BEGIN_CASE(JSOP_PUSH)
|
BEGIN_CASE(JSOP_UNDEFINED)
|
||||||
frame.push(UndefinedValue());
|
frame.push(UndefinedValue());
|
||||||
END_CASE(JSOP_PUSH)
|
END_CASE(JSOP_UNDEFINED)
|
||||||
|
|
||||||
BEGIN_CASE(JSOP_POPV)
|
BEGIN_CASE(JSOP_POPV)
|
||||||
BEGIN_CASE(JSOP_SETRVAL)
|
BEGIN_CASE(JSOP_SETRVAL)
|
||||||
|
|
|
@ -581,7 +581,7 @@ nsSVGPatternFrame::ConstructCTM(const gfxRect &callerBBox,
|
||||||
|
|
||||||
const nsSVGViewBoxRect viewBox = GetViewBox().GetAnimValue();
|
const nsSVGViewBoxRect viewBox = GetViewBox().GetAnimValue();
|
||||||
|
|
||||||
if (viewBox.height <= 0.0f && viewBox.width <= 0.0f) {
|
if (viewBox.height <= 0.0f || viewBox.width <= 0.0f) {
|
||||||
return tCTM;
|
return tCTM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="reftest-wait">
|
||||||
|
<!-- Test to be sure that a zero-sized-in-one-dimension viewBox doesn't
|
||||||
|
make us fail assertions. -->
|
||||||
|
<script>
|
||||||
|
document.addEventListener("MozReftestInvalidate", waitAndFinish, false);
|
||||||
|
|
||||||
|
function waitAndFinish() {
|
||||||
|
// Sadly, MozReftestInvalidate fires sooner than PaintPattern here, so
|
||||||
|
// we need to wait a little bit to give PaintPattern a chance to hit
|
||||||
|
// this bug.
|
||||||
|
setTimeout(finish, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
function finish() {
|
||||||
|
document.documentElement.removeAttribute("class");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<pattern id="test" viewBox="0 0 1 0">
|
||||||
|
<rect/>
|
||||||
|
</pattern>
|
||||||
|
<rect width="200" height="200" fill="url(#test)"/>
|
||||||
|
</svg>
|
После (image error) Размер: 730 B |
|
@ -0,0 +1,23 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="reftest-wait">
|
||||||
|
<!-- Test to be sure that a zero-sized-in-one-dimension viewBox doesn't
|
||||||
|
make us fail assertions. -->
|
||||||
|
<script>
|
||||||
|
document.addEventListener("MozReftestInvalidate", waitAndFinish, false);
|
||||||
|
|
||||||
|
function waitAndFinish() {
|
||||||
|
// Sadly, MozReftestInvalidate fires sooner than PaintPattern here, so
|
||||||
|
// we need to wait a little bit to give PaintPattern a chance to hit
|
||||||
|
// this bug.
|
||||||
|
setTimeout(finish, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
function finish() {
|
||||||
|
document.documentElement.removeAttribute("class");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<pattern id="test" viewBox="0 0 0 1">
|
||||||
|
<rect/>
|
||||||
|
</pattern>
|
||||||
|
<rect width="200" height="200" fill="url(#test)"/>
|
||||||
|
</svg>
|
После (image error) Размер: 730 B |
|
@ -119,3 +119,5 @@ load 682411-1.svg
|
||||||
load 692203-1.svg
|
load 692203-1.svg
|
||||||
load 692203-2.svg
|
load 692203-2.svg
|
||||||
load 693424-1.svg
|
load 693424-1.svg
|
||||||
|
load 709920-1.svg
|
||||||
|
load 709920-2.svg
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
*
|
*
|
||||||
* Contributor(s):
|
* Contributor(s):
|
||||||
* Brad Lassey <blassey@mozilla.com>
|
* Brad Lassey <blassey@mozilla.com>
|
||||||
|
* Lucas Rocha <lucasr@mozilla.com>
|
||||||
*
|
*
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
* 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
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
@ -37,120 +38,183 @@
|
||||||
|
|
||||||
package org.mozilla.gecko;
|
package org.mozilla.gecko;
|
||||||
|
|
||||||
import android.app.Activity;
|
import java.io.File;
|
||||||
import android.os.Bundle;
|
import java.io.FileInputStream;
|
||||||
import android.widget.*;
|
import java.io.FileNotFoundException;
|
||||||
import android.database.*;
|
import java.io.InputStream;
|
||||||
import android.view.*;
|
import java.util.zip.ZipEntry;
|
||||||
import android.graphics.*;
|
import java.util.zip.ZipFile;
|
||||||
import android.content.*;
|
|
||||||
import android.provider.Browser;
|
|
||||||
import android.util.Log;
|
|
||||||
import java.util.Date;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.provider.Browser;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ListAdapter;
|
|
||||||
import android.widget.GridView;
|
|
||||||
import android.widget.SimpleCursorAdapter;
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.zip.*;
|
|
||||||
import android.os.Handler;
|
|
||||||
import org.json.*;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
import org.mozilla.gecko.db.BrowserDB;
|
import org.mozilla.gecko.db.BrowserDB;
|
||||||
import org.mozilla.gecko.db.BrowserDB.URLColumns;
|
import org.mozilla.gecko.db.BrowserDB.URLColumns;
|
||||||
|
|
||||||
public class AboutHomeContent extends LinearLayout {
|
import android.app.Activity;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.style.UnderlineSpan;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.GridView;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.SimpleCursorAdapter;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
public class AboutHomeContent extends ScrollView {
|
||||||
|
private static final String LOGTAG = "GeckoAboutHome";
|
||||||
|
|
||||||
|
private static final int NUMBER_OF_TOP_SITES_PORTRAIT = 4;
|
||||||
|
private static final int NUMBER_OF_TOP_SITES_LANDSCAPE = 3;
|
||||||
|
|
||||||
|
private static final int NUMBER_OF_COLS_PORTRAIT = 2;
|
||||||
|
private static final int NUMBER_OF_COLS_LANDSCAPE = 3;
|
||||||
|
|
||||||
|
private boolean mInflated;
|
||||||
|
|
||||||
|
private Cursor mCursor;
|
||||||
|
UriLoadCallback mUriLoadCallback = null;
|
||||||
|
|
||||||
|
protected SimpleCursorAdapter mTopSitesAdapter;
|
||||||
|
protected GridView mTopSitesGrid;
|
||||||
|
|
||||||
|
protected ArrayAdapter<String> mAddonsAdapter;
|
||||||
|
protected ListView mAddonsList;
|
||||||
|
|
||||||
public interface UriLoadCallback {
|
public interface UriLoadCallback {
|
||||||
public void callback(String uriSpec);
|
public void callback(String uriSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
UriLoadCallback mUriLoadCallback = null;
|
|
||||||
|
|
||||||
void setUriLoadCallback(UriLoadCallback uriLoadCallback) {
|
|
||||||
mUriLoadCallback = uriLoadCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AboutHomeContent(Context context, AttributeSet attrs) {
|
public AboutHomeContent(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
|
mInflated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String LOGTAG = "GeckoAboutHome";
|
@Override
|
||||||
private static final String TITLE_KEY = "title";
|
protected void onFinishInflate() {
|
||||||
private static final int NUMBER_OF_TOP_SITES = 3;
|
super.onFinishInflate();
|
||||||
private static final int kTileWidth = 122;
|
|
||||||
|
|
||||||
private Cursor mCursor;
|
// HACK: Without this, the onFinishInflate is called twice
|
||||||
private Uri mUri;
|
// This issue is due to a bug when Android inflates a layout with a
|
||||||
private String mTitle;
|
// parent. Fixed in Honeycomb
|
||||||
|
if (mInflated)
|
||||||
protected ListAdapter mGridAdapter;
|
|
||||||
protected ArrayAdapter<String> mAddonAdapter;
|
|
||||||
protected GridView mGrid;
|
|
||||||
protected ListView mAddonList;
|
|
||||||
private Handler mHandler = new Handler();
|
|
||||||
|
|
||||||
public void onActivityContentChanged(Activity activity) {
|
|
||||||
mGrid = (GridView)findViewById(R.id.grid);
|
|
||||||
if (mGrid == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mGrid.setOnItemClickListener(mGridOnClickListener);
|
mInflated = true;
|
||||||
|
|
||||||
// we want to do this: mGrid.setNumColumns(GridView.AUTO_FIT); but it doesn't work
|
mTopSitesGrid = (GridView)findViewById(R.id.top_sites_grid);
|
||||||
Display display = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
mTopSitesGrid.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
int width = display.getWidth();
|
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||||
mGrid.setNumColumns((int) Math.floor(width / kTileWidth));
|
Cursor c = (Cursor) parent.getItemAtPosition(position);
|
||||||
mAddonList = (ListView)findViewById(R.id.recommended_addon_list);
|
|
||||||
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
|
String spec = c.getString(c.getColumnIndex(URLColumns.URL));
|
||||||
public void run() {
|
Log.i(LOGTAG, "clicked: " + spec);
|
||||||
mGrid.setAdapter(mGridAdapter);
|
|
||||||
|
if (mUriLoadCallback != null)
|
||||||
|
mUriLoadCallback.callback(spec);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mAddonsList = (ListView) findViewById(R.id.recommended_addons_list);
|
||||||
|
|
||||||
|
TextView allTopSitesText = (TextView) findViewById(R.id.all_top_sites_text);
|
||||||
|
allTopSitesText.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
GeckoApp.mAppContext.showAwesomebar(AwesomeBar.Type.EDIT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TextView allAddonsText = (TextView) findViewById(R.id.all_addons_text);
|
||||||
|
allAddonsText.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (mUriLoadCallback != null)
|
||||||
|
mUriLoadCallback.callback("about:addons");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getNumberOfTopSites() {
|
||||||
|
Configuration config = getContext().getResources().getConfiguration();
|
||||||
|
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE)
|
||||||
|
return NUMBER_OF_TOP_SITES_LANDSCAPE;
|
||||||
|
else
|
||||||
|
return NUMBER_OF_TOP_SITES_PORTRAIT;
|
||||||
|
}
|
||||||
|
|
||||||
private AdapterView.OnItemClickListener mGridOnClickListener = new AdapterView.OnItemClickListener() {
|
private int getNumberOfColumns() {
|
||||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id)
|
Configuration config = getContext().getResources().getConfiguration();
|
||||||
{
|
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE)
|
||||||
onGridItemClick((GridView)parent, v, position, id);
|
return NUMBER_OF_COLS_LANDSCAPE;
|
||||||
}
|
else
|
||||||
};
|
return NUMBER_OF_COLS_PORTRAIT;
|
||||||
|
}
|
||||||
|
|
||||||
void init(final Activity activity) {
|
void init(final Activity activity) {
|
||||||
GeckoAppShell.getHandler().post(new Runnable() {
|
GeckoAppShell.getHandler().post(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
if (mCursor != null)
|
||||||
|
activity.stopManagingCursor(mCursor);
|
||||||
|
|
||||||
ContentResolver resolver = GeckoApp.mAppContext.getContentResolver();
|
ContentResolver resolver = GeckoApp.mAppContext.getContentResolver();
|
||||||
mCursor = BrowserDB.filter(resolver, "", NUMBER_OF_TOP_SITES);
|
mCursor = BrowserDB.filter(resolver, "", NUMBER_OF_TOP_SITES_PORTRAIT);
|
||||||
activity.startManagingCursor(mCursor);
|
activity.startManagingCursor(mCursor);
|
||||||
|
|
||||||
onActivityContentChanged(activity);
|
mTopSitesAdapter = new TopSitesCursorAdapter(activity,
|
||||||
mAddonAdapter = new ArrayAdapter<String>(activity, R.layout.abouthome_addon_list_item);
|
R.layout.abouthome_topsite_item,
|
||||||
|
mCursor,
|
||||||
|
new String[] { URLColumns.TITLE,
|
||||||
|
URLColumns.THUMBNAIL },
|
||||||
|
new int[] { R.id.title, R.id.thumbnail });
|
||||||
|
|
||||||
|
if (mAddonsAdapter == null)
|
||||||
|
mAddonsAdapter = new ArrayAdapter<String>(activity, R.layout.abouthome_addon_row);
|
||||||
|
|
||||||
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
|
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
final SimpleCursorAdapter gridAdapter =
|
mTopSitesGrid.setNumColumns(getNumberOfColumns());
|
||||||
new SimpleCursorAdapter(activity, R.layout.abouthome_grid_box, mCursor,
|
|
||||||
new String[] { URLColumns.TITLE,
|
mTopSitesGrid.setAdapter(mTopSitesAdapter);
|
||||||
URLColumns.FAVICON,
|
mTopSitesAdapter.setViewBinder(new TopSitesViewBinder());
|
||||||
URLColumns.URL,
|
|
||||||
URLColumns.THUMBNAIL },
|
mAddonsList.setAdapter(mAddonsAdapter);
|
||||||
new int[] {R.id.bookmark_title, R.id.bookmark_icon, R.id.bookmark_url, R.id.screenshot});
|
|
||||||
mGrid.setAdapter(gridAdapter);
|
|
||||||
gridAdapter.setViewBinder(new AwesomeCursorViewBinder());
|
|
||||||
mAddonList.setAdapter(mAddonAdapter);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
readRecommendedAddons(activity);
|
readRecommendedAddons(activity);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUriLoadCallback(UriLoadCallback uriLoadCallback) {
|
||||||
|
mUriLoadCallback = uriLoadCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onActivityContentChanged(Activity activity) {
|
||||||
|
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
mTopSitesGrid.setAdapter(mTopSitesAdapter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
|
mTopSitesGrid.setNumColumns(getNumberOfColumns());
|
||||||
|
mTopSitesAdapter.notifyDataSetChanged();
|
||||||
|
|
||||||
|
super.onConfigurationChanged(newConfig);
|
||||||
|
}
|
||||||
|
|
||||||
InputStream getProfileRecommendedAddonsStream() {
|
InputStream getProfileRecommendedAddonsStream() {
|
||||||
try {
|
try {
|
||||||
File profileDir = GeckoApp.mAppContext.getProfileDir();
|
File profileDir = GeckoApp.mAppContext.getProfileDir();
|
||||||
|
@ -172,7 +236,11 @@ public class AboutHomeContent extends LinearLayout {
|
||||||
return is;
|
return is;
|
||||||
File applicationPackage = new File(activity.getApplication().getPackageResourcePath());
|
File applicationPackage = new File(activity.getApplication().getPackageResourcePath());
|
||||||
ZipFile zip = new ZipFile(applicationPackage);
|
ZipFile zip = new ZipFile(applicationPackage);
|
||||||
|
if (zip == null)
|
||||||
|
return null;
|
||||||
ZipEntry fileEntry = zip.getEntry("recommended-addons.json");
|
ZipEntry fileEntry = zip.getEntry("recommended-addons.json");
|
||||||
|
if (fileEntry == null)
|
||||||
|
return null;
|
||||||
return zip.getInputStream(fileEntry);
|
return zip.getInputStream(fileEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,6 +250,8 @@ public class AboutHomeContent extends LinearLayout {
|
||||||
try {
|
try {
|
||||||
byte[] buf = new byte[32768];
|
byte[] buf = new byte[32768];
|
||||||
InputStream fileStream = getRecommendedAddonsStream(activity);
|
InputStream fileStream = getRecommendedAddonsStream(activity);
|
||||||
|
if (fileStream == null)
|
||||||
|
return;
|
||||||
StringBuffer jsonString = new StringBuffer();
|
StringBuffer jsonString = new StringBuffer();
|
||||||
int read = 0;
|
int read = 0;
|
||||||
while ((read = fileStream.read(buf, 0, 32768)) != -1) {
|
while ((read = fileStream.read(buf, 0, 32768)) != -1) {
|
||||||
|
@ -193,7 +263,7 @@ public class AboutHomeContent extends LinearLayout {
|
||||||
try {
|
try {
|
||||||
for (int i = 0; i < array.length(); i++) {
|
for (int i = 0; i < array.length(); i++) {
|
||||||
JSONObject jsonobj = array.getJSONObject(i);
|
JSONObject jsonobj = array.getJSONObject(i);
|
||||||
mAddonAdapter.add(jsonobj.getString("name"));
|
mAddonsAdapter.add(jsonobj.getString("name"));
|
||||||
Log.i("GeckoAddons", "addon #" + i +": " + jsonobj.getString("name"));
|
Log.i("GeckoAddons", "addon #" + i +": " + jsonobj.getString("name"));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -208,91 +278,111 @@ public class AboutHomeContent extends LinearLayout {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onGridItemClick(GridView l, View v, int position, long id) {
|
public static class TopSitesGridView extends GridView {
|
||||||
mCursor.moveToPosition(position);
|
public TopSitesGridView(Context context, AttributeSet attrs) {
|
||||||
String spec = mCursor.getString(mCursor.getColumnIndex(URLColumns.URL));
|
super(context, attrs);
|
||||||
Log.i(LOGTAG, "clicked: " + spec);
|
}
|
||||||
if (mUriLoadCallback != null)
|
|
||||||
mUriLoadCallback.callback(spec);
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
// This is to ensure that the GridView always has a size that shows
|
||||||
|
// all items with no need for scrolling.
|
||||||
|
int expandedHeightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
|
||||||
|
MeasureSpec.AT_MOST);
|
||||||
|
super.onMeasure(widthMeasureSpec, expandedHeightSpec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public class TopSitesCursorAdapter extends SimpleCursorAdapter {
|
||||||
class AwesomeCursorViewBinder implements SimpleCursorAdapter.ViewBinder {
|
public TopSitesCursorAdapter(Context context, int layout, Cursor c,
|
||||||
private static final String LOGTAG = "GeckoAwesomeCursorViewBinder";
|
String[] from, int[] to) {
|
||||||
|
super(context, layout, c, from, to);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean updateImage(View view, Cursor cursor, int faviconIndex) {
|
@Override
|
||||||
byte[] b = cursor.getBlob(faviconIndex);
|
public int getCount() {
|
||||||
ImageView favicon = (ImageView) view;
|
return Math.min(super.getCount(), getNumberOfTopSites());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (b == null) {
|
class TopSitesViewBinder implements SimpleCursorAdapter.ViewBinder {
|
||||||
favicon.setImageResource(R.drawable.favicon);
|
private boolean updateThumbnail(View view, Cursor cursor, int thumbIndex) {
|
||||||
} else {
|
byte[] b = cursor.getBlob(thumbIndex);
|
||||||
try {
|
ImageView thumbnail = (ImageView) view;
|
||||||
Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
|
|
||||||
favicon.setImageBitmap(bitmap);
|
if (b == null) {
|
||||||
} catch (OutOfMemoryError oom) {
|
thumbnail.setImageResource(R.drawable.abouthome_topsite_placeholder);
|
||||||
Log.e(LOGTAG, "Unable to load thumbnail bitmap", oom);
|
} else {
|
||||||
favicon.setImageResource(R.drawable.favicon);
|
try {
|
||||||
|
Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
|
||||||
|
thumbnail.setImageBitmap(bitmap);
|
||||||
|
} catch (OutOfMemoryError oom) {
|
||||||
|
Log.e(LOGTAG, "Unable to load thumbnail bitmap", oom);
|
||||||
|
thumbnail.setImageResource(R.drawable.abouthome_topsite_placeholder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
private boolean updateTitle(View view, Cursor cursor, int titleIndex) {
|
||||||
|
String title = cursor.getString(titleIndex);
|
||||||
|
TextView titleView = (TextView) view;
|
||||||
|
|
||||||
|
// Use the URL instead of an empty title for consistency with the normal URL
|
||||||
|
// bar view - this is the equivalent of getDisplayTitle() in Tab.java
|
||||||
|
if (title == null || title.length() == 0) {
|
||||||
|
int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL);
|
||||||
|
title = cursor.getString(urlIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
titleView.setText(title);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
|
||||||
|
int titleIndex = cursor.getColumnIndexOrThrow(URLColumns.TITLE);
|
||||||
|
if (columnIndex == titleIndex) {
|
||||||
|
return updateTitle(view, cursor, titleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int thumbIndex = cursor.getColumnIndexOrThrow(URLColumns.THUMBNAIL);
|
||||||
|
if (columnIndex == thumbIndex) {
|
||||||
|
return updateThumbnail(view, cursor, thumbIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other columns are handled automatically
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean updateTitle(View view, Cursor cursor, int titleIndex) {
|
public static class AddonsListView extends ListView {
|
||||||
String title = cursor.getString(titleIndex);
|
public AddonsListView(Context context, AttributeSet attrs) {
|
||||||
TextView titleView = (TextView)view;
|
super(context, attrs);
|
||||||
// Use the URL instead of an empty title for consistency with the normal URL
|
|
||||||
// bar view - this is the equivalent of getDisplayTitle() in Tab.java
|
|
||||||
if (title == null || title.length() == 0) {
|
|
||||||
int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL);
|
|
||||||
title = cursor.getString(urlIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
titleView.setText(title);
|
@Override
|
||||||
return true;
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
// This is to ensure that the ListView always has a size that shows
|
||||||
|
// all items with no need for scrolling.
|
||||||
|
int expandedHeightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
|
||||||
|
MeasureSpec.AT_MOST);
|
||||||
|
super.onMeasure(widthMeasureSpec, expandedHeightSpec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean updateUrl(View view, Cursor cursor, int urlIndex) {
|
public static class LinkTextView extends TextView {
|
||||||
String title = cursor.getString(urlIndex);
|
public LinkTextView(Context context, AttributeSet attrs) {
|
||||||
TextView urlView = (TextView)view;
|
super(context, attrs);
|
||||||
if (title != null) {
|
}
|
||||||
int index;
|
|
||||||
if ((index = title.indexOf("://")) != -1)
|
@Override
|
||||||
title = title.substring(index + 3);
|
public void setText(CharSequence text, BufferType type) {
|
||||||
if (title.startsWith("www."))
|
SpannableString content = new SpannableString(text + " \u00BB");
|
||||||
title = title.substring(4);
|
content.setSpan(new UnderlineSpan(), 0, text.length(), 0);
|
||||||
if (title.endsWith("/"))
|
|
||||||
title = title.substring(0, title.length() -1);
|
super.setText(content, BufferType.SPANNABLE);
|
||||||
}
|
}
|
||||||
urlView.setText(title);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
|
|
||||||
int faviconIndex = cursor.getColumnIndexOrThrow(URLColumns.FAVICON);
|
|
||||||
if (columnIndex == faviconIndex) {
|
|
||||||
return updateImage(view, cursor, faviconIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
int titleIndex = cursor.getColumnIndexOrThrow(URLColumns.TITLE);
|
|
||||||
if (columnIndex == titleIndex) {
|
|
||||||
return updateTitle(view, cursor, titleIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL);
|
|
||||||
if (columnIndex == urlIndex) {
|
|
||||||
return updateUrl(view, cursor, urlIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
int thumbIndex = cursor.getColumnIndexOrThrow(URLColumns.THUMBNAIL);
|
|
||||||
if (columnIndex == thumbIndex) {
|
|
||||||
return updateImage(view, cursor, thumbIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other columns are handled automatically
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,7 +132,8 @@
|
||||||
android:windowSoftInputMode="stateAlwaysVisible|adjustResize"/>
|
android:windowSoftInputMode="stateAlwaysVisible|adjustResize"/>
|
||||||
|
|
||||||
<activity android:name="org.mozilla.gecko.TabsTray"
|
<activity android:name="org.mozilla.gecko.TabsTray"
|
||||||
android:theme="@style/Gecko.Translucent"/>
|
android:theme="@style/Gecko.Translucent"
|
||||||
|
android:launchMode="singleTask"/>
|
||||||
|
|
||||||
<activity android:name="org.mozilla.gecko.GeckoPreferences"
|
<activity android:name="org.mozilla.gecko.GeckoPreferences"
|
||||||
android:theme="@style/Gecko.TitleBar"
|
android:theme="@style/Gecko.TitleBar"
|
||||||
|
|
|
@ -84,7 +84,7 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
|
||||||
mAwesomeTabs = (AwesomeBarTabs) findViewById(R.id.awesomebar_tabs);
|
mAwesomeTabs = (AwesomeBarTabs) findViewById(R.id.awesomebar_tabs);
|
||||||
mAwesomeTabs.setOnUrlOpenListener(new AwesomeBarTabs.OnUrlOpenListener() {
|
mAwesomeTabs.setOnUrlOpenListener(new AwesomeBarTabs.OnUrlOpenListener() {
|
||||||
public void onUrlOpen(String url) {
|
public void onUrlOpen(String url) {
|
||||||
openUrlAndFinish(url);
|
submitAndFinish(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSearch(String engine) {
|
public void onSearch(String engine) {
|
||||||
|
@ -95,7 +95,7 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
|
||||||
mGoButton = (ImageButton) findViewById(R.id.awesomebar_button);
|
mGoButton = (ImageButton) findViewById(R.id.awesomebar_button);
|
||||||
mGoButton.setOnClickListener(new Button.OnClickListener() {
|
mGoButton.setOnClickListener(new Button.OnClickListener() {
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
openUrlAndFinish(mText.getText().toString());
|
submitAndFinish(mText.getText().toString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
|
||||||
if (event.getAction() != KeyEvent.ACTION_DOWN)
|
if (event.getAction() != KeyEvent.ACTION_DOWN)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
openUrlAndFinish(mText.getText().toString());
|
submitAndFinish(mText.getText().toString());
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -209,6 +209,34 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This method tries to guess if the given string could be a search query or URL
|
||||||
|
* Search examples:
|
||||||
|
* foo
|
||||||
|
* foo bar.com
|
||||||
|
* foo http://bar.com
|
||||||
|
*
|
||||||
|
* URL examples
|
||||||
|
* foo.com
|
||||||
|
* foo.c
|
||||||
|
* :foo
|
||||||
|
* http://foo.com bar
|
||||||
|
*/
|
||||||
|
private boolean isSearchUrl(String text) {
|
||||||
|
text = text.trim();
|
||||||
|
if (text.length() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int colon = text.indexOf(':');
|
||||||
|
int dot = text.indexOf('.');
|
||||||
|
int space = text.indexOf(' ');
|
||||||
|
|
||||||
|
// If a space is found before any dot or colon, we assume this is a search query
|
||||||
|
boolean spacedOut = space > -1 && (space < colon || space < dot);
|
||||||
|
|
||||||
|
return spacedOut || (dot == -1 && colon == -1);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateGoButton(String text) {
|
private void updateGoButton(String text) {
|
||||||
if (text.length() == 0) {
|
if (text.length() == 0) {
|
||||||
mGoButton.setVisibility(View.GONE);
|
mGoButton.setVisibility(View.GONE);
|
||||||
|
@ -218,13 +246,19 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
|
||||||
mGoButton.setVisibility(View.VISIBLE);
|
mGoButton.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
int imageResource = R.drawable.ic_awesomebar_go;
|
int imageResource = R.drawable.ic_awesomebar_go;
|
||||||
if (!GeckoAppShell.canCreateFixupURI(text)) {
|
if (isSearchUrl(text))
|
||||||
imageResource = R.drawable.ic_awesomebar_search;
|
imageResource = R.drawable.ic_awesomebar_search;
|
||||||
}
|
|
||||||
|
|
||||||
mGoButton.setImageResource(imageResource);
|
mGoButton.setImageResource(imageResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void submitAndFinish(String url) {
|
||||||
|
if (isSearchUrl(url))
|
||||||
|
openSearchAndFinish(url, "__default__");
|
||||||
|
else
|
||||||
|
openUrlAndFinish(url);
|
||||||
|
}
|
||||||
|
|
||||||
private void cancelAndFinish() {
|
private void cancelAndFinish() {
|
||||||
setResult(Activity.RESULT_CANCELED);
|
setResult(Activity.RESULT_CANCELED);
|
||||||
finish();
|
finish();
|
||||||
|
|
|
@ -456,11 +456,13 @@ abstract public class GeckoApp
|
||||||
MenuItem forward = aMenu.findItem(R.id.forward);
|
MenuItem forward = aMenu.findItem(R.id.forward);
|
||||||
MenuItem share = aMenu.findItem(R.id.share);
|
MenuItem share = aMenu.findItem(R.id.share);
|
||||||
MenuItem agentMode = aMenu.findItem(R.id.agent_mode);
|
MenuItem agentMode = aMenu.findItem(R.id.agent_mode);
|
||||||
|
MenuItem saveAsPDF = aMenu.findItem(R.id.save_as_pdf);
|
||||||
|
|
||||||
if (tab == null) {
|
if (tab == null) {
|
||||||
bookmark.setEnabled(false);
|
bookmark.setEnabled(false);
|
||||||
forward.setEnabled(false);
|
forward.setEnabled(false);
|
||||||
share.setEnabled(false);
|
share.setEnabled(false);
|
||||||
|
saveAsPDF.setEnabled(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,7 +483,11 @@ abstract public class GeckoApp
|
||||||
|
|
||||||
// Don't share about:, chrome: and file: URIs
|
// Don't share about:, chrome: and file: URIs
|
||||||
String scheme = Uri.parse(tab.getURL()).getScheme();
|
String scheme = Uri.parse(tab.getURL()).getScheme();
|
||||||
share.setEnabled(!scheme.equals("about") && !scheme.equals("chrome") && !scheme.equals("file"));
|
share.setEnabled(!(scheme.equals("about") || scheme.equals("chrome") || scheme.equals("file")));
|
||||||
|
|
||||||
|
// Disable save as PDF for about:home and xul pages
|
||||||
|
saveAsPDF.setEnabled(!(tab.getURL().equals("about:home") ||
|
||||||
|
tab.getContentType().equals("application/vnd.mozilla.xul+xml")));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -667,7 +673,8 @@ abstract public class GeckoApp
|
||||||
tab.setFaviconLoadId(id);
|
tab.setFaviconLoadId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleLocationChange(final int tabId, final String uri) {
|
void handleLocationChange(final int tabId, final String uri,
|
||||||
|
final String documentURI, final String contentType) {
|
||||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||||
if (tab == null)
|
if (tab == null)
|
||||||
return;
|
return;
|
||||||
|
@ -681,6 +688,8 @@ abstract public class GeckoApp
|
||||||
|
|
||||||
String oldBaseURI = tab.getURL();
|
String oldBaseURI = tab.getURL();
|
||||||
tab.updateURL(uri);
|
tab.updateURL(uri);
|
||||||
|
tab.setDocumentURI(documentURI);
|
||||||
|
tab.setContentType(contentType);
|
||||||
|
|
||||||
String baseURI = uri;
|
String baseURI = uri;
|
||||||
if (baseURI.indexOf('#') != -1)
|
if (baseURI.indexOf('#') != -1)
|
||||||
|
@ -848,8 +857,10 @@ abstract public class GeckoApp
|
||||||
} else if (event.equals("Content:LocationChange")) {
|
} else if (event.equals("Content:LocationChange")) {
|
||||||
final int tabId = message.getInt("tabID");
|
final int tabId = message.getInt("tabID");
|
||||||
final String uri = message.getString("uri");
|
final String uri = message.getString("uri");
|
||||||
|
final String documentURI = message.getString("documentURI");
|
||||||
|
final String contentType = message.getString("contentType");
|
||||||
Log.i(LOGTAG, "URI - " + uri);
|
Log.i(LOGTAG, "URI - " + uri);
|
||||||
handleLocationChange(tabId, uri);
|
handleLocationChange(tabId, uri, documentURI, contentType);
|
||||||
} else if (event.equals("Content:SecurityChange")) {
|
} else if (event.equals("Content:SecurityChange")) {
|
||||||
final int tabId = message.getInt("tabID");
|
final int tabId = message.getInt("tabID");
|
||||||
final String mode = message.getString("mode");
|
final String mode = message.getString("mode");
|
||||||
|
@ -902,7 +913,11 @@ abstract public class GeckoApp
|
||||||
setLaunchState(GeckoApp.LaunchState.GeckoRunning);
|
setLaunchState(GeckoApp.LaunchState.GeckoRunning);
|
||||||
GeckoAppShell.sendPendingEventsToGecko();
|
GeckoAppShell.sendPendingEventsToGecko();
|
||||||
connectGeckoLayerClient();
|
connectGeckoLayerClient();
|
||||||
Looper.myQueue().addIdleHandler(new UpdateIdleHandler());
|
GeckoAppShell.getHandler().post(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
Looper.myQueue().addIdleHandler(new UpdateIdleHandler());
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (event.equals("ToggleChrome:Hide")) {
|
} else if (event.equals("ToggleChrome:Hide")) {
|
||||||
mMainHandler.post(new Runnable() {
|
mMainHandler.post(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -1436,7 +1451,6 @@ abstract public class GeckoApp
|
||||||
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
|
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
|
||||||
.detectAll()
|
.detectAll()
|
||||||
.penaltyLog()
|
.penaltyLog()
|
||||||
.penaltyDialog()
|
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
||||||
|
|
|
@ -130,7 +130,6 @@ public class GeckoAppShell
|
||||||
public static native void onChangeNetworkLinkStatus(String status);
|
public static native void onChangeNetworkLinkStatus(String status);
|
||||||
public static native void reportJavaCrash(String stack);
|
public static native void reportJavaCrash(String stack);
|
||||||
public static native void notifyUriVisited(String uri);
|
public static native void notifyUriVisited(String uri);
|
||||||
public static native boolean canCreateFixupURI(String text);
|
|
||||||
|
|
||||||
public static native void processNextNativeEvent();
|
public static native void processNextNativeEvent();
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
|
|
||||||
package org.mozilla.gecko;
|
package org.mozilla.gecko;
|
||||||
|
|
||||||
|
import java.lang.CharSequence;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
@ -131,8 +132,12 @@ public class GeckoPreferences
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
String prefName = preference.getKey();
|
String prefName = preference.getKey();
|
||||||
setPreference(prefName, newValue);
|
setPreference(prefName, newValue);
|
||||||
if (preference instanceof ListPreference)
|
if (preference instanceof ListPreference) {
|
||||||
((ListPreference)preference).setSummary((String)newValue);
|
// We need to find the entry for the new value
|
||||||
|
int newIndex = ((ListPreference)preference).findIndexOfValue((String) newValue);
|
||||||
|
CharSequence newEntry = ((ListPreference)preference).getEntries()[newIndex];
|
||||||
|
((ListPreference)preference).setSummary(newEntry);
|
||||||
|
}
|
||||||
if (preference instanceof LinkPreference)
|
if (preference instanceof LinkPreference)
|
||||||
finish();
|
finish();
|
||||||
return true;
|
return true;
|
||||||
|
@ -177,6 +182,9 @@ public class GeckoPreferences
|
||||||
GeckoAppShell.getMainHandler().post(new Runnable() {
|
GeckoAppShell.getMainHandler().post(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
((ListPreference)pref).setValue(value);
|
((ListPreference)pref).setValue(value);
|
||||||
|
// Set the summary string to the current entry
|
||||||
|
CharSequence selectedEntry = ((ListPreference)pref).getEntry();
|
||||||
|
((ListPreference)pref).setSummary(selectedEntry);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,8 +194,8 @@ RES_LAYOUT = \
|
||||||
res/layout/list_item_header.xml \
|
res/layout/list_item_header.xml \
|
||||||
res/layout/select_dialog_list.xml \
|
res/layout/select_dialog_list.xml \
|
||||||
res/layout/abouthome_content.xml \
|
res/layout/abouthome_content.xml \
|
||||||
res/layout/abouthome_grid_box.xml \
|
res/layout/abouthome_topsite_item.xml \
|
||||||
res/layout/abouthome_addon_list_item.xml \
|
res/layout/abouthome_addon_row.xml \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
RES_LAYOUT_V11 = \
|
RES_LAYOUT_V11 = \
|
||||||
|
@ -206,6 +206,7 @@ RES_LAYOUT_V11 = \
|
||||||
|
|
||||||
RES_VALUES = \
|
RES_VALUES = \
|
||||||
res/values/defaults.xml \
|
res/values/defaults.xml \
|
||||||
|
res/values/arrays.xml \
|
||||||
res/values/colors.xml \
|
res/values/colors.xml \
|
||||||
res/values/styles.xml \
|
res/values/styles.xml \
|
||||||
res/values/themes.xml \
|
res/values/themes.xml \
|
||||||
|
@ -224,6 +225,11 @@ RES_ANIM = \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
RES_DRAWABLE_MDPI_V8 = \
|
RES_DRAWABLE_MDPI_V8 = \
|
||||||
|
res/drawable-mdpi-v8/abouthome_icon.png \
|
||||||
|
res/drawable-mdpi-v8/abouthome_logo.png \
|
||||||
|
res/drawable-mdpi-v8/abouthome_separator.9.png \
|
||||||
|
res/drawable-mdpi-v8/abouthome_topsite_placeholder.png \
|
||||||
|
res/drawable-mdpi-v8/abouthome_topsite_shadow.9.png \
|
||||||
res/drawable-mdpi-v8/ic_awesomebar_go.png \
|
res/drawable-mdpi-v8/ic_awesomebar_go.png \
|
||||||
res/drawable-mdpi-v8/ic_awesomebar_search.png \
|
res/drawable-mdpi-v8/ic_awesomebar_search.png \
|
||||||
res/drawable-mdpi-v8/ic_menu_bookmark_add.png \
|
res/drawable-mdpi-v8/ic_menu_bookmark_add.png \
|
||||||
|
@ -246,6 +252,11 @@ RES_DRAWABLE_MDPI_V8 = \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
RES_DRAWABLE_HDPI_V8 = \
|
RES_DRAWABLE_HDPI_V8 = \
|
||||||
|
res/drawable-hdpi-v8/abouthome_icon.png \
|
||||||
|
res/drawable-hdpi-v8/abouthome_logo.png \
|
||||||
|
res/drawable-hdpi-v8/abouthome_separator.9.png \
|
||||||
|
res/drawable-hdpi-v8/abouthome_topsite_placeholder.png \
|
||||||
|
res/drawable-hdpi-v8/abouthome_topsite_shadow.9.png \
|
||||||
res/drawable-hdpi-v8/ic_awesomebar_go.png \
|
res/drawable-hdpi-v8/ic_awesomebar_go.png \
|
||||||
res/drawable-hdpi-v8/ic_awesomebar_search.png \
|
res/drawable-hdpi-v8/ic_awesomebar_search.png \
|
||||||
res/drawable-hdpi-v8/ic_menu_bookmark_add.png \
|
res/drawable-hdpi-v8/ic_menu_bookmark_add.png \
|
||||||
|
@ -318,6 +329,11 @@ RES_DRAWABLE_HDPI_V11 = \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
RES_DRAWABLE_XHDPI_V11 = \
|
RES_DRAWABLE_XHDPI_V11 = \
|
||||||
|
res/drawable-xhdpi-v11/abouthome_icon.png \
|
||||||
|
res/drawable-xhdpi-v11/abouthome_logo.png \
|
||||||
|
res/drawable-xhdpi-v11/abouthome_separator.9.png \
|
||||||
|
res/drawable-xhdpi-v11/abouthome_topsite_placeholder.png \
|
||||||
|
res/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png \
|
||||||
res/drawable-xhdpi-v11/ic_awesomebar_go.png \
|
res/drawable-xhdpi-v11/ic_awesomebar_go.png \
|
||||||
res/drawable-xhdpi-v11/ic_awesomebar_search.png \
|
res/drawable-xhdpi-v11/ic_awesomebar_search.png \
|
||||||
res/drawable-xhdpi-v11/ic_menu_bookmark_add.png \
|
res/drawable-xhdpi-v11/ic_menu_bookmark_add.png \
|
||||||
|
@ -346,7 +362,11 @@ MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/crash_reporter.p
|
||||||
RES_LAYOUT += res/layout/crash_reporter.xml
|
RES_LAYOUT += res/layout/crash_reporter.xml
|
||||||
endif
|
endif
|
||||||
|
|
||||||
MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/address_bar_bg.xml \
|
MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/abouthome_bg.png \
|
||||||
|
mobile/android/base/resources/drawable/abouthome_bg_repeat.xml \
|
||||||
|
mobile/android/base/resources/drawable/abouthome_topsites_bg.png \
|
||||||
|
mobile/android/base/resources/drawable/abouthome_topsites_bg_repeat.xml \
|
||||||
|
mobile/android/base/resources/drawable/address_bar_bg.xml \
|
||||||
mobile/android/base/resources/drawable/address_bar_url_default.xml \
|
mobile/android/base/resources/drawable/address_bar_url_default.xml \
|
||||||
mobile/android/base/resources/drawable/address_bar_url_pressed.xml \
|
mobile/android/base/resources/drawable/address_bar_url_pressed.xml \
|
||||||
mobile/android/base/resources/drawable/awesomebar_tab_focus.xml \
|
mobile/android/base/resources/drawable/awesomebar_tab_focus.xml \
|
||||||
|
@ -387,8 +407,6 @@ MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/address_bar_bg.x
|
||||||
mobile/android/base/resources/drawable/tabs_tray_bg.9.png \
|
mobile/android/base/resources/drawable/tabs_tray_bg.9.png \
|
||||||
mobile/android/base/resources/drawable/checkerboard.png \
|
mobile/android/base/resources/drawable/checkerboard.png \
|
||||||
mobile/android/base/resources/drawable/shadow.png \
|
mobile/android/base/resources/drawable/shadow.png \
|
||||||
mobile/android/base/resources/drawable/rounded_grey_border.xml \
|
|
||||||
mobile/android/base/resources/drawable/rounded_grey_box.xml \
|
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
|
||||||
|
@ -436,6 +454,7 @@ $(RES_VALUES): \
|
||||||
$(srcdir)/resources/values/colors.xml \
|
$(srcdir)/resources/values/colors.xml \
|
||||||
$(srcdir)/resources/values/styles.xml \
|
$(srcdir)/resources/values/styles.xml \
|
||||||
$(srcdir)/resources/values/themes.xml \
|
$(srcdir)/resources/values/themes.xml \
|
||||||
|
$(srcdir)/resources/values/arrays.xml \
|
||||||
$(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/res/values/defaults.xml
|
$(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/res/values/defaults.xml
|
||||||
$(NSINSTALL) -D res/values
|
$(NSINSTALL) -D res/values
|
||||||
$(NSINSTALL) $^ res/values
|
$(NSINSTALL) $^ res/values
|
||||||
|
|
|
@ -75,6 +75,8 @@ public class Tab {
|
||||||
private HashMap<String, DoorHanger> mDoorHangers;
|
private HashMap<String, DoorHanger> mDoorHangers;
|
||||||
private long mFaviconLoadId;
|
private long mFaviconLoadId;
|
||||||
private AgentMode mAgentMode = AgentMode.MOBILE;
|
private AgentMode mAgentMode = AgentMode.MOBILE;
|
||||||
|
private String mDocumentURI;
|
||||||
|
private String mContentType;
|
||||||
|
|
||||||
static class HistoryEntry {
|
static class HistoryEntry {
|
||||||
public final String mUri; // must never be null
|
public final String mUri; // must never be null
|
||||||
|
@ -103,6 +105,8 @@ public class Tab {
|
||||||
mBookmark = false;
|
mBookmark = false;
|
||||||
mDoorHangers = new HashMap<String, DoorHanger>();
|
mDoorHangers = new HashMap<String, DoorHanger>();
|
||||||
mFaviconLoadId = 0;
|
mFaviconLoadId = 0;
|
||||||
|
mDocumentURI = "";
|
||||||
|
mContentType = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() {
|
public int getId() {
|
||||||
|
@ -182,6 +186,22 @@ public class Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDocumentURI(String documentURI) {
|
||||||
|
mDocumentURI = documentURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDocumentURI() {
|
||||||
|
return mDocumentURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentType(String contentType) {
|
||||||
|
mContentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
return mContentType;
|
||||||
|
}
|
||||||
|
|
||||||
public void updateTitle(String title) {
|
public void updateTitle(String title) {
|
||||||
mTitle = (title == null ? "" : title);
|
mTitle = (title == null ? "" : title);
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,13 @@ public class BrowserContract {
|
||||||
public static final class Bookmarks implements CommonColumns, URLColumns, ImageColumns, SyncColumns {
|
public static final class Bookmarks implements CommonColumns, URLColumns, ImageColumns, SyncColumns {
|
||||||
private Bookmarks() {}
|
private Bookmarks() {}
|
||||||
|
|
||||||
|
public static final String MOBILE_FOLDER_GUID = "mobile";
|
||||||
|
public static final String PLACES_FOLDER_GUID = "places";
|
||||||
|
public static final String MENU_FOLDER_GUID = "menu";
|
||||||
|
public static final String TAGS_FOLDER_GUID = "tags";
|
||||||
|
public static final String TOOLBAR_FOLDER_GUID = "toolbar";
|
||||||
|
public static final String UNFILED_FOLDER_GUID = "unfiled";
|
||||||
|
|
||||||
public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "bookmarks");
|
public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "bookmarks");
|
||||||
|
|
||||||
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/bookmark";
|
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/bookmark";
|
||||||
|
@ -133,4 +140,4 @@ public class BrowserContract {
|
||||||
|
|
||||||
public static final String VERSION = "version";
|
public static final String VERSION = "version";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -83,6 +83,9 @@ public class BrowserProvider extends ContentProvider {
|
||||||
static final String TABLE_HISTORY = "history";
|
static final String TABLE_HISTORY = "history";
|
||||||
static final String TABLE_IMAGES = "images";
|
static final String TABLE_IMAGES = "images";
|
||||||
|
|
||||||
|
static final String VIEW_BOOKMARKS_WITH_IMAGES = "bookmarks_with_images";
|
||||||
|
static final String VIEW_HISTORY_WITH_IMAGES = "history_with_images";
|
||||||
|
|
||||||
// Bookmark matches
|
// Bookmark matches
|
||||||
static final int BOOKMARKS = 100;
|
static final int BOOKMARKS = 100;
|
||||||
static final int BOOKMARKS_ID = 101;
|
static final int BOOKMARKS_ID = 101;
|
||||||
|
@ -106,12 +109,20 @@ public class BrowserProvider extends ContentProvider {
|
||||||
static final String DEFAULT_HISTORY_SORT_ORDER = History.DATE_LAST_VISITED + " DESC";
|
static final String DEFAULT_HISTORY_SORT_ORDER = History.DATE_LAST_VISITED + " DESC";
|
||||||
|
|
||||||
static final String TABLE_BOOKMARKS_JOIN_IMAGES = TABLE_BOOKMARKS + " LEFT OUTER JOIN " +
|
static final String TABLE_BOOKMARKS_JOIN_IMAGES = TABLE_BOOKMARKS + " LEFT OUTER JOIN " +
|
||||||
TABLE_IMAGES + " ON " + qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks.URL) +
|
"(SELECT " + Images.URL + ", " + Images.FAVICON + ", " + Images.THUMBNAIL + " FROM " +
|
||||||
" = " + qualifyColumnValue(TABLE_IMAGES, Images.URL);
|
TABLE_IMAGES + ", " + TABLE_BOOKMARKS + " WHERE " +
|
||||||
|
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " +
|
||||||
|
qualifyColumn(TABLE_IMAGES, Images.URL) + ") AS bookmark_images ON " +
|
||||||
|
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " +
|
||||||
|
qualifyColumn("bookmark_images", Images.URL);
|
||||||
|
|
||||||
static final String TABLE_HISTORY_JOIN_IMAGES = TABLE_HISTORY + " LEFT OUTER JOIN " +
|
static final String TABLE_HISTORY_JOIN_IMAGES = TABLE_HISTORY + " LEFT OUTER JOIN " +
|
||||||
TABLE_IMAGES + " ON " + qualifyColumnValue(TABLE_HISTORY, History.URL) +
|
"(SELECT " + Images.URL + ", " + Images.FAVICON + ", " + Images.THUMBNAIL + " FROM " +
|
||||||
" = " + qualifyColumnValue(TABLE_IMAGES, Images.URL);
|
TABLE_IMAGES + ", " + TABLE_HISTORY + " WHERE " +
|
||||||
|
qualifyColumn(TABLE_HISTORY, History.URL) + " = " +
|
||||||
|
qualifyColumn(TABLE_IMAGES, Images.URL) + ") AS history_images ON " +
|
||||||
|
qualifyColumn(TABLE_HISTORY, History.URL) + " = " +
|
||||||
|
qualifyColumn("history_images", Images.URL);
|
||||||
|
|
||||||
static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
|
static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
|
|
||||||
|
@ -131,7 +142,7 @@ public class BrowserProvider extends ContentProvider {
|
||||||
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "bookmarks/folder/#", BOOKMARKS_FOLDER_ID);
|
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "bookmarks/folder/#", BOOKMARKS_FOLDER_ID);
|
||||||
|
|
||||||
map = BOOKMARKS_PROJECTION_MAP;
|
map = BOOKMARKS_PROJECTION_MAP;
|
||||||
map.put(Bookmarks._ID, qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID));
|
map.put(Bookmarks._ID, Bookmarks._ID);
|
||||||
map.put(Bookmarks.TITLE, Bookmarks.TITLE);
|
map.put(Bookmarks.TITLE, Bookmarks.TITLE);
|
||||||
map.put(Bookmarks.URL, Bookmarks.URL);
|
map.put(Bookmarks.URL, Bookmarks.URL);
|
||||||
map.put(Bookmarks.FAVICON, Bookmarks.FAVICON);
|
map.put(Bookmarks.FAVICON, Bookmarks.FAVICON);
|
||||||
|
@ -142,41 +153,41 @@ public class BrowserProvider extends ContentProvider {
|
||||||
map.put(Bookmarks.TAGS, Bookmarks.TAGS);
|
map.put(Bookmarks.TAGS, Bookmarks.TAGS);
|
||||||
map.put(Bookmarks.DESCRIPTION, Bookmarks.DESCRIPTION);
|
map.put(Bookmarks.DESCRIPTION, Bookmarks.DESCRIPTION);
|
||||||
map.put(Bookmarks.KEYWORD, Bookmarks.KEYWORD);
|
map.put(Bookmarks.KEYWORD, Bookmarks.KEYWORD);
|
||||||
map.put(Bookmarks.DATE_CREATED, qualifyColumn(TABLE_BOOKMARKS, Bookmarks.DATE_CREATED));
|
map.put(Bookmarks.DATE_CREATED, Bookmarks.DATE_CREATED);
|
||||||
map.put(Bookmarks.DATE_MODIFIED, qualifyColumn(TABLE_BOOKMARKS, Bookmarks.DATE_MODIFIED));
|
map.put(Bookmarks.DATE_MODIFIED, Bookmarks.DATE_MODIFIED);
|
||||||
map.put(Bookmarks.GUID, qualifyColumn(TABLE_BOOKMARKS, Bookmarks.GUID));
|
map.put(Bookmarks.GUID, Bookmarks.GUID);
|
||||||
map.put(Bookmarks.IS_DELETED, qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED));
|
map.put(Bookmarks.IS_DELETED, Bookmarks.IS_DELETED);
|
||||||
|
|
||||||
// History
|
// History
|
||||||
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "history", HISTORY);
|
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "history", HISTORY);
|
||||||
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "history/#", HISTORY_ID);
|
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "history/#", HISTORY_ID);
|
||||||
|
|
||||||
map = HISTORY_PROJECTION_MAP;
|
map = HISTORY_PROJECTION_MAP;
|
||||||
map.put(History._ID, qualifyColumn(TABLE_HISTORY, History._ID));
|
map.put(History._ID, History._ID);
|
||||||
map.put(History.TITLE, History.TITLE);
|
map.put(History.TITLE, History.TITLE);
|
||||||
map.put(History.URL, History.URL);
|
map.put(History.URL, History.URL);
|
||||||
map.put(History.FAVICON, History.FAVICON);
|
map.put(History.FAVICON, History.FAVICON);
|
||||||
map.put(History.THUMBNAIL, History.THUMBNAIL);
|
map.put(History.THUMBNAIL, History.THUMBNAIL);
|
||||||
map.put(History.VISITS, History.VISITS);
|
map.put(History.VISITS, History.VISITS);
|
||||||
map.put(History.DATE_LAST_VISITED, History.DATE_LAST_VISITED);
|
map.put(History.DATE_LAST_VISITED, History.DATE_LAST_VISITED);
|
||||||
map.put(History.DATE_CREATED, qualifyColumn(TABLE_HISTORY, History.DATE_CREATED));
|
map.put(History.DATE_CREATED, History.DATE_CREATED);
|
||||||
map.put(History.DATE_MODIFIED, qualifyColumn(TABLE_HISTORY, History.DATE_MODIFIED));
|
map.put(History.DATE_MODIFIED, History.DATE_MODIFIED);
|
||||||
map.put(History.GUID, qualifyColumn(TABLE_HISTORY, History.GUID));
|
map.put(History.GUID, History.GUID);
|
||||||
map.put(History.IS_DELETED, qualifyColumn(TABLE_HISTORY, History.IS_DELETED));
|
map.put(History.IS_DELETED, History.IS_DELETED);
|
||||||
|
|
||||||
// Images
|
// Images
|
||||||
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "images", IMAGES);
|
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "images", IMAGES);
|
||||||
|
|
||||||
map = IMAGES_PROJECTION_MAP;
|
map = IMAGES_PROJECTION_MAP;
|
||||||
map.put(Images._ID, qualifyColumn(TABLE_IMAGES, Images._ID));
|
map.put(Images._ID, Images._ID);
|
||||||
map.put(Images.URL, Images.URL);
|
map.put(Images.URL, Images.URL);
|
||||||
map.put(Images.FAVICON, Images.FAVICON);
|
map.put(Images.FAVICON, Images.FAVICON);
|
||||||
map.put(Images.FAVICON_URL, Images.FAVICON_URL);
|
map.put(Images.FAVICON_URL, Images.FAVICON_URL);
|
||||||
map.put(Images.THUMBNAIL, Images.THUMBNAIL);
|
map.put(Images.THUMBNAIL, Images.THUMBNAIL);
|
||||||
map.put(Images.DATE_CREATED, qualifyColumn(TABLE_IMAGES, Images.DATE_CREATED));
|
map.put(Images.DATE_CREATED, Images.DATE_CREATED);
|
||||||
map.put(Images.DATE_MODIFIED, qualifyColumn(TABLE_IMAGES, Images.DATE_MODIFIED));
|
map.put(Images.DATE_MODIFIED, Images.DATE_MODIFIED);
|
||||||
map.put(Images.GUID, qualifyColumn(TABLE_IMAGES, Images.GUID));
|
map.put(Images.GUID, Images.GUID);
|
||||||
map.put(Images.IS_DELETED, qualifyColumn(TABLE_IMAGES, Images.IS_DELETED));
|
map.put(Images.IS_DELETED, Images.IS_DELETED);
|
||||||
|
|
||||||
// Schema
|
// Schema
|
||||||
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "schema", SCHEMA);
|
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "schema", SCHEMA);
|
||||||
|
@ -186,10 +197,6 @@ public class BrowserProvider extends ContentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
static final String qualifyColumn(String table, String column) {
|
static final String qualifyColumn(String table, String column) {
|
||||||
return table + "." + column + " AS " + column;
|
|
||||||
}
|
|
||||||
|
|
||||||
static final String qualifyColumnValue(String table, String column) {
|
|
||||||
return table + "." + column;
|
return table + "." + column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +246,16 @@ public class BrowserProvider extends ContentProvider {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean hasImagesInProjection(String[] projection) {
|
||||||
|
for (int i = 0; i < projection.length; ++i) {
|
||||||
|
if (projection[i].equals(Images.FAVICON) ||
|
||||||
|
projection[i].equals(Images.THUMBNAIL))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
final class DatabaseHelper extends SQLiteOpenHelper {
|
final class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
public DatabaseHelper(Context context, String databasePath) {
|
public DatabaseHelper(Context context, String databasePath) {
|
||||||
super(context, databasePath, null, DATABASE_VERSION);
|
super(context, databasePath, null, DATABASE_VERSION);
|
||||||
|
@ -314,9 +331,34 @@ public class BrowserProvider extends ContentProvider {
|
||||||
db.execSQL("CREATE INDEX images_modified_index ON " + TABLE_IMAGES + "("
|
db.execSQL("CREATE INDEX images_modified_index ON " + TABLE_IMAGES + "("
|
||||||
+ Images.DATE_MODIFIED + ")");
|
+ Images.DATE_MODIFIED + ")");
|
||||||
|
|
||||||
|
db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_BOOKMARKS_WITH_IMAGES + " AS " +
|
||||||
|
"SELECT " + qualifyColumn(TABLE_BOOKMARKS, "*") +
|
||||||
|
", " + Images.FAVICON + ", " + Images.THUMBNAIL + " FROM " +
|
||||||
|
TABLE_BOOKMARKS_JOIN_IMAGES);
|
||||||
|
|
||||||
|
db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_HISTORY_WITH_IMAGES + " AS " +
|
||||||
|
"SELECT " + qualifyColumn(TABLE_HISTORY, "*") +
|
||||||
|
", " + Images.FAVICON + ", " + Images.THUMBNAIL + " FROM " +
|
||||||
|
TABLE_HISTORY_JOIN_IMAGES);
|
||||||
|
|
||||||
|
createMobileBookmarksFolder(db);
|
||||||
|
|
||||||
// FIXME: Create default bookmarks here
|
// FIXME: Create default bookmarks here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createMobileBookmarksFolder(SQLiteDatabase db) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Bookmarks.GUID, Bookmarks.MOBILE_FOLDER_GUID);
|
||||||
|
values.put(Bookmarks.IS_FOLDER, 1);
|
||||||
|
values.put(Bookmarks.POSITION, 0);
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
values.put(Bookmarks.DATE_CREATED, now);
|
||||||
|
values.put(Bookmarks.DATE_MODIFIED, now);
|
||||||
|
|
||||||
|
db.insertOrThrow(TABLE_BOOKMARKS, Bookmarks.GUID, values);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
Log.d(LOGTAG, "Upgrading browser.db: " + db.getPath() + " from " +
|
Log.d(LOGTAG, "Upgrading browser.db: " + db.getPath() + " from " +
|
||||||
|
@ -428,11 +470,8 @@ public class BrowserProvider extends ContentProvider {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
String isDeletedColumn = qualifyColumnValue(tableName, SyncColumns.IS_DELETED);
|
String selection = SyncColumns.IS_DELETED + " = 1 AND " +
|
||||||
String dateModifiedColumn = qualifyColumnValue(tableName, SyncColumns.DATE_MODIFIED);
|
SyncColumns.DATE_MODIFIED + " <= " + (now - MAX_AGE_OF_DELETED_RECORDS);
|
||||||
|
|
||||||
String selection = isDeletedColumn + " = 1 AND " +
|
|
||||||
dateModifiedColumn + " <= " + (now - MAX_AGE_OF_DELETED_RECORDS);
|
|
||||||
|
|
||||||
cursor = query(uriWithArgs,
|
cursor = query(uriWithArgs,
|
||||||
new String[] { CommonColumns._ID },
|
new String[] { CommonColumns._ID },
|
||||||
|
@ -801,22 +840,18 @@ public class BrowserProvider extends ContentProvider {
|
||||||
|
|
||||||
if (match == BOOKMARKS_ID) {
|
if (match == BOOKMARKS_ID) {
|
||||||
Log.d(LOGTAG, "Query is BOOKMARKS_ID: " + uri);
|
Log.d(LOGTAG, "Query is BOOKMARKS_ID: " + uri);
|
||||||
selection = concatenateWhere(selection,
|
selection = concatenateWhere(selection, Bookmarks._ID + " = ?");
|
||||||
qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks._ID) + " = ?");
|
|
||||||
selectionArgs = appendSelectionArgs(selectionArgs,
|
selectionArgs = appendSelectionArgs(selectionArgs,
|
||||||
new String[] { Long.toString(ContentUris.parseId(uri)) });
|
new String[] { Long.toString(ContentUris.parseId(uri)) });
|
||||||
} else if (match == BOOKMARKS_FOLDER_ID) {
|
} else if (match == BOOKMARKS_FOLDER_ID) {
|
||||||
Log.d(LOGTAG, "Query is BOOKMARKS_FOLDER_ID: " + uri);
|
Log.d(LOGTAG, "Query is BOOKMARKS_FOLDER_ID: " + uri);
|
||||||
selection = concatenateWhere(selection,
|
selection = concatenateWhere(selection, Bookmarks.PARENT + " = ?");
|
||||||
qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks.PARENT) + " = ?");
|
|
||||||
selectionArgs = appendSelectionArgs(selectionArgs,
|
selectionArgs = appendSelectionArgs(selectionArgs,
|
||||||
new String[] { Long.toString(ContentUris.parseId(uri)) });
|
new String[] { Long.toString(ContentUris.parseId(uri)) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shouldShowDeleted(uri)) {
|
if (!shouldShowDeleted(uri))
|
||||||
String isDeletedColumn = qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks.IS_DELETED);
|
selection = concatenateWhere(Bookmarks.IS_DELETED + " = 0", selection);
|
||||||
selection = concatenateWhere(isDeletedColumn + " = 0", selection);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(sortOrder)) {
|
if (TextUtils.isEmpty(sortOrder)) {
|
||||||
Log.d(LOGTAG, "Using default sort order on query: " + uri);
|
Log.d(LOGTAG, "Using default sort order on query: " + uri);
|
||||||
|
@ -824,7 +859,11 @@ public class BrowserProvider extends ContentProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP);
|
qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP);
|
||||||
qb.setTables(TABLE_BOOKMARKS_JOIN_IMAGES);
|
|
||||||
|
if (hasImagesInProjection(projection))
|
||||||
|
qb.setTables(VIEW_BOOKMARKS_WITH_IMAGES);
|
||||||
|
else
|
||||||
|
qb.setTables(TABLE_BOOKMARKS);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -835,22 +874,23 @@ public class BrowserProvider extends ContentProvider {
|
||||||
|
|
||||||
if (match == HISTORY_ID) {
|
if (match == HISTORY_ID) {
|
||||||
Log.d(LOGTAG, "Query is HISTORY_ID: " + uri);
|
Log.d(LOGTAG, "Query is HISTORY_ID: " + uri);
|
||||||
selection = concatenateWhere(selection,
|
selection = concatenateWhere(selection, History._ID + " = ?");
|
||||||
qualifyColumnValue(TABLE_HISTORY, History._ID) + " = ?");
|
|
||||||
selectionArgs = appendSelectionArgs(selectionArgs,
|
selectionArgs = appendSelectionArgs(selectionArgs,
|
||||||
new String[] { Long.toString(ContentUris.parseId(uri)) });
|
new String[] { Long.toString(ContentUris.parseId(uri)) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shouldShowDeleted(uri)) {
|
if (!shouldShowDeleted(uri))
|
||||||
String isDeletedColumn = qualifyColumnValue(TABLE_HISTORY, History.IS_DELETED);
|
selection = concatenateWhere(History.IS_DELETED + " = 0", selection);
|
||||||
selection = concatenateWhere(isDeletedColumn + " = 0", selection);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(sortOrder))
|
if (TextUtils.isEmpty(sortOrder))
|
||||||
sortOrder = DEFAULT_HISTORY_SORT_ORDER;
|
sortOrder = DEFAULT_HISTORY_SORT_ORDER;
|
||||||
|
|
||||||
qb.setProjectionMap(HISTORY_PROJECTION_MAP);
|
qb.setProjectionMap(HISTORY_PROJECTION_MAP);
|
||||||
qb.setTables(TABLE_HISTORY_JOIN_IMAGES);
|
|
||||||
|
if (hasImagesInProjection(projection))
|
||||||
|
qb.setTables(VIEW_HISTORY_WITH_IMAGES);
|
||||||
|
else
|
||||||
|
qb.setTables(TABLE_HISTORY);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -861,16 +901,13 @@ public class BrowserProvider extends ContentProvider {
|
||||||
|
|
||||||
if (match == IMAGES_ID) {
|
if (match == IMAGES_ID) {
|
||||||
Log.d(LOGTAG, "Query is IMAGES_ID: " + uri);
|
Log.d(LOGTAG, "Query is IMAGES_ID: " + uri);
|
||||||
selection = concatenateWhere(selection,
|
selection = concatenateWhere(selection, Images._ID + " = ?");
|
||||||
qualifyColumnValue(TABLE_IMAGES, Images._ID) + " = ?");
|
|
||||||
selectionArgs = appendSelectionArgs(selectionArgs,
|
selectionArgs = appendSelectionArgs(selectionArgs,
|
||||||
new String[] { Long.toString(ContentUris.parseId(uri)) });
|
new String[] { Long.toString(ContentUris.parseId(uri)) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shouldShowDeleted(uri)) {
|
if (!shouldShowDeleted(uri))
|
||||||
String isDeletedColumn = qualifyColumnValue(TABLE_IMAGES, Images.IS_DELETED);
|
selection = concatenateWhere(Images.IS_DELETED + " = 0", selection);
|
||||||
selection = concatenateWhere(isDeletedColumn + " = 0", selection);
|
|
||||||
}
|
|
||||||
|
|
||||||
qb.setProjectionMap(IMAGES_PROJECTION_MAP);
|
qb.setProjectionMap(IMAGES_PROJECTION_MAP);
|
||||||
qb.setTables(TABLE_IMAGES);
|
qb.setTables(TABLE_IMAGES);
|
||||||
|
@ -1173,10 +1210,10 @@ public class BrowserProvider extends ContentProvider {
|
||||||
|
|
||||||
String selection = Images.URL + " NOT IN (SELECT " + Bookmarks.URL +
|
String selection = Images.URL + " NOT IN (SELECT " + Bookmarks.URL +
|
||||||
" FROM " + TABLE_BOOKMARKS + " WHERE " + Bookmarks.URL + " IS NOT NULL AND " +
|
" FROM " + TABLE_BOOKMARKS + " WHERE " + Bookmarks.URL + " IS NOT NULL AND " +
|
||||||
qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0) AND " +
|
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0) AND " +
|
||||||
Images.URL + " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY +
|
Images.URL + " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY +
|
||||||
" WHERE " + History.URL + " IS NOT NULL AND " +
|
" WHERE " + History.URL + " IS NOT NULL AND " +
|
||||||
qualifyColumnValue(TABLE_HISTORY, History.IS_DELETED) + " = 0)";
|
qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0)";
|
||||||
|
|
||||||
return deleteImages(uri, selection, null);
|
return deleteImages(uri, selection, null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,9 +65,11 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||||
public static final int TRUNCATE_N_OLDEST = 5;
|
public static final int TRUNCATE_N_OLDEST = 5;
|
||||||
|
|
||||||
private final String mProfile;
|
private final String mProfile;
|
||||||
|
private long mMobileFolderId;
|
||||||
|
|
||||||
public LocalBrowserDB(String profile) {
|
public LocalBrowserDB(String profile) {
|
||||||
mProfile = profile;
|
mProfile = profile;
|
||||||
|
mMobileFolderId = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Uri appendProfileAndLimit(Uri uri, int limit) {
|
private Uri appendProfileAndLimit(Uri uri, int limit) {
|
||||||
|
@ -233,10 +235,38 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||||
return (count == 1);
|
return (count == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long getMobileBookmarksFolderId(ContentResolver cr) {
|
||||||
|
if (mMobileFolderId >= 0)
|
||||||
|
return mMobileFolderId;
|
||||||
|
|
||||||
|
Cursor c = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
c = cr.query(appendProfile(Bookmarks.CONTENT_URI),
|
||||||
|
new String[] { Bookmarks._ID },
|
||||||
|
Bookmarks.GUID + " = ?",
|
||||||
|
new String[] { Bookmarks.MOBILE_FOLDER_GUID },
|
||||||
|
null);
|
||||||
|
|
||||||
|
if (c.moveToFirst())
|
||||||
|
mMobileFolderId = c.getLong(c.getColumnIndexOrThrow(Bookmarks._ID));
|
||||||
|
} finally {
|
||||||
|
if (c != null)
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mMobileFolderId;
|
||||||
|
}
|
||||||
|
|
||||||
public void addBookmark(ContentResolver cr, String title, String uri) {
|
public void addBookmark(ContentResolver cr, String title, String uri) {
|
||||||
|
long folderId = getMobileBookmarksFolderId(cr);
|
||||||
|
if (folderId < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(Browser.BookmarkColumns.TITLE, title);
|
values.put(Browser.BookmarkColumns.TITLE, title);
|
||||||
values.put(Bookmarks.URL, uri);
|
values.put(Bookmarks.URL, uri);
|
||||||
|
values.put(Bookmarks.PARENT, folderId);
|
||||||
|
|
||||||
// Restore deleted record if possible
|
// Restore deleted record if possible
|
||||||
values.put(Bookmarks.IS_DELETED, 0);
|
values.put(Bookmarks.IS_DELETED, 0);
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
|
|
||||||
package org.mozilla.gecko.gfx;
|
package org.mozilla.gecko.gfx;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.GeckoAppShell;
|
||||||
import org.mozilla.gecko.gfx.CairoImage;
|
import org.mozilla.gecko.gfx.CairoImage;
|
||||||
import org.mozilla.gecko.gfx.CairoUtils;
|
import org.mozilla.gecko.gfx.CairoUtils;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
@ -46,6 +47,7 @@ import java.nio.ByteBuffer;
|
||||||
public class BufferedCairoImage extends CairoImage {
|
public class BufferedCairoImage extends CairoImage {
|
||||||
private ByteBuffer mBuffer;
|
private ByteBuffer mBuffer;
|
||||||
private int mWidth, mHeight, mFormat;
|
private int mWidth, mHeight, mFormat;
|
||||||
|
private boolean mNeedToFreeBuffer = false;
|
||||||
|
|
||||||
/** Creates a buffered Cairo image from a byte buffer. */
|
/** Creates a buffered Cairo image from a byte buffer. */
|
||||||
public BufferedCairoImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) {
|
public BufferedCairoImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) {
|
||||||
|
@ -57,11 +59,23 @@ public class BufferedCairoImage extends CairoImage {
|
||||||
mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig());
|
mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig());
|
||||||
mWidth = bitmap.getWidth();
|
mWidth = bitmap.getWidth();
|
||||||
mHeight = bitmap.getHeight();
|
mHeight = bitmap.getHeight();
|
||||||
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
|
mNeedToFreeBuffer = true;
|
||||||
|
mBuffer = GeckoAppShell.allocateDirectBuffer(mWidth * mHeight * 4);
|
||||||
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
|
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected void finalize() throws Throwable {
|
||||||
|
try {
|
||||||
|
if (mNeedToFreeBuffer && mBuffer != null)
|
||||||
|
GeckoAppShell.freeDirectBuffer(mBuffer);
|
||||||
|
mNeedToFreeBuffer = false;
|
||||||
|
mBuffer = null;
|
||||||
|
} finally {
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ByteBuffer getBuffer() { return mBuffer; }
|
public ByteBuffer getBuffer() { return mBuffer; }
|
||||||
@Override
|
@Override
|
||||||
public int getWidth() { return mWidth; }
|
public int getWidth() { return mWidth; }
|
||||||
|
|
|
@ -103,7 +103,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
||||||
|
|
||||||
mScreenSize = new IntSize(1, 1);
|
mScreenSize = new IntSize(1, 1);
|
||||||
|
|
||||||
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 2);
|
mBuffer = GeckoAppShell.allocateDirectBuffer(mWidth * mHeight * 2);
|
||||||
|
|
||||||
mCairoImage = new CairoImage() {
|
mCairoImage = new CairoImage() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -119,6 +119,17 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
||||||
mTileLayer = new SingleTileLayer(mCairoImage);
|
mTileLayer = new SingleTileLayer(mCairoImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
try {
|
||||||
|
if (mBuffer != null)
|
||||||
|
GeckoAppShell.freeDirectBuffer(mBuffer);
|
||||||
|
mBuffer = null;
|
||||||
|
} finally {
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Attaches the root layer to the layer controller so that Gecko appears. */
|
/** Attaches the root layer to the layer controller so that Gecko appears. */
|
||||||
@Override
|
@Override
|
||||||
public void setLayerController(LayerController layerController) {
|
public void setLayerController(LayerController layerController) {
|
||||||
|
@ -151,28 +162,22 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
||||||
mGeckoViewport = new ViewportMetrics(viewportObject);
|
mGeckoViewport = new ViewportMetrics(viewportObject);
|
||||||
mGeckoViewport.setSize(viewportSize);
|
mGeckoViewport.setSize(viewportSize);
|
||||||
|
|
||||||
mTileLayer.setOrigin(PointUtils.round(mGeckoViewport.getDisplayportOrigin()));
|
LayerController controller = getLayerController();
|
||||||
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
|
synchronized (controller) {
|
||||||
|
PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin();
|
||||||
|
mTileLayer.setOrigin(PointUtils.round(displayportOrigin));
|
||||||
|
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
|
||||||
|
|
||||||
// Make sure LayerController metrics changes only happen in the
|
if (onlyUpdatePageSize) {
|
||||||
// UI thread.
|
// Don't adjust page size when zooming unless zoom levels are
|
||||||
final LayerController controller = getLayerController();
|
// approximately equal.
|
||||||
|
if (FloatUtils.fuzzyEquals(controller.getZoomFactor(),
|
||||||
if (controller != null) {
|
mGeckoViewport.getZoomFactor()))
|
||||||
controller.post(new Runnable() {
|
controller.setPageSize(mGeckoViewport.getPageSize());
|
||||||
@Override
|
} else {
|
||||||
public void run() {
|
controller.setViewportMetrics(mGeckoViewport);
|
||||||
if (onlyUpdatePageSize) {
|
controller.notifyPanZoomControllerOfGeometryChange(true);
|
||||||
// Don't adjust page size when zooming unless zoom levels are
|
}
|
||||||
// approximately equal.
|
|
||||||
if (FloatUtils.fuzzyEquals(controller.getZoomFactor(), mGeckoViewport.getZoomFactor()))
|
|
||||||
controller.setPageSize(mGeckoViewport.getPageSize());
|
|
||||||
} else {
|
|
||||||
controller.setViewportMetrics(mGeckoViewport);
|
|
||||||
controller.notifyPanZoomControllerOfGeometryChange(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
Log.e(LOGTAG, "Bad viewport description: " + viewportDescription);
|
Log.e(LOGTAG, "Bad viewport description: " + viewportDescription);
|
||||||
|
@ -209,7 +214,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
||||||
b.copyPixelsFromBuffer(mBuffer.asIntBuffer());
|
b.copyPixelsFromBuffer(mBuffer.asIntBuffer());
|
||||||
return b;
|
return b;
|
||||||
} catch (OutOfMemoryError oom) {
|
} catch (OutOfMemoryError oom) {
|
||||||
Log.e(LOGTAG, "Unable to create bitmap", oom);
|
Log.w(LOGTAG, "Unable to create bitmap", oom);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,12 +58,13 @@ import android.view.GestureDetector;
|
||||||
import android.view.ScaleGestureDetector;
|
import android.view.ScaleGestureDetector;
|
||||||
import android.view.View.OnTouchListener;
|
import android.view.View.OnTouchListener;
|
||||||
import java.lang.Math;
|
import java.lang.Math;
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The layer controller manages a tile that represents the visible page. It does panning and
|
* The layer controller manages a tile that represents the visible page. It does panning and
|
||||||
* zooming natively by delegating to a panning/zooming controller. Touch events can be dispatched
|
* zooming natively by delegating to a panning/zooming controller. Touch events can be dispatched
|
||||||
* to a higher-level view.
|
* to a higher-level view.
|
||||||
|
*
|
||||||
|
* Many methods require that the monitor be held, with a synchronized (controller) { ... } block.
|
||||||
*/
|
*/
|
||||||
public class LayerController {
|
public class LayerController {
|
||||||
private static final String LOGTAG = "GeckoLayerController";
|
private static final String LOGTAG = "GeckoLayerController";
|
||||||
|
@ -155,7 +156,8 @@ public class LayerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The view calls this to indicate that the viewport changed size.
|
* The view calls this function to indicate that the viewport changed size. It must hold the
|
||||||
|
* monitor while calling it.
|
||||||
*
|
*
|
||||||
* TODO: Refactor this to use an interface. Expose that interface only to the view and not
|
* TODO: Refactor this to use an interface. Expose that interface only to the view and not
|
||||||
* to the layer client. That way, the layer client won't be tempted to call this, which might
|
* to the layer client. That way, the layer client won't be tempted to call this, which might
|
||||||
|
@ -173,6 +175,7 @@ public class LayerController {
|
||||||
mView.requestRender();
|
mView.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Scrolls the viewport to the given point. You must hold the monitor while calling this. */
|
||||||
public void scrollTo(PointF point) {
|
public void scrollTo(PointF point) {
|
||||||
mViewportMetrics.setOrigin(point);
|
mViewportMetrics.setOrigin(point);
|
||||||
notifyLayerClientOfGeometryChange();
|
notifyLayerClientOfGeometryChange();
|
||||||
|
@ -181,6 +184,7 @@ public class LayerController {
|
||||||
mView.requestRender();
|
mView.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Scrolls the viewport by the given offset. You must hold the monitor while calling this. */
|
||||||
public void scrollBy(PointF point) {
|
public void scrollBy(PointF point) {
|
||||||
PointF origin = mViewportMetrics.getOrigin();
|
PointF origin = mViewportMetrics.getOrigin();
|
||||||
origin.offset(point.x, point.y);
|
origin.offset(point.x, point.y);
|
||||||
|
@ -192,6 +196,7 @@ public class LayerController {
|
||||||
mView.requestRender();
|
mView.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sets the current viewport. You must hold the monitor while calling this. */
|
||||||
public void setViewport(RectF viewport) {
|
public void setViewport(RectF viewport) {
|
||||||
mViewportMetrics.setViewport(viewport);
|
mViewportMetrics.setViewport(viewport);
|
||||||
notifyLayerClientOfGeometryChange();
|
notifyLayerClientOfGeometryChange();
|
||||||
|
@ -200,6 +205,7 @@ public class LayerController {
|
||||||
mView.requestRender();
|
mView.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sets the current page size. You must hold the monitor while calling this. */
|
||||||
public void setPageSize(FloatSize size) {
|
public void setPageSize(FloatSize size) {
|
||||||
if (mViewportMetrics.getPageSize().fuzzyEquals(size))
|
if (mViewportMetrics.getPageSize().fuzzyEquals(size))
|
||||||
return;
|
return;
|
||||||
|
@ -215,7 +221,8 @@ public class LayerController {
|
||||||
/**
|
/**
|
||||||
* Sets the entire viewport metrics at once. This function does not notify the layer client or
|
* Sets the entire viewport metrics at once. This function does not notify the layer client or
|
||||||
* the pan/zoom controller, so you will need to call notifyLayerClientOfGeometryChange() or
|
* the pan/zoom controller, so you will need to call notifyLayerClientOfGeometryChange() or
|
||||||
* notifyPanZoomControllerOfGeometryChange() after calling this.
|
* notifyPanZoomControllerOfGeometryChange() after calling this. You must hold the monitor
|
||||||
|
* while calling this.
|
||||||
*/
|
*/
|
||||||
public void setViewportMetrics(ViewportMetrics viewport) {
|
public void setViewportMetrics(ViewportMetrics viewport) {
|
||||||
mViewportMetrics = new ViewportMetrics(viewport);
|
mViewportMetrics = new ViewportMetrics(viewport);
|
||||||
|
@ -223,10 +230,15 @@ public class LayerController {
|
||||||
mView.requestRender();
|
mView.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Scales the viewport. You must hold the monitor while calling this. */
|
||||||
public void scaleTo(float zoomFactor) {
|
public void scaleTo(float zoomFactor) {
|
||||||
scaleWithFocus(zoomFactor, new PointF(0,0));
|
scaleWithFocus(zoomFactor, new PointF(0,0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales the viewport, keeping the given focus point in the same place before and after the
|
||||||
|
* scale operation. You must hold the monitor while calling this.
|
||||||
|
*/
|
||||||
public void scaleWithFocus(float zoomFactor, PointF focus) {
|
public void scaleWithFocus(float zoomFactor, PointF focus) {
|
||||||
mViewportMetrics.scaleTo(zoomFactor, focus);
|
mViewportMetrics.scaleTo(zoomFactor, focus);
|
||||||
|
|
||||||
|
@ -237,6 +249,10 @@ public class LayerController {
|
||||||
mView.requestRender();
|
mView.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the viewport origin and scales in one operation. You must hold the monitor while
|
||||||
|
* calling this.
|
||||||
|
*/
|
||||||
public void scaleWithOrigin(float zoomFactor, PointF origin) {
|
public void scaleWithOrigin(float zoomFactor, PointF origin) {
|
||||||
mViewportMetrics.setOrigin(origin);
|
mViewportMetrics.setOrigin(origin);
|
||||||
scaleTo(zoomFactor);
|
scaleTo(zoomFactor);
|
||||||
|
|
|
@ -132,9 +132,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called whenever a new frame is about to be drawn.
|
* Called whenever a new frame is about to be drawn.
|
||||||
*
|
|
||||||
* FIXME: This is racy. Layers and page sizes can be modified by the pan/zoom controller while
|
|
||||||
* this is going on.
|
|
||||||
*/
|
*/
|
||||||
public void onDrawFrame(GL10 gl) {
|
public void onDrawFrame(GL10 gl) {
|
||||||
long frameStartTime = SystemClock.uptimeMillis();
|
long frameStartTime = SystemClock.uptimeMillis();
|
||||||
|
@ -142,63 +139,68 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
||||||
TextureReaper.get().reap(gl);
|
TextureReaper.get().reap(gl);
|
||||||
|
|
||||||
LayerController controller = mView.getController();
|
LayerController controller = mView.getController();
|
||||||
Layer rootLayer = controller.getRoot();
|
RenderContext screenContext = createScreenContext();
|
||||||
RenderContext screenContext = createScreenContext(), pageContext = createPageContext();
|
|
||||||
|
|
||||||
if (!pageContext.fuzzyEquals(mLastPageContext)) {
|
synchronized (controller) {
|
||||||
// the viewport or page changed, so show the scrollbars again
|
Layer rootLayer = controller.getRoot();
|
||||||
// as per UX decision
|
RenderContext pageContext = createPageContext();
|
||||||
mVertScrollLayer.unfade();
|
|
||||||
mHorizScrollLayer.unfade();
|
if (!pageContext.fuzzyEquals(mLastPageContext)) {
|
||||||
mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
|
// the viewport or page changed, so show the scrollbars again
|
||||||
} else if (mFadeRunnable.timeToFade()) {
|
// as per UX decision
|
||||||
boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade();
|
mVertScrollLayer.unfade();
|
||||||
if (stillFading) {
|
mHorizScrollLayer.unfade();
|
||||||
mFadeRunnable.scheduleNextFadeFrame();
|
mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
|
||||||
|
} else if (mFadeRunnable.timeToFade()) {
|
||||||
|
boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade();
|
||||||
|
if (stillFading) {
|
||||||
|
mFadeRunnable.scheduleNextFadeFrame();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
mLastPageContext = pageContext;
|
||||||
|
|
||||||
|
/* Update layers. */
|
||||||
|
if (rootLayer != null) rootLayer.update(gl);
|
||||||
|
mShadowLayer.update(gl);
|
||||||
|
mCheckerboardLayer.update(gl);
|
||||||
|
mFrameRateLayer.update(gl);
|
||||||
|
mVertScrollLayer.update(gl);
|
||||||
|
mHorizScrollLayer.update(gl);
|
||||||
|
|
||||||
|
/* Draw the background. */
|
||||||
|
gl.glClearColor(BACKGROUND_COLOR_R, BACKGROUND_COLOR_G, BACKGROUND_COLOR_B, 1.0f);
|
||||||
|
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
/* Draw the drop shadow, if we need to. */
|
||||||
|
Rect pageRect = getPageRect();
|
||||||
|
RectF untransformedPageRect = new RectF(0.0f, 0.0f, pageRect.width(),
|
||||||
|
pageRect.height());
|
||||||
|
if (!untransformedPageRect.contains(controller.getViewport()))
|
||||||
|
mShadowLayer.draw(pageContext);
|
||||||
|
|
||||||
|
/* Draw the checkerboard. */
|
||||||
|
Rect scissorRect = transformToScissorRect(pageRect);
|
||||||
|
gl.glEnable(GL10.GL_SCISSOR_TEST);
|
||||||
|
gl.glScissor(scissorRect.left, scissorRect.top,
|
||||||
|
scissorRect.width(), scissorRect.height());
|
||||||
|
|
||||||
|
mCheckerboardLayer.draw(screenContext);
|
||||||
|
|
||||||
|
/* Draw the layer the client added to us. */
|
||||||
|
if (rootLayer != null)
|
||||||
|
rootLayer.draw(pageContext);
|
||||||
|
|
||||||
|
gl.glDisable(GL10.GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
/* Draw the vertical scrollbar. */
|
||||||
|
IntSize screenSize = new IntSize(controller.getViewportSize());
|
||||||
|
if (pageRect.height() > screenSize.height)
|
||||||
|
mVertScrollLayer.draw(pageContext);
|
||||||
|
|
||||||
|
/* Draw the horizontal scrollbar. */
|
||||||
|
if (pageRect.width() > screenSize.width)
|
||||||
|
mHorizScrollLayer.draw(pageContext);
|
||||||
}
|
}
|
||||||
mLastPageContext = pageContext;
|
|
||||||
|
|
||||||
/* Update layers. */
|
|
||||||
if (rootLayer != null) rootLayer.update(gl);
|
|
||||||
mShadowLayer.update(gl);
|
|
||||||
mCheckerboardLayer.update(gl);
|
|
||||||
mFrameRateLayer.update(gl);
|
|
||||||
mVertScrollLayer.update(gl);
|
|
||||||
mHorizScrollLayer.update(gl);
|
|
||||||
|
|
||||||
/* Draw the background. */
|
|
||||||
gl.glClearColor(BACKGROUND_COLOR_R, BACKGROUND_COLOR_G, BACKGROUND_COLOR_B, 1.0f);
|
|
||||||
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
/* Draw the drop shadow, if we need to. */
|
|
||||||
Rect pageRect = getPageRect();
|
|
||||||
RectF untransformedPageRect = new RectF(0.0f, 0.0f, pageRect.width(), pageRect.height());
|
|
||||||
if (!untransformedPageRect.contains(controller.getViewport()))
|
|
||||||
mShadowLayer.draw(pageContext);
|
|
||||||
|
|
||||||
/* Draw the checkerboard. */
|
|
||||||
Rect scissorRect = transformToScissorRect(pageRect);
|
|
||||||
gl.glEnable(GL10.GL_SCISSOR_TEST);
|
|
||||||
gl.glScissor(scissorRect.left, scissorRect.top,
|
|
||||||
scissorRect.width(), scissorRect.height());
|
|
||||||
|
|
||||||
mCheckerboardLayer.draw(screenContext);
|
|
||||||
|
|
||||||
/* Draw the layer the client added to us. */
|
|
||||||
if (rootLayer != null)
|
|
||||||
rootLayer.draw(pageContext);
|
|
||||||
|
|
||||||
gl.glDisable(GL10.GL_SCISSOR_TEST);
|
|
||||||
|
|
||||||
/* Draw the vertical scrollbar. */
|
|
||||||
IntSize screenSize = new IntSize(controller.getViewportSize());
|
|
||||||
if (pageRect.height() > screenSize.height)
|
|
||||||
mVertScrollLayer.draw(pageContext);
|
|
||||||
|
|
||||||
/* Draw the horizontal scrollbar. */
|
|
||||||
if (pageRect.width() > screenSize.width)
|
|
||||||
mHorizScrollLayer.draw(pageContext);
|
|
||||||
|
|
||||||
/* Draw the FPS. */
|
/* Draw the FPS. */
|
||||||
if (mShowFrameRate) {
|
if (mShowFrameRate) {
|
||||||
|
|
|
@ -51,6 +51,7 @@ import android.util.Log;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import javax.microedition.khronos.opengles.GL10;
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
import org.mozilla.gecko.FloatUtils;
|
import org.mozilla.gecko.FloatUtils;
|
||||||
|
import org.mozilla.gecko.GeckoAppShell;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a small rect. This is scaled to become a scrollbar.
|
* Draws a small rect. This is scaled to become a scrollbar.
|
||||||
|
@ -74,6 +75,7 @@ public class ScrollbarLayer extends TileLayer {
|
||||||
private final Bitmap mBitmap;
|
private final Bitmap mBitmap;
|
||||||
private final Canvas mCanvas;
|
private final Canvas mCanvas;
|
||||||
private float mOpacity;
|
private float mOpacity;
|
||||||
|
private boolean mFinalized = false;
|
||||||
|
|
||||||
private ScrollbarLayer(CairoImage image, boolean vertical, ByteBuffer buffer) {
|
private ScrollbarLayer(CairoImage image, boolean vertical, ByteBuffer buffer) {
|
||||||
super(false, image);
|
super(false, image);
|
||||||
|
@ -84,11 +86,22 @@ public class ScrollbarLayer extends TileLayer {
|
||||||
mCanvas = new Canvas(mBitmap);
|
mCanvas = new Canvas(mBitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
try {
|
||||||
|
if (!mFinalized && mBuffer != null)
|
||||||
|
GeckoAppShell.freeDirectBuffer(mBuffer);
|
||||||
|
mFinalized = true;
|
||||||
|
} finally {
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static ScrollbarLayer create(boolean vertical) {
|
public static ScrollbarLayer create(boolean vertical) {
|
||||||
// just create an empty image for now, it will get drawn
|
// just create an empty image for now, it will get drawn
|
||||||
// on demand anyway
|
// on demand anyway
|
||||||
int imageSize = nextPowerOfTwo(BAR_SIZE);
|
int imageSize = nextPowerOfTwo(BAR_SIZE);
|
||||||
ByteBuffer buffer = ByteBuffer.allocateDirect(imageSize * imageSize * 4);
|
ByteBuffer buffer = GeckoAppShell.allocateDirectBuffer(imageSize * imageSize * 4);
|
||||||
CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize, CairoImage.FORMAT_ARGB32);
|
CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize, CairoImage.FORMAT_ARGB32);
|
||||||
return new ScrollbarLayer(image, vertical, buffer);
|
return new ScrollbarLayer(image, vertical, buffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
|
|
||||||
package org.mozilla.gecko.gfx;
|
package org.mozilla.gecko.gfx;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.GeckoAppShell;
|
||||||
import org.mozilla.gecko.gfx.BufferedCairoImage;
|
import org.mozilla.gecko.gfx.BufferedCairoImage;
|
||||||
import org.mozilla.gecko.gfx.CairoImage;
|
import org.mozilla.gecko.gfx.CairoImage;
|
||||||
import org.mozilla.gecko.gfx.IntSize;
|
import org.mozilla.gecko.gfx.IntSize;
|
||||||
|
@ -56,6 +57,7 @@ import java.nio.IntBuffer;
|
||||||
public class TextLayer extends SingleTileLayer {
|
public class TextLayer extends SingleTileLayer {
|
||||||
private final ByteBuffer mBuffer;
|
private final ByteBuffer mBuffer;
|
||||||
private final IntSize mSize;
|
private final IntSize mSize;
|
||||||
|
private boolean mFinalized = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This awkward pattern is necessary due to Java's restrictions on when one can call superclass
|
* This awkward pattern is necessary due to Java's restrictions on when one can call superclass
|
||||||
|
@ -68,8 +70,18 @@ public class TextLayer extends SingleTileLayer {
|
||||||
renderText(text);
|
renderText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
try {
|
||||||
|
if (!mFinalized && mBuffer != null)
|
||||||
|
GeckoAppShell.freeDirectBuffer(mBuffer);
|
||||||
|
mFinalized = true;
|
||||||
|
} finally {
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static TextLayer create(IntSize size, String text) {
|
public static TextLayer create(IntSize size, String text) {
|
||||||
ByteBuffer buffer = ByteBuffer.allocateDirect(size.width * size.height * 4);
|
ByteBuffer buffer = GeckoAppShell.allocateDirectBuffer(size.width * size.height * 4);
|
||||||
BufferedCairoImage image = new BufferedCairoImage(buffer, size.width, size.height,
|
BufferedCairoImage image = new BufferedCairoImage(buffer, size.width, size.height,
|
||||||
CairoImage.FORMAT_ARGB32);
|
CairoImage.FORMAT_ARGB32);
|
||||||
return new TextLayer(buffer, image, size, text);
|
return new TextLayer(buffer, image, size, text);
|
||||||
|
|
|
@ -168,15 +168,5 @@ public abstract class TileLayer extends Layer {
|
||||||
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, repeatMode);
|
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, repeatMode);
|
||||||
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, repeatMode);
|
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, repeatMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static FloatBuffer createBuffer(float[] values) {
|
|
||||||
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(values.length * 4);
|
|
||||||
byteBuffer.order(ByteOrder.nativeOrder());
|
|
||||||
|
|
||||||
FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();
|
|
||||||
floatBuffer.put(values);
|
|
||||||
floatBuffer.position(0);
|
|
||||||
return floatBuffer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -236,17 +236,17 @@ public class ViewportMetrics {
|
||||||
|
|
||||||
public String toJSON() {
|
public String toJSON() {
|
||||||
try {
|
try {
|
||||||
return new JSONStringer().object()
|
JSONStringer object = new JSONStringer().object();
|
||||||
.key("x").value(mViewportRect.left)
|
object.key("zoom").value(mZoomFactor);
|
||||||
.key("y").value(mViewportRect.top)
|
object.key("offsetY").value(mViewportOffset.y);
|
||||||
.key("width").value(mViewportRect.width())
|
object.key("offsetX").value(mViewportOffset.x);
|
||||||
.key("height").value(mViewportRect.height())
|
object.key("pageHeight").value(mPageSize.height);
|
||||||
.key("pageWidth").value(mPageSize.width)
|
object.key("pageWidth").value(mPageSize.width);
|
||||||
.key("pageHeight").value(mPageSize.height)
|
object.key("height").value(mViewportRect.height());
|
||||||
.key("offsetX").value(mViewportOffset.x)
|
object.key("width").value(mViewportRect.width());
|
||||||
.key("offsetY").value(mViewportOffset.y)
|
object.key("y").value(mViewportRect.top);
|
||||||
.key("zoom").value(mZoomFactor)
|
object.key("x").value(mViewportRect.left);
|
||||||
.endObject().toString();
|
return object.endObject().toString();
|
||||||
} catch (JSONException je) {
|
} catch (JSONException je) {
|
||||||
Log.e(LOGTAG, "Error serializing viewportmetrics", je);
|
Log.e(LOGTAG, "Error serializing viewportmetrics", je);
|
||||||
return "";
|
return "";
|
||||||
|
|
|
@ -62,6 +62,10 @@
|
||||||
<!ENTITY pref_clear_history_confirm "Browsing history will be deleted">
|
<!ENTITY pref_clear_history_confirm "Browsing history will be deleted">
|
||||||
<!ENTITY pref_clear_private_data "Clear private data">
|
<!ENTITY pref_clear_private_data "Clear private data">
|
||||||
<!ENTITY pref_clear_private_data_confirm "Browsing settings, including passwords and cookies, will be deleted">
|
<!ENTITY pref_clear_private_data_confirm "Browsing settings, including passwords and cookies, will be deleted">
|
||||||
|
<!ENTITY pref_enable_flash "Enable Flash">
|
||||||
|
<!ENTITY pref_enable_flash_yes "Yes">
|
||||||
|
<!ENTITY pref_enable_flash_tap_to_play "Tap To Play">
|
||||||
|
<!ENTITY pref_enable_flash_no "No">
|
||||||
|
|
||||||
<!ENTITY quit "Quit">
|
<!ENTITY quit "Quit">
|
||||||
|
|
||||||
|
|
После (image error) Размер: 69 KiB |
После (image error) Размер: 1.4 KiB |
Двоичные данные
mobile/android/base/resources/drawable-hdpi-v8/abouthome_separator.9.png
Normal file
После (image error) Размер: 93 B |
Двоичные данные
mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_placeholder.png
Normal file
После (image error) Размер: 28 KiB |
Двоичные данные
mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_shadow.9.png
Normal file
После (image error) Размер: 800 B |
После (image error) Размер: 36 KiB |
После (image error) Размер: 984 B |
Двоичные данные
mobile/android/base/resources/drawable-mdpi-v8/abouthome_separator.9.png
Normal file
После (image error) Размер: 93 B |
Двоичные данные
mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_placeholder.png
Normal file
После (image error) Размер: 14 KiB |
Двоичные данные
mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_shadow.9.png
Normal file
После (image error) Размер: 729 B |
После (image error) Размер: 105 KiB |
После (image error) Размер: 1.8 KiB |
Двоичные данные
mobile/android/base/resources/drawable-xhdpi-v11/abouthome_separator.9.png
Normal file
После (image error) Размер: 100 B |
Двоичные данные
mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_placeholder.png
Normal file
После (image error) Размер: 41 KiB |
Двоичные данные
mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png
Normal file
После (image error) Размер: 1.0 KiB |
После (image error) Размер: 11 KiB |
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:src="@drawable/abouthome_bg"
|
||||||
|
android:tileMode="repeat"/>
|
После (image error) Размер: 11 KiB |
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:src="@drawable/abouthome_topsites_bg"
|
||||||
|
android:tileMode="repeat"/>
|
|
@ -1,10 +0,0 @@
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<solid android:color="#FFFFFFFF" />
|
|
||||||
<stroke android:width="2dip" android:color="#ffD0D0D0"/>
|
|
||||||
<padding
|
|
||||||
android:left="7dp"
|
|
||||||
android:top="7dp"
|
|
||||||
android:right="7dp"
|
|
||||||
android:bottom="7dp" />
|
|
||||||
<corners android:radius="4dp" />
|
|
||||||
</shape>
|
|
|
@ -1,7 +0,0 @@
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<solid android:color="#FFD0D0D0" />
|
|
||||||
<stroke android:width="2dip" android:color="#FFD0D0D0"/>
|
|
||||||
<padding android:left="7dp" android:top="7dp"
|
|
||||||
android:right="7dp" android:bottom="7dp" />
|
|
||||||
<corners android:radius="4dp" />
|
|
||||||
</shape>
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<TextView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="10dip"
|
|
||||||
android:textColor="#FF000000"
|
|
||||||
android:background="@drawable/rounded_grey_border" />
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="47dip"
|
||||||
|
android:paddingLeft="12dip"
|
||||||
|
android:gravity="left|center_vertical"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textColor="#222222"/>
|
|
@ -1,36 +1,112 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<ImageView
|
|
||||||
android:layout_width="fill_parent"
|
<RelativeLayout android:layout_width="fill_parent"
|
||||||
android:layout_height="100dip"
|
android:layout_height="fill_parent"
|
||||||
android:layout_gravity="center_horizontal"
|
android:background="@drawable/abouthome_bg_repeat">
|
||||||
android:src="@drawable/icon" />
|
|
||||||
<TextView
|
<RelativeLayout android:id="@+id/top_sites"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dip"
|
android:layout_alignParentTop="true"
|
||||||
android:textColor="#FF202020"
|
android:layout_alignParentLeft="true"
|
||||||
android:textStyle="bold"
|
android:isScrollContainer="false"
|
||||||
android:text="Recommended Addons"
|
android:background="@drawable/abouthome_topsites_bg_repeat">
|
||||||
android:isScrollContainer="false" />
|
|
||||||
<ListView
|
<ImageView android:layout_width="fill_parent"
|
||||||
android:id="@+id/recommended_addon_list"
|
android:layout_height="50dip"
|
||||||
android:layout_width="fill_parent"
|
android:gravity="fill"
|
||||||
android:layout_height="wrap_content"
|
android:background="@drawable/abouthome_bg_repeat"/>
|
||||||
android:divider="@android:color/transparent"
|
|
||||||
android:isScrollContainer="false"
|
<ImageView android:layout_width="wrap_content"
|
||||||
android:dividerHeight="8dip" />
|
android:layout_height="wrap_content"
|
||||||
<TextView
|
android:layout_alignParentTop="true"
|
||||||
android:layout_width="fill_parent"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_height="wrap_content"
|
android:src="@drawable/abouthome_icon"/>
|
||||||
android:layout_marginTop="10dip"
|
|
||||||
android:textColor="#FF202020"
|
<TextView android:id="@+id/top_sites_title"
|
||||||
android:textStyle="bold"
|
android:layout_width="fill_parent"
|
||||||
android:isScrollContainer="false"
|
android:layout_height="wrap_content"
|
||||||
android:text="Favorite Sites" />
|
android:layout_marginLeft="12dip"
|
||||||
<GridView
|
android:layout_marginTop="55dip"
|
||||||
android:id="@+id/grid"
|
android:textSize="12sp"
|
||||||
android:layout_width="fill_parent"
|
android:textColor="#000000"
|
||||||
android:isScrollContainer="false"
|
android:textStyle="bold"
|
||||||
android:layout_height="wrap_content"/>
|
android:text="Top Sites"/>
|
||||||
|
|
||||||
|
<view class="org.mozilla.gecko.AboutHomeContent$TopSitesGridView"
|
||||||
|
android:id="@+id/top_sites_grid"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/top_sites_title"
|
||||||
|
android:verticalSpacing="8dip"
|
||||||
|
android:horizontalSpacing="0dip"
|
||||||
|
android:isScrollContainer="false"
|
||||||
|
android:gravity="center"/>
|
||||||
|
|
||||||
|
<view class="org.mozilla.gecko.AboutHomeContent$LinkTextView"
|
||||||
|
android:id="@+id/all_top_sites_text"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="30dip"
|
||||||
|
android:layout_below="@id/top_sites_grid"
|
||||||
|
android:layout_marginTop="7dip"
|
||||||
|
android:textColor="#22629e"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:gravity="top|center_horizontal"
|
||||||
|
android:text="Browse all your top sites"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/logo"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dip"
|
||||||
|
android:layout_marginBottom="10dip"
|
||||||
|
android:layout_marginLeft="12dip"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:src="@drawable/abouthome_logo"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/recommended_addons_title"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="26dip"
|
||||||
|
android:paddingLeft="12dip"
|
||||||
|
android:layout_below="@id/top_sites"
|
||||||
|
android:background="@drawable/abouthome_separator"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="#000000"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:gravity="left|center_vertical"
|
||||||
|
android:text="Add-ons for your Firefox"/>
|
||||||
|
|
||||||
|
<LinearLayout android:id="@+id/recommended_addons"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/recommended_addons_title"
|
||||||
|
android:background="@drawable/abouthome_separator"
|
||||||
|
android:isScrollContainer="false">
|
||||||
|
|
||||||
|
<view class="org.mozilla.gecko.AboutHomeContent$AddonsListView"
|
||||||
|
android:id="@+id/recommended_addons_list"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:divider="@drawable/abouthome_separator"
|
||||||
|
android:isScrollContainer="false"
|
||||||
|
android:dividerHeight="2dip"/>
|
||||||
|
|
||||||
|
<view class="org.mozilla.gecko.AboutHomeContent$LinkTextView"
|
||||||
|
android:id="@+id/all_addons_text"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="47dip"
|
||||||
|
android:background="@drawable/abouthome_separator"
|
||||||
|
android:textColor="#22629e"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="Browse all Firefox Add-ons"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
</merge>
|
</merge>
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="100dip"
|
|
||||||
android:layout_height="150dip"
|
|
||||||
android:padding="6dip"
|
|
||||||
android:gravity="center_horizontal">
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
android:background="@drawable/rounded_grey_box" >
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/screenshot"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="80dip" />
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="fill_parent" >
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/bookmark_icon"
|
|
||||||
android:layout_width="16dip"
|
|
||||||
android:layout_height="16dip"
|
|
||||||
android:layout_marginRight="6dip" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/bookmark_title"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textColor="#FF202020"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
</LinearLayout>
|
|
||||||
<TextView
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textColor="#FF202020"
|
|
||||||
android:id="@+id/bookmark_url"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:ellipsize="marquee" />
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="140dip"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent">
|
||||||
|
|
||||||
|
<LinearLayout android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/abouthome_topsite_shadow"
|
||||||
|
android:padding="1dip"
|
||||||
|
android:paddingTop="2dip">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/thumbnail"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="80dip"
|
||||||
|
android:scaleType="centerCrop"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/title"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="3dip"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="#000000"
|
||||||
|
android:textSize="9dip"
|
||||||
|
android:gravity="center_horizontal"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<string-array name="pref_enable_flash_entries">
|
||||||
|
<item>@string/pref_enable_flash_yes</item>
|
||||||
|
<item>@string/pref_enable_flash_tap_to_play</item>
|
||||||
|
<item>@string/pref_enable_flash_no</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="pref_enable_flash_values">
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
<item>0</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
|
@ -19,6 +19,12 @@
|
||||||
android:title="@string/pref_char_encoding"
|
android:title="@string/pref_char_encoding"
|
||||||
android:persistent="false" />
|
android:persistent="false" />
|
||||||
|
|
||||||
|
<ListPreference android:key="plugin.enable"
|
||||||
|
android:title="@string/pref_enable_flash"
|
||||||
|
android:entries="@array/pref_enable_flash_entries"
|
||||||
|
android:entryValues="@array/pref_enable_flash_values"
|
||||||
|
android:persistent="false" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/pref_category_privacy">
|
<PreferenceCategory android:title="@string/pref_category_privacy">
|
||||||
|
|
|
@ -68,6 +68,10 @@
|
||||||
<string name="pref_clear_history_confirm">&pref_clear_history_confirm;</string>
|
<string name="pref_clear_history_confirm">&pref_clear_history_confirm;</string>
|
||||||
<string name="pref_clear_private_data">&pref_clear_private_data;</string>
|
<string name="pref_clear_private_data">&pref_clear_private_data;</string>
|
||||||
<string name="pref_clear_private_data_confirm">&pref_clear_private_data_confirm;</string>
|
<string name="pref_clear_private_data_confirm">&pref_clear_private_data_confirm;</string>
|
||||||
|
<string name="pref_enable_flash">&pref_enable_flash;</string>
|
||||||
|
<string name="pref_enable_flash_yes">&pref_enable_flash_yes;</string>
|
||||||
|
<string name="pref_enable_flash_tap_to_play">&pref_enable_flash_tap_to_play;</string>
|
||||||
|
<string name="pref_enable_flash_no">&pref_enable_flash_no;</string>
|
||||||
|
|
||||||
<string name="reload">&reload;</string>
|
<string name="reload">&reload;</string>
|
||||||
<string name="forward">&forward;</string>
|
<string name="forward">&forward;</string>
|
||||||
|
|
|
@ -560,7 +560,9 @@ public class PanZoomController
|
||||||
GeckoAppShell.sendEventToGecko(e);
|
GeckoAppShell.sendEventToGecko(e);
|
||||||
mOverrideScrollAck = false;
|
mOverrideScrollAck = false;
|
||||||
} else {
|
} else {
|
||||||
mController.scrollBy(new PointF(mX.displacement, mY.displacement));
|
synchronized (mController) {
|
||||||
|
mController.scrollBy(new PointF(mX.displacement, mY.displacement));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mX.displacement = mY.displacement = 0;
|
mX.displacement = mY.displacement = 0;
|
||||||
|
@ -592,18 +594,22 @@ public class PanZoomController
|
||||||
|
|
||||||
/* Performs one frame of a bounce animation. */
|
/* Performs one frame of a bounce animation. */
|
||||||
private void advanceBounce() {
|
private void advanceBounce() {
|
||||||
float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
|
synchronized (mController) {
|
||||||
ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
|
float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
|
||||||
mController.setViewportMetrics(newMetrics);
|
ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
|
||||||
mController.notifyLayerClientOfGeometryChange();
|
mController.setViewportMetrics(newMetrics);
|
||||||
mBounceFrame++;
|
mController.notifyLayerClientOfGeometryChange();
|
||||||
|
mBounceFrame++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Concludes a bounce animation and snaps the viewport into place. */
|
/* Concludes a bounce animation and snaps the viewport into place. */
|
||||||
private void finishBounce() {
|
private void finishBounce() {
|
||||||
mController.setViewportMetrics(mBounceEndMetrics);
|
synchronized (mController) {
|
||||||
mController.notifyLayerClientOfGeometryChange();
|
mController.setViewportMetrics(mBounceEndMetrics);
|
||||||
mBounceFrame = -1;
|
mController.notifyLayerClientOfGeometryChange();
|
||||||
|
mBounceFrame = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -881,11 +887,14 @@ public class PanZoomController
|
||||||
else
|
else
|
||||||
spanRatio = 1.0f - (1.0f - spanRatio) * resistance;
|
spanRatio = 1.0f - (1.0f - spanRatio) * resistance;
|
||||||
|
|
||||||
float newZoomFactor = mController.getZoomFactor() * spanRatio;
|
synchronized (mController) {
|
||||||
|
float newZoomFactor = mController.getZoomFactor() * spanRatio;
|
||||||
|
|
||||||
mController.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(),
|
mController.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(),
|
||||||
mLastZoomFocus.y - detector.getFocusY()));
|
mLastZoomFocus.y - detector.getFocusY()));
|
||||||
mController.scaleWithFocus(newZoomFactor, new PointF(detector.getFocusX(), detector.getFocusY()));
|
PointF focus = new PointF(detector.getFocusX(), detector.getFocusY());
|
||||||
|
mController.scaleWithFocus(newZoomFactor, focus);
|
||||||
|
}
|
||||||
|
|
||||||
mLastZoomFocus.set(detector.getFocusX(), detector.getFocusY());
|
mLastZoomFocus.set(detector.getFocusX(), detector.getFocusY());
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,26 @@
|
||||||
<div id="addons-list" style="display: none;">
|
<div id="addons-list" style="display: none;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="addons-details" style="display: none">
|
||||||
|
<div class="addon-item">
|
||||||
|
<img class="favicon"/>
|
||||||
|
<div class="inner">
|
||||||
|
<div class="details">
|
||||||
|
<div class="title"></div><div class="version"></div><div class="tag"></div>
|
||||||
|
</div>
|
||||||
|
<div class="description-full"></div>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button id="enable-btn" class="show-on-disable hide-on-enable hide-on-uninstall" onclick="Addons.enable();">&addonAction.enable;</button>
|
||||||
|
<button id="disable-btn" class="show-on-enable hide-on-disable hide-on-uninstall" onclick="Addons.disable();">&addonAction.disable;</button>
|
||||||
|
<button id="uninstall-btn" class="hide-on-uninstall" onclick="Addons.uninstall();">&addonAction.uninstall;</button>
|
||||||
|
<button id="cancel-btn" class="show-on-uninstall" onclick="Addons.cancelUninstall();">&addonAction.cancel;</button>
|
||||||
|
</div>
|
||||||
|
<div class="options-header">&aboutAddons.options;</div>
|
||||||
|
<div class="options-box"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script type="application/javascript;version=1.8"><![CDATA[
|
<script type="application/javascript;version=1.8"><![CDATA[
|
||||||
let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
|
let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
|
||||||
|
|
||||||
|
@ -116,6 +136,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
|
window.addEventListener("popstate", onPopState, false);
|
||||||
|
|
||||||
AddonManager.addInstallListener(Addons);
|
AddonManager.addInstallListener(Addons);
|
||||||
Addons.getAddons();
|
Addons.getAddons();
|
||||||
}
|
}
|
||||||
|
@ -124,11 +146,34 @@
|
||||||
AddonManager.removeInstallListener(Addons);
|
AddonManager.removeInstallListener(Addons);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onPopState(aEvent) {
|
||||||
|
// Called when back/forward is used to change the state of the page
|
||||||
|
if (aEvent.state) {
|
||||||
|
// Show the detail page for an addon
|
||||||
|
Addons.showDetails(Addons._getElementForAddon(aEvent.state.id));
|
||||||
|
} else {
|
||||||
|
// Clear any previous detail addon
|
||||||
|
let detailItem = document.querySelector("#addons-details > .addon-item");
|
||||||
|
detailItem.addon = null;
|
||||||
|
|
||||||
|
// Hide the detail page and show the list
|
||||||
|
let details = document.querySelector("#addons-details");
|
||||||
|
details.style.display = "none";
|
||||||
|
let list = document.querySelector("#addons-list");
|
||||||
|
list.style.display = "block";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var Addons = {
|
var Addons = {
|
||||||
_createItem: function _createItem(aAddon) {
|
_createItem: function _createItem(aAddon) {
|
||||||
let outer = document.createElement("div");
|
let outer = document.createElement("div");
|
||||||
outer.setAttribute("addonID", aAddon.id);
|
outer.setAttribute("addonID", aAddon.id);
|
||||||
outer.className = "addon-item";
|
outer.className = "addon-item";
|
||||||
|
outer.setAttribute("role", "button");
|
||||||
|
outer.addEventListener("click", function() {
|
||||||
|
this.showDetails(outer);
|
||||||
|
history.pushState({ id: aAddon.id }, document.title);
|
||||||
|
}.bind(this), true);
|
||||||
|
|
||||||
let img = document.createElement("img");
|
let img = document.createElement("img");
|
||||||
img.className = "favicon";
|
img.className = "favicon";
|
||||||
|
@ -138,15 +183,24 @@
|
||||||
let inner = document.createElement("div");
|
let inner = document.createElement("div");
|
||||||
inner.className = "inner";
|
inner.className = "inner";
|
||||||
|
|
||||||
let titlePart = document.createElement("span");
|
let details = document.createElement("div");
|
||||||
|
details.className = "details";
|
||||||
|
inner.appendChild(details);
|
||||||
|
|
||||||
|
let titlePart = document.createElement("div");
|
||||||
titlePart.textContent = aAddon.name;
|
titlePart.textContent = aAddon.name;
|
||||||
titlePart.className = "title";
|
titlePart.className = "title";
|
||||||
inner.appendChild(titlePart);
|
details.appendChild(titlePart);
|
||||||
|
|
||||||
let versionPart = document.createElement("span");
|
let versionPart = document.createElement("div");
|
||||||
versionPart.textContent = aAddon.version;
|
versionPart.textContent = aAddon.version;
|
||||||
versionPart.className = "version";
|
versionPart.className = "version";
|
||||||
inner.appendChild(versionPart);
|
details.appendChild(versionPart);
|
||||||
|
|
||||||
|
let tagPart = document.createElement("div");
|
||||||
|
tagPart.textContent = gStringBundle.GetStringFromName("addonType." + aAddon.type);
|
||||||
|
tagPart.className = "tag";
|
||||||
|
details.appendChild(tagPart);
|
||||||
|
|
||||||
if ("description" in aAddon) {
|
if ("description" in aAddon) {
|
||||||
let descPart = document.createElement("div");
|
let descPart = document.createElement("div");
|
||||||
|
@ -156,56 +210,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
outer.appendChild(inner);
|
outer.appendChild(inner);
|
||||||
|
|
||||||
let buttons = document.createElement("div");
|
|
||||||
buttons.className = "buttons";
|
|
||||||
|
|
||||||
let optionsBtn = document.createElement("button");
|
|
||||||
optionsBtn.className = "options-btn";
|
|
||||||
optionsBtn.textContent = gStringBundle.GetStringFromName("addonAction.options");
|
|
||||||
optionsBtn.setAttribute("disabled", "true"); // TODO (bug 696533)
|
|
||||||
optionsBtn.addEventListener("click", function() {
|
|
||||||
this.toggleOptions(outer);
|
|
||||||
}.bind(this), false)
|
|
||||||
buttons.appendChild(optionsBtn);
|
|
||||||
|
|
||||||
let enableBtn = document.createElement("button");
|
|
||||||
enableBtn.className = "show-on-disable hide-on-enable hide-on-uninstall";
|
|
||||||
enableBtn.textContent = gStringBundle.GetStringFromName("addonAction.enable");
|
|
||||||
if (aAddon.appDisabled)
|
|
||||||
enableBtn.setAttribute("disabled", "true");
|
|
||||||
enableBtn.addEventListener("click", function() {
|
|
||||||
this.enable(outer);
|
|
||||||
}.bind(this), false)
|
|
||||||
buttons.appendChild(enableBtn);
|
|
||||||
|
|
||||||
let disableBtn = document.createElement("button");
|
|
||||||
disableBtn.className = "show-on-enable hide-on-disable hide-on-uninstall";
|
|
||||||
disableBtn.textContent = gStringBundle.GetStringFromName("addonAction.disable");
|
|
||||||
disableBtn.addEventListener("click", function() {
|
|
||||||
this.disable(outer);
|
|
||||||
}.bind(this), false)
|
|
||||||
buttons.appendChild(disableBtn);
|
|
||||||
|
|
||||||
let uninstallBtn = document.createElement("button");
|
|
||||||
uninstallBtn.className = "hide-on-uninstall";
|
|
||||||
uninstallBtn.textContent = gStringBundle.GetStringFromName("addonAction.uninstall");
|
|
||||||
if (aAddon.scope == AddonManager.SCOPE_APPLICATION)
|
|
||||||
uninstallBtn.setAttribute("disabled", "true");
|
|
||||||
uninstallBtn.addEventListener("click", function() {
|
|
||||||
this.uninstall(outer);
|
|
||||||
}.bind(this), false)
|
|
||||||
buttons.appendChild(uninstallBtn);
|
|
||||||
|
|
||||||
let cancelUninstallBtn = document.createElement("button");
|
|
||||||
cancelUninstallBtn.className = "show-on-uninstall";
|
|
||||||
cancelUninstallBtn.textContent = gStringBundle.GetStringFromName("addonAction.cancel");
|
|
||||||
cancelUninstallBtn.addEventListener("click", function() {
|
|
||||||
this.cancelUninstall(outer);
|
|
||||||
}.bind(this), false)
|
|
||||||
buttons.appendChild(cancelUninstallBtn);
|
|
||||||
|
|
||||||
outer.appendChild(buttons);
|
|
||||||
return outer;
|
return outer;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -302,16 +306,92 @@
|
||||||
return "";
|
return "";
|
||||||
},
|
},
|
||||||
|
|
||||||
enable: function enable(aItem) {
|
showDetails: function showDetails(aListItem) {
|
||||||
if (!aItem.addon)
|
Services.console.logStringMessage("---- showing details")
|
||||||
|
let detailItem = document.querySelector("#addons-details > .addon-item");
|
||||||
|
detailItem.setAttribute("isDisabled", aListItem.getAttribute("isDisabled"));
|
||||||
|
detailItem.setAttribute("opType", aListItem.getAttribute("opType"));
|
||||||
|
detailItem.setAttribute("optionsURL", aListItem.getAttribute("optionsURL"));
|
||||||
|
detailItem.addon = aListItem.addon;
|
||||||
|
Services.console.logStringMessage("---- did step 1")
|
||||||
|
|
||||||
|
let addon = detailItem.addon;
|
||||||
|
document.querySelector("#addons-details > .addon-item .favicon").setAttribute("src", addon.iconURL);
|
||||||
|
document.querySelector("#addons-details > .addon-item .title").textContent = addon.name;
|
||||||
|
document.querySelector("#addons-details > .addon-item .version").textContent = addon.version;
|
||||||
|
document.querySelector("#addons-details > .addon-item .tag").textContent = gStringBundle.GetStringFromName("addonType." + addon.type);
|
||||||
|
document.querySelector("#addons-details > .addon-item .description-full").textContent = addon.description;
|
||||||
|
Services.console.logStringMessage("---- did step 2")
|
||||||
|
|
||||||
|
let enableBtn = document.getElementById("uninstall-btn");
|
||||||
|
if (addon.appDisabled)
|
||||||
|
enableBtn.setAttribute("disabled", "true");
|
||||||
|
else
|
||||||
|
enableBtn.removeAttribute("disabled");
|
||||||
|
|
||||||
|
let uninstallBtn = document.getElementById("uninstall-btn");
|
||||||
|
if (addon.scope == AddonManager.SCOPE_APPLICATION)
|
||||||
|
uninstallBtn.setAttribute("disabled", "true");
|
||||||
|
else
|
||||||
|
uninstallBtn.removeAttribute("disabled");
|
||||||
|
|
||||||
|
let box = document.querySelector("#addons-details > .addon-item .options-box");
|
||||||
|
box.innerHTML = "";
|
||||||
|
|
||||||
|
// Retrieve the extensions preferences
|
||||||
|
try {
|
||||||
|
let optionsURL = aListItem.getAttribute("optionsURL");
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", optionsURL, false);
|
||||||
|
xhr.send();
|
||||||
|
if (xhr.responseXML) {
|
||||||
|
let currentNode;
|
||||||
|
let nodeIterator = xhr.responseXML.createNodeIterator(xhr.responseXML, NodeFilter.SHOW_TEXT, null, false);
|
||||||
|
while (currentNode = nodeIterator.nextNode()) {
|
||||||
|
let trimmed = currentNode.nodeValue.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
|
||||||
|
if (!trimmed.length)
|
||||||
|
currentNode.parentNode.removeChild(currentNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow <setting> for now
|
||||||
|
let prefs = xhr.responseXML.querySelectorAll(":root > setting");
|
||||||
|
for (let i = 0; i < prefs.length; i++)
|
||||||
|
box.appendChild(prefs.item(i));
|
||||||
|
/*
|
||||||
|
// Send an event so add-ons can prepopulate any non-preference based
|
||||||
|
// settings
|
||||||
|
let event = document.createEvent("Events");
|
||||||
|
event.initEvent("AddonOptionsLoad", true, false);
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
|
||||||
|
// Also send a notification to match the behavior of desktop Firefox
|
||||||
|
let id = this.id.substring(17); // length of |urn:mozilla:item:|
|
||||||
|
Services.obs.notifyObservers(document, "addon-options-displayed", id);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Cu.reportError(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
let list = document.querySelector("#addons-list");
|
||||||
|
list.style.display = "none";
|
||||||
|
let details = document.querySelector("#addons-details");
|
||||||
|
details.style.display = "block";
|
||||||
|
Services.console.logStringMessage("---- did step 3")
|
||||||
|
},
|
||||||
|
|
||||||
|
enable: function enable() {
|
||||||
|
let detailItem = document.querySelector("#addons-details > .addon-item");
|
||||||
|
if (!detailItem.addon)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let opType;
|
let opType;
|
||||||
if (aItem.addon.type == "search") {
|
let isDisabled;
|
||||||
aItem.setAttribute("isDisabled", false);
|
if (detailItem.addon.type == "search") {
|
||||||
aItem.addon.engine.hidden = false;
|
isDisabled = false;
|
||||||
|
detailItem.addon.engine.hidden = false;
|
||||||
opType = "needs-enable";
|
opType = "needs-enable";
|
||||||
} else if (aItem.addon.type == "theme") {
|
} else if (detailItem.addon.type == "theme") {
|
||||||
// We can have only one theme enabled, so disable the current one if any
|
// We can have only one theme enabled, so disable the current one if any
|
||||||
let theme = null;
|
let theme = null;
|
||||||
let list = document.getElementById("addons-list");
|
let list = document.getElementById("addons-list");
|
||||||
|
@ -326,98 +406,118 @@
|
||||||
if (theme)
|
if (theme)
|
||||||
this.disable(theme);
|
this.disable(theme);
|
||||||
|
|
||||||
aItem.addon.userDisabled = false;
|
detailItem.addon.userDisabled = false;
|
||||||
aItem.setAttribute("isDisabled", false);
|
isDisabled = false;
|
||||||
} else {
|
} else {
|
||||||
aItem.addon.userDisabled = false;
|
detailItem.addon.userDisabled = false;
|
||||||
opType = this._getOpTypeForOperations(aItem.addon.pendingOperations);
|
isDisabled = false;
|
||||||
|
opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
|
||||||
|
|
||||||
if (aItem.addon.pendingOperations & AddonManager.PENDING_ENABLE) {
|
if (detailItem.addon.pendingOperations & AddonManager.PENDING_ENABLE) {
|
||||||
this.showRestart();
|
this.showRestart();
|
||||||
} else {
|
} else {
|
||||||
aItem.setAttribute("isDisabled", false);
|
if (detailItem.getAttribute("opType") == "needs-disable")
|
||||||
if (aItem.getAttribute("opType") == "needs-disable")
|
|
||||||
this.hideRestart();
|
this.hideRestart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aItem.setAttribute("opType", opType);
|
detailItem.setAttribute("opType", opType);
|
||||||
|
detailItem.setAttribute("isDisabled", isDisabled);
|
||||||
|
|
||||||
|
// Sync to the list item
|
||||||
|
let listItem = this._getElementForAddon(detailItemaddon.id);
|
||||||
|
listItem.setAttribute("isDisabled", detailItem.getAttribute("isDisabled"));
|
||||||
|
listItem.setAttribute("opType", detailItem.getAttribute("opType"));
|
||||||
},
|
},
|
||||||
|
|
||||||
disable: function disable(aItem) {
|
disable: function disable() {
|
||||||
if (!aItem.addon)
|
let detailItem = document.querySelector("#addons-details > .addon-item");
|
||||||
|
if (!detailItem.addon)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let opType;
|
let opType;
|
||||||
if (aItem.addon.type == "search") {
|
let isDisabled;
|
||||||
aItem.setAttribute("isDisabled", true);
|
if (detailItem.addon.type == "search") {
|
||||||
aItem.addon.engine.hidden = true;
|
isDisabled = true;
|
||||||
|
detailItem.addon.engine.hidden = true;
|
||||||
opType = "needs-disable";
|
opType = "needs-disable";
|
||||||
} else if (aItem.addon.type == "theme") {
|
} else if (detailItem.addon.type == "theme") {
|
||||||
aItem.addon.userDisabled = true;
|
detailItem.addon.userDisabled = true;
|
||||||
aItem.setAttribute("isDisabled", true);
|
isDisabled = true;
|
||||||
} else if (aItem.addon.type == "locale") {
|
} else if (detailItem.addon.type == "locale") {
|
||||||
aItem.addon.userDisabled = true;
|
detailItem.addon.userDisabled = true;
|
||||||
aItem.setAttribute("isDisabled", true);
|
isDisabled = true;
|
||||||
} else {
|
} else {
|
||||||
aItem.addon.userDisabled = true;
|
detailItem.addon.userDisabled = true;
|
||||||
opType = this._getOpTypeForOperations(aItem.addon.pendingOperations);
|
opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
|
||||||
|
isDisabled = !detailItem.addon.isActive;
|
||||||
|
|
||||||
if (aItem.addon.pendingOperations & AddonManager.PENDING_DISABLE) {
|
if (detailItem.addon.pendingOperations & AddonManager.PENDING_DISABLE) {
|
||||||
this.showRestart();
|
this.showRestart();
|
||||||
} else {
|
} else {
|
||||||
aItem.setAttribute("isDisabled", !aItem.addon.isActive);
|
if (detailItem.getAttribute("opType") == "needs-enable")
|
||||||
if (aItem.getAttribute("opType") == "needs-enable")
|
|
||||||
this.hideRestart();
|
this.hideRestart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aItem.setAttribute("opType", opType);
|
detailItem.setAttribute("opType", opType);
|
||||||
|
detailItem.setAttribute("isDisabled", isDisabled);
|
||||||
|
|
||||||
|
// Sync to the list item
|
||||||
|
let listItem = this._getElementForAddon(detailItem.addon.id);
|
||||||
|
listItem.setAttribute("isDisabled", detailItem.getAttribute("isDisabled"));
|
||||||
|
listItem.setAttribute("opType", detailItem.getAttribute("opType"));
|
||||||
},
|
},
|
||||||
|
|
||||||
uninstall: function uninstall(aItem) {
|
uninstall: function uninstall() {
|
||||||
let list = document.getElementById("addons-list");
|
let list = document.getElementById("addons-list");
|
||||||
if (!aItem.addon) {
|
let detailItem = document.querySelector("#addons-details > .addon-item");
|
||||||
list.removeChild(aItem);
|
if (!detailItem.addon)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
let opType;
|
let listItem = this._getElementForAddon(detailItem.addon.id);
|
||||||
if (aItem.addon.type == "search") {
|
|
||||||
|
if (detailItem.addon.type == "search") {
|
||||||
// Make sure the engine isn't hidden before removing it, to make sure it's
|
// Make sure the engine isn't hidden before removing it, to make sure it's
|
||||||
// visible if the user later re-adds it (works around bug 341833)
|
// visible if the user later re-adds it (works around bug 341833)
|
||||||
aItem.addon.engine.hidden = false;
|
detailItem.addon.engine.hidden = false;
|
||||||
Services.search.removeEngine(aItem.addon.engine);
|
Services.search.removeEngine(detailItem.addon.engine);
|
||||||
// the search-engine-modified observer in browser.js will take care of
|
// the search-engine-modified observer in browser.js will take care of
|
||||||
// updating the list
|
// updating the list
|
||||||
} else {
|
} else {
|
||||||
aItem.addon.uninstall();
|
detailItem.addon.uninstall();
|
||||||
opType = this._getOpTypeForOperations(aItem.addon.pendingOperations);
|
let opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
|
||||||
|
|
||||||
if (aItem.addon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
|
if (detailItem.addon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
|
||||||
this.showRestart();
|
this.showRestart();
|
||||||
|
|
||||||
// A disabled addon doesn't need a restart so it has no pending ops and
|
// A disabled addon doesn't need a restart so it has no pending ops and
|
||||||
// can't be cancelled
|
// can't be cancelled
|
||||||
if (!aItem.addon.isActive && opType == "")
|
if (!detailItem.addon.isActive && opType == "")
|
||||||
opType = "needs-uninstall";
|
opType = "needs-uninstall";
|
||||||
|
|
||||||
aItem.setAttribute("opType", opType);
|
detailItem.setAttribute("opType", opType);
|
||||||
|
listItem.setAttribute("opType", opType);
|
||||||
} else {
|
} else {
|
||||||
list.removeChild(aItem);
|
list.removeChild(listItem);
|
||||||
|
history.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelUninstall: function ev_cancelUninstall(aItem) {
|
cancelUninstall: function ev_cancelUninstall() {
|
||||||
if (!aItem.addon)
|
let detailItem = document.querySelector("#addons-details > .addon-item");
|
||||||
|
if (!detailItem.addon)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
aItem.addon.cancelUninstall();
|
detailItem.addon.cancelUninstall();
|
||||||
this.hideRestart();
|
this.hideRestart();
|
||||||
|
|
||||||
let opType = this._getOpTypeForOperations(aItem.addon.pendingOperations);
|
let opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
|
||||||
aItem.setAttribute("opType", opType);
|
detailItem.setAttribute("opType", opType);
|
||||||
|
|
||||||
|
let listItem = this._getElementForAddon(detailItem.addon.id);
|
||||||
|
listItem.setAttribute("opType", opType);
|
||||||
},
|
},
|
||||||
|
|
||||||
showRestart: function showRestart(aMode) {
|
showRestart: function showRestart(aMode) {
|
||||||
|
|
|
@ -205,6 +205,7 @@ var BrowserApp = {
|
||||||
IndexedDB.init();
|
IndexedDB.init();
|
||||||
XPInstallObserver.init();
|
XPInstallObserver.init();
|
||||||
ConsoleAPI.init();
|
ConsoleAPI.init();
|
||||||
|
ClipboardHelper.init();
|
||||||
|
|
||||||
// Init LoginManager
|
// Init LoginManager
|
||||||
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
|
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
|
||||||
|
@ -435,6 +436,16 @@ var BrowserApp = {
|
||||||
name: prefName
|
name: prefName
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The plugin pref is actually two separate prefs, so
|
||||||
|
// we need to handle it differently
|
||||||
|
if (prefName == "plugin.enable") {
|
||||||
|
// Use a string type for java's ListPreference
|
||||||
|
pref.type = "string";
|
||||||
|
pref.value = PluginHelper.getPluginPreference();
|
||||||
|
prefs.push(pref);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (Services.prefs.getPrefType(prefName)) {
|
switch (Services.prefs.getPrefType(prefName)) {
|
||||||
case Ci.nsIPrefBranch.PREF_BOOL:
|
case Ci.nsIPrefBranch.PREF_BOOL:
|
||||||
|
@ -490,6 +501,13 @@ var BrowserApp = {
|
||||||
setPreferences: function setPreferences(aPref) {
|
setPreferences: function setPreferences(aPref) {
|
||||||
let json = JSON.parse(aPref);
|
let json = JSON.parse(aPref);
|
||||||
|
|
||||||
|
// The plugin pref is actually two separate prefs, so
|
||||||
|
// we need to handle it differently
|
||||||
|
if (json.name == "plugin.enable") {
|
||||||
|
PluginHelper.setPluginPreference(json.value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// when sending to java, we normalized special preferences that use
|
// when sending to java, we normalized special preferences that use
|
||||||
// integers and strings to represent booleans. here, we convert them back
|
// integers and strings to represent booleans. here, we convert them back
|
||||||
// to their actual types so we can store them.
|
// to their actual types so we can store them.
|
||||||
|
@ -540,10 +558,17 @@ var BrowserApp = {
|
||||||
let args = JSON.parse(aData);
|
let args = JSON.parse(aData);
|
||||||
let uri;
|
let uri;
|
||||||
if (args.engine) {
|
if (args.engine) {
|
||||||
let engine = Services.search.getEngineByName(args.engine);
|
let engine;
|
||||||
uri = engine.getSubmission(args.url).uri;
|
if (args.engine == "__default__")
|
||||||
} else
|
engine = Services.search.currentEngine || Services.search.defaultEngine;
|
||||||
|
else
|
||||||
|
engine = Services.search.getEngineByName(args.engine);
|
||||||
|
|
||||||
|
if (engine)
|
||||||
|
uri = engine.getSubmission(args.url).uri;
|
||||||
|
} else {
|
||||||
uri = URIFixup.createFixupURI(args.url, Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);
|
uri = URIFixup.createFixupURI(args.url, Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);
|
||||||
|
}
|
||||||
return uri ? uri.spec : args.url;
|
return uri ? uri.spec : args.url;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -746,12 +771,6 @@ var NativeWindow = {
|
||||||
BrowserApp.addTab(url, { selected: false });
|
BrowserApp.addTab(url, { selected: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.add(Strings.browser.GetStringFromName("contextmenu.changeInputMethod"),
|
|
||||||
this.textContext,
|
|
||||||
function(aTarget) {
|
|
||||||
Cc["@mozilla.org/imepicker;1"].getService(Ci.nsIIMEPicker).show();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.add(Strings.browser.GetStringFromName("contextmenu.fullScreen"),
|
this.add(Strings.browser.GetStringFromName("contextmenu.fullScreen"),
|
||||||
this.SelectorContext("video:not(:-moz-full-screen)"),
|
this.SelectorContext("video:not(:-moz-full-screen)"),
|
||||||
function(aTarget) {
|
function(aTarget) {
|
||||||
|
@ -1153,10 +1172,10 @@ Tab.prototype = {
|
||||||
|
|
||||||
get viewport() {
|
get viewport() {
|
||||||
// Update the viewport to current dimensions
|
// Update the viewport to current dimensions
|
||||||
this._viewport.x = this.browser.contentWindow.scrollX +
|
this._viewport.x = (this.browser.contentWindow.scrollX +
|
||||||
this.viewportExcess.x;
|
this.viewportExcess.x) || 0;
|
||||||
this._viewport.y = this.browser.contentWindow.scrollY +
|
this._viewport.y = (this.browser.contentWindow.scrollY +
|
||||||
this.viewportExcess.y;
|
this.viewportExcess.y) || 0;
|
||||||
|
|
||||||
// Transform coordinates based on zoom
|
// Transform coordinates based on zoom
|
||||||
this._viewport.x = Math.round(this._viewport.x * this._viewport.zoom);
|
this._viewport.x = Math.round(this._viewport.x * this._viewport.zoom);
|
||||||
|
@ -1185,8 +1204,8 @@ Tab.prototype = {
|
||||||
updateViewport: function(aReset) {
|
updateViewport: function(aReset) {
|
||||||
let win = this.browser.contentWindow;
|
let win = this.browser.contentWindow;
|
||||||
let zoom = (aReset ? this.getDefaultZoomLevel() : this._viewport.zoom);
|
let zoom = (aReset ? this.getDefaultZoomLevel() : this._viewport.zoom);
|
||||||
let xpos = (aReset ? win.scrollX * zoom : this._viewport.x);
|
let xpos = ((aReset && win) ? win.scrollX * zoom : this._viewport.x);
|
||||||
let ypos = (aReset ? win.scrollY * zoom : this._viewport.y);
|
let ypos = ((aReset && win) ? win.scrollY * zoom : this._viewport.y);
|
||||||
|
|
||||||
this.viewportExcess = { x: 0, y: 0 };
|
this.viewportExcess = { x: 0, y: 0 };
|
||||||
this.viewport = { x: xpos, y: ypos,
|
this.viewport = { x: xpos, y: ypos,
|
||||||
|
@ -1374,12 +1393,16 @@ Tab.prototype = {
|
||||||
|
|
||||||
let browser = BrowserApp.getBrowserForWindow(contentWin);
|
let browser = BrowserApp.getBrowserForWindow(contentWin);
|
||||||
let uri = browser.currentURI.spec;
|
let uri = browser.currentURI.spec;
|
||||||
|
let documentURI = browser.contentDocument.documentURIObject.spec;
|
||||||
|
let contentType = browser.contentDocument.contentType;
|
||||||
|
|
||||||
let message = {
|
let message = {
|
||||||
gecko: {
|
gecko: {
|
||||||
type: "Content:LocationChange",
|
type: "Content:LocationChange",
|
||||||
tabID: this.id,
|
tabID: this.id,
|
||||||
uri: uri
|
uri: uri,
|
||||||
|
documentURI: documentURI,
|
||||||
|
contentType: contentType
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2891,6 +2914,83 @@ var ConsoleAPI = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var ClipboardHelper = {
|
||||||
|
init: function() {
|
||||||
|
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.copy"), ClipboardHelper.getCopyContext(false), ClipboardHelper.copy.bind(ClipboardHelper));
|
||||||
|
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.copyAll"), ClipboardHelper.getCopyContext(true), ClipboardHelper.copy.bind(ClipboardHelper));
|
||||||
|
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.selectAll"), NativeWindow.contextmenus.textContext, ClipboardHelper.select.bind(ClipboardHelper));
|
||||||
|
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.paste"), ClipboardHelper.pasteContext, ClipboardHelper.paste.bind(ClipboardHelper));
|
||||||
|
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.changeInputMethod"), NativeWindow.contextmenus.textContext, ClipboardHelper.inputMethod.bind(ClipboardHelper));
|
||||||
|
},
|
||||||
|
|
||||||
|
get clipboardHelper() {
|
||||||
|
delete this.clipboardHelper;
|
||||||
|
return this.clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
||||||
|
},
|
||||||
|
|
||||||
|
get clipboard() {
|
||||||
|
delete this.clipboard;
|
||||||
|
return this.clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
|
||||||
|
},
|
||||||
|
|
||||||
|
copy: function(aElement) {
|
||||||
|
let selectionStart = aElement.selectionStart;
|
||||||
|
let selectionEnd = aElement.selectionEnd;
|
||||||
|
if (selectionStart != selectionEnd) {
|
||||||
|
string = aElement.value.slice(selectionStart, selectionEnd);
|
||||||
|
this.clipboardHelper.copyString(string);
|
||||||
|
} else {
|
||||||
|
this.clipboardHelper.copyString(aElement.value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
select: function(aElement) {
|
||||||
|
if (!aElement || !(aElement instanceof Ci.nsIDOMNSEditableElement))
|
||||||
|
return;
|
||||||
|
let target = aElement.QueryInterface(Ci.nsIDOMNSEditableElement);
|
||||||
|
target.editor.selectAll();
|
||||||
|
target.focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
paste: function(aElement) {
|
||||||
|
if (!aElement || !(aElement instanceof Ci.nsIDOMNSEditableElement))
|
||||||
|
return;
|
||||||
|
let target = aElement.QueryInterface(Ci.nsIDOMNSEditableElement);
|
||||||
|
target.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
|
||||||
|
target.focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
inputMethod: function(aElement) {
|
||||||
|
Cc["@mozilla.org/imepicker;1"].getService(Ci.nsIIMEPicker).show();
|
||||||
|
},
|
||||||
|
|
||||||
|
getCopyContext: function(isCopyAll) {
|
||||||
|
return {
|
||||||
|
matches: function(aElement) {
|
||||||
|
if (NativeWindow.contextmenus.textContext.matches(aElement)) {
|
||||||
|
let selectionStart = aElement.selectionStart;
|
||||||
|
let selectionEnd = aElement.selectionEnd;
|
||||||
|
if (selectionStart != selectionEnd)
|
||||||
|
return true;
|
||||||
|
else if (isCopyAll)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
pasteContext: {
|
||||||
|
matches: function(aElement) {
|
||||||
|
if (NativeWindow.contextmenus.textContext.matches(aElement)) {
|
||||||
|
let flavors = ["text/unicode"];
|
||||||
|
return ClipboardHelper.clipboard.hasDataMatchingFlavors(flavors, flavors.length, Ci.nsIClipboard.kGlobalClipboard);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var PluginHelper = {
|
var PluginHelper = {
|
||||||
showDoorHanger: function(aTab) {
|
showDoorHanger: function(aTab) {
|
||||||
let message = Strings.browser.GetStringFromName("clickToPlayFlash.message");
|
let message = Strings.browser.GetStringFromName("clickToPlayFlash.message");
|
||||||
|
@ -2919,6 +3019,32 @@ var PluginHelper = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getPluginPreference: function getPluginPreference() {
|
||||||
|
let pluginDisable = Services.prefs.getBoolPref("plugin.disable");
|
||||||
|
if (pluginDisable)
|
||||||
|
return "0";
|
||||||
|
|
||||||
|
let clickToPlay = Services.prefs.getBoolPref("plugins.click_to_play");
|
||||||
|
return clickToPlay ? "2" : "1";
|
||||||
|
},
|
||||||
|
|
||||||
|
setPluginPreference: function setPluginPreference(aValue) {
|
||||||
|
switch (aValue) {
|
||||||
|
case "0": // Enable Plugins = No
|
||||||
|
Services.prefs.setBoolPref("plugin.disable", true);
|
||||||
|
Services.prefs.clearUserPref("plugins.click_to_play");
|
||||||
|
break;
|
||||||
|
case "1": // Enable Plugins = Yes
|
||||||
|
Services.prefs.clearUserPref("plugin.disable");
|
||||||
|
Services.prefs.setBoolPref("plugins.click_to_play", false);
|
||||||
|
break;
|
||||||
|
case "2": // Enable Plugins = Tap to Play (default)
|
||||||
|
Services.prefs.clearUserPref("plugin.disable");
|
||||||
|
Services.prefs.clearUserPref("plugins.click_to_play");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Mostly copied from /browser/base/content/browser.js
|
// Mostly copied from /browser/base/content/browser.js
|
||||||
addPluginClickCallback: function (plugin, callbackName /*callbackArgs...*/) {
|
addPluginClickCallback: function (plugin, callbackName /*callbackArgs...*/) {
|
||||||
// XXX just doing (callback)(arg) was giving a same-origin error. bug?
|
// XXX just doing (callback)(arg) was giving a same-origin error. bug?
|
||||||
|
|
|
@ -1,2 +1,8 @@
|
||||||
<!ENTITY aboutAddons.title "Add-ons Manager">
|
<!ENTITY aboutAddons.title "Add-ons Manager">
|
||||||
<!ENTITY aboutAddons.header "Add-ons">
|
<!ENTITY aboutAddons.header "Add-ons">
|
||||||
|
<!ENTITY aboutAddons.options "Options">
|
||||||
|
|
||||||
|
<!ENTITY addonAction.enable "Enable">
|
||||||
|
<!ENTITY addonAction.disable "Disable">
|
||||||
|
<!ENTITY addonAction.uninstall "Uninstall">
|
||||||
|
<!ENTITY addonAction.cancel "Cancel">
|
||||||
|
|
|
@ -3,4 +3,10 @@ addonAction.disable=Disable
|
||||||
addonAction.uninstall=Uninstall
|
addonAction.uninstall=Uninstall
|
||||||
addonAction.cancel=Cancel
|
addonAction.cancel=Cancel
|
||||||
addonAction.options=Options
|
addonAction.options=Options
|
||||||
|
|
||||||
addonsSearchEngine.description=Integrated Search
|
addonsSearchEngine.description=Integrated Search
|
||||||
|
|
||||||
|
addonType.extension=Extension
|
||||||
|
addonType.theme=Theme
|
||||||
|
addonType.locale=Locale
|
||||||
|
addonType.search=Search
|
||||||
|
|
|
@ -11,18 +11,6 @@
|
||||||
<!ENTITY newtab.label "New Tab">
|
<!ENTITY newtab.label "New Tab">
|
||||||
<!ENTITY closetab.label "Close Tab">
|
<!ENTITY closetab.label "Close Tab">
|
||||||
|
|
||||||
<!ENTITY cut.label "Cut">
|
|
||||||
<!ENTITY copy.label "Copy">
|
|
||||||
<!ENTITY copyAll.label "Copy All">
|
|
||||||
<!ENTITY copylink.label "Copy Link Location">
|
|
||||||
<!ENTITY paste.label "Paste">
|
|
||||||
<!ENTITY pasteAndGo.label "Paste & Go">
|
|
||||||
<!ENTITY delete.label "Delete">
|
|
||||||
<!ENTITY selectAll.label "Select All">
|
|
||||||
<!ENTITY noSuggestions.label "(No suggestions)">
|
|
||||||
<!ENTITY addToDictionary.label "Add to Dictionary">
|
|
||||||
<!ENTITY inputMethod.label "Select Input Method">
|
|
||||||
|
|
||||||
<!ENTITY allPagesHeader.label "All Pages">
|
<!ENTITY allPagesHeader.label "All Pages">
|
||||||
<!ENTITY bookmarksHeader.label "Bookmarks">
|
<!ENTITY bookmarksHeader.label "Bookmarks">
|
||||||
<!ENTITY historyHeader.label "History">
|
<!ENTITY historyHeader.label "History">
|
||||||
|
|
|
@ -35,11 +35,6 @@ addonsSearchEngine.description=Integrated Search
|
||||||
addonsConfirmInstall.title=Installing Add-on
|
addonsConfirmInstall.title=Installing Add-on
|
||||||
addonsConfirmInstall.install=Install
|
addonsConfirmInstall.install=Install
|
||||||
|
|
||||||
addonType.2=Extension
|
|
||||||
addonType.4=Theme
|
|
||||||
addonType.8=Locale
|
|
||||||
addonType.1024=Search
|
|
||||||
|
|
||||||
addonUpdate.checking=Checking for updates…
|
addonUpdate.checking=Checking for updates…
|
||||||
addonUpdate.updating=Updating to %S
|
addonUpdate.updating=Updating to %S
|
||||||
addonUpdate.updated=Updated to %S
|
addonUpdate.updated=Updated to %S
|
||||||
|
@ -261,6 +256,11 @@ contextmenu.openInNewTab=Open Link in New Tab
|
||||||
contextmenu.changeInputMethod=Select Input Method
|
contextmenu.changeInputMethod=Select Input Method
|
||||||
contextmenu.fullScreen=Full Screen
|
contextmenu.fullScreen=Full Screen
|
||||||
|
|
||||||
|
contextmenu.copy=Copy
|
||||||
|
contextmenu.copyAll=Copy All
|
||||||
|
contextmenu.selectAll=Select All
|
||||||
|
contextmenu.paste=Paste
|
||||||
|
|
||||||
# Select UI
|
# Select UI
|
||||||
selectHelper.closeMultipleSelectDialog=Done
|
selectHelper.closeMultipleSelectDialog=Done
|
||||||
|
|
||||||
|
|
|
@ -36,41 +36,82 @@
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-size: 24px;
|
font-size: 18px;
|
||||||
|
background-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addons-header {
|
body {
|
||||||
border-bottom: 3px solid black;
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#addons-header {
|
||||||
|
color: white;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
height: 44px;
|
||||||
|
border-bottom: 2px solid #d96016;
|
||||||
|
width: 100%;
|
||||||
|
display: -moz-box;
|
||||||
|
-moz-box-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#addons-list {
|
||||||
|
background-color: #0e1012;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-item {
|
.addon-item {
|
||||||
border-bottom: 1px solid black;
|
color: #656565;
|
||||||
padding: 8px;
|
background-color: #0e1012;
|
||||||
|
border-bottom: 3px solid black;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
padding-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-item:last-child {
|
.addon-item:last-child {
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-item:not([optionsURL]) .options-btn {
|
.addon-item:not([optionsURL]) .options-header,
|
||||||
visibility: hidden;
|
.addon-item[optionsURL=""] .options-header,
|
||||||
|
.addon-item:not([optionsURL]) .options-box,
|
||||||
|
.addon-item[optionsURL=""] .options-box {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make room for the image */
|
/* Make room for the image */
|
||||||
.inner {
|
.inner {
|
||||||
-moz-margin-start: 48px;
|
-moz-margin-start: 56px;
|
||||||
|
-moz-margin-end: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
display: -moz-box;
|
||||||
|
-moz-box-orient: horizontal;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details > div {
|
||||||
|
display: -moz-box;
|
||||||
|
-moz-box-align: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
color: black;
|
font-size: 22px;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.version {
|
.version {
|
||||||
/* The addon title is not localized, so keep the margin on the left side */
|
/* The addon title is not localized, so keep the margin on the left side */
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: gray;
|
-moz-box-flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
text-align: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
|
@ -82,6 +123,31 @@ html {
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
|
display: -moz-box;
|
||||||
|
-moz-box-orient: horizontal;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons > button {
|
||||||
|
-moz-appearance: none;
|
||||||
|
color: white;
|
||||||
|
font-size: 18px !important;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-top-color: black;
|
||||||
|
-moz-border-start-color: black;
|
||||||
|
background-image: none;
|
||||||
|
background-color: #17181a;
|
||||||
|
border-radius: 0px !important;
|
||||||
|
-moz-box-flex: 1;
|
||||||
|
padding: 20px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons > button[disabled="true"] {
|
||||||
|
color: #b5b5b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons:first-child {
|
||||||
|
-moz-border-start-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
body[dir="ltr"] .favicon {
|
body[dir="ltr"] .favicon {
|
||||||
|
@ -99,9 +165,3 @@ body[dir="ltr"] .favicon {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
|
||||||
color: black;
|
|
||||||
font-size: 28px !important;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
NSPR_4_9_BETA4
|
NSPR_4_9_BETA5
|
||||||
|
|
|
@ -42,4 +42,3 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#error "Do not include this header file."
|
#error "Do not include this header file."
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,36 @@ i?86-*android*)
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
dnl ========================================================
|
||||||
|
dnl = Gonk is a fork of Android used for Mozilla's B2G project.
|
||||||
|
dnl = Configuration is done largely by the top level config
|
||||||
|
dnl = and the specified gonk directory doesn't matter here.
|
||||||
|
dnl ========================================================
|
||||||
|
|
||||||
|
AC_ARG_WITH(gonk,
|
||||||
|
[ --with-gonk=DIR
|
||||||
|
location of gonk dir],
|
||||||
|
gonkdir=$withval)
|
||||||
|
|
||||||
|
if test -n "$gonkdir" ; then
|
||||||
|
dnl Most things are directly configured by env vars when building for gonk
|
||||||
|
|
||||||
|
dnl prevent cross compile section from using these flags as host flags
|
||||||
|
if test -z "$HOST_CPPFLAGS" ; then
|
||||||
|
HOST_CPPFLAGS=" "
|
||||||
|
fi
|
||||||
|
if test -z "$HOST_CFLAGS" ; then
|
||||||
|
HOST_CFLAGS=" "
|
||||||
|
fi
|
||||||
|
if test -z "$HOST_CXXFLAGS" ; then
|
||||||
|
HOST_CXXFLAGS=" "
|
||||||
|
fi
|
||||||
|
if test -z "$HOST_LDFLAGS" ; then
|
||||||
|
HOST_LDFLAGS=" "
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_DEFINE(ANDROID)
|
||||||
|
else
|
||||||
case "$target" in
|
case "$target" in
|
||||||
*-android*|*-linuxandroid*)
|
*-android*|*-linuxandroid*)
|
||||||
if test -z "$android_ndk" ; then
|
if test -z "$android_ndk" ; then
|
||||||
|
@ -240,6 +270,7 @@ case "$target" in
|
||||||
AC_DEFINE(ANDROID)
|
AC_DEFINE(ANDROID)
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
dnl ========================================================
|
dnl ========================================================
|
||||||
dnl =
|
dnl =
|
||||||
|
@ -1358,6 +1389,9 @@ case "$target" in
|
||||||
CPU_ARCH=i386
|
CPU_ARCH=i386
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
x86_64)
|
||||||
|
CPU_ARCH=x86_64
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
CPU_ARCH=ppc
|
CPU_ARCH=ppc
|
||||||
;;
|
;;
|
||||||
|
@ -3308,55 +3342,68 @@ dnl ========================================================
|
||||||
dnl Generate output files.
|
dnl Generate output files.
|
||||||
dnl ========================================================
|
dnl ========================================================
|
||||||
MAKEFILES="
|
MAKEFILES="
|
||||||
Makefile
|
Makefile
|
||||||
config/Makefile
|
config/Makefile
|
||||||
config/autoconf.mk
|
config/autoconf.mk
|
||||||
config/nsprincl.mk
|
config/nsprincl.mk
|
||||||
config/nsprincl.sh
|
config/nsprincl.sh
|
||||||
config/nspr-config
|
config/nspr-config
|
||||||
lib/Makefile
|
lib/Makefile
|
||||||
lib/ds/Makefile
|
lib/ds/Makefile
|
||||||
lib/libc/Makefile
|
lib/libc/Makefile
|
||||||
lib/libc/include/Makefile
|
lib/libc/include/Makefile
|
||||||
lib/libc/src/Makefile
|
lib/libc/src/Makefile
|
||||||
lib/tests/Makefile
|
lib/tests/Makefile
|
||||||
pkg/Makefile
|
pkg/Makefile
|
||||||
pkg/linux/Makefile
|
pr/Makefile
|
||||||
pkg/solaris/Makefile
|
pr/include/Makefile
|
||||||
pkg/solaris/SUNWpr/Makefile
|
pr/include/md/Makefile
|
||||||
pkg/solaris/SUNWprd/Makefile
|
pr/include/obsolete/Makefile
|
||||||
pr/Makefile
|
pr/include/private/Makefile
|
||||||
pr/include/Makefile
|
pr/src/Makefile
|
||||||
pr/include/md/Makefile
|
pr/src/io/Makefile
|
||||||
pr/include/obsolete/Makefile
|
pr/src/linking/Makefile
|
||||||
pr/include/private/Makefile
|
pr/src/malloc/Makefile
|
||||||
pr/src/Makefile
|
pr/src/md/Makefile
|
||||||
pr/src/io/Makefile
|
pr/src/md/${PR_MD_ARCH_DIR}/Makefile
|
||||||
pr/src/linking/Makefile
|
pr/src/memory/Makefile
|
||||||
pr/src/malloc/Makefile
|
pr/src/misc/Makefile
|
||||||
pr/src/md/Makefile
|
pr/src/threads/Makefile
|
||||||
pr/src/md/${PR_MD_ARCH_DIR}/Makefile
|
pr/tests/Makefile
|
||||||
pr/src/memory/Makefile
|
pr/tests/dll/Makefile
|
||||||
pr/src/misc/Makefile
|
|
||||||
pr/src/threads/Makefile
|
|
||||||
pr/tests/Makefile
|
|
||||||
pr/tests/dll/Makefile
|
|
||||||
"
|
"
|
||||||
|
|
||||||
dnl lib/tests/Makefile
|
if test "$OS_TARGET" = "Linux"; then
|
||||||
dnl pr/tests/w16gui/Makefile
|
MAKEFILES="$MAKEFILES
|
||||||
dnl tools/Makefile
|
pkg/linux/Makefile
|
||||||
|
"
|
||||||
|
elif test "$OS_TARGET" = "SunOS"; then
|
||||||
|
MAKEFILES="$MAKEFILES
|
||||||
|
pkg/solaris/Makefile
|
||||||
|
pkg/solaris/SUNWpr/Makefile
|
||||||
|
pkg/solaris/SUNWprd/Makefile
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
if test -z "$USE_PTHREADS" && test -z "$USE_BTHREADS"; then
|
if test -z "$USE_PTHREADS" && test -z "$USE_BTHREADS"; then
|
||||||
MAKEFILES="$MAKEFILES pr/src/threads/combined/Makefile"
|
MAKEFILES="$MAKEFILES
|
||||||
|
pr/src/threads/combined/Makefile
|
||||||
|
"
|
||||||
elif test -n "$USE_PTHREADS"; then
|
elif test -n "$USE_PTHREADS"; then
|
||||||
MAKEFILES="$MAKEFILES pr/src/pthreads/Makefile"
|
MAKEFILES="$MAKEFILES
|
||||||
|
pr/src/pthreads/Makefile
|
||||||
|
"
|
||||||
elif test -n "$USE_BTHREADS"; then
|
elif test -n "$USE_BTHREADS"; then
|
||||||
MAKEFILES="$MAKEFILES pr/src/bthreads/Makefile"
|
MAKEFILES="$MAKEFILES
|
||||||
|
pr/src/bthreads/Makefile
|
||||||
|
"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test -n "$USE_CPLUS"; then
|
if test -n "$USE_CPLUS"; then
|
||||||
MAKEFILES="$MAKEFILES pr/src/cplus/Makefile pr/src/cplus/tests/Makefile"
|
MAKEFILES="$MAKEFILES
|
||||||
|
pr/src/cplus/Makefile
|
||||||
|
pr/src/cplus/tests/Makefile
|
||||||
|
"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo $MAKEFILES > unallmakefiles
|
echo $MAKEFILES > unallmakefiles
|
||||||
|
|
|
@ -1497,8 +1497,9 @@ PR_ParseTimeStringToExplodedTime(
|
||||||
/* "-" is ignored at the beginning of a token if we have not yet
|
/* "-" is ignored at the beginning of a token if we have not yet
|
||||||
parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
|
parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
|
||||||
the character after the dash is not a digit. */
|
the character after the dash is not a digit. */
|
||||||
if (*rest == '-' && ((rest > string && isalpha(rest[-1]) && year < 0)
|
if (*rest == '-' && ((rest > string &&
|
||||||
|| rest[1] < '0' || rest[1] > '9'))
|
isalpha((unsigned char)rest[-1]) && year < 0) ||
|
||||||
|
rest[1] < '0' || rest[1] > '9'))
|
||||||
{
|
{
|
||||||
rest++;
|
rest++;
|
||||||
goto SKIP_MORE;
|
goto SKIP_MORE;
|
||||||
|
|
|
@ -298,7 +298,6 @@ SHELL_WRAPPER0(executeNextRunnable)
|
||||||
SHELL_WRAPPER1(cameraCallbackBridge, jbyteArray)
|
SHELL_WRAPPER1(cameraCallbackBridge, jbyteArray)
|
||||||
SHELL_WRAPPER1(notifyUriVisited, jstring)
|
SHELL_WRAPPER1(notifyUriVisited, jstring)
|
||||||
SHELL_WRAPPER3(notifyBatteryChange, jdouble, jboolean, jdouble);
|
SHELL_WRAPPER3(notifyBatteryChange, jdouble, jboolean, jdouble);
|
||||||
SHELL_WRAPPER1_WITH_RETURN(canCreateFixupURI, bool, jstring);
|
|
||||||
SHELL_WRAPPER3(notifySmsReceived, jstring, jstring, jlong);
|
SHELL_WRAPPER3(notifySmsReceived, jstring, jstring, jlong);
|
||||||
|
|
||||||
static void * xul_handle = NULL;
|
static void * xul_handle = NULL;
|
||||||
|
@ -704,7 +703,6 @@ loadLibs(const char *apkName)
|
||||||
GETFUNC(cameraCallbackBridge);
|
GETFUNC(cameraCallbackBridge);
|
||||||
GETFUNC(notifyUriVisited);
|
GETFUNC(notifyUriVisited);
|
||||||
GETFUNC(notifyBatteryChange);
|
GETFUNC(notifyBatteryChange);
|
||||||
GETFUNC(canCreateFixupURI);
|
|
||||||
GETFUNC(notifySmsReceived);
|
GETFUNC(notifySmsReceived);
|
||||||
#undef GETFUNC
|
#undef GETFUNC
|
||||||
sStartupTimeline = (uint64_t *)__wrap_dlsym(xul_handle, "_ZN7mozilla15StartupTimeline16sStartupTimelineE");
|
sStartupTimeline = (uint64_t *)__wrap_dlsym(xul_handle, "_ZN7mozilla15StartupTimeline16sStartupTimelineE");
|
||||||
|
|
|
@ -37,20 +37,23 @@
|
||||||
|
|
||||||
MAKEFILES_crypto="
|
MAKEFILES_crypto="
|
||||||
services/crypto/Makefile
|
services/crypto/Makefile
|
||||||
"
|
services/crypto/component/Makefile
|
||||||
|
"
|
||||||
|
|
||||||
MAKEFILES_sync="
|
MAKEFILES_sync="
|
||||||
services/sync/Makefile
|
services/sync/Makefile
|
||||||
services/sync/locales/Makefile
|
services/sync/locales/Makefile
|
||||||
"
|
"
|
||||||
|
|
||||||
if [ "$ENABLE_TESTS" ]; then
|
|
||||||
MAKEFILES_crypto="$MAKEFILES_crypto services/crypto/tests/Makefile"
|
|
||||||
MAKEFILES_sync="$MAKEFILES_sync services/sync/tests/Makefile"
|
|
||||||
fi
|
|
||||||
|
|
||||||
add_makefiles "
|
add_makefiles "
|
||||||
services/Makefile
|
services/Makefile
|
||||||
$MAKEFILES_crypto
|
$MAKEFILES_crypto
|
||||||
$MAKEFILES_sync
|
$MAKEFILES_sync
|
||||||
|
"
|
||||||
|
|
||||||
|
if [ "$ENABLE_TESTS" ]; then
|
||||||
|
add_makefiles "
|
||||||
|
services/crypto/tests/Makefile
|
||||||
|
services/sync/tests/Makefile
|
||||||
"
|
"
|
||||||
|
fi
|
||||||
|
|
|
@ -4,7 +4,7 @@ Universal manifests for Mozilla test harnesses
|
||||||
|
|
||||||
What ManifestDestiny gives you:
|
What ManifestDestiny gives you:
|
||||||
|
|
||||||
* manifests are (ordered) lists of tests
|
* manifests are ordered lists of tests
|
||||||
* tests may have an arbitrary number of key, value pairs
|
* tests may have an arbitrary number of key, value pairs
|
||||||
* the parser returns an ordered list of test data structures, which
|
* the parser returns an ordered list of test data structures, which
|
||||||
are just dicts with some keys. For example, a test with no
|
are just dicts with some keys. For example, a test with no
|
||||||
|
@ -23,6 +23,14 @@ additional key, value metadata to each test.
|
||||||
|
|
||||||
# Why have test manifests?
|
# Why have test manifests?
|
||||||
|
|
||||||
|
It is desirable to have a unified format for test manifests for testing
|
||||||
|
[mozilla-central](http://hg.mozilla.org/mozilla-central), etc.
|
||||||
|
|
||||||
|
* It is desirable to be able to selectively enable or disable tests based on platform or other conditions. This should be easy to do. Currently, since many of the harnesses just crawl directories, there is no effective way of disabling a test except for removal from mozilla-central
|
||||||
|
* It is desriable to do this in a universal way so that enabling and disabling tests as well as other tasks are easily accessible to a wider audience than just those intimately familiar with the specific test framework.
|
||||||
|
* It is desirable to have other metadata on top of the test. For instance, let's say a test is marked as skipped. It would be nice to give the reason why.
|
||||||
|
|
||||||
|
|
||||||
Most Mozilla test harnesses work by crawling a directory structure.
|
Most Mozilla test harnesses work by crawling a directory structure.
|
||||||
While this is straight-forward, manifests offer several practical
|
While this is straight-forward, manifests offer several practical
|
||||||
advantages::
|
advantages::
|
||||||
|
@ -37,8 +45,8 @@ advantages::
|
||||||
removing it from the tree and a bug filed with the appropriate
|
removing it from the tree and a bug filed with the appropriate
|
||||||
reason:
|
reason:
|
||||||
|
|
||||||
[test_broken.js]
|
[test_broken.js]
|
||||||
disabled = https://bugzilla.mozilla.org/show_bug.cgi?id=123456
|
disabled = https://bugzilla.mozilla.org/show_bug.cgi?id=123456
|
||||||
|
|
||||||
* ability to run different (subsets of) tests on different
|
* ability to run different (subsets of) tests on different
|
||||||
platforms. Traditionally, we've done a bit of magic or had the test
|
platforms. Traditionally, we've done a bit of magic or had the test
|
||||||
|
@ -46,8 +54,8 @@ advantages::
|
||||||
can mark what platforms a test will or will not run on and change
|
can mark what platforms a test will or will not run on and change
|
||||||
these without changing the test.
|
these without changing the test.
|
||||||
|
|
||||||
[test_works_on_windows_only.js]
|
[test_works_on_windows_only.js]
|
||||||
run-if = os == 'win'
|
run-if = os == 'win'
|
||||||
|
|
||||||
* ability to markup tests with metadata. We have a large, complicated,
|
* ability to markup tests with metadata. We have a large, complicated,
|
||||||
and always changing infrastructure. key, value metadata may be used
|
and always changing infrastructure. key, value metadata may be used
|
||||||
|
@ -65,32 +73,32 @@ advantages::
|
||||||
Manifests are .ini file with the section names denoting the path
|
Manifests are .ini file with the section names denoting the path
|
||||||
relative to the manifest:
|
relative to the manifest:
|
||||||
|
|
||||||
[foo.js]
|
[foo.js]
|
||||||
[bar.js]
|
[bar.js]
|
||||||
[fleem.js]
|
[fleem.js]
|
||||||
|
|
||||||
The sections are read in order. In addition, tests may include
|
The sections are read in order. In addition, tests may include
|
||||||
arbitrary key, value metadata to be used by the harness. You may also
|
arbitrary key, value metadata to be used by the harness. You may also
|
||||||
have a `[DEFAULT]` section that will give key, value pairs that will
|
have a `[DEFAULT]` section that will give key, value pairs that will
|
||||||
be inherited by each test unless overridden:
|
be inherited by each test unless overridden:
|
||||||
|
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
type = restart
|
type = restart
|
||||||
|
|
||||||
[lilies.js]
|
[lilies.js]
|
||||||
color = white
|
color = white
|
||||||
|
|
||||||
[daffodils.js]
|
[daffodils.js]
|
||||||
color = yellow
|
color = yellow
|
||||||
type = other
|
type = other
|
||||||
# override type from DEFAULT
|
# override type from DEFAULT
|
||||||
|
|
||||||
[roses.js]
|
[roses.js]
|
||||||
color = red
|
color = red
|
||||||
|
|
||||||
You can also include other manifests:
|
You can also include other manifests:
|
||||||
|
|
||||||
[include:subdir/anothermanifest.ini]
|
[include:subdir/anothermanifest.ini]
|
||||||
|
|
||||||
Manifests are included relative to the directory of the manifest with
|
Manifests are included relative to the directory of the manifest with
|
||||||
the `[include:]` directive unless they are absolute paths.
|
the `[include:]` directive unless they are absolute paths.
|
||||||
|
@ -109,7 +117,7 @@ terms).
|
||||||
|
|
||||||
This data corresponds to a one-line manifest:
|
This data corresponds to a one-line manifest:
|
||||||
|
|
||||||
[testToolbar/testBackForwardButtons.js]
|
[testToolbar/testBackForwardButtons.js]
|
||||||
|
|
||||||
If additional key, values were specified, they would be in this dict
|
If additional key, values were specified, they would be in this dict
|
||||||
as well.
|
as well.
|
||||||
|
@ -128,13 +136,13 @@ integration layer. This should allow whatever sort of logic is
|
||||||
desired. For instance, if in yourtestharness you wanted to run only on
|
desired. For instance, if in yourtestharness you wanted to run only on
|
||||||
mondays for a certain class of tests:
|
mondays for a certain class of tests:
|
||||||
|
|
||||||
tests = []
|
tests = []
|
||||||
for test in manifests.tests:
|
for test in manifests.tests:
|
||||||
if 'runOnDay' in test:
|
if 'runOnDay' in test:
|
||||||
if calendar.day_name[calendar.weekday(*datetime.datetime.now().timetuple()[:3])].lower() == test['runOnDay'].lower():
|
if calendar.day_name[calendar.weekday(*datetime.datetime.now().timetuple()[:3])].lower() == test['runOnDay'].lower():
|
||||||
tests.append(test)
|
tests.append(test)
|
||||||
else:
|
else:
|
||||||
tests.append(test)
|
tests.append(test)
|
||||||
|
|
||||||
To recap:
|
To recap:
|
||||||
* the manifests allow you to specify test data
|
* the manifests allow you to specify test data
|
||||||
|
@ -146,7 +154,7 @@ http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests/
|
||||||
|
|
||||||
Additional manifest files may be included with an `[include:]` directive:
|
Additional manifest files may be included with an `[include:]` directive:
|
||||||
|
|
||||||
[include:path-to-additional-file.manifest]
|
[include:path-to-additional-file.manifest]
|
||||||
|
|
||||||
The path to included files is relative to the current manifest.
|
The path to included files is relative to the current manifest.
|
||||||
|
|
||||||
|
@ -183,7 +191,7 @@ in particular.
|
||||||
|
|
||||||
A test harness will normally call `TestManifest.active_tests`:
|
A test harness will normally call `TestManifest.active_tests`:
|
||||||
|
|
||||||
def active_tests(self, exists=True, disabled=True, **tags):
|
def active_tests(self, exists=True, disabled=True, **tags):
|
||||||
|
|
||||||
The manifests are passed to the `__init__` or `read` methods with
|
The manifests are passed to the `__init__` or `read` methods with
|
||||||
appropriate arguments. `active_tests` then allows you to select the
|
appropriate arguments. `active_tests` then allows you to select the
|
||||||
|
@ -216,7 +224,7 @@ files. Run `manifestparser help create` for usage information.
|
||||||
|
|
||||||
To copy tests and manifests from a source:
|
To copy tests and manifests from a source:
|
||||||
|
|
||||||
manifestparser [options] copy from_manifest to_directory -tag1 -tag2 --key1=value1 key2=value2 ...
|
manifestparser [options] copy from_manifest to_directory -tag1 -tag2 --key1=value1 key2=value2 ...
|
||||||
|
|
||||||
|
|
||||||
# Upating Tests
|
# Upating Tests
|
||||||
|
@ -224,7 +232,81 @@ To copy tests and manifests from a source:
|
||||||
To update the tests associated with with a manifest from a source
|
To update the tests associated with with a manifest from a source
|
||||||
directory:
|
directory:
|
||||||
|
|
||||||
manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
|
manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
|
||||||
|
|
||||||
|
|
||||||
|
# Usage example
|
||||||
|
|
||||||
|
Here is an example of how to create manifests for a directory tree and
|
||||||
|
update the tests listed in the manifests from an external source.
|
||||||
|
|
||||||
|
## Creating Manifests
|
||||||
|
|
||||||
|
Let's say you want to make a series of manifests for a given directory structure containing `.js` test files:
|
||||||
|
|
||||||
|
testing/mozmill/tests/firefox/
|
||||||
|
testing/mozmill/tests/firefox/testAwesomeBar/
|
||||||
|
testing/mozmill/tests/firefox/testPreferences/
|
||||||
|
testing/mozmill/tests/firefox/testPrivateBrowsing/
|
||||||
|
testing/mozmill/tests/firefox/testSessionStore/
|
||||||
|
testing/mozmill/tests/firefox/testTechnicalTools/
|
||||||
|
testing/mozmill/tests/firefox/testToolbar/
|
||||||
|
testing/mozmill/tests/firefox/restartTests
|
||||||
|
|
||||||
|
You can use `manifestparser create` to do this:
|
||||||
|
|
||||||
|
$ manifestparser help create
|
||||||
|
Usage: manifestparser.py [options] create directory <directory> <...>
|
||||||
|
|
||||||
|
create a manifest from a list of directories
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-p PATTERN, --pattern=PATTERN
|
||||||
|
glob pattern for files
|
||||||
|
-i IGNORE, --ignore=IGNORE
|
||||||
|
directories to ignore
|
||||||
|
-w IN_PLACE, --in-place=IN_PLACE
|
||||||
|
Write .ini files in place; filename to write to
|
||||||
|
|
||||||
|
We only want `.js` files and we want to skip the `restartTests` directory.
|
||||||
|
We also want to write a manifest per directory, so I use the `--in-place`
|
||||||
|
option to write the manifests:
|
||||||
|
|
||||||
|
manifestparser create . -i restartTests -p '*.js' -w manifest.ini
|
||||||
|
|
||||||
|
This creates a manifest.ini per directory that we care about with the JS test files:
|
||||||
|
|
||||||
|
testing/mozmill/tests/firefox/manifest.ini
|
||||||
|
testing/mozmill/tests/firefox/testAwesomeBar/manifest.ini
|
||||||
|
testing/mozmill/tests/firefox/testPreferences/manifest.ini
|
||||||
|
testing/mozmill/tests/firefox/testPrivateBrowsing/manifest.ini
|
||||||
|
testing/mozmill/tests/firefox/testSessionStore/manifest.ini
|
||||||
|
testing/mozmill/tests/firefox/testTechnicalTools/manifest.ini
|
||||||
|
testing/mozmill/tests/firefox/testToolbar/manifest.ini
|
||||||
|
|
||||||
|
The top-level `manifest.ini` merely has `[include:]` references to the sub manifests:
|
||||||
|
|
||||||
|
[include:testAwesomeBar/manifest.ini]
|
||||||
|
[include:testPreferences/manifest.ini]
|
||||||
|
[include:testPrivateBrowsing/manifest.ini]
|
||||||
|
[include:testSessionStore/manifest.ini]
|
||||||
|
[include:testTechnicalTools/manifest.ini]
|
||||||
|
[include:testToolbar/manifest.ini]
|
||||||
|
|
||||||
|
Each sub-level manifest contains the (`.js`) test files relative to it.
|
||||||
|
|
||||||
|
## Updating the tests from manifests
|
||||||
|
|
||||||
|
You may need to update tests as given in manifests from a different source directory.
|
||||||
|
`manifestparser update` was made for just this purpose:
|
||||||
|
|
||||||
|
Usage: manifestparser [options] update manifest directory -tag1 -tag2 --key1=value1 --key2=value2 ...
|
||||||
|
|
||||||
|
update the tests as listed in a manifest from a directory
|
||||||
|
|
||||||
|
To update from a directory of tests in `~/mozmill/src/mozmill-tests/firefox/` run:
|
||||||
|
|
||||||
|
manifestparser update manifest.ini ~/mozmill/src/mozmill-tests/firefox/
|
||||||
|
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
|
@ -252,20 +334,20 @@ Run `manifestparser help` for usage information.
|
||||||
|
|
||||||
To create a manifest from a set of directories:
|
To create a manifest from a set of directories:
|
||||||
|
|
||||||
manifestparser [options] create directory <directory> <...> [create-options]
|
manifestparser [options] create directory <directory> <...> [create-options]
|
||||||
|
|
||||||
To output a manifest of tests:
|
To output a manifest of tests:
|
||||||
|
|
||||||
manifestparser [options] write manifest <manifest> <...> -tag1 -tag2 --key1=value1 --key2=value2 ...
|
manifestparser [options] write manifest <manifest> <...> -tag1 -tag2 --key1=value1 --key2=value2 ...
|
||||||
|
|
||||||
To copy tests and manifests from a source:
|
To copy tests and manifests from a source:
|
||||||
|
|
||||||
manifestparser [options] copy from_manifest to_manifest -tag1 -tag2 --key1=value1 key2=value2 ...
|
manifestparser [options] copy from_manifest to_manifest -tag1 -tag2 --key1=value1 key2=value2 ...
|
||||||
|
|
||||||
To update the tests associated with with a manifest from a source
|
To update the tests associated with with a manifest from a source
|
||||||
directory:
|
directory:
|
||||||
|
|
||||||
manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
|
manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
|
||||||
|
|
||||||
|
|
||||||
# Design Considerations
|
# Design Considerations
|
||||||
|
@ -309,6 +391,14 @@ through several design considerations.
|
||||||
installation.
|
installation.
|
||||||
|
|
||||||
|
|
||||||
|
# Developing ManifestDestiny
|
||||||
|
|
||||||
|
ManifestDestiny is developed and maintained by Mozilla's
|
||||||
|
[Automation and Testing Team](https://wiki.mozilla.org/Auto-tools).
|
||||||
|
The project page is located at
|
||||||
|
https://wiki.mozilla.org/Auto-tools/Projects/ManifestDestiny .
|
||||||
|
|
||||||
|
|
||||||
# Historical Reference
|
# Historical Reference
|
||||||
|
|
||||||
Date-ordered list of links about how manifests came to be where they are today::
|
Date-ordered list of links about how manifests came to be where they are today::
|
||||||
|
|
|
@ -3,6 +3,7 @@ from devicemanager import DeviceManager, DMError
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
class DeviceManagerADB(DeviceManager):
|
class DeviceManagerADB(DeviceManager):
|
||||||
|
|
||||||
|
@ -13,6 +14,7 @@ class DeviceManagerADB(DeviceManager):
|
||||||
self.retries = 0
|
self.retries = 0
|
||||||
self._sock = None
|
self._sock = None
|
||||||
self.useRunAs = False
|
self.useRunAs = False
|
||||||
|
self.useZip = False
|
||||||
self.packageName = None
|
self.packageName = None
|
||||||
if packageName == None:
|
if packageName == None:
|
||||||
if os.getenv('USER'):
|
if os.getenv('USER'):
|
||||||
|
@ -30,6 +32,10 @@ class DeviceManagerADB(DeviceManager):
|
||||||
except:
|
except:
|
||||||
self.useRunAs = False
|
self.useRunAs = False
|
||||||
self.packageName = None
|
self.packageName = None
|
||||||
|
try:
|
||||||
|
self.verifyZip()
|
||||||
|
except:
|
||||||
|
self.useZip = False
|
||||||
try:
|
try:
|
||||||
# a test to see if we have root privs
|
# a test to see if we have root privs
|
||||||
files = self.listFiles("/data/data")
|
files = self.listFiles("/data/data")
|
||||||
|
@ -103,31 +109,41 @@ class DeviceManagerADB(DeviceManager):
|
||||||
def pushDir(self, localDir, remoteDir):
|
def pushDir(self, localDir, remoteDir):
|
||||||
# adb "push" accepts a directory as an argument, but if the directory
|
# adb "push" accepts a directory as an argument, but if the directory
|
||||||
# contains symbolic links, the links are pushed, rather than the linked
|
# contains symbolic links, the links are pushed, rather than the linked
|
||||||
# files; we push file-by-file to get around this limitation
|
# files; we either zip/unzip or push file-by-file to get around this
|
||||||
|
# limitation
|
||||||
try:
|
try:
|
||||||
if (not self.dirExists(remoteDir)):
|
if (self.useZip):
|
||||||
self.mkDirs(remoteDir+"/x")
|
localZip = tempfile.mktemp()+".zip"
|
||||||
for root, dirs, files in os.walk(localDir, followlinks='true'):
|
remoteZip = remoteDir + "/adbdmtmp.zip"
|
||||||
relRoot = os.path.relpath(root, localDir)
|
subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir)
|
||||||
for file in files:
|
self.pushFile(localZip, remoteZip)
|
||||||
localFile = os.path.join(root, file)
|
os.remove(localZip)
|
||||||
remoteFile = remoteDir + "/"
|
self.checkCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir])
|
||||||
if (relRoot!="."):
|
self.checkCmdAs(["shell", "rm", remoteZip])
|
||||||
remoteFile = remoteFile + relRoot + "/"
|
else:
|
||||||
remoteFile = remoteFile + file
|
if (not self.dirExists(remoteDir)):
|
||||||
self.pushFile(localFile, remoteFile)
|
self.mkDirs(remoteDir+"/x")
|
||||||
for dir in dirs:
|
for root, dirs, files in os.walk(localDir, followlinks='true'):
|
||||||
targetDir = remoteDir + "/"
|
relRoot = os.path.relpath(root, localDir)
|
||||||
if (relRoot!="."):
|
for file in files:
|
||||||
targetDir = targetDir + relRoot + "/"
|
localFile = os.path.join(root, file)
|
||||||
targetDir = targetDir + dir
|
remoteFile = remoteDir + "/"
|
||||||
if (not self.dirExists(targetDir)):
|
if (relRoot!="."):
|
||||||
self.mkDir(targetDir)
|
remoteFile = remoteFile + relRoot + "/"
|
||||||
|
remoteFile = remoteFile + file
|
||||||
|
self.pushFile(localFile, remoteFile)
|
||||||
|
for dir in dirs:
|
||||||
|
targetDir = remoteDir + "/"
|
||||||
|
if (relRoot!="."):
|
||||||
|
targetDir = targetDir + relRoot + "/"
|
||||||
|
targetDir = targetDir + dir
|
||||||
|
if (not self.dirExists(targetDir)):
|
||||||
|
self.mkDir(targetDir)
|
||||||
self.checkCmdAs(["shell", "chmod", "777", remoteDir])
|
self.checkCmdAs(["shell", "chmod", "777", remoteDir])
|
||||||
return True
|
return remoteDir
|
||||||
except:
|
except:
|
||||||
print "pushing " + localDir + " to " + remoteDir + " failed"
|
print "pushing " + localDir + " to " + remoteDir + " failed"
|
||||||
return False
|
return None
|
||||||
|
|
||||||
# external function
|
# external function
|
||||||
# returns:
|
# returns:
|
||||||
|
@ -241,11 +257,25 @@ class DeviceManagerADB(DeviceManager):
|
||||||
acmd = ["shell", "am","start"]
|
acmd = ["shell", "am","start"]
|
||||||
cmd = ' '.join(cmd).strip()
|
cmd = ' '.join(cmd).strip()
|
||||||
i = cmd.find(" ")
|
i = cmd.find(" ")
|
||||||
|
# SUT identifies the URL by looking for :\\ -- another strategy to consider
|
||||||
|
re_url = re.compile('^[http|file|chrome|about].*')
|
||||||
|
last = cmd.rfind(" ")
|
||||||
|
uri = ""
|
||||||
|
args = ""
|
||||||
|
if re_url.match(cmd[last:].strip()):
|
||||||
|
args = cmd[i:last].strip()
|
||||||
|
uri = cmd[last:].strip()
|
||||||
|
else:
|
||||||
|
args = cmd[i:].strip()
|
||||||
acmd.append("-n")
|
acmd.append("-n")
|
||||||
acmd.append(cmd[0:i] + "/.App")
|
acmd.append(cmd[0:i] + "/.App")
|
||||||
acmd.append("--es")
|
acmd.append("--es")
|
||||||
acmd.append("args")
|
if args != "":
|
||||||
acmd.append(cmd[i:])
|
acmd.append("args")
|
||||||
|
acmd.append(args)
|
||||||
|
if uri != "":
|
||||||
|
acmd.append("-d")
|
||||||
|
acmd.append(''.join(['\'',uri, '\'']));
|
||||||
print acmd
|
print acmd
|
||||||
self.checkCmd(acmd)
|
self.checkCmd(acmd)
|
||||||
return outputFile;
|
return outputFile;
|
||||||
|
@ -578,3 +608,25 @@ class DeviceManagerADB(DeviceManager):
|
||||||
self.checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"])
|
self.checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"])
|
||||||
self.checkCmd(["shell", "run-as", packageName, "rm", "-r", devroot + "/sanity"])
|
self.checkCmd(["shell", "run-as", packageName, "rm", "-r", devroot + "/sanity"])
|
||||||
|
|
||||||
|
def isUnzipAvailable(self):
|
||||||
|
data = self.runCmd(["shell", "unzip"]).stdout.read()
|
||||||
|
if (re.search('Usage', data)):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def isLocalZipAvailable(self):
|
||||||
|
try:
|
||||||
|
subprocess.check_call(["zip", "-?"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def verifyZip(self):
|
||||||
|
# If "zip" can be run locally, and "unzip" can be run remotely, then pushDir
|
||||||
|
# can use these to push just one file per directory -- a significant
|
||||||
|
# optimization for large directories.
|
||||||
|
self.useZip = False
|
||||||
|
if (self.isUnzipAvailable() and self.isLocalZipAvailable()):
|
||||||
|
print "will use zip to push directories"
|
||||||
|
self.useZip = True
|
||||||
|
|
|
@ -110,31 +110,51 @@ def _extract(path, extdir=None, delete=False):
|
||||||
If delete is set to True, deletes the bundle at path
|
If delete is set to True, deletes the bundle at path
|
||||||
Returns the list of top level files that were extracted
|
Returns the list of top level files that were extracted
|
||||||
"""
|
"""
|
||||||
|
assert not os.path.isfile(extdir), "extdir cannot be a file"
|
||||||
|
if extdir is None:
|
||||||
|
extdir = os.path.dirname(path)
|
||||||
|
elif not os.path.isdir(extdir):
|
||||||
|
os.makedirs(extdir)
|
||||||
if zipfile.is_zipfile(path):
|
if zipfile.is_zipfile(path):
|
||||||
bundle = zipfile.ZipFile(path)
|
bundle = zipfile.ZipFile(path)
|
||||||
namelist = bundle.namelist()
|
namelist = bundle.namelist()
|
||||||
|
if hasattr(bundle, 'extractall'):
|
||||||
|
bundle.extractall(path=extdir)
|
||||||
|
# zipfile.extractall doesn't exist in Python 2.5
|
||||||
|
else:
|
||||||
|
for name in namelist:
|
||||||
|
filename = os.path.realpath(os.path.join(extdir, name))
|
||||||
|
if name.endswith("/"):
|
||||||
|
os.makedirs(filename)
|
||||||
|
else:
|
||||||
|
path = os.path.dirname(filename)
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
dest = open(filename, "wb")
|
||||||
|
dest.write(bundle.read(name))
|
||||||
|
dest.close()
|
||||||
elif tarfile.is_tarfile(path):
|
elif tarfile.is_tarfile(path):
|
||||||
bundle = tarfile.open(path)
|
bundle = tarfile.open(path)
|
||||||
namelist = bundle.getnames()
|
namelist = bundle.getnames()
|
||||||
|
if hasattr(bundle, 'extractall'):
|
||||||
|
bundle.extractall(path=extdir)
|
||||||
|
# tarfile.extractall doesn't exist in Python 2.4
|
||||||
|
else:
|
||||||
|
for name in namelist:
|
||||||
|
bundle.extract(name, path=extdir)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
if extdir is None:
|
|
||||||
extdir = os.path.dirname(path)
|
|
||||||
elif not os.path.exists(extdir):
|
|
||||||
os.makedirs(extdir)
|
|
||||||
bundle.extractall(path=extdir)
|
|
||||||
bundle.close()
|
bundle.close()
|
||||||
if delete:
|
if delete:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
# namelist returns paths with forward slashes even in windows
|
# namelist returns paths with forward slashes even in windows
|
||||||
top_level_files = [os.path.join(extdir, name) for name in namelist
|
top_level_files = [os.path.join(extdir, name) for name in namelist
|
||||||
if len(name.rstrip('/').split('/')) == 1]
|
if len(name.rstrip('/').split('/')) == 1]
|
||||||
# namelist doesn't include folders in windows, append these to the list
|
# namelist doesn't include folders, append these to the list
|
||||||
if mozinfo.isWin:
|
for name in namelist:
|
||||||
for name in namelist:
|
root = os.path.join(extdir, name[:name.find('/')])
|
||||||
root = name[:name.find('/')]
|
if root not in top_level_files:
|
||||||
if root not in top_level_files:
|
top_level_files.append(root)
|
||||||
top_level_files.append(root)
|
|
||||||
return top_level_files
|
return top_level_files
|
||||||
|
|
||||||
def _install_dmg(src, dest):
|
def _install_dmg(src, dest):
|
||||||
|
|
|
@ -100,10 +100,12 @@ TestSuite.prototype.loadTest = function(test) {
|
||||||
log.log('TEST-END', test.name + ' ' + runTime + fThreshold);
|
log.log('TEST-END', test.name + ' ' + runTime + fThreshold);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error(test.name + ' | ' + e);
|
log.error(test.name + ' | ' + e);
|
||||||
log.debug(test.name + ' | Traceback:');
|
if (e['stack'] !== undefined) {
|
||||||
lines = e.stack.split('\n');
|
log.debug(test.name + ' | Traceback:');
|
||||||
for (let i = 0; i < lines.length - 1; ++i) {
|
lines = e.stack.split('\n');
|
||||||
log.debug('\t' + lines[i]);
|
for (let i = 0; i < lines.length - 1; ++i) {
|
||||||
|
log.debug('\t' + lines[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
|
|
||||||
from mozprocess import ProcessHandler
|
from mozprocess import ProcessHandler
|
||||||
from pepresults import Results
|
from pepresults import Results
|
||||||
|
from time import sleep
|
||||||
|
from threading import Thread
|
||||||
import mozlog
|
import mozlog
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
@ -57,6 +59,13 @@ class PepProcess(ProcessHandler):
|
||||||
self.logger = mozlog.getLogger('PEP')
|
self.logger = mozlog.getLogger('PEP')
|
||||||
results.fails[str(None)] = []
|
results.fails[str(None)] = []
|
||||||
|
|
||||||
|
def waitForQuit(self, timeout=5):
|
||||||
|
for i in range(1, timeout):
|
||||||
|
if self.proc.returncode != None:
|
||||||
|
return
|
||||||
|
sleep(1)
|
||||||
|
self.proc.kill()
|
||||||
|
|
||||||
def processOutputLine(self, line):
|
def processOutputLine(self, line):
|
||||||
"""
|
"""
|
||||||
Callback called on each line of output
|
Callback called on each line of output
|
||||||
|
@ -68,6 +77,11 @@ class PepProcess(ProcessHandler):
|
||||||
# The output is generated from the Peptest extension
|
# The output is generated from the Peptest extension
|
||||||
# Format is 'PEP <LEVEL> <MSG>' where <MSG> can have multiple tokens
|
# Format is 'PEP <LEVEL> <MSG>' where <MSG> can have multiple tokens
|
||||||
# The content of <MSG> depends on the <LEVEL>
|
# The content of <MSG> depends on the <LEVEL>
|
||||||
|
if line.find('Test Suite Finished') != -1:
|
||||||
|
thread = Thread(target=self.waitForQuit)
|
||||||
|
thread.setDaemon(True) # don't hang on quit
|
||||||
|
thread.start()
|
||||||
|
|
||||||
level = tokens[1]
|
level = tokens[1]
|
||||||
if level == 'TEST-START':
|
if level == 'TEST-START':
|
||||||
results.currentTest = tokens[2].rstrip()
|
results.currentTest = tokens[2].rstrip()
|
||||||
|
@ -81,11 +95,12 @@ class PepProcess(ProcessHandler):
|
||||||
threshold = 0.0
|
threshold = 0.0
|
||||||
|
|
||||||
msg = results.currentTest \
|
msg = results.currentTest \
|
||||||
+ ' | fail threshold: ' + str(threshold) \
|
+ ' | fail threshold: ' + str(threshold)
|
||||||
+ ' | metric: ' + str(metric)
|
|
||||||
if metric > threshold:
|
if metric > threshold:
|
||||||
|
msg += ' < metric: ' + str(metric)
|
||||||
self.logger.testFail(msg)
|
self.logger.testFail(msg)
|
||||||
else:
|
else:
|
||||||
|
msg += ' >= metric: ' + str(metric)
|
||||||
self.logger.testPass(msg)
|
self.logger.testPass(msg)
|
||||||
|
|
||||||
self.logger.testEnd(
|
self.logger.testEnd(
|
||||||
|
|
|
@ -68,25 +68,53 @@ def isURL(path):
|
||||||
def extract(path, extdir=None, delete=False):
|
def extract(path, extdir=None, delete=False):
|
||||||
"""
|
"""
|
||||||
Takes in a tar or zip file and extracts it to extdir
|
Takes in a tar or zip file and extracts it to extdir
|
||||||
If extdir is not specified, extracts to path
|
If extdir is not specified, extracts to os.path.dirname(path)
|
||||||
If delete is set to True, deletes the bundle at path
|
If delete is set to True, deletes the bundle at path
|
||||||
Returns the list of top level files that were extracted
|
Returns the list of top level files that were extracted
|
||||||
"""
|
"""
|
||||||
|
assert not os.path.isfile(extdir), "extdir cannot be a file"
|
||||||
|
if extdir is None:
|
||||||
|
extdir = os.path.dirname(path)
|
||||||
|
elif not os.path.isdir(extdir):
|
||||||
|
os.makedirs(extdir)
|
||||||
if zipfile.is_zipfile(path):
|
if zipfile.is_zipfile(path):
|
||||||
bundle = zipfile.ZipFile(path)
|
bundle = zipfile.ZipFile(path)
|
||||||
namelist = bundle.namelist()
|
namelist = bundle.namelist()
|
||||||
|
if hasattr(bundle, 'extractall'):
|
||||||
|
bundle.extractall(path=extdir)
|
||||||
|
# zipfile.extractall doesn't exist in Python 2.5
|
||||||
|
else:
|
||||||
|
for name in namelist:
|
||||||
|
filename = os.path.realpath(os.path.join(extdir, name))
|
||||||
|
if name.endswith("/"):
|
||||||
|
os.makedirs(filename)
|
||||||
|
else:
|
||||||
|
path = os.path.dirname(filename)
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
dest = open(filename, "wb")
|
||||||
|
dest.write(bundle.read(name))
|
||||||
|
dest.close()
|
||||||
elif tarfile.is_tarfile(path):
|
elif tarfile.is_tarfile(path):
|
||||||
bundle = tarfile.open(path)
|
bundle = tarfile.open(path)
|
||||||
namelist = bundle.getnames()
|
namelist = bundle.getnames()
|
||||||
|
if hasattr(bundle, 'extractall'):
|
||||||
|
bundle.extractall(path=extdir)
|
||||||
|
# tarfile.extractall doesn't exist in Python 2.4
|
||||||
|
else:
|
||||||
|
for name in namelist:
|
||||||
|
bundle.extract(name, path=extdir)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
if extdir is None:
|
|
||||||
extdir = os.path.dirname(path)
|
|
||||||
elif not os.path.exists(extdir):
|
|
||||||
os.makedirs(extdir)
|
|
||||||
bundle.extractall(path=extdir)
|
|
||||||
bundle.close()
|
bundle.close()
|
||||||
if delete:
|
if delete:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
return [os.path.join(extdir, name) for name in namelist
|
# namelist returns paths with forward slashes even in windows
|
||||||
if len(name.rstrip(os.sep).split(os.sep)) == 1]
|
top_level_files = [os.path.join(extdir, name) for name in namelist
|
||||||
|
if len(name.rstrip('/').split('/')) == 1]
|
||||||
|
# namelist doesn't include folders, append these to the list
|
||||||
|
for name in namelist:
|
||||||
|
root = os.path.join(extdir, name[:name.find('/')])
|
||||||
|
if root not in top_level_files:
|
||||||
|
top_level_files.append(root)
|
||||||
|
return top_level_files
|
||||||
|
|
|
@ -82,6 +82,7 @@ class Peptest():
|
||||||
testObj = {}
|
testObj = {}
|
||||||
testObj['path'] = os.path.realpath(self.options.testPath)
|
testObj['path'] = os.path.realpath(self.options.testPath)
|
||||||
testObj['name'] = os.path.basename(self.options.testPath)
|
testObj['name'] = os.path.basename(self.options.testPath)
|
||||||
|
testObj['here'] = os.path.dirname(testObj['path'])
|
||||||
tests.append(testObj)
|
tests.append(testObj)
|
||||||
else:
|
else:
|
||||||
# a test manifest was passed in
|
# a test manifest was passed in
|
||||||
|
|
|
@ -44,12 +44,15 @@ try:
|
||||||
except IOError:
|
except IOError:
|
||||||
description = ''
|
description = ''
|
||||||
|
|
||||||
version = "0.0"
|
version = "0.1"
|
||||||
|
|
||||||
dependencies = ['mozprofile',
|
dependencies = ['ManifestDestiny',
|
||||||
|
'mozhttpd',
|
||||||
|
'mozlog',
|
||||||
|
'mozprofile >= 0.1',
|
||||||
'mozprocess',
|
'mozprocess',
|
||||||
'mozrunner >= 3.0b3',
|
'mozrunner >= 3.0b3',
|
||||||
'mozlog']
|
]
|
||||||
|
|
||||||
setup(name='peptest',
|
setup(name='peptest',
|
||||||
version=version,
|
version=version,
|
||||||
|
|
|
@ -359,8 +359,6 @@ FormHistory.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
get dbConnection() {
|
get dbConnection() {
|
||||||
let connection;
|
|
||||||
|
|
||||||
// Make sure dbConnection can't be called from now to prevent infinite loops.
|
// Make sure dbConnection can't be called from now to prevent infinite loops.
|
||||||
delete FormHistory.prototype.dbConnection;
|
delete FormHistory.prototype.dbConnection;
|
||||||
|
|
||||||
|
|
|
@ -144,11 +144,7 @@ function endTest() {
|
||||||
.getMostRecentWindow("Download:Manager")
|
.getMostRecentWindow("Download:Manager")
|
||||||
.close();
|
.close();
|
||||||
|
|
||||||
Cc["@mozilla.org/browser/nav-history-service;1"]
|
waitForClearHistory(SimpleTest.finish);
|
||||||
.getService(Ci.nsINavHistoryService)
|
|
||||||
.removeAllPages();
|
|
||||||
|
|
||||||
SimpleTest.finish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
]]>
|
]]>
|
||||||
|
|
|
@ -168,3 +168,16 @@ function setCleanState()
|
||||||
let win = getDMWindow();
|
let win = getDMWindow();
|
||||||
if (win) win.close();
|
if (win) win.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears history invoking callback when done.
|
||||||
|
*/
|
||||||
|
function waitForClearHistory(aCallback) {
|
||||||
|
Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
|
||||||
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
|
Services.obs.addObserver(function observeClearHistory(aSubject, aTopic) {
|
||||||
|
Services.obs.removeObserver(observeClearHistory, aTopic);
|
||||||
|
aCallback();
|
||||||
|
}, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
|
||||||
|
PlacesUtils.bhistory.removeAllPages();
|
||||||
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
<xul:spacer flex="1"/>
|
<xul:spacer flex="1"/>
|
||||||
<xul:box class="icon"/>
|
<xul:box class="icon"/>
|
||||||
<html:div class="msg msgUnsupported">&missingPlugin;</html:div>
|
<html:div class="msg msgUnsupported">&missingPlugin;</html:div>
|
||||||
<html:div class="msg msgClickToPlay"><html:a class="clickToPlayLink" href="">&clickToPlayPlugin;</html:a></html:div>
|
<html:div class="msg msgClickToPlay">&clickToPlayPlugin;</html:div>
|
||||||
<html:div class="msg msgDisabled">&disabledPlugin;</html:div>
|
<html:div class="msg msgDisabled">&disabledPlugin;</html:div>
|
||||||
<html:div class="msg msgBlocked">&blockedPlugin.label;</html:div>
|
<html:div class="msg msgBlocked">&blockedPlugin.label;</html:div>
|
||||||
<html:div class="msg msgCrashed"><!-- set at runtime --></html:div>
|
<html:div class="msg msgCrashed"><!-- set at runtime --></html:div>
|
||||||
|
|
|
@ -542,6 +542,10 @@ MAKEFILES_profiler="
|
||||||
tools/profiler/Makefile
|
tools/profiler/Makefile
|
||||||
"
|
"
|
||||||
|
|
||||||
|
MAKEFILES_snappy="
|
||||||
|
other-licenses/snappy/Makefile
|
||||||
|
"
|
||||||
|
|
||||||
add_makefiles "
|
add_makefiles "
|
||||||
$MAKEFILES_dom
|
$MAKEFILES_dom
|
||||||
$MAKEFILES_editor
|
$MAKEFILES_editor
|
||||||
|
@ -577,6 +581,7 @@ add_makefiles "
|
||||||
$MAKEFILES_hal
|
$MAKEFILES_hal
|
||||||
$MAKEFILES_psm_public
|
$MAKEFILES_psm_public
|
||||||
$MAKEFILES_profiler
|
$MAKEFILES_profiler
|
||||||
|
$MAKEFILES_snappy
|
||||||
"
|
"
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -926,6 +931,7 @@ if [ "$ENABLE_TESTS" ]; then
|
||||||
accessible/tests/mochitest/actions/Makefile
|
accessible/tests/mochitest/actions/Makefile
|
||||||
accessible/tests/mochitest/attributes/Makefile
|
accessible/tests/mochitest/attributes/Makefile
|
||||||
accessible/tests/mochitest/editabletext/Makefile
|
accessible/tests/mochitest/editabletext/Makefile
|
||||||
|
accessible/tests/mochitest/elm/Makefile
|
||||||
accessible/tests/mochitest/events/Makefile
|
accessible/tests/mochitest/events/Makefile
|
||||||
accessible/tests/mochitest/focus/Makefile
|
accessible/tests/mochitest/focus/Makefile
|
||||||
accessible/tests/mochitest/hyperlink/Makefile
|
accessible/tests/mochitest/hyperlink/Makefile
|
||||||
|
@ -1144,6 +1150,14 @@ if [ "$MOZ_ANGLE" ]; then
|
||||||
"
|
"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$MOZ_B2G_RIL" ]; then
|
||||||
|
add_makefiles "
|
||||||
|
dom/system/b2g/Makefile
|
||||||
|
dom/telephony/Makefile
|
||||||
|
ipc/ril/Makefile
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$MOZ_CRASHREPORTER" ]; then
|
if [ "$MOZ_CRASHREPORTER" ]; then
|
||||||
add_makefiles "
|
add_makefiles "
|
||||||
toolkit/crashreporter/Makefile
|
toolkit/crashreporter/Makefile
|
||||||
|
|