This commit is contained in:
Ryan VanderMeulen 2014-10-28 16:10:05 -04:00
Родитель 7de5530079 a84ceb9d0b
Коммит 10b87c9f24
71 изменённых файлов: 2972 добавлений и 1012 удалений

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

@ -600,12 +600,14 @@ finalizeCB(GObject *aObj)
const gchar*
getNameCB(AtkObject* aAtkObj)
{
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
if (!accWrap)
return nullptr;
nsAutoString name;
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
if (accWrap)
accWrap->Name(name);
else if (ProxyAccessible* proxy = GetProxy(aAtkObj))
proxy->Name(name);
else
return nullptr;
// XXX Firing an event from here does not seem right
MaybeFireNameChange(aAtkObj, name);
@ -642,13 +644,18 @@ MaybeFireNameChange(AtkObject* aAtkObj, const nsString& aNewName)
const gchar *
getDescriptionCB(AtkObject *aAtkObj)
{
nsAutoString uniDesc;
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
if (!accWrap || accWrap->IsDefunct())
if (accWrap) {
if (accWrap->IsDefunct())
return nullptr;
/* nsIAccessible is responsible for the nonnull description */
nsAutoString uniDesc;
accWrap->Description(uniDesc);
} else if (ProxyAccessible* proxy = GetProxy(aAtkObj)) {
proxy->Description(uniDesc);
} else {
return nullptr;
}
NS_ConvertUTF8toUTF16 objDesc(aAtkObj->description);
if (!uniDesc.Equals(objDesc))
@ -782,19 +789,21 @@ GetLocaleCB(AtkObject* aAtkObj)
AtkObject *
getParentCB(AtkObject *aAtkObj)
{
if (!aAtkObj->accessible_parent) {
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
if (!accWrap)
return nullptr;
if (aAtkObj->accessible_parent)
return aAtkObj->accessible_parent;
Accessible* accParent = accWrap->Parent();
if (!accParent)
return nullptr;
AtkObject* parent = AccessibleWrap::GetAtkObject(accParent);
if (parent)
atk_object_set_parent(aAtkObj, parent);
AtkObject* atkParent = nullptr;
if (AccessibleWrap* wrapper = GetAccessibleWrap(aAtkObj)) {
Accessible* parent = wrapper->Parent();
atkParent = parent ? AccessibleWrap::GetAtkObject(parent) : nullptr;
} else if (ProxyAccessible* proxy = GetProxy(aAtkObj)) {
ProxyAccessible* parent = proxy->Parent();
atkParent = parent ? GetWrapperFor(parent) : nullptr;
}
if (atkParent)
atk_object_set_parent(aAtkObj, atkParent);
return aAtkObj->accessible_parent;
}
@ -982,6 +991,12 @@ GetProxy(AtkObject* aObj)
& ~IS_PROXY);
}
AtkObject*
GetWrapperFor(ProxyAccessible* aProxy)
{
return reinterpret_cast<AtkObject*>(aProxy->GetWrapper() & ~IS_PROXY);
}
static uint16_t
GetInterfacesForProxy(ProxyAccessible* aProxy)
{

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

@ -36,6 +36,7 @@ GType mai_atk_object_get_type(void);
GType mai_util_get_type();
mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj);
AtkObject* GetWrapperFor(mozilla::a11y::ProxyAccessible* aProxy);
extern int atkMajorVersion, atkMinorVersion;

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

@ -18,9 +18,7 @@ SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree)
uint32_t role = aRoot->Role();
uint32_t childCount = aRoot->ChildCount();
nsString name;
aRoot->Name(name);
aTree.AppendElement(AccessibleData(id, role, childCount, name));
aTree.AppendElement(AccessibleData(id, role, childCount));
for (uint32_t i = 0; i < childCount; i++)
SerializeTree(aRoot->GetChildAt(i), aTree);
}
@ -50,5 +48,27 @@ DocAccessibleChild::RecvState(const uint64_t& aID, uint64_t* aState)
return true;
}
bool
DocAccessibleChild::RecvName(const uint64_t& aID, nsString* aName)
{
Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID);
if (!acc)
return true;
acc->Name(*aName);
return true;
}
bool
DocAccessibleChild::RecvDescription(const uint64_t& aID, nsString* aDesc)
{
Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID);
if (!acc)
return true;
acc->Description(*aDesc);
return true;
}
}
}

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

@ -38,6 +38,16 @@ public:
*/
virtual bool RecvState(const uint64_t& aID, uint64_t* aState) MOZ_OVERRIDE;
/*
* Get the name for the accessible with given id.
*/
virtual bool RecvName(const uint64_t& aID, nsString* aName) MOZ_OVERRIDE;
/*
* Get the description for the accessible with given id.
*/
virtual bool RecvDescription(const uint64_t& aID, nsString* aDesc) MOZ_OVERRIDE;
private:
DocAccessible* mDoc;
};

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

@ -43,10 +43,12 @@ DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
MOZ_ASSERT(consumed == aData.NewTree().Length());
#ifdef DEBUG
for (uint32_t i = 0; i < consumed; i++) {
uint64_t id = aData.NewTree()[i].ID();
MOZ_ASSERT(mAccessibles.GetEntry(id));
}
#endif
return consumed;
}
@ -69,7 +71,7 @@ DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
auto role = static_cast<a11y::role>(newChild.Role());
ProxyAccessible* newProxy =
new ProxyAccessible(newChild.ID(), aParent, this, role, newChild.Name());
new ProxyAccessible(newChild.ID(), aParent, this, role);
aParent->AddChildAt(aIdxInParent, newProxy);
mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
ProxyCreated(newProxy);

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

@ -14,7 +14,6 @@ struct AccessibleData
uint64_t ID;
uint32_t Role;
uint32_t ChildrenCount;
nsString Name;
};
struct ShowEventData
@ -41,6 +40,8 @@ parent:
child:
prio(high) sync State(uint64_t aID) returns(uint64_t states);
prio(high) sync Name(uint64_t aID) returns(nsString name);
prio(high) sync Description(uint64_t aID) returns(nsString desc);
};
}

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

@ -47,5 +47,17 @@ ProxyAccessible::State() const
unused << mDoc->SendState(mID, &state);
return state;
}
void
ProxyAccessible::Name(nsString& aName) const
{
unused << mDoc->SendName(mID, &aName);
}
void
ProxyAccessible::Description(nsString& aDesc) const
{
unused << mDoc->SendDescription(mID, &aDesc);
}
}
}

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

@ -21,9 +21,8 @@ class ProxyAccessible
public:
ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
DocAccessibleParent* aDoc, role aRole,
const nsString& aName) :
mParent(aParent), mDoc(aDoc), mID(aID), mRole(aRole), mOuterDoc(false), mName(aName)
DocAccessibleParent* aDoc, role aRole) :
mParent(aParent), mDoc(aDoc), mID(aID), mRole(aRole), mOuterDoc(false)
{
MOZ_COUNT_CTOR(ProxyAccessible);
}
@ -59,6 +58,16 @@ public:
*/
uint64_t State() const;
/*
* Set aName to the name of the proxied accessible.
*/
void Name(nsString& aName) const;
/**
* Set aDesc to the description of the proxied accessible.
*/
void Description(nsString& aDesc) const;
/**
* Allow the platform to store a pointers worth of data on us.
*/
@ -85,7 +94,6 @@ private:
uint64_t mID;
role mRole : 31;
bool mOuterDoc : 1;
nsString mName;
};
}

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

@ -11,16 +11,12 @@
"linkedin.com": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
// bug 826353, itau.com.br
"itau.com.br": "\\(Mobile#(Android; Mobile",
// bug 826504, orkut.com.br
"orkut.com.br": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
// bug 826510, r7.com
"r7.com": "\\(Mobile#(Android; Mobile",
// bug 826514, estadao.com.br
"estadao.com.br": "\\(Mobile#(Android; Mobile",
// bug 826711, bb.com.br
"bb.com.br": "\\(Mobile#(Android; Mobile",
// bug 826712, orkut.com
"orkut.com": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
// bug 827622, bing.com
"bing.com": "\\(Mobile#(Android; Mobile",
// bug 827626, magazineluiza.com.br
@ -39,8 +35,6 @@
"marca.com": "\\(Mobile#(Android; Mobile",
// bug 828371, ingbank.pl
"ingbank.pl": "\\(Mobile#(Android; Mobile",
// bug 828399, antena3.com
"antena3.com": "\\(Mobile#(Android; Mobile",
// bug 828416, loteriasyapuestas.es
"loteriasyapuestas.es": "\\(Mobile#(Android; Mobile",
// bug 828418, bbva.es
@ -49,8 +43,6 @@
"publico.es": "\\(Mobile#(Android; Mobile",
// bug 828439, movistar.com.ve
"movistar.com.ve": "\\(Mobile#(Android; Mobile",
// bug 843126, es.playstation.com
"es.playstation.com": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
// bug 843129, 11870.com
"iphonejuegosgratis.com": "\\(Mobile#(Android; Mobile",
// bug 843132, comunio.es

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

@ -8,6 +8,7 @@ support-files =
doc_theme.css
[browser_devtools_api.js]
skip-if = e10s # Bug 1090340
[browser_devtools_api_destroy.js]
skip-if = e10s # Bug 1070837 - devtools/framework/toolbox.js |doc| getter not e10s friendly
[browser_dynamic_tool_enabling.js]

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

@ -95,6 +95,7 @@ skip-if = (os == "win" && debug) || e10s # bug 963492: win. bug 1040653: e10s.
[browser_ruleview_override.js]
[browser_ruleview_pseudo-element_01.js]
[browser_ruleview_pseudo-element_02.js]
skip-if = e10s # Bug 1090340
[browser_ruleview_refresh-on-attribute-change_01.js]
[browser_ruleview_refresh-on-attribute-change_02.js]
[browser_ruleview_refresh-on-style-change.js]

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

@ -419,8 +419,12 @@ endif # FAIL_ON_WARNINGS_DEBUG
# Check for normal version of flag, and add WARNINGS_AS_ERRORS if it's set to 1.
ifdef FAIL_ON_WARNINGS
# Never treat warnings as errors in clang-cl, because it warns about many more
# things than MSVC does.
ifndef CLANG_CL
CXXFLAGS += $(WARNINGS_AS_ERRORS)
CFLAGS += $(WARNINGS_AS_ERRORS)
endif # CLANG_CL
endif # FAIL_ON_WARNINGS
ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_)

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

@ -9356,13 +9356,23 @@ nsGlobalWindow::UpdateCommands(const nsAString& anAction, nsISelection* aSel, in
SelectionChangeEventInit init;
init.mBubbles = true;
if (aSel) {
nsCOMPtr<nsIDOMRange> range;
nsresult rv = aSel->GetRangeAt(0, getter_AddRefs(range));
if (NS_SUCCEEDED(rv) && range) {
nsRefPtr<nsRange> nsrange = static_cast<nsRange*>(range.get());
init.mBoundingClientRect = nsrange->GetBoundingClientRect(true, false);
range->ToString(init.mSelectedText);
Selection* selection = static_cast<Selection*>(aSel);
int32_t rangeCount = selection->GetRangeCount();
nsLayoutUtils::RectAccumulator accumulator;
for (int32_t idx = 0; idx < rangeCount; ++idx) {
nsRange* range = selection->GetRangeAt(idx);
nsRange::CollectClientRects(&accumulator, range,
range->GetStartParent(), range->StartOffset(),
range->GetEndParent(), range->EndOffset(),
true, false);
}
nsRect rect = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
accumulator.mResultRect;
nsRefPtr<DOMRect> domRect = new DOMRect(ToSupports(this));
domRect->SetLayoutRect(rect);
init.mBoundingClientRect = domRect;
selection->Stringify(init.mSelectedText);
for (uint32_t reasonType = 0;
reasonType < static_cast<uint32_t>(SelectionChangeReason::EndGuard_);
++reasonType) {
@ -9372,7 +9382,6 @@ nsGlobalWindow::UpdateCommands(const nsAString& anAction, nsISelection* aSel, in
init.mReasons.AppendElement(strongReasonType);
}
}
}
nsRefPtr<SelectionChangeEvent> event =
SelectionChangeEvent::Constructor(mDoc, NS_LITERAL_STRING("mozselectionchange"), init);

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

@ -415,6 +415,7 @@ skip-if = e10s # Bug 1081453 - shutdown leaks with e10s and WebIDL dom::File
[test_bug380418.html]
[test_bug380418.html^headers^]
[test_bug382113.html]
skip-if == (os == 'mac' || os == 'win') && debug # bug 453969
[test_bug382871.html]
[test_bug384003.xhtml]
[test_bug390219.html]

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

@ -216,7 +216,7 @@ skip-if = (toolkit == 'gonk' && debug) #bug 1045153
[test_canvas.html]
skip-if = (toolkit == 'gonk' && debug) #debug-only crash; bug 933541
[test_canvas_focusring.html]
skip-if = (toolkit == 'gonk' && !debug) #specialpowers.wrap
skip-if = (toolkit == 'gonk' && !debug) || (os == 'win' && debug) #specialpowers.wrap
[test_canvas_font_setter.html]
[test_canvas_path.html]
[test_hitregion_canvas.html]

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

@ -679,9 +679,12 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
iter->GetNext(getter_AddRefs(tmp));
nsCOMPtr<nsIDOMFile> domFile = do_QueryInterface(tmp);
MOZ_ASSERT(domFile);
NS_WARN_IF_FALSE(domFile,
"Null file object from FilePicker's file enumerator?");
if (domFile) {
newFiles.AppendElement(static_cast<File*>(domFile.get()));
}
}
} else {
MOZ_ASSERT(mode == static_cast<int16_t>(nsIFilePicker::modeOpen));
nsCOMPtr<nsIDOMFile> domFile;

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

@ -9,7 +9,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=885996
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style> label { display: block } </style>
<script type="application/javascript;version=1.8">
/** Test the behaviour of the <input type='color'> when clicking on it from

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

@ -304,8 +304,8 @@ TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, mInnerSize);
uint32_t presShellId;
mozilla::layers::FrameMetrics::ViewID viewId;
uint32_t presShellId = 0;
mozilla::layers::FrameMetrics::ViewID viewId = FrameMetrics::NULL_SCROLL_ID;
bool scrollIdentifiersValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
document->GetDocumentElement(), &presShellId, &viewId);
if (scrollIdentifiersValid) {

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

@ -688,14 +688,14 @@ RtspMediaResource::OnConnected(uint8_t aTrackIdx,
NS_DispatchToMainThread(event);
return NS_ERROR_FAILURE;
}
uint64_t duration = 0;
uint64_t durationUs = 0;
for (int i = 0; i < tracks; ++i) {
nsCString rtspTrackId("RtspTrack");
rtspTrackId.AppendInt(i);
nsCOMPtr<nsIStreamingProtocolMetaData> trackMeta;
mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta));
MOZ_ASSERT(trackMeta);
trackMeta->GetDuration(&duration);
trackMeta->GetDuration(&durationUs);
// Here is a heuristic to estimate the slot size.
// For video track, calculate the width*height.
@ -716,12 +716,12 @@ RtspMediaResource::OnConnected(uint8_t aTrackIdx,
return NS_ERROR_FAILURE;
}
// If the duration is 0, imply the stream is live stream.
if (duration) {
// If the durationUs is 0, imply the stream is live stream.
if (durationUs) {
// Not live stream.
mRealTime = false;
mDecoder->SetInfinite(false);
mDecoder->SetDuration(duration);
mDecoder->SetDuration((double)(durationUs) / USECS_PER_S);
} else {
// Live stream.
// Check the preference "media.realtime_decoder.enabled".

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

@ -9,6 +9,7 @@
#include "MediaResource.h"
#include "mozilla/Monitor.h"
#include "nsITimer.h"
#include "VideoUtils.h"
namespace mozilla {

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

@ -368,7 +368,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1
[test_eme_stream_capture_blocked.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
[test_error_in_video_document.html]
skip-if = toolkit == 'android' # bug 608634
skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
[test_error_on_404.html]
[test_fastSeek.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439

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

@ -0,0 +1,2 @@
<!DOCTYPE html>
<textarea placeholder="placeholder"></textarea>

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

@ -0,0 +1,8 @@
<!DOCTYPE html>
<script>
onload = function() {
var t = document.createElement('textarea');
t.placeholder = "placeholder";
document.body.appendChild(t.cloneNode(true));
}
</script>

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

@ -127,3 +127,4 @@ needs-focus == spellcheck-contenteditable-focused-reframe.html spellcheck-conten
== spellcheck-contenteditable-property-dynamic-override-inherit.html spellcheck-contenteditable-disabled-ref.html
needs-focus == 969773.html 969773-ref.html
== 997805.html 997805-ref.html
== 1088158.html 1088158-ref.html

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

@ -510,6 +510,7 @@ NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions)
DrawTargetCairo::DrawTargetCairo()
: mContext(nullptr)
, mSurface(nullptr)
, mLockedBits(nullptr)
{
}

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

@ -36,22 +36,16 @@ using namespace mozilla::gfx;
UserDataKey gfxContext::sDontUseAsSourceKey;
/* This class lives on the stack and allows gfxContext users to easily, and
* performantly get a gfx::Pattern to use for drawing in their current context.
*/
class PatternFromState
{
public:
explicit PatternFromState(gfxContext *aContext) : mContext(aContext), mPattern(nullptr) {}
~PatternFromState() { if (mPattern) { mPattern->~Pattern(); } }
operator mozilla::gfx::Pattern&()
{
PatternFromState::operator mozilla::gfx::Pattern&()
{
gfxContext::AzureState &state = mContext->CurrentState();
if (state.pattern) {
return *state.pattern->GetPattern(mContext->mDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
} else if (state.sourceSurface) {
}
if (state.sourceSurface) {
Matrix transform = state.surfTransform;
if (state.patternTransformChanged) {
@ -67,22 +61,13 @@ public:
mPattern = new (mSurfacePattern.addr())
SurfacePattern(state.sourceSurface, ExtendMode::CLAMP, transform);
return *mPattern;
} else {
}
mPattern = new (mColorPattern.addr())
ColorPattern(state.color);
return *mPattern;
}
}
}
private:
union {
mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
};
gfxContext *mContext;
Pattern *mPattern;
};
gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
: mPathIsRect(false)

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

@ -710,67 +710,6 @@ private:
gfxContext *mContext;
};
/**
* Sentry helper class for functions with multiple return points that need to
* back up the current path of a context and have it automatically restored
* before they return. This class assumes that the transformation matrix will
* be the same when Save and Restore are called. The calling function must
* ensure that this is the case or the path will be copied incorrectly.
*/
class gfxContextPathAutoSaveRestore
{
typedef mozilla::gfx::Path Path;
public:
gfxContextPathAutoSaveRestore() : mContext(nullptr) {}
explicit gfxContextPathAutoSaveRestore(gfxContext *aContext, bool aSave = true) : mContext(aContext)
{
if (aSave)
Save();
}
~gfxContextPathAutoSaveRestore()
{
Restore();
}
void SetContext(gfxContext *aContext, bool aSave = true)
{
mContext = aContext;
if (aSave)
Save();
}
/**
* If a path is already saved, does nothing. Else copies the current path
* so that it may be restored.
*/
void Save()
{
if (!mPath && mContext) {
mPath = mContext->GetPath();
}
}
/**
* If no path is saved, does nothing. Else replaces the context's path with
* a copy of the saved one, and clears the saved path.
*/
void Restore()
{
if (mPath) {
mContext->SetPath(mPath);
mPath = nullptr;
}
}
private:
gfxContext *mContext;
mozilla::RefPtr<Path> mPath;
};
/**
* Sentry helper class for functions with multiple return points that need to
* back up the current matrix of a context and have it automatically restored
@ -844,4 +783,25 @@ private:
bool mSubpixelAntialiasingEnabled;
};
/* This class lives on the stack and allows gfxContext users to easily, and
* performantly get a gfx::Pattern to use for drawing in their current context.
*/
class PatternFromState
{
public:
explicit PatternFromState(gfxContext *aContext) : mContext(aContext), mPattern(nullptr) {}
~PatternFromState() { if (mPattern) { mPattern->~Pattern(); } }
operator mozilla::gfx::Pattern&();
private:
union {
mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
};
gfxContext *mContext;
mozilla::gfx::Pattern *mPattern;
};
#endif /* GFX_CONTEXT_H */

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

@ -3,8 +3,11 @@
* 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/. */
#include "gfxFont.h"
#include "mozilla/BinarySearch.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/MathAlgorithms.h"
#include "prlog.h"
@ -12,7 +15,6 @@
#include "nsExpirationTracker.h"
#include "nsITimer.h"
#include "gfxFont.h"
#include "gfxGlyphExtents.h"
#include "gfxPlatform.h"
#include "gfxTextRun.h"
@ -656,8 +658,8 @@ gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
} else {
gfxFloat width =
std::max(aFont->GetMetrics(gfxFont::eHorizontal).aveCharWidth,
gfxFontMissingGlyphs::GetDesiredMinWidth(aChar,
mAppUnitsPerDevUnit));
gfxFloat(gfxFontMissingGlyphs::GetDesiredMinWidth(aChar,
mAppUnitsPerDevUnit)));
details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit);
}
details->mXOffset = 0;
@ -1797,15 +1799,15 @@ gfxFont::DrawGlyphs(gfxShapedText *aShapedText,
glyphX -= advance;
}
}
gfxPoint pt(ToDeviceUnits(glyphX, aRunParams.devPerApp),
ToDeviceUnits(glyphY, aRunParams.devPerApp));
gfxFloat advanceDevUnits =
ToDeviceUnits(advance, aRunParams.devPerApp);
gfxFloat height = GetMetrics(eHorizontal).maxAscent;
gfxRect glyphRect = aFontParams.isVerticalFont ?
gfxRect(pt.x - height / 2, pt.y,
Point pt(Float(ToDeviceUnits(glyphX, aRunParams.devPerApp)),
Float(ToDeviceUnits(glyphY, aRunParams.devPerApp)));
Float advanceDevUnits =
Float(ToDeviceUnits(advance, aRunParams.devPerApp));
Float height = GetMetrics(eHorizontal).maxAscent;
Rect glyphRect = aFontParams.isVerticalFont ?
Rect(pt.x - height / 2, pt.y,
height, advanceDevUnits) :
gfxRect(pt.x, pt.y - height,
Rect(pt.x, pt.y - height,
advanceDevUnits, height);
// If there's a fake-italic skew in effect as part
@ -1819,7 +1821,8 @@ gfxFont::DrawGlyphs(gfxShapedText *aShapedText,
}
gfxFontMissingGlyphs::DrawMissingGlyph(
aRunParams.context, glyphRect, details->mGlyphID,
details->mGlyphID, glyphRect, *aRunParams.dt,
PatternFromState(aRunParams.context),
aShapedText->GetAppUnitsPerDevUnit());
// Restore the matrix, if we modified it before

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

@ -4,9 +4,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxFontMissingGlyphs.h"
#include "gfxUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Helpers.h"
#include "mozilla/gfx/PathHelpers.h"
#include "mozilla/RefPtr.h"
#include "nsDeviceContext.h"
#include "gfxContext.h"
#include "gfxColor.h"
#include "nsLayoutUtils.h"
using namespace mozilla;
using namespace mozilla::gfx;
#define CHAR_BITS(b00, b01, b02, b10, b11, b12, b20, b21, b22, b30, b31, b32, b40, b41, b42) \
((b00 << 0) | (b01 << 1) | (b02 << 2) | (b10 << 3) | (b11 << 4) | (b12 << 5) | \
@ -139,7 +147,7 @@ static const int BOX_BORDER_WIDTH = 1;
* The scaling factor for the border opacity; this is multiplied by the current
* opacity being used to draw the text.
*/
static const gfxFloat BOX_BORDER_OPACITY = 0.5;
static const Float BOX_BORDER_OPACITY = 0.5;
/**
* Draw a single hex character using the current color. A nice way to do this
* would be to fill in an A8 image surface and then use it as a mask
@ -149,119 +157,119 @@ static const gfxFloat BOX_BORDER_OPACITY = 0.5;
*/
#ifndef MOZ_GFX_OPTIMIZE_MOBILE
static void
DrawHexChar(gfxContext *aContext, const gfxPoint& aPt, uint32_t aDigit)
DrawHexChar(uint32_t aDigit, const Point& aPt, DrawTarget& aDrawTarget,
const Pattern &aPattern)
{
aContext->NewPath();
// To avoid the potential for seams showing between rects when we're under
// a transform we concat all the rects into a PathBuilder and fill the
// resulting Path (rather than using DrawTarget::FillRect).
RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
uint32_t glyphBits = glyphMicroFont[aDigit];
int x, y;
for (y = 0; y < MINIFONT_HEIGHT; ++y) {
for (x = 0; x < MINIFONT_WIDTH; ++x) {
for (int y = 0; y < MINIFONT_HEIGHT; ++y) {
for (int x = 0; x < MINIFONT_WIDTH; ++x) {
if (glyphBits & 1) {
aContext->Rectangle(gfxRect(x, y, 1, 1) + aPt, true);
Rect r(aPt.x + x, aPt.y + y, 1, 1);
MaybeSnapToDevicePixels(r, aDrawTarget, true);
builder->MoveTo(r.TopLeft());
builder->LineTo(r.TopRight());
builder->LineTo(r.BottomRight());
builder->LineTo(r.BottomLeft());
builder->Close();
}
glyphBits >>= 1;
}
}
aContext->Fill();
RefPtr<Path> path = builder->Finish();
aDrawTarget.Fill(path, aPattern);
}
#endif // MOZ_GFX_OPTIMIZE_MOBILE
void
gfxFontMissingGlyphs::DrawMissingGlyph(gfxContext *aContext,
const gfxRect& aRect,
uint32_t aChar,
gfxFontMissingGlyphs::DrawMissingGlyph(uint32_t aChar,
const Rect& aRect,
DrawTarget& aDrawTarget,
const Pattern& aPattern,
uint32_t aAppUnitsPerDevPixel)
{
aContext->Save();
gfxRGBA currentColor;
if (!aContext->GetDeviceColor(currentColor)) {
// We're currently drawing with some kind of pattern... Just draw
// the missing-glyph data in black.
currentColor = gfxRGBA(0,0,0,1);
}
// If we're currently drawing with some kind of pattern, we just draw the
// missing-glyph data in black.
ColorPattern color = aPattern.GetType() == PatternType::COLOR ?
static_cast<const ColorPattern&>(aPattern) :
ColorPattern(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)));
// Stroke a rectangle so that the stroke's left edge is inset one pixel
// from the left edge of the glyph box and the stroke's right edge
// is inset one pixel from the right edge of the glyph box.
gfxFloat halfBorderWidth = BOX_BORDER_WIDTH / 2.0;
gfxFloat borderLeft = aRect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth;
gfxFloat borderRight = aRect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth;
gfxRect borderStrokeRect(borderLeft, aRect.Y() + halfBorderWidth,
Float halfBorderWidth = BOX_BORDER_WIDTH / 2.0;
Float borderLeft = aRect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth;
Float borderRight = aRect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth;
Rect borderStrokeRect(borderLeft, aRect.Y() + halfBorderWidth,
borderRight - borderLeft,
aRect.Height() - 2.0 * halfBorderWidth);
if (!borderStrokeRect.IsEmpty()) {
aContext->SetLineWidth(BOX_BORDER_WIDTH);
aContext->SetDash(gfxContext::gfxLineSolid);
aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER);
gfxRGBA color = currentColor;
color.a *= BOX_BORDER_OPACITY;
aContext->SetDeviceColor(color);
aContext->NewPath();
aContext->Rectangle(borderStrokeRect);
ColorPattern adjustedColor = color;
color.mColor.a *= BOX_BORDER_OPACITY;
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
aContext->Fill();
aDrawTarget.FillRect(borderStrokeRect, adjustedColor);
#else
aContext->Stroke();
StrokeOptions strokeOptions(BOX_BORDER_WIDTH);
aDrawTarget.StrokeRect(borderStrokeRect, adjustedColor, strokeOptions);
#endif
}
#ifndef MOZ_GFX_OPTIMIZE_MOBILE
gfxPoint center(aRect.X() + aRect.Width() / 2,
aRect.Y() + aRect.Height() / 2);
gfxFloat halfGap = HEX_CHAR_GAP / 2.0;
gfxFloat top = -(MINIFONT_HEIGHT + halfGap);
aContext->SetDeviceColor(currentColor);
Point center = aRect.Center();
Float halfGap = HEX_CHAR_GAP / 2.f;
Float top = -(MINIFONT_HEIGHT + halfGap);
// We always want integer scaling, otherwise the "bitmap" glyphs will look
// even uglier than usual when zoomed
int32_t scale =
int32_t devPixelsPerCSSPx =
std::max<int32_t>(1, nsDeviceContext::AppUnitsPerCSSPixel() /
aAppUnitsPerDevPixel);
aContext->SetMatrix(
aContext->CurrentMatrix().Translate(center).Scale(scale, scale));
AutoRestoreTransform autoRestoreTransform(&aDrawTarget);
aDrawTarget.SetTransform(
aDrawTarget.GetTransform().PreTranslate(center).
PreScale(devPixelsPerCSSPx,
devPixelsPerCSSPx));
if (aChar < 0x10000) {
if (aRect.Width() >= 2 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
// Draw 4 digits for BMP
gfxFloat left = -(MINIFONT_WIDTH + halfGap);
DrawHexChar(aContext,
gfxPoint(left, top), (aChar >> 12) & 0xF);
DrawHexChar(aContext,
gfxPoint(halfGap, top), (aChar >> 8) & 0xF);
DrawHexChar(aContext,
gfxPoint(left, halfGap), (aChar >> 4) & 0xF);
DrawHexChar(aContext,
gfxPoint(halfGap, halfGap), aChar & 0xF);
Float left = -(MINIFONT_WIDTH + halfGap);
DrawHexChar((aChar >> 12) & 0xF,
Point(left, top), aDrawTarget, color);
DrawHexChar((aChar >> 8) & 0xF,
Point(halfGap, top), aDrawTarget, color);
DrawHexChar((aChar >> 4) & 0xF,
Point(left, halfGap), aDrawTarget, color);
DrawHexChar(aChar & 0xF,
Point(halfGap, halfGap), aDrawTarget, color);
}
} else {
if (aRect.Width() >= 3 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
// Draw 6 digits for non-BMP
gfxFloat first = -(MINIFONT_WIDTH * 1.5 + HEX_CHAR_GAP);
gfxFloat second = -(MINIFONT_WIDTH / 2.0);
gfxFloat third = (MINIFONT_WIDTH / 2.0 + HEX_CHAR_GAP);
DrawHexChar(aContext,
gfxPoint(first, top), (aChar >> 20) & 0xF);
DrawHexChar(aContext,
gfxPoint(second, top), (aChar >> 16) & 0xF);
DrawHexChar(aContext,
gfxPoint(third, top), (aChar >> 12) & 0xF);
DrawHexChar(aContext,
gfxPoint(first, halfGap), (aChar >> 8) & 0xF);
DrawHexChar(aContext,
gfxPoint(second, halfGap), (aChar >> 4) & 0xF);
DrawHexChar(aContext,
gfxPoint(third, halfGap), aChar & 0xF);
Float first = -(MINIFONT_WIDTH * 1.5 + HEX_CHAR_GAP);
Float second = -(MINIFONT_WIDTH / 2.0);
Float third = (MINIFONT_WIDTH / 2.0 + HEX_CHAR_GAP);
DrawHexChar((aChar >> 20) & 0xF,
Point(first, top), aDrawTarget, color);
DrawHexChar((aChar >> 16) & 0xF,
Point(second, top), aDrawTarget, color);
DrawHexChar((aChar >> 12) & 0xF,
Point(third, top), aDrawTarget, color);
DrawHexChar((aChar >> 8) & 0xF,
Point(first, halfGap), aDrawTarget, color);
DrawHexChar((aChar >> 4) & 0xF,
Point(second, halfGap), aDrawTarget, color);
DrawHexChar(aChar & 0xF,
Point(third, halfGap), aDrawTarget, color);
}
}
#endif
aContext->Restore();
}
gfxFloat
Float
gfxFontMissingGlyphs::GetDesiredMinWidth(uint32_t aChar,
uint32_t aAppUnitsPerDevPixel)
{
@ -269,12 +277,12 @@ gfxFontMissingGlyphs::GetDesiredMinWidth(uint32_t aChar,
* The minimum desired width for a missing-glyph glyph box. I've laid it out
* like this so you can see what goes where.
*/
gfxFloat width = BOX_HORIZONTAL_INSET + BOX_BORDER_WIDTH + HEX_CHAR_GAP +
Float width = BOX_HORIZONTAL_INSET + BOX_BORDER_WIDTH + HEX_CHAR_GAP +
MINIFONT_WIDTH + HEX_CHAR_GAP + MINIFONT_WIDTH +
((aChar < 0x10000) ? 0 : HEX_CHAR_GAP + MINIFONT_WIDTH) +
HEX_CHAR_GAP + BOX_BORDER_WIDTH + BOX_HORIZONTAL_INSET;
// Note that this will give us floating-point division, so the width will
// -not- be snapped to integer multiples of its basic pixel value
width *= gfxFloat(nsDeviceContext::AppUnitsPerCSSPixel()) / aAppUnitsPerDevPixel;
width *= Float(nsDeviceContext::AppUnitsPerCSSPixel()) / aAppUnitsPerDevPixel;
return width;
}

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

@ -6,34 +6,49 @@
#ifndef GFX_FONTMISSINGGLYPHS_H
#define GFX_FONTMISSINGGLYPHS_H
#include "gfxTypes.h"
#include "gfxRect.h"
#include "mozilla/Attributes.h"
#include "mozilla/gfx/Rect.h"
class gfxContext;
namespace mozilla {
namespace gfx {
class DrawTarget;
class Pattern;
}
}
/**
* This class should not be instantiated. It's just a container
* for some helper functions.
*/
class gfxFontMissingGlyphs {
class gfxFontMissingGlyphs MOZ_FINAL
{
typedef mozilla::gfx::DrawTarget DrawTarget;
typedef mozilla::gfx::Float Float;
typedef mozilla::gfx::Pattern Pattern;
typedef mozilla::gfx::Rect Rect;
gfxFontMissingGlyphs() MOZ_DELETE; // prevent instantiation
public:
/**
* Draw hexboxes for a missing glyph.
* @param aContext the context to draw to
* @param aRect the glyph-box for the glyph that is missing
* @param aChar the UTF16 codepoint for the character
* @param aRect the glyph-box for the glyph that is missing
* @param aDrawTarget the DrawTarget to draw to
* @param aPattern the pattern currently being used to paint
* @param aAppUnitsPerDevPixel the appUnits to devPixel ratio we're using,
* (so we can scale glyphs to a sensible size)
*/
static void DrawMissingGlyph(gfxContext *aContext,
const gfxRect& aRect,
uint32_t aChar,
static void DrawMissingGlyph(uint32_t aChar,
const Rect& aRect,
DrawTarget& aDrawTarget,
const Pattern& aPattern,
uint32_t aAppUnitsPerDevPixel);
/**
* @return the desired minimum width for a glyph-box that will allow
* the hexboxes to be drawn reasonably.
*/
static gfxFloat GetDesiredMinWidth(uint32_t aChar,
static Float GetDesiredMinWidth(uint32_t aChar,
uint32_t aAppUnitsPerDevUnit);
};

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

@ -7,6 +7,8 @@
#include "gfxGlyphExtents.h"
#include "gfxPlatformFontList.h"
#include "gfxUserFontSet.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/PathHelpers.h"
#include "nsGkAtoms.h"
#include "nsILanguageAtomService.h"
#include "nsServiceManagerUtils.h"
@ -24,6 +26,7 @@
#include "cairo.h"
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::unicode;
static const char16_t kEllipsisChar[] = { 0x2026, 0x0 };
@ -454,30 +457,18 @@ gfxTextRun::DrawPartialLigature(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
}
{
// Need to preserve the path, otherwise this can break canvas text-on-path;
// in general it seems like a good thing, as naive callers probably won't
// expect gfxTextRun::Draw to implicitly destroy the current path.
gfxContextPathAutoSaveRestore savePath(aParams.context);
// use division here to ensure that when the rect is aligned on multiples
// of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
// Also, make sure we snap the rectangle to device pixels.
Rect clipRect = aParams.isVerticalRun ?
Rect(clipExtents.X(), start / mAppUnitsPerDevUnit,
clipExtents.Width(), (end - start) / mAppUnitsPerDevUnit) :
Rect(start / mAppUnitsPerDevUnit, clipExtents.Y(),
(end - start) / mAppUnitsPerDevUnit, clipExtents.Height());
MaybeSnapToDevicePixels(clipRect, *aParams.dt, true);
aParams.context->Save();
aParams.context->NewPath();
if (aParams.isVerticalRun) {
aParams.context->Rectangle(gfxRect(clipExtents.X(),
start / mAppUnitsPerDevUnit,
clipExtents.Width(),
(end - start) / mAppUnitsPerDevUnit),
true);
} else {
aParams.context->Rectangle(gfxRect(start / mAppUnitsPerDevUnit,
clipExtents.Y(),
(end - start) / mAppUnitsPerDevUnit,
clipExtents.Height()),
true);
}
aParams.context->Clip();
aParams.context->Clip(clipRect);
}
gfxPoint pt;

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

@ -308,7 +308,10 @@ AsmJSModule::finish(ExclusiveContext *cx, TokenStream &tokenStream, MacroAssembl
MOZ_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
uint32_t endBeforeCurly = tokenStream.currentToken().pos.end;
uint32_t endAfterCurly = tokenStream.peekTokenPos().end;
TokenPos pos;
if (!tokenStream.peekTokenPos(&pos))
return false;
uint32_t endAfterCurly = pos.end;
MOZ_ASSERT(endBeforeCurly >= srcBodyStart_);
MOZ_ASSERT(endAfterCurly >= srcBodyStart_);
pod.srcLength_ = endBeforeCurly - srcStart_;
@ -1975,7 +1978,9 @@ class ModuleChars
}
static uint32_t endOffset(AsmJSParser &parser) {
return parser.tokenStream.peekTokenPos().end;
TokenPos pos;
MOZ_ALWAYS_TRUE(parser.tokenStream.peekTokenPos(&pos));
return pos.end;
}
};

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

@ -363,19 +363,28 @@ NextNonEmptyStatement(ParseNode *pn)
return SkipEmptyStatements(pn->pn_next);
}
static TokenKind
PeekToken(AsmJSParser &parser)
static bool
PeekToken(AsmJSParser &parser, TokenKind *tkp)
{
TokenStream &ts = parser.tokenStream;
while (ts.peekToken(TokenStream::Operand) == TOK_SEMI)
TokenKind tk;
while (true) {
if (!ts.peekToken(&tk, TokenStream::Operand))
return false;
if (tk != TOK_SEMI)
break;
ts.consumeKnownToken(TOK_SEMI);
return ts.peekToken(TokenStream::Operand);
}
*tkp = tk;
return true;
}
static bool
ParseVarOrConstStatement(AsmJSParser &parser, ParseNode **var)
{
TokenKind tk = PeekToken(parser);
TokenKind tk;
if (!PeekToken(parser, &tk))
return false;
if (tk != TOK_VAR && tk != TOK_CONST) {
*var = nullptr;
return true;
@ -1452,7 +1461,10 @@ class MOZ_STACK_CLASS ModuleCompiler
// Since pn is typically only null under OOM, this suppression simply forces any GC to be
// delayed until the compilation is off the stack and more memory can be freed.
gc::AutoSuppressGC nogc(cx_);
return failOffset(tokenStream().peekTokenPos().begin, str);
TokenPos pos;
if (!tokenStream().peekTokenPos(&pos))
return false;
return failOffset(pos.begin, str);
}
bool failfVA(ParseNode *pn, const char *fmt, va_list ap) {
@ -3863,13 +3875,18 @@ CheckModuleProcessingDirectives(ModuleCompiler &m)
{
TokenStream &ts = m.parser().tokenStream;
while (true) {
if (!ts.matchToken(TOK_STRING))
bool matched;
if (!ts.matchToken(&matched, TOK_STRING))
return false;
if (!matched)
return true;
if (!IsIgnoredDirectiveName(m.cx(), ts.currentToken().atom()))
return m.fail(nullptr, "unsupported processing directive");
if (!ts.matchToken(TOK_SEMI))
if (!ts.matchToken(&matched, TOK_SEMI))
return false;
if (!matched)
return m.fail(nullptr, "expected semicolon after string literal");
}
}
@ -6858,15 +6875,16 @@ ParseFunction(ModuleCompiler &m, ParseNode **fnOut)
{
TokenStream &tokenStream = m.tokenStream();
DebugOnly<TokenKind> tk = tokenStream.getToken();
MOZ_ASSERT(tk == TOK_FUNCTION);
tokenStream.consumeKnownToken(TOK_FUNCTION);
RootedPropertyName name(m.cx());
TokenKind tt = tokenStream.getToken();
if (tt == TOK_NAME) {
TokenKind tk;
if (!tokenStream.getToken(&tk))
return false;
if (tk == TOK_NAME) {
name = tokenStream.currentName();
} else if (tt == TOK_YIELD) {
} else if (tk == TOK_YIELD) {
if (!m.parser().checkYieldNameValidity())
return false;
name = m.cx()->names().yield;
@ -7046,7 +7064,13 @@ CheckFunctionsSequential(ModuleCompiler &m)
// function by the LifoAllocScope inside the loop.
LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
while (PeekToken(m.parser()) == TOK_FUNCTION) {
while (true) {
TokenKind tk;
if (!PeekToken(m.parser(), &tk))
return false;
if (tk != TOK_FUNCTION)
break;
LifoAllocScope scope(&lifo);
MIRGenerator *mir;
@ -7208,7 +7232,13 @@ CheckFunctionsParallel(ModuleCompiler &m, ParallelGroupState &group)
HelperThreadState().resetAsmJSFailureState();
AsmJSParallelTask *task = nullptr;
for (unsigned i = 0; PeekToken(m.parser()) == TOK_FUNCTION; i++) {
for (unsigned i = 0;; i++) {
TokenKind tk;
if (!PeekToken(m.parser(), &tk))
return false;
if (tk != TOK_FUNCTION)
break;
if (!task && !GetUnusedTask(group, i, &task) && !GetUsedTask(m, group, &task))
return false;
@ -7460,8 +7490,10 @@ CheckModuleExportObject(ModuleCompiler &m, ParseNode *object)
static bool
CheckModuleReturn(ModuleCompiler &m)
{
if (PeekToken(m.parser()) != TOK_RETURN) {
TokenKind tk = PeekToken(m.parser());
TokenKind tk;
if (!PeekToken(m.parser(), &tk))
return false;
if (tk != TOK_RETURN) {
if (tk == TOK_RC || tk == TOK_EOF)
return m.fail(nullptr, "expecting return statement");
return m.fail(nullptr, "invalid asm.js statement");
@ -8573,7 +8605,9 @@ CheckModule(ExclusiveContext *cx, AsmJSParser &parser, ParseNode *stmtList,
if (!CheckModuleReturn(m))
return false;
TokenKind tk = PeekToken(m.parser());
TokenKind tk;
if (!PeekToken(m.parser(), &tk))
return false;
if (tk != TOK_EOF && tk != TOK_RC)
return m.fail(nullptr, "top-level export (return) must be the last statement");

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

@ -329,13 +329,11 @@ frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject sco
bool canHaveDirectives = true;
for (;;) {
TokenKind tt = parser.tokenStream.peekToken(TokenStream::Operand);
if (tt <= TOK_EOF) {
TokenKind tt;
if (!parser.tokenStream.peekToken(&tt, TokenStream::Operand))
return nullptr;
if (tt == TOK_EOF)
break;
MOZ_ASSERT(tt == TOK_ERROR);
return nullptr;
}
TokenStream::Position pos(parser.keepAtoms);
parser.tokenStream.tell(&pos);

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

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

@ -461,7 +461,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
Node templateLiteral();
bool taggedTemplate(Node nodeList, TokenKind tt);
bool appendToCallSiteObj(Node callSiteObj);
bool addExprAndGetNextTemplStrToken(Node nodeList, TokenKind &tt);
bool addExprAndGetNextTemplStrToken(Node nodeList, TokenKind *ttp);
inline Node newName(PropertyName *name);
inline Node newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false);
@ -622,7 +622,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
};
bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
bool matchInOrOf(bool *isForOfp);
bool matchInOrOf(bool *isForInp, bool *isForOfp);
bool checkFunctionArguments();
bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom, bool *pbodyLevelHoistedUse);

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

@ -196,7 +196,6 @@ namespace frontend {
// Values of this type are used to index into arrays such as isExprEnding[],
// so the first value must be zero.
enum TokenKind {
TOK_ERROR = 0,
#define EMIT_ENUM(name, desc) TOK_##name,
#define EMIT_ENUM_RANGE(name, value) TOK_##name = TOK_##value,
FOR_EACH_TOKEN_KIND_WITH_RANGE(EMIT_ENUM, EMIT_ENUM_RANGE)

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

@ -516,13 +516,14 @@ TokenStream::TokenBuf::findEOLMax(const char16_t *p, size_t max)
void
TokenStream::advance(size_t position)
{
MOZ_ASSERT(position <= mozilla::PointerRangeSize(userbuf.base(), userbuf.limit()));
const char16_t *end = userbuf.base() + position;
while (userbuf.addressOfNextRawChar() < end)
getChar();
Token *cur = &tokens[cursor];
cur->pos.begin = userbuf.addressOfNextRawChar() - userbuf.base();
cur->type = TOK_ERROR;
MOZ_MAKE_MEM_UNDEFINED(&cur->type, sizeof(cur->type));
lookahead = 0;
}
@ -943,7 +944,7 @@ IsTokenSane(Token *tp)
{
// Nb: TOK_EOL should never be used in an actual Token; it should only be
// returned as a TokenKind from peekTokenSameLine().
if (tp->type < TOK_ERROR || tp->type >= TOK_LIMIT || tp->type == TOK_EOL)
if (tp->type < 0 || tp->type >= TOK_LIMIT || tp->type == TOK_EOL)
return false;
if (tp->pos.end < tp->pos.begin)
@ -1093,8 +1094,8 @@ static const uint8_t firstCharKinds[] = {
static_assert(LastCharKind < (1 << (sizeof(firstCharKinds[0]) * 8)),
"Elements of firstCharKinds[] are too small");
TokenKind
TokenStream::getTokenInternal(Modifier modifier)
bool
TokenStream::getTokenInternal(TokenKind *ttp, Modifier modifier)
{
int c, qc;
Token *tp;
@ -1625,15 +1626,23 @@ TokenStream::getTokenInternal(Modifier modifier)
flags.isDirtyLine = true;
tp->pos.end = userbuf.addressOfNextRawChar() - userbuf.base();
MOZ_ASSERT(IsTokenSane(tp));
return tp->type;
*ttp = tp->type;
return true;
error:
flags.isDirtyLine = true;
tp->pos.end = userbuf.addressOfNextRawChar() - userbuf.base();
tp->type = TOK_ERROR;
MOZ_ASSERT(IsTokenSane(tp));
onError();
return TOK_ERROR;
MOZ_MAKE_MEM_UNDEFINED(&tp->type, sizeof(tp->type));
flags.hadError = true;
#ifdef DEBUG
// Poisoning userbuf on error establishes an invariant: once an erroneous
// token has been seen, userbuf will not be consulted again. This is true
// because the parser will deal with the illegal token by aborting parsing
// immediately.
userbuf.poison();
#endif
MOZ_MAKE_MEM_UNDEFINED(ttp, sizeof(*ttp));
return false;
}
bool TokenStream::getStringOrTemplateToken(int qc, Token **tp)
@ -1763,22 +1772,6 @@ bool TokenStream::getStringOrTemplateToken(int qc, Token **tp)
return true;
}
void
TokenStream::onError()
{
flags.hadError = true;
#ifdef DEBUG
// Poisoning userbuf on error establishes an invariant: once an erroneous
// token has been seen, userbuf will not be consulted again. This is true
// because the parser will either (a) deal with the TOK_ERROR token by
// aborting parsing immediately; or (b) if the TOK_ERROR token doesn't
// match what it expected, it will unget the token, and the next getToken()
// call will immediately return the just-gotten TOK_ERROR token again
// without consulting userbuf, thanks to the lookahead buffer.
userbuf.poison();
#endif
}
JS_FRIEND_API(int)
js_fgets(char *buf, int size, FILE *file)
{
@ -1814,9 +1807,6 @@ frontend::TokenKindToDesc(TokenKind tt)
#define EMIT_CASE(name, desc) case TOK_##name: return desc;
FOR_EACH_TOKEN_KIND(EMIT_CASE)
#undef EMIT_CASE
case TOK_ERROR:
MOZ_ASSERT_UNREACHABLE("TOK_ERROR should not be passed.");
break;
case TOK_LIMIT:
MOZ_ASSERT_UNREACHABLE("TOK_LIMIT should not be passed.");
break;
@ -1833,7 +1823,6 @@ TokenKindToString(TokenKind tt)
#define EMIT_CASE(name, desc) case TOK_##name: return "TOK_" #name;
FOR_EACH_TOKEN_KIND(EMIT_CASE)
#undef EMIT_CASE
case TOK_ERROR: return "TOK_ERROR";
case TOK_LIMIT: break;
}

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

@ -105,9 +105,9 @@ struct Token
// pointer, and it's only initialized a very few places, so having a
// user-defined constructor won't hurt perf.) See also bug 920318.
Token()
: type(TOK_ERROR),
pos(0, 0)
: pos(0, 0)
{
MOZ_MAKE_MEM_UNDEFINED(&type, sizeof(type));
}
// Mutators
@ -339,7 +339,6 @@ class MOZ_STACK_CLASS TokenStream
bool reportStrictModeError(unsigned errorNumber, ...);
bool strictMode() const { return strictModeGetter && strictModeGetter->strictMode(); }
void onError();
static JSAtom *atomize(ExclusiveContext *cx, CharBuffer &cb);
bool putIdentInTokenbuf(const char16_t *identStart);
@ -348,7 +347,8 @@ class MOZ_STACK_CLASS TokenStream
bool isEOF:1; // Hit end of file.
bool isDirtyLine:1; // Non-whitespace since start of line.
bool sawOctalEscape:1; // Saw an octal character escape.
bool hadError:1; // Returned TOK_ERROR from getToken.
bool hadError:1; // Hit a syntax error, at start or during a
// token.
Flags()
: isEOF(), isDirtyLine(), sawOctalEscape(), hadError()
@ -368,19 +368,21 @@ class MOZ_STACK_CLASS TokenStream
TemplateTail, // Treat next characters as part of a template string
};
// Get the next token from the stream, make it the current token, and
// return its kind.
TokenKind getToken(Modifier modifier = None) {
// Advance to the next token. If the token stream encountered an error,
// return false. Otherwise return true and store the token kind in |*ttp|.
bool getToken(TokenKind *ttp, Modifier modifier = None) {
// Check for a pushed-back token resulting from mismatching lookahead.
if (lookahead != 0) {
MOZ_ASSERT(!flags.hadError);
lookahead--;
cursor = (cursor + 1) & ntokensMask;
TokenKind tt = currentToken().type;
MOZ_ASSERT(tt != TOK_EOL);
return tt;
*ttp = tt;
return true;
}
return getTokenInternal(modifier);
return getTokenInternal(ttp, modifier);
}
// Push the last scanned token back into the stream.
@ -390,29 +392,40 @@ class MOZ_STACK_CLASS TokenStream
cursor = (cursor - 1) & ntokensMask;
}
TokenKind peekToken(Modifier modifier = None) {
if (lookahead != 0)
return tokens[(cursor + 1) & ntokensMask].type;
TokenKind tt = getTokenInternal(modifier);
bool peekToken(TokenKind *ttp, Modifier modifier = None) {
if (lookahead > 0) {
MOZ_ASSERT(!flags.hadError);
*ttp = tokens[(cursor + 1) & ntokensMask].type;
return true;
}
if (!getTokenInternal(ttp, modifier))
return false;
ungetToken();
return tt;
return true;
}
TokenPos peekTokenPos(Modifier modifier = None) {
if (lookahead != 0)
return tokens[(cursor + 1) & ntokensMask].pos;
getTokenInternal(modifier);
bool peekTokenPos(TokenPos *posp, Modifier modifier = None) {
if (lookahead == 0) {
TokenKind tt;
if (!getTokenInternal(&tt, modifier))
return false;
ungetToken();
MOZ_ASSERT(lookahead != 0);
return tokens[(cursor + 1) & ntokensMask].pos;
} else {
MOZ_ASSERT(!flags.hadError);
}
*posp = tokens[(cursor + 1) & ntokensMask].pos;
return true;
}
// This is like peekToken(), with one exception: if there is an EOL
// between the end of the current token and the start of the next token, it
// returns TOK_EOL. In that case, no token with TOK_EOL is actually
// created, just a TOK_EOL TokenKind is returned, and currentToken()
// shouldn't be consulted. (This is the only place TOK_EOL is produced.)
MOZ_ALWAYS_INLINE TokenKind peekTokenSameLine(Modifier modifier = None) {
// return true and store TOK_EOL in |*ttp|. In that case, no token with
// TOK_EOL is actually created, just a TOK_EOL TokenKind is returned, and
// currentToken() shouldn't be consulted. (This is the only place TOK_EOL
// is produced.)
MOZ_ALWAYS_INLINE bool
peekTokenSameLine(TokenKind *ttp, Modifier modifier = None) {
const Token &curr = currentToken();
// If lookahead != 0, we have scanned ahead at least one token, and
@ -420,8 +433,11 @@ class MOZ_STACK_CLASS TokenStream
// it's the same as the line that the current token ends on, that's a
// stronger condition than what we are looking for, and we don't need
// to return TOK_EOL.
if (lookahead != 0 && srcCoords.isOnThisLine(curr.pos.end, lineno))
return tokens[(cursor + 1) & ntokensMask].type;
if (lookahead != 0 && srcCoords.isOnThisLine(curr.pos.end, lineno)) {
MOZ_ASSERT(!flags.hadError);
*ttp = tokens[(cursor + 1) & ntokensMask].type;
return true;
}
// The above check misses two cases where we don't have to return
// TOK_EOL.
@ -430,35 +446,58 @@ class MOZ_STACK_CLASS TokenStream
// is a newline between the next token and the one after that.
// The following test is somewhat expensive but gets these cases (and
// all others) right.
(void)getToken(modifier);
TokenKind tmp;
if (!getToken(&tmp, modifier))
return false;
const Token &next = currentToken();
ungetToken();
return srcCoords.lineNum(curr.pos.end) == srcCoords.lineNum(next.pos.begin)
*ttp = srcCoords.lineNum(curr.pos.end) == srcCoords.lineNum(next.pos.begin)
? next.type
: TOK_EOL;
return true;
}
// Get the next token from the stream if its kind is |tt|.
bool matchToken(TokenKind tt, Modifier modifier = None) {
if (getToken(modifier) == tt)
return true;
ungetToken();
bool matchToken(bool *matchedp, TokenKind tt, Modifier modifier = None) {
TokenKind token;
if (!getToken(&token, modifier))
return false;
if (token == tt) {
*matchedp = true;
} else {
ungetToken();
*matchedp = false;
}
return true;
}
void consumeKnownToken(TokenKind tt) {
JS_ALWAYS_TRUE(matchToken(tt));
bool matched;
MOZ_ASSERT(lookahead != 0);
MOZ_ALWAYS_TRUE(matchToken(&matched, tt));
MOZ_ALWAYS_TRUE(matched);
}
bool matchContextualKeyword(Handle<PropertyName*> keyword) {
if (getToken() == TOK_NAME && currentToken().name() == keyword)
return true;
ungetToken();
bool matchContextualKeyword(bool *matchedp, Handle<PropertyName*> keyword) {
TokenKind token;
if (!getToken(&token))
return false;
if (token == TOK_NAME && currentToken().name() == keyword) {
*matchedp = true;
} else {
*matchedp = false;
ungetToken();
}
return true;
}
bool nextTokenEndsExpr() {
return isExprEnding[peekToken()];
bool nextTokenEndsExpr(bool *endsExpr) {
TokenKind tt;
if (!peekToken(&tt))
return false;
*endsExpr = isExprEnding[tt];
return true;
}
class MOZ_STACK_CLASS Position {
@ -699,7 +738,7 @@ class MOZ_STACK_CLASS TokenStream
const char16_t *ptr; // next char to get
};
TokenKind getTokenInternal(Modifier modifier);
bool getTokenInternal(TokenKind *ttp, Modifier modifier);
bool getStringOrTemplateToken(int qc, Token **tp);

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

@ -249,7 +249,7 @@ class GCRuntime
void setMarkStackLimit(size_t limit);
void setParameter(JSGCParamKey key, uint32_t value);
uint32_t getParameter(JSGCParamKey key);
uint32_t getParameter(JSGCParamKey key, const AutoLockGC &lock);
bool isHeapBusy() { return heapState != js::Idle; }
bool isHeapMajorCollecting() { return heapState == js::MajorCollecting; }
@ -456,8 +456,10 @@ class GCRuntime
GCChunkSet::Range allChunks() { return chunkSet.all(); }
inline Chunk **getAvailableChunkList(Zone *zone);
void moveChunkToFreePool(Chunk *chunk);
void moveChunkToFreePool(Chunk *chunk, const AutoLockGC &lock);
bool hasChunk(Chunk *chunk) { return chunkSet.has(chunk); }
ChunkPool &emptyChunks(const AutoLockGC &lock) { return emptyChunks_; }
const ChunkPool &emptyChunks(const AutoLockGC &lock) const { return emptyChunks_; }
#ifdef JS_GC_ZEAL
void startVerifyPreBarriers();
@ -490,8 +492,8 @@ class GCRuntime
* Return the list of chunks that can be released outside the GC lock.
* Must be called either during the GC or with the GC lock taken.
*/
Chunk *expireChunkPool(bool shrinkBuffers, bool releaseAll);
void expireAndFreeChunkPool(bool releaseAll);
Chunk *expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC &lock);
void freeEmptyChunks(JSRuntime *rt);
void freeChunkList(Chunk *chunkListHead);
void prepareToFreeChunk(ChunkInfo &info);
void releaseChunk(Chunk *chunk);
@ -537,7 +539,7 @@ class GCRuntime
void sweepZones(FreeOp *fop, bool lastGC);
void decommitArenasFromAvailableList(Chunk **availableListHeadp);
void decommitArenas();
void expireChunksAndArenas(bool shouldShrink);
void expireChunksAndArenas(bool shouldShrink, const AutoLockGC &lock);
void sweepBackgroundThings();
void assertBackgroundSweepingFinished();
bool shouldCompact();
@ -606,7 +608,7 @@ class GCRuntime
*/
js::gc::Chunk *systemAvailableChunkListHead;
js::gc::Chunk *userAvailableChunkListHead;
js::gc::ChunkPool emptyChunks;
js::gc::ChunkPool emptyChunks_;
js::RootedValueMap rootsHash;

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

@ -0,0 +1,57 @@
var JSMSG_ILLEGAL_CHARACTER = "illegal character";
function test_reflect(code) {
var caught = false;
try {
Reflect.parse(code);
} catch (e) {
caught = true;
assertEq(e instanceof SyntaxError, true, code);
assertEq(e.message, JSMSG_ILLEGAL_CHARACTER, code);
}
assertEq(caught, true);
}
function test_eval(code) {
var caught = false;
try {
eval(code);
} catch (e) {
caught = true;
assertEq(e instanceof SyntaxError, true, code);
assertEq(e.message, JSMSG_ILLEGAL_CHARACTER, code);
}
assertEq(caught, true);
}
function test(code) {
test_reflect(code);
test_eval(code);
}
test("(function() { 'use asm'; @");
test("(function() { 'use asm'; var @");
test("(function() { 'use asm'; var a @");
test("(function() { 'use asm'; var a = @");
test("(function() { 'use asm'; var a = 1 @");
test("(function() { 'use asm'; var a = 1; @");
test("(function() { 'use asm'; var a = 1; function @");
test("(function() { 'use asm'; var a = 1; function f @");
test("(function() { 'use asm'; var a = 1; function f( @");
test("(function() { 'use asm'; var a = 1; function f() @");
test("(function() { 'use asm'; var a = 1; function f() { @");
test("(function() { 'use asm'; var a = 1; function f() { } @");
test("(function() { 'use asm'; var a = 1; function f() { } var @");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl @");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = @");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [ @");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f @");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f] @");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; @");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return @");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f @");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f; @");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f; } @");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f; }) @");
test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f; }); @");

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

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

@ -1,4 +1,6 @@
setJitCompilerOption("ion.warmup.trigger", 30);
gcPreserveCode();
var o = {};
function f(i) {
var obj_1 = { a: 0 };
@ -70,11 +72,87 @@ function f(i) {
obj_30.a = 1;
}
gcPreserveCode();
var o = {};
for (var i = 0; i < 200; i++) {
// Do not inline 'f', to keep re-entering 'f' at every loop iteration.
with (o) { }
f(i);
}
// This is the same test except that we do not preserve code under shrinking GC.
gczeal(14);
function g(i) {
var obj_1 = { a: 0 };
var obj_2 = { a: 0 };
var obj_3 = { a: 0 };
var obj_4 = { a: 0 };
var obj_5 = { a: 0 };
var obj_6 = { a: 0 };
var obj_7 = { a: 0 };
var obj_8 = { a: 0 };
var obj_9 = { a: 0 };
var obj_10 = { a: 0 };
var obj_11 = { a: 0 };
var obj_12 = { a: 0 };
var obj_13 = { a: 0 };
var obj_14 = { a: 0 };
var obj_15 = { a: 0 };
var obj_16 = { a: 0 };
var obj_17 = { a: 0 };
var obj_18 = { a: 0 };
var obj_19 = { a: 0 };
var obj_20 = { a: 0 };
var obj_21 = { a: 0 };
var obj_22 = { a: 0 };
var obj_23 = { a: 0 };
var obj_24 = { a: 0 };
var obj_25 = { a: 0 };
var obj_26 = { a: 0 };
var obj_27 = { a: 0 };
var obj_28 = { a: 0 };
var obj_29 = { a: 0 };
var obj_30 = { a: 0 };
// Doing a bailout after the return of the function call implies that we
// cannot resume before the function call. Thus, we would have to recover
// the 30 objects allocations during the bailout.
schedulegc(i % 100);
bailout();
obj_1.a = 1;
obj_2.a = 1;
obj_3.a = 1;
obj_4.a = 1;
obj_5.a = 1;
obj_6.a = 1;
obj_7.a = 1;
obj_8.a = 1;
obj_9.a = 1;
obj_10.a = 1;
obj_11.a = 1;
obj_12.a = 1;
obj_13.a = 1;
obj_14.a = 1;
obj_15.a = 1;
obj_16.a = 1;
obj_17.a = 1;
obj_18.a = 1;
obj_19.a = 1;
obj_20.a = 1;
obj_21.a = 1;
obj_22.a = 1;
obj_23.a = 1;
obj_24.a = 1;
obj_25.a = 1;
obj_26.a = 1;
obj_27.a = 1;
obj_28.a = 1;
obj_29.a = 1;
obj_30.a = 1;
}
for (var i = 0; i < 200; i++) {
// Do not inline 'g', to keep re-entering 'g' at every loop iteration.
with (o) { }
g(i);
}

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

@ -928,6 +928,7 @@ MarkActiveBaselineScripts(JSRuntime *rt, const JitActivationIterator &activation
case JitFrame_BaselineJS:
iter.script()->baselineScript()->setActive();
break;
case JitFrame_Bailout:
case JitFrame_IonJS: {
// Keep the baseline script around, since bailouts from the ion
// jitcode might need to re-enter into the baseline jitcode.

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

@ -1936,7 +1936,8 @@ JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value)
JS_PUBLIC_API(uint32_t)
JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key)
{
return rt->gc.getParameter(key);
AutoLockGC lock(rt);
return rt->gc.getParameter(key, lock);
}
JS_PUBLIC_API(void)

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

@ -936,7 +936,10 @@ js::FindBody(JSContext *cx, HandleFunction fun, HandleLinearString src, size_t *
bool onward = true;
// Skip arguments list.
do {
switch (ts.getToken()) {
TokenKind tt;
if (!ts.getToken(&tt))
return false;
switch (tt) {
case TOK_NAME:
case TOK_YIELD:
if (nest == 0)
@ -949,18 +952,17 @@ js::FindBody(JSContext *cx, HandleFunction fun, HandleLinearString src, size_t *
if (--nest == 0)
onward = false;
break;
case TOK_ERROR:
// Must be memory.
return false;
default:
break;
}
} while (onward);
TokenKind tt = ts.getToken();
if (tt == TOK_ARROW)
tt = ts.getToken();
if (tt == TOK_ERROR)
TokenKind tt;
if (!ts.getToken(&tt))
return false;
if (tt == TOK_ARROW) {
if (!ts.getToken(&tt))
return false;
}
bool braced = tt == TOK_LC;
MOZ_ASSERT_IF(fun->isExprClosure(), !braced);
*bodyStart = ts.currentToken().pos.begin;
@ -1660,12 +1662,9 @@ js_fun_bind(JSContext *cx, HandleObject target, HandleValue thisArg,
* error was already reported.
*/
static bool
OnBadFormal(JSContext *cx, TokenKind tt)
OnBadFormal(JSContext *cx)
{
if (tt != TOK_ERROR)
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_FORMAL);
else
MOZ_ASSERT(cx->isExceptionPending());
return false;
}
@ -1806,13 +1805,12 @@ FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind gener
bool yieldIsValidName = ts.versionNumber() < JSVERSION_1_7 && !isStarGenerator;
/* The argument string may be empty or contain no tokens. */
TokenKind tt = ts.getToken();
TokenKind tt;
if (!ts.getToken(&tt))
return false;
if (tt != TOK_EOF) {
for (;;) {
/*
* Check that it's a name. This also implicitly guards against
* TOK_ERROR, which was already reported.
*/
/* Check that it's a name. */
if (hasRest) {
ts.reportError(JSMSG_PARAMETER_AFTER_REST);
return false;
@ -1824,16 +1822,16 @@ FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind gener
if (tt != TOK_NAME) {
if (tt == TOK_TRIPLEDOT) {
hasRest = true;
tt = ts.getToken();
if (!ts.getToken(&tt))
return false;
if (tt == TOK_YIELD && yieldIsValidName)
tt = TOK_NAME;
if (tt != TOK_NAME) {
if (tt != TOK_ERROR)
ts.reportError(JSMSG_NO_REST_NAME);
return false;
}
} else {
return OnBadFormal(cx, tt);
return OnBadFormal(cx);
}
}
@ -1844,12 +1842,14 @@ FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind gener
* Get the next token. Stop on end of stream. Otherwise
* insist on a comma, get another name, and iterate.
*/
tt = ts.getToken();
if (!ts.getToken(&tt))
return false;
if (tt == TOK_EOF)
break;
if (tt != TOK_COMMA)
return OnBadFormal(cx, tt);
tt = ts.getToken();
return OnBadFormal(cx);
if (!ts.getToken(&tt))
return false;
}
}
}

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

@ -708,9 +708,8 @@ ChunkPool::Enum::removeAndPopFront()
--pool.count_;
}
/* Must be called either during the GC or with the GC lock taken. */
Chunk *
GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll)
GCRuntime::expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC &lock)
{
/*
* Return old empty chunks to the system while preserving the order of
@ -720,11 +719,11 @@ GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll)
*/
Chunk *freeList = nullptr;
unsigned freeChunkCount = 0;
for (ChunkPool::Enum e(emptyChunks); !e.empty(); ) {
for (ChunkPool::Enum e(emptyChunks(lock)); !e.empty(); ) {
Chunk *chunk = e.front();
MOZ_ASSERT(chunk->unused());
MOZ_ASSERT(!chunkSet.has(chunk));
if (releaseAll || freeChunkCount >= tunables.maxEmptyChunkCount() ||
if (freeChunkCount >= tunables.maxEmptyChunkCount() ||
(freeChunkCount >= tunables.minEmptyChunkCount() &&
(shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
{
@ -739,12 +738,22 @@ GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll)
e.popFront();
}
}
MOZ_ASSERT(emptyChunks.count() <= tunables.maxEmptyChunkCount());
MOZ_ASSERT_IF(shrinkBuffers, emptyChunks.count() <= tunables.minEmptyChunkCount());
MOZ_ASSERT_IF(releaseAll, emptyChunks.count() == 0);
MOZ_ASSERT(emptyChunks(lock).count() <= tunables.maxEmptyChunkCount());
MOZ_ASSERT_IF(shrinkBuffers, emptyChunks(lock).count() <= tunables.minEmptyChunkCount());
return freeList;
}
void
GCRuntime::freeEmptyChunks(JSRuntime *rt)
{
for (ChunkPool::Enum e(emptyChunks_); !e.empty();) {
Chunk *chunk = e.front();
e.removeAndPopFront();
MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
FreeChunk(rt, chunk);
}
}
void
GCRuntime::freeChunkList(Chunk *chunkListHead)
{
@ -755,12 +764,6 @@ GCRuntime::freeChunkList(Chunk *chunkListHead)
}
}
void
GCRuntime::expireAndFreeChunkPool(bool releaseAll)
{
freeChunkList(expireChunkPool(true, releaseAll));
}
/* static */ Chunk *
Chunk::allocate(JSRuntime *rt)
{
@ -1052,20 +1055,22 @@ Chunk::releaseArena(ArenaHeader *aheader)
} else if (!unused()) {
MOZ_ASSERT(info.prevp);
} else {
if (maybeLock.isNothing())
maybeLock.emplace(rt);
MOZ_ASSERT(unused());
removeFromAvailableList();
decommitAllArenas(rt);
rt->gc.moveChunkToFreePool(this);
rt->gc.moveChunkToFreePool(this, maybeLock.ref());
}
}
void
GCRuntime::moveChunkToFreePool(Chunk *chunk)
GCRuntime::moveChunkToFreePool(Chunk *chunk, const AutoLockGC &lock)
{
MOZ_ASSERT(chunk->unused());
MOZ_ASSERT(chunkSet.has(chunk));
chunkSet.remove(chunk);
emptyChunks.put(chunk);
emptyChunks(lock).put(chunk);
}
inline bool
@ -1075,7 +1080,7 @@ GCRuntime::wantBackgroundAllocation(const AutoLockGC &lock) const
// allocation if we already have some empty chunks or when the runtime has
// a small heap size (and therefore likely has a small growth rate).
return allocTask.enabled() &&
emptyChunks.count() < tunables.minEmptyChunkCount() &&
emptyChunks(lock).count() < tunables.minEmptyChunkCount() &&
chunkSet.count() >= 4;
}
@ -1124,7 +1129,7 @@ GCRuntime::pickChunk(const AutoLockGC &lock, Zone *zone,
if (chunk)
return chunk;
chunk = emptyChunks.get(rt);
chunk = emptyChunks(lock).get(rt);
if (!chunk) {
chunk = Chunk::allocate(rt);
if (!chunk)
@ -1241,7 +1246,7 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
#endif
lock(nullptr),
lockOwner(nullptr),
allocTask(rt, emptyChunks),
allocTask(rt, emptyChunks_),
helperState(rt)
{
setGCMode(JSGC_MODE_GLOBAL);
@ -1419,7 +1424,7 @@ GCRuntime::finish()
chunkSet.clear();
}
expireAndFreeChunkPool(true);
freeEmptyChunks(rt);
if (rootsHash.initialized())
rootsHash.clear();
@ -1535,7 +1540,7 @@ GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value)
}
uint32_t
GCRuntime::getParameter(JSGCParamKey key)
GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC &lock)
{
switch (key) {
case JSGC_MAX_BYTES:
@ -1547,9 +1552,9 @@ GCRuntime::getParameter(JSGCParamKey key)
case JSGC_MODE:
return uint32_t(mode);
case JSGC_UNUSED_CHUNKS:
return uint32_t(emptyChunks.count());
return uint32_t(emptyChunks(lock).count());
case JSGC_TOTAL_CHUNKS:
return uint32_t(chunkSet.count() + emptyChunks.count());
return uint32_t(chunkSet.count() + emptyChunks(lock).count());
case JSGC_SLICE_TIME_BUDGET:
return uint32_t(sliceBudget > 0 ? sliceBudget / PRMJ_USEC_PER_MSEC : 0);
case JSGC_MARK_STACK_LIMIT:
@ -2553,7 +2558,7 @@ GCRuntime::releaseRelocatedArenas(ArenaHeader *relocatedList)
}
AutoLockGC lock(rt);
expireChunksAndArenas(true);
expireChunksAndArenas(true, lock);
}
#endif // JSGC_COMPACTING
@ -3216,15 +3221,14 @@ GCRuntime::decommitArenas()
decommitArenasFromAvailableList(&userAvailableChunkListHead);
}
/* Must be called with the GC lock taken. */
void
GCRuntime::expireChunksAndArenas(bool shouldShrink)
GCRuntime::expireChunksAndArenas(bool shouldShrink, const AutoLockGC &lock)
{
#ifdef JSGC_FJGENERATIONAL
rt->threadPool.pruneChunkCache();
#endif
if (Chunk *toFree = expireChunkPool(shouldShrink, false)) {
if (Chunk *toFree = expireEmptyChunkPool(shouldShrink, lock)) {
AutoUnlockGC unlock(rt);
freeChunkList(toFree);
}
@ -3372,7 +3376,7 @@ GCHelperState::work()
case SWEEPING: {
AutoTraceLog logSweeping(logger, TraceLogger::GCSweeping);
doSweep();
doSweep(lock);
MOZ_ASSERT(state() == SWEEPING);
break;
}
@ -3454,9 +3458,8 @@ GCHelperState::waitBackgroundSweepEnd()
rt->gc.assertBackgroundSweepingFinished();
}
/* Must be called with the GC lock taken. */
void
GCHelperState::doSweep()
GCHelperState::doSweep(const AutoLockGC &lock)
{
if (sweepFlag) {
sweepFlag = false;
@ -3468,7 +3471,7 @@ GCHelperState::doSweep()
}
bool shrinking = shrinkFlag;
rt->gc.expireChunksAndArenas(shrinking);
rt->gc.expireChunksAndArenas(shrinking, lock);
/*
* The main thread may have called ShrinkGCBuffers while
@ -3477,7 +3480,7 @@ GCHelperState::doSweep()
*/
if (!shrinking && shrinkFlag) {
shrinkFlag = false;
rt->gc.expireChunksAndArenas(true);
rt->gc.expireChunksAndArenas(true, lock);
}
}
@ -5255,7 +5258,7 @@ GCRuntime::endSweepPhase(bool lastGC)
* Expire needs to unlock it for other callers.
*/
AutoLockGC lock(rt);
expireChunksAndArenas(invocationKind == GC_SHRINK);
expireChunksAndArenas(invocationKind == GC_SHRINK, lock);
}
}
@ -6141,7 +6144,7 @@ GCRuntime::shrinkBuffers()
if (CanUseExtraThreads())
helperState.startBackgroundShrink();
else
expireChunksAndArenas(true);
expireChunksAndArenas(true, lock);
}
void

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

@ -24,6 +24,8 @@
namespace js {
class AutoLockGC;
namespace gc {
class ForkJoinNursery;
}
@ -1041,7 +1043,7 @@ class GCHelperState
}
/* Must be called with the GC lock taken. */
void doSweep();
void doSweep(const AutoLockGC &lock);
public:
explicit GCHelperState(JSRuntime *rt)

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

@ -384,21 +384,24 @@ SelectionCarets::UpdateSelectionCarets()
return;
}
if (selection->GetRangeCount() <= 0) {
if (selection->IsCollapsed()) {
SetVisibility(false);
return;
}
nsRefPtr<nsRange> range = selection->GetRangeAt(0);
if (range->Collapsed()) {
SetVisibility(false);
return;
}
int32_t rangeCount = selection->GetRangeCount();
nsRefPtr<nsRange> firstRange = selection->GetRangeAt(0);
nsRefPtr<nsRange> lastRange = selection->GetRangeAt(rangeCount - 1);
nsLayoutUtils::FirstAndLastRectCollector collector;
nsRange::CollectClientRects(&collector, range,
range->GetStartParent(), range->StartOffset(),
range->GetEndParent(), range->EndOffset(), true, true);
nsLayoutUtils::FirstAndLastRectCollector collectorStart;
nsRange::CollectClientRects(&collectorStart, firstRange,
firstRange->GetStartParent(), firstRange->StartOffset(),
firstRange->GetEndParent(), firstRange->EndOffset(), true, true);
nsLayoutUtils::FirstAndLastRectCollector collectorEnd;
nsRange::CollectClientRects(&collectorEnd, lastRange,
lastRange->GetStartParent(), lastRange->StartOffset(),
lastRange->GetEndParent(), lastRange->EndOffset(), true, true);
nsIFrame* canvasFrame = mPresShell->GetCanvasFrame();
nsIFrame* rootFrame = mPresShell->GetRootFrame();
@ -412,11 +415,11 @@ SelectionCarets::UpdateSelectionCarets()
nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
int32_t startOffset;
nsIFrame* startFrame = FindFirstNodeWithFrame(mPresShell->GetDocument(),
range, fs, false, startOffset);
firstRange, fs, false, startOffset);
int32_t endOffset;
nsIFrame* endFrame = FindFirstNodeWithFrame(mPresShell->GetDocument(),
range, fs, true, endOffset);
lastRange, fs, true, endOffset);
if (!startFrame || !endFrame) {
SetVisibility(false);
@ -442,15 +445,15 @@ SelectionCarets::UpdateSelectionCarets()
// If start frame is LTR, then place start caret in first rect's leftmost
// otherwise put it to first rect's rightmost.
ReduceRectToVerticalEdge(collector.mFirstRect, startFrameIsRTL);
ReduceRectToVerticalEdge(collectorStart.mFirstRect, startFrameIsRTL);
// Contrary to start frame, if end frame is LTR, put end caret to last
// rect's rightmost position, otherwise, put it to last rect's leftmost.
ReduceRectToVerticalEdge(collector.mLastRect, !endFrameIsRTL);
ReduceRectToVerticalEdge(collectorEnd.mLastRect, !endFrameIsRTL);
nsAutoTArray<nsIFrame*, 16> hitFramesInFirstRect;
nsLayoutUtils::GetFramesForArea(rootFrame,
collector.mFirstRect,
collectorStart.mFirstRect,
hitFramesInFirstRect,
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
nsLayoutUtils::IGNORE_CROSS_DOC |
@ -458,7 +461,7 @@ SelectionCarets::UpdateSelectionCarets()
nsAutoTArray<nsIFrame*, 16> hitFramesInLastRect;
nsLayoutUtils::GetFramesForArea(rootFrame,
collector.mLastRect,
collectorEnd.mLastRect,
hitFramesInLastRect,
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
nsLayoutUtils::IGNORE_CROSS_DOC |
@ -467,11 +470,11 @@ SelectionCarets::UpdateSelectionCarets()
SetStartFrameVisibility(hitFramesInFirstRect.Contains(startFrame));
SetEndFrameVisibility(hitFramesInLastRect.Contains(endFrame));
nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collector.mFirstRect);
nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collector.mLastRect);
nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collectorStart.mFirstRect);
nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collectorEnd.mLastRect);
SetStartFramePos(collector.mFirstRect.BottomLeft());
SetEndFramePos(collector.mLastRect.BottomRight());
SetStartFramePos(collectorStart.mFirstRect.BottomLeft());
SetEndFramePos(collectorEnd.mLastRect.BottomRight());
SetVisibility(true);
// If range select only one character, append tilt class name to it.
@ -689,11 +692,13 @@ SelectionCarets::DragSelection(const nsPoint &movePoint)
}
nsRefPtr<dom::Selection> selection = GetSelection();
if (selection->GetRangeCount() <= 0) {
int32_t rangeCount = selection->GetRangeCount();
if (rangeCount <= 0) {
return nsEventStatus_eConsumeNoDefault;
}
nsRefPtr<nsRange> range = selection->GetRangeAt(0);
nsRefPtr<nsRange> range = mDragMode == START_FRAME ?
selection->GetRangeAt(0) : selection->GetRangeAt(rangeCount - 1);
if (!CompareRangeWithContentOffset(range, fs, offsets, mDragMode)) {
return nsEventStatus_eConsumeNoDefault;
}
@ -737,20 +742,22 @@ SelectionCarets::GetCaretYCenterPosition()
}
nsRefPtr<dom::Selection> selection = GetSelection();
if (selection->GetRangeCount() <= 0) {
int32_t rangeCount = selection->GetRangeCount();
if (rangeCount <= 0) {
return 0;
}
nsRefPtr<nsRange> range = selection->GetRangeAt(0);
nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
MOZ_ASSERT(mDragMode != NONE);
nsCOMPtr<nsIContent> node;
uint32_t nodeOffset;
if (mDragMode == START_FRAME) {
nsRefPtr<nsRange> range = selection->GetRangeAt(0);
node = do_QueryInterface(range->GetStartParent());
nodeOffset = range->StartOffset();
} else {
nsRefPtr<nsRange> range = selection->GetRangeAt(rangeCount - 1);
node = do_QueryInterface(range->GetEndParent());
nodeOffset = range->EndOffset();
}
@ -885,12 +892,6 @@ SelectionCarets::NotifySelectionChanged(nsIDOMDocument* aDoc,
nsISelection* aSel,
int16_t aReason)
{
bool isCollapsed;
aSel->GetIsCollapsed(&isCollapsed);
if (isCollapsed) {
SetVisibility(false);
return NS_OK;
}
if (!aReason || (aReason & (nsISelectionListener::DRAG_REASON |
nsISelectionListener::KEYPRESS_REASON |
nsISelectionListener::MOUSEDOWN_REASON))) {

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

@ -13,3 +13,4 @@ skip = false
[test_touchcaret.py]
[test_selectioncarets.py]
[test_selectioncarets_multiplerange.py]

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

@ -0,0 +1,108 @@
# -*- coding: utf-8 -*-
# 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 by import By
from marionette import Actions
from marionette_test import MarionetteTestCase
from selection import SelectionManager
class SelectionCaretsMultipleRangeTest(MarionetteTestCase):
_long_press_time = 1 # 1 second
def setUp(self):
# Code to execute before a tests are run.
MarionetteTestCase.setUp(self)
self.actions = Actions(self.marionette)
def openTestHtml(self, enabled=True):
# Open html for testing and enable selectioncaret and
# non-editable support
self.marionette.execute_script(
'SpecialPowers.setBoolPref("selectioncaret.enabled", %s);' %
('true' if enabled else 'false'))
self.marionette.execute_script(
'SpecialPowers.setBoolPref("selectioncaret.noneditable", %s);' %
('true' if enabled else 'false'))
test_html = self.marionette.absolute_url('test_selectioncarets_multiplerange.html')
self.marionette.navigate(test_html)
self._body = self.marionette.find_element(By.ID, 'bd')
self._sel3 = self.marionette.find_element(By.ID, 'sel3')
self._sel4 = self.marionette.find_element(By.ID, 'sel4')
self._sel6 = self.marionette.find_element(By.ID, 'sel6')
self._nonsel1 = self.marionette.find_element(By.ID, 'nonsel1')
def _long_press_without_contextmenu(self, el, x, y):
return self.actions.press(el, x, y).move_by_offset(0, 0).\
wait(self._long_press_time).release()
def _long_press_to_select_word(self, el, wordOrdinal):
sel = SelectionManager(el)
original_content = sel.content
words = original_content.split()
self.assertTrue(wordOrdinal < len(words),
'Expect at least %d words in the content.' % wordOrdinal)
# Calc offset
offset = 0
for i in range(wordOrdinal):
offset += (len(words[i]) + 1)
# Move caret inside the word.
el.tap()
sel.move_caret_to_front()
sel.move_caret_by_offset(offset)
x, y = sel.caret_location()
# Long press the caret position. Selection carets should appear, and the
# word will be selected. On Windows, those spaces after the word
# will also be selected.
self._long_press_without_contextmenu(el, x, y).perform()
def _to_unix_line_ending(self, s):
"""Changes all Windows/Mac line endings in s to UNIX line endings."""
return s.replace('\r\n', '\n').replace('\r', '\n')
def test_long_press_to_select_non_selectable_word(self):
'''Testing long press on non selectable field.
We should not select anything when long press on non selectable fields.'''
self.openTestHtml(enabled=True)
halfY = self._nonsel1.size['height'] / 2
self._long_press_without_contextmenu(self._nonsel1, 0, halfY).perform()
sel = SelectionManager(self._nonsel1)
range_count = sel.range_count()
self.assertEqual(range_count, 0)
def test_drag_caret_over_non_selectable_field(self):
'''Testing drag caret over non selectable field.
So that the selected content should exclude non selectable field and
end selection caret should appear in last range's position.'''
self.openTestHtml(enabled=True)
# Select target element and get target caret location
self._long_press_to_select_word(self._sel4, 3)
sel = SelectionManager(self._body)
(_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location()
self._long_press_to_select_word(self._sel6, 0)
(_, _), (end_caret2_x, end_caret2_y) = sel.selection_carets_location()
# Select start element
self._long_press_to_select_word(self._sel3, 3)
# Drag end caret to target location
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
self.actions.flick(self._body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform()
self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()),
'this 3\nuser can select this')
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
self.actions.flick(self._body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform()
self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()),
'this 3\nuser can select this 4\nuser can select this 5\nuser')

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

@ -351,6 +351,13 @@ nsTextControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
placeholderStyleContext))) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!IsSingleLineTextControl()) {
// For textareas, UpdateValueDisplay doesn't initialize the visibility
// status of the placeholder because it returns early, so we have to
// do that manually here.
txtCtrl->UpdatePlaceholderVisibility(true);
}
}
rv = UpdateValueDisplay(false);

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

@ -225,7 +225,7 @@ typedef struct fsmdef_media_t_ {
/*
* Data Channel properties
*/
#define WEBRTC_DATACHANNEL_STREAMS_DEFAULT 16
#define WEBRTC_DATACHANNEL_STREAMS_DEFAULT 256
uint32 datachannel_streams;
char datachannel_protocol[SDP_MAX_STRING_LEN + 1];

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

@ -45,7 +45,7 @@ interface nsIStreamingProtocolMetaData : nsISupports
attribute unsigned long height;
/**
* The duration of the media stream.
* The duration of the media stream in units of microseconds.
*/
attribute unsigned long long duration;

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

@ -53,7 +53,7 @@ public:
mIndex->AssertOwnsLock();
mHash = aHash;
CacheIndexEntry *entry = FindEntry();
const CacheIndexEntry *entry = FindEntry();
mIndex->mIndexStats.BeforeChange(entry);
if (entry && entry->IsInitialized() && !entry->IsRemoved()) {
mOldRecord = entry->mRec;
@ -66,7 +66,7 @@ public:
{
mIndex->AssertOwnsLock();
CacheIndexEntry *entry = FindEntry();
const CacheIndexEntry *entry = FindEntry();
mIndex->mIndexStats.AfterChange(entry);
if (!entry || !entry->IsInitialized() || entry->IsRemoved()) {
entry = nullptr;
@ -125,9 +125,9 @@ public:
void DoNotSearchInUpdates() { mDoNotSearchInUpdates = true; }
private:
CacheIndexEntry * FindEntry()
const CacheIndexEntry * FindEntry()
{
CacheIndexEntry *entry = nullptr;
const CacheIndexEntry *entry = nullptr;
switch (mIndex->mState) {
case CacheIndex::READING:
@ -520,6 +520,7 @@ CacheIndex::AddEntry(const SHA1Sum::Hash *aHash)
CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
bool entryRemoved = entry && entry->IsRemoved();
CacheIndexEntryUpdate *updated = nullptr;
if (index->mState == READY || index->mState == UPDATING ||
index->mState == BUILDING) {
@ -558,7 +559,7 @@ CacheIndex::AddEntry(const SHA1Sum::Hash *aHash)
entry = index->mIndex.PutEntry(*aHash);
}
} else { // WRITING, READING
CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
updated = index->mPendingUpdates.GetEntry(*aHash);
bool updatedRemoved = updated && updated->IsRemoved();
if ((updated && !updatedRemoved) ||
@ -578,13 +579,18 @@ CacheIndex::AddEntry(const SHA1Sum::Hash *aHash)
}
updated = index->mPendingUpdates.PutEntry(*aHash);
entry = updated;
}
if (updated) {
updated->InitNew();
updated->MarkDirty();
updated->MarkFresh();
} else {
entry->InitNew();
entry->MarkDirty();
entry->MarkFresh();
}
}
if (updateIfNonFreshEntriesExist &&
index->mIndexStats.Count() != index->mIndexStats.Fresh()) {
@ -652,7 +658,7 @@ CacheIndex::EnsureEntryExists(const SHA1Sum::Hash *aHash)
}
entry->MarkFresh();
} else { // WRITING, READING
CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
CacheIndexEntryUpdate *updated = index->mPendingUpdates.GetEntry(*aHash);
bool updatedRemoved = updated && updated->IsRemoved();
if (updatedRemoved ||
@ -732,6 +738,7 @@ CacheIndex::InitEntry(const SHA1Sum::Hash *aHash,
CacheIndexEntryAutoManage entryMng(aHash, index);
CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
CacheIndexEntryUpdate *updated = nullptr;
bool reinitEntry = false;
if (entry && entry->IsRemoved()) {
@ -753,7 +760,7 @@ CacheIndex::InitEntry(const SHA1Sum::Hash *aHash,
}
}
} else {
CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
updated = index->mPendingUpdates.GetEntry(*aHash);
DebugOnly<bool> removed = updated && updated->IsRemoved();
MOZ_ASSERT(updated || !removed);
@ -770,7 +777,6 @@ CacheIndex::InitEntry(const SHA1Sum::Hash *aHash,
return NS_OK;
}
}
entry = updated;
} else {
MOZ_ASSERT(entry->IsFresh());
@ -786,19 +792,29 @@ CacheIndex::InitEntry(const SHA1Sum::Hash *aHash,
// make a copy of a read-only entry
updated = index->mPendingUpdates.PutEntry(*aHash);
*updated = *entry;
entry = updated;
}
}
if (reinitEntry) {
// There is a collision and we are going to rewrite this entry. Initialize
// it as a new entry.
if (updated) {
updated->InitNew();
updated->MarkFresh();
} else {
entry->InitNew();
entry->MarkFresh();
}
}
if (updated) {
updated->Init(aAppId, aAnonymous, aInBrowser);
updated->MarkDirty();
} else {
entry->Init(aAppId, aAnonymous, aInBrowser);
entry->MarkDirty();
}
}
index->StartUpdatingIndexIfNeeded();
index->WriteIndexToDiskIfNeeded();
@ -865,7 +881,7 @@ CacheIndex::RemoveEntry(const SHA1Sum::Hash *aHash)
}
}
} else { // WRITING, READING
CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
CacheIndexEntryUpdate *updated = index->mPendingUpdates.GetEntry(*aHash);
bool updatedRemoved = updated && updated->IsRemoved();
if (updatedRemoved ||
@ -945,27 +961,6 @@ CacheIndex::UpdateEntry(const SHA1Sum::Hash *aHash,
if (!HasEntryChanged(entry, aFrecency, aExpirationTime, aSize)) {
return NS_OK;
}
} else {
CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
DebugOnly<bool> removed = updated && updated->IsRemoved();
MOZ_ASSERT(updated || !removed);
MOZ_ASSERT(updated || entry);
if (!updated) {
if (entry &&
HasEntryChanged(entry, aFrecency, aExpirationTime, aSize)) {
// make a copy of a read-only entry
updated = index->mPendingUpdates.PutEntry(*aHash);
*updated = *entry;
entry = updated;
} else {
return NS_ERROR_NOT_AVAILABLE;
}
} else {
entry = updated;
}
}
MOZ_ASSERT(entry->IsFresh());
MOZ_ASSERT(entry->IsInitialized());
@ -982,6 +977,43 @@ CacheIndex::UpdateEntry(const SHA1Sum::Hash *aHash,
if (aSize) {
entry->SetFileSize(*aSize);
}
} else {
CacheIndexEntryUpdate *updated = index->mPendingUpdates.GetEntry(*aHash);
DebugOnly<bool> removed = updated && updated->IsRemoved();
MOZ_ASSERT(updated || !removed);
MOZ_ASSERT(updated || entry);
if (!updated) {
if (!entry) {
LOG(("CacheIndex::UpdateEntry() - Entry was found neither in mIndex "
"nor in mPendingUpdates!"));
NS_WARNING(("CacheIndex::UpdateEntry() - Entry was found neither in "
"mIndex nor in mPendingUpdates!"));
return NS_ERROR_NOT_AVAILABLE;
}
// make a copy of a read-only entry
updated = index->mPendingUpdates.PutEntry(*aHash);
*updated = *entry;
}
MOZ_ASSERT(updated->IsFresh());
MOZ_ASSERT(updated->IsInitialized());
updated->MarkDirty();
if (aFrecency) {
updated->SetFrecency(*aFrecency);
}
if (aExpirationTime) {
updated->SetExpirationTime(*aExpirationTime);
}
if (aSize) {
updated->SetFileSize(*aSize);
}
}
}
index->WriteIndexToDiskIfNeeded();
@ -1096,7 +1128,7 @@ CacheIndex::HasEntry(const nsACString &aKey, EntryStatus *_retval)
sum.update(aKey.BeginReading(), aKey.Length());
sum.finish(hash);
CacheIndexEntry *entry = nullptr;
const CacheIndexEntry *entry = nullptr;
switch (index->mState) {
case READING:
@ -1481,7 +1513,7 @@ CacheIndex::ProcessPendingOperations()
// static
PLDHashOperator
CacheIndex::UpdateEntryInIndex(CacheIndexEntry *aEntry, void* aClosure)
CacheIndex::UpdateEntryInIndex(CacheIndexEntryUpdate *aEntry, void* aClosure)
{
CacheIndex *index = static_cast<CacheIndex *>(aClosure);
@ -1516,8 +1548,16 @@ CacheIndex::UpdateEntryInIndex(CacheIndexEntry *aEntry, void* aClosure)
return PL_DHASH_REMOVE;
}
if (entry) {
// Some information in mIndex can be newer than in mPendingUpdates (see bug
// 1074832). This will copy just those values that were really updated.
aEntry->ApplyUpdate(entry);
} else {
// There is no entry in mIndex, copy all information from mPendingUpdates
// to mIndex.
entry = index->mIndex.PutEntry(*aEntry->Hash());
*entry = *aEntry;
}
return PL_DHASH_REMOVE;
}

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

@ -166,32 +166,32 @@ public:
}
}
const SHA1Sum::Hash * Hash() { return &mRec->mHash; }
const SHA1Sum::Hash * Hash() const { return &mRec->mHash; }
bool IsInitialized() { return !!(mRec->mFlags & kInitializedMask); }
bool IsInitialized() const { return !!(mRec->mFlags & kInitializedMask); }
uint32_t AppId() { return mRec->mAppId; }
bool Anonymous() { return !!(mRec->mFlags & kAnonymousMask); }
bool InBrowser() { return !!(mRec->mFlags & kInBrowserMask); }
uint32_t AppId() const { return mRec->mAppId; }
bool Anonymous() const { return !!(mRec->mFlags & kAnonymousMask); }
bool InBrowser() const { return !!(mRec->mFlags & kInBrowserMask); }
bool IsRemoved() { return !!(mRec->mFlags & kRemovedMask); }
bool IsRemoved() const { return !!(mRec->mFlags & kRemovedMask); }
void MarkRemoved() { mRec->mFlags |= kRemovedMask; }
bool IsDirty() { return !!(mRec->mFlags & kDirtyMask); }
bool IsDirty() const { return !!(mRec->mFlags & kDirtyMask); }
void MarkDirty() { mRec->mFlags |= kDirtyMask; }
void ClearDirty() { mRec->mFlags &= ~kDirtyMask; }
bool IsFresh() { return !!(mRec->mFlags & kFreshMask); }
bool IsFresh() const { return !!(mRec->mFlags & kFreshMask); }
void MarkFresh() { mRec->mFlags |= kFreshMask; }
void SetFrecency(uint32_t aFrecency) { mRec->mFrecency = aFrecency; }
uint32_t GetFrecency() { return mRec->mFrecency; }
uint32_t GetFrecency() const { return mRec->mFrecency; }
void SetExpirationTime(uint32_t aExpirationTime)
{
mRec->mExpirationTime = aExpirationTime;
}
uint32_t GetExpirationTime() { return mRec->mExpirationTime; }
uint32_t GetExpirationTime() const { return mRec->mExpirationTime; }
// Sets filesize in kilobytes.
void SetFileSize(uint32_t aFileSize)
@ -205,12 +205,12 @@ public:
mRec->mFlags |= aFileSize;
}
// Returns filesize in kilobytes.
uint32_t GetFileSize() { return GetFileSize(mRec); }
uint32_t GetFileSize() const { return GetFileSize(mRec); }
static uint32_t GetFileSize(CacheIndexRecord *aRec)
{
return aRec->mFlags & kFileSizeMask;
}
bool IsFileEmpty() { return GetFileSize() == 0; }
bool IsFileEmpty() const { return GetFileSize() == 0; }
void WriteToBuf(void *aBuf)
{
@ -246,7 +246,7 @@ public:
mRec->mFlags = NetworkEndian::readUint32(&src->mFlags);
}
void Log() {
void Log() const {
LOG(("CacheIndexEntry::Log() [this=%p, hash=%08x%08x%08x%08x%08x, fresh=%u,"
" initialized=%u, removed=%u, dirty=%u, anonymous=%u, inBrowser=%u, "
"appId=%u, frecency=%u, expirationTime=%u, size=%u]",
@ -280,6 +280,7 @@ public:
}
private:
friend class CacheIndexEntryUpdate;
friend class CacheIndex;
friend class CacheIndexEntryAutoManage;
@ -308,6 +309,83 @@ private:
nsAutoPtr<CacheIndexRecord> mRec;
};
class CacheIndexEntryUpdate : public CacheIndexEntry
{
public:
explicit CacheIndexEntryUpdate(CacheIndexEntry::KeyTypePointer aKey)
: CacheIndexEntry(aKey)
, mUpdateFlags(0)
{
MOZ_COUNT_CTOR(CacheIndexEntryUpdate);
LOG(("CacheIndexEntryUpdate::CacheIndexEntryUpdate()"));
}
~CacheIndexEntryUpdate()
{
MOZ_COUNT_DTOR(CacheIndexEntryUpdate);
LOG(("CacheIndexEntryUpdate::~CacheIndexEntryUpdate()"));
}
CacheIndexEntryUpdate& operator=(const CacheIndexEntry& aOther)
{
MOZ_ASSERT(memcmp(&mRec->mHash, &aOther.mRec->mHash,
sizeof(SHA1Sum::Hash)) == 0);
mUpdateFlags = 0;
*(static_cast<CacheIndexEntry *>(this)) = aOther;
return *this;
}
void InitNew()
{
mUpdateFlags = kFrecencyUpdatedMask | kExpirationUpdatedMask |
kFileSizeUpdatedMask;
CacheIndexEntry::InitNew();
}
void SetFrecency(uint32_t aFrecency)
{
mUpdateFlags |= kFrecencyUpdatedMask;
CacheIndexEntry::SetFrecency(aFrecency);
}
void SetExpirationTime(uint32_t aExpirationTime)
{
mUpdateFlags |= kExpirationUpdatedMask;
CacheIndexEntry::SetExpirationTime(aExpirationTime);
}
void SetFileSize(uint32_t aFileSize)
{
mUpdateFlags |= kFileSizeUpdatedMask;
CacheIndexEntry::SetFileSize(aFileSize);
}
void ApplyUpdate(CacheIndexEntry *aDst) {
MOZ_ASSERT(memcmp(&mRec->mHash, &aDst->mRec->mHash,
sizeof(SHA1Sum::Hash)) == 0);
if (mUpdateFlags & kFrecencyUpdatedMask) {
aDst->mRec->mFrecency = mRec->mFrecency;
}
if (mUpdateFlags & kExpirationUpdatedMask) {
aDst->mRec->mExpirationTime = mRec->mExpirationTime;
}
aDst->mRec->mAppId = mRec->mAppId;
if (mUpdateFlags & kFileSizeUpdatedMask) {
aDst->mRec->mFlags = mRec->mFlags;
} else {
// Copy all flags except file size.
aDst->mRec->mFlags &= kFileSizeMask;
aDst->mRec->mFlags |= (mRec->mFlags & ~kFileSizeMask);
}
}
private:
static const uint32_t kFrecencyUpdatedMask = 0x00000001;
static const uint32_t kExpirationUpdatedMask = 0x00000002;
static const uint32_t kFileSizeUpdatedMask = 0x00000004;
uint32_t mUpdateFlags;
};
class CacheIndexStats
{
public:
@ -397,7 +475,7 @@ public:
return mSize;
}
void BeforeChange(CacheIndexEntry *aEntry) {
void BeforeChange(const CacheIndexEntry *aEntry) {
#ifdef DEBUG_STATS
if (!mDisableLogging) {
LOG(("CacheIndexStats::BeforeChange()"));
@ -441,7 +519,7 @@ public:
}
}
void AfterChange(CacheIndexEntry *aEntry) {
void AfterChange(const CacheIndexEntry *aEntry) {
MOZ_ASSERT(mStateLogged, "CacheIndexStats::AfterChange() - state not "
"logged!");
#ifdef DEBUG
@ -642,7 +720,7 @@ private:
// Merge all pending operations from mPendingUpdates into mIndex.
void ProcessPendingOperations();
static PLDHashOperator UpdateEntryInIndex(CacheIndexEntry *aEntry,
static PLDHashOperator UpdateEntryInIndex(CacheIndexEntryUpdate *aEntry,
void* aClosure);
// Following methods perform writing of the index file.
@ -929,7 +1007,7 @@ private:
// We cannot add, remove or change any entry in mIndex in states READING and
// WRITING. We track all changes in mPendingUpdates during these states.
nsTHashtable<CacheIndexEntry> mPendingUpdates;
nsTHashtable<CacheIndexEntryUpdate> mPendingUpdates;
// Contains information statistics for mIndex + mPendingUpdates.
CacheIndexStats mIndexStats;

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

@ -1036,16 +1036,6 @@ WebSocketChannel::WebSocketChannel() :
LOG(("Failed to initiate dashboard service."));
mSerial = sSerialSeed++;
// Register for prefs change notifications
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
} else {
NS_WARNING("failed to get observer service");
}
}
WebSocketChannel::~WebSocketChannel()
@ -1972,6 +1962,33 @@ WebSocketChannel::EnsureHdrOut(uint32_t size)
mHdrOut = mDynamicOutput;
}
namespace {
class RemoveObserverRunnable : public nsRunnable
{
nsRefPtr<WebSocketChannel> mChannel;
public:
RemoveObserverRunnable(WebSocketChannel* aChannel)
: mChannel(aChannel)
{}
NS_IMETHOD Run()
{
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (!observerService) {
NS_WARNING("failed to get observer service");
return NS_OK;
}
observerService->RemoveObserver(mChannel, NS_NETWORK_LINK_TOPIC);
return NS_OK;
}
};
} // anonymous namespace
void
WebSocketChannel::CleanupConnection()
{
@ -2003,6 +2020,10 @@ WebSocketChannel::CleanupConnection()
mConnectionLogService->RemoveHost(mHost, mSerial);
}
// This method can run in any thread, but the observer has to be removed on
// the main-thread.
NS_DispatchToMainThread(new RemoveObserverRunnable(this));
DecrementSessionCount();
}
@ -2110,8 +2131,6 @@ WebSocketChannel::StopSession(nsresult reason)
mTargetThread->Dispatch(new CallOnStop(this, reason),
NS_DISPATCH_NORMAL);
}
return;
}
void
@ -2919,6 +2938,19 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI,
if (NS_FAILED(rv))
return rv;
// Register for prefs change notifications
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (!observerService) {
NS_WARNING("failed to get observer service");
return NS_ERROR_FAILURE;
}
rv = observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Only set these if the open was successful:
//
mWasOpened = 1;

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

@ -914,10 +914,12 @@ DataChannelConnection::RequestMoreStreams(int32_t aNeeded)
uint32_t outStreamsNeeded;
socklen_t len;
if (aNeeded + mStreams.Length() > MAX_NUM_STREAMS)
if (aNeeded + mStreams.Length() > MAX_NUM_STREAMS) {
aNeeded = MAX_NUM_STREAMS - mStreams.Length();
if (aNeeded <= 0)
}
if (aNeeded <= 0) {
return false;
}
len = (socklen_t)sizeof(struct sctp_status);
if (usrsctp_getsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_STATUS, &status, &len) < 0) {
@ -926,19 +928,25 @@ DataChannelConnection::RequestMoreStreams(int32_t aNeeded)
}
outStreamsNeeded = aNeeded; // number to add
memset(&sas, 0, sizeof(struct sctp_add_streams));
// Note: if multiple channel opens happen when we don't have enough space,
// we'll call RequestMoreStreams() multiple times
memset(&sas, 0, sizeof(sas));
sas.sas_instrms = 0;
sas.sas_outstrms = (uint16_t)outStreamsNeeded; /* XXX error handling */
// Doesn't block, we get an event when it succeeds or fails
if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_ADD_STREAMS, &sas,
(socklen_t) sizeof(struct sctp_add_streams)) < 0) {
if (errno == EALREADY)
if (errno == EALREADY) {
LOG(("Already have %u output streams", outStreamsNeeded));
return true;
}
LOG(("***failed: setsockopt ADD errno=%d", errno));
return false;
}
LOG(("Requested %u more streams", outStreamsNeeded));
// We add to mStreams when we get a SCTP_STREAM_CHANGE_EVENT and the
// values are larger than mStreams.Length()
return true;
}
@ -1054,6 +1062,13 @@ DataChannelConnection::SendDeferredMessages()
channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED,
channel->mPrPolicy, channel->mPrValue)) {
channel->mFlags &= ~DATA_CHANNEL_FLAGS_SEND_REQ;
channel->mState = OPEN;
channel->mReady = true;
LOG(("%s: sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel.get()));
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, this,
channel));
sent = true;
} else {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
@ -1181,6 +1196,7 @@ DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_
prPolicy = SCTP_PR_SCTP_TTL;
break;
default:
LOG(("Unknown channel type", req->channel_type));
/* XXX error handling */
return;
}
@ -1207,6 +1223,10 @@ DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_
}
return;
}
if (stream >= mStreams.Length()) {
LOG(("%s: stream %u out of bounds (%u)", __FUNCTION__, stream, mStreams.Length()));
return;
}
nsCString label(nsDependentCSubstring(&req->label[0], ntohs(req->label_length)));
nsCString protocol(nsDependentCSubstring(&req->label[ntohs(req->label_length)],
@ -1224,8 +1244,8 @@ DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_
channel->mState = DataChannel::WAITING_TO_OPEN;
LOG(("%s: sending ON_CHANNEL_CREATED for %s/%s: %u", __FUNCTION__,
channel->mLabel.get(), channel->mProtocol.get(), stream));
LOG(("%s: sending ON_CHANNEL_CREATED for %s/%s: %u (state %u)", __FUNCTION__,
channel->mLabel.get(), channel->mProtocol.get(), stream, channel->mState));
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CHANNEL_CREATED,
this, channel));
@ -1733,13 +1753,14 @@ DataChannelConnection::HandleStreamResetEvent(const struct sctp_stream_reset_eve
// 2. We sent our own reset (CLOSING); either they crossed on the
// wire, or this is a response to our Reset.
// Go to CLOSED
// 3. We've sent a open but haven't gotten a response yet (OPENING)
// 3. We've sent a open but haven't gotten a response yet (CONNECTING)
// I believe this is impossible, as we don't have an input stream yet.
LOG(("Incoming: Channel %u closed, state %d",
channel->mStream, channel->mState));
ASSERT_WEBRTC(channel->mState == DataChannel::OPEN ||
channel->mState == DataChannel::CLOSING ||
channel->mState == DataChannel::CONNECTING ||
channel->mState == DataChannel::WAITING_TO_OPEN);
if (channel->mState == DataChannel::OPEN ||
channel->mState == DataChannel::WAITING_TO_OPEN) {
@ -1785,20 +1806,21 @@ DataChannelConnection::HandleStreamChangeEvent(const struct sctp_stream_change_e
return;
} else {
if (strchg->strchange_instrms > mStreams.Length()) {
LOG(("Other side increased streamds from %u to %u",
LOG(("Other side increased streams from %u to %u",
mStreams.Length(), strchg->strchange_instrms));
}
if (strchg->strchange_outstrms > mStreams.Length()) {
if (strchg->strchange_outstrms > mStreams.Length() ||
strchg->strchange_instrms > mStreams.Length()) {
uint16_t old_len = mStreams.Length();
uint16_t new_len = std::max(strchg->strchange_outstrms,
strchg->strchange_instrms);
LOG(("Increasing number of streams from %u to %u - adding %u (in: %u)",
old_len,
strchg->strchange_outstrms,
strchg->strchange_outstrms - old_len,
old_len, new_len, new_len - old_len,
strchg->strchange_instrms));
// make sure both are the same length
mStreams.AppendElements(strchg->strchange_outstrms - old_len);
mStreams.AppendElements(new_len - old_len);
LOG(("New length = %d (was %d)", mStreams.Length(), old_len));
for (uint32_t i = old_len; i < mStreams.Length(); ++i) {
for (size_t i = old_len; i < mStreams.Length(); ++i) {
mStreams[i] = nullptr;
}
// Re-process any channels waiting for streams.
@ -1809,13 +1831,17 @@ DataChannelConnection::HandleStreamChangeEvent(const struct sctp_stream_change_e
// Could make a more complex API for OpenXxxFinish() and avoid this loop
int32_t num_needed = mPending.GetSize();
LOG(("%d of %d new streams already needed", num_needed,
strchg->strchange_outstrms - old_len));
num_needed -= (strchg->strchange_outstrms - old_len); // number we added
new_len - old_len));
num_needed -= (new_len - old_len); // number we added
if (num_needed > 0) {
if (num_needed < 16)
num_needed = 16;
LOG(("Not enough new streams, asking for %d more", num_needed));
RequestMoreStreams(num_needed);
} else if (strchg->strchange_outstrms < strchg->strchange_instrms) {
LOG(("Requesting %d output streams to match partner",
strchg->strchange_instrms - strchg->strchange_outstrms));
RequestMoreStreams(strchg->strchange_instrms - strchg->strchange_outstrms);
}
ProcessQueuedOpens();

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

@ -17,7 +17,7 @@
#endif
// Duplicated in fsm.def
#define WEBRTC_DATACHANNEL_STREAMS_DEFAULT 16
#define WEBRTC_DATACHANNEL_STREAMS_DEFAULT 256
#define DATA_CHANNEL_PPID_CONTROL 50
#define DATA_CHANNEL_PPID_BINARY 52

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

@ -382,9 +382,10 @@ permissions.forEach(function (perm) {
self.marionette.timeouts(self.marionette.TIMEOUT_PAGE, self.marionette.timeout)
else:
self.marionette.timeouts(self.marionette.TIMEOUT_PAGE, 30000)
if hasattr(self, 'test_container') and self.test_container:
self.switch_into_test_container()
else:
elif hasattr(self, 'test_container') and self.test_container is False:
if self.marionette.session_capabilities.has_key('b2g') \
and self.marionette.session_capabilities['b2g'] == True:
self.close_test_container()
@ -416,46 +417,8 @@ permissions.forEach(function (perm) {
self.marionette = None
def switch_into_test_container(self):
self.marionette.set_context("content")
frame = None
try:
frame = self.marionette.find_element(
'css selector',
'iframe[src*="app://test-container.gaiamobile.org/index.html"]'
)
except NoSuchElementException:
result = self.marionette.execute_async_script("""
if((navigator.mozSettings == undefined) || (navigator.mozSettings == null) || (navigator.mozApps == undefined) || (navigator.mozApps == null)) {
marionetteScriptFinished(false);
return;
}
let setReq = navigator.mozSettings.createLock().set({'lockscreen.enabled': false});
setReq.onsuccess = function() {
let appsReq = navigator.mozApps.mgmt.getAll();
appsReq.onsuccess = function() {
let apps = appsReq.result;
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
if (app.manifest.name === 'Test Container') {
app.launch();
window.addEventListener('apploadtime', function apploadtime(){
window.removeEventListener('apploadtime', apploadtime);
marionetteScriptFinished(true);
});
return;
}
}
marionetteScriptFinished(false);
}
appsReq.onerror = function() {
marionetteScriptFinished(false);
}
}
setReq.onerror = function() {
marionetteScriptFinished(false);
}""", script_timeout=60000)
self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
self.assertTrue(result)
frame = Wait(self.marionette, timeout=10, interval=0.2).until(element_present(
'css selector',
'iframe[src*="app://test-container.gaiamobile.org/index.html"]'
@ -464,8 +427,8 @@ setReq.onerror = function() {
self.marionette.switch_to_frame(frame)
def close_test_container(self):
self.marionette.set_context("content")
self.marionette.switch_to_frame()
self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
result = self.marionette.execute_async_script("""
if((navigator.mozSettings == undefined) || (navigator.mozSettings == null) || (navigator.mozApps == undefined) || (navigator.mozApps == null)) {
marionetteScriptFinished(false);
@ -499,7 +462,10 @@ setReq.onerror = function() {
marionetteScriptFinished(false);
}""", script_timeout=60000)
frame = Wait(self.marionette, timeout=10, interval=0.2).until(element_not_present(
if not result:
raise Exception('Failed to close Test Container app')
Wait(self.marionette, timeout=10, interval=0.2).until(element_not_present(
'css selector',
'iframe[src*="app://test-container.gaiamobile.org/index.html"]'
))
@ -517,7 +483,7 @@ class MarionetteTestCase(CommonTestCase):
self.methodName = methodName
self.filepath = filepath
self.testvars = kwargs.pop('testvars', None)
self.test_container = kwargs.pop('test_container', False)
self.test_container = kwargs.pop('test_container', None)
CommonTestCase.__init__(self, methodName, **kwargs)
@classmethod
@ -588,7 +554,7 @@ class MarionetteJSTestCase(CommonTestCase):
self.jsFile = jsFile
self._marionette_weakref = marionette_weakref
self.marionette = None
self.test_container = kwargs.pop('test_container', False)
self.test_container = kwargs.pop('test_container', None)
CommonTestCase.__init__(self, methodName)
@classmethod

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

@ -638,6 +638,53 @@ class BaseMarionetteTestRunner(object):
def start_marionette(self):
self.marionette = Marionette(**self._build_kwargs())
def launch_test_container(self):
if self.marionette.session is None:
self.marionette.start_session()
self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
result = self.marionette.execute_async_script("""
if((navigator.mozSettings == undefined) || (navigator.mozSettings == null) || (navigator.mozApps == undefined) || (navigator.mozApps == null)) {
marionetteScriptFinished(false);
return;
}
let setReq = navigator.mozSettings.createLock().set({'lockscreen.enabled': false});
setReq.onsuccess = function() {
let appName = 'Test Container';
let activeApp = window.wrappedJSObject.System.currentApp;
// if the Test Container is already open then do nothing
if(activeApp.name === appName){
marionetteScriptFinished(true);
}
let appsReq = navigator.mozApps.mgmt.getAll();
appsReq.onsuccess = function() {
let apps = appsReq.result;
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
if (app.manifest.name === appName) {
app.launch();
window.addEventListener('appopen', function apploadtime(){
window.removeEventListener('appopen', apploadtime);
marionetteScriptFinished(true);
});
return;
}
}
marionetteScriptFinished(false);
}
appsReq.onerror = function() {
marionetteScriptFinished(false);
}
}
setReq.onerror = function() {
marionetteScriptFinished(false);
}""", script_timeout=60000)
if not result:
raise Exception("Could not launch test container app")
def run_tests(self, tests):
self.reset_test_stats()
self.start_time = time.time()
@ -724,7 +771,7 @@ class BaseMarionetteTestRunner(object):
self.logger.suite_end()
def add_test(self, test, expected='pass', test_container=False):
def add_test(self, test, expected='pass', test_container=None):
filepath = os.path.abspath(test)
if os.path.isdir(filepath):
@ -778,9 +825,13 @@ class BaseMarionetteTestRunner(object):
raise IOError("test file: %s does not exist" % i["path"])
file_ext = os.path.splitext(os.path.split(i['path'])[-1])[-1]
test_container = False
if i.get('test_container') and i.get('test_container') == 'true' and testarg_b2g:
test_container = None
if i.get('test_container') and testarg_b2g:
if i.get('test_container') == "true":
test_container = True
elif i.get('test_container') == "false":
test_container = False
self.add_test(i["path"], i["expected"], test_container)
return
@ -811,6 +862,9 @@ class BaseMarionetteTestRunner(object):
logcat_stdout=self.logcat_stdout,
result_callbacks=self.result_callbacks)
if test_container:
self.launch_test_container()
results = runner.run(suite)
self.results.append(results)

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

@ -73,16 +73,22 @@ class SelectionManager(object):
self.element.marionette.execute_script(cmd, script_args=[self.element])
def selection_rect_list(self):
'''Return the selection's DOMRectList object.
def selection_rect_list(self, idx):
'''Return the selection's DOMRectList object for the range at given idx.
If the element is either <input> or <textarea>, return the selection's
DOMRectList within the element. Otherwise, return the DOMRectList of the
current selection.
If the element is either <input> or <textarea>, return the DOMRectList of
the range at given idx of the selection within the element. Otherwise,
return the DOMRectList of the of the range at given idx of current selection.
'''
cmd = self.js_selection_cmd() +\
'''return sel.getRangeAt(0).getClientRects();'''
'''return sel.getRangeAt(%d).getClientRects();''' % idx
return self.element.marionette.execute_script(cmd, script_args=[self.element])
def range_count(self):
'''Get selection's range count'''
cmd = self.js_selection_cmd() +\
'''return sel.rangeCount;'''
return self.element.marionette.execute_script(cmd, script_args=[self.element])
def _selection_location_helper(self, location_type):
@ -94,9 +100,11 @@ class SelectionManager(object):
considered.
'''
rect_list = self.selection_rect_list()
list_length = rect_list['length']
first_rect, last_rect = rect_list['0'], rect_list[str(list_length - 1)]
range_count = self.range_count();
first_rect_list = self.selection_rect_list(0)
last_rect_list = self.selection_rect_list(range_count - 1)
last_list_length = last_rect_list['length']
first_rect, last_rect = first_rect_list['0'], last_rect_list[str(last_list_length - 1)]
origin_x, origin_y = self.element.location['x'], self.element.location['y']
if self.element.get_attribute('dir') == 'rtl': # such as Arabic

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

@ -0,0 +1,19 @@
<html>
<style>
h4 {
-moz-user-select: none;
}
</style>
<body id=bd>
<h3 id=sel1>user can select this 1</h3>
<h3 id=sel2>user can select this 2</h3>
<h3 id=sel3>user can select this 3</h3>
<h4 id=nonsel1>user cannot select this 1</h4>
<h4 id=nonsel2>user cannot select this 2</h4>
<h3 id=sel4>user can select this 4</h3>
<h3 id=sel5>user can select this 5</h3>
<h4 id=nonsel3>user cannot select this 3</h4>
<h3 id=sel6>user can select this 6</h3>
<h3 id=sel7>user can select this 7</h3>
</body>
</html>

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

@ -84,9 +84,9 @@ def get_debugger_info(debugger, debuggerArgs = None, debuggerInteractive = False
If the debugger cannot be found in the system, returns |None|.
:param debugger: The name of the debugger.
:param debuggerArgs: If specified, it's the list of arguments to pass to the
debugger. A debugger specific separator arguments is appended at the end of
that list.
:param debuggerArgs: If specified, it's the arguments to pass to the debugger,
as a string. Any debugger-specific separator arguments are appended after these
arguments.
:param debuggerInteractive: If specified, forces the debugger to be interactive.
'''
@ -118,13 +118,14 @@ def get_debugger_info(debugger, debuggerArgs = None, debuggerInteractive = False
['path', 'interactive', 'args', 'requiresEscapedArgs']
)
debugger_arguments = get_debugger_info('args', [])
debugger_arguments = []
# Extend the default arguments for the chosen debugger with the ones passed in, if any.
if debuggerArgs:
# Append the provided debugger arguments at the end of the arguments list.
debugger_arguments += debuggerArgs.split()
debugger_arguments += get_debugger_info('args', [])
# Override the default debugger interactive mode if needed.
debugger_interactive = get_debugger_info('interactive', False)
if debuggerInteractive:

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

@ -27,7 +27,7 @@ class BaseRunner(object):
output_timeout = None
def __init__(self, app_ctx=None, profile=None, clean_profile=True, env=None,
process_class=None, process_args=None, symbols_path=None):
process_class=None, process_args=None, symbols_path=None, dump_save_path=None):
self.app_ctx = app_ctx or DefaultContext()
if isinstance(profile, basestring):
@ -45,6 +45,7 @@ class BaseRunner(object):
self.process_class = process_class or ProcessHandler
self.process_args = process_args or {}
self.symbols_path = symbols_path
self.dump_save_path = dump_save_path
self.crashed = 0
@ -181,6 +182,9 @@ class BaseRunner(object):
if not dump_directory:
dump_directory = os.path.join(self.profile.profile, 'minidumps')
if not dump_save_path:
dump_save_path = self.dump_save_path
try:
logger = get_default_logger()
if logger is not None:

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

@ -6,7 +6,7 @@ import sys
from setuptools import setup, find_packages
PACKAGE_NAME = 'mozrunner'
PACKAGE_VERSION = '6.4'
PACKAGE_VERSION = '6.5'
desc = """Reliable start/stop/configuration of Mozilla Applications (Firefox, Thunderbird, etc.)"""

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

@ -1,11 +1,11 @@
{
"talos.zip": {
"url": "http://talos-bundles.pvt.build.mozilla.org/zips/talos.9dfad0333c76.zip",
"url": "http://talos-bundles.pvt.build.mozilla.org/zips/talos.e4a77270a43a.zip",
"path": ""
},
"global": {
"talos_repo": "https://hg.mozilla.org/build/talos",
"talos_revision": "9dfad0333c76"
"talos_revision": "e4a77270a43a"
},
"extra_options": {
"android": [ "--apkPath=%(apk_path)s" ]

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

@ -311,8 +311,6 @@ xpcshell-tests:
--test-plugin-path='$(DIST)/plugins' \
--tests-root-dir=$(abspath _tests/xpcshell) \
--testing-modules-dir=$(abspath _tests/modules) \
--xunit-file=$(abspath _tests/xpcshell/results.xml) \
--xunit-suite-name=xpcshell \
$(SYMBOLS_PATH) \
$(TEST_PATH_ARG) $(EXTRA_TEST_ARGS) \
$(xpcshell_path)

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

@ -1074,11 +1074,6 @@ class XPCShellTests(object):
self.debuggerInfo = None
if debugger:
# We need a list of arguments, not a string, to feed into
# the debugger
if debuggerArgs:
debuggerArgs = debuggerArgs.split();
self.debuggerInfo = mozdebug.get_debugger_info(debugger, debuggerArgs, debuggerInteractive)
self.xpcshell = xpcshell

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

@ -1694,8 +1694,10 @@ static PLDHashOperator EnumerateEntries(const nsACString& key,
nsCString entry,
void* userData)
{
if (!entry.IsEmpty()) {
crashReporterAPIData->Append(key + NS_LITERAL_CSTRING("=") + entry +
NS_LITERAL_CSTRING("\n"));
}
return PL_DHASH_NEXT;
}
@ -1797,6 +1799,11 @@ nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data)
return NS_OK;
}
nsresult RemoveCrashReportAnnotation(const nsACString& key)
{
return AnnotateCrashReport(key, NS_LITERAL_CSTRING(""));
}
nsresult SetGarbageCollecting(bool collecting)
{
if (!GetEnabled())

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

@ -64,10 +64,11 @@ bool GetMinidumpPath(nsAString& aPath);
nsresult SetMinidumpPath(const nsAString& aPath);
// AnnotateCrashReport and AppendAppNotesToCrashReport may be called from any
// thread in a chrome process, but may only be called from the main thread in
// a content process.
// AnnotateCrashReport, RemoveCrashReportAnnotation and
// AppendAppNotesToCrashReport may be called from any thread in a chrome
// process, but may only be called from the main thread in a content process.
nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data);
nsresult RemoveCrashReportAnnotation(const nsACString& key);
nsresult AppendAppNotesToCrashReport(const nsACString& data);
void AnnotateOOMAllocationSize(size_t size);