merge mozilla-inbound to mozilla-central. r=merge a=merge

MozReview-Commit-ID: DPQl41S3ZkU
This commit is contained in:
Sebastian Hengst 2017-06-17 22:26:03 +02:00
Родитель 0d1b7d9966 4fcefda78e
Коммит 3654d560d6
68 изменённых файлов: 900 добавлений и 598 удалений

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

@ -1074,6 +1074,16 @@ static const nsRoleMapEntry sWAIRoleMaps[] =
kGenericAccType,
kNoReqStates
},
{ // term
&nsGkAtoms::term,
roles::TERM,
kUseMapRole,
eNoValue,
eNoAction,
eNoLiveAttr,
kGenericAccType,
states::READONLY
},
{ // textbox
&nsGkAtoms::textbox,
roles::ENTRY,

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

@ -306,8 +306,8 @@ ROLE(OUTLINE,
ROLE(OUTLINEITEM,
"outlineitem",
ATK_ROLE_LIST_ITEM,
NSAccessibilityRowRole, //XXX: use OutlineRow as subrole.
ATK_ROLE_TREE_ITEM,
NSAccessibilityRowRole,
ROLE_SYSTEM_OUTLINEITEM,
ROLE_SYSTEM_OUTLINEITEM,
eNameFromSubtreeRule)
@ -1034,7 +1034,7 @@ ROLE(DEFINITION_LIST,
ROLE(TERM,
"term",
ATK_ROLE_LIST_ITEM,
ATK_ROLE_DESCRIPTION_TERM,
NSAccessibilityGroupRole,
ROLE_SYSTEM_LISTITEM,
ROLE_SYSTEM_LISTITEM,

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

@ -901,6 +901,9 @@ ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
case roles::NOTE:
return @"AXDocumentNote";
case roles::OUTLINEITEM:
return @"AXOutlineRow";
// macOS added an AXSubrole value to distinguish generic AXGroup objects
// from those which are AXGroups as a result of an explicit ARIA role,
// such as the non-landmark, non-listitem text containers in DPub ARIA.

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

@ -66,6 +66,7 @@
testRole("aria_tab", ROLE_PAGETAB);
testRole("aria_tablist", ROLE_PAGETABLIST);
testRole("aria_tabpanel", ROLE_PROPERTYPAGE);
testRole("aria_term", ROLE_TERM);
testRole("aria_textbox", ROLE_ENTRY);
testRole("aria_timer", ROLE_TEXT); // weak role
testRole("aria_toolbar", ROLE_TOOLBAR);
@ -243,6 +244,7 @@
<span id="aria_tab" role="tab"/>
<span id="aria_tablist" role="tablist"/>
<span id="aria_tabpanel" role="tabpanel"/>
<span id="aria_term" role="term"/>
<span id="aria_textbox" role="textbox"/>
<span id="aria_timer" role="timer"/>
<span id="aria_toolbar" role="toolbar"/>

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

@ -56,7 +56,7 @@ tags = blocklist
skip-if = toolkit == "gtk2" || toolkit == "gtk3" # fails intermittently on Linux (bug 909342)
tags = blocklist
[browser_CTP_crashreporting.js]
skip-if = !crashreporter || (os == 'linux' && debug)
skip-if = !crashreporter
tags = blocklist
[browser_CTP_data_urls.js]
tags = blocklist

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

@ -133,10 +133,11 @@ add_task(async function() {
getUI("submitButton").click();
// And wait for the parent to say that the crash report was submitted
// successfully.
// successfully. This can take time on debug builds.
await ContentTaskUtils.waitForCondition(() => {
return statusDiv.getAttribute("status") == "success";
}, "Timed out waiting for plugin binding to be in success state");
}, "Timed out waiting for plugin binding to be in success state",
100, 200);
});
let [subject, ] = await crashReportPromise;

31
browser/extensions/pocket/bootstrap.js поставляемый
Просмотреть файл

@ -9,7 +9,6 @@ const {classes: Cc, interfaces: Ci, utils: Cu, manager: Cm} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://services-common/utils.js");
Cu.import("resource://gre/modules/Preferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
@ -318,7 +317,7 @@ var PocketOverlay = {
for (let window of CustomizableUI.windows) {
for (let id of ["panelMenu_pocket", "menu_pocket", "BMB_pocket",
"panelMenu_pocketSeparator", "menu_pocketSeparator",
"BMB_pocketSeparator"]) {
"BMB_pocketSeparator", "appMenu-library-pocket-button"]) {
let element = window.document.getElementById(id);
if (element)
element.remove();
@ -412,6 +411,19 @@ var PocketOverlay = {
sib.parentNode.insertBefore(sep, sib);
sib.parentNode.insertBefore(menu, sib);
}
// Add to library panel
sib = document.getElementById("appMenu-library-history-button");
if (sib && !document.getElementById("appMenu-library-pocket-button")) {
let menu = createElementWithAttrs(document, "toolbarbutton", {
"id": "appMenu-library-pocket-button",
"label": gPocketBundle.GetStringFromName("pocketMenuitem.label"),
"class": "subviewbutton subviewbutton-iconic",
"oncommand": "openUILink(Pocket.listURL, event);",
"hidden": hidden
});
sib.parentNode.insertBefore(menu, sib);
}
},
onWidgetAfterDOMChange(aWidgetNode) {
if (aWidgetNode.id != "pocket-button") {
@ -419,11 +431,20 @@ var PocketOverlay = {
}
let doc = aWidgetNode.ownerDocument;
let hidden = !CustomizableUI.getPlacementOfWidget("pocket-button");
for (let prefix of ["panelMenu_", "menu_", "BMB_"]) {
let element = doc.getElementById(prefix + "pocket");
let elementIds = [
"panelMenu_pocket",
"menu_pocket",
"BMB_pocket",
"appMenu-library-pocket-button",
];
for (let elementId of elementIds) {
let element = doc.getElementById(elementId);
if (element) {
element.hidden = hidden;
doc.getElementById(prefix + "pocketSeparator").hidden = hidden;
let sep = doc.getElementById(elementId + "Separator");
if (sep) {
sep.hidden = hidden;
}
}
}
// enable or disable reader button

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

@ -0,0 +1,7 @@
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="context-fill" d="M4 7a4 4 0 0 1-4-4V1a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2a4 4 0 0 1-4 4zm1.993-5.016a.5.5 0 0 0-.362.159L3.989 3.785 2.378 2.168A.492.492 0 0 0 2 1.984a.5.5 0 0 0-.357.85l-.008.006 1.647 1.653.353.354a.5.5 0 0 0 .707 0l.358-.354L6.35 2.84a.5.5 0 0 0-.357-.856z"/>
<path fill="context-fill" d="M13 1h-3a1 1 0 0 0 0 2h3a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V8a1 1 0 0 0-2 0v4a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V4a3 3 0 0 0-3-3z"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 764 B

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

@ -51,6 +51,10 @@ toolbar[brighttext] #pocket-button {
}
}
#appMenu-library-pocket-button {
list-style-image: url("chrome://pocket-shared/skin/pocket-list.svg");
}
#panelMenu_pocket,
#menu_pocket,
#BMB_pocket {

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

@ -150,12 +150,11 @@ DOMIntersectionObserver::GetThresholds(nsTArray<double>& aRetVal)
void
DOMIntersectionObserver::Observe(Element& aTarget)
{
if (mObservationTargets.Contains(&aTarget)) {
return;
if (mObservationTargets.EnsureInserted(&aTarget)) {
// A new entry was created.
aTarget.RegisterIntersectionObserver(this);
Connect();
}
aTarget.RegisterIntersectionObserver(this);
mObservationTargets.PutEntry(&aTarget);
Connect();
}
void
@ -173,14 +172,11 @@ DOMIntersectionObserver::Unobserve(Element& aTarget)
void
DOMIntersectionObserver::UnlinkTarget(Element& aTarget)
{
if (!mObservationTargets.Contains(&aTarget)) {
return;
}
mObservationTargets.RemoveEntry(&aTarget);
if (mObservationTargets.Count() == 0) {
Disconnect();
}
if (mObservationTargets.EnsureRemoved(&aTarget) &&
mObservationTargets.Count() == 0) {
// We removed the last entry.
Disconnect();
}
}
void

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

@ -1309,6 +1309,7 @@ GK_ATOM(td, "td")
GK_ATOM(_template, "template")
GK_ATOM(text_decoration, "text-decoration")
GK_ATOM(terminate, "terminate")
GK_ATOM(term, "term")
GK_ATOM(test, "test")
GK_ATOM(text, "text")
GK_ATOM(textAlign, "text-align")

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

@ -195,7 +195,7 @@ static bool sNeedsFullCC = false;
static bool sNeedsFullGC = false;
static bool sNeedsGCAfterCC = false;
static bool sIncrementalCC = false;
static int32_t sActiveIntersliceGCBudget = 0; // ms;
static int32_t sActiveIntersliceGCBudget = 5; // ms;
static nsScriptNameSpaceManager *gNameSpaceManager;
static PRTime sFirstCollectionTime;

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

@ -324,9 +324,8 @@ nsWindowRoot::GetEnabledDisabledCommandsForControllers(nsIControllers* aControll
// Use a hash to determine which commands have already been handled by
// earlier controllers, as the earlier controller's result should get
// priority.
if (!aCommandsHandled.Contains(commands[e])) {
aCommandsHandled.PutEntry(commands[e]);
if (aCommandsHandled.EnsureInserted(commands[e])) {
// We inserted a new entry into aCommandsHandled.
bool enabled = false;
controller->IsCommandEnabled(commands[e], &enabled);

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

@ -4988,7 +4988,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
}
${typeName}::EntryType* entry;
if (idsSeen.Contains(propName)) {
if (!idsSeen.EnsureInserted(propName)) {
// Find the existing entry.
auto idx = recordEntries.IndexOf(propName);
MOZ_ASSERT(idx != recordEntries.NoIndex,
@ -5004,7 +5004,6 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
// Safe to do an infallible append here, because we did a
// SetCapacity above to the right capacity.
entry = recordEntries.AppendElement();
idsSeen.PutEntry(propName);
}
entry->mKey = propName;
${valueType}& slot = entry->mValue;

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

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<script>
var c = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
var cx = c.getContext('2d');
cx.setTransform(1, 2, 2, 4, 0.41577222277777554, 0.89);
cx.fillText("AA",2048,4);
</script>
</html>

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

@ -38,6 +38,7 @@ load 1290628-1.html
load 1283113-1.html
load 1286458-1.html
load 1299062-1.html
load 1305085-1.html
load 1305312-1.html
load 1298576-1.html
load 1334366-1.html

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

@ -9,6 +9,7 @@
#include "base/compiler_specific.h"
#include "mozilla/ArrayUtils.h"
#include "nsGkAtoms.h"
#include "nsITableCellLayout.h" // for MAX_COLSPAN / MAX_ROWSPAN
#include "nsCRT.h"
#include "nsLayoutStylesheetCache.h"
#include "nsRuleData.h"
@ -146,8 +147,10 @@ nsMathMLElement::ParseAttribute(int32_t aNamespaceID,
const nsAString& aValue,
nsAttrValue& aResult)
{
MOZ_ASSERT(IsMathMLElement());
if (aNamespaceID == kNameSpaceID_None) {
if (IsMathMLElement(nsGkAtoms::math) && aAttribute == nsGkAtoms::mode) {
if (mNodeInfo->Equals(nsGkAtoms::math) && aAttribute == nsGkAtoms::mode) {
WarnDeprecated(nsGkAtoms::mode->GetUTF16String(),
nsGkAtoms::display->GetUTF16String(), OwnerDoc());
}
@ -161,6 +164,16 @@ nsMathMLElement::ParseAttribute(int32_t aNamespaceID,
aAttribute == nsGkAtoms::mathbackground_) {
return aResult.ParseColor(aValue);
}
if (mNodeInfo->Equals(nsGkAtoms::mtd_)) {
if (aAttribute == nsGkAtoms::columnspan_) {
aResult.ParseClampedNonNegativeInt(aValue, 1, 1, MAX_COLSPAN);
return true;
}
if (aAttribute == nsGkAtoms::rowspan) {
aResult.ParseClampedNonNegativeInt(aValue, 1, 0, MAX_ROWSPAN);
return true;
}
}
}
return nsMathMLElementBase::ParseAttribute(aNamespaceID, aAttribute,
@ -205,6 +218,8 @@ static Element::MappedAttributeEntry sDirStyles[] = {
bool
nsMathMLElement::IsAttributeMapped(const nsIAtom* aAttribute) const
{
MOZ_ASSERT(IsMathMLElement());
static const MappedAttributeEntry* const mtableMap[] = {
sMtableStyles,
sCommonPresStyles
@ -236,10 +251,10 @@ nsMathMLElement::IsAttributeMapped(const nsIAtom* aAttribute) const
if (IsAnyOfMathMLElements(nsGkAtoms::mstyle_, nsGkAtoms::math))
return FindAttributeDependence(aAttribute, mstyleMap);
if (IsMathMLElement(nsGkAtoms::mtable_))
if (mNodeInfo->Equals(nsGkAtoms::mtable_))
return FindAttributeDependence(aAttribute, mtableMap);
if (IsMathMLElement(nsGkAtoms::mrow_))
if (mNodeInfo->Equals(nsGkAtoms::mrow_))
return FindAttributeDependence(aAttribute, mrowMap);
if (IsAnyOfMathMLElements(nsGkAtoms::maction_,

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

@ -23,6 +23,8 @@ fivee sixx<br>
<script class="testbody" type="application/javascript">
let {onSpellCheck} = SpecialPowers.Cu.import("resource://testing-common/AsyncSpellCheckTestHelper.jsm", {});
/** Test for Bug 1100966 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
@ -37,12 +39,14 @@ SimpleTest.waitForFocus(function() {
setTimeout(function() {
synthesizeKey("VK_BACK_SPACE", {});
var sel = getSpellCheckSelection();
is(sel.rangeCount, 2, "We should have two misspelled words");
is(String(sel.getRangeAt(0)), "fivee", "Correct misspelled word");
is(String(sel.getRangeAt(1)), "sixx", "Correct misspelled word");
onSpellCheck(div, function() {
var sel = getSpellCheckSelection();
is(sel.rangeCount, 2, "We should have two misspelled words");
is(String(sel.getRangeAt(0)), "fivee", "Correct misspelled word");
is(String(sel.getRangeAt(1)), "sixx", "Correct misspelled word");
SimpleTest.finish();
SimpleTest.finish();
});
},0);
},0);
});

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

@ -22,6 +22,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1154791
<script class="testbody" type="application/javascript">
let {onSpellCheck} = SpecialPowers.Cu.import("resource://testing-common/AsyncSpellCheckTestHelper.jsm", {});
/** Test for Bug 1154791 **/
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
@ -37,14 +39,14 @@ SimpleTest.waitForFocus(function() {
setTimeout(function() {
synthesizeKey(" ", {});
setTimeout(function() {
onSpellCheck(div, function() {
var sel = getSpellCheckSelection();
is(sel.rangeCount, 2, "We should have two misspelled words");
is(String(sel.getRangeAt(0)), "thiss", "Correct misspelled word");
is(String(sel.getRangeAt(1)), "onee", "Correct misspelled word");
SimpleTest.finish();
},0);
});
},0);
},0);
});

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

@ -21,6 +21,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=432225
/** Test for Bug 432225 **/
let {onSpellCheck} = SpecialPowers.Cu.import("resource://testing-common/AsyncSpellCheckTestHelper.jsm", {});
SimpleTest.waitForExplicitFinish();
addLoadEvent(runTest);
@ -60,7 +62,9 @@ function addWords(aLimit) {
getEdit().focus();
sendString('aa OK ');
gMisspeltWords.push("aa");
setTimeout(function() { addWords(aLimit-1); }, 0);
onSpellCheck(editDoc(), function() {
addWords(aLimit-1);
});
}
</script>
</pre>

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

@ -490,7 +490,8 @@ public:
nsresult Post()
{
return NS_DispatchToMainThread(this);
nsCOMPtr<nsIRunnable> runnable(this);
return NS_IdleDispatchToCurrentThread(runnable.forget(), 1000);
}
NS_IMETHOD Run() override

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

@ -19,8 +19,11 @@
#include "mozilla/layers/PLayerTransaction.h" // for PaintedLayerAttributes
namespace mozilla {
namespace layers {
namespace gfx {
class DrawEventRecorderMemory;
};
namespace layers {
class CompositableClient;
class ShadowableLayer;
class SpecificLayerAttributes;
@ -109,6 +112,15 @@ public:
protected:
void PaintThebes(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates);
void RecordThebes();
bool CanRecordLayer(ReadbackProcessor* aReadback);
bool HasMaskLayers();
already_AddRefed<gfx::DrawEventRecorderMemory> RecordPaintedLayer();
void ReplayPaintedLayer(DrawEventRecorderMemory* aRecorder);
bool EnsureContentClient();
uint32_t GetPaintFlags();
void UpdateContentClient(PaintState& aState);
bool UpdatePaintRegion(PaintState& aState);
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;

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

@ -760,12 +760,24 @@ bool SkScalerContextRec::computeMatrices(PreMatrixScale preMatrixScale, SkVector
// At this point, given GA, create s.
switch (preMatrixScale) {
case kFull_PreMatrixScale:
s->fX = SkScalarAbs(GA.get(SkMatrix::kMScaleX));
s->fY = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
case kFull_PreMatrixScale: {
SkScalar xScale = SkScalarAbs(GA.get(SkMatrix::kMScaleX));
SkScalar yScale = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
if (xScale <= SK_ScalarNearlyZero) {
xScale = SK_Scalar1;
}
if (yScale <= SK_ScalarNearlyZero) {
yScale = SK_Scalar1;
}
s->fX = xScale;
s->fY = yScale;
break;
}
case kVertical_PreMatrixScale: {
SkScalar yScale = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
if (yScale <= SK_ScalarNearlyZero) {
yScale = SK_Scalar1;
}
s->fX = yScale;
s->fY = yScale;
break;

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

@ -591,6 +591,10 @@ GetBlur(gfxContext* aDestinationCtx,
return nullptr;
}
if (RefPtr<SourceSurface> opt = destDT->OptimizeSourceSurface(boxShadow)) {
boxShadow = opt;
}
if (!useDestRect) {
CacheBlur(destDT, minSize, aBlurRadius, aCornerRadii, aShadowColor,
aOutBlurMargin, boxShadow);
@ -1133,6 +1137,10 @@ gfxAlphaBoxBlur::GetInsetBlur(const Rect& aOuterRect,
return nullptr;
}
if (RefPtr<SourceSurface> opt = aDestDrawTarget->OptimizeSourceSurface(minInsetBlur)) {
minInsetBlur = opt;
}
if (!aIsDestRect) {
CacheInsetBlur(outerSize, whitespaceSize,
aBlurRadius, aInnerClipRadii,

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

@ -3428,7 +3428,8 @@ ICCall_Native::Compiler::generateStubCode(MacroAssembler& masm)
// Load the callee in R1.
if (isSpread_) {
masm.loadValue(Address(masm.getStackPointer(), ICStackValueOffset + 2 * sizeof(Value)), R1);
unsigned skipToCallee = (2 + isConstructing_) * sizeof(Value);
masm.loadValue(Address(masm.getStackPointer(), skipToCallee + ICStackValueOffset), R1);
} else {
unsigned nonArgsSlots = (1 + isConstructing_) * sizeof(Value);
BaseValueIndex calleeSlot(masm.getStackPointer(), argcReg, ICStackValueOffset + nonArgsSlots);

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

@ -575,6 +575,12 @@ JS_NewDeadWrapper(JSContext* cx, JSObject* origObj)
return NewDeadProxyObject(cx, origObj);
}
JS_FRIEND_API(bool)
JS_IsScriptSourceObject(JSObject* obj)
{
return obj->is<ScriptSourceObject>();
}
void
js::TraceWeakMaps(WeakMapTracer* trc)
{

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

@ -106,6 +106,12 @@ JS_IsDeadWrapper(JSObject* obj);
extern JS_FRIEND_API(JSObject*)
JS_NewDeadWrapper(JSContext* cx, JSObject* origObject = nullptr);
/**
* Determine whether the given object is a ScriptSourceObject.
*/
extern JS_FRIEND_API(bool)
JS_IsScriptSourceObject(JSObject* obj);
/*
* Used by the cycle collector to trace through a shape or object group and
* all cycle-participating data it reaches, using bounded stack space.
@ -1383,6 +1389,10 @@ class MOZ_STACK_CLASS JS_FRIEND_API(AutoStableStringChars)
bool isLatin1() const { return state_ == Latin1; }
bool isTwoByte() const { return state_ == TwoByte; }
const JS::Latin1Char* latin1Chars() const {
MOZ_ASSERT(state_ == Latin1);
return latin1Chars_;
}
const char16_t* twoByteChars() const {
MOZ_ASSERT(state_ == TwoByte);
return twoByteChars_;

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

@ -2906,6 +2906,67 @@ CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObj
return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
}
template <typename TextChar>
static MOZ_ALWAYS_INLINE JSObject*
SplitSingleCharHelper(JSContext* cx, HandleLinearString str, const TextChar* text,
uint32_t textLen, char16_t patCh, HandleObjectGroup group)
{
// Count the number of occurrences of patCh within text.
uint32_t count = 0;
for (size_t index = 0; index < textLen; index++) {
if (static_cast<char16_t>(text[index]) == patCh)
count++;
}
// Handle zero-occurrence case - return input string in an array.
if (count == 0) {
RootedValue strValue(cx, StringValue(str.get()));
return NewCopiedArrayTryUseGroup(cx, group, &strValue.get(), 1);
}
// Reserve memory for substring values.
AutoValueVector splits(cx);
if (!splits.reserve(count + 1))
return nullptr;
// Add substrings.
size_t lastEndIndex = 0;
for (size_t index = 0; index < textLen; index++) {
if (static_cast<char16_t>(text[index]) == patCh) {
size_t subLength = size_t(index - lastEndIndex);
JSString* sub = NewDependentString(cx, str, lastEndIndex, subLength);
if (!sub || !splits.append(StringValue(sub)))
return nullptr;
lastEndIndex = index + 1;
}
}
// Add substring for tail of string (after last match).
JSString* sub = NewDependentString(cx, str, lastEndIndex, textLen - lastEndIndex);
if (!sub || !splits.append(StringValue(sub)))
return nullptr;
return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
}
// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
static JSObject*
SplitSingleCharHelper(JSContext* cx, HandleLinearString str, char16_t ch, HandleObjectGroup group) {
// Step 12.
size_t strLength = str->length();
AutoStableStringChars linearChars(cx);
if (!linearChars.init(cx, str))
return nullptr;
if (linearChars.isLatin1()) {
return SplitSingleCharHelper(cx, str, linearChars.latin1Chars(), strLength, ch, group);
} else {
return SplitSingleCharHelper(cx, str, linearChars.twoByteChars(), strLength, ch, group);
}
}
// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
JSObject*
js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep, uint32_t limit)
@ -2922,6 +2983,11 @@ js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, H
if (linearSep->length() == 0)
return CharSplitHelper(cx, linearStr, limit, group);
if (linearSep->length() == 1 && limit >= static_cast<uint32_t>(INT32_MAX)) {
char16_t ch = linearSep->latin1OrTwoByteChar(0);
return SplitSingleCharHelper(cx, linearStr, ch, group);
}
return SplitHelper(cx, linearStr, limit, linearSep, group);
}

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

@ -219,6 +219,26 @@ EvalScript(JSContext* cx,
nsCString uriStr;
if (preloadCache && NS_SUCCEEDED(uri->GetSpec(uriStr))) {
// Note that, when called during startup, this will keep the
// original JSScript object alive for an indefinite amount of time.
// This has the side-effect of keeping the global that the script
// was compiled for alive, too.
//
// For most startups, the global in question will be the
// CompilationScope, since we pre-compile any scripts that were
// needed during the last startup in that scope. But for startups
// when a non-cached script is used (e.g., after add-on
// installation), this may be a Sandbox global, which may be
// nuked but held alive by the JSScript.
//
// In general, this isn't a problem, since add-on Sandboxes which
// use the script preloader are not destroyed until add-on shutdown,
// and when add-ons are uninstalled or upgraded, the preloader cache
// is immediately flushed after shutdown. But it's possible to
// disable and reenable an add-on without uninstalling it, leading
// to cached scripts being held alive, and tied to nuked Sandbox
// globals. Given the unusual circumstances required to trigger
// this, it's not a major concern. But it should be kept in mind.
ScriptPreloader::GetSingleton().NoteScript(uriStr, cachePath, script);
}

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

@ -139,12 +139,8 @@ public:
nsresult Dispatch()
{
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
if (!thread) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIRunnable> self(this);
return thread->IdleDispatch(self.forget());
return NS_IdleDispatchToCurrentThread(self.forget(), 1000);
}
void Start(bool aContinuation = false, bool aPurge = false)

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

@ -0,0 +1,20 @@
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
add_task(function* test_executeScriptAfterNuked() {
let scriptUrl = Services.io.newFileURI(do_get_file("file_simple_script.js")).spec;
// Load the script for the first time into a sandbox, and then nuke
// that sandbox.
let sandbox = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal());
Services.scriptloader.loadSubScript(scriptUrl, sandbox);
Cu.nukeSandbox(sandbox);
// Load the script again into a new sandbox, and make sure it
// succeeds.
sandbox = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal());
Services.scriptloader.loadSubScript(scriptUrl, sandbox);
});

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

@ -99,6 +99,7 @@ skip-if = toolkit == "android" # bug 1347431
[test_nuke_sandbox.js]
[test_nuke_sandbox_event_listeners.js]
[test_nuke_webextension_wrappers.js]
[test_subScriptLoader.js]
[test_rewrap_dead_wrapper.js]
[test_sandbox_metadata.js]
[test_exportFunction.js]

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

@ -191,10 +191,14 @@ WrapperFactory::PrepareForWrapping(JSContext* cx, HandleObject scope,
// If we've somehow gotten to this point after either the source or target
// compartment has been nuked, return a DeadObjectProxy to prevent further
// access.
// However, we always need to provide live wrappers for ScriptSourceObjects,
// since they're used for cross-compartment cloned scripts, and need to
// remain accessible even after the original compartment has been nuked.
JSCompartment* origin = js::GetObjectCompartment(obj);
JSCompartment* target = js::GetObjectCompartment(scope);
if (CompartmentPrivate::Get(origin)->wasNuked ||
CompartmentPrivate::Get(target)->wasNuked) {
if (!JS_IsScriptSourceObject(obj) &&
(CompartmentPrivate::Get(origin)->wasNuked ||
CompartmentPrivate::Get(target)->wasNuked)) {
NS_WARNING("Trying to create a wrapper into or out of a nuked compartment");
retObj.set(JS_NewDeadWrapper(cx));
@ -352,7 +356,8 @@ static void
DEBUG_CheckUnwrapSafety(HandleObject obj, const js::Wrapper* handler,
JSCompartment* origin, JSCompartment* target)
{
if (CompartmentPrivate::Get(origin)->wasNuked || CompartmentPrivate::Get(target)->wasNuked) {
if (!JS_IsScriptSourceObject(obj) &&
(CompartmentPrivate::Get(origin)->wasNuked || CompartmentPrivate::Get(target)->wasNuked)) {
// If either compartment has already been nuked, we should have returned
// a dead wrapper from our prewrap callback, and this function should
// not be called.

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

@ -5768,10 +5768,8 @@ PresShell::MarkFramesInListApproximatelyVisible(const nsDisplayList& aList,
// Use the presshell containing the frame.
auto* presShell = static_cast<PresShell*>(frame->PresContext()->PresShell());
uint32_t count = presShell->mApproximatelyVisibleFrames.Count();
MOZ_ASSERT(!presShell->AssumeAllFramesVisible());
presShell->mApproximatelyVisibleFrames.PutEntry(frame);
if (presShell->mApproximatelyVisibleFrames.Count() > count) {
if (presShell->mApproximatelyVisibleFrames.EnsureInserted(frame)) {
// The frame was added to mApproximatelyVisibleFrames, so increment its visible count.
frame->IncApproximateVisibleCount();
}
@ -5909,9 +5907,7 @@ PresShell::MarkFramesInSubtreeApproximatelyVisible(nsIFrame* aFrame,
aFrame->StyleVisibility()->IsVisible() &&
(!aRemoveOnly || aFrame->GetVisibility() == Visibility::APPROXIMATELY_VISIBLE)) {
MOZ_ASSERT(!AssumeAllFramesVisible());
uint32_t count = mApproximatelyVisibleFrames.Count();
mApproximatelyVisibleFrames.PutEntry(aFrame);
if (mApproximatelyVisibleFrames.Count() > count) {
if (mApproximatelyVisibleFrames.EnsureInserted(aFrame)) {
// The frame was added to mApproximatelyVisibleFrames, so increment its visible count.
aFrame->IncApproximateVisibleCount();
}
@ -6233,9 +6229,8 @@ PresShell::EnsureFrameInApproximatelyVisibleList(nsIFrame* aFrame)
}
#endif
if (!mApproximatelyVisibleFrames.Contains(aFrame)) {
MOZ_ASSERT(!AssumeAllFramesVisible());
mApproximatelyVisibleFrames.PutEntry(aFrame);
if (mApproximatelyVisibleFrames.EnsureInserted(aFrame)) {
// We inserted a new entry.
aFrame->IncApproximateVisibleCount();
}
}
@ -6258,11 +6253,8 @@ PresShell::RemoveFrameFromApproximatelyVisibleList(nsIFrame* aFrame)
return;
}
uint32_t count = mApproximatelyVisibleFrames.Count();
mApproximatelyVisibleFrames.RemoveEntry(aFrame);
if (aFrame->TrackingVisibility() &&
mApproximatelyVisibleFrames.Count() < count) {
if (mApproximatelyVisibleFrames.EnsureRemoved(aFrame) &&
aFrame->TrackingVisibility()) {
// aFrame was in the hashtable, and we're still tracking its visibility,
// so we need to decrement its visible count.
aFrame->DecApproximateVisibleCount();

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

@ -239,10 +239,6 @@ public:
{
MOZ_ASSERT(NS_IsMainThread());
if (LastTickSkippedAnyPaints()) {
return TimeStamp::Now();
}
TimeStamp mostRecentRefresh = MostRecentRefresh();
TimeDuration refreshRate = GetTimerRate();
TimeStamp idleEnd = mostRecentRefresh + refreshRate;

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

@ -1161,47 +1161,6 @@ nsMathMLmtdFrame::Init(nsIContent* aContent,
RemoveStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
}
int32_t
nsMathMLmtdFrame::GetRowSpan()
{
int32_t rowspan = 1;
// Don't look at the content's rowspan if we're not an mtd or a pseudo cell.
if (mContent->IsMathMLElement(nsGkAtoms::mtd_) &&
!StyleContext()->GetPseudo()) {
nsAutoString value;
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rowspan, value);
if (!value.IsEmpty()) {
nsresult error;
rowspan = value.ToInteger(&error);
if (NS_FAILED(error) || rowspan < 0)
rowspan = 1;
rowspan = std::min(rowspan, MAX_ROWSPAN);
}
}
return rowspan;
}
int32_t
nsMathMLmtdFrame::GetColSpan()
{
int32_t colspan = 1;
// Don't look at the content's colspan if we're not an mtd or a pseudo cell.
if (mContent->IsMathMLElement(nsGkAtoms::mtd_) &&
!StyleContext()->GetPseudo()) {
nsAutoString value;
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::columnspan_, value);
if (!value.IsEmpty()) {
nsresult error;
colspan = value.ToInteger(&error);
if (NS_FAILED(error) || colspan <= 0 || colspan > MAX_COLSPAN)
colspan = 1;
}
}
return colspan;
}
nsresult
nsMathMLmtdFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,

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

@ -266,8 +266,6 @@ public:
nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists) override;
virtual int32_t GetRowSpan() override;
virtual int32_t GetColSpan() override;
virtual bool IsFrameOfType(uint32_t aFlags) const override
{
return nsTableCellFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML));

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

@ -2312,14 +2312,17 @@ ContainerState::AttemptToRecyclePaintedLayer(AnimatedGeometryRoot* aAnimatedGeom
const nsPoint& aTopLeft)
{
Layer* oldLayer = mLayerBuilder->GetOldLayerFor(aItem);
if (!oldLayer || !oldLayer->AsPaintedLayer() ||
!mPaintedLayersAvailableForRecycling.Contains(oldLayer->AsPaintedLayer())) {
if (!oldLayer || !oldLayer->AsPaintedLayer()) {
return nullptr;
}
// Try to recycle a layer
if (!mPaintedLayersAvailableForRecycling.EnsureRemoved(oldLayer->AsPaintedLayer())) {
// Not found.
return nullptr;
}
// Try to recycle the layer.
RefPtr<PaintedLayer> layer = oldLayer->AsPaintedLayer();
mPaintedLayersAvailableForRecycling.RemoveEntry(layer);
// Check if the layer hint has changed and whether or not the layer should
// be recreated because of it.
@ -4548,8 +4551,6 @@ FrameLayerBuilder::ComputeGeometryChangeForItem(DisplayItemData* aData)
return;
}
PaintedLayerItemsEntry* entry = mPaintedLayerItems.GetEntry(paintedLayer);
nsAutoPtr<nsDisplayItemGeometry> geometry;
PaintedDisplayItemLayerUserData* layerData =
@ -4611,6 +4612,7 @@ FrameLayerBuilder::ComputeGeometryChangeForItem(DisplayItemData* aData)
changedFrameInvalidations.IsEmpty() == 0) {
notifyRenderingChanged = false;
}
PaintedLayerItemsEntry* entry = mPaintedLayerItems.GetEntry(paintedLayer);
aData->mClip.AddOffsetAndComputeDifference(entry->mCommonClipCount,
shift, aData->mGeometry->ComputeInvalidationRegion(),
clip, entry->mLastCommonClipCount,

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

@ -725,16 +725,16 @@ FontFaceSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
// font entries if possible rather than creating new ones; set modified to
// true if we detect that rule ordering has changed, or if a new entry is
// created.
if (handledRules.Contains(aRules[i].mRule)) {
nsCSSFontFaceRule* rule = aRules[i].mRule;
if (!handledRules.EnsureInserted(rule)) {
// rule was already present in the hashtable
continue;
}
nsCSSFontFaceRule* rule = aRules[i].mRule;
RefPtr<FontFace> f = ruleFaceMap.Get(rule);
if (!f.get()) {
f = FontFace::CreateForRule(GetParentObject(), this, rule);
}
InsertRuleFontFace(f, aRules[i].mSheetType, oldRecords, modified);
handledRules.PutEntry(aRules[i].mRule);
}
for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) {

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

@ -6,6 +6,7 @@
#define CellData_h__
#include "nsISupports.h"
#include "nsITableCellLayout.h" // for MAX_COLSPAN / MAX_ROWSPAN
#include "nsCoord.h"
#include "mozilla/gfx/Types.h"
#include "mozilla/WritingModes.h"
@ -16,10 +17,6 @@ class nsCellMap;
class BCCellData;
#define MAX_ROWSPAN 65534 // the cellmap can not handle more.
#define MAX_COLSPAN 1000 // limit as IE and opera do. If this ever changes,
// change COL_SPAN_OFFSET/COL_SPAN_SHIFT accordingly.
/**
* Data stored by nsCellMap to rationalize rowspan and colspan cells.
*/

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

@ -7,6 +7,10 @@
#include "nsQueryFrame.h"
#define MAX_ROWSPAN 65534 // the cellmap can not handle more.
#define MAX_COLSPAN 1000 // limit as IE and opera do. If this ever changes,
// change COL_SPAN_OFFSET/COL_SPAN_SHIFT accordingly.
/**
* nsITableCellLayout
* interface for layout objects that act like table cells.

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

@ -709,16 +709,18 @@ nsTableCellFrame::GetCellBaseline() const
borderPadding;
}
int32_t nsTableCellFrame::GetRowSpan()
int32_t
nsTableCellFrame::GetRowSpan()
{
int32_t rowSpan=1;
nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
// Don't look at the content's rowspan if we're a pseudo cell
if (hc && !StyleContext()->GetPseudo()) {
const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::rowspan);
if (!StyleContext()->GetPseudo()) {
dom::Element* elem = mContent->AsElement();
const nsAttrValue* attr = elem->GetParsedAttr(nsGkAtoms::rowspan);
// Note that we don't need to check the tag name, because only table cells
// and table headers parse the "rowspan" attribute into an integer.
// (including MathML <mtd>) and table headers parse the "rowspan" attribute
// into an integer.
if (attr && attr->Type() == nsAttrValue::eInteger) {
rowSpan = attr->GetIntegerValue();
}
@ -726,16 +728,20 @@ int32_t nsTableCellFrame::GetRowSpan()
return rowSpan;
}
int32_t nsTableCellFrame::GetColSpan()
int32_t
nsTableCellFrame::GetColSpan()
{
int32_t colSpan=1;
nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
// Don't look at the content's colspan if we're a pseudo cell
if (hc && !StyleContext()->GetPseudo()) {
const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::colspan);
if (!StyleContext()->GetPseudo()) {
dom::Element* elem = mContent->AsElement();
const nsAttrValue* attr = elem->GetParsedAttr(
MOZ_UNLIKELY(elem->IsMathMLElement()) ? nsGkAtoms::columnspan_
: nsGkAtoms::colspan);
// Note that we don't need to check the tag name, because only table cells
// and table headers parse the "colspan" attribute into an integer.
// (including MathML <mtd>) and table headers parse the "colspan" attribute
// into an integer.
if (attr && attr->Type() == nsAttrValue::eInteger) {
colSpan = attr->GetIntegerValue();
}

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

@ -150,11 +150,11 @@ public:
/**
* return the cell's specified row span. this is what was specified in the
* content model or in the style info, and is always >= 1.
* content model or in the style info, and is always >= 0.
* to get the effective row span (the actual value that applies), use GetEffectiveRowSpan()
* @see nsTableFrame::GetEffectiveRowSpan()
*/
virtual int32_t GetRowSpan();
int32_t GetRowSpan();
// there is no set row index because row index depends on the cell's parent row only
@ -179,7 +179,7 @@ public:
* to get the effective col span (the actual value that applies), use GetEffectiveColSpan()
* @see nsTableFrame::GetEffectiveColSpan()
*/
virtual int32_t GetColSpan();
int32_t GetColSpan();
/** return the cell's column index (starting at 0 for the first column) */
virtual nsresult GetColIndex(int32_t &aColIndex) const override;

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

@ -100,7 +100,7 @@ protected:
};
// I420 buffer size macros
#define YSIZE(x,y) ((x)*(y))
#define YSIZE(x,y) (CheckedInt<int>(x)*(y))
#define CRSIZE(x,y) ((((x)+1) >> 1) * (((y)+1) >> 1))
#define I420SIZE(x,y) (YSIZE((x),(y)) + 2 * CRSIZE((x),(y)))
@ -295,20 +295,26 @@ protected:
if (aForceBlack) {
IntSize size = aImage->GetSize();
uint32_t yPlaneLen = YSIZE(size.width, size.height);
uint32_t cbcrPlaneLen = 2 * CRSIZE(size.width, size.height);
uint32_t length = yPlaneLen + cbcrPlaneLen;
CheckedInt<int> yPlaneLen = YSIZE(size.width, size.height);
// doesn't need to be CheckedInt, any overflow will be caught by YSIZE
int cbcrPlaneLen = 2 * CRSIZE(size.width, size.height);
CheckedInt<int> length = yPlaneLen + cbcrPlaneLen;
if (!yPlaneLen.isValid() || !length.isValid()) {
return;
}
// Send a black image.
auto pixelData = MakeUniqueFallible<uint8_t[]>(length);
auto pixelData = MakeUniqueFallible<uint8_t[]>(length.value());
if (pixelData) {
// YCrCb black = 0x10 0x80 0x80
memset(pixelData.get(), 0x10, yPlaneLen);
memset(pixelData.get(), 0x10, yPlaneLen.value());
// Fill Cb/Cr planes
memset(pixelData.get() + yPlaneLen, 0x80, cbcrPlaneLen);
memset(pixelData.get() + yPlaneLen.value(), 0x80, cbcrPlaneLen);
MOZ_MTLOG(ML_DEBUG, "Sending a black video frame");
VideoFrameConverted(Move(pixelData), length, size.width, size.height,
VideoFrameConverted(Move(pixelData), length.value(),
size.width, size.height,
mozilla::kVideoI420, 0);
}
return;
@ -363,11 +369,17 @@ protected:
}
IntSize size = aImage->GetSize();
// these don't need to be CheckedInt, any overflow will be caught by YSIZE
int half_width = (size.width + 1) >> 1;
int half_height = (size.height + 1) >> 1;
int c_size = half_width * half_height;
int buffer_size = YSIZE(size.width, size.height) + 2 * c_size;
auto yuv_scoped = MakeUniqueFallible<uint8[]>(buffer_size);
CheckedInt<int> buffer_size = YSIZE(size.width, size.height) + 2 * c_size;
if (!buffer_size.isValid()) {
return;
}
auto yuv_scoped = MakeUniqueFallible<uint8[]>(buffer_size.value());
if (!yuv_scoped) {
return;
}
@ -382,7 +394,7 @@ protected:
}
int rv;
int cb_offset = YSIZE(size.width, size.height);
int cb_offset = YSIZE(size.width, size.height).value();
int cr_offset = cb_offset + c_size;
switch (surf->GetFormat()) {
case SurfaceFormat::B8G8R8A8:
@ -413,7 +425,7 @@ protected:
}
MOZ_MTLOG(ML_DEBUG, "Sending an I420 video frame converted from " <<
Stringify(surf->GetFormat()));
VideoFrameConverted(Move(yuv_scoped), buffer_size, size.width, size.height, mozilla::kVideoI420, 0);
VideoFrameConverted(Move(yuv_scoped), buffer_size.value(), size.width, size.height, mozilla::kVideoI420, 0);
}
Atomic<int32_t, Relaxed> mLength;

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

@ -1409,7 +1409,7 @@ public abstract class GeckoApp extends GeckoActivity
"ToggleChrome:Show",
null);
Tabs.getInstance().attachToContext(this, mLayerView);
Tabs.getInstance().attachToContext(this, mLayerView, getAppEventDispatcher());
Tabs.registerOnTabsChangedListener(this);
// Use global layout state change to kick off additional initialization
@ -1503,7 +1503,7 @@ public abstract class GeckoApp extends GeckoActivity
// If we are doing a restore, send the parsed session data to Gecko.
if (!mIsRestoringActivity) {
EventDispatcher.getInstance().dispatch("Session:Restore", restoreMessage);
getAppEventDispatcher().dispatch("Session:Restore", restoreMessage);
}
// Make sure sessionstore.old is either updated or deleted as necessary.
@ -2480,6 +2480,7 @@ public abstract class GeckoApp extends GeckoActivity
super.onDestroy();
Tabs.unregisterOnTabsChangedListener(this);
Tabs.getInstance().detachFromContext();
if (mShutdownOnDestroy) {
GeckoApplication.shutdown(!mRestartOnShutdown ? null : new Intent(

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

@ -102,6 +102,7 @@ public class Tabs implements BundleEventListener {
private volatile boolean mInitialTabsAdded;
private Context mAppContext;
private EventDispatcher mEventDispatcher;
private LayerView mLayerView;
private ContentObserver mBookmarksContentObserver;
private PersistTabsRunnable mPersistTabsRunnable;
@ -166,18 +167,11 @@ public class Tabs implements BundleEventListener {
mPrivateClearColor = Color.RED;
}
public synchronized void attachToContext(Context context, LayerView layerView) {
public synchronized void attachToContext(Context context, LayerView layerView, EventDispatcher eventDispatcher) {
final Context appContext = context.getApplicationContext();
if (mAppContext == appContext) {
return;
}
if (mAppContext != null) {
// This should never happen.
Log.w(LOGTAG, "The application context has changed!");
}
mAppContext = appContext;
mEventDispatcher = eventDispatcher;
mLayerView = layerView;
mPrivateClearColor = ContextCompat.getColor(context, R.color.tabs_tray_grey_pressed);
mAccountManager = AccountManager.get(appContext);
@ -199,6 +193,10 @@ public class Tabs implements BundleEventListener {
}
}
public void detachFromContext() {
mLayerView = null;
}
/**
* Gets the tab count corresponding to the category and private state of the
* selected tab.
@ -333,6 +331,7 @@ public class Tabs implements BundleEventListener {
// Pass a message to Gecko to update tab state in BrowserApp.
final GeckoBundle data = new GeckoBundle(1);
data.putInt("id", tab.getId());
mEventDispatcher.dispatch("Tab:Selected", data);
EventDispatcher.getInstance().dispatch("Tab:Selected", data);
return tab;
}
@ -474,7 +473,7 @@ public class Tabs implements BundleEventListener {
final GeckoBundle data = new GeckoBundle(2);
data.putInt("tabId", tabId);
data.putBoolean("showUndoToast", showUndoToast);
EventDispatcher.getInstance().dispatch("Tab:Closed", data);
mEventDispatcher.dispatch("Tab:Closed", data);
}
/** Return the tab that will be selected by default after this one is closed */
@ -1088,7 +1087,7 @@ public class Tabs implements BundleEventListener {
}
}
EventDispatcher.getInstance().dispatch("Tab:Load", data);
mEventDispatcher.dispatch("Tab:Load", data);
if (tabToSelect == null) {
return null;
@ -1282,7 +1281,7 @@ public class Tabs implements BundleEventListener {
data.putInt("fromPosition", fromPosition);
data.putInt("toTabId", toTabId);
data.putInt("toPosition", toPosition);
EventDispatcher.getInstance().dispatch("Tab:Move", data);
mEventDispatcher.dispatch("Tab:Move", data);
}
/**

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

@ -28,6 +28,7 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import org.mozilla.gecko.GeckoView;
import org.mozilla.gecko.R;
import org.mozilla.gecko.SiteIdentity;
import org.mozilla.gecko.Tab;
@ -106,25 +107,24 @@ public class ActionBarPresenter {
* @param url Url String to display
*/
public void displayUrlOnly(@NonNull final String url) {
updateCustomView(null, null, url);
updateCustomView(null, url, GeckoView.ProgressListener.STATE_IS_INSECURE);
}
/**
* Update appearance of CustomView of ActionBar.
* Update appearance of CustomView of ActionBar
*
* @param tab a Tab instance of current web page to provide information to render ActionBar.
* @param title Title for current website. Could be null if don't want to show title.
* @param url URL for current website. At least Custom will show this url.
* @param securityStatus Security status, possible values given in GeckoView.ProgressListener
*/
public void update(@NonNull final Tab tab) {
final String title = tab.getTitle();
final String url = tab.getBaseDomain();
public void update(final String title, final String url, final int securityStatus) {
// Do not update CustomView immediately. If this method be invoked rapidly several times,
// only apply last one.
mHandler.removeCallbacks(mUpdateAction);
mUpdateAction = new Runnable() {
@Override
public void run() {
updateCustomView(tab.getSiteIdentity(), title, url);
updateCustomView(title, url, securityStatus);
}
};
mHandler.postDelayed(mUpdateAction, CUSTOM_VIEW_UPDATE_DELAY);
@ -218,25 +218,15 @@ public class ActionBarPresenter {
* @param url URL for current website. At least Custom will show this url.
*/
@UiThread
private void updateCustomView(@Nullable SiteIdentity identity,
@Nullable String title,
@NonNull String url) {
// update site-info icon
if (identity == null) {
mIconView.setVisibility(View.INVISIBLE);
} else {
final SecurityModeUtil.IconType type = SecurityModeUtil.resolve(identity);
private void updateCustomView(final String title, final String url, final int securityStatus) {
if (securityStatus == GeckoView.ProgressListener.STATE_IS_SECURE) {
mIconView.setVisibility(View.VISIBLE);
mIconView.setImageLevel(SecurityModeUtil.getImageLevel(type));
mIdentityPopup.setSiteIdentity(identity);
if (type == SecurityModeUtil.IconType.LOCK_SECURE) {
// Lock-Secure is special case. Keep its original green color.
DrawableCompat.setTintList(mIconView.getDrawable(), null);
} else {
// Icon use same color as TextView.
DrawableCompat.setTint(mIconView.getDrawable(), mTextPrimaryColor);
}
mIconView.setImageLevel(SecurityModeUtil.getImageLevel(SecurityModeUtil.IconType.LOCK_SECURE));
// Lock-Secure is special case. Keep its original green color.
DrawableCompat.setTintList(mIconView.getDrawable(), null);
} else {
mIconView.setVisibility(View.INVISIBLE);
DrawableCompat.setTint(mIconView.getDrawable(), mTextPrimaryColor);
}
// If no title to use, use Url as title

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

@ -20,7 +20,7 @@ import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.util.SparseArrayCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.view.ActionMode;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
@ -28,17 +28,14 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoView;
import org.mozilla.gecko.GeckoViewSettings;
import org.mozilla.gecko.R;
import org.mozilla.gecko.SingleTabActivity;
import org.mozilla.gecko.SnackbarBuilder;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.menu.GeckoMenuInflater;
import org.mozilla.gecko.mozglue.SafeIntent;
@ -46,14 +43,15 @@ import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.ColorUtil;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.IntentUtils;
import org.mozilla.gecko.widget.ActionModePresenter;
import org.mozilla.gecko.widget.GeckoPopupMenu;
import java.util.List;
import static org.mozilla.gecko.Tabs.TabEvents;
public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabsChangedListener {
public class CustomTabsActivity extends AppCompatActivity
implements GeckoMenu.Callback,
GeckoView.ContentListener,
GeckoView.NavigationListener,
GeckoView.ProgressListener {
private static final String LOGTAG = "CustomTabsActivity";
@ -61,21 +59,30 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
private GeckoPopupMenu popupMenu;
private View doorhangerOverlay;
private ActionBarPresenter actionBarPresenter;
private ProgressBar mProgressView;
// A state to indicate whether this activity is finishing with customize animation
private boolean usingCustomAnimation = false;
private MenuItem menuItemControl;
private GeckoView mGeckoView;
private boolean mCanGoBack = false;
private boolean mCanGoForward = false;
private boolean mCanStop = false;
private String mCurrentUrl;
private String mCurrentTitle;
private int mSecurityStatus = GeckoView.ProgressListener.STATE_IS_INSECURE;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.customtabs_activity);
final SafeIntent intent = new SafeIntent(getIntent());
doorhangerOverlay = findViewById(R.id.custom_tabs_doorhanger_overlay);
mProgressView = (ProgressBar) findViewById(R.id.page_progress);
final Toolbar toolbar = (Toolbar) findViewById(R.id.actionbar);
setSupportActionBar(toolbar);
final ActionBar actionBar = getSupportActionBar();
@ -85,26 +92,22 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
actionBarPresenter.displayUrlOnly(intent.getDataString());
actionBarPresenter.setBackgroundColor(IntentUtil.getToolbarColor(intent), getWindow());
actionBarPresenter.setTextLongClickListener(new UrlCopyListener());
}
@Override
protected void onTabOpenFromIntent(Tab tab) {
super.onTabOpenFromIntent(tab);
mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
final String host = getReferrerHost();
recordCustomTabUsage(host);
sendTelemetry();
}
mGeckoView.setNavigationListener(this);
mGeckoView.setProgressListener(this);
mGeckoView.setContentListener(this);
@Override
protected void onTabSelectFromIntent(Tab tab) {
super.onTabSelectFromIntent(tab);
final GeckoViewSettings settings = mGeckoView.getSettings();
settings.setBoolean(GeckoViewSettings.USE_MULTIPROCESS, false);
// We already listen for SELECTED events, but if the activity has been destroyed and
// subsequently recreated without a different tab having been selected in Gecko in the
// meantime, our startup won't trigger a SELECTED event because the selected tab in Gecko
// doesn't actually change.
actionBarPresenter.update(tab);
if (intent != null && !TextUtils.isEmpty(intent.getDataString())) {
mGeckoView.loadUri(intent.getDataString());
} else {
Log.w(LOGTAG, "No intend found for custom tab");
finish();
}
}
private void sendTelemetry() {
@ -154,13 +157,6 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
}
}
@Override
public void onDone() {
// We're most probably running within a foreign app's task, so we have no choice what to
// call here if we want to allow the user to return to that task's previous activity.
finish();
}
@Override
public void finish() {
super.finish();
@ -176,63 +172,12 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
}
@Override
protected int getNewTabFlags() {
return Tabs.LOADURL_CUSTOMTAB | super.getNewTabFlags();
}
@Override
public int getLayout() {
return R.layout.customtabs_activity;
}
@Override
public View getDoorhangerOverlay() {
return doorhangerOverlay;
}
@Override
public void onTabChanged(Tab tab, TabEvents msg, String data) {
super.onTabChanged(tab, msg, data);
if (!Tabs.getInstance().isSelectedTab(tab) ||
tab.getType() != Tab.TabType.CUSTOMTAB) {
return;
public void onBackPressed() {
if (mCanGoBack) {
mGeckoView.goBack();
} else {
finish();
}
if (msg == TabEvents.START
|| msg == TabEvents.STOP
|| msg == TabEvents.ADDED
|| msg == TabEvents.LOAD_ERROR
|| msg == TabEvents.LOADED
|| msg == TabEvents.LOCATION_CHANGE
|| msg == TabEvents.SELECTED) {
updateProgress((tab.getState() == Tab.STATE_LOADING),
tab.getLoadProgress());
}
if (msg == TabEvents.LOCATION_CHANGE
|| msg == TabEvents.SECURITY_CHANGE
|| msg == TabEvents.TITLE
|| msg == TabEvents.SELECTED) {
actionBarPresenter.update(tab);
}
updateMenuItemForward();
}
@Override
public void onResume() {
super.onResume();
mLayerView.getDynamicToolbarAnimator().setPinned(true, PinReason.CUSTOM_TAB);
actionBarPresenter.onResume();
}
@Override
public void onPause() {
super.onPause();
mLayerView.getDynamicToolbarAnimator().setPinned(false, PinReason.CUSTOM_TAB);
actionBarPresenter.onPause();
}
// Usually should use onCreateOptionsMenu() to initialize menu items. But GeckoApp overwrite
@ -240,7 +185,7 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
// and this.onPrepareOptionsMenu() are different instances - GeckoApp.onCreatePanelMenu() changed it.
// CustomTabsActivity only use standard menu in ActionBar, so initialize menu here.
@Override
public boolean onCreatePanelMenu(final int id, final Menu menu) {
public boolean onCreateOptionsMenu(final Menu menu) {
// if 3rd-party app asks to add an action button
SafeIntent intent = new SafeIntent(getIntent());
@ -275,6 +220,16 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
return true;
}
@Override
public boolean onMenuItemClick(MenuItem item) {
return onOptionsItemSelected(item);
}
@Override
public boolean onMenuItemLongClick(MenuItem item) {
return false;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@ -317,34 +272,11 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
performPendingIntent(intent);
}
@Override
protected ActionModePresenter getTextSelectPresenter() {
return new ActionModePresenter() {
private ActionMode mMode;
@Override
public void startActionMode(ActionMode.Callback callback) {
mMode = startSupportActionMode(callback);
}
@Override
public void endActionMode() {
if (mMode != null) {
mMode.finish();
}
}
};
}
private void bindNavigationCallback(@NonNull final Toolbar toolbar) {
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onDone();
final Tabs tabs = Tabs.getInstance();
final Tab tab = tabs.getSelectedTab();
mSuppressActivitySwitch = true;
tabs.closeTab(tab);
finish();
}
});
}
@ -352,8 +284,7 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
private void performPendingIntent(@NonNull PendingIntent pendingIntent) {
// bug 1337771: If intent-creator haven't set data url, call send() directly won't work.
final Intent additional = new Intent();
final Tab tab = Tabs.getInstance().getSelectedTab();
additional.setData(Uri.parse(tab.getURL()));
additional.setData(Uri.parse(mCurrentUrl));
try {
pendingIntent.send(this, 0, additional);
} catch (PendingIntent.CanceledException e) {
@ -435,49 +366,45 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
}
final MenuItem forwardMenuItem = popupMenu.getMenu().findItem(R.id.custom_tabs_menu_forward);
final Tab tab = Tabs.getInstance().getSelectedTab();
final boolean enabled = (tab != null && tab.canDoForward());
forwardMenuItem.setEnabled(enabled);
forwardMenuItem.setEnabled(mCanGoForward);
}
/**
* Update loading progress of current page
*
* @param isLoading to indicate whether ProgressBar should be visible or not
* @param progress value of loading progress in percent, should be 0 - 100.
* Update loading status of current page
*/
private void updateProgress(final boolean isLoading, final int progress) {
if (isLoading) {
mProgressView.setVisibility(View.VISIBLE);
mProgressView.setProgress(progress);
} else {
mProgressView.setVisibility(View.GONE);
}
private void updateCanStop() {
if (menuItemControl != null) {
Drawable icon = menuItemControl.getIcon();
icon.setLevel(progress);
if (mCanStop) {
icon.setLevel(0);
} else {
icon.setLevel(100);
}
}
}
/**
* Update the state of the action bar
*/
private void updateActionBar() {
actionBarPresenter.update(mCurrentTitle, mCurrentUrl, mSecurityStatus);
}
/**
* Call this method to reload page, or stop page loading if progress not complete yet.
*/
private void onLoadingControlClicked() {
final Tab tab = Tabs.getInstance().getSelectedTab();
if (tab != null) {
if (tab.getLoadProgress() == Tab.LOAD_PROGRESS_STOP) {
tab.doReload(true);
} else {
tab.doStop();
}
if (mCanStop) {
// TODO: enable this after implementing GeckoView.stop()
//mGeckoView.stop();
} else {
mGeckoView.reload();
}
}
private void onForwardClicked() {
final Tab tab = Tabs.getInstance().getSelectedTab();
if ((tab != null) && tab.canDoForward()) {
tab.doForward();
if (mCanGoForward) {
mGeckoView.goForward();
}
}
@ -485,15 +412,11 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
* Callback for Open-in menu item.
*/
private void onOpenInClicked() {
final Tab tab = Tabs.getInstance().getSelectedTab();
if (tab != null) {
// To launch default browser with url of current tab.
final Intent intent = new Intent();
intent.setData(Uri.parse(tab.getURL()));
intent.setAction(Intent.ACTION_VIEW);
startActivity(intent);
finish();
}
final Intent intent = new Intent();
intent.setData(Uri.parse(mCurrentUrl));
intent.setAction(Intent.ACTION_VIEW);
startActivity(intent);
finish();
}
private void onActionButtonClicked() {
@ -507,12 +430,10 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
* Callback for Share menu item.
*/
private void onShareClicked() {
final String url = Tabs.getInstance().getSelectedTab().getURL();
if (!TextUtils.isEmpty(url)) {
if (!TextUtils.isEmpty(mCurrentUrl)) {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, url);
shareIntent.putExtra(Intent.EXTRA_TEXT, mCurrentUrl);
Intent chooserIntent = Intent.createChooser(shareIntent, getString(R.string.share_title));
chooserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@ -526,9 +447,8 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
private class UrlCopyListener implements View.OnLongClickListener {
@Override
public boolean onLongClick(View v) {
final String url = Tabs.getInstance().getSelectedTab().getURL();
if (!TextUtils.isEmpty(url)) {
Clipboard.setText(url);
if (!TextUtils.isEmpty(mCurrentUrl)) {
Clipboard.setText(mCurrentUrl);
SnackbarBuilder.builder(CustomTabsActivity.this)
.message(R.string.custom_tabs_hint_url_copy)
.duration(Snackbar.LENGTH_SHORT)
@ -554,4 +474,59 @@ public class CustomTabsActivity extends SingleTabActivity implements Tabs.OnTabs
}
return null;
}
/* GeckoView.NavigationListener */
@Override
public void onLocationChange(GeckoView view, String url) {
mCurrentUrl = url;
updateActionBar();
}
@Override
public void onCanGoBack(GeckoView view, boolean canGoBack) {
mCanGoBack = canGoBack;
}
@Override
public void onCanGoForward(GeckoView view, boolean canGoForward) {
mCanGoForward = canGoForward;
updateMenuItemForward();
}
/* GeckoView.ProgressListener */
@Override
public void onPageStart(GeckoView view, String url) {
mCurrentUrl = url;
mCanStop = true;
updateActionBar();
updateCanStop();
}
@Override
public void onPageStop(GeckoView view, boolean success) {
mCanStop = false;
updateCanStop();
}
@Override
public void onSecurityChange(GeckoView view, int status) {
if ((status & STATE_IS_INSECURE) != 0) {
mSecurityStatus = STATE_IS_INSECURE;
} else if ((status & STATE_IS_BROKEN) != 0) {
mSecurityStatus = STATE_IS_BROKEN;
} else if ((status & STATE_IS_SECURE) != 0) {
mSecurityStatus = STATE_IS_SECURE;
}
updateActionBar();
}
/* GeckoView.ContentListener */
@Override
public void onTitleChange(GeckoView view, String title) {
mCurrentTitle = title;
updateActionBar();
}
@Override
public void onFullScreen(GeckoView view, boolean fullScreen) {}
}

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

@ -7,15 +7,17 @@ package org.mozilla.gecko.webapps;
import java.io.File;
import java.io.IOException;
import java.util.List;
import android.app.ActivityManager;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.Toolbar;
import android.support.v7.app.ActionBar;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@ -30,6 +32,8 @@ import org.json.JSONException;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoView;
import org.mozilla.gecko.GeckoViewSettings;
import org.mozilla.gecko.SingleTabActivity;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
@ -48,20 +52,22 @@ import org.mozilla.gecko.widget.AnchoredPopup;
import static org.mozilla.gecko.Tabs.TabEvents;
public class WebAppActivity extends SingleTabActivity {
public class WebAppActivity extends AppCompatActivity
implements GeckoView.NavigationListener {
private static final String LOGTAG = "WebAppActivity";
public static final String MANIFEST_PATH = "MANIFEST_PATH";
private static final String SAVED_INTENT = "savedIntent";
private TextView mUrlView;
private View doorhangerOverlay;
private GeckoView mGeckoView;
private Uri mScope;
@Override
public void onCreate(Bundle savedInstanceState) {
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0 &&
savedInstanceState != null) {
savedInstanceState != null) {
// Even though we're a single task activity, Android's task switcher has the
// annoying habit of never updating its stored intent after our initial creation,
// even if we've been subsequently started with a new intent.
@ -75,73 +81,33 @@ public class WebAppActivity extends SingleTabActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.customtabs_activity);
final Toolbar toolbar = (Toolbar) findViewById(R.id.actionbar);
setSupportActionBar(toolbar);
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.page_progress);
progressBar.setVisibility(View.GONE);
final ActionBar actionBar = getSupportActionBar();
actionBar.setCustomView(R.layout.webapps_action_bar_custom_view);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.hide();
doorhangerOverlay = findViewById(R.id.custom_tabs_doorhanger_overlay);
final View customView = actionBar.getCustomView();
mUrlView = (TextView) customView.findViewById(R.id.webapps_action_bar_url);
EventDispatcher.getInstance().registerUiThreadListener(this,
"Website:AppEntered",
"Website:AppLeft",
null);
}
mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
@Override
public View getDoorhangerOverlay() {
return doorhangerOverlay;
}
mGeckoView.setNavigationListener(this);
@Override
public int getLayout() {
return R.layout.customtabs_activity;
}
final GeckoViewSettings settings = mGeckoView.getSettings();
settings.setBoolean(GeckoViewSettings.USE_MULTIPROCESS, false);
@Override
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
super.handleMessage(event, message, callback);
if (message == null ||
!message.containsKey("tabId") || message.getInt("tabId") != mLastSelectedTabId) {
return;
final Uri u = getIntent().getData();
if (u != null) {
mGeckoView.loadUri(u.toString());
}
switch (event) {
case "Website:AppEntered":
getSupportActionBar().hide();
break;
case "Website:AppLeft":
getSupportActionBar().show();
break;
}
}
@Override
public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) {
super.onTabChanged(tab, msg, data);
if (tab == null || !Tabs.getInstance().isSelectedTab(tab) ||
tab.getType() != Tab.TabType.WEBAPP) {
return;
}
if (msg == TabEvents.LOCATION_CHANGE ||
msg == TabEvents.SELECTED) {
mUrlView.setText(tab.getURL());
}
loadManifest(getIntent().getStringExtra(MANIFEST_PATH));
}
@Override
@ -151,75 +117,6 @@ public class WebAppActivity extends SingleTabActivity {
outState.putParcelable(SAVED_INTENT, getIntent());
}
@Override
public void onDestroy() {
super.onDestroy();
EventDispatcher.getInstance().unregisterUiThreadListener(this,
"Website:AppEntered",
"Website:AppLeft",
null);
}
@Override
protected int getNewTabFlags() {
return Tabs.LOADURL_WEBAPP | super.getNewTabFlags();
}
@Override
protected void onTabOpenFromIntent(Tab tab) {
super.onTabOpenFromIntent(tab);
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "webapp");
loadManifest(tab.getManifestPath());
}
/**
* In case this activity and its tab are reused (the user has opened
* > 10 current web apps), we check that app launched is still within
* the same host as the intent has set.
* If it isn't, we reload the intent URL.
*/
@Override
protected void onTabSelectFromIntent(Tab tab) {
super.onTabSelectFromIntent(tab);
SafeIntent intent = new SafeIntent(getIntent());
final String launchUrl = intent.getDataString();
final String currentUrl = tab.getURL();
final boolean isSameDomain = Uri.parse(currentUrl).getHost()
.equals(Uri.parse(launchUrl).getHost());
final String manifestPath;
if (!isSameDomain) {
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "webapp");
manifestPath = intent.getStringExtra(MANIFEST_PATH);
tab.setManifestUrl(manifestPath);
Tabs.getInstance().loadUrl(launchUrl);
} else {
manifestPath = tab.getManifestPath();
}
loadManifest(manifestPath);
}
@Override
protected ActionModePresenter getTextSelectPresenter() {
return new ActionModePresenter() {
private ActionMode mMode;
@Override
public void startActionMode(ActionMode.Callback callback) {
mMode = startSupportActionMode(callback);
}
@Override
public void endActionMode() {
if (mMode != null) {
mMode.finish();
}
}
};
}
private void loadManifest(String manifestPath) {
if (TextUtils.isEmpty(manifestPath)) {
Log.e(LOGTAG, "Missing manifest");
@ -237,6 +134,7 @@ public class WebAppActivity extends SingleTabActivity {
final Integer color = readColorFromManifest(manifestField);
final String name = readNameFromManifest(manifestField);
final Bitmap icon = readIconFromManifest(manifest);
mScope = readScopeFromManifest(manifest, manifestPath);
final ActivityManager.TaskDescription taskDescription = (color == null)
? new ActivityManager.TaskDescription(name, icon)
: new ActivityManager.TaskDescription(name, icon, color);
@ -288,4 +186,70 @@ public class WebAppActivity extends SingleTabActivity {
}
return loadIconResult.getBestBitmap(GeckoAppShell.getPreferredIconSize());
}
private Uri readScopeFromManifest(JSONObject manifest, String manifestPath) {
final String scopeStr = manifest.optString("scope", null);
if (scopeStr == null) {
return null;
}
Uri res = Uri.parse(scopeStr);
if (res.isRelative()) {
// TODO: Handle this more correctly.
return null;
}
return res;
}
private boolean isInScope(String url) {
if (mScope == null) {
return true;
}
final Uri uri = Uri.parse(url);
if (!uri.getScheme().equals(mScope.getScheme())) {
return false;
}
if (!uri.getHost().equals(mScope.getHost())) {
return false;
}
final List<String> scopeSegments = mScope.getPathSegments();
final List<String> urlSegments = uri.getPathSegments();
if (scopeSegments.size() > urlSegments.size()) {
return false;
}
for (int i = 0; i < scopeSegments.size(); i++) {
if (!scopeSegments.get(i).equals(urlSegments.get(i))) {
return false;
}
}
return true;
}
/* GeckoView.NavigationListener */
@Override
public void onLocationChange(GeckoView view, String url) {
if (isInScope(url)) {
getSupportActionBar().hide();
} else {
getSupportActionBar().show();
}
mUrlView.setText(url);
}
@Override
public void onCanGoBack(GeckoView view, boolean canGoBack) {
}
@Override
public void onCanGoForward(GeckoView view, boolean canGoForward) {
}
}

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

@ -12,11 +12,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--
This layout is quite complex because GeckoApp accesses all view groups
in this tree. In a perfect world this should just include a GeckoView.
-->
<android.support.v7.widget.Toolbar
android:id="@id/actionbar"
android:layout_width="match_parent"
@ -25,48 +20,18 @@
android:background="@color/text_and_tabs_tray_grey"
app:layout_scrollFlags="scroll|enterAlways"/>
<view class="org.mozilla.gecko.GeckoApp$MainLayout"
android:id="@+id/main_layout"
android:layout_width="match_parent"
<org.mozilla.gecko.GeckoView
android:id="@+id/gecko_view"
android:layout_width="fill_parent"
android:layout_below="@id/actionbar"
android:layout_height="match_parent"
android:background="@android:color/transparent">
<RelativeLayout android:id="@+id/gecko_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tablet_tab_strip"
android:layout_above="@+id/find_in_page">
<fragment class="org.mozilla.gecko.GeckoViewFragment"
android:id="@+id/layer_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"/>
<org.mozilla.gecko.FormAssistPopup android:id="@+id/form_assist_popup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
</RelativeLayout>
</view>
<ProgressBar
android:id="@id/page_progress"
style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="4dp"
android:layout_alignTop="@id/main_layout"
android:background="@drawable/url_bar_bg"
android:progressDrawable="@drawable/progressbar"
tools:progress="70"/>
android:scrollbars="none"/>
<View android:id="@+id/custom_tabs_doorhanger_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/dark_transparent_overlay"
android:alpha="0"
android:layerType="hardware"/>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/dark_transparent_overlay"
android:alpha="0"
android:layerType="hardware"/>
</RelativeLayout>

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

@ -87,7 +87,7 @@ var FindHelper = {
this._initialViewport = JSON.stringify(this._targetTab.getViewport());
this._viewportChanged = false;
GlobalEventDispatcher.registerListener(this, [
WindowEventDispatcher.registerListener(this, [
"Tab:Selected",
]);
},
@ -109,7 +109,7 @@ var FindHelper = {
this._initialViewport = null;
this._viewportChanged = false;
GlobalEventDispatcher.unregisterListener(this, [
WindowEventDispatcher.unregisterListener(this, [
"Tab:Selected",
]);
},

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

@ -388,11 +388,15 @@ var BrowserApp = {
Services.androidBridge.browserApp = this;
GlobalEventDispatcher.registerListener(this, [
WindowEventDispatcher.registerListener(this, [
"Session:Restore",
"Tab:Load",
"Tab:Selected",
"Tab:Closed",
"Tab:Move",
]);
GlobalEventDispatcher.registerListener(this, [
"Browser:LoadManifest",
"Browser:Quit",
"Fonts:Reload",
@ -1850,6 +1854,10 @@ var BrowserApp = {
break;
}
case "Session:Restore":
GlobalEventDispatcher.dispatch("Session:Restore", data);
break;
case "Session:Stop":
browser.stop();
break;

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

@ -7,7 +7,7 @@
<window id="main-window"
onload="startup();"
windowtype="navigator:browser"
windowtype="navigator:geckoview"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<browser id="content" type="content" primary="true" src="about:blank" flex="1"/>

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

@ -599,6 +599,13 @@ public class GeckoView extends LayerView {
throw new IllegalArgumentException("Must import script from 'resources://android/assets/' location.");
}
/**
* Exits fullscreen mode
*/
public void exitFullScreen() {
mEventDispatcher.dispatch("GeckoViewContent:ExitFullScreen", null);
}
/**
* Set the content callback handler.
* This will replace the current handler.

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

@ -28,8 +28,21 @@ class GeckoViewContent extends GeckoViewModule {
/* capture */ true, /* untrusted */ false);
this.window.addEventListener("MozDOMFullscreen:Exited", this,
/* capture */ true, /* untrusted */ false);
this.eventDispatcher.registerListener(this, ["GeckoViewContent:ExitFullScreen"]);
}
// Bundle event handler.
onEvent(aEvent, aData, aCallback) {
debug("onEvent: " + aEvent);
switch (aEvent) {
case "GeckoViewContent:ExitFullScreen":
this.messageManager.sendAsyncMessage("GeckoView:DOMFullscreenExited");
break;
}
}
// DOM event handler
handleEvent(aEvent) {
debug("handleEvent: aEvent.type=" + aEvent.type);
@ -37,11 +50,11 @@ class GeckoViewContent extends GeckoViewModule {
case "MozDOMFullscreen:Entered":
if (this.browser == aEvent.target) {
// Remote browser; dispatch to content process.
this.browser.messageManager.sendAsyncMessage("GeckoView:DOMFullscreenEntered");
this.messageManager.sendAsyncMessage("GeckoView:DOMFullscreenEntered");
}
break;
case "MozDOMFullscreen:Exited":
this.browser.messageManager.sendAsyncMessage("GeckoView:DOMFullscreenExited");
this.messageManager.sendAsyncMessage("GeckoView:DOMFullscreenExited");
break;
}
}

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

@ -440,6 +440,17 @@ WebSocketEventService::RemoveListener(uint64_t aInnerWindowID,
return NS_OK;
}
NS_IMETHODIMP
WebSocketEventService::HasListenerFor(uint64_t aInnerWindowID,
bool* aResult)
{
MOZ_ASSERT(NS_IsMainThread());
*aResult = mWindows.Get(aInnerWindowID);
return NS_OK;
}
NS_IMETHODIMP
WebSocketEventService::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)

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

@ -76,4 +76,6 @@ interface nsIWebSocketEventService : nsISupports
[must_use] void removeListener(in unsigned long long aInnerWindowID,
in nsIWebSocketEventListener aListener);
[must_use] bool hasListenerFor(in unsigned long long aInnerWindowID);
};

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

@ -933,11 +933,7 @@ nsHtml5TreeOpExecutor::ShouldPreloadURI(nsIURI *aURI)
nsAutoCString spec;
nsresult rv = aURI->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, false);
if (mPreloadedURLs.Contains(spec)) {
return false;
}
mPreloadedURLs.PutEntry(spec);
return true;
return mPreloadedURLs.EnsureInserted(spec);
}
void

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

@ -293,6 +293,10 @@ class LcovFile(object):
print("Invalid lcov line start at %s:%d:\n%s" %
(lcov_fh.name, count + 1, line))
raise
except TypeError:
print("Invalid lcov line start at %s:%d:\n%s" %
(lcov_fh.name, count + 1, line))
raise
def print_file(self, fh):
for record in self.records:

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

@ -413,7 +413,7 @@ public:
#if defined(MOZ_GECKO_PROFILER)
static void DoStackCapture(const nsACString& aKey);
#endif
static void RecordThreadHangStats(Telemetry::ThreadHangStats& aStats);
static void RecordThreadHangStats(Telemetry::ThreadHangStats&& aStats);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
struct Stat {
uint32_t hitCount;
@ -1479,11 +1479,6 @@ CreateJSThreadHangStats(JSContext* cx, const Telemetry::ThreadHangStats& thread)
return nullptr;
}
// Create a CombinedStacks instance which will contain all of the stacks
// collected by the BHR. Specify a custom maxStacksCount to ensure that all of
// our native hang stacks fit.
CombinedStacks combinedStacks(Telemetry::kMaximumNativeHangStacks);
// Process the hangs into a hangs object.
JS::RootedObject hangs(cx, JS_NewArrayObject(cx, 0));
if (!hangs) {
@ -1495,13 +1490,9 @@ CreateJSThreadHangStats(JSContext* cx, const Telemetry::ThreadHangStats& thread)
return nullptr;
}
// Check if we have a native stack, and if we do, add it to combinedStacks,
// and store its index in the 'nativeStack' member of the hang object.
const Telemetry::NativeHangStack& stack = thread.mHangs[i].GetNativeStack();
if (!stack.empty() &&
combinedStacks.GetStackCount() < Telemetry::kMaximumNativeHangStacks) {
Telemetry::ProcessedStack processed = Telemetry::GetStackAndModules(stack);
uint32_t index = combinedStacks.AddStack(processed);
// Check if we have a cached native stack index, and if we do record it.
uint32_t index = thread.mHangs[i].GetNativeStackIndex();
if (index != Telemetry::HangHistogram::NO_NATIVE_STACK_INDEX) {
if (!JS_DefineProperty(cx, obj, "nativeStack", index, JSPROP_ENUMERATE)) {
return nullptr;
}
@ -1515,7 +1506,9 @@ CreateJSThreadHangStats(JSContext* cx, const Telemetry::ThreadHangStats& thread)
return nullptr;
}
JS::RootedObject fullReportObj(cx, CreateJSStackObject(cx, combinedStacks));
// We should already have a CombinedStacks object on the ThreadHangStats, so
// add that one.
JS::RootedObject fullReportObj(cx, CreateJSStackObject(cx, thread.mCombinedStacks));
if (!fullReportObj) {
return nullptr;
}
@ -2095,7 +2088,7 @@ TelemetryImpl::CaptureStack(const nsACString& aKey) {
}
void
TelemetryImpl::RecordThreadHangStats(Telemetry::ThreadHangStats& aStats)
TelemetryImpl::RecordThreadHangStats(Telemetry::ThreadHangStats&& aStats)
{
if (!sTelemetry || !TelemetryHistogram::CanRecordExtended())
return;
@ -2791,9 +2784,9 @@ void CaptureStack(const nsACString& aKey)
}
#endif
void RecordThreadHangStats(ThreadHangStats& aStats)
void RecordThreadHangStats(ThreadHangStats&& aStats)
{
TelemetryImpl::RecordThreadHangStats(aStats);
TelemetryImpl::RecordThreadHangStats(Move(aStats));
}

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

@ -343,7 +343,7 @@ class ThreadHangStats;
* will be moved and aStats should be treated as
* invalid after this function returns
*/
void RecordThreadHangStats(ThreadHangStats& aStats);
void RecordThreadHangStats(ThreadHangStats&& aStats);
/**
* Record a failed attempt at locking the user's profile.

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

@ -13,6 +13,7 @@
#include "mozilla/Mutex.h"
#include "mozilla/PodOperations.h"
#include "mozilla/Vector.h"
#include "mozilla/CombinedStacks.h"
#include "nsString.h"
#include "prinrval.h"
@ -162,12 +163,20 @@ public:
hang, along with a time histogram of the hang times. */
class HangHistogram : public TimeHistogram
{
public:
// Value used for mNativeStackIndex to represent the absence of a cached
// native stack.
static const uint32_t NO_NATIVE_STACK_INDEX = UINT32_MAX;
private:
static uint32_t GetHash(const HangStack& aStack);
HangStack mStack;
// Native stack that corresponds to the pseudostack in mStack
NativeHangStack mNativeStack;
// Cached index of the native stack in the mCombinedStacks list in the owning
// ThreadHangStats object. A default value of NO_NATIVE_STACK_INDEX means that
// the ThreadHangStats object which owns this HangHistogram doesn't have a
// cached CombinedStacks with this HangHistogram in it.
uint32_t mNativeStackIndex;
// Use a hash to speed comparisons
const uint32_t mHash;
// Annotations attributed to this stack
@ -176,14 +185,7 @@ private:
public:
explicit HangHistogram(HangStack&& aStack)
: mStack(mozilla::Move(aStack))
, mHash(GetHash(mStack))
{
}
explicit HangHistogram(HangStack&& aStack,
NativeHangStack&& aNativeStack)
: mStack(mozilla::Move(aStack))
, mNativeStack(mozilla::Move(aNativeStack))
, mNativeStackIndex(NO_NATIVE_STACK_INDEX)
, mHash(GetHash(mStack))
{
}
@ -191,7 +193,7 @@ public:
HangHistogram(HangHistogram&& aOther)
: TimeHistogram(mozilla::Move(aOther))
, mStack(mozilla::Move(aOther.mStack))
, mNativeStack(mozilla::Move(aOther.mNativeStack))
, mNativeStackIndex(mozilla::Move(aOther.mNativeStackIndex))
, mHash(mozilla::Move(aOther.mHash))
, mAnnotations(mozilla::Move(aOther.mAnnotations))
{
@ -204,11 +206,12 @@ public:
const HangStack& GetStack() const {
return mStack;
}
NativeHangStack& GetNativeStack() {
return mNativeStack;
uint32_t GetNativeStackIndex() const {
return mNativeStackIndex;
}
const NativeHangStack& GetNativeStack() const {
return mNativeStack;
void SetNativeStackIndex(uint32_t aIndex) {
MOZ_ASSERT(aIndex != NO_NATIVE_STACK_INDEX);
mNativeStackIndex = aIndex;
}
const HangMonitor::HangAnnotationsVector& GetAnnotations() const {
return mAnnotations;
@ -228,6 +231,7 @@ public:
- time histogram of all task run times
- hang histograms of individual hangs
- annotations for each hang
- combined native stacks for all hangs
*/
class ThreadHangStats
{
@ -238,10 +242,12 @@ public:
TimeHistogram mActivity;
mozilla::Vector<HangHistogram, 4> mHangs;
uint32_t mNativeStackCnt;
CombinedStacks mCombinedStacks;
explicit ThreadHangStats(const char* aName)
: mName(aName)
, mNativeStackCnt(0)
, mCombinedStacks(Telemetry::kMaximumNativeHangStacks)
{
}
ThreadHangStats(ThreadHangStats&& aOther)
@ -249,6 +255,7 @@ public:
, mActivity(mozilla::Move(aOther.mActivity))
, mHangs(mozilla::Move(aOther.mHangs))
, mNativeStackCnt(aOther.mNativeStackCnt)
, mCombinedStacks(mozilla::Move(aOther.mCombinedStacks))
{
aOther.mNativeStackCnt = 0;
}

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

@ -45,6 +45,7 @@ EXPORTS.mozilla += [
'!TelemetryHistogramEnums.h',
'!TelemetryProcessEnums.h',
'!TelemetryScalarEnums.h',
'CombinedStacks.h',
'ipc/TelemetryComms.h',
'ipc/TelemetryIPC.h',
'ProcessedStack.h',

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

@ -1460,7 +1460,6 @@ nsWindow::nsWindow() :
mScreenId(0), // Use 0 (primary screen) as the default value.
mIsVisible(false),
mParent(nullptr),
mAwaitingFullScreen(false),
mIsFullScreen(false)
{
}
@ -1764,12 +1763,6 @@ nsWindow::Resize(double aX,
// Should we skip honoring aRepaint here?
if (aRepaint && FindTopLevel() == nsWindow::TopWindow())
RedrawAll();
nsIWidgetListener* listener = GetWidgetListener();
if (mAwaitingFullScreen && listener) {
listener->FullscreenChanged(mIsFullScreen);
mAwaitingFullScreen = false;
}
}
void
@ -1925,9 +1918,13 @@ nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen*)
}
mIsFullScreen = aFullScreen;
mAwaitingFullScreen = true;
mAndroidView->mEventDispatcher->Dispatch(aFullScreen ?
u"GeckoView:FullScreenEnter" : u"GeckoView:FullScreenExit");
nsIWidgetListener* listener = GetWidgetListener();
if (listener) {
listener->FullscreenChanged(mIsFullScreen);
}
return NS_OK;
}

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

@ -340,7 +340,6 @@ protected:
nsCOMPtr<nsIIdleServiceInternal> mIdleService;
bool mAwaitingFullScreen;
bool mIsFullScreen;
bool UseExternalCompositingSurface() const override {

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

@ -162,6 +162,26 @@ public:
mozilla::fallible));
}
/**
* Get the entry associated with a key, or create a new entry using infallible
* allocation and insert that.
* @param aKey the key to retrieve
* @param aEntry will be assigned (if non-null) to the entry that was found
* or created
* @return true if a new entry was created, or false if an existing entry
* was found
*/
MOZ_MUST_USE
bool EnsureInserted(KeyType aKey, EntryType** aEntry = nullptr)
{
auto oldCount = Count();
EntryType* entry = PutEntry(aKey);
if (aEntry) {
*aEntry = entry;
}
return oldCount != Count();
}
/**
* Remove the entry associated with a key.
* @param aKey of the entry to remove
@ -171,6 +191,23 @@ public:
mTable.Remove(EntryType::KeyToPointer(aKey));
}
/**
* Lookup the entry associated with aKey and remove it if found, otherwise
* do nothing.
* @param aKey of the entry to remove
* @return true if an entry was found and removed, or false if no entry
* was found for aKey
*/
bool EnsureRemoved(KeyType aKey)
{
auto* entry = GetEntry(aKey);
if (entry) {
RemoveEntry(entry);
return true;
}
return false;
}
/**
* Remove the entry associated with a key.
* @param aEntry the entry-pointer to remove (obtained from GetEntry)
@ -493,7 +530,6 @@ public:
nsTHashtable(nsTHashtable&&) = default;
/* Wrapper functions */
using Base::GetGeneration;
using Base::Count;
using Base::IsEmpty;
@ -506,6 +542,7 @@ public:
using Base::MarkImmutable;
#endif
/* Wrapper functions */
EntryType* GetEntry(T* aKey) const
{
return reinterpret_cast<EntryType*>(Base::GetEntry(aKey));
@ -528,11 +565,22 @@ public:
Base::PutEntry(aKey, mozilla::fallible));
}
MOZ_MUST_USE
bool EnsureInserted(T* aKey, EntryType** aEntry = nullptr)
{
return Base::EnsureInserted(aKey, reinterpret_cast<::detail::VoidPtrHashKey**>(aEntry));
}
void RemoveEntry(T* aKey)
{
Base::RemoveEntry(aKey);
}
bool EnsureRemoved(T* aKey)
{
return Base::EnsureRemoved(aKey);
}
void RemoveEntry(EntryType* aEntry)
{
Base::RemoveEntry(reinterpret_cast<::detail::VoidPtrHashKey*>(aEntry));

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

@ -14,6 +14,7 @@
#include "mozilla/Telemetry.h"
#include "mozilla/ThreadHangStats.h"
#include "mozilla/ThreadLocal.h"
#include "mozilla/SystemGroup.h"
#include "prinrval.h"
#include "prthread.h"
@ -24,6 +25,7 @@
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "GeckoProfiler.h"
#include "nsNetCID.h"
#include <algorithm>
@ -46,6 +48,8 @@ bool StackScriptEntriesCollapser(const char* aStackEntry, const char *aAnotherSt
namespace mozilla {
class ProcessHangRunnable;
/**
* BackgroundHangManager is the global object that
* manages all instances of BackgroundHangThread.
@ -88,6 +92,10 @@ public:
PRIntervalTime mIntervalNow;
// List of BackgroundHangThread instances associated with each thread
LinkedList<BackgroundHangThread> mHangThreads;
// A reference to the StreamTransportService. This is gotten on the main
// thread, and carried around, as nsStreamTransportService::Init is
// non-threadsafe.
nsCOMPtr<nsIEventTarget> mSTS;
void Shutdown()
{
@ -188,14 +196,18 @@ public:
UniquePtr<HangMonitor::HangAnnotations> mAnnotations;
// Annotators registered for this thread
HangMonitor::Observer::Annotators mAnnotators;
// List of runnables which can hold a reference to us which need to be
// canceled before we can go away.
LinkedList<RefPtr<ProcessHangRunnable>> mProcessHangRunnables;
BackgroundHangThread(const char* aName,
uint32_t aTimeoutMs,
uint32_t aMaxTimeoutMs,
BackgroundHangMonitor::ThreadType aThreadType = BackgroundHangMonitor::THREAD_SHARED);
// Report a hang; aManager->mLock IS locked
Telemetry::HangHistogram& ReportHang(PRIntervalTime aHangTime);
// Report a hang; aManager->mLock IS locked. The hang will be processed
// off-main-thread, and will then be submitted back.
void ReportHang(PRIntervalTime aHangTime);
// Report a permanent hang; aManager->mLock IS locked
void ReportPermaHang();
// Called by BackgroundHangMonitor::NotifyActivity
@ -235,9 +247,11 @@ BackgroundHangManager::BackgroundHangManager()
: mShutdown(false)
, mLock("BackgroundHangManager")
, mIntervalNow(0)
, mSTS(do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID))
{
// Lock so we don't race against the new monitor thread
MonitorAutoLock autoLock(mLock);
mHangMonitorThread = PR_CreateThread(
PR_USER_THREAD, MonitorThread, this,
PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
@ -417,6 +431,93 @@ BackgroundHangThread::BackgroundHangThread(const char* aName,
autoLock.Notify();
}
// This runnable is used to pre-process a hang, performing any expensive
// operations on it, before submitting it into the BackgroundHangThread object
// for Telemetry.
//
// If this object is canceled, it will submit its payload to the
// BackgroundHangThread without performing the processing.
class ProcessHangRunnable final
: public CancelableRunnable
, public LinkedListElement<RefPtr<ProcessHangRunnable>>
{
public:
ProcessHangRunnable(BackgroundHangManager* aManager,
BackgroundHangThread* aThread,
Telemetry::HangHistogram&& aHistogram,
Telemetry::NativeHangStack&& aNativeStack)
: mManager(aManager)
, mNativeStack(mozilla::Move(aNativeStack))
, mThread(aThread)
, mHistogram(mozilla::Move(aHistogram))
{
MOZ_ASSERT(mThread);
}
NS_IMETHOD
Run() override
{
// Start processing this histogram's native hang stack before we try to lock
// anything, as we can do this without any locks held. This is the expensive
// part of the operation.
Telemetry::ProcessedStack processed;
if (!mNativeStack.empty()) {
processed = Telemetry::GetStackAndModules(mNativeStack);
}
// Lock the manager's lock, so that we can take a look at our mThread
{
MonitorAutoLock autoLock(mManager->mLock);
if (NS_WARN_IF(!mThread)) {
return NS_OK;
}
// If we have a stack, check if we can add it to combined stacks. This is
// a relatively cheap operation, and must occur with the lock held.
if (!mNativeStack.empty() &&
mThread->mStats.mCombinedStacks.GetStackCount() < Telemetry::kMaximumNativeHangStacks) {
mHistogram.SetNativeStackIndex(mThread->mStats.mCombinedStacks.AddStack(processed));
}
// Submit, remove ourselves from the list, and clear out mThread so we
// don't run again.
MOZ_ALWAYS_TRUE(mThread->mStats.mHangs.append(Move(mHistogram)));
remove();
mThread = nullptr;
}
return NS_OK;
}
// Submits hang, and removes from list.
nsresult
Cancel() override
{
mManager->mLock.AssertCurrentThreadOwns();
if (NS_WARN_IF(!mThread)) {
return NS_OK;
}
// Submit, remove ourselves from the list, and clear out mThread so we
// don't run again.
MOZ_ALWAYS_TRUE(mThread->mStats.mHangs.append(Move(mHistogram)));
if (isInList()) {
remove();
}
mThread = nullptr;
return NS_OK;
}
private:
// These variables are constant after initialization, and do not need
// synchronization.
RefPtr<BackgroundHangManager> mManager;
const Telemetry::NativeHangStack mNativeStack;
// These variables are guarded by mManager->mLock.
BackgroundHangThread* MOZ_NON_OWNING_REF mThread; // Will Cancel us before it dies
Telemetry::HangHistogram mHistogram;
};
BackgroundHangThread::~BackgroundHangThread()
{
// Lock here because LinkedList is not thread-safe
@ -431,11 +532,18 @@ BackgroundHangThread::~BackgroundHangThread()
sTlsKey.set(nullptr);
}
// Move our copy of ThreadHangStats to Telemetry storage
Telemetry::RecordThreadHangStats(mStats);
// Cancel any remaining process hang runnables, as they hold a weak reference
// into our mStats variable, which we're about to move.
while (RefPtr<ProcessHangRunnable> runnable = mProcessHangRunnables.popFirst()) {
runnable->Cancel();
}
// Record the ThreadHangStats for this thread before we go away. All stats
// should be in this method now, as we canceled any pending runnables.
Telemetry::RecordThreadHangStats(Move(mStats));
}
Telemetry::HangHistogram&
void
BackgroundHangThread::ReportHang(PRIntervalTime aHangTime)
{
// Recovered from a hang; called on the monitor thread
@ -465,21 +573,30 @@ BackgroundHangThread::ReportHang(PRIntervalTime aHangTime)
mHangStack.erase(mHangStack.begin() + 1, mHangStack.begin() + elementsToRemove);
}
Telemetry::HangHistogram newHistogram(Move(mHangStack), Move(mNativeHangStack));
Telemetry::HangHistogram newHistogram(Move(mHangStack));
for (Telemetry::HangHistogram* oldHistogram = mStats.mHangs.begin();
oldHistogram != mStats.mHangs.end(); oldHistogram++) {
if (newHistogram == *oldHistogram) {
// New histogram matches old one
oldHistogram->Add(aHangTime, Move(mAnnotations));
return *oldHistogram;
return;
}
}
// Add new histogram
newHistogram.Add(aHangTime, Move(mAnnotations));
if (!mStats.mHangs.append(Move(newHistogram))) {
MOZ_CRASH();
// Process the hang off-main thread. We record a reference to the runnable in
// mProcessHangRunnables so we can abort this preprocessing and just submit
// the message if the processing takes too long and our thread is going away.
RefPtr<ProcessHangRunnable> processHang =
new ProcessHangRunnable(mManager, this, Move(newHistogram), Move(mNativeHangStack));
mProcessHangRunnables.insertFront(processHang);
// Try to dispatch the runnable to the StreamTransportService threadpool. If
// we fail, cancel our runnable.
if (!mManager->mSTS || NS_FAILED(mManager->mSTS->Dispatch(processHang.forget()))) {
RefPtr<ProcessHangRunnable> runnable = mProcessHangRunnables.popFirst();
runnable->Cancel();
}
return mStats.mHangs.back();
}
void
@ -488,14 +605,13 @@ BackgroundHangThread::ReportPermaHang()
// Permanently hanged; called on the monitor thread
// mManager->mLock IS locked
Telemetry::HangHistogram& hang = ReportHang(mMaxTimeout);
Telemetry::NativeHangStack& stack = hang.GetNativeStack();
if (stack.empty()) {
mStats.mNativeStackCnt += 1;
if (mStats.mNativeStackCnt <= Telemetry::kMaximumNativeHangStacks) {
mStackHelper.GetNativeStack(stack);
}
}
// NOTE: We used to capture a native stack in this situation if one had not
// already been captured, but with the new ReportHang design that is less
// practical.
//
// We currently don't look at hang reports outside of nightly, and already
// collect native stacks eagerly on nightly, so this should be OK.
ReportHang(mMaxTimeout);
}
MOZ_ALWAYS_INLINE void
@ -587,6 +703,7 @@ BackgroundHangMonitor::DisableOnBeta() {
void
BackgroundHangMonitor::Startup()
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
MOZ_ASSERT(!BackgroundHangManager::sInstance, "Already initialized");

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

@ -370,6 +370,11 @@ NS_IdleDispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent,
{
nsCOMPtr<nsIRunnable> event(Move(aEvent));
NS_ENSURE_TRUE(event, NS_ERROR_INVALID_ARG);
//XXX Using current thread for now as the nsIEventTarget.
nsIEventTarget* target = mozilla::GetCurrentThreadEventTarget();
NS_ENSURE_STATE(target);
nsCOMPtr<nsIIdleRunnable> idleEvent = do_QueryInterface(event);
if (!idleEvent) {
@ -377,9 +382,7 @@ NS_IdleDispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent,
event = do_QueryInterface(idleEvent);
MOZ_DIAGNOSTIC_ASSERT(event);
}
//XXX Using current thread for now as the nsIEventTarget.
idleEvent->SetTimer(aTimeout, NS_GetCurrentThread());
idleEvent->SetTimer(aTimeout, target);
return NS_IdleDispatchToCurrentThread(event.forget());
}