diff --git a/CLOBBER b/CLOBBER index e38196bc4c1..faae27f5a53 100644 --- a/CLOBBER +++ b/CLOBBER @@ -19,3 +19,5 @@ # Bug 921718 presumably needed a clobber due to bug 928195. +and +Bug 853423 - New code (inc. new function IsIPAddrLocal) is not being included in incremental builds. diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index b8f1eb7f905..4b99d28c5a0 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -2001,6 +2001,7 @@ GK_ATOM(DisplayPort, "_displayport") GK_ATOM(CriticalDisplayPort, "_critical_displayport") // Names for system metrics +GK_ATOM(color_picker_available, "color-picker-available") GK_ATOM(scrollbar_start_backward, "scrollbar-start-backward") GK_ATOM(scrollbar_start_forward, "scrollbar-start-forward") GK_ATOM(scrollbar_end_backward, "scrollbar-end-backward") @@ -2038,6 +2039,7 @@ GK_ATOM(windows_version_win7, "windows-version-win7") GK_ATOM(windows_version_win8, "windows-version-win8") // And the same again, as media query keywords. +GK_ATOM(_moz_color_picker_available, "-moz-color-picker-available") GK_ATOM(_moz_scrollbar_start_backward, "-moz-scrollbar-start-backward") GK_ATOM(_moz_scrollbar_start_forward, "-moz-scrollbar-start-forward") GK_ATOM(_moz_scrollbar_end_backward, "-moz-scrollbar-end-backward") diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h index 5c656602b88..552f4231f22 100644 --- a/js/src/gc/GCInternals.h +++ b/js/src/gc/GCInternals.h @@ -26,9 +26,10 @@ BufferGrayRoots(GCMarker *gcmarker); class AutoCopyFreeListToArenas { JSRuntime *runtime; + ZoneSelector selector; public: - AutoCopyFreeListToArenas(JSRuntime *rt); + AutoCopyFreeListToArenas(JSRuntime *rt, ZoneSelector selector); ~AutoCopyFreeListToArenas(); }; @@ -64,7 +65,7 @@ struct AutoPrepareForTracing AutoTraceSession session; AutoCopyFreeListToArenas copy; - AutoPrepareForTracing(JSRuntime *rt); + AutoPrepareForTracing(JSRuntime *rt, ZoneSelector selector); }; class IncrementalSafety diff --git a/js/src/gc/Iteration.cpp b/js/src/gc/Iteration.cpp index b5f38d4a575..ae14a2c974c 100644 --- a/js/src/gc/Iteration.cpp +++ b/js/src/gc/Iteration.cpp @@ -11,6 +11,7 @@ #include "js/HashTable.h" #include "vm/Runtime.h" +#include "jscntxtinlines.h" #include "jsgcinlines.h" using namespace js; @@ -21,7 +22,8 @@ js::TraceRuntime(JSTracer *trc) { JS_ASSERT(!IS_GC_MARKING_TRACER(trc)); - AutoPrepareForTracing prep(trc->runtime); + AutoLockForExclusiveAccess lock(trc->runtime); + AutoPrepareForTracing prep(trc->runtime, WithAtoms); MarkRuntime(trc); } @@ -54,9 +56,10 @@ js::IterateZonesCompartmentsArenasCells(JSRuntime *rt, void *data, IterateArenaCallback arenaCallback, IterateCellCallback cellCallback) { - AutoPrepareForTracing prop(rt); + AutoLockForExclusiveAccess lock(rt); + AutoPrepareForTracing prop(rt, WithAtoms); - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { (*zoneCallback)(rt, data, zone); IterateCompartmentsArenasCells(rt, zone, data, compartmentCallback, arenaCallback, cellCallback); @@ -70,7 +73,8 @@ js::IterateZoneCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data, IterateArenaCallback arenaCallback, IterateCellCallback cellCallback) { - AutoPrepareForTracing prop(rt); + AutoLockForExclusiveAccess lock(rt); + AutoPrepareForTracing prop(rt, WithAtoms); (*zoneCallback)(rt, data, zone); IterateCompartmentsArenasCells(rt, zone, data, @@ -80,7 +84,7 @@ js::IterateZoneCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data, void js::IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback) { - AutoPrepareForTracing prep(rt); + AutoPrepareForTracing prep(rt, SkipAtoms); for (js::GCChunkSet::Range r = rt->gcChunkSet.all(); !r.empty(); r.popFront()) chunkCallback(rt, data, r.front()); @@ -90,7 +94,7 @@ void js::IterateScripts(JSRuntime *rt, JSCompartment *compartment, void *data, IterateScriptCallback scriptCallback) { - AutoPrepareForTracing prep(rt); + AutoPrepareForTracing prep(rt, SkipAtoms); if (compartment) { for (CellIterUnderGC i(compartment->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) { @@ -99,7 +103,7 @@ js::IterateScripts(JSRuntime *rt, JSCompartment *compartment, scriptCallback(rt, data, script); } } else { - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { for (CellIterUnderGC i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) scriptCallback(rt, data, i.get()); } @@ -109,7 +113,7 @@ js::IterateScripts(JSRuntime *rt, JSCompartment *compartment, void js::IterateGrayObjects(Zone *zone, GCThingCallback cellCallback, void *data) { - AutoPrepareForTracing prep(zone->runtimeFromMainThread()); + AutoPrepareForTracing prep(zone->runtimeFromMainThread(), SkipAtoms); for (size_t finalizeKind = 0; finalizeKind <= FINALIZE_OBJECT_LAST; finalizeKind++) { for (CellIterUnderGC i(zone, AllocKind(finalizeKind)); !i.done(); i.next()) { @@ -126,9 +130,10 @@ JS_IterateCompartments(JSRuntime *rt, void *data, { JS_ASSERT(!rt->isHeapBusy()); + AutoLockForExclusiveAccess lock(rt); AutoPauseWorkersForTracing pause(rt); AutoTraceSession session(rt); - for (CompartmentsIter c(rt); !c.done(); c.next()) + for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) (*compartmentCallback)(rt, data, c); } diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index cd2f2679df8..32fd95f9b41 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -602,7 +602,7 @@ js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason) rt->gcStoreBuffer.mark(&trc); // This must happen first. MarkRuntime(&trc); Debugger::markAll(&trc); - for (CompartmentsIter comp(rt); !comp.done(); comp.next()) { + for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) { comp->markAllCrossCompartmentWrappers(&trc); comp->markAllInitialShapeTableEntries(&trc); } diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index ccc8fc5e12b..f6e03fd88ec 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -664,7 +664,7 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots) JS_ASSERT(!rt->mainThread.suppressGC); if (IS_GC_MARKING_TRACER(trc)) { - for (CompartmentsIter c(rt); !c.done(); c.next()) { + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { if (!c->zone()->isCollecting()) c->markCrossCompartmentWrappers(trc); } @@ -721,7 +721,7 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots) for (ContextIter acx(rt); !acx.done(); acx.next()) acx->mark(trc); - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { if (IS_GC_MARKING_TRACER(trc) && !zone->isCollecting()) continue; @@ -743,7 +743,7 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots) } /* We can't use GCCompartmentsIter if we're called from TraceRuntime. */ - for (CompartmentsIter c(rt); !c.done(); c.next()) { + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { if (trc->runtime->isHeapMinorCollecting()) c->globalWriteBarriered = false; @@ -773,7 +773,7 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots) * which have been entered. Globals aren't nursery allocated so there's * no need to do this for minor GCs. */ - for (CompartmentsIter c(rt); !c.done(); c.next()) + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) c->mark(trc); /* diff --git a/js/src/gc/Verifier.cpp b/js/src/gc/Verifier.cpp index ced8318eb77..c91972a6ef7 100644 --- a/js/src/gc/Verifier.cpp +++ b/js/src/gc/Verifier.cpp @@ -16,6 +16,7 @@ #include "gc/Zone.h" #include "js/HashTable.h" +#include "jscntxtinlines.h" #include "jsgcinlines.h" using namespace js; @@ -235,13 +236,13 @@ JS::CheckStackRoots(JSContext *cx) // Can switch to the atoms compartment during analysis. if (IsAtomsCompartment(cx->compartment())) { - for (CompartmentsIter c(rt); !c.done(); c.next()) { + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { if (c.get()->activeAnalysis) return; } } - AutoCopyFreeListToArenas copy(rt); + AutoCopyFreeListToArenas copy(rt, WithAtoms); ConservativeGCData *cgcd = &rt->conservativeGC; cgcd->recordStackTop(); @@ -447,7 +448,8 @@ gc::StartVerifyPreBarriers(JSRuntime *rt) MinorGC(rt, JS::gcreason::API); - AutoPrepareForTracing prep(rt); + AutoLockForExclusiveAccess lock(rt); + AutoPrepareForTracing prep(rt, WithAtoms); if (!IsIncrementalGCSafe(rt)) return; @@ -510,7 +512,7 @@ gc::StartVerifyPreBarriers(JSRuntime *rt) rt->gcMarker.start(); rt->setNeedsBarrier(true); - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { PurgeJITCaches(zone); zone->setNeedsBarrier(true, Zone::UpdateIon); zone->allocator.arenas.purge(); @@ -575,7 +577,7 @@ AssertMarkedOrAllocated(const EdgeValue &edge) void gc::EndVerifyPreBarriers(JSRuntime *rt) { - AutoPrepareForTracing prep(rt); + AutoPrepareForTracing prep(rt, SkipAtoms); VerifyPreTracer *trc = (VerifyPreTracer *)rt->gcVerifyPreData; @@ -585,7 +587,7 @@ gc::EndVerifyPreBarriers(JSRuntime *rt) bool compartmentCreated = false; /* We need to disable barriers before tracing, which may invoke barriers. */ - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { if (!zone->needsBarrier()) compartmentCreated = true; @@ -740,7 +742,7 @@ js::gc::EndVerifyPostBarriers(JSRuntime *rt) { #ifdef JSGC_GENERATIONAL VerifyPostTracer::EdgeSet edges; - AutoPrepareForTracing prep(rt); + AutoPrepareForTracing prep(rt, SkipAtoms); VerifyPostTracer *trc = (VerifyPostTracer *)rt->gcVerifyPostData; diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 55cb553a476..a4a2c2cc8c0 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -319,21 +319,39 @@ struct Zone : public JS::shadow::Zone, namespace js { +/* + * Using the atoms zone without holding the exclusive access lock is dangerous + * because worker threads may be using it simultaneously. Therefore, it's + * better to skip the atoms zone when iterating over zones. If you need to + * iterate over the atoms zone, consider taking the exclusive access lock first. + */ +enum ZoneSelector { + WithAtoms, + SkipAtoms +}; + class ZonesIter { private: JS::Zone **it, **end; public: - ZonesIter(JSRuntime *rt) { + ZonesIter(JSRuntime *rt, ZoneSelector selector) { it = rt->zones.begin(); end = rt->zones.end(); + + if (selector == SkipAtoms) { + JS_ASSERT(rt->isAtomsZone(*it)); + it++; + } } bool done() const { return it == end; } void next() { JS_ASSERT(!done()); - it++; + do { + it++; + } while (!done() && (*it)->usedByExclusiveThread); } JS::Zone *get() const { @@ -383,8 +401,15 @@ class CompartmentsIterT CompartmentsIterT(JSRuntime *rt) : zone(rt) { - JS_ASSERT(!zone.done()); - comp.construct(zone); + if (!zone.done()) + comp.construct(zone); + } + + CompartmentsIterT(JSRuntime *rt, ZoneSelector selector) + : zone(rt, selector) + { + if (!zone.done()) + comp.construct(zone); } bool done() const { return zone.done(); } diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 816f78667b0..1fe214a8e8a 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -904,7 +904,7 @@ jit::AddSizeOfBaselineData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf, void jit::ToggleBaselineSPS(JSRuntime *runtime, bool enable) { - for (ZonesIter zone(runtime); !zone.done(); zone.next()) { + for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { for (gc::CellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { JSScript *script = i.get(); if (!script->hasBaselineScript()) diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index b07d2ba5206..551bd538b59 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -260,7 +260,7 @@ js::DestroyContext(JSContext *cx, DestroyContextMode mode) * Dump remaining type inference results while we still have a context. * This printing depends on atoms still existing. */ - for (CompartmentsIter c(rt); !c.done(); c.next()) + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) c->types.print(cx, false); } if (mode == DCM_FORCE_GC) { diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 6b236f4c207..4f780cfb93a 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -155,7 +155,7 @@ JS::PrepareZoneForGC(Zone *zone) JS_FRIEND_API(void) JS::PrepareForFullGC(JSRuntime *rt) { - for (ZonesIter zone(rt); !zone.done(); zone.next()) + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) zone->scheduleGC(); } @@ -165,7 +165,7 @@ JS::PrepareForIncrementalGC(JSRuntime *rt) if (!JS::IsIncrementalGCInProgress(rt)) return; - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { if (zone->wasGCStarted()) PrepareZoneForGC(zone); } @@ -174,7 +174,7 @@ JS::PrepareForIncrementalGC(JSRuntime *rt) JS_FRIEND_API(bool) JS::IsGCScheduled(JSRuntime *rt) { - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { if (zone->isGCScheduled()) return true; } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index e1c093e0ded..51a058ca32c 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1010,7 +1010,7 @@ js_FinishGC(JSRuntime *rt) #endif /* Delete all remaining zones. */ - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) js_delete(comp.get()); js_delete(zone.get()); @@ -1908,7 +1908,7 @@ size_t GCMarker::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { size_t size = stack.sizeOfExcludingThis(mallocSizeOf); - for (ZonesIter zone(runtime); !zone.done(); zone.next()) + for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) size += zone->gcGrayRoots.sizeOfExcludingThis(mallocSizeOf); return size; } @@ -2208,7 +2208,7 @@ static void AssertBackgroundSweepingFinished(JSRuntime *rt) { JS_ASSERT(!rt->gcSweepingZones); - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { for (unsigned i = 0; i < FINALIZE_LIMIT; ++i) { JS_ASSERT(!zone->allocator.arenas.arenaListsToSweep[i]); JS_ASSERT(zone->allocator.arenas.doneBackgroundFinalize(AllocKind(i))); @@ -2771,7 +2771,7 @@ CheckForCompartmentMismatches(JSRuntime *rt) CompartmentCheckTracer trc; JS_TracerInit(&trc, rt, CheckCompartmentCallback); - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { trc.zone = zone; for (size_t thingKind = 0; thingKind < FINALIZE_LAST; thingKind++) { for (CellIterUnderGC i(zone, AllocKind(thingKind)); !i.done(); i.next()) { @@ -2798,7 +2798,7 @@ BeginMarkPhase(JSRuntime *rt) rt->gcIsFull = true; bool any = false; - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { /* Assert that zone state is as we expect */ JS_ASSERT(!zone->isCollecting()); JS_ASSERT(!zone->compartments.empty()); @@ -2820,7 +2820,7 @@ BeginMarkPhase(JSRuntime *rt) zone->setPreservingCode(false); } - for (CompartmentsIter c(rt); !c.done(); c.next()) { + for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) { JS_ASSERT(!c->gcLiveArrayBuffers); c->marked = false; if (ShouldPreserveJITCode(c, currentTime)) @@ -2953,7 +2953,7 @@ BeginMarkPhase(JSRuntime *rt) */ /* Set the maybeAlive flag based on cross-compartment edges. */ - for (CompartmentsIter c(rt); !c.done(); c.next()) { + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { Cell *dst = e.front().key.wrapped; dst->tenuredZone()->maybeAlive = true; @@ -3293,7 +3293,7 @@ AssertNeedsBarrierFlagsConsistent(JSRuntime *rt) { #ifdef DEBUG bool anyNeedsBarrier = false; - for (ZonesIter zone(rt); !zone.done(); zone.next()) + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) anyNeedsBarrier |= zone->needsBarrier(); JS_ASSERT(rt->needsBarrier() == anyNeedsBarrier); #endif @@ -3307,7 +3307,7 @@ DropStringWrappers(JSRuntime *rt) * us to sweep the wrappers in all compartments every time we sweep a * compartment group. */ - for (CompartmentsIter c(rt); !c.done(); c.next()) { + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { if (e.front().key.kind == CrossCompartmentKey::StringWrapper) e.removeFront(); @@ -3856,7 +3856,7 @@ BeginSweepPhase(JSRuntime *rt, bool lastGC) #endif #ifdef DEBUG - for (CompartmentsIter c(rt); !c.done(); c.next()) { + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { JS_ASSERT(!c->gcIncomingGrayPointers); for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { if (e.front().key.kind != CrossCompartmentKey::StringWrapper) @@ -3966,7 +3966,7 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC) * newly created zones. Can only change from full to not full. */ if (rt->gcIsFull) { - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { if (!zone->isCollecting()) { rt->gcIsFull = false; break; @@ -3981,7 +3981,7 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC) * prevent the cycle collector from collecting some dead objects. */ if (rt->gcFoundBlackGrayEdges) { - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { if (!zone->isCollecting()) zone->allocator.arenas.unmarkAll(); } @@ -4057,7 +4057,7 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC) SweepZones(&fop, lastGC); } - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { zone->setGCLastBytes(zone->gcBytes, gckind); if (zone->isCollecting()) { JS_ASSERT(zone->isGCFinished()); @@ -4077,7 +4077,7 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC) } #ifdef DEBUG - for (CompartmentsIter c(rt); !c.done(); c.next()) { + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { JS_ASSERT(!c->gcIncomingGrayPointers); JS_ASSERT(!c->gcLiveArrayBuffers); @@ -4165,7 +4165,7 @@ AutoGCSession::~AutoGCSession() #endif /* Clear gcMallocBytes for all compartments */ - for (ZonesIter zone(runtime); !zone.done(); zone.next()) { + for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) { zone->resetGCMallocBytes(); zone->unscheduleGC(); } @@ -4173,19 +4173,39 @@ AutoGCSession::~AutoGCSession() runtime->resetGCMallocBytes(); } -AutoCopyFreeListToArenas::AutoCopyFreeListToArenas(JSRuntime *rt) - : runtime(rt) +AutoCopyFreeListToArenas::AutoCopyFreeListToArenas(JSRuntime *rt, ZoneSelector selector) + : runtime(rt), + selector(selector) { - for (ZonesIter zone(rt); !zone.done(); zone.next()) + for (ZonesIter zone(rt, selector); !zone.done(); zone.next()) zone->allocator.arenas.copyFreeListsToArenas(); } AutoCopyFreeListToArenas::~AutoCopyFreeListToArenas() { - for (ZonesIter zone(runtime); !zone.done(); zone.next()) + for (ZonesIter zone(runtime, selector); !zone.done(); zone.next()) zone->allocator.arenas.clearFreeListsInArenas(); } +class AutoCopyFreeListToArenasForGC +{ + JSRuntime *runtime; + + public: + AutoCopyFreeListToArenasForGC(JSRuntime *rt) : runtime(rt) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { + //if (zone->canCollect()) + zone->allocator.arenas.copyFreeListsToArenas(); + } + } + ~AutoCopyFreeListToArenasForGC() { + for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) { + //if (zone->canCollect()) + zone->allocator.arenas.clearFreeListsInArenas(); + } + } +}; + static void IncrementalCollectSlice(JSRuntime *rt, int64_t budget, @@ -4201,7 +4221,7 @@ ResetIncrementalGC(JSRuntime *rt, const char *reason) case MARK: { /* Cancel any ongoing marking. */ - AutoCopyFreeListToArenas copy(rt); + AutoCopyFreeListToArenasForGC copy(rt); rt->gcMarker.reset(); rt->gcMarker.stop(); @@ -4229,7 +4249,7 @@ ResetIncrementalGC(JSRuntime *rt, const char *reason) case SWEEP: rt->gcMarker.reset(); - for (ZonesIter zone(rt); !zone.done(); zone.next()) + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) zone->scheduledForDestruction = false; /* Finish sweeping the current zone group, then abort. */ @@ -4249,10 +4269,10 @@ ResetIncrementalGC(JSRuntime *rt, const char *reason) rt->gcStats.reset(reason); #ifdef DEBUG - for (CompartmentsIter c(rt); !c.done(); c.next()) + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) JS_ASSERT(!c->gcLiveArrayBuffers); - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { JS_ASSERT(!zone->needsBarrier()); for (unsigned i = 0; i < FINALIZE_LIMIT; ++i) JS_ASSERT(!zone->allocator.arenas.arenaListsToSweep[i]); @@ -4307,7 +4327,7 @@ AutoGCSlice::~AutoGCSlice() { /* We can't use GCZonesIter if this is the end of the last slice. */ bool haveBarriers = false; - for (ZonesIter zone(runtime); !zone.done(); zone.next()) { + for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) { if (zone->isGCMarking()) { zone->setNeedsBarrier(true, Zone::UpdateIon); zone->allocator.arenas.prepareForIncrementalGC(runtime); @@ -4339,7 +4359,7 @@ IncrementalCollectSlice(JSRuntime *rt, JS::gcreason::Reason reason, JSGCInvocationKind gckind) { - AutoCopyFreeListToArenas copy(rt); + AutoCopyFreeListToArenasForGC copy(rt); AutoGCSlice slice(rt); bool lastGC = (reason == JS::gcreason::DESTROY_RUNTIME); @@ -4504,7 +4524,7 @@ BudgetIncrementalGC(JSRuntime *rt, int64_t *budget) } bool reset = false; - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { if (zone->gcBytes >= zone->gcTriggerBytes) { *budget = SliceBudget::Unlimited; rt->gcStats.nonincremental("allocation trigger"); @@ -4682,7 +4702,7 @@ Collect(JSRuntime *rt, bool incremental, int64_t budget, int zoneCount = 0; int compartmentCount = 0; int collectedCount = 0; - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { if (rt->gcMode == JSGC_MODE_GLOBAL) zone->scheduleGC(); @@ -4695,7 +4715,7 @@ Collect(JSRuntime *rt, bool incremental, int64_t budget, collectedCount++; } - for (CompartmentsIter c(rt); !c.done(); c.next()) + for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) compartmentCount++; rt->gcShouldCleanUpEverything = ShouldCleanUpEverything(rt, reason, gckind); @@ -4767,7 +4787,7 @@ js::GCFinalSlice(JSRuntime *rt, JSGCInvocationKind gckind, JS::gcreason::Reason static bool ZonesSelected(JSRuntime *rt) { - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { if (zone->isGCScheduled()) return true; } @@ -4836,11 +4856,11 @@ AutoFinishGC::AutoFinishGC(JSRuntime *rt) gc::FinishBackgroundFinalize(rt); } -AutoPrepareForTracing::AutoPrepareForTracing(JSRuntime *rt) +AutoPrepareForTracing::AutoPrepareForTracing(JSRuntime *rt, ZoneSelector selector) : finish(rt), pause(rt), session(rt), - copy(rt) + copy(rt, selector) { RecordNativeStackTopForGC(rt); } @@ -4896,7 +4916,7 @@ void gc::MergeCompartments(JSCompartment *source, JSCompartment *target) { JSRuntime *rt = source->runtimeFromMainThread(); - AutoPrepareForTracing prepare(rt); + AutoPrepareForTracing prepare(rt, SkipAtoms); // Cleanup tables and other state in the source compartment that will be // meaningless after merging into the target compartment. @@ -5031,7 +5051,7 @@ void js::ReleaseAllJITCode(FreeOp *fop) { #ifdef JS_ION - for (ZonesIter zone(fop->runtime()); !zone.done(); zone.next()) { + for (ZonesIter zone(fop->runtime(), SkipAtoms); !zone.done(); zone.next()) { # ifdef DEBUG /* Assert no baseline scripts are marked as active. */ @@ -5059,7 +5079,7 @@ js::ReleaseAllJITCode(FreeOp *fop) } /* Sweep now invalidated compiler outputs from each compartment. */ - for (CompartmentsIter comp(fop->runtime()); !comp.done(); comp.next()) + for (CompartmentsIter comp(fop->runtime(), SkipAtoms); !comp.done(); comp.next()) comp->types.clearCompilerOutputs(fop); #endif } @@ -5134,7 +5154,7 @@ js::StopPCCountProfiling(JSContext *cx) if (!vec) return; - for (ZonesIter zone(rt); !zone.done(); zone.next()) { + for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { for (CellIter i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) { JSScript *script = i.get(); if (script->hasScriptCounts && script->types) { diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index b0cbd1c0466..53c549f8e73 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -306,7 +306,7 @@ class GCZonesIter ZonesIter zone; public: - GCZonesIter(JSRuntime *rt) : zone(rt) { + GCZonesIter(JSRuntime *rt) : zone(rt, WithAtoms) { if (!zone->isCollecting()) next(); } diff --git a/js/src/jswatchpoint.cpp b/js/src/jswatchpoint.cpp index 34078095f8a..0a3d42d9a68 100644 --- a/js/src/jswatchpoint.cpp +++ b/js/src/jswatchpoint.cpp @@ -229,7 +229,7 @@ void WatchpointMap::traceAll(WeakMapTracer *trc) { JSRuntime *rt = trc->runtime; - for (CompartmentsIter comp(rt); !comp.done(); comp.next()) { + for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) { if (WatchpointMap *wpmap = comp->watchpointMap) wpmap->trace(trc); } diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp index 86b5ee13b65..54b902e4860 100644 --- a/js/src/jsweakmap.cpp +++ b/js/src/jsweakmap.cpp @@ -56,7 +56,7 @@ void WeakMapBase::traceAllMappings(WeakMapTracer *tracer) { JSRuntime *rt = tracer->runtime; - for (CompartmentsIter c(rt); !c.done(); c.next()) { + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next) m->traceMappings(tracer); } @@ -440,3 +440,4 @@ js_InitWeakMapClass(JSContext *cx, HandleObject obj) return nullptr; return weakMapProto; } + diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 4ee83e3f261..72f120e9f06 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -885,7 +885,7 @@ js::NukeCrossCompartmentWrappers(JSContext* cx, // Iterate through scopes looking for system cross compartment wrappers // that point to an object that shares a global with obj. - for (CompartmentsIter c(rt); !c.done(); c.next()) { + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { if (!sourceFilter.match(c)) continue; @@ -992,7 +992,7 @@ js::RemapAllWrappersForObject(JSContext *cx, JSObject *oldTargetArg, if (!toTransplant.reserve(cx->runtime()->numCompartments)) return false; - for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) { + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) { // We found a wrapper. Remember and root it. toTransplant.infallibleAppend(WrapperValue(wp)); @@ -1017,7 +1017,7 @@ js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter, AutoWrapperVector toRecompute(cx); - for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) { + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { // Filter by source compartment. if (!sourceFilter.match(c)) continue; diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 79d0e177242..c5de1f2783c 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -1448,7 +1448,7 @@ Debugger::markAllIteratively(GCMarker *trc) * convoluted since the easiest way to find them is via their debuggees. */ JSRuntime *rt = trc->runtime; - for (CompartmentsIter c(rt); !c.done(); c.next()) { + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { GlobalObjectSet &debuggees = c->getDebuggees(); for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) { GlobalObject *global = e.front(); @@ -1950,7 +1950,7 @@ Debugger::addAllGlobalsAsDebuggees(JSContext *cx, unsigned argc, Value *vp) { THIS_DEBUGGER(cx, argc, vp, "addAllGlobalsAsDebuggees", args, dbg); AutoDebugModeGC dmgc(cx->runtime()); - for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) { + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { if (c == dbg->object->compartment() || c->options().invisibleToDebugger) continue; c->zone()->scheduledForDestruction = false; @@ -2629,7 +2629,7 @@ Debugger::findAllGlobals(JSContext *cx, unsigned argc, Value *vp) if (!result) return false; - for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) { + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { c->zone()->scheduledForDestruction = false; GlobalObject *global = c->maybeGlobal(); diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index bd3d7434cd5..7aea4c4c8cc 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -487,7 +487,7 @@ JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisit JS_ASSERT(totalArenaSize % gc::ArenaSize == 0); #endif - for (CompartmentsIter comp(rt); !comp.done(); comp.next()) + for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next()) comp->compartmentStats = nullptr; size_t numDirtyChunks = @@ -512,7 +512,7 @@ JS_PUBLIC_API(size_t) JS::SystemCompartmentCount(JSRuntime *rt) { size_t n = 0; - for (CompartmentsIter comp(rt); !comp.done(); comp.next()) { + for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next()) { if (comp->isSystem) ++n; } @@ -523,7 +523,7 @@ JS_PUBLIC_API(size_t) JS::UserCompartmentCount(JSRuntime *rt) { size_t n = 0; - for (CompartmentsIter comp(rt); !comp.done(); comp.next()) { + for (CompartmentsIter comp(rt, WithAtoms); !comp.done(); comp.next()) { if (!comp->isSystem) ++n; } diff --git a/js/src/vm/OldDebugAPI.cpp b/js/src/vm/OldDebugAPI.cpp index dd28fcd9df3..66dcc003d29 100644 --- a/js/src/vm/OldDebugAPI.cpp +++ b/js/src/vm/OldDebugAPI.cpp @@ -172,7 +172,7 @@ JS_SetDebugModeForAllCompartments(JSContext *cx, bool debug) { AutoDebugModeGC dmgc(cx->runtime()); - for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) { + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { // Ignore special compartments (atoms, JSD compartments) if (c->principals) { if (!c->setDebugModeFromC(cx, !!debug, dmgc)) diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 1bc0bd3974c..b8e6af73ab6 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -408,7 +408,7 @@ JSRuntime::~JSRuntime() sourceHook = nullptr; /* Off thread compilation and parsing depend on atoms still existing. */ - for (CompartmentsIter comp(this); !comp.done(); comp.next()) + for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next()) CancelOffThreadIonCompile(comp, nullptr); WaitForOffThreadParsingToFinish(this); @@ -421,7 +421,7 @@ JSRuntime::~JSRuntime() FinishCommonNames(this); /* Clear debugging state to remove GC roots. */ - for (CompartmentsIter comp(this); !comp.done(); comp.next()) { + for (CompartmentsIter comp(this, SkipAtoms); !comp.done(); comp.next()) { comp->clearTraps(defaultFreeOp()); if (WatchpointMap *wpmap = comp->watchpointMap) wpmap->clear(); @@ -713,7 +713,7 @@ JSRuntime::setGCMaxMallocBytes(size_t value) */ gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1; resetGCMallocBytes(); - for (ZonesIter zone(this); !zone.done(); zone.next()) + for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) zone->setGCMaxMallocBytes(value); } diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 5fbefcaaa48..e6fbe266cf9 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -3874,9 +3874,8 @@ nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame) NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!"); if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { - gfxRect bbox = nsSVGUtils::GetBBox(const_cast(aFrame)); - return nsLayoutUtils::RoundGfxRectToAppRect(bbox, - aFrame->PresContext()->AppUnitsPerCSSPixel()) - aFrame->GetPosition(); + // TODO: SVG needs to define what percentage translations resolve against. + return nsRect(); } return nsRect(nsPoint(0, 0), aFrame->GetSize()); @@ -3892,9 +3891,8 @@ nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame) nsRect result; if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { - gfxRect bbox = nsSVGUtils::GetBBox(const_cast(aFrame)); - return nsLayoutUtils::RoundGfxRectToAppRect(bbox, - aFrame->PresContext()->AppUnitsPerCSSPixel()) - aFrame->GetPosition(); + // TODO: SVG needs to define what percentage translations resolve against. + return result; } /* Iterate through the continuation list, unioning together all the @@ -3964,56 +3962,51 @@ nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame, * a distance, it's already computed for us! */ const nsStyleDisplay* display = aFrame->StyleDisplay(); - nsRect boundingRect; - if (aBoundsOverride) { - boundingRect = *aBoundsOverride; - } else if (display->mTransformOrigin[0].GetUnit() != eStyleUnit_Coord || - display->mTransformOrigin[1].GetUnit() != eStyleUnit_Coord) { - // GetFrameBoundsForTransform is expensive for SVG frames and we don't need - // it if the origin is coords (which it is by default for SVG). - boundingRect = nsDisplayTransform::GetFrameBoundsForTransform(aFrame); - } + nsRect boundingRect = (aBoundsOverride ? *aBoundsOverride : + nsDisplayTransform::GetFrameBoundsForTransform(aFrame)); /* Allows us to access named variables by index. */ - float coords[2]; - nscoord boundingOffsets[2] = {boundingRect.x, boundingRect.y}; - nscoord boundingDimensions[2] = {boundingRect.width, boundingRect.height}; - nscoord frameOffsets[2] = {aFrame->GetPosition().x, aFrame->GetPosition().y}; + float coords[3]; + const nscoord* dimensions[2] = + {&boundingRect.width, &boundingRect.height}; for (uint8_t index = 0; index < 2; ++index) { /* If the -moz-transform-origin specifies a percentage, take the percentage * of the size of the box. */ const nsStyleCoord &coord = display->mTransformOrigin[index]; - if (coord.GetUnit() == eStyleUnit_Percent) { + if (coord.GetUnit() == eStyleUnit_Calc) { + const nsStyleCoord::Calc *calc = coord.GetCalcValue(); coords[index] = - NSAppUnitsToFloatPixels(boundingDimensions[index], aAppUnitsPerPixel) * - coord.GetPercentValue() + - NSAppUnitsToFloatPixels(boundingOffsets[index], aAppUnitsPerPixel); + NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * + calc->mPercent + + NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel); + } else if (coord.GetUnit() == eStyleUnit_Percent) { + coords[index] = + NSAppUnitsToFloatPixels(*dimensions[index], aAppUnitsPerPixel) * + coord.GetPercentValue(); } else { - if (coord.GetUnit() == eStyleUnit_Calc) { - const nsStyleCoord::Calc *calc = coord.GetCalcValue(); - coords[index] = - NSAppUnitsToFloatPixels(boundingDimensions[index], aAppUnitsPerPixel) * - calc->mPercent + - NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel); - } else { - NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); - coords[index] = - NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel); - } - if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { - // values represent offsets from the origin of the SVG element's - // user space, not the top left of its border-box, so we must - // convert them to be relative to the border-box. - coords[index] -= NSAppUnitsToFloatPixels(frameOffsets[index], aAppUnitsPerPixel); - } + NS_ABORT_IF_FALSE(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); + coords[index] = + NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel); + } + if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) && + coord.GetUnit() != eStyleUnit_Percent) { + // values represent offsets from the origin of the SVG element's + // user space, not the top left of its bounds, so we must adjust for that: + nscoord offset = + (index == 0) ? aFrame->GetPosition().x : aFrame->GetPosition().y; + coords[index] -= NSAppUnitsToFloatPixels(offset, aAppUnitsPerPixel); } } - return gfxPoint3D(coords[0], coords[1], - NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(), - aAppUnitsPerPixel)); + coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(), + aAppUnitsPerPixel); + /* Adjust based on the origin of the rectangle. */ + coords[0] += NSAppUnitsToFloatPixels(boundingRect.x, aAppUnitsPerPixel); + coords[1] += NSAppUnitsToFloatPixels(boundingRect.y, aAppUnitsPerPixel); + + return gfxPoint3D(coords[0], coords[1], coords[2]); } /* Returns the delta specified by the -moz-perspective-origin property. @@ -4087,6 +4080,7 @@ nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(const nsI : mFrame(aFrame) , mTransformList(aFrame->StyleDisplay()->mSpecifiedTransform) , mToTransformOrigin(GetDeltaToTransformOrigin(aFrame, aAppUnitsPerPixel, aBoundsOverride)) + , mToPerspectiveOrigin(GetDeltaToPerspectiveOrigin(aFrame, aAppUnitsPerPixel)) , mChildPerspective(0) { const nsStyleDisplay* parentDisp = nullptr; @@ -4096,9 +4090,6 @@ nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(const nsI } if (parentDisp && parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord) { mChildPerspective = parentDisp->mChildPerspective.GetCoordValue(); - if (mChildPerspective > 0.0) { - mToPerspectiveOrigin = GetDeltaToPerspectiveOrigin(aFrame, aAppUnitsPerPixel); - } } } @@ -4198,7 +4189,7 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp /* At the point when perspective is applied, we have been translated to the transform origin. * The translation to the perspective origin is the difference between these values. */ - result = result * nsLayoutUtils::ChangeMatrixBasis(aProperties.GetToPerspectiveOrigin() - aProperties.mToTransformOrigin, perspective); + result = result * nsLayoutUtils::ChangeMatrixBasis(aProperties.mToPerspectiveOrigin - aProperties.mToTransformOrigin, perspective); } gfxPoint3D rounded(hasSVGTransforms ? newOrigin.x : NS_round(newOrigin.x), diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index d641be22e0e..927f6a1d47b 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -3061,24 +3061,15 @@ public: : mFrame(nullptr) , mTransformList(aTransformList) , mToTransformOrigin(aToTransformOrigin) - , mChildPerspective(aChildPerspective) , mToPerspectiveOrigin(aToPerspectiveOrigin) + , mChildPerspective(aChildPerspective) {} const nsIFrame* mFrame; const nsCSSValueList* mTransformList; const gfxPoint3D mToTransformOrigin; + const gfxPoint3D mToPerspectiveOrigin; nscoord mChildPerspective; - - const gfxPoint3D& GetToPerspectiveOrigin() const - { - NS_ASSERTION(mChildPerspective > 0, "Only valid with mChildPerspective > 0"); - return mToPerspectiveOrigin; - } - - private: - // mToPerspectiveOrigin is only valid if mChildPerspective > 0. - gfxPoint3D mToPerspectiveOrigin; }; /** diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 46812d2ae1d..d138713d48b 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1643,7 +1643,7 @@ fuzzy-if(Android&&AndroidVersion>=15,8,300) == 625409-1.html 625409-1-ref.html == 630835-1.html about:blank == 631352-1.html 631352-1-ref.html skip-if(B2G) fails-if(Android) == 632423-1.html 632423-1-ref.html -skip-if(Android||B2G) random-if(winWidget&&!d2d) == 632781-verybig.html 632781-ref.html +skip-if(Android||B2G) random-if(winWidget) == 632781-verybig.html 632781-ref.html == 632781-normalsize.html 632781-ref.html fails-if(Android) == 633344-1.html 633344-1-ref.html == 634232-1.html 634232-1-ref.html diff --git a/layout/reftests/transform/reftest.list b/layout/reftests/transform/reftest.list index b3ae5b21eb5..810dbd103e7 100644 --- a/layout/reftests/transform/reftest.list +++ b/layout/reftests/transform/reftest.list @@ -124,7 +124,3 @@ skip-if(B2G) == stresstest-1.html stresstest-1-ref.html # bug 773482 == table-2b.html table-2-ref.html # Bug 722463 == inline-1a.html inline-1-ref.html -== transform-origin-svg-1a.svg transform-origin-svg-1-ref.svg -== transform-origin-svg-1b.svg transform-origin-svg-1-ref.svg -== transform-origin-svg-2a.svg transform-origin-svg-2-ref.svg -== transform-origin-svg-2b.svg transform-origin-svg-2-ref.svg diff --git a/layout/reftests/transform/transform-origin-svg-1-ref.svg b/layout/reftests/transform/transform-origin-svg-1-ref.svg deleted file mode 100644 index 191ca90410c..00000000000 --- a/layout/reftests/transform/transform-origin-svg-1-ref.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/layout/reftests/transform/transform-origin-svg-1a.svg b/layout/reftests/transform/transform-origin-svg-1a.svg deleted file mode 100644 index da94ad39291..00000000000 --- a/layout/reftests/transform/transform-origin-svg-1a.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/layout/reftests/transform/transform-origin-svg-1b.svg b/layout/reftests/transform/transform-origin-svg-1b.svg deleted file mode 100644 index 4478318600e..00000000000 --- a/layout/reftests/transform/transform-origin-svg-1b.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/layout/reftests/transform/transform-origin-svg-2-ref.svg b/layout/reftests/transform/transform-origin-svg-2-ref.svg deleted file mode 100644 index 35d3da1408b..00000000000 --- a/layout/reftests/transform/transform-origin-svg-2-ref.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/layout/reftests/transform/transform-origin-svg-2a.svg b/layout/reftests/transform/transform-origin-svg-2a.svg deleted file mode 100644 index 05a0c829b97..00000000000 --- a/layout/reftests/transform/transform-origin-svg-2a.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/layout/reftests/transform/transform-origin-svg-2b.svg b/layout/reftests/transform/transform-origin-svg-2b.svg deleted file mode 100644 index 3ceca01c69a..00000000000 --- a/layout/reftests/transform/transform-origin-svg-2b.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index a4618870ecc..aad90fc2101 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1168,6 +1168,11 @@ InitSystemMetrics() sSystemMetrics->AppendElement(nsGkAtoms::windows_glass); } + rv = LookAndFeel::GetInt(LookAndFeel::eIntID_ColorPickerAvailable, &metricResult); + if (NS_SUCCEEDED(rv) && metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::color_picker_available); + } + rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsClassic, &metricResult); if (NS_SUCCEEDED(rv) && metricResult) { sSystemMetrics->AppendElement(nsGkAtoms::windows_classic); diff --git a/layout/style/nsMediaFeatures.cpp b/layout/style/nsMediaFeatures.cpp index af6942356d2..338b5b3f153 100644 --- a/layout/style/nsMediaFeatures.cpp +++ b/layout/style/nsMediaFeatures.cpp @@ -495,6 +495,13 @@ nsMediaFeatures::features[] = { { nullptr }, GetIsResourceDocument }, + { + &nsGkAtoms::_moz_color_picker_available, + nsMediaFeature::eMinMaxNotAllowed, + nsMediaFeature::eBoolInteger, + { &nsGkAtoms::color_picker_available }, + GetSystemMetric + }, { &nsGkAtoms::_moz_scrollbar_start_backward, nsMediaFeature::eMinMaxNotAllowed, diff --git a/layout/svg/nsSVGTextFrame2.cpp b/layout/svg/nsSVGTextFrame2.cpp index d9f6b988912..259baf6c695 100644 --- a/layout/svg/nsSVGTextFrame2.cpp +++ b/layout/svg/nsSVGTextFrame2.cpp @@ -3765,16 +3765,14 @@ nsSVGTextFrame2::ReflowSVG() nsSVGEffects::UpdateEffects(this); } - // Now unset the various reflow bits. Do this before calling - // FinishAndStoreOverflow since FinishAndStoreOverflow can require glyph - // positions (to resolve transform-origin). - mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | - NS_FRAME_HAS_DIRTY_CHILDREN); - nsRect overflow = nsRect(nsPoint(0,0), mRect.Size()); nsOverflowAreas overflowAreas(overflow, overflow); FinishAndStoreOverflow(overflowAreas, mRect.Size()); + // Now unset the various reflow bits: + mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | + NS_FRAME_HAS_DIRTY_CHILDREN); + // XXX nsSVGContainerFrame::ReflowSVG only looks at its nsISVGChildFrame // children, and calls ConsiderChildOverflow on them. Does it matter // that ConsiderChildOverflow won't be called on our children? @@ -3809,21 +3807,9 @@ nsSVGTextFrame2::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace, { NS_ASSERTION(GetFirstPrincipalChild(), "must have a child frame"); - SVGBBox bbox; - if (NS_SUBTREE_DIRTY(this)) { - // Return an empty bbox if this frame's subtree is dirty. This may be called - // in that situation, e.g. when we're building a display list after an - // interrupted reflow. This can also be called during reflow before we've - // been reflowed, e.g. if an earlier sibling is calling FinishAndStoreOverflow and - // needs our parent's perspective matrix, which depends on the SVG bbox - // contribution of this frame. In the latter situation, when all siblings have - // been reflowed, the parent will compute its perspective and rerun - // FinishAndStoreOverflow for all its children. - return bbox; - } - UpdateGlyphPositioning(); + SVGBBox bbox; nsPresContext* presContext = PresContext(); TextRenderedRunIterator it(this); diff --git a/layout/svg/svg.css b/layout/svg/svg.css index c85058d955d..154f3f9b477 100644 --- a/layout/svg/svg.css +++ b/layout/svg/svg.css @@ -51,7 +51,7 @@ foreignObject { text-indent: 0; } -/* Set |transform-origin:0 0;| for all SVG elements except outer-, +/* Set |transform-origin:0% 0%;| for all SVG elements except outer-, noting that 'svg' as a child of 'foreignObject' counts as outer-. */ *:not(svg), diff --git a/netwerk/base/public/nsISocketTransport.idl b/netwerk/base/public/nsISocketTransport.idl index 50173d338d6..f225f4e27d7 100644 --- a/netwerk/base/public/nsISocketTransport.idl +++ b/netwerk/base/public/nsISocketTransport.idl @@ -176,6 +176,12 @@ interface nsISocketTransport : nsITransport */ const unsigned long DISABLE_IPV4 = (1 << 4); + /** + * If set, indicates that the socket should not connect if the hostname + * resolves to an RFC1918 address or IPv6 equivalent. + */ + const unsigned long DISABLE_RFC1918 = (1 << 5); + /** * Socket QoS/ToS markings. Valid values are IPTOS_DSCP_AFxx or * IPTOS_CLASS_CSx (or IPTOS_DSCP_EF, but currently no supported diff --git a/netwerk/base/src/nsSocketTransport2.cpp b/netwerk/base/src/nsSocketTransport2.cpp index d09b46f2ed1..7e37bbbd726 100644 --- a/netwerk/base/src/nsSocketTransport2.cpp +++ b/netwerk/base/src/nsSocketTransport2.cpp @@ -1161,6 +1161,28 @@ nsSocketTransport::InitiateSocket() return NS_ERROR_OFFLINE; } + // Hosts/Proxy Hosts that are Local IP Literals should not be speculatively + // connected - Bug 853423. + if (mConnectionFlags & nsISocketTransport::DISABLE_RFC1918 && + IsIPAddrLocal(&mNetAddr)) { +#ifdef PR_LOGGING + if (SOCKET_LOG_ENABLED()) { + nsAutoCString netAddrCString; + netAddrCString.SetCapacity(kIPv6CStrBufSize); + if (!NetAddrToString(&mNetAddr, + netAddrCString.BeginWriting(), + kIPv6CStrBufSize)) + netAddrCString = NS_LITERAL_CSTRING(""); + SOCKET_LOG(("nsSocketTransport::InitiateSocket skipping " + "speculative connection for host [%s:%d] proxy " + "[%s:%d] with Local IP address [%s]", + mHost.get(), mPort, mProxyHost.get(), mProxyPort, + netAddrCString.get())); + } +#endif + return NS_ERROR_CONNECTION_REFUSED; + } + // // find out if it is going to be ok to attach another socket to the STS. // if not then we have to wait for the STS to tell us that it is ok. diff --git a/netwerk/dns/DNS.cpp b/netwerk/dns/DNS.cpp index cf1a284f506..f953e457dd6 100644 --- a/netwerk/dns/DNS.cpp +++ b/netwerk/dns/DNS.cpp @@ -177,6 +177,32 @@ bool IsIPAddrV4Mapped(const NetAddr *addr) return false; } +bool IsIPAddrLocal(const NetAddr *addr) +{ + MOZ_ASSERT(addr); + + // IPv4 RFC1918 and Link Local Addresses. + if (addr->raw.family == AF_INET) { + uint32_t addr32 = ntohl(addr->inet.ip); + if (addr32 >> 24 == 0x0A || // 10/8 prefix (RFC 1918). + addr32 >> 20 == 0xAC1 || // 172.16/12 prefix (RFC 1918). + addr32 >> 16 == 0xC0A8 || // 192.168/16 prefix (RFC 1918). + addr32 >> 16 == 0xA9FE) { // 169.254/16 prefix (Link Local). + return true; + } + } + // IPv6 Unique and Link Local Addresses. + if (addr->raw.family == AF_INET6) { + uint16_t addr16 = ntohs(addr->inet6.ip.u16[0]); + if (addr16 >> 9 == 0xfc >> 1 || // fc00::/7 Unique Local Address. + addr16 >> 6 == 0xfe80 >> 6) { // fe80::/10 Link Local Address. + return true; + } + } + // Not an IPv4/6 local address. + return false; +} + NetAddrElement::NetAddrElement(const PRNetAddr *prNetAddr) { PRNetAddrToNetAddr(prNetAddr, &mAddress); diff --git a/netwerk/dns/DNS.h b/netwerk/dns/DNS.h index 4fe64bafb84..e4f0b956b40 100644 --- a/netwerk/dns/DNS.h +++ b/netwerk/dns/DNS.h @@ -160,6 +160,8 @@ bool IsIPAddrAny(const NetAddr *addr); bool IsIPAddrV4Mapped(const NetAddr *addr); +bool IsIPAddrLocal(const NetAddr *addr); + } // namespace net } // namespace mozilla diff --git a/netwerk/protocol/http/nsHttpConnectionInfo.cpp b/netwerk/protocol/http/nsHttpConnectionInfo.cpp index 01c58ada531..42d1a793469 100644 --- a/netwerk/protocol/http/nsHttpConnectionInfo.cpp +++ b/netwerk/protocol/http/nsHttpConnectionInfo.cpp @@ -8,6 +8,10 @@ #include "HttpLog.h" #include "nsHttpConnectionInfo.h" +#include "mozilla/net/DNS.h" +#include "prnetdb.h" + +using namespace mozilla::net; nsHttpConnectionInfo::nsHttpConnectionInfo(const nsACString &host, int32_t port, nsProxyInfo* proxyInfo, @@ -113,3 +117,19 @@ nsHttpConnectionInfo::UsingProxy() return !mProxyInfo->IsDirect(); } +bool +nsHttpConnectionInfo::HostIsLocalIPLiteral() const +{ + PRNetAddr prAddr; + // If the host/proxy host is not an IP address literal, return false. + if (ProxyHost()) { + if (PR_StringToNetAddr(ProxyHost(), &prAddr) != PR_SUCCESS) { + return false; + } + } else if (PR_StringToNetAddr(Host(), &prAddr) != PR_SUCCESS) { + return false; + } + NetAddr netAddr; + PRNetAddrToNetAddr(&prAddr, &netAddr); + return IsIPAddrLocal(&netAddr); +} diff --git a/netwerk/protocol/http/nsHttpConnectionInfo.h b/netwerk/protocol/http/nsHttpConnectionInfo.h index 08584fb220c..128f94b2911 100644 --- a/netwerk/protocol/http/nsHttpConnectionInfo.h +++ b/netwerk/protocol/http/nsHttpConnectionInfo.h @@ -92,6 +92,9 @@ public: // Returns true for any kind of proxy (http, socks, etc..) bool UsingProxy(); + // Returns true when mHost is an RFC1918 literal. + bool HostIsLocalIPLiteral() const; + private: mozilla::ThreadSafeAutoRefCnt mRef; nsCString mHashKey; diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index 65c24906b93..05cd4c8ee39 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -357,6 +357,14 @@ nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci, LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n", ci->HashKey().get())); + // Hosts that are Local IP Literals should not be speculatively + // connected - Bug 853423. + if (ci && ci->HostIsLocalIPLiteral()) { + LOG(("nsHttpConnectionMgr::SpeculativeConnect skipping RFC1918 " + "address [%s]", ci->Host())); + return NS_OK; + } + nsRefPtr args = new SpeculativeConnectArgs(); // Wrap up the callbacks and the target to ensure they're released on the target @@ -1984,13 +1992,13 @@ nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent, MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); nsRefPtr sock = new nsHalfOpenSocket(ent, trans, caps); + if (speculative) + sock->SetSpeculative(true); nsresult rv = sock->SetupPrimaryStreams(); NS_ENSURE_SUCCESS(rv, rv); ent->mHalfOpens.AppendElement(sock); mNumHalfOpenConns++; - if (speculative) - sock->SetSpeculative(true); return NS_OK; } @@ -2711,6 +2719,10 @@ nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport, tmpFlags |= nsISocketTransport::DISABLE_IPV6; } + if (IsSpeculative()) { + tmpFlags |= nsISocketTransport::DISABLE_RFC1918; + } + socketTransport->SetConnectionFlags(tmpFlags); socketTransport->SetQoSBits(gHttpHandler->GetQoSBits()); diff --git a/netwerk/test/unit/test_speculative_connect.js b/netwerk/test/unit/test_speculative_connect.js index 3444762668b..89179ddf0b4 100644 --- a/netwerk/test/unit/test_speculative_connect.js +++ b/netwerk/test/unit/test_speculative_connect.js @@ -1,11 +1,89 @@ -const CC = Components.Constructor; +/* -*- Mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=4 sts=4 et sw=4 tw=80: */ +/* 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/. */ +const CC = Components.Constructor; const ServerSocket = CC("@mozilla.org/network/server-socket;1", "nsIServerSocket", "init"); - var serv; +var ios; +/** Example local IP addresses (literal IP address hostname). + * + * Note: for IPv6 Unique Local and Link Local, a wider range of addresses is + * set aside than those most commonly used. Technically, link local addresses + * include those beginning with fe80:: through febf::, although in practise + * only fe80:: is used. Necko code blocks speculative connections for the wider + * range; hence, this test considers that range too. + */ +var localIPv4Literals = + [ // IPv4 RFC1918 \ + "10.0.0.1", "10.10.10.10", "10.255.255.255", // 10/8 + "172.16.0.1", "172.23.172.12", "172.31.255.255", // 172.16/20 + "192.168.0.1", "192.168.192.168", "192.168.255.255", // 192.168/16 + // IPv4 Link Local + "169.254.0.1", "169.254.192.154", "169.254.255.255" // 169.254/16 + ]; +var localIPv6Literals = + [ // IPv6 Unique Local fc00::/7 + "fc00::1", "fdfe:dcba:9876:abcd:ef01:2345:6789:abcd", + "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + // IPv6 Link Local fe80::/10 + "fe80::1", "fe80::abcd:ef01:2345:6789", + "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff" + ]; +var localIPLiterals = localIPv4Literals.concat(localIPv6Literals); + +/** Example remote IP addresses + * + * Note: The test environment may not have external network access, so + * resolving hostnames may not be possible. Thus, literals are used here, and + * should be directly converted by the stub resolver to IP addresses. + */ +var remoteIPv4Literals = + [ "93.184.216.119", // example.com + "74.125.239.130", // google.com + "63.245.217.105", // mozilla.org + "173.252.110.27" // facebook.com + ]; + +var remoteIPv6Literals = + [ "2607:f8b0:4005:802::1009", // google.com + "2620:101:8008:5::2:1", // mozilla.org + "2a03:2880:2110:df07:face:b00c::1" // facebook.com + ]; + +var remoteIPLiterals = remoteIPv4Literals.concat(remoteIPv6Literals); + +/** Test function list and descriptions. + */ +var testList = + [ test_speculative_connect, + test_hostnames_resolving_to_local_addresses, + test_hostnames_resolving_to_remote_addresses, + test_proxies_with_local_addresses, + test_proxies_with_remote_addresses + ]; + +var testDescription = + [ "Expect pass with localhost", + "Expect failure with resolved local IPs", + "Expect success with resolved remote IPs", + "Expect failure for proxies with local IPs", + "Expect success for proxies with remote IPs" + ]; + +var testIdx = 0; +var hostIdx = 0; + + +/** TestServer + * + * Implements nsIServerSocket for test_speculative_connect. + */ function TestServer() { this.listener = ServerSocket(-1, true, -1); this.listener.asyncListen(this); @@ -16,25 +94,310 @@ TestServer.prototype = { if (iid.equals(Ci.nsIServerSocket) || iid.equals(Ci.nsISupports)) return this; - throw Components.results.NS_ERROR_NO_INTERFACE; + throw Cr.NS_ERROR_NO_INTERFACE; }, onSocketAccepted: function(socket, trans) { try { this.listener.close(); } catch(e) {} do_check_true(true); - do_test_finished(); + next_test(); }, onStopListening: function(socket) {} +}; + +/** TestOutputStreamCallback + * + * Implements nsIOutputStreamCallback for socket layer tests. + */ +function TestOutputStreamCallback(transport, hostname, proxied, expectSuccess, next) { + this.transport = transport; + this.hostname = hostname; + this.proxied = proxied; + this.expectSuccess = expectSuccess; + this.next = next; + this.dummyContent = "Dummy content"; } -function run_test() { - var ios = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); +TestOutputStreamCallback.prototype = { + QueryInterface: function(iid) { + if (iid.equals(Ci.nsIOutputStreamCallback) || + iid.equals(Ci.nsISupports)) + return this; + throw Cr.NS_ERROR_NO_INTERFACE; + }, + onOutputStreamReady: function(stream) { + do_check_neq(typeof(stream), undefined); + try { + stream.write(this.dummyContent, this.dummyContent.length); + } catch (e) { + // Spec Connect FAILED. + do_check_instanceof(e, Ci.nsIException); + if (this.expectSuccess) { + // We may expect success, but the address could be unreachable + // in the test environment, so expect errors. + if (this.proxied) { + do_check_true(e.result == Cr.NS_ERROR_NET_TIMEOUT || + e.result == Cr.NS_ERROR_PROXY_CONNECTION_REFUSED); + } else { + do_check_true(e.result == Cr.NS_ERROR_NET_TIMEOUT || + e.result == Cr.NS_ERROR_CONNECTION_REFUSED); + } + } else { + // A refusal to connect speculatively should throw an error. + do_check_eq(e.result, Cr.NS_ERROR_CONNECTION_REFUSED); + } + this.transport.close(Cr.NS_BINDING_ABORTED); + this.next(); + return; + } + // Spec Connect SUCCEEDED. + if (this.expectSuccess) { + do_check_true(true, "Success for " + this.hostname); + } else { + do_throw("Speculative Connect should have failed for " + + this.hostname); + } + this.transport.close(Cr.NS_BINDING_ABORTED); + this.next(); + } +}; +/** test_speculative_connect + * + * Tests a basic positive case using nsIOService.SpeculativeConnect: + * connecting to localhost. + */ +function test_speculative_connect() { serv = new TestServer(); - URI = ios.newURI("http://localhost:" + serv.listener.port + "/just/a/test", null, null); - ios.QueryInterface(Components.interfaces.nsISpeculativeConnect) + var URI = ios.newURI("http://localhost:" + serv.listener.port + "/just/a/test", null, null); + ios.QueryInterface(Ci.nsISpeculativeConnect) .speculativeConnect(URI, null); - do_test_pending(); +} + +/* Speculative connections should not be allowed for hosts with local IP + * addresses (Bug 853423). That list includes: + * -- IPv4 RFC1918 and Link Local Addresses. + * -- IPv6 Unique and Link Local Addresses. + * + * Two tests are required: + * 1. Verify IP Literals passed to the SpeculativeConnect API. + * 2. Verify hostnames that need to be resolved at the socket layer. + */ + +/** test_hostnames_resolving_to_addresses + * + * Common test function for resolved hostnames. Takes a list of hosts, a + * boolean to determine if the test is expected to succeed or fail, and a + * function to call the next test case. + */ +function test_hostnames_resolving_to_addresses(host, expectSuccess, next) { + do_print(host); + var sts = Cc["@mozilla.org/network/socket-transport-service;1"] + .getService(Ci.nsISocketTransportService); + do_check_neq(typeof(sts), undefined); + var transport = sts.createTransport(null, 0, host, 80, null); + do_check_neq(typeof(transport), undefined); + + transport.connectionFlags = Ci.nsISocketTransport.DISABLE_RFC1918; + transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 1); + transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_READ_WRITE, 1); + do_check_eq(1, transport.getTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT)); + + var outStream = transport.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED,0,0); + do_check_neq(typeof(outStream), undefined); + + var callback = new TestOutputStreamCallback(transport, host, false, + expectSuccess, + next); + do_check_neq(typeof(callback), undefined); + + // Need to get main thread pointer to ensure nsSocketTransport::AsyncWait + // adds callback to nsOutputStreamReadyEvent on main thread, and doesn't + // addref off the main thread. + var gThreadManager = Cc["@mozilla.org/thread-manager;1"] + .getService(Ci.nsIThreadManager); + var mainThread = gThreadManager.currentThread; + + try { + outStream.QueryInterface(Ci.nsIAsyncOutputStream) + .asyncWait(callback, 0, 0, mainThread); + } catch (e) { + do_throw("asyncWait should not fail!"); + } +} + +/** + * test_hostnames_resolving_to_local_addresses + * + * Creates an nsISocketTransport and simulates a speculative connect request + * for a hostname that resolves to a local IP address. + * Runs asynchronously; on test success (i.e. failure to connect), the callback + * will call this function again until all hostnames in the test list are done. + * + * Note: This test also uses an IP literal for the hostname. This should be ok, + * as the socket layer will ask for the hostname to be resolved anyway, and DNS + * code should return a numerical version of the address internally. + */ +function test_hostnames_resolving_to_local_addresses() { + if (hostIdx >= localIPLiterals.length) { + // No more local IP addresses; move on. + next_test(); + return; + } + var host = localIPLiterals[hostIdx++]; + // Test another local IP address when the current one is done. + var next = test_hostnames_resolving_to_local_addresses; + test_hostnames_resolving_to_addresses(host, false, next); +} + +/** + * test_hostnames_resolving_to_remote_addresses + * + * Creates an nsISocketTransport and simulates a speculative connect request + * for a hostname that resolves to a local IP address. + * Runs asynchronously; on test success (i.e. failure to connect), the callback + * will call this function again until all hostnames in the test list are done. + * + * Note: This test also uses an IP literal for the hostname. This should be ok, + * as the socket layer will ask for the hostname to be resolved anyway, and DNS + * code should return a numerical version of the address internally. + */ +function test_hostnames_resolving_to_remote_addresses() { + if (hostIdx >= remoteIPLiterals.length) { + // No more remote IP addresses; move on. + next_test(); + return; + } + var host = remoteIPLiterals[hostIdx++]; + // Test another remote IP address when the current one is done. + var next = test_hostnames_resolving_to_remote_addresses; + test_hostnames_resolving_to_addresses(host, true, next); +} + +/** test_speculative_connect_with_host_list + * + * Common test function for resolved proxy hosts. Takes a list of hosts, a + * boolean to determine if the test is expected to succeed or fail, and a + * function to call the next test case. + */ +function test_proxies(proxyHost, expectSuccess, next) { + do_print("Proxy: " + proxyHost); + var sts = Cc["@mozilla.org/network/socket-transport-service;1"] + .getService(Ci.nsISocketTransportService); + do_check_neq(typeof(sts), undefined); + var pps = Cc["@mozilla.org/network/protocol-proxy-service;1"] + .getService(); + do_check_neq(typeof(pps), undefined); + + var proxyInfo = pps.newProxyInfo("http", proxyHost, 8080, 0, 1, null); + do_check_neq(typeof(proxyInfo), undefined); + + var transport = sts.createTransport(null, 0, "dummyHost", 80, proxyInfo); + do_check_neq(typeof(transport), undefined); + + transport.connectionFlags = Ci.nsISocketTransport.DISABLE_RFC1918; + + transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 1); + do_check_eq(1, transport.getTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT)); + transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_READ_WRITE, 1); + + var outStream = transport.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED,0,0); + do_check_neq(typeof(outStream), undefined); + + var callback = new TestOutputStreamCallback(transport, proxyHost, true, + expectSuccess, + next); + do_check_neq(typeof(callback), undefined); + + // Need to get main thread pointer to ensure nsSocketTransport::AsyncWait + // adds callback to nsOutputStreamReadyEvent on main thread, and doesn't + // addref off the main thread. + var gThreadManager = Cc["@mozilla.org/thread-manager;1"] + .getService(Ci.nsIThreadManager); + var mainThread = gThreadManager.currentThread; + + try { + outStream.QueryInterface(Ci.nsIAsyncOutputStream) + .asyncWait(callback, 0, 0, mainThread); + } catch (e) { + do_throw("asyncWait should not fail!"); + } +} + +/** + * test_proxies_with_local_addresses + * + * Creates an nsISocketTransport and simulates a speculative connect request + * for a proxy that resolves to a local IP address. + * Runs asynchronously; on test success (i.e. failure to connect), the callback + * will call this function again until all proxies in the test list are done. + * + * Note: This test also uses an IP literal for the proxy. This should be ok, + * as the socket layer will ask for the proxy to be resolved anyway, and DNS + * code should return a numerical version of the address internally. + */ +function test_proxies_with_local_addresses() { + if (hostIdx >= localIPLiterals.length) { + // No more local IP addresses; move on. + next_test(); + return; + } + var host = localIPLiterals[hostIdx++]; + // Test another local IP address when the current one is done. + var next = test_proxies_with_local_addresses; + test_proxies(host, false, next); +} + +/** + * test_proxies_with_remote_addresses + * + * Creates an nsISocketTransport and simulates a speculative connect request + * for a proxy that resolves to a local IP address. + * Runs asynchronously; on test success (i.e. failure to connect), the callback + * will call this function again until all proxies in the test list are done. + * + * Note: This test also uses an IP literal for the proxy. This should be ok, + * as the socket layer will ask for the proxy to be resolved anyway, and DNS + * code should return a numerical version of the address internally. + */ +function test_proxies_with_remote_addresses() { + if (hostIdx >= remoteIPLiterals.length) { + // No more local IP addresses; move on. + next_test(); + return; + } + var host = remoteIPLiterals[hostIdx++]; + // Test another local IP address when the current one is done. + var next = test_proxies_with_remote_addresses; + test_proxies(host, true, next); +} + +/** next_test + * + * Calls the next test in testList. Each test is responsible for calling this + * function when its test cases are complete. + */ +function next_test() { + if (testIdx >= testList.length) { + // No more tests; we're done. + do_test_finished(); + return; + } + do_print("SpeculativeConnect: " + testDescription[testIdx]); + hostIdx = 0; + // Start next test in list. + testList[testIdx++](); +} + +/** run_test + * + * Main entry function for test execution. + */ +function run_test() { + ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + + do_test_pending(); + next_test(); } diff --git a/testing/marionette/client/marionette/tests/unit/test_screenshot.py b/testing/marionette/client/marionette/tests/unit/test_screenshot.py index c693a34a905..b5e8d61afb7 100644 --- a/testing/marionette/client/marionette/tests/unit/test_screenshot.py +++ b/testing/marionette/client/marionette/tests/unit/test_screenshot.py @@ -7,19 +7,19 @@ class ScreenshotTests(MarionetteTestCase): test_url = self.marionette.absolute_url('html5Page.html') self.marionette.navigate(test_url) el = self.marionette.find_element('id', 'red') - self.assertEqual('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAVUlEQVRoge3PsQ0AIAzAsI78fzBwBhHykD2ePev80LweAAGJB1ILpBZILZBaILVAaoHUAqkFUgukFkgtkFogtUBqgdQCqQVSC6QWSC2QWiC1QGp9A7ma+7nyXgOpzQAAAABJRU5ErkJggg==', + self.assertEqual('iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAVUlEQVRoge3PsQ0AIAzAsI78fzBwBhHykD2ePev80LweAAGJB1ILpBZILZBaILVAaoHUAqkFUgukFkgtkFogtUBqgdQCqQVSC6QWSC2QWiC1QGp9A7ma+7nyXgOpzQAAAABJRU5ErkJggg==', self.marionette.screenshot(element=el)) def testWeCanTakeAScreenShotWithHighlightOfAnElement(self): test_url = self.marionette.absolute_url('html5Page.html') self.marionette.navigate(test_url) el = self.marionette.find_element('id', 'green') - self.assertEqual('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAV0lEQVRoge3PQRGAQAwAsWINvXgsNnI3+4iAzM7sDWZn9vneoxXRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNHcF7nBD/Ha5Ye4BbsYAAAAAElFTkSuQmCC', + self.assertEqual('iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAV0lEQVRoge3PQRGAQAwAsWINvXgsNnI3+4iAzM7sDWZn9vneoxXRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNHcF7nBD/Ha5Ye4BbsYAAAAAElFTkSuQmCC', self.marionette.screenshot(element=el, highlights=[el])) def testWeCanTakeAScreenShotEntireCanvas(self): test_url = self.marionette.absolute_url('html5Page.html') self.marionette.navigate(test_url) - self.assertTrue('data:image/png;base64,iVBORw0KGgo' in + self.assertTrue('iVBORw0KGgo' in self.marionette.screenshot()) diff --git a/testing/marionette/marionette-listener.js b/testing/marionette/marionette-listener.js index 9bc9a8560ec..ec2a9334cce 100644 --- a/testing/marionette/marionette-listener.js +++ b/testing/marionette/marionette-listener.js @@ -1937,7 +1937,8 @@ function screenShot(msg) { // Return the Base64 String back to the client bindings and they can manage // saving the file to disk if it is required - sendResponse({value:canvas.toDataURL("image/png","")}, msg.json.command_id); + var data_url = canvas.toDataURL("image/png",""); + sendResponse({value: data_url.substring(data_url.indexOf(",") + 1)}, msg.json.command_id); } //call register self when we get loaded diff --git a/widget/LookAndFeel.h b/widget/LookAndFeel.h index 08de0709aeb..c3139bb95e4 100644 --- a/widget/LookAndFeel.h +++ b/widget/LookAndFeel.h @@ -356,6 +356,15 @@ public: */ eIntID_SwipeAnimationEnabled, + /* + * A Boolean value to determine whether we have a color picker available + * for