Merge mozilla-central to inbound. a=merge CLOSED TREE

This commit is contained in:
Margareta Eliza Balazs 2018-07-12 12:33:01 +03:00
Родитель 7b416abaf1 46292b1212
Коммит 88a3947938
69 изменённых файлов: 1309 добавлений и 338 удалений

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

@ -28,6 +28,7 @@ static const uint32_t ACTIVITY_STREAM_FLAGS =
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::ENABLE_INDEXED_DB |
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGED_CHILD |
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT;
struct RedirEntry {
@ -92,6 +93,7 @@ static const RedirEntry kRedirMap[] = {
{ "newtab", "about:blank", ACTIVITY_STREAM_FLAGS },
{ "welcome", "about:blank",
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGED_CHILD |
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT },
{ "library", "chrome://browser/content/aboutLibrary.xhtml",

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

@ -9,23 +9,29 @@
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/E10SUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AboutNewTab",
"resource:///modules/AboutNewTab.jsm");
const TOPIC_APP_QUIT = "quit-application-granted";
const TOPIC_LOCALES_CHANGE = "intl:app-locales-changed";
const TOPIC_CONTENT_DOCUMENT_INTERACTIVE = "content-document-interactive";
// Automated tests ensure packaged locales are in this list. Copied output of:
// https://github.com/mozilla/activity-stream/blob/master/bin/render-activity-stream-html.js
const ACTIVITY_STREAM_LOCALES = "en-US ach an ar ast az be bg bn-BD bn-IN br bs ca cak crh cs cy da de dsb el en-CA en-GB eo es-AR es-CL es-ES es-MX et eu fa ff fi fr fy-NL ga-IE gd gl gn gu-IN he hi-IN hr hsb hu hy-AM ia id it ja ja-JP-mac ka kab kk km kn ko lij lo lt ltg lv mai mk ml mr ms my nb-NO ne-NP nl nn-NO oc pa-IN pl pt-BR pt-PT rm ro ru si sk sl sq sr sv-SE ta te th tl tr uk ur uz vi zh-CN zh-TW".split(" ");
const ABOUT_URL = "about:newtab";
const BASE_URL = "resource://activity-stream/";
const ACTIVITY_STREAM_PAGES = new Set(["home", "newtab", "welcome"]);
const IS_MAIN_PROCESS = Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;
const IS_PRIVILEGED_PROCESS = Services.appinfo.remoteType === E10SUtils.PRIVILEGED_REMOTE_TYPE;
const IS_RELEASE_OR_BETA = AppConstants.RELEASE_OR_BETA;
const PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS = "browser.tabs.remote.separatePrivilegedContentProcess";
const PREF_ACTIVITY_STREAM_PRERENDER_ENABLED = "browser.newtabpage.activity-stream.prerender";
const PREF_ACTIVITY_STREAM_DEBUG = "browser.newtabpage.activity-stream.debug";
@ -33,6 +39,7 @@ const PREF_ACTIVITY_STREAM_DEBUG = "browser.newtabpage.activity-stream.debug";
function AboutNewTabService() {
Services.obs.addObserver(this, TOPIC_APP_QUIT);
Services.obs.addObserver(this, TOPIC_LOCALES_CHANGE);
Services.prefs.addObserver(PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS, this);
Services.prefs.addObserver(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED, this);
if (!IS_RELEASE_OR_BETA) {
Services.prefs.addObserver(PREF_ACTIVITY_STREAM_DEBUG, this);
@ -44,6 +51,8 @@ function AboutNewTabService() {
if (IS_MAIN_PROCESS) {
AboutNewTab.init();
} else if (IS_PRIVILEGED_PROCESS) {
Services.obs.addObserver(this, TOPIC_CONTENT_DOCUMENT_INTERACTIVE);
}
}
@ -85,6 +94,7 @@ AboutNewTabService.prototype = {
_activityStreamPrerender: false,
_activityStreamPath: "",
_activityStreamDebug: false,
_privilegedContentProcess: false,
_overridden: false,
willNotifyUser: false,
@ -100,7 +110,9 @@ AboutNewTabService.prototype = {
observe(subject, topic, data) {
switch (topic) {
case "nsPref:changed":
if (data === PREF_ACTIVITY_STREAM_PRERENDER_ENABLED) {
if (data === PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS) {
this._privilegedContentProcess = Services.prefs.getBoolPref(PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS);
} else if (data === PREF_ACTIVITY_STREAM_PRERENDER_ENABLED) {
this._activityStreamPrerender = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED);
this.notifyChange();
} else if (!IS_RELEASE_OR_BETA && data === PREF_ACTIVITY_STREAM_DEBUG) {
@ -109,10 +121,69 @@ AboutNewTabService.prototype = {
this.notifyChange();
}
break;
case TOPIC_CONTENT_DOCUMENT_INTERACTIVE:
const win = subject.defaultView;
// It seems like "content-document-interactive" is triggered multiple
// times for a single window. The first event always seems to be an
// HTMLDocument object that contains a non-null window reference
// whereas the remaining ones seem to be proxied objects.
// https://searchfox.org/mozilla-central/rev/d2966246905102b36ef5221b0e3cbccf7ea15a86/devtools/server/actors/object.js#100-102
if (win === null) {
break;
}
// We use win.location.pathname instead of win.location.toString()
// because we want to account for URLs that contain the location hash
// property or query strings (e.g. about:newtab#foo, about:home?bar).
// Asserting here would be ideal, but this code path is also taken
// by the view-source:// scheme, so we should probably just bail out
// and do nothing.
if (!ACTIVITY_STREAM_PAGES.has(win.location.pathname)) {
break;
}
const onLoaded = () => {
const debugString = this._activityStreamDebug ? "-dev" : "";
// This list must match any similar ones in render-activity-stream-html.js.
const scripts = [
"chrome://browser/content/contentSearchUI.js",
"chrome://browser/content/contentTheme.js",
`${BASE_URL}vendor/react${debugString}.js`,
`${BASE_URL}vendor/react-dom${debugString}.js`,
`${BASE_URL}vendor/prop-types.js`,
`${BASE_URL}vendor/react-intl.js`,
`${BASE_URL}vendor/redux.js`,
`${BASE_URL}vendor/react-redux.js`,
`${BASE_URL}prerendered/${this.activityStreamLocale}/activity-stream-strings.js`,
`${BASE_URL}data/content/activity-stream.bundle.js`
];
if (this._activityStreamPrerender) {
scripts.unshift(`${BASE_URL}prerendered/static/activity-stream-initial-state.js`);
}
for (let script of scripts) {
Services.scriptloader.loadSubScript(script, win); // Synchronous call
}
};
subject.addEventListener("DOMContentLoaded", onLoaded, {once: true});
// There is a possibility that DOMContentLoaded won't be fired. This
// unload event (which cannot be cancelled) will attempt to remove
// the listener for the DOMContentLoaded event.
const onUnloaded = () => {
subject.removeEventListener("DOMContentLoaded", onLoaded);
};
subject.addEventListener("unload", onUnloaded, {once: true});
break;
case TOPIC_APP_QUIT:
this.uninit();
if (IS_MAIN_PROCESS) {
AboutNewTab.uninit();
} else if (IS_PRIVILEGED_PROCESS) {
Services.obs.removeObserver(this, TOPIC_CONTENT_DOCUMENT_INTERACTIVE);
}
break;
case TOPIC_LOCALES_CHANGE:
@ -137,7 +208,6 @@ AboutNewTabService.prototype = {
* @param {Boolean} forceState force state change
*/
toggleActivityStream(stateEnabled, forceState = false) {
if (!forceState && (this.overridden || stateEnabled === this.activityStreamEnabled)) {
// exit there is no change of state
return false;
@ -147,6 +217,7 @@ AboutNewTabService.prototype = {
} else {
this._activityStreamEnabled = false;
}
this._privilegedContentProcess = Services.prefs.getBoolPref(PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS);
this._activityStreamPrerender = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED);
if (!IS_RELEASE_OR_BETA) {
this._activityStreamDebug = Services.prefs.getBoolPref(PREF_ACTIVITY_STREAM_DEBUG, false);
@ -182,6 +253,7 @@ AboutNewTabService.prototype = {
"activity-stream",
this._activityStreamPrerender ? "-prerendered" : "",
this._activityStreamDebug ? "-debug" : "",
this._privilegedContentProcess ? "-noscripts" : "",
".html"
].join("");
},
@ -245,6 +317,7 @@ AboutNewTabService.prototype = {
}
Services.obs.removeObserver(this, TOPIC_APP_QUIT);
Services.obs.removeObserver(this, TOPIC_LOCALES_CHANGE);
Services.prefs.removeObserver(PREF_SEPARATE_PRIVILEGED_CONTENT_PROCESS, this);
Services.prefs.removeObserver(PREF_ACTIVITY_STREAM_PRERENDER_ENABLED, this);
if (!IS_RELEASE_OR_BETA) {
Services.prefs.removeObserver(PREF_ACTIVITY_STREAM_DEBUG, this);

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

@ -37,6 +37,7 @@ ManifestDPIAware true
Var TmpVal
Var MaintCertKey
Var ShouldOpenSurvey
; Other included files may depend upon these includes!
; The following includes are provided by NSIS.
@ -124,6 +125,8 @@ OutFile "helper.exe"
!endif
ShowUnInstDetails nevershow
!define URLUninstallSurvey "https://qsurvey.mozilla.com/s3/FF-Desktop-Post-Uninstall?channel=${UpdateChannel}&version=${AppVersion}&osversion="
################################################################################
# Modern User Interface - MUI
@ -157,7 +160,11 @@ UninstPage custom un.preConfirm
!insertmacro MUI_UNPAGE_INSTFILES
; Finish Page
!define MUI_FINISHPAGE_SHOWREADME
!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED
!define MUI_FINISHPAGE_SHOWREADME_TEXT $(UN_SURVEY_CHECKBOX_LABEL)
!define MUI_FINISHPAGE_SHOWREADME_FUNCTION un.Survey
!define MUI_PAGE_CUSTOMFUNCTION_PRE un.preFinish
!insertmacro MUI_UNPAGE_FINISH
; Use the default dialog for IDD_VERIFY for a simple Banner
@ -166,6 +173,15 @@ ChangeUI IDD_VERIFY "${NSISDIR}\Contrib\UIs\default.exe"
################################################################################
# Helper Functions
Function un.Survey
; We can't actually call ExecInExplorer here because it's going to have to
; make some marshalled COM calls and those are not allowed from within a
; synchronous message handler (where we currently are); we'll be thrown
; RPC_E_CANTCALLOUT_ININPUTSYNCCALL if we try. So all we can do is record
; that we need to make the call later, which we'll do from un.onGUIEnd.
StrCpy $ShouldOpenSurvey "1"
FunctionEnd
; This function is used to uninstall the maintenance service if the
; application currently being uninstalled is the last application to use the
; maintenance service.
@ -587,6 +603,12 @@ Function un.preConfirm
!insertmacro MUI_INSTALLOPTIONS_SHOW
FunctionEnd
Function un.preFinish
; Need to give the survey (readme) checkbox a few extra DU's of height
; to accommodate a potentially multi-line label.
WriteINIStr "$PLUGINSDIR\ioSpecial.ini" "Field 4" Bottom "120"
FunctionEnd
################################################################################
# Initialization Functions
@ -605,6 +627,7 @@ Function un.onInit
System::Call 'kernel32::SetDllDirectoryW(w "")'
StrCpy $LANGUAGE 0
StrCpy $ShouldOpenSurvey "0"
${un.UninstallUnOnInitCommon}
@ -617,4 +640,31 @@ FunctionEnd
Function un.onGUIEnd
${un.OnEndCommon}
${If} $ShouldOpenSurvey == "1"
; Though these values are sometimes incorrect due to bug 444664 it happens
; so rarely it isn't worth working around it by reading the registry values.
${WinVerGetMajor} $0
${WinVerGetMinor} $1
${WinVerGetBuild} $2
${WinVerGetServicePackLevel} $3
StrCpy $R1 "${URLUninstallSurvey}$0.$1.$2.$3"
; We can't just open the URL normally because we are most likely running
; elevated without an unelevated process to redirect through, and we're
; not going to go around starting elevated web browsers. But to start an
; unelevated process directly from here we need a pretty nasty hack; see
; the ExecInExplorer plugin code itself for the details.
${If} ${AtLeastWin10}
; If we were the default browser and we've now been uninstalled, we need
; to take steps to make sure the user doesn't see an "open with" dialog;
; they're helping us out by answering this survey, they don't need more
; friction. Windows 7 and 8 automatically switch the default to IE, so
; nothing to do for those, but 10 leaves the default empty and does show
; the dialog, so we have to force Edge there.
ExecInExplorer::Exec "microsoft-edge:$R1"
${Else}
ExecInExplorer::Exec "$R1"
${EndIf}
${EndIf}
FunctionEnd

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

@ -68,6 +68,8 @@ STATUS_INSTALL_LANG=Installing Language Files (${AB_CD})…
STATUS_UNINSTALL_MAIN=Uninstalling $BrandShortName…
STATUS_CLEANUP=A Little Housekeeping…
UN_SURVEY_CHECKBOX_LABEL=Tell Mozilla why you uninstalled $BrandShortName
# _DESC strings support approximately 65 characters per line.
# One line
OPTIONS_SUMMARY=Choose the type of setup you prefer, then click Next.

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

@ -24,9 +24,12 @@ XPIDL_SOURCES += [
XPIDL_MODULE = 'caps'
EXPORTS += [
'ContentPrincipal.h',
'nsJSPrincipals.h',
'nsScriptSecurityManager.h',
'NullPrincipal.h',
'NullPrincipalURI.h',
'SystemPrincipal.h',
]
EXPORTS.mozilla = [

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

@ -908,12 +908,20 @@ function doTestScroll(aSettings, aCallback)
var currentTest = kTests[currentTestIndex];
description = "doTestScroll(aSettings=" + aSettings.description + "), " + currentTest.description + ": ";
if (currentTest.prepare) {
currentTest.prepare(doTestCurrentScroll);
// prepare() can make changes to a page such as
// changing the 'overflow' property which requires
// a repaint to take effect before sending
// scroll-wheel events.
currentTest.prepare(doWaitForPaintsAndScroll);
} else {
doTestCurrentScroll();
}
}
function doWaitForPaintsAndScroll() {
waitForAllPaintsFlushed(doTestCurrentScroll);
}
function doTestCurrentScroll() {
var currentTest = kTests[currentTestIndex];
sendWheelAndWait(10, 10, currentTest.event, function () {

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

@ -3933,7 +3933,7 @@ MediaManager::IsActivelyCapturingOrHasAPermission(uint64_t aWindowId)
// Or are persistent permissions (audio or video) granted?
auto* window = nsGlobalWindowInner::GetInnerWindowWithId(aWindowId);
if (NS_WARN_IF(!window)) {
if (NS_WARN_IF(!window) || NS_WARN_IF(!window->GetPrincipal())) {
return false;
}
// Check if this site has persistent permissions.
@ -3947,7 +3947,7 @@ MediaManager::IsActivelyCapturingOrHasAPermission(uint64_t aWindowId)
uint32_t audio = nsIPermissionManager::UNKNOWN_ACTION;
uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
{
auto* principal = window->GetExtantDoc()->NodePrincipal();
auto* principal = window->GetPrincipal();
rv = mgr->TestExactPermissionFromPrincipal(principal, "microphone", &audio);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;

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

@ -62,7 +62,7 @@ ServiceWorker::Create(nsIGlobalObject* aOwner,
NS_ENSURE_TRUE(reg, nullptr);
RefPtr<ServiceWorkerInfo> info = reg->GetByDescriptor(aDescriptor);
NS_ENSURE_TRUE(reg, nullptr);
NS_ENSURE_TRUE(info, nullptr);
inner = new ServiceWorkerImpl(info, reg);
}

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

@ -529,22 +529,25 @@ ServiceWorkerRegistration::UpdateStateInternal(const Maybe<ServiceWorkerDescript
}
if (aActive.isSome()) {
mActiveWorker = global->GetOrCreateServiceWorker(aActive.ref());
mActiveWorker->SetState(aActive.ref().State());
if ((mActiveWorker = global->GetOrCreateServiceWorker(aActive.ref()))) {
mActiveWorker->SetState(aActive.ref().State());
}
} else {
mActiveWorker = nullptr;
}
if (aWaiting.isSome()) {
mWaitingWorker = global->GetOrCreateServiceWorker(aWaiting.ref());
mWaitingWorker->SetState(aWaiting.ref().State());
if ((mWaitingWorker = global->GetOrCreateServiceWorker(aWaiting.ref()))) {
mWaitingWorker->SetState(aWaiting.ref().State());
}
} else {
mWaitingWorker = nullptr;
}
if (aInstalling.isSome()) {
mInstallingWorker = global->GetOrCreateServiceWorker(aInstalling.ref());
mInstallingWorker->SetState(aInstalling.ref().State());
if ((mInstallingWorker = global->GetOrCreateServiceWorker(aInstalling.ref()))) {
mInstallingWorker->SetState(aInstalling.ref().State());
}
} else {
mInstallingWorker = nullptr;
}

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

@ -13,6 +13,39 @@ namespace layers {
const FrameMetrics::ViewID FrameMetrics::NULL_SCROLL_ID = 0;
void
FrameMetrics::RecalculateViewportOffset()
{
if (!mIsRootContent) {
return;
}
CSSRect visualViewport = GetVisualViewport();
// If the visual viewport is contained within the layout viewport, we don't
// need to make any adjustments, so we can exit early.
//
// Additionally, if the composition bounds changes (due to an orientation
// change, window resize, etc.), it may take a few frames for mViewport to
// update and during that time, the visual viewport may be larger than the
// layout viewport, so don't attempt to make any adjustments.
if (mViewport.Contains(visualViewport) ||
(mViewport.Width() < visualViewport.Width() &&
!FuzzyEqualsMultiplicative(mViewport.Width(), visualViewport.Width())) ||
(mViewport.Height() < visualViewport.Height() &&
!FuzzyEqualsMultiplicative(mViewport.Height(), visualViewport.Height()))) {
return;
}
if (visualViewport.X() < mViewport.X()) {
mViewport.MoveToX(visualViewport.X());
} else if (mViewport.XMost() < visualViewport.XMost()) {
mViewport.MoveByX(visualViewport.XMost() - mViewport.XMost());
}
if (visualViewport.Y() < mViewport.Y()) {
mViewport.MoveToY(visualViewport.Y());
} else if (mViewport.YMost() < visualViewport.YMost()) {
mViewport.MoveByY(visualViewport.YMost() - mViewport.YMost());
}
}
void
ScrollMetadata::SetUsesContainerScrolling(bool aValue) {
MOZ_ASSERT_IF(aValue, gfxPrefs::LayoutUseContainersForRootFrames());

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

@ -481,6 +481,11 @@ public:
return mViewport;
}
CSSRect GetVisualViewport() const
{
return CSSRect(mScrollOffset, CalculateCompositedSizeInCssPixels());
}
void SetExtraResolution(const ScreenToLayerScale2D& aExtraResolution)
{
mExtraResolution = aExtraResolution;
@ -526,6 +531,13 @@ public:
return mIsScrollInfoLayer;
}
// Determine if the visual viewport is outside of the layout viewport and
// adjust the x,y-offset in mViewport accordingly. This is necessary to
// allow APZ to async-scroll the layout viewport.
//
// This is a no-op if mIsRootContent is false.
void RecalculateViewportOffset();
private:
// A unique ID assigned to each scrollable frame.
ViewID mScrollId;

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

@ -242,7 +242,7 @@ StackScrollerFlingAnimation::DoSample(FrameMetrics& aFrameMetrics,
mPreviousOffset = offset;
mApzc.SetVelocityVector(velocity);
aFrameMetrics.SetScrollOffset(offset / aFrameMetrics.GetZoom());
mApzc.SetScrollOffset(offset / aFrameMetrics.GetZoom());
// If we hit a bounds while flinging, send the velocity so that the bounce
// animation can play.

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

@ -581,11 +581,13 @@ private:
class ZoomAnimation: public AsyncPanZoomAnimation {
public:
ZoomAnimation(const CSSPoint& aStartOffset,
ZoomAnimation(AsyncPanZoomController& aApzc,
const CSSPoint& aStartOffset,
const CSSToParentLayerScale2D& aStartZoom,
const CSSPoint& aEndOffset,
const CSSToParentLayerScale2D& aEndZoom)
: mTotalDuration(
: mApzc(aApzc)
, mTotalDuration(
TimeDuration::FromMilliseconds(gfxPrefs::APZZoomAnimationDuration()))
, mStartOffset(aStartOffset)
, mStartZoom(aStartZoom)
@ -602,7 +604,7 @@ public:
if (animPosition >= 1.0) {
aFrameMetrics.SetZoom(mEndZoom);
aFrameMetrics.SetScrollOffset(mEndOffset);
mApzc.SetScrollOffset(mEndOffset);
return false;
}
@ -618,7 +620,7 @@ public:
1 / (sampledPosition / mEndZoom.xScale + (1 - sampledPosition) / mStartZoom.xScale),
1 / (sampledPosition / mEndZoom.yScale + (1 - sampledPosition) / mStartZoom.yScale)));
aFrameMetrics.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
mApzc.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
mEndOffset.x * sampledPosition + mStartOffset.x * (1 - sampledPosition),
mEndOffset.y * sampledPosition + mStartOffset.y * (1 - sampledPosition)
)));
@ -632,6 +634,8 @@ public:
}
private:
AsyncPanZoomController& mApzc;
TimeDuration mDuration;
const TimeDuration mTotalDuration;
@ -680,7 +684,7 @@ public:
// offset to end up being a bit off from the destination, we can get
// artefacts like "scroll to the next snap point in this direction"
// scrolling to the snap point we're already supposed to be at.
aFrameMetrics.ClampAndSetScrollOffset(
mApzc.ClampAndSetScrollOffset(
CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetDestination(),
mYAxisModel.GetDestination())));
return false;
@ -719,7 +723,7 @@ public:
mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y);
aFrameMetrics.ScrollBy(adjustedOffset / zoom);
mApzc.ScrollBy(adjustedOffset / zoom);
// The smooth scroll may have caused us to reach the end of our scroll range.
// This can happen if either the layout.css.scroll-behavior.damping-ratio
@ -1075,7 +1079,7 @@ nsEventStatus AsyncPanZoomController::HandleDragEvent(const MouseInput& aEvent,
scrollOffset.y = scrollPosition;
}
APZC_LOG("%p set scroll offset to %s from scrollbar drag\n", this, Stringify(scrollOffset).c_str());
mFrameMetrics.SetScrollOffset(scrollOffset);
SetScrollOffset(scrollOffset);
ScheduleCompositeAndMaybeRepaint();
UpdateSharedCompositorFrameMetrics();
@ -3310,7 +3314,7 @@ void AsyncPanZoomController::AdjustScrollForSurfaceShift(const ScreenPoint& aShi
this, Stringify(adjustment).c_str());
CSSRect scrollRange = mFrameMetrics.CalculateScrollRange();
// Apply shift to mFrameMetrics.mScrollOffset.
mFrameMetrics.SetScrollOffset(scrollRange.ClampPoint(
SetScrollOffset(scrollRange.ClampPoint(
mFrameMetrics.GetScrollOffset() + adjustment));
// Apply shift to mCompositedScrollOffset, since the dynamic toolbar expects
// the shift to take effect right away, without the usual frame delay.
@ -3320,12 +3324,27 @@ void AsyncPanZoomController::AdjustScrollForSurfaceShift(const ScreenPoint& aShi
UpdateSharedCompositorFrameMetrics();
}
void AsyncPanZoomController::SetScrollOffset(const CSSPoint& aOffset) {
mFrameMetrics.SetScrollOffset(aOffset);
mFrameMetrics.RecalculateViewportOffset();
}
void AsyncPanZoomController::ClampAndSetScrollOffset(const CSSPoint& aOffset) {
mFrameMetrics.ClampAndSetScrollOffset(aOffset);
mFrameMetrics.RecalculateViewportOffset();
}
void AsyncPanZoomController::ScrollBy(const CSSPoint& aOffset) {
mFrameMetrics.ScrollBy(aOffset);
SetScrollOffset(mFrameMetrics.GetScrollOffset() + aOffset);
}
void AsyncPanZoomController::ScrollByAndClamp(const CSSPoint& aOffset) {
mFrameMetrics.ClampAndSetScrollOffset(mFrameMetrics.GetScrollOffset() + aOffset);
ClampAndSetScrollOffset(mFrameMetrics.GetScrollOffset() + aOffset);
}
void AsyncPanZoomController::CopyScrollInfoFrom(const FrameMetrics& aFrameMetrics) {
mFrameMetrics.CopyScrollInfoFrom(aFrameMetrics);
mFrameMetrics.RecalculateViewportOffset();
}
void AsyncPanZoomController::ScaleWithFocus(float aScale,
@ -3335,7 +3354,7 @@ void AsyncPanZoomController::ScaleWithFocus(float aScale,
// at the same position on the screen before and after the change in zoom. The below code
// accomplishes this; see https://bugzilla.mozilla.org/show_bug.cgi?id=923431#c6 for an
// in-depth explanation of how.
mFrameMetrics.SetScrollOffset((mFrameMetrics.GetScrollOffset() + aFocus) - (aFocus / aScale));
SetScrollOffset((mFrameMetrics.GetScrollOffset() + aFocus) - (aFocus / aScale));
}
/**
@ -3458,9 +3477,8 @@ const ScreenMargin AsyncPanZoomController::CalculatePendingDisplayPort(
displayPort.MoveBy(velocity * paintFactor * gfxPrefs::APZVelocityBias());
APZC_LOG_FM(aFrameMetrics,
"Calculated displayport as (%f %f %f %f) from velocity %s paint time %f metrics",
displayPort.x, displayPort.y, displayPort.Width(), displayPort.Height(),
ToString(aVelocity).c_str(), paintFactor);
"Calculated displayport as %s from velocity %s paint time %f metrics",
Stringify(displayPort).c_str(), ToString(aVelocity).c_str(), paintFactor);
CSSMargin cssMargins;
cssMargins.left = -displayPort.X();
@ -3610,6 +3628,10 @@ AsyncPanZoomController::RequestContentRepaint(const FrameMetrics& aFrameMetrics,
mLastPaintRequestMetrics.GetViewport().Width()) < EPSILON &&
fabsf(aFrameMetrics.GetViewport().Height() -
mLastPaintRequestMetrics.GetViewport().Height()) < EPSILON &&
fabsf(aFrameMetrics.GetViewport().X() -
mLastPaintRequestMetrics.GetViewport().X()) < EPSILON &&
fabsf(aFrameMetrics.GetViewport().Y() -
mLastPaintRequestMetrics.GetViewport().Y()) < EPSILON &&
aFrameMetrics.GetScrollGeneration() ==
mLastPaintRequestMetrics.GetScrollGeneration() &&
aFrameMetrics.GetScrollUpdateType() ==
@ -4068,20 +4090,6 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe
}
}
bool needContentRepaint = false;
bool viewportUpdated = false;
if (FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Width(), mFrameMetrics.GetCompositionBounds().Width()) &&
FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Height(), mFrameMetrics.GetCompositionBounds().Height())) {
// Remote content has sync'd up to the composition geometry
// change, so we can accept the viewport it's calculated.
if (mFrameMetrics.GetViewport().Width() != aLayerMetrics.GetViewport().Width() ||
mFrameMetrics.GetViewport().Height() != aLayerMetrics.GetViewport().Height()) {
needContentRepaint = true;
viewportUpdated = true;
}
mFrameMetrics.SetViewport(aLayerMetrics.GetViewport());
}
// If the layers update was not triggered by our own repaint request, then
// we want to take the new scroll offset. Check the scroll generation as well
// to filter duplicate calls to NotifyLayersUpdated with the same scroll offset
@ -4101,6 +4109,22 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe
// TODO if we're in a drag and scrollOffsetUpdated is set then we want to
// ignore it
bool needContentRepaint = false;
bool viewportUpdated = false;
if (FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Width(), mFrameMetrics.GetCompositionBounds().Width()) &&
FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Height(), mFrameMetrics.GetCompositionBounds().Height())) {
// Remote content has sync'd up to the composition geometry
// change, so we can accept the viewport it's calculated.
if (mFrameMetrics.GetViewport().Width() != aLayerMetrics.GetViewport().Width() ||
mFrameMetrics.GetViewport().Height() != aLayerMetrics.GetViewport().Height()) {
needContentRepaint = true;
viewportUpdated = true;
}
if (viewportUpdated || scrollOffsetUpdated) {
mFrameMetrics.SetViewport(aLayerMetrics.GetViewport());
}
}
#if defined(MOZ_WIDGET_ANDROID)
if (aLayerMetrics.IsRootContent()) {
if (APZCTreeManager* manager = GetApzcTreeManager()) {
@ -4204,7 +4228,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe
// becomes incorrect for the purposes of calculating the LD transform. To
// correct this we need to update mExpectedGeckoMetrics to be the
// last thing we know was painted by Gecko.
mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
CopyScrollInfoFrom(aLayerMetrics);
mCompositedLayoutViewport = mFrameMetrics.GetViewport();
mCompositedScrollOffset = mFrameMetrics.GetScrollOffset();
mExpectedGeckoMetrics = aLayerMetrics;
@ -4229,7 +4253,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe
// Even if we didn't accept a new scroll offset from content, the
// scrollable rect may have changed in a way that makes our local
// scroll offset out of bounds, so re-clamp it.
mFrameMetrics.ClampAndSetScrollOffset(mFrameMetrics.GetScrollOffset());
ClampAndSetScrollOffset(mFrameMetrics.GetScrollOffset());
}
}
@ -4407,8 +4431,10 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) {
}
endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
endZoomToMetrics.RecalculateViewportOffset();
StartAnimation(new ZoomAnimation(
*this,
mFrameMetrics.GetScrollOffset(),
mFrameMetrics.GetZoom(),
endZoomToMetrics.GetScrollOffset(),

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

@ -665,6 +665,23 @@ protected:
*/
nsEventStatus OnCancelTap(const TapGestureInput& aEvent);
/**
* The following five methods modify the scroll offset. For the APZC
* representing the RCD-RSF, they also recalculate the offset of the layout
* viewport.
*/
/**
* Scroll the scroll frame to an X,Y offset.
*/
void SetScrollOffset(const CSSPoint& aOffset);
/**
* Scroll the scroll frame to an X,Y offset, clamping the resulting scroll
* offset to the scroll range.
*/
void ClampAndSetScrollOffset(const CSSPoint& aOffset);
/**
* Scroll the scroll frame by an X,Y offset.
* The resulting scroll offset is not clamped to the scrollable rect;
@ -674,10 +691,15 @@ protected:
/**
* Scroll the scroll frame by an X,Y offset, clamping the resulting
* scroll offset to the scrollable rect.
* scroll offset to the scroll range.
*/
void ScrollByAndClamp(const CSSPoint& aOffset);
/**
* Copy the scroll offset and scroll generation from |aFrameMetrics|.
*/
void CopyScrollInfoFrom(const FrameMetrics& aFrameMetrics);
/**
* Scales the viewport by an amount (note that it multiplies this scale in to
* the current scale, it doesn't set it to |aScale|). Also considers a focus
@ -1185,6 +1207,7 @@ private:
friend class GenericScrollAnimation;
friend class WheelScrollAnimation;
friend class KeyboardScrollAnimation;
friend class ZoomAnimation;
friend class GenericOverscrollEffect;
friend class WidgetOverscrollEffect;

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

@ -155,7 +155,7 @@ public:
mApzc.mX.AdjustDisplacement(offset.x, adjustedOffset.x, overscroll.x);
mApzc.mY.AdjustDisplacement(offset.y, adjustedOffset.y, overscroll.y);
aFrameMetrics.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom());
mApzc.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom());
// The fling may have caused us to reach the end of our scroll range.
if (!IsZero(overscroll)) {

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

@ -105,7 +105,7 @@ GenericScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, const TimeDuration
return false;
}
aFrameMetrics.ScrollBy(adjustedOffset / zoom);
mApzc.ScrollBy(adjustedOffset / zoom);
return !finished;
}

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

@ -19,9 +19,10 @@ protected:
FrameMetrics GetPinchableFrameMetrics()
{
FrameMetrics fm;
fm.SetCompositionBounds(ParentLayerRect(200, 200, 100, 200));
fm.SetCompositionBounds(ParentLayerRect(0, 0, 100, 200));
fm.SetScrollableRect(CSSRect(0, 0, 980, 1000));
fm.SetScrollOffset(CSSPoint(300, 300));
fm.SetViewport(CSSRect(300, 300, 100, 200));
fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
// APZC only allows zooming on the root scrollable frame.
fm.SetIsRootContent(true);
@ -56,10 +57,10 @@ protected:
FrameMetrics fm = apzc->GetFrameMetrics();
if (aShouldTriggerPinch) {
// the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80
// the visible area of the document in CSS pixels is now x=325 y=330 w=40 h=80
EXPECT_EQ(2.5f, fm.GetZoom().ToScaleFactor().scale);
EXPECT_EQ(305, fm.GetScrollOffset().x);
EXPECT_EQ(310, fm.GetScrollOffset().y);
EXPECT_EQ(325, fm.GetScrollOffset().x);
EXPECT_EQ(330, fm.GetScrollOffset().y);
} else {
// The frame metrics should stay the same since touch-action:none makes
// apzc ignore pinch gestures.
@ -88,9 +89,9 @@ protected:
fm = apzc->GetFrameMetrics();
if (aShouldTriggerPinch) {
// the visible area of the document in CSS pixels is now x=880 y=0 w=100 h=200
// the visible area of the document in CSS pixels is now x=805 y=0 w=100 h=200
EXPECT_EQ(1.0f, fm.GetZoom().ToScaleFactor().scale);
EXPECT_EQ(880, fm.GetScrollOffset().x);
EXPECT_EQ(805, fm.GetScrollOffset().x);
EXPECT_EQ(0, fm.GetScrollOffset().y);
} else {
EXPECT_EQ(2.0f, fm.GetZoom().ToScaleFactor().scale);
@ -341,6 +342,85 @@ TEST_F(APZCPinchTester, Panning_TwoFinger_ZoomDisabled) {
EXPECT_EQ(2.0, fm.GetZoom().ToScaleFactor().scale);
}
TEST_F(APZCPinchTester, Panning_Beyond_LayoutViewport) {
apzc->SetFrameMetrics(GetPinchableFrameMetrics());
MakeApzcZoomable();
// Case 1 - visual viewport is still inside layout viewport.
Pan(apzc, 350, 300, PanOptions::NoFling);
FrameMetrics fm = apzc->GetFrameMetrics();
// It starts from (300, 300) pans by (0, 50) screen pixels, but there is a
// 2x zoom, which causes the scroll offset to change by half of that (0, 25).
// But the visual viewport is still inside the layout viewport.
EXPECT_EQ(300, fm.GetScrollOffset().x);
EXPECT_EQ(325, fm.GetScrollOffset().y);
EXPECT_EQ(300, fm.GetViewport().X());
EXPECT_EQ(300, fm.GetViewport().Y());
// Case 2 - visual viewport crosses the bottom boundary of the layout
// viewport.
Pan(apzc, 525, 325, PanOptions::NoFling);
fm = apzc->GetFrameMetrics();
// It starts from (300, 325) pans by (0, 200) screen pixels, but there is a
// 2x zoom, which causes the scroll offset to change by half of that
// (0, 100). The visual viewport crossed the bottom boundary of the layout
// viewport by 25px.
EXPECT_EQ(300, fm.GetScrollOffset().x);
EXPECT_EQ(425, fm.GetScrollOffset().y);
EXPECT_EQ(300, fm.GetViewport().X());
EXPECT_EQ(325, fm.GetViewport().Y());
// Case 3 - visual viewport crosses the top boundary of the layout viewport.
Pan(apzc, 425, 775, PanOptions::NoFling);
fm = apzc->GetFrameMetrics();
// It starts from (300, 425) pans by (0, -350) screen pixels, but there is a
// 2x zoom, which causes the scroll offset to change by half of that
// (0, -175). The visual viewport crossed the top of the layout viewport by
// 75px.
EXPECT_EQ(300, fm.GetScrollOffset().x);
EXPECT_EQ(250, fm.GetScrollOffset().y);
EXPECT_EQ(300, fm.GetViewport().X());
EXPECT_EQ(250, fm.GetViewport().Y());
// Case 4 - visual viewport crosses the left boundary of the layout viewport.
Pan(apzc, ScreenIntPoint(150, 10), ScreenIntPoint(350, 10), PanOptions::NoFling);
fm = apzc->GetFrameMetrics();
// It starts from (300, 250) pans by (-200, 0) screen pixels, but there is a
// 2x zoom, which causes the scroll offset to change by half of that
// (-100, 0). The visual viewport crossed the left boundary of the layout
// viewport by 100px.
EXPECT_EQ(200, fm.GetScrollOffset().x);
EXPECT_EQ(250, fm.GetScrollOffset().y);
EXPECT_EQ(200, fm.GetViewport().X());
EXPECT_EQ(250, fm.GetViewport().Y());
// Case 5 - visual viewport crosses the right boundary of the layout viewport.
Pan(apzc, ScreenIntPoint(350, 10), ScreenIntPoint(150, 10), PanOptions::NoFling);
fm = apzc->GetFrameMetrics();
// It starts from (200, 250) pans by (200, 0) screen pixels, but there is a
// 2x zoom, which causes the scroll offset to change by half of that
// (100, 0). The visual viewport crossed the right boundary of the layout
// viewport by 50px.
EXPECT_EQ(300, fm.GetScrollOffset().x);
EXPECT_EQ(250, fm.GetScrollOffset().y);
EXPECT_EQ(250, fm.GetViewport().X());
EXPECT_EQ(250, fm.GetViewport().Y());
// Case 6 - visual viewport crosses both the vertical and horizontal
// boundaries of the layout viewport by moving diagonally towards the
// top-right corner.
Pan(apzc, ScreenIntPoint(350, 200), ScreenIntPoint(150, 400), PanOptions::NoFling);
fm = apzc->GetFrameMetrics();
// It starts from (300, 250) pans by (200, -200) screen pixels, but there is
// a 2x zoom, which causes the scroll offset to change by half of that
// (100, -100). The visual viewport moved by (100, -100) outside the
// boundary of the layout viewport.
EXPECT_EQ(400, fm.GetScrollOffset().x);
EXPECT_EQ(150, fm.GetScrollOffset().y);
EXPECT_EQ(350, fm.GetViewport().X());
EXPECT_EQ(150, fm.GetViewport().Y());
}
TEST_F(APZCPinchGestureDetectorTester, Pinch_APZZoom_Disabled) {
SCOPED_GFX_PREF(APZAllowZooming, bool, false);

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

@ -0,0 +1,80 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Ensure layout viewport responds to panning while pinched</title>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
#content {
height: 5000px;
width: 5000px;
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
}
</style>
</head>
<body>
<div id="content"></div>
<script type="application/javascript">
const RESOLUTION = 4;
const OFFSET_SCREEN_PX = 50;
const OFFSET_CSS_PX = OFFSET_SCREEN_PX / RESOLUTION;
function computeDelta(visual) {
// Compute the distance from the right/bottom edge of the visual
// viewport to the same edge of the layout viewport and add the desired
// offset to that.
const layout = visual * RESOLUTION;
return layout - visual + OFFSET_SCREEN_PX;
}
function* test(testDriver) {
const target = document.getElementById("content");
const cases = [
{
x: 0,
y: 0,
dx: (width) => -computeDelta(width),
dy: (height) => 0,
expected: {
x: [OFFSET_CSS_PX, "x-offset was adjusted"],
y: [0, "y-offset was not affected"],
},
},
{
x: OFFSET_SCREEN_PX,
y: 0,
dx: (width) => 0,
dy: (height) => -computeDelta(height),
expected: {
x: [OFFSET_CSS_PX, "x-offset was not affected"],
y: [OFFSET_CSS_PX, "y-offset was adjusted"],
},
},
];
for (let c of cases) {
yield synthesizeNativeTouchDrag(target,
c.x,
c.y,
c.dx(document.documentElement.clientWidth),
c.dy(document.documentElement.clientHeight),
testDriver);
yield waitForApzFlushedRepaints(testDriver);
is(window.scrollX, c.expected.x[0], c.expected.x[1]);
is(window.scrollY, c.expected.y[0], c.expected.y[1]);
}
}
SpecialPowers.getDOMWindowUtils(window).setResolutionAndScaleTo(RESOLUTION);
waitUntilApzStable().then(runContinuation(test)).then(subtestDone);
</script>
</body>
</html>

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

@ -17,27 +17,35 @@ var prefs = [
["apz.touch_start_tolerance", "0.0"],
// The subtests in this test do touch-drags to pan the page, but we don't
// want those pans to turn into fling animations, so we increase the
// fling-stop threshold velocity to absurdly high.
["apz.fling_stopped_threshold", "10000"],
// fling-min threshold velocity to an arbitrarily large value.
["apz.fling_min_velocity_threshold", "10000"],
// The helper_bug1280013's div gets a displayport on scroll, but if the
// test takes too long the displayport can expire before we read the value
// out of the test. So we disable displayport expiry for these tests.
["apz.displayport_expiry_ms", 0],
// Prevent the dynamic toolbar from interfering with main-thread scroll
// offset values.
["browser.chrome.dynamictoolbar", false],
];
// Increase the tap timeouts so the double-tap is still detected in case of
// random delays during testing.
var doubletap_prefs = prefs.slice(); // make a copy
doubletap_prefs.push(["ui.click_hold_context_menus.delay", 10000]);
doubletap_prefs.push(["apz.max_tap_time", 10000]);
var doubletap_prefs = [
...prefs,
["ui.click_hold_context_menus.delay", 10000],
["apz.max_tap_time", 10000],
];
var subtests = [
{'file': 'helper_bug1280013.html', 'prefs': prefs},
{'file': 'helper_basic_zoom.html', 'prefs': prefs},
{'file': 'helper_zoomed_pan.html', 'prefs': prefs},
{'file': 'helper_basic_doubletap_zoom.html', 'prefs': doubletap_prefs},
];
if (isApzEnabled()) {
// This has a lot of subtests, and Android emulators are slow.
SimpleTest.requestLongerTimeout(2);
SimpleTest.waitForExplicitFinish();
window.onload = function() {
runSubtestsSeriallyInFreshWindows(subtests)

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

@ -78,7 +78,9 @@ static CSSPoint
ScrollFrameTo(nsIScrollableFrame* aFrame, const FrameMetrics& aMetrics, bool& aSuccessOut)
{
aSuccessOut = false;
CSSPoint targetScrollPosition = aMetrics.GetScrollOffset();
CSSPoint targetScrollPosition = aMetrics.IsRootContent()
? aMetrics.GetViewport().TopLeft()
: aMetrics.GetScrollOffset();
if (!aFrame) {
return targetScrollPosition;
@ -93,18 +95,20 @@ ScrollFrameTo(nsIScrollableFrame* aFrame, const FrameMetrics& aMetrics, bool& aS
return geckoScrollPosition;
}
// If the frame is overflow:hidden on a particular axis, we don't want to allow
// user-driven scroll on that axis. Simply set the scroll position on that axis
// to whatever it already is. Note that this will leave the APZ's async scroll
// position out of sync with the gecko scroll position, but APZ can deal with that
// (by design). Note also that when we run into this case, even if both axes
// have overflow:hidden, we want to set aSuccessOut to true, so that the displayport
// follows the async scroll position rather than the gecko scroll position.
// If this is the root content with overflow:hidden, then APZ should not
// allow scrolling in such a way that moves the layout viewport.
//
// If this is overflow:hidden, but not the root content, then
// nsLayoutUtils::CalculateScrollableRectForFrame should have sized the
// scrollable rect in a way that prevents APZ from scrolling it at all.
//
// In either case, targetScrollPosition should be the same as
// geckoScrollPosition here.
if (aFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
targetScrollPosition.y = geckoScrollPosition.y;
MOZ_ASSERT(targetScrollPosition.y == geckoScrollPosition.y);
}
if (aFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
targetScrollPosition.x = geckoScrollPosition.x;
MOZ_ASSERT(targetScrollPosition.x == geckoScrollPosition.x);
}
// If the scrollable frame is currently in the middle of an async or smooth
@ -162,6 +166,15 @@ ScrollFrame(nsIContent* aContent,
// actual scroll offset.
APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
}
} else if (aMetrics.IsRootContent() &&
aMetrics.GetScrollOffset() != aMetrics.GetViewport().TopLeft()) {
// APZ uses the visual viewport's offset to calculate where to place the
// display port, so the display port is misplaced when a pinch zoom occurs.
//
// We need to force a display port adjustment in the following paint to
// account for a difference between mScrollOffset and the actual scroll
// offset in repaints requested by AsyncPanZoomController::NotifyLayersUpdated.
APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
} else {
// For whatever reason we couldn't update the scroll offset on the scroll frame,
// which means the data APZ used for its displayport calculation is stale. Fall

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

@ -16,7 +16,7 @@
*/
/* fluent-dom@0.2.0 */
/* fluent-dom@aa95b1f (July 10, 2018) */
const { Localization } =
ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
@ -403,13 +403,12 @@ const L10N_ELEMENT_QUERY = `[${L10NID_ATTR_NAME}]`;
*/
class DOMLocalization extends Localization {
/**
* @param {Window} windowElement
* @param {Array<String>} resourceIds - List of resource IDs
* @param {Function} generateMessages - Function that returns a
* generator over MessageContexts
* @returns {DOMLocalization}
*/
constructor(windowElement, resourceIds, generateMessages) {
constructor(resourceIds, generateMessages) {
super(resourceIds, generateMessages);
// A Set of DOM trees observed by the `MutationObserver`.
@ -418,10 +417,8 @@ class DOMLocalization extends Localization {
this.pendingrAF = null;
// list of elements pending for translation.
this.pendingElements = new Set();
this.windowElement = windowElement;
this.mutationObserver = new windowElement.MutationObserver(
mutations => this.translateMutations(mutations)
);
this.windowElement = null;
this.mutationObserver = null;
this.observerConfig = {
attribute: true,
@ -519,6 +516,18 @@ class DOMLocalization extends Localization {
}
}
if (this.windowElement) {
if (this.windowElement !== newRoot.ownerGlobal) {
throw new Error(`Cannot connect a root:
DOMLocalization already has a root from a different window`);
}
} else {
this.windowElement = newRoot.ownerGlobal;
this.mutationObserver = new this.windowElement.MutationObserver(
mutations => this.translateMutations(mutations)
);
}
this.roots.add(newRoot);
this.mutationObserver.observe(newRoot, this.observerConfig);
}
@ -537,11 +546,20 @@ class DOMLocalization extends Localization {
*/
disconnectRoot(root) {
this.roots.delete(root);
// Pause and resume the mutation observer to stop observing `root`.
// Pause the mutation observer to stop observing `root`.
this.pauseObserving();
this.resumeObserving();
return this.roots.size === 0;
if (this.roots.size === 0) {
this.mutationObserver = null;
this.windowElement = null;
this.pendingrAF = null;
this.pendingElements.clear();
return true;
}
// Resume observing all other roots.
this.resumeObserving();
return false;
}
/**
@ -562,6 +580,10 @@ class DOMLocalization extends Localization {
* @private
*/
pauseObserving() {
if (!this.mutationObserver) {
return;
}
this.translateMutations(this.mutationObserver.takeRecords());
this.mutationObserver.disconnect();
}
@ -572,6 +594,10 @@ class DOMLocalization extends Localization {
* @private
*/
resumeObserving() {
if (!this.mutationObserver) {
return;
}
for (const root of this.roots) {
this.mutationObserver.observe(root, this.observerConfig);
}

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

@ -16,7 +16,7 @@
*/
/* fluent-dom@0.2.0 */
/* fluent-dom@aa95b1f (July 10, 2018) */
/* eslint no-console: ["error", { allow: ["warn", "error"] }] */
/* global console */
@ -109,7 +109,7 @@ class Localization {
*
* @returns {Localization}
*/
constructor(resourceIds, generateMessages = defaultGenerateMessages) {
constructor(resourceIds = [], generateMessages = defaultGenerateMessages) {
this.resourceIds = resourceIds;
this.generateMessages = generateMessages;
this.ctxs =
@ -119,11 +119,13 @@ class Localization {
addResourceIds(resourceIds) {
this.resourceIds.push(...resourceIds);
this.onChange();
return this.resourceIds.length;
}
removeResourceIds(resourceIds) {
this.resourceIds = this.resourceIds.filter(r => !resourceIds.includes(r));
this.onChange();
return this.resourceIds.length;
}
/**
@ -277,6 +279,7 @@ class Localization {
onChange() {
this.ctxs =
new CachedAsyncIterable(this.generateMessages(this.resourceIds));
this.ctxs.touchNext(2);
}
}

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

@ -16,7 +16,7 @@
*/
/* fluent@0.6.3 */
/* fluent@aa95b1f (July 10, 2018) */
/* eslint no-magic-numbers: [0] */
@ -1670,6 +1670,22 @@ function resolve(ctx, args, message, errors = []) {
return Type(env, message).toString(ctx);
}
/**
* Fluent Resource is a structure storing a map
* of localization entries.
*/
class FluentResource extends Map {
constructor(entries, errors = []) {
super(entries);
this.errors = errors;
}
static fromString(source) {
const [entries, errors] = parse(source);
return new FluentResource(Object.entries(entries), errors);
}
}
/**
* Message contexts are single-language stores of translations. They are
* responsible for parsing translation resources in the Fluent syntax and can
@ -1712,11 +1728,17 @@ class MessageContext {
* - `useIsolating` - boolean specifying whether to use Unicode isolation
* marks (FSI, PDI) for bidi interpolations.
*
* - `transform` - a function used to transform string parts of patterns.
*
* @param {string|Array<string>} locales - Locale or locales of the context
* @param {Object} [options]
* @returns {MessageContext}
*/
constructor(locales, { functions = {}, useIsolating = true, transform = v => v } = {}) {
constructor(locales, {
functions = {},
useIsolating = true,
transform = v => v
} = {}) {
this.locales = Array.isArray(locales) ? locales : [locales];
this._terms = new Map();
@ -1778,8 +1800,31 @@ class MessageContext {
* @returns {Array<Error>}
*/
addMessages(source) {
const [entries, errors] = parse(source);
for (const id in entries) {
const res = FluentResource.fromString(source);
return this.addResource(res);
}
/**
* Add a translation resource to the context.
*
* The translation resource must be a proper FluentResource
* parsed by `MessageContext.parseResource`.
*
* let res = MessageContext.parseResource("foo = Foo");
* ctx.addResource(res);
* ctx.getMessage('foo');
*
* // Returns a raw representation of the 'foo' message.
*
* Parsed entities should be formatted with the `format` method in case they
* contain logic (references, select expressions etc.).
*
* @param {FluentResource} res - FluentResource object.
* @returns {Array<Error>}
*/
addResource(res) {
const errors = res.errors.slice();
for (const [id, value] of res) {
if (id.startsWith("-")) {
// Identifiers starting with a dash (-) define terms. Terms are private
// and cannot be retrieved from MessageContext.
@ -1787,13 +1832,13 @@ class MessageContext {
errors.push(`Attempt to override an existing term: "${id}"`);
continue;
}
this._terms.set(id, entries[id]);
this._terms.set(id, value);
} else {
if (this._messages.has(id)) {
errors.push(`Attempt to override an existing message: "${id}"`);
continue;
}
this._messages.set(id, entries[id]);
this._messages.set(id, value);
}
}

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

@ -44,7 +44,7 @@
const resourceIds = getResourceLinks(document.head || document);
document.l10n = new DOMLocalization(window, resourceIds);
document.l10n = new DOMLocalization(resourceIds);
// Trigger the first two contexts to be loaded eagerly.
document.l10n.ctxs.touchNext(2);

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

@ -33,7 +33,6 @@ new-tab
const domLoc = new DOMLocalization(
window,
[],
generateMessages
);

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

@ -25,7 +25,6 @@ key2 = Value for <a>Key 2<a/>.
SimpleTest.waitForExplicitFinish();
addLoadEvent(async () => {
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -17,7 +17,6 @@
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -17,7 +17,6 @@
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -16,7 +16,6 @@
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -23,7 +23,6 @@
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -23,7 +23,6 @@
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -22,7 +22,6 @@
addLoadEvent(async () => {
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -22,7 +22,6 @@
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -22,7 +22,6 @@
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -26,7 +26,6 @@ key2 =
async function test() {
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -25,7 +25,6 @@ key2 = Visit <a data-l10n-name="link">this link<a/>.
SimpleTest.waitForExplicitFinish();
addLoadEvent(async () => {
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -16,7 +16,6 @@
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -23,7 +23,6 @@
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -23,7 +23,6 @@
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -23,7 +23,6 @@
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
window,
[],
mockGenerateMessages
);

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

@ -2315,6 +2315,10 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
MOZ_ASSERT(cases->isKind(ParseNodeKind::LexicalScope) ||
cases->isKind(ParseNodeKind::StatementList));
// Ensure that the column of the switch statement is set properly.
if (!updateSourceCoordNotes(pn->pn_pos.begin))
return false;
// Emit code for the discriminant.
if (!emitTree(pn->pn_left))
return false;
@ -4574,6 +4578,11 @@ BytecodeEmitter::emitIf(ParseNode* pn)
IfEmitter ifThenElse(this);
if_again:
// Make sure this code is attributed to the "if" so that it gets a useful
// column number, instead of the default 0 value.
if (!updateSourceCoordNotes(pn->pn_pos.begin))
return false;
/* Emit code for the condition before pushing stmtInfo. */
if (!emitTree(pn->pn_kid1))
return false;
@ -4704,6 +4713,10 @@ BytecodeEmitter::emitLexicalScope(ParseNode* pn)
bool
BytecodeEmitter::emitWith(ParseNode* pn)
{
// Ensure that the column of the 'with' is set properly.
if (!updateSourceCoordNotes(pn->pn_pos.begin))
return false;
if (!emitTree(pn->pn_left))
return false;
@ -5842,6 +5855,10 @@ BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isA
bool
BytecodeEmitter::emitDo(ParseNode* pn)
{
// Ensure that the column of the 'do' is set properly.
if (!updateSourceCoordNotes(pn->pn_pos.begin))
return false;
/* Emit an annotated nop so IonBuilder can recognize the 'do' loop. */
unsigned noteIndex;
if (!newSrcNote(SRC_WHILE, &noteIndex))
@ -8495,11 +8512,19 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
break;
case ParseNodeKind::Break:
// Ensure that the column of the 'break' is set properly.
if (!updateSourceCoordNotes(pn->pn_pos.begin))
return false;
if (!emitBreak(pn->as<BreakStatement>().label()))
return false;
break;
case ParseNodeKind::Continue:
// Ensure that the column of the 'continue' is set properly.
if (!updateSourceCoordNotes(pn->pn_pos.begin))
return false;
if (!emitContinue(pn->as<ContinueStatement>().label()))
return false;
break;

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

@ -0,0 +1,74 @@
// Set breakpoints "everywhere" in a function, then call the function and check that
// the breakpoints were added are at the expected columns, and the breakpoints
// were executed in th expected order.
//
// `code` is a JS Script. The final line should define a function `f` to validate.
// `expectedBpts` is a string of spaces and carets ('^'). Throws if we don't hit
// breakpoints on exactly the columns indicated by the carets.
// `expectedOrdering` is a string of integer indices for the offsets that are
// executed, in the order that then are executed. Test code can also push
// additional items into this string using items.push("!").
function assertOffsetColumns(code, expectedBpts, expectedOrdering = null) {
if (expectedOrdering === null) {
// The default ordering simply runs the breakpoints in order.
expectedOrdering = Array.from(expectedBpts.match(/\^/g), (_, i) => i).join(" ");
}
// Define the function `f` in a new global.
const global = newGlobal();
const lines = code.split(/\r?\n|\r]/g);
const initCode = lines.slice(0, -1).join("\n");
const execCode = lines[lines.length - 1];
// Treat everything but the last line as initialization code.
global.eval(initCode);
// Run the test code itself.
global.eval(execCode);
// Allow some tests to append to a log that will show up in expected ordering.
const hits = global.hits = [];
const bpts = new Set();
// Set breakpoints everywhere and call the function.
const dbg = new Debugger;
const script = dbg.addDebuggee(global).makeDebuggeeValue(global.f).script;
for (const offset of script.getAllColumnOffsets()) {
assertEq(offset.lineNumber, 1);
assertEq(offset.columnNumber < execCode.length, true);
bpts.add(offset.columnNumber);
script.setBreakpoint(offset.offset, {
hit(frame) {
hits.push(offset.columnNumber);
},
});
}
global.f(3);
const actualBpts = Array.from(execCode, (_, i) => {
return bpts.has(i) ? "^" : " ";
}).join("");
if (actualBpts.trimEnd() !== expectedBpts.trimEnd()) {
throw new Error(`Assertion failed:
code: ${execCode}
expected bpts: ${expectedBpts}
actual bpts: ${actualBpts}\n`);
}
const indexLookup = new Map(
Array.from(bpts).sort().map((col, i) => [col, i]));
const actualOrdering = hits
.map(item => typeof item === "number" ? indexLookup.get(item) : item)
.join(" ");
if (actualOrdering.trimEnd() !== expectedOrdering.trimEnd()) {
throw new Error(`Assertion failed:
code: ${execCode}
bpts: ${expectedBpts}
expected order: ${expectedOrdering}
actual order: ${actualOrdering}\n`);
}
}

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

@ -1,19 +0,0 @@
// getColumnOffsets correctly places the various parts of a ForStatement.
var global = newGlobal();
Debugger(global).onDebuggerStatement = function (frame) {
var script = frame.eval("f").return.script;
script.getAllColumnOffsets().forEach(function (offset) {
script.setBreakpoint(offset.offset, {
hit: function (frame) {
assertEq(offset.lineNumber, 1);
global.log += offset.columnNumber + " ";
}
});
});
};
global.log = '';
global.eval("function f(n) { for (var i = 0; i < n; ++i) log += '. '; log += '! '; } debugger;");
global.f(3);
assertEq(global.log, "25 32 44 . 39 32 44 . 39 32 44 . 39 32 57 ! 70 ");

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

@ -1,21 +0,0 @@
// getColumnOffsets correctly places multiple variable declarations.
var global = newGlobal();
Debugger(global).onDebuggerStatement = function (frame) {
var script = frame.eval("f").return.script;
script.getAllColumnOffsets().forEach(function (offset) {
script.setBreakpoint(offset.offset, {
hit: function (frame) {
assertEq(offset.lineNumber, 1);
global.log += offset.columnNumber + " ";
}
});
});
};
global.log = '';
global.eval("function f(n){var w0,x1=3,y2=4,z3=9} debugger;");
global.f(3);
// Should have hit each variable declared.
assertEq(global.log, "21 26 31 35 ");

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

@ -1,20 +0,0 @@
// getColumnOffsets correctly places comma separated expressions.
var global = newGlobal();
Debugger(global).onDebuggerStatement = function (frame) {
var script = frame.eval("f").return.script;
script.getAllColumnOffsets().forEach(function (offset) {
script.setBreakpoint(offset.offset, {
hit: function (frame) {
assertEq(offset.lineNumber, 1);
global.log += offset.columnNumber + " ";
}
});
});
};
global.log = '';
global.eval("function f(n){print(n),print(n),print(n)} debugger;");
global.f(3);
// Should hit each call that was separated by commas.
assertEq(global.log, "14 23 32 40 ");

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

@ -1,20 +0,0 @@
// getColumnOffsets correctly places object properties.
var global = newGlobal();
Debugger(global).onDebuggerStatement = function (frame) {
var script = frame.eval("f").return.script;
script.getAllColumnOffsets().forEach(function (offset) {
script.setBreakpoint(offset.offset, {
hit: function (frame) {
assertEq(offset.lineNumber, 1);
global.log += offset.columnNumber + " ";
}
});
});
};
global.log = '';
global.eval("function f(n){var o={a:1,b:2,c:3}} debugger;");
global.f(3);
// Should hit each property in the object.
assertEq(global.log, "18 21 25 29 33 ");

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

@ -1,20 +0,0 @@
// getColumnOffsets correctly places array properties.
var global = newGlobal();
Debugger(global).onDebuggerStatement = function (frame) {
var script = frame.eval("f").return.script;
script.getAllColumnOffsets().forEach(function (offset) {
script.setBreakpoint(offset.offset, {
hit: function (frame) {
assertEq(offset.lineNumber, 1);
global.log += offset.columnNumber + " ";
}
});
});
};
global.log = '';
global.eval("function f(n){var a=[1,2,n]} debugger;");
global.f(3);
// Should hit each item in the array.
assertEq(global.log, "18 21 23 25 27 ");

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

@ -1,28 +0,0 @@
// getColumnOffsets correctly places function calls.
var global = newGlobal();
Debugger(global).onDebuggerStatement = function (frame) {
var script = frame.eval("f").return.script;
script.getAllColumnOffsets().forEach(function (offset) {
script.setBreakpoint(offset.offset, {
hit: function (frame) {
assertEq(offset.lineNumber, 1);
global.log += offset.columnNumber + " ";
}
});
});
};
global.log = "";
global.eval("function ppppp() { return 1; }");
// 1 2 3 4
// 01234567890123456789012345678901234567890123456789
global.eval("function f(){ 1 && ppppp(ppppp()) && new Error() } debugger;");
global.f();
// 14 - Enter the function body
// 25 - Inner print()
// 19 - Outer print()
// 37 - new Error()
// 49 - Exit the function body
assertEq(global.log, "14 25 19 37 49 ");

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

@ -0,0 +1,85 @@
load(libdir + "assert-offset-columns.js");
// getColumnOffsets correctly places the various parts of a ForStatement.
assertOffsetColumns(
"function f(n) { for (var i = 0; i < n; ++i) hits.push('.'); hits.push('!'); }",
" ^ ^ ^ ^ ^ ^",
"0 1 3 . 2 1 3 . 2 1 3 . 2 1 4 ! 5",
);
// getColumnOffsets correctly places multiple variable declarations.
assertOffsetColumns(
"function f(n){var w0,x1=3,y2=4,z3=9}",
" ^ ^ ^ ^",
);
// getColumnOffsets correctly places comma separated expressions.
assertOffsetColumns(
"function f(n){print(n),print(n),print(n)}",
" ^ ^ ^ ^",
);
// getColumnOffsets correctly places object properties.
assertOffsetColumns(
// Should hit each property in the object.
"function f(n){var o={a:1,b:2,c:3}}",
" ^ ^ ^ ^ ^",
);
// getColumnOffsets correctly places array properties.
assertOffsetColumns(
// Should hit each item in the array.
"function f(n){var a=[1,2,n]}",
" ^ ^ ^ ^ ^",
);
// getColumnOffsets correctly places function calls.
assertOffsetColumns(
"function ppppp() { return 1; }\n" +
"function f(){ 1 && ppppp(ppppp()) && new Error() }",
" ^ ^ ^ ^ ^",
"0 2 1 3 4",
);
// getColumnOffsets correctly places the various parts of a SwitchStatement.
assertOffsetColumns(
"function f(n) { switch(n) { default: print(n); } }",
" ^ ^ ^",
);
// getColumnOffsets correctly places the various parts of a BreakStatement.
assertOffsetColumns(
"function f(n) { do { print(n); break; } while(false); }",
" ^ ^ ^ ^",
);
// getColumnOffsets correctly places the various parts of a ContinueStatement.
assertOffsetColumns(
"function f(n) { do { print(n); continue; } while(false); }",
" ^ ^ ^ ^",
);
// getColumnOffsets correctly places the various parts of a WithStatement.
assertOffsetColumns(
"function f(n) { with({}) { print(n); } }",
" ^ ^ ^",
);
// getColumnOffsets correctly places the various parts of a IfStatement.
assertOffsetColumns(
"function f(n) { if (n == 3) print(n); }",
" ^ ^ ^",
);
// getColumnOffsets correctly places the various parts of a IfStatement
// with an if/else
assertOffsetColumns(
"function f(n) { if (n == 2); else if (n === 3) print(n); }",
" ^ ^ ^ ^",
);
// getColumnOffsets correctly places the various parts of a DoWhileStatement.
assertOffsetColumns(
"function f(n) { do { print(n); } while(false); }",
" ^ ^ ^",
);

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

@ -1455,7 +1455,7 @@
* Pushes a JS_UNINITIALIZED_LEXICAL value onto the stack, representing an
* uninitialized lexical binding.
*
* This opcode is used with the JSOP_INITLET opcode.
* This opcode is used with the JSOP_INITLEXICAL opcode.
* Category: Literals
* Type: Constants
* Operands:
@ -2025,7 +2025,7 @@
/* Lexical environment support. */ \
/*
* Replaces the current block on the env chain with a fresh block
* that copies all the bindings in the bock. This operation implements the
* that copies all the bindings in the block. This operation implements the
* behavior of inducing a fresh lexical environment for every iteration of a
* for(let ...; ...; ...) loop, if any declarations induced by such a loop
* are captured within the loop.

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

@ -34,6 +34,7 @@
#define STARTUP_COMPLETE_TOPIC "browser-delayed-startup-finished"
#define DOC_ELEM_INSERTED_TOPIC "document-element-inserted"
#define CONTENT_DOCUMENT_LOADED_TOPIC "content-document-loaded"
#define CACHE_WRITE_TOPIC "browser-idle-startup-tasks-finished"
#define CLEANUP_TOPIC "xpcom-shutdown"
#define SHUTDOWN_TOPIC "quit-application-granted"
@ -240,10 +241,10 @@ ScriptPreloader::ScriptPreloader()
: mMonitor("[ScriptPreloader.mMonitor]")
, mSaveMonitor("[ScriptPreloader.mSaveMonitor]")
{
// We do not set the process type for child processes here because the
// remoteType in ContentChild is not ready yet.
if (XRE_IsParentProcess()) {
sProcessType = ProcessType::Parent;
} else {
sProcessType = GetChildProcessType(dom::ContentChild::GetSingleton()->GetRemoteType());
}
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
@ -254,13 +255,8 @@ ScriptPreloader::ScriptPreloader()
// as idle tasks for the first browser window have completed.
obs->AddObserver(this, STARTUP_COMPLETE_TOPIC, false);
obs->AddObserver(this, CACHE_WRITE_TOPIC, false);
} else {
// In the child process, we need to freeze the script cache before any
// untrusted code has been executed. The insertion of the first DOM
// document element may sometimes be earlier than is ideal, but at
// least it should always be safe.
obs->AddObserver(this, DOC_ELEM_INSERTED_TOPIC, false);
}
obs->AddObserver(this, SHUTDOWN_TOPIC, false);
obs->AddObserver(this, CLEANUP_TOPIC, false);
obs->AddObserver(this, CACHE_INVALIDATE_TOPIC, false);
@ -367,6 +363,7 @@ ScriptPreloader::Observe(nsISupports* subject, const char* topic, const char16_t
mStartupFinished = true;
} else if (!strcmp(topic, CACHE_WRITE_TOPIC)) {
obs->RemoveObserver(this, CACHE_WRITE_TOPIC);
MOZ_ASSERT(mStartupFinished);
MOZ_ASSERT(XRE_IsParentProcess());
@ -374,7 +371,7 @@ ScriptPreloader::Observe(nsISupports* subject, const char* topic, const char16_t
Unused << NS_NewNamedThread("SaveScripts",
getter_AddRefs(mSaveThread), this);
}
} else if (!strcmp(topic, DOC_ELEM_INSERTED_TOPIC)) {
} else if (mContentStartupFinishedTopic.Equals(topic)) {
// If this is an uninitialized about:blank viewer or a chrome: document
// (which should always be an XBL binding document), ignore it. We don't
// have to worry about it loading malicious content.
@ -407,8 +404,16 @@ ScriptPreloader::FinishContentStartup()
{
MOZ_ASSERT(XRE_IsContentProcess());
#ifdef DEBUG
if (mContentStartupFinishedTopic.Equals(CONTENT_DOCUMENT_LOADED_TOPIC)) {
MOZ_ASSERT(sProcessType == ProcessType::Privileged);
} else {
MOZ_ASSERT(sProcessType != ProcessType::Privileged);
}
#endif /* DEBUG */
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
obs->RemoveObserver(this, DOC_ELEM_INSERTED_TOPIC);
obs->RemoveObserver(this, mContentStartupFinishedTopic.get());
mSaveTimer = nullptr;
@ -441,7 +446,7 @@ ScriptPreloader::GetCacheFile(const nsAString& suffix)
return std::move(cacheFile);
}
static const uint8_t MAGIC[] = "mozXDRcachev001";
static const uint8_t MAGIC[] = "mozXDRcachev002";
Result<Ok, nsresult>
ScriptPreloader::OpenCache()
@ -503,6 +508,25 @@ ScriptPreloader::InitCache(const Maybe<ipc::FileDescriptor>& cacheFile, ScriptCa
mCacheInitialized = true;
mChildActor = cacheChild;
sProcessType = GetChildProcessType(dom::ContentChild::GetSingleton()->GetRemoteType());
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
MOZ_RELEASE_ASSERT(obs);
if (sProcessType == ProcessType::Privileged) {
// Since we control all of the documents loaded in the privileged
// content process, we can increase the window of active time for the
// ScriptPreloader to include the scripts that are loaded until the
// first document finishes loading.
mContentStartupFinishedTopic.AssignLiteral(CONTENT_DOCUMENT_LOADED_TOPIC);
} else {
// In the child process, we need to freeze the script cache before any
// untrusted code has been executed. The insertion of the first DOM
// document element may sometimes be earlier than is ideal, but at
// least it should always be safe.
mContentStartupFinishedTopic.AssignLiteral(DOC_ELEM_INSERTED_TOPIC);
}
obs->AddObserver(this, mContentStartupFinishedTopic.get(), false);
RegisterWeakMemoryReporter(this);

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

@ -41,6 +41,7 @@ namespace loader {
class ScriptCacheChild;
enum class ProcessType : uint8_t {
Uninitialized,
Parent,
Web,
Extension,
@ -111,6 +112,7 @@ public:
static ProcessType CurrentProcessType()
{
MOZ_ASSERT(sProcessType != ProcessType::Uninitialized);
return sProcessType;
}
@ -490,6 +492,7 @@ private:
ScriptCacheChild* mChildActor = nullptr;
nsString mBaseName;
nsCString mContentStartupFinishedTopic;
nsCOMPtr<nsIFile> mProfD;
nsCOMPtr<nsIThread> mSaveThread;

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

@ -15,6 +15,8 @@
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsIFileURL.h"
#include "ContentPrincipal.h"
#include "SystemPrincipal.h"
#include "jsapi.h"
#include "jsfriendapi.h"
@ -135,7 +137,7 @@ ReportError(JSContext* cx, const char* origMsg, nsIURI* uri)
static bool
PrepareScript(nsIURI* uri,
JSContext* cx,
HandleObject targetObj,
bool wantGlobalScript,
const char* uriStr,
const nsAString& charset,
const char* buf,
@ -162,7 +164,7 @@ PrepareScript(nsIURI* uri,
return false;
}
if (JS_IsGlobalObject(targetObj)) {
if (wantGlobalScript) {
return JS::Compile(cx, options, srcBuf, script);
}
return JS::CompileForNonSyntacticScope(cx, options, srcBuf, script);
@ -170,7 +172,7 @@ PrepareScript(nsIURI* uri,
// We only use lazy source when no special encoding is specified because
// the lazy source loader doesn't know the encoding.
options.setSourceIsLazy(true);
if (JS_IsGlobalObject(targetObj)) {
if (wantGlobalScript) {
return JS::Compile(cx, options, buf, len, script);
}
return JS::CompileForNonSyntacticScope(cx, options, buf, len, script);
@ -252,7 +254,9 @@ EvalScript(JSContext* cx,
// needed during the last startup in that scope. But for startups
// when a non-cached script is used (e.g., after add-on
// installation), this may be a Sandbox global, which may be
// nuked but held alive by the JSScript.
// nuked but held alive by the JSScript. We can avoid this problem
// by using a different scope when compiling the script. See
// useCompilationScope in ReadScript().
//
// In general, this isn't a problem, since add-on Sandboxes which
// use the script preloader are not destroyed until add-on shutdown,
@ -417,8 +421,8 @@ AsyncScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
RootedObject targetObj(cx, mTargetObj);
RootedObject loadScope(cx, mLoadScope);
if (!PrepareScript(uri, cx, targetObj, spec.get(), mCharset,
reinterpret_cast<const char*>(aBuf), aLength,
if (!PrepareScript(uri, cx, JS_IsGlobalObject(targetObj), spec.get(),
mCharset, reinterpret_cast<const char*>(aBuf), aLength,
mWantReturnValue, &script))
{
return NS_OK;
@ -506,6 +510,7 @@ mozJSSubScriptLoader::ReadScript(nsIURI* uri,
const char* uriStr,
nsIIOService* serv,
bool wantReturnValue,
bool useCompilationScope,
MutableHandleScript script)
{
script.set(nullptr);
@ -553,8 +558,22 @@ mozJSSubScriptLoader::ReadScript(nsIURI* uri,
rv = NS_ReadInputStreamToString(instream, buf, len);
NS_ENSURE_SUCCESS(rv, false);
return PrepareScript(uri, cx, targetObj, uriStr, charset,
buf.get(), len, wantReturnValue,
Maybe<JSAutoRealm> ar;
// Note that when using the ScriptPreloader cache with loadSubScript, there
// will be a side-effect of keeping the global that the script was compiled
// for alive. See note above in EvalScript().
//
// This will compile the script in XPConnect compilation scope. When the
// script is evaluated, it will be cloned into the target scope to be
// executed, avoiding leaks on the first session when we don't have a
// startup cache.
if (useCompilationScope) {
ar.emplace(cx, xpc::CompilationScope());
}
return PrepareScript(uri, cx, JS_IsGlobalObject(targetObj),
uriStr, charset, buf.get(), len, wantReturnValue,
script);
}
@ -690,9 +709,29 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
// Suppress caching if we're compiling as content or if we're loading a
// blob: URI.
bool ignoreCache = options.ignoreCache
|| !GetObjectPrincipal(targetObj)->GetIsSystemPrincipal()
|| scheme.EqualsLiteral("blob");
bool useCompilationScope = false;
auto* principal = BasePrincipal::Cast(GetObjectPrincipal(targetObj));
bool isSystem = principal->Is<SystemPrincipal>();
if (!isSystem && principal->Is<ContentPrincipal>()) {
auto* content = principal->As<ContentPrincipal>();
nsAutoCString scheme;
content->mCodebase->GetScheme(scheme);
// We want to enable caching for scripts with Activity Stream's
// codebase URLs.
if (scheme.EqualsLiteral("about")) {
nsAutoCString filePath;
content->mCodebase->GetFilePath(filePath);
useCompilationScope = filePath.EqualsLiteral("home") ||
filePath.EqualsLiteral("newtab") ||
filePath.EqualsLiteral("welcome");
isSystem = true;
}
}
bool ignoreCache = options.ignoreCache || !isSystem || scheme.EqualsLiteral("blob");
StartupCache* cache = ignoreCache ? nullptr : StartupCache::GetSingleton();
nsAutoCString cachePath;
@ -722,7 +761,7 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
cache = nullptr;
} else if (!ReadScript(uri, cx, targetObj, options.charset,
static_cast<const char*>(uriStr.get()), serv,
options.wantReturnValue, &script)) {
options.wantReturnValue, useCompilationScope, &script)) {
return NS_OK;
}

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

@ -36,7 +36,7 @@ private:
bool ReadScript(nsIURI* uri, JSContext* cx, JS::HandleObject targetObj,
const nsAString& charset, const char* uriStr,
nsIIOService* serv,
bool wantReturnValue,
bool wantReturnValue, bool useCompilationScope,
JS::MutableHandleScript script);
nsresult ReadScriptAsync(nsIURI* uri,

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

@ -4,7 +4,7 @@ import os
import struct
import sys
MAGIC = b'mozXDRcachev001\0'
MAGIC = b'mozXDRcachev002\0'
def usage():
@ -17,17 +17,20 @@ def usage():
class ProcessTypes:
Default = 0
Web = 1
Extension = 2
Privileged = 3
Uninitialized = 0
Parent = 1
Web = 2
Extension = 3
Privileged = 4
def __init__(self, val):
self.val = val
def __str__(self):
res = []
if self.val & (1 << self.Default):
if self.val & (1 << self.Uninitialized):
raise Exception('Uninitialized process type')
if self.val & (1 << self.Parent):
res.append('Parent')
if self.val & (1 << self.Web):
res.append('Web')

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

@ -4106,11 +4106,11 @@ nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame* aFrame)
}
struct BoxToRect : public nsLayoutUtils::BoxCallback {
nsIFrame* mRelativeTo;
const nsIFrame* mRelativeTo;
nsLayoutUtils::RectCallback* mCallback;
uint32_t mFlags;
BoxToRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback,
BoxToRect(const nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback,
uint32_t aFlags)
: mRelativeTo(aRelativeTo), mCallback(aCallback), mFlags(aFlags) {}
@ -4145,7 +4145,7 @@ struct BoxToRect : public nsLayoutUtils::BoxCallback {
struct MOZ_RAII BoxToRectAndText : public BoxToRect {
Sequence<nsString>* mTextList;
BoxToRectAndText(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback,
BoxToRectAndText(const nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback,
Sequence<nsString>* aTextList, uint32_t aFlags)
: BoxToRect(aRelativeTo, aCallback, aFlags), mTextList(aTextList) {}
@ -4185,7 +4185,7 @@ struct MOZ_RAII BoxToRectAndText : public BoxToRect {
};
void
nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, const nsIFrame* aRelativeTo,
RectCallback* aCallback, uint32_t aFlags)
{
BoxToRect converter(aRelativeTo, aCallback, aFlags);
@ -4193,7 +4193,8 @@ nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
}
void
nsLayoutUtils::GetAllInFlowRectsAndTexts(nsIFrame* aFrame, nsIFrame* aRelativeTo,
nsLayoutUtils::GetAllInFlowRectsAndTexts(nsIFrame* aFrame,
const nsIFrame* aRelativeTo,
RectCallback* aCallback,
Sequence<nsString>* aTextList,
uint32_t aFlags)
@ -4230,7 +4231,7 @@ nsIFrame* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame* aFrame)
}
nsRect
nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo,
nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, const nsIFrame* aRelativeTo,
uint32_t aFlags) {
RectAccumulator accumulator;
GetAllInFlowRects(aFrame, aRelativeTo, &accumulator, aFlags);
@ -9121,8 +9122,12 @@ nsLayoutUtils::ComputeScrollMetadata(nsIFrame* aForFrame,
nsLayoutUtils::CalculateScrollableRectForFrame(scrollableFrame, aForFrame)));
if (scrollableFrame) {
nsPoint scrollPosition = scrollableFrame->GetScrollPosition();
metrics.SetScrollOffset(CSSPoint::FromAppUnits(scrollPosition));
CSSPoint scrollPosition = CSSPoint::FromAppUnits(scrollableFrame->GetScrollPosition());
metrics.SetScrollOffset(scrollPosition);
CSSRect viewport = metrics.GetViewport();
viewport.MoveTo(scrollPosition);
metrics.SetViewport(viewport);
nsPoint smoothScrollPosition = scrollableFrame->LastScrollDestination();
metrics.SetSmoothScrollOffset(CSSPoint::FromAppUnits(smoothScrollPosition));

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

@ -1221,10 +1221,11 @@ public:
* or RECTS_USE_MARGIN_BOX, the corresponding type of box is used.
* Otherwise (by default), the border box is used.
*/
static void GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
static void GetAllInFlowRects(nsIFrame* aFrame, const nsIFrame* aRelativeTo,
RectCallback* aCallback, uint32_t aFlags = 0);
static void GetAllInFlowRectsAndTexts(nsIFrame* aFrame, nsIFrame* aRelativeTo,
static void GetAllInFlowRectsAndTexts(nsIFrame* aFrame,
const nsIFrame* aRelativeTo,
RectCallback* aCallback,
mozilla::dom::Sequence<nsString>* aTextList,
uint32_t aFlags = 0);
@ -1239,7 +1240,7 @@ public:
* or RECTS_USE_MARGIN_BOX, the corresponding type of box is used.
* Otherwise (by default), the border box is used.
*/
static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo,
static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, const nsIFrame* aRelativeTo,
uint32_t aFlags = 0);
enum {

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

@ -144,7 +144,6 @@ namespace mozilla {
class RefreshDriverTimer {
public:
RefreshDriverTimer()
: mLastFireEpoch(0)
{
}
@ -202,7 +201,6 @@ public:
}
TimeStamp MostRecentRefresh() const { return mLastFireTime; }
int64_t MostRecentRefreshEpochTime() const { return mLastFireEpoch; }
void SwapRefreshDrivers(RefreshDriverTimer* aNewTimer)
{
@ -220,7 +218,6 @@ public:
}
mRootRefreshDrivers.Clear();
aNewTimer->mLastFireEpoch = mLastFireEpoch;
aNewTimer->mLastFireTime = mLastFireTime;
StopTimer();
@ -278,16 +275,15 @@ protected:
/*
* Actually runs a tick, poking all the attached RefreshDrivers.
* Grabs the "now" time via JS_Now and TimeStamp::Now().
* Grabs the "now" time via TimeStamp::Now().
*/
void Tick()
{
int64_t jsnow = JS_Now();
TimeStamp now = TimeStamp::Now();
Tick(jsnow, now);
Tick(now);
}
void TickRefreshDrivers(int64_t aJsNow, TimeStamp aNow, nsTArray<RefPtr<nsRefreshDriver>>& aDrivers)
void TickRefreshDrivers(TimeStamp aNow, nsTArray<RefPtr<nsRefreshDriver>>& aDrivers)
{
if (aDrivers.IsEmpty()) {
return;
@ -300,37 +296,34 @@ protected:
continue;
}
TickDriver(driver, aJsNow, aNow);
TickDriver(driver, aNow);
}
}
/*
* Tick the refresh drivers based on the given timestamp.
*/
void Tick(int64_t jsnow, TimeStamp now)
void Tick(TimeStamp now)
{
ScheduleNextTick(now);
mLastFireEpoch = jsnow;
mLastFireTime = now;
LOG("[%p] ticking drivers...", this);
// RD is short for RefreshDriver
AUTO_PROFILER_TRACING("Paint", "RefreshDriverTick");
TickRefreshDrivers(jsnow, now, mContentRefreshDrivers);
TickRefreshDrivers(jsnow, now, mRootRefreshDrivers);
TickRefreshDrivers(now, mContentRefreshDrivers);
TickRefreshDrivers(now, mRootRefreshDrivers);
LOG("[%p] done.", this);
}
static void TickDriver(nsRefreshDriver* driver, int64_t jsnow, TimeStamp now)
static void TickDriver(nsRefreshDriver* driver, TimeStamp now)
{
LOG(">> TickDriver: %p (jsnow: %" PRId64 ")", driver, jsnow);
driver->Tick(jsnow, now);
driver->Tick(now);
}
int64_t mLastFireEpoch;
TimeStamp mLastFireTime;
TimeStamp mTargetTime;
@ -394,7 +387,6 @@ protected:
void StartTimer() override
{
// pretend we just fired, and we schedule the next tick normally
mLastFireEpoch = JS_Now();
mLastFireTime = TimeStamp::Now();
mTargetTime = mLastFireTime + mRateDuration;
@ -725,7 +717,6 @@ private:
// Protect updates to `sActiveVsyncTimers`.
MOZ_ASSERT(NS_IsMainThread());
mLastFireEpoch = JS_Now();
mLastFireTime = TimeStamp::Now();
if (XRE_IsParentProcess()) {
@ -761,10 +752,7 @@ private:
void RunRefreshDrivers(TimeStamp aTimeStamp)
{
int64_t jsnow = JS_Now();
TimeDuration diff = TimeStamp::Now() - aTimeStamp;
int64_t vsyncJsNow = jsnow - diff.ToMicroseconds();
Tick(vsyncJsNow, aTimeStamp);
Tick(aTimeStamp);
}
RefPtr<RefreshDriverVsyncObserver> mVsyncObserver;
@ -885,7 +873,6 @@ protected:
void StartTimer() override
{
mLastFireEpoch = JS_Now();
mLastFireTime = TimeStamp::Now();
mTargetTime = mLastFireTime + mRateDuration;
@ -936,12 +923,10 @@ protected:
/* Runs just one driver's tick. */
void TickOne()
{
int64_t jsnow = JS_Now();
TimeStamp now = TimeStamp::Now();
ScheduleNextTick(now);
mLastFireEpoch = jsnow;
mLastFireTime = now;
nsTArray<RefPtr<nsRefreshDriver>> drivers(mContentRefreshDrivers);
@ -951,7 +936,7 @@ protected:
if (index < drivers.Length() &&
!drivers[index]->IsTestControllingRefreshesEnabled())
{
TickDriver(drivers[index], jsnow, now);
TickDriver(drivers[index], now);
}
mNextDriverIndex++;
@ -1147,7 +1132,6 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
MOZ_ASSERT(mPresContext,
"Need a pres context to tell us to call Disconnect() later "
"and decrement sRefreshDriverCount.");
mMostRecentRefreshEpochTime = JS_Now();
mMostRecentRefresh = TimeStamp::Now();
mMostRecentTick = mMostRecentRefresh;
mNextThrottledFrameRequestTick = mMostRecentTick;
@ -1182,7 +1166,6 @@ nsRefreshDriver::AdvanceTimeAndRefresh(int64_t aMilliseconds)
StopTimer();
if (!mTestControllingRefreshes) {
mMostRecentRefreshEpochTime = JS_Now();
mMostRecentRefresh = TimeStamp::Now();
mTestControllingRefreshes = true;
@ -1194,7 +1177,6 @@ nsRefreshDriver::AdvanceTimeAndRefresh(int64_t aMilliseconds)
}
}
mMostRecentRefreshEpochTime += aMilliseconds * 1000;
mMostRecentRefresh += TimeDuration::FromMilliseconds((double) aMilliseconds);
mozilla::dom::AutoNoJSAPI nojsapi;
@ -1221,14 +1203,6 @@ nsRefreshDriver::MostRecentRefresh() const
return mMostRecentRefresh;
}
int64_t
nsRefreshDriver::MostRecentRefreshEpochTime() const
{
const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
return mMostRecentRefreshEpochTime;
}
bool
nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
FlushType aFlushType)
@ -1408,11 +1382,6 @@ nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags)
aFlags & eAllowTimeToGoBackwards
? mActiveTimer->MostRecentRefresh()
: std::max(mActiveTimer->MostRecentRefresh(), mMostRecentRefresh);
mMostRecentRefreshEpochTime =
aFlags & eAllowTimeToGoBackwards
? mActiveTimer->MostRecentRefreshEpochTime()
: std::max(mActiveTimer->MostRecentRefreshEpochTime(),
mMostRecentRefreshEpochTime);
if (mMostRecentRefresh != newMostRecentRefresh) {
mMostRecentRefresh = newMostRecentRefresh;
@ -1526,9 +1495,9 @@ nsRefreshDriver::DoTick()
"Shouldn't have a JSContext on the stack");
if (mTestControllingRefreshes) {
Tick(mMostRecentRefreshEpochTime, mMostRecentRefresh);
Tick(mMostRecentRefresh);
} else {
Tick(JS_Now(), TimeStamp::Now());
Tick(TimeStamp::Now());
}
}
@ -1789,7 +1758,7 @@ nsRefreshDriver::CancelIdleRunnable(nsIRunnable* aRunnable)
}
void
nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
nsRefreshDriver::Tick(TimeStamp aNowTime)
{
MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
"Shouldn't have a JSContext on the stack");
@ -1822,7 +1791,6 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
TimeStamp previousRefresh = mMostRecentRefresh;
mMostRecentRefresh = aNowTime;
mMostRecentRefreshEpochTime = aNowEpoch;
if (IsWaitingForPaint(aNowTime)) {
// We're currently suspended waiting for earlier Tick's to

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

@ -116,10 +116,6 @@ public:
* the main event loop have the same start time.)
*/
mozilla::TimeStamp MostRecentRefresh() const;
/**
* Same thing, but in microseconds since the epoch.
*/
int64_t MostRecentRefreshEpochTime() const;
/**
* Add / remove refresh observers. Returns whether the operation
@ -430,7 +426,7 @@ private:
void DispatchAnimationEvents();
void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
void UpdateIntersectionObservations();
void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime);
void Tick(mozilla::TimeStamp aNowTime);
enum EnsureTimerStartedFlags {
eNone = 0,
@ -512,7 +508,6 @@ private:
// True if the next tick should notify DOMContentFlushed.
bool mNotifyDOMContentFlushed;
int64_t mMostRecentRefreshEpochTime;
// Number of seconds that the refresh driver is blocked waiting for a compositor
// transaction to be completed before we append a note to the gfx critical log.
// The number is doubled every time the threshold is hit.

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

@ -10,11 +10,11 @@ interface nsIChannel;
interface nsILoadInfo;
[scriptable, uuid(c0c19db9-1b5a-4ac5-b656-ed6f8149fa48)]
interface nsIAboutModule : nsISupports
interface nsIAboutModule : nsISupports
{
/**
* Constructs a new channel for the about protocol module.
* Constructs a new channel for the about protocol module.
*
* @param aURI the uri of the new channel
* @param aLoadInfo the loadinfo of the new channel
@ -71,6 +71,13 @@ interface nsIAboutModule : nsISupports
*/
const unsigned long MAKE_LINKABLE = (1 << 7);
/**
* A flag that indicates that this URI can be loaded in the privileged
* content process if privileged content process is enabled. Ignored unless
* URI_MUST_LOAD_IN_CHILD is also specified.
*/
const unsigned long URI_CAN_LOAD_IN_PRIVILEGED_CHILD = (1 << 8);
/**
* A method to get the flags that apply to a given about: URI. The URI
* passed in is guaranteed to be one of the URIs that this module
@ -81,8 +88,8 @@ interface nsIAboutModule : nsISupports
%{C++
#define NS_ABOUT_MODULE_CONTRACTID "@mozilla.org/network/protocol/about;1"
#define NS_ABOUT_MODULE_CONTRACTID_PREFIX NS_ABOUT_MODULE_CONTRACTID "?what="
#define NS_ABOUT_MODULE_CONTRACTID "@mozilla.org/network/protocol/about;1"
#define NS_ABOUT_MODULE_CONTRACTID_PREFIX NS_ABOUT_MODULE_CONTRACTID "?what="
#define NS_ABOUT_MODULE_CONTRACTID_LENGTH 49 // strlen(NS_ABOUT_MODULE_CONTRACTID_PREFIX)
%}

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

@ -0,0 +1,187 @@
/* 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/. */
// This file is an NSIS plugin which exports a function that starts a process
// from a provided path by using the shell automation API to have explorer.exe
// invoke ShellExecute. This roundabout method of starting a process is useful
// because it means the new process will use the integrity level and security
// token of the shell, so it allows starting an unelevated process from inside
// an elevated one. The method is based on
// https://blogs.msdn.microsoft.com/oldnewthing/20131118-00/?p=2643
// but the code has been rewritten to remove the need for ATL or the C runtime.
// Normally an NSIS installer would use the UAC plugin, which itself uses both
// an unelevated and an elevated process, and the elevated process can invoke
// functions in the unelevated one, so this plugin wouldn't be needed.
// But uninstallers are often directly run elevated because that's just how
// the Windows UI launches them, so there is no unelevated process. This
// plugin allows starting a needed unelevated process in that situation.
#include <windows.h>
#include <shlobj.h>
#pragma comment(lib, "shlwapi.lib")
static IShellView*
GetDesktopWindowShellView()
{
IShellView* view = nullptr;
IShellWindows* shell = nullptr;
CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER,
IID_PPV_ARGS(&shell));
if (shell) {
VARIANT empty;
VariantInit(&empty);
VARIANT loc;
loc.vt = VT_VARIANT | VT_BYREF;
PIDLIST_ABSOLUTE locList;
SHGetFolderLocation(nullptr, CSIDL_DESKTOP, nullptr, 0, &locList);
loc.byref = locList;
HWND windowHandle = 0;
IDispatch* dispatch = nullptr;
shell->FindWindowSW(&loc, &empty, SWC_DESKTOP, (long*)&windowHandle,
SWFO_NEEDDISPATCH, &dispatch);
if (dispatch) {
IServiceProvider* provider = nullptr;
dispatch->QueryInterface(IID_PPV_ARGS(&provider));
if (provider) {
IShellBrowser* browser = nullptr;
provider->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&browser));
if (browser) {
browser->QueryActiveShellView(&view);
browser->Release();
}
provider->Release();
}
dispatch->Release();
}
shell->Release();
}
return view;
}
static IShellDispatch2*
GetApplicationFromShellView(IShellView* view)
{
IShellDispatch2* shellDispatch = nullptr;
IDispatch* viewDisp = nullptr;
HRESULT hr = view->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&viewDisp));
if (SUCCEEDED(hr)) {
IShellFolderViewDual* shellViewFolder = nullptr;
viewDisp->QueryInterface(IID_PPV_ARGS(&shellViewFolder));
if (shellViewFolder) {
IDispatch* dispatch = nullptr;
shellViewFolder->get_Application(&dispatch);
if (dispatch) {
dispatch->QueryInterface(IID_PPV_ARGS(&shellDispatch));
dispatch->Release();
}
shellViewFolder->Release();
}
viewDisp->Release();
}
return shellDispatch;
}
static bool
ShellExecInExplorerProcess(wchar_t* path)
{
bool rv = false;
if (SUCCEEDED(CoInitialize(nullptr))) {
IShellView *desktopView = GetDesktopWindowShellView();
if (desktopView) {
IShellDispatch2 *shellDispatch = GetApplicationFromShellView(desktopView);
if (shellDispatch) {
BSTR bstrPath = SysAllocString(path);
rv = SUCCEEDED(shellDispatch->ShellExecuteW(bstrPath,
VARIANT{}, VARIANT{},
VARIANT{}, VARIANT{}));
SysFreeString(bstrPath);
shellDispatch->Release();
}
desktopView->Release();
}
CoUninitialize();
}
return rv;
}
struct stack_t {
stack_t* next;
TCHAR text[MAX_PATH];
};
/**
* Removes an element from the top of the NSIS stack
*
* @param stacktop A pointer to the top of the stack
* @param str The string to pop to
* @param len The max length
* @return 0 on success
*/
int
popstring(stack_t **stacktop, TCHAR *str, int len)
{
// Removes the element from the top of the stack and puts it in the buffer
stack_t *th;
if (!stacktop || !*stacktop) {
return 1;
}
th = (*stacktop);
lstrcpyn(str, th->text, len);
*stacktop = th->next;
HeapFree(GetProcessHeap(), 0, th);
return 0;
}
/**
* Adds an element to the top of the NSIS stack
*
* @param stacktop A pointer to the top of the stack
* @param str The string to push on the stack
* @param len The length of the string to push on the stack
* @return 0 on success
*/
void
pushstring(stack_t **stacktop, const TCHAR *str, int len)
{
stack_t *th;
if (!stacktop) {
return;
}
th = (stack_t*)HeapAlloc(GetProcessHeap(), 0, sizeof(stack_t) + len);
lstrcpyn(th->text, str, len);
th->next = *stacktop;
*stacktop = th;
}
/**
* Starts an executable or URL from the shell process.
*
* @param stacktop Pointer to the top of the stack, AKA the first parameter to
the plugin call. Should contain the file or URL to execute.
* @return 1 if the file/URL was executed successfully, 0 if it was not
*/
extern "C" void __declspec(dllexport)
Exec(HWND, int, TCHAR *, stack_t **stacktop, void *)
{
wchar_t path[MAX_PATH + 1];
// We're skipping building the C runtime to keep the file size low, so we
// can't use a normal string initialization because that would call memset.
path[0] = L'\0';
popstring(stacktop, path, MAX_PATH);
bool rv = ShellExecInExplorerProcess(path);
pushstring(stacktop, rv ? L"1" : L"0", 2);
}
BOOL APIENTRY
DllMain(HMODULE, DWORD, LPVOID)
{
return TRUE;
}

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

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27428.2015
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExecInExplorer", "ExecInExplorer.vcxproj", "{B5DBA89B-37EE-425C-A375-4E04E69731FA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B5DBA89B-37EE-425C-A375-4E04E69731FA}.Debug|x64.ActiveCfg = Debug|x64
{B5DBA89B-37EE-425C-A375-4E04E69731FA}.Debug|x64.Build.0 = Debug|x64
{B5DBA89B-37EE-425C-A375-4E04E69731FA}.Debug|x86.ActiveCfg = Debug|Win32
{B5DBA89B-37EE-425C-A375-4E04E69731FA}.Debug|x86.Build.0 = Debug|Win32
{B5DBA89B-37EE-425C-A375-4E04E69731FA}.Release|x64.ActiveCfg = Release|x64
{B5DBA89B-37EE-425C-A375-4E04E69731FA}.Release|x64.Build.0 = Release|x64
{B5DBA89B-37EE-425C-A375-4E04E69731FA}.Release|x86.ActiveCfg = Release|Win32
{B5DBA89B-37EE-425C-A375-4E04E69731FA}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {69740CF4-5E42-4A56-AFF5-2D17D42CFB75}
EndGlobalSection
EndGlobal

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

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{B5DBA89B-37EE-425C-A375-4E04E69731FA}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>ExecInExplorer</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;EXECINEXPLORER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<OmitDefaultLibName>true</OmitDefaultLibName>
<BufferSecurityCheck>false</BufferSecurityCheck>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EntryPointSymbol>DllMain</EntryPointSymbol>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>_DEBUG;EXECINEXPLORER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<OmitDefaultLibName>true</OmitDefaultLibName>
<BufferSecurityCheck>false</BufferSecurityCheck>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EntryPointSymbol>DllMain</EntryPointSymbol>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;EXECINEXPLORER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<OmitDefaultLibName>true</OmitDefaultLibName>
<BufferSecurityCheck>false</BufferSecurityCheck>
<WholeProgramOptimization>false</WholeProgramOptimization>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
<EntryPointSymbol>DllMain</EntryPointSymbol>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>false</SDLCheck>
<PreprocessorDefinitions>NDEBUG;EXECINEXPLORER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<OmitDefaultLibName>true</OmitDefaultLibName>
<BufferSecurityCheck>false</BufferSecurityCheck>
<WholeProgramOptimization>false</WholeProgramOptimization>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
<EntryPointSymbol>DllMain</EntryPointSymbol>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="ExecInExplorer.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

Двоичные данные
other-licenses/nsis/Plugins/ExecInExplorer.dll Normal file

Двоичный файл не отображается.

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

@ -128,6 +128,7 @@ skip-if = os == 'android'
skip-if = os == 'android' && debug || (os == 'linux' && !asan) # bug 1397615, bug 1455405
[test_ext_webrequest_errors.html]
[test_ext_webrequest_filter.html]
skip-if = os == 'android' && debug # bug 1452348
[test_ext_webrequest_frameId.html]
[test_ext_webrequest_hsts.html]
skip-if = os == 'android' || os == 'linux' # linux, bug 1398120

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

@ -38,7 +38,11 @@ function getExtension() {
browser.test.assertTrue(securityInfo.certificates.length == 1, "no certificate chain");
}
let cert = securityInfo.certificates[0];
browser.test.assertTrue(cert.validity.start < Date.now() < cert.validity.end, "cert validity is correct");
let now = Date.now();
browser.test.assertTrue(Number.isInteger(cert.validity.start), "cert start is integer");
browser.test.assertTrue(Number.isInteger(cert.validity.end), "cert end is integer");
browser.test.assertTrue(cert.validity.start < now, "cert start validity is correct");
browser.test.assertTrue(now < cert.validity.end, "cert end validity is correct");
if (options.rawDER) {
for (let cert of securityInfo.certificates) {
browser.test.assertTrue(cert.rawDER.length > 0, "have rawDER");

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

@ -43,8 +43,6 @@ const PRIVILEGED_REMOTE_TYPE = "privileged";
const LARGE_ALLOCATION_REMOTE_TYPE = "webLargeAllocation";
const DEFAULT_REMOTE_TYPE = WEB_REMOTE_TYPE;
const ACTIVITY_STREAM_PAGES = new Set(["home", "newtab", "welcome"]);
function validatedWebRemoteType(aPreferredRemoteType, aTargetUri, aCurrentUri) {
// If the domain is whitelisted to allow it to use file:// URIs, then we have
// to run it in a file content process, in case it uses file:// sub-resources.
@ -159,9 +157,8 @@ var E10SUtils = {
let flags = module.getURIFlags(aURI);
if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD) {
// Load Activity Stream in a separate process.
if (useSeparatePrivilegedContentProcess &&
ACTIVITY_STREAM_PAGES.has(aURI.filePath)) {
if ((flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_PRIVILEGED_CHILD) &&
useSeparatePrivilegedContentProcess) {
return PRIVILEGED_REMOTE_TYPE;
}
return DEFAULT_REMOTE_TYPE;

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

@ -214,8 +214,8 @@ const SecurityInfo = {
subject: cert.subjectName,
issuer: cert.issuerName,
validity: {
start: cert.validity.notBefore,
end: cert.validity.notAfter,
start: cert.validity.notBefore ? Math.trunc(cert.validity.notBefore / 1000) : 0,
end: cert.validity.notAfter ? Math.trunc(cert.validity.notAfter / 1000) : 0,
},
fingerprint: {
sha1: cert.sha1Fingerprint,

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

@ -28,6 +28,7 @@ CUSTOM_NSIS_PLUGINS = \
ApplicationID.dll \
CertCheck.dll \
CityHash.dll \
ExecInExplorer.dll \
InetBgDL.dll \
InvokeShellVerb.dll \
liteFirewallW.dll \