зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central. r=merge a=merge
MozReview-Commit-ID: DPQl41S3ZkU
This commit is contained in:
Коммит
3654d560d6
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче