merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2016-09-13 11:57:54 +02:00
commit f77a18b1b7
142 changed files with 3468 additions and 1123 deletions

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 1287426 - Clobber required because of Linux Chromium sandbox file moves.
Bug 1288460 requires another clobber due to bug 1298779.

View File

@ -258,6 +258,24 @@ EventTree::FindOrInsert(Accessible* aContainer)
// We got a match.
if (parent->Parent() == node->mContainer) {
// Reject the node if it's contained by a show/hide event target
uint32_t evCount = node->mDependentEvents.Length();
for (uint32_t idx = 0; idx < evCount; idx++) {
AccMutationEvent* ev = node->mDependentEvents[idx];
if (ev->GetAccessible() == parent) {
#ifdef A11Y_LOG
if (logging::IsEnabledAll(logging::eEventTree |
logging::eVerbose)) {
logging::MsgBegin("EVENTS_TREE",
"Rejecting node since contained by show/hide target");
logging::AccessibleInfo("Container", aContainer);
logging::MsgEnd();
}
#endif
return nullptr;
}
}
return node->FindOrInsert(aContainer);
}

View File

@ -446,6 +446,47 @@
}
}
/**
* Insert accessibles with a child node moved by aria-owns
* Markup:
* <div id="t6_fc">
* <div id="t6_owns"></div>
* </div>
* <div id="t6_sc" aria-owns="t6_owns"></div>
*/
function test6()
{
this.parent = getNode("t6");
this.fc = document.createElement("div");
this.fc.setAttribute("id", "t6_fc");
this.owns = document.createElement("div");
this.owns.setAttribute("id", "t6_owns");
this.sc = document.createElement("div");
this.sc.setAttribute("id", "t6_sc");
this.eventSeq = [
new invokerChecker(EVENT_SHOW, this.fc),
new invokerChecker(EVENT_SHOW, this.sc),
new invokerChecker(EVENT_REORDER, this.parent),
new unexpectedInvokerChecker(EVENT_REORDER, this.fc),
new unexpectedInvokerChecker(EVENT_REORDER, this.sc),
new unexpectedInvokerChecker(EVENT_HIDE, this.owns),
new unexpectedInvokerChecker(EVENT_SHOW, this.owns)
];
this.invoke = function test6_invoke()
{
getNode("t6").appendChild(this.fc);
getNode("t6_fc").appendChild(this.owns);
getNode("t6").appendChild(this.sc);
getNode("t6_sc").setAttribute("aria-owns", "t6_owns");
};
this.getID = function test6_getID() {
return "Insert accessibles with a child node moved by aria-owns";
};
}
////////////////////////////////////////////////////////////////////////////
// Do tests.
@ -477,6 +518,7 @@
gQueue.push(new test3());
gQueue.push(new test4());
gQueue.push(new test5());
gQueue.push(new test6());
gQueue.invoke(); // Will call SimpleTest.finish();
}
@ -567,5 +609,8 @@
<div role="option" id="t5_o">opt</div>
</div>
</div>
<div id="t6">
</div>
</body>
</html>

View File

@ -425,12 +425,6 @@ Section "-Application" APP_IDX
${Else}
WriteRegDWORD HKCU "$0" "IconsVisible" 0
${EndIf}
${If} ${AtLeastWin8}
${RemoveDEHRegistration} ${DELEGATE_EXECUTE_HANDLER_ID} \
$AppUserModelID \
"FirefoxURL" \
"FirefoxHTML"
${EndIf}
${EndIf}
!ifdef MOZ_MAINTENANCE_SERVICE

View File

@ -2,29 +2,7 @@
# 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/.
; The registration ID of the COM server which is used for choosing wether
; to launch the Win8 metro browser or desktop browser.
!define DELEGATE_EXECUTE_HANDLER_ID {5100FEC1-212B-4BF5-9BF8-3E650FD794A3}
;
; Defines for adjust token privs and for enumerating keys
!ifndef TOKEN_QUERY
!define TOKEN_QUERY 0x0008
!endif
!ifndef TOKEN_ADJUST_PRIVILEGES
!define TOKEN_ADJUST_PRIVILEGES 0x0020
!endif
!ifndef SE_RESTORE_NAME
!define SE_RESTORE_NAME SeRestorePrivilege
!endif
!ifndef SE_PRIVILEGE_ENABLED
!define SE_PRIVILEGE_ENABLED 0x00000002
!endif
!ifndef HKEY_USERS
!define HKEY_USERS 0x80000003
!endif
!macro PostUpdate
; PostUpdate is called from both session 0 and from the user session
; for service updates, make sure that we only register with the user session
; Otherwise ApplicationID::Set can fail intermittently with a file in use error.
@ -191,14 +169,6 @@
${EndIf}
${EndIf}
!endif
; Register the DEH
${If} ${AtLeastWin8}
${RemoveDEHRegistration} ${DELEGATE_EXECUTE_HANDLER_ID} \
$AppUserModelID \
"FirefoxURL" \
"FirefoxHTML"
${EndIf}
!macroend
!define PostUpdate "!insertmacro PostUpdate"
@ -831,168 +801,6 @@ ${EndIf}
!macroend
!define RemoveDeprecatedKeys "!insertmacro RemoveDeprecatedKeys"
; Resets Win8+ specific toast keys Windows sets. We call this on a
; fresh install and on uninstall.
!macro ResetWin8PromptKeys KEY PREFIX
${If} ${AtLeastWin8}
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.htm"
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.html"
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.xht"
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.xhtml"
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.shtml"
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxURL_ftp"
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxURL_http"
DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxURL_https"
${EndIf}
!macroend
!define ResetWin8PromptKeys "!insertmacro ResetWin8PromptKeys"
; Adds SE_RESTORE_NAME privs
!macro AcquireSERestoreName
StrCpy $R1 0
System::Call "kernel32::GetCurrentProcess() i .R0"
System::Call "advapi32::OpenProcessToken(i R0, i ${TOKEN_QUERY}|${TOKEN_ADJUST_PRIVILEGES}, \
*i R1R1) i .R0"
${If} $R0 != 0
System::Call "advapi32::LookupPrivilegeValue(t n, t '${SE_RESTORE_NAME}', *l .R2) i .R0"
${If} $R0 != 0
System::Call "*(i 1, l R2, i ${SE_PRIVILEGE_ENABLED}) i .R0"
System::Call "advapi32::AdjustTokenPrivileges(i R1, i 0, i R0, i 0, i 0, i 0)"
System::Free $R0
${EndIf}
System::Call "kernel32::CloseHandle(i R1)"
${EndIf}
!macroend
!define AcquireSERestoreName "!insertmacro AcquireSERestoreName"
!define un.AcquireSERestoreName "!insertmacro AcquireSERestoreName"
; Mounts all user ntuser.dat files into the registry as a subkey of HKU
!macro MountRegistryIntoHKU
; $0 is used as an index for HKEY_USERS enumeration
StrCpy $0 0
${Do}
EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $0
${If} $1 == ""
${Break}
${EndIf}
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$1" "ProfileImagePath"
System::Call "advapi32::RegLoadKey(i ${HKEY_USERS}, t 'User-$0', t '$2\ntuser.dat')"
System::Call "advapi32::RegLoadKey(i ${HKEY_USERS}, t 'User-$0_Classes', t '$2\AppData\Local\Microsoft\Windows\UsrClass.dat')"
IntOp $0 $0 + 1
${Loop}
!macroend
!define MountRegistryIntoHKU "!insertmacro MountRegistryIntoHKU"
!define un.MountRegistryIntoHKU "!insertmacro MountRegistryIntoHKU"
; Unmounts all user ntuser.dat files into the registry as a subkey of HKU
!macro UnmountRegistryIntoHKU
; $0 is used as an index for HKEY_USERS enumeration
StrCpy $0 0
${Do}
EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $0
${If} $1 == ""
${Break}
${EndIf}
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$1" "ProfileImagePath"
System::Call "advapi32::RegUnLoadKey(i ${HKEY_USERS}, t 'User-$0')"
System::Call "advapi32::RegUnLoadKey(i ${HKEY_USERS}, t 'User-$0_Classes')"
IntOp $0 $0 + 1
${Loop}
!macroend
!define UnmountRegistryIntoHKU "!insertmacro UnmountRegistryIntoHKU"
!define un.UnmountRegistryIntoHKU "!insertmacro UnmountRegistryIntoHKU"
; Unconditionally removes the delegate execute handler registration used to
; launch the metro browser and misc. metro related registry values.
!macro RemoveDEHRegistration DELEGATE_EXECUTE_HANDLER_ID \
APP_USER_MODEL_ID \
PROTOCOL_ACTIVATION_ID \
FILE_ACTIVATION_ID
${AcquireSERestoreName}
${MountRegistryIntoHKU}
; $0 is used as an index for HKEY_USERS enumeration
StrCpy $0 0
${Do}
EnumRegKey $1 HKU "" $0
${If} $1 == ""
${Break}
${EndIf}
ClearErrors
${WordFind} "$1" "_Classes" "E#" $3
${Unless} ${Errors}
; remove the app user model id root registration. We don't need this
; here anymore, we just use it for tray registrationdown in widget,
; which we read out of the mozilla keys.
${If} "${APP_USER_MODEL_ID}" != ""
; The removal of this key intermittently fails, so do the best we can in cleanup
DeleteRegValue HKU "$1\${APP_USER_MODEL_ID}\.exe\shell\open\command" "DelegateExecute"
DeleteRegKey HKU "$1\${APP_USER_MODEL_ID}\.exe\shell\open"
DeleteRegKey HKU "$1\${APP_USER_MODEL_ID}\.exe\shell"
DeleteRegKey HKU "$1\${APP_USER_MODEL_ID}\.exe"
DeleteRegKey HKU "$1\\${APP_USER_MODEL_ID}"
${EndIf}
;
; Remove delegate execute handler clsid registration
DeleteRegKey HKU "$1\CLSID\${DELEGATE_EXECUTE_HANDLER_ID}"
; Remove protocol and file delegate execute handler id assoc
DeleteRegValue HKU "$1\${PROTOCOL_ACTIVATION_ID}" "AppUserModelID"
DeleteRegValue HKU "$1\${FILE_ACTIVATION_ID}" "AppUserModelID"
; Remove delegate execute application registry keys
DeleteRegKey HKU "$1\${PROTOCOL_ACTIVATION_ID}\Application"
DeleteRegKey HKU "$1\${FILE_ACTIVATION_ID}\Application"
; Remove misc. shell open info
DeleteRegValue HKU "$1\${PROTOCOL_ACTIVATION_ID}\shell\open" "CommandId"
DeleteRegValue HKU "$1\${FILE_ACTIVATION_ID}\shell\open" "CommandId"
DeleteRegValue HKU "$1\${PROTOCOL_ACTIVATION_ID}\shell\open\command" "DelegateExecute"
DeleteRegValue HKU "$1\${FILE_ACTIVATION_ID}\shell\open\command" "DelegateExecute"
; remove metro browser splash image data
DeleteRegKey HKU "$1\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\SystemAppData\DefaultBrowser_NOPUBLISHERID\SplashScreen\DefaultBrowser_NOPUBLISHERID!${APP_USER_MODEL_ID}"
${Else}
; misc. Metro keys
DeleteRegKey HKU "$1\Software\Mozilla\Firefox\Metro"
DeleteRegValue HKU "$1\Software\Mozilla\Firefox" "CEHDump"
DeleteRegValue HKU "$1\Software\Mozilla\Firefox" "MetroD3DAvailable"
DeleteRegValue HKU "$1\Software\Mozilla\Firefox" "MetroLastAHE"
${ResetWin8PromptKeys} "HKU" "$1\"
${EndIf}
IntOp $0 $0 + 1
${Loop}
${UnmountRegistryIntoHKU}
; The removal of this key intermittently fails, so do the best we can in cleanup
${If} "${APP_USER_MODEL_ID}" != ""
DeleteRegValue HKLM "Software\Classes\${APP_USER_MODEL_ID}\.exe\shell\open\command" "DelegateExecute"
DeleteRegKey HKLM "Software\Classes\${APP_USER_MODEL_ID}\.exe\shell\open"
DeleteRegKey HKLM "Software\Classes\${APP_USER_MODEL_ID}\.exe\shell"
DeleteRegKey HKLM "Software\Classes\${APP_USER_MODEL_ID}\.exe"
DeleteRegKey HKLM "Software\Classes\${APP_USER_MODEL_ID}"
${EndIf}
; Remove HKLM entries
DeleteRegKey HKLM "Software\Classes\CLSID\${DELEGATE_EXECUTE_HANDLER_ID}"
DeleteRegKey HKLM "Software\Classes\${PROTOCOL_ACTIVATION_ID}\Application"
DeleteRegKey HKLM "Software\Classes\${FILE_ACTIVATION_ID}\Application"
DeleteRegValue HKLM "Software\Classes\${PROTOCOL_ACTIVATION_ID}\shell\open\command" "DelegateExecute"
DeleteRegValue HKLM "Software\Classes\${FILE_ACTIVATION_ID}\shell\open\command" "DelegateExecute"
DeleteRegValue HKLM "Software\Classes\${PROTOCOL_ACTIVATION_ID}" "AppUserModelID"
DeleteRegValue HKLM "Software\Classes\${FILE_ACTIVATION_ID}" "AppUserModelID"
DeleteRegValue HKLM "Software\Classes\${PROTOCOL_ACTIVATION_ID}\shell\open" "CommandId"
DeleteRegValue HKLM "Software\Classes\${FILE_ACTIVATION_ID}\shell\open" "CommandId"
ClearErrors
!macroend
!define RemoveDEHRegistration "!insertmacro RemoveDEHRegistration"
!define un.RemoveDEHRegistration "!insertmacro RemoveDEHRegistration"
; Removes various directories and files for reasons noted below.
!macro RemoveDeprecatedFiles
; Some users are ending up with unpacked chrome instead of omni.ja. This

View File

@ -278,13 +278,6 @@ Section "Uninstall"
${un.SetAppLSPCategories}
${EndIf}
${If} ${AtLeastWin8}
${RemoveDEHRegistration} ${DELEGATE_EXECUTE_HANDLER_ID} \
$AppUserModelID \
"FirefoxURL" \
"FirefoxHTML"
${EndIf}
${un.RegCleanAppHandler} "FirefoxURL"
${un.RegCleanAppHandler} "FirefoxHTML"
${un.RegCleanProtocolHandler} "ftp"

View File

@ -33,6 +33,9 @@
--toolbarbutton-checkedhover-backgroundcolor: rgba(200,200,200,.5);
--panel-separator-color: ThreeDShadow;
--arrowpanel-dimmed: hsla(210,4%,10%,.07);
--arrowpanel-dimmed-further: hsla(210,4%,10%,.12);
--arrowpanel-dimmed-even-further: hsla(210,4%,10%,.17);
--urlbar-separator-color: ThreeDShadow;
}
@ -995,7 +998,7 @@ toolbaritem[cui-areatype="menu-panel"] > :-moz-any(@nestedButtons@) > .toolbarbu
#urlbar-search-footer {
border-top: 1px solid var(--panel-separator-color);
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
}
#urlbar-search-settings {
@ -1009,11 +1012,11 @@ toolbaritem[cui-areatype="menu-panel"] > :-moz-any(@nestedButtons@) > .toolbarbu
}
#urlbar-search-settings:hover {
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
}
#urlbar-search-settings:hover:active {
background-color: hsla(210,4%,10%,.12);
background-color: var(--arrowpanel-dimmed-further);
}
#urlbar-search-splitter {

View File

@ -137,7 +137,7 @@ menuitem[cmd="cmd_clearhistory"][disabled] {
.search-panel-header {
font-weight: normal;
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
border: none;
border-top: 1px solid var(--panel-separator-color);
padding: 3px 5px;
@ -301,7 +301,7 @@ menuitem[cmd="cmd_clearhistory"][disabled] {
}
.search-setting-button[selected] {
background-color: hsla(210,4%,10%,.15);
background-color: var(--arrowpanel-dimmed-further);
}
.search-setting-button-compact {

View File

@ -44,6 +44,9 @@
--urlbar-dropmarker-active-2x-region: rect(0, 44px, 28px, 22px);
--panel-separator-color: hsla(210,4%,10%,.14);
--arrowpanel-dimmed: hsla(210,4%,10%,.07);
--arrowpanel-dimmed-further: hsla(210,4%,10%,.12);
--arrowpanel-dimmed-even-further: hsla(210,4%,10%,.17);
--urlbar-separator-color: hsla(0,0%,16%,.2);
}
@ -1708,7 +1711,7 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
#urlbar-search-footer {
border-top: 1px solid var(--panel-separator-color);
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
}
#urlbar-search-settings {
@ -1720,11 +1723,11 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
}
#urlbar-search-settings:hover {
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
}
#urlbar-search-settings:hover:active {
background-color: hsla(210,4%,10%,.12);
background-color: var(--arrowpanel-dimmed-further);
}
#urlbar-search-splitter {

View File

@ -127,7 +127,7 @@
.search-panel-header {
font-size: 10px;
font-weight: normal;
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
border-top: 1px solid var(--panel-separator-color);
margin: 0;
padding: 3px 6px;
@ -283,7 +283,7 @@
}
.search-setting-button[selected] {
background-color: hsla(210,4%,10%,.15);
background-color: var(--arrowpanel-dimmed-further);
}
.search-setting-button-compact {

View File

@ -156,11 +156,11 @@
}
.identity-popup-expander:hover {
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
}
.identity-popup-expander:hover:active {
background-color: hsla(210,4%,10%,.12);
background-color: var(--arrowpanel-dimmed-further);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}
@ -278,7 +278,7 @@
#identity-popup-securityView-footer {
margin-top: 1em;
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
}
#identity-popup-securityView-footer > button {
@ -293,11 +293,11 @@
#identity-popup-securityView-footer > button:hover,
#identity-popup-securityView-footer > button:focus {
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
}
#identity-popup-securityView-footer > button:hover:active {
background-color: hsla(210,4%,10%,.12);
background-color: var(--arrowpanel-dimmed-further);
}
#identity-popup-content-verifier ~ description {

View File

@ -407,7 +407,7 @@ toolbarpaletteitem:-moz-any([place="palette"], [place="panel"]) > toolbaritem[sd
}
.panelUI-grid .toolbarbutton-1:not([buttonover])@buttonStateHover@ > .toolbarbutton-menubutton-dropmarker {
background-color: hsla(210,4%,10%,.1) !important;
background-color: var(--arrowpanel-dimmed) !important;
border-radius: 0 0 0 2px;
}
@ -434,7 +434,7 @@ toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"] > iframe {
}
#PanelUI-multiView[viewtype="subview"] > .panel-viewcontainer > .panel-viewstack > .panel-mainview > #PanelUI-mainView {
background-color: hsla(210,4%,10%,.1);
background-color: var(--arrowpanel-dimmed);
}
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .panel-wide-item,
@ -535,7 +535,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
display: flex;
flex-shrink: 0;
flex-direction: column;
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
padding: 0;
margin: 0;
}
@ -552,7 +552,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
#PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status::after {
content: url(chrome://browser/skin/warning.svg);
filter: drop-shadow(0 1px 0 hsla(206, 50%, 10%, .15));
filter: drop-shadow(0 1px 0 hsla(206,50%,10%,.15));
width: 47px;
padding-top: 1px;
display: block;
@ -566,7 +566,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
width: 14px;
height: 14px;
margin-right: 16.5px;
box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px rgba(0, 0, 0, 0.1) inset, 0px 1px 0px rgba(12, 27, 38, 0.2);
box-shadow: 0px 1px 0px rgba(255,255,255,.2) inset, 0px -1px 0px rgba(0,0,0,.1) inset, 0px 1px 0px rgba(12,27,38,.2);
border-radius: 2px;
background-size: contain;
display: -moz-box;
@ -909,8 +909,8 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
#PanelUI-help:not([disabled]):hover,
#PanelUI-customize:hover,
#PanelUI-quit:not([disabled]):hover {
outline: 1px solid hsla(210,4%,10%,.07);
background-color: hsla(210,4%,10%,.07);
outline: 1px solid var(--arrowpanel-dimmed);
background-color: var(--arrowpanel-dimmed);
}
#PanelUI-fxa-status:not([disabled]):hover:active,
@ -918,8 +918,8 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
#PanelUI-help:not([disabled]):hover:active,
#PanelUI-customize:hover:active,
#PanelUI-quit:not([disabled]):hover:active {
outline: 1px solid hsla(210,4%,10%,.12);
background-color: hsla(210,4%,10%,.12);
outline: 1px solid var(--arrowpanel-dimmed-further);
background-color: var(--arrowpanel-dimmed-further);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}
@ -931,16 +931,16 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
}
#PanelUI-footer-fxa[fxastatus="error"] {
background-color: hsla(42, 94%, 88%, 1.0);
border-top: 1px solid hsla(42, 94%, 70%, 1.0);
background-color: hsl(42,94%,88%);
border-top: 1px solid hsl(42,94%,70%);
}
#PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status:hover {
background-color: hsla(42, 94%, 85%, 1.0);
background-color: hsl(42,94%,85%);
}
#PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status:hover:active {
background-color: hsla(42, 94%, 82%, 1.0);
background-color: hsl(42,94%,82%);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}
@ -949,27 +949,27 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
}
#PanelUI-update-status[update-status="succeeded"] {
background-color: hsla(96, 65%, 75%, 0.5);
background-color: hsla(96,65%,75%,.5);
}
#PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover {
background-color: hsla(96, 65%, 75%, 0.8);
background-color: hsla(96,65%,75%,.8);
}
#PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover:active {
background-color: hsl(96, 65%, 75%);
background-color: hsl(96,65%,75%);
}
#PanelUI-update-status[update-status="failed"] {
background-color: hsla(359, 69%, 84%, 0.5);
background-color: hsla(359,69%,84%,.5);
}
#PanelUI-update-status[update-status="failed"]:not([disabled]):hover {
background-color: hsla(359, 69%, 84%, 0.8);
background-color: hsla(359,69%,84%,.8);
}
#PanelUI-update-status[update-status="failed"]:not([disabled]):hover:active {
background-color: hsl(359, 69%, 84%);
background-color: hsl(359,69%,84%);
}
#PanelUI-quit:not([disabled]):hover {
@ -1011,10 +1011,10 @@ panelview .toolbarbutton-1,
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton {
-moz-appearance: none;
padding: 0 6px;
background-color: hsla(210,4%,10%,0);
background-color: transparent;
border-radius: 2px;
border-style: solid;
border-color: hsla(210,4%,10%,0);
border-color: transparent;
}
panelview .toolbarbutton-1,
@ -1097,12 +1097,12 @@ menuitem.subviewbutton@menuStateHover@,
.share-provider-button@buttonStateHover@:not([checked="true"]),
.widget-overflow-list .toolbarbutton-1@buttonStateHover@,
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateHover@ {
background-color: hsla(210,4%,10%,.08);
border-color: hsla(210,4%,10%,.11);
background-color: var(--arrowpanel-dimmed);
border-color: var(--panel-separator-color);
}
.toolbaritem-combined-buttons@inAnyPanel@@buttonStateHover@ {
border-color: hsla(210,4%,10%,.11);
border-color: var(--panel-separator-color);
}
panelview .toolbarbutton-1:-moz-any(@buttonStateActive@,[checked=true]),
@ -1112,26 +1112,26 @@ menuitem.subviewbutton@menuStateActive@,
.share-provider-button:-moz-any(@buttonStateActive@,[checked=true]),
.widget-overflow-list .toolbarbutton-1@buttonStateActive@,
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateActive@ {
background-color: hsla(210,4%,10%,.12);
background-color: var(--arrowpanel-dimmed-further);
border-color: var(--panel-separator-color);
box-shadow: 0 1px 0 hsla(210,4%,10%,.03) inset;
}
.subviewbutton.panel-subview-footer {
margin: 4px -4px -4px;
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
border-top: 1px solid var(--panel-separator-color);
border-radius: 0;
}
menuitem.panel-subview-footer@menuStateHover@,
.subviewbutton.panel-subview-footer@buttonStateHover@ {
background-color: hsla(210,4%,10%,.15);
background-color: var(--arrowpanel-dimmed-further);
}
menuitem.panel-subview-footer@menuStateActive@,
.subviewbutton.panel-subview-footer@buttonStateActive@ {
background-color: hsla(210,4%,10%,.19);
background-color: var(--arrowpanel-dimmed-even-further);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}
@ -1298,11 +1298,11 @@ toolbarpaletteitem[place="palette"] > #search-container {
}
.toolbaritem-combined-buttons@inAnyPanel@ {
background-color: hsla(210,4%,10%,0);
background-color: transparent;
border-radius: 2px;
border: 1px solid;
border-color: hsla(210,4%,10%,0);
border-bottom-color: hsla(210,4%,10%,.1);
border-color: transparent;
border-bottom-color: var(--panel-separator-color);
padding: 0;
transition-property: background-color, border-color;
transition-duration: 150ms;
@ -1481,7 +1481,7 @@ menuitem[checked="true"].subviewbutton > .menu-iconic-left {
#PanelUI-panic-timeframe {
padding: 15px;
border-bottom: 1px solid rgba(0,0,0,0.1);
border-bottom: 1px solid var(--panel-separator-color);
}
#panic-button-success-icon,
@ -1540,14 +1540,14 @@ menuitem[checked="true"].subviewbutton > .menu-iconic-left {
}
.subviewradio@buttonStateHover@ {
background-color: hsla(210,4%,10%,.08);
background-color: var(--arrowpanel-dimmed);
border-color: var(--panel-separator-color);
}
.subviewradio[selected],
.subviewradio[selected]:hover,
.subviewradio@buttonStateActive@ {
background-color: hsla(210,4%,10%,.12);
background-color: var(--arrowpanel-dimmed-further);
border-color: var(--panel-separator-color);
box-shadow: 0 1px 0 hsla(210,4%,10%,.03) inset;
}

View File

@ -41,7 +41,7 @@
}
.downloadsPanelFooter {
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
border-top: 1px solid var(--panel-separator-color);
}
@ -65,14 +65,14 @@
}
.downloadsPanelFooterButton:hover {
outline: 1px solid hsla(210,4%,10%,.07);
background-color: hsla(210,4%,10%,.07);
outline: 1px solid var(--arrowpanel-dimmed);
background-color: var(--arrowpanel-dimmed);
}
.downloadsPanelFooterButton:hover:active,
.downloadsPanelFooterButton[open="true"] {
outline: 1px solid hsla(210,4%,10%,.12);
background-color: hsla(210,4%,10%,.12);
outline: 1px solid var(--arrowpanel-dimmed-further);
background-color: var(--arrowpanel-dimmed-further);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}

View File

@ -26,8 +26,8 @@
}
.click-to-play-plugins-notification-button-container {
background-color: hsla(210,4%,10%,.07);
border-top: 1px solid hsla(210,4%,10%,.12);
background-color: var(--arrowpanel-dimmed);
border-top: 1px solid var(--panel-separator-color);
padding: 10px;
margin-top: 5px;
}

View File

@ -46,6 +46,9 @@
--urlbar-dropmarker-active-2x-region: rect(0, 66px, 28px, 44px);
--panel-separator-color: ThreeDLightShadow;
--arrowpanel-dimmed: hsla(210,4%,10%,.07);
--arrowpanel-dimmed-further: hsla(210,4%,10%,.12);
--arrowpanel-dimmed-even-further: hsla(210,4%,10%,.17);
--urlbar-separator-color: ThreeDLightShadow;
}
@ -1444,7 +1447,7 @@ html|*.urlbar-input:-moz-lwtheme::placeholder,
#urlbar-search-footer {
border-top: 1px solid var(--panel-separator-color);
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
}
#urlbar-search-settings {
@ -1458,11 +1461,11 @@ html|*.urlbar-input:-moz-lwtheme::placeholder,
}
#urlbar-search-settings:hover {
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
}
#urlbar-search-settings:hover:active {
background-color: hsla(210,4%,10%,.12);
background-color: var(--arrowpanel-dimmed-further);
}
#urlbar-search-splitter {

View File

@ -132,7 +132,7 @@
.search-panel-header {
font-weight: normal;
background-color: hsla(210,4%,10%,.07);
background-color: var(--arrowpanel-dimmed);
border: none;
border-top: 1px solid var(--panel-separator-color);
margin: 0;
@ -293,7 +293,7 @@
}
.search-setting-button[selected] {
background-color: hsla(210,4%,10%,.15);
background-color: var(--arrowpanel-dimmed-further);
}
.search-setting-button-compact {

View File

@ -2,6 +2,14 @@ dnl This Source Code Form is subject to the terms of the Mozilla Public
dnl License, v. 2.0. If a copy of the MPL was not distributed with this
dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
dnl Several autoconf functions AC_REQUIRE AC_PROG_CPP/AC_PROG_CXXCPP,
dnl meaning they are called even when we don't call them explicitly.
dnl However, theses checks are not necessary and python configure sets
dnl the corresponding variables already, so just skip those tests
dnl entirely.
define([AC_PROG_CPP],[])
define([AC_PROG_CXXCPP],[])
AC_DEFUN([MOZ_TOOL_VARIABLES],
[
GNU_AS=

View File

@ -754,6 +754,23 @@ def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
valid_compiler.try_compile(check_msg='%s works' % what,
onerror=compiler_error)
# Set CPP/CXXCPP for both the build system and old-configure. We don't
# need to check this works for preprocessing, because we already relied
# on $CC -E/$CXX -E doing preprocessing work to validate the compiler
# in the first place.
if host_or_target == target:
pp_var = {
'C': 'CPP',
'C++': 'CXXCPP',
}[language]
preprocessor = depends_if(valid_compiler)(
lambda x: list(x.wrapper) + [x.compiler, '-E'] + list(x.flags))
set_config(pp_var, preprocessor)
add_old_configure_assignment(pp_var, preprocessor)
return valid_compiler

View File

@ -21,7 +21,7 @@ add_task(function* () {
webconsole: hud,
messages: [
{
text: "SyntaxError: redefining arguments is deprecated",
text: "SyntaxError: 'arguments' can't be defined or assigned to in strict mode code",
category: CATEGORY_JS,
severity: SEVERITY_ERROR,
},

View File

@ -126,6 +126,22 @@ KeyframeEffect::SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget)
}
}
void
KeyframeEffect::SetIterationComposite(
const IterationCompositeOperation& aIterationComposite)
{
if (mEffectOptions.mIterationComposite == aIterationComposite) {
return;
}
if (mAnimation && mAnimation->IsRelevant()) {
nsNodeUtils::AnimationChanged(mAnimation);
}
mEffectOptions.mIterationComposite = aIterationComposite;
RequestRestyle(EffectCompositor::RestyleType::Layer);
}
void
KeyframeEffect::SetSpacing(JSContext* aCx,
const nsAString& aSpacing,

View File

@ -67,6 +67,8 @@ public:
void SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget);
void SetSpacing(JSContext* aCx, const nsAString& aSpacing, ErrorResult& aRv);
void SetIterationComposite(
const IterationCompositeOperation& aIterationComposite);
};
} // namespace dom

View File

@ -9,6 +9,11 @@
#include "nsCSSProps.h"
#include "nsString.h"
// X11 has a #define for None
#ifdef None
#undef None
#endif
#include "mozilla/dom/KeyframeEffectBinding.h" // IterationCompositeOperation
namespace mozilla {
@ -51,8 +56,9 @@ struct KeyframeEffectParams
nsAString& aInvalidPacedProperty,
ErrorResult& aRv);
// FIXME: Bug 1216843: Add IterationCompositeOperations and
// Bug 1216844: Add CompositeOperation
dom::IterationCompositeOperation mIterationComposite =
dom::IterationCompositeOperation::Replace;
// FIXME: Bug 1216844: Add CompositeOperation
SpacingMode mSpacingMode = SpacingMode::distribute;
nsCSSPropertyID mPacedProperty = eCSSProperty_UNKNOWN;
};

View File

@ -77,7 +77,7 @@ KeyframeEffectReadOnly::WrapObject(JSContext* aCx,
IterationCompositeOperation
KeyframeEffectReadOnly::IterationComposite() const
{
return IterationCompositeOperation::Replace;
return mEffectOptions.mIterationComposite;
}
CompositeOperation
@ -111,12 +111,7 @@ KeyframeEffectReadOnly::NotifyAnimationTimingUpdated()
}
// Request restyle if necessary.
//
// Bug 1216843: When we implement iteration composite modes, we need to
// also detect if the current iteration has changed.
if (mAnimation &&
!mProperties.IsEmpty() &&
GetComputedTiming().mProgress != mProgressOnLastCompose) {
if (mAnimation && !mProperties.IsEmpty() && HasComputedTimingChanged()) {
EffectCompositor::RestyleType restyleType =
CanThrottle() ?
EffectCompositor::RestyleType::Throttled :
@ -125,11 +120,13 @@ KeyframeEffectReadOnly::NotifyAnimationTimingUpdated()
}
// If we're no longer "in effect", our ComposeStyle method will never be
// called and we will never have a chance to update mProgressOnLastCompose.
// We clear mProgressOnLastCompose here to ensure that if we later become
// "in effect" we will request a restyle (above).
// called and we will never have a chance to update mProgressOnLastCompose
// and mCurrentIterationOnLastCompose.
// We clear them here to ensure that if we later become "in effect" we will
// request a restyle (above).
if (!inEffect) {
mProgressOnLastCompose.SetNull();
mCurrentIterationOnLastCompose = 0;
}
}
@ -312,6 +309,7 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
{
ComputedTiming computedTiming = GetComputedTiming();
mProgressOnLastCompose = computedTiming.mProgress;
mCurrentIterationOnLastCompose = computedTiming.mCurrentIteration;
// If the progress is null, we don't have fill data for the current
// time so we shouldn't animate.
@ -373,12 +371,42 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
aStyleRule = new AnimValuesStyleRule();
}
StyleAnimationValue fromValue = segment->mFromValue;
StyleAnimationValue toValue = segment->mToValue;
// Iteration composition for accumulate
if (mEffectOptions.mIterationComposite ==
IterationCompositeOperation::Accumulate &&
computedTiming.mCurrentIteration > 0) {
const AnimationPropertySegment& lastSegment =
prop.mSegments.LastElement();
// FIXME: Bug 1293492: Add a utility function to calculate both of
// below StyleAnimationValues.
DebugOnly<bool> accumulateResult =
StyleAnimationValue::Accumulate(prop.mProperty,
fromValue,
lastSegment.mToValue,
computedTiming.mCurrentIteration);
// We can't check the accumulation result in case of filter property.
// That's because some filter property can't accumulate,
// e.g. 'contrast(2) brightness(2)' onto 'brightness(1) contrast(1)'
// because of mismatch of the order.
MOZ_ASSERT(accumulateResult || prop.mProperty == eCSSProperty_filter,
"could not accumulate value");
accumulateResult =
StyleAnimationValue::Accumulate(prop.mProperty,
toValue,
lastSegment.mToValue,
computedTiming.mCurrentIteration);
MOZ_ASSERT(accumulateResult || prop.mProperty == eCSSProperty_filter,
"could not accumulate value");
}
// Special handling for zero-length segments
if (segment->mToKey == segment->mFromKey) {
if (computedTiming.mProgress.Value() < 0) {
aStyleRule->AddValue(prop.mProperty, segment->mFromValue);
aStyleRule->AddValue(prop.mProperty, Move(fromValue));
} else {
aStyleRule->AddValue(prop.mProperty, segment->mToValue);
aStyleRule->AddValue(prop.mProperty, Move(toValue));
}
continue;
}
@ -394,14 +422,14 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
MOZ_ASSERT(IsFinite(valuePosition), "Position value should be finite");
StyleAnimationValue val;
if (StyleAnimationValue::Interpolate(prop.mProperty,
segment->mFromValue,
segment->mToValue,
fromValue,
toValue,
valuePosition, val)) {
aStyleRule->AddValue(prop.mProperty, Move(val));
} else if (valuePosition < 0.5) {
aStyleRule->AddValue(prop.mProperty, segment->mFromValue);
aStyleRule->AddValue(prop.mProperty, Move(fromValue));
} else {
aStyleRule->AddValue(prop.mProperty, segment->mToValue);
aStyleRule->AddValue(prop.mProperty, Move(toValue));
}
}
}
@ -482,6 +510,7 @@ KeyframeEffectParamsFromUnion(const OptionsType& aOptions,
result.mPacedProperty,
aInvalidPacedProperty,
aRv);
result.mIterationComposite = options.mIterationComposite;
}
return result;
}
@ -1280,5 +1309,20 @@ KeyframeEffectReadOnly::MarkCascadeNeedsUpdate()
effectSet->MarkCascadeNeedsUpdate();
}
bool
KeyframeEffectReadOnly::HasComputedTimingChanged() const
{
// Typically we don't need to request a restyle if the progress hasn't
// changed since the last call to ComposeStyle. The one exception is if the
// iteration composite mode is 'accumulate' and the current iteration has
// changed, since that will often produce a different result.
ComputedTiming computedTiming = GetComputedTiming();
return computedTiming.mProgress != mProgressOnLastCompose ||
(mEffectOptions.mIterationComposite ==
IterationCompositeOperation::Accumulate &&
computedTiming.mCurrentIteration !=
mCurrentIterationOnLastCompose);
}
} // namespace dom
} // namespace mozilla

