зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c. a=merge
This commit is contained in:
Коммит
10b87c9f24
|
@ -600,12 +600,14 @@ finalizeCB(GObject *aObj)
|
|||
const gchar*
|
||||
getNameCB(AtkObject* aAtkObj)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
|
||||
if (!accWrap)
|
||||
return nullptr;
|
||||
|
||||
nsAutoString name;
|
||||
accWrap->Name(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)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
|
||||
if (!accWrap || accWrap->IsDefunct())
|
||||
return nullptr;
|
||||
nsAutoString uniDesc;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
|
||||
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,21 +9356,30 @@ 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;
|
||||
|
||||
for (uint32_t reasonType = 0;
|
||||
reasonType < static_cast<uint32_t>(SelectionChangeReason::EndGuard_);
|
||||
++reasonType) {
|
||||
SelectionChangeReason strongReasonType =
|
||||
static_cast<SelectionChangeReason>(reasonType);
|
||||
if (CheckReason(aReason, strongReasonType)) {
|
||||
init.mReasons.AppendElement(strongReasonType);
|
||||
}
|
||||
selection->Stringify(init.mSelectedText);
|
||||
for (uint32_t reasonType = 0;
|
||||
reasonType < static_cast<uint32_t>(SelectionChangeReason::EndGuard_);
|
||||
++reasonType) {
|
||||
SelectionChangeReason strongReasonType =
|
||||
static_cast<SelectionChangeReason>(reasonType);
|
||||
if (CheckReason(aReason, strongReasonType)) {
|
||||
init.mReasons.AppendElement(strongReasonType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,8 +679,11 @@ 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);
|
||||
newFiles.AppendElement(static_cast<File*>(domFile.get()));
|
||||
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));
|
||||
|
|
|
@ -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,53 +36,38 @@ 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
|
||||
|
||||
PatternFromState::operator mozilla::gfx::Pattern&()
|
||||
{
|
||||
public:
|
||||
explicit PatternFromState(gfxContext *aContext) : mContext(aContext), mPattern(nullptr) {}
|
||||
~PatternFromState() { if (mPattern) { mPattern->~Pattern(); } }
|
||||
gfxContext::AzureState &state = mContext->CurrentState();
|
||||
|
||||
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) {
|
||||
Matrix transform = state.surfTransform;
|
||||
|
||||
if (state.patternTransformChanged) {
|
||||
Matrix mat = mContext->GetDTTransform();
|
||||
if (!mat.Invert()) {
|
||||
mPattern = new (mColorPattern.addr())
|
||||
ColorPattern(Color()); // transparent black to paint nothing
|
||||
return *mPattern;
|
||||
}
|
||||
transform = transform * state.patternTransform * mat;
|
||||
}
|
||||
|
||||
mPattern = new (mSurfacePattern.addr())
|
||||
SurfacePattern(state.sourceSurface, ExtendMode::CLAMP, transform);
|
||||
return *mPattern;
|
||||
} else {
|
||||
mPattern = new (mColorPattern.addr())
|
||||
ColorPattern(state.color);
|
||||
return *mPattern;
|
||||
}
|
||||
if (state.pattern) {
|
||||
return *state.pattern->GetPattern(mContext->mDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
|
||||
mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
|
||||
};
|
||||
if (state.sourceSurface) {
|
||||
Matrix transform = state.surfTransform;
|
||||
|
||||
if (state.patternTransformChanged) {
|
||||
Matrix mat = mContext->GetDTTransform();
|
||||
if (!mat.Invert()) {
|
||||
mPattern = new (mColorPattern.addr())
|
||||
ColorPattern(Color()); // transparent black to paint nothing
|
||||
return *mPattern;
|
||||
}
|
||||
transform = transform * state.patternTransform * mat;
|
||||
}
|
||||
|
||||
mPattern = new (mSurfacePattern.addr())
|
||||
SurfacePattern(state.sourceSurface, ExtendMode::CLAMP, transform);
|
||||
return *mPattern;
|
||||
}
|
||||
|
||||
mPattern = new (mColorPattern.addr())
|
||||
ColorPattern(state.color);
|
||||
return *mPattern;
|
||||
}
|
||||
|
||||
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,16 +1799,16 @@ 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,
|
||||
height, advanceDevUnits) :
|
||||
gfxRect(pt.x, pt.y - height,
|
||||
advanceDevUnits, height);
|
||||
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) :
|
||||
Rect(pt.x, pt.y - height,
|
||||
advanceDevUnits, height);
|
||||
|
||||
// If there's a fake-italic skew in effect as part
|
||||
// of the drawTarget's transform, we need to remove
|
||||
|
@ -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,
|
||||
uint32_t aAppUnitsPerDevPixel)
|
||||
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,
|
||||
borderRight - borderLeft,
|
||||
aRect.Height() - 2.0 * 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,35 +6,50 @@
|
|||
#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,
|
||||
uint32_t aAppUnitsPerDevPixel);
|
||||
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,
|
||||
uint32_t aAppUnitsPerDevUnit);
|
||||
static Float GetDesiredMinWidth(uint32_t aChar,
|
||||
uint32_t aAppUnitsPerDevUnit);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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) {
|
||||
if (tt == TOK_EOF)
|
||||
break;
|
||||
MOZ_ASSERT(tt == TOK_ERROR);
|
||||
TokenKind tt;
|
||||
if (!parser.tokenStream.peekToken(&tt, TokenStream::Operand))
|
||||
return nullptr;
|
||||
}
|
||||
if (tt == TOK_EOF)
|
||||
break;
|
||||
|
||||
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,38 +392,52 @@ 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);
|
||||
ungetToken();
|
||||
MOZ_ASSERT(lookahead != 0);
|
||||
return tokens[(cursor + 1) & ntokensMask].pos;
|
||||
bool peekTokenPos(TokenPos *posp, Modifier modifier = None) {
|
||||
if (lookahead == 0) {
|
||||
TokenKind tt;
|
||||
if (!getTokenInternal(&tt, modifier))
|
||||
return false;
|
||||
ungetToken();
|
||||
MOZ_ASSERT(lookahead != 0);
|
||||
} 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) {
|
||||
const Token &curr = currentToken();
|
||||
// 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
|
||||
// |lineno| is the line that the furthest-scanned token ends on. If
|
||||
// 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)
|
||||
? next.type
|
||||
: TOK_EOL;
|
||||
|
||||
*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();
|
||||
return false;
|
||||
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();
|
||||
return false;
|
||||
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());
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_FORMAL);
|
||||
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);
|
||||
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,12 +579,17 @@ CacheIndex::AddEntry(const SHA1Sum::Hash *aHash)
|
|||
}
|
||||
|
||||
updated = index->mPendingUpdates.PutEntry(*aHash);
|
||||
entry = updated;
|
||||
}
|
||||
|
||||
entry->InitNew();
|
||||
entry->MarkDirty();
|
||||
entry->MarkFresh();
|
||||
if (updated) {
|
||||
updated->InitNew();
|
||||
updated->MarkDirty();
|
||||
updated->MarkFresh();
|
||||
} else {
|
||||
entry->InitNew();
|
||||
entry->MarkDirty();
|
||||
entry->MarkFresh();
|
||||
}
|
||||
}
|
||||
|
||||
if (updateIfNonFreshEntriesExist &&
|
||||
|
@ -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,18 +792,28 @@ 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.
|
||||
entry->InitNew();
|
||||
entry->MarkFresh();
|
||||
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();
|
||||
}
|
||||
entry->Init(aAppId, aAnonymous, aInBrowser);
|
||||
entry->MarkDirty();
|
||||
}
|
||||
|
||||
index->StartUpdatingIndexIfNeeded();
|
||||
|
@ -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,42 +961,58 @@ CacheIndex::UpdateEntry(const SHA1Sum::Hash *aHash,
|
|||
if (!HasEntryChanged(entry, aFrecency, aExpirationTime, aSize)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(entry->IsFresh());
|
||||
MOZ_ASSERT(entry->IsInitialized());
|
||||
entry->MarkDirty();
|
||||
|
||||
if (aFrecency) {
|
||||
entry->SetFrecency(*aFrecency);
|
||||
}
|
||||
|
||||
if (aExpirationTime) {
|
||||
entry->SetExpirationTime(*aExpirationTime);
|
||||
}
|
||||
|
||||
if (aSize) {
|
||||
entry->SetFileSize(*aSize);
|
||||
}
|
||||
} else {
|
||||
CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
|
||||
CacheIndexEntryUpdate *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 {
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
entry = updated;
|
||||
|
||||
// make a copy of a read-only entry
|
||||
updated = index->mPendingUpdates.PutEntry(*aHash);
|
||||
*updated = *entry;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(entry->IsFresh());
|
||||
MOZ_ASSERT(entry->IsInitialized());
|
||||
entry->MarkDirty();
|
||||
MOZ_ASSERT(updated->IsFresh());
|
||||
MOZ_ASSERT(updated->IsInitialized());
|
||||
updated->MarkDirty();
|
||||
|
||||
if (aFrecency) {
|
||||
entry->SetFrecency(*aFrecency);
|
||||
}
|
||||
if (aFrecency) {
|
||||
updated->SetFrecency(*aFrecency);
|
||||
}
|
||||
|
||||
if (aExpirationTime) {
|
||||
entry->SetExpirationTime(*aExpirationTime);
|
||||
}
|
||||
if (aExpirationTime) {
|
||||
updated->SetExpirationTime(*aExpirationTime);
|
||||
}
|
||||
|
||||
if (aSize) {
|
||||
entry->SetFileSize(*aSize);
|
||||
if (aSize) {
|
||||
updated->SetFileSize(*aSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
entry = index->mIndex.PutEntry(*aEntry->Hash());
|
||||
*entry = *aEntry;
|
||||
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;
|
||||
|
@ -3228,8 +3260,8 @@ WebSocketChannel::OnStartRequest(nsIRequest *aRequest,
|
|||
|
||||
NS_IMETHODIMP
|
||||
WebSocketChannel::OnStopRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatusCode)
|
||||
nsISupports *aContext,
|
||||
nsresult aStatusCode)
|
||||
{
|
||||
LOG(("WebSocketChannel::OnStopRequest() %p [%p %p %x]\n",
|
||||
this, aRequest, aContext, aStatusCode));
|
||||
|
|
|
@ -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,56 +417,18 @@ 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"]'
|
||||
))
|
||||
frame = Wait(self.marionette, timeout=10, interval=0.2).until(element_present(
|
||||
'css selector',
|
||||
'iframe[src*="app://test-container.gaiamobile.org/index.html"]'
|
||||
))
|
||||
|
||||
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 = True
|
||||
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)
|
||||
{
|
||||
crashReporterAPIData->Append(key + NS_LITERAL_CSTRING("=") + entry +
|
||||
NS_LITERAL_CSTRING("\n"));
|
||||
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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче