import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1257611 - Fix wrong CondVar::Wait() and Monitor::Wait() usage in netwerk/cache2, r=honzab (54f1a79f43)
- Bug 1239687 - Leak invalid/doomed file handles immediately after HTTP cache shutdown. r=michal (a20bcf662b)
- Bug 996836 - Merge CLOSE and WRITE priority in cache2 I/O thread. r=michal (8b0af50593)
- Bug 1265243 - Do not initialize CacheObserver in the child process, r=honzab (87f69529d2)
- Bug 1247432 - Don't do any unnecessary I/O in cache2 after shutdown. r=michal (7c5a2b89ac)
- Bug 1220272 - Fix signalling in HTTP cache test code suspender. r=michal (293a16731d)
- Bug 1160368 - Part 2: Clean up some cruft in nsCookie.h from PRBool/bool conversions. r=jdm (aa873f2dcc)
- Bug 1160368 - Part 5: Clean up some confusing browser_storage_listings.js checks. r=jdm (b27f5930a9)
- Bug 866380 - Null check for amlogic libc implementation. r=valentin (c3a487af9c)
- Bug 1257320 - Disable C4577 to unblock compilation on VS2015; r=mayhemer (2f800ca85c)
- Bug 1229726 - fix the data copy from sockaddr to NetAddr on OSX/FreeBSD. r=mcmanus. (fde11e004e)
- Bug 1241368 - provide JS implemented MDNS service as a fallback. r=nwgh,nalexander. (afe1445eb8)
- Bug 571126: Remove NECKO_SEPARATE_STACKS support, r=jduell (fb5b87bb3a)
- Bug 1057689 - Add xpcshell test checking correct notifications and app-offline state r=jduell (89946b44ba)
- Bug 1259459 - h2 0 length options puts end-stream on headers r=hurley (618480a609)
- Bug 1234251 - Remove HttpChannelChild::mSynthesizedResponseHead; r=jdm (20981affea)
- Bug 904559 - Veto redirect when target channel doesn't implement nsIParentRedirectingChannel. r=jduell (b3da2fae91)
- Bug 1261784 Make sure InterceptedChannel body streams are always closed. r=jdm (cd50b1a52b)
- Bug 1224508 - Use URI path without reference as the callback key. r=valentin. (b8a953f23a)
- Bug 1226760 - ViewSource doesn't work for packaged app resources r=mcmanus (ec9b0297de)
- Bug 1254061 - Rewrite nsHttp::ParseInt64 using strtoll r=mcmanus (2125b8ae6f)
- Bug 1241565 - nsIHttpChannelInternal should be a builtinclass, r=honzab.moz (d24da6a95a)
- Bug 1252386 - Removed debugging printf,r=me (c5d89f353c)
- bug 277813 - autogenerated expires needs max r=mayhemer (6aa7c255a5)
- Bug 1225384 - Change how the default resource "host names" are handled. r=michal (868732baab)
- Bug 719905: Fix resolution of resource: URLs containing : and / characters. r=valentin (f60f4baacf)
- Bug 1195173 - Convert ExtensionProtocolHandler to use channel->Open2() (r=maglione) (063f5d5d10)
- Bug 1226909 followup to fix bustage. Make sure that the channel returned by NS_NewChannel doesn't have a loadinfo that indicates that the channel has already been opened (42ebe0f44e)
- Bug 241698 - Fixed init and use of nsDirIndex::mLastModified (-1LL) + built in nullptr checks where they were missing. r=dragana (e9c2277a3d)
- Bug 1261318 - make sure brotli context is created in onstoprequest r=bagder (6646fed64d)
- Bug 1212223 - Update |bufLen| as well when we probed the multipart preamble. r=valentin. (ec878c5b0f)
- Bug 1259561: Increase CRAZY_COORD (threshold for debug build layout warnings) by an order of magnitude. r=mats (c05c16dd85)
- Bug 1261698. Make ReparentFrameViewTo return void because it always returns NS_OK. r=mats (e806d6abcb)
- Bug 1261698. Remove comment that is not relevant in nsContainerFrame.cpp. (f570189d15)
- Bug 1261698. Don't descend into child frames looking for views in ReparentFrameViewTo if the frame doesn't have the NS_FRAME_HAS_CHILD_WITH_VIEW bit set. r=mats (0d42befd46)
- Bug 1265154 - Fix compile error in MSVC 2013 caused by ArrayLength; r=heycam (1c962f2840)
- Bug 1260351 - Image: Enable ConvolveVertically/Horizontally in LS3 MMI. r=tnikkel (576b6bbdb3)
- Bug 1209780 (Part 1) - Mark DrawResult MOZ_MUST_USE. r=tn (e6c113bef2)
- Bug 1253753 - Remove unnecessary switch fallthrough to avoid -Wimplicit-fallthrough warning. r=karlt (96bd93fc5c)
- bug 1260178 null check pattern from -unico-border-gradient r=acomminos (f63c9c7ffb)
- Bug 1247796. Use keyboardFocusIndicatorColor for ActiveBorder system color keyword. r=mstange (7ed133de97)
- Bug 1248675 - Update the cached mBounds in nsChildView when its backing scale factor (display DPI) changes. r=mstange (8197274118)
- Bug 1256576 - Make sure texture is (re)initialized if the size changed. r=snorp (2c56790ca9)
- Bug 1242449 - Fix confusion among CSS, desktop and device pixel units in nsXULWindow position/size and window staggering so as to work consistently across mixed resolution displays. r=emk (f73d2fd41d)
- Bug 1255645 - Ensure nsXULWindow constrains the window to the bounds of its screen after applying intrinsic sizing (if appropriate), by re-doing positioning after the window has been sized properly. r=emk (e87e0cea81)
- Bug 1259492 - Ensure window position is constrained to the screen after it has been sized properly in nsXULWindow::OnChromeLoaded. r=emk (7cf599b39b)
- Bug 1251620 - Update pdf.js to version 1.4.95. r=bdahl, r=Mossop (f8bbd73419)
- Bug 1228655 - Move TabsInTitlebar to its own file to remove ifdefs. r=Gijs (ca6cd981bf)
- Bug 1832708 - Disable std::__throw_* wrapping on libc++ >= 14.0. r=firefox-build-system-reviewers,andi (29e0cc9319)
This commit is contained in:
2024-08-09 14:30:33 +08:00
parent 96a33978d6
commit 12dc484cbd
69 changed files with 2153 additions and 646 deletions
@@ -0,0 +1,17 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This file is used as a stub object for platforms which
// don't have CAN_DRAW_IN_TITLEBAR defined.
var TabsInTitlebar = {
init: function () {},
uninit: function () {},
allowedBy: function (condition, allow) {},
updateAppearance: function updateAppearance(aForce) {},
get enabled() {
return document.documentElement.getAttribute("tabsintitlebar") == "true";
},
};
@@ -0,0 +1,285 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Note: the file browser-tabsintitlebar-stub.js is used instead of
// this one on platforms which don't have CAN_DRAW_IN_TITLEBAR defined.
var TabsInTitlebar = {
init: function () {
this._readPref();
Services.prefs.addObserver(this._prefName, this, false);
// We need to update the appearance of the titlebar when the menu changes
// from the active to the inactive state. We can't, however, rely on
// DOMMenuBarInactive, because the menu fires this event and then removes
// the inactive attribute after an event-loop spin.
//
// Because updating the appearance involves sampling the heights and margins
// of various elements, it's important that the layout be more or less
// settled before updating the titlebar. So instead of listening to
// DOMMenuBarActive and DOMMenuBarInactive, we use a MutationObserver to
// watch the "invalid" attribute directly.
let menu = document.getElementById("toolbar-menubar");
this._menuObserver = new MutationObserver(this._onMenuMutate);
this._menuObserver.observe(menu, {attributes: true});
this.onAreaReset = function(aArea) {
if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
this._update(true);
};
this.onWidgetAdded = this.onWidgetRemoved = function(aWidgetId, aArea) {
if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
this._update(true);
};
addEventListener("resolutionchange", this, false);
this._initialized = true;
},
allowedBy: function (condition, allow) {
if (allow) {
if (condition in this._disallowed) {
delete this._disallowed[condition];
this._update(true);
}
} else {
if (!(condition in this._disallowed)) {
this._disallowed[condition] = null;
this._update(true);
}
}
},
updateAppearance: function updateAppearance(aForce) {
this._update(aForce);
},
get enabled() {
return document.documentElement.getAttribute("tabsintitlebar") == "true";
},
observe: function (subject, topic, data) {
if (topic == "nsPref:changed")
this._readPref();
},
handleEvent: function (aEvent) {
if (aEvent.type == "resolutionchange" && aEvent.target == window) {
this._update(true);
}
},
_onMenuMutate: function (aMutations) {
// We don't care about restored windows, since the menu shouldn't be
// pushing the tab-strip down.
if (document.documentElement.getAttribute("sizemode") == "normal") {
return;
}
for (let mutation of aMutations) {
if (mutation.attributeName == "inactive" ||
mutation.attributeName == "autohide") {
TabsInTitlebar._update(true);
return;
}
}
},
_initialized: false,
_disallowed: {},
_prefName: "browser.tabs.drawInTitlebar",
_lastSizeMode: null,
_readPref: function () {
this.allowedBy("pref",
Services.prefs.getBoolPref(this._prefName));
},
_update: function (aForce=false) {
let $ = id => document.getElementById(id);
let rect = ele => ele.getBoundingClientRect();
let verticalMargins = cstyle => parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
if (!this._initialized || window.fullScreen)
return;
let allowed = true;
if (!aForce) {
// _update is called on resize events, because the window is not ready
// after sizemode events. However, we only care about the event when the
// sizemode is different from the last time we updated the appearance of
// the tabs in the titlebar.
let sizemode = document.documentElement.getAttribute("sizemode");
if (this._lastSizeMode == sizemode) {
return;
}
let oldSizeMode = this._lastSizeMode;
this._lastSizeMode = sizemode;
// Don't update right now if we are leaving fullscreen, since the UI is
// still changing in the consequent "fullscreen" event. Code there will
// call this function again when everything is ready.
// See browser-fullScreen.js: FullScreen.toggle and bug 1173768.
if (oldSizeMode == "fullscreen") {
return;
}
}
for (let something in this._disallowed) {
allowed = false;
break;
}
let titlebar = $("titlebar");
let titlebarContent = $("titlebar-content");
let menubar = $("toolbar-menubar");
// Reset the margins that _update modifies so that we can take accurate
// measurements.
titlebarContent.style.marginBottom = "";
titlebar.style.marginBottom = "";
menubar.style.marginBottom = "";
if (allowed) {
// We set the tabsintitlebar attribute first so that our CSS for
// tabsintitlebar manifests before we do our measurements.
document.documentElement.setAttribute("tabsintitlebar", "true");
#ifdef MENUBAR_CAN_AUTOHIDE
let appmenuButtonBox = $("appmenu-button-container");
this._sizePlaceholder("appmenu-button", rect(appmenuButtonBox).width);
#endif
let captionButtonsBox = $("titlebar-buttonbox");
this._sizePlaceholder("caption-buttons", rect(captionButtonsBox).width);
let titlebarContentHeight = rect(titlebarContent).height;
let menuHeight = this._outerHeight(menubar);
// If the titlebar is taller than the menubar, add more padding to the
// bottom of the menubar so that it matches.
if (menuHeight && titlebarContentHeight > menuHeight) {
let menuTitlebarDelta = titlebarContentHeight - menuHeight;
menubar.style.marginBottom = menuTitlebarDelta + "px";
menuHeight += menuTitlebarDelta;
}
// Next, we calculate how much we need to stretch the titlebar down to
// go all the way to the bottom of the tab strip.
let tabsToolbar = $("TabsToolbar");
let tabAndMenuHeight = this._outerHeight(tabsToolbar) + menuHeight;
titlebarContent.style.marginBottom = tabAndMenuHeight + "px";
// Finally, we have to determine how much to bring up the elements below
// the titlebar. We start with a baseHeight of tabAndMenuHeight, to offset
// the amount we added to the titlebar content. Then, we have two cases:
//
// 1) The titlebar is larger than the tabAndMenuHeight. This can happen in
// large font mode with the menu autohidden. In this case, we want to
// add tabAndMenuHeight, since this should line up the bottom of the
// tabstrip with the bottom of the titlebar.
//
// 2) The titlebar is equal to or smaller than the tabAndMenuHeight. This
// is the more common case, and occurs with normal font sizes. In this
// case, we want to bring the menu and tabstrip right up to the top of
// the titlebar, so we add the titlebarContentHeight to the baseHeight.
let baseHeight = tabAndMenuHeight;
baseHeight += (titlebarContentHeight > tabAndMenuHeight) ? tabAndMenuHeight
: titlebarContentHeight;
titlebar.style.marginBottom = "-" + baseHeight + "px";
if (!this._draghandles) {
this._draghandles = {};
let tmp = {};
Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
let mouseDownCheck = function () {
return !this._dragBindingAlive && TabsInTitlebar.enabled;
};
this._draghandles.tabsToolbar = new tmp.WindowDraggingElement(tabsToolbar);
this._draghandles.tabsToolbar.mouseDownCheck = mouseDownCheck;
this._draghandles.navToolbox = new tmp.WindowDraggingElement(gNavToolbox);
this._draghandles.navToolbox.mouseDownCheck = mouseDownCheck;
}
} else {
document.documentElement.removeAttribute("tabsintitlebar");
}
ToolbarIconColor.inferFromText();
if (CustomizationHandler.isCustomizing()) {
gCustomizeMode.updateLWTStyling();
}
},
_sizePlaceholder: function (type, width) {
Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='"+ type +"']"),
function (node) { node.width = width; });
},
/**
* Retrieve the height of an element, including its top and bottom
* margins.
*
* @param ele
* The element to measure.
* @return
* The height and margins as an integer. If the height of the element
* is 0, then this returns 0, regardless of what the margins are.
*/
_outerHeight: function (ele) {
let cstyle = document.defaultView.getComputedStyle(ele);
let margins = parseInt(cstyle.marginTop) + parseInt(cstyle.marginBottom);
let height = ele.getBoundingClientRect().height;
return height > 0 ? Math.abs(height + margins) : 0;
},
uninit: function () {
this._initialized = false;
removeEventListener("resolutionchange", this);
Services.prefs.removeObserver(this._prefName, this);
this._menuObserver.disconnect();
}
};
function updateTitlebarDisplay() {
if (AppConstants.platform == "macosx") {
// OS X and the other platforms differ enough to necessitate this kind of
// special-casing. Like the other platforms where we CAN_DRAW_IN_TITLEBAR,
// we draw in the OS X titlebar when putting the tabs up there. However, OS X
// also draws in the titlebar when a lightweight theme is applied, regardless
// of whether or not the tabs are drawn in the titlebar.
if (TabsInTitlebar.enabled) {
document.documentElement.setAttribute("chromemargin-nonlwtheme", "0,-1,-1,-1");
document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
document.documentElement.removeAttribute("drawtitle");
} else {
// We set chromemargin-nonlwtheme to "" instead of removing it as a way of
// making sure that LightweightThemeConsumer doesn't take it upon itself to
// detect this value again if and when we do a lwtheme state change.
document.documentElement.setAttribute("chromemargin-nonlwtheme", "");
let isCustomizing = document.documentElement.hasAttribute("customizing");
let hasLWTheme = document.documentElement.hasAttribute("lwtheme");
let isPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
if ((!hasLWTheme || isCustomizing) && !isPrivate) {
document.documentElement.removeAttribute("chromemargin");
}
document.documentElement.setAttribute("drawtitle", "true");
}
} else { // not OS X
if (TabsInTitlebar.enabled)
document.documentElement.setAttribute("chromemargin", "0,2,2,2");
else
document.documentElement.removeAttribute("chromemargin");
}
}
function onTitlebarMaxClick() {
if (window.windowState == window.STATE_MAXIMIZED)
window.restore();
else
window.maximize();
}
+23 -259
View File
@@ -5308,6 +5308,29 @@ function setToolbarVisibility(toolbar, isVisible) {
ToolbarIconColor.inferFromText();
}
#ifdef MENUBAR_CAN_AUTOHIDE
function updateAppButtonDisplay() {
var displayAppButton =
!gInPrintPreviewMode &&
window.menubar.visible &&
document.getElementById("toolbar-menubar").getAttribute("autohide") == "true";
#ifdef CAN_DRAW_IN_TITLEBAR
document.getElementById("titlebar").hidden = gInPrintPreviewMode;
if (!gInPrintPreviewMode)
document.documentElement.setAttribute("chromemargin", "0,2,2,2");
else
document.documentElement.removeAttribute("chromemargin");
TabsInTitlebar.allowedBy("drawing-in-titlebar", !gInPrintPreviewMode);
#else
document.getElementById("appmenu-toolbar-button").hidden =
!displayAppButton;
#endif
}
#endif
var TabsOnTop = {
init: function TabsOnTop_init() {
Services.prefs.addObserver(this._prefName, this, false);
@@ -5359,256 +5382,6 @@ var TabsOnTop = {
_prefName: "browser.tabs.onTop"
}
var TabsInTitlebar = {
init: function () {
#ifdef CAN_DRAW_IN_TITLEBAR
this._readPref();
Services.prefs.addObserver(this._prefName, this, false);
// We need to update the appearance of the titlebar when the menu changes
// from the active to the inactive state. We can't, however, rely on
// DOMMenuBarInactive, because the menu fires this event and then removes
// the inactive attribute after an event-loop spin.
//
// Because updating the appearance involves sampling the heights and margins
// of various elements, it's important that the layout be more or less
// settled before updating the titlebar. So instead of listening to
// DOMMenuBarActive and DOMMenuBarInactive, we use a MutationObserver to
// watch the "invalid" attribute directly.
let menu = document.getElementById("toolbar-menubar");
this._menuObserver = new MutationObserver(this._onMenuMutate);
this._menuObserver.observe(menu, {attributes: true});
this._initialized = true;
#endif
},
allowedBy: function (condition, allow) {
#ifdef CAN_DRAW_IN_TITLEBAR
if (allow) {
if (condition in this._disallowed) {
delete this._disallowed[condition];
this._update(true);
}
} else {
if (!(condition in this._disallowed)) {
this._disallowed[condition] = null;
this._update(true);
}
}
#endif
},
updateAppearance: function updateAppearance(aForce) {
#ifdef CAN_DRAW_IN_TITLEBAR
this._update(aForce);
#endif
},
get enabled() {
return document.documentElement.getAttribute("tabsintitlebar") == "true";
},
#ifdef CAN_DRAW_IN_TITLEBAR
observe: function (subject, topic, data) {
if (topic == "nsPref:changed")
this._readPref();
},
_onMenuMutate: function (aMutations) {
// We don't care about restored windows, since the menu shouldn't be
// pushing the tab-strip down.
if (document.documentElement.getAttribute("sizemode") == "normal") {
return;
}
for (let mutation of aMutations) {
if (mutation.attributeName == "inactive" ||
mutation.attributeName == "autohide") {
TabsInTitlebar._update(true);
return;
}
}
},
_initialized: false,
_disallowed: {},
_prefName: "browser.tabs.drawInTitlebar",
_lastSizeMode: null,
_readPref: function () {
this.allowedBy("pref",
Services.prefs.getBoolPref(this._prefName));
},
_update: function (aForce=false) {
let $ = id => document.getElementById(id);
let rect = ele => ele.getBoundingClientRect();
let verticalMargins = cstyle => parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
if (!this._initialized || window.fullScreen)
return;
let allowed = true;
if (!aForce) {
// _update is called on resize events, because the window is not ready
// after sizemode events. However, we only care about the event when the
// sizemode is different from the last time we updated the appearance of
// the tabs in the titlebar.
let sizemode = document.documentElement.getAttribute("sizemode");
if (this._lastSizeMode == sizemode) {
return;
}
let oldSizeMode = this._lastSizeMode;
this._lastSizeMode = sizemode;
// Don't update right now if we are leaving fullscreen, since the UI is
// still changing in the consequent "fullscreen" event. Code there will
// call this function again when everything is ready.
// See browser-fullScreen.js: FullScreen.toggle and bug 1173768.
if (oldSizeMode == "fullscreen") {
return;
}
}
for (let something in this._disallowed) {
allowed = false;
break;
}
let titlebar = $("titlebar");
let titlebarContent = $("titlebar-content");
let menubar = $("toolbar-menubar");
// Reset the margins that _update modifies so that we can take accurate
// measurements.
titlebarContent.style.marginBottom = "";
titlebar.style.marginBottom = "";
menubar.style.marginBottom = "";
if (allowed) {
// We set the tabsintitlebar attribute first so that our CSS for
// tabsintitlebar manifests before we do our measurements.
document.documentElement.setAttribute("tabsintitlebar", "true");
#ifdef MENUBAR_CAN_AUTOHIDE
let appmenuButtonBox = $("appmenu-button-container");
this._sizePlaceholder("appmenu-button", rect(appmenuButtonBox).width);
#endif
let captionButtonsBox = $("titlebar-buttonbox");
this._sizePlaceholder("caption-buttons", rect(captionButtonsBox).width);
let titlebarContentHeight = rect(titlebarContent).height;
let menuHeight = this._outerHeight(menubar);
// If the titlebar is taller than the menubar, add more padding to the
// bottom of the menubar so that it matches.
if (menuHeight && titlebarContentHeight > menuHeight) {
let menuTitlebarDelta = titlebarContentHeight - menuHeight;
menubar.style.marginBottom = menuTitlebarDelta + "px";
menuHeight += menuTitlebarDelta;
}
// Next, we calculate how much we need to stretch the titlebar down to
// go all the way to the bottom of the tab strip.
let tabsToolbar = $("TabsToolbar");
let tabAndMenuHeight = this._outerHeight(tabsToolbar) + menuHeight;
titlebarContent.style.marginBottom = tabAndMenuHeight + "px";
// Finally, we have to determine how much to bring up the elements below
// the titlebar. We start with a baseHeight of tabAndMenuHeight, to offset
// the amount we added to the titlebar content. Then, we have two cases:
//
// 1) The titlebar is larger than the tabAndMenuHeight. This can happen in
// large font mode with the menu autohidden. In this case, we want to
// add tabAndMenuHeight, since this should line up the bottom of the
// tabstrip with the bottom of the titlebar.
//
// 2) The titlebar is equal to or smaller than the tabAndMenuHeight. This
// is the more common case, and occurs with normal font sizes. In this
// case, we want to bring the menu and tabstrip right up to the top of
// the titlebar, so we add the titlebarContentHeight to the baseHeight.
let baseHeight = tabAndMenuHeight;
baseHeight += (titlebarContentHeight > tabAndMenuHeight) ? tabAndMenuHeight
: titlebarContentHeight;
titlebar.style.marginBottom = "-" + baseHeight + "px";
if (!this._draghandles) {
this._draghandles = {};
let tmp = {};
Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
let mouseDownCheck = function () {
return !this._dragBindingAlive && TabsInTitlebar.enabled;
};
this._draghandles.tabsToolbar = new tmp.WindowDraggingElement(tabsToolbar);
this._draghandles.tabsToolbar.mouseDownCheck = mouseDownCheck;
this._draghandles.navToolbox = new tmp.WindowDraggingElement(gNavToolbox);
this._draghandles.navToolbox.mouseDownCheck = mouseDownCheck;
}
} else {
document.documentElement.removeAttribute("tabsintitlebar");
}
ToolbarIconColor.inferFromText();
},
_sizePlaceholder: function (type, width) {
Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='"+ type +"']"),
function (node) { node.width = width; });
},
/**
* Retrieve the height of an element, including its top and bottom
* margins.
*
* @param ele
* The element to measure.
* @return
* The height and margins as an integer. If the height of the element
* is 0, then this returns 0, regardless of what the margins are.
*/
_outerHeight: function (ele) {
let cstyle = document.defaultView.getComputedStyle(ele);
let margins = parseInt(cstyle.marginTop) + parseInt(cstyle.marginBottom);
let height = ele.getBoundingClientRect().height;
return height > 0 ? Math.abs(height + margins) : 0;
},
#endif
uninit: function () {
#ifdef CAN_DRAW_IN_TITLEBAR
this._initialized = false;
Services.prefs.removeObserver(this._prefName, this);
this._menuObserver.disconnect();
#endif
}
};
#ifdef MENUBAR_CAN_AUTOHIDE
function updateAppButtonDisplay() {
var displayAppButton =
!gInPrintPreviewMode &&
window.menubar.visible &&
document.getElementById("toolbar-menubar").getAttribute("autohide") == "true";
#ifdef CAN_DRAW_IN_TITLEBAR
document.getElementById("titlebar").hidden = gInPrintPreviewMode;
if (!gInPrintPreviewMode)
document.documentElement.setAttribute("chromemargin", "0,2,2,2");
else
document.documentElement.removeAttribute("chromemargin");
TabsInTitlebar.allowedBy("drawing-in-titlebar", !gInPrintPreviewMode);
#else
document.getElementById("appmenu-toolbar-button").hidden =
!displayAppButton;
#endif
}
#endif
var TabletModeUpdater = {
init() {
if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
@@ -5660,15 +5433,6 @@ var gTabletModePageCounter = {
},
};
#ifdef CAN_DRAW_IN_TITLEBAR
function onTitlebarMaxClick() {
if (window.windowState == window.STATE_MAXIMIZED)
window.restore();
else
window.maximize();
}
#endif
function displaySecurityInfo()
{
BrowserPageInfo(null, "securityTab");
+1
View File
@@ -28,6 +28,7 @@
#endif
<script type="application/javascript" src="chrome://browser/content/browser-sidebar.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-syncui.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-tabsintitlebar.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-tabview.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-thumbnails.js"/>
+5
View File
@@ -76,6 +76,11 @@ browser.jar:
content/browser/browser-sidebar.js (content/browser-sidebar.js)
content/browser/browser-syncui.js (content/browser-syncui.js)
* content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml)
#ifdef CAN_DRAW_IN_TITLEBAR
* content/browser/browser-tabsintitlebar.js (content/browser-tabsintitlebar.js)
#else
content/browser/browser-tabsintitlebar.js (content/browser-tabsintitlebar-stub.js)
#endif
content/browser/browser-tabview.js (content/browser-tabview.js)
content/browser/browser-thumbnails.js (content/browser-thumbnails.js)
content/browser/tab-content.js (content/tab-content.js)
+1 -1
View File
@@ -1,3 +1,3 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js
Current extension version is: 1.4.83
Current extension version is: 1.4.95
@@ -288,7 +288,7 @@ ChromeActions.prototype = {
try {
// contentDisposition/contentDispositionFilename is readonly before FF18
channel.contentDisposition = Ci.nsIChannel.DISPOSITION_ATTACHMENT;
if (self.contentDispositionFilename) {
if (self.contentDispositionFilename && !data.isAttachment) {
channel.contentDispositionFilename = self.contentDispositionFilename;
} else {
channel.contentDispositionFilename = filename;
+148 -21
View File
@@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdf = {}));
// Use strict in our context only - users might not want it
'use strict';
var pdfjsVersion = '1.4.83';
var pdfjsBuild = '0629fd0';
var pdfjsVersion = '1.4.95';
var pdfjsBuild = '2b813c0';
var pdfjsFilePath =
typeof document !== 'undefined' && document.currentScript ?
@@ -407,6 +407,17 @@ var UNSUPPORTED_FEATURES = PDFJS.UNSUPPORTED_FEATURES = {
font: 'font'
};
// Gets the file name from a given URL.
function getFilenameFromUrl(url) {
var anchor = url.indexOf('#');
var query = url.indexOf('?');
var end = Math.min(
anchor > 0 ? anchor : url.length,
query > 0 ? query : url.length);
return url.substring(url.lastIndexOf('/', end) + 1, end);
}
PDFJS.getFilenameFromUrl = getFilenameFromUrl;
// Combines two URLs. The baseUrl shall be absolute URL. If the url is an
// absolute URL, it will be returned as is.
function combineUrl(baseUrl, url) {
@@ -1537,6 +1548,7 @@ exports.combineUrl = combineUrl;
exports.createPromiseCapability = createPromiseCapability;
exports.deprecated = deprecated;
exports.error = error;
exports.getFilenameFromUrl = getFilenameFromUrl;
exports.getLookupTableFactory = getLookupTableFactory;
exports.info = info;
exports.isArray = isArray;
@@ -1577,6 +1589,7 @@ var AnnotationBorderStyleType = sharedUtil.AnnotationBorderStyleType;
var AnnotationType = sharedUtil.AnnotationType;
var Util = sharedUtil.Util;
var addLinkAttributes = sharedUtil.addLinkAttributes;
var getFilenameFromUrl = sharedUtil.getFilenameFromUrl;
var warn = sharedUtil.warn;
var CustomStyle = displayDOMUtils.CustomStyle;
@@ -1587,6 +1600,7 @@ var CustomStyle = displayDOMUtils.CustomStyle;
* @property {PDFPage} page
* @property {PageViewport} viewport
* @property {IPDFLinkService} linkService
* @property {DownloadManager} downloadManager
*/
/**
@@ -1628,6 +1642,9 @@ AnnotationElementFactory.prototype =
case AnnotationType.STRIKEOUT:
return new StrikeOutAnnotationElement(parameters);
case AnnotationType.FILEATTACHMENT:
return new FileAttachmentAnnotationElement(parameters);
default:
return new AnnotationElement(parameters);
}
@@ -1646,6 +1663,7 @@ var AnnotationElement = (function AnnotationElementClosure() {
this.page = parameters.page;
this.viewport = parameters.viewport;
this.linkService = parameters.linkService;
this.downloadManager = parameters.downloadManager;
if (isRenderable) {
this.container = this._createContainer();
@@ -1744,6 +1762,43 @@ var AnnotationElement = (function AnnotationElementClosure() {
return container;
},
/**
* Create a popup for the annotation's HTML element. This is used for
* annotations that do not have a Popup entry in the dictionary, but
* are of a type that works with popups (such as Highlight annotations).
*
* @private
* @param {HTMLSectionElement} container
* @param {HTMLDivElement|HTMLImageElement|null} trigger
* @param {Object} data
* @memberof AnnotationElement
*/
_createPopup:
function AnnotationElement_createPopup(container, trigger, data) {
// If no trigger element is specified, create it.
if (!trigger) {
trigger = document.createElement('div');
trigger.style.height = container.style.height;
trigger.style.width = container.style.width;
container.appendChild(trigger);
}
var popupElement = new PopupElement({
container: container,
trigger: trigger,
color: data.color,
title: data.title,
contents: data.contents,
hideWrapper: true
});
var popup = popupElement.render();
// Position the popup next to the annotation's container.
popup.style.left = container.style.width;
container.appendChild(popup);
},
/**
* Render the annotation's HTML element in the empty container.
*
@@ -1872,20 +1927,7 @@ var TextAnnotationElement = (function TextAnnotationElementClosure() {
image.dataset.l10nArgs = JSON.stringify({type: this.data.name});
if (!this.data.hasPopup) {
var popupElement = new PopupElement({
container: this.container,
trigger: image,
color: this.data.color,
title: this.data.title,
contents: this.data.contents,
hideWrapper: true
});
var popup = popupElement.render();
// Position the popup next to the Text annotation's container.
popup.style.left = image.style.width;
this.container.appendChild(popup);
this._createPopup(this.container, image, this.data);
}
this.container.appendChild(image);
@@ -2162,7 +2204,9 @@ var PopupElement = (function PopupElementClosure() {
var HighlightAnnotationElement = (
function HighlightAnnotationElementClosure() {
function HighlightAnnotationElement(parameters) {
AnnotationElement.call(this, parameters, true);
var isRenderable = !!(parameters.data.hasPopup ||
parameters.data.title || parameters.data.contents);
AnnotationElement.call(this, parameters, isRenderable);
}
Util.inherit(HighlightAnnotationElement, AnnotationElement, {
@@ -2175,6 +2219,11 @@ var HighlightAnnotationElement = (
*/
render: function HighlightAnnotationElement_render() {
this.container.className = 'highlightAnnotation';
if (!this.data.hasPopup) {
this._createPopup(this.container, null, this.data);
}
return this.container;
}
});
@@ -2189,7 +2238,9 @@ var HighlightAnnotationElement = (
var UnderlineAnnotationElement = (
function UnderlineAnnotationElementClosure() {
function UnderlineAnnotationElement(parameters) {
AnnotationElement.call(this, parameters, true);
var isRenderable = !!(parameters.data.hasPopup ||
parameters.data.title || parameters.data.contents);
AnnotationElement.call(this, parameters, isRenderable);
}
Util.inherit(UnderlineAnnotationElement, AnnotationElement, {
@@ -2202,6 +2253,11 @@ var UnderlineAnnotationElement = (
*/
render: function UnderlineAnnotationElement_render() {
this.container.className = 'underlineAnnotation';
if (!this.data.hasPopup) {
this._createPopup(this.container, null, this.data);
}
return this.container;
}
});
@@ -2215,7 +2271,9 @@ var UnderlineAnnotationElement = (
*/
var SquigglyAnnotationElement = (function SquigglyAnnotationElementClosure() {
function SquigglyAnnotationElement(parameters) {
AnnotationElement.call(this, parameters, true);
var isRenderable = !!(parameters.data.hasPopup ||
parameters.data.title || parameters.data.contents);
AnnotationElement.call(this, parameters, isRenderable);
}
Util.inherit(SquigglyAnnotationElement, AnnotationElement, {
@@ -2228,6 +2286,11 @@ var SquigglyAnnotationElement = (function SquigglyAnnotationElementClosure() {
*/
render: function SquigglyAnnotationElement_render() {
this.container.className = 'squigglyAnnotation';
if (!this.data.hasPopup) {
this._createPopup(this.container, null, this.data);
}
return this.container;
}
});
@@ -2242,7 +2305,9 @@ var SquigglyAnnotationElement = (function SquigglyAnnotationElementClosure() {
var StrikeOutAnnotationElement = (
function StrikeOutAnnotationElementClosure() {
function StrikeOutAnnotationElement(parameters) {
AnnotationElement.call(this, parameters, true);
var isRenderable = !!(parameters.data.hasPopup ||
parameters.data.title || parameters.data.contents);
AnnotationElement.call(this, parameters, isRenderable);
}
Util.inherit(StrikeOutAnnotationElement, AnnotationElement, {
@@ -2255,6 +2320,11 @@ var StrikeOutAnnotationElement = (
*/
render: function StrikeOutAnnotationElement_render() {
this.container.className = 'strikeoutAnnotation';
if (!this.data.hasPopup) {
this._createPopup(this.container, null, this.data);
}
return this.container;
}
});
@@ -2262,6 +2332,62 @@ var StrikeOutAnnotationElement = (
return StrikeOutAnnotationElement;
})();
/**
* @class
* @alias FileAttachmentAnnotationElement
*/
var FileAttachmentAnnotationElement = (
function FileAttachmentAnnotationElementClosure() {
function FileAttachmentAnnotationElement(parameters) {
AnnotationElement.call(this, parameters, true);
this.filename = getFilenameFromUrl(parameters.data.file.filename);
this.content = parameters.data.file.content;
}
Util.inherit(FileAttachmentAnnotationElement, AnnotationElement, {
/**
* Render the file attachment annotation's HTML element in the empty
* container.
*
* @public
* @memberof FileAttachmentAnnotationElement
* @returns {HTMLSectionElement}
*/
render: function FileAttachmentAnnotationElement_render() {
this.container.className = 'fileAttachmentAnnotation';
var trigger = document.createElement('div');
trigger.style.height = this.container.style.height;
trigger.style.width = this.container.style.width;
trigger.addEventListener('dblclick', this._download.bind(this));
if (!this.data.hasPopup && (this.data.title || this.data.contents)) {
this._createPopup(this.container, trigger, this.data);
}
this.container.appendChild(trigger);
return this.container;
},
/**
* Download the file attachment associated with this annotation.
*
* @private
* @memberof FileAttachmentAnnotationElement
*/
_download: function FileAttachmentAnnotationElement_download() {
if (!this.downloadManager) {
warn('Download cannot be started due to unavailable download manager');
return;
}
this.downloadManager.downloadData(this.content, this.filename, '');
}
});
return FileAttachmentAnnotationElement;
})();
/**
* @typedef {Object} AnnotationLayerParameters
* @property {PageViewport} viewport
@@ -2298,7 +2424,8 @@ var AnnotationLayer = (function AnnotationLayerClosure() {
layer: parameters.div,
page: parameters.page,
viewport: parameters.viewport,
linkService: parameters.linkService
linkService: parameters.linkService,
downloadManager: parameters.downloadManager
};
var element = annotationElementFactory.create(properties);
if (element.isRenderable) {
+61 -17
View File
@@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdfWorker = {}));
// Use strict in our context only - users might not want it
'use strict';
var pdfjsVersion = '1.4.83';
var pdfjsBuild = '0629fd0';
var pdfjsVersion = '1.4.95';
var pdfjsBuild = '2b813c0';
var pdfjsFilePath =
typeof document !== 'undefined' && document.currentScript ?
@@ -2366,6 +2366,17 @@ var UNSUPPORTED_FEATURES = PDFJS.UNSUPPORTED_FEATURES = {
font: 'font'
};
// Gets the file name from a given URL.
function getFilenameFromUrl(url) {
var anchor = url.indexOf('#');
var query = url.indexOf('?');
var end = Math.min(
anchor > 0 ? anchor : url.length,
query > 0 ? query : url.length);
return url.substring(url.lastIndexOf('/', end) + 1, end);
}
PDFJS.getFilenameFromUrl = getFilenameFromUrl;
// Combines two URLs. The baseUrl shall be absolute URL. If the url is an
// absolute URL, it will be returned as is.
function combineUrl(baseUrl, url) {
@@ -3496,6 +3507,7 @@ exports.combineUrl = combineUrl;
exports.createPromiseCapability = createPromiseCapability;
exports.deprecated = deprecated;
exports.error = error;
exports.getFilenameFromUrl = getFilenameFromUrl;
exports.getLookupTableFactory = getLookupTableFactory;
exports.info = info;
exports.isArray = isArray;
@@ -34982,6 +34994,7 @@ var ObjectLoader = (function() {
exports.Catalog = Catalog;
exports.ObjectLoader = ObjectLoader;
exports.XRef = XRef;
exports.FileSpec = FileSpec;
}));
@@ -38792,6 +38805,7 @@ var isName = corePrimitives.isName;
var Stream = coreStream.Stream;
var ColorSpace = coreColorSpace.ColorSpace;
var ObjectLoader = coreObj.ObjectLoader;
var FileSpec = coreObj.FileSpec;
var OperatorList = coreEvaluator.OperatorList;
/**
@@ -38817,6 +38831,7 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ {
// Return the right annotation object based on the subtype and field type.
var parameters = {
xref: xref,
dict: dict,
ref: ref
};
@@ -38850,6 +38865,9 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ {
case 'StrikeOut':
return new StrikeOutAnnotation(parameters);
case 'FileAttachment':
return new FileAttachmentAnnotation(parameters);
default:
warn('Unimplemented annotation type "' + subtype + '", ' +
'falling back to base annotation');
@@ -39083,6 +39101,24 @@ var Annotation = (function AnnotationClosure() {
}
},
/**
* Prepare the annotation for working with a popup in the display layer.
*
* @private
* @memberof Annotation
* @param {Dict} dict - The annotation's data dictionary
*/
_preparePopup: function Annotation_preparePopup(dict) {
if (!dict.has('C')) {
// Fall back to the default background color.
this.data.color = null;
}
this.data.hasPopup = dict.has('Popup');
this.data.title = stringToPDFString(dict.get('T') || '');
this.data.contents = stringToPDFString(dict.get('Contents') || '');
},
loadResources: function Annotation_loadResources(keys) {
return new Promise(function (resolve, reject) {
this.appearance.dict.getAsync('Resources').then(function (resources) {
@@ -39400,27 +39436,15 @@ var TextAnnotation = (function TextAnnotationClosure() {
this.data.annotationType = AnnotationType.TEXT;
var dict = parameters.dict;
if (this.data.hasAppearance) {
this.data.name = 'NoIcon';
} else {
this.data.rect[1] = this.data.rect[3] - DEFAULT_ICON_SIZE;
this.data.rect[2] = this.data.rect[0] + DEFAULT_ICON_SIZE;
this.data.name = dict.has('Name') ? dict.get('Name').name : 'Note';
}
if (!dict.has('C')) {
// Fall back to the default background color.
this.data.color = null;
}
this.data.hasPopup = dict.has('Popup');
if (!this.data.hasPopup) {
// There is no associated Popup annotation, so the Text annotation
// must create its own popup.
this.data.title = stringToPDFString(dict.get('T') || '');
this.data.contents = stringToPDFString(dict.get('Contents') || '');
this.data.name = parameters.dict.has('Name') ?
parameters.dict.get('Name').name : 'Note';
}
this._preparePopup(parameters.dict);
}
Util.inherit(TextAnnotation, Annotation, {});
@@ -39539,6 +39563,7 @@ var HighlightAnnotation = (function HighlightAnnotationClosure() {
Annotation.call(this, parameters);
this.data.annotationType = AnnotationType.HIGHLIGHT;
this._preparePopup(parameters.dict);
// PDF viewers completely ignore any border styles.
this.data.borderStyle.setWidth(0);
@@ -39554,6 +39579,7 @@ var UnderlineAnnotation = (function UnderlineAnnotationClosure() {
Annotation.call(this, parameters);
this.data.annotationType = AnnotationType.UNDERLINE;
this._preparePopup(parameters.dict);
// PDF viewers completely ignore any border styles.
this.data.borderStyle.setWidth(0);
@@ -39569,6 +39595,7 @@ var SquigglyAnnotation = (function SquigglyAnnotationClosure() {
Annotation.call(this, parameters);
this.data.annotationType = AnnotationType.SQUIGGLY;
this._preparePopup(parameters.dict);
// PDF viewers completely ignore any border styles.
this.data.borderStyle.setWidth(0);
@@ -39584,6 +39611,7 @@ var StrikeOutAnnotation = (function StrikeOutAnnotationClosure() {
Annotation.call(this, parameters);
this.data.annotationType = AnnotationType.STRIKEOUT;
this._preparePopup(parameters.dict);
// PDF viewers completely ignore any border styles.
this.data.borderStyle.setWidth(0);
@@ -39594,6 +39622,22 @@ var StrikeOutAnnotation = (function StrikeOutAnnotationClosure() {
return StrikeOutAnnotation;
})();
var FileAttachmentAnnotation = (function FileAttachmentAnnotationClosure() {
function FileAttachmentAnnotation(parameters) {
Annotation.call(this, parameters);
var file = new FileSpec(parameters.dict.get('FS'), parameters.xref);
this.data.annotationType = AnnotationType.FILEATTACHMENT;
this.data.file = file.serializable;
this._preparePopup(parameters.dict);
}
Util.inherit(FileAttachmentAnnotation, Annotation, {});
return FileAttachmentAnnotation;
})();
exports.Annotation = Annotation;
exports.AnnotationBorderStyle = AnnotationBorderStyle;
exports.AnnotationFactory = AnnotationFactory;
@@ -132,7 +132,8 @@
.annotationLayer .highlightAnnotation,
.annotationLayer .underlineAnnotation,
.annotationLayer .squigglyAnnotation,
.annotationLayer .strikeoutAnnotation {
.annotationLayer .strikeoutAnnotation,
.annotationLayer .fileAttachmentAnnotation {
cursor: pointer;
}
+29 -30
View File
@@ -12,15 +12,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals PDFJS, PDFBug, FirefoxCom, Stats, ProgressBar,
DownloadManager, getFileName, getPDFFileNameFromURL,
PDFHistory, Preferences, SidebarView, ViewHistory, Stats,
PDFThumbnailViewer, URL, noContextMenuHandler, SecondaryToolbar,
PasswordPrompt, PDFPresentationMode, PDFDocumentProperties, HandTool,
Promise, PDFLinkService, PDFOutlineView, PDFAttachmentView,
OverlayManager, PDFFindController, PDFFindBar, PDFViewer,
PDFRenderingQueue, PresentationModeState, parseQueryString,
RenderingStates, UNKNOWN_SCALE, DEFAULT_SCALE_VALUE,
/* globals PDFJS, PDFBug, FirefoxCom, Stats, ProgressBar, DownloadManager,
getPDFFileNameFromURL, PDFHistory, Preferences, SidebarView,
ViewHistory, Stats, PDFThumbnailViewer, URL, noContextMenuHandler,
SecondaryToolbar, PasswordPrompt, PDFPresentationMode,
PDFDocumentProperties, HandTool, Promise, PDFLinkService,
PDFOutlineView, PDFAttachmentView, OverlayManager,
PDFFindController, PDFFindBar, PDFViewer, PDFRenderingQueue,
PresentationModeState, parseQueryString, RenderingStates,
UNKNOWN_SCALE, DEFAULT_SCALE_VALUE,
IGNORE_CURRENT_POSITION_ON_ZOOM: true */
'use strict';
@@ -52,15 +52,6 @@ var MAX_AUTO_SCALE = 1.25;
var SCROLLBAR_PADDING = 40;
var VERTICAL_PADDING = 5;
function getFileName(url) {
var anchor = url.indexOf('#');
var query = url.indexOf('?');
var end = Math.min(
anchor > 0 ? anchor : url.length,
query > 0 ? query : url.length);
return url.substring(url.lastIndexOf('/', end) + 1, end);
}
/**
* Returns scale factor for the canvas. It makes sense for the HiDPI displays.
* @return {Object} The object with horizontal (sx) and vertical (sy)
@@ -4299,6 +4290,7 @@ DefaultTextLayerFactory.prototype = {
* @property {HTMLDivElement} pageDiv
* @property {PDFPage} pdfPage
* @property {IPDFLinkService} linkService
* @property {DownloadManager} downloadManager
*/
/**
@@ -4313,6 +4305,7 @@ var AnnotationLayerBuilder = (function AnnotationLayerBuilderClosure() {
this.pageDiv = options.pageDiv;
this.pdfPage = options.pdfPage;
this.linkService = options.linkService;
this.downloadManager = options.downloadManager;
this.div = null;
}
@@ -4337,7 +4330,8 @@ var AnnotationLayerBuilder = (function AnnotationLayerBuilderClosure() {
div: self.div,
annotations: annotations,
page: self.pdfPage,
linkService: self.linkService
linkService: self.linkService,
downloadManager: self.downloadManager
};
if (self.div) {
@@ -4401,6 +4395,8 @@ DefaultAnnotationLayerFactory.prototype = {
* @property {HTMLDivElement} container - The container for the viewer element.
* @property {HTMLDivElement} viewer - (optional) The viewer element.
* @property {IPDFLinkService} linkService - The navigation/linking service.
* @property {DownloadManager} downloadManager - (optional) The download
* manager component.
* @property {PDFRenderingQueue} renderingQueue - (optional) The rendering
* queue object.
* @property {boolean} removePageBorders - (optional) Removes the border shadow
@@ -4453,6 +4449,7 @@ var PDFViewer = (function pdfViewer() {
this.container = options.container;
this.viewer = options.viewer || options.container.firstElementChild;
this.linkService = options.linkService || new SimpleLinkService();
this.downloadManager = options.downloadManager || null;
this.removePageBorders = options.removePageBorders || false;
this.defaultRenderingQueue = !options.renderingQueue;
@@ -5118,7 +5115,8 @@ var PDFViewer = (function pdfViewer() {
return new AnnotationLayerBuilder({
pageDiv: pageDiv,
pdfPage: pdfPage,
linkService: this.linkService
linkService: this.linkService,
downloadManager: this.downloadManager
});
},
@@ -5242,7 +5240,6 @@ var PDFThumbnailView = (function PDFThumbnailViewClosure() {
this.linkService = linkService;
this.renderingQueue = renderingQueue;
this.hasImage = false;
this.resume = null;
this.renderingState = RenderingStates.INITIAL;
@@ -5298,7 +5295,6 @@ var PDFThumbnailView = (function PDFThumbnailViewClosure() {
if (this.renderTask) {
this.renderTask.cancel();
}
this.hasImage = false;
this.resume = null;
this.renderingState = RenderingStates.INITIAL;
@@ -5401,11 +5397,9 @@ var PDFThumbnailView = (function PDFThumbnailViewClosure() {
draw: function PDFThumbnailView_draw() {
if (this.renderingState !== RenderingStates.INITIAL) {
console.error('Must be in new state before drawing');
}
if (this.hasImage) {
return Promise.resolve(undefined);
}
this.hasImage = true;
this.renderingState = RenderingStates.RUNNING;
var resolveRenderPromise, rejectRenderPromise;
@@ -5426,6 +5420,7 @@ var PDFThumbnailView = (function PDFThumbnailViewClosure() {
rejectRenderPromise(error);
return;
}
self.renderingState = RenderingStates.FINISHED;
self._convertCanvasToImage();
@@ -5469,14 +5464,17 @@ var PDFThumbnailView = (function PDFThumbnailViewClosure() {
},
setImage: function PDFThumbnailView_setImage(pageView) {
if (this.renderingState !== RenderingStates.INITIAL) {
return;
}
var img = pageView.canvas;
if (this.hasImage || !img) {
if (!img) {
return;
}
if (!this.pdfPage) {
this.setPdfPage(pageView.pdfPage);
}
this.hasImage = true;
this.renderingState = RenderingStates.FINISHED;
var ctx = this._getPageDrawContext(true);
@@ -5927,7 +5925,7 @@ var PDFAttachmentView = (function PDFAttachmentViewClosure() {
for (var i = 0; i < attachmentsCount; i++) {
var item = attachments[names[i]];
var filename = getFileName(item.filename);
var filename = PDFJS.getFilenameFromUrl(item.filename);
var div = document.createElement('div');
div.className = 'attachmentsItem';
var button = document.createElement('button');
@@ -5993,7 +5991,8 @@ var PDFViewerApplication = {
container: container,
viewer: viewer,
renderingQueue: pdfRenderingQueue,
linkService: pdfLinkService
linkService: pdfLinkService,
downloadManager: new DownloadManager()
});
pdfRenderingQueue.setViewer(this.pdfViewer);
pdfLinkService.setViewer(this.pdfViewer);
@@ -6334,7 +6333,7 @@ var PDFViewerApplication = {
setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
this.url = url;
try {
this.setTitle(decodeURIComponent(getFileName(url)) || url);
this.setTitle(decodeURIComponent(PDFJS.getFilenameFromUrl(url)) || url);
} catch (e) {
// decodeURIComponent may throw URIError,
// fall back to using the unprocessed url in that case
@@ -395,9 +395,7 @@ let testCookiesObjects = Task.async(function*(index, hosts, cookiesActor) {
break;
}
}
if (!found) {
ok(false, "cookie " + item.name + " should not exist in response;");
}
ok(found, "cookie " + item.name + " should exist in response");
}
};
@@ -434,10 +432,7 @@ let testLocalStorageObjects = Task.async(function*(index, hosts, localStorageAct
break;
}
}
if (!found) {
ok(false, "local storage item " + item.name +
" should not exist in response;");
}
ok(found, "local storage item " + item.name + " should exist in response");
}
};
@@ -475,10 +470,7 @@ let testSessionStorageObjects = Task.async(function*(index, hosts, sessionStorag
break;
}
}
if (!found) {
ok(false, "session storage item " + item.name +
" should not exist in response;");
}
ok(found, "session storage item " + item.name + " should exist in response");
}
};
@@ -504,12 +496,7 @@ let testIndexedDB = Task.async(function*(indexedDBActor) {
break;
}
}
if (!found) {
ok (false, item + " should not be present in list stores response");
}
else {
ok (true, item + " found from indexedDB list stores response");
}
ok(found, item + " should exist in list stores response");
}
}
@@ -536,9 +523,7 @@ let testIndexedDBs = Task.async(function*(index, hosts, indexedDBActor) {
break;
}
}
if (!found) {
ok(false, "indexed db " + item.name + " should not exist in response");
}
ok(found, "indexed db " + item.name + " should exist in response");
}
};
@@ -579,16 +564,12 @@ let testObjectStores = Task.async(function*(index, hosts, indexedDBActor) {
break;
}
}
if (!indexFound) {
ok(false, "Index " + index + " should not be present in response");
}
ok(indexFound, "Index " + index + " should exist in response");
}
break;
}
}
if (!found) {
ok(false, "indexed db " + item.name + " should not exist in response");
}
ok(found, "indexed db " + item.name + " should exist in response");
}
};
@@ -626,9 +607,7 @@ let testIDBEntries = Task.async(function*(index, hosts, indexedDBActor) {
break;
}
}
if (!found) {
ok(false, "indexed db item " + item.name + " should not exist in response");
}
ok(found, "indexed db item " + item.name + " should exist in response");
}
};
+6 -4
View File
@@ -1,7 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let test_generator = do_run_test();
"use strict";
var test_generator = do_run_test();
function run_test()
{
@@ -31,10 +33,10 @@ function repeat_test()
}
// Purge threshold, in seconds.
let gPurgeAge = 1;
var gPurgeAge = 1;
// Short expiry age, in seconds.
let gShortExpiry = 2;
var gShortExpiry = 2;
// Required delay to ensure a purge occurs, in milliseconds. This must be at
// least gPurgeAge + 10%, and includes a little fuzz to account for timer
@@ -231,7 +233,7 @@ function get_creationTime(i)
function check_remaining_cookies(aNumberTotal, aNumberOld, aNumberToExpect) {
var enumerator = Services.cookiemgr.enumerator;
i = 0;
let i = 0;
while (enumerator.hasMoreElements()) {
var cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
++i;
+3 -2
View File
@@ -11,6 +11,7 @@
#include "gfxPrefs.h"
#include "image_operations.h"
#include "mozilla/SSE.h"
#include "mozilla/mips.h"
#include "convolver.h"
#include "skia/include/core/SkTypes.h"
@@ -228,7 +229,7 @@ Downscaler::CommitRow()
if (mCurrentInLine == inLineToRead) {
skia::ConvolveHorizontally(mRowBuffer.get(), *mXFilter,
mWindow[mLinesInBuffer++], mHasAlpha,
supports_sse2());
supports_sse2() || supports_mmi());
}
MOZ_ASSERT(mCurrentOutLine < mTargetSize.height,
@@ -316,7 +317,7 @@ Downscaler::DownscaleInputLine()
&mOutputBuffer[currentOutLine * mTargetSize.width * sizeof(uint32_t)];
skia::ConvolveVertically(static_cast<const FilterValue*>(filterValues),
filterLength, mWindow.get(), mXFilter->num_values(),
outputLine, mHasAlpha, supports_sse2());
outputLine, mHasAlpha, supports_sse2() || supports_mmi());
mCurrentOutLine += 1;
+3 -2
View File
@@ -23,6 +23,7 @@
#include "mozilla/Maybe.h"
#include "mozilla/SSE.h"
#include "mozilla/mips.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/gfx/2D.h"
#include "gfxPrefs.h"
@@ -236,7 +237,7 @@ public:
if (mInputRow == inputRowToRead) {
skia::ConvolveHorizontally(mRowBuffer.get(), *mXFilter,
mWindow[mRowsInWindow++], mHasAlpha,
supports_sse2());
supports_sse2() || supports_mmi());
}
MOZ_ASSERT(mOutputRow < mNext.InputSize().height,
@@ -311,7 +312,7 @@ private:
skia::ConvolveVertically(static_cast<const FilterValue*>(filterValues),
filterLength, mWindow.get(), mXFilter->num_values(),
reinterpret_cast<uint8_t*>(aRow), mHasAlpha,
supports_sse2());
supports_sse2() || supports_mmi());
return Some(WriteState::NEED_MORE_DATA);
});
+1 -1
View File
@@ -45,7 +45,7 @@ namespace image {
*
* BAD_ARGS: We failed to draw because bad arguments were passed to draw().
*/
enum class DrawResult : uint8_t
enum class MOZ_MUST_USE DrawResult : uint8_t
{
SUCCESS,
INCOMPLETE,
+5 -12
View File
@@ -372,22 +372,17 @@ nsContainerFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
/////////////////////////////////////////////////////////////////////////////
// Helper member functions
static nsresult
static void
ReparentFrameViewTo(nsIFrame* aFrame,
nsViewManager* aViewManager,
nsView* aNewParentView,
nsView* aOldParentView)
{
// XXX What to do about placeholder views for "position: fixed" elements?
// They should be reparented too.
// Does aFrame have a view?
if (aFrame->HasView()) {
#ifdef MOZ_XUL
if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) {
// This view must be parented by the root view, don't reparent it.
return NS_OK;
return;
}
#endif
nsView* view = aFrame->GetView();
@@ -400,7 +395,7 @@ ReparentFrameViewTo(nsIFrame* aFrame,
// The view will remember the Z-order and other attributes that have been set on it.
nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame);
aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr);
} else {
} else if (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) {
nsIFrame::ChildListIterator lists(aFrame);
for (; !lists.IsDone(); lists.Next()) {
// Iterate the child frames, and check each child frame to see if it has
@@ -412,8 +407,6 @@ ReparentFrameViewTo(nsIFrame* aFrame,
}
}
}
return NS_OK;
}
void
@@ -545,8 +538,8 @@ nsContainerFrame::ReparentFrameView(nsIFrame* aChildFrame,
// anything
if (oldParentView != newParentView) {
// They're not so we need to reparent any child views
return ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView,
oldParentView);
ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView,
oldParentView);
}
return NS_OK;
+4 -1
View File
@@ -34,7 +34,10 @@ class FramePropertyTable;
// dependency on nsDeviceContext.h. It doesn't matter if it's a
// little off.
#ifdef DEBUG
#define CRAZY_COORD (1000000*60)
// 10 million pixels, converted to app units. Note that this a bit larger
// than 1/4 of nscoord_MAX. So, if any content gets to be this large, we're
// definitely in danger of grazing up against nscoord_MAX; hence, it's CRAZY.
#define CRAZY_COORD (10000000*60)
#define CRAZY_SIZE(_x) (((_x) < -CRAZY_COORD) || ((_x) > CRAZY_COORD))
#endif
+2 -4
View File
@@ -910,12 +910,10 @@ KTableEntry nsCSSProps::kBackgroundClipKTable[] = {
{ eCSSKeyword_UNKNOWN, -1 }
};
#if 0
static_assert(ArrayLength(nsCSSProps::kImageLayerOriginKTable) ==
ArrayLength(nsCSSProps::kBackgroundClipKTable) - 1,
static_assert(MOZ_ARRAY_LENGTH(nsCSSProps::kImageLayerOriginKTable) ==
MOZ_ARRAY_LENGTH(nsCSSProps::kBackgroundClipKTable) - 1,
"background-clip has one extra value, which is text, compared"
"to {background,mask}-origin");
#endif
// Note: Don't change this table unless you update
// ParseImageLayerPosition!
+7
View File
@@ -8,6 +8,8 @@
#ifndef mozilla_throw_gcc_h
#define mozilla_throw_gcc_h
#if !defined(_LIBCPP_VERSION) || _LIBCPP_VERSION < 14000
#include "mozilla/Attributes.h"
#include <stdio.h> // snprintf
@@ -151,4 +153,9 @@ __throw_system_error(int err)
} // namespace std
#undef MOZ_THROW_NORETURN
#undef MOZ_THROW_INLINE
#endif
#endif // mozilla_throw_gcc_h
+3
View File
@@ -2204,6 +2204,9 @@ pref("network.stricttransportsecurity.enabled", true);
// Use the HSTS preload list by default
pref("network.stricttransportsecurity.preloadlist", true);
// Use JS mDNS as a fallback
pref("network.mdns.use_js_fallback", false);
pref("converter.html2txt.structs", true); // Output structured phrases (strong, em, code, sub, sup, b, i, u)
pref("converter.html2txt.header_strategy", 1); // 0 = no indention; 1 = indention, increased with header level; 2 = numbering and slight indention
// Whether we include ruby annotation in the text despite whether it
+44 -25
View File
@@ -532,9 +532,9 @@ CacheFileHandles::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
class ShutdownEvent : public nsRunnable {
public:
ShutdownEvent(mozilla::Mutex *aLock, mozilla::CondVar *aCondVar)
: mLock(aLock)
, mCondVar(aCondVar)
ShutdownEvent()
: mMonitor("ShutdownEvent.mMonitor")
, mNotified(false)
, mPrepare(true)
{
MOZ_COUNT_CTOR(ShutdownEvent);
@@ -564,18 +564,35 @@ public:
return NS_OK;
}
MutexAutoLock lock(*mLock);
MonitorAutoLock mon(mMonitor);
CacheFileIOManager::gInstance->ShutdownInternal();
mCondVar->Notify();
mNotified = true;
mon.Notify();
return NS_OK;
}
void PostAndWait()
{
MonitorAutoLock mon(mMonitor);
DebugOnly<nsresult> rv;
nsCOMPtr<nsIEventTarget> ioTarget =
CacheFileIOManager::gInstance->mIOThread->Target();
MOZ_ASSERT(ioTarget);
rv = ioTarget->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
MOZ_ASSERT(NS_SUCCEEDED(rv));
while (!mNotified) {
mon.Wait();
}
}
protected:
mozilla::Mutex *mLock;
mozilla::CondVar *mCondVar;
bool mPrepare;
mozilla::Monitor mMonitor;
bool mNotified;
bool mPrepare;
};
class OpenFileEvent : public nsRunnable {
@@ -1157,23 +1174,14 @@ CacheFileIOManager::Shutdown()
return NS_ERROR_NOT_INITIALIZED;
}
gInstance->mShutdownDemanded = true;
CacheIndex::PreShutdown();
ShutdownMetadataWriteScheduling();
{
mozilla::Mutex lock("CacheFileIOManager::Shutdown() lock");
mozilla::CondVar condVar(lock, "CacheFileIOManager::Shutdown() condVar");
MutexAutoLock autoLock(lock);
RefPtr<ShutdownEvent> ev = new ShutdownEvent(&lock, &condVar);
DebugOnly<nsresult> rv;
nsCOMPtr<nsIEventTarget> ioTarget = gInstance->mIOThread->Target();
MOZ_ASSERT(ioTarget);
rv = ioTarget->Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
MOZ_ASSERT(NS_SUCCEEDED(rv));
condVar.Wait();
}
RefPtr<ShutdownEvent> ev = new ShutdownEvent();
ev->PostAndWait();
MOZ_ASSERT(gInstance->mHandles.HandleCount() == 0);
MOZ_ASSERT(gInstance->mHandlesByLastUsed.Length() == 0);
@@ -2289,15 +2297,21 @@ CacheFileIOManager::ReleaseNSPRHandleInternal(CacheFileHandle *aHandle,
found = mHandlesByLastUsed.RemoveElement(aHandle);
MOZ_ASSERT(found);
if (aIgnoreShutdownLag || !IsPastShutdownIOLag()) {
PR_Close(aHandle->mFD);
} else {
// Leak invalid (w/o metadata) and doomed handles immediately after shutdown.
// Leak other handles when past the shutdown time maximum lag.
if (
#ifndef DEBUG
((aHandle->mInvalid || aHandle->mIsDoomed) && MOZ_UNLIKELY(mShutdownDemanded)) ||
#endif
MOZ_UNLIKELY(!aIgnoreShutdownLag && IsPastShutdownIOLag())) {
// Pretend this file has been validated (the metadata has been written)
// to prevent removal I/O on this apparently used file. The entry will
// never be used, since it doesn't have correct metadata, thus we don't
// need to worry about removing it.
aHandle->mInvalid = false;
LOG((" past the shutdown I/O lag, leaking file handle"));
} else {
PR_Close(aHandle->mFD);
}
aHandle->mFD = nullptr;
@@ -4034,13 +4048,16 @@ public:
}
mozilla::MonitorAutoLock mon(mMonitor);
mMonitorNotified = false;
nsresult rv = target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_ERROR("Dispatch failed, cannot do memory report of CacheFileHandles");
return 0;
}
mon.Wait();
while (!mMonitorNotified) {
mon.Wait();
}
return mSize;
}
@@ -4054,12 +4071,14 @@ public:
mSize += mSpecialHandles[i]->SizeOfIncludingThis(mMallocSizeOf);
}
mMonitorNotified = true;
mon.Notify();
return NS_OK;
}
private:
mozilla::Monitor mMonitor;
bool mMonitorNotified;
mozilla::MallocSizeOf mMallocSizeOf;
CacheFileHandles const &mHandles;
nsTArray<CacheFileHandle *> const &mSpecialHandles;
+4
View File
@@ -444,6 +444,10 @@ private:
// Shutdown time stamp, accessed only on the I/O thread. Used to bypass
// I/O after a certain time pass the shutdown has been demanded.
TimeStamp mShutdownDemandedTime;
// Set true on the main thread when cache shutdown is first demanded.
Atomic<bool, Relaxed> mShutdownDemanded;
// Set true on the IO thread, CLOSE level as part of the internal shutdown
// procedure.
bool mShuttingDown;
RefPtr<CacheIOThread> mIOThread;
nsCOMPtr<nsIFile> mCacheDirectory;
+2 -1
View File
@@ -157,8 +157,9 @@ already_AddRefed<nsIEventTarget> CacheIOThread::Target()
if (!target && mThread)
{
MonitorAutoLock lock(mMonitor);
if (!mXPCOMThread)
while (!mXPCOMThread) {
lock.Wait();
}
target = mXPCOMThread;
}
+1 -1
View File
@@ -36,7 +36,7 @@ public:
READ,
MANAGEMENT,
WRITE,
CLOSE,
CLOSE = WRITE,
INDEX,
EVICT,
LAST_LEVEL,
+12 -11
View File
@@ -13,6 +13,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/TimeStamp.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/net/NeckoCommon.h"
#include "prsystem.h"
#include <time.h>
#include <math.h>
@@ -98,6 +99,10 @@ NS_IMPL_ISUPPORTS(CacheObserver,
nsresult
CacheObserver::Init()
{
if (IsNeckoChild()) {
return NS_OK;
}
if (sSelf) {
return NS_OK;
}
@@ -446,18 +451,13 @@ CacheObserver::Observe(nsISupports* aSubject,
return NS_OK;
}
if (!strcmp(aTopic, "profile-before-change")) {
if (!strcmp(aTopic, "profile-change-net-teardown") ||
!strcmp(aTopic, "profile-before-change") ||
!strcmp(aTopic, "xpcom-shutdown")) {
RefPtr<CacheStorageService> service = CacheStorageService::Self();
if (service)
service->Shutdown();
return NS_OK;
}
if (!strcmp(aTopic, "xpcom-shutdown")) {
RefPtr<CacheStorageService> service = CacheStorageService::Self();
if (service)
if (service) {
service->Shutdown();
}
CacheFileIOManager::Shutdown();
return NS_OK;
@@ -465,8 +465,9 @@ CacheObserver::Observe(nsISupports* aSubject,
if (!strcmp(aTopic, "last-pb-context-exited")) {
RefPtr<CacheStorageService> service = CacheStorageService::Self();
if (service)
if (service) {
service->DropPrivateBrowsingEntries();
}
return NS_OK;
}
+4 -1
View File
@@ -2084,7 +2084,9 @@ NS_IMETHODIMP
CacheStorageService::IOThreadSuspender::Run()
{
MonitorAutoLock mon(mMon);
mon.Wait();
while (!mSignaled) {
mon.Wait();
}
return NS_OK;
}
@@ -2092,6 +2094,7 @@ void
CacheStorageService::IOThreadSuspender::Notify()
{
MonitorAutoLock mon(mMon);
mSignaled = true;
mon.Notify();
}
+2 -1
View File
@@ -366,13 +366,14 @@ private:
class IOThreadSuspender : public nsRunnable
{
public:
IOThreadSuspender() : mMon("IOThreadSuspender") { }
IOThreadSuspender() : mMon("IOThreadSuspender"), mSignaled(false) { }
void Notify();
private:
virtual ~IOThreadSuspender() { }
NS_IMETHOD Run() override;
Monitor mMon;
bool mSignaled;
};
RefPtr<IOThreadSuspender> mActiveIOSuspender;
+5 -4
View File
@@ -56,9 +56,9 @@ class nsCookie : public nsICookie2
, mExpiry(aExpiry)
, mLastAccessed(aLastAccessed)
, mCreationTime(aCreationTime)
, mIsSession(aIsSession != false)
, mIsSecure(aIsSecure != false)
, mIsHttpOnly(aIsHttpOnly != false)
, mIsSession(aIsSession)
, mIsSecure(aIsSecure)
, mIsHttpOnly(aIsHttpOnly)
, mOriginAttributes(aOriginAttributes)
{
}
@@ -101,7 +101,7 @@ class nsCookie : public nsICookie2
// setters
inline void SetExpiry(int64_t aExpiry) { mExpiry = aExpiry; }
inline void SetLastAccessed(int64_t aTime) { mLastAccessed = aTime; }
inline void SetIsSession(bool aIsSession) { mIsSession = (bool) aIsSession; }
inline void SetIsSession(bool aIsSession) { mIsSession = aIsSession; }
// Set the creation time manually, overriding the monotonicity checks in
// Create(). Use with caution!
inline void SetCreationTime(int64_t aTime) { mCreationTime = aTime; }
@@ -111,6 +111,7 @@ class nsCookie : public nsICookie2
protected:
virtual ~nsCookie() {}
private:
// member variables
// we use char* ptrs to store the strings in a contiguous block,
// so we save on the overhead of using nsCStrings. However, we
@@ -281,7 +281,11 @@ GetAddrInfoReplyRunnable::Reply(DNSServiceRef aSdRef,
}
NetAddr address;
memcpy(&address, aAddress, sizeof(*aAddress));
address.raw.family = aAddress->sa_family;
static_assert(sizeof(address.raw.data) >= sizeof(aAddress->sa_data),
"size of sockaddr.sa_data is too big");
memcpy(&address.raw.data, aAddress->sa_data, sizeof(aAddress->sa_data));
thread->Dispatch(new GetAddrInfoReplyRunnable(aSdRef,
aFlags,
@@ -0,0 +1,244 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* 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/. */
"use strict";
this.EXPORTED_SYMBOLS = ["MulticastDNS"];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Messaging.jsm");
Cu.import("resource://gre/modules/Services.jsm");
var log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.d.bind(null, "MulticastDNS");
const FAILURE_INTERNAL_ERROR = -65537;
// Helper function for sending commands to Java.
function send(type, data, callback) {
let msg = {
type: type
};
for (let i in data) {
try {
msg[i] = data[i];
} catch (e) {
}
}
Messaging.sendRequestForResult(msg)
.then(result => callback(result, null),
err => callback(null, typeof err === "number" ? err : FAILURE_INTERNAL_ERROR));
}
// Receives service found/lost event from NsdManager
function ServiceManager() {
}
ServiceManager.prototype = {
listeners: {},
numListeners: 0,
registerEvent: function() {
log("registerEvent");
Messaging.addListener(this.onServiceFound.bind(this), "NsdManager:ServiceFound");
Messaging.addListener(this.onServiceLost.bind(this), "NsdManager:ServiceLost");
},
unregisterEvent: function() {
log("unregisterEvent");
Messaging.removeListener("NsdManager:ServiceFound");
Messaging.removeListener("NsdManager:ServiceLost");
},
addListener: function(aServiceType, aListener) {
log("addListener: " + aServiceType + ", " + aListener);
if (!this.listeners[aServiceType]) {
this.listeners[aServiceType] = [];
}
if (this.listeners[aServiceType].includes(aListener)) {
log("listener already exists");
return;
}
this.listeners[aServiceType].push(aListener);
++this.numListeners;
if (this.numListeners === 1) {
this.registerEvent();
}
log("listener added: " + this);
},
removeListener: function(aServiceType, aListener) {
log("removeListener: " + aServiceType + ", " + aListener);
if (!this.listeners[aServiceType]) {
log("listener doesn't exist");
return;
}
let index = this.listeners[aServiceType].indexOf(aListener);
if (index < 0) {
log("listener doesn't exist");
return;
}
this.listeners[aServiceType].splice(index, 1);
--this.numListeners;
if (this.numListeners === 0) {
this.unregisterEvent();
}
log("listener removed" + this);
},
onServiceFound: function(aServiceInfo) {
let listeners = this.listeners[aServiceInfo.serviceType];
if (listeners) {
for (let listener of listeners) {
listener.onServiceFound(aServiceInfo);
}
} else {
log("no listener");
}
return {};
},
onServiceLost: function(aServiceInfo) {
let listeners = this.listeners[aServiceInfo.serviceType];
if (listeners) {
for (let listener of listeners) {
listener.onServiceLost(aServiceInfo);
}
} else {
log("no listener");
}
return {};
}
};
// make an object from nsIPropertyBag2
function parsePropertyBag2(bag) {
if (!bag || !(bag instanceof Ci.nsIPropertyBag2)) {
throw new TypeError("Not a property bag");
}
let attributes = [];
let enumerator = bag.enumerator;
while (enumerator.hasMoreElements()) {
let name = enumerator.getNext().QueryInterface(Ci.nsIProperty).name;
let value = bag.getPropertyAsACString(name);
attributes.push({
"name": name,
"value": value
});
}
return attributes;
}
function MulticastDNS() {
this.serviceManager = new ServiceManager();
}
MulticastDNS.prototype = {
startDiscovery: function(aServiceType, aListener) {
this.serviceManager.addListener(aServiceType, aListener);
let serviceInfo = {
serviceType: aServiceType,
uniqueId: aListener.uuid
};
send("NsdManager:DiscoverServices", serviceInfo, (result, err) => {
if (err) {
log("onStartDiscoveryFailed: " + aServiceType + " (" + err + ")");
this.serviceManager.removeListener(aServiceType, aListener);
aListener.onStartDiscoveryFailed(aServiceType, err);
} else {
aListener.onDiscoveryStarted(result);
}
});
},
stopDiscovery: function(aServiceType, aListener) {
this.serviceManager.removeListener(aServiceType, aListener);
let serviceInfo = {
uniqueId: aListener.uuid
};
send("NsdManager:StopServiceDiscovery", serviceInfo, (result, err) => {
if (err) {
log("onStopDiscoveryFailed: " + aServiceType + " (" + err + ")");
aListener.onStopDiscoveryFailed(aServiceType, err);
} else {
aListener.onDiscoveryStopped(aServiceType);
}
});
},
registerService: function(aServiceInfo, aListener) {
let serviceInfo = {
port: aServiceInfo.port,
serviceType: aServiceInfo.serviceType,
uniqueId: aListener.uuid
};
try {
serviceInfo.host = aServiceInfo.host;
} catch(e) {
// host unspecified
}
try {
serviceInfo.serviceName = aServiceInfo.serviceName;
} catch(e) {
// serviceName unspecified
}
try {
serviceInfo.attributes = parsePropertyBag2(aServiceInfo.attributes);
} catch(e) {
// attributes unspecified
}
send("NsdManager:RegisterService", serviceInfo, (result, err) => {
if (err) {
log("onRegistrationFailed: (" + err + ")");
aListener.onRegistrationFailed(aServiceInfo, err);
} else {
aListener.onServiceRegistered(result);
}
});
},
unregisterService: function(aServiceInfo, aListener) {
let serviceInfo = {
uniqueId: aListener.uuid
};
send("NsdManager:UnregisterService", serviceInfo, (result, err) => {
if (err) {
log("onUnregistrationFailed: (" + err + ")");
aListener.onUnregistrationFailed(aServiceInfo, err);
} else {
aListener.onServiceUnregistered(aServiceInfo);
}
});
},
resolveService: function(aServiceInfo, aListener) {
send("NsdManager:ResolveService", aServiceInfo, (result, err) => {
if (err) {
log("onResolveFailed: (" + err + ")");
aListener.onResolveFailed(aServiceInfo, err);
} else {
aListener.onServiceResolved(result);
}
});
}
};
@@ -0,0 +1,577 @@
/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* jshint esnext: true, moz: true */
'use strict';
this.EXPORTED_SYMBOLS = ['MulticastDNS'];
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
const MDNS_PORT = 5353;
const MDNS_ADDRESS = '224.0.0.251';
const DNS_REC_TYPE_PTR = 12;
const DNS_REC_TYPE_TXT = 16;
const DNS_REC_TYPE_SRV = 33;
const DNS_REC_TYPE_A = 1;
const DNS_REC_TYPE_NSEC= 47;
const DNS_CLASS_QU = 0x8000;
const DNS_CLASS_IN = 0x0001;
const DNS_SECTION_QD = 'qd';
const DNS_SECTION_AN = 'an';
const DNS_SECTION_NS = 'ns';
const DNS_SECTION_AR = 'ar';
const DEBUG = false;
function debug(msg) {
Services.console.logStringMessage('MulticastDNSFallback: ' + msg);
}
/* The following was taken from https://raw.githubusercontent.com/GoogleChrome/chrome-app-samples/master/mdns-browser/dns.js */
/**
* DataWriter writes data to an ArrayBuffer, presenting it as the instance
* variable 'buffer'.
*
* @constructor
*/
let DataWriter = function(opt_size) {
let loc = 0;
let view = new Uint8Array(new ArrayBuffer(opt_size || 512));
this.byte_ = function(v) {
view[loc] = v;
++loc;
this.buffer = view.buffer.slice(0, loc);
}.bind(this);
};
DataWriter.prototype.byte = function(v) {
this.byte_(v);
return this;
};
DataWriter.prototype.short = function(v) {
return this.byte((v >> 8) & 0xff).byte(v & 0xff);
};
DataWriter.prototype.long = function(v) {
return this.short((v >> 16) & 0xffff).short(v & 0xffff);
};
/**
* Writes a DNS name. If opt_ref is specified, will finish this name with a
* suffix reference (i.e., 0xc0 <ref>). If not, then will terminate with a NULL
* byte.
*/
DataWriter.prototype.name = function(v, opt_ref) {
let parts = v.split('.');
parts.forEach(function(part) {
this.byte(part.length);
for (let i = 0; i < part.length; ++i) {
this.byte(part.charCodeAt(i));
}
}.bind(this));
if (opt_ref) {
this.byte(0xc0).byte(opt_ref);
} else {
this.byte(0);
}
return this;
};
/**
* DataConsumer consumes data from an ArrayBuffer.
*
* @constructor
*/
let DataConsumer = function(arg) {
if (arg instanceof Uint8Array) {
this.view_ = arg;
} else {
this.view_ = new Uint8Array(arg);
}
this.loc_ = 0;
};
/**
* @return whether this DataConsumer has consumed all its data
*/
DataConsumer.prototype.isEOF = function() {
return this.loc_ >= this.view_.byteLength;
};
/**
* @param length {integer} number of bytes to return from the front of the view
* @return a Uint8Array
*/
DataConsumer.prototype.slice = function(length) {
let view = this.view_.subarray(this.loc_, this.loc_ + length);
this.loc_ += length;
return view;
};
DataConsumer.prototype.byte = function() {
this.loc_ += 1;
return this.view_[this.loc_ - 1];
};
DataConsumer.prototype.short = function() {
return (this.byte() << 8) + this.byte();
};
DataConsumer.prototype.long = function() {
return (this.short() << 16) + this.short();
};
/**
* Consumes a DNS name, which will finish with a NULL byte.
*/
DataConsumer.prototype.name = function() {
let parts = [];
for (;;) {
let len = this.byte();
if (!len) {
break;
} else if (len == 0xc0) {
// concat suffix reference (i.e., 0xc0 <ref>).
let ref = this.byte();
let refConsumer = new DataConsumer(new Uint8Array(this.view_.buffer, ref));
parts.push(refConsumer.name());
break;
}
// Otherwise, consume a string!
let v = '';
while (len-- > 0) {
v += String.fromCharCode(this.byte());
}
parts.push(v);
}
return parts.join('.');
};
/**
* Consumes a string according to length.
*/
DataConsumer.prototype.string = function() {
let len = this.byte();
if (!len) {
return;
}
let v = '';
while (len-- > 0) {
v += String.fromCharCode(this.byte());
}
return v;
};
/**
* DNSPacket holds the state of a DNS packet. It can be modified or serialized
* in-place.
*
* @constructor
*/
let DNSPacket = function(opt_flags) {
this.flags_ = opt_flags || 0; /* uint16 */
this.data_ = {};
this.data_[DNS_SECTION_QD] = [];
this.data_[DNS_SECTION_AN] = [];
this.data_[DNS_SECTION_NS] = [];
this.data_[DNS_SECTION_AR] = [];
};
/**
* Parse a DNSPacket from an ArrayBuffer (or Uint8Array).
*/
DNSPacket.parse = function(buffer) {
let consumer = new DataConsumer(buffer);
if (consumer.short()) {
throw new Error('DNS packet must start with 00 00');
}
let flags = consumer.short();
let count = {};
count[DNS_SECTION_QD] = consumer.short();
count[DNS_SECTION_AN] = consumer.short();
count[DNS_SECTION_NS] = consumer.short();
count[DNS_SECTION_AR] = consumer.short();
let packet = new DNSPacket(flags);
// Parse the QUESTION section.
for (let i = 0; i < count[DNS_SECTION_QD]; ++i) {
let part = new DNSRecord(
consumer.name(),
consumer.short(), // type
consumer.short()); // class
packet.push(DNS_SECTION_QD, part);
}
// Parse the ANSWER, AUTHORITY and ADDITIONAL sections.
[DNS_SECTION_AN, DNS_SECTION_NS, DNS_SECTION_AR].forEach(function(section) {
for (let i = 0; i < count[section]; ++i) {
let part = new DNSRecord(
consumer.name(),
consumer.short(),
consumer.short(), // class
consumer.long(), // ttl
consumer.slice(consumer.short()));
packet.push(section, part);
}
});
if (consumer.isEOF()) {
DEBUG && debug('was not EOF on incoming packet');
}
return packet;
};
DNSPacket.prototype.push = function(section, record) {
this.data_[section].push(record);
};
DNSPacket.prototype.each = function(section) {
let filter = false;
let call;
if (arguments.length == 2) {
call = arguments[1];
} else {
filter = arguments[1];
call = arguments[2];
}
this.data_[section].forEach(function(rec) {
if (!filter || rec.type == filter) {
call(rec);
}
});
};
/**
* Serialize this DNSPacket into an ArrayBuffer for sending over UDP.
*/
DNSPacket.prototype.serialize = function() {
let out = new DataWriter();
let s = [DNS_SECTION_QD, DNS_SECTION_AN, DNS_SECTION_NS, DNS_SECTION_AR];
out.short(0).short(this.flags_);
s.forEach(function(section) {
out.short(this.data_[section].length);
}.bind(this));
s.forEach(function(section) {
this.data_[section].forEach(function(rec) {
out.name(rec.name).short(rec.type).short(rec.cl);
if (section != DNS_SECTION_QD) {
// TODO: implement .bytes()
throw new Error('can\'t yet serialize non-QD records');
// out.long(rec.ttl).bytes(rec.data_);
}
});
}.bind(this));
return out.buffer;
};
/**
* DNSRecord is a record inside a DNS packet; e.g. a QUESTION, or an ANSWER,
* AUTHORITY, or ADDITIONAL record. Note that QUESTION records are special,
* and do not have ttl or data.
*/
let DNSRecord = function(name, type, cl, opt_ttl, opt_data) {
this.name = name;
this.type = type;
this.cl = cl;
this.isQD = (arguments.length == 3);
if (!this.isQD) {
this.ttl = opt_ttl;
this.data_ = opt_data;
}
};
DNSRecord.prototype.asName = function() {
return new DataConsumer(this.data_).name();
};
DNSRecord.prototype.asSRV = function() {
if (this.type !== DNS_REC_TYPE_SRV) {
return null;
}
let consumer = new DataConsumer(this.data_);
let data_length = this.data_.length;
return {
priority: consumer.short(),
weight: consumer.short(),
port: consumer.short(),
target: consumer.name() + consumer.name(),
};
};
DNSRecord.prototype.asTXT = function() {
if (this.type !== DNS_REC_TYPE_TXT) {
return null;
}
let consumer = new DataConsumer(this.data_);
let attributes = [];
while(!consumer.isEOF()) {
attributes.push(consumer.string());
}
return attributes;
};
/* end https://raw.githubusercontent.com/GoogleChrome/chrome-app-samples/master/mdns-browser/dns.js */
/**
* Parse fully qualified domain name to service name, instance name,
* and domain name. See https://tools.ietf.org/html/rfc6763#section-7.
*
* example: The Server._http._tcp.example.com
* instance name = "The Server"
* service type = "_http._tcp"
* domain = "example.com"
* @private
*/
function _parseDomainName(str) {
let items = str.split('.');
let idx = items.findIndex(function(element) {
return element === '_tcp' || element === '_udp';
});
return {
instanceName: items.splice(0, idx - 1).join('.'),
serviceType: items.splice(0, 2). join('.'),
domainName: items.join('.')
};
}
function _createPropertyBag(map) {
let bag = Cc['@mozilla.org/hash-property-bag;1']
.createInstance(Ci.nsIWritablePropertyBag);
for (let entry of map.entries()) {
bag.setProperty(entry[0], entry[1]);
}
return bag;
}
let MulticastDNS = function() {
this._targets = new Map();
};
MulticastDNS.prototype = {
socket: null,
//public API
startDiscovery: function(aServiceType, aListener) {
DEBUG && debug('startDiscovery for ' + aServiceType);
let { serviceType } = _parseDomainName(aServiceType);
this._addServiceListener(serviceType, aListener);
try {
this._ensureSocket();
this._query(serviceType + '.local', DNS_REC_TYPE_PTR);
aListener.onDiscoveryStarted(serviceType);
} catch (e) {
DEBUG && debug('onStartDiscoveryFailed: ' + serviceType + ' (' + e + ')');
this._removeServiceListener(serviceType, aListener);
aListener.onStartDiscoveryFailed(serviceType, Cr.NS_ERROR_FAILURE);
}
},
stopDiscovery: function(aServiceType, aListener) {
DEBUG && debug('stopDiscovery for ' + aServiceType);
let { serviceType } = _parseDomainName(aServiceType);
this._removeServiceListener(serviceType, aListener);
aListener.onDiscoveryStopped(serviceType);
if (this._targets.size === 0) {
DEBUG && debug('close current socket');
this.socket.close();
delete this.socket;
}
},
registerService: function(aServiceInfo, aListener) {
DEBUG && debug('service registration is not supported');
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
unregisterService: function(aServiceInfo, aListener) {
DEBUG && debug('service registration is not supported');
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
resolveService: function(aServiceInfo, aListener) {
DEBUG && debug('address info is already resolve during discovery phase');
aListener.onServiceResolved(aServiceInfo);
},
//private API
onReceive: function(info) {
let packet = DNSPacket.parse(info.rawData);
let serviceRecords = {};
packet.each(DNS_SECTION_AR, DNS_REC_TYPE_SRV, (rec) => {
DEBUG && debug('recieve SRV: ' + rec.name);
let srv = rec.asSRV();
serviceRecords[rec.name] = {
port: srv.port,
host: srv.target,
};
});
packet.each(DNS_SECTION_AR, DNS_REC_TYPE_TXT, (rec) => {
DEBUG && debug('recieve TXT: ' + rec.name);
if (!serviceRecords[rec.name]) {
return;
}
let txt = rec.asTXT();
let attributes = new Map();
for(let x in txt) {
let idx = x.indexOf('=');
if (idx < 0) {
attributes.set(txt[x], true);
continue;
}
let key = txt[x].substring(0, idx);
let value = txt[x].substring(idx + 1);
attributes.set(key, value);
}
serviceRecords[rec.name].attributes = attributes;
});
packet.each(DNS_SECTION_AN, DNS_REC_TYPE_PTR, (rec) => {
DEBUG && debug('recieve PTR: ' + rec.name);
let { serviceType: answerType } = _parseDomainName(rec.name);
if (this._targets.has(answerType)) {
let name = rec.asName();
DEBUG && debug('>> for ' + name);
let {instanceName, serviceType, domainName} = _parseDomainName(name);
let serviceInfo = {
host: serviceRecords[name].host,
address: info.fromAddr.address,
port: serviceRecords[name].port,
serviceType: serviceType,
serviceName: instanceName,
domainName: domainName,
attributes: _createPropertyBag(serviceRecords[name].attributes),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceInfo]),
};
this._targets.get(serviceType).forEach(function(listener) {
listener.onServiceFound(serviceInfo);
});
}
});
},
/**
* Handles network error occured while waiting for data.
* @private
*/
onReceiveError: function(socket, status) {
DEBUG && debug('receiver socket error' + status);
return true;
},
/**
* Broadcasts for services on the given socket/address.
* @private
*/
_query: function(search, type) {
DEBUG && debug('query for service: ' + search);
let packet = new DNSPacket();
packet.push(DNS_SECTION_QD, new DNSRecord(search, type, DNS_CLASS_IN | DNS_CLASS_QU));
this._broadcast(this.socket, packet);
},
/**
* Broadcasts a MDNS packet on the given socket/address.
* @private
*/
_broadcast: function(sock, packet) {
let raw = new DataView(packet.serialize());
let length = raw.byteLength;
let buf = [];
for (let x = 0; x < length; x++) {
let charcode = raw.getUint8(x);
buf[x] = charcode;
}
sock.send(MDNS_ADDRESS, MDNS_PORT, buf, buf.length);
},
_addServiceListener: function(serviceType, listener) {
let listeners = this._targets.get(serviceType);
if (!listeners) {
listeners = [];
this._targets.set(serviceType, listeners);
}
if (!listeners.find((element) => {
return element === listener;
})) {
DEBUG && debug('insert new listener');
listeners.push(listener);
}
},
_removeServiceListener: function(serviceType, listener) {
if (!this._targets.has(serviceType)) {
DEBUG && debug('listener doesnt exist');
return;
}
let listeners = this._targets.get(serviceType);
let idx = listeners.findIndex(function(element) {
return element === listener;
});
if (idx >= 0) {
listeners.splice(idx, 1);
}
if (listeners.length === 0) {
this._targets.delete(serviceType);
}
},
_ensureSocket: function() {
if (this.socket) {
DEBUG && debug('reuse current socket');
return;
}
this.socket = Cc['@mozilla.org/network/udp-socket;1']
.createInstance(Ci.nsIUDPSocket);
let self = this;
this.socket.init(MDNS_PORT, false,
Services.scriptSecurityManager.getSystemPrincipal());
this.socket.asyncListen({
onPacketReceived: function(aSocket, aMessage) {
self.onReceive(aMessage);
},
onStopListening: function(aSocket, aStatus) {
self.onReceiveError(aSocket, aStatus);
},
});
this.socket.joinMulticast(MDNS_ADDRESS);
},
};
+5
View File
@@ -10,6 +10,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
'nsDNSServiceDiscovery.manifest',
]
EXTRA_JS_MODULES += [
'MulticastDNSAndroid.jsm',
'MulticastDNSFallback.jsm',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' or \
(CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['ANDROID_VERSION'] >= '16'):
UNIFIED_SOURCES += [
@@ -5,9 +5,15 @@
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
Cu.import("resource://gre/modules/MulticastDNS.jsm");
Cu.import('resource://gre/modules/Services.jsm');
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
if (Services.prefs.getBoolPref("network.mdns.use_js_fallback")) {
Cu.import("resource://gre/modules/MulticastDNSFallback.jsm");
} else {
Cu.import("resource://gre/modules/MulticastDNSAndroid.jsm");
}
const DNSSERVICEDISCOVERY_CID = Components.ID("{f9346d98-f27a-4e89-b744-493843416480}");
const DNSSERVICEDISCOVERY_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1";
const DNSSERVICEINFO_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-info;1";
@@ -135,7 +141,7 @@ nsDNSServiceDiscovery.prototype = {
this.stopDiscovery = true;
return;
}
this.mdns.stopDiscovery(aServiceType, this);
this.mdns.stopDiscovery(aServiceType, listener);
}).bind(listener)
};
},
@@ -153,7 +159,7 @@ nsDNSServiceDiscovery.prototype = {
this.stopRegistration = true;
return;
}
this.mdns.unregisterService(aServiceInfo, this);
this.mdns.unregisterService(aServiceInfo, listener);
}).bind(listener)
};
},
+7
View File
@@ -79,3 +79,10 @@ else:
UNIFIED_SOURCES += [
'nameprep.c',
]
if CONFIG['_MSC_VER']:
# This is intended as a temporary hack to support building with VS2015.
# icu\source\common\unicode/ucasemap.h(93): warning C4577:
# 'noexcept' used with no exception handling mode specified;
# termination on exception is not guaranteed. Specify /EHsc from unified dns
CXXFLAGS += ['-wd4577']
+2 -1
View File
@@ -406,7 +406,8 @@ HostDB_MatchEntry(const PLDHashEntryHdr *entry,
const nsHostDBEnt *he = static_cast<const nsHostDBEnt *>(entry);
const nsHostKey *hk = static_cast<const nsHostKey *>(key);
return !strcmp(he->rec->host, hk->host) &&
return !strcmp(he->rec->host ? he->rec->host : "",
hk->host ? hk->host : "") &&
RES_KEY_FLAGS (he->rec->flags) == RES_KEY_FLAGS(hk->flags) &&
he->rec->af == hk->af &&
!strcmp(he->rec->netInterface, hk->netInterface);
+1 -6
View File
@@ -90,13 +90,8 @@ IsNeckoChild()
static bool amChild = false;
if (!didCheck) {
// This allows independent necko-stacks (instead of single stack in chrome)
// to still be run.
// TODO: Remove eventually when no longer supported (bug 571126)
const char * e = PR_GetEnv("NECKO_SEPARATE_STACKS");
if (!e)
amChild = XRE_IsContentProcess();
didCheck = true;
amChild = (XRE_GetProcessType() == GeckoProcessType_Content);
}
return amChild;
}
+11
View File
@@ -1007,6 +1007,17 @@ NeckoParent::OfflineNotification(nsISupports *aSubject)
}
// XPCShells don't have any TabParents
// Just send the ipdl message to the child process.
if (!UsingNeckoIPCSecurity()) {
bool offline = false;
gIOService->IsAppOffline(targetAppId, &offline);
if (!SendAppOfflineStatus(targetAppId, offline)) {
printf_stderr("NeckoParent: "
"SendAppOfflineStatus failed for targetAppId: %u\n", targetAppId);
}
}
return NS_OK;
}
+1 -2
View File
@@ -564,8 +564,7 @@ Http2Stream::GenerateOpen()
firstFrameFlags |= Http2Session::kFlag_END_STREAM;
} else if (head->IsPost() ||
head->IsPut() ||
head->IsConnect() ||
head->IsOptions()) {
head->IsConnect()) {
// place fin in a data frame even for 0 length messages for iterop
} else if (!mRequestBodyLenRemaining) {
// for other HTTP extension methods, rely on the content-length
-1
View File
@@ -184,7 +184,6 @@ private:
nsCOMPtr<nsIChildChannel> mRedirectChannelChild;
RefPtr<InterceptStreamListener> mInterceptListener;
RefPtr<nsInputStreamPump> mSynthesizedResponsePump;
nsAutoPtr<nsHttpResponseHead> mSynthesizedResponseHead;
nsCOMPtr<nsIInputStream> mSynthesizedInput;
int64_t mSynthesizedStreamLength;
@@ -148,6 +148,14 @@ HttpChannelParentListener::AsyncOnChannelRedirect(
{
nsresult rv;
nsCOMPtr<nsIParentRedirectingChannel> activeRedirectingChannel =
do_QueryInterface(mNextListener);
if (!activeRedirectingChannel) {
NS_ERROR("Channel got a redirect response, but doesn't implement "
"nsIParentRedirectingChannel to handle it.");
return NS_ERROR_NOT_IMPLEMENTED;
}
// Register the new channel and obtain id for it
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv);
@@ -158,13 +166,6 @@ HttpChannelParentListener::AsyncOnChannelRedirect(
LOG(("Registered %p channel under id=%d", newChannel, mRedirectChannelId));
nsCOMPtr<nsIParentRedirectingChannel> activeRedirectingChannel =
do_QueryInterface(mNextListener);
if (!activeRedirectingChannel) {
NS_RUNTIMEABORT("Channel got a redirect response, but doesn't implement "
"nsIParentRedirectingChannel to handle it.");
}
return activeRedirectingChannel->StartRedirect(mRedirectChannelId,
newChannel,
redirectFlags,
@@ -198,6 +198,9 @@ InterceptedChannelChrome::ResetInterception()
nsresult rv = mChannel->StartRedirectChannelToURI(uri, nsIChannelEventSink::REDIRECT_INTERNAL);
NS_ENSURE_SUCCESS(rv, rv);
mResponseBody->Close();
mResponseBody = nullptr;
mReleaseHandle = nullptr;
mChannel = nullptr;
return NS_OK;
@@ -230,6 +233,11 @@ InterceptedChannelChrome::FinishSynthesizedResponse(const nsACString& aFinalURLS
return NS_ERROR_NOT_AVAILABLE;
}
// Make sure the cache entry's output stream is always closed. If the
// channel was intercepted with a null-body response then its possible
// the synthesis completed without a stream copy operation.
mResponseBody->Close();
mReportCollector->FlushConsoleReports(mChannel);
EnsureSynthesizedResponse();
@@ -382,6 +390,7 @@ InterceptedChannelContent::ResetInterception()
mReportCollector->FlushConsoleReports(mChannel);
mResponseBody->Close();
mResponseBody = nullptr;
mSynthesizedInput = nullptr;
@@ -418,6 +427,11 @@ InterceptedChannelContent::FinishSynthesizedResponse(const nsACString& aFinalURL
return NS_ERROR_NOT_AVAILABLE;
}
// Make sure the body output stream is always closed. If the channel was
// intercepted with a null-body response then its possible the synthesis
// completed without a stream copy operation.
mResponseBody->Close();
mReportCollector->FlushConsoleReports(mChannel);
EnsureSynthesizedResponse();
+2 -2
View File
@@ -736,7 +736,7 @@ PackagedAppService::PackagedAppDownloader::AddCallback(nsIURI *aURI,
{
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mCallbacks hashtable is not thread safe");
nsAutoCString spec;
aURI->GetAsciiSpec(spec);
aURI->GetSpecIgnoringRef(spec);
LogURI("PackagedAppDownloader::AddCallback", this, aURI);
LOG(("[%p] > callback: %p\n", this, aCallback));
@@ -1075,7 +1075,7 @@ PackagedAppService::GetResource(nsIChannel *aChannel,
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
nsCOMPtr<nsIURI> uri;
rv = principal->GetURI(getter_AddRefs(uri));
rv = aChannel->GetURI(getter_AddRefs(uri));
if (NS_WARN_IF(NS_FAILED(rv))) {
LOG(("[%p] > Error calling GetURI rv=%X\n", this, rv));
return rv;
+19 -12
View File
@@ -12,6 +12,7 @@
#include "mozilla/Mutex.h"
#include "mozilla/HashFunctions.h"
#include "nsCRT.h"
#include <errno.h>
namespace mozilla {
namespace net {
@@ -294,19 +295,25 @@ nsHttp::FindToken(const char *input, const char *token, const char *seps)
bool
nsHttp::ParseInt64(const char *input, const char **next, int64_t *r)
{
const char *start = input;
*r = 0;
while (*input >= '0' && *input <= '9') {
int64_t next = 10 * (*r) + (*input - '0');
if (next < *r) // overflow?
return false;
*r = next;
++input;
}
if (input == start) // nothing parsed?
MOZ_ASSERT(input);
MOZ_ASSERT(r);
char *end = nullptr;
errno = 0; // Clear errno to make sure its value is set by strtoll
int64_t value = strtoll(input, &end, /* base */ 10);
// Fail if: - the parsed number overflows.
// - the end points to the start of the input string.
// - we parsed a negative value. Consumers don't expect that.
if (errno != 0 || end == input || value < 0) {
LOG(("nsHttp::ParseInt64 value=%ld errno=%d", value, errno));
return false;
if (next)
*next = input;
}
if (next) {
*next = end;
}
*r = value;
return true;
}
@@ -824,9 +824,7 @@ nsHttpChannelAuthProvider::BlockPrompt()
nsCOMPtr<nsIHttpChannelInternal> chanInternal = do_QueryInterface(mAuthChannel);
MOZ_ASSERT(chanInternal);
bool skipAuthentication = false;
nsresult rv = chanInternal->GetBlockAuthPrompt(&skipAuthentication);
if (NS_SUCCEEDED(rv) && skipAuthentication) {
if (chanInternal->GetBlockAuthPrompt()) {
return true;
}
@@ -185,7 +185,6 @@ nsHttpConnectionMgr::Shutdown()
// wait for shutdown event to complete
while (!shutdownWrapper->mBool) {
fprintf(stderr, "nsHttpConnectionMgr::Shutdown() ProcessNextEvent\n");
NS_ProcessNextEvent(NS_GetCurrentThread());
}
+3 -1
View File
@@ -415,7 +415,7 @@ nsHttpResponseHead::ComputeCurrentAge(uint32_t now,
// <or>
// freshnessLifetime = expires_value - date_value
// <or>
// freshnessLifetime = (date_value - last_modified_value) * 0.10
// freshnessLifetime = min(one-week,(date_value - last_modified_value) * 0.10)
// <or>
// freshnessLifetime = 0
//
@@ -463,6 +463,8 @@ nsHttpResponseHead::ComputeFreshnessLifetime(uint32_t *result) const
if (date2 <= date) {
// this only makes sense if last-modified is actually in the past
*result = (date - date2) / 10;
const uint32_t kOneWeek = 60 * 60 * 24 * 7;
*result = std::min(kOneWeek, *result);
return NS_OK;
}
}
@@ -39,7 +39,7 @@ interface nsIHttpUpgradeListener : nsISupports
* using any feature exposed by this interface, be aware that this interface
* will change and you will be broken. You have been warned.
*/
[scriptable, uuid(01b8296a-e206-4e5f-acab-82bd8b6a900c)]
[builtinclass, scriptable, uuid(4e28263d-1e03-46f4-aa5c-9512f91957f9)]
interface nsIHttpChannelInternal : nsISupports
{
/**
@@ -283,5 +283,6 @@ interface nsIHttpChannelInternal : nsISupports
* authentication failure, that failure will be propagated to the channel
* listener. Must be called before opening the channel, otherwise throws.
*/
[infallible]
attribute boolean blockAuthPrompt;
};
@@ -18,6 +18,7 @@
#include "nsIStreamConverterService.h"
#include "nsIPipe.h"
#include "nsNetUtil.h"
#include "LoadInfo.h"
namespace mozilla {
@@ -103,8 +104,9 @@ ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI,
const char* kToType = "text/css";
nsCOMPtr<nsIInputStream> inputStream;
if (aLoadInfo && aLoadInfo->GetSecurityMode()) {
// Certain security checks require an async channel.
if (aLoadInfo &&
aLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
// If the channel needs to enforce CORS, we need to open the channel async.
nsCOMPtr<nsIOutputStream> outputStream;
rv = NS_NewPipe(getter_AddRefs(inputStream), getter_AddRefs(outputStream),
@@ -121,13 +123,20 @@ ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI,
aURI, getter_AddRefs(converter));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILoadInfo> loadInfo =
static_cast<mozilla::LoadInfo*>(aLoadInfo)->CloneForNewRequest();
(*result)->SetLoadInfo(loadInfo);
rv = (*result)->AsyncOpen2(converter);
} else {
// Stylesheet loads for extension content scripts require a sync channel,
// but fortunately do not invoke security checks.
// Stylesheet loads for extension content scripts require a sync channel.
nsCOMPtr<nsIInputStream> sourceStream;
rv = (*result)->Open(getter_AddRefs(sourceStream));
if (aLoadInfo && aLoadInfo->GetEnforceSecurity()) {
rv = (*result)->Open2(getter_AddRefs(sourceStream));
} else {
rv = (*result)->Open(getter_AddRefs(sourceStream));
}
NS_ENSURE_SUCCESS(rv, rv);
rv = convService->Convert(sourceStream, kFromType, kToType,
@@ -353,28 +353,40 @@ SubstitutingProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
return NS_OK;
}
// Unescape the path so we can perform some checks on it.
nsAutoCString unescapedPath(path);
NS_UnescapeURL(unescapedPath);
// Don't misinterpret the filepath as an absolute URI.
if (unescapedPath.FindChar(':') != -1)
return NS_ERROR_MALFORMED_URI;
if (unescapedPath.FindChar('\\') != -1)
return NS_ERROR_MALFORMED_URI;
const char *p = path.get() + 1; // path always starts with a slash
NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!");
if (*p == '/')
return NS_ERROR_MALFORMED_URI;
nsCOMPtr<nsIURI> baseURI;
rv = GetSubstitution(host, getter_AddRefs(baseURI));
if (NS_FAILED(rv)) return rv;
rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result);
// Unescape the path so we can perform some checks on it.
nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
if (!url) {
return NS_ERROR_MALFORMED_URI;
}
nsAutoCString unescapedPath;
rv = url->GetFilePath(unescapedPath);
if (NS_FAILED(rv)) return rv;
NS_UnescapeURL(unescapedPath);
if (unescapedPath.FindChar('\\') != -1) {
return NS_ERROR_MALFORMED_URI;
}
// Some code relies on an empty path resolving to a file rather than a
// directory.
NS_ASSERTION(path.CharAt(0) == '/', "Path must begin with '/'");
if (path.Length() == 1) {
rv = baseURI->GetSpec(result);
} else {
// Make sure we always resolve the path as file-relative to our target URI.
path.InsertLiteral(".", 0);
rv = baseURI->Resolve(path, result);
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (MOZ_LOG_TEST(gResLog, LogLevel::Debug)) {
nsAutoCString spec;
+40 -43
View File
@@ -21,46 +21,27 @@ using mozilla::dom::ContentParent;
using mozilla::LogLevel;
using mozilla::Unused;
#define kAPP NS_LITERAL_CSTRING("app")
#define kGRE NS_LITERAL_CSTRING("gre")
#define kAPP "app"
#define kGRE "gre"
nsresult
nsResProtocolHandler::Init()
{
nsresult rv;
nsAutoCString appURI, greURI;
rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appURI);
rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, mAppURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greURI);
rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, mGREURI);
NS_ENSURE_SUCCESS(rv, rv);
//
// make resource:/// point to the application directory or omnijar
//
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), appURI.Length() ? appURI : greURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = SetSubstitution(EmptyCString(), uri);
NS_ENSURE_SUCCESS(rv, rv);
//
// make resource://app/ point to the application directory or omnijar
//
rv = SetSubstitution(kAPP, uri);
NS_ENSURE_SUCCESS(rv, rv);
//
// make resource://gre/ point to the GRE directory
//
if (appURI.Length()) { // We already have greURI in uri if appURI.Length() is 0.
rv = NS_NewURI(getter_AddRefs(uri), greURI);
NS_ENSURE_SUCCESS(rv, rv);
// mozilla::Omnijar::GetURIString always returns a string ending with /,
// and we want to remove it.
mGREURI.Truncate(mGREURI.Length() - 1);
if (mAppURI.Length()) {
mAppURI.Truncate(mAppURI.Length() - 1);
} else {
mAppURI = mGREURI;
}
rv = SetSubstitution(kGRE, uri);
NS_ENSURE_SUCCESS(rv, rv);
//XXXbsmedberg Neil wants a resource://pchrome/ for the profile chrome dir...
// but once I finish multiple chrome registration I'm not sure that it is needed
@@ -83,20 +64,36 @@ NS_IMPL_RELEASE_INHERITED(nsResProtocolHandler, SubstitutingProtocolHandler)
nsresult
nsResProtocolHandler::GetSubstitutionInternal(const nsACString& root, nsIURI **result)
{
// try invoking the directory service for "resource:root"
nsAutoCString uri;
nsAutoCString key;
key.AssignLiteral("resource:");
key.Append(root);
nsCOMPtr<nsIFile> file;
nsresult rv = NS_GetSpecialDirectory(key.get(), getter_AddRefs(file));
if (NS_FAILED(rv))
return NS_ERROR_NOT_AVAILABLE;
rv = IOService()->NewFileURI(file, result);
if (NS_FAILED(rv))
if (!ResolveSpecialCases(root, NS_LITERAL_CSTRING("/"), uri)) {
return NS_ERROR_NOT_AVAILABLE;
}
return NS_OK;
return NS_NewURI(result, uri);
}
bool
nsResProtocolHandler::ResolveSpecialCases(const nsACString& aHost,
const nsACString& aPath,
nsACString& aResult)
{
if (aHost.Equals("") || aHost.Equals(kAPP)) {
aResult.Assign(mAppURI);
} else if (aHost.Equals(kGRE)) {
aResult.Assign(mGREURI);
} else {
return false;
}
aResult.Append(aPath);
return true;
}
nsresult
nsResProtocolHandler::SetSubstitution(const nsACString& aRoot, nsIURI* aBaseURI)
{
MOZ_ASSERT(!aRoot.Equals(""));
MOZ_ASSERT(!aRoot.Equals(kAPP));
MOZ_ASSERT(!aRoot.Equals(kGRE));
return SubstitutingProtocolHandler::SetSubstitution(aRoot, aBaseURI);
}
+24 -1
View File
@@ -23,7 +23,6 @@ public:
NS_DECL_NSIRESPROTOCOLHANDLER
NS_FORWARD_NSIPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::)
NS_FORWARD_NSISUBSTITUTINGPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::)
nsResProtocolHandler()
: SubstitutingProtocolHandler("resource", URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE,
@@ -32,9 +31,33 @@ public:
nsresult Init();
NS_IMETHOD SetSubstitution(const nsACString& aRoot, nsIURI* aBaseURI) override;
NS_IMETHOD GetSubstitution(const nsACString& aRoot, nsIURI** aResult) override
{
return mozilla::SubstitutingProtocolHandler::GetSubstitution(aRoot, aResult);
}
NS_IMETHOD HasSubstitution(const nsACString& aRoot, bool* aResult) override
{
return mozilla::SubstitutingProtocolHandler::HasSubstitution(aRoot, aResult);
}
NS_IMETHOD ResolveURI(nsIURI *aResURI, nsACString& aResult) override
{
return mozilla::SubstitutingProtocolHandler::ResolveURI(aResURI, aResult);
}
protected:
nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult) override;
virtual ~nsResProtocolHandler() {}
bool ResolveSpecialCases(const nsACString& aHost, const nsACString& aPath,
nsACString& aResult) override;
private:
nsCString mAppURI;
nsCString mGREURI;
};
#endif /* nsResProtocolHandler_h___ */
+23 -16
View File
@@ -10,7 +10,8 @@ NS_IMPL_ISUPPORTS(nsDirIndex,
nsDirIndex::nsDirIndex() : mType(TYPE_UNKNOWN),
mSize(UINT64_MAX),
mLastModified(-1) {
mLastModified(-1LL)
{
}
nsDirIndex::~nsDirIndex() {}
@@ -18,9 +19,7 @@ nsDirIndex::~nsDirIndex() {}
NS_IMETHODIMP
nsDirIndex::GetType(uint32_t* aType)
{
if (!aType) {
return NS_ERROR_NULL_POINTER;
}
NS_ENSURE_ARG_POINTER(aType);
*aType = mType;
return NS_OK;
@@ -34,7 +33,10 @@ nsDirIndex::SetType(uint32_t aType)
}
NS_IMETHODIMP
nsDirIndex::GetContentType(char* *aContentType) {
nsDirIndex::GetContentType(char* *aContentType)
{
NS_ENSURE_ARG_POINTER(aContentType);
*aContentType = ToNewCString(mContentType);
if (!*aContentType)
return NS_ERROR_OUT_OF_MEMORY;
@@ -43,13 +45,17 @@ nsDirIndex::GetContentType(char* *aContentType) {
}
NS_IMETHODIMP
nsDirIndex::SetContentType(const char* aContentType) {
nsDirIndex::SetContentType(const char* aContentType)
{
mContentType = aContentType;
return NS_OK;
}
NS_IMETHODIMP
nsDirIndex::GetLocation(char* *aLocation) {
nsDirIndex::GetLocation(char* *aLocation)
{
NS_ENSURE_ARG_POINTER(aLocation);
*aLocation = ToNewCString(mLocation);
if (!*aLocation)
return NS_ERROR_OUT_OF_MEMORY;
@@ -58,13 +64,17 @@ nsDirIndex::GetLocation(char* *aLocation) {
}
NS_IMETHODIMP
nsDirIndex::SetLocation(const char* aLocation) {
nsDirIndex::SetLocation(const char* aLocation)
{
mLocation = aLocation;
return NS_OK;
}
NS_IMETHODIMP
nsDirIndex::GetDescription(char16_t* *aDescription) {
nsDirIndex::GetDescription(char16_t* *aDescription)
{
NS_ENSURE_ARG_POINTER(aDescription);
*aDescription = ToNewUnicode(mDescription);
if (!*aDescription)
return NS_ERROR_OUT_OF_MEMORY;
@@ -73,7 +83,8 @@ nsDirIndex::GetDescription(char16_t* *aDescription) {
}
NS_IMETHODIMP
nsDirIndex::SetDescription(const char16_t* aDescription) {
nsDirIndex::SetDescription(const char16_t* aDescription)
{
mDescription.Assign(aDescription);
return NS_OK;
}
@@ -81,9 +92,7 @@ nsDirIndex::SetDescription(const char16_t* aDescription) {
NS_IMETHODIMP
nsDirIndex::GetSize(int64_t* aSize)
{
if (!aSize) {
return NS_ERROR_NULL_POINTER;
}
NS_ENSURE_ARG_POINTER(aSize);
*aSize = mSize;
return NS_OK;
@@ -99,9 +108,7 @@ nsDirIndex::SetSize(int64_t aSize)
NS_IMETHODIMP
nsDirIndex::GetLastModified(PRTime* aLastModified)
{
if (!aLastModified) {
return NS_ERROR_NULL_POINTER;
}
NS_ENSURE_ARG_POINTER(aLastModified);
*aLastModified = mLastModified;
return NS_OK;
@@ -134,7 +134,7 @@ nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports *aContext,
if (fpChannel && !isPending) {
fpChannel->ForcePending(true);
}
if (mBrotli->mTotalOut == 0 && !BrotliDecoderIsFinished(&mBrotli->mState)) {
if (mBrotli && (mBrotli->mTotalOut == 0) && !BrotliDecoderIsFinished(&mBrotli->mState)) {
status = NS_ERROR_INVALID_CONTENT_ENCODING;
}
if (fpChannel && !isPending) {
@@ -800,7 +800,7 @@ nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest,
PRTime t;
aIndex->GetLastModified(&t);
if (t == -1) {
if (t == -1LL) {
pushBuffer.AppendLiteral("></td>\n <td>");
} else {
pushBuffer.AppendLiteral(" sortable-data=\"");
@@ -666,6 +666,10 @@ nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context,
// Push the cursor to the token so that the while loop below will
// find token from the right position.
cursor = tokenPos;
// Update bufLen to exlude the preamble. Otherwise, the first
// |SendData| would claim longer buffer length.
bufLen -= mPreamble.Length();
}
} else {
// If the boundary was set in the header,
+95 -22
View File
@@ -1,41 +1,114 @@
/* verify that certain invalid URIs are not parsed by the resource
protocol handler */
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const specs = [
"resource:////",
"resource:///http://www.mozilla.org/",
"resource:///file:///",
"resource:///..\\",
"resource:///..\\..\\",
"resource:///..%5C",
"resource:///..%5c"
"resource://res-test//",
"resource://res-test/?foo=http:",
"resource://res-test/?foo=" + encodeURIComponent("http://example.com/"),
"resource://res-test/?foo=" + encodeURIComponent("x\\y"),
"resource://res-test/..%2F",
"resource://res-test/..%2f",
"resource://res-test/..%2F..",
"resource://res-test/..%2f..",
"resource://res-test/../../",
"resource://res-test/http://www.mozilla.org/",
"resource://res-test/file:///",
];
function check_for_exception(spec)
const error_specs = [
"resource://res-test/..\\",
"resource://res-test/..\\..\\",
"resource://res-test/..%5C",
"resource://res-test/..%5c",
];
// Create some fake principal that has not enough
// privileges to access any resource: uri.
var uri = NetUtil.newURI("http://www.example.com", null, null);
var principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
function get_channel(spec)
{
var ios =
Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var channelURI = NetUtil.newURI(spec, null, null);
var channel = NetUtil.newChannel({
uri: NetUtil.newURI(spec, null, null),
loadingPrincipal: principal,
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
});
try {
var channel = ios.newChannel2(spec,
null,
null,
null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_OTHER);
channel.asyncOpen2(null);
ok(false, "asyncOpen2() of URI: " + spec + "should throw");
}
catch (e) {
return;
// make sure we get the right error code in the exception
// ERROR code for NS_ERROR_DOM_BAD_URI is 1012
equal(e.code, 1012);
}
do_throw("Successfully opened invalid URI: '" + spec + "'");
try {
channel.open2();
ok(false, "Open2() of uri: " + spec + "should throw");
}
catch (e) {
// make sure we get the right error code in the exception
// ERROR code for NS_ERROR_DOM_BAD_URI is 1012
equal(e.code, 1012);
}
return channel;
}
function check_safe_resolution(spec, rootURI)
{
do_print(`Testing URL "${spec}"`);
let channel = get_channel(spec);
ok(channel.name.startsWith(rootURI), `URL resolved safely to ${channel.name}`);
ok(!/%2f/i.test(channel.name), `URL contains no escaped / characters`);
}
function check_resolution_error(spec)
{
try {
get_channel(spec);
ok(false, "Expected an error");
} catch (e) {
equal(e.result, Components.results.NS_ERROR_MALFORMED_URI,
"Expected a malformed URI error");
}
}
function run_test() {
// resource:/// and resource://gre/ are resolved specially, so we need
// to create a temporary resource package to test the standard logic
// with.
let resProto = Cc['@mozilla.org/network/protocol;1?name=resource'].getService(Ci.nsIResProtocolHandler);
let rootFile = Services.dirsvc.get("GreD", Ci.nsIFile);
let rootURI = Services.io.newFileURI(rootFile);
resProto.setSubstitution("res-test", rootURI);
do_register_cleanup(() => {
resProto.setSubstitution("res-test", null);
});
let baseRoot = resProto.resolveURI(Services.io.newURI("resource:///", null, null));
let greRoot = resProto.resolveURI(Services.io.newURI("resource://gre/", null, null));
for (var spec of specs) {
check_for_exception(spec);
check_safe_resolution(spec, rootURI.spec);
check_safe_resolution(spec.replace("res-test", ""), baseRoot);
check_safe_resolution(spec.replace("res-test", "gre"), greRoot);
}
for (var spec of error_specs) {
check_resolution_error(spec);
}
}
@@ -4,7 +4,7 @@ This test exercises the CacheFileContextEvictor::WasEvicted API and code using i
- We store 10+10 (pinned and non-pinned) entries to the cache, wait for them being written.
- Then we purge the memory pools.
- Now the IO thread is suspended on the EVICT (8) level to prevent actual deletion of the files.
- Now the IO thread is suspended on the EVICT (7) level to prevent actual deletion of the files.
- Index is disabled.
- We do clear() of the cache, this creates the "ce_*" file and posts to the EVICT level
the eviction loop mechanics.
@@ -47,9 +47,9 @@ function run_test()
// (1), here we start
log_("first set of opens");
var i;
for (i = 0; i < kENTRYCOUNT; ++i) {
log_("first set of opens");
// Callbacks 1-20
mc.add();
@@ -72,7 +72,7 @@ function run_test()
log_("after purge");
// Prevent the I/O thread from evicting physically the data. We first want to re-open the entries.
// This deterministically emulates a slow hard drive.
testingInterface.suspendCacheIOThread(8);
testingInterface.suspendCacheIOThread(7);
log_("clearing");
// Now clear everything except pinned. Stores the "ce_*" file and schedules background eviction.
@@ -96,7 +96,9 @@ function run_test()
// an early check on CacheIOThread::YieldAndRerun() in that method.
// CacheFileIOManager::OpenFileInternal should now run and CacheFileContextEvictor::WasEvicted
// should be checked on.
log_("resuming");
testingInterface.resumeCacheIOThread();
log_("resumed");
mc.fired(); // Finishes this test
}
@@ -76,11 +76,17 @@ function contentHandler_type_missing(metadata, response)
response.bodyOutputStream.write(body, body.length);
}
function contentHandler_with_package_header(metadata, response)
function contentHandler_with_package_header(chunkSize, metadata, response)
{
response.setHeader("Content-Type", 'application/package');
var body = testData.packageHeader + testData.getData();
response.bodyOutputStream.write(body, body.length);
response.bodyOutputStream.write(body.substring(0,chunkSize), chunkSize);
response.processAsync();
do_timeout(5, function() {
response.bodyOutputStream.write(body.substring(chunkSize), body.length-chunkSize);
response.finish();
});
}
var testData = {
@@ -244,7 +250,7 @@ function test_multipart_content_type_other() {
chan.asyncOpen(conv, null);
}
function test_multipart_package_header() {
function test_multipart_package_header(aChunkSize) {
var streamConv = Cc["@mozilla.org/streamConverters;1"]
.getService(Ci.nsIStreamConverterService);
@@ -253,10 +259,29 @@ function test_multipart_package_header() {
new multipartListener(testData, false, true),
null);
var chan = make_channel(uri + "/multipart5");
var chan = make_channel(uri + "/multipart5_" + aChunkSize);
chan.asyncOpen(conv, null);
}
// Bug 1212223 - Test multipart with package header and different chunk size.
// Use explict function name to make the test case log more readable.
function test_multipart_package_header_50() {
return test_multipart_package_header(50);
}
function test_multipart_package_header_100() {
return test_multipart_package_header(100);
}
function test_multipart_package_header_150() {
return test_multipart_package_header(150);
}
function test_multipart_package_header_200() {
return test_multipart_package_header(200);
}
function run_test()
{
httpserver = new HttpServer();
@@ -264,7 +289,13 @@ function run_test()
httpserver.registerPathHandler("/multipart2", contentHandler_with_boundary);
httpserver.registerPathHandler("/multipart3", contentHandler_chunked_headers);
httpserver.registerPathHandler("/multipart4", contentHandler_type_missing);
httpserver.registerPathHandler("/multipart5", contentHandler_with_package_header);
// Bug 1212223 - Test multipart with package header and different chunk size.
httpserver.registerPathHandler("/multipart5_50", contentHandler_with_package_header.bind(null, 50));
httpserver.registerPathHandler("/multipart5_100", contentHandler_with_package_header.bind(null, 100));
httpserver.registerPathHandler("/multipart5_150", contentHandler_with_package_header.bind(null, 150));
httpserver.registerPathHandler("/multipart5_200", contentHandler_with_package_header.bind(null, 200));
httpserver.start(-1);
run_next_test();
@@ -274,4 +305,9 @@ add_test(test_multipart);
add_test(test_multipart_with_boundary);
add_test(test_multipart_chunked_headers);
add_test(test_multipart_content_type_other);
add_test(test_multipart_package_header);
// Bug 1212223 - Test multipart with package header and different chunk size.
add_test(test_multipart_package_header_50);
add_test(test_multipart_package_header_100);
add_test(test_multipart_package_header_150);
add_test(test_multipart_package_header_200);
@@ -234,6 +234,8 @@ function run_test()
add_test(test_worse_package_4);
add_test(test_worse_package_5);
add_test(test_request_has_ref);
// run tests
run_next_test();
}
@@ -321,6 +323,13 @@ function test_updated_package() {
new packagedResourceListener(testData.content[0].data.replace(/\.\.\./g, 'xxx')));
}
// This tests that requested URI with reference should still work.
function test_request_has_ref() {
packagePath = "/package";
let url = uri + packagePath + "!//index.html#Ref";
paservice.getResource(getChannelForURL(url), cacheListener);
}
// ----------------------------------------------------------------------------
// This listener checks that the requested resources are not returned
@@ -0,0 +1,43 @@
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
function is_app_offline(appId) {
let ioservice = Cc['@mozilla.org/network/io-service;1'].
getService(Ci.nsIIOService);
return ioservice.isAppOffline(appId);
}
var events_observed_no = 0;
// Holds the last observed app-offline event
var info = null;
function observer(aSubject, aTopic, aData) {
events_observed_no++;
info = aSubject.QueryInterface(Ci.nsIAppOfflineInfo);
dump("ChildObserver - subject: {" + aSubject.appId + ", " + aSubject.mode + "} ");
}
// Add observer for the app-offline notification
function run_test() {
Services.obs.addObserver(observer, "network:app-offline-status-changed", false);
}
// Chech that the app has the proper offline status
function check_status(appId, status)
{
do_check_eq(is_app_offline(appId), status == Ci.nsIAppOfflineInfo.OFFLINE);
}
// Check that the app has the proper offline status
// and that the correct notification has been received
function check_notification_and_status(appId, status) {
do_check_eq(info.appId, appId);
do_check_eq(info.mode, status);
do_check_eq(is_app_offline(appId), status == Ci.nsIAppOfflineInfo.OFFLINE);
}
// Remove the observer from the child process
function finished() {
Services.obs.removeObserver(observer, "network:app-offline-status-changed");
do_check_eq(events_observed_no, 2);
}
@@ -0,0 +1,102 @@
// Checks that app-offline notifications are received in both the parent
// and the child process, and that after receiving the notification
// isAppOffline returns the correct value
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var test_index = 0;
var APP_ID = 42;
var events_observed_no = 0;
function set_app_offline(appId, offline) {
let ioservice = Cc['@mozilla.org/network/io-service;1'].
getService(Ci.nsIIOService);
ioservice.setAppOffline(appId, offline);
}
function is_app_offline(appId) {
let ioservice = Cc['@mozilla.org/network/io-service;1'].
getService(Ci.nsIIOService);
return ioservice.isAppOffline(appId);
}
// The expected offline status after running each function,
// and the next function that should be run.
// test0 test1 test2 test3 test4
let expected_offline = [ false, true, true, false, false];
let callbacks = [ test1, test2, test3, test4, finished];
function observer(aSubject, aTopic, aData) {
events_observed_no++;
let info = aSubject.QueryInterface(Ci.nsIAppOfflineInfo);
dump("ParentObserver - subject: {" + aSubject.appId + ", " + aSubject.mode + "} " +
"topic: " + aTopic + "\n");
// Check that the correct offline status is in place
do_check_eq(is_app_offline(APP_ID), expected_offline[test_index]);
// Execute the callback for the current test
do_execute_soon(callbacks[test_index]);
}
function run_test() {
Services.obs.addObserver(observer, "network:app-offline-status-changed", false);
test_index = 0;
do_check_eq(is_app_offline(APP_ID), expected_offline[test_index]) // The app should be online at first
run_test_in_child("child_app_offline_notifications.js", test0);
}
// Check that the app is online by default in the child
function test0() {
dump("parent: RUNNING: test0\n");
test_index = 0;
sendCommand('check_status('+APP_ID+','+Ci.nsIAppOfflineInfo.ONLINE+');\n', test1);
}
// Set the app OFFLINE
// Check that the notification is emmited in the parent process
// The observer function will execute test2 which does the check in the child
function test1() {
dump("parent: RUNNING: test1\n");
test_index = 1;
set_app_offline(APP_ID, Ci.nsIAppOfflineInfo.OFFLINE);
}
// Checks that child process sees the app OFFLINE
function test2() {
dump("parent: RUNNING: test2\n");
test_index = 2;
sendCommand('check_notification_and_status('+APP_ID+','+Ci.nsIAppOfflineInfo.OFFLINE+');\n', test3);
}
// Set the app ONLINE
// Chech that the notification is received in the parent
// The observer function will execute test3 and do the check in the child
function test3() {
dump("parent: RUNNING: test3\n");
test_index = 3;
set_app_offline(APP_ID, Ci.nsIAppOfflineInfo.ONLINE);
}
// Chech that the app is back online
function test4() {
dump("parent: RUNNING: test4\n");
test_index = 4;
sendCommand('check_notification_and_status('+APP_ID+','+Ci.nsIAppOfflineInfo.ONLINE+');\n', function() {
// Send command to unregister observer on the child
sendCommand('finished();\n', finished);
});
}
// Remove observer and end test
function finished() {
dump("parent: RUNNING: finished\n");
Services.obs.removeObserver(observer, "network:app-offline-status-changed");
do_check_eq(events_observed_no, 2);
do_test_finished();
}
+1
View File
@@ -92,3 +92,4 @@ skip-if = true
[test_reply_without_content_type_wrap.js]
[test_app_offline_http.js]
[test_getHost_wrap.js]
[test_app_offline_notifications.js]
+3
View File
@@ -991,6 +991,8 @@ nsChildView::BackingScaleFactorChanged()
}
mBackingScaleFactor = newScale;
NSRect frame = [mView frame];
mBounds = nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, newScale);
if (mWidgetListener && !mWidgetListener->GetXULWindow()) {
nsIPresShell* presShell = mWidgetListener->GetPresShell();
@@ -2968,6 +2970,7 @@ RectTextureImage::EndUpdate(bool aKeepSurface)
LayoutDeviceIntRegion updateRegion = mUpdateRegion;
if (mTextureSize != mBufferSize) {
mTextureSize = mBufferSize;
needInit = true;
}
if (needInit || !CanUploadSubtextures()) {
+2 -2
View File
@@ -169,7 +169,7 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor)
aColor = GetColorFromNSColor([NSColor gridColor]);
break;
case eColorID_activeborder:
aColor = NS_RGB(0x00,0x00,0x00);
aColor = GetColorFromNSColor([NSColor keyboardFocusIndicatorColor]);
break;
case eColorID_appworkspace:
aColor = NS_RGB(0xFF,0xFF,0xFF);
@@ -601,4 +601,4 @@ nsLookAndFeel::RefreshImpl()
mUseOverlayScrollbarsCached = false;
mAllowOverlayScrollbarsOverlapCached = false;
}
}
}
+1 -1
View File
@@ -226,7 +226,7 @@ NativeKeyBindings::GetInstance(NativeKeyBindingsType aType)
default:
// fallback to multiline editor case in release build
MOZ_ASSERT(false, "aType is invalid or not yet implemented");
MOZ_FALLTHROUGH_ASSERT("aType is invalid or not yet implemented");
case nsIWidget::NativeKeyBindingsForMultiLineEditor:
case nsIWidget::NativeKeyBindingsForRichTextEditor:
if (!sInstanceForMultiLineEditor) {
+2
View File
@@ -83,6 +83,8 @@ GetGradientColors(const GValue* aValue,
return false;
auto pattern = static_cast<cairo_pattern_t*>(g_value_get_boxed(aValue));
if (!pattern)
return false;
// Just picking the lightest and darkest colors as simple samples rather
// than trying to blend, which could get messy if there are many stops.
+108 -55
View File
@@ -1038,6 +1038,13 @@ void nsXULWindow::OnChromeLoaded()
ApplyChromeFlags();
SyncAttributesToWidget();
int32_t specWidth = -1, specHeight = -1;
bool gotSize = false;
if (!mIgnoreXULSize) {
gotSize = LoadSizeFromXUL(specWidth, specHeight);
}
bool positionSet = !mIgnoreXULPosition;
nsCOMPtr<nsIXULWindow> parentWindow(do_QueryReferent(mParentWindow));
#if defined(XP_UNIX) && !defined(XP_MACOSX)
@@ -1047,11 +1054,18 @@ void nsXULWindow::OnChromeLoaded()
if (!parentWindow)
positionSet = false;
#endif
if (positionSet)
positionSet = LoadPositionFromXUL();
if (positionSet) {
// We have to do this before sizing the window, because sizing depends
// on the resolution of the screen we're on. But positioning needs to
// know the size so that it can constrain to screen bounds.... as an
// initial guess here, we'll use the specified size (if any).
positionSet = LoadPositionFromXUL(specWidth, specHeight);
}
if (gotSize) {
SetSpecifiedSize(specWidth, specHeight);
}
if (!mIgnoreXULSize)
LoadSizeFromXUL();
if (mIntrinsicallySized) {
// (if LoadSizeFromXUL set the size, mIntrinsicallySized will be false)
nsCOMPtr<nsIContentViewer> cv;
@@ -1066,15 +1080,25 @@ void nsXULWindow::OnChromeLoaded()
int32_t width = 0, height = 0;
if (NS_SUCCEEDED(cv->GetContentSize(&width, &height))) {
treeOwner->SizeShellTo(docShellAsItem, width, height);
// Update specified size for the final LoadPositionFromXUL call.
specWidth = width;
specHeight = height;
}
}
}
}
// Now that we have set the window's final size, we can re-do its
// positioning so that it is properly constrained to the screen.
if (positionSet) {
LoadPositionFromXUL(specWidth, specHeight);
}
LoadMiscPersistentAttributesFromXUL();
if (mCenterAfterLoad && !positionSet)
if (mCenterAfterLoad && !positionSet) {
Center(parentWindow, parentWindow ? false : true, false);
}
if (mShowAfterLoad) {
SetVisibility(true);
@@ -1085,7 +1109,10 @@ void nsXULWindow::OnChromeLoaded()
mPersistentAttributesMask |= PAD_POSITION | PAD_SIZE | PAD_MISC;
}
bool nsXULWindow::LoadPositionFromXUL()
// If aSpecWidth and/or aSpecHeight are > 0, we will use these CSS px sizes
// to fit to the screen when staggering windows; if they're negative,
// we use the window's current size instead.
bool nsXULWindow::LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight)
{
bool gotPosition = false;
@@ -1108,11 +1135,16 @@ bool nsXULWindow::LoadPositionFromXUL()
// Convert to global display pixels for consistent window management across
// screens with diverse resolutions
double scale = mWindow->GetDesktopToDeviceScale().scale;
currX = NSToIntRound(currX / scale);
currY = NSToIntRound(currY / scale);
currWidth = NSToIntRound(currWidth / scale);
currHeight = NSToIntRound(currHeight / scale);
double devToDesktopScale = 1.0 / mWindow->GetDesktopToDeviceScale().scale;
currX = NSToIntRound(currX * devToDesktopScale);
currY = NSToIntRound(currY * devToDesktopScale);
// For size, use specified value if > 0, else current value
double devToCSSScale = 1.0 / mWindow->GetDefaultScale().scale;
int32_t cssWidth =
aSpecWidth > 0 ? aSpecWidth : NSToIntRound(currWidth * devToCSSScale);
int32_t cssHeight =
aSpecHeight > 0 ? aSpecHeight : NSToIntRound(currHeight * devToCSSScale);
// Obtain the position information from the <xul:window> element.
int32_t specX = currX;
@@ -1148,7 +1180,7 @@ bool nsXULWindow::LoadPositionFromXUL()
}
}
else {
StaggerPosition(specX, specY, currWidth, currHeight);
StaggerPosition(specX, specY, cssWidth, cssHeight);
}
}
mWindow->ConstrainPosition(false, &specX, &specY);
@@ -1159,76 +1191,81 @@ bool nsXULWindow::LoadPositionFromXUL()
return gotPosition;
}
bool nsXULWindow::LoadSizeFromXUL()
bool
nsXULWindow::LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight)
{
bool gotSize = false;
// if we're the hidden window, don't try to validate our size/position. We're
// special.
if (mIsHiddenWindow)
if (mIsHiddenWindow) {
return false;
}
nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
NS_ENSURE_TRUE(windowElement, false);
int32_t currWidth = 0;
int32_t currHeight = 0;
nsresult errorCode;
int32_t temp;
NS_ASSERTION(mWindow, "we expected to have a window already");
GetSize(&currWidth, &currHeight);
double displayToDevPx =
mWindow ? mWindow->GetDesktopToDeviceScale().scale : 1.0;
double cssToDevPx = mWindow ? mWindow->GetDefaultScale().scale : 1.0;
currWidth = NSToIntRound(currWidth * displayToDevPx / cssToDevPx);
currHeight = NSToIntRound(currHeight * displayToDevPx / cssToDevPx);
// Obtain the position and sizing information from the <xul:window> element.
int32_t specWidth = currWidth;
int32_t specHeight = currHeight;
// Obtain the sizing information from the <xul:window> element.
aSpecWidth = 100;
aSpecHeight = 100;
nsAutoString sizeString;
windowElement->GetAttribute(WIDTH_ATTRIBUTE, sizeString);
temp = sizeString.ToInteger(&errorCode);
if (NS_SUCCEEDED(errorCode) && temp > 0) {
specWidth = std::max(temp, 100);
aSpecWidth = std::max(temp, 100);
gotSize = true;
}
windowElement->GetAttribute(HEIGHT_ATTRIBUTE, sizeString);
temp = sizeString.ToInteger(&errorCode);
if (NS_SUCCEEDED(errorCode) && temp > 0) {
specHeight = std::max(temp, 100);
aSpecHeight = std::max(temp, 100);
gotSize = true;
}
if (gotSize) {
// constrain to screen size
nsCOMPtr<mozIDOMWindowProxy> domWindow;
GetWindowDOMWindow(getter_AddRefs(domWindow));
if (domWindow) {
auto* window = nsPIDOMWindowOuter::From(domWindow);
nsCOMPtr<nsIDOMScreen> screen = window->GetScreen();
if (screen) {
int32_t screenWidth;
int32_t screenHeight;
screen->GetAvailWidth(&screenWidth); // CSS pixels
screen->GetAvailHeight(&screenHeight);
if (specWidth > screenWidth)
specWidth = screenWidth;
if (specHeight > screenHeight)
specHeight = screenHeight;
}
}
return gotSize;
}
mIntrinsicallySized = false;
if (specWidth != currWidth || specHeight != currHeight) {
SetSize(specWidth * cssToDevPx, specHeight * cssToDevPx, false);
void
nsXULWindow::SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight)
{
// constrain to screen size
nsCOMPtr<mozIDOMWindowProxy> domWindow;
GetWindowDOMWindow(getter_AddRefs(domWindow));
if (domWindow) {
auto* window = nsPIDOMWindowOuter::From(domWindow);
nsCOMPtr<nsIDOMScreen> screen = window->GetScreen();
if (screen) {
int32_t screenWidth;
int32_t screenHeight;
screen->GetAvailWidth(&screenWidth); // CSS pixels
screen->GetAvailHeight(&screenHeight);
if (aSpecWidth > screenWidth) {
aSpecWidth = screenWidth;
}
if (aSpecHeight > screenHeight) {
aSpecHeight = screenHeight;
}
}
}
return gotSize;
NS_ASSERTION(mWindow, "we expected to have a window already");
int32_t currWidth = 0;
int32_t currHeight = 0;
GetSize(&currWidth, &currHeight); // returns device pixels
// convert specified values to device pixels, and resize if needed
double cssToDevPx = mWindow ? mWindow->GetDefaultScale().scale : 1.0;
aSpecWidth = NSToIntRound(aSpecWidth * cssToDevPx);
aSpecHeight = NSToIntRound(aSpecHeight * cssToDevPx);
mIntrinsicallySized = false;
if (aSpecWidth != currWidth || aSpecHeight != currHeight) {
SetSize(aSpecWidth, aSpecHeight, false);
}
}
/* Miscellaneous persistent attributes are attributes named in the
@@ -1315,12 +1352,17 @@ bool nsXULWindow::LoadMiscPersistentAttributesFromXUL()
This code does have a scary double loop -- it'll keep passing through
the entire list of open windows until it finds a non-collision. Doesn't
seem to be a problem, but it deserves watching.
The aRequested{X,Y} parameters here are in desktop pixels;
the aSpec{Width,Height} parameters are CSS pixel dimensions.
*/
void nsXULWindow::StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
int32_t aSpecWidth, int32_t aSpecHeight)
{
const int32_t kOffset = 22;
const uint32_t kSlop = 4;
// These "constants" will be converted from CSS to desktop pixels
// for the appropriate screen, assuming we find a screen to use...
// hence they're not actually declared const here.
int32_t kOffset = 22;
uint32_t kSlop = 4;
bool keepTrying;
int bouncedX = 0, // bounced off vertical edge of screen
@@ -1361,6 +1403,17 @@ void nsXULWindow::StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
&screenWidth, &screenHeight);
screenBottom = screenTop + screenHeight;
screenRight = screenLeft + screenWidth;
// Get the screen's scaling factors and convert staggering constants
// from CSS px to desktop pixel units
double desktopToDeviceScale = 1.0, cssToDeviceScale = 1.0;
ourScreen->GetContentsScaleFactor(&desktopToDeviceScale);
ourScreen->GetDefaultCSSScaleFactor(&cssToDeviceScale);
double cssToDesktopFactor = cssToDeviceScale / desktopToDeviceScale;
kOffset = NSToIntRound(kOffset * cssToDesktopFactor);
kSlop = NSToIntRound(kSlop * cssToDesktopFactor);
// Convert dimensions from CSS to desktop pixels
aSpecWidth = NSToIntRound(aSpecWidth * cssToDesktopFactor);
aSpecHeight = NSToIntRound(aSpecHeight * cssToDesktopFactor);
gotScreen = true;
}
}
@@ -1391,7 +1444,7 @@ void nsXULWindow::StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
nsCOMPtr<nsIBaseWindow> listBaseWindow(do_QueryInterface(supportsWindow));
listBaseWindow->GetPosition(&listX, &listY);
double scale;
if (NS_SUCCEEDED(listBaseWindow->GetUnscaledDevicePixelsPerCSSPixel(&scale))) {
if (NS_SUCCEEDED(listBaseWindow->GetDevicePixelsPerDesktopPixel(&scale))) {
listX = NSToIntRound(listX / scale);
listY = NSToIntRound(listY / scale);
}
+3 -2
View File
@@ -93,8 +93,9 @@ protected:
void OnChromeLoaded();
void StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
int32_t aSpecWidth, int32_t aSpecHeight);
bool LoadPositionFromXUL();
bool LoadSizeFromXUL();
bool LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight);
bool LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight);
void SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight);
bool LoadMiscPersistentAttributesFromXUL();
void SyncAttributesToWidget();
NS_IMETHOD SavePersistentAttributes();