зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1269962 - Implement a popup menu for showing a submenu in Downloads Panel Footer., r=Paolo
MozReview-Commit-ID: 7K1W15039W8 --HG-- extra : rebase_source : 01b66909876cb22ea8442d037ebd8b8f3f5748ec
This commit is contained in:
Родитель
ff7bc87130
Коммит
9b70990ee3
|
@ -508,20 +508,32 @@ this.DownloadsCommon = {
|
||||||
// or the file doesn't exist), try using the parent if we have it.
|
// or the file doesn't exist), try using the parent if we have it.
|
||||||
let parent = aFile.parent;
|
let parent = aFile.parent;
|
||||||
if (parent) {
|
if (parent) {
|
||||||
try {
|
this.showDirectory(parent);
|
||||||
// Open the parent directory to show where the file should be.
|
|
||||||
parent.launch();
|
|
||||||
} catch (ex) {
|
|
||||||
// If launch also fails (probably because it's not implemented), let
|
|
||||||
// the OS handler try to open the parent.
|
|
||||||
Cc["@mozilla.org/uriloader/external-protocol-service;1"]
|
|
||||||
.getService(Ci.nsIExternalProtocolService)
|
|
||||||
.loadUrl(NetUtil.newURI(parent));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the specified folder in the system file manager.
|
||||||
|
*
|
||||||
|
* @param aDirectory
|
||||||
|
* a directory to be opened with system file manager.
|
||||||
|
*/
|
||||||
|
showDirectory(aDirectory) {
|
||||||
|
if (!(aDirectory instanceof Ci.nsIFile)) {
|
||||||
|
throw new Error("aDirectory must be a nsIFile object");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
aDirectory.launch();
|
||||||
|
} catch (ex) {
|
||||||
|
// If launch fails (probably because it's not implemented), let
|
||||||
|
// the OS handler try to open the directory.
|
||||||
|
Cc["@mozilla.org/uriloader/external-protocol-service;1"]
|
||||||
|
.getService(Ci.nsIExternalProtocolService)
|
||||||
|
.loadUrl(NetUtil.newURI(aDirectory));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays an alert message box which asks the user if they want to
|
* Displays an alert message box which asks the user if they want to
|
||||||
* unblock the downloaded file or not.
|
* unblock the downloaded file or not.
|
||||||
|
|
|
@ -19,7 +19,7 @@ richlistitem[type="download"].download-state[state="1"]:not([exists]) .downloadS
|
||||||
|
|
||||||
#downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryProgress,
|
#downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryProgress,
|
||||||
#downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryDetails,
|
#downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryDetails,
|
||||||
#downloadsFooter[showingsummary] > #downloadsHistory,
|
#downloadsFooter[showingsummary] > #downloadsFooterButtons,
|
||||||
#downloadsFooter:not([showingsummary]) > #downloadsSummary {
|
#downloadsFooter:not([showingsummary]) > #downloadsSummary {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,6 +367,23 @@ const DownloadsPanel = {
|
||||||
this._state = this.kStateHidden;
|
this._state = this.kStateHidden;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onFooterPopupShowing(aEvent) {
|
||||||
|
let itemClearList = document.getElementById("downloadsDropdownItemClearList");
|
||||||
|
if (DownloadsCommon.getData(window).canRemoveFinished) {
|
||||||
|
itemClearList.removeAttribute("hidden");
|
||||||
|
} else {
|
||||||
|
itemClearList.setAttribute("hidden", "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("downloadsFooterButtonsSplitter").classList
|
||||||
|
.add("downloadsDropmarkerSplitterExtend");
|
||||||
|
},
|
||||||
|
|
||||||
|
onFooterPopupHidden(aEvent) {
|
||||||
|
document.getElementById("downloadsFooterButtonsSplitter").classList
|
||||||
|
.remove("downloadsDropmarkerSplitterExtend");
|
||||||
|
},
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//// Related operations
|
//// Related operations
|
||||||
|
|
||||||
|
@ -382,6 +399,13 @@ const DownloadsPanel = {
|
||||||
BrowserDownloadsUI();
|
BrowserDownloadsUI();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
openDownloadsFolder() {
|
||||||
|
Downloads.getPreferredDownloadsDirectory().then(downloadsPath => {
|
||||||
|
DownloadsCommon.showDirectory(new FileUtils.File(downloadsPath));
|
||||||
|
}).catch(Cu.reportError);
|
||||||
|
this.hidePanel();
|
||||||
|
},
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//// Internal functions
|
//// Internal functions
|
||||||
|
|
||||||
|
@ -1188,6 +1212,9 @@ const DownloadsViewController = {
|
||||||
//// nsIController
|
//// nsIController
|
||||||
|
|
||||||
supportsCommand(aCommand) {
|
supportsCommand(aCommand) {
|
||||||
|
if (aCommand === "downloadsCmd_clearList") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// Firstly, determine if this is a command that we can handle.
|
// Firstly, determine if this is a command that we can handle.
|
||||||
if (!DownloadsViewUI.isCommandName(aCommand)) {
|
if (!DownloadsViewUI.isCommandName(aCommand)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -104,8 +104,8 @@
|
||||||
<menuseparator/>
|
<menuseparator/>
|
||||||
|
|
||||||
<menuitem command="downloadsCmd_clearList"
|
<menuitem command="downloadsCmd_clearList"
|
||||||
label="&cmd.clearList.label;"
|
label="&cmd.clearList2.label;"
|
||||||
accesskey="&cmd.clearList.accesskey;"/>
|
accesskey="&cmd.clearList2.accesskey;"/>
|
||||||
</menupopup>
|
</menupopup>
|
||||||
|
|
||||||
<panelmultiview id="downloadsPanel-multiView"
|
<panelmultiview id="downloadsPanel-multiView"
|
||||||
|
@ -148,11 +148,31 @@
|
||||||
crop="end"/>
|
crop="end"/>
|
||||||
</vbox>
|
</vbox>
|
||||||
</hbox>
|
</hbox>
|
||||||
<button id="downloadsHistory"
|
<hbox id="downloadsFooterButtons">
|
||||||
class="plain downloadsPanelFooterButton"
|
<button id="downloadsHistory"
|
||||||
label="&downloadsHistory.label;"
|
class="plain downloadsPanelFooterButton"
|
||||||
accesskey="&downloadsHistory.accesskey;"
|
label="&downloadsHistory.label;"
|
||||||
oncommand="DownloadsPanel.showDownloadsHistory();"/>
|
accesskey="&downloadsHistory.accesskey;"
|
||||||
|
flex="1"
|
||||||
|
oncommand="DownloadsPanel.showDownloadsHistory();"/>
|
||||||
|
<toolbarseparator id="downloadsFooterButtonsSplitter"
|
||||||
|
class="downloadsDropmarkerSplitter"/>
|
||||||
|
<button id="downloadsFooterDropmarker"
|
||||||
|
class="plain downloadsPanelFooterButton downloadsDropmarker"
|
||||||
|
type="menu">
|
||||||
|
<menupopup id="downloadSubPanel"
|
||||||
|
onpopupshowing="DownloadsPanel.onFooterPopupShowing(event);"
|
||||||
|
onpopuphidden="DownloadsPanel.onFooterPopupHidden(event);"
|
||||||
|
position="after_end">
|
||||||
|
<menuitem id="downloadsDropdownItemClearList"
|
||||||
|
command="downloadsCmd_clearList"
|
||||||
|
label="&cmd.clearList2.label;"/>
|
||||||
|
<menuitem id="downloadsDropdownItemOpenDownloadsFolder"
|
||||||
|
oncommand="DownloadsPanel.openDownloadsFolder();"
|
||||||
|
label="&openDownloadsFolder.label;"/>
|
||||||
|
</menupopup>
|
||||||
|
</button>
|
||||||
|
</hbox>
|
||||||
</vbox>
|
</vbox>
|
||||||
</panelview>
|
</panelview>
|
||||||
|
|
||||||
|
|
|
@ -10,3 +10,4 @@ skip-if = os == "linux" # Bug 952422
|
||||||
[browser_confirm_unblock_download.js]
|
[browser_confirm_unblock_download.js]
|
||||||
[browser_iframe_gone_mid_download.js]
|
[browser_iframe_gone_mid_download.js]
|
||||||
[browser_downloads_panel_block.js]
|
[browser_downloads_panel_block.js]
|
||||||
|
[browser_downloads_panel_footer.js]
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function *task_openDownloadsSubPanel() {
|
||||||
|
let downloadSubPanel = document.getElementById("downloadSubPanel");
|
||||||
|
let popupShownPromise = BrowserTestUtils.waitForEvent(downloadSubPanel, "popupshown");
|
||||||
|
|
||||||
|
let downloadsDropmarker = document.getElementById("downloadsFooterDropmarker");
|
||||||
|
EventUtils.synthesizeMouseAtCenter(downloadsDropmarker, {}, window);
|
||||||
|
|
||||||
|
yield popupShownPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(function* test_openDownloadsFolder() {
|
||||||
|
yield task_openPanel();
|
||||||
|
|
||||||
|
yield task_openDownloadsSubPanel();
|
||||||
|
|
||||||
|
yield new Promise(resolve => {
|
||||||
|
sinon.stub(DownloadsCommon, "showDirectory", file => {
|
||||||
|
resolve(Downloads.getPreferredDownloadsDirectory().then(downloadsPath => {
|
||||||
|
is(file.path, downloadsPath, "Check the download folder path.");
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
let itemOpenDownloadsFolder =
|
||||||
|
document.getElementById("downloadsDropdownItemOpenDownloadsFolder");
|
||||||
|
EventUtils.synthesizeMouseAtCenter(itemOpenDownloadsFolder, {}, window);
|
||||||
|
});
|
||||||
|
|
||||||
|
yield task_resetState();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* test_clearList() {
|
||||||
|
const kTestCases = [{
|
||||||
|
downloads: [
|
||||||
|
{ state: nsIDM.DOWNLOAD_NOTSTARTED },
|
||||||
|
{ state: nsIDM.DOWNLOAD_FINISHED },
|
||||||
|
{ state: nsIDM.DOWNLOAD_FAILED },
|
||||||
|
{ state: nsIDM.DOWNLOAD_CANCELED },
|
||||||
|
],
|
||||||
|
expectClearListShown: true,
|
||||||
|
expectedItemNumber: 0,
|
||||||
|
},{
|
||||||
|
downloads: [
|
||||||
|
{ state: nsIDM.DOWNLOAD_NOTSTARTED },
|
||||||
|
{ state: nsIDM.DOWNLOAD_FINISHED },
|
||||||
|
{ state: nsIDM.DOWNLOAD_FAILED },
|
||||||
|
{ state: nsIDM.DOWNLOAD_PAUSED },
|
||||||
|
{ state: nsIDM.DOWNLOAD_CANCELED },
|
||||||
|
],
|
||||||
|
expectClearListShown: true,
|
||||||
|
expectedItemNumber: 1,
|
||||||
|
},{
|
||||||
|
downloads: [
|
||||||
|
{ state: nsIDM.DOWNLOAD_PAUSED },
|
||||||
|
],
|
||||||
|
expectClearListShown: false,
|
||||||
|
expectedItemNumber: 1,
|
||||||
|
}];
|
||||||
|
|
||||||
|
for (let testCase of kTestCases) {
|
||||||
|
yield verify_clearList(testCase);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function *verify_clearList(testCase) {
|
||||||
|
let downloads = testCase.downloads;
|
||||||
|
yield task_addDownloads(downloads);
|
||||||
|
|
||||||
|
yield task_openPanel();
|
||||||
|
is(DownloadsView._downloads.length, downloads.length,
|
||||||
|
"Expect the number of download items");
|
||||||
|
|
||||||
|
yield task_openDownloadsSubPanel();
|
||||||
|
|
||||||
|
let itemClearList = document.getElementById("downloadsDropdownItemClearList");
|
||||||
|
let itemNumberPromise = BrowserTestUtils.waitForCondition(() => {
|
||||||
|
return DownloadsView._downloads.length === testCase.expectedItemNumber;
|
||||||
|
});
|
||||||
|
if (testCase.expectClearListShown) {
|
||||||
|
isnot("true", itemClearList.getAttribute("hidden"),
|
||||||
|
"Should show Clear Preview Panel button");
|
||||||
|
EventUtils.synthesizeMouseAtCenter(itemClearList, {}, window);
|
||||||
|
} else {
|
||||||
|
is("true", itemClearList.getAttribute("hidden"),
|
||||||
|
"Should not show Clear Preview Panel button");
|
||||||
|
}
|
||||||
|
|
||||||
|
yield itemNumberPromise;
|
||||||
|
is(DownloadsView._downloads.length, testCase.expectedItemNumber,
|
||||||
|
"Download items remained.");
|
||||||
|
|
||||||
|
yield task_resetState();
|
||||||
|
}
|
|
@ -24,8 +24,17 @@ const nsIDM = Ci.nsIDownloadManager;
|
||||||
|
|
||||||
var gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
|
var gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
|
||||||
gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
|
gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
|
||||||
|
|
||||||
|
// Load mocking/stubbing library, sinon
|
||||||
|
// docs: http://sinonjs.org/docs/
|
||||||
|
Services.scriptloader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
|
||||||
|
|
||||||
registerCleanupFunction(function () {
|
registerCleanupFunction(function () {
|
||||||
gTestTargetFile.remove(false);
|
gTestTargetFile.remove(false);
|
||||||
|
|
||||||
|
delete window.sinon;
|
||||||
|
delete window.setImmediate;
|
||||||
|
delete window.clearImmediate;
|
||||||
});
|
});
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -62,8 +62,8 @@
|
||||||
<!ENTITY cmd.copyDownloadLink.accesskey "L">
|
<!ENTITY cmd.copyDownloadLink.accesskey "L">
|
||||||
<!ENTITY cmd.removeFromHistory.label "Remove From History">
|
<!ENTITY cmd.removeFromHistory.label "Remove From History">
|
||||||
<!ENTITY cmd.removeFromHistory.accesskey "e">
|
<!ENTITY cmd.removeFromHistory.accesskey "e">
|
||||||
<!ENTITY cmd.clearList.label "Clear List">
|
<!ENTITY cmd.clearList2.label "Clear Preview Panel">
|
||||||
<!ENTITY cmd.clearList.accesskey "a">
|
<!ENTITY cmd.clearList2.accesskey "a">
|
||||||
<!ENTITY cmd.clearDownloads.label "Clear Downloads">
|
<!ENTITY cmd.clearDownloads.label "Clear Downloads">
|
||||||
<!ENTITY cmd.clearDownloads.accesskey "D">
|
<!ENTITY cmd.clearDownloads.accesskey "D">
|
||||||
<!-- LOCALIZATION NOTE (cmd.unblock2.label):
|
<!-- LOCALIZATION NOTE (cmd.unblock2.label):
|
||||||
|
@ -109,6 +109,8 @@
|
||||||
<!ENTITY downloadsHistory.label "Show All Downloads">
|
<!ENTITY downloadsHistory.label "Show All Downloads">
|
||||||
<!ENTITY downloadsHistory.accesskey "S">
|
<!ENTITY downloadsHistory.accesskey "S">
|
||||||
|
|
||||||
|
<!ENTITY openDownloadsFolder.label "Open Downloads Folder">
|
||||||
|
|
||||||
<!ENTITY clearDownloadsButton.label "Clear Downloads">
|
<!ENTITY clearDownloadsButton.label "Clear Downloads">
|
||||||
<!ENTITY clearDownloadsButton.tooltip "Clears completed, canceled and failed downloads">
|
<!ENTITY clearDownloadsButton.tooltip "Clears completed, canceled and failed downloads">
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#emptyDownloads {
|
#emptyDownloads {
|
||||||
padding: 10px 20px;
|
padding: 16px 25px;
|
||||||
|
margin: 0;
|
||||||
/* The panel can be wider than this description after the blocked subview is
|
/* The panel can be wider than this description after the blocked subview is
|
||||||
shown, so center the text. */
|
shown, so center the text. */
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -41,7 +42,7 @@
|
||||||
border-top: 1px solid var(--panel-separator-color);
|
border-top: 1px solid var(--panel-separator-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.downloadsPanelFooter > toolbarseparator {
|
.downloadsPanelFooter toolbarseparator {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
@ -55,6 +56,7 @@
|
||||||
color: inherit;
|
color: inherit;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
min-width: 0;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +65,8 @@
|
||||||
background-color: hsla(210,4%,10%,.07);
|
background-color: hsla(210,4%,10%,.07);
|
||||||
}
|
}
|
||||||
|
|
||||||
.downloadsPanelFooterButton:hover:active {
|
.downloadsPanelFooterButton:hover:active,
|
||||||
|
.downloadsPanelFooterButton[open="true"] {
|
||||||
outline: 1px solid hsla(210,4%,10%,.12);
|
outline: 1px solid hsla(210,4%,10%,.12);
|
||||||
background-color: hsla(210,4%,10%,.12);
|
background-color: hsla(210,4%,10%,.12);
|
||||||
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
|
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
|
||||||
|
@ -82,6 +85,47 @@
|
||||||
background-color: #0568ba;
|
background-color: #0568ba;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#downloadsPanel[hasdownloads] #downloadsHistory {
|
||||||
|
padding-left: 58px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbarseparator.downloadsDropmarkerSplitter {
|
||||||
|
margin: 7px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#downloadsFooter:hover toolbarseparator.downloadsDropmarkerSplitter,
|
||||||
|
#downloadsFooter toolbarseparator.downloadsDropmarkerSplitterExtend {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.downloadsDropmarker {
|
||||||
|
padding: 0 19px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.downloadsDropmarker > .button-box > hbox {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.downloadsDropmarker > .button-box > .button-menu-dropmarker {
|
||||||
|
/* This is to override the linux !important */
|
||||||
|
-moz-appearance: none !important;
|
||||||
|
display: -moz-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.downloadsDropmarker > .button-box > .button-menu-dropmarker > .dropmarker-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
list-style-image: url("chrome://browser/skin/downloads/menubutton-dropmarker.svg");
|
||||||
|
filter: url("chrome://browser/skin/filters.svg#fill");
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override default icon size which is too small for this dropdown */
|
||||||
|
.downloadsDropmarker > .button-box > .button-menu-dropmarker {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
#downloadsSummary {
|
#downloadsSummary {
|
||||||
--summary-padding-end: 38px;
|
--summary-padding-end: 38px;
|
||||||
--summary-padding-start: 12px;
|
--summary-padding-start: 12px;
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- 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/. -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<path d="m 2,6 6,6 6,-6 -1.5,-1.5 -4.5,4.5 -4.5,-4.5 z" />
|
||||||
|
</svg>
|
После Ширина: | Высота: | Размер: 391 B |
|
@ -54,6 +54,7 @@
|
||||||
skin/classic/browser/customizableui/whimsy@2x.png (../shared/customizableui/whimsy@2x.png)
|
skin/classic/browser/customizableui/whimsy@2x.png (../shared/customizableui/whimsy@2x.png)
|
||||||
skin/classic/browser/downloads/contentAreaDownloadsView.css (../shared/downloads/contentAreaDownloadsView.css)
|
skin/classic/browser/downloads/contentAreaDownloadsView.css (../shared/downloads/contentAreaDownloadsView.css)
|
||||||
skin/classic/browser/downloads/download-blocked.svg (../shared/downloads/download-blocked.svg)
|
skin/classic/browser/downloads/download-blocked.svg (../shared/downloads/download-blocked.svg)
|
||||||
|
skin/classic/browser/downloads/menubutton-dropmarker.svg (../shared/downloads/menubutton-dropmarker.svg)
|
||||||
skin/classic/browser/drm-icon.svg (../shared/drm-icon.svg)
|
skin/classic/browser/drm-icon.svg (../shared/drm-icon.svg)
|
||||||
skin/classic/browser/filters.svg (../shared/filters.svg)
|
skin/classic/browser/filters.svg (../shared/filters.svg)
|
||||||
skin/classic/browser/fullscreen/insecure.svg (../shared/fullscreen/insecure.svg)
|
skin/classic/browser/fullscreen/insecure.svg (../shared/fullscreen/insecure.svg)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче