Merge autoland to mozilla-central. a=merge

This commit is contained in:
Ciure Andrei 2018-02-23 02:02:51 +02:00
commit d068047dfa
76 changed files with 1356 additions and 1093 deletions

View File

@ -9,6 +9,8 @@
%charsetDTD;
<!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd" >
%textcontextDTD;
<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
%downloadsDTD;
<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
%placesDTD;
<!ENTITY % safebrowsingDTD SYSTEM "chrome://browser/locale/safebrowsing/phishing-afterload-warning-message.dtd">

View File

@ -7,10 +7,12 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/controlcenter/panel.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/customizableui/panelUI.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/downloads/downloads.css"?>
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
@ -496,6 +498,7 @@
#include ../../components/customizableui/content/panelUI.inc.xul
#include ../../components/controlcenter/content/panel.inc.xul
#include ../../components/downloads/content/downloadsPanel.inc.xul
<hbox id="downloads-animation-container" mousethrough="always">
<vbox id="downloads-notification-anchor" hidden="true">
@ -925,7 +928,20 @@
overflows="false"
cui-areatype="toolbar"
hidden="true"
tooltip="dynamic-shortcut-tooltip"/>
tooltip="dynamic-shortcut-tooltip"
indicator="true">
<!-- The panel's anchor area is smaller than the outer button, but must
always be visible and must not move or resize when the indicator
state changes, otherwise the panel could change its position or lose
its arrow unexpectedly. -->
<stack id="downloads-indicator-anchor"
consumeanchor="downloads-button">
<box id="downloads-indicator-icon"/>
<stack id="downloads-indicator-progress-outer">
<box id="downloads-indicator-progress-inner"/>
</stack>
</stack>
</toolbarbutton>
<toolbarbutton id="library-button" class="toolbarbutton-1 chromeclass-toolbar-additional subviewbutton-nav"
removable="true"

View File

@ -195,27 +195,15 @@ class DownloadsSubview extends DownloadsViewUI.BaseView {
// ----- Static methods. -----
/**
* Perform all tasks necessary to be able to show a Downloads Subview.
*
* @param {DOMWindow} window Global window object.
* @return {Promise} Will resolve when all tasks are done.
*/
static init(window) {
return new Promise(resolve =>
window.DownloadsOverlayLoader.ensureOverlayLoaded(window.DownloadsPanel.kDownloadsOverlay, resolve));
}
/**
* Show the Downloads subview panel and listen for events that will trigger
* building the dynamic part of the view.
*
* @param {DOMNode} anchor The button that was commanded to trigger this function.
*/
static async show(anchor) {
static show(anchor) {
let document = anchor.ownerDocument;
let window = anchor.ownerGlobal;
await DownloadsSubview.init(window);
let panelview = document.getElementById("PanelUI-downloads");
anchor.setAttribute("closemenu", "none");

View File

@ -13,10 +13,6 @@
* DownloadsPanel
* Main entry point for the downloads panel interface.
*
* DownloadsOverlayLoader
* Allows loading the downloads panel and the status indicator interfaces on
* demand, to improve startup performance.
*
* DownloadsView
* Builds and updates the downloads list widget, responding to changes in the
* download state and real-time data. In addition, handles part of the user
@ -103,36 +99,19 @@ var DownloadsPanel = {
get kStateWaitingData() {
return 2;
},
/** The panel is almost shown - we're just waiting to get a handle on the
anchor. */
get kStateWaitingAnchor() {
return 3;
},
/** The panel is open. */
get kStateShown() {
return 4;
},
/**
* Location of the panel overlay.
*/
get kDownloadsOverlay() {
return "chrome://browser/content/downloads/downloadsOverlay.xul";
return 3;
},
/**
* Starts loading the download data in background, without opening the panel.
* Use showPanel instead to load the data and open the panel at the same time.
*
* @param aCallback
* Called when initialization is complete.
*/
initialize(aCallback) {
initialize() {
DownloadsCommon.log("Attempting to initialize DownloadsPanel for a window.");
if (this._state != this.kStateUninitialized) {
DownloadsCommon.log("DownloadsPanel is already initialized.");
DownloadsOverlayLoader.ensureOverlayLoaded(this.kDownloadsOverlay,
aCallback);
return;
}
this._state = this.kStateHidden;
@ -145,19 +124,17 @@ var DownloadsPanel = {
// Now that data loading has eventually started, load the required XUL
// elements and initialize our views.
DownloadsCommon.log("Ensuring DownloadsPanel overlay loaded.");
DownloadsOverlayLoader.ensureOverlayLoaded(this.kDownloadsOverlay, () => {
DownloadsViewController.initialize();
DownloadsCommon.log("Attaching DownloadsView...");
DownloadsCommon.getData(window).addView(DownloadsView);
DownloadsCommon.getSummary(window, DownloadsView.kItemCountLimit)
.addView(DownloadsSummary);
DownloadsCommon.log("DownloadsView attached - the panel for this window",
"should now see download items come in.");
DownloadsPanel._attachEventListeners();
DownloadsCommon.log("DownloadsPanel initialized.");
aCallback();
});
this.panel.hidden = false;
DownloadsViewController.initialize();
DownloadsCommon.log("Attaching DownloadsView...");
DownloadsCommon.getData(window).addView(DownloadsView);
DownloadsCommon.getSummary(window, DownloadsView.kItemCountLimit)
.addView(DownloadsSummary);
DownloadsCommon.log("DownloadsView attached - the panel for this window",
"should now see download items come in.");
DownloadsPanel._attachEventListeners();
DownloadsCommon.log("DownloadsPanel initialized.");
},
/**
@ -192,18 +169,11 @@ var DownloadsPanel = {
// Panel interface
/**
* Main panel element in the browser window, or null if the panel overlay
* hasn't been loaded yet.
* Main panel element in the browser window.
*/
get panel() {
// If the downloads panel overlay hasn't loaded yet, just return null
// without resetting this.panel.
let downloadsPanel = document.getElementById("downloadsPanel");
if (!downloadsPanel)
return null;
delete this.panel;
return this.panel = downloadsPanel;
return this.panel = document.getElementById("downloadsPanel");
},
/**
@ -224,13 +194,12 @@ var DownloadsPanel = {
// As a belt-and-suspenders check, ensure the button is not hidden.
DownloadsButton.unhide();
this.initialize(() => {
// Delay displaying the panel because this function will sometimes be
// called while another window is closing (like the window for selecting
// whether to save or open the file), and that would cause the panel to
// close immediately.
setTimeout(() => this._openPopupIfDataReady(), 0);
});
this.initialize();
// Delay displaying the panel because this function will sometimes be
// called while another window is closing (like the window for selecting
// whether to save or open the file), and that would cause the panel to
// close immediately.
setTimeout(() => this._openPopupIfDataReady(), 0);
DownloadsCommon.log("Waiting for the downloads panel to appear.");
this._state = this.kStateWaitingData;
@ -262,7 +231,6 @@ var DownloadsPanel = {
*/
get isPanelShowing() {
return this._state == this.kStateWaitingData ||
this._state == this.kStateWaitingAnchor ||
this._state == this.kStateShown;
},
@ -530,131 +498,43 @@ var DownloadsPanel = {
return;
}
this._state = this.kStateWaitingAnchor;
// At this point, if the window is minimized, opening the panel could fail
// without any notification, and there would be no way to either open or
// close the panel any more. To prevent this, check if the window is
// minimized and in that case force the panel to the closed state.
if (window.windowState == window.STATE_MINIMIZED) {
this._state = this.kStateHidden;
return;
}
// Ensure the anchor is visible. If that is not possible, show the panel
// anchored to the top area of the window, near the default anchor position.
DownloadsButton.getAnchor(anchor => {
// If somehow we've switched states already (by getting a panel hiding
// event before an overlay is loaded, for example), bail out.
if (this._state != this.kStateWaitingAnchor) {
return;
}
let anchor = DownloadsButton.getAnchor();
// At this point, if the window is minimized, opening the panel could fail
// without any notification, and there would be no way to either open or
// close the panel any more. To prevent this, check if the window is
// minimized and in that case force the panel to the closed state.
if (window.windowState == window.STATE_MINIMIZED) {
DownloadsButton.releaseAnchor();
this._state = this.kStateHidden;
return;
}
if (!anchor) {
DownloadsCommon.error("Downloads button cannot be found.");
return;
}
if (!anchor) {
DownloadsCommon.error("Downloads button cannot be found.");
return;
}
let onBookmarksToolbar = !!anchor.closest("#PersonalToolbar");
this.panel.classList.toggle("bookmarks-toolbar", onBookmarksToolbar);
let onBookmarksToolbar = !!anchor.closest("#PersonalToolbar");
this.panel.classList.toggle("bookmarks-toolbar", onBookmarksToolbar);
// When the panel is opened, we check if the target files of visible items
// still exist, and update the allowed items interactions accordingly. We
// do these checks on a background thread, and don't prevent the panel to
// be displayed while these checks are being performed.
for (let viewItem of DownloadsView._visibleViewItems.values()) {
viewItem.download.refresh().catch(Cu.reportError);
}
// When the panel is opened, we check if the target files of visible items
// still exist, and update the allowed items interactions accordingly. We
// do these checks on a background thread, and don't prevent the panel to
// be displayed while these checks are being performed.
for (let viewItem of DownloadsView._visibleViewItems.values()) {
viewItem.download.refresh().catch(Cu.reportError);
}
DownloadsCommon.log("Opening downloads panel popup.");
PanelMultiView.openPopup(this.panel, anchor, "bottomcenter topright",
0, 0, false, null).catch(Cu.reportError);
});
DownloadsCommon.log("Opening downloads panel popup.");
PanelMultiView.openPopup(this.panel, anchor, "bottomcenter topright",
0, 0, false, null).catch(Cu.reportError);
},
};
XPCOMUtils.defineConstant(this, "DownloadsPanel", DownloadsPanel);
// DownloadsOverlayLoader
/**
* Allows loading the downloads panel and the status indicator interfaces on
* demand, to improve startup performance.
*/
var DownloadsOverlayLoader = {
/**
* We cannot load two overlays at the same time, thus we use a queue of
* pending load requests.
*/
_loadRequests: [],
/**
* True while we are waiting for an overlay to be loaded.
*/
_overlayLoading: false,
/**
* This object has a key for each overlay URI that is already loaded.
*/
_loadedOverlays: {},
/**
* Loads the specified overlay and invokes the given callback when finished.
*
* @param aOverlay
* String containing the URI of the overlay to load in the current
* window. If this overlay has already been loaded using this
* function, then the overlay is not loaded again.
* @param aCallback
* Invoked when loading is completed. If the overlay is already
* loaded, the function is called immediately.
*/
ensureOverlayLoaded(aOverlay, aCallback) {
// The overlay is already loaded, invoke the callback immediately.
if (aOverlay in this._loadedOverlays) {
aCallback();
return;
}
// The callback will be invoked when loading is finished.
this._loadRequests.push({ overlay: aOverlay, callback: aCallback });
if (this._overlayLoading) {
return;
}
this._overlayLoading = true;
DownloadsCommon.log("Loading overlay ", aOverlay);
document.loadOverlay(aOverlay, () => {
this._overlayLoading = false;
this._loadedOverlays[aOverlay] = true;
this.processPendingRequests();
});
},
/**
* Re-processes all the currently pending requests, invoking the callbacks
* and/or loading more overlays as needed. In most cases, there will be a
* single request for one overlay, that will be processed immediately.
*/
processPendingRequests() {
// Re-process all the currently pending requests, yet allow more requests
// to be appended at the end of the array if we're not ready for them.
let currentLength = this._loadRequests.length;
for (let i = 0; i < currentLength; i++) {
let request = this._loadRequests.shift();
// We must call ensureOverlayLoaded again for each request, to check if
// the associated callback can be invoked now, or if we must still wait
// for the associated overlay to load.
this.ensureOverlayLoaded(request.overlay, request.callback);
}
},
};
XPCOMUtils.defineConstant(this, "DownloadsOverlayLoader", DownloadsOverlayLoader);
// DownloadsView
/**

View File

@ -1,214 +0,0 @@
<?xml version="1.0"?>
# -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
# vim: set ts=2 et sw=2 tw=80:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css"?>
<?xml-stylesheet href="chrome://browser/skin/downloads/downloads.css"?>
<!DOCTYPE overlay SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
<overlay xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="downloadsOverlay">
<commandset>
<command id="downloadsCmd_doDefault"
oncommand="goDoCommand('downloadsCmd_doDefault')"/>
<command id="downloadsCmd_pauseResume"
oncommand="goDoCommand('downloadsCmd_pauseResume')"/>
<command id="downloadsCmd_cancel"
oncommand="goDoCommand('downloadsCmd_cancel')"/>
<command id="downloadsCmd_unblock"
oncommand="goDoCommand('downloadsCmd_unblock')"/>
<command id="downloadsCmd_chooseUnblock"
oncommand="goDoCommand('downloadsCmd_chooseUnblock')"/>
<command id="downloadsCmd_unblockAndOpen"
oncommand="goDoCommand('downloadsCmd_unblockAndOpen')"/>
<command id="downloadsCmd_confirmBlock"
oncommand="goDoCommand('downloadsCmd_confirmBlock')"/>
<command id="downloadsCmd_open"
oncommand="goDoCommand('downloadsCmd_open')"/>
<command id="downloadsCmd_show"
oncommand="goDoCommand('downloadsCmd_show')"/>
<command id="downloadsCmd_retry"
oncommand="goDoCommand('downloadsCmd_retry')"/>
<command id="downloadsCmd_openReferrer"
oncommand="goDoCommand('downloadsCmd_openReferrer')"/>
<command id="downloadsCmd_copyLocation"
oncommand="goDoCommand('downloadsCmd_copyLocation')"/>
<command id="downloadsCmd_clearList"
oncommand="goDoCommand('downloadsCmd_clearList')"/>
</commandset>
<popupset id="mainPopupSet">
<!-- The panel has level="top" to ensure that it is never hidden by the
taskbar on Windows. See bug 672365. For accessibility to screen
readers, we use a label on the panel instead of the anchor because the
panel can also be displayed without an anchor. -->
<panel id="downloadsPanel"
aria-label="&downloads.title;"
role="group"
type="arrow"
orient="vertical"
level="top"
onpopupshown="DownloadsPanel.onPopupShown(event);"
onpopuphidden="DownloadsPanel.onPopupHidden(event);">
<!-- The following popup menu should be a child of the panel element,
otherwise flickering may occur when the cursor is moved over the area
of a disabled menu item that overlaps the panel. See bug 492960. -->
<menupopup id="downloadsContextMenu"
onpopupshown="DownloadsView.onContextPopupShown(event);"
onpopuphidden="DownloadsView.onContextPopupHidden(event);"
class="download-state">
<menuitem command="downloadsCmd_pauseResume"
class="downloadPauseMenuItem"
label="&cmd.pause.label;"
accesskey="&cmd.pause.accesskey;"/>
<menuitem command="downloadsCmd_pauseResume"
class="downloadResumeMenuItem"
label="&cmd.resume.label;"
accesskey="&cmd.resume.accesskey;"/>
<menuitem command="downloadsCmd_unblock"
class="downloadUnblockMenuItem"
label="&cmd.unblock2.label;"
accesskey="&cmd.unblock2.accesskey;"/>
<menuitem command="downloadsCmd_show"
class="downloadShowMenuItem"
#ifdef XP_MACOSX
label="&cmd.showMac.label;"
accesskey="&cmd.showMac.accesskey;"
#else
label="&cmd.show.label;"
accesskey="&cmd.show.accesskey;"
#endif
/>
<menuseparator class="downloadCommandsSeparator"/>
<menuitem command="downloadsCmd_openReferrer"
label="&cmd.goToDownloadPage.label;"
accesskey="&cmd.goToDownloadPage.accesskey;"/>
<menuitem command="downloadsCmd_copyLocation"
label="&cmd.copyDownloadLink.label;"
accesskey="&cmd.copyDownloadLink.accesskey;"/>
<menuseparator/>
<menuitem command="cmd_delete"
class="downloadRemoveFromHistoryMenuItem"
label="&cmd.removeFromHistory.label;"
accesskey="&cmd.removeFromHistory.accesskey;"/>
<menuitem command="downloadsCmd_clearList"
label="&cmd.clearList2.label;"
accesskey="&cmd.clearList2.accesskey;"/>
<menuitem command="downloadsCmd_clearDownloads"
hidden="true"
label="&cmd.clearDownloads.label;"/>
</menupopup>
<panelmultiview id="downloadsPanel-multiView"
mainViewId="downloadsPanel-mainView">
<panelview id="downloadsPanel-mainView">
<vbox class="panel-view-body-unscrollable">
<richlistbox id="downloadsListBox"
context="downloadsContextMenu"
onmouseover="DownloadsView.onDownloadMouseOver(event);"
onmouseout="DownloadsView.onDownloadMouseOut(event);"
oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
ondragstart="DownloadsView.onDownloadDragStart(event);"/>
<description id="emptyDownloads"
mousethrough="always"
value="&downloadsPanelEmpty.label;"/>
</vbox>
<vbox id="downloadsFooter"
class="downloadsPanelFooter">
<stack>
<hbox id="downloadsSummary"
align="center"
orient="horizontal"
onkeydown="DownloadsSummary.onKeyDown(event);"
onclick="DownloadsSummary.onClick(event);">
<image class="downloadTypeIcon" />
<vbox pack="center"
class="downloadContainer"
style="width: &downloadDetails.width;">
<description id="downloadsSummaryDescription"
style="min-width: &downloadsSummary.minWidth2;"/>
<progressmeter id="downloadsSummaryProgress"
class="downloadProgress"
min="0"
max="100"
mode="normal" />
<description id="downloadsSummaryDetails"
crop="end"/>
</vbox>
</hbox>
<hbox id="downloadsFooterButtons">
<button id="downloadsHistory"
class="downloadsPanelFooterButton"
label="&downloadsHistory.label;"
accesskey="&downloadsHistory.accesskey;"
flex="1"
oncommand="DownloadsPanel.showDownloadsHistory();"
pack="start"/>
</hbox>
</stack>
</vbox>
</panelview>
<panelview id="downloadsPanel-blockedSubview"
descriptionheightworkaround="true"
class="PanelUI-subView"
title="&downloadDetails.label;">
<vbox class="panel-view-body-unscrollable">
<description id="downloadsPanel-blockedSubview-title"/>
<description id="downloadsPanel-blockedSubview-details1"/>
<description id="downloadsPanel-blockedSubview-details2"/>
</vbox>
<hbox id="downloadsPanel-blockedSubview-buttons"
class="downloadsPanelFooter"
align="stretch">
<button id="downloadsPanel-blockedSubview-openButton"
class="downloadsPanelFooterButton"
command="downloadsCmd_unblockAndOpen"
flex="1"/>
<toolbarseparator/>
<button id="downloadsPanel-blockedSubview-deleteButton"
class="downloadsPanelFooterButton"
oncommand="DownloadsBlockedSubview.confirmBlock();"
default="true"
flex="1"/>
</hbox>
</panelview>
<panelview id="PanelUI-downloads" class="PanelUI-subView">
<vbox class="panel-subview-body">
<toolbarbutton id="appMenu-library-downloads-show-button"
class="subviewbutton subviewbutton-iconic"
label="&cmd.showDownloads.label;"
closemenu="none"
oncommand="DownloadsSubview.onShowDownloads(this);"/>
<toolbarseparator/>
<toolbaritem id="panelMenu_downloadsMenu"
orient="vertical"
smoothscroll="false"
flatList="true"
tooltip="bhTooltip">
<!-- downloads menu items will go here -->
</toolbaritem>
</vbox>
<toolbarbutton id="PanelUI-downloadsMore"
class="panel-subview-footer subviewbutton"
label="&downloadsHistory.label;"
oncommand="BrowserDownloadsUI(); CustomizableUI.hidePanelForNode(this);"/>
</panelview>
</panelmultiview>
</panel>
</popupset>
</overlay>

View File

@ -0,0 +1,200 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<commandset>
<command id="downloadsCmd_doDefault"
oncommand="goDoCommand('downloadsCmd_doDefault')"/>
<command id="downloadsCmd_pauseResume"
oncommand="goDoCommand('downloadsCmd_pauseResume')"/>
<command id="downloadsCmd_cancel"
oncommand="goDoCommand('downloadsCmd_cancel')"/>
<command id="downloadsCmd_unblock"
oncommand="goDoCommand('downloadsCmd_unblock')"/>
<command id="downloadsCmd_chooseUnblock"
oncommand="goDoCommand('downloadsCmd_chooseUnblock')"/>
<command id="downloadsCmd_unblockAndOpen"
oncommand="goDoCommand('downloadsCmd_unblockAndOpen')"/>
<command id="downloadsCmd_confirmBlock"
oncommand="goDoCommand('downloadsCmd_confirmBlock')"/>
<command id="downloadsCmd_open"
oncommand="goDoCommand('downloadsCmd_open')"/>
<command id="downloadsCmd_show"
oncommand="goDoCommand('downloadsCmd_show')"/>
<command id="downloadsCmd_retry"
oncommand="goDoCommand('downloadsCmd_retry')"/>
<command id="downloadsCmd_openReferrer"
oncommand="goDoCommand('downloadsCmd_openReferrer')"/>
<command id="downloadsCmd_copyLocation"
oncommand="goDoCommand('downloadsCmd_copyLocation')"/>
<command id="downloadsCmd_clearList"
oncommand="goDoCommand('downloadsCmd_clearList')"/>
</commandset>
<!-- The panel has level="top" to ensure that it is never hidden by the
taskbar on Windows. See bug 672365. For accessibility to screen
readers, we use a label on the panel instead of the anchor because the
panel can also be displayed without an anchor. -->
<panel id="downloadsPanel"
aria-label="&downloads.title;"
role="group"
type="arrow"
orient="vertical"
level="top"
onpopupshown="DownloadsPanel.onPopupShown(event);"
onpopuphidden="DownloadsPanel.onPopupHidden(event);"
hidden="true">
<!-- The following popup menu should be a child of the panel element,
otherwise flickering may occur when the cursor is moved over the area
of a disabled menu item that overlaps the panel. See bug 492960. -->
<menupopup id="downloadsContextMenu"
onpopupshown="DownloadsView.onContextPopupShown(event);"
onpopuphidden="DownloadsView.onContextPopupHidden(event);"
class="download-state">
<menuitem command="downloadsCmd_pauseResume"
class="downloadPauseMenuItem"
label="&cmd.pause.label;"
accesskey="&cmd.pause.accesskey;"/>
<menuitem command="downloadsCmd_pauseResume"
class="downloadResumeMenuItem"
label="&cmd.resume.label;"
accesskey="&cmd.resume.accesskey;"/>
<menuitem command="downloadsCmd_unblock"
class="downloadUnblockMenuItem"
label="&cmd.unblock2.label;"
accesskey="&cmd.unblock2.accesskey;"/>
<menuitem command="downloadsCmd_show"
class="downloadShowMenuItem"
#ifdef XP_MACOSX
label="&cmd.showMac.label;"
accesskey="&cmd.showMac.accesskey;"
#else
label="&cmd.show.label;"
accesskey="&cmd.show.accesskey;"
#endif
/>
<menuseparator class="downloadCommandsSeparator"/>
<menuitem command="downloadsCmd_openReferrer"
label="&cmd.goToDownloadPage.label;"
accesskey="&cmd.goToDownloadPage.accesskey;"/>
<menuitem command="downloadsCmd_copyLocation"
label="&cmd.copyDownloadLink.label;"
accesskey="&cmd.copyDownloadLink.accesskey;"/>
<menuseparator/>
<menuitem command="cmd_delete"
class="downloadRemoveFromHistoryMenuItem"
label="&cmd.removeFromHistory.label;"
accesskey="&cmd.removeFromHistory.accesskey;"/>
<menuitem command="downloadsCmd_clearList"
label="&cmd.clearList2.label;"
accesskey="&cmd.clearList2.accesskey;"/>
<menuitem command="downloadsCmd_clearDownloads"
hidden="true"
label="&cmd.clearDownloads.label;"/>
</menupopup>
<panelmultiview id="downloadsPanel-multiView"
mainViewId="downloadsPanel-mainView">
<panelview id="downloadsPanel-mainView">
<vbox class="panel-view-body-unscrollable">
<richlistbox id="downloadsListBox"
context="downloadsContextMenu"
onmouseover="DownloadsView.onDownloadMouseOver(event);"
onmouseout="DownloadsView.onDownloadMouseOut(event);"
oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
ondragstart="DownloadsView.onDownloadDragStart(event);"/>
<description id="emptyDownloads"
mousethrough="always"
value="&downloadsPanelEmpty.label;"/>
</vbox>
<vbox id="downloadsFooter"
class="downloadsPanelFooter">
<stack>
<hbox id="downloadsSummary"
align="center"
orient="horizontal"
onkeydown="DownloadsSummary.onKeyDown(event);"
onclick="DownloadsSummary.onClick(event);">
<image class="downloadTypeIcon" />
<vbox pack="center"
class="downloadContainer"
style="width: &downloadDetails.width;">
<description id="downloadsSummaryDescription"
style="min-width: &downloadsSummary.minWidth2;"/>
<progressmeter id="downloadsSummaryProgress"
class="downloadProgress"
min="0"
max="100"
mode="normal" />
<description id="downloadsSummaryDetails"
crop="end"/>
</vbox>
</hbox>
<hbox id="downloadsFooterButtons">
<button id="downloadsHistory"
class="downloadsPanelFooterButton"
label="&downloadsHistory.label;"
accesskey="&downloadsHistory.accesskey;"
flex="1"
oncommand="DownloadsPanel.showDownloadsHistory();"
pack="start"/>
</hbox>
</stack>
</vbox>
</panelview>
<panelview id="downloadsPanel-blockedSubview"
descriptionheightworkaround="true"
class="PanelUI-subView"
title="&downloadDetails.label;">
<vbox class="panel-view-body-unscrollable">
<description id="downloadsPanel-blockedSubview-title"/>
<description id="downloadsPanel-blockedSubview-details1"/>
<description id="downloadsPanel-blockedSubview-details2"/>
</vbox>
<hbox id="downloadsPanel-blockedSubview-buttons"
class="downloadsPanelFooter"
align="stretch">
<button id="downloadsPanel-blockedSubview-openButton"
class="downloadsPanelFooterButton"
command="downloadsCmd_unblockAndOpen"
flex="1"/>
<toolbarseparator/>
<button id="downloadsPanel-blockedSubview-deleteButton"
class="downloadsPanelFooterButton"
oncommand="DownloadsBlockedSubview.confirmBlock();"
default="true"
flex="1"/>
</hbox>
</panelview>
<panelview id="PanelUI-downloads" class="PanelUI-subView">
<vbox class="panel-subview-body">
<toolbarbutton id="appMenu-library-downloads-show-button"
class="subviewbutton subviewbutton-iconic"
label="&cmd.showDownloads.label;"
closemenu="none"
oncommand="DownloadsSubview.onShowDownloads(this);"/>
<toolbarseparator/>
<toolbaritem id="panelMenu_downloadsMenu"
orient="vertical"
smoothscroll="false"
flatList="true"
tooltip="bhTooltip">
<!-- downloads menu items will go here -->
</toolbaritem>
</vbox>
<toolbarbutton id="PanelUI-downloadsMore"
class="panel-subview-footer subviewbutton"
label="&downloadsHistory.label;"
oncommand="BrowserDownloadsUI(); CustomizableUI.hidePanelForNode(this);"/>
</panelview>
</panelmultiview>
</panel>

View File

@ -39,13 +39,6 @@ ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
* downloads panel.
*/
const DownloadsButton = {
/**
* Location of the indicator overlay.
*/
get kIndicatorOverlay() {
return "chrome://browser/content/downloads/indicatorOverlay.xul";
},
/**
* Returns a reference to the downloads button position placeholder, or null
* if not available because it has been removed from the toolbars.
@ -78,8 +71,7 @@ const DownloadsButton = {
_getAnchorInternal() {
let indicator = DownloadsIndicatorView.indicator;
if (!indicator) {
// Exit now if the indicator overlay isn't loaded yet, or if the button
// is not in the document.
// Exit now if the button is not in the document.
return null;
}
@ -104,22 +96,18 @@ const DownloadsButton = {
/**
* Ensures that there is an anchor available for the panel.
*
* @param aCallback
* Called when the anchor is available, passing the element where the
* panel should be anchored, or null if an anchor is not available (for
* example because both the tab bar and the navigation bar are hidden).
* @return Anchor element where the panel should be anchored, or null if an
* anchor is not available (for example because both the tab bar and
* the navigation bar are hidden).
*/
getAnchor(aCallback) {
getAnchor() {
// Do not allow anchoring the panel to the element while customizing.
if (this._customizing) {
aCallback(null);
return;
return null;
}
DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay, () => {
this._anchorRequested = true;
aCallback(this._getAnchorInternal());
});
this._anchorRequested = true;
return this._getAnchorInternal();
},
/**
@ -300,39 +288,26 @@ const DownloadsIndicatorView = {
/**
* Ensures that the user interface elements required to display the indicator
* are loaded, then invokes the given callback.
* are loaded.
*/
_ensureOperational(aCallback) {
_ensureOperational() {
if (this._operational) {
if (aCallback) {
aCallback();
}
return;
}
// If we don't have a _placeholder, there's no chance that the overlay
// If we don't have a _placeholder, there's no chance that everything
// will load correctly: bail (and don't set _operational to true!)
if (!DownloadsButton._placeholder) {
return;
}
DownloadsOverlayLoader.ensureOverlayLoaded(
DownloadsButton.kIndicatorOverlay,
() => {
this._operational = true;
this._operational = true;
// If the view is initialized, we need to update the elements now that
// they are finally available in the document.
// We need to re-check for the placeholder because it might have
// disappeared since then.
if (this._initialized && DownloadsButton._placeholder) {
DownloadsCommon.getIndicatorData(window).refreshView(this);
}
if (aCallback) {
aCallback();
}
});
// If the view is initialized, we need to update the elements now that
// they are finally available in the document.
if (this._initialized) {
DownloadsCommon.getIndicatorData(window).refreshView(this);
}
},
// Direct control functions

View File

@ -1,34 +0,0 @@
<?xml version="1.0"?>
<!-- -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- -->
<!-- vim: set ts=2 et sw=2 tw=80: -->
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this file,
- You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE overlay [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
%browserDTD;
]>
<overlay xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="indicatorOverlay">
<!-- We dynamically add the stack with the progress meter and notification icon,
originally loaded lazily because of performance reasons, to the existing
downloads-button. -->
<toolbarbutton id="downloads-button" indicator="true">
<!-- The panel's anchor area is smaller than the outer button, but must
always be visible and must not move or resize when the indicator
state changes, otherwise the panel could change its position or lose
its arrow unexpectedly. -->
<stack id="downloads-indicator-anchor"
consumeanchor="downloads-button">
<box id="downloads-indicator-icon"/>
<stack id="downloads-indicator-progress-outer">
<box id="downloads-indicator-progress-inner"/>
</stack>
</stack>
</toolbarbutton>
</overlay>

View File

@ -6,9 +6,7 @@ browser.jar:
* content/browser/downloads/download.xml (content/download.xml)
content/browser/downloads/downloads.css (content/downloads.css)
content/browser/downloads/downloads.js (content/downloads.js)
* content/browser/downloads/downloadsOverlay.xul (content/downloadsOverlay.xul)
content/browser/downloads/indicator.js (content/indicator.js)
content/browser/downloads/indicatorOverlay.xul (content/indicatorOverlay.xul)
* content/browser/downloads/allDownloadsViewOverlay.xul (content/allDownloadsViewOverlay.xul)
content/browser/downloads/allDownloadsViewOverlay.js (content/allDownloadsViewOverlay.js)
* content/browser/downloads/contentAreaDownloadsView.xul (content/contentAreaDownloadsView.xul)

View File

@ -138,3 +138,24 @@ responsive.deviceDetails=Size: %1$S x %2$S\nDPR: %3$S\nUA: %4$S\nTouch: %5$S
# LOCALIZATION NOTE (responsive.devicePixelRatioOption): UI option in a menu to configure
# the device pixel ratio. %1$S is the devicePixelRatio value of the device.
responsive.devicePixelRatioOption=DPR: %1$S
# LOCALIZATION NOTE (responsive.reloadConditions.label): Label on button to open a menu
# used to choose whether to reload the page automatically when certain actions occur.
responsive.reloadConditions.label=Reload when…
# LOCALIZATION NOTE (responsive.reloadConditions.title): Title on button to open a menu
# used to choose whether to reload the page automatically when certain actions occur.
responsive.reloadConditions.title=Choose whether to reload the page automatically when certain actions occur
# LOCALIZATION NOTE (responsive.reloadConditions.touchSimulation): Label on checkbox used
# to select whether to reload when touch simulation is toggled.
responsive.reloadConditions.touchSimulation=Reload when touch simulation is toggled
# LOCALIZATION NOTE (responsive.reloadConditions.userAgent): Label on checkbox used
# to select whether to reload when user agent is changed.
responsive.reloadConditions.userAgent=Reload when user agent is changed
# LOCALIZATION NOTE (responsive.reloadNotification.description): Text in notification bar
# shown on first open to clarify that some features need a reload to apply. %1$S is the
# label on the reload conditions menu (responsive.reloadConditions.label).
responsive.reloadNotification.description=Device simulation changes require a reload to fully apply. Automatic reloads are disabled by default to avoid losing any changes in DevTools. You can enable reloading via the “%1$S” menu.

View File

@ -357,3 +357,10 @@ pref("devtools.editor.autocomplete", true);
// opened developer tool. This allows us to ping telemetry just once per browser
// version for each user.
pref("devtools.telemetry.tools.opened.version", "{}");
// Whether to reload when touch simulation is toggled
pref("devtools.responsive.reloadConditions.touchSimulation", false);
// Whether to reload when user agent is changed
pref("devtools.responsive.reloadConditions.userAgent", false);
// Whether to show the notification about reloading to apply emulation
pref("devtools.responsive.reloadNotification.enabled", true);

View File

@ -41,18 +41,24 @@ createEnum([
// selected from the device pixel ratio dropdown.
"CHANGE_PIXEL_RATIO",
// Change one of the reload conditions.
"CHANGE_RELOAD_CONDITION",
// Change the touch simulation state.
"CHANGE_TOUCH_SIMULATION",
// Indicates that the device list is being loaded
// Indicates that the device list is being loaded.
"LOAD_DEVICE_LIST_START",
// Indicates that the device list loading action threw an error
// Indicates that the device list loading action threw an error.
"LOAD_DEVICE_LIST_ERROR",
// Indicates that the device list has been loaded successfully
// Indicates that the device list has been loaded successfully.
"LOAD_DEVICE_LIST_END",
// Indicates that the reload conditions have been loaded successfully.
"LOAD_RELOAD_CONDITIONS_END",
// Remove a device.
"REMOVE_DEVICE",

View File

@ -10,6 +10,7 @@ DevToolsModules(
'index.js',
'location.js',
'network-throttling.js',
'reload-conditions.js',
'screenshot.js',
'touch-simulation.js',
'viewports.js',

View File

@ -0,0 +1,52 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
CHANGE_RELOAD_CONDITION,
LOAD_RELOAD_CONDITIONS_END,
} = require("./index");
const Types = require("../types");
const Services = require("Services");
const PREF_PREFIX = "devtools.responsive.reloadConditions.";
module.exports = {
changeReloadCondition(id, value) {
return dispatch => {
let pref = PREF_PREFIX + id;
Services.prefs.setBoolPref(pref, value);
dispatch({
type: CHANGE_RELOAD_CONDITION,
id,
value,
});
};
},
loadReloadConditions() {
return dispatch => {
// Loop over the conditions and load their values from prefs.
for (let id in Types.reloadConditions) {
// Skip over the loading state of the list.
if (id == "state") {
return;
}
let pref = PREF_PREFIX + id;
let value = Services.prefs.getBoolPref(pref, false);
dispatch({
type: CHANGE_RELOAD_CONDITION,
id,
value,
});
}
dispatch({ type: LOAD_RELOAD_CONDITIONS_END });
};
},
};

View File

@ -19,6 +19,7 @@ const {
updatePreferredDevices,
} = require("./actions/devices");
const { changeNetworkThrottling } = require("./actions/network-throttling");
const { changeReloadCondition } = require("./actions/reload-conditions");
const { takeScreenshot } = require("./actions/screenshot");
const { changeTouchSimulation } = require("./actions/touch-simulation");
const {
@ -40,6 +41,7 @@ class App extends Component {
dispatch: PropTypes.func.isRequired,
displayPixelRatio: Types.pixelRatio.value.isRequired,
networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
reloadConditions: PropTypes.shape(Types.reloadConditions).isRequired,
screenshot: PropTypes.shape(Types.screenshot).isRequired,
touchSimulation: PropTypes.shape(Types.touchSimulation).isRequired,
viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
@ -53,6 +55,7 @@ class App extends Component {
this.onChangeDevice = this.onChangeDevice.bind(this);
this.onChangeNetworkThrottling = this.onChangeNetworkThrottling.bind(this);
this.onChangePixelRatio = this.onChangePixelRatio.bind(this);
this.onChangeReloadCondition = this.onChangeReloadCondition.bind(this);
this.onChangeTouchSimulation = this.onChangeTouchSimulation.bind(this);
this.onContentResize = this.onContentResize.bind(this);
this.onDeviceListUpdate = this.onDeviceListUpdate.bind(this);
@ -104,6 +107,10 @@ class App extends Component {
this.props.dispatch(changePixelRatio(0, pixelRatio));
}
onChangeReloadCondition(id, value) {
this.props.dispatch(changeReloadCondition(id, value));
}
onChangeTouchSimulation(enabled) {
window.postMessage({
type: "change-touch-simulation",
@ -165,6 +172,7 @@ class App extends Component {
devices,
displayPixelRatio,
networkThrottling,
reloadConditions,
screenshot,
touchSimulation,
viewports,
@ -176,6 +184,7 @@ class App extends Component {
onChangeDevice,
onChangeNetworkThrottling,
onChangePixelRatio,
onChangeReloadCondition,
onChangeTouchSimulation,
onContentResize,
onDeviceListUpdate,
@ -210,12 +219,14 @@ class App extends Component {
devices,
displayPixelRatio,
networkThrottling,
reloadConditions,
screenshot,
selectedDevice,
selectedPixelRatio,
touchSimulation,
onChangeNetworkThrottling,
onChangePixelRatio,
onChangeReloadCondition,
onChangeTouchSimulation,
onExit,
onScreenshot,

View File

@ -92,8 +92,8 @@ class DevicePixelRatioSelector extends PureComponent {
}
let state = devices.listState;
let isDisabled = (state !== Types.deviceListState.LOADED) || (selectedDevice !== "");
let selectorClass = "";
let isDisabled = (state !== Types.loadableState.LOADED) || (selectedDevice !== "");
let selectorClass = "toolbar-dropdown";
let title;
if (isDisabled) {
@ -113,7 +113,7 @@ class DevicePixelRatioSelector extends PureComponent {
let listContent = PIXEL_RATIO_PRESET.map(createVisibleOption);
if (state == Types.deviceListState.LOADED) {
if (state == Types.loadableState.LOADED) {
listContent = listContent.concat(hiddenOptions.map(createHiddenOption));
}

View File

@ -72,7 +72,7 @@ class DeviceSelector extends PureComponent {
return a.name.localeCompare(b.name);
});
let selectClass = "viewport-device-selector";
let selectClass = "viewport-device-selector toolbar-dropdown";
if (selectedDevice) {
selectClass += " selected";
}
@ -80,7 +80,7 @@ class DeviceSelector extends PureComponent {
let state = devices.listState;
let listContent;
if (state == Types.deviceListState.LOADED) {
if (state == Types.loadableState.LOADED) {
listContent = [
dom.option({
value: "",
@ -100,14 +100,14 @@ class DeviceSelector extends PureComponent {
value: OPEN_DEVICE_MODAL_VALUE,
title: "",
}, getStr("responsive.editDeviceList"))];
} else if (state == Types.deviceListState.LOADING
|| state == Types.deviceListState.INITIALIZED) {
} else if (state == Types.loadableState.LOADING
|| state == Types.loadableState.INITIALIZED) {
listContent = [dom.option({
value: "",
title: "",
disabled: true,
}, getStr("responsive.deviceListLoading"))];
} else if (state == Types.deviceListState.ERROR) {
} else if (state == Types.loadableState.ERROR) {
listContent = [dom.option({
value: "",
title: "",
@ -121,7 +121,7 @@ class DeviceSelector extends PureComponent {
value: selectedDevice,
title: selectedDevice,
onChange: this.onSelectChange,
disabled: (state !== Types.deviceListState.LOADED),
disabled: (state !== Types.loadableState.LOADED),
},
...listContent
);

View File

@ -12,6 +12,7 @@ const { getStr } = require("../utils/l10n");
const Types = require("../types");
const DevicePixelRatioSelector = createFactory(require("./DevicePixelRatioSelector"));
const NetworkThrottlingSelector = createFactory(require("./NetworkThrottlingSelector"));
const ReloadConditions = createFactory(require("./ReloadConditions"));
class GlobalToolbar extends PureComponent {
static get propTypes() {
@ -19,12 +20,14 @@ class GlobalToolbar extends PureComponent {
devices: PropTypes.shape(Types.devices).isRequired,
displayPixelRatio: Types.pixelRatio.value.isRequired,
networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
reloadConditions: PropTypes.shape(Types.reloadConditions).isRequired,
screenshot: PropTypes.shape(Types.screenshot).isRequired,
selectedDevice: PropTypes.string.isRequired,
selectedPixelRatio: PropTypes.shape(Types.pixelRatio).isRequired,
touchSimulation: PropTypes.shape(Types.touchSimulation).isRequired,
onChangeNetworkThrottling: PropTypes.func.isRequired,
onChangePixelRatio: PropTypes.func.isRequired,
onChangeReloadCondition: PropTypes.func.isRequired,
onChangeTouchSimulation: PropTypes.func.isRequired,
onExit: PropTypes.func.isRequired,
onScreenshot: PropTypes.func.isRequired,
@ -36,12 +39,14 @@ class GlobalToolbar extends PureComponent {
devices,
displayPixelRatio,
networkThrottling,
reloadConditions,
screenshot,
selectedDevice,
selectedPixelRatio,
touchSimulation,
onChangeNetworkThrottling,
onChangePixelRatio,
onChangeReloadCondition,
onChangeTouchSimulation,
onExit,
onScreenshot,
@ -74,6 +79,10 @@ class GlobalToolbar extends PureComponent {
selectedPixelRatio,
onChangePixelRatio,
}),
ReloadConditions({
reloadConditions,
onChangeReloadCondition,
}),
dom.button({
id: "global-touch-simulation-button",
className: touchButtonClass,

View File

@ -48,7 +48,7 @@ class NetworkThrottlingSelector extends PureComponent {
networkThrottling,
} = this.props;
let selectClass = "";
let selectClass = "toolbar-dropdown";
let selectedProfile;
if (networkThrottling.enabled) {
selectClass += " selected";

View File

@ -0,0 +1,49 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { PureComponent, createFactory } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const Types = require("../types");
const { getStr } = require("../utils/l10n");
const ToggleMenu = createFactory(require("./ToggleMenu"));
class ReloadConditions extends PureComponent {
static get propTypes() {
return {
reloadConditions: PropTypes.shape(Types.reloadConditions).isRequired,
onChangeReloadCondition: PropTypes.func.isRequired,
};
}
render() {
let {
reloadConditions,
onChangeReloadCondition,
} = this.props;
return ToggleMenu({
id: "global-reload-conditions-menu",
items: [
{
id: "touchSimulation",
label: getStr("responsive.reloadConditions.touchSimulation"),
checked: reloadConditions.touchSimulation,
},
{
id: "userAgent",
label: getStr("responsive.reloadConditions.userAgent"),
checked: reloadConditions.userAgent,
},
],
label: getStr("responsive.reloadConditions.label"),
title: getStr("responsive.reloadConditions.title"),
onChange: onChangeReloadCondition,
});
}
}
module.exports = ReloadConditions;

View File

@ -0,0 +1,129 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { PureComponent } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
let MenuItem = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
checked: PropTypes.bool,
};
class ToggleMenu extends PureComponent {
static get propTypes() {
return {
id: PropTypes.string,
items: PropTypes.arrayOf(PropTypes.shape(MenuItem)).isRequired,
label: PropTypes.string,
title: PropTypes.string,
onChange: PropTypes.func.isRequired,
};
}
constructor(props) {
super(props);
this.state = {
isOpen: false,
};
this.onItemChange = this.onItemChange.bind(this);
this.onToggleOpen = this.onToggleOpen.bind(this);
}
onItemChange({ target }) {
let {
onChange,
} = this.props;
// Close menu after changing an item
this.setState({
isOpen: false,
});
let id = target.name;
onChange(id, target.checked);
}
onToggleOpen() {
let {
isOpen,
} = this.state;
this.setState({
isOpen: !isOpen,
});
}
render() {
let {
id: menuID,
items,
label: toggleLabel,
title,
} = this.props;
let {
isOpen,
} = this.state;
let {
onItemChange,
onToggleOpen,
} = this;
let menuItems = items.map(({ id, label, checked }) => {
let inputID = `devtools-menu-item-${id}`;
return dom.div(
{
className: "devtools-menu-item",
},
dom.input({
type: "checkbox",
id: inputID,
name: id,
checked,
onChange: onItemChange,
}),
dom.label({
htmlFor: inputID,
}, label)
);
});
let menuClass = "devtools-menu";
if (isOpen) {
menuClass += " opened";
}
let menu = dom.div(
{
className: menuClass,
},
menuItems
);
let buttonClass = "devtools-toggle-menu";
buttonClass += " toolbar-dropdown toolbar-button devtools-button";
if (isOpen || items.some(({ checked }) => checked)) {
buttonClass += " selected";
}
return dom.div(
{
id: menuID,
className: buttonClass,
title,
onClick: onToggleOpen,
},
toggleLabel,
menu
);
}
}
module.exports = ToggleMenu;

View File

@ -12,7 +12,9 @@ DevToolsModules(
'DeviceSelector.js',
'GlobalToolbar.js',
'NetworkThrottlingSelector.js',
'ReloadConditions.js',
'ResizableViewport.js',
'ToggleMenu.js',
'Viewport.js',
'ViewportDimension.js',
'Viewports.js',

View File

@ -67,10 +67,13 @@ body,
margin: 0;
padding: 0;
border: none;
color: var(--viewport-color);
}
.toolbar-button:empty:hover:not(:disabled),
.toolbar-button:empty:-moz-any(:hover:active, .checked):not(:disabled) {
.toolbar-button:empty:-moz-any(:hover:active, .checked):not(:disabled),
.toolbar-button:not(:empty),
.toolbar-button:hover:not(:empty):not(:disabled):not(.checked) {
/* Reset background from .devtools-button */
background: none;
}
@ -79,16 +82,17 @@ body,
filter: var(--theme-icon-checked-filter);
}
.toolbar-button.selected {
color: var(--viewport-active-color);
}
.toolbar-button:not(:disabled):hover {
color: var(--viewport-hover-color);
}
select {
-moz-appearance: none;
background-color: var(--theme-toolbar-background);
background-image: var(--viewport-selection-arrow);
-moz-context-properties: fill;
fill: currentColor;
color: var(--viewport-color);
background-position: 100% 50%;
background-repeat: no-repeat;
background-size: 7px;
border: none;
height: 100%;
padding: 0 8px;
@ -129,6 +133,52 @@ select > option.divider {
font-size: 0px;
}
/**
* Toggle Menu
*/
.devtools-toggle-menu {
position: relative;
}
.devtools-toggle-menu .devtools-menu {
display: none;
flex-direction: column;
align-items: start;
position: absolute;
left: 0;
top: 100%;
z-index: 1;
padding: 5px;
border-radius: 2px;
background-color: var(--theme-toolbar-background);
box-shadow: var(--rdm-box-shadow);
}
.devtools-toggle-menu .devtools-menu.opened {
display: flex;
}
.devtools-toggle-menu .devtools-menu-item {
display: flex;
align-items: center;
}
/**
* Common background for dropdowns like select and toggle menu
*/
.toolbar-dropdown,
.toolbar-dropdown.devtools-button,
.toolbar-dropdown.devtools-button:hover:not(:empty):not(:disabled):not(.checked) {
background-color: var(--theme-toolbar-background);
background-image: var(--viewport-selection-arrow);
background-position: 100% 50%;
background-repeat: no-repeat;
background-size: 7px;
-moz-context-properties: fill;
fill: currentColor;
}
/**
* Global Toolbar
*/
@ -140,6 +190,7 @@ select > option.divider {
margin: 0 0 15px 0;
padding: 4px 5px;
display: inline-flex;
align-items: center;
-moz-user-select: none;
}
@ -157,6 +208,13 @@ select > option.divider {
height: 12px;
}
#global-toolbar .toolbar-dropdown {
background-position-x: right 5px;
border-right: 1px solid var(--theme-splitter-color);
padding-right: 15px;
/* padding-left: 0; */
}
#global-touch-simulation-button::before {
background-image: url("./images/touch-events.svg");
}

View File

@ -23,10 +23,11 @@ const { Provider } = require("devtools/client/shared/vendor/react-redux");
const message = require("./utils/message");
const App = createFactory(require("./app"));
const Store = require("./store");
const { changeLocation } = require("./actions/location");
const { changeDisplayPixelRatio } = require("./actions/display-pixel-ratio");
const { addViewport, resizeViewport } = require("./actions/viewports");
const { loadDevices } = require("./actions/devices");
const { changeDisplayPixelRatio } = require("./actions/display-pixel-ratio");
const { changeLocation } = require("./actions/location");
const { loadReloadConditions } = require("./actions/reload-conditions");
const { addViewport, resizeViewport } = require("./actions/viewports");
// Exposed for use by tests
window.require = require;
@ -79,7 +80,10 @@ message.wait(window, "init").then(() => bootstrap.init());
// manager.js sends a message to signal init is done, which can be used for delayed
// startup work that shouldn't block initial load
message.wait(window, "post-init").then(() => bootstrap.dispatch(loadDevices()));
message.wait(window, "post-init").then(() => {
bootstrap.dispatch(loadDevices());
bootstrap.dispatch(loadReloadConditions());
});
window.addEventListener("unload", function () {
bootstrap.destroy();

View File

@ -6,26 +6,24 @@
const { Ci } = require("chrome");
const promise = require("promise");
const Services = require("Services");
const EventEmitter = require("devtools/shared/old-event-emitter");
const TOOL_URL = "chrome://devtools/content/responsive.html/index.xhtml";
loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugger-client", true);
loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
loader.lazyRequireGetter(this, "throttlingProfiles",
"devtools/client/shared/network-throttling-profiles");
loader.lazyRequireGetter(this, "swapToInnerBrowser",
"devtools/client/responsive.html/browser/swap", true);
loader.lazyRequireGetter(this, "startup",
"devtools/client/responsive.html/utils/window", true);
loader.lazyRequireGetter(this, "message",
"devtools/client/responsive.html/utils/message");
loader.lazyRequireGetter(this, "getStr",
"devtools/client/responsive.html/utils/l10n", true);
loader.lazyRequireGetter(this, "EmulationFront",
"devtools/shared/fronts/emulation", true);
loader.lazyRequireGetter(this, "throttlingProfiles", "devtools/client/shared/network-throttling-profiles");
loader.lazyRequireGetter(this, "swapToInnerBrowser", "devtools/client/responsive.html/browser/swap", true);
loader.lazyRequireGetter(this, "startup", "devtools/client/responsive.html/utils/window", true);
loader.lazyRequireGetter(this, "message", "devtools/client/responsive.html/utils/message");
loader.lazyRequireGetter(this, "showNotification", "devtools/client/responsive.html/utils/notification", true);
loader.lazyRequireGetter(this, "l10n", "devtools/client/responsive.html/utils/l10n");
loader.lazyRequireGetter(this, "EmulationFront", "devtools/shared/fronts/emulation", true);
loader.lazyRequireGetter(this, "PriorityLevels", "devtools/client/shared/components/NotificationBox", true);
const RELOAD_CONDITION_PREF_PREFIX = "devtools.responsive.reloadConditions.";
const RELOAD_NOTIFICATION_PREF = "devtools.responsive.reloadNotification.enabled";
function debug(msg) {
// console.log(`RDM manager: ${msg}`);
@ -221,42 +219,20 @@ const ResponsiveUIManager = exports.ResponsiveUIManager = {
}
},
showRemoteOnlyNotification(window, tab, options) {
this.showErrorNotification(window, tab, options, getStr("responsive.remoteOnly"));
showRemoteOnlyNotification(window, tab, { command } = {}) {
showNotification(window, tab, {
command,
msg: l10n.getStr("responsive.remoteOnly"),
priority: PriorityLevels.PRIORITY_CRITICAL_MEDIUM,
});
},
showNoContainerTabsNotification(window, tab, options) {
this.showErrorNotification(window, tab, options,
getStr("responsive.noContainerTabs"));
},
showErrorNotification(window, tab, { command } = {}, msg) {
// Default to using the browser's per-tab notification box
let nbox = window.gBrowser.getNotificationBox(tab.linkedBrowser);
// If opening was initiated by GCLI command bar or toolbox button, check for an open
// toolbox for the tab. If one exists, use the toolbox's notification box so that the
// message is placed closer to the action taken by the user.
if (command) {
let target = TargetFactory.forTab(tab);
let toolbox = gDevTools.getToolbox(target);
if (toolbox) {
nbox = toolbox.notificationBox;
}
}
let value = "devtools-responsive-error";
if (nbox.getNotificationWithValue(value)) {
// Notification already displayed
return;
}
nbox.appendNotification(
msg,
value,
null,
nbox.PRIORITY_CRITICAL_MEDIUM,
[]);
showNoContainerTabsNotification(window, tab, { command } = {}) {
showNotification(window, tab, {
command,
msg: l10n.getStr("responsive.noContainerTabs"),
priority: PriorityLevels.PRIORITY_CRITICAL_MEDIUM,
});
},
};
@ -405,10 +381,12 @@ ResponsiveUI.prototype = {
// settings are left in a customized state.
if (!isTabContentDestroying) {
let reloadNeeded = false;
reloadNeeded |= await this.updateDPPX();
reloadNeeded |= await this.updateNetworkThrottling();
reloadNeeded |= await this.updateUserAgent();
reloadNeeded |= await this.updateTouchSimulation();
await this.updateDPPX();
await this.updateNetworkThrottling();
reloadNeeded |= await this.updateUserAgent() &&
this.reloadOnChange("userAgent");
reloadNeeded |= await this.updateTouchSimulation() &&
this.reloadOnChange("touchSimulation");
if (reloadNeeded) {
this.getViewportBrowser().reload();
}
@ -450,6 +428,25 @@ ResponsiveUI.prototype = {
this.emulationFront = EmulationFront(this.client, tab);
},
/**
* Show one-time notification about reloads for emulation.
*/
showReloadNotification() {
if (Services.prefs.getBoolPref(RELOAD_NOTIFICATION_PREF, false)) {
showNotification(this.browserWindow, this.tab, {
msg: l10n.getFormatStr("responsive.reloadNotification.description",
l10n.getStr("responsive.reloadConditions.label")),
});
Services.prefs.setBoolPref(RELOAD_NOTIFICATION_PREF, false);
}
},
reloadOnChange(id) {
this.showReloadNotification();
let pref = RELOAD_CONDITION_PREF_PREFIX + id;
return Services.prefs.getBoolPref(pref, false);
},
handleEvent(event) {
let { browserWindow, tab } = this;
@ -499,10 +496,12 @@ ResponsiveUI.prototype = {
async onChangeDevice(event) {
let { userAgent, pixelRatio, touch } = event.data.device;
// Bug 1428799: Should we reload on UA change as well?
await this.updateUserAgent(userAgent);
let reloadNeeded = false;
await this.updateDPPX(pixelRatio);
let reloadNeeded = await this.updateTouchSimulation(touch);
reloadNeeded |= await this.updateUserAgent(userAgent) &&
this.reloadOnChange("userAgent");
reloadNeeded |= await this.updateTouchSimulation(touch) &&
this.reloadOnChange("touchSimulation");
if (reloadNeeded) {
this.getViewportBrowser().reload();
}
@ -524,7 +523,8 @@ ResponsiveUI.prototype = {
async onChangeTouchSimulation(event) {
let { enabled } = event.data;
let reloadNeeded = await this.updateTouchSimulation(enabled);
let reloadNeeded = await this.updateTouchSimulation(enabled) &&
this.reloadOnChange("touchSimulation");
if (reloadNeeded) {
this.getViewportBrowser().reload();
}
@ -546,10 +546,12 @@ ResponsiveUI.prototype = {
},
async onRemoveDeviceAssociation(event) {
// Bug 1428799: Should we reload on UA change as well?
await this.updateUserAgent();
let reloadNeeded = false;
await this.updateDPPX();
let reloadNeeded = await this.updateTouchSimulation();
reloadNeeded |= await this.updateUserAgent() &&
this.reloadOnChange("userAgent");
reloadNeeded |= await this.updateTouchSimulation() &&
this.reloadOnChange("touchSimulation");
if (reloadNeeded) {
this.getViewportBrowser().reload();
}

View File

@ -8,6 +8,7 @@ exports.devices = require("./reducers/devices");
exports.displayPixelRatio = require("./reducers/display-pixel-ratio");
exports.location = require("./reducers/location");
exports.networkThrottling = require("./reducers/network-throttling");
exports.reloadConditions = require("./reducers/reload-conditions");
exports.screenshot = require("./reducers/screenshot");
exports.touchSimulation = require("./reducers/touch-simulation");
exports.viewports = require("./reducers/viewports");

View File

@ -21,7 +21,7 @@ const INITIAL_DEVICES = {
types: [],
isModalOpen: false,
modalOpenedFromViewport: null,
listState: Types.deviceListState.INITIALIZED,
listState: Types.loadableState.INITIALIZED,
};
let reducers = {
@ -55,19 +55,19 @@ let reducers = {
[LOAD_DEVICE_LIST_START](devices, action) {
return Object.assign({}, devices, {
listState: Types.deviceListState.LOADING,
listState: Types.loadableState.LOADING,
});
},
[LOAD_DEVICE_LIST_ERROR](devices, action) {
return Object.assign({}, devices, {
listState: Types.deviceListState.ERROR,
listState: Types.loadableState.ERROR,
});
},
[LOAD_DEVICE_LIST_END](devices, action) {
return Object.assign({}, devices, {
listState: Types.deviceListState.LOADED,
listState: Types.loadableState.LOADED,
});
},

View File

@ -9,6 +9,7 @@ DevToolsModules(
'display-pixel-ratio.js',
'location.js',
'network-throttling.js',
'reload-conditions.js',
'screenshot.js',
'touch-simulation.js',
'viewports.js',

View File

@ -0,0 +1,42 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
CHANGE_RELOAD_CONDITION,
LOAD_RELOAD_CONDITIONS_END,
} = require("../actions/index");
const Types = require("../types");
const INITIAL_RELOAD_CONDITIONS = {
touchSimulation: false,
userAgent: false,
state: Types.loadableState.INITIALIZED,
};
let reducers = {
[CHANGE_RELOAD_CONDITION](conditions, { id, value }) {
return Object.assign({}, conditions, {
[id]: value,
});
},
[LOAD_RELOAD_CONDITIONS_END](conditions) {
return Object.assign({}, conditions, {
state: Types.loadableState.LOADED,
});
},
};
module.exports = function (conditions = INITIAL_RELOAD_CONDITIONS, action) {
let reducer = reducers[action.type];
if (!reducer) {
return conditions;
}
return reducer(conditions, action);
};

View File

@ -31,9 +31,11 @@ addDeviceForTest(testDevice);
addRDMTask(TEST_URL, async function ({ ui }) {
let { store } = ui.toolWindow;
reloadOnUAChange(true);
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
// Test defaults
testViewportDimensions(ui, 320, 480);
@ -72,6 +74,8 @@ addRDMTask(TEST_URL, async function ({ ui }) {
await testUserAgent(ui, DEFAULT_UA);
await testDevicePixelRatio(ui, 1);
await testTouchEventsOverride(ui, false);
reloadOnUAChange(false);
});
add_task(async function () {
@ -80,9 +84,11 @@ add_task(async function () {
let { store } = ui.toolWindow;
reloadOnUAChange(true);
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
// Select device with custom UA
let reloaded = waitForViewportLoad(ui);
@ -102,6 +108,8 @@ add_task(async function () {
await testUserAgentFromBrowser(tab.linkedBrowser, DEFAULT_UA);
await removeTab(tab);
reloadOnUAChange(false);
});
function testViewportDimensions(ui, w, h) {

View File

@ -32,7 +32,7 @@ addRDMTask(TEST_URL, async function ({ ui }) {
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
let deviceSelector = document.querySelector(".viewport-device-selector");
let submitButton = document.querySelector("#device-submit-button");
@ -73,7 +73,7 @@ addRDMTask(TEST_URL, async function ({ ui }) {
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
let deviceSelector = document.querySelector(".viewport-device-selector");
let submitButton = document.querySelector("#device-submit-button");
@ -116,7 +116,7 @@ addRDMTask(TEST_URL, async function ({ ui }) {
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
let deviceSelector = document.querySelector(".viewport-device-selector");
let submitButton = document.querySelector("#device-submit-button");
@ -150,7 +150,7 @@ addRDMTask(TEST_URL, async function ({ ui }) {
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
let deviceSelector = document.querySelector(".viewport-device-selector");

View File

@ -32,7 +32,7 @@ addRDMTask(TEST_URL, async function ({ ui }) {
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
let deviceSelector = document.querySelector(".viewport-device-selector");
let submitButton = document.querySelector("#device-submit-button");
@ -95,7 +95,7 @@ addRDMTask(TEST_URL, async function ({ ui }) {
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
let deviceSelector = document.querySelector(".viewport-device-selector");

View File

@ -23,7 +23,7 @@ addRDMTask(TEST_URL, async function ({ ui }) {
// Wait until the viewport has been added and the device list state indicates
// an error
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.ERROR);
&& state.devices.listState == Types.loadableState.ERROR);
// The device selector placeholder should be set accordingly
let placeholder = select.options[select.selectedIndex].innerHTML;

View File

@ -15,7 +15,7 @@ addRDMTask(TEST_URL, async function ({ ui }) {
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
openDeviceModal(ui);

View File

@ -29,7 +29,7 @@ addRDMTask(TEST_URL, async function ({ ui }) {
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
openDeviceModal(ui);
@ -116,7 +116,7 @@ addRDMTask(TEST_URL, async function ({ ui }) {
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
openDeviceModal(ui);

View File

@ -38,7 +38,7 @@ async function waitStartup(ui) {
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
}
async function testDefaults(ui) {
@ -56,9 +56,7 @@ async function testDefaults(ui) {
async function testChangingDevice(ui) {
info("Test Changing Device");
let reloaded = waitForViewportLoad(ui);
await selectDevice(ui, testDevice.name);
await reloaded;
await waitForViewportResizeTo(ui, testDevice.width, testDevice.height);
let dppx = await waitForDevicePixelRatio(ui, testDevice.pixelRatio);
is(dppx, testDevice.pixelRatio, "Content has expected devicePixelRatio");
@ -73,10 +71,9 @@ async function testResetWhenResizingViewport(ui) {
info("Test reset when resizing the viewport");
let deviceRemoved = once(ui, "device-association-removed");
let reloaded = waitForViewportLoad(ui);
await testViewportResize(ui, ".viewport-vertical-resize-handle",
[-10, -10], [testDevice.width, testDevice.height - 10], [0, -10], ui);
await Promise.all([ deviceRemoved, reloaded ]);
await deviceRemoved;
let dppx = await waitForDevicePixelRatio(ui, DEFAULT_DPPX);
is(dppx, DEFAULT_DPPX, "Content has expected devicePixelRatio");

View File

@ -23,6 +23,8 @@ const testDevice = {
addDeviceForTest(testDevice);
addRDMTask(TEST_URL, async function ({ ui, manager }) {
reloadOnTouchChange(true);
await waitStartup(ui);
await testDefaults(ui);
@ -31,6 +33,8 @@ addRDMTask(TEST_URL, async function ({ ui, manager }) {
await testEnableTouchSimulation(ui);
await testResizingViewport(ui, false, true);
await testDisableTouchSimulation(ui);
reloadOnTouchChange(false);
});
async function waitStartup(ui) {
@ -38,7 +42,7 @@ async function waitStartup(ui) {
// Wait until the viewport has been added and the device list has been loaded
await waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
&& state.devices.listState == Types.loadableState.LOADED);
}
async function testDefaults(ui) {

View File

@ -9,6 +9,8 @@ const TEST_URL = `${URL_ROOT}touch.html`;
const PREF_DOM_META_VIEWPORT_ENABLED = "dom.meta-viewport.enabled";
addRDMTask(TEST_URL, async function ({ ui }) {
reloadOnTouchChange(true);
await injectEventUtilsInContentTask(ui.getViewportBrowser());
await waitBootstrap(ui);
@ -18,6 +20,8 @@ addRDMTask(TEST_URL, async function ({ ui }) {
await testWithMetaViewportEnabled(ui);
await testWithMetaViewportDisabled(ui);
testTouchButton(ui);
reloadOnTouchChange(false);
});
async function testWithNoTouch(ui) {

View File

@ -34,6 +34,7 @@ Services.scriptloader.loadSubScript(
const E10S_MULTI_ENABLED = Services.prefs.getIntPref("dom.ipc.processCount") > 1;
const TEST_URI_ROOT = "http://example.com/browser/devtools/client/responsive.html/test/browser/";
const OPEN_DEVICE_MODAL_VALUE = "OPEN_DEVICE_MODAL";
const RELOAD_CONDITION_PREF_PREFIX = "devtools.responsive.reloadConditions.";
const { _loadPreferredDevices } = require("devtools/client/responsive.html/actions/devices");
const asyncStorage = require("devtools/shared/async-storage");
@ -48,14 +49,14 @@ SimpleTest.waitForExplicitFinish();
requestLongerTimeout(2);
flags.testing = true;
Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
Services.prefs.setCharPref("devtools.devices.url",
TEST_URI_ROOT + "devices.json");
Services.prefs.setCharPref("devtools.devices.url", TEST_URI_ROOT + "devices.json");
registerCleanupFunction(async () => {
flags.testing = false;
Services.prefs.clearUserPref("devtools.devices.url");
Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
Services.prefs.clearUserPref("devtools.responsive.reloadConditions.touchSimulation");
Services.prefs.clearUserPref("devtools.responsive.reloadConditions.userAgent");
await asyncStorage.removeItem("devtools.devices.url_cache");
await removeLocalDevices();
});
@ -419,3 +420,13 @@ function addDeviceInModal(ui, device) {
Simulate.click(adderSave);
return saved;
}
function reloadOnUAChange(enabled) {
let pref = RELOAD_CONDITION_PREF_PREFIX + "userAgent";
Services.prefs.setBoolPref(pref, enabled);
}
function reloadOnTouchChange(enabled) {
let pref = RELOAD_CONDITION_PREF_PREFIX + "touchSimulation";
Services.prefs.setBoolPref(pref, enabled);
}

View File

@ -10,6 +10,18 @@ const { createEnum } = require("devtools/client/shared/enum");
// React PropTypes are used to describe the expected "shape" of various common
// objects that get passed down as props to components.
/* ENUMS */
/**
* An enum containing the possible states for loadable things.
*/
exports.loadableState = createEnum([
"INITIALIZED",
"LOADING",
"LOADED",
"ERROR",
]);
/* GLOBAL */
/**
@ -17,6 +29,22 @@ const { createEnum } = require("devtools/client/shared/enum");
*/
exports.location = PropTypes.string;
/**
* Whether to reload the page automatically when certain actions occur.
*/
exports.reloadConditions = {
// Whether to reload when touch simulation is toggled
touchSimulation: PropTypes.bool,
// Whether to reload when user agent is changed
userAgent: PropTypes.bool,
// Loaded state of these conditions
state: PropTypes.oneOf(Object.keys(exports.loadableState)),
};
/* DEVICE */
/**
@ -50,16 +78,6 @@ const device = {
};
/**
* An enum containing the possible values for the device list state
*/
exports.deviceListState = createEnum([
"INITIALIZED",
"LOADING",
"LOADED",
"ERROR",
]);
/**
* A list of devices and their types that can be displayed in the viewport.
*/
@ -93,7 +111,7 @@ exports.devices = {
modalOpenedFromViewport: PropTypes.number,
// Device list state, possible values are exported above in an enum
listState: PropTypes.oneOf(Object.keys(exports.deviceListState)),
listState: PropTypes.oneOf(Object.keys(exports.loadableState)),
};

View File

@ -10,5 +10,6 @@ DevToolsModules(
'key.js',
'l10n.js',
'message.js',
'notification.js',
'window.js',
)

View File

@ -0,0 +1,53 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
/**
* Displays a notification either at the browser or toolbox level, depending on whether
* a toolbox is currently open for this tab.
*
* @param window
* The main browser chrome window.
* @param tab
* The browser tab.
* @param options
* Other options associated with opening. Currently includes:
* - `command`: Whether initiated via GCLI command bar or toolbox button
* - `msg`: String to show in the notification
* - `priority`: Priority level for the notification, which affects the icon and
* overall appearance.
*/
function showNotification(window, tab, { command, msg, priority } = {}) {
// Default to using the browser's per-tab notification box
let nbox = window.gBrowser.getNotificationBox(tab.linkedBrowser);
// If opening was initiated by GCLI command bar or toolbox button, check for an open
// toolbox for the tab. If one exists, use the toolbox's notification box so that the
// message is placed closer to the action taken by the user.
if (command) {
let target = TargetFactory.forTab(tab);
let toolbox = gDevTools.getToolbox(target);
if (toolbox) {
nbox = toolbox.notificationBox;
}
}
let value = "devtools-responsive";
if (nbox.getNotificationWithValue(value)) {
// Notification already displayed
return;
}
if (!priority) {
priority = nbox.PRIORITY_INFO_MEDIUM;
}
nbox.appendNotification(msg, value, null, priority, []);
}
exports.showNotification = showNotification;

View File

@ -994,11 +994,6 @@ a.learn-more-link.webconsole-learn-more-link {
font-size: 0.8em;
}
/* Prefix text that can be set by ConsoleAPI option */
.webconsole-output-wrapper .console-message-prefix {
color: var(--theme-comment);
}
/* Network Messages */
.webconsole-output-wrapper .message.network .method {

View File

@ -51,7 +51,6 @@ function ConsoleApiCall(props) {
timeStamp,
parameters,
messageText,
prefix,
userProvidedStyles,
} = message;
@ -75,11 +74,6 @@ function ConsoleApiCall(props) {
messageBody = dom.span({className: "cm-variable"}, "console.table()");
} else if (parameters) {
messageBody = formatReps(messageBodyConfig);
if (prefix) {
messageBody.unshift(dom.span({
className: "console-message-prefix"
}, `${prefix}: `));
}
} else {
messageBody = messageText;
}

View File

@ -703,8 +703,6 @@ function passSearchFilters(message, filters) {
|| isTextInMessageText(text, message.messageText)
// Look for a match in notes.
|| isTextInNotes(text, message.notes)
// Look for a match in prefix.
|| isTextInPrefix(text, message.prefix)
);
}
@ -806,17 +804,6 @@ function isTextInNotes(text, notes) {
);
}
/**
* Returns true if given text is included in prefix.
*/
function isTextInPrefix(text, prefix) {
if (!prefix) {
return false;
}
return `${prefix}: `.toLocaleLowerCase().includes(text.toLocaleLowerCase());
}
/**
* Get a flat array of all the grips and their properties.
*

View File

@ -19,7 +19,6 @@ const {
MESSAGE_CLOSE,
} = require("devtools/client/webconsole/new-console-output/constants");
const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/MessageIndent");
const {prepareMessage} = require("devtools/client/webconsole/new-console-output/utils/messages");
// Test fakes.
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
@ -88,37 +87,6 @@ describe("ConsoleAPICall component:", () => {
expect(thirdElementStyle.color).toBe("blue");
});
it("renders prefixed messages", () => {
const stub = {
"level": "debug",
"filename": "resource:///modules/CustomizableUI.jsm",
"lineNumber": 181,
"functionName": "initialize",
"timeStamp": 1519311532912,
"arguments": [
"Initializing"
],
"prefix": "MyNicePrefix",
"workerType": "none",
"styles": [],
"category": "webdev",
"_type": "ConsoleAPI"
};
const wrapper = render(ConsoleApiCall({
message: prepareMessage(stub, {getNextId: () => "p"}),
serviceContainer
}));
const prefix = wrapper.find(".console-message-prefix");
expect(prefix.text()).toBe("MyNicePrefix: ");
expect(wrapper.find(".message-body").text()).toBe("MyNicePrefix: Initializing");
// There should be the location
const locationLink = wrapper.find(`.message-location`);
expect(locationLink.length).toBe(1);
expect(locationLink.text()).toBe("CustomizableUI.jsm:181");
});
it("renders repeat node", () => {
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
const wrapper = render(ConsoleApiCall({

View File

@ -40,7 +40,7 @@ describe("Message - location element", () => {
expect(onViewSource.notCalled).toBe(true);
});
it("Calls onViewSource when clicked and onViewSourceInDebugger undefined", () => {
it.only("Calls onViewSource when clicked and onViewSourceInDebugger undefined", () => {
const onViewSource = sinon.spy();
const message = stubPreparedMessages.get("console.log('foobar', 'test')");

View File

@ -186,37 +186,6 @@ describe("Filtering", () => {
expect(messages.length - numUnfilterableMessages).toEqual(1);
});
it("matches prefixed log message", () => {
const stub = {
"level": "debug",
"filename": "resource:///modules/CustomizableUI.jsm",
"lineNumber": 181,
"functionName": "initialize",
"timeStamp": 1519311532912,
"arguments": [
"Initializing"
],
"prefix": "MyNicePrefix",
"workerType": "none",
"styles": [],
"category": "webdev",
"_type": "ConsoleAPI"
};
store.dispatch(messagesAdd([stub]));
store.dispatch(actions.filterTextSet("MyNice"));
let messages = getVisibleMessages(store.getState());
expect(messages.length - numUnfilterableMessages).toEqual(1);
store.dispatch(actions.filterTextSet("MyNicePrefix"));
messages = getVisibleMessages(store.getState());
expect(messages.length - numUnfilterableMessages).toEqual(1);
store.dispatch(actions.filterTextSet("MyNicePrefix:"));
messages = getVisibleMessages(store.getState());
expect(messages.length - numUnfilterableMessages).toEqual(1);
});
it("restores all messages once text is cleared", () => {
store.dispatch(actions.filterTextSet("danger"));
store.dispatch(actions.filterTextSet(""));

View File

@ -43,7 +43,6 @@ exports.ConsoleMessage = function (props) {
userProvidedStyles: null,
notes: null,
indent: 0,
prefix: "",
}, props);
};

View File

@ -20,6 +20,7 @@ const {
} = require("../types");
function prepareMessage(packet, idGenerator) {
// This packet is already in the expected packet structure. Simply return.
if (!packet.source) {
packet = transformPacket(packet);
}
@ -166,7 +167,6 @@ function transformConsoleAPICallPacket(packet) {
frame,
timeStamp: message.timeStamp,
userProvidedStyles: message.styles,
prefix: message.prefix,
});
}

View File

@ -1193,8 +1193,16 @@ GeckoChildProcessHost::LaunchAndroidService(const char* type,
int32_t ipcFd = it->first;
it++;
// If the Crash Reporter is disabled, there will not be a second file descriptor.
int32_t crashFd = (it != fds_to_remap.end()) ? it->first : -1;
int32_t crashAnnotationFd = (it != fds_to_remap.end()) ? it->first : -1;
int32_t crashFd = -1;
int32_t crashAnnotationFd = -1;
if (it != fds_to_remap.end() && !CrashReporter::IsDummy()) {
crashFd = it->first;
it++;
}
if (it != fds_to_remap.end()) {
crashAnnotationFd = it->first;
it++;
}
int32_t handle = java::GeckoProcessManager::Start(type, jargs, crashFd, ipcFd, crashAnnotationFd);
if (process_handle) {

View File

@ -22,10 +22,12 @@ ServoStyleContext::ResolveSameStructsAs(const ServoStyleContext* aOther)
uint64_t otherBits = aOther->mBits & NS_STYLE_INHERIT_MASK;
uint64_t newBits = otherBits & ~ourBits & NS_STYLE_INHERIT_MASK;
#define STYLE_STRUCT(name_, checkdata_cb) \
if (nsStyle##name_::kHasFinishStyle && newBits & NS_STYLE_INHERIT_BIT(name_)) { \
const nsStyle##name_* data = ComputedData()->GetStyle##name_(); \
const_cast<nsStyle##name_*>(data)->FinishStyle(mPresContext); \
#define STYLE_STRUCT(name_, checkdata_cb) \
if (nsStyle##name_::kHasFinishStyle && \
(newBits & NS_STYLE_INHERIT_BIT(name_))) { \
const nsStyle##name_* data = ComputedData()->GetStyle##name_(); \
const nsStyle##name_* oldData = aOther->ComputedData()->GetStyle##name_(); \
const_cast<nsStyle##name_*>(data)->FinishStyle(mPresContext, oldData); \
}
#include "nsStyleStructList.h"
#undef STYLE_STRUCT

View File

@ -105,28 +105,28 @@ const nsStyle##name_ * nsStyleContext::PeekStyle##name_() { \
#endif
// Helper functions for GetStyle* and PeekStyle*
#define STYLE_STRUCT_INHERITED(name_, checkdata_cb_) \
template<bool aComputeData> \
const nsStyle##name_ * nsStyleContext::DoGetStyle##name_() { \
if (IsGecko()) { \
DO_GET_STYLE_INHERITED_GECKO(name_, checkdata_cb_) \
} \
const bool needToCompute = !(mBits & NS_STYLE_INHERIT_BIT(name_));\
if (!aComputeData && needToCompute) { \
return nullptr; \
} \
\
const nsStyle##name_* data = ComputedData()->GetStyle##name_(); \
\
/* perform any remaining main thread work on the struct */ \
if (needToCompute) { \
MOZ_ASSERT(NS_IsMainThread()); \
MOZ_ASSERT(!mozilla::ServoStyleSet::IsInServoTraversal()); \
const_cast<nsStyle##name_*>(data)->FinishStyle(PresContext()); \
/* the ServoStyleContext owns the struct */ \
AddStyleBit(NS_STYLE_INHERIT_BIT(name_)); \
} \
return data; \
#define STYLE_STRUCT_INHERITED(name_, checkdata_cb_) \
template<bool aComputeData> \
const nsStyle##name_ * nsStyleContext::DoGetStyle##name_() { \
if (IsGecko()) { \
DO_GET_STYLE_INHERITED_GECKO(name_, checkdata_cb_) \
} \
const bool needToCompute = !(mBits & NS_STYLE_INHERIT_BIT(name_)); \
if (!aComputeData && needToCompute) { \
return nullptr; \
} \
\
const nsStyle##name_* data = ComputedData()->GetStyle##name_(); \
\
/* perform any remaining main thread work on the struct */ \
if (needToCompute) { \
MOZ_ASSERT(NS_IsMainThread()); \
MOZ_ASSERT(!mozilla::ServoStyleSet::IsInServoTraversal()); \
const_cast<nsStyle##name_*>(data)->FinishStyle(PresContext(), nullptr); \
/* the ServoStyleContext owns the struct */ \
AddStyleBit(NS_STYLE_INHERIT_BIT(name_)); \
} \
return data; \
}
#ifdef MOZ_OLD_STYLE
@ -162,7 +162,7 @@ const nsStyle##name_ * nsStyleContext::DoGetStyle##name_() { \
const nsStyle##name_* data = ComputedData()->GetStyle##name_(); \
/* perform any remaining main thread work on the struct */ \
if (needToCompute) { \
const_cast<nsStyle##name_*>(data)->FinishStyle(PresContext()); \
const_cast<nsStyle##name_*>(data)->FinishStyle(PresContext(), nullptr); \
/* the ServoStyleContext owns the struct */ \
AddStyleBit(NS_STYLE_INHERIT_BIT(name_)); \
} \

View File

@ -393,12 +393,13 @@ nsStyleBorder::~nsStyleBorder()
}
void
nsStyleBorder::FinishStyle(nsPresContext* aPresContext)
nsStyleBorder::FinishStyle(nsPresContext* aPresContext, const nsStyleBorder* aOldStyle)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPresContext->StyleSet()->IsServo());
mBorderImageSource.ResolveImage(aPresContext);
mBorderImageSource.ResolveImage(
aPresContext, aOldStyle ? &aOldStyle->mBorderImageSource : nullptr);
}
nsMargin
@ -613,13 +614,14 @@ nsStyleList::nsStyleList(const nsStyleList& aSource)
}
void
nsStyleList::FinishStyle(nsPresContext* aPresContext)
nsStyleList::FinishStyle(nsPresContext* aPresContext, const nsStyleList* aOldStyle)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPresContext->StyleSet()->IsServo());
if (mListStyleImage && !mListStyleImage->IsResolved()) {
mListStyleImage->Resolve(aPresContext);
mListStyleImage->Resolve(
aPresContext, aOldStyle ? aOldStyle->mListStyleImage.get() : nullptr);
}
mCounterStyle.Resolve(aPresContext->CounterStyleManager());
}
@ -1300,7 +1302,7 @@ nsStyleSVGReset::Destroy(nsPresContext* aContext)
}
void
nsStyleSVGReset::FinishStyle(nsPresContext* aPresContext)
nsStyleSVGReset::FinishStyle(nsPresContext* aPresContext, const nsStyleSVGReset* aOldStyle)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPresContext->StyleSet()->IsServo());
@ -1313,7 +1315,12 @@ nsStyleSVGReset::FinishStyle(nsPresContext* aPresContext)
// resolve this style image, since we do not depend on it to get the
// SVG mask resource.
if (!image.GetURLValue()->HasRef()) {
image.ResolveImage(aPresContext);
const nsStyleImage* oldImage =
(aOldStyle && aOldStyle->mMask.mLayers.Length() > i)
? &aOldStyle->mMask.mLayers[i].mImage
: nullptr;
image.ResolveImage(aPresContext, oldImage);
}
}
}
@ -2191,7 +2198,9 @@ nsStyleImageRequest::~nsStyleImageRequest()
}
bool
nsStyleImageRequest::Resolve(nsPresContext* aPresContext)
nsStyleImageRequest::Resolve(
nsPresContext* aPresContext,
const nsStyleImageRequest* aOldImageRequest)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsResolved(), "already resolved");
@ -2211,14 +2220,29 @@ nsStyleImageRequest::Resolve(nsPresContext* aPresContext)
}
}
mDocGroup = doc->GetDocGroup();
// TODO(emilio, bug 1440442): This is a hackaround to avoid flickering due the
// lack of non-http image caching in imagelib (bug 1406134), which causes
// stuff like bug 1439285. Cleanest fix if that doesn't get fixed is bug
// 1440305, but that seems too risky, and a lot of work to do before 60.
//
// Once that's fixed, the "old style" argument to FinishStyle can go away.
if (aPresContext->IsChrome() && aOldImageRequest &&
aOldImageRequest->IsResolved() && DefinitelyEquals(*aOldImageRequest)) {
MOZ_ASSERT(aOldImageRequest->mDocGroup == doc->GetDocGroup());
MOZ_ASSERT(mModeFlags == aOldImageRequest->mModeFlags);
mImageValue->Initialize(doc);
mDocGroup = aOldImageRequest->mDocGroup;
mImageValue = aOldImageRequest->mImageValue;
mRequestProxy = aOldImageRequest->mRequestProxy;
} else {
mDocGroup = doc->GetDocGroup();
mImageValue->Initialize(doc);
nsCSSValue value;
value.SetImageValue(mImageValue);
mRequestProxy = value.GetPossiblyStaticImageValue(aPresContext->Document(),
aPresContext);
nsCSSValue value;
value.SetImageValue(mImageValue);
mRequestProxy = value.GetPossiblyStaticImageValue(aPresContext->Document(),
aPresContext);
}
if (!mRequestProxy) {
// The URL resolution or image load failed.
@ -2226,7 +2250,7 @@ nsStyleImageRequest::Resolve(nsPresContext* aPresContext)
}
if (mModeFlags & Mode::Track) {
mImageTracker = aPresContext->Document()->ImageTracker();
mImageTracker = doc->ImageTracker();
}
MaybeTrackAndLock();
@ -3345,12 +3369,13 @@ nsStyleBackground::Destroy(nsPresContext* aContext)
}
void
nsStyleBackground::FinishStyle(nsPresContext* aPresContext)
nsStyleBackground::FinishStyle(
nsPresContext* aPresContext, const nsStyleBackground* aOldStyle)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPresContext->StyleSet()->IsServo());
mImage.ResolveImages(aPresContext);
mImage.ResolveImages(aPresContext, aOldStyle ? &aOldStyle->mImage : nullptr);
}
nsChangeHint
@ -3724,7 +3749,8 @@ nsStyleDisplay::~nsStyleDisplay()
}
void
nsStyleDisplay::FinishStyle(nsPresContext* aPresContext)
nsStyleDisplay::FinishStyle(
nsPresContext* aPresContext, const nsStyleDisplay* aOldStyle)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPresContext->StyleSet()->IsServo());
@ -3739,7 +3765,11 @@ nsStyleDisplay::FinishStyle(nsPresContext* aPresContext)
shapeImage->GetImageRequest()->GetImageValue()->SetCORSMode(
CORSMode::CORS_ANONYMOUS);
}
shapeImage->ResolveImage(aPresContext);
const nsStyleImage* oldShapeImage =
(aOldStyle &&
aOldStyle->mShapeOutside.GetType() == StyleShapeSourceType::Image)
? &*aOldStyle->mShapeOutside.GetShapeImage() : nullptr;
shapeImage->ResolveImage(aPresContext, oldShapeImage);
}
}
@ -4016,6 +4046,10 @@ nsStyleDisplay::CalcDifference(const nsStyleDisplay& aNewData) const
void
nsStyleDisplay::GenerateCombinedTransform()
{
// FIXME(emilio): This should probably be called from somewhere like what we
// do for image layers, instead of FinishStyle.
//
// This does and undoes the work a ton of times in Stylo.
mCombinedTransform = nullptr;
// Follow the order defined in the spec to append transform functions.
@ -4208,12 +4242,16 @@ nsStyleContentData::operator==(const nsStyleContentData& aOther) const
}
void
nsStyleContentData::Resolve(nsPresContext* aPresContext)
nsStyleContentData::Resolve(
nsPresContext* aPresContext, const nsStyleContentData* aOldStyle)
{
switch (mType) {
case eStyleContentType_Image:
if (!mContent.mImage->IsResolved()) {
mContent.mImage->Resolve(aPresContext);
const nsStyleImageRequest* oldRequest =
(aOldStyle && aOldStyle->mType == eStyleContentType_Image)
? aOldStyle->mContent.mImage : nullptr;
mContent.mImage->Resolve(aPresContext, oldRequest);
}
break;
case eStyleContentType_Counter:
@ -4250,10 +4288,14 @@ nsStyleContent::Destroy(nsPresContext* aContext)
}
void
nsStyleContent::FinishStyle(nsPresContext* aPresContext)
nsStyleContent::FinishStyle(nsPresContext* aPresContext, const nsStyleContent* aOldStyle)
{
for (nsStyleContentData& data : mContents) {
data.Resolve(aPresContext);
for (size_t i = 0; i < mContents.Length(); ++i) {
const nsStyleContentData* oldData =
(aOldStyle && aOldStyle->mContents.Length() > i)
? &aOldStyle->mContents[i]
: nullptr;
mContents[i].Resolve(aPresContext, oldData);
}
}
@ -4631,14 +4673,22 @@ nsStyleUserInterface::~nsStyleUserInterface()
}
void
nsStyleUserInterface::FinishStyle(nsPresContext* aPresContext)
nsStyleUserInterface::FinishStyle(
nsPresContext* aPresContext, const nsStyleUserInterface* aOldStyle)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPresContext->StyleSet()->IsServo());
for (nsCursorImage& cursor : mCursorImages) {
for (size_t i = 0; i < mCursorImages.Length(); ++i) {
nsCursorImage& cursor = mCursorImages[i];
if (cursor.mImage && !cursor.mImage->IsResolved()) {
cursor.mImage->Resolve(aPresContext);
const nsCursorImage* oldCursor =
(aOldStyle && aOldStyle->mCursorImages.Length() > i)
? &aOldStyle->mCursorImages[i]
: nullptr;
cursor.mImage->Resolve(
aPresContext, oldCursor ? oldCursor->mImage.get() : nullptr);
}
}
}

View File

@ -165,7 +165,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleFont
~nsStyleFont() {
MOZ_COUNT_DTOR(nsStyleFont);
}
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleFont*) {}
const static bool kHasFinishStyle = false;
nsChangeHint CalcDifference(const nsStyleFont& aNewData) const;
@ -358,7 +358,7 @@ public:
Mode aModeFlags,
mozilla::css::ImageValue* aImageValue);
bool Resolve(nsPresContext* aPresContext);
bool Resolve(nsPresContext*, const nsStyleImageRequest* aOldImageRequest);
bool IsResolved() const { return mResolved; }
imgRequestProxy* get() {
@ -455,10 +455,13 @@ struct nsStyleImage
void SetCropRect(mozilla::UniquePtr<nsStyleSides> aCropRect);
void SetURLValue(already_AddRefed<URLValue> aData);
void ResolveImage(nsPresContext* aContext) {
void ResolveImage(nsPresContext* aContext, const nsStyleImage* aOldImage) {
MOZ_ASSERT(mType != eStyleImageType_Image || mImage);
if (mType == eStyleImageType_Image && !mImage->IsResolved()) {
mImage->Resolve(aContext);
const nsStyleImageRequest* oldRequest =
(aOldImage && aOldImage->GetType() == eStyleImageType_Image)
? aOldImage->GetImageRequest() : nullptr;
mImage->Resolve(aContext, oldRequest);
}
}
@ -594,7 +597,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleColor
~nsStyleColor() {
MOZ_COUNT_DTOR(nsStyleColor);
}
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleColor*) {}
const static bool kHasFinishStyle = false;
nscolor CalcComplexColor(const mozilla::StyleComplexColor& aColor) const {
@ -785,10 +788,8 @@ struct nsStyleImageLayers {
// Initialize mRepeat and mOrigin by specified layer type
void Initialize(LayerType aType);
void ResolveImage(nsPresContext* aContext) {
if (mImage.GetType() == eStyleImageType_Image) {
mImage.ResolveImage(aContext);
}
void ResolveImage(nsPresContext* aContext, const Layer* aOldLayer) {
mImage.ResolveImage(aContext, aOldLayer ? &aOldLayer->mImage : nullptr);
}
// True if the rendering of this layer might change when the size
@ -837,9 +838,13 @@ struct nsStyleImageLayers {
const Layer& BottomLayer() const { return mLayers[mImageCount - 1]; }
void ResolveImages(nsPresContext* aContext) {
void ResolveImages(nsPresContext* aContext, const nsStyleImageLayers* aOldLayers) {
for (uint32_t i = 0; i < mImageCount; ++i) {
mLayers[i].ResolveImage(aContext);
const Layer* oldLayer =
(aOldLayers && aOldLayers->mLayers.Length() > i)
? &aOldLayers->mLayers[i]
: nullptr;
mLayers[i].ResolveImage(aContext, oldLayer);
}
}
@ -873,7 +878,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBackground {
// Resolves and tracks the images in mImage. Only called with a Servo-backed
// style system, where those images must be resolved later than the OMT
// nsStyleBackground constructor call.
void FinishStyle(nsPresContext* aPresContext);
void FinishStyle(nsPresContext*, const nsStyleBackground*);
const static bool kHasFinishStyle = true;
void* operator new(size_t sz, nsStyleBackground* aSelf) { return aSelf; }
@ -916,7 +921,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleMargin
~nsStyleMargin() {
MOZ_COUNT_DTOR(nsStyleMargin);
}
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleMargin*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleMargin* aSelf) { return aSelf; }
@ -955,7 +960,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStylePadding
~nsStylePadding() {
MOZ_COUNT_DTOR(nsStylePadding);
}
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStylePadding*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStylePadding* aSelf) { return aSelf; }
@ -1122,7 +1127,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBorder
// Resolves and tracks mBorderImageSource. Only called with a Servo-backed
// style system, where those images must be resolved later than the OMT
// nsStyleBorder constructor call.
void FinishStyle(nsPresContext* aPresContext);
void FinishStyle(nsPresContext*, const nsStyleBorder*);
const static bool kHasFinishStyle = true;
void* operator new(size_t sz, nsStyleBorder* aSelf) { return aSelf; }
@ -1196,13 +1201,6 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBorder
return mBorderImageSource.IsLoaded();
}
void ResolveImage(nsPresContext* aContext)
{
if (mBorderImageSource.GetType() == eStyleImageType_Image) {
mBorderImageSource.ResolveImage(aContext);
}
}
nsMargin GetImageOutset() const;
imgIRequest* GetBorderImageRequest() const
@ -1307,7 +1305,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleOutline
~nsStyleOutline() {
MOZ_COUNT_DTOR(nsStyleOutline);
}
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleOutline*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleOutline* aSelf) { return aSelf; }
@ -1379,7 +1377,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleList
nsStyleList(const nsStyleList& aStyleList);
~nsStyleList();
void FinishStyle(nsPresContext* aPresContext);
void FinishStyle(nsPresContext*, const nsStyleList*);
const static bool kHasFinishStyle = true;
void* operator new(size_t sz, nsStyleList* aSelf) { return aSelf; }
@ -1581,7 +1579,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStylePosition
explicit nsStylePosition(const nsPresContext* aContext);
nsStylePosition(const nsStylePosition& aOther);
~nsStylePosition();
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStylePosition*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStylePosition* aSelf) { return aSelf; }
@ -1806,7 +1804,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleTextReset
explicit nsStyleTextReset(const nsPresContext* aContext);
nsStyleTextReset(const nsStyleTextReset& aOther);
~nsStyleTextReset();
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleTextReset*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleTextReset* aSelf) { return aSelf; }
@ -1844,7 +1842,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleText
explicit nsStyleText(const nsPresContext* aContext);
nsStyleText(const nsStyleText& aOther);
~nsStyleText();
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleText*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleText* aSelf) { return aSelf; }
@ -2064,7 +2062,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleVisibility
~nsStyleVisibility() {
MOZ_COUNT_DTOR(nsStyleVisibility);
}
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleVisibility*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleVisibility* aSelf) { return aSelf; }
@ -2485,7 +2483,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay
nsStyleDisplay(const nsStyleDisplay& aOther);
~nsStyleDisplay();
void FinishStyle(nsPresContext* aPresContext);
void FinishStyle(nsPresContext*, const nsStyleDisplay*);
const static bool kHasFinishStyle = true;
void* operator new(size_t sz, nsStyleDisplay* aSelf) { return aSelf; }
@ -2874,7 +2872,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleTable
explicit nsStyleTable(const nsPresContext* aContext);
nsStyleTable(const nsStyleTable& aOther);
~nsStyleTable();
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleTable*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleTable* aSelf) { return aSelf; }
@ -2899,7 +2897,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleTableBorder
explicit nsStyleTableBorder(const nsPresContext* aContext);
nsStyleTableBorder(const nsStyleTableBorder& aOther);
~nsStyleTableBorder();
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleTableBorder*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleTableBorder* aSelf) { return aSelf; }
@ -3045,7 +3043,7 @@ public:
MOZ_ASSERT(mContent.mImage);
}
void Resolve(nsPresContext* aPresContext);
void Resolve(nsPresContext*, const nsStyleContentData*);
private:
nsStyleContentType mType;
@ -3075,7 +3073,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleContent
explicit nsStyleContent(const nsPresContext* aContext);
nsStyleContent(const nsStyleContent& aContent);
~nsStyleContent();
void FinishStyle(nsPresContext* aPresContext);
void FinishStyle(nsPresContext*, const nsStyleContent*);
const static bool kHasFinishStyle = true;
void* operator new(size_t sz, nsStyleContent* aSelf) { return aSelf; }
@ -3146,7 +3144,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUIReset
explicit nsStyleUIReset(const nsPresContext* aContext);
nsStyleUIReset(const nsStyleUIReset& aOther);
~nsStyleUIReset();
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleUIReset*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleUIReset* aSelf) { return aSelf; }
@ -3200,7 +3198,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUserInterface
nsStyleUserInterface(const nsStyleUserInterface& aOther);
~nsStyleUserInterface();
void FinishStyle(nsPresContext* aPresContext);
void FinishStyle(nsPresContext*, const nsStyleUserInterface*);
const static bool kHasFinishStyle = true;
void* operator new(size_t sz, nsStyleUserInterface* aSelf) { return aSelf; }
@ -3233,7 +3231,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleXUL
explicit nsStyleXUL(const nsPresContext* aContext);
nsStyleXUL(const nsStyleXUL& aSource);
~nsStyleXUL();
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleXUL*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleXUL* aSelf) { return aSelf; }
@ -3263,7 +3261,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleColumn
explicit nsStyleColumn(const nsPresContext* aContext);
nsStyleColumn(const nsStyleColumn& aSource);
~nsStyleColumn();
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleColumn*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleColumn* aSelf) { return aSelf; }
@ -3398,7 +3396,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleSVG
explicit nsStyleSVG(const nsPresContext* aContext);
nsStyleSVG(const nsStyleSVG& aSource);
~nsStyleSVG();
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleSVG*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleSVG* aSelf) { return aSelf; }
@ -3535,7 +3533,7 @@ struct nsStyleFilter
nsStyleFilter();
nsStyleFilter(const nsStyleFilter& aSource);
~nsStyleFilter();
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleFilter*) {}
const static bool kHasFinishStyle = false;
nsStyleFilter& operator=(const nsStyleFilter& aOther);
@ -3597,7 +3595,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleSVGReset
// Resolves and tracks the images in mMask. Only called with a Servo-backed
// style system, where those images must be resolved later than the OMT
// nsStyleSVGReset constructor call.
void FinishStyle(nsPresContext* aPresContext);
void FinishStyle(nsPresContext*, const nsStyleSVGReset*);
const static bool kHasFinishStyle = true;
void* operator new(size_t sz, nsStyleSVGReset* aSelf) { return aSelf; }
@ -3640,7 +3638,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleVariables
explicit nsStyleVariables(const nsPresContext* aContext);
nsStyleVariables(const nsStyleVariables& aSource);
~nsStyleVariables();
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleVariables*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleVariables* aSelf) { return aSelf; }
@ -3666,7 +3664,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleEffects
explicit nsStyleEffects(const nsPresContext* aContext);
nsStyleEffects(const nsStyleEffects& aSource);
~nsStyleEffects();
void FinishStyle(nsPresContext* aPresContext) {}
void FinishStyle(nsPresContext*, const nsStyleEffects*) {}
const static bool kHasFinishStyle = false;
void* operator new(size_t sz, nsStyleEffects* aSelf) { return aSelf; }

View File

@ -425,7 +425,7 @@ nsSVGMaskProperty::ResolveImage(uint32_t aIndex)
if (!image.IsResolved()) {
MOZ_ASSERT(image.GetType() == nsStyleImageType::eStyleImageType_Image);
image.ResolveImage(mFrame->PresContext());
image.ResolveImage(mFrame->PresContext(), nullptr);
mozilla::css::ImageLoader* imageLoader =
mFrame->PresContext()->Document()->StyleImageLoader();

View File

@ -112,6 +112,7 @@ pub enum Command {
AddWebFont(LowercaseString, EffectiveSources, IpcSender<()>),
AddDownloadedWebFont(LowercaseString, ServoUrl, Vec<u8>, IpcSender<()>),
Exit(IpcSender<()>),
Ping,
}
/// Reply messages sent from the font cache thread to the FontContext caller.
@ -204,6 +205,7 @@ impl FontCache {
templates.add_template(Atom::from(url.to_string()), Some(bytes));
drop(result.send(()));
}
Command::Ping => (),
Command::Exit(result) => {
let _ = result.send(());
break;
@ -472,10 +474,13 @@ impl FontSource for FontCacheThread {
self.chan.send(Command::GetFontInstance(key, size, response_chan))
.expect("failed to send message to font cache thread");
let instance_key = response_port.recv()
.expect("failed to receive response to font request");
instance_key
let instance_key = response_port.recv();
if instance_key.is_err() {
let font_thread_has_closed = self.chan.send(Command::Ping).is_err();
assert!(font_thread_has_closed, "Failed to receive a response from live font cache");
panic!("Font cache thread has already exited.");
}
instance_key.unwrap()
}
fn find_font_template(&mut self, family: SingleFontFamily, desc: FontTemplateDescriptor)
@ -485,10 +490,15 @@ impl FontSource for FontCacheThread {
self.chan.send(Command::GetFontTemplate(family, desc, response_chan))
.expect("failed to send message to font cache thread");
let reply = response_port.recv()
.expect("failed to receive response to font request");
let reply = response_port.recv();
match reply {
if reply.is_err() {
let font_thread_has_closed = self.chan.send(Command::Ping).is_err();
assert!(font_thread_has_closed, "Failed to receive a response from live font cache");
panic!("Font cache thread has already exited.");
}
match reply.unwrap() {
Reply::GetFontTemplateReply(data) => {
data
}
@ -502,10 +512,14 @@ impl FontSource for FontCacheThread {
self.chan.send(Command::GetLastResortFontTemplate(desc, response_chan))
.expect("failed to send message to font cache thread");
let reply = response_port.recv()
.expect("failed to receive response to font request");
let reply = response_port.recv();
if reply.is_err() {
let font_thread_has_closed = self.chan.send(Command::Ping).is_err();
assert!(font_thread_has_closed, "Failed to receive a response from live font cache");
panic!("Font cache thread has already exited.");
}
match reply {
match reply.unwrap() {
Reply::GetFontTemplateReply(data) => {
data.unwrap()
}

View File

@ -47,6 +47,8 @@ use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::ffi::CString;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use task::TaskCanceller;
use task_source::file_reading::FileReadingTaskSource;
use task_source::networking::NetworkingTaskSource;
@ -55,6 +57,17 @@ use time::{Timespec, get_time};
use timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle};
use timers::{OneshotTimers, TimerCallback};
#[derive(JSTraceable)]
pub struct AutoCloseWorker(
Arc<AtomicBool>,
);
impl Drop for AutoCloseWorker {
fn drop(&mut self) {
self.0.store(true, Ordering::SeqCst);
}
}
#[dom_struct]
pub struct GlobalScope {
eventtarget: EventTarget,
@ -110,6 +123,10 @@ pub struct GlobalScope {
/// <https://html.spec.whatwg.org/multipage/#microtask-queue>
#[ignore_malloc_size_of = "Rc<T> is hard"]
microtask_queue: Rc<MicrotaskQueue>,
/// Vector storing closing references of all workers
#[ignore_malloc_size_of = "Arc"]
list_auto_close_worker: DomRefCell<Vec<AutoCloseWorker>>,
}
impl GlobalScope {
@ -142,9 +159,14 @@ impl GlobalScope {
timers: OneshotTimers::new(timer_event_chan, scheduler_chan),
origin,
microtask_queue,
list_auto_close_worker: Default::default(),
}
}
pub fn track_worker(&self, closing_worker: Arc<AtomicBool>) {
self.list_auto_close_worker.borrow_mut().push(AutoCloseWorker(closing_worker));
}
/// Returns the global scope of the realm that the given DOM object's reflector
/// was created in.
#[allow(unsafe_code)]

View File

@ -48,7 +48,6 @@ use script_traits::ScriptToConstellationChan;
use servo_atoms::Atom;
use std::borrow::ToOwned;
use std::cell::Cell;
use std::mem;
use std::ops::Range;
use style::attr::AttrValue;
use style::element_state::ElementState;
@ -990,42 +989,34 @@ impl HTMLInputElement {
}
// https://html.spec.whatwg.org/multipage/#value-sanitization-algorithm
fn sanitize_value(&self) {
fn sanitize_value(&self, value: &mut DOMString) {
match self.input_type() {
InputType::Text | InputType::Search | InputType::Tel | InputType::Password => {
self.textinput.borrow_mut().single_line_content_mut().strip_newlines();
value.strip_newlines();
}
InputType::Url => {
let mut textinput = self.textinput.borrow_mut();
let content = textinput.single_line_content_mut();
content.strip_newlines();
content.strip_leading_and_trailing_ascii_whitespace();
value.strip_newlines();
value.strip_leading_and_trailing_ascii_whitespace();
}
InputType::Date => {
let mut textinput = self.textinput.borrow_mut();
if !textinput.single_line_content().is_valid_date_string() {
textinput.single_line_content_mut().clear();
if !value.is_valid_date_string() {
value.clear();
}
}
InputType::Month => {
let mut textinput = self.textinput.borrow_mut();
if !textinput.single_line_content().is_valid_month_string() {
textinput.single_line_content_mut().clear();
if !value.is_valid_month_string() {
value.clear();
}
}
InputType::Week => {
let mut textinput = self.textinput.borrow_mut();
if !textinput.single_line_content().is_valid_week_string() {
textinput.single_line_content_mut().clear();
if !value.is_valid_week_string() {
value.clear();
}
}
InputType::Color => {
let mut textinput = self.textinput.borrow_mut();
let is_valid = {
let content = textinput.single_line_content();
let mut chars = content.chars();
if content.len() == 7 && chars.next() == Some('#') {
let mut chars = value.chars();
if value.len() == 7 && chars.next() == Some('#') {
chars.all(|c| c.is_digit(16))
} else {
false
@ -1033,38 +1024,29 @@ impl HTMLInputElement {
};
if is_valid {
let content = textinput.single_line_content_mut();
content.make_ascii_lowercase();
value.make_ascii_lowercase();
} else {
textinput.set_content("#000000".into(), true);
*value = "#000000".into();
}
}
InputType::Time => {
let mut textinput = self.textinput.borrow_mut();
if !textinput.single_line_content().is_valid_time_string() {
textinput.single_line_content_mut().clear();
if !value.is_valid_time_string() {
value.clear();
}
}
InputType::DatetimeLocal => {
let mut textinput = self.textinput.borrow_mut();
if textinput.single_line_content_mut()
.convert_valid_normalized_local_date_and_time_string().is_err() {
textinput.single_line_content_mut().clear();
if value.convert_valid_normalized_local_date_and_time_string().is_err() {
value.clear();
}
}
InputType::Number => {
let mut textinput = self.textinput.borrow_mut();
if !textinput.single_line_content().is_valid_floating_point_number_string() {
textinput.single_line_content_mut().clear();
if !value.is_valid_floating_point_number_string() {
value.clear();
}
}
// https://html.spec.whatwg.org/multipage/#range-state-(type=range):value-sanitization-algorithm
InputType::Range => {
self.textinput
.borrow_mut()
.single_line_content_mut()
.set_best_representation_of_the_floating_point_number();
value.set_best_representation_of_the_floating_point_number();
}
_ => ()
}
@ -1075,20 +1057,23 @@ impl HTMLInputElement {
TextControlSelection::new(&self, &self.textinput)
}
fn update_text_contents(&self, value: DOMString, update_text_cursor: bool) -> ErrorResult {
fn update_text_contents(&self, mut value: DOMString, update_text_cursor: bool) -> ErrorResult {
match self.value_mode() {
ValueMode::Value => {
// Steps 1-2.
let old_value = mem::replace(self.textinput.borrow_mut().single_line_content_mut(), value);
// Step 3.
self.value_dirty.set(true);
// Step 4.
if update_text_cursor {
self.sanitize_value();
}
// Step 5.
if *self.textinput.borrow().single_line_content() != old_value {
self.textinput.borrow_mut().clear_selection_to_limit(Direction::Forward, update_text_cursor);
self.sanitize_value(&mut value);
let mut textinput = self.textinput.borrow_mut();
if *textinput.single_line_content() != value {
// Steps 1-2
textinput.set_content(value, update_text_cursor);
// Step 5.
textinput.clear_selection_to_limit(Direction::Forward, update_text_cursor);
}
}
ValueMode::Default |
@ -1215,11 +1200,14 @@ impl VirtualMethods for HTMLInputElement {
}
// Step 6
self.sanitize_value();
let mut textinput = self.textinput.borrow_mut();
let mut value = textinput.single_line_content().clone();
self.sanitize_value(&mut value);
textinput.set_content(value, true);
// Steps 7-9
if !previously_selectable && self.selection_api_applies() {
self.textinput.borrow_mut().clear_selection_to_limit(Direction::Backward, true);
textinput.clear_selection_to_limit(Direction::Backward, true);
}
},
AttributeMutation::Removed => {
@ -1240,9 +1228,10 @@ impl VirtualMethods for HTMLInputElement {
},
&local_name!("value") if !self.value_dirty.get() => {
let value = mutation.new_value(attr).map(|value| (**value).to_owned());
self.textinput.borrow_mut().set_content(
value.map_or(DOMString::new(), DOMString::from), true);
self.sanitize_value();
let mut value = value.map_or(DOMString::new(), DOMString::from);
self.sanitize_value(&mut value);
self.textinput.borrow_mut().set_content(value, true);
self.update_placeholder_shown_state();
},
&local_name!("name") if self.input_type() == InputType::Radio => {
@ -1252,10 +1241,12 @@ impl VirtualMethods for HTMLInputElement {
&local_name!("maxlength") => {
match *attr.value() {
AttrValue::Int(_, value) => {
let mut textinput = self.textinput.borrow_mut();
if value < 0 {
self.textinput.borrow_mut().max_length = None
textinput.set_max_length(None);
} else {
self.textinput.borrow_mut().max_length = Some(value as usize)
textinput.set_max_length(Some(value as usize))
}
},
_ => panic!("Expected an AttrValue::Int"),
@ -1264,10 +1255,12 @@ impl VirtualMethods for HTMLInputElement {
&local_name!("minlength") => {
match *attr.value() {
AttrValue::Int(_, value) => {
let mut textinput = self.textinput.borrow_mut();
if value < 0 {
self.textinput.borrow_mut().min_length = None
textinput.set_min_length(None);
} else {
self.textinput.borrow_mut().min_length = Some(value as usize)
textinput.set_min_length(Some(value as usize))
}
},
_ => panic!("Expected an AttrValue::Int"),

View File

@ -323,7 +323,6 @@ impl HTMLTextAreaElement {
// Step 1
let old_value = textinput.get_content();
let old_selection = textinput.selection_origin;
// Step 2
textinput.set_content(value, update_text_cursor);
@ -334,8 +333,6 @@ impl HTMLTextAreaElement {
if old_value != textinput.get_content() {
// Step 4
textinput.clear_selection_to_limit(Direction::Forward, update_text_cursor);
} else {
textinput.selection_origin = old_selection;
}
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);

View File

@ -257,7 +257,7 @@ impl<'a, E: TextControlElement> TextControlSelection<'a, E> {
}
fn direction(&self) -> SelectionDirection {
self.textinput.borrow().selection_direction
self.textinput.borrow().selection_direction()
}
// https://html.spec.whatwg.org/multipage/#set-the-selection-range

View File

@ -79,6 +79,7 @@ impl Worker {
let (sender, receiver) = channel();
let closing = Arc::new(AtomicBool::new(false));
let worker = Worker::new(global, sender.clone(), closing.clone());
global.track_worker(closing.clone());
let worker_ref = Trusted::new(&*worker);
let worker_load_origin = WorkerScriptLoadOrigin {

View File

@ -56,6 +56,18 @@ pub struct TextPoint {
pub index: usize,
}
impl TextPoint {
/// Returns a TextPoint constrained to be a valid location within lines
fn constrain_to(&self, lines: &[DOMString]) -> TextPoint {
let line = min(self.line, lines.len() - 1);
TextPoint {
line,
index: min(self.index, lines[line].len()),
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub struct SelectionState {
start: TextPoint,
@ -68,21 +80,26 @@ pub struct SelectionState {
pub struct TextInput<T: ClipboardProvider> {
/// Current text input content, split across lines without trailing '\n'
lines: Vec<DOMString>,
/// Current cursor input point
pub edit_point: TextPoint,
edit_point: TextPoint,
/// The current selection goes from the selection_origin until the edit_point. Note that the
/// selection_origin may be after the edit_point, in the case of a backward selection.
pub selection_origin: Option<TextPoint>,
selection_origin: Option<TextPoint>,
selection_direction: SelectionDirection,
/// Is this a multiline input?
multiline: bool,
#[ignore_malloc_size_of = "Can't easily measure this generic type"]
clipboard_provider: T,
/// The maximum number of UTF-16 code units this text input is allowed to hold.
///
/// <https://html.spec.whatwg.org/multipage/#attr-fe-maxlength>
pub max_length: Option<usize>,
pub min_length: Option<usize>,
pub selection_direction: SelectionDirection,
max_length: Option<usize>,
min_length: Option<usize>,
}
/// Resulting action to be taken by the owner of a text input that is handling an event.
@ -175,6 +192,32 @@ impl<T: ClipboardProvider> TextInput<T> {
i
}
pub fn edit_point(&self) -> TextPoint {
self.edit_point
}
pub fn selection_origin(&self) -> Option<TextPoint> {
self.selection_origin
}
/// The selection origin, or the edit point if there is no selection. Note that the selection
/// origin may be after the edit point, in the case of a backward selection.
pub fn selection_origin_or_edit_point(&self) -> TextPoint {
self.selection_origin.unwrap_or(self.edit_point)
}
pub fn selection_direction(&self) -> SelectionDirection {
self.selection_direction
}
pub fn set_max_length(&mut self, length: Option<usize>) {
self.max_length = length;
}
pub fn set_min_length(&mut self, length: Option<usize>) {
self.min_length = length;
}
/// Remove a character at the current editing point
pub fn delete_char(&mut self, dir: Direction) {
if self.selection_origin.is_none() || self.selection_origin == Some(self.edit_point) {
@ -196,12 +239,6 @@ impl<T: ClipboardProvider> TextInput<T> {
self.replace_selection(DOMString::from(s.into()));
}
/// The selection origin, or the edit point if there is no selection. Note that the selection
/// origin may be after the edit point, in the case of a backward selection.
pub fn selection_origin_or_edit_point(&self) -> TextPoint {
self.selection_origin.unwrap_or(self.edit_point)
}
/// The start of the selection (or the edit point, if there is no selection). Always less than
/// or equal to selection_end(), regardless of the selection direction.
pub fn selection_start(&self) -> TextPoint {
@ -832,12 +869,6 @@ impl<T: ClipboardProvider> TextInput<T> {
&self.lines[0]
}
/// Get a mutable reference to the contents of a single-line text input. Panics if self is a multiline input.
pub fn single_line_content_mut(&mut self) -> &mut DOMString {
assert!(!self.multiline);
&mut self.lines[0]
}
/// Set the current contents of the text input. If this is control supports multiple lines,
/// any \n encountered will be stripped and force a new logical line.
pub fn set_content(&mut self, content: DOMString, update_text_cursor: bool) {
@ -850,11 +881,15 @@ impl<T: ClipboardProvider> TextInput<T> {
} else {
vec!(content)
};
if update_text_cursor {
self.edit_point.line = min(self.edit_point.line, self.lines.len() - 1);
self.edit_point.index = min(self.edit_point.index, self.current_line_length());
self.edit_point = self.edit_point.constrain_to(&self.lines);
}
self.selection_origin = None;
if let Some(origin) = self.selection_origin {
self.selection_origin = Some(origin.constrain_to(&self.lines));
}
self.assert_ok_selection();
}

View File

@ -0,0 +1,3 @@
<script>
var w = new Worker('work.js');
</script>

View File

@ -0,0 +1,8 @@
<body>
<script>
var iframe = document.createElement('iframe');
iframe.src = "child.html";
iframe.onload = function() { iframe.remove(); }
document.body.appendChild(iframe);
</script>
</body>

4
servo/tests/html/work.js Normal file
View File

@ -0,0 +1,4 @@
console.log("reached inside work.js file");
setInterval(function() {
console.log('hi');
}, 500);

View File

@ -42,7 +42,7 @@ fn test_textinput_when_inserting_multiple_lines_over_a_selection_respects_max_le
SelectionDirection::None,
);
textinput.edit_point = TextPoint { line: 0, index: 1 };
textinput.adjust_horizontal(1, Selection::NotSelected);
textinput.adjust_horizontal(3, Selection::Selected);
textinput.adjust_vertical(1, Selection::Selected);
@ -67,8 +67,7 @@ fn test_textinput_when_inserting_multiple_lines_still_respects_max_length() {
SelectionDirection::None
);
textinput.edit_point = TextPoint { line: 1, index: 0 };
textinput.adjust_vertical(1, Selection::NotSelected);
textinput.insert_string("cruel\nterrible".to_string());
assert_eq!(textinput.get_content(), "hello\ncruel\nworld");
@ -117,7 +116,7 @@ fn test_single_line_textinput_with_max_length_doesnt_allow_appending_characters_
SelectionDirection::None,
);
textinput.edit_point = TextPoint { line: 0, index: 1 };
textinput.adjust_horizontal(1, Selection::NotSelected);
textinput.adjust_horizontal(3, Selection::Selected);
// Selection is now "abcde"
@ -220,9 +219,7 @@ fn test_textinput_delete_char() {
assert_eq!(textinput.get_content(), "ab");
let mut textinput = text_input(Lines::Single, "abcdefg");
textinput.adjust_horizontal(2, Selection::NotSelected);
// Set an empty selection range.
textinput.selection_origin = Some(textinput.edit_point);
textinput.set_selection_range(2, 2, SelectionDirection::None);
textinput.delete_char(Direction::Backward);
assert_eq!(textinput.get_content(), "acdefg");
}
@ -300,16 +297,16 @@ fn test_textinput_adjust_vertical() {
let mut textinput = text_input(Lines::Multiple, "abc\nde\nf");
textinput.adjust_horizontal(3, Selection::NotSelected);
textinput.adjust_vertical(1, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point.index, 2);
assert_eq!(textinput.edit_point().line, 1);
assert_eq!(textinput.edit_point().index, 2);
textinput.adjust_vertical(-1, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 2);
assert_eq!(textinput.edit_point().line, 0);
assert_eq!(textinput.edit_point().index, 2);
textinput.adjust_vertical(2, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 2);
assert_eq!(textinput.edit_point.index, 1);
assert_eq!(textinput.edit_point().line, 2);
assert_eq!(textinput.edit_point().index, 1);
}
#[test]
@ -317,32 +314,32 @@ fn test_textinput_adjust_vertical_multibyte() {
let mut textinput = text_input(Lines::Multiple, "áé\nae");
textinput.adjust_horizontal_by_one(Direction::Forward, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 2);
assert_eq!(textinput.edit_point().line, 0);
assert_eq!(textinput.edit_point().index, 2);
textinput.adjust_vertical(1, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point.index, 1);
assert_eq!(textinput.edit_point().line, 1);
assert_eq!(textinput.edit_point().index, 1);
}
#[test]
fn test_textinput_adjust_horizontal() {
let mut textinput = text_input(Lines::Multiple, "abc\nde\nf");
textinput.adjust_horizontal(4, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point().line, 1);
assert_eq!(textinput.edit_point().index, 0);
textinput.adjust_horizontal(1, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point.index, 1);
assert_eq!(textinput.edit_point().line, 1);
assert_eq!(textinput.edit_point().index, 1);
textinput.adjust_horizontal(2, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 2);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point().line, 2);
assert_eq!(textinput.edit_point().index, 0);
textinput.adjust_horizontal(-1, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point.index, 2);
assert_eq!(textinput.edit_point().line, 1);
assert_eq!(textinput.edit_point().index, 2);
}
#[test]
@ -351,45 +348,45 @@ fn test_textinput_adjust_horizontal_by_word() {
let mut textinput = text_input(Lines::Single, "abc def");
textinput.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected);
textinput.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 7);
assert_eq!(textinput.edit_point().line, 0);
assert_eq!(textinput.edit_point().index, 7);
textinput.adjust_horizontal_by_word(Direction::Backward, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 4);
assert_eq!(textinput.edit_point().line, 0);
assert_eq!(textinput.edit_point().index, 4);
textinput.adjust_horizontal_by_word(Direction::Backward, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point().line, 0);
assert_eq!(textinput.edit_point().index, 0);
// Test new line case of movement word by word based on UAX#29 rules
let mut textinput_2 = text_input(Lines::Multiple, "abc\ndef");
textinput_2.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected);
textinput_2.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected);
assert_eq!(textinput_2.edit_point.line, 1);
assert_eq!(textinput_2.edit_point.index, 3);
assert_eq!(textinput_2.edit_point().line, 1);
assert_eq!(textinput_2.edit_point().index, 3);
textinput_2.adjust_horizontal_by_word(Direction::Backward, Selection::NotSelected);
assert_eq!(textinput_2.edit_point.line, 1);
assert_eq!(textinput_2.edit_point.index, 0);
assert_eq!(textinput_2.edit_point().line, 1);
assert_eq!(textinput_2.edit_point().index, 0);
textinput_2.adjust_horizontal_by_word(Direction::Backward, Selection::NotSelected);
assert_eq!(textinput_2.edit_point.line, 0);
assert_eq!(textinput_2.edit_point.index, 0);
assert_eq!(textinput_2.edit_point().line, 0);
assert_eq!(textinput_2.edit_point().index, 0);
// Test non-standard sized characters case of movement word by word based on UAX#29 rules
let mut textinput_3 = text_input(Lines::Single, "áéc d🌠bc");
textinput_3.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected);
assert_eq!(textinput_3.edit_point.line, 0);
assert_eq!(textinput_3.edit_point.index, 5);
assert_eq!(textinput_3.edit_point().line, 0);
assert_eq!(textinput_3.edit_point().index, 5);
textinput_3.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected);
assert_eq!(textinput_3.edit_point.line, 0);
assert_eq!(textinput_3.edit_point.index, 7);
assert_eq!(textinput_3.edit_point().line, 0);
assert_eq!(textinput_3.edit_point().index, 7);
textinput_3.adjust_horizontal_by_word(Direction::Forward, Selection::NotSelected);
assert_eq!(textinput_3.edit_point.line, 0);
assert_eq!(textinput_3.edit_point.index, 13);
assert_eq!(textinput_3.edit_point().line, 0);
assert_eq!(textinput_3.edit_point().index, 13);
textinput_3.adjust_horizontal_by_word(Direction::Backward, Selection::NotSelected);
assert_eq!(textinput_3.edit_point.line, 0);
assert_eq!(textinput_3.edit_point.index, 11);
assert_eq!(textinput_3.edit_point().line, 0);
assert_eq!(textinput_3.edit_point().index, 11);
textinput_3.adjust_horizontal_by_word(Direction::Backward, Selection::NotSelected);
assert_eq!(textinput_3.edit_point.line, 0);
assert_eq!(textinput_3.edit_point.index, 6);
assert_eq!(textinput_3.edit_point().line, 0);
assert_eq!(textinput_3.edit_point().index, 6);
}
#[test]
@ -397,29 +394,29 @@ fn test_textinput_adjust_horizontal_to_line_end() {
// Test standard case of movement to end based on UAX#29 rules
let mut textinput = text_input(Lines::Single, "abc def");
textinput.adjust_horizontal_to_line_end(Direction::Forward, Selection::NotSelected);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 7);
assert_eq!(textinput.edit_point().line, 0);
assert_eq!(textinput.edit_point().index, 7);
// Test new line case of movement to end based on UAX#29 rules
let mut textinput_2 = text_input(Lines::Multiple, "abc\ndef");
textinput_2.adjust_horizontal_to_line_end(Direction::Forward, Selection::NotSelected);
assert_eq!(textinput_2.edit_point.line, 0);
assert_eq!(textinput_2.edit_point.index, 3);
assert_eq!(textinput_2.edit_point().line, 0);
assert_eq!(textinput_2.edit_point().index, 3);
textinput_2.adjust_horizontal_to_line_end(Direction::Forward, Selection::NotSelected);
assert_eq!(textinput_2.edit_point.line, 0);
assert_eq!(textinput_2.edit_point.index, 3);
assert_eq!(textinput_2.edit_point().line, 0);
assert_eq!(textinput_2.edit_point().index, 3);
textinput_2.adjust_horizontal_to_line_end(Direction::Backward, Selection::NotSelected);
assert_eq!(textinput_2.edit_point.line, 0);
assert_eq!(textinput_2.edit_point.index, 0);
assert_eq!(textinput_2.edit_point().line, 0);
assert_eq!(textinput_2.edit_point().index, 0);
// Test non-standard sized characters case of movement to end based on UAX#29 rules
let mut textinput_3 = text_input(Lines::Single, "áéc d🌠bc");
textinput_3.adjust_horizontal_to_line_end(Direction::Forward, Selection::NotSelected);
assert_eq!(textinput_3.edit_point.line, 0);
assert_eq!(textinput_3.edit_point.index, 13);
assert_eq!(textinput_3.edit_point().line, 0);
assert_eq!(textinput_3.edit_point().index, 13);
textinput_3.adjust_horizontal_to_line_end(Direction::Backward, Selection::NotSelected);
assert_eq!(textinput_3.edit_point.line, 0);
assert_eq!(textinput_3.edit_point.index, 0);
assert_eq!(textinput_3.edit_point().line, 0);
assert_eq!(textinput_3.edit_point().index, 0);
}
#[test]
@ -429,29 +426,29 @@ fn test_navigation_keyboard_shortcuts() {
// Test that CMD + Right moves to the end of the current line.
textinput.handle_keydown_aux(None, Key::Right, KeyModifiers::SUPER);
assert_eq!(textinput.edit_point.index, 11);
assert_eq!(textinput.edit_point().index, 11);
// Test that CMD + Right moves to the beginning of the current line.
textinput.handle_keydown_aux(None, Key::Left, KeyModifiers::SUPER);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point().index, 0);
// Test that CTRL + ALT + E moves to the end of the current line also.
textinput.handle_keydown_aux(None, Key::E, KeyModifiers::CONTROL | KeyModifiers::ALT);
assert_eq!(textinput.edit_point.index, 11);
assert_eq!(textinput.edit_point().index, 11);
// Test that CTRL + ALT + A moves to the beginning of the current line also.
textinput.handle_keydown_aux(None, Key::A, KeyModifiers::CONTROL | KeyModifiers::ALT);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point().index, 0);
// Test that ALT + Right moves to the end of the word.
textinput.handle_keydown_aux(None, Key::Right, KeyModifiers::ALT);
assert_eq!(textinput.edit_point.index, 5);
assert_eq!(textinput.edit_point().index, 5);
// Test that CTRL + ALT + F moves to the end of the word also.
textinput.handle_keydown_aux(None, Key::F, KeyModifiers::CONTROL | KeyModifiers::ALT);
assert_eq!(textinput.edit_point.index, 11);
assert_eq!(textinput.edit_point().index, 11);
// Test that ALT + Left moves to the end of the word.
textinput.handle_keydown_aux(None, Key::Left, KeyModifiers::ALT);
assert_eq!(textinput.edit_point.index, 6);
assert_eq!(textinput.edit_point().index, 6);
// Test that CTRL + ALT + B moves to the end of the word also.
textinput.handle_keydown_aux(None, Key::B, KeyModifiers::CONTROL | KeyModifiers::ALT);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point().index, 0);
}
#[test]
@ -470,12 +467,12 @@ fn test_textinput_handle_return() {
#[test]
fn test_textinput_select_all() {
let mut textinput = text_input(Lines::Multiple, "abc\nde\nf");
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point().line, 0);
assert_eq!(textinput.edit_point().index, 0);
textinput.select_all();
assert_eq!(textinput.edit_point.line, 2);
assert_eq!(textinput.edit_point.index, 1);
assert_eq!(textinput.edit_point().line, 2);
assert_eq!(textinput.edit_point().index, 1);
}
#[test]
@ -495,15 +492,15 @@ fn test_textinput_set_content() {
textinput.set_content(DOMString::from("abc\nf"), true);
assert_eq!(textinput.get_content(), "abc\nf");
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point().line, 0);
assert_eq!(textinput.edit_point().index, 0);
textinput.adjust_horizontal(3, Selection::Selected);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 3);
assert_eq!(textinput.edit_point().line, 0);
assert_eq!(textinput.edit_point().index, 3);
textinput.set_content(DOMString::from("de"), true);
assert_eq!(textinput.get_content(), "de");
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 2);
assert_eq!(textinput.edit_point().line, 0);
assert_eq!(textinput.edit_point().index, 2);
}
#[test]
@ -520,7 +517,7 @@ fn test_clipboard_paste() {
None,
SelectionDirection::None);
assert_eq!(textinput.get_content(), "defg");
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point().index, 0);
textinput.handle_keydown_aux(Some('v'), Key::V, MODIFIERS);
assert_eq!(textinput.get_content(), "abcdefg");
}
@ -532,23 +529,23 @@ fn test_textinput_cursor_position_correct_after_clearing_selection() {
// Single line - Forward
textinput.adjust_horizontal(3, Selection::Selected);
textinput.adjust_horizontal(1, Selection::NotSelected);
assert_eq!(textinput.edit_point.index, 3);
assert_eq!(textinput.edit_point().index, 3);
textinput.adjust_horizontal(-3, Selection::NotSelected);
textinput.adjust_horizontal(3, Selection::Selected);
textinput.adjust_horizontal_by_one(Direction::Forward, Selection::NotSelected);
assert_eq!(textinput.edit_point.index, 3);
assert_eq!(textinput.edit_point().index, 3);
// Single line - Backward
textinput.adjust_horizontal(-3, Selection::NotSelected);
textinput.adjust_horizontal(3, Selection::Selected);
textinput.adjust_horizontal(-1, Selection::NotSelected);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point().index, 0);
textinput.adjust_horizontal(-3, Selection::NotSelected);
textinput.adjust_horizontal(3, Selection::Selected);
textinput.adjust_horizontal_by_one(Direction::Backward, Selection::NotSelected);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point().index, 0);
let mut textinput = text_input(Lines::Multiple, "abc\nde\nf");
@ -556,27 +553,27 @@ fn test_textinput_cursor_position_correct_after_clearing_selection() {
// Multiline - Forward
textinput.adjust_horizontal(4, Selection::Selected);
textinput.adjust_horizontal(1, Selection::NotSelected);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point().index, 0);
assert_eq!(textinput.edit_point().line, 1);
textinput.adjust_horizontal(-4, Selection::NotSelected);
textinput.adjust_horizontal(4, Selection::Selected);
textinput.adjust_horizontal_by_one(Direction::Forward, Selection::NotSelected);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point().index, 0);
assert_eq!(textinput.edit_point().line, 1);
// Multiline - Backward
textinput.adjust_horizontal(-4, Selection::NotSelected);
textinput.adjust_horizontal(4, Selection::Selected);
textinput.adjust_horizontal(-1, Selection::NotSelected);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point().index, 0);
assert_eq!(textinput.edit_point().line, 0);
textinput.adjust_horizontal(-4, Selection::NotSelected);
textinput.adjust_horizontal(4, Selection::Selected);
textinput.adjust_horizontal_by_one(Direction::Backward, Selection::NotSelected);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point().index, 0);
assert_eq!(textinput.edit_point().line, 0);
}
@ -584,53 +581,53 @@ fn test_textinput_cursor_position_correct_after_clearing_selection() {
fn test_textinput_set_selection_with_direction() {
let mut textinput = text_input(Lines::Single, "abcdef");
textinput.set_selection_range(2, 6, SelectionDirection::Forward);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 6);
assert_eq!(textinput.selection_direction, SelectionDirection::Forward);
assert_eq!(textinput.edit_point().line, 0);
assert_eq!(textinput.edit_point().index, 6);
assert_eq!(textinput.selection_direction(), SelectionDirection::Forward);
assert!(textinput.selection_origin.is_some());
assert_eq!(textinput.selection_origin.unwrap().line, 0);
assert_eq!(textinput.selection_origin.unwrap().index, 2);
assert!(textinput.selection_origin().is_some());
assert_eq!(textinput.selection_origin().unwrap().line, 0);
assert_eq!(textinput.selection_origin().unwrap().index, 2);
textinput.set_selection_range(2, 6, SelectionDirection::Backward);
assert_eq!(textinput.edit_point.line, 0);
assert_eq!(textinput.edit_point.index, 2);
assert_eq!(textinput.selection_direction, SelectionDirection::Backward);
assert_eq!(textinput.edit_point().line, 0);
assert_eq!(textinput.edit_point().index, 2);
assert_eq!(textinput.selection_direction(), SelectionDirection::Backward);
assert!(textinput.selection_origin.is_some());
assert_eq!(textinput.selection_origin.unwrap().line, 0);
assert_eq!(textinput.selection_origin.unwrap().index, 6);
assert!(textinput.selection_origin().is_some());
assert_eq!(textinput.selection_origin().unwrap().line, 0);
assert_eq!(textinput.selection_origin().unwrap().index, 6);
textinput = text_input(Lines::Multiple, "\n\n");
textinput.set_selection_range(0, 1, SelectionDirection::Forward);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.selection_direction, SelectionDirection::Forward);
assert_eq!(textinput.edit_point().line, 1);
assert_eq!(textinput.edit_point().index, 0);
assert_eq!(textinput.selection_direction(), SelectionDirection::Forward);
assert!(textinput.selection_origin.is_some());
assert_eq!(textinput.selection_origin.unwrap().line, 0);
assert_eq!(textinput.selection_origin.unwrap().index, 0);
assert!(textinput.selection_origin().is_some());
assert_eq!(textinput.selection_origin().unwrap().line, 0);
assert_eq!(textinput.selection_origin().unwrap().index, 0);
textinput = text_input(Lines::Multiple, "\n");
textinput.set_selection_range(0, 1, SelectionDirection::Forward);
assert_eq!(textinput.edit_point.line, 1);
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.selection_direction, SelectionDirection::Forward);
assert_eq!(textinput.edit_point().line, 1);
assert_eq!(textinput.edit_point().index, 0);
assert_eq!(textinput.selection_direction(), SelectionDirection::Forward);
assert!(textinput.selection_origin.is_some());
assert_eq!(textinput.selection_origin.unwrap().line, 0);
assert_eq!(textinput.selection_origin.unwrap().index, 0);
assert!(textinput.selection_origin().is_some());
assert_eq!(textinput.selection_origin().unwrap().line, 0);
assert_eq!(textinput.selection_origin().unwrap().index, 0);
}
#[test]
fn test_textinput_unicode_handling() {
let mut textinput = text_input(Lines::Single, "éèùµ$£");
assert_eq!(textinput.edit_point.index, 0);
assert_eq!(textinput.edit_point().index, 0);
textinput.set_edit_point_index(1);
assert_eq!(textinput.edit_point.index, 2);
assert_eq!(textinput.edit_point().index, 2);
textinput.set_edit_point_index(4);
assert_eq!(textinput.edit_point.index, 8);
assert_eq!(textinput.edit_point().index, 8);
}
#[test]

View File

@ -514,52 +514,6 @@ android-api-16-old-id-nightly/opt:
- linux64-sccache
- proguard-jar
android-api-16-gradle/opt:
description: "Android 4.0 api-16+ (non-Gradle) Opt"
index:
product: mobile
job-name: android-api-16-gradle-opt
treeherder:
platform: android-api-16-gradle/opt
symbol: Bng
tier: 1
worker-type: aws-provisioner-v1/gecko-{level}-b-android
worker:
docker-image: {in-tree: android-build}
max-run-time: 7200
env:
TOOLTOOL_MANIFEST: "mobile/android/config/tooltool-manifests/android/releng.manifest"
artifacts:
- name: public/android/R
path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/app/R
type: directory
- name: public/android/maven
path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/maven/
type: directory
- name: public/build/geckoview_example.apk
path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview_example/outputs/apk/officialWithGeckoBinariesNoMinApi/debug/geckoview_example-official-withGeckoBinaries-noMinApi-debug.apk
type: file
- name: public/build
path: /builds/worker/artifacts/
type: directory
run:
using: mozharness
actions: [get-secrets build multi-l10n update]
config:
- builds/releng_base_android_64_builds.py
- disable_signing.py
script: "mozharness/scripts/fx_desktop_build.py"
secrets: true
custom-build-variant-cfg: api-16-gradle
tooltool-downloads: internal
toolchains:
- android-ndk-linux
- android-sdk-linux
- linux64-clang
- linux64-rust-android
- linux64-sccache
- proguard-jar
android-aarch64/opt:
description: "Android 5.0 AArch64 Opt"
index:

View File

@ -29,7 +29,6 @@ robocop:
by-test-platform:
# android-4.3-arm7-api-16/debug -- not run
android-4.3-arm7-api-16/opt: 4
android-4.3-arm7-api-16-gradle/opt: 4
loopback-video: true
e10s: false
mozharness:

View File

@ -474,6 +474,7 @@ talos-tp6:
run-on-projects:
by-test-platform:
.*-qr/.*: ['mozilla-central', 'try']
windows.*: []
default: ['mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland', 'try']
max-run-time: 1200
mozharness:
@ -504,6 +505,7 @@ talos-tp6-stylo-threads:
by-test-platform:
.*-qr/.*: ['mozilla-central', 'try']
macosx.*: ['mozilla-beta', 'autoland', 'try']
windows.*: []
default: ['mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland', 'try']
mozharness:
extra-options:

View File

@ -294,10 +294,3 @@ android-4.2-x86/opt:
build-platform: android-x86/opt
test-sets:
- android-x86-tests
# android-4.3-arm7-api-16-gradle/opt actually uses the non-gradle build
# which is tier 2, and requires only a smoketest, like robocop
android-4.3-arm7-api-16-gradle/opt:
build-platform: android-api-16-gradle/opt
test-sets:
- android-opt-tests

View File

@ -35,6 +35,7 @@ def enable_coalescing(config, jobs):
'aws-provisioner-v1/gecko-t-win7-32-gpu',
'aws-provisioner-v1/gecko-t-win10-64',
'aws-provisioner-v1/gecko-t-win10-64-gpu',
'releng-hardware/gecko-t-win10-64-hw',
]:
job['coalesce'] = {
'job-identifier': sha256(job["label"]).hexdigest()[:20],

View File

@ -52,27 +52,27 @@ WINDOWS_WORKER_TYPES = {
'windows7-32': {
'virtual': 'aws-provisioner-v1/gecko-t-win7-32',
'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win7-32-gpu',
'hardware': 'releng-hardware/gecko-t-win7-32-hw',
'hardware': 'releng-hardware/gecko-t-win10-64-hw',
},
'windows7-32-pgo': {
'virtual': 'aws-provisioner-v1/gecko-t-win7-32',
'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win7-32-gpu',
'hardware': 'releng-hardware/gecko-t-win7-32-hw',
'hardware': 'releng-hardware/gecko-t-win10-64-hw',
},
'windows7-32-nightly': {
'virtual': 'aws-provisioner-v1/gecko-t-win7-32',
'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win7-32-gpu',
'hardware': 'releng-hardware/gecko-t-win7-32-hw',
'hardware': 'releng-hardware/gecko-t-win10-64-hw',
},
'windows7-32-devedition': {
'virtual': 'aws-provisioner-v1/gecko-t-win7-32',
'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win7-32-gpu',
'hardware': 'releng-hardware/gecko-t-win7-32-hw',
'hardware': 'releng-hardware/gecko-t-win10-64-hw',
},
'windows7-32-stylo-disabled': {
'virtual': 'aws-provisioner-v1/gecko-t-win7-32',
'virtual-with-gpu': 'aws-provisioner-v1/gecko-t-win7-32-gpu',
'hardware': 'releng-hardware/gecko-t-win7-32-hw',
'hardware': 'releng-hardware/gecko-t-win10-64-hw',
},
'windows10-64': {
'virtual': 'aws-provisioner-v1/gecko-t-win10-64',
@ -916,25 +916,23 @@ def set_worker_type(config, tests):
# during the taskcluster migration, this is a bit tortured, but it
# will get simpler eventually!
test_platform = test['test-platform']
try_options = config.params['try_options'] if config.params['try_options'] else {}
if test.get('worker-type'):
# This test already has its worker type defined, so just use that (yields below)
pass
elif test_platform.startswith('macosx'):
test['worker-type'] = MACOSX_WORKER_TYPES['macosx64']
elif test_platform.startswith('win'):
win_worker_type_platform = WINDOWS_WORKER_TYPES[
test_platform.split('/')[0]
]
if test.get('suite', '') == 'talos' and 'ccov' not in test['build-platform']:
if try_options.get('taskcluster_worker'):
test['worker-type'] = win_worker_type_platform['hardware']
elif test['virtualization'] == 'virtual':
test['worker-type'] = win_worker_type_platform[test['virtualization']]
else:
test['worker-type'] = 'buildbot-bridge/buildbot-bridge'
# figure out what platform the job needs to run on
if test['virtualization'] == 'hardware':
# some jobs like talos and reftest run on real h/w - those are all win10
win_worker_type_platform = WINDOWS_WORKER_TYPES['windows10-64']
else:
test['worker-type'] = win_worker_type_platform[test['virtualization']]
# the other jobs run on a vm which may or may not be a win10 vm
win_worker_type_platform = WINDOWS_WORKER_TYPES[
test_platform.split('/')[0]
]
# now we have the right platform set the worker type accordingly
test['worker-type'] = win_worker_type_platform[test['virtualization']]
elif test_platform.startswith('linux') or test_platform.startswith('android'):
if test.get('suite', '') == 'talos' and test['build-platform'] != 'linux64-ccov/opt':
test['worker-type'] = 'releng-hardware/gecko-t-linux-talos'
@ -946,18 +944,6 @@ def set_worker_type(config, tests):
yield test
@transforms.add
def skip_win10_hardware(config, tests):
"""Windows 10 hardware isn't ready yet, don't even bother scheduling
unless we're on try"""
for test in tests:
if 'releng-hardware/gecko-t-win10-64-hw' not in test['worker-type']:
yield test
if config.params == 'try':
yield test
# Silently drop the test on the floor if its win10 hardware and we're not try
@transforms.add
def make_job_description(config, tests):
"""Convert *test* descriptions to *job* descriptions (input to

View File

@ -1,10 +0,0 @@
config = {
'base_name': 'Android armv7 api-16+ %(branch)s non-Gradle',
'stage_platform': 'android-api-16-gradle',
'build_type': 'api-16-gradle',
'src_mozconfig': 'mobile/android/config/mozconfigs/android-api-16-gradle/nightly',
'multi_locale_config_platform': 'android',
'artifact_flag_build_variant_in_try': 'api-16-gradle-artifact',
# Gradle is temporarily non-Gradle, and non-Gradle implies no geckoview.
'postflight_build_mach_commands': [],
}

View File

@ -1,10 +0,0 @@
config = {
'base_name': 'Android armv7 api-16+ %(branch)s non-Gradle Artifact build',
'stage_platform': 'android-api-16-gradle',
'build_type': 'api-16-gradle-artifact',
'src_mozconfig': 'mobile/android/config/mozconfigs/android-api-16-gradle/nightly-artifact',
'tooltool_manifest_src': 'mobile/android/config/tooltool-manifests/android/releng.manifest',
'multi_locale_config_platform': 'android',
# Gradle is temporarily non-Gradle, and non-Gradle implies no geckoview.
'postflight_build_mach_commands': [],
}

View File

@ -1,7 +1,8 @@
import os
import socket
import sys
PYTHON = 'c:/mozilla-build/python27/python.exe'
PYTHON = sys.executable
PYTHON_DLL = 'c:/mozilla-build/python27/python27.dll'
VENV_PATH = os.path.join(os.getcwd(), 'build/venv')
@ -22,7 +23,8 @@ config = {
'%s/scripts/easy_install-2.7-script.py' % VENV_PATH],
'mozinstall': ['%s/scripts/python' % VENV_PATH,
'%s/scripts/mozinstall-script.py' % VENV_PATH],
'hg': 'c:/mozilla-build/hg/hg',
'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg'),
'tooltool.py': [PYTHON, os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')],
},
"title": socket.gethostname().split('.')[0],
"default_actions": [