mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 05:37:11 +00:00
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:
@@ -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
@@ -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");
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -157,8 +157,9 @@ already_AddRefed<nsIEventTarget> CacheIOThread::Target()
|
||||
if (!target && mThread)
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
if (!mXPCOMThread)
|
||||
while (!mXPCOMThread) {
|
||||
lock.Wait();
|
||||
}
|
||||
|
||||
target = mXPCOMThread;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
READ,
|
||||
MANAGEMENT,
|
||||
WRITE,
|
||||
CLOSE,
|
||||
CLOSE = WRITE,
|
||||
INDEX,
|
||||
EVICT,
|
||||
LAST_LEVEL,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
};
|
||||
@@ -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)
|
||||
};
|
||||
},
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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___ */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user