Merge m-c to inbound. a=merge
Merge m-c to inbound. a=merge
--- a/addon-sdk/source/test/test-buffer.js
+++ b/addon-sdk/source/test/test-buffer.js
@@ -27,17 +27,17 @@
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
const { Buffer, TextEncoder, TextDecoder } = require('sdk/io/buffer');
const { safeMerge } = require('sdk/util/object');
-const ENCODINGS = ['utf-8', 'utf-16le', 'utf-16be'];
+const ENCODINGS = ['utf-8'];
exports.testBufferMain = function (assert) {
let b = Buffer('abcdef');
// try to create 0-length buffers
new Buffer('');
new Buffer(0);
// test encodings supported by node;
@@ -241,19 +241,16 @@ exports.testBufferCopy = function (asser
// try to copy 0 bytes from past the end of the source buffer
b.copy(new Buffer(1), 0, 2048, 2048);
};
exports.testBufferWrite = function (assert) {
let b = Buffer(1024);
b.fill(0);
- assert.throws(() => {
- b.write('test string', 0, 5, 'invalid');
- }, RangeError, 'invalid encoding with buffer write throws');
// try to write a 0-length string beyond the end of b
assert.throws(function() {
b.write('', 2048);
}, RangeError, 'writing a 0-length string beyond buffer throws');
// throw when writing to negative offset
assert.throws(function() {
b.write('a', -1);
}, RangeError, 'writing negative offset on buffer throws');
--- a/browser/base/content/test/general/browser_tab_dragdrop.js
+++ b/browser/base/content/test/general/browser_tab_dragdrop.js
@@ -31,57 +31,78 @@ function loadURI(tab, url) {
tab.linkedBrowser.loadURI(url);
return BrowserTestUtils.browserLoaded(tab.linkedBrowser);
}
// Creates a framescript which caches the current object value from the plugin
// in the page. checkObjectValue below verifies that the framescript is still
// active for the browser and that the cached value matches that from the plugin
// in the page which tells us the plugin hasn't been reinitialized.
-function cacheObjectValue(browser) {
- let frame_script = function() {
+function* cacheObjectValue(browser) {
+ info("11111111")
+ yield ContentTask.spawn(browser, null, function*() {
+ info("ct--------------11111111")
let plugin = content.document.wrappedJSObject.body.firstChild;
- let objectValue = plugin.getObjectValue();
-
- addMessageListener("Test:CheckObjectValue", () => {
+ info(`plugin is ${plugin}`);
+ let win = content.document.defaultView;
+ info(`win is ${win}`);
+ win.objectValue = plugin.getObjectValue();
+ info(`got objectValue: ${win.objectValue}`);
+ win.checkObjectValueListener = () => {
+ let result;
+ let exception;
try {
- let plugin = content.document.wrappedJSObject.body.firstChild;
- sendAsyncMessage("Test:CheckObjectValue", {
- result: plugin.checkObjectValue(objectValue)
- });
+ result = plugin.checkObjectValue(win.objectValue);
+ } catch (e) {
+ exception = e.toString();
}
- catch (e) {
- sendAsyncMessage("Test:CheckObjectValue", {
- result: null,
- exception: e.toString()
- });
- }
- });
- };
+ info(`sending plugin.checkObjectValue(objectValue): ${result}`);
+ sendAsyncMessage("Test:CheckObjectValueResult", {
+ result,
+ exception
+ });
+ };
+
+ addMessageListener("Test:CheckObjectValue", win.checkObjectValueListener);
+ });
+}
- browser.messageManager.loadFrameScript("data:,(" + frame_script.toString() + ")();", false)
+// Note, can't run this via registerCleanupFunction because it needs the
+// browser to still be alive and have a messageManager.
+function* cleanupObjectValue(browser) {
+ info("entered cleanupObjectValue")
+ yield ContentTask.spawn(browser, null, function*() {
+ info("in cleanup function");
+ let win = content.document.defaultView;
+ info(`about to delete objectValue: ${win.objectValue}`);
+ delete win.objectValue;
+ removeMessageListener("Test:CheckObjectValue", win.checkObjectValueListener);
+ info(`about to delete checkObjectValueListener: ${win.checkObjectValueListener}`);
+ delete win.checkObjectValueListener;
+ info(`deleted objectValue (${win.objectValue}) and checkObjectValueListener (${win.checkObjectValueListener})`);
+ });
+ info("exiting cleanupObjectValue")
}
// See the notes for cacheObjectValue above.
function checkObjectValue(browser) {
let mm = browser.messageManager;
return new Promise((resolve, reject) => {
let listener = ({ data }) => {
- mm.removeMessageListener("Test:CheckObjectValue", listener);
+ mm.removeMessageListener("Test:CheckObjectValueResult", listener);
if (data.result === null) {
ok(false, "checkObjectValue threw an exception: " + data.exception);
reject(data.exception);
- }
- else {
+ } else {
resolve(data.result);
}
};
- mm.addMessageListener("Test:CheckObjectValue", listener);
+ mm.addMessageListener("Test:CheckObjectValueResult", listener);
mm.sendAsyncMessage("Test:CheckObjectValue");
});
}
add_task(function*() {
let embed = '<embed type="application/x-test" allowscriptaccess="always" allowfullscreen="true" wmode="window" width="640" height="480"></embed>'
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED);
@@ -94,68 +115,74 @@ add_task(function*() {
gBrowser.addTab("about:blank", {skipAnimation: true})
];
// Initially 0 1 2 3 4
yield loadURI(tabs[1], "data:text/html;charset=utf-8,<title>tab1</title><body>tab1<iframe>");
yield loadURI(tabs[2], "data:text/plain;charset=utf-8,tab2");
yield loadURI(tabs[3], "data:text/html;charset=utf-8,<title>tab3</title><body>tab3<iframe>");
yield loadURI(tabs[4], "data:text/html;charset=utf-8,<body onload='clicks=0' onclick='++clicks'>"+embed);
- gBrowser.selectedTab = tabs[3];
+ yield BrowserTestUtils.switchTab(gBrowser, tabs[3]);
swapTabsAndCloseOther(2, 3); // now: 0 1 2 4
is(gBrowser.tabs[1], tabs[1], "tab1");
is(gBrowser.tabs[2], tabs[3], "tab3");
is(gBrowser.tabs[3], tabs[4], "tab4");
+ delete tabs[2];
- cacheObjectValue(tabs[4].linkedBrowser);
+ info("about to cacheObjectValue")
+ yield cacheObjectValue(tabs[4].linkedBrowser);
+ info("just finished cacheObjectValue")
swapTabsAndCloseOther(3, 2); // now: 0 1 4
- gBrowser.selectedTab = gBrowser.tabs[2];
+ is(Array.prototype.indexOf.call(gBrowser.tabs, gBrowser.selectedTab), 2,
+ "The third tab should be selected");
+ delete tabs[4];
+
ok((yield checkObjectValue(gBrowser.tabs[2].linkedBrowser)), "same plugin instance");
is(gBrowser.tabs[1], tabs[1], "tab1");
is(gBrowser.tabs[2], tabs[3], "tab4");
let clicks = yield getClicks(gBrowser.tabs[2]);
is(clicks, 0, "no click on BODY so far");
yield clickTest(gBrowser.tabs[2]);
swapTabsAndCloseOther(2, 1); // now: 0 4
is(gBrowser.tabs[1], tabs[1], "tab1");
+ delete tabs[3];
ok((yield checkObjectValue(gBrowser.tabs[1].linkedBrowser)), "same plugin instance");
+ yield cleanupObjectValue(gBrowser.tabs[1].linkedBrowser);
yield clickTest(gBrowser.tabs[1]);
// Load a new document (about:blank) in tab4, then detach that tab into a new window.
// In the new window, navigate back to the original document and click on its <body>,
// verify that its onclick was called.
- gBrowser.selectedTab = tabs[1];
+ is(Array.prototype.indexOf.call(gBrowser.tabs, gBrowser.selectedTab), 1,
+ "The second tab should be selected");
+ is(gBrowser.tabs[1], tabs[1],
+ "The second tab in gBrowser.tabs should be equal to the second tab in our array");
+ is(gBrowser.selectedTab, tabs[1],
+ "The second tab in our array is the selected tab");
yield loadURI(tabs[1], "about:blank");
let key = tabs[1].linkedBrowser.permanentKey;
let win = gBrowser.replaceTabWithWindow(tabs[1]);
yield new Promise(resolve => whenDelayedStartupFinished(win, resolve));
+ delete tabs[1];
// Verify that the original window now only has the initial tab left in it.
is(gBrowser.tabs[0], tabs[0], "tab0");
is(gBrowser.tabs[0].linkedBrowser.currentURI.spec, "about:blank", "tab0 uri");
let tab = win.gBrowser.tabs[0];
is(tab.linkedBrowser.permanentKey, key, "Should have kept the key");
- let pageshowPromise = ContentTask.spawn(tab.linkedBrowser, {}, function*() {
- return new Promise(resolve => {
- let listener = function() {
- removeEventListener("pageshow", listener, false);
- resolve();
- }
- addEventListener("pageshow", listener, false);
- });
- });
+ let awaitPageShow = BrowserTestUtils.waitForContentEvent(tab.linkedBrowser, "pageshow");
win.gBrowser.goBack();
- yield pageshowPromise;
+ yield awaitPageShow;
yield clickTest(tab);
promiseWindowClosed(win);
});
--- a/build/moz.configure/checks.configure
+++ b/build/moz.configure/checks.configure
@@ -62,20 +62,20 @@ def check_prog(var, progs, allow_missing
if value:
progs[:] = value
for prog in progs:
result = find_program(prog)
if result:
return result
return not_found
- @depends(check)
+ @depends(check, var)
@advanced
- def postcheck(value):
- if value is not_found and not allow_missing:
+ def postcheck(value, raw_value):
+ if value is not_found and (not allow_missing or raw_value):
from mozbuild.shellutil import quote
error('Cannot find %s (tried: %s)'
% (var.lower(), ', '.join(quote(p) for p in progs)))
return None if value is not_found else value
@depends(postcheck)
def normalized_for_config(value):
return ':' if value is None else value
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -3,29 +3,28 @@
# 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/.
# yasm detection
# ==============================================================
yasm = check_prog('YASM', ['yasm'], allow_missing=True)
-@depends(yasm)
+@depends_if(yasm)
@checking('yasm version')
@advanced
def yasm_version(yasm):
- if yasm:
- import subprocess
- try:
- version = Version(subprocess.check_output(
- [yasm, '--version']
- ).splitlines()[0].split()[1])
- return version
- except subprocess.CalledProcessError as e:
- error('Failed to get yasm version: %s' % e.message)
+ import subprocess
+ try:
+ version = Version(subprocess.check_output(
+ [yasm, '--version']
+ ).splitlines()[0].split()[1])
+ return version
+ except subprocess.CalledProcessError as e:
+ error('Failed to get yasm version: %s' % e.message)
# Until we move all the yasm consumers out of old-configure.
# bug 1257904
add_old_configure_assignment('_YASM_MAJOR_VERSION',
delayed_getattr(yasm_version, 'major'))
add_old_configure_assignment('_YASM_MINOR_VERSION',
delayed_getattr(yasm_version, 'minor'))
--- a/build/moz.configure/util.configure
+++ b/build/moz.configure/util.configure
@@ -117,10 +117,29 @@ def namespace(**kwargs):
# def option(value)
# return namespace(foo=value)
# set_config('FOO', delayed_getattr(option, 'foo')
@template
def delayed_getattr(func, key):
@depends(func)
@advanced
def result(value):
- return getattr(value, key)
+ try:
+ return getattr(value, key)
+ except AttributeError:
+ # The @depends function we're being passed may have returned
+ # None, or an object that simply doesn't have the wanted key.
+ # In that case, just return None.
+ return None
return result
+
+
+# Like @depends, but the decorated function is only called if one of the
+# arguments it would be called with has a positive value (bool(value) is True)
+@template
+def depends_if(*args):
+ def decorator(func):
+ @depends(*args)
+ def wrapper(*args):
+ if any(arg for arg in args):
+ return func(*args)
+ return wrapper
+ return decorator
--- a/devtools/client/webconsole/test/browser.ini
+++ b/devtools/client/webconsole/test/browser.ini
@@ -310,16 +310,17 @@ skip-if = e10s # Bug 1042253 - webconsol
[browser_webconsole_js_input_expansion.js]
[browser_webconsole_jsterm.js]
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
[browser_webconsole_live_filtering_of_message_types.js]
[browser_webconsole_live_filtering_on_search_strings.js]
[browser_webconsole_message_node_id.js]
[browser_webconsole_multiline_input.js]
[browser_webconsole_netlogging.js]
+skip-if = e10s # Bug 1242234, bug 1242318
[browser_webconsole_netlogging_basic.js]
[browser_webconsole_netlogging_panel.js]
[browser_webconsole_netlogging_reset_filter.js]
[browser_webconsole_notifications.js]
[browser_webconsole_open-links-without-callback.js]
[browser_webconsole_promise.js]
[browser_webconsole_output_copy_newlines.js]
[browser_webconsole_output_order.js]
--- a/docshell/test/mochitest.ini
+++ b/docshell/test/mochitest.ini
@@ -49,17 +49,17 @@ skip-if = buildapp == 'mulet' || (builda
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_bug413310.html]
skip-if = true
# Disabled for too many intermittent failures (bug 719186)
[test_bug475636.html]
[test_bug509055.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_bug511449.html]
-skip-if = toolkit != "cocoa" || e10s
+skip-if = toolkit != "cocoa"
support-files = file_bug511449.html
[test_bug529119-1.html]
skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_bug529119-2.html]
skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(debug-only failure) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
[test_bug530396.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Timeouts on B2G desktop
support-files = bug530396-noref.sjs bug530396-subframe.html
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -1225,22 +1225,24 @@ Animation::GetPresContext() const
}
return mEffect->GetPresContext();
}
void
Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
{
+ CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+
if (aSyncNotifyFlag == SyncNotifyFlag::Sync) {
DoFinishNotificationImmediately();
} else if (!mFinishNotificationTask.IsPending()) {
RefPtr<nsRunnableMethod<Animation>> runnable =
NS_NewRunnableMethod(this, &Animation::DoFinishNotificationImmediately);
- Promise::DispatchToMicroTask(runnable);
+ runtime->DispatchToMicroTask(runnable);
mFinishNotificationTask = runnable;
}
}
void
Animation::ResetFinishedPromise()
{
mFinishedIsResolved = false;
--- a/dom/base/test/chrome/chrome.ini
+++ b/dom/base/test/chrome/chrome.ini
@@ -58,17 +58,16 @@ skip-if = buildapp == 'mulet'
[test_bug816340.xul]
[test_bug914381.html]
[test_bug990812.xul]
[test_bug1063837.xul]
[test_bug1139964.xul]
[test_bug1209621.xul]
[test_cpows.xul]
skip-if = buildapp == 'mulet'
-[test_document_register.xul]
[test_registerElement_content.xul]
[test_registerElement_ep.xul]
[test_domparsing.xul]
[test_fileconstructor.xul]
[test_fileconstructor_tempfile.xul]
[test_nsITextInputProcessor.xul]
[test_title.xul]
[test_windowroot.xul]
deleted file mode 100644
--- a/dom/base/test/chrome/test_document_register.xul
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
- type="text/css"?>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=783129
--->
-<window title="Mozilla Bug 549682"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
- <script type="application/javascript"
- src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-
- <!-- test results are displayed in the html:body -->
- <body xmlns="http://www.w3.org/1999/xhtml">
- <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129"
- target="_blank">Mozilla Bug 783129</a>
- <iframe onload="startTests()" id="fooframe" src="http://example.com"></iframe>
- </body>
-
- <!-- test code goes here -->
- <script type="application/javascript"><![CDATA[
-
- /** Test for Bug 783129 **/
- SimpleTest.waitForExplicitFinish();
-
- function startTests() {
- var c = $("fooframe").contentDocument.registerElement("x-foo");
- var elem = new c();
- is(elem.tagName, "X-FOO", "Constructor should create an x-foo element.");
-
- var anotherElem = $("fooframe").contentDocument.createElement("x-foo");
- is(anotherElem.tagName, "X-FOO", "createElement should create an x-foo element.");
- SimpleTest.finish();
- }
-
- ]]></script>
-</window>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -890,8 +890,9 @@ skip-if = buildapp == 'b2g' #no ssl supp
[test_document.all_iteration.html]
[test_bug1198095.html]
[test_bug1187157.html]
[test_bug769117.html]
[test_bug1250148.html]
[test_bug1240471.html]
[test_mozbrowser_apis_allowed.html]
[test_mozbrowser_apis_blocked.html]
+[test_document_register.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_document_register.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ </head>
+ <body onload="startTests()">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129" target="_blank">Mozilla Bug 783129</a>
+ <iframe id="fooframe" src="/"></iframe>
+ <script type="application/javascript">
+
+ /** Test for Bug 783129 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function startTests() {
+ var c = document.getElementById("fooframe").contentDocument.registerElement("x-foo");
+ var elem = new c();
+ is(elem.tagName, "X-FOO", "Constructor should create an x-foo element.");
+
+ var anotherElem = $("fooframe").contentDocument.createElement("x-foo");
+ is(anotherElem.tagName, "X-FOO", "createElement should create an x-foo element.");
+ SimpleTest.finish();
+ }
+
+ </script>
+ </body>
+</html>
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -469,24 +469,54 @@ ShaderValidator::FindUniformByMappedName
const sh::ShaderVariable* found;
if (!itr->findInfoByMappedName(mappedName, &found, out_userName))
continue;
*out_isArray = found->isArray();
return true;
}
+ const size_t dotPos = mappedName.find(".");
+
const std::vector<sh::InterfaceBlock>& interfaces = *ShGetInterfaceBlocks(mHandle);
for (const auto& interface : interfaces) {
+
+ std::string mappedFieldName;
+ const bool hasInstanceName = !interface.instanceName.empty();
+
+ // If the InterfaceBlock has an instanceName, all variables defined
+ // within the block are qualified with the block name, as opposed
+ // to being placed in the global scope.
+ if (hasInstanceName) {
+
+ // If mappedName has no block name prefix, skip
+ if (std::string::npos == dotPos)
+ continue;
+
+ // If mappedName has a block name prefix that doesn't match, skip
+ const std::string mappedInterfaceBlockName = mappedName.substr(0, dotPos);
+ if (interface.mappedName != mappedInterfaceBlockName)
+ continue;
+
+ mappedFieldName = mappedName.substr(dotPos + 1);
+ } else {
+ mappedFieldName = mappedName;
+ }
+
for (const auto& field : interface.fields) {
const sh::ShaderVariable* found;
- if (!field.findInfoByMappedName(mappedName, &found, out_userName))
+ if (!field.findInfoByMappedName(mappedFieldName, &found, out_userName))
continue;
+ if (hasInstanceName) {
+ // Prepend the user name of the interface that matched
+ *out_userName = interface.name + "." + *out_userName;
+ }
+
*out_isArray = found->isArray();
return true;
}
}
return false;
}
--- a/dom/encoding/TextEncoder.cpp
+++ b/dom/encoding/TextEncoder.cpp
@@ -8,38 +8,20 @@
#include "mozilla/dom/EncodingUtils.h"
#include "mozilla/UniquePtrExtensions.h"
#include "nsContentUtils.h"
namespace mozilla {
namespace dom {
void
-TextEncoder::Init(const nsAString& aEncoding, ErrorResult& aRv)
+TextEncoder::Init()
{
- nsAutoString label(aEncoding);
- EncodingUtils::TrimSpaceCharacters(label);
-
- // Let encoding be the result of getting an encoding from label.
- // If encoding is failure, or is none of utf-8, utf-16, and utf-16be,
- // throw a RangeError (https://encoding.spec.whatwg.org/#dom-textencoder).
- if (!EncodingUtils::FindEncodingForLabel(label, mEncoding)) {
- aRv.ThrowRangeError<MSG_ENCODING_NOT_SUPPORTED>(label);
- return;
- }
-
- if (!mEncoding.EqualsLiteral("UTF-8") &&
- !mEncoding.EqualsLiteral("UTF-16LE") &&
- !mEncoding.EqualsLiteral("UTF-16BE")) {
- aRv.ThrowRangeError<MSG_DOM_ENCODING_NOT_UTF>();
- return;
- }
-
- // Create an encoder object for mEncoding.
- mEncoder = EncodingUtils::EncoderForEncoding(mEncoding);
+ // Create an encoder object for utf-8.
+ mEncoder = EncodingUtils::EncoderForEncoding(NS_LITERAL_CSTRING("UTF-8"));
}
void
TextEncoder::Encode(JSContext* aCx,
JS::Handle<JSObject*> aObj,
const nsAString& aString,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv)
@@ -87,14 +69,13 @@ TextEncoder::Encode(JSContext* aCx,
aRv.Throw(rv);
}
aRetval.set(outView);
}
void
TextEncoder::GetEncoding(nsAString& aEncoding)
{
- CopyASCIItoUTF16(mEncoding, aEncoding);
- nsContentUtils::ASCIIToLower(aEncoding);
+ aEncoding.AssignLiteral("utf-8");
}
} // namespace dom
} // namespace mozilla
--- a/dom/encoding/TextEncoder.h
+++ b/dom/encoding/TextEncoder.h
@@ -19,24 +19,20 @@ namespace dom {
class TextEncoder final : public NonRefcountedDOMObject
{
public:
// The WebIDL constructor.
static TextEncoder*
Constructor(const GlobalObject& aGlobal,
- const nsAString& aEncoding,
ErrorResult& aRv)
{
nsAutoPtr<TextEncoder> txtEncoder(new TextEncoder());
- txtEncoder->Init(aEncoding, aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
+ txtEncoder->Init();
return txtEncoder.forget();
}
TextEncoder()
{
}
virtual
@@ -45,50 +41,40 @@ public:
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector)
{
return TextEncoderBinding::Wrap(aCx, this, aGivenProto, aReflector);
}
protected:
- /**
- * Validates provided encoding and throws an exception if invalid encoding.
- * If no encoding is provided then mEncoding is default initialised to "utf-8".
- *
- * @param aEncoding Optional encoding (case insensitive) provided.
- * (valid values are "utf-8", "utf-16", "utf-16be")
- * Default value is "utf-8" if no encoding is provided.
- * @return aRv EncodingError exception else null.
- */
- void Init(const nsAString& aEncoding, ErrorResult& aRv);
+ void Init();
public:
/**
* Return the encoding name.
*
* @param aEncoding, current encoding.
*/
void GetEncoding(nsAString& aEncoding);
/**
- * Encodes incoming utf-16 code units/ DOM string to the requested encoding.
+ * Encodes incoming utf-16 code units/ DOM string to utf-8.
*
* @param aCx Javascript context.
* @param aObj the wrapper of the TextEncoder
* @param aString utf-16 code units to be encoded.
* @return JSObject* The Uint8Array wrapped in a JS object. Returned via
* the aRetval out param.
*/
void Encode(JSContext* aCx,
JS::Handle<JSObject*> aObj,
const nsAString& aString,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv);
private:
- nsCString mEncoding;
nsCOMPtr<nsIUnicodeEncoder> mEncoder;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_textencoder_h_
--- a/dom/encoding/test/test_TextEncoder.js
+++ b/dom/encoding/test/test_TextEncoder.js
@@ -1,15 +1,24 @@
/*
* test_TextEncoder.js
* bug 764234 tests
*/
function runTextEncoderTests()
{
+ test(testEncoderEncode, "testEncoderEncode");
+ test(testEncoderGetEncoding, "testEncoderGetEncoding");
+ test(testInvalidSequence, "testInvalidSequence");
+ test(testInputString, "testInputString");
+ test(testStreamingOptions, "testStreamingOptions");
+}
+
+function testEncoderEncode()
+{
var data = "\u00a0\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09"
+ "\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14"
+ "\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f"
+ "\u0e20\u0e21\u0e22\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a"
+ "\u0e2b\u0e2c\u0e2d\u0e2e\u0e2f\u0e30\u0e31\u0e32\u0e33\u0e34\u0e35"
+ "\u0e36\u0e37\u0e38\u0e39\u0e3a\u0e3f\u0e40\u0e41\u0e42\u0e43\u0e44"
+ "\u0e45\u0e46\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0e4f"
+ "\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0e5a"
@@ -41,118 +50,49 @@ function runTextEncoderTests()
0xB9, 0x89, 0xE0, 0xB9, 0x8A, 0xE0, 0xB9, 0x8B, 0xE0,
0xB9, 0x8C, 0xE0, 0xB9, 0x8D, 0xE0, 0xB9, 0x8E, 0xE0,
0xB9, 0x8F, 0xE0, 0xB9, 0x90, 0xE0, 0xB9, 0x91, 0xE0,
0xB9, 0x92, 0xE0, 0xB9, 0x93, 0xE0, 0xB9, 0x94, 0xE0,
0xB9, 0x95, 0xE0, 0xB9, 0x96, 0xE0, 0xB9, 0x97, 0xE0,
0xB9, 0x98, 0xE0, 0xB9, 0x99, 0xE0, 0xB9, 0x9A, 0xE0,
0xB9, 0x9B];
- test(testEncoderGetEncoding, "testEncoderGetEncoding");
- test(testInvalidSequence, "testInvalidSequence");
- test(testEncodeUTF16ToUTF16, "testEncodeUTF16ToUTF16");
- test(function() {
- testConstructorEncodingOption(data, expectedString)
- }, "testConstructorEncodingOption");
- test(function() {
- testEncodingValues(data, expectedString)
- }, "testEncodingValues");
- test(function() {
- testInputString(data, expectedString)
- }, "testInputString");
- test(testStreamingOptions, "testStreamingOptions");
+ // valid encoding passed
+ testSingleString({input: data, expected: expectedString,
+ msg: "testing encoding with valid utf-8 encoding."});
}
function testInvalidSequence()
{
var data = "\u0e43\u0e44\ufffd\u0e45";
var expectedString = [0xE0, 0xB9, 0x83, 0xE0, 0xB9, 0x84, 0xEF, 0xBF, 0xBD,
0xE0, 0xB9, 0x85];
//Test null input string
- testSingleString({encoding: "utf-8", input: data, expected: expectedString,
+ testSingleString({input: data, expected: expectedString,
msg: "encoder with replacement character test."});
}
-function testEncodeUTF16ToUTF16()
-{
- var data = "\u0e43\u0e44\u0e45\u0e46\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c"
- + "\u0e4d\u0e4e\u0e4f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56"
- + "\u0e57\u0e58\u0e59\u0e5a\u0e5b";
-
- var expected = [0x43, 0x0E, 0x44, 0x0E, 0x45, 0x0E, 0x46, 0x0E, 0x47, 0x0E,
- 0x48, 0x0E, 0x49, 0x0E, 0x4A, 0x0E, 0x4B, 0x0E, 0x4C, 0x0E,
- 0x4D, 0x0E, 0x4E, 0x0E, 0x4F, 0x0E, 0x50, 0x0E, 0x51, 0x0E,
- 0x52, 0x0E, 0x53, 0x0E, 0x54, 0x0E, 0x55, 0x0E, 0x56, 0x0E,
- 0x57, 0x0E, 0x58, 0x0E, 0x59, 0x0E, 0x5A, 0x0E, 0x5B, 0x0E];
-
- testSingleString({encoding: "Utf-16", input: data, expected: expected,
- msg: "testing encoding from utf-16 to utf-16 zero."});
-}
-
-function testConstructorEncodingOption(aData, aExpectedString)
-{
- function errorMessage(encoding) {
- return `The given encoding '${String(encoding).trim()}' is not supported.`;
- }
-
- // valid encoding passed
- var encoding = "UTF-8";
- testSingleString({encoding: encoding, input: aData, expected: aExpectedString,
- msg: "testing encoding with valid utf-8 encoding."});
-
- // passing spaces for encoding
- encoding = " ";
- testSingleString({encoding: encoding, input: aData, error: "RangeError",
- errorMessage: errorMessage(encoding),
- msg: "constructor encoding, spaces encoding test."});
-
- // invalid encoding passed
- encoding = "asdfasdf";
- testSingleString({encoding: encoding, input: aData, error: "RangeError",
- errorMessage: errorMessage(encoding),
- msg: "constructor encoding, invalid encoding test."});
-
- // null encoding passed
- encoding = null;
- testSingleString({encoding: encoding, input: aData, error: "RangeError",
- errorMessage: errorMessage(encoding),
- msg: "constructor encoding, \"null\" encoding test."});
-
- // empty encoding passed
- encoding = "";
- testSingleString({encoding: encoding, input: aData, error: "RangeError",
- errorMessage: errorMessage(encoding),
- msg: "constructor encoding, empty encoding test."});
-}
-
-function testEncodingValues(aData, aExpectedString)
-{
- var encoding = "ISO-8859-11";
- testSingleString({encoding: aData, input: encoding, error: "RangeError",
- msg: "encoder encoding values test."});
-}
-
-function testInputString(aData, aExpectedString)
+function testInputString()
{
//Test null input string
- testSingleString({encoding: "utf-8", input: "", expected: [],
+ testSingleString({input: "", expected: [],
msg: "encoder null input string test."});
//Test spaces as input string
- testSingleString({encoding: "utf-8", input: " ", expected: [32, 32],
+ testSingleString({input: " ", expected: [32, 32],
msg: "spaces as input string."});
}
function testSingleString(test)
{
var outText;
try {
var stream = test.stream ? {stream: true} : null;
- outText = (new TextEncoder(test.encoding)).encode(test.input, stream);
+ outText = (new TextEncoder()).encode(test.input, stream);
} catch (e) {
assert_equals(e.name, test.error, test.msg + " error thrown from the constructor.");
if (test.errorMessage) {
assert_equals(e.message, test.errorMessage, test.msg + " error thrown from the constructor.");
}
return;
}
assert_true(!test.error, test.msg);
@@ -218,47 +158,43 @@ function testStreamingOptions()
0xE0, 0xB9, 0x89, 0xE0, 0xB9, 0x8A, 0xE0, 0xB9, 0x8B,
0xE0, 0xB9, 0x8C, 0xE0, 0xB9, 0x8D, 0xE0, 0xB9, 0x8E,
0xE0, 0xB9, 0x8F, 0xE0, 0xB9, 0x90, 0xE0, 0xB9, 0x91,
0xE0, 0xB9, 0x92, 0xE0, 0xB9, 0x93, 0xE0, 0xB9, 0x94,
0xE0, 0xB9, 0x95, 0xE0, 0xB9, 0x96, 0xE0, 0xB9, 0x97,
0xE0, 0xB9, 0x98, 0xE0, 0xB9, 0x99, 0xE0, 0xB9, 0x9A,
0xE0, 0xB9, 0x9B]];
- var expectedUTF16 = data.map(function(d) {
- return new Uint8Array(new Uint16Array(arrayFromString(d)).buffer);
- });
-
// STREAMING TEST ONE: test streaming three valid strings with stream option
// set to true for all three.
- testArrayOfStrings({encoding: "utf-8", array: [
+ testArrayOfStrings({array: [
{input: data[0], stream: true, expected: expected[0]},
{input: data[1], stream: true, expected: expected[1]},
{input: data[2], stream: true, expected: expected[2]},
], msg: "streaming test one."});
// STREAMING TEST TWO: test streaming valid strings with stream option
// streaming option: false from constructor, string 1 stream: true,
// string 2 stream: false, string 3 stream: false
- testArrayOfStrings({encoding: "utf-16", array: [
- {input: data[0], stream: true, expected: expectedUTF16[0]},
- {input: data[1], expected: expectedUTF16[1]},
- {input: data[2], expected: expectedUTF16[2]},
+ testArrayOfStrings({array: [
+ {input: data[0], stream: true, expected: expected[0]},
+ {input: data[1], expected: expected[1]},
+ {input: data[2], expected: expected[2]},
], msg: "streaming test two."});
}
function arrayFromString(s) {
return s.split('').map(function(c){return String.charCodeAt(c)});
}
function testArrayOfStrings(test)
{
var encoder;
try {
- encoder = new TextEncoder(test.encoding);
+ encoder = new TextEncoder();
} catch (e) {
assert_equals(e.name, test.error, test.msg);
return;
}
assert_true(!test.error, test.msg);
var array = test.array;
for (var i = 0; i < array.length; i += 1) {
@@ -273,21 +209,11 @@ function testArrayOfStrings(test)
return;
}
}
}
}
function testEncoderGetEncoding()
{
- var labelEncodings = [
- {encoding: "utf-8", labels: ["unicode-1-1-utf-8", "utf-8", "utf8"]},
- {encoding: "utf-16le", labels: ["utf-16", "utf-16"]},
- {encoding: "utf-16be", labels: ["utf-16be"]},
- ];
-
- for (var le of labelEncodings) {
- for (var label of le.labels) {
- var encoder = new TextEncoder(label);
- assert_equals(encoder.encoding, le.encoding, label + " label encoding test.");
- }
- }
+ var encoder = new TextEncoder();
+ assert_equals(encoder.encoding, "utf-8", "TextEncoder encoding test.");
}
--- a/dom/encoding/test/unit/test_misc.js
+++ b/dom/encoding/test/unit/test_misc.js
@@ -128,17 +128,27 @@ test(
},
"Encoding names"
);
test(
function () {
["utf-8", "utf-16le", "utf-16be"].forEach(function (encoding) {
var string = "\x00123ABCabc\x80\xFF\u0100\u1000\uFFFD\uD800\uDC00\uDBFF\uDFFF";
- var encoded = new TextEncoder(encoding).encode(string);
+ var octets = {
+ "utf-16le": [0x00,0x00,0x31,0x00,0x32,0x00,0x33,0x00,0x41,0x00,0x42,0x00,
+ 0x43,0x00,0x61,0x00,0x62,0x00,0x63,0x00,0x80,0x00,0xFF,0x00,
+ 0x00,0x01,0x00,0x10,0xFD,0xFF,0x00,0xD8,0x00,0xDC,0xFF,0xDB,
+ 0xFF,0xDF],
+ "utf-16be": [0x00,0x00,0x00,0x31,0x00,0x32,0x00,0x33,0x00,0x41,0x00,0x42,
+ 0x00,0x43,0x00,0x61,0x00,0x62,0x00,0x63,0x00,0x80,0x00,0xFF,
+ 0x01,0x00,0x10,0x00,0xFF,0xFD,0xD8,0x00,0xDC,0x00,0xDB,0xFF,
+ 0xDF,0xFF]
+ };
+ var encoded = octets[encoding] || new TextEncoder(encoding).encode(string);
for (var len = 1; len <= 5; ++len) {
var out = "", decoder = new TextDecoder(encoding);
for (var i = 0; i < encoded.length; i += len) {
var sub = [];
for (var j = i; j < encoded.length && j < i + len; ++j) {
sub.push(encoded[j]);
}
@@ -199,24 +209,17 @@ test(
new TextDecoder("utf-16be").decode(new Uint8Array([0x00]));
},
"Non-fatal errors at EOF"
);
test(
function () {
- var utf_encodings = ["utf-8", "utf-16le", "utf-16be"];
+ var encodings = ["utf-8", "ibm866", "iso-8859-2", "iso-8859-3", "iso-8859-4", "iso-8859-5", "iso-8859-6", "iso-8859-7", "iso-8859-8", "iso-8859-8-i", "iso-8859-10", "iso-8859-13", "iso-8859-14", "iso-8859-15", "iso-8859-16", "koi8-r", "koi8-u", "macintosh", "windows-874", "windows-1250", "windows-1251", "windows-1252", "windows-1253", "windows-1254", "windows-1255", "windows-1256", "windows-1257", "windows-1258", "x-mac-cyrillic", "gbk", "gb18030", "big5", "euc-jp", "iso-2022-jp", "shift_jis", "euc-kr", "x-user-defined", "utf-16le", "utf-16be"];
- var legacy_encodings = ["ibm866", "iso-8859-2", "iso-8859-3", "iso-8859-4", "iso-8859-5", "iso-8859-6", "iso-8859-7", "iso-8859-8", "iso-8859-8-i", "iso-8859-10", "iso-8859-13", "iso-8859-14", "iso-8859-15", "iso-8859-16", "koi8-r", "koi8-u", "macintosh", "windows-874", "windows-1250", "windows-1251", "windows-1252", "windows-1253", "windows-1254", "windows-1255", "windows-1256", "windows-1257", "windows-1258", "x-mac-cyrillic", "gbk", "gb18030", "big5", "euc-jp", "iso-2022-jp", "shift_jis", "euc-kr", "x-user-defined"];
-
- utf_encodings.forEach(function(encoding) {
+ encodings.forEach(function(encoding) {
assert_equals(new TextDecoder(encoding).encoding, encoding);
- assert_equals(new TextEncoder(encoding).encoding, encoding);
- });
-
- legacy_encodings.forEach(function(encoding) {
- assert_equals(new TextDecoder(encoding).encoding, encoding);
- assert_throws({name: 'RangeError'}, function() { new TextEncoder(encoding); });
+ assert_equals(new TextEncoder(encoding).encoding, "utf-8");
});
},
- "Non-UTF encodings supported only for decode, not encode"
+ "Non-UTF-8 encodings supported only for decode, not encode"
);
--- a/dom/encoding/test/unit/test_utf.js
+++ b/dom/encoding/test/unit/test_utf.js
@@ -37,16 +37,38 @@ function encode_utf8(string) {
var utf8 = unescape(encodeURIComponent(string));
var octets = new Uint8Array(utf8.length), i;
for (i = 0; i < utf8.length; i += 1) {
octets[i] = utf8.charCodeAt(i);
}
return octets;
}
+function encode_utf16le(string) {
+ var octets = new Uint8Array(string.length * 2);
+ var di = 0;
+ for (var i = 0; i < string.length; i++) {
+ var code = string.charCodeAt(i);
+ octets[di++] = code & 0xFF;
+ octets[di++] = code >> 8;
+ }
+ return octets;
+}
+
+function encode_utf16be(string) {
+ var octets = new Uint8Array(string.length * 2);
+ var di = 0;
+ for (var i = 0; i < string.length; i++) {
+ var code = string.charCodeAt(i);
+ octets[di++] = code >> 8;
+ octets[di++] = code & 0xFF;
+ }
+ return octets;
+}
+
function decode_utf8(octets) {
var utf8 = String.fromCharCode.apply(null, octets);
return decodeURIComponent(escape(utf8));
}
// Helpers for test_utf_roundtrip.
function cpname(n) {
if (n+0 !== n)
@@ -89,35 +111,33 @@ function genblock(from, len) {
function test_utf_roundtrip () {
var MIN_CODEPOINT = 0;
var MAX_CODEPOINT = 0x10FFFF;
var BLOCK_SIZE = 0x1000;
var block, block_tag, i, j, encoded, decoded, exp_encoded, exp_decoded;
- var TE_U16LE = new TextEncoder("UTF-16LE");
var TD_U16LE = new TextDecoder("UTF-16LE");
- var TE_U16BE = new TextEncoder("UTF-16BE");
var TD_U16BE = new TextDecoder("UTF-16BE");
- var TE_U8 = new TextEncoder("UTF-8");
+ var TE_U8 = new TextEncoder();
var TD_U8 = new TextDecoder("UTF-8");
for (i = MIN_CODEPOINT; i < MAX_CODEPOINT; i += BLOCK_SIZE) {
block_tag = cpname(i) + " - " + cpname(i + BLOCK_SIZE - 1);
block = genblock(i, BLOCK_SIZE);
// test UTF-16LE, UTF-16BE, and UTF-8 encodings against themselves
- encoded = TE_U16LE.encode(block);
+ encoded = encode_utf16le(block);
decoded = TD_U16LE.decode(encoded);
assert_string_equals(block, decoded, "UTF-16LE round trip " + block_tag);
- encoded = TE_U16BE.encode(block);
+ encoded = encode_utf16be(block);
decoded = TD_U16BE.decode(encoded);
assert_string_equals(block, decoded, "UTF-16BE round trip " + block_tag);
encoded = TE_U8.encode(block);
decoded = TD_U8.decode(encoded);
assert_string_equals(block, decoded, "UTF-8 round trip " + block_tag);
// test TextEncoder(UTF-8) against the older idiom
@@ -140,22 +160,22 @@ function test_utf_samples () {
{ encoding: "utf-16le",
expected: [0x7A, 0x00, 0xA2, 0x00, 0x34, 0x6C, 0x34, 0xD8, 0x1E, 0xDD, 0xFF, 0xDB, 0xFD, 0xDF] },
{ encoding: "utf-16",
expected: [0x7A, 0x00, 0xA2, 0x00, 0x34, 0x6C, 0x34, 0xD8, 0x1E, 0xDD, 0xFF, 0xDB, 0xFD, 0xDF] },
{ encoding: "utf-16be",
expected: [0x00, 0x7A, 0x00, 0xA2, 0x6C, 0x34, 0xD8, 0x34, 0xDD, 0x1E, 0xDB, 0xFF, 0xDF, 0xFD] }
];
+ var encoded = new TextEncoder().encode(sample);
+ assert_array_equals(encoded, cases[0].expected,
+ "expected equal encodings");
+
cases.forEach(
function(t) {
- var encoded = new TextEncoder(t.encoding).encode(sample);
- assert_array_equals(encoded, t.expected,
- "expected equal encodings - " + t.encoding);
-
var decoded = new TextDecoder(t.encoding)
.decode(new Uint8Array(t.expected));
assert_equals(decoded, sample,
"expected equal decodings - " + t.encoding);
});
}
test(test_utf_samples,
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -1727,17 +1727,17 @@ HTMLFormElement::GetActionURL(nsIURI** a
// form-action is only enforced if explicitly defined in the
// policy - do *not* consult default-src, see:
// http://www.w3.org/TR/CSP2/#directive-default-src
rv = csp->Permits(actionURL, nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE,
true, &permitsFormAction);
NS_ENSURE_SUCCESS(rv, rv);
if (!permitsFormAction) {
- rv = NS_ERROR_CSP_FORM_ACTION_VIOLATION;
+ return NS_ERROR_CSP_FORM_ACTION_VIOLATION;
}
}
// Potentially the page uses the CSP directive 'upgrade-insecure-requests'. In
// such a case we have to upgrade the action url from http:// to https://.
// If the actionURL is not http, then there is nothing to do.
bool isHttpScheme = false;
rv = actionURL->SchemeIs("http", &isHttpScheme);
--- a/dom/html/HTMLSummaryElement.cpp
+++ b/dom/html/HTMLSummaryElement.cpp
@@ -57,23 +57,21 @@ HTMLSummaryElement::PostHandleEvent(Even
if (mouseEvent->IsLeftClickEvent()) {
RefPtr<HTMLDetailsElement> details = GetDetails();
MOZ_ASSERT(details,
"Expected to find details since this is the main summary!");
// When dispatching a synthesized mouse click event to a details element
// with 'display: none', both Chrome and Safari do not toggle the 'open'
- // attribute. We follow them by checking whether the details element has a
- // frame or not.
- if (details->GetPrimaryFrame(Flush_Frames)) {
- details->ToggleOpen();
- aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
- return NS_OK;
- }
+ // attribute. We had tried to be compatible with this behavior, but found
+ // more inconsistency in test cases in bug 1245424. So we stop doing that.
+ details->ToggleOpen();
+ aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
+ return NS_OK;
}
} // event->HasMouseEventMessage()
if (event->HasKeyEventMessage()) {
WidgetKeyboardEvent* keyboardEvent = event->AsKeyboardEvent();
bool dispatchClick = false;
switch (event->mMessage) {
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -929,33 +929,32 @@ nsHTMLDocument::SetDomain(const nsAStrin
// Create new URI
nsCOMPtr<nsIURI> uri = GetDomainURI();
if (!uri) {
rv.Throw(NS_ERROR_FAILURE);
return;
}
- nsAutoCString newURIString;
- if (NS_FAILED(uri->GetScheme(newURIString))) {
- rv.Throw(NS_ERROR_FAILURE);
+ nsCOMPtr<nsIURI> newURI;
+ nsresult rv2 = uri->Clone(getter_AddRefs(newURI));
+ if (NS_FAILED(rv2)) {
+ rv.Throw(rv2);
return;
}
- nsAutoCString path;
- if (NS_FAILED(uri->GetPath(path))) {
- rv.Throw(NS_ERROR_FAILURE);
+
+ rv2 = newURI->SetUserPass(EmptyCString());
+ if (NS_FAILED(rv2)) {
+ rv.Throw(rv2);
return;
}
- newURIString.AppendLiteral("://");
- AppendUTF16toUTF8(aDomain, newURIString);
- newURIString.Append(path);
-
- nsCOMPtr<nsIURI> newURI;
- if (NS_FAILED(NS_NewURI(getter_AddRefs(newURI), newURIString))) {
- rv.Throw(NS_ERROR_FAILURE);
+
+ rv2 = newURI->SetHostPort(NS_ConvertUTF16toUTF8(aDomain));
+ if (NS_FAILED(rv2)) {
+ rv.Throw(rv2);
return;
}
// Check new domain - must be a superdomain of the current host
// For example, a page from foo.bar.com may set domain to bar.com,
// but not to ar.com, baz.com, or fi.foo.bar.com.
nsAutoCString current, domain;
if (NS_FAILED(uri->GetAsciiHost(current)))
@@ -984,16 +983,17 @@ nsHTMLDocument::SetDomain(const nsAStrin
ok = ok && domain.Length() >= currentBaseDomain.Length();
}
if (!ok) {
// Error: illegal domain
rv.Throw(NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN);
return;
}
+ NS_TryToSetImmutable(newURI);
rv = NodePrincipal()->SetDomain(newURI);
}
nsGenericHTMLElement*
nsHTMLDocument::GetBody()
{
Element* html = GetHtmlElement();
if (!html) {
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/MP3Demuxer.cpp
@@ -114,17 +114,17 @@ MP3TrackDemuxer::MP3TrackDemuxer(MediaRe
Reset();
}
bool
MP3TrackDemuxer::Init() {
Reset();
FastSeek(TimeUnit());
// Read the first frame to fetch sample rate and other meta data.
- RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame()));
+ RefPtr<MediaRawData> frame(GetNextFrame(FindFirstFrame()));
MP3LOG("Init StreamLength()=%" PRId64 " first-frame-found=%d",
StreamLength(), !!frame);
if (!frame) {
return false;
}
@@ -368,16 +368,63 @@ MP3TrackDemuxer::Duration(int64_t aNumFr
if (!mSamplesPerSecond) {
return TimeUnit::FromMicroseconds(-1);
}
const double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond;
return TimeUnit::FromMicroseconds(aNumFrames * usPerFrame);
}
+MediaByteRange
+MP3TrackDemuxer::FindFirstFrame() {
+ static const int MIN_SUCCESSIVE_FRAMES = 4;
+
+ MediaByteRange candidateFrame = FindNextFrame();
+ int numSuccFrames = candidateFrame.Length() > 0;
+ MediaByteRange currentFrame = candidateFrame;
+ MP3LOGV("FindFirst() first candidate frame: mOffset=%" PRIu64 " Length()=%" PRIu64,
+ candidateFrame.mStart, candidateFrame.Length());
+
+ while (candidateFrame.Length() && numSuccFrames < MIN_SUCCESSIVE_FRAMES) {
+ mParser.EndFrameSession();
+ mOffset = currentFrame.mEnd;
+ const MediaByteRange prevFrame = currentFrame;
+
+ // FindNextFrame() here will only return frames consistent with our candidate frame.
+ currentFrame = FindNextFrame();
+ numSuccFrames += currentFrame.Length() > 0;
+ // Multiple successive false positives, which wouldn't be caught by the consistency
+ // checks alone, can be detected by wrong alignment (non-zero gap between frames).
+ const int64_t frameSeparation = currentFrame.mStart - prevFrame.mEnd;
+
+ if (!currentFrame.Length() || frameSeparation != 0) {
+ MP3LOGV("FindFirst() not enough successive frames detected, "
+ "rejecting candidate frame: successiveFrames=%d, last Length()=%" PRIu64
+ ", last frameSeparation=%" PRId64, numSuccFrames, currentFrame.Length(),
+ frameSeparation);
+
+ mParser.ResetFrameData();
+ mOffset = candidateFrame.mStart + 1;
+ candidateFrame = FindNextFrame();
+ numSuccFrames = candidateFrame.Length() > 0;
+ currentFrame = candidateFrame;
+ MP3LOGV("FindFirst() new candidate frame: mOffset=%" PRIu64 " Length()=%" PRIu64,
+ candidateFrame.mStart, candidateFrame.Length());
+ }
+ }
+
+ if (numSuccFrames >= MIN_SUCCESSIVE_FRAMES) {
+ MP3LOG("FindFirst() accepting candidate frame: "
+ "successiveFrames=%d", numSuccFrames);
+ } else {
+ MP3LOG("FindFirst() no suitable first frame found");
+ }
+ return candidateFrame;
+}
+
static bool
VerifyFrameConsistency(
const FrameParser::Frame& aFrame1, const FrameParser::Frame& aFrame2) {
const auto& h1 = aFrame1.Header();
const auto& h2 = aFrame2.Header();
return h1.IsValid() && h2.IsValid() &&
h1.Layer() == h2.Layer() &&
@@ -649,16 +696,23 @@ FrameParser::FrameParser()
void
FrameParser::Reset() {
mID3Parser.Reset();
mFrame.Reset();
}
void
+FrameParser::ResetFrameData() {
+ mFrame.Reset();
+ mFirstFrame.Reset();
+ mPrevFrame.Reset();
+}
+
+void
FrameParser::EndFrameSession() {
if (!mID3Parser.Header().IsValid()) {
// Reset ID3 tags only if we have not parsed a valid ID3 header yet.
mID3Parser.Reset();
}
mPrevFrame = mFrame;
mFrame.Reset();
}
--- a/dom/media/MP3Demuxer.h
+++ b/dom/media/MP3Demuxer.h
@@ -304,19 +304,23 @@ public:
const Frame& FirstFrame() const;
// Returns the parsed ID3 header. Note: check for validity.
const ID3Parser::ID3Header& ID3Header() const;
// Returns the parsed VBR header info. Note: check for validity by type.
const VBRHeader& VBRInfo() const;
- // Resets the parser. Don't use between frames as first frame data is reset.
+ // Resets the parser.
void Reset();
+ // Resets all frame data, but not the ID3Header.
+ // Don't use between frames as first frame data is reset.
+ void ResetFrameData();
+
// Clear the last parsed frame to allow for next frame parsing, i.e.:
// - sets PrevFrame to CurrentFrame
// - resets the CurrentFrame
// - resets ID3Header if no valid header was parsed yet
void EndFrameSession();
// Parses contents of given ByteReader for a valid frame header and returns true
// if one was found. After returning, the variable passed to 'aBytesToSkip' holds
@@ -389,17 +393,22 @@ private:
~MP3TrackDemuxer() {}
// Fast approximate seeking to given time.
media::TimeUnit FastSeek(const media::TimeUnit& aTime);
// Seeks by scanning the stream up to the given time for more accurate results.
media::TimeUnit ScanUntil(const media::TimeUnit& aTime);
- // Finds the next valid frame and returns its byte range.
+ // Finds the first valid frame and returns its byte range if found
+ // or a null-byte range otherwise.
+ MediaByteRange FindFirstFrame();
+
+ // Finds the next valid frame and returns its byte range if found
+ // or a null-byte range otherwise.
MediaByteRange FindNextFrame();
// Skips the next frame given the provided byte range.
bool SkipNextFrame(const MediaByteRange& aRange);
// Returns the next MPEG frame, if available.
already_AddRefed<MediaRawData> GetNextFrame(const MediaByteRange& aRange);
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -176,16 +176,17 @@ public:
void NoteSeek(MediaCacheStream* aStream, int64_t aOldOffset);
// Notify the cache that a block has been read from. This is used
// to update last-use times. The block may not actually have a
// cache entry yet since Read can read data from a stream's
// in-memory mPartialBlockBuffer while the block is only partly full,
// and thus hasn't yet been committed to the cache. The caller will
// call QueueUpdate().
void NoteBlockUsage(MediaCacheStream* aStream, int32_t aBlockIndex,
+ int64_t aStreamOffset,
MediaCacheStream::ReadMode aMode, TimeStamp aNow);
// Mark aStream as having the block, adding it as an owner.
void AddBlockOwnerAsReadahead(int32_t aBlockIndex, MediaCacheStream* aStream,
int32_t aStreamBlockIndex);
// This queues a call to Update() on the main thread.
void QueueUpdate();
@@ -1617,35 +1618,35 @@ MediaCache::Truncate()
// to have a cross-platform API for doing that. At least when all
// streams are closed we shut down the cache, which erases the
// file at that point.
}
}
void
MediaCache::NoteBlockUsage(MediaCacheStream* aStream, int32_t aBlockIndex,
- MediaCacheStream::ReadMode aMode,
- TimeStamp aNow)
+ int64_t aStreamOffset,
+ MediaCacheStream::ReadMode aMode, TimeStamp aNow)
{
mReentrantMonitor.AssertCurrentThreadIn();
if (aBlockIndex < 0) {
// this block is not in the cache yet
return;
}
BlockOwner* bo = GetBlockOwner(aBlockIndex, aStream);
if (!bo) {
// this block is not in the cache yet
return;
}
// The following check has to be <= because the stream offset has
// not yet been updated for the data read from this block
- NS_ASSERTION(bo->mStreamBlock*BLOCK_SIZE <= bo->mStream->mStreamOffset,
+ NS_ASSERTION(bo->mStreamBlock*BLOCK_SIZE <= aStreamOffset,
"Using a block that's behind the read position?");
GetListForBlock(bo)->RemoveBlock(aBlockIndex);
bo->mClass =
(aMode == MediaCacheStream::MODE_METADATA || bo->mClass == METADATA_BLOCK)
? METADATA_BLOCK : PLAYED_BLOCK;
// Since this is just being used now, it can definitely be at the front
// of mMetadataBlocks or mPlayedBlocks
@@ -1668,18 +1669,18 @@ MediaCache::NoteSeek(MediaCacheStream* a
std::min<int64_t>((aStream->mStreamOffset + BLOCK_SIZE - 1)/BLOCK_SIZE,
aStream->mBlocks.Length());
TimeStamp now = TimeStamp::Now();
while (blockIndex < endIndex) {
int32_t cacheBlockIndex = aStream->mBlocks[blockIndex];
if (cacheBlockIndex >= 0) {
// Marking the block used may not be exactly what we want but
// it's simple
- NoteBlockUsage(aStream, cacheBlockIndex, MediaCacheStream::MODE_PLAYBACK,
- now);
+ NoteBlockUsage(aStream, cacheBlockIndex, aStream->mStreamOffset,
+ MediaCacheStream::MODE_PLAYBACK, now);
}
++blockIndex;
}
} else {
// We seeked backward. Convert from played to readahead.
// Any played block that is entirely after the start of the seeked-over
// range must be converted.
int32_t blockIndex =
@@ -2204,27 +2205,30 @@ nsresult
MediaCacheStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
{
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
if (mClosed)
return NS_ERROR_FAILURE;
+ // Cache the offset in case it is changed again when we are waiting for the
+ // monitor to be notified to avoid reading at the wrong position.
+ auto streamOffset = mStreamOffset;
+
uint32_t count = 0;
// Read one block (or part of a block) at a time
while (count < aCount) {
- uint32_t streamBlock = uint32_t(mStreamOffset/BLOCK_SIZE);
- uint32_t offsetInStreamBlock =
- uint32_t(mStreamOffset - streamBlock*BLOCK_SIZE);
+ uint32_t streamBlock = uint32_t(streamOffset/BLOCK_SIZE);
+ uint32_t offsetInStreamBlock = uint32_t(streamOffset - streamBlock*BLOCK_SIZE);
int64_t size = std::min<int64_t>(aCount - count, BLOCK_SIZE - offsetInStreamBlock);
if (mStreamLength >= 0) {
// Don't try to read beyond the end of the stream
- int64_t bytesRemaining = mStreamLength - mStreamOffset;
+ int64_t bytesRemaining = mStreamLength - streamOffset;
if (bytesRemaining <= 0) {
// Get out of here and return NS_OK
break;
}
size = std::min(size, bytesRemaining);
// Clamp size until 64-bit file size issues are fixed.
size = std::min(size, int64_t(INT32_MAX));
}
@@ -2242,73 +2246,73 @@ MediaCacheStream::Read(char* aBuffer, ui
// See if the data is available in the partial cache block of any
// stream reading this resource. We need to do this in case there is
// another stream with this resource that has all the data to the end of
// the stream but the data doesn't end on a block boundary.
MediaCacheStream* streamWithPartialBlock = nullptr;
MediaCache::ResourceStreamIterator iter(mResourceID);
while (MediaCacheStream* stream = iter.Next()) {
if (uint32_t(stream->mChannelOffset/BLOCK_SIZE) == streamBlock &&
- mStreamOffset < stream->mChannelOffset) {
+ streamOffset < stream->mChannelOffset) {
streamWithPartialBlock = stream;
break;
}
}
if (streamWithPartialBlock) {
// We can just use the data in mPartialBlockBuffer. In fact we should
// use it rather than waiting for the block to fill and land in
// the cache.
- int64_t bytes = std::min<int64_t>(size, streamWithPartialBlock->mChannelOffset - mStreamOffset);
+ int64_t bytes = std::min<int64_t>(size, streamWithPartialBlock->mChannelOffset - streamOffset);
// Clamp bytes until 64-bit file size issues are fixed.
bytes = std::min(bytes, int64_t(INT32_MAX));
MOZ_ASSERT(bytes >= 0 && bytes <= aCount, "Bytes out of range.");
memcpy(aBuffer,
reinterpret_cast<char*>(streamWithPartialBlock->mPartialBlockBuffer.get()) + offsetInStreamBlock, bytes);
if (mCurrentMode == MODE_METADATA) {
streamWithPartialBlock->mMetadataInPartialBlockBuffer = true;
}
- mStreamOffset += bytes;
+ streamOffset += bytes;
count = bytes;
break;
}
// No data has been read yet, so block
mon.Wait();
if (mClosed) {
// We may have successfully read some data, but let's just throw
// that out.
return NS_ERROR_FAILURE;
}
continue;
}
- gMediaCache->NoteBlockUsage(this, cacheBlock, mCurrentMode, TimeStamp::Now());
+ gMediaCache->NoteBlockUsage(this, cacheBlock, streamOffset, mCurrentMode, TimeStamp::Now());
int64_t offset = cacheBlock*BLOCK_SIZE + offsetInStreamBlock;
int32_t bytes;
MOZ_ASSERT(size >= 0 && size <= INT32_MAX, "Size out of range.");
nsresult rv = gMediaCache->ReadCacheFile(offset, aBuffer + count, int32_t(size), &bytes);
if (NS_FAILED(rv)) {
if (count == 0)
return rv;
// If we did successfully read some data, may as well return it
break;
}
- mStreamOffset += bytes;
+ streamOffset += bytes;
count += bytes;
}
if (count > 0) {
// Some data was read, so queue an update since block priorities may
// have changed
gMediaCache->QueueUpdate();
}
- CACHE_LOG(LogLevel::Debug,
- ("Stream %p Read at %lld count=%d", this, (long long)(mStreamOffset-count), count));
+ CACHE_LOG(LogLevel::Debug, ("Stream %p Read at %lld count=%d", this, streamOffset-count, count));
*aBytes = count;
+ mStreamOffset = streamOffset;
return NS_OK;
}
nsresult
MediaCacheStream::ReadAt(int64_t aOffset, char* aBuffer,
uint32_t aCount, uint32_t* aBytes)
{
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -7,16 +7,17 @@
#include "mozilla/Logging.h"
#include "nsComponentManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsIRunnable.h"
#include "nsIWritablePropertyBag2.h"
#include "mozIGeckoMediaPluginService.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/SSE.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/unused.h"
#include "nsIObserverService.h"
#include "GMPTimerParent.h"
#include "runnable_utils.h"
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
#include "mozilla/SandboxInfo.h"
#endif
@@ -829,16 +830,30 @@ GMPParent::ReadGMPMetaData()
if (!mozilla::SandboxInfo::Get().CanSandboxMedia()) {
printf_stderr("GMPParent::ReadGMPMetaData: Plugin \"%s\" is an EME CDM"
" but this system can't sandbox it; not loading.\n",
mDisplayName.get());
delete cap;
return NS_ERROR_FAILURE;
}
#endif
+#ifdef XP_WIN
+ // Adobe GMP doesn't work without SSE2. Check the tags to see if
+ // the decryptor is for the Adobe GMP, and refuse to load it if
+ // SSE2 isn't supported.
+ for (const nsCString& tag : cap->mAPITags) {
+ if (!tag.EqualsLiteral("com.adobe.primetime")) {
+ continue;
+ }
+ if (!mozilla::supports_sse2()) {
+ return NS_ERROR_FAILURE;
+ }
+ break;
+ }
+#endif // XP_WIN
}
mCapabilities.AppendElement(cap);
}
if (mCapabilities.IsEmpty()) {
return NS_ERROR_FAILURE;
}
--- a/dom/media/gtest/TestMP3Demuxer.cpp
+++ b/dom/media/gtest/TestMP3Demuxer.cpp
@@ -225,16 +225,56 @@ protected:
res.mDemuxer = new MP3TrackDemuxer(res.mResource);
mTargets.push_back(res);
streamRes.mResource = new MockMP3StreamMediaResource(streamRes.mFilePath);
streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
mTargets.push_back(streamRes);
}
+ {
+ MP3Resource res;
+ // This file contains a false frame sync at 34, just after the ID3 tag,
+ // which should be identified as a false positive and skipped.
+ res.mFilePath = "small-shot-false-positive.mp3";
+ res.mIsVBR = true;
+ res.mFileSize = 6845;
+ res.mMPEGLayer = 3;
+ res.mMPEGVersion = 1;
+ res.mID3MajorVersion = 4;
+ res.mID3MinorVersion = 0;
+ res.mID3Flags = 0;
+ res.mID3Size = 24;
+ res.mDuration = 336686;
+ res.mDurationError = 0.01f;
+ res.mSeekError = 0.2f;
+ res.mSampleRate = 44100;
+ res.mSamplesPerFrame = 1152;
+ res.mNumSamples = 12;
+ res.mNumTrailingFrames = 0;
+ res.mBitrate = 256000;
+ res.mSlotSize = 1;
+ res.mPrivate = 0;
+ const int syncs[] = { 54, 576, 1098, 1621, 2143, 2666, 3188, 3711, 4233,
+ 4756, 5278, 5801, 6323 };
+ res.mSyncOffsets.insert(res.mSyncOffsets.begin(), syncs, syncs + 13);
+
+ // No content length can be estimated for CBR stream resources.
+ MP3Resource streamRes = res;
+ streamRes.mFileSize = -1;
+
+ res.mResource = new MockMP3MediaResource(res.mFilePath);
+ res.mDemuxer = new MP3TrackDemuxer(res.mResource);
+ mTargets.push_back(res);
+
+ streamRes.mResource = new MockMP3StreamMediaResource(streamRes.mFilePath);
+ streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
+ mTargets.push_back(streamRes);
+ }
+
for (auto& target: mTargets) {
ASSERT_EQ(NS_OK, target.mResource->Open(nullptr));
ASSERT_TRUE(target.mDemuxer->Init());
}
}
std::vector<MP3Resource> mTargets;
};
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -47,16 +47,17 @@ TEST_HARNESS_FILES.gtest += [
'../test/vp9cake.webm',
'dash_dashinit.mp4',
'id3v2header.mp3',
'mediasource_test.mp4',
'noise.mp3',
'noise_vbr.mp3',
'short-zero-in-moov.mp4',
'short-zero-inband.mov',
+ 'small-shot-false-positive.mp3',
'small-shot.mp3',
'test.webm',
'test_case_1224361.vp8.ivf',
'test_case_1224363.vp8.ivf',
'test_case_1224369.vp8.ivf',
]
include('/ipc/chromium/chromium-config.mozbuild')
new file mode 100644
index 0000000000000000000000000000000000000000..2f1e794051e9dd05955514f15bfca0dda8f917df
GIT binary patch
literal 6845
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
J{G0y~{{g{=W{m&<
--- a/dom/plugins/test/testplugin/README
+++ b/dom/plugins/test/testplugin/README
@@ -119,17 +119,17 @@ used as the exception message. Example:
* () - default method
Returns a string consisting of the plugin name, concatenated with any
arguments passed to the method.
* .crash() - Crashes the plugin
* getObjectValue() - Returns a custom plugin-implemented scriptable object.
-* checkObjectValue(obj) - Returns true if the object from setObjectValue() is
+* checkObjectValue(obj) - Returns true if the object from getObjectValue() is
the same object passed into this function. It should return true when
the object is passed to the same plugin instance, and false when passed
to other plugin instances, see bug 532246 and
test_multipleinstanceobjects.html.
* callOnDestroy(fn) - Calls `fn` when the plugin instance is being destroyed
* getAuthInfo(protocol, host, port, scheme, realm) - a wrapper for
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -917,48 +917,111 @@ void
Promise::MaybeRejectWithNull()
{
MaybeSomething(JS::NullHandleValue, &Promise::MaybeReject);
}
bool
Promise::PerformMicroTaskCheckpoint()
{
+ MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+
+ // On the main thread, we always use the main promise micro task queue.
std::queue<nsCOMPtr<nsIRunnable>>& microtaskQueue =
runtime->GetPromiseMicroTaskQueue();
if (microtaskQueue.empty()) {
return false;
}
- Maybe<AutoSafeJSContext> cx;
- if (NS_IsMainThread()) {
- cx.emplace();
- }
+ AutoSafeJSContext cx;
do {
nsCOMPtr<nsIRunnable> runnable = microtaskQueue.front();
MOZ_ASSERT(runnable);
// This function can re-enter, so we remove the element before calling.
microtaskQueue.pop();
nsresult rv = runnable->Run();
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
- if (cx.isSome()) {
- JS_CheckForInterrupt(cx.ref());
- }
+ JS_CheckForInterrupt(cx);
runtime->AfterProcessMicrotask();
} while (!microtaskQueue.empty());
return true;
}
+void
+Promise::PerformWorkerMicroTaskCheckpoint()
+{
+ MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
+
+ CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+
+ for (;;) {
+ // For a normal microtask checkpoint, we try to use the debugger microtask
+ // queue first. If the debugger queue is empty, we use the normal microtask
+ // queue instead.
+ std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
+ &runtime->GetDebuggerPromiseMicroTaskQueue();
+
+ if (microtaskQueue->empty()) {
+ microtaskQueue = &runtime->GetPromiseMicroTaskQueue();
+ if (microtaskQueue->empty()) {
+ break;
+ }
+ }
+
+ nsCOMPtr<nsIRunnable> runnable = microtaskQueue->front();
+ MOZ_ASSERT(runnable);
+
+ // This function can re-enter, so we remove the element before calling.
+ microtaskQueue->pop();
+ nsresult rv = runnable->Run();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ runtime->AfterProcessMicrotask();
+ }
+}
+
+void
+Promise::PerformWorkerDebuggerMicroTaskCheckpoint()
+{
+ MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
+
+ CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+
+ for (;;) {
+ // For a debugger microtask checkpoint, we always use the debugger microtask
+ // queue.
+ std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
+ &runtime->GetDebuggerPromiseMicroTaskQueue();
+
+ if (microtaskQueue->empty()) {
+ break;
+ }
+
+ nsCOMPtr<nsIRunnable> runnable = microtaskQueue->front();
+ MOZ_ASSERT(runnable);
+
+ // This function can re-enter, so we remove the element before calling.
+ microtaskQueue->pop();
+ nsresult rv = runnable->Run();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ runtime->AfterProcessMicrotask();
+ }
+}
+
#ifndef SPIDERMONKEY_PROMISE
/* static */ bool
Promise::JSCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
JS::Rooted<JS::Value> v(aCx,
@@ -2410,28 +2473,16 @@ Promise::AppendCallbacks(PromiseCallback
// callbacks with promise's result. If promise's state is rejected, queue a
// task to process our reject callbacks with promise's result.
if (mState != Pending) {
TriggerPromiseReactions();
}
}
#endif // SPIDERMONKEY_PROMISE
-/* static */ void
-Promise::DispatchToMicroTask(nsIRunnable* aRunnable)
-{
- MOZ_ASSERT(aRunnable);
-
- CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
- std::queue<nsCOMPtr<nsIRunnable>>& microtaskQueue =
- runtime->GetPromiseMicroTaskQueue();
-
- microtaskQueue.push(aRunnable);
-}
-
#ifndef SPIDERMONKEY_PROMISE
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
void
Promise::MaybeReportRejected()
{
if (mState != Rejected || mHadRejectCallback || mResult.isUndefined()) {
return;
}
@@ -2512,16 +2563,18 @@ Promise::HandleException(JSContext* aCx)
RejectInternal(aCx, exn);
}
}
void
Promise::ResolveInternal(JSContext* aCx,
JS::Handle<JS::Value> aValue)
{
+ CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+
mResolvePending = true;
if (aValue.isObject()) {
AutoDontReportUncaught silenceReporting(aCx);
JS::Rooted<JSObject*> valueObj(aCx, &aValue.toObject());
// Thenables.
JS::Rooted<JS::Value> then(aCx);
@@ -2553,17 +2606,17 @@ Promise::ResolveInternal(JSContext* aCx,
//
// Ensuring that stuff while not inside SpiderMonkey is painful, so let's
// drop the fast path for now.
RefPtr<PromiseInit> thenCallback =
new PromiseInit(nullptr, thenObj, mozilla::dom::GetIncumbentGlobal());
RefPtr<PromiseResolveThenableJob> task =
new PromiseResolveThenableJob(this, valueObj, thenCallback);
- DispatchToMicroTask(task);
+ runtime->DispatchToMicroTask(task);
return;
}
}
MaybeSettle(aValue, Resolved);
}
void
@@ -2643,26 +2696,28 @@ Promise::MaybeSettle(JS::Handle<JS::Valu
}
Settle(aValue, aState);
}
void
Promise::TriggerPromiseReactions()
{
+ CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
+
nsTArray<RefPtr<PromiseCallback>> callbacks;
callbacks.SwapElements(mState == Resolved ? mResolveCallbacks
: mRejectCallbacks);
mResolveCallbacks.Clear();
mRejectCallbacks.Clear();
for (uint32_t i = 0; i < callbacks.Length(); ++i) {
RefPtr<PromiseReactionJob> task =
new PromiseReactionJob(this, callbacks[i], mResult);
- DispatchToMicroTask(task);
+ runtime->DispatchToMicroTask(task);
}
}
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
void
Promise::RemoveFeature()
{
if (mFeature) {
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -161,16 +161,20 @@ public:
void MaybeRejectBrokenly(const T& aArg); // Not implemented by default; see
// specializations in the .cpp for
// the T values we support.
// Called by DOM to let us execute our callbacks. May be called recursively.
// Returns true if at least one microtask was processed.
static bool PerformMicroTaskCheckpoint();
+ static void PerformWorkerMicroTaskCheckpoint();
+
+ static void PerformWorkerDebuggerMicroTaskCheckpoint();
+
// WebIDL
nsIGlobalObject* GetParentObject() const
{
return mGlobal;
}
#ifdef SPIDERMONKEY_PROMISE
@@ -283,20 +287,16 @@ public:
JSCompartment* Compartment() const;
#ifndef SPIDERMONKEY_PROMISE
// Return a unique-to-the-process identifier for this Promise.
uint64_t GetID();
#endif // SPIDERMONKEY_PROMISE
- // Queue an async microtask to current main or worker thread.
- static void
- DispatchToMicroTask(nsIRunnable* aRunnable);
-
#ifndef SPIDERMONKEY_PROMISE
enum JSCallbackSlots {
SLOT_PROMISE = 0,
SLOT_DATA
};
#endif // SPIDERMONKEY_PROMISE
#ifdef SPIDERMONKEY_PROMISE
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_form_action_server.sjs
@@ -0,0 +1,33 @@
+// Custom *.sjs file specifically for the needs of Bug 1251043
+
+const FRAME = `
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title>Bug 1251043 - Test form-action blocks URL</title>
+ <meta http-equiv="Content-Security-Policy" content="form-action 'none';">
+ </head>
+ <body>
+ CONTROL-TEXT
+ <form action="file_form_action_server.sjs?formsubmission" method="GET">
+ <input type="submit" id="submitButton" value="submit">
+ </form>
+ </body>
+ </html>`;
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // PART 1: Return a frame including the FORM and the CSP
+ if (request.queryString === "loadframe") {
+ response.write(FRAME);
+ return;
+ }
+
+ // PART 2: We should never get here because the form
+ // should not be submitted. Just in case; return
+ // something unexpected so the test fails!
+ response.write("do'h");
+}
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -154,16 +154,17 @@ support-files =
file_docwrite_meta.css
file_docwrite_meta.js
file_multipart_testserver.sjs
file_fontloader.sjs
file_fontloader.woff
file_block_all_mcb.sjs
file_block_all_mixed_content_frame_navigation1.html
file_block_all_mixed_content_frame_navigation2.html
+ file_form_action_server.sjs
[test_base-uri.html]
[test_blob_data_schemes.html]
[test_connect-src.html]
[test_CSP.html]
[test_allow_https_schemes.html]
skip-if = buildapp == 'b2g' #no ssl support
[test_bug663567.html]
@@ -238,8 +239,9 @@ skip-if = toolkit == 'android' #investig
[test_meta_header_dual.html]
[test_docwrite_meta.html]
[test_multipartchannel.html]
[test_fontloader.html]
[test_block_all_mixed_content.html]
tags = mcb
[test_block_all_mixed_content_frame_navigation.html]
tags = mcb
+[test_form_action_blocks_url.html]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/test_form_action_blocks_url.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Bug 1251043 - Test form-action blocks URL</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <iframe id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+/*
+ * Description of the test:
+ * 1) Let's load a form into an iframe which uses a CSP of: form-action 'none';
+ * 2) Let's hit the submit button and make sure the form is not submitted.
+ *
+ * Since a blocked form submission does not fire any event handler, we have to
+ * use timeout triggered function that verifies that the form didn't get submitted.
+ */
+
+SimpleTest.requestFlakyTimeout(
+ "Form submission blocked by CSP does not fire any events " +
+ "hence we have to check back after 300ms to make sure the form " +
+ "is not submitted");
+SimpleTest.waitForExplicitFinish();
+
+const FORM_SUBMITTED = "form submission succeeded";
+var timeOutId;
+var testframe = document.getElementById("testframe");
+
+// In case the form gets submitted, the test would receive an 'load'
+// event and would trigger the test to fail early.
+function logFormSubmittedError() {
+ clearTimeout(timeOutId);
+ testframe.removeEventListener('load', logFormSubmittedError, false);
+ ok(false, "form submission should be blocked");
+ SimpleTest.finish();
+}
+
+// After 300ms we verify the form did not get submitted.
+function verifyFormNotSubmitted() {
+ clearTimeout(timeOutId);
+ var frameContent = testframe.contentWindow.document.body.innerHTML;
+ isnot(frameContent.indexOf("CONTROL-TEXT"), -1,
+ "form should not be submitted and still contain the control text");
+ SimpleTest.finish();
+}
+
+function submitForm() {
+ // Part 1: The form has loaded in the testframe
+ // unregister the current event handler
+ testframe.removeEventListener('load', submitForm, false);
+
+ // Part 2: Register a new load event handler. In case the
+ // form gets submitted, this load event fires and we can
+ // fail the test right away.
+ testframe.addEventListener("load", logFormSubmittedError, false);
+
+ // Part 3: Since blocking the form does not throw any kind of error;
+ // Firefox just logs the CSP error to the console we have to register
+ // this timeOut function which then verifies that the form didn't
+ // get submitted.
+ timeOutId = setTimeout(verifyFormNotSubmitted, 300);
+
+ // Part 4: We are ready, let's hit the submit button of the form.
+ var submitButton = testframe.contentWindow.document.getElementById('submitButton');
+ submitButton.click();
+}
+
+testframe.addEventListener("load", submitForm, false);
+testframe.src = "file_form_action_server.sjs?loadframe";
+
+</script>
+</body>
+</html>
--- a/dom/webidl/Promise.webidl
+++ b/dom/webidl/Promise.webidl
@@ -17,17 +17,17 @@ callback PromiseJobCallback = void();
[TreatNonCallableAsNull]
callback AnyCallback = any (any value);
// When using SpiderMonkey promises, we don't want to define all this stuff;
// just define a tiny interface to make codegen of Promise arguments and return
// values work.
#ifndef SPIDERMONKEY_PROMISE
[Constructor(PromiseInit init),
- Exposed=(Window,Worker,System)]
+ Exposed=(Window,Worker,WorkerDebugger,System)]
// Need to escape "Promise" so it's treated as an identifier.
interface _Promise {
// Have to use "any" (or "object", but "any" is simpler) as the type to
// support the subclassing behavior, since nothing actually requires the
// return value of PromiseSubclass.resolve/reject to be a Promise object.
[NewObject, Throws]
static any resolve(optional any value);
[NewObject, Throws]
--- a/dom/webidl/TextEncoder.webidl
+++ b/dom/webidl/TextEncoder.webidl
@@ -5,16 +5,16 @@
*
* The origin of this IDL file is
* http://encoding.spec.whatwg.org/#interface-textencoder
*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
-[Constructor(optional DOMString utfLabel = "utf-8"),
+[Constructor,
Exposed=(Window,Worker,System)]
interface TextEncoder {
[Constant]
readonly attribute DOMString encoding;
[NewObject]
Uint8Array encode(optional USVString input = "");
};
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -955,16 +955,44 @@ public:
{
// Only perform the Promise microtask checkpoint on the outermost event
// loop. Don't run it, for example, during sync XHR or importScripts.
if (aRecursionDepth == 2) {
CycleCollectedJSRuntime::AfterProcessTask(aRecursionDepth);
}
}
+ virtual void DispatchToMicroTask(nsIRunnable* aRunnable) override
+ {
+ MOZ_ASSERT(!NS_IsMainThread());
+ MOZ_ASSERT(aRunnable);
+
+ std::queue<nsCOMPtr<nsIRunnable>>* microTaskQueue = nullptr;
+
+ JSContext* cx = GetCurrentThreadJSContext();
+ NS_ASSERTION(cx, "This should never be null!");
+
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+ NS_ASSERTION(global, "This should never be null!");
+
+ // On worker threads, if the current global is the worker global, we use the
+ // main promise micro task queue. Otherwise, the current global must be
+ // either the debugger global or a debugger sandbox, and we use the debugger
+ // promise micro task queue instead.
+ if (IsWorkerGlobal(global)) {
+ microTaskQueue = &mPromiseMicroTaskQueue;
+ } else {
+ MOZ_ASSERT(IsDebuggerGlobal(global) || IsDebuggerSandbox(global));
+
+ microTaskQueue = &mDebuggerPromiseMicroTaskQueue;
+ }
+
+ microTaskQueue->push(aRunnable);
+ }
+
private:
WorkerPrivate* mWorkerPrivate;
};
class WorkerBackgroundChildCallback final :
public nsIIPCBackgroundChildCreateCallback
{
bool* mDone;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -4527,16 +4527,19 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
mDebuggerQueue.Pop(runnable);
debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
}
MOZ_ASSERT(runnable);
static_cast<nsIRunnable*>(runnable)->Run();
runnable->Release();
+ // Flush the promise queue.
+ Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
+
if (debuggerRunnablesPending) {
WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
MOZ_ASSERT(globalScope);
// Now *might* be a good time to GC. Let the JS engine make the decision.
JSAutoCompartment ac(aCx, globalScope->GetGlobalJSObject());
JS_MaybeGC(aCx);
}
@@ -5578,16 +5581,19 @@ WorkerPrivate::EnterDebuggerEventLoop()
mDebuggerQueue.Pop(runnable);
}
MOZ_ASSERT(runnable);
static_cast<nsIRunnable*>(runnable)->Run();
runnable->Release();
+ // Flush the promise queue.
+ Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
+
// Now *might* be a good time to GC. Let the JS engine make the decision.
if (JS::CurrentGlobalOrNull(cx)) {
JS_MaybeGC(cx);
}
}
}
}
@@ -6072,17 +6078,17 @@ WorkerPrivate::RunExpiredTimeouts(JSCont
retval = false;
break;
}
}
}
// Since we might be processing more timeouts, go ahead and flush
// the promise queue now before we do that.
- Promise::PerformMicroTaskCheckpoint();
+ Promise::PerformWorkerMicroTaskCheckpoint();
NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
}
// No longer possible to be called recursively.
mRunningExpiredTimeouts = false;
// Now remove canceled and expired timeouts from the main list.
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger_promise_debugger.js
@@ -0,0 +1,30 @@
+"use strict";
+
+var self = this;
+
+self.onmessage = function (event) {
+ if (event.data !== "resolve") {
+ return;
+ }
+ // This then-handler should be executed inside the top-level event loop,
+ // within the context of the debugger's global.
+ Promise.resolve().then(function () {
+ var dbg = new Debugger(global);
+ dbg.onDebuggerStatement = function () {
+ self.onmessage = function (event) {
+ if (event.data !== "resume") {
+ return;
+ }
+ // This then-handler should be executed inside the nested event loop,
+ // within the context of the debugger's global.
+ Promise.resolve().then(function () {
+ postMessage("resumed");
+ leaveEventLoop();
+ });
+ };
+ postMessage("paused");
+ enterEventLoop();
+ };
+ postMessage("resolved");
+ });
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger_promise_worker.js
@@ -0,0 +1,25 @@
+"use strict";
+
+self.onmessage = function (event) {
+ if (event.data !== "resolve") {
+ return;
+ }
+ // This then-handler should be executed inside the top-level event loop,
+ // within the context of the worker's global.
+ Promise.resolve().then(function () {
+ self.onmessage = function (event) {
+ if (event.data !== "pause") {
+ return;
+ }
+ // This then-handler should be executed inside the top-level event loop,
+ // within the context of the worker's global. Because the debugger
+ // statement here below enters a nested event loop, the then-handler
+ // should not be executed until the debugger statement returns.
+ Promise.resolve().then(function () {
+ postMessage("resumed");
+ });
+ debugger;
+ }
+ postMessage("resolved");
+ });
+};
--- a/dom/workers/test/chrome.ini
+++ b/dom/workers/test/chrome.ini
@@ -23,16 +23,18 @@ support-files =
WorkerDebuggerGlobalScope.reportError_childWorker.js
WorkerDebuggerGlobalScope.reportError_debugger.js
WorkerDebuggerGlobalScope.reportError_worker.js
WorkerDebuggerGlobalScope.setImmediate_debugger.js
WorkerDebuggerGlobalScope.setImmediate_worker.js
WorkerDebuggerManager_childWorker.js
WorkerDebuggerManager_worker.js
WorkerDebugger_childWorker.js
+ WorkerDebugger_promise_debugger.js
+ WorkerDebugger_promise_worker.js
WorkerDebugger_worker.js
WorkerDebugger_sharedWorker.js
WorkerDebugger_suspended_debugger.js
WorkerDebugger_suspended_worker.js
WorkerTest.jsm
WorkerTest_subworker.js
WorkerTest_worker.js
chromeWorker_subworker.js
@@ -62,16 +64,17 @@ support-files =
[test_WorkerDebugger.xul]
[test_WorkerDebuggerGlobalScope.createSandbox.xul]
[test_WorkerDebuggerGlobalScope.enterEventLoop.xul]
[test_WorkerDebuggerGlobalScope.reportError.xul]
[test_WorkerDebuggerGlobalScope.setImmediate.xul]
[test_WorkerDebuggerManager.xul]
[test_WorkerDebugger_console.xul]
[test_WorkerDebugger_frozen.xul]
+[test_WorkerDebugger_promise.xul]
[test_WorkerDebugger_suspended.xul]
[test_bug883784.xul]
[test_chromeWorker.xul]
[test_chromeWorkerJSM.xul]
[test_extension.xul]
[test_extensionBootstrap.xul]
[test_file.xul]
[test_fileBlobPosting.xul]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_WorkerDebugger_promise.xul
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
++ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebugger with DOM Promises"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const WORKER_URL = "WorkerDebugger_promise_worker.js";
+ const DEBUGGER_URL = BASE_URL + "WorkerDebugger_promise_debugger.js";
+
+ function test() {
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ let promise = waitForRegister(WORKER_URL, DEBUGGER_URL);
+ let worker = new Worker(WORKER_URL);
+ let dbg = yield promise;
+
+ info("Send a request to the worker. This should cause the worker " +
+ "to send a response.");
+ promise = waitForWorkerMessage(worker, "resolved");
+ worker.postMessage("resolve");
+ yield promise;
+
+ info("Send a request to the debugger. This should cause the debugger " +
+ "to send a response.");
+ promise = waitForDebuggerMessage(dbg, "resolved");
+ dbg.postMessage("resolve");
+ yield promise;
+
+ info("Send a request to the worker. This should cause the debugger " +
+ "to enter a nested event loop.");
+ promise = waitForDebuggerMessage(dbg, "paused");
+ worker.postMessage("pause");
+ yield promise;
+
+ info("Send a request to the debugger. This should cause the debugger " +
+ "to leave the nested event loop.");
+ promise = waitForMultiple([
+ waitForDebuggerMessage(dbg, "resumed"),
+ waitForWorkerMessage(worker, "resumed")
+ ]);
+ dbg.postMessage("resume");
+ yield promise;
+
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -127,17 +127,16 @@ enum class LogReason : int {
FilterNodeD2D1Backend,
SourceSurfaceIncompatible,
GlyphAllocFailedCairo,
GlyphAllocFailedCG,
InvalidRect,
CannotDraw3D, // 20
IncompatibleBasicTexturedEffect,
InvalidFont,
- AsyncTransactionTimeout,
// End
MustBeLessThanThis = 101,
};
struct BasicLogger
{
// For efficiency, this method exists and copies the logic of the
// OutputMessage below. If making any changes here, also make it
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -19,38 +19,24 @@
#include "nsDataHashtable.h" // for nsDataHashtable
#include "nsDebug.h" // for NS_ASSERTION
#include "nsHashKeys.h" // for nsPtrHashKey
#include "nsISupportsImpl.h" // for Layer::AddRef, etc
#include "nsRect.h" // for IntRect
#include "nsTArray.h" // for AutoTArray, nsTArray_Impl
#include "mozilla/layers/ImageHost.h"
#include "mozilla/layers/LayerManagerComposite.h"
-#include "LayersLogging.h"
-
-// LayerTreeInvalidation debugging
-#define LTI_DEBUG 0
-
-#if LTI_DEBUG
-# define LTI_DEEPER(aPrefix) nsPrintfCString("%s ", aPrefix).get()
-# define LTI_DUMP(rgn, label) if (!(rgn).IsEmpty()) printf_stderr("%s%p: " label " portion is %s\n", aPrefix, mLayer.get(), Stringify(rgn).c_str());
-# define LTI_LOG(...) printf_stderr(__VA_ARGS__)
-#else
-# define LTI_DEEPER(aPrefix) nullptr
-# define LTI_DUMP(rgn, label)
-# define LTI_LOG(...)
-#endif
using namespace mozilla::gfx;
namespace mozilla {
namespace layers {
struct LayerPropertiesBase;
-UniquePtr<LayerPropertiesBase> CloneLayerTreePropertiesInternal(Layer* aRoot);
+UniquePtr<LayerPropertiesBase> CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask = false);
/**
* Get accumulated transform of from the context creating layer to the
* given layer.
*/
static Matrix4x4
GetTransformIn3DContext(Layer* aLayer) {
Matrix4x4 transform = aLayer->GetLocalTransform();
@@ -149,21 +135,21 @@ struct LayerPropertiesBase : public Laye
, mInvalidRegion(aLayer->GetInvalidRegion())
, mPostXScale(aLayer->GetPostXScale())
, mPostYScale(aLayer->GetPostYScale())
, mOpacity(aLayer->GetLocalOpacity())
, mUseClipRect(!!aLayer->GetEffectiveClipRect())
{
MOZ_COUNT_CTOR(LayerPropertiesBase);
if (aLayer->GetMaskLayer()) {
- mMaskLayer = CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer());
+ mMaskLayer = CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer(), true);
}
for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
Layer* maskLayer = aLayer->GetAncestorMaskLayerAt(i);
- mAncestorMaskLayers.AppendElement(CloneLayerTreePropertiesInternal(maskLayer));
+ mAncestorMaskLayers.AppendElement(CloneLayerTreePropertiesInternal(maskLayer, true));
}
if (mUseClipRect) {
mClipRect = *aLayer->GetEffectiveClipRect();
}
mTransform = GetTransformForInvalidation(aLayer);
}
LayerPropertiesBase()
: mLayer(nullptr)
@@ -177,18 +163,17 @@ struct LayerPropertiesBase : public Laye
}
virtual nsIntRegion ComputeDifferences(Layer* aRoot,
NotifySubDocInvalidationFunc aCallback,
bool* aGeometryChanged);
virtual void MoveBy(const IntPoint& aOffset);
- nsIntRegion ComputeChange(const char* aPrefix,
- NotifySubDocInvalidationFunc aCallback,
+ nsIntRegion ComputeChange(NotifySubDocInvalidationFunc aCallback,
bool& aGeometryChanged)
{
bool transformChanged = !mTransform.FuzzyEqual(GetTransformForInvalidation(mLayer)) ||
mLayer->GetPostXScale() != mPostXScale ||
mLayer->GetPostYScale() != mPostYScale;
const Maybe<ParentLayerIntRect>& otherClip = mLayer->GetEffectiveClipRect();
nsIntRegion result;
@@ -206,49 +191,43 @@ struct LayerPropertiesBase : public Laye
if ((mMaskLayer ? mMaskLayer->mLayer : nullptr) != otherMask ||
ancestorMaskChanged ||
(mUseClipRect != !!otherClip) ||
mLayer->GetLocalOpacity() != mOpacity ||
transformChanged)
{
aGeometryChanged = true;
result = OldTransformedBounds();
- LTI_DUMP(result, "oldtransform");
- LTI_DUMP(NewTransformedBounds(), "newtransform");
AddRegion(result, NewTransformedBounds());
+
// We can't bail out early because we need to update mChildrenChanged.
}
- nsIntRegion internal = ComputeChangeInternal(aPrefix, aCallback, aGeometryChanged);
- LTI_DUMP(internal, "internal");
- AddRegion(result, internal);
- LTI_DUMP(mLayer->GetInvalidRegion(), "invalid");
+ AddRegion(result, ComputeChangeInternal(aCallback, aGeometryChanged));
AddTransformedRegion(result, mLayer->GetInvalidRegion(), mTransform);
if (mMaskLayer && otherMask) {
- nsIntRegion mask = mMaskLayer->ComputeChange(aPrefix, aCallback, aGeometryChanged);
- LTI_DUMP(mask, "mask");
- AddTransformedRegion(result, mask, mTransform);
+ AddTransformedRegion(result, mMaskLayer->ComputeChange(aCallback, aGeometryChanged),
+ mTransform);
}
for (size_t i = 0;
i < std::min(mAncestorMaskLayers.Length(), mLayer->GetAncestorMaskLayerCount());
i++)
{
- nsIntRegion mask = mAncestorMaskLayers[i]->ComputeChange(aPrefix, aCallback, aGeometryChanged);
- LTI_DUMP(mask, "ancestormask");
- AddTransformedRegion(result, mask, mTransform);
+ AddTransformedRegion(result,
+ mAncestorMaskLayers[i]->ComputeChange(aCallback, aGeometryChanged),
+ mTransform);
}
if (mUseClipRect && otherClip) {
if (!mClipRect.IsEqualInterior(*otherClip)) {
aGeometryChanged = true;
nsIntRegion tmp;
tmp.Xor(mClipRect.ToUnknownRect(), otherClip->ToUnknownRect());
- LTI_DUMP(tmp, "clip");
AddRegion(result, tmp);
}
}
mLayer->ClearInvalidRect();
return result;
}
@@ -258,18 +237,17 @@ struct LayerPropertiesBase : public Laye
GetTransformForInvalidation(mLayer));
}
virtual IntRect OldTransformedBounds()
{
return TransformRect(mVisibleRegion.ToUnknownRegion().GetBounds(), mTransform);
}
- virtual nsIntRegion ComputeChangeInternal(const char* aPrefix,
- NotifySubDocInvalidationFunc aCallback,
+ virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
bool& aGeometryChanged)
{
return IntRect();
}
RefPtr<Layer> mLayer;
UniquePtr<LayerPropertiesBase> mMaskLayer;
nsTArray<UniquePtr<LayerPropertiesBase>> mAncestorMaskLayers;
@@ -290,18 +268,17 @@ struct ContainerLayerProperties : public
, mPreXScale(aLayer->GetPreXScale())
, mPreYScale(aLayer->GetPreYScale())
{
for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) {
mChildren.AppendElement(Move(CloneLayerTreePropertiesInternal(child)));
}
}
- nsIntRegion ComputeChangeInternal(const char* aPrefix,
- NotifySubDocInvalidationFunc aCallback,
+ nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
bool& aGeometryChanged) override
{
ContainerLayer* container = mLayer->AsContainerLayer();
nsIntRegion invalidOfLayer; // Invalid regions of this layer.
nsIntRegion result; // Invliad regions for children only.
bool childrenChanged = false;
@@ -341,21 +318,19 @@ struct ContainerLayerProperties : public
// old list mChildren (if any of those children have been reordered
// rather than removed, we will invalidate their new area when we
// encounter them in the new list):
for (uint32_t j = i; j < childsOldIndex; ++j) {
AddRegion(result, mChildren[j]->OldTransformedBounds());
childrenChanged |= true;
}
// Invalidate any regions of the child that have changed:
- nsIntRegion region = mChildren[childsOldIndex]->ComputeChange(LTI_DEEPER(aPrefix), aCallback, aGeometryChanged);
+ nsIntRegion region = mChildren[childsOldIndex]->ComputeChange(aCallback, aGeometryChanged);
i = childsOldIndex + 1;
if (!region.IsEmpty()) {
- LTI_LOG("%s%p: child %p produced %s\n", aPrefix, mLayer.get(),
- mChildren[childsOldIndex]->mLayer.get(), Stringify(region).c_str());
AddRegion(result, region);
childrenChanged |= true;
}
} else {
// We've already seen this child in mChildren (which means it must
// have been reordered) and invalidated its old area. We need to
// invalidate its new area too:
invalidateChildsCurrentArea = true;
@@ -365,32 +340,30 @@ struct ContainerLayerProperties : public
invalidateChildsCurrentArea = true;
}
} else {
// |child| is new, or was reordered to a higher index
invalidateChildsCurrentArea = true;
}
if (invalidateChildsCurrentArea) {
aGeometryChanged = true;
- LTI_DUMP(child->GetLocalVisibleRegion().ToUnknownRegion(), "invalidateChildsCurrentArea");
AddTransformedRegion(result, child->GetLocalVisibleRegion().ToUnknownRegion(),
GetTransformForInvalidation(child));
if (aCallback) {
NotifySubdocumentInvalidationRecursive(child, aCallback);
} else {
ClearInvalidations(child);
}
}
childrenChanged |= invalidateChildsCurrentArea;
}
// Process remaining removed children.
while (i < mChildren.Length()) {
childrenChanged |= true;
- LTI_DUMP(mChildren[i]->OldTransformedBounds(), "removed child");
AddRegion(result, mChildren[i]->OldTransformedBounds());
i++;
}
if (aCallback) {
aCallback(container, result);
}
@@ -399,17 +372,16 @@ struct ContainerLayerProperties : public
}
if (!mLayer->Extend3DContext()) {
// |result| contains invalid regions only of children.
result.Transform(GetTransformForInvalidation(mLayer));
}
// else, effective transforms have applied on children.
- LTI_DUMP(invalidOfLayer, "invalidOfLayer");
result.OrWith(invalidOfLayer);
return result;
}
IntRect NewTransformedBounds() override
{
if (mLayer->Extend3DContext()) {
@@ -444,30 +416,28 @@ struct ContainerLayerProperties : public
struct ColorLayerProperties : public LayerPropertiesBase
{
explicit ColorLayerProperties(ColorLayer *aLayer)
: LayerPropertiesBase(aLayer)
, mColor(aLayer->GetColor())
, mBounds(aLayer->GetBounds())
{ }
- virtual nsIntRegion ComputeChangeInternal(const char* aPrefix,
- NotifySubDocInvalidationFunc aCallback,
+ virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
bool& aGeometryChanged)
{
ColorLayer* color = static_cast<ColorLayer*>(mLayer.get());
if (mColor != color->GetColor()) {
aGeometryChanged = true;
return NewTransformedBounds();
}
nsIntRegion boundsDiff;
boundsDiff.Xor(mBounds, color->GetBounds());
- LTI_DUMP(boundsDiff, "color");
nsIntRegion result;
AddTransformedRegion(result, boundsDiff, mTransform);
return result;
}
Color mColor;
@@ -480,115 +450,95 @@ static ImageHost* GetImageHost(Layer* aL
if (composite) {
return static_cast<ImageHost*>(composite->GetCompositableHost());
}
return nullptr;
}
struct ImageLayerProperties : public LayerPropertiesBase
{
- explicit ImageLayerProperties(ImageLayer* aImage)
+ explicit ImageLayerProperties(ImageLayer* aImage, bool aIsMask)
: LayerPropertiesBase(aImage)
, mContainer(aImage->GetContainer())
, mImageHost(GetImageHost(aImage))
, mFilter(aImage->GetFilter())
, mScaleToSize(aImage->GetScaleToSize())
, mScaleMode(aImage->GetScaleMode())
, mLastProducerID(-1)
, mLastFrameID(-1)
+ , mIsMask(aIsMask)
{
- if (mContainer) {
- mRect.SizeTo(mContainer->GetCurrentSize());
- }
if (mImageHost) {
- mRect.SizeTo(mImageHost->GetImageSize());
mLastProducerID = mImageHost->GetLastProducerID();
mLastFrameID = mImageHost->GetLastFrameID();
}
}
- // For image layers we want to use the image size rather than the
- // visible region for two reasons:
- // 1) If the image is mask layer, then the visible region is empty
- // 2) If the image is partially outside the displayport then the visible
- // region is truncated, but the compositor is still drawing the full
- // image each time. Partial composition on the image can result in
- // the un-composited portion of the image becoming visibly stale in
- // a checkerboarding area.
- IntRect NewTransformedBounds() override
- {
- IntRect rect;
- ImageLayer* imageLayer = static_cast<ImageLayer*>(mLayer.get());
- if (ImageContainer* container = imageLayer->GetContainer()) {
- rect.SizeTo(container->GetCurrentSize());
- }
- if (ImageHost* host = GetImageHost(imageLayer)) {
- rect.SizeTo(host->GetImageSize());
- }
- return TransformRect(rect, GetTransformForInvalidation(mLayer));
- }
-
- IntRect OldTransformedBounds() override
- {
- return TransformRect(mRect, mTransform);
- }
-
- virtual nsIntRegion ComputeChangeInternal(const char* aPrefix,
- NotifySubDocInvalidationFunc aCallback,
- bool& aGeometryChanged) override
+ virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+ bool& aGeometryChanged)
{
ImageLayer* imageLayer = static_cast<ImageLayer*>(mLayer.get());
if (!imageLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(mVisibleRegion)) {
aGeometryChanged = true;
IntRect result = NewTransformedBounds();
result = result.Union(OldTransformedBounds());
- LTI_DUMP(result, "image");
return result;
}
ImageContainer* container = imageLayer->GetContainer();
ImageHost* host = GetImageHost(imageLayer);
if (mContainer != container ||
mFilter != imageLayer->GetFilter() ||
mScaleToSize != imageLayer->GetScaleToSize() ||
mScaleMode != imageLayer->GetScaleMode() ||
host != mImageHost ||
(host && host->GetProducerID() != mLastProducerID) ||
(host && host->GetFrameID() != mLastFrameID)) {
aGeometryChanged = true;
- LTI_DUMP(NewTransformedBounds(), "bounds");
+ if (mIsMask) {
+ // Mask layers have an empty visible region, so we have to
+ // use the image size instead.
+ IntSize size;
+ if (container) {
+ size = container->GetCurrentSize();
+ }
+ if (host) {
+ size = host->GetImageSize();
+ }
+ IntRect rect(0, 0, size.width, size.height);
+ return TransformRect(rect, GetTransformForInvalidation(mLayer));
+ }
return NewTransformedBounds();
}
return IntRect();
}
RefPtr<ImageContainer> mContainer;
RefPtr<ImageHost> mImageHost;
Filter mFilter;
gfx::IntSize mScaleToSize;
ScaleMode mScaleMode;
- IntRect mRect;
int32_t mLastProducerID;
int32_t mLastFrameID;
+ bool mIsMask;
};
struct CanvasLayerProperties : public LayerPropertiesBase
{
explicit CanvasLayerProperties(CanvasLayer* aCanvas)
: LayerPropertiesBase(aCanvas)
, mImageHost(GetImageHost(aCanvas))
{
mFrameID = mImageHost ? mImageHost->GetFrameID() : -1;
}
- virtual nsIntRegion ComputeChangeInternal(const char* aPrefix,
- NotifySubDocInvalidationFunc aCallback,
+ virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
bool& aGeometryChanged)
{
CanvasLayer* canvasLayer = static_cast<CanvasLayer*>(mLayer.get());
ImageHost* host = GetImageHost(canvasLayer);
if (host && host->GetFrameID() != mFrameID) {
aGeometryChanged = true;
@@ -598,30 +548,32 @@ struct CanvasLayerProperties : public La
return IntRect();
}
RefPtr<ImageHost> mImageHost;
int32_t mFrameID;
};
UniquePtr<LayerPropertiesBase>
-CloneLayerTreePropertiesInternal(Layer* aRoot)
+CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask /* = false */)
{
if (!aRoot) {
return MakeUnique<LayerPropertiesBase>();
}
+ MOZ_ASSERT(!aIsMask || aRoot->GetType() == Layer::TYPE_IMAGE);
+
switch (aRoot->GetType()) {
case Layer::TYPE_CONTAINER:
case Layer::TYPE_REF:
return MakeUnique<ContainerLayerProperties>(aRoot->AsContainerLayer());
case Layer::TYPE_COLOR:
return MakeUnique<ColorLayerProperties>(static_cast<ColorLayer*>(aRoot));
case Layer::TYPE_IMAGE:
- return MakeUnique<ImageLayerProperties>(static_cast<ImageLayer*>(aRoot));
+ return MakeUnique<ImageLayerProperties>(static_cast<ImageLayer*>(aRoot), aIsMask);
case Layer::TYPE_CANVAS:
return MakeUnique<CanvasLayerProperties>(static_cast<CanvasLayer*>(aRoot));
case Layer::TYPE_READBACK:
case Layer::TYPE_SHADOW:
case Layer::TYPE_PAINTED:
return MakeUnique<LayerPropertiesBase>(aRoot);
}
@@ -671,21 +623,20 @@ LayerPropertiesBase::ComputeDifferences(
aRoot->GetLocalTransform());
result = result.Union(OldTransformedBounds());
if (aGeometryChanged != nullptr) {
*aGeometryChanged = true;
}
return result;
} else {
bool geometryChanged = (aGeometryChanged != nullptr) ? *aGeometryChanged : false;
- nsIntRegion invalid = ComputeChange(" ", aCallback, geometryChanged);
+ nsIntRegion invalid = ComputeChange(aCallback, geometryChanged);
if (aGeometryChanged != nullptr) {
*aGeometryChanged = geometryChanged;
}
- LTI_LOG("ComputeDifferences returned %s\n", Stringify(invalid).c_str());
return invalid;
}
}
void
LayerPropertiesBase::MoveBy(const IntPoint& aOffset)
{
mTransform.PostTranslate(aOffset.x, aOffset.y, 0);
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -515,51 +515,42 @@ RenderMinimap(ContainerT* aContainer, La
return;
}
// Compute a scale with an appropriate aspect ratio
// We allocate up to 100px of width and the height of this layer.
float scaleFactor;
float scaleFactorX;
float scaleFactorY;
- scaleFactorX = 100.f / scrollRect.width;
- scaleFactorY = ((viewRect.height) - 2 * verticalPadding) / scrollRect.height;
+ Rect dest = Rect(aClipRect.ToUnknownRect());
+ if (aLayer->GetEffectiveClipRect()) {
+ dest = Rect(aLayer->GetEffectiveClipRect().value().ToUnknownRect());
+ } else {
+ dest = aContainer->GetEffectiveTransform().Inverse().TransformBounds(dest);
+ }
+ dest = dest.Intersect(compositionBounds.ToUnknownRect());
+ scaleFactorX = std::min(100.f, dest.width - (2 * horizontalPadding)) / scrollRect.width;
+ scaleFactorY = (dest.height - (2 * verticalPadding)) / scrollRect.height;
scaleFactor = std::min(scaleFactorX, scaleFactorY);
+ if (scaleFactor <= 0) {
+ return;
+ }
Matrix4x4 transform = Matrix4x4::Scaling(scaleFactor, scaleFactor, 1);
- transform.PostTranslate(horizontalPadding + compositionBounds.x, verticalPadding + compositionBounds.y, 0);
+ transform.PostTranslate(horizontalPadding + dest.x, verticalPadding + dest.y, 0);
- Rect clipRect = aContainer->GetEffectiveTransform().TransformBounds(
- transform.TransformBounds(scrollRect.ToUnknownRect()));
+ Rect transformedScrollRect = transform.TransformBounds(scrollRect.ToUnknownRect());
+
+ Rect clipRect = aContainer->GetEffectiveTransform().TransformBounds(transformedScrollRect);
clipRect.width++;
clipRect.height++;
- Rect r;
- r = transform.TransformBounds(scrollRect.ToUnknownRect());
- compositor->FillRect(r, backgroundColor, clipRect, aContainer->GetEffectiveTransform());
-
- /* Disabled because on long pages SlowDrawRect becomes a bottleneck.
- int tileW = gfxPrefs::LayersTileWidth();
- int tileH = gfxPrefs::LayersTileHeight();
-
- for (int x = scrollRect.x; x < scrollRect.XMost(); x += tileW) {
- for (int y = scrollRect.y; y < scrollRect.YMost(); y += tileH) {
- LayerRect tileRect = LayerRect(x - x % tileW, y - y % tileH, tileW, tileH);
- r = transform.TransformBounds(tileRect.ToUnknownRect());
- if (tileRect.Intersects(dp)) {
- compositor->FillRect(r, tileActiveColor, clipRect, aContainer->GetEffectiveTransform());
- }
- compositor->SlowDrawRect(r, tileBorderColor, clipRect, aContainer->GetEffectiveTransform());
- }
- }
- */
-
// Render the scrollable area.
- r = transform.TransformBounds(scrollRect.ToUnknownRect());
- compositor->SlowDrawRect(r, pageBorderColor, clipRect, aContainer->GetEffectiveTransform());
+ compositor->FillRect(transformedScrollRect, backgroundColor, clipRect, aContainer->GetEffectiveTransform());
+ compositor->SlowDrawRect(transformedScrollRect, pageBorderColor, clipRect, aContainer->GetEffectiveTransform());
// If enabled, render information about visibility.
if (gfxPrefs::APZMinimapVisibilityEnabled()) {
// Retrieve the APZC scrollable layer guid, which we'll use to get the
// appropriate visibility information from the layer manager.
AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0);
MOZ_ASSERT(controller);
@@ -580,19 +571,18 @@ RenderMinimap(ContainerT* aContainer, La
CSSIntRect rect = iterator.Get();
LayerRect scaledRect = rect * fm.LayersPixelsPerCSSPixel();
Rect r = transform.TransformBounds(scaledRect.ToUnknownRect());
compositor->FillRect(r, visibilityColor, clipRect, aContainer->GetEffectiveTransform());
}
}
// Render the displayport.
- r = transform.TransformBounds(dp.ToUnknownRect());
+ Rect r = transform.TransformBounds(dp.ToUnknownRect());
compositor->FillRect(r, tileActiveColor, clipRect, aContainer->GetEffectiveTransform());
- r = transform.TransformBounds(dp.ToUnknownRect());
compositor->SlowDrawRect(r, displayPortColor, clipRect, aContainer->GetEffectiveTransform());
// Render the viewport.
r = transform.TransformBounds(viewRect.ToUnknownRect());
compositor->SlowDrawRect(r, viewPortColor, clipRect, aContainer->GetEffectiveTransform(), 2);
}
--- a/gfx/layers/ipc/AsyncTransactionTracker.cpp
+++ b/gfx/layers/ipc/AsyncTransactionTracker.cpp
@@ -29,20 +29,16 @@ AsyncTransactionWaiter::WaitComplete()
printf_stderr("Waiting async transaction complete.\n");
}
count++;
}
if (mWaitCount > 0) {
printf_stderr("Timeout of waiting transaction complete.");
}
-
- if (count == maxCount) {
- gfxDevCrash(LogReason::AsyncTransactionTimeout) << "Bug 1244883: AsyncTransactionWaiter timed out.";
- }
}
Atomic<uint64_t> AsyncTransactionTracker::sSerialCounter(0);
AsyncTransactionTracker::AsyncTransactionTracker(AsyncTransactionWaiter* aWaiter)
: mSerial(GetNextSerial())
, mWaiter(aWaiter)
#ifdef DEBUG
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -524,16 +524,26 @@ gfxWindowsPlatform::UpdateRenderMode()
{
bool didReset = HandleDeviceReset();
UpdateBackendPrefs();
if (didReset) {
mScreenReferenceDrawTarget =
CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
+ if (!mScreenReferenceDrawTarget) {
+ gfxCriticalNote << "Failed to update reference draw target after device reset"
+ << ", D3D11 device:" << hexa(Factory::GetDirect3D11Device())
+ << ", D3D11 status:" << FeatureStatusToString(GetD3D11Status())
+ << ", D2D1 device:" << hexa(Factory::GetD2D1Device())
+ << ", D2D1 status:" << FeatureStatusToString(GetD2D1Status())
+ << ", content:" << int(GetDefaultContentBackend())
+ << ", compositor:" << int(GetCompositorBackend());
+ MOZ_CRASH("GFX: Failed to update reference draw target after device reset");
+ }
}
}
void
gfxWindowsPlatform::ForceDeviceReset(ForcedDeviceResetReason aReason)
{
Telemetry::Accumulate(Telemetry::FORCED_DEVICE_RESET_REASON, uint32_t(aReason));
--- a/ipc/chromium/src/third_party/libevent/evutil_rand.c
+++ b/ipc/chromium/src/third_party/libevent/evutil_rand.c
@@ -134,16 +134,16 @@ ev_arc4random_buf(void *buf, size_t n)
#endif /* } !_EVENT_HAVE_ARC4RANDOM */
void
evutil_secure_rng_get_bytes(void *buf, size_t n)
{
ev_arc4random_buf(buf, n);
}
-#ifndef __OpenBSD__
+#if !defined(__OpenBSD__) && !defined(ANDROID)
void
evutil_secure_rng_add_bytes(const char *buf, size_t n)
{
arc4random_addrandom((unsigned char*)buf,
n>(size_t)INT_MAX ? INT_MAX : (int)n);
}
#endif
--- a/ipc/chromium/src/third_party/libevent/include/event2/util.h
+++ b/ipc/chromium/src/third_party/libevent/include/event2/util.h
@@ -667,17 +667,17 @@ void evutil_secure_rng_get_bytes(void *b
* numbers. You only need to call it if (a) you want to double-check
* that one of the seeding methods did succeed, or (b) you plan to drop
* the capability to seed (by chrooting, or dropping capabilities, or
* whatever), and you want to make sure that seeding happens before your
* program loses the ability to do it.
*/
int evutil_secure_rng_init(void);
-#ifndef __OpenBSD__
+#if !defined(__OpenBSD__) && !defined(ANDROID)
/** Seed the random number generator with extra random bytes.
You should almost never need to call this function; it should be
sufficient to invoke evutil_secure_rng_init(), or let Libevent take
care of calling evutil_secure_rng_init() on its own.
If you call this function as a _replacement_ for the regular
entropy sources, then you need to be sure that your input
--- a/ipc/chromium/src/third_party/libevent/patches/openbsd-no-arc4random_addrandom.patch
+++ b/ipc/chromium/src/third_party/libevent/patches/openbsd-no-arc4random_addrandom.patch
@@ -1,29 +1,31 @@
# HG changeset patch
# User Landry Breuil <landry@openbsd.org>
# Date 1384377262 -3600
# Wed Nov 13 22:14:22 2013 +0100
# Node ID 026009b2c94dc564413d48df824fabec98e2c631
# Parent f2b602a5ee27b2e05abe84ea7cbd358dadd2ffb5
Bug 931354: OpenBSD doesn't provide arc4random_addrandom anymore, fix libevent accordingly by #ifndef'ing out its caller evutil_secure_rng_add_bytes() (which isnt called anywhere) r=joshaas
+See also bug 1259218 for why we remove it for Android.
+
diff --git a/ipc/chromium/src/third_party/libevent/evutil_rand.c b/ipc/chromium/src/third_party/libevent/evutil_rand.c
--- a/ipc/chromium/src/third_party/libevent/evutil_rand.c
+++ b/ipc/chromium/src/third_party/libevent/evutil_rand.c
@@ -134,15 +134,16 @@ ev_arc4random_buf(void *buf, size_t n)
#endif /* } !_EVENT_HAVE_ARC4RANDOM */
void
evutil_secure_rng_get_bytes(void *buf, size_t n)
{
ev_arc4random_buf(buf, n);
}
-+#ifndef __OpenBSD__
++#if !defined(__OpenBSD__) && !defined(ANDROID)
void
evutil_secure_rng_add_bytes(const char *buf, size_t n)
{
arc4random_addrandom((unsigned char*)buf,
n>(size_t)INT_MAX ? INT_MAX : (int)n);
}
-
+#endif
@@ -34,17 +36,17 @@ diff --git a/ipc/chromium/src/third_part
* numbers. You only need to call it if (a) you want to double-check
* that one of the seeding methods did succeed, or (b) you plan to drop
* the capability to seed (by chrooting, or dropping capabilities, or
* whatever), and you want to make sure that seeding happens before your
* program loses the ability to do it.
*/
int evutil_secure_rng_init(void);
-+#ifndef __OpenBSD__
++#if !defined(__OpenBSD__) && !defined(ANDROID)
/** Seed the random number generator with extra random bytes.
You should almost never need to call this function; it should be
sufficient to invoke evutil_secure_rng_init(), or let Libevent take
care of calling evutil_secure_rng_init() on its own.
If you call this function as a _replacement_ for the regular
entropy sources, then you need to be sure that your input
--- a/js/src/jit-test/tests/asm.js/testExpressions.js
+++ b/js/src/jit-test/tests/asm.js/testExpressions.js
@@ -388,8 +388,9 @@ for (let i = 0; i < 31; i++) {
assertEq(f(Math.pow(2,i)), (Math.pow(2,i)/Math.pow(2,30))|0);
assertEq(f(Math.pow(2,i+1)-1), ((Math.pow(2,i+1)-1)/Math.pow(2,30))|0);
}
var f = asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; i=(i&2147483647)|0; return ((((i|0)/1)|0)+i)|0; } return f;"));
for (let i = 0; i < 31; i++) {
assertEq(f(Math.pow(2,i)), (Math.pow(2,i) * 2)|0);
assertEq(f(Math.pow(2,i+1) - 1), ((Math.pow(2,i+1) - 1) * 2)|0);
}
+assertEq(asmLink(asmCompile(USE_ASM + "var g=0; function f(x, y) { x = x|0; y = y|0; g = (x>>>0)%(y>>>0)|0; return (x|0)%(y|0)|0; } return f;"))(0xff40001, 0xfff80000), 0x40001);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/bug1258301.js
@@ -0,0 +1,5 @@
+x = new WeakMap;
+x.__proto__ = null;
+for (var i = 0; i < 3; i++)
+ x.someprop;
+gc();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/long-proto-chains.js
@@ -0,0 +1,10 @@
+function f() {
+ var o = {x: 1};
+ for (var i = 0; i < 300; i++)
+ o = Object.create(o);
+ for (var i = 0; i < 15; i++) {
+ assertEq(o.x, 1);
+ assertEq(o.y, undefined);
+ }
+}
+f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Environment-unscopables.js
@@ -0,0 +1,37 @@
+// An Environment for a `with` statement does not observe bindings ruled out by @@unscopables.
+
+load(libdir + "asserts.js");
+
+let g = newGlobal();
+g.eval(`
+ let x = 'global';
+ function f() {
+ let obj = {
+ x: 'obj',
+ y: 'obj',
+ [Symbol.unscopables]: {x: 1},
+ };
+ with (obj)
+ debugger;
+ }
+`);
+let dbg = Debugger(g);
+let hits = 0;
+dbg.onDebuggerStatement = function (frame) {
+ let env = frame.environment;
+
+ assertEq(env.find("x") !== env, true);
+ assertEq(env.names().indexOf("x"), -1);
+ assertEq(env.getVariable("x"), undefined);
+ assertThrowsInstanceOf(() => env.setVariable("x", 7), TypeError);
+
+ assertEq(env.find("y") === env, true);
+ assertEq(env.getVariable("y"), "obj");
+ env.setVariable("y", 8);
+
+ assertEq(frame.eval("x").return, "global");
+ assertEq(frame.eval("y").return, 8);
+ hits++;
+};
+g.f();
+assertEq(hits, 1);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1252464.js
@@ -0,0 +1,15 @@
+// |jit-test| error: ReferenceError
+
+g = newGlobal();
+dbg = Debugger(g);
+hits = 0;
+dbg.onNewScript = function () hits++;
+assertEq(g.eval("eval('2 + 3')"), 5);
+this.gczeal(hits,1);
+dbg = Debugger(g);
+g.h = function () {
+ var env = dbg.getNewestFrame().environment;
+ dbg = 0;
+ assertThrowsInstanceOf;
+}
+g.eval("h()");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/profiler/bug1233921.js
@@ -0,0 +1,19 @@
+g = newGlobal();
+g.parent = this;
+g.eval("new Debugger(parent).onExceptionUnwind = function () {}");
+enableSPSProfiling();
+try {
+ enableSingleStepProfiling();
+} catch(e) {
+ quit();
+}
+f();
+f();
+function $ERROR() {
+ throw Error;
+}
+function f() {
+ try {
+ $ERROR()
+ } catch (ex) {}
+}
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1085,26 +1085,23 @@ InitFromBailout(JSContext* cx, HandleScr
// When propagating an exception for debug mode, set the
// resume pc to the throwing pc, so that Debugger hooks report
// the correct pc offset of the throwing op instead of its
// successor (this pc will be used as the BaselineFrame's
// override pc).
//
// Note that we never resume at this pc, it is set for the sake
// of frame iterators giving the correct answer.
- //
- // We also set nativeCodeForPC to nullptr as this address
- // won't be used anywhere.
jsbytecode* throwPC = script->offsetToPC(iter.pcOffset());
builder.setResumePC(throwPC);
- nativeCodeForPC = nullptr;
+ nativeCodeForPC = baselineScript->nativeCodeForPC(script, throwPC);
} else {
nativeCodeForPC = baselineScript->nativeCodeForPC(script, pc, &slotInfo);
- MOZ_ASSERT(nativeCodeForPC);
}
+ MOZ_ASSERT(nativeCodeForPC);
unsigned numUnsynced = slotInfo.numUnsynced();
MOZ_ASSERT(numUnsynced <= 2);
PCMappingSlotInfo::SlotLocation loc1, loc2;
if (numUnsynced > 0) {
loc1 = slotInfo.topSlotLocation();
JitSpew(JitSpew_BaselineBailouts, " Popping top stack value into %d.",
--- a/js/src/jit/BaselineCacheIR.cpp
+++ b/js/src/jit/BaselineCacheIR.cpp
@@ -1038,23 +1038,26 @@ void
jit::TraceBaselineCacheIRStub(JSTracer* trc, ICStub* stub, const CacheIRStubInfo* stubInfo)
{
uint32_t field = 0;
while (true) {
switch (stubInfo->gcType(field)) {
case StubField::GCType::NoGCThing:
break;
case StubField::GCType::Shape:
- TraceEdge(trc, &stubInfo->getStubField<Shape*>(stub, field), "baseline-cacheir-shape");
+ TraceNullableEdge(trc, &stubInfo->getStubField<Shape*>(stub, field),
+ "baseline-cacheir-shape");
break;
case StubField::GCType::ObjectGroup:
- TraceEdge(trc, &stubInfo->getStubField<ObjectGroup*>(stub, field), "baseline-cacheir-group");
+ TraceNullableEdge(trc, &stubInfo->getStubField<ObjectGroup*>(stub, field),
+ "baseline-cacheir-group");
break;
case StubField::GCType::JSObject:
- TraceEdge(trc, &stubInfo->getStubField<JSObject*>(stub, field), "baseline-cacheir-object");
+ TraceNullableEdge(trc, &stubInfo->getStubField<JSObject*>(stub, field),
+ "baseline-cacheir-object");
break;
case StubField::GCType::Limit:
return; // Done.
default:
MOZ_CRASH();
}
field++;
}
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -142,18 +142,18 @@ class MOZ_RAII CacheIRWriter
void writeOp(CacheOp op) {
MOZ_ASSERT(uint32_t(op) <= UINT8_MAX);
buffer_.writeByte(uint32_t(op));
nextInstructionId_++;
}
void writeOperandId(OperandId opId) {
- MOZ_ASSERT(size_t(opId.id()) <= UINT8_MAX);
if (opId.id() < MaxOperandIds) {
+ static_assert(MaxOperandIds <= UINT8_MAX, "operand id must fit in a single byte");
buffer_.writeByte(opId.id());
} else {
tooLarge_ = true;
return;
}
if (opId.id() >= operandLastUsed_.length()) {
buffer_.propagateOOM(operandLastUsed_.resize(opId.id() + 1));
if (buffer_.oom())
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2812,16 +2812,19 @@ TryEliminateBoundsCheck(BoundsCheckMap&
// bounds checks and would otherwise use the wrong index and
// (b) helps register allocation. Note that this is safe since
// no other pass after bounds check elimination moves instructions.
dominated->replaceAllUsesWith(dominated->index());
if (!dominated->isMovable())
return true;
+ if (!dominated->fallible())
+ return true;
+
MBoundsCheck* dominating = FindDominatingBoundsCheck(checks, dominated, blockIndex);
if (!dominating)
return false;
if (dominating == dominated) {
// We didn't find a dominating bounds check.
return true;
}
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6900,16 +6900,21 @@ class MDiv : public MBinaryArithInstruct
void collectRangeInfoPreTrunc() override;
TruncateKind operandTruncateKind(size_t index) const override;
bool writeRecoverData(CompactBufferWriter& writer) const override;
bool canRecoverOnBailout() const override {
return specialization_ < MIRType_Object;
}
+ bool congruentTo(const MDefinition* ins) const override {
+ return MBinaryArithInstruction::congruentTo(ins) &&
+ unsigned_ == ins->toDiv()->isUnsigned();
+ }
+
ALLOW_CLONE(MDiv)
};
class MMod : public MBinaryArithInstruction
{
bool unsigned_;
bool canBeNegativeDividend_;
bool canBePowerOfTwoDivisor_;
@@ -6978,16 +6983,21 @@ class MMod : public MBinaryArithInstruct
bool fallible() const;
void computeRange(TempAllocator& alloc) override;
bool needTruncation(TruncateKind kind) override;
void truncate() override;
void collectRangeInfoPreTrunc() override;
TruncateKind operandTruncateKind(size_t index) const override;
+ bool congruentTo(const MDefinition* ins) const override {
+ return MBinaryArithInstruction::congruentTo(ins) &&
+ unsigned_ == ins->toMod()->isUnsigned();
+ }
+
ALLOW_CLONE(MMod)
};
class MConcat
: public MBinaryInstruction,
public MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1> >::Data
{
MConcat(MDefinition* left, MDefinition* right)
@@ -9060,31 +9070,35 @@ class MBoundsCheck
}
MDefinition* length() const {
return getOperand(1);
}
int32_t minimum() const {
return minimum_;
}
void setMinimum(int32_t n) {
+ MOZ_ASSERT(fallible_);
minimum_ = n;
}
int32_t maximum() const {
return maximum_;
}
void setMaximum(int32_t n) {
+ MOZ_ASSERT(fallible_);
maximum_ = n;
}
MDefinition* foldsTo(TempAllocator& alloc) override;
bool congruentTo(const MDefinition* ins) const override {
if (!ins->isBoundsCheck())
return false;
const MBoundsCheck* other = ins->toBoundsCheck();
if (minimum() != other->minimum() || maximum() != other->maximum())
return false;
+ if (fallible() != other->fallible())
+ return false;
return congruentIfOperandsEqual(other);
}
virtual AliasSet getAliasSet() const override {
return AliasSet::None();
}
void computeRange(TempAllocator& alloc) override;
bool fallible() const {
return fallible_;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6450,33 +6450,39 @@ JS::SetOutOfMemoryCallback(JSRuntime* rt
{
rt->oomCallback = cb;
rt->oomCallbackData = data;
}
JS_PUBLIC_API(bool)
JS::CaptureCurrentStack(JSContext* cx, JS::MutableHandleObject stackp, unsigned maxFrameCount)
{
+ AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ MOZ_RELEASE_ASSERT(cx->compartment());
+
JSCompartment* compartment = cx->compartment();
- MOZ_ASSERT(compartment);
Rooted<SavedFrame*> frame(cx);
if (!compartment->savedStacks().saveCurrentStack(cx, &frame, maxFrameCount))
return false;
stackp.set(frame.get());
return true;
}
JS_PUBLIC_API(bool)
JS::CopyAsyncStack(JSContext* cx, JS::HandleObject asyncStack,
JS::HandleString asyncCause, JS::MutableHandleObject stackp,
unsigned maxFrameCount)
{
+ AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ MOZ_RELEASE_ASSERT(cx->compartment());
+
js::AssertObjectIsSavedFrameOrWrapper(cx, asyncStack);
JSCompartment* compartment = cx->compartment();
- MOZ_ASSERT(compartment);
Rooted<SavedFrame*> frame(cx);
if (!compartment->savedStacks().copyAsyncStack(cx, asyncStack, asyncCause,
&frame, maxFrameCount))
return false;
stackp.set(frame.get());
return true;
}
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4858,27 +4858,35 @@ GetSymbolFor(JSContext* cx, HandleString
*
* This function is infallible. If it returns null, that means the symbol's
* [[Description]] is undefined.
*/
JS_PUBLIC_API(JSString*)
GetSymbolDescription(HandleSymbol symbol);
/* Well-known symbols. */
+#define JS_FOR_EACH_WELL_KNOWN_SYMBOL(macro) \
+ macro(iterator) \
+ macro(match) \
+ macro(species) \
+ macro(toPrimitive) \
+ macro(unscopables)
+
enum class SymbolCode : uint32_t {
- iterator, // well-known symbols
- match,
- species,
- toPrimitive,
+ // There is one SymbolCode for each well-known symbol.
+#define JS_DEFINE_SYMBOL_ENUM(name) name,
+ JS_FOR_EACH_WELL_KNOWN_SYMBOL(JS_DEFINE_SYMBOL_ENUM) // SymbolCode::iterator, etc.
+#undef JS_DEFINE_SYMBOL_ENUM
+ Limit,
InSymbolRegistry = 0xfffffffe, // created by Symbol.for() or JS::GetSymbolFor()
UniqueSymbol = 0xffffffff // created by Symbol() or JS::NewSymbol()
};
/* For use in loops that iterate over the well-known symbols. */
-const size_t WellKnownSymbolLimit = 4;
+const size_t WellKnownSymbolLimit = size_t(SymbolCode::Limit);
/**
* Return the SymbolCode telling what sort of symbol `symbol` is.
*
* A symbol's SymbolCode never changes once it is created.
*/
JS_PUBLIC_API(SymbolCode)
GetSymbolCode(Handle<Symbol*> symbol);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3165,16 +3165,17 @@ static const JSFunctionSpec array_method
JS_SELF_HOSTED_FN("findIndex", "ArrayFindIndex", 1,0),
JS_SELF_HOSTED_FN("copyWithin", "ArrayCopyWithin", 3,0),
JS_SELF_HOSTED_FN("fill", "ArrayFill", 3,0),
JS_SELF_HOSTED_SYM_FN(iterator, "ArrayValues", 0,0),
JS_SELF_HOSTED_FN("entries", "ArrayEntries", 0,0),
JS_SELF_HOSTED_FN("keys", "ArrayKeys", 0,0),
+ JS_SELF_HOSTED_FN("values", "ArrayValues", 0,0),
/* ES7 additions */
JS_SELF_HOSTED_FN("includes", "ArrayIncludes", 2,0),
JS_FS_END
};
static const JSFunctionSpec array_static_methods[] = {
JS_INLINABLE_FN("isArray", array_isArray, 1,0, ArrayIsArray),
@@ -3280,16 +3281,42 @@ CreateArrayPrototype(JSContext* cx, JSPr
* be used without updating the indexed type set for such default arrays.
*/
if (!JSObject::setNewGroupUnknown(cx, &ArrayObject::class_, arrayProto))
return nullptr;
return arrayProto;
}
+static bool
+array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
+{
+ // Add Array.prototype[@@unscopables]. ECMA-262 draft (2016 Mar 19) 22.1.3.32.
+ RootedObject unscopables(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, TenuredObject));
+ if (!unscopables)
+ return false;
+
+ RootedValue value(cx, BooleanValue(true));
+ if (!DefineProperty(cx, unscopables, cx->names().copyWithin, value) ||
+ !DefineProperty(cx, unscopables, cx->names().entries, value) ||
+ !DefineProperty(cx, unscopables, cx->names().fill, value) ||
+ !DefineProperty(cx, unscopables, cx->names().find, value) ||
+ !DefineProperty(cx, unscopables, cx->names().findIndex, value) ||
+ !DefineProperty(cx, unscopables, cx->names().includes, value) ||
+ !DefineProperty(cx, unscopables, cx->names().keys, value) ||
+ !DefineProperty(cx, unscopables, cx->names().values, value))
+ {
+ return false;
+ }
+
+ RootedId id(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().get(JS::SymbolCode::unscopables)));
+ value.setObject(*unscopables);
+ return DefineProperty(cx, proto, id, value, nullptr, nullptr, JSPROP_READONLY);
+}
+
const Class ArrayObject::class_ = {
"Array",
JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_DELAY_METADATA_CALLBACK,
array_addProperty,
nullptr, /* delProperty */
nullptr, /* getProperty */
nullptr, /* setProperty */
nullptr, /* enumerate */
@@ -3300,17 +3327,19 @@ const Class ArrayObject::class_ = {
nullptr, /* hasInstance */
nullptr, /* construct */
nullptr, /* trace */
{
GenericCreateConstructor<ArrayConstructor, 1, AllocKind::FUNCTION, &jit::JitInfo_Array>,
CreateArrayPrototype,
array_static_methods,
nullptr,
- array_methods
+ array_methods,
+ nullptr,
+ array_proto_finish
}
};
/*
* Array allocation functions.
*/
static inline bool
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -29,19 +29,16 @@ IdIsIndex(jsid id, uint32_t* indexp)
}
if (MOZ_UNLIKELY(!JSID_IS_STRING(id)))
return false;
return js::StringIsArrayIndex(JSID_TO_ATOM(id), indexp);
}
-extern JSObject*
-InitArrayClass(JSContext* cx, js::HandleObject obj);
-
// The methods below only create dense boxed arrays.
/* Create a dense array with no capacity allocated, length set to 0. */
extern ArrayObject * JS_FASTCALL
NewDenseEmptyArray(JSContext* cx, HandleObject proto = nullptr,
NewObjectKind newKind = GenericObject);
/*
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -927,18 +927,19 @@ CreateThisForFunctionWithGroup(JSContext
// Not enough objects with this group have been created yet, so make a
// plain object and register it with the group. Use the maximum number
// of fixed slots, as is also required by the TypeNewScript.
gc::AllocKind allocKind = GuessObjectGCKind(NativeObject::MAX_FIXED_SLOTS);
PlainObject* res = NewObjectWithGroup<PlainObject>(cx, group, allocKind, newKind);
if (!res)
return nullptr;
- if (newKind != SingletonObject)
- newScript->registerNewObject(res);
+ // Make sure group->newScript is still there.
+ if (newKind != SingletonObject && group->newScript())
+ group->newScript()->registerNewObject(res);
return res;
}
gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_);
if (newKind == SingletonObject) {
Rooted<TaggedProto> protoRoot(cx, group->proto());
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/unscopables.js
@@ -0,0 +1,51 @@
+let Array_unscopables = Array.prototype[Symbol.unscopables];
+
+let desc = Reflect.getOwnPropertyDescriptor(Array.prototype, Symbol.unscopables);
+assertDeepEq(desc, {
+ value: Array_unscopables,
+ writable: false,
+ enumerable: false,
+ configurable: true
+});
+
+assertEq(Reflect.getPrototypeOf(Array_unscopables), null);
+
+let desc2 = Object.getOwnPropertyDescriptor(Array_unscopables, "values");
+assertDeepEq(desc2, {
+ value: true,
+ writable: true,
+ enumerable: true,
+ configurable: true
+});
+
+let keys = Reflect.ownKeys(Array_unscopables);
+print(uneval(keys));
+assertDeepEq(keys, [
+ "copyWithin",
+ "entries",
+ "fill",
+ "find",
+ "findIndex",
+ "includes",
+ "keys",
+ "values"
+]);
+
+for (let key of keys)
+ assertEq(Array_unscopables[key], true);
+
+// Test that it actually works
+assertThrowsInstanceOf(() => {
+ with ([]) {
+ return entries;
+ }
+}, ReferenceError);
+
+{
+ let fill = 33;
+ with (Array.prototype) {
+ assertEq(fill, 33);
+ }
+}
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/values.js
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+assertEq(Array.prototype.values, Array.prototype[Symbol.iterator]);
+assertEq(Array.prototype.values.name, "values");
+assertEq(Array.prototype.values.length, 0);
+
+function valuesUnscopeable() {
+ var values = "foo";
+ with ([1, 2, 3]) {
+ assertEq(indexOf, Array.prototype.indexOf);
+ assertEq(values, "foo");
+ }
+}
+valuesUnscopeable();
+
+if (typeof reportCompare === 'function')
+ reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-basics.js
@@ -0,0 +1,22 @@
+// Basics of @@unscopables support.
+
+// In with(obj), if obj[@@unscopables][id] is truthy, then the identifier id
+// is not present as a binding in the with-block's scope.
+var x = "global";
+with ({x: "with", [Symbol.unscopables]: {x: true}})
+ assertEq(x, "global");
+
+// But if obj[@@unscopables][id] is false or not present, there is a binding.
+with ({y: "with", z: "with", [Symbol.unscopables]: {y: false}}) {
+ assertEq(y, "with");
+ assertEq(z, "with");
+}
+
+// ToBoolean(obj[@@unscopables][id]) determines whether there's a binding.
+let someValues = [0, -0, NaN, "", undefined, null, "x", {}, []];
+for (let v of someValues) {
+ with ({x: "with", [Symbol.unscopables]: {x: v}})
+ assertEq(x, v ? "global" : "with");
+}
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-closures.js
@@ -0,0 +1,23 @@
+// @@unscopables continues to work after exiting the relevant `with` block,
+// if the environment is captured by a closure.
+
+let env = {
+ x: 9000,
+ [Symbol.unscopables]: {x: true}
+};
+
+function make_adder(x) {
+ with (env)
+ return function (y) { return x + y; };
+}
+assertEq(make_adder(3)(10), 13);
+
+// Same test, but with a bunch of different parts for bad luck
+let x = 500;
+function make_adder_with_eval() {
+ with (env)
+ return eval('y => eval("x + y")');
+}
+assertEq(make_adder_with_eval()(10), 510);
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-const.js
@@ -0,0 +1,8 @@
+// @@unscopables prevents a property from having any effect on assigning to a
+// const binding (which is an error).
+
+const x = 1;
+with ({x: 1, [Symbol.unscopables]: {x: true}})
+ assertThrowsInstanceOf(() => {x = 2;}, TypeError);
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-delete.js
@@ -0,0 +1,27 @@
+// If obj[@@unscopables][id], then `delete id` works across `with (obj)` scope.
+
+this.niche = 7;
+let obj = { niche: 8, [Symbol.unscopables]: { niche: true } };
+with (obj) {
+ delete niche;
+}
+
+assertEq(obj.niche, 8);
+assertEq("niche" in this, false);
+
+// Same thing, but delete a variable introduced by sloppy direct eval.
+this.niche = 9;
+function f() {
+ eval("var niche = 10;");
+ with (obj) {
+ assertEq(niche, 10);
+ delete niche;
+ }
+ assertEq(niche, 9);
+}
+
+// Of course none of this affects a qualified delete.
+assertEq(delete this.niche, true);
+assertEq("niche" in this, false);
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-getters.js
@@ -0,0 +1,41 @@
+// @@unscopables checks can call getters.
+
+// The @@unscopables property itself can be a getter.
+let hit1 = 0;
+let x = "global x";
+let env1 = {
+ x: "env1.x",
+ get [Symbol.unscopables]() {
+ hit1++;
+ return {x: true};
+ }
+};
+with (env1)
+ assertEq(x, "global x");
+assertEq(hit1, 1);
+
+// It can throw; the exception is propagated out.
+function Fit() {}
+with ({x: 0, get [Symbol.unscopables]() { throw new Fit; }})
+ assertThrowsInstanceOf(() => x, Fit);
+
+// Individual properties on the @@unscopables object can have getters.
+let hit2 = 0;
+let env2 = {
+ x: "env2.x",
+ [Symbol.unscopables]: {
+ get x() {
+ hit2++;
+ return true;
+ }
+ }
+};
+with (env2)
+ assertEq(x, "global x");
+assertEq(hit2, 1);
+
+// And they can throw.
+with ({x: 0, [Symbol.unscopables]: {get x() { throw new Fit; }}})
+ assertThrowsInstanceOf(() => x, Fit);
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-global.js
@@ -0,0 +1,18 @@
+// @@unscopables does not affect the global environment.
+
+this.x = "global property x";
+let y = "global lexical y";
+this[Symbol.unscopables] = {x: true, y: true};
+assertEq(x, "global property x");
+assertEq(y, "global lexical y");
+assertEq(eval("x"), "global property x");
+assertEq(eval("y"), "global lexical y");
+
+// But it does affect `with` statements targeting the global object.
+{
+ let x = "local x";
+ with (this)
+ assertEq(x, "local x");
+}
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-ignored.js
@@ -0,0 +1,22 @@
+// In these cases, @@unscopables should not be consulted.
+
+// Because obj has no properties `assertEq` or `x`,
+// obj[@@unscopables] is not checked here:
+var obj = {
+ get [Symbol.unscopables]() {
+ throw "tried to read @@unscopables";
+ }
+};
+var x = 3;
+with (obj)
+ assertEq(x, 3);
+
+// If @@unscopables is present but not an object, it is ignored:
+for (let nonObject of [undefined, null, "nothing", Symbol.for("moon")]) {
+ let y = 4;
+ let obj2 = {[Symbol.unscopables]: nonObject, y: 5};
+ with (obj2)
+ assertEq(y, 5);
+}
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-miss.js
@@ -0,0 +1,7 @@
+// Trying to access a binding that doesn't exist due to @@unscopables
+// is a ReferenceError.
+
+with ({x: 1, [Symbol.unscopables]: {x: true}})
+ assertThrowsInstanceOf(() => x, ReferenceError);
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-mutation-frozen.js
@@ -0,0 +1,18 @@
+// When env[@@unscopables].x changes, bindings can appear even if env is inextensible.
+
+let x = "global";
+let unscopables = {x: true};
+let env = Object.create(null);
+env[Symbol.unscopables] = unscopables;
+env.x = "object";
+Object.freeze(env);
+
+for (let i = 0; i < 1004; i++) {
+ if (i === 1000)
+ unscopables.x = false;
+ with (env) {
+ assertEq(x, i < 1000 ? "global" : "object");
+ }
+}
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-mutation.js
@@ -0,0 +1,44 @@
+// When obj[@@unscopables].x changes, bindings appear and disappear accordingly.
+
+let x = "global";
+function getX() { return x; }
+
+let unscopables = {x: true};
+let obj = {x: "obj", [Symbol.unscopables]: unscopables};
+
+with (obj) {
+ assertEq(x, "global");
+ x = "global-1";
+ assertEq(x, "global-1");
+ assertEq(obj.x, "obj");
+
+ unscopables.x = false; // suddenly x appears in the with-environment
+
+ assertEq(x, "obj");
+ x = "obj-1";
+ assertEq(getX(), "global-1"); // unchanged
+ assertEq(obj.x, "obj-1");
+
+ unscopables.x = true; // *poof*
+
+ assertEq(x, "global-1");
+ x = "global-2";
+ assertEq(getX(), "global-2");
+ assertEq(obj.x, "obj-1"); // unchanged
+
+ // The determination of which binding is assigned happens when the LHS of
+ // assignment is evaluated, before the RHS. This is observable if we make
+ // the binding appear or disappear during evaluation of the RHS, before
+ // assigning.
+ x = (unscopables.x = false, "global-3");
+ assertEq(getX(), "global-3");
+ assertEq(obj.x, "obj-1");
+
+ x = (unscopables.x = true, "obj-2");
+ assertEq(getX(), "global-3");
+ assertEq(obj.x, "obj-2");
+}
+
+assertEq(x, "global-3");
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-proto.js
@@ -0,0 +1,39 @@
+// @@unscopables treats properties found on prototype chains the same as other
+// properties.
+
+const x = "global x";
+const y = "global y";
+
+// obj[@@unscopables].x works when obj.x is inherited via the prototype chain.
+let proto = {x: "object x", y: "object y"};
+let env = Object.create(proto);
+env[Symbol.unscopables] = {x: true, y: false};
+with (env) {
+ assertEq(x, "global x");
+ assertEq(delete x, false);
+ assertEq(y, "object y");
+}
+assertEq(env.x, "object x");
+
+// @@unscopables works if is inherited via the prototype chain.
+env = {
+ x: "object",
+ [Symbol.unscopables]: {x: true, y: true}
+};
+for (let i = 0; i < 50; i++)
+ env = Object.create(env);
+env.y = 1;
+with (env) {
+ assertEq(x, "global x");
+ assertEq(y, "global y");
+}
+
+// @@unscopables works if the obj[@@unscopables][id] property is inherited.
+env = {
+ x: "object",
+ [Symbol.unscopables]: Object.create({x: true})
+};
+with (env)
+ assertEq(x, "global x");
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-proxy.js
@@ -0,0 +1,46 @@
+// Object operations are performed in the right order, as observed by proxies.
+
+let log = [];
+function LoggingProxyHandlerWrapper(name, handler={}) {
+ return new Proxy(handler, {
+ get(t, id) {
+ let method = handler[id];
+ return function (...args) {
+ log.push([name + "." + id, ...args.filter(v => typeof v !== "object")]);
+ if (method === undefined)
+ return Reflect[id].apply(null, args);
+ return method.apply(this, args);
+ };
+ }
+ });
+}
+
+function LoggingProxy(name, target) {
+ return new Proxy(target, new LoggingProxyHandlerWrapper(name));
+}
+
+let proto = {x: 44};
+let proto_proxy = new LoggingProxy("proto", proto);
+let unscopables = {x: true};
+let unscopables_proxy = new LoggingProxy("unscopables", {x: true});
+let env = Object.create(proto_proxy, {
+ [Symbol.unscopables]: { value: unscopables_proxy }
+});
+let env_proxy = new LoggingProxy("env", env);
+
+let x = 11;
+function f() {
+ with (env_proxy)
+ return x;
+}
+
+assertEq(f(), 11);
+
+assertDeepEq(log, [
+ ["env.has", "x"],
+ ["proto.has", "x"],
+ ["env.get", Symbol.unscopables],
+ ["unscopables.get", "x"]
+]);
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-strict.js
@@ -0,0 +1,32 @@
+// Strict assignment to the name of a property that's masked by @@unscopables
+// throws a ReferenceError.
+
+let env = {k: 1};
+let f;
+with (env) {
+ f = function () {
+ "use strict";
+ k = 2;
+ };
+}
+
+f();
+assertEq(env.k, 2);
+
+env[Symbol.unscopables] = {k: true};
+assertThrowsInstanceOf(f, ReferenceError);
+
+// @@unscopables is tested when the LHS of assignment is evaluated, so there is
+// no effect on the assignment if it is changed while evaluating the RHS.
+let g;
+with (env) {
+ g = function () {
+ "use strict";
+ k = (env[Symbol.unscopables].k = true, 3);
+ }
+}
+env[Symbol.unscopables].k = false;
+g();
+assertEq(env.k, 3);
+
+reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/unscopables-tdz.js
@@ -0,0 +1,9 @@
+// Accessing an uninitialized variable due to @@unscopables is still a ReferenceError.
+
+with ({x: 1, [Symbol.unscopables]: {x: true}})
+ assertThrowsInstanceOf(() => x, ReferenceError);
+
+let x;
+
+reportCompare(0, 0);
+
--- a/js/src/tests/ecma_6/Symbol/well-known.js
+++ b/js/src/tests/ecma_6/Symbol/well-known.js
@@ -1,15 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/ */
var names = [
"iterator",
"match",
"species",
+ "toPrimitive",
+ "unscopables"
];
for (var name of names) {
// Well-known symbols exist.
assertEq(typeof Symbol[name], "symbol");
// They are never in the registry.
assertEq(Symbol[name] !== Symbol.for("Symbol." + name), true);
--- a/js/src/tests/lib/tasks_unix.py
+++ b/js/src/tests/lib/tasks_unix.py
@@ -1,13 +1,13 @@
# A unix-oriented process dispatcher. Uses a single thread with select and
# waitpid to dispatch tasks. This avoids several deadlocks that are possible
# with fork/exec + threads + Python.
-import errno, os, select
+import errno, os, select, sys
from datetime import datetime, timedelta
from progressbar import ProgressBar
from results import NullTestOutput, TestOutput, escape_cmdline
class Task(object):
def __init__(self, test, prefix, pid, stdout, stderr):
self.test = test
self.cmd = test.get_command(prefix)
--- a/js/src/tests/user.js
+++ b/js/src/tests/user.js
@@ -20,13 +20,16 @@ user_pref("extensions.checkUpdateSecurit
user_pref("browser.EULA.override", true);
user_pref("javascript.options.strict", false);
user_pref("javascript.options.werror", false);
user_pref("toolkit.startup.max_resumed_crashes", -1);
user_pref("security.turn_off_all_security_so_that_viruses_can_take_over_this_computer", true);
user_pref("toolkit.telemetry.enabled", false);
user_pref("browser.safebrowsing.enabled", false);
user_pref("browser.safebrowsing.malware.enabled", false);
+user_pref("browser.safebrowsing.forbiddenURIs.enabled", false);
+user_pref("privacy.trackingprotection.enabled", false);
+user_pref("privacy.trackingprotection.pbmode.enabled", false);
user_pref("browser.snippets.enabled", false);
user_pref("browser.snippets.syncPromo.enabled", false);
user_pref("browser.snippets.firstrunHomepage.enabled", false);
user_pref("general.useragent.updates.enabled", false);
user_pref("browser.webapps.checkForUpdates", 0);
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -51,16 +51,17 @@
macro(collections, collections, "collections") \
macro(columnNumber, columnNumber, "columnNumber") \
macro(comma, comma, ",") \
macro(compare, compare, "compare") \
macro(configurable, configurable, "configurable") \
macro(construct, construct, "construct") \
macro(constructor, constructor, "constructor") \
macro(ConvertAndCopyTo, ConvertAndCopyTo, "ConvertAndCopyTo") \
+ macro(copyWithin, copyWithin, "copyWithin") \
macro(count, count, "count") \
macro(CreateResolvingFunctions, CreateResolvingFunctions, "CreateResolvingFunctions") \
macro(currency, currency, "currency") \
macro(currencyDisplay, currencyDisplay, "currencyDisplay") \
macro(DateTimeFormat, DateTimeFormat, "DateTimeFormat") \
macro(DateTimeFormatFormatGet, DateTimeFormatFormatGet, "Intl_DateTimeFormat_format_get") \
macro(DateTimeFormatFormatToPartsGet, DateTimeFormatFormatToPartsGet, "Intl_DateTimeFormat_formatToParts_get") \
macro(day, day, "day") \
@@ -81,25 +82,29 @@
macro(dotThis, dotThis, ".this") \
macro(each, each, "each") \
macro(elementType, elementType, "elementType") \
macro(empty, empty, "") \
macro(emptyRegExp, emptyRegExp, "(?:)") \
macro(encodeURI, encodeURI, "encodeURI") \
macro(encodeURIComponent, encodeURIComponent, "encodeURIComponent") \
macro(endTimestamp, endTimestamp, "endTimestamp") \
+ macro(entries, entries, "entries") \
macro(enumerable, enumerable, "enumerable") \
macro(enumerate, enumerate, "enumerate") \
macro(era, era, "era") \
macro(escape, escape, "escape") \
macro(eval, eval, "eval") \
macro(false, false_, "false") \
macro(fieldOffsets, fieldOffsets, "fieldOffsets") \
macro(fieldTypes, fieldTypes, "fieldTypes") \
macro(fileName, fileName, "fileName") \
+ macro(fill, fill, "fill") \
+ macro(find, find, "find") \
+ macro(findIndex, findIndex, "findIndex") \
macro(fix, fix, "fix") \
macro(flags, flags, "flags") \
macro(float32, float32, "float32") \
macro(Float32x4, Float32x4, "Float32x4") \
macro(float64, float64, "float64") \
macro(Float64x2, Float64x2, "Float64x2") \
macro(forceInterpreter, forceInterpreter, "forceInterpreter") \
macro(forEach, forEach, "forEach") \
@@ -117,16 +122,17 @@
macro(global, global, "global") \
macro(Handle, Handle, "Handle") \
macro(has, has, "has") \
macro(hasOwn, hasOwn, "hasOwn") \
macro(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \
macro(hour, hour, "hour") \
macro(ignoreCase, ignoreCase, "ignoreCase") \
macro(ignorePunctuation, ignorePunctuation, "ignorePunctuation") \
+ macro(includes, includes, "includes") \
macro(index, index, "index") \
macro(InitializeCollator, InitializeCollator, "InitializeCollator") \
macro(InitializeDateTimeFormat, InitializeDateTimeFormat, "InitializeDateTimeFormat") \
macro(InitializeNumberFormat, InitializeNumberFormat, "InitializeNumberFormat") \
macro(inNursery, inNursery, "inNursery") \
macro(innermost, innermost, "innermost") \
macro(input, input, "input") \
macro(Int8x16, Int8x16, "Int8x16") \
@@ -294,21 +300,21 @@
macro(null, null, "null") \
macro(symbol, symbol, "symbol") \
/* Well-known atom names must be continuous and ordered, matching \
* enum JS::SymbolCode in jsapi.h. */ \
macro(iterator, iterator, "iterator") \
macro(match, match, "match") \
macro(species, species, "species") \
macro(toPrimitive, toPrimitive, "toPrimitive") \
+ macro(unscopables, unscopables, "unscopables") \
/* Same goes for the descriptions of the well-known symbols. */ \
macro(Symbol_hasInstance, Symbol_hasInstance, "Symbol.hasInstance") \
macro(Symbol_isConcatSpreadable, Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") \
macro(Symbol_iterator, Symbol_iterator, "Symbol.iterator") \
macro(Symbol_match, Symbol_match, "Symbol.match") \
macro(Symbol_species, Symbol_species, "Symbol.species") \
macro(Symbol_toPrimitive, Symbol_toPrimitive, "Symbol.toPrimitive") \
- macro(Symbol_toStringTag, Symbol_toStringTag, "Symbol.toStringTag") \
macro(Symbol_unscopables, Symbol_unscopables, "Symbol.unscopables") \
/* Function names for properties named by symbols. */ \
macro(Symbol_iterator_fun, Symbol_iterator_fun, "[Symbol.iterator]") \
#endif /* vm_CommonPropertyNames_h */
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -144,121 +144,16 @@ ValueToIdentifier(JSContext* cx, HandleV
ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
JSDVG_SEARCH_STACK, val, nullptr, "not an identifier",
nullptr);
return false;
}
return true;
}
-/*
- * A range of all the Debugger.Frame objects for a particular AbstractFramePtr.
- *
- * FIXME This checks only current debuggers, so it relies on a hack in
- * Debugger::removeDebuggeeGlobal to make sure only current debuggers
- * have Frame objects with .live === true.
- */
-class Debugger::FrameRange
-{
- AbstractFramePtr frame;
-
- /* The debuggers in |fp|'s compartment, or nullptr if there are none. */
- GlobalObject::DebuggerVector* debuggers;
-
- /*
- * The index of the front Debugger.Frame's debugger in debuggers.
- * nextDebugger < debuggerCount if and only if the range is not empty.
- */
- size_t debuggerCount, nextDebugger;
-
- /*
- * If the range is not empty, this is front Debugger.Frame's entry in its
- * debugger's frame table.
- */
- FrameMap::Ptr entry;
-
- public:
- /*
- * Return a range containing all Debugger.Frame instances referring to
- * |fp|. |global| is |fp|'s global object; if nullptr or omitted, we
- * compute it ourselves from |fp|.
- *
- * We keep an index into the compartment's debugger list, and a
- * FrameMap::Ptr into the current debugger's frame map. Thus, if the set of
- * debuggers in |fp|'s compartment changes, this range becomes invalid.
- * Similarly, if stack frames are added to or removed from frontDebugger(),
- * then the range's front is invalid until popFront is called.
- */
- explicit FrameRange(AbstractFramePtr frame, GlobalObject* global = nullptr)
- : frame(frame)
- {
- nextDebugger = 0;
-
- /* Find our global, if we were not given one. */
- if (!global)
- global = &frame.script()->global();
-
- /* The frame and global must match. */
- MOZ_ASSERT(&frame.script()->global() == global);
-
- /* Find the list of debuggers we'll iterate over. There may be none. */
- debuggers = global->getDebuggers();
- if (debuggers) {
- debuggerCount = debuggers->length();
- findNext();
- } else {
- debuggerCount = 0;
- }
- }
-
- bool empty() const {
- return nextDebugger >= debuggerCount;
- }
-
- NativeObject* frontFrame() const {
- MOZ_ASSERT(!empty());
- return entry->value();
- }
-
- Debugger* frontDebugger() const {
- MOZ_ASSERT(!empty());
- return (*debuggers)[nextDebugger];
- }
-
- /*
- * Delete the front frame from its Debugger's frame map. After this call,
- * the range's front is invalid until popFront is called.
- */
- void removeFrontFrame() const {
- MOZ_ASSERT(!empty());
- frontDebugger()->frames.remove(entry);
- }
-
- void popFront() {
- MOZ_ASSERT(!empty());
- nextDebugger++;
- findNext();
- }
-
- private:
- /*
- * Either make this range refer to the first appropriate Debugger.Frame at
- * or after nextDebugger, or make it empty.
- */
- void findNext() {
- while (!empty()) {
- Debugger* dbg = (*debuggers)[nextDebugger];
- entry = dbg->frames.lookup(frame);
- if (entry)
- break;
- nextDebugger++;
- }
- }
-};
-
class AutoRestoreCompartmentDebugMode
{
JSCompartment* comp_;
unsigned bits_;
public:
explicit AutoRestoreCompartmentDebugMode(JSCompartment* comp)
: comp_(comp), bits_(comp->debugModeBits)
@@ -741,54 +636,45 @@ DebuggerFrame_freeScriptFrameIterData(Fr
/*
* Handle leaving a frame with debuggers watching. |frameOk| indicates whether
* the frame is exiting normally or abruptly. Set |cx|'s exception and/or
* |cx->fp()|'s return value, and return a new success value.
*/
/* static */ bool
Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc, bool frameOk)
{
- Handle<GlobalObject*> global = cx->global();
+ mozilla::DebugOnly<Handle<GlobalObject*>> debuggeeGlobal = cx->global();
+
+ auto frameMapsGuard = MakeScopeExit([&] {
+ // Clean up all Debugger.Frame instances.
+ removeFromFrameMapsAndClearBreakpointsIn(cx, frame);
+ });
// The onPop handler and associated clean up logic should not run multiple
// times on the same frame. If slowPathOnLeaveFrame has already been
// called, the frame will not be present in the Debugger frame maps.
- FrameRange frameRange(frame, global);
- if (frameRange.empty())
+ Rooted<DebuggerFrameVector> frames(cx, DebuggerFrameVector(cx));
+ if (!getDebuggerFrames(frame, &frames))
+ return false;
+ if (frames.empty())
return frameOk;
- auto frameMapsGuard = MakeScopeExit([&] {
- // Clean up all Debugger.Frame instances. This call creates a fresh
- // FrameRange, as one debugger's onPop handler could have caused another
- // debugger to create its own Debugger.Frame instance.
- removeFromFrameMapsAndClearBreakpointsIn(cx, frame);
- });
-
/* Save the frame's completion value. */
JSTrapStatus status;
RootedValue value(cx);
Debugger::resultToCompletion(cx, frameOk, frame.returnValue(), &status, &value);
// This path can be hit via unwinding the stack due to over-recursion or
// OOM. In those cases, don't fire the frames' onPop handlers, because
// invoking JS will only trigger the same condition. See
// slowPathOnExceptionUnwind.
if (!cx->isThrowingOverRecursed() && !cx->isThrowingOutOfMemory()) {
- /* Build a list of the recipients. */
- AutoObjectVector frames(cx);
- for (; !frameRange.empty(); frameRange.popFront()) {
- if (!frames.append(frameRange.frontFrame())) {
- cx->clearPendingException();
- return false;
- }
- }
-
/* For each Debugger.Frame, fire its onPop handler, if any. */
- for (JSObject** p = frames.begin(); p != frames.end(); p++) {
- RootedNativeObject frameobj(cx, &(*p)->as<NativeObject>());
+ for (size_t i = 0; i < frames.length(); i++) {
+ HandleNativeObject frameobj = frames[i];
Debugger* dbg = Debugger::fromChildJSObject(frameobj);
EnterDebuggeeNoExecute nx(cx, *dbg);
if (dbg->enabled &&
!frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined())
{
RootedValue handler(cx, frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER));
@@ -808,17 +694,17 @@ Debugger::slowPathOnLeaveFrame(JSContext
RootedValue nextValue(cx);
JSTrapStatus nextStatus = dbg->parseResumptionValue(ac, hookOk, rval,
frame, pc, &nextValue);
/*
* At this point, we are back in the debuggee compartment, and any error has
* been wrapped up as a completion value.
*/
- MOZ_ASSERT(cx->compartment() == global->compartment());
+ MOZ_ASSERT(cx->compartment() == debuggeeGlobal->compartment());
MOZ_ASSERT(!cx->isExceptionPending());
/* JSTRAP_CONTINUE means "make no change". */
if (nextStatus != JSTRAP_CONTINUE) {
status = nextStatus;
value = nextValue;
}
}
@@ -1725,25 +1611,19 @@ Debugger::onSingleStep(JSContext* cx, Mu
return JSTRAP_ERROR;
cx->clearPendingException();
}
/*
* Build list of Debugger.Frame instances referring to this frame with
* onStep handlers.
*/
- AutoObjectVector frames(cx);
- for (FrameRange r(iter.abstractFramePtr()); !r.empty(); r.popFront()) {
- NativeObject* frame = r.frontFrame();
- if (!frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() &&
- !frames.append(frame))
- {
- return JSTRAP_ERROR;
- }
- }
+ Rooted<DebuggerFrameVector> frames(cx, DebuggerFrameVector(cx));
+ if (!getDebuggerFrames(iter.abstractFramePtr(), &frames))
+ return JSTRAP_ERROR;
#ifdef DEBUG
/*
* Validate the single-step count on this frame's script, to ensure that
* we're not receiving traps we didn't ask for. Even when frames is
* non-empty (and thus we know this trap was requested), do the check
* anyway, to make sure the count has the correct non-zero value.
*
@@ -1767,19 +1647,22 @@ Debugger::onSingleStep(JSContext* cx, Mu
}
}
}
}
MOZ_ASSERT(stepperCount == trappingScript->stepModeCount());
}
#endif
- /* Call all the onStep handlers we found. */
- for (JSObject** p = frames.begin(); p != frames.end(); p++) {
- RootedNativeObject frame(cx, &(*p)->as<NativeObject>());
+ // Call onStep for frames that have the handler set.
+ for (size_t i = 0; i < frames.length(); i++) {
+ HandleNativeObject frame = frames[i];
+ if (frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
+ continue;
+
Debugger* dbg = Debugger::fromChildJSObject(frame);
EnterDebuggeeNoExecute nx(cx, *dbg);
Maybe<AutoCompartment> ac;
ac.emplace(cx, dbg->object);
const Value& handler = frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER);
RootedValue rval(cx);
@@ -2188,18 +2071,17 @@ Debugger::updateExecutionObservabilityOf
oldestEnabledFrame = iter.abstractFramePtr();
oldestEnabledFrame.setIsDebuggee();
}
} else {
#ifdef DEBUG
// Debugger.Frame lifetimes are managed by the debug epilogue,
// so in general it's unsafe to unmark a frame if it has a
// Debugger.Frame associated with it.
- FrameRange r(iter.abstractFramePtr());
- MOZ_ASSERT(r.empty());
+ MOZ_ASSERT(!inFrameMaps(iter.abstractFramePtr()));
#endif
iter.abstractFramePtr().unsetIsDebuggee();
}
}
}
// See comment in unsetPrevUpToDateUntil.
if (oldestEnabledFrame) {
@@ -2314,16 +2196,41 @@ Debugger::updateExecutionObservabilityOf
for (ZoneRange r = obs.zones()->all(); !r.empty(); r.popFront()) {
if (!UpdateExecutionObservabilityOfScriptsInZone(cx, r.front(), obs, observing))
return false;
}
return true;
}
+template <typename FrameFn>
+/* static */ void
+Debugger::forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn)
+{
+ GlobalObject* global = &frame.script()->global();
+ if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
+ for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
+ Debugger* dbg = *p;
+ if (FrameMap::Ptr entry = dbg->frames.lookup(frame))
+ fn(entry->value());
+ }
+ }
+}
+
+/* static */ bool
+Debugger::getDebuggerFrames(AbstractFramePtr frame, MutableHandle<DebuggerFrameVector> frames)
+{
+ bool hadOOM = false;
+ forEachDebuggerFrame(frame, [&](NativeObject* frameobj) {
+ if (!hadOOM && !frames.append(frameobj))
+ hadOOM = true;
+ });
+ return !hadOOM;
+}
+
/* static */ bool
Debugger::updateExecutionObservability(JSContext* cx, ExecutionObservableSet& obs,
IsObserving observing)
{
if (!obs.singleZone() && obs.zones()->empty())
return true;
// Invalidate scripts first so we can set the needsArgsObj flag on scripts
@@ -5784,75 +5691,75 @@ Debugger::observesScript(JSScript* scrip
/* static */ bool
Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to,
ScriptFrameIter& iter)
{
auto removeFromDebuggerFramesOnExit = MakeScopeExit([&] {
// Remove any remaining old entries on exit, as the 'from' frame will
// be gone. On success, the range will be empty.
- for (Debugger::FrameRange r(from); !r.empty(); r.popFront()) {
- r.frontFrame()->setPrivate(nullptr);
- r.removeFrontFrame();
- }
+ Debugger::forEachDebuggerFrame(from, [&](NativeObject* frameobj) {
+ frameobj->setPrivate(nullptr);
+ Debugger::fromChildJSObject(frameobj)->frames.remove(from);
+ });
// Rekey missingScopes to maintain Debugger.Environment identity and
// forward liveScopes to point to the new frame.
DebugScopes::forwardLiveFrame(cx, from, to);
});
// Forward live Debugger.Frame objects.
- for (Debugger::FrameRange r(from); !r.empty(); r.popFront()) {
- RootedNativeObject frameobj(cx, r.frontFrame());
- Debugger* dbg = r.frontDebugger();
- MOZ_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
+ Rooted<DebuggerFrameVector> frames(cx, DebuggerFrameVector(cx));
+ if (!getDebuggerFrames(from, &frames))
+ return false;
+
+ for (size_t i = 0; i < frames.length(); i++) {
+ HandleNativeObject frameobj = frames[i];
+ Debugger* dbg = Debugger::fromChildJSObject(frameobj);
// Update frame object's ScriptFrameIter::data pointer.
DebuggerFrame_freeScriptFrameIterData(cx->runtime()->defaultFreeOp(), frameobj);
ScriptFrameIter::Data* data = iter.copyData();
if (!data)
return false;
frameobj->setPrivate(data);
- // Remove the old frame.
- r.removeFrontFrame();
+ // Remove old frame.
+ dbg->frames.remove(from);
// Add the frame object with |to| as key.
if (!dbg->frames.putNew(to, frameobj)) {
ReportOutOfMemory(cx);
return false;
}
}
return true;
}
/* static */ bool
Debugger::inFrameMaps(AbstractFramePtr frame)
{
- FrameRange r(frame);
- return !r.empty();
+ bool foundAny = false;
+ forEachDebuggerFrame(frame, [&](NativeObject* frameobj) { foundAny = true; });
+ return foundAny;
}
/* static */ void
Debugger::removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx, AbstractFramePtr frame)
{
- Handle<GlobalObject*> global = cx->global();
-
- for (FrameRange r(frame, global); !r.empty(); r.popFront()) {
- RootedNativeObject frameobj(cx, r.frontFrame());
- Debugger* dbg = r.frontDebugger();
- MOZ_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
+ forEachDebuggerFrame(frame, [&](NativeObject* frameobj) {
+ Debugger* dbg = Debugger::fromChildJSObject(frameobj);
FreeOp* fop = cx->runtime()->defaultFreeOp();
DebuggerFrame_freeScriptFrameIterData(fop, frameobj);
DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj);
dbg->frames.remove(frame);
- }
+ });
/*
* If this is an eval frame, then from the debugger's perspective the
* script is about to be destroyed. Remove any breakpoints in it.
*/
if (frame.isEvalFrame()) {
RootedScript script(cx, frame.script());
script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), nullptr, nullptr);
@@ -9040,16 +8947,24 @@ DebuggerEnv_getVariable(JSContext* cx, u
RootedValue v(cx);
{
Maybe<AutoCompartment> ac;
ac.emplace(cx, env);
/* This can trigger getters. */
ErrorCopier ec(ac);
+ bool found;
+ if (!HasProperty(cx, env, id, &found))
+ return false;
+ if (!found) {
+ args.rval().setUndefined();
+ return true;
+ }
+
// For DebugScopeObjects, we get sentinel values for optimized out
// slots and arguments instead of throwing (the default behavior).
//
// See wrapDebuggeeValue for how the sentinel values are wrapped.
if (env->is<DebugScopeObject>()) {
if (!env->as<DebugScopeObject>().getMaybeSentinelValue(cx, id, &v))
return false;
} else {
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -473,17 +473,16 @@ class Debugger : private mozilla::Linked
*/
#ifdef NIGHTLY_BUILD
uint32_t traceLoggerLastDrainedSize;
uint32_t traceLoggerLastDrainedIteration;
#endif
uint32_t traceLoggerScriptedCallsLastDrainedSize;
uint32_t traceLoggerScriptedCallsLastDrainedIteration;
- class FrameRange;
class ScriptQuery;
class ObjectQuery;
bool addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> obj);
void removeDebuggeeGlobal(FreeOp* fop, GlobalObject* global,
WeakGlobalObjectSet::Enum* debugEnum);
/*
@@ -618,16 +617,28 @@ class Debugger : private mozilla::Linked
static void removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx, AbstractFramePtr frame);
static bool updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObservableSet& obs,
IsObserving observing);
static bool updateExecutionObservabilityOfScripts(JSContext* cx, const ExecutionObservableSet& obs,
IsObserving observing);
static bool updateExecutionObservability(JSContext* cx, ExecutionObservableSet& obs,
IsObserving observing);
+ template <typename FrameFn /* void (NativeObject*) */>
+ static void forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn);
+
+ /*
+ * Return a vector containing all Debugger.Frame instances referring to
+ * |frame|. |global| is |frame|'s global object; if nullptr or omitted, we
+ * compute it ourselves from |frame|.
+ */
+ using DebuggerFrameVector = GCVector<NativeObject*>;
+ static bool getDebuggerFrames(AbstractFramePtr frame,
+ MutableHandle<DebuggerFrameVector> frames);
+
public:
static bool ensureExecutionObservabilityOfOsrFrame(JSContext* cx, InterpreterFrame* frame);
// Public for DebuggerScript_setBreakpoint.
static bool ensureExecutionObservabilityOfScript(JSContext* cx, JSScript* script);
// Whether the Debugger instance needs to observe all non-AOT JS
// execution of its debugees.
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -442,20 +442,19 @@ namespace js {
* field is a smart pointer that's immutable once initialized.
* `rt->wellKnownSymbols->iterator` is convertible to Handle<Symbol*>.
*
* Well-known symbols are never GC'd. The description() of each well-known
* symbol is a permanent atom.
*/
struct WellKnownSymbols
{
- js::ImmutableSymbolPtr iterator;
- js::ImmutableSymbolPtr match;
- js::ImmutableSymbolPtr species;
- js::ImmutableSymbolPtr toPrimitive;
+#define DECLARE_SYMBOL(name) js::ImmutableSymbolPtr name;
+ JS_FOR_EACH_WELL_KNOWN_SYMBOL(DECLARE_SYMBOL)
+#undef DECLARE_SYMBOL
const ImmutableSymbolPtr& get(size_t u) const {
MOZ_ASSERT(u < JS::WellKnownSymbolLimit);
const ImmutableSymbolPtr* symbols = reinterpret_cast<const ImmutableSymbolPtr*>(this);
return symbols[u];
}
const ImmutableSymbolPtr& get(JS::SymbolCode code) const {
--- a/js/src/vm/SavedStacks-inl.h
+++ b/js/src/vm/SavedStacks-inl.h
@@ -17,12 +17,13 @@
// SavedFrame objects and the SavedFrame accessors themselves handle wrappers
// and use the original caller's compartment's principals to determine what
// level of data to present. Unwrapping and entering the referent's compartment
// would mess that up. See the module level documentation in
// `js/src/vm/SavedStacks.h` as well as the comments in `js/src/jsapi.h`.
inline void
js::AssertObjectIsSavedFrameOrWrapper(JSContext* cx, HandleObject stack)
{
- MOZ_ASSERT_IF(stack, js::SavedFrame::isSavedFrameOrWrapperAndNotProto(*stack));
+ if (stack)
+ MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameOrWrapperAndNotProto(*stack));
}
#endif // vm_SavedStacksInl_h
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -652,18 +652,23 @@ namespace {
class MOZ_STACK_CLASS AutoMaybeEnterFrameCompartment
{
public:
AutoMaybeEnterFrameCompartment(JSContext* cx,
HandleObject obj
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
- // Note that obj might be null here, since we're doing this
- // before UnwrapSavedFrame.
+
+ MOZ_RELEASE_ASSERT(cx->compartment());
+ if (obj)
+ MOZ_RELEASE_ASSERT(obj->compartment());
+
+ // Note that obj might be null here, since we're doing this before
+ // UnwrapSavedFrame.
if (obj && cx->compartment() != obj->compartment())
{
JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
if (subsumes && subsumes(cx->compartment()->principals(),
obj->compartment()->principals()))
{
ac_.emplace(cx, obj);
}
@@ -683,87 +688,107 @@ UnwrapSavedFrame(JSContext* cx, HandleOb
{
if (!obj)
return nullptr;
RootedObject savedFrameObj(cx, CheckedUnwrap(obj));
if (!savedFrameObj)
return nullptr;
- MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
+ MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
js::RootedSavedFrame frame(cx, &savedFrameObj->as<js::SavedFrame>());
return GetFirstSubsumedFrame(cx, frame, selfHosted, skippedAsync);
}
JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep,
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
{
+ AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ MOZ_RELEASE_ASSERT(cx->compartment());
+
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
if (!frame) {
sourcep.set(cx->runtime()->emptyString);
return SavedFrameResult::AccessDenied;
}
sourcep.set(frame->getSource());
return SavedFrameResult::Ok;
}
JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep,
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
{
+ AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ MOZ_RELEASE_ASSERT(cx->compartment());
MOZ_ASSERT(linep);
+
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
if (!frame) {
*linep = 0;
return SavedFrameResult::AccessDenied;
}
*linep = frame->getLine();
return SavedFrameResult::Ok;
}
JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp,
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
{
+ AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ MOZ_RELEASE_ASSERT(cx->compartment());
MOZ_ASSERT(columnp);
+
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
if (!frame) {
*columnp = 0;
return SavedFrameResult::AccessDenied;
}
*columnp = frame->getColumn();
return SavedFrameResult::Ok;
}
JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep,
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
{
+ AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ MOZ_RELEASE_ASSERT(cx->compartment());
+
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
if (!frame) {
namep.set(nullptr);
return SavedFrameResult::AccessDenied;
}
namep.set(frame->getFunctionDisplayName());
return SavedFrameResult::Ok;
}
JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep,
SavedFrameSelfHosted unused_ /* = SavedFrameSelfHosted::Include */)
{
+ AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ MOZ_RELEASE_ASSERT(cx->compartment());
+
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
bool skippedAsync;
// This function is always called with self-hosted frames excluded by
// GetValueIfNotCached in dom/bindings/Exceptions.cpp. However, we want
// to include them because our Promise implementation causes us to have
// the async cause on a self-hosted frame. So we just ignore the
// parameter and always include self-hosted frames.
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, SavedFrameSelfHosted::Include,
@@ -777,16 +802,20 @@ GetSavedFrameAsyncCause(JSContext* cx, H
asyncCausep.set(cx->names().Async);
return SavedFrameResult::Ok;
}
JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp,
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
{
+ AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ MOZ_RELEASE_ASSERT(cx->compartment());
+
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
if (!frame) {
asyncParentp.set(nullptr);
return SavedFrameResult::AccessDenied;
}
js::RootedSavedFrame parent(cx, frame->getParent());
@@ -806,16 +835,20 @@ GetSavedFrameAsyncParent(JSContext* cx,
asyncParentp.set(nullptr);
return SavedFrameResult::Ok;
}
JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp,
SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
{
+ AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ MOZ_RELEASE_ASSERT(cx->compartment());
+
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
if (!frame) {
parentp.set(nullptr);
return SavedFrameResult::AccessDenied;
}
js::RootedSavedFrame parent(cx, frame->getParent());
@@ -834,16 +867,20 @@ GetSavedFrameParent(JSContext* cx, Handl
else
parentp.set(nullptr);
return SavedFrameResult::Ok;
}
JS_PUBLIC_API(bool)
BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp, size_t indent)
{
+ AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ MOZ_RELEASE_ASSERT(cx->compartment());
+
js::StringBuffer sb(cx);
// Enter a new block to constrain the scope of possibly entering the stack's
// compartment. This ensures that when we finish the StringBuffer, we are
// back in the cx's original compartment, and fulfill our contract with
// callers to place the output string in the cx's current compartment.
{
AutoMaybeEnterFrameCompartment ac(cx, stack);
@@ -1008,16 +1045,17 @@ SavedStacks::init()
return frames.init() &&
pcLocationMap.init();
}
bool
SavedStacks::saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame, unsigned maxFrameCount)
{
MOZ_ASSERT(initialized());
+ MOZ_RELEASE_ASSERT(cx->compartment());
assertSameCompartment(cx, this);
if (creatingSavedFrame ||
cx->isExceptionPending() ||
!cx->global()->isStandardClassResolved(JSProto_Object))
{
frame.set(nullptr);
return true;
@@ -1028,21 +1066,22 @@ SavedStacks::saveCurrentStack(JSContext*
return insertFrames(cx, iter, frame, maxFrameCount);
}
bool
SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString asyncCause,
MutableHandleSavedFrame adoptedStack, unsigned maxFrameCount)
{
MOZ_ASSERT(initialized());
+ MOZ_RELEASE_ASSERT(cx->compartment());
assertSameCompartment(cx, this);
RootedObject asyncStackObj(cx, CheckedUnwrap(asyncStack));
- MOZ_ASSERT(asyncStackObj);
- MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*asyncStackObj));
+ MOZ_RELEASE_ASSERT(asyncStackObj);
+ MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*asyncStackObj));
RootedSavedFrame frame(cx, &asyncStackObj->as<js::SavedFrame>());
return adoptAsyncStack(cx, frame, asyncCause, adoptedStack, maxFrameCount);
}
void
SavedStacks::sweep()
{
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -750,44 +750,84 @@ DynamicWithObject::create(JSContext* cx,
obj->setEnclosingScope(enclosing);
obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object));
obj->setFixedSlot(THIS_SLOT, thisv);
obj->setFixedSlot(KIND_SLOT, Int32Value(kind));
return obj;
}
+/* Implements ES6 8.1.1.2.1 HasBinding steps 7-9. */
+static bool
+CheckUnscopables(JSContext *cx, HandleObject obj, HandleId id, bool *scopable)
+{
+ RootedId unscopablesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols()
+ .get(JS::SymbolCode::unscopables)));
+ RootedValue v(cx);
+ if (!GetProperty(cx, obj, obj, unscopablesId, &v))
+ return false;
+ if (v.isObject()) {
+ RootedObject unscopablesObj(cx, &v.toObject());
+ if (!GetProperty(cx, unscopablesObj, unscopablesObj, id, &v))
+ return false;
+ *scopable = !ToBoolean(v);
+ } else {
+ *scopable = true;
+ }
+ return true;
+}
+
static bool
with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
MutableHandleObject objp, MutableHandleShape propp)
{
if (JSID_IS_ATOM(id, cx->names().dotThis)) {
objp.set(nullptr);
propp.set(nullptr);
return true;
}
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
- return LookupProperty(cx, actual, id, objp, propp);
+ if (!LookupProperty(cx, actual, id, objp, propp))
+ return false;
+
+ if (propp) {
+ bool scopable;
+ if (!CheckUnscopables(cx, actual, id, &scopable))
+ return false;
+ if (!scopable) {
+ objp.set(nullptr);
+ propp.set(nullptr);
+ }
+ }
+ return true;
}
static bool
with_DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc,
ObjectOpResult& result)
{
MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
return DefineProperty(cx, actual, id, desc, result);
}
static bool
with_HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
{
MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
- return HasProperty(cx, actual, id, foundp);
+
+ // ES 8.1.1.2.1 step 3-5.
+ if (!HasProperty(cx, actual, id, foundp))
+ return false;
+ if (!*foundp)
+ return true;
+
+ // Steps 7-10. (Step 6 is a no-op.)
+ return CheckUnscopables(cx, actual, id, foundp);
}
static bool
with_GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
MutableHandleValue vp)
{
MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
@@ -2300,22 +2340,35 @@ class DebugScopeProxy : public BaseProxy
return false;
}
// DynamicWithObject isn't a very good proxy. It doesn't have a
// JSNewEnumerateOp implementation, because if it just delegated to the
// target object, the object would indicate that native enumeration is
// the thing to do, but native enumeration over the DynamicWithObject
// wrapper yields no properties. So instead here we hack around the
- // issue, and punch a hole through to the with object target.
- Rooted<JSObject*> target(cx, (scope->is<DynamicWithObject>()
- ? &scope->as<DynamicWithObject>().object() : scope));
+ // issue: punch a hole through to the with object target, then manually
+ // examine @@unscopables.
+ bool isWith = scope->is<DynamicWithObject>();
+ Rooted<JSObject*> target(cx, (isWith ? &scope->as<DynamicWithObject>().object() : scope));
if (!GetPropertyKeys(cx, target, JSITER_OWNONLY, &props))
return false;
+ if (isWith) {
+ size_t j = 0;
+ for (size_t i = 0; i < props.length(); i++) {
+ bool inScope;
+ if (!CheckUnscopables(cx, scope, props[i], &inScope))
+ return false;
+ if (inScope)
+ props[j++].set(props[i]);
+ }
+ props.resize(j);
+ }
+
/*
* Function scopes are optimized to not contain unaliased variables so
* they must be manually appended here.
*/
if (isFunctionScope(*scope)) {
RootedScript script(cx, scope->as<CallObject>().callee().nonLazyScript());
for (BindingIter bi(script); bi; bi++) {
if (!bi->aliased() && !props.append(NameToId(bi->name())))
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -185,17 +185,18 @@ https://bugzilla.mozilla.org/show_bug.cg
gConstructorProperties['Object'] =
gConstructorProperties['Object'].concat(["values", "entries"]);
}
gPrototypeProperties['Array'] =
["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
"pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
"includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find",
- "findIndex", "copyWithin", "fill", Symbol.iterator, "entries", "keys", "constructor"];
+ "findIndex", "copyWithin", "fill", Symbol.iterator, Symbol.unscopables, "entries", "keys",
+ "values", "constructor"];
gConstructorProperties['Array'] =
constructorProps(["join", "reverse", "sort", "push", "pop", "shift",
"unshift", "splice", "concat", "slice", "isArray",
"lastIndexOf", "indexOf", "forEach", "map", "filter",
"every", "some", "reduce", "reduceRight", "from", "of"]);
for (var c of typedArrayClasses) {
gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT"];
gConstructorProperties[c] = constructorProps(["BYTES_PER_ELEMENT"]);
@@ -373,17 +374,18 @@ https://bugzilla.mozilla.org/show_bug.cg
is(xrayProto, iwin[classname].prototype, "Xray proto is correct");
is(xrayProto, xray.__proto__, "Proto accessors agree");
var protoProto = classname == "Object" ? null : iwin.Object.prototype;
is(Object.getPrototypeOf(xrayProto), protoProto, "proto proto is correct");
testProtoCallables(protoCallables, xray, xrayProto, localProto);
is(Object.getOwnPropertyNames(xrayProto).sort().toSource(),
protoProps.toSource(), "getOwnPropertyNames works");
is(Object.getOwnPropertySymbols(xrayProto).map(uneval).sort().toSource(),
- gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(),
+ gPrototypeProperties[classname].filter(id => typeof id !== "string" && !propsToSkip.includes(id))
+ .map(uneval).sort().toSource(),
"getOwnPropertySymbols works");
is(xrayProto.constructor, iwin[classname], "constructor property works");
xrayProto.expando = 42;
is(xray.expando, 42, "Xrayed instances see proto expandos");
is(xray2.expando, 42, "Xrayed instances see proto expandos");
@@ -490,17 +492,22 @@ https://bugzilla.mozilla.org/show_bug.cg
function testArray() {
// The |length| property is generally very weird, especially with respect
// to its behavior on the prototype. Array.prototype is actually an Array
// instance, and therefore has a vestigial .length. But we don't want to
// show that over Xrays, and generally want .length to just appear as an
// |own| data property. So we add it to the ignore list here, and check it
// separately.
- let propsToSkip = ['length'];
+ //
+ // |Symbol.unscopables| should in principle be exposed, but it is
+ // inconvenient (as it's a data property, unsupported by ClassSpec) and
+ // low value.
+ let propsToSkip = ['length', Symbol.unscopables];
+
// On the constructor, we want to skip all the non-standard "generic"
// functions. We're trying to remove them anyway; no point doing extra work
// to expose them over Xrays.
let ctorPropsToSkip = ["join", "reverse", "sort", "push", "pop", "shift",
"unshift", "splice", "concat", "slice"];
testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip,
ctorPropsToSkip);
--- a/layout/base/FramePropertyTable.h
+++ b/layout/base/FramePropertyTable.h
@@ -1,18 +1,19 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */
#ifndef FRAMEPROPERTYTABLE_H_
#define FRAMEPROPERTYTABLE_H_
+#include "mozilla/MemoryReporting.h"
#include "mozilla/TypeTraits.h"
-#include "mozilla/MemoryReporting.h"
+#include "mozilla/unused.h"
#include "nsTArray.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
class nsIFrame;
namespace mozilla {
@@ -166,16 +167,42 @@ public:
template<typename T>
void Set(const nsIFrame* aFrame, Descriptor<T> aProperty,
PropertyType<T> aValue)
{
ReinterpretHelper<T> helper{};
helper.value = aValue;
SetInternal(aFrame, aProperty, helper.ptr);
}
+
+ /**
+ * @return true if @aProperty is set for @aFrame. This requires one hashtable
+ * lookup (using the frame as the key) and a linear search through the
+ * properties of that frame.
+ *
+ * In most cases, this shouldn't be used outside of assertions, because if
+ * you're doing a lookup anyway it would be far more efficient to call Get()
+ * or Remove() and check the aFoundResult outparam to find out whether the
+ * property is set. Legitimate non-assertion uses include:
+ *
+ * - Checking if a frame property is set in cases where that's all we want
+ * to know (i.e., we don't intend to read the actual value or remove the
+ * property).
+ *
+ * - Calling IsSet() before Set() in cases where we don't want to overwrite
+ * an existing value for the frame property.
+ */
+ template<typename T>
+ bool IsSet(const nsIFrame* aFrame, Descriptor<T> aProperty)
+ {
+ bool foundResult = false;
+ mozilla::Unused << GetInternal(aFrame, aProperty, &foundResult);
+ return foundResult;
+ }
+
/**
* Get a property value for a frame. This requires one hashtable
* lookup (using the frame as the key) and a linear search through
* the properties of that frame. If the frame has no such property,
* returns zero-filled result, which means null for pointers and
* zero for integers and floating point types.
* @param aFoundResult if non-null, receives a value 'true' iff
* the frame has a value for the property. This lets callers
@@ -355,16 +382,23 @@ public:
FrameProperties(FramePropertyTable* aTable, const nsIFrame* aFrame)
: mTable(aTable), mFrame(aFrame) {}
template<typename T>
void Set(Descriptor<T> aProperty, PropertyType<T> aValue) const
{
mTable->Set(mFrame, aProperty, aValue);
}
+
+ template<typename T>
+ bool IsSet(Descriptor<T> aProperty) const
+ {
+ return mTable->IsSet(mFrame, aProperty);
+ }
+
template<typename T>
PropertyType<T> Get(Descriptor<T> aProperty,
bool* aFoundResult = nullptr) const
{
return mTable->Get(mFrame, aProperty, aFoundResult);
}
template<typename T>
PropertyType<T> Remove(Descriptor<T> aProperty,
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -336,16 +336,25 @@ IsAnonymousFlexOrGridItem(const nsIFrame
static inline bool
IsFlexOrGridContainer(const nsIFrame* aFrame)
{
const nsIAtom* t = aFrame->GetType();
return t == nsGkAtoms::flexContainerFrame ||
t == nsGkAtoms::gridContainerFrame;
}
+// Returns true if aFrame has "display: -webkit-{inline-}box"
+static inline bool
+IsWebkitBox(const nsIFrame* aFrame)
+{
+ auto containerDisplay = aFrame->StyleDisplay()->mDisplay;
+ return containerDisplay == NS_STYLE_DISPLAY_WEBKIT_BOX ||
+ containerDisplay == NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX;
+}
+
#if DEBUG
static void
AssertAnonymousFlexOrGridItemParent(const nsIFrame* aChild,
const nsIFrame* aParent)
{
MOZ_ASSERT(IsAnonymousFlexOrGridItem(aChild),
"expected an anonymous flex or grid item child frame");
MOZ_ASSERT(aParent, "expected a parent frame");
@@ -2495,17 +2504,18 @@ nsCSSFrameConstructor::ConstructDocEleme
nsFrameItems frameItems;
contentFrame = static_cast<nsContainerFrame*>(
ConstructOuterSVG(state, item, mDocElementContainingBlock,
styleContext->StyleDisplay(),
frameItems));
newFrame = frameItems.FirstChild();
NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
- } else if (display->mDisplay == NS_STYLE_DISPLAY_FLEX) {
+ } else if (display->mDisplay == NS_STYLE_DISPLAY_FLEX ||
+ display->mDisplay == NS_STYLE_DISPLAY_WEBKIT_BOX) {
contentFrame = NS_NewFlexContainerFrame(mPresShell, styleContext);
InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
contentFrame);
newFrame = contentFrame;
processChildren = true;
} else if (display->mDisplay == NS_STYLE_DISPLAY_GRID) {
contentFrame = NS_NewGridContainerFrame(mPresShell, styleContext);
InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
@@ -4657,17 +4667,18 @@ nsCSSFrameConstructor::FindDisplayData(c
};
return &sNonScrollableBlockData[suppressScrollFrame][caption];
}
// If this is for a <body> node and we've propagated the scroll-frame to the
// viewport, we need to make sure not to add another layer of scrollbars, so
// we use a different FCData struct without FCDATA_MAY_NEED_SCROLLFRAME.
if (propagatedScrollToViewport && aDisplay->IsScrollableOverflow()) {
- if (aDisplay->mDisplay == NS_STYLE_DISPLAY_FLEX) {
+ if (aDisplay->mDisplay == NS_STYLE_DISPLAY_FLEX ||
+ aDisplay->mDisplay == NS_STYLE_DISPLAY_WEBKIT_BOX) {
static const FrameConstructionData sNonScrollableFlexData =
FCDATA_DECL(0, NS_NewFlexContainerFrame);
return &sNonScrollableFlexData;
}
if (aDisplay->mDisplay == NS_STYLE_DISPLAY_GRID) {
static const FrameConstructionData sNonScrollableGridData =
FCDATA_DECL(0, NS_NewGridContainerFrame);
return &sNonScrollableGridData;
@@ -4681,16 +4692,20 @@ nsCSSFrameConstructor::FindDisplayData(c
// XXXbz the "quickly" part is a bald-faced lie!
{ NS_STYLE_DISPLAY_INLINE,
FULL_CTOR_FCDATA(FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT,
&nsCSSFrameConstructor::ConstructInline) },
{ NS_STYLE_DISPLAY_FLEX,
FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) },
{ NS_STYLE_DISPLAY_INLINE_FLEX,
FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) },
+ { NS_STYLE_DISPLAY_WEBKIT_BOX,
+ FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) },
+ { NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX,
+ FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame) },
{ NS_STYLE_DISPLAY_GRID,
FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) },
{ NS_STYLE_DISPLAY_INLINE_GRID,
FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) },
{ NS_STYLE_DISPLAY_RUBY,
FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT,
NS_NewRubyFrame) },
{ NS_STYLE_DISPLAY_RUBY_BASE,
@@ -9741,20 +9756,23 @@ nsCSSFrameConstructor::CreateNeededAnonF
nsIFrame* aParentFrame)
{
if (aItems.IsEmpty() ||
!::IsFlexOrGridContainer(aParentFrame)) {
return;
}
nsIAtom* containerType = aParentFrame->GetType();
+ const bool isWebkitBox = IsWebkitBox(aParentFrame);
+
FCItemIterator iter(aItems);
do {
// Advance iter past children that don't want to be wrapped
- if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState, containerType)) {
+ if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState, containerType,
+ isWebkitBox)) {
// Hit the end of the items without finding any remaining children that
// need to be wrapped. We're finished!
return;
}
// If our next potentially-wrappable child is whitespace, then see if
// there's anything wrappable immediately after it. If not, we just drop
// the whitespace and move on. (We're not supposed to create any anonymous
@@ -9766,42 +9784,46 @@ nsCSSFrameConstructor::CreateNeededAnonF
// entirely whitespace*, then we technically should still skip over it, per
// the CSS grid & flexbox specs. I'm not bothering with that at this point,
// since it's a pretty extreme edge case.
if (!aParentFrame->IsGeneratedContentFrame() &&
iter.item().IsWhitespace(aState)) {
FCItemIterator afterWhitespaceIter(iter);
bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState);
bool nextChildNeedsAnonItem =
- !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(aState,
- containerType);
+ !hitEnd &&
+ afterWhitespaceIter.item().NeedsAnonFlexOrGridItem(aState,
+ containerType,
+ isWebkitBox);
if (!nextChildNeedsAnonItem) {
// There's nothing after the whitespace that we need to wrap, so we
// just drop this run of whitespace.
iter.DeleteItemsTo(afterWhitespaceIter);
if (hitEnd) {
// Nothing left to do -- we're finished!
return;
}
// else, we have a next child and it does not want to be wrapped. So,
// we jump back to the beginning of the loop to skip over that child
// (and anything else non-wrappable after it)
MOZ_ASSERT(!iter.IsDone() &&
- !iter.item().NeedsAnonFlexOrGridItem(aState, containerType),
+ !iter.item().NeedsAnonFlexOrGridItem(aState, containerType,
+ isWebkitBox),
"hitEnd and/or nextChildNeedsAnonItem lied");
continue;
}
}
// Now |iter| points to the first child that needs to be wrapped in an
// anonymous flex/grid item. Now we see how many children after it also want
// to be wrapped in an anonymous flex/grid item.
FCItemIterator endIter(iter); // iterator to find the end of the group
- endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState, containerType);
+ endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState, containerType,
+ isWebkitBox);
NS_ASSERTION(iter != endIter,
"Should've had at least one wrappable child to seek past");
// Now, we create the anonymous flex or grid item to contain the children
// between |iter| and |endIter|.
nsIAtom* pseudoType = containerType == nsGkAtoms::flexContainerFrame ?
nsCSSAnonBoxes::anonymousFlexItem : nsCSSAnonBoxes::anonymousGridItem;
@@ -12031,30 +12053,33 @@ nsCSSFrameConstructor::WipeContainingBlo
// new inline non-replaced children, adjacent to an existing anonymous
// flex or grid item.
if (::IsFlexOrGridContainer(aFrame)) {
FCItemIterator iter(aItems);
// Check if we're adding to-be-wrapped content right *after* an existing
// anonymous flex or grid item (which would need to absorb this content).
nsIAtom* containerType = aFrame->GetType();
+ bool isWebkitBox = IsWebkitBox(aFrame);
if (aPrevSibling && IsAnonymousFlexOrGridItem(aPrevSibling) &&
- iter.item().NeedsAnonFlexOrGridItem(aState, containerType)) {
+ iter.item().NeedsAnonFlexOrGridItem(aState, containerType,
+ isWebkitBox)) {
RecreateFramesForContent(aFrame->GetContent(), true,
REMOVE_FOR_RECONSTRUCTION, nullptr);
return true;
}
// Check if we're adding to-be-wrapped content right *before* an existing
// anonymous flex or grid item (which would need to absorb this content).
if (nextSibling && IsAnonymousFlexOrGridItem(nextSibling)) {
// Jump to the last entry in the list
iter.SetToEnd();
iter.Prev();
- if (iter.item().NeedsAnonFlexOrGridItem(aState, containerType)) {
+ if (iter.item().NeedsAnonFlexOrGridItem(aState, containerType,
+ isWebkitBox)) {
RecreateFramesForContent(aFrame->GetContent(), true,
REMOVE_FOR_RECONSTRUCTION, nullptr);
return true;
}
}
}
// Situation #3 is an anonymous flex or grid item that's getting new children
@@ -12071,17 +12096,18 @@ nsCSSFrameConstructor::WipeContainingBlo
nsFrameConstructorSaveState floatSaveState;
aState.PushFloatContainingBlock(nullptr, floatSaveState);
FCItemIterator iter(aItems);
// Skip over things that _do_ need an anonymous flex item, because
// they're perfectly happy to go here -- they won't cause a reframe.
nsIFrame* containerFrame = aFrame->GetParent();
if (!iter.SkipItemsThatNeedAnonFlexOrGridItem(aState,
- containerFrame->GetType())) {
+ containerFrame->GetType(),
+ IsWebkitBox(containerFrame))) {
// We hit something that _doesn't_ need an anonymous flex item!
// Rebuild the flex container to bust it out.
RecreateFramesForContent(containerFrame->GetContent(), true,
REMOVE_FOR_RECONSTRUCTION, nullptr);
return true;
}
// If we get here, then everything in |aItems| needs to be wrapped in
@@ -12523,20 +12549,24 @@ Iterator::SkipItemsNotWantingParentType(
Next();
if (IsDone()) {
return true;
}
}
return false;
}
+// Note: we implement -webkit-box & -webkit-inline-box using
+// nsFlexContainerFrame, but we use different rules for what gets wrapped in an
+// anonymous flex item.
bool
nsCSSFrameConstructor::FrameConstructionItem::
NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState,
- nsIAtom* aContainerType)
+ nsIAtom* aContainerType,
+ bool aIsWebkitBox)
{
if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) {
// This will be an inline non-replaced box.
return true;
}
// Bug 874718: Flex containers still wrap placeholders; Grid containers don't.
if (aContainerType == nsGkAtoms::flexContainerFrame &&
@@ -12544,43 +12574,53 @@ nsCSSFrameConstructor::FrameConstruction
aState.GetGeometricParent(mStyleContext->StyleDisplay(), nullptr)) {
// We're abspos or fixedpos, which means we'll spawn a placeholder which
// we'll need to wrap in an anonymous flex item. So, we just treat
// _this_ frame as if _it_ needs to be wrapped in an anonymous flex item,
// and then when we spawn the placeholder, it'll end up in the right spot.
return true;
}
+ if (aIsWebkitBox &&
+ mStyleContext->StyleDisplay()->IsInlineOutsideStyle()) {
+ // In a -webkit-box, all inline-level content gets wrapped in an anon item.
+ return true;
+ }
+
return false;
}
inline bool
nsCSSFrameConstructor::FrameConstructionItemList::
Iterator::SkipItemsThatNeedAnonFlexOrGridItem(
const nsFrameConstructorState& aState,
- nsIAtom* aContainerType)
+ nsIAtom* aContainerType,
+ bool aIsWebkitBox)
{
NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
- while (item().NeedsAnonFlexOrGridItem(aState, aContainerType)) {
+ while (item().NeedsAnonFlexOrGridItem(aState, aContainerType,
+ aIsWebkitBox)) {
Next();
if (IsDone()) {
return true;
}
}
return false;
}
inline bool
nsCSSFrameConstructor::FrameConstructionItemList::
Iterator::SkipItemsThatDontNeedAnonFlexOrGridItem(
const nsFrameConstructorState& aState,
- nsIAtom* aContainerType)
+ nsIAtom* aContainerType,
+ bool aIsWebkitBox)
{
NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
- while (!(item().NeedsAnonFlexOrGridItem(aState, aContainerType))) {
+ while (!(item().NeedsAnonFlexOrGridItem(aState, aContainerType,
+ aIsWebkitBox))) {
Next();
if (IsDone()) {
return true;
}
}
return false;
}
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -920,23 +920,25 @@ private:
// one. Return whether the iterator is done after doing that. The
// iterator must not be done when this is called.
inline bool SkipItemsNotWantingParentType(ParentType aParentType);
// Skip over non-replaced inline frames and positioned frames.
// Return whether the iterator is done after doing that.
// The iterator must not be done when this is called.
inline bool SkipItemsThatNeedAnonFlexOrGridItem(
- const nsFrameConstructorState& aState, nsIAtom* aContainerType);
+ const nsFrameConstructorState& aState, nsIAtom* aContainerType,
+ bool aIsWebkitBox);
// Skip to the first frame that is a non-replaced inline or is
// positioned. Return whether the iterator is done after doing that.
// The iterator must not be done when this is called.
inline bool SkipItemsThatDontNeedAnonFlexOrGridItem(
- const nsFrameConstructorState& aState, nsIAtom* aContainerType);
+ const nsFrameConstructorState& aState, nsIAtom* aContainerType,
+ bool aIsWebkitBox);
// Skip over all items that do not want a ruby parent. Return whether
// the iterator is done after doing that. The iterator must not be done
// when this is called.
inline bool SkipItemsNotWantingRubyParent();
// Skip over whitespace. Return whether the iterator is done after doing
// that. The iterator must not be done, and must be pointing to a
@@ -1062,19 +1064,23 @@ private:
}
}
ParentType DesiredParentType() {
return FCDATA_DESIRED_PARENT_TYPE(mFCData->mBits);
}
// Indicates whether (when in a flex or grid container) this item needs
- // to be wrapped in an anonymous block.
+ // to be wrapped in an anonymous block. (Note that we implement
+ // -webkit-box/-webkit-inline-box using our standard flexbox frame class,
+ // but we use different rules for what gets wrapped. The aIsWebkitBox
+ // parameter here tells us whether to use those different rules.)
bool NeedsAnonFlexOrGridItem(const nsFrameConstructorState& aState,
- nsIAtom* aContainerType);
+ nsIAtom* aContainerType,
+ bool aIsWebkitBox);
// Don't call this unless the frametree really depends on the answer!
// Especially so for generated content, where we don't want to reframe
// things.
bool IsWhitespace(nsFrameConstructorState& aState) const;
bool IsLineBoundary() const {
return mIsBlock || (mFCData->mBits & FCDATA_IS_LINE_BREAK);
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -137,16 +137,17 @@ using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::image;
using namespace mozilla::layers;
using namespace mozilla::layout;
using namespace mozilla::gfx;
#define GRID_ENABLED_PREF_NAME "layout.css.grid.enabled"
#define GRID_TEMPLATE_SUBGRID_ENABLED_PREF_NAME "layout.css.grid-template-subgrid-value.enabled"
+#define WEBKIT_PREFIXES_ENABLED_PREF_NAME "layout.css.prefixes.webkit"
#define DISPLAY_CONTENTS_ENABLED_PREF_NAME "layout.css.display-contents.enabled"
#define TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME "layout.css.text-align-unsafe-value.enabled"
#define FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME "layout.css.float-logical-values.enabled"
#ifdef DEBUG
// TODO: remove, see bug 598468.
bool nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
#endif // DEBUG
@@ -217,16 +218,63 @@ GridEnabledPrefChangeCallback(const char
isGridEnabled ? eCSSKeyword_grid : eCSSKeyword_UNKNOWN;
}
if (sIndexOfInlineGridInDisplayTable >= 0) {
nsCSSProps::kDisplayKTable[sIndexOfInlineGridInDisplayTable].mKeyword =
isGridEnabled ? eCSSKeyword_inline_grid : eCSSKeyword_UNKNOWN;
}
}
+// When the pref "layout.css.prefixes.webkit" changes, this function is invoked
+// to let us update kDisplayKTable, to selectively disable or restore the
+// entries for "-webkit-box" and "-webkit-inline-box" in that table.
+static void
+WebkitPrefixEnabledPrefChangeCallback(const char* aPrefName, void* aClosure)
+{
+ MOZ_ASSERT(strncmp(aPrefName, WEBKIT_PREFIXES_ENABLED_PREF_NAME,
+ ArrayLength(WEBKIT_PREFIXES_ENABLED_PREF_NAME)) == 0,
+ "We only registered this callback for a single pref, so it "
+ "should only be called for that pref");
+
+ static int32_t sIndexOfWebkitBoxInDisplayTable;
+ static int32_t sIndexOfWebkitInlineBoxInDisplayTable;
+ static bool sAreWebkitBoxKeywordIndicesInitialized; // initialized to false
+
+ bool isWebkitPrefixSupportEnabled =
+ Preferences::GetBool(WEBKIT_PREFIXES_ENABLED_PREF_NAME, false);
+ if (!sAreWebkitBoxKeywordIndicesInitialized) {
+ // First run: find the position of "-webkit-box" and "-webkit-inline-box"
+ // in kDisplayKTable.
+ sIndexOfWebkitBoxInDisplayTable =
+ nsCSSProps::FindIndexOfKeyword(eCSSKeyword__webkit_box,
+ nsCSSProps::kDisplayKTable);
+ MOZ_ASSERT(sIndexOfWebkitBoxInDisplayTable >= 0,
+ "Couldn't find -webkit-box in kDisplayKTable");
+ sIndexOfWebkitInlineBoxInDisplayTable =
+ nsCSSProps::FindIndexOfKeyword(eCSSKeyword__webkit_inline_box,
+ nsCSSProps::kDisplayKTable);
+ MOZ_ASSERT(sIndexOfWebkitInlineBoxInDisplayTable >= 0,
+ "Couldn't find -webkit-inline-box in kDisplayKTable");
+ sAreWebkitBoxKeywordIndicesInitialized = true;
+ }
+
+ // OK -- now, stomp on or restore the "-webkit-box" entries in kDisplayKTable,
+ // depending on whether the webkit prefix pref is enabled vs. disabled.
+ if (sIndexOfWebkitBoxInDisplayTable >= 0) {
+ nsCSSProps::kDisplayKTable[sIndexOfWebkitBoxInDisplayTable].mKeyword =
+ isWebkitPrefixSupportEnabled ?
+ eCSSKeyword__webkit_box : eCSSKeyword_UNKNOWN;
+ }
+ if (sIndexOfWebkitInlineBoxInDisplayTable >= 0) {
+ nsCSSProps::kDisplayKTable[sIndexOfWebkitInlineBoxInDisplayTable].mKeyword =
+ isWebkitPrefixSupportEnabled ?
+ eCSSKeyword__webkit_inline_box : eCSSKeyword_UNKNOWN;
+ }
+}
+
// When the pref "layout.css.display-contents.enabled" changes, this function is
// invoked to let us update kDisplayKTable, to selectively disable or restore
// the entries for "contents" in that table.
static void
DisplayContentsEnabledPrefChangeCallback(const char* aPrefName, void* aClosure)
{
NS_ASSERTION(strcmp(aPrefName, DISPLAY_CONTENTS_ENABLED_PREF_NAME) == 0,
"Did you misspell " DISPLAY_CONTENTS_ENABLED_PREF_NAME " ?");
@@ -7551,16 +7599,20 @@ nsLayoutUtils::Initialize()
Preferences::AddBoolVarCache(&sInterruptibleReflowEnabled,
"layout.interruptible-reflow.enabled");
Preferences::AddBoolVarCache(&sSVGTransformBoxEnabled,
"svg.transform-box.enabled");
Preferences::RegisterCallback(GridEnabledPrefChangeCallback,
GRID_ENABLED_PREF_NAME);
GridEnabledPrefChangeCallback(GRID_ENABLED_PREF_NAME, nullptr);
+ Preferences::RegisterCallback(WebkitPrefixEnabledPrefChangeCallback,
+ WEBKIT_PREFIXES_ENABLED_PREF_NAME);
+ WebkitPrefixEnabledPrefChangeCallback(WEBKIT_PREFIXES_ENABLED_PREF_NAME,
+ nullptr);
Preferences::RegisterCallback(TextAlignUnsafeEnabledPrefChangeCallback,
TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME);
Preferences::RegisterCallback(DisplayContentsEnabledPrefChangeCallback,
DISPLAY_CONTENTS_ENABLED_PREF_NAME);
DisplayContentsEnabledPrefChangeCallback(DISPLAY_CONTENTS_ENABLED_PREF_NAME,
nullptr);
TextAlignUnsafeEnabledPrefChangeCallback(TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME,
nullptr);
@@ -7578,16 +7630,18 @@ nsLayoutUtils::Shutdown()
{
if (sContentMap) {
delete sContentMap;
sContentMap = nullptr;
}
Preferences::UnregisterCallback(GridEnabledPrefChangeCallback,
GRID_ENABLED_PREF_NAME);
+ Preferences::UnregisterCallback(WebkitPrefixEnabledPrefChangeCallback,
+ WEBKIT_PREFIXES_ENABLED_PREF_NAME);
nsComputedDOMStyle::UnregisterPrefChangeCallbacks();
}
/* static */
void
nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext,
imgIRequest* aRequest,
bool* aRequestRegistered)
@@ -8957,19 +9011,17 @@ nsLayoutUtils::GetSelectionBoundingRect(
/* static */ bool
nsLayoutUtils::IsScrollFrameWithSnapping(nsIFrame* aFrame)
{
nsIScrollableFrame* sf = do_QueryFrame(aFrame);
if (!sf) {
return false;
}
- ScrollbarStyles styles = sf->GetScrollbarStyles();
- return styles.mScrollSnapTypeY != NS_STYLE_SCROLL_SNAP_TYPE_NONE ||
- styles.mScrollSnapTypeX != NS_STYLE_SCROLL_SNAP_TYPE_NONE;
+ return sf->IsScrollFrameWithSnapping();
}
/* static */ nsBlockFrame*
nsLayoutUtils::GetFloatContainingBlock(nsIFrame* aFrame)
{
nsIFrame* ancestor = aFrame->GetParent();
while (ancestor && !ancestor->IsFloatContainingBlock()) {
ancestor = ancestor->GetParent();
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -699,17 +699,17 @@ public:
* return the element that we took the overflow from (which should then be
* treated as "overflow: visible"), and we store the overflow style here.
* If the document is in fullscreen, and the fullscreen element is not the
* root, the scrollbar of viewport will be suppressed.
* @return if scroll was propagated from some content node, the content node
* it was propagated from.
*/
nsIContent* UpdateViewportScrollbarStylesOverride();
- ScrollbarStyles GetViewportScrollbarStylesOverride()
+ const ScrollbarStyles& GetViewportScrollbarStylesOverride()
{
return mViewportStyleScrollbar;
}
/**
* Set and get methods for controlling the background drawing
*/
bool GetBackgroundImageDraw() const { return mDrawImageBackground; }
--- a/layout/forms/nsListControlFrame.h
+++ b/layout/forms/nsListControlFrame.h
@@ -103,16 +103,19 @@ public:
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
// nsIFormControlFrame
virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) override;
virtual void SetFocus(bool aOn = true, bool aRepaint = false) override;
+ virtual bool IsScrollFrameWithSnapping() const override {
+ return false;
+ }
virtual mozilla::ScrollbarStyles GetScrollbarStyles() const override;
virtual bool ShouldPropagateComputedBSizeToScrolledContent() const override;
// for accessibility purposes
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() override;
#endif
--- a/layout/generic/DetailsFrame.cpp
+++ b/layout/generic/DetailsFrame.cpp
@@ -42,17 +42,17 @@ DetailsFrame::GetType() const
{
return nsGkAtoms::detailsFrame;
}
void
DetailsFrame::SetInitialChildList(ChildListID aListID, nsFrameList& aChildList)
{
if (aListID == kPrincipalList) {
- auto* details = HTMLDetailsElement::FromContent(GetContent());
+ HTMLDetailsElement* details = HTMLDetailsElement::FromContent(GetContent());
bool isOpen = details->Open();
if (isOpen) {
// If details is open, the first summary needs to be rendered as if it is
// the first child.
for (nsIFrame* child : aChildList) {
auto* realFrame = nsPlaceholderFrame::GetRealFrameFor(child);
auto* cif = realFrame->GetContentInsertionFrame();
@@ -62,24 +62,30 @@ DetailsFrame::SetInitialChildList(ChildL
aChildList.RemoveFrame(child);
aChildList.InsertFrame(nullptr, nullptr, child);
break;
}
}
}
#ifdef DEBUG
- nsIFrame* realFrame =
- nsPlaceholderFrame::GetRealFrameFor(isOpen ?
- aChildList.FirstChild() :
- aChildList.OnlyChild());
- MOZ_ASSERT(realFrame, "Principal list of details should not be empty!");
- nsIFrame* summaryFrame = realFrame->GetContentInsertionFrame();
- MOZ_ASSERT(summaryFrame->GetType() == nsGkAtoms::summaryFrame,
- "The frame should be summary frame!");
+ for (nsIFrame* child : aChildList) {
+ HTMLSummaryElement* summary =
+ HTMLSummaryElement::FromContent(child->GetContent());
+
+ if (child == aChildList.FirstChild()) {
+ if (summary && summary->IsMainSummary()) {
+ break;
+ }
+ } else {
+ MOZ_ASSERT(!summary || !summary->IsMainSummary(),
+ "Rest of the children are neither summary elements nor"
+ "the main summary!");
+ }
+ }
#endif
}
nsBlockFrame::SetInitialChildList(aListID, aChildList);
}
void
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -598,16 +598,19 @@ load 1223568-1.html
load 1223568-2.html
load 1224230-1.html
pref(layout.css.grid.enabled,true) load 1225118.html
pref(layout.css.grid.enabled,true) load 1225376.html
pref(layout.css.grid.enabled,true) load 1225592.html
load 1229437-1.html
load 1229437-2.html
pref(dom.details_element.enabled,true) load details-containing-only-text.html
+pref(dom.details_element.enabled,true) load details-display-none-summary-1.html
+pref(dom.details_element.enabled,true) load details-display-none-summary-2.html
+pref(dom.details_element.enabled,true) load details-display-none-summary-3.html
pref(dom.details_element.enabled,true) load details-open-overflow-auto.html
pref(dom.details_element.enabled,true) load details-open-overflow-hidden.html
pref(dom.details_element.enabled,true) load details-three-columns.html
load first-letter-638937-1.html
load first-letter-638937-2.html
pref(dom.meta-viewport.enabled,true) test-pref(font.size.inflation.emPerLine,15) asserts(0-100) load font-inflation-762332.html # bug 762332
load outline-on-frameset.xhtml
pref(dom.details_element.enabled,true) load summary-position-out-of-flow.html
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/details-display-none-summary-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <body>
+ <details open>
+ <summary style="display: none;">summary (display: none)</summary>
+ </details>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/details-display-none-summary-2.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <body>
+ <details open>
+ <summary style="display: none;">summary (display: none)</summary>
+ <p>This is the details.</p>
+ </details>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/details-display-none-summary-3.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <body>
+ <details open>
+ <summary style="display: none;">summary (display: none)</summary>
+ <summary>summary 2</summary>
+ <p>This is the details.</p>
+ </details>
+ </body>
+</html>
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3625,16 +3625,36 @@ static void HandleScrollPref(nsIScrollab
aValue = NS_STYLE_OVERFLOW_HIDDEN;
break;
case nsIScrollable::Scrollbar_Always:
aValue = NS_STYLE_OVERFLOW_SCROLL;
break;
}
}
+bool
+ScrollFrameHelper::IsScrollFrameWithSnapping() const
+{
+ nsPresContext* presContext = mOuter->PresContext();
+ if (!presContext->IsDynamic() &&
+ !(mIsRoot && presContext->HasPaginatedScrolling())) {
+ return false;
+ }
+
+ if (!mIsRoot) {
+ const nsStyleDisplay& display = *mOuter->StyleDisplay();
+ return display.mScrollSnapTypeY != NS_STYLE_SCROLL_SNAP_TYPE_NONE ||
+ display.mScrollSnapTypeX != NS_STYLE_SCROLL_SNAP_TYPE_NONE;
+ } else {
+ const ScrollbarStyles& display = presContext->GetViewportScrollbarStylesOverride();
+ return display.mScrollSnapTypeY != NS_STYLE_SCROLL_SNAP_TYPE_NONE ||
+ display.mScrollSnapTypeX != NS_STYLE_SCROLL_SNAP_TYPE_NONE;
+ }
+}
+
ScrollbarStyles
ScrollFrameHelper::GetScrollbarStylesFromFrame() const
{
nsPresContext* presContext = mOuter->PresContext();
if (!presContext->IsDynamic() &&
!(mIsRoot && presContext->HasPaginatedScrolling())) {
return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN);
}
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -52,16 +52,17 @@ public:
typedef mozilla::layers::Layer Layer;
class AsyncScroll;
class AsyncSmoothMSDScroll;
ScrollFrameHelper(nsContainerFrame* aOuter, bool aIsRoot);
~ScrollFrameHelper();
+ bool IsScrollFrameWithSnapping() const;
mozilla::ScrollbarStyles GetScrollbarStylesFromFrame() const;
// If a child frame was added or removed on the scrollframe,
// reload our child frame list.
// We need this if a scrollbar frame is recreated.
void ReloadChildFrames();
nsresult CreateAnonymousContent(
@@ -744,16 +745,19 @@ public:
virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
uint32_t aFilter) override;
// nsIScrollableFrame
virtual nsIFrame* GetScrolledFrame() const override {
return mHelper.GetScrolledFrame();
}
+ virtual bool IsScrollFrameWithSnapping() const override {
+ return mHelper.IsScrollFrameWithSnapping();
+ }
virtual mozilla::ScrollbarStyles GetScrollbarStyles() const override {
return mHelper.GetScrollbarStylesFromFrame();
}
virtual uint32_t GetScrollbarVisibility() const override {
return mHelper.GetScrollbarVisibility();
}
virtual nsMargin GetActualScrollbarSizes() const override {
return mHelper.GetActualScrollbarSizes();
@@ -1156,16 +1160,19 @@ public:
static void AdjustReflowStateForPrintPreview(nsBoxLayoutState& aState, bool& aSetBack);
static void AdjustReflowStateBack(nsBoxLayoutState& aState, bool aSetBack);
// nsIScrollableFrame
virtual nsIFrame* GetScrolledFrame() const override {
return mHelper.GetScrolledFrame();
}
+ virtual bool IsScrollFrameWithSnapping() const override {
+ return mHelper.IsScrollFrameWithSnapping();
+ }
virtual mozilla::ScrollbarStyles GetScrollbarStyles() const override {
return mHelper.GetScrollbarStylesFromFrame();
}
virtual uint32_t GetScrollbarVisibility() const override {
return mHelper.GetScrollbarVisibility();
}
virtual nsMargin GetActualScrollbarSizes() const override {
return mHelper.GetActualScrollbarSizes();
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -790,28 +790,30 @@ nsHTMLReflowState::InitFrameType(nsIAtom
}
else {
switch (GetDisplay()) {
case NS_STYLE_DISPLAY_BLOCK:
case NS_STYLE_DISPLAY_LIST_ITEM:
case NS_STYLE_DISPLAY_TABLE:
case NS_STYLE_DISPLAY_TABLE_CAPTION:
case NS_STYLE_DISPLAY_FLEX:
+ case NS_STYLE_DISPLAY_WEBKIT_BOX:
case NS_STYLE_DISPLAY_GRID:
case NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER:
frameType = NS_CSS_FRAME_TYPE_BLOCK;
break;
case NS_STYLE_DISPLAY_INLINE:
case NS_STYLE_DISPLAY_INLINE_BLOCK:
case NS_STYLE_DISPLAY_INLINE_TABLE:
case NS_STYLE_DISPLAY_INLINE_BOX:
case NS_STYLE_DISPLAY_INLINE_XUL_GRID:
case NS_STYLE_DISPLAY_INLINE_STACK:
case NS_STYLE_DISPLAY_INLINE_FLEX:
+ case NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX:
case NS_STYLE_DISPLAY_INLINE_GRID:
case NS_STYLE_DISPLAY_RUBY:
case NS_STYLE_DISPLAY_RUBY_BASE:
case NS_STYLE_DISPLAY_RUBY_TEXT:
case NS_STYLE_DISPLAY_RUBY_BASE_CONTAINER:
frameType = NS_CSS_FRAME_TYPE_INLINE;
break;
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -59,16 +59,18 @@ public:
/**
* Get the styles (NS_STYLE_OVERFLOW_SCROLL, NS_STYLE_OVERFLOW_HIDDEN,
* or NS_STYLE_OVERFLOW_AUTO) governing the horizontal and vertical
* scrollbars for this frame.
*/
virtual mozilla::ScrollbarStyles GetScrollbarStyles() const = 0;
+ virtual bool IsScrollFrameWithSnapping() const = 0;
+
enum { HORIZONTAL = 0x01, VERTICAL = 0x02 };
/**
* Return the scrollbars which are visible. It's OK to call this during reflow
* of the scrolled contents, in which case it will reflect the current
* assumptions about scrollbar visibility.
*/
virtual uint32_t GetScrollbarVisibility() const = 0;
/**
--- a/layout/inspector/tests/test_bug877690.html
+++ b/layout/inspector/tests/test_bug877690.html
@@ -163,19 +163,21 @@ function do_test() {
// test proprety
var prop = "display";
var values = getCSSValuesForProperty(prop);
var expected = [ "initial", "inherit", "unset", "none", "inline", "block", "inline-block", "list-item",
"table", "inline-table", "table-row-group", "table-header-group", "table-footer-group", "table-row",
"table-column-group", "table-column", "table-cell", "table-caption", "-moz-box", "-moz-inline-box",
"-moz-grid", "-moz-inline-grid", "-moz-grid-group", "-moz-grid-line", "-moz-stack", "-moz-inline-stack",
- "-moz-deck", "-moz-popup", "-moz-groupbox", "flex", "inline-flex", "grid",
- "inline-grid", "ruby", "ruby-base", "ruby-base-container", "ruby-text",
- "ruby-text-container", "contents" ];
+ "-moz-deck", "-moz-popup", "-moz-groupbox",
+ "flex", "inline-flex", "-webkit-box", "-webkit-inline-box",
+ "grid", "inline-grid",
+ "ruby", "ruby-base", "ruby-base-container", "ruby-text", "ruby-text-container",
+ "contents" ];
ok(testValues(values, expected), "property display's values.");
// test property
var prop = "float";
var values = getCSSValuesForProperty(prop);
var expected = [ "initial", "inherit", "unset", "none", "left", "right", "inline-start", "inline-end" ];
ok(testValues(values, expected), "property float's values.");
new file mode 100644
--- /dev/null
+++ b/layout/reftests/details-summary/mouse-click-change-details-to-display-none.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html class="reftest-wait">
+ <script>
+ function runTest() {
+ // Both Chrome and Safari add the 'open' attribute to the details element.
+ var details = document.getElementById("details");
+ var summary = document.getElementById("summary");
+
+ document.body.addEventListener("click", function () {
+ // Change details to display: none in capturing phase.
+ details.style.display = "none";
+ }, true);
+
+ summary.dispatchEvent(new MouseEvent("click"));
+
+ details.style.display = "block";
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ <body onload="runTest();">
+ <details id="details">
+ <summary id="summary">Summary</summary>
+ <p>This is the details.</p>
+ </details>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/details-summary/mouse-click-change-summary-to-display-none-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <body>
+ <div>
+ <p>This is the details.</p>
+ </div>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/details-summary/mouse-click-change-summary-to-display-none.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html class="reftest-wait">
+ <script>
+ function runTest() {
+ // Both Chrome and Safari add the 'open' attribute to the details element.
+ // Firefox has the same behavior.
+ var details = document.getElementById("details");
+ var summary = document.getElementById("summary");
+
+ document.body.addEventListener("click", function () {
+ // Change summary to display: none in capturing phase.
+ summary.style.display = "none";
+ }, true);
+
+ summary.dispatchEvent(new MouseEvent("click"));
+
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ <body onload="runTest();">
+ <details id="details">
+ <summary id="summary">Summary</summary>
+ <p>This is the details.</p>
+ </details>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/details-summary/mouse-click-move-summary-to-different-details-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+ <body>
+ <details open>
+ <summary>Summary 2</summary>
+ <summary>Summary 1</summary>
+ <p>This is the details 1.</p>
+ </details>
+ <details>
+ <p>This is the details 2.</p>
+ </details>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/details-summary/mouse-click-move-summary-to-different-details.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html class="reftest-wait">
+ <script>
+ function runTest() {
+ // Both Chrome and Safari do not add the 'open' attribute to details1
+ // element, but Firefox does add 'open' to details1 since summary2 had been
+ // moved to details1 before receiving the 'click' event.
+ var details1 = document.getElementById("details1");
+ var summary2 = document.getElementById("summary2");
+
+ document.body.addEventListener("click", function () {
+ // Move summary2 into details1 at capture phase, and summary2 will be the
+ // main summary of details1 at target phase.
+ details1.insertBefore(summary2, details1.children[0]);
+ }, true);
+
+ summary2.dispatchEvent(new MouseEvent("click"));
+
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ <body onload="runTest();">
+ <details id="details1">
+ <summary id="summary1">Summary 1</summary>
+ <p>This is the details 1.</p>
+ </details>
+ <details>
+ <summary id="summary2">Summary 2</summary>
+ <p>This is the details 2.</p>
+ </details>
+ </body>
+</html>
--- a/layout/reftests/details-summary/reftest.list
+++ b/layout/reftests/details-summary/reftest.list
@@ -60,17 +60,20 @@ pref(dom.details_element.enabled,true) =
pref(dom.details_element.enabled,true) == mouse-click-twice-single-summary.html single-summary.html
pref(dom.details_element.enabled,true) == mouse-click-open-single-summary.html single-summary.html
pref(dom.details_element.enabled,true) == mouse-click-twice-open-single-summary.html open-single-summary.html
pref(dom.details_element.enabled,true) == mouse-click-open-second-summary.html open-multiple-summary.html
pref(dom.details_element.enabled,true) == mouse-click-overflow-hidden-details.html overflow-hidden-open-details.html
pref(dom.details_element.enabled,true) == mouse-click-twice-overflow-hidden-details.html overflow-hidden-details.html
pref(dom.details_element.enabled,true) == mouse-click-overflow-auto-details.html overflow-auto-open-details.html
pref(dom.details_element.enabled,true) == mouse-click-twice-overflow-auto-details.html overflow-auto-details.html
-pref(dom.details_element.enabled,true) == mouse-click-display-none-details.html single-summary.html
+pref(dom.details_element.enabled,true) == mouse-click-display-none-details.html open-single-summary.html
+pref(dom.details_element.enabled,true) == mouse-click-change-details-to-display-none.html open-single-summary.html
+pref(dom.details_element.enabled,true) == mouse-click-change-summary-to-display-none.html mouse-click-change-summary-to-display-none-ref.html
+pref(dom.details_element.enabled,true) == mouse-click-move-summary-to-different-details.html mouse-click-move-summary-to-different-details-ref.html
# Dispatch mouse click to out-of-flow details or summary
pref(dom.details_element.enabled,true) == mouse-click-fixed-summary.html open-fixed-summary.html
pref(dom.details_element.enabled,true) == mouse-click-twice-fixed-summary.html fixed-summary.html
pref(dom.details_element.enabled,true) == mouse-click-float-details.html open-float-details.html
pref(dom.details_element.enabled,true) == mouse-click-twice-float-details.html float-details.html
# Dispatch keyboard event to summary
--- a/layout/reftests/webkit-box/reftest.list
+++ b/layout/reftests/webkit-box/reftest.list
@@ -1,12 +1,21 @@
# This directory contains tests for "display: -webkit-box" and associated
# CSS properties. These tests require webkit prefix support to be enabled.
default-preferences pref(layout.css.prefixes.webkit,true)
+# Tests for anonymous flex item formation inside of a "-webkit-box":
+# Note: some of these tests are marked as failing, because we don't match
+# WebKit/Blink on them. (The reference case represents the WebKit/Blink
+# rendering.) We could probably make them pass by implementing some quirks, if
+# it turns out that the web depends on WebKit/Blink's behavior in these cases.
+== webkit-box-anon-flex-items-1.html webkit-box-anon-flex-items-1-ref.html
+fails == webkit-box-anon-flex-items-2.html webkit-box-anon-flex-items-2-ref.html
+fails == webkit-box-anon-flex-items-3.html webkit-box-anon-flex-items-3-ref.html
+
# Tests for "-webkit-box" & "-webkit-inline-box" as display values:
== webkit-display-values-1.html webkit-display-values-1-ref.html
# Tests for "-webkit-box-align" (cross-axis alignment):
== webkit-box-align-horiz-1a.html webkit-box-align-horiz-1-ref.html
== webkit-box-align-horiz-1b.html webkit-box-align-horiz-1-ref.html
== webkit-box-align-vert-1.html webkit-box-align-vert-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webkit-box/webkit-box-anon-flex-items-1-ref.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>
+ Reference Case
+ </title>
+ <style>
+ .container {
+ display: flex;
+ justify-content: space-between;
+ width: 300px;
+ border: 1px solid black;
+ font: 10px sans-serif;
+ margin-bottom: 10px;
+ }
+ .container > * {
+ background: lightgray;
+ }
+
+ .fakeAnonWrapper {
+ background: none;
+ }
+ .fakeAnonWrapper > * {
+ background: lightgray;
+ }
+ .table {
+ display: table;
+ }
+ </style>
+</head>
+<body>
+<!-- We use an explicit div (with class "fakeAnonWrapper") here, to match the
+ anonymous div that the testcase is expected to generate around contiguous
+ runs of inline content. -->
+<div class="container">
+ <div class="fakeAnonWrapper">
+ a
+ <i>i</i><img src="bogus"><img src="bogus" alt="alt">
+ <canvas height="5" width="5"></canvas>
+ <video height="5" width="5"></video>
+ <div style="display:inline-block">ib</div>
+ <div style="display:inline-table">it</div>
+ z
+ </div>
+ <div>block</div>
+ <div>block</div>
+</div>
+
+<div class="container">
+ <div>block</div>
+ <div class="fakeAnonWrapper">
+ a
+ <i>i</i><img src="bogus"><img src="bogus" alt="alt">
+ <canvas height="5" width="5"></canvas>
+ <video height="5" width="5"></video>
+ <div style="display:inline-block">ib</div>
+ <div style="display:inline-table">it</div>
+ z
+ </div>
+ <div>block</div>
+</div>
+
+<div class="container">
+ <div>block</div>
+ <div>block</div>
+ <div class="fakeAnonWrapper">
+ a
+ <i>i</i><img src="bogus"><img src="bogus" alt="alt">
+ <canvas height="5" width="5"></canvas>
+ <video height="5" width="5"></video>
+ <div style="display:inline-block">ib</div>
+ <div style="display:inline-table">it</div>
+ z
+ </div>
+</div>
+
+<!-- We use explicit divs here for each anonymous wrapper-box that the testcase
+ is expected to produce (with class "fakeAnonWrapper", and also "table" if
+ it's expected to be a table wrapper) -->
+<div class="container">
+ <div class="fakeAnonWrapper">a</div>
+ <div class="fakeAnonWrapper table">
+ <div style="display: table-cell">tc</div>
+ <div style="display: table-cell">tc</div>
+ </div>
+ <div class="fakeAnonWrapper">b</div>
+ <div class="fakeAnonWrapper table">
+ <div style="display: table-row">tr</div>
+ <div style="display: table-cell">tc</div>
+ </div>
+ <div class="fakeAnonWrapper">c</div>
+ <div class="fakeAnonWrapper table">
+ <div style="display: table-row">tr</div>
+ <div style="display: table-row-group">trg</div>
+ </div>
+ <div class="fakeAnonWrapper">d</div>
+ <table><tbody><tr><td>t</td></tr></tbody></table>
+ <div class="fakeAnonWrapper">e</div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webkit-box/webkit-box-anon-flex-items-1.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>
+ Test for contiguous inline content getting wrapped in a single block,
+ inside of -webkit-box.
+ </title>
+ <style>
+ .container {
+ display: -webkit-box;
+ -webkit-box-pack: justify;
+ justify-content: space-between; /* XXX remove when bug 1231682 is fixed */
+ width: 300px;
+ border: 1px solid black;
+ font: 10px sans-serif;
+ margin-bottom: 10px;
+ }
+ .container > * {
+ background: lightgray;
+ }
+ </style>
+</head>
+<body>
+<!-- The following containers each have a bunch of contiguous inline-level
+ content. In each case, all of the inline content ("a" through "z") should
+ get wrapped into a single anonymous block. -->
+<div class="container">
+ a
+ <i>i</i><img src="bogus"><img src="bogus" alt="alt">
+ <canvas height="5" width="5"></canvas>
+ <video height="5" width="5"></video>
+ <div style="display:inline-block">ib</div>
+ <div style="display:inline-table">it</div>
+ z
+ <div>block</div>
+ <div>block</div>
+</div>
+
+<div class="container">
+ <div>block</div>
+ a
+ <i>i</i><img src="bogus"><img src="bogus" alt="alt">
+ <canvas height="5" width="5"></canvas>
+ <video height="5" width="5"></video>
+ <div style="display:inline-block">ib</div>
+ <div style="display:inline-table">it</div>
+ z
+ <div>block</div>
+</div>
+
+<div class="container">
+ <div>block</div>
+ <div>block</div>
+ a
+ <i>i</i><img src="bogus"><img src="bogus" alt="alt">
+ <canvas height="5" width="5"></canvas>
+ <video height="5" width="5"></video>
+ <div style="display:inline-block">ib</div>
+ <div style="display:inline-table">it</div>
+ z
+</div>
+
+<!-- This container tests how flex items are formed when table parts are placed
+ directly inside of a -webkit-box, alongside inline-level content.
+ (Table-fixup should produce an anonymous table around each contiguous run
+ of table-parts, and we should get an anonymous block around each piece of
+ raw text.) -->
+<div class="container">
+ a
+ <div style="display: table-cell">tc</div>
+ <div style="display: table-cell">tc</div>
+ b
+ <div style="display: table-row">tr</div>
+ <div style="display: table-cell">tc</div>
+ c
+ <div style="display: table-row">tr</div>
+ <div style="display: table-row-group">trg</div>
+ d
+ <table><tbody><tr><td>t</td></tr></tbody></table>
+ e
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webkit-box/webkit-box-anon-flex-items-2-ref.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>
+ Reference Case
+ </title>
+ <style>
+ .container {
+ display: flex;
+ justify-content: space-between;
+ width: 300px;
+ border: 1px solid black;
+ font: 10px sans-serif;
+ }
+ span {
+ background: lightgray;
+ }
+ </style>
+</head>
+<body>
+<!-- We use an explicit div here, to match the anonymous div that the testcase
+ is expected to generate around contiguous runs of inline content. -->
+<div class="container">
+ <div>
+ raw text
+ <span>start</span>
+ </div>
+ <div>BLOCK</div>
+ <div>
+ <span>end</span>
+ raw text
+ </div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webkit-box/webkit-box-anon-flex-items-2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>
+ Test for how block-in-inline splits behave inside of -webkit-box.
+ </title>
+ <style>
+ .container {
+ display: -webkit-box;
+ -webkit-box-pack: justify;
+ justify-content: space-between; /* XXX remove when bug 1231682 is fixed */
+ width: 300px;
+ border: 1px solid black;
+ font: 10px sans-serif;
+ }
+ .container > * {
+ background: lightgray;
+ }
+ </style>
+</head>
+<body>
+<div class="container">
+ raw text
+ <span>start<div>BLOCK</div>end</span>
+ raw text
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webkit-box/webkit-box-anon-flex-items-3-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>
+ Reference Case
+ </title>
+ <style>
+ .container {
+ display: flex;
+ justify-content: space-between;
+ width: 300px;
+ border: 1px solid black;
+ }
+ .wspWrapper {
+ white-space: pre;
+ }
+ </style>
+</head>
+<body>
+<!-- We use an explicit div here, to match the anonymous div that the testcase
+ is expected to generate around the whitespace. -->
+<div class="container">
+ <div>a</div>
+ <div class="wspWrapper"> </div>
+ <div>b</div>
+ <div>c</div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webkit-box/webkit-box-anon-flex-items-3.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>
+ Test for whether whitespace gets wrapped in an anonymous box,
+ inside of -webkit-box.
+ </title>
+ <style>
+ .container {
+ display: -webkit-box;
+ -webkit-box-pack: justify;
+ justify-content: space-between; /* XXX remove when bug 1231682 is fixed */
+ width: 300px;
+ border: 1px solid black;
+ white-space: pre;
+ }
+ </style>
+</head>
+<body>
+ <!-- Note the space characters between the first and second divs here: -->
+ <div class="container"><div>a</div> <div>b</div><div>c</div></div>
+</body>
+</html>
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -6918,45 +6918,71 @@ CSSParserImpl::ParseTreePseudoElement(ns
nsCSSKeyword
CSSParserImpl::LookupKeywordPrefixAware(nsAString& aKeywordStr,
const KTableEntry aKeywordTable[])
{
nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aKeywordStr);
if (aKeywordTable == nsCSSProps::kDisplayKTable) {
+ // NOTE: This code will be considerably simpler once we can do away with
+ // all Unprefixing Service code, in bug 1259348. But for the time being, we
+ // have to support two different strategies for handling -webkit-box here:
+ // (1) "Native support" (sWebkitPrefixedAliasesEnabled): we assume that
+ // -webkit-box will parse correctly (via an entry in kDisplayKTable),
+ // and we simply make a note that we've parsed it (so that we can we
+ // can give later "-moz-box" styling special handling as noted below).
+ // (2) "Unprefixing Service support" (ShouldUseUnprefixingService): we
+ // convert "-webkit-box" directly to modern "flex" (& do the same for
+ // any later "-moz-box" styling).
+ //
+ // Note that sWebkitPrefixedAliasesEnabled and
+ // ShouldUseUnprefixingService() are mutually exlusive, because the latter
+ // explicitly defers to the former.
if ((keyword == eCSSKeyword__webkit_box ||
- keyword == eCSSKeyword__webkit_inline_box) &&
- (sWebkitPrefixedAliasesEnabled || ShouldUseUnprefixingService())) {
- // Treat "display: -webkit-box" as "display: flex", and
- // "display: -webkit-inline-box" as "display: inline-flex". In simple
- // scenarios, they largely behave the same, as long as we alias the
- // associated properties to modern flexbox equivalents as well.
- if (mWebkitBoxUnprefixState == eHaveNotUnprefixed) {
- mWebkitBoxUnprefixState = eHaveUnprefixed;
- }
- return (keyword == eCSSKeyword__webkit_box) ?
- eCSSKeyword_flex : eCSSKeyword_inline_flex;
+ keyword == eCSSKeyword__webkit_inline_box)) {
+ const bool usingUnprefixingService = ShouldUseUnprefixingService();
+ if (sWebkitPrefixedAliasesEnabled || usingUnprefixingService) {
+ // Make a note that we're accepting some "-webkit-{inline-}box" styling,
+ // so we can give special treatment to subsequent "-moz-{inline}-box".
+ // (See special treatment below.)
+ if (mWebkitBoxUnprefixState == eHaveNotUnprefixed) {
+ mWebkitBoxUnprefixState = eHaveUnprefixed;
+ }
+ if (usingUnprefixingService) {
+ // When we're using the unprefixing service, we treat
+ // "display:-webkit-box" as if it were "display:flex"
+ // (and "-webkit-inline-box" as "inline-flex").
+ return (keyword == eCSSKeyword__webkit_box) ?
+ eCSSKeyword_flex : eCSSKeyword_inline_flex;
+ }
+ }
}
// If we've seen "display: -webkit-box" (or "-webkit-inline-box") in an
- // earlier declaration and we tried to unprefix it to emulate support for
- // it, then we have to watch out for later "display: -moz-box" (and
- // "-moz-inline-box") declarations; they're likely just a halfhearted
- // attempt at compatibility, and they actually end up stomping on our
- // emulation of the earlier -webkit-box display-value, via the CSS
- // cascade. To prevent this problem, we also treat "display: -moz-box" as
- // "display: flex" (but only if we unprefixed an earlier "-webkit-box").
+ // earlier declaration and we honored it, then we have to watch out for
+ // later "display: -moz-box" (and "-moz-inline-box") declarations; they're
+ // likely just a halfhearted attempt at compatibility, and they actually
+ // end up stomping on our emulation of the earlier -webkit-box
+ // display-value, via the CSS cascade. To prevent this problem, we treat
+ // "display: -moz-box" & "-moz-inline-box" as if they were simply a
+ // repetition of the webkit equivalent that we already parsed.
if (mWebkitBoxUnprefixState == eHaveUnprefixed &&
(keyword == eCSSKeyword__moz_box ||
keyword == eCSSKeyword__moz_inline_box)) {
MOZ_ASSERT(sWebkitPrefixedAliasesEnabled || ShouldUseUnprefixingService(),
"mDidUnprefixWebkitBoxInEarlierDecl should only be set if "
"we're supporting webkit-prefixed aliases, or if we're using "
"the css unprefixing service on this site");
+ if (sWebkitPrefixedAliasesEnabled) {
+ return (keyword == eCSSKeyword__moz_box) ?
+ eCSSKeyword__webkit_box : eCSSKeyword__webkit_inline_box;
+ }
+ // (If we get here, we're using the Unprefixing Service, which means
+ // we're unprefixing all the way to modern flexbox display values.)
return (keyword == eCSSKeyword__moz_box) ?
eCSSKeyword_flex : eCSSKeyword_inline_flex;
}
}
return keyword;
}
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1259,16 +1259,19 @@ KTableEntry nsCSSProps::kDisplayKTable[]
{ eCSSKeyword_ruby, NS_STYLE_DISPLAY_RUBY },
{ eCSSKeyword_ruby_base, NS_STYLE_DISPLAY_RUBY_BASE },
{ eCSSKeyword_ruby_base_container, NS_STYLE_DISPLAY_RUBY_BASE_CONTAINER },
{ eCSSKeyword_ruby_text, NS_STYLE_DISPLAY_RUBY_TEXT },
{ eCSSKeyword_ruby_text_container, NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER },
// The next two entries are controlled by the layout.css.grid.enabled pref.
{ eCSSKeyword_grid, NS_STYLE_DISPLAY_GRID },
{ eCSSKeyword_inline_grid, NS_STYLE_DISPLAY_INLINE_GRID },
+ // The next two entries are controlled by the layout.css.prefixes.webkit pref.
+ { eCSSKeyword__webkit_box, NS_STYLE_DISPLAY_WEBKIT_BOX },
+ { eCSSKeyword__webkit_inline_box, NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX },
// The next entry is controlled by the layout.css.display-contents.enabled
// pref.
{ eCSSKeyword_contents, NS_STYLE_DISPLAY_CONTENTS },
{ eCSSKeyword_UNKNOWN, -1 }
};
const KTableEntry nsCSSProps::kEmptyCellsKTable[] = {
{ eCSSKeyword_show, NS_STYLE_TABLE_EMPTY_CELLS_SHOW },
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -188,16 +188,17 @@ nsRuleNode::EnsureBlockDisplay(uint8_t&
} // else, fall through to share the 'break' for non-changing display vals
MOZ_FALLTHROUGH;
case NS_STYLE_DISPLAY_NONE :
case NS_STYLE_DISPLAY_CONTENTS :
// never change display:none or display:contents *ever*
case NS_STYLE_DISPLAY_TABLE :
case NS_STYLE_DISPLAY_BLOCK :
case NS_STYLE_DISPLAY_FLEX :
+ case NS_STYLE_DISPLAY_WEBKIT_BOX :
case NS_STYLE_DISPLAY_GRID :
// do not muck with these at all - already blocks
// This is equivalent to nsStyleDisplay::IsBlockOutside. (XXX Maybe we
// should just call that?)
// This needs to match the check done in
// nsCSSFrameConstructor::FindMathMLData for <math>.
break;
@@ -206,16 +207,21 @@ nsRuleNode::EnsureBlockDisplay(uint8_t&
display = NS_STYLE_DISPLAY_TABLE;
break;
case NS_STYLE_DISPLAY_INLINE_FLEX:
// make inline flex containers into flex containers
display = NS_STYLE_DISPLAY_FLEX;
break;
+ case NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX:
+ // make -webkit-inline-box containers into -webkit-box containers
+ display = NS_STYLE_DISPLAY_WEBKIT_BOX;
+ break;
+
case NS_STYLE_DISPLAY_INLINE_GRID:
// make inline grid containers into grid containers
display = NS_STYLE_DISPLAY_GRID;
break;
default :
// make it a block
display = NS_STYLE_DISPLAY_BLOCK;
@@ -235,16 +241,19 @@ nsRuleNode::EnsureInlineDisplay(uint8_t&
display = NS_STYLE_DISPLAY_INLINE_BLOCK;
break;
case NS_STYLE_DISPLAY_TABLE :
display = NS_STYLE_DISPLAY_INLINE_TABLE;
break;
case NS_STYLE_DISPLAY_FLEX :
display = NS_STYLE_DISPLAY_INLINE_FLEX;
break;
+ case NS_STYLE_DISPLAY_WEBKIT_BOX :
+ display = NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX;
+ break;
case NS_STYLE_DISPLAY_GRID :
display = NS_STYLE_DISPLAY_INLINE_GRID;
break;
case NS_STYLE_DISPLAY_BOX:
display = NS_STYLE_DISPLAY_INLINE_BOX;
break;
case NS_STYLE_DISPLAY_STACK:
display = NS_STYLE_DISPLAY_INLINE_STACK;
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -459,16 +459,18 @@ enum class FillMode : uint32_t;
#define NS_STYLE_DISPLAY_GRID 31
#define NS_STYLE_DISPLAY_INLINE_GRID 32
#define NS_STYLE_DISPLAY_RUBY 33
#define NS_STYLE_DISPLAY_RUBY_BASE 34
#define NS_STYLE_DISPLAY_RUBY_BASE_CONTAINER 35
#define NS_STYLE_DISPLAY_RUBY_TEXT 36
#define NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER 37
#define NS_STYLE_DISPLAY_CONTENTS 38
+#define NS_STYLE_DISPLAY_WEBKIT_BOX 39
+#define NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX 40
// See nsStyleDisplay
// If these are re-ordered, nsComputedDOMStyle::DoGetContain() and
// nsCSSValue::AppendToString() must be updated.
#define NS_STYLE_CONTAIN_NONE 0
#define NS_STYLE_CONTAIN_STRICT 0x1
#define NS_STYLE_CONTAIN_LAYOUT 0x2
#define NS_STYLE_CONTAIN_STYLE 0x4
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -592,16 +592,31 @@ ShouldSuppressLineBreak(const nsStyleCon
// non-anonymous frame outside, we should also check them.
aDisplay->mDisplay == NS_STYLE_DISPLAY_RUBY_BASE ||
aDisplay->mDisplay == NS_STYLE_DISPLAY_RUBY_TEXT) {
return true;
}
return false;
}
+// Flex & grid containers blockify their children.
+// "The display value of a flex item is blockified"
+// https://drafts.csswg.org/css-flexbox-1/#flex-items
+// "The display value of a grid item is blockified"
+// https://drafts.csswg.org/css-grid/#grid-items
+static bool
+ShouldBlockifyChildren(const nsStyleDisplay* aStyleDisp)
+{
+ auto displayVal = aStyleDisp->mDisplay;
+ return NS_STYLE_DISPLAY_FLEX == displayVal ||
+ NS_STYLE_DISPLAY_INLINE_FLEX == displayVal ||
+ NS_STYLE_DISPLAY_GRID == displayVal ||
+ NS_STYLE_DISPLAY_INLINE_GRID == displayVal;
+}
+
void
nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup)
{
// See if we have any text decorations.
// First see if our parent has text decorations. If our parent does, then we inherit the bit.
if (mParent && mParent->HasTextDecorationLines()) {
mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES;
} else {
@@ -698,17 +713,17 @@ nsStyleContext::ApplyStyleFixups(bool aS
const nsStyleDisplay* containerDisp = containerContext->StyleDisplay();
while (containerDisp->mDisplay == NS_STYLE_DISPLAY_CONTENTS) {
if (!containerContext->GetParent()) {
break;
}
containerContext = containerContext->GetParent();
containerDisp = containerContext->StyleDisplay();
}
- if (containerDisp->IsFlexOrGridDisplayType() &&
+ if (ShouldBlockifyChildren(containerDisp) &&
GetPseudo() != nsCSSAnonBoxes::mozNonElement) {
// NOTE: Technically, we shouldn't modify the 'display' value of
// positioned elements, since they aren't flex/grid items. However,
// we don't need to worry about checking for that, because if we're
// positioned, we'll have already been through a call to
// EnsureBlockDisplay() in nsRuleNode, so this call here won't change
// anything. So we're OK.
uint8_t displayVal = disp->mDisplay;
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2450,27 +2450,29 @@ struct nsStyleDisplay
// Should TABLE_CELL be included here? They have
// block frames nested inside of them.
// (But please audit all callers before changing.)
}
bool IsBlockOutsideStyle() const {
return NS_STYLE_DISPLAY_BLOCK == mDisplay ||
NS_STYLE_DISPLAY_FLEX == mDisplay ||
+ NS_STYLE_DISPLAY_WEBKIT_BOX == mDisplay ||
NS_STYLE_DISPLAY_GRID == mDisplay ||
NS_STYLE_DISPLAY_LIST_ITEM == mDisplay ||
NS_STYLE_DISPLAY_TABLE == mDisplay;
}
static bool IsDisplayTypeInlineOutside(uint8_t aDisplay) {
return NS_STYLE_DISPLAY_INLINE == aDisplay ||
NS_STYLE_DISPLAY_INLINE_BLOCK == aDisplay ||
NS_STYLE_DISPLAY_INLINE_TABLE == aDisplay ||
NS_STYLE_DISPLAY_INLINE_BOX == aDisplay ||
NS_STYLE_DISPLAY_INLINE_FLEX == aDisplay ||
+ NS_STYLE_DISPLAY_WEBKIT_INLINE_BOX == aDisplay ||
NS_STYLE_DISPLAY_INLINE_GRID == aDisplay ||
NS_STYLE_DISPLAY_INLINE_XUL_GRID == aDisplay ||
NS_STYLE_DISPLAY_INLINE_STACK == aDisplay ||
NS_STYLE_DISPLAY_RUBY == aDisplay ||
NS_STYLE_DISPLAY_RUBY_BASE == aDisplay ||
NS_STYLE_DISPLAY_RUBY_BASE_CONTAINER == aDisplay ||
NS_STYLE_DISPLAY_RUBY_TEXT == aDisplay ||
NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER == aDisplay ||
@@ -2521,23 +2523,16 @@ struct nsStyleDisplay
NS_STYLE_DISPLAY_RUBY_TEXT == aDisplay ||
NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER == aDisplay;
}
bool IsRubyDisplayType() const {
return IsRubyDisplayType(mDisplay);
}
- bool IsFlexOrGridDisplayType() const {
- return NS_STYLE_DISPLAY_FLEX == mDisplay ||
- NS_STYLE_DISPLAY_INLINE_FLEX == mDisplay ||
- NS_STYLE_DISPLAY_GRID == mDisplay ||
- NS_STYLE_DISPLAY_INLINE_GRID == mDisplay;
- }
-
bool IsOutOfFlowStyle() const {
return (IsAbsolutelyPositionedStyle() || IsFloatingStyle());
}
bool IsScrollableOverflow() const {
// mOverflowX and mOverflowY always match when one of them is
// NS_STYLE_OVERFLOW_VISIBLE or NS_STYLE_OVERFLOW_CLIP.
return mOverflowX != NS_STYLE_OVERFLOW_VISIBLE &&
--- a/layout/style/res/html.css
+++ b/layout/style/res/html.css
@@ -769,18 +769,16 @@ video > .caption-box {
position: relative;
overflow: hidden;
}
/* details & summary */
details > summary::-moz-list-bullet {
list-style-type: disclosure-closed;
- /* Prevent elements in summary being selected when clicking on the triangle. */
- -moz-user-select: none;
}
details[open] > summary::-moz-list-bullet {
list-style-type: disclosure-open;
}
/* emulation of non-standard HTML <marquee> tag */
marquee {
--- a/layout/style/res/ua.css
+++ b/layout/style/res/ua.css
@@ -96,16 +96,18 @@
}
/* Lists */
*|*::-moz-list-bullet, *|*::-moz-list-number {
display: inline;
vertical-align: baseline;
font-variant-numeric: tabular-nums;
+ /* Prevent the element from being selected when clicking on the marker. */
+ -moz-user-select: none;
}
/* SVG documents don't always load this file but they do have links.
* If you change the link rules, consider carefully whether to make
* the same changes to svg.css.
*/
/* Links */
--- a/layout/tools/reftest/reftest-preferences.js
+++ b/layout/tools/reftest/reftest-preferences.js
@@ -76,16 +76,17 @@ user_pref("security.view-source.reachabl
// Ensure that telemetry is disabled, so we don't connect to the telemetry
// server in the middle of the tests.
user_pref("toolkit.telemetry.enabled", false);
user_pref("toolkit.telemetry.unified", false);
// Likewise for safebrowsing.
user_pref("browser.safebrowsing.enabled", false);
user_pref("browser.safebrowsing.malware.enabled", false);
+user_pref("browser.safebrowsing.forbiddenURIs.enabled", false);
// Likewise for tracking protection.
user_pref("privacy.trackingprotection.enabled", false);
user_pref("privacy.trackingprotection.pbmode.enabled", false);
// And for snippets.
user_pref("browser.snippets.enabled", false);
user_pref("browser.snippets.syncPromo.enabled", false);
user_pref("browser.snippets.firstrunHomepage.enabled", false);
// And for useragent updates.
--- a/memory/mozjemalloc/jemalloc.c
+++ b/memory/mozjemalloc/jemalloc.c
@@ -542,21 +542,22 @@ static const bool isthreaded = true;
* Maximum size of L1 cache line. This is used to avoid cache line aliasing,
* so over-estimates are okay (up to a point), but under-estimates will
* negatively affect performance.
*/
#define CACHELINE_2POW 6
#define CACHELINE ((size_t)(1U << CACHELINE_2POW))
/*
- * Smallest size class to support. On Linux and Mac, even malloc(1) must
- * reserve a word's worth of memory (see Mozilla bug 691003).
+ * Smallest size class to support. On Windows the smallest allocation size
+ * must be 8 bytes on 32-bit, 16 bytes on 64-bit. On Linux and Mac, even
+ * malloc(1) must reserve a word's worth of memory (see Mozilla bug 691003).
*/
#ifdef MOZ_MEMORY_WINDOWS
-#define TINY_MIN_2POW 1
+#define TINY_MIN_2POW (sizeof(void*) == 8 ? 4 : 3)
#else
#define TINY_MIN_2POW (sizeof(void*) == 8 ? 3 : 2)
#endif
/*
* Maximum size class that is a multiple of the quantum, but not (necessarily)
* a power of 2. Above this size, allocations are rounded up to the nearest
* power of 2.
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -17,16 +17,17 @@
#include "nsProxyRelease.h"
#include "nsContentSecurityManager.h"
#include "nsIScriptSecurityManager.h"
#include "nsIPrincipal.h"
#include "nsIFileURL.h"
#include "mozilla/Preferences.h"
+#include "mozilla/Telemetry.h"
#include "mozilla/net/RemoteOpenFileChild.h"
#include "nsITabChild.h"
#include "private/pprio.h"
#include "nsInputStreamPump.h"
using namespace mozilla;
using namespace mozilla::net;
@@ -889,16 +890,22 @@ nsJARChannel::AsyncOpen(nsIStreamListene
// Not a local file...
// Check preferences to see if all remote jar support should be disabled
if (mBlockRemoteFiles) {
mIsUnsafe = true;
return NS_ERROR_UNSAFE_CONTENT_TYPE;
}
+ static bool reportedRemoteJAR = false;
+ if (!reportedRemoteJAR) {
+ reportedRemoteJAR = true;
+ Telemetry::Accumulate(Telemetry::REMOTE_JAR_PROTOCOL_USED, 1);
+ }
+
// kick off an async download of the base URI...
nsCOMPtr<nsIStreamListener> downloader = new MemoryDownloader(this);
uint32_t loadFlags =
mLoadFlags & ~(LOAD_DOCUMENT_URI | LOAD_CALL_CONTENT_SNIFFERS);
rv = NS_NewChannelInternal(getter_AddRefs(channel),
mJarBaseURI,
mLoadInfo,
mLoadGroup,
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -191,16 +191,19 @@ static DllBlockInfo sWindowsDllBlocklist
// NetOp School, discontinued product, bug 763395
{ "nlsp.dll", MAKE_VERSION(6, 23, 2012, 19) },
// Orbit Downloader, bug 1222819
{ "grabdll.dll", MAKE_VERSION(2, 6, 1, 0) },
{ "grabkernel.dll", MAKE_VERSION(1, 0, 0, 1) },
+ // ESET, bug 1229252
+ { "eOppMonitor.dll", ALL_VERSIONS },
+
{ nullptr, 0 }
};
#ifndef STATUS_DLL_NOT_FOUND
#define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L)
#endif
// define this for very verbose dll load debug spew
--- a/mozglue/linker/Zip.h
+++ b/mozglue/linker/Zip.h
@@ -67,23 +67,24 @@ private:
* Helper class for each buffer in StaticAllocator.
*/
template <size_t Size>
class ZStreamBuf
{
public:
ZStreamBuf() : inUse(false) { }
- char *get()
+ bool get(char*& out)
{
if (!inUse) {
inUse = true;
- return buf;
+ out = buf;
+ return true;
} else {
- MOZ_CRASH("ZStreamBuf already in use");
+ return false;
}
}
void Release()
{
memset(buf, 0, Size);
inUse = false;
}
@@ -101,38 +102,51 @@ public:
/**
* Special allocator that uses static buffers to allocate from.
*/
class StaticAllocator
{
public:
void *Alloc(uInt items, uInt size)
{
- if (items == 1 && size <= stateBuf.size) {
- return stateBuf.get();
- } else if (items * size == windowBuf.size) {
- return windowBuf.get();
+ if (items == 1 && size <= stateBuf1.size) {
+ char* res = nullptr;
+ if (stateBuf1.get(res) || stateBuf2.get(res)) {
+ return res;
+ }
+ MOZ_CRASH("ZStreamBuf already in use");
+ } else if (items * size == windowBuf1.size) {
+ char* res = nullptr;
+ if (windowBuf1.get(res) || windowBuf2.get(res)) {
+ return res;
+ }
+ MOZ_CRASH("ZStreamBuf already in use");
} else {
MOZ_CRASH("No ZStreamBuf for allocation");
}
}
void Free(void *ptr)
{
- if (stateBuf.Equals(ptr)) {
- stateBuf.Release();
- } else if (windowBuf.Equals(ptr)) {
- windowBuf.Release();
+ if (stateBuf1.Equals(ptr)) {
+ stateBuf1.Release();
+ } else if (stateBuf2.Equals(ptr)) {
+ stateBuf2.Release();
+ }else if (windowBuf1.Equals(ptr)) {
+ windowBuf1.Release();
+ } else if (windowBuf2.Equals(ptr)) {
+ windowBuf2.Release();
} else {
MOZ_CRASH("Pointer doesn't match a ZStreamBuf");
}
}
- ZStreamBuf<0x3000> stateBuf; // 0x3000 is an arbitrary size above 10K.
- ZStreamBuf<1 << MAX_WBITS> windowBuf;
+ // 0x3000 is an arbitrary size above 10K.
+ ZStreamBuf<0x3000> stateBuf1, stateBuf2;
+ ZStreamBuf<1 << MAX_WBITS> windowBuf1, windowBuf2;
};
private:
StaticAllocator *allocator;
};
/**
* Forward declaration
@@ -330,17 +344,17 @@ private:
/**
* Returns a pointer to the data associated with this header
*/
const void *GetData() const
{
return reinterpret_cast<const char *>(this) + sizeof(*this)
+ filenameSize + extraFieldSize;
}
-
+
le_uint16 minVersion;
le_uint16 generalFlag;
le_uint16 compression;
le_uint16 lastModifiedTime;
le_uint16 lastModifiedDate;
le_uint32 CRC32;
le_uint32 compressedSize;
le_uint32 uncompressedSize;
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -1193,17 +1193,18 @@ nsSocketTransport::BuildSocket(PRFileDes
proxyInfo = nullptr;
proxyTransparent = true;
}
}
if (NS_FAILED(rv)) {
SOCKET_LOG((" error pushing io layer [%u:%s rv=%x]\n", i, mTypes[i], rv));
if (fd) {
- CloseSocket(fd, mSocketTransportService->IsTelemetryEnabled());
+ CloseSocket(fd,
+ mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
}
}
}
return rv;
}
nsresult
@@ -1371,17 +1372,18 @@ nsSocketTransport::InitiateSocket()
opt.value.linger.polarity = 1;
opt.value.linger.linger = 0;
PR_SetSocketOption(fd, &opt);
#endif
// inform socket transport about this newly created socket...
rv = mSocketTransportService->AttachSocket(fd, this);
if (NS_FAILED(rv)) {
- CloseSocket(fd, mSocketTransportService->IsTelemetryEnabled());
+ CloseSocket(fd,
+ mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
return rv;
}
mAttached = true;
// assign mFD so that we can properly handle OnSocketDetached before we've
// established a connection.
{
MutexAutoLock lock(mLock);
@@ -1418,23 +1420,24 @@ nsSocketTransport::InitiateSocket()
}
NetAddrToPRNetAddr(&mNetAddr, &prAddr);
// We use PRIntervalTime here because we need
// nsIOService::LastOfflineStateChange time and
// nsIOService::LastConectivityChange time to be atomic.
PRIntervalTime connectStarted = 0;
- if (gSocketTransportService->IsTelemetryEnabled()) {
+ if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
connectStarted = PR_IntervalNow();
}
status = PR_Connect(fd, &prAddr, NS_SOCKET_CONNECT_TIMEOUT);
- if (gSocketTransportService->IsTelemetryEnabled() && connectStarted) {
+ if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
+ connectStarted) {
SendPRBlockingTelemetry(connectStarted,
Telemetry::PRCONNECT_BLOCKING_TIME_NORMAL,
Telemetry::PRCONNECT_BLOCKING_TIME_SHUTDOWN,
Telemetry::PRCONNECT_BLOCKING_TIME_CONNECTIVITY_CHANGE,
Telemetry::PRCONNECT_BLOCKING_TIME_LINK_CHANGE,
Telemetry::PRCONNECT_BLOCKING_TIME_OFFLINE);
}
@@ -1491,16 +1494,26 @@ nsSocketTransport::InitiateSocket()
!mProxyHost.IsEmpty()) {
code = PR_GetOSError();
rv = ErrorAccordingToNSPR(code);
}
//
// The connection was refused...
//
else {
+ if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
+ connectStarted) {
+ SendPRBlockingTelemetry(connectStarted,
+ Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_NORMAL,
+ Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_SHUTDOWN,
+ Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_CONNECTIVITY_CHANGE,
+ Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_LINK_CHANGE,
+ Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_OFFLINE);
+ }
+
rv = ErrorAccordingToNSPR(code);
if ((rv == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty())
rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
}
}
return rv;
}
@@ -1540,17 +1553,17 @@ nsSocketTransport::RecoverFromError()
mCondition != NS_ERROR_NET_TIMEOUT &&
mCondition != NS_ERROR_UNKNOWN_HOST &&
mCondition != NS_ERROR_UNKNOWN_PROXY_HOST)
return false;
bool tryAgain = false;
if ((mState == STATE_CONNECTING) && mDNSRecord &&
- mSocketTransportService->IsTelemetryEnabled()) {
+ mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
if (mNetAddr.raw.family == AF_INET) {
Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS);
} else if (mNetAddr.raw.family == AF_INET6) {
Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS);
}
}
@@ -1746,17 +1759,17 @@ nsSocketTransport::GetFD_Locked()
class ThunkPRClose : public nsRunnable
{
public:
explicit ThunkPRClose(PRFileDesc *fd) : mFD(fd) {}
NS_IMETHOD Run()
{
nsSocketTransport::CloseSocket(mFD,
- gSocketTransportService->IsTelemetryEnabled());
+ gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
return NS_OK;
}
private:
PRFileDesc *mFD;
};
void
STS_PRCloseOnSocketTransport(PRFileDesc *fd)
@@ -1783,17 +1796,18 @@ nsSocketTransport::ReleaseFD_Locked(PRFi
if (--mFDref == 0) {
if (gIOService->IsNetTearingDown() &&
((PR_IntervalNow() - gIOService->NetTearingDownStarted()) >
gSocketTransportService->MaxTimeForPrClosePref())) {
// If shutdown last to long, let the socket leak and do not close it.
SOCKET_LOG(("Intentional leak"));
} else if (PR_GetCurrentThread() == gSocketThread) {
SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%p]\n", this));
- CloseSocket(mFD, mSocketTransportService->IsTelemetryEnabled());
+ CloseSocket(mFD,
+ mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
} else {
// Can't PR_Close() a socket off STS thread. Thunk it to STS to die
STS_PRCloseOnSocketTransport(mFD);
}
mFD = nullptr;
}
}
@@ -1944,38 +1958,39 @@ nsSocketTransport::OnSocketReady(PRFileD
else if ((mState == STATE_CONNECTING) && !gIOService->IsNetTearingDown()) {
// We do not need to do PR_ConnectContinue when we are already
// shutting down.
// We use PRIntervalTime here because we need
// nsIOService::LastOfflineStateChange time and
// nsIOService::LastConectivityChange time to be atomic.
PRIntervalTime connectStarted = 0;
- if (gSocketTransportService->IsTelemetryEnabled()) {
+ if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
connectStarted = PR_IntervalNow();
}
PRStatus status = PR_ConnectContinue(fd, outFlags);
- if (gSocketTransportService->IsTelemetryEnabled() && connectStarted) {
+ if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
+ connectStarted) {
SendPRBlockingTelemetry(connectStarted,
Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_NORMAL,
Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_SHUTDOWN,
Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_CONNECTIVITY_CHANGE,
Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_LINK_CHANGE,
Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_OFFLINE);
}
if (status == PR_SUCCESS) {
//
// we are connected!
//
OnSocketConnected();
- if (mSocketTransportService->IsTelemetryEnabled()) {
+ if (mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
if (mNetAddr.raw.family == AF_INET) {
Telemetry::Accumulate(
Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS);
} else if (mNetAddr.raw.family == AF_INET6) {
Telemetry::Accumulate(
Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS);
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -20,16 +20,17 @@
#include "mozilla/Services.h"
#include "mozilla/Likely.h"
#include "mozilla/PublicSSL.h"
#include "mozilla/ChaosMode.h"
#include "mozilla/PodOperations.h"
#include "mozilla/Telemetry.h"
#include "nsThreadUtils.h"
#include "nsIFile.h"
+#include "nsIWidget.h"
using namespace mozilla;
using namespace mozilla::net;
LazyLogModule gSocketTransportLog("nsSocketTransport");
LazyLogModule gUDPSocketLog("UDPSocket");
nsSocketTransportService *gSocketTransportService = nullptr;
@@ -106,16 +107,17 @@ nsSocketTransportService::nsSocketTransp
, mKeepaliveIdleTimeS(600)
, mKeepaliveRetryIntervalS(1)
, mKeepaliveProbeCount(kDefaultTCPKeepCount)
, mKeepaliveEnabledPref(false)
, mServingPendingQueue(false)
, mMaxTimePerPollIter(100)
, mTelemetryEnabledPref(false)
, mMaxTimeForPrClosePref(PR_SecondsToInterval(5))
+ , mSleepPhase(false)
, mProbedMaxCount(false)
{
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
mActiveList = (SocketContext *)
moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
mIdleList = (SocketContext *)
@@ -553,16 +555,18 @@ nsSocketTransportService::Init()
tmpPrefService->AddObserver(MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN, this, false);
}
UpdatePrefs();
nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
if (obsSvc) {
obsSvc->AddObserver(this, "profile-initial-state", false);
obsSvc->AddObserver(this, "last-pb-context-exited", false);
+ obsSvc->AddObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC, true);
+ obsSvc->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, true);
}
mInitialized = true;
return NS_OK;
}
// called from main thread only
NS_IMETHODIMP
@@ -601,16 +605,23 @@ nsSocketTransportService::Shutdown()
nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (tmpPrefService)
tmpPrefService->RemoveObserver(SEND_BUFFER_PREF, this);
nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
if (obsSvc) {
obsSvc->RemoveObserver(this, "profile-initial-state");
obsSvc->RemoveObserver(this, "last-pb-context-exited");
+ obsSvc->RemoveObserver(this, NS_WIDGET_SLEEP_OBSERVER_TOPIC);
+ obsSvc->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC);
+ }
+
+ if (mAfterWakeUpTimer) {
+ mAfterWakeUpTimer->Cancel();
+ mAfterWakeUpTimer = nullptr;
}
mozilla::net::NetworkActivityMonitor::Shutdown();
mInitialized = false;
mShuttingDown = false;
return NS_OK;
@@ -1266,16 +1277,37 @@ nsSocketTransportService::Observe(nsISup
if (!strcmp(topic, "last-pb-context-exited")) {
nsCOMPtr<nsIRunnable> ev =
NS_NewRunnableMethod(this,
&nsSocketTransportService::ClosePrivateConnections);
nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
}
+ if (!strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
+ nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
+ if (timer == mAfterWakeUpTimer) {
+ mAfterWakeUpTimer = nullptr;
+ mSleepPhase = false;
+ }
+ } else if (!strcmp(topic, NS_WIDGET_SLEEP_OBSERVER_TOPIC)) {
+ mSleepPhase = true;
+ if (mAfterWakeUpTimer) {
+ mAfterWakeUpTimer->Cancel();
+ mAfterWakeUpTimer = nullptr;
+ }
+ } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
+ if (mSleepPhase && !mAfterWakeUpTimer) {
+ mAfterWakeUpTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (mAfterWakeUpTimer) {
+ mAfterWakeUpTimer->Init(this, 2000, nsITimer::TYPE_ONE_SHOT);
+ }
+ }
+ }
+
return NS_OK;
}
void
nsSocketTransportService::ClosePrivateConnections()
{
// Must be called on the socket thread.
#ifdef DEBUG
--- a/netwerk/base/nsSocketTransportService2.h
+++ b/netwerk/base/nsSocketTransportService2.h
@@ -14,16 +14,17 @@
#include "prinrval.h"
#include "mozilla/Logging.h"
#include "prinit.h"
#include "nsIObserver.h"
#include "mozilla/Mutex.h"
#include "mozilla/net/DashboardTypes.h"
#include "mozilla/Atomics.h"
#include "mozilla/TimeStamp.h"
+#include "nsITimer.h"
class nsASocketHandler;
struct PRPollDesc;
//-----------------------------------------------------------------------------
//
// set NSPR_LOG_MODULES=nsSocketTransport:5
@@ -106,17 +107,18 @@ public:
// Fills the passed array with socket information
void GetSocketConnections(nsTArray<mozilla::net::SocketInfo> *);
uint64_t GetSentBytes() { return mSentBytesCount; }
uint64_t GetReceivedBytes() { return mReceivedBytesCount; }
// Returns true if keepalives are enabled in prefs.
bool IsKeepaliveEnabled() { return mKeepaliveEnabledPref; }
- bool IsTelemetryEnabled() { return mTelemetryEnabledPref; }
+ bool IsTelemetryEnabledAndNotSleepPhase() { return mTelemetryEnabledPref &&
+ !mSleepPhase; }
PRIntervalTime MaxTimeForPrClosePref() {return mMaxTimeForPrClosePref; }
protected:
virtual ~nsSocketTransportService();
private:
//-------------------------------------------------------------------------
@@ -235,16 +237,21 @@ private:
// True if TCP keepalive is enabled globally.
bool mKeepaliveEnabledPref;
mozilla::Atomic<bool> mServingPendingQueue;
mozilla::Atomic<int32_t, mozilla::Relaxed> mMaxTimePerPollIter;
mozilla::Atomic<bool, mozilla::Relaxed> mTelemetryEnabledPref;
mozilla::Atomic<PRIntervalTime, mozilla::Relaxed> mMaxTimeForPrClosePref;
+ // Between a computer going to sleep and waking up the PR_*** telemetry
+ // will be corrupted - so do not record it.
+ mozilla::Atomic<bool, mozilla::Relaxed> mSleepPhase;
+ nsCOMPtr<nsITimer> mAfterWakeUpTimer;
+
void OnKeepaliveEnabledPrefChange();
void NotifyKeepaliveEnabledPrefChange(SocketContext *sock);
// Socket thread only for dynamically adjusting max socket size
#if defined(XP_WIN)
void ProbeMaxCount();
#endif
bool mProbedMaxCount;
--- a/netwerk/base/nsUDPSocket.cpp
+++ b/netwerk/base/nsUDPSocket.cpp
@@ -781,23 +781,23 @@ nsUDPSocket::CloseSocket()
if (gIOService->IsNetTearingDown() &&
((PR_IntervalNow() - gIOService->NetTearingDownStarted()) >
gSocketTransportService->MaxTimeForPrClosePref())) {
// If shutdown last to long, let the socket leak and do not close it.
UDPSOCKET_LOG(("Intentional leak"));
} else {
PRIntervalTime closeStarted = 0;
- if (gSocketTransportService->IsTelemetryEnabled()) {
+ if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
closeStarted = PR_IntervalNow();
}
PR_Close(mFD);
- if (gSocketTransportService->IsTelemetryEnabled()) {
+ if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
PRIntervalTime now = PR_IntervalNow();
if (gIOService->IsNetTearingDown()) {
Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_SHUTDOWN,
PR_IntervalToMilliseconds(now - closeStarted));
} else if (PR_IntervalToSeconds(now - gIOService->LastConnectivityChange())
< 60) {
Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_CONNECTIVITY_CHANGE,
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1090,22 +1090,25 @@ HttpChannelChild::FailedAsyncOpen(const
// We're already being called from IPDL, therefore already "async"
HandleAsyncAbort();
}
void
HttpChannelChild::DoNotifyListenerCleanup()
{
LOG(("HttpChannelChild::DoNotifyListenerCleanup [this=%p]\n", this));
- if (mIPCOpen)
- PHttpChannelChild::Send__delete__(this);
+
if (mInterceptListener) {
mInterceptListener->Cleanup();
mInterceptListener = nullptr;
}
+
+ if (mIPCOpen) {
+ PHttpChannelChild::Send__delete__(this);
+ }
}
class DeleteSelfEvent : public ChannelEvent
{
public:
explicit DeleteSelfEvent(HttpChannelChild* child) : mChild(child) {}
void Run() { mChild->DeleteSelf(); }
private:
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -1241,17 +1241,21 @@ class RecursiveMakeBackend(CommonBackend
dest = mozpath.join(reltarget, path, f.target_basename)
if not isinstance(f, ObjDirPath):
if '*' in f:
if not isinstance(f, SourcePath):
raise Exception("Wildcards are only supported in "
"SourcePath objects in %s. Path is: %s" % (
type(obj), f
))
- install_manifest.add_pattern_symlink(f.srcdir, f, path)
+ if f.startswith('/'):
+ basepath = f.full_path.rstrip('*')
+ install_manifest.add_pattern_symlink(basepath, '*', path)
+ else:
+ install_manifest.add_pattern_symlink(f.srcdir, f, path)
else:
install_manifest.add_symlink(f.full_path, dest)
else:
install_manifest.add_optional_exists(dest)
backend_file.write('%s_FILES += %s\n' % (
target_var, self._pretty_path(f, backend_file)))
have_objdir_files = True
if have_objdir_files:
--- a/python/mozbuild/mozbuild/virtualenv.py
+++ b/python/mozbuild/mozbuild/virtualenv.py
@@ -168,16 +168,21 @@ class VirtualenvManager(object):
Receives the path to virtualenv's virtualenv.py script (which will be
called out to), the path to create the virtualenv in, and a handle to
write output to.
"""
env = dict(os.environ)
env.pop('PYTHONDONTWRITEBYTECODE', None)
args = [python, self.virtualenv_script_path,
+ # Without this, virtualenv.py may attempt to contact the outside
+ # world and search for or download a newer version of pip,
+ # setuptools, or wheel. This is bad for security, reproducibility,
+ # and speed.
+ '--no-download',
self.virtualenv_root]
result = subprocess.call(args, stdout=self.log_handle,
stderr=subprocess.STDOUT, env=env)
if result:
raise Exception(
'Failed to create virtualenv: %s' % self.virtualenv_root)
--- a/python/virtualenv/LICENSE.txt
+++ b/python/virtualenv/LICENSE.txt
@@ -1,11 +1,11 @@
Copyright (c) 2007 Ian Bicking and Contributors
Copyright (c) 2009 Ian Bicking, The Open Planning Project
-Copyright (c) 2011-2015 The virtualenv developers
+Copyright (c) 2011-2016 The virtualenv developers
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
--- a/python/virtualenv/PKG-INFO
+++ b/python/virtualenv/PKG-INFO
@@ -1,11 +1,11 @@
Metadata-Version: 1.1
Name: virtualenv
-Version: 14.0.1
+Version: 15.0.1
Summary: Virtual Python Environment builder
Home-page: https://virtualenv.pypa.io/
Author: Jannis Leidel, Carl Meyer and Brian Rosner
Author-email: python-virtualenv@groups.google.com
License: MIT
Description: Virtualenv
==========
@@ -42,72 +42,46 @@ Description: Virtualenv
share libraries with other virtualenv environments (and optionally
doesn't access the globally installed libraries either).
.. comment:
Release History
===============
- 14.0.1 (2016-01-21)
- -------------------
-
- * Upgrade from pip 8.0.0 to 8.0.2.
-
- * Fix the default of ``--(no-)download`` to default to downloading.
-
-
- 14.0.0 (2016-01-19)
+ 15.0.1 (2016-03-17)
-------------------
- * **BACKWARDS INCOMPATIBLE** Drop support for Python 3.2.
-
- * Upgrade setuptools to 19.4
+ * Print error message when DEST_DIR exists and is a file
- * Upgrade wheel to 0.26.0
-
- * Upgrade pip to 8.0.0
+ * Upgrade setuptools to 20.3
- * Upgrade argparse to 1.4.0
+ * Upgrade pip to 8.1.1.
- * Added support for ``python-config`` script (PR #798)
- * Updated activate.fish (PR #589) (PR #799)
-
- * Account for a ``site.pyo`` correctly in some python implementations (PR #759)
-
- * Properly restore an empty PS1 (#407)
+ 15.0.0 (2016-03-05)
+ -------------------
- * Properly remove ``pydoc`` when deactivating
-
- * Remove workaround for very old Mageia / Mandriva linuxes (PR #472)
-
- * Added a space after virtualenv name in the prompt: ``(env) $PS1``
-
- * Make sure not to run a --user install when creating the virtualenv (PR #803)
+ * Remove the `virtualenv-N.N` script from the package; this can no longer be
+ correctly created from a wheel installation.
+ Resolves #851, #692
- * Remove virtualenv file's path from directory when executing with a new
- python. Fixes issue #779, #763 (PR #805)
-
- * Remove use of () in .bat files so ``Program Files (x86)`` works #35
+ * Remove accidental runtime dependency on pip by extracting certificate in the
+ subprocess.
- * Download new releases of the preinstalled software from PyPI when there are
- new releases available. This behavior can be disabled using
- ``--no-download``.
+ * Upgrade setuptools 20.2.2.
- * Make ``--no-setuptools``, ``--no-pip``, and ``--no-wheel`` independent of
- each other.
+ * Upgrade pip to 8.1.0.
`Full Changelog <https://virtualenv.pypa.io/en/latest/changes.html>`_.
Keywords: setuptools deployment installation distutils
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.1
-Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
--- a/python/virtualenv/README.rst
+++ b/python/virtualenv/README.rst
@@ -1,18 +1,29 @@
virtualenv
==========
+A tool for creating isolated 'virtual' python environments.
+
.. image:: https://img.shields.io/pypi/v/virtualenv.svg
:target: https://pypi.python.org/pypi/virtualenv
.. image:: https://img.shields.io/travis/pypa/virtualenv/develop.svg
:target: http://travis-ci.org/pypa/virtualenv
-For documentation, see https://virtualenv.pypa.io/
+* `Installation <https://virtualenv.pypa.io/en/latest/installation.html>`_
+* `Documentation <https://virtualenv.pypa.io/>`_
+* `Changelog <https://virtualenv.pypa.io/en/latest/changes.html>`_
+* `Issues <https://github.com/pypa/virtualenv/issues>`_
+* `PyPI <https://pypi.python.org/pypi/virtualenv/>`_
+* `Github <https://github.com/pypa/virtualenv>`_
+* `User mailing list <http://groups.google.com/group/python-virtualenv>`_
+* `Dev mailing list <http://groups.google.com/group/pypa-dev>`_
+* User IRC: #pypa on Freenode.
+* Dev IRC: #pypa-dev on Freenode.
Code of Conduct
---------------
Everyone interacting in the virtualenv project's codebases, issue trackers,
chat rooms, and mailing lists is expected to follow the
`PyPA Code of Conduct`_.
--- a/python/virtualenv/docs/changes.rst
+++ b/python/virtualenv/docs/changes.rst
@@ -1,11 +1,78 @@
Release History
===============
+15.0.1 (2016-03-17)
+-------------------
+
+* Print error message when DEST_DIR exists and is a file
+
+* Upgrade setuptools to 20.3
+
+* Upgrade pip to 8.1.1.
+
+
+15.0.0 (2016-03-05)
+-------------------
+
+* Remove the `virtualenv-N.N` script from the package; this can no longer be
+ correctly created from a wheel installation.
+ Resolves :issue:`851`, :issue:`692`
+
+* Remove accidental runtime dependency on pip by extracting certificate in the
+ subprocess.
+
+* Upgrade setuptools 20.2.2.
+
+* Upgrade pip to 8.1.0.
+
+
+14.0.6 (2016-02-07)
+-------------------
+
+* Upgrade setuptools to 20.0
+
+* Upgrade wheel to 0.29.0
+
+* Fix an error where virtualenv didn't pass in a working ssl certificate for
+ pip, causing "weird" errors related to ssl.
+
+
+14.0.5 (2016-02-01)
+-------------------
+
+* Homogenize drive letter casing for both prefixes and filenames. :issue:`858`
+
+
+14.0.4 (2016-01-31)
+-------------------
+
+* Upgrade setuptools to 19.6.2
+
+* Revert ac4ea65; only correct drive letter case.
+ Fixes :issue:`856`, :issue:`815`
+
+
+14.0.3 (2016-01-28)
+-------------------
+
+* Upgrade setuptools to 19.6.1
+
+
+14.0.2 (2016-01-28)
+-------------------
+
+* Upgrade setuptools to 19.6
+
+* Supress any errors from `unset` on different shells (:pull:`843`)
+
+* Normalize letter case for prefix path checking. Fixes :issue:`837`
+
+
14.0.1 (2016-01-21)
-------------------
* Upgrade from pip 8.0.0 to 8.0.2.
* Fix the default of ``--(no-)download`` to default to downloading.
@@ -33,17 +100,17 @@ 14.0.0 (2016-01-19)
* Properly remove ``pydoc`` when deactivating
* Remove workaround for very old Mageia / Mandriva linuxes (:pull:`472`)
* Added a space after virtualenv name in the prompt: ``(env) $PS1``
* Make sure not to run a --user install when creating the virtualenv (:pull:`803`)
-* Remove virtualenv file's path from directory when executing with a new
+* Remove virtualenv.py's path from sys.path when executing with a new
python. Fixes issue :issue:`779`, :issue:`763` (:pull:`805`)
* Remove use of () in .bat files so ``Program Files (x86)`` works :issue:`35`
* Download new releases of the preinstalled software from PyPI when there are
new releases available. This behavior can be disabled using
``--no-download``.
@@ -51,25 +118,27 @@ 14.0.0 (2016-01-19)
each other.
13.1.2 (2015-08-23)
-------------------
* Upgrade pip to 7.1.2.
+
13.1.1 (2015-08-20)
-------------------
* Upgrade pip to 7.1.1.
* Upgrade setuptools to 18.2.
* Make the activate script safe to use when bash is running with ``-u``.
+
13.1.0 (2015-06-30)
-------------------
* Upgrade pip to 7.1.0
* Upgrade setuptools to 18.0.1
--- a/python/virtualenv/docs/userguide.rst
+++ b/python/virtualenv/docs/userguide.rst
@@ -40,17 +40,19 @@ In a newly created virtualenv there will
script. For Windows systems, activation scripts are provided for
the Command Prompt and Powershell.
On Posix systems, this resides in :file:`/ENV/bin/`, so you can run::
$ source bin/activate
For some shells (e.g. the original Bourne Shell) you may need to use the
-:command:`.` command, when :command:`source` does not exist.
+:command:`.` command, when :command:`source` does not exist. There are also
+separate activate files for some other shells, like csh and fish.
+:file:`bin/activate` should work for bash/zsh/dash.
This will change your ``$PATH`` so its first entry is the virtualenv's
``bin/`` directory. (You have to use ``source`` because it changes your
shell environment in-place.) This is all it does; it's purely a
convenience. If you directly run a script or the python interpreter
from the virtualenv's ``bin/`` directory (e.g. ``path/to/ENV/bin/pip``
or ``/path/to/ENV/bin/python-script.py``) there's no need for
activation.
--- a/python/virtualenv/setup.cfg
+++ b/python/virtualenv/setup.cfg
@@ -1,8 +1,8 @@
-[wheel]
+[bdist_wheel]
universal = 1
[egg_info]
-tag_svn_revision = 0
tag_date = 0
tag_build =
+tag_svn_revision = 0
--- a/python/virtualenv/setup.py
+++ b/python/virtualenv/setup.py
@@ -24,36 +24,31 @@ try:
def run_tests(self):
# import here, because outside the eggs aren't loaded
import pytest
sys.exit(pytest.main(self.pytest_args))
setup_params = {
'entry_points': {
- 'console_scripts': [
- 'virtualenv=virtualenv:main',
- 'virtualenv-%s.%s=virtualenv:main' % sys.version_info[:2]
- ],
+ 'console_scripts': ['virtualenv=virtualenv:main'],
},
'zip_safe': False,
'cmdclass': {'test': PyTest},
'tests_require': ['pytest', 'mock'],
}
except ImportError:
from distutils.core import setup
if sys.platform == 'win32':
print('Note: without Setuptools installed you will '
'have to use "python -m virtualenv ENV"')
setup_params = {}
else:
script = 'scripts/virtualenv'
- script_ver = script + '-%s.%s' % sys.version_info[:2]
- shutil.copy(script, script_ver)
- setup_params = {'scripts': [script, script_ver]}
+ setup_params = {'scripts': [script]}
def read_file(*paths):
here = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(here, *paths)) as f:
return f.read()
# Get long_description from index.rst:
@@ -106,20 +101,19 @@ setup(
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.1',
- 'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
],
keywords='setuptools deployment installation distutils',
author='Ian Bicking',
author_email='ianb@colorstudy.com',
maintainer='Jannis Leidel, Carl Meyer and Brian Rosner',
maintainer_email='python-virtualenv@groups.google.com',
url='https://virtualenv.pypa.io/',
license='MIT',
new file mode 100644
--- /dev/null
+++ b/python/virtualenv/tests/test_cmdline.py
@@ -0,0 +1,44 @@
+import sys
+import subprocess
+import virtualenv
+import pytest
+
+VIRTUALENV_SCRIPT = virtualenv.__file__
+
+def test_commandline_basic(tmpdir):
+ """Simple command line usage should work"""
+ subprocess.check_call([
+ sys.executable,
+ VIRTUALENV_SCRIPT,
+ str(tmpdir.join('venv'))
+ ])
+
+def test_commandline_explicit_interp(tmpdir):
+ """Specifying the Python interpreter should work"""
+ subprocess.check_call([
+ sys.executable,
+ VIRTUALENV_SCRIPT,
+ '-p', sys.executable,
+ str(tmpdir.join('venv'))
+ ])
+
+# The registry lookups to support the abbreviated "-p 3.5" form of specifying
+# a Python interpreter on Windows don't seem to work with Python 3.5. The
+# registry layout is not well documented, and it's not clear that the feature
+# is sufficiently widely used to be worth fixing.
+# See https://github.com/pypa/virtualenv/issues/864
+@pytest.mark.skipif("sys.platform == 'win32' and sys.version_info[:2] >= (3,5)")
+def test_commandline_abbrev_interp(tmpdir):
+ """Specifying abbreviated forms of the Python interpreter should work"""
+ if sys.platform == 'win32':
+ fmt = '%s.%s'
+ else:
+ fmt = 'python%s.%s'
+ abbrev = fmt % (sys.version_info[0], sys.version_info[1])
+ subprocess.check_call([
+ sys.executable,
+ VIRTUALENV_SCRIPT,
+ '-p', abbrev,
+ str(tmpdir.join('venv'))
+ ])
+
--- a/python/virtualenv/virtualenv.py
+++ b/python/virtualenv/virtualenv.py
@@ -20,25 +20,28 @@ import re
import shutil
import logging
import zlib
import errno
import glob
import distutils.sysconfig
import struct
import subprocess
+import pkgutil
+import tempfile
+import textwrap
from distutils.util import strtobool
from os.path import join
try:
import ConfigParser
except ImportError:
import configparser as ConfigParser
-__version__ = "14.0.1"
+__version__ = "15.0.1"
virtualenv_version = __version__ # legacy
if sys.version_info < (2, 6):
print('ERROR: %s' % sys.exc_info()[1])
print('ERROR: this script requires Python 2.6 or greater.')
sys.exit(101)
try:
@@ -346,32 +349,29 @@ def copyfile(src, dest, symlink=True):
copyfileordir(src, dest, symlink)
else:
logger.info('Copying to %s', dest)
copyfileordir(src, dest, symlink)
def writefile(dest, content, overwrite=True):
if not os.path.exists(dest):
logger.info('Writing %s', dest)
- f = open(dest, 'wb')
- f.write(content.encode('utf-8'))
- f.close()
+ with open(dest, 'wb') as f:
+ f.write(content.encode('utf-8'))
return
else:
- f = open(dest, 'rb')
- c = f.read()
- f.close()
+ with open(dest, 'rb') as f:
+ c = f.read()
if c != content.encode("utf-8"):
if not overwrite:
logger.notify('File %s exists with different content; not overwriting', dest)
return
logger.notify('Overwriting %s with new content', dest)
- f = open(dest, 'wb')
- f.write(content.encode('utf-8'))
- f.close()
+ with open(dest, 'wb') as f:
+ f.write(content.encode('utf-8'))
else:
logger.info('Content %s already in place', dest)
def rmtree(dir):
if os.path.exists(dir):
logger.notify('Deleting tree %s', dir)
shutil.rmtree(dir)
else:
@@ -672,16 +672,21 @@ def main():
if len(args) > 1:
print('There must be only one argument: DEST_DIR (you gave %s)' % (
' '.join(args)))
parser.print_help()
sys.exit(2)
home_dir = args[0]
+ if os.path.exists(home_dir) and os.path.isfile(home_dir):
+ logger.fatal('ERROR: File already exists and is not a directory.')
+ logger.fatal('Please provide a different path or delete the file.')
+ sys.exit(3)
+
if os.environ.get('WORKING_ENV'):
logger.fatal('ERROR: you cannot run virtualenv while in a workingenv')
logger.fatal('Please deactivate your workingenv, then re-run this script')
sys.exit(3)
if 'PYTHONHOME' in os.environ:
logger.warn('PYTHONHOME is set. You *must* activate the virtualenv before using it')
del os.environ['PYTHONHOME']
@@ -702,17 +707,17 @@ def main():
no_wheel=options.no_wheel,
symlink=options.symlink)
if 'after_install' in globals():
after_install(options, home_dir)
def call_subprocess(cmd, show_stdout=True,
filter_stdout=None, cwd=None,
raise_on_returncode=True, extra_env=None,
- remove_from_env=None):
+ remove_from_env=None, stdin=None):
cmd_parts = []
for part in cmd:
if len(part) > 45:
part = part[:20]+"..."+part[-20:]
if ' ' in part or '\n' in part or '"' in part or "'" in part:
part = '"%s"' % part.replace('"', '\\"')
if hasattr(part, 'decode'):
try:
@@ -732,25 +737,31 @@ def call_subprocess(cmd, show_stdout=Tru
env.update(extra_env)
if remove_from_env:
for varname in remove_from_env:
env.pop(varname, None)
else:
env = None
try:
proc = subprocess.Popen(
- cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout,
+ cmd, stderr=subprocess.STDOUT,
+ stdin=None if stdin is None else subprocess.PIPE,
+ stdout=stdout,
cwd=cwd, env=env)
except Exception:
e = sys.exc_info()[1]
logger.fatal(
"Error %s while executing command %s" % (e, cmd_desc))
raise
all_output = []
if stdout is not None:
+ if stdin is not None:
+ proc.stdin.write(stdin)
+ proc.stdin.close()
+
stdout = proc.stdout
encoding = sys.getdefaultencoding()
fs_encoding = sys.getfilesystemencoding()
while 1:
line = stdout.readline()
try:
line = line.decode(encoding)
except UnicodeDecodeError:
@@ -764,17 +775,17 @@ def call_subprocess(cmd, show_stdout=Tru
if isinstance(level, tuple):
level, line = level
logger.log(level, line)
if not logger.stdout_level_matches(level):
logger.show_progress()
else:
logger.info(line)
else:
- proc.communicate()
+ proc.communicate(stdin)
proc.wait()
if proc.returncode:
if raise_on_returncode:
if all_output:
logger.notify('Complete output from command %s:' % cmd_desc)
logger.notify('\n'.join(all_output) + '\n----------------------------------------')
raise OSError(
"Command %s failed with error code %s"
@@ -832,20 +843,45 @@ def install_wheel(project_names, py_exec
from urllib.parse import urljoin
from urllib.request import pathname2url
def space_path2url(p):
if ' ' not in p:
return p
return urljoin('file:', pathname2url(os.path.abspath(p)))
findlinks = ' '.join(space_path2url(d) for d in search_dirs)
- cmd = [
- py_executable, '-c',
- 'import sys, pip; sys.exit(pip.main(["install", "--ignore-installed"] + sys.argv[1:]))',
- ] + project_names
+ SCRIPT = textwrap.dedent("""
+ import sys
+ import pkgutil
+ import tempfile
+ import os
+
+ import pip
+
+ cert_data = pkgutil.get_data("pip._vendor.requests", "cacert.pem")
+ if cert_data is not None:
+ cert_file = tempfile.NamedTemporaryFile(delete=False)
+ cert_file.write(cert_data)
+ cert_file.close()
+ else:
+ cert_file = None
+
+ try:
+ args = ["install", "--ignore-installed"]
+ if cert_file is not None:
+ args += ["--cert", cert_file.name]
+ args += sys.argv[1:]
+
+ sys.exit(pip.main(args))
+ finally:
+ if cert_file is not None:
+ os.remove(cert_file.name)
+ """).encode("utf8")
+
+ cmd = [py_executable, '-'] + project_names
logger.start_progress('Installing %s...' % (', '.join(project_names)))
logger.indent += 2
env = {
"PYTHONPATH": pythonpath,
"JYTHONPATH": pythonpath, # for Jython < 3.x
"PIP_FIND_LINKS": findlinks,
"PIP_USE_WHEEL": "1",
@@ -853,21 +889,22 @@ def install_wheel(project_names, py_exec
"PIP_PRE": "1",
"PIP_USER": "0",
}
if not download:
env["PIP_NO_INDEX"] = "1"
try:
- call_subprocess(cmd, show_stdout=False, extra_env=env)
+ call_subprocess(cmd, show_stdout=False, extra_env=env, stdin=SCRIPT)
finally:
logger.indent -= 2
logger.end_progress()
+
def create_environment(home_dir, site_packages=False, clear=False,
unzip_setuptools=False,
prompt=None, search_dirs=None, download=False,
no_setuptools=False, no_pip=False, no_wheel=False,
symlink=True):
"""
Creates a new environment in ``home_dir``.
@@ -973,16 +1010,22 @@ def change_prefix(filename, dst_prefix):
prefixes.append(sys.real_prefix)
if hasattr(sys, 'base_prefix'):
prefixes.append(sys.base_prefix)
prefixes = list(map(os.path.expanduser, prefixes))
prefixes = list(map(os.path.abspath, prefixes))
# Check longer prefixes first so we don't split in the middle of a filename
prefixes = sorted(prefixes, key=len, reverse=True)
filename = os.path.abspath(filename)
+ # On Windows, make sure drive letter is uppercase
+ if is_win and filename[0] in 'abcdefghijklmnopqrstuvwxyz':
+ filename = filename[0].upper() + filename[1:]
+ for i, prefix in enumerate(prefixes):
+ if is_win and prefix[0] in 'abcdefghijklmnopqrstuvwxyz':
+ prefixes[i] = prefix[0].upper() + prefix[1:]
for src_prefix in prefixes:
if filename.startswith(src_prefix):
_, relpath = filename.split(src_prefix, 1)
if src_prefix != os.sep: # sys.prefix == "/"
assert relpath[0] == os.sep
relpath = relpath[1:]
return join(dst_prefix, relpath)
assert False, "Filename %s does not start with any of these prefixes: %s" % \
@@ -1552,26 +1595,24 @@ def fixup_scripts(home_dir, bin_dir):
# This is what we'll put:
new_shebang = '#!%s python%s%s' % new_shebang_args
for filename in os.listdir(bin_dir):
filename = os.path.join(bin_dir, filename)
if not os.path.isfile(filename):
# ignore subdirs, e.g. .svn ones.
continue
- f = open(filename, 'rb')
- try:
+ lines = None
+ with open(filename, 'rb') as f:
try:
lines = f.read().decode('utf-8').splitlines()
except UnicodeDecodeError:
# This is probably a binary program instead
# of a script, so just ignore it.
continue
- finally:
- f.close()
if not lines:
logger.warn('Script %s is an empty file' % filename)
continue
old_shebang = lines[0].strip()
old_shebang = old_shebang[0:2] + os.path.normcase(old_shebang[2:])
if not old_shebang.startswith(shebang):
@@ -1580,19 +1621,19 @@ def fixup_scripts(home_dir, bin_dir):
elif lines[0].strip() == new_shebang:
logger.info('Script %s has already been made relative' % filename)
else:
logger.warn('Script %s cannot be made relative (it\'s not a normal script that starts with %s)'
% (filename, shebang))
continue
logger.notify('Making script %s relative' % filename)
script = relative_script([new_shebang] + lines[1:])
- f = open(filename, 'wb')
- f.write('\n'.join(script).encode('utf-8'))
- f.close()
+ with open(filename, 'wb') as f:
+ f.write('\n'.join(script).encode('utf-8'))
+
def relative_script(lines):
"Return a script that'll work in a relocatable environment."
activate = "import os; activate_this=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'activate_this.py'); exec(compile(open(activate_this).read(), activate_this, 'exec'), dict(__file__=activate_this)); del os, activate_this"
# Find the last future statement in the script. If we insert the activation
# line before a future statement, Python will raise a SyntaxError.
activate_at = None
for idx, line in reversed(list(enumerate(lines))):
@@ -1629,49 +1670,45 @@ def fixup_pth_and_egg_link(home_dir, sys
if not os.access(filename, os.W_OK):
logger.warn('Cannot write .egg-link file %s, skipping' % filename)
else:
fixup_egg_link(filename)
def fixup_pth_file(filename):
lines = []
prev_lines = []
- f = open(filename)
- prev_lines = f.readlines()
- f.close()
+ with open(filename) as f:
+ prev_lines = f.readlines()
for line in prev_lines:
line = line.strip()
if (not line or line.startswith('#') or line.startswith('import ')
or os.path.abspath(line) != line):
lines.append(line)
else:
new_value = make_relative_path(filename, line)
if line != new_value:
logger.debug('Rewriting path %s as %s (in %s)' % (line, new_value, filename))
lines.append(new_value)
if lines == prev_lines:
logger.info('No changes to .pth file %s' % filename)
return
logger.notify('Making paths in .pth file %s relative' % filename)
- f = open(filename, 'w')
- f.write('\n'.join(lines) + '\n')
- f.close()
+ with open(filename, 'w') as f:
+ f.write('\n'.join(lines) + '\n')
def fixup_egg_link(filename):
- f = open(filename)
- link = f.readline().strip()
- f.close()
+ with open(filename) as f:
+ link = f.readline().strip()
if os.path.abspath(link) != link:
logger.debug('Link in %s already relative' % filename)
return
new_link = make_relative_path(filename, link)
logger.notify('Rewriting link %s in %s as %s' % (link, filename, new_link))
- f = open(filename, 'w')
- f.write(new_link)
- f.close()
+ with open(filename, 'w') as f:
+ f.write(new_link)
def make_relative_path(source, dest, dest_is_directory=True):
"""
Make a filename relative, where the filename is dest, and it is
being referred to from the filename source.
>>> make_relative_path('/usr/share/something/a-file.pth',
... '/usr/share/another-place/src/Directory')
@@ -1744,19 +1781,18 @@ def create_bootstrap_script(extra_text,
If you provide something like ``python_version='2.5'`` then the
script will start with ``#!/usr/bin/env python2.5`` instead of
``#!/usr/bin/env python``. You can use this when the script must
be run with a particular Python version.
"""
filename = __file__
if filename.endswith('.pyc'):
filename = filename[:-1]
- f = codecs.open(filename, 'r', encoding='utf-8')
- content = f.read()
- f.close()
+ with codecs.open(filename, 'r', encoding='utf-8') as f:
+ content = f.read()
py_exe = 'python%s' % python_version
content = (('#!/usr/bin/env %s\n' % py_exe)
+ '## WARNING: This file is generated\n'
+ content)
return content.replace('##EXT' 'END##', extra_text)
##EXTEND##
@@ -1906,30 +1942,30 @@ MXdeKul0J2Ypp5Suh3s1JySsW1w5UNknGNrbdEpS
n6kJTcsp4tG42yeT7nQbtdUFwgVJjwDSUYEAC8F0dKOTILrlLO/xC70bnNd0Ha97whQ6UkHJYj5H
cA/j+zX4tbtTIfGjujOKpj83aHOgXnIQbvYduNXEC4UMm4T21Bs+GHABuCa7v//LR/TvpjHa7oe7
/Grb6lVvHSD7spj5iplBLRKZxxEYGdCbY9LWWC5hBB2voWno6DJUMzfkC3T8KJsWL9umDQY5szPt
AVijEPwfucjncQ==
""")
##file activate.sh
ACTIVATE_SH = convert("""
-eJytVdtu2kAQffdXDAalSVqK6GMrqhIFCaQEIkyp2qZyFnuIVzVrtLsmIZd/76wvYONAHxI/gHdn
-dvbMnDPjOkwCrmDOQ4RFrDTMEGKFPtxxHYCtolh6CDMuWszTfMU02nA6l9ECZkwFp1Yd1lEMHhMi
-0iBjAVyDzyV6Olxblo/5KTg+gUcL6ImFQg3NOSzXfuRZyV4dJJrdKPQBxYrLSCxQaFgxydksRJV5
-1eA3NB+g8Tjtjt+7z/CHzulYCgVaxgh8DmQAysdHL2SS0mAaWBgmx8manbcbj+7o4tydDsaT790L
-96o76VM4m+J9AR2gSPzNYywdu1HxtjceeL+MpE4cN3tpipVDiX3O/wfm56Q/GvZHl709kDb2CrCN
-pQpvYzoIsuxFULO6JxpRQRQTPz5qYjehH5jw4UEFH+Au4F4AAVshMPojkxctFsasA6LAKCsLRfry
-iBGiRkdwSwhIMPQ2j6RZLBlJMDuqPgL8IBVGsc7MmovbLEzJ0RQIGqbE4AVM3KKCO5Iz883PGow0
-6VqS2JKQo58TQOUXpvxnXaffTEr99LTZ/OX03Wlv7AxGw+ZLNCRJNiV8+trycdUScaayvGgHCHba
-e5h12hVKnXaVS6d9kMTMnANJXXJrbzjdpl8z2NomvQ7YIhI+Kuoj07G4A68ODoZzyB1qOwCaxpS3
-en77s0XTIbVzKTHEFSu1dGE4lO+2rALaju26haXr2lZWh2JKVqXZqJJpo2aLgnfLdc8GQ3fYvey5
-7ufMrdjHG9zbhjAFox2rROuhVt3TWAbWTpvuXmUZ5lJ5JrcUsz8fON2zi557NR5dXk0qwtwVgrkt
-V1AS0b7fVjONQQWFWgfu98ix6r6NiKHCsvfxDY0FFGyBcF0q+bV9cwLbk9kQLAja5FyHS/YXQcUS
-zUiIBQs5U+l3wsDn+p2iaS6R+WsDVaJV9Ch0IhRej47KkSwrdd98kJZrmjECmossjt34ZqfifZOx
-9wYj75Xj7jWj7qUxR1z9A7WjbI8=
+eJytVd9v2kAMfs9fYQLq2m4MscdNVKMqEkgtVIQxbeuUHolpTgsXdHehpT/+9/mSEBJS2MOaB0ji
+z77P9menDpOAK5jzEGERKw0zhFihD/dcB2CrKJYewoyLFvM0XzGNNpzOZbSAGVPBqVWHdRSDx4SI
+NMhYANfgc4meDteW5ePGC45P4MkCumKhUENzDsu1H3lw1vJx1RJxGMKns6O2lWDqINGgotAHFCsu
+I7FAoWHFJGezEFWGqsEvaD5C42naHb93X+A3+elYCgVaxgh8DmQAys9HL2SS0mIaWBgm7mTN/O3G
+kzu6vHCng/HkW/fSve5O+hTOpnhfQAcoEry5jKVjNypoO0fgwzKSOgHm79KUK06Jfc7/RebHpD8a
+9kdXvT2UcnuFWG6p0stNB0mWUUQ1q3uiGRVEMfXHR03dTuQATPjwqIIPcB9wL4CArRAY/ZHJixYL
+Y9YBtcAoLQtFevOoI9QaHcEdMSAB0d08kuZhyUiSmav6CPCdVBnFOjNrLu6yMCWgKRA0TInBC5i4
+QwX3JG/mm581GKnSsSSxJTFHf9MAKr8w5T/vOv1mUurn5/zlT6fvTntjZzAaNl9rQ5JkU5KIc0GX
+inagwU57T2eddqWlTrvaS6d9sImZeUMkhWysveF0m37NcGub9Dpgi0j4qGiOzATjDr06OBjOYQOo
+7RBoGtNm9Denv1i0LVI7lxJDXLHSSBeWRflsyyqw7diuW3h0XdvK6lBMyaoMG1UyHdTsoYBuue75
+YOgOu1c91/2cwYpznPPeDoQpGL2xSm09NKp7BsvQ2hnT3aMs07lUnskpxewvBk73/LLnXo9HV9eT
+ijB3hWBO2ygoiWg/bKuZxqCCQq0DD3vkWIVvI2KosIw+vqW1gIItEG5KJb+xb09g65ktwYKgTc51
+uGJ/EFQs0ayEWLCQM5V9N4g+1+8UbXOJzF8bqhKtIqIwicWvzNFROZJlpfD8A7Vc044R0FxkcezG
+VzsV75usvTdYef+57v5n1b225qhXfwEmxHEs
""")
##file activate.fish
ACTIVATE_FISH = convert("""
eJyFVVFv0zAQfs+vONJO3RDNxCsSQoMVrdK2Vl03CSHkesllMXLsYDvZivjx2GmTOG0YfWhV+7u7
73z33Y1gnTENKeMIeakNPCKUGhP7xcQTbCJ4ZOKcxoZV1GCUMp1t4O0zMxkTQEGVQjicO4dTyIwp
Ppyfu386Q86jWOZwBhq1ZlK8jYIRXEoQ0jhDYAYSpjA2fBsFQVoKG0UKSLAJB9MEJrMXi6uYMiXl
KCrIZYJARQIKTakEGAkmQ+tU5ZSDRTAlRY7CRJMA7GdkgRoNSJ74t1BRxegjR12jWAoGbfpTAeGY
@@ -2266,17 +2302,19 @@ def mach_o_change(path, what, value):
elif magic == MH_CIGAM:
do_macho(file, 32, LITTLE_ENDIAN)
elif magic == MH_MAGIC_64:
do_macho(file, 64, BIG_ENDIAN)
elif magic == MH_CIGAM_64:
do_macho(file, 64, LITTLE_ENDIAN)
assert(len(what) >= len(value))
- do_file(open(path, 'r+b'))
+
+ with open(path, 'r+b') as f:
+ do_file(f)
if __name__ == '__main__':
main()
# TODO:
# Copy python.exe.manifest
# Monkeypatch distutils.sysconfig
--- a/python/virtualenv/virtualenv_embedded/activate.sh
+++ b/python/virtualenv/virtualenv_embedded/activate.sh
@@ -1,13 +1,13 @@
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
deactivate () {
- unset -f pydoc
+ unset -f pydoc >/dev/null 2>&1
# reset old environment variables
# ! [ -z ${VAR+_} ] returns true if VAR is declared at all
if ! [ -z "${_OLD_VIRTUAL_PATH+_}" ] ; then
PATH="$_OLD_VIRTUAL_PATH"
export PATH
unset _OLD_VIRTUAL_PATH
fi
deleted file mode 100644
index cff6237a14ef4f3639c5b505ae7c79580313e286..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
index 0000000000000000000000000000000000000000..8632eb7af04c6337f0442a878ecb99cd2b1a67e0
GIT binary patch
literal 1197664
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