mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Merge backout.
This commit is contained in:
commit
9ff2ccdd5f
@ -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);
|
||||
})();
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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" +
|
||||
|
@ -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; \
|
||||
|
@ -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)
|
||||
|
||||
|
@ -39,8 +39,6 @@ endif #} INCLUDED_XPIDL_MK
|
||||
###########################################################################
|
||||
ifdef _xpidl-todo_ #{
|
||||
|
||||
$(call requiredfunction,install_cmd)
|
||||
|
||||
## Logic batch #1
|
||||
xpidl-install-src-preqs=\
|
||||
$(XPIDLSRCS) \
|
||||
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ DIRS = \
|
||||
|
||||
MOCHITEST_FILES = \
|
||||
test_contacts_basics.html \
|
||||
test_contacts_events.html \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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; ");
|
||||
|
@ -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), " ");
|
||||
|
12
js/src/jit-test/tests/collections/Map-gc-3.js
Normal file
12
js/src/jit-test/tests/collections/Map-gc-3.js
Normal 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);
|
||||
}
|
15
js/src/jit-test/tests/collections/Map-gc-4.js
Normal file
15
js/src/jit-test/tests/collections/Map-gc-4.js
Normal 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);
|
11
js/src/jit-test/tests/collections/Map-iterator-1.js
Normal file
11
js/src/jit-test/tests/collections/Map-iterator-1.js
Normal 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;');
|
11
js/src/jit-test/tests/collections/Map-iterator-2.js
Normal file
11
js/src/jit-test/tests/collections/Map-iterator-2.js
Normal 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;');
|
15
js/src/jit-test/tests/collections/Map-iterator-add-1.js
Normal file
15
js/src/jit-test/tests/collections/Map-iterator-add-1.js
Normal 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);
|
11
js/src/jit-test/tests/collections/Map-iterator-add-2.js
Normal file
11
js/src/jit-test/tests/collections/Map-iterator-add-2.js
Normal 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
|
14
js/src/jit-test/tests/collections/Map-iterator-add-remove.js
Normal file
14
js/src/jit-test/tests/collections/Map-iterator-add-remove.js
Normal 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);
|
15
js/src/jit-test/tests/collections/Map-iterator-order.js
Normal file
15
js/src/jit-test/tests/collections/Map-iterator-order.js
Normal 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);
|
15
js/src/jit-test/tests/collections/Map-iterator-pairs-1.js
Normal file
15
js/src/jit-test/tests/collections/Map-iterator-pairs-1.js
Normal 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');
|
10
js/src/jit-test/tests/collections/Map-iterator-pairs-2.js
Normal file
10
js/src/jit-test/tests/collections/Map-iterator-pairs-2.js
Normal 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);
|
12
js/src/jit-test/tests/collections/Map-iterator-pairs-3.js
Normal file
12
js/src/jit-test/tests/collections/Map-iterator-pairs-3.js
Normal 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);
|
@ -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");
|
19
js/src/jit-test/tests/collections/Map-iterator-proxies-2.js
Normal file
19
js/src/jit-test/tests/collections/Map-iterator-proxies-2.js
Normal 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);
|
40
js/src/jit-test/tests/collections/Map-iterator-remove-1.js
Normal file
40
js/src/jit-test/tests/collections/Map-iterator-remove-1.js
Normal 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]]);
|
11
js/src/jit-test/tests/collections/Map-iterator-remove-2.js
Normal file
11
js/src/jit-test/tests/collections/Map-iterator-remove-2.js
Normal 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');
|
12
js/src/jit-test/tests/collections/Map-iterator-remove-3.js
Normal file
12
js/src/jit-test/tests/collections/Map-iterator-remove-3.js
Normal 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);
|
32
js/src/jit-test/tests/collections/Map-iterator-remove-4.js
Normal file
32
js/src/jit-test/tests/collections/Map-iterator-remove-4.js
Normal 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);
|
||||
}
|
22
js/src/jit-test/tests/collections/Map-iterator-remove-5.js
Normal file
22
js/src/jit-test/tests/collections/Map-iterator-remove-5.js
Normal 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);
|
20
js/src/jit-test/tests/collections/Map-iterator-remove-6.js
Normal file
20
js/src/jit-test/tests/collections/Map-iterator-remove-6.js
Normal 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);
|
11
js/src/jit-test/tests/collections/Set-iterator-1.js
Normal file
11
js/src/jit-test/tests/collections/Set-iterator-1.js
Normal 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;');
|
11
js/src/jit-test/tests/collections/Set-iterator-2.js
Normal file
11
js/src/jit-test/tests/collections/Set-iterator-2.js
Normal 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;');
|
11
js/src/jit-test/tests/collections/Set-iterator-3.js
Normal file
11
js/src/jit-test/tests/collections/Set-iterator-3.js
Normal 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);
|
||||
|
11
js/src/jit-test/tests/collections/Set-iterator-add-1.js
Normal file
11
js/src/jit-test/tests/collections/Set-iterator-add-1.js
Normal 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);
|
10
js/src/jit-test/tests/collections/Set-iterator-add-2.js
Normal file
10
js/src/jit-test/tests/collections/Set-iterator-add-2.js
Normal 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
|
13
js/src/jit-test/tests/collections/Set-iterator-add-remove.js
Normal file
13
js/src/jit-test/tests/collections/Set-iterator-add-remove.js
Normal 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);
|
8
js/src/jit-test/tests/collections/Set-iterator-gc-1.js
Normal file
8
js/src/jit-test/tests/collections/Set-iterator-gc-1.js
Normal 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);
|
8
js/src/jit-test/tests/collections/Set-iterator-gc-2.js
Normal file
8
js/src/jit-test/tests/collections/Set-iterator-gc-2.js
Normal 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;
|
||||
}
|
20
js/src/jit-test/tests/collections/Set-iterator-gc-3.js
Normal file
20
js/src/jit-test/tests/collections/Set-iterator-gc-3.js
Normal 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();
|
14
js/src/jit-test/tests/collections/Set-iterator-order.js
Normal file
14
js/src/jit-test/tests/collections/Set-iterator-order.js
Normal 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);
|
@ -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");
|
18
js/src/jit-test/tests/collections/Set-iterator-proxies-2.js
Normal file
18
js/src/jit-test/tests/collections/Set-iterator-proxies-2.js
Normal 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);
|
26
js/src/jit-test/tests/collections/Set-iterator-remove-1.js
Normal file
26
js/src/jit-test/tests/collections/Set-iterator-remove-1.js
Normal 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
|
||||
|
11
js/src/jit-test/tests/collections/Set-iterator-remove-2.js
Normal file
11
js/src/jit-test/tests/collections/Set-iterator-remove-2.js
Normal 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");
|
12
js/src/jit-test/tests/collections/Set-iterator-remove-3.js
Normal file
12
js/src/jit-test/tests/collections/Set-iterator-remove-3.js
Normal 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);
|
31
js/src/jit-test/tests/collections/Set-iterator-remove-4.js
Normal file
31
js/src/jit-test/tests/collections/Set-iterator-remove-4.js
Normal 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);
|
||||
}
|
22
js/src/jit-test/tests/collections/Set-iterator-remove-5.js
Normal file
22
js/src/jit-test/tests/collections/Set-iterator-remove-5.js
Normal 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);
|
20
js/src/jit-test/tests/collections/Set-iterator-remove-6.js
Normal file
20
js/src/jit-test/tests/collections/Set-iterator-remove-6.js
Normal 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);
|
12
js/src/jit-test/tests/collections/iterator-1.js
Normal file
12
js/src/jit-test/tests/collections/iterator-1.js
Normal 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);
|
12
js/src/jit-test/tests/collections/iterator-2.js
Normal file
12
js/src/jit-test/tests/collections/iterator-2.js
Normal 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);
|
14
js/src/jit-test/tests/collections/iterator-gc.js
Normal file
14
js/src/jit-test/tests/collections/iterator-gc.js
Normal 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");
|
13
js/src/jit-test/tests/collections/iterator-proto-1.js
Normal file
13
js/src/jit-test/tests/collections/iterator-proto-1.js
Normal 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]));
|
11
js/src/jit-test/tests/collections/iterator-proto-2.js
Normal file
11
js/src/jit-test/tests/collections/iterator-proto-2.js
Normal 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);
|
22
js/src/jit-test/tests/collections/iterator-proto-surfaces.js
Normal file
22
js/src/jit-test/tests/collections/iterator-proto-surfaces.js
Normal 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);
|
@ -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 \
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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); }
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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])
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user