зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c a=merge
This commit is contained in:
Коммит
ed461fdf11
|
@ -83,9 +83,7 @@ addEventListener("blur", function(event) {
|
|||
LoginManagerContent.onUsernameInput(event);
|
||||
});
|
||||
|
||||
var gLastContextMenuEvent = null; // null or a WeakReference to a contextmenu event
|
||||
var handleContentContextMenu = function (event) {
|
||||
gLastContextMenuEvent = null;
|
||||
let defaultPrevented = event.defaultPrevented;
|
||||
if (!Services.prefs.getBoolPref("dom.event.contextmenu.enabled")) {
|
||||
let plugin = null;
|
||||
|
@ -100,36 +98,8 @@ var handleContentContextMenu = function (event) {
|
|||
defaultPrevented = false;
|
||||
}
|
||||
|
||||
if (defaultPrevented) {
|
||||
if (defaultPrevented)
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH) {
|
||||
// If this was triggered by touch, then we don't want to show the actual
|
||||
// context menu until we get the APZ:LongTapUp notification. However, we
|
||||
// will need the |event| object when we get that notification, so we save
|
||||
// it in a WeakReference. That way it won't leak things if we never get
|
||||
// the APZ:LongTapUp notification (which is quite possible).
|
||||
gLastContextMenuEvent = Cu.getWeakReference(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// For non-touch-derived contextmenu events, we can handle it right away.
|
||||
showContentContextMenu(event);
|
||||
}
|
||||
|
||||
var showContentContextMenu = function (event) {
|
||||
if (event == null) {
|
||||
// If we weren't given an event, then this is being invoked from the
|
||||
// APZ:LongTapUp observer, and the contextmenu event is stashed in
|
||||
// gLastContextMenuEvent.
|
||||
event = (gLastContextMenuEvent ? gLastContextMenuEvent.get() : null);
|
||||
gLastContextMenuEvent = null;
|
||||
if (event == null) {
|
||||
// Still no event? We can't do anything, bail out.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let addonInfo = {};
|
||||
let subject = {
|
||||
|
@ -247,11 +217,6 @@ Cc["@mozilla.org/eventlistenerservice;1"]
|
|||
.getService(Ci.nsIEventListenerService)
|
||||
.addSystemEventListener(global, "contextmenu", handleContentContextMenu, false);
|
||||
|
||||
Services.obs.addObserver(showContentContextMenu, "APZ:LongTapUp", false);
|
||||
addEventListener("unload", () => {
|
||||
Services.obs.removeObserver(showContentContextMenu, "APZ:LongTapUp")
|
||||
}, false);
|
||||
|
||||
// Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
|
||||
const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0;
|
||||
const TLS_ERROR_REPORT_TELEMETRY_EXPANDED = 1;
|
||||
|
|
|
@ -39,6 +39,21 @@ const PAGECONTENT_SMALL =
|
|||
" <option value='Six'>Six</option>" +
|
||||
"</select></body></html>";
|
||||
|
||||
const PAGECONTENT_SOMEHIDDEN =
|
||||
"<html>" +
|
||||
"<body><select id='one'>" +
|
||||
" <option value='One' style='display: none;'>OneHidden</option>" +
|
||||
" <option value='Two' style='display: none;'>TwoHidden</option>" +
|
||||
" <option value='Three'>ThreeVisible</option>" +
|
||||
" <option value='Four'style='display: table;'>FourVisible</option>" +
|
||||
" <option value='Five'>FiveVisible</option>" +
|
||||
" <optgroup label='GroupHidden' style='display: none;'>" +
|
||||
" <option value='Four'>Six.OneHidden</option>" +
|
||||
" <option value='Five' style='display: block;'>Six.TwoHidden</option>" +
|
||||
" </optgroup>" +
|
||||
" <option value='Six'>SevenVisible</option>" +
|
||||
"</select></body></html>";
|
||||
|
||||
const PAGECONTENT_TRANSLATED =
|
||||
"<html><body>" +
|
||||
"<div id='div'>" +
|
||||
|
@ -485,3 +500,33 @@ add_task(function* test_mousemove_correcttarget() {
|
|||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
// This test checks when a <select> element has some options with altered display values.
|
||||
add_task(function* test_somehidden() {
|
||||
const pageUrl = "data:text/html," + escape(PAGECONTENT_SOMEHIDDEN);
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
|
||||
|
||||
let selectPopup = document.getElementById("ContentSelectDropdown").menupopup;
|
||||
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
|
||||
yield popupShownPromise;
|
||||
|
||||
// The exact number is not needed; just ensure the height is larger than 4 items to accomodate any popup borders.
|
||||
ok(selectPopup.getBoundingClientRect().height >= selectPopup.lastChild.getBoundingClientRect().height * 4, "Height contains at least 4 items");
|
||||
ok(selectPopup.getBoundingClientRect().height < selectPopup.lastChild.getBoundingClientRect().height * 5, "Height doesn't contain 5 items");
|
||||
|
||||
// The label contains the substring 'Visible' for items that are visible.
|
||||
// Otherwise, it is expected to be display: none.
|
||||
is(selectPopup.parentNode.itemCount, 9, "Correct number of items");
|
||||
let child = selectPopup.firstChild;
|
||||
let idx = 1;
|
||||
while (child) {
|
||||
is(getComputedStyle(child).display, child.label.indexOf("Visible") > 0 ? "-moz-box" : "none",
|
||||
"Item " + (idx++) + " is visible");
|
||||
child = child.nextSibling;
|
||||
}
|
||||
|
||||
yield hideSelectPopup(selectPopup, "escape");
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
|
|
@ -59,7 +59,7 @@ add_task(function* () {
|
|||
|
||||
let obsPromise = TestUtils.topicObserved("PopupNotifications-updateNotShowing");
|
||||
let overlayPromise = promisePopupNotification("click-to-play-plugins");
|
||||
gTestBrowser.contentWindow.history.back();
|
||||
gTestBrowser.goBack();
|
||||
yield obsPromise;
|
||||
yield overlayPromise;
|
||||
});
|
||||
|
|
|
@ -177,6 +177,15 @@ restartNow=Restart Now
|
|||
restartLater=Restart Later
|
||||
|
||||
disableContainersAlertTitle=Close All Container Tabs?
|
||||
|
||||
# LOCALIZATION NOTE (disableContainersMsg): Semi-colon list of plural forms.
|
||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
# #S is the number of container tabs
|
||||
disableContainersMsg=If you disable Container Tabs now, #S container tab will be closed. Are you sure you want to disable Container Tabs?;If you disable Containers Tabs now, #S container tabs will be closed. Are you sure you want to disable Containers Tabs?
|
||||
|
||||
# LOCALIZATION NOTE (disableContainersOkButton): Semi-colon list of plural forms.
|
||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
# #S is the number of container tabs
|
||||
disableContainersOkButton=Close #S Container Tab;Close #S Container Tabs
|
||||
|
||||
disableContainersButton2=Keep enabled
|
||||
|
|
|
@ -1054,7 +1054,7 @@ toolbaritem[cui-areatype="menu-panel"] > :-moz-any(@nestedButtons@) > .toolbarbu
|
|||
}
|
||||
|
||||
#identity-box:-moz-focusring {
|
||||
outline: 1px dotted #000;
|
||||
outline: 1px dotted;
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#PopupAutoCompleteRichResult > hbox[anonid="search-suggestions-notification"] {
|
||||
border-bottom: 1px solid hsla(210, 4%, 10%, 0.14);
|
||||
border-bottom: 1px solid var(--panel-separator-color);
|
||||
background-color: hsla(210, 4%, 10%, 0.07);
|
||||
padding: 6px 0;
|
||||
padding-inline-start: 44px;
|
||||
|
|
|
@ -1489,7 +1489,7 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
|
|||
/* identity box */
|
||||
|
||||
#identity-box:-moz-focusring {
|
||||
outline: 1px dotted #000;
|
||||
outline: 1px dotted;
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
|
|
|
@ -220,9 +220,9 @@ nsPrincipal::SubsumesInternal(nsIPrincipal* aOther,
|
|||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> otherURI;
|
||||
rv = aOther->GetURI(getter_AddRefs(otherURI));
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
nsCOMPtr<nsIURI> otherURI;
|
||||
rv = aOther->GetURI(getter_AddRefs(otherURI));
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
// Compare codebases.
|
||||
return nsScriptSecurityManager::SecurityCompareURIs(mCodebase, otherURI);
|
||||
|
|
|
@ -4844,8 +4844,6 @@ nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject)
|
|||
NS_ASSERTION(!mScriptGlobalObject ||
|
||||
mScriptGlobalObject == aScriptObject,
|
||||
"Wrong script object!");
|
||||
// XXXkhuey why bother?
|
||||
nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aScriptObject);
|
||||
if (aScriptObject) {
|
||||
mScopeObject = do_GetWeakReference(aScriptObject);
|
||||
mHasHadScriptHandlingObject = true;
|
||||
|
@ -7710,8 +7708,6 @@ nsDOMAttributeMap::BlastSubtreeToPieces(nsINode *aNode)
|
|||
Element *element = aNode->AsElement();
|
||||
const nsDOMAttributeMap *map = element->GetAttributeMap();
|
||||
if (map) {
|
||||
nsCOMPtr<nsIAttribute> attr;
|
||||
|
||||
// This non-standard style of iteration is presumably used because some
|
||||
// of the code in the loop body can trigger element removal, which
|
||||
// invalidates the iterator.
|
||||
|
@ -8764,7 +8760,6 @@ nsDocument::Sanitize()
|
|||
|
||||
RefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input"));
|
||||
|
||||
nsCOMPtr<nsIContent> item;
|
||||
nsAutoString value;
|
||||
|
||||
uint32_t length = nodes->Length(true);
|
||||
|
@ -10439,7 +10434,6 @@ nsDocument::GetStateObject(nsIVariant** aState)
|
|||
// mStateObjectContainer may be null; this just means that there's no
|
||||
// current state object.
|
||||
|
||||
nsCOMPtr<nsIVariant> stateObj;
|
||||
if (!mStateObjectCached && mStateObjectContainer) {
|
||||
AutoJSContext cx;
|
||||
nsIGlobalObject* sgo = GetScopeObject();
|
||||
|
@ -10880,7 +10874,6 @@ nsIDocument::CaretPositionFromPoint(float aX, float aY)
|
|||
if (nodeIsAnonymous) {
|
||||
node = ptFrame->GetContent();
|
||||
nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(nonanon);
|
||||
nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon);
|
||||
nsITextControlFrame* textFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
|
||||
nsNumberControlFrame* numberFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* Object that can be used to serialize selections, ranges, or nodes
|
||||
* to strings in a gazillion different ways.
|
||||
*/
|
||||
|
||||
|
||||
#include "nsIDocumentEncoder.h"
|
||||
|
||||
#include "nscore.h"
|
||||
|
@ -89,8 +89,8 @@ protected:
|
|||
nsAString& aStr);
|
||||
nsresult SerializeRangeToString(nsRange *aRange,
|
||||
nsAString& aOutputString);
|
||||
nsresult SerializeRangeNodes(nsRange* aRange,
|
||||
nsINode* aNode,
|
||||
nsresult SerializeRangeNodes(nsRange* aRange,
|
||||
nsINode* aNode,
|
||||
nsAString& aString,
|
||||
int32_t aDepth);
|
||||
nsresult SerializeRangeContextStart(const nsTArray<nsINode*>& aAncestorArray,
|
||||
|
@ -167,7 +167,7 @@ protected:
|
|||
AutoTArray<int32_t, 8> mStartOffsets;
|
||||
AutoTArray<nsIContent*, 8> mEndNodes;
|
||||
AutoTArray<int32_t, 8> mEndOffsets;
|
||||
bool mHaltRangeHint;
|
||||
bool mHaltRangeHint;
|
||||
// Used when context has already been serialized for
|
||||
// table cell selections (where parent is <tr>)
|
||||
bool mDisableContextSerialize;
|
||||
|
@ -412,14 +412,14 @@ nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
|
|||
{
|
||||
if (!IsVisibleNode(aNode))
|
||||
return NS_OK;
|
||||
|
||||
|
||||
nsINode* node = nullptr;
|
||||
nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip;
|
||||
|
||||
// Caller didn't do fixup, so we'll do it ourselves
|
||||
if (!aOriginalNode) {
|
||||
aOriginalNode = aNode;
|
||||
if (mNodeFixup) {
|
||||
if (mNodeFixup) {
|
||||
bool dummy;
|
||||
nsCOMPtr<nsIDOMNode> domNodeIn = do_QueryInterface(aNode);
|
||||
nsCOMPtr<nsIDOMNode> domNodeOut;
|
||||
|
@ -433,7 +433,7 @@ nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
|
|||
// or the caller did fixup themselves and aNode is already fixed
|
||||
if (!node)
|
||||
node = aNode;
|
||||
|
||||
|
||||
if (node->IsElement()) {
|
||||
if ((mFlags & (nsIDocumentEncoder::OutputPreformatted |
|
||||
nsIDocumentEncoder::OutputDropInvisibleBreak)) &&
|
||||
|
@ -655,7 +655,7 @@ ConvertAndWrite(const nsAString& aString,
|
|||
// If the converter couldn't convert a chraacer we replace the
|
||||
// character with a characre entity.
|
||||
if (convert_rv == NS_ERROR_UENC_NOMAPPING) {
|
||||
// Finishes the conversion.
|
||||
// Finishes the conversion.
|
||||
// The converter has the possibility to write some extra data and flush its final state.
|
||||
char finish_buf[33];
|
||||
charLength = sizeof(finish_buf) - 1;
|
||||
|
@ -671,7 +671,7 @@ ConvertAndWrite(const nsAString& aString,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString entString("&#");
|
||||
if (NS_IS_HIGH_SURROGATE(unicodeBuf[unicodeLength - 1]) &&
|
||||
if (NS_IS_HIGH_SURROGATE(unicodeBuf[unicodeLength - 1]) &&
|
||||
unicodeLength < startLength && NS_IS_LOW_SURROGATE(unicodeBuf[unicodeLength])) {
|
||||
entString.AppendInt(SURROGATE_TO_UCS4(unicodeBuf[unicodeLength - 1],
|
||||
unicodeBuf[unicodeLength]));
|
||||
|
@ -859,7 +859,7 @@ nsDocumentEncoder::SerializeRangeNodes(nsRange* aRange,
|
|||
}
|
||||
else
|
||||
{
|
||||
// due to implementation it is impossible for text node to be both start and end of
|
||||
// due to implementation it is impossible for text node to be both start and end of
|
||||
// range. We would have handled that case without getting here.
|
||||
//XXXsmaug What does this all mean?
|
||||
if (IsTextNode(aNode))
|
||||
|
@ -889,12 +889,12 @@ nsDocumentEncoder::SerializeRangeNodes(nsRange* aRange,
|
|||
}
|
||||
if ((startNode == content) && !mHaltRangeHint) mStartDepth++;
|
||||
if ((endNode == content) && !mHaltRangeHint) mEndDepth++;
|
||||
|
||||
|
||||
// serialize the start of this node
|
||||
rv = SerializeNodeStart(aNode, 0, -1, aString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
|
||||
// do some calculations that will tell us which children of this
|
||||
// node are in the range.
|
||||
nsIContent* childAsNode = nullptr;
|
||||
|
@ -903,7 +903,7 @@ nsDocumentEncoder::SerializeRangeNodes(nsRange* aRange,
|
|||
startOffset = mStartOffsets[mStartRootIndex - aDepth];
|
||||
if (endNode == content && mEndRootIndex >= aDepth)
|
||||
endOffset = mEndOffsets[mEndRootIndex - aDepth];
|
||||
// generated content will cause offset values of -1 to be returned.
|
||||
// generated content will cause offset values of -1 to be returned.
|
||||
int32_t j;
|
||||
uint32_t childCount = content->GetChildCount();
|
||||
|
||||
|
@ -914,7 +914,7 @@ nsDocumentEncoder::SerializeRangeNodes(nsRange* aRange,
|
|||
// if we are at the "tip" of the selection, endOffset is fine.
|
||||
// otherwise, we need to add one. This is because of the semantics
|
||||
// of the offset list created by GetAncestorsAndOffsets(). The
|
||||
// intermediate points on the list use the endOffset of the
|
||||
// intermediate points on the list use the endOffset of the
|
||||
// location of the ancestor, rather than just past it. So we need
|
||||
// to add one here in order to include it in the children we serialize.
|
||||
if (aNode != aRange->GetEndParent())
|
||||
|
@ -939,7 +939,7 @@ nsDocumentEncoder::SerializeRangeNodes(nsRange* aRange,
|
|||
if (aNode != mCommonParent)
|
||||
{
|
||||
rv = SerializeNodeEnd(aNode, aString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1020,7 +1020,7 @@ nsDocumentEncoder::SerializeRangeToString(nsRange *aRange,
|
|||
|
||||
if (!mCommonParent)
|
||||
return NS_OK;
|
||||
|
||||
|
||||
nsINode* startParent = aRange->GetStartParent();
|
||||
NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE);
|
||||
int32_t startOffset = aRange->StartOffset();
|
||||
|
@ -1047,7 +1047,7 @@ nsDocumentEncoder::SerializeRangeToString(nsRange *aRange,
|
|||
nsCOMPtr<nsIContent> commonContent = do_QueryInterface(mCommonParent);
|
||||
mStartRootIndex = mStartNodes.IndexOf(commonContent);
|
||||
mEndRootIndex = mEndNodes.IndexOf(commonContent);
|
||||
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
rv = SerializeRangeContextStart(mCommonAncestors, aOutputString);
|
||||
|
@ -1108,7 +1108,7 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
|
|||
mCachedBuffer->ToString(0, output, true);
|
||||
// output owns the buffer now!
|
||||
mCachedBuffer = nullptr;
|
||||
|
||||
|
||||
|
||||
if (!mSerializer) {
|
||||
nsAutoCString progId(NS_CONTENTSERIALIZER_CONTRACTID_PREFIX);
|
||||
|
@ -1120,8 +1120,6 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
|
|||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
nsCOMPtr<nsIAtom> charsetAtom;
|
||||
|
||||
bool rewriteEncodingDeclaration = !(mSelection || mRange || mNode) && !(mFlags & OutputDontRewriteEncodingDeclaration);
|
||||
mSerializer->Init(mFlags, mWrapColumn, mCharset.get(), mIsCopying, rewriteEncodingDeclaration);
|
||||
|
||||
|
@ -1195,13 +1193,13 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mCommonAncestors.Clear();
|
||||
nsContentUtils::GetAncestors(p->GetParentNode(), mCommonAncestors);
|
||||
mDisableContextSerialize = false;
|
||||
mDisableContextSerialize = false;
|
||||
rv = SerializeRangeContextEnd(mCommonAncestors, output);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Just to be safe
|
||||
mDisableContextSerialize = false;
|
||||
mDisableContextSerialize = false;
|
||||
|
||||
mSelection = nullptr;
|
||||
} else if (mRange) {
|
||||
|
@ -1226,7 +1224,7 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
|
|||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mSerializer->Flush(output);
|
||||
|
||||
|
||||
mCachedBuffer = nsStringBuffer::FromString(output);
|
||||
// We have to be careful how we set aOutputString, because we don't
|
||||
// want it to end up sharing mCachedBuffer if we plan to reuse it.
|
||||
|
@ -1335,12 +1333,12 @@ protected:
|
|||
kStart,
|
||||
kEnd
|
||||
};
|
||||
|
||||
|
||||
nsresult PromoteRange(nsIDOMRange *inRange);
|
||||
nsresult PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode,
|
||||
int32_t *ioStartOffset,
|
||||
nsresult PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode,
|
||||
int32_t *ioStartOffset,
|
||||
int32_t *ioEndOffset);
|
||||
nsresult GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t aOffset,
|
||||
nsresult GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t aOffset,
|
||||
nsCOMPtr<nsIDOMNode> *outNode, int32_t *outOffset, nsIDOMNode *aCommon);
|
||||
nsCOMPtr<nsIDOMNode> GetChildAt(nsIDOMNode *aParent, int32_t aOffset);
|
||||
bool IsMozBR(nsIDOMNode* aNode);
|
||||
|
@ -1406,10 +1404,10 @@ nsHTMLCopyEncoder::SetSelection(nsISelection* aSelection)
|
|||
// check for text widgets: we need to recognize these so that
|
||||
// we don't tweak the selection to be outside of the magic
|
||||
// div that ender-lite text widgets are embedded in.
|
||||
|
||||
if (!aSelection)
|
||||
|
||||
if (!aSelection)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
nsCOMPtr<nsIDOMNode> commonParent;
|
||||
Selection* selection = aSelection->AsSelection();
|
||||
|
@ -1418,7 +1416,7 @@ nsHTMLCopyEncoder::SetSelection(nsISelection* aSelection)
|
|||
// if selection is uninitialized return
|
||||
if (!rangeCount)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
// we'll just use the common parent of the first range. Implicit assumption
|
||||
// here that multi-range selections are table cell selections, in which case
|
||||
// the common parent is somewhere in the table and we don't really care where.
|
||||
|
@ -1481,7 +1479,7 @@ nsHTMLCopyEncoder::SetSelection(nsISelection* aSelection)
|
|||
}
|
||||
|
||||
// normalize selection if we are not in a widget
|
||||
if (mIsTextWidget)
|
||||
if (mIsTextWidget)
|
||||
{
|
||||
mSelection = aSelection;
|
||||
mMimeType.AssignLiteral("text/plain");
|
||||
|
@ -1496,13 +1494,13 @@ nsHTMLCopyEncoder::SetSelection(nsISelection* aSelection)
|
|||
// mMimeType is set to text/plain when encoding starts.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// there's no Clone() for selection! fix...
|
||||
//nsresult rv = aSelection->Clone(getter_AddRefs(mSelection);
|
||||
//NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_NewDomSelection(getter_AddRefs(mSelection));
|
||||
NS_ENSURE_TRUE(mSelection, NS_ERROR_FAILURE);
|
||||
|
||||
|
||||
// loop thru the ranges in the selection
|
||||
for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
|
||||
range = selection->GetRangeAt(rangeIdx);
|
||||
|
@ -1514,7 +1512,7 @@ nsHTMLCopyEncoder::SetSelection(nsISelection* aSelection)
|
|||
// adjust range to include any ancestors who's children are entirely selected
|
||||
rv = PromoteRange(myRange);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
rv = mSelection->AddRange(myRange);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
@ -1544,7 +1542,7 @@ nsHTMLCopyEncoder::EncodeToStringWithContext(nsAString& aContextString,
|
|||
|
||||
// now encode common ancestors into aContextString. Note that the common ancestors
|
||||
// will be for the last range in the selection in the case of multirange selections.
|
||||
// encoding ancestors every range in a multirange selection in a way that could be
|
||||
// encoding ancestors every range in a multirange selection in a way that could be
|
||||
// understood by the paste code would be a lot more work to do. As a practical matter,
|
||||
// selections are single range, and the ones that aren't are table cell selections
|
||||
// where all the cells are in the same table.
|
||||
|
@ -1556,7 +1554,7 @@ nsHTMLCopyEncoder::EncodeToStringWithContext(nsAString& aContextString,
|
|||
if (count > 0)
|
||||
node = mCommonAncestors.ElementAt(0);
|
||||
|
||||
if (node && IsTextNode(node))
|
||||
if (node && IsTextNode(node))
|
||||
{
|
||||
mCommonAncestors.RemoveElementAt(0);
|
||||
// don't forget to adjust range depth info
|
||||
|
@ -1565,7 +1563,7 @@ nsHTMLCopyEncoder::EncodeToStringWithContext(nsAString& aContextString,
|
|||
// and the count
|
||||
count--;
|
||||
}
|
||||
|
||||
|
||||
i = count;
|
||||
while (i > 0)
|
||||
{
|
||||
|
@ -1579,7 +1577,7 @@ nsHTMLCopyEncoder::EncodeToStringWithContext(nsAString& aContextString,
|
|||
SerializeNodeEnd(node, aContextString);
|
||||
}
|
||||
|
||||
// encode range info : the start and end depth of the selection, where the depth is
|
||||
// encode range info : the start and end depth of the selection, where the depth is
|
||||
// distance down in the parent hierarchy. Later we will need to add leading/trailing
|
||||
// whitespace info to this.
|
||||
nsAutoString infoString;
|
||||
|
@ -1587,7 +1585,7 @@ nsHTMLCopyEncoder::EncodeToStringWithContext(nsAString& aContextString,
|
|||
infoString.Append(char16_t(','));
|
||||
infoString.AppendInt(mEndDepth);
|
||||
aInfoString = infoString;
|
||||
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1629,14 +1627,14 @@ nsHTMLCopyEncoder::IncludeInContext(nsINode *aNode)
|
|||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsresult
|
||||
nsHTMLCopyEncoder::PromoteRange(nsIDOMRange *inRange)
|
||||
{
|
||||
if (!inRange) return NS_ERROR_NULL_POINTER;
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIDOMNode> startNode, endNode, common;
|
||||
int32_t startOffset, endOffset;
|
||||
|
||||
|
||||
rv = inRange->GetCommonAncestorContainer(getter_AddRefs(common));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = inRange->GetStartContainer(getter_AddRefs(startNode));
|
||||
|
@ -1647,18 +1645,17 @@ nsHTMLCopyEncoder::PromoteRange(nsIDOMRange *inRange)
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = inRange->GetEndOffset(&endOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
nsCOMPtr<nsIDOMNode> opStartNode;
|
||||
nsCOMPtr<nsIDOMNode> opEndNode;
|
||||
int32_t opStartOffset, opEndOffset;
|
||||
nsCOMPtr<nsIDOMRange> opRange;
|
||||
|
||||
// examine range endpoints.
|
||||
|
||||
// examine range endpoints.
|
||||
rv = GetPromotedPoint( kStart, startNode, startOffset, address_of(opStartNode), &opStartOffset, common);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = GetPromotedPoint( kEnd, endNode, endOffset, address_of(opEndNode), &opEndOffset, common);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
// if both range endpoints are at the common ancestor, check for possible inclusion of ancestors
|
||||
if ( (opStartNode == common) && (opEndNode == common) )
|
||||
{
|
||||
|
@ -1666,22 +1663,22 @@ nsHTMLCopyEncoder::PromoteRange(nsIDOMRange *inRange)
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
opEndNode = opStartNode;
|
||||
}
|
||||
|
||||
|
||||
// set the range to the new values
|
||||
rv = inRange->SetStart(opStartNode, opStartOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = inRange->SetEnd(opEndNode, opEndOffset);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// PromoteAncestorChain will promote a range represented by [{*ioNode,*ioStartOffset} , {*ioNode,*ioEndOffset}]
|
||||
// The promotion is different from that found in getPromotedPoint: it will only promote one endpoint if it can
|
||||
// promote the other. Thus, instead of having a startnode/endNode, there is just the one ioNode.
|
||||
nsresult
|
||||
nsHTMLCopyEncoder::PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode,
|
||||
int32_t *ioStartOffset,
|
||||
int32_t *ioEndOffset)
|
||||
nsHTMLCopyEncoder::PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode,
|
||||
int32_t *ioStartOffset,
|
||||
int32_t *ioEndOffset)
|
||||
{
|
||||
if (!ioNode || !ioStartOffset || !ioEndOffset) return NS_ERROR_NULL_POINTER;
|
||||
|
||||
|
@ -1694,7 +1691,7 @@ nsHTMLCopyEncoder::PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode,
|
|||
//save the editable state of the ioNode, so we don't promote an ancestor if it has different editable state
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(*ioNode);
|
||||
bool isEditable = node->IsEditable();
|
||||
|
||||
|
||||
// loop for as long as we can promote both endpoints
|
||||
while (!done)
|
||||
{
|
||||
|
@ -1712,13 +1709,13 @@ nsHTMLCopyEncoder::PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsINode> frontINode = do_QueryInterface(frontNode);
|
||||
// if both endpoints were promoted one level and isEditable is the same as the original node,
|
||||
// if both endpoints were promoted one level and isEditable is the same as the original node,
|
||||
// keep looping - otherwise we are done.
|
||||
if ( (frontNode != parent) || (endNode != parent) || (frontINode->IsEditable() != isEditable) )
|
||||
done = true;
|
||||
else
|
||||
{
|
||||
*ioNode = frontNode;
|
||||
*ioNode = frontNode;
|
||||
*ioStartOffset = frontOffset;
|
||||
*ioEndOffset = endOffset;
|
||||
}
|
||||
|
@ -1728,7 +1725,7 @@ nsHTMLCopyEncoder::PromoteAncestorChain(nsCOMPtr<nsIDOMNode> *ioNode,
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t aOffset,
|
||||
nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t aOffset,
|
||||
nsCOMPtr<nsIDOMNode> *outNode, int32_t *outOffset, nsIDOMNode *common)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
@ -1736,14 +1733,14 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t
|
|||
nsCOMPtr<nsIDOMNode> parent = aNode;
|
||||
int32_t offset = aOffset;
|
||||
bool bResetPromotion = false;
|
||||
|
||||
|
||||
// default values
|
||||
*outNode = node;
|
||||
*outOffset = offset;
|
||||
|
||||
if (common == node)
|
||||
if (common == node)
|
||||
return NS_OK;
|
||||
|
||||
|
||||
if (aWhere == kStart)
|
||||
{
|
||||
// some special casing for text nodes
|
||||
|
@ -1751,7 +1748,7 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t
|
|||
if (IsTextNode(t))
|
||||
{
|
||||
// if not at beginning of text node, we are done
|
||||
if (offset > 0)
|
||||
if (offset > 0)
|
||||
{
|
||||
// unless everything before us in just whitespace. NOTE: we need a more
|
||||
// general solution that truly detects all cases of non-significant
|
||||
|
@ -1774,7 +1771,7 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t
|
|||
}
|
||||
if (!node) node = parent;
|
||||
|
||||
// finding the real start for this point. look up the tree for as long as we are the
|
||||
// finding the real start for this point. look up the tree for as long as we are the
|
||||
// first node in the container, and as long as we haven't hit the body node.
|
||||
if (!IsRoot(node) && (parent != common))
|
||||
{
|
||||
|
@ -1798,9 +1795,9 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t
|
|||
{
|
||||
bResetPromotion = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
node = parent;
|
||||
rv = GetNodeLocation(node, address_of(parent), &offset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -1811,7 +1808,7 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t
|
|||
offset = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bResetPromotion)
|
||||
{
|
||||
*outNode = aNode;
|
||||
|
@ -1825,7 +1822,7 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t
|
|||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (aWhere == kEnd)
|
||||
{
|
||||
// some special casing for text nodes
|
||||
|
@ -1856,8 +1853,8 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t
|
|||
node = GetChildAt(parent,offset);
|
||||
}
|
||||
if (!node) node = parent;
|
||||
|
||||
// finding the real end for this point. look up the tree for as long as we are the
|
||||
|
||||
// finding the real end for this point. look up the tree for as long as we are the
|
||||
// last node in the container, and as long as we haven't hit the body node.
|
||||
if (!IsRoot(node) && (parent != common))
|
||||
{
|
||||
|
@ -1881,9 +1878,9 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t
|
|||
{
|
||||
bResetPromotion = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
node = parent;
|
||||
rv = GetNodeLocation(node, address_of(parent), &offset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -1894,7 +1891,7 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t
|
|||
offset = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bResetPromotion)
|
||||
{
|
||||
*outNode = aNode;
|
||||
|
@ -1909,18 +1906,18 @@ nsHTMLCopyEncoder::GetPromotedPoint(Endpoint aWhere, nsIDOMNode *aNode, int32_t
|
|||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMNode>
|
||||
nsCOMPtr<nsIDOMNode>
|
||||
nsHTMLCopyEncoder::GetChildAt(nsIDOMNode *aParent, int32_t aOffset)
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> resultNode;
|
||||
|
||||
if (!aParent)
|
||||
|
||||
if (!aParent)
|
||||
return resultNode;
|
||||
|
||||
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);
|
||||
NS_PRECONDITION(content, "null content in nsHTMLCopyEncoder::GetChildAt");
|
||||
|
||||
|
@ -1929,7 +1926,7 @@ nsHTMLCopyEncoder::GetChildAt(nsIDOMNode *aParent, int32_t aOffset)
|
|||
return resultNode;
|
||||
}
|
||||
|
||||
bool
|
||||
bool
|
||||
nsHTMLCopyEncoder::IsMozBR(nsIDOMNode* aNode)
|
||||
{
|
||||
MOZ_ASSERT(aNode);
|
||||
|
@ -1940,7 +1937,7 @@ nsHTMLCopyEncoder::IsMozBR(nsIDOMNode* aNode)
|
|||
NS_LITERAL_STRING("_moz"), eIgnoreCase);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsresult
|
||||
nsHTMLCopyEncoder::GetNodeLocation(nsIDOMNode *inChild,
|
||||
nsCOMPtr<nsIDOMNode> *outParent,
|
||||
int32_t *outOffset)
|
||||
|
@ -1986,26 +1983,26 @@ nsHTMLCopyEncoder::IsFirstNode(nsIDOMNode *aNode)
|
|||
nsCOMPtr<nsIDOMNode> parent;
|
||||
int32_t offset, j=0;
|
||||
nsresult rv = GetNodeLocation(aNode, address_of(parent), &offset);
|
||||
if (NS_FAILED(rv))
|
||||
if (NS_FAILED(rv))
|
||||
{
|
||||
NS_NOTREACHED("failure in IsFirstNode");
|
||||
return false;
|
||||
}
|
||||
if (offset == 0) // easy case, we are first dom child
|
||||
return true;
|
||||
if (!parent)
|
||||
if (!parent)
|
||||
return true;
|
||||
|
||||
|
||||
// need to check if any nodes before us are really visible.
|
||||
// Mike wrote something for me along these lines in nsSelectionController,
|
||||
// but I don't think it's ready for use yet - revisit.
|
||||
// HACK: for now, simply consider all whitespace text nodes to be
|
||||
// HACK: for now, simply consider all whitespace text nodes to be
|
||||
// invisible formatting nodes.
|
||||
nsCOMPtr<nsIDOMNodeList> childList;
|
||||
nsCOMPtr<nsIDOMNode> child;
|
||||
|
||||
rv = parent->GetChildNodes(getter_AddRefs(childList));
|
||||
if (NS_FAILED(rv) || !childList)
|
||||
if (NS_FAILED(rv) || !childList)
|
||||
{
|
||||
NS_NOTREACHED("failure in IsFirstNode");
|
||||
return true;
|
||||
|
@ -2013,7 +2010,7 @@ nsHTMLCopyEncoder::IsFirstNode(nsIDOMNode *aNode)
|
|||
while (j < offset)
|
||||
{
|
||||
childList->Item(j, getter_AddRefs(child));
|
||||
if (!IsEmptyTextContent(child))
|
||||
if (!IsEmptyTextContent(child))
|
||||
return false;
|
||||
j++;
|
||||
}
|
||||
|
@ -2027,7 +2024,7 @@ nsHTMLCopyEncoder::IsLastNode(nsIDOMNode *aNode)
|
|||
nsCOMPtr<nsIDOMNode> parent;
|
||||
int32_t offset,j;
|
||||
nsresult rv = GetNodeLocation(aNode, address_of(parent), &offset);
|
||||
if (NS_FAILED(rv))
|
||||
if (NS_FAILED(rv))
|
||||
{
|
||||
NS_NOTREACHED("failure in IsLastNode");
|
||||
return false;
|
||||
|
@ -2043,13 +2040,13 @@ nsHTMLCopyEncoder::IsLastNode(nsIDOMNode *aNode)
|
|||
// need to check if any nodes after us are really visible.
|
||||
// Mike wrote something for me along these lines in nsSelectionController,
|
||||
// but I don't think it's ready for use yet - revisit.
|
||||
// HACK: for now, simply consider all whitespace text nodes to be
|
||||
// HACK: for now, simply consider all whitespace text nodes to be
|
||||
// invisible formatting nodes.
|
||||
j = (int32_t)numChildren-1;
|
||||
nsCOMPtr<nsIDOMNodeList>childList;
|
||||
nsCOMPtr<nsIDOMNode> child;
|
||||
rv = parent->GetChildNodes(getter_AddRefs(childList));
|
||||
if (NS_FAILED(rv) || !childList)
|
||||
if (NS_FAILED(rv) || !childList)
|
||||
{
|
||||
NS_NOTREACHED("failure in IsLastNode");
|
||||
return true;
|
||||
|
@ -2058,9 +2055,9 @@ nsHTMLCopyEncoder::IsLastNode(nsIDOMNode *aNode)
|
|||
{
|
||||
childList->Item(j, getter_AddRefs(child));
|
||||
j--;
|
||||
if (IsMozBR(child)) // we ignore trailing moz BRs.
|
||||
if (IsMozBR(child)) // we ignore trailing moz BRs.
|
||||
continue;
|
||||
if (!IsEmptyTextContent(child))
|
||||
if (!IsEmptyTextContent(child))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -2105,4 +2102,3 @@ nsHTMLCopyEncoder::GetImmediateContextCount(const nsTArray<nsINode*>& aAncestorA
|
|||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
|
|
|
@ -14020,7 +14020,6 @@ nsGlobalModalWindow::GetReturnValue(nsIVariant **aRetVal)
|
|||
{
|
||||
FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetReturnValue, (aRetVal), NS_OK);
|
||||
|
||||
nsCOMPtr<nsIVariant> result;
|
||||
if (!mReturnValue) {
|
||||
nsCOMPtr<nsIVariant> variant = CreateVoidVariant();
|
||||
variant.forget(aRetVal);
|
||||
|
|
|
@ -220,7 +220,6 @@ class BlobURLsReporter final : public nsIMemoryReporter
|
|||
"blob cannot be freed until all URLs for it have been explicitly "
|
||||
"invalidated with URL.revokeObjectURL.");
|
||||
nsAutoCString path, url, owner, specialDesc;
|
||||
nsCOMPtr<nsIURI> principalURI;
|
||||
uint64_t size = 0;
|
||||
uint32_t refCount = 1;
|
||||
DebugOnly<bool> blobImplWasCounted;
|
||||
|
|
|
@ -263,7 +263,6 @@ nsLocation::SetURI(nsIURI* aURI, bool aReplace)
|
|||
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
|
||||
if (docShell) {
|
||||
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
|
||||
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell));
|
||||
|
||||
if(NS_FAILED(CheckURL(aURI, getter_AddRefs(loadInfo))))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
|
|
@ -2660,7 +2660,6 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> baseURL;
|
||||
channel->GetURI(getter_AddRefs(request->mBaseURL));
|
||||
|
||||
// Attempt to compile off main thread.
|
||||
|
|
|
@ -3118,8 +3118,6 @@ nsDOMDeviceStorage::AddOrAppendNamed(Blob* aBlob, const nsAString& aPath,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> r;
|
||||
|
||||
if (IsFullPath(aPath)) {
|
||||
nsString storagePath;
|
||||
RefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
|
||||
|
|
|
@ -614,7 +614,6 @@ HTMLSelectElement::Add(nsIDOMHTMLElement* aElement,
|
|||
}
|
||||
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
nsCOMPtr<nsIDOMHTMLElement> beforeElement;
|
||||
|
||||
// whether aBefore is nsIDOMHTMLElement...
|
||||
if (NS_SUCCEEDED(aBefore->GetAsISupports(getter_AddRefs(supports)))) {
|
||||
|
|
|
@ -3123,9 +3123,6 @@ nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString& aValue,
|
|||
void
|
||||
nsGenericHTMLElement::SetInnerText(const nsAString& aValue)
|
||||
{
|
||||
// Fire DOMNodeRemoved mutation events before we do anything else.
|
||||
nsCOMPtr<nsIContent> kungFuDeathGrip;
|
||||
|
||||
// Batch possible DOMSubtreeModified events.
|
||||
mozAutoSubtreeModified subtree(OwnerDoc(), nullptr);
|
||||
FireNodeRemovedForChildren();
|
||||
|
|
|
@ -1303,6 +1303,14 @@ StartMacOSContentSandbox()
|
|||
MOZ_CRASH("Failed to get NS_OS_TEMP_DIR path");
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> profileDir;
|
||||
ContentChild::GetSingleton()->GetProfileDir(getter_AddRefs(profileDir));
|
||||
nsCString profileDirPath;
|
||||
rv = profileDir->GetNativePath(profileDirPath);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_CRASH("Failed to get profile path");
|
||||
}
|
||||
|
||||
MacSandboxInfo info;
|
||||
info.type = MacSandboxType_Content;
|
||||
info.level = info.level = sandboxLevel;
|
||||
|
@ -1310,6 +1318,7 @@ StartMacOSContentSandbox()
|
|||
info.appBinaryPath.assign(appBinaryPath.get());
|
||||
info.appDir.assign(appDir.get());
|
||||
info.appTempDir.assign(tempDirPath.get());
|
||||
info.profileDir.assign(profileDirPath.get());
|
||||
|
||||
std::string err;
|
||||
if (!mozilla::StartMacSandbox(info, err)) {
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
#include "nsWeakPtr.h"
|
||||
#include "nsIWindowProvider.h"
|
||||
|
||||
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
|
||||
#include "nsIFile.h"
|
||||
#endif
|
||||
|
||||
struct ChromePackage;
|
||||
class nsIObserver;
|
||||
|
@ -114,6 +117,19 @@ public:
|
|||
|
||||
void GetProcessName(nsACString& aName) const;
|
||||
|
||||
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
|
||||
void GetProfileDir(nsIFile** aProfileDir) const
|
||||
{
|
||||
*aProfileDir = mProfileDir;
|
||||
NS_IF_ADDREF(*aProfileDir);
|
||||
}
|
||||
|
||||
void SetProfileDir(nsIFile* aProfileDir)
|
||||
{
|
||||
mProfileDir = aProfileDir;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool IsAlive() const;
|
||||
|
||||
static void AppendProcessId(nsACString& aName);
|
||||
|
@ -681,6 +697,10 @@ private:
|
|||
nsCOMPtr<nsIDomainPolicy> mPolicy;
|
||||
nsCOMPtr<nsITimer> mForceKillTimer;
|
||||
|
||||
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
|
||||
nsCOMPtr<nsIFile> mProfileDir;
|
||||
#endif
|
||||
|
||||
// Hashtable to keep track of the pending GetFilesHelper objects.
|
||||
// This GetFilesHelperChild objects are removed when RecvGetFilesResponse is
|
||||
// received.
|
||||
|
|
|
@ -1085,7 +1085,6 @@ ContentParent::CreateBrowserOrApp(const TabContext& aContext,
|
|||
}
|
||||
|
||||
if (aContext.IsMozBrowserElement() || !aContext.HasOwnApp()) {
|
||||
RefPtr<TabParent> tp;
|
||||
RefPtr<nsIContentParent> constructorSender;
|
||||
if (isInContentProcess) {
|
||||
MOZ_ASSERT(aContext.IsMozBrowserElement());
|
||||
|
|
|
@ -114,6 +114,21 @@ ContentProcess::SetAppDir(const nsACString& aPath)
|
|||
mXREEmbed.SetAppDir(aPath);
|
||||
}
|
||||
|
||||
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
|
||||
void
|
||||
ContentProcess::SetProfile(const nsACString& aProfile)
|
||||
{
|
||||
bool flag;
|
||||
nsresult rv =
|
||||
XRE_GetFileFromPath(aProfile.BeginReading(), getter_AddRefs(mProfileDir));
|
||||
if (NS_FAILED(rv) ||
|
||||
NS_FAILED(mProfileDir->Exists(&flag)) || !flag) {
|
||||
NS_WARNING("Invalid profile directory passed to content process.");
|
||||
mProfileDir = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
ContentProcess::Init()
|
||||
{
|
||||
|
@ -124,6 +139,10 @@ ContentProcess::Init()
|
|||
mContent.InitXPCOM();
|
||||
mContent.InitGraphicsDeviceData();
|
||||
|
||||
#if (defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
|
||||
mContent.SetProfileDir(mProfileDir);
|
||||
#endif
|
||||
|
||||
#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
|
||||
SetUpSandboxEnvironment();
|
||||
#endif
|
||||
|
|
|
@ -39,9 +39,18 @@ public:
|
|||
|
||||
void SetAppDir(const nsACString& aPath);
|
||||
|
||||
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
|
||||
void SetProfile(const nsACString& aProfile);
|
||||
#endif
|
||||
|
||||
private:
|
||||
ContentChild mContent;
|
||||
mozilla::ipc::ScopedXREEmbed mXREEmbed;
|
||||
|
||||
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
|
||||
nsCOMPtr<nsIFile> mProfileDir;
|
||||
#endif
|
||||
|
||||
#if defined(XP_WIN)
|
||||
// This object initializes and configures COM.
|
||||
mozilla::mscom::MainThreadRuntime mCOMRuntime;
|
||||
|
|
|
@ -398,21 +398,31 @@ SystemClockDriver::WaitForNextIteration()
|
|||
|
||||
PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
if (mGraphImpl->mNeedAnotherIteration) {
|
||||
|
||||
// This lets us avoid hitting the Atomic twice when we know we won't sleep
|
||||
bool another = mGraphImpl->mNeedAnotherIteration; // atomic
|
||||
if (!another) {
|
||||
mGraphImpl->mGraphDriverAsleep = true; // atomic
|
||||
mWaitState = WAITSTATE_WAITING_INDEFINITELY;
|
||||
}
|
||||
// NOTE: mNeedAnotherIteration while also atomic may have changed before
|
||||
// we could set mGraphDriverAsleep, so we must re-test it.
|
||||
// (EnsureNextIteration sets mNeedAnotherIteration, then tests
|
||||
// mGraphDriverAsleep
|
||||
if (another || mGraphImpl->mNeedAnotherIteration) { // atomic
|
||||
int64_t timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS -
|
||||
int64_t((now - mCurrentTimeStamp).ToMilliseconds());
|
||||
// Make sure timeoutMS doesn't overflow 32 bits by waking up at
|
||||
// least once a minute, if we need to wake up at all
|
||||
timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60*1000));
|
||||
timeout = PR_MillisecondsToInterval(uint32_t(timeoutMS));
|
||||
STREAM_LOG(LogLevel::Verbose, ("Waiting for next iteration; at %f, timeout=%f", (now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0));
|
||||
STREAM_LOG(LogLevel::Verbose,
|
||||
("Waiting for next iteration; at %f, timeout=%f",
|
||||
(now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0));
|
||||
if (mWaitState == WAITSTATE_WAITING_INDEFINITELY) {
|
||||
mGraphImpl->mGraphDriverAsleep = false; // atomic
|
||||
}
|
||||
mWaitState = WAITSTATE_WAITING_FOR_NEXT_ITERATION;
|
||||
} else {
|
||||
mGraphImpl->mGraphDriverAsleep = true; // atomic
|
||||
mWaitState = WAITSTATE_WAITING_INDEFINITELY;
|
||||
}
|
||||
if (timeout > 0) {
|
||||
mGraphImpl->GetMonitor().Wait(timeout);
|
||||
|
@ -424,13 +434,19 @@ SystemClockDriver::WaitForNextIteration()
|
|||
if (mWaitState == WAITSTATE_WAITING_INDEFINITELY) {
|
||||
mGraphImpl->mGraphDriverAsleep = false; // atomic
|
||||
}
|
||||
// Note: this can race against the EnsureNextIteration setting
|
||||
// WAITSTATE_RUNNING and setting mGraphDriverAsleep to false, so you can
|
||||
// have an iteration with WAITSTATE_WAKING_UP instead of RUNNING.
|
||||
mWaitState = WAITSTATE_RUNNING;
|
||||
mGraphImpl->mNeedAnotherIteration = false;
|
||||
mGraphImpl->mNeedAnotherIteration = false; // atomic
|
||||
}
|
||||
|
||||
void SystemClockDriver::WakeUp()
|
||||
{
|
||||
mGraphImpl->GetMonitor().AssertCurrentThreadOwns();
|
||||
// Note: this can race against the thread setting WAITSTATE_RUNNING and
|
||||
// setting mGraphDriverAsleep to false, so you can have an iteration
|
||||
// with WAITSTATE_WAKING_UP instead of RUNNING.
|
||||
mWaitState = WAITSTATE_WAKING_UP;
|
||||
mGraphImpl->mGraphDriverAsleep = false; // atomic
|
||||
mGraphImpl->GetMonitor().Notify();
|
||||
|
|
|
@ -528,8 +528,6 @@ MediaDecoderReader::Shutdown()
|
|||
// Shut down the watch manager before shutting down our task queue.
|
||||
mWatchManager.Shutdown();
|
||||
|
||||
RefPtr<ShutdownPromise> p;
|
||||
|
||||
mDecoder = nullptr;
|
||||
|
||||
ReaderQueue::Instance().Remove(this);
|
||||
|
|
|
@ -522,6 +522,8 @@ public:
|
|||
void EnsureNextIteration()
|
||||
{
|
||||
mNeedAnotherIteration = true; // atomic
|
||||
// Note: GraphDriver must ensure that there's no race on setting
|
||||
// mNeedAnotherIteration and mGraphDriverAsleep -- see WaitForNextIteration()
|
||||
if (mGraphDriverAsleep) { // atomic
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
CurrentDriver()->WakeUp(); // Might not be the same driver; might have woken already
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
// Service instantiation
|
||||
#include "ipc/SmsIPCService.h"
|
||||
#include "MobileMessageService.h"
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_WEBSMS_BACKEND)
|
||||
#include "android/MobileMessageDatabaseService.h"
|
||||
#include "android/SmsService.h"
|
||||
#elif defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
|
||||
|
@ -853,7 +853,7 @@ NS_CreateSmsService()
|
|||
if (XRE_IsContentProcess()) {
|
||||
smsService = SmsIPCService::GetSingleton();
|
||||
} else {
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_WEBSMS_BACKEND)
|
||||
smsService = new SmsService();
|
||||
#elif defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
|
||||
smsService = do_GetService(GONK_SMSSERVICE_CONTRACTID);
|
||||
|
@ -870,7 +870,7 @@ NS_CreateMobileMessageDatabaseService()
|
|||
if (XRE_IsContentProcess()) {
|
||||
mobileMessageDBService = SmsIPCService::GetSingleton();
|
||||
} else {
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_WEBSMS_BACKEND)
|
||||
mobileMessageDBService = new MobileMessageDatabaseService();
|
||||
#elif defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
|
||||
mobileMessageDBService =
|
||||
|
|
|
@ -16,7 +16,7 @@ EXPORTS.mozilla.dom.mobilemessage += [
|
|||
'Types.h', # Required by IPDL SmsTypes.h
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android' and CONFIG['MOZ_WEBSMS_BACKEND']:
|
||||
SOURCES += [
|
||||
'android/MobileMessageDatabaseService.cpp',
|
||||
'android/SmsManager.cpp',
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
<h1>I'm just a support file</h1>
|
||||
<p>I get loaded to do permission testing.</p>
|
|
@ -2,6 +2,7 @@
|
|||
support-files =
|
||||
file_framework.js
|
||||
file_shim.html
|
||||
file_empty.html
|
||||
|
||||
[test_alarms.html]
|
||||
skip-if = true
|
||||
|
|
|
@ -4,143 +4,203 @@
|
|||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Permissions API</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body onload='runTests()'>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.8">
|
||||
'use strict';
|
||||
|
||||
let { UNKNOWN_ACTION, PROMPT_ACTION, ALLOW_ACTION, DENY_ACTION } =
|
||||
SpecialPowers.Ci.nsIPermissionManager;
|
||||
<body>
|
||||
<pre id="test"></pre>
|
||||
<script type="application/javascript;version=1.8">
|
||||
/*globals SpecialPowers, SimpleTest, is, ok, */
|
||||
'use strict';
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
const {
|
||||
UNKNOWN_ACTION,
|
||||
PROMPT_ACTION,
|
||||
ALLOW_ACTION,
|
||||
DENY_ACTION
|
||||
} = SpecialPowers.Ci.nsIPermissionManager;
|
||||
|
||||
const PERMISSIONS = [
|
||||
{ name: 'geolocation', perm: 'geo' },
|
||||
{ name: 'notifications', perm: 'desktop-notification' },
|
||||
{ name: 'push', perm: 'desktop-notification' },
|
||||
];
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const UNSUPPORTED_PERMISSIONS = [
|
||||
'foobarbaz', // Not in spec, for testing only.
|
||||
'midi'
|
||||
];
|
||||
const PERMISSIONS = [{
|
||||
name: 'geolocation',
|
||||
type: 'geo'
|
||||
}, {
|
||||
name: 'notifications',
|
||||
type: 'desktop-notification'
|
||||
}, {
|
||||
name: 'push',
|
||||
type: 'desktop-notification'
|
||||
}, ];
|
||||
|
||||
function setPermissions(action) {
|
||||
let permissions = PERMISSIONS.map(x => {
|
||||
return { 'type': x.perm, 'allow': action, 'context': document };
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
SpecialPowers.popPermissions(() => {
|
||||
SpecialPowers.pushPermissions(permissions, resolve);
|
||||
const UNSUPPORTED_PERMISSIONS = [
|
||||
'foobarbaz', // Not in spec, for testing only.
|
||||
'midi',
|
||||
];
|
||||
|
||||
// Create a closure, so that tests are run on the correct window object.
|
||||
function createPermissionTester(aWindow) {
|
||||
return {
|
||||
setPermissions(allow) {
|
||||
const permissions = PERMISSIONS.map(({ type }) => {
|
||||
return {
|
||||
type,
|
||||
allow,
|
||||
'context': aWindow.document
|
||||
};
|
||||
});
|
||||
return new Promise((resolve) => {
|
||||
SpecialPowers.popPermissions(() => {
|
||||
SpecialPowers.pushPermissions(permissions, resolve);
|
||||
});
|
||||
});
|
||||
},
|
||||
revokePermissions() {
|
||||
const promisesToRevoke = PERMISSIONS.map(({ name }) => {
|
||||
return aWindow.navigator.permissions
|
||||
.revoke({ name })
|
||||
.then(
|
||||
({ state }) => is(state, 'prompt', `correct state for '${name}'`),
|
||||
() => ok(false, `revoke should not have rejected for '${name}'`)
|
||||
);
|
||||
});
|
||||
return Promise.all(promisesToRevoke);
|
||||
},
|
||||
revokeUnsupportedPermissions() {
|
||||
const promisesToRevoke = UNSUPPORTED_PERMISSIONS.map(({ name }) => {
|
||||
return aWindow.navigator.permissions
|
||||
.revoke({ name })
|
||||
.then(
|
||||
() => ok(false, `revoke should not have resolved for '${name}'`),
|
||||
error => is(error.name, 'TypeError', `revoke should have thrown TypeError for '${name}'`)
|
||||
);
|
||||
});
|
||||
return Promise.all(promisesToRevoke);
|
||||
},
|
||||
checkPermissions(state) {
|
||||
const promisesToQuery = PERMISSIONS.map(({ name }) => {
|
||||
return aWindow.navigator.permissions
|
||||
.query({ name })
|
||||
.then(
|
||||
() => is(state, state, `correct state for '${name}'`),
|
||||
() => ok(false, `query should not have rejected for '${name}'`)
|
||||
);
|
||||
});
|
||||
return Promise.all(promisesToQuery);
|
||||
},
|
||||
checkUnsupportedPermissions() {
|
||||
const promisesToQuery = UNSUPPORTED_PERMISSIONS.map(({ name }) => {
|
||||
return aWindow.navigator.permissions
|
||||
.query({ name })
|
||||
.then(
|
||||
() => ok(false, `query should not have resolved for '${name}'`),
|
||||
error => {
|
||||
is(error.name, 'TypeError',
|
||||
`query should have thrown TypeError for '${name}'`);
|
||||
}
|
||||
);
|
||||
});
|
||||
return Promise.all(promisesToQuery);
|
||||
},
|
||||
promiseStateChanged(name, state) {
|
||||
return aWindow.navigator.permissions
|
||||
.query({ name })
|
||||
.then(status => {
|
||||
return new Promise( resolve => {
|
||||
status.onchange = () => {
|
||||
status.onchange = null;
|
||||
is(status.state, state, `state changed for '${name}'`);
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
},
|
||||
() => ok(false, `query should not have rejected for '${name}'`));
|
||||
},
|
||||
testStatusOnChange() {
|
||||
return new Promise((resolve) => {
|
||||
SpecialPowers.popPermissions(() => {
|
||||
const permission = 'geolocation';
|
||||
const promiseGranted = this.promiseStateChanged(permission, 'granted');
|
||||
this.setPermissions(ALLOW_ACTION);
|
||||
promiseGranted.then(() => {
|
||||
const promisePrompt = this.promiseStateChanged(permission, 'prompt');
|
||||
SpecialPowers.popPermissions();
|
||||
return promisePrompt;
|
||||
}).then(resolve);
|
||||
});
|
||||
});
|
||||
},
|
||||
testInvalidQuery() {
|
||||
return aWindow.navigator.permissions
|
||||
.query({ name: 'invalid' })
|
||||
.then(
|
||||
() => ok(false, 'invalid query should not have resolved'),
|
||||
() => ok(true, 'invalid query should have rejected')
|
||||
);
|
||||
},
|
||||
testInvalidRevoke() {
|
||||
return aWindow.navigator.permissions
|
||||
.revoke({ name: 'invalid' })
|
||||
.then(
|
||||
() => ok(false, 'invalid revoke should not have resolved'),
|
||||
() => ok(true, 'invalid revoke should have rejected')
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function enablePrefs() {
|
||||
const ops = {
|
||||
'set': [
|
||||
['dom.permissions.revoke.enable', true],
|
||||
],
|
||||
};
|
||||
return SpecialPowers.pushPrefEnv(ops);
|
||||
}
|
||||
|
||||
function createIframe() {
|
||||
return new Promise((resolve) => {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.src = 'file_empty.html';
|
||||
iframe.onload = () => resolve(iframe.contentWindow);
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function revokePermissions(action) {
|
||||
return Promise.all(PERMISSIONS.map(x =>
|
||||
navigator.permissions.revoke({ name: x.name }).then(
|
||||
result => is(result.state, "prompt", `correct state for '${x.name}'`),
|
||||
error => ok(false, `revoke should not have rejected for '${x.name}'`))
|
||||
));
|
||||
}
|
||||
|
||||
function revokeUnsupportedPermissions() {
|
||||
return Promise.all(UNSUPPORTED_PERMISSIONS.map(name =>
|
||||
navigator.permissions.revoke({ name: name }).then(
|
||||
result => ok(false, `revoke should not have resolved for '${name}'`),
|
||||
error => is(error.name, 'TypeError', `revoke should have thrown TypeError for '${name}'`))
|
||||
));
|
||||
}
|
||||
|
||||
function checkPermissions(state) {
|
||||
return Promise.all(PERMISSIONS.map(x => {
|
||||
return navigator.permissions.query({ name: x.name }).then(
|
||||
result => is(result.state, state, `correct state for '${x.name}'`),
|
||||
error => ok(false, `query should not have rejected for '${x.name}'`));
|
||||
}));
|
||||
}
|
||||
|
||||
function checkUnsupportedPermissions() {
|
||||
return Promise.all(UNSUPPORTED_PERMISSIONS.map(name => {
|
||||
return navigator.permissions.query({ name: name }).then(
|
||||
result => ok(false, `query should not have resolved for '${name}'`),
|
||||
error => {
|
||||
is(error.name, 'TypeError',
|
||||
`query should have thrown TypeError for '${name}'`);
|
||||
}
|
||||
debugger;
|
||||
window.onload = () => {
|
||||
enablePrefs()
|
||||
.then(createIframe)
|
||||
.then(createPermissionTester)
|
||||
.then((tester) => {
|
||||
return tester
|
||||
.checkUnsupportedPermissions()
|
||||
.then(() => tester.setPermissions(UNKNOWN_ACTION))
|
||||
.then(() => tester.checkPermissions('prompt'))
|
||||
.then(() => tester.setPermissions(PROMPT_ACTION))
|
||||
.then(() => tester.checkPermissions('prompt'))
|
||||
.then(() => tester.setPermissions(ALLOW_ACTION))
|
||||
.then(() => tester.checkPermissions('granted'))
|
||||
.then(() => tester.setPermissions(DENY_ACTION))
|
||||
.then(() => tester.checkPermissions('denied'))
|
||||
.then(() => tester.testStatusOnChange())
|
||||
.then(() => tester.testInvalidQuery())
|
||||
.then(() => tester.revokeUnsupportedPermissions())
|
||||
.then(() => tester.revokePermissions())
|
||||
.then(() => tester.checkPermissions('prompt'))
|
||||
.then(() => tester.testInvalidRevoke());
|
||||
})
|
||||
.then(SimpleTest.finish)
|
||||
.catch((e) => {
|
||||
ok(false, `Unexpected error ${e}`);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
function promiseStateChanged(name, state) {
|
||||
return navigator.permissions.query({ name }).then(
|
||||
status => {
|
||||
return new Promise((resolve, reject) => {
|
||||
status.onchange = () => {
|
||||
status.onchange = null;
|
||||
is(status.state, state, `state changed for '${name}'`);
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
},
|
||||
error => ok(false, `query should not have rejected for '${name}'`));
|
||||
}
|
||||
|
||||
function testStatusOnChange() {
|
||||
return new Promise((resolve, reject) => {
|
||||
SpecialPowers.popPermissions(() => {
|
||||
let permission = 'geolocation';
|
||||
let promiseGranted = promiseStateChanged(permission, 'granted');
|
||||
setPermissions(ALLOW_ACTION);
|
||||
promiseGranted.then(() => {
|
||||
let promisePrompt = promiseStateChanged(permission, 'prompt');
|
||||
SpecialPowers.popPermissions();
|
||||
return promisePrompt;
|
||||
}).then(resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testInvalidQuery() {
|
||||
navigator.permissions.query({ name: 'invalid' }).then(
|
||||
result => ok(false, 'invalid query should not have resolved'),
|
||||
error => ok(true, 'invalid query should have rejected'));
|
||||
}
|
||||
|
||||
function testInvalidRevoke() {
|
||||
navigator.permissions.revoke({ name: 'invalid' }).then(
|
||||
result => ok(false, 'invalid revoke should not have resolved'),
|
||||
error => ok(true, 'invalid revoke should have rejected'));
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
checkUnsupportedPermissions()
|
||||
.then(() => setPermissions(UNKNOWN_ACTION))
|
||||
.then(() => checkPermissions('prompt'))
|
||||
.then(() => setPermissions(PROMPT_ACTION))
|
||||
.then(() => checkPermissions('prompt'))
|
||||
.then(() => setPermissions(ALLOW_ACTION))
|
||||
.then(() => checkPermissions('granted'))
|
||||
.then(() => setPermissions(DENY_ACTION))
|
||||
.then(() => checkPermissions('denied'))
|
||||
.then(testStatusOnChange)
|
||||
.then(testInvalidQuery)
|
||||
.then(revokeUnsupportedPermissions)
|
||||
.then(revokePermissions)
|
||||
.then(() => checkPermissions('prompt'))
|
||||
.then(testInvalidRevoke)
|
||||
.then(SimpleTest.finish)
|
||||
.catch ((e) => {
|
||||
ok(false, 'Unexpected error ' + e);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -411,7 +411,6 @@ nsPluginStreamListenerPeer::SetupPluginCacheFile(nsIChannel* channel)
|
|||
return rv;
|
||||
|
||||
// create a file output stream to write to...
|
||||
nsCOMPtr<nsIOutputStream> outstream;
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(mFileCacheOutputStream), pluginTmp, -1, 00600);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
|
|
@ -3355,7 +3355,6 @@ PluginInstanceChild::CreateOptSurface(void)
|
|||
"Need a valid surface type here");
|
||||
NS_ASSERTION(!mCurrentSurface, "mCurrentSurfaceActor can get out of sync.");
|
||||
|
||||
RefPtr<gfxASurface> retsurf;
|
||||
// Use an opaque surface unless we're transparent and *don't* have
|
||||
// a background to source from.
|
||||
gfxImageFormat format =
|
||||
|
@ -3581,7 +3580,9 @@ PluginInstanceChild::EnsureCurrentBuffer(void)
|
|||
void
|
||||
PluginInstanceChild::UpdateWindowAttributes(bool aForceSetWindow)
|
||||
{
|
||||
#if defined(MOZ_X11) || defined(XP_WIN)
|
||||
RefPtr<gfxASurface> curSurface = mHelperSurface ? mHelperSurface : mCurrentSurface;
|
||||
#endif // Only used within MOZ_X11 or XP_WIN blocks. Unused variable otherwise
|
||||
bool needWindowUpdate = aForceSetWindow;
|
||||
#ifdef MOZ_X11
|
||||
Visual* visual = nullptr;
|
||||
|
|
|
@ -7488,8 +7488,6 @@ UpgradeDirectoryMetadataFrom1To2Helper::DoProcessOriginDirectories()
|
|||
{
|
||||
AssertIsOnIOThread();
|
||||
|
||||
nsCOMPtr<nsIFile> permanentStorageDir;
|
||||
|
||||
for (uint32_t count = mOriginProps.Length(), index = 0;
|
||||
index < count;
|
||||
index++) {
|
||||
|
|
|
@ -457,7 +457,6 @@ DOMStorageDBThread::OpenDatabaseConnection()
|
|||
= do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> connection;
|
||||
rv = service->OpenUnsharedDatabase(mDatabaseFile, getter_AddRefs(mWorkerConnection));
|
||||
if (rv == NS_ERROR_FILE_CORRUPTED) {
|
||||
// delete the db and try opening again
|
||||
|
|
|
@ -25,6 +25,6 @@ dictionary PermissionDescriptor {
|
|||
interface Permissions {
|
||||
[Throws]
|
||||
Promise<PermissionStatus> query(object permission);
|
||||
[Throws]
|
||||
[Throws, Pref="dom.permissions.revoke.enable"]
|
||||
Promise<PermissionStatus> revoke(object permission);
|
||||
};
|
||||
|
|
|
@ -1050,7 +1050,8 @@ public:
|
|||
const nsString& aLine, uint32_t aLineNumber,
|
||||
uint32_t aColumnNumber, uint32_t aFlags,
|
||||
uint32_t aErrorNumber, JSExnType aExnType,
|
||||
bool aMutedError, uint64_t aInnerWindowId)
|
||||
bool aMutedError, uint64_t aInnerWindowId,
|
||||
JS::Handle<JS::Value> aException = JS::NullHandleValue)
|
||||
{
|
||||
if (aWorkerPrivate) {
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
@ -1070,6 +1071,7 @@ public:
|
|||
init.mMessage = aMessage;
|
||||
init.mFilename = aFilename;
|
||||
init.mLineno = aLineNumber;
|
||||
init.mError = aException;
|
||||
}
|
||||
|
||||
init.mCancelable = true;
|
||||
|
@ -5919,6 +5921,12 @@ WorkerPrivate::ReportError(JSContext* aCx, const char* aFallbackMessage,
|
|||
mErrorHandlerRecursionCount == 1,
|
||||
"Bad recursion logic!");
|
||||
|
||||
JS::Rooted<JS::Value> exn(aCx);
|
||||
if (!JS_GetPendingException(aCx, &exn)) {
|
||||
// Probably shouldn't actually happen? But let's go ahead and just use null
|
||||
// for lack of anything better.
|
||||
exn.setNull();
|
||||
}
|
||||
JS_ClearPendingException(aCx);
|
||||
|
||||
nsString message, filename, line;
|
||||
|
@ -5966,7 +5974,7 @@ WorkerPrivate::ReportError(JSContext* aCx, const char* aFallbackMessage,
|
|||
ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, message,
|
||||
filename, line, lineNumber,
|
||||
columnNumber, flags, errorNumber, exnType,
|
||||
mutedError, 0);
|
||||
mutedError, 0, exn);
|
||||
|
||||
mErrorHandlerRecursionCount--;
|
||||
}
|
||||
|
|
|
@ -2613,7 +2613,7 @@ XMLHttpRequestMainThread::InitiateFetch(nsIInputStream* aUploadStream,
|
|||
|
||||
// Per spec, we throw on sync errors, but not async.
|
||||
if (mFlagSynchronous) {
|
||||
return rv;
|
||||
return NS_ERROR_DOM_NETWORK_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2874,7 +2874,7 @@ XMLHttpRequestMainThread::SendInternal(const RequestBodyBase* aBody)
|
|||
if (!mChannel) {
|
||||
// Per spec, silently fail on async request failures; throw for sync.
|
||||
if (mFlagSynchronous) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return NS_ERROR_DOM_NETWORK_ERR;
|
||||
} else {
|
||||
// Defer the actual sending of async events just in case listeners
|
||||
// are attached after the send() method is called.
|
||||
|
|
|
@ -167,8 +167,6 @@ nsXMLFragmentContentSink::WillBuildModel(nsDTDMode aDTDMode)
|
|||
NS_IMETHODIMP
|
||||
nsXMLFragmentContentSink::DidBuildModel(bool aTerminated)
|
||||
{
|
||||
RefPtr<nsParserBase> kungFuDeathGrip(mParser);
|
||||
|
||||
// Drop our reference to the parser to get rid of a circular
|
||||
// reference.
|
||||
mParser = nullptr;
|
||||
|
|
|
@ -225,7 +225,6 @@ nsXULTemplateQueryProcessorStorage::GetDatasource(nsIArray* aDataSources,
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = fileChannel->GetFile(getter_AddRefs(databaseFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
|
|
@ -281,11 +281,10 @@ RectTyped<units> IntRectToRect(const IntRectTyped<units>& aRect)
|
|||
return RectTyped<units>(aRect.x, aRect.y, aRect.width, aRect.height);
|
||||
}
|
||||
|
||||
// Convenience function for intersecting two IntRects wrapped in Maybes.
|
||||
template <typename Units>
|
||||
Maybe<IntRectTyped<Units>>
|
||||
IntersectMaybeRects(const Maybe<IntRectTyped<Units>>& a,
|
||||
const Maybe<IntRectTyped<Units>>& b)
|
||||
// Convenience function for intersecting two rectangles wrapped in Maybes.
|
||||
template <typename T>
|
||||
Maybe<T>
|
||||
IntersectMaybeRects(const Maybe<T>& a, const Maybe<T>& b)
|
||||
{
|
||||
if (!a) {
|
||||
return b;
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
virtual void SetContentController(GeckoContentController* aController) = 0;
|
||||
|
||||
// Return the Async Pan/Zoom Tree Manager for this compositor.
|
||||
virtual already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager() const = 0;
|
||||
virtual RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const = 0;
|
||||
|
||||
// Return the child end of the compositor IPC bridge.
|
||||
CompositorBridgeChild* GetCompositorBridgeChild();
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
#include "mozilla/ipc/ProcessChild.h"
|
||||
#include "mozilla/layers/APZThreadUtils.h"
|
||||
#include "mozilla/layers/CompositorBridgeParent.h"
|
||||
#include "mozilla/layers/CompositorThread.h"
|
||||
#include "mozilla/layers/ImageBridgeParent.h"
|
||||
|
@ -63,6 +64,7 @@ GPUParent::Init(base::ProcessId aParentPid,
|
|||
return false;
|
||||
}
|
||||
CompositorThreadHolder::Start();
|
||||
APZThreadUtils::SetControllerThread(CompositorThreadHolder::Loop());
|
||||
VRManager::ManagerInit();
|
||||
LayerTreeOwnerTracker::Initialize();
|
||||
mozilla::ipc::SetThisProcessName("GPU Process");
|
||||
|
@ -170,8 +172,12 @@ GPUParent::RecvGetDeviceStatus(GPUDeviceData* aOut)
|
|||
|
||||
#if defined(XP_WIN)
|
||||
if (DeviceManagerDx* dm = DeviceManagerDx::Get()) {
|
||||
dm->ExportDeviceInfo(&aOut->d3d11Device());
|
||||
D3D11DeviceStatus deviceStatus;
|
||||
dm->ExportDeviceInfo(&deviceStatus);
|
||||
aOut->gpuDevice() = deviceStatus;
|
||||
}
|
||||
#else
|
||||
aOut->gpuDevice() = null_t();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/layers/APZCTreeManager.h"
|
||||
#include "mozilla/layers/APZCTreeManagerChild.h"
|
||||
#include "mozilla/layers/CompositorBridgeParent.h"
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/layers/ImageBridgeParent.h"
|
||||
|
@ -394,8 +395,17 @@ GPUProcessManager::CreateRemoteSession(nsBaseWidget* aWidget,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<APZCTreeManagerChild> apz = nullptr;
|
||||
if (aUseAPZ) {
|
||||
PAPZCTreeManagerChild* papz = child->SendPAPZCTreeManagerConstructor(0);
|
||||
if (!papz) {
|
||||
return nullptr;
|
||||
}
|
||||
apz = static_cast<APZCTreeManagerChild*>(papz);
|
||||
}
|
||||
|
||||
RefPtr<RemoteCompositorSession> session =
|
||||
new RemoteCompositorSession(child, widget, aRootLayerTreeId);
|
||||
new RemoteCompositorSession(child, widget, apz, aRootLayerTreeId);
|
||||
return session.forget();
|
||||
#else
|
||||
gfxCriticalNote << "Platform does not support out-of-process compositing";
|
||||
|
|
|
@ -54,12 +54,18 @@ union FeatureChange
|
|||
FeatureFailure;
|
||||
};
|
||||
|
||||
union GPUDeviceStatus
|
||||
{
|
||||
null_t;
|
||||
D3D11DeviceStatus;
|
||||
};
|
||||
|
||||
struct GPUDeviceData
|
||||
{
|
||||
FeatureChange d3d11Compositing;
|
||||
FeatureChange d3d9Compositing;
|
||||
FeatureChange oglCompositing;
|
||||
D3D11DeviceStatus d3d11Device;
|
||||
GPUDeviceStatus gpuDevice;
|
||||
};
|
||||
|
||||
union GfxVarValue
|
||||
|
|
|
@ -54,7 +54,7 @@ InProcessCompositorSession::SetContentController(GeckoContentController* aContro
|
|||
mCompositorBridgeParent->SetControllerForLayerTree(mRootLayerTreeId, aController);
|
||||
}
|
||||
|
||||
already_AddRefed<IAPZCTreeManager>
|
||||
RefPtr<IAPZCTreeManager>
|
||||
InProcessCompositorSession::GetAPZCTreeManager() const
|
||||
{
|
||||
return mCompositorBridgeParent->GetAPZCTreeManager(mRootLayerTreeId);
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
|
||||
CompositorBridgeParent* GetInProcessBridge() const override;
|
||||
void SetContentController(GeckoContentController* aController) override;
|
||||
already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager() const override;
|
||||
RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override;
|
||||
void Shutdown() override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -3,8 +3,12 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "RemoteCompositorSession.h"
|
||||
|
||||
#include "mozilla/layers/APZChild.h"
|
||||
#include "mozilla/layers/APZCTreeManagerChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
|
@ -13,8 +17,10 @@ using namespace widget;
|
|||
|
||||
RemoteCompositorSession::RemoteCompositorSession(CompositorBridgeChild* aChild,
|
||||
CompositorWidgetDelegate* aWidgetDelegate,
|
||||
APZCTreeManagerChild* aAPZ,
|
||||
const uint64_t& aRootLayerTreeId)
|
||||
: CompositorSession(aWidgetDelegate, aChild, aRootLayerTreeId)
|
||||
, mAPZ(aAPZ)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -27,13 +33,13 @@ RemoteCompositorSession::GetInProcessBridge() const
|
|||
void
|
||||
RemoteCompositorSession::SetContentController(GeckoContentController* aController)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
mCompositorBridgeChild->SendPAPZConstructor(new APZChild(aController), 0);
|
||||
}
|
||||
|
||||
already_AddRefed<IAPZCTreeManager>
|
||||
RefPtr<IAPZCTreeManager>
|
||||
RemoteCompositorSession::GetAPZCTreeManager() const
|
||||
{
|
||||
return nullptr;
|
||||
return mAPZ;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -18,13 +18,16 @@ class RemoteCompositorSession final : public CompositorSession
|
|||
public:
|
||||
RemoteCompositorSession(CompositorBridgeChild* aChild,
|
||||
CompositorWidgetDelegate* aWidgetDelegate,
|
||||
APZCTreeManagerChild* aAPZ,
|
||||
const uint64_t& aRootLayerTreeId);
|
||||
|
||||
CompositorBridgeParent* GetInProcessBridge() const override;
|
||||
void SetContentController(GeckoContentController* aController) override;
|
||||
already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager() const override;
|
||||
RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override;
|
||||
void Shutdown() override;
|
||||
|
||||
private:
|
||||
RefPtr<APZCTreeManagerChild> mAPZ;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
|
|
|
@ -27,7 +27,11 @@ public:
|
|||
/**
|
||||
* Requests a paint of the given FrameMetrics |aFrameMetrics| from Gecko.
|
||||
* Implementations per-platform are responsible for actually handling this.
|
||||
* This method will always be called on the Gecko main thread.
|
||||
*
|
||||
* This method must always be called on the repaint thread, which depends
|
||||
* on the GeckoContentController. For ChromeProcessController it is the
|
||||
* Gecko main thread, while for RemoteContentController it is the compositor
|
||||
* thread where it can send IPDL messages.
|
||||
*/
|
||||
virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) = 0;
|
||||
|
||||
|
|
|
@ -171,6 +171,10 @@ ChromeProcessController::HandleTap(TapType aType,
|
|||
return;
|
||||
}
|
||||
|
||||
if (!mAPZEventState) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
|
||||
if (!presShell) {
|
||||
return;
|
||||
|
@ -217,6 +221,10 @@ ChromeProcessController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
|
|||
return;
|
||||
}
|
||||
|
||||
if (!mAPZEventState) {
|
||||
return;
|
||||
}
|
||||
|
||||
mAPZEventState->ProcessAPZStateChange(GetRootDocument(), aGuid.mScrollId, aChange, aArg);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,14 @@ namespace layers {
|
|||
class IAPZCTreeManager;
|
||||
class APZEventState;
|
||||
|
||||
// A ChromeProcessController is attached to the root of a compositor's layer
|
||||
// tree.
|
||||
/**
|
||||
* ChromeProcessController is a GeckoContentController attached to the root of
|
||||
* a compositor's layer tree. It's used directly by APZ by default, and remoted
|
||||
* using PAPZ if there is a gpu process.
|
||||
*
|
||||
* If ChromeProcessController needs to implement a new method on GeckoContentController
|
||||
* PAPZ, APZChild, and RemoteContentController must be updated to handle it.
|
||||
*/
|
||||
class ChromeProcessController : public mozilla::layers::GeckoContentController
|
||||
{
|
||||
protected:
|
||||
|
|
|
@ -21,6 +21,17 @@ namespace layers {
|
|||
|
||||
class APZChild;
|
||||
|
||||
/**
|
||||
* ContentProcessController is a GeckoContentController for a TabChild, and is always
|
||||
* remoted using PAPZ/APZChild.
|
||||
*
|
||||
* ContentProcessController is created in ContentChild when a layer tree id has
|
||||
* been allocated for a PBrowser that lives in that content process, and is destroyed
|
||||
* when the Destroy message is received, or when the tab dies.
|
||||
*
|
||||
* If ContentProcessController needs to implement a new method on GeckoContentController
|
||||
* PAPZ, APZChild, and RemoteContentController must be updated to handle it.
|
||||
*/
|
||||
class ContentProcessController final
|
||||
: public GeckoContentController
|
||||
{
|
||||
|
|
|
@ -15,6 +15,10 @@ namespace layers {
|
|||
|
||||
class GeckoContentController;
|
||||
|
||||
/**
|
||||
* APZChild implements PAPZChild and is used to remote a GeckoContentController
|
||||
* that lives in a different process than where APZ lives.
|
||||
*/
|
||||
class APZChild final : public PAPZChild
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -1432,31 +1432,64 @@ CompositorBridgeParent::ForceComposeToTarget(DrawTarget* aTarget, const gfx::Int
|
|||
PAPZCTreeManagerParent*
|
||||
CompositorBridgeParent::AllocPAPZCTreeManagerParent(const uint64_t& aLayersId)
|
||||
{
|
||||
return nullptr;
|
||||
// The main process should pass in 0 because we assume mRootLayerTreeID
|
||||
MOZ_ASSERT(aLayersId == 0);
|
||||
|
||||
// This message doubles as initialization
|
||||
MOZ_ASSERT(!mApzcTreeManager);
|
||||
mApzcTreeManager = new APZCTreeManager();
|
||||
|
||||
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
||||
CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID];
|
||||
MOZ_ASSERT(state.mParent);
|
||||
MOZ_ASSERT(!state.mApzcTreeManagerParent);
|
||||
state.mApzcTreeManagerParent = new APZCTreeManagerParent(mRootLayerTreeID, state.mParent->GetAPZCTreeManager());
|
||||
|
||||
return state.mApzcTreeManagerParent;
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor)
|
||||
{
|
||||
return false;
|
||||
delete aActor;
|
||||
return true;
|
||||
}
|
||||
|
||||
PAPZParent*
|
||||
CompositorBridgeParent::AllocPAPZParent(const uint64_t& aLayersId)
|
||||
{
|
||||
return nullptr;
|
||||
// The main process should pass in 0 because we assume mRootLayerTreeID
|
||||
MOZ_ASSERT(aLayersId == 0);
|
||||
|
||||
RemoteContentController* controller = new RemoteContentController();
|
||||
|
||||
// Increment the controller's refcount before we return it. This will keep the
|
||||
// controller alive until it is released by IPDL in DeallocPAPZParent.
|
||||
controller->AddRef();
|
||||
|
||||
MonitorAutoLock lock(*sIndirectLayerTreesLock);
|
||||
CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID];
|
||||
MOZ_ASSERT(!state.mController);
|
||||
state.mController = controller;
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor)
|
||||
{
|
||||
return false;
|
||||
RemoteContentController* controller = static_cast<RemoteContentController*>(aActor);
|
||||
controller->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorBridgeParent::RecvAsyncPanZoomEnabled(const uint64_t& aLayersId, bool* aHasAPZ)
|
||||
{
|
||||
return false;
|
||||
// The main process should pass in 0 because we assume mRootLayerTreeID
|
||||
MOZ_ASSERT(aLayersId == 0);
|
||||
*aHasAPZ = AsyncPanZoomEnabled();
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<APZCTreeManager>
|
||||
|
|
|
@ -29,13 +29,19 @@ namespace layers {
|
|||
|
||||
|
||||
/**
|
||||
* If APZ is enabled then one PAPZ will be opened per PBrowser between the
|
||||
* process where the PBrowser child actor lives and the main process (the
|
||||
* PBrowser parent actor doesn't necessarily live in the main process, for
|
||||
* example with nested browsers). This will typically be set up when the layers
|
||||
* id is allocated for the PBrowser.
|
||||
* PAPZ is a protocol for remoting a GeckoContentController. PAPZ lives on the
|
||||
* PCompositorBridge protocol which either connects to the compositor thread
|
||||
* in the main process, or to the compositor thread in the gpu processs.
|
||||
*
|
||||
* Opened through PContent and runs on the main thread in both parent and child.
|
||||
* PAPZParent lives in the compositor thread, while PAPZChild lives wherever the remoted
|
||||
* GeckoContentController lives (generally the main thread of the main or content process).
|
||||
* RemoteContentController implements PAPZParent, while APZChild implements PAPZChild.
|
||||
*
|
||||
* PAPZ is always used for ContentProcessController and only used for ChromeProcessController
|
||||
* when there is a gpu process, otherwhise ChromeProcessController is used directly on the
|
||||
* compositor thread. Only the methods that are used by the [Chrome,Content]ProcessController
|
||||
* are implemented. If a new method is needed then PAPZ, APZChild, and RemoteContentController
|
||||
* must be updated to handle it.
|
||||
*/
|
||||
sync protocol PAPZ
|
||||
{
|
||||
|
@ -48,6 +54,7 @@ parent:
|
|||
async __delete__();
|
||||
|
||||
child:
|
||||
|
||||
async RequestContentRepaint(FrameMetrics frame);
|
||||
|
||||
// The aCallTakeFocusForClickFromTap argument is used for eSingleTap types,
|
||||
|
|
|
@ -34,6 +34,15 @@ using class mozilla::ScrollWheelInput from "InputData.h";
|
|||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
/**
|
||||
* PAPZCTreeManager is a protocol for remoting an IAPZCTreeManager. PAPZCTreeManager
|
||||
* lives on the PCompositorBridge protocol which either connects to the compositor
|
||||
* thread in the main process, or to the compositor thread in the gpu processs.
|
||||
*
|
||||
* PAPZCTreeManagerParent lives in the compositor thread, while PAPZCTreeManagerChild
|
||||
* lives in the main thread of the main or the content process. APZCTreeManagerParent
|
||||
* and APZCTreeManagerChild implement this protocol.
|
||||
*/
|
||||
sync protocol PAPZCTreeManager
|
||||
{
|
||||
manager PCompositorBridge;
|
||||
|
|
|
@ -20,11 +20,14 @@ class TabParent;
|
|||
namespace layers {
|
||||
|
||||
/**
|
||||
* RemoteContentController uses the PAPZ protocol to implement a
|
||||
* GeckoContentController for a browser living in a remote process.
|
||||
* Most of the member functions can be called on any thread, exceptions are
|
||||
* annotated in comments. The PAPZ protocol runs on the main thread (so all the
|
||||
* Recv* member functions do too).
|
||||
* RemoteContentController implements PAPZChild and is used to access a
|
||||
* GeckoContentController that lives in a different process.
|
||||
*
|
||||
* RemoteContentController lives on the compositor thread. All methods can
|
||||
* be called off the compositor thread and will get dispatched to the right
|
||||
* thread, with the exception of RequestContentRepaint and NotifyFlushComplete,
|
||||
* which must be called on the repaint thread, which in this case is the compositor
|
||||
* thread.
|
||||
*/
|
||||
class RemoteContentController : public GeckoContentController
|
||||
, public PAPZParent
|
||||
|
|
|
@ -219,6 +219,9 @@ public:
|
|||
if (!gFontHintingEnabled || !isAxisAligned(*rec)) {
|
||||
rec->setHinting(SkPaint::kNo_Hinting);
|
||||
}
|
||||
|
||||
// Don't apply any gamma so that we match cairo-ft's results.
|
||||
rec->ignorePreBlend();
|
||||
}
|
||||
|
||||
virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const override
|
||||
|
@ -760,7 +763,7 @@ void SkScalerContext_CairoFT::generatePath(const SkGlyph& glyph, SkPath* path)
|
|||
CairoLockedFTFace faceLock(fScaledFont);
|
||||
FT_Face face = faceLock.getFace();
|
||||
|
||||
SkASSERT(&glyph && path);
|
||||
SkASSERT(path);
|
||||
|
||||
uint32_t flags = fLoadGlyphFlags;
|
||||
flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline
|
||||
|
@ -780,7 +783,9 @@ void SkScalerContext_CairoFT::generatePath(const SkGlyph& glyph, SkPath* path)
|
|||
|
||||
void SkScalerContext_CairoFT::generateFontMetrics(SkPaint::FontMetrics* metrics)
|
||||
{
|
||||
SkDEBUGCODE(SkDebugf("SkScalerContext_CairoFT::generateFontMetrics unimplemented\n"));
|
||||
if (metrics) {
|
||||
memset(metrics, 0, sizeof(SkPaint::FontMetrics));
|
||||
}
|
||||
}
|
||||
|
||||
SkUnichar SkScalerContext_CairoFT::generateGlyphToChar(uint16_t glyph)
|
||||
|
|
|
@ -2004,7 +2004,7 @@ gfxWindowsPlatform::ImportGPUDeviceData(const mozilla::gfx::GPUDeviceData& aData
|
|||
|
||||
DeviceManagerDx* dm = DeviceManagerDx::Get();
|
||||
if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
|
||||
dm->ImportDeviceInfo(aData.d3d11Device());
|
||||
dm->ImportDeviceInfo(aData.gpuDevice().get_D3D11DeviceStatus());
|
||||
} else {
|
||||
// There should be no devices, so this just takes away the device status.
|
||||
dm->ResetDevices();
|
||||
|
|
|
@ -23,6 +23,10 @@
|
|||
#include "prenv.h"
|
||||
#include "nsXPCOMPrivate.h"
|
||||
|
||||
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#endif
|
||||
|
||||
#include "nsExceptionHandler.h"
|
||||
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
|
@ -608,6 +612,20 @@ AddAppDirToCommandLine(std::vector<std::string>& aCmdLine)
|
|||
aCmdLine.push_back(path.get());
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
|
||||
// Full path to the profile dir
|
||||
nsCOMPtr<nsIFile> profileDir;
|
||||
rv = directoryService->Get(NS_APP_USER_PROFILE_50_DIR,
|
||||
NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(profileDir));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsAutoCString path;
|
||||
MOZ_ALWAYS_SUCCEEDS(profileDir->GetNativePath(path));
|
||||
aCmdLine.push_back("-profile");
|
||||
aCmdLine.push_back(path.get());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -464,5 +464,17 @@ void ArrayLengthReadError(const char* aElementName)
|
|||
NS_RUNTIMEABORT(message.get());
|
||||
}
|
||||
|
||||
void
|
||||
TableToArray(const nsTHashtable<nsPtrHashKey<void>>& aTable,
|
||||
nsTArray<void*>& aArray)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
void** elements = aArray.AppendElements(aTable.Count());
|
||||
for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
elements[i] = iter.Get()->GetKey();
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=4 ts=4 et :
|
||||
*/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
|
@ -121,11 +121,13 @@ struct Trigger
|
|||
|
||||
Trigger(Action action, int32_t msg) :
|
||||
mAction(action),
|
||||
mMsg(msg)
|
||||
{}
|
||||
mMessage(msg)
|
||||
{
|
||||
MOZ_ASSERT(0 <= msg && msg < INT32_MAX);
|
||||
}
|
||||
|
||||
Action mAction;
|
||||
int32_t mMsg;
|
||||
uint32_t mAction : 1;
|
||||
uint32_t mMessage : 31;
|
||||
};
|
||||
|
||||
class ProtocolCloneContext
|
||||
|
@ -604,10 +606,31 @@ CreateEndpoints(const PrivateIPDLInterface& aPrivate,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
TableToArray(const nsTHashtable<nsPtrHashKey<void>>& aTable,
|
||||
nsTArray<void*>& aArray);
|
||||
|
||||
} // namespace ipc
|
||||
|
||||
template<typename Protocol>
|
||||
using ManagedContainer = nsTHashtable<nsPtrHashKey<Protocol>>;
|
||||
class ManagedContainer : public nsTHashtable<nsPtrHashKey<Protocol>>
|
||||
{
|
||||
typedef nsTHashtable<nsPtrHashKey<Protocol>> BaseClass;
|
||||
|
||||
public:
|
||||
// Having the core logic work on void pointers, rather than typed pointers,
|
||||
// means that we can have one instance of this code out-of-line, rather
|
||||
// than several hundred instances of this code out-of-lined. (Those
|
||||
// repeated instances don't necessarily get folded together by the linker
|
||||
// because they contain member offsets and such that differ between the
|
||||
// functions.) We do have to pay for it with some eye-bleedingly bad casts,
|
||||
// though.
|
||||
void ToArray(nsTArray<Protocol*>& aArray) const {
|
||||
::mozilla::ipc::TableToArray(*reinterpret_cast<const nsTHashtable<nsPtrHashKey<void>>*>
|
||||
(static_cast<const BaseClass*>(this)),
|
||||
reinterpret_cast<nsTArray<void*>&>(aArray));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Protocol>
|
||||
Protocol*
|
||||
|
|
|
@ -50,6 +50,7 @@ HeaderIncludes = (
|
|||
'nsTArray.h',
|
||||
'mozilla/ipc/ProtocolUtils.h',
|
||||
'nsTHashtable.h',
|
||||
'mozilla/OperatorNewExtensions.h',
|
||||
)
|
||||
|
||||
CppIncludes = (
|
||||
|
|
|
@ -851,7 +851,7 @@ IPDL union type."""
|
|||
else:
|
||||
return ExprNew(self.bareType(self.side),
|
||||
args=args,
|
||||
newargs=[ self.callGetPtr() ])
|
||||
newargs=[ ExprVar('mozilla::KnownNotNull'), self.callGetPtr() ])
|
||||
|
||||
def callDtor(self):
|
||||
if self.recursive:
|
||||
|
@ -1797,17 +1797,17 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
|
|||
else:
|
||||
return ExprVar(s.decl.cxxname)
|
||||
|
||||
# bool Transition(State from, Trigger trigger, State* next)
|
||||
# bool Transition(Trigger trigger, State* next)
|
||||
# The state we are transitioning from is stored in *next.
|
||||
fromvar = ExprVar('from')
|
||||
triggervar = ExprVar('trigger')
|
||||
nextvar = ExprVar('next')
|
||||
msgexpr = ExprSelect(triggervar, '.', 'mMsg')
|
||||
msgexpr = ExprSelect(triggervar, '.', 'mMessage')
|
||||
actionexpr = ExprSelect(triggervar, '.', 'mAction')
|
||||
|
||||
transitionfunc = FunctionDefn(FunctionDecl(
|
||||
'Transition',
|
||||
params=[ Decl(Type('State'), fromvar.name),
|
||||
Decl(Type('mozilla::ipc::Trigger'), triggervar.name),
|
||||
params=[ Decl(Type('mozilla::ipc::Trigger'), triggervar.name),
|
||||
Decl(Type('State', ptr=1), nextvar.name) ],
|
||||
ret=Type.BOOL))
|
||||
|
||||
|
@ -1902,6 +1902,8 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
|
|||
if usesend or userecv:
|
||||
transitionfunc.addstmt(Whitespace.NL)
|
||||
|
||||
transitionfunc.addstmt(StmtDecl(Decl(Type('State'), fromvar.name),
|
||||
init=ExprDeref(nextvar)))
|
||||
transitionfunc.addstmt(fromswitch)
|
||||
# all --> Error transitions break to here. But only insert this
|
||||
# block if there is any possibility of such transitions.
|
||||
|
@ -3086,22 +3088,10 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
params=[ Decl(_cxxArrayType(p.managedCxxType(managed, self.side), ref=1),
|
||||
arrvar.name) ],
|
||||
const=1))
|
||||
ivar = ExprVar('i')
|
||||
elementsvar = ExprVar('elements')
|
||||
itervar = ExprVar('iter')
|
||||
meth.addstmt(StmtDecl(Decl(Type.UINT32, ivar.name),
|
||||
init=ExprLiteral.ZERO))
|
||||
meth.addstmt(StmtDecl(Decl(Type(_actorName(managed.name(), self.side), ptrptr=1), elementsvar.name),
|
||||
init=ExprCall(ExprSelect(arrvar, '.', 'AppendElements'),
|
||||
args=[ ExprCall(ExprSelect(p.managedVar(managed, self.side),
|
||||
'.', 'Count')) ])))
|
||||
foreachaccumulate = forLoopOverHashtable(p.managedVar(managed, self.side),
|
||||
itervar, const=True)
|
||||
foreachaccumulate.addstmt(StmtExpr(
|
||||
ExprAssn(ExprIndex(elementsvar, ivar),
|
||||
actorFromIter(itervar))))
|
||||
foreachaccumulate.addstmt(StmtExpr(ExprPrefixUnop(ivar, '++')))
|
||||
meth.addstmt(foreachaccumulate)
|
||||
meth.addstmt(StmtExpr(
|
||||
ExprCall(ExprSelect(p.managedVar(managed, self.side),
|
||||
'.', 'ToArray'),
|
||||
args=[ arrvar ])))
|
||||
|
||||
refmeth = MethodDefn(MethodDecl(
|
||||
p.managedMethod(managed, self.side).name,
|
||||
|
@ -5441,8 +5431,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
|||
ifbad = StmtIf(ExprNot(
|
||||
ExprCall(
|
||||
ExprVar(self.protocol.name +'::Transition'),
|
||||
args=[ stateexpr,
|
||||
ExprCall(ExprVar('Trigger'),
|
||||
args=[ ExprCall(ExprVar('Trigger'),
|
||||
args=[ action, ExprVar(msgid) ]),
|
||||
ExprAddrOf(stateexpr) ])))
|
||||
ifbad.addifstmts(_badTransition())
|
||||
|
|
|
@ -132,7 +132,7 @@ struct ParamTraits<mozilla::mscom::COMPtrHolder<Interface, _IID>>
|
|||
if (!proxyStream.GetInterface(_IID, (void**)&rawInterface)) {
|
||||
return false;
|
||||
}
|
||||
paramType::COMPtrType ptr(rawInterface);
|
||||
typename paramType::COMPtrType ptr(rawInterface);
|
||||
aResult->Set(mozilla::Move(ptr));
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Function.h"
|
||||
#include "mozilla/mscom/COMApartmentRegion.h"
|
||||
#include "mozilla/mscom/utils.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "mozilla/mscom/DispatchForwarder.h"
|
||||
#include "mozilla/mscom/MainThreadInvoker.h"
|
||||
#include "mozilla/mscom/Registration.h"
|
||||
#include "mozilla/mscom/utils.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include "mozilla/mscom/InterceptorLog.h"
|
||||
#include "mozilla/mscom/Registration.h"
|
||||
#include "mozilla/mscom/utils.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "DynamicallyLinkedFunctionPtr.h"
|
||||
#include "mozilla/mscom/EnsureMTA.h"
|
||||
#include "mozilla/mscom/ProxyStream.h"
|
||||
#include "mozilla/mscom/utils.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
|
|
|
@ -781,7 +781,7 @@ ParseVarOrConstStatement(AsmJSParser& parser, ParseNode** var)
|
|||
return true;
|
||||
}
|
||||
|
||||
*var = parser.statement(YieldIsName);
|
||||
*var = parser.statementListItem(YieldIsName);
|
||||
if (!*var)
|
||||
return false;
|
||||
|
||||
|
@ -7206,7 +7206,7 @@ CheckModuleReturn(ModuleValidator& m)
|
|||
}
|
||||
ts.ungetToken();
|
||||
|
||||
ParseNode* returnStmt = m.parser().statement(YieldIsName);
|
||||
ParseNode* returnStmt = m.parser().statementListItem(YieldIsName);
|
||||
if (!returnStmt)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -698,6 +698,19 @@ class FunctionCompiler
|
|||
}
|
||||
|
||||
private:
|
||||
// False means we're sure to be out-of-bounds after this bounds check.
|
||||
bool maybeAddBoundsCheck(MDefinition* base, const MWasmMemoryAccess& access)
|
||||
{
|
||||
if (access.offset() > uint32_t(INT32_MAX)) {
|
||||
curBlock_->end(MWasmTrap::New(alloc(), Trap::OutOfBounds));
|
||||
curBlock_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
if (!mg().usesSignal.forOOB)
|
||||
curBlock_->add(MWasmBoundsCheck::New(alloc(), base, access));
|
||||
return true;
|
||||
}
|
||||
|
||||
MDefinition* loadHeapPrivate(MDefinition* base, const MWasmMemoryAccess& access,
|
||||
bool isInt64 = false)
|
||||
{
|
||||
|
@ -708,8 +721,8 @@ class FunctionCompiler
|
|||
if (mg().isAsmJS()) {
|
||||
load = MAsmJSLoadHeap::New(alloc(), base, access);
|
||||
} else {
|
||||
if (!mg().usesSignal.forOOB)
|
||||
curBlock_->add(MWasmBoundsCheck::New(alloc(), base, access));
|
||||
if (!maybeAddBoundsCheck(base, access))
|
||||
return nullptr;
|
||||
load = MWasmLoad::New(alloc(), base, access, isInt64);
|
||||
}
|
||||
|
||||
|
@ -726,8 +739,8 @@ class FunctionCompiler
|
|||
if (mg().isAsmJS()) {
|
||||
store = MAsmJSStoreHeap::New(alloc(), base, access, v);
|
||||
} else {
|
||||
if (!mg().usesSignal.forOOB)
|
||||
curBlock_->add(MWasmBoundsCheck::New(alloc(), base, access));
|
||||
if (!maybeAddBoundsCheck(base, access))
|
||||
return;
|
||||
store = MWasmStore::New(alloc(), base, access, v);
|
||||
}
|
||||
|
||||
|
@ -1109,7 +1122,7 @@ class FunctionCompiler
|
|||
if (inDeadCode())
|
||||
return;
|
||||
|
||||
auto* ins = MAsmThrowUnreachable::New(alloc());
|
||||
auto* ins = MWasmTrap::New(alloc(), wasm::Trap::Unreachable);
|
||||
curBlock_->end(ins);
|
||||
curBlock_ = nullptr;
|
||||
}
|
||||
|
|
|
@ -101,9 +101,6 @@ function RadixSort(array, len, buffer, nbytes, signed, floating, comparefn) {
|
|||
return array;
|
||||
}
|
||||
|
||||
// Verify that the buffer is non-null
|
||||
assert(buffer !== null, "Attached data buffer should be reified when array length is >= 128.");
|
||||
|
||||
let aux = new List();
|
||||
for (let i = 0; i < len; i++) {
|
||||
aux[i] = 0;
|
||||
|
@ -114,6 +111,14 @@ function RadixSort(array, len, buffer, nbytes, signed, floating, comparefn) {
|
|||
|
||||
// Preprocess
|
||||
if (floating) {
|
||||
// This happens if the array object is constructed under JIT
|
||||
if (buffer === null) {
|
||||
buffer = callFunction(std_TypedArray_buffer, array);
|
||||
}
|
||||
|
||||
// Verify that the buffer is non-null
|
||||
assert(buffer !== null, "Attached data buffer should be reified when array length is >= 128.");
|
||||
|
||||
view = new Int32Array(buffer);
|
||||
|
||||
// Flip sign bit for positive numbers; flip all bits for negative
|
||||
|
|
|
@ -766,7 +766,7 @@ Parser<ParseHandler>::parse()
|
|||
if (!varScope.init(pc))
|
||||
return null();
|
||||
|
||||
Node pn = statements(YieldIsName);
|
||||
Node pn = statementList(YieldIsName);
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
|
@ -1296,7 +1296,7 @@ Parser<FullParseHandler>::checkStatementsEOF()
|
|||
// This is designed to be paired with parsing a statement list at the top
|
||||
// level.
|
||||
//
|
||||
// The statements() call breaks on TOK_RC, so make sure we've
|
||||
// The statementList() call breaks on TOK_RC, so make sure we've
|
||||
// reached EOF here.
|
||||
TokenKind tt;
|
||||
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
|
||||
|
@ -1732,7 +1732,7 @@ Parser<FullParseHandler>::evalBody(EvalSharedContext* evalsc)
|
|||
if (!lexicalScope.init(pc))
|
||||
return nullptr;
|
||||
|
||||
ParseNode* body = statements(YieldIsName);
|
||||
ParseNode* body = statementList(YieldIsName);
|
||||
if (!body)
|
||||
return nullptr;
|
||||
|
||||
|
@ -1805,7 +1805,7 @@ Parser<FullParseHandler>::globalBody(GlobalSharedContext* globalsc)
|
|||
if (!varScope.init(pc))
|
||||
return nullptr;
|
||||
|
||||
ParseNode* body = statements(YieldIsName);
|
||||
ParseNode* body = statementList(YieldIsName);
|
||||
if (!body)
|
||||
return nullptr;
|
||||
|
||||
|
@ -1844,7 +1844,7 @@ Parser<FullParseHandler>::moduleBody(ModuleSharedContext* modulesc)
|
|||
if (!mn)
|
||||
return null();
|
||||
|
||||
ParseNode* pn = statements(YieldIsKeyword);
|
||||
ParseNode* pn = statementList(YieldIsKeyword);
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
|
@ -2249,7 +2249,7 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
|
|||
|
||||
Node pn;
|
||||
if (type == StatementListBody) {
|
||||
pn = statements(yieldHandling);
|
||||
pn = statementList(yieldHandling);
|
||||
if (!pn)
|
||||
return null();
|
||||
} else {
|
||||
|
@ -2714,12 +2714,12 @@ Parser<ParseHandler>::checkFunctionDefinition(HandleAtom funAtom, Node pn, Funct
|
|||
// declarations. Otherwise it is a parse error.
|
||||
ParseContext::Statement* declaredInStmt = pc->innermostStatement();
|
||||
if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) {
|
||||
if (pc->sc()->strict()) {
|
||||
reportWithOffset(ParseError, false, pos.begin, JSMSG_FUNCTION_LABEL);
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(!pc->sc()->strict(),
|
||||
"labeled functions shouldn't be parsed in strict mode");
|
||||
|
||||
// Find the innermost non-label statement.
|
||||
// Find the innermost non-label statement. Report an error if it's
|
||||
// unbraced: functions can't appear in it. Otherwise the statement
|
||||
// (or its absence) determines the scope the function's bound in.
|
||||
while (declaredInStmt && declaredInStmt->kind() == StatementKind::Label)
|
||||
declaredInStmt = declaredInStmt->enclosing();
|
||||
|
||||
|
@ -2730,9 +2730,8 @@ Parser<ParseHandler>::checkFunctionDefinition(HandleAtom funAtom, Node pn, Funct
|
|||
}
|
||||
|
||||
if (declaredInStmt) {
|
||||
DeclarationKind declKind = DeclarationKind::LexicalFunction;
|
||||
if (!checkLexicalDeclarationDirectlyWithinBlock(*declaredInStmt, declKind, pos))
|
||||
return false;
|
||||
MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label);
|
||||
MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind()));
|
||||
|
||||
if (!pc->sc()->strict()) {
|
||||
// Under sloppy mode, try Annex B.3.3 semantics. If making an
|
||||
|
@ -2745,7 +2744,7 @@ Parser<ParseHandler>::checkFunctionDefinition(HandleAtom funAtom, Node pn, Funct
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!noteDeclaredName(funName, declKind, pos))
|
||||
if (!noteDeclaredName(funName, DeclarationKind::LexicalFunction, pos))
|
||||
return false;
|
||||
} else {
|
||||
if (!noteDeclaredName(funName, DeclarationKind::BodyLevelFunction, pos))
|
||||
|
@ -3528,14 +3527,9 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool* cont)
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the statements in a block, creating a StatementList node that lists
|
||||
* the statements. If called from block-parsing code, the caller must match
|
||||
* '{' before and '}' after.
|
||||
*/
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::statements(YieldHandling yieldHandling)
|
||||
Parser<ParseHandler>::statementList(YieldHandling yieldHandling)
|
||||
{
|
||||
JS_CHECK_RECURSION(context, return null());
|
||||
|
||||
|
@ -3562,7 +3556,7 @@ Parser<ParseHandler>::statements(YieldHandling yieldHandling)
|
|||
return null();
|
||||
statementBegin = pos.begin;
|
||||
}
|
||||
Node next = statement(yieldHandling, canHaveDirectives);
|
||||
Node next = statementListItem(yieldHandling, canHaveDirectives);
|
||||
if (!next) {
|
||||
if (tokenStream.isEOF())
|
||||
isUnexpectedEOF_ = true;
|
||||
|
@ -3957,7 +3951,7 @@ Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned error
|
|||
if (!scope.init(pc))
|
||||
return null();
|
||||
|
||||
Node list = statements(yieldHandling);
|
||||
Node list = statementList(yieldHandling);
|
||||
if (!list)
|
||||
return null();
|
||||
|
||||
|
@ -4864,6 +4858,27 @@ Parser<ParseHandler>::expressionStatement(YieldHandling yieldHandling, InvokedPr
|
|||
return handler.newExprStatement(pnexpr, pos().end);
|
||||
}
|
||||
|
||||
template <class ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::consequentOrAlternative(YieldHandling yieldHandling)
|
||||
{
|
||||
TokenKind next;
|
||||
if (!tokenStream.peekToken(&next, TokenStream::Operand))
|
||||
return null();
|
||||
|
||||
if (next == TOK_FUNCTION) {
|
||||
// Apply Annex B.3.4 in non-strict code to allow FunctionDeclaration as
|
||||
// the consequent/alternative of an |if| or |else|. Parser::statement
|
||||
// will report the strict mode error.
|
||||
if (!pc->sc()->strict()) {
|
||||
tokenStream.consumeKnownToken(next, TokenStream::Operand);
|
||||
return functionStmt(yieldHandling, NameRequired);
|
||||
}
|
||||
}
|
||||
|
||||
return statement(yieldHandling);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::ifStatement(YieldHandling yieldHandling)
|
||||
|
@ -4890,7 +4905,7 @@ Parser<ParseHandler>::ifStatement(YieldHandling yieldHandling)
|
|||
return null();
|
||||
}
|
||||
|
||||
Node thenBranch = statement(yieldHandling);
|
||||
Node thenBranch = consequentOrAlternative(yieldHandling);
|
||||
if (!thenBranch)
|
||||
return null();
|
||||
|
||||
|
@ -4905,7 +4920,7 @@ Parser<ParseHandler>::ifStatement(YieldHandling yieldHandling)
|
|||
return null();
|
||||
if (matched)
|
||||
continue;
|
||||
elseBranch = statement(yieldHandling);
|
||||
elseBranch = consequentOrAlternative(yieldHandling);
|
||||
if (!elseBranch)
|
||||
return null();
|
||||
} else {
|
||||
|
@ -5062,13 +5077,24 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
|
|||
parsingLexicalDeclaration = true;
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
} else if (tt == TOK_NAME && tokenStream.nextName() == context->names().let) {
|
||||
// Check for the backwards-compatibility corner case in sloppy
|
||||
// mode like |for (let in e)| where the 'let' token should be
|
||||
// parsed as an identifier.
|
||||
if (!peekShouldParseLetDeclaration(&parsingLexicalDeclaration, TokenStream::Operand))
|
||||
MOZ_ASSERT(!pc->sc()->strict(),
|
||||
"should parse |let| as TOK_LET in strict mode code");
|
||||
|
||||
// We could have a {For,Lexical}Declaration, or we could have a
|
||||
// LeftHandSideExpression with lookahead restrictions so it's not
|
||||
// ambiguous with the former. Check for a continuation of the former
|
||||
// to decide which we have.
|
||||
tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
|
||||
|
||||
TokenKind next;
|
||||
if (!tokenStream.peekToken(&next))
|
||||
return false;
|
||||
|
||||
letIsIdentifier = !parsingLexicalDeclaration;
|
||||
parsingLexicalDeclaration = nextTokenContinuesLetDeclaration(next, yieldHandling);
|
||||
if (!parsingLexicalDeclaration) {
|
||||
tokenStream.ungetToken();
|
||||
letIsIdentifier = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsingLexicalDeclaration) {
|
||||
|
@ -5386,7 +5412,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
|
|||
return null();
|
||||
statementBegin = pos.begin;
|
||||
}
|
||||
Node stmt = statement(yieldHandling);
|
||||
Node stmt = statementListItem(yieldHandling);
|
||||
if (!stmt)
|
||||
return null();
|
||||
if (!warnedAboutStatementsAfterReturn) {
|
||||
|
@ -5774,6 +5800,41 @@ Parser<SyntaxParseHandler>::withStatement(YieldHandling yieldHandling)
|
|||
return null();
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::labeledItem(YieldHandling yieldHandling)
|
||||
{
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
|
||||
if (tt == TOK_FUNCTION) {
|
||||
TokenKind next;
|
||||
if (!tokenStream.peekToken(&next))
|
||||
return null();
|
||||
|
||||
// GeneratorDeclaration is only matched by HoistableDeclaration in
|
||||
// StatementListItem, so generators can't be inside labels.
|
||||
if (next == TOK_MUL) {
|
||||
report(ParseError, false, null(), JSMSG_GENERATOR_LABEL);
|
||||
return null();
|
||||
}
|
||||
|
||||
// Per 13.13.1 it's a syntax error if LabelledItem: FunctionDeclaration
|
||||
// is ever matched. Per Annex B.3.2 that modifies this text, this
|
||||
// applies only to strict mode code.
|
||||
if (pc->sc()->strict()) {
|
||||
report(ParseError, false, null(), JSMSG_FUNCTION_LABEL);
|
||||
return null();
|
||||
}
|
||||
|
||||
return functionStmt(yieldHandling, NameRequired);
|
||||
}
|
||||
|
||||
tokenStream.ungetToken();
|
||||
return statement(yieldHandling);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling)
|
||||
|
@ -5794,7 +5855,7 @@ Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling)
|
|||
|
||||
/* Push a label struct and parse the statement. */
|
||||
ParseContext::LabelStatement stmt(pc, label);
|
||||
Node pn = statement(yieldHandling);
|
||||
Node pn = labeledItem(yieldHandling);
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
|
@ -5865,7 +5926,7 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
|
|||
if (!scope.init(pc))
|
||||
return null();
|
||||
|
||||
innerBlock = statements(yieldHandling);
|
||||
innerBlock = statementList(yieldHandling);
|
||||
if (!innerBlock)
|
||||
return null();
|
||||
|
||||
|
@ -6005,7 +6066,7 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
|
|||
if (!scope.init(pc))
|
||||
return null();
|
||||
|
||||
finallyBlock = statements(yieldHandling);
|
||||
finallyBlock = statementList(yieldHandling);
|
||||
if (!finallyBlock)
|
||||
return null();
|
||||
|
||||
|
@ -6047,7 +6108,7 @@ Parser<ParseHandler>::catchBlockStatement(YieldHandling yieldHandling,
|
|||
if (!noteDeclaredName(simpleCatchParam, DeclarationKind::SimpleCatchParameter, pos()))
|
||||
return null();
|
||||
|
||||
Node list = statements(yieldHandling);
|
||||
Node list = statementList(yieldHandling);
|
||||
if (!list)
|
||||
return null();
|
||||
|
||||
|
@ -6057,7 +6118,7 @@ Parser<ParseHandler>::catchBlockStatement(YieldHandling yieldHandling,
|
|||
|
||||
body = finishLexicalScope(scope, list);
|
||||
} else {
|
||||
body = statements(yieldHandling);
|
||||
body = statementList(yieldHandling);
|
||||
}
|
||||
if (!body)
|
||||
return null();
|
||||
|
@ -6361,77 +6422,71 @@ Parser<SyntaxParseHandler>::classDefinition(YieldHandling yieldHandling,
|
|||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
template <class ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::shouldParseLetDeclaration(bool* parseDeclOut)
|
||||
Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling)
|
||||
{
|
||||
TokenKind tt;
|
||||
if (!tokenStream.peekToken(&tt))
|
||||
return false;
|
||||
|
||||
if (tt == TOK_NAME) {
|
||||
// |let| followed by a name is a lexical declaration. This is so even
|
||||
// if the name is on a new line. ASI applies *only* if an offending
|
||||
// token not allowed by the grammar is encountered, and there's no
|
||||
// [no LineTerminator here] restriction in LexicalDeclaration or
|
||||
// ForDeclaration forbidding a line break.
|
||||
//
|
||||
// It's a tricky point, but this is true *even if* the name is "let", a
|
||||
// name that can't be bound by LexicalDeclaration or ForDeclaration.
|
||||
// Per ES6 5.3, static semantics early errors are validated *after*
|
||||
// determining productions matching the source text. So in this
|
||||
// example:
|
||||
//
|
||||
// let // ASI opportunity...except not
|
||||
// let;
|
||||
//
|
||||
// the text matches LexicalDeclaration. *Then* static semantics in
|
||||
// ES6 13.3.1.1 (corresponding to the LexicalDeclaration production
|
||||
// just chosen), per ES6 5.3, are validated to recognize the Script as
|
||||
// invalid. It can't be evaluated, so a SyntaxError is thrown.
|
||||
*parseDeclOut = true;
|
||||
} else if (tt == TOK_LB || tt == TOK_LC) {
|
||||
*parseDeclOut = true;
|
||||
} else {
|
||||
// Whatever we have isn't a declaration. Either it's an expression, or
|
||||
// it's invalid: expression-parsing code will decide.
|
||||
*parseDeclOut = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::peekShouldParseLetDeclaration(bool* parseDeclOut,
|
||||
TokenStream::Modifier modifier)
|
||||
{
|
||||
// 'let' is a reserved keyword in strict mode and we shouldn't get here.
|
||||
MOZ_ASSERT(!pc->sc()->strict());
|
||||
|
||||
*parseDeclOut = false;
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME),
|
||||
"TOK_LET should have been summarily considered a "
|
||||
"LexicalDeclaration");
|
||||
MOZ_ASSERT(tokenStream.currentName() == context->names().let);
|
||||
|
||||
#ifdef DEBUG
|
||||
TokenKind tt;
|
||||
if (!tokenStream.peekToken(&tt, modifier))
|
||||
return false;
|
||||
MOZ_ASSERT(tt == TOK_NAME && tokenStream.nextName() == context->names().let);
|
||||
TokenKind verify;
|
||||
MOZ_ALWAYS_TRUE(tokenStream.peekToken(&verify));
|
||||
MOZ_ASSERT(next == verify);
|
||||
#endif
|
||||
|
||||
tokenStream.consumeKnownToken(TOK_NAME, modifier);
|
||||
if (!shouldParseLetDeclaration(parseDeclOut))
|
||||
// Destructuring is (for once) the easy case.
|
||||
if (next == TOK_LB || next == TOK_LC)
|
||||
return true;
|
||||
|
||||
// Otherwise a let declaration must have a name.
|
||||
if (next == TOK_NAME) {
|
||||
// One non-"yield" TOK_NAME edge case deserves special comment.
|
||||
// Consider this:
|
||||
//
|
||||
// let // not an ASI opportunity
|
||||
// let;
|
||||
//
|
||||
// Static semantics in §13.3.1.1 turn a LexicalDeclaration that binds
|
||||
// "let" into an early error. Does this retroactively permit ASI so
|
||||
// that we should parse this as two ExpressionStatements? No. ASI
|
||||
// resolves during parsing. Static semantics only apply to the full
|
||||
// parse tree with ASI applied. No backsies!
|
||||
if (tokenStream.nextName() != context->names().yield)
|
||||
return true;
|
||||
} else if (next != TOK_YIELD) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unget the TOK_NAME of 'let' if not parsing a declaration.
|
||||
if (!*parseDeclOut)
|
||||
tokenStream.ungetToken();
|
||||
|
||||
return true;
|
||||
// We have the name "yield": the grammar parameter exactly states whether
|
||||
// this is okay. Even if YieldIsKeyword, the code might be valid if ASI
|
||||
// induces a preceding semicolon. If YieldIsName, the code is valid
|
||||
// outside strict mode, and declaration-parsing code will enforce strict
|
||||
// mode restrictions.
|
||||
//
|
||||
// No checkYieldNameValidity for TOK_YIELD is needed here. It'll happen
|
||||
// when TOK_YIELD is consumed as BindingIdentifier or as start of a fresh
|
||||
// Statement.
|
||||
return yieldHandling == YieldIsName;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirectives)
|
||||
Parser<ParseHandler>::variableStatement(YieldHandling yieldHandling)
|
||||
{
|
||||
Node vars = declarationList(yieldHandling, PNK_VAR);
|
||||
if (!vars)
|
||||
return null();
|
||||
if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
|
||||
return null();
|
||||
return vars;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::statement(YieldHandling yieldHandling)
|
||||
{
|
||||
MOZ_ASSERT(checkOptionsCalled);
|
||||
|
||||
|
@ -6447,31 +6502,14 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirecti
|
|||
return blockStatement(yieldHandling);
|
||||
|
||||
// VariableStatement[?Yield]
|
||||
case TOK_VAR: {
|
||||
Node pn = declarationList(yieldHandling, PNK_VAR);
|
||||
if (!pn)
|
||||
return null();
|
||||
if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
|
||||
return null();
|
||||
return pn;
|
||||
}
|
||||
case TOK_VAR:
|
||||
return variableStatement(yieldHandling);
|
||||
|
||||
// EmptyStatement
|
||||
case TOK_SEMI:
|
||||
return handler.newEmptyStatement(pos());
|
||||
|
||||
// ExpressionStatement[?Yield].
|
||||
//
|
||||
// These should probably be handled by a single ExpressionStatement
|
||||
// function in a default, not split up this way.
|
||||
case TOK_STRING:
|
||||
if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
|
||||
return null();
|
||||
}
|
||||
return expressionStatement(yieldHandling);
|
||||
|
||||
case TOK_YIELD: {
|
||||
// Don't use a ternary operator here due to obscure linker issues
|
||||
|
@ -6494,29 +6532,58 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirecti
|
|||
}
|
||||
|
||||
case TOK_NAME: {
|
||||
// 'let' is a contextual keyword outside strict mode. In strict mode
|
||||
// it's always tokenized as TOK_LET except in this one weird case:
|
||||
//
|
||||
// "use strict" // ExpressionStatement, terminated by ASI
|
||||
// let a = 1; // LexicalDeclaration
|
||||
//
|
||||
// We can't apply strict mode until we know "use strict" is the entire
|
||||
// statement, but we can't know "use strict" is the entire statement
|
||||
// until we see the next token. So 'let' is still TOK_NAME here.
|
||||
if (tokenStream.currentName() == context->names().let) {
|
||||
bool parseDecl;
|
||||
if (!shouldParseLetDeclaration(&parseDecl))
|
||||
return null();
|
||||
|
||||
if (parseDecl)
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ false);
|
||||
}
|
||||
|
||||
TokenKind next;
|
||||
if (!tokenStream.peekToken(&next))
|
||||
return null();
|
||||
|
||||
#ifdef DEBUG
|
||||
if (tokenStream.currentName() == context->names().let) {
|
||||
MOZ_ASSERT(!pc->sc()->strict(),
|
||||
"observing |let| as TOK_NAME and not TOK_LET implies "
|
||||
"non-strict code (and the edge case of 'use strict' "
|
||||
"immediately followed by |let| on a new line only "
|
||||
"applies to StatementListItems, not to Statements)");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Statement context forbids LexicalDeclaration.
|
||||
if ((next == TOK_LB || next == TOK_LC || next == TOK_NAME) &&
|
||||
tokenStream.currentName() == context->names().let)
|
||||
{
|
||||
bool forbiddenLetDeclaration = false;
|
||||
if (next == TOK_LB) {
|
||||
// ExpressionStatement has a 'let [' lookahead restriction.
|
||||
forbiddenLetDeclaration = true;
|
||||
} else {
|
||||
// 'let {' and 'let foo' aren't completely forbidden, if ASI
|
||||
// causes 'let' to be the entire Statement. But if they're
|
||||
// same-line, we can aggressively give a better error message.
|
||||
//
|
||||
// Note that this ignores 'yield' as TOK_YIELD: we'll handle it
|
||||
// correctly but with a worse error message.
|
||||
TokenKind nextSameLine;
|
||||
if (!tokenStream.peekTokenSameLine(&nextSameLine))
|
||||
return null();
|
||||
|
||||
MOZ_ASSERT(nextSameLine == TOK_NAME ||
|
||||
nextSameLine == TOK_LC ||
|
||||
nextSameLine == TOK_EOL);
|
||||
|
||||
forbiddenLetDeclaration = nextSameLine != TOK_EOL;
|
||||
}
|
||||
|
||||
if (forbiddenLetDeclaration) {
|
||||
report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT,
|
||||
"lexical declarations");
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: It's unfortunately allowed to have a label named 'let' in
|
||||
// non-strict code. 💯
|
||||
if (next == TOK_COLON)
|
||||
return labeledStatement(yieldHandling);
|
||||
|
||||
return expressionStatement(yieldHandling);
|
||||
}
|
||||
|
||||
|
@ -6585,23 +6652,222 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirecti
|
|||
case TOK_DEBUGGER:
|
||||
return debuggerStatement();
|
||||
|
||||
// HoistableDeclaration[?Yield]
|
||||
// |function| is forbidden by lookahead restriction (unless as child
|
||||
// statement of |if| or |else|, but Parser::consequentOrAlternative
|
||||
// handles that).
|
||||
case TOK_FUNCTION:
|
||||
report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations");
|
||||
return null();
|
||||
|
||||
// |class| is also forbidden by lookahead restriction.
|
||||
case TOK_CLASS:
|
||||
report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "classes");
|
||||
return null();
|
||||
|
||||
// ImportDeclaration (only inside modules)
|
||||
case TOK_IMPORT:
|
||||
return importDeclaration();
|
||||
|
||||
// ExportDeclaration (only inside modules)
|
||||
case TOK_EXPORT:
|
||||
return exportDeclaration();
|
||||
|
||||
// Miscellaneous error cases arguably better caught here than elsewhere.
|
||||
|
||||
case TOK_CATCH:
|
||||
report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
|
||||
return null();
|
||||
|
||||
case TOK_FINALLY:
|
||||
report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
|
||||
return null();
|
||||
|
||||
// TOK_LET implies we're in strict mode code where static semantics
|
||||
// forbid IdentifierName to be "let": a stronger restriction than
|
||||
// Statement's lookahead restriction on |let [|. Provide a better error
|
||||
// message here than the default case would.
|
||||
case TOK_LET:
|
||||
report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "let declarations");
|
||||
return null();
|
||||
|
||||
// NOTE: default case handled in the ExpressionStatement section.
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
|
||||
bool canHaveDirectives /* = false */)
|
||||
{
|
||||
MOZ_ASSERT(checkOptionsCalled);
|
||||
|
||||
JS_CHECK_RECURSION(context, return null());
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
|
||||
switch (tt) {
|
||||
// BlockStatement[?Yield, ?Return]
|
||||
case TOK_LC:
|
||||
return blockStatement(yieldHandling);
|
||||
|
||||
// VariableStatement[?Yield]
|
||||
case TOK_VAR:
|
||||
return variableStatement(yieldHandling);
|
||||
|
||||
// EmptyStatement
|
||||
case TOK_SEMI:
|
||||
return handler.newEmptyStatement(pos());
|
||||
|
||||
// ExpressionStatement[?Yield].
|
||||
//
|
||||
// These should probably be handled by a single ExpressionStatement
|
||||
// function in a default, not split up this way.
|
||||
case TOK_STRING:
|
||||
if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
|
||||
return null();
|
||||
}
|
||||
return expressionStatement(yieldHandling);
|
||||
|
||||
case TOK_YIELD: {
|
||||
// Don't use a ternary operator here due to obscure linker issues
|
||||
// around using static consts in the arms of a ternary.
|
||||
TokenStream::Modifier modifier;
|
||||
if (yieldExpressionsSupported())
|
||||
modifier = TokenStream::Operand;
|
||||
else
|
||||
modifier = TokenStream::None;
|
||||
|
||||
TokenKind next;
|
||||
if (!tokenStream.peekToken(&next, modifier))
|
||||
return null();
|
||||
if (next == TOK_COLON) {
|
||||
if (!checkYieldNameValidity())
|
||||
return null();
|
||||
return labeledStatement(yieldHandling);
|
||||
}
|
||||
return expressionStatement(yieldHandling);
|
||||
}
|
||||
|
||||
case TOK_NAME: {
|
||||
TokenKind next;
|
||||
if (!tokenStream.peekToken(&next))
|
||||
return null();
|
||||
|
||||
if (tokenStream.currentName() == context->names().let) {
|
||||
if (nextTokenContinuesLetDeclaration(next, yieldHandling))
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ false);
|
||||
|
||||
// IdentifierName can't be "let" in strict mode code. |let| in
|
||||
// strict mode code is usually TOK_LET, but in this one weird case
|
||||
// in global code it's TOK_NAME:
|
||||
//
|
||||
// "use strict" // ExpressionStatement ended by ASI
|
||||
// let <...whatever else...> // a fresh StatementListItem
|
||||
//
|
||||
// Carefully reject strict mode |let| non-declarations.
|
||||
if (pc->sc()->strict()) {
|
||||
report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
|
||||
"declaration pattern", TokenKindToDesc(next));
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
if (next == TOK_COLON)
|
||||
return labeledStatement(yieldHandling);
|
||||
|
||||
return expressionStatement(yieldHandling);
|
||||
}
|
||||
|
||||
case TOK_NEW:
|
||||
return expressionStatement(yieldHandling, PredictInvoked);
|
||||
|
||||
default:
|
||||
return expressionStatement(yieldHandling);
|
||||
|
||||
// IfStatement[?Yield, ?Return]
|
||||
case TOK_IF:
|
||||
return ifStatement(yieldHandling);
|
||||
|
||||
// BreakableStatement[?Yield, ?Return]
|
||||
//
|
||||
// BreakableStatement[Yield, Return]:
|
||||
// IterationStatement[?Yield, ?Return]
|
||||
// SwitchStatement[?Yield, ?Return]
|
||||
case TOK_DO:
|
||||
return doWhileStatement(yieldHandling);
|
||||
|
||||
case TOK_WHILE:
|
||||
return whileStatement(yieldHandling);
|
||||
|
||||
case TOK_FOR:
|
||||
return forStatement(yieldHandling);
|
||||
|
||||
case TOK_SWITCH:
|
||||
return switchStatement(yieldHandling);
|
||||
|
||||
// ContinueStatement[?Yield]
|
||||
case TOK_CONTINUE:
|
||||
return continueStatement(yieldHandling);
|
||||
|
||||
// BreakStatement[?Yield]
|
||||
case TOK_BREAK:
|
||||
return breakStatement(yieldHandling);
|
||||
|
||||
// [+Return] ReturnStatement[?Yield]
|
||||
case TOK_RETURN:
|
||||
// The Return parameter is only used here, and the effect is easily
|
||||
// detected this way, so don't bother passing around an extra parameter
|
||||
// everywhere.
|
||||
if (!pc->isFunctionBox()) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
|
||||
return null();
|
||||
}
|
||||
return returnStatement(yieldHandling);
|
||||
|
||||
// WithStatement[?Yield, ?Return]
|
||||
case TOK_WITH:
|
||||
return withStatement(yieldHandling);
|
||||
|
||||
// LabelledStatement[?Yield, ?Return]
|
||||
// This is really handled by TOK_NAME and TOK_YIELD cases above.
|
||||
|
||||
// ThrowStatement[?Yield]
|
||||
case TOK_THROW:
|
||||
return throwStatement(yieldHandling);
|
||||
|
||||
// TryStatement[?Yield, ?Return]
|
||||
case TOK_TRY:
|
||||
return tryStatement(yieldHandling);
|
||||
|
||||
// DebuggerStatement
|
||||
case TOK_DEBUGGER:
|
||||
return debuggerStatement();
|
||||
|
||||
// Declaration[Yield]:
|
||||
|
||||
// HoistableDeclaration[?Yield, ~Default]
|
||||
case TOK_FUNCTION:
|
||||
return functionStmt(yieldHandling, NameRequired);
|
||||
|
||||
// ClassDeclaration[?Yield]
|
||||
// ClassDeclaration[?Yield, ~Default]
|
||||
case TOK_CLASS:
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
return classDefinition(yieldHandling, ClassStatement, NameRequired);
|
||||
|
||||
// LexicalDeclaration[In, ?Yield]
|
||||
// LexicalDeclaration[In, ?Yield]
|
||||
// LetOrConst BindingList[?In, ?Yield]
|
||||
case TOK_LET:
|
||||
case TOK_CONST:
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
// [In] is the default behavior, because for-loops currently specially
|
||||
// parse their heads to handle |in| in this situation.
|
||||
// [In] is the default behavior, because for-loops specially parse
|
||||
// their heads to handle |in| in this situation.
|
||||
return lexicalDeclaration(yieldHandling, /* isConst = */ tt == TOK_CONST);
|
||||
|
||||
// ImportDeclaration (only inside modules)
|
||||
|
|
|
@ -938,7 +938,8 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
|
|||
|
||||
public:
|
||||
/* Public entry points for parsing. */
|
||||
Node statement(YieldHandling yieldHandling, bool canHaveDirectives = false);
|
||||
Node statement(YieldHandling yieldHandling);
|
||||
Node statementListItem(YieldHandling yieldHandling, bool canHaveDirectives = false);
|
||||
|
||||
bool maybeParseDirective(Node list, Node pn, bool* cont);
|
||||
|
||||
|
@ -1017,11 +1018,11 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
|
|||
*/
|
||||
Node functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling);
|
||||
Node functionExpr(InvokedPrediction invoked = PredictUninvoked);
|
||||
Node statements(YieldHandling yieldHandling);
|
||||
|
||||
Node statementList(YieldHandling yieldHandling);
|
||||
|
||||
Node blockStatement(YieldHandling yieldHandling,
|
||||
unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
|
||||
Node ifStatement(YieldHandling yieldHandling);
|
||||
Node doWhileStatement(YieldHandling yieldHandling);
|
||||
Node whileStatement(YieldHandling yieldHandling);
|
||||
|
||||
|
@ -1039,13 +1040,26 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
|
|||
Node breakStatement(YieldHandling yieldHandling);
|
||||
Node returnStatement(YieldHandling yieldHandling);
|
||||
Node withStatement(YieldHandling yieldHandling);
|
||||
Node labeledStatement(YieldHandling yieldHandling);
|
||||
Node throwStatement(YieldHandling yieldHandling);
|
||||
Node tryStatement(YieldHandling yieldHandling);
|
||||
Node catchBlockStatement(YieldHandling yieldHandling, HandlePropertyName simpleCatchParam);
|
||||
Node debuggerStatement();
|
||||
|
||||
Node variableStatement(YieldHandling yieldHandling);
|
||||
|
||||
Node labeledStatement(YieldHandling yieldHandling);
|
||||
Node labeledItem(YieldHandling yieldHandling);
|
||||
|
||||
Node ifStatement(YieldHandling yieldHandling);
|
||||
Node consequentOrAlternative(YieldHandling yieldHandling);
|
||||
|
||||
// While on a |let| TOK_NAME token, examine |next|. Indicate whether
|
||||
// |next|, the next token already gotten with modifier TokenStream::None,
|
||||
// continues a LexicalDeclaration.
|
||||
bool nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling);
|
||||
|
||||
Node lexicalDeclaration(YieldHandling yieldHandling, bool isConst);
|
||||
|
||||
Node importDeclaration();
|
||||
Node exportDeclaration();
|
||||
Node expressionStatement(YieldHandling yieldHandling,
|
||||
|
@ -1239,15 +1253,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
|
|||
bool finishFunction();
|
||||
bool leaveInnerFunction(ParseContext* outerpc);
|
||||
|
||||
// Use when the current token is TOK_NAME and is known to be 'let'.
|
||||
bool shouldParseLetDeclaration(bool* parseDeclOut);
|
||||
|
||||
// Use when the lookahead token is TOK_NAME and is known to be 'let'. If a
|
||||
// let declaration should be parsed, the TOK_NAME token of 'let' is
|
||||
// consumed. Otherwise, the current token remains the TOK_NAME token of
|
||||
// 'let'.
|
||||
bool peekShouldParseLetDeclaration(bool* parseDeclOut, TokenStream::Modifier modifier);
|
||||
|
||||
public:
|
||||
enum FunctionCallBehavior {
|
||||
PermitAssignmentToFunctionCalls,
|
||||
|
|
|
@ -2,6 +2,8 @@ setJitCompilerOption("baseline.warmup.trigger", 10);
|
|||
setJitCompilerOption("ion.warmup.trigger", 20);
|
||||
var i;
|
||||
|
||||
var config = getBuildConfiguration();
|
||||
|
||||
// Check that we are able to remove the operation inside recover test functions (denoted by "rop..."),
|
||||
// when we inline the first version of uceFault, and ensure that the bailout is correct
|
||||
// when uceFault is replaced (which cause an invalidation bailout)
|
||||
|
@ -1286,6 +1288,29 @@ function rhypot_object_4args(i) {
|
|||
return i;
|
||||
}
|
||||
|
||||
var uceFault_random = eval(uneval(uceFault).replace('uceFault', 'uceFault_random'));
|
||||
function rrandom(i) {
|
||||
// setRNGState() exists only in debug builds
|
||||
|
||||
if(config.debug) {
|
||||
setRNGState(2, 0);
|
||||
var x = Math.random();
|
||||
if (uceFault_random(i) || uceFault_random(i)) {
|
||||
setRNGState(2, 0);
|
||||
assertEq(x, Math.random());
|
||||
}
|
||||
assertRecoveredOnBailout(x, true);
|
||||
} else {
|
||||
var x = Math.random();
|
||||
if (uceFault_random(i) || uceFault_random(i)) {
|
||||
Math.random();
|
||||
}
|
||||
assertRecoveredOnBailout(x, true);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
var uceFault_sin_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_sin_number'));
|
||||
function rsin_number(i) {
|
||||
var x = Math.sin(i);
|
||||
|
@ -1444,6 +1469,7 @@ for (i = 0; i < 100; i++) {
|
|||
rhypot_object_2args(i);
|
||||
rhypot_object_3args(i);
|
||||
rhypot_object_4args(i);
|
||||
rrandom(i);
|
||||
rsin_number(i);
|
||||
rsin_object(i);
|
||||
rlog_number(i);
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
setJitCompilerOption("ion.warmup.trigger", 40);
|
||||
|
||||
const constructors = [
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array ];
|
||||
|
||||
// Ensure that when creating TypedArrays under JIT
|
||||
// the sort() method works as expected (bug 1295034).
|
||||
for (var ctor of constructors) {
|
||||
for (var _ of Array(1024)) {
|
||||
var testArray = new ctor(10);
|
||||
testArray.sort();
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
|
@ -11520,10 +11520,10 @@ CodeGenerator::visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir)
|
|||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitAsmThrowUnreachable(LAsmThrowUnreachable* lir)
|
||||
CodeGenerator::visitWasmTrap(LWasmTrap* lir)
|
||||
{
|
||||
MOZ_ASSERT(gen->compilingAsmJS());
|
||||
masm.jump(wasm::JumpTarget::Unreachable);
|
||||
masm.jump(wasm::JumpTarget(lir->mir()->trap()));
|
||||
}
|
||||
|
||||
typedef bool (*RecompileFn)(JSContext*);
|
||||
|
|
|
@ -415,7 +415,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
|||
void visitInterruptCheck(LInterruptCheck* lir);
|
||||
void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit* ins);
|
||||
void visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir);
|
||||
void visitAsmThrowUnreachable(LAsmThrowUnreachable* lir);
|
||||
void visitWasmTrap(LWasmTrap* lir);
|
||||
void visitRecompileCheck(LRecompileCheck* ins);
|
||||
void visitRotate(LRotate* ins);
|
||||
|
||||
|
|
|
@ -2530,9 +2530,9 @@ LIRGenerator::visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins)
|
|||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitAsmThrowUnreachable(MAsmThrowUnreachable* ins)
|
||||
LIRGenerator::visitWasmTrap(MWasmTrap* ins)
|
||||
{
|
||||
add(new(alloc()) LAsmThrowUnreachable, ins);
|
||||
add(new(alloc()) LWasmTrap, ins);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -191,7 +191,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitFunctionEnvironment(MFunctionEnvironment* ins);
|
||||
void visitInterruptCheck(MInterruptCheck* ins);
|
||||
void visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins);
|
||||
void visitAsmThrowUnreachable(MAsmThrowUnreachable* ins);
|
||||
void visitWasmTrap(MWasmTrap* ins);
|
||||
void visitAsmReinterpret(MAsmReinterpret* ins);
|
||||
void visitStoreSlot(MStoreSlot* ins);
|
||||
void visitFilterTypeSet(MFilterTypeSet* ins);
|
||||
|
|
|
@ -6584,6 +6584,16 @@ class MRandom : public MNullaryInstruction
|
|||
|
||||
void computeRange(TempAllocator& alloc) override;
|
||||
|
||||
MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
|
||||
|
||||
bool canRecoverOnBailout() const override {
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MRandom)
|
||||
};
|
||||
|
||||
|
@ -7738,18 +7748,28 @@ class MAsmJSInterruptCheck
|
|||
TRIVIAL_NEW_WRAPPERS
|
||||
};
|
||||
|
||||
// Directly jumps to the unreachable trap handler.
|
||||
class MAsmThrowUnreachable
|
||||
// Directly jumps to the indicated trap, leaving Wasm code and reporting a
|
||||
// runtime error.
|
||||
|
||||
class MWasmTrap
|
||||
: public MAryControlInstruction<0, 0>,
|
||||
public NoTypePolicy::Data
|
||||
{
|
||||
wasm::Trap trap_;
|
||||
|
||||
explicit MWasmTrap(wasm::Trap trap)
|
||||
: trap_(trap)
|
||||
{}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(AsmThrowUnreachable)
|
||||
INSTRUCTION_HEADER(WasmTrap)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
wasm::Trap trap() const { return trap_; }
|
||||
};
|
||||
|
||||
// Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving
|
||||
|
|
|
@ -262,7 +262,6 @@ namespace jit {
|
|||
_(CallInstanceOf) \
|
||||
_(InterruptCheck) \
|
||||
_(AsmJSInterruptCheck) \
|
||||
_(AsmThrowUnreachable) \
|
||||
_(GetDOMProperty) \
|
||||
_(GetDOMMember) \
|
||||
_(SetDOMProperty) \
|
||||
|
@ -274,6 +273,7 @@ namespace jit {
|
|||
_(WasmBoundsCheck) \
|
||||
_(WasmLoad) \
|
||||
_(WasmStore) \
|
||||
_(WasmTrap) \
|
||||
_(WasmTruncateToInt32) \
|
||||
_(AsmJSNeg) \
|
||||
_(AsmJSUnsignedToDouble) \
|
||||
|
|
|
@ -1005,6 +1005,24 @@ RMathFunction::recover(JSContext* cx, SnapshotIterator& iter) const
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MRandom::writeRecoverData(CompactBufferWriter& writer) const
|
||||
{
|
||||
MOZ_ASSERT(this->canRecoverOnBailout());
|
||||
writer.writeUnsigned(uint32_t(RInstruction::Recover_Random));
|
||||
return true;
|
||||
}
|
||||
|
||||
RRandom::RRandom(CompactBufferReader& reader)
|
||||
{}
|
||||
|
||||
bool
|
||||
RRandom::recover(JSContext* cx, SnapshotIterator& iter) const
|
||||
{
|
||||
iter.storeInstructionResult(DoubleValue(math_random_impl(cx)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MStringSplit::writeRecoverData(CompactBufferWriter& writer) const
|
||||
{
|
||||
|
|
|
@ -88,6 +88,7 @@ namespace jit {
|
|||
_(Atan2) \
|
||||
_(Hypot) \
|
||||
_(MathFunction) \
|
||||
_(Random) \
|
||||
_(StringSplit) \
|
||||
_(RegExpMatcher) \
|
||||
_(RegExpSearcher) \
|
||||
|
@ -462,6 +463,13 @@ class RMathFunction final : public RInstruction
|
|||
MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const;
|
||||
};
|
||||
|
||||
class RRandom final : public RInstruction
|
||||
{
|
||||
RINSTRUCTION_HEADER_NUM_OP_(Random, 0)
|
||||
public:
|
||||
MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const;
|
||||
};
|
||||
|
||||
class RStringSplit final : public RInstruction
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -2291,11 +2291,7 @@ CodeGeneratorARM::visitWasmBoundsCheck(LWasmBoundsCheck* ins)
|
|||
{
|
||||
MWasmBoundsCheck* mir = ins->mir();
|
||||
|
||||
uint32_t offset = mir->offset();
|
||||
if (offset > INT32_MAX) {
|
||||
masm.as_b(wasm::JumpTarget::OutOfBounds);
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(mir->offset() <= INT32_MAX);
|
||||
|
||||
if (!mir->isRedundant()) {
|
||||
// No guarantee that heapBase + endOffset can be properly encoded in
|
||||
|
@ -2348,11 +2344,7 @@ CodeGeneratorARM::emitWasmLoad(T* lir)
|
|||
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
|
||||
|
||||
uint32_t offset = mir->offset();
|
||||
if (offset > INT32_MAX) {
|
||||
// This is unreachable because of bounds checks.
|
||||
masm.breakpoint();
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(offset <= INT32_MAX);
|
||||
|
||||
Register ptr = ToRegister(lir->ptr());
|
||||
Scalar::Type type = mir->accessType();
|
||||
|
@ -2419,11 +2411,7 @@ CodeGeneratorARM::emitWasmUnalignedLoad(T* lir)
|
|||
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
|
||||
|
||||
uint32_t offset = mir->offset();
|
||||
if (offset > INT32_MAX) {
|
||||
// This is unreachable because of bounds checks.
|
||||
masm.breakpoint();
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(offset <= INT32_MAX);
|
||||
|
||||
Register ptr = ToRegister(lir->ptrCopy());
|
||||
if (offset)
|
||||
|
@ -2503,11 +2491,7 @@ CodeGeneratorARM::emitWasmStore(T* lir)
|
|||
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
|
||||
|
||||
uint32_t offset = mir->offset();
|
||||
if (offset > INT32_MAX) {
|
||||
// This is unreachable because of bounds checks.
|
||||
masm.breakpoint();
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(offset <= INT32_MAX);
|
||||
|
||||
Register ptr = ToRegister(lir->ptr());
|
||||
unsigned byteSize = mir->byteSize();
|
||||
|
|
|
@ -1666,10 +1666,7 @@ CodeGeneratorMIPSShared::visitWasmBoundsCheck(LWasmBoundsCheck* ins)
|
|||
MWasmBoundsCheck* mir = ins->mir();
|
||||
|
||||
uint32_t offset = mir->offset();
|
||||
if (offset > INT32_MAX) {
|
||||
masm.jump(wasm::JumpTarget::OutOfBounds);
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(offset <= INT32_MAX);
|
||||
|
||||
uint32_t endOffset = mir->endOffset();
|
||||
Register ptr = ToRegister(ins->ptr());
|
||||
|
@ -1693,11 +1690,7 @@ CodeGeneratorMIPSShared::visitWasmLoad(LWasmLoad* lir)
|
|||
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
|
||||
|
||||
uint32_t offset = mir->offset();
|
||||
if (offset > INT32_MAX) {
|
||||
// This is unreachable because of bounds checks.
|
||||
masm.breakpoint();
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(offset <= INT32_MAX);
|
||||
|
||||
Register ptr = ToRegister(lir->ptr());
|
||||
|
||||
|
@ -1745,11 +1738,7 @@ CodeGeneratorMIPSShared::visitWasmStore(LWasmStore* lir)
|
|||
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
|
||||
|
||||
uint32_t offset = mir->offset();
|
||||
if (offset > INT32_MAX) {
|
||||
// This is unreachable because of bounds checks.
|
||||
masm.breakpoint();
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(offset <= INT32_MAX);
|
||||
|
||||
Register ptr = ToRegister(lir->ptr());
|
||||
|
||||
|
|
|
@ -1067,26 +1067,14 @@ MacroAssemblerMIPSCompat::storePtr(Register src, AbsoluteAddress dest)
|
|||
void
|
||||
MacroAssemblerMIPSCompat::clampIntToUint8(Register reg)
|
||||
{
|
||||
// look at (reg >> 8) if it is 0, then src shouldn't be clamped
|
||||
// if it is <0, then we want to clamp to 0,
|
||||
// otherwise, we wish to clamp to 255
|
||||
Label done;
|
||||
ma_move(ScratchRegister, reg);
|
||||
asMasm().rshiftPtrArithmetic(Imm32(8), ScratchRegister);
|
||||
ma_b(ScratchRegister, ScratchRegister, &done, Assembler::Zero, ShortJump);
|
||||
{
|
||||
Label negative;
|
||||
ma_b(ScratchRegister, ScratchRegister, &negative, Assembler::Signed, ShortJump);
|
||||
{
|
||||
ma_li(reg, Imm32(255));
|
||||
ma_b(&done, ShortJump);
|
||||
}
|
||||
bind(&negative);
|
||||
{
|
||||
ma_move(reg, zero);
|
||||
}
|
||||
}
|
||||
bind(&done);
|
||||
// If reg is < 0, then we want to clamp to 0.
|
||||
as_slti(ScratchRegister, reg, 0);
|
||||
as_movn(reg, zero, ScratchRegister);
|
||||
|
||||
// If reg is >= 255, then we want to clamp to 255.
|
||||
ma_li(SecondScratchReg, Imm32(255));
|
||||
as_slti(ScratchRegister, reg, 255);
|
||||
as_movz(reg, SecondScratchReg, ScratchRegister);
|
||||
}
|
||||
|
||||
// Note: this function clobbers the input register.
|
||||
|
|
|
@ -1214,26 +1214,14 @@ MacroAssemblerMIPS64Compat::storePtr(Register src, AbsoluteAddress dest)
|
|||
void
|
||||
MacroAssemblerMIPS64Compat::clampIntToUint8(Register reg)
|
||||
{
|
||||
// look at (reg >> 8) if it is 0, then src shouldn't be clamped
|
||||
// if it is <0, then we want to clamp to 0,
|
||||
// otherwise, we wish to clamp to 255
|
||||
Label done;
|
||||
ma_move(ScratchRegister, reg);
|
||||
asMasm().rshiftPtrArithmetic(Imm32(8), ScratchRegister);
|
||||
ma_b(ScratchRegister, ScratchRegister, &done, Assembler::Zero, ShortJump);
|
||||
{
|
||||
Label negative;
|
||||
ma_b(ScratchRegister, ScratchRegister, &negative, Assembler::Signed, ShortJump);
|
||||
{
|
||||
ma_li(reg, Imm32(255));
|
||||
ma_b(&done, ShortJump);
|
||||
}
|
||||
bind(&negative);
|
||||
{
|
||||
ma_move(reg, zero);
|
||||
}
|
||||
}
|
||||
bind(&done);
|
||||
// If reg is < 0, then we want to clamp to 0.
|
||||
as_slti(ScratchRegister, reg, 0);
|
||||
as_movn(reg, zero, ScratchRegister);
|
||||
|
||||
// If reg is >= 255, then we want to clamp to 255.
|
||||
ma_li(SecondScratchReg, Imm32(255));
|
||||
as_slti(ScratchRegister, reg, 255);
|
||||
as_movz(reg, SecondScratchReg, ScratchRegister);
|
||||
}
|
||||
|
||||
// Note: this function clobbers the input register.
|
||||
|
@ -1330,15 +1318,13 @@ MacroAssemblerMIPS64Compat::unboxNonDouble(const BaseIndex& src, Register dest)
|
|||
void
|
||||
MacroAssemblerMIPS64Compat::unboxInt32(const ValueOperand& operand, Register dest)
|
||||
{
|
||||
ma_dsll(dest, operand.valueReg(), Imm32(32));
|
||||
ma_dsra(dest, dest, Imm32(32));
|
||||
ma_sll(dest, operand.valueReg(), Imm32(0));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS64Compat::unboxInt32(Register src, Register dest)
|
||||
{
|
||||
ma_dsll(dest, src, Imm32(32));
|
||||
ma_dsra(dest, dest, Imm32(32));
|
||||
ma_sll(dest, src, Imm32(0));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1404,13 +1404,17 @@ class LAsmJSInterruptCheck : public LInstructionHelper<0, 0, 0>
|
|||
}
|
||||
};
|
||||
|
||||
class LAsmThrowUnreachable : public LInstructionHelper<0, 0, 0>
|
||||
class LWasmTrap : public LInstructionHelper<0, 0, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(AsmThrowUnreachable);
|
||||
LIR_HEADER(WasmTrap);
|
||||
|
||||
LAsmThrowUnreachable()
|
||||
LWasmTrap()
|
||||
{ }
|
||||
|
||||
const MWasmTrap* mir() const {
|
||||
return mir_->toWasmTrap();
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t Defs, size_t Ops>
|
||||
|
|
|
@ -369,7 +369,7 @@
|
|||
_(CallInstanceOf) \
|
||||
_(InterruptCheck) \
|
||||
_(AsmJSInterruptCheck) \
|
||||
_(AsmThrowUnreachable) \
|
||||
_(WasmTrap) \
|
||||
_(AsmReinterpret) \
|
||||
_(AsmReinterpretToI64) \
|
||||
_(AsmReinterpretFromI64) \
|
||||
|
|
|
@ -538,11 +538,7 @@ CodeGeneratorX64::emitWasmLoad(T* ins)
|
|||
Scalar::Type accessType = mir->accessType();
|
||||
MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD NYI");
|
||||
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
|
||||
|
||||
if (mir->offset() > INT32_MAX) {
|
||||
masm.jump(wasm::JumpTarget::OutOfBounds);
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(mir->offset() <= INT32_MAX);
|
||||
|
||||
const LAllocation* ptr = ins->ptr();
|
||||
Operand srcAddr = ptr->isBogus()
|
||||
|
@ -583,11 +579,7 @@ CodeGeneratorX64::emitWasmStore(T* ins)
|
|||
Scalar::Type accessType = mir->accessType();
|
||||
MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD NYI");
|
||||
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
|
||||
|
||||
if (mir->offset() > INT32_MAX) {
|
||||
masm.jump(wasm::JumpTarget::OutOfBounds);
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(mir->offset() <= INT32_MAX);
|
||||
|
||||
const LAllocation* value = ins->getOperand(ins->ValueIndex);
|
||||
const LAllocation* ptr = ins->ptr();
|
||||
|
|
|
@ -494,11 +494,9 @@ void
|
|||
CodeGeneratorX86Shared::visitWasmBoundsCheck(LWasmBoundsCheck* ins)
|
||||
{
|
||||
const MWasmBoundsCheck* mir = ins->mir();
|
||||
|
||||
MOZ_ASSERT(gen->needsBoundsCheckBranch(mir));
|
||||
if (mir->offset() > INT32_MAX) {
|
||||
masm.jump(wasm::JumpTarget::OutOfBounds);
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(mir->offset() <= INT32_MAX);
|
||||
|
||||
Register ptrReg = ToRegister(ins->ptr());
|
||||
maybeEmitWasmBoundsCheckBranch(mir, ptrReg, mir->isRedundant());
|
||||
|
@ -548,9 +546,8 @@ CodeGeneratorX86Shared::maybeEmitWasmBoundsCheckBranch(const MWasmMemoryAccess*
|
|||
|
||||
MOZ_ASSERT(mir->endOffset() >= 1,
|
||||
"need to subtract 1 to use JAE, see also AssemblerX86Shared::UpdateBoundsCheck");
|
||||
/*
|
||||
* TODO: See 1287224 Unify MWasmBoundsCheck::redunant_ and needsBoundsCheck
|
||||
*/
|
||||
|
||||
// TODO: See 1287224 Unify MWasmBoundsCheck::redunant_ and needsBoundsCheck
|
||||
if (!redundant) {
|
||||
uint32_t cmpOffset = masm.cmp32WithPatch(ptr, Imm32(1 - mir->endOffset())).offset();
|
||||
masm.j(Assembler::AboveOrEqual, wasm::JumpTarget::OutOfBounds);
|
||||
|
|
|
@ -500,12 +500,7 @@ CodeGeneratorX86::emitWasmLoad(T* ins)
|
|||
Scalar::Type accessType = mir->accessType();
|
||||
MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD NYI");
|
||||
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
|
||||
|
||||
if (mir->offset() > INT32_MAX) {
|
||||
// This is unreachable because of the bounds check.
|
||||
masm.breakpoint();
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(mir->offset() <= INT32_MAX);
|
||||
|
||||
const LAllocation* ptr = ins->ptr();
|
||||
Operand srcAddr = ptr->isBogus()
|
||||
|
@ -539,12 +534,7 @@ CodeGeneratorX86::emitWasmStore(T* ins)
|
|||
Scalar::Type accessType = mir->accessType();
|
||||
MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD NYI");
|
||||
MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
|
||||
|
||||
if (mir->offset() > INT32_MAX) {
|
||||
// This is unreachable because of the bounds check.
|
||||
masm.breakpoint();
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(mir->offset() <= INT32_MAX);
|
||||
|
||||
const LAllocation* ptr = ins->ptr();
|
||||
Operand dstAddr = ptr->isBogus()
|
||||
|
|
|
@ -248,6 +248,7 @@ MSG_DEF(JSMSG_EMPTY_CONSEQUENT, 0, JSEXN_SYNTAXERR, "mistyped ; after con
|
|||
MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 0, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?")
|
||||
MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL,0, JSEXN_SYNTAXERR, "export declarations may only appear at top level of a module")
|
||||
MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 0, JSEXN_SYNTAXERR, "finally without try")
|
||||
MSG_DEF(JSMSG_FORBIDDEN_AS_STATEMENT, 1, JSEXN_SYNTAXERR, "{0} can't appear in single-statement context")
|
||||
MSG_DEF(JSMSG_FROM_AFTER_IMPORT_CLAUSE, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import clause")
|
||||
MSG_DEF(JSMSG_FROM_AFTER_EXPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after export *")
|
||||
MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}")
|
||||
|
@ -260,6 +261,7 @@ MSG_DEF(JSMSG_LET_CLASS_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid
|
|||
MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
|
||||
MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
|
||||
MSG_DEF(JSMSG_LEXICAL_DECL_LABEL, 1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled")
|
||||
MSG_DEF(JSMSG_GENERATOR_LABEL, 0, JSEXN_SYNTAXERR, "generator functions cannot be labelled")
|
||||
MSG_DEF(JSMSG_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions cannot be labelled")
|
||||
MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks")
|
||||
MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
|
||||
|
|
|
@ -719,16 +719,19 @@ JSCompartment::ensureRandomNumberGenerator()
|
|||
}
|
||||
}
|
||||
|
||||
double
|
||||
js::math_random_impl(JSContext* cx)
|
||||
{
|
||||
JSCompartment* comp = cx->compartment();
|
||||
comp->ensureRandomNumberGenerator();
|
||||
return comp->randomNumberGenerator.ref().nextDouble();
|
||||
}
|
||||
|
||||
bool
|
||||
js::math_random(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
JSCompartment* comp = cx->compartment();
|
||||
comp->ensureRandomNumberGenerator();
|
||||
|
||||
double z = comp->randomNumberGenerator.ref().nextDouble();
|
||||
args.rval().setDouble(z);
|
||||
args.rval().setNumber(math_random_impl(cx));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,9 @@ GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed);
|
|||
extern uint64_t
|
||||
random_next(uint64_t* rngState, int bits);
|
||||
|
||||
extern double
|
||||
math_random_impl(JSContext* cx);
|
||||
|
||||
extern bool
|
||||
math_random(JSContext* cx, unsigned argc, js::Value* vp);
|
||||
|
||||
|
|
|
@ -2544,6 +2544,13 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::Object
|
|||
return Proxy::setPrototype(cx, obj, proto, result);
|
||||
}
|
||||
|
||||
/*
|
||||
* ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return true.
|
||||
* Since the values in question are objects, we can just compare pointers.
|
||||
*/
|
||||
if (proto == obj->staticPrototype())
|
||||
return result.succeed();
|
||||
|
||||
/* Disallow mutation of immutable [[Prototype]]s. */
|
||||
if (obj->staticPrototypeIsImmutable() && ImmutablePrototypesEnabled)
|
||||
return result.fail(JSMSG_CANT_SET_PROTO);
|
||||
|
@ -2578,13 +2585,6 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::Object
|
|||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return true.
|
||||
* Since the values in question are objects, we can just compare pointers.
|
||||
*/
|
||||
if (proto == obj->staticPrototype())
|
||||
return result.succeed();
|
||||
|
||||
/* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
|
||||
bool extensible;
|
||||
if (!IsExtensible(cx, obj, &extensible))
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// Setting a "new" prototype to the current [[Prototype]] value should never fail
|
||||
|
||||
var x = {}, t = Object.create(x);
|
||||
Object.preventExtensions(t);
|
||||
// Should not fail, because it is the same [[Prototype]] value
|
||||
Object.setPrototypeOf(t, x);
|
||||
|
||||
// Object.prototype's [[Prototype]] is immutable, make sure we can still set null
|
||||
Object.setPrototypeOf(Object.prototype, null);
|
||||
|
||||
reportCompare(true, true);
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 1288459;
|
||||
var summary =
|
||||
"Properly implement the spec's distinctions between StatementListItem and " +
|
||||
"Statement grammar productions and their uses";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
assertThrowsInstanceOf(() => Function("a: let x;"), SyntaxError);
|
||||
assertThrowsInstanceOf(() => Function("b: const y = 3;"), SyntaxError);
|
||||
assertThrowsInstanceOf(() => Function("c: class z {};"), SyntaxError);
|
||||
|
||||
assertThrowsInstanceOf(() => Function("'use strict'; d: function w() {};"), SyntaxError);
|
||||
|
||||
// Annex B.3.2 allows this in non-strict mode code.
|
||||
Function("e: function x() {};");
|
||||
|
||||
assertThrowsInstanceOf(() => Function("f: function* y() {}"), SyntaxError);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
||||
print("Tests complete");
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 1288459;
|
||||
var summary = "let can't be used as a label in strict mode code";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
Function("let: 42")
|
||||
assertThrowsInstanceOf(() => Function(" 'use strict'; let: 42"), SyntaxError);
|
||||
assertThrowsInstanceOf(() => Function(" 'use strict' \n let: 42"), SyntaxError);
|
||||
|
||||
eval("let: 42")
|
||||
assertThrowsInstanceOf(() => eval(" 'use strict'; let: 42"), SyntaxError);
|
||||
assertThrowsInstanceOf(() => eval(" 'use strict' \n let: 42;"), SyntaxError);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
||||
print("Tests complete");
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче