зеркало из https://github.com/mozilla/pjs.git
backing out bug 468011
This commit is contained in:
Коммит
7fc19dc9b5
|
@ -1,169 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Embedding distribution information</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<h2>Introduction</h2>
|
|
||||||
|
|
||||||
<p>This is the Embedding distribution of <a
|
|
||||||
href="http://www.mozilla.org/">Mozilla</a>. It serves as a reference
|
|
||||||
and smoketest distribution to demonstrate a typical subset of Mozilla
|
|
||||||
needed to embed it into another application. The distribution contains
|
|
||||||
sample applications for testing and development purposes.</p>
|
|
||||||
|
|
||||||
<p>Please note the phrase<i> typical subset</i>! If you want to use a different
|
|
||||||
subset then see the <a href="#cust">customizing the embedding distribution</a>
|
|
||||||
section below.</p>
|
|
||||||
|
|
||||||
<h2>Where can I find more information about embedding? </h2>
|
|
||||||
|
|
||||||
<p>Mozilla.org - <a href="http://www.mozilla.org/">http://www.mozilla.org/</a>.</p>
|
|
||||||
|
|
||||||
<p>The Embedding project page - <a
|
|
||||||
href="http://www.mozilla.org/projects/embedding/">http://www.mozilla.org/projects/embedding/</a>.
|
|
||||||
This page should serve as your main point of information for learning
|
|
||||||
how to embed Mozilla in your own applications.</p>
|
|
||||||
|
|
||||||
<p>The Embedding Frequently Asked Questions (FAQ) page - <a
|
|
||||||
href="http://www.mozilla.org/projects/embedding/faq.html">http://www.mozilla.org/projects/embedding/faq.html</a>. </p>
|
|
||||||
|
|
||||||
<p>The Embedding newsgroup - <a
|
|
||||||
href="news:netscape.public.mozilla.embedding">news:netscape.public.mozilla.embedding</a>.</p>
|
|
||||||
|
|
||||||
<p>The Embedding QA page - <a
|
|
||||||
href="http://www.mozilla.org/quality/embed/">http://www.mozilla.org/quality/embed/</a>.</p>
|
|
||||||
|
|
||||||
<p>Instructions for building Mozilla (including Embedding) - <a
|
|
||||||
href="http://www.mozilla.org/build/">http://www.mozilla.org/build/</a>.</p>
|
|
||||||
|
|
||||||
<p>Instructions for fetching Mozilla source code (including Embedding) - <a
|
|
||||||
href="http://www.mozilla.org/source.html">http://www.mozilla.org/source.html</a>.</p>
|
|
||||||
|
|
||||||
<h2>Contents </h2>
|
|
||||||
|
|
||||||
<p>The distribution contains, libraries, resources and chrome. All of this
|
|
||||||
is a subset of Mozilla, repackaged for embedding. The layout follows
|
|
||||||
the usual Mozilla directory, i.e. </p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>./ - Test applications, launch scripts and core libraries (xpcom,
|
|
||||||
nspr etc.)</li>
|
|
||||||
<li>./components/ - XPCOM components (network, graphics, layout,
|
|
||||||
content, etc.).</li>
|
|
||||||
<li>./chrome/ - chrome files. In embedding most of the chrome has
|
|
||||||
been re-packaged into a single embed.jar file to make it more manageable.</li>
|
|
||||||
<li>./res/ - miscellaneous rendering and parser files.</li>
|
|
||||||
<li>./defaults/ - default preference settings</li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3>Unix</h3>
|
|
||||||
|
|
||||||
<h4>TestGtkEmbed</h4>
|
|
||||||
|
|
||||||
<p>A simple browser application built using the GTK+ Browser Widget.</p>
|
|
||||||
|
|
||||||
<h4>GTK+ Browser Widget</h4>
|
|
||||||
|
|
||||||
<p>A GTK+ widget for embedding Mozilla into GTK+/GNOME applications such as
|
|
||||||
<a href="http://galeon.sourceforge.net/">Galeon</a>. More information
|
|
||||||
about the widget may be found here <a
|
|
||||||
href="http://www.mozilla.org/unix/gtk-embedding.html">http://www.mozilla.org/unix/gtk-embedding.html</a>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>Windows</h3>
|
|
||||||
|
|
||||||
<h4>MFCEmbed</h4>
|
|
||||||
|
|
||||||
<p>A complete browser application written in MFC and the Gecko embedding APIs.
|
|
||||||
This application demonstrates and exercises the functionality necessary
|
|
||||||
to put together a fully featured browser using Gecko.</p>
|
|
||||||
|
|
||||||
<h4>Mozilla ActiveX Control</h4>
|
|
||||||
|
|
||||||
<p>An ActiveX browser control built with the embedding APIs. You may use the
|
|
||||||
control to embed a browser inside applications written in VB, Delphi
|
|
||||||
etc. More information about the control including installation instructions
|
|
||||||
may be found at <a href="http://www.iol.ie/%7Elocka/mozilla/mozilla.htm">http://www.iol.ie/~locka/mozilla/mozilla.htm</a>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>Macintosh </h3>
|
|
||||||
|
|
||||||
<p>PPEmbed is built as part of the standard Mac embedding build. Work on <a
|
|
||||||
href="http://www.mozilla.org/ports/fizzilla/Mach.html">Fizilla Mach</a>
|
|
||||||
may also yield a Mac embedding distribution in the future. </p>
|
|
||||||
|
|
||||||
<h2>Installation and Usage </h2>
|
|
||||||
|
|
||||||
<h3>Unix</h3>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>Untar the embedding distribution into an empty directory of your
|
|
||||||
choice.</li>
|
|
||||||
<li>From the command-line, cd into the directory and type
|
|
||||||
'./run-mozilla.sh ./TestGtkEmbed'</li>
|
|
||||||
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h3>Windows </h3>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>Unzip the embedding distribution into an empty directory of your
|
|
||||||
choice.</li>
|
|
||||||
<li>Double-click on mfcembed.exe</li>
|
|
||||||
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h2><a name="cust">Customizing the embedding distribution</a></h2>
|
|
||||||
|
|
||||||
<p>The embedding distribution is produced by first building the full Mozilla
|
|
||||||
source and cherry picking files and chrome into a separate embedding
|
|
||||||
directory. Basically the steps are:</p>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>Build Mozilla as usual</li>
|
|
||||||
<li>cd mozilla/embedding/config</li>
|
|
||||||
<li>make</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<p><strong>Note:</strong> When using an objdir, cd into
|
|
||||||
<objdir>/embedding/config instead, and run make there.</p>
|
|
||||||
|
|
||||||
<p>The embedding distribution will appear in mozilla/dist/Embed.</p>
|
|
||||||
|
|
||||||
<p>The build process works by reading a file called basebrowser-win (or
|
|
||||||
basebrowser-unix, basebrowser-mac-macho etc.) and copying the listed files
|
|
||||||
from mozilla/dist/bin into mozilla/dist/Embed. It then reads a file called
|
|
||||||
embed-jar.mn and repackages the specified chrome files and directories
|
|
||||||
into a single embed.jar.<br>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>This process can be customized in one of two ways:<br>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li>Edit basebrowser-foo (where foo = win, unix etc.) and remove/add the
|
|
||||||
items you require</li>
|
|
||||||
<li>Create a new file called client-foo and add the items you require.
|
|
||||||
This file is detected by the build process and will be run <i>after</i> the
|
|
||||||
contents of basebrowser-foo have been copied.</li>
|
|
||||||
|
|
||||||
</ol>
|
|
||||||
The latter is the best probably approach to take since the embedder can
|
|
||||||
maintain these files without modifying ones in Mozilla<i></i>. The client-foo
|
|
||||||
file allows embedders to specify files to be added or removed from the
|
|
||||||
default embedding distribution. A minus sign in front of a filename
|
|
||||||
deletes that file.
|
|
||||||
<p>For example if you wanted a distribution containing extra character converters,
|
|
||||||
but not the ActiveX control and not XMLExtras you might define a client-win
|
|
||||||
like this:</p>
|
|
||||||
|
|
||||||
<pre>[Embed]<br>components\ucvja.dll<br>components\ucvko.dll<br>components\ucvcn.dll<br>components\ucvtw.dll<br>components\ucvtw2.dll<br>components\ucvibm.dll<br>-mozctl.dll<br>-mozctlx.dll<br>-components\xmlextras.dll<br>-components\xmlextras.xpt<br></pre>
|
|
||||||
|
|
||||||
<p></p>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -7,7 +7,7 @@
|
||||||
document.cookie = "can=has";
|
document.cookie = "can=has";
|
||||||
|
|
||||||
// send a message to our test document, to say we're done loading
|
// send a message to our test document, to say we're done loading
|
||||||
window.opener.postMessage("message", "http://localhost:8888");
|
window.opener.postMessage("f_lf_i msg data", "http://localhost:8888");
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
SimpleTest.waitForExplicitFinish();
|
var gExpectedCookies;
|
||||||
|
var gExpectedLoads;
|
||||||
|
|
||||||
var gPopup = null;
|
var gPopup;
|
||||||
|
|
||||||
var gExpectedCookies = 0;
|
|
||||||
var gExpectedLoads = 0;
|
|
||||||
var gLoads = 0;
|
var gLoads = 0;
|
||||||
|
|
||||||
function setupTest(uri, cookies, loads) {
|
function setupTest(uri, cookies, loads) {
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
|
||||||
Components.classes["@mozilla.org/preferences-service;1"]
|
Components.classes["@mozilla.org/preferences-service;1"]
|
||||||
|
@ -20,36 +21,39 @@ function setupTest(uri, cookies, loads) {
|
||||||
gExpectedCookies = cookies;
|
gExpectedCookies = cookies;
|
||||||
gExpectedLoads = loads;
|
gExpectedLoads = loads;
|
||||||
|
|
||||||
|
// Listen for MessageEvents.
|
||||||
|
window.addEventListener("message", messageReceiver, false);
|
||||||
|
|
||||||
// load a window which contains an iframe; each will attempt to set
|
// load a window which contains an iframe; each will attempt to set
|
||||||
// cookies from their respective domains.
|
// cookies from their respective domains.
|
||||||
gPopup = window.open(uri, 'hai', 'width=100,height=100');
|
gPopup = window.open(uri, 'hai', 'width=100,height=100');
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("message", messageReceiver, false);
|
|
||||||
|
|
||||||
/** Receives MessageEvents to this window. */
|
/** Receives MessageEvents to this window. */
|
||||||
|
// Count and check loads.
|
||||||
function messageReceiver(evt)
|
function messageReceiver(evt)
|
||||||
{
|
{
|
||||||
ok(evt instanceof MessageEvent, "event type", evt);
|
is(evt.data, "message", "message data received from popup");
|
||||||
|
|
||||||
if (evt.data != "message") {
|
if (evt.data != "message") {
|
||||||
|
gPopup.close();
|
||||||
window.removeEventListener("message", messageReceiver, false);
|
window.removeEventListener("message", messageReceiver, false);
|
||||||
|
|
||||||
ok(false, "message", evt.data);
|
|
||||||
|
|
||||||
gPopup.close();
|
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only run the test when all our children are done loading & setting cookies
|
// only run the test when all our children are done loading & setting cookies
|
||||||
if (++gLoads == gExpectedLoads) {
|
if (++gLoads == gExpectedLoads) {
|
||||||
|
gPopup.close();
|
||||||
window.removeEventListener("message", messageReceiver, false);
|
window.removeEventListener("message", messageReceiver, false);
|
||||||
|
|
||||||
runTest();
|
runTest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runTest() is run by messageReceiver().
|
||||||
|
// Count and check cookies.
|
||||||
function runTest() {
|
function runTest() {
|
||||||
// set a cookie from a domain of "localhost"
|
// set a cookie from a domain of "localhost"
|
||||||
document.cookie = "oh=hai";
|
document.cookie = "oh=hai";
|
||||||
|
@ -58,15 +62,11 @@ function runTest() {
|
||||||
|
|
||||||
var cs = Components.classes["@mozilla.org/cookiemanager;1"]
|
var cs = Components.classes["@mozilla.org/cookiemanager;1"]
|
||||||
.getService(Components.interfaces.nsICookieManager);
|
.getService(Components.interfaces.nsICookieManager);
|
||||||
var list = cs.enumerator;
|
|
||||||
var count = 0;
|
var count = 0;
|
||||||
while (list.hasMoreElements()) {
|
for(var list = cs.enumerator; list.hasMoreElements(); list.getNext())
|
||||||
count++;
|
++count;
|
||||||
list.getNext();
|
is(count, gExpectedCookies, "total number of cookies");
|
||||||
}
|
|
||||||
is(count, gExpectedCookies, "number of cookies");
|
|
||||||
cs.removeAll();
|
cs.removeAll();
|
||||||
|
|
||||||
gPopup.close();
|
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
|
var gExpectedCookies;
|
||||||
|
var gExpectedHeaders;
|
||||||
|
var gExpectedLoads;
|
||||||
|
|
||||||
|
var gObs;
|
||||||
|
var gPopup;
|
||||||
|
|
||||||
|
var gHeaders = 0;
|
||||||
|
var gLoads = 0;
|
||||||
|
|
||||||
|
// setupTest() is run from 'onload='.
|
||||||
|
function setupTest(uri, domain, cookies, loads, headers) {
|
||||||
SimpleTest.waitForExplicitFinish();
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
var gPopup = null;
|
|
||||||
|
|
||||||
var gExpectedCookies = 0;
|
|
||||||
var gExpectedLoads = 0;
|
|
||||||
var gExpectedHeaders = 0;
|
|
||||||
var gLoads = 0;
|
|
||||||
var gHeaders = 0;
|
|
||||||
|
|
||||||
var o = null;
|
|
||||||
|
|
||||||
function setupTest(uri, domain, cookies, loads, headers) {
|
|
||||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
|
||||||
Components.classes["@mozilla.org/preferences-service;1"]
|
Components.classes["@mozilla.org/preferences-service;1"]
|
||||||
|
@ -21,26 +22,29 @@ function setupTest(uri, domain, cookies, loads, headers) {
|
||||||
.getService(Components.interfaces.nsICookieManager2);
|
.getService(Components.interfaces.nsICookieManager2);
|
||||||
cs.removeAll();
|
cs.removeAll();
|
||||||
cs.add(domain, "", "oh", "hai", false, false, true, Math.pow(2, 62));
|
cs.add(domain, "", "oh", "hai", false, false, true, Math.pow(2, 62));
|
||||||
is(cs.countCookiesFromHost(domain), 1, "cookie inited");
|
is(cs.countCookiesFromHost(domain), 1, "number of cookies for domain " + domain);
|
||||||
|
|
||||||
o = new obs();
|
|
||||||
|
|
||||||
gExpectedCookies = cookies;
|
gExpectedCookies = cookies;
|
||||||
gExpectedLoads = loads;
|
gExpectedLoads = loads;
|
||||||
gExpectedHeaders = headers;
|
gExpectedHeaders = headers;
|
||||||
|
|
||||||
|
gObs = new obs();
|
||||||
|
// Listen for MessageEvents.
|
||||||
|
window.addEventListener("message", messageReceiver, false);
|
||||||
|
|
||||||
// load a window which contains an iframe; each will attempt to set
|
// load a window which contains an iframe; each will attempt to set
|
||||||
// cookies from their respective domains.
|
// cookies from their respective domains.
|
||||||
gPopup = window.open(uri, 'hai', 'width=100,height=100');
|
gPopup = window.open(uri, 'hai', 'width=100,height=100');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Count headers.
|
||||||
function obs () {
|
function obs () {
|
||||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
|
||||||
|
this.window = window;
|
||||||
this.os = Components.classes["@mozilla.org/observer-service;1"]
|
this.os = Components.classes["@mozilla.org/observer-service;1"]
|
||||||
.getService(Components.interfaces.nsIObserverService);
|
.getService(Components.interfaces.nsIObserverService);
|
||||||
this.os.addObserver(this, "http-on-modify-request", false);
|
this.os.addObserver(this, "http-on-modify-request", false);
|
||||||
this.window = window;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
obs.prototype = {
|
obs.prototype = {
|
||||||
|
@ -49,11 +53,11 @@ obs.prototype = {
|
||||||
this.window.netscape.security
|
this.window.netscape.security
|
||||||
.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
|
||||||
var cookie = theSubject.QueryInterface(this.window.Components.interfaces
|
var channel = theSubject.QueryInterface(
|
||||||
.nsIHttpChannel)
|
this.window.Components.interfaces.nsIHttpChannel);
|
||||||
.getRequestHeader("Cookie");
|
this.window.isnot(channel.getRequestHeader("Cookie").indexOf("oh=hai"), -1,
|
||||||
this.window.isnot(cookie.indexOf("oh=hai"), -1, "cookie sent");
|
"cookie 'oh=hai' is in header for " + channel.URI.spec);
|
||||||
gHeaders++;
|
++gHeaders;
|
||||||
},
|
},
|
||||||
|
|
||||||
remove: function obs_remove()
|
remove: function obs_remove()
|
||||||
|
@ -66,52 +70,48 @@ obs.prototype = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("message", messageReceiver, false);
|
|
||||||
|
|
||||||
/** Receives MessageEvents to this window. */
|
/** Receives MessageEvents to this window. */
|
||||||
|
// Count and check loads.
|
||||||
function messageReceiver(evt)
|
function messageReceiver(evt)
|
||||||
{
|
{
|
||||||
ok(evt instanceof MessageEvent, "event type", evt);
|
is(evt.data, "f_lf_i msg data", "message data received from popup");
|
||||||
|
if (evt.data != "f_lf_i msg data") {
|
||||||
if (evt.data != "message") {
|
gPopup.close();
|
||||||
window.removeEventListener("message", messageReceiver, false);
|
window.removeEventListener("message", messageReceiver, false);
|
||||||
|
|
||||||
ok(false, "message", evt.data);
|
gObs.remove();
|
||||||
|
|
||||||
o.remove();
|
|
||||||
gPopup.close();
|
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only run the test when all our children are done loading & setting cookies
|
// only run the test when all our children are done loading & setting cookies
|
||||||
if (++gLoads == gExpectedLoads) {
|
if (++gLoads == gExpectedLoads) {
|
||||||
|
gPopup.close();
|
||||||
window.removeEventListener("message", messageReceiver, false);
|
window.removeEventListener("message", messageReceiver, false);
|
||||||
|
|
||||||
runTest();
|
runTest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runTest() is run by messageReceiver().
|
||||||
|
// Check headers, and count and check cookies.
|
||||||
function runTest() {
|
function runTest() {
|
||||||
// set a cookie from a domain of "localhost"
|
// set a cookie from a domain of "localhost"
|
||||||
document.cookie = "o=noes";
|
document.cookie = "o=noes";
|
||||||
|
|
||||||
|
gObs.remove();
|
||||||
|
|
||||||
|
is(gHeaders, gExpectedHeaders, "number of observed request headers");
|
||||||
|
|
||||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
|
||||||
var cs = Components.classes["@mozilla.org/cookiemanager;1"]
|
var cs = Components.classes["@mozilla.org/cookiemanager;1"]
|
||||||
.getService(Components.interfaces.nsICookieManager);
|
.getService(Components.interfaces.nsICookieManager);
|
||||||
var list = cs.enumerator;
|
|
||||||
var count = 0;
|
var count = 0;
|
||||||
while (list.hasMoreElements()) {
|
for(var list = cs.enumerator; list.hasMoreElements(); list.getNext())
|
||||||
count++;
|
++count;
|
||||||
list.getNext();
|
is(count, gExpectedCookies, "total number of cookies");
|
||||||
}
|
|
||||||
is(count, gExpectedCookies, "number of cookies");
|
|
||||||
cs.removeAll();
|
cs.removeAll();
|
||||||
|
|
||||||
is(gHeaders, gExpectedHeaders, "number of request headers");
|
|
||||||
|
|
||||||
o.remove();
|
|
||||||
gPopup.close();
|
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||||
</head>
|
</head>
|
||||||
|
<!--
|
||||||
|
*5 cookies: 1+1 from file_testloadflags.js, 2 from file_loadflags_inner.html + 1 from image1.png^headers^.
|
||||||
|
*1 load: file_loadflags_inner.html.
|
||||||
|
*2 headers: 1 for file_loadflags_inner.html + 1 for image1.png.
|
||||||
|
-->
|
||||||
<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_loadflags_inner.html', 'example.org', 5, 1, 2)">
|
<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_loadflags_inner.html', 'example.org', 5, 1, 2)">
|
||||||
<p id="display"></p>
|
<p id="display"></p>
|
||||||
<pre id="test">
|
<pre id="test">
|
||||||
|
|
|
@ -41,6 +41,7 @@ DEPTH = .
|
||||||
topsrcdir = @top_srcdir@
|
topsrcdir = @top_srcdir@
|
||||||
srcdir = @srcdir@
|
srcdir = @srcdir@
|
||||||
|
|
||||||
|
run_for_side_effects := $(shell echo "MAKE: $(MAKE)")
|
||||||
include $(DEPTH)/config/autoconf.mk
|
include $(DEPTH)/config/autoconf.mk
|
||||||
|
|
||||||
DIRS = config
|
DIRS = config
|
||||||
|
|
|
@ -893,7 +893,7 @@ js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj)
|
||||||
|
|
||||||
#ifdef JS_TRACER
|
#ifdef JS_TRACER
|
||||||
|
|
||||||
static JSBool FASTCALL
|
static inline JSBool FASTCALL
|
||||||
dense_grow(JSContext* cx, JSObject* obj, jsint i, jsval v)
|
dense_grow(JSContext* cx, JSObject* obj, jsint i, jsval v)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -179,7 +179,7 @@ math_atan(JSContext *cx, uintN argc, jsval *vp)
|
||||||
return js_NewNumberInRootedValue(cx, z, vp);
|
return js_NewNumberInRootedValue(cx, z, vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static jsdouble JS_FASTCALL
|
static inline jsdouble JS_FASTCALL
|
||||||
math_atan2_kernel(jsdouble x, jsdouble y)
|
math_atan2_kernel(jsdouble x, jsdouble y)
|
||||||
{
|
{
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
|
@ -227,7 +227,7 @@ math_atan2(JSContext *cx, uintN argc, jsval *vp)
|
||||||
return js_NewNumberInRootedValue(cx, math_atan2_kernel (x, y), vp);
|
return js_NewNumberInRootedValue(cx, math_atan2_kernel (x, y), vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline jsdouble
|
static inline jsdouble JS_FASTCALL
|
||||||
math_ceil_kernel(jsdouble x)
|
math_ceil_kernel(jsdouble x)
|
||||||
{
|
{
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|
|
@ -72,7 +72,6 @@ XPIDLSRCS = \
|
||||||
nsIAuthPromptAdapterFactory.idl \
|
nsIAuthPromptAdapterFactory.idl \
|
||||||
nsIAuthPromptCallback.idl \
|
nsIAuthPromptCallback.idl \
|
||||||
nsIAsyncStreamCopier.idl \
|
nsIAsyncStreamCopier.idl \
|
||||||
nsISafeOutputStream.idl \
|
|
||||||
nsIBufferedStreams.idl \
|
nsIBufferedStreams.idl \
|
||||||
nsICancelable.idl \
|
nsICancelable.idl \
|
||||||
nsICryptoHash.idl \
|
nsICryptoHash.idl \
|
||||||
|
|
|
@ -110,11 +110,48 @@ function test_async_write_file()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function test_async_write_file_nsISafeOutputStream()
|
||||||
|
{
|
||||||
|
do_test_pending();
|
||||||
|
|
||||||
|
// First, we need an output file to write to.
|
||||||
|
let file = Cc["@mozilla.org/file/directory_service;1"].
|
||||||
|
getService(Ci.nsIProperties).
|
||||||
|
get("TmpD", Ci.nsIFile);
|
||||||
|
file.append("NetUtil-async-test-file.tmp");
|
||||||
|
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
|
||||||
|
|
||||||
|
// Then, we need an output stream to our output file.
|
||||||
|
let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
|
||||||
|
createInstance(Ci.nsIFileOutputStream);
|
||||||
|
ostream.init(file, -1, -1, 0);
|
||||||
|
|
||||||
|
// Finally, we need an input stream to take data from.
|
||||||
|
const TEST_DATA = "this is a test string";
|
||||||
|
let istream = Cc["@mozilla.org/io/string-input-stream;1"].
|
||||||
|
createInstance(Ci.nsIStringInputStream);
|
||||||
|
istream.setData(TEST_DATA, TEST_DATA.length);
|
||||||
|
|
||||||
|
NetUtil.asyncCopy(istream, ostream, function(aResult) {
|
||||||
|
// Make sure the copy was successful!
|
||||||
|
do_check_true(Components.isSuccessCode(aResult));
|
||||||
|
|
||||||
|
// Check the file contents.
|
||||||
|
do_check_eq(TEST_DATA, getFileContents(file));
|
||||||
|
|
||||||
|
// Remove the file, and finish the test.
|
||||||
|
file.remove(false);
|
||||||
|
do_test_finished();
|
||||||
|
run_next_test();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//// Test Runner
|
//// Test Runner
|
||||||
|
|
||||||
let tests = [
|
let tests = [
|
||||||
test_async_write_file,
|
test_async_write_file,
|
||||||
|
test_async_write_file_nsISafeOutputStream,
|
||||||
];
|
];
|
||||||
let index = 0;
|
let index = 0;
|
||||||
|
|
||||||
|
|
|
@ -49,22 +49,89 @@ var doctypes = [
|
||||||
'BackCompat' , '<!DOCTYPE HTMLz PUBLIC "DTD HTML 3.2">',
|
'BackCompat' , '<!DOCTYPE HTMLz PUBLIC "DTD HTML 3.2">',
|
||||||
'BackCompat' , '<!DOCTYPE "DTD HTML 3.2">',
|
'BackCompat' , '<!DOCTYPE "DTD HTML 3.2">',
|
||||||
/* end from bug 363883 */
|
/* end from bug 363883 */
|
||||||
|
// from bug 502600
|
||||||
|
'BackCompat' , '<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">',
|
||||||
];
|
];
|
||||||
|
|
||||||
function test(mode,i){
|
////
|
||||||
is(mode,doctypes[i],doctypes[i+1])
|
// Restore the original value of the html5.enable pref,
|
||||||
|
// and finish.
|
||||||
|
//
|
||||||
|
function finishTest() {
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
||||||
|
.getService(Components.interfaces.nsIPrefBranch);
|
||||||
|
prefs.setBoolPref("html5.enable", gOriginalHtml5Pref);
|
||||||
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
var results = new Array();
|
|
||||||
|
////
|
||||||
|
// Verify that the iframe's compatibility mode matches
|
||||||
|
// the expected value. This function is called from the
|
||||||
|
// test iframe's onload handler. When verifying the
|
||||||
|
// last test in the group, if there is no original
|
||||||
|
// value for the html5.enable pref stored in the
|
||||||
|
// 'gOriginalHtml5Pref' variable, then run the tests
|
||||||
|
// again in HTML5 mode. Otherwise, finish the test.
|
||||||
|
//
|
||||||
|
function test(mode,i){
|
||||||
|
is(mode,doctypes[i],doctypes[i+1]);
|
||||||
|
if (i == doctypes.length - 2) {
|
||||||
|
if (typeof(gOriginalHtml5Pref) == "undefined") {
|
||||||
|
doTestHtml5();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
finishTest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
// Enable the HTML5 parser, then iterate through the tests
|
||||||
|
// a second time.
|
||||||
|
//
|
||||||
|
function doTestHtml5() {
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
||||||
|
.getService(Components.interfaces.nsIPrefBranch);
|
||||||
|
gOriginalHtml5Pref = prefs.getBoolPref("html5.enable");
|
||||||
|
prefs.setBoolPref("html5.enable", true);
|
||||||
|
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
// Insert a hidden iframe into the document, with the src
|
||||||
|
// containing the test doctype. The iframe's onload
|
||||||
|
// function is set to call the test's verification step.
|
||||||
|
//
|
||||||
function insert_iframe(doctype,expected) {
|
function insert_iframe(doctype,expected) {
|
||||||
var elm = document.createElement('iframe');
|
var elm = document.createElement('iframe');
|
||||||
elm.setAttribute('src', 'data:text/html,'+doctype+'<html><body onload="parent.test(document.compatMode,'+i+')"></body>');
|
elm.setAttribute('src', 'data:text/html,' + doctype +
|
||||||
|
'<html><body onload="parent.test(document.compatMode,'+i+')"></body>');
|
||||||
elm.setAttribute('style', 'display:none');
|
elm.setAttribute('style', 'display:none');
|
||||||
document.getElementsByTagName('body')[0].appendChild(elm);
|
document.getElementsByTagName('body')[0].appendChild(elm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
// First iteration of the compatibility mode tests, without
|
||||||
|
// the HTML5 parser enabled.
|
||||||
|
//
|
||||||
|
function doTest() {
|
||||||
for (i=0; i < doctypes.length; i+=2) {
|
for (i=0; i < doctypes.length; i+=2) {
|
||||||
insert_iframe(doctypes[i+1],doctypes[i]);
|
insert_iframe(doctypes[i+1],doctypes[i]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
// Run the compatbility mode tests. First, the tests are run
|
||||||
|
// without the HTML5 parser enabled. Completing the last test
|
||||||
|
// then triggers a second iteration, this time with the HTML5
|
||||||
|
// parser enabled.
|
||||||
|
//
|
||||||
|
var gOriginalHtml5Pref;
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
doTest();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
|
@ -48,8 +48,7 @@ const resultFileName = "test\u00e3\u041b\u3056" + Date.now() + ".doc";
|
||||||
|
|
||||||
function checkResult() {
|
function checkResult() {
|
||||||
// delete the saved file (this doesn't affect the "recent documents" list)
|
// delete the saved file (this doesn't affect the "recent documents" list)
|
||||||
var resultFile = dirSvc.get("ProfD", Ci.nsIFile);
|
var resultFile = do_get_file(resultFileName);
|
||||||
resultFile.append(resultFileName);
|
|
||||||
resultFile.remove(false);
|
resultFile.remove(false);
|
||||||
|
|
||||||
do_check_true(checkRecentDocsFor(resultFileName));
|
do_check_true(checkRecentDocsFor(resultFileName));
|
||||||
|
@ -118,5 +117,9 @@ function run_test()
|
||||||
dm.addListener(listener);
|
dm.addListener(listener);
|
||||||
dm.addListener(getDownloadListener());
|
dm.addListener(getDownloadListener());
|
||||||
|
|
||||||
var dl = addDownload({resultFileName: resultFileName});
|
// need to save the file to the CWD, because the profile dir is in $TEMP,
|
||||||
|
// and Windows apparently doesn't like putting things from $TEMP into
|
||||||
|
// the recent files list.
|
||||||
|
var dl = addDownload({resultFileName: resultFileName,
|
||||||
|
targetFile: do_get_file(resultFileName, true)});
|
||||||
}
|
}
|
||||||
|
|
|
@ -5606,12 +5606,6 @@ PRBool nsWindow::OnHotKey(WPARAM wParam, LPARAM lParam)
|
||||||
|
|
||||||
void nsWindow::OnSettingsChange(WPARAM wParam, LPARAM lParam)
|
void nsWindow::OnSettingsChange(WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
#if defined(WINCE_WINDOWS_MOBILE)
|
|
||||||
if (wParam == SPI_SETSIPINFO) {
|
|
||||||
nsWindowCE::NotifySoftKbObservers();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
nsWindowGfx::OnSettingsChangeGfx(wParam);
|
nsWindowGfx::OnSettingsChangeGfx(wParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,25 +78,28 @@ TriStateBool nsWindowCE::sShowSIPButton = TRI_UNKNOWN;
|
||||||
**************************************************************/
|
**************************************************************/
|
||||||
|
|
||||||
#ifdef WINCE_HAVE_SOFTKB
|
#ifdef WINCE_HAVE_SOFTKB
|
||||||
void nsWindowCE::NotifySoftKbObservers()
|
void nsWindowCE::NotifySoftKbObservers(LPRECT visRect)
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
|
if (!visRect) {
|
||||||
if (observerService) {
|
|
||||||
SIPINFO sipInfo;
|
SIPINFO sipInfo;
|
||||||
wchar_t rectBuf[256];
|
|
||||||
memset(&sipInfo, 0, sizeof(SIPINFO));
|
memset(&sipInfo, 0, sizeof(SIPINFO));
|
||||||
sipInfo.cbSize = sizeof(SIPINFO);
|
sipInfo.cbSize = sizeof(SIPINFO);
|
||||||
if (SipGetInfo(&sipInfo)) {
|
if (SipGetInfo(&sipInfo))
|
||||||
|
visRect = &(sipInfo.rcVisibleDesktop);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
|
||||||
|
if (observerService) {
|
||||||
|
wchar_t rectBuf[256];
|
||||||
_snwprintf(rectBuf, 256, L"{\"left\": %d, \"top\": %d,"
|
_snwprintf(rectBuf, 256, L"{\"left\": %d, \"top\": %d,"
|
||||||
L" \"right\": %d, \"bottom\": %d}",
|
L" \"right\": %d, \"bottom\": %d}",
|
||||||
sipInfo.rcVisibleDesktop.left,
|
visRect->left, visRect->top, visRect->right, visRect->bottom);
|
||||||
sipInfo.rcVisibleDesktop.top,
|
|
||||||
sipInfo.rcVisibleDesktop.right,
|
|
||||||
sipInfo.rcVisibleDesktop.bottom);
|
|
||||||
observerService->NotifyObservers(nsnull, "softkb-change", rectBuf);
|
observerService->NotifyObservers(nsnull, "softkb-change", rectBuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void nsWindowCE::ToggleSoftKB(PRBool show)
|
void nsWindowCE::ToggleSoftKB(PRBool show)
|
||||||
{
|
{
|
||||||
|
@ -131,11 +134,29 @@ void nsWindowCE::ToggleSoftKB(PRBool show)
|
||||||
GetWindowRect(hWndSIP, &sipRect);
|
GetWindowRect(hWndSIP, &sipRect);
|
||||||
int sipH = sipRect.bottom - sipRect.top;
|
int sipH = sipRect.bottom - sipRect.top;
|
||||||
int sipW = sipRect.right - sipRect.left;
|
int sipW = sipRect.right - sipRect.left;
|
||||||
MoveWindow(hWndSIP, (sX - sipW)/2, sY - sipH, sX, sY, TRUE);
|
sipRect.left = (sX - sipW)/2;
|
||||||
}
|
sipRect.top = sY - sipH;
|
||||||
|
sipRect.bottom = sY;
|
||||||
|
sipRect.right = sipRect.left + sipW;
|
||||||
|
MoveWindow(hWndSIP, (sX - sipW)/2, sY - sipH, sipW, sipH, TRUE);
|
||||||
|
SIPINFO sipInfo;
|
||||||
|
RECT visRect;
|
||||||
|
visRect.top = 0;
|
||||||
|
visRect.left = 0;
|
||||||
|
visRect.bottom = show ? sipRect.top : sY;
|
||||||
|
visRect.right = sX;
|
||||||
|
sipInfo.cbSize = sizeof(SIPINFO);
|
||||||
|
sipInfo.fdwFlags = SIPF_DOCKED | SIPF_LOCKED | (show ? SIPF_ON : SIPF_OFF);
|
||||||
|
sipInfo.rcSipRect = sipRect;
|
||||||
|
sipInfo.rcVisibleDesktop = visRect;
|
||||||
|
sipInfo.dwImDataSize = 0;
|
||||||
|
sipInfo.pvImData = NULL;
|
||||||
|
SipSetInfo(&sipInfo);
|
||||||
|
NotifySoftKbObservers(&visRect);
|
||||||
|
} else {
|
||||||
NotifySoftKbObservers();
|
NotifySoftKbObservers();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void nsWindowCE::CreateSoftKeyMenuBar(HWND wnd)
|
void nsWindowCE::CreateSoftKeyMenuBar(HWND wnd)
|
||||||
{
|
{
|
||||||
|
|
|
@ -86,7 +86,7 @@ public:
|
||||||
#if defined(WINCE_HAVE_SOFTKB)
|
#if defined(WINCE_HAVE_SOFTKB)
|
||||||
static void ToggleSoftKB(PRBool show);
|
static void ToggleSoftKB(PRBool show);
|
||||||
static void CreateSoftKeyMenuBar(HWND wnd);
|
static void CreateSoftKeyMenuBar(HWND wnd);
|
||||||
static void NotifySoftKbObservers();
|
static void NotifySoftKbObservers(LPRECT = NULL);
|
||||||
static TriStateBool sShowSIPButton;
|
static TriStateBool sShowSIPButton;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
|
@ -156,6 +156,7 @@ XPIDLSRCS = \
|
||||||
nsIConverterInputStream.idl \
|
nsIConverterInputStream.idl \
|
||||||
nsIConverterOutputStream.idl \
|
nsIConverterOutputStream.idl \
|
||||||
nsIIOUtil.idl \
|
nsIIOUtil.idl \
|
||||||
|
nsISafeOutputStream.idl \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
|
ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "nsIPipe.h"
|
#include "nsIPipe.h"
|
||||||
#include "nsIEventTarget.h"
|
#include "nsIEventTarget.h"
|
||||||
#include "nsIRunnable.h"
|
#include "nsIRunnable.h"
|
||||||
|
#include "nsISafeOutputStream.h"
|
||||||
#include "nsAutoLock.h"
|
#include "nsAutoLock.h"
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
|
|
||||||
|
@ -311,12 +312,15 @@ public:
|
||||||
cancelStatus = mCancelStatus;
|
cancelStatus = mCancelStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ok, copy data from source to sink.
|
// Copy data from the source to the sink until we hit failure or have
|
||||||
|
// copied all the data.
|
||||||
for (;;) {
|
for (;;) {
|
||||||
PRUint32 n;
|
// Note: copyFailed will be true if the source or the sink have
|
||||||
|
// reported an error, or if we failed to write any bytes
|
||||||
|
// because we have consumed all of our data.
|
||||||
PRBool copyFailed = PR_FALSE;
|
PRBool copyFailed = PR_FALSE;
|
||||||
if (!canceled) {
|
if (!canceled) {
|
||||||
n = DoCopy(&sourceCondition, &sinkCondition);
|
PRUint32 n = DoCopy(&sourceCondition, &sinkCondition);
|
||||||
copyFailed = NS_FAILED(sourceCondition) ||
|
copyFailed = NS_FAILED(sourceCondition) ||
|
||||||
NS_FAILED(sinkCondition) || n == 0;
|
NS_FAILED(sinkCondition) || n == 0;
|
||||||
|
|
||||||
|
@ -366,9 +370,19 @@ public:
|
||||||
if (mAsyncSink)
|
if (mAsyncSink)
|
||||||
mAsyncSink->CloseWithStatus(canceled ? cancelStatus :
|
mAsyncSink->CloseWithStatus(canceled ? cancelStatus :
|
||||||
sourceCondition);
|
sourceCondition);
|
||||||
|
else {
|
||||||
|
// If we have an nsISafeOutputStream, and our
|
||||||
|
// sourceCondition and sinkCondition are not set to a
|
||||||
|
// failure state, finish writing.
|
||||||
|
nsCOMPtr<nsISafeOutputStream> sostream =
|
||||||
|
do_QueryInterface(mSink);
|
||||||
|
if (sostream && NS_SUCCEEDED(sourceCondition) &&
|
||||||
|
NS_SUCCEEDED(sinkCondition))
|
||||||
|
sostream->Finish();
|
||||||
else
|
else
|
||||||
mSink->Close();
|
mSink->Close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
mAsyncSink = nsnull;
|
mAsyncSink = nsnull;
|
||||||
mSink = nsnull;
|
mSink = nsnull;
|
||||||
|
|
||||||
|
|
|
@ -539,6 +539,34 @@ class NS_STACK_CLASS nsTAutoString_CharT : public nsTFixedString_CharT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// nsAutoString stores pointers into itself which are invalidated when an
|
||||||
|
// nsTArray is resized, so nsTArray must not be instantiated with nsAutoString
|
||||||
|
// elements!
|
||||||
|
//
|
||||||
|
template<class E> class nsTArrayElementTraits;
|
||||||
|
template<>
|
||||||
|
class nsTArrayElementTraits<nsTAutoString_CharT> {
|
||||||
|
public:
|
||||||
|
template<class A> struct Dont_Instantiate_nsTArray_of;
|
||||||
|
template<class A> struct Instead_Use_nsTArray_of;
|
||||||
|
|
||||||
|
static Dont_Instantiate_nsTArray_of<nsTAutoString_CharT> *
|
||||||
|
Construct(Instead_Use_nsTArray_of<nsTString_CharT> *e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
template<class A>
|
||||||
|
static Dont_Instantiate_nsTArray_of<nsTAutoString_CharT> *
|
||||||
|
Construct(Instead_Use_nsTArray_of<nsTString_CharT> *e,
|
||||||
|
const A &arg) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static Dont_Instantiate_nsTArray_of<nsTAutoString_CharT> *
|
||||||
|
Destruct(Instead_Use_nsTArray_of<nsTString_CharT> *e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nsTXPIDLString extends nsTString such that:
|
* nsTXPIDLString extends nsTString such that:
|
||||||
*
|
*
|
||||||
|
|
Загрузка…
Ссылка в новой задаче