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 удалений

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

После

Ширина:  |  Высота:  |  Размер: 730 B

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

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

После

Ширина:  |  Высота:  |  Размер: 730 B

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

После

Ширина:  |  Высота:  |  Размер: 69 KiB

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

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

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

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

После

Ширина:  |  Высота:  |  Размер: 93 B

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

После

Ширина:  |  Высота:  |  Размер: 28 KiB

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

После

Ширина:  |  Высота:  |  Размер: 800 B

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

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

После

Ширина:  |  Высота:  |  Размер: 36 KiB

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

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

После

Ширина:  |  Высота:  |  Размер: 984 B

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

После

Ширина:  |  Высота:  |  Размер: 93 B

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

После

Ширина:  |  Высота:  |  Размер: 14 KiB

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

После

Ширина:  |  Высота:  |  Размер: 729 B

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

После

Ширина:  |  Высота:  |  Размер: 105 KiB

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

После

Ширина:  |  Высота:  |  Размер: 1.8 KiB

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

После

Ширина:  |  Высота:  |  Размер: 100 B

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

После

Ширина:  |  Высота:  |  Размер: 41 KiB

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

После

Ширина:  |  Высота:  |  Размер: 1.0 KiB

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

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

После

Ширина:  |  Высота:  |  Размер: 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

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

После

Ширина:  |  Высота:  |  Размер: 11 KiB

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -11,18 +11,6 @@
<!ENTITY newtab.label "New Tab">
<!ENTITY closetab.label "Close Tab">
<!ENTITY cut.label "Cut">
<!ENTITY copy.label "Copy">
<!ENTITY copyAll.label "Copy All">
<!ENTITY copylink.label "Copy Link Location">
<!ENTITY paste.label "Paste">
<!ENTITY pasteAndGo.label "Paste &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 bookmarksHeader.label "Bookmarks">
<!ENTITY historyHeader.label "History">

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

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

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

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

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

@ -1 +1 @@
NSPR_4_9_BETA4
NSPR_4_9_BETA5

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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