Merge m-c to autoland. a=merge

This commit is contained in:
Ryan VanderMeulen 2017-05-23 18:13:54 -04:00
commit 4202c4feab
150 changed files with 2473 additions and 1292 deletions

View File

@ -910,17 +910,17 @@
let sizedIconUrl = browser.mIconURL || "";
if (sizedIconUrl != aTab.getAttribute("image")) {
if (sizedIconUrl) {
aTab.setAttribute("image", sizedIconUrl);
if (!browser.mIconLoadingPrincipal ||
!browser.mIconLoadingPrincipal.equals(loadingPrincipal)) {
aTab.setAttribute("iconLoadingPrincipal",
this.serializationHelper.serializeToString(loadingPrincipal));
browser.mIconLoadingPrincipal = loadingPrincipal;
}
aTab.setAttribute("image", sizedIconUrl);
} else {
aTab.removeAttribute("image");
aTab.removeAttribute("iconLoadingPrincipal");
delete browser.mIconLoadingPrincipal;
aTab.removeAttribute("image");
}
this._tabAttrModified(aTab, ["image"]);
}

View File

@ -132,6 +132,8 @@ AboutRedirector::NewChannel(nsIURI* aURI,
nsIChannel** result)
{
NS_ENSURE_ARG_POINTER(aURI);
NS_ENSURE_ARG_POINTER(aLoadInfo);
NS_ASSERTION(result, "must not be null");
nsAutoCString path = GetAboutModuleName(aURI);
@ -172,26 +174,22 @@ AboutRedirector::NewChannel(nsIURI* aURI,
NS_ENSURE_SUCCESS(rv, rv);
// If tempURI links to an external URI (i.e. something other than
// chrome:// or resource://) then set the LOAD_REPLACE flag on the
// channel which forces the channel owner to reflect the displayed
// chrome:// or resource://) then set the result principal URI on the
// load info which forces the channel prncipal to reflect the displayed
// URL rather then being the systemPrincipal.
bool isUIResource = false;
rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
&isUIResource);
NS_ENSURE_SUCCESS(rv, rv);
nsLoadFlags loadFlags = isUIResource
? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
: static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
tempURI,
aLoadInfo,
nullptr, // aLoadGroup
nullptr, // aCallbacks
loadFlags);
aLoadInfo);
NS_ENSURE_SUCCESS(rv, rv);
if (!isUIResource) {
aLoadInfo->SetResultPrincipalURI(tempURI);
}
tempChannel->SetOriginalURI(aURI);
NS_ADDREF(*result = tempChannel);

View File

@ -199,44 +199,48 @@ const CustomizableWidgets = [
for (let i = 0, l = staticButtons.length; i < l; ++i)
CustomizableUI.addShortcut(staticButtons[i]);
PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
.asyncExecuteLegacyQueries([query], 1, options, {
handleResult(aResultSet) {
let onItemCommand = function(aItemCommandEvent) {
// Only handle the click event for middle clicks, we're using the command
// event otherwise.
if (aItemCommandEvent.type == "click" &&
aItemCommandEvent.button != 1) {
return;
}
let item = aItemCommandEvent.target;
win.openUILink(item.getAttribute("targetURI"), aItemCommandEvent);
CustomizableUI.hidePanelForNode(item);
};
let fragment = doc.createDocumentFragment();
let row;
while ((row = aResultSet.getNextRow())) {
let uri = row.getResultByIndex(1);
let title = row.getResultByIndex(2);
aEvent.detail.addBlocker(new Promise((resolve, reject) => {
PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
.asyncExecuteLegacyQueries([query], 1, options, {
handleResult(aResultSet) {
let onItemCommand = function(aItemCommandEvent) {
// Only handle the click event for middle clicks, we're using the command
// event otherwise.
if (aItemCommandEvent.type == "click" &&
aItemCommandEvent.button != 1) {
return;
}
let item = aItemCommandEvent.target;
win.openUILink(item.getAttribute("targetURI"), aItemCommandEvent);
CustomizableUI.hidePanelForNode(item);
};
let fragment = doc.createDocumentFragment();
let row;
while ((row = aResultSet.getNextRow())) {
let uri = row.getResultByIndex(1);
let title = row.getResultByIndex(2);
let item = doc.createElementNS(kNSXUL, "toolbarbutton");
item.setAttribute("label", title || uri);
item.setAttribute("targetURI", uri);
item.setAttribute("class", "subviewbutton");
item.addEventListener("command", onItemCommand);
item.addEventListener("click", onItemCommand);
item.setAttribute("image", "page-icon:" + uri);
fragment.appendChild(item);
}
items.appendChild(fragment);
},
handleError(aError) {
log.debug("History view tried to show but had an error: " + aError);
},
handleCompletion(aReason) {
log.debug("History view is being shown!");
},
});
let item = doc.createElementNS(kNSXUL, "toolbarbutton");
item.setAttribute("label", title || uri);
item.setAttribute("targetURI", uri);
item.setAttribute("class", "subviewbutton");
item.addEventListener("command", onItemCommand);
item.addEventListener("click", onItemCommand);
item.setAttribute("image", "page-icon:" + uri);
fragment.appendChild(item);
}
items.appendChild(fragment);
},
handleError(aError) {
log.debug("History view tried to show but had an error: " + aError);
reject();
},
handleCompletion(aReason) {
log.debug("History view is being shown!");
resolve();
},
});
}));
let recentlyClosedTabs = doc.getElementById("PanelUI-recentlyClosedTabs");
while (recentlyClosedTabs.firstChild) {

View File

@ -37,7 +37,7 @@ let getExtension = () => {
};
add_task(async function testProfilerControl() {
SpecialPowers.pushPrefEnv({
await SpecialPowers.pushPrefEnv({
set: [
[
"extensions.geckoProfiler.symbols.url",

View File

@ -1,20 +0,0 @@
"use strict";
const EDGE_AVAILABLE_MIGRATIONS =
MigrationUtils.resourceTypes.COOKIES |
MigrationUtils.resourceTypes.BOOKMARKS |
MigrationUtils.resourceTypes.HISTORY |
MigrationUtils.resourceTypes.PASSWORDS;
add_task(function* () {
let migrator = MigrationUtils.getMigrator("edge");
Cu.import("resource://gre/modules/AppConstants.jsm");
Assert.equal(!!(migrator && migrator.sourceExists), AppConstants.isPlatformAndVersionAtLeast("win", "10"),
"Edge should be available for migration if and only if we're on Win 10+");
if (migrator) {
let migratableData = migrator.getMigrateData(null, false);
Assert.equal(migratableData, EDGE_AVAILABLE_MIGRATIONS,
"All the data types we expect should be available");
}
});

View File

@ -12,7 +12,6 @@ support-files =
skip-if = os != "mac" # Relies on ULibDir
[test_Chrome_passwords.js]
skip-if = os != "win"
[test_Edge_availability.js]
[test_Edge_db_migration.js]
skip-if = os != "win" || os_version == "5.1" || os_version == "5.2" # Relies on post-XP bits of ESEDB
[test_fx_telemetry.js]

View File

@ -18,6 +18,19 @@ const TEST_STATE = {
}]
};
const TEST_STATE_2 = {
windows: [{
tabs: [
{ entries: [{ url: "about:robots" }]
},
{ entries: [],
userTypedValue: "http://example.com",
userTypedClear: 1
}
]
}]
};
function countNonLazyTabs(win) {
win = win || window;
let count = 0;
@ -82,6 +95,17 @@ add_task(async function test() {
newWindow.close();
});
// Bug 1365933.
info("Check that session with tab having empty entries array gets restored properly");
await promiseBrowserState(TEST_STATE_2);
is(gBrowser.tabs.length, 2, "Window has 2 tabs");
is(gBrowser.selectedBrowser.currentURI.spec, "about:robots", "Tab has the expected URL");
gBrowser.selectedTab = gBrowser.tabs[1];
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
is(gBrowser.selectedBrowser.currentURI.spec, "http://example.com/", "Tab has the expected URL");
// Cleanup.
await promiseBrowserState(backupState);
});

View File

@ -265,14 +265,6 @@ menuitem.bookmark-item {
}
/* Location bar */
#main-window {
--urlbar-border-color: ThreeDShadow;
--urlbar-border-color-hover: var(--urlbar-border-color);
}
#navigator-toolbox:-moz-lwtheme {
--urlbar-border-color: rgba(0,0,0,.3);
}
#urlbar {
/* override textbox[enablehistory="true"] styling: */
@ -283,6 +275,11 @@ menuitem.bookmark-item {
%ifdef MOZ_PHOTON_THEME
#urlbar:not(:-moz-lwtheme):not([focused="true"]),
.searchbar-textbox:not(:-moz-lwtheme):not([focused="true"]) {
border-color: ThreeDShadow;
}
#urlbar[focused="true"],
.searchbar-textbox[focused="true"] {
border-color: Highlight;
@ -290,6 +287,15 @@ menuitem.bookmark-item {
%else
#main-window {
--urlbar-border-color: ThreeDShadow;
--urlbar-border-color-hover: var(--urlbar-border-color);
}
#navigator-toolbox:-moz-lwtheme {
--urlbar-border-color: rgba(0,0,0,.3);
}
#urlbar,
.searchbar-textbox {
-moz-appearance: none;

View File

@ -526,9 +526,9 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
%ifdef MOZ_PHOTON_THEME
#main-window {
--urlbar-border-color: hsla(240, 5%, 5%, .25);
--urlbar-border-color-hover: hsla(240, 5%, 5%, .35);
#urlbar,
.searchbar-textbox {
font-size: 1.25em;
}
#urlbar[focused="true"],
@ -537,11 +537,6 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
box-shadow: var(--focus-ring-box-shadow);
}
#urlbar,
.searchbar-textbox {
font-size: 1.25em;
}
%else
#urlbar,

View File

@ -8,14 +8,20 @@
.searchbar-textbox {
-moz-appearance: none;
background-clip: content-box;
border: 1px solid var(--urlbar-border-color);
border: 1px solid hsla(240,5%,5%,.25);
border-radius: var(--toolbarbutton-border-radius);
box-shadow: 0 1px 4px hsla(0, 0%, 0%, .05);
box-shadow: 0 1px 4px rgba(0,0,0,.05);
padding: 0;
margin: 0 5px;
min-height: 30px;
}
#urlbar:hover,
.searchbar-textbox:hover {
border-color: hsla(240,5%,5%,.35);
box-shadow: 0 1px 6px rgba(0,0,0,.1), 0 0 1px rgba(0,0,0,.1);
}
#urlbar:-moz-lwtheme,
.searchbar-textbox:-moz-lwtheme {
background-color: hsla(0,0%,100%,.8);
@ -29,12 +35,6 @@
background-color: white;
}
#urlbar:hover,
.searchbar-textbox:hover {
border: 1px solid var(--urlbar-border-color-hover);
box-shadow: 0 1px 6px hsla(0, 0%, 0%, .1), 0 0 1px 0 rgba(0, 0, 0, .1);
}
%endif
#urlbar-container {

View File

@ -652,31 +652,22 @@ toolbar[brighttext] #close-button {
/* ::::: Location Bar ::::: */
#main-window {
--urlbar-border-color: ThreeDShadow;
--urlbar-border-color-hover: var(--urlbar-border-color);
}
#navigator-toolbox:-moz-lwtheme {
--urlbar-border-color: var(--toolbarbutton-hover-bordercolor);
}
%include ../shared/urlbar-searchbar.inc.css
%ifdef MOZ_PHOTON_THEME
@media (-moz-windows-default-theme) {
#main-window:not(:-moz-lwtheme) {
--urlbar-border-color: hsla(240, 5%, 5%, .25);
--urlbar-border-color-hover: hsla(240, 5%, 5%, .35);
}
}
#urlbar,
.searchbar-textbox {
font-size: 1.15em;
}
@media (-moz-windows-default-theme: 0) {
#urlbar:not(:-moz-lwtheme):not([focused="true"]),
.searchbar-textbox:not(:-moz-lwtheme):not([focused="true"]) {
border-color: ThreeDShadow;
}
}
#urlbar[focused="true"],
.searchbar-textbox[focused="true"] {
border-color: Highlight;
@ -684,6 +675,15 @@ toolbar[brighttext] #close-button {
%else
#main-window {
--urlbar-border-color: ThreeDShadow;
--urlbar-border-color-hover: var(--urlbar-border-color);
}
#navigator-toolbox:-moz-lwtheme {
--urlbar-border-color: var(--toolbarbutton-hover-bordercolor);
}
@media (-moz-windows-default-theme) {
@media (-moz-os-version: windows-win7),
(-moz-os-version: windows-win8) {

View File

@ -105,6 +105,8 @@ nsChromeProtocolHandler::NewChannel2(nsIURI* aURI,
nsresult rv;
NS_ENSURE_ARG_POINTER(aURI);
NS_ENSURE_ARG_POINTER(aLoadInfo);
NS_PRECONDITION(aResult, "Null out param");
#ifdef DEBUG
@ -145,6 +147,12 @@ nsChromeProtocolHandler::NewChannel2(nsIURI* aURI,
return rv;
}
// We don't want to allow the inner protocol handler modify the result principal URI
// since we want either |aURI| or anything pre-set by upper layers to prevail.
nsCOMPtr<nsIURI> savedResultPrincipalURI;
rv = aLoadInfo->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI));
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewChannelInternal(getter_AddRefs(result),
resolvedURI,
aLoadInfo);
@ -168,9 +176,8 @@ nsChromeProtocolHandler::NewChannel2(nsIURI* aURI,
// Make sure that the channel remembers where it was
// originally loaded from.
nsLoadFlags loadFlags = 0;
result->GetLoadFlags(&loadFlags);
result->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
rv = aLoadInfo->SetResultPrincipalURI(savedResultPrincipalURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = result->SetOriginalURI(aURI);
if (NS_FAILED(rv)) return rv;

View File

@ -26,6 +26,7 @@ AboutURL.prototype = {
newChannel: function (aURI, aLoadInfo) {
let chan = Services.io.newChannelFromURIWithLoadInfo(this.uri, aLoadInfo);
chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
chan.originalURI = aURI;
return chan;
},

View File

@ -71,7 +71,10 @@
div.addEventListener("mousedown", function (evt) {
if (previousEvent === "touchend" && touchendTime !== 0) {
let now = performance.now();
div.dataset.isDelay = ((now - touchendTime) >= 300);
// Do to time spent processing events our measurement might
// be fractionally short of the actual delay. Round up any
// microsecond changes in case we get something like 299.9.
div.dataset.isDelay = ((now - touchendTime) >= 299.5);
} else {
div.dataset.isDelay = false;
}

View File

@ -70,7 +70,10 @@
div.addEventListener("mousedown", function(evt){
if (previousEvent === "touchend" && touchendTime !== 0) {
let now = performance.now();
div.dataset.isDelay = ((now - touchendTime) >= 300) ? true : false;
// Do to time spent processing events our measurement might
// be fractionally short of the actual delay. Round up any
// microsecond changes in case we get something like 299.9.
div.dataset.isDelay = ((now - touchendTime) >= 299.5) ? true : false;
} else {
div.dataset.isDelay = false;
}

View File

@ -99,7 +99,7 @@ function* testCompletion([key, completion, index, total], editor) {
yield onSuggest;
yield onVisibilityChange;
yield waitForTick();
yield waitForTime(5);
info("Checking the state");
if (completion !== null) {

View File

@ -158,6 +158,7 @@ nsAboutRedirector::NewChannel(nsIURI* aURI,
nsIChannel** aResult)
{
NS_ENSURE_ARG_POINTER(aURI);
NS_ENSURE_ARG_POINTER(aLoadInfo);
NS_ASSERTION(aResult, "must not be null");
nsAutoCString path;
@ -174,9 +175,14 @@ nsAboutRedirector::NewChannel(nsIURI* aURI,
rv = NS_NewURI(getter_AddRefs(tempURI), kRedirMap[i].url);
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
tempURI,
aLoadInfo);
NS_ENSURE_SUCCESS(rv, rv);
// If tempURI links to an external URI (i.e. something other than
// chrome:// or resource://) then set the LOAD_REPLACE flag on the
// channel which forces the channel owner to reflect the displayed
// chrome:// or resource://) then set result principal URI on the
// load info which forces the channel principal to reflect the displayed
// URL rather then being the systemPrincipal.
bool isUIResource = false;
rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
@ -185,17 +191,9 @@ nsAboutRedirector::NewChannel(nsIURI* aURI,
bool isAboutBlank = NS_IsAboutBlank(tempURI);
nsLoadFlags loadFlags = isUIResource || isAboutBlank
? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
: static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
tempURI,
aLoadInfo,
nullptr, // aLoadGroup
nullptr, // aCallbacks
loadFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (!isUIResource && !isAboutBlank) {
aLoadInfo->SetResultPrincipalURI(tempURI);
}
tempChannel->SetOriginalURI(aURI);

View File

@ -11182,6 +11182,12 @@ nsDocShell::DoURILoad(nsIURI* aURI,
if (aOriginalURI) {
channel->SetOriginalURI(aOriginalURI);
// The LOAD_REPLACE flag and its handling here will be removed as part
// of bug 1319110. For now preserve its restoration here to not break
// any code expecting it being set specially on redirected channels.
// If the flag has originally been set to change result of
// NS_GetFinalChannelURI it won't have any effect and also won't cause
// any harm.
if (aLoadReplace) {
uint32_t loadFlags;
channel->GetLoadFlags(&loadFlags);

View File

@ -260,8 +260,8 @@ private:
bool mCalledPropertyDtor;
#endif
uint32_t mMayHaveOpacityAnim;
uint32_t mMayHaveTransformAnim;
bool mMayHaveOpacityAnim;
bool mMayHaveTransformAnim;
};
} // namespace mozilla

View File

@ -79,7 +79,7 @@ public:
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIEVENTTARGET
NS_DECL_NSIEVENTTARGET_FULL
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
explicit EventSourceImpl(EventSource* aEventSource);

View File

@ -13,7 +13,6 @@
#include "nsCycleCollectionParticipant.h"
#include "nsDOMNavigationTiming.h"
#include "nsICancelableRunnable.h"
#include "nsIIncrementalRunnable.h"
#include "nsIRunnable.h"
#include "nsString.h"

View File

@ -1579,7 +1579,7 @@ void
TimeoutManager::MaybeStartThrottleTrackingTimout()
{
if (gTrackingTimeoutThrottlingDelay <= 0 ||
mWindow.AsInner()->InnerObjectsFreed()) {
mWindow.AsInner()->InnerObjectsFreed() || mWindow.IsSuspended()) {
return;
}

View File

@ -87,8 +87,7 @@ public:
NS_DECL_NSIOBSERVER
NS_DECL_NSIREQUEST
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIEVENTTARGET
using nsIEventTarget::Dispatch;
NS_DECL_NSIEVENTTARGET_FULL
explicit WebSocketImpl(WebSocket* aWebSocket)
: mWebSocket(aWebSocket)

View File

@ -5087,7 +5087,7 @@ nsDocument::BeginUpdate(nsUpdateType aUpdateType)
// in the wrong DocGroup. We're unlikely to run JS or do anything else
// observable at this point. We reach this point when cycle collecting a
// <link> element and the unlink code removes a style sheet.
if (mDocGroup && !mIsGoingAway && !mIgnoreDocGroupMismatches) {
if (mDocGroup && !mIsGoingAway && !mInUnlinkOrDeletion && !mIgnoreDocGroupMismatches) {
mDocGroup->ValidateAccess();
}

View File

@ -3732,6 +3732,37 @@ nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration)
}
}
bool
nsFocusManager::CanSkipFocus(nsIContent* aContent)
{
if (!aContent ||
nsContentUtils::IsChromeDoc(aContent->OwnerDoc())) {
return false;
}
if (mFocusedContent == aContent) {
return true;
}
nsIDocShell* ds = aContent->OwnerDoc()->GetDocShell();
if (!ds) {
return true;
}
nsCOMPtr<nsIDocShellTreeItem> root;
ds->GetRootTreeItem(getter_AddRefs(root));
nsCOMPtr<nsPIDOMWindowOuter> newRootWindow =
root ? root->GetWindow() : nullptr;
if (mActiveWindow != newRootWindow) {
nsPIDOMWindowOuter* outerWindow = aContent->OwnerDoc()->GetWindow();
if (outerWindow && outerWindow->GetFocusedNode() == aContent) {
return true;
}
}
return false;
}
nsresult
NS_NewFocusManager(nsIFocusManager** aResult)
{

View File

@ -101,10 +101,7 @@ public:
}
}
bool CanSkipFocus(nsIContent* aContent)
{
return mFocusedContent == aContent;
}
bool CanSkipFocus(nsIContent* aContent);
void FlushBeforeEventHandlingIfNeeded(nsIContent* aContent)
{

View File

@ -563,7 +563,7 @@ NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)
class IdleRequestExecutor final : public nsIRunnable
, public nsICancelableRunnable
, public nsINamed
, public nsIIncrementalRunnable
, public nsIIdleRunnable
{
public:
explicit IdleRequestExecutor(nsGlobalWindow* aWindow)
@ -650,7 +650,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
NS_INTERFACE_MAP_ENTRY(nsINamed)
NS_INTERFACE_MAP_ENTRY(nsIIncrementalRunnable)
NS_INTERFACE_MAP_ENTRY(nsIIdleRunnable)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
NS_INTERFACE_MAP_END

View File

@ -26,6 +26,7 @@
#include "nsThreadUtils.h"
#include "nsNetUtil.h"
#include "nsImageFrame.h"
#include "nsSVGImageFrame.h"
#include "nsIPresShell.h"
@ -1180,8 +1181,9 @@ nsImageLoadingContent::CancelPendingEvent()
RefPtr<imgRequestProxy>&
nsImageLoadingContent::PrepareNextRequest(ImageLoadType aImageLoadType)
{
nsImageFrame* frame = do_QueryFrame(GetOurPrimaryFrame());
if (frame) {
nsImageFrame* imageFrame = do_QueryFrame(GetOurPrimaryFrame());
nsSVGImageFrame* svgImageFrame = do_QueryFrame(GetOurPrimaryFrame());
if (imageFrame || svgImageFrame) {
// Detect JavaScript-based animations created by changing the |src|
// attribute on a timer.
TimeStamp now = TimeStamp::Now();
@ -1191,7 +1193,12 @@ nsImageLoadingContent::PrepareNextRequest(ImageLoadType aImageLoadType)
// If the length of time between request changes is less than the threshold,
// then force sync decoding to eliminate flicker from the animation.
frame->SetForceSyncDecoding(now - mMostRecentRequestChange < threshold);
bool forceSync = (now - mMostRecentRequestChange < threshold);
if (imageFrame) {
imageFrame->SetForceSyncDecoding(forceSync);
} else {
svgImageFrame->SetForceSyncDecoding(forceSync);
}
mMostRecentRequestChange = now;
}

View File

@ -1689,7 +1689,7 @@ public:
}
NS_IMETHOD
Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags) override
Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags = NS_DISPATCH_NORMAL) override
{
MutexAutoLock lock(mMutex);
@ -2106,7 +2106,7 @@ public:
}
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIEVENTTARGET
NS_DECL_NSIEVENTTARGET_FULL
private:
~EventTarget()

View File

@ -0,0 +1,30 @@
<html>
<head>
<script>
function go()
{
try { o114 = document.createElement('canvas'); } catch(e) {;}
try { o103 = (new DOMParser).parseFromString('/', 'text/html'); } catch(e) {;}
try { o217 = o103.all[1]; } catch(e) {;}
try { o253 = window.getSelection(); } catch(e) {;}
try { o270 = document.body.appendChild(document.createElement('a')); } catch(e) {;}
try { o301 = window.getSelection(); } catch(e) {;}
try { o314 = window.getSelection(); } catch(e) {;}
try { document.body.appendChild(o114); } catch(e) {;}
try { document.documentElement.appendChild(o217); } catch(e) {;}
try { document.body.appendChild(o270); } catch(e) {;}
try { o217.contentEditable = true } catch(e) {;}
try { o253.modify('move','forward','lineboundary'); } catch(e) {;}
try { o314.modify('extend','left','line'); } catch(e) {;}
try { o301.modify('extend','right','line'); } catch(e) {;}
}
</script>
</head>
<body onload="go();">
<plaintext></plaintext>
</bdoy>
</html>

View File

@ -74,3 +74,4 @@ load 1317718.html
load 1324505.html
load 1348851.html
load 1350772.html
load 1366176.html

View File

@ -416,6 +416,15 @@ public:
return mLayer->GetScrollThumbData();
}
uint64_t GetScrollbarAnimationId() const
{
MOZ_ASSERT(IsValid());
// This function is only really needed for template-compatibility with
// WebRenderScrollDataWrapper. Although it will be called, the return
// value is not used.
return 0;
}
FrameMetrics::ViewID GetScrollbarTargetContainerId() const
{
MOZ_ASSERT(IsValid());

View File

@ -205,14 +205,20 @@ Layer::~Layer()
{
}
void
Layer::EnsureAnimationsId()
{
if (!mCompositorAnimationsId) {
mCompositorAnimationsId = AnimationHelper::GetNextCompositorAnimationsId();
}
}
Animation*
Layer::AddAnimation()
{
// Here generates a new id when the first animation is added and
// this id is used to represent the animations in this layer.
if (!mCompositorAnimationsId) {
mCompositorAnimationsId = AnimationHelper::GetNextCompositorAnimationsId();
}
EnsureAnimationsId();
MOZ_LAYERS_LOG_IF_SHADOWABLE(
this, ("Layer::Mutated(%p) AddAnimation with id=%" PRIu64, this, mCompositorAnimationsId));

View File

@ -1223,6 +1223,9 @@ public:
}
}
// Ensure that this layer has a valid (non-zero) animations id. This value is
// unique across layers.
void EnsureAnimationsId();
// Call AddAnimation to add a new animation to this layer from layout code.
// Caller must fill in all the properties of the returned animation.
// A later animation overrides an earlier one.

View File

@ -367,13 +367,21 @@ APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
bool
APZCTreeManager::PushStateToWR(wr::WebRenderAPI* aWrApi,
const TimeStamp& aSampleTime)
const TimeStamp& aSampleTime,
nsTArray<WrTransformProperty>& aTransformArray)
{
APZThreadUtils::AssertOnCompositorThread();
MOZ_ASSERT(aWrApi);
MutexAutoLock lock(mTreeLock);
// During the first pass through the tree, we build a cache of guid->HTTN so
// that we can find the relevant APZC instances quickly in subsequent passes,
// such as the one below to generate scrollbar transforms. Without this, perf
// could end up being O(n^2) instead of O(n log n) because we'd have to search
// the tree to find the corresponding APZC every time we hit a thumb node.
std::map<ScrollableLayerGuid, HitTestingTreeNode*> httnMap;
bool activeAnimations = false;
uint64_t lastLayersId = -1;
WrPipelineId lastPipelineId;
@ -396,12 +404,22 @@ APZCTreeManager::PushStateToWR(wr::WebRenderAPI* aWrApi,
if (aNode->GetLayersId() != lastLayersId) {
// If we walked into or out of a subtree, we need to get the new
// pipeline id.
lastLayersId = aNode->GetLayersId();
const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(lastLayersId);
MOZ_ASSERT(state && state->mWrBridge);
const LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aNode->GetLayersId());
if (!(state && state->mWrBridge)) {
// During shutdown we might have layer tree information for stuff
// that has already been torn down. In that case just skip over
// those layers.
return;
}
lastPipelineId = state->mWrBridge->PipelineId();
lastLayersId = aNode->GetLayersId();
}
// Use a 0 presShellId because when we do a lookup in this map for the
// scrollbar below we don't have (or care about) the presShellId.
ScrollableLayerGuid guid(lastLayersId, 0, apzc->GetGuid().mScrollId);
httnMap.emplace(guid, aNode);
ParentLayerPoint layerTranslation = apzc->GetCurrentAsyncTransform(
AsyncPanZoomController::RESPECT_FORCE_DISABLE).mTranslation;
// The positive translation means the painted content is supposed to
@ -416,6 +434,45 @@ APZCTreeManager::PushStateToWR(wr::WebRenderAPI* aWrApi,
activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
});
// Now we iterate over the nodes again, and generate the transforms needed
// for scrollbar thumbs. Although we *could* do this as part of the previous
// iteration, it's cleaner and more efficient to do it as a separate pass
// because now we have a populated httnMap which allows O(log n) lookup here,
// resulting in O(n log n) runtime.
ForEachNode<ReverseIterator>(mRootNode.get(),
[&](HitTestingTreeNode* aNode)
{
if (!aNode->IsScrollThumbNode()) {
return;
}
ScrollableLayerGuid guid(aNode->GetLayersId(), 0, aNode->GetScrollTargetId());
auto it = httnMap.find(guid);
if (it == httnMap.end()) {
// A scrollbar for content which didn't have an APZC. Possibly the
// content isn't layerized. Regardless, we can't async-scroll it so
// we can skip the async transform on the scrollbar.
return;
}
HitTestingTreeNode* scrollTargetNode = it->second;
AsyncPanZoomController* scrollTargetApzc = scrollTargetNode->GetApzc();
MOZ_ASSERT(scrollTargetApzc);
LayerToParentLayerMatrix4x4 transform = scrollTargetApzc->CallWithLastContentPaintMetrics(
[&](const FrameMetrics& aMetrics) {
return AsyncCompositionManager::ComputeTransformForScrollThumb(
aNode->GetTransform() * AsyncTransformMatrix(),
scrollTargetNode->GetTransform().ToUnknownMatrix(),
scrollTargetApzc,
aMetrics,
aNode->GetScrollThumbData(),
scrollTargetNode->IsAncestorOf(aNode),
nullptr);
});
aTransformArray.AppendElement(wr::ToWrTransformProperty(
aNode->GetScrollbarAnimationId(),
transform));
});
return activeAnimations;
}
@ -573,6 +630,7 @@ APZCTreeManager::PrepareNodeForLayer(const ScrollNode& aLayer,
aLayer.GetClipRect() ? Some(ParentLayerIntRegion(*aLayer.GetClipRect())) : Nothing(),
GetEventRegionsOverride(aParent, aLayer));
node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
aLayer.GetScrollbarAnimationId(),
aLayer.GetScrollThumbData(),
aLayer.IsScrollbarContainer());
node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
@ -762,6 +820,7 @@ APZCTreeManager::PrepareNodeForLayer(const ScrollNode& aLayer,
// LayerTransactionParent.cpp must ensure that APZ will be notified
// when those properties change.
node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
aLayer.GetScrollbarAnimationId(),
aLayer.GetScrollThumbData(),
aLayer.IsScrollbarContainer());
node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());

View File

@ -23,6 +23,7 @@
#include "mozilla/layers/AndroidDynamicToolbarAnimator.h"
#endif // defined(MOZ_WIDGET_ANDROID)
struct WrTransformProperty;
namespace mozilla {
class MultiTouchInput;
@ -153,12 +154,15 @@ public:
* walks through the tree of APZC instances and tells webrender about the
* async scroll position. It also advances APZ animations to the specified
* sample time. In effect it is the webrender equivalent of (part of) the
* code in AsyncCompositionManager.
* code in AsyncCompositionManager. If scrollbar transforms need updating
* to reflect the async scroll position, the updated transforms are appended
* to the provided aTransformArray.
* Returns true if any APZ animations are in progress and we need to keep
* compositing.
*/
bool PushStateToWR(wr::WebRenderAPI* aWrApi,
const TimeStamp& aSampleTime);
const TimeStamp& aSampleTime,
nsTArray<WrTransformProperty>& aTransformArray);
/**
* Walk the tree of APZCs and flushes the repaint requests for all the APZCS

View File

@ -27,6 +27,7 @@ HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
, mIsPrimaryApzcHolder(aIsPrimaryHolder)
, mLayersId(aLayersId)
, mScrollViewId(FrameMetrics::NULL_SCROLL_ID)
, mScrollbarAnimationId(0)
, mIsScrollbarContainer(false)
, mFixedPosTarget(FrameMetrics::NULL_SCROLL_ID)
, mOverride(EventRegionsOverride::NoOverride)
@ -94,10 +95,12 @@ HitTestingTreeNode::SetLastChild(HitTestingTreeNode* aChild)
void
HitTestingTreeNode::SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
const uint64_t& aScrollbarAnimationId,
const ScrollThumbData& aThumbData,
bool aIsScrollContainer)
{
mScrollViewId = aScrollViewId;
mScrollbarAnimationId = aScrollbarAnimationId;
mScrollThumbData = aThumbData;
mIsScrollbarContainer = aIsScrollContainer;
}
@ -128,6 +131,12 @@ HitTestingTreeNode::GetScrollTargetId() const
return mScrollViewId;
}
const uint64_t&
HitTestingTreeNode::GetScrollbarAnimationId() const
{
return mScrollbarAnimationId;
}
const ScrollThumbData&
HitTestingTreeNode::GetScrollThumbData() const
{

View File

@ -94,6 +94,7 @@ public:
/* Scrollbar info */
void SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
const uint64_t& aScrollbarAnimationId,
const ScrollThumbData& aThumbData,
bool aIsScrollContainer);
bool MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const;
@ -101,6 +102,7 @@ public:
bool IsScrollThumbNode() const; // Scroll thumb container layer.
FrameMetrics::ViewID GetScrollTargetId() const;
const ScrollThumbData& GetScrollThumbData() const;
const uint64_t& GetScrollbarAnimationId() const;
/* Fixed pos info */
@ -138,6 +140,11 @@ private:
// represents the scroll id of the scroll frame scrolled by the scrollbar.
FrameMetrics::ViewID mScrollViewId;
// This is only set to non-zero if WebRender is enabled, and only for HTTNs
// where IsScrollThumbNode() returns true. It holds the animation id that we
// use to move the thumb node to reflect async scrolling.
uint64_t mScrollbarAnimationId;
// This is set for scroll thumb Container layers only.
ScrollThumbData mScrollThumbData;

View File

@ -373,7 +373,7 @@ WebRenderBridgeParent::UpdateAPZ()
}
bool
WebRenderBridgeParent::PushAPZStateToWR()
WebRenderBridgeParent::PushAPZStateToWR(nsTArray<WrTransformProperty>& aTransformArray)
{
CompositorBridgeParent* cbp = GetRootCompositorBridgeParent();
if (!cbp) {
@ -387,7 +387,7 @@ WebRenderBridgeParent::PushAPZStateToWR()
if (frameInterval != TimeDuration::Forever()) {
animationTime += frameInterval;
}
return apzc->PushStateToWR(mApi, animationTime);
return apzc->PushStateToWR(mApi, animationTime, aTransformArray);
}
return false;
}
@ -776,28 +776,36 @@ WebRenderBridgeParent::CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::In
return;
}
if (PushAPZStateToWR()) {
ScheduleComposition();
}
bool scheduleComposite = false;
nsTArray<WrOpacityProperty> opacityArray;
nsTArray<WrTransformProperty> transformArray;
if (gfxPrefs::WebRenderOMTAEnabled()) {
nsTArray<WrOpacityProperty> opacityArray;
nsTArray<WrTransformProperty> transformArray;
SampleAnimations(opacityArray, transformArray);
if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
mApi->GenerateFrame(opacityArray, transformArray);
ScheduleComposition();
return;
scheduleComposite = true;
}
}
mApi->GenerateFrame();
if (PushAPZStateToWR(transformArray)) {
scheduleComposite = true;
}
if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
mApi->GenerateFrame(opacityArray, transformArray);
} else {
mApi->GenerateFrame();
}
// XXX Enable it when async video is supported.
// if (!mCompositableHolder->GetCompositeUntilTime().IsNull()) {
// ScheduleComposition();
// scheduleComposite = true;
// }
if (scheduleComposite) {
ScheduleComposition();
}
}
void

View File

@ -199,7 +199,9 @@ private:
// Have APZ push the async scroll state to WR. Returns true if an APZ
// animation is in effect and we need to schedule another composition.
bool PushAPZStateToWR();
// If scrollbars need their transforms updated, the provided aTransformArray
// is populated with the property update details.
bool PushAPZStateToWR(nsTArray<WrTransformProperty>& aTransformArray);
private:
struct PendingTransactionId {

View File

@ -90,6 +90,23 @@ WebRenderContainerLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
WrBridge()->AddWebRenderParentCommand(anim);
}
// If APZ is enabled and this layer is a scroll thumb, then it might need
// to move in the compositor to represent the async scroll position. So we
// ensure that there is an animations id set on it, we will use this to give
// WebRender updated transforms for composition.
if (WrManager()->AsyncPanZoomEnabled() &&
GetScrollThumbData().mDirection != ScrollDirection::NONE) {
// A scroll thumb better not have a transform animation already or we're
// going to end up clobbering it with APZ animating it too.
MOZ_ASSERT(transformForSC);
EnsureAnimationsId();
animationsId = GetCompositorAnimationsId();
// We need to set the transform in the stacking context to null for it to
// pick up and install the animation id.
transformForSC = nullptr;
}
ScrollingLayersHelper scroller(this, aBuilder, aSc);
StackingContextHelper sc(aSc, aBuilder, this, animationsId, opacityForSC, transformForSC);

View File

@ -44,6 +44,7 @@ WebRenderLayerScrollData::Initialize(WebRenderScrollData& aOwner,
? aLayer->AsContainerLayer()->GetEventRegionsOverride()
: EventRegionsOverride::NoOverride;
mScrollThumbData = aLayer->GetScrollThumbData();
mScrollbarAnimationId = aLayer->GetCompositorAnimationsId();
mScrollbarTargetContainerId = aLayer->GetScrollbarTargetContainerId();
mIsScrollbarContainer = aLayer->IsScrollbarContainer();
mFixedPosScrollContainerId = aLayer->GetFixedPositionScrollContainerId();

View File

@ -54,6 +54,7 @@ public:
Maybe<uint64_t> GetReferentId() const { return mReferentId; }
EventRegionsOverride GetEventRegionsOverride() const { return mEventRegionsOverride; }
const ScrollThumbData& GetScrollThumbData() const { return mScrollThumbData; }
const uint64_t& GetScrollbarAnimationId() const { return mScrollbarAnimationId; }
FrameMetrics::ViewID GetScrollbarTargetContainerId() const { return mScrollbarTargetContainerId; }
bool IsScrollbarContainer() const { return mIsScrollbarContainer; }
FrameMetrics::ViewID GetFixedPositionScrollContainerId() const { return mFixedPosScrollContainerId; }
@ -83,6 +84,7 @@ private:
Maybe<uint64_t> mReferentId;
EventRegionsOverride mEventRegionsOverride;
ScrollThumbData mScrollThumbData;
uint64_t mScrollbarAnimationId;
FrameMetrics::ViewID mScrollbarTargetContainerId;
bool mIsScrollbarContainer;
FrameMetrics::ViewID mFixedPosScrollContainerId;
@ -176,6 +178,7 @@ struct ParamTraits<mozilla::layers::WebRenderLayerScrollData>
WriteParam(aMsg, aParam.mReferentId);
WriteParam(aMsg, aParam.mEventRegionsOverride);
WriteParam(aMsg, aParam.mScrollThumbData);
WriteParam(aMsg, aParam.mScrollbarAnimationId);
WriteParam(aMsg, aParam.mScrollbarTargetContainerId);
WriteParam(aMsg, aParam.mIsScrollbarContainer);
WriteParam(aMsg, aParam.mFixedPosScrollContainerId);
@ -193,6 +196,7 @@ struct ParamTraits<mozilla::layers::WebRenderLayerScrollData>
&& ReadParam(aMsg, aIter, &aResult->mReferentId)
&& ReadParam(aMsg, aIter, &aResult->mEventRegionsOverride)
&& ReadParam(aMsg, aIter, &aResult->mScrollThumbData)
&& ReadParam(aMsg, aIter, &aResult->mScrollbarAnimationId)
&& ReadParam(aMsg, aIter, &aResult->mScrollbarTargetContainerId)
&& ReadParam(aMsg, aIter, &aResult->mIsScrollbarContainer)
&& ReadParam(aMsg, aIter, &aResult->mFixedPosScrollContainerId);

View File

@ -285,6 +285,12 @@ public:
return mLayer->GetScrollThumbData();
}
uint64_t GetScrollbarAnimationId() const
{
MOZ_ASSERT(IsValid());
return mLayer->GetScrollbarAnimationId();
}
FrameMetrics::ViewID GetScrollbarTargetContainerId() const
{
MOZ_ASSERT(IsValid());

View File

@ -363,7 +363,9 @@ static inline WrRepeatMode ToWrRepeatMode(uint8_t repeatMode)
return WrRepeatMode::Stretch;
}
static inline WrTransformProperty ToWrTransformProperty(uint64_t id, gfx::Matrix4x4& transform)
template<class S, class T>
static inline WrTransformProperty ToWrTransformProperty(uint64_t id,
const gfx::Matrix4x4Typed<S, T>& transform)
{
WrTransformProperty prop;
prop.id = id;

View File

@ -21,6 +21,7 @@
#include "nsContentUtils.h"
#include "nsString.h"
#include "nsTArray.h"
#include "URIUtils.h"
namespace mozilla {
namespace net {
@ -304,6 +305,13 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
sandboxedLoadingPrincipalInfo = sandboxedLoadingPrincipalInfoTemp;
}
OptionalURIParams optionalResultPrincipalURI = mozilla::void_t();
nsCOMPtr<nsIURI> resultPrincipalURI;
Unused << aLoadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
if (resultPrincipalURI) {
SerializeURI(resultPrincipalURI, optionalResultPrincipalURI);
}
nsTArray<PrincipalInfo> redirectChainIncludingInternalRedirects;
for (const nsCOMPtr<nsIPrincipal>& principal : aLoadInfo->RedirectChainIncludingInternalRedirects()) {
rv = PrincipalToPrincipalInfo(principal, redirectChainIncludingInternalRedirects.AppendElement());
@ -322,6 +330,7 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
triggeringPrincipalInfo,
principalToInheritInfo,
sandboxedLoadingPrincipalInfo,
optionalResultPrincipalURI,
aLoadInfo->GetSecurityFlags(),
aLoadInfo->InternalContentPolicyType(),
static_cast<uint32_t>(aLoadInfo->GetTainting()),
@ -385,6 +394,12 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIURI> resultPrincipalURI;
if (loadInfoArgs.resultPrincipalURI().type() != OptionalURIParams::Tvoid_t) {
resultPrincipalURI = DeserializeURI(loadInfoArgs.resultPrincipalURI());
NS_ENSURE_TRUE(resultPrincipalURI, NS_ERROR_UNEXPECTED);
}
nsTArray<nsCOMPtr<nsIPrincipal>> redirectChainIncludingInternalRedirects;
for (const PrincipalInfo& principalInfo : loadInfoArgs.redirectChainIncludingInternalRedirects()) {
nsCOMPtr<nsIPrincipal> redirectedPrincipal =
@ -406,6 +421,7 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
triggeringPrincipal,
principalToInherit,
sandboxedLoadingPrincipal,
resultPrincipalURI,
loadInfoArgs.securityFlags(),
loadInfoArgs.contentPolicyType(),
static_cast<LoadTainting>(loadInfoArgs.tainting()),

View File

@ -126,7 +126,7 @@ struct ParamTraits<mozilla::mscom::COMPtrHolder<Interface, _IID>>
}
}
mozilla::mscom::ProxyStream proxyStream(buf.get(), length);
mozilla::mscom::ProxyStream proxyStream(_IID, buf.get(), length);
if (!proxyStream.IsValid()) {
return false;
}

View File

@ -11,6 +11,7 @@
#include "mozilla/NotNull.h"
#include "nsExceptionHandler.h"
#include "nsWindowsHelpers.h"
#include "nsXULAppAPI.h"
#include <oleauto.h>
@ -354,7 +355,14 @@ AnnotateInterfaceRegistration(REFIID aIid)
json.End();
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("InterfaceRegistrationInfo"),
nsAutoCString annotationKey;
annotationKey.AppendLiteral("InterfaceRegistrationInfo");
if (XRE_IsParentProcess()) {
annotationKey.AppendLiteral("Parent");
} else {
annotationKey.AppendLiteral("Child");
}
CrashReporter::AnnotateCrashReport(annotationKey,
static_cast<CStringWriter*>(json.WriteFunc())->Get());
}

View File

@ -32,12 +32,12 @@ ProxyStream::ProxyStream()
// GetBuffer() fails with this variant, but that's okay because we're just
// reconstructing the stream from a buffer anyway.
ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf,
const int aInitBufSize)
: mStream(InitStream(aInitBuf, static_cast<const UINT>(aInitBufSize)))
, mGlobalLockedBuf(nullptr)
, mHGlobal(nullptr)
, mBufSize(aInitBufSize)
, mUnmarshalResult(E_UNEXPECTED)
{
if (!aInitBufSize) {
// We marshaled a nullptr. Nothing else to do here.
@ -51,6 +51,8 @@ ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
return;
}
HRESULT unmarshalResult = S_OK;
// We need to convert to an interface here otherwise we mess up const
// correctness with IPDL. We'll request an IUnknown and then QI the
// actual interface later.
@ -59,10 +61,10 @@ ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
{
// OK to forget mStream when calling into this function because the stream
// gets released even if the unmarshaling part fails.
mUnmarshalResult =
unmarshalResult =
::CoGetInterfaceAndReleaseStream(mStream.forget().take(), IID_IUnknown,
getter_AddRefs(mUnmarshaledProxy));
MOZ_ASSERT(SUCCEEDED(mUnmarshalResult));
MOZ_ASSERT(SUCCEEDED(unmarshalResult));
};
if (XRE_IsParentProcess()) {
@ -75,10 +77,11 @@ ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
}
#if defined(MOZ_CRASHREPORTER)
if (FAILED(mUnmarshalResult)) {
nsPrintfCString hrAsStr("0x%08X", mUnmarshalResult);
if (FAILED(unmarshalResult)) {
nsPrintfCString hrAsStr("0x%08X", unmarshalResult);
CrashReporter::AnnotateCrashReport(
NS_LITERAL_CSTRING("CoGetInterfaceAndReleaseStreamFailure"), hrAsStr);
AnnotateInterfaceRegistration(aIID);
}
#endif // defined(MOZ_CRASHREPORTER)
}
@ -197,12 +200,6 @@ ProxyStream::GetInterface(REFIID aIID, void** aOutInterface) const
return false;
}
#if defined(MOZ_CRASHREPORTER)
if (FAILED(mUnmarshalResult)) {
AnnotateInterfaceRegistration(aIID);
}
#endif
if (!mUnmarshaledProxy) {
*aOutInterface = nullptr;
return true;

View File

@ -21,7 +21,7 @@ class ProxyStream final
public:
ProxyStream();
ProxyStream(REFIID aIID, IUnknown* aObject);
ProxyStream(const BYTE* aInitBuf, const int aInitBufSize);
ProxyStream(REFIID aIID, const BYTE* aInitBuf, const int aInitBufSize);
~ProxyStream();

View File

@ -65,6 +65,20 @@ const size_t ArenaHeaderSize = sizeof(size_t) + 2 * sizeof(uintptr_t) +
static const uint32_t BLACK = 0;
static const uint32_t GRAY = 1;
/*
* Two bits determine the mark color as follows:
* BLACK_BIT GRAY_OR_BLACK_BIT color
* 0 0 white
* 0 1 gray
* 1 0 black
* 1 1 black
*/
enum class ColorBit : uint32_t
{
BlackBit = 0,
GrayOrBlackBit = 1
};
/*
* The "location" field in the Chunk trailer is a enum indicating various roles
* of the chunk.
@ -301,11 +315,12 @@ GetGCThingMarkBitmap(const uintptr_t addr)
}
static MOZ_ALWAYS_INLINE void
GetGCThingMarkWordAndMask(const uintptr_t addr, uint32_t color,
GetGCThingMarkWordAndMask(const uintptr_t addr, ColorBit colorBit,
uintptr_t** wordp, uintptr_t* maskp)
{
MOZ_ASSERT(addr);
const size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellBytesPerMarkBit + color;
const size_t bit = (addr & js::gc::ChunkMask) / js::gc::CellBytesPerMarkBit +
static_cast<uint32_t>(colorBit);
MOZ_ASSERT(bit < js::gc::ChunkMarkBitmapBits);
uintptr_t* bitmap = GetGCThingMarkBitmap(addr);
const uintptr_t nbits = sizeof(*bitmap) * CHAR_BIT;
@ -325,12 +340,20 @@ GetGCThingZone(const uintptr_t addr)
static MOZ_ALWAYS_INLINE bool
TenuredCellIsMarkedGray(const Cell* cell)
{
// Return true if GrayOrBlackBit is set and BlackBit is not set.
MOZ_ASSERT(cell);
MOZ_ASSERT(!js::gc::IsInsideNursery(cell));
uintptr_t* word, mask;
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::GRAY, &word, &mask);
return *word & mask;
uintptr_t* grayWord, grayMask;
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::ColorBit::GrayOrBlackBit,
&grayWord, &grayMask);
if (!(*grayWord & grayMask))
return false;
uintptr_t* blackWord, blackMask;
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::ColorBit::BlackBit,
&blackWord, &blackMask);
return !(*blackWord & blackMask);
}
static MOZ_ALWAYS_INLINE bool

View File

@ -907,7 +907,6 @@ intl_availableLocales(JSContext* cx, CountAvailable countAvailable,
#if ENABLE_INTL_API
RootedAtom a(cx);
uint32_t count = countAvailable();
RootedValue t(cx, BooleanValue(true));
for (uint32_t i = 0; i < count; i++) {
const char* locale = getAvailable(i);
auto lang = DuplicateString(cx, locale);
@ -919,7 +918,7 @@ intl_availableLocales(JSContext* cx, CountAvailable countAvailable,
a = Atomize(cx, lang.get(), strlen(lang.get()));
if (!a)
return false;
if (!DefineProperty(cx, locales, a->asPropertyName(), t, nullptr, nullptr,
if (!DefineProperty(cx, locales, a->asPropertyName(), TrueHandleValue, nullptr, nullptr,
JSPROP_ENUMERATE))
{
return false;
@ -1215,7 +1214,6 @@ js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
if (!DefineElement(cx, collations, index++, NullHandleValue))
return false;
RootedString jscollation(cx);
RootedValue element(cx);
for (uint32_t i = 0; i < count; i++) {
const char* collation = uenum_next(values, nullptr, &status);
@ -1232,7 +1230,7 @@ js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
continue;
// ICU returns old-style keyword values; map them to BCP 47 equivalents.
jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation));
JSString* jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation));
if (!jscollation)
return false;
element = StringValue(jscollation);
@ -2674,6 +2672,30 @@ js::intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp
return true;
}
static bool
DefaultCalendar(JSContext* cx, const JSAutoByteString& locale, MutableHandleValue rval)
{
UErrorCode status = U_ZERO_ERROR;
UCalendar* cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
// This correctly handles nullptr |cal| when opening failed.
ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
const char* calendar = ucal_getType(cal, &status);
if (U_FAILURE(status)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
return false;
}
// ICU returns old-style keyword values; map them to BCP 47 equivalents
JSString* str = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("ca", calendar));
if (!str)
return false;
rval.setString(str);
return true;
}
struct CalendarAlias
{
const char* const calendar;
@ -2702,31 +2724,15 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
uint32_t index = 0;
// We need the default calendar for the locale as the first result.
UErrorCode status = U_ZERO_ERROR;
RootedString jscalendar(cx);
{
UCalendar* cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
RootedValue element(cx);
if (!DefaultCalendar(cx, locale, &element))
return false;
// This correctly handles nullptr |cal| when opening failed.
ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
const char* calendar = ucal_getType(cal, &status);
if (U_FAILURE(status)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
return false;
}
// ICU returns old-style keyword values; map them to BCP 47 equivalents
jscalendar = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("ca", calendar));
if (!jscalendar)
return false;
}
RootedValue element(cx, StringValue(jscalendar));
if (!DefineElement(cx, calendars, index++, element))
return false;
// Now get the calendars that "would make a difference", i.e., not the default.
UErrorCode status = U_ZERO_ERROR;
UEnumeration* values = ucal_getKeywordValuesForLocale("ca", locale.ptr(), false, &status);
if (U_FAILURE(status)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
@ -2750,7 +2756,7 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
// ICU returns old-style keyword values; map them to BCP 47 equivalents
calendar = uloc_toUnicodeLocaleType("ca", calendar);
jscalendar = JS_NewStringCopyZ(cx, calendar);
JSString* jscalendar = JS_NewStringCopyZ(cx, calendar);
if (!jscalendar)
return false;
element = StringValue(jscalendar);
@ -2760,7 +2766,7 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
// ICU doesn't return calendar aliases, append them here.
for (const auto& calendarAlias : calendarAliases) {
if (equal(calendar, calendarAlias.calendar)) {
jscalendar = JS_NewStringCopyZ(cx, calendarAlias.alias);
JSString* jscalendar = JS_NewStringCopyZ(cx, calendarAlias.alias);
if (!jscalendar)
return false;
element = StringValue(jscalendar);
@ -2774,6 +2780,20 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
return true;
}
bool
js::intl_defaultCalendar(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isString());
JSAutoByteString locale(cx, args[0].toString());
if (!locale)
return false;
return DefaultCalendar(cx, locale, args.rval());
}
template<typename Char>
static constexpr Char
ToUpperASCII(Char c)

View File

@ -435,6 +435,16 @@ intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp);
extern MOZ_MUST_USE bool
intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp);
/**
* Returns the calendar type identifier per Unicode Technical Standard 35,
* Unicode Locale Data Markup Language, for the default calendar for the given
* locale.
*
* Usage: calendar = intl_defaultCalendar(locale)
*/
extern MOZ_MUST_USE bool
intl_defaultCalendar(JSContext* cx, unsigned argc, Value* vp);
/**
* 6.4.1 IsValidTimeZoneName ( timeZone )
*

View File

@ -898,7 +898,6 @@ function BestAvailableLocaleIgnoringDefault(availableLocales, locale) {
return BestAvailableLocaleHelper(availableLocales, locale, false);
}
var noRelevantExtensionKeys = [];
/**
* Compares a BCP 47 language priority list against the set of locales in
@ -1058,26 +1057,17 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte
// Step 8.
var supportedExtension = "-u";
// In this implementation, localeData is a function, not an object.
var localeDataProvider = localeData();
// Steps 9-12.
var i = 0;
var len = relevantExtensionKeys.length;
var foundLocaleData;
if (len > 0) {
// In this implementation, localeData is a function, not an object.
// Step 12.b.
foundLocaleData = localeData(foundLocale);
}
while (i < len) {
for (var i = 0; i < relevantExtensionKeys.length; i++) {
// Step 12.a.
var key = relevantExtensionKeys[i];
// Step 12.c.
var keyLocaleData = foundLocaleData[key];
// Locale data provides default value.
// Step 12.d.
var value = keyLocaleData[0];
assert(typeof value === "string" || value === null, "unexpected locale data value");
// Steps 12.b-d (The locale data is only computed when needed).
var keyLocaleData = undefined;
var value = undefined;
// Locale tag may override.
@ -1096,6 +1086,9 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte
// Step 12.f.ii.
if (requestedValue !== undefined) {
// Steps 12.b-c.
keyLocaleData = callFunction(localeDataProvider[key], null, foundLocale);
// Step 12.f.ii.1.
if (requestedValue !== "") {
// Step 12.f.ii.1.a.
@ -1120,18 +1113,29 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte
var optionsValue = options[key];
// Step 12.g, 12.g.ii.
if (optionsValue !== undefined &&
optionsValue !== value &&
callFunction(ArrayIndexOf, keyLocaleData, optionsValue) !== -1)
{
value = optionsValue;
supportedExtensionAddition = "";
if (optionsValue !== undefined && optionsValue !== value) {
// Steps 12.b-c.
if (keyLocaleData === undefined)
keyLocaleData = callFunction(localeDataProvider[key], null, foundLocale);
if (callFunction(ArrayIndexOf, keyLocaleData, optionsValue) !== -1) {
value = optionsValue;
supportedExtensionAddition = "";
}
}
// Locale data provides default value.
if (value === undefined) {
// Steps 12.b-d.
value = keyLocaleData === undefined
? callFunction(localeDataProvider.default[key], null, foundLocale)
: keyLocaleData[0];
}
// Steps 12.h-j.
assert(typeof value === "string" || value === null, "unexpected locale data value");
result[key] = value;
supportedExtension += supportedExtensionAddition;
i++;
}
// Step 13.
@ -1554,15 +1558,11 @@ function resolveCollatorInternals(lazyCollatorData) {
// Steps 21-22.
var s = lazyCollatorData.rawSensitivity;
if (s === undefined) {
if (collatorIsSorting) {
// Step 21.a.
s = "variant";
} else {
// Step 21.b.
var dataLocale = r.dataLocale;
var dataLocaleData = localeData(dataLocale);
s = dataLocaleData.sensitivity;
}
// In theory the default sensitivity for the "search" collator is
// locale dependent; in reality the CLDR/ICU default strength is
// always tertiary. Therefore use "variant" as the default value for
// both collation modes.
s = "variant";
}
internalProps.sensitivity = s;
@ -1735,49 +1735,97 @@ var collatorInternalProperties = {
/**
* Returns the default caseFirst values for the given locale and usage. The
* first element in the returned array denotes the default value per ES2017
* Intl, 9.1 Internal slots of Service Constructors.
* Returns the actual locale used when a collator for |locale| is constructed.
*/
function collatorCaseFirst(locale, usage) {
function collatorActualLocale(locale) {
assert(typeof locale === "string", "locale should be string");
assert(usage === "sort" || usage === "search", "invalid usage option");
if (usage === "sort") {
// If |locale| is the default locale (e.g. da-DK), but only supported
// through a fallback (da), we need to get the actual locale before we
// can call intl_isUpperCaseFirst. Also see BestAvailableLocaleHelper.
var availableLocales = callFunction(collatorInternalProperties.availableLocales,
collatorInternalProperties);
var actualLocale = BestAvailableLocaleIgnoringDefault(availableLocales, locale);
// If |locale| is the default locale (e.g. da-DK), but only supported
// through a fallback (da), we need to get the actual locale before we
// can call intl_isUpperCaseFirst. Also see BestAvailableLocaleHelper.
var availableLocales = callFunction(collatorInternalProperties.availableLocales,
collatorInternalProperties);
return BestAvailableLocaleIgnoringDefault(availableLocales, locale);
}
if (intl_isUpperCaseFirst(actualLocale))
return ["upper", "false", "lower"];
}
/**
* Returns the default caseFirst values for the given locale. The first
* element in the returned array denotes the default value per ES2017 Intl,
* 9.1 Internal slots of Service Constructors.
*/
function collatorSortCaseFirst(locale) {
var actualLocale = collatorActualLocale(locale);
if (intl_isUpperCaseFirst(actualLocale))
return ["upper", "false", "lower"];
// Default caseFirst values for all other languages.
return ["false", "lower", "upper"];
}
function collatorSortLocaleData(locale) {
return {
co: intl_availableCollations(locale),
kn: ["false", "true"],
kf: collatorCaseFirst(locale, "sort"),
};
/**
* Returns the default caseFirst value for the given locale.
*/
function collatorSortCaseFirstDefault(locale) {
var actualLocale = collatorActualLocale(locale);
if (intl_isUpperCaseFirst(actualLocale))
return "upper";
// Default caseFirst value for all other languages.
return "false";
}
function collatorSearchLocaleData(locale) {
function collatorSortLocaleData() {
/* eslint-disable object-shorthand */
return {
co: [null],
kn: ["false", "true"],
kf: collatorCaseFirst(locale, "search"),
// In theory the default sensitivity is locale dependent;
// in reality the CLDR/ICU default strength is always tertiary.
sensitivity: "variant"
co: intl_availableCollations,
kn: function() {
return ["false", "true"];
},
kf: collatorSortCaseFirst,
default: {
co: function() {
// The first element of the collations array must be |null|
// per ES2017 Intl, 10.2.3 Internal Slots.
return null;
},
kn: function() {
return "false";
},
kf: collatorSortCaseFirstDefault,
}
};
/* eslint-enable object-shorthand */
}
function collatorSearchLocaleData() {
/* eslint-disable object-shorthand */
return {
co: function() {
return [null];
},
kn: function() {
return ["false", "true"];
},
kf: function() {
return ["false", "lower", "upper"];
},
default: {
co: function() {
return null;
},
kn: function() {
return "false";
},
kf: function() {
return "false";
},
}
};
/* eslint-enable object-shorthand */
}
@ -2232,9 +2280,12 @@ function getNumberingSystems(locale) {
}
function numberFormatLocaleData(locale) {
function numberFormatLocaleData() {
return {
nu: getNumberingSystems(locale)
nu: getNumberingSystems,
default: {
nu: intl_numberingSystem,
}
};
}
@ -2926,10 +2977,14 @@ var dateTimeFormatInternalProperties = {
};
function dateTimeFormatLocaleData(locale) {
function dateTimeFormatLocaleData() {
return {
ca: intl_availableCalendars(locale),
nu: getNumberingSystems(locale)
ca: intl_availableCalendars,
nu: getNumberingSystems,
default: {
ca: intl_defaultCalendar,
nu: intl_numberingSystem,
}
};
}
@ -3143,6 +3198,7 @@ function resolveICUPattern(pattern, result) {
* Spec: ECMAScript 402 API, PluralRules, 1.3.3.
*/
var pluralRulesInternalProperties = {
localeData: pluralRulesLocaleData,
_availableLocales: null,
availableLocales: function() // eslint-disable-line object-shorthand
{
@ -3153,9 +3209,17 @@ var pluralRulesInternalProperties = {
locales = intl_PluralRules_availableLocales();
addSpecialMissingLanguageTags(locales);
return (this._availableLocales = locales);
}
},
relevantExtensionKeys: [],
};
function pluralRulesLocaleData() {
// PluralRules don't support any extension keys.
return {};
}
/**
* Compute an internal properties object from |lazyPluralRulesData|.
*/
@ -3170,7 +3234,7 @@ function resolvePluralRulesInternals(lazyPluralRulesData) {
const r = ResolveLocale(callFunction(PluralRules.availableLocales, PluralRules),
lazyPluralRulesData.requestedLocales,
lazyPluralRulesData.opt,
noRelevantExtensionKeys, undefined);
PluralRules.relevantExtensionKeys, PluralRules.localeData);
// Step 14.
internalProps.locale = r.locale;

View File

@ -293,7 +293,7 @@ class TenuredCell : public Cell
MOZ_ALWAYS_INLINE bool isMarked(uint32_t color = BLACK) const;
// The return value indicates if the cell went from unmarked to marked.
MOZ_ALWAYS_INLINE bool markIfUnmarked(uint32_t color = BLACK) const;
MOZ_ALWAYS_INLINE void unmark(uint32_t color) const;
MOZ_ALWAYS_INLINE void markBlack() const;
MOZ_ALWAYS_INLINE void copyMarkBitsFrom(const TenuredCell* src);
// Access to the arena.
@ -898,31 +898,42 @@ struct ChunkBitmap
public:
ChunkBitmap() { }
MOZ_ALWAYS_INLINE void getMarkWordAndMask(const Cell* cell, uint32_t color,
MOZ_ALWAYS_INLINE void getMarkWordAndMask(const Cell* cell, ColorBit colorBit,
uintptr_t** wordp, uintptr_t* maskp)
{
detail::GetGCThingMarkWordAndMask(uintptr_t(cell), color, wordp, maskp);
detail::GetGCThingMarkWordAndMask(uintptr_t(cell), colorBit, wordp, maskp);
}
MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool markBit(const Cell* cell, ColorBit colorBit) {
uintptr_t* word, mask;
getMarkWordAndMask(cell, colorBit, &word, &mask);
return *word & mask;
}
MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarked(const Cell* cell, uint32_t color) {
uintptr_t* word, mask;
getMarkWordAndMask(cell, color, &word, &mask);
return *word & mask;
if (color == BLACK) {
return markBit(cell, ColorBit::BlackBit) ||
markBit(cell, ColorBit::GrayOrBlackBit);
} else {
return !markBit(cell, ColorBit::BlackBit) &&
markBit(cell, ColorBit::GrayOrBlackBit);
}
}
// The return value indicates if the cell went from unmarked to marked.
MOZ_ALWAYS_INLINE bool markIfUnmarked(const Cell* cell, uint32_t color) {
uintptr_t* word, mask;
getMarkWordAndMask(cell, BLACK, &word, &mask);
getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask);
if (*word & mask)
return false;
*word |= mask;
if (color != BLACK) {
if (color == BLACK) {
*word |= mask;
} else {
/*
* We use getMarkWordAndMask to recalculate both mask and word as
* doing just mask << color may overflow the mask.
*/
getMarkWordAndMask(cell, color, &word, &mask);
getMarkWordAndMask(cell, ColorBit::GrayOrBlackBit, &word, &mask);
if (*word & mask)
return false;
*word |= mask;
@ -930,16 +941,22 @@ struct ChunkBitmap
return true;
}
MOZ_ALWAYS_INLINE void unmark(const Cell* cell, uint32_t color) {
MOZ_ALWAYS_INLINE void markBlack(const Cell* cell) {
uintptr_t* word, mask;
getMarkWordAndMask(cell, color, &word, &mask);
*word &= ~mask;
getMarkWordAndMask(cell, ColorBit::BlackBit, &word, &mask);
*word |= mask;
}
MOZ_ALWAYS_INLINE void copyMarkBit(Cell* dst, const TenuredCell* src, uint32_t color) {
uintptr_t* word, mask;
getMarkWordAndMask(dst, color, &word, &mask);
*word = (*word & ~mask) | (src->isMarked(color) ? mask : 0);
MOZ_ALWAYS_INLINE void copyMarkBit(Cell* dst, const TenuredCell* src, ColorBit colorBit) {
uintptr_t* srcWord, srcMask;
getMarkWordAndMask(src, colorBit, &srcWord, &srcMask);
uintptr_t* dstWord, dstMask;
getMarkWordAndMask(dst, colorBit, &dstWord, &dstMask);
*dstWord &= ~dstMask;
if (*srcWord & srcMask)
*dstWord |= dstMask;
}
void clear() {
@ -953,7 +970,7 @@ struct ChunkBitmap
"that covers bits from two arenas.");
uintptr_t* word, unused;
getMarkWordAndMask(reinterpret_cast<Cell*>(arena->address()), BLACK, &word, &unused);
getMarkWordAndMask(reinterpret_cast<Cell*>(arena->address()), ColorBit::BlackBit, &word, &unused);
return word;
}
};
@ -1239,19 +1256,17 @@ TenuredCell::markIfUnmarked(uint32_t color /* = BLACK */) const
}
void
TenuredCell::unmark(uint32_t color) const
TenuredCell::markBlack() const
{
MOZ_ASSERT(color != BLACK);
AssertValidColor(this, color);
chunk()->bitmap.unmark(this, color);
chunk()->bitmap.markBlack(this);
}
void
TenuredCell::copyMarkBitsFrom(const TenuredCell* src)
{
ChunkBitmap& bitmap = chunk()->bitmap;
bitmap.copyMarkBit(this, src, BLACK);
bitmap.copyMarkBit(this, src, GRAY);
bitmap.copyMarkBit(this, src, ColorBit::BlackBit);
bitmap.copyMarkBit(this, src, ColorBit::GrayOrBlackBit);
}
inline Arena*

View File

@ -3369,7 +3369,7 @@ UnmarkGrayTracer::onChild(const JS::GCCellPtr& thing)
if (!tenured.isMarked(GRAY))
return;
tenured.unmark(GRAY);
tenured.markBlack();
unmarkedAny = true;
if (!stack.append(thing))
@ -3459,19 +3459,22 @@ GetMarkWordAddress(Cell* cell)
uintptr_t* wordp;
uintptr_t mask;
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), js::gc::BLACK, &wordp, &mask);
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), ColorBit::BlackBit, &wordp, &mask);
return wordp;
}
uintptr_t
GetMarkMask(Cell* cell, uint32_t color)
{
MOZ_ASSERT(color == 0 || color == 1);
if (!cell->isTenured())
return 0;
ColorBit bit = color == 0 ? ColorBit::BlackBit : ColorBit::GrayOrBlackBit;
uintptr_t* wordp;
uintptr_t mask;
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), color, &wordp, &mask);
js::gc::detail::GetGCThingMarkWordAndMask(uintptr_t(cell), bit, &wordp, &mask);
return mask;
}

View File

@ -4224,6 +4224,21 @@ JS::DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
return StartOffThreadDecodeScript(cx, options, range, callback, callbackData);
}
JS_PUBLIC_API(bool)
JS::DecodeMultiOffThreadScripts(JSContext* cx, const ReadOnlyCompileOptions& options,
TranscodeSources& sources,
OffThreadCompileCallback callback, void* callbackData)
{
#ifdef DEBUG
size_t length = 0;
for (auto& source : sources) {
length += source.range.length();
}
MOZ_ASSERT(CanCompileOffThread(cx, options, length));
#endif
return StartOffThreadDecodeMultiScripts(cx, options, sources, callback, callbackData);
}
JS_PUBLIC_API(JSScript*)
JS::FinishOffThreadScriptDecoder(JSContext* cx, void* token)
{
@ -4240,6 +4255,22 @@ JS::CancelOffThreadScriptDecoder(JSContext* cx, void* token)
HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::ScriptDecode, token);
}
JS_PUBLIC_API(bool)
JS::FinishMultiOffThreadScriptsDecoder(JSContext* cx, void* token, MutableHandle<ScriptVector> scripts)
{
MOZ_ASSERT(cx);
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
return HelperThreadState().finishMultiScriptsDecodeTask(cx, token, scripts);
}
JS_PUBLIC_API(void)
JS::CancelMultiOffThreadScriptsDecoder(JSContext* cx, void* token)
{
MOZ_ASSERT(cx);
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
HelperThreadState().cancelParseTask(cx->runtime(), ParseTaskKind::MultiScriptsDecode, token);
}
JS_PUBLIC_API(bool)
JS_CompileScript(JSContext* cx, const char* ascii, size_t length,
const JS::CompileOptions& options, MutableHandleScript script)

View File

@ -841,6 +841,8 @@ class MOZ_STACK_CLASS SourceBufferHolder final
bool ownsChars_;
};
struct TranscodeSource;
} /* namespace JS */
/************************************************************************/
@ -4301,6 +4303,17 @@ FinishOffThreadScriptDecoder(JSContext* cx, void* token);
extern JS_PUBLIC_API(void)
CancelOffThreadScriptDecoder(JSContext* cx, void* token);
extern JS_PUBLIC_API(bool)
DecodeMultiOffThreadScripts(JSContext* cx, const ReadOnlyCompileOptions& options,
mozilla::Vector<TranscodeSource>& sources,
OffThreadCompileCallback callback, void* callbackData);
extern JS_PUBLIC_API(bool)
FinishMultiOffThreadScriptsDecoder(JSContext* cx, void* token, JS::MutableHandle<JS::ScriptVector> scripts);
extern JS_PUBLIC_API(void)
CancelMultiOffThreadScriptsDecoder(JSContext* cx, void* token);
/**
* Compile a function with envChain plus the global as its scope chain.
* envChain must contain objects in the current compartment of cx. The actual
@ -6187,6 +6200,19 @@ class MOZ_RAII AutoHideScriptedCaller
typedef mozilla::Vector<uint8_t> TranscodeBuffer;
typedef mozilla::Range<uint8_t> TranscodeRange;
struct TranscodeSource
{
TranscodeSource(const TranscodeRange& range_, const char* file, uint32_t line)
: range(range_), filename(file), lineno(line)
{}
const TranscodeRange range;
const char* filename;
const uint32_t lineno;
};
typedef mozilla::Vector<JS::TranscodeSource> TranscodeSources;
enum TranscodeResult
{
// Successful encoding / decoding.

View File

@ -300,27 +300,43 @@ static const JSClass parseTaskGlobalClass = {
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData)
: kind(kind), options(cx),
: kind(kind), options(cx), data(AsVariant(TwoByteChars(chars, length))),
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
parseGlobal(parseGlobal),
callback(callback), callbackData(callbackData),
script(nullptr), sourceObject(nullptr),
scripts(cx), sourceObjects(cx),
overRecursed(false), outOfMemory(false)
{
data.construct<TwoByteChars>(chars, length);
MOZ_ALWAYS_TRUE(scripts.reserve(scripts.capacity()));
MOZ_ALWAYS_TRUE(sourceObjects.reserve(sourceObjects.capacity()));
}
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData)
: kind(kind), options(cx),
: kind(kind), options(cx), data(AsVariant(range)),
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
parseGlobal(parseGlobal),
callback(callback), callbackData(callbackData),
script(nullptr), sourceObject(nullptr),
scripts(cx), sourceObjects(cx),
overRecursed(false), outOfMemory(false)
{
data.construct<const JS::TranscodeRange>(range);
MOZ_ALWAYS_TRUE(scripts.reserve(scripts.capacity()));
MOZ_ALWAYS_TRUE(sourceObjects.reserve(sourceObjects.capacity()));
}
ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
JS::TranscodeSources& sources,
JS::OffThreadCompileCallback callback, void* callbackData)
: kind(kind), options(cx), data(AsVariant(&sources)),
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
parseGlobal(parseGlobal),
callback(callback), callbackData(callbackData),
scripts(cx), sourceObjects(cx),
overRecursed(false), outOfMemory(false)
{
MOZ_ALWAYS_TRUE(scripts.reserve(scripts.capacity()));
MOZ_ALWAYS_TRUE(sourceObjects.reserve(sourceObjects.capacity()));
}
bool
@ -341,7 +357,7 @@ ParseTask::activate(JSRuntime* rt)
bool
ParseTask::finish(JSContext* cx)
{
if (sourceObject) {
for (auto& sourceObject : sourceObjects) {
RootedScriptSource sso(cx, sourceObject);
if (!ScriptSourceObject::initFromOptions(cx, sso, options))
return false;
@ -370,10 +386,8 @@ ParseTask::trace(JSTracer* trc)
}
TraceManuallyBarrieredEdge(trc, &parseGlobal, "ParseTask::parseGlobal");
if (script)
TraceManuallyBarrieredEdge(trc, &script, "ParseTask::script");
if (sourceObject)
TraceManuallyBarrieredEdge(trc, &sourceObject, "ParseTask::sourceObject");
scripts.trace(trc);
sourceObjects.trace(trc);
}
ScriptParseTask::ScriptParseTask(JSContext* cx, JSObject* parseGlobal,
@ -387,11 +401,17 @@ ScriptParseTask::ScriptParseTask(JSContext* cx, JSObject* parseGlobal,
void
ScriptParseTask::parse(JSContext* cx)
{
auto& range = data.ref<TwoByteChars>();
auto& range = data.as<TwoByteChars>();
SourceBufferHolder srcBuf(range.begin().get(), range.length(), SourceBufferHolder::NoOwnership);
script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global,
options, srcBuf,
/* sourceObjectOut = */ &sourceObject);
Rooted<ScriptSourceObject*> sourceObject(cx);
JSScript* script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global,
options, srcBuf,
/* sourceObjectOut = */ &sourceObject.get());
if (script)
scripts.infallibleAppend(script);
if (sourceObject)
sourceObjects.infallibleAppend(sourceObject);
}
ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
@ -405,11 +425,16 @@ ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
void
ModuleParseTask::parse(JSContext* cx)
{
auto& range = data.ref<TwoByteChars>();
auto& range = data.as<TwoByteChars>();
SourceBufferHolder srcBuf(range.begin().get(), range.length(), SourceBufferHolder::NoOwnership);
ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject);
if (module)
script = module->script();
Rooted<ScriptSourceObject*> sourceObject(cx);
ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject.get());
if (module) {
scripts.infallibleAppend(module->script());
if (sourceObject)
sourceObjects.infallibleAppend(sourceObject);
}
}
ScriptDecodeTask::ScriptDecodeTask(JSContext* cx, JSObject* parseGlobal,
@ -424,14 +449,54 @@ void
ScriptDecodeTask::parse(JSContext* cx)
{
RootedScript resultScript(cx);
XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject,
data.ref<const JS::TranscodeRange>());
Rooted<ScriptSourceObject*> sourceObject(cx);
XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject.get(),
data.as<const JS::TranscodeRange>());
decoder.codeScript(&resultScript);
MOZ_ASSERT(bool(resultScript) == (decoder.resultCode() == JS::TranscodeResult_Ok));
if (decoder.resultCode() == JS::TranscodeResult_Ok) {
script = resultScript.get();
} else {
sourceObject = nullptr;
scripts.infallibleAppend(resultScript);
if (sourceObject)
sourceObjects.infallibleAppend(sourceObject);
}
}
MultiScriptsDecodeTask::MultiScriptsDecodeTask(JSContext* cx, JSObject* parseGlobal,
JS::TranscodeSources& sources,
JS::OffThreadCompileCallback callback, void* callbackData)
: ParseTask(ParseTaskKind::MultiScriptsDecode, cx, parseGlobal,
sources, callback, callbackData)
{
}
void
MultiScriptsDecodeTask::parse(JSContext* cx)
{
auto sources = data.as<JS::TranscodeSources*>();
if (!scripts.reserve(sources->length()) ||
!sourceObjects.reserve(sources->length()))
{
return;
}
for (auto& source : *sources) {
CompileOptions opts(cx, options);
opts.setFileAndLine(source.filename, source.lineno);
RootedScript resultScript(cx);
Rooted<ScriptSourceObject*> sourceObject(cx);
XDROffThreadDecoder decoder(cx, alloc, &opts, &sourceObject.get(), source.range);
decoder.codeScript(&resultScript);
MOZ_ASSERT(bool(resultScript) == (decoder.resultCode() == JS::TranscodeResult_Ok));
if (decoder.resultCode() != JS::TranscodeResult_Ok)
break;
MOZ_ASSERT(resultScript);
scripts.infallibleAppend(resultScript);
sourceObjects.infallibleAppend(sourceObject);
}
}
@ -666,6 +731,17 @@ js::StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& opti
return StartOffThreadParseTask(cx, options, ParseTaskKind::ScriptDecode, functor);
}
bool
js::StartOffThreadDecodeMultiScripts(JSContext* cx, const ReadOnlyCompileOptions& options,
JS::TranscodeSources& sources,
JS::OffThreadCompileCallback callback, void* callbackData)
{
auto functor = [&](JSObject* global) -> MultiScriptsDecodeTask* {
return cx->new_<MultiScriptsDecodeTask>(cx, global, sources, callback, callbackData);
};
return StartOffThreadParseTask(cx, options, ParseTaskKind::MultiScriptsDecode, functor);
}
void
js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt)
{
@ -1300,8 +1376,9 @@ GlobalHelperThreadState::removeFinishedParseTask(ParseTaskKind kind, void* token
MOZ_CRASH("Invalid ParseTask token");
}
JSScript*
GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void* token)
template <typename F, typename>
bool
GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void* token, F&& finishCallback)
{
MOZ_ASSERT(cx->compartment());
@ -1312,21 +1389,23 @@ GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void
Rooted<GlobalObject*> global(cx, &cx->global()->as<GlobalObject>());
if (!EnsureParserCreatedClasses(cx, kind)) {
LeaveParseTaskZone(cx->runtime(), parseTask);
return nullptr;
return false;
}
mergeParseTaskCompartment(cx, parseTask, global, cx->compartment());
RootedScript script(cx, parseTask->script);
releaseAssertSameCompartment(cx, script);
bool ok = finishCallback(parseTask);
if (!parseTask->finish(cx))
return nullptr;
for (auto& script : parseTask->scripts)
releaseAssertSameCompartment(cx, script);
if (!parseTask->finish(cx) || !ok)
return false;
// Report out of memory errors eagerly, or errors could be malformed.
if (parseTask->outOfMemory) {
ReportOutOfMemory(cx);
return nullptr;
return false;
}
// Report any error or warnings generated during the parse, and inform the
@ -1336,6 +1415,26 @@ GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void
if (parseTask->overRecursed)
ReportOverRecursed(cx);
if (cx->isExceptionPending())
return false;
return true;
}
JSScript*
GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void* token)
{
JS::RootedScript script(cx);
bool ok = finishParseTask(cx, kind, token, [&script] (ParseTask* parseTask) {
MOZ_RELEASE_ASSERT(parseTask->scripts.length() <= 1);
if (parseTask->scripts.length() > 0)
script = parseTask->scripts[0];
return true;
});
if (!ok)
return nullptr;
if (!script) {
@ -1351,6 +1450,45 @@ GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void
return script;
}
bool
GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind, void* token,
MutableHandle<ScriptVector> scripts)
{
size_t expectedLength = 0;
bool ok = finishParseTask(cx, kind, token, [&scripts, &expectedLength] (ParseTask* parseTask) {
expectedLength = parseTask->data.as<JS::TranscodeSources*>()->length();
if (!scripts.reserve(parseTask->scripts.length()))
return false;
for (auto& script : parseTask->scripts)
scripts.infallibleAppend(script);
return true;
});
if (!ok)
return false;
if (scripts.length() != expectedLength) {
// No error was reported, but fewer scripts produced than expected.
// Assume we hit out of memory.
ReportOutOfMemory(cx);
return false;
}
// The Debugger only needs to be told about the topmost script that was compiled.
JS::RootedScript rooted(cx);
for (auto& script : scripts) {
MOZ_ASSERT(script->isGlobalCode());
rooted = script;
Debugger::onNewScript(cx, rooted);
}
return true;
}
JSScript*
GlobalHelperThreadState::finishScriptParseTask(JSContext* cx, void* token)
{
@ -1367,6 +1505,12 @@ GlobalHelperThreadState::finishScriptDecodeTask(JSContext* cx, void* token)
return script;
}
bool
GlobalHelperThreadState::finishMultiScriptsDecodeTask(JSContext* cx, void* token, MutableHandle<ScriptVector> scripts)
{
return finishParseTask(cx, ParseTaskKind::MultiScriptsDecode, token, scripts);
}
JSObject*
GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
{

View File

@ -15,9 +15,9 @@
#include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/MaybeOneOf.h"
#include "mozilla/PodOperations.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/TypeTraits.h"
#include "mozilla/Variant.h"
#include "jsapi.h"
@ -53,7 +53,8 @@ enum class ParseTaskKind
{
Script,
Module,
ScriptDecode
ScriptDecode,
MultiScriptsDecode
};
// Per-process state for off thread work items.
@ -262,7 +263,19 @@ class GlobalHelperThreadState
return bool(numWasmFailedJobs);
}
template <
typename F,
typename = typename mozilla::EnableIf<
// Matches when the type is a function or lambda with the signature `bool(ParseTask*)`
mozilla::IsSame<bool, decltype((*(F*)nullptr)((ParseTask*)nullptr))>::value
>::Type
>
bool finishParseTask(JSContext* cx, ParseTaskKind kind, void* token, F&& finishCallback);
JSScript* finishParseTask(JSContext* cx, ParseTaskKind kind, void* token);
bool finishParseTask(JSContext* cx, ParseTaskKind kind, void* token, MutableHandle<ScriptVector> scripts);
void cancelParseTask(JSRuntime* rt, ParseTaskKind kind, void* token);
void mergeParseTaskCompartment(JSContext* cx, ParseTask* parseTask,
@ -286,6 +299,7 @@ class GlobalHelperThreadState
public:
JSScript* finishScriptParseTask(JSContext* cx, void* token);
JSScript* finishScriptDecodeTask(JSContext* cx, void* token);
bool finishMultiScriptsDecodeTask(JSContext* cx, void* token, MutableHandle<ScriptVector> scripts);
JSObject* finishModuleParseTask(JSContext* cx, void* token);
bool hasActiveThreads(const AutoLockHelperThreadState&);
@ -538,6 +552,11 @@ StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData);
bool
StartOffThreadDecodeMultiScripts(JSContext* cx, const ReadOnlyCompileOptions& options,
JS::TranscodeSources& sources,
JS::OffThreadCompileCallback callback, void* callbackData);
/*
* Called at the end of GC to enqueue any Parse tasks that were waiting on an
* atoms-zone GC to finish.
@ -595,7 +614,9 @@ struct ParseTask
ParseTaskKind kind;
OwningCompileOptions options;
mozilla::MaybeOneOf<const JS::TranscodeRange, JS::TwoByteChars> data;
mozilla::Variant<const JS::TranscodeRange,
JS::TwoByteChars,
JS::TranscodeSources*> data;
LifoAlloc alloc;
@ -606,13 +627,13 @@ struct ParseTask
JS::OffThreadCompileCallback callback;
void* callbackData;
// Holds the final script between the invocation of the callback and the
// Holds the final scripts between the invocation of the callback and the
// point where FinishOffThreadScript is called, which will destroy the
// ParseTask.
JSScript* script;
GCVector<JSScript*, 1> scripts;
// Holds the ScriptSourceObject generated for the script compilation.
ScriptSourceObject* sourceObject;
// Holds the ScriptSourceObjects generated for the script compilation.
GCVector<ScriptSourceObject*, 1> sourceObjects;
// Any errors or warnings produced during compilation. These are reported
// when finishing the script.
@ -626,6 +647,9 @@ struct ParseTask
ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
const JS::TranscodeRange& range,
JS::OffThreadCompileCallback callback, void* callbackData);
ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
JS::TranscodeSources& sources,
JS::OffThreadCompileCallback callback, void* callbackData);
bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
void activate(JSRuntime* rt);
@ -665,6 +689,14 @@ struct ScriptDecodeTask : public ParseTask
void parse(JSContext* cx) override;
};
struct MultiScriptsDecodeTask : public ParseTask
{
MultiScriptsDecodeTask(JSContext* cx, JSObject* parseGlobal,
JS::TranscodeSources& sources,
JS::OffThreadCompileCallback callback, void* callbackData);
void parse(JSContext* cx) override;
};
// Return whether, if a new parse task was started, it would need to wait for
// an in-progress GC to complete before starting.
extern bool

View File

@ -2553,6 +2553,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("intl_CompareStrings", intl_CompareStrings, 3,0),
JS_FN("intl_DateTimeFormat", intl_DateTimeFormat, 2,0),
JS_FN("intl_DateTimeFormat_availableLocales", intl_DateTimeFormat_availableLocales, 0,0),
JS_FN("intl_defaultCalendar", intl_defaultCalendar, 1,0),
JS_FN("intl_defaultTimeZone", intl_defaultTimeZone, 0,0),
JS_FN("intl_defaultTimeZoneOffset", intl_defaultTimeZoneOffset, 0,0),
JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0),

View File

@ -269,19 +269,16 @@ ScriptPreloader::Cleanup()
void
ScriptPreloader::InvalidateCache()
{
mMonitor.AssertNotCurrentThreadOwns();
MonitorAutoLock mal(mMonitor);
mCacheInvalidated = true;
for (auto& script : IterHash(mScripts)) {
// We can only purge finished scripts here. Async scripts that are
// still being parsed off-thread have a non-refcounted reference to
// this script, which needs to stay alive until they finish parsing.
if (script->mReadyToExecute) {
script->Cancel();
script.Remove();
}
}
mParsingScripts.clearAndFree();
while (auto script = mPendingScripts.getFirst())
script->remove();
for (auto& script : IterHash(mScripts))
script.Remove();
// If we've already finished saving the cache at this point, start a new
// delayed save operation. This will write out an empty cache file in place
@ -437,13 +434,13 @@ ScriptPreloader::InitCacheInternal()
return Err(NS_ERROR_UNEXPECTED);
}
AutoTArray<CachedScript*, 256> scripts;
{
auto cleanup = MakeScopeExit([&] () {
mScripts.Clear();
});
LinkedList<CachedScript> scripts;
Range<uint8_t> header(data, data + headerSize);
data += headerSize;
@ -468,7 +465,14 @@ ScriptPreloader::InitCacheInternal()
script->mXDRRange.emplace(scriptData, scriptData + script->mSize);
scripts.AppendElement(script.get());
// Don't pre-decode the script unless it was used in this process type during the
// previous session.
if (script->mOriginalProcessTypes.contains(CurrentProcessType())) {
scripts.insertBack(script.get());
} else {
script->mReadyToExecute = true;
}
mScripts.Put(script->mCachePath, script.get());
Unused << script.release();
}
@ -477,32 +481,11 @@ ScriptPreloader::InitCacheInternal()
return Err(NS_ERROR_UNEXPECTED);
}
mPendingScripts = Move(scripts);
cleanup.release();
}
AutoJSAPI jsapi;
MOZ_RELEASE_ASSERT(jsapi.Init(xpc::CompilationScope()));
JSContext* cx = jsapi.cx();
auto start = TimeStamp::Now();
LOG(Info, "Off-thread decoding scripts...\n");
JS::CompileOptions options(cx, JSVERSION_LATEST);
for (auto& script : scripts) {
// Only async decode scripts which have been used in this process type.
if (script->mProcessTypes.contains(CurrentProcessType()) &&
script->AsyncDecodable() &&
JS::CanCompileOffThread(cx, options, script->mSize)) {
DecodeScriptOffThread(cx, script);
} else {
script->mReadyToExecute = true;
}
}
LOG(Info, "Initialized decoding in %fms\n",
(TimeStamp::Now() - start).ToMilliseconds());
DecodeNextBatch(OFF_THREAD_FIRST_CHUNK_SIZE);
return Ok();
}
@ -530,39 +513,37 @@ ScriptPreloader::PrepareCacheWrite()
return;
}
bool found = Find(IterHash(mScripts), [] (CachedScript* script) {
return (script->mStatus == ScriptStatus::Restored ||
!script->HasRange() || script->HasArray());
});
if (!found) {
mSaveComplete = true;
return;
}
AutoSafeJSAPI jsapi;
bool found = false;
for (auto& script : IterHash(mScripts, Match<ScriptStatus::Saved>())) {
// Don't write any scripts that are also in the child cache. They'll be
// loaded from the child cache in that case, so there's no need to write
// them twice.
CachedScript* childScript = mChildCache ? mChildCache->mScripts.Get(script->mCachePath) : nullptr;
if (childScript) {
if (childScript->mStatus == ScriptStatus::Saved) {
childScript->UpdateLoadTime(script->mLoadTime);
childScript->mProcessTypes += script->mProcessTypes;
} else {
childScript = nullptr;
}
if (childScript && !childScript->mProcessTypes.isEmpty()) {
childScript->UpdateLoadTime(script->mLoadTime);
childScript->mProcessTypes += script->mProcessTypes;
script.Remove();
continue;
}
if (childScript || (!script->mSize && !script->XDREncode(jsapi.cx()))) {
if (!(script->mProcessTypes == script->mOriginalProcessTypes)) {
// Note: EnumSet doesn't support operator!=, hence the weird form above.
found = true;
}
if (!script->mSize && !script->XDREncode(jsapi.cx())) {
script.Remove();
} else {
script->mSize = script->Range().length();
}
}
if (!found) {
mSaveComplete = true;
return;
}
mDataPrepared = true;
}
@ -693,9 +674,7 @@ ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
auto script = mScripts.LookupOrAdd(cachePath, *this, url, cachePath, jsscript);
if (script->mStatus == ScriptStatus::Restored) {
script->mStatus = ScriptStatus::Saved;
if (!script->mScript) {
MOZ_ASSERT(jsscript);
script->mScript = jsscript;
script->mReadyToExecute = true;
@ -712,20 +691,14 @@ ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
{
auto script = mScripts.LookupOrAdd(cachePath, *this, url, cachePath, nullptr);
if (script->mStatus == ScriptStatus::Restored) {
script->mStatus = ScriptStatus::Saved;
if (!script->HasRange()) {
MOZ_ASSERT(!script->HasArray());
script->mReadyToExecute = true;
} else {
if (!script->HasRange()) {
MOZ_ASSERT(!script->HasArray());
script->mSize = xdrData.Length();
script->mXDRData.construct<nsTArray<uint8_t>>(Forward<nsTArray<uint8_t>>(xdrData));
script->mSize = xdrData.Length();
script->mXDRData.construct<nsTArray<uint8_t>>(Forward<nsTArray<uint8_t>>(xdrData));
auto& data = script->Array();
script->mXDRRange.emplace(data.Elements(), data.Length());
}
auto& data = script->Array();
script->mXDRRange.emplace(data.Elements(), data.Length());
}
if (!script->mSize && !script->mScript) {
@ -770,12 +743,25 @@ ScriptPreloader::GetCachedScript(JSContext* cx, const nsCString& path)
JSScript*
ScriptPreloader::WaitForCachedScript(JSContext* cx, CachedScript* script)
{
// Check for finished operations before locking so that we can move onto
// decoding the next batch as soon as possible after the pending batch is
// ready. If we wait until we hit an unfinished script, we wind up having at
// most one batch of buffered scripts, and occasionally under-running that
// buffer.
FinishOffThreadDecode();
if (!script->mReadyToExecute) {
LOG(Info, "Must wait for async script load: %s\n", script->mURL.get());
auto start = TimeStamp::Now();
mMonitor.AssertNotCurrentThreadOwns();
MonitorAutoLock mal(mMonitor);
// Check for finished operations again *after* locking, or we may race
// against mToken being set between our last check and the time we
// entered the mutex.
FinishOffThreadDecode();
if (!script->mReadyToExecute && script->mSize < MAX_MAINTHREAD_DECODE_SIZE) {
LOG(Info, "Script is small enough to recompile on main thread\n");
@ -783,68 +769,178 @@ ScriptPreloader::WaitForCachedScript(JSContext* cx, CachedScript* script)
} else {
while (!script->mReadyToExecute) {
mal.Wait();
MonitorAutoUnlock mau(mMonitor);
FinishOffThreadDecode();
}
}
LOG(Info, "Waited %fms\n", (TimeStamp::Now() - start).ToMilliseconds());
LOG(Debug, "Waited %fms\n", (TimeStamp::Now() - start).ToMilliseconds());
}
return script->GetJSScript(cx);
}
void
ScriptPreloader::DecodeScriptOffThread(JSContext* cx, CachedScript* script)
/* static */ void
ScriptPreloader::OffThreadDecodeCallback(void* token, void* context)
{
JS::CompileOptions options(cx, JSVERSION_LATEST);
auto cache = static_cast<ScriptPreloader*>(context);
options.setNoScriptRval(true)
.setFileAndLine(script->mURL.get(), 1);
cache->mMonitor.AssertNotCurrentThreadOwns();
MonitorAutoLock mal(cache->mMonitor);
if (!JS::DecodeOffThreadScript(cx, options, script->Range(),
OffThreadDecodeCallback,
static_cast<void*>(script))) {
// First notify any tasks that are already waiting on scripts, since they'll
// be blocking the main thread, and prevent any runnables from executing.
cache->mToken = token;
mal.NotifyAll();
// If nothing processed the token, and we don't already have a pending
// runnable, then dispatch a new one to finish the processing on the main
// thread as soon as possible.
if (cache->mToken && !cache->mFinishDecodeRunnablePending) {
cache->mFinishDecodeRunnablePending = true;
NS_DispatchToMainThread(
NewRunnableMethod(cache, &ScriptPreloader::DoFinishOffThreadDecode));
}
}
void
ScriptPreloader::DoFinishOffThreadDecode()
{
mFinishDecodeRunnablePending = false;
FinishOffThreadDecode();
}
void
ScriptPreloader::FinishOffThreadDecode()
{
if (!mToken) {
return;
}
auto cleanup = MakeScopeExit([&] () {
mToken = nullptr;
mParsingSources.clear();
mParsingScripts.clear();
DecodeNextBatch(OFF_THREAD_CHUNK_SIZE);
});
AutoJSAPI jsapi;
MOZ_RELEASE_ASSERT(jsapi.Init(xpc::CompilationScope()));
JSContext* cx = jsapi.cx();
JS::Rooted<JS::ScriptVector> jsScripts(cx, JS::ScriptVector(cx));
// If this fails, we still need to mark the scripts as finished. Any that
// weren't successfully compiled in this operation (which should never
// happen under ordinary circumstances) will be re-decoded on the main
// thread, and raise the appropriate errors when they're executed.
//
// The exception from the off-thread decode operation will be reported when
// we pop the AutoJSAPI off the stack.
Unused << JS::FinishMultiOffThreadScriptsDecoder(cx, mToken, &jsScripts);
unsigned i = 0;
for (auto script : mParsingScripts) {
LOG(Debug, "Finished off-thread decode of %s\n", script->mURL.get());
if (i < jsScripts.length())
script->mScript = jsScripts[i++];
script->mReadyToExecute = true;
}
}
void
ScriptPreloader::CancelOffThreadParse(void* token)
ScriptPreloader::DecodeNextBatch(size_t chunkSize)
{
AutoSafeJSAPI jsapi;
JS::CancelOffThreadScriptDecoder(jsapi.cx(), token);
}
MOZ_ASSERT(mParsingSources.length() == 0);
MOZ_ASSERT(mParsingScripts.length() == 0);
/* static */ void
ScriptPreloader::OffThreadDecodeCallback(void* token, void* context)
{
auto script = static_cast<CachedScript*>(context);
auto cleanup = MakeScopeExit([&] () {
mParsingScripts.clearAndFree();
mParsingSources.clearAndFree();
});
MonitorAutoLock mal(script->mCache.mMonitor);
auto start = TimeStamp::Now();
LOG(Debug, "Off-thread decoding scripts...\n");
if (script->mReadyToExecute) {
// We've already executed this script on the main thread, and opted to
// main thread decode it rather waiting for off-thread decoding to
// finish. So just cancel the off-thread parse rather than completing
// it.
NS_DispatchToMainThread(
NewRunnableMethod<void*>(&script->mCache,
&ScriptPreloader::CancelOffThreadParse,
token));
size_t size = 0;
for (CachedScript* next = mPendingScripts.getFirst(); next;) {
auto script = next;
next = script->getNext();
// Skip any scripts that we decoded on the main thread rather than
// waiting for an off-thread operation to complete.
if (script->mReadyToExecute) {
script->remove();
continue;
}
// If we have enough data for one chunk and this script would put us
// over our chunk size limit, we're done.
if (size > SMALL_SCRIPT_CHUNK_THRESHOLD &&
size + script->mSize > chunkSize) {
break;
}
if (!mParsingScripts.append(script) ||
!mParsingSources.emplaceBack(script->Range(), script->mURL.get(), 0)) {
break;
}
LOG(Debug, "Beginning off-thread decode of script %s (%u bytes)\n",
script->mURL.get(), script->mSize);
script->remove();
size += script->mSize;
}
if (size == 0 && mPendingScripts.isEmpty()) {
return;
}
script->mToken = token;
script->mReadyToExecute = true;
AutoJSAPI jsapi;
MOZ_RELEASE_ASSERT(jsapi.Init(xpc::CompilationScope()));
JSContext* cx = jsapi.cx();
mal.NotifyAll();
JS::CompileOptions options(cx, JSVERSION_LATEST);
options.setNoScriptRval(true);
if (!JS::CanCompileOffThread(cx, options, size) ||
!JS::DecodeMultiOffThreadScripts(cx, options, mParsingSources,
OffThreadDecodeCallback,
static_cast<void*>(this))) {
// If we fail here, we don't move on to process the next batch, so make
// sure we don't have any other scripts left to process.
MOZ_ASSERT(mPendingScripts.isEmpty());
for (auto script : mPendingScripts) {
script->mReadyToExecute = true;
}
LOG(Info, "Can't decode %lu bytes of scripts off-thread", (unsigned long)size);
for (auto script : mParsingScripts) {
script->mReadyToExecute = true;
}
return;
}
cleanup.release();
LOG(Debug, "Initialized decoding of %u scripts (%u bytes) in %fms\n",
(unsigned)mParsingSources.length(), (unsigned)size, (TimeStamp::Now() - start).ToMilliseconds());
}
ScriptPreloader::CachedScript::CachedScript(ScriptPreloader& cache, InputBuffer& buf)
: mCache(cache)
, mStatus(ScriptStatus::Restored)
{
Code(buf);
// Swap the mProcessTypes and mOriginalProcessTypes values, since we want to
// start with an empty set of processes loaded into for this session, and
// compare against last session's values later.
mOriginalProcessTypes = mProcessTypes;
mProcessTypes = {};
}
bool
@ -864,20 +960,6 @@ ScriptPreloader::CachedScript::XDREncode(JSContext* cx)
return false;
}
void
ScriptPreloader::CachedScript::Cancel()
{
if (mToken) {
mCache.mMonitor.AssertCurrentThreadOwns();
AutoSafeJSAPI jsapi;
JS::CancelOffThreadScriptDecoder(jsapi.cx(), mToken);
mReadyToExecute = true;
mToken = nullptr;
}
}
JSScript*
ScriptPreloader::CachedScript::GetJSScript(JSContext* cx)
{
@ -886,35 +968,27 @@ ScriptPreloader::CachedScript::GetJSScript(JSContext* cx)
return mScript;
}
// If we have no token at this point, the script was too small to decode
// If we have no script at this point, the script was too small to decode
// off-thread, or it was needed before the off-thread compilation was
// finished, and is small enough to decode on the main thread rather than
// wait for the off-thread decoding to finish. In either case, we decode
// it synchronously the first time it's needed.
if (!mToken) {
MOZ_ASSERT(HasRange());
MOZ_ASSERT(HasRange());
JS::RootedScript script(cx);
if (JS::DecodeScript(cx, Range(), &script)) {
mScript = script;
auto start = TimeStamp::Now();
LOG(Info, "Decoding script %s on main thread...\n", mURL.get());
if (mCache.mSaveComplete) {
FreeData();
}
JS::RootedScript script(cx);
if (JS::DecodeScript(cx, Range(), &script)) {
mScript = script;
if (mCache.mSaveComplete) {
FreeData();
}
return mScript;
}
Maybe<JSAutoCompartment> ac;
if (JS::CompartmentCreationOptionsRef(cx).addonIdOrNull()) {
// Make sure we never try to finish the parse in a compartment with an
// add-on ID, it wasn't started in one.
ac.emplace(cx, xpc::CompilationScope());
}
LOG(Debug, "Finished decoding in %fms", (TimeStamp::Now() - start).ToMilliseconds());
mScript = JS::FinishOffThreadScriptDecoder(cx, mToken);
mToken = nullptr;
return mScript;
}

View File

@ -137,7 +137,7 @@ private:
// the next session's cache file. If it was compiled in this session, its
// mXDRRange will initially be empty, and its mXDRData buffer will be
// populated just before it is written to the cache file.
class CachedScript
class CachedScript : public LinkedListElement<CachedScript>
{
public:
CachedScript(CachedScript&&) = default;
@ -146,7 +146,6 @@ private:
: mCache(cache)
, mURL(url)
, mCachePath(cachePath)
, mStatus(ScriptStatus::Saved)
, mScript(script)
, mReadyToExecute(true)
{}
@ -155,29 +154,25 @@ private:
~CachedScript() = default;
ScriptStatus Status() const
{
return mProcessTypes.isEmpty() ? ScriptStatus::Restored : ScriptStatus::Saved;
}
// For use with nsTArray::Sort.
//
// Orders scripts by:
//
// 1) Async-decoded scripts before sync-decoded scripts, since the
// former are needed immediately at startup, and should be stored
// contiguously.
// 2) Script load time, so that scripts which are needed earlier are
// stored earlier, and scripts needed at approximately the same
// time are stored approximately contiguously.
// Orders scripts by script load time, so that scripts which are needed
// earlier are stored earlier, and scripts needed at approximately the
// same time are stored approximately contiguously.
struct Comparator
{
bool Equals(const CachedScript* a, const CachedScript* b) const
{
return (a->AsyncDecodable() == b->AsyncDecodable() &&
a->mLoadTime == b->mLoadTime);
return a->mLoadTime == b->mLoadTime;
}
bool LessThan(const CachedScript* a, const CachedScript* b) const
{
if (a->AsyncDecodable() != b->AsyncDecodable()) {
return a->AsyncDecodable();
}
return a->mLoadTime < b->mLoadTime;
}
};
@ -188,14 +183,12 @@ private:
virtual bool Matches(CachedScript* script)
{
return script->mStatus == mStatus;
return script->Status() == mStatus;
}
const ScriptStatus mStatus;
};
void Cancel();
void FreeData()
{
// If the script data isn't mmapped, we need to release both it
@ -213,8 +206,6 @@ private:
}
}
bool AsyncDecodable() const { return mSize > MIN_OFFTHREAD_SIZE; }
// Encodes this script into XDR data, and stores the result in mXDRData.
// Returns true on success, false on failure.
bool XDREncode(JSContext* cx);
@ -292,8 +283,6 @@ private:
// The size of this script's encoded XDR data.
uint32_t mSize = 0;
ScriptStatus mStatus;
TimeStamp mLoadTime{};
JS::Heap<JSScript*> mScript;
@ -304,13 +293,13 @@ private:
// whenever it is first executed.
bool mReadyToExecute = false;
// The off-thread decode token for a completed off-thread decode, which
// has not yet been finalized on the main thread.
void* mToken = nullptr;
// The set of processes in which this script has been used.
EnumSet<ProcessType> mProcessTypes{};
// The set of processes which the script was loaded into during the
// last session, as read from the cache file.
EnumSet<ProcessType> mOriginalProcessTypes{};
// The read-only XDR data for this script, which was either read from an
// existing cache file, or generated by encoding a script which was
// compiled during this session.
@ -328,15 +317,31 @@ private:
return &matcher;
}
// There's a trade-off between the time it takes to setup an off-thread
// decode and the time we save by doing the decode off-thread. At this
// point, the setup is quite expensive, and 20K is about where we start to
// see an improvement rather than a regression.
// There's a significant setup cost for each off-thread decode operation,
// so scripts are decoded in chunks to minimize the overhead. There's a
// careful balancing act in choosing the size of chunks, to minimize the
// number of decode operations, while also minimizing the number of buffer
// underruns that require the main thread to wait for a script to finish
// decoding.
//
// This also means that we get much better performance loading one big
// script than several small scripts, since the setup is per-script, and the
// OMT compile is almost always complete by the time we need a given script.
static constexpr int MIN_OFFTHREAD_SIZE = 20 * 1024;
// For the first chunk, we don't have much time between the start of the
// decode operation and the time the first script is needed, so that chunk
// needs to be fairly small. After the first chunk is finished, we have
// some buffered scripts to fall back on, and a lot more breathing room,
// so the chunks can be a bit bigger, but still not too big.
static constexpr int OFF_THREAD_FIRST_CHUNK_SIZE = 128 * 1024;
static constexpr int OFF_THREAD_CHUNK_SIZE = 512 * 1024;
// Ideally, we want every chunk to be smaller than the chunk sizes
// specified above. However, if we have some number of small scripts
// followed by a huge script that would put us over the normal chunk size,
// we're better off processing them as a single chunk.
//
// In order to guarantee that the JS engine will process a chunk
// off-thread, it needs to be at least 100K (which is an implementation
// detail that can change at any time), so make sure that we always hit at
// least that size, with a bit of breathing room to be safe.
static constexpr int SMALL_SCRIPT_CHUNK_THRESHOLD = 128 * 1024;
// The maximum size of scripts to re-decode on the main thread if off-thread
// decoding hasn't finished yet. In practice, we don't hit this very often,
@ -371,11 +376,11 @@ private:
// decodes it synchronously on the main thread, as appropriate.
JSScript* WaitForCachedScript(JSContext* cx, CachedScript* script);
// Begins decoding the given script in a background thread.
void DecodeScriptOffThread(JSContext* cx, CachedScript* script);
void DecodeNextBatch(size_t chunkSize);
static void OffThreadDecodeCallback(void* token, void* context);
void CancelOffThreadParse(void* token);
void FinishOffThreadDecode();
void DoFinishOffThreadDecode();
size_t ShallowHeapSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
@ -406,6 +411,22 @@ private:
bool mDataPrepared = false;
bool mCacheInvalidated = false;
// The list of scripts that we read from the initial startup cache file,
// but have yet to initiate a decode task for.
LinkedList<CachedScript> mPendingScripts;
// The lists of scripts and their sources that make up the chunk currently
// being decoded in a background thread.
JS::TranscodeSources mParsingSources;
Vector<CachedScript*> mParsingScripts;
// The token for the completed off-thread decode task.
void* mToken = nullptr;
// True if a runnable has been dispatched to the main thread to finish an
// off-thread decode operation.
bool mFinishDecodeRunnablePending = false;
// The process type of the current process.
static ProcessType sProcessType;

View File

@ -4674,7 +4674,7 @@ PresShell::StyleRuleRemoved(StyleSheet* aStyleSheet)
RecordStyleSheetChange(aStyleSheet);
}
nsIFrame*
nsPlaceholderFrame*
PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame) const
{
return mFrameConstructor->GetPlaceholderFrameFor(aFrame);

View File

@ -122,7 +122,7 @@ public:
virtual nsIPageSequenceFrame* GetPageSequenceFrame() const override;
virtual nsCanvasFrame* GetCanvasFrame() const override;
virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const override;
virtual nsPlaceholderFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const override;
virtual void FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
nsFrameState aBitToAdd,
ReflowRootHandling aRootHandling =

View File

@ -1316,6 +1316,14 @@ nsBidiPresUtils::ChildListMayRequireBidi(nsIFrame* aFirstChild,
if (IsBidiLeaf(frame)) {
if (frame->IsTextFrame()) {
// If the frame already has a BidiDataProperty, we know we need to
// perform bidi resolution (even if no bidi content is NOW present --
// we might need to remove the property set by a previous reflow, if
// content has changed; see bug 1366623).
if (frame->Properties().Has(nsIFrame::BidiDataProperty())) {
return true;
}
// Check whether the text frame has any RTL characters; if so, bidi
// resolution will be needed.
nsIContent* content = frame->GetContent();

View File

@ -77,6 +77,7 @@ template<class E> class nsCOMArray;
class AutoWeakFrame;
class WeakFrame;
class nsIScrollableFrame;
class nsPlaceholderFrame;
class gfxContext;
class nsIDOMEvent;
class nsDisplayList;
@ -478,7 +479,7 @@ public:
* Gets the placeholder frame associated with the specified frame. This is
* a helper frame that forwards the request to the frame manager.
*/
virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const = 0;
virtual nsPlaceholderFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const = 0;
/**
* Tell the pres shell that a frame needs to be marked dirty and needs

View File

@ -1224,20 +1224,29 @@ ReflowInput::CalculateBorderPaddingMargin(
* Returns true iff a pre-order traversal of the normal child
* frames rooted at aFrame finds no non-empty frame before aDescendant.
*/
static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
nsIFrame* aDescendant, bool* aFound) {
static bool
AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
nsIFrame* aDescendant,
bool* aFound)
{
if (aFrame == aDescendant) {
*aFound = true;
return true;
}
if (!aFrame->IsSelfEmpty()) {
*aFound = false;
return false;
}
for (nsIFrame* f : aFrame->PrincipalChildList()) {
bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
if (*aFound || !allEmpty) {
return allEmpty;
if (aFrame->IsPlaceholderFrame()) {
auto ph = static_cast<nsPlaceholderFrame*>(aFrame);
MOZ_ASSERT(ph->IsSelfEmpty() && ph->PrincipalChildList().IsEmpty());
ph->SetLineIsEmptySoFar(true);
} else {
if (!aFrame->IsSelfEmpty()) {
*aFound = false;
return false;
}
for (nsIFrame* f : aFrame->PrincipalChildList()) {
bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
if (*aFound || !allEmpty) {
return allEmpty;
}
}
}
*aFound = false;
@ -1256,7 +1265,7 @@ static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
void
ReflowInput::CalculateHypotheticalPosition(
nsPresContext* aPresContext,
nsIFrame* aPlaceholderFrame,
nsPlaceholderFrame* aPlaceholderFrame,
const ReflowInput* cbrs,
nsHypotheticalPosition& aHypotheticalPos,
LayoutFrameType aFrameType) const
@ -1389,15 +1398,31 @@ ReflowInput::CalculateHypotheticalPosition(
// XXXbz the line box is not fully reflowed yet if our
// containing block is relatively positioned...
if (lineBox != iter.End()) {
nsIFrame * firstFrame = lineBox->mFirstChild;
bool found = false;
bool allEmpty = true;
while (firstFrame) { // See bug 223064
allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
aPlaceholderFrame, &found);
if (found || !allEmpty)
break;
firstFrame = firstFrame->GetNextSibling();
nsIFrame* firstFrame = lineBox->mFirstChild;
bool allEmpty = false;
if (firstFrame == aPlaceholderFrame) {
aPlaceholderFrame->SetLineIsEmptySoFar(true);
allEmpty = true;
} else {
auto prev = aPlaceholderFrame->GetPrevSibling();
if (prev && prev->IsPlaceholderFrame()) {
auto ph = static_cast<nsPlaceholderFrame*>(prev);
if (ph->GetLineIsEmptySoFar(&allEmpty)) {
aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty);
}
}
}
if (!allEmpty) {
bool found = false;
while (firstFrame) { // See bug 223064
allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
aPlaceholderFrame, &found);
if (found || !allEmpty) {
break;
}
firstFrame = firstFrame->GetNextSibling();
}
aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty);
}
NS_ASSERTION(firstFrame, "Couldn't find placeholder!");
@ -1581,9 +1606,9 @@ ReflowInput::InitAbsoluteConstraints(nsPresContext* aPresContext,
// have been if it had been in the flow
nsHypotheticalPosition hypotheticalPos;
if ((iStartIsAuto && iEndIsAuto) || (bStartIsAuto && bEndIsAuto)) {
nsIFrame* placeholderFrame =
nsPlaceholderFrame* placeholderFrame =
aPresContext->PresShell()->GetPlaceholderFrameFor(mFrame);
NS_ASSERTION(placeholderFrame, "no placeholder frame");
MOZ_ASSERT(placeholderFrame, "no placeholder frame");
if (placeholderFrame->HasAnyStateBits(
PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)) {

View File

@ -14,12 +14,13 @@
#include "mozilla/Assertions.h"
#include <algorithm>
class nsFloatManager;
struct nsHypotheticalPosition;
class nsIPercentBSizeObserver;
class nsLineLayout;
class nsPlaceholderFrame;
class nsPresContext;
class nsRenderingContext;
class nsFloatManager;
class nsLineLayout;
class nsIPercentBSizeObserver;
struct nsHypotheticalPosition;
/**
* @return aValue clamped to [aMinValue, aMaxValue].
@ -992,7 +993,7 @@ protected:
// mode with the same block direction as the absolute containing block
// (cbrs->frame), though it may differ in inline direction.
void CalculateHypotheticalPosition(nsPresContext* aPresContext,
nsIFrame* aPlaceholderFrame,
nsPlaceholderFrame* aPlaceholderFrame,
const ReflowInput* cbrs,
nsHypotheticalPosition& aHypotheticalPos,
mozilla::LayoutFrameType aFrameType) const;

View File

@ -4209,6 +4209,11 @@ nsBlockFrame::ReflowInlineFrame(BlockReflowInput& aState,
aLineLayout.GetFirstLetterStyleOK() ? "on" : "off");
#endif
if (aFrame->IsPlaceholderFrame()) {
auto ph = static_cast<nsPlaceholderFrame*>(aFrame);
ph->ForgetLineIsEmptySoFar();
}
// Reflow the inline frame
nsReflowStatus frameReflowStatus;
bool pushedFrame;
@ -5659,6 +5664,14 @@ nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
return;
LineIterator line_end = aFrame->LinesEnd();
mLine = aFrame->LinesBegin();
if (mLine != line_end && mLine.next() == line_end &&
!aFrame->HasOverflowLines()) {
// The block has a single line - that must be it!
*aFoundValidLine = true;
return;
}
// Try to use the cursor if it exists, otherwise fall back to the first line
if (nsLineBox* const cursor = aFrame->GetLineCursor()) {
mLine = line_end;

View File

@ -624,6 +624,10 @@ FRAME_STATE_BIT(Placeholder, 24, PLACEHOLDER_FOR_TOPLAYER)
// resolve the actual static position using the alignment properties.
FRAME_STATE_BIT(Placeholder, 25, PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)
// Are all earlier frames on the same block line empty?
FRAME_STATE_BIT(Placeholder, 26, PLACEHOLDER_LINE_IS_EMPTY_SO_FAR)
// Does the above bit have a valid value?
FRAME_STATE_BIT(Placeholder, 27, PLACEHOLDER_HAVE_LINE_IS_EMPTY_SO_FAR)
// == Frame state bits that apply to table cell frames ========================

View File

@ -1952,6 +1952,9 @@ public:
*/
void AddStateBits(nsFrameState aBits) { mState |= aBits; }
void RemoveStateBits(nsFrameState aBits) { mState &= ~aBits; }
void AddOrRemoveStateBits(nsFrameState aBits, bool aVal) {
aVal ? AddStateBits(aBits) : RemoveStateBits(aBits);
}
/**
* Checks if the current frame-state includes all of the listed bits

View File

@ -69,6 +69,7 @@ public:
nsFrameState aTypeBit);
nsPlaceholderFrame(nsStyleContext* aContext, nsFrameState aTypeBit)
: nsFrame(aContext, mozilla::LayoutFrameType::Placeholder)
, mOutOfFlowFrame(nullptr)
{
NS_PRECONDITION(aTypeBit == PLACEHOLDER_FOR_FLOAT ||
aTypeBit == PLACEHOLDER_FOR_ABSPOS ||
@ -121,6 +122,21 @@ public:
virtual bool CanContinueTextRun() const override;
void SetLineIsEmptySoFar(bool aValue) {
AddOrRemoveStateBits(PLACEHOLDER_LINE_IS_EMPTY_SO_FAR, aValue);
AddStateBits(PLACEHOLDER_HAVE_LINE_IS_EMPTY_SO_FAR);
}
bool GetLineIsEmptySoFar(bool* aResult) const {
bool haveValue = HasAnyStateBits(PLACEHOLDER_HAVE_LINE_IS_EMPTY_SO_FAR);
if (haveValue) {
*aResult = HasAnyStateBits(PLACEHOLDER_LINE_IS_EMPTY_SO_FAR);
}
return haveValue;
}
void ForgetLineIsEmptySoFar() {
RemoveStateBits(PLACEHOLDER_HAVE_LINE_IS_EMPTY_SO_FAR);
}
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() override
{
@ -166,7 +182,7 @@ public:
}
protected:
nsIFrame* mOutOfFlowFrame { nullptr };
nsIFrame* mOutOfFlowFrame;
};
#endif /* nsPlaceholderFrame_h___ */

View File

@ -3864,7 +3864,7 @@ Selection::AddItem(nsRange* aItem, int32_t* aOutIndex, bool aNoStartSelect)
if (mUserInitiated) {
AutoTArray<RefPtr<nsRange>, 4> rangesToAdd;
*aOutIndex = -1;
*aOutIndex = int32_t(mRanges.Length()) - 1;
nsIDocument* doc = GetParentObject();
bool selectEventsEnabled =

View File

@ -0,0 +1,16 @@
<html class="reftest-wait">
<meta charset=utf-8>
<body>
<div id="test" contenteditable>Example</div>
<script>
var elem = document.getElementById("test");
var sel = window.getSelection();
sel.removeAllRanges();
var range = document.createRange();
range.setStart(elem.firstChild, 1);
range.setEnd(elem.firstChild, 1);
sel.addRange(range);
sel.removeAllRanges();
elem.offsetHeight;
document.documentElement.removeAttribute("class");
</script>

View File

@ -0,0 +1,17 @@
<html class="reftest-wait">
<meta charset=utf-8>
<body>
<div id="test" contenteditable>E&#x202e;xample</div>
<script>
var elem = document.getElementById("test");
var sel = window.getSelection();
sel.removeAllRanges();
var range = document.createRange();
range.setStart(elem.firstChild, 1);
range.setEnd(elem.firstChild, 2);
sel.addRange(range);
sel.deleteFromDocument();
sel.removeAllRanges();
elem.offsetHeight;
document.documentElement.removeAttribute("class");
</script>

View File

@ -172,3 +172,4 @@ fuzzy-if(Android,254,231) == brackets-2c-rtl.html brackets-2c-rtl-ref.html
fails-if(stylo) == 1217833-1.html 1217833-1-ref.html
fails-if(stylo) == 1217833-2.html 1217833-2-ref.html
== 1231175-1.html 1231175-1-ref.html
== 1366623-1.html 1366623-1-ref.html

View File

@ -20,6 +20,7 @@ EXPORTS += [
'nsSVGEffects.h',
'nsSVGFilterInstance.h',
'nsSVGForeignObjectFrame.h',
'nsSVGImageFrame.h',
'nsSVGIntegrationUtils.h',
'nsSVGUtils.h',
'SVGImageContext.h',

View File

@ -3,6 +3,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsSVGImageFrame.h"
// Keep in (case-insensitive) order:
#include "gfxContext.h"
#include "gfxPlatform.h"
@ -29,97 +31,11 @@ using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::image;
class nsSVGImageFrame;
class nsSVGImageListener final : public imgINotificationObserver
{
public:
explicit nsSVGImageListener(nsSVGImageFrame *aFrame);
NS_DECL_ISUPPORTS
NS_DECL_IMGINOTIFICATIONOBSERVER
void SetFrame(nsSVGImageFrame *frame) { mFrame = frame; }
private:
~nsSVGImageListener() {}
nsSVGImageFrame *mFrame;
};
class nsSVGImageFrame final
: public SVGGeometryFrame
, public nsIReflowCallback
{
friend nsIFrame*
NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
protected:
explicit nsSVGImageFrame(nsStyleContext* aContext)
: SVGGeometryFrame(aContext, LayoutFrameType::SVGImage)
, mReflowCallbackPosted(false)
{
EnableVisibilityTracking();
}
virtual ~nsSVGImageFrame();
public:
NS_DECL_FRAMEARENA_HELPERS
// nsSVGDisplayableFrame interface:
virtual void PaintSVG(gfxContext& aContext,
const gfxMatrix& aTransform,
imgDrawingParams& aImgParams,
const nsIntRect* aDirtyRect = nullptr) override;
virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
virtual void ReflowSVG() override;
// SVGGeometryFrame methods:
virtual uint16_t GetHitTestFlags() override;
// nsIFrame interface:
virtual nsresult AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType) override;
void OnVisibilityChange(Visibility aNewVisibility,
const Maybe<OnNonvisible>& aNonvisibleAction = Nothing()) override;
virtual void Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) override;
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override
{
return MakeFrameName(NS_LITERAL_STRING("SVGImage"), aResult);
}
#endif
// nsIReflowCallback
virtual bool ReflowFinished() override;
virtual void ReflowCallbackCanceled() override;
private:
gfx::Matrix GetRasterImageTransform(int32_t aNativeWidth,
int32_t aNativeHeight);
gfx::Matrix GetVectorImageTransform();
bool TransformContextForPainting(gfxContext* aGfxContext,
const gfxMatrix& aTransform);
nsCOMPtr<imgINotificationObserver> mListener;
nsCOMPtr<imgIContainer> mImageContainer;
bool mReflowCallbackPosted;
friend class nsSVGImageListener;
};
//----------------------------------------------------------------------
// Implementation
// ---------------------------------------------------------------------
// nsQueryFrame methods
NS_QUERYFRAME_HEAD(nsSVGImageFrame)
NS_QUERYFRAME_ENTRY(nsSVGImageFrame)
NS_QUERYFRAME_TAIL_INHERITING(SVGGeometryFrame)
nsIFrame*
NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
@ -397,6 +313,11 @@ nsSVGImageFrame::PaintSVG(gfxContext& aContext,
dirtyRect.MoveBy(-rootRect.TopLeft());
}
uint32_t flags = aImgParams.imageFlags;
if (mForceSyncDecoding) {
flags |= imgIContainer::FLAG_SYNC_DECODE;
}
if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
// Package up the attributes of this image element which can override the
// attributes of mImageContainer's internal SVG document. The 'width' &
@ -426,7 +347,7 @@ nsSVGImageFrame::PaintSVG(gfxContext& aContext,
destRect,
aDirtyRect ? dirtyRect : destRect,
context,
aImgParams.imageFlags);
flags);
} else { // mImageContainer->GetType() == TYPE_RASTER
aImgParams.result &= nsLayoutUtils::DrawSingleUnscaledImage(
aContext,
@ -435,7 +356,7 @@ nsSVGImageFrame::PaintSVG(gfxContext& aContext,
nsLayoutUtils::GetSamplingFilterForFrame(this),
nsPoint(0, 0),
aDirtyRect ? &dirtyRect : nullptr,
aImgParams.imageFlags);
flags);
}
if (opacity != 1.0f || StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {

View File

@ -0,0 +1,131 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 __NS_SVGIMAGEFRAME_H__
#define __NS_SVGIMAGEFRAME_H__
// Keep in (case-insensitive) order:
#include "gfxContext.h"
#include "gfxPlatform.h"
#include "mozilla/gfx/2D.h"
#include "imgIContainer.h"
#include "nsContainerFrame.h"
#include "nsIDOMMutationEvent.h"
#include "nsIImageLoadingContent.h"
#include "nsLayoutUtils.h"
#include "imgINotificationObserver.h"
#include "nsSVGEffects.h"
#include "mozilla/dom/SVGSVGElement.h"
#include "nsSVGUtils.h"
#include "SVGContentUtils.h"
#include "SVGGeometryFrame.h"
#include "SVGImageContext.h"
#include "mozilla/dom/SVGImageElement.h"
#include "nsContentUtils.h"
#include "nsIReflowCallback.h"
#include "mozilla/Unused.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::image;
class nsSVGImageFrame;
class nsSVGImageListener final : public imgINotificationObserver
{
public:
explicit nsSVGImageListener(nsSVGImageFrame *aFrame);
NS_DECL_ISUPPORTS
NS_DECL_IMGINOTIFICATIONOBSERVER
void SetFrame(nsSVGImageFrame *frame) { mFrame = frame; }
private:
~nsSVGImageListener() {}
nsSVGImageFrame *mFrame;
};
class nsSVGImageFrame final
: public SVGGeometryFrame
, public nsIReflowCallback
{
friend nsIFrame*
NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
protected:
explicit nsSVGImageFrame(nsStyleContext* aContext)
: SVGGeometryFrame(aContext, LayoutFrameType::SVGImage)
, mReflowCallbackPosted(false)
, mForceSyncDecoding(false)
{
EnableVisibilityTracking();
}
virtual ~nsSVGImageFrame();
public:
NS_DECL_QUERYFRAME_TARGET(nsSVGImageFrame)
NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS
// nsSVGDisplayableFrame interface:
virtual void PaintSVG(gfxContext& aContext,
const gfxMatrix& aTransform,
imgDrawingParams& aImgParams,
const nsIntRect* aDirtyRect = nullptr) override;
virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
virtual void ReflowSVG() override;
// SVGGeometryFrame methods:
virtual uint16_t GetHitTestFlags() override;
// nsIFrame interface:
virtual nsresult AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType) override;
void OnVisibilityChange(Visibility aNewVisibility,
const Maybe<OnNonvisible>& aNonvisibleAction = Nothing()) override;
virtual void Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) override;
virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override
{
return MakeFrameName(NS_LITERAL_STRING("SVGImage"), aResult);
}
#endif
// nsIReflowCallback
virtual bool ReflowFinished() override;
virtual void ReflowCallbackCanceled() override;
/// Always sync decode our image when painting if @aForce is true.
void SetForceSyncDecoding(bool aForce) { mForceSyncDecoding = aForce; }
private:
gfx::Matrix GetRasterImageTransform(int32_t aNativeWidth,
int32_t aNativeHeight);
gfx::Matrix GetVectorImageTransform();
bool TransformContextForPainting(gfxContext* aGfxContext,
const gfxMatrix& aTransform);
nsCOMPtr<imgINotificationObserver> mListener;
nsCOMPtr<imgIContainer> mImageContainer;
bool mReflowCallbackPosted;
bool mForceSyncDecoding;
friend class nsSVGImageListener;
};
#endif // __NS_SVGIMAGEFRAME_H__

View File

@ -920,8 +920,8 @@ nsJARChannel::OnDownloadComplete(MemoryDownloader* aDownloader,
uint32_t loadFlags;
channel->GetLoadFlags(&loadFlags);
if (loadFlags & LOAD_REPLACE) {
mLoadFlags |= LOAD_REPLACE;
// Update our URI to reflect any redirects that happen during
// the HTTP request.
if (!mOriginalURI) {
SetOriginalURI(mJarURI);
}
@ -943,6 +943,9 @@ nsJARChannel::OnDownloadComplete(MemoryDownloader* aDownloader,
}
if (NS_SUCCEEDED(status) && channel) {
// In case the load info object has changed during a redirect,
// grab it from the target channel.
channel->GetLoadInfo(getter_AddRefs(mLoadInfo));
// Grab the security info from our base channel
channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));

View File

@ -269,6 +269,7 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
, mTriggeringPrincipal(rhs.mTriggeringPrincipal)
, mPrincipalToInherit(rhs.mPrincipalToInherit)
, mSandboxedLoadingPrincipal(rhs.mSandboxedLoadingPrincipal)
, mResultPrincipalURI(rhs.mResultPrincipalURI)
, mLoadingContext(rhs.mLoadingContext)
, mSecurityFlags(rhs.mSecurityFlags)
, mInternalContentPolicyType(rhs.mInternalContentPolicyType)
@ -300,6 +301,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
nsIPrincipal* aTriggeringPrincipal,
nsIPrincipal* aPrincipalToInherit,
nsIPrincipal* aSandboxedLoadingPrincipal,
nsIURI* aResultPrincipalURI,
nsSecurityFlags aSecurityFlags,
nsContentPolicyType aContentPolicyType,
LoadTainting aTainting,
@ -325,6 +327,7 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
: mLoadingPrincipal(aLoadingPrincipal)
, mTriggeringPrincipal(aTriggeringPrincipal)
, mPrincipalToInherit(aPrincipalToInherit)
, mResultPrincipalURI(aResultPrincipalURI)
, mSecurityFlags(aSecurityFlags)
, mInternalContentPolicyType(aContentPolicyType)
, mTainting(aTainting)
@ -936,5 +939,19 @@ LoadInfo::GetIsTopLevelLoad(bool *aResult)
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetResultPrincipalURI(nsIURI **aURI)
{
NS_IF_ADDREF(*aURI = mResultPrincipalURI);
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetResultPrincipalURI(nsIURI *aURI)
{
mResultPrincipalURI = aURI;
return NS_OK;
}
} // namespace net
} // namespace mozilla

View File

@ -84,6 +84,7 @@ private:
nsIPrincipal* aTriggeringPrincipal,
nsIPrincipal* aPrincipalToInherit,
nsIPrincipal* aSandboxedLoadingPrincipal,
nsIURI* aResultPrincipalURI,
nsSecurityFlags aSecurityFlags,
nsContentPolicyType aContentPolicyType,
LoadTainting aTainting,
@ -128,6 +129,7 @@ private:
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
nsCOMPtr<nsIPrincipal> mSandboxedLoadingPrincipal;
nsCOMPtr<nsIURI> mResultPrincipalURI;
nsWeakPtr mLoadingContext;
nsSecurityFlags mSecurityFlags;
nsContentPolicyType mInternalContentPolicyType;

View File

@ -438,6 +438,11 @@ nsChannelClassifier::StartInternal()
NS_ENSURE_SUCCESS(rv, rv);
// Don't bother checking certain types of URIs.
bool isAbout = false;
rv = uri->SchemeIs("about", &isAbout);
NS_ENSURE_SUCCESS(rv, rv);
if (isAbout) return NS_ERROR_UNEXPECTED;
bool hasFlags;
rv = NS_URIChainHasFlags(uri,
nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,

View File

@ -10,6 +10,7 @@
interface nsIDOMDocument;
interface nsINode;
interface nsIPrincipal;
interface nsIURI;
%{C++
#include "nsTArray.h"
@ -740,6 +741,14 @@ interface nsILoadInfo : nsISupports
*/
[infallible] readonly attribute boolean isTopLevelLoad;
/**
* If this is non-null, this property represents two things: (1) the
* URI to be used for the principal if the channel with this loadinfo
* gets a principal based on URI and (2) the URI to use for a document
* created from the channel with this loadinfo.
*/
attribute nsIURI resultPrincipalURI;
/**
* Returns the null principal of the resulting resource if the SEC_SANDBOXED
* flag is set. Otherwise returns null. This is used by

View File

@ -188,11 +188,17 @@ NS_NewChannelInternal(nsIChannel **outChannel,
NS_ENSURE_SUCCESS(rv, rv);
}
#ifdef DEBUG
nsLoadFlags channelLoadFlags = 0;
channel->GetLoadFlags(&channelLoadFlags);
// Will be removed when we remove LOAD_REPLACE altogether
// This check is trying to catch protocol handlers that still
// try to set the LOAD_REPLACE flag.
MOZ_DIAGNOSTIC_ASSERT(!(channelLoadFlags & nsIChannel::LOAD_REPLACE));
#endif
if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
// Retain the LOAD_REPLACE load flag if set.
nsLoadFlags normalLoadFlags = 0;
channel->GetLoadFlags(&normalLoadFlags);
rv = channel->SetLoadFlags(aLoadFlags | (normalLoadFlags & nsIChannel::LOAD_REPLACE));
rv = channel->SetLoadFlags(aLoadFlags);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -267,11 +273,17 @@ NS_NewChannelInternal(nsIChannel **outChannel,
NS_ENSURE_SUCCESS(rv, rv);
}
#ifdef DEBUG
nsLoadFlags channelLoadFlags = 0;
channel->GetLoadFlags(&channelLoadFlags);
// Will be removed when we remove LOAD_REPLACE altogether
// This check is trying to catch protocol handlers that still
// try to set the LOAD_REPLACE flag.
MOZ_DIAGNOSTIC_ASSERT(!(channelLoadFlags & nsIChannel::LOAD_REPLACE));
#endif
if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
// Retain the LOAD_REPLACE load flag if set.
nsLoadFlags normalLoadFlags = 0;
channel->GetLoadFlags(&normalLoadFlags);
rv = channel->SetLoadFlags(aLoadFlags | (normalLoadFlags & nsIChannel::LOAD_REPLACE));
rv = channel->SetLoadFlags(aLoadFlags);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1885,12 +1897,15 @@ nsresult
NS_GetFinalChannelURI(nsIChannel *channel, nsIURI **uri)
{
*uri = nullptr;
nsLoadFlags loadFlags = 0;
nsresult rv = channel->GetLoadFlags(&loadFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (loadFlags & nsIChannel::LOAD_REPLACE) {
return channel->GetURI(uri);
nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
if (loadInfo) {
nsCOMPtr<nsIURI> resultPrincipalURI;
loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
if (resultPrincipalURI) {
resultPrincipalURI.forget(uri);
return NS_OK;
}
}
return channel->GetOriginalURI(uri);

View File

@ -781,11 +781,10 @@ nsresult NS_URIChainHasFlags(nsIURI *uri,
already_AddRefed<nsIURI> NS_GetInnermostURI(nsIURI *aURI);
/**
* Get the "final" URI for a channel. This is either the same as GetURI or
* GetOriginalURI, depending on whether this channel has
* nsIChanel::LOAD_REPLACE set. For channels without that flag set, the final
* URI is the original URI, while for ones with the flag the final URI is the
* channel URI.
* Get the "final" URI for a channel. This is either channel's load info
* resultPrincipalURI, if set, or GetOriginalURI. In most cases (but not all) load
* info resultPrincipalURI, if set, corresponds to URI of the channel if it's required
* to represent the actual principal for the channel.
*/
nsresult NS_GetFinalChannelURI(nsIChannel *channel, nsIURI **uri);

View File

@ -93,11 +93,10 @@ public:
NS_DECL_NSPISOCKETTRANSPORTSERVICE
NS_DECL_NSISOCKETTRANSPORTSERVICE
NS_DECL_NSIROUTEDSOCKETTRANSPORTSERVICE
NS_DECL_NSIEVENTTARGET
NS_DECL_NSIEVENTTARGET_FULL
NS_DECL_NSITHREADOBSERVER
NS_DECL_NSIRUNNABLE
NS_DECL_NSIOBSERVER
using nsIEventTarget::Dispatch;
nsSocketTransportService();

View File

@ -25,9 +25,8 @@ class nsStreamTransportService final : public nsIStreamTransportService
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISTREAMTRANSPORTSERVICE
NS_DECL_NSIEVENTTARGET
NS_DECL_NSIEVENTTARGET_FULL
NS_DECL_NSIOBSERVER
using nsIEventTarget::Dispatch;
nsresult Init();

View File

@ -34,6 +34,7 @@ struct LoadInfoArgs
PrincipalInfo triggeringPrincipalInfo;
OptionalPrincipalInfo principalToInheritInfo;
OptionalPrincipalInfo sandboxedLoadingPrincipalInfo;
OptionalURIParams resultPrincipalURI;
uint32_t securityFlags;
uint32_t contentPolicyType;
uint32_t tainting;

View File

@ -251,7 +251,20 @@ nsFileUploadContentStream::OnCopyComplete()
//-----------------------------------------------------------------------------
nsFileChannel::nsFileChannel(nsIURI *uri)
: mFileURI(uri)
{
}
nsresult
nsFileChannel::Init()
{
NS_ENSURE_STATE(mLoadInfo);
nsresult rv;
rv = nsBaseChannel::Init();
NS_ENSURE_SUCCESS(rv, rv);
// If we have a link file, we should resolve its target right away.
// This is to protect against a same origin attack where the same link file
// can point to different resources right after the first resource is loaded.
@ -264,24 +277,24 @@ nsFileChannel::nsFileChannel(nsIURI *uri)
#endif
nsCOMPtr<nsIFile> resolvedFile;
bool symLink;
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri);
if (fileURL &&
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mFileURI);
if (fileURL &&
NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(file))) &&
NS_SUCCEEDED(file->IsSymlink(&symLink)) &&
NS_SUCCEEDED(file->IsSymlink(&symLink)) &&
symLink &&
#ifdef XP_WIN
NS_SUCCEEDED(file->GetTarget(fileTarget)) &&
NS_SUCCEEDED(NS_NewLocalFile(fileTarget, PR_TRUE,
NS_SUCCEEDED(NS_NewLocalFile(fileTarget, true,
getter_AddRefs(resolvedFile))) &&
#else
NS_SUCCEEDED(file->GetNativeTarget(fileTarget)) &&
NS_SUCCEEDED(NS_NewNativeLocalFile(fileTarget, PR_TRUE,
NS_SUCCEEDED(NS_NewNativeLocalFile(fileTarget, true,
getter_AddRefs(resolvedFile))) &&
#endif
NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(targetURI),
resolvedFile, nullptr))) {
NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(targetURI),
resolvedFile, nullptr))) {
// Make an effort to match up the query strings.
nsCOMPtr<nsIURL> origURL = do_QueryInterface(uri);
nsCOMPtr<nsIURL> origURL = do_QueryInterface(mFileURI);
nsCOMPtr<nsIURL> targetURL = do_QueryInterface(targetURI);
nsAutoCString queryString;
if (origURL && targetURL && NS_SUCCEEDED(origURL->GetQuery(queryString))) {
@ -289,13 +302,13 @@ nsFileChannel::nsFileChannel(nsIURI *uri)
}
SetURI(targetURI);
SetOriginalURI(uri);
nsLoadFlags loadFlags = 0;
GetLoadFlags(&loadFlags);
SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
SetOriginalURI(mFileURI);
mLoadInfo->SetResultPrincipalURI(targetURI);
} else {
SetURI(uri);
SetURI(mFileURI);
}
return NS_OK;
}
nsFileChannel::~nsFileChannel()

View File

@ -22,6 +22,8 @@ public:
explicit nsFileChannel(nsIURI *uri);
nsresult Init();
protected:
~nsFileChannel();
@ -40,6 +42,7 @@ protected:
private:
nsCOMPtr<nsIInputStream> mUploadStream;
int64_t mUploadLength;
nsCOMPtr<nsIURI> mFileURI;
};
#endif // !nsFileChannel_h__

View File

@ -190,6 +190,8 @@ nsFileProtocolHandler::NewChannel2(nsIURI* uri,
nsILoadInfo* aLoadInfo,
nsIChannel** result)
{
nsresult rv;
nsFileChannel *chan;
if (IsNeckoChild()) {
chan = new mozilla::net::FileChannelChild(uri);
@ -200,14 +202,16 @@ nsFileProtocolHandler::NewChannel2(nsIURI* uri,
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(chan);
nsresult rv = chan->Init();
// set the loadInfo on the new channel ; must do this
// before calling Init() on it, since it needs the load
// info be already set.
rv = chan->SetLoadInfo(aLoadInfo);
if (NS_FAILED(rv)) {
NS_RELEASE(chan);
return rv;
}
// set the loadInfo on the new channel
rv = chan->SetLoadInfo(aLoadInfo);
rv = chan->Init();
if (NS_FAILED(rv)) {
NS_RELEASE(chan);
return rv;

View File

@ -2973,6 +2973,72 @@ void HttpBaseChannel::AssertPrivateBrowsingId()
}
#endif
already_AddRefed<nsILoadInfo>
HttpBaseChannel::CloneLoadInfoForRedirect(nsIURI * newURI, uint32_t redirectFlags)
{
// make a copy of the loadinfo, append to the redirectchain
// this will be set on the newly created channel for the redirect target.
if (!mLoadInfo) {
return nullptr;
}
nsCOMPtr<nsILoadInfo> newLoadInfo =
static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->Clone();
nsContentPolicyType contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
nsCOMPtr<nsIPrincipal> nullPrincipalToInherit = NullPrincipal::Create();
newLoadInfo->SetPrincipalToInherit(nullPrincipalToInherit);
}
// re-compute the origin attributes of the loadInfo if it's top-level load.
bool isTopLevelDoc =
newLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT;
if (isTopLevelDoc) {
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(this, loadContext);
OriginAttributes docShellAttrs;
if (loadContext) {
loadContext->GetOriginAttributes(docShellAttrs);
}
OriginAttributes attrs = newLoadInfo->GetOriginAttributes();
MOZ_ASSERT(docShellAttrs.mUserContextId == attrs.mUserContextId,
"docshell and necko should have the same userContextId attribute.");
MOZ_ASSERT(docShellAttrs.mInIsolatedMozBrowser == attrs.mInIsolatedMozBrowser,
"docshell and necko should have the same inIsolatedMozBrowser attribute.");
MOZ_ASSERT(docShellAttrs.mPrivateBrowsingId == attrs.mPrivateBrowsingId,
"docshell and necko should have the same privateBrowsingId attribute.");
attrs = docShellAttrs;
attrs.SetFirstPartyDomain(true, newURI);
newLoadInfo->SetOriginAttributes(attrs);
}
// This makes the original URI (the redirect URL) of the target channel become
// the resulting principal URI.
// After the veto and security checking of this redirect is done, the original
// URI of the target channel will be rewriten with the first URI in the redirect
// chain (the source URI of this load). Hance the original URI itself can't be
// used for determining the result principal URI.
// If the target schema protocol handler wants to express a different URI as
// the result principal URI, it's free to rewrite this property.
// By not modifying the result principal URI the target protocol handler
// expresses that it's OK to use the channel's pre-redirect-veto original URI
// (the redirect URL) as the result principal URI.
newLoadInfo->SetResultPrincipalURI(newURI);
bool isInternalRedirect =
(redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
nsIChannelEventSink::REDIRECT_STS_UPGRADE));
newLoadInfo->AppendRedirectedPrincipal(GetURIPrincipal(), isInternalRedirect);
return newLoadInfo.forget();
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsITraceableChannel
//-----------------------------------------------------------------------------
@ -3161,57 +3227,6 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
}
}
// make a copy of the loadinfo, append to the redirectchain
// and set it on the new channel
if (mLoadInfo) {
nsCOMPtr<nsILoadInfo> newLoadInfo =
static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->Clone();
nsContentPolicyType contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
nsCOMPtr<nsIPrincipal> nullPrincipalToInherit = NullPrincipal::Create();
newLoadInfo->SetPrincipalToInherit(nullPrincipalToInherit);
}
// re-compute the origin attributes of the loadInfo if it's top-level load.
bool isTopLevelDoc =
newLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT;
if (isTopLevelDoc) {
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(this, loadContext);
OriginAttributes docShellAttrs;
if (loadContext) {
loadContext->GetOriginAttributes(docShellAttrs);
}
OriginAttributes attrs = newLoadInfo->GetOriginAttributes();
MOZ_ASSERT(docShellAttrs.mUserContextId == attrs.mUserContextId,
"docshell and necko should have the same userContextId attribute.");
MOZ_ASSERT(docShellAttrs.mInIsolatedMozBrowser == attrs.mInIsolatedMozBrowser,
"docshell and necko should have the same inIsolatedMozBrowser attribute.");
MOZ_ASSERT(docShellAttrs.mPrivateBrowsingId == attrs.mPrivateBrowsingId,
"docshell and necko should have the same privateBrowsingId attribute.");
attrs = docShellAttrs;
attrs.SetFirstPartyDomain(true, newURI);
newLoadInfo->SetOriginAttributes(attrs);
}
bool isInternalRedirect =
(redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
nsIChannelEventSink::REDIRECT_STS_UPGRADE));
newLoadInfo->AppendRedirectedPrincipal(GetURIPrincipal(), isInternalRedirect);
newChannel->SetLoadInfo(newLoadInfo);
}
else {
// the newChannel was created with a dummy loadInfo, we should clear
// it in case the original channel does not have a loadInfo
newChannel->SetLoadInfo(nullptr);
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
if (!httpChannel)
return NS_OK; // no other options to set

View File

@ -424,6 +424,8 @@ protected:
void AssertPrivateBrowsingId();
#endif
already_AddRefed<nsILoadInfo> CloneLoadInfoForRedirect(nsIURI *newURI, uint32_t redirectFlags);
friend class PrivateBrowsingChannel<HttpBaseChannel>;
friend class InterceptFailedOnStop;

View File

@ -1493,9 +1493,10 @@ HttpChannelChild::SetupRedirect(nsIURI* uri,
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> newChannel;
nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(uri, redirectFlags);
rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
uri,
mLoadInfo,
redirectLoadInfo,
nullptr, // aLoadGroup
nullptr, // aCallbacks
nsIRequest::LOAD_NORMAL,

View File

@ -2756,6 +2756,8 @@ nsHttpChannel::StartRedirectChannelToURI(nsIURI *upgradedURI, uint32_t flags)
LOG(("nsHttpChannel::StartRedirectChannelToURI()\n"));
nsCOMPtr<nsIChannel> newChannel;
nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(upgradedURI,
flags);
nsCOMPtr<nsIIOService> ioService;
rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
@ -2763,7 +2765,7 @@ nsHttpChannel::StartRedirectChannelToURI(nsIURI *upgradedURI, uint32_t flags)
rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
upgradedURI,
mLoadInfo,
redirectLoadInfo,
nullptr, // aLoadGroup
nullptr, // aCallbacks
nsIRequest::LOAD_NORMAL,
@ -5621,22 +5623,23 @@ nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv)
rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIChannel> newChannel;
rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
mRedirectURI,
mLoadInfo,
nullptr, // aLoadGroup
nullptr, // aCallbacks
nsIRequest::LOAD_NORMAL,
ioService);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t redirectFlags;
if (nsHttp::IsPermanentRedirect(mRedirectType))
redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
else
redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
nsCOMPtr<nsIChannel> newChannel;
nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(mRedirectURI, redirectFlags);
rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
mRedirectURI,
redirectLoadInfo,
nullptr, // aLoadGroup
nullptr, // aCallbacks
nsIRequest::LOAD_NORMAL,
ioService);
NS_ENSURE_SUCCESS(rv, rv);
rv = SetupReplacementChannel(mRedirectURI, newChannel,
!rewriteToGET, redirectFlags);
if (NS_FAILED(rv)) return rv;

View File

@ -33,6 +33,10 @@ protected:
const nsACString& aPathname,
nsACString& aResult) override;
// |result| is an inout param. On entry to this function, *result
// is expected to be non-null and already addrefed. This function
// may release the object stored in *result on entry and write
// a new pointer to an already addrefed channel to *result.
virtual MOZ_MUST_USE nsresult SubstituteChannel(nsIURI* uri,
nsILoadInfo* aLoadInfo,
nsIChannel** result) override;

View File

@ -246,6 +246,8 @@ SubstitutingProtocolHandler::NewChannel2(nsIURI* uri,
nsIChannel** result)
{
NS_ENSURE_ARG_POINTER(uri);
NS_ENSURE_ARG_POINTER(aLoadInfo);
nsAutoCString spec;
nsresult rv = ResolveURI(uri, spec);
NS_ENSURE_SUCCESS(rv, rv);
@ -254,12 +256,18 @@ SubstitutingProtocolHandler::NewChannel2(nsIURI* uri,
rv = NS_NewURI(getter_AddRefs(newURI), spec);
NS_ENSURE_SUCCESS(rv, rv);
// We don't want to allow the inner protocol handler to modify the result
// principal URI since we want either |uri| or anything pre-set by upper
// layers to prevail.
nsCOMPtr<nsIURI> savedResultPrincipalURI;
rv = aLoadInfo->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI));
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewChannelInternal(result, newURI, aLoadInfo);
NS_ENSURE_SUCCESS(rv, rv);
nsLoadFlags loadFlags = 0;
(*result)->GetLoadFlags(&loadFlags);
(*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
rv = aLoadInfo->SetResultPrincipalURI(savedResultPrincipalURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = (*result)->SetOriginalURI(uri);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -877,6 +877,10 @@ nsMultiMixedConv::SendStart()
mPartChannel->SetContentDisposition(mContentDisposition);
// Each part of a multipart/replace response can be used
// for the top level document. We must inform upper layers
// about this by setting the LOAD_REPLACE flag so that certain
// state assertions are evaluated as positive.
nsLoadFlags loadFlags = 0;
mPartChannel->GetLoadFlags(&loadFlags);
loadFlags |= nsIChannel::LOAD_REPLACE;

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