View File

@ -375,6 +375,11 @@ protected:
// timing function) so we can avoid unnecessary style updates.
Nullable<double> mProgressOnLastCompose;
// The purpose of this value is the same as mProgressOnLastCompose but
// this is used to detect when the current iteration is not changing
// in the case when iterationComposite is accumulate.
uint64_t mCurrentIterationOnLastCompose = 0;
// We need to track when we go to or from being "in effect" since
// we need to re-evaluate the cascade of animations when that changes.
bool mInEffectOnLastAnimationTimingUpdate;
@ -387,6 +392,10 @@ private:
bool CanThrottle() const;
bool CanThrottleTransformChanges(nsIFrame& aFrame) const;
// Returns true if the computedTiming has changed since the last
// composition.
bool HasComputedTimingChanged() const;
// Returns true unless Gecko limitations prevent performing transform
// animations for |aFrame|. When returning true, the reason for the
// limitation is stored in |aOutPerformanceWarning|.

View File

@ -8167,6 +8167,7 @@ nsContentUtils::SendMouseEvent(nsCOMPtr<nsIPresShell> aPresShell,
return NS_ERROR_FAILURE;
EventMessage msg;
WidgetMouseEvent::ExitFrom exitFrom = WidgetMouseEvent::eChild;
bool contextMenuKey = false;
if (aType.EqualsLiteral("mousedown")) {
msg = eMouseDown;
@ -8178,6 +8179,9 @@ nsContentUtils::SendMouseEvent(nsCOMPtr<nsIPresShell> aPresShell,
msg = eMouseEnterIntoWidget;
} else if (aType.EqualsLiteral("mouseout")) {
msg = eMouseExitFromWidget;
} else if (aType.EqualsLiteral("mousecancel")) {
msg = eMouseExitFromWidget;
exitFrom = WidgetMouseEvent::eTopLevel;
} else if (aType.EqualsLiteral("mouselongtap")) {
msg = eMouseLongTap;
} else if (aType.EqualsLiteral("contextmenu")) {
@ -8209,6 +8213,7 @@ nsContentUtils::SendMouseEvent(nsCOMPtr<nsIPresShell> aPresShell,
event.mClickCount = aClickCount;
event.mTime = PR_IntervalNow();
event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
event.mExitFrom = exitFrom;
nsPresContext* presContext = aPresShell->GetPresContext();
if (!presContext)

View File

@ -2581,6 +2581,44 @@ void
nsJSContext::NotifyDidPaint()
{
sDidPaintAfterPreviousICCSlice = true;
if (sICCTimer) {
static uint32_t sCount = 0;
// 16 here is the common value for refresh driver tick frequency.
static const uint32_t kTicksPerSliceDelay = kICCIntersliceDelay / 16;
if (++sCount % kTicksPerSliceDelay != 0) {
// Don't trigger CC slice all the time after paint, but often still.
// The key point is to trigger it right after paint, especially when
// we're running RefreshDriver constantly.
return;
}
sICCTimer->Cancel();
ICCTimerFired(nullptr, nullptr);
if (sICCTimer) {
sICCTimer->InitWithNamedFuncCallback(ICCTimerFired, nullptr,
kICCIntersliceDelay,
nsITimer::TYPE_REPEATING_SLACK,
"ICCTimerFired");
}
} else if (sCCTimer) {
static uint32_t sCount = 0;
static const uint32_t kTicksPerForgetSkippableDelay =
NS_CC_SKIPPABLE_DELAY / 16;
if (++sCount % kTicksPerForgetSkippableDelay != 0) {
// The comment above about triggering CC slice applies to forget skippable
// too.
return;
}
sCCTimer->Cancel();
CCTimerFired(nullptr, nullptr);
if (sCCTimer) {
sCCTimer->InitWithNamedFuncCallback(CCTimerFired, nullptr,
NS_CC_SKIPPABLE_DELAY,
nsITimer::TYPE_REPEATING_SLACK,
"CCTimerFired");
}
}
}
nsScriptNameSpaceManager*

View File

@ -16,7 +16,7 @@ def generateLine(propName, extendedAttrs):
return " [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs),
propName)
def generate(output, idlFilename, preprocessorHeader):
cpp = shellutil.split(buildconfig.substs['CPP'])
cpp = list(buildconfig.substs['CPP'])
cpp += shellutil.split(buildconfig.substs['ACDEFINES'])
cpp.append(preprocessorHeader)
preprocessed = subprocess.check_output(cpp)

View File

@ -1908,6 +1908,8 @@ CanvasRenderingContext2D::ReturnTarget(bool aForceReset)
// balance out here. See the comment in RestoreClipsAndTransformToTarget.
mTarget->PopClip();
}
mTarget->SetTransform(Matrix());
}
mBufferProvider->ReturnDrawTarget(mTarget.forget());

View File

@ -61,7 +61,6 @@ support-files =
support-files = pointerevent_pointerleave_mouse-manual.html
[test_pointerevent_pointerleave_pen-manual.html]
support-files = pointerevent_pointerleave_pen-manual.html
disabled = should be investigated
[test_pointerevent_pointerleave_touch-manual.html]
support-files = pointerevent_pointerleave_touch-manual.html
[test_pointerevent_pointermove-manual.html]
@ -80,7 +79,6 @@ support-files =
support-files = pointerevent_pointerout_after_pointerup_nohover-manual.html
[test_pointerevent_pointerout_pen-manual.html]
support-files = pointerevent_pointerout_pen-manual.html
disabled = should be investigated
[test_pointerevent_pointerout_received_once-manual.html]
support-files = pointerevent_pointerout_received_once-manual.html
[test_pointerevent_pointerover-manual.html]

View File

@ -42,26 +42,6 @@ function completion_function() {
}
}
// Helper function to send PointerEvent with different parameters
function sendPointerEvent(int_win, elemId, pointerEventType, inputSource, params) {
var elem = int_win.document.getElementById(elemId);
if(!!elem) {
var rect = elem.getBoundingClientRect();
var eventObj = {type: pointerEventType, inputSource: inputSource};
if(params && "button" in params)
eventObj.button = params.button;
if(params && "isPrimary" in params)
eventObj.isPrimary = params.isPrimary;
else if(MouseEvent.MOZ_SOURCE_MOUSE == inputSource)
eventObj.isPrimary = true;
console.log(elemId, eventObj);
var salt = ("pointermove" == pointerEventType) ? 1 : 2;
synthesizePointer(elem, rect.width*salt/5, rect.height/2, eventObj, int_win);
} else {
is(!!elem, true, "Document should have element with id: " + elemId);
}
}
// Helper function to send MouseEvent with different parameters
function sendMouseEvent(int_win, elemId, mouseEventType, params) {
var elem = int_win.document.getElementById(elemId);

View File

@ -18,13 +18,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
iframe.src = "pointerevent_capture_mouse-manual.html";
}
function executeTest(int_win) {
sendPointerEvent(int_win, "btnCapture", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "btnCapture", "pointerdown", MouseEvent.MOZ_SOURCE_MOUSE, {button:1});
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "target1", "pointerup", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendMouseEvent(int_win, "btnCapture", "mousemove");
sendMouseEvent(int_win, "target0", "mousemove");
sendMouseEvent(int_win, "target1", "mousemove");
sendMouseEvent(int_win, "btnCapture", "mousedown", {button:1});
sendMouseEvent(int_win, "target1", "mousemove");
sendMouseEvent(int_win, "target1", "mouseup");
sendMouseEvent(int_win, "target1", "mousemove");
}
</script>
</head>

View File

@ -18,14 +18,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
iframe.src = "pointerevent_capture_suppressing_mouse-manual.html";
}
function executeTest(int_win) {
sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "btnCapture", "pointerdown", MouseEvent.MOZ_SOURCE_MOUSE, {button:1});
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "target1", "pointerup", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendMouseEvent(int_win, "target0", "mousemove");
sendMouseEvent(int_win, "target1", "mousemove");
sendMouseEvent(int_win, "btnCapture", "mousedown", {button:1});
sendMouseEvent(int_win, "target1", "mousemove");
sendMouseEvent(int_win, "target0", "mousemove");
sendMouseEvent(int_win, "target1", "mousemove");
sendMouseEvent(int_win, "target1", "mouseup");
sendMouseEvent(int_win, "target1", "mousemove");
}
</script>
</head>

View File

@ -18,8 +18,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
iframe.src = "pointerevent_pointerleave_descendants-manual.html";
}
function executeTest(int_win) {
sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "log", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendMouseEvent(int_win, "target0", "mousemove");
sendMouseEvent(int_win, "log", "mousemove");
}
</script>
</head>

View File

@ -18,9 +18,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
iframe.src = "pointerevent_pointerleave_pen-manual.html";
}
function executeTest(int_win) {
sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
sendMouseEvent(int_win, "target0", "mouseleave", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
sendMouseEvent(int_win, "target0", "mousecancel", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
}
</script>
</head>

View File

@ -18,9 +18,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
iframe.src = "pointerevent_pointerout_pen-manual.html";
}
function executeTest(int_win) {
sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
sendMouseEvent(int_win, "target0", "mouseout", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
sendMouseEvent(int_win, "target0", "mousecancel", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
}
</script>
</head>

View File

@ -18,8 +18,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
iframe.src = "pointerevent_releasepointercapture_onpointerup_mouse-manual.html";
}
function executeTest(int_win) {
sendPointerEvent(int_win, "btnCapture", "pointerdown", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "btnCapture", "pointerup", MouseEvent.MOZ_SOURCE_MOUSE);
sendMouseEvent(int_win, "btnCapture", "mousedown");
sendMouseEvent(int_win, "btnCapture", "mouseup");
}
</script>
</head>

View File

@ -18,9 +18,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
iframe.src = "pointerevent_setpointercapture_inactive_button_mouse-manual.html";
}
function executeTest(int_win) {
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
sendMouseEvent(int_win, "target1", "mousemove");
sendMouseEvent(int_win, "target0", "mousemove");
sendMouseEvent(int_win, "target1", "mousemove");
}
</script>
</head>

View File

@ -1676,10 +1676,9 @@ nsGenericHTMLElement::GetURIAttr(nsIAtom* aAttr, nsIAtom* aBaseAttr, nsIURI** aU
/* static */ bool
nsGenericHTMLElement::IsScrollGrabAllowed(JSContext*, JSObject*)
{
// Only allow scroll grabbing in chrome and certified apps.
// Only allow scroll grabbing in chrome
nsIPrincipal* prin = nsContentUtils::SubjectPrincipal();
return nsContentUtils::IsSystemPrincipal(prin) ||
prin->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
return nsContentUtils::IsSystemPrincipal(prin);
}
nsresult

View File

@ -280,8 +280,8 @@ interface nsIDOMWindowUtils : nsISupports {
const long MODIFIER_OS = 0x1000;
/** Synthesize a mouse event. The event types supported are:
* mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu,
* MozMouseHittest
* mousedown, mouseup, mousemove, mouseover, mouseout, mousecancel,
* contextmenu, MozMouseHittest
*
* Events are sent in coordinates offset by aX and aY from the window.
*
@ -301,6 +301,11 @@ interface nsIDOMWindowUtils : nsISupports {
* window under the toplevel window, in some cases it could never reach this
* window at all.
*
* NOTE: mousecancel is used to represent the vanishing of an input device
* such as a pen leaving its digitizer by synthesizing a WidgetMouseEvent,
* whose mMessage is eMouseExitFromWidget and mExitFrom is
* WidgetMouseEvent::eTopLevel.
*
* @param aType event type
* @param aX x offset in CSS pixels
* @param aY y offset in CSS pixels

View File

@ -69,8 +69,7 @@ partial interface KeyframeEffectReadOnly {
optional (unrestricted double or KeyframeEffectOptions) options)]
interface KeyframeEffect : KeyframeEffectReadOnly {
inherit attribute (Element or CSSPseudoElement)? target;
// Bug 1216843 - implement animation composition
// inherit attribute IterationCompositeOperation iterationComposite;
inherit attribute IterationCompositeOperation iterationComposite;
// Bug 1216844 - implement additive animation
// inherit attribute CompositeOperation composite;
[SetterThrows]

View File

@ -73,7 +73,7 @@ interface XMLHttpRequest : XMLHttpRequestEventTarget {
void open(ByteString method, DOMString url);
[Throws]
void open(ByteString method, DOMString url, boolean async,
optional DOMString? user, optional DOMString? password);
optional DOMString? user=null, optional DOMString? password=null);
[Throws]
void setRequestHeader(ByteString header, ByteString value);

View File

@ -53,9 +53,8 @@ public:
Open(const nsACString& aMethod, const nsAString& aUrl, ErrorResult& aRv) = 0;
virtual void
Open(const nsACString& aMethod, const nsAString& aUrl,
bool aAsync, const Optional<nsAString>& aUser,
const Optional<nsAString>& aPassword, ErrorResult& aRv) = 0;
Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync,
const nsAString& aUser, const nsAString& aPassword, ErrorResult& aRv) = 0;
virtual void
SetRequestHeader(const nsACString& aHeader, const nsACString& aValue,

View File

@ -1400,7 +1400,7 @@ XMLHttpRequestMainThread::Open(const nsACString& aMethod, const nsACString& aUrl
if (optional_argc > 2) {
password = &aPassword;
}
return OpenInternal(aMethod, aUrl, async, username, password);
return Open(aMethod, aUrl, async, username, password);
}
// This case is hit when the async parameter is outright omitted, which
@ -1409,8 +1409,8 @@ void
XMLHttpRequestMainThread::Open(const nsACString& aMethod, const nsAString& aUrl,
ErrorResult& aRv)
{
aRv = OpenInternal(aMethod, NS_ConvertUTF16toUTF8(aUrl), Optional<bool>(true),
Optional<nsAString>(), Optional<nsAString>());
aRv = Open(aMethod, NS_ConvertUTF16toUTF8(aUrl), Optional<bool>(true),
Optional<nsAString>(), Optional<nsAString>());
}
// This case is hit when the async parameter is specified, even if the
@ -1420,20 +1420,24 @@ void
XMLHttpRequestMainThread::Open(const nsACString& aMethod,
const nsAString& aUrl,
bool aAsync,
const Optional<nsAString>& aUsername,
const Optional<nsAString>& aPassword,
const nsAString& aUsername,
const nsAString& aPassword,
ErrorResult& aRv)
{
aRv = OpenInternal(aMethod, NS_ConvertUTF16toUTF8(aUrl),
Optional<bool>(aAsync), aUsername, aPassword);
Optional<nsAString> username;
username = &aUsername;
Optional<nsAString> password;
password = &aPassword;
aRv = Open(aMethod, NS_ConvertUTF16toUTF8(aUrl),
Optional<bool>(aAsync), username, password);
}
nsresult
XMLHttpRequestMainThread::OpenInternal(const nsACString& aMethod,
const nsACString& aUrl,
const Optional<bool>& aAsync,
const Optional<nsAString>& aUsername,
const Optional<nsAString>& aPassword)
XMLHttpRequestMainThread::Open(const nsACString& aMethod,
const nsACString& aUrl,
const Optional<bool>& aAsync,
const Optional<nsAString>& aUsername,
const Optional<nsAString>& aPassword)
{
bool async = aAsync.WasPassed() ? aAsync.Value() : true;

View File

@ -249,10 +249,16 @@ public:
virtual void
Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync,
const Optional<nsAString>& aUser,
const Optional<nsAString>& aPassword,
const nsAString& aUsername, const nsAString& aPassword,
ErrorResult& aRv) override;
nsresult
Open(const nsACString& aMethod,
const nsACString& aUrl,
const Optional<bool>& aAsync,
const Optional<nsAString>& aUsername,
const Optional<nsAString>& aPassword);
virtual void
SetRequestHeader(const nsACString& aName, const nsACString& aValue,
ErrorResult& aRv) override
@ -591,12 +597,6 @@ protected:
nsresult OnRedirectVerifyCallback(nsresult result);
nsresult OpenInternal(const nsACString& aMethod,
const nsACString& aUrl,
const Optional<bool>& aAsync,
const Optional<nsAString>& aUsername,
const Optional<nsAString>& aPassword);
already_AddRefed<nsXMLHttpRequestXPCOMifier> EnsureXPCOMifier();
nsCOMPtr<nsISupports> mContext;

View File

@ -1426,16 +1426,15 @@ OpenRunnable::MainThreadRunInternal()
MOZ_ASSERT(!mProxy->mInOpen);
mProxy->mInOpen = true;
ErrorResult rv2;
mProxy->mXHR->Open(mMethod, mURL, true, mUser, mPassword, rv2);
rv = mProxy->mXHR->Open(mMethod, NS_ConvertUTF16toUTF8(mURL),
Optional<bool>(true), mUser, mPassword);
MOZ_ASSERT(mProxy->mInOpen);
mProxy->mInOpen = false;
if (rv2.Failed()) {
return rv2.StealNSResult();
}
NS_ENSURE_SUCCESS(rv, rv);
ErrorResult rv2;
mProxy->mXHR->SetResponseType(mResponseType, rv2);
if (rv2.Failed()) {
return rv2.StealNSResult();
@ -1849,9 +1848,11 @@ XMLHttpRequestWorker::Notify(Status aStatus)
}
void
XMLHttpRequestWorker::Open(const nsACString& aMethod, const nsAString& aUrl,
bool aAsync, const Optional<nsAString>& aUser,
const Optional<nsAString>& aPassword, ErrorResult& aRv)
XMLHttpRequestWorker::Open(const nsACString& aMethod,
const nsAString& aUrl, bool aAsync,
const Optional<nsAString>& aUser,
const Optional<nsAString>& aPassword,
ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();

View File

@ -97,8 +97,20 @@ public:
virtual void
Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync,
const Optional<nsAString>& aUser, const Optional<nsAString>& aPassword,
ErrorResult& aRv) override;
const nsAString& aUsername, const nsAString& aPassword,
ErrorResult& aRv) override
{
Optional<nsAString> username;
username = &aUsername;
Optional<nsAString> password;
password = &aPassword;
Open(aMethod, aUrl, aAsync, username, password, aRv);
}
void
Open(const nsACString& aMethod, const nsAString& aUrl,
bool aAsync, const Optional<nsAString>& aUser,
const Optional<nsAString>& aPassword, ErrorResult& aRv);
virtual void
SetRequestHeader(const nsACString& aHeader, const nsACString& aValue,

View File

@ -16,6 +16,7 @@
#include "mozilla/WidgetUtils.h" // for ComputeTransformForRotation
#include "mozilla/dom/KeyframeEffectReadOnly.h"
#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for dom::FillMode
#include "mozilla/dom/KeyframeEffectBinding.h" // for dom::IterationComposite
#include "mozilla/gfx/BaseRect.h" // for BaseRect
#include "mozilla/gfx/Point.h" // for RoundedToInt, PointTyped
#include "mozilla/gfx/Rect.h" // for RoundedToInt, RectTyped
@ -553,18 +554,45 @@ AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aTransformedSubtreeRoo
}
static void
SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
StyleAnimationValue& aEnd, Animatable* aValue, Layer* aLayer)
SampleValue(float aPortion, Animation& aAnimation,
const StyleAnimationValue& aStart, const StyleAnimationValue& aEnd,
const StyleAnimationValue& aLastValue, uint64_t aCurrentIteration,
Animatable* aValue, Layer* aLayer)
{
StyleAnimationValue interpolatedValue;
NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
aStart.GetUnit() == StyleAnimationValue::eUnit_None ||
aEnd.GetUnit() == StyleAnimationValue::eUnit_None,
"Must have same unit");
StyleAnimationValue startValue = aStart;
StyleAnimationValue endValue = aEnd;
// Iteration composition for accumulate
if (static_cast<dom::IterationCompositeOperation>
(aAnimation.iterationComposite()) ==
dom::IterationCompositeOperation::Accumulate &&
aCurrentIteration > 0) {
// FIXME: Bug 1293492: Add a utility function to calculate both of
// below StyleAnimationValues.
DebugOnly<bool> accumulateResult =
StyleAnimationValue::Accumulate(aAnimation.property(),
startValue,
aLastValue,
aCurrentIteration);
MOZ_ASSERT(accumulateResult, "could not accumulate value");
accumulateResult =
StyleAnimationValue::Accumulate(aAnimation.property(),
endValue,
aLastValue,
aCurrentIteration);
MOZ_ASSERT(accumulateResult, "could not accumulate value");
}
StyleAnimationValue interpolatedValue;
// This should never fail because we only pass transform and opacity values
// to the compositor and they should never fail to interpolate.
DebugOnly<bool> uncomputeResult =
StyleAnimationValue::Interpolate(aAnimation.property(), aStart, aEnd,
StyleAnimationValue::Interpolate(aAnimation.property(),
startValue, endValue,
aPortion, interpolatedValue);
MOZ_ASSERT(uncomputeResult, "could not uncompute value");
@ -683,8 +711,12 @@ SampleAnimations(Layer* aLayer, TimeStamp aPoint)
// interpolate the property
Animatable interpolatedValue;
SampleValue(portion, animation, animData.mStartValues[segmentIndex],
animData.mEndValues[segmentIndex], &interpolatedValue, layer);
SampleValue(portion, animation,
animData.mStartValues[segmentIndex],
animData.mEndValues[segmentIndex],
animData.mEndValues.LastElement(),
computedTiming.mCurrentIteration,
&interpolatedValue, layer);
LayerComposite* layerComposite = layer->AsLayerComposite();
switch (animation.property()) {
case eCSSProperty_opacity:

View File

@ -204,6 +204,7 @@ struct Animation {
float playbackRate;
// This is used in the transformed progress calculation.
TimingFunction easingFunction;
uint8_t iterationComposite;
};
// Change a layer's attributes

View File

@ -35,6 +35,16 @@ namespace detail {
/*****************************************************************************/
// The "generation" of a hash table is an opaque value indicating the state of
// modification of the hash table through its lifetime. If the generation of
// a hash table compares equal at times T1 and T2, then lookups in the hash
// table, pointers to (or into) hash table entries, etc. at time T1 are valid
// at time T2. If the generation compares unequal, these computations are all
// invalid and must be performed again to be used.
//
// Generations are meaningfully comparable only with respect to a single hash
// table. It's always nonsensical to compare the generation of distinct hash
// tables H1 and H2.
using Generation = mozilla::Opaque<uint64_t>;
// A JS-friendly, STL-like container providing a hash-based map from keys to
@ -210,8 +220,6 @@ class HashMap
return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
}
// If |generation()| is the same before and after a HashMap operation,
// pointers into the table remain valid.
Generation generation() const {
return impl.generation();
}
@ -451,8 +459,6 @@ class HashSet
return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
}
// If |generation()| is the same before and after a HashSet operation,
// pointers into the table remain valid.
Generation generation() const {
return impl.generation();
}

View File

@ -219,6 +219,14 @@ class JS_FRIEND_API(BaseProxyHandler)
return true;
}
virtual bool canNurseryAllocate() const {
/*
* Nursery allocation is allowed if and only if it is safe to not
* run |finalize| when the ProxyObject dies.
*/
return false;
}
/* Policy enforcement methods.
*
* enter() allows the policy to specify whether the caller may perform |act|

View File

@ -1721,6 +1721,9 @@ struct GCPolicy<JS::Value>
static void trace(JSTracer* trc, Value* v, const char* name) {
js::UnsafeTraceManuallyBarrieredEdge(trc, v, name);
}
static bool isTenured(const Value& thing) {
return !thing.isGCThing() || !IsInsideNursery(thing.toGCThing());
}
};
} // namespace JS

View File

@ -6966,20 +6966,15 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand);
*line = tokenStream.srcCoords.lineNum(tokenStream.currentToken().pos.end);
RootedPropertyName name(m.cx());
TokenKind tk;
if (!tokenStream.getToken(&tk, TokenStream::Operand))
return false;
if (tk == TOK_NAME) {
name = tokenStream.currentName();
} else if (tk == TOK_YIELD) {
if (!m.parser().checkYieldNameValidity())
return false;
name = m.cx()->names().yield;
} else {
if (tk != TOK_NAME && tk != TOK_YIELD)
return false; // The regular parser will throw a SyntaxError, no need to m.fail.
}
RootedPropertyName name(m.cx(), m.parser().bindingIdentifier(YieldIsName));
if (!name)
return false;
ParseNode* fn = m.parser().handler.newFunctionDefinition();
if (!fn)

View File

@ -231,7 +231,6 @@ CodeSegment::create(JSContext* cx,
cs->interruptCode_ = codeBase + linkData.interruptOffset;
cs->outOfBoundsCode_ = codeBase + linkData.outOfBoundsOffset;
cs->unalignedAccessCode_ = codeBase + linkData.unalignedAccessOffset;
cs->badIndirectCallCode_ = codeBase + linkData.badIndirectCallOffset;
{
JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));

View File

@ -54,7 +54,6 @@ class CodeSegment
// These are pointers into code for stubs used for asynchronous
// signal-handler control-flow transfer.
uint8_t* interruptCode_;
uint8_t* badIndirectCallCode_;
uint8_t* outOfBoundsCode_;
uint8_t* unalignedAccessCode_;
@ -84,7 +83,6 @@ class CodeSegment
uint32_t totalLength() const { return codeLength_ + globalDataLength_; }
uint8_t* interruptCode() const { return interruptCode_; }
uint8_t* badIndirectCallCode() const { return badIndirectCallCode_; }
uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; }
uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; }

View File

@ -385,12 +385,12 @@ wasm::GenerateFunctionPrologue(MacroAssembler& masm, unsigned framePushed, const
Register scratch = WasmTableCallScratchReg;
masm.loadWasmGlobalPtr(sigId.globalDataOffset(), scratch);
masm.branch32(Assembler::Condition::NotEqual, WasmTableCallSigReg, scratch,
JumpTarget::BadIndirectCall);
JumpTarget::IndirectCallBadSig);
break;
}
case SigIdDesc::Kind::Immediate:
masm.branch32(Assembler::Condition::NotEqual, WasmTableCallSigReg, Imm32(sigId.immediate()),
JumpTarget::BadIndirectCall);
JumpTarget::IndirectCallBadSig);
break;
case SigIdDesc::Kind::None:
break;

View File

@ -508,7 +508,6 @@ ModuleGenerator::finishCodegen()
linkData_.interruptOffset = interruptExit.begin;
linkData_.outOfBoundsOffset = jumpTargets[JumpTarget::OutOfBounds].begin;
linkData_.unalignedAccessOffset = jumpTargets[JumpTarget::UnalignedAccess].begin;
linkData_.badIndirectCallOffset = jumpTargets[JumpTarget::BadIndirectCall].begin;
// Only call convertOutOfRangeBranchesToThunks after all other codegen that may
// emit new jumps to JumpTargets has finished.

View File

@ -561,6 +561,12 @@ ReadCustomDoubleNaNObject(JSContext* cx, HandleValue v, double* ret)
return true;
}
WasmInstanceObject*
Instance::objectUnbarriered() const
{
return object_.unbarrieredGet();
}
WasmInstanceObject*
Instance::object() const
{
@ -843,7 +849,7 @@ Instance::ensureProfilingState(JSContext* cx, bool newProfilingEnabled)
}
for (const SharedTable& table : tables_) {
if (!table->isTypedFunction() || !table->initialized())
if (!table->isTypedFunction())
continue;
// This logic will have to be generalized to match the import logic
@ -853,8 +859,10 @@ Instance::ensureProfilingState(JSContext* cx, bool newProfilingEnabled)
void** array = table->internalArray();
uint32_t length = table->length();
for (size_t i = 0; i < length; i++)
UpdateEntry(*code_, newProfilingEnabled, &array[i]);
for (size_t i = 0; i < length; i++) {
if (array[i])
UpdateEntry(*code_, newProfilingEnabled, &array[i]);
}
}
return true;

View File

@ -69,6 +69,10 @@ class Instance
friend class js::WasmMemoryObject;
void onMovingGrow(uint8_t* prevMemoryBase);
// Called by WasmTableObject to barrier table writes.
friend class Table;
WasmInstanceObject* objectUnbarriered() const;
public:
Instance(JSContext* cx,
HandleWasmInstanceObject object,

View File

@ -534,6 +534,8 @@ WasmInstanceObject::create(JSContext* cx,
obj->setReservedSlot(EXPORTS_SLOT, PrivateValue(exports.release()));
MOZ_ASSERT(obj->isNewborn());
MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
// Root the Instance via WasmInstanceObject before any possible GC.
auto* instance = cx->new_<Instance>(cx,
obj,
@ -1200,24 +1202,19 @@ WasmTableObject::getImpl(JSContext* cx, const CallArgs& args)
uint32_t index = uint32_t(indexDbl);
MOZ_ASSERT(double(index) == indexDbl);
if (!table.initialized()) {
args.rval().setNull();
return true;
}
ExternalTableElem& elem = table.externalArray()[index];
Instance& instance = *elem.tls->instance;
const CodeRange* codeRange = instance.code().lookupRange(elem.code);
// A non-function code range means the bad-indirect-call stub, so a null element.
if (!codeRange || !codeRange->isFunction()) {
if (!elem.code) {
args.rval().setNull();
return true;
}
Instance& instance = *elem.tls->instance;
const CodeRange& codeRange = *instance.code().lookupRange(elem.code);
MOZ_ASSERT(codeRange.isFunction());
RootedWasmInstanceObject instanceObj(cx, instance.object());
RootedFunction fun(cx);
if (!instanceObj->getExportedFunction(cx, instanceObj, codeRange->funcDefIndex(), &fun))
if (!instanceObj->getExportedFunction(cx, instanceObj, codeRange.funcDefIndex(), &fun))
return false;
args.rval().setObject(*fun);
@ -1258,15 +1255,6 @@ WasmTableObject::setImpl(JSContext* cx, const CallArgs& args)
return false;
}
if (!table.initialized()) {
if (!value) {
args.rval().setUndefined();
return true;
}
table.init(ExportedFunctionToInstance(value));
}
if (value) {
RootedWasmInstanceObject instanceObj(cx, ExportedFunctionToInstanceObject(value));
uint32_t funcDefIndex = ExportedFunctionToDefinitionIndex(value);

View File

@ -462,13 +462,6 @@ Module::initSegments(JSContext* cx,
MOZ_ASSERT(dataSegments_.empty());
}
// Ensure all tables are initialized before storing into them.
for (const SharedTable& table : tables) {
if (!table->initialized())
table->init(instance);
}
// Now that initialization can't fail partway through, write data/elem
// segments into memories/tables.

View File

@ -51,7 +51,6 @@ struct LinkDataCacheablePod
uint32_t interruptOffset;
uint32_t outOfBoundsOffset;
uint32_t unalignedAccessOffset;
uint32_t badIndirectCallOffset;
LinkDataCacheablePod() { mozilla::PodZero(this); }
};

View File

@ -916,7 +916,8 @@ wasm::GenerateJumpTarget(MacroAssembler& masm, JumpTarget target)
return GenerateStackOverflow(masm);
case JumpTarget::Throw:
return GenerateThrow(masm);
case JumpTarget::BadIndirectCall:
case JumpTarget::IndirectCallToNull:
case JumpTarget::IndirectCallBadSig:
case JumpTarget::OutOfBounds:
case JumpTarget::UnalignedAccess:
case JumpTarget::Unreachable:

View File

@ -48,32 +48,10 @@ Table::create(JSContext* cx, const TableDesc& desc, HandleWasmTableObject maybeO
table->array_.reset((uint8_t*)array);
table->kind_ = desc.kind;
table->length_ = desc.initial;
table->initialized_ = false;
table->external_ = desc.external;
return table;
}
void
Table::init(Instance& instance)
{
MOZ_ASSERT(!initialized());
initialized_ = true;
void* code = instance.codeSegment().badIndirectCallCode();
if (external_) {
ExternalTableElem* array = externalArray();
TlsData* tls = &instance.tlsData();
for (uint32_t i = 0; i < length_; i++) {
array[i].code = code;
array[i].tls = tls;
}
} else {
void** array = internalArray();
for (uint32_t i = 0; i < length_; i++)
array[i] = code;
}
}
void
Table::tracePrivate(JSTracer* trc)
{
@ -87,12 +65,15 @@ Table::tracePrivate(JSTracer* trc)
TraceEdge(trc, &maybeObject_, "wasm table object");
}
if (!initialized_ || !external_)
return;
ExternalTableElem* array = externalArray();
for (uint32_t i = 0; i < length_; i++)
array[i].tls->instance->trace(trc);
if (external_) {
ExternalTableElem* array = externalArray();
for (uint32_t i = 0; i < length_; i++) {
if (array[i].tls)
array[i].tls->instance->trace(trc);
else
MOZ_ASSERT(!array[i].code);
}
}
}
void
@ -112,7 +93,6 @@ Table::trace(JSTracer* trc)
void**
Table::internalArray() const
{
MOZ_ASSERT(initialized_);
MOZ_ASSERT(!external_);
return (void**)array_.get();
}
@ -120,7 +100,6 @@ Table::internalArray() const
ExternalTableElem*
Table::externalArray() const
{
MOZ_ASSERT(initialized_);
MOZ_ASSERT(external_);
return (ExternalTableElem*)array_.get();
}
@ -130,8 +109,13 @@ Table::set(uint32_t index, void* code, Instance& instance)
{
if (external_) {
ExternalTableElem& elem = externalArray()[index];
if (elem.tls)
JSObject::writeBarrierPre(elem.tls->instance->objectUnbarriered());
elem.code = code;
elem.tls = &instance.tlsData();
MOZ_ASSERT(elem.tls->instance->objectUnbarriered()->isTenured(), "no writeBarrierPost");
} else {
internalArray()[index] = code;
}
@ -142,7 +126,11 @@ Table::setNull(uint32_t index)
{
// Only external tables can set elements to null after initialization.
ExternalTableElem& elem = externalArray()[index];
elem.code = elem.tls->instance->codeSegment().badIndirectCallCode();
if (elem.tls)
JSObject::writeBarrierPre(elem.tls->instance->objectUnbarriered());
elem.code = nullptr;
elem.tls = nullptr;
}
size_t

View File

@ -36,7 +36,6 @@ class Table : public ShareableBase<Table>
UniqueByteArray array_;
TableKind kind_;
uint32_t length_;
bool initialized_;
bool external_;
void tracePrivate(JSTracer* trc);
@ -47,21 +46,14 @@ class Table : public ShareableBase<Table>
HandleWasmTableObject maybeObject);
void trace(JSTracer* trc);
// These accessors may be used before initialization.
bool external() const { return external_; }
bool isTypedFunction() const { return kind_ == TableKind::TypedFunction; }
uint32_t length() const { return length_; }
uint8_t* base() const { return array_.get(); }
// A Table must be initialized before any dependent instance can execute.
bool initialized() const { return initialized_; }
void init(Instance& instance);
// After initialization, elements must be accessed. All updates must go
// through a set() function with the exception of (profiling) updates to the
// callee pointer that do not change which logical function is being called.
// All updates must go through a set() function with the exception of
// (profiling) updates to the callee pointer that do not change which
// logical function is being called.
void** internalArray() const;
ExternalTableElem* externalArray() const;

View File

@ -117,8 +117,11 @@ HandleTrap(int32_t trapIndex)
case Trap::IntegerDivideByZero:
errorNumber = JSMSG_WASM_INT_DIVIDE_BY_ZERO;
break;
case Trap::BadIndirectCall:
errorNumber = JSMSG_WASM_BAD_IND_CALL;
case Trap::IndirectCallToNull:
errorNumber = JSMSG_WASM_IND_CALL_TO_NULL;
break;
case Trap::IndirectCallBadSig:
errorNumber = JSMSG_WASM_IND_CALL_BAD_SIG;
break;
case Trap::ImpreciseSimdConversion:
errorNumber = JSMSG_SIMD_FAILED_CONVERSION;

View File

@ -875,8 +875,10 @@ enum class Trap
OutOfBounds,
// Unaligned memory access.
UnalignedAccess,
// Bad signature for an indirect call.
BadIndirectCall,
// call_indirect to null.
IndirectCallToNull,
// call_indirect signature mismatch.
IndirectCallBadSig,
// (asm.js only) SIMD float to int conversion failed because the input
// wasn't in bounds.
@ -899,7 +901,8 @@ enum class JumpTarget
IntegerDivideByZero = unsigned(Trap::IntegerDivideByZero),
OutOfBounds = unsigned(Trap::OutOfBounds),
UnalignedAccess = unsigned(Trap::UnalignedAccess),
BadIndirectCall = unsigned(Trap::BadIndirectCall),
IndirectCallToNull = unsigned(Trap::IndirectCallToNull),
IndirectCallBadSig = unsigned(Trap::IndirectCallBadSig),
ImpreciseSimdConversion = unsigned(Trap::ImpreciseSimdConversion),
// Non-traps
StackOverflow,

View File

@ -717,6 +717,8 @@ PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /
// Steps 3-7.
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx, usedProto, wrappedProto));
if (!promise)
return nullptr;
RootedValue promiseVal(cx, ObjectValue(*promise));
if (wrappedProto && !cx->compartment()->wrap(cx, &promiseVal))

View File

@ -24,8 +24,8 @@
using namespace js;
using namespace js::unicode;
using mozilla::CheckedInt;
using mozilla::ArrayLength;
using mozilla::CheckedInt;
using mozilla::Maybe;
/*
@ -187,8 +187,18 @@ enum RegExpSharedUse {
/*
* ES 2016 draft Mar 25, 2016 21.2.3.2.2.
* Because this function only ever returns |obj| in the spec, provided by the
* user, we omit it and just return the usual success/failure.
*
* Steps 14-15 set |obj|'s "lastIndex" property to zero. Some of
* RegExpInitialize's callers have a fresh RegExp not yet exposed to script:
* in these cases zeroing "lastIndex" is infallible. But others have a RegExp
* whose "lastIndex" property might have been made non-writable: here, zeroing
* "lastIndex" can fail. We efficiently solve this problem by completely
* removing "lastIndex" zeroing from the provided function.
*
* CALLERS MUST HANDLE "lastIndex" ZEROING THEMSELVES!
*
* Because this function only ever returns a user-provided |obj| in the spec,
* we omit it and just return the usual success/failure.
*/
static bool
RegExpInitializeIgnoringLastIndex(JSContext* cx, Handle<RegExpObject*> obj,
@ -347,6 +357,10 @@ regexp_compile_impl(JSContext* cx, const CallArgs& args)
return false;
}
// The final niggling bit of step 5.
//
// |regexp| is user-exposed, but if its "lastIndex" property hasn't been
// made non-writable, we can still use a fast path to zero it.
if (regexp->lookupPure(cx->names().lastIndex)->writable()) {
regexp->zeroLastIndex(cx);
} else {

76
js/src/devtools/octane-csv.sh Executable file
View File

@ -0,0 +1,76 @@
#!/usr/bin/env bash
set -e -o pipefail
function echo_to_stderr {
echo "$1" 1>&2
}
function usage_and_exit {
echo_to_stderr "Usage:"
echo_to_stderr " $0 <path-to-js> <number-of-iterations>"
echo_to_stderr
echo_to_stderr "Run octane <number-of-iterations> times, and aggregate the results"
echo_to_stderr "into one CSV file, which is written to stdout."
echo_to_stderr
echo_to_stderr "See the js/src/devtools/plot-octane.R script for plotting the"
echo_to_stderr "results."
echo_to_stderr
echo_to_stderr "Complete example usage with plotting:"
echo_to_stderr
echo_to_stderr " \$ ./js/src/devtools/octane-csv.sh path/to/js 20 > control.csv"
echo_to_stderr
echo_to_stderr " Next, apply some patch you'd like to test."
echo_to_stderr
echo_to_stderr " \$ ./js/src/devtools/octane-csv.sh path/to/js 20 > variable.csv"
echo_to_stderr " \$ ./js/src/devtools/plot-octane.R control.csv variable.csv"
echo_to_stderr
echo_to_stderr " Open Rplots.pdf to view the results."
exit 1
}
if [[ "$#" != "2" ]]; then
usage_and_exit
fi
# Get the absolute, normalized $JS path, and ensure its an executable.
JS_DIR=$(dirname $1)
if [[ ! -d "$JS_DIR" ]]; then
echo_to_stderr "error: no such directory $JS_DIR"
echo_to_stderr
usage_and_exit
fi
JS=$(basename $1)
cd "$JS_DIR" > /dev/null
JS="$(pwd)/$JS"
if [[ ! -e "$JS" ]]; then
echo_to_stderr "error: '$JS' is not executable"
echo_to_stderr
usage_and_exit
fi
cd - > /dev/null
# Go to the js/src/octane directory.
cd $(dirname $0)/../octane > /dev/null
# Run octane and transform the results into CSV.
#
# Run once as a warm up, and to grab the column headers. Then run the benchmark
# $ITERS times, grabbing just the data rows.
echo_to_stderr "Warm up"
"$JS" ./run.js | grep -v -- "----" | cut -f 1 -d ':' | tr '\n' ','
echo
ITERS=$2
while [[ "$ITERS" -ge "1" ]]; do
echo_to_stderr "Iterations left: $ITERS"
"$JS" ./run.js | grep -v -- "----" | cut -f 2 -d ':' | tr '\n' ','
echo
ITERS=$((ITERS - 1))
done
echo_to_stderr "All done :)"

38
js/src/devtools/plot-octane.R Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env Rscript
# Usage:
#
# octane.R control.csv variable.csv
#
# Output will be placed in Rplots.pdf
#
# Remember: on Octane, higher is better!
library(ggplot2)
args <- commandArgs(trailingOnly = TRUE)
# Reading in data.
control <- read.table(args[1], sep=",", header=TRUE)
variable <- read.table(args[2], sep=",", header=TRUE)
# Pulling out columns that we want to plot.
# Not totally necessary.
ctrl <- control$Score..version.9.
var <- variable$Score..version.9.
# Concatenating the values we want to plot.
score <- c(ctrl, var)
# Creating a vector of labels for the data points.
label <- c(rep("control", length(ctrl)), rep("variable", length(var)))
# Creating a data frame of the score and label.
data <- data.frame(label, score)
# Now plotting!
ggplot(data, aes(label, score, color=label, pch=label)) +
# Adding boxplot without the outliers.
geom_boxplot(outlier.shape=NA) +
# Adding jitter plot on top of the boxplot. If you want to spread the points
# more, increase jitter.
geom_jitter(position=position_jitter(width=0.05))

View File

@ -845,7 +845,12 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName* name, TokenPos pos)
if (!pc->sc()->needStrictChecks())
return true;
if (name == context->names().eval || name == context->names().arguments || IsKeyword(name)) {
if (name == context->names().eval ||
name == context->names().arguments ||
name == context->names().let ||
name == context->names().static_ ||
IsKeyword(name))
{
JSAutoByteString bytes;
if (!AtomToPrintableString(context, name, &bytes))
return false;
@ -2639,13 +2644,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
break;
}
case TOK_YIELD:
if (!checkYieldNameValidity())
return false;
MOZ_ASSERT(yieldHandling == YieldIsName);
goto TOK_NAME;
case TOK_TRIPLEDOT: {
case TOK_TRIPLEDOT:
if (IsSetterKind(kind)) {
report(ParseError, false, null(),
JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
@ -2655,18 +2654,6 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
hasRest = true;
funbox->function()->setHasRest();
if (!tokenStream.getToken(&tt))
return false;
// FIXME: This fails to handle a rest parameter named |yield|
// correctly outside of generators: that is,
// |var f = (...yield) => 42;| should be valid code!
// When this is fixed, make sure to consult both
// |yieldHandling| and |checkYieldNameValidity| for
// correctness until legacy generator syntax is removed.
if (tt != TOK_NAME) {
report(ParseError, false, null(), JSMSG_NO_REST_NAME);
return false;
}
disallowDuplicateParams = true;
if (duplicatedParam) {
// Has duplicated args before the rest parameter.
@ -2674,15 +2661,23 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
return false;
}
goto TOK_NAME;
}
if (!tokenStream.getToken(&tt))
return false;
if (tt != TOK_NAME && tt != TOK_YIELD) {
report(ParseError, false, null(), JSMSG_NO_REST_NAME);
return false;
}
MOZ_FALLTHROUGH;
TOK_NAME:
case TOK_NAME: {
case TOK_NAME:
case TOK_YIELD: {
if (parenFreeArrow)
funbox->setStart(tokenStream);
RootedPropertyName name(context, tokenStream.currentName());
RootedPropertyName name(context, bindingIdentifier(yieldHandling));
if (!name)
return false;
if (!notePositionalFormalParameter(funcpn, name, disallowDuplicateParams,
&duplicatedParam))
{
@ -3341,19 +3336,6 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
return true;
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::checkYieldNameValidity()
{
// In star generators and in JS >= 1.7, yield is a keyword. Otherwise in
// strict mode, yield is a future reserved word.
if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7 || pc->sc()->strict()) {
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
return false;
}
return true;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling)
@ -3387,12 +3369,10 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
return null();
}
if (tt == TOK_NAME) {
name = tokenStream.currentName();
} else if (tt == TOK_YIELD) {
if (!checkYieldNameValidity())
if (tt == TOK_NAME || tt == TOK_YIELD) {
name = bindingIdentifier(yieldHandling);
if (!name)
return null();
name = tokenStream.currentName();
} else if (defaultHandling == AllowDefaultName) {
name = context->names().starDefaultStar;
tokenStream.ungetToken();
@ -3436,12 +3416,10 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked)
}
RootedPropertyName name(context);
if (tt == TOK_NAME) {
name = tokenStream.currentName();
} else if (tt == TOK_YIELD) {
if (!checkYieldNameValidity())
if (tt == TOK_NAME || tt == TOK_YIELD) {
name = bindingIdentifier(YieldIsName);
if (!name)
return null();
name = tokenStream.currentName();
} else {
tokenStream.ungetToken();
}
@ -3688,19 +3666,12 @@ Parser<ParseHandler>::matchLabel(YieldHandling yieldHandling, MutableHandle<Prop
if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
return false;
if (tt == TOK_NAME) {
tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
MOZ_ASSERT_IF(tokenStream.currentName() == context->names().yield,
yieldHandling == YieldIsName);
label.set(tokenStream.currentName());
} else if (tt == TOK_YIELD) {
// We might still consider |yield| to be valid here, contrary to ES6.
// Fix bug 1104014, then stop shipping legacy generators in chrome
// code, then remove this check!
tokenStream.consumeKnownToken(TOK_YIELD, TokenStream::Operand);
if (!checkYieldNameValidity())
if (tt == TOK_NAME || tt == TOK_YIELD) {
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
label.set(labelIdentifier(yieldHandling));
if (!label)
return false;
label.set(tokenStream.currentName());
} else {
label.set(nullptr);
}
@ -4200,19 +4171,16 @@ Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, Token
bool initialDeclaration, YieldHandling yieldHandling,
ParseNodeKind* forHeadKind, Node* forInOrOfExpression)
{
if (tt != TOK_NAME) {
// Anything other than TOK_YIELD or TOK_NAME is an error.
if (tt != TOK_YIELD) {
report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
return null();
}
// TOK_YIELD is only okay if it's treated as a name.
if (!checkYieldNameValidity())
return null();
// Anything other than TOK_YIELD or TOK_NAME is an error.
if (tt != TOK_NAME && tt != TOK_YIELD) {
report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
return null();
}
RootedPropertyName name(context, tokenStream.currentName());
RootedPropertyName name(context, bindingIdentifier(yieldHandling));
if (!name)
return null();
Node binding = newName(name);
if (!binding)
return null();
@ -4385,38 +4353,58 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor
// peekToken matched it as a TOK_NAME, and put it in the
// lookahead buffer, so this call will match keywords as well.
MUST_MATCH_TOKEN_MOD(TOK_NAME, TokenStream::KeywordIsName, JSMSG_NO_IMPORT_NAME);
Node importName = newName(tokenStream.currentName());
if (!importName)
return false;
Rooted<PropertyName*> importName(context, tokenStream.currentName());
TokenPos importNamePos = pos();
bool foundAs;
if (!tokenStream.matchContextualKeyword(&foundAs, context->names().as))
return false;
TokenKind maybeAs;
if (!tokenStream.peekToken(&maybeAs))
return null();
if (foundAs) {
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
if (maybeAs == TOK_NAME &&
tokenStream.nextName() == context->names().as)
{
tokenStream.consumeKnownToken(TOK_NAME);
if (!checkUnescapedName())
return false;
TokenKind afterAs;
if (!tokenStream.getToken(&afterAs))
return false;
if (afterAs != TOK_NAME && afterAs != TOK_YIELD) {
report(ParseError, false, null(), JSMSG_NO_BINDING_NAME);
return false;
}
} else {
// Keywords cannot be bound to themselves, so an import name
// that is a keyword is a syntax error if it is not followed
// by the keyword 'as'.
// See the ImportSpecifier production in ES6 section 15.2.2.
if (IsKeyword(importName->name())) {
if (IsKeyword(importName)) {
JSAutoByteString bytes;
if (!AtomToPrintableString(context, importName->name(), &bytes))
if (!AtomToPrintableString(context, importName, &bytes))
return false;
report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr());
return false;
}
}
RootedPropertyName bindingAtom(context, tokenStream.currentName());
RootedPropertyName bindingAtom(context, importedBinding());
if (!bindingAtom)
return false;
Node bindingName = newName(bindingAtom);
if (!bindingName)
return false;
if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos()))
return false;
Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
Node importNameNode = newName(importName, importNamePos);
if (!importNameNode)
return false;
Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importNameNode, bindingName);
if (!importSpec)
return false;
@ -4456,7 +4444,9 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor
// definitions that hold a module namespace object. They are treated
// as const variables which are initialized during the
// ModuleDeclarationInstantiation step.
RootedPropertyName bindingName(context, tokenStream.currentName());
RootedPropertyName bindingName(context, importedBinding());
if (!bindingName)
return false;
Node bindingNameNode = newName(bindingName);
if (!bindingNameNode)
return false;
@ -4515,10 +4505,14 @@ Parser<FullParseHandler>::importDeclaration()
if (!importName)
return null();
RootedPropertyName bindingAtom(context, tokenStream.currentName());
RootedPropertyName bindingAtom(context, importedBinding());
if (!bindingAtom)
return null();
Node bindingName = newName(bindingAtom);
if (!bindingName)
return null();
if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos()))
return null();
@ -4870,15 +4864,28 @@ Parser<FullParseHandler>::exportDeclaration()
return node;
}
case TOK_LET:
case TOK_CONST:
kid = lexicalDeclaration(YieldIsName, tt == TOK_CONST);
kid = lexicalDeclaration(YieldIsName, /* isConst = */ true);
if (!kid)
return null();
if (!checkExportedNamesForDeclaration(kid))
return null();
break;
case TOK_NAME:
if (tokenStream.currentName() == context->names().let) {
if (!checkUnescapedName())
return null();
kid = lexicalDeclaration(YieldIsName, /* isConst = */ false);
if (!kid)
return null();
if (!checkExportedNamesForDeclaration(kid))
return null();
break;
}
MOZ_FALLTHROUGH;
default:
report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT);
return null();
@ -5124,16 +5131,15 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
// For-in loop backwards compatibility requires that |let| starting a
// for-loop that's not a (new to ES6) for-of loop, in non-strict mode code,
// parse as an identifier. (|let| in for-of is always a declaration.)
// Thus we must can't just sniff out TOK_CONST/TOK_LET here. :-(
bool parsingLexicalDeclaration = false;
bool letIsIdentifier = false;
if (tt == TOK_LET || tt == TOK_CONST) {
if (tt == TOK_CONST) {
parsingLexicalDeclaration = true;
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
} else if (tt == TOK_NAME && tokenStream.nextName() == context->names().let) {
MOZ_ASSERT(!pc->sc()->strict(),
"should parse |let| as TOK_LET in strict mode code");
} else if (tt == TOK_NAME &&
tokenStream.nextName() == context->names().let &&
!tokenStream.nextNameContainsEscape())
{
// 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
@ -5875,8 +5881,9 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling)
{
uint32_t begin = pos().begin;
RootedPropertyName label(context, tokenStream.currentName());
RootedPropertyName label(context, labelIdentifier(yieldHandling));
if (!label)
return null();
auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) {
return stmt->label() == label;
@ -5887,6 +5894,8 @@ Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling)
return null();
}
uint32_t begin = pos().begin;
tokenStream.consumeKnownToken(TOK_COLON);
/* Push a label struct and parse the statement. */
@ -6021,19 +6030,12 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
if (!catchName)
return null();
break;
case TOK_YIELD:
if (yieldHandling == YieldIsKeyword) {
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
return null();
}
// Even if yield is *not* necessarily a keyword, we still must
// check its validity for legacy generators.
if (!checkYieldNameValidity())
case TOK_NAME:
case TOK_YIELD: {
RootedPropertyName param(context, bindingIdentifier(yieldHandling));
if (!param)
return null();
MOZ_FALLTHROUGH;
case TOK_NAME: {
RootedPropertyName param(context, tokenStream.currentName());
catchName = newName(param);
if (!catchName)
return null();
@ -6233,13 +6235,10 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
return null();
RootedPropertyName name(context);
if (tt == TOK_NAME) {
name = tokenStream.currentName();
} else if (tt == TOK_YIELD) {
if (!checkYieldNameValidity())
if (tt == TOK_NAME || tt == TOK_YIELD) {
name = bindingIdentifier(yieldHandling);
if (!name)
return null();
MOZ_ASSERT(yieldHandling != YieldIsKeyword);
name = tokenStream.currentName();
} else if (classContext == ClassStatement) {
if (defaultHandling == AllowDefaultName) {
name = context->names().starDefaultStar;
@ -6308,6 +6307,13 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
bool isStatic = false;
if (tt == TOK_NAME && tokenStream.currentName() == context->names().static_) {
MOZ_ASSERT(pc->sc()->strict(), "classes are always strict");
// Strict mode forbids "class" as Identifier, so it can only be the
// unescaped keyword.
if (!checkUnescapedName())
return null();
if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
return null();
if (tt == TOK_RC) {
@ -6438,10 +6444,9 @@ template <class ParseHandler>
bool
Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME),
"TOK_LET should have been summarily considered a "
"LexicalDeclaration");
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
MOZ_ASSERT(tokenStream.currentName() == context->names().let);
MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape());
#ifdef DEBUG
TokenKind verify;
@ -6455,6 +6460,9 @@ Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHand
// Otherwise a let declaration must have a name.
if (next == TOK_NAME) {
MOZ_ASSERT(tokenStream.nextName() != context->names().yield,
"token stream should interpret 'yield' as TOK_YIELD");
// One non-"yield" TOK_NAME edge case deserves special comment.
// Consider this:
//
@ -6466,22 +6474,21 @@ Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHand
// 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;
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;
// If we have the name "yield", the grammar parameter exactly states
// whether this is okay. (This wasn't true for SpiderMonkey's ancient
// legacy generator syntax, but that's dead now.) If YieldIsName,
// declaration-parsing code will (if necessary) enforce a strict mode
// restriction on defining "yield". If YieldIsKeyword, consider this the
// end of the declaration, in case ASI induces a semicolon that makes the
// "yield" valid.
if (next == TOK_YIELD)
return yieldHandling == YieldIsName;
// Otherwise not a let declaration.
return false;
}
template <typename ParseHandler>
@ -6535,11 +6542,10 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
TokenKind next;
if (!tokenStream.peekToken(&next, modifier))
return null();
if (next == TOK_COLON) {
if (!checkYieldNameValidity())
return null();
if (next == TOK_COLON)
return labeledStatement(yieldHandling);
}
return expressionStatement(yieldHandling);
}
@ -6548,25 +6554,21 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
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) &&
// |let| here can only be an Identifier, not a declaration. Give nicer
// errors for declaration-looking typos.
if (!tokenStream.currentToken().nameContainsEscape() &&
tokenStream.currentName() == context->names().let)
{
bool forbiddenLetDeclaration = false;
if (next == TOK_LB) {
// ExpressionStatement has a 'let [' lookahead restriction.
if (pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) {
// |let| can't be an Identifier in strict mode code. Ditto for
// non-standard JavaScript 1.7+.
forbiddenLetDeclaration = true;
} else {
} else if (next == TOK_LB) {
// Enforce ExpressionStatement's 'let [' lookahead restriction.
forbiddenLetDeclaration = true;
} else if (next == TOK_LC || next == TOK_NAME) {
// '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.
@ -6694,14 +6696,6 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
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.
}
}
@ -6757,11 +6751,10 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
TokenKind next;
if (!tokenStream.peekToken(&next, modifier))
return null();
if (next == TOK_COLON) {
if (!checkYieldNameValidity())
return null();
if (next == TOK_COLON)
return labeledStatement(yieldHandling);
}
return expressionStatement(yieldHandling);
}
@ -6770,23 +6763,11 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
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 (!tokenStream.currentToken().nameContainsEscape() &&
tokenStream.currentName() == context->names().let &&
nextTokenContinuesLetDeclaration(next, yieldHandling))
{
return lexicalDeclaration(yieldHandling, /* isConst = */ false);
}
if (next == TOK_COLON)
@ -6872,11 +6853,10 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
// LexicalDeclaration[In, ?Yield]
// LetOrConst BindingList[?In, ?Yield]
case TOK_LET:
case TOK_CONST:
// [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);
return lexicalDeclaration(yieldHandling, /* isConst = */ true);
// ImportDeclaration (only inside modules)
case TOK_IMPORT:
@ -7248,8 +7228,13 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (tt == TOK_NAME) {
if (!tokenStream.nextTokenEndsExpr(&endsExpr))
return null();
if (endsExpr)
return identifierName(yieldHandling);
if (endsExpr) {
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
if (!name)
return null();
return identifierReference(name);
}
}
if (tt == TOK_NUMBER) {
@ -8258,18 +8243,99 @@ Parser<ParseHandler>::newName(PropertyName* name, TokenPos pos)
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::identifierName(YieldHandling yieldHandling)
PropertyName*
Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling)
{
RootedPropertyName name(context, tokenStream.currentName());
if (yieldHandling == YieldIsKeyword && name == context->names().yield) {
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
return null();
PropertyName* ident;
const Token& tok = tokenStream.currentToken();
if (tok.type == TOK_NAME) {
ident = tok.name();
MOZ_ASSERT(ident != context->names().yield,
"tokenizer should have treated 'yield' as TOK_YIELD");
if (pc->sc()->strict()) {
const char* badName = ident == context->names().let
? "let"
: ident == context->names().static_
? "static"
: nullptr;
if (badName) {
report(ParseError, false, null(), JSMSG_RESERVED_ID, badName);
return nullptr;
}
}
} else {
MOZ_ASSERT(tok.type == TOK_YIELD);
if (yieldHandling == YieldIsKeyword ||
pc->sc()->strict() ||
pc->isStarGenerator() ||
versionNumber() >= JSVERSION_1_7)
{
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
return nullptr;
}
ident = context->names().yield;
}
// If we're inside a function that later becomes a legacy generator, then
// a |yield| identifier name here will be detected by a subsequent
// |checkYieldNameValidity| call.
return ident;
}
template <typename ParseHandler>
PropertyName*
Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling)
{
PropertyName* ident;
const Token& tok = tokenStream.currentToken();
if (tok.type == TOK_NAME) {
ident = tok.name();
MOZ_ASSERT(ident != context->names().yield,
"tokenizer should have treated 'yield' as TOK_YIELD");
if (pc->sc()->strict()) {
const char* badName = ident == context->names().arguments
? "arguments"
: ident == context->names().eval
? "eval"
: nullptr;
if (badName) {
report(ParseError, false, null(), JSMSG_BAD_STRICT_ASSIGN, badName);
return nullptr;
}
badName = ident == context->names().let
? "let"
: ident == context->names().static_
? "static"
: nullptr;
if (badName) {
report(ParseError, false, null(), JSMSG_RESERVED_ID, badName);
return nullptr;
}
}
} else {
MOZ_ASSERT(tok.type == TOK_YIELD);
if (yieldHandling == YieldIsKeyword ||
pc->sc()->strict() ||
pc->isStarGenerator() ||
versionNumber() >= JSVERSION_1_7)
{
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
return nullptr;
}
ident = context->names().yield;
}
return ident;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::identifierReference(Handle<PropertyName*> name)
{
Node pn = newName(name);
if (!pn)
return null();
@ -8670,7 +8736,11 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
if (!tokenStream.checkForKeyword(propAtom, nullptr))
return null();
Node nameExpr = identifierName(yieldHandling);
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
if (!name)
return null();
Node nameExpr = identifierReference(name);
if (!nameExpr)
return null();
@ -8684,7 +8754,11 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
if (!tokenStream.checkForKeyword(propAtom, nullptr))
return null();
Node lhs = identifierName(yieldHandling);
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
if (!name)
return null();
Node lhs = identifierReference(name);
if (!lhs)
return null();
@ -8693,7 +8767,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
Node rhs;
{
// Clearing `inDestructuringDecl` allows name use to be noted
// in `identifierName`. See bug 1255167.
// in Parser::identifierReference. See bug 1255167.
AutoClearInDestructuringDecl autoClear(pc);
rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
if (!rhs)
@ -8900,11 +8974,13 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
return stringLiteral();
case TOK_YIELD:
if (!checkYieldNameValidity())
case TOK_NAME: {
Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
if (!name)
return null();
MOZ_FALLTHROUGH;
case TOK_NAME:
return identifierName(yieldHandling);
return identifierReference(name);
}
case TOK_REGEXP:
return newRegExp();
@ -8946,12 +9022,11 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
TokenKind next;
if (!tokenStream.getToken(&next))
return null();
// FIXME: This fails to handle a rest parameter named |yield| correctly
// outside of generators: |var f = (...yield) => 42;| should be
// valid code! When this is fixed, make sure to consult both
// |yieldHandling| and |checkYieldNameValidity| for correctness
// until legacy generator syntax is removed.
if (next != TOK_NAME) {
// This doesn't check that the provided name is allowed, e.g. if the
// enclosing code is strict mode code, any of "arguments", "let", or
// "yield" should be prohibited. Argument-parsing code handles that.
if (next != TOK_NAME && next != TOK_YIELD) {
report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
"rest argument name", TokenKindToDesc(next));
return null();

View File

@ -981,11 +981,15 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
// Determine whether |yield| is a valid name in the current context, or
// whether it's prohibited due to strictness, JS version, or occurrence
// inside a star generator.
bool checkYieldNameValidity();
bool yieldExpressionsSupported() {
return versionNumber() >= JSVERSION_1_7 || pc->isGenerator();
}
// Match the current token against the BindingIdentifier production with
// the given Yield parameter. If there is no match, report a syntax
// error.
PropertyName* bindingIdentifier(YieldHandling yieldHandling);
virtual bool strictMode() { return pc->sc()->strict(); }
bool setLocalStrictMode(bool strict) {
MOZ_ASSERT(tokenStream.debugHasNoLookahead());
@ -1206,7 +1210,21 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
Node classDefinition(YieldHandling yieldHandling, ClassContext classContext,
DefaultHandling defaultHandling);
Node identifierName(YieldHandling yieldHandling);
PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling);
PropertyName* labelIdentifier(YieldHandling yieldHandling) {
return labelOrIdentifierReference(yieldHandling);
}
PropertyName* identifierReference(YieldHandling yieldHandling) {
return labelOrIdentifierReference(yieldHandling);
}
PropertyName* importedBinding() {
return bindingIdentifier(YieldIsName);
}
Node identifierReference(Handle<PropertyName*> name);
bool matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label);

View File

@ -107,7 +107,6 @@
macro(THROW, "keyword 'throw'") \
macro(DEBUGGER, "keyword 'debugger'") \
macro(YIELD, "keyword 'yield'") \
macro(LET, "keyword 'let'") \
macro(EXPORT, "keyword 'export'") \
macro(IMPORT, "keyword 'import'") \
macro(CLASS, "keyword 'class'") \
@ -239,12 +238,6 @@ TokenKindIsAssignment(TokenKind tt)
return TOK_ASSIGNMENT_START <= tt && tt <= TOK_ASSIGNMENT_LAST;
}
inline bool
TokenKindIsDecl(TokenKind tt)
{
return tt == TOK_VAR || tt == TOK_LET;
}
} // namespace frontend
} // namespace js

View File

@ -981,11 +981,6 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
if (kw->tokentype == TOK_STRICT_RESERVED)
return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars);
// Treat 'let' as an identifier and contextually a keyword in sloppy mode.
// It is always a keyword in strict mode.
if (kw->tokentype == TOK_LET && !strictMode())
return true;
// Working keyword.
if (ttp) {
*ttp = kw->tokentype;

View File

@ -351,6 +351,13 @@ class MOZ_STACK_CLASS TokenStream
return nextToken().name();
}
bool nextNameContainsEscape() const {
if (nextToken().type == TOK_YIELD)
return false;
MOZ_ASSERT(nextToken().type == TOK_NAME);
return nextToken().nameContainsEscape();
}
bool isCurrentTokenAssignment() const {
return TokenKindIsAssignment(currentToken().type);
}

View File

@ -2529,11 +2529,14 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind
tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
} else if (src->is<ArgumentsObject>()) {
tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
} else if (src->is<ProxyObject>()) {
tenuredSize += ProxyObject::objectMovedDuringMinorGC(this, dst, src);
} else if (JSObjectMovedOp op = dst->getClass()->extObjectMovedOp()) {
op(dst, src);
} else if (src->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE) {
// Objects with JSCLASS_SKIP_NURSERY_FINALIZE need to be handled above
// to ensure any additional nursery buffers they hold are moved.
} else if (src->getClass()->hasFinalize()) {
// Such objects need to be handled specially above to ensure any
// additional nursery buffers they hold are moved.
MOZ_RELEASE_ASSERT(CanNurseryAllocateFinalizedClass(src->getClass()));
MOZ_CRASH("Unhandled JSCLASS_SKIP_NURSERY_FINALIZE Class");
}
@ -2804,30 +2807,6 @@ FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTION
} /* namespace gc */
} /* namespace js */
/*** Type Marking *********************************************************************************/
void
TypeSet::MarkTypeRoot(JSTracer* trc, TypeSet::Type* v, const char* name)
{
AssertRootMarkingPhase(trc);
MarkTypeUnbarriered(trc, v, name);
}
void
TypeSet::MarkTypeUnbarriered(JSTracer* trc, TypeSet::Type* v, const char* name)
{
if (v->isSingletonUnchecked()) {
JSObject* obj = v->singletonNoBarrier();
DispatchToTracer(trc, &obj, name);
*v = TypeSet::ObjectType(obj);
} else if (v->isGroupUnchecked()) {
ObjectGroup* group = v->groupNoBarrier();
DispatchToTracer(trc, &group, name);
*v = TypeSet::ObjectType(group);
}
}
/*** Cycle Collector Barrier Implementation *******************************************************/

