Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2016-07-07 11:44:06 +02:00
commit 486d248cca
88 changed files with 2429 additions and 544 deletions

View File

@ -523,6 +523,10 @@ toolbar:not(#TabsToolbar) > #personal-bookmarks {
pointer-events: none;
}
#urlbar[pageproxystate="invalid"] > #identity-box > #notification-popup-box {
pointer-events: auto;
}
#identity-icon-labels {
max-width: 18em;
}

View File

@ -2252,6 +2252,26 @@ function BrowserViewSourceOfDocument(aArgsOrDocument) {
let inTab = Services.prefs.getBoolPref("view_source.tab");
if (inTab) {
let tabBrowser = gBrowser;
let forceNotRemote = false;
if (!tabBrowser) {
if (!args.browser) {
throw new Error("BrowserViewSourceOfDocument should be passed the " +
"subject browser if called from a window without " +
"gBrowser defined.");
}
forceNotRemote = !args.browser.isRemoteBrowser;
} else {
// Some internal URLs (such as specific chrome: and about: URLs that are
// not yet remote ready) cannot be loaded in a remote browser. View
// source in tab expects the new view source browser's remoteness to match
// that of the original URL, so disable remoteness if necessary for this
// URL.
let contentProcess = Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
forceNotRemote =
gMultiProcessBrowser &&
!E10SUtils.canLoadURIInProcess(args.URL, contentProcess)
}
// In the case of sidebars and chat windows, gBrowser is defined but null,
// because no #content element exists. For these cases, we need to find
// the most recent browser window.
@ -2261,15 +2281,7 @@ function BrowserViewSourceOfDocument(aArgsOrDocument) {
let browserWindow = RecentWindow.getMostRecentBrowserWindow();
tabBrowser = browserWindow.gBrowser;
}
// Some internal URLs (such as specific chrome: and about: URLs that are
// not yet remote ready) cannot be loaded in a remote browser. View
// source in tab expects the new view source browser's remoteness to match
// that of the original URL, so disable remoteness if necessary for this
// URL.
let contentProcess = Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
let forceNotRemote =
gMultiProcessBrowser &&
!E10SUtils.canLoadURIInProcess(args.URL, contentProcess);
// `viewSourceInBrowser` will load the source content from the page
// descriptor for the tab (when possible) or fallback to the network if
// that fails. Either way, the view source module will manage the tab's
@ -2322,7 +2334,8 @@ function BrowserViewSource(browser) {
// initialTab - name of the initial tab to display, or null for the first tab
// imageElement - image to load in the Media Tab of the Page Info window; can be null/omitted
// frameOuterWindowID - the id of the frame that the context menu opened in; can be null/omitted
function BrowserPageInfo(documentURL, initialTab, imageElement, frameOuterWindowID) {
// browser - the browser containing the document we're interested in inspecting; can be null/omitted
function BrowserPageInfo(documentURL, initialTab, imageElement, frameOuterWindowID, browser) {
if (documentURL instanceof HTMLDocument) {
Deprecated.warning("Please pass the location URL instead of the document " +
"to BrowserPageInfo() as the first argument.",
@ -2330,7 +2343,7 @@ function BrowserPageInfo(documentURL, initialTab, imageElement, frameOuterWindow
documentURL = documentURL.location;
}
let args = { initialTab, imageElement, frameOuterWindowID };
let args = { initialTab, imageElement, frameOuterWindowID, browser };
var windows = Services.wm.getEnumerator("Browser:page-info");
documentURL = documentURL || window.gBrowser.selectedBrowser.currentURI.spec;

View File

@ -714,46 +714,6 @@
pageproxystate="invalid"
onfocus="document.getElementById('identity-box').style.MozUserFocus= 'normal'"
onblur="setTimeout(() => { document.getElementById('identity-box').style.MozUserFocus = ''; }, 0);">
<box id="notification-popup-box" hidden="true" align="center">
<image id="default-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.defaultNotificationAnchor.label;"/>
<image id="geo-notification-icon" class="notification-anchor-icon geo-icon" role="button"
aria-label="&urlbar.geolocationNotificationAnchor.label;"/>
<image id="addons-notification-icon" class="notification-anchor-icon install-icon" role="button"
aria-label="&urlbar.addonsNotificationAnchor.label;"/>
<image id="indexedDB-notification-icon" class="notification-anchor-icon indexedDB-icon" role="button"
aria-label="&urlbar.indexedDBNotificationAnchor.label;"/>
<image id="login-fill-notification-icon" class="notification-anchor-icon login-icon" role="button"
aria-label="&urlbar.loginFillNotificationAnchor.label;"/>
<image id="password-notification-icon" class="notification-anchor-icon login-icon" role="button"
aria-label="&urlbar.passwordNotificationAnchor.label;"/>
<image id="plugins-notification-icon" class="notification-anchor-icon plugin-icon" role="button"
aria-label="&urlbar.pluginsNotificationAnchor.label;"/>
<image id="web-notifications-notification-icon" class="notification-anchor-icon desktop-notification-icon" role="button"
aria-label="&urlbar.webNotsNotificationAnchor3.label;"/>
<image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon camera-icon" role="button"
aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
<image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon camera-icon in-use" role="button"
aria-label="&urlbar.webRTCSharingDevicesNotificationAnchor.label;"/>
<image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon microphone-icon" role="button"
aria-label="&urlbar.webRTCShareMicrophoneNotificationAnchor.label;"/>
<image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon microphone-icon in-use" role="button"
aria-label="&urlbar.webRTCSharingMicrophoneNotificationAnchor.label;"/>
<image id="webRTC-shareScreen-notification-icon" class="notification-anchor-icon screen-icon" role="button"
aria-label="&urlbar.webRTCShareScreenNotificationAnchor.label;"/>
<image id="webRTC-sharingScreen-notification-icon" class="notification-anchor-icon screen-icon in-use" role="button"
aria-label="&urlbar.webRTCSharingScreenNotificationAnchor.label;"/>
<image id="pointerLock-notification-icon" class="notification-anchor-icon pointerLock-icon" role="button"
aria-label="&urlbar.pointerLockNotificationAnchor.label;"/>
<image id="servicesInstall-notification-icon" class="notification-anchor-icon service-icon" role="button"
aria-label="&urlbar.servicesNotificationAnchor.label;"/>
<image id="translate-notification-icon" class="notification-anchor-icon translation-icon" role="button"
aria-label="&urlbar.translateNotificationAnchor.label;"/>
<image id="translated-notification-icon" class="notification-anchor-icon translation-icon in-use" role="button"
aria-label="&urlbar.translatedNotificationAnchor.label;"/>
<image id="eme-notification-icon" class="notification-anchor-icon drm-icon" role="button"
aria-label="&urlbar.emeNotificationAnchor.label;"/>
</box>
<!-- Use onclick instead of normal popup= syntax since the popup
code fires onmousedown, and hence eats our favicon drag events.
We only add the identity-box button to the tab order when the location bar
@ -767,6 +727,51 @@
<image id="identity-icon"
consumeanchor="identity-box"
onclick="PageProxyClickHandler(event);"/>
<box id="notification-popup-box"
hidden="true"
tooltiptext=""
onmouseover="document.getElementById('identity-icon').classList.add('no-hover');"
onmouseout="document.getElementById('identity-icon').classList.remove('no-hover');"
align="center">
<image id="default-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.defaultNotificationAnchor.label;"/>
<image id="geo-notification-icon" class="notification-anchor-icon geo-icon" role="button"
aria-label="&urlbar.geolocationNotificationAnchor.label;"/>
<image id="addons-notification-icon" class="notification-anchor-icon install-icon" role="button"
aria-label="&urlbar.addonsNotificationAnchor.label;"/>
<image id="indexedDB-notification-icon" class="notification-anchor-icon indexedDB-icon" role="button"
aria-label="&urlbar.indexedDBNotificationAnchor.label;"/>
<image id="login-fill-notification-icon" class="notification-anchor-icon login-icon" role="button"
aria-label="&urlbar.loginFillNotificationAnchor.label;"/>
<image id="password-notification-icon" class="notification-anchor-icon login-icon" role="button"
aria-label="&urlbar.passwordNotificationAnchor.label;"/>
<image id="plugins-notification-icon" class="notification-anchor-icon plugin-icon" role="button"
aria-label="&urlbar.pluginsNotificationAnchor.label;"/>
<image id="web-notifications-notification-icon" class="notification-anchor-icon desktop-notification-icon" role="button"
aria-label="&urlbar.webNotsNotificationAnchor3.label;"/>
<image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon camera-icon" role="button"
aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
<image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon camera-icon in-use" role="button"
aria-label="&urlbar.webRTCSharingDevicesNotificationAnchor.label;"/>
<image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon microphone-icon" role="button"
aria-label="&urlbar.webRTCShareMicrophoneNotificationAnchor.label;"/>
<image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon microphone-icon in-use" role="button"
aria-label="&urlbar.webRTCSharingMicrophoneNotificationAnchor.label;"/>
<image id="webRTC-shareScreen-notification-icon" class="notification-anchor-icon screen-icon" role="button"
aria-label="&urlbar.webRTCShareScreenNotificationAnchor.label;"/>
<image id="webRTC-sharingScreen-notification-icon" class="notification-anchor-icon screen-icon in-use" role="button"
aria-label="&urlbar.webRTCSharingScreenNotificationAnchor.label;"/>
<image id="pointerLock-notification-icon" class="notification-anchor-icon pointerLock-icon" role="button"
aria-label="&urlbar.pointerLockNotificationAnchor.label;"/>
<image id="servicesInstall-notification-icon" class="notification-anchor-icon service-icon" role="button"
aria-label="&urlbar.servicesNotificationAnchor.label;"/>
<image id="translate-notification-icon" class="notification-anchor-icon translation-icon" role="button"
aria-label="&urlbar.translateNotificationAnchor.label;"/>
<image id="translated-notification-icon" class="notification-anchor-icon translation-icon in-use" role="button"
aria-label="&urlbar.translatedNotificationAnchor.label;"/>
<image id="eme-notification-icon" class="notification-anchor-icon drm-icon" role="button"
aria-label="&urlbar.emeNotificationAnchor.label;"/>
</box>
<image id="tracking-protection-icon"/>
<image id="connection-icon"/>
<hbox id="identity-icon-labels">

View File

@ -1110,14 +1110,12 @@ nsContextMenu.prototype = {
},
viewInfo: function() {
BrowserPageInfo();
BrowserPageInfo(gContextMenuContentData.docLocation, null, null, null, this.browser);
},
viewImageInfo: function() {
// Don't need to pass in ownerDocument.defaultView.top.document here;
// window.gBrowser.selectedBrowser.currentURI.spec does the job without
// using CPOWs
BrowserPageInfo(null, "mediaTab", this.target);
BrowserPageInfo(gContextMenuContentData.docLocation, "mediaTab",
this.target, null, this.browser);
},
viewImageDesc: function(e) {
@ -1130,7 +1128,7 @@ nsContextMenu.prototype = {
viewFrameInfo: function() {
BrowserPageInfo(gContextMenuContentData.docLocation, null, null,
this.frameOuterWindowID);
this.frameOuterWindowID, this.browser);
},
reloadImage: function() {

View File

@ -350,9 +350,10 @@ function onLoadPageInfo()
.notifyObservers(window, "page-info-dialog-loaded", null);
}
function loadPageInfo(frameOuterWindowID, imageElement)
function loadPageInfo(frameOuterWindowID, imageElement, browser)
{
let mm = window.opener.gBrowser.selectedBrowser.messageManager;
browser = browser || window.opener.gBrowser.selectedBrowser;
let mm = browser.messageManager;
gStrings["application/rss+xml"] = gBundle.getString("feedRss");
gStrings["application/atom+xml"] = gBundle.getString("feedAtom");
@ -486,11 +487,11 @@ function loadTab(args)
// If the "View Image Info" context menu item was used, the related image
// element is provided as an argument. This can't be a background image.
let imageElement = args && args.imageElement;
let frameOuterWindowID = args && args.frameOuterWindowID;
let browser = args && args.browser;
/* Load the page info */
loadPageInfo(frameOuterWindowID, imageElement);
loadPageInfo(frameOuterWindowID, imageElement, browser);
var initialTab = (args && args.initialTab) || "generalTab";
var radioGroup = document.getElementById("viewGroup");

View File

@ -9,7 +9,3 @@
<svg:clipPath id="tab-curve-clip-path-end" clipPathUnits="objectBoundingBox">
<svg:path d="m 0,0.0625 -0.05,0 0,0.938 1,0 0,-0.028 C 0.67917542,0.95840561 0.56569036,0.81970962 0.51599998,0.5625 0.48279998,0.3905 0.465,0.0659 0,0.0625 z"/>
</svg:clipPath>
<svg:clipPath id="tab-hover-clip-path" clipPathUnits="objectBoundingBox">
<svg:path d="M 0,0.2 0,1 1,1, 1,0.2 z"/>
</svg:clipPath>

Before

Width:  |  Height:  |  Size: 865 B

After

Width:  |  Height:  |  Size: 731 B

View File

@ -1765,12 +1765,12 @@
<body>
<![CDATA[
// Supported parameters:
// userContextId, remote, isPreloadBrowser, uriIsAboutBlank
// userContextId, remote, isPreloadBrowser, uriIsAboutBlank, permanentKey
const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let b = document.createElementNS(NS_XUL, "browser");
b.permanentKey = {};
b.permanentKey = aParams.permanentKey || {};
b.setAttribute("type", "content-targetable");
b.setAttribute("message", "true");
b.setAttribute("messagemanagergroup", "browsers");
@ -1865,12 +1865,16 @@
!aParams.userContextId &&
!PrivateBrowsingUtils.isWindowPrivate(window)) {
browser = this._getPreloadedBrowser();
usingPreloadedContent = !!browser;
if (browser) {
usingPreloadedContent = true;
aTab.permanentKey = browser.permanentKey;
}
}
if (!browser) {
// No preloaded browser found, create one.
browser = this._createBrowser({remote: remote,
browser = this._createBrowser({permanentKey: aTab.permanentKey,
remote: remote,
uriIsAboutBlank: uriIsAboutBlank,
userContextId: aParams.userContextId});
}
@ -2013,6 +2017,7 @@
var position = this.tabs.length - 1;
t._tPos = position;
t.lastAccessed = Date.now();
t.permanentKey = {};
this.tabContainer._setPositionalAttributes();
this.tabContainer.updateVisibility();
@ -4454,6 +4459,7 @@
var uniqueId = this._generateUniquePanelID();
this.mPanelContainer.childNodes[0].id = uniqueId;
this.mCurrentTab.linkedPanel = uniqueId;
this.mCurrentTab.permanentKey = this.mCurrentBrowser.permanentKey;
this.mCurrentTab._tPos = 0;
this.mCurrentTab._fullyOpen = true;
this.mCurrentTab.lastAccessed = Infinity;

View File

@ -238,5 +238,29 @@ var tests = [
ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
window.locationbar.visible = true;
}
},
// Test that dismissed popupnotifications can be opened on about:blank
// (where the rest of the identity block is disabled)
{ id: "Test#11",
run: function() {
this.oldSelectedTab = gBrowser.selectedTab;
gBrowser.selectedTab = gBrowser.addTab("about:blank");
this.notifyObj = new BasicNotification(this.id);
this.notifyObj.anchorID = "geo-notification-icon";
this.notifyObj.addOptions({dismissed: true});
this.notification = showNotification(this.notifyObj);
EventUtils.synthesizeMouse(document.getElementById("geo-notification-icon"), 0, 0, {});
},
onShown: function(popup) {
checkPopup(popup, this.notifyObj);
dismissNotification(popup);
},
onHidden: function(popup) {
this.notification.remove();
gBrowser.removeTab(gBrowser.selectedTab);
gBrowser.selectedTab = this.oldSelectedTab;
}
}
];

View File

@ -47,10 +47,11 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
Cu.import("resource://gre/modules/PageThumbs.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
// Pref to enable/disable preview-per-tab
const TOGGLE_PREF_NAME = "browser.taskbar.previews.enable";
@ -66,12 +67,11 @@ const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
XPCOMUtils.defineLazyServiceGetter(this, "imgTools",
"@mozilla.org/image/tools;1",
"imgITools");
XPCOMUtils.defineLazyServiceGetter(this, "faviconSvc",
"@mozilla.org/browser/favicon-service;1",
"nsIFaviconService");
XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
"resource://gre/modules/PageThumbs.jsm");
// nsIURI -> imgIContainer
function _imageFromURI(doc, uri, privateMode, callback) {
function _imageFromURI(uri, privateMode, callback) {
let channel = NetUtil.newChannel({
uri: uri,
loadUsingSystemPrincipal: true,
@ -94,19 +94,20 @@ function _imageFromURI(doc, uri, privateMode, callback) {
} catch (e) {
// We failed, so use the default favicon (only if this wasn't the default
// favicon).
let defaultURI = faviconSvc.defaultFavicon;
let defaultURI = PlacesUtils.favicons.defaultFavicon;
if (!defaultURI.equals(uri))
_imageFromURI(doc, defaultURI, privateMode, callback);
_imageFromURI(defaultURI, privateMode, callback);
}
});
}
// string? -> imgIContainer
function getFaviconAsImage(doc, iconurl, privateMode, callback) {
if (iconurl)
_imageFromURI(doc, NetUtil.newURI(iconurl), privateMode, callback);
else
_imageFromURI(doc, faviconSvc.defaultFavicon, privateMode, callback);
function getFaviconAsImage(iconurl, privateMode, callback) {
if (iconurl) {
_imageFromURI(NetUtil.newURI(iconurl), privateMode, callback);
} else {
_imageFromURI(PlacesUtils.favicons.defaultFavicon, privateMode, callback);
}
}
// Snaps the given rectangle to be pixel-aligned at the given scale
@ -451,7 +452,6 @@ TabWindow.prototype = {
preview.active = this.tabbrowser.selectedTab == controller.tab;
// Grab the default favicon
getFaviconAsImage(
null,
null,
PrivateBrowsingUtils.isWindowPrivate(this.win),
function (img) {
@ -606,20 +606,41 @@ TabWindow.prototype = {
}
},
directRequestProtocols: new Set([
"file", "chrome", "resource", "about"
]),
onLinkIconAvailable: function (aBrowser, aIconURL) {
let self = this;
if (!aIconURL) {
return;
}
let tab = this.tabbrowser.getTabForBrowser(aBrowser);
let shouldRequestFaviconURL = true;
try {
urlObject = NetUtil.newURI(aIconURL);
shouldRequestFaviconURL =
!this.directRequestProtocols.has(urlObject.scheme);
} catch (ex) {}
let requestURL = shouldRequestFaviconURL ?
"moz-anno:favicon:" + aIconURL :
aIconURL;
getFaviconAsImage(
null,
aIconURL,
requestURL,
PrivateBrowsingUtils.isWindowPrivate(this.win),
function (img) {
let index = self.tabbrowser.browsers.indexOf(aBrowser);
// Only add it if we've found the index. The tab could have closed!
img => {
let index = this.tabbrowser.browsers.indexOf(aBrowser);
// Only add it if we've found the index and the URI is still the same.
// The tab could have closed, and there's no guarantee the icons
// will have finished fetching 'in order'.
if (index != -1) {
let tab = self.tabbrowser.tabs[index];
self.previews.get(tab).icon = img;
let tab = this.tabbrowser.tabs[index];
if (tab.getAttribute("image") == aIconURL) {
this.previews.get(tab).icon = img;
}
}
});
}
);
}
}
@ -663,6 +684,7 @@ this.AeroPeek = {
this.prefs.addObserver(TOGGLE_PREF_NAME, this, false);
this.prefs.addObserver(DISABLE_THRESHOLD_PREF_NAME, this, false);
this.prefs.addObserver(CACHE_EXPIRATION_TIME_PREF_NAME, this, false);
PlacesUtils.history.addObserver(this, true);
this.cacheLifespan = this.prefs.getIntPref(CACHE_EXPIRATION_TIME_PREF_NAME);
@ -761,7 +783,31 @@ this.AeroPeek = {
});
break;
}
}
},
/* nsINavHistoryObserver implementation */
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onVisit() {},
onTitleChanged() {},
onFrecencyChanged() {},
onManyFrecenciesChanged() {},
onDeleteURI() {},
onClearHistory() {},
onDeleteVisits() {},
onPageChanged(uri, changedConst, newValue) {
if (this._enabled && changedConst == Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
for (let win of this.windows) {
for (let [tab, preview] of win.previews) {
if (tab.getAttribute("image") == newValue) {
win.onLinkIconAvailable(tab.linkedBrowser, newValue);
}
}
}
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, Ci.nsINavHistoryObserver]),
};
XPCOMUtils.defineLazyGetter(AeroPeek, "cacheTimer", () =>

View File

@ -864,6 +864,14 @@ menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
margin-inline-start: 0;
}
.urlbar-input-box,
#urlbar-display-box {
padding-inline-start: 4px;
border-inline-start: 1px solid var(--urlbar-separator-color);
border-image: linear-gradient(transparent 15%, var(--urlbar-separator-color) 15%, var(--urlbar-separator-color) 85%, transparent 85%);
border-image-slice: 1;
}
.urlbar-history-dropmarker {
-moz-appearance: toolbarbutton-dropdown;
transition: opacity 0.15s ease;
@ -958,8 +966,6 @@ menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
#urlbar-display-box {
margin-top: -1px;
margin-bottom: -1px;
border-inline-end: 1px solid #AAA;
margin-inline-end: 3px;
}
.urlbar-display {
@ -1011,9 +1017,6 @@ menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
}
/* Notification icon box */
#notification-popup-box {
border-radius: 2.5px 0 0 2.5px;
}
.notification-anchor-icon:-moz-focusring {
outline: 1px dotted -moz-DialogText;

View File

@ -1576,10 +1576,6 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
padding-top: 4px;
padding-bottom: 4px;
}
#identity-box {
--identity-box-hover-background-color: rgb(240,237,237);
--identity-box-selected-background-color: rgb(220,217,217);
}
}
#identity-box:-moz-locale-dir(ltr) {
@ -1604,6 +1600,14 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
padding: 3px 0 2px;
}
.urlbar-input-box,
#urlbar-display-box {
padding-inline-start: 4px;
border-inline-start: 1px solid var(--urlbar-separator-color);
border-image: linear-gradient(transparent 15%, var(--urlbar-separator-color) 15%, var(--urlbar-separator-color) 85%, transparent 85%);
border-image-slice: 1;
}
.urlbar-history-dropmarker {
padding: 0 3px;
list-style-image: var(--urlbar-dropmarker-url);
@ -1689,11 +1693,6 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button {
max-width: 20em;
}
#urlbar-display-box {
border-inline-end: 1px solid #AAA;
margin-inline-end: 3px;
}
.urlbar-display {
margin-top: 0;
margin-bottom: 0;
@ -2587,16 +2586,6 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
padding: 6px 0 4px;
}
/* Background tabs:
*
* Decrease the height of the hoverable region of background tabs whenever the tabs are at the top
* of the window (e.g. no menubar, tabs in titlebar, etc.) to make it easier to drag the window by
* the titlebar. We don't need this in fullscreen since window dragging is not an issue there.
*/
#main-window[tabsintitlebar]:not([inFullscreen]) .tab-background-middle:not([visuallyselected=true]) {
clip-path: url(chrome://browser/content/browser.xul#tab-hover-clip-path);
}
/**
* Tab Drag and Drop
*/
@ -2970,10 +2959,6 @@ menuitem:hover > hbox > .alltabs-endimage[soundplaying] {
%include ../shared/notification-icons.inc.css
#notification-popup-box {
border-radius: 2px 0 0 2px;
}
.notification-anchor-icon:-moz-focusring {
box-shadow: 0 0 2px 1px -moz-mac-focusring inset,
0 0 3px 2px -moz-mac-focusring;

View File

@ -16,10 +16,6 @@
<clipPath id="tab-curve-clip-path-end" clipPathUnits="objectBoundingBox">
<path d="m 0,0.0625 -0.05,0 0,0.938 1,0 0,-0.028 C 0.67917542,0.95840561 0.56569036,0.81970962 0.51599998,0.5625 0.48279998,0.3905 0.465,0.0659 0,0.0625 z"/>
</clipPath>
<clipPath id="tab-hover-clip-path" clipPathUnits="objectBoundingBox">
<path d="M 0,0.2 0,1 1,1, 1,0.2 z"/>
</clipPath>
</defs>
<foreignObject width="30" height="31" clip-path="url(#tab-curve-clip-path-end)">

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -16,10 +16,6 @@
<clipPath id="tab-curve-clip-path-start" clipPathUnits="objectBoundingBox">
<path d="m 1,0.0625 0.05,0 0,0.938 -1,0 0,-0.028 C 0.32082458,0.95840561 0.4353096,0.81970962 0.48499998,0.5625 0.51819998,0.3905 0.535,0.0659 1,0.0625 z"/>
</clipPath>
<clipPath id="tab-hover-clip-path" clipPathUnits="objectBoundingBox">
<path d="M 0,0.2 0,1 1,1, 1,0.2 z"/>
</clipPath>
</defs>
<foreignObject width="30" height="31" clip-path="url(#tab-curve-clip-path-start)">

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -10,18 +10,10 @@
display: none;
}
.style-icon-notification {
fill: #666;
}
.style-icon-notification.hover {
fill: #808080;
}
.style-icon-notification.active {
fill: #4d4d4d;
fill: #999;
}
</style>
<path id="shape-notifcations-addons" d="M10,15c0.5,0,1-0.4,1-1v-3c0,0,0-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2c0-1.8-0.9-2-1.5-2 c-1.1,0-1.1,0.7-1.8,0.7C11,7.7,11,7,11,7V6c0-0.6-0.5-1-1-1H8c0,0-0.8,0-0.8-0.8C7.2,3.6,8,3.6,8,2.5C8,1.9,7.8,1,6,1 C4.2,1,4,1.9,4,2.5c0,1.1,0.8,1.1,0.8,1.8C4.8,5,4,5,4,5H2C1.5,5,1,5.4,1,6l0,1.5c0,0-0.1,1,1.1,1c0.8,0,0.9-1,1.9-1 C4.5,7.4,5,8,5,9c0,1-0.5,1.6-1,1.6c-1,0-1.1-1.1-1.9-1.1C0.9,9.5,1,10.8,1,10.8V14c0,0.6,0.5,1,1,1l2.6,0c0,0,1.1,0,1.1-1 c0-0.8-1-0.1-1-1.1c0-0.5,0.7-1.2,1.8-1.2s1.8,0.7,1.8,1.2c0,1-1.1,0.3-1.1,1.1c0,1,1.2,1,1.2,1H10z"/>
<path id="shape-notifications-addons" d="M10,15c0.5,0,1-0.4,1-1v-3c0,0,0-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2c0-1.8-0.9-2-1.5-2 c-1.1,0-1.1,0.7-1.8,0.7C11,7.7,11,7,11,7V6c0-0.6-0.5-1-1-1H8c0,0-0.8,0-0.8-0.8C7.2,3.6,8,3.6,8,2.5C8,1.9,7.8,1,6,1 C4.2,1,4,1.9,4,2.5c0,1.1,0.8,1.1,0.8,1.8C4.8,5,4,5,4,5H2C1.5,5,1,5.4,1,6l0,1.5c0,0-0.1,1,1.1,1c0.8,0,0.9-1,1.9-1 C4.5,7.4,5,8,5,9c0,1-0.5,1.6-1,1.6c-1,0-1.1-1.1-1.9-1.1C0.9,9.5,1,10.8,1,10.8V14c0,0.6,0.5,1,1,1l2.6,0c0,0,1.1,0,1.1-1 c0-0.8-1-0.1-1-1.1c0-0.5,0.7-1.2,1.8-1.2s1.8,0.7,1.8,1.2c0,1-1.1,0.3-1.1,1.1c0,1,1.2,1,1.2,1H10z"/>
</defs>
<use id="default" xlink:href="#shape-notifcations-addons" class="style-icon-notification"/>
<use id="hover" xlink:href="#shape-notifcations-addons" class="style-icon-notification hover"/>
<use id="active" xlink:href="#shape-notifcations-addons" class="style-icon-notification active"/>
<use id="default" xlink:href="#shape-notifications-addons" class="style-icon-notification"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -61,8 +61,6 @@
:root[devtoolstheme="dark"] #identity-box {
--identity-box-chrome-color: #46afe3;
--identity-box-verified-background-color: transparent;
--identity-box-hover-background-color: rgba(231,230,230,.2);
--identity-box-selected-background-color: rgba(211,210,210,.2);
}
:root[devtoolstheme="light"] {
@ -209,23 +207,6 @@ window:not([chromehidden~="toolbar"]) #urlbar-wrapper {
margin-inline-start: 0;
}
/* Swap out the white arrow with a dark one for the dark theme */
:root[devtoolstheme="dark"] #notification-popup-box {
border-image: url("chrome://browser/skin/devedition/urlbar-arrow.png") 0 8 0 0 fill;
}
@media (min-resolution: 1.1dppx) {
:root[devtoolstheme="dark"] #notification-popup-box {
border-image: url("chrome://browser/skin/devedition/urlbar-arrow@2x.png") 0 16 0 0 fill;
}
}
/* The (white) notification box background color should match the theme */
#notification-popup-box {
border-radius: 0;
background-color: var(--url-and-searchbar-background-color);
}
/* Nav bar specific stuff */
#nav-bar {
margin-top: 0 !important;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 B

View File

@ -10,6 +10,11 @@ body {
/* Top padding for when the window height is small.
Bottom padding to keep everything centered. */
padding: 75px 0;
/* info-pages.css sets a minimum width of 13em to the content
* container. If we don't set a min-width here, the content
* gets clipped in iframes with small width. We don't accomodate
* any padding to prioritize real estate in the small viewport. */
min-width: 13em;
}
.button-container {

View File

@ -5,8 +5,6 @@
%endif
#identity-box {
--identity-box-hover-background-color: rgb(231,230,230);
--identity-box-selected-background-color: rgb(211,210,210);
--identity-box-verified-color: hsl(92,100%,30%);
%ifdef MOZ_OFFICIAL_BRANDING
--identity-box-chrome-color: rgb(229,115,0);
@ -18,35 +16,14 @@
%endif
%endif
border-inline-end: 1px solid var(--urlbar-separator-color);
border-image: linear-gradient(transparent 15%,
var(--urlbar-separator-color) 15%,
var(--urlbar-separator-color) 85%,
transparent 85%);
border-image-slice: 1;
font-size: .9em;
padding: 3px 5px;
margin-inline-end: 4px;
overflow: hidden;
/* The latter two properties have a transition to handle the delayed hiding of
the forward button when hovered. */
transition: background-color 150ms ease, padding-left, padding-right;
}
#identity-box:hover,
#identity-box[open=true] {
border-image-source: none;
}
#identity-box:hover {
background-color: var(--identity-box-hover-background-color);
}
#identity-box:hover:active,
#identity-box[open=true] {
background-color: var(--identity-box-selected-background-color);
}
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
color: var(--identity-box-verified-color);
}
@ -63,27 +40,18 @@
padding-right: 2px;
}
#notification-popup-box:not([hidden]) + #identity-box {
padding-inline-start: 10px;
border-radius: 0;
}
@conditionalForwardWithUrlbar@ > #urlbar > #identity-box {
border-radius: 0;
}
@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #identity-box {
padding-inline-start: calc(var(--backbutton-urlbar-overlap) + 5px);
}
@conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
@conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] + #urlbar > #identity-box {
/* Forward button hiding is delayed when hovered, so we should use the same
delay for the identity box. We handle both horizontal paddings (for LTR and
RTL), the latter two delays here are for padding-left and padding-right. */
transition-delay: 0s, 100s, 100s;
}
@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #identity-box {
/* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
padding-inline-start: calc(var(--backbutton-urlbar-overlap) + 5.01px);
}
@ -96,7 +64,7 @@
list-style-image: url(chrome://browser/skin/identity-icon.svg#normal);
}
#identity-box:hover > #identity-icon,
#identity-box:hover > #identity-icon:not(.no-hover),
#identity-box[open=true] > #identity-icon {
list-style-image: url(chrome://browser/skin/identity-icon.svg#hover);
}
@ -105,7 +73,7 @@
list-style-image: url(chrome://browser/skin/identity-icon.svg#notice);
}
#identity-box.grantedPermissions:hover > #identity-icon,
#identity-box.grantedPermissions:hover > #identity-icon:not(.no-hover),
#identity-box.grantedPermissions[open=true] > #identity-icon {
list-style-image: url(chrome://browser/skin/identity-icon.svg#notice-hover);
}

View File

@ -116,8 +116,6 @@
skin/classic/browser/undoCloseTab@2x.png (../shared/undoCloseTab@2x.png)
skin/classic/browser/update-badge.svg (../shared/update-badge.svg)
skin/classic/browser/update-badge-failed.svg (../shared/update-badge-failed.svg)
skin/classic/browser/urlbar-arrow.png (../shared/urlbar-arrow.png)
skin/classic/browser/urlbar-arrow@2x.png (../shared/urlbar-arrow@2x.png)
skin/classic/browser/warning.svg (../shared/warning.svg)
skin/classic/browser/cert-error.svg (../shared/incontent-icons/cert-error.svg)
skin/classic/browser/session-restore.svg (../shared/incontent-icons/session-restore.svg)
@ -150,8 +148,6 @@
skin/classic/browser/privatebrowsing/tracking-protection-off.svg (../shared/privatebrowsing/tracking-protection-off.svg)
skin/classic/browser/privatebrowsing/tracking-protection.svg (../shared/privatebrowsing/tracking-protection.svg)
skin/classic/browser/devedition/urlbar-history-dropmarker.svg (../shared/devedition/urlbar-history-dropmarker.svg)
skin/classic/browser/devedition/urlbar-arrow.png (../shared/devedition/urlbar-arrow.png)
skin/classic/browser/devedition/urlbar-arrow@2x.png (../shared/devedition/urlbar-arrow@2x.png)
skin/classic/browser/urlbar-star.svg (../shared/urlbar-star.svg)
skin/classic/browser/urlbar-tab.svg (../shared/urlbar-tab.svg)
skin/classic/browser/usercontext/personal.svg (../shared/usercontext/personal.svg)

View File

@ -5,38 +5,10 @@
%endif
#notification-popup-box {
position: relative;
background-color: #fff;
background-clip: padding-box;
padding-left: 3px;
border-width: 0 8px 0 0;
border-style: solid;
border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 fill;
margin-inline-end: -8px;
}
@media (min-resolution: 1.1dppx) {
#notification-popup-box {
border-image: url("chrome://browser/skin/urlbar-arrow@2x.png") 0 16 0 0 fill;
}
}
@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box {
padding-left: calc(var(--backbutton-urlbar-overlap) + 3px);
}
/* This changes the direction of the main notification box on the url bar. */
#notification-popup-box:-moz-locale-dir(rtl),
/* This adds a second flip for the notification anchors, as they don't switch direction
for RTL mode. */
.notification-anchor-icon:-moz-locale-dir(rtl) {
transform: scaleX(-1);
}
/* For the anchor icons in the chat window, we don't have the notification popup box,
so we need to cancel the RTL transform. */
.notification-anchor-icon.chat-toolbarbutton:-moz-locale-dir(rtl) {
transform: none;
padding: 5px 0px;
margin: -5px 0px;
margin-inline-end: -5px;
padding-inline-end: 5px;
}
/* This class can be used alone or in combination with the class defining the
@ -45,7 +17,7 @@
.notification-anchor-icon {
width: 16px;
height: 16px;
margin: 0 2px;
margin-inline-start: 2px;
%ifdef MOZ_WIDGET_GTK
list-style-image: url(moz-icon://stock/gtk-dialog-info?size=16);
%else
@ -247,14 +219,6 @@
list-style-image: url(chrome://browser/skin/addons/addon-install-anchor.svg#default);
}
.install-icon:hover {
list-style-image: url(chrome://browser/skin/addons/addon-install-anchor.svg#hover);
}
.install-icon:hover:active {
list-style-image: url(chrome://browser/skin/addons/addon-install-anchor.svg#active);
}
.popup-notification-icon[popupid="xpinstall-disabled"],
.popup-notification-icon[popupid="addon-install-blocked"],
.popup-notification-icon[popupid="addon-install-origin-blocked"] {
@ -314,14 +278,6 @@
-moz-image-region: rect(0, 16px, 16px, 0);
}
.plugin-icon:hover {
-moz-image-region: rect(0, 32px, 16px, 16px);
}
.plugin-icon:active {
-moz-image-region: rect(0, 48px, 16px, 32px);
}
%ifdef XP_MACOSX
@media (min-resolution: 1.1dppx) {
.plugin-icon {
@ -339,14 +295,6 @@
.plugin-icon {
-moz-image-region: rect(0, 32px, 32px, 0);
}
.plugin-icon:hover {
-moz-image-region: rect(0, 64px, 32px, 32px);
}
.plugin-icon:active {
-moz-image-region: rect(0, 96px, 32px, 64px);
}
}
%endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 B

View File

@ -1265,6 +1265,14 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
margin-inline-start: 0;
}
.urlbar-input-box,
#urlbar-display-box {
padding-inline-start: 4px;
border-inline-start: 1px solid var(--urlbar-separator-color);
border-image: linear-gradient(transparent 15%, var(--urlbar-separator-color) 15%, var(--urlbar-separator-color) 85%, transparent 85%);
border-image-slice: 1;
}
#urlbar-icons {
-moz-box-align: center;
}
@ -1314,11 +1322,6 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
margin-inline-start: 0;
}
#urlbar-display-box {
border-inline-end: 1px solid #AAA;
margin-inline-end: 3px;
}
.urlbar-display {
margin-top: 0;
margin-bottom: 0;
@ -1964,16 +1967,6 @@ html|span.ac-emphasize-text-url {
outline-offset: -6px;
}
/* Background tabs:
*
* Decrease the height of the hoverable region of background tabs whenever the tabs are at the top
* of the window (e.g. no menubar, tabs in titlebar, etc.) to make it easier to drag the window by
* the titlebar. We don't need this in fullscreen since window dragging is not an issue there.
*/
#main-window[tabsintitlebar][sizemode=normal] #toolbar-menubar[autohide="true"][inactive] + #TabsToolbar .tab-background-middle:not([visuallyselected=true]) {
clip-path: url(chrome://browser/content/browser.xul#tab-hover-clip-path);
}
/* Tab DnD indicator */
.tab-drop-indicator {
list-style-image: url(chrome://browser/skin/tabbrowser/tabDragIndicator.png);
@ -2176,9 +2169,6 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
}
/* Notification icon box */
#notification-popup-box {
border-radius: 2.5px 0 0 2.5px;
}
.notification-anchor-icon:-moz-focusring {
outline: 1px dotted -moz-DialogText;

View File

@ -133,6 +133,19 @@ var removeTab = Task.async(function* (tab) {
info("Tab removed and finished closing");
});
/**
* Refresh the given tab.
* @param {Object} tab The tab to be refreshed.
* @return Promise<undefined> resolved when the tab is successfully refreshed.
*/
var refreshTab = Task.async(function*(tab) {
info("Refreshing tab.");
const finished = once(gBrowser.selectedBrowser, "load", true);
gBrowser.reloadTab(gBrowser.selectedTab);
yield finished;
info("Tab finished refreshing.");
});
/**
* Simulate a key event from a <key> element.
* @param {DOMNode} key

View File

@ -37,6 +37,10 @@ storage.panelLabel=Storage Panel
# A keyboard shortcut for Storage Inspector will be shown inside the brackets.
storage.tooltip3=Storage Inspector (Cookies, Local Storage, …) (%S)
# LOCALIZATION NOTE (storage.filter.key):
# Key shortcut used to focus the filter box on top of the data view
storage.filter.key=CmdOrCtrl+F
# LOCALIZATION NOTE (tree.emptyText):
# This string is displayed when the Storage Tree is empty. This can happen when
# there are no websites on the current page (about:blank)

View File

@ -4,6 +4,7 @@ subsuite = devtools
support-files =
head.js
doc_big_tree.html
doc_empty.html
doc_steady_allocation.html
!/devtools/client/framework/test/shared-head.js
!/devtools/client/framework/test/shared-redux-head.js
@ -23,6 +24,7 @@ support-files =
[browser_memory_no_auto_expand.js]
skip-if = debug # bug 1219554
[browser_memory_percents_01.js]
[browser_memory_refresh_does_not_leak.js]
[browser_memory_simple_01.js]
[browser_memory_transferHeapSnapshot_e10s_01.js]
[browser_memory_tree_map-01.js]

View File

@ -0,0 +1,108 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that refreshing the page with devtools open does not leak the old
// windows from previous navigations.
//
// IF THIS TEST STARTS FAILING, YOU ARE LEAKING EVERY WINDOW EVER NAVIGATED TO
// WHILE DEVTOOLS ARE OPEN! THIS IS NOT SPECIFIC TO THE MEMORY TOOL ONLY!
"use strict";
const HeapSnapshotFileUtils = require("devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
const { getLabelAndShallowSize } = require("devtools/shared/heapsnapshot/DominatorTreeNode");
const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_empty.html";
function* getWindowsInSnapshot(front) {
dumpn("Taking snapshot.");
const path = yield front.saveHeapSnapshot();
dumpn("Took snapshot with path = " + path);
const snapshot = ChromeUtils.readHeapSnapshot(path);
dumpn("Read snapshot into memory, taking census.");
const report = snapshot.takeCensus({
breakdown: {
by: "objectClass",
then: { by: "bucket" },
other: { by: "count", count: true, bytes: false },
}
});
dumpn("Took census, window count = " + report.Window.count);
return report.Window;
}
const DESCRIPTION = {
by: "coarseType",
objects: {
by: "objectClass",
then: { by: "count", count: true, bytes: false },
other: { by: "count", count: true, bytes: false },
},
strings: { by: "count", count: true, bytes: false },
scripts: {
by: "internalType",
then: { by: "count", count: true, bytes: false },
},
other: {
by: "internalType",
then: { by: "count", count: true, bytes: false },
}
};
this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
const heapWorker = panel.panelWin.gHeapAnalysesClient;
const front = panel.panelWin.gFront;
const store = panel.panelWin.gStore;
const { getState, dispatch } = store;
const doc = panel.panelWin.document;
const startWindows = yield getWindowsInSnapshot(front);
dumpn("Initial windows found = " + startWindows.map(w => "0x" + w.toString(16)).join(", "));
is(startWindows.length, 1);
yield refreshTab(tab);
const endWindows = yield getWindowsInSnapshot(front);
is(endWindows.length, 1);
if (endWindows.length === 1) {
return;
}
dumpn("Test failed, diagnosing leaking windows.");
dumpn("(This may fail if a moving GC has relocated the initial Window objects.)");
dumpn("Taking full runtime snapshot.");
const path = yield front.saveHeapSnapshot({ boundaries: { runtime: true } });
dumpn("Full runtime's snapshot path = " + path);
dumpn("Reading full runtime heap snapshot.");
const snapshot = ChromeUtils.readHeapSnapshot(path);
dumpn("Done reading full runtime heap snapshot.");
const dominatorTree = snapshot.computeDominatorTree();
const paths = snapshot.computeShortestPaths(dominatorTree.root, startWindows, 50);
for (let i = 0; i < startWindows.length; i++) {
dumpn("Shortest retaining paths for leaking Window 0x" + startWindows[i].toString(16) + " =========================");
let j = 0;
for (let retainingPath of paths.get(startWindows[i])) {
if (retainingPath.find(part => part.predecessor === startWindows[i])) {
// Skip paths that loop out from the target window and back to it again.
continue;
}
dumpn(" Path #" + (++j) + ": --------------------------------------------------------------------");
for (let part of retainingPath) {
const { label } = getLabelAndShallowSize(part.predecessor, snapshot, DESCRIPTION);
dumpn(" 0x" + part.predecessor.toString(16) +
" (" + label.join(" > ") + ")");
dumpn(" |");
dumpn(" " + part.edge);
dumpn(" |");
dumpn(" V");
}
dumpn(" 0x" + startWindows[i].toString(16) + " (objects > Window)");
}
}
});

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
This is an empty window.
</body>
</html>

View File

@ -124,6 +124,7 @@ skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
[browser_html_tooltip_offset.js]
[browser_html_tooltip_variable-height.js]
[browser_html_tooltip_width-auto.js]
[browser_html_tooltip_xul-wrapper.js]
[browser_inplace-editor-01.js]
[browser_inplace-editor-02.js]
[browser_inplace-editor_autocomplete_01.js]

View File

@ -9,7 +9,7 @@
*/
const HTML_NS = "http://www.w3.org/1999/xhtml";
const TEST_WINDOW_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
@ -22,19 +22,11 @@ const TEST_WINDOW_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const TEST_PAGE_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Tooltip test with document using a Page element">
<vbox flex="1">
<hbox id="box1" flex="1">test1</hbox>
</vbox>
</page>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;
function getTooltipContent(doc) {
let div = doc.createElementNS(HTML_NS, "div");
div.style.height = "50px";
@ -44,18 +36,20 @@ function getTooltipContent(doc) {
}
add_task(function* () {
info("Test showing a basic tooltip in XUL document using <window>");
yield testTooltipForUri(TEST_WINDOW_URI);
let [,, doc] = yield createHost("bottom", TEST_URI);
info("Test showing a basic tooltip in XUL document using <page>");
yield testTooltipForUri(TEST_PAGE_URI);
info("Run tests for a Tooltip without using a XUL panel");
useXulWrapper = false;
yield runTests(doc);
info("Run tests for a Tooltip with a XUL panel");
useXulWrapper = true;
yield runTests(doc);
});
function* testTooltipForUri(uri) {
let tab = yield addTab("about:blank");
let [,, doc] = yield createHost("bottom", uri);
let tooltip = new HTMLTooltip({doc}, {});
function* runTests(doc) {
yield addTab("about:blank");
let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
info("Set tooltip content");
tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
@ -90,5 +84,5 @@ function* testTooltipForUri(uri) {
yield waitForReflow(tooltip);
is(tooltip.isVisible(), false, "Tooltip is not visible");
yield removeTab(tab);
tooltip.destroy();
}

View File

@ -27,21 +27,33 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;
add_task(function* () {
yield addTab("about:blank");
let [,, doc] = yield createHost("bottom", TEST_URI);
info("Run tests for a Tooltip without using a XUL panel");
useXulWrapper = false;
yield runTests(doc);
info("Run tests for a Tooltip with a XUL panel");
useXulWrapper = true;
yield runTests(doc);
});
function* runTests(doc) {
yield testClickInTooltipContent(doc);
yield testConsumeOutsideClicksFalse(doc);
yield testConsumeOutsideClicksTrue(doc);
yield testClickInOuterIframe(doc);
yield testClickInInnerIframe(doc);
});
}
function* testClickInTooltipContent(doc) {
info("Test a tooltip is not closed when clicking inside itself");
let tooltip = new HTMLTooltip({doc}, {});
let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
yield showTooltip(tooltip, doc.getElementById("box1"));
@ -57,7 +69,7 @@ function* testConsumeOutsideClicksFalse(doc) {
info("Test closing a tooltip via click with consumeOutsideClicks: false");
let box4 = doc.getElementById("box4");
let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: false});
let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: false, useXulWrapper});
tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
yield showTooltip(tooltip, doc.getElementById("box1"));
@ -80,7 +92,7 @@ function* testConsumeOutsideClicksTrue(doc) {
let box4clicks = 0;
box4.addEventListener("click", () => box4clicks++);
let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: true});
let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: true, useXulWrapper});
tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
yield showTooltip(tooltip, doc.getElementById("box1"));
@ -98,7 +110,7 @@ function* testClickInOuterIframe(doc) {
info("Test clicking an iframe outside of the tooltip closes the tooltip");
let frame = doc.getElementById("frame");
let tooltip = new HTMLTooltip({doc});
let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
yield showTooltip(tooltip, doc.getElementById("box1"));
@ -113,7 +125,7 @@ function* testClickInOuterIframe(doc) {
function* testClickInInnerIframe(doc) {
info("Test clicking an iframe inside the tooltip content does not close the tooltip");
let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: false});
let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: false, useXulWrapper});
let iframe = doc.createElementNS(HTML_NS, "iframe");
iframe.style.width = "100px";

View File

@ -31,14 +31,26 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;
add_task(function* () {
yield addTab("about:blank");
let [, , doc] = yield createHost("bottom", TEST_URI);
info("Run tests for a Tooltip without using a XUL panel");
useXulWrapper = false;
yield runTests(doc);
info("Run tests for a Tooltip with a XUL panel");
useXulWrapper = true;
yield runTests(doc);
});
function* runTests(doc) {
yield testNoAutoFocus(doc);
yield testAutoFocus(doc);
yield testAutoFocusPreservesFocusChange(doc);
});
}
function* testNoAutoFocus(doc) {
yield focusNode(doc, "#box4-input");
@ -52,6 +64,8 @@ function* testNoAutoFocus(doc) {
yield hideTooltip(tooltip);
yield blurNode(doc, "#box4-input");
tooltip.destroy();
}
function* testAutoFocus(doc) {
@ -70,6 +84,8 @@ function* testAutoFocus(doc) {
info("Blur the textbox before moving to the next test to reset the state.");
yield blurNode(doc, "#box4-input");
tooltip.destroy();
}
function* testAutoFocusPreservesFocusChange(doc) {
@ -92,6 +108,8 @@ function* testAutoFocusPreservesFocusChange(doc) {
info("Blur the textbox before moving to the next test to reset the state.");
yield blurNode(doc, "#box3-input");
tooltip.destroy();
}
/**
@ -126,7 +144,7 @@ function blurNode(doc, selector) {
* tooltip content will be ready.
*/
function* createTooltip(doc, autofocus) {
let tooltip = new HTMLTooltip({doc}, {autofocus});
let tooltip = new HTMLTooltip({doc}, {autofocus, useXulWrapper});
let div = doc.createElementNS(HTML_NS, "div");
div.classList.add("tooltip-content");
div.style.height = "50px";

View File

@ -40,7 +40,7 @@ add_task(function* () {
let [,, doc] = yield createHost("bottom", TEST_URI);
info("Create HTML tooltip");
let tooltip = new HTMLTooltip({doc}, {});
let tooltip = new HTMLTooltip({doc}, {useXulWrapper: false});
let div = doc.createElementNS(HTML_NS, "div");
div.style.height = "100%";
tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
@ -105,4 +105,6 @@ add_task(function* () {
yield hideTooltip(tooltip);
is(tooltip.isVisible(), false, "Tooltip is not visible");
tooltip.destroy();
});

View File

@ -32,12 +32,11 @@ const TOOLTIP_WIDTH = 200;
add_task(function* () {
// Force the toolbox to be 200px high;
yield pushPref("devtools.toolbox.footer.height", 200);
yield addTab("about:blank");
let [,, doc] = yield createHost("bottom", TEST_URI);
info("Create HTML tooltip");
let tooltip = new HTMLTooltip({doc}, {});
let tooltip = new HTMLTooltip({doc}, {useXulWrapper: false});
let div = doc.createElementNS(HTML_NS, "div");
div.style.height = "100%";
tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
@ -57,7 +56,7 @@ add_task(function* () {
yield hideTooltip(tooltip);
info("Try to display the tooltip on top of box1.");
yield showTooltip(tooltip, box1, "top");
yield showTooltip(tooltip, box1, {position: "top"});
expectedTooltipGeometry = {position: "bottom", height: 150, width};
checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
yield hideTooltip(tooltip);
@ -105,4 +104,6 @@ add_task(function* () {
yield hideTooltip(tooltip);
is(tooltip.isVisible(), false, "Tooltip is not visible");
tooltip.destroy();
});

View File

@ -49,6 +49,8 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;
add_task(function* () {
// Force the toolbox to be 200px high;
yield pushPref("devtools.toolbox.footer.height", 200);
@ -56,8 +58,18 @@ add_task(function* () {
yield addTab("about:blank");
let [,, doc] = yield createHost("bottom", TEST_URI);
info("Run tests for a Tooltip without using a XUL panel");
useXulWrapper = false;
yield runTests(doc);
info("Run tests for a Tooltip with a XUL panel");
useXulWrapper = true;
yield runTests(doc);
});
function* runTests(doc) {
info("Create HTML tooltip");
let tooltip = new HTMLTooltip({doc}, {type: "arrow"});
let tooltip = new HTMLTooltip({doc}, {type: "arrow", useXulWrapper});
let div = doc.createElementNS(HTML_NS, "div");
div.style.height = "35px";
tooltip.setContent(div, {width: 200, height: 35});
@ -91,4 +103,6 @@ add_task(function* () {
yield hideTooltip(tooltip);
}
});
tooltip.destroy();
}

View File

@ -43,15 +43,26 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;
add_task(function* () {
// Force the toolbox to be 200px high;
yield pushPref("devtools.toolbox.footer.height", 200);
yield addTab("about:blank");
let [,, doc] = yield createHost("bottom", TEST_URI);
info("Run tests for a Tooltip without using a XUL panel");
useXulWrapper = false;
yield runTests(doc);
info("Run tests for a Tooltip with a XUL panel");
useXulWrapper = true;
yield runTests(doc);
});
function* runTests(doc) {
info("Create HTML tooltip");
let tooltip = new HTMLTooltip({doc}, {type: "arrow"});
let tooltip = new HTMLTooltip({doc}, {type: "arrow", useXulWrapper});
let div = doc.createElementNS(HTML_NS, "div");
div.style.height = "35px";
tooltip.setContent(div, {width: 200, height: 35});
@ -84,4 +95,6 @@ add_task(function* () {
"The tooltip arrow remains inside the tooltip panel horizontally");
yield hideTooltip(tooltip);
}
});
tooltip.destroy();
}

View File

@ -34,7 +34,6 @@ function getTooltipContent(doc) {
}
add_task(function* () {
yield addTab("about:blank");
let [,, doc] = yield createHost("bottom", TEST_URI);
let box1 = doc.getElementById("box1");
@ -44,7 +43,7 @@ add_task(function* () {
let width = 100, height = 50;
let tooltip = new HTMLTooltip({doc}, {});
let tooltip = new HTMLTooltip({doc}, {useXulWrapper: false});
tooltip.setContent(getTooltipContent(doc), {width, height});
info("Show the tooltip on each of the 4 hbox, without calling hide in between");
@ -67,4 +66,6 @@ add_task(function* () {
info("Hide tooltip before leaving test");
yield hideTooltip(tooltip);
tooltip.destroy();
});

View File

@ -30,7 +30,6 @@ add_task(function* () {
// Force the toolbox to be 200px high;
yield pushPref("devtools.toolbox.footer.height", 200);
yield addTab("about:blank");
let [,, doc] = yield createHost("bottom", TEST_URI);
info("Test a tooltip is not closed when clicking inside itself");
@ -40,7 +39,7 @@ add_task(function* () {
let box3 = doc.getElementById("box3");
let box4 = doc.getElementById("box4");
let tooltip = new HTMLTooltip({doc}, {});
let tooltip = new HTMLTooltip({doc}, {useXulWrapper: false});
let div = doc.createElementNS(HTML_NS, "div");
div.style.height = "100px";

View File

@ -29,6 +29,8 @@ const TOOLTIP_HEIGHT = 50;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;
add_task(function* () {
// Force the toolbox to be 400px tall => 50px for each box.
yield pushPref("devtools.toolbox.footer.height", 400);
@ -36,7 +38,17 @@ add_task(function* () {
yield addTab("about:blank");
let [,, doc] = yield createHost("bottom", TEST_URI);
let tooltip = new HTMLTooltip({doc}, {});
info("Run tests for a Tooltip without using a XUL panel");
useXulWrapper = false;
yield runTests(doc);
info("Run tests for a Tooltip with a XUL panel");
useXulWrapper = true;
yield runTests(doc);
});
function* runTests(doc) {
let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
info("Set tooltip content 50px tall, but request a container 200px tall");
let tooltipContent = doc.createElementNS(HTML_NS, "div");
tooltipContent.style.cssText = "height: " + TOOLTIP_HEIGHT + "px; background: red;";
@ -71,4 +83,6 @@ add_task(function* () {
EventUtils.synthesizeMouse(tooltip.container, 100, CONTAINER_HEIGHT + 10,
{}, doc.defaultView);
yield onHidden;
});
tooltip.destroy();
}

View File

@ -25,11 +25,23 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;
add_task(function* () {
yield addTab("about:blank");
let [,, doc] = yield createHost("bottom", TEST_URI);
let tooltip = new HTMLTooltip({doc}, {});
info("Run tests for a Tooltip without using a XUL panel");
useXulWrapper = false;
yield runTests(doc);
info("Run tests for a Tooltip with a XUL panel");
useXulWrapper = true;
yield runTests(doc);
});
function* runTests(doc) {
let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
info("Create tooltip content width to 150px");
let tooltipContent = doc.createElementNS(HTML_NS, "div");
tooltipContent.style.cssText = "height: 100%; width: 150px; background: red;";
@ -44,4 +56,6 @@ add_task(function* () {
is(panelRect.width, 150, "Tooltip panel has the expected width.");
yield hideTooltip(tooltip);
});
tooltip.destroy();
}

View File

@ -0,0 +1,80 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from helper_html_tooltip.js */
"use strict";
/**
* Test the HTMLTooltip can overflow out of the toolbox when using a XUL panel wrapper.
*/
const HTML_NS = "http://www.w3.org/1999/xhtml";
const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Tooltip test">
<vbox flex="1">
<hbox id="box1" style="height: 50px">test1</hbox>
<hbox id="box2" style="height: 50px">test2</hbox>
<hbox id="box3" style="height: 50px">test3</hbox>
<hbox id="box4" style="height: 50px">test4</hbox>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
// The test toolbox will be 200px tall, the anchors are 50px tall, therefore, the maximum
// tooltip height that could fit in the toolbox is 150px. Setting 160px, the tooltip will
// either have to overflow or to be resized.
const TOOLTIP_HEIGHT = 160;
const TOOLTIP_WIDTH = 200;
add_task(function* () {
// Force the toolbox to be 200px high;
yield pushPref("devtools.toolbox.footer.height", 200);
let [, win, doc] = yield createHost("bottom", TEST_URI);
info("Resizing window to have some space below the window.");
let originalWidth = win.top.outerWidth;
let originalHeight = win.top.outerHeight;
win.top.resizeBy(0, -100);
info("Create HTML tooltip");
let tooltip = new HTMLTooltip({doc}, {useXulWrapper: true});
let div = doc.createElementNS(HTML_NS, "div");
div.style.height = "200px";
div.style.background = "red";
tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
let box1 = doc.getElementById("box1");
// Above box1: check that the tooltip can overflow onto the content page.
info("Display the tooltip above box1.");
yield showTooltip(tooltip, box1, {position: "top"});
checkTooltip(tooltip, "top", TOOLTIP_HEIGHT);
yield hideTooltip(tooltip);
// Below box1: check that the tooltip can overflow out of the browser window.
info("Display the tooltip below box1.");
yield showTooltip(tooltip, box1, {position: "bottom"});
checkTooltip(tooltip, "bottom", TOOLTIP_HEIGHT);
yield hideTooltip(tooltip);
is(tooltip.isVisible(), false, "Tooltip is not visible");
tooltip.destroy();
info("Restore original window dimensions.");
win.top.resizeTo(originalWidth, originalHeight);
});
function checkTooltip(tooltip, position, height) {
is(tooltip.position, position, "Actual tooltip position is " + position);
let rect = tooltip.container.getBoundingClientRect();
is(rect.height, height, "Actual tooltip height is " + height);
// Testing the actual left/top offsets is not relevant here as it is handled by the XUL
// panel.
}

View File

@ -8,6 +8,8 @@
const EventEmitter = require("devtools/shared/event-emitter");
const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipToggle");
const {listenOnce} = require("devtools/shared/async-utils");
const {Task} = require("devtools/shared/task");
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const XHTML_NS = "http://www.w3.org/1999/xhtml";
@ -49,8 +51,9 @@ const EXTRA_BORDER = {
*
* @param {DOMRect} anchorRect
* Bounding rectangle for the anchor, relative to the tooltip document.
* @param {DOMRect} docRect
* Bounding rectange for the tooltip document owner.
* @param {DOMRect} viewportRect
* Bounding rectangle for the viewport. top/left can be different from 0 if some
* space should not be used by tooltips (for instance OS toolbars, taskbars etc.).
* @param {Number} height
* Preferred height for the tooltip.
* @param {String} pos
@ -61,15 +64,18 @@ const EXTRA_BORDER = {
* - {String} computedPosition: Can differ from the preferred position depending
* on the available height). "top" or "bottom"
*/
const calculateVerticalPosition = function (anchorRect, docRect, height, pos, offset) {
const calculateVerticalPosition =
function (anchorRect, viewportRect, height, pos, offset) {
let {TOP, BOTTOM} = POSITION;
let {top: anchorTop, height: anchorHeight} = anchorRect;
let {bottom: docBottom} = docRect;
// Translate to the available viewport space before calculating dimensions and position.
anchorTop -= viewportRect.top;
// Calculate available space for the tooltip.
let availableTop = anchorTop;
let availableBottom = docBottom - (anchorTop + anchorHeight);
let availableBottom = viewportRect.height - (anchorTop + anchorHeight);
// Find POSITION
let keepPosition = false;
@ -90,6 +96,9 @@ const calculateVerticalPosition = function (anchorRect, docRect, height, pos, of
// Calculate TOP.
let top = pos === TOP ? anchorTop - height - offset : anchorTop + anchorHeight + offset;
// Translate back to absolute coordinates by re-including viewport top margin.
top += viewportRect.top;
return {top, height, computedPosition: pos};
};
@ -100,8 +109,9 @@ const calculateVerticalPosition = function (anchorRect, docRect, height, pos, of
*
* @param {DOMRect} anchorRect
* Bounding rectangle for the anchor, relative to the tooltip document.
* @param {DOMRect} docRect
* Bounding rectange for the tooltip document owner.
* @param {DOMRect} viewportRect
* Bounding rectangle for the viewport. top/left can be different from 0 if some
* space should not be used by tooltips (for instance OS toolbars, taskbars etc.).
* @param {Number} width
* Preferred width for the tooltip.
* @return {Object}
@ -109,18 +119,20 @@ const calculateVerticalPosition = function (anchorRect, docRect, height, pos, of
* - {Number} width: the width to use for the tooltip container.
* - {Number} arrowLeft: the left offset to use for the arrow element.
*/
const calculateHorizontalPosition = function (anchorRect, docRect, width, type, offset) {
const calculateHorizontalPosition =
function (anchorRect, viewportRect, width, type, offset) {
let {left: anchorLeft, width: anchorWidth} = anchorRect;
let {right: docRight} = docRect;
// Translate to the available viewport space before calculating dimensions and position.
anchorLeft -= viewportRect.left;
// Calculate WIDTH.
let availableWidth = docRight;
width = Math.min(width, availableWidth);
width = Math.min(width, viewportRect.width);
// Calculate LEFT.
// By default the tooltip is aligned with the anchor left edge. Unless this
// makes it overflow the viewport, in which case is shifts to the left.
let left = Math.min(anchorLeft + offset, docRight - width);
let left = Math.min(anchorLeft + offset, viewportRect.width - width);
// Calculate ARROW LEFT (tooltip's LEFT might be updated)
let arrowLeft;
@ -141,6 +153,9 @@ const calculateHorizontalPosition = function (anchorRect, docRect, width, type,
arrowLeft = Math.max(arrowLeft, 0);
}
// Translate back to absolute coordinates by re-including viewport left margin.
left += viewportRect.left;
return {left, width, arrowLeft};
};
@ -177,15 +192,25 @@ const getRelativeRect = function (node, relativeTo) {
* - {Boolean} consumeOutsideClicks
* Defaults to true. The tooltip is closed when clicking outside.
* Should this event be stopped and consumed or not.
* - {Boolean} useXulWrapper
* Defaults to true. If the tooltip is hosted in a XUL document, use a XUL panel
* in order to use all the screen viewport available.
*/
function HTMLTooltip(toolbox,
{type = "normal", autofocus = false, consumeOutsideClicks = true} = {}) {
function HTMLTooltip(toolbox, {
type = "normal",
autofocus = false,
consumeOutsideClicks = true,
useXulWrapper = true,
} = {}) {
EventEmitter.decorate(this);
this.doc = toolbox.doc;
this.type = type;
this.autofocus = autofocus;
this.consumeOutsideClicks = consumeOutsideClicks;
this.useXulWrapper = useXulWrapper;
this._position = null;
// Use the topmost window to listen for click events to close the tooltip
this.topWindow = this.doc.defaultView.top;
@ -198,7 +223,20 @@ function HTMLTooltip(toolbox,
this.container = this._createContainer();
if (this._isXUL()) {
if (this._isXUL() && this.useXulWrapper) {
// When using a XUL panel as the wrapper, the actual markup for the tooltip is as
// follows :
// <panel> <!-- XUL panel used to position the tooltip anywhere on screen -->
// <div> <!-- div wrapper used to isolate the tooltip container -->
// <div> <! the actual tooltip.container element -->
this.xulPanelWrapper = this._createXulPanelWrapper();
let inner = this.doc.createElementNS(XHTML_NS, "div");
inner.classList.add("tooltip-xul-wrapper-inner");
this.doc.documentElement.appendChild(this.xulPanelWrapper);
this.xulPanelWrapper.appendChild(inner);
inner.appendChild(this.container);
} else if (this._isXUL()) {
this.doc.documentElement.appendChild(this.container);
} else {
// In non-XUL context the container is ready to use as is.
@ -224,6 +262,13 @@ HTMLTooltip.prototype = {
return this.container.querySelector(".tooltip-arrow");
},
/**
* Retrieve the displayed position used for the tooltip. Null if the tooltip is hidden.
*/
get position() {
return this.isVisible() ? this._position : null;
},
/**
* Set the tooltip content element. The preferred width/height should also be
* specified here.
@ -260,39 +305,53 @@ HTMLTooltip.prototype = {
* - {Number} x: optional, horizontal offset between the anchor and the tooltip
* - {Number} y: optional, vertical offset between the anchor and the tooltip
*/
show: function (anchor, {position, x = 0, y = 0} = {}) {
show: Task.async(function* (anchor, {position, x = 0, y = 0} = {}) {
// Get anchor geometry
let anchorRect = getRelativeRect(anchor, this.doc);
// Get document geometry
let docRect = this.doc.documentElement.getBoundingClientRect();
if (this.useXulWrapper) {
anchorRect = this._convertToScreenRect(anchorRect);
}
// Get viewport size
let viewportRect = this._getViewportRect();
let themeHeight = EXTRA_HEIGHT[this.type] + 2 * EXTRA_BORDER[this.type];
let preferredHeight = this.preferredHeight + themeHeight;
let {top, height, computedPosition} =
calculateVerticalPosition(anchorRect, docRect, preferredHeight, position, y);
calculateVerticalPosition(anchorRect, viewportRect, preferredHeight, position, y);
// Apply height and top information before measuring the content width (if "auto").
this._position = computedPosition;
// Apply height before measuring the content width (if width="auto").
let isTop = computedPosition === POSITION.TOP;
this.container.classList.toggle("tooltip-top", isTop);
this.container.classList.toggle("tooltip-bottom", !isTop);
this.container.style.height = height + "px";
this.container.style.top = top + "px";
let themeWidth = 2 * EXTRA_BORDER[this.type];
let preferredWidth = this.preferredWidth === "auto" ?
this._measureContainerWidth() : this.preferredWidth + themeWidth;
let preferredWidth;
if (this.preferredWidth === "auto") {
preferredWidth = this._measureContainerWidth();
} else {
let themeWidth = 2 * EXTRA_BORDER[this.type];
preferredWidth = this.preferredWidth + themeWidth;
}
let {left, width, arrowLeft} =
calculateHorizontalPosition(anchorRect, docRect, preferredWidth, this.type, x);
calculateHorizontalPosition(anchorRect, viewportRect, preferredWidth, this.type, x);
this.container.style.width = width + "px";
this.container.style.left = left + "px";
if (this.type === TYPE.ARROW) {
this.arrow.style.left = arrowLeft + "px";
}
if (this.useXulWrapper) {
this._showXulWrapperAt(left, top);
} else {
this.container.style.left = left + "px";
this.container.style.top = top + "px";
}
this.container.classList.add("tooltip-visible");
// Keep a pointer on the focused element to refocus it when hiding the tooltip.
@ -304,14 +363,53 @@ HTMLTooltip.prototype = {
this.topWindow.addEventListener("click", this._onClick, true);
this.emit("shown");
}, 0);
}),
/**
* Calculate the rect of the viewport that limits the tooltip dimensions. When using a
* XUL panel wrapper, the viewport will be able to use the whole screen (excluding space
* reserved by the OS for toolbars etc.). Otherwise, the viewport is limited to the
* tooltip's document.
*
* @return {Object} DOMRect-like object with the Number properties: top, right, bottom,
* left, width, height
*/
_getViewportRect: function () {
if (this.useXulWrapper) {
// availLeft/Top are the coordinates first pixel available on the screen for
// applications (excluding space dedicated for OS toolbars, menus etc...)
// availWidth/Height are the dimensions available to applications excluding all
// the OS reserved space
let {availLeft, availTop, availHeight, availWidth} = this.doc.defaultView.screen;
return {
top: availTop,
right: availLeft + availWidth,
bottom: availTop + availHeight,
left: availLeft,
width: availWidth,
height: availHeight,
};
}
return this.doc.documentElement.getBoundingClientRect();
},
_measureContainerWidth: function () {
let xulParent = this.container.parentNode;
if (this.useXulWrapper && !this.isVisible()) {
// Move the container out of the XUL Panel to measure it.
this.doc.documentElement.appendChild(this.container);
}
this.container.classList.add("tooltip-hidden");
this.container.style.left = "0px";
this.container.style.width = "auto";
let width = this.container.getBoundingClientRect().width;
this.container.classList.remove("tooltip-hidden");
if (this.useXulWrapper && !this.isVisible()) {
xulParent.appendChild(this.container);
}
return width;
},
@ -319,7 +417,7 @@ HTMLTooltip.prototype = {
* Hide the current tooltip. The event "hidden" will be fired when the tooltip
* is hidden.
*/
hide: function () {
hide: Task.async(function* () {
this.doc.defaultView.clearTimeout(this.attachEventsTimer);
if (!this.isVisible()) {
return;
@ -327,6 +425,10 @@ HTMLTooltip.prototype = {
this.topWindow.removeEventListener("click", this._onClick, true);
this.container.classList.remove("tooltip-visible");
if (this.useXulWrapper) {
yield this._hideXulWrapper();
}
this.emit("hidden");
let tooltipHasFocus = this.container.contains(this.doc.activeElement);
@ -334,7 +436,7 @@ HTMLTooltip.prototype = {
this._focusedElement.focus();
this._focusedElement = null;
}
},
}),
/**
* Check if the tooltip is currently displayed.
@ -351,6 +453,9 @@ HTMLTooltip.prototype = {
destroy: function () {
this.hide();
this.container.remove();
if (this.xulPanelWrapper) {
this.xulPanelWrapper.remove();
}
},
_createContainer: function () {
@ -407,13 +512,6 @@ HTMLTooltip.prototype = {
return false;
},
/**
* Check if the tooltip's owner document is a XUL document.
*/
_isXUL: function () {
return this.doc.documentElement.namespaceURI === XUL_NS;
},
/**
* If the tootlip is configured to autofocus and a focusable element can be found,
* focus it.
@ -427,4 +525,52 @@ HTMLTooltip.prototype = {
focusableElement.focus();
}
},
/**
* Check if the tooltip's owner document is a XUL document.
*/
_isXUL: function () {
return this.doc.documentElement.namespaceURI === XUL_NS;
},
_createXulPanelWrapper: function () {
let panel = this.doc.createElementNS(XUL_NS, "panel");
// XUL panel is only a way to display DOM elements outside of the document viewport,
// so disable all features that impact the behavior.
panel.setAttribute("animate", false);
panel.setAttribute("consumeoutsideclicks", false);
panel.setAttribute("noautofocus", true);
panel.setAttribute("ignorekeys", true);
panel.setAttribute("level", "float");
panel.setAttribute("class", "tooltip-xul-wrapper");
return panel;
},
_showXulWrapperAt: function (left, top) {
let onPanelShown = listenOnce(this.xulPanelWrapper, "popupshown");
this.xulPanelWrapper.openPopupAtScreen(left, top, false);
return onPanelShown;
},
_hideXulWrapper: function () {
let onPanelHidden = listenOnce(this.xulPanelWrapper, "popuphidden");
this.xulPanelWrapper.hidePopup();
return onPanelHidden;
},
/**
* Convert from coordinates relative to the tooltip's document, to coordinates relative
* to the "available" screen. By "available" we mean the screen, excluding the OS bars
* display on screen edges.
*/
_convertToScreenRect: function ({left, top, width, height}) {
// mozInnerScreenX/Y are the coordinates of the top left corner of the window's
// viewport, excluding chrome UI.
left += this.doc.defaultView.mozInnerScreenX;
top += this.doc.defaultView.mozInnerScreenY;
return {top, right: left + width, bottom: top + height, left, width, height};
},
};

View File

@ -36,6 +36,7 @@ support-files =
[browser_storage_localstorage_error.js]
[browser_storage_overflow.js]
[browser_storage_search.js]
[browser_storage_search_keyboard_trap.js]
[browser_storage_sessionstorage_edit.js]
[browser_storage_sidebar.js]
[browser_storage_sidebar_update.js]

View File

@ -0,0 +1,15 @@
// Test ability to focus search field by using keyboard
"use strict";
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-search.html");
gUI.tree.expandAll();
yield selectTreeItem(["localStorage", "http://test1.example.org"]);
yield focusSearchBoxUsingShortcut(gPanelWindow);
ok(containsFocus(gPanelWindow.document, gUI.searchBox),
"Focus is in a searchbox");
yield finishTests();
});

View File

@ -793,3 +793,37 @@ function* checkState(state) {
}
}
}
/**
* Checks if document's active element is within the given element.
* @param {HTMLDocument} doc document with active element in question
* @param {DOMNode} container element tested on focus containment
* @return {Boolean}
*/
function containsFocus(doc, container) {
let elm = doc.activeElement;
while (elm) {
if (elm === container) {
return true;
}
elm = elm.parentNode;
}
return false;
}
var focusSearchBoxUsingShortcut = Task.async(function* (panelWin, callback) {
info("Focusing search box");
let searchBox = panelWin.document.getElementById("storage-searchbox");
let focused = once(searchBox, "focus");
panelWin.focus();
let strings = Services.strings.createBundle(
"chrome://devtools/locale/storage.properties");
synthesizeKeyShortcut(strings.GetStringFromName("storage.filter.key"));
yield focused;
if (callback) {
callback();
}
});

View File

@ -8,6 +8,7 @@
const {Task} = require("devtools/shared/task");
const EventEmitter = require("devtools/shared/event-emitter");
const {LocalizationHelper} = require("devtools/client/shared/l10n");
const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
loader.lazyRequireGetter(this, "TreeWidget",
"devtools/client/shared/widgets/TreeWidget", true);
@ -111,6 +112,15 @@ function StorageUI(front, target, panelWin, toolbox) {
this.filterItems = this.filterItems.bind(this);
this.searchBox.addEventListener("command", this.filterItems);
let shortcuts = new KeyShortcuts({
window: this._panelDoc.defaultView,
});
let key = L10N.getStr("storage.filter.key");
shortcuts.on(key, (name, event) => {
event.preventDefault();
this.searchBox.focus();
});
this.front.listStores().then(storageTypes => {
this.populateStorageTree(storageTypes);
}).then(null, console.error);

View File

@ -109,6 +109,17 @@
overflow: hidden;
}
.tooltip-xul-wrapper {
-moz-appearance: none;
background: transparent;
overflow: visible;
border-style: none;
}
.tooltip-xul-wrapper .tooltip-container {
position: absolute;
}
.tooltip-top {
flex-direction: column;
}
@ -137,6 +148,11 @@
filter: drop-shadow(0 3px 4px var(--theme-tooltip-shadow));
}
.tooltip-xul-wrapper .tooltip-container[type="arrow"] {
/* When displayed in a XUL panel the drop shadow would be abruptly cut by the panel */
filter: none;
}
.tooltip-container[type="arrow"] > .tooltip-panel {
position: relative;
flex-grow: 0;
@ -265,7 +281,7 @@
.event-tooltip-content-box {
display: none;
height: 54px;
height: 100px;
overflow: hidden;
margin-inline-end: 0;
border: 1px solid var(--theme-splitter-color);

View File

@ -13,6 +13,17 @@
element.
*/
:-moz-native-anonymous {
/*
Content CSS applying to the html element impact the highlighters.
To avoid that, possible cases have been set to initial.
*/
text-transform: initial;
text-indent: initial;
letter-spacing: initial;
word-spacing: initial;
}
:-moz-native-anonymous .highlighter-container {
--highlighter-guide-color: #08c;
--highlighter-content-color: #87ceeb;

View File

@ -47,8 +47,8 @@ exports.MemoryActor = protocol.ActorClassWithSpec(memorySpec, {
getState: actorBridgeWithSpec("getState"),
saveHeapSnapshot: function () {
return this.bridge.saveHeapSnapshot();
saveHeapSnapshot: function (boundaries) {
return this.bridge.saveHeapSnapshot(boundaries);
},
takeCensus: actorBridgeWithSpec("takeCensus"),

View File

@ -620,7 +620,7 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
}
this._state = "attached";
this._debuggerSourcesSeen = new Set();
this._debuggerSourcesSeen = new WeakSet();
Object.assign(this._options, aRequest.options || {});
this.sources.setOptions(this._options);

View File

@ -120,8 +120,8 @@ var Memory = exports.Memory = Class({
*/
_onWindowReady: function ({ isTopLevel }) {
if (this.state == "attached") {
this._clearDebuggees();
if (isTopLevel && this.isRecordingAllocations()) {
this._clearDebuggees();
this._frameCache.initFrames();
}
this.dbg.addDebuggees();
@ -140,15 +140,19 @@ var Memory = exports.Memory = Class({
* Save a heap snapshot scoped to the current debuggees' portion of the heap
* graph.
*
* @param {Object|null} boundaries
*
* @returns {String} The snapshot id.
*/
saveHeapSnapshot: expectState("attached", function () {
saveHeapSnapshot: expectState("attached", function (boundaries = null) {
// If we are observing the whole process, then scope the snapshot
// accordingly. Otherwise, use the debugger's debuggees.
const opts = this.parent instanceof ChromeActor || this.parent instanceof ChildProcessActor
? { runtime: true }
: { debugger: this.dbg };
const path = ThreadSafeChromeUtils.saveHeapSnapshot(opts);
if (!boundaries) {
boundaries = this.parent instanceof ChromeActor || this.parent instanceof ChildProcessActor
? { runtime: true }
: { debugger: this.dbg };
}
const path = ThreadSafeChromeUtils.saveHeapSnapshot(boundaries);
return HeapSnapshotFileUtils.getSnapshotIdFromPath(path);
}, "saveHeapSnapshot"),

View File

@ -35,10 +35,14 @@ const MemoryFront = protocol.FrontClassWithSpec(memorySpec, {
* Always force a bulk data copy of the saved heap snapshot, even when
* the server and client share a file system.
*
* @params {Object|undefined} options.boundaries
* The boundaries for the heap snapshot. See
* ThreadSafeChromeUtils.webidl for more details.
*
* @returns Promise<String>
*/
saveHeapSnapshot: protocol.custom(Task.async(function* (options = {}) {
const snapshotId = yield this._saveHeapSnapshotImpl();
const snapshotId = yield this._saveHeapSnapshotImpl(options.boundaries);
if (!options.forceCopy &&
(yield HeapSnapshotFileUtils.haveHeapSnapshotTempFile(snapshotId))) {

View File

@ -111,6 +111,9 @@ const memorySpec = generateActorSpec({
response: { value: RetVal("number") }
},
saveHeapSnapshot: {
request: {
boundaries: Arg(0, "nullable:json")
},
response: {
snapshotId: RetVal("string")
}

View File

@ -50,12 +50,10 @@ NextFrameSeekTask::NextFrameSeekTask(const void* aDecoderID,
, mAudioQueue(aAudioQueue)
, mVideoQueue(aVideoQueue)
, mCurrentTimeBeforeSeek(aCurrentMediaTime)
, mHasAudio(aInfo.HasAudio())
, mHasVideo(aInfo.HasVideo())
, mDuration(aDuration)
{
AssertOwnerThread();
MOZ_ASSERT(HasVideo());
MOZ_ASSERT(aInfo.HasVideo());
// Configure MediaDecoderReaderWrapper.
SetCallbacks();
@ -67,20 +65,6 @@ NextFrameSeekTask::~NextFrameSeekTask()
MOZ_ASSERT(mIsDiscarded);
}
bool
NextFrameSeekTask::HasAudio() const
{
AssertOwnerThread();
return mHasAudio;
}
bool
NextFrameSeekTask::HasVideo() const
{
AssertOwnerThread();
return mHasVideo;
}
void
NextFrameSeekTask::Discard()
{
@ -194,10 +178,10 @@ bool
NextFrameSeekTask::IsVideoDecoding() const
{
AssertOwnerThread();
return HasVideo() && !mIsVideoQueueFinished;
return !mIsVideoQueueFinished;
}
nsresult
void
NextFrameSeekTask::EnsureVideoDecodeTaskQueued()
{
AssertOwnerThread();
@ -207,11 +191,10 @@ NextFrameSeekTask::EnsureVideoDecodeTaskQueued()
if (!IsVideoDecoding() ||
mReader->IsRequestingVideoData() ||
mReader->IsWaitingVideoData()) {
return NS_OK;
return;
}
RequestVideoData();
return NS_OK;
}
const char*
@ -248,9 +231,7 @@ NextFrameSeekTask::IsAudioSeekComplete()
// Just make sure that we are not requesting or waiting for audio data. We
// don't really need to get an decoded audio data or get EOS here.
return
!HasAudio() ||
(Exists() && !mReader->IsRequestingAudioData() && !mReader->IsWaitingAudioData());
return !mReader->IsRequestingAudioData() && !mReader->IsWaitingAudioData();
}
bool
@ -260,8 +241,7 @@ NextFrameSeekTask::IsVideoSeekComplete()
SAMPLE_LOG("IsVideoSeekComplete() curTarVal=%d vqFin=%d vqSz=%d",
mSeekJob.Exists(), mIsVideoQueueFinished, !!mSeekedVideoData);
return
!HasVideo() || (Exists() && (mIsVideoQueueFinished || mSeekedVideoData));
return mIsVideoQueueFinished || mSeekedVideoData;
}
void
@ -272,12 +252,9 @@ NextFrameSeekTask::CheckIfSeekComplete()
const bool audioSeekComplete = IsAudioSeekComplete();
const bool videoSeekComplete = IsVideoSeekComplete();
if (HasVideo() && !videoSeekComplete) {
if (!videoSeekComplete) {
// We haven't reached the target. Ensure we have requested another sample.
if (NS_FAILED(EnsureVideoDecodeTaskQueued())) {
DECODER_WARN("Failed to request video during seek");
RejectIfExist(__func__);
}
EnsureVideoDecodeTaskQueued();
}
SAMPLE_LOG("CheckIfSeekComplete() audioSeekComplete=%d videoSeekComplete=%d",
@ -294,6 +271,7 @@ NextFrameSeekTask::OnAudioDecoded(MediaData* aAudioSample)
{
AssertOwnerThread();
MOZ_ASSERT(aAudioSample);
MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
// The MDSM::mDecodedAudioEndTime will be updated once the whole SeekTask is
// resolved.
@ -303,11 +281,6 @@ NextFrameSeekTask::OnAudioDecoded(MediaData* aAudioSample)
aAudioSample->GetEndTime(),
aAudioSample->mDiscontinuity);
if (!Exists()) {
// We've received a sample from a previous decode. Discard it.
return;
}
// We accept any audio data here.
mSeekedAudioData = aAudioSample;
@ -318,12 +291,9 @@ void
NextFrameSeekTask::OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
{
AssertOwnerThread();
SAMPLE_LOG("OnAudioNotDecoded (aReason=%u)", aReason);
MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
if (!Exists()) {
// We've received a sample from a previous decode. Discard it.
return;
}
SAMPLE_LOG("OnAudioNotDecoded (aReason=%u)", aReason);
// We don't really handle audio deocde error here. Let MDSM to trigger further
// audio decoding tasks if it needs to play audio, and MDSM will then receive
@ -337,6 +307,7 @@ NextFrameSeekTask::OnVideoDecoded(MediaData* aVideoSample)
{
AssertOwnerThread();
MOZ_ASSERT(aVideoSample);
MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
// The MDSM::mDecodedVideoEndTime will be updated once the whole SeekTask is
// resolved.
@ -346,11 +317,6 @@ NextFrameSeekTask::OnVideoDecoded(MediaData* aVideoSample)
aVideoSample->GetEndTime(),
aVideoSample->mDiscontinuity);
if (!Exists()) {
// We've received a sample from a previous decode. Discard it.
return;
}
if (aVideoSample->mTime > mCurrentTimeBeforeSeek) {
mSeekedVideoData = aVideoSample;
}
@ -362,12 +328,9 @@ void
NextFrameSeekTask::OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
{
AssertOwnerThread();
SAMPLE_LOG("OnVideoNotDecoded (aReason=%u)", aReason);
MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
if (!Exists()) {
// We've received a sample from a previous decode. Discard it.
return;
}
SAMPLE_LOG("OnVideoNotDecoded (aReason=%u)", aReason);
if (aReason == MediaDecoderReader::DECODE_ERROR) {
if (mVideoQueue.GetSize() > 0) {
@ -389,14 +352,7 @@ NextFrameSeekTask::OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReaso
// If the decoder is waiting for data, we tell it to call us back when the
// data arrives.
if (aReason == MediaDecoderReader::WAITING_FOR_DATA) {
MOZ_ASSERT(mReader->IsWaitForDataSupported(),
"Readers that send WAITING_FOR_DATA need to implement WaitForData");
mReader->WaitForData(MediaData::VIDEO_DATA);
// We are out of data to decode and will enter buffering mode soon.
// We want to play the frames we have already decoded, so we stop pre-rolling
// and ensure that loadeddata is fired as required.
mNeedToStopPrerollingVideo = true;
return;
}

View File

@ -43,13 +43,9 @@ public:
private:
~NextFrameSeekTask();
bool HasAudio() const;
bool HasVideo() const;
bool IsVideoDecoding() const;
nsresult EnsureVideoDecodeTaskQueued();
void EnsureVideoDecodeTaskQueued();
const char* VideoRequestStatus();
@ -87,8 +83,6 @@ private:
* Internal state.
*/
const int64_t mCurrentTimeBeforeSeek;
const bool mHasAudio;
const bool mHasVideo;
media::TimeUnit mDuration;
MediaEventListener mAudioCallback;

View File

@ -200,7 +200,7 @@ public:
virtual already_AddRefed<DataTextureSource>
CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) { return nullptr; }
virtual bool Initialize() = 0;
virtual bool Initialize(nsCString* const out_failureReason) = 0;
virtual void Destroy();
bool IsDestroyed() const { return mIsDestroyed; }

View File

@ -799,15 +799,13 @@ Layer::CalculateScissorRect(const RenderTargetIntRect& aCurrentScissorRect)
return currentClip;
}
if (GetVisibleRegion().IsEmpty()) {
if (GetLocalVisibleRegion().IsEmpty() &&
!(AsLayerComposite() && AsLayerComposite()->NeedToDrawCheckerboarding())) {
// When our visible region is empty, our parent may not have created the
// intermediate surface that we would require for correct clipping; however,
// this does not matter since we are invisible.
// Note that we do not use GetLocalVisibleRegion(), because that can be
// empty for a layer whose rendered contents have been async-scrolled
// completely offscreen, but for which we still need to draw a
// checkerboarding backround color, and calculating an empty scissor rect
// for such a layer would prevent that (see bug 1247452 comment 10).
// Make sure we still compute a clip rect if we want to draw checkboarding
// for this layer, since we want to do this even if the layer is invisible.
return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
}

View File

@ -96,7 +96,7 @@ BasicCompositor::~BasicCompositor()
}
bool
BasicCompositor::Initialize()
BasicCompositor::Initialize(nsCString* const out_failureReason)
{
return mWidget ? mWidget->InitCompositor(this) : false;
};

View File

@ -50,7 +50,7 @@ public:
virtual BasicCompositor* AsBasicCompositor() override { return this; }
virtual bool Initialize() override;
virtual bool Initialize(nsCString* const out_failureReason) override;
virtual void DetachWidget() override;

View File

@ -53,24 +53,6 @@ namespace layers {
using namespace gfx;
static bool
LayerHasCheckerboardingAPZC(Layer* aLayer, Color* aOutColor)
{
for (LayerMetricsWrapper i(aLayer, LayerMetricsWrapper::StartAt::BOTTOM); i; i = i.GetParent()) {
if (!i.Metrics().IsScrollable()) {
continue;
}
if (i.GetApzc() && i.GetApzc()->IsCurrentlyCheckerboarding()) {
if (aOutColor) {
*aOutColor = i.Metadata().GetBackgroundColor();
}
return true;
}
break;
}
return false;
}
static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
LayerManagerComposite* aManager,
Layer* aLayer)
@ -357,15 +339,6 @@ ContainerRenderVR(ContainerT* aContainer,
DUMP("<<< ContainerRenderVR [%p]\n", aContainer);
}
static bool
NeedToDrawCheckerboardingForLayer(Layer* aLayer, Color* aOutCheckerboardingColor)
{
return (aLayer->Manager()->AsyncPanZoomEnabled() &&
aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
aLayer->IsOpaqueForVisibility() &&
LayerHasCheckerboardingAPZC(aLayer, aOutCheckerboardingColor);
}
/* all of the prepared data that we need in RenderLayer() */
struct PreparedData
{
@ -412,7 +385,7 @@ ContainerPrepare(ContainerT* aContainer,
// may be null which is not allowed.
if (!layerToRender->GetLayer()->AsContainerLayer()) {
if (!layerToRender->GetLayer()->IsVisible() &&
!NeedToDrawCheckerboardingForLayer(layerToRender->GetLayer(), nullptr)) {
!layerToRender->NeedToDrawCheckerboarding(nullptr)) {
CULLING_LOG("Sublayer %p has no effective visible region\n", layerToRender->GetLayer());
continue;
}
@ -635,7 +608,7 @@ RenderLayers(ContainerT* aContainer,
}
Color color;
if (NeedToDrawCheckerboardingForLayer(layer, &color)) {
if (layerToRender->NeedToDrawCheckerboarding(&color)) {
if (gfxPrefs::APZHighlightCheckerboardedAreas()) {
color = Color(255 / 255.f, 188 / 255.f, 217 / 255.f, 1.f); // "Cotton Candy"
}
@ -967,7 +940,6 @@ RefLayerComposite::Prepare(const RenderTargetIntRect& aClipRect)
ContainerPrepare(this, mCompositeManager, aClipRect);
}
void
RefLayerComposite::CleanupResources()
{

View File

@ -22,6 +22,7 @@
#include "TiledContentHost.h"
#include "Units.h" // for ScreenIntRect
#include "UnitTransforms.h" // for ViewAs
#include "apz/src/AsyncPanZoomController.h" // for AsyncPanZoomController
#include "gfxPrefs.h" // for gfxPrefs
#ifdef XP_MACOSX
#include "gfxPlatformMac.h"
@ -1603,6 +1604,35 @@ LayerComposite::HasStaleCompositor() const
return mCompositeManager->GetCompositor() != mCompositor;
}
static bool
LayerHasCheckerboardingAPZC(Layer* aLayer, Color* aOutColor)
{
bool answer = false;
for (LayerMetricsWrapper i(aLayer, LayerMetricsWrapper::StartAt::BOTTOM); i; i = i.GetParent()) {
if (!i.Metrics().IsScrollable()) {
continue;
}
if (i.GetApzc() && i.GetApzc()->IsCurrentlyCheckerboarding()) {
if (aOutColor) {
*aOutColor = i.Metadata().GetBackgroundColor();
}
answer = true;
break;
}
break;
}
return answer;
}
bool
LayerComposite::NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor)
{
return GetLayer()->Manager()->AsyncPanZoomEnabled() &&
(GetLayer()->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
GetLayer()->IsOpaqueForVisibility() &&
LayerHasCheckerboardingAPZC(GetLayer(), aOutCheckerboardingColor);
}
#ifndef MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
/*static*/ bool

View File

@ -560,6 +560,12 @@ public:
*/
virtual nsIntRegion GetFullyRenderedRegion();
/**
* Return true if a checkerboarding background color needs to be drawn
* for this layer.
*/
bool NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor = nullptr);
protected:
gfx::Matrix4x4 mShadowTransform;
LayerIntRegion mShadowVisibleRegion;

View File

@ -195,7 +195,7 @@ CompositorD3D11::~CompositorD3D11()
}
bool
CompositorD3D11::Initialize()
CompositorD3D11::Initialize(nsCString* const out_failureReason)
{
ScopedGfxFeatureReporter reporter("D3D11 Layers");
@ -204,6 +204,7 @@ CompositorD3D11::Initialize()
HRESULT hr;
if (!gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&mDevice)) {
*out_failureReason = "FEATURE_FAILURE_D3D11_NO_DEVICE";
return false;
}
@ -211,6 +212,7 @@ CompositorD3D11::Initialize()
if (!mContext) {
gfxCriticalNote << "[D3D11] failed to get immediate context";
*out_failureReason = "FEATURE_FAILURE_D3D11_CONTEXT";
return false;
}
@ -250,6 +252,7 @@ CompositorD3D11::Initialize()
getter_AddRefs(mAttachments->mInputLayout));
if (Failed(hr, "CreateInputLayout")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_INPUT_LAYOUT";
return false;
}
@ -261,10 +264,12 @@ CompositorD3D11::Initialize()
hr = mDevice->CreateBuffer(&bufferDesc, &data, getter_AddRefs(mAttachments->mVertexBuffer));
if (Failed(hr, "create vertex buffer")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_VERTEX_BUFFER";
return false;
}
if (!mAttachments->CreateShaders()) {
*out_failureReason = "FEATURE_FAILURE_D3D11_CREATE_SHADERS";
return false;
}
@ -275,12 +280,14 @@ CompositorD3D11::Initialize()
hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mAttachments->mVSConstantBuffer));
if (Failed(hr, "create vs buffer")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_VS_BUFFER";
return false;
}
cBufferDesc.ByteWidth = sizeof(PixelShaderConstants);
hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mAttachments->mPSConstantBuffer));
if (Failed(hr, "create ps buffer")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_PS_BUFFER";
return false;
}
@ -290,18 +297,21 @@ CompositorD3D11::Initialize()
hr = mDevice->CreateRasterizerState(&rastDesc, getter_AddRefs(mAttachments->mRasterizerState));
if (Failed(hr, "create rasterizer")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_RASTERIZER";
return false;
}
CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
hr = mDevice->CreateSamplerState(&samplerDesc, getter_AddRefs(mAttachments->mLinearSamplerState));
if (Failed(hr, "create linear sampler")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_LINEAR_SAMPLER";
return false;
}
samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
hr = mDevice->CreateSamplerState(&samplerDesc, getter_AddRefs(mAttachments->mPointSamplerState));
if (Failed(hr, "create point sampler")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_POINT_SAMPLER";
return false;
}
@ -315,6 +325,7 @@ CompositorD3D11::Initialize()
blendDesc.RenderTarget[0] = rtBlendPremul;
hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mPremulBlendState));
if (Failed(hr, "create pm blender")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_PM_BLENDER";
return false;
}
@ -327,6 +338,7 @@ CompositorD3D11::Initialize()
blendDesc.RenderTarget[0] = rtBlendNonPremul;
hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mNonPremulBlendState));
if (Failed(hr, "create npm blender")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_NPM_BLENDER";
return false;
}
@ -344,6 +356,7 @@ CompositorD3D11::Initialize()
blendDesc.RenderTarget[0] = rtBlendComponent;
hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mComponentBlendState));
if (Failed(hr, "create component blender")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_COMP_BLENDER";
return false;
}
}
@ -357,13 +370,15 @@ CompositorD3D11::Initialize()
blendDesc.RenderTarget[0] = rtBlendDisabled;
hr = mDevice->CreateBlendState(&blendDesc, getter_AddRefs(mAttachments->mDisabledBlendState));
if (Failed(hr, "create null blender")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_NULL_BLENDER";
return false;
}
if (!mAttachments->InitSyncObject()) {
*out_failureReason = "FEATURE_FAILURE_D3D11_OBJ_SYNC";
return false;
}
//
// VR additions
//
@ -389,6 +404,7 @@ CompositorD3D11::Initialize()
cBufferDesc.ByteWidth = sizeof(gfx::VRDistortionConstants);
hr = mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mAttachments->mVRDistortionConstants));
if (Failed(hr, "create vr buffer ")) {
*out_failureReason = "FEATURE_FAILURE_D3D11_VR_BUFFER";
return false;
}
}
@ -427,7 +443,8 @@ CompositorD3D11::Initialize()
*/
hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc, getter_AddRefs(mSwapChain));
if (Failed(hr, "create swap chain")) {
return false;
*out_failureReason = "FEATURE_FAILURE_D3D11_SWAP_CHAIN";
return false;
}
// We need this because we don't want DXGI to respond to Alt+Enter.
@ -436,10 +453,12 @@ CompositorD3D11::Initialize()
}
if (!mWidget->InitCompositor(this)) {
*out_failureReason = "FEATURE_FAILURE_D3D11_INIT_COMPOSITOR";
return false;
}
reporter.SetSuccessful();
return true;
}

View File

@ -47,7 +47,7 @@ public:
virtual CompositorD3D11* AsCompositorD3D11() override { return this; }
virtual bool Initialize() override;
virtual bool Initialize(nsCString* const out_failureReason) override;
virtual TextureFactoryIdentifier
GetTextureFactoryIdentifier() override;

View File

@ -38,25 +38,29 @@ CompositorD3D9::~CompositorD3D9()
}
bool
CompositorD3D9::Initialize()
CompositorD3D9::Initialize(nsCString* const out_failureReason)
{
ScopedGfxFeatureReporter reporter("D3D9 Layers");
mDeviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager();
if (!mDeviceManager) {
*out_failureReason = "FEATURE_FAILURE_D3D9_DEVICE_MANAGER";
return false;
}
mSwapChain = mDeviceManager->CreateSwapChain(mWidget->AsWindows()->GetHwnd());
if (!mSwapChain) {
*out_failureReason = "FEATURE_FAILURE_D3D9_SWAP_CHAIN";
return false;
}
if (!mWidget->InitCompositor(this)) {
*out_failureReason = "FEATURE_FAILURE_D3D9_INIT_COMPOSITOR";
return false;
}
reporter.SetSuccessful();
return true;
}

View File

@ -26,7 +26,7 @@ public:
virtual CompositorD3D9* AsCompositorD3D9() override { return this; }
virtual bool Initialize() override;
virtual bool Initialize(nsCString* const out_failureReason) override;
virtual TextureFactoryIdentifier
GetTextureFactoryIdentifier() override;

View File

@ -72,6 +72,7 @@
#include "mozilla/Hal.h"
#include "mozilla/HalTypes.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Telemetry.h"
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "ProfilerMarkers.h"
#endif
@ -1519,8 +1520,22 @@ CompositorBridgeParent::NewCompositor(const nsTArray<LayersBackend>& aBackendHin
compositor = new CompositorD3D9(this, mWidget);
#endif
}
if (compositor && compositor->Initialize()) {
nsCString failureReason;
if (compositor && compositor->Initialize(&failureReason)) {
if (failureReason.IsEmpty()){
failureReason = "SUCCESS";
}
if (compositor->GetBackendType() == LayersBackend::LAYERS_OPENGL){
Telemetry::Accumulate(Telemetry::OPENGL_COMPOSITING_FAILURE_ID, failureReason);
}
#ifdef XP_WIN
else if (compositor->GetBackendType() == LayersBackend::LAYERS_D3D9){
Telemetry::Accumulate(Telemetry::D3D9_COMPOSITING_FAILURE_ID, failureReason);
}
else if (compositor->GetBackendType() == LayersBackend::LAYERS_D3D11){
Telemetry::Accumulate(Telemetry::D3D11_COMPOSITING_FAILURE_ID, failureReason);
}
#endif
compositor->SetCompositorID(mCompositorID);
return compositor;
}

View File

@ -227,7 +227,7 @@ CompositorOGL::CleanupResources()
}
bool
CompositorOGL::Initialize()
CompositorOGL::Initialize(nsCString* const out_failureReason)
{
ScopedGfxFeatureReporter reporter("GL Layers");
@ -237,12 +237,16 @@ CompositorOGL::Initialize()
mGLContext = CreateContext();
#ifdef MOZ_WIDGET_ANDROID
if (!mGLContext)
if (!mGLContext){
*out_failureReason = "FEATURE_FAILURE_OPENGL_NO_ANDROID_CONTEXT";
NS_RUNTIMEABORT("We need a context on Android");
}
#endif
if (!mGLContext)
if (!mGLContext){
*out_failureReason = "FEATURE_FAILURE_OPENGL_CREATE_CONTEXT";
return false;
}
MakeCurrent();
@ -258,6 +262,7 @@ CompositorOGL::Initialize()
RefPtr<EffectSolidColor> effect = new EffectSolidColor(Color(0, 0, 0, 0));
ShaderConfigOGL config = GetShaderConfigFor(effect);
if (!GetShaderProgramFor(config)) {
*out_failureReason = "FEATURE_FAILURE_OPENGL_COMPILE_SHADER";
return false;
}
@ -332,6 +337,7 @@ CompositorOGL::Initialize()
if (mFBOTextureTarget == LOCAL_GL_NONE) {
/* Unable to find a texture target that works with FBOs and NPOT textures */
*out_failureReason = "FEATURE_FAILURE_OPENGL_NO_TEXTURE_TARGET";
return false;
}
} else {
@ -349,6 +355,7 @@ CompositorOGL::Initialize()
* texture2DRect).
*/
if (!mGLContext->IsExtensionSupported(gl::GLContext::ARB_texture_rectangle))
*out_failureReason = "FEATURE_FAILURE_OPENGL_ARB_EXT";
return false;
}
@ -425,6 +432,7 @@ CompositorOGL::Initialize()
}
reporter.SetSuccessful();
return true;
}

View File

@ -206,7 +206,7 @@ public:
virtual already_AddRefed<DataTextureSource>
CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
virtual bool Initialize() override;
virtual bool Initialize(nsCString* const out_failureReason) override;
virtual void Destroy() override;

View File

@ -138,8 +138,8 @@ static already_AddRefed<Compositor> CreateTestCompositor(LayersBackend backend,
MOZ_CRASH(); // No support yet
#endif
}
if (!compositor || !compositor->Initialize()) {
nsCString failureReason;
if (!compositor || !compositor->Initialize(&failureReason)) {
printf_stderr("Failed to construct layer manager for the requested backend\n");
abort();
}

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@ var server = spdy.createServer(options, function(req, res) {
res.end('hello world!');
});
server.listen(443);
server.listen(3000);
```
Client:
@ -80,7 +80,7 @@ app.use(/* your favorite middleware */);
var server = spdy.createServer(options, app);
server.listen(443);
server.listen(3000);
```
## API
@ -120,9 +120,35 @@ spdy.createServer(options, function(req, res) {
stream.end('alert("hello from push stream!");');
res.end('<script src="/main.js"></script>');
}).listen(443);
}).listen(3000);
```
The method is also avaliable when using SPDY with an Express server:
```javascript
var app = express();
var server = spdy.createServer(options, app);
app.get('/', function(req, res) {
var headers = { 'content-type': 'application/javascript' };
res.push('/main.js', headers, function(err, stream) {
stream.on('acknowledge', function() {
});
stream.on('error', function() {
});
stream.end('alert("hello from push stream!");');
});
res.end('<script src="/main.js"></script>');
});
server.listen(3000);
```
Push is accomplished via the `push()` method invoked on the current response
object (this works for express.js response objects as well). The format of the
`push()` method is:

View File

@ -171,7 +171,6 @@ proto.createConnection = function createConnection(options) {
options.spdy.decompress
});
state.id += 2;
state.connection._addStream(stream);
return stream;
};

View File

@ -169,6 +169,9 @@ Connection.prototype._init = function init() {
// for both legacy and non-legacy, when our socket is ready for writes again,
// drain the sinks in case they were queuing due to socketBuffering.
this.socket.on('drain', function () {
if (!state.socketBuffering)
return;
state.socketBuffering = false;
Object.keys(state.streams).forEach(function(id) {
state.streams[id]._drainSink(0);
@ -298,7 +301,6 @@ Connection.prototype._handleSynStream = function handleSynStream(frame) {
}
var stream = new Stream(this, frame);
this._addStream(stream);
// Associate streams
if (associated) {
@ -556,6 +558,8 @@ Connection.prototype._handlePing = function handlePing(id) {
state.framer.pingFrame(id, function(err, frame) {
if (err)
return self.emit('error', err);
self.emit('ping', id);
self.write(frame);
});
return;

View File

@ -4,6 +4,8 @@ var Buffer = require('buffer').Buffer;
var EventEmitter = require('events').EventEmitter;
var constants = require('./').constants;
var empty = new Buffer(0);
function Framer() {
EventEmitter.call(this);
@ -310,7 +312,7 @@ Framer.prototype.dataFrame = function dataFrame(id, fin, data, callback) {
}
if (!fin && !data.length)
return callback(null, []);
return callback(null, empty);
var frame = new Buffer(8 + data.length);

View File

@ -59,6 +59,40 @@ parser.create = function create(connection) {
Parser.prototype.destroy = function destroy() {
};
Parser.prototype._slice = function _slice(waiting) {
var buffer = new Buffer(waiting);
var sliced = 0;
var offset = 0;
while (waiting > offset && sliced < this.buffer.length) {
var chunk = this.buffer[sliced++],
overmatched = false;
// Copy chunk into `buffer`
if (chunk.length > waiting - offset) {
chunk.copy(buffer, offset, 0, waiting - offset);
this.buffer[--sliced] = chunk.slice(waiting - offset);
this.buffered += this.buffer[sliced].length;
overmatched = true;
} else {
chunk.copy(buffer, offset);
}
// Move offset and decrease amount of buffered data
offset += chunk.length;
this.buffered -= chunk.length;
if (overmatched) break;
}
// Remove used buffers
this.buffer = this.buffer.slice(sliced);
return buffer;
};
//
// ### function _write (data, encoding, cb)
// #### @data {Buffer} chunk of data
@ -83,48 +117,36 @@ Parser.prototype._write = function write(data, encoding, cb) {
return false;
}
if (this.needDrain) {
// Mark parser as drained
this.needDrain = false;
this.emit('drain');
}
// We shall not do anything until we get all expected data
if (this.buffered < this.waiting) {
if (this.needDrain) {
// Mark parser as drained
this.needDrain = false;
this.emit('drain');
// Emit DATA by chunks as they come
if (this.buffered !== 0 &&
this.state.header &&
!this.state.header.control) {
var buffer = this._slice(this.buffered);
this.waiting -= buffer.length;
this.emit('frame', {
type: 'DATA',
id: this.state.header.id,
fin: false,
compressed: (this.state.header.flags & 0x02) === 0x02,
data: buffer
});
}
cb();
return;
}
var self = this,
buffer = new Buffer(this.waiting),
sliced = 0,
offset = 0;
while (this.waiting > offset && sliced < this.buffer.length) {
var chunk = this.buffer[sliced++],
overmatched = false;
// Copy chunk into `buffer`
if (chunk.length > this.waiting - offset) {
chunk.copy(buffer, offset, 0, this.waiting - offset);
this.buffer[--sliced] = chunk.slice(this.waiting - offset);
this.buffered += this.buffer[sliced].length;
overmatched = true;
} else {
chunk.copy(buffer, offset);
}
// Move offset and decrease amount of buffered data
offset += chunk.length;
this.buffered -= chunk.length;
if (overmatched) break;
}
// Remove used buffers
this.buffer = this.buffer.slice(sliced);
var self = this;
var buffer = this._slice(this.waiting);
// Executed parser for buffered data
this.paused = true;
@ -232,6 +254,7 @@ Parser.prototype.execute = function execute(state, data, callback) {
self.emit('frame', frame);
state.type = 'frame-head';
state.header = null;
callback(null, 8);
};
}

View File

@ -165,7 +165,6 @@ exports.push = function push(url, headers, priority, callback) {
});
stream.associated = socket;
socket.connection._addStream(stream);
socket._lock(function() {
this._spdyState.framer.streamFrame(

View File

@ -7,6 +7,8 @@ var stream = require('stream');
var Buffer = require('buffer').Buffer;
var constants = spdy.protocol.constants;
var empty = new Buffer(0);
if (!/^v(0\.10|0\.8|0\.9)\./.test(process.version))
var httpCommon = require('_http_common');
@ -50,6 +52,15 @@ function Stream(connection, options) {
state.id = options.id;
state.associated = options.associated;
state.headers = options.headers || {};
// Protocol standard compliance:
// Client does not have to send this header, we must assume,
// that it can handle compression
if (connection._spdyState.isServer && !state.associated &&
!state.headers['accept-encoding']) {
state.headers['accept-encoding'] = 'gzip, deflate';
}
if (connection._spdyState.xForward !== null)
state.headers['x-forwarded-for'] = connection._spdyState.xForward;
@ -58,6 +69,7 @@ function Stream(connection, options) {
connection._spdyState.counters.streamCount++;
if (state.isPush)
connection._spdyState.counters.pushCount++;
connection._addStream(this);
// Useful for PUSH streams
state.scheme = state.headers.scheme;
@ -111,8 +123,25 @@ exports.Stream = Stream;
// Parser lookup methods
Stream.prototype._parserHeadersComplete = function parserHeadersComplete() {
var method;
if (this.parser)
return this.parser.onHeadersComplete || this.parser[1];
method = this.parser.onHeadersComplete || this.parser[1];
if (!method || !utils.isArgvParser)
return method;
return function onHeadersCompleteWrap(info) {
// NOTE: shouldKeepAlive = false
return method.call(this,
info.versionMajor,
info.versionMinor,
info.headers,
info.method,
info.url,
info.statusCode,
info.statusMessage,
info.upgrade,
false);
};
};
Stream.prototype._parserBody = function parserBody() {
@ -154,7 +183,7 @@ Stream.prototype._init = function init() {
return this.once('_chunkDone', onfinish);
var self = this;
this._writeData(true, [], function() {
this._writeData(true, empty, function() {
state.closedBy.us = true;
if (state.sinkBuffer.length !== 0)
return;
@ -716,7 +745,6 @@ Stream.prototype._parseClientRequest = function parseClientRequest(data, cb) {
}
connection.write(frame);
self._unlock();
connection._addStream(self);
self.emit('_spdyRequest');
state.initialized = true;

View File

@ -12,6 +12,8 @@ if (utils.isLegacy)
else
utils.DuplexStream = stream.Duplex;
utils.isArgvParser = !/^v(0\.12|0\.11|0\.10|0\.8|0\.9)\./.test(process.version);
//
// ### function createDeflate (version, compression)
// #### @version {Number} SPDY version
@ -44,7 +46,7 @@ utils.createInflate = function createInflate(version) {
var inflate = zlib.createInflate({
dictionary: spdy.protocol.dictionary[version],
flush: zlib.Z_SYNC_FLUSH,
windowBits: 15
windowBits: 0
});
// Define lock information early

View File

@ -1,6 +1,6 @@
{
"name": "spdy",
"version": "1.29.1",
"version": "1.32.5",
"description": "Implementation of the SPDY protocol on node.js.",
"license": "MIT",
"keywords": [
@ -13,7 +13,7 @@
"homepage": "https://github.com/indutny/node-spdy",
"bugs": {
"email": "node-spdy+bugs@indutny.com",
"url": "https://github.com/indunty/node-spdy/issues"
"url": "https://github.com/indutny/node-spdy/issues"
},
"author": "Fedor Indutny <fedor.indutny@gmail.com>",
"contributors": [

View File

@ -17,6 +17,13 @@ suite('A SPDY Server / Connect', function() {
req.url === '/deflate' ? zlib.createDeflate() :
null;
if (req.url == '/headerTest') {
if (req.headers['accept-encoding'] == 'gzip, deflate')
return res.end(fox);
else
return res.end();
}
// Terminate connection gracefully
if (req.url === '/goaway')
req.socket.connection.end();
@ -202,4 +209,31 @@ suite('A SPDY Server / Connect', function() {
done();
});
});
test('should add accept-encoding header to request headers, ' +
'if none present',
function(done) {
var agent = spdy.createAgent({
host: '127.0.0.1',
port: PORT,
rejectUnauthorized: false
});
var req = https.request({
path: '/headerTest',
method: 'GET',
agent: agent,
}, function(res) {
var total = '';
res.on('data', function(chunk){
total += chunk;
});
res.once('end', function() {
agent.close();
assert.equal(total, fox);
done();
});
});
req.end();
});
});

View File

@ -368,4 +368,44 @@ suite('A SPDY Server / Stream', function() {
pair.client.req.end();
});
}
test('it should not attempt to send empty arrays', function(done) {
var server = spdy.createServer(keys);
var agent;
server.on('request', function(req, res) {
setTimeout(function() {
res.end();
done();
server.close();
agent.close();
}, 100);
});
server.listen(PORT, function() {
agent = spdy.createAgent({
port: PORT,
rejectUnauthorized: false
}).on('error', function(err) {
done(err);
});
var body = new Buffer([1,2,3,4]);
var opts = {
method: 'POST',
headers: {
'Host': 'localhost',
'Content-Length': body.length
},
path: '/some-url',
agent: agent
};
var req = https.request(opts, function(res) {
}).on('error', function(err) {
});
req.end(body);
});
});
});

View File

@ -9966,6 +9966,30 @@
"bug_numbers": [1276714, 1276716],
"description": "Number of times about:crashes has been opened.",
"releaseChannelCollection": "opt-out"
},
"D3D9_COMPOSITING_FAILURE_ID": {
"alert_emails": ["bgirard@mozilla.com"],
"expires_in_version": "never",
"kind": "count",
"keyed": true,
"description": "D3D9 compositor runtime and dynamic failure IDs. This will record a count for each context creation success or failure. Each failure id is a unique identifier that can be traced back to a particular failure branch or blocklist rule.",
"bug_numbers": [1002846]
},
"D3D11_COMPOSITING_FAILURE_ID": {
"alert_emails": ["bgirard@mozilla.com"],
"expires_in_version": "never",
"kind": "count",
"keyed": true,
"description": "D3D11 compositor runtime and dynamic failure IDs. This will record a count for each context creation success or failure. Each failure id is a unique identifier that can be traced back to a particular failure branch or blocklist rule.",
"bug_numbers": [1002846]
},
"OPENGL_COMPOSITING_FAILURE_ID": {
"alert_emails": ["bgirard@mozilla.com"],
"expires_in_version": "never",
"kind": "count",
"keyed": true,
"description": "OpenGL compositor runtime and dynamic failure IDs. This will record a count for each context creation success or failure. Each failure id is a unique identifier that can be traced back to a particular failure branch or blocklist rule.",
"bug_numbers": [1002846]
},
"XHR_IN_WORKER": {
"alert_emails": ["amarchesini@mozilla.com"],

View File

@ -955,6 +955,8 @@ PopupNotifications.prototype = {
if (this._currentNotifications.length == 0)
return;
event.stopPropagation();
// Get the anchor that is the immediate child of the icon box
let anchor = event.target;
while (anchor && anchor.parentNode != this.iconBox)

View File

@ -3923,6 +3923,9 @@ this.XPIProvider = {
* same ID is already temporarily installed
*/
installTemporaryAddon: Task.async(function*(aFile) {
if (aFile.exists() && aFile.isFile()) {
flushJarCache(aFile);
}
let addon = yield loadManifestFromFile(aFile, TemporaryInstallLocation);
if (!addon.bootstrap) {

View File

@ -127,14 +127,13 @@ add_task(function*() {
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
// test that an unpacked add-on works too
let tempdir = gTmpD.clone();
// test that an unpacked add-on works too
writeInstallRDFToDir({
id: ID,
version: "2.0",
version: "3.0",
bootstrap: true,
unpack: true,
targetApplications: [{
id: "xpcshell@tests.mozilla.org",
minVersion: "1",
@ -150,14 +149,14 @@ add_task(function*() {
yield AddonManager.installTemporaryAddon(unpacked_addon);
BootstrapMonitor.checkAddonInstalled(ID, "2.0");
BootstrapMonitor.checkAddonStarted(ID, "2.0");
BootstrapMonitor.checkAddonInstalled(ID, "3.0");
BootstrapMonitor.checkAddonStarted(ID, "3.0");
addon = yield promiseAddonByID(ID);
// temporary add-on is installed and started
do_check_neq(addon, null);
do_check_eq(addon.version, "2.0");
do_check_eq(addon.version, "3.0");
do_check_eq(addon.name, "Test Bootstrap 1 (temporary)");
do_check_true(addon.isCompatible);
do_check_false(addon.appDisabled);
@ -183,6 +182,130 @@ add_task(function*() {
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
unpacked_addon.remove(true);
// on Windows XPI files will be locked by the JAR cache, skip this test there.
if (!("nsIWindowsRegKey" in Components.interfaces)) {
// test that a packed (XPI) add-on works
writeInstallRDFToXPI({
id: ID,
version: "2.0",
bootstrap: true,
targetApplications: [{
id: "xpcshell@tests.mozilla.org",
minVersion: "1",
maxVersion: "1"
}],
name: "Test Bootstrap 1 (temporary)",
}, tempdir, "bootstrap1@tests.mozilla.org");
let packed_addon = tempdir.clone();
packed_addon.append(ID + ".xpi");
yield AddonManager.installTemporaryAddon(packed_addon);
addon = yield promiseAddonByID(ID);
// temporary add-on is installed and started
do_check_neq(addon, null);
do_check_eq(addon.version, "2.0");
do_check_eq(addon.name, "Test Bootstrap 1 (temporary)");
do_check_true(addon.isCompatible);
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
restartManager();
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
BootstrapMonitor.checkAddonStarted(ID, "1.0");
addon = yield promiseAddonByID(ID);
// existing add-on is back
do_check_neq(addon, null);
do_check_eq(addon.version, "1.0");
do_check_eq(addon.name, "Test Bootstrap 1");
do_check_true(addon.isCompatible);
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
packed_addon.remove(false);
// test that a webextension works
let webext = createTempWebExtensionFile({
manifest: {
version: "4.0",
name: "Test WebExtension 1 (temporary)",
applications: {
gecko: {
id: ID
}
}
}
});
yield AddonManager.installTemporaryAddon(webext);
addon = yield promiseAddonByID(ID);
// temporary add-on is installed and started
do_check_neq(addon, null);
do_check_eq(addon.version, "4.0");
do_check_eq(addon.name, "Test WebExtension 1 (temporary)");
do_check_true(addon.isCompatible);
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
// test that re-loading a webextension works, using the same filename
webext.remove(false);
webext = createTempWebExtensionFile({
manifest: {
version: "5.0",
name: "Test WebExtension 1 (temporary)",
applications: {
gecko: {
id: ID
}
}
}
});
yield AddonManager.installTemporaryAddon(webext);
addon = yield promiseAddonByID(ID);
// temporary add-on is installed and started
do_check_neq(addon, null);
do_check_eq(addon.version, "5.0");
do_check_eq(addon.name, "Test WebExtension 1 (temporary)");
do_check_true(addon.isCompatible);
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
restartManager();
BootstrapMonitor.checkAddonInstalled(ID, "1.0");
BootstrapMonitor.checkAddonStarted(ID, "1.0");
addon = yield promiseAddonByID(ID);
// existing add-on is back
do_check_neq(addon, null);
do_check_eq(addon.version, "1.0");
do_check_eq(addon.name, "Test Bootstrap 1");
do_check_true(addon.isCompatible);
do_check_false(addon.appDisabled);
do_check_true(addon.isActive);
do_check_eq(addon.type, "extension");
do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
}
// remove original add-on
addon.uninstall();
BootstrapMonitor.checkAddonNotInstalled(ID);

View File

@ -32,6 +32,9 @@
#ifdef MOZ_WIDGET_GTK
#include <gtk/gtk.h>
#include <dlfcn.h>
#endif
#if defined (XP_LINUX) && !defined (ANDROID)
#include <unistd.h>
#include <fstream>
#include "mozilla/Tokenizer.h"
@ -73,7 +76,7 @@ NS_EXPORT int android_sdk_version;
// only happens well after that point.
uint32_t nsSystemInfo::gUserUmask = 0;
#if defined (MOZ_WIDGET_GTK)
#if defined (XP_LINUX) && !defined (ANDROID)
static void
SimpleParseKeyValuePairs(const std::string& aFilename,
std::map<nsCString, nsCString>& aKeyValuePairs)
@ -497,7 +500,7 @@ nsSystemInfo::Init()
}
MOZ_ASSERT(sizeof(sysctlValue32) == len);
#elif defined (MOZ_WIDGET_GTK)
#elif defined (XP_LINUX) && !defined (ANDROID)
// Get vendor, family, model, stepping, physical cores, L3 cache size
// from /proc/cpuinfo file
{