Merge mozilla-central to mozilla-inbound
|
@ -52,10 +52,10 @@ TEST_FILES = \
|
|||
exceptions_in_success_events_iframe.html \
|
||||
helpers.js \
|
||||
leaving_page_iframe.html \
|
||||
test_add_put.html \
|
||||
test_add_twice_failure.html \
|
||||
test_advance.html \
|
||||
test_autoIncrement_indexes.html \
|
||||
test_bad_keypath.html \
|
||||
test_bfcache.html \
|
||||
test_clear.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;
|
||||
}
|
||||
|
||||
// Returns the image buffer size
|
||||
NS_IMETHODIMP nsBMPEncoder::GetImageBufferSize(PRUint32 *aOutputSize)
|
||||
// Returns the number of bytes in the image buffer used.
|
||||
// For a BMP file, this is all bytes in the buffer.
|
||||
NS_IMETHODIMP nsBMPEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aOutputSize);
|
||||
*aOutputSize = mImageBufferSize;
|
||||
|
|
|
@ -111,9 +111,10 @@ NS_IMETHODIMP nsICOEncoder::InitFromData(const PRUint8* aData,
|
|||
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
|
||||
nsICOEncoder::GetImageBufferSize(PRUint32 *aOutputSize)
|
||||
nsICOEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aOutputSize);
|
||||
*aOutputSize = mImageBufferSize;
|
||||
|
@ -147,16 +148,16 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
|
|||
aStride, aInputFormat, noParams);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUint32 imageBufferSize;
|
||||
mContainedEncoder->GetImageBufferSize(&imageBufferSize);
|
||||
PRUint32 PNGImageBufferSize;
|
||||
mContainedEncoder->GetImageBufferUsed(&PNGImageBufferSize);
|
||||
mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE +
|
||||
imageBufferSize;
|
||||
PNGImageBufferSize;
|
||||
mImageBufferStart = static_cast<PRUint8*>(moz_malloc(mImageBufferSize));
|
||||
if (!mImageBufferStart) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
mImageBufferCurr = mImageBufferStart;
|
||||
mICODirEntry.mBytesInRes = imageBufferSize;
|
||||
mICODirEntry.mBytesInRes = PNGImageBufferSize;
|
||||
|
||||
EncodeFileHeader();
|
||||
EncodeInfoHeader();
|
||||
|
@ -164,8 +165,8 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
|
|||
char *imageBuffer;
|
||||
rv = mContainedEncoder->GetImageBuffer(&imageBuffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
memcpy(mImageBufferCurr, imageBuffer, imageBufferSize);
|
||||
mImageBufferCurr += imageBufferSize;
|
||||
memcpy(mImageBufferCurr, imageBuffer, PNGImageBufferSize);
|
||||
mImageBufferCurr += PNGImageBufferSize;
|
||||
} else {
|
||||
mContainedEncoder = new nsBMPEncoder();
|
||||
nsresult rv;
|
||||
|
@ -181,10 +182,10 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
|
|||
PRUint32 andMaskSize = ((GetRealWidth() + 31) / 32) * 4 * // row AND mask
|
||||
GetRealHeight(); // num rows
|
||||
|
||||
PRUint32 imageBufferSize;
|
||||
mContainedEncoder->GetImageBufferSize(&imageBufferSize);
|
||||
PRUint32 BMPImageBufferSize;
|
||||
mContainedEncoder->GetImageBufferUsed(&BMPImageBufferSize);
|
||||
mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE +
|
||||
imageBufferSize + andMaskSize;
|
||||
BMPImageBufferSize + andMaskSize;
|
||||
mImageBufferStart = static_cast<PRUint8*>(moz_malloc(mImageBufferSize));
|
||||
if (!mImageBufferStart) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -192,7 +193,7 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
|
|||
mImageBufferCurr = mImageBufferStart;
|
||||
|
||||
// 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
|
||||
EncodeFileHeader();
|
||||
|
@ -202,13 +203,13 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
|
|||
rv = mContainedEncoder->GetImageBuffer(&imageBuffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
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
|
||||
PRUint32 fixedHeight = GetRealHeight() * 2;
|
||||
fixedHeight = NATIVE32_TO_LITTLE(fixedHeight);
|
||||
// The height is stored at an offset of 8 from the DIB header
|
||||
memcpy(mImageBufferCurr + 8, &fixedHeight, sizeof(fixedHeight));
|
||||
mImageBufferCurr += imageBufferSize - BFH_LENGTH;
|
||||
mImageBufferCurr += BMPImageBufferSize - BFH_LENGTH;
|
||||
|
||||
// Calculate rowsize in DWORD's
|
||||
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;
|
||||
}
|
||||
|
||||
// Returns the image buffer size
|
||||
NS_IMETHODIMP nsJPEGEncoder::GetImageBufferSize(PRUint32 *aOutputSize)
|
||||
// Returns the number of bytes in the image buffer used.
|
||||
NS_IMETHODIMP nsJPEGEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aOutputSize);
|
||||
*aOutputSize = mImageBufferSize;
|
||||
*aOutputSize = mImageBufferUsed;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -206,11 +206,11 @@ NS_IMETHODIMP nsPNGEncoder::StartImageEncode(PRUint32 aWidth,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// Returns the image buffer size
|
||||
NS_IMETHODIMP nsPNGEncoder::GetImageBufferSize(PRUint32 *aOutputSize)
|
||||
// Returns the number of bytes in the image buffer used.
|
||||
NS_IMETHODIMP nsPNGEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aOutputSize);
|
||||
*aOutputSize = mImageBufferSize;
|
||||
*aOutputSize = mImageBufferUsed;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
/**
|
||||
* imgIEncoder interface
|
||||
*/
|
||||
[scriptable, uuid(d02e2d95-072d-4b91-8b8f-1300e45fcb2b)]
|
||||
[scriptable, uuid(4baa2d6e-fee7-42df-ae3f-5fbebc0c267c)]
|
||||
interface imgIEncoder : nsIAsyncInputStream
|
||||
{
|
||||
// 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
|
||||
* 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();
|
||||
};
|
||||
|
|
|
@ -2788,7 +2788,8 @@ EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool callContex
|
|||
if (op == JSOP_ARGUMENTS || op == JSOP_CALLEE) {
|
||||
if (Emit1(cx, bce, op) < 0)
|
||||
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;
|
||||
} else {
|
||||
if (!pn->pn_cookie.isFree()) {
|
||||
|
@ -6075,7 +6076,8 @@ EmitReturn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
if (!EmitTree(cx, bce, pn2))
|
||||
return false;
|
||||
} else {
|
||||
if (Emit1(cx, bce, JSOP_PUSH) < 0)
|
||||
/* No explicit return value provided */
|
||||
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
|
||||
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.
|
||||
*
|
||||
* Then (or in a call case that has no explicit reference-base
|
||||
* object) we emit JSOP_PUSH to produce the |this| slot required
|
||||
* for calls (which non-strict mode functions will box into the
|
||||
* global object).
|
||||
* object) we emit JSOP_UNDEFINED to produce the undefined |this|
|
||||
* value required for calls (which non-strict mode functions
|
||||
* will box into the global object).
|
||||
*/
|
||||
ParseNode *pn2 = pn->pn_head;
|
||||
switch (pn2->getKind()) {
|
||||
|
@ -6379,16 +6381,16 @@ EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
|||
JS_ASSERT(pn2->isOp(JSOP_XMLNAME));
|
||||
if (!EmitXMLName(cx, pn2, JSOP_CALLXMLNAME, bce))
|
||||
return false;
|
||||
callop = true; /* suppress JSOP_PUSH after */
|
||||
callop = true; /* suppress JSOP_UNDEFINED after */
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
if (!EmitTree(cx, bce, pn2))
|
||||
return false;
|
||||
callop = false; /* trigger JSOP_PUSH after */
|
||||
callop = false; /* trigger JSOP_UNDEFINED after */
|
||||
break;
|
||||
}
|
||||
if (!callop && Emit1(cx, bce, JSOP_PUSH) < 0)
|
||||
if (!callop && Emit1(cx, bce, JSOP_UNDEFINED) < 0)
|
||||
return false;
|
||||
|
||||
/* 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))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
if (Emit1(cx, bce, JSOP_PUSH) < 0)
|
||||
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
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 *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
@ -102,8 +102,7 @@ js_InitArrayClass(JSContext *cx, JSObject *obj);
|
|||
extern bool
|
||||
js_InitContextBusyArrayTable(JSContext *cx);
|
||||
|
||||
namespace js
|
||||
{
|
||||
namespace js {
|
||||
|
||||
/* Create a dense array with no capacity allocated, length set to 0. */
|
||||
extern JSObject * JS_FASTCALL
|
||||
|
@ -136,7 +135,7 @@ NewDenseCopiedArray(JSContext *cx, uint32 length, const Value *vp, JSObject *pro
|
|||
extern JSObject *
|
||||
NewSlowEmptyArray(JSContext *cx);
|
||||
|
||||
}
|
||||
} /* namespace js */
|
||||
|
||||
extern JSBool
|
||||
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
|
||||
GetElements(JSContext *cx, JSObject *aobj, jsuint length, js::Value *vp);
|
||||
|
||||
}
|
||||
|
||||
/* Natives exposed for optimization by the interpreter and JITs. */
|
||||
namespace js {
|
||||
|
||||
extern JSBool
|
||||
array_sort(JSContext *cx, uintN argc, js::Value *vp);
|
||||
|
@ -213,7 +209,4 @@ js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id,
|
|||
JSBool
|
||||
js_Array(JSContext *cx, uintN argc, js::Value *vp);
|
||||
|
||||
extern JSBool JS_FASTCALL
|
||||
js_EnsureDenseArrayCapacity(JSContext *cx, JSObject *obj, jsint i);
|
||||
|
||||
#endif /* jsarray_h___ */
|
||||
|
|
|
@ -697,15 +697,6 @@ CreateEvalCallObject(JSContext *cx, StackFrame *fp)
|
|||
|
||||
} // 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
|
||||
js_PutCallObject(StackFrame *fp)
|
||||
{
|
||||
|
|
|
@ -312,9 +312,6 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
|
|||
uintN flags, JSObject *parent, JSAtom *atom,
|
||||
js::gc::AllocKind kind = JSFunction::FinalizeKind);
|
||||
|
||||
extern void
|
||||
js_FinalizeFunction(JSContext *cx, JSFunction *fun);
|
||||
|
||||
extern JSFunction * JS_FASTCALL
|
||||
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto,
|
||||
js::gc::AllocKind kind = JSFunction::FinalizeKind);
|
||||
|
@ -345,16 +342,9 @@ js_ValueToCallableObject(JSContext *cx, js::Value *vp, uintN flags);
|
|||
extern void
|
||||
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
|
||||
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 {
|
||||
|
||||
CallObject *
|
||||
|
|
|
@ -3421,7 +3421,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
|
|||
|
||||
/* Bytecodes pushing values of known type. */
|
||||
case JSOP_VOID:
|
||||
case JSOP_PUSH:
|
||||
case JSOP_UNDEFINED:
|
||||
pushed[0].addType(cx, Type::UndefinedType());
|
||||
break;
|
||||
case JSOP_ZERO:
|
||||
|
|
|
@ -1995,9 +1995,9 @@ END_CASE(JSOP_LINENO)
|
|||
BEGIN_CASE(JSOP_BLOCKCHAIN)
|
||||
END_CASE(JSOP_BLOCKCHAIN)
|
||||
|
||||
BEGIN_CASE(JSOP_PUSH)
|
||||
BEGIN_CASE(JSOP_UNDEFINED)
|
||||
PUSH_UNDEFINED();
|
||||
END_CASE(JSOP_PUSH)
|
||||
END_CASE(JSOP_UNDEFINED)
|
||||
|
||||
BEGIN_CASE(JSOP_POP)
|
||||
regs.sp--;
|
||||
|
|
|
@ -1191,7 +1191,7 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
|
|||
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
|
||||
* isn't JSON, JSON parsing will probably fail quickly, so little time
|
||||
* 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.
|
||||
*/
|
||||
if (length > 2 &&
|
||||
chars[0] == '(' && chars[length - 1] == ')' &&
|
||||
(!caller || !caller->script()->strictModeCode))
|
||||
((chars[0] == '[' && chars[length - 1] == ']') ||
|
||||
(chars[0] == '(' && chars[length - 1] == ')')) &&
|
||||
(!caller || !caller->script()->strictModeCode))
|
||||
{
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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);
|
||||
Value tmp;
|
||||
if (!parser.parse(&tmp))
|
||||
|
|
|
@ -1917,7 +1917,7 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
|
|||
const char *rval;
|
||||
|
||||
LOAD_OP_DATA(pc);
|
||||
LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL);
|
||||
LOCAL_ASSERT(op == JSOP_GETLOCAL);
|
||||
|
||||
todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
|
||||
if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
|
||||
|
@ -1934,7 +1934,7 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
|
|||
if (pc == endpc)
|
||||
return pc;
|
||||
LOAD_OP_DATA(pc);
|
||||
if (op != JSOP_PUSH && op != JSOP_GETLOCAL)
|
||||
if (op != JSOP_GETLOCAL)
|
||||
break;
|
||||
if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
|
||||
return NULL;
|
||||
|
@ -2548,20 +2548,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
}
|
||||
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_BINDGNAME:
|
||||
todo = Sprint(&ss->sprinter, "");
|
||||
|
@ -4193,8 +4179,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
LOCAL_ASSERT(*pc == JSOP_NULLBLOCKCHAIN);
|
||||
pc += JSOP_NULLBLOCKCHAIN_LENGTH;
|
||||
}
|
||||
LOCAL_ASSERT(*pc == JSOP_PUSH);
|
||||
pc += JSOP_PUSH_LENGTH;
|
||||
LOCAL_ASSERT(*pc == JSOP_UNDEFINED);
|
||||
pc += JSOP_UNDEFINED_LENGTH;
|
||||
LOCAL_ASSERT(*pc == JSOP_CALL);
|
||||
LOCAL_ASSERT(GET_ARGC(pc) == 0);
|
||||
len = JSOP_CALL_LENGTH;
|
||||
|
@ -5225,10 +5211,6 @@ DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
|
|||
JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX &&
|
||||
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
|
||||
* its keyword name instead.
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/* 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_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE|JOF_PARENHEAD)
|
||||
OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE)
|
||||
|
|
|
@ -1681,9 +1681,9 @@ mjit::Compiler::generateMethod()
|
|||
BEGIN_CASE(JSOP_NOP)
|
||||
END_CASE(JSOP_NOP)
|
||||
|
||||
BEGIN_CASE(JSOP_PUSH)
|
||||
BEGIN_CASE(JSOP_UNDEFINED)
|
||||
frame.push(UndefinedValue());
|
||||
END_CASE(JSOP_PUSH)
|
||||
END_CASE(JSOP_UNDEFINED)
|
||||
|
||||
BEGIN_CASE(JSOP_POPV)
|
||||
BEGIN_CASE(JSOP_SETRVAL)
|
||||
|
|
|
@ -581,7 +581,7 @@ nsSVGPatternFrame::ConstructCTM(const gfxRect &callerBBox,
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
После Ширина: | Высота: | Размер: 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>
|
После Ширина: | Высота: | Размер: 730 B |
|
@ -119,3 +119,5 @@ load 682411-1.svg
|
|||
load 692203-1.svg
|
||||
load 692203-2.svg
|
||||
load 693424-1.svg
|
||||
load 709920-1.svg
|
||||
load 709920-2.svg
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Brad Lassey <blassey@mozilla.com>
|
||||
* Lucas Rocha <lucasr@mozilla.com>
|
||||
*
|
||||
* 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
|
||||
|
@ -37,120 +38,183 @@
|
|||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.widget.*;
|
||||
import android.database.*;
|
||||
import android.view.*;
|
||||
import android.graphics.*;
|
||||
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 java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
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 void callback(String uriSpec);
|
||||
}
|
||||
|
||||
UriLoadCallback mUriLoadCallback = null;
|
||||
|
||||
void setUriLoadCallback(UriLoadCallback uriLoadCallback) {
|
||||
mUriLoadCallback = uriLoadCallback;
|
||||
}
|
||||
|
||||
public AboutHomeContent(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mInflated = false;
|
||||
}
|
||||
|
||||
private static final String LOGTAG = "GeckoAboutHome";
|
||||
private static final String TITLE_KEY = "title";
|
||||
private static final int NUMBER_OF_TOP_SITES = 3;
|
||||
private static final int kTileWidth = 122;
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
private Cursor mCursor;
|
||||
private Uri mUri;
|
||||
private String mTitle;
|
||||
|
||||
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)
|
||||
// HACK: Without this, the onFinishInflate is called twice
|
||||
// This issue is due to a bug when Android inflates a layout with a
|
||||
// parent. Fixed in Honeycomb
|
||||
if (mInflated)
|
||||
return;
|
||||
|
||||
mGrid.setOnItemClickListener(mGridOnClickListener);
|
||||
mInflated = true;
|
||||
|
||||
// we want to do this: mGrid.setNumColumns(GridView.AUTO_FIT); but it doesn't work
|
||||
Display display = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
int width = display.getWidth();
|
||||
mGrid.setNumColumns((int) Math.floor(width / kTileWidth));
|
||||
mAddonList = (ListView)findViewById(R.id.recommended_addon_list);
|
||||
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
mGrid.setAdapter(mGridAdapter);
|
||||
mTopSitesGrid = (GridView)findViewById(R.id.top_sites_grid);
|
||||
mTopSitesGrid.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||
Cursor c = (Cursor) parent.getItemAtPosition(position);
|
||||
|
||||
String spec = c.getString(c.getColumnIndex(URLColumns.URL));
|
||||
Log.i(LOGTAG, "clicked: " + spec);
|
||||
|
||||
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() {
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id)
|
||||
{
|
||||
onGridItemClick((GridView)parent, v, position, id);
|
||||
}
|
||||
};
|
||||
private int getNumberOfColumns() {
|
||||
Configuration config = getContext().getResources().getConfiguration();
|
||||
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE)
|
||||
return NUMBER_OF_COLS_LANDSCAPE;
|
||||
else
|
||||
return NUMBER_OF_COLS_PORTRAIT;
|
||||
}
|
||||
|
||||
void init(final Activity activity) {
|
||||
GeckoAppShell.getHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
if (mCursor != null)
|
||||
activity.stopManagingCursor(mCursor);
|
||||
|
||||
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);
|
||||
|
||||
onActivityContentChanged(activity);
|
||||
mAddonAdapter = new ArrayAdapter<String>(activity, R.layout.abouthome_addon_list_item);
|
||||
mTopSitesAdapter = new TopSitesCursorAdapter(activity,
|
||||
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() {
|
||||
public void run() {
|
||||
final SimpleCursorAdapter gridAdapter =
|
||||
new SimpleCursorAdapter(activity, R.layout.abouthome_grid_box, mCursor,
|
||||
new String[] { URLColumns.TITLE,
|
||||
URLColumns.FAVICON,
|
||||
URLColumns.URL,
|
||||
URLColumns.THUMBNAIL },
|
||||
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);
|
||||
mTopSitesGrid.setNumColumns(getNumberOfColumns());
|
||||
|
||||
mTopSitesGrid.setAdapter(mTopSitesAdapter);
|
||||
mTopSitesAdapter.setViewBinder(new TopSitesViewBinder());
|
||||
|
||||
mAddonsList.setAdapter(mAddonsAdapter);
|
||||
}
|
||||
});
|
||||
|
||||
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() {
|
||||
try {
|
||||
File profileDir = GeckoApp.mAppContext.getProfileDir();
|
||||
|
@ -172,7 +236,11 @@ public class AboutHomeContent extends LinearLayout {
|
|||
return is;
|
||||
File applicationPackage = new File(activity.getApplication().getPackageResourcePath());
|
||||
ZipFile zip = new ZipFile(applicationPackage);
|
||||
if (zip == null)
|
||||
return null;
|
||||
ZipEntry fileEntry = zip.getEntry("recommended-addons.json");
|
||||
if (fileEntry == null)
|
||||
return null;
|
||||
return zip.getInputStream(fileEntry);
|
||||
}
|
||||
|
||||
|
@ -182,6 +250,8 @@ public class AboutHomeContent extends LinearLayout {
|
|||
try {
|
||||
byte[] buf = new byte[32768];
|
||||
InputStream fileStream = getRecommendedAddonsStream(activity);
|
||||
if (fileStream == null)
|
||||
return;
|
||||
StringBuffer jsonString = new StringBuffer();
|
||||
int read = 0;
|
||||
while ((read = fileStream.read(buf, 0, 32768)) != -1) {
|
||||
|
@ -193,7 +263,7 @@ public class AboutHomeContent extends LinearLayout {
|
|||
try {
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
JSONObject jsonobj = array.getJSONObject(i);
|
||||
mAddonAdapter.add(jsonobj.getString("name"));
|
||||
mAddonsAdapter.add(jsonobj.getString("name"));
|
||||
Log.i("GeckoAddons", "addon #" + i +": " + jsonobj.getString("name"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -208,91 +278,111 @@ public class AboutHomeContent extends LinearLayout {
|
|||
});
|
||||
}
|
||||
|
||||
protected void onGridItemClick(GridView l, View v, int position, long id) {
|
||||
mCursor.moveToPosition(position);
|
||||
String spec = mCursor.getString(mCursor.getColumnIndex(URLColumns.URL));
|
||||
Log.i(LOGTAG, "clicked: " + spec);
|
||||
if (mUriLoadCallback != null)
|
||||
mUriLoadCallback.callback(spec);
|
||||
public static class TopSitesGridView extends GridView {
|
||||
public TopSitesGridView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
class AwesomeCursorViewBinder implements SimpleCursorAdapter.ViewBinder {
|
||||
private static final String LOGTAG = "GeckoAwesomeCursorViewBinder";
|
||||
public class TopSitesCursorAdapter extends SimpleCursorAdapter {
|
||||
public TopSitesCursorAdapter(Context context, int layout, Cursor c,
|
||||
String[] from, int[] to) {
|
||||
super(context, layout, c, from, to);
|
||||
}
|
||||
|
||||
private boolean updateImage(View view, Cursor cursor, int faviconIndex) {
|
||||
byte[] b = cursor.getBlob(faviconIndex);
|
||||
ImageView favicon = (ImageView) view;
|
||||
@Override
|
||||
public int getCount() {
|
||||
return Math.min(super.getCount(), getNumberOfTopSites());
|
||||
}
|
||||
}
|
||||
|
||||
if (b == null) {
|
||||
favicon.setImageResource(R.drawable.favicon);
|
||||
} else {
|
||||
try {
|
||||
Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
|
||||
favicon.setImageBitmap(bitmap);
|
||||
} catch (OutOfMemoryError oom) {
|
||||
Log.e(LOGTAG, "Unable to load thumbnail bitmap", oom);
|
||||
favicon.setImageResource(R.drawable.favicon);
|
||||
class TopSitesViewBinder implements SimpleCursorAdapter.ViewBinder {
|
||||
private boolean updateThumbnail(View view, Cursor cursor, int thumbIndex) {
|
||||
byte[] b = cursor.getBlob(thumbIndex);
|
||||
ImageView thumbnail = (ImageView) view;
|
||||
|
||||
if (b == null) {
|
||||
thumbnail.setImageResource(R.drawable.abouthome_topsite_placeholder);
|
||||
} else {
|
||||
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) {
|
||||
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);
|
||||
public static class AddonsListView extends ListView {
|
||||
public AddonsListView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
titleView.setText(title);
|
||||
return true;
|
||||
@Override
|
||||
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) {
|
||||
String title = cursor.getString(urlIndex);
|
||||
TextView urlView = (TextView)view;
|
||||
if (title != null) {
|
||||
int index;
|
||||
if ((index = title.indexOf("://")) != -1)
|
||||
title = title.substring(index + 3);
|
||||
if (title.startsWith("www."))
|
||||
title = title.substring(4);
|
||||
if (title.endsWith("/"))
|
||||
title = title.substring(0, title.length() -1);
|
||||
public static class LinkTextView extends TextView {
|
||||
public LinkTextView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(CharSequence text, BufferType type) {
|
||||
SpannableString content = new SpannableString(text + " \u00BB");
|
||||
content.setSpan(new UnderlineSpan(), 0, text.length(), 0);
|
||||
|
||||
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"/>
|
||||
|
||||
<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"
|
||||
android:theme="@style/Gecko.TitleBar"
|
||||
|
|
|
@ -84,7 +84,7 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
|
|||
mAwesomeTabs = (AwesomeBarTabs) findViewById(R.id.awesomebar_tabs);
|
||||
mAwesomeTabs.setOnUrlOpenListener(new AwesomeBarTabs.OnUrlOpenListener() {
|
||||
public void onUrlOpen(String url) {
|
||||
openUrlAndFinish(url);
|
||||
submitAndFinish(url);
|
||||
}
|
||||
|
||||
public void onSearch(String engine) {
|
||||
|
@ -95,7 +95,7 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
|
|||
mGoButton = (ImageButton) findViewById(R.id.awesomebar_button);
|
||||
mGoButton.setOnClickListener(new Button.OnClickListener() {
|
||||
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)
|
||||
return true;
|
||||
|
||||
openUrlAndFinish(mText.getText().toString());
|
||||
submitAndFinish(mText.getText().toString());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -209,6 +209,34 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
|
|||
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) {
|
||||
if (text.length() == 0) {
|
||||
mGoButton.setVisibility(View.GONE);
|
||||
|
@ -218,13 +246,19 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
|
|||
mGoButton.setVisibility(View.VISIBLE);
|
||||
|
||||
int imageResource = R.drawable.ic_awesomebar_go;
|
||||
if (!GeckoAppShell.canCreateFixupURI(text)) {
|
||||
if (isSearchUrl(text))
|
||||
imageResource = R.drawable.ic_awesomebar_search;
|
||||
}
|
||||
|
||||
mGoButton.setImageResource(imageResource);
|
||||
}
|
||||
|
||||
private void submitAndFinish(String url) {
|
||||
if (isSearchUrl(url))
|
||||
openSearchAndFinish(url, "__default__");
|
||||
else
|
||||
openUrlAndFinish(url);
|
||||
}
|
||||
|
||||
private void cancelAndFinish() {
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
|
|
|
@ -456,11 +456,13 @@ abstract public class GeckoApp
|
|||
MenuItem forward = aMenu.findItem(R.id.forward);
|
||||
MenuItem share = aMenu.findItem(R.id.share);
|
||||
MenuItem agentMode = aMenu.findItem(R.id.agent_mode);
|
||||
MenuItem saveAsPDF = aMenu.findItem(R.id.save_as_pdf);
|
||||
|
||||
if (tab == null) {
|
||||
bookmark.setEnabled(false);
|
||||
forward.setEnabled(false);
|
||||
share.setEnabled(false);
|
||||
saveAsPDF.setEnabled(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -481,7 +483,11 @@ abstract public class GeckoApp
|
|||
|
||||
// Don't share about:, chrome: and file: URIs
|
||||
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;
|
||||
}
|
||||
|
@ -667,7 +673,8 @@ abstract public class GeckoApp
|
|||
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);
|
||||
if (tab == null)
|
||||
return;
|
||||
|
@ -681,6 +688,8 @@ abstract public class GeckoApp
|
|||
|
||||
String oldBaseURI = tab.getURL();
|
||||
tab.updateURL(uri);
|
||||
tab.setDocumentURI(documentURI);
|
||||
tab.setContentType(contentType);
|
||||
|
||||
String baseURI = uri;
|
||||
if (baseURI.indexOf('#') != -1)
|
||||
|
@ -848,8 +857,10 @@ abstract public class GeckoApp
|
|||
} else if (event.equals("Content:LocationChange")) {
|
||||
final int tabId = message.getInt("tabID");
|
||||
final String uri = message.getString("uri");
|
||||
final String documentURI = message.getString("documentURI");
|
||||
final String contentType = message.getString("contentType");
|
||||
Log.i(LOGTAG, "URI - " + uri);
|
||||
handleLocationChange(tabId, uri);
|
||||
handleLocationChange(tabId, uri, documentURI, contentType);
|
||||
} else if (event.equals("Content:SecurityChange")) {
|
||||
final int tabId = message.getInt("tabID");
|
||||
final String mode = message.getString("mode");
|
||||
|
@ -902,7 +913,11 @@ abstract public class GeckoApp
|
|||
setLaunchState(GeckoApp.LaunchState.GeckoRunning);
|
||||
GeckoAppShell.sendPendingEventsToGecko();
|
||||
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")) {
|
||||
mMainHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
|
@ -1436,7 +1451,6 @@ abstract public class GeckoApp
|
|||
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.penaltyDialog()
|
||||
.build());
|
||||
|
||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
||||
|
|
|
@ -130,7 +130,6 @@ public class GeckoAppShell
|
|||
public static native void onChangeNetworkLinkStatus(String status);
|
||||
public static native void reportJavaCrash(String stack);
|
||||
public static native void notifyUriVisited(String uri);
|
||||
public static native boolean canCreateFixupURI(String text);
|
||||
|
||||
public static native void processNextNativeEvent();
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import java.lang.CharSequence;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.os.Build;
|
||||
|
@ -131,8 +132,12 @@ public class GeckoPreferences
|
|||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
String prefName = preference.getKey();
|
||||
setPreference(prefName, newValue);
|
||||
if (preference instanceof ListPreference)
|
||||
((ListPreference)preference).setSummary((String)newValue);
|
||||
if (preference instanceof ListPreference) {
|
||||
// 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)
|
||||
finish();
|
||||
return true;
|
||||
|
@ -177,6 +182,9 @@ public class GeckoPreferences
|
|||
GeckoAppShell.getMainHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
((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/select_dialog_list.xml \
|
||||
res/layout/abouthome_content.xml \
|
||||
res/layout/abouthome_grid_box.xml \
|
||||
res/layout/abouthome_addon_list_item.xml \
|
||||
res/layout/abouthome_topsite_item.xml \
|
||||
res/layout/abouthome_addon_row.xml \
|
||||
$(NULL)
|
||||
|
||||
RES_LAYOUT_V11 = \
|
||||
|
@ -206,6 +206,7 @@ RES_LAYOUT_V11 = \
|
|||
|
||||
RES_VALUES = \
|
||||
res/values/defaults.xml \
|
||||
res/values/arrays.xml \
|
||||
res/values/colors.xml \
|
||||
res/values/styles.xml \
|
||||
res/values/themes.xml \
|
||||
|
@ -224,6 +225,11 @@ RES_ANIM = \
|
|||
$(NULL)
|
||||
|
||||
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_search.png \
|
||||
res/drawable-mdpi-v8/ic_menu_bookmark_add.png \
|
||||
|
@ -246,6 +252,11 @@ RES_DRAWABLE_MDPI_V8 = \
|
|||
$(NULL)
|
||||
|
||||
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_search.png \
|
||||
res/drawable-hdpi-v8/ic_menu_bookmark_add.png \
|
||||
|
@ -318,6 +329,11 @@ RES_DRAWABLE_HDPI_V11 = \
|
|||
$(NULL)
|
||||
|
||||
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_search.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
|
||||
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_pressed.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/checkerboard.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)
|
||||
|
||||
|
||||
|
@ -436,6 +454,7 @@ $(RES_VALUES): \
|
|||
$(srcdir)/resources/values/colors.xml \
|
||||
$(srcdir)/resources/values/styles.xml \
|
||||
$(srcdir)/resources/values/themes.xml \
|
||||
$(srcdir)/resources/values/arrays.xml \
|
||||
$(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/res/values/defaults.xml
|
||||
$(NSINSTALL) -D res/values
|
||||
$(NSINSTALL) $^ res/values
|
||||
|
|
|
@ -75,6 +75,8 @@ public class Tab {
|
|||
private HashMap<String, DoorHanger> mDoorHangers;
|
||||
private long mFaviconLoadId;
|
||||
private AgentMode mAgentMode = AgentMode.MOBILE;
|
||||
private String mDocumentURI;
|
||||
private String mContentType;
|
||||
|
||||
static class HistoryEntry {
|
||||
public final String mUri; // must never be null
|
||||
|
@ -103,6 +105,8 @@ public class Tab {
|
|||
mBookmark = false;
|
||||
mDoorHangers = new HashMap<String, DoorHanger>();
|
||||
mFaviconLoadId = 0;
|
||||
mDocumentURI = "";
|
||||
mContentType = "";
|
||||
}
|
||||
|
||||
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) {
|
||||
mTitle = (title == null ? "" : title);
|
||||
|
||||
|
|
|
@ -93,6 +93,13 @@ public class BrowserContract {
|
|||
public static final class Bookmarks implements CommonColumns, URLColumns, ImageColumns, SyncColumns {
|
||||
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 String CONTENT_TYPE = "vnd.android.cursor.dir/bookmark";
|
||||
|
@ -133,4 +140,4 @@ public class BrowserContract {
|
|||
|
||||
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_IMAGES = "images";
|
||||
|
||||
static final String VIEW_BOOKMARKS_WITH_IMAGES = "bookmarks_with_images";
|
||||
static final String VIEW_HISTORY_WITH_IMAGES = "history_with_images";
|
||||
|
||||
// Bookmark matches
|
||||
static final int BOOKMARKS = 100;
|
||||
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 TABLE_BOOKMARKS_JOIN_IMAGES = TABLE_BOOKMARKS + " LEFT OUTER JOIN " +
|
||||
TABLE_IMAGES + " ON " + qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks.URL) +
|
||||
" = " + qualifyColumnValue(TABLE_IMAGES, Images.URL);
|
||||
"(SELECT " + Images.URL + ", " + Images.FAVICON + ", " + Images.THUMBNAIL + " FROM " +
|
||||
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 " +
|
||||
TABLE_IMAGES + " ON " + qualifyColumnValue(TABLE_HISTORY, History.URL) +
|
||||
" = " + qualifyColumnValue(TABLE_IMAGES, Images.URL);
|
||||
"(SELECT " + Images.URL + ", " + Images.FAVICON + ", " + Images.THUMBNAIL + " FROM " +
|
||||
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);
|
||||
|
||||
|
@ -131,7 +142,7 @@ public class BrowserProvider extends ContentProvider {
|
|||
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "bookmarks/folder/#", BOOKMARKS_FOLDER_ID);
|
||||
|
||||
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.URL, Bookmarks.URL);
|
||||
map.put(Bookmarks.FAVICON, Bookmarks.FAVICON);
|
||||
|
@ -142,41 +153,41 @@ public class BrowserProvider extends ContentProvider {
|
|||
map.put(Bookmarks.TAGS, Bookmarks.TAGS);
|
||||
map.put(Bookmarks.DESCRIPTION, Bookmarks.DESCRIPTION);
|
||||
map.put(Bookmarks.KEYWORD, Bookmarks.KEYWORD);
|
||||
map.put(Bookmarks.DATE_CREATED, qualifyColumn(TABLE_BOOKMARKS, Bookmarks.DATE_CREATED));
|
||||
map.put(Bookmarks.DATE_MODIFIED, qualifyColumn(TABLE_BOOKMARKS, Bookmarks.DATE_MODIFIED));
|
||||
map.put(Bookmarks.GUID, qualifyColumn(TABLE_BOOKMARKS, Bookmarks.GUID));
|
||||
map.put(Bookmarks.IS_DELETED, qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED));
|
||||
map.put(Bookmarks.DATE_CREATED, Bookmarks.DATE_CREATED);
|
||||
map.put(Bookmarks.DATE_MODIFIED, Bookmarks.DATE_MODIFIED);
|
||||
map.put(Bookmarks.GUID, Bookmarks.GUID);
|
||||
map.put(Bookmarks.IS_DELETED, Bookmarks.IS_DELETED);
|
||||
|
||||
// History
|
||||
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "history", HISTORY);
|
||||
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "history/#", HISTORY_ID);
|
||||
|
||||
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.URL, History.URL);
|
||||
map.put(History.FAVICON, History.FAVICON);
|
||||
map.put(History.THUMBNAIL, History.THUMBNAIL);
|
||||
map.put(History.VISITS, History.VISITS);
|
||||
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_MODIFIED, qualifyColumn(TABLE_HISTORY, History.DATE_MODIFIED));
|
||||
map.put(History.GUID, qualifyColumn(TABLE_HISTORY, History.GUID));
|
||||
map.put(History.IS_DELETED, qualifyColumn(TABLE_HISTORY, History.IS_DELETED));
|
||||
map.put(History.DATE_CREATED, History.DATE_CREATED);
|
||||
map.put(History.DATE_MODIFIED, History.DATE_MODIFIED);
|
||||
map.put(History.GUID, History.GUID);
|
||||
map.put(History.IS_DELETED, History.IS_DELETED);
|
||||
|
||||
// Images
|
||||
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "images", IMAGES);
|
||||
|
||||
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.FAVICON, Images.FAVICON);
|
||||
map.put(Images.FAVICON_URL, Images.FAVICON_URL);
|
||||
map.put(Images.THUMBNAIL, Images.THUMBNAIL);
|
||||
map.put(Images.DATE_CREATED, qualifyColumn(TABLE_IMAGES, Images.DATE_CREATED));
|
||||
map.put(Images.DATE_MODIFIED, qualifyColumn(TABLE_IMAGES, Images.DATE_MODIFIED));
|
||||
map.put(Images.GUID, qualifyColumn(TABLE_IMAGES, Images.GUID));
|
||||
map.put(Images.IS_DELETED, qualifyColumn(TABLE_IMAGES, Images.IS_DELETED));
|
||||
map.put(Images.DATE_CREATED, Images.DATE_CREATED);
|
||||
map.put(Images.DATE_MODIFIED, Images.DATE_MODIFIED);
|
||||
map.put(Images.GUID, Images.GUID);
|
||||
map.put(Images.IS_DELETED, Images.IS_DELETED);
|
||||
|
||||
// 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) {
|
||||
return table + "." + column + " AS " + column;
|
||||
}
|
||||
|
||||
static final String qualifyColumnValue(String table, String column) {
|
||||
return table + "." + column;
|
||||
}
|
||||
|
||||
|
@ -239,6 +246,16 @@ public class BrowserProvider extends ContentProvider {
|
|||
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 {
|
||||
public DatabaseHelper(Context context, String databasePath) {
|
||||
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 + "("
|
||||
+ 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
|
||||
}
|
||||
|
||||
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
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
Log.d(LOGTAG, "Upgrading browser.db: " + db.getPath() + " from " +
|
||||
|
@ -428,11 +470,8 @@ public class BrowserProvider extends ContentProvider {
|
|||
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
String isDeletedColumn = qualifyColumnValue(tableName, SyncColumns.IS_DELETED);
|
||||
String dateModifiedColumn = qualifyColumnValue(tableName, SyncColumns.DATE_MODIFIED);
|
||||
|
||||
String selection = isDeletedColumn + " = 1 AND " +
|
||||
dateModifiedColumn + " <= " + (now - MAX_AGE_OF_DELETED_RECORDS);
|
||||
String selection = SyncColumns.IS_DELETED + " = 1 AND " +
|
||||
SyncColumns.DATE_MODIFIED + " <= " + (now - MAX_AGE_OF_DELETED_RECORDS);
|
||||
|
||||
cursor = query(uriWithArgs,
|
||||
new String[] { CommonColumns._ID },
|
||||
|
@ -801,22 +840,18 @@ public class BrowserProvider extends ContentProvider {
|
|||
|
||||
if (match == BOOKMARKS_ID) {
|
||||
Log.d(LOGTAG, "Query is BOOKMARKS_ID: " + uri);
|
||||
selection = concatenateWhere(selection,
|
||||
qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks._ID) + " = ?");
|
||||
selection = concatenateWhere(selection, Bookmarks._ID + " = ?");
|
||||
selectionArgs = appendSelectionArgs(selectionArgs,
|
||||
new String[] { Long.toString(ContentUris.parseId(uri)) });
|
||||
} else if (match == BOOKMARKS_FOLDER_ID) {
|
||||
Log.d(LOGTAG, "Query is BOOKMARKS_FOLDER_ID: " + uri);
|
||||
selection = concatenateWhere(selection,
|
||||
qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks.PARENT) + " = ?");
|
||||
selection = concatenateWhere(selection, Bookmarks.PARENT + " = ?");
|
||||
selectionArgs = appendSelectionArgs(selectionArgs,
|
||||
new String[] { Long.toString(ContentUris.parseId(uri)) });
|
||||
}
|
||||
|
||||
if (!shouldShowDeleted(uri)) {
|
||||
String isDeletedColumn = qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks.IS_DELETED);
|
||||
selection = concatenateWhere(isDeletedColumn + " = 0", selection);
|
||||
}
|
||||
if (!shouldShowDeleted(uri))
|
||||
selection = concatenateWhere(Bookmarks.IS_DELETED + " = 0", selection);
|
||||
|
||||
if (TextUtils.isEmpty(sortOrder)) {
|
||||
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.setTables(TABLE_BOOKMARKS_JOIN_IMAGES);
|
||||
|
||||
if (hasImagesInProjection(projection))
|
||||
qb.setTables(VIEW_BOOKMARKS_WITH_IMAGES);
|
||||
else
|
||||
qb.setTables(TABLE_BOOKMARKS);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -835,22 +874,23 @@ public class BrowserProvider extends ContentProvider {
|
|||
|
||||
if (match == HISTORY_ID) {
|
||||
Log.d(LOGTAG, "Query is HISTORY_ID: " + uri);
|
||||
selection = concatenateWhere(selection,
|
||||
qualifyColumnValue(TABLE_HISTORY, History._ID) + " = ?");
|
||||
selection = concatenateWhere(selection, History._ID + " = ?");
|
||||
selectionArgs = appendSelectionArgs(selectionArgs,
|
||||
new String[] { Long.toString(ContentUris.parseId(uri)) });
|
||||
}
|
||||
|
||||
if (!shouldShowDeleted(uri)) {
|
||||
String isDeletedColumn = qualifyColumnValue(TABLE_HISTORY, History.IS_DELETED);
|
||||
selection = concatenateWhere(isDeletedColumn + " = 0", selection);
|
||||
}
|
||||
if (!shouldShowDeleted(uri))
|
||||
selection = concatenateWhere(History.IS_DELETED + " = 0", selection);
|
||||
|
||||
if (TextUtils.isEmpty(sortOrder))
|
||||
sortOrder = DEFAULT_HISTORY_SORT_ORDER;
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -861,16 +901,13 @@ public class BrowserProvider extends ContentProvider {
|
|||
|
||||
if (match == IMAGES_ID) {
|
||||
Log.d(LOGTAG, "Query is IMAGES_ID: " + uri);
|
||||
selection = concatenateWhere(selection,
|
||||
qualifyColumnValue(TABLE_IMAGES, Images._ID) + " = ?");
|
||||
selection = concatenateWhere(selection, Images._ID + " = ?");
|
||||
selectionArgs = appendSelectionArgs(selectionArgs,
|
||||
new String[] { Long.toString(ContentUris.parseId(uri)) });
|
||||
}
|
||||
|
||||
if (!shouldShowDeleted(uri)) {
|
||||
String isDeletedColumn = qualifyColumnValue(TABLE_IMAGES, Images.IS_DELETED);
|
||||
selection = concatenateWhere(isDeletedColumn + " = 0", selection);
|
||||
}
|
||||
if (!shouldShowDeleted(uri))
|
||||
selection = concatenateWhere(Images.IS_DELETED + " = 0", selection);
|
||||
|
||||
qb.setProjectionMap(IMAGES_PROJECTION_MAP);
|
||||
qb.setTables(TABLE_IMAGES);
|
||||
|
@ -1173,10 +1210,10 @@ public class BrowserProvider extends ContentProvider {
|
|||
|
||||
String selection = Images.URL + " NOT IN (SELECT " + Bookmarks.URL +
|
||||
" 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 +
|
||||
" 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);
|
||||
}
|
||||
|
|
|
@ -65,9 +65,11 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
|||
public static final int TRUNCATE_N_OLDEST = 5;
|
||||
|
||||
private final String mProfile;
|
||||
private long mMobileFolderId;
|
||||
|
||||
public LocalBrowserDB(String profile) {
|
||||
mProfile = profile;
|
||||
mMobileFolderId = -1;
|
||||
}
|
||||
|
||||
private Uri appendProfileAndLimit(Uri uri, int limit) {
|
||||
|
@ -233,10 +235,38 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
|||
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) {
|
||||
long folderId = getMobileBookmarksFolderId(cr);
|
||||
if (folderId < 0)
|
||||
return;
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Browser.BookmarkColumns.TITLE, title);
|
||||
values.put(Bookmarks.URL, uri);
|
||||
values.put(Bookmarks.PARENT, folderId);
|
||||
|
||||
// Restore deleted record if possible
|
||||
values.put(Bookmarks.IS_DELETED, 0);
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.gfx.CairoImage;
|
||||
import org.mozilla.gecko.gfx.CairoUtils;
|
||||
import android.graphics.Bitmap;
|
||||
|
@ -46,6 +47,7 @@ import java.nio.ByteBuffer;
|
|||
public class BufferedCairoImage extends CairoImage {
|
||||
private ByteBuffer mBuffer;
|
||||
private int mWidth, mHeight, mFormat;
|
||||
private boolean mNeedToFreeBuffer = false;
|
||||
|
||||
/** Creates a buffered Cairo image from a byte buffer. */
|
||||
public BufferedCairoImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) {
|
||||
|
@ -57,11 +59,23 @@ public class BufferedCairoImage extends CairoImage {
|
|||
mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig());
|
||||
mWidth = bitmap.getWidth();
|
||||
mHeight = bitmap.getHeight();
|
||||
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
|
||||
mNeedToFreeBuffer = true;
|
||||
mBuffer = GeckoAppShell.allocateDirectBuffer(mWidth * mHeight * 4);
|
||||
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; }
|
||||
@Override
|
||||
public int getWidth() { return mWidth; }
|
||||
|
|
|
@ -103,7 +103,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
|||
|
||||
mScreenSize = new IntSize(1, 1);
|
||||
|
||||
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 2);
|
||||
mBuffer = GeckoAppShell.allocateDirectBuffer(mWidth * mHeight * 2);
|
||||
|
||||
mCairoImage = new CairoImage() {
|
||||
@Override
|
||||
|
@ -119,6 +119,17 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
|||
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. */
|
||||
@Override
|
||||
public void setLayerController(LayerController layerController) {
|
||||
|
@ -151,28 +162,22 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
|||
mGeckoViewport = new ViewportMetrics(viewportObject);
|
||||
mGeckoViewport.setSize(viewportSize);
|
||||
|
||||
mTileLayer.setOrigin(PointUtils.round(mGeckoViewport.getDisplayportOrigin()));
|
||||
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
|
||||
LayerController controller = getLayerController();
|
||||
synchronized (controller) {
|
||||
PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin();
|
||||
mTileLayer.setOrigin(PointUtils.round(displayportOrigin));
|
||||
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
|
||||
|
||||
// Make sure LayerController metrics changes only happen in the
|
||||
// UI thread.
|
||||
final LayerController controller = getLayerController();
|
||||
|
||||
if (controller != null) {
|
||||
controller.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (onlyUpdatePageSize) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (onlyUpdatePageSize) {
|
||||
// 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) {
|
||||
Log.e(LOGTAG, "Bad viewport description: " + viewportDescription);
|
||||
|
@ -209,7 +214,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
|
|||
b.copyPixelsFromBuffer(mBuffer.asIntBuffer());
|
||||
return b;
|
||||
} catch (OutOfMemoryError oom) {
|
||||
Log.e(LOGTAG, "Unable to create bitmap", oom);
|
||||
Log.w(LOGTAG, "Unable to create bitmap", oom);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,12 +58,13 @@ import android.view.GestureDetector;
|
|||
import android.view.ScaleGestureDetector;
|
||||
import android.view.View.OnTouchListener;
|
||||
import java.lang.Math;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* to a higher-level view.
|
||||
*
|
||||
* Many methods require that the monitor be held, with a synchronized (controller) { ... } block.
|
||||
*/
|
||||
public class LayerController {
|
||||
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
|
||||
* 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();
|
||||
}
|
||||
|
||||
/** Scrolls the viewport to the given point. You must hold the monitor while calling this. */
|
||||
public void scrollTo(PointF point) {
|
||||
mViewportMetrics.setOrigin(point);
|
||||
notifyLayerClientOfGeometryChange();
|
||||
|
@ -181,6 +184,7 @@ public class LayerController {
|
|||
mView.requestRender();
|
||||
}
|
||||
|
||||
/** Scrolls the viewport by the given offset. You must hold the monitor while calling this. */
|
||||
public void scrollBy(PointF point) {
|
||||
PointF origin = mViewportMetrics.getOrigin();
|
||||
origin.offset(point.x, point.y);
|
||||
|
@ -192,6 +196,7 @@ public class LayerController {
|
|||
mView.requestRender();
|
||||
}
|
||||
|
||||
/** Sets the current viewport. You must hold the monitor while calling this. */
|
||||
public void setViewport(RectF viewport) {
|
||||
mViewportMetrics.setViewport(viewport);
|
||||
notifyLayerClientOfGeometryChange();
|
||||
|
@ -200,6 +205,7 @@ public class LayerController {
|
|||
mView.requestRender();
|
||||
}
|
||||
|
||||
/** Sets the current page size. You must hold the monitor while calling this. */
|
||||
public void setPageSize(FloatSize size) {
|
||||
if (mViewportMetrics.getPageSize().fuzzyEquals(size))
|
||||
return;
|
||||
|
@ -215,7 +221,8 @@ public class LayerController {
|
|||
/**
|
||||
* 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
|
||||
* notifyPanZoomControllerOfGeometryChange() after calling this.
|
||||
* notifyPanZoomControllerOfGeometryChange() after calling this. You must hold the monitor
|
||||
* while calling this.
|
||||
*/
|
||||
public void setViewportMetrics(ViewportMetrics viewport) {
|
||||
mViewportMetrics = new ViewportMetrics(viewport);
|
||||
|
@ -223,10 +230,15 @@ public class LayerController {
|
|||
mView.requestRender();
|
||||
}
|
||||
|
||||
/** Scales the viewport. You must hold the monitor while calling this. */
|
||||
public void scaleTo(float zoomFactor) {
|
||||
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) {
|
||||
mViewportMetrics.scaleTo(zoomFactor, focus);
|
||||
|
||||
|
@ -237,6 +249,10 @@ public class LayerController {
|
|||
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) {
|
||||
mViewportMetrics.setOrigin(origin);
|
||||
scaleTo(zoomFactor);
|
||||
|
|
|
@ -132,9 +132,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
|
||||
/**
|
||||
* 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) {
|
||||
long frameStartTime = SystemClock.uptimeMillis();
|
||||
|
@ -142,63 +139,68 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
|
|||
TextureReaper.get().reap(gl);
|
||||
|
||||
LayerController controller = mView.getController();
|
||||
Layer rootLayer = controller.getRoot();
|
||||
RenderContext screenContext = createScreenContext(), pageContext = createPageContext();
|
||||
RenderContext screenContext = createScreenContext();
|
||||
|
||||
if (!pageContext.fuzzyEquals(mLastPageContext)) {
|
||||
// the viewport or page changed, so show the scrollbars again
|
||||
// as per UX decision
|
||||
mVertScrollLayer.unfade();
|
||||
mHorizScrollLayer.unfade();
|
||||
mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
|
||||
} else if (mFadeRunnable.timeToFade()) {
|
||||
boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade();
|
||||
if (stillFading) {
|
||||
mFadeRunnable.scheduleNextFadeFrame();
|
||||
synchronized (controller) {
|
||||
Layer rootLayer = controller.getRoot();
|
||||
RenderContext pageContext = createPageContext();
|
||||
|
||||
if (!pageContext.fuzzyEquals(mLastPageContext)) {
|
||||
// the viewport or page changed, so show the scrollbars again
|
||||
// as per UX decision
|
||||
mVertScrollLayer.unfade();
|
||||
mHorizScrollLayer.unfade();
|
||||
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. */
|
||||
if (mShowFrameRate) {
|
||||
|
|
|
@ -51,6 +51,7 @@ import android.util.Log;
|
|||
import java.nio.ByteBuffer;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import org.mozilla.gecko.FloatUtils;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
|
||||
/**
|
||||
* 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 Canvas mCanvas;
|
||||
private float mOpacity;
|
||||
private boolean mFinalized = false;
|
||||
|
||||
private ScrollbarLayer(CairoImage image, boolean vertical, ByteBuffer buffer) {
|
||||
super(false, image);
|
||||
|
@ -84,11 +86,22 @@ public class ScrollbarLayer extends TileLayer {
|
|||
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) {
|
||||
// just create an empty image for now, it will get drawn
|
||||
// on demand anyway
|
||||
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);
|
||||
return new ScrollbarLayer(image, vertical, buffer);
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.gfx.BufferedCairoImage;
|
||||
import org.mozilla.gecko.gfx.CairoImage;
|
||||
import org.mozilla.gecko.gfx.IntSize;
|
||||
|
@ -56,6 +57,7 @@ import java.nio.IntBuffer;
|
|||
public class TextLayer extends SingleTileLayer {
|
||||
private final ByteBuffer mBuffer;
|
||||
private final IntSize mSize;
|
||||
private boolean mFinalized = false;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
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) {
|
||||
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,
|
||||
CairoImage.FORMAT_ARGB32);
|
||||
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_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() {
|
||||
try {
|
||||
return new JSONStringer().object()
|
||||
.key("x").value(mViewportRect.left)
|
||||
.key("y").value(mViewportRect.top)
|
||||
.key("width").value(mViewportRect.width())
|
||||
.key("height").value(mViewportRect.height())
|
||||
.key("pageWidth").value(mPageSize.width)
|
||||
.key("pageHeight").value(mPageSize.height)
|
||||
.key("offsetX").value(mViewportOffset.x)
|
||||
.key("offsetY").value(mViewportOffset.y)
|
||||
.key("zoom").value(mZoomFactor)
|
||||
.endObject().toString();
|
||||
JSONStringer object = new JSONStringer().object();
|
||||
object.key("zoom").value(mZoomFactor);
|
||||
object.key("offsetY").value(mViewportOffset.y);
|
||||
object.key("offsetX").value(mViewportOffset.x);
|
||||
object.key("pageHeight").value(mPageSize.height);
|
||||
object.key("pageWidth").value(mPageSize.width);
|
||||
object.key("height").value(mViewportRect.height());
|
||||
object.key("width").value(mViewportRect.width());
|
||||
object.key("y").value(mViewportRect.top);
|
||||
object.key("x").value(mViewportRect.left);
|
||||
return object.endObject().toString();
|
||||
} catch (JSONException je) {
|
||||
Log.e(LOGTAG, "Error serializing viewportmetrics", je);
|
||||
return "";
|
||||
|
|
|
@ -62,6 +62,10 @@
|
|||
<!ENTITY pref_clear_history_confirm "Browsing history will be deleted">
|
||||
<!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_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">
|
||||
|
||||
|
|
После Ширина: | Высота: | Размер: 69 KiB |
После Ширина: | Высота: | Размер: 1.4 KiB |
Двоичные данные
mobile/android/base/resources/drawable-hdpi-v8/abouthome_separator.9.png
Normal file
После Ширина: | Высота: | Размер: 93 B |
Двоичные данные
mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_placeholder.png
Normal file
После Ширина: | Высота: | Размер: 28 KiB |
Двоичные данные
mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_shadow.9.png
Normal file
После Ширина: | Высота: | Размер: 800 B |
После Ширина: | Высота: | Размер: 36 KiB |
После Ширина: | Высота: | Размер: 984 B |
Двоичные данные
mobile/android/base/resources/drawable-mdpi-v8/abouthome_separator.9.png
Normal file
После Ширина: | Высота: | Размер: 93 B |
Двоичные данные
mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_placeholder.png
Normal file
После Ширина: | Высота: | Размер: 14 KiB |
Двоичные данные
mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_shadow.9.png
Normal file
После Ширина: | Высота: | Размер: 729 B |
После Ширина: | Высота: | Размер: 105 KiB |
После Ширина: | Высота: | Размер: 1.8 KiB |
Двоичные данные
mobile/android/base/resources/drawable-xhdpi-v11/abouthome_separator.9.png
Normal file
После Ширина: | Высота: | Размер: 100 B |
Двоичные данные
mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_placeholder.png
Normal file
После Ширина: | Высота: | Размер: 41 KiB |
Двоичные данные
mobile/android/base/resources/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png
Normal file
После Ширина: | Высота: | Размер: 1.0 KiB |
После Ширина: | Высота: | Размер: 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"/>
|
После Ширина: | Высота: | Размер: 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"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<ImageView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="100dip"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:src="@drawable/icon" />
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dip"
|
||||
android:textColor="#FF202020"
|
||||
android:textStyle="bold"
|
||||
android:text="Recommended Addons"
|
||||
android:isScrollContainer="false" />
|
||||
<ListView
|
||||
android:id="@+id/recommended_addon_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:divider="@android:color/transparent"
|
||||
android:isScrollContainer="false"
|
||||
android:dividerHeight="8dip" />
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dip"
|
||||
android:textColor="#FF202020"
|
||||
android:textStyle="bold"
|
||||
android:isScrollContainer="false"
|
||||
android:text="Favorite Sites" />
|
||||
<GridView
|
||||
android:id="@+id/grid"
|
||||
android:layout_width="fill_parent"
|
||||
android:isScrollContainer="false"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<RelativeLayout android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@drawable/abouthome_bg_repeat">
|
||||
|
||||
<RelativeLayout android:id="@+id/top_sites"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:isScrollContainer="false"
|
||||
android:background="@drawable/abouthome_topsites_bg_repeat">
|
||||
|
||||
<ImageView android:layout_width="fill_parent"
|
||||
android:layout_height="50dip"
|
||||
android:gravity="fill"
|
||||
android:background="@drawable/abouthome_bg_repeat"/>
|
||||
|
||||
<ImageView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:src="@drawable/abouthome_icon"/>
|
||||
|
||||
<TextView android:id="@+id/top_sites_title"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="12dip"
|
||||
android:layout_marginTop="55dip"
|
||||
android:textSize="12sp"
|
||||
android:textColor="#000000"
|
||||
android:textStyle="bold"
|
||||
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>
|
||||
|
|
|
@ -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: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 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_private_data">&pref_clear_private_data;</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="forward">&forward;</string>
|
||||
|
|
|
@ -560,7 +560,9 @@ public class PanZoomController
|
|||
GeckoAppShell.sendEventToGecko(e);
|
||||
mOverrideScrollAck = false;
|
||||
} else {
|
||||
mController.scrollBy(new PointF(mX.displacement, mY.displacement));
|
||||
synchronized (mController) {
|
||||
mController.scrollBy(new PointF(mX.displacement, mY.displacement));
|
||||
}
|
||||
}
|
||||
|
||||
mX.displacement = mY.displacement = 0;
|
||||
|
@ -592,18 +594,22 @@ public class PanZoomController
|
|||
|
||||
/* Performs one frame of a bounce animation. */
|
||||
private void advanceBounce() {
|
||||
float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
|
||||
ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
|
||||
mController.setViewportMetrics(newMetrics);
|
||||
mController.notifyLayerClientOfGeometryChange();
|
||||
mBounceFrame++;
|
||||
synchronized (mController) {
|
||||
float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
|
||||
ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
|
||||
mController.setViewportMetrics(newMetrics);
|
||||
mController.notifyLayerClientOfGeometryChange();
|
||||
mBounceFrame++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Concludes a bounce animation and snaps the viewport into place. */
|
||||
private void finishBounce() {
|
||||
mController.setViewportMetrics(mBounceEndMetrics);
|
||||
mController.notifyLayerClientOfGeometryChange();
|
||||
mBounceFrame = -1;
|
||||
synchronized (mController) {
|
||||
mController.setViewportMetrics(mBounceEndMetrics);
|
||||
mController.notifyLayerClientOfGeometryChange();
|
||||
mBounceFrame = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -881,11 +887,14 @@ public class PanZoomController
|
|||
else
|
||||
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(),
|
||||
mLastZoomFocus.y - detector.getFocusY()));
|
||||
mController.scaleWithFocus(newZoomFactor, new PointF(detector.getFocusX(), detector.getFocusY()));
|
||||
mController.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(),
|
||||
mLastZoomFocus.y - detector.getFocusY()));
|
||||
PointF focus = new PointF(detector.getFocusX(), detector.getFocusY());
|
||||
mController.scaleWithFocus(newZoomFactor, focus);
|
||||
}
|
||||
|
||||
mLastZoomFocus.set(detector.getFocusX(), detector.getFocusY());
|
||||
|
||||
|
|
|
@ -92,6 +92,26 @@
|
|||
<div id="addons-list" style="display: none;">
|
||||
</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[
|
||||
let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
|
||||
|
||||
|
@ -116,6 +136,8 @@
|
|||
}
|
||||
|
||||
function init() {
|
||||
window.addEventListener("popstate", onPopState, false);
|
||||
|
||||
AddonManager.addInstallListener(Addons);
|
||||
Addons.getAddons();
|
||||
}
|
||||
|
@ -124,11 +146,34 @@
|
|||
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 = {
|
||||
_createItem: function _createItem(aAddon) {
|
||||
let outer = document.createElement("div");
|
||||
outer.setAttribute("addonID", aAddon.id);
|
||||
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");
|
||||
img.className = "favicon";
|
||||
|
@ -138,15 +183,24 @@
|
|||
let inner = document.createElement("div");
|
||||
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.className = "title";
|
||||
inner.appendChild(titlePart);
|
||||
details.appendChild(titlePart);
|
||||
|
||||
let versionPart = document.createElement("span");
|
||||
let versionPart = document.createElement("div");
|
||||
versionPart.textContent = aAddon.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) {
|
||||
let descPart = document.createElement("div");
|
||||
|
@ -156,56 +210,6 @@
|
|||
}
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
|
@ -302,16 +306,92 @@
|
|||
return "";
|
||||
},
|
||||
|
||||
enable: function enable(aItem) {
|
||||
if (!aItem.addon)
|
||||
showDetails: function showDetails(aListItem) {
|
||||
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;
|
||||
|
||||
let opType;
|
||||
if (aItem.addon.type == "search") {
|
||||
aItem.setAttribute("isDisabled", false);
|
||||
aItem.addon.engine.hidden = false;
|
||||
let isDisabled;
|
||||
if (detailItem.addon.type == "search") {
|
||||
isDisabled = false;
|
||||
detailItem.addon.engine.hidden = false;
|
||||
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
|
||||
let theme = null;
|
||||
let list = document.getElementById("addons-list");
|
||||
|
@ -326,98 +406,118 @@
|
|||
if (theme)
|
||||
this.disable(theme);
|
||||
|
||||
aItem.addon.userDisabled = false;
|
||||
aItem.setAttribute("isDisabled", false);
|
||||
detailItem.addon.userDisabled = false;
|
||||
isDisabled = false;
|
||||
} else {
|
||||
aItem.addon.userDisabled = false;
|
||||
opType = this._getOpTypeForOperations(aItem.addon.pendingOperations);
|
||||
detailItem.addon.userDisabled = false;
|
||||
isDisabled = false;
|
||||
opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
|
||||
|
||||
if (aItem.addon.pendingOperations & AddonManager.PENDING_ENABLE) {
|
||||
if (detailItem.addon.pendingOperations & AddonManager.PENDING_ENABLE) {
|
||||
this.showRestart();
|
||||
} else {
|
||||
aItem.setAttribute("isDisabled", false);
|
||||
if (aItem.getAttribute("opType") == "needs-disable")
|
||||
if (detailItem.getAttribute("opType") == "needs-disable")
|
||||
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) {
|
||||
if (!aItem.addon)
|
||||
disable: function disable() {
|
||||
let detailItem = document.querySelector("#addons-details > .addon-item");
|
||||
if (!detailItem.addon)
|
||||
return;
|
||||
|
||||
let opType;
|
||||
if (aItem.addon.type == "search") {
|
||||
aItem.setAttribute("isDisabled", true);
|
||||
aItem.addon.engine.hidden = true;
|
||||
let isDisabled;
|
||||
if (detailItem.addon.type == "search") {
|
||||
isDisabled = true;
|
||||
detailItem.addon.engine.hidden = true;
|
||||
opType = "needs-disable";
|
||||
} else if (aItem.addon.type == "theme") {
|
||||
aItem.addon.userDisabled = true;
|
||||
aItem.setAttribute("isDisabled", true);
|
||||
} else if (aItem.addon.type == "locale") {
|
||||
aItem.addon.userDisabled = true;
|
||||
aItem.setAttribute("isDisabled", true);
|
||||
} else if (detailItem.addon.type == "theme") {
|
||||
detailItem.addon.userDisabled = true;
|
||||
isDisabled = true;
|
||||
} else if (detailItem.addon.type == "locale") {
|
||||
detailItem.addon.userDisabled = true;
|
||||
isDisabled = true;
|
||||
} else {
|
||||
aItem.addon.userDisabled = true;
|
||||
opType = this._getOpTypeForOperations(aItem.addon.pendingOperations);
|
||||
detailItem.addon.userDisabled = true;
|
||||
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();
|
||||
} else {
|
||||
aItem.setAttribute("isDisabled", !aItem.addon.isActive);
|
||||
if (aItem.getAttribute("opType") == "needs-enable")
|
||||
if (detailItem.getAttribute("opType") == "needs-enable")
|
||||
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");
|
||||
if (!aItem.addon) {
|
||||
list.removeChild(aItem);
|
||||
let detailItem = document.querySelector("#addons-details > .addon-item");
|
||||
if (!detailItem.addon)
|
||||
return;
|
||||
}
|
||||
|
||||
let opType;
|
||||
if (aItem.addon.type == "search") {
|
||||
let listItem = this._getElementForAddon(detailItem.addon.id);
|
||||
|
||||
if (detailItem.addon.type == "search") {
|
||||
// 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)
|
||||
aItem.addon.engine.hidden = false;
|
||||
Services.search.removeEngine(aItem.addon.engine);
|
||||
detailItem.addon.engine.hidden = false;
|
||||
Services.search.removeEngine(detailItem.addon.engine);
|
||||
// the search-engine-modified observer in browser.js will take care of
|
||||
// updating the list
|
||||
} else {
|
||||
aItem.addon.uninstall();
|
||||
opType = this._getOpTypeForOperations(aItem.addon.pendingOperations);
|
||||
detailItem.addon.uninstall();
|
||||
let opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
|
||||
|
||||
if (aItem.addon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
|
||||
if (detailItem.addon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
|
||||
this.showRestart();
|
||||
|
||||
// A disabled addon doesn't need a restart so it has no pending ops and
|
||||
// can't be cancelled
|
||||
if (!aItem.addon.isActive && opType == "")
|
||||
if (!detailItem.addon.isActive && opType == "")
|
||||
opType = "needs-uninstall";
|
||||
|
||||
aItem.setAttribute("opType", opType);
|
||||
detailItem.setAttribute("opType", opType);
|
||||
listItem.setAttribute("opType", opType);
|
||||
} else {
|
||||
list.removeChild(aItem);
|
||||
list.removeChild(listItem);
|
||||
history.back();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
cancelUninstall: function ev_cancelUninstall(aItem) {
|
||||
if (!aItem.addon)
|
||||
cancelUninstall: function ev_cancelUninstall() {
|
||||
let detailItem = document.querySelector("#addons-details > .addon-item");
|
||||
if (!detailItem.addon)
|
||||
return;
|
||||
|
||||
aItem.addon.cancelUninstall();
|
||||
detailItem.addon.cancelUninstall();
|
||||
this.hideRestart();
|
||||
|
||||
let opType = this._getOpTypeForOperations(aItem.addon.pendingOperations);
|
||||
aItem.setAttribute("opType", opType);
|
||||
let opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
|
||||
detailItem.setAttribute("opType", opType);
|
||||
|
||||
let listItem = this._getElementForAddon(detailItem.addon.id);
|
||||
listItem.setAttribute("opType", opType);
|
||||
},
|
||||
|
||||
showRestart: function showRestart(aMode) {
|
||||
|
|
|
@ -205,6 +205,7 @@ var BrowserApp = {
|
|||
IndexedDB.init();
|
||||
XPInstallObserver.init();
|
||||
ConsoleAPI.init();
|
||||
ClipboardHelper.init();
|
||||
|
||||
// Init LoginManager
|
||||
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
|
||||
|
@ -435,6 +436,16 @@ var BrowserApp = {
|
|||
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 {
|
||||
switch (Services.prefs.getPrefType(prefName)) {
|
||||
case Ci.nsIPrefBranch.PREF_BOOL:
|
||||
|
@ -490,6 +501,13 @@ var BrowserApp = {
|
|||
setPreferences: function setPreferences(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
|
||||
// integers and strings to represent booleans. here, we convert them back
|
||||
// to their actual types so we can store them.
|
||||
|
@ -540,10 +558,17 @@ var BrowserApp = {
|
|||
let args = JSON.parse(aData);
|
||||
let uri;
|
||||
if (args.engine) {
|
||||
let engine = Services.search.getEngineByName(args.engine);
|
||||
uri = engine.getSubmission(args.url).uri;
|
||||
} else
|
||||
let engine;
|
||||
if (args.engine == "__default__")
|
||||
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);
|
||||
}
|
||||
return uri ? uri.spec : args.url;
|
||||
},
|
||||
|
||||
|
@ -746,12 +771,6 @@ var NativeWindow = {
|
|||
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.SelectorContext("video:not(:-moz-full-screen)"),
|
||||
function(aTarget) {
|
||||
|
@ -1153,10 +1172,10 @@ Tab.prototype = {
|
|||
|
||||
get viewport() {
|
||||
// Update the viewport to current dimensions
|
||||
this._viewport.x = this.browser.contentWindow.scrollX +
|
||||
this.viewportExcess.x;
|
||||
this._viewport.y = this.browser.contentWindow.scrollY +
|
||||
this.viewportExcess.y;
|
||||
this._viewport.x = (this.browser.contentWindow.scrollX +
|
||||
this.viewportExcess.x) || 0;
|
||||
this._viewport.y = (this.browser.contentWindow.scrollY +
|
||||
this.viewportExcess.y) || 0;
|
||||
|
||||
// Transform coordinates based on zoom
|
||||
this._viewport.x = Math.round(this._viewport.x * this._viewport.zoom);
|
||||
|
@ -1185,8 +1204,8 @@ Tab.prototype = {
|
|||
updateViewport: function(aReset) {
|
||||
let win = this.browser.contentWindow;
|
||||
let zoom = (aReset ? this.getDefaultZoomLevel() : this._viewport.zoom);
|
||||
let xpos = (aReset ? win.scrollX * zoom : this._viewport.x);
|
||||
let ypos = (aReset ? win.scrollY * zoom : this._viewport.y);
|
||||
let xpos = ((aReset && win) ? win.scrollX * zoom : this._viewport.x);
|
||||
let ypos = ((aReset && win) ? win.scrollY * zoom : this._viewport.y);
|
||||
|
||||
this.viewportExcess = { x: 0, y: 0 };
|
||||
this.viewport = { x: xpos, y: ypos,
|
||||
|
@ -1374,12 +1393,16 @@ Tab.prototype = {
|
|||
|
||||
let browser = BrowserApp.getBrowserForWindow(contentWin);
|
||||
let uri = browser.currentURI.spec;
|
||||
let documentURI = browser.contentDocument.documentURIObject.spec;
|
||||
let contentType = browser.contentDocument.contentType;
|
||||
|
||||
let message = {
|
||||
gecko: {
|
||||
type: "Content:LocationChange",
|
||||
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 = {
|
||||
showDoorHanger: function(aTab) {
|
||||
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
|
||||
addPluginClickCallback: function (plugin, callbackName /*callbackArgs...*/) {
|
||||
// XXX just doing (callback)(arg) was giving a same-origin error. bug?
|
||||
|
|
|
@ -1,2 +1,8 @@
|
|||
<!ENTITY aboutAddons.title "Add-ons Manager">
|
||||
<!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.cancel=Cancel
|
||||
addonAction.options=Options
|
||||
|
||||
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 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 bookmarksHeader.label "Bookmarks">
|
||||
<!ENTITY historyHeader.label "History">
|
||||
|
|
|
@ -35,11 +35,6 @@ addonsSearchEngine.description=Integrated Search
|
|||
addonsConfirmInstall.title=Installing Add-on
|
||||
addonsConfirmInstall.install=Install
|
||||
|
||||
addonType.2=Extension
|
||||
addonType.4=Theme
|
||||
addonType.8=Locale
|
||||
addonType.1024=Search
|
||||
|
||||
addonUpdate.checking=Checking for updates…
|
||||
addonUpdate.updating=Updating to %S
|
||||
addonUpdate.updated=Updated to %S
|
||||
|
@ -261,6 +256,11 @@ contextmenu.openInNewTab=Open Link in New Tab
|
|||
contextmenu.changeInputMethod=Select Input Method
|
||||
contextmenu.fullScreen=Full Screen
|
||||
|
||||
contextmenu.copy=Copy
|
||||
contextmenu.copyAll=Copy All
|
||||
contextmenu.selectAll=Select All
|
||||
contextmenu.paste=Paste
|
||||
|
||||
# Select UI
|
||||
selectHelper.closeMultipleSelectDialog=Done
|
||||
|
||||
|
|
|
@ -36,41 +36,82 @@
|
|||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
html {
|
||||
font-size: 24px;
|
||||
font-size: 18px;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.addons-header {
|
||||
border-bottom: 3px solid black;
|
||||
body {
|
||||
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 {
|
||||
border-bottom: 1px solid black;
|
||||
padding: 8px;
|
||||
color: #656565;
|
||||
background-color: #0e1012;
|
||||
border-bottom: 3px solid black;
|
||||
position: relative;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.addon-item:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.addon-item:not([optionsURL]) .options-btn {
|
||||
visibility: hidden;
|
||||
.addon-item:not([optionsURL]) .options-header,
|
||||
.addon-item[optionsURL=""] .options-header,
|
||||
.addon-item:not([optionsURL]) .options-box,
|
||||
.addon-item[optionsURL=""] .options-box {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Make room for the image */
|
||||
.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 {
|
||||
color: black;
|
||||
font-size: 22px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.version {
|
||||
/* The addon title is not localized, so keep the margin on the left side */
|
||||
margin-left: 12px;
|
||||
font-size: 18px;
|
||||
color: gray;
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
.tag {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.description {
|
||||
|
@ -82,6 +123,31 @@ html {
|
|||
|
||||
.buttons {
|
||||
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 {
|
||||
|
@ -99,9 +165,3 @@ body[dir="ltr"] .favicon {
|
|||
height: 32px;
|
||||
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."
|
||||
|
||||
|
|
|
@ -166,6 +166,36 @@ i?86-*android*)
|
|||
;;
|
||||
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
|
||||
*-android*|*-linuxandroid*)
|
||||
if test -z "$android_ndk" ; then
|
||||
|
@ -240,6 +270,7 @@ case "$target" in
|
|||
AC_DEFINE(ANDROID)
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl =
|
||||
|
@ -1358,6 +1389,9 @@ case "$target" in
|
|||
CPU_ARCH=i386
|
||||
fi
|
||||
;;
|
||||
x86_64)
|
||||
CPU_ARCH=x86_64
|
||||
;;
|
||||
*)
|
||||
CPU_ARCH=ppc
|
||||
;;
|
||||
|
@ -3308,55 +3342,68 @@ dnl ========================================================
|
|||
dnl Generate output files.
|
||||
dnl ========================================================
|
||||
MAKEFILES="
|
||||
Makefile
|
||||
config/Makefile
|
||||
config/autoconf.mk
|
||||
config/nsprincl.mk
|
||||
config/nsprincl.sh
|
||||
config/nspr-config
|
||||
lib/Makefile
|
||||
lib/ds/Makefile
|
||||
lib/libc/Makefile
|
||||
lib/libc/include/Makefile
|
||||
lib/libc/src/Makefile
|
||||
lib/tests/Makefile
|
||||
pkg/Makefile
|
||||
pkg/linux/Makefile
|
||||
pkg/solaris/Makefile
|
||||
pkg/solaris/SUNWpr/Makefile
|
||||
pkg/solaris/SUNWprd/Makefile
|
||||
pr/Makefile
|
||||
pr/include/Makefile
|
||||
pr/include/md/Makefile
|
||||
pr/include/obsolete/Makefile
|
||||
pr/include/private/Makefile
|
||||
pr/src/Makefile
|
||||
pr/src/io/Makefile
|
||||
pr/src/linking/Makefile
|
||||
pr/src/malloc/Makefile
|
||||
pr/src/md/Makefile
|
||||
pr/src/md/${PR_MD_ARCH_DIR}/Makefile
|
||||
pr/src/memory/Makefile
|
||||
pr/src/misc/Makefile
|
||||
pr/src/threads/Makefile
|
||||
pr/tests/Makefile
|
||||
pr/tests/dll/Makefile
|
||||
Makefile
|
||||
config/Makefile
|
||||
config/autoconf.mk
|
||||
config/nsprincl.mk
|
||||
config/nsprincl.sh
|
||||
config/nspr-config
|
||||
lib/Makefile
|
||||
lib/ds/Makefile
|
||||
lib/libc/Makefile
|
||||
lib/libc/include/Makefile
|
||||
lib/libc/src/Makefile
|
||||
lib/tests/Makefile
|
||||
pkg/Makefile
|
||||
pr/Makefile
|
||||
pr/include/Makefile
|
||||
pr/include/md/Makefile
|
||||
pr/include/obsolete/Makefile
|
||||
pr/include/private/Makefile
|
||||
pr/src/Makefile
|
||||
pr/src/io/Makefile
|
||||
pr/src/linking/Makefile
|
||||
pr/src/malloc/Makefile
|
||||
pr/src/md/Makefile
|
||||
pr/src/md/${PR_MD_ARCH_DIR}/Makefile
|
||||
pr/src/memory/Makefile
|
||||
pr/src/misc/Makefile
|
||||
pr/src/threads/Makefile
|
||||
pr/tests/Makefile
|
||||
pr/tests/dll/Makefile
|
||||
"
|
||||
|
||||
dnl lib/tests/Makefile
|
||||
dnl pr/tests/w16gui/Makefile
|
||||
dnl tools/Makefile
|
||||
if test "$OS_TARGET" = "Linux"; then
|
||||
MAKEFILES="$MAKEFILES
|
||||
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
|
||||
MAKEFILES="$MAKEFILES pr/src/threads/combined/Makefile"
|
||||
MAKEFILES="$MAKEFILES
|
||||
pr/src/threads/combined/Makefile
|
||||
"
|
||||
elif test -n "$USE_PTHREADS"; then
|
||||
MAKEFILES="$MAKEFILES pr/src/pthreads/Makefile"
|
||||
MAKEFILES="$MAKEFILES
|
||||
pr/src/pthreads/Makefile
|
||||
"
|
||||
elif test -n "$USE_BTHREADS"; then
|
||||
MAKEFILES="$MAKEFILES pr/src/bthreads/Makefile"
|
||||
MAKEFILES="$MAKEFILES
|
||||
pr/src/bthreads/Makefile
|
||||
"
|
||||
fi
|
||||
|
||||
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
|
||||
|
||||
echo $MAKEFILES > unallmakefiles
|
||||
|
|
|
@ -1497,8 +1497,9 @@ PR_ParseTimeStringToExplodedTime(
|
|||
/* "-" 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
|
||||
the character after the dash is not a digit. */
|
||||
if (*rest == '-' && ((rest > string && isalpha(rest[-1]) && year < 0)
|
||||
|| rest[1] < '0' || rest[1] > '9'))
|
||||
if (*rest == '-' && ((rest > string &&
|
||||
isalpha((unsigned char)rest[-1]) && year < 0) ||
|
||||
rest[1] < '0' || rest[1] > '9'))
|
||||
{
|
||||
rest++;
|
||||
goto SKIP_MORE;
|
||||
|
|
|
@ -298,7 +298,6 @@ SHELL_WRAPPER0(executeNextRunnable)
|
|||
SHELL_WRAPPER1(cameraCallbackBridge, jbyteArray)
|
||||
SHELL_WRAPPER1(notifyUriVisited, jstring)
|
||||
SHELL_WRAPPER3(notifyBatteryChange, jdouble, jboolean, jdouble);
|
||||
SHELL_WRAPPER1_WITH_RETURN(canCreateFixupURI, bool, jstring);
|
||||
SHELL_WRAPPER3(notifySmsReceived, jstring, jstring, jlong);
|
||||
|
||||
static void * xul_handle = NULL;
|
||||
|
@ -704,7 +703,6 @@ loadLibs(const char *apkName)
|
|||
GETFUNC(cameraCallbackBridge);
|
||||
GETFUNC(notifyUriVisited);
|
||||
GETFUNC(notifyBatteryChange);
|
||||
GETFUNC(canCreateFixupURI);
|
||||
GETFUNC(notifySmsReceived);
|
||||
#undef GETFUNC
|
||||
sStartupTimeline = (uint64_t *)__wrap_dlsym(xul_handle, "_ZN7mozilla15StartupTimeline16sStartupTimelineE");
|
||||
|
|
|
@ -37,20 +37,23 @@
|
|||
|
||||
MAKEFILES_crypto="
|
||||
services/crypto/Makefile
|
||||
"
|
||||
services/crypto/component/Makefile
|
||||
"
|
||||
|
||||
MAKEFILES_sync="
|
||||
services/sync/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 "
|
||||
services/Makefile
|
||||
$MAKEFILES_crypto
|
||||
$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:
|
||||
|
||||
* manifests are (ordered) lists of tests
|
||||
* manifests are ordered lists of tests
|
||||
* tests may have an arbitrary number of key, value pairs
|
||||
* the parser returns an ordered list of test data structures, which
|
||||
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?
|
||||
|
||||
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.
|
||||
While this is straight-forward, manifests offer several practical
|
||||
advantages::
|
||||
|
@ -37,8 +45,8 @@ advantages::
|
|||
removing it from the tree and a bug filed with the appropriate
|
||||
reason:
|
||||
|
||||
[test_broken.js]
|
||||
disabled = https://bugzilla.mozilla.org/show_bug.cgi?id=123456
|
||||
[test_broken.js]
|
||||
disabled = https://bugzilla.mozilla.org/show_bug.cgi?id=123456
|
||||
|
||||
* ability to run different (subsets of) tests on different
|
||||
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
|
||||
these without changing the test.
|
||||
|
||||
[test_works_on_windows_only.js]
|
||||
run-if = os == 'win'
|
||||
[test_works_on_windows_only.js]
|
||||
run-if = os == 'win'
|
||||
|
||||
* ability to markup tests with metadata. We have a large, complicated,
|
||||
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
|
||||
relative to the manifest:
|
||||
|
||||
[foo.js]
|
||||
[bar.js]
|
||||
[fleem.js]
|
||||
[foo.js]
|
||||
[bar.js]
|
||||
[fleem.js]
|
||||
|
||||
The sections are read in order. In addition, tests may include
|
||||
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
|
||||
be inherited by each test unless overridden:
|
||||
|
||||
[DEFAULT]
|
||||
type = restart
|
||||
[DEFAULT]
|
||||
type = restart
|
||||
|
||||
[lilies.js]
|
||||
color = white
|
||||
[lilies.js]
|
||||
color = white
|
||||
|
||||
[daffodils.js]
|
||||
color = yellow
|
||||
type = other
|
||||
# override type from DEFAULT
|
||||
[daffodils.js]
|
||||
color = yellow
|
||||
type = other
|
||||
# override type from DEFAULT
|
||||
|
||||
[roses.js]
|
||||
color = red
|
||||
[roses.js]
|
||||
color = red
|
||||
|
||||
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
|
||||
the `[include:]` directive unless they are absolute paths.
|
||||
|
@ -109,7 +117,7 @@ terms).
|
|||
|
||||
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
|
||||
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
|
||||
mondays for a certain class of tests:
|
||||
|
||||
tests = []
|
||||
for test in manifests.tests:
|
||||
if 'runOnDay' in test:
|
||||
if calendar.day_name[calendar.weekday(*datetime.datetime.now().timetuple()[:3])].lower() == test['runOnDay'].lower():
|
||||
tests.append(test)
|
||||
else:
|
||||
tests.append(test)
|
||||
tests = []
|
||||
for test in manifests.tests:
|
||||
if 'runOnDay' in test:
|
||||
if calendar.day_name[calendar.weekday(*datetime.datetime.now().timetuple()[:3])].lower() == test['runOnDay'].lower():
|
||||
tests.append(test)
|
||||
else:
|
||||
tests.append(test)
|
||||
|
||||
To recap:
|
||||
* 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:
|
||||
|
||||
[include:path-to-additional-file.manifest]
|
||||
[include:path-to-additional-file.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`:
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
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
|
||||
|
@ -224,7 +232,81 @@ To copy tests and manifests from a source:
|
|||
To update the tests associated with with a manifest from a source
|
||||
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
|
||||
|
@ -252,20 +334,20 @@ Run `manifestparser help` for usage information.
|
|||
|
||||
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:
|
||||
|
||||
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:
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -309,6 +391,14 @@ through several design considerations.
|
|||
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
|
||||
|
||||
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 os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
class DeviceManagerADB(DeviceManager):
|
||||
|
||||
|
@ -13,6 +14,7 @@ class DeviceManagerADB(DeviceManager):
|
|||
self.retries = 0
|
||||
self._sock = None
|
||||
self.useRunAs = False
|
||||
self.useZip = False
|
||||
self.packageName = None
|
||||
if packageName == None:
|
||||
if os.getenv('USER'):
|
||||
|
@ -30,6 +32,10 @@ class DeviceManagerADB(DeviceManager):
|
|||
except:
|
||||
self.useRunAs = False
|
||||
self.packageName = None
|
||||
try:
|
||||
self.verifyZip()
|
||||
except:
|
||||
self.useZip = False
|
||||
try:
|
||||
# a test to see if we have root privs
|
||||
files = self.listFiles("/data/data")
|
||||
|
@ -103,31 +109,41 @@ class DeviceManagerADB(DeviceManager):
|
|||
def pushDir(self, localDir, remoteDir):
|
||||
# adb "push" accepts a directory as an argument, but if the directory
|
||||
# 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:
|
||||
if (not self.dirExists(remoteDir)):
|
||||
self.mkDirs(remoteDir+"/x")
|
||||
for root, dirs, files in os.walk(localDir, followlinks='true'):
|
||||
relRoot = os.path.relpath(root, localDir)
|
||||
for file in files:
|
||||
localFile = os.path.join(root, file)
|
||||
remoteFile = remoteDir + "/"
|
||||
if (relRoot!="."):
|
||||
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)
|
||||
if (self.useZip):
|
||||
localZip = tempfile.mktemp()+".zip"
|
||||
remoteZip = remoteDir + "/adbdmtmp.zip"
|
||||
subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir)
|
||||
self.pushFile(localZip, remoteZip)
|
||||
os.remove(localZip)
|
||||
self.checkCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir])
|
||||
self.checkCmdAs(["shell", "rm", remoteZip])
|
||||
else:
|
||||
if (not self.dirExists(remoteDir)):
|
||||
self.mkDirs(remoteDir+"/x")
|
||||
for root, dirs, files in os.walk(localDir, followlinks='true'):
|
||||
relRoot = os.path.relpath(root, localDir)
|
||||
for file in files:
|
||||
localFile = os.path.join(root, file)
|
||||
remoteFile = remoteDir + "/"
|
||||
if (relRoot!="."):
|
||||
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])
|
||||
return True
|
||||
return remoteDir
|
||||
except:
|
||||
print "pushing " + localDir + " to " + remoteDir + " failed"
|
||||
return False
|
||||
return None
|
||||
|
||||
# external function
|
||||
# returns:
|
||||
|
@ -241,11 +257,25 @@ class DeviceManagerADB(DeviceManager):
|
|||
acmd = ["shell", "am","start"]
|
||||
cmd = ' '.join(cmd).strip()
|
||||
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(cmd[0:i] + "/.App")
|
||||
acmd.append("--es")
|
||||
acmd.append("args")
|
||||
acmd.append(cmd[i:])
|
||||
if args != "":
|
||||
acmd.append("args")
|
||||
acmd.append(args)
|
||||
if uri != "":
|
||||
acmd.append("-d")
|
||||
acmd.append(''.join(['\'',uri, '\'']));
|
||||
print acmd
|
||||
self.checkCmd(acmd)
|
||||
return outputFile;
|
||||
|
@ -578,3 +608,25 @@ class DeviceManagerADB(DeviceManager):
|
|||
self.checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"])
|
||||
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
|
||||
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):
|
||||
bundle = zipfile.ZipFile(path)
|
||||
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):
|
||||
bundle = tarfile.open(path)
|
||||
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:
|
||||
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()
|
||||
if delete:
|
||||
os.remove(path)
|
||||
# namelist returns paths with forward slashes even in windows
|
||||
top_level_files = [os.path.join(extdir, name) for name in namelist
|
||||
if len(name.rstrip('/').split('/')) == 1]
|
||||
# namelist doesn't include folders in windows, append these to the list
|
||||
if mozinfo.isWin:
|
||||
for name in namelist:
|
||||
root = name[:name.find('/')]
|
||||
if root not in top_level_files:
|
||||
top_level_files.append(root)
|
||||
# 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
|
||||
|
||||
def _install_dmg(src, dest):
|
||||
|
|
|
@ -100,10 +100,12 @@ TestSuite.prototype.loadTest = function(test) {
|
|||
log.log('TEST-END', test.name + ' ' + runTime + fThreshold);
|
||||
} catch (e) {
|
||||
log.error(test.name + ' | ' + e);
|
||||
log.debug(test.name + ' | Traceback:');
|
||||
lines = e.stack.split('\n');
|
||||
for (let i = 0; i < lines.length - 1; ++i) {
|
||||
log.debug('\t' + lines[i]);
|
||||
if (e['stack'] !== undefined) {
|
||||
log.debug(test.name + ' | Traceback:');
|
||||
lines = e.stack.split('\n');
|
||||
for (let i = 0; i < lines.length - 1; ++i) {
|
||||
log.debug('\t' + lines[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
|
||||
from mozprocess import ProcessHandler
|
||||
from pepresults import Results
|
||||
from time import sleep
|
||||
from threading import Thread
|
||||
import mozlog
|
||||
import os
|
||||
|
||||
|
@ -57,6 +59,13 @@ class PepProcess(ProcessHandler):
|
|||
self.logger = mozlog.getLogger('PEP')
|
||||
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):
|
||||
"""
|
||||
Callback called on each line of output
|
||||
|
@ -68,6 +77,11 @@ class PepProcess(ProcessHandler):
|
|||
# The output is generated from the Peptest extension
|
||||
# Format is 'PEP <LEVEL> <MSG>' where <MSG> can have multiple tokens
|
||||
# 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]
|
||||
if level == 'TEST-START':
|
||||
results.currentTest = tokens[2].rstrip()
|
||||
|
@ -81,11 +95,12 @@ class PepProcess(ProcessHandler):
|
|||
threshold = 0.0
|
||||
|
||||
msg = results.currentTest \
|
||||
+ ' | fail threshold: ' + str(threshold) \
|
||||
+ ' | metric: ' + str(metric)
|
||||
+ ' | fail threshold: ' + str(threshold)
|
||||
if metric > threshold:
|
||||
msg += ' < metric: ' + str(metric)
|
||||
self.logger.testFail(msg)
|
||||
else:
|
||||
msg += ' >= metric: ' + str(metric)
|
||||
self.logger.testPass(msg)
|
||||
|
||||
self.logger.testEnd(
|
||||
|
|
|
@ -68,25 +68,53 @@ def isURL(path):
|
|||
def extract(path, extdir=None, delete=False):
|
||||
"""
|
||||
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
|
||||
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):
|
||||
bundle = zipfile.ZipFile(path)
|
||||
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):
|
||||
bundle = tarfile.open(path)
|
||||
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:
|
||||
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()
|
||||
if delete:
|
||||
os.remove(path)
|
||||
return [os.path.join(extdir, name) for name in namelist
|
||||
if len(name.rstrip(os.sep).split(os.sep)) == 1]
|
||||
# namelist returns paths with forward slashes even in windows
|
||||
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['path'] = os.path.realpath(self.options.testPath)
|
||||
testObj['name'] = os.path.basename(self.options.testPath)
|
||||
testObj['here'] = os.path.dirname(testObj['path'])
|
||||
tests.append(testObj)
|
||||
else:
|
||||
# a test manifest was passed in
|
||||
|
|
|
@ -44,12 +44,15 @@ try:
|
|||
except IOError:
|
||||
description = ''
|
||||
|
||||
version = "0.0"
|
||||
version = "0.1"
|
||||
|
||||
dependencies = ['mozprofile',
|
||||
dependencies = ['ManifestDestiny',
|
||||
'mozhttpd',
|
||||
'mozlog',
|
||||
'mozprofile >= 0.1',
|
||||
'mozprocess',
|
||||
'mozrunner >= 3.0b3',
|
||||
'mozlog']
|
||||
]
|
||||
|
||||
setup(name='peptest',
|
||||
version=version,
|
||||
|
|
|
@ -359,8 +359,6 @@ FormHistory.prototype = {
|
|||
},
|
||||
|
||||
get dbConnection() {
|
||||
let connection;
|
||||
|
||||
// Make sure dbConnection can't be called from now to prevent infinite loops.
|
||||
delete FormHistory.prototype.dbConnection;
|
||||
|
||||
|
|
|
@ -144,11 +144,7 @@ function endTest() {
|
|||
.getMostRecentWindow("Download:Manager")
|
||||
.close();
|
||||
|
||||
Cc["@mozilla.org/browser/nav-history-service;1"]
|
||||
.getService(Ci.nsINavHistoryService)
|
||||
.removeAllPages();
|
||||
|
||||
SimpleTest.finish();
|
||||
waitForClearHistory(SimpleTest.finish);
|
||||
}
|
||||
|
||||
]]>
|
||||
|
|
|
@ -168,3 +168,16 @@ function setCleanState()
|
|||
let win = getDMWindow();
|
||||
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:box class="icon"/>
|
||||
<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 msgBlocked">&blockedPlugin.label;</html:div>
|
||||
<html:div class="msg msgCrashed"><!-- set at runtime --></html:div>
|
||||
|
|
|
@ -542,6 +542,10 @@ MAKEFILES_profiler="
|
|||
tools/profiler/Makefile
|
||||
"
|
||||
|
||||
MAKEFILES_snappy="
|
||||
other-licenses/snappy/Makefile
|
||||
"
|
||||
|
||||
add_makefiles "
|
||||
$MAKEFILES_dom
|
||||
$MAKEFILES_editor
|
||||
|
@ -577,6 +581,7 @@ add_makefiles "
|
|||
$MAKEFILES_hal
|
||||
$MAKEFILES_psm_public
|
||||
$MAKEFILES_profiler
|
||||
$MAKEFILES_snappy
|
||||
"
|
||||
|
||||
#
|
||||
|
@ -926,6 +931,7 @@ if [ "$ENABLE_TESTS" ]; then
|
|||
accessible/tests/mochitest/actions/Makefile
|
||||
accessible/tests/mochitest/attributes/Makefile
|
||||
accessible/tests/mochitest/editabletext/Makefile
|
||||
accessible/tests/mochitest/elm/Makefile
|
||||
accessible/tests/mochitest/events/Makefile
|
||||
accessible/tests/mochitest/focus/Makefile
|
||||
accessible/tests/mochitest/hyperlink/Makefile
|
||||
|
@ -1144,6 +1150,14 @@ if [ "$MOZ_ANGLE" ]; then
|
|||
"
|
||||
fi
|
||||
|
||||
if [ "$MOZ_B2G_RIL" ]; then
|
||||
add_makefiles "
|
||||
dom/system/b2g/Makefile
|
||||
dom/telephony/Makefile
|
||||
ipc/ril/Makefile
|
||||
"
|
||||
fi
|
||||
|
||||
if [ "$MOZ_CRASHREPORTER" ]; then
|
||||
add_makefiles "
|
||||
toolkit/crashreporter/Makefile
|
||||
|
|