зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset e7624782898d (bug 1487428) for test_inspector-insert.html failures CLOSED TREE
This commit is contained in:
Родитель
b0a0465f39
Коммит
273a59bcda
|
@ -5,6 +5,7 @@
|
|||
"use strict";
|
||||
|
||||
const CSSCompleter = require("devtools/client/sourceeditor/css-autocompleter");
|
||||
const {InspectorFront} = require("devtools/shared/fronts/inspector");
|
||||
|
||||
const CSS_URI = "http://mochi.test:8888/browser/devtools/client/sourceeditor" +
|
||||
"/test/css_statemachine_testcases.css";
|
||||
|
@ -85,7 +86,7 @@ add_task(async function test() {
|
|||
async function runTests() {
|
||||
const target = await TargetFactory.forTab(gBrowser.selectedTab);
|
||||
await target.attach();
|
||||
inspector = target.getFront("inspector");
|
||||
inspector = InspectorFront(target.client, target.form);
|
||||
const walker = await inspector.getWalker();
|
||||
completer = new CSSCompleter({walker: walker,
|
||||
cssProperties: getClientCssProperties()});
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const {InspectorFront} = require("devtools/shared/fronts/inspector");
|
||||
const TEST_URI = "data:text/html;charset=UTF-8,<html><body><bar></bar>" +
|
||||
"<div id='baz'></div><body></html>";
|
||||
|
||||
|
@ -15,7 +16,7 @@ add_task(async function() {
|
|||
async function runTests() {
|
||||
const target = await TargetFactory.forTab(gBrowser.selectedTab);
|
||||
await target.attach();
|
||||
const inspector = target.getFront("inspector");
|
||||
const inspector = InspectorFront(target.client, target.form);
|
||||
const walker = await inspector.getWalker();
|
||||
const {ed, win, edWin} = await setup(null, {
|
||||
autocomplete: true,
|
||||
|
|
|
@ -10,7 +10,7 @@ const { truncateString } = require("devtools/shared/inspector/utils");
|
|||
const { MAX_STRING_LENGTH } = require("devtools/server/actors/highlighters/utils/accessibility");
|
||||
|
||||
add_task(async function() {
|
||||
const { target, walker, accessibility } =
|
||||
const { client, walker, accessibility } =
|
||||
await initAccessibilityFrontForUrl(MAIN_DOMAIN + "doc_accessibility_infobar.html");
|
||||
|
||||
const a11yWalker = await accessibility.getWalker();
|
||||
|
@ -25,7 +25,7 @@ add_task(async function() {
|
|||
|
||||
await accessibility.disable();
|
||||
await waitForA11yShutdown();
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Checks for the AccessibleActor
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, accessibility} =
|
||||
const {client, walker, accessibility} =
|
||||
await initAccessibilityFrontForUrl(MAIN_DOMAIN + "doc_accessibility.html");
|
||||
const modifiers = Services.appinfo.OS === "Darwin" ? "\u2303\u2325" : "Alt+Shift+";
|
||||
|
||||
|
@ -50,6 +50,6 @@ add_task(async function() {
|
|||
|
||||
await accessibility.disable();
|
||||
await waitForA11yShutdown();
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Checks for the AccessibleActor events
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, accessibility} =
|
||||
const {client, walker, accessibility} =
|
||||
await initAccessibilityFrontForUrl(MAIN_DOMAIN + "doc_accessibility.html");
|
||||
const modifiers = Services.appinfo.OS === "Darwin" ? "\u2303\u2325" : "Alt+Shift+";
|
||||
|
||||
|
@ -123,6 +123,6 @@ add_task(async function() {
|
|||
|
||||
await accessibility.disable();
|
||||
await waitForA11yShutdown();
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -16,7 +16,7 @@ function checkAccessibilityState(accessibility, expected) {
|
|||
// Simple checks for the AccessibilityActor and AccessibleWalkerActor
|
||||
|
||||
add_task(async function() {
|
||||
const { walker: domWalker, target, accessibility} = await initAccessibilityFrontForUrl(
|
||||
const { walker: domWalker, client, accessibility} = await initAccessibilityFrontForUrl(
|
||||
"data:text/html;charset=utf-8,<title>test</title><div></div>");
|
||||
|
||||
ok(accessibility, "The AccessibilityFront was created");
|
||||
|
@ -63,6 +63,6 @@ add_task(async function() {
|
|||
checkAccessibilityState(accessibility,
|
||||
{ enabled: false, canBeDisabled: true, canBeEnabled: true });
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Checks for the AccessibleWalkerActor
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, accessibility} =
|
||||
const {client, walker, accessibility} =
|
||||
await initAccessibilityFrontForUrl(MAIN_DOMAIN + "doc_accessibility.html");
|
||||
|
||||
const a11yWalker = await accessibility.getWalker();
|
||||
|
@ -127,6 +127,6 @@ add_task(async function() {
|
|||
|
||||
await accessibility.disable();
|
||||
await waitForA11yShutdown();
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// node after getAnimationPlayersForNode was called on that node.
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} =
|
||||
const {client, walker, animations} =
|
||||
await initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
info("Retrieve a non-animated node");
|
||||
|
@ -57,6 +57,6 @@ add_task(async function() {
|
|||
ok(changes[1].player === p1 || changes[1].player === p2,
|
||||
"The second removed player was one of the previously added players");
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
// multiple animations.
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} =
|
||||
const {client, walker, animations} =
|
||||
await initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
await playerHasAnInitialState(walker, animations);
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
// Check the output of getAnimationPlayersForNode
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} =
|
||||
const {client, walker, animations} =
|
||||
await initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
await theRightNumberOfPlayersIsReturned(walker, animations);
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
const URL = MAIN_DOMAIN + "animation.html";
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} = await initAnimationsFrontForUrl(URL);
|
||||
const {client, walker, animations} = await initAnimationsFrontForUrl(URL);
|
||||
|
||||
info("Get the test node and its animation front");
|
||||
const node = await walker.querySelector(walker.rootNode, ".simple-animation");
|
||||
|
@ -31,6 +31,6 @@ add_task(async function() {
|
|||
// purpose. This object comes straight out of the web animations API
|
||||
// unmodified.
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// information.
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} =
|
||||
const {client, walker, animations} =
|
||||
await initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
info("Retrieve a non animated node");
|
||||
|
@ -51,6 +51,6 @@ add_task(async function() {
|
|||
is(players[1].state.iterationCount, 100,
|
||||
"The iterationCount of the second animation is correct");
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -12,7 +12,7 @@ const URL = MAIN_DOMAIN + "animation.html";
|
|||
add_task(async function() {
|
||||
info("Creating a test document with 2 iframes containing animated nodes");
|
||||
|
||||
const {target, walker, animations} = await initAnimationsFrontForUrl(
|
||||
const {client, walker, animations} = await initAnimationsFrontForUrl(
|
||||
"data:text/html;charset=utf-8," +
|
||||
"<iframe id='iframe' src='" + URL + "'></iframe>");
|
||||
|
||||
|
@ -33,6 +33,6 @@ add_task(async function() {
|
|||
// at least have the infinitely running animations.
|
||||
ok(players.length >= 4, "All subtree animations were retrieved");
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// AnimationPlayerActor.
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} =
|
||||
const {client, walker, animations} =
|
||||
await initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
info("Retrieve a non-animated node");
|
||||
|
@ -44,7 +44,7 @@ add_task(async function() {
|
|||
|
||||
animations.off("mutations", onMutations);
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ const URL = MAIN_DOMAIN + "animation.html";
|
|||
add_task(async function() {
|
||||
info("Creating a test document with 2 iframes containing animated nodes");
|
||||
|
||||
const {target, walker, animations} = await initAnimationsFrontForUrl(
|
||||
const {client, walker, animations} = await initAnimationsFrontForUrl(
|
||||
"data:text/html;charset=utf-8," +
|
||||
"<iframe id='i1' src='" + URL + "'></iframe>" +
|
||||
"<iframe id='i2' src='" + URL + "'></iframe>");
|
||||
|
@ -29,7 +29,7 @@ add_task(async function() {
|
|||
await toggleAndCheckStates(animations, nodeInFrame1, "running");
|
||||
await toggleAndCheckStates(animations, nodeInFrame2, "running");
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ const ALL_ANIMATED_NODES = [".simple-animation", ".multiple-animations",
|
|||
".delayed-animation"];
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} =
|
||||
const {client, walker, animations} =
|
||||
await initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
info("Pause all animations in the test document");
|
||||
|
@ -24,7 +24,7 @@ add_task(async function() {
|
|||
info("Play all animations in the test document");
|
||||
await toggleAndCheckStates(walker, animations, ALL_ANIMATED_NODES, "running");
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
// Check the animation player's initial state
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} =
|
||||
const {client, walker, animations} =
|
||||
await initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
await playerHasAnInitialState(walker, animations);
|
||||
await playerStateIsCorrect(walker, animations);
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
// state that change, the front reconstructs the whole state everytime.
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} =
|
||||
const {client, walker, animations} =
|
||||
await initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
await playerHasCompleteStateAtAllTimes(walker, animations);
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
// AnimationPlayerFront should be sent, and the old one should be removed.
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} =
|
||||
const {client, walker, animations} =
|
||||
await initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
info("Retrieve the test node");
|
||||
|
@ -49,7 +49,7 @@ add_task(async function() {
|
|||
is(reportedMutations.filter(m => m.type === "added").length, 2,
|
||||
"2 'added' events were sent (for the new transitions)");
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
// Check that the AnimationsActor allows changing many players' currentTimes at once.
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} =
|
||||
const {client, walker, animations} =
|
||||
await initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
await testSetCurrentTimes(walker, animations);
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// can have their rates changed at the same time.
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} =
|
||||
const {client, walker, animations} =
|
||||
await initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
info("Retrieve an animated node");
|
||||
|
@ -44,6 +44,6 @@ add_task(async function() {
|
|||
is(animPlayerState.playbackRate, .5, "The playbackRate was updated");
|
||||
}
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Simple checks for the AnimationsActor
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} = await initAnimationsFrontForUrl(
|
||||
const {client, walker, animations} = await initAnimationsFrontForUrl(
|
||||
"data:text/html;charset=utf-8,<title>test</title><div></div>");
|
||||
|
||||
ok(animations, "The AnimationsFront was created");
|
||||
|
@ -32,6 +32,6 @@ add_task(async function() {
|
|||
ok(Array.isArray(players), "An array of players was returned");
|
||||
is(players.length, 0, "0 players have been returned for the invalid node");
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
// Check the animation player's updated state
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, animations} =
|
||||
const {client, walker, animations} =
|
||||
await initAnimationsFrontForUrl(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
await playStateIsUpdatedDynamically(walker, animations);
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ const GRID_FRAGMENT_DATA = {
|
|||
};
|
||||
|
||||
add_task(async function() {
|
||||
const { target, walker, layout } =
|
||||
const { client, walker, layout } =
|
||||
await initLayoutFrontForUrl(MAIN_DOMAIN + "grid.html");
|
||||
const grids = await layout.getGrids(walker.rootNode);
|
||||
const grid = grids[0];
|
||||
|
@ -129,6 +129,6 @@ add_task(async function() {
|
|||
ok(false, "Did not get grid container node front.");
|
||||
}
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Simple checks for the LayoutActor and GridActor
|
||||
|
||||
add_task(async function() {
|
||||
const {target, walker, layout} = await initLayoutFrontForUrl(
|
||||
const {client, walker, layout} = await initLayoutFrontForUrl(
|
||||
"data:text/html;charset=utf-8,<title>test</title><div></div>");
|
||||
|
||||
ok(layout, "The LayoutFront was created");
|
||||
|
@ -26,6 +26,6 @@ add_task(async function() {
|
|||
ok(Array.isArray(grids), "An array of grids was returned");
|
||||
is(grids.length, 0, "0 grids have been returned for the invalid node");
|
||||
|
||||
await target.destroy();
|
||||
await client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -41,50 +41,43 @@ var addTab = async function(url) {
|
|||
const tab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, url);
|
||||
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
|
||||
info(`Tab added a URL ${url} loaded`);
|
||||
info(`Tab added and URL ${url} loaded`);
|
||||
|
||||
return tab.linkedBrowser;
|
||||
};
|
||||
|
||||
// does almost the same thing as addTab, but directly returns an object
|
||||
async function addTabTarget(url) {
|
||||
info(`Adding a new tab with URL: ${url}`);
|
||||
const tab = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, url);
|
||||
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
info(`Tab added a URL ${url} loaded`);
|
||||
return getTargetForTab(tab);
|
||||
}
|
||||
|
||||
async function getTargetForTab(tab) {
|
||||
const target = await TargetFactory.forTab(tab);
|
||||
info("Attaching to the active tab.");
|
||||
await target.attach();
|
||||
return target;
|
||||
}
|
||||
|
||||
async function initAnimationsFrontForUrl(url) {
|
||||
const { inspector, walker, target } = await initInspectorFront(url);
|
||||
const animations = target.getFront("animations");
|
||||
const {AnimationsFront} = require("devtools/shared/fronts/animation");
|
||||
|
||||
return {inspector, walker, animations, target};
|
||||
const { inspector, walker, client, form } = await initInspectorFront(url);
|
||||
const animations = AnimationsFront(client, form);
|
||||
|
||||
return {inspector, walker, animations, client};
|
||||
}
|
||||
|
||||
async function initLayoutFrontForUrl(url) {
|
||||
const {inspector, walker, target} = await initInspectorFront(url);
|
||||
const {inspector, walker, client} = await initInspectorFront(url);
|
||||
const layout = await walker.getLayoutInspector();
|
||||
|
||||
return {inspector, walker, layout, target};
|
||||
return {inspector, walker, layout, client};
|
||||
}
|
||||
|
||||
async function initAccessibilityFrontForUrl(url) {
|
||||
const target = await addTabTarget(url);
|
||||
const inspector = target.getFront("inspector");
|
||||
const {AccessibilityFront} = require("devtools/shared/fronts/accessibility");
|
||||
const {InspectorFront} = require("devtools/shared/fronts/inspector");
|
||||
|
||||
await addTab(url);
|
||||
|
||||
initDebuggerServer();
|
||||
const client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
const form = await connectDebuggerClient(client);
|
||||
const inspector = InspectorFront(client, form);
|
||||
const walker = await inspector.getWalker();
|
||||
const accessibility = target.getFront("accessibility");
|
||||
const accessibility = AccessibilityFront(client, form);
|
||||
|
||||
await accessibility.bootstrap();
|
||||
|
||||
return {inspector, walker, accessibility, target};
|
||||
return {inspector, walker, accessibility, client};
|
||||
}
|
||||
|
||||
function initDebuggerServer() {
|
||||
|
@ -100,20 +93,28 @@ function initDebuggerServer() {
|
|||
}
|
||||
|
||||
async function initPerfFront() {
|
||||
const {PerfFront} = require("devtools/shared/fronts/perf");
|
||||
|
||||
initDebuggerServer();
|
||||
const client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
await waitUntilClientConnected(client);
|
||||
const front = await client.mainRoot.getFront("perf");
|
||||
const rootForm = await getRootForm(client);
|
||||
const front = PerfFront(client, rootForm);
|
||||
return {front, client};
|
||||
}
|
||||
|
||||
async function initInspectorFront(url) {
|
||||
const target = await addTabTarget(url);
|
||||
const { InspectorFront } = require("devtools/shared/fronts/inspector");
|
||||
await addTab(url);
|
||||
|
||||
initDebuggerServer();
|
||||
const client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
const form = await connectDebuggerClient(client);
|
||||
const inspector = InspectorFront(client, form);
|
||||
|
||||
const inspector = target.getFront("inspector");
|
||||
const walker = await inspector.getWalker();
|
||||
|
||||
return {inspector, walker, target};
|
||||
return {inspector, walker, client, form};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -248,7 +249,7 @@ function waitForMarkerType(front, types, predicate,
|
|||
}
|
||||
|
||||
const markers = unpackFun(name, data);
|
||||
info("Got markers");
|
||||
info("Got markers: " + JSON.stringify(markers, null, 2));
|
||||
|
||||
filteredMarkers = filteredMarkers.concat(
|
||||
markers.filter(m => types.includes(m.name)));
|
||||
|
|
|
@ -25,12 +25,10 @@ let gInspectee = null;
|
|||
|
||||
addTest(function setup() {
|
||||
const url = document.getElementById("inspectorContent").href;
|
||||
attachURL(url, async function(err, client, tab, doc) {
|
||||
attachURL(url, function(err, client, tab, doc) {
|
||||
gInspectee = doc;
|
||||
const {TargetFactory} = require("devtools/client/framework/target");
|
||||
const target = await TargetFactory.forTab(tab);
|
||||
await target.attach();
|
||||
const inspectorFront = target.getFront("inspector");
|
||||
const {InspectorFront} = require("devtools/shared/fronts/inspector");
|
||||
const inspectorFront = InspectorFront(client, tab);
|
||||
promiseDone(inspectorFront.getWalker().then(walker => {
|
||||
ok(walker, "getWalker() should return an actor.");
|
||||
gWalker = walker;
|
||||
|
|
Загрузка…
Ссылка в новой задаче