Merge mozilla-central to mozilla-inbound

This commit is contained in:
Ed Morley 2011-12-14 02:53:59 +00:00
Родитель 123ff365d1 83561ee1fd
Коммит a3f9820512
111 изменённых файлов: 2258 добавлений и 1161 удалений
dom/indexedDB/test
image
js/src
layout/svg
mobile/android
nsprpub
other-licenses/android
services
testing
mozbase
manifestdestiny
mozdevice/mozdevice
mozinstall/mozinstall
peptest
toolkit

Просмотреть файл

@ -52,10 +52,10 @@ TEST_FILES = \
exceptions_in_success_events_iframe.html \ exceptions_in_success_events_iframe.html \
helpers.js \ helpers.js \
leaving_page_iframe.html \ leaving_page_iframe.html \
test_add_put.html \
test_add_twice_failure.html \ test_add_twice_failure.html \
test_advance.html \ test_advance.html \
test_autoIncrement_indexes.html \ test_autoIncrement_indexes.html \
test_bad_keypath.html \
test_bfcache.html \ test_bfcache.html \
test_clear.html \ test_clear.html \
test_cmp.html \ test_cmp.html \

Просмотреть файл

@ -0,0 +1,180 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const name = window.location.pathname;
let openRequest = mozIndexedDB.open(name, 1);
openRequest.onerror = errorHandler;
openRequest.onupgradeneeded = grabEventAndContinueHandler;
openRequest.onsuccess = unexpectedSuccessHandler;
let event = yield;
let db = event.target.result;
let trans = event.target.transaction;
for each (let autoincrement in [true, false]) {
for each (let keypath in [false, true, "missing", "invalid"]) {
for each (let method in ["put", "add"]) {
for each (let explicit in [true, false, undefined, "invalid"]) {
for each (let existing in [true, false]) {
let speccedNoKey = (keypath == false || keypath == "missing") &&
!explicit;
// We can't do 'existing' checks if we use autogenerated key
if (speccedNoKey && autoincrement && existing) {
continue;
}
// Create store
if (db.objectStoreNames.contains("mystore"))
db.deleteObjectStore("mystore");
let store = db.createObjectStore("mystore",
{ autoIncrement: autoincrement,
keyPath: (keypath ? "id" : null) });
test = " for test " + JSON.stringify({ autoincrement: autoincrement,
keypath: keypath,
method: method,
explicit: explicit === undefined ? "undefined" : explicit,
existing: existing });
// Insert "existing" data if needed
if (existing) {
if (keypath)
store.add({ existing: "data", id: 5 }).onsuccess = grabEventAndContinueHandler;
else
store.add({ existing: "data" }, 5).onsuccess = grabEventAndContinueHandler;
let e = yield;
is(e.type, "success", "success inserting existing" + test);
is(e.target.result, 5, "inserted correct key" + test);
}
// Set up value to be inserted
let value = { theObj: true };
if (keypath === true) {
value.id = 5;
}
else if (keypath === "invalid") {
value.id = /x/;
}
// Which arguments are passed to function
args = [value];
if (explicit === true) {
args.push(5);
}
else if (explicit === undefined) {
args.push(undefined);
}
else if (explicit === "invalid") {
args.push(/x/);
}
let expected = expectedResult(method, keypath, explicit, autoincrement, existing);
ok(true, "making call" + test);
// Make function call for throwing functions
if (expected === "throw") {
try {
store[method].apply(store, args);
ok(false, "should have thrown" + test);
}
catch (ex) {
ok(true, "did throw" + test);
ok(ex instanceof IDBDatabaseException, "Got a IDBDatabaseException" + test);
is(ex.code, IDBDatabaseException.DATA_ERR, "expect a DATA_ERR" + test);
}
continue;
}
// Make non-throwing function call
let req = store[method].apply(store, args);
req.onsuccess = req.onerror = grabEventAndContinueHandler
let e = yield;
// Figure out what key we used
let key = 5;
if (autoincrement && speccedNoKey) {
key = 1;
}
// Adjust value if expected
if (autoincrement && keypath && speccedNoKey) {
value.id = key;
}
// Check result
if (expected === "error") {
is(e.type, "error", "write should fail" + test);
e.preventDefault();
e.stopPropagation();
continue;
}
is(e.type, "success", "write should succeed" + test);
if (autoincrement && speccedNoKey) {
todo_is(e.target.result, key, "(fix ai) write should return correct key" + test);
key = e.target.result;
if (keypath) {
value.id = key;
}
}
else {
is(e.target.result, key, "write should return correct key" + test);
}
store.get(key).onsuccess = grabEventAndContinueHandler;
e = yield;
is(e.type, "success", "read back should succeed" + test);
is(JSON.stringify(e.target.result),
JSON.stringify(value),
"read back should return correct value" + test);
}
}
}
}
}
function expectedResult(method, keypath, explicit, autoincrement, existing) {
if (keypath && explicit)
return "throw";
if (!keypath && !explicit && !autoincrement)
return "throw";
if (keypath == "invalid")
return "throw";
if (keypath == "missing" && !autoincrement)
return "throw";
if (explicit == "invalid")
return "throw";
if (method == "add" && existing)
return "error";
return "success";
}
openRequest.onsuccess = grabEventAndContinueHandler;
yield;
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

Просмотреть файл

@ -1,49 +0,0 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const name = window.location.pathname;
const description = "My Test Database";
let request = mozIndexedDB.open(name, 1, description);
request.onerror = errorHandler;
request.onupgradeneeded = grabEventAndContinueHandler;
let event = yield;
let db = request.result;
let objectStore = db.createObjectStore("foo", { keyPath: "keyPath" });
request = objectStore.add({keyPath:"foo"});
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
try {
request = objectStore.add({});
ok(false, "Shouldn't get here!");
}
catch (e) {
is(e.code, IDBDatabaseException.DATA_ERR, "Good error");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

Просмотреть файл

@ -174,8 +174,9 @@ NS_IMETHODIMP nsBMPEncoder::StartImageEncode(PRUint32 aWidth,
return NS_OK; return NS_OK;
} }
// Returns the image buffer size // Returns the number of bytes in the image buffer used.
NS_IMETHODIMP nsBMPEncoder::GetImageBufferSize(PRUint32 *aOutputSize) // For a BMP file, this is all bytes in the buffer.
NS_IMETHODIMP nsBMPEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
{ {
NS_ENSURE_ARG_POINTER(aOutputSize); NS_ENSURE_ARG_POINTER(aOutputSize);
*aOutputSize = mImageBufferSize; *aOutputSize = mImageBufferSize;

Просмотреть файл

@ -111,9 +111,10 @@ NS_IMETHODIMP nsICOEncoder::InitFromData(const PRUint8* aData,
return rv; return rv;
} }
// Returns the image buffer size // Returns the number of bytes in the image buffer used
// For an ICO file, this is all bytes in the buffer.
NS_IMETHODIMP NS_IMETHODIMP
nsICOEncoder::GetImageBufferSize(PRUint32 *aOutputSize) nsICOEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
{ {
NS_ENSURE_ARG_POINTER(aOutputSize); NS_ENSURE_ARG_POINTER(aOutputSize);
*aOutputSize = mImageBufferSize; *aOutputSize = mImageBufferSize;
@ -147,16 +148,16 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
aStride, aInputFormat, noParams); aStride, aInputFormat, noParams);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
PRUint32 imageBufferSize; PRUint32 PNGImageBufferSize;
mContainedEncoder->GetImageBufferSize(&imageBufferSize); mContainedEncoder->GetImageBufferUsed(&PNGImageBufferSize);
mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE + mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE +
imageBufferSize; PNGImageBufferSize;
mImageBufferStart = static_cast<PRUint8*>(moz_malloc(mImageBufferSize)); mImageBufferStart = static_cast<PRUint8*>(moz_malloc(mImageBufferSize));
if (!mImageBufferStart) { if (!mImageBufferStart) {
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
} }
mImageBufferCurr = mImageBufferStart; mImageBufferCurr = mImageBufferStart;
mICODirEntry.mBytesInRes = imageBufferSize; mICODirEntry.mBytesInRes = PNGImageBufferSize;
EncodeFileHeader(); EncodeFileHeader();
EncodeInfoHeader(); EncodeInfoHeader();
@ -164,8 +165,8 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
char *imageBuffer; char *imageBuffer;
rv = mContainedEncoder->GetImageBuffer(&imageBuffer); rv = mContainedEncoder->GetImageBuffer(&imageBuffer);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
memcpy(mImageBufferCurr, imageBuffer, imageBufferSize); memcpy(mImageBufferCurr, imageBuffer, PNGImageBufferSize);
mImageBufferCurr += imageBufferSize; mImageBufferCurr += PNGImageBufferSize;
} else { } else {
mContainedEncoder = new nsBMPEncoder(); mContainedEncoder = new nsBMPEncoder();
nsresult rv; nsresult rv;
@ -181,10 +182,10 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
PRUint32 andMaskSize = ((GetRealWidth() + 31) / 32) * 4 * // row AND mask PRUint32 andMaskSize = ((GetRealWidth() + 31) / 32) * 4 * // row AND mask
GetRealHeight(); // num rows GetRealHeight(); // num rows
PRUint32 imageBufferSize; PRUint32 BMPImageBufferSize;
mContainedEncoder->GetImageBufferSize(&imageBufferSize); mContainedEncoder->GetImageBufferUsed(&BMPImageBufferSize);
mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE + mImageBufferSize = ICONFILEHEADERSIZE + ICODIRENTRYSIZE +
imageBufferSize + andMaskSize; BMPImageBufferSize + andMaskSize;
mImageBufferStart = static_cast<PRUint8*>(moz_malloc(mImageBufferSize)); mImageBufferStart = static_cast<PRUint8*>(moz_malloc(mImageBufferSize));
if (!mImageBufferStart) { if (!mImageBufferStart) {
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
@ -192,7 +193,7 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
mImageBufferCurr = mImageBufferStart; mImageBufferCurr = mImageBufferStart;
// The icon buffer does not include the BFH at all. // The icon buffer does not include the BFH at all.
mICODirEntry.mBytesInRes = imageBufferSize - BFH_LENGTH + andMaskSize; mICODirEntry.mBytesInRes = BMPImageBufferSize - BFH_LENGTH + andMaskSize;
// Encode the icon headers // Encode the icon headers
EncodeFileHeader(); EncodeFileHeader();
@ -202,13 +203,13 @@ nsICOEncoder::AddImageFrame(const PRUint8* aData,
rv = mContainedEncoder->GetImageBuffer(&imageBuffer); rv = mContainedEncoder->GetImageBuffer(&imageBuffer);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
memcpy(mImageBufferCurr, imageBuffer + BFH_LENGTH, memcpy(mImageBufferCurr, imageBuffer + BFH_LENGTH,
imageBufferSize - BFH_LENGTH); BMPImageBufferSize - BFH_LENGTH);
// We need to fix the BMP height to be *2 for the AND mask // We need to fix the BMP height to be *2 for the AND mask
PRUint32 fixedHeight = GetRealHeight() * 2; PRUint32 fixedHeight = GetRealHeight() * 2;
fixedHeight = NATIVE32_TO_LITTLE(fixedHeight); fixedHeight = NATIVE32_TO_LITTLE(fixedHeight);
// The height is stored at an offset of 8 from the DIB header // The height is stored at an offset of 8 from the DIB header
memcpy(mImageBufferCurr + 8, &fixedHeight, sizeof(fixedHeight)); memcpy(mImageBufferCurr + 8, &fixedHeight, sizeof(fixedHeight));
mImageBufferCurr += imageBufferSize - BFH_LENGTH; mImageBufferCurr += BMPImageBufferSize - BFH_LENGTH;
// Calculate rowsize in DWORD's // Calculate rowsize in DWORD's
PRUint32 rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up PRUint32 rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up

Просмотреть файл

@ -221,11 +221,11 @@ NS_IMETHODIMP nsJPEGEncoder::StartImageEncode(PRUint32 aWidth,
return NS_ERROR_NOT_IMPLEMENTED; return NS_ERROR_NOT_IMPLEMENTED;
} }
// Returns the image buffer size // Returns the number of bytes in the image buffer used.
NS_IMETHODIMP nsJPEGEncoder::GetImageBufferSize(PRUint32 *aOutputSize) NS_IMETHODIMP nsJPEGEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
{ {
NS_ENSURE_ARG_POINTER(aOutputSize); NS_ENSURE_ARG_POINTER(aOutputSize);
*aOutputSize = mImageBufferSize; *aOutputSize = mImageBufferUsed;
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -206,11 +206,11 @@ NS_IMETHODIMP nsPNGEncoder::StartImageEncode(PRUint32 aWidth,
return NS_OK; return NS_OK;
} }
// Returns the image buffer size // Returns the number of bytes in the image buffer used.
NS_IMETHODIMP nsPNGEncoder::GetImageBufferSize(PRUint32 *aOutputSize) NS_IMETHODIMP nsPNGEncoder::GetImageBufferUsed(PRUint32 *aOutputSize)
{ {
NS_ENSURE_ARG_POINTER(aOutputSize); NS_ENSURE_ARG_POINTER(aOutputSize);
*aOutputSize = mImageBufferSize; *aOutputSize = mImageBufferUsed;
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -44,7 +44,7 @@
/** /**
* imgIEncoder interface * imgIEncoder interface
*/ */
[scriptable, uuid(d02e2d95-072d-4b91-8b8f-1300e45fcb2b)] [scriptable, uuid(4baa2d6e-fee7-42df-ae3f-5fbebc0c267c)]
interface imgIEncoder : nsIAsyncInputStream interface imgIEncoder : nsIAsyncInputStream
{ {
// Possible values for outputOptions. Multiple values are semicolon-separated. // Possible values for outputOptions. Multiple values are semicolon-separated.
@ -159,8 +159,9 @@ interface imgIEncoder : nsIAsyncInputStream
/* /*
* Sometimes an encoder can contain another encoder and direct access * Sometimes an encoder can contain another encoder and direct access
* to its buffer is necessary. * to its buffer is necessary. It is only safe to assume that the buffer
* returned from getImageBuffer() is of size equal to getImageBufferUsed().
*/ */
[noscript] unsigned long getImageBufferSize(); [noscript] unsigned long getImageBufferUsed();
[noscript] charPtr getImageBuffer(); [noscript] charPtr getImageBuffer();
}; };

Просмотреть файл

@ -2788,7 +2788,8 @@ EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool callContex
if (op == JSOP_ARGUMENTS || op == JSOP_CALLEE) { if (op == JSOP_ARGUMENTS || op == JSOP_CALLEE) {
if (Emit1(cx, bce, op) < 0) if (Emit1(cx, bce, op) < 0)
return JS_FALSE; return JS_FALSE;
if (callContext && Emit1(cx, bce, JSOP_PUSH) < 0) /* Need to provide |this| value for call */
if (callContext && Emit1(cx, bce, JSOP_UNDEFINED) < 0)
return JS_FALSE; return JS_FALSE;
} else { } else {
if (!pn->pn_cookie.isFree()) { if (!pn->pn_cookie.isFree()) {
@ -6075,7 +6076,8 @@ EmitReturn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (!EmitTree(cx, bce, pn2)) if (!EmitTree(cx, bce, pn2))
return false; return false;
} else { } else {
if (Emit1(cx, bce, JSOP_PUSH) < 0) /* No explicit return value provided */
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
return false; return false;
} }
@ -6355,9 +6357,9 @@ EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
* in jsinterp.cpp for JSOP_LAMBDA followed by JSOP_{SET,INIT}PROP. * in jsinterp.cpp for JSOP_LAMBDA followed by JSOP_{SET,INIT}PROP.
* *
* Then (or in a call case that has no explicit reference-base * Then (or in a call case that has no explicit reference-base
* object) we emit JSOP_PUSH to produce the |this| slot required * object) we emit JSOP_UNDEFINED to produce the undefined |this|
* for calls (which non-strict mode functions will box into the * value required for calls (which non-strict mode functions
* global object). * will box into the global object).
*/ */
ParseNode *pn2 = pn->pn_head; ParseNode *pn2 = pn->pn_head;
switch (pn2->getKind()) { switch (pn2->getKind()) {
@ -6379,16 +6381,16 @@ EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
JS_ASSERT(pn2->isOp(JSOP_XMLNAME)); JS_ASSERT(pn2->isOp(JSOP_XMLNAME));
if (!EmitXMLName(cx, pn2, JSOP_CALLXMLNAME, bce)) if (!EmitXMLName(cx, pn2, JSOP_CALLXMLNAME, bce))
return false; return false;
callop = true; /* suppress JSOP_PUSH after */ callop = true; /* suppress JSOP_UNDEFINED after */
break; break;
#endif #endif
default: default:
if (!EmitTree(cx, bce, pn2)) if (!EmitTree(cx, bce, pn2))
return false; return false;
callop = false; /* trigger JSOP_PUSH after */ callop = false; /* trigger JSOP_UNDEFINED after */
break; break;
} }
if (!callop && Emit1(cx, bce, JSOP_PUSH) < 0) if (!callop && Emit1(cx, bce, JSOP_UNDEFINED) < 0)
return false; return false;
/* Remember start of callable-object bytecode for decompilation hint. */ /* Remember start of callable-object bytecode for decompilation hint. */
@ -7027,7 +7029,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (!EmitTree(cx, bce, pn->pn_kid)) if (!EmitTree(cx, bce, pn->pn_kid))
return JS_FALSE; return JS_FALSE;
} else { } else {
if (Emit1(cx, bce, JSOP_PUSH) < 0) if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
return JS_FALSE; return JS_FALSE;
} }
if (pn->pn_hidden && NewSrcNote(cx, bce, SRC_HIDDEN) < 0) if (pn->pn_hidden && NewSrcNote(cx, bce, SRC_HIDDEN) < 0)

Просмотреть файл

@ -0,0 +1,13 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
load(libdir + 'eqArrayHelper.js');
assertEqArray(eval('[]'), []);
assertEqArray(eval('[,]'), [,]);
assertEqArray(eval('[,,]'), [,,]);
assertEqArray(eval('[1, 1, ]'), [1,1, ]);
assertEqArray(eval('[1, 1, true]'), [1, 1, true]);
assertEqArray(eval('[1, false, true]'), [1, false, true]);

Просмотреть файл

@ -1,4 +1,4 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* *
* ***** BEGIN LICENSE BLOCK ***** * ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -102,8 +102,7 @@ js_InitArrayClass(JSContext *cx, JSObject *obj);
extern bool extern bool
js_InitContextBusyArrayTable(JSContext *cx); js_InitContextBusyArrayTable(JSContext *cx);
namespace js namespace js {
{
/* Create a dense array with no capacity allocated, length set to 0. */ /* Create a dense array with no capacity allocated, length set to 0. */
extern JSObject * JS_FASTCALL extern JSObject * JS_FASTCALL
@ -136,7 +135,7 @@ NewDenseCopiedArray(JSContext *cx, uint32 length, const Value *vp, JSObject *pro
extern JSObject * extern JSObject *
NewSlowEmptyArray(JSContext *cx); NewSlowEmptyArray(JSContext *cx);
} } /* namespace js */
extern JSBool extern JSBool
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
@ -162,10 +161,7 @@ array_deleteElement(JSContext *cx, JSObject *obj, uint32 index, Value *rval, JSB
extern bool extern bool
GetElements(JSContext *cx, JSObject *aobj, jsuint length, js::Value *vp); GetElements(JSContext *cx, JSObject *aobj, jsuint length, js::Value *vp);
}
/* Natives exposed for optimization by the interpreter and JITs. */ /* Natives exposed for optimization by the interpreter and JITs. */
namespace js {
extern JSBool extern JSBool
array_sort(JSContext *cx, uintN argc, js::Value *vp); array_sort(JSContext *cx, uintN argc, js::Value *vp);
@ -213,7 +209,4 @@ js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id,
JSBool JSBool
js_Array(JSContext *cx, uintN argc, js::Value *vp); js_Array(JSContext *cx, uintN argc, js::Value *vp);
extern JSBool JS_FASTCALL
js_EnsureDenseArrayCapacity(JSContext *cx, JSObject *obj, jsint i);
#endif /* jsarray_h___ */ #endif /* jsarray_h___ */

Просмотреть файл

@ -697,15 +697,6 @@ CreateEvalCallObject(JSContext *cx, StackFrame *fp)
} // namespace js } // namespace js
JSObject * JS_FASTCALL
js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain)
{
JS_ASSERT(!js_IsNamedLambda(fun));
JS_ASSERT(scopeChain);
JS_ASSERT(callee);
return CallObject::create(cx, fun->script(), *scopeChain, callee);
}
void void
js_PutCallObject(StackFrame *fp) js_PutCallObject(StackFrame *fp)
{ {

Просмотреть файл

@ -312,9 +312,6 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
uintN flags, JSObject *parent, JSAtom *atom, uintN flags, JSObject *parent, JSAtom *atom,
js::gc::AllocKind kind = JSFunction::FinalizeKind); js::gc::AllocKind kind = JSFunction::FinalizeKind);
extern void
js_FinalizeFunction(JSContext *cx, JSFunction *fun);
extern JSFunction * JS_FASTCALL extern JSFunction * JS_FASTCALL
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto, js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto,
js::gc::AllocKind kind = JSFunction::FinalizeKind); js::gc::AllocKind kind = JSFunction::FinalizeKind);
@ -345,16 +342,9 @@ js_ValueToCallableObject(JSContext *cx, js::Value *vp, uintN flags);
extern void extern void
js_ReportIsNotFunction(JSContext *cx, const js::Value *vp, uintN flags); js_ReportIsNotFunction(JSContext *cx, const js::Value *vp, uintN flags);
extern JSObject * JS_FASTCALL
js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain);
extern void extern void
js_PutCallObject(js::StackFrame *fp); js_PutCallObject(js::StackFrame *fp);
extern JSBool JS_FASTCALL
js_PutCallObjectOnTrace(JSObject *scopeChain, uint32 nargs, js::Value *argv,
uint32 nvars, js::Value *slots);
namespace js { namespace js {
CallObject * CallObject *

Просмотреть файл

@ -3421,7 +3421,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
/* Bytecodes pushing values of known type. */ /* Bytecodes pushing values of known type. */
case JSOP_VOID: case JSOP_VOID:
case JSOP_PUSH: case JSOP_UNDEFINED:
pushed[0].addType(cx, Type::UndefinedType()); pushed[0].addType(cx, Type::UndefinedType());
break; break;
case JSOP_ZERO: case JSOP_ZERO:

Просмотреть файл

@ -1995,9 +1995,9 @@ END_CASE(JSOP_LINENO)
BEGIN_CASE(JSOP_BLOCKCHAIN) BEGIN_CASE(JSOP_BLOCKCHAIN)
END_CASE(JSOP_BLOCKCHAIN) END_CASE(JSOP_BLOCKCHAIN)
BEGIN_CASE(JSOP_PUSH) BEGIN_CASE(JSOP_UNDEFINED)
PUSH_UNDEFINED(); PUSH_UNDEFINED();
END_CASE(JSOP_PUSH) END_CASE(JSOP_UNDEFINED)
BEGIN_CASE(JSOP_POP) BEGIN_CASE(JSOP_POP)
regs.sp--; regs.sp--;

Просмотреть файл

@ -1191,7 +1191,7 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
size_t length = linearStr->length(); size_t length = linearStr->length();
/* /*
* If the eval string starts with '(' and ends with ')', it may be JSON. * If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
* Try the JSON parser first because it's much faster. If the eval string * Try the JSON parser first because it's much faster. If the eval string
* isn't JSON, JSON parsing will probably fail quickly, so little time * isn't JSON, JSON parsing will probably fail quickly, so little time
* will be lost. * will be lost.
@ -1202,8 +1202,9 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
* JSON with eval and using strict mode, you deserve to be slow. * JSON with eval and using strict mode, you deserve to be slow.
*/ */
if (length > 2 && if (length > 2 &&
chars[0] == '(' && chars[length - 1] == ')' && ((chars[0] == '[' && chars[length - 1] == ']') ||
(!caller || !caller->script()->strictModeCode)) (chars[0] == '(' && chars[length - 1] == ')')) &&
(!caller || !caller->script()->strictModeCode))
{ {
/* /*
* Remarkably, JavaScript syntax is not a superset of JSON syntax: * Remarkably, JavaScript syntax is not a superset of JSON syntax:
@ -1218,7 +1219,8 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
break; break;
if (cp == end) { if (cp == end) {
JSONParser parser(cx, chars + 1, length - 2, bool isArray = (chars[0] == '[');
JSONParser parser(cx, isArray ? chars : chars + 1, isArray ? length : length - 2,
JSONParser::StrictJSON, JSONParser::NoError); JSONParser::StrictJSON, JSONParser::NoError);
Value tmp; Value tmp;
if (!parser.parse(&tmp)) if (!parser.parse(&tmp))

Просмотреть файл

@ -1917,7 +1917,7 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
const char *rval; const char *rval;
LOAD_OP_DATA(pc); LOAD_OP_DATA(pc);
LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL); LOCAL_ASSERT(op == JSOP_GETLOCAL);
todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn)); todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
@ -1934,7 +1934,7 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
if (pc == endpc) if (pc == endpc)
return pc; return pc;
LOAD_OP_DATA(pc); LOAD_OP_DATA(pc);
if (op != JSOP_PUSH && op != JSOP_GETLOCAL) if (op != JSOP_GETLOCAL)
break; break;
if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
return NULL; return NULL;
@ -2548,20 +2548,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
} }
break; break;
case JSOP_PUSH:
#if JS_HAS_DESTRUCTURING
sn = js_GetSrcNote(jp->script, pc);
if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
if (!pc)
return NULL;
LOCAL_ASSERT(*pc == JSOP_POPN);
len = oplen = JSOP_POPN_LENGTH;
goto end_groupassignment;
}
#endif
/* FALL THROUGH */
case JSOP_BINDNAME: case JSOP_BINDNAME:
case JSOP_BINDGNAME: case JSOP_BINDGNAME:
todo = Sprint(&ss->sprinter, ""); todo = Sprint(&ss->sprinter, "");
@ -4193,8 +4179,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
LOCAL_ASSERT(*pc == JSOP_NULLBLOCKCHAIN); LOCAL_ASSERT(*pc == JSOP_NULLBLOCKCHAIN);
pc += JSOP_NULLBLOCKCHAIN_LENGTH; pc += JSOP_NULLBLOCKCHAIN_LENGTH;
} }
LOCAL_ASSERT(*pc == JSOP_PUSH); LOCAL_ASSERT(*pc == JSOP_UNDEFINED);
pc += JSOP_PUSH_LENGTH; pc += JSOP_UNDEFINED_LENGTH;
LOCAL_ASSERT(*pc == JSOP_CALL); LOCAL_ASSERT(*pc == JSOP_CALL);
LOCAL_ASSERT(GET_ARGC(pc) == 0); LOCAL_ASSERT(GET_ARGC(pc) == 0);
len = JSOP_CALL_LENGTH; len = JSOP_CALL_LENGTH;
@ -5225,10 +5211,6 @@ DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX && JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX &&
op != JSOP_DUP && op != JSOP_DUP2); op != JSOP_DUP && op != JSOP_DUP2);
/* JSOP_PUSH is used to generate undefined for group assignment holes. */
if (op == JSOP_PUSH)
return JS_strdup(cx, js_undefined_str);
/* /*
* |this| could convert to a very long object initialiser, so cite it by * |this| could convert to a very long object initialiser, so cite it by
* its keyword name instead. * its keyword name instead.

Просмотреть файл

@ -113,7 +113,7 @@
OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE)
/* Long-standing JavaScript bytecodes. */ /* Long-standing JavaScript bytecodes. */
OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE) OPDEF(JSOP_UNDEFINED, 1, js_undefined_str, "", 1, 0, 1, 0, JOF_BYTE)
OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE) OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE)
OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE|JOF_PARENHEAD) OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE|JOF_PARENHEAD)
OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE)

Просмотреть файл

@ -1681,9 +1681,9 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_NOP) BEGIN_CASE(JSOP_NOP)
END_CASE(JSOP_NOP) END_CASE(JSOP_NOP)
BEGIN_CASE(JSOP_PUSH) BEGIN_CASE(JSOP_UNDEFINED)
frame.push(UndefinedValue()); frame.push(UndefinedValue());
END_CASE(JSOP_PUSH) END_CASE(JSOP_UNDEFINED)
BEGIN_CASE(JSOP_POPV) BEGIN_CASE(JSOP_POPV)
BEGIN_CASE(JSOP_SETRVAL) BEGIN_CASE(JSOP_SETRVAL)

Просмотреть файл

@ -581,7 +581,7 @@ nsSVGPatternFrame::ConstructCTM(const gfxRect &callerBBox,
const nsSVGViewBoxRect viewBox = GetViewBox().GetAnimValue(); const nsSVGViewBoxRect viewBox = GetViewBox().GetAnimValue();
if (viewBox.height <= 0.0f && viewBox.width <= 0.0f) { if (viewBox.height <= 0.0f || viewBox.width <= 0.0f) {
return tCTM; return tCTM;
} }

Просмотреть файл

@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg"
class="reftest-wait">
<!-- Test to be sure that a zero-sized-in-one-dimension viewBox doesn't
make us fail assertions. -->
<script>
document.addEventListener("MozReftestInvalidate", waitAndFinish, false);
function waitAndFinish() {
// Sadly, MozReftestInvalidate fires sooner than PaintPattern here, so
// we need to wait a little bit to give PaintPattern a chance to hit
// this bug.
setTimeout(finish, 100);
}
function finish() {
document.documentElement.removeAttribute("class");
}
</script>
<pattern id="test" viewBox="0 0 1 0">
<rect/>
</pattern>
<rect width="200" height="200" fill="url(#test)"/>
</svg>

После

(image error) Размер: 730 B

Просмотреть файл

@ -0,0 +1,23 @@
<svg xmlns="http://www.w3.org/2000/svg"
class="reftest-wait">
<!-- Test to be sure that a zero-sized-in-one-dimension viewBox doesn't
make us fail assertions. -->
<script>
document.addEventListener("MozReftestInvalidate", waitAndFinish, false);
function waitAndFinish() {
// Sadly, MozReftestInvalidate fires sooner than PaintPattern here, so
// we need to wait a little bit to give PaintPattern a chance to hit
// this bug.
setTimeout(finish, 100);
}
function finish() {
document.documentElement.removeAttribute("class");
}
</script>
<pattern id="test" viewBox="0 0 0 1">
<rect/>
</pattern>
<rect width="200" height="200" fill="url(#test)"/>
</svg>

После

(image error) Размер: 730 B

Просмотреть файл

@ -119,3 +119,5 @@ load 682411-1.svg
load 692203-1.svg load 692203-1.svg
load 692203-2.svg load 692203-2.svg
load 693424-1.svg load 693424-1.svg
load 709920-1.svg
load 709920-2.svg

Просмотреть файл

@ -20,6 +20,7 @@
* *
* Contributor(s): * Contributor(s):
* Brad Lassey <blassey@mozilla.com> * Brad Lassey <blassey@mozilla.com>
* Lucas Rocha <lucasr@mozilla.com>
* *
* Alternatively, the contents of this file may be used under the terms of * Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or * either the GNU General Public License Version 2 or later (the "GPL"), or
@ -37,120 +38,183 @@
package org.mozilla.gecko; package org.mozilla.gecko;
import android.app.Activity; import java.io.File;
import android.os.Bundle; import java.io.FileInputStream;
import android.widget.*; import java.io.FileNotFoundException;
import android.database.*; import java.io.InputStream;
import android.view.*; import java.util.zip.ZipEntry;
import android.graphics.*; import java.util.zip.ZipFile;
import android.content.*;
import android.provider.Browser;
import android.util.Log;
import java.util.Date;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Browser;
import android.util.Log;
import android.view.View;
import android.widget.ListAdapter;
import android.widget.GridView;
import android.widget.SimpleCursorAdapter;
import java.io.*;
import java.util.zip.*;
import android.os.Handler;
import org.json.*;
import android.util.AttributeSet;
import org.json.JSONArray;
import org.json.JSONObject;
import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.BrowserDB.URLColumns; import org.mozilla.gecko.db.BrowserDB.URLColumns;
public class AboutHomeContent extends LinearLayout { import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.SpannableString;
import android.text.style.UnderlineSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
public class AboutHomeContent extends ScrollView {
private static final String LOGTAG = "GeckoAboutHome";
private static final int NUMBER_OF_TOP_SITES_PORTRAIT = 4;
private static final int NUMBER_OF_TOP_SITES_LANDSCAPE = 3;
private static final int NUMBER_OF_COLS_PORTRAIT = 2;
private static final int NUMBER_OF_COLS_LANDSCAPE = 3;
private boolean mInflated;
private Cursor mCursor;
UriLoadCallback mUriLoadCallback = null;
protected SimpleCursorAdapter mTopSitesAdapter;
protected GridView mTopSitesGrid;
protected ArrayAdapter<String> mAddonsAdapter;
protected ListView mAddonsList;
public interface UriLoadCallback { public interface UriLoadCallback {
public void callback(String uriSpec); public void callback(String uriSpec);
} }
UriLoadCallback mUriLoadCallback = null;
void setUriLoadCallback(UriLoadCallback uriLoadCallback) {
mUriLoadCallback = uriLoadCallback;
}
public AboutHomeContent(Context context, AttributeSet attrs) { public AboutHomeContent(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
mInflated = false;
} }
private static final String LOGTAG = "GeckoAboutHome"; @Override
private static final String TITLE_KEY = "title"; protected void onFinishInflate() {
private static final int NUMBER_OF_TOP_SITES = 3; super.onFinishInflate();
private static final int kTileWidth = 122;
private Cursor mCursor; // HACK: Without this, the onFinishInflate is called twice
private Uri mUri; // This issue is due to a bug when Android inflates a layout with a
private String mTitle; // parent. Fixed in Honeycomb
if (mInflated)
protected ListAdapter mGridAdapter;
protected ArrayAdapter<String> mAddonAdapter;
protected GridView mGrid;
protected ListView mAddonList;
private Handler mHandler = new Handler();
public void onActivityContentChanged(Activity activity) {
mGrid = (GridView)findViewById(R.id.grid);
if (mGrid == null)
return; return;
mGrid.setOnItemClickListener(mGridOnClickListener); mInflated = true;
// we want to do this: mGrid.setNumColumns(GridView.AUTO_FIT); but it doesn't work mTopSitesGrid = (GridView)findViewById(R.id.top_sites_grid);
Display display = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); mTopSitesGrid.setOnItemClickListener(new AdapterView.OnItemClickListener() {
int width = display.getWidth(); public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
mGrid.setNumColumns((int) Math.floor(width / kTileWidth)); Cursor c = (Cursor) parent.getItemAtPosition(position);
mAddonList = (ListView)findViewById(R.id.recommended_addon_list);
GeckoApp.mAppContext.mMainHandler.post(new Runnable() { String spec = c.getString(c.getColumnIndex(URLColumns.URL));
public void run() { Log.i(LOGTAG, "clicked: " + spec);
mGrid.setAdapter(mGridAdapter);
if (mUriLoadCallback != null)
mUriLoadCallback.callback(spec);
}
});
mAddonsList = (ListView) findViewById(R.id.recommended_addons_list);
TextView allTopSitesText = (TextView) findViewById(R.id.all_top_sites_text);
allTopSitesText.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GeckoApp.mAppContext.showAwesomebar(AwesomeBar.Type.EDIT);
}
});
TextView allAddonsText = (TextView) findViewById(R.id.all_addons_text);
allAddonsText.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (mUriLoadCallback != null)
mUriLoadCallback.callback("about:addons");
} }
}); });
} }
private int getNumberOfTopSites() {
Configuration config = getContext().getResources().getConfiguration();
if (config.orientation == Configuration.ORIENTATION_LANDSCAPE)
return NUMBER_OF_TOP_SITES_LANDSCAPE;
else
return NUMBER_OF_TOP_SITES_PORTRAIT;
}
private AdapterView.OnItemClickListener mGridOnClickListener = new AdapterView.OnItemClickListener() { private int getNumberOfColumns() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) Configuration config = getContext().getResources().getConfiguration();
{ if (config.orientation == Configuration.ORIENTATION_LANDSCAPE)
onGridItemClick((GridView)parent, v, position, id); return NUMBER_OF_COLS_LANDSCAPE;
} else
}; return NUMBER_OF_COLS_PORTRAIT;
}
void init(final Activity activity) { void init(final Activity activity) {
GeckoAppShell.getHandler().post(new Runnable() { GeckoAppShell.getHandler().post(new Runnable() {
public void run() { public void run() {
if (mCursor != null)
activity.stopManagingCursor(mCursor);
ContentResolver resolver = GeckoApp.mAppContext.getContentResolver(); ContentResolver resolver = GeckoApp.mAppContext.getContentResolver();
mCursor = BrowserDB.filter(resolver, "", NUMBER_OF_TOP_SITES); mCursor = BrowserDB.filter(resolver, "", NUMBER_OF_TOP_SITES_PORTRAIT);
activity.startManagingCursor(mCursor); activity.startManagingCursor(mCursor);
onActivityContentChanged(activity); mTopSitesAdapter = new TopSitesCursorAdapter(activity,
mAddonAdapter = new ArrayAdapter<String>(activity, R.layout.abouthome_addon_list_item); R.layout.abouthome_topsite_item,
mCursor,
new String[] { URLColumns.TITLE,
URLColumns.THUMBNAIL },
new int[] { R.id.title, R.id.thumbnail });
if (mAddonsAdapter == null)
mAddonsAdapter = new ArrayAdapter<String>(activity, R.layout.abouthome_addon_row);
GeckoApp.mAppContext.mMainHandler.post(new Runnable() { GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
public void run() { public void run() {
final SimpleCursorAdapter gridAdapter = mTopSitesGrid.setNumColumns(getNumberOfColumns());
new SimpleCursorAdapter(activity, R.layout.abouthome_grid_box, mCursor,
new String[] { URLColumns.TITLE, mTopSitesGrid.setAdapter(mTopSitesAdapter);
URLColumns.FAVICON, mTopSitesAdapter.setViewBinder(new TopSitesViewBinder());
URLColumns.URL,
URLColumns.THUMBNAIL }, mAddonsList.setAdapter(mAddonsAdapter);
new int[] {R.id.bookmark_title, R.id.bookmark_icon, R.id.bookmark_url, R.id.screenshot});
mGrid.setAdapter(gridAdapter);
gridAdapter.setViewBinder(new AwesomeCursorViewBinder());
mAddonList.setAdapter(mAddonAdapter);
} }
}); });
readRecommendedAddons(activity); readRecommendedAddons(activity);
} }
}); });
} }
public void setUriLoadCallback(UriLoadCallback uriLoadCallback) {
mUriLoadCallback = uriLoadCallback;
}
public void onActivityContentChanged(Activity activity) {
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
public void run() {
mTopSitesGrid.setAdapter(mTopSitesAdapter);
}
});
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
mTopSitesGrid.setNumColumns(getNumberOfColumns());
mTopSitesAdapter.notifyDataSetChanged();
super.onConfigurationChanged(newConfig);
}
InputStream getProfileRecommendedAddonsStream() { InputStream getProfileRecommendedAddonsStream() {
try { try {
File profileDir = GeckoApp.mAppContext.getProfileDir(); File profileDir = GeckoApp.mAppContext.getProfileDir();
@ -172,7 +236,11 @@ public class AboutHomeContent extends LinearLayout {
return is; return is;
File applicationPackage = new File(activity.getApplication().getPackageResourcePath()); File applicationPackage = new File(activity.getApplication().getPackageResourcePath());
ZipFile zip = new ZipFile(applicationPackage); ZipFile zip = new ZipFile(applicationPackage);
if (zip == null)
return null;
ZipEntry fileEntry = zip.getEntry("recommended-addons.json"); ZipEntry fileEntry = zip.getEntry("recommended-addons.json");
if (fileEntry == null)
return null;
return zip.getInputStream(fileEntry); return zip.getInputStream(fileEntry);
} }
@ -182,6 +250,8 @@ public class AboutHomeContent extends LinearLayout {
try { try {
byte[] buf = new byte[32768]; byte[] buf = new byte[32768];
InputStream fileStream = getRecommendedAddonsStream(activity); InputStream fileStream = getRecommendedAddonsStream(activity);
if (fileStream == null)
return;
StringBuffer jsonString = new StringBuffer(); StringBuffer jsonString = new StringBuffer();
int read = 0; int read = 0;
while ((read = fileStream.read(buf, 0, 32768)) != -1) { while ((read = fileStream.read(buf, 0, 32768)) != -1) {
@ -193,7 +263,7 @@ public class AboutHomeContent extends LinearLayout {
try { try {
for (int i = 0; i < array.length(); i++) { for (int i = 0; i < array.length(); i++) {
JSONObject jsonobj = array.getJSONObject(i); JSONObject jsonobj = array.getJSONObject(i);
mAddonAdapter.add(jsonobj.getString("name")); mAddonsAdapter.add(jsonobj.getString("name"));
Log.i("GeckoAddons", "addon #" + i +": " + jsonobj.getString("name")); Log.i("GeckoAddons", "addon #" + i +": " + jsonobj.getString("name"));
} }
} catch (Exception e) { } catch (Exception e) {
@ -208,91 +278,111 @@ public class AboutHomeContent extends LinearLayout {
}); });
} }
protected void onGridItemClick(GridView l, View v, int position, long id) { public static class TopSitesGridView extends GridView {
mCursor.moveToPosition(position); public TopSitesGridView(Context context, AttributeSet attrs) {
String spec = mCursor.getString(mCursor.getColumnIndex(URLColumns.URL)); super(context, attrs);
Log.i(LOGTAG, "clicked: " + spec); }
if (mUriLoadCallback != null)
mUriLoadCallback.callback(spec); @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// This is to ensure that the GridView always has a size that shows
// all items with no need for scrolling.
int expandedHeightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandedHeightSpec);
}
} }
} public class TopSitesCursorAdapter extends SimpleCursorAdapter {
class AwesomeCursorViewBinder implements SimpleCursorAdapter.ViewBinder { public TopSitesCursorAdapter(Context context, int layout, Cursor c,
private static final String LOGTAG = "GeckoAwesomeCursorViewBinder"; String[] from, int[] to) {
super(context, layout, c, from, to);
}
private boolean updateImage(View view, Cursor cursor, int faviconIndex) { @Override
byte[] b = cursor.getBlob(faviconIndex); public int getCount() {
ImageView favicon = (ImageView) view; return Math.min(super.getCount(), getNumberOfTopSites());
}
}
if (b == null) { class TopSitesViewBinder implements SimpleCursorAdapter.ViewBinder {
favicon.setImageResource(R.drawable.favicon); private boolean updateThumbnail(View view, Cursor cursor, int thumbIndex) {
} else { byte[] b = cursor.getBlob(thumbIndex);
try { ImageView thumbnail = (ImageView) view;
Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
favicon.setImageBitmap(bitmap); if (b == null) {
} catch (OutOfMemoryError oom) { thumbnail.setImageResource(R.drawable.abouthome_topsite_placeholder);
Log.e(LOGTAG, "Unable to load thumbnail bitmap", oom); } else {
favicon.setImageResource(R.drawable.favicon); try {
Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
thumbnail.setImageBitmap(bitmap);
} catch (OutOfMemoryError oom) {
Log.e(LOGTAG, "Unable to load thumbnail bitmap", oom);
thumbnail.setImageResource(R.drawable.abouthome_topsite_placeholder);
}
} }
return true;
} }
return true; private boolean updateTitle(View view, Cursor cursor, int titleIndex) {
String title = cursor.getString(titleIndex);
TextView titleView = (TextView) view;
// Use the URL instead of an empty title for consistency with the normal URL
// bar view - this is the equivalent of getDisplayTitle() in Tab.java
if (title == null || title.length() == 0) {
int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL);
title = cursor.getString(urlIndex);
}
titleView.setText(title);
return true;
}
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
int titleIndex = cursor.getColumnIndexOrThrow(URLColumns.TITLE);
if (columnIndex == titleIndex) {
return updateTitle(view, cursor, titleIndex);
}
int thumbIndex = cursor.getColumnIndexOrThrow(URLColumns.THUMBNAIL);
if (columnIndex == thumbIndex) {
return updateThumbnail(view, cursor, thumbIndex);
}
// Other columns are handled automatically
return false;
}
} }
private boolean updateTitle(View view, Cursor cursor, int titleIndex) { public static class AddonsListView extends ListView {
String title = cursor.getString(titleIndex); public AddonsListView(Context context, AttributeSet attrs) {
TextView titleView = (TextView)view; super(context, attrs);
// Use the URL instead of an empty title for consistency with the normal URL
// bar view - this is the equivalent of getDisplayTitle() in Tab.java
if (title == null || title.length() == 0) {
int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL);
title = cursor.getString(urlIndex);
} }
titleView.setText(title); @Override
return true; protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// This is to ensure that the ListView always has a size that shows
// all items with no need for scrolling.
int expandedHeightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandedHeightSpec);
}
} }
private boolean updateUrl(View view, Cursor cursor, int urlIndex) { public static class LinkTextView extends TextView {
String title = cursor.getString(urlIndex); public LinkTextView(Context context, AttributeSet attrs) {
TextView urlView = (TextView)view; super(context, attrs);
if (title != null) { }
int index;
if ((index = title.indexOf("://")) != -1) @Override
title = title.substring(index + 3); public void setText(CharSequence text, BufferType type) {
if (title.startsWith("www.")) SpannableString content = new SpannableString(text + " \u00BB");
title = title.substring(4); content.setSpan(new UnderlineSpan(), 0, text.length(), 0);
if (title.endsWith("/"))
title = title.substring(0, title.length() -1); super.setText(content, BufferType.SPANNABLE);
} }
urlView.setText(title);
return true;
} }
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
int faviconIndex = cursor.getColumnIndexOrThrow(URLColumns.FAVICON);
if (columnIndex == faviconIndex) {
return updateImage(view, cursor, faviconIndex);
}
int titleIndex = cursor.getColumnIndexOrThrow(URLColumns.TITLE);
if (columnIndex == titleIndex) {
return updateTitle(view, cursor, titleIndex);
}
int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL);
if (columnIndex == urlIndex) {
return updateUrl(view, cursor, urlIndex);
}
int thumbIndex = cursor.getColumnIndexOrThrow(URLColumns.THUMBNAIL);
if (columnIndex == thumbIndex) {
return updateImage(view, cursor, thumbIndex);
}
// Other columns are handled automatically
return false;
}
} }

Просмотреть файл

@ -132,7 +132,8 @@
android:windowSoftInputMode="stateAlwaysVisible|adjustResize"/> android:windowSoftInputMode="stateAlwaysVisible|adjustResize"/>
<activity android:name="org.mozilla.gecko.TabsTray" <activity android:name="org.mozilla.gecko.TabsTray"
android:theme="@style/Gecko.Translucent"/> android:theme="@style/Gecko.Translucent"
android:launchMode="singleTask"/>
<activity android:name="org.mozilla.gecko.GeckoPreferences" <activity android:name="org.mozilla.gecko.GeckoPreferences"
android:theme="@style/Gecko.TitleBar" android:theme="@style/Gecko.TitleBar"

Просмотреть файл

@ -84,7 +84,7 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
mAwesomeTabs = (AwesomeBarTabs) findViewById(R.id.awesomebar_tabs); mAwesomeTabs = (AwesomeBarTabs) findViewById(R.id.awesomebar_tabs);
mAwesomeTabs.setOnUrlOpenListener(new AwesomeBarTabs.OnUrlOpenListener() { mAwesomeTabs.setOnUrlOpenListener(new AwesomeBarTabs.OnUrlOpenListener() {
public void onUrlOpen(String url) { public void onUrlOpen(String url) {
openUrlAndFinish(url); submitAndFinish(url);
} }
public void onSearch(String engine) { public void onSearch(String engine) {
@ -95,7 +95,7 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
mGoButton = (ImageButton) findViewById(R.id.awesomebar_button); mGoButton = (ImageButton) findViewById(R.id.awesomebar_button);
mGoButton.setOnClickListener(new Button.OnClickListener() { mGoButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
openUrlAndFinish(mText.getText().toString()); submitAndFinish(mText.getText().toString());
} }
}); });
@ -166,7 +166,7 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
if (event.getAction() != KeyEvent.ACTION_DOWN) if (event.getAction() != KeyEvent.ACTION_DOWN)
return true; return true;
openUrlAndFinish(mText.getText().toString()); submitAndFinish(mText.getText().toString());
return true; return true;
} else { } else {
return false; return false;
@ -209,6 +209,34 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
return true; return true;
} }
/*
* This method tries to guess if the given string could be a search query or URL
* Search examples:
* foo
* foo bar.com
* foo http://bar.com
*
* URL examples
* foo.com
* foo.c
* :foo
* http://foo.com bar
*/
private boolean isSearchUrl(String text) {
text = text.trim();
if (text.length() == 0)
return false;
int colon = text.indexOf(':');
int dot = text.indexOf('.');
int space = text.indexOf(' ');
// If a space is found before any dot or colon, we assume this is a search query
boolean spacedOut = space > -1 && (space < colon || space < dot);
return spacedOut || (dot == -1 && colon == -1);
}
private void updateGoButton(String text) { private void updateGoButton(String text) {
if (text.length() == 0) { if (text.length() == 0) {
mGoButton.setVisibility(View.GONE); mGoButton.setVisibility(View.GONE);
@ -218,13 +246,19 @@ public class AwesomeBar extends Activity implements GeckoEventListener {
mGoButton.setVisibility(View.VISIBLE); mGoButton.setVisibility(View.VISIBLE);
int imageResource = R.drawable.ic_awesomebar_go; int imageResource = R.drawable.ic_awesomebar_go;
if (!GeckoAppShell.canCreateFixupURI(text)) { if (isSearchUrl(text))
imageResource = R.drawable.ic_awesomebar_search; imageResource = R.drawable.ic_awesomebar_search;
}
mGoButton.setImageResource(imageResource); mGoButton.setImageResource(imageResource);
} }
private void submitAndFinish(String url) {
if (isSearchUrl(url))
openSearchAndFinish(url, "__default__");
else
openUrlAndFinish(url);
}
private void cancelAndFinish() { private void cancelAndFinish() {
setResult(Activity.RESULT_CANCELED); setResult(Activity.RESULT_CANCELED);
finish(); finish();

Просмотреть файл

@ -456,11 +456,13 @@ abstract public class GeckoApp
MenuItem forward = aMenu.findItem(R.id.forward); MenuItem forward = aMenu.findItem(R.id.forward);
MenuItem share = aMenu.findItem(R.id.share); MenuItem share = aMenu.findItem(R.id.share);
MenuItem agentMode = aMenu.findItem(R.id.agent_mode); MenuItem agentMode = aMenu.findItem(R.id.agent_mode);
MenuItem saveAsPDF = aMenu.findItem(R.id.save_as_pdf);
if (tab == null) { if (tab == null) {
bookmark.setEnabled(false); bookmark.setEnabled(false);
forward.setEnabled(false); forward.setEnabled(false);
share.setEnabled(false); share.setEnabled(false);
saveAsPDF.setEnabled(false);
return true; return true;
} }
@ -481,7 +483,11 @@ abstract public class GeckoApp
// Don't share about:, chrome: and file: URIs // Don't share about:, chrome: and file: URIs
String scheme = Uri.parse(tab.getURL()).getScheme(); String scheme = Uri.parse(tab.getURL()).getScheme();
share.setEnabled(!scheme.equals("about") && !scheme.equals("chrome") && !scheme.equals("file")); share.setEnabled(!(scheme.equals("about") || scheme.equals("chrome") || scheme.equals("file")));
// Disable save as PDF for about:home and xul pages
saveAsPDF.setEnabled(!(tab.getURL().equals("about:home") ||
tab.getContentType().equals("application/vnd.mozilla.xul+xml")));
return true; return true;
} }
@ -667,7 +673,8 @@ abstract public class GeckoApp
tab.setFaviconLoadId(id); tab.setFaviconLoadId(id);
} }
void handleLocationChange(final int tabId, final String uri) { void handleLocationChange(final int tabId, final String uri,
final String documentURI, final String contentType) {
final Tab tab = Tabs.getInstance().getTab(tabId); final Tab tab = Tabs.getInstance().getTab(tabId);
if (tab == null) if (tab == null)
return; return;
@ -681,6 +688,8 @@ abstract public class GeckoApp
String oldBaseURI = tab.getURL(); String oldBaseURI = tab.getURL();
tab.updateURL(uri); tab.updateURL(uri);
tab.setDocumentURI(documentURI);
tab.setContentType(contentType);
String baseURI = uri; String baseURI = uri;
if (baseURI.indexOf('#') != -1) if (baseURI.indexOf('#') != -1)
@ -848,8 +857,10 @@ abstract public class GeckoApp
} else if (event.equals("Content:LocationChange")) { } else if (event.equals("Content:LocationChange")) {
final int tabId = message.getInt("tabID"); final int tabId = message.getInt("tabID");
final String uri = message.getString("uri"); final String uri = message.getString("uri");
final String documentURI = message.getString("documentURI");
final String contentType = message.getString("contentType");
Log.i(LOGTAG, "URI - " + uri); Log.i(LOGTAG, "URI - " + uri);
handleLocationChange(tabId, uri); handleLocationChange(tabId, uri, documentURI, contentType);
} else if (event.equals("Content:SecurityChange")) { } else if (event.equals("Content:SecurityChange")) {
final int tabId = message.getInt("tabID"); final int tabId = message.getInt("tabID");
final String mode = message.getString("mode"); final String mode = message.getString("mode");
@ -902,7 +913,11 @@ abstract public class GeckoApp
setLaunchState(GeckoApp.LaunchState.GeckoRunning); setLaunchState(GeckoApp.LaunchState.GeckoRunning);
GeckoAppShell.sendPendingEventsToGecko(); GeckoAppShell.sendPendingEventsToGecko();
connectGeckoLayerClient(); connectGeckoLayerClient();
Looper.myQueue().addIdleHandler(new UpdateIdleHandler()); GeckoAppShell.getHandler().post(new Runnable() {
public void run() {
Looper.myQueue().addIdleHandler(new UpdateIdleHandler());
}
});
} else if (event.equals("ToggleChrome:Hide")) { } else if (event.equals("ToggleChrome:Hide")) {
mMainHandler.post(new Runnable() { mMainHandler.post(new Runnable() {
public void run() { public void run() {
@ -1436,7 +1451,6 @@ abstract public class GeckoApp
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll() .detectAll()
.penaltyLog() .penaltyLog()
.penaltyDialog()
.build()); .build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()

Просмотреть файл

@ -130,7 +130,6 @@ public class GeckoAppShell
public static native void onChangeNetworkLinkStatus(String status); public static native void onChangeNetworkLinkStatus(String status);
public static native void reportJavaCrash(String stack); public static native void reportJavaCrash(String stack);
public static native void notifyUriVisited(String uri); public static native void notifyUriVisited(String uri);
public static native boolean canCreateFixupURI(String text);
public static native void processNextNativeEvent(); public static native void processNextNativeEvent();

Просмотреть файл

@ -38,6 +38,7 @@
package org.mozilla.gecko; package org.mozilla.gecko;
import java.lang.CharSequence;
import java.util.ArrayList; import java.util.ArrayList;
import android.os.Build; import android.os.Build;
@ -131,8 +132,12 @@ public class GeckoPreferences
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference, Object newValue) {
String prefName = preference.getKey(); String prefName = preference.getKey();
setPreference(prefName, newValue); setPreference(prefName, newValue);
if (preference instanceof ListPreference) if (preference instanceof ListPreference) {
((ListPreference)preference).setSummary((String)newValue); // We need to find the entry for the new value
int newIndex = ((ListPreference)preference).findIndexOfValue((String) newValue);
CharSequence newEntry = ((ListPreference)preference).getEntries()[newIndex];
((ListPreference)preference).setSummary(newEntry);
}
if (preference instanceof LinkPreference) if (preference instanceof LinkPreference)
finish(); finish();
return true; return true;
@ -177,6 +182,9 @@ public class GeckoPreferences
GeckoAppShell.getMainHandler().post(new Runnable() { GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() { public void run() {
((ListPreference)pref).setValue(value); ((ListPreference)pref).setValue(value);
// Set the summary string to the current entry
CharSequence selectedEntry = ((ListPreference)pref).getEntry();
((ListPreference)pref).setSummary(selectedEntry);
} }
}); });
} }

Просмотреть файл

@ -194,8 +194,8 @@ RES_LAYOUT = \
res/layout/list_item_header.xml \ res/layout/list_item_header.xml \
res/layout/select_dialog_list.xml \ res/layout/select_dialog_list.xml \
res/layout/abouthome_content.xml \ res/layout/abouthome_content.xml \
res/layout/abouthome_grid_box.xml \ res/layout/abouthome_topsite_item.xml \
res/layout/abouthome_addon_list_item.xml \ res/layout/abouthome_addon_row.xml \
$(NULL) $(NULL)
RES_LAYOUT_V11 = \ RES_LAYOUT_V11 = \
@ -206,6 +206,7 @@ RES_LAYOUT_V11 = \
RES_VALUES = \ RES_VALUES = \
res/values/defaults.xml \ res/values/defaults.xml \
res/values/arrays.xml \
res/values/colors.xml \ res/values/colors.xml \
res/values/styles.xml \ res/values/styles.xml \
res/values/themes.xml \ res/values/themes.xml \
@ -224,6 +225,11 @@ RES_ANIM = \
$(NULL) $(NULL)
RES_DRAWABLE_MDPI_V8 = \ RES_DRAWABLE_MDPI_V8 = \
res/drawable-mdpi-v8/abouthome_icon.png \
res/drawable-mdpi-v8/abouthome_logo.png \
res/drawable-mdpi-v8/abouthome_separator.9.png \
res/drawable-mdpi-v8/abouthome_topsite_placeholder.png \
res/drawable-mdpi-v8/abouthome_topsite_shadow.9.png \
res/drawable-mdpi-v8/ic_awesomebar_go.png \ res/drawable-mdpi-v8/ic_awesomebar_go.png \
res/drawable-mdpi-v8/ic_awesomebar_search.png \ res/drawable-mdpi-v8/ic_awesomebar_search.png \
res/drawable-mdpi-v8/ic_menu_bookmark_add.png \ res/drawable-mdpi-v8/ic_menu_bookmark_add.png \
@ -246,6 +252,11 @@ RES_DRAWABLE_MDPI_V8 = \
$(NULL) $(NULL)
RES_DRAWABLE_HDPI_V8 = \ RES_DRAWABLE_HDPI_V8 = \
res/drawable-hdpi-v8/abouthome_icon.png \
res/drawable-hdpi-v8/abouthome_logo.png \
res/drawable-hdpi-v8/abouthome_separator.9.png \
res/drawable-hdpi-v8/abouthome_topsite_placeholder.png \
res/drawable-hdpi-v8/abouthome_topsite_shadow.9.png \
res/drawable-hdpi-v8/ic_awesomebar_go.png \ res/drawable-hdpi-v8/ic_awesomebar_go.png \
res/drawable-hdpi-v8/ic_awesomebar_search.png \ res/drawable-hdpi-v8/ic_awesomebar_search.png \
res/drawable-hdpi-v8/ic_menu_bookmark_add.png \ res/drawable-hdpi-v8/ic_menu_bookmark_add.png \
@ -318,6 +329,11 @@ RES_DRAWABLE_HDPI_V11 = \
$(NULL) $(NULL)
RES_DRAWABLE_XHDPI_V11 = \ RES_DRAWABLE_XHDPI_V11 = \
res/drawable-xhdpi-v11/abouthome_icon.png \
res/drawable-xhdpi-v11/abouthome_logo.png \
res/drawable-xhdpi-v11/abouthome_separator.9.png \
res/drawable-xhdpi-v11/abouthome_topsite_placeholder.png \
res/drawable-xhdpi-v11/abouthome_topsite_shadow.9.png \
res/drawable-xhdpi-v11/ic_awesomebar_go.png \ res/drawable-xhdpi-v11/ic_awesomebar_go.png \
res/drawable-xhdpi-v11/ic_awesomebar_search.png \ res/drawable-xhdpi-v11/ic_awesomebar_search.png \
res/drawable-xhdpi-v11/ic_menu_bookmark_add.png \ res/drawable-xhdpi-v11/ic_menu_bookmark_add.png \
@ -346,7 +362,11 @@ MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/crash_reporter.p
RES_LAYOUT += res/layout/crash_reporter.xml RES_LAYOUT += res/layout/crash_reporter.xml
endif endif
MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/address_bar_bg.xml \ MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/abouthome_bg.png \
mobile/android/base/resources/drawable/abouthome_bg_repeat.xml \
mobile/android/base/resources/drawable/abouthome_topsites_bg.png \
mobile/android/base/resources/drawable/abouthome_topsites_bg_repeat.xml \
mobile/android/base/resources/drawable/address_bar_bg.xml \
mobile/android/base/resources/drawable/address_bar_url_default.xml \ mobile/android/base/resources/drawable/address_bar_url_default.xml \
mobile/android/base/resources/drawable/address_bar_url_pressed.xml \ mobile/android/base/resources/drawable/address_bar_url_pressed.xml \
mobile/android/base/resources/drawable/awesomebar_tab_focus.xml \ mobile/android/base/resources/drawable/awesomebar_tab_focus.xml \
@ -387,8 +407,6 @@ MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/address_bar_bg.x
mobile/android/base/resources/drawable/tabs_tray_bg.9.png \ mobile/android/base/resources/drawable/tabs_tray_bg.9.png \
mobile/android/base/resources/drawable/checkerboard.png \ mobile/android/base/resources/drawable/checkerboard.png \
mobile/android/base/resources/drawable/shadow.png \ mobile/android/base/resources/drawable/shadow.png \
mobile/android/base/resources/drawable/rounded_grey_border.xml \
mobile/android/base/resources/drawable/rounded_grey_box.xml \
$(NULL) $(NULL)
@ -436,6 +454,7 @@ $(RES_VALUES): \
$(srcdir)/resources/values/colors.xml \ $(srcdir)/resources/values/colors.xml \
$(srcdir)/resources/values/styles.xml \ $(srcdir)/resources/values/styles.xml \
$(srcdir)/resources/values/themes.xml \ $(srcdir)/resources/values/themes.xml \
$(srcdir)/resources/values/arrays.xml \
$(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/res/values/defaults.xml $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/res/values/defaults.xml
$(NSINSTALL) -D res/values $(NSINSTALL) -D res/values
$(NSINSTALL) $^ res/values $(NSINSTALL) $^ res/values

Просмотреть файл

@ -75,6 +75,8 @@ public class Tab {
private HashMap<String, DoorHanger> mDoorHangers; private HashMap<String, DoorHanger> mDoorHangers;
private long mFaviconLoadId; private long mFaviconLoadId;
private AgentMode mAgentMode = AgentMode.MOBILE; private AgentMode mAgentMode = AgentMode.MOBILE;
private String mDocumentURI;
private String mContentType;
static class HistoryEntry { static class HistoryEntry {
public final String mUri; // must never be null public final String mUri; // must never be null
@ -103,6 +105,8 @@ public class Tab {
mBookmark = false; mBookmark = false;
mDoorHangers = new HashMap<String, DoorHanger>(); mDoorHangers = new HashMap<String, DoorHanger>();
mFaviconLoadId = 0; mFaviconLoadId = 0;
mDocumentURI = "";
mContentType = "";
} }
public int getId() { public int getId() {
@ -182,6 +186,22 @@ public class Tab {
} }
} }
public void setDocumentURI(String documentURI) {
mDocumentURI = documentURI;
}
public String getDocumentURI() {
return mDocumentURI;
}
public void setContentType(String contentType) {
mContentType = contentType;
}
public String getContentType() {
return mContentType;
}
public void updateTitle(String title) { public void updateTitle(String title) {
mTitle = (title == null ? "" : title); mTitle = (title == null ? "" : title);

Просмотреть файл

@ -93,6 +93,13 @@ public class BrowserContract {
public static final class Bookmarks implements CommonColumns, URLColumns, ImageColumns, SyncColumns { public static final class Bookmarks implements CommonColumns, URLColumns, ImageColumns, SyncColumns {
private Bookmarks() {} private Bookmarks() {}
public static final String MOBILE_FOLDER_GUID = "mobile";
public static final String PLACES_FOLDER_GUID = "places";
public static final String MENU_FOLDER_GUID = "menu";
public static final String TAGS_FOLDER_GUID = "tags";
public static final String TOOLBAR_FOLDER_GUID = "toolbar";
public static final String UNFILED_FOLDER_GUID = "unfiled";
public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "bookmarks"); public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "bookmarks");
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/bookmark"; public static final String CONTENT_TYPE = "vnd.android.cursor.dir/bookmark";
@ -133,4 +140,4 @@ public class BrowserContract {
public static final String VERSION = "version"; public static final String VERSION = "version";
} }
} }

Просмотреть файл

@ -83,6 +83,9 @@ public class BrowserProvider extends ContentProvider {
static final String TABLE_HISTORY = "history"; static final String TABLE_HISTORY = "history";
static final String TABLE_IMAGES = "images"; static final String TABLE_IMAGES = "images";
static final String VIEW_BOOKMARKS_WITH_IMAGES = "bookmarks_with_images";
static final String VIEW_HISTORY_WITH_IMAGES = "history_with_images";
// Bookmark matches // Bookmark matches
static final int BOOKMARKS = 100; static final int BOOKMARKS = 100;
static final int BOOKMARKS_ID = 101; static final int BOOKMARKS_ID = 101;
@ -106,12 +109,20 @@ public class BrowserProvider extends ContentProvider {
static final String DEFAULT_HISTORY_SORT_ORDER = History.DATE_LAST_VISITED + " DESC"; static final String DEFAULT_HISTORY_SORT_ORDER = History.DATE_LAST_VISITED + " DESC";
static final String TABLE_BOOKMARKS_JOIN_IMAGES = TABLE_BOOKMARKS + " LEFT OUTER JOIN " + static final String TABLE_BOOKMARKS_JOIN_IMAGES = TABLE_BOOKMARKS + " LEFT OUTER JOIN " +
TABLE_IMAGES + " ON " + qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks.URL) + "(SELECT " + Images.URL + ", " + Images.FAVICON + ", " + Images.THUMBNAIL + " FROM " +
" = " + qualifyColumnValue(TABLE_IMAGES, Images.URL); TABLE_IMAGES + ", " + TABLE_BOOKMARKS + " WHERE " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " +
qualifyColumn(TABLE_IMAGES, Images.URL) + ") AS bookmark_images ON " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " +
qualifyColumn("bookmark_images", Images.URL);
static final String TABLE_HISTORY_JOIN_IMAGES = TABLE_HISTORY + " LEFT OUTER JOIN " + static final String TABLE_HISTORY_JOIN_IMAGES = TABLE_HISTORY + " LEFT OUTER JOIN " +
TABLE_IMAGES + " ON " + qualifyColumnValue(TABLE_HISTORY, History.URL) + "(SELECT " + Images.URL + ", " + Images.FAVICON + ", " + Images.THUMBNAIL + " FROM " +
" = " + qualifyColumnValue(TABLE_IMAGES, Images.URL); TABLE_IMAGES + ", " + TABLE_HISTORY + " WHERE " +
qualifyColumn(TABLE_HISTORY, History.URL) + " = " +
qualifyColumn(TABLE_IMAGES, Images.URL) + ") AS history_images ON " +
qualifyColumn(TABLE_HISTORY, History.URL) + " = " +
qualifyColumn("history_images", Images.URL);
static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
@ -131,7 +142,7 @@ public class BrowserProvider extends ContentProvider {
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "bookmarks/folder/#", BOOKMARKS_FOLDER_ID); URI_MATCHER.addURI(BrowserContract.AUTHORITY, "bookmarks/folder/#", BOOKMARKS_FOLDER_ID);
map = BOOKMARKS_PROJECTION_MAP; map = BOOKMARKS_PROJECTION_MAP;
map.put(Bookmarks._ID, qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID)); map.put(Bookmarks._ID, Bookmarks._ID);
map.put(Bookmarks.TITLE, Bookmarks.TITLE); map.put(Bookmarks.TITLE, Bookmarks.TITLE);
map.put(Bookmarks.URL, Bookmarks.URL); map.put(Bookmarks.URL, Bookmarks.URL);
map.put(Bookmarks.FAVICON, Bookmarks.FAVICON); map.put(Bookmarks.FAVICON, Bookmarks.FAVICON);
@ -142,41 +153,41 @@ public class BrowserProvider extends ContentProvider {
map.put(Bookmarks.TAGS, Bookmarks.TAGS); map.put(Bookmarks.TAGS, Bookmarks.TAGS);
map.put(Bookmarks.DESCRIPTION, Bookmarks.DESCRIPTION); map.put(Bookmarks.DESCRIPTION, Bookmarks.DESCRIPTION);
map.put(Bookmarks.KEYWORD, Bookmarks.KEYWORD); map.put(Bookmarks.KEYWORD, Bookmarks.KEYWORD);
map.put(Bookmarks.DATE_CREATED, qualifyColumn(TABLE_BOOKMARKS, Bookmarks.DATE_CREATED)); map.put(Bookmarks.DATE_CREATED, Bookmarks.DATE_CREATED);
map.put(Bookmarks.DATE_MODIFIED, qualifyColumn(TABLE_BOOKMARKS, Bookmarks.DATE_MODIFIED)); map.put(Bookmarks.DATE_MODIFIED, Bookmarks.DATE_MODIFIED);
map.put(Bookmarks.GUID, qualifyColumn(TABLE_BOOKMARKS, Bookmarks.GUID)); map.put(Bookmarks.GUID, Bookmarks.GUID);
map.put(Bookmarks.IS_DELETED, qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED)); map.put(Bookmarks.IS_DELETED, Bookmarks.IS_DELETED);
// History // History
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "history", HISTORY); URI_MATCHER.addURI(BrowserContract.AUTHORITY, "history", HISTORY);
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "history/#", HISTORY_ID); URI_MATCHER.addURI(BrowserContract.AUTHORITY, "history/#", HISTORY_ID);
map = HISTORY_PROJECTION_MAP; map = HISTORY_PROJECTION_MAP;
map.put(History._ID, qualifyColumn(TABLE_HISTORY, History._ID)); map.put(History._ID, History._ID);
map.put(History.TITLE, History.TITLE); map.put(History.TITLE, History.TITLE);
map.put(History.URL, History.URL); map.put(History.URL, History.URL);
map.put(History.FAVICON, History.FAVICON); map.put(History.FAVICON, History.FAVICON);
map.put(History.THUMBNAIL, History.THUMBNAIL); map.put(History.THUMBNAIL, History.THUMBNAIL);
map.put(History.VISITS, History.VISITS); map.put(History.VISITS, History.VISITS);
map.put(History.DATE_LAST_VISITED, History.DATE_LAST_VISITED); map.put(History.DATE_LAST_VISITED, History.DATE_LAST_VISITED);
map.put(History.DATE_CREATED, qualifyColumn(TABLE_HISTORY, History.DATE_CREATED)); map.put(History.DATE_CREATED, History.DATE_CREATED);
map.put(History.DATE_MODIFIED, qualifyColumn(TABLE_HISTORY, History.DATE_MODIFIED)); map.put(History.DATE_MODIFIED, History.DATE_MODIFIED);
map.put(History.GUID, qualifyColumn(TABLE_HISTORY, History.GUID)); map.put(History.GUID, History.GUID);
map.put(History.IS_DELETED, qualifyColumn(TABLE_HISTORY, History.IS_DELETED)); map.put(History.IS_DELETED, History.IS_DELETED);
// Images // Images
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "images", IMAGES); URI_MATCHER.addURI(BrowserContract.AUTHORITY, "images", IMAGES);
map = IMAGES_PROJECTION_MAP; map = IMAGES_PROJECTION_MAP;
map.put(Images._ID, qualifyColumn(TABLE_IMAGES, Images._ID)); map.put(Images._ID, Images._ID);
map.put(Images.URL, Images.URL); map.put(Images.URL, Images.URL);
map.put(Images.FAVICON, Images.FAVICON); map.put(Images.FAVICON, Images.FAVICON);
map.put(Images.FAVICON_URL, Images.FAVICON_URL); map.put(Images.FAVICON_URL, Images.FAVICON_URL);
map.put(Images.THUMBNAIL, Images.THUMBNAIL); map.put(Images.THUMBNAIL, Images.THUMBNAIL);
map.put(Images.DATE_CREATED, qualifyColumn(TABLE_IMAGES, Images.DATE_CREATED)); map.put(Images.DATE_CREATED, Images.DATE_CREATED);
map.put(Images.DATE_MODIFIED, qualifyColumn(TABLE_IMAGES, Images.DATE_MODIFIED)); map.put(Images.DATE_MODIFIED, Images.DATE_MODIFIED);
map.put(Images.GUID, qualifyColumn(TABLE_IMAGES, Images.GUID)); map.put(Images.GUID, Images.GUID);
map.put(Images.IS_DELETED, qualifyColumn(TABLE_IMAGES, Images.IS_DELETED)); map.put(Images.IS_DELETED, Images.IS_DELETED);
// Schema // Schema
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "schema", SCHEMA); URI_MATCHER.addURI(BrowserContract.AUTHORITY, "schema", SCHEMA);
@ -186,10 +197,6 @@ public class BrowserProvider extends ContentProvider {
} }
static final String qualifyColumn(String table, String column) { static final String qualifyColumn(String table, String column) {
return table + "." + column + " AS " + column;
}
static final String qualifyColumnValue(String table, String column) {
return table + "." + column; return table + "." + column;
} }
@ -239,6 +246,16 @@ public class BrowserProvider extends ContentProvider {
return result; return result;
} }
private static boolean hasImagesInProjection(String[] projection) {
for (int i = 0; i < projection.length; ++i) {
if (projection[i].equals(Images.FAVICON) ||
projection[i].equals(Images.THUMBNAIL))
return true;
}
return false;
}
final class DatabaseHelper extends SQLiteOpenHelper { final class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context, String databasePath) { public DatabaseHelper(Context context, String databasePath) {
super(context, databasePath, null, DATABASE_VERSION); super(context, databasePath, null, DATABASE_VERSION);
@ -314,9 +331,34 @@ public class BrowserProvider extends ContentProvider {
db.execSQL("CREATE INDEX images_modified_index ON " + TABLE_IMAGES + "(" db.execSQL("CREATE INDEX images_modified_index ON " + TABLE_IMAGES + "("
+ Images.DATE_MODIFIED + ")"); + Images.DATE_MODIFIED + ")");
db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_BOOKMARKS_WITH_IMAGES + " AS " +
"SELECT " + qualifyColumn(TABLE_BOOKMARKS, "*") +
", " + Images.FAVICON + ", " + Images.THUMBNAIL + " FROM " +
TABLE_BOOKMARKS_JOIN_IMAGES);
db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_HISTORY_WITH_IMAGES + " AS " +
"SELECT " + qualifyColumn(TABLE_HISTORY, "*") +
", " + Images.FAVICON + ", " + Images.THUMBNAIL + " FROM " +
TABLE_HISTORY_JOIN_IMAGES);
createMobileBookmarksFolder(db);
// FIXME: Create default bookmarks here // FIXME: Create default bookmarks here
} }
private void createMobileBookmarksFolder(SQLiteDatabase db) {
ContentValues values = new ContentValues();
values.put(Bookmarks.GUID, Bookmarks.MOBILE_FOLDER_GUID);
values.put(Bookmarks.IS_FOLDER, 1);
values.put(Bookmarks.POSITION, 0);
long now = System.currentTimeMillis();
values.put(Bookmarks.DATE_CREATED, now);
values.put(Bookmarks.DATE_MODIFIED, now);
db.insertOrThrow(TABLE_BOOKMARKS, Bookmarks.GUID, values);
}
@Override @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.d(LOGTAG, "Upgrading browser.db: " + db.getPath() + " from " + Log.d(LOGTAG, "Upgrading browser.db: " + db.getPath() + " from " +
@ -428,11 +470,8 @@ public class BrowserProvider extends ContentProvider {
try { try {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
String isDeletedColumn = qualifyColumnValue(tableName, SyncColumns.IS_DELETED); String selection = SyncColumns.IS_DELETED + " = 1 AND " +
String dateModifiedColumn = qualifyColumnValue(tableName, SyncColumns.DATE_MODIFIED); SyncColumns.DATE_MODIFIED + " <= " + (now - MAX_AGE_OF_DELETED_RECORDS);
String selection = isDeletedColumn + " = 1 AND " +
dateModifiedColumn + " <= " + (now - MAX_AGE_OF_DELETED_RECORDS);
cursor = query(uriWithArgs, cursor = query(uriWithArgs,
new String[] { CommonColumns._ID }, new String[] { CommonColumns._ID },
@ -801,22 +840,18 @@ public class BrowserProvider extends ContentProvider {
if (match == BOOKMARKS_ID) { if (match == BOOKMARKS_ID) {
Log.d(LOGTAG, "Query is BOOKMARKS_ID: " + uri); Log.d(LOGTAG, "Query is BOOKMARKS_ID: " + uri);
selection = concatenateWhere(selection, selection = concatenateWhere(selection, Bookmarks._ID + " = ?");
qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks._ID) + " = ?");
selectionArgs = appendSelectionArgs(selectionArgs, selectionArgs = appendSelectionArgs(selectionArgs,
new String[] { Long.toString(ContentUris.parseId(uri)) }); new String[] { Long.toString(ContentUris.parseId(uri)) });
} else if (match == BOOKMARKS_FOLDER_ID) { } else if (match == BOOKMARKS_FOLDER_ID) {
Log.d(LOGTAG, "Query is BOOKMARKS_FOLDER_ID: " + uri); Log.d(LOGTAG, "Query is BOOKMARKS_FOLDER_ID: " + uri);
selection = concatenateWhere(selection, selection = concatenateWhere(selection, Bookmarks.PARENT + " = ?");
qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks.PARENT) + " = ?");
selectionArgs = appendSelectionArgs(selectionArgs, selectionArgs = appendSelectionArgs(selectionArgs,
new String[] { Long.toString(ContentUris.parseId(uri)) }); new String[] { Long.toString(ContentUris.parseId(uri)) });
} }
if (!shouldShowDeleted(uri)) { if (!shouldShowDeleted(uri))
String isDeletedColumn = qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks.IS_DELETED); selection = concatenateWhere(Bookmarks.IS_DELETED + " = 0", selection);
selection = concatenateWhere(isDeletedColumn + " = 0", selection);
}
if (TextUtils.isEmpty(sortOrder)) { if (TextUtils.isEmpty(sortOrder)) {
Log.d(LOGTAG, "Using default sort order on query: " + uri); Log.d(LOGTAG, "Using default sort order on query: " + uri);
@ -824,7 +859,11 @@ public class BrowserProvider extends ContentProvider {
} }
qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP); qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP);
qb.setTables(TABLE_BOOKMARKS_JOIN_IMAGES);
if (hasImagesInProjection(projection))
qb.setTables(VIEW_BOOKMARKS_WITH_IMAGES);
else
qb.setTables(TABLE_BOOKMARKS);
break; break;
} }
@ -835,22 +874,23 @@ public class BrowserProvider extends ContentProvider {
if (match == HISTORY_ID) { if (match == HISTORY_ID) {
Log.d(LOGTAG, "Query is HISTORY_ID: " + uri); Log.d(LOGTAG, "Query is HISTORY_ID: " + uri);
selection = concatenateWhere(selection, selection = concatenateWhere(selection, History._ID + " = ?");
qualifyColumnValue(TABLE_HISTORY, History._ID) + " = ?");
selectionArgs = appendSelectionArgs(selectionArgs, selectionArgs = appendSelectionArgs(selectionArgs,
new String[] { Long.toString(ContentUris.parseId(uri)) }); new String[] { Long.toString(ContentUris.parseId(uri)) });
} }
if (!shouldShowDeleted(uri)) { if (!shouldShowDeleted(uri))
String isDeletedColumn = qualifyColumnValue(TABLE_HISTORY, History.IS_DELETED); selection = concatenateWhere(History.IS_DELETED + " = 0", selection);
selection = concatenateWhere(isDeletedColumn + " = 0", selection);
}
if (TextUtils.isEmpty(sortOrder)) if (TextUtils.isEmpty(sortOrder))
sortOrder = DEFAULT_HISTORY_SORT_ORDER; sortOrder = DEFAULT_HISTORY_SORT_ORDER;
qb.setProjectionMap(HISTORY_PROJECTION_MAP); qb.setProjectionMap(HISTORY_PROJECTION_MAP);
qb.setTables(TABLE_HISTORY_JOIN_IMAGES);
if (hasImagesInProjection(projection))
qb.setTables(VIEW_HISTORY_WITH_IMAGES);
else
qb.setTables(TABLE_HISTORY);
break; break;
} }
@ -861,16 +901,13 @@ public class BrowserProvider extends ContentProvider {
if (match == IMAGES_ID) { if (match == IMAGES_ID) {
Log.d(LOGTAG, "Query is IMAGES_ID: " + uri); Log.d(LOGTAG, "Query is IMAGES_ID: " + uri);
selection = concatenateWhere(selection, selection = concatenateWhere(selection, Images._ID + " = ?");
qualifyColumnValue(TABLE_IMAGES, Images._ID) + " = ?");
selectionArgs = appendSelectionArgs(selectionArgs, selectionArgs = appendSelectionArgs(selectionArgs,
new String[] { Long.toString(ContentUris.parseId(uri)) }); new String[] { Long.toString(ContentUris.parseId(uri)) });
} }
if (!shouldShowDeleted(uri)) { if (!shouldShowDeleted(uri))
String isDeletedColumn = qualifyColumnValue(TABLE_IMAGES, Images.IS_DELETED); selection = concatenateWhere(Images.IS_DELETED + " = 0", selection);
selection = concatenateWhere(isDeletedColumn + " = 0", selection);
}
qb.setProjectionMap(IMAGES_PROJECTION_MAP); qb.setProjectionMap(IMAGES_PROJECTION_MAP);
qb.setTables(TABLE_IMAGES); qb.setTables(TABLE_IMAGES);
@ -1173,10 +1210,10 @@ public class BrowserProvider extends ContentProvider {
String selection = Images.URL + " NOT IN (SELECT " + Bookmarks.URL + String selection = Images.URL + " NOT IN (SELECT " + Bookmarks.URL +
" FROM " + TABLE_BOOKMARKS + " WHERE " + Bookmarks.URL + " IS NOT NULL AND " + " FROM " + TABLE_BOOKMARKS + " WHERE " + Bookmarks.URL + " IS NOT NULL AND " +
qualifyColumnValue(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0) AND " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0) AND " +
Images.URL + " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + Images.URL + " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY +
" WHERE " + History.URL + " IS NOT NULL AND " + " WHERE " + History.URL + " IS NOT NULL AND " +
qualifyColumnValue(TABLE_HISTORY, History.IS_DELETED) + " = 0)"; qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0)";
return deleteImages(uri, selection, null); return deleteImages(uri, selection, null);
} }

Просмотреть файл

@ -65,9 +65,11 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
public static final int TRUNCATE_N_OLDEST = 5; public static final int TRUNCATE_N_OLDEST = 5;
private final String mProfile; private final String mProfile;
private long mMobileFolderId;
public LocalBrowserDB(String profile) { public LocalBrowserDB(String profile) {
mProfile = profile; mProfile = profile;
mMobileFolderId = -1;
} }
private Uri appendProfileAndLimit(Uri uri, int limit) { private Uri appendProfileAndLimit(Uri uri, int limit) {
@ -233,10 +235,38 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
return (count == 1); return (count == 1);
} }
private long getMobileBookmarksFolderId(ContentResolver cr) {
if (mMobileFolderId >= 0)
return mMobileFolderId;
Cursor c = null;
try {
c = cr.query(appendProfile(Bookmarks.CONTENT_URI),
new String[] { Bookmarks._ID },
Bookmarks.GUID + " = ?",
new String[] { Bookmarks.MOBILE_FOLDER_GUID },
null);
if (c.moveToFirst())
mMobileFolderId = c.getLong(c.getColumnIndexOrThrow(Bookmarks._ID));
} finally {
if (c != null)
c.close();
}
return mMobileFolderId;
}
public void addBookmark(ContentResolver cr, String title, String uri) { public void addBookmark(ContentResolver cr, String title, String uri) {
long folderId = getMobileBookmarksFolderId(cr);
if (folderId < 0)
return;
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(Browser.BookmarkColumns.TITLE, title); values.put(Browser.BookmarkColumns.TITLE, title);
values.put(Bookmarks.URL, uri); values.put(Bookmarks.URL, uri);
values.put(Bookmarks.PARENT, folderId);
// Restore deleted record if possible // Restore deleted record if possible
values.put(Bookmarks.IS_DELETED, 0); values.put(Bookmarks.IS_DELETED, 0);

Просмотреть файл

@ -37,6 +37,7 @@
package org.mozilla.gecko.gfx; package org.mozilla.gecko.gfx;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.gfx.CairoImage; import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.CairoUtils; import org.mozilla.gecko.gfx.CairoUtils;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@ -46,6 +47,7 @@ import java.nio.ByteBuffer;
public class BufferedCairoImage extends CairoImage { public class BufferedCairoImage extends CairoImage {
private ByteBuffer mBuffer; private ByteBuffer mBuffer;
private int mWidth, mHeight, mFormat; private int mWidth, mHeight, mFormat;
private boolean mNeedToFreeBuffer = false;
/** Creates a buffered Cairo image from a byte buffer. */ /** Creates a buffered Cairo image from a byte buffer. */
public BufferedCairoImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) { public BufferedCairoImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) {
@ -57,11 +59,23 @@ public class BufferedCairoImage extends CairoImage {
mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig()); mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig());
mWidth = bitmap.getWidth(); mWidth = bitmap.getWidth();
mHeight = bitmap.getHeight(); mHeight = bitmap.getHeight();
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 4); mNeedToFreeBuffer = true;
mBuffer = GeckoAppShell.allocateDirectBuffer(mWidth * mHeight * 4);
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer()); bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
} }
@Override protected void finalize() throws Throwable {
try {
if (mNeedToFreeBuffer && mBuffer != null)
GeckoAppShell.freeDirectBuffer(mBuffer);
mNeedToFreeBuffer = false;
mBuffer = null;
} finally {
super.finalize();
}
}
@Override
public ByteBuffer getBuffer() { return mBuffer; } public ByteBuffer getBuffer() { return mBuffer; }
@Override @Override
public int getWidth() { return mWidth; } public int getWidth() { return mWidth; }

Просмотреть файл

@ -103,7 +103,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
mScreenSize = new IntSize(1, 1); mScreenSize = new IntSize(1, 1);
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 2); mBuffer = GeckoAppShell.allocateDirectBuffer(mWidth * mHeight * 2);
mCairoImage = new CairoImage() { mCairoImage = new CairoImage() {
@Override @Override
@ -119,6 +119,17 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
mTileLayer = new SingleTileLayer(mCairoImage); mTileLayer = new SingleTileLayer(mCairoImage);
} }
protected void finalize() throws Throwable {
try {
if (mBuffer != null)
GeckoAppShell.freeDirectBuffer(mBuffer);
mBuffer = null;
} finally {
super.finalize();
}
}
/** Attaches the root layer to the layer controller so that Gecko appears. */ /** Attaches the root layer to the layer controller so that Gecko appears. */
@Override @Override
public void setLayerController(LayerController layerController) { public void setLayerController(LayerController layerController) {
@ -151,28 +162,22 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
mGeckoViewport = new ViewportMetrics(viewportObject); mGeckoViewport = new ViewportMetrics(viewportObject);
mGeckoViewport.setSize(viewportSize); mGeckoViewport.setSize(viewportSize);
mTileLayer.setOrigin(PointUtils.round(mGeckoViewport.getDisplayportOrigin())); LayerController controller = getLayerController();
mTileLayer.setResolution(mGeckoViewport.getZoomFactor()); synchronized (controller) {
PointF displayportOrigin = mGeckoViewport.getDisplayportOrigin();
mTileLayer.setOrigin(PointUtils.round(displayportOrigin));
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
// Make sure LayerController metrics changes only happen in the if (onlyUpdatePageSize) {
// UI thread. // Don't adjust page size when zooming unless zoom levels are
final LayerController controller = getLayerController(); // approximately equal.
if (FloatUtils.fuzzyEquals(controller.getZoomFactor(),
if (controller != null) { mGeckoViewport.getZoomFactor()))
controller.post(new Runnable() { controller.setPageSize(mGeckoViewport.getPageSize());
@Override } else {
public void run() { controller.setViewportMetrics(mGeckoViewport);
if (onlyUpdatePageSize) { controller.notifyPanZoomControllerOfGeometryChange(true);
// Don't adjust page size when zooming unless zoom levels are }
// approximately equal.
if (FloatUtils.fuzzyEquals(controller.getZoomFactor(), mGeckoViewport.getZoomFactor()))
controller.setPageSize(mGeckoViewport.getPageSize());
} else {
controller.setViewportMetrics(mGeckoViewport);
controller.notifyPanZoomControllerOfGeometryChange(true);
}
}
});
} }
} catch (JSONException e) { } catch (JSONException e) {
Log.e(LOGTAG, "Bad viewport description: " + viewportDescription); Log.e(LOGTAG, "Bad viewport description: " + viewportDescription);
@ -209,7 +214,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
b.copyPixelsFromBuffer(mBuffer.asIntBuffer()); b.copyPixelsFromBuffer(mBuffer.asIntBuffer());
return b; return b;
} catch (OutOfMemoryError oom) { } catch (OutOfMemoryError oom) {
Log.e(LOGTAG, "Unable to create bitmap", oom); Log.w(LOGTAG, "Unable to create bitmap", oom);
return null; return null;
} }
} }

Просмотреть файл

@ -58,12 +58,13 @@ import android.view.GestureDetector;
import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector;
import android.view.View.OnTouchListener; import android.view.View.OnTouchListener;
import java.lang.Math; import java.lang.Math;
import java.util.ArrayList;
/** /**
* The layer controller manages a tile that represents the visible page. It does panning and * The layer controller manages a tile that represents the visible page. It does panning and
* zooming natively by delegating to a panning/zooming controller. Touch events can be dispatched * zooming natively by delegating to a panning/zooming controller. Touch events can be dispatched
* to a higher-level view. * to a higher-level view.
*
* Many methods require that the monitor be held, with a synchronized (controller) { ... } block.
*/ */
public class LayerController { public class LayerController {
private static final String LOGTAG = "GeckoLayerController"; private static final String LOGTAG = "GeckoLayerController";
@ -155,7 +156,8 @@ public class LayerController {
} }
/** /**
* The view calls this to indicate that the viewport changed size. * The view calls this function to indicate that the viewport changed size. It must hold the
* monitor while calling it.
* *
* TODO: Refactor this to use an interface. Expose that interface only to the view and not * TODO: Refactor this to use an interface. Expose that interface only to the view and not
* to the layer client. That way, the layer client won't be tempted to call this, which might * to the layer client. That way, the layer client won't be tempted to call this, which might
@ -173,6 +175,7 @@ public class LayerController {
mView.requestRender(); mView.requestRender();
} }
/** Scrolls the viewport to the given point. You must hold the monitor while calling this. */
public void scrollTo(PointF point) { public void scrollTo(PointF point) {
mViewportMetrics.setOrigin(point); mViewportMetrics.setOrigin(point);
notifyLayerClientOfGeometryChange(); notifyLayerClientOfGeometryChange();
@ -181,6 +184,7 @@ public class LayerController {
mView.requestRender(); mView.requestRender();
} }
/** Scrolls the viewport by the given offset. You must hold the monitor while calling this. */
public void scrollBy(PointF point) { public void scrollBy(PointF point) {
PointF origin = mViewportMetrics.getOrigin(); PointF origin = mViewportMetrics.getOrigin();
origin.offset(point.x, point.y); origin.offset(point.x, point.y);
@ -192,6 +196,7 @@ public class LayerController {
mView.requestRender(); mView.requestRender();
} }
/** Sets the current viewport. You must hold the monitor while calling this. */
public void setViewport(RectF viewport) { public void setViewport(RectF viewport) {
mViewportMetrics.setViewport(viewport); mViewportMetrics.setViewport(viewport);
notifyLayerClientOfGeometryChange(); notifyLayerClientOfGeometryChange();
@ -200,6 +205,7 @@ public class LayerController {
mView.requestRender(); mView.requestRender();
} }
/** Sets the current page size. You must hold the monitor while calling this. */
public void setPageSize(FloatSize size) { public void setPageSize(FloatSize size) {
if (mViewportMetrics.getPageSize().fuzzyEquals(size)) if (mViewportMetrics.getPageSize().fuzzyEquals(size))
return; return;
@ -215,7 +221,8 @@ public class LayerController {
/** /**
* Sets the entire viewport metrics at once. This function does not notify the layer client or * Sets the entire viewport metrics at once. This function does not notify the layer client or
* the pan/zoom controller, so you will need to call notifyLayerClientOfGeometryChange() or * the pan/zoom controller, so you will need to call notifyLayerClientOfGeometryChange() or
* notifyPanZoomControllerOfGeometryChange() after calling this. * notifyPanZoomControllerOfGeometryChange() after calling this. You must hold the monitor
* while calling this.
*/ */
public void setViewportMetrics(ViewportMetrics viewport) { public void setViewportMetrics(ViewportMetrics viewport) {
mViewportMetrics = new ViewportMetrics(viewport); mViewportMetrics = new ViewportMetrics(viewport);
@ -223,10 +230,15 @@ public class LayerController {
mView.requestRender(); mView.requestRender();
} }
/** Scales the viewport. You must hold the monitor while calling this. */
public void scaleTo(float zoomFactor) { public void scaleTo(float zoomFactor) {
scaleWithFocus(zoomFactor, new PointF(0,0)); scaleWithFocus(zoomFactor, new PointF(0,0));
} }
/**
* Scales the viewport, keeping the given focus point in the same place before and after the
* scale operation. You must hold the monitor while calling this.
*/
public void scaleWithFocus(float zoomFactor, PointF focus) { public void scaleWithFocus(float zoomFactor, PointF focus) {
mViewportMetrics.scaleTo(zoomFactor, focus); mViewportMetrics.scaleTo(zoomFactor, focus);
@ -237,6 +249,10 @@ public class LayerController {
mView.requestRender(); mView.requestRender();
} }
/**
* Sets the viewport origin and scales in one operation. You must hold the monitor while
* calling this.
*/
public void scaleWithOrigin(float zoomFactor, PointF origin) { public void scaleWithOrigin(float zoomFactor, PointF origin) {
mViewportMetrics.setOrigin(origin); mViewportMetrics.setOrigin(origin);
scaleTo(zoomFactor); scaleTo(zoomFactor);

Просмотреть файл

@ -132,9 +132,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
/** /**
* Called whenever a new frame is about to be drawn. * Called whenever a new frame is about to be drawn.
*
* FIXME: This is racy. Layers and page sizes can be modified by the pan/zoom controller while
* this is going on.
*/ */
public void onDrawFrame(GL10 gl) { public void onDrawFrame(GL10 gl) {
long frameStartTime = SystemClock.uptimeMillis(); long frameStartTime = SystemClock.uptimeMillis();
@ -142,63 +139,68 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
TextureReaper.get().reap(gl); TextureReaper.get().reap(gl);
LayerController controller = mView.getController(); LayerController controller = mView.getController();
Layer rootLayer = controller.getRoot(); RenderContext screenContext = createScreenContext();
RenderContext screenContext = createScreenContext(), pageContext = createPageContext();
if (!pageContext.fuzzyEquals(mLastPageContext)) { synchronized (controller) {
// the viewport or page changed, so show the scrollbars again Layer rootLayer = controller.getRoot();
// as per UX decision RenderContext pageContext = createPageContext();
mVertScrollLayer.unfade();
mHorizScrollLayer.unfade(); if (!pageContext.fuzzyEquals(mLastPageContext)) {
mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY); // the viewport or page changed, so show the scrollbars again
} else if (mFadeRunnable.timeToFade()) { // as per UX decision
boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade(); mVertScrollLayer.unfade();
if (stillFading) { mHorizScrollLayer.unfade();
mFadeRunnable.scheduleNextFadeFrame(); mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY);
} else if (mFadeRunnable.timeToFade()) {
boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade();
if (stillFading) {
mFadeRunnable.scheduleNextFadeFrame();
}
} }
mLastPageContext = pageContext;
/* Update layers. */
if (rootLayer != null) rootLayer.update(gl);
mShadowLayer.update(gl);
mCheckerboardLayer.update(gl);
mFrameRateLayer.update(gl);
mVertScrollLayer.update(gl);
mHorizScrollLayer.update(gl);
/* Draw the background. */
gl.glClearColor(BACKGROUND_COLOR_R, BACKGROUND_COLOR_G, BACKGROUND_COLOR_B, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
/* Draw the drop shadow, if we need to. */
Rect pageRect = getPageRect();
RectF untransformedPageRect = new RectF(0.0f, 0.0f, pageRect.width(),
pageRect.height());
if (!untransformedPageRect.contains(controller.getViewport()))
mShadowLayer.draw(pageContext);
/* Draw the checkerboard. */
Rect scissorRect = transformToScissorRect(pageRect);
gl.glEnable(GL10.GL_SCISSOR_TEST);
gl.glScissor(scissorRect.left, scissorRect.top,
scissorRect.width(), scissorRect.height());
mCheckerboardLayer.draw(screenContext);
/* Draw the layer the client added to us. */
if (rootLayer != null)
rootLayer.draw(pageContext);
gl.glDisable(GL10.GL_SCISSOR_TEST);
/* Draw the vertical scrollbar. */
IntSize screenSize = new IntSize(controller.getViewportSize());
if (pageRect.height() > screenSize.height)
mVertScrollLayer.draw(pageContext);
/* Draw the horizontal scrollbar. */
if (pageRect.width() > screenSize.width)
mHorizScrollLayer.draw(pageContext);
} }
mLastPageContext = pageContext;
/* Update layers. */
if (rootLayer != null) rootLayer.update(gl);
mShadowLayer.update(gl);
mCheckerboardLayer.update(gl);
mFrameRateLayer.update(gl);
mVertScrollLayer.update(gl);
mHorizScrollLayer.update(gl);
/* Draw the background. */
gl.glClearColor(BACKGROUND_COLOR_R, BACKGROUND_COLOR_G, BACKGROUND_COLOR_B, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
/* Draw the drop shadow, if we need to. */
Rect pageRect = getPageRect();
RectF untransformedPageRect = new RectF(0.0f, 0.0f, pageRect.width(), pageRect.height());
if (!untransformedPageRect.contains(controller.getViewport()))
mShadowLayer.draw(pageContext);
/* Draw the checkerboard. */
Rect scissorRect = transformToScissorRect(pageRect);
gl.glEnable(GL10.GL_SCISSOR_TEST);
gl.glScissor(scissorRect.left, scissorRect.top,
scissorRect.width(), scissorRect.height());
mCheckerboardLayer.draw(screenContext);
/* Draw the layer the client added to us. */
if (rootLayer != null)
rootLayer.draw(pageContext);
gl.glDisable(GL10.GL_SCISSOR_TEST);
/* Draw the vertical scrollbar. */
IntSize screenSize = new IntSize(controller.getViewportSize());
if (pageRect.height() > screenSize.height)
mVertScrollLayer.draw(pageContext);
/* Draw the horizontal scrollbar. */
if (pageRect.width() > screenSize.width)
mHorizScrollLayer.draw(pageContext);
/* Draw the FPS. */ /* Draw the FPS. */
if (mShowFrameRate) { if (mShowFrameRate) {

Просмотреть файл

@ -51,6 +51,7 @@ import android.util.Log;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL10;
import org.mozilla.gecko.FloatUtils; import org.mozilla.gecko.FloatUtils;
import org.mozilla.gecko.GeckoAppShell;
/** /**
* Draws a small rect. This is scaled to become a scrollbar. * Draws a small rect. This is scaled to become a scrollbar.
@ -74,6 +75,7 @@ public class ScrollbarLayer extends TileLayer {
private final Bitmap mBitmap; private final Bitmap mBitmap;
private final Canvas mCanvas; private final Canvas mCanvas;
private float mOpacity; private float mOpacity;
private boolean mFinalized = false;
private ScrollbarLayer(CairoImage image, boolean vertical, ByteBuffer buffer) { private ScrollbarLayer(CairoImage image, boolean vertical, ByteBuffer buffer) {
super(false, image); super(false, image);
@ -84,11 +86,22 @@ public class ScrollbarLayer extends TileLayer {
mCanvas = new Canvas(mBitmap); mCanvas = new Canvas(mBitmap);
} }
protected void finalize() throws Throwable {
try {
if (!mFinalized && mBuffer != null)
GeckoAppShell.freeDirectBuffer(mBuffer);
mFinalized = true;
} finally {
super.finalize();
}
}
public static ScrollbarLayer create(boolean vertical) { public static ScrollbarLayer create(boolean vertical) {
// just create an empty image for now, it will get drawn // just create an empty image for now, it will get drawn
// on demand anyway // on demand anyway
int imageSize = nextPowerOfTwo(BAR_SIZE); int imageSize = nextPowerOfTwo(BAR_SIZE);
ByteBuffer buffer = ByteBuffer.allocateDirect(imageSize * imageSize * 4); ByteBuffer buffer = GeckoAppShell.allocateDirectBuffer(imageSize * imageSize * 4);
CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize, CairoImage.FORMAT_ARGB32); CairoImage image = new BufferedCairoImage(buffer, imageSize, imageSize, CairoImage.FORMAT_ARGB32);
return new ScrollbarLayer(image, vertical, buffer); return new ScrollbarLayer(image, vertical, buffer);
} }

Просмотреть файл

@ -37,6 +37,7 @@
package org.mozilla.gecko.gfx; package org.mozilla.gecko.gfx;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.gfx.BufferedCairoImage; import org.mozilla.gecko.gfx.BufferedCairoImage;
import org.mozilla.gecko.gfx.CairoImage; import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.IntSize; import org.mozilla.gecko.gfx.IntSize;
@ -56,6 +57,7 @@ import java.nio.IntBuffer;
public class TextLayer extends SingleTileLayer { public class TextLayer extends SingleTileLayer {
private final ByteBuffer mBuffer; private final ByteBuffer mBuffer;
private final IntSize mSize; private final IntSize mSize;
private boolean mFinalized = false;
/* /*
* This awkward pattern is necessary due to Java's restrictions on when one can call superclass * This awkward pattern is necessary due to Java's restrictions on when one can call superclass
@ -68,8 +70,18 @@ public class TextLayer extends SingleTileLayer {
renderText(text); renderText(text);
} }
protected void finalize() throws Throwable {
try {
if (!mFinalized && mBuffer != null)
GeckoAppShell.freeDirectBuffer(mBuffer);
mFinalized = true;
} finally {
super.finalize();
}
}
public static TextLayer create(IntSize size, String text) { public static TextLayer create(IntSize size, String text) {
ByteBuffer buffer = ByteBuffer.allocateDirect(size.width * size.height * 4); ByteBuffer buffer = GeckoAppShell.allocateDirectBuffer(size.width * size.height * 4);
BufferedCairoImage image = new BufferedCairoImage(buffer, size.width, size.height, BufferedCairoImage image = new BufferedCairoImage(buffer, size.width, size.height,
CairoImage.FORMAT_ARGB32); CairoImage.FORMAT_ARGB32);
return new TextLayer(buffer, image, size, text); return new TextLayer(buffer, image, size, text);

Просмотреть файл

@ -168,15 +168,5 @@ public abstract class TileLayer extends Layer {
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, repeatMode); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, repeatMode);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, repeatMode); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, repeatMode);
} }
protected static FloatBuffer createBuffer(float[] values) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(values.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();
floatBuffer.put(values);
floatBuffer.position(0);
return floatBuffer;
}
} }

Просмотреть файл

@ -236,17 +236,17 @@ public class ViewportMetrics {
public String toJSON() { public String toJSON() {
try { try {
return new JSONStringer().object() JSONStringer object = new JSONStringer().object();
.key("x").value(mViewportRect.left) object.key("zoom").value(mZoomFactor);
.key("y").value(mViewportRect.top) object.key("offsetY").value(mViewportOffset.y);
.key("width").value(mViewportRect.width()) object.key("offsetX").value(mViewportOffset.x);
.key("height").value(mViewportRect.height()) object.key("pageHeight").value(mPageSize.height);
.key("pageWidth").value(mPageSize.width) object.key("pageWidth").value(mPageSize.width);
.key("pageHeight").value(mPageSize.height) object.key("height").value(mViewportRect.height());
.key("offsetX").value(mViewportOffset.x) object.key("width").value(mViewportRect.width());
.key("offsetY").value(mViewportOffset.y) object.key("y").value(mViewportRect.top);
.key("zoom").value(mZoomFactor) object.key("x").value(mViewportRect.left);
.endObject().toString(); return object.endObject().toString();
} catch (JSONException je) { } catch (JSONException je) {
Log.e(LOGTAG, "Error serializing viewportmetrics", je); Log.e(LOGTAG, "Error serializing viewportmetrics", je);
return ""; return "";

Просмотреть файл

@ -62,6 +62,10 @@
<!ENTITY pref_clear_history_confirm "Browsing history will be deleted"> <!ENTITY pref_clear_history_confirm "Browsing history will be deleted">
<!ENTITY pref_clear_private_data "Clear private data"> <!ENTITY pref_clear_private_data "Clear private data">
<!ENTITY pref_clear_private_data_confirm "Browsing settings, including passwords and cookies, will be deleted"> <!ENTITY pref_clear_private_data_confirm "Browsing settings, including passwords and cookies, will be deleted">
<!ENTITY pref_enable_flash "Enable Flash">
<!ENTITY pref_enable_flash_yes "Yes">
<!ENTITY pref_enable_flash_tap_to_play "Tap To Play">
<!ENTITY pref_enable_flash_no "No">
<!ENTITY quit "Quit"> <!ENTITY quit "Quit">

Двоичные данные
mobile/android/base/resources/drawable-hdpi-v8/abouthome_icon.png Normal file

Двоичный файл не отображается.

После

(image error) Размер: 69 KiB

Двоичные данные
mobile/android/base/resources/drawable-hdpi-v8/abouthome_logo.png Normal file

Двоичный файл не отображается.

После

(image error) Размер: 1.4 KiB

Двоичный файл не отображается.

После

(image error) Размер: 93 B

Двоичный файл не отображается.

После

(image error) Размер: 28 KiB

Двоичный файл не отображается.

После

(image error) Размер: 800 B

Двоичные данные
mobile/android/base/resources/drawable-mdpi-v8/abouthome_icon.png Normal file

Двоичный файл не отображается.

После

(image error) Размер: 36 KiB

Двоичные данные
mobile/android/base/resources/drawable-mdpi-v8/abouthome_logo.png Normal file

Двоичный файл не отображается.

После

(image error) Размер: 984 B

Двоичный файл не отображается.

После

(image error) Размер: 93 B

Двоичный файл не отображается.

После

(image error) Размер: 14 KiB

Двоичный файл не отображается.

После

(image error) Размер: 729 B

Двоичный файл не отображается.

После

(image error) Размер: 105 KiB

Двоичный файл не отображается.

После

(image error) Размер: 1.8 KiB

Двоичный файл не отображается.

После

(image error) Размер: 100 B

Двоичный файл не отображается.

После

(image error) Размер: 41 KiB

Двоичный файл не отображается.

После

(image error) Размер: 1.0 KiB

Двоичные данные
mobile/android/base/resources/drawable/abouthome_bg.png Normal file

Двоичный файл не отображается.

После

(image error) Размер: 11 KiB

Просмотреть файл

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/abouthome_bg"
android:tileMode="repeat"/>

Двоичные данные
mobile/android/base/resources/drawable/abouthome_topsites_bg.png Normal file

Двоичный файл не отображается.

После

(image error) Размер: 11 KiB

Просмотреть файл

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/abouthome_topsites_bg"
android:tileMode="repeat"/>

Просмотреть файл

@ -1,10 +0,0 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFFFF" />
<stroke android:width="2dip" android:color="#ffD0D0D0"/>
<padding
android:left="7dp"
android:top="7dp"
android:right="7dp"
android:bottom="7dp" />
<corners android:radius="4dp" />
</shape>

Просмотреть файл

@ -1,7 +0,0 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFD0D0D0" />
<stroke android:width="2dip" android:color="#FFD0D0D0"/>
<padding android:left="7dp" android:top="7dp"
android:right="7dp" android:bottom="7dp" />
<corners android:radius="4dp" />
</shape>

Просмотреть файл

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="10dip"
android:textColor="#FF000000"
android:background="@drawable/rounded_grey_border" />

Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="47dip"
android:paddingLeft="12dip"
android:gravity="left|center_vertical"
android:textSize="15sp"
android:textColor="#222222"/>

Просмотреть файл

@ -1,36 +1,112 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:layout_width="fill_parent" <RelativeLayout android:layout_width="fill_parent"
android:layout_height="100dip" android:layout_height="fill_parent"
android:layout_gravity="center_horizontal" android:background="@drawable/abouthome_bg_repeat">
android:src="@drawable/icon" />
<TextView <RelativeLayout android:id="@+id/top_sites"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dip" android:layout_alignParentTop="true"
android:textColor="#FF202020" android:layout_alignParentLeft="true"
android:textStyle="bold" android:isScrollContainer="false"
android:text="Recommended Addons" android:background="@drawable/abouthome_topsites_bg_repeat">
android:isScrollContainer="false" />
<ListView <ImageView android:layout_width="fill_parent"
android:id="@+id/recommended_addon_list" android:layout_height="50dip"
android:layout_width="fill_parent" android:gravity="fill"
android:layout_height="wrap_content" android:background="@drawable/abouthome_bg_repeat"/>
android:divider="@android:color/transparent"
android:isScrollContainer="false" <ImageView android:layout_width="wrap_content"
android:dividerHeight="8dip" /> android:layout_height="wrap_content"
<TextView android:layout_alignParentTop="true"
android:layout_width="fill_parent" android:layout_alignParentRight="true"
android:layout_height="wrap_content" android:src="@drawable/abouthome_icon"/>
android:layout_marginTop="10dip"
android:textColor="#FF202020" <TextView android:id="@+id/top_sites_title"
android:textStyle="bold" android:layout_width="fill_parent"
android:isScrollContainer="false" android:layout_height="wrap_content"
android:text="Favorite Sites" /> android:layout_marginLeft="12dip"
<GridView android:layout_marginTop="55dip"
android:id="@+id/grid" android:textSize="12sp"
android:layout_width="fill_parent" android:textColor="#000000"
android:isScrollContainer="false" android:textStyle="bold"
android:layout_height="wrap_content"/> android:text="Top Sites"/>
<view class="org.mozilla.gecko.AboutHomeContent$TopSitesGridView"
android:id="@+id/top_sites_grid"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/top_sites_title"
android:verticalSpacing="8dip"
android:horizontalSpacing="0dip"
android:isScrollContainer="false"
android:gravity="center"/>
<view class="org.mozilla.gecko.AboutHomeContent$LinkTextView"
android:id="@+id/all_top_sites_text"
android:layout_width="fill_parent"
android:layout_height="30dip"
android:layout_below="@id/top_sites_grid"
android:layout_marginTop="7dip"
android:textColor="#22629e"
android:textSize="12sp"
android:gravity="top|center_horizontal"
android:text="Browse all your top sites"/>
</RelativeLayout>
<ImageView android:id="@+id/logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dip"
android:layout_marginBottom="10dip"
android:layout_marginLeft="12dip"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:src="@drawable/abouthome_logo"/>
<TextView android:id="@+id/recommended_addons_title"
android:layout_width="fill_parent"
android:layout_height="26dip"
android:paddingLeft="12dip"
android:layout_below="@id/top_sites"
android:background="@drawable/abouthome_separator"
android:textSize="12sp"
android:textColor="#000000"
android:textStyle="bold"
android:gravity="left|center_vertical"
android:text="Add-ons for your Firefox"/>
<LinearLayout android:id="@+id/recommended_addons"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/recommended_addons_title"
android:background="@drawable/abouthome_separator"
android:isScrollContainer="false">
<view class="org.mozilla.gecko.AboutHomeContent$AddonsListView"
android:id="@+id/recommended_addons_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="@drawable/abouthome_separator"
android:isScrollContainer="false"
android:dividerHeight="2dip"/>
<view class="org.mozilla.gecko.AboutHomeContent$LinkTextView"
android:id="@+id/all_addons_text"
android:layout_width="fill_parent"
android:layout_height="47dip"
android:background="@drawable/abouthome_separator"
android:textColor="#22629e"
android:textSize="12sp"
android:gravity="center"
android:text="Browse all Firefox Add-ons"/>
</LinearLayout>
</RelativeLayout>
</merge> </merge>

Просмотреть файл

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dip"
android:layout_height="150dip"
android:padding="6dip"
android:gravity="center_horizontal">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/rounded_grey_box" >
<ImageView
android:id="@+id/screenshot"
android:layout_width="fill_parent"
android:layout_height="80dip" />
<LinearLayout
android:orientation="horizontal"
android:layout_height="wrap_content"
android:layout_width="fill_parent" >
<ImageView
android:id="@+id/bookmark_icon"
android:layout_width="16dip"
android:layout_height="16dip"
android:layout_marginRight="6dip" />
<TextView
android:id="@+id/bookmark_title"
android:singleLine="true"
android:textColor="#FF202020"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#FF202020"
android:id="@+id/bookmark_url"
android:singleLine="true"
android:ellipsize="marquee" />
</LinearLayout>
</LinearLayout>

Просмотреть файл

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="140dip"
android:layout_height="wrap_content">
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/abouthome_topsite_shadow"
android:padding="1dip"
android:paddingTop="2dip">
<ImageView android:id="@+id/thumbnail"
android:layout_width="fill_parent"
android:layout_height="80dip"
android:scaleType="centerCrop"/>
</LinearLayout>
<TextView android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dip"
android:singleLine="true"
android:textColor="#000000"
android:textSize="9dip"
android:gravity="center_horizontal"/>
</LinearLayout>
</LinearLayout>

Просмотреть файл

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="pref_enable_flash_entries">
<item>@string/pref_enable_flash_yes</item>
<item>@string/pref_enable_flash_tap_to_play</item>
<item>@string/pref_enable_flash_no</item>
</string-array>
<string-array name="pref_enable_flash_values">
<item>1</item>
<item>2</item>
<item>0</item>
</string-array>
</resources>

Просмотреть файл

@ -19,6 +19,12 @@
android:title="@string/pref_char_encoding" android:title="@string/pref_char_encoding"
android:persistent="false" /> android:persistent="false" />
<ListPreference android:key="plugin.enable"
android:title="@string/pref_enable_flash"
android:entries="@array/pref_enable_flash_entries"
android:entryValues="@array/pref_enable_flash_values"
android:persistent="false" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/pref_category_privacy"> <PreferenceCategory android:title="@string/pref_category_privacy">

Просмотреть файл

@ -68,6 +68,10 @@
<string name="pref_clear_history_confirm">&pref_clear_history_confirm;</string> <string name="pref_clear_history_confirm">&pref_clear_history_confirm;</string>
<string name="pref_clear_private_data">&pref_clear_private_data;</string> <string name="pref_clear_private_data">&pref_clear_private_data;</string>
<string name="pref_clear_private_data_confirm">&pref_clear_private_data_confirm;</string> <string name="pref_clear_private_data_confirm">&pref_clear_private_data_confirm;</string>
<string name="pref_enable_flash">&pref_enable_flash;</string>
<string name="pref_enable_flash_yes">&pref_enable_flash_yes;</string>
<string name="pref_enable_flash_tap_to_play">&pref_enable_flash_tap_to_play;</string>
<string name="pref_enable_flash_no">&pref_enable_flash_no;</string>
<string name="reload">&reload;</string> <string name="reload">&reload;</string>
<string name="forward">&forward;</string> <string name="forward">&forward;</string>

Просмотреть файл

@ -560,7 +560,9 @@ public class PanZoomController
GeckoAppShell.sendEventToGecko(e); GeckoAppShell.sendEventToGecko(e);
mOverrideScrollAck = false; mOverrideScrollAck = false;
} else { } else {
mController.scrollBy(new PointF(mX.displacement, mY.displacement)); synchronized (mController) {
mController.scrollBy(new PointF(mX.displacement, mY.displacement));
}
} }
mX.displacement = mY.displacement = 0; mX.displacement = mY.displacement = 0;
@ -592,18 +594,22 @@ public class PanZoomController
/* Performs one frame of a bounce animation. */ /* Performs one frame of a bounce animation. */
private void advanceBounce() { private void advanceBounce() {
float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame]; synchronized (mController) {
ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t); float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
mController.setViewportMetrics(newMetrics); ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
mController.notifyLayerClientOfGeometryChange(); mController.setViewportMetrics(newMetrics);
mBounceFrame++; mController.notifyLayerClientOfGeometryChange();
mBounceFrame++;
}
} }
/* Concludes a bounce animation and snaps the viewport into place. */ /* Concludes a bounce animation and snaps the viewport into place. */
private void finishBounce() { private void finishBounce() {
mController.setViewportMetrics(mBounceEndMetrics); synchronized (mController) {
mController.notifyLayerClientOfGeometryChange(); mController.setViewportMetrics(mBounceEndMetrics);
mBounceFrame = -1; mController.notifyLayerClientOfGeometryChange();
mBounceFrame = -1;
}
} }
} }
@ -881,11 +887,14 @@ public class PanZoomController
else else
spanRatio = 1.0f - (1.0f - spanRatio) * resistance; spanRatio = 1.0f - (1.0f - spanRatio) * resistance;
float newZoomFactor = mController.getZoomFactor() * spanRatio; synchronized (mController) {
float newZoomFactor = mController.getZoomFactor() * spanRatio;
mController.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(), mController.scrollBy(new PointF(mLastZoomFocus.x - detector.getFocusX(),
mLastZoomFocus.y - detector.getFocusY())); mLastZoomFocus.y - detector.getFocusY()));
mController.scaleWithFocus(newZoomFactor, new PointF(detector.getFocusX(), detector.getFocusY())); PointF focus = new PointF(detector.getFocusX(), detector.getFocusY());
mController.scaleWithFocus(newZoomFactor, focus);
}
mLastZoomFocus.set(detector.getFocusX(), detector.getFocusY()); mLastZoomFocus.set(detector.getFocusX(), detector.getFocusY());

Просмотреть файл

@ -92,6 +92,26 @@
<div id="addons-list" style="display: none;"> <div id="addons-list" style="display: none;">
</div> </div>
<div id="addons-details" style="display: none">
<div class="addon-item">
<img class="favicon"/>
<div class="inner">
<div class="details">
<div class="title"></div><div class="version"></div><div class="tag"></div>
</div>
<div class="description-full"></div>
</div>
<div class="buttons">
<button id="enable-btn" class="show-on-disable hide-on-enable hide-on-uninstall" onclick="Addons.enable();">&addonAction.enable;</button>
<button id="disable-btn" class="show-on-enable hide-on-disable hide-on-uninstall" onclick="Addons.disable();">&addonAction.disable;</button>
<button id="uninstall-btn" class="hide-on-uninstall" onclick="Addons.uninstall();">&addonAction.uninstall;</button>
<button id="cancel-btn" class="show-on-uninstall" onclick="Addons.cancelUninstall();">&addonAction.cancel;</button>
</div>
<div class="options-header">&aboutAddons.options;</div>
<div class="options-box"></div>
</div>
</div>
<script type="application/javascript;version=1.8"><![CDATA[ <script type="application/javascript;version=1.8"><![CDATA[
let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils; let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
@ -116,6 +136,8 @@
} }
function init() { function init() {
window.addEventListener("popstate", onPopState, false);
AddonManager.addInstallListener(Addons); AddonManager.addInstallListener(Addons);
Addons.getAddons(); Addons.getAddons();
} }
@ -124,11 +146,34 @@
AddonManager.removeInstallListener(Addons); AddonManager.removeInstallListener(Addons);
} }
function onPopState(aEvent) {
// Called when back/forward is used to change the state of the page
if (aEvent.state) {
// Show the detail page for an addon
Addons.showDetails(Addons._getElementForAddon(aEvent.state.id));
} else {
// Clear any previous detail addon
let detailItem = document.querySelector("#addons-details > .addon-item");
detailItem.addon = null;
// Hide the detail page and show the list
let details = document.querySelector("#addons-details");
details.style.display = "none";
let list = document.querySelector("#addons-list");
list.style.display = "block";
}
}
var Addons = { var Addons = {
_createItem: function _createItem(aAddon) { _createItem: function _createItem(aAddon) {
let outer = document.createElement("div"); let outer = document.createElement("div");
outer.setAttribute("addonID", aAddon.id); outer.setAttribute("addonID", aAddon.id);
outer.className = "addon-item"; outer.className = "addon-item";
outer.setAttribute("role", "button");
outer.addEventListener("click", function() {
this.showDetails(outer);
history.pushState({ id: aAddon.id }, document.title);
}.bind(this), true);
let img = document.createElement("img"); let img = document.createElement("img");
img.className = "favicon"; img.className = "favicon";
@ -138,15 +183,24 @@
let inner = document.createElement("div"); let inner = document.createElement("div");
inner.className = "inner"; inner.className = "inner";
let titlePart = document.createElement("span"); let details = document.createElement("div");
details.className = "details";
inner.appendChild(details);
let titlePart = document.createElement("div");
titlePart.textContent = aAddon.name; titlePart.textContent = aAddon.name;
titlePart.className = "title"; titlePart.className = "title";
inner.appendChild(titlePart); details.appendChild(titlePart);
let versionPart = document.createElement("span"); let versionPart = document.createElement("div");
versionPart.textContent = aAddon.version; versionPart.textContent = aAddon.version;
versionPart.className = "version"; versionPart.className = "version";
inner.appendChild(versionPart); details.appendChild(versionPart);
let tagPart = document.createElement("div");
tagPart.textContent = gStringBundle.GetStringFromName("addonType." + aAddon.type);
tagPart.className = "tag";
details.appendChild(tagPart);
if ("description" in aAddon) { if ("description" in aAddon) {
let descPart = document.createElement("div"); let descPart = document.createElement("div");
@ -156,56 +210,6 @@
} }
outer.appendChild(inner); outer.appendChild(inner);
let buttons = document.createElement("div");
buttons.className = "buttons";
let optionsBtn = document.createElement("button");
optionsBtn.className = "options-btn";
optionsBtn.textContent = gStringBundle.GetStringFromName("addonAction.options");
optionsBtn.setAttribute("disabled", "true"); // TODO (bug 696533)
optionsBtn.addEventListener("click", function() {
this.toggleOptions(outer);
}.bind(this), false)
buttons.appendChild(optionsBtn);
let enableBtn = document.createElement("button");
enableBtn.className = "show-on-disable hide-on-enable hide-on-uninstall";
enableBtn.textContent = gStringBundle.GetStringFromName("addonAction.enable");
if (aAddon.appDisabled)
enableBtn.setAttribute("disabled", "true");
enableBtn.addEventListener("click", function() {
this.enable(outer);
}.bind(this), false)
buttons.appendChild(enableBtn);
let disableBtn = document.createElement("button");
disableBtn.className = "show-on-enable hide-on-disable hide-on-uninstall";
disableBtn.textContent = gStringBundle.GetStringFromName("addonAction.disable");
disableBtn.addEventListener("click", function() {
this.disable(outer);
}.bind(this), false)
buttons.appendChild(disableBtn);
let uninstallBtn = document.createElement("button");
uninstallBtn.className = "hide-on-uninstall";
uninstallBtn.textContent = gStringBundle.GetStringFromName("addonAction.uninstall");
if (aAddon.scope == AddonManager.SCOPE_APPLICATION)
uninstallBtn.setAttribute("disabled", "true");
uninstallBtn.addEventListener("click", function() {
this.uninstall(outer);
}.bind(this), false)
buttons.appendChild(uninstallBtn);
let cancelUninstallBtn = document.createElement("button");
cancelUninstallBtn.className = "show-on-uninstall";
cancelUninstallBtn.textContent = gStringBundle.GetStringFromName("addonAction.cancel");
cancelUninstallBtn.addEventListener("click", function() {
this.cancelUninstall(outer);
}.bind(this), false)
buttons.appendChild(cancelUninstallBtn);
outer.appendChild(buttons);
return outer; return outer;
}, },
@ -302,16 +306,92 @@
return ""; return "";
}, },
enable: function enable(aItem) { showDetails: function showDetails(aListItem) {
if (!aItem.addon) Services.console.logStringMessage("---- showing details")
let detailItem = document.querySelector("#addons-details > .addon-item");
detailItem.setAttribute("isDisabled", aListItem.getAttribute("isDisabled"));
detailItem.setAttribute("opType", aListItem.getAttribute("opType"));
detailItem.setAttribute("optionsURL", aListItem.getAttribute("optionsURL"));
detailItem.addon = aListItem.addon;
Services.console.logStringMessage("---- did step 1")
let addon = detailItem.addon;
document.querySelector("#addons-details > .addon-item .favicon").setAttribute("src", addon.iconURL);
document.querySelector("#addons-details > .addon-item .title").textContent = addon.name;
document.querySelector("#addons-details > .addon-item .version").textContent = addon.version;
document.querySelector("#addons-details > .addon-item .tag").textContent = gStringBundle.GetStringFromName("addonType." + addon.type);
document.querySelector("#addons-details > .addon-item .description-full").textContent = addon.description;
Services.console.logStringMessage("---- did step 2")
let enableBtn = document.getElementById("uninstall-btn");
if (addon.appDisabled)
enableBtn.setAttribute("disabled", "true");
else
enableBtn.removeAttribute("disabled");
let uninstallBtn = document.getElementById("uninstall-btn");
if (addon.scope == AddonManager.SCOPE_APPLICATION)
uninstallBtn.setAttribute("disabled", "true");
else
uninstallBtn.removeAttribute("disabled");
let box = document.querySelector("#addons-details > .addon-item .options-box");
box.innerHTML = "";
// Retrieve the extensions preferences
try {
let optionsURL = aListItem.getAttribute("optionsURL");
let xhr = new XMLHttpRequest();
xhr.open("GET", optionsURL, false);
xhr.send();
if (xhr.responseXML) {
let currentNode;
let nodeIterator = xhr.responseXML.createNodeIterator(xhr.responseXML, NodeFilter.SHOW_TEXT, null, false);
while (currentNode = nodeIterator.nextNode()) {
let trimmed = currentNode.nodeValue.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
if (!trimmed.length)
currentNode.parentNode.removeChild(currentNode);
}
// Only allow <setting> for now
let prefs = xhr.responseXML.querySelectorAll(":root > setting");
for (let i = 0; i < prefs.length; i++)
box.appendChild(prefs.item(i));
/*
// Send an event so add-ons can prepopulate any non-preference based
// settings
let event = document.createEvent("Events");
event.initEvent("AddonOptionsLoad", true, false);
this.dispatchEvent(event);
// Also send a notification to match the behavior of desktop Firefox
let id = this.id.substring(17); // length of |urn:mozilla:item:|
Services.obs.notifyObservers(document, "addon-options-displayed", id);
*/
}
} catch (e) {
Cu.reportError(e)
}
let list = document.querySelector("#addons-list");
list.style.display = "none";
let details = document.querySelector("#addons-details");
details.style.display = "block";
Services.console.logStringMessage("---- did step 3")
},
enable: function enable() {
let detailItem = document.querySelector("#addons-details > .addon-item");
if (!detailItem.addon)
return; return;
let opType; let opType;
if (aItem.addon.type == "search") { let isDisabled;
aItem.setAttribute("isDisabled", false); if (detailItem.addon.type == "search") {
aItem.addon.engine.hidden = false; isDisabled = false;
detailItem.addon.engine.hidden = false;
opType = "needs-enable"; opType = "needs-enable";
} else if (aItem.addon.type == "theme") { } else if (detailItem.addon.type == "theme") {
// We can have only one theme enabled, so disable the current one if any // We can have only one theme enabled, so disable the current one if any
let theme = null; let theme = null;
let list = document.getElementById("addons-list"); let list = document.getElementById("addons-list");
@ -326,98 +406,118 @@
if (theme) if (theme)
this.disable(theme); this.disable(theme);
aItem.addon.userDisabled = false; detailItem.addon.userDisabled = false;
aItem.setAttribute("isDisabled", false); isDisabled = false;
} else { } else {
aItem.addon.userDisabled = false; detailItem.addon.userDisabled = false;
opType = this._getOpTypeForOperations(aItem.addon.pendingOperations); isDisabled = false;
opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
if (aItem.addon.pendingOperations & AddonManager.PENDING_ENABLE) { if (detailItem.addon.pendingOperations & AddonManager.PENDING_ENABLE) {
this.showRestart(); this.showRestart();
} else { } else {
aItem.setAttribute("isDisabled", false); if (detailItem.getAttribute("opType") == "needs-disable")
if (aItem.getAttribute("opType") == "needs-disable")
this.hideRestart(); this.hideRestart();
} }
} }
aItem.setAttribute("opType", opType); detailItem.setAttribute("opType", opType);
detailItem.setAttribute("isDisabled", isDisabled);
// Sync to the list item
let listItem = this._getElementForAddon(detailItemaddon.id);
listItem.setAttribute("isDisabled", detailItem.getAttribute("isDisabled"));
listItem.setAttribute("opType", detailItem.getAttribute("opType"));
}, },
disable: function disable(aItem) { disable: function disable() {
if (!aItem.addon) let detailItem = document.querySelector("#addons-details > .addon-item");
if (!detailItem.addon)
return; return;
let opType; let opType;
if (aItem.addon.type == "search") { let isDisabled;
aItem.setAttribute("isDisabled", true); if (detailItem.addon.type == "search") {
aItem.addon.engine.hidden = true; isDisabled = true;
detailItem.addon.engine.hidden = true;
opType = "needs-disable"; opType = "needs-disable";
} else if (aItem.addon.type == "theme") { } else if (detailItem.addon.type == "theme") {
aItem.addon.userDisabled = true; detailItem.addon.userDisabled = true;
aItem.setAttribute("isDisabled", true); isDisabled = true;
} else if (aItem.addon.type == "locale") { } else if (detailItem.addon.type == "locale") {
aItem.addon.userDisabled = true; detailItem.addon.userDisabled = true;
aItem.setAttribute("isDisabled", true); isDisabled = true;
} else { } else {
aItem.addon.userDisabled = true; detailItem.addon.userDisabled = true;
opType = this._getOpTypeForOperations(aItem.addon.pendingOperations); opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
isDisabled = !detailItem.addon.isActive;
if (aItem.addon.pendingOperations & AddonManager.PENDING_DISABLE) { if (detailItem.addon.pendingOperations & AddonManager.PENDING_DISABLE) {
this.showRestart(); this.showRestart();
} else { } else {
aItem.setAttribute("isDisabled", !aItem.addon.isActive); if (detailItem.getAttribute("opType") == "needs-enable")
if (aItem.getAttribute("opType") == "needs-enable")
this.hideRestart(); this.hideRestart();
} }
} }
aItem.setAttribute("opType", opType); detailItem.setAttribute("opType", opType);
detailItem.setAttribute("isDisabled", isDisabled);
// Sync to the list item
let listItem = this._getElementForAddon(detailItem.addon.id);
listItem.setAttribute("isDisabled", detailItem.getAttribute("isDisabled"));
listItem.setAttribute("opType", detailItem.getAttribute("opType"));
}, },
uninstall: function uninstall(aItem) { uninstall: function uninstall() {
let list = document.getElementById("addons-list"); let list = document.getElementById("addons-list");
if (!aItem.addon) { let detailItem = document.querySelector("#addons-details > .addon-item");
list.removeChild(aItem); if (!detailItem.addon)
return; return;
}
let opType; let listItem = this._getElementForAddon(detailItem.addon.id);
if (aItem.addon.type == "search") {
if (detailItem.addon.type == "search") {
// Make sure the engine isn't hidden before removing it, to make sure it's // Make sure the engine isn't hidden before removing it, to make sure it's
// visible if the user later re-adds it (works around bug 341833) // visible if the user later re-adds it (works around bug 341833)
aItem.addon.engine.hidden = false; detailItem.addon.engine.hidden = false;
Services.search.removeEngine(aItem.addon.engine); Services.search.removeEngine(detailItem.addon.engine);
// the search-engine-modified observer in browser.js will take care of // the search-engine-modified observer in browser.js will take care of
// updating the list // updating the list
} else { } else {
aItem.addon.uninstall(); detailItem.addon.uninstall();
opType = this._getOpTypeForOperations(aItem.addon.pendingOperations); let opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
if (aItem.addon.pendingOperations & AddonManager.PENDING_UNINSTALL) { if (detailItem.addon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
this.showRestart(); this.showRestart();
// A disabled addon doesn't need a restart so it has no pending ops and // A disabled addon doesn't need a restart so it has no pending ops and
// can't be cancelled // can't be cancelled
if (!aItem.addon.isActive && opType == "") if (!detailItem.addon.isActive && opType == "")
opType = "needs-uninstall"; opType = "needs-uninstall";
aItem.setAttribute("opType", opType); detailItem.setAttribute("opType", opType);
listItem.setAttribute("opType", opType);
} else { } else {
list.removeChild(aItem); list.removeChild(listItem);
history.back();
} }
} }
}, },
cancelUninstall: function ev_cancelUninstall(aItem) { cancelUninstall: function ev_cancelUninstall() {
if (!aItem.addon) let detailItem = document.querySelector("#addons-details > .addon-item");
if (!detailItem.addon)
return; return;
aItem.addon.cancelUninstall(); detailItem.addon.cancelUninstall();
this.hideRestart(); this.hideRestart();
let opType = this._getOpTypeForOperations(aItem.addon.pendingOperations); let opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
aItem.setAttribute("opType", opType); detailItem.setAttribute("opType", opType);
let listItem = this._getElementForAddon(detailItem.addon.id);
listItem.setAttribute("opType", opType);
}, },
showRestart: function showRestart(aMode) { showRestart: function showRestart(aMode) {

Просмотреть файл

@ -205,6 +205,7 @@ var BrowserApp = {
IndexedDB.init(); IndexedDB.init();
XPInstallObserver.init(); XPInstallObserver.init();
ConsoleAPI.init(); ConsoleAPI.init();
ClipboardHelper.init();
// Init LoginManager // Init LoginManager
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
@ -435,6 +436,16 @@ var BrowserApp = {
name: prefName name: prefName
}; };
// The plugin pref is actually two separate prefs, so
// we need to handle it differently
if (prefName == "plugin.enable") {
// Use a string type for java's ListPreference
pref.type = "string";
pref.value = PluginHelper.getPluginPreference();
prefs.push(pref);
continue;
}
try { try {
switch (Services.prefs.getPrefType(prefName)) { switch (Services.prefs.getPrefType(prefName)) {
case Ci.nsIPrefBranch.PREF_BOOL: case Ci.nsIPrefBranch.PREF_BOOL:
@ -490,6 +501,13 @@ var BrowserApp = {
setPreferences: function setPreferences(aPref) { setPreferences: function setPreferences(aPref) {
let json = JSON.parse(aPref); let json = JSON.parse(aPref);
// The plugin pref is actually two separate prefs, so
// we need to handle it differently
if (json.name == "plugin.enable") {
PluginHelper.setPluginPreference(json.value);
return;
}
// when sending to java, we normalized special preferences that use // when sending to java, we normalized special preferences that use
// integers and strings to represent booleans. here, we convert them back // integers and strings to represent booleans. here, we convert them back
// to their actual types so we can store them. // to their actual types so we can store them.
@ -540,10 +558,17 @@ var BrowserApp = {
let args = JSON.parse(aData); let args = JSON.parse(aData);
let uri; let uri;
if (args.engine) { if (args.engine) {
let engine = Services.search.getEngineByName(args.engine); let engine;
uri = engine.getSubmission(args.url).uri; if (args.engine == "__default__")
} else engine = Services.search.currentEngine || Services.search.defaultEngine;
else
engine = Services.search.getEngineByName(args.engine);
if (engine)
uri = engine.getSubmission(args.url).uri;
} else {
uri = URIFixup.createFixupURI(args.url, Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP); uri = URIFixup.createFixupURI(args.url, Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);
}
return uri ? uri.spec : args.url; return uri ? uri.spec : args.url;
}, },
@ -746,12 +771,6 @@ var NativeWindow = {
BrowserApp.addTab(url, { selected: false }); BrowserApp.addTab(url, { selected: false });
}); });
this.add(Strings.browser.GetStringFromName("contextmenu.changeInputMethod"),
this.textContext,
function(aTarget) {
Cc["@mozilla.org/imepicker;1"].getService(Ci.nsIIMEPicker).show();
});
this.add(Strings.browser.GetStringFromName("contextmenu.fullScreen"), this.add(Strings.browser.GetStringFromName("contextmenu.fullScreen"),
this.SelectorContext("video:not(:-moz-full-screen)"), this.SelectorContext("video:not(:-moz-full-screen)"),
function(aTarget) { function(aTarget) {
@ -1153,10 +1172,10 @@ Tab.prototype = {
get viewport() { get viewport() {
// Update the viewport to current dimensions // Update the viewport to current dimensions
this._viewport.x = this.browser.contentWindow.scrollX + this._viewport.x = (this.browser.contentWindow.scrollX +
this.viewportExcess.x; this.viewportExcess.x) || 0;
this._viewport.y = this.browser.contentWindow.scrollY + this._viewport.y = (this.browser.contentWindow.scrollY +
this.viewportExcess.y; this.viewportExcess.y) || 0;
// Transform coordinates based on zoom // Transform coordinates based on zoom
this._viewport.x = Math.round(this._viewport.x * this._viewport.zoom); this._viewport.x = Math.round(this._viewport.x * this._viewport.zoom);
@ -1185,8 +1204,8 @@ Tab.prototype = {
updateViewport: function(aReset) { updateViewport: function(aReset) {
let win = this.browser.contentWindow; let win = this.browser.contentWindow;
let zoom = (aReset ? this.getDefaultZoomLevel() : this._viewport.zoom); let zoom = (aReset ? this.getDefaultZoomLevel() : this._viewport.zoom);
let xpos = (aReset ? win.scrollX * zoom : this._viewport.x); let xpos = ((aReset && win) ? win.scrollX * zoom : this._viewport.x);
let ypos = (aReset ? win.scrollY * zoom : this._viewport.y); let ypos = ((aReset && win) ? win.scrollY * zoom : this._viewport.y);
this.viewportExcess = { x: 0, y: 0 }; this.viewportExcess = { x: 0, y: 0 };
this.viewport = { x: xpos, y: ypos, this.viewport = { x: xpos, y: ypos,
@ -1374,12 +1393,16 @@ Tab.prototype = {
let browser = BrowserApp.getBrowserForWindow(contentWin); let browser = BrowserApp.getBrowserForWindow(contentWin);
let uri = browser.currentURI.spec; let uri = browser.currentURI.spec;
let documentURI = browser.contentDocument.documentURIObject.spec;
let contentType = browser.contentDocument.contentType;
let message = { let message = {
gecko: { gecko: {
type: "Content:LocationChange", type: "Content:LocationChange",
tabID: this.id, tabID: this.id,
uri: uri uri: uri,
documentURI: documentURI,
contentType: contentType
} }
}; };
@ -2891,6 +2914,83 @@ var ConsoleAPI = {
} }
}; };
var ClipboardHelper = {
init: function() {
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.copy"), ClipboardHelper.getCopyContext(false), ClipboardHelper.copy.bind(ClipboardHelper));
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.copyAll"), ClipboardHelper.getCopyContext(true), ClipboardHelper.copy.bind(ClipboardHelper));
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.selectAll"), NativeWindow.contextmenus.textContext, ClipboardHelper.select.bind(ClipboardHelper));
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.paste"), ClipboardHelper.pasteContext, ClipboardHelper.paste.bind(ClipboardHelper));
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.changeInputMethod"), NativeWindow.contextmenus.textContext, ClipboardHelper.inputMethod.bind(ClipboardHelper));
},
get clipboardHelper() {
delete this.clipboardHelper;
return this.clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
},
get clipboard() {
delete this.clipboard;
return this.clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
},
copy: function(aElement) {
let selectionStart = aElement.selectionStart;
let selectionEnd = aElement.selectionEnd;
if (selectionStart != selectionEnd) {
string = aElement.value.slice(selectionStart, selectionEnd);
this.clipboardHelper.copyString(string);
} else {
this.clipboardHelper.copyString(aElement.value);
}
},
select: function(aElement) {
if (!aElement || !(aElement instanceof Ci.nsIDOMNSEditableElement))
return;
let target = aElement.QueryInterface(Ci.nsIDOMNSEditableElement);
target.editor.selectAll();
target.focus();
},
paste: function(aElement) {
if (!aElement || !(aElement instanceof Ci.nsIDOMNSEditableElement))
return;
let target = aElement.QueryInterface(Ci.nsIDOMNSEditableElement);
target.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
target.focus();
},
inputMethod: function(aElement) {
Cc["@mozilla.org/imepicker;1"].getService(Ci.nsIIMEPicker).show();
},
getCopyContext: function(isCopyAll) {
return {
matches: function(aElement) {
if (NativeWindow.contextmenus.textContext.matches(aElement)) {
let selectionStart = aElement.selectionStart;
let selectionEnd = aElement.selectionEnd;
if (selectionStart != selectionEnd)
return true;
else if (isCopyAll)
return true;
}
return false;
}
}
},
pasteContext: {
matches: function(aElement) {
if (NativeWindow.contextmenus.textContext.matches(aElement)) {
let flavors = ["text/unicode"];
return ClipboardHelper.clipboard.hasDataMatchingFlavors(flavors, flavors.length, Ci.nsIClipboard.kGlobalClipboard);
}
return false;
}
}
}
var PluginHelper = { var PluginHelper = {
showDoorHanger: function(aTab) { showDoorHanger: function(aTab) {
let message = Strings.browser.GetStringFromName("clickToPlayFlash.message"); let message = Strings.browser.GetStringFromName("clickToPlayFlash.message");
@ -2919,6 +3019,32 @@ var PluginHelper = {
} }
}, },
getPluginPreference: function getPluginPreference() {
let pluginDisable = Services.prefs.getBoolPref("plugin.disable");
if (pluginDisable)
return "0";
let clickToPlay = Services.prefs.getBoolPref("plugins.click_to_play");
return clickToPlay ? "2" : "1";
},
setPluginPreference: function setPluginPreference(aValue) {
switch (aValue) {
case "0": // Enable Plugins = No
Services.prefs.setBoolPref("plugin.disable", true);
Services.prefs.clearUserPref("plugins.click_to_play");
break;
case "1": // Enable Plugins = Yes
Services.prefs.clearUserPref("plugin.disable");
Services.prefs.setBoolPref("plugins.click_to_play", false);
break;
case "2": // Enable Plugins = Tap to Play (default)
Services.prefs.clearUserPref("plugin.disable");
Services.prefs.clearUserPref("plugins.click_to_play");
break;
}
},
// Mostly copied from /browser/base/content/browser.js // Mostly copied from /browser/base/content/browser.js
addPluginClickCallback: function (plugin, callbackName /*callbackArgs...*/) { addPluginClickCallback: function (plugin, callbackName /*callbackArgs...*/) {
// XXX just doing (callback)(arg) was giving a same-origin error. bug? // XXX just doing (callback)(arg) was giving a same-origin error. bug?

Просмотреть файл

@ -1,2 +1,8 @@
<!ENTITY aboutAddons.title "Add-ons Manager"> <!ENTITY aboutAddons.title "Add-ons Manager">
<!ENTITY aboutAddons.header "Add-ons"> <!ENTITY aboutAddons.header "Add-ons">
<!ENTITY aboutAddons.options "Options">
<!ENTITY addonAction.enable "Enable">
<!ENTITY addonAction.disable "Disable">
<!ENTITY addonAction.uninstall "Uninstall">
<!ENTITY addonAction.cancel "Cancel">

Просмотреть файл

@ -3,4 +3,10 @@ addonAction.disable=Disable
addonAction.uninstall=Uninstall addonAction.uninstall=Uninstall
addonAction.cancel=Cancel addonAction.cancel=Cancel
addonAction.options=Options addonAction.options=Options
addonsSearchEngine.description=Integrated Search addonsSearchEngine.description=Integrated Search
addonType.extension=Extension
addonType.theme=Theme
addonType.locale=Locale
addonType.search=Search

Просмотреть файл

@ -11,18 +11,6 @@
<!ENTITY newtab.label "New Tab"> <!ENTITY newtab.label "New Tab">
<!ENTITY closetab.label "Close Tab"> <!ENTITY closetab.label "Close Tab">
<!ENTITY cut.label "Cut">
<!ENTITY copy.label "Copy">
<!ENTITY copyAll.label "Copy All">
<!ENTITY copylink.label "Copy Link Location">
<!ENTITY paste.label "Paste">
<!ENTITY pasteAndGo.label "Paste &amp; Go">
<!ENTITY delete.label "Delete">
<!ENTITY selectAll.label "Select All">
<!ENTITY noSuggestions.label "(No suggestions)">
<!ENTITY addToDictionary.label "Add to Dictionary">
<!ENTITY inputMethod.label "Select Input Method">
<!ENTITY allPagesHeader.label "All Pages"> <!ENTITY allPagesHeader.label "All Pages">
<!ENTITY bookmarksHeader.label "Bookmarks"> <!ENTITY bookmarksHeader.label "Bookmarks">
<!ENTITY historyHeader.label "History"> <!ENTITY historyHeader.label "History">

Просмотреть файл

@ -35,11 +35,6 @@ addonsSearchEngine.description=Integrated Search
addonsConfirmInstall.title=Installing Add-on addonsConfirmInstall.title=Installing Add-on
addonsConfirmInstall.install=Install addonsConfirmInstall.install=Install
addonType.2=Extension
addonType.4=Theme
addonType.8=Locale
addonType.1024=Search
addonUpdate.checking=Checking for updates… addonUpdate.checking=Checking for updates…
addonUpdate.updating=Updating to %S addonUpdate.updating=Updating to %S
addonUpdate.updated=Updated to %S addonUpdate.updated=Updated to %S
@ -261,6 +256,11 @@ contextmenu.openInNewTab=Open Link in New Tab
contextmenu.changeInputMethod=Select Input Method contextmenu.changeInputMethod=Select Input Method
contextmenu.fullScreen=Full Screen contextmenu.fullScreen=Full Screen
contextmenu.copy=Copy
contextmenu.copyAll=Copy All
contextmenu.selectAll=Select All
contextmenu.paste=Paste
# Select UI # Select UI
selectHelper.closeMultipleSelectDialog=Done selectHelper.closeMultipleSelectDialog=Done

Просмотреть файл

@ -36,41 +36,82 @@
* ***** END LICENSE BLOCK ***** */ * ***** END LICENSE BLOCK ***** */
html { html {
font-size: 24px; font-size: 18px;
background-color: black;
} }
.addons-header { body {
border-bottom: 3px solid black; margin: 0;
}
#addons-header {
color: white;
padding: 8px;
font-size: 20px;
font-weight: bold;
text-transform: uppercase;
height: 44px;
border-bottom: 2px solid #d96016;
width: 100%;
display: -moz-box;
-moz-box-align: center;
}
#addons-list {
background-color: #0e1012;
} }
.addon-item { .addon-item {
border-bottom: 1px solid black; color: #656565;
padding: 8px; background-color: #0e1012;
border-bottom: 3px solid black;
position: relative; position: relative;
padding-top: 8px;
} }
.addon-item:last-child { .addon-item:last-child {
border-bottom: 0; border-bottom: 0;
} }
.addon-item:not([optionsURL]) .options-btn { .addon-item:not([optionsURL]) .options-header,
visibility: hidden; .addon-item[optionsURL=""] .options-header,
.addon-item:not([optionsURL]) .options-box,
.addon-item[optionsURL=""] .options-box {
display: none;
} }
/* Make room for the image */ /* Make room for the image */
.inner { .inner {
-moz-margin-start: 48px; -moz-margin-start: 56px;
-moz-margin-end: 8px;
}
.details {
display: -moz-box;
-moz-box-orient: horizontal;
width: 100%;
}
.details > div {
display: -moz-box;
-moz-box-align: end;
} }
.title { .title {
color: black; font-size: 22px;
color: white;
font-weight: bold;
} }
.version { .version {
/* The addon title is not localized, so keep the margin on the left side */ /* The addon title is not localized, so keep the margin on the left side */
margin-left: 12px; margin-left: 12px;
font-size: 18px; font-size: 18px;
color: gray; -moz-box-flex: 1;
}
.tag {
text-align: end;
} }
.description { .description {
@ -82,6 +123,31 @@ html {
.buttons { .buttons {
padding-top: 8px; padding-top: 8px;
display: -moz-box;
-moz-box-orient: horizontal;
width: 100%;
}
.buttons > button {
-moz-appearance: none;
color: white;
font-size: 18px !important;
border: 2px solid transparent;
border-top-color: black;
-moz-border-start-color: black;
background-image: none;
background-color: #17181a;
border-radius: 0px !important;
-moz-box-flex: 1;
padding: 20px 8px;
}
.buttons > button[disabled="true"] {
color: #b5b5b5;
}
.buttons:first-child {
-moz-border-start-color: transparent;
} }
body[dir="ltr"] .favicon { body[dir="ltr"] .favicon {
@ -99,9 +165,3 @@ body[dir="ltr"] .favicon {
height: 32px; height: 32px;
position: absolute; position: absolute;
} }
button {
color: black;
font-size: 28px !important;
padding: 5px;
}

Просмотреть файл

@ -1 +1 @@
NSPR_4_9_BETA4 NSPR_4_9_BETA5

Просмотреть файл

@ -42,4 +42,3 @@
*/ */
#error "Do not include this header file." #error "Do not include this header file."

355
nsprpub/configure поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -166,6 +166,36 @@ i?86-*android*)
;; ;;
esac esac
dnl ========================================================
dnl = Gonk is a fork of Android used for Mozilla's B2G project.
dnl = Configuration is done largely by the top level config
dnl = and the specified gonk directory doesn't matter here.
dnl ========================================================
AC_ARG_WITH(gonk,
[ --with-gonk=DIR
location of gonk dir],
gonkdir=$withval)
if test -n "$gonkdir" ; then
dnl Most things are directly configured by env vars when building for gonk
dnl prevent cross compile section from using these flags as host flags
if test -z "$HOST_CPPFLAGS" ; then
HOST_CPPFLAGS=" "
fi
if test -z "$HOST_CFLAGS" ; then
HOST_CFLAGS=" "
fi
if test -z "$HOST_CXXFLAGS" ; then
HOST_CXXFLAGS=" "
fi
if test -z "$HOST_LDFLAGS" ; then
HOST_LDFLAGS=" "
fi
AC_DEFINE(ANDROID)
else
case "$target" in case "$target" in
*-android*|*-linuxandroid*) *-android*|*-linuxandroid*)
if test -z "$android_ndk" ; then if test -z "$android_ndk" ; then
@ -240,6 +270,7 @@ case "$target" in
AC_DEFINE(ANDROID) AC_DEFINE(ANDROID)
;; ;;
esac esac
fi
dnl ======================================================== dnl ========================================================
dnl = dnl =
@ -1358,6 +1389,9 @@ case "$target" in
CPU_ARCH=i386 CPU_ARCH=i386
fi fi
;; ;;
x86_64)
CPU_ARCH=x86_64
;;
*) *)
CPU_ARCH=ppc CPU_ARCH=ppc
;; ;;
@ -3308,55 +3342,68 @@ dnl ========================================================
dnl Generate output files. dnl Generate output files.
dnl ======================================================== dnl ========================================================
MAKEFILES=" MAKEFILES="
Makefile Makefile
config/Makefile config/Makefile
config/autoconf.mk config/autoconf.mk
config/nsprincl.mk config/nsprincl.mk
config/nsprincl.sh config/nsprincl.sh
config/nspr-config config/nspr-config
lib/Makefile lib/Makefile
lib/ds/Makefile lib/ds/Makefile
lib/libc/Makefile lib/libc/Makefile
lib/libc/include/Makefile lib/libc/include/Makefile
lib/libc/src/Makefile lib/libc/src/Makefile
lib/tests/Makefile lib/tests/Makefile
pkg/Makefile pkg/Makefile
pkg/linux/Makefile pr/Makefile
pkg/solaris/Makefile pr/include/Makefile
pkg/solaris/SUNWpr/Makefile pr/include/md/Makefile
pkg/solaris/SUNWprd/Makefile pr/include/obsolete/Makefile
pr/Makefile pr/include/private/Makefile
pr/include/Makefile pr/src/Makefile
pr/include/md/Makefile pr/src/io/Makefile
pr/include/obsolete/Makefile pr/src/linking/Makefile
pr/include/private/Makefile pr/src/malloc/Makefile
pr/src/Makefile pr/src/md/Makefile
pr/src/io/Makefile pr/src/md/${PR_MD_ARCH_DIR}/Makefile
pr/src/linking/Makefile pr/src/memory/Makefile
pr/src/malloc/Makefile pr/src/misc/Makefile
pr/src/md/Makefile pr/src/threads/Makefile
pr/src/md/${PR_MD_ARCH_DIR}/Makefile pr/tests/Makefile
pr/src/memory/Makefile pr/tests/dll/Makefile
pr/src/misc/Makefile
pr/src/threads/Makefile
pr/tests/Makefile
pr/tests/dll/Makefile
" "
dnl lib/tests/Makefile if test "$OS_TARGET" = "Linux"; then
dnl pr/tests/w16gui/Makefile MAKEFILES="$MAKEFILES
dnl tools/Makefile pkg/linux/Makefile
"
elif test "$OS_TARGET" = "SunOS"; then
MAKEFILES="$MAKEFILES
pkg/solaris/Makefile
pkg/solaris/SUNWpr/Makefile
pkg/solaris/SUNWprd/Makefile
"
fi
if test -z "$USE_PTHREADS" && test -z "$USE_BTHREADS"; then if test -z "$USE_PTHREADS" && test -z "$USE_BTHREADS"; then
MAKEFILES="$MAKEFILES pr/src/threads/combined/Makefile" MAKEFILES="$MAKEFILES
pr/src/threads/combined/Makefile
"
elif test -n "$USE_PTHREADS"; then elif test -n "$USE_PTHREADS"; then
MAKEFILES="$MAKEFILES pr/src/pthreads/Makefile" MAKEFILES="$MAKEFILES
pr/src/pthreads/Makefile
"
elif test -n "$USE_BTHREADS"; then elif test -n "$USE_BTHREADS"; then
MAKEFILES="$MAKEFILES pr/src/bthreads/Makefile" MAKEFILES="$MAKEFILES
pr/src/bthreads/Makefile
"
fi fi
if test -n "$USE_CPLUS"; then if test -n "$USE_CPLUS"; then
MAKEFILES="$MAKEFILES pr/src/cplus/Makefile pr/src/cplus/tests/Makefile" MAKEFILES="$MAKEFILES
pr/src/cplus/Makefile
pr/src/cplus/tests/Makefile
"
fi fi
echo $MAKEFILES > unallmakefiles echo $MAKEFILES > unallmakefiles

Просмотреть файл

@ -1497,8 +1497,9 @@ PR_ParseTimeStringToExplodedTime(
/* "-" is ignored at the beginning of a token if we have not yet /* "-" is ignored at the beginning of a token if we have not yet
parsed a year (e.g., the second "-" in "30-AUG-1966"), or if parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
the character after the dash is not a digit. */ the character after the dash is not a digit. */
if (*rest == '-' && ((rest > string && isalpha(rest[-1]) && year < 0) if (*rest == '-' && ((rest > string &&
|| rest[1] < '0' || rest[1] > '9')) isalpha((unsigned char)rest[-1]) && year < 0) ||
rest[1] < '0' || rest[1] > '9'))
{ {
rest++; rest++;
goto SKIP_MORE; goto SKIP_MORE;

Просмотреть файл

@ -298,7 +298,6 @@ SHELL_WRAPPER0(executeNextRunnable)
SHELL_WRAPPER1(cameraCallbackBridge, jbyteArray) SHELL_WRAPPER1(cameraCallbackBridge, jbyteArray)
SHELL_WRAPPER1(notifyUriVisited, jstring) SHELL_WRAPPER1(notifyUriVisited, jstring)
SHELL_WRAPPER3(notifyBatteryChange, jdouble, jboolean, jdouble); SHELL_WRAPPER3(notifyBatteryChange, jdouble, jboolean, jdouble);
SHELL_WRAPPER1_WITH_RETURN(canCreateFixupURI, bool, jstring);
SHELL_WRAPPER3(notifySmsReceived, jstring, jstring, jlong); SHELL_WRAPPER3(notifySmsReceived, jstring, jstring, jlong);
static void * xul_handle = NULL; static void * xul_handle = NULL;
@ -704,7 +703,6 @@ loadLibs(const char *apkName)
GETFUNC(cameraCallbackBridge); GETFUNC(cameraCallbackBridge);
GETFUNC(notifyUriVisited); GETFUNC(notifyUriVisited);
GETFUNC(notifyBatteryChange); GETFUNC(notifyBatteryChange);
GETFUNC(canCreateFixupURI);
GETFUNC(notifySmsReceived); GETFUNC(notifySmsReceived);
#undef GETFUNC #undef GETFUNC
sStartupTimeline = (uint64_t *)__wrap_dlsym(xul_handle, "_ZN7mozilla15StartupTimeline16sStartupTimelineE"); sStartupTimeline = (uint64_t *)__wrap_dlsym(xul_handle, "_ZN7mozilla15StartupTimeline16sStartupTimelineE");

Просмотреть файл

@ -37,20 +37,23 @@
MAKEFILES_crypto=" MAKEFILES_crypto="
services/crypto/Makefile services/crypto/Makefile
" services/crypto/component/Makefile
"
MAKEFILES_sync=" MAKEFILES_sync="
services/sync/Makefile services/sync/Makefile
services/sync/locales/Makefile services/sync/locales/Makefile
" "
if [ "$ENABLE_TESTS" ]; then
MAKEFILES_crypto="$MAKEFILES_crypto services/crypto/tests/Makefile"
MAKEFILES_sync="$MAKEFILES_sync services/sync/tests/Makefile"
fi
add_makefiles " add_makefiles "
services/Makefile services/Makefile
$MAKEFILES_crypto $MAKEFILES_crypto
$MAKEFILES_sync $MAKEFILES_sync
"
if [ "$ENABLE_TESTS" ]; then
add_makefiles "
services/crypto/tests/Makefile
services/sync/tests/Makefile
" "
fi

Просмотреть файл

@ -4,7 +4,7 @@ Universal manifests for Mozilla test harnesses
What ManifestDestiny gives you: What ManifestDestiny gives you:
* manifests are (ordered) lists of tests * manifests are ordered lists of tests
* tests may have an arbitrary number of key, value pairs * tests may have an arbitrary number of key, value pairs
* the parser returns an ordered list of test data structures, which * the parser returns an ordered list of test data structures, which
are just dicts with some keys. For example, a test with no are just dicts with some keys. For example, a test with no
@ -23,6 +23,14 @@ additional key, value metadata to each test.
# Why have test manifests? # Why have test manifests?
It is desirable to have a unified format for test manifests for testing
[mozilla-central](http://hg.mozilla.org/mozilla-central), etc.
* It is desirable to be able to selectively enable or disable tests based on platform or other conditions. This should be easy to do. Currently, since many of the harnesses just crawl directories, there is no effective way of disabling a test except for removal from mozilla-central
* It is desriable to do this in a universal way so that enabling and disabling tests as well as other tasks are easily accessible to a wider audience than just those intimately familiar with the specific test framework.
* It is desirable to have other metadata on top of the test. For instance, let's say a test is marked as skipped. It would be nice to give the reason why.
Most Mozilla test harnesses work by crawling a directory structure. Most Mozilla test harnesses work by crawling a directory structure.
While this is straight-forward, manifests offer several practical While this is straight-forward, manifests offer several practical
advantages:: advantages::
@ -37,8 +45,8 @@ advantages::
removing it from the tree and a bug filed with the appropriate removing it from the tree and a bug filed with the appropriate
reason: reason:
[test_broken.js] [test_broken.js]
disabled = https://bugzilla.mozilla.org/show_bug.cgi?id=123456 disabled = https://bugzilla.mozilla.org/show_bug.cgi?id=123456
* ability to run different (subsets of) tests on different * ability to run different (subsets of) tests on different
platforms. Traditionally, we've done a bit of magic or had the test platforms. Traditionally, we've done a bit of magic or had the test
@ -46,8 +54,8 @@ advantages::
can mark what platforms a test will or will not run on and change can mark what platforms a test will or will not run on and change
these without changing the test. these without changing the test.
[test_works_on_windows_only.js] [test_works_on_windows_only.js]
run-if = os == 'win' run-if = os == 'win'
* ability to markup tests with metadata. We have a large, complicated, * ability to markup tests with metadata. We have a large, complicated,
and always changing infrastructure. key, value metadata may be used and always changing infrastructure. key, value metadata may be used
@ -65,32 +73,32 @@ advantages::
Manifests are .ini file with the section names denoting the path Manifests are .ini file with the section names denoting the path
relative to the manifest: relative to the manifest:
[foo.js] [foo.js]
[bar.js] [bar.js]
[fleem.js] [fleem.js]
The sections are read in order. In addition, tests may include The sections are read in order. In addition, tests may include
arbitrary key, value metadata to be used by the harness. You may also arbitrary key, value metadata to be used by the harness. You may also
have a `[DEFAULT]` section that will give key, value pairs that will have a `[DEFAULT]` section that will give key, value pairs that will
be inherited by each test unless overridden: be inherited by each test unless overridden:
[DEFAULT] [DEFAULT]
type = restart type = restart
[lilies.js] [lilies.js]
color = white color = white
[daffodils.js] [daffodils.js]
color = yellow color = yellow
type = other type = other
# override type from DEFAULT # override type from DEFAULT
[roses.js] [roses.js]
color = red color = red
You can also include other manifests: You can also include other manifests:
[include:subdir/anothermanifest.ini] [include:subdir/anothermanifest.ini]
Manifests are included relative to the directory of the manifest with Manifests are included relative to the directory of the manifest with
the `[include:]` directive unless they are absolute paths. the `[include:]` directive unless they are absolute paths.
@ -109,7 +117,7 @@ terms).
This data corresponds to a one-line manifest: This data corresponds to a one-line manifest:
[testToolbar/testBackForwardButtons.js] [testToolbar/testBackForwardButtons.js]
If additional key, values were specified, they would be in this dict If additional key, values were specified, they would be in this dict
as well. as well.
@ -128,13 +136,13 @@ integration layer. This should allow whatever sort of logic is
desired. For instance, if in yourtestharness you wanted to run only on desired. For instance, if in yourtestharness you wanted to run only on
mondays for a certain class of tests: mondays for a certain class of tests:
tests = [] tests = []
for test in manifests.tests: for test in manifests.tests:
if 'runOnDay' in test: if 'runOnDay' in test:
if calendar.day_name[calendar.weekday(*datetime.datetime.now().timetuple()[:3])].lower() == test['runOnDay'].lower(): if calendar.day_name[calendar.weekday(*datetime.datetime.now().timetuple()[:3])].lower() == test['runOnDay'].lower():
tests.append(test) tests.append(test)
else: else:
tests.append(test) tests.append(test)
To recap: To recap:
* the manifests allow you to specify test data * the manifests allow you to specify test data
@ -146,7 +154,7 @@ http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests/
Additional manifest files may be included with an `[include:]` directive: Additional manifest files may be included with an `[include:]` directive:
[include:path-to-additional-file.manifest] [include:path-to-additional-file.manifest]
The path to included files is relative to the current manifest. The path to included files is relative to the current manifest.
@ -183,7 +191,7 @@ in particular.
A test harness will normally call `TestManifest.active_tests`: A test harness will normally call `TestManifest.active_tests`:
def active_tests(self, exists=True, disabled=True, **tags): def active_tests(self, exists=True, disabled=True, **tags):
The manifests are passed to the `__init__` or `read` methods with The manifests are passed to the `__init__` or `read` methods with
appropriate arguments. `active_tests` then allows you to select the appropriate arguments. `active_tests` then allows you to select the
@ -216,7 +224,7 @@ files. Run `manifestparser help create` for usage information.
To copy tests and manifests from a source: To copy tests and manifests from a source:
manifestparser [options] copy from_manifest to_directory -tag1 -tag2 --key1=value1 key2=value2 ... manifestparser [options] copy from_manifest to_directory -tag1 -tag2 --key1=value1 key2=value2 ...
# Upating Tests # Upating Tests
@ -224,7 +232,81 @@ To copy tests and manifests from a source:
To update the tests associated with with a manifest from a source To update the tests associated with with a manifest from a source
directory: directory:
manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ... manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
# Usage example
Here is an example of how to create manifests for a directory tree and
update the tests listed in the manifests from an external source.
## Creating Manifests
Let's say you want to make a series of manifests for a given directory structure containing `.js` test files:
testing/mozmill/tests/firefox/
testing/mozmill/tests/firefox/testAwesomeBar/
testing/mozmill/tests/firefox/testPreferences/
testing/mozmill/tests/firefox/testPrivateBrowsing/
testing/mozmill/tests/firefox/testSessionStore/
testing/mozmill/tests/firefox/testTechnicalTools/
testing/mozmill/tests/firefox/testToolbar/
testing/mozmill/tests/firefox/restartTests
You can use `manifestparser create` to do this:
$ manifestparser help create
Usage: manifestparser.py [options] create directory <directory> <...>
create a manifest from a list of directories
Options:
-p PATTERN, --pattern=PATTERN
glob pattern for files
-i IGNORE, --ignore=IGNORE
directories to ignore
-w IN_PLACE, --in-place=IN_PLACE
Write .ini files in place; filename to write to
We only want `.js` files and we want to skip the `restartTests` directory.
We also want to write a manifest per directory, so I use the `--in-place`
option to write the manifests:
manifestparser create . -i restartTests -p '*.js' -w manifest.ini
This creates a manifest.ini per directory that we care about with the JS test files:
testing/mozmill/tests/firefox/manifest.ini
testing/mozmill/tests/firefox/testAwesomeBar/manifest.ini
testing/mozmill/tests/firefox/testPreferences/manifest.ini
testing/mozmill/tests/firefox/testPrivateBrowsing/manifest.ini
testing/mozmill/tests/firefox/testSessionStore/manifest.ini
testing/mozmill/tests/firefox/testTechnicalTools/manifest.ini
testing/mozmill/tests/firefox/testToolbar/manifest.ini
The top-level `manifest.ini` merely has `[include:]` references to the sub manifests:
[include:testAwesomeBar/manifest.ini]
[include:testPreferences/manifest.ini]
[include:testPrivateBrowsing/manifest.ini]
[include:testSessionStore/manifest.ini]
[include:testTechnicalTools/manifest.ini]
[include:testToolbar/manifest.ini]
Each sub-level manifest contains the (`.js`) test files relative to it.
## Updating the tests from manifests
You may need to update tests as given in manifests from a different source directory.
`manifestparser update` was made for just this purpose:
Usage: manifestparser [options] update manifest directory -tag1 -tag2 --key1=value1 --key2=value2 ...
update the tests as listed in a manifest from a directory
To update from a directory of tests in `~/mozmill/src/mozmill-tests/firefox/` run:
manifestparser update manifest.ini ~/mozmill/src/mozmill-tests/firefox/
# Tests # Tests
@ -252,20 +334,20 @@ Run `manifestparser help` for usage information.
To create a manifest from a set of directories: To create a manifest from a set of directories:
manifestparser [options] create directory <directory> <...> [create-options] manifestparser [options] create directory <directory> <...> [create-options]
To output a manifest of tests: To output a manifest of tests:
manifestparser [options] write manifest <manifest> <...> -tag1 -tag2 --key1=value1 --key2=value2 ... manifestparser [options] write manifest <manifest> <...> -tag1 -tag2 --key1=value1 --key2=value2 ...
To copy tests and manifests from a source: To copy tests and manifests from a source:
manifestparser [options] copy from_manifest to_manifest -tag1 -tag2 --key1=value1 key2=value2 ... manifestparser [options] copy from_manifest to_manifest -tag1 -tag2 --key1=value1 key2=value2 ...
To update the tests associated with with a manifest from a source To update the tests associated with with a manifest from a source
directory: directory:
manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ... manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
# Design Considerations # Design Considerations
@ -309,6 +391,14 @@ through several design considerations.
installation. installation.
# Developing ManifestDestiny
ManifestDestiny is developed and maintained by Mozilla's
[Automation and Testing Team](https://wiki.mozilla.org/Auto-tools).
The project page is located at
https://wiki.mozilla.org/Auto-tools/Projects/ManifestDestiny .
# Historical Reference # Historical Reference
Date-ordered list of links about how manifests came to be where they are today:: Date-ordered list of links about how manifests came to be where they are today::

Просмотреть файл

@ -3,6 +3,7 @@ from devicemanager import DeviceManager, DMError
import re import re
import os import os
import sys import sys
import tempfile
class DeviceManagerADB(DeviceManager): class DeviceManagerADB(DeviceManager):
@ -13,6 +14,7 @@ class DeviceManagerADB(DeviceManager):
self.retries = 0 self.retries = 0
self._sock = None self._sock = None
self.useRunAs = False self.useRunAs = False
self.useZip = False
self.packageName = None self.packageName = None
if packageName == None: if packageName == None:
if os.getenv('USER'): if os.getenv('USER'):
@ -30,6 +32,10 @@ class DeviceManagerADB(DeviceManager):
except: except:
self.useRunAs = False self.useRunAs = False
self.packageName = None self.packageName = None
try:
self.verifyZip()
except:
self.useZip = False
try: try:
# a test to see if we have root privs # a test to see if we have root privs
files = self.listFiles("/data/data") files = self.listFiles("/data/data")
@ -103,31 +109,41 @@ class DeviceManagerADB(DeviceManager):
def pushDir(self, localDir, remoteDir): def pushDir(self, localDir, remoteDir):
# adb "push" accepts a directory as an argument, but if the directory # adb "push" accepts a directory as an argument, but if the directory
# contains symbolic links, the links are pushed, rather than the linked # contains symbolic links, the links are pushed, rather than the linked
# files; we push file-by-file to get around this limitation # files; we either zip/unzip or push file-by-file to get around this
# limitation
try: try:
if (not self.dirExists(remoteDir)): if (self.useZip):
self.mkDirs(remoteDir+"/x") localZip = tempfile.mktemp()+".zip"
for root, dirs, files in os.walk(localDir, followlinks='true'): remoteZip = remoteDir + "/adbdmtmp.zip"
relRoot = os.path.relpath(root, localDir) subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir)
for file in files: self.pushFile(localZip, remoteZip)
localFile = os.path.join(root, file) os.remove(localZip)
remoteFile = remoteDir + "/" self.checkCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir])
if (relRoot!="."): self.checkCmdAs(["shell", "rm", remoteZip])
remoteFile = remoteFile + relRoot + "/" else:
remoteFile = remoteFile + file if (not self.dirExists(remoteDir)):
self.pushFile(localFile, remoteFile) self.mkDirs(remoteDir+"/x")
for dir in dirs: for root, dirs, files in os.walk(localDir, followlinks='true'):
targetDir = remoteDir + "/" relRoot = os.path.relpath(root, localDir)
if (relRoot!="."): for file in files:
targetDir = targetDir + relRoot + "/" localFile = os.path.join(root, file)
targetDir = targetDir + dir remoteFile = remoteDir + "/"
if (not self.dirExists(targetDir)): if (relRoot!="."):
self.mkDir(targetDir) remoteFile = remoteFile + relRoot + "/"
remoteFile = remoteFile + file
self.pushFile(localFile, remoteFile)
for dir in dirs:
targetDir = remoteDir + "/"
if (relRoot!="."):
targetDir = targetDir + relRoot + "/"
targetDir = targetDir + dir
if (not self.dirExists(targetDir)):
self.mkDir(targetDir)
self.checkCmdAs(["shell", "chmod", "777", remoteDir]) self.checkCmdAs(["shell", "chmod", "777", remoteDir])
return True return remoteDir
except: except:
print "pushing " + localDir + " to " + remoteDir + " failed" print "pushing " + localDir + " to " + remoteDir + " failed"
return False return None
# external function # external function
# returns: # returns:
@ -241,11 +257,25 @@ class DeviceManagerADB(DeviceManager):
acmd = ["shell", "am","start"] acmd = ["shell", "am","start"]
cmd = ' '.join(cmd).strip() cmd = ' '.join(cmd).strip()
i = cmd.find(" ") i = cmd.find(" ")
# SUT identifies the URL by looking for :\\ -- another strategy to consider
re_url = re.compile('^[http|file|chrome|about].*')
last = cmd.rfind(" ")
uri = ""
args = ""
if re_url.match(cmd[last:].strip()):
args = cmd[i:last].strip()
uri = cmd[last:].strip()
else:
args = cmd[i:].strip()
acmd.append("-n") acmd.append("-n")
acmd.append(cmd[0:i] + "/.App") acmd.append(cmd[0:i] + "/.App")
acmd.append("--es") acmd.append("--es")
acmd.append("args") if args != "":
acmd.append(cmd[i:]) acmd.append("args")
acmd.append(args)
if uri != "":
acmd.append("-d")
acmd.append(''.join(['\'',uri, '\'']));
print acmd print acmd
self.checkCmd(acmd) self.checkCmd(acmd)
return outputFile; return outputFile;
@ -578,3 +608,25 @@ class DeviceManagerADB(DeviceManager):
self.checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"]) self.checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"])
self.checkCmd(["shell", "run-as", packageName, "rm", "-r", devroot + "/sanity"]) self.checkCmd(["shell", "run-as", packageName, "rm", "-r", devroot + "/sanity"])
def isUnzipAvailable(self):
data = self.runCmd(["shell", "unzip"]).stdout.read()
if (re.search('Usage', data)):
return True
else:
return False
def isLocalZipAvailable(self):
try:
subprocess.check_call(["zip", "-?"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except:
return False
return True
def verifyZip(self):
# If "zip" can be run locally, and "unzip" can be run remotely, then pushDir
# can use these to push just one file per directory -- a significant
# optimization for large directories.
self.useZip = False
if (self.isUnzipAvailable() and self.isLocalZipAvailable()):
print "will use zip to push directories"
self.useZip = True

Просмотреть файл

@ -110,31 +110,51 @@ def _extract(path, extdir=None, delete=False):
If delete is set to True, deletes the bundle at path If delete is set to True, deletes the bundle at path
Returns the list of top level files that were extracted Returns the list of top level files that were extracted
""" """
assert not os.path.isfile(extdir), "extdir cannot be a file"
if extdir is None:
extdir = os.path.dirname(path)
elif not os.path.isdir(extdir):
os.makedirs(extdir)
if zipfile.is_zipfile(path): if zipfile.is_zipfile(path):
bundle = zipfile.ZipFile(path) bundle = zipfile.ZipFile(path)
namelist = bundle.namelist() namelist = bundle.namelist()
if hasattr(bundle, 'extractall'):
bundle.extractall(path=extdir)
# zipfile.extractall doesn't exist in Python 2.5
else:
for name in namelist:
filename = os.path.realpath(os.path.join(extdir, name))
if name.endswith("/"):
os.makedirs(filename)
else:
path = os.path.dirname(filename)
if not os.path.isdir(path):
os.makedirs(path)
dest = open(filename, "wb")
dest.write(bundle.read(name))
dest.close()
elif tarfile.is_tarfile(path): elif tarfile.is_tarfile(path):
bundle = tarfile.open(path) bundle = tarfile.open(path)
namelist = bundle.getnames() namelist = bundle.getnames()
if hasattr(bundle, 'extractall'):
bundle.extractall(path=extdir)
# tarfile.extractall doesn't exist in Python 2.4
else:
for name in namelist:
bundle.extract(name, path=extdir)
else: else:
return return
if extdir is None:
extdir = os.path.dirname(path)
elif not os.path.exists(extdir):
os.makedirs(extdir)
bundle.extractall(path=extdir)
bundle.close() bundle.close()
if delete: if delete:
os.remove(path) os.remove(path)
# namelist returns paths with forward slashes even in windows # namelist returns paths with forward slashes even in windows
top_level_files = [os.path.join(extdir, name) for name in namelist top_level_files = [os.path.join(extdir, name) for name in namelist
if len(name.rstrip('/').split('/')) == 1] if len(name.rstrip('/').split('/')) == 1]
# namelist doesn't include folders in windows, append these to the list # namelist doesn't include folders, append these to the list
if mozinfo.isWin: for name in namelist:
for name in namelist: root = os.path.join(extdir, name[:name.find('/')])
root = name[:name.find('/')] if root not in top_level_files:
if root not in top_level_files: top_level_files.append(root)
top_level_files.append(root)
return top_level_files return top_level_files
def _install_dmg(src, dest): def _install_dmg(src, dest):

Просмотреть файл

@ -100,10 +100,12 @@ TestSuite.prototype.loadTest = function(test) {
log.log('TEST-END', test.name + ' ' + runTime + fThreshold); log.log('TEST-END', test.name + ' ' + runTime + fThreshold);
} catch (e) { } catch (e) {
log.error(test.name + ' | ' + e); log.error(test.name + ' | ' + e);
log.debug(test.name + ' | Traceback:'); if (e['stack'] !== undefined) {
lines = e.stack.split('\n'); log.debug(test.name + ' | Traceback:');
for (let i = 0; i < lines.length - 1; ++i) { lines = e.stack.split('\n');
log.debug('\t' + lines[i]); for (let i = 0; i < lines.length - 1; ++i) {
log.debug('\t' + lines[i]);
}
} }
} }
}; };

Просмотреть файл

@ -36,6 +36,8 @@
from mozprocess import ProcessHandler from mozprocess import ProcessHandler
from pepresults import Results from pepresults import Results
from time import sleep
from threading import Thread
import mozlog import mozlog
import os import os
@ -57,6 +59,13 @@ class PepProcess(ProcessHandler):
self.logger = mozlog.getLogger('PEP') self.logger = mozlog.getLogger('PEP')
results.fails[str(None)] = [] results.fails[str(None)] = []
def waitForQuit(self, timeout=5):
for i in range(1, timeout):
if self.proc.returncode != None:
return
sleep(1)
self.proc.kill()
def processOutputLine(self, line): def processOutputLine(self, line):
""" """
Callback called on each line of output Callback called on each line of output
@ -68,6 +77,11 @@ class PepProcess(ProcessHandler):
# The output is generated from the Peptest extension # The output is generated from the Peptest extension
# Format is 'PEP <LEVEL> <MSG>' where <MSG> can have multiple tokens # Format is 'PEP <LEVEL> <MSG>' where <MSG> can have multiple tokens
# The content of <MSG> depends on the <LEVEL> # The content of <MSG> depends on the <LEVEL>
if line.find('Test Suite Finished') != -1:
thread = Thread(target=self.waitForQuit)
thread.setDaemon(True) # don't hang on quit
thread.start()
level = tokens[1] level = tokens[1]
if level == 'TEST-START': if level == 'TEST-START':
results.currentTest = tokens[2].rstrip() results.currentTest = tokens[2].rstrip()
@ -81,11 +95,12 @@ class PepProcess(ProcessHandler):
threshold = 0.0 threshold = 0.0
msg = results.currentTest \ msg = results.currentTest \
+ ' | fail threshold: ' + str(threshold) \ + ' | fail threshold: ' + str(threshold)
+ ' | metric: ' + str(metric)
if metric > threshold: if metric > threshold:
msg += ' < metric: ' + str(metric)
self.logger.testFail(msg) self.logger.testFail(msg)
else: else:
msg += ' >= metric: ' + str(metric)
self.logger.testPass(msg) self.logger.testPass(msg)
self.logger.testEnd( self.logger.testEnd(

Просмотреть файл

@ -68,25 +68,53 @@ def isURL(path):
def extract(path, extdir=None, delete=False): def extract(path, extdir=None, delete=False):
""" """
Takes in a tar or zip file and extracts it to extdir Takes in a tar or zip file and extracts it to extdir
If extdir is not specified, extracts to path If extdir is not specified, extracts to os.path.dirname(path)
If delete is set to True, deletes the bundle at path If delete is set to True, deletes the bundle at path
Returns the list of top level files that were extracted Returns the list of top level files that were extracted
""" """
assert not os.path.isfile(extdir), "extdir cannot be a file"
if extdir is None:
extdir = os.path.dirname(path)
elif not os.path.isdir(extdir):
os.makedirs(extdir)
if zipfile.is_zipfile(path): if zipfile.is_zipfile(path):
bundle = zipfile.ZipFile(path) bundle = zipfile.ZipFile(path)
namelist = bundle.namelist() namelist = bundle.namelist()
if hasattr(bundle, 'extractall'):
bundle.extractall(path=extdir)
# zipfile.extractall doesn't exist in Python 2.5
else:
for name in namelist:
filename = os.path.realpath(os.path.join(extdir, name))
if name.endswith("/"):
os.makedirs(filename)
else:
path = os.path.dirname(filename)
if not os.path.isdir(path):
os.makedirs(path)
dest = open(filename, "wb")
dest.write(bundle.read(name))
dest.close()
elif tarfile.is_tarfile(path): elif tarfile.is_tarfile(path):
bundle = tarfile.open(path) bundle = tarfile.open(path)
namelist = bundle.getnames() namelist = bundle.getnames()
if hasattr(bundle, 'extractall'):
bundle.extractall(path=extdir)
# tarfile.extractall doesn't exist in Python 2.4
else:
for name in namelist:
bundle.extract(name, path=extdir)
else: else:
return return
if extdir is None:
extdir = os.path.dirname(path)
elif not os.path.exists(extdir):
os.makedirs(extdir)
bundle.extractall(path=extdir)
bundle.close() bundle.close()
if delete: if delete:
os.remove(path) os.remove(path)
return [os.path.join(extdir, name) for name in namelist # namelist returns paths with forward slashes even in windows
if len(name.rstrip(os.sep).split(os.sep)) == 1] top_level_files = [os.path.join(extdir, name) for name in namelist
if len(name.rstrip('/').split('/')) == 1]
# namelist doesn't include folders, append these to the list
for name in namelist:
root = os.path.join(extdir, name[:name.find('/')])
if root not in top_level_files:
top_level_files.append(root)
return top_level_files

Просмотреть файл

@ -82,6 +82,7 @@ class Peptest():
testObj = {} testObj = {}
testObj['path'] = os.path.realpath(self.options.testPath) testObj['path'] = os.path.realpath(self.options.testPath)
testObj['name'] = os.path.basename(self.options.testPath) testObj['name'] = os.path.basename(self.options.testPath)
testObj['here'] = os.path.dirname(testObj['path'])
tests.append(testObj) tests.append(testObj)
else: else:
# a test manifest was passed in # a test manifest was passed in

Просмотреть файл

@ -44,12 +44,15 @@ try:
except IOError: except IOError:
description = '' description = ''
version = "0.0" version = "0.1"
dependencies = ['mozprofile', dependencies = ['ManifestDestiny',
'mozhttpd',
'mozlog',
'mozprofile >= 0.1',
'mozprocess', 'mozprocess',
'mozrunner >= 3.0b3', 'mozrunner >= 3.0b3',
'mozlog'] ]
setup(name='peptest', setup(name='peptest',
version=version, version=version,

Просмотреть файл

@ -359,8 +359,6 @@ FormHistory.prototype = {
}, },
get dbConnection() { get dbConnection() {
let connection;
// Make sure dbConnection can't be called from now to prevent infinite loops. // Make sure dbConnection can't be called from now to prevent infinite loops.
delete FormHistory.prototype.dbConnection; delete FormHistory.prototype.dbConnection;

Просмотреть файл

@ -144,11 +144,7 @@ function endTest() {
.getMostRecentWindow("Download:Manager") .getMostRecentWindow("Download:Manager")
.close(); .close();
Cc["@mozilla.org/browser/nav-history-service;1"] waitForClearHistory(SimpleTest.finish);
.getService(Ci.nsINavHistoryService)
.removeAllPages();
SimpleTest.finish();
} }
]]> ]]>

Просмотреть файл

@ -168,3 +168,16 @@ function setCleanState()
let win = getDMWindow(); let win = getDMWindow();
if (win) win.close(); if (win) win.close();
} }
/**
* Clears history invoking callback when done.
*/
function waitForClearHistory(aCallback) {
Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Services.obs.addObserver(function observeClearHistory(aSubject, aTopic) {
Services.obs.removeObserver(observeClearHistory, aTopic);
aCallback();
}, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
PlacesUtils.bhistory.removeAllPages();
}

Просмотреть файл

@ -58,7 +58,7 @@
<xul:spacer flex="1"/> <xul:spacer flex="1"/>
<xul:box class="icon"/> <xul:box class="icon"/>
<html:div class="msg msgUnsupported">&missingPlugin;</html:div> <html:div class="msg msgUnsupported">&missingPlugin;</html:div>
<html:div class="msg msgClickToPlay"><html:a class="clickToPlayLink" href="">&clickToPlayPlugin;</html:a></html:div> <html:div class="msg msgClickToPlay">&clickToPlayPlugin;</html:div>
<html:div class="msg msgDisabled">&disabledPlugin;</html:div> <html:div class="msg msgDisabled">&disabledPlugin;</html:div>
<html:div class="msg msgBlocked">&blockedPlugin.label;</html:div> <html:div class="msg msgBlocked">&blockedPlugin.label;</html:div>
<html:div class="msg msgCrashed"><!-- set at runtime --></html:div> <html:div class="msg msgCrashed"><!-- set at runtime --></html:div>

Просмотреть файл

@ -542,6 +542,10 @@ MAKEFILES_profiler="
tools/profiler/Makefile tools/profiler/Makefile
" "
MAKEFILES_snappy="
other-licenses/snappy/Makefile
"
add_makefiles " add_makefiles "
$MAKEFILES_dom $MAKEFILES_dom
$MAKEFILES_editor $MAKEFILES_editor
@ -577,6 +581,7 @@ add_makefiles "
$MAKEFILES_hal $MAKEFILES_hal
$MAKEFILES_psm_public $MAKEFILES_psm_public
$MAKEFILES_profiler $MAKEFILES_profiler
$MAKEFILES_snappy
" "
# #
@ -926,6 +931,7 @@ if [ "$ENABLE_TESTS" ]; then
accessible/tests/mochitest/actions/Makefile accessible/tests/mochitest/actions/Makefile
accessible/tests/mochitest/attributes/Makefile accessible/tests/mochitest/attributes/Makefile
accessible/tests/mochitest/editabletext/Makefile accessible/tests/mochitest/editabletext/Makefile
accessible/tests/mochitest/elm/Makefile
accessible/tests/mochitest/events/Makefile accessible/tests/mochitest/events/Makefile
accessible/tests/mochitest/focus/Makefile accessible/tests/mochitest/focus/Makefile
accessible/tests/mochitest/hyperlink/Makefile accessible/tests/mochitest/hyperlink/Makefile
@ -1144,6 +1150,14 @@ if [ "$MOZ_ANGLE" ]; then
" "
fi fi
if [ "$MOZ_B2G_RIL" ]; then
add_makefiles "
dom/system/b2g/Makefile
dom/telephony/Makefile
ipc/ril/Makefile
"
fi
if [ "$MOZ_CRASHREPORTER" ]; then if [ "$MOZ_CRASHREPORTER" ]; then
add_makefiles " add_makefiles "
toolkit/crashreporter/Makefile toolkit/crashreporter/Makefile

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше