Merge mozilla-central to mozilla-inbound
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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">
|
||||
|
@ -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() {
|
||||
|
@ -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");
|
||||
|
@ -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 |
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
@ -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", () =>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 |
@ -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 |
@ -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 |
@ -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;
|
||||
|
Before Width: | Height: | Size: 189 B |
Before Width: | Height: | Size: 263 B |
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 265 B |
Before Width: | Height: | Size: 324 B |
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
|
@ -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)");
|
||||
}
|
||||
}
|
||||
});
|
9
devtools/client/memory/test/browser/doc_empty.html
Normal file
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
</head>
|
||||
<body>
|
||||
This is an empty window.
|
||||
</body>
|
||||
</html>
|
@ -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]
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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";
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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.
|
||||
}
|
@ -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};
|
||||
},
|
||||
};
|
||||
|
@ -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]
|
||||
|
@ -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();
|
||||
});
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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"),
|
||||
|
@ -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);
|
||||
|
@ -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"),
|
||||
|
||||
|
@ -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))) {
|
||||
|
@ -111,6 +111,9 @@ const memorySpec = generateActorSpec({
|
||||
response: { value: RetVal("number") }
|
||||
},
|
||||
saveHeapSnapshot: {
|
||||
request: {
|
||||
boundaries: Arg(0, "nullable:json")
|
||||
},
|
||||
response: {
|
||||
snapshotId: RetVal("string")
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ BasicCompositor::~BasicCompositor()
|
||||
}
|
||||
|
||||
bool
|
||||
BasicCompositor::Initialize()
|
||||
BasicCompositor::Initialize(nsCString* const out_failureReason)
|
||||
{
|
||||
return mWidget ? mWidget->InitCompositor(this) : false;
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -171,7 +171,6 @@ proto.createConnection = function createConnection(options) {
|
||||
options.spdy.decompress
|
||||
});
|
||||
state.id += 2;
|
||||
state.connection._addStream(stream);
|
||||
|
||||
return stream;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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": [
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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"],
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|