Merge backout.

This commit is contained in:
Ms2ger 2012-07-25 22:39:15 +02:00
commit 9ff2ccdd5f
86 changed files with 2427 additions and 486 deletions

View File

@ -613,3 +613,23 @@ Services.obs.addObserver(function ContentHandler(subject, topic, data) {
});
}, 'content-handler', false);
(function geolocationStatusTracker() {
gGeolocationActiveCount = 0;
Services.obs.addObserver(function(aSubject, aTopic, aData) {
let oldCount = gGeolocationActiveCount;
if (aData == "starting") {
gGeolocationActiveCount += 1;
} else if (aData == "shutdown") {
gGeolocationActiveCount -= 1;
}
// We need to track changes from 1 <-> 0
if (gGeolocationActiveCount + oldCount == 1) {
shell.sendChromeEvent({
type: 'geolocation-status',
active: (gGeolocationActiveCount == 1)
});
}
}, "geolocation-device-events", false);
})();

View File

@ -43,6 +43,7 @@ let SocialUI = {
break;
case "social:profile-changed":
SocialToolbar.updateProfile();
SocialShareButton.updateProfileInfo();
break;
case "nsPref:changed":
SocialSidebar.updateSidebar();
@ -153,7 +154,10 @@ let SocialShareButton = {
// Called once, after window load, when the Social.provider object is initialized
init: function SSB_init() {
this.updateButtonHiddenState();
this.updateProfileInfo();
},
updateProfileInfo: function SSB_updateProfileInfo() {
let profileRow = document.getElementById("editSharePopupHeader");
let profile = Social.provider.profile;
if (profile && profile.portrait && profile.displayName) {

View File

@ -1268,6 +1268,7 @@ window[tabsontop="false"] richlistitem[type~="action"][actiontype="switchtab"][s
#socialUserDisplayName {
-moz-appearance: none;
color: #fff;
border: none;
background-color: transparent;
margin-top: 0;

View File

@ -107,7 +107,7 @@ public class DoCommand {
String ffxProvider = "org.mozilla.ffxcp";
String fenProvider = "org.mozilla.fencp";
private final String prgVersion = "SUTAgentAndroid Version 1.10";
private final String prgVersion = "SUTAgentAndroid Version 1.11";
public enum Command
{
@ -121,6 +121,7 @@ public class DoCommand {
OS ("os"),
ID ("id"),
UPTIME ("uptime"),
UPTIMEMILLIS ("uptimemillis"),
SETTIME ("settime"),
SYSTIME ("systime"),
SCREEN ("screen"),
@ -421,6 +422,8 @@ public class DoCommand {
strReturn += "\n";
strReturn += GetUptime();
strReturn += "\n";
strReturn += GetUptimeMillis();
strReturn += "\n";
strReturn += GetScreenInfo();
strReturn += "\n";
strReturn += GetRotationInfo();
@ -464,6 +467,10 @@ public class DoCommand {
strReturn = GetUptime();
break;
case UPTIMEMILLIS:
strReturn = GetUptimeMillis();
break;
case MEMORY:
strReturn = GetMemoryInfo();
break;
@ -2924,6 +2931,11 @@ private void CancelNotification()
return (sRet);
}
public String GetUptimeMillis()
{
return Long.toString(SystemClock.uptimeMillis());
}
public String NewKillProc(String sProcId, OutputStream out)
{
String sRet = "";
@ -3771,6 +3783,7 @@ private void CancelNotification()
" [os] - os version for device\n" +
" [id] - unique identifier for device\n" +
" [uptime] - uptime for device\n" +
" [uptimemillis] - uptime for device in milliseconds\n" +
" [systime] - current system time\n" +
" [screen] - width, height and bits per pixel for device\n" +
" [memory] - physical, free, available, storage memory\n" +

View File

@ -1,8 +1,5 @@
ifdef __WIN32__
VPSEP = ;
else
VPSEP = :
endif
# On Windows, MSYS make takes Unix paths but Pymake takes Windows paths
VPSEP := $(if $(and $(__WIN32__),$(.PYMAKE)),;,:)
$(shell \
mkdir subd1 subd2 subd3; \

View File

@ -19,11 +19,8 @@ INSTALL := cp
XPIDLSRCS = $(srcdir)/check-xpidl.mk
# Avoid permutations induced by 'include {config,kitchen-sink}.mk'
install_cmd ?= $(INSTALL) $(1)
include $(topsrcdir)/config/makefiles/xpidl.mk
$(call requiredfunction,topsrcdir)
$(call requiredfunction,XPIDL_GEN_DIR)

View File

@ -39,8 +39,6 @@ endif #} INCLUDED_XPIDL_MK
###########################################################################
ifdef _xpidl-todo_ #{
$(call requiredfunction,install_cmd)
## Logic batch #1
xpidl-install-src-preqs=\
$(XPIDLSRCS) \

View File

@ -67,6 +67,7 @@ let DOMContactManager = {
receiveMessage: function(aMessage) {
debug("Fallback DOMContactManager::receiveMessage " + aMessage.name);
let mm = aMessage.target.QueryInterface(Ci.nsIFrameMessageManager);
let msg = aMessage.json;
/*
@ -124,29 +125,29 @@ let DOMContactManager = {
}
debug("result:" + JSON.stringify(result));
ppmm.sendAsyncMessage("Contacts:Find:Return:OK", {requestID: msg.requestID, contacts: result});
mm.sendAsyncMessage("Contacts:Find:Return:OK", {requestID: msg.requestID, contacts: result});
}.bind(this),
function(aErrorMsg) { ppmm.sendAsyncMessage("Contacts:Find:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }) }.bind(this),
function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }) }.bind(this),
msg.findOptions);
break;
case "Contact:Save":
this._db.saveContact(
msg.contact,
function() { ppmm.sendAsyncMessage("Contact:Save:Return:OK", { requestID: msg.requestID, contactID: msg.contact.id }); }.bind(this),
function(aErrorMsg) { ppmm.sendAsyncMessage("Contact:Save:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)
function() { mm.sendAsyncMessage("Contact:Save:Return:OK", { requestID: msg.requestID, contactID: msg.contact.id }); }.bind(this),
function(aErrorMsg) { mm.sendAsyncMessage("Contact:Save:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)
);
break;
case "Contact:Remove":
this._db.removeContact(
msg.id,
function() { ppmm.sendAsyncMessage("Contact:Remove:Return:OK", { requestID: msg.requestID, contactID: msg.id }); }.bind(this),
function(aErrorMsg) { ppmm.sendAsyncMessage("Contact:Remove:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)
function() { mm.sendAsyncMessage("Contact:Remove:Return:OK", { requestID: msg.requestID, contactID: msg.id }); }.bind(this),
function(aErrorMsg) { mm.sendAsyncMessage("Contact:Remove:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)
);
break;
case "Contacts:Clear":
this._db.clear(
function() { ppmm.sendAsyncMessage("Contacts:Clear:Return:OK", { requestID: msg.requestID }); }.bind(this),
function(aErrorMsg) { ppmm.sendAsyncMessage("Contacts:Clear:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)
function() { mm.sendAsyncMessage("Contacts:Clear:Return:OK", { requestID: msg.requestID }); }.bind(this),
function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Clear:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)
);
}
}

View File

@ -16,6 +16,7 @@ DIRS = \
MOCHITEST_FILES = \
test_contacts_basics.html \
test_contacts_events.html \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -34,6 +34,12 @@ namespace layers {
// affects CrossProcessCompositorParent below.
static CompositorParent* sCurrentCompositor;
static Thread* sCompositorThread = nsnull;
// When ContentParent::StartUp() is called, we use the Thread global.
// When StartUpWithExistingThread() is used, we have to use the two
// duplicated globals, because there's no API to make a Thread from an
// existing thread.
static PlatformThreadId sCompositorThreadID = 0;
static MessageLoop* sCompositorLoop = nsnull;
struct LayerTreeState {
nsRefPtr<Layer> mRoot;
@ -59,8 +65,19 @@ struct PanZoomUserData : public LayerUserData {
*/
static const LayerTreeState* GetIndirectShadowTree(uint64_t aId);
void
CompositorParent::StartUpWithExistingThread(MessageLoop* aMsgLoop,
PlatformThreadId aThreadID)
{
MOZ_ASSERT(!sCompositorThread);
CreateCompositorMap();
sCompositorLoop = aMsgLoop;
sCompositorThreadID = aThreadID;
}
void CompositorParent::StartUp()
{
MOZ_ASSERT(!sCompositorLoop);
CreateCompositorMap();
CreateThread();
}
@ -74,7 +91,7 @@ void CompositorParent::ShutDown()
bool CompositorParent::CreateThread()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
if (sCompositorThread) {
if (sCompositorThread || sCompositorLoop) {
return true;
}
sCompositorThread = new Thread("Compositor");
@ -93,11 +110,13 @@ void CompositorParent::DestroyThread()
delete sCompositorThread;
sCompositorThread = nsnull;
}
sCompositorLoop = nsnull;
sCompositorThreadID = 0;
}
MessageLoop* CompositorParent::CompositorLoop()
{
return sCompositorThread ? sCompositorThread->message_loop() : nsnull;
return sCompositorThread ? sCompositorThread->message_loop() : sCompositorLoop;
}
CompositorParent::CompositorParent(nsIWidget* aWidget,
@ -115,7 +134,7 @@ CompositorParent::CompositorParent(nsIWidget* aWidget,
, mPauseCompositionMonitor("PauseCompositionMonitor")
, mResumeCompositionMonitor("ResumeCompositionMonitor")
{
NS_ABORT_IF_FALSE(sCompositorThread != nsnull,
NS_ABORT_IF_FALSE(sCompositorThread != nsnull || sCompositorThreadID,
"The compositor thread must be Initialized before instanciating a COmpositorParent.");
MOZ_COUNT_CTOR(CompositorParent);
mCompositorID = 0;
@ -131,7 +150,7 @@ CompositorParent::CompositorParent(nsIWidget* aWidget,
PlatformThreadId
CompositorParent::CompositorThreadID()
{
return sCompositorThread->thread_id();
return sCompositorThread ? sCompositorThread->thread_id() : sCompositorThreadID;
}
CompositorParent::~CompositorParent()

View File

@ -138,6 +138,15 @@ public:
static PCompositorParent*
Create(Transport* aTransport, ProcessId aOtherProcess);
/**
* Setup external message loop and thread ID for Compositor.
* Should be used when CompositorParent should work in existing thread/MessageLoop,
* for example moving Compositor into native toolkit main thread will allow to avoid
* extra synchronization and call ::Composite() right from toolkit::Paint event
*/
static void StartUpWithExistingThread(MessageLoop* aMsgLoop,
PlatformThreadId aThreadID);
protected:
virtual PLayersParent* AllocPLayers(const LayersBackend& aBackendHint,
const uint64_t& aId,

View File

@ -344,7 +344,18 @@ ShadowContainerLayerOGL::ShadowContainerLayerOGL(LayerManagerOGL *aManager)
ShadowContainerLayerOGL::~ShadowContainerLayerOGL()
{
Destroy();
// We don't Destroy() on destruction here because this destructor
// can be called after remote content has crashed, and it may not be
// safe to free the IPC resources of our children. Those resources
// are automatically cleaned up by IPDL-generated code.
//
// In the common case of normal shutdown, either
// LayerManagerOGL::Destroy(), a parent
// *ContainerLayerOGL::Destroy(), or Disconnect() will trigger
// cleanup of our resources.
while (mFirstChild) {
ContainerRemoveChild(this, mFirstChild);
}
}
void

View File

@ -15,9 +15,6 @@ namespace js {
class TempAllocPolicy;
/* Integral types for all hash functions. */
typedef uint32_t HashNumber;
/*****************************************************************************/
namespace detail {
@ -288,7 +285,6 @@ class HashTable : private AllocPolicy
static const uint8_t sMinAlphaFrac = 64; /* (0x100 * .25) taken from jsdhash.h */
static const uint8_t sMaxAlphaFrac = 192; /* (0x100 * .75) taken from jsdhash.h */
static const uint8_t sInvMaxAlpha = 171; /* (ceil(0x100 / .75) >> 1) */
static const HashNumber sGoldenRatio = 0x9E3779B9U; /* taken from jsdhash.h */
static const HashNumber sFreeKey = Entry::sFreeKey;
static const HashNumber sRemovedKey = Entry::sRemovedKey;
static const HashNumber sCollisionBit = Entry::sCollisionBit;
@ -308,10 +304,7 @@ class HashTable : private AllocPolicy
static HashNumber prepareHash(const Lookup& l)
{
HashNumber keyHash = HashPolicy::hash(l);
/* Improve keyHash distribution. */
keyHash *= sGoldenRatio;
HashNumber keyHash = ScrambleHashCode(HashPolicy::hash(l));
/* Avoid reserved hash codes. */
if (!isLiveHash(keyHash))

View File

@ -355,6 +355,26 @@ JS_FLOOR_LOG2W(size_t n)
return js_FloorLog2wImpl(n);
}
/*
* JS_ROTATE_LEFT32
*
* There is no rotate operation in the C Language so the construct (a << 4) |
* (a >> 28) is used instead. Most compilers convert this to a rotate
* instruction but some versions of MSVC don't without a little help. To get
* MSVC to generate a rotate instruction, we have to use the _rotl intrinsic
* and use a pragma to make _rotl inline.
*
* MSVC in VS2005 will do an inline rotate instruction on the above construct.
*/
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || \
defined(_M_X64))
#include <stdlib.h>
#pragma intrinsic(_rotl)
#define JS_ROTATE_LEFT32(a, bits) _rotl(a, bits)
#else
#define JS_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits))))
#endif
JS_END_EXTERN_C
#ifdef __cplusplus
@ -902,6 +922,51 @@ RoundUpPow2(size_t x)
return size_t(1) << JS_CEILING_LOG2W(x);
}
/* Integral types for all hash functions. */
typedef uint32_t HashNumber;
const unsigned HashNumberSizeBits = 32;
namespace detail {
/*
* Given a raw hash code, h, return a number that can be used to select a hash
* bucket.
*
* This function aims to produce as uniform an output distribution as possible,
* especially in the most significant (leftmost) bits, even though the input
* distribution may be highly nonrandom, given the constraints that this must
* be deterministic and quick to compute.
*
* Since the leftmost bits of the result are best, the hash bucket index is
* computed by doing ScrambleHashCode(h) / (2^32/N) or the equivalent
* right-shift, not ScrambleHashCode(h) % N or the equivalent bit-mask.
*
* FIXME: OrderedHashTable uses a bit-mask; see bug 775896.
*/
inline HashNumber
ScrambleHashCode(HashNumber h)
{
/*
* Simply returning h would not cause any hash tables to produce wrong
* answers. But it can produce pathologically bad performance: The caller
* right-shifts the result, keeping only the highest bits. The high bits of
* hash codes are very often completely entropy-free. (So are the lowest
* bits.)
*
* So we use Fibonacci hashing, as described in Knuth, The Art of Computer
* Programming, 6.4. This mixes all the bits of the input hash code h.
*
* The value of goldenRatio is taken from the hex
* expansion of the golden ratio, which starts 1.9E3779B9....
* This value is especially good if values with consecutive hash codes
* are stored in a hash table; see Knuth for details.
*/
static const HashNumber goldenRatio = 0x9E3779B9U;
return h * goldenRatio;
}
} /* namespace detail */
} /* namespace js */
namespace JS {

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@
#include "jscntxt.h"
#include "jsobj.h"
#include "js/HashTable.h"
#include "mozilla/FloatingPoint.h"
namespace js {
@ -32,6 +32,8 @@ class HashableValue {
typedef HashableValue Lookup;
static HashNumber hash(const Lookup &v) { return v.hash(); }
static bool match(const HashableValue &k, const Lookup &l) { return k.equals(l); }
static bool isEmpty(const HashableValue &v) { return v.value.isMagic(JS_HASH_KEY_EMPTY); }
static void makeEmpty(HashableValue *vp) { vp->value = MagicValue(JS_HASH_KEY_EMPTY); }
};
HashableValue() : value(UndefinedValue()) {}
@ -40,6 +42,7 @@ class HashableValue {
HashNumber hash() const;
bool equals(const HashableValue &other) const;
HashableValue mark(JSTracer *trc) const;
Value get() const { return value.get(); }
class AutoRooter : private AutoGCRooter
{
@ -61,13 +64,20 @@ class HashableValue {
};
};
typedef HashMap<HashableValue,
RelocatableValue,
HashableValue::Hasher,
RuntimeAllocPolicy> ValueMap;
typedef HashSet<HashableValue,
HashableValue::Hasher,
RuntimeAllocPolicy> ValueSet;
template <class Key, class Value, class OrderedHashPolicy, class AllocPolicy>
class OrderedHashMap;
template <class T, class OrderedHashPolicy, class AllocPolicy>
class OrderedHashSet;
typedef OrderedHashMap<HashableValue,
RelocatableValue,
HashableValue::Hasher,
RuntimeAllocPolicy> ValueMap;
typedef OrderedHashSet<HashableValue,
HashableValue::Hasher,
RuntimeAllocPolicy> ValueSet;
class MapObject : public JSObject {
public:
@ -93,6 +103,8 @@ class MapObject : public JSObject {
static JSBool set(JSContext *cx, unsigned argc, Value *vp);
static bool delete_impl(JSContext *cx, CallArgs args);
static JSBool delete_(JSContext *cx, unsigned argc, Value *vp);
static bool iterator_impl(JSContext *cx, CallArgs args);
static JSBool iterator(JSContext *cx, unsigned argc, Value *vp);
};
class SetObject : public JSObject {
@ -117,6 +129,8 @@ class SetObject : public JSObject {
static JSBool add(JSContext *cx, unsigned argc, Value *vp);
static bool delete_impl(JSContext *cx, CallArgs args);
static JSBool delete_(JSContext *cx, unsigned argc, Value *vp);
static bool iterator_impl(JSContext *cx, CallArgs args);
static JSBool iterator(JSContext *cx, unsigned argc, Value *vp);
};
} /* namespace js */

View File

@ -379,7 +379,7 @@ class RelocatableValue : public EncapsulatedValue
public:
explicit inline RelocatableValue();
explicit inline RelocatableValue(const Value &v);
explicit inline RelocatableValue(const RelocatableValue &v);
inline RelocatableValue(const RelocatableValue &v);
inline ~RelocatableValue();
inline RelocatableValue &operator=(const Value &v);

View File

@ -2,14 +2,14 @@ var f1 = function f0(a, b) { return a + b; }
assertEq(f1.toSource(), "(function f0(a, b) { return a + b; })");
assertEq(f1.toString(), "function f0(a, b) { return a + b; }");
assertEq(decompileFunction(f1), f1.toString());
assertEq(decompileBody(f1), "return a + b;");
assertEq(decompileBody(f1), " return a + b; ");
var f2 = function (a, b) { return a + b; };
assertEq(f2.toSource(), "(function (a, b) { return a + b; })");
assertEq(f2.toString(), "function (a, b) { return a + b; }");
assertEq(decompileFunction(f2), f2.toString());
assertEq(decompileBody(f2), "return a + b;");
assertEq(decompileBody(f2), " return a + b; ");
var f3 = (function (a, b) { return a + b; });
assertEq(f3.toSource(), "(function (a, b) { return a + b; })");
assertEq(f3.toString(), "function (a, b) { return a + b; }");
assertEq(decompileFunction(f3), f3.toString());
assertEq(decompileBody(f3), "return a + b;");
assertEq(decompileBody(f3), " return a + b; ");

View File

@ -8,3 +8,6 @@ function f2(a, /* ))))pernicious comment */ b,
d) {}
assertEq(f2.toString(), "function f2(a, /* ))))pernicious comment */ b,\n c, // another comment((\n d) {}");
assertEq(decompileBody(f2), "");
function f3() { }
assertEq(f3.toString(), "function f3() { }");
assertEq(decompileBody(f3), " ");

View File

@ -0,0 +1,12 @@
// Maps do not keep deleted keys or values alive.
load(libdir + "referencesVia.js");
var m = new Map;
var k = {}, v = {};
m.set(k, v);
m.delete(k);
if (typeof findReferences == 'function') {
assertEq(referencesVia(m, 'key', k), false);
assertEq(referencesVia(m, 'value', v), false);
}

View File

@ -0,0 +1,15 @@
// Bug 770954.
gczeal(4);
var s = new Set;
s.add(-0);
s.add(0);
assertEq(s.delete(-0), true);
assertEq(s.has(0), (true ));
assertEq(s.has(-0), false);
var m = new Map;
m.set(-0, 'x');
assertEq(m.has(0), false);
assertEq(m.get(0), undefined);
assertEq(m.has(-0), true);
assertEq(m.delete(-0), true);

View File

@ -0,0 +1,11 @@
// for-of can be used to iterate over a Map twice.
var map = Map([['a', 0], ['b', 1], ['c', 2]]);
var log = '';
for (let i = 0; i < 2; i++) {
for (let [k, v] of map)
log += k + v;
log += ';'
}
assertEq(log, 'a0b1c2;a0b1c2;');

View File

@ -0,0 +1,11 @@
// Nested for-of loops can iterate over a Map.
var map = Map([['a', 0], ['b', 1]]);
var log = '';
for (let [k0, v0] of map) {
log += k0 + v0 + ':'
for (let [k1, v1] of map)
log += k1 + v1;
log += ';'
};
assertEq(log, 'a0:a0b1;b1:a0b1;');

View File

@ -0,0 +1,15 @@
// map.iterator() is live: entries added during iteration are visited.
var map = Map();
function force(k) {
if (!map.has(k) && k >= 0)
map.set(k, k - 1);
}
force(5);
var log = '';
for (let [k, v] of map) {
log += k + ';';
force(v);
}
assertEq(log, '5;4;3;2;1;0;');
assertEq(map.size(), 6);

View File

@ -0,0 +1,11 @@
// A Map iterator does not iterate over new entries added after it throws StopIteration.
load(libdir + "asserts.js");
load(libdir + "eqArrayHelper.js");
var map = Map();
var iter0 = map.iterator(), iter1 = map.iterator();
assertThrowsValue(function () { iter0.next(); }, StopIteration); // closes iter0
map.set(1, 2);
assertThrowsValue(function () { iter0.next(); }, StopIteration); // already closed
assertEqArray(iter1.next(), [1, 2]); // was not yet closed

View File

@ -0,0 +1,14 @@
// Removing and re-adding entries while an iterator is live causes the iterator to visit them again.
var map = Map([['a', 1]]);
var n = 5;
for (let [k, v] of map) {
assertEq(k, 'a');
assertEq(v, 1);
if (n === 0)
break;
map.delete('a');
map.set('a', 1);
n--;
}
assertEq(n, 0);

View File

@ -0,0 +1,15 @@
// Map iterators produces entries in the order they were inserted.
load(libdir + "eqArrayHelper.js");
var map = Map();
for (var i = 7; i !== 1; i = i * 7 % 1117)
map.set("" + i, i);
assertEq(map.size(), 557);
i = 7;
for (var pair of map) {
assertEqArray(pair, ["" + i, i]);
i = i * 7 % 1117;
}
assertEq(i, 1);

View File

@ -0,0 +1,15 @@
// mapiter.next() returns an actual array.
var key = {};
var map = Map([[key, 'value']]);
var entry = map.iterator().next();
assertEq(Array.isArray(entry), true);
assertEq(Object.getPrototypeOf(entry), Array.prototype);
assertEq(Object.isExtensible(entry), true);
assertEq(entry.length, 2);
var names = Object.getOwnPropertyNames(entry).sort();
assertEq(names.length, 3);
assertEq(names.join(","), "0,1,length");
assertEq(entry[0], key);
assertEq(entry[1], 'value');

View File

@ -0,0 +1,10 @@
// mapiter.next() returns a fresh array each time.
var map = Map([['a', 1], ['b', 2]]);
var iter = map.iterator();
var a = iter.next(), b = iter.next();
assertEq(a !== b, true);
assertEq(a[0], 'a');
assertEq(b[0], 'b');
var a1 = map.iterator().next();
assertEq(a !== a1, true);

View File

@ -0,0 +1,12 @@
// Modifying an array returned by mapiter.next() does not modify the Map.
var map = Map([['a', 1]]);
var pair = map.iterator().next();
assertEq(pair[0], 'a');
pair[0] = 'b';
pair[1] = 2;
assertEq(pair[0], 'b');
assertEq(pair[1], 2);
assertEq(map.get('a'), 1);
assertEq(map.has('b'), false);
assertEq(map.size(), 1);

View File

@ -0,0 +1,8 @@
// for-of works on a cross-compartment wrapper of a Map.
var g = newGlobal('new-compartment');
var mw = g.eval("Map([['a', 1], ['b', 2]])");
var log = '';
for (let [k, v] of mw)
log += k + v;
assertEq(log, "a1b2");

View File

@ -0,0 +1,19 @@
// map.iterator() and iter.next() are non-generic but work on cross-compartment wrappers.
load(libdir + "asserts.js");
load(libdir + "eqArrayHelper.js");
var g = newGlobal('new-compartment');
var iterator_fn = Map.prototype.iterator;
assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError);
assertThrowsInstanceOf(function () { iterator_fn.call(Set()); }, TypeError);
var mapw = g.eval("Map([['x', 1], ['y', 2]])");
assertEqArray(iterator_fn.call(mapw).next(), ["x", 1]);
var next_fn = Map().iterator().next;
assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError);
assertThrowsInstanceOf(function () { next_fn.call(Set().iterator()); }, TypeError);
var iterw = mapw.iterator();
assertEqArray(next_fn.call(iterw), ["x", 1]);
assertEqArray(next_fn.call(iterw), ["y", 2]);
assertThrowsValue(function () { next_fn.call(iterw); }, g.StopIteration);

View File

@ -0,0 +1,40 @@
// A map iterator can cope with removing the current entry.
function test(pairs) {
print(uneval(pairs));
var map = Map(pairs);
var all_keys = '';
var false_keys = '';
for (let [k, v] of map) {
all_keys += k;
if (!v)
false_keys += k;
}
var log = '';
for (let [k, remove] of map) {
log += k;
if (remove)
map.delete(k);
}
assertEq(log, all_keys);
var remaining_keys = [k for ([k] of map)].join('');
assertEq(remaining_keys, false_keys);
}
// removing the only entry
test([['a', true]]);
// removing the first entry
test([['a', true], ['b', false], ['c', false]]);
// removing a middle entry
test([['a', false], ['b', true], ['c', false]]);
// removing the last entry
test([['a', false], ['b', false], ['c', true]]);
// removing all entries
test([['a', true], ['b', true], ['c', true]]);

View File

@ -0,0 +1,11 @@
// A map iterator can cope with removing the next entry.
var map = Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
var iter = map.iterator();
var log = '';
for (let [k, v] of iter) {
log += k + v;
if (k === 'b')
map.delete('c');
}
assertEq(log, 'a0b1d3');

View File

@ -0,0 +1,12 @@
// A map iterator can cope with removing the next entry, then the current entry.
load(libdir + "asserts.js");
var map = Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
var iter = map.iterator();
assertEq(iter.next()[0], 'a');
assertEq(iter.next()[0], 'b');
map.delete('c');
map.delete('b');
assertEq(iter.next()[0], 'd');
assertThrowsValue(function () { iter.next(); }, StopIteration);

View File

@ -0,0 +1,32 @@
// Multiple live iterators on the same Map can cope with removing entries.
load(libdir + "eqArrayHelper.js");
load(libdir + "asserts.js");
// Make a map.
var map = Map();
var SIZE = 7;
for (var j = 0; j < SIZE; j++)
map.set(j, j);
// Make lots of iterators pointing to entry 2 of the map.
var NITERS = 5;
var iters = [];
for (var i = 0; i < NITERS; i++) {
var iter = map.iterator();
assertEqArray(iter.next(), [0, 0]);
assertEqArray(iter.next(), [1, 1]);
iters[i] = iter;
}
// Remove half of the map entries.
for (var j = 0; j < SIZE; j += 2)
map.delete(j);
// Make sure all the iterators still work.
for (var i = 0; i < NITERS; i++) {
var iter = iters[i];
for (var j = 3; j < SIZE; j += 2)
assertEqArray(iter.next(), [j, j]);
assertThrowsValue(function () { iter.next(); }, StopIteration);
}

View File

@ -0,0 +1,22 @@
// Removing a Map entry already visited by an iterator does not cause any
// entries to be skipped.
var map = Map();
for (var i = 0; i < 20; i++)
map.set(String.fromCharCode('A'.charCodeAt(0) + i), i);
var log = '';
for (var [k, v] of map) {
log += k;
if (v % 5 === 4) {
// Delete all entries preceding this one.
for (let [k1, v1] of map) {
if (k1 === k)
break;
map.delete(k1);
}
}
}
assertEq(log, 'ABCDEFGHIJKLMNOPQRST');
assertEq(map.size(), 1); // Only the last entry remains.
assertEq(map.get('T'), 19);

View File

@ -0,0 +1,20 @@
// Removing many Map entries does not cause a live iterator to skip any of the
// entries that were not removed. (Compacting a Map must not be observable to
// script.)
load(libdir + "asserts.js");
var map = Map();
for (var i = 0; i < 32; i++)
map.set(i, i);
var iter = map.iterator();
assertEq(iter.next()[0], 0);
for (var i = 0; i < 30; i++)
map.delete(i);
assertEq(map.size(), 2);
for (var i = 32; i < 100; i++)
map.set(i, i); // eventually triggers compaction
for (var i = 30; i < 100; i++)
assertEq(iter.next()[0], i);
assertThrowsValue(function () { iter.next(); }, StopIteration);

View File

@ -0,0 +1,11 @@
// for-of can be used to iterate over a Set twice.
var set = Set(['a', 'b', 'c']);
var log = '';
for (let i = 0; i < 2; i++) {
for (let x of set)
log += x;
log += ';'
}
assertEq(log, 'abc;abc;');

View File

@ -0,0 +1,11 @@
// Nested for-of loops can iterate over a Set.
var map = Set(['a', 'b']);
var log = '';
for (let x of map) {
log += x + ':'
for (let y of map)
log += y;
log += ';'
};
assertEq(log, 'a:ab;b:ab;');

View File

@ -0,0 +1,11 @@
// Iterating over a set of objects yields those exact objects.
var arr = [{}, {}, {}, [], /xyz/, new Date];
var set = Set(arr);
assertEq(set.size(), arr.length);
var i = 0;
for (var x of set)
assertEq(x, arr[i++]);
assertEq(i, arr.length);

View File

@ -0,0 +1,11 @@
// set.iterator() is live: entries added during iteration are visited.
var set = Set([5]);
var log = '';
for (let x of set) {
log += x + ';';
if (x > 0)
set.add(x - 1);
}
assertEq(log, '5;4;3;2;1;0;');
assertEq(set.size(), 6);

View File

@ -0,0 +1,10 @@
// A Set iterator does not iterate over new entries added after it throws StopIteration.
load(libdir + "asserts.js");
var set = Set();
var iter0 = set.iterator(), iter1 = set.iterator();
assertThrowsValue(function () { iter0.next(); }, StopIteration); // closes iter0
set.add("x");
assertThrowsValue(function () { iter0.next(); }, StopIteration); // already closed
assertEq(iter1.next(), "x"); // was not yet closed

View File

@ -0,0 +1,13 @@
// Removing and re-adding entries while an iterator is live causes the iterator to visit them again.
var set = Set(['a']);
var n = 5;
for (let v of set) {
assertEq(v, 'a');
if (n === 0)
break;
set.delete('a');
set.add('a');
n--;
}
assertEq(n, 0);

View File

@ -0,0 +1,8 @@
// A Set iterator keeps the data alive.
load(libdir + "referencesVia.js");
var key = {};
var set = Set([key]);
var iter = set.iterator();
referencesVia(iter, "**UNKNOWN SLOT 0**", set);
referencesVia(set, "key", key);

View File

@ -0,0 +1,8 @@
// GC-ing during a for-of loop doesn't crash.
var i = 0;
for (var x of Set(Object.getOwnPropertyNames(this))) {
gc();
if (++i >= 20)
break;
}

View File

@ -0,0 +1,20 @@
// GC in nested for-loops is safe.
var x;
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
for (x of Set([1]))
gc();

View File

@ -0,0 +1,14 @@
// Set iterators produces entries in the order they were inserted.
var set = Set();
var i;
for (i = 7; i !== 1; i = i * 7 % 1117)
set.add(i);
assertEq(set.size(), 557);
i = 7;
for (var v of set) {
assertEq(v, i);
i = i * 7 % 1117;
}
assertEq(i, 1);

View File

@ -0,0 +1,8 @@
// for-of works on a cross-compartment wrapper of a Set.
var g = newGlobal('new-compartment');
var mw = g.eval("Set(['a', 'b', 1, 2])");
var log = '';
for (let x of mw)
log += x;
assertEq(log, "ab12");

View File

@ -0,0 +1,18 @@
// map.iterator() and iter.next() are non-generic but work on cross-compartment wrappers.
load(libdir + "asserts.js");
var g = newGlobal('new-compartment');
var iterator_fn = Set.prototype.iterator;
assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError);
assertThrowsInstanceOf(function () { iterator_fn.call(Map()); }, TypeError);
var setw = g.eval("Set(['x', 'y'])");
assertEq(iterator_fn.call(setw).next(), "x");
var next_fn = Set().iterator().next;
assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError);
assertThrowsInstanceOf(function () { next_fn.call(Map().iterator()); }, TypeError);
var iterw = setw.iterator();
assertEq(next_fn.call(iterw), "x");
assertEq(next_fn.call(iterw), "y");
assertThrowsValue(function () { next_fn.call(iterw); }, g.StopIteration);

View File

@ -0,0 +1,26 @@
// A set iterator can cope with removing the current entry.
function test(letters, toRemove) {
var set = Set(letters);
toRemove = Set(toRemove);
var leftovers = [x for (x of set) if (!toRemove.has(x))].join("");
var log = "";
for (let x of set) {
log += x;
if (toRemove.has(x))
set.delete(x);
}
assertEq(log, letters);
var remaining = [x for (x of set)].join("");
assertEq(remaining, leftovers);
}
test('a', 'a'); // removing the only entry
test('abc', 'a'); // removing the first entry
test('abc', 'b'); // removing a middle entry
test('abc', 'c'); // removing the last entry
test('abc', 'abc') // removing all entries

View File

@ -0,0 +1,11 @@
// A map iterator can cope with removing the next entry.
var set = Set("abcd");
var iter = set.iterator();
var log = "";
for (let x of iter) {
log += x;
if (x === "b")
set.delete("c");
}
assertEq(log, "abd");

View File

@ -0,0 +1,12 @@
// A set iterator can cope with removing the next entry, then the current entry.
load(libdir + "asserts.js");
var set = Set("abcd");
var iter = set.iterator();
assertEq(iter.next(), "a");
assertEq(iter.next(), "b");
set.delete("c");
set.delete("b");
assertEq(iter.next(), "d");
assertThrowsValue(function () { iter.next(); }, StopIteration);

View File

@ -0,0 +1,31 @@
// Multiple live iterators on the same Set can cope with removing entries.
load(libdir + "asserts.js");
// Make a set.
var set = Set();
var SIZE = 7;
for (var j = 0; j < SIZE; j++)
set.add(j);
// Make lots of iterators pointing to entry 2 of the set.
var NITERS = 5;
var iters = [];
for (var i = 0; i < NITERS; i++) {
var iter = set.iterator();
assertEq(iter.next(), 0);
assertEq(iter.next(), 1);
iters[i] = iter;
}
// Remove half of the set entries.
for (var j = 0; j < SIZE; j += 2)
set.delete(j);
// Make sure all the iterators still work.
for (var i = 0; i < NITERS; i++) {
var iter = iters[i];
for (var j = 3; j < SIZE; j += 2)
assertEq(iter.next(), j);
assertThrowsValue(function () { iter.next(); }, StopIteration);
}

View File

@ -0,0 +1,22 @@
// Removing a Set entry already visited by an iterator does not cause any
// entries to be skipped.
var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var set = Set(str);
var log = '';
var i = 0;
for (var x of set) {
log += x;
if (i++ % 5 === 0) {
// Delete all entries preceding this one.
for (let y of set) {
if (y === x)
break;
set.delete(y);
}
}
}
assertEq(log, str);
assertEq(set.size(), 1); // Elements 0 to 24 are removed, leaving only 25 (Z).
assertEq(set.has('Z'), true);

View File

@ -0,0 +1,20 @@
// Removing many Set entries does not cause a live iterator to skip any of the
// entries that were not removed. (Compacting a Set must not be observable to
// script.)
load(libdir + "asserts.js");
var set = Set();
for (var i = 0; i < 32; i++)
set.add(i);
var iter = set.iterator();
assertEq(iter.next(), 0);
for (var i = 0; i < 30; i++)
set.delete(i);
assertEq(set.size(), 2);
for (var i = 32; i < 100; i++)
set.add(i); // eventually triggers compaction
for (var i = 30; i < 100; i++)
assertEq(iter.next(), i);
assertThrowsValue(function () { iter.next(); }, StopIteration);

View File

@ -0,0 +1,12 @@
// collection.iterator() returns an Iterator object.
function test(obj) {
var iter = obj.iterator();
assertEq(typeof iter, "object");
assertEq(iter instanceof Iterator, true);
assertEq(iter.toString(), "[object Iterator]");
}
test([]);
test(new Map);
test(new Set);

View File

@ -0,0 +1,12 @@
// for-of on an empty collection does not execute the loop body or modify the loop variable.
function test(empty) {
var x = 'unchanged';
for (x of empty)
throw fit;
assertEq(x, 'unchanged');
}
test([]);
test(new Map);
test(new Set);

View File

@ -0,0 +1,14 @@
// An iterator keeps its data alive.
load(libdir + "referencesVia.js");
var key = {};
function test(obj, edgeName) {
var iter = obj.iterator();
referencesVia(iter, "**UNKNOWN SLOT 0**", obj);
referencesVia(obj, edgeName, key);
}
test([key], "element[0]");
test(Map([[key, 'value']]), "key");
test(Set([key]), "key");

View File

@ -0,0 +1,13 @@
// All iterators of the same collection type share their immediate prototype.
// Those prototype objects in turn inherit directly from Iterator.prototype.
function test(obj0, obj1) {
var iter0 = obj0.iterator(), iter1 = obj1.iterator();
var proto = Object.getPrototypeOf(iter0);
assertEq(Object.getPrototypeOf(iter1), proto);
assertEq(Object.getPrototypeOf(proto), Iterator.prototype);
}
test([], [1]);
test(Map(), Map([[1, 1]]));
test(Set(), Set([1]));

View File

@ -0,0 +1,11 @@
// Iterators of different collection types have different prototypes.
var aproto = Object.getPrototypeOf(Array().iterator());
var mproto = Object.getPrototypeOf(Map().iterator());
var sproto = Object.getPrototypeOf(Set().iterator());
assertEq(aproto !== mproto, true);
assertEq(aproto !== sproto, true);
assertEq(mproto !== sproto, true);
assertEq(aproto.next !== mproto.next, true);
assertEq(aproto.next !== sproto.next, true);
assertEq(mproto.next !== sproto.next, true);

View File

@ -0,0 +1,22 @@
// Iterator prototype surfaces.
load(libdir + "asserts.js");
function test(constructor) {
var proto = Object.getPrototypeOf(constructor().iterator());
var names = Object.getOwnPropertyNames(proto);
assertEq(names.length, 1);
assertEq(names[0], 'next');
var desc = Object.getOwnPropertyDescriptor(proto, 'next');
assertEq(desc.configurable, true);
assertEq(desc.enumerable, false);
assertEq(desc.writable, true);
assertEq(proto.iterator(), proto);
assertThrowsValue(function () { proto.next(); }, StopIteration);
}
//test(Array);
test(Map);
test(Set);

View File

@ -4074,7 +4074,7 @@ struct JSClass {
* with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
* prevously allowed, but is now an ES5 violation and thus unsupported.
*/
#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 21)
#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 23)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \

View File

@ -552,23 +552,25 @@ FindBody(JSContext *cx, HandleFunction fun, const jschar *chars, size_t length,
break;
}
} while (onward);
DebugOnly<bool> braced = ts.matchToken(TOK_LC);
if (ts.getToken() == TOK_ERROR)
TokenKind tt = ts.getToken();
if (tt == TOK_ERROR)
return false;
bool braced = tt == TOK_LC;
JS_ASSERT(!!(fun->flags & JSFUN_EXPR_CLOSURE) ^ braced);
*bodyStart = ts.offsetOfToken(ts.currentToken());
RangedPtr<const jschar> p(chars, length);
p = chars + length;
for (; unicode::IsSpaceOrBOM2(p[-1]); p--)
;
if (p[-1] == '}') {
p--;
for (; unicode::IsSpaceOrBOM2(p[-1]); p--)
;
if (braced)
*bodyStart += 1;
RangedPtr<const jschar> end(chars, length);
end = chars + length;
if (end[-1] == '}') {
end--;
} else {
JS_ASSERT(!braced);
for (; unicode::IsSpaceOrBOM2(end[-1]); end--)
;
}
*bodyEnd = p.get() - chars;
*bodyEnd = end.get() - chars;
JS_ASSERT(*bodyStart <= *bodyEnd);
return true;
}
@ -672,7 +674,7 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb
if (!out.append("/* use strict */ "))
return NULL;
} else {
if (!out.append("\"use strict\";\n"))
if (!out.append("\n\"use strict\";\n"))
return NULL;
}
}

View File

@ -210,6 +210,7 @@ extern Class ErrorClass;
extern Class ElementIteratorClass;
extern Class GeneratorClass;
extern Class JSONClass;
extern Class MapIteratorClass;
extern Class MathClass;
extern Class NumberClass;
extern Class NormalArgumentsObjectClass;
@ -217,6 +218,7 @@ extern Class ObjectClass;
extern Class ProxyClass;
extern Class RegExpClass;
extern Class RegExpStaticsClass;
extern Class SetIteratorClass;
extern Class SlowArrayClass;
extern Class StopIterationClass;
extern Class StringClass;
@ -235,12 +237,16 @@ class DebugScopeObject;
class DeclEnvObject;
class ElementIteratorObject;
class GlobalObject;
class MapObject;
class MapIteratorObject;
class NestedScopeObject;
class NewObjectCache;
class NormalArgumentsObject;
class NumberObject;
class PropertyIteratorObject;
class ScopeObject;
class SetObject;
class SetIteratorObject;
class StaticBlockObject;
class StrictArgumentsObject;
class StringObject;
@ -557,7 +563,6 @@ struct JSObject : public js::ObjectImpl
bool growElements(JSContext *cx, unsigned cap);
void shrinkElements(JSContext *cx, unsigned cap);
inline js::ElementIteratorObject *asElementIterator();
/*
* Array-specific getters and setters (for both dense and slow arrays).
@ -909,6 +914,7 @@ struct JSObject : public js::ObjectImpl
inline bool isFunction() const;
inline bool isGenerator() const;
inline bool isGlobal() const;
inline bool isMapIterator() const;
inline bool isObject() const;
inline bool isPrimitive() const;
inline bool isPropertyIterator() const;
@ -917,6 +923,7 @@ struct JSObject : public js::ObjectImpl
inline bool isRegExpStatics() const;
inline bool isScope() const;
inline bool isScript() const;
inline bool isSetIterator() const;
inline bool isStopIteration() const;
inline bool isTypedArray() const;
inline bool isWeakMap() const;
@ -962,12 +969,16 @@ struct JSObject : public js::ObjectImpl
inline js::DeclEnvObject &asDeclEnv();
inline js::DebugScopeObject &asDebugScope();
inline js::GlobalObject &asGlobal();
inline js::MapObject &asMap();
inline js::MapIteratorObject &asMapIterator();
inline js::NestedScopeObject &asNestedScope();
inline js::NormalArgumentsObject &asNormalArguments();
inline js::NumberObject &asNumber();
inline js::PropertyIteratorObject &asPropertyIterator();
inline js::RegExpObject &asRegExp();
inline js::ScopeObject &asScope();
inline js::SetObject &asSet();
inline js::SetIteratorObject &asSetIterator();
inline js::StrictArgumentsObject &asStrictArguments();
inline js::StaticBlockObject &asStaticBlock();
inline js::StringObject &asString();

View File

@ -28,6 +28,7 @@
#include "jsxml.h"
#include "jswrapper.h"
#include "builtin/MapObject.h"
#include "gc/Barrier.h"
#include "gc/Marking.h"
#include "gc/Root.h"
@ -787,6 +788,7 @@ inline bool JSObject::isError() const { return hasClass(&js::ErrorClass); }
inline bool JSObject::isFunction() const { return hasClass(&js::FunctionClass); }
inline bool JSObject::isFunctionProxy() const { return hasClass(&js::FunctionProxyClass); }
inline bool JSObject::isGenerator() const { return hasClass(&js::GeneratorClass); }
inline bool JSObject::isMapIterator() const { return hasClass(&js::MapIteratorClass); }
inline bool JSObject::isNestedScope() const { return isBlock() || isWith(); }
inline bool JSObject::isNormalArguments() const { return hasClass(&js::NormalArgumentsObjectClass); }
inline bool JSObject::isNumber() const { return hasClass(&js::NumberClass); }
@ -795,6 +797,7 @@ inline bool JSObject::isPrimitive() const { return isNumber() || isString() || i
inline bool JSObject::isRegExp() const { return hasClass(&js::RegExpClass); }
inline bool JSObject::isRegExpStatics() const { return hasClass(&js::RegExpStaticsClass); }
inline bool JSObject::isScope() const { return isCall() || isDeclEnv() || isNestedScope(); }
inline bool JSObject::isSetIterator() const { return hasClass(&js::SetIteratorClass); }
inline bool JSObject::isStaticBlock() const { return isBlock() && !getProto(); }
inline bool JSObject::isStopIteration() const { return hasClass(&js::StopIterationClass); }
inline bool JSObject::isStrictArguments() const { return hasClass(&js::StrictArgumentsObjectClass); }

View File

@ -353,26 +353,6 @@ bool DecompressString(const unsigned char *inp, size_t inplen,
} /* namespace js */
#endif /* __cplusplus */
/*
* JS_ROTATE_LEFT32
*
* There is no rotate operation in the C Language so the construct (a << 4) |
* (a >> 28) is used instead. Most compilers convert this to a rotate
* instruction but some versions of MSVC don't without a little help. To get
* MSVC to generate a rotate instruction, we have to use the _rotl intrinsic
* and use a pragma to make _rotl inline.
*
* MSVC in VS2005 will do an inline rotate instruction on the above construct.
*/
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || \
defined(_M_X64))
#include <stdlib.h>
#pragma intrinsic(_rotl)
#define JS_ROTATE_LEFT32(a, bits) _rotl(a, bits)
#else
#define JS_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits))))
#endif
/* Crash diagnostics */
#ifdef DEBUG
# define JS_CRASH_DIAGNOSTICS 1

View File

@ -218,6 +218,7 @@ typedef enum JSWhyMagic
JS_OVERWRITTEN_CALLEE, /* arguments.callee has been overwritten */
JS_FORWARD_TO_CALL_OBJECT, /* args object element stored in call object */
JS_BLOCK_NEEDS_CLONE, /* value of static block object slot */
JS_HASH_KEY_EMPTY, /* see class js::HashableValue */
JS_GENERIC_MAGIC /* for local use */
} JSWhyMagic;

View File

@ -11,6 +11,7 @@ from subprocess import list2cmdline, call
from lib.results import NullTestOutput
from lib.tests import TestCase
from lib.results import ResultsSink
from lib.progressbar import ProgressBar
if (sys.platform.startswith('linux') or
sys.platform.startswith('darwin')
@ -147,8 +148,11 @@ def parse_args():
raise SystemExit("Failed to open output file: " + str(ex))
# Hide the progress bar if it will get in the way of other output.
options.hide_progress = ((options.show_cmd or options.show_output) and
options.output_fp == sys.stdout or options.tinderbox)
options.hide_progress = (((options.show_cmd or options.show_output) and
options.output_fp == sys.stdout) or
options.tinderbox or
ProgressBar.conservative_isatty() or
options.hide_progress)
return (options, requested_paths, excluded_paths)

View File

@ -1,36 +1,93 @@
# Text progress bar library, like curl or scp.
import sys, datetime
import sys
from datetime import datetime
class NullProgressBar:
def update(self, current, data): pass
def poke(self): pass
def finish(self, complete=True): pass
def message(self, msg): sys.stdout.write(msg + '\n')
class ProgressBar:
def __init__(self, label, limit, label_width=12):
self.label = label
self.limit = limit
self.label_width = label_width
self.cur = 0
self.t0 = datetime.datetime.now()
GREEN = '\x1b[32;1m'
RED = '\x1b[31;1m'
BLUE = '\x1b[34;1m'
GRAY = '\x1b[37;2m'
self.barlen = 64 - self.label_width
self.fmt = '\r%-' + str(label_width) + 's %3d%% %-' + str(self.barlen) + 's| %6.1fs'
RESET = '\x1b[0m'
def update(self, value):
self.cur = value
pct = int(100.0 * self.cur / self.limit)
barlen = int(1.0 * self.barlen * self.cur / self.limit) - 1
CLEAR_RIGHT = '\x1b[K'
def __init__(self, limit, fmt):
assert not self.conservative_isatty()
assert limit < 9999
self.prior = None
self.counters_fmt = fmt # [{str:str}] Describtion of how to lay out each
# field in the counters map.
self.limit = limit # int: The value of 'current' equal to 100%.
self.t0 = datetime.now() # datetime: The start time.
# Compute the width of the counters and build the format string.
self.counters_width = 1 # [
for layout in self.counters_fmt:
self.counters_width += 4 # Less than 9999 tests total.
self.counters_width += 1 # | (or ']' for the last one)
self.barlen = 64 - self.counters_width
self.fmt = ('\r%-' + str(self.counters_width) + 's %3d%% %-' +
str(self.barlen) + 's| %6.1fs' + self.CLEAR_RIGHT)
def update(self, current, data):
# Record prior for poke.
self.prior = (current, data)
# Build counters string.
counters = '['
for layout in self.counters_fmt:
counters += layout['color'] + ('%4d' % data[layout['value']]) + self.RESET
counters += '|'
counters = counters[:-1] + ']'
# Build the bar.
pct = int(100.0 * current / self.limit)
barlen = int(1.0 * self.barlen * current / self.limit) - 1
bar = '='*barlen + '>'
dt = datetime.datetime.now() - self.t0
# Update the bar.
dt = datetime.now() - self.t0
dt = dt.seconds + dt.microseconds * 1e-6
sys.stdout.write(self.fmt % (self.label[:self.label_width], pct, bar, dt))
sys.stdout.write(self.fmt % (counters, pct, bar, dt))
# Force redisplay, since we didn't write a \n.
sys.stdout.flush()
def poke(self):
if not self.prior:
return
self.update(*self.prior)
def finish(self, complete=True):
final_count = self.limit if complete else self.cur
self.update(final_count)
final_count = self.limit if complete else self.prior[0]
self.update(final_count, self.prior[1])
sys.stdout.write('\n')
if __name__ == '__main__':
pb = ProgressBar('test', 12)
for i in range(12):
pb.update(i)
time.sleep(0.5)
pb.finish()
def message(self, msg):
sys.stdout.write('\n')
sys.stdout.write(msg)
sys.stdout.write('\n')
@staticmethod
def conservative_isatty():
"""
Prefer erring on the side of caution and not using terminal commands if
the current output stream may be a file. We explicitly check for the
Android platform because terminal commands work poorly over ADB's
redirection.
"""
try:
import android
except ImportError:
return False
return sys.stdout.isatty()

View File

@ -1,6 +1,6 @@
import re
from subprocess import list2cmdline
from progressbar import ProgressBar
from progressbar import NullProgressBar, ProgressBar
class TestOutput:
"""Output from a test run."""
@ -83,18 +83,27 @@ class ResultsSink:
self.fp = options.output_fp
self.groups = {}
self.counts = [ 0, 0, 0 ]
self.counts = {'PASS': 0, 'FAIL': 0, 'TIMEOUT': 0, 'SKIP': 0}
self.n = 0
self.pb = None
if not options.hide_progress:
self.pb = ProgressBar('', testcount, 16)
if options.hide_progress:
self.pb = NullProgressBar()
else:
fmt = [
{'value': 'PASS', 'color': ProgressBar.GREEN},
{'value': 'FAIL', 'color': ProgressBar.RED},
{'value': 'TIMEOUT', 'color': ProgressBar.BLUE},
{'value': 'SKIP', 'color': ProgressBar.GRAY},
]
self.pb = ProgressBar(testcount, fmt)
def push(self, output):
if output.timed_out:
self.counts['TIMEOUT'] += 1
if isinstance(output, NullTestOutput):
if self.options.tinderbox:
self.print_tinderbox_result('TEST-KNOWN-FAIL', output.test.path, time=output.dt, skip=True)
self.counts[2] += 1
self.counts['SKIP'] += 1
self.n += 1
else:
if self.options.show_cmd:
@ -115,11 +124,11 @@ class ResultsSink:
self.n += 1
if result.result == TestResult.PASS and not result.test.random:
self.counts[0] += 1
self.counts['PASS'] += 1
elif result.test.expect and not result.test.random:
self.counts[1] += 1
self.counts['FAIL'] += 1
else:
self.counts[2] += 1
self.counts['SKIP'] += 1
if self.options.tinderbox:
if len(result.results) > 1:
@ -134,19 +143,14 @@ class ResultsSink:
return
if dev_label:
if self.pb:
self.fp.write("\n")
def singular(label):
return "FIXED" if label == "FIXES" else label[:-1]
print >> self.fp, "%s - %s" % (singular(dev_label), output.test.path)
self.pb.message("%s - %s" % (singular(dev_label), output.test.path))
if self.pb:
self.pb.label = '[%4d|%4d|%4d]'%tuple(self.counts)
self.pb.update(self.n)
self.pb.update(self.n, self.counts)
def finish(self, completed):
if self.pb:
self.pb.finish(completed)
self.pb.finish(completed)
if not self.options.tinderbox:
self.list(completed)

View File

@ -61,8 +61,7 @@ def get_max_wait(tasks, results, timeout):
return None
# If we have a progress-meter, we need to wake up to update it frequently.
if results.pb is not None:
wait = min(wait, PROGRESS_BAR_GRANULARITY)
wait = min(wait, PROGRESS_BAR_GRANULARITY)
return wait
@ -195,8 +194,7 @@ def run_all_tests(tests, results, options):
kill_undead(tasks, results, options.timeout)
tasks = reap_zombies(tasks, results, options.timeout)
if results.pb:
results.pb.update(results.n)
results.pb.poke()
return True

View File

@ -450,14 +450,14 @@ GlobalObject::initStandardClasses(JSContext *cx, Handle<GlobalObject*> global)
#if JS_HAS_XML_SUPPORT
(!VersionHasAllowXML(cx->findVersion()) || js_InitXMLClasses(cx, global)) &&
#endif
#if JS_HAS_GENERATORS
js_InitIteratorClasses(cx, global) &&
#endif
js_InitDateClass(cx, global) &&
js_InitWeakMapClass(cx, global) &&
js_InitProxyClass(cx, global) &&
js_InitMapClass(cx, global) &&
js_InitSetClass(cx, global);
GlobalObject::initMapIteratorProto(cx, global) &&
js_InitSetClass(cx, global) &&
GlobalObject::initSetIteratorProto(cx, global);
}
void

View File

@ -93,7 +93,9 @@ class GlobalObject : public JSObject
/* One-off properties stored after slots for built-ins. */
static const unsigned ELEMENT_ITERATOR_PROTO = FROM_BUFFER_UINT8CLAMPED + 1;
static const unsigned GENERATOR_PROTO = ELEMENT_ITERATOR_PROTO + 1;
static const unsigned REGEXP_STATICS = GENERATOR_PROTO + 1;
static const unsigned MAP_ITERATOR_PROTO = GENERATOR_PROTO + 1;
static const unsigned SET_ITERATOR_PROTO = MAP_ITERATOR_PROTO + 1;
static const unsigned REGEXP_STATICS = SET_ITERATOR_PROTO + 1;
static const unsigned FUNCTION_NS = REGEXP_STATICS + 1;
static const unsigned RUNTIME_CODEGEN_ENABLED = FUNCTION_NS + 1;
static const unsigned FLAGS = RUNTIME_CODEGEN_ENABLED + 1;
@ -316,23 +318,37 @@ class GlobalObject : public JSObject
}
private:
JSObject *getOrCreateIteratorSubclassPrototype(JSContext *cx, unsigned slot) {
typedef bool (*ObjectInitOp)(JSContext *cx, Handle<GlobalObject*> global);
JSObject *getOrCreateObject(JSContext *cx, unsigned slot, ObjectInitOp init) {
Value v = getSlotRef(slot);
if (v.isObject())
return &v.toObject();
Rooted<GlobalObject*> self(cx, this);
if (!initIteratorClasses(cx, self))
if (!init(cx, self))
return NULL;
return &self->getSlot(slot).toObject();
}
public:
JSObject *getOrCreateIteratorPrototype(JSContext *cx) {
return getOrCreateObject(cx, JSProto_LIMIT + JSProto_Iterator, initIteratorClasses);
}
JSObject *getOrCreateElementIteratorPrototype(JSContext *cx) {
return getOrCreateIteratorSubclassPrototype(cx, ELEMENT_ITERATOR_PROTO);
return getOrCreateObject(cx, ELEMENT_ITERATOR_PROTO, initIteratorClasses);
}
JSObject *getOrCreateGeneratorPrototype(JSContext *cx) {
return getOrCreateIteratorSubclassPrototype(cx, GENERATOR_PROTO);
return getOrCreateObject(cx, GENERATOR_PROTO, initIteratorClasses);
}
JSObject *getOrCreateMapIteratorPrototype(JSContext *cx) {
return getOrCreateObject(cx, MAP_ITERATOR_PROTO, initMapIteratorProto);
}
JSObject *getOrCreateSetIteratorPrototype(JSContext *cx) {
return getOrCreateObject(cx, SET_ITERATOR_PROTO, initSetIteratorProto);
}
JSObject *getOrCreateDataViewPrototype(JSContext *cx) {
@ -387,6 +403,10 @@ class GlobalObject : public JSObject
// Implemented in jsiter.cpp.
static bool initIteratorClasses(JSContext *cx, Handle<GlobalObject*> global);
// Implemented in builtin/MapObject.cpp.
static bool initMapIteratorProto(JSContext *cx, Handle<GlobalObject*> global);
static bool initSetIteratorProto(JSContext *cx, Handle<GlobalObject*> global);
static bool initStandardClasses(JSContext *cx, Handle<GlobalObject*> global);
typedef js::Vector<js::Debugger *, 0, js::SystemAllocPolicy> DebuggerVector;

View File

@ -62,11 +62,10 @@ abstract public class BrowserApp extends GeckoApp
maybeCancelFaviconLoad(tab);
}
break;
case LOAD_ERROR:
case START:
if (Tabs.getInstance().isSelectedTab(tab)) {
invalidateOptionsMenu();
}
case STOP:
case MENU_UPDATED:
if (Tabs.getInstance().isSelectedTab(tab)) {
invalidateOptionsMenu();
}

View File

@ -558,6 +558,7 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
if (!mHasSoftMenuButton)
return false;
GeckoApp.mAppContext.invalidateOptionsMenu();
if (mMenuPopup != null && !mMenuPopup.isShowing())
mMenuPopup.showAsDropDown(mMenu);

View File

@ -169,14 +169,6 @@ abstract public class GeckoApp
updatePopups(tab);
invalidateOptionsMenu();
break;
case LOAD_ERROR:
case START:
case STOP:
// The options menu only applies to the selected tab.
if (Tabs.getInstance().isSelectedTab(tab))
invalidateOptionsMenu();
break;
}
}

View File

@ -101,7 +101,6 @@ public final class Tab {
mContentObserver = new ContentObserver(GeckoAppShell.getHandler()) {
public void onChange(boolean selfChange) {
updateBookmark();
updateReadingListItem();
}
};
BrowserDB.registerBookmarkObserver(mContentResolver, mContentObserver);
@ -248,7 +247,6 @@ public final class Tab {
mUrl = url;
Log.i(LOGTAG, "Updated url: " + url + " for tab with id: " + mId);
updateBookmark();
updateReadingListItem();
updateHistory(mUrl, mTitle);
}
}
@ -379,6 +377,11 @@ public final class Tab {
public void setReaderEnabled(boolean readerEnabled) {
mReaderEnabled = readerEnabled;
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
Tabs.getInstance().notifyListeners(Tab.this, Tabs.TabEvents.MENU_UPDATED);
}
});
}
private void updateBookmark() {
@ -386,29 +389,21 @@ public final class Tab {
if (url == null)
return;
GeckoBackgroundThread.getHandler().post(new Runnable() {
public void run() {
boolean bookmark = BrowserDB.isBookmark(mContentResolver, url);
(new GeckoAsyncTask<Void, Void, Void>() {
@Override
public Void doInBackground(Void... params) {
if (url.equals(getURL())) {
mBookmark = bookmark;
mBookmark = BrowserDB.isBookmark(mContentResolver, url);
mReadingListItem = BrowserDB.isReadingListItem(mContentResolver, url);
}
return null;
}
});
}
private void updateReadingListItem() {
final String url = getURL();
if (url == null)
return;
GeckoBackgroundThread.getHandler().post(new Runnable() {
public void run() {
boolean readingListItem = BrowserDB.isReadingListItem(mContentResolver, url);
if (url.equals(getURL())) {
mReadingListItem = readingListItem;
}
@Override
public void onPostExecute(Void result) {
Tabs.getInstance().notifyListeners(Tab.this, Tabs.TabEvents.MENU_UPDATED);
}
});
}).execute();
}
public void addBookmark() {

View File

@ -350,7 +350,8 @@ public class Tabs implements GeckoEventListener {
UNSELECTED,
ADDED,
RESTORED,
LOCATION_CHANGE
LOCATION_CHANGE,
MENU_UPDATED
}
public void notifyListeners(Tab tab, TabEvents msg) {

View File

@ -48,6 +48,9 @@ Readability.prototype = {
// it quits and just show a link.
MAX_PAGES: 5,
// The number of iterations processed before yielding.
GEN_ITERATIONS: 100,
// All of the regular expressions in use within readability.
// Defined up here so we don't instantiate them repeatedly in loops.
REGEXPS: {
@ -326,277 +329,311 @@ Readability.prototype = {
node.readability.contentScore += this._getClassWeight(node);
},
_grabArticle: function (callback) {
let gen = this._grabArticleGenerator();
let iterate = function () {
for (let i = this.GEN_ITERATIONS; i--;) {
let result;
try {
// Parse can be interrupted if document changes (will throw dead
// object exception)
result = gen.next();
} catch (e) {
dump("Caught exception while grabbing article, aborting");
result = null;
}
if (result !== undefined) {
callback(result);
return;
}
}
setTimeout(iterate, 0);
}.bind(this);
iterate();
},
/***
* grabArticle - Using a variety of metrics (content score, classname, element types), find the content that is
* grabArticleGenerator - Using a variety of metrics (content score, classname, element types), find the content that is
* most likely to be the stuff a user wants to read. Then return it wrapped up in a div.
*
* @param page a document to run upon. Needs to be a full document, complete with body.
* @return Element
**/
_grabArticle: function(page) {
let doc = this._doc;
let stripUnlikelyCandidates = this._flagIsActive(this.FLAG_STRIP_UNLIKELYS);
let isChecking = this._flagIsActive(this.FLAG_READABILITY_CHECK);
let isPaging = (page !== null ? true: false);
_grabArticleGenerator: function(page) {
while (true) {
let doc = this._doc;
let stripUnlikelyCandidates = this._flagIsActive(this.FLAG_STRIP_UNLIKELYS);
let isChecking = this._flagIsActive(this.FLAG_READABILITY_CHECK);
let isPaging = (page !== null ? true: false);
page = page ? page : this._doc.body;
page = page ? page : this._doc.body;
let pageCacheHtml = page.innerHTML;
let allElements = page.getElementsByTagName('*');
let pageCacheHtml = page.innerHTML;
let allElements = page.getElementsByTagName('*');
// First, node prepping. Trash nodes that look cruddy (like ones with the
// class name "comment", etc), and turn divs into P tags where they have been
// used inappropriately (as in, where they contain no other block level elements.)
//
// Note: Assignment from index for performance. See http://www.peachpit.com/articles/article.aspx?p=31567&seqNum=5
// TODO: Shouldn't this be a reverse traversal?
let node = null;
let nodesToScore = [];
yield;
for (let nodeIndex = 0; (node = allElements[nodeIndex]); nodeIndex += 1) {
// Remove unlikely candidates
if (stripUnlikelyCandidates) {
let unlikelyMatchString = node.className + node.id;
if (unlikelyMatchString.search(this.REGEXPS.unlikelyCandidates) !== -1 &&
unlikelyMatchString.search(this.REGEXPS.okMaybeItsACandidate) === -1 &&
node.tagName !== "BODY") {
dump("Removing unlikely candidate - " + unlikelyMatchString);
node.parentNode.removeChild(node);
nodeIndex -= 1;
continue;
}
}
// First, node prepping. Trash nodes that look cruddy (like ones with the
// class name "comment", etc), and turn divs into P tags where they have been
// used inappropriately (as in, where they contain no other block level elements.)
//
// Note: Assignment from index for performance. See http://www.peachpit.com/articles/article.aspx?p=31567&seqNum=5
// TODO: Shouldn't this be a reverse traversal?
let node = null;
let nodesToScore = [];
if (node.tagName === "P" || node.tagName === "TD" || node.tagName === "PRE")
nodesToScore[nodesToScore.length] = node;
// Turn all divs that don't have children block level elements into p's
if (node.tagName === "DIV") {
if (node.innerHTML.search(this.REGEXPS.divToPElements) === -1) {
if (!isChecking) {
let newNode = doc.createElement('p');
newNode.innerHTML = node.innerHTML;
node.parentNode.replaceChild(newNode, node);
for (let nodeIndex = 0; (node = allElements[nodeIndex]); nodeIndex += 1) {
// Remove unlikely candidates
if (stripUnlikelyCandidates) {
let unlikelyMatchString = node.className + node.id;
if (unlikelyMatchString.search(this.REGEXPS.unlikelyCandidates) !== -1 &&
unlikelyMatchString.search(this.REGEXPS.okMaybeItsACandidate) === -1 &&
node.tagName !== "BODY") {
dump("Removing unlikely candidate - " + unlikelyMatchString);
node.parentNode.removeChild(node);
nodeIndex -= 1;
continue;
}
}
if (node.tagName === "P" || node.tagName === "TD" || node.tagName === "PRE")
nodesToScore[nodesToScore.length] = node;
} else if (!isChecking) {
// EXPERIMENTAL
for (let i = 0, il = node.childNodes.length; i < il; i += 1) {
let childNode = node.childNodes[i];
if (!childNode)
continue;
if (childNode.nodeType === 3) { // Node.TEXT_NODE
let p = doc.createElement('p');
p.innerHTML = childNode.nodeValue;
p.style.display = 'inline';
p.className = 'readability-styled';
// Turn all divs that don't have children block level elements into p's
if (node.tagName === "DIV") {
if (node.innerHTML.search(this.REGEXPS.divToPElements) === -1) {
if (!isChecking) {
let newNode = doc.createElement('p');
newNode.innerHTML = node.innerHTML;
node.parentNode.replaceChild(newNode, node);
nodeIndex -= 1;
}
childNode.parentNode.replaceChild(p, childNode);
nodesToScore[nodesToScore.length] = node;
} else if (!isChecking) {
// EXPERIMENTAL
for (let i = 0, il = node.childNodes.length; i < il; i += 1) {
let childNode = node.childNodes[i];
if (!childNode)
continue;
if (childNode.nodeType === 3) { // Node.TEXT_NODE
let p = doc.createElement('p');
p.innerHTML = childNode.nodeValue;
p.style.display = 'inline';
p.className = 'readability-styled';
childNode.parentNode.replaceChild(p, childNode);
}
}
}
}
}
}
/**
* Loop through all paragraphs, and assign a score to them based on how content-y they look.
* Then add their score to their parent node.
*
* A score is determined by things like number of commas, class names, etc. Maybe eventually link density.
**/
let candidates = [];
for (let pt = 0; pt < nodesToScore.length; pt += 1) {
let parentNode = nodesToScore[pt].parentNode;
let grandParentNode = parentNode ? parentNode.parentNode : null;
let innerText = this._getInnerText(nodesToScore[pt]);
if (!parentNode || typeof(parentNode.tagName) === 'undefined')
continue;
// If this paragraph is less than 25 characters, don't even count it.
if (innerText.length < 25)
continue;
// Initialize readability data for the parent.
if (typeof parentNode.readability === 'undefined') {
this._initializeNode(parentNode);
candidates.push(parentNode);
yield;
}
// Initialize readability data for the grandparent.
if (grandParentNode &&
typeof(grandParentNode.readability) === 'undefined' &&
typeof(grandParentNode.tagName) !== 'undefined') {
this._initializeNode(grandParentNode);
candidates.push(grandParentNode);
/**
* Loop through all paragraphs, and assign a score to them based on how content-y they look.
* Then add their score to their parent node.
*
* A score is determined by things like number of commas, class names, etc. Maybe eventually link density.
**/
let candidates = [];
for (let pt = 0; pt < nodesToScore.length; pt += 1) {
let parentNode = nodesToScore[pt].parentNode;
let grandParentNode = parentNode ? parentNode.parentNode : null;
let innerText = this._getInnerText(nodesToScore[pt]);
if (!parentNode || typeof(parentNode.tagName) === 'undefined')
continue;
// If this paragraph is less than 25 characters, don't even count it.
if (innerText.length < 25)
continue;
// Initialize readability data for the parent.
if (typeof parentNode.readability === 'undefined') {
this._initializeNode(parentNode);
candidates.push(parentNode);
}
// Initialize readability data for the grandparent.
if (grandParentNode &&
typeof(grandParentNode.readability) === 'undefined' &&
typeof(grandParentNode.tagName) !== 'undefined') {
this._initializeNode(grandParentNode);
candidates.push(grandParentNode);
}
let contentScore = 0;
// Add a point for the paragraph itself as a base.
contentScore += 1;
// Add points for any commas within this paragraph.
contentScore += innerText.split(',').length;
// For every 100 characters in this paragraph, add another point. Up to 3 points.
contentScore += Math.min(Math.floor(innerText.length / 100), 3);
// Add the score to the parent. The grandparent gets half.
parentNode.readability.contentScore += contentScore;
if (grandParentNode)
grandParentNode.readability.contentScore += contentScore / 2;
yield;
}
let contentScore = 0;
// After we've calculated scores, loop through all of the possible
// candidate nodes we found and find the one with the highest score.
let topCandidate = null;
for (let c = 0, cl = candidates.length; c < cl; c += 1) {
// Scale the final candidates score based on link density. Good content
// should have a relatively small link density (5% or less) and be mostly
// unaffected by this operation.
candidates[c].readability.contentScore =
candidates[c].readability.contentScore * (1 - this._getLinkDensity(candidates[c]));
// Add a point for the paragraph itself as a base.
contentScore += 1;
dump('Candidate: ' + candidates[c] + " (" + candidates[c].className + ":" +
candidates[c].id + ") with score " +
candidates[c].readability.contentScore);
// Add points for any commas within this paragraph.
contentScore += innerText.split(',').length;
if (!topCandidate ||
candidates[c].readability.contentScore > topCandidate.readability.contentScore) {
topCandidate = candidates[c];
}
// For every 100 characters in this paragraph, add another point. Up to 3 points.
contentScore += Math.min(Math.floor(innerText.length / 100), 3);
// Add the score to the parent. The grandparent gets half.
parentNode.readability.contentScore += contentScore;
if (grandParentNode)
grandParentNode.readability.contentScore += contentScore / 2;
}
// After we've calculated scores, loop through all of the possible
// candidate nodes we found and find the one with the highest score.
let topCandidate = null;
for (let c = 0, cl = candidates.length; c < cl; c += 1) {
// Scale the final candidates score based on link density. Good content
// should have a relatively small link density (5% or less) and be mostly
// unaffected by this operation.
candidates[c].readability.contentScore =
candidates[c].readability.contentScore * (1 - this._getLinkDensity(candidates[c]));
dump('Candidate: ' + candidates[c] + " (" + candidates[c].className + ":" +
candidates[c].id + ") with score " +
candidates[c].readability.contentScore);
if (!topCandidate ||
candidates[c].readability.contentScore > topCandidate.readability.contentScore) {
topCandidate = candidates[c];
}
}
// If we still have no top candidate, just use the body as a last resort.
// We also have to copy the body node so it is something we can modify.
if (topCandidate === null || topCandidate.tagName === "BODY") {
// If we couldn't find a candidate for article content at this point,
// it's very unlikely to be a convertible page, just bail the check.
if (isChecking) {
dump('No top candidate found, failed readability check');
return null;
yield;
}
topCandidate = doc.createElement("DIV");
topCandidate.innerHTML = page.innerHTML;
// If we still have no top candidate, just use the body as a last resort.
// We also have to copy the body node so it is something we can modify.
if (topCandidate === null || topCandidate.tagName === "BODY") {
// If we couldn't find a candidate for article content at this point,
// it's very unlikely to be a convertible page, just bail the check.
if (isChecking) {
dump('No top candidate found, failed readability check');
yield null;
}
page.innerHTML = "";
page.appendChild(topCandidate);
topCandidate = doc.createElement("DIV");
topCandidate.innerHTML = page.innerHTML;
this._initializeNode(topCandidate);
} else if (isChecking) {
dump('Found a top candidate, passed readability check');
page.innerHTML = "";
page.appendChild(topCandidate);
// Just return a non-null value, no need to post-process the article content
// as we're just checking for readability.
return {};
}
this._initializeNode(topCandidate);
} else if (isChecking) {
dump('Found a top candidate, passed readability check');
// Now that we have the top candidate, look through its siblings for content
// that might also be related. Things like preambles, content split by ads
// that we removed, etc.
let articleContent = doc.createElement("DIV");
if (isPaging)
articleContent.id = "readability-content";
let siblingScoreThreshold = Math.max(10, topCandidate.readability.contentScore * 0.2);
let siblingNodes = topCandidate.parentNode.childNodes;
for (let s = 0, sl = siblingNodes.length; s < sl; s += 1) {
let siblingNode = siblingNodes[s];
let append = false;
dump("Looking at sibling node: " + siblingNode + " (" + siblingNode.className + ":" + siblingNode.id + ")" + ((typeof siblingNode.readability !== 'undefined') ? (" with score " + siblingNode.readability.contentScore) : ''));
dump("Sibling has score " + (siblingNode.readability ? siblingNode.readability.contentScore : 'Unknown'));
if (siblingNode === topCandidate)
append = true;
let contentBonus = 0;
// Give a bonus if sibling nodes and top candidates have the example same classname
if (siblingNode.className === topCandidate.className && topCandidate.className !== "")
contentBonus += topCandidate.readability.contentScore * 0.2;
if (typeof siblingNode.readability !== 'undefined' &&
(siblingNode.readability.contentScore+contentBonus) >= siblingScoreThreshold) {
append = true;
// Just return a non-null value, no need to post-process the article content
// as we're just checking for readability.
yield {};
}
if (siblingNode.nodeName === "P") {
let linkDensity = this._getLinkDensity(siblingNode);
let nodeContent = this._getInnerText(siblingNode);
let nodeLength = nodeContent.length;
// Now that we have the top candidate, look through its siblings for content
// that might also be related. Things like preambles, content split by ads
// that we removed, etc.
let articleContent = doc.createElement("DIV");
if (isPaging)
articleContent.id = "readability-content";
if (nodeLength > 80 && linkDensity < 0.25) {
let siblingScoreThreshold = Math.max(10, topCandidate.readability.contentScore * 0.2);
let siblingNodes = topCandidate.parentNode.childNodes;
for (let s = 0, sl = siblingNodes.length; s < sl; s += 1) {
let siblingNode = siblingNodes[s];
let append = false;
dump("Looking at sibling node: " + siblingNode + " (" + siblingNode.className + ":" + siblingNode.id + ")" + ((typeof siblingNode.readability !== 'undefined') ? (" with score " + siblingNode.readability.contentScore) : ''));
dump("Sibling has score " + (siblingNode.readability ? siblingNode.readability.contentScore : 'Unknown'));
if (siblingNode === topCandidate)
append = true;
} else if (nodeLength < 80 && linkDensity === 0 && nodeContent.search(/\.( |$)/) !== -1) {
let contentBonus = 0;
// Give a bonus if sibling nodes and top candidates have the example same classname
if (siblingNode.className === topCandidate.className && topCandidate.className !== "")
contentBonus += topCandidate.readability.contentScore * 0.2;
if (typeof siblingNode.readability !== 'undefined' &&
(siblingNode.readability.contentScore+contentBonus) >= siblingScoreThreshold) {
append = true;
}
if (siblingNode.nodeName === "P") {
let linkDensity = this._getLinkDensity(siblingNode);
let nodeContent = this._getInnerText(siblingNode);
let nodeLength = nodeContent.length;
if (nodeLength > 80 && linkDensity < 0.25) {
append = true;
} else if (nodeLength < 80 && linkDensity === 0 && nodeContent.search(/\.( |$)/) !== -1) {
append = true;
}
}
if (append) {
dump("Appending node: " + siblingNode);
let nodeToAppend = null;
if (siblingNode.nodeName !== "DIV" && siblingNode.nodeName !== "P") {
// We have a node that isn't a common block level element, like a form or td tag.
// Turn it into a div so it doesn't get filtered out later by accident. */
dump("Altering siblingNode of " + siblingNode.nodeName + ' to div.');
nodeToAppend = doc.createElement("DIV");
nodeToAppend.id = siblingNode.id;
nodeToAppend.innerHTML = siblingNode.innerHTML;
} else {
nodeToAppend = siblingNode;
s -= 1;
sl -= 1;
}
// To ensure a node does not interfere with readability styles,
// remove its classnames.
nodeToAppend.className = "";
// Append sibling and subtract from our list because it removes
// the node when you append to another node.
articleContent.appendChild(nodeToAppend);
}
yield;
}
if (append) {
dump("Appending node: " + siblingNode);
// So we have all of the content that we need. Now we clean it up for presentation.
this._prepArticle(articleContent);
let nodeToAppend = null;
if (siblingNode.nodeName !== "DIV" && siblingNode.nodeName !== "P") {
// We have a node that isn't a common block level element, like a form or td tag.
// Turn it into a div so it doesn't get filtered out later by accident. */
dump("Altering siblingNode of " + siblingNode.nodeName + ' to div.');
yield;
nodeToAppend = doc.createElement("DIV");
nodeToAppend.id = siblingNode.id;
nodeToAppend.innerHTML = siblingNode.innerHTML;
if (this._curPageNum === 1)
articleContent.innerHTML = '<div id="readability-page-1" class="page">' + articleContent.innerHTML + '</div>';
// Now that we've gone through the full algorithm, check to see if
// we got any meaningful content. If we didn't, we may need to re-run
// grabArticle with different flags set. This gives us a higher likelihood of
// finding the content, and the sieve approach gives us a higher likelihood of
// finding the -right- content.
if (this._getInnerText(articleContent, false).length < 250) {
page.innerHTML = pageCacheHtml;
if (this._flagIsActive(this.FLAG_STRIP_UNLIKELYS)) {
this._removeFlag(this.FLAG_STRIP_UNLIKELYS);
} else if (this._flagIsActive(this.FLAG_WEIGHT_CLASSES)) {
this._removeFlag(this.FLAG_WEIGHT_CLASSES);
} else if (this._flagIsActive(this.FLAG_CLEAN_CONDITIONALLY)) {
this._removeFlag(this.FLAG_CLEAN_CONDITIONALLY);
} else {
nodeToAppend = siblingNode;
s -= 1;
sl -= 1;
yield null;
}
// To ensure a node does not interfere with readability styles,
// remove its classnames.
nodeToAppend.className = "";
// Append sibling and subtract from our list because it removes
// the node when you append to another node.
articleContent.appendChild(nodeToAppend);
}
}
// So we have all of the content that we need. Now we clean it up for presentation.
this._prepArticle(articleContent);
if (this._curPageNum === 1)
articleContent.innerHTML = '<div id="readability-page-1" class="page">' + articleContent.innerHTML + '</div>';
// Now that we've gone through the full algorithm, check to see if
// we got any meaningful content. If we didn't, we may need to re-run
// grabArticle with different flags set. This gives us a higher likelihood of
// finding the content, and the sieve approach gives us a higher likelihood of
// finding the -right- content.
if (this._getInnerText(articleContent, false).length < 250) {
page.innerHTML = pageCacheHtml;
if (this._flagIsActive(this.FLAG_STRIP_UNLIKELYS)) {
this._removeFlag(this.FLAG_STRIP_UNLIKELYS);
return this._grabArticle(page);
} else if (this._flagIsActive(this.FLAG_WEIGHT_CLASSES)) {
this._removeFlag(this.FLAG_WEIGHT_CLASSES);
return this._grabArticle(page);
} else if (this._flagIsActive(this.FLAG_CLEAN_CONDITIONALLY)) {
this._removeFlag(this.FLAG_CLEAN_CONDITIONALLY);
return this._grabArticle(page);
} else {
return null;
yield articleContent;
}
}
return articleContent;
},
/**
@ -1027,6 +1064,9 @@ Readability.prototype = {
this._flags = 0x1 | 0x2 | 0x4;
let nextPageLink = this._findNextPageLink(page);
// NOTE: if we end up supporting _appendNextPage(), we'll need to
// change this call to be async
let content = this._grabArticle(page);
if (!content) {
@ -1254,10 +1294,12 @@ Readability.prototype = {
*
* @return void
**/
parse: function() {
parse: function (callback) {
let uri = this._uri;
if ((uri.prePath + "/") === uri.spec)
return null;
if ((uri.prePath + "/") === uri.spec) {
callback(null);
return;
}
// Remove script tags from the document.
this._removeScripts(this._doc);
@ -1275,36 +1317,42 @@ Readability.prototype = {
this._prepDocument();
let articleTitle = this._getArticleTitle();
let articleContent = this._grabArticle();
this._grabArticle(function (articleContent) {
if (!articleContent) {
callback(null);
return;
}
if (!articleContent)
return null;
// If we're simply checking whether the document is convertible
// or not, we don't need to do any post-processing on the article
// content, just return a non-null value (see check() method)
if (this._flagIsActive(this.FLAG_READABILITY_CHECK)) {
callback({});
return;
}
// If we're simply checking whether the document is convertible
// or not, we don't need to do any post-processing on the article
// content, just return a non-null value (see check() method)
if (this._flagIsActive(this.FLAG_READABILITY_CHECK))
return {};
this._postProcessContent(articleContent);
this._postProcessContent(articleContent);
// if (nextPageLink) {
// // Append any additional pages after a small timeout so that people
// // can start reading without having to wait for this to finish processing.
// setTimeout((function() {
// this._appendNextPage(nextPageLink);
// }).bind(this), 500);
// }
// if (nextPageLink) {
// // Append any additional pages after a small timeout so that people
// // can start reading without having to wait for this to finish processing.
// setTimeout((function() {
// this._appendNextPage(nextPageLink);
// }).bind(this), 500);
// }
return { title: this._getInnerText(articleTitle),
content: articleContent.innerHTML };
callback({ title: this._getInnerText(articleTitle),
content: articleContent.innerHTML });
}.bind(this));
},
check: function() {
check: function (callback) {
// Set proper flags for parsing document in readability check mode, skipping
// any DOM manipulation.
this._flags = this.FLAG_READABILITY_CHECK;
return (this.parse() != null);
this.parse(function (result) {
callback(result != null);
});
}
};

View File

@ -6320,18 +6320,18 @@ let Reader = {
let doc = tab.browser.contentWindow.document.cloneNode(true);
let readability = new Readability(uri, doc);
article = readability.parse();
readability.parse(function (article) {
if (!article) {
this.log("Failed to parse page");
callback(null);
return;
}
if (!article) {
this.log("Failed to parse page");
callback(null);
return;
}
// Append URL to the article data
article.url = url;
// Append URL to the article data
article.url = url;
callback(article);
callback(article);
}.bind(this));
}.bind(this));
} catch (e) {
this.log("Error parsing document from tab: " + e);
@ -6358,7 +6358,7 @@ let Reader = {
let doc = tab.browser.contentWindow.document;
let readability = new Readability(uri, doc);
callback(readability.check());
readability.check(callback);
}.bind(this));
} catch (e) {
this.log("Error checking tab readability: " + e);
@ -6501,9 +6501,6 @@ let Reader = {
}
callback(doc);
// Request has finished, remove browser element
browser.parentNode.removeChild(browser);
}.bind(this));
browser.loadURIWithFlags(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
@ -6519,33 +6516,37 @@ let Reader = {
request.browser = this._downloadDocument(url, function(doc) {
this.log("Finished loading page: " + doc);
// Delete reference to the browser element as we're
// now done with this request.
delete request.browser;
if (!doc) {
this.log("Error loading page");
this._runCallbacksAndFinish(request, null);
return;
}
this.log("Parsing response with Readability");
let uri = Services.io.newURI(url, null, null);
let readability = new Readability(uri, doc);
let article = readability.parse();
readability.parse(function (article) {
// Delete reference to the browser element as we've finished parsing.
let browser = request.browser;
if (browser) {
browser.parentNode.removeChild(browser);
delete request.browser;
}
if (!article) {
this.log("Failed to parse page");
this._runCallbacksAndFinish(request, null);
return;
}
if (!article) {
this.log("Failed to parse page");
this._runCallbacksAndFinish(request, null);
return;
}
this.log("Parsing has been successful");
this.log("Parsing has been successful");
// Append URL to the article data
article.url = url;
// Append URL to the article data
article.url = url;
this._runCallbacksAndFinish(request, article);
this._runCallbacksAndFinish(request, article);
}.bind(this));
}.bind(this));
} catch (e) {
this.log("Error downloading and parsing document: " + e);

View File

@ -323,7 +323,7 @@ SHELL_WRAPPER2(notifyFilePickerResult, jstring, jlong)
SHELL_WRAPPER1_WITH_RETURN(getSurfaceBits, jobject, jobject)
SHELL_WRAPPER1(onFullScreenPluginHidden, jobject)
SHELL_WRAPPER1_WITH_RETURN(getNextMessageFromQueue, jobject, jobject)
SHELL_WRAPPER2(onSurfaceTextureFrameAvailable, jobject, jint);
SHELL_WRAPPER2(onSurfaceTextureFrameAvailable, jobject, jint)
static void * xul_handle = NULL;
static void * sqlite_handle = NULL;

View File

@ -0,0 +1,37 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from manifestparser import TestManifest
import os.path
import sys
def print_test_dirs(topsrcdir, manifest_file):
"""
Simple routine which prints the paths of directories specified
in a Marionette manifest, relative to topsrcdir. This does not recurse
into manifests, as we currently have no need for that.
"""
# output the directory of this (parent) manifest
topsrcdir = os.path.abspath(topsrcdir)
scriptdir = os.path.abspath(os.path.dirname(__file__))
print scriptdir[len(topsrcdir) + 1:]
# output the directories of all the other manifests
dirs = set()
manifest = TestManifest()
manifest.read(manifest_file)
for i in manifest.get():
dirs.add(os.path.dirname(i['manifest'])[len(topsrcdir) + 1:])
for path in dirs:
print path
if __name__ == '__main__':
if len(sys.argv) < 3:
print >>sys.stderr, "Usage: %s topsrcdir manifest.ini" % sys.argv[0]
sys.exit(1)
print_test_dirs(sys.argv[1], sys.argv[2])

View File

@ -290,6 +290,7 @@ package-tests: \
stage-mozbase \
stage-tps \
stage-modules \
stage-marionette \
$(NULL)
else
# This staging area has been built for us by universal/flight.mk
@ -369,6 +370,16 @@ stage-modules: make-stage-dir
$(NSINSTALL) -D $(PKG_STAGE)/modules
cp -RL $(DEPTH)/_tests/modules $(PKG_STAGE)
MARIONETTE_DIR=$(PKG_STAGE)/marionette
stage-marionette: make-stage-dir
$(NSINSTALL) -D $(MARIONETTE_DIR)/tests
@(cd $(topsrcdir)/testing/marionette/client && tar --exclude marionette/tests $(TAR_CREATE_FLAGS) - *) | (cd $(MARIONETTE_DIR) && tar -xf -)
$(PYTHON) $(topsrcdir)/testing/marionette/client/marionette/tests/print-manifest-dirs.py \
$(topsrcdir) \
$(topsrcdir)/testing/marionette/client/marionette/tests/unit-tests.ini \
| (cd $(topsrcdir) && xargs tar $(TAR_CREATE_FLAGS_QUIET) -) \
| (cd $(MARIONETTE_DIR)/tests && tar -xf -)
stage-mozbase: make-stage-dir
$(MAKE) -C $(DEPTH)/testing/mozbase stage-package
.PHONY: \
@ -395,5 +406,6 @@ stage-mozbase: make-stage-dir
stage-mozbase \
stage-tps \
stage-modules \
stage-marionette \
$(NULL)

View File

@ -156,8 +156,7 @@ install::
$(SYSINSTALL) $(IFLAGS1) $(srcdir)/test.properties $(DESTDIR)$(mozappdir)/res
ifeq (,$(filter-out WINNT os2-emx, $(HOST_OS_ARCH)))
swapslashes = $(shell echo $(1) | sed -e 's|/|\\|g')
getnativepath = $(call swapslashes,$(call normalizepath,$(1)))
getnativepath = $(call normalizepath,$(1))
else
getnativepath = $(1)
endif

View File

@ -156,8 +156,20 @@ int main(int argc, char** argv)
}
ScopedLogging logging;
#ifdef XP_WIN
// On Windows, convert to backslashes
size_t regPathLen = strlen(argv[1]);
char* regPath = new char[regPathLen + 1];
for (size_t i = 0; i < regPathLen; i++) {
char curr = argv[1][i];
regPath[i] = (curr == '/') ? '\\' : curr;
}
regPath[regPathLen] = '\0';
#else
const char *regPath = argv[1];
#endif
XRE_AddManifestLocation(NS_COMPONENT_LOCATION,
nsCOMPtr<nsIFile>(GetRegDirectory(regPath, "core", "component.manifest")));
XRE_AddManifestLocation(NS_COMPONENT_LOCATION,