View File

@ -254,13 +254,9 @@ js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const
/* Ensure there's enough space to replace the contents with a RelocationOverlay. */
MOZ_ASSERT(size >= sizeof(RelocationOverlay));
/*
* Classes with JSCLASS_SKIP_NURSERY_FINALIZE will not have their finalizer
* called if they are nursery allocated and not promoted to the tenured
* heap. The finalizers for these classes must do nothing except free data
* which was allocated via Nursery::allocateBuffer.
*/
MOZ_ASSERT_IF(clasp->hasFinalize(), clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE);
/* Sanity check the finalizer. */
MOZ_ASSERT_IF(clasp->hasFinalize(), CanNurseryAllocateFinalizedClass(clasp) ||
clasp->isProxy());
/* Make the object allocation. */
JSObject* obj = static_cast<JSObject*>(allocate(size));
@ -270,7 +266,7 @@ js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const
/* If we want external slots, add them. */
HeapSlot* slots = nullptr;
if (numDynamic) {
MOZ_ASSERT(clasp->isNative());
MOZ_ASSERT(clasp->isNative() || clasp->isProxy());
slots = static_cast<HeapSlot*>(allocateBuffer(cx->zone(), numDynamic * sizeof(HeapSlot)));
if (!slots) {
/*
@ -700,7 +696,7 @@ js::Nursery::doCollection(JSRuntime* rt, JS::gcreason::Reason reason,
// Sweep compartments to update the array buffer object's view lists.
maybeStartProfile(ProfileKey::SweepArrayBufferViewList);
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
c->sweepAfterMinorGC();
c->sweepAfterMinorGC(&mover);
maybeEndProfile(ProfileKey::SweepArrayBufferViewList);
// Update any slot or element pointers whose destination has been tenured.

View File

@ -112,6 +112,20 @@ class TenuringTracer : public JSTracer
void traceSlots(JS::Value* vp, JS::Value* end);
};
/*
* Classes with JSCLASS_SKIP_NURSERY_FINALIZE or Wrapper classes with
* CROSS_COMPARTMENT flags will not have their finalizer called if they are
* nursery allocated and not promoted to the tenured heap. The finalizers for
* these classes must do nothing except free data which was allocated via
* Nursery::allocateBuffer.
*/
inline bool
CanNurseryAllocateFinalizedClass(const js::Class* const clasp)
{
MOZ_ASSERT(clasp->hasFinalize());
return clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE;
}
class Nursery
{
public:

View File

@ -0,0 +1,178 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
#ifndef gc_NurseryAwareHashMap_h
#define gc_NurseryAwareHashMap_h
namespace js {
namespace detail {
// This class only handles the incremental case and does not deal with nursery
// pointers. The only users should be for NurseryAwareHashMap; it is defined
// externally because we need a GCPolicy for its use in the contained map.
template <typename T>
class UnsafeBareReadBarriered : public ReadBarrieredBase<T>
{
public:
UnsafeBareReadBarriered() : ReadBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
MOZ_IMPLICIT UnsafeBareReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {}
explicit UnsafeBareReadBarriered(const UnsafeBareReadBarriered& v) : ReadBarrieredBase<T>(v) {}
UnsafeBareReadBarriered(UnsafeBareReadBarriered&& v)
: ReadBarrieredBase<T>(mozilla::Forward<UnsafeBareReadBarriered<T>>(v))
{}
UnsafeBareReadBarriered& operator=(const UnsafeBareReadBarriered& v) {
this->value = v.value;
return *this;
}
UnsafeBareReadBarriered& operator=(const T& v) {
this->value = v;
return *this;
}
const T get() const {
if (!InternalBarrierMethods<T>::isMarkable(this->value))
return JS::GCPolicy<T>::initial();
this->read();
return this->value;
}
explicit operator bool() const {
return bool(this->value);
}
const T unbarrieredGet() const { return this->value; }
T* unsafeGet() { return &this->value; }
T const* unsafeGet() const { return &this->value; }
};
} // namespace detail
// The "nursery aware" hash map is a special case of GCHashMap that is able to
// treat nursery allocated members weakly during a minor GC: e.g. it allows for
// nursery allocated objects to be collected during nursery GC where a normal
// hash table treats such edges strongly.
//
// Doing this requires some strong constraints on what can be stored in this
// table and how it can be accessed. At the moment, this table assumes that
// all values contain a strong reference to the key. It also requires the
// policy to contain an |isTenured| and |needsSweep| members, which is fairly
// non-standard. This limits its usefulness to the CrossCompartmentMap at the
// moment, but might serve as a useful base for other tables in future.
template <typename Key,
typename Value,
typename HashPolicy = DefaultHasher<Key>,
typename AllocPolicy = TempAllocPolicy>
class NurseryAwareHashMap
{
using BarrieredValue = detail::UnsafeBareReadBarriered<Value>;
using MapType = GCRekeyableHashMap<Key, BarrieredValue, HashPolicy, AllocPolicy>;
MapType map;
// Keep a list of all keys for which JS::GCPolicy<Key>::isTenured is false.
// This lets us avoid a full traveral of the map on each minor GC, keeping
// the minor GC times proportional to the nursery heap size.
Vector<Key, 0, AllocPolicy> nurseryEntries;
public:
using Lookup = typename MapType::Lookup;
using Ptr = typename MapType::Ptr;
using Range = typename MapType::Range;
explicit NurseryAwareHashMap(AllocPolicy a = AllocPolicy()) : map(a) {}
MOZ_MUST_USE bool init(uint32_t len = 16) { return map.init(len); }
bool empty() const { return map.empty(); }
Ptr lookup(const Lookup& l) const { return map.lookup(l); }
void remove(Ptr p) { map.remove(p); }
Range all() const { return map.all(); }
struct Enum : public MapType::Enum {
explicit Enum(NurseryAwareHashMap& namap) : MapType::Enum(namap.map) {}
};
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
return map.sizeOfExcludingThis(mallocSizeOf);
}
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
return map.sizeOfIncludingThis(mallocSizeOf);
}
MOZ_MUST_USE bool put(const Key& k, const Value& v) {
auto p = map.lookupForAdd(k);
if (p) {
if (!JS::GCPolicy<Key>::isTenured(k) || !JS::GCPolicy<Value>::isTenured(v)) {
if (!nurseryEntries.append(k))
return false;
}
p->value() = v;
return true;
}
bool ok = map.add(p, k, v);
if (!ok)
return false;
if (!JS::GCPolicy<Key>::isTenured(k) || !JS::GCPolicy<Value>::isTenured(v)) {
if (!nurseryEntries.append(k)) {
map.remove(k);
return false;
}
}
return true;
}
void sweepAfterMinorGC(JSTracer* trc) {
for (auto& key : nurseryEntries) {
auto p = map.lookup(key);
if (!p)
continue;
// Drop the entry if the value is not marked.
if (JS::GCPolicy<BarrieredValue>::needsSweep(&p->value())) {
map.remove(key);
continue;
}
// Update and relocate the key, if the value is still needed.
//
// Note that this currently assumes that all Value will contain a
// strong reference to Key, as per its use as the
// CrossCompartmentWrapperMap. We may need to make the following
// behavior more dynamic if we use this map in other nursery-aware
// contexts.
Key copy(key);
mozilla::DebugOnly<bool> sweepKey = JS::GCPolicy<Key>::needsSweep(&copy);
MOZ_ASSERT(!sweepKey);
map.rekeyIfMoved(key, copy);
}
nurseryEntries.clear();
}
void sweep() {
MOZ_ASSERT(nurseryEntries.empty());
map.sweep();
}
};
} // namespace js
namespace JS {
template <typename T>
struct GCPolicy<js::detail::UnsafeBareReadBarriered<T>>
{
static void trace(JSTracer* trc, js::detail::UnsafeBareReadBarriered<T>* thingp,
const char* name)
{
js::TraceEdge(trc, thingp, name);
}
static bool needsSweep(js::detail::UnsafeBareReadBarriered<T>* thingp) {
return js::gc::IsAboutToBeFinalized(thingp);
}
};
} // namespace JS
#endif // gc_NurseryAwareHashMap_h

View File

@ -153,37 +153,102 @@ assertEq(evaled, true);
assertThrowsInstanceOf(() => new Function('var [...rest = defaults] = [];'), SyntaxError);
// bug 1124480: destructuring defaults should work correctly as part of for-in
var defaultsSupportedInForVar = false;
try
{
new Function('for (var [a, b = 10] in " ") {}');
defaultsSupportedInForVar = true;
}
catch (e)
{
}
new Function(`
b = undefined;
for (var [a, b = 10] in " ") {}
assertEq(b, 10);`)();
if (defaultsSupportedInForVar) {
new Function(`
b = undefined;
for (var [a, b = 10] in " ") {}
assertEq(b, 10);
new Function(`
b = undefined;
for (let [a, c = 10] in " ") { b = c; }
assertEq(b, 10);`)();
b = undefined;
for (let [a, c = 10] in " ") { b = c; }
assertEq(b, 10);
new Function(`
b = undefined;
for (let [a, c = (x => y)] in " ") { b = c; }
assertEq(typeof b, "function");`)();
b = undefined;
for (var {1: b = 10} in " ") {}
assertEq(b, 10);
new Function(`
b = undefined;
for (let [a, __proto__ = 10] in " ") { b = __proto__; }
assertEq(b, 10);`)();
b = undefined;
for (let {1: c = 10} in " ") { b = c; }
assertEq(b, 10);
new Function(`
b = undefined;
for (let [a, __proto__ = (x => y)] in " ") { b = __proto__; }
assertEq(typeof b, "function");`)();
b = undefined;
for (let {c = 10} in " ") { b = c; }
assertEq(b, 10);
`)();
}
new Function(`
b = undefined;
for (var {1: b = 10} in " ") {}
assertEq(b, 10);`)();
new Function(`
b = undefined;
for (let {1: c = 10} in " ") { b = c; }
assertEq(b, 10);`)();
new Function(`
b = undefined;
for (let { c = 10 } in " ") { b = c; }
assertEq(b, 10);`)();
new Function(`
b = undefined;
assertEq(Number.prototype.a, undefined);
for (var { a: c = (x => y) } in [{}]) { b = c; }
assertEq(typeof b, "function");`)();
new Function(`
b = undefined;
Object.defineProperty(String.prototype, "__proto__",
{ value: undefined, configurable: true });
for (var { __proto__: c = (x => y) } in [{}]) { b = c; }
delete String.prototype.__proto__;
assertEq(typeof b, "function");`)();
new Function(`
b = undefined;
for (var { a: c = (x => y) } of [{ a: undefined }]) { b = c; }
assertEq(typeof b, "function");`)();
new Function(`
b = undefined;
for (var { __proto__: c = (x => y) } of [{ ["__proto__"]: undefined }]) { b = c; }
assertEq(typeof b, "function");`)();
new Function(`
b = undefined;
var ts = Function.prototype.toString;
Function.prototype.toString = () => 'hi';
String.prototype.hi = 42;
for (var { [(x => y)]: c } in [0]) { b = c; }
Function.prototype.toString = ts;
delete String.prototype.hi;
assertEq(b, 42);`)();
new Function(`
b = undefined;
var ts = Function.prototype.toString;
Function.prototype.toString = () => 'hi';
String.prototype.hi = 42;
for (var { [(x => y)]: __proto__ } in [0]) { b = __proto__; }
Function.prototype.toString = ts;
delete String.prototype.hi;
assertEq(b, 42);`)();
new Function(`
b = undefined;
var ts = Function.prototype.toString;
Function.prototype.toString = () => 'hi';
for (var { [(x => y)]: c } of [{ 'hi': 42 }]) { b = c; }
Function.prototype.toString = ts;
assertEq(b, 42);`)();
new Function(`
b = undefined;
var ts = Function.prototype.toString;
Function.prototype.toString = () => 'hi';
for (var { [(x => y)]: __proto__ } of [{ hi: 42 }]) { b = __proto__; }
Function.prototype.toString = ts;
assertEq(b, 42);`)();

View File

@ -0,0 +1,2 @@
if (typeof oomTest === 'function')
oomTest(Function(`new Promise(res=>res)`));

View File

@ -0,0 +1,21 @@
if (helperThreadCount() === 0)
quit(0);
offThreadCompileScript(`
function foo(x, {}) {
do {
re = /erwe/;
if (x === 1)
re.x = 1;
else
re.x = "a";
assertEq(re.x.length, (x === 1) ? undefined : 1);
} while (!inIon());
}
foo(0, 0);
RegExp.multiline = 1;
foo(1, 0);
`);
runOffThreadScript();

View File

@ -367,28 +367,28 @@ var {v2i, i2i, i2v} = wasmEvalText(`(module
(export "i2v" 8)
)`);
const badIndirectCall = /bad wasm indirect call/;
const signatureMismatch = /indirect call signature mismatch/;
assertEq(v2i(0), 13);
assertEq(v2i(1), 42);
assertErrorMessage(() => v2i(2), Error, badIndirectCall);
assertErrorMessage(() => v2i(3), Error, badIndirectCall);
assertErrorMessage(() => v2i(4), Error, badIndirectCall);
assertErrorMessage(() => v2i(5), Error, badIndirectCall);
assertErrorMessage(() => v2i(2), Error, signatureMismatch);
assertErrorMessage(() => v2i(3), Error, signatureMismatch);
assertErrorMessage(() => v2i(4), Error, signatureMismatch);
assertErrorMessage(() => v2i(5), Error, signatureMismatch);
assertErrorMessage(() => i2i(0), Error, badIndirectCall);
assertErrorMessage(() => i2i(1), Error, badIndirectCall);
assertErrorMessage(() => i2i(0), Error, signatureMismatch);
assertErrorMessage(() => i2i(1), Error, signatureMismatch);
assertEq(i2i(2, 100), 101);
assertEq(i2i(3, 100), 102);
assertEq(i2i(4, 100), 103);
assertEq(i2i(5, 100), 104);
assertErrorMessage(() => i2v(0), Error, badIndirectCall);
assertErrorMessage(() => i2v(1), Error, badIndirectCall);
assertErrorMessage(() => i2v(2), Error, badIndirectCall);
assertErrorMessage(() => i2v(3), Error, badIndirectCall);
assertErrorMessage(() => i2v(4), Error, badIndirectCall);
assertErrorMessage(() => i2v(5), Error, badIndirectCall);
assertErrorMessage(() => i2v(0), Error, signatureMismatch);
assertErrorMessage(() => i2v(1), Error, signatureMismatch);
assertErrorMessage(() => i2v(2), Error, signatureMismatch);
assertErrorMessage(() => i2v(3), Error, signatureMismatch);
assertErrorMessage(() => i2v(4), Error, signatureMismatch);
assertErrorMessage(() => i2v(5), Error, signatureMismatch);
{
enableSPSProfiling();

View File

@ -22,7 +22,7 @@ var e = i.exports;
var t = e.tbl;
var f = t.get(0);
assertEq(f(), e.call(0));
assertErrorMessage(() => e.call(1), Error, /bad wasm indirect call/);
assertErrorMessage(() => e.call(1), Error, /indirect call to null/);
assertErrorMessage(() => e.call(2), Error, /out-of-range/);
assertEq(finalizeCount(), 0);
i.edge = makeFinalizeObserver();
@ -79,8 +79,7 @@ t = null;
gc();
assertEq(finalizeCount(), 4);
// The bad-indirect-call stub should (currently, could be changed later) keep
// the instance containing that stub alive.
// Null elements shouldn't keep anything alive.
resetFinalizeCount();
var i = evalText(`(module (table (resizable 2)) (export "tbl" table) ${caller})`);
var e = i.exports;
@ -96,7 +95,7 @@ gc();
assertEq(finalizeCount(), 1);
i = null;
gc();
assertEq(finalizeCount(), 1);
assertEq(finalizeCount(), 2);
t = null;
gc();
assertEq(finalizeCount(), 3);
@ -144,14 +143,13 @@ assertEq(finalizeCount(), 2);
t.set(0, null);
assertEq(t.get(0), null);
gc();
assertEq(finalizeCount(), 2);
assertEq(finalizeCount(), 3);
t = null;
gc();
assertEq(finalizeCount(), 4);
// Once all of an instance's elements in a Table (including the
// bad-indirect-call stub) have been clobbered, the Instance should not be
// rooted.
// Once all of an instance's elements in a Table have been clobbered, the
// Instance should not be reachable.
resetFinalizeCount();
var i1 = evalText(`(module (func $f1 (result i32) (i32.const 13)) (export "f1" $f1))`);
var i2 = evalText(`(module (func $f2 (result i32) (i32.const 42)) (export "f2" $f2))`);

View File

@ -0,0 +1,24 @@
load(libdir + "wasm.js");
const Module = WebAssembly.Module;
const Instance = WebAssembly.Instance;
const Table = WebAssembly.Table;
var i42 = new Instance(new Module(textToBinary(`(module (func (result i32) (i32.const 42)) (export "f" 0))`)));
var i13 = new Instance(new Module(textToBinary(`(module (func (result i32) (i32.const 13)) (export "f" 0))`)));
var t = new Table({element:"anyfunc", initial:1});
t.set(0, i42.exports.f);
assertEq(t.get(0)(), 42);
verifyprebarriers();
t.set(0, i13.exports.f);
verifyprebarriers();
assertEq(t.get(0)(), 13);
verifyprebarriers();
t.set(0, null);
verifyprebarriers();
assertEq(t.get(0), null);

View File

@ -40,27 +40,27 @@ var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result
var callee = i => `(func $f${i} (type $v2i) (result i32) (i32.const ${i}))`;
var call = evalText(`(module (table (resizable 10)) ${callee(0)} ${caller})`).exports.call;
assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(0), Error, /indirect call to null/);
assertErrorMessage(() => call(10), Error, /out-of-range/);
var call = evalText(`(module (table (resizable 10)) (elem (i32.const 0)) ${callee(0)} ${caller})`).exports.call;
assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(0), Error, /indirect call to null/);
assertErrorMessage(() => call(10), Error, /out-of-range/);
var call = evalText(`(module (table (resizable 10)) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`).exports.call;
assertEq(call(0), 0);
assertErrorMessage(() => call(1), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(2), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(1), Error, /indirect call to null/);
assertErrorMessage(() => call(2), Error, /indirect call to null/);
assertErrorMessage(() => call(10), Error, /out-of-range/);
var call = evalText(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f1) (elem (i32.const 4) $f0 $f2) ${callee(0)} ${callee(1)} ${callee(2)} ${caller})`).exports.call;
assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(0), Error, /indirect call to null/);
assertEq(call(1), 0);
assertEq(call(2), 1);
assertErrorMessage(() => call(3), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(3), Error, /indirect call to null/);
assertEq(call(4), 0);
assertEq(call(5), 2);
assertErrorMessage(() => call(6), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(6), Error, /indirect call to null/);
assertErrorMessage(() => call(10), Error, /out-of-range/);
var tbl = new Table({initial:3, element:"anyfunc"});
@ -69,7 +69,7 @@ assertEq(call(0), 0);
assertEq(call(1), 1);
assertEq(tbl.get(0)(), 0);
assertEq(tbl.get(1)(), 1);
assertErrorMessage(() => call(2), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(2), Error, /indirect call to null/);
assertEq(tbl.get(2), null);
var exp = evalText(`(module (import "a" "b" (table 3)) (export "tbl" table) (elem (i32.const 2) $f2) ${callee(2)} ${caller})`, {a:{b:tbl}}).exports;
@ -90,7 +90,7 @@ assertEq(exp1.tbl.get(1), exp1.f0);
assertEq(exp1.tbl.get(2), null);
assertEq(exp1.call(0), 0);
assertEq(exp1.call(1), 0);
assertErrorMessage(() => exp1.call(2), Error, /bad wasm indirect call/);
assertErrorMessage(() => exp1.call(2), Error, /indirect call to null/);
var exp2 = evalText(`(module (import "a" "b" (table 10)) (export "tbl" table) (elem (i32.const 1) $f1 $f1) ${callee(1)} (export "f1" $f1) ${caller})`, {a:{b:exp1.tbl}}).exports
assertEq(exp1.tbl, exp2.tbl);
assertEq(exp2.tbl.get(0), exp1.f0);
@ -112,7 +112,7 @@ tbl.set(1, e2.g);
tbl.set(2, e3.h);
var e4 = evalText(`(module (import "a" "b" (table 3)) ${caller})`, {a:{b:tbl}}).exports;
assertEq(e4.call(0), 42);
assertErrorMessage(() => e4.call(1), Error, /bad wasm indirect call/);
assertErrorMessage(() => e4.call(1), Error, /indirect call signature mismatch/);
assertEq(e4.call(2), 13);
var m = new Module(textToBinary(`(module
@ -164,7 +164,7 @@ var call = evalText(`(module
)`).exports.call;
assertEq(call(0), 0);
assertEq(call(1), 1);
assertErrorMessage(() => call(2), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(2), Error, /indirect call signature mismatch/);
var call = evalText(`(module
(type $A (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
@ -188,5 +188,5 @@ var call = evalText(`(module
)`).exports.call;
assertEq(call(0), 42);
for (var i = 1; i < 7; i++)
assertErrorMessage(() => call(i), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(i), Error, /indirect call signature mismatch/);
assertErrorMessage(() => call(7), Error, /out-of-range/);

View File

@ -24,6 +24,8 @@
#include "jsscriptinlines.h"
#include "vm/TypeInference-inl.h"
using mozilla::Maybe;
namespace js {
@ -898,7 +900,7 @@ JitcodeGlobalEntry::IonEntry::mark(JSTracer* trc)
iter != optsAllTypes_->end(); iter++)
{
if (ShouldMarkProvider::ShouldMark(&iter->type)) {
TypeSet::MarkTypeUnbarriered(trc, &iter->type, "jitcodeglobaltable-ionentry-type");
iter->type.trace(trc);
markedAny = true;
}
if (iter->hasAllocationSite() && ShouldMarkProvider::ShouldMark(&iter->script)) {

View File

@ -2818,11 +2818,14 @@ MacroAssembler::wasmCallIndirect(const wasm::CallSiteDesc& desc, const wasm::Cal
}
loadPtr(Address(scratch, offsetof(wasm::ExternalTableElem, tls)), WasmTlsReg);
branchTest32(Assembler::Zero, WasmTlsReg, WasmTlsReg, wasm::JumpTarget::IndirectCallToNull);
loadWasmPinnedRegsFromTls();
loadPtr(Address(scratch, offsetof(wasm::ExternalTableElem, code)), scratch);
} else {
loadPtr(BaseIndex(scratch, index, ScalePointer), scratch);
branchTest32(Assembler::Zero, scratch, scratch, wasm::JumpTarget::IndirectCallToNull);
}
call(desc, scratch);

View File

@ -285,6 +285,7 @@ CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic*
Operand srcAddr(ptr, int32_t(mir->base().asValue()) + int32_t(offset));
switch (accessType) {
case Scalar::Int8: masm.movsblWithPatch(srcAddr, out.gpr()); break;
case Scalar::Uint8Clamped:
case Scalar::Uint8: masm.movzblWithPatch(srcAddr, out.gpr()); break;
case Scalar::Int16: masm.movswlWithPatch(srcAddr, out.gpr()); break;
case Scalar::Uint16: masm.movzwlWithPatch(srcAddr, out.gpr()); break;

View File

@ -204,7 +204,7 @@ MSG_DEF(JSMSG_BAD_OCTAL, 1, JSEXN_SYNTAXERR, "{0} is not a legal E
MSG_DEF(JSMSG_BAD_OPERAND, 1, JSEXN_SYNTAXERR, "invalid {0} operand")
MSG_DEF(JSMSG_BAD_PROP_ID, 0, JSEXN_SYNTAXERR, "invalid property id")
MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 1, JSEXN_SYNTAXERR, "{0} not in function")
MSG_DEF(JSMSG_BAD_STRICT_ASSIGN, 1, JSEXN_SYNTAXERR, "can't assign to {0} in strict mode")
MSG_DEF(JSMSG_BAD_STRICT_ASSIGN, 1, JSEXN_SYNTAXERR, "'{0}' can't be defined or assigned to in strict mode code")
MSG_DEF(JSMSG_BAD_SWITCH, 0, JSEXN_SYNTAXERR, "invalid switch statement")
MSG_DEF(JSMSG_BAD_SUPER, 0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'")
MSG_DEF(JSMSG_BAD_SUPERPROP, 1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods")
@ -348,7 +348,8 @@ MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 1, JSEXN_WARN, "Successfully compiled
MSG_DEF(JSMSG_WASM_FAIL, 1, JSEXN_TYPEERR, "wasm error: {0}")
MSG_DEF(JSMSG_WASM_DECODE_FAIL, 2, JSEXN_TYPEERR, "wasm validation error at offset {0}: {1}")
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
MSG_DEF(JSMSG_WASM_BAD_IND_CALL, 0, JSEXN_ERR, "bad wasm indirect call")
MSG_DEF(JSMSG_WASM_IND_CALL_TO_NULL, 0, JSEXN_ERR, "indirect call to null")
MSG_DEF(JSMSG_WASM_IND_CALL_BAD_SIG, 0, JSEXN_ERR, "indirect call signature mismatch")
MSG_DEF(JSMSG_WASM_BAD_GROW, 0, JSEXN_ERR, "failed to grow memory")
MSG_DEF(JSMSG_WASM_BAD_BUF_ARG, 0, JSEXN_TYPEERR, "first argument must be an ArrayBuffer or typed array object")
MSG_DEF(JSMSG_WASM_BAD_MOD_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Module")

View File

@ -6,11 +6,70 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jsapi.h"
#include "jscompartment.h"
#include "js/RootingAPI.h"
#include "js/SliceBudget.h"
#include "jsapi-tests/tests.h"
static bool
ConstructCCW(JSContext* cx, const JSClass* globalClasp,
JS::HandleObject global1, JS::MutableHandleObject wrapper,
JS::MutableHandleObject global2, JS::MutableHandleObject wrappee)
{
if (!global1) {
fprintf(stderr, "null initial global");
return false;
}
// Define a second global in a different zone.
JS::CompartmentOptions options;
global2.set(JS_NewGlobalObject(cx, globalClasp, nullptr,
JS::FireOnNewGlobalHook, options));
if (!global2) {
fprintf(stderr, "failed to create second global");
return false;
}
// This should always be false, regardless.
if (global1->compartment() == global2->compartment()) {
fprintf(stderr, "second global claims to be in global1's compartment");
return false;
}
// This checks that the API obeys the implicit zone request.
if (global1->zone() == global2->zone()) {
fprintf(stderr, "global2 is in global1's zone");
return false;
}
// Define an object in compartment 2, that is wrapped by a CCW into compartment 1.
{
JSAutoCompartment ac(cx, global2);
wrappee.set(JS_NewPlainObject(cx));
if (wrappee->compartment() != global2->compartment()) {
fprintf(stderr, "wrappee in wrong compartment");
return false;
}
}
wrapper.set(wrappee);
if (!JS_WrapObject(cx, wrapper)) {
fprintf(stderr, "failed to wrap");
return false;
}
if (wrappee == wrapper) {
fprintf(stderr, "expected wrapping");
return false;
}
if (wrapper->compartment() != global1->compartment()) {
fprintf(stderr, "wrapper in wrong compartment");
return false;
}
return true;
}
class CCWTestTracer : public JS::CallbackTracer {
void onChild(const JS::GCCellPtr& thing) override {
numberOfThingsTraced++;
@ -42,35 +101,31 @@ class CCWTestTracer : public JS::CallbackTracer {
BEGIN_TEST(testTracingIncomingCCWs)
{
// Get two globals, in two different zones.
#ifdef JS_GC_ZEAL
// Disable zeal modes because this test needs to control exactly when the GC happens.
JS_SetGCZeal(cx, 0, 100);
#endif
JS_GC(cx);
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
CHECK(global1);
JS::CompartmentOptions options;
JS::RootedObject global2(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
JS::FireOnNewGlobalHook, options));
CHECK(global2);
CHECK(global1->compartment() != global2->compartment());
JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
JS_GC(cx);
CHECK(!js::gc::IsInsideNursery(wrappee));
CHECK(!js::gc::IsInsideNursery(wrapper));
// Define an object in one compartment, that is wrapped by a CCW in another
// compartment.
JS::RootedObject obj(cx, JS_NewPlainObject(cx));
CHECK(obj->compartment() == global1->compartment());
JSAutoCompartment ac(cx, global2);
JS::RootedObject wrapper(cx, obj);
CHECK(JS_WrapObject(cx, &wrapper));
JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
CHECK(JS_SetProperty(cx, global2, "ccw", v));
CHECK(JS_SetProperty(cx, global1, "ccw", v));
// Ensure that |TraceIncomingCCWs| finds the object wrapped by the CCW.
JS::CompartmentSet compartments;
CHECK(compartments.init());
CHECK(compartments.put(global1->compartment()));
CHECK(compartments.put(global2->compartment()));
void* thing = obj.get();
void* thing = wrappee.get();
CCWTestTracer trc(cx, &thing, JS::TraceKind::Object);
JS::TraceIncomingCCWs(&trc, compartments);
CHECK(trc.numberOfThingsTraced == 1);
@ -80,6 +135,147 @@ BEGIN_TEST(testTracingIncomingCCWs)
}
END_TEST(testTracingIncomingCCWs)
static size_t
countWrappers(JSCompartment* comp)
{
size_t count = 0;
for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront())
++count;
return count;
}
BEGIN_TEST(testDeadNurseryCCW)
{
#ifdef JS_GC_ZEAL
// Disable zeal modes because this test needs to control exactly when the GC happens.
JS_SetGCZeal(cx, 0, 100);
#endif
JS_GC(cx);
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
CHECK(js::gc::IsInsideNursery(wrappee));
CHECK(js::gc::IsInsideNursery(wrapper));
// Now let the obj and wrapper die.
wrappee = wrapper = nullptr;
// Now a GC should clear the CCW.
CHECK(countWrappers(global1->compartment()) == 1);
cx->gc.evictNursery();
CHECK(countWrappers(global1->compartment()) == 0);
// Check for corruption of the CCW table by doing a full GC to force sweeping.
JS_GC(cx);
return true;
}
END_TEST(testDeadNurseryCCW)
BEGIN_TEST(testLiveNurseryCCW)
{
#ifdef JS_GC_ZEAL
// Disable zeal modes because this test needs to control exactly when the GC happens.
JS_SetGCZeal(cx, 0, 100);
#endif
JS_GC(cx);
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
CHECK(js::gc::IsInsideNursery(wrappee));
CHECK(js::gc::IsInsideNursery(wrapper));
// Now a GC should not kill the CCW.
CHECK(countWrappers(global1->compartment()) == 1);
cx->gc.evictNursery();
CHECK(countWrappers(global1->compartment()) == 1);
CHECK(!js::gc::IsInsideNursery(wrappee));
CHECK(!js::gc::IsInsideNursery(wrapper));
// Check for corruption of the CCW table by doing a full GC to force sweeping.
JS_GC(cx);
return true;
}
END_TEST(testLiveNurseryCCW)
BEGIN_TEST(testLiveNurseryWrapperCCW)
{
#ifdef JS_GC_ZEAL
// Disable zeal modes because this test needs to control exactly when the GC happens.
JS_SetGCZeal(cx, 0, 100);
#endif
JS_GC(cx);
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
CHECK(js::gc::IsInsideNursery(wrappee));
CHECK(js::gc::IsInsideNursery(wrapper));
// The wrapper contains a strong reference to the wrappee, so just dropping
// the reference to the wrappee will not drop the CCW table entry as long
// as the wrapper is held strongly. Thus, the minor collection here must
// tenure both the wrapper and the wrappee and keep both in the table.
wrappee = nullptr;
// Now a GC should not kill the CCW.
CHECK(countWrappers(global1->compartment()) == 1);
cx->gc.evictNursery();
CHECK(countWrappers(global1->compartment()) == 1);
CHECK(!js::gc::IsInsideNursery(wrapper));
// Check for corruption of the CCW table by doing a full GC to force sweeping.
JS_GC(cx);
return true;
}
END_TEST(testLiveNurseryWrapperCCW)
BEGIN_TEST(testLiveNurseryWrappeeCCW)
{
#ifdef JS_GC_ZEAL
// Disable zeal modes because this test needs to control exactly when the GC happens.
JS_SetGCZeal(cx, 0, 100);
#endif
JS_GC(cx);
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
CHECK(js::gc::IsInsideNursery(wrappee));
CHECK(js::gc::IsInsideNursery(wrapper));
// Let the wrapper die. The wrapper should drop from the table when we GC,
// even though there are other non-cross-compartment edges to it.
wrapper = nullptr;
// Now a GC should not kill the CCW.
CHECK(countWrappers(global1->compartment()) == 1);
cx->gc.evictNursery();
CHECK(countWrappers(global1->compartment()) == 0);
CHECK(!js::gc::IsInsideNursery(wrappee));
// Check for corruption of the CCW table by doing a full GC to force sweeping.
JS_GC(cx);
return true;
}
END_TEST(testLiveNurseryWrappeeCCW)
BEGIN_TEST(testIncrementalRoots)
{
JSRuntime* rt = cx->runtime();

View File

@ -191,6 +191,11 @@ JSObject* newCCW(JS::HandleObject sourceZone, JS::HandleObject destZone)
if (!JS_WrapObject(cx, &object))
return nullptr;
}
// In order to test the SCC algorithm, we need the wrapper/wrappee to be
// tenured.
cx->gc.evictNursery();
return object;
}

View File

@ -233,12 +233,6 @@ JSCompartment::checkWrapperMapAfterMovingGC()
}
#endif
namespace {
struct IsInsideNurseryFunctor {
template <class T> bool operator()(T tp) { return IsInsideNursery(*tp); }
};
} // namespace (anonymous)
bool
JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
const js::Value& wrapper)
@ -246,21 +240,7 @@ JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
MOZ_ASSERT(wrapped.is<JSString*>() == wrapper.isString());
MOZ_ASSERT_IF(!wrapped.is<JSString*>(), wrapper.isObject());
/* There's no point allocating wrappers in the nursery since we will tenure them anyway. */
MOZ_ASSERT(!IsInsideNursery(static_cast<gc::Cell*>(wrapper.toGCThing())));
bool isNuseryKey =
const_cast<CrossCompartmentKey&>(wrapped).applyToWrapped(IsInsideNurseryFunctor()) ||
const_cast<CrossCompartmentKey&>(wrapped).applyToDebugger(IsInsideNurseryFunctor());
if (isNuseryKey && !nurseryCCKeys.append(wrapped)) {
ReportOutOfMemory(cx);
return false;
}
if (!crossCompartmentWrappers.put(wrapped, ReadBarriered<Value>(wrapper))) {
if (isNuseryKey)
nurseryCCKeys.popBack();
if (!crossCompartmentWrappers.put(wrapped, wrapper)) {
ReportOutOfMemory(cx);
return false;
}
@ -606,16 +586,6 @@ JSCompartment::trace(JSTracer* trc)
varNames_.trace(trc);
}
struct TraceFunctor {
JSTracer* trc_;
const char* name_;
TraceFunctor(JSTracer *trc, const char* name)
: trc_(trc), name_(name) {}
template <class T> void operator()(T* t) {
TraceManuallyBarrieredEdge(trc_, t, name_);
}
};
void
JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark)
{
@ -687,18 +657,6 @@ JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime t
if (nonSyntacticLexicalEnvironments_)
nonSyntacticLexicalEnvironments_->trace(trc);
// In a minor GC we need to mark nursery objects that are the targets of
// cross compartment wrappers.
if (trc->runtime()->isHeapMinorCollecting()) {
for (auto key : nurseryCCKeys) {
CrossCompartmentKey prior = key;
key.applyToWrapped(TraceFunctor(trc, "ccw wrapped"));
key.applyToDebugger(TraceFunctor(trc, "ccw debugger"));
crossCompartmentWrappers.rekeyIfMoved(prior, key);
}
nurseryCCKeys.clear();
}
wasm.trace(trc);
}
@ -724,12 +682,14 @@ JSCompartment::finishRoots()
}
void
JSCompartment::sweepAfterMinorGC()
JSCompartment::sweepAfterMinorGC(JSTracer* trc)
{
globalWriteBarriered = 0;
if (innerViews.needsSweepAfterMinorGC())
innerViews.sweepAfterMinorGC();
crossCompartmentWrappers.sweepAfterMinorGC(trc);
}
void

View File

@ -17,6 +17,7 @@
#include "asmjs/WasmCompartment.h"
#include "builtin/RegExp.h"
#include "gc/Barrier.h"
#include "gc/NurseryAwareHashMap.h"
#include "gc/Zone.h"
#include "vm/GlobalObject.h"
#include "vm/PIC.h"
@ -68,7 +69,7 @@ class DtoaCache {
#endif
};
struct CrossCompartmentKey
class CrossCompartmentKey
{
public:
enum DebuggerObjectKind : uint8_t { DebuggerSource, DebuggerEnvironment, DebuggerObject,
@ -169,6 +170,17 @@ struct CrossCompartmentKey
return l.wrapped == k.wrapped;
}
};
bool isTenured() const {
struct IsTenuredFunctor {
using ReturnType = bool;
ReturnType operator()(JSObject** tp) { return !IsInsideNursery(*tp); }
ReturnType operator()(JSScript** tp) { return true; }
ReturnType operator()(JSString** tp) { return true; }
};
return const_cast<CrossCompartmentKey*>(this)->applyToWrapped(IsTenuredFunctor());
}
void trace(JSTracer* trc);
bool needsSweep();
@ -177,8 +189,9 @@ struct CrossCompartmentKey
WrappedType wrapped;
};
using WrapperMap = GCRekeyableHashMap<CrossCompartmentKey, ReadBarrieredValue,
CrossCompartmentKey::Hasher, SystemAllocPolicy>;
using WrapperMap = NurseryAwareHashMap<CrossCompartmentKey, JS::Value,
CrossCompartmentKey::Hasher, SystemAllocPolicy>;
// We must ensure that all newly allocated JSObjects get their metadata
// set. However, metadata builders may require the new object be in a sane
@ -617,7 +630,7 @@ struct JSCompartment
/* Whether to preserve JIT code on non-shrinking GCs. */
bool preserveJitCode() { return creationOptions_.preserveJitCode(); }
void sweepAfterMinorGC();
void sweepAfterMinorGC(JSTracer* trc);
void sweepCrossCompartmentWrappers();
void sweepSavedStacks();
@ -1066,4 +1079,11 @@ class MOZ_RAII AutoSuppressAllocationMetadataBuilder {
} /* namespace js */
namespace JS {
template <>
struct GCPolicy<js::CrossCompartmentKey> : public StructGCPolicy<js::CrossCompartmentKey> {
static bool isTenured(const js::CrossCompartmentKey& key) { return key.isTenured(); }
};
} // namespace JS
#endif /* jscompartment_h */

View File

@ -110,7 +110,7 @@ JSCompartment::wrap(JSContext* cx, JS::MutableHandleValue vp)
#ifdef DEBUG
cacheResult = &p->value().get().toObject();
#else
vp.set(p->value());
vp.set(p->value().get());
return true;
#endif
}

View File

@ -3675,7 +3675,7 @@ InCrossCompartmentMap(JSObject* src, JS::GCCellPtr dst)
*/
for (JSCompartment::WrapperEnum e(srccomp); !e.empty(); e.popFront()) {
if (e.front().mutableKey().applyToWrapped(IsDestComparatorFunctor(dst)) &&
ToMarkable(e.front().value()) == src)
ToMarkable(e.front().value().unbarrieredGet()) == src)
{
return true;
}

View File

@ -3691,8 +3691,9 @@ JSObject::allocKindForTenure(const js::Nursery& nursery) const
return GetBackgroundAllocKind(TypedArrayObject::AllocKindForLazyBuffer(nbytes));
}
// Proxies have finalizers and are not nursery allocated.
MOZ_ASSERT(!IsProxy(this));
// Proxies that are CrossCompartmentWrappers may be nursery allocated.
if (IsProxy(this))
return as<ProxyObject>().allocKindForTenure();
// Unboxed plain objects are sized according to the data they store.
if (is<UnboxedPlainObject>()) {

View File

@ -1129,9 +1129,15 @@ namespace js {
inline gc::InitialHeap
GetInitialHeap(NewObjectKind newKind, const Class* clasp)
{
if (newKind == NurseryAllocatedProxy) {
MOZ_ASSERT(clasp->isProxy());
MOZ_ASSERT(clasp->hasFinalize());
MOZ_ASSERT(!CanNurseryAllocateFinalizedClass(clasp));
return gc::DefaultHeap;
}
if (newKind != GenericObject)
return gc::TenuredHeap;
if (clasp->hasFinalize() && !(clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE))
if (clasp->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp))
return gc::TenuredHeap;
return gc::DefaultHeap;
}

View File

@ -350,7 +350,8 @@ JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::Initi
}
MOZ_ASSERT_IF(clasp->hasFinalize(), heap == js::gc::TenuredHeap ||
(flags & JSCLASS_SKIP_NURSERY_FINALIZE));
CanNurseryAllocateFinalizedClass(clasp) ||
clasp->isProxy());
MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(), heap == js::gc::TenuredHeap);
#endif

View File

@ -215,6 +215,9 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
// Allocate CrossCompartmentWrappers in the nursery.
virtual bool canNurseryAllocate() const override { return true; }
static const CrossCompartmentWrapper singleton;
static const CrossCompartmentWrapper singletonWithPrototype;
};

Some files were not shown because too many files have changed in this diff Show More