mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 04:27:37 +00:00
Merge last PGO-green changeset of mozilla-inbound to mozilla-central
This commit is contained in:
commit
50c4cb2d1d
@ -8868,7 +8868,8 @@ let gPrivateBrowsingUI = {
|
||||
* and the setter should only be used in tests.
|
||||
*/
|
||||
get privateWindow() {
|
||||
return window.getInterface(Ci.nsIWebNavigation)
|
||||
return window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.treeOwner
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
@ -8878,7 +8879,8 @@ let gPrivateBrowsingUI = {
|
||||
},
|
||||
|
||||
set privateWindow(val) {
|
||||
return window.getInterface(Ci.nsIWebNavigation)
|
||||
return window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.treeOwner
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
|
@ -65,7 +65,7 @@ let gSyncPane = {
|
||||
this.needsUpdate();
|
||||
} else {
|
||||
this.page = PAGE_HAS_ACCOUNT;
|
||||
document.getElementById("accountName").value = Weave.Service.account;
|
||||
document.getElementById("accountName").value = Weave.Identity.account;
|
||||
document.getElementById("syncComputerName").value = Weave.Clients.localName;
|
||||
document.getElementById("tosPP").hidden = this._usingCustomServer;
|
||||
}
|
||||
|
@ -95,6 +95,7 @@ function PrivateBrowsingService() {
|
||||
this._obs.addObserver(this, "private-browsing", true);
|
||||
this._obs.addObserver(this, "command-line-startup", true);
|
||||
this._obs.addObserver(this, "sessionstore-browser-state-restored", true);
|
||||
this._obs.addObserver(this, "domwindowopened", true);
|
||||
|
||||
// List of nsIXULWindows we are going to be closing during the transition
|
||||
this._windowsToClose = [];
|
||||
@ -152,6 +153,17 @@ PrivateBrowsingService.prototype = {
|
||||
this.privateBrowsingEnabled = false;
|
||||
},
|
||||
|
||||
_setPerWindowPBFlag: function PBS__setPerWindowPBFlag(aWindow, aFlag) {
|
||||
aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.treeOwner
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIXULWindow)
|
||||
.docShell.QueryInterface(Ci.nsILoadContext)
|
||||
.usePrivateBrowsing = aFlag;
|
||||
},
|
||||
|
||||
_onBeforePrivateBrowsingModeChange: function PBS__onBeforePrivateBrowsingModeChange() {
|
||||
// nothing needs to be done here if we're enabling at startup
|
||||
if (!this._autoStarted) {
|
||||
@ -222,23 +234,15 @@ PrivateBrowsingService.prototype = {
|
||||
.docShell.contentViewer.resetCloseWindow();
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._quitting) {
|
||||
var windowsEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
var window = windowsEnum.getNext();
|
||||
window.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.treeOwner
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIXULWindow)
|
||||
.docShell.QueryInterface(Ci.nsILoadContext)
|
||||
.usePrivateBrowsing = this._inPrivateBrowsing;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
this._saveSession = false;
|
||||
|
||||
var windowsEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
var window = windowsEnum.getNext();
|
||||
this._setPerWindowPBFlag(window, this._inPrivateBrowsing);
|
||||
}
|
||||
},
|
||||
|
||||
_onAfterPrivateBrowsingModeChange: function PBS__onAfterPrivateBrowsingModeChange() {
|
||||
@ -523,6 +527,18 @@ PrivateBrowsingService.prototype = {
|
||||
this._notifyIfTransitionComplete();
|
||||
}
|
||||
break;
|
||||
case "domwindowopened":
|
||||
let aWindow = aSubject;
|
||||
let self = this;
|
||||
aWindow.addEventListener("load", function PBS__onWindowLoad(aEvent) {
|
||||
aWindow.removeEventListener("load", arguments.callee);
|
||||
if (aWindow.document
|
||||
.documentElement
|
||||
.getAttribute("windowtype") == "navigator:browser") {
|
||||
self._setPerWindowPBFlag(aWindow, self._inPrivateBrowsing);
|
||||
}
|
||||
}, false);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
||||
|
||||
function test() {
|
||||
// initialization
|
||||
waitForExplicitFinish();
|
||||
gPrefService.setBoolPref("browser.privatebrowsing.keep_current_session", true);
|
||||
let pb = Cc["@mozilla.org/privatebrowsing;1"].
|
||||
getService(Ci.nsIPrivateBrowsingService);
|
||||
@ -54,6 +55,27 @@ function test() {
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
let originalTitle = document.title;
|
||||
|
||||
function testNewWindow(aCallback, expected) {
|
||||
Services.obs.addObserver(function observer1(aSubject, aTopic, aData) {
|
||||
aSubject.addEventListener("load", function() {
|
||||
aSubject.removeEventListener("load", arguments.callee);
|
||||
executeSoon(function() {
|
||||
let ui = aSubject.gPrivateBrowsingUI;
|
||||
is(ui.privateBrowsingEnabled, expected, "The privateBrowsingEnabled property on the new window is set correctly");
|
||||
is(ui.privateWindow, expected, "The privateWindow property on the new window is set correctly");
|
||||
|
||||
Services.obs.addObserver(function observer2(aSubject, aTopic, aData) {
|
||||
aCallback();
|
||||
Services.obs.removeObserver(observer2, "domwindowclosed");
|
||||
}, "domwindowclosed", false);
|
||||
aSubject.close();
|
||||
});
|
||||
Services.obs.removeObserver(observer1, "domwindowopened");
|
||||
}, false);
|
||||
}, "domwindowopened", false);
|
||||
OpenBrowserWindow();
|
||||
}
|
||||
|
||||
// test the gPrivateBrowsingUI object
|
||||
ok(gPrivateBrowsingUI, "The gPrivateBrowsingUI object exists");
|
||||
is(pb.privateBrowsingEnabled, false, "The private browsing mode should not be started initially");
|
||||
@ -61,45 +83,53 @@ function test() {
|
||||
is(gPrivateBrowsingUI.privateWindow, false, "gPrivateBrowsingUI should expose the correct per-window private browsing status");
|
||||
ok(pbMenuItem, "The Private Browsing menu item exists");
|
||||
is(pbMenuItem.getAttribute("label"), pbMenuItem.getAttribute("startlabel"), "The Private Browsing menu item should read \"Start Private Browsing\"");
|
||||
gPrivateBrowsingUI.toggleMode();
|
||||
is(pb.privateBrowsingEnabled, true, "The private browsing mode should be started");
|
||||
is(gPrivateBrowsingUI.privateBrowsingEnabled, true, "gPrivateBrowsingUI should expose the correct private browsing status");
|
||||
is(gPrivateBrowsingUI.privateWindow, true, "gPrivateBrowsingUI should expose the correct per-window private browsing status");
|
||||
// check to see if the Private Browsing mode was activated successfully
|
||||
is(observerData, "enter", "Private Browsing mode was activated using the gPrivateBrowsingUI object");
|
||||
is(pbMenuItem.getAttribute("label"), pbMenuItem.getAttribute("stoplabel"), "The Private Browsing menu item should read \"Stop Private Browsing\"");
|
||||
gPrivateBrowsingUI.toggleMode()
|
||||
is(pb.privateBrowsingEnabled, false, "The private browsing mode should not be started");
|
||||
is(gPrivateBrowsingUI.privateBrowsingEnabled, false, "gPrivateBrowsingUI should expose the correct private browsing status");
|
||||
is(gPrivateBrowsingUI.privateWindow, false, "gPrivateBrowsingUI should expose the correct per-window private browsing status");
|
||||
// check to see if the Private Browsing mode was deactivated successfully
|
||||
is(observerData, "exit", "Private Browsing mode was deactivated using the gPrivateBrowsingUI object");
|
||||
is(pbMenuItem.getAttribute("label"), pbMenuItem.getAttribute("startlabel"), "The Private Browsing menu item should read \"Start Private Browsing\"");
|
||||
testNewWindow(function() {
|
||||
gPrivateBrowsingUI.toggleMode();
|
||||
is(pb.privateBrowsingEnabled, true, "The private browsing mode should be started");
|
||||
is(gPrivateBrowsingUI.privateBrowsingEnabled, true, "gPrivateBrowsingUI should expose the correct private browsing status");
|
||||
is(gPrivateBrowsingUI.privateWindow, true, "gPrivateBrowsingUI should expose the correct per-window private browsing status");
|
||||
// check to see if the Private Browsing mode was activated successfully
|
||||
is(observerData, "enter", "Private Browsing mode was activated using the gPrivateBrowsingUI object");
|
||||
is(pbMenuItem.getAttribute("label"), pbMenuItem.getAttribute("stoplabel"), "The Private Browsing menu item should read \"Stop Private Browsing\"");
|
||||
testNewWindow(function() {
|
||||
gPrivateBrowsingUI.toggleMode()
|
||||
is(pb.privateBrowsingEnabled, false, "The private browsing mode should not be started");
|
||||
is(gPrivateBrowsingUI.privateBrowsingEnabled, false, "gPrivateBrowsingUI should expose the correct private browsing status");
|
||||
is(gPrivateBrowsingUI.privateWindow, false, "gPrivateBrowsingUI should expose the correct per-window private browsing status");
|
||||
// check to see if the Private Browsing mode was deactivated successfully
|
||||
is(observerData, "exit", "Private Browsing mode was deactivated using the gPrivateBrowsingUI object");
|
||||
is(pbMenuItem.getAttribute("label"), pbMenuItem.getAttribute("startlabel"), "The Private Browsing menu item should read \"Start Private Browsing\"");
|
||||
|
||||
// These are tests for the privateWindow setter. Note that the setter should
|
||||
// not be used anywhere else for now!
|
||||
gPrivateBrowsingUI.privateWindow = true;
|
||||
is(gPrivateBrowsingUI.privateWindow, true, "gPrivateBrowsingUI should accept the correct per-window private browsing status");
|
||||
gPrivateBrowsingUI.privateWindow = false;
|
||||
is(gPrivateBrowsingUI.privateWindow, false, "gPrivateBrowsingUI should accept the correct per-window private browsing status");
|
||||
testNewWindow(function() {
|
||||
// These are tests for the privateWindow setter. Note that the setter should
|
||||
// not be used anywhere else for now!
|
||||
gPrivateBrowsingUI.privateWindow = true;
|
||||
is(gPrivateBrowsingUI.privateWindow, true, "gPrivateBrowsingUI should accept the correct per-window private browsing status");
|
||||
gPrivateBrowsingUI.privateWindow = false;
|
||||
is(gPrivateBrowsingUI.privateWindow, false, "gPrivateBrowsingUI should accept the correct per-window private browsing status");
|
||||
|
||||
// now, test using the <command> object
|
||||
let cmd = document.getElementById("Tools:PrivateBrowsing");
|
||||
isnot(cmd, null, "XUL command object for the private browsing service exists");
|
||||
var func = new Function("", cmd.getAttribute("oncommand"));
|
||||
func.call(cmd);
|
||||
// check to see if the Private Browsing mode was activated successfully
|
||||
is(observerData, "enter", "Private Browsing mode was activated using the command object");
|
||||
// check to see that the window title has been changed correctly
|
||||
isnot(document.title, originalTitle, "Private browsing mode has correctly changed the title");
|
||||
func.call(cmd);
|
||||
// check to see if the Private Browsing mode was deactivated successfully
|
||||
is(observerData, "exit", "Private Browsing mode was deactivated using the command object");
|
||||
// check to see that the window title has been restored correctly
|
||||
is(document.title, originalTitle, "Private browsing mode has correctly restored the title");
|
||||
// now, test using the <command> object
|
||||
let cmd = document.getElementById("Tools:PrivateBrowsing");
|
||||
isnot(cmd, null, "XUL command object for the private browsing service exists");
|
||||
var func = new Function("", cmd.getAttribute("oncommand"));
|
||||
func.call(cmd);
|
||||
// check to see if the Private Browsing mode was activated successfully
|
||||
is(observerData, "enter", "Private Browsing mode was activated using the command object");
|
||||
// check to see that the window title has been changed correctly
|
||||
isnot(document.title, originalTitle, "Private browsing mode has correctly changed the title");
|
||||
func.call(cmd);
|
||||
// check to see if the Private Browsing mode was deactivated successfully
|
||||
is(observerData, "exit", "Private Browsing mode was deactivated using the command object");
|
||||
// check to see that the window title has been restored correctly
|
||||
is(document.title, originalTitle, "Private browsing mode has correctly restored the title");
|
||||
|
||||
// cleanup
|
||||
gBrowser.removeCurrentTab();
|
||||
Services.obs.removeObserver(observer, "private-browsing");
|
||||
gPrefService.clearUserPref("browser.privatebrowsing.keep_current_session");
|
||||
// cleanup
|
||||
gBrowser.removeCurrentTab();
|
||||
Services.obs.removeObserver(observer, "private-browsing");
|
||||
gPrefService.clearUserPref("browser.privatebrowsing.keep_current_session");
|
||||
|
||||
finish();
|
||||
}, false);
|
||||
}, true);
|
||||
}, false);
|
||||
}
|
||||
|
@ -1716,6 +1716,7 @@ GK_ATOM(onMozTouchUp, "onMozTouchUp")
|
||||
GK_ATOM(ondevicemotion, "ondevicemotion")
|
||||
GK_ATOM(ondeviceorientation, "ondeviceorientation")
|
||||
GK_ATOM(ondeviceproximity, "ondeviceproximity")
|
||||
GK_ATOM(onuserproximity, "onuserproximity")
|
||||
|
||||
// light sensor support
|
||||
GK_ATOM(ondevicelight, "ondevicelight")
|
||||
|
@ -390,10 +390,14 @@ nsWebSocket::OnServerClose(nsISupports *aContext, PRUint16 aCode,
|
||||
CopyUTF8toUTF16(aReason, mCloseEventReason);
|
||||
|
||||
if (mReadyState == nsIWebSocket::OPEN) {
|
||||
// Send reciprocal Close frame.
|
||||
// 5.5.1: "When sending a Close frame in response, the endpoint typically
|
||||
// echos the status code it received"
|
||||
CloseConnection(aCode, aReason);
|
||||
// RFC 6455, 5.5.1: "When sending a Close frame in response, the endpoint
|
||||
// typically echos the status code it received".
|
||||
// But never send certain codes, per section 7.4.1
|
||||
if (aCode == 1005 || aCode == 1006 || aCode == 1015) {
|
||||
CloseConnection(0, EmptyCString());
|
||||
} else {
|
||||
CloseConnection(aCode, aReason);
|
||||
}
|
||||
} else {
|
||||
// Nothing else to do: OnStop does the rest of the work.
|
||||
NS_ASSERTION (mReadyState == nsIWebSocket::CLOSING, "unknown state");
|
||||
|
@ -459,6 +459,10 @@ WINDOW_ONLY_EVENT(deviceproximity,
|
||||
NS_DEVICE_PROXIMITY,
|
||||
EventNameType_None,
|
||||
NS_EVENT)
|
||||
WINDOW_ONLY_EVENT(userproximity,
|
||||
NS_USER_PROXIMITY,
|
||||
EventNameType_None,
|
||||
NS_EVENT)
|
||||
WINDOW_ONLY_EVENT(devicelight,
|
||||
NS_DEVICE_LIGHT,
|
||||
EventNameType_None,
|
||||
|
@ -94,6 +94,8 @@ NS_NewDOMPopupBlockedEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, n
|
||||
nsresult
|
||||
NS_NewDOMDeviceProximityEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsEvent *aEvent);
|
||||
nsresult
|
||||
NS_NewDOMUserProximityEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsEvent *aEvent);
|
||||
nsresult
|
||||
NS_NewDOMDeviceOrientationEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent);
|
||||
nsresult
|
||||
NS_NewDOMDeviceLightEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent);
|
||||
|
@ -66,6 +66,7 @@ CPPSRCS = \
|
||||
nsDOMMutationEvent.cpp \
|
||||
nsDOMPopupBlockedEvent.cpp \
|
||||
nsDOMDeviceProximityEvent.cpp \
|
||||
nsDOMUserProximityEvent.cpp \
|
||||
nsDOMDeviceLightEvent.cpp \
|
||||
nsDOMDeviceOrientationEvent.cpp \
|
||||
nsDOMDeviceMotionEvent.cpp \
|
||||
|
@ -128,6 +128,7 @@ static const char* const sEventNames[] = {
|
||||
"devicemotion",
|
||||
"deviceorientation",
|
||||
"deviceproximity",
|
||||
"userproximity",
|
||||
"devicelight"
|
||||
};
|
||||
|
||||
@ -1557,6 +1558,8 @@ const char* nsDOMEvent::GetEventName(PRUint32 aEventType)
|
||||
return sEventNames[eDOMEvents_deviceorientation];
|
||||
case NS_DEVICE_PROXIMITY:
|
||||
return sEventNames[eDOMEvents_deviceproximity];
|
||||
case NS_USER_PROXIMITY:
|
||||
return sEventNames[eDOMEvents_userproximity];
|
||||
case NS_DEVICE_LIGHT:
|
||||
return sEventNames[eDOMEvents_devicelight];
|
||||
case NS_FULLSCREENCHANGE:
|
||||
|
@ -211,6 +211,7 @@ public:
|
||||
eDOMEvents_devicemotion,
|
||||
eDOMEvents_deviceorientation,
|
||||
eDOMEvents_deviceproximity,
|
||||
eDOMEvents_userproximity,
|
||||
eDOMEvents_devicelight
|
||||
};
|
||||
|
||||
|
57
content/events/src/nsDOMUserProximityEvent.cpp
Normal file
57
content/events/src/nsDOMUserProximityEvent.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
/* 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/. */
|
||||
|
||||
#include "nsDOMUserProximityEvent.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "DictionaryHelpers.h"
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsDOMUserProximityEvent, nsDOMEvent)
|
||||
NS_IMPL_RELEASE_INHERITED(nsDOMUserProximityEvent, nsDOMEvent)
|
||||
|
||||
DOMCI_DATA(UserProximityEvent, nsDOMUserProximityEvent)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsDOMUserProximityEvent)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMUserProximityEvent)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(UserProximityEvent)
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMUserProximityEvent::InitUserProximityEvent(const nsAString & aEventTypeArg,
|
||||
bool aCanBubbleArg,
|
||||
bool aCancelableArg,
|
||||
bool aNear)
|
||||
{
|
||||
nsresult rv = nsDOMEvent::InitEvent(aEventTypeArg, aCanBubbleArg, aCancelableArg);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mNear = aNear;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMUserProximityEvent::GetNear(bool *aNear)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aNear);
|
||||
*aNear = mNear;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMUserProximityEvent::InitFromCtor(const nsAString& aType,
|
||||
JSContext* aCx, jsval* aVal)
|
||||
{
|
||||
mozilla::dom::UserProximityEventInit d;
|
||||
nsresult rv = d.Init(aCx, aVal);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return InitUserProximityEvent(aType, d.bubbles, d.cancelable, d.near);
|
||||
}
|
||||
|
||||
nsresult
|
||||
NS_NewDOMUserProximityEvent(nsIDOMEvent** aInstancePtrResult,
|
||||
nsPresContext* aPresContext,
|
||||
nsEvent *aEvent)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aInstancePtrResult);
|
||||
nsDOMUserProximityEvent* it = new nsDOMUserProximityEvent(aPresContext, aEvent);
|
||||
return CallQueryInterface(it, aInstancePtrResult);
|
||||
}
|
36
content/events/src/nsDOMUserProximityEvent.h
Normal file
36
content/events/src/nsDOMUserProximityEvent.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* 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 nsDOMUserProximityEvent_h__
|
||||
#define nsDOMUserProximityEvent_h__
|
||||
|
||||
#include "nsIDOMUserProximityEvent.h"
|
||||
#include "nsDOMEvent.h"
|
||||
|
||||
class nsDOMUserProximityEvent
|
||||
: public nsDOMEvent
|
||||
, public nsIDOMUserProximityEvent
|
||||
{
|
||||
public:
|
||||
|
||||
nsDOMUserProximityEvent(nsPresContext* aPresContext, nsEvent* aEvent)
|
||||
: nsDOMEvent(aPresContext, aEvent),
|
||||
mNear(false) {}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// Forward to nsDOMEvent
|
||||
NS_FORWARD_TO_NSDOMEVENT
|
||||
|
||||
// nsIDOMUserProximityEvent Interface
|
||||
NS_DECL_NSIDOMUSERPROXIMITYEVENT
|
||||
|
||||
virtual nsresult InitFromCtor(const nsAString& aType,
|
||||
JSContext* aCx,
|
||||
jsval* aVal);
|
||||
protected:
|
||||
bool mNear;
|
||||
};
|
||||
|
||||
#endif
|
@ -290,7 +290,7 @@ nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
|
||||
}
|
||||
} else if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
|
||||
EnableDevice(NS_DEVICE_ORIENTATION);
|
||||
} else if (aTypeAtom == nsGkAtoms::ondeviceproximity) {
|
||||
} else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) {
|
||||
EnableDevice(NS_DEVICE_PROXIMITY);
|
||||
} else if (aTypeAtom == nsGkAtoms::ondevicelight) {
|
||||
EnableDevice(NS_DEVICE_LIGHT);
|
||||
@ -351,6 +351,7 @@ nsEventListenerManager::EnableDevice(PRUint32 aType)
|
||||
window->EnableDeviceSensor(SENSOR_ORIENTATION);
|
||||
break;
|
||||
case NS_DEVICE_PROXIMITY:
|
||||
case NS_USER_PROXIMITY:
|
||||
window->EnableDeviceSensor(SENSOR_PROXIMITY);
|
||||
break;
|
||||
case NS_DEVICE_LIGHT:
|
||||
@ -387,6 +388,7 @@ nsEventListenerManager::DisableDevice(PRUint32 aType)
|
||||
window->DisableDeviceSensor(SENSOR_GYROSCOPE);
|
||||
break;
|
||||
case NS_DEVICE_PROXIMITY:
|
||||
case NS_USER_PROXIMITY:
|
||||
window->DisableDeviceSensor(SENSOR_PROXIMITY);
|
||||
break;
|
||||
case NS_DEVICE_LIGHT:
|
||||
|
@ -2222,6 +2222,8 @@ nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext,
|
||||
|
||||
nsCOMPtr<nsISupports> container = aPresContext->GetContainer();
|
||||
nsCOMPtr<nsIDOMWindow> window = do_GetInterface(container);
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
// GetDragData determines if a selection, link or image in the content
|
||||
// should be dragged, and places the data associated with the drag in the
|
||||
|
@ -379,6 +379,14 @@ is(e.max, 2, "max should be 2");
|
||||
document.dispatchEvent(e);
|
||||
is(receivedEvent, e, "Wrong event!");
|
||||
|
||||
// UserProximityEvent
|
||||
e = new UserProximityEvent("hello", {near: true});
|
||||
ok(e.type, "hello", "Wrong event type!");
|
||||
ok(!e.isTrusted, "Event should not be trusted");
|
||||
is(e.near, true, "near should be true");
|
||||
document.dispatchEvent(e);
|
||||
is(receivedEvent, e, "Wrong event!");
|
||||
|
||||
// DeviceLightEvent
|
||||
e = new DeviceLightEvent("hello", {value: 1} );
|
||||
ok(e.type, "hello", "Wrong event type!");
|
||||
|
@ -50,7 +50,6 @@ _TEST_FILES = \
|
||||
$(NULL)
|
||||
|
||||
_CHROME_FILES = \
|
||||
test_bug330705-2.xul \
|
||||
test_bug233643.xul \
|
||||
test_bug398289.html \
|
||||
398289-resource.xul \
|
||||
|
@ -1,50 +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"?>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
title="Test for Bug 330705">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=330705
|
||||
-->
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=330705">Mozilla Bug 330705</a>
|
||||
<p id="display">
|
||||
<box tabindex="1" style="-moz-user-focus:normal;" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
|
||||
<box tabindex="1" style="-moz-user-focus:normal;" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
<script class="testbody" type="text/javascript">
|
||||
<![CDATA[
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var isFocused = false;
|
||||
|
||||
function doTest() {
|
||||
document.getElementsByTagName('box')[1].blur();
|
||||
setTimeout(function () {
|
||||
ok(isFocused,
|
||||
"The first box element is still focused after blur() has been called on the second box element");
|
||||
SimpleTest.finish();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
var box = document.getElementsByTagName('box')[0];
|
||||
box.addEventListener('focus', function() {
|
||||
isFocused = true;
|
||||
setTimeout(doTest, 0);
|
||||
box.removeEventListener('focus', arguments.callee, true);
|
||||
}, true);
|
||||
box.addEventListener('blur', function() { isFocused = false;}, true);
|
||||
box.focus();
|
||||
}
|
||||
|
||||
addLoadEvent(onLoad);
|
||||
]]>
|
||||
</script>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</div>
|
||||
</body>
|
||||
</window>
|
@ -326,6 +326,7 @@
|
||||
#include "nsIDOMCSSStyleSheet.h"
|
||||
#include "nsDOMCSSValueList.h"
|
||||
#include "nsIDOMDeviceProximityEvent.h"
|
||||
#include "nsIDOMUserProximityEvent.h"
|
||||
#include "nsIDOMDeviceLightEvent.h"
|
||||
#include "nsIDOMDeviceOrientationEvent.h"
|
||||
#include "nsIDOMDeviceMotionEvent.h"
|
||||
@ -821,6 +822,9 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
||||
// Device Proximity
|
||||
NS_DEFINE_CLASSINFO_DATA(DeviceProximityEvent, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
// User Proximity
|
||||
NS_DEFINE_CLASSINFO_DATA(UserProximityEvent, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
// Device Orientation
|
||||
NS_DEFINE_CLASSINFO_DATA(DeviceOrientationEvent, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
@ -1697,6 +1701,7 @@ NS_DEFINE_EVENT_CTOR(UIEvent)
|
||||
NS_DEFINE_EVENT_CTOR(MouseEvent)
|
||||
NS_DEFINE_EVENT_CTOR(DeviceLightEvent)
|
||||
NS_DEFINE_EVENT_CTOR(DeviceProximityEvent)
|
||||
NS_DEFINE_EVENT_CTOR(UserProximityEvent)
|
||||
|
||||
nsresult
|
||||
NS_DOMStorageEventCtor(nsISupports** aInstancePtrResult)
|
||||
@ -1732,6 +1737,7 @@ static const nsConstructorFuncMapData kConstructorFuncMap[] =
|
||||
NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(UIEvent)
|
||||
NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MouseEvent)
|
||||
NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(DeviceProximityEvent)
|
||||
NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(UserProximityEvent)
|
||||
NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(DeviceLightEvent)
|
||||
NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(StorageEvent)
|
||||
NS_DEFINE_CONSTRUCTOR_FUNC_DATA(MozSmsFilter, sms::SmsFilter::NewSmsFilter)
|
||||
@ -2621,6 +2627,11 @@ nsDOMClassInfo::Init()
|
||||
DOM_CLASSINFO_EVENT_MAP_ENTRIES
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(UserProximityEvent, nsIDOMUserProximityEvent)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMUserProximityEvent)
|
||||
DOM_CLASSINFO_EVENT_MAP_ENTRIES
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(DeviceOrientationEvent, nsIDOMDeviceOrientationEvent)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceOrientationEvent)
|
||||
DOM_CLASSINFO_EVENT_MAP_ENTRIES
|
||||
|
@ -81,6 +81,7 @@ DOMCI_CLASS(CompositionEvent)
|
||||
DOMCI_CLASS(PopupBlockedEvent)
|
||||
DOMCI_CLASS(DeviceLightEvent)
|
||||
DOMCI_CLASS(DeviceProximityEvent)
|
||||
DOMCI_CLASS(UserProximityEvent)
|
||||
DOMCI_CLASS(DeviceOrientationEvent)
|
||||
DOMCI_CLASS(DeviceMotionEvent)
|
||||
DOMCI_CLASS(DeviceAcceleration)
|
||||
|
@ -69,7 +69,7 @@ interface nsIDOMMozURLProperty : nsISupports
|
||||
* @see <http://www.whatwg.org/html/#window>
|
||||
*/
|
||||
|
||||
[scriptable, uuid(e6198a86-1a46-46ec-9501-dfcd84a5f630)]
|
||||
[scriptable, uuid(a89569e6-4fef-49a5-a19b-612ac481fa2e)]
|
||||
interface nsIDOMWindow : nsISupports
|
||||
{
|
||||
// the current browsing context
|
||||
@ -465,6 +465,7 @@ interface nsIDOMWindow : nsISupports
|
||||
[implicit_jscontext] attribute jsval ondevicemotion;
|
||||
[implicit_jscontext] attribute jsval ondeviceorientation;
|
||||
[implicit_jscontext] attribute jsval ondeviceproximity;
|
||||
[implicit_jscontext] attribute jsval onuserproximity;
|
||||
[implicit_jscontext] attribute jsval ondevicelight;
|
||||
|
||||
[implicit_jscontext] attribute jsval onmouseenter;
|
||||
|
@ -77,6 +77,7 @@ XPIDLSRCS = \
|
||||
nsIDOMMozTouchEvent.idl \
|
||||
nsIDOMDeviceLightEvent.idl \
|
||||
nsIDOMDeviceProximityEvent.idl \
|
||||
nsIDOMUserProximityEvent.idl \
|
||||
nsIDOMDeviceOrientationEvent.idl \
|
||||
nsIDOMDeviceMotionEvent.idl \
|
||||
nsIDOMScrollAreaEvent.idl \
|
||||
|
21
dom/interfaces/events/nsIDOMUserProximityEvent.idl
Normal file
21
dom/interfaces/events/nsIDOMUserProximityEvent.idl
Normal file
@ -0,0 +1,21 @@
|
||||
/* 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/. */
|
||||
|
||||
#include "nsIDOMEvent.idl"
|
||||
|
||||
[scriptable, uuid(e67432b8-ead4-4247-bf6c-f2e426472478)]
|
||||
interface nsIDOMUserProximityEvent : nsIDOMEvent
|
||||
{
|
||||
[noscript] void initUserProximityEvent(in DOMString eventTypeArg,
|
||||
in boolean canBubbleArg,
|
||||
in boolean cancelableArg,
|
||||
in boolean near);
|
||||
|
||||
readonly attribute boolean near;
|
||||
};
|
||||
|
||||
dictionary UserProximityEventInit : EventInit
|
||||
{
|
||||
boolean near;
|
||||
};
|
@ -54,6 +54,8 @@
|
||||
using namespace mozilla;
|
||||
using namespace hal;
|
||||
|
||||
#undef near
|
||||
|
||||
// also see sDefaultSensorHint in mobile/android/base/GeckoAppShell.java
|
||||
#define DEFAULT_SENSOR_POLL 100
|
||||
|
||||
@ -124,6 +126,7 @@ NS_IMPL_ISUPPORTS1(nsDeviceSensors, nsIDeviceSensors)
|
||||
|
||||
nsDeviceSensors::nsDeviceSensors()
|
||||
{
|
||||
mIsUserProximityNear = false;
|
||||
mLastDOMMotionEventTime = TimeStamp::Now();
|
||||
mEnabled = Preferences::GetBool("device.sensors.enabled", true);
|
||||
|
||||
@ -274,14 +277,46 @@ nsDeviceSensors::FireDOMProximityEvent(nsIDOMEventTarget *aTarget,
|
||||
}
|
||||
bool defaultActionEnabled;
|
||||
aTarget->DispatchEvent(event, &defaultActionEnabled);
|
||||
|
||||
// Some proximity sensors only support a binary near or
|
||||
// far measurement. In this case, the sensor should report
|
||||
// its maximum range value in the far state and a lesser
|
||||
// value in the near state.
|
||||
|
||||
bool near = (aValue < aMax);
|
||||
if (mIsUserProximityNear != near) {
|
||||
mIsUserProximityNear = near;
|
||||
FireDOMUserProximityEvent(aTarget, mIsUserProximityNear);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDeviceSensors::FireDOMUserProximityEvent(nsIDOMEventTarget *aTarget, bool aNear)
|
||||
{
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
NS_NewDOMUserProximityEvent(getter_AddRefs(event), nsnull, nsnull);
|
||||
nsCOMPtr<nsIDOMUserProximityEvent> pe = do_QueryInterface(event);
|
||||
|
||||
pe->InitUserProximityEvent(NS_LITERAL_STRING("userproximity"),
|
||||
true,
|
||||
false,
|
||||
aNear);
|
||||
|
||||
nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
|
||||
privateEvent = do_QueryInterface(event);
|
||||
if (privateEvent) {
|
||||
privateEvent->SetTrusted(true);
|
||||
}
|
||||
bool defaultActionEnabled;
|
||||
aTarget->DispatchEvent(event, &defaultActionEnabled);
|
||||
}
|
||||
|
||||
void
|
||||
nsDeviceSensors::FireDOMOrientationEvent(nsIDOMDocument *domdoc,
|
||||
nsIDOMEventTarget *target,
|
||||
double alpha,
|
||||
double beta,
|
||||
double gamma)
|
||||
nsIDOMEventTarget *target,
|
||||
double alpha,
|
||||
double beta,
|
||||
double gamma)
|
||||
{
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
bool defaultActionEnabled = true;
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "nsIDOMDeviceLightEvent.h"
|
||||
#include "nsIDOMDeviceOrientationEvent.h"
|
||||
#include "nsIDOMDeviceProximityEvent.h"
|
||||
#include "nsIDOMUserProximityEvent.h"
|
||||
#include "nsIDOMDeviceMotionEvent.h"
|
||||
#include "nsDOMDeviceMotionEvent.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
@ -84,6 +85,9 @@ private:
|
||||
double aMin,
|
||||
double aMax);
|
||||
|
||||
void FireDOMUserProximityEvent(nsIDOMEventTarget *aTarget,
|
||||
bool aNear);
|
||||
|
||||
void FireDOMOrientationEvent(class nsIDOMDocument *domDoc,
|
||||
class nsIDOMEventTarget *target,
|
||||
double alpha,
|
||||
@ -104,6 +108,7 @@ private:
|
||||
}
|
||||
|
||||
mozilla::TimeStamp mLastDOMMotionEventTime;
|
||||
bool mIsUserProximityNear;
|
||||
nsRefPtr<nsDOMDeviceAcceleration> mLastAcceleration;
|
||||
nsRefPtr<nsDOMDeviceAcceleration> mLastAccelerationIncluduingGravity;
|
||||
nsRefPtr<nsDOMDeviceRotationRate> mLastRotationRate;
|
||||
|
@ -491,7 +491,9 @@ function startTest()
|
||||
ok(gEvents === "", "focusing element that is already focused");
|
||||
|
||||
$("t2").blur();
|
||||
$("t7").blur();
|
||||
ok(gEvents === "", "blurring element that is not focused");
|
||||
is(document.activeElement, $("t1"), "old element still focused after blur() on another element");
|
||||
|
||||
// focus() method on elements that are not tabbable
|
||||
for (idx = 1; idx <= kFocusSteps; idx++) {
|
||||
|
13897
gfx/2d/ShadersD2D.h
13897
gfx/2d/ShadersD2D.h
File diff suppressed because it is too large
Load Diff
@ -65,6 +65,8 @@ struct PRLogModuleInfo;
|
||||
# define MOZ_LAYERS_LOG(_args)
|
||||
#endif // if defined(DEBUG) || defined(PR_LOGGING)
|
||||
|
||||
#define MOZ_ENABLE_MASK_LAYERS
|
||||
|
||||
class gfxContext;
|
||||
class nsPaintEvent;
|
||||
|
||||
@ -719,6 +721,7 @@ public:
|
||||
*/
|
||||
void SetMaskLayer(Layer* aMaskLayer)
|
||||
{
|
||||
#ifdef MOZ_ENABLE_MASK_LAYERS
|
||||
#ifdef DEBUG
|
||||
if (aMaskLayer) {
|
||||
gfxMatrix maskTransform;
|
||||
@ -730,6 +733,7 @@ public:
|
||||
|
||||
mMaskLayer = aMaskLayer;
|
||||
Mutated();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,6 +64,46 @@ struct TypeInferenceSizes
|
||||
size_t temporary;
|
||||
};
|
||||
|
||||
// These measurements relate directly to the JSRuntime, and not to
|
||||
// compartments within it.
|
||||
struct RuntimeSizes
|
||||
{
|
||||
RuntimeSizes()
|
||||
: object(0)
|
||||
, atomsTable(0)
|
||||
, contexts(0)
|
||||
, dtoa(0)
|
||||
, temporary(0)
|
||||
, mjitCode(0)
|
||||
, regexpCode(0)
|
||||
, unusedCodeMemory(0)
|
||||
, stackCommitted(0)
|
||||
, gcMarker(0)
|
||||
, mathCache(0)
|
||||
, scriptFilenames(0)
|
||||
, compartmentObjects(0)
|
||||
{}
|
||||
|
||||
size_t object;
|
||||
size_t atomsTable;
|
||||
size_t contexts;
|
||||
size_t dtoa;
|
||||
size_t temporary;
|
||||
size_t mjitCode;
|
||||
size_t regexpCode;
|
||||
size_t unusedCodeMemory;
|
||||
size_t stackCommitted;
|
||||
size_t gcMarker;
|
||||
size_t mathCache;
|
||||
size_t scriptFilenames;
|
||||
|
||||
// This is the exception to the "RuntimeSizes doesn't measure things within
|
||||
// compartments" rule. We combine the sizes of all the JSCompartment
|
||||
// objects into a single measurement because each one is fairly small, and
|
||||
// they're all the same size.
|
||||
size_t compartmentObjects;
|
||||
};
|
||||
|
||||
struct CompartmentStats
|
||||
{
|
||||
CompartmentStats() {
|
||||
@ -95,6 +135,7 @@ struct CompartmentStats
|
||||
size_t shapesCompartmentTables;
|
||||
size_t scriptData;
|
||||
size_t mjitData;
|
||||
size_t crossCompartmentWrappers;
|
||||
|
||||
TypeInferenceSizes typeInferenceSizes;
|
||||
};
|
||||
@ -102,16 +143,7 @@ struct CompartmentStats
|
||||
struct RuntimeStats
|
||||
{
|
||||
RuntimeStats(JSMallocSizeOfFun mallocSizeOf)
|
||||
: runtimeObject(0)
|
||||
, runtimeAtomsTable(0)
|
||||
, runtimeContexts(0)
|
||||
, runtimeNormal(0)
|
||||
, runtimeTemporary(0)
|
||||
, runtimeMjitCode(0)
|
||||
, runtimeRegexpCode(0)
|
||||
, runtimeUnusedCodeMemory(0)
|
||||
, runtimeStackCommitted(0)
|
||||
, runtimeGCMarker(0)
|
||||
: runtime()
|
||||
, gcHeapChunkTotal(0)
|
||||
, gcHeapCommitted(0)
|
||||
, gcHeapUnused(0)
|
||||
@ -133,16 +165,8 @@ struct RuntimeStats
|
||||
, mallocSizeOf(mallocSizeOf)
|
||||
{}
|
||||
|
||||
size_t runtimeObject;
|
||||
size_t runtimeAtomsTable;
|
||||
size_t runtimeContexts;
|
||||
size_t runtimeNormal;
|
||||
size_t runtimeTemporary;
|
||||
size_t runtimeMjitCode;
|
||||
size_t runtimeRegexpCode;
|
||||
size_t runtimeUnusedCodeMemory;
|
||||
size_t runtimeStackCommitted;
|
||||
size_t runtimeGCMarker;
|
||||
js::RuntimeSizes runtime;
|
||||
|
||||
size_t gcHeapChunkTotal;
|
||||
size_t gcHeapCommitted;
|
||||
size_t gcHeapUnused;
|
||||
|
@ -69,6 +69,8 @@ StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
|
||||
// Get the compartment-level numbers.
|
||||
compartment->sizeOfTypeInferenceData(&cStats.typeInferenceSizes, rtStats->mallocSizeOf);
|
||||
cStats.shapesCompartmentTables = compartment->sizeOfShapeTable(rtStats->mallocSizeOf);
|
||||
cStats.crossCompartmentWrappers =
|
||||
compartment->crossCompartmentWrappers.sizeOfExcludingThis(rtStats->mallocSizeOf);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -195,23 +197,8 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats)
|
||||
StatsArenaCallback, StatsCellCallback);
|
||||
IterateChunks(rt, rtStats, StatsChunkCallback);
|
||||
|
||||
rtStats->runtimeObject = rtStats->mallocSizeOf(rt);
|
||||
rt->sizeOfIncludingThis(rtStats->mallocSizeOf, &rtStats->runtime);
|
||||
|
||||
rt->sizeOfExcludingThis(rtStats->mallocSizeOf,
|
||||
&rtStats->runtimeNormal,
|
||||
&rtStats->runtimeTemporary,
|
||||
&rtStats->runtimeMjitCode,
|
||||
&rtStats->runtimeRegexpCode,
|
||||
&rtStats->runtimeUnusedCodeMemory,
|
||||
&rtStats->runtimeStackCommitted,
|
||||
&rtStats->runtimeGCMarker);
|
||||
|
||||
rtStats->runtimeAtomsTable =
|
||||
rt->atomState.atoms.sizeOfExcludingThis(rtStats->mallocSizeOf);
|
||||
|
||||
for (ContextIter acx(rt); !acx.done(); acx.next())
|
||||
rtStats->runtimeContexts += acx->sizeOfIncludingThis(rtStats->mallocSizeOf);
|
||||
|
||||
// This is initialized to all bytes stored in used chunks, and then we
|
||||
// subtract used space from it each time around the loop.
|
||||
rtStats->gcHeapChunkDirtyUnused = rtStats->gcHeapChunkTotal -
|
||||
@ -219,7 +206,7 @@ CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats)
|
||||
rtStats->gcHeapChunkCleanDecommitted -
|
||||
rtStats->gcHeapChunkDirtyDecommitted;
|
||||
|
||||
rtStats->totalMjit = rtStats->runtimeMjitCode;
|
||||
rtStats->totalMjit = rtStats->runtime.mjitCode;
|
||||
|
||||
for (size_t index = 0;
|
||||
index < rtStats->compartmentStatsVector.length();
|
||||
@ -293,19 +280,7 @@ GetExplicitNonHeapForRuntime(JSRuntime *rt, JSMallocSizeOfFun mallocSizeOf)
|
||||
// explicit/runtime/regexp-code
|
||||
// explicit/runtime/stack-committed
|
||||
// explicit/runtime/unused-code-memory
|
||||
size_t dummy, mjitCode, regexpCode, unusedCodeMemory, stackCommitted;
|
||||
rt->sizeOfExcludingThis(mallocSizeOf,
|
||||
&dummy,
|
||||
&dummy,
|
||||
&mjitCode,
|
||||
®expCode,
|
||||
&unusedCodeMemory,
|
||||
&stackCommitted,
|
||||
NULL);
|
||||
n += mjitCode;
|
||||
n += regexpCode;
|
||||
n += unusedCodeMemory;
|
||||
n += stackCommitted;
|
||||
n += rt->sizeOfExplicitNonHeap();
|
||||
|
||||
return int64_t(n);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "jsfriendapi.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsprf.h"
|
||||
#include "jswrapper.h"
|
||||
|
||||
@ -142,6 +143,22 @@ GCParameter(JSContext *cx, unsigned argc, jsval *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
IsProxy(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (argc != 1) {
|
||||
JS_ReportError(cx, "the function takes exactly one argument");
|
||||
return false;
|
||||
}
|
||||
if (!args[0].isObject()) {
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
args.rval().setBoolean(args[0].toObject().isProxy());
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
InternalConst(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
@ -583,6 +600,10 @@ static JSFunctionSpecWithHelp TestingFunctions[] = {
|
||||
" Query an internal constant for the engine. See InternalConst source for\n"
|
||||
" the list of constant names."),
|
||||
|
||||
JS_FN_HELP("isProxy", IsProxy, 1, 0,
|
||||
"isProxy(obj)",
|
||||
" If true, obj is a proxy of some sort"),
|
||||
|
||||
JS_FN_HELP("mjitChunkLimit", MJitChunkLimit, 1, 0,
|
||||
"mjitChunkLimit(N)",
|
||||
" Specify limit on compiled chunk size during mjit compilation."),
|
||||
|
@ -157,8 +157,9 @@ frontend::CompileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF
|
||||
/* If this is a direct call to eval, inherit the caller's strictness. */
|
||||
if (callerFrame &&
|
||||
callerFrame->isScriptFrame() &&
|
||||
callerFrame->script()->strictModeCode) {
|
||||
bce.sc->flags |= TCF_STRICT_MODE_CODE;
|
||||
callerFrame->script()->strictModeCode)
|
||||
{
|
||||
bce.sc->setInStrictMode();
|
||||
tokenStream.setStrictMode();
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,6 @@ BytecodeEmitter::BytecodeEmitter(Parser *parser, SharedContext *sc, unsigned lin
|
||||
bool
|
||||
BytecodeEmitter::init()
|
||||
{
|
||||
roLexdeps.init();
|
||||
return constMap.init() && atomIndices.ensureMap(sc->context);
|
||||
}
|
||||
|
||||
@ -1000,7 +999,7 @@ BytecodeEmitter::noteClosedVar(ParseNode *pn)
|
||||
for (size_t i = 0; i < closedVars.length(); ++i)
|
||||
JS_ASSERT(closedVars[i] != pn->pn_cookie.slot());
|
||||
#endif
|
||||
sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
sc->setFunIsHeavyweight();
|
||||
return closedVars.append(pn->pn_cookie.slot());
|
||||
}
|
||||
|
||||
@ -1015,7 +1014,7 @@ BytecodeEmitter::noteClosedArg(ParseNode *pn)
|
||||
for (size_t i = 0; i < closedArgs.length(); ++i)
|
||||
JS_ASSERT(closedArgs[i] != pn->pn_cookie.slot());
|
||||
#endif
|
||||
sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
sc->setFunIsHeavyweight();
|
||||
return closedArgs.append(pn->pn_cookie.slot());
|
||||
}
|
||||
|
||||
@ -1090,7 +1089,7 @@ EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
|
||||
* clones must get unique shapes; see the comments for
|
||||
* js::Bindings::extensibleParents.
|
||||
*/
|
||||
if ((bce->sc->flags & TCF_FUN_EXTENSIBLE_SCOPE) ||
|
||||
if (bce->sc->funHasExtensibleScope() ||
|
||||
bce->sc->bindings.extensibleParents()) {
|
||||
Shape *newShape = Shape::setExtensibleParents(cx, blockObj->lastProperty());
|
||||
if (!newShape)
|
||||
@ -1127,9 +1126,9 @@ TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
|
||||
{
|
||||
if (bce->parser->compileAndGo &&
|
||||
bce->globalScope->globalObj &&
|
||||
!bce->sc->mightAliasLocals() &&
|
||||
!bce->sc->funMightAliasLocals() &&
|
||||
!pn->isDeoptimized() &&
|
||||
!(bce->sc->flags & TCF_STRICT_MODE_CODE)) {
|
||||
!bce->sc->inStrictMode()) {
|
||||
switch (*op) {
|
||||
case JSOP_NAME: *op = JSOP_GETGNAME; break;
|
||||
case JSOP_SETNAME: *op = JSOP_SETGNAME; break;
|
||||
@ -1356,7 +1355,7 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
* the scope chain so that assignment will throw a TypeError.
|
||||
*/
|
||||
JS_ASSERT(op != JSOP_DELNAME);
|
||||
if (!(bce->sc->flags & TCF_FUN_HEAVYWEIGHT)) {
|
||||
if (!bce->sc->funIsHeavyweight()) {
|
||||
op = JSOP_CALLEE;
|
||||
pn->pn_dflags |= PND_CONST;
|
||||
}
|
||||
@ -2618,7 +2617,7 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
|
||||
* execution starts from script->code, so this has no semantic effect.
|
||||
*/
|
||||
|
||||
if (bce->sc->argumentsHasLocalBinding()) {
|
||||
if (bce->sc->funArgumentsHasLocalBinding()) {
|
||||
JS_ASSERT(bce->next() == bce->base()); /* See JSScript::argumentsBytecode. */
|
||||
bce->switchToProlog();
|
||||
if (Emit1(cx, bce, JSOP_ARGUMENTS) < 0)
|
||||
@ -2637,7 +2636,7 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
|
||||
bce->switchToMain();
|
||||
}
|
||||
|
||||
if (bce->sc->flags & TCF_FUN_IS_GENERATOR) {
|
||||
if (bce->sc->funIsGenerator()) {
|
||||
bce->switchToProlog();
|
||||
if (Emit1(cx, bce, JSOP_GENERATOR) < 0)
|
||||
return false;
|
||||
@ -2663,7 +2662,7 @@ MaybeEmitVarDecl(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *
|
||||
}
|
||||
|
||||
if (JOF_OPTYPE(pn->getOp()) == JOF_ATOM &&
|
||||
(!bce->sc->inFunction || (bce->sc->flags & TCF_FUN_HEAVYWEIGHT)))
|
||||
(!bce->sc->inFunction || bce->sc->funIsHeavyweight()))
|
||||
{
|
||||
bce->switchToProlog();
|
||||
if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.begin.lineno))
|
||||
@ -4827,7 +4826,7 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
return EmitFunctionDefNop(cx, bce, pn->pn_index);
|
||||
}
|
||||
|
||||
JS_ASSERT_IF(pn->pn_funbox->tcflags & TCF_FUN_HEAVYWEIGHT,
|
||||
JS_ASSERT_IF(pn->pn_funbox->funIsHeavyweight(),
|
||||
fun->kind() == JSFUN_INTERPRETED);
|
||||
|
||||
{
|
||||
@ -4837,10 +4836,14 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
if (!bce2.init())
|
||||
return false;
|
||||
|
||||
bce2.sc->flags = pn->pn_funbox->tcflags | (bce->sc->flags & TCF_FUN_MIGHT_ALIAS_LOCALS);
|
||||
bce2.sc->bindings.transfer(cx, &pn->pn_funbox->bindings);
|
||||
FunctionBox *funbox = pn->pn_funbox;
|
||||
bce2.sc->cxFlags = funbox->cxFlags;
|
||||
if (bce->sc->funMightAliasLocals())
|
||||
bce2.sc->setFunMightAliasLocals(); // inherit funMightAliasLocals from parent
|
||||
|
||||
bce2.sc->bindings.transfer(cx, &funbox->bindings);
|
||||
bce2.sc->setFunction(fun);
|
||||
bce2.sc->funbox = pn->pn_funbox;
|
||||
bce2.sc->funbox = funbox;
|
||||
bce2.parent = bce;
|
||||
bce2.globalScope = bce->globalScope;
|
||||
|
||||
@ -4863,7 +4866,7 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
|
||||
/* Emit a bytecode pointing to the closure object in its immediate. */
|
||||
if (pn->getOp() != JSOP_NOP) {
|
||||
if ((pn->pn_funbox->inGenexpLambda) && NewSrcNote(cx, bce, SRC_GENEXP) < 0)
|
||||
if (pn->pn_funbox->inGenexpLambda && NewSrcNote(cx, bce, SRC_GENEXP) < 0)
|
||||
return false;
|
||||
|
||||
return EmitFunctionOp(cx, pn->getOp(), index, bce);
|
||||
@ -5925,9 +5928,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
|
||||
case PNK_UPVARS:
|
||||
JS_ASSERT(pn->pn_names->count() != 0);
|
||||
bce->roLexdeps = pn->pn_names;
|
||||
ok = EmitTree(cx, bce, pn->pn_tree);
|
||||
bce->roLexdeps.clearMap();
|
||||
pn->pn_names.releaseMap(cx);
|
||||
break;
|
||||
|
||||
|
@ -123,7 +123,6 @@ struct BytecodeEmitter
|
||||
Parser *parser; /* the parser */
|
||||
|
||||
OwnedAtomIndexMapPtr atomIndices; /* literals indexed for mapping */
|
||||
AtomDefnMapPtr roLexdeps;
|
||||
unsigned firstLine; /* first line, for JSScript::NewScriptFromEmitter */
|
||||
|
||||
int stackDepth; /* current stack depth in script frame */
|
||||
|
@ -39,6 +39,7 @@
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "frontend/ParseNode.h"
|
||||
#include "frontend/Parser.h"
|
||||
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
@ -112,18 +113,12 @@ bool
|
||||
FunctionBox::inAnyDynamicScope() const
|
||||
{
|
||||
for (const FunctionBox *funbox = this; funbox; funbox = funbox->parent) {
|
||||
if (funbox->inWith || (funbox->tcflags & TCF_FUN_EXTENSIBLE_SCOPE))
|
||||
if (funbox->inWith || funbox->funHasExtensibleScope())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
FunctionBox::scopeIsExtensible() const
|
||||
{
|
||||
return tcflags & TCF_FUN_EXTENSIBLE_SCOPE;
|
||||
}
|
||||
|
||||
/* Add |node| to |parser|'s free node list. */
|
||||
void
|
||||
ParseNodeAllocator::freeNode(ParseNode *pn)
|
||||
|
@ -47,6 +47,7 @@
|
||||
|
||||
#include "frontend/ParseMaps.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
#include "frontend/TreeContext.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
@ -1528,24 +1529,35 @@ struct ObjectBox {
|
||||
ObjectBox *emitLink;
|
||||
JSObject *object;
|
||||
bool isFunctionBox;
|
||||
|
||||
ObjectBox(ObjectBox *traceLink, JSObject *obj);
|
||||
};
|
||||
|
||||
#define JSFB_LEVEL_BITS 14
|
||||
|
||||
struct FunctionBox : public ObjectBox
|
||||
{
|
||||
ParseNode *node;
|
||||
FunctionBox *siblings;
|
||||
FunctionBox *kids;
|
||||
FunctionBox *parent;
|
||||
Bindings bindings; /* bindings for this function */
|
||||
uint32_t queued:1,
|
||||
inLoop:1, /* in a loop in parent function */
|
||||
level:JSFB_LEVEL_BITS;
|
||||
uint32_t tcflags;
|
||||
bool inWith:1; /* some enclosing scope is a with-statement
|
||||
or E4X filter-expression */
|
||||
bool inGenexpLambda:1; /* lambda from generator expression */
|
||||
ParseNode *node;
|
||||
FunctionBox *siblings;
|
||||
FunctionBox *kids;
|
||||
FunctionBox *parent;
|
||||
Bindings bindings; /* bindings for this function */
|
||||
uint32_t level:JSFB_LEVEL_BITS;
|
||||
bool queued:1;
|
||||
bool inLoop:1; /* in a loop in parent function */
|
||||
bool inWith:1; /* some enclosing scope is a with-statement
|
||||
or E4X filter-expression */
|
||||
bool inGenexpLambda:1; /* lambda from generator expression */
|
||||
|
||||
ContextFlags cxFlags;
|
||||
|
||||
FunctionBox(ObjectBox* traceListHead, JSObject *obj, ParseNode *fn, TreeContext *tc);
|
||||
|
||||
bool funIsHeavyweight() const { return cxFlags.funIsHeavyweight; }
|
||||
bool funIsGenerator() const { return cxFlags.funIsGenerator; }
|
||||
bool funHasExtensibleScope() const { return cxFlags.funHasExtensibleScope; }
|
||||
|
||||
void setFunIsHeavyweight() { cxFlags.funIsHeavyweight = true; }
|
||||
|
||||
JSFunction *function() const { return (JSFunction *) object; }
|
||||
|
||||
@ -1554,12 +1566,6 @@ struct FunctionBox : public ObjectBox
|
||||
* filter-expression, or a function that uses direct eval.
|
||||
*/
|
||||
bool inAnyDynamicScope() const;
|
||||
|
||||
/*
|
||||
* Must this function's descendants be marked as having an extensible
|
||||
* ancestor?
|
||||
*/
|
||||
bool scopeIsExtensible() const;
|
||||
};
|
||||
|
||||
struct FunctionBoxQueue {
|
||||
|
@ -117,9 +117,7 @@ Parser::Parser(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin,
|
||||
principals(NULL),
|
||||
originPrincipals(NULL),
|
||||
callerFrame(cfp),
|
||||
callerVarObj(cfp ? &cfp->varObj() : NULL),
|
||||
allocator(cx),
|
||||
functionCount(0),
|
||||
traceListHead(NULL),
|
||||
tc(NULL),
|
||||
keepAtoms(cx->runtime),
|
||||
@ -127,7 +125,6 @@ Parser::Parser(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin,
|
||||
compileAndGo(compileAndGo)
|
||||
{
|
||||
cx->activeCompilations++;
|
||||
PodArrayZero(tempFreeList);
|
||||
setPrincipals(prin, originPrin);
|
||||
JS_ASSERT_IF(cfp, cfp->isScriptFrame());
|
||||
}
|
||||
@ -172,6 +169,14 @@ Parser::setPrincipals(JSPrincipals *prin, JSPrincipals *originPrin)
|
||||
JS_HoldPrincipals(originPrincipals);
|
||||
}
|
||||
|
||||
ObjectBox::ObjectBox(ObjectBox* traceLink, JSObject *obj)
|
||||
: traceLink(traceLink),
|
||||
emitLink(NULL),
|
||||
object(obj),
|
||||
isFunctionBox(false)
|
||||
{
|
||||
}
|
||||
|
||||
ObjectBox *
|
||||
Parser::newObjectBox(JSObject *obj)
|
||||
{
|
||||
@ -184,19 +189,49 @@ Parser::newObjectBox(JSObject *obj)
|
||||
* scanning, parsing and code generation for the whole script or top-level
|
||||
* function.
|
||||
*/
|
||||
ObjectBox *objbox = context->tempLifoAlloc().new_<ObjectBox>();
|
||||
|
||||
ObjectBox *objbox = context->tempLifoAlloc().new_<ObjectBox>(traceListHead, obj);
|
||||
if (!objbox) {
|
||||
js_ReportOutOfMemory(context);
|
||||
return NULL;
|
||||
}
|
||||
objbox->traceLink = traceListHead;
|
||||
|
||||
traceListHead = objbox;
|
||||
objbox->emitLink = NULL;
|
||||
objbox->object = obj;
|
||||
objbox->isFunctionBox = false;
|
||||
|
||||
return objbox;
|
||||
}
|
||||
|
||||
FunctionBox::FunctionBox(ObjectBox* traceListHead, JSObject *obj, ParseNode *fn, TreeContext *tc)
|
||||
: ObjectBox(traceListHead, obj),
|
||||
node(fn),
|
||||
siblings(tc->sc->functionList),
|
||||
kids(NULL),
|
||||
parent(tc->sc->funbox),
|
||||
bindings(tc->sc->context),
|
||||
level(tc->sc->staticLevel),
|
||||
queued(false),
|
||||
inLoop(false),
|
||||
inWith(!!tc->innermostWith),
|
||||
inGenexpLambda(false),
|
||||
cxFlags(tc->sc->context) // the cxFlags are set in LeaveFunction
|
||||
{
|
||||
isFunctionBox = true;
|
||||
for (StmtInfo *stmt = tc->sc->topStmt; stmt; stmt = stmt->down) {
|
||||
if (STMT_IS_LOOP(stmt)) {
|
||||
inLoop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!tc->sc->inFunction) {
|
||||
JSObject *scope = tc->sc->scopeChain();
|
||||
while (scope) {
|
||||
if (scope->isWith())
|
||||
inWith = true;
|
||||
scope = scope->enclosingScope();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FunctionBox *
|
||||
Parser::newFunctionBox(JSObject *obj, ParseNode *fn, TreeContext *tc)
|
||||
{
|
||||
@ -210,43 +245,14 @@ Parser::newFunctionBox(JSObject *obj, ParseNode *fn, TreeContext *tc)
|
||||
* scanning, parsing and code generation for the whole script or top-level
|
||||
* function.
|
||||
*/
|
||||
FunctionBox *funbox = context->tempLifoAlloc().newPod<FunctionBox>();
|
||||
FunctionBox *funbox = context->tempLifoAlloc().new_<FunctionBox>(traceListHead, obj, fn, tc);
|
||||
if (!funbox) {
|
||||
js_ReportOutOfMemory(context);
|
||||
return NULL;
|
||||
}
|
||||
funbox->traceLink = traceListHead;
|
||||
traceListHead = funbox;
|
||||
funbox->emitLink = NULL;
|
||||
funbox->object = obj;
|
||||
funbox->isFunctionBox = true;
|
||||
funbox->node = fn;
|
||||
funbox->siblings = tc->sc->functionList;
|
||||
tc->sc->functionList = funbox;
|
||||
++functionCount;
|
||||
funbox->kids = NULL;
|
||||
funbox->parent = tc->sc->funbox;
|
||||
new (&funbox->bindings) Bindings(context);
|
||||
funbox->queued = false;
|
||||
funbox->inLoop = false;
|
||||
for (StmtInfo *stmt = tc->sc->topStmt; stmt; stmt = stmt->down) {
|
||||
if (STMT_IS_LOOP(stmt)) {
|
||||
funbox->inLoop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
funbox->level = tc->sc->staticLevel;
|
||||
funbox->tcflags = tc->sc->flags & TCF_STRICT_MODE_CODE;
|
||||
funbox->inWith = !!tc->innermostWith;
|
||||
if (!tc->sc->inFunction) {
|
||||
JSObject *scope = tc->sc->scopeChain();
|
||||
while (scope) {
|
||||
if (scope->isWith())
|
||||
funbox->inWith = true;
|
||||
scope = scope->enclosingScope();
|
||||
}
|
||||
}
|
||||
funbox->inGenexpLambda = false;
|
||||
|
||||
traceListHead = tc->sc->functionList = funbox;
|
||||
|
||||
return funbox;
|
||||
}
|
||||
|
||||
@ -605,7 +611,6 @@ Parser::functionBody(FunctionBodyType type)
|
||||
PushStatement(tc->sc, &stmtInfo, STMT_BLOCK, -1);
|
||||
stmtInfo.flags = SIF_BODY_BLOCK;
|
||||
|
||||
unsigned oldflags = tc->sc->flags;
|
||||
JS_ASSERT(!tc->hasReturnExpr && !tc->hasReturnVoid);
|
||||
|
||||
ParseNode *pn;
|
||||
@ -620,7 +625,7 @@ Parser::functionBody(FunctionBodyType type)
|
||||
if (!pn->pn_kid) {
|
||||
pn = NULL;
|
||||
} else {
|
||||
if (tc->sc->flags & TCF_FUN_IS_GENERATOR) {
|
||||
if (tc->sc->funIsGenerator()) {
|
||||
ReportBadReturn(context, this, pn, JSREPORT_ERROR,
|
||||
JSMSG_BAD_GENERATOR_RETURN,
|
||||
JSMSG_BAD_ANON_GENERATOR_RETURN);
|
||||
@ -666,16 +671,16 @@ Parser::functionBody(FunctionBodyType type)
|
||||
for (FuncStmtSet::Range r = set->all(); !r.empty(); r.popFront()) {
|
||||
PropertyName *name = r.front()->asPropertyName();
|
||||
if (name == arguments)
|
||||
tc->sc->noteBindingsAccessedDynamically();
|
||||
tc->sc->setBindingsAccessedDynamically();
|
||||
else if (Definition *dn = tc->decls.lookupFirst(name))
|
||||
dn->pn_dflags |= PND_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* As explained by the TCF_ARGUMENTS_HAS_LOCAL_BINDING comment, turn uses
|
||||
* of 'arguments' into bindings. Use of 'arguments' should never escape a
|
||||
* nested function as an upvar.
|
||||
* As explained by the ContextFlags::funArgumentsHasLocalBinding comment,
|
||||
* turn uses of 'arguments' into bindings. Use of 'arguments' should never
|
||||
* escape a nested function as an upvar.
|
||||
*/
|
||||
for (AtomDefnRange r = tc->lexdeps->all(); !r.empty(); r.popFront()) {
|
||||
JSAtom *atom = r.front().key();
|
||||
@ -713,11 +718,11 @@ Parser::functionBody(FunctionBodyType type)
|
||||
*/
|
||||
BindingKind bindKind = tc->sc->bindings.lookup(context, arguments, NULL);
|
||||
if (bindKind == VARIABLE || bindKind == CONSTANT) {
|
||||
tc->sc->noteArgumentsHasLocalBinding();
|
||||
tc->sc->setFunArgumentsHasLocalBinding();
|
||||
|
||||
/* Dynamic scope access destroys all hope of optimization. */
|
||||
if (tc->sc->bindingsAccessedDynamically())
|
||||
tc->sc->noteDefinitelyNeedsArgsObj();
|
||||
tc->sc->setFunDefinitelyNeedsArgsObj();
|
||||
|
||||
/*
|
||||
* Check whether any parameters have been assigned within this
|
||||
@ -730,14 +735,13 @@ Parser::functionBody(FunctionBodyType type)
|
||||
AtomDeclsIter iter(&tc->decls);
|
||||
while (Definition *dn = iter.next()) {
|
||||
if (dn->kind() == Definition::ARG && dn->isAssigned()) {
|
||||
tc->sc->noteDefinitelyNeedsArgsObj();
|
||||
tc->sc->setFunDefinitelyNeedsArgsObj();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tc->sc->flags = oldflags | (tc->sc->flags & TCF_FUN_FLAGS);
|
||||
return pn;
|
||||
}
|
||||
|
||||
@ -1085,24 +1089,23 @@ EnterFunction(ParseNode *fn, Parser *parser, JSAtom *funAtom = NULL,
|
||||
FunctionSyntaxKind kind = Expression)
|
||||
{
|
||||
TreeContext *funtc = parser->tc;
|
||||
TreeContext *tc = funtc->parent;
|
||||
JSFunction *fun = parser->newFunction(tc, funAtom, kind);
|
||||
TreeContext *outertc = funtc->parent;
|
||||
JSFunction *fun = parser->newFunction(outertc, funAtom, kind);
|
||||
if (!fun)
|
||||
return NULL;
|
||||
|
||||
/* Create box for fun->object early to protect against last-ditch GC. */
|
||||
FunctionBox *funbox = parser->newFunctionBox(fun, fn, tc);
|
||||
FunctionBox *funbox = parser->newFunctionBox(fun, fn, outertc);
|
||||
if (!funbox)
|
||||
return NULL;
|
||||
|
||||
/* Initialize non-default members of funtc. */
|
||||
funtc->sc->flags |= funbox->tcflags;
|
||||
funtc->sc->blockidGen = tc->sc->blockidGen;
|
||||
/* Initialize non-default members of funtc->sc. */
|
||||
funtc->sc->blockidGen = outertc->sc->blockidGen;
|
||||
if (!GenerateBlockId(funtc->sc, funtc->sc->bodyid))
|
||||
return NULL;
|
||||
funtc->sc->setFunction(fun);
|
||||
funtc->sc->funbox = funbox;
|
||||
if (!SetStaticLevel(funtc->sc, tc->sc->staticLevel + 1))
|
||||
if (!SetStaticLevel(funtc->sc, outertc->sc->staticLevel + 1))
|
||||
return NULL;
|
||||
|
||||
return funbox;
|
||||
@ -1140,7 +1143,7 @@ LeaveFunction(ParseNode *fn, Parser *parser, PropertyName *funName = NULL,
|
||||
tc->sc->blockidGen = funtc->sc->blockidGen;
|
||||
|
||||
FunctionBox *funbox = fn->pn_funbox;
|
||||
funbox->tcflags |= funtc->sc->flags & TCF_FUN_FLAGS;
|
||||
funbox->cxFlags = funtc->sc->cxFlags; // copy all the flags
|
||||
|
||||
fn->pn_dflags |= PND_INITIALIZED;
|
||||
if (!tc->sc->topStmt || tc->sc->topStmt->type == STMT_BLOCK)
|
||||
@ -1542,6 +1545,9 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
||||
if (!funbox)
|
||||
return NULL;
|
||||
|
||||
if (outertc->sc->inStrictMode())
|
||||
funsc.setInStrictMode(); // inherit strict mode from parent
|
||||
|
||||
RootedVarFunction fun(context, funbox->function());
|
||||
|
||||
/* Now parse formal argument list and compute fun->nargs. */
|
||||
@ -1619,7 +1625,7 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
||||
* parents: any local can be read at runtime.
|
||||
*/
|
||||
if (funsc.bindingsAccessedDynamically())
|
||||
outertc->sc->noteBindingsAccessedDynamically();
|
||||
outertc->sc->setBindingsAccessedDynamically();
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
/*
|
||||
@ -1662,9 +1668,9 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
||||
* visible eval call, or assignment to 'arguments'), flag the function as
|
||||
* heavyweight (requiring a call object per invocation).
|
||||
*/
|
||||
if (funsc.flags & TCF_FUN_HEAVYWEIGHT) {
|
||||
if (funsc.funIsHeavyweight()) {
|
||||
fun->flags |= JSFUN_HEAVYWEIGHT;
|
||||
outertc->sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
outertc->sc->setFunIsHeavyweight();
|
||||
}
|
||||
|
||||
JSOp op = JSOP_NOP;
|
||||
@ -1680,12 +1686,12 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
||||
*/
|
||||
JS_ASSERT(!outertc->sc->inStrictMode());
|
||||
op = JSOP_DEFFUN;
|
||||
outertc->sc->noteMightAliasLocals();
|
||||
outertc->sc->noteHasExtensibleScope();
|
||||
outertc->sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
outertc->sc->setFunMightAliasLocals();
|
||||
outertc->sc->setFunHasExtensibleScope();
|
||||
outertc->sc->setFunIsHeavyweight();
|
||||
|
||||
/*
|
||||
* Instead of noteBindingsAccessedDynamically, which would be
|
||||
* Instead of setting bindingsAccessedDynamically, which would be
|
||||
* overly conservative, remember the names of all function
|
||||
* statements and mark any bindings with the same as aliased at the
|
||||
* end of functionBody.
|
||||
@ -1821,7 +1827,7 @@ Parser::recognizeDirectivePrologue(ParseNode *pn, bool *isDirectivePrologueMembe
|
||||
return false;
|
||||
}
|
||||
|
||||
tc->sc->flags |= TCF_STRICT_MODE_CODE;
|
||||
tc->sc->setInStrictMode();
|
||||
tokenStream.setStrictMode();
|
||||
}
|
||||
}
|
||||
@ -1887,7 +1893,7 @@ Parser::statements(bool *hasFunctionStmt)
|
||||
* General deoptimization was done in functionDef, here we just
|
||||
* need to tell TOK_LC in Parser::statement to add braces.
|
||||
*/
|
||||
JS_ASSERT(tc->sc->hasExtensibleScope());
|
||||
JS_ASSERT(tc->sc->funHasExtensibleScope());
|
||||
if (hasFunctionStmt)
|
||||
*hasFunctionStmt = true;
|
||||
}
|
||||
@ -2124,7 +2130,7 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser)
|
||||
if (stmt && stmt->type == STMT_WITH) {
|
||||
data->fresh = false;
|
||||
pn->pn_dflags |= PND_DEOPTIMIZED;
|
||||
tc->sc->noteMightAliasLocals();
|
||||
tc->sc->setFunMightAliasLocals();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2308,7 +2314,7 @@ NoteLValue(JSContext *cx, ParseNode *pn, SharedContext *sc, unsigned dflag = PND
|
||||
* happens by making such functions heavyweight.
|
||||
*/
|
||||
if (sc->inFunction && pn->pn_atom == sc->fun()->atom)
|
||||
sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
sc->setFunIsHeavyweight();
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -2660,7 +2666,7 @@ Parser::returnOrYield(bool useAssignExpr)
|
||||
* a |for| token, so we have to delay flagging the current function.
|
||||
*/
|
||||
if (tc->parenDepth == 0) {
|
||||
tc->sc->flags |= TCF_FUN_IS_GENERATOR;
|
||||
tc->sc->setFunIsGenerator();
|
||||
} else {
|
||||
tc->yieldCount++;
|
||||
tc->yieldNode = pn;
|
||||
@ -2697,7 +2703,7 @@ Parser::returnOrYield(bool useAssignExpr)
|
||||
tc->hasReturnVoid = true;
|
||||
}
|
||||
|
||||
if (tc->hasReturnExpr && (tc->sc->flags & TCF_FUN_IS_GENERATOR)) {
|
||||
if (tc->hasReturnExpr && tc->sc->funIsGenerator()) {
|
||||
/* As in Python (see PEP-255), disallow return v; in generators. */
|
||||
ReportBadReturn(context, this, pn, JSREPORT_ERROR,
|
||||
JSMSG_BAD_GENERATOR_RETURN,
|
||||
@ -3607,7 +3613,7 @@ Parser::withStatement()
|
||||
* doesn't even merit a warning under JSOPTION_STRICT. See
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
|
||||
*/
|
||||
if (tc->sc->flags & TCF_STRICT_MODE_CODE) {
|
||||
if (tc->sc->inStrictMode()) {
|
||||
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_STRICT_CODE_WITH);
|
||||
return NULL;
|
||||
}
|
||||
@ -3635,8 +3641,8 @@ Parser::withStatement()
|
||||
pn->pn_pos.end = pn2->pn_pos.end;
|
||||
pn->pn_right = pn2;
|
||||
|
||||
tc->sc->noteBindingsAccessedDynamically();
|
||||
tc->sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
tc->sc->setBindingsAccessedDynamically();
|
||||
tc->sc->setFunIsHeavyweight();
|
||||
tc->innermostWith = oldWith;
|
||||
|
||||
/*
|
||||
@ -4121,7 +4127,7 @@ Parser::statement()
|
||||
pn = new_<DebuggerStatement>(tokenStream.currentToken().pos);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
tc->sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
tc->sc->setFunIsHeavyweight();
|
||||
break;
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
@ -4146,7 +4152,7 @@ Parser::statement()
|
||||
JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NOP);
|
||||
|
||||
/* Is this an E4X dagger I see before me? */
|
||||
tc->sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
tc->sc->setFunIsHeavyweight();
|
||||
ParseNode *pn2 = expr();
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
@ -4535,11 +4541,9 @@ Parser::condExpr1()
|
||||
* where it's unambiguous, even if we might be parsing the init of a
|
||||
* for statement.
|
||||
*/
|
||||
uint32_t oldflags = tc->sc->flags;
|
||||
bool oldInForInit = tc->sc->inForInit;
|
||||
tc->sc->inForInit = false;
|
||||
ParseNode *thenExpr = assignExpr();
|
||||
tc->sc->flags = oldflags | (tc->sc->flags & TCF_FUN_FLAGS);
|
||||
tc->sc->inForInit = oldInForInit;
|
||||
if (!thenExpr)
|
||||
return NULL;
|
||||
@ -4910,7 +4914,7 @@ class GenexpGuard {
|
||||
TreeContext *tc = parser->tc;
|
||||
if (tc->parenDepth == 0) {
|
||||
tc->yieldCount = 0;
|
||||
tc->yieldNode = tc->argumentsNode = NULL;
|
||||
tc->yieldNode = NULL;
|
||||
}
|
||||
startYieldCount = tc->yieldCount;
|
||||
tc->parenDepth++;
|
||||
@ -4961,14 +4965,14 @@ GenexpGuard::maybeNoteGenerator(ParseNode *pn)
|
||||
{
|
||||
TreeContext *tc = parser->tc;
|
||||
if (tc->yieldCount > 0) {
|
||||
tc->sc->flags |= TCF_FUN_IS_GENERATOR;
|
||||
tc->sc->setFunIsGenerator();
|
||||
if (!tc->sc->inFunction) {
|
||||
parser->reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD,
|
||||
js_yield_str);
|
||||
return false;
|
||||
}
|
||||
if (tc->hasReturnExpr) {
|
||||
/* At the time we saw the yield, we might not have set TCF_FUN_IS_GENERATOR yet. */
|
||||
/* At the time we saw the yield, we might not have set funIsGenerator yet. */
|
||||
ReportBadReturn(tc->sc->context, parser, pn, JSREPORT_ERROR,
|
||||
JSMSG_BAD_GENERATOR_RETURN,
|
||||
JSMSG_BAD_ANON_GENERATOR_RETURN);
|
||||
@ -5464,13 +5468,14 @@ Parser::generatorExpr(ParseNode *kid)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* We assume conservatively that any deoptimization flag in tc->sc->flags
|
||||
* We assume conservatively that any deoptimization flags in tc->sc
|
||||
* come from the kid. So we propagate these flags into genfn. For code
|
||||
* simplicity we also do not detect if the flags were only set in the
|
||||
* kid and could be removed from tc->sc->flags.
|
||||
* kid and could be removed from tc->sc.
|
||||
*/
|
||||
gensc.flags |= TCF_FUN_IS_GENERATOR | (outertc->sc->flags & TCF_FUN_FLAGS);
|
||||
funbox->tcflags |= gensc.flags;
|
||||
gensc.cxFlags = outertc->sc->cxFlags;
|
||||
gensc.setFunIsGenerator();
|
||||
|
||||
funbox->inGenexpLambda = true;
|
||||
genfn->pn_funbox = funbox;
|
||||
genfn->pn_blockid = gensc.bodyid;
|
||||
@ -5641,8 +5646,8 @@ Parser::memberExpr(JSBool allowCallSyntax)
|
||||
TokenPtr begin = lhs->pn_pos.begin;
|
||||
if (tt == TOK_LP) {
|
||||
/* Filters are effectively 'with', so deoptimize names. */
|
||||
tc->sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
tc->sc->noteBindingsAccessedDynamically();
|
||||
tc->sc->setFunIsHeavyweight();
|
||||
tc->sc->setBindingsAccessedDynamically();
|
||||
|
||||
StmtInfo stmtInfo(context);
|
||||
ParseNode *oldWith = tc->innermostWith;
|
||||
@ -5761,14 +5766,14 @@ Parser::memberExpr(JSBool allowCallSyntax)
|
||||
if (lhs->pn_atom == context->runtime->atomState.evalAtom) {
|
||||
/* Select JSOP_EVAL and flag tc as heavyweight. */
|
||||
nextMember->setOp(JSOP_EVAL);
|
||||
tc->sc->noteBindingsAccessedDynamically();
|
||||
tc->sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
tc->sc->setBindingsAccessedDynamically();
|
||||
tc->sc->setFunIsHeavyweight();
|
||||
/*
|
||||
* In non-strict mode code, direct calls to eval can add
|
||||
* variables to the call object.
|
||||
*/
|
||||
if (!tc->sc->inStrictMode())
|
||||
tc->sc->noteHasExtensibleScope();
|
||||
tc->sc->setFunHasExtensibleScope();
|
||||
}
|
||||
} else if (lhs->isOp(JSOP_GETPROP)) {
|
||||
/* Select JSOP_FUNAPPLY given foo.apply(...). */
|
||||
@ -5809,11 +5814,9 @@ Parser::bracketedExpr()
|
||||
* where it's unambiguous, even if we might be parsing the init of a
|
||||
* for statement.
|
||||
*/
|
||||
uint32_t oldflags = tc->sc->flags;
|
||||
bool oldInForInit = tc->sc->inForInit;
|
||||
tc->sc->inForInit = false;
|
||||
ParseNode *pn = expr();
|
||||
tc->sc->flags = oldflags | (tc->sc->flags & TCF_FUN_FLAGS);
|
||||
tc->sc->inForInit = oldInForInit;
|
||||
return pn;
|
||||
}
|
||||
@ -5919,8 +5922,8 @@ Parser::qualifiedSuffix(ParseNode *pn)
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
|
||||
tc->sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
tc->sc->noteBindingsAccessedDynamically();
|
||||
tc->sc->setFunIsHeavyweight();
|
||||
tc->sc->setBindingsAccessedDynamically();
|
||||
|
||||
/* Left operand of :: must be evaluated if it is an identifier. */
|
||||
if (pn->isOp(JSOP_QNAMEPART))
|
||||
@ -5966,8 +5969,8 @@ Parser::qualifiedIdentifier()
|
||||
return NULL;
|
||||
if (tokenStream.matchToken(TOK_DBLCOLON)) {
|
||||
/* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
|
||||
tc->sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
tc->sc->noteBindingsAccessedDynamically();
|
||||
tc->sc->setFunIsHeavyweight();
|
||||
tc->sc->setBindingsAccessedDynamically();
|
||||
pn = qualifiedSuffix(pn);
|
||||
}
|
||||
return pn;
|
||||
@ -6471,8 +6474,8 @@ Parser::propertyQualifiedIdentifier()
|
||||
JS_ASSERT(tokenStream.peekToken() == TOK_DBLCOLON);
|
||||
|
||||
/* Deoptimize QualifiedIdentifier properties to avoid tricky analysis. */
|
||||
tc->sc->flags |= TCF_FUN_HEAVYWEIGHT;
|
||||
tc->sc->noteBindingsAccessedDynamically();
|
||||
tc->sc->setFunIsHeavyweight();
|
||||
tc->sc->setBindingsAccessedDynamically();
|
||||
|
||||
PropertyName *name = tokenStream.currentToken().name();
|
||||
ParseNode *node = NameNode::create(PNK_NAME, name, this, this->tc->sc);
|
||||
|
@ -55,8 +55,6 @@
|
||||
#include "frontend/ParseNode.h"
|
||||
#include "frontend/TreeContext.h"
|
||||
|
||||
#define NUM_TEMP_FREELISTS 6U /* 32 to 2048 byte size classes (32 bit) */
|
||||
|
||||
typedef struct BindData BindData;
|
||||
|
||||
namespace js {
|
||||
@ -70,15 +68,12 @@ enum VarContext { HoistVars, DontHoistVars };
|
||||
struct Parser : private AutoGCRooter
|
||||
{
|
||||
JSContext *const context; /* FIXME Bug 551291: use AutoGCRooter::context? */
|
||||
void *tempFreeList[NUM_TEMP_FREELISTS];
|
||||
TokenStream tokenStream;
|
||||
void *tempPoolMark; /* initial JSContext.tempLifoAlloc mark */
|
||||
JSPrincipals *principals; /* principals associated with source */
|
||||
JSPrincipals *originPrincipals; /* see jsapi.h 'originPrincipals' comment */
|
||||
StackFrame *const callerFrame; /* scripted caller frame for eval and dbgapi */
|
||||
JSObject *const callerVarObj; /* callerFrame's varObj */
|
||||
ParseNodeAllocator allocator;
|
||||
uint32_t functionCount; /* number of functions in current unit */
|
||||
ObjectBox *traceListHead; /* list of parsed object for GC tracing */
|
||||
|
||||
TreeContext *tc; /* innermost tree context (stack-allocated) */
|
||||
|
@ -52,7 +52,7 @@ using namespace js;
|
||||
using namespace js::frontend;
|
||||
|
||||
static void
|
||||
FlagHeavyweights(Definition *dn, FunctionBox *funbox, uint32_t *tcflags, bool topInFunction)
|
||||
FlagHeavyweights(Definition *dn, FunctionBox *funbox, bool *isHeavyweight, bool topInFunction)
|
||||
{
|
||||
unsigned dnLevel = dn->frameLevel();
|
||||
|
||||
@ -64,17 +64,17 @@ FlagHeavyweights(Definition *dn, FunctionBox *funbox, uint32_t *tcflags, bool to
|
||||
* funbox whose body contains the dn definition.
|
||||
*/
|
||||
if (funbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
|
||||
funbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
|
||||
funbox->setFunIsHeavyweight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!funbox && topInFunction)
|
||||
*tcflags |= TCF_FUN_HEAVYWEIGHT;
|
||||
*isHeavyweight = true;
|
||||
}
|
||||
|
||||
static void
|
||||
SetFunctionKinds(FunctionBox *funbox, uint32_t *tcflags, bool topInFunction, bool isDirectEval)
|
||||
SetFunctionKinds(FunctionBox *funbox, bool *isHeavyweight, bool topInFunction, bool isDirectEval)
|
||||
{
|
||||
for (; funbox; funbox = funbox->siblings) {
|
||||
ParseNode *fn = funbox->node;
|
||||
@ -86,13 +86,13 @@ SetFunctionKinds(FunctionBox *funbox, uint32_t *tcflags, bool topInFunction, boo
|
||||
continue;
|
||||
|
||||
if (funbox->kids)
|
||||
SetFunctionKinds(funbox->kids, tcflags, topInFunction, isDirectEval);
|
||||
SetFunctionKinds(funbox->kids, isHeavyweight, topInFunction, isDirectEval);
|
||||
|
||||
JSFunction *fun = funbox->function();
|
||||
|
||||
JS_ASSERT(fun->kind() == JSFUN_INTERPRETED);
|
||||
|
||||
if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
|
||||
if (funbox->funIsHeavyweight()) {
|
||||
/* nothing to do */
|
||||
} else if (isDirectEval || funbox->inAnyDynamicScope()) {
|
||||
/*
|
||||
@ -134,7 +134,7 @@ SetFunctionKinds(FunctionBox *funbox, uint32_t *tcflags, bool topInFunction, boo
|
||||
* ensure that its containing function has been flagged as
|
||||
* heavyweight.
|
||||
*
|
||||
* The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
|
||||
* The emitter must see funIsHeavyweight() accurately before
|
||||
* generating any code for a tree of nested functions.
|
||||
*/
|
||||
AtomDefnMapPtr upvars = pn->pn_names;
|
||||
@ -144,7 +144,7 @@ SetFunctionKinds(FunctionBox *funbox, uint32_t *tcflags, bool topInFunction, boo
|
||||
Definition *defn = r.front().value();
|
||||
Definition *lexdep = defn->resolve();
|
||||
if (!lexdep->isFreeVar())
|
||||
FlagHeavyweights(lexdep, funbox, tcflags, topInFunction);
|
||||
FlagHeavyweights(lexdep, funbox, isHeavyweight, topInFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -179,7 +179,9 @@ MarkExtensibleScopeDescendants(JSContext *context, FunctionBox *funbox, bool has
|
||||
|
||||
if (funbox->kids) {
|
||||
if (!MarkExtensibleScopeDescendants(context, funbox->kids,
|
||||
hasExtensibleParent || funbox->scopeIsExtensible())) {
|
||||
hasExtensibleParent ||
|
||||
funbox->funHasExtensibleScope()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -197,6 +199,9 @@ frontend::AnalyzeFunctions(Parser *parser)
|
||||
if (!MarkExtensibleScopeDescendants(sc->context, sc->functionList, false))
|
||||
return false;
|
||||
bool isDirectEval = !!parser->callerFrame;
|
||||
SetFunctionKinds(sc->functionList, &sc->flags, sc->inFunction, isDirectEval);
|
||||
bool isHeavyweight = false;
|
||||
SetFunctionKinds(sc->functionList, &isHeavyweight, sc->inFunction, isDirectEval);
|
||||
if (isHeavyweight)
|
||||
sc->setFunIsHeavyweight();
|
||||
return true;
|
||||
}
|
||||
|
@ -563,7 +563,7 @@ js::ReportStrictModeError(JSContext *cx, TokenStream *ts, SharedContext *sc, Par
|
||||
|
||||
/* In strict mode code, this is an error, not merely a warning. */
|
||||
unsigned flags;
|
||||
if ((ts && ts->isStrictMode()) || (sc && (sc->flags & TCF_STRICT_MODE_CODE))) {
|
||||
if ((ts && ts->isStrictMode()) || (sc && sc->inStrictMode())) {
|
||||
flags = JSREPORT_ERROR;
|
||||
} else {
|
||||
if (!cx->hasStrictOption())
|
||||
|
@ -51,7 +51,6 @@ namespace js {
|
||||
inline
|
||||
SharedContext::SharedContext(JSContext *cx, bool inFunction)
|
||||
: context(cx),
|
||||
flags(0),
|
||||
bodyid(0),
|
||||
blockidGen(0),
|
||||
topStmt(NULL),
|
||||
@ -65,7 +64,8 @@ SharedContext::SharedContext(JSContext *cx, bool inFunction)
|
||||
bindings(cx),
|
||||
bindingsRoot(cx, &bindings),
|
||||
inFunction(inFunction),
|
||||
inForInit(false)
|
||||
inForInit(false),
|
||||
cxFlags(cx)
|
||||
{
|
||||
}
|
||||
|
||||
@ -103,7 +103,6 @@ TreeContext::TreeContext(Parser *prs, SharedContext *sc)
|
||||
blockNode(NULL),
|
||||
decls(prs->context),
|
||||
yieldNode(NULL),
|
||||
argumentsNode(NULL),
|
||||
parserTC(&prs->tc),
|
||||
lexdeps(prs->context),
|
||||
parent(prs->tc),
|
||||
@ -119,8 +118,6 @@ TreeContext::TreeContext(Parser *prs, SharedContext *sc)
|
||||
inline bool
|
||||
TreeContext::init(JSContext *cx)
|
||||
{
|
||||
if (cx->hasRunOption(JSOPTION_STRICT_MODE))
|
||||
sc->flags |= TCF_STRICT_MODE_CODE;
|
||||
return decls.init() && lexdeps.ensureMap(sc->context);
|
||||
}
|
||||
|
||||
|
@ -56,20 +56,20 @@ typedef struct BindData BindData;
|
||||
|
||||
namespace js {
|
||||
|
||||
JS_ENUM_HEADER(TreeContextFlags, uint32_t)
|
||||
{
|
||||
// function needs Call object per call
|
||||
TCF_FUN_HEAVYWEIGHT = 0x1,
|
||||
struct StmtInfo;
|
||||
|
||||
// parsed yield statement in function
|
||||
TCF_FUN_IS_GENERATOR = 0x2,
|
||||
class ContextFlags {
|
||||
|
||||
// This class's data is all private and so only visible to these friends.
|
||||
friend class SharedContext;
|
||||
friend class FunctionBox;
|
||||
|
||||
// This function/global/eval code body contained a Use Strict Directive.
|
||||
// Treat certain strict warnings as errors, and forbid the use of 'with'.
|
||||
// See also TSF_STRICT_MODE_CODE, JSScript::strictModeCode, and
|
||||
// JSREPORT_STRICT_ERROR.
|
||||
//
|
||||
TCF_STRICT_MODE_CODE = 0x4,
|
||||
bool inStrictMode:1;
|
||||
|
||||
// The (static) bindings of this script need to support dynamic name
|
||||
// read/write access. Here, 'dynamic' means dynamic dictionary lookup on
|
||||
@ -91,11 +91,22 @@ JS_ENUM_HEADER(TreeContextFlags, uint32_t)
|
||||
// taken not to turn off the whole 'arguments' optimization). To answer the
|
||||
// more general "is this argument aliased" question, script->needsArgsObj
|
||||
// should be tested (see JSScript::argIsAlised).
|
||||
TCF_BINDINGS_ACCESSED_DYNAMICALLY = 0x8,
|
||||
//
|
||||
bool bindingsAccessedDynamically:1;
|
||||
|
||||
// The |fun*| flags are only relevant if |inFunction| is true. Due to
|
||||
// sloppiness, however, some are set in cases where |inFunction| is
|
||||
// false.
|
||||
|
||||
// The function needs Call object per call.
|
||||
bool funIsHeavyweight:1;
|
||||
|
||||
// We parsed a yield statement in the function.
|
||||
bool funIsGenerator:1;
|
||||
|
||||
// The function or a function that encloses it may define new local names
|
||||
// at runtime through means other than calling eval.
|
||||
TCF_FUN_MIGHT_ALIAS_LOCALS = 0x10,
|
||||
bool funMightAliasLocals:1;
|
||||
|
||||
// This function does something that can extend the set of bindings in its
|
||||
// call objects --- it does a direct eval in non-strict code, or includes a
|
||||
@ -104,7 +115,7 @@ JS_ENUM_HEADER(TreeContextFlags, uint32_t)
|
||||
// This flag is *not* inherited by enclosed or enclosing functions; it
|
||||
// applies only to the function in whose flags it appears.
|
||||
//
|
||||
TCF_FUN_EXTENSIBLE_SCOPE = 0x20,
|
||||
bool funHasExtensibleScope:1;
|
||||
|
||||
// Technically, every function has a binding named 'arguments'. Internally,
|
||||
// this binding is only added when 'arguments' is mentioned by the function
|
||||
@ -127,7 +138,7 @@ JS_ENUM_HEADER(TreeContextFlags, uint32_t)
|
||||
// have no special semantics: the initial value is unconditionally the
|
||||
// actual argument (or undefined if nactual < nformal).
|
||||
//
|
||||
TCF_ARGUMENTS_HAS_LOCAL_BINDING = 0x40,
|
||||
bool funArgumentsHasLocalBinding:1;
|
||||
|
||||
// In many cases where 'arguments' has a local binding (as described above)
|
||||
// we do not need to actually create an arguments object in the function
|
||||
@ -138,27 +149,24 @@ JS_ENUM_HEADER(TreeContextFlags, uint32_t)
|
||||
// be unsound in several cases. The frontend filters out such cases by
|
||||
// setting this flag which eagerly sets script->needsArgsObj to true.
|
||||
//
|
||||
TCF_DEFINITELY_NEEDS_ARGS_OBJ = 0x80
|
||||
bool funDefinitelyNeedsArgsObj:1;
|
||||
|
||||
} JS_ENUM_FOOTER(TreeContextFlags);
|
||||
|
||||
// Sticky deoptimization flags to propagate from FunctionBody.
|
||||
static const uint32_t TCF_FUN_FLAGS = TCF_FUN_HEAVYWEIGHT |
|
||||
TCF_FUN_IS_GENERATOR |
|
||||
TCF_BINDINGS_ACCESSED_DYNAMICALLY |
|
||||
TCF_FUN_MIGHT_ALIAS_LOCALS |
|
||||
TCF_STRICT_MODE_CODE |
|
||||
TCF_FUN_EXTENSIBLE_SCOPE |
|
||||
TCF_ARGUMENTS_HAS_LOCAL_BINDING |
|
||||
TCF_DEFINITELY_NEEDS_ARGS_OBJ;
|
||||
|
||||
|
||||
struct StmtInfo;
|
||||
public:
|
||||
ContextFlags(JSContext *cx)
|
||||
: inStrictMode(cx->hasRunOption(JSOPTION_STRICT_MODE)),
|
||||
bindingsAccessedDynamically(false),
|
||||
funIsHeavyweight(false),
|
||||
funIsGenerator(false),
|
||||
funMightAliasLocals(false),
|
||||
funHasExtensibleScope(false),
|
||||
funArgumentsHasLocalBinding(false),
|
||||
funDefinitelyNeedsArgsObj(false)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct SharedContext {
|
||||
JSContext *context;
|
||||
|
||||
uint32_t flags; /* statement state flags, see above */
|
||||
uint32_t bodyid; /* block number of program/function body */
|
||||
uint32_t blockidGen; /* preincremented block number generator */
|
||||
|
||||
@ -190,21 +198,28 @@ struct SharedContext {
|
||||
|
||||
bool inForInit:1; /* parsing/emitting init expr of for; exclude 'in' */
|
||||
|
||||
ContextFlags cxFlags;
|
||||
|
||||
inline SharedContext(JSContext *cx, bool inFunction);
|
||||
|
||||
bool inStrictMode() const { return flags & TCF_STRICT_MODE_CODE; }
|
||||
bool bindingsAccessedDynamically() const { return flags & TCF_BINDINGS_ACCESSED_DYNAMICALLY; }
|
||||
bool mightAliasLocals() const { return flags & TCF_FUN_MIGHT_ALIAS_LOCALS; }
|
||||
bool hasExtensibleScope() const { return flags & TCF_FUN_EXTENSIBLE_SCOPE; }
|
||||
bool argumentsHasLocalBinding() const { return flags & TCF_ARGUMENTS_HAS_LOCAL_BINDING; }
|
||||
bool definitelyNeedsArgsObj() const { return flags & TCF_DEFINITELY_NEEDS_ARGS_OBJ; }
|
||||
bool inStrictMode() const { return cxFlags.inStrictMode; }
|
||||
bool bindingsAccessedDynamically() const { return cxFlags.bindingsAccessedDynamically; }
|
||||
bool funIsHeavyweight() const { return cxFlags.funIsHeavyweight; }
|
||||
bool funIsGenerator() const { return cxFlags.funIsGenerator; }
|
||||
bool funMightAliasLocals() const { return cxFlags.funMightAliasLocals; }
|
||||
bool funHasExtensibleScope() const { return cxFlags.funHasExtensibleScope; }
|
||||
bool funArgumentsHasLocalBinding() const { return cxFlags.funArgumentsHasLocalBinding; }
|
||||
bool funDefinitelyNeedsArgsObj() const { return cxFlags.funDefinitelyNeedsArgsObj; }
|
||||
|
||||
void noteMightAliasLocals() { flags |= TCF_FUN_MIGHT_ALIAS_LOCALS; }
|
||||
void noteBindingsAccessedDynamically() { flags |= TCF_BINDINGS_ACCESSED_DYNAMICALLY; }
|
||||
void noteHasExtensibleScope() { flags |= TCF_FUN_EXTENSIBLE_SCOPE; }
|
||||
void noteArgumentsHasLocalBinding() { flags |= TCF_ARGUMENTS_HAS_LOCAL_BINDING; }
|
||||
void noteDefinitelyNeedsArgsObj() { JS_ASSERT(argumentsHasLocalBinding());
|
||||
flags |= TCF_DEFINITELY_NEEDS_ARGS_OBJ; }
|
||||
void setInStrictMode() { cxFlags.inStrictMode = true; }
|
||||
void setBindingsAccessedDynamically() { cxFlags.bindingsAccessedDynamically = true; }
|
||||
void setFunIsHeavyweight() { cxFlags.funIsHeavyweight = true; }
|
||||
void setFunIsGenerator() { cxFlags.funIsGenerator = true; }
|
||||
void setFunMightAliasLocals() { cxFlags.funMightAliasLocals = true; }
|
||||
void setFunHasExtensibleScope() { cxFlags.funHasExtensibleScope = true; }
|
||||
void setFunArgumentsHasLocalBinding() { cxFlags.funArgumentsHasLocalBinding = true; }
|
||||
void setFunDefinitelyNeedsArgsObj() { JS_ASSERT(cxFlags.funArgumentsHasLocalBinding);
|
||||
cxFlags.funDefinitelyNeedsArgsObj = true; }
|
||||
|
||||
unsigned argumentsLocalSlot() const;
|
||||
|
||||
@ -257,9 +272,6 @@ struct TreeContext { /* tree context for semantic checks */
|
||||
ParseNode *yieldNode; /* parse node for a yield expression that might
|
||||
be an error if we turn out to be inside a
|
||||
generator expression */
|
||||
ParseNode *argumentsNode; /* parse node for an arguments variable that
|
||||
might be an error if we turn out to be
|
||||
inside a generator expression */
|
||||
|
||||
private:
|
||||
TreeContext **parserTC; /* this points to the Parser's active tc
|
||||
|
40
js/src/jit-test/tests/basic/bug754150.js
Normal file
40
js/src/jit-test/tests/basic/bug754150.js
Normal file
@ -0,0 +1,40 @@
|
||||
// |jit-test| error: TypeError;
|
||||
function printStatus (msg) {}
|
||||
function toPrinted(value) {
|
||||
value = value.replace(/\\n/g, 'NL')
|
||||
}
|
||||
function reportCompare (expected, actual, description) {
|
||||
printStatus ("Expected value '" + toPrinted(expected) + "' matched actual value '" + toPrinted(actual) + "'");
|
||||
}
|
||||
var UBound = 0;
|
||||
var statusitems = [];
|
||||
var actual = '';
|
||||
var actualvalues = [];
|
||||
var expect= '';
|
||||
var expectedvalues = [];
|
||||
testThis('x()');
|
||||
testThis('"abc"()');
|
||||
testThis('x()');
|
||||
testThis('Date(12345)()');
|
||||
testThis('x()');
|
||||
testThis('1()');
|
||||
testThis('x()');
|
||||
testThis('void(0)()');
|
||||
testThis('x()');
|
||||
testThis('[1,2,3,4,5](1)');
|
||||
gczeal(4);
|
||||
testThis('x(1)');
|
||||
checkThis('(function (y) {return y+1;})("abc")');
|
||||
checkThis('f("abc")');
|
||||
function testThis(sInvalidSyntax) {
|
||||
expectedvalues[UBound] = expect;
|
||||
actualvalues[UBound] = actual;
|
||||
UBound++;
|
||||
}
|
||||
function checkThis(sValidSyntax) {
|
||||
for (var i=0; i<UBound; i++)
|
||||
reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]);
|
||||
}
|
||||
var actualvalues = [];
|
||||
for (var i=0; i<UBound; i++)
|
||||
reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]);
|
@ -484,18 +484,6 @@ js_DumpAtoms(JSContext *cx, FILE *fp)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
# define TEMP_SIZE_START_LOG2 5
|
||||
#else
|
||||
# define TEMP_SIZE_START_LOG2 6
|
||||
#endif
|
||||
#define TEMP_SIZE_LIMIT_LOG2 (TEMP_SIZE_START_LOG2 + NUM_TEMP_FREELISTS)
|
||||
|
||||
#define TEMP_SIZE_START JS_BIT(TEMP_SIZE_START_LOG2)
|
||||
#define TEMP_SIZE_LIMIT JS_BIT(TEMP_SIZE_LIMIT_LOG2)
|
||||
|
||||
JS_STATIC_ASSERT(TEMP_SIZE_START >= sizeof(JSHashTable));
|
||||
|
||||
namespace js {
|
||||
|
||||
void
|
||||
|
@ -395,7 +395,8 @@ Valueify(const JSClass *c)
|
||||
* value of objects.
|
||||
*/
|
||||
enum ESClassValue {
|
||||
ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boolean, ESClass_RegExp
|
||||
ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boolean,
|
||||
ESClass_RegExp, ESClass_ArrayBuffer
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -81,6 +81,7 @@
|
||||
# include "methodjit/MethodJIT.h"
|
||||
#endif
|
||||
#include "gc/Marking.h"
|
||||
#include "js/MemoryMetrics.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
#include "frontend/ParseMaps.h"
|
||||
#include "yarr/BumpPointerAllocator.h"
|
||||
@ -93,27 +94,65 @@
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
void
|
||||
JSRuntime::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
|
||||
size_t *mjitCode, size_t *regexpCode, size_t *unusedCodeMemory,
|
||||
size_t *stackCommitted, size_t *gcMarkerSize)
|
||||
struct CallbackData
|
||||
{
|
||||
if (normal)
|
||||
*normal = mallocSizeOf(dtoaState);
|
||||
CallbackData(JSMallocSizeOfFun f) : mallocSizeOf(f), n(0) {}
|
||||
JSMallocSizeOfFun mallocSizeOf;
|
||||
size_t n;
|
||||
};
|
||||
|
||||
if (temporary)
|
||||
*temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
|
||||
void CompartmentCallback(JSRuntime *rt, void *vdata, JSCompartment *compartment)
|
||||
{
|
||||
CallbackData *data = (CallbackData *) vdata;
|
||||
data->n += data->mallocSizeOf(compartment);
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, RuntimeSizes *runtime)
|
||||
{
|
||||
runtime->object = mallocSizeOf(this);
|
||||
|
||||
runtime->atomsTable = atomState.atoms.sizeOfExcludingThis(mallocSizeOf);
|
||||
|
||||
runtime->contexts = 0;
|
||||
for (ContextIter acx(this); !acx.done(); acx.next())
|
||||
runtime->contexts += acx->sizeOfIncludingThis(mallocSizeOf);
|
||||
|
||||
runtime->dtoa = mallocSizeOf(dtoaState);
|
||||
|
||||
runtime->temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
|
||||
|
||||
if (execAlloc_)
|
||||
execAlloc_->sizeOfCode(mjitCode, regexpCode, unusedCodeMemory);
|
||||
execAlloc_->sizeOfCode(&runtime->mjitCode, &runtime->regexpCode,
|
||||
&runtime->unusedCodeMemory);
|
||||
else
|
||||
*mjitCode = *regexpCode = *unusedCodeMemory = 0;
|
||||
runtime->mjitCode = runtime->regexpCode = runtime->unusedCodeMemory = 0;
|
||||
|
||||
if (stackCommitted)
|
||||
*stackCommitted = stackSpace.sizeOfCommitted();
|
||||
runtime->stackCommitted = stackSpace.sizeOfCommitted();
|
||||
|
||||
if (gcMarkerSize)
|
||||
*gcMarkerSize = gcMarker.sizeOfExcludingThis(mallocSizeOf);
|
||||
runtime->gcMarker = gcMarker.sizeOfExcludingThis(mallocSizeOf);
|
||||
|
||||
runtime->mathCache = mathCache_ ? mathCache_->sizeOfIncludingThis(mallocSizeOf) : 0;
|
||||
|
||||
runtime->scriptFilenames = scriptFilenameTable.sizeOfExcludingThis(mallocSizeOf);
|
||||
for (ScriptFilenameTable::Range r = scriptFilenameTable.all(); !r.empty(); r.popFront())
|
||||
runtime->scriptFilenames += mallocSizeOf(r.front());
|
||||
|
||||
runtime->compartmentObjects = 0;
|
||||
CallbackData data(mallocSizeOf);
|
||||
JS_IterateCompartments(this, &data, CompartmentCallback);
|
||||
runtime->compartmentObjects = data.n;
|
||||
}
|
||||
|
||||
size_t
|
||||
JSRuntime::sizeOfExplicitNonHeap()
|
||||
{
|
||||
if (!execAlloc_)
|
||||
return 0;
|
||||
|
||||
size_t mjitCode, regexpCode, unusedCodeMemory;
|
||||
execAlloc_->sizeOfCode(&mjitCode, ®expCode, &unusedCodeMemory);
|
||||
return mjitCode + regexpCode + unusedCodeMemory + stackSpace.sizeOfCommitted();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -376,6 +376,10 @@ class FreeOp : public JSFreeOp {
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
namespace JS {
|
||||
struct RuntimeSizes;
|
||||
}
|
||||
|
||||
struct JSRuntime : js::RuntimeFriendFields
|
||||
{
|
||||
/* Default compartment. */
|
||||
@ -880,9 +884,8 @@ struct JSRuntime : js::RuntimeFriendFields
|
||||
return jitHardening;
|
||||
}
|
||||
|
||||
void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
|
||||
size_t *mjitCode, size_t *regexpCode, size_t *unusedCodeMemory,
|
||||
size_t *stackCommitted, size_t *gcMarker);
|
||||
void sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, js::RuntimeSizes *runtime);
|
||||
size_t sizeOfExplicitNonHeap();
|
||||
};
|
||||
|
||||
/* Common macros to access thread-local caches in JSRuntime. */
|
||||
|
@ -1201,30 +1201,6 @@ IsBuiltinFunctionConstructor(JSFunction *fun)
|
||||
return fun->maybeNative() == Function;
|
||||
}
|
||||
|
||||
const Shape *
|
||||
LookupInterpretedFunctionPrototype(JSContext *cx, RootedVarObject funobj)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JSFunction *fun = funobj->toFunction();
|
||||
JS_ASSERT(fun->isInterpreted());
|
||||
JS_ASSERT(!fun->isFunctionPrototype());
|
||||
JS_ASSERT(!funobj->isBoundFunction());
|
||||
#endif
|
||||
|
||||
jsid id = NameToId(cx->runtime->atomState.classPrototypeAtom);
|
||||
RootedVar<const Shape*> shape(cx, funobj->nativeLookup(cx, id));
|
||||
if (!shape) {
|
||||
if (!ResolveInterpretedFunctionPrototype(cx, funobj))
|
||||
return NULL;
|
||||
id = NameToId(cx->runtime->atomState.classPrototypeAtom);
|
||||
shape = funobj->nativeLookup(cx, id);
|
||||
}
|
||||
JS_ASSERT(!shape->configurable());
|
||||
JS_ASSERT(shape->isDataDescriptor());
|
||||
JS_ASSERT(shape->hasSlot());
|
||||
return shape;
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
JSFunction *
|
||||
|
@ -107,6 +107,12 @@ MathCache::MathCache() {
|
||||
JS_ASSERT(hash(-0.0) != hash(+0.0));
|
||||
}
|
||||
|
||||
size_t
|
||||
MathCache::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf)
|
||||
{
|
||||
return mallocSizeOf(this);
|
||||
}
|
||||
|
||||
Class js::MathClass = {
|
||||
js_Math_str,
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Math),
|
||||
|
@ -74,6 +74,8 @@ class MathCache
|
||||
e.f = f;
|
||||
return (e.out = f(x));
|
||||
}
|
||||
|
||||
size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
@ -1595,6 +1595,7 @@ ObjectClassIs(JSObject &obj, ESClassValue classValue, JSContext *cx)
|
||||
case ESClass_String: return obj.isString();
|
||||
case ESClass_Boolean: return obj.isBoolean();
|
||||
case ESClass_RegExp: return obj.isRegExp();
|
||||
case ESClass_ArrayBuffer: return obj.isArrayBuffer();
|
||||
}
|
||||
JS_NOT_REACHED("bad classValue");
|
||||
return false;
|
||||
|
@ -2950,7 +2950,7 @@ ASTSerializer::function(ParseNode *pn, ASTType type, Value *dst)
|
||||
|
||||
bool isGenerator =
|
||||
#if JS_HAS_GENERATORS
|
||||
pn->pn_funbox->tcflags & TCF_FUN_IS_GENERATOR;
|
||||
pn->pn_funbox->funIsGenerator();
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
@ -1329,24 +1329,28 @@ JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
|
||||
bce->regexpList.finish(script->regexps());
|
||||
if (bce->constList.length() != 0)
|
||||
bce->constList.finish(script->consts());
|
||||
if (bce->sc->flags & TCF_STRICT_MODE_CODE)
|
||||
script->strictModeCode = true;
|
||||
script->strictModeCode = bce->sc->inStrictMode();
|
||||
if (bce->parser->compileAndGo) {
|
||||
script->compileAndGo = true;
|
||||
const StackFrame *fp = bce->parser->callerFrame;
|
||||
if (fp && fp->isFunctionFrame())
|
||||
script->savedCallerFun = true;
|
||||
}
|
||||
if (bce->sc->bindingsAccessedDynamically())
|
||||
script->bindingsAccessedDynamically = true;
|
||||
script->bindingsAccessedDynamically = bce->sc->bindingsAccessedDynamically();
|
||||
script->hasSingletons = bce->hasSingletons;
|
||||
if (bce->sc->flags & TCF_FUN_IS_GENERATOR)
|
||||
script->isGenerator = true;
|
||||
|
||||
if (bce->sc->argumentsHasLocalBinding()) {
|
||||
script->setArgumentsHasLocalBinding(bce->sc->argumentsLocalSlot());
|
||||
if (bce->sc->definitelyNeedsArgsObj())
|
||||
script->setNeedsArgsObj(true);
|
||||
if (bce->sc->inFunction) {
|
||||
if (bce->sc->funArgumentsHasLocalBinding()) {
|
||||
// This must precede the script->bindings.transfer() call below.
|
||||
script->setArgumentsHasLocalBinding(bce->sc->argumentsLocalSlot());
|
||||
if (bce->sc->funDefinitelyNeedsArgsObj())
|
||||
script->setNeedsArgsObj(true);
|
||||
} else {
|
||||
JS_ASSERT(!bce->sc->funDefinitelyNeedsArgsObj());
|
||||
}
|
||||
} else {
|
||||
JS_ASSERT(!bce->sc->funArgumentsHasLocalBinding());
|
||||
JS_ASSERT(!bce->sc->funDefinitelyNeedsArgsObj());
|
||||
}
|
||||
|
||||
if (nClosedArgs)
|
||||
@ -1360,6 +1364,9 @@ JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
|
||||
if (bce->sc->inFunction) {
|
||||
JS_ASSERT(!bce->noScriptRval);
|
||||
JS_ASSERT(!bce->needScriptGlobal);
|
||||
|
||||
script->isGenerator = bce->sc->funIsGenerator();
|
||||
|
||||
/*
|
||||
* We initialize fun->script() to be the script constructed above
|
||||
* so that the debugger has a valid fun->script().
|
||||
@ -1367,7 +1374,7 @@ JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
|
||||
fun = bce->sc->fun();
|
||||
JS_ASSERT(fun->isInterpreted());
|
||||
JS_ASSERT(!fun->script());
|
||||
if (bce->sc->flags & TCF_FUN_HEAVYWEIGHT)
|
||||
if (bce->sc->funIsHeavyweight())
|
||||
fun->flags |= JSFUN_HEAVYWEIGHT;
|
||||
|
||||
/*
|
||||
@ -1383,7 +1390,12 @@ JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
|
||||
|
||||
fun->setScript(script);
|
||||
script->globalObject = fun->getParent() ? &fun->getParent()->global() : NULL;
|
||||
|
||||
} else {
|
||||
// It'd be nice to JS_ASSERT(!bce->sc->funIsHeavyweight()) here, but
|
||||
// Parser.cpp is sloppy and sometimes applies it to non-functions.
|
||||
JS_ASSERT(!bce->sc->funIsGenerator());
|
||||
|
||||
/*
|
||||
* Initialize script->object, if necessary, so that the debugger has a
|
||||
* valid holder object.
|
||||
|
@ -529,7 +529,7 @@ struct JSScript : public js::gc::Cell
|
||||
bool savedCallerFun:1; /* can call getCallerFunction() */
|
||||
bool strictModeCode:1; /* code is in strict mode */
|
||||
bool compileAndGo:1; /* see Parser::compileAndGo */
|
||||
bool bindingsAccessedDynamically:1; /* see TCF_BINDINGS_ACCESSED_DYNAMICALLY */
|
||||
bool bindingsAccessedDynamically:1; /* see ContextFlags' field of the same name */
|
||||
bool warnedAboutTwoArgumentEval:1; /* have warned about use of
|
||||
obsolete eval(s, o) in
|
||||
this script */
|
||||
@ -589,7 +589,7 @@ struct JSScript : public js::gc::Cell
|
||||
|
||||
void setVersion(JSVersion v) { version = v; }
|
||||
|
||||
/* See TCF_ARGUMENTS_HAS_LOCAL_BINDING comment. */
|
||||
/* See ContextFlags::funArgumentsHasLocalBinding comment. */
|
||||
bool argumentsHasLocalBinding() const { return argsHasLocalBinding_; }
|
||||
jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; }
|
||||
unsigned argumentsLocalSlot() const { JS_ASSERT(argsHasLocalBinding_); return argsSlot_; }
|
||||
|
@ -785,6 +785,22 @@ ArrayBufferObject::obj_typeOf(JSContext *cx, JSObject *obj)
|
||||
return JSTYPE_OBJECT;
|
||||
}
|
||||
|
||||
/*
|
||||
* ArrayBufferViews of various sorts
|
||||
*/
|
||||
|
||||
static JSObject *
|
||||
GetProtoForClass(JSContext *cx, Class *clasp)
|
||||
{
|
||||
// Pass in the proto from this compartment
|
||||
GlobalObject *parent = GetCurrentGlobal(cx);
|
||||
Root<GlobalObject*> parentRoot(cx, &parent);
|
||||
JSObject *proto;
|
||||
if (!FindProto(cx, clasp, parentRoot, &proto))
|
||||
return NULL;
|
||||
return proto;
|
||||
}
|
||||
|
||||
/*
|
||||
* TypedArray
|
||||
*
|
||||
@ -1068,12 +1084,6 @@ template<typename NativeType>
|
||||
class TypedArrayTemplate
|
||||
: public TypedArray
|
||||
{
|
||||
template<typename ElementType>
|
||||
friend JSObject *NewTypedArrayFromArray(JSContext *cx, JSObject *other);
|
||||
|
||||
template<typename ElementType>
|
||||
friend JSObject *NewArray(JSContext *cx, uint32_t nelements);
|
||||
|
||||
public:
|
||||
typedef NativeType ThisType;
|
||||
typedef TypedArrayTemplate<NativeType> ThisTypeArray;
|
||||
@ -1440,7 +1450,8 @@ class TypedArrayTemplate
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
createTypedArray(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, uint32_t len)
|
||||
makeInstance(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, uint32_t len,
|
||||
HandleObject proto)
|
||||
{
|
||||
RootedVarObject obj(cx, NewBuiltinClassInstance(cx, protoClass()));
|
||||
if (!obj)
|
||||
@ -1451,14 +1462,19 @@ class TypedArrayTemplate
|
||||
JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT8);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Specialize the type of the object on the current scripted location,
|
||||
* and mark the type as definitely a typed array.
|
||||
*/
|
||||
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(protoClass());
|
||||
types::TypeObject *type = types::GetTypeCallerInitObject(cx, key);
|
||||
if (!type)
|
||||
return NULL;
|
||||
types::TypeObject *type;
|
||||
if (proto) {
|
||||
type = proto->getNewType(cx);
|
||||
} else {
|
||||
/*
|
||||
* Specialize the type of the object on the current scripted location,
|
||||
* and mark the type as definitely a typed array.
|
||||
*/
|
||||
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(protoClass());
|
||||
type = types::GetTypeCallerInitObject(cx, key);
|
||||
if (!type)
|
||||
return NULL;
|
||||
}
|
||||
obj->setType(type);
|
||||
|
||||
obj->setSlot(FIELD_TYPE, Int32Value(ArrayTypeID()));
|
||||
@ -1516,6 +1532,18 @@ class TypedArrayTemplate
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
fromBuffer(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JSObject *obj = fromBuffer(cx, RootedVarObject(cx, &args[0].toObject()),
|
||||
args[1].toInt32(), args[2].toInt32(), RootedVarObject(cx, &args[3].toObject()));
|
||||
if (!obj)
|
||||
return false;
|
||||
vp->setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
create(JSContext *cx, unsigned argc, Value *argv)
|
||||
{
|
||||
@ -1523,40 +1551,29 @@ class TypedArrayTemplate
|
||||
|
||||
/* () or (number) */
|
||||
uint32_t len = 0;
|
||||
if (argc == 0 || ValueIsLength(cx, argv[0], &len)) {
|
||||
RootedVarObject bufobj(cx, createBufferWithSizeAndCount(cx, len));
|
||||
if (!bufobj)
|
||||
return NULL;
|
||||
|
||||
return createTypedArray(cx, bufobj, 0, len);
|
||||
}
|
||||
if (argc == 0 || ValueIsLength(cx, argv[0], &len))
|
||||
return fromLength(cx, len);
|
||||
|
||||
/* (not an object) */
|
||||
if (!argv[0].isObject()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_TYPED_ARRAY_BAD_ARGS);
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RootedVarObject dataObj(cx, &argv[0].toObject());
|
||||
|
||||
/* (typedArray) */
|
||||
if (dataObj->isTypedArray()) {
|
||||
JSObject *otherTypedArray = getTypedArray(dataObj);
|
||||
JS_ASSERT(otherTypedArray);
|
||||
/*
|
||||
* (typedArray)
|
||||
* (type[] array)
|
||||
*
|
||||
* Otherwise create a new typed array and copy elements 0..len-1
|
||||
* properties from the object, treating it as some sort of array.
|
||||
* Note that offset and length will be ignored
|
||||
*/
|
||||
if (!UnwrapObject(dataObj)->isArrayBuffer())
|
||||
return fromArray(cx, dataObj);
|
||||
|
||||
uint32_t len = getLength(otherTypedArray);
|
||||
RootedVarObject bufobj(cx, createBufferWithSizeAndCount(cx, len));
|
||||
if (!bufobj)
|
||||
return NULL;
|
||||
|
||||
JSObject *obj = createTypedArray(cx, bufobj, 0, len);
|
||||
if (!obj || !copyFromTypedArray(cx, obj, otherTypedArray, 0))
|
||||
return NULL;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* (obj, byteOffset, length). */
|
||||
/* (ArrayBuffer, [byteOffset, [length]]) */
|
||||
int32_t byteOffset = -1;
|
||||
int32_t length = -1;
|
||||
|
||||
@ -1580,8 +1597,7 @@ class TypedArrayTemplate
|
||||
}
|
||||
}
|
||||
|
||||
/* (obj, byteOffset, length) */
|
||||
return createTypedArrayWithOffsetLength(cx, dataObj, byteOffset, length);
|
||||
return fromBuffer(cx, dataObj, byteOffset, length, RootedVarObject(cx));
|
||||
}
|
||||
|
||||
/* subarray(start[, end]) */
|
||||
@ -1693,9 +1709,61 @@ class TypedArrayTemplate
|
||||
|
||||
public:
|
||||
static JSObject *
|
||||
createTypedArrayWithBuffer(JSContext *cx, HandleObject bufobj,
|
||||
int32_t byteOffsetInt, int32_t lengthInt)
|
||||
fromBuffer(JSContext *cx, HandleObject bufobj, int32_t byteOffsetInt, int32_t lengthInt,
|
||||
HandleObject proto)
|
||||
{
|
||||
if (!ObjectClassIs(*bufobj, ESClass_ArrayBuffer, cx)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
|
||||
return NULL; // must be arrayBuffer
|
||||
}
|
||||
|
||||
JS_ASSERT(bufobj->isArrayBuffer() || bufobj->isProxy());
|
||||
if (bufobj->isProxy()) {
|
||||
/*
|
||||
* Normally, NonGenericMethodGuard handles the case of transparent
|
||||
* wrappers. However, we have a peculiar situation: we want to
|
||||
* construct the new typed array in the compartment of the buffer,
|
||||
* so that the typed array can point directly at their buffer's
|
||||
* data without crossing compartment boundaries. So we use the
|
||||
* machinery underlying NonGenericMethodGuard directly to proxy the
|
||||
* native call. We will end up with a wrapper in the origin
|
||||
* compartment for a view in the target compartment referencing the
|
||||
* ArrayBuffer in that same compartment.
|
||||
*/
|
||||
JSObject *wrapped = UnwrapObjectChecked(cx, bufobj);
|
||||
if (!wrapped)
|
||||
return NULL;
|
||||
if (wrapped->isArrayBuffer()) {
|
||||
/*
|
||||
* And for even more fun, the new view's prototype should be
|
||||
* set to the origin compartment's prototype object, not the
|
||||
* target's (specifically, the actual view in the target
|
||||
* compartment will use as its prototype a wrapper around the
|
||||
* origin compartment's view.prototype object)
|
||||
*/
|
||||
JSObject *proto = GetProtoForClass(cx, fastClass());
|
||||
if (!proto)
|
||||
return NULL;
|
||||
Value argv[] = { UndefinedValue(),
|
||||
MagicValue(JS_IS_CONSTRUCTING),
|
||||
ObjectValue(*bufobj),
|
||||
Int32Value(byteOffsetInt),
|
||||
Int32Value(lengthInt),
|
||||
ObjectValue(*proto) };
|
||||
uint32_t argc = sizeof(argv) / sizeof(argv[0]) - 2;
|
||||
|
||||
CallArgs args = CallArgsFromVp(argc, argv);
|
||||
if (!Proxy::nativeCall(cx, bufobj, &ArrayBufferClass, fromBuffer, args))
|
||||
return NULL;
|
||||
return &args.rval().toObject();
|
||||
}
|
||||
}
|
||||
|
||||
if (!bufobj->isArrayBuffer()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
|
||||
return NULL; // must be arrayBuffer
|
||||
}
|
||||
|
||||
uint32_t boffset = (byteOffsetInt == -1) ? 0 : uint32_t(byteOffsetInt);
|
||||
|
||||
ArrayBufferObject &buffer = bufobj->asArrayBuffer();
|
||||
@ -1728,11 +1796,20 @@ class TypedArrayTemplate
|
||||
return NULL; // boffset + len is too big for the arraybuffer
|
||||
}
|
||||
|
||||
return createTypedArray(cx, bufobj, boffset, len);
|
||||
return makeInstance(cx, bufobj, boffset, len, proto);
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
createTypedArrayFromArray(JSContext *cx, HandleObject other)
|
||||
fromLength(JSContext *cx, int32_t nelements)
|
||||
{
|
||||
RootedVarObject buffer(cx, createBufferWithSizeAndCount(cx, nelements));
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
return makeInstance(cx, buffer, 0, nelements, RootedVarObject(cx, NULL));
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
fromArray(JSContext *cx, HandleObject other)
|
||||
{
|
||||
uint32_t len;
|
||||
if (!js_GetLengthProperty(cx, other, &len))
|
||||
@ -1742,33 +1819,12 @@ class TypedArrayTemplate
|
||||
if (!bufobj)
|
||||
return NULL;
|
||||
|
||||
RootedVarObject obj(cx, createTypedArray(cx, bufobj, 0, len));
|
||||
RootedVarObject obj(cx, makeInstance(cx, bufobj, 0, len, RootedVarObject(cx)));
|
||||
if (!obj || !copyFromArray(cx, obj, other, len))
|
||||
return NULL;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: the offset and length arguments are ignored if an array is passed in.
|
||||
*/
|
||||
static JSObject *
|
||||
createTypedArrayWithOffsetLength(JSContext *cx, HandleObject other,
|
||||
int32_t byteOffsetInt, int32_t lengthInt)
|
||||
{
|
||||
JS_ASSERT(!other->isTypedArray());
|
||||
|
||||
if (other->isArrayBuffer()) {
|
||||
/* Handle creation from an ArrayBuffer not ArrayBuffer.prototype. */
|
||||
return createTypedArrayWithBuffer(cx, other, byteOffsetInt, lengthInt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise create a new typed array and copy len properties from
|
||||
* the object.
|
||||
*/
|
||||
return createTypedArrayFromArray(cx, other);
|
||||
}
|
||||
|
||||
static const NativeType
|
||||
getIndex(JSObject *obj, uint32_t index)
|
||||
{
|
||||
@ -1801,7 +1857,7 @@ class TypedArrayTemplate
|
||||
JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= getByteOffset(tarray));
|
||||
uint32_t byteOffset = getByteOffset(tarray) + begin * sizeof(NativeType);
|
||||
|
||||
return createTypedArray(cx, bufobj, byteOffset, length);
|
||||
return makeInstance(cx, bufobj, byteOffset, length, RootedVarObject(cx));
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -2172,14 +2228,8 @@ TypedArrayTemplate<double>::copyIndexToValue(JSContext *cx, JSObject *tarray, ui
|
||||
}
|
||||
|
||||
JSBool
|
||||
DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
|
||||
DataViewObject::construct(JSContext *cx, JSObject *bufobj, const CallArgs &args, JSObject *proto)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
JSObject *bufobj;
|
||||
if (!GetFirstArgumentAsObject(cx, args.length(), args.base(), "DataView constructor", &bufobj))
|
||||
return false;
|
||||
|
||||
if (!bufobj->isArrayBuffer()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_EXPECTED_TYPE,
|
||||
"DataView", "ArrayBuffer", bufobj->getClass()->name);
|
||||
@ -2227,7 +2277,7 @@ DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject *obj = DataViewObject::create(cx, byteOffset, byteLength, buffer);
|
||||
JSObject *obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto);
|
||||
if (!obj)
|
||||
return false;
|
||||
args.rval().setObject(*obj);
|
||||
@ -2264,6 +2314,51 @@ DataViewObject::prop_getByteOffset(JSContext *cx, JSObject *obj, jsid id, Value
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
DataViewObject::constructWithProto(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
// Pop the proto argument off the end
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JSObject &proto = args[args.length() - 1].toObject();
|
||||
|
||||
// And now mimic class_constructor for everything else, but pass in the proto
|
||||
args = CallArgsFromVp(argc - 1, vp);
|
||||
JSObject *bufobj;
|
||||
if (!GetFirstArgumentAsObject(cx, args.length(), args.base(), "DataView constructor", &bufobj))
|
||||
return false;
|
||||
return construct(cx, bufobj, args, &proto);
|
||||
}
|
||||
|
||||
JSBool
|
||||
DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
JSObject *bufobj;
|
||||
if (!GetFirstArgumentAsObject(cx, args.length(), args.base(), "DataView constructor", &bufobj))
|
||||
return false;
|
||||
|
||||
if (bufobj->isWrapper() && UnwrapObject(bufobj)->isArrayBuffer()) {
|
||||
JSObject *proto = GetProtoForClass(cx, &DataViewClass);
|
||||
if (!proto)
|
||||
return false;
|
||||
|
||||
Vector<Value, 6> argv(cx);
|
||||
argv.resize(argc + 2 + 1);
|
||||
memcpy(argv.begin(), args.base(), sizeof(Value) * (argc + 2));
|
||||
argv[argc + 2].setObject(*proto);
|
||||
argv[0].setUndefined(); // We want to use a different callee (avoid an assertion)
|
||||
|
||||
CallArgs proxyArgs = CallArgsFromVp(argc + 1, argv.begin());
|
||||
if (!Proxy::nativeCall(cx, bufobj, &DataViewClass, constructWithProto, proxyArgs))
|
||||
return false;
|
||||
args.rval() = proxyArgs.rval();
|
||||
return true;
|
||||
}
|
||||
|
||||
return construct(cx, bufobj, args, NULL);
|
||||
}
|
||||
|
||||
JSBool
|
||||
DataViewObject::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||
{
|
||||
@ -2831,48 +2926,26 @@ JSFunctionSpec _typedArray::jsfuncs[] = { \
|
||||
JS_FS_END \
|
||||
}
|
||||
|
||||
template<typename ElementType>
|
||||
static inline JSObject *
|
||||
NewArray(JSContext *cx, uint32_t nelements)
|
||||
{
|
||||
RootedVarObject buffer(cx, TypedArrayTemplate<ElementType>::createBufferWithSizeAndCount(cx, nelements));
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
return TypedArrayTemplate<ElementType>::createTypedArray(cx, buffer, 0, nelements);
|
||||
}
|
||||
|
||||
template<typename ElementType>
|
||||
static inline JSObject *
|
||||
NewArrayWithBuffer(JSContext *cx, HandleObject arrayBuffer, int32_t byteoffset, int32_t intLength)
|
||||
{
|
||||
if (!arrayBuffer->isArrayBuffer()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
|
||||
return NULL; // must be arrayBuffer
|
||||
}
|
||||
|
||||
return TypedArrayTemplate<ElementType>::createTypedArrayWithBuffer(cx, arrayBuffer,
|
||||
byteoffset, intLength);
|
||||
}
|
||||
|
||||
#define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType) \
|
||||
JS_FRIEND_API(JSObject *) JS_New ## Name ## Array(JSContext *cx, uint32_t nelements) \
|
||||
{ \
|
||||
MOZ_ASSERT(nelements <= INT32_MAX); \
|
||||
return NewArray<NativeType>(cx, nelements); \
|
||||
return TypedArrayTemplate<NativeType>::fromLength(cx, nelements); \
|
||||
} \
|
||||
JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayFromArray(JSContext *cx, JSObject *other) \
|
||||
{ \
|
||||
return TypedArrayTemplate<NativeType>::createTypedArrayFromArray(cx, RootedVarObject(cx, other)); \
|
||||
return TypedArrayTemplate<NativeType>::fromArray(cx, RootedVarObject(cx, other)); \
|
||||
} \
|
||||
JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayWithBuffer(JSContext *cx, \
|
||||
JSObject *arrayBuffer, uint32_t byteoffset, int32_t length) \
|
||||
{ \
|
||||
MOZ_ASSERT(byteoffset <= INT32_MAX); \
|
||||
return NewArrayWithBuffer<NativeType>(cx, RootedVarObject(cx, arrayBuffer), byteoffset, length); \
|
||||
return TypedArrayTemplate<NativeType>::fromBuffer(cx, RootedVarObject(cx, arrayBuffer), byteoffset, length, RootedVarObject(cx)); \
|
||||
} \
|
||||
JS_FRIEND_API(JSBool) JS_Is ## Name ## Array(JSObject *obj, JSContext *cx) \
|
||||
{ \
|
||||
obj = UnwrapObject(obj); \
|
||||
if (!(obj = UnwrapObjectChecked(cx, obj))) \
|
||||
return false; \
|
||||
Class *clasp = obj->getClass(); \
|
||||
return (clasp == &TypedArray::classes[TypedArrayTemplate<NativeType>::ArrayTypeID()]); \
|
||||
}
|
||||
@ -3192,38 +3265,66 @@ js_InitTypedArrayClasses(JSContext *cx, JSObject *obj)
|
||||
|
||||
/* JS Friend API */
|
||||
|
||||
// The typed array friend API defines a number of accessor functions that want
|
||||
// to unwrap an argument, but in certain rare cases may not have a cx available
|
||||
// and so pass in NULL instead. Use UnwrapObjectChecked when possible.
|
||||
static JSObject *
|
||||
CheckedUnwrap(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
if (!cx)
|
||||
return UnwrapObject(obj);
|
||||
MOZ_ASSERT(!cx->isExceptionPending());
|
||||
obj = UnwrapObjectChecked(cx, obj);
|
||||
MOZ_ASSERT(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
JS_IsArrayBufferObject(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
MOZ_ASSERT(!cx->isExceptionPending());
|
||||
if (!(obj = UnwrapObjectChecked(cx, obj))) {
|
||||
cx->clearPendingException();
|
||||
return false;
|
||||
}
|
||||
return obj->isArrayBuffer();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
JS_IsTypedArrayObject(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
MOZ_ASSERT(!cx->isExceptionPending());
|
||||
if (!(obj = UnwrapObjectChecked(cx, obj))) {
|
||||
cx->clearPendingException();
|
||||
return false;
|
||||
}
|
||||
return obj->isTypedArray();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
JS_IsArrayBufferViewObject(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
MOZ_ASSERT(!cx->isExceptionPending());
|
||||
if (!(obj = UnwrapObjectChecked(cx, obj))) {
|
||||
cx->clearPendingException();
|
||||
return false;
|
||||
}
|
||||
return obj->isTypedArray() || obj->isDataView();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(uint32_t)
|
||||
JS_GetArrayBufferByteLength(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return 0;
|
||||
return obj->asArrayBuffer().byteLength();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(uint8_t *)
|
||||
JS_GetArrayBufferData(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return NULL;
|
||||
return obj->asArrayBuffer().dataPointer();
|
||||
}
|
||||
|
||||
@ -3237,7 +3338,8 @@ JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
|
||||
JS_FRIEND_API(uint32_t)
|
||||
JS_GetTypedArrayLength(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return 0;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
return obj->getSlot(TypedArray::FIELD_LENGTH).toInt32();
|
||||
}
|
||||
@ -3245,7 +3347,8 @@ JS_GetTypedArrayLength(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(uint32_t)
|
||||
JS_GetTypedArrayByteOffset(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return 0;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
return obj->getSlot(TypedArray::FIELD_BYTEOFFSET).toInt32();
|
||||
}
|
||||
@ -3253,7 +3356,8 @@ JS_GetTypedArrayByteOffset(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(uint32_t)
|
||||
JS_GetTypedArrayByteLength(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return 0;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
return obj->getSlot(TypedArray::FIELD_BYTELENGTH).toInt32();
|
||||
}
|
||||
@ -3261,7 +3365,8 @@ JS_GetTypedArrayByteLength(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(JSArrayBufferViewType)
|
||||
JS_GetTypedArrayType(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return ArrayBufferView::TYPE_MAX;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
return static_cast<JSArrayBufferViewType>(obj->getSlot(TypedArray::FIELD_TYPE).toInt32());
|
||||
}
|
||||
@ -3269,7 +3374,8 @@ JS_GetTypedArrayType(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(int8_t *)
|
||||
JS_GetInt8ArrayData(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_INT8);
|
||||
return static_cast<int8_t *>(TypedArray::getDataOffset(obj));
|
||||
@ -3278,7 +3384,8 @@ JS_GetInt8ArrayData(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(uint8_t *)
|
||||
JS_GetUint8ArrayData(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_UINT8);
|
||||
return static_cast<uint8_t *>(TypedArray::getDataOffset(obj));
|
||||
@ -3287,7 +3394,8 @@ JS_GetUint8ArrayData(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(uint8_t *)
|
||||
JS_GetUint8ClampedArrayData(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_UINT8_CLAMPED);
|
||||
return static_cast<uint8_t *>(TypedArray::getDataOffset(obj));
|
||||
@ -3296,7 +3404,8 @@ JS_GetUint8ClampedArrayData(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(int16_t *)
|
||||
JS_GetInt16ArrayData(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_INT16);
|
||||
return static_cast<int16_t *>(TypedArray::getDataOffset(obj));
|
||||
@ -3305,7 +3414,8 @@ JS_GetInt16ArrayData(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(uint16_t *)
|
||||
JS_GetUint16ArrayData(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_UINT16);
|
||||
return static_cast<uint16_t *>(TypedArray::getDataOffset(obj));
|
||||
@ -3314,7 +3424,8 @@ JS_GetUint16ArrayData(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(int32_t *)
|
||||
JS_GetInt32ArrayData(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_INT32);
|
||||
return static_cast<int32_t *>(TypedArray::getDataOffset(obj));
|
||||
@ -3323,7 +3434,8 @@ JS_GetInt32ArrayData(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(uint32_t *)
|
||||
JS_GetUint32ArrayData(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_UINT32);
|
||||
return static_cast<uint32_t *>(TypedArray::getDataOffset(obj));
|
||||
@ -3332,7 +3444,8 @@ JS_GetUint32ArrayData(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(float *)
|
||||
JS_GetFloat32ArrayData(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_FLOAT32);
|
||||
return static_cast<float *>(TypedArray::getDataOffset(obj));
|
||||
@ -3341,7 +3454,8 @@ JS_GetFloat32ArrayData(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(double *)
|
||||
JS_GetFloat64ArrayData(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isTypedArray());
|
||||
JS_ASSERT(obj->getSlot(TypedArray::FIELD_TYPE).toInt32() == ArrayBufferView::TYPE_FLOAT64);
|
||||
return static_cast<double *>(TypedArray::getDataOffset(obj));
|
||||
@ -3350,7 +3464,8 @@ JS_GetFloat64ArrayData(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(JSBool)
|
||||
JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return false;
|
||||
*isDataView = obj->isDataView();
|
||||
return true;
|
||||
}
|
||||
@ -3358,14 +3473,16 @@ JS_IsDataViewObject(JSContext *cx, JSObject *obj, JSBool *isDataView)
|
||||
JS_FRIEND_API(uint32_t)
|
||||
JS_GetDataViewByteOffset(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return 0;
|
||||
return obj->asDataView().byteOffset();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void *)
|
||||
JS_GetDataViewData(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isDataView());
|
||||
return obj->asDataView().dataPointer();
|
||||
}
|
||||
@ -3373,7 +3490,8 @@ JS_GetDataViewData(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(uint32_t)
|
||||
JS_GetDataViewByteLength(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return 0;
|
||||
JS_ASSERT(obj->isDataView());
|
||||
return obj->asDataView().byteLength();
|
||||
}
|
||||
@ -3381,7 +3499,8 @@ JS_GetDataViewByteLength(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(void *)
|
||||
JS_GetArrayBufferViewData(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isTypedArray() || obj->isDataView());
|
||||
return obj->isDataView() ? obj->asDataView().dataPointer() : TypedArray::getDataOffset(obj);
|
||||
}
|
||||
@ -3389,7 +3508,8 @@ JS_GetArrayBufferViewData(JSObject *obj, JSContext *cx)
|
||||
JS_FRIEND_API(uint32_t)
|
||||
JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *cx)
|
||||
{
|
||||
obj = UnwrapObject(obj);
|
||||
if (!(obj = CheckedUnwrap(cx, obj)))
|
||||
return 0;
|
||||
JS_ASSERT(obj->isTypedArray() || obj->isDataView());
|
||||
return obj->isDataView() ? obj->asDataView().byteLength() : TypedArray::getByteLength(obj);
|
||||
}
|
||||
|
@ -330,9 +330,12 @@ class DataViewObject : public JSObject
|
||||
static JSBool prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp);
|
||||
|
||||
static JSBool class_constructor(JSContext *cx, unsigned argc, Value *vp);
|
||||
static JSBool constructWithProto(JSContext *cx, unsigned argc, Value *vp);
|
||||
static JSBool construct(JSContext *cx, JSObject *bufobj, const CallArgs &args, JSObject *proto);
|
||||
|
||||
static inline DataViewObject *
|
||||
create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, Handle<ArrayBufferObject*> arrayBuffer);
|
||||
create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
|
||||
Handle<ArrayBufferObject*> arrayBuffer, JSObject *proto);
|
||||
|
||||
static JSBool fun_getInt8(JSContext *cx, unsigned argc, Value *vp);
|
||||
static JSBool fun_getUint8(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
@ -129,7 +129,7 @@ TypedArray::getDataOffset(JSObject *obj) {
|
||||
|
||||
inline DataViewObject *
|
||||
DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
|
||||
Handle<ArrayBufferObject*> arrayBuffer)
|
||||
Handle<ArrayBufferObject*> arrayBuffer, JSObject *proto)
|
||||
{
|
||||
JS_ASSERT(byteOffset <= INT32_MAX);
|
||||
JS_ASSERT(byteLength <= INT32_MAX);
|
||||
@ -138,6 +138,21 @@ DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
types::TypeObject *type;
|
||||
if (proto) {
|
||||
type = proto->getNewType(cx);
|
||||
} else {
|
||||
/*
|
||||
* Specialize the type of the object on the current scripted location,
|
||||
* and mark the type as definitely a data view
|
||||
*/
|
||||
JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(&DataViewClass);
|
||||
type = types::GetTypeCallerInitObject(cx, key);
|
||||
if (!type)
|
||||
return NULL;
|
||||
}
|
||||
obj->setType(type);
|
||||
|
||||
JS_ASSERT(arrayBuffer->isArrayBuffer());
|
||||
|
||||
DataViewObject &dvobj = obj->asDataView();
|
||||
|
@ -793,7 +793,7 @@ CrossCompartmentWrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *cla
|
||||
JS_ASSERT_IF(!srcArgs.calleev().isUndefined(),
|
||||
srcArgs.callee().toFunction()->native() == native ||
|
||||
srcArgs.callee().toFunction()->native() == js_generic_native_method_dispatcher);
|
||||
JS_ASSERT(&srcArgs.thisv().toObject() == wrapper);
|
||||
JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) || &srcArgs.thisv().toObject() == wrapper);
|
||||
JS_ASSERT(!UnwrapObject(wrapper)->isCrossCompartmentWrapper());
|
||||
|
||||
JSObject *wrapped = wrappedObject(wrapper);
|
||||
|
@ -1302,6 +1302,13 @@ JITScript::destroyChunk(FreeOp *fop, unsigned chunkIndex, bool resetUses)
|
||||
ChunkDescriptor &desc = chunkDescriptor(chunkIndex);
|
||||
|
||||
if (desc.chunk) {
|
||||
/*
|
||||
* Write barrier: Before we destroy the chunk, trace through the objects
|
||||
* it holds.
|
||||
*/
|
||||
if (script->compartment()->needsBarrier())
|
||||
desc.chunk->trace(script->compartment()->barrierTracer());
|
||||
|
||||
Probes::discardMJITCode(fop, this, desc.chunk, desc.chunk->code.m_code.executableAddress());
|
||||
fop->delete_(desc.chunk);
|
||||
desc.chunk = NULL;
|
||||
@ -1532,8 +1539,10 @@ void
|
||||
JITChunk::trace(JSTracer *trc)
|
||||
{
|
||||
JSObject **rootedTemplates_ = rootedTemplates();
|
||||
for (size_t i = 0; i < nRootedTemplates; i++)
|
||||
for (size_t i = 0; i < nRootedTemplates; i++) {
|
||||
/* We use a manual write barrier in destroyChunk. */
|
||||
MarkObjectUnbarriered(trc, &rootedTemplates_[i], "jitchunk_template");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1626,6 +1626,11 @@ function test() {
|
||||
buffer = Object.create(buffer1);
|
||||
checkThrow(function () new DataView(buffer), TypeError);
|
||||
|
||||
// view of proxy for buffer
|
||||
av = new DataView(alien_buffer);
|
||||
assertEq(av.getUint8(4), 100);
|
||||
assertEq(Object.getPrototypeOf(av), DataView.prototype);
|
||||
|
||||
reportCompare(0, 0, 'done.');
|
||||
exitFunc ('test');
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
/* -*- Mode: js2; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
@ -424,6 +425,33 @@ function test()
|
||||
// use a loop to invoke the TM
|
||||
for (var i = 0; i < b.length; i++)
|
||||
check(function() b[90] == 5)
|
||||
|
||||
// Protos and proxies, oh my!
|
||||
var alien = newGlobal('new-compartment');
|
||||
|
||||
var alien_view = alien.eval('view = new Uint8Array(7)');
|
||||
var alien_buffer = alien.eval('buffer = view.buffer');
|
||||
|
||||
// when creating a view of a buffer in a different compartment, the view
|
||||
// itself should be created in the other compartment and wrapped for use in
|
||||
// this compartment. (There should never be a compartment boundary between
|
||||
// an ArrayBufferView and its ArrayBuffer.)
|
||||
var view = new Int8Array(alien_buffer);
|
||||
|
||||
// First make sure they're looking at the same data
|
||||
alien_view[3] = 77;
|
||||
check(function () view[3] == 77);
|
||||
|
||||
// Now check that the proxy setup is as expected
|
||||
check(function () isProxy(alien_view));
|
||||
check(function () isProxy(alien_buffer));
|
||||
check(function () isProxy(view)); // the real test
|
||||
|
||||
// typed array protos should be equal
|
||||
simple = new Int8Array(12);
|
||||
check(function () Object.getPrototypeOf(view) == Object.getPrototypeOf(simple));
|
||||
check(function () Object.getPrototypeOf(view) == Int8Array.prototype);
|
||||
|
||||
print ("done");
|
||||
|
||||
reportCompare(0, TestFailCount, "typed array tests");
|
||||
|
@ -1481,12 +1481,17 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats,
|
||||
"Memory allocated for JSScript bytecode and various "
|
||||
"variable-length tables.");
|
||||
|
||||
REPORT_BYTES0(MakePath(pathPrefix, cStats, "mjit/data"),
|
||||
REPORT_BYTES0(MakePath(pathPrefix, cStats, "mjit-data"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.mjitData,
|
||||
"Memory used by the method JIT for the compartment's "
|
||||
"compilation data: JITScripts, native maps, and inline "
|
||||
"cache structs.");
|
||||
|
||||
REPORT_BYTES0(MakePath(pathPrefix, cStats, "cross-compartment-wrappers"),
|
||||
nsIMemoryReporter::KIND_HEAP, cStats.crossCompartmentWrappers,
|
||||
"Memory used by the compartment's cross-compartment "
|
||||
"wrappers.");
|
||||
|
||||
REPORT_BYTES0(MakePath(pathPrefix, cStats, "type-inference/script-main"),
|
||||
nsIMemoryReporter::KIND_HEAP,
|
||||
cStats.typeInferenceSizes.scripts,
|
||||
@ -1533,52 +1538,66 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
|
||||
}
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/runtime-object"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtimeObject,
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.object,
|
||||
"Memory used by the JSRuntime object.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/atoms-table"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtimeAtomsTable,
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.atomsTable,
|
||||
"Memory used by the atoms table.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/contexts"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtimeContexts,
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.contexts,
|
||||
"Memory used by JSContext objects and certain structures "
|
||||
"hanging off them.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/normal"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtimeNormal,
|
||||
"Memory used by a JSRuntime, excluding memory that is "
|
||||
"reported by other reporters under 'explicit/js/runtime/'.");
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/dtoa"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.dtoa,
|
||||
"Memory used by DtoaState, which is used for converting "
|
||||
"strings to numbers and vice versa.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/temporary"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtimeTemporary,
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.temporary,
|
||||
"Memory held transiently in JSRuntime and used during "
|
||||
"compilation. It mostly holds parse nodes.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/mjit-code"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtimeMjitCode,
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.mjitCode,
|
||||
"Memory used by the method JIT to hold the runtime's "
|
||||
"generated code.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/regexp-code"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtimeRegexpCode,
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.regexpCode,
|
||||
"Memory used by the regexp JIT to hold generated code.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/unused-code-memory"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtimeUnusedCodeMemory,
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.unusedCodeMemory,
|
||||
"Memory allocated by the method and/or regexp JIT to hold the "
|
||||
"runtime's code, but which is currently unused.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/stack-committed"),
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtimeStackCommitted,
|
||||
nsIMemoryReporter::KIND_NONHEAP, rtStats.runtime.stackCommitted,
|
||||
"Memory used for the JS call stack. This is the committed "
|
||||
"portion of the stack; the uncommitted portion is not "
|
||||
"measured because it hardly costs anything.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/gc-marker"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtimeGCMarker,
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.gcMarker,
|
||||
"Memory used for the GC mark stack and gray roots.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/math-cache"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.mathCache,
|
||||
"Memory used for the math cache.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/script-filenames"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.scriptFilenames,
|
||||
"Memory used for the table holding script filenames.");
|
||||
|
||||
REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("runtime/compartment-objects"),
|
||||
nsIMemoryReporter::KIND_HEAP, rtStats.runtime.compartmentObjects,
|
||||
"Memory used for JSCompartment objects. These are fairly "
|
||||
"small and all the same size, so they're not worth reporting "
|
||||
"on a per-compartment basis.");
|
||||
|
||||
REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"),
|
||||
rtStats.gcHeapChunkDirtyUnused,
|
||||
"Memory on the garbage-collected JavaScript heap, within "
|
||||
|
@ -16,6 +16,7 @@ dictionaries = [
|
||||
[ 'SettingsEventInit', 'nsIDOMSettingsManager.idl' ],
|
||||
[ 'GeoPositionOptions', 'nsIDOMGeoGeolocation.idl' ],
|
||||
[ 'DeviceProximityEventInit', 'nsIDOMDeviceProximityEvent.idl' ],
|
||||
[ 'UserProximityEventInit', 'nsIDOMUserProximityEvent.idl' ],
|
||||
[ 'DeviceLightEventInit', 'nsIDOMDeviceLightEvent.idl' ]
|
||||
]
|
||||
|
||||
|
@ -146,6 +146,10 @@ def print_header_file(fd, conf):
|
||||
"#include \"nsString.h\"\n"
|
||||
"#include \"nsCOMPtr.h\"\n\n")
|
||||
|
||||
# win32 namespace issues
|
||||
fd.write("#undef near\n"
|
||||
"\n\n")
|
||||
|
||||
forwards = []
|
||||
attrnames = []
|
||||
for d in conf.dictionaries:
|
||||
|
@ -1162,7 +1162,11 @@ ContainerState::ThebesLayerData::UpdateCommonClipCount(
|
||||
already_AddRefed<ImageContainer>
|
||||
ContainerState::ThebesLayerData::CanOptimizeImageLayer()
|
||||
{
|
||||
#ifdef MOZ_ENABLE_MASK_LAYERS
|
||||
if (!mImage) {
|
||||
#else
|
||||
if (!mImage || !mItemClip.mRoundedClipRects.IsEmpty()) {
|
||||
#endif
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
@ -1192,6 +1196,10 @@ ContainerState::PopThebesLayerData()
|
||||
gfx3DMatrix transform = imageLayer->GetTransform()*
|
||||
gfx3DMatrix::ScalingMatrix(mParameters.mXScale, mParameters.mYScale, 1.0f);
|
||||
imageLayer->SetTransform(transform);
|
||||
#ifndef MOZ_ENABLE_MASK_LAYERS
|
||||
NS_ASSERTION(data->mItemClip.mRoundedClipRects.IsEmpty(),
|
||||
"How did we get rounded clip rects here?");
|
||||
#endif
|
||||
if (data->mItemClip.mHaveClipRect) {
|
||||
nsIntRect clip = ScaleToNearestPixels(data->mItemClip.mClipRect);
|
||||
imageLayer->IntersectClipRect(clip);
|
||||
@ -1273,6 +1281,7 @@ ContainerState::PopThebesLayerData()
|
||||
}
|
||||
userData->mForcedBackgroundColor = backgroundColor;
|
||||
|
||||
#ifdef MOZ_ENABLE_MASK_LAYERS
|
||||
// use a mask layer for rounded rect clipping
|
||||
PRInt32 commonClipCount = data->mCommonClipCount;
|
||||
NS_ASSERTION(commonClipCount >= 0, "Inconsistent clip count.");
|
||||
@ -1284,6 +1293,7 @@ ContainerState::PopThebesLayerData()
|
||||
} else {
|
||||
// mask layer for image and color layers
|
||||
SetupMaskLayer(layer, data->mItemClip);
|
||||
#endif
|
||||
}
|
||||
PRUint32 flags;
|
||||
if (isOpaque && !data->mForceTransparentSurface) {
|
||||
@ -1646,7 +1656,13 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
|
||||
// Assign the item to a layer
|
||||
if (layerState == LAYER_ACTIVE_FORCE ||
|
||||
layerState == LAYER_ACTIVE_EMPTY ||
|
||||
#ifdef MOZ_ENABLE_MASK_LAYERS
|
||||
layerState == LAYER_ACTIVE) {
|
||||
#else
|
||||
(layerState == LAYER_ACTIVE &&
|
||||
(aClip.mRoundedClipRects.IsEmpty() ||
|
||||
!aClip.IsRectClippedByRoundedCorner(item->GetVisibleRect())))) {
|
||||
#endif
|
||||
|
||||
// LAYER_ACTIVE_EMPTY means the layer is created just for its metadata.
|
||||
// We should never see an empty layer with any visible content!
|
||||
@ -1707,11 +1723,13 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
|
||||
}
|
||||
RestrictVisibleRegionForLayer(ownLayer, itemVisibleRect);
|
||||
|
||||
#ifdef MOZ_ENABLE_MASK_LAYERS
|
||||
// rounded rectangle clipping using mask layers
|
||||
// (must be done after visible rect is set on layer)
|
||||
if (aClip.IsRectClippedByRoundedCorner(itemContent)) {
|
||||
SetupMaskLayer(ownLayer, aClip);
|
||||
}
|
||||
#endif
|
||||
|
||||
ContainerLayer* oldContainer = ownLayer->GetParent();
|
||||
if (oldContainer && oldContainer != mContainerLayer) {
|
||||
@ -2802,6 +2820,7 @@ void
|
||||
ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aClip,
|
||||
PRUint32 aRoundedRectClipCount)
|
||||
{
|
||||
#ifdef MOZ_ENABLE_MASK_LAYERS
|
||||
// don't build an unnecessary mask
|
||||
if (aClip.mRoundedClipRects.IsEmpty() ||
|
||||
aRoundedRectClipCount <= 0) {
|
||||
@ -2890,6 +2909,7 @@ ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aCl
|
||||
userData->mBounds = aLayer->GetEffectiveVisibleRegion().GetBounds();
|
||||
|
||||
aLayer->SetMaskLayer(maskLayer);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6771,26 +6771,6 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
|
||||
nsRect& o = aOverflowAreas.Overflow(otype);
|
||||
o.UnionRectEdges(o, bounds);
|
||||
}
|
||||
|
||||
if (!nsLayoutUtils::IsPopup(this)) {
|
||||
// Include margin in scrollable overflow.
|
||||
// XXX In theory this should consider margin collapsing
|
||||
nsRect marginBounds(bounds);
|
||||
nsMargin margin = GetUsedMargin();
|
||||
|
||||
// Bug 724352 - vertical scrollable overflow only matters for
|
||||
// scroll frames which are block margin roots and has already
|
||||
// accumulated child vertical margins during reflow. We need
|
||||
// to revisit this when using UpdateOverflow for non-transform
|
||||
// style changes (bug 719177).
|
||||
margin.top = 0;
|
||||
margin.bottom = 0;
|
||||
|
||||
ApplySkipSides(margin);
|
||||
marginBounds.SaturatingInflate(margin);
|
||||
nsRect& so = aOverflowAreas.ScrollableOverflow();
|
||||
so.SaturatingUnionRectEdges(so, marginBounds);
|
||||
}
|
||||
}
|
||||
|
||||
// Note that NS_STYLE_OVERFLOW_CLIP doesn't clip the frame background,
|
||||
|
@ -1847,6 +1847,12 @@ CanScrollWithBlitting(nsIFrame* aFrame)
|
||||
f->IsFrameOfType(nsIFrame::eSVG)) {
|
||||
return false;
|
||||
}
|
||||
#ifndef MOZ_ENABLE_MASK_LAYERS
|
||||
nsIScrollableFrame* sf = do_QueryFrame(f);
|
||||
if ((sf || f->IsFrameOfType(nsIFrame::eReplaced)) &&
|
||||
nsLayoutUtils::HasNonZeroCorner(f->GetStyleBorder()->mBorderRadius))
|
||||
return false;
|
||||
#endif
|
||||
if (nsLayoutUtils::IsPopup(f))
|
||||
break;
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<hbox style="-moz-appearance: textfield-multiline; width: 200px; height: 200px;"/>
|
||||
<html:div style="position:fixed;top:0;left:0;width:100%;height:100%;"></html:div>
|
||||
|
||||
</window>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<html:textarea xmlns:html="http://www.w3.org/1999/xhtml" style="width: 200px; height: 200px; margin: 0; resize: none;"/>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<html:textarea style="width: 200px; height: 200px; margin: 0; resize: none;"/>
|
||||
<html:div style="position:fixed;top:0;left:0;width:100%;height:100%;"></html:div>
|
||||
</window>
|
||||
|
@ -11,14 +11,14 @@
|
||||
x1,x2,x3,x4,x5,x6 { display:block; }
|
||||
x2 { overflow:auto; width:100px; background:grey; }
|
||||
x4 { width: 70px; }
|
||||
x3 { width: 70px; padding: 0 20px; }
|
||||
x3 { width: 70px; padding-left: 20px; padding-right: 10px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<x1><x2 style="height:50px;">
|
||||
<x3><x4 style="height:20px; background:lime; "></x4></x3>
|
||||
</x2>
|
||||
</x2></x1>
|
||||
|
||||
|
||||
</body>
|
||||
|
@ -17,7 +17,7 @@ x4 { width: 70px; margin: 0 20px; }
|
||||
|
||||
<x1><x2 style="height:50px;">
|
||||
<x4 style="height:20px; background:lime; "></x4>
|
||||
</x2>
|
||||
</x2></x1>
|
||||
|
||||
|
||||
</body>
|
||||
|
@ -48,6 +48,11 @@ html,body {
|
||||
}
|
||||
span { background-color:white; position: relative; }
|
||||
ml3 {position: absolute; left:29ch; }
|
||||
|
||||
#block-hover {
|
||||
position:fixed;
|
||||
top:0;left:0;width:100%;height:100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -70,5 +75,6 @@ ml3 {position: absolute; left:29ch; }
|
||||
<div class="bacon scroll4"><span>6Beef hamburger bacon tri-ti JOWLBILTONG tail ribeye ham</span></div><br>
|
||||
</div>
|
||||
|
||||
<div id="block-hover"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -53,6 +53,10 @@ html,body {
|
||||
}
|
||||
span { background-color:white; }
|
||||
|
||||
#block-hover {
|
||||
position:fixed;
|
||||
top:0;left:0;width:100%;height:100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="document.getElementById('rtl_auto').scrollLeft=999999; document.documentElement.removeAttribute('class')">
|
||||
@ -75,5 +79,6 @@ span { background-color:white; }
|
||||
<div class="bacon scroll4"><span>6Beef hamburger bacon tri-tipJOWLBILTONG tail ribeye ham</span></div><br>
|
||||
</div>
|
||||
|
||||
<div id="block-hover"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
23
layout/reftests/xul/accesskey-ref.xul
Normal file
23
layout/reftests/xul/accesskey-ref.xul
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="data:text/css,u { display: inline; text-decoration: underline; }" type="text/css"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<hbox align="start">
|
||||
<label control="b1"><u>L</u>abel1</label><textbox id="b1" size="2"/>
|
||||
<label control="b2">Labe<u>l</u>2</label><textbox id="b2" size="2"/>
|
||||
<label control="b3">La<u>b</u>el3</label><textbox id="b3" size="2"/>
|
||||
<label control="b4">Label4 (<u>X</u>)</label><textbox id="b4" size="2"/>
|
||||
<label control="b5" maxwidth="50">Quite a <u>l</u>ong label. Hopefully it will wrap</label><textbox id="b5" label="Button5" size="2"/>
|
||||
</hbox>
|
||||
|
||||
<hbox id="d" align="start">
|
||||
<label control="d1"><u>L</u>abel1</label><textbox id="d1" size="2"/>
|
||||
<label control="d2">Labe<u>l</u>2</label><textbox id="d2" size="2"/>
|
||||
<label control="d3">La<u>b</u>el3</label><textbox id="d3" size="2"/>
|
||||
<label control="d4">Label4 (<u>X</u>)</label><textbox id="d4" size="2"/>
|
||||
<label control="d5" maxwidth="50">Quite a <u>l</u>ong label. Hopefully it will wrap</label><textbox id="d5" size="2"/>
|
||||
</hbox>
|
||||
|
||||
</window>
|
43
layout/reftests/xul/accesskey.xul
Normal file
43
layout/reftests/xul/accesskey.xul
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
|
||||
<!-- This test checks that access keys are rendered properly, both when set with the
|
||||
accesskey attribute in the markup and updated via script later.
|
||||
-->
|
||||
<window class="reftest-wait" onload="changeKeys()"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script>
|
||||
<![CDATA[
|
||||
|
||||
var keys = ['L', 'l', 'b', 'x', 'l'];
|
||||
|
||||
function changeKeys()
|
||||
{
|
||||
var box = document.getElementById('d');
|
||||
for (var b = 0; b < 5; b++)
|
||||
box.childNodes[b * 2].accessKey = keys[b];
|
||||
|
||||
document.documentElement.className='';
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<hbox align="start">
|
||||
<label control="b1">Label1</label><textbox id="b1" accesskey="L" size="2"/>
|
||||
<label control="b2">Label2</label><textbox id="b2" accesskey="l" size="2"/>
|
||||
<label control="b3">Label3</label><textbox id="b3" accesskey="b" size="2"/>
|
||||
<label control="b4">Label4</label><textbox id="b4" accesskey="x" size="2"/>
|
||||
<label control="b5" maxwidth="50">Quite a long label. Hopefully it will wrap</label><textbox id="b5" accesskey="l" size="2"/>
|
||||
</hbox>
|
||||
|
||||
<hbox id="d" align="start">
|
||||
<label control="d1">Label1</label><textbox id="d1" accesskey="z" size="2"/>
|
||||
<label control="d2">Label2</label><textbox id="d2" size="2"/>
|
||||
<label control="d3">Label3</label><textbox id="d3" accesskey="t" size="2"/>
|
||||
<label control="d4">Label4</label><textbox id="d4" accesskey="b" size="2"/>
|
||||
<label control="d5" maxwidth="50">Quite a long label. Hopefully it will wrap</label><textbox id="d5" accesskey="l" size="2"/>
|
||||
</hbox>
|
||||
|
||||
</window>
|
@ -3,3 +3,5 @@
|
||||
random-if(Android) == menulist-shrinkwrap-1.xul menulist-shrinkwrap-1-ref.xul
|
||||
random-if(Android) fails-if(winWidget) == menulist-shrinkwrap-2.xul menulist-shrinkwrap-2-ref.xul
|
||||
== textbox-overflow-1.xul textbox-overflow-1-ref.xul # for bug 749658
|
||||
# accesskeys are not normally displayed on Mac, so skip this test
|
||||
skip-if(cocoaWidget) == accesskey.xul accesskey-ref.xul
|
||||
|
@ -126,8 +126,7 @@
|
||||
|
||||
<activity android:name="Restarter"
|
||||
android:process="@ANDROID_PACKAGE_NAME@Restarter"
|
||||
android:theme="@style/Gecko"
|
||||
android:excludeFromRecents="true">
|
||||
android:theme="@style/Gecko">
|
||||
<intent-filter>
|
||||
<action android:name="org.mozilla.gecko.restart"/>
|
||||
<action android:name="org.mozilla.gecko.restart_update"/>
|
||||
|
@ -1525,6 +1525,14 @@ abstract public class GeckoApp
|
||||
|
||||
mAppContext = this;
|
||||
|
||||
// Check to see if the activity is restarted after configuration change.
|
||||
if (getLastNonConfigurationInstance() != null) {
|
||||
// Restart the application as a safe way to handle the configuration change.
|
||||
doRestart();
|
||||
System.exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// StrictMode is set by defaults resource flag |enableStrictMode|.
|
||||
if (getResources().getBoolean(R.bool.enableStrictMode)) {
|
||||
enableStrictMode();
|
||||
@ -2137,6 +2145,13 @@ abstract public class GeckoApp
|
||||
GeckoScreenOrientationListener.getInstance().start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
// Send a non-null value so that we can restart the application,
|
||||
// when activity restarts due to configuration change.
|
||||
return new Boolean(true);
|
||||
}
|
||||
|
||||
abstract public String getPackageName();
|
||||
abstract public String getContentProcessName();
|
||||
|
||||
|
@ -85,6 +85,7 @@ public class Restarter extends Activity {
|
||||
if (b != null)
|
||||
intent.putExtras(b);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
Log.i(LOGTAG, intent.toString());
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
|
@ -46,7 +46,7 @@ extern "C" {
|
||||
typedef enum {
|
||||
SNAPPY_OK = 0,
|
||||
SNAPPY_INVALID_INPUT = 1,
|
||||
SNAPPY_BUFFER_TOO_SMALL = 2,
|
||||
SNAPPY_BUFFER_TOO_SMALL = 2
|
||||
} snappy_status;
|
||||
|
||||
/*
|
||||
|
@ -4,7 +4,7 @@ This pywebsocket code is mostly unchanged from the source at
|
||||
|
||||
The current Mozilla code is based on
|
||||
|
||||
svnversion: 606 (supports RFC 6455, aka Sec-WebSocket-Version: 13)
|
||||
svnversion: 631 (supports RFC 6455)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
STEPS TO UPDATE MOZILLA TO NEWER PYWEBSOCKET VERSION
|
||||
@ -20,7 +20,7 @@ STEPS TO UPDATE MOZILLA TO NEWER PYWEBSOCKET VERSION
|
||||
- rsync new version into our tree, deleting files that aren't needed any more
|
||||
(NOTE: this will blow away this file! hg revert it or keep a copy.)
|
||||
|
||||
rsync -r --delete dist/ $MOZ_SRC/testing/mochitest/pywebsocket
|
||||
rsync -rv --delete dist/ $MOZ_SRC/testing/mochitest/pywebsocket
|
||||
|
||||
- Get rid of examples/test directory and some cruft:
|
||||
|
||||
|
@ -28,8 +28,11 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Stream of WebSocket protocol with the framing used by IETF HyBi 00 and
|
||||
Hixie 75. For Hixie 75 this stream doesn't perform closing handshake.
|
||||
"""This file provides a class for parsing/building frames of the WebSocket
|
||||
protocol version HyBi 00 and Hixie 75.
|
||||
|
||||
Specification:
|
||||
http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
|
||||
"""
|
||||
|
||||
|
||||
@ -43,7 +46,9 @@ from mod_pywebsocket import util
|
||||
|
||||
|
||||
class StreamHixie75(StreamBase):
|
||||
"""Stream of WebSocket messages."""
|
||||
"""A class for parsing/building frames of the WebSocket protocol version
|
||||
HyBi 00 and Hixie 75.
|
||||
"""
|
||||
|
||||
def __init__(self, request, enable_closing_handshake=False):
|
||||
"""Construct an instance.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright 2011, Google Inc.
|
||||
# Copyright 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -28,7 +28,11 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""Stream class for IETF HyBi latest WebSocket protocol.
|
||||
"""This file provides classes and helper functions for parsing/building frames
|
||||
of the WebSocket protocol (RFC 6455).
|
||||
|
||||
Specification:
|
||||
http://tools.ietf.org/html/rfc6455
|
||||
"""
|
||||
|
||||
|
||||
@ -238,7 +242,9 @@ class StreamOptions(object):
|
||||
|
||||
|
||||
class Stream(StreamBase):
|
||||
"""Stream of WebSocket messages."""
|
||||
"""A class for parsing/building frames of the WebSocket protocol
|
||||
(RFC 6455).
|
||||
"""
|
||||
|
||||
def __init__(self, request, options):
|
||||
"""Constructs an instance.
|
||||
@ -353,8 +359,8 @@ class Stream(StreamBase):
|
||||
|
||||
Raises:
|
||||
BadOperationException: when called on a server-terminated
|
||||
connection or called with inconsistent message type or binary
|
||||
parameter.
|
||||
connection or called with inconsistent message type or
|
||||
binary parameter.
|
||||
"""
|
||||
|
||||
if self._request.server_terminated:
|
||||
@ -482,7 +488,11 @@ class Stream(StreamBase):
|
||||
# - no application data: no code no reason
|
||||
# - 2 octet of application data: has code but no reason
|
||||
# - 3 or more octet of application data: both code and reason
|
||||
if len(message) == 1:
|
||||
if len(message) == 0:
|
||||
self._logger.debug('Received close frame (empty body)')
|
||||
self._request.ws_close_code = (
|
||||
common.STATUS_NO_STATUS_RECEIVED)
|
||||
elif len(message) == 1:
|
||||
raise InvalidFrameException(
|
||||
'If a close frame has status code, the length of '
|
||||
'status code must be 2 octet')
|
||||
@ -501,22 +511,28 @@ class Stream(StreamBase):
|
||||
|
||||
if self._request.server_terminated:
|
||||
self._logger.debug(
|
||||
'Received ack for server-initiated closing '
|
||||
'handshake')
|
||||
'Received ack for server-initiated closing handshake')
|
||||
return None
|
||||
|
||||
self._logger.debug(
|
||||
'Received client-initiated closing handshake')
|
||||
|
||||
code = common.STATUS_NORMAL
|
||||
code = common.STATUS_NORMAL_CLOSURE
|
||||
reason = ''
|
||||
if hasattr(self._request, '_dispatcher'):
|
||||
dispatcher = self._request._dispatcher
|
||||
code, reason = dispatcher.passive_closing_handshake(
|
||||
self._request)
|
||||
if code is None and reason is not None and len(reason) > 0:
|
||||
self._logger.warning(
|
||||
'Handler specified reason despite code being None')
|
||||
reason = ''
|
||||
if reason is None:
|
||||
reason = ''
|
||||
self._send_closing_handshake(code, reason)
|
||||
self._logger.debug(
|
||||
'Sent ack for client-initiated closing handshake')
|
||||
'Sent ack for client-initiated closing handshake '
|
||||
'(code=%r, reason=%r)', code, reason)
|
||||
return None
|
||||
elif self._original_opcode == common.OPCODE_PING:
|
||||
try:
|
||||
@ -565,17 +581,19 @@ class Stream(StreamBase):
|
||||
'Opcode %d is not supported' % self._original_opcode)
|
||||
|
||||
def _send_closing_handshake(self, code, reason):
|
||||
if code >= (1 << 16) or code < 0:
|
||||
raise BadOperationException('Status code is out of range')
|
||||
|
||||
encoded_reason = reason.encode('utf-8')
|
||||
if len(encoded_reason) + 2 > 125:
|
||||
raise BadOperationException(
|
||||
'Application data size of close frames must be 125 bytes or '
|
||||
'less')
|
||||
body = ''
|
||||
if code is not None:
|
||||
if code >= (1 << 16) or code < 0:
|
||||
raise BadOperationException('Status code is out of range')
|
||||
encoded_reason = reason.encode('utf-8')
|
||||
if len(encoded_reason) + 2 > 125:
|
||||
raise BadOperationException(
|
||||
'Application data size of close frames must be 125 bytes '
|
||||
'or less')
|
||||
body = struct.pack('!H', code) + encoded_reason
|
||||
|
||||
frame = create_close_frame(
|
||||
struct.pack('!H', code) + encoded_reason,
|
||||
body,
|
||||
self._options.mask_send,
|
||||
self._options.outgoing_frame_filters)
|
||||
|
||||
@ -583,16 +601,37 @@ class Stream(StreamBase):
|
||||
|
||||
self._write(frame)
|
||||
|
||||
def close_connection(self, code=common.STATUS_NORMAL, reason=''):
|
||||
"""Closes a WebSocket connection."""
|
||||
def close_connection(self, code=common.STATUS_NORMAL_CLOSURE, reason=''):
|
||||
"""Closes a WebSocket connection.
|
||||
|
||||
Args:
|
||||
code: Status code for close frame. If code is None, a close
|
||||
frame with empty body will be sent.
|
||||
reason: string representing close reason.
|
||||
Raises:
|
||||
BadOperationException: when reason is specified with code None
|
||||
or reason is not an instance of both str and unicode.
|
||||
"""
|
||||
|
||||
if self._request.server_terminated:
|
||||
self._logger.debug(
|
||||
'Requested close_connection but server is already terminated')
|
||||
return
|
||||
|
||||
if code is None:
|
||||
if reason is not None and len(reason) > 0:
|
||||
raise BadOperationException(
|
||||
'close reason must not be specified if code is None')
|
||||
reason = ''
|
||||
else:
|
||||
if not isinstance(reason, str) and not isinstance(reason, unicode):
|
||||
raise BadOperationException(
|
||||
'close reason must be an instance of str or unicode')
|
||||
|
||||
self._send_closing_handshake(code, reason)
|
||||
self._logger.debug('Sent server-initiated closing handshake')
|
||||
self._logger.debug(
|
||||
'Sent server-initiated closing handshake (code=%r, reason=%r)',
|
||||
code, reason)
|
||||
|
||||
if (code == common.STATUS_GOING_AWAY or
|
||||
code == common.STATUS_PROTOCOL_ERROR):
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright 2011, Google Inc.
|
||||
# Copyright 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -93,21 +93,41 @@ SEC_WEBSOCKET_LOCATION_HEADER = 'Sec-WebSocket-Location'
|
||||
# Extensions
|
||||
DEFLATE_STREAM_EXTENSION = 'deflate-stream'
|
||||
DEFLATE_FRAME_EXTENSION = 'deflate-frame'
|
||||
X_WEBKIT_DEFLATE_FRAME_EXTENSION = 'x-webkit-deflate-frame'
|
||||
|
||||
# Status codes
|
||||
# Code STATUS_CODE_NOT_AVAILABLE should not be used in actual frames. This code
|
||||
# is exposed to JavaScript API as pseudo status code which represent actual
|
||||
# frame does not have status code.
|
||||
STATUS_NORMAL = 1000
|
||||
# Code STATUS_NO_STATUS_RECEIVED, STATUS_ABNORMAL_CLOSURE, and
|
||||
# STATUS_TLS_HANDSHAKE are pseudo codes to indicate specific error cases.
|
||||
# Could not be used for codes in actual closing frames.
|
||||
# Application level errors must use codes in the range
|
||||
# STATUS_USER_REGISTERED_BASE to STATUS_USER_PRIVATE_MAX. The codes in the
|
||||
# range STATUS_USER_REGISTERED_BASE to STATUS_USER_REGISTERED_MAX are managed
|
||||
# by IANA. Usually application must define user protocol level errors in the
|
||||
# range STATUS_USER_PRIVATE_BASE to STATUS_USER_PRIVATE_MAX.
|
||||
STATUS_NORMAL_CLOSURE = 1000
|
||||
STATUS_GOING_AWAY = 1001
|
||||
STATUS_PROTOCOL_ERROR = 1002
|
||||
STATUS_UNSUPPORTED = 1003
|
||||
STATUS_CODE_NOT_AVAILABLE = 1005
|
||||
STATUS_ABNORMAL_CLOSE = 1006
|
||||
STATUS_INVALID_FRAME_PAYLOAD = 1007
|
||||
STATUS_UNSUPPORTED_DATA = 1003
|
||||
STATUS_NO_STATUS_RECEIVED = 1005
|
||||
STATUS_ABNORMAL_CLOSURE = 1006
|
||||
STATUS_INVALID_FRAME_PAYLOAD_DATA = 1007
|
||||
STATUS_POLICY_VIOLATION = 1008
|
||||
STATUS_MESSAGE_TOO_BIG = 1009
|
||||
STATUS_MANDATORY_EXT = 1010
|
||||
STATUS_MANDATORY_EXTENSION = 1010
|
||||
STATUS_INTERNAL_SERVER_ERROR = 1011
|
||||
STATUS_TLS_HANDSHAKE = 1015
|
||||
STATUS_USER_REGISTERED_BASE = 3000
|
||||
STATUS_USER_REGISTERED_MAX = 3999
|
||||
STATUS_USER_PRIVATE_BASE = 4000
|
||||
STATUS_USER_PRIVATE_MAX = 4999
|
||||
# Following definitions are aliases to keep compatibility. Applications must
|
||||
# not use these obsoleted definitions anymore.
|
||||
STATUS_NORMAL = STATUS_NORMAL_CLOSURE
|
||||
STATUS_UNSUPPORTED = STATUS_UNSUPPORTED_DATA
|
||||
STATUS_CODE_NOT_AVAILABLE = STATUS_NO_STATUS_RECEIVED
|
||||
STATUS_ABNORMAL_CLOSE = STATUS_ABNORMAL_CLOSURE
|
||||
STATUS_INVALID_FRAME_PAYLOAD = STATUS_INVALID_FRAME_PAYLOAD_DATA
|
||||
STATUS_MANDATORY_EXT = STATUS_MANDATORY_EXTENSION
|
||||
|
||||
# HTTP status codes
|
||||
HTTP_STATUS_BAD_REQUEST = 400
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright 2011, Google Inc.
|
||||
# Copyright 2012, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -62,7 +62,7 @@ class DispatchException(Exception):
|
||||
def _default_passive_closing_handshake_handler(request):
|
||||
"""Default web_socket_passive_closing_handshake handler."""
|
||||
|
||||
return common.STATUS_NORMAL, ''
|
||||
return common.STATUS_NORMAL_CLOSURE, ''
|
||||
|
||||
|
||||
def _normalize_path(path):
|
||||
@ -292,7 +292,7 @@ class Dispatcher(object):
|
||||
raise
|
||||
except msgutil.BadOperationException, e:
|
||||
self._logger.debug('%s', e)
|
||||
request.ws_stream.close_connection(common.STATUS_ABNORMAL_CLOSE)
|
||||
request.ws_stream.close_connection(common.STATUS_ABNORMAL_CLOSURE)
|
||||
except msgutil.InvalidFrameException, e:
|
||||
# InvalidFrameException must be caught before
|
||||
# ConnectionTerminatedException that catches InvalidFrameException.
|
||||
@ -300,11 +300,11 @@ class Dispatcher(object):
|
||||
request.ws_stream.close_connection(common.STATUS_PROTOCOL_ERROR)
|
||||
except msgutil.UnsupportedFrameException, e:
|
||||
self._logger.debug('%s', e)
|
||||
request.ws_stream.close_connection(common.STATUS_UNSUPPORTED)
|
||||
request.ws_stream.close_connection(common.STATUS_UNSUPPORTED_DATA)
|
||||
except stream.InvalidUTF8Exception, e:
|
||||
self._logger.debug('%s', e)
|
||||
request.ws_stream.close_connection(
|
||||
common.STATUS_INVALID_FRAME_PAYLOAD)
|
||||
common.STATUS_INVALID_FRAME_PAYLOAD_DATA)
|
||||
except msgutil.ConnectionTerminatedException, e:
|
||||
self._logger.debug('%s', e)
|
||||
except Exception, e:
|
||||
|
@ -72,7 +72,7 @@ _available_processors[common.DEFLATE_STREAM_EXTENSION] = (
|
||||
class DeflateFrameExtensionProcessor(ExtensionProcessorInterface):
|
||||
"""WebSocket Per-frame DEFLATE extension processor."""
|
||||
|
||||
_WINDOW_BITS_PARAM = 'window_bits'
|
||||
_WINDOW_BITS_PARAM = 'max_window_bits'
|
||||
_NO_CONTEXT_TAKEOVER_PARAM = 'no_context_takeover'
|
||||
|
||||
def __init__(self, request):
|
||||
@ -83,6 +83,18 @@ class DeflateFrameExtensionProcessor(ExtensionProcessorInterface):
|
||||
self._response_window_bits = None
|
||||
self._response_no_context_takeover = False
|
||||
|
||||
# Counters for statistics.
|
||||
|
||||
# Total number of outgoing bytes supplied to this filter.
|
||||
self._total_outgoing_payload_bytes = 0
|
||||
# Total number of bytes sent to the network after applying this filter.
|
||||
self._total_filtered_outgoing_payload_bytes = 0
|
||||
|
||||
# Total number of bytes received from the network.
|
||||
self._total_incoming_payload_bytes = 0
|
||||
# Total number of incoming bytes obtained after applying this filter.
|
||||
self._total_filtered_incoming_payload_bytes = 0
|
||||
|
||||
def get_extension_response(self):
|
||||
# Any unknown parameter will be just ignored.
|
||||
|
||||
@ -110,7 +122,7 @@ class DeflateFrameExtensionProcessor(ExtensionProcessorInterface):
|
||||
|
||||
self._compress_outgoing = True
|
||||
|
||||
response = common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)
|
||||
response = common.ExtensionParameter(self._request.name())
|
||||
|
||||
if self._response_window_bits is not None:
|
||||
response.add_parameter(
|
||||
@ -123,7 +135,7 @@ class DeflateFrameExtensionProcessor(ExtensionProcessorInterface):
|
||||
'Enable %s extension ('
|
||||
'request: window_bits=%s; no_context_takeover=%r, '
|
||||
'response: window_wbits=%s; no_context_takeover=%r)' %
|
||||
(common.DEFLATE_STREAM_EXTENSION,
|
||||
(self._request.name(),
|
||||
window_bits,
|
||||
no_context_takeover,
|
||||
self._response_window_bits,
|
||||
@ -171,29 +183,77 @@ class DeflateFrameExtensionProcessor(ExtensionProcessorInterface):
|
||||
an _OutgoingFilter instance.
|
||||
"""
|
||||
|
||||
original_payload_size = len(frame.payload)
|
||||
self._total_outgoing_payload_bytes += original_payload_size
|
||||
|
||||
if (not self._compress_outgoing or
|
||||
common.is_control_opcode(frame.opcode)):
|
||||
self._total_filtered_outgoing_payload_bytes += (
|
||||
original_payload_size)
|
||||
return
|
||||
|
||||
frame.payload = self._deflater.filter(frame.payload)
|
||||
frame.rsv1 = 1
|
||||
|
||||
filtered_payload_size = len(frame.payload)
|
||||
self._total_filtered_outgoing_payload_bytes += filtered_payload_size
|
||||
|
||||
# Print inf when ratio is not available.
|
||||
ratio = float('inf')
|
||||
average_ratio = float('inf')
|
||||
if original_payload_size != 0:
|
||||
ratio = float(filtered_payload_size) / original_payload_size
|
||||
if self._total_outgoing_payload_bytes != 0:
|
||||
average_ratio = (
|
||||
float(self._total_filtered_outgoing_payload_bytes) /
|
||||
self._total_outgoing_payload_bytes)
|
||||
self._logger.debug(
|
||||
'Outgoing compress ratio: %f (average: %f)' %
|
||||
(ratio, average_ratio))
|
||||
|
||||
def _incoming_filter(self, frame):
|
||||
"""Transform incoming frames. This method is called only by
|
||||
an _IncomingFilter instance.
|
||||
"""
|
||||
|
||||
received_payload_size = len(frame.payload)
|
||||
self._total_incoming_payload_bytes += received_payload_size
|
||||
|
||||
if frame.rsv1 != 1 or common.is_control_opcode(frame.opcode):
|
||||
self._total_filtered_incoming_payload_bytes += (
|
||||
received_payload_size)
|
||||
return
|
||||
|
||||
frame.payload = self._inflater.filter(frame.payload)
|
||||
frame.rsv1 = 0
|
||||
|
||||
filtered_payload_size = len(frame.payload)
|
||||
self._total_filtered_incoming_payload_bytes += filtered_payload_size
|
||||
|
||||
# Print inf when ratio is not available.
|
||||
ratio = float('inf')
|
||||
average_ratio = float('inf')
|
||||
if received_payload_size != 0:
|
||||
ratio = float(received_payload_size) / filtered_payload_size
|
||||
if self._total_filtered_incoming_payload_bytes != 0:
|
||||
average_ratio = (
|
||||
float(self._total_incoming_payload_bytes) /
|
||||
self._total_filtered_incoming_payload_bytes)
|
||||
self._logger.debug(
|
||||
'Incoming compress ratio: %f (average: %f)' %
|
||||
(ratio, average_ratio))
|
||||
|
||||
|
||||
_available_processors[common.DEFLATE_FRAME_EXTENSION] = (
|
||||
DeflateFrameExtensionProcessor)
|
||||
|
||||
|
||||
# Adding vendor-prefixed deflate-frame extension.
|
||||
# TODO(bashi): Remove this after WebKit stops using vender prefix.
|
||||
_available_processors[common.X_WEBKIT_DEFLATE_FRAME_EXTENSION] = (
|
||||
DeflateFrameExtensionProcessor)
|
||||
|
||||
|
||||
def get_extension_processor(extension_request):
|
||||
global _available_processors
|
||||
processor_class = _available_processors.get(extension_request.name())
|
||||
|
@ -83,15 +83,15 @@ def do_handshake(request, dispatcher, allowDraft75=False, strict=False):
|
||||
|
||||
handshakers = []
|
||||
handshakers.append(
|
||||
('IETF HyBi latest', hybi.Handshaker(request, dispatcher)))
|
||||
('RFC 6455', hybi.Handshaker(request, dispatcher)))
|
||||
handshakers.append(
|
||||
('IETF HyBi 00', hybi00.Handshaker(request, dispatcher)))
|
||||
('HyBi 00', hybi00.Handshaker(request, dispatcher)))
|
||||
if allowDraft75:
|
||||
handshakers.append(
|
||||
('IETF Hixie 75', draft75.Handshaker(request, dispatcher, strict)))
|
||||
('Hixie 75', draft75.Handshaker(request, dispatcher, strict)))
|
||||
|
||||
for name, handshaker in handshakers:
|
||||
_LOGGER.debug('Trying %s protocol', name)
|
||||
_LOGGER.debug('Trying protocol version %s', name)
|
||||
try:
|
||||
handshaker.do_handshake()
|
||||
_LOGGER.info('Established (%s protocol)', name)
|
||||
|
@ -89,7 +89,7 @@ def validate_subprotocol(subprotocol, hixie):
|
||||
Sec-WebSocket-Protocol.
|
||||
|
||||
See
|
||||
- HyBi 10: Section 5.1. and 5.2.2.
|
||||
- RFC 6455: Section 4.1., 4.2.2., and 4.3.
|
||||
- HyBi 00: Section 4.1. Opening handshake
|
||||
- Hixie 75: Section 4.1. Handshake
|
||||
"""
|
||||
|
@ -28,7 +28,12 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""WebSocket HyBi latest opening handshake processor."""
|
||||
"""This file provides the opening handshake processor for the WebSocket
|
||||
protocol (RFC 6455).
|
||||
|
||||
Specification:
|
||||
http://tools.ietf.org/html/rfc6455
|
||||
"""
|
||||
|
||||
|
||||
# Note: request.connection.write is used in this module, even though mod_python
|
||||
@ -59,7 +64,10 @@ from mod_pywebsocket.stream import StreamOptions
|
||||
from mod_pywebsocket import util
|
||||
|
||||
|
||||
_BASE64_REGEX = re.compile('^[+/0-9A-Za-z]*=*$')
|
||||
# Used to validate the value in the Sec-WebSocket-Key header strictly. RFC 4648
|
||||
# disallows non-zero padding, so the character right before == must be any of
|
||||
# A, Q, g and w.
|
||||
_SEC_WEBSOCKET_KEY_REGEX = re.compile('^[+/0-9A-Za-z]{21}[AQgw]==$')
|
||||
|
||||
# Defining aliases for values used frequently.
|
||||
_VERSION_HYBI08 = common.VERSION_HYBI08
|
||||
@ -85,7 +93,7 @@ def compute_accept(key):
|
||||
|
||||
|
||||
class Handshaker(object):
|
||||
"""This class performs WebSocket handshake."""
|
||||
"""Opening handshake processor for the WebSocket protocol (RFC 6455)."""
|
||||
|
||||
def __init__(self, request, dispatcher):
|
||||
"""Construct an instance.
|
||||
@ -161,7 +169,7 @@ class Handshaker(object):
|
||||
accept,
|
||||
util.hexify(accept_binary))
|
||||
|
||||
self._logger.debug('IETF HyBi protocol')
|
||||
self._logger.debug('Protocol version is RFC 6455')
|
||||
|
||||
# Setup extension processors.
|
||||
|
||||
@ -258,7 +266,6 @@ class Handshaker(object):
|
||||
|
||||
def _set_protocol(self):
|
||||
self._request.ws_protocol = None
|
||||
|
||||
# MOZILLA
|
||||
self._request.sts = None
|
||||
# /MOZILLA
|
||||
@ -307,7 +314,7 @@ class Handshaker(object):
|
||||
# module. Because base64 module skips invalid characters, we have
|
||||
# to do this in advance to make this server strictly reject illegal
|
||||
# keys.
|
||||
if _BASE64_REGEX.match(key):
|
||||
if _SEC_WEBSOCKET_KEY_REGEX.match(key):
|
||||
decoded_key = base64.b64decode(key)
|
||||
if len(decoded_key) == 16:
|
||||
key_is_valid = True
|
||||
|
@ -28,7 +28,12 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
"""WebSocket initial handshake hander for HyBi 00 protocol."""
|
||||
"""This file provides the opening handshake processor for the WebSocket
|
||||
protocol version HyBi 00.
|
||||
|
||||
Specification:
|
||||
http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00
|
||||
"""
|
||||
|
||||
|
||||
# Note: request.connection.write/read are used in this module, even though
|
||||
@ -61,7 +66,8 @@ _MANDATORY_HEADERS = [
|
||||
|
||||
|
||||
class Handshaker(object):
|
||||
"""This class performs WebSocket handshake."""
|
||||
"""Opening handshake processor for the WebSocket protocol version HyBi 00.
|
||||
"""
|
||||
|
||||
def __init__(self, request, dispatcher):
|
||||
"""Construct an instance.
|
||||
@ -139,7 +145,7 @@ class Handshaker(object):
|
||||
(common.SEC_WEBSOCKET_DRAFT_HEADER,
|
||||
draft))
|
||||
|
||||
self._logger.debug('IETF HyBi 00 protocol')
|
||||
self._logger.debug('Protocol version is HyBi 00')
|
||||
self._request.ws_version = common.VERSION_HYBI00
|
||||
self._request.ws_stream = StreamHixie75(self._request, True)
|
||||
|
||||
|
@ -82,7 +82,15 @@ def receive_message(request):
|
||||
Args:
|
||||
request: mod_python request.
|
||||
Raises:
|
||||
BadOperationException: when client already terminated.
|
||||
InvalidFrameException: when client send invalid frame.
|
||||
UnsupportedFrameException: when client send unsupported frame e.g. some
|
||||
of reserved bit is set but no extension can
|
||||
recognize it.
|
||||
InvalidUTF8Exception: when client send a text frame containing any
|
||||
invalid UTF-8 string.
|
||||
ConnectionTerminatedException: when the connection is closed
|
||||
unexpectedly.
|
||||
BadOperationException: when client already terminated.
|
||||
"""
|
||||
return request.ws_stream.receive_message()
|
||||
|
||||
|
@ -177,9 +177,16 @@ class RepeatedXorMasker(object):
|
||||
def mask(self, s):
|
||||
result = array.array('B')
|
||||
result.fromstring(s)
|
||||
# Use temporary local variables to eliminate the cost to access
|
||||
# attributes
|
||||
count = self._count
|
||||
mask = self._mask
|
||||
mask_size = self._mask_size
|
||||
for i in xrange(len(result)):
|
||||
result[i] ^= self._mask[self._count]
|
||||
self._count = (self._count + 1) % self._mask_size
|
||||
result[i] ^= mask[count]
|
||||
count = (count + 1) % mask_size
|
||||
self._count = count
|
||||
|
||||
return result.tostring()
|
||||
|
||||
|
||||
|
@ -32,6 +32,8 @@
|
||||
|
||||
"""Standalone WebSocket server.
|
||||
|
||||
BASIC USAGE
|
||||
|
||||
Use this server to run mod_pywebsocket without Apache HTTP Server.
|
||||
|
||||
Usage:
|
||||
@ -52,11 +54,39 @@ handlers. If this path is relative, <document_root> is used as the base.
|
||||
<scan_dir> is a path under the root directory. If specified, only the
|
||||
handlers under scan_dir are scanned. This is useful in saving scan time.
|
||||
|
||||
Note:
|
||||
|
||||
CONFIGURATION FILE
|
||||
|
||||
You can also write a configuration file and use it by specifying the path to
|
||||
the configuration file by --config option. Please write a configuration file
|
||||
following the documentation of the Python ConfigParser library. Name of each
|
||||
entry must be the long version argument name. E.g. to set log level to debug,
|
||||
add the following line:
|
||||
|
||||
log_level=debug
|
||||
|
||||
For options which doesn't take value, please add some fake value. E.g. for
|
||||
--tls option, add the following line:
|
||||
|
||||
tls=True
|
||||
|
||||
Note that tls will be enabled even if you write tls=False as the value part is
|
||||
fake.
|
||||
|
||||
When both a command line argument and a configuration file entry are set for
|
||||
the same configuration item, the command line value will override one in the
|
||||
configuration file.
|
||||
|
||||
|
||||
THREADING
|
||||
|
||||
This server is derived from SocketServer.ThreadingMixIn. Hence a thread is
|
||||
used for each request.
|
||||
|
||||
SECURITY WARNING: This uses CGIHTTPServer and CGIHTTPServer is not secure.
|
||||
|
||||
SECURITY WARNING
|
||||
|
||||
This uses CGIHTTPServer and CGIHTTPServer is not secure.
|
||||
It may execute arbitrary Python code or external programs. It should not be
|
||||
used outside a firewall.
|
||||
"""
|
||||
@ -65,6 +95,7 @@ import BaseHTTPServer
|
||||
import CGIHTTPServer
|
||||
import SimpleHTTPServer
|
||||
import SocketServer
|
||||
import ConfigParser
|
||||
import httplib
|
||||
import logging
|
||||
import logging.handlers
|
||||
@ -77,13 +108,17 @@ import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
_HAS_SSL = False
|
||||
_HAS_OPEN_SSL = False
|
||||
try:
|
||||
import OpenSSL.SSL
|
||||
_HAS_OPEN_SSL = True
|
||||
import ssl
|
||||
_HAS_SSL = True
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
import OpenSSL.SSL
|
||||
_HAS_OPEN_SSL = True
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from mod_pywebsocket import common
|
||||
from mod_pywebsocket import dispatch
|
||||
@ -193,6 +228,28 @@ class _StandaloneRequest(object):
|
||||
'Drained data following close frame: %r', drained_data)
|
||||
|
||||
|
||||
class _StandaloneSSLConnection(object):
|
||||
"""A wrapper class for OpenSSL.SSL.Connection to provide makefile method
|
||||
which is not supported by the class.
|
||||
"""
|
||||
|
||||
def __init__(self, connection):
|
||||
self._connection = connection
|
||||
|
||||
def __getattribute__(self, name):
|
||||
if name in ('_connection', 'makefile'):
|
||||
return object.__getattribute__(self, name)
|
||||
return self._connection.__getattribute__(name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name in ('_connection', 'makefile'):
|
||||
return object.__setattr__(self, name, value)
|
||||
return self._connection.__setattr__(name, value)
|
||||
|
||||
def makefile(self, mode='r', bufsize=-1):
|
||||
return socket._fileobject(self._connection, mode, bufsize)
|
||||
|
||||
|
||||
class WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
|
||||
"""HTTPServer specialized for WebSocket."""
|
||||
|
||||
@ -253,12 +310,18 @@ class WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
|
||||
self._logger.info('Skip by failure: %r', e)
|
||||
continue
|
||||
if self.websocket_server_options.use_tls:
|
||||
ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
|
||||
ctx.use_privatekey_file(
|
||||
self.websocket_server_options.private_key)
|
||||
ctx.use_certificate_file(
|
||||
self.websocket_server_options.certificate)
|
||||
socket_ = OpenSSL.SSL.Connection(ctx, socket_)
|
||||
if _HAS_SSL:
|
||||
socket_ = ssl.wrap_socket(socket_,
|
||||
keyfile=self.websocket_server_options.private_key,
|
||||
certfile=self.websocket_server_options.certificate,
|
||||
ssl_version=ssl.PROTOCOL_SSLv23)
|
||||
if _HAS_OPEN_SSL:
|
||||
ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
|
||||
ctx.use_privatekey_file(
|
||||
self.websocket_server_options.private_key)
|
||||
ctx.use_certificate_file(
|
||||
self.websocket_server_options.certificate)
|
||||
socket_ = OpenSSL.SSL.Connection(ctx, socket_)
|
||||
self._sockets.append((socket_, addrinfo))
|
||||
|
||||
def server_bind(self):
|
||||
@ -328,6 +391,18 @@ class WebSocketServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
|
||||
util.get_stack_trace())
|
||||
# Note: client_address is a tuple.
|
||||
|
||||
def get_request(self):
|
||||
"""Override TCPServer.get_request to wrap OpenSSL.SSL.Connection
|
||||
object with _StandaloneSSLConnection to provide makefile method. We
|
||||
cannot substitute OpenSSL.SSL.Connection.makefile since it's readonly
|
||||
attribute.
|
||||
"""
|
||||
|
||||
accepted_socket, client_address = self.socket.accept()
|
||||
if self.websocket_server_options.use_tls and _HAS_OPEN_SSL:
|
||||
accepted_socket = _StandaloneSSLConnection(accepted_socket)
|
||||
return accepted_socket, client_address
|
||||
|
||||
def serve_forever(self, poll_interval=0.5):
|
||||
"""Override SocketServer.BaseServer.serve_forever."""
|
||||
|
||||
@ -413,7 +488,9 @@ class WebSocketRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
|
||||
# it needs variables set by CGIHTTPRequestHandler.parse_request.
|
||||
#
|
||||
# Variables set by this method will be also used by WebSocket request
|
||||
# handling. See _StandaloneRequest.get_request, etc.
|
||||
# handling (self.path, self.command, self.requestline, etc. See also
|
||||
# how _StandaloneRequest's members are implemented using these
|
||||
# attributes).
|
||||
if not CGIHTTPServer.CGIHTTPRequestHandler.parse_request(self):
|
||||
return False
|
||||
host, port, resource = http_header_util.parse_uri(self.path)
|
||||
@ -567,6 +644,11 @@ def _alias_handlers(dispatcher, websock_handlers_map_file):
|
||||
def _build_option_parser():
|
||||
parser = optparse.OptionParser()
|
||||
|
||||
parser.add_option('--config', dest='config_file', type='string',
|
||||
default=None,
|
||||
help=('Path to configuration file. See the file comment '
|
||||
'at the top of this file for the configuration '
|
||||
'file format'))
|
||||
parser.add_option('-H', '--server-host', '--server_host',
|
||||
dest='server_host',
|
||||
default='',
|
||||
@ -676,14 +758,46 @@ class ThreadMonitor(threading.Thread):
|
||||
time.sleep(self._interval_in_sec)
|
||||
|
||||
|
||||
def _main(args=None):
|
||||
def _parse_args_and_config(args):
|
||||
parser = _build_option_parser()
|
||||
|
||||
options, args = parser.parse_args(args=args)
|
||||
if args:
|
||||
logging.critical('Unrecognized positional arguments: %r', args)
|
||||
# First, parse options without configuration file.
|
||||
temporary_options, temporary_args = parser.parse_args(args=args)
|
||||
if temporary_args:
|
||||
logging.critical(
|
||||
'Unrecognized positional arguments: %r', temporary_args)
|
||||
sys.exit(1)
|
||||
|
||||
if temporary_options.config_file:
|
||||
try:
|
||||
config_fp = open(temporary_options.config_file, 'r')
|
||||
except IOError, e:
|
||||
logging.critical(
|
||||
'Failed to open configuration file %r: %r',
|
||||
temporary_options.config_file,
|
||||
e)
|
||||
sys.exit(1)
|
||||
|
||||
config_parser = ConfigParser.SafeConfigParser()
|
||||
config_parser.readfp(config_fp)
|
||||
config_fp.close()
|
||||
|
||||
args_from_config = []
|
||||
for name, value in config_parser.items('pywebsocket'):
|
||||
args_from_config.append('--' + name)
|
||||
args_from_config.append(value)
|
||||
if args is None:
|
||||
args = args_from_config
|
||||
else:
|
||||
args = args_from_config + args
|
||||
return parser.parse_args(args=args)
|
||||
else:
|
||||
return temporary_options, temporary_args
|
||||
|
||||
|
||||
def _main(args=None):
|
||||
options, args = _parse_args_and_config(args=args)
|
||||
|
||||
os.chdir(options.document_root)
|
||||
|
||||
_configure_logging(options)
|
||||
@ -710,8 +824,8 @@ def _main(args=None):
|
||||
options.is_executable_method = __check_script
|
||||
|
||||
if options.use_tls:
|
||||
if not _HAS_OPEN_SSL:
|
||||
logging.critical('To use TLS, install pyOpenSSL.')
|
||||
if not (_HAS_SSL or _HAS_OPEN_SSL):
|
||||
logging.critical('TLS support requires ssl or pyOpenSSL.')
|
||||
sys.exit(1)
|
||||
if not options.private_key or not options.certificate:
|
||||
logging.critical(
|
||||
@ -750,7 +864,7 @@ def _main(args=None):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
_main()
|
||||
_main(sys.argv[1:])
|
||||
|
||||
|
||||
# vi:sts=4 sw=4 et
|
||||
|
@ -78,3 +78,5 @@ endif
|
||||
TEST_DIRS += test
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
CXXFLAGS += $(TK_CFLAGS)
|
||||
|
@ -87,6 +87,10 @@
|
||||
#include "AndroidBridge.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK2
|
||||
#include <gtk/gtk.h>
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
#define DOWNLOAD_MANAGER_BUNDLE "chrome://mozapps/locale/downloads/downloads.properties"
|
||||
@ -131,6 +135,9 @@ nsDownloadManager::GetSingleton()
|
||||
|
||||
gDownloadManagerService = new nsDownloadManager();
|
||||
if (gDownloadManagerService) {
|
||||
#if defined(MOZ_WIDGET_GTK2)
|
||||
g_type_init();
|
||||
#endif
|
||||
NS_ADDREF(gDownloadManagerService);
|
||||
if (NS_FAILED(gDownloadManagerService->Init()))
|
||||
NS_RELEASE(gDownloadManagerService);
|
||||
@ -2299,7 +2306,7 @@ nsDownload::SetState(DownloadState aState)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
|
||||
#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GTK2)
|
||||
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget);
|
||||
nsCOMPtr<nsIFile> file;
|
||||
nsAutoString path;
|
||||
@ -2309,8 +2316,8 @@ nsDownload::SetState(DownloadState aState)
|
||||
file &&
|
||||
NS_SUCCEEDED(file->GetPath(path))) {
|
||||
|
||||
#ifdef XP_WIN
|
||||
// On windows, add the download to the system's "recent documents"
|
||||
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK2)
|
||||
// On Windows and Gtk, add the download to the system's "recent documents"
|
||||
// list, with a pref to disable.
|
||||
{
|
||||
bool addToRecentDocs = true;
|
||||
@ -2319,7 +2326,18 @@ nsDownload::SetState(DownloadState aState)
|
||||
|
||||
if (addToRecentDocs &&
|
||||
!nsDownloadManager::gDownloadManagerService->mInPrivateBrowsing) {
|
||||
#ifdef XP_WIN
|
||||
::SHAddToRecentDocs(SHARD_PATHW, path.get());
|
||||
#elif defined(MOZ_WIDGET_GTK2)
|
||||
GtkRecentManager* manager = gtk_recent_manager_get_default();
|
||||
|
||||
gchar* uri = g_filename_to_uri(NS_ConvertUTF16toUTF8(path).get(),
|
||||
NULL, NULL);
|
||||
if (uri) {
|
||||
gtk_recent_manager_add_item(manager, uri);
|
||||
g_free(uri);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -57,7 +57,7 @@
|
||||
|
||||
<binding id="label-control" extends="chrome://global/content/bindings/text.xml#text-label">
|
||||
<content>
|
||||
<html:span anonid="accessKeyParens"><children/></html:span>
|
||||
<children/><html:span anonid="accessKeyParens"></html:span>
|
||||
</content>
|
||||
<implementation implements="nsIDOMXULLabelElement">
|
||||
<constructor>
|
||||
@ -106,7 +106,7 @@
|
||||
}
|
||||
|
||||
var afterLabel = document.getAnonymousElementByAttribute(this, "anonid", "accessKeyParens");
|
||||
afterLabel.textContent = ""; // This does not clear real nodes!
|
||||
afterLabel.textContent = "";
|
||||
|
||||
var oldAccessKey = this.getElementsByAttribute('class', 'accesskey').item(0);
|
||||
if (oldAccessKey) { // Clear old accesskey
|
||||
|
@ -409,6 +409,7 @@ var gCheckUpdateSecurityDefault = true;
|
||||
var gCheckUpdateSecurity = gCheckUpdateSecurityDefault;
|
||||
var gUpdateEnabled = true;
|
||||
var gAutoUpdateDefault = true;
|
||||
var gHotfixID = null;
|
||||
|
||||
/**
|
||||
* This is the real manager, kept here rather than in AddonManager to keep its
|
||||
@ -538,6 +539,11 @@ var AddonManagerInternal = {
|
||||
} catch (e) {}
|
||||
Services.prefs.addObserver(PREF_EM_AUTOUPDATE_DEFAULT, this, false);
|
||||
|
||||
try {
|
||||
gHotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
|
||||
} catch (e) {}
|
||||
Services.prefs.addObserver(PREF_EM_HOTFIX_ID, this, false);
|
||||
|
||||
// Ensure all default providers have had a chance to register themselves
|
||||
DEFAULT_PROVIDERS.forEach(function(url) {
|
||||
try {
|
||||
@ -664,6 +670,7 @@ var AddonManagerInternal = {
|
||||
Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this);
|
||||
Services.prefs.removeObserver(PREF_EM_UPDATE_ENABLED, this);
|
||||
Services.prefs.removeObserver(PREF_EM_AUTOUPDATE_DEFAULT, this);
|
||||
Services.prefs.removeObserver(PREF_EM_HOTFIX_ID, this);
|
||||
|
||||
this.providers.forEach(function(provider) {
|
||||
callProvider(provider, "shutdown");
|
||||
@ -752,6 +759,14 @@ var AddonManagerInternal = {
|
||||
this.callManagerListeners("onUpdateModeChanged");
|
||||
break;
|
||||
}
|
||||
case PREF_EM_HOTFIX_ID: {
|
||||
try {
|
||||
gHotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
|
||||
} catch(e) {
|
||||
gHotfixID = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -825,9 +840,7 @@ var AddonManagerInternal = {
|
||||
* that can be updated.
|
||||
*/
|
||||
backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
|
||||
let hotfixID = null;
|
||||
if (Services.prefs.getPrefType(PREF_EM_HOTFIX_ID) == Ci.nsIPrefBranch.PREF_STRING)
|
||||
hotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
|
||||
let hotfixID = this.hotfixID;
|
||||
|
||||
let checkHotfix = hotfixID &&
|
||||
Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
|
||||
@ -1688,7 +1701,11 @@ var AddonManagerInternal = {
|
||||
if (aValue != gUpdateEnabled)
|
||||
Services.prefs.setBoolPref(PREF_EM_UPDATE_ENABLED, aValue);
|
||||
return aValue;
|
||||
}
|
||||
},
|
||||
|
||||
get hotfixID() {
|
||||
return gHotfixID;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2068,6 +2085,10 @@ var AddonManager = {
|
||||
AddonManagerInternal.autoUpdateDefault = aValue;
|
||||
},
|
||||
|
||||
get hotfixID() {
|
||||
return AddonManagerInternal.hotfixID;
|
||||
},
|
||||
|
||||
escapeAddonURI: function AM_escapeAddonURI(aAddon, aUri, aAppVersion) {
|
||||
return AddonManagerInternal.escapeAddonURI(aAddon, aUri, aAppVersion);
|
||||
}
|
||||
|
@ -2672,7 +2672,7 @@ var gDetailView = {
|
||||
downloadsRow.value = null;
|
||||
}
|
||||
|
||||
var canUpdate = !aIsRemote && hasPermission(aAddon, "upgrade");
|
||||
var canUpdate = !aIsRemote && hasPermission(aAddon, "upgrade") && aAddon.id != AddonManager.hotfixID;
|
||||
document.getElementById("detail-updates-row").hidden = !canUpdate;
|
||||
|
||||
if ("applyBackgroundUpdates" in aAddon) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
const PREF_AUTOUPDATE_DEFAULT = "extensions.update.autoUpdateDefault"
|
||||
const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
|
||||
const SEARCH_URL = TESTROOT + "browser_details.xml";
|
||||
const PREF_EM_HOTFIX_ID = "extensions.hotfix.id";
|
||||
|
||||
var gManagerWindow;
|
||||
var gCategoryUtilities;
|
||||
@ -47,6 +48,7 @@ function test() {
|
||||
// Turn on searching for this test
|
||||
Services.prefs.setIntPref(PREF_SEARCH_MAXRESULTS, 15);
|
||||
Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS, SEARCH_URL);
|
||||
Services.prefs.setCharPref(PREF_EM_HOTFIX_ID, "hotfix@tests.mozilla.org");
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
@ -141,6 +143,9 @@ function test() {
|
||||
blocklistURL: "http://example.com/addon8@tests.mozilla.org",
|
||||
name: "Test add-on 8",
|
||||
blocklistState: Ci.nsIBlocklistService.STATE_OUTDATED
|
||||
}, {
|
||||
id: "hotfix@tests.mozilla.org",
|
||||
name: "Test hotfix 1",
|
||||
}]);
|
||||
|
||||
open_manager(null, function(aWindow) {
|
||||
@ -152,6 +157,7 @@ function test() {
|
||||
}
|
||||
|
||||
function end_test() {
|
||||
Services.prefs.clearUserPref(PREF_EM_HOTFIX_ID);
|
||||
close_manager(gManagerWindow, function() {
|
||||
finish();
|
||||
});
|
||||
@ -175,6 +181,7 @@ add_test(function() {
|
||||
is_element_visible(get("detail-contrib-suggested"), "Contributions amount should be visible");
|
||||
ok(get("detail-contrib-suggested").value, "$0.99");
|
||||
|
||||
is_element_visible(get("detail-updates-row"), "Updates should not be hidden");
|
||||
is_element_hidden(get("detail-dateUpdated"), "Update date should be hidden");
|
||||
|
||||
is_element_visible(get("detail-rating-row"), "Rating row should not be hidden");
|
||||
@ -336,6 +343,7 @@ add_test(function() {
|
||||
|
||||
is_element_hidden(get("detail-contributions"), "Contributions section should be hidden");
|
||||
|
||||
is_element_visible(get("detail-updates-row"), "Updates should not be hidden");
|
||||
is_element_visible(get("detail-dateUpdated"), "Update date should not be hidden");
|
||||
is(get("detail-dateUpdated").value, formatDate(gDate), "Update date should be correct");
|
||||
|
||||
@ -678,6 +686,26 @@ add_test(function() {
|
||||
});
|
||||
});
|
||||
|
||||
// Opens and tests the details view for hotfix 1
|
||||
add_test(function() {
|
||||
open_details("hotfix@tests.mozilla.org", "extension", function() {
|
||||
is(get("detail-name").textContent, "Test hotfix 1", "Name should be correct");
|
||||
|
||||
is_element_hidden(get("detail-updates-row"), "Updates should be hidden");
|
||||
|
||||
is_element_hidden(get("detail-prefs-btn"), "Preferences button should be hidden");
|
||||
is_element_hidden(get("detail-enable-btn"), "Enable button should be hidden");
|
||||
is_element_visible(get("detail-disable-btn"), "Disable button should be visible");
|
||||
is_element_visible(get("detail-uninstall-btn"), "Remove button should be visible");
|
||||
|
||||
is_element_hidden(get("detail-warning"), "Warning message should be hidden");
|
||||
is_element_hidden(get("detail-warning-link"), "Warning link should be hidden");
|
||||
is_element_hidden(get("detail-pending"), "Pending message should be hidden");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
// Tests that upgrades with onExternalInstall apply immediately
|
||||
add_test(function() {
|
||||
open_details("addon1@tests.mozilla.org", "extension", function() {
|
||||
|
@ -205,6 +205,21 @@ function run_test() {
|
||||
|
||||
gManagerEventsListener.shutdown();
|
||||
|
||||
// AddonManager.hotfixID
|
||||
let hotfixID = "hotfix@tests.mozilla.org";
|
||||
Services.prefs.setCharPref("extensions.hotfix.id", hotfixID);
|
||||
do_check_eq(AddonManager.hotfixID, hotfixID);
|
||||
// Change the pref and make sure the property is updated
|
||||
hotfixID = "hotfix2@tests.mozilla.org";
|
||||
Services.prefs.setCharPref("extensions.hotfix.id", hotfixID);
|
||||
do_check_eq(AddonManager.hotfixID, hotfixID);
|
||||
// Test an invalid pref value
|
||||
hotfixID = 99;
|
||||
Services.prefs.deleteBranch("extensions.hotfix.id");
|
||||
Services.prefs.setIntPref("extensions.hotfix.id", hotfixID);
|
||||
do_check_eq(AddonManager.hotfixID, null);
|
||||
Services.prefs.clearUserPref("extensions.hotfix.id");
|
||||
|
||||
// After removing the listener, ensure we get no further events.
|
||||
gManagerEventsListener.expect([]);
|
||||
AddonManager.updateEnabled = false;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user