Merge from mozilla-central.
Merge from mozilla-central.
--- a/accessible/src/atk/ApplicationAccessibleWrap.cpp
+++ b/accessible/src/atk/ApplicationAccessibleWrap.cpp
@@ -567,17 +567,17 @@ toplevel_event_watcher(GSignalInvocation
}
}
}
return TRUE;
}
-bool
+void
ApplicationAccessibleWrap::Init()
{
if (ShouldA11yBeEnabled()) {
// load and initialize gail library
nsresult rv = LoadGtkModule(sGail);
if (NS_SUCCEEDED(rv)) {
(*sGail.init)();
}
@@ -610,17 +610,17 @@ ApplicationAccessibleWrap::Init()
reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW), NULL);
sToplevel_hide_hook =
g_signal_add_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW),
0, toplevel_event_watcher,
reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_HIDE), NULL);
}
}
- return ApplicationAccessible::Init();
+ ApplicationAccessible::Init();
}
void
ApplicationAccessibleWrap::Unload()
{
if (sToplevel_event_hook_added) {
sToplevel_event_hook_added = false;
g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
--- a/accessible/src/atk/ApplicationAccessibleWrap.h
+++ b/accessible/src/atk/ApplicationAccessibleWrap.h
@@ -18,17 +18,17 @@ public:
static void Unload();
static void PreCreate();
public:
ApplicationAccessibleWrap();
virtual ~ApplicationAccessibleWrap();
// nsAccessNode
- virtual bool Init();
+ virtual void Init();
// Accessible
virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
virtual bool AppendChild(Accessible* aChild);
virtual bool RemoveChild(Accessible* aChild);
/**
* Return the atk object for app root accessible.
--- a/accessible/src/base/nsAccDocManager.cpp
+++ b/accessible/src/base/nsAccDocManager.cpp
@@ -383,20 +383,17 @@ nsAccDocManager::CreateDocOrRootAccessib
nsRefPtr<DocAccessible> docAcc = isRootDoc ?
new RootAccessibleWrap(aDocument, rootElm, presShell) :
new DocAccessibleWrap(aDocument, rootElm, presShell);
// Cache the document accessible into document cache.
mDocAccessibleCache.Put(aDocument, docAcc);
// Initialize the document accessible.
- if (!docAcc->Init()) {
- docAcc->Shutdown();
- return nullptr;
- }
+ docAcc->Init();
docAcc->SetRoleMapEntry(aria::GetRoleMap(aDocument));
// Bind the document to the tree.
if (isRootDoc) {
Accessible* appAcc = nsAccessNode::GetApplicationAccessible();
if (!appAcc->AppendChild(docAcc)) {
docAcc->Shutdown();
return nullptr;
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -91,22 +91,17 @@ nsAccessNode::GetApplicationAccessible()
if (!gApplicationAccessible) {
ApplicationAccessibleWrap::PreCreate();
gApplicationAccessible = new ApplicationAccessibleWrap();
// Addref on create. Will Release in ShutdownXPAccessibility()
NS_ADDREF(gApplicationAccessible);
- nsresult rv = gApplicationAccessible->Init();
- if (NS_FAILED(rv)) {
- gApplicationAccessible->Shutdown();
- NS_RELEASE(gApplicationAccessible);
- return nullptr;
- }
+ gApplicationAccessible->Init();
}
return gApplicationAccessible;
}
void nsAccessNode::ShutdownXPAccessibility()
{
// Called by nsAccessibilityService::Shutdown()
--- a/accessible/src/base/nsTextEquivUtils.cpp
+++ b/accessible/src/base/nsTextEquivUtils.cpp
@@ -6,16 +6,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsTextEquivUtils.h"
#include "Accessible-inl.h"
#include "AccIterator.h"
#include "nsAccessibilityService.h"
#include "nsAccUtils.h"
+#include "nsStyleStructInlines.h"
#include "nsIDOMXULLabeledControlEl.h"
#include "nsArrayUtils.h"
using namespace mozilla::a11y;
#define NS_OK_NO_NAME_CLAUSE_HANDLED \
@@ -121,17 +122,17 @@ nsTextEquivUtils::AppendTextEquivFromTex
nsIContent *parentContent = aContent->GetParent();
if (parentContent) {
nsIFrame *frame = parentContent->GetPrimaryFrame();
if (frame) {
// If this text is inside a block level frame (as opposed to span
// level), we need to add spaces around that block's text, so we don't
// get words jammed together in final name.
const nsStyleDisplay* display = frame->GetStyleDisplay();
- if (display->IsBlockOutside() ||
+ if (display->IsBlockOutsideStyle() ||
display->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL) {
isHTMLBlock = true;
if (!aString->IsEmpty()) {
aString->Append(PRUnichar(' '));
}
}
}
}
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -190,20 +190,19 @@ Accessible::~Accessible()
}
void
Accessible::SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry)
{
mRoleMapEntry = aRoleMapEntry;
}
-bool
+void
Accessible::Init()
{
- return true;
}
NS_IMETHODIMP
Accessible::GetDocument(nsIAccessibleDocument** aDocument)
{
NS_ENSURE_ARG_POINTER(aDocument);
NS_IF_ADDREF(*aDocument = Document());
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -124,17 +124,17 @@ public:
virtual void Shutdown();
//////////////////////////////////////////////////////////////////////////////
// Public methods
/**
* Initialize the accessible.
*/
- virtual bool Init();
+ virtual void Init();
/**
* Get the description of this accessible.
*/
virtual void Description(nsString& aDescription);
/**
* Get the value of this accessible.
--- a/accessible/src/generic/ApplicationAccessible.cpp
+++ b/accessible/src/generic/ApplicationAccessible.cpp
@@ -263,21 +263,20 @@ ApplicationAccessible::GetPlatformVersio
AppendUTF8toUTF16(cversion, aVersion);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsAccessNode public methods
-bool
+void
ApplicationAccessible::Init()
{
mAppInfo = do_GetService("@mozilla.org/xre/app-info;1");
- return true;
}
void
ApplicationAccessible::Shutdown()
{
mAppInfo = nullptr;
}
--- a/accessible/src/generic/ApplicationAccessible.h
+++ b/accessible/src/generic/ApplicationAccessible.h
@@ -56,17 +56,17 @@ public:
NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString &aName);
NS_IMETHOD GetActionDescription(PRUint8 aIndex, nsAString &aDescription);
NS_IMETHOD DoAction(PRUint8 aIndex);
// nsIAccessibleApplication
NS_DECL_NSIACCESSIBLEAPPLICATION
// nsAccessNode
- virtual bool Init();
+ virtual void Init();
virtual void Shutdown();
virtual bool IsPrimaryForNode() const;
// Accessible
virtual GroupPos GroupPosition();
virtual ENameValueFlag Name(nsString& aName);
virtual void ApplyARIAState(PRUint64* aState) const;
virtual void Description(nsString& aDescription);
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -599,37 +599,34 @@ DocAccessible::GetAccessible(nsINode* aN
#endif
return accessible;
}
////////////////////////////////////////////////////////////////////////////////
// nsAccessNode
-bool
+void
DocAccessible::Init()
{
#ifdef DEBUG
if (logging::IsEnabled(logging::eDocCreate))
logging::DocCreate("document initialize", mDocument, this);
#endif
// Initialize notification controller.
mNotificationController = new NotificationController(this, mPresShell);
- if (!mNotificationController)
- return false;
// Mark the document accessible as loaded if its DOM document was loaded at
// this point (this can happen because a11y is started late or DOM document
// having no container was loaded.
if (mDocument->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE)
mLoadState |= eDOMLoaded;
AddEventListeners();
- return true;
}
void
DocAccessible::Shutdown()
{
if (!mPresShell) // already shutdown
return;
@@ -1392,22 +1389,17 @@ DocAccessible::BindToDocument(Accessible
// Put into DOM node cache.
if (aAccessible->IsPrimaryForNode())
mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible);
// Put into unique ID cache.
mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible);
// Initialize the accessible.
- if (!aAccessible->Init()) {
- NS_ERROR("Failed to initialize an accessible!");
-
- UnbindFromDocument(aAccessible);
- return false;
- }
+ aAccessible->Init();
aAccessible->SetRoleMapEntry(aRoleMapEntry);
if (aAccessible->IsElement())
AddDependentIDsFor(aAccessible);
return true;
}
--- a/accessible/src/generic/DocAccessible.h
+++ b/accessible/src/generic/DocAccessible.h
@@ -66,17 +66,17 @@ public:
// nsIScrollPositionListener
virtual void ScrollPositionWillChange(nscoord aX, nscoord aY) {}
virtual void ScrollPositionDidChange(nscoord aX, nscoord aY);
// nsIDocumentObserver
NS_DECL_NSIDOCUMENTOBSERVER
// nsAccessNode
- virtual bool Init();
+ virtual void Init();
virtual void Shutdown();
virtual nsIFrame* GetFrame() const;
virtual nsINode* GetNode() const { return mDocument; }
virtual nsIDocument* GetDocumentNode() const { return mDocument; }
// Accessible
virtual mozilla::a11y::ENameValueFlag Name(nsString& aName);
virtual void Description(nsString& aDescription);
--- a/accessible/src/xul/XULTreeAccessible.cpp
+++ b/accessible/src/xul/XULTreeAccessible.cpp
@@ -1145,24 +1145,21 @@ XULTreeItemAccessible::Name(nsString& aN
GetCellName(mColumn, aName);
return eNameOK;
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessible: nsAccessNode implementation
-bool
+void
XULTreeItemAccessible::Init()
{
- if (!XULTreeItemAccessibleBase::Init())
- return false;
-
+ XULTreeItemAccessibleBase::Init();
Name(mCachedName);
- return true;
}
void
XULTreeItemAccessible::Shutdown()
{
mColumn = nullptr;
XULTreeItemAccessibleBase::Shutdown();
}
--- a/accessible/src/xul/XULTreeAccessible.h
+++ b/accessible/src/xul/XULTreeAccessible.h
@@ -230,17 +230,17 @@ public:
nsITreeView* aTreeView, PRInt32 aRow);
// nsISupports and cycle collection
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeItemAccessible,
XULTreeItemAccessibleBase)
// nsAccessNode
- virtual bool Init();
+ virtual void Init();
virtual void Shutdown();
// Accessible
virtual ENameValueFlag Name(nsString& aName);
virtual a11y::role NativeRole();
// XULTreeItemAccessibleBase
virtual void RowInvalidated(PRInt32 aStartColIdx, PRInt32 aEndColIdx);
--- a/accessible/src/xul/XULTreeGridAccessible.cpp
+++ b/accessible/src/xul/XULTreeGridAccessible.cpp
@@ -774,30 +774,29 @@ XULTreeGridCellAccessible::IsSelected(bo
NS_ENSURE_SUCCESS(rv, rv);
return selection->IsSelected(mRow, aIsSelected);
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeGridCellAccessible: nsAccessNode implementation
-bool
+void
XULTreeGridCellAccessible::Init()
{
- if (!LeafAccessible::Init() || !mTreeView)
- return false;
+ LeafAccessible::Init();
+
+ NS_ASSERTION(mTreeView, "mTreeView is null");
PRInt16 type;
mColumn->GetType(&type);
if (type == nsITreeColumn::TYPE_CHECKBOX)
mTreeView->GetCellValue(mRow, mColumn, mCachedTextEquiv);
else
mTreeView->GetCellText(mRow, mColumn, mCachedTextEquiv);
-
- return true;
}
bool
XULTreeGridCellAccessible::IsPrimaryForNode() const
{
return false;
}
--- a/accessible/src/xul/XULTreeGridAccessible.h
+++ b/accessible/src/xul/XULTreeGridAccessible.h
@@ -145,17 +145,17 @@ public:
NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
NS_IMETHOD DoAction(PRUint8 aIndex);
// nsIAccessibleTableCell
NS_DECL_OR_FORWARD_NSIACCESSIBLETABLECELL_WITH_XPCACCESSIBLETABLECELL
// nsAccessNode
- virtual bool Init();
+ virtual void Init();
virtual bool IsPrimaryForNode() const;
// Accessible
virtual void Shutdown();
virtual ENameValueFlag Name(nsString& aName);
virtual Accessible* FocusedChild();
virtual nsresult GetAttributesInternal(nsIPersistentProperties* aAttributes);
virtual PRInt32 IndexInParent() const;
--- a/accessible/tests/mochitest/name/markuprules.xml
+++ b/accessible/tests/mochitest/name/markuprules.xml
@@ -197,16 +197,17 @@
aria-labelledby="l1 l2"
label="test4"
title="test5"
a11yname="option1">option1</html:option>
<html:option>option2</html:option>
</html:select>
</markup>
+<!-- Temporarily disabled for causing bug 733848
<markup ref="html:img" ruleset="htmlimage">
<html:span id="l1" a11yname="test2">test2</html:span>
<html:span id="l2" a11yname="test3">test3</html:span>
<html:img id="img"
aria-label="Logo of Mozilla"
aria-labelledby="l1 l2"
alt="Mozilla logo"
title="This is a logo"
@@ -218,16 +219,17 @@
<html:span id="l2" a11yname="test3">test3</html:span>
<html:img id="img"
aria-label="Logo of Mozilla"
aria-labelledby="l1 l2"
title="This is a logo"
alt=""
src="../moz.png"/>
</markup>
+-->
<markup ref="html:table/html:tr/html:td" ruleset="htmlelm"
id="markup4test">
<html:span id="l1" a11yname="test2">test2</html:span>
<html:span id="l2" a11yname="test3">test3</html:span>
<html:label for="tc" a11yname="test4">test4</html:label>
<html:table>
<html:tr>
--- a/b2g/app/nsBrowserApp.cpp
+++ b/b2g/app/nsBrowserApp.cpp
@@ -169,29 +169,22 @@ int main(int argc, char* argv[])
strcpy(++lastSlash, XPCOM_DLL);
int gotCounters;
#if defined(XP_UNIX)
struct rusage initialRUsage;
gotCounters = !getrusage(RUSAGE_SELF, &initialRUsage);
#elif defined(XP_WIN)
- // GetProcessIoCounters().ReadOperationCount seems to have little to
- // do with actual read operations. It reports 0 or 1 at this stage
- // in the program. Luckily 1 coincides with when prefetch is
- // enabled. If Windows prefetch didn't happen we can do our own
- // faster dll preloading.
IO_COUNTERS ioCounters;
gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
- if (gotCounters && !ioCounters.ReadOperationCount)
#endif
- {
- XPCOMGlueEnablePreload();
- }
+ // We do this because of data in bug 771745
+ XPCOMGlueEnablePreload();
rv = XPCOMGlueStartup(exePath);
if (NS_FAILED(rv)) {
Output("Couldn't load XPCOM.\n");
return 255;
}
// Reset exePath so that it is the directory name and not the xpcom dll name
*lastSlash = 0;
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -169,48 +169,16 @@ static int do_main(int argc, char* argv[
int result = XRE_main(argc, argv, appData, 0);
XRE_FreeAppData(appData);
return result;
}
return XRE_main(argc, argv, &sAppData, 0);
}
-#ifdef XP_WIN
-/**
- * Determines if the registry is disabled via the service or not.
- *
- * @return true if prefetch is disabled
- * false if prefetch is not disabled or an error occurred.
-*/
-bool IsPrefetchDisabledViaService()
-{
- // We don't need to return false when we don't have MOZ_MAINTENANCE_SERVICE
- // defined. The reason is because another product installed that has it
- // defined may have cleared our prefetch for us. There is no known way
- // to figure out which prefetch files are associated with which apps
- // because of the prefetch hash. So we disable all of them that start
- // with FIREFOX.
- HKEY baseKey;
- LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
- L"SOFTWARE\\Mozilla\\MaintenanceService", 0,
- KEY_READ | KEY_WOW64_64KEY, &baseKey);
- if (retCode != ERROR_SUCCESS) {
- return false;
- }
- DWORD disabledValue = 0;
- DWORD disabledValueSize = sizeof(DWORD);
- RegQueryValueExW(baseKey, L"FFPrefetchDisabled", 0, NULL,
- reinterpret_cast<LPBYTE>(&disabledValue),
- &disabledValueSize);
- RegCloseKey(baseKey);
- return disabledValue == 1;
-}
-#endif
-
/* Local implementation of PR_Now, since the executable can't depend on NSPR */
static PRTime _PR_Now()
{
#ifdef XP_WIN
MOZ_STATIC_ASSERT(sizeof(PRTime) == sizeof(FILETIME), "PRTime must have the same size as FILETIME");
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
PRTime now;
@@ -253,19 +221,18 @@ int main(int argc, char* argv[])
#if defined(XP_UNIX)
struct rusage initialRUsage;
gotCounters = !getrusage(RUSAGE_SELF, &initialRUsage);
#elif defined(XP_WIN)
IO_COUNTERS ioCounters;
gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
#endif
-#if !defined(XP_WIN)
+ // We do this because of data in bug 771745
XPCOMGlueEnablePreload();
-#endif
rv = XPCOMGlueStartup(exePath);
if (NS_FAILED(rv)) {
Output("Couldn't load XPCOM.\n");
return 255;
}
// Reset exePath so that it is the directory name and not the xpcom dll name
*lastSlash = 0;
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -72,24 +72,27 @@ const gXPInstallObserver = {
PopupNotifications.show(browser, notificationID, messageString, anchorID,
action, null, options);
break;
case "addon-install-blocked":
messageString = gNavigatorBundle.getFormattedString("xpinstallPromptWarning",
[brandShortName, installInfo.originatingURI.host]);
+ let secHistogram = Components.classes["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry).getHistogramById("SECURITY_UI");
action = {
label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
callback: function() {
+ secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED_CLICK_THROUGH);
installInfo.install();
}
};
+ secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED);
PopupNotifications.show(browser, notificationID, messageString, anchorID,
action, null, options);
break;
case "addon-install-started":
function needsDownload(aInstall) {
return aInstall.state != AddonManager.STATE_DOWNLOADED;
}
// If all installs have already been downloaded then there is no need to
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -155,20 +155,20 @@ let SocialShareButton = {
init: function SSB_init() {
this.updateButtonHiddenState();
this.updateProfileInfo();
},
updateProfileInfo: function SSB_updateProfileInfo() {
let profileRow = document.getElementById("editSharePopupHeader");
let profile = Social.provider.profile;
- if (profile && profile.portrait && profile.displayName) {
+ if (profile && profile.displayName) {
profileRow.hidden = false;
let portrait = document.getElementById("socialUserPortrait");
- portrait.style.listStyleImage = profile.portrait;
+ portrait.setAttribute("src", profile.portrait || "chrome://browser/skin/social/social.png");
let displayName = document.getElementById("socialUserDisplayName");
displayName.setAttribute("label", profile.displayName);
} else {
profileRow.hidden = true;
}
},
get shareButton() {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2514,19 +2514,23 @@ let BrowserOnClick = {
}
else if (/^about:home$/i.test(ownerDoc.documentURI)) {
this.onAboutHome(originalTarget, ownerDoc);
}
},
onAboutCertError: function BrowserOnClick_onAboutCertError(aTargetElm, aOwnerDoc) {
let elmId = aTargetElm.getAttribute("id");
+ let secHistogram = Cc["@mozilla.org/base/telemetry;1"].
+ getService(Ci.nsITelemetry).
+ getHistogramById("SECURITY_UI");
switch (elmId) {
case "exceptionDialogButton":
+ secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_CLICK_ADD_EXCEPTION);
let params = { exceptionAdded : false, handlePrivateBrowsing : true };
try {
switch (Services.prefs.getIntPref("browser.ssl_override_behavior")) {
case 2 : // Pre-fetch & pre-populate
params.prefetchCert = true;
case 1 : // Pre-populate
params.location = aOwnerDoc.location.href;
@@ -2540,39 +2544,59 @@ let BrowserOnClick = {
// If the user added the exception cert, attempt to reload the page
if (params.exceptionAdded) {
aOwnerDoc.location.reload();
}
break;
case "getMeOutOfHereButton":
+ secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_GET_ME_OUT_OF_HERE);
getMeOutOfHere();
break;
+
+ case "technicalContent":
+ secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TECHNICAL_DETAILS);
+ break;
+
+ case "expertContent":
+ secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_UNDERSTAND_RISKS);
+ break;
+
}
},
onAboutBlocked: function BrowserOnClick_onAboutBlocked(aTargetElm, aOwnerDoc) {
let elmId = aTargetElm.getAttribute("id");
+ let secHistogram = Cc["@mozilla.org/base/telemetry;1"].
+ getService(Ci.nsITelemetry).
+ getHistogramById("SECURITY_UI");
// The event came from a button on a malware/phishing block page
// First check whether it's malware or phishing, so that we can
// use the right strings/links
let isMalware = /e=malwareBlocked/.test(aOwnerDoc.documentURI);
+ let bucketName = isMalware ? "WARNING_MALWARE_PAGE_":"WARNING_PHISHING_PAGE_";
+ let nsISecTel = Ci.nsISecurityUITelemetry;
switch (elmId) {
case "getMeOutButton":
+ secHistogram.add(nsISecTel[bucketName + "GET_ME_OUT_OF_HERE"]);
getMeOutOfHere();
break;
case "reportButton":
// This is the "Why is this site blocked" button. For malware,
// we can fetch a site-specific report, for phishing, we redirect
// to the generic page describing phishing protection.
+ // We log even if malware/phishing info URL couldn't be found:
+ // the measurement is for how many users clicked the WHY BLOCKED button
+ secHistogram.add(nsISecTel[bucketName + "WHY_BLOCKED"]);
+
if (isMalware) {
// Get the stop badware "why is this blocked" report url,
// append the current url, and go there.
try {
let reportURL = formatURL("browser.safebrowsing.malware.reportURL", true);
reportURL += aOwnerDoc.location.href;
content.location = reportURL;
} catch (e) {
@@ -2584,16 +2608,17 @@ let BrowserOnClick = {
content.location = formatURL("browser.safebrowsing.warning.infoURL", true);
} catch (e) {
Components.utils.reportError("Couldn't get phishing info URL: " + e);
}
}
break;
case "ignoreWarningButton":
+ secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]);
this.ignoreWarningButton(isMalware);
break;
}
},
ignoreWarningButton: function BrowserOnClick_ignoreWarningButton(aIsMalware) {
// Allow users to override and continue through to the site,
// but add a notify bar as a reminder, so that they don't lose
--- a/browser/base/content/test/browser_social_toolbar.js
+++ b/browser/base/content/test/browser_social_toolbar.js
@@ -18,25 +18,25 @@ function test() {
SocialService.removeProvider(Social.provider.origin, finish);
});
});
}
var tests = {
testProfileSet: function(next) {
let profile = {
- portrait: "chrome://branding/content/icon48.png",
+ portrait: "https://example.com/portrait.jpg",
userName: "trickster",
displayName: "Kuma Lisa",
profileURL: "http://en.wikipedia.org/wiki/Kuma_Lisa"
}
Social.provider.updateUserProfile(profile);
// check dom values
let portrait = document.getElementById("social-statusarea-user-portrait").getAttribute("src");
- is(portrait, profile.portrait, "portrait is set");
+ is(profile.portrait, portrait, "portrait is set");
let userButton = document.getElementById("social-statusarea-username");
ok(!userButton.hidden, "username is visible");
is(userButton.label, profile.userName, "username is set");
next();
},
testAmbientNotifications: function(next) {
let ambience = {
name: "testIcon",
--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -47,21 +47,16 @@
#define REG_SUCCEEDED(val) \
(val == ERROR_SUCCESS)
#define REG_FAILED(val) \
(val != ERROR_SUCCESS)
#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
-// We clear the prefetch files one time after the browser is started after
-// 3 minutes. After this is done once we set a pref so this will never happen
-// again except in updater code.
-#define CLEAR_PREFETCH_TIMEOUT_MS 180000
-
NS_IMPL_ISUPPORTS2(nsWindowsShellService, nsIWindowsShellService, nsIShellService)
static nsresult
OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey)
{
const nsString &flatName = PromiseFlatString(aKeyName);
DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
@@ -189,27 +184,16 @@ static SETTING gDDESettings[] = {
{ MAKE_KEY_NAME1("Software\\Classes\\FirefoxURL", SOD) },
// Protocol Handlers
{ MAKE_KEY_NAME1("Software\\Classes\\FTP", SOD) },
{ MAKE_KEY_NAME1("Software\\Classes\\HTTP", SOD) },
{ MAKE_KEY_NAME1("Software\\Classes\\HTTPS", SOD) }
};
-#if defined(MOZ_MAINTENANCE_SERVICE)
-
-#define ONLY_SERVICE_LAUNCHING
-#include "updatehelper.h"
-#include "updatehelper.cpp"
-
-static const char *kPrefetchClearedPref =
- "app.update.service.lastVersionPrefetchCleared";
-static nsCOMPtr<nsIThread> sThread;
-#endif
-
nsresult
GetHelperPath(nsAutoString& aPath)
{
nsresult rv;
nsCOMPtr<nsIProperties> directoryService =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
@@ -996,133 +980,22 @@ nsWindowsShellService::SetDesktopBackgro
NS_ENSURE_SUCCESS(rv, rv);
return regKey->Close();
}
nsWindowsShellService::nsWindowsShellService() :
mCheckedThisSession(false)
{
-#if defined(MOZ_MAINTENANCE_SERVICE)
-
- // Check to make sure the service is installed
- PRUint32 installed = 0;
- nsCOMPtr<nsIWindowsRegKey> regKey =
- do_CreateInstance("@mozilla.org/windows-registry-key;1");
- if (!regKey ||
- NS_FAILED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
- NS_LITERAL_STRING(
- "SOFTWARE\\Mozilla\\MaintenanceService"),
- nsIWindowsRegKey::ACCESS_READ |
- nsIWindowsRegKey::WOW64_64)) ||
- NS_FAILED(regKey->ReadIntValue(NS_LITERAL_STRING("Installed"),
- &installed)) ||
- !installed) {
- return;
- }
-
- // check to see if we have attempted to do the one time operation of clearing
- // the prefetch. We do it once per version upgrade.
- nsCString lastClearedVer;
- nsCOMPtr<nsIPrefBranch> prefBranch;
- nsCOMPtr<nsIPrefService> prefs =
- do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (!prefs ||
- NS_FAILED(prefs->GetBranch(nullptr, getter_AddRefs(prefBranch))) ||
- (NS_SUCCEEDED(prefBranch->GetCharPref(kPrefetchClearedPref,
- getter_Copies(lastClearedVer))))) {
- // If the versions are the same, then bail out early. We only want to clear
- // once per version.
- if (!strcmp(MOZ_APP_VERSION, lastClearedVer.get())) {
- return;
- }
- }
-
- // In a minute after startup is definitely complete, launch the
- // service command.
- mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
- if (mTimer) {
- mTimer->InitWithFuncCallback(
- nsWindowsShellService::LaunchPrefetchClearCommand,
- nullptr, CLEAR_PREFETCH_TIMEOUT_MS, nsITimer::TYPE_ONE_SHOT);
- }
-#endif
}
nsWindowsShellService::~nsWindowsShellService()
{
-#if defined(MOZ_MAINTENANCE_SERVICE)
- if (mTimer) {
- mTimer->Cancel();
- mTimer = nullptr;
- }
- if (sThread) {
- sThread->Shutdown();
- sThread = nullptr;
- }
-#endif
}
-#if defined(MOZ_MAINTENANCE_SERVICE)
-
-class ClearPrefetchEvent : public nsRunnable {
-public:
- ClearPrefetchEvent()
- {
- }
-
- NS_IMETHOD Run()
- {
- // Start the service command
- LPCWSTR updaterServiceArgv[2];
- updaterServiceArgv[0] = L"MozillaMaintenance";
- updaterServiceArgv[1] = L"clear-prefetch";
- // If this command fails, it is not critical as prefetch will be cleared
- // on the next software update.
- StartServiceCommand(NS_ARRAY_LENGTH(updaterServiceArgv),
- updaterServiceArgv);
- return NS_OK;
- }
-};
-#endif
-
-/**
- * For faster startup we attempt to clear the prefetch if the maintenance
- * service is installed. Please see the definition of ClearPrefetch()
- * in toolkit/components/maintenanceservice/prefetch.cpp for more info.
- * For now the only application that gets prefetch cleaned is Firefox
- * since we have not done performance checking for other applications.
- * This is done on every update but also there is a one time operation done
- * from within the program for first time installs.
- */
-#if defined(MOZ_MAINTENANCE_SERVICE)
-void
-nsWindowsShellService::LaunchPrefetchClearCommand(nsITimer *aTimer, void*)
-{
- // Make sure we don't call this again from the application, it will be
- // called on each application update instead.
- nsCOMPtr<nsIPrefBranch> prefBranch;
- nsCOMPtr<nsIPrefService> prefs =
- do_GetService(NS_PREFSERVICE_CONTRACTID);
- if (prefs) {
- if (NS_SUCCEEDED(prefs->GetBranch(nullptr, getter_AddRefs(prefBranch)))) {
- prefBranch->SetCharPref(kPrefetchClearedPref, MOZ_APP_VERSION);
- }
- }
-
- // Starting the sevice can take a bit of time and we don't want to block the
- // main thread, so start an event on another thread to handle the operation
- NS_NewThread(getter_AddRefs(sThread));
- if (sThread) {
- nsCOMPtr<nsIRunnable> prefetchEvent = new ClearPrefetchEvent();
- sThread->Dispatch(prefetchEvent, NS_DISPATCH_NORMAL);
- }
-}
-#endif
-
NS_IMETHODIMP
nsWindowsShellService::OpenApplicationWithURI(nsIFile* aApplication,
const nsACString& aURI)
{
nsresult rv;
nsCOMPtr<nsIProcess> process =
do_CreateInstance("@mozilla.org/process/util;1", &rv);
if (NS_FAILED(rv))
--- a/browser/components/shell/src/nsWindowsShellService.h
+++ b/browser/components/shell/src/nsWindowsShellService.h
@@ -24,16 +24,12 @@ public:
NS_DECL_NSISHELLSERVICE
NS_DECL_NSIWINDOWSSHELLSERVICE
protected:
static nsresult IsDefaultBrowser(bool* aIsDefaultBrowser);
static bool IsDefaultBrowserVista(bool* aIsDefaultBrowser);
private:
-#if defined(MOZ_MAINTENANCE_SERVICE)
- static void LaunchPrefetchClearCommand(nsITimer *aTimer, void*);
- nsCOMPtr<nsITimer> mTimer;
-#endif
bool mCheckedThisSession;
};
#endif // nswindowsshellservice_h____
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -903,16 +903,21 @@ SourceScripts.prototype = {
_onNewScript: function SS__onNewScript(aNotification, aPacket) {
// Ignore scripts generated from 'clientEvaluate' packets.
if (aPacket.url == "debugger eval code") {
return;
}
this._addScript({ url: aPacket.url, startLine: aPacket.startLine }, true);
+ // Select the script if it's the preferred one.
+ if (aPacket.url === DebuggerView.Scripts.preferredScriptUrl) {
+ DebuggerView.Scripts.selectScript(aPacket.url);
+ }
+
// If there are any stored breakpoints for this script, display them again,
// both in the editor and the pane.
for each (let breakpoint in DebuggerController.Breakpoints.store) {
if (breakpoint.location.url == aPacket.url) {
DebuggerController.Breakpoints.displayBreakpoint(breakpoint);
}
}
},
@@ -921,16 +926,24 @@ SourceScripts.prototype = {
* Handler for the thread client's scriptsadded notification.
*/
_onScriptsAdded: function SS__onScriptsAdded() {
for each (let script in this.activeThread.cachedScripts) {
this._addScript(script, false);
}
DebuggerView.Scripts.commitScripts();
DebuggerController.Breakpoints.updatePaneBreakpoints();
+
+ // Select the preferred script if one exists, the first entry otherwise.
+ let preferredScriptUrl = DebuggerView.Scripts.preferredScriptUrl;
+ if (preferredScriptUrl) {
+ DebuggerView.Scripts.selectScript(preferredScriptUrl);
+ } else {
+ DebuggerView.Scripts.selectIndex(0);
+ }
},
/**
* Handler for the thread client's scriptscleared notification.
*/
_onScriptsCleared: function SS__onScriptsCleared() {
DebuggerView.Scripts.empty();
DebuggerView.Breakpoints.emptyText();
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -235,16 +235,26 @@ ScriptsView.prototype = {
}
if (this._scripts.getElementsByAttribute("label", aLabel).length > 0) {
return true;
}
return false;
},
/**
+ * Selects the script with the specified index from the list.
+ *
+ * @param number aIndex
+ * The script index.
+ */
+ selectIndex: function DVS_selectIndex(aIndex) {
+ this._scripts.selectedIndex = aIndex;
+ },
+
+ /**
* Selects the script with the specified URL from the list.
*
* @param string aUrl
* The script URL.
*/
selectScript: function DVS_selectScript(aUrl) {
for (let i = 0, l = this._scripts.itemCount; i < l; i++) {
if (this._scripts.getItemAtIndex(i).value == aUrl) {
@@ -273,16 +283,23 @@ ScriptsView.prototype = {
* @return string | null
*/
get selected() {
return this._scripts.selectedItem ?
this._scripts.selectedItem.value : null;
},
/**
+ * Gets the most recently selected script url.
+ * @return string | null
+ */
+ get preferredScriptUrl()
+ this._preferredScriptUrl ? this._preferredScriptUrl : null,
+
+ /**
* Returns the list of labels in the scripts container.
* @return array
*/
get scriptLabels() {
let labels = [];
for (let i = 0, l = this._scripts.itemCount; i < l; i++) {
labels.push(this._scripts.getItemAtIndex(i).label);
}
@@ -340,17 +357,17 @@ ScriptsView.prototype = {
// Find the target position in the menulist and insert the script there.
for (let i = 0, l = this._scripts.itemCount; i < l; i++) {
if (this._scripts.getItemAtIndex(i).label > aLabel) {
this._createScriptElement(aLabel, aScript, i);
return;
}
}
// The script is alphabetically the last one.
- this._createScriptElement(aLabel, aScript, -1, true);
+ this._createScriptElement(aLabel, aScript, -1);
},
/**
* Adds all the prepared scripts to the scripts container.
* If a script already exists (was previously added), nothing happens.
*/
commitScripts: function DVS_commitScripts() {
let newScripts = this._tmpScripts;
@@ -360,53 +377,45 @@ ScriptsView.prototype = {
return;
}
newScripts.sort(function(a, b) {
return a.label.toLowerCase() > b.label.toLowerCase();
});
for (let i = 0, l = newScripts.length; i < l; i++) {
let item = newScripts[i];
- this._createScriptElement(item.label, item.script, -1, true);
+ this._createScriptElement(item.label, item.script, -1);
}
},
/**
* Creates a custom script element and adds it to the scripts container.
* If the script with the specified label already exists, nothing happens.
*
* @param string aLabel
* The simplified script location to be shown.
* @param string aScript
* The source script.
* @param number aIndex
* The index where to insert to new script in the container.
* Pass -1 to append the script at the end.
- * @param boolean aSelectIfEmptyFlag
- * True to set the newly created script as the currently selected item
- * if there are no other existing scripts in the container.
*/
- _createScriptElement: function DVS__createScriptElement(
- aLabel, aScript, aIndex, aSelectIfEmptyFlag)
+ _createScriptElement: function DVS__createScriptElement(aLabel, aScript, aIndex)
{
// Make sure we don't duplicate anything.
if (aLabel == "null" || this.containsLabel(aLabel) || this.contains(aScript.url)) {
return;
}
let scriptItem =
aIndex == -1 ? this._scripts.appendItem(aLabel, aScript.url)
: this._scripts.insertItemAt(aIndex, aLabel, aScript.url);
scriptItem.setAttribute("tooltiptext", aScript.url);
scriptItem.setUserData("sourceScript", aScript, null);
-
- if (this._scripts.itemCount == 1 && aSelectIfEmptyFlag) {
- this._scripts.selectedItem = scriptItem;
- }
},
/**
* Gets the entered file, line and token entered in the searchbox.
*
* @return array
* A [file, line, token] array.
*/
@@ -432,16 +441,17 @@ ScriptsView.prototype = {
*/
_onScriptsChange: function DVS__onScriptsChange() {
let selectedItem = this._scripts.selectedItem;
if (!selectedItem) {
return;
}
this._preferredScript = selectedItem;
+ this._preferredScriptUrl = selectedItem.value;
this._scripts.setAttribute("tooltiptext", selectedItem.value);
DebuggerController.SourceScripts.showScript(selectedItem.getUserData("sourceScript"));
},
/**
* The search listener for the scripts search box.
*/
_onScriptsSearch: function DVS__onScriptsSearch(e) {
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -30,16 +30,17 @@ MOCHITEST_BROWSER_TESTS = \
browser_dbg_propertyview-04.js \
browser_dbg_propertyview-05.js \
browser_dbg_propertyview-06.js \
browser_dbg_propertyview-07.js \
browser_dbg_propertyview-08.js \
browser_dbg_propertyview-09.js \
browser_dbg_propertyview-10.js \
browser_dbg_propertyview-edit.js \
+ browser_dbg_reload-same-script.js \
browser_dbg_panesize.js \
browser_dbg_panesize-inner.js \
browser_dbg_stack-01.js \
browser_dbg_stack-02.js \
browser_dbg_stack-03.js \
browser_dbg_stack-04.js \
browser_dbg_stack-05.js \
browser_dbg_location-changes.js \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_reload-same-script.js
@@ -0,0 +1,126 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the same script is shown after a page is reloaded.
+ */
+
+const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
+
+let gPane = null;
+let gTab = null;
+let gDebuggee = null;
+let gDebugger = null;
+let gView = null;
+
+function test()
+{
+ let step = 0;
+ let scriptShown = false;
+ let scriptShownUrl = null;
+ let resumed = false;
+ let testStarted = false;
+
+ debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+ gTab = aTab;
+ gDebuggee = aDebuggee;
+ gPane = aPane;
+ gDebugger = gPane.contentWindow;
+ gView = gDebugger.DebuggerView;
+ resumed = true;
+
+ executeSoon(startTest);
+ });
+
+ function onScriptShown(aEvent)
+ {
+ scriptShown = aEvent.detail.url.indexOf("-01.js") != -1;
+ scriptShownUrl = aEvent.detail.url;
+ executeSoon(startTest);
+ }
+
+ function onUlteriorScriptShown(aEvent)
+ {
+ scriptShownUrl = aEvent.detail.url;
+ executeSoon(testScriptShown);
+ }
+
+ window.addEventListener("Debugger:ScriptShown", onScriptShown);
+
+ function startTest()
+ {
+ if (scriptShown && resumed && !testStarted) {
+ window.removeEventListener("Debugger:ScriptShown", onScriptShown);
+ window.addEventListener("Debugger:ScriptShown", onUlteriorScriptShown);
+ testStarted = true;
+ Services.tm.currentThread.dispatch({ run: performTest }, 0);
+ }
+ }
+
+ function finishTest()
+ {
+ if (scriptShown && resumed && testStarted) {
+ window.removeEventListener("Debugger:ScriptShown", onUlteriorScriptShown);
+ closeDebuggerAndFinish();
+ }
+ }
+
+ function performTest()
+ {
+ testCurrentScript("-01.js", step);
+ step = 1;
+ reloadPage();
+ }
+
+ function testScriptShown()
+ {
+ if (step === 1) {
+ testCurrentScript("-01.js", step);
+ step = 2;
+ reloadPage();
+ }
+ else if (step === 2) {
+ testCurrentScript("-01.js", step);
+ step = 3;
+ gView.Scripts.selectScript(gView.Scripts.scriptLocations[1]);
+ }
+ else if (step === 3) {
+ testCurrentScript("-02.js", step);
+ step = 4;
+ reloadPage();
+ }
+ else if (step === 4) {
+ testCurrentScript("-02.js", step);
+ finishTest();
+ }
+ }
+
+ function testCurrentScript(part, step)
+ {
+ info("Currently preferred script: " + gView.Scripts.preferredScriptUrl);
+ info("Currently selected script: " + gView.Scripts.selected);
+
+ isnot(gView.Scripts.preferredScriptUrl.indexOf(part), -1,
+ "The preferred script url wasn't set correctly. (" + step + ")");
+ isnot(gView.Scripts.selected.indexOf(part), -1,
+ "The selected script isn't the correct one. (" + step + ")");
+ is(gView.Scripts.selected, scriptShownUrl,
+ "The shown script is not the the correct one. (" + step + ")");
+ }
+
+ function reloadPage()
+ {
+ executeSoon(function() {
+ gDebuggee.location.reload();
+ });
+ }
+
+ registerCleanupFunction(function() {
+ removeTab(gTab);
+ gPane = null;
+ gTab = null;
+ gDebuggee = null;
+ gDebugger = null;
+ gView = null;
+ });
+}
--- a/browser/devtools/highlighter/TreePanel.jsm
+++ b/browser/devtools/highlighter/TreePanel.jsm
@@ -514,17 +514,21 @@ TreePanel.prototype = {
}
},
/**
* Close the editor and cleanup.
*/
closeEditor: function TP_closeEditor()
{
+ if (!this.treeBrowserDocument) // already closed, bug 706092
+ return;
+
let editor = this.treeBrowserDocument.getElementById("attribute-editor");
+
let editorInput =
this.treeBrowserDocument.getElementById("attribute-editor-input");
// remove highlight from attribute-value node in tree
this.removeClass(this.editingContext.attrObj, "editingAttributeValue");
// hide editor
this.removeClass(editor, "editing");
--- a/browser/devtools/highlighter/test/browser_inspector_editor.js
+++ b/browser/devtools/highlighter/test/browser_inspector_editor.js
@@ -6,17 +6,21 @@
* ***** END LICENSE BLOCK *****
*/
let doc;
let div;
let editorTestSteps;
function doNextStep() {
- editorTestSteps.next();
+ try {
+ editorTestSteps.next();
+ } catch(exception) {
+ info("caught:", exception);
+ }
}
function setupEditorTests()
{
div = doc.createElement("div");
div.setAttribute("id", "foobar");
div.setAttribute("class", "barbaz");
doc.body.appendChild(div);
@@ -209,26 +213,43 @@ function doEditorTestSteps()
// single-click the `class` attribute-value node
executeSoon(function() {
EventUtils.synthesizeMouse(attrValNode_class, 2, 2, {}, attrValNode_class.ownerDocument.defaultView);
});
yield; // End of Step 7
-
// Step 8: validate that the editor was closed and that the editing was not saved
ok(!treePanel.editingContext, "Step 8: editor session ended");
editorVisible = editor.classList.contains("editing");
ok(!editorVisible, "editor popup hidden");
is(div.getAttribute("id"), "Hello World", "`id` attribute-value *not* updated");
is(attrValNode_id.innerHTML, "Hello World", "attribute-value node in HTML panel *not* updated");
+ executeSoon(doNextStep);
- // End of Step 8
- executeSoon(finishUp);
+ yield; // End of Step 8
+
+ // Step 9: Open the Editor and verify that closing the tree panel does not make the
+ // Inspector go cray-cray.
+ executeSoon(function() {
+ // firing 2 clicks right in a row to simulate a double-click
+ EventUtils.synthesizeMouse(attrValNode_id, 2, 2, {clickCount: 2}, attrValNode_id.ownerDocument.defaultView);
+ doNextStep();
+ });
+
+ yield; // End of Step 9
+
+ ok(treePanel.editingContext, "Step 9: editor session started");
+ editorVisible = editor.classList.contains("editing");
+ ok(editorVisible, "editor popup is visible");
+ executeSoon(function() {
+ InspectorUI.toggleHTMLPanel();
+ finishUp();
+ });
}
function finishUp() {
// end of all steps, so clean up
Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false);
Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false);
Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false);
doc = div = null;
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -486,19 +486,34 @@ var Scratchpad = {
/**
* Write out an error at the current insertion point as a block comment
* @param object aValue
* The Error object to write out the message and stack trace
*/
writeAsErrorComment: function SP_writeAsErrorComment(aError)
{
- let stack = aError.stack || aError.fileName + ":" + aError.lineNumber;
- let newComment = "Exception: " + aError.message + "\n" + stack.replace(/\n$/, "");
+ let stack = "";
+ if (aError.stack) {
+ stack = aError.stack;
+ }
+ else if (aError.fileName) {
+ if (aError.lineNumber) {
+ stack = "@" + aError.fileName + ":" + aError.lineNumber;
+ }
+ else {
+ stack = "@" + aError.fileName;
+ }
+ }
+ else if (aError.lineNumber) {
+ stack = "@" + aError.lineNumber;
+ }
+ let newComment = "Exception: " + ( aError.message || aError) + ( stack == "" ? stack : "\n" + stack.replace(/\n$/, "") );
+
this.writeAsComment(newComment);
},
/**
* Open the Property Panel to inspect the given object.
*
* @param string aEvalString
* The string that was evaluated. This is re-used when the user updates
--- a/browser/devtools/scratchpad/test/Makefile.in
+++ b/browser/devtools/scratchpad/test/Makefile.in
@@ -27,11 +27,12 @@ MOCHITEST_BROWSER_FILES = \
browser_scratchpad_bug_669612_unsaved.js \
browser_scratchpad_bug_653427_confirm_close.js \
browser_scratchpad_bug684546_reset_undo.js \
browser_scratchpad_bug690552_display_outputs_errors.js \
browser_scratchpad_bug650345_find_ui.js \
browser_scratchpad_bug714942_goto_line_ui.js \
browser_scratchpad_bug_650760_help_key.js \
browser_scratchpad_bug_651942_recent_files.js \
+ browser_scratchpad_bug756681_display_non_error_exceptions.js \
head.js \
include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug756681_display_non_error_exceptions.js
@@ -0,0 +1,104 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test()
+{
+ waitForExplicitFinish();
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.selectedBrowser.addEventListener("load", function browserLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", browserLoad, true);
+ openScratchpad(runTests, {"state":{"text":""}});
+ }, true);
+
+ content.location = "data:text/html, test that exceptions are output as " +
+ "comments correctly in Scratchpad";
+}
+
+function runTests()
+{
+ var scratchpad = gScratchpadWindow.Scratchpad;
+
+ var message = "\"Hello World!\""
+ var openComment = "\n/*\n";
+ var closeComment = "\n*/";
+ var error1 = "throw new Error(\"Ouch!\")";
+ var error2 = "throw \"A thrown string\"";
+ var error3 = "throw {}";
+ var error4 = "document.body.appendChild(document.body)";
+ let messageArray = {};
+ let count = {};
+
+ // Display message
+ scratchpad.setText(message);
+ scratchpad.display();
+ is(scratchpad.getText(),
+ message + openComment + "Hello World!" + closeComment,
+ "message display output");
+
+ // Display error1, throw new Error("Ouch")
+ scratchpad.setText(error1);
+ scratchpad.display();
+ is(scratchpad.getText(),
+ error1 + openComment + "Exception: Ouch!\n@Scratchpad:1" + closeComment,
+ "error display output");
+
+ // Display error2, throw "A thrown string"
+ scratchpad.setText(error2);
+ scratchpad.display();
+ is(scratchpad.getText(),
+ error2 + openComment + "Exception: A thrown string" + closeComment,
+ "thrown string display output");
+
+ // Display error3, throw {}
+ scratchpad.setText(error3);
+ scratchpad.display();
+ is(scratchpad.getText(),
+ error3 + openComment + "Exception: [object Object]" + closeComment,
+ "thrown object display output");
+
+ // Display error4, document.body.appendChild(document.body)
+ scratchpad.setText(error4);
+ scratchpad.display();
+ is(scratchpad.getText(),
+ error4 + openComment + "Exception: Node cannot be inserted " +
+ "at the specified point in the hierarchy\n@1" + closeComment,
+ "Alternative format error display output");
+
+ // Run message
+ scratchpad.setText(message);
+ scratchpad.run();
+ is(scratchpad.getText(), message, "message run output");
+
+ // Run error1, throw new Error("Ouch")
+ scratchpad.setText(error1);
+ scratchpad.run();
+ is(scratchpad.getText(),
+ error1 + openComment + "Exception: Ouch!\n@Scratchpad:1" + closeComment,
+ "error run output");
+
+ // Run error2, throw "A thrown string"
+ scratchpad.setText(error2);
+ scratchpad.run();
+ is(scratchpad.getText(),
+ error2 + openComment + "Exception: A thrown string" + closeComment,
+ "thrown string run output");
+
+ // Run error3, throw {}
+ scratchpad.setText(error3);
+ scratchpad.run();
+ is(scratchpad.getText(),
+ error3 + openComment + "Exception: [object Object]" + closeComment,
+ "thrown object run output");
+
+ // Run error4, document.body.appendChild(document.body)
+ scratchpad.setText(error4);
+ scratchpad.run();
+ is(scratchpad.getText(),
+ error4 + openComment + "Exception: Node cannot be inserted " +
+ "at the specified point in the hierarchy\n@1" + closeComment,
+ "Alternative format error run output");
+
+ finish();
+}
--- a/browser/devtools/webconsole/HUDService-content.js
+++ b/browser/devtools/webconsole/HUDService-content.js
@@ -518,21 +518,21 @@ let Manager = {
function JSTermHelper(aJSTerm)
{
/**
* Find a node by ID.
*
* @param string aId
* The ID of the element you want.
* @return nsIDOMNode or null
- * The result of calling document.getElementById(aId).
+ * The result of calling document.querySelector(aSelector).
*/
- aJSTerm.sandbox.$ = function JSTH_$(aId)
+ aJSTerm.sandbox.$ = function JSTH_$(aSelector)
{
- return aJSTerm.window.document.getElementById(aId);
+ return aJSTerm.window.document.querySelector(aSelector);
};
/**
* Find the nodes matching a CSS selector.
*
* @param string aSelector
* A string that is passed to window.document.querySelectorAll.
* @return nsIDOMNodeList
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -606,29 +606,30 @@ WebConsole.prototype = {
this.iframe.setAttribute("animated", "true");
this.iframe.setAttribute("tooltip", "aHTMLTooltip");
this.iframe.style.height = 0;
this.iframe.addEventListener("load", this._onIframeLoad, true);
this.iframe.setAttribute("src", UI_IFRAME_URL);
let position = Services.prefs.getCharPref("devtools.webconsole.position");
this.positionConsole(position);
- this._currentUIPosition = position;
},
/**
* The "load" event handler for the Web Console iframe.
* @private
*/
_onIframeLoad: function WC__onIframeLoad()
{
this.iframe.removeEventListener("load", this._onIframeLoad, true);
+ let position = Services.prefs.getCharPref("devtools.webconsole.position");
+
this.iframeWindow = this.iframe.contentWindow.wrappedJSObject;
- this.ui = new this.iframeWindow.WebConsoleFrame(this, this._currentUIPosition);
+ this.ui = new this.iframeWindow.WebConsoleFrame(this, position);
this._setupMessageManager();
},
/**
* Create a panel to open the web console if it should float above
* the content in its own window.
* @private
*/
@@ -690,18 +691,16 @@ WebConsole.prototype = {
// Make sure that the HUDBox size updates when the panel is resized.
let height = panel.clientHeight;
this.iframe.style.height = "auto";
this.iframe.flex = 1;
panel.setAttribute("height", height);
-
- this._afterPositionConsole("window", lastIndex);
}).bind(this);
panel.addEventListener("popupshown", onPopupShown,false);
let onPopupHidden = (function HUD_onPopupHidden(aEvent) {
if (aEvent.target != panel) {
return;
}
@@ -731,16 +730,19 @@ WebConsole.prototype = {
if (this.outputNode && this.outputNode.getIndexOfFirstVisibleRow) {
lastIndex = this.outputNode.getIndexOfFirstVisibleRow() +
this.outputNode.getNumberOfVisibleRows() - 1;
}
if (this.splitter.parentNode) {
this.splitter.parentNode.removeChild(this.splitter);
}
+
+ this._beforePositionConsole("window", lastIndex);
+
panel.appendChild(this.iframe);
let space = this.chromeDocument.createElement("spacer");
space.flex = 1;
let bottomBox = this.chromeDocument.createElement("hbox");
let resizer = this.chromeDocument.createElement("resizer");
@@ -817,16 +819,18 @@ WebConsole.prototype = {
this.outputNode.getNumberOfVisibleRows() - 1;
}
// remove the console and splitter and reposition
if (this.splitter.parentNode) {
this.splitter.parentNode.removeChild(this.splitter);
}
+ this._beforePositionConsole(aPosition, lastIndex);
+
if (aPosition == "below") {
nBox.appendChild(this.splitter);
nBox.appendChild(this.iframe);
}
else {
nBox.insertBefore(this.splitter, node);
nBox.insertBefore(this.iframe, this.splitter);
}
@@ -836,31 +840,29 @@ WebConsole.prototype = {
this.consoleWindowUnregisterOnHide = false;
this.consolePanel.hidePopup();
this.consolePanel.parentNode.removeChild(this.consolePanel);
this.consolePanel = null; // remove this as we're not in panel anymore
this.iframe.removeAttribute("flex");
this.iframe.removeAttribute("height");
this.iframe.style.height = height + "px";
}
-
- this._afterPositionConsole(aPosition, lastIndex);
},
/**
- * Common code that needs to execute after the Web Console is repositioned.
+ * Common code that needs to execute before the Web Console is repositioned.
* @private
* @param string aPosition
* The new position: "above", "below" or "window".
* @param number aLastIndex
* The last visible message in the console output before repositioning
* occurred.
*/
- _afterPositionConsole:
- function WC__afterPositionConsole(aPosition, aLastIndex)
+ _beforePositionConsole:
+ function WC__beforePositionConsole(aPosition, aLastIndex)
{
if (!this.ui) {
return;
}
let onLoad = function() {
this.iframe.removeEventListener("load", onLoad, true);
this.iframeWindow = this.iframe.contentWindow.wrappedJSObject;
--- a/browser/devtools/webconsole/test/browser_webconsole_jsterm.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_jsterm.js
@@ -47,17 +47,17 @@ function checkResult(msg, desc, lines) {
});
}
function testJSTerm(hud)
{
jsterm = hud.jsterm;
jsterm.clearOutput();
- jsterm.execute("'id=' + $('header').getAttribute('id')");
+ jsterm.execute("'id=' + $('#header').getAttribute('id')");
checkResult('"id=header"', "$() worked", 1);
yield;
jsterm.clearOutput();
jsterm.execute("headerQuery = $$('h1')");
jsterm.execute("'length=' + headerQuery.length");
checkResult('"length=1"', "$$() worked", 2);
yield;
--- a/browser/devtools/webconsole/test/browser_webconsole_position_ui.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_position_ui.js
@@ -32,17 +32,17 @@ function testEnd() {
function waitForPosition(aPosition, aCallback) {
waitForSuccess({
name: "web console position changed to '" + aPosition + "'",
validatorFn: function()
{
return hudRef._currentUIPosition == aPosition;
},
- successFn: aCallback,
+ successFn: executeSoon.bind(null, aCallback),
failureFn: finishTest,
});
}
function consoleOpened(aHudRef) {
hudRef = aHudRef;
testMenuitems();
@@ -50,19 +50,20 @@ function consoleOpened(aHudRef) {
is(hudBox.parentNode.childNodes[2].getAttribute("id"), hudRef.hudId,
"initial console position is correct");
is(hudRef.ui.positionMenuitems.below.getAttribute("checked"), "true",
"position menu checkbox is below");
is(Services.prefs.getCharPref(POSITION_PREF), "below", "pref is below");
- hudRef.positionConsole("above");
-
- waitForPosition("above", onPositionAbove);
+ executeSoon(function() {
+ hudRef.positionConsole("above");
+ waitForPosition("above", onPositionAbove);
+ });
}
function onPositionAbove() {
let hudBox = hudRef.iframe;
let id = hudBox.parentNode.childNodes[0].getAttribute("id");
is(id, hudRef.hudId, "above position is correct");
@@ -76,18 +77,20 @@ function onPositionAbove() {
hudBox.style.height = boxHeight + "px";
boxHeight = hudBox.clientHeight;
Services.prefs.setIntPref(WIDTH_PREF, panelWidth);
Services.prefs.setIntPref(TOP_PREF, 50);
Services.prefs.setIntPref(LEFT_PREF, 51);
- hudRef.positionConsole("window");
- waitForPosition("window", onPositionWindow);
+ executeSoon(function() {
+ hudRef.positionConsole("window");
+ waitForPosition("window", onPositionWindow);
+ });
}
function onPositionWindow() {
let hudBox = hudRef.iframe;
let id = hudBox.parentNode.getAttribute("id");
is(id, "console_window_" + hudRef.hudId, "window position is correct");
is(Services.prefs.getCharPref(POSITION_PREF), "window", "pref is window");
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -814,17 +814,17 @@ user_pref("camino.use_system_proxy_setti
# We should have a "crashinject" program in our utility path
crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe"))
if os.path.exists(crashinject) and subprocess.Popen([crashinject, str(proc.pid)]).wait() == 0:
return
#TODO: kill the process such that it triggers Breakpad on OS X (bug 525296)
self.log.info("Can't trigger Breakpad, just killing process")
proc.kill()
- def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath, logger):
+ def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath):
""" Look for timeout or crashes and return the status after the process terminates """
stackFixerProcess = None
stackFixerFunction = None
didTimeout = False
hitMaxTime = False
if proc.stdout is None:
self.log.info("TEST-INFO: Not logging stdout or stderr due to debugger connection")
else:
@@ -851,18 +851,16 @@ user_pref("camino.use_system_proxy_setti
stdout=subprocess.PIPE)
logsource = stackFixerProcess.stdout
(line, didTimeout) = self.readWithTimeout(logsource, timeout)
while line != "" and not didTimeout:
if stackFixerFunction:
line = stackFixerFunction(line)
self.log.info(line.rstrip().decode("UTF-8", "ignore"))
- if logger:
- logger.log(line)
if "TEST-START" in line and "|" in line:
self.lastTestSeen = line.split("|")[1].strip()
if not debuggerInfo and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line:
if self.haveDumpedScreen:
self.log.info("Not taking screenshot here: see the one that was previously logged")
else:
self.dumpScreen(utilityPath)
@@ -942,17 +940,17 @@ user_pref("camino.use_system_proxy_setti
self.log.info("TEST-UNEXPECTED-FAIL | automation.py | child process %d still alive after shutdown", processPID)
self.killPid(processPID)
def checkForCrashes(self, profileDir, symbolsPath):
automationutils.checkForCrashes(os.path.join(profileDir, "minidumps"), symbolsPath, self.lastTestSeen)
def runApp(self, testURL, env, app, profileDir, extraArgs,
runSSLTunnel = False, utilityPath = None,
- xrePath = None, certPath = None, logger = None,
+ xrePath = None, certPath = None,
debuggerInfo = None, symbolsPath = None,
timeout = -1, maxTime = None):
"""
Run the app, log the duration it took to execute, return the status code.
Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds.
"""
if utilityPath == None:
@@ -1001,17 +999,17 @@ user_pref("camino.use_system_proxy_setti
self.lastTestSeen = "automation.py"
proc = self.Process([cmd] + args,
env = self.environment(env, xrePath = xrePath,
crashreporter = not debuggerInfo),
stdout = outputPipe,
stderr = subprocess.STDOUT)
self.log.info("INFO | automation.py | Application pid: %d", proc.pid)
- status = self.waitForFinish(proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath, logger)
+ status = self.waitForFinish(proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath)
self.log.info("INFO | automation.py | Application ran for: %s", str(datetime.now() - startTime))
# Do a final check for zombie child processes.
self.checkForZombies(processLog)
self.checkForCrashes(profileDir, symbolsPath)
if os.path.exists(processLog):
os.unlink(processLog)
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -2,30 +2,28 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import with_statement
import glob, logging, os, platform, shutil, subprocess, sys, tempfile, urllib2, zipfile
import re
from urlparse import urlparse
-from operator import itemgetter
__all__ = [
"ZipFileReader",
"addCommonOptions",
"checkForCrashes",
"dumpLeakLog",
"isURL",
"processLeakLog",
"getDebuggerInfo",
"DEBUGGER_INFO",
"replaceBackSlashes",
"wrapCommand",
- "ShutdownLeakLogger"
]
# Map of debugging programs to information about them, like default arguments
# and whether or not they are interactive.
DEBUGGER_INFO = {
# gdb requires that you supply the '--args' flag in order to pass arguments
# after the executable name to the executable.
"gdb": {
@@ -414,120 +412,8 @@ def wrapCommand(cmd):
binary.
"""
if platform.system() == "Darwin" and \
hasattr(platform, 'mac_ver') and \
platform.mac_ver()[0][:4] < '10.6':
return ["arch", "-arch", "i386"] + cmd
# otherwise just execute the command normally
return cmd
-
-class ShutdownLeakLogger(object):
- """
- Parses the mochitest run log when running a debug build, assigns all leaked
- DOM windows (that are still around after test suite shutdown, despite running
- the GC) to the tests that created them and prints leak statistics.
- """
- MAX_LEAK_COUNT = 4
-
- def __init__(self, logger):
- self.logger = logger
- self.tests = []
- self.leakedWindows = {}
- self.leakedDocShells = set()
- self.currentTest = None
- self.seenShutdown = False
-
- def log(self, line):
- if line[2:11] == "DOMWINDOW":
- self._logWindow(line)
- elif line[2:10] == "DOCSHELL":
- self._logDocShell(line)
- elif line.startswith("TEST-START"):
- fileName = line.split(" ")[-1].strip().replace("chrome://mochitests/content/browser/", "")
- self.currentTest = {"fileName": fileName, "windows": set(), "docShells": set()}
- elif line.startswith("INFO TEST-END"):
- # don't track a test if no windows or docShells leaked
- if self.currentTest and (self.currentTest["windows"] or self.currentTest["docShells"]):
- self.tests.append(self.currentTest)
- self.currentTest = None
- elif line.startswith("INFO TEST-START | Shutdown"):
- self.seenShutdown = True
-
- def parse(self):
- leakingTests = self._parseLeakingTests()
-
- if leakingTests:
- totalWindows = sum(len(test["leakedWindows"]) for test in leakingTests)
- totalDocShells = sum(len(test["leakedDocShells"]) for test in leakingTests)
- msgType = "TEST-INFO" if totalWindows + totalDocShells <= self.MAX_LEAK_COUNT else "TEST-UNEXPECTED-FAIL"
- self.logger.info("%s | ShutdownLeaks | leaked %d DOMWindow(s) and %d DocShell(s) until shutdown", msgType, totalWindows, totalDocShells)
-
- for test in leakingTests:
- for url, count in self._zipLeakedWindows(test["leakedWindows"]):
- self.logger.info("%s | %s | leaked %d window(s) until shutdown [url = %s]", msgType, test["fileName"], count, url)
-
- if test["leakedDocShells"]:
- self.logger.info("%s | %s | leaked %d docShell(s) until shutdown", msgType, test["fileName"], len(test["leakedDocShells"]))
-
- def _logWindow(self, line):
- created = line[:2] == "++"
- id = self._parseValue(line, "serial")
-
- # log line has invalid format
- if not id:
- return
-
- if self.currentTest:
- windows = self.currentTest["windows"]
- if created:
- windows.add(id)
- else:
- windows.discard(id)
- elif self.seenShutdown and not created:
- self.leakedWindows[id] = self._parseValue(line, "url")
-
- def _logDocShell(self, line):
- created = line[:2] == "++"
- id = self._parseValue(line, "id")
-
- # log line has invalid format
- if not id:
- return
-
- if self.currentTest:
- docShells = self.currentTest["docShells"]
- if created:
- docShells.add(id)
- else:
- docShells.discard(id)
- elif self.seenShutdown and not created:
- self.leakedDocShells.add(id)
-
- def _parseValue(self, line, name):
- match = re.search("\[%s = (.+?)\]" % name, line)
- if match:
- return match.group(1)
- return None
-
- def _parseLeakingTests(self):
- leakingTests = []
-
- for test in self.tests:
- test["leakedWindows"] = [self.leakedWindows[id] for id in test["windows"] if id in self.leakedWindows]
- test["leakedDocShells"] = [id for id in test["docShells"] if id in self.leakedDocShells]
- test["leakCount"] = len(test["leakedWindows"]) + len(test["leakedDocShells"])
-
- if test["leakCount"]:
- leakingTests.append(test)
-
- return sorted(leakingTests, key=itemgetter("leakCount"), reverse=True)
-
- def _zipLeakedWindows(self, leakedWindows):
- counts = []
- counted = set()
-
- for url in leakedWindows:
- if not url in counted:
- counts.append((url, leakedWindows.count(url)))
- counted.add(url)
-
- return sorted(counts, key=itemgetter(1), reverse=True)
--- a/build/mobile/devicemanager.py
+++ b/build/mobile/devicemanager.py
@@ -432,16 +432,18 @@ class DeviceManager:
@abstractmethod
def getInfo(self, directive=None):
"""
Returns information about the device:
Directive indicates the information you want to get, your choices are:
os - name of the os
id - unique id of the device
uptime - uptime of the device
+ uptimemillis - uptime of the device in milliseconds (NOT supported on all
+ platforms)
systime - system time of the device
screen - screen resolution
memory - memory stats
process - list of running processes (same as ps)
disk - total, free, available bytes on disk
power - power status (charge, battery temp)
all - all of them - or call it with no parameters to get all the information
returns:
--- a/build/mobile/devicemanagerADB.py
+++ b/build/mobile/devicemanagerADB.py
@@ -658,16 +658,18 @@ class DeviceManagerADB(DeviceManager):
# uptime - uptime of the device
# systime - system time of the device
# screen - screen resolution
# memory - memory stats
# process - list of running processes (same as ps)
# disk - total, free, available bytes on disk
# power - power status (charge, battery temp)
# all - all of them - or call it with no parameters to get all the information
+ ### Note that uptimemillis is NOT supported, as there is no way to get this
+ ### data from the shell.
# returns:
# success: dict of info strings by directive name
# failure: {}
def getInfo(self, directive="all"):
ret = {}
if (directive == "id" or directive == "all"):
ret["id"] = self.runCmd(["get-serialno"]).stdout.read()
if (directive == "os" or directive == "all"):
--- a/build/mobile/devicemanagerSUT.py
+++ b/build/mobile/devicemanagerSUT.py
@@ -912,34 +912,35 @@ class DeviceManagerSUT(DeviceManager):
if (self.debug > 3): print "INFO: rebt- got status back: " + str(status)
return status
# Returns information about the device:
# Directive indicates the information you want to get, your choices are:
# os - name of the os
# id - unique id of the device
# uptime - uptime of the device
+ # uptimemillis - uptime of the device in milliseconds
# systime - system time of the device
# screen - screen resolution
# rotation - rotation of the device (in degrees)
# memory - memory stats
# process - list of running processes (same as ps)
# disk - total, free, available bytes on disk
# power - power status (charge, battery temp)
# all - all of them - or call it with no parameters to get all the information
# returns:
# success: dict of info strings by directive name
# failure: {}
def getInfo(self, directive=None):
data = None
result = {}
collapseSpaces = re.compile(' +')
- directives = ['os','id','uptime','systime','screen','rotation','memory','process',
- 'disk','power']
+ directives = ['os','id','uptime','uptimemillis','systime','screen',
+ 'rotation','memory','process','disk','power']
if (directive in directives):
directives = [directive]
for d in directives:
data = self.runCmds([{ 'cmd': 'info ' + d }])
if (data is None):
continue
data = collapseSpaces.sub(' ', data)
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -57,17 +57,17 @@ class RemoteAutomation(Automation):
if crashreporter:
env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
env['MOZ_CRASHREPORTER'] = '1'
else:
env['MOZ_CRASHREPORTER_DISABLE'] = '1'
return env
- def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsDir, logger):
+ def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsDir):
# maxTime is used to override the default timeout, we should honor that
status = proc.wait(timeout = maxTime)
print proc.stdout
if (status == 1 and self._devicemanager.processExist(proc.procName)):
# Then we timed out, make sure Fennec is dead
proc.kill()
--- a/build/mobile/sutagent/android/watcher/WatcherService.java
+++ b/build/mobile/sutagent/android/watcher/WatcherService.java
@@ -45,17 +45,17 @@ import android.widget.Toast;
public class WatcherService extends Service
{
String sErrorPrefix = "##Installer Error## ";
String currentDir = "/";
String sPingTarget = "";
long lDelay = 60000;
long lPeriod = 300000;
- int nMaxStrikes = 3; // maximum number of tries before we consider network unreachable
+ int nMaxStrikes = 0; // maximum number of tries before we consider network unreachable (0 means don't check)
boolean bStartSUTAgent = true;
Process pProc;
Context myContext = null;
Timer myTimer = null;
private PowerManager.WakeLock pwl = null;
public static final int NOTIFICATION_ID = 1964;
boolean bInstalling = false;
@@ -106,17 +106,17 @@ public class WatcherService extends Serv
String sHold = "";
Log.i("Watcher", String.format("Loading settings from %s", sIniFile));
this.sPingTarget = GetIniData("watcher", "PingTarget", sIniFile, "www.mozilla.org");
sHold = GetIniData("watcher", "delay", sIniFile, "60000");
this.lDelay = Long.parseLong(sHold.trim());
sHold = GetIniData("watcher", "period", sIniFile,"300000");
this.lPeriod = Long.parseLong(sHold.trim());
- sHold = GetIniData("watcher", "strikes", sIniFile,"3");
+ sHold = GetIniData("watcher", "strikes", sIniFile,"0");
this.nMaxStrikes = Integer.parseInt(sHold.trim());
Log.i("Watcher", String.format("Pinging %s after a delay of %s sec, period of %s sec, max number of failed attempts is %s (if max # of failed attempts is 0, then no checking)",
this.sPingTarget, this.lDelay / 1000.0, this.lPeriod / 1000.0, nMaxStrikes));
sHold = GetIniData("watcher", "StartSUTAgent", sIniFile, "true");
this.bStartSUTAgent = Boolean.parseBoolean(sHold.trim());
sHold = GetIniData("watcher", "stayon", sIniFile,"0");
--- a/content/base/public/nsDOMFile.h
+++ b/content/base/public/nsDOMFile.h
@@ -50,16 +50,34 @@ public:
virtual const nsTArray<nsCOMPtr<nsIDOMBlob> >*
GetSubBlobs() const { return nullptr; }
NS_DECL_NSIDOMBLOB
NS_DECL_NSIDOMFILE
NS_DECL_NSIXHRSENDABLE
NS_DECL_NSIMUTABLE
+ void
+ SetLazyData(const nsAString& aName, const nsAString& aContentType,
+ PRUint64 aLength)
+ {
+ NS_ASSERTION(aLength, "must have length");
+
+ mName = aName;
+ mContentType = aContentType;
+ mLength = aLength;
+
+ mIsFile = !aName.IsVoid();
+ }
+
+ bool IsSizeUnknown() const
+ {
+ return mLength == UINT64_MAX;
+ }
+
protected:
nsDOMFileBase(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength)
: mIsFile(true), mImmutable(false), mContentType(aContentType),
mName(aName), mStart(0), mLength(aLength)
{
// Ensure non-null mContentType by default
mContentType.SetIsVoid(false);
@@ -81,21 +99,16 @@ protected:
NS_ASSERTION(aLength != UINT64_MAX,
"Must know length when creating slice");
// Ensure non-null mContentType by default
mContentType.SetIsVoid(false);
}
virtual ~nsDOMFileBase() {}
- bool IsSizeUnknown() const
- {
- return mLength == UINT64_MAX;
- }
-
virtual bool IsStoredFile() const
{
return false;
}
virtual bool IsWholeFile() const
{
NS_NOTREACHED("Should only be called on dom blobs backed by files!");
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -8434,18 +8434,17 @@ nsDocument::CreateTouch(nsIDOMWindow* aV
aForce));
return NS_OK;
}
NS_IMETHODIMP
nsDocument::CreateTouchList(nsIVariant* aPoints,
nsIDOMTouchList** aRetVal)
{
- nsRefPtr<nsDOMTouchList> retval =
- new nsDOMTouchList(static_cast<nsIDocument*>(this));
+ nsRefPtr<nsDOMTouchList> retval = new nsDOMTouchList();
if (aPoints) {
PRUint16 type;
aPoints->GetDataType(&type);
if (type == nsIDataType::VTYPE_INTERFACE ||
type == nsIDataType::VTYPE_INTERFACE_IS) {
nsCOMPtr<nsISupports> data;
aPoints->GetAsISupports(getter_AddRefs(data));
nsCOMPtr<nsIDOMTouch> point = do_QueryInterface(data);
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -146,32 +146,34 @@ WebGLContext::WebGLContext()
mGLMaxTextureUnits = 0;
mGLMaxTextureSize = 0;
mGLMaxCubeMapTextureSize = 0;
mGLMaxTextureImageUnits = 0;
mGLMaxVertexTextureImageUnits = 0;
mGLMaxVaryingVectors = 0;
mGLMaxFragmentUniformVectors = 0;
mGLMaxVertexUniformVectors = 0;
-
+
// See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
mPixelStorePackAlignment = 4;
mPixelStoreUnpackAlignment = 4;
WebGLMemoryMultiReporterWrapper::AddWebGLContext(this);
mAllowRestore = true;
mContextLossTimerRunning = false;
mDrawSinceContextLossTimerSet = false;
mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
mContextStatus = ContextStable;
mContextLostErrorSet = false;
mAlreadyGeneratedWarnings = 0;
mAlreadyWarnedAboutFakeVertexAttrib0 = false;
+
+ mLastUseIndex = 0;
}
WebGLContext::~WebGLContext()
{
DestroyResourcesAndContext();
WebGLMemoryMultiReporterWrapper::RemoveWebGLContext(this);
TerminateContextLossTimer();
mContextRestorer = nullptr;
@@ -330,17 +332,17 @@ WebGLContext::SetContextOptions(nsIPrope
mOptions = newOpts;
return NS_OK;
}
NS_IMETHODIMP
WebGLContext::SetDimensions(PRInt32 width, PRInt32 height)
{
/*** early success return cases ***/
-
+
if (mCanvasElement) {
mCanvasElement->InvalidateCanvas();
}
if (gl && mWidth == width && mHeight == height)
return NS_OK;
// Zero-sized surfaces can cause problems.
@@ -361,21 +363,27 @@ WebGLContext::SetDimensions(PRInt32 widt
mHeight = gl->OffscreenActualSize().height;
mResetLayer = true;
gl->ClearSafely();
return NS_OK;
}
- /*** end of early success return cases ***/
+ /*** End of early success return cases.
+ *** At this point we know that we're not just resizing an existing context,
+ *** we are initializing a new context.
+ ***/
- // At this point we know that the old context is not going to survive, even though we still don't
- // know if creating the new context will succeed.
- DestroyResourcesAndContext();
+ // if we exceeded either the global or the per-principal limit for WebGL contexts,
+ // lose the oldest-used context now to free resources. Note that we can't do that
+ // in the WebGLContext constructor as we don't have a canvas element yet there.
+ // Here is the right place to do so, as we are about to create the OpenGL context
+ // and that is what can fail if we already have too many.
+ LoseOldestWebGLContextIfLimitExceeded();
// Get some prefs for some preferred/overriden things
NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
bool forceOSMesa =
Preferences::GetBool("webgl.force_osmesa", false);
#ifdef XP_WIN
bool preferEGL =
@@ -624,16 +632,101 @@ WebGLContext::Render(gfxContext *ctx, gf
ctx->NewPath();
ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
ctx->Fill();
return NS_OK;
}
+void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
+{
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+ // some mobile devices can't have more than 8 GL contexts overall
+ const size_t kMaxWebGLContextsPerPrincipal = 2;
+ const size_t kMaxWebGLContexts = 4;
+#else
+ const size_t kMaxWebGLContextsPerPrincipal = 8;
+ const size_t kMaxWebGLContexts = 16;
+#endif
+ MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
+
+ // it's important to update the index on a new context before losing old contexts,
+ // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
+ // when choosing which one to lose first.
+ UpdateLastUseIndex();
+
+ WebGLMemoryMultiReporterWrapper::ContextsArrayType &contexts
+ = WebGLMemoryMultiReporterWrapper::Contexts();
+
+ // quick exit path, should cover a majority of cases
+ if (contexts.Length() <= kMaxWebGLContextsPerPrincipal) {
+ return;
+ }
+
+ // note that here by "context" we mean "non-lost context". See the check for
+ // IsContextLost() below. Indeed, the point of this function is to maybe lose
+ // some currently non-lost context.
+
+ uint64_t oldestIndex = UINT64_MAX;
+ uint64_t oldestIndexThisPrincipal = UINT64_MAX;
+ const WebGLContext *oldestContext = nsnull;
+ const WebGLContext *oldestContextThisPrincipal = nsnull;
+ size_t numContexts = 0;
+ size_t numContextsThisPrincipal = 0;
+
+ for(size_t i = 0; i < contexts.Length(); ++i) {
+
+ // don't want to lose ourselves.
+ if (contexts[i] == this)
+ continue;
+
+ if (contexts[i]->IsContextLost())
+ continue;
+
+ if (!contexts[i]->GetCanvas()) {
+ // Zombie context: the canvas is already destroyed, but something else
+ // (typically the compositor) is still holding on to the context.
+ // Killing zombies is a no-brainer.
+ const_cast<WebGLContext*>(contexts[i])->LoseContext();
+ continue;
+ }
+
+ numContexts++;
+ if (contexts[i]->mLastUseIndex < oldestIndex) {
+ oldestIndex = contexts[i]->mLastUseIndex;
+ oldestContext = contexts[i];
+ }
+
+ nsIPrincipal *ourPrincipal = GetCanvas()->NodePrincipal();
+ nsIPrincipal *theirPrincipal = contexts[i]->GetCanvas()->NodePrincipal();
+ bool samePrincipal;
+ nsresult rv = ourPrincipal->Equals(theirPrincipal, &samePrincipal);
+ if (NS_SUCCEEDED(rv) && samePrincipal) {
+ numContextsThisPrincipal++;
+ if (contexts[i]->mLastUseIndex < oldestIndexThisPrincipal) {
+ oldestIndexThisPrincipal = contexts[i]->mLastUseIndex;
+ oldestContextThisPrincipal = contexts[i];
+ }
+ }
+ }
+
+ if (numContextsThisPrincipal > kMaxWebGLContextsPerPrincipal) {
+ GenerateWarning("Exceeded %d live WebGL contexts for this principal, losing the "
+ "least recently used one.", kMaxWebGLContextsPerPrincipal);
+ MOZ_ASSERT(oldestContextThisPrincipal); // if we reach this point, this can't be null
+ const_cast<WebGLContext*>(oldestContextThisPrincipal)->LoseContext();
+ } else if (numContexts > kMaxWebGLContexts) {
+ GenerateWarning("Exceeded %d live WebGL contexts, losing the least recently used one.",
+ kMaxWebGLContexts);
+ MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
+ const_cast<WebGLContext*>(oldestContext)->LoseContext();
+ }
+}
+
NS_IMETHODIMP
WebGLContext::GetInputStream(const char* aMimeType,
const PRUnichar* aEncoderOptions,
nsIInputStream **aStream)
{
NS_ASSERTION(gl, "GetInputStream on invalid context?");
if (!gl)
return NS_ERROR_FAILURE;
@@ -686,16 +779,31 @@ WebGLContext::GetInputStream(const char*
}
NS_IMETHODIMP
WebGLContext::GetThebesSurface(gfxASurface **surface)
{
return NS_ERROR_NOT_AVAILABLE;
}
+void WebGLContext::UpdateLastUseIndex()
+{
+ static CheckedInt<uint64_t> sIndex = 0;
+
+ sIndex++;
+
+ // should never happen with 64-bit; trying to handle this would be riskier than
+ // not handling it as the handler code would never get exercised.
+ if (!sIndex.isValid()) {
+ NS_RUNTIMEABORT("Can't believe it's been 2^64 transactions already!");
+ }
+
+ mLastUseIndex = sIndex.value();
+}
+
static PRUint8 gWebGLLayerUserData;
namespace mozilla {
class WebGLContextUserData : public LayerUserData {
public:
WebGLContextUserData(nsHTMLCanvasElement *aContent)
: mContent(aContent) {}
@@ -706,16 +814,18 @@ public:
static void DidTransactionCallback(void* aData)
{
WebGLContextUserData *userdata = static_cast<WebGLContextUserData*>(aData);
nsHTMLCanvasElement *canvas = userdata->mContent;
WebGLContext *context = static_cast<WebGLContext*>(canvas->GetContextAtIndex(0));
context->mBackbufferClearingStatus = BackbufferClearingStatus::NotClearedSinceLastPresented;
canvas->MarkContextClean();
+
+ context->UpdateLastUseIndex();
}
private:
nsRefPtr<nsHTMLCanvasElement> mContent;
};
} // end namespace mozilla
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -1356,16 +1356,21 @@ protected:
int mAlreadyGeneratedWarnings;
bool mAlreadyWarnedAboutFakeVertexAttrib0;
bool ShouldGenerateWarnings() const {
return mAlreadyGeneratedWarnings < 32;
}
+ uint64_t mLastUseIndex;
+
+ void LoseOldestWebGLContextIfLimitExceeded();
+ void UpdateLastUseIndex();
+
#ifdef XP_MACOSX
// see bug 713305. This RAII helper guarantees that we're on the discrete GPU, during its lifetime
// Debouncing note: we don't want to switch GPUs too frequently, so try to not create and destroy
// these objects at high frequency. Having WebGLContext's hold one such object seems fine,
// because WebGLContext objects only go away during GC, which shouldn't happen too frequently.
// If in the future GC becomes much more frequent, we may have to revisit then (maybe use a timer).
ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper;
#endif
@@ -3169,23 +3174,25 @@ class WebGLMemoryMultiReporterWrapper
~WebGLMemoryMultiReporterWrapper();
static WebGLMemoryMultiReporterWrapper* sUniqueInstance;
// here we store plain pointers, not RefPtrs: we don't want the
// WebGLMemoryMultiReporterWrapper unique instance to keep alive all
// WebGLContexts ever created.
typedef nsTArray<const WebGLContext*> ContextsArrayType;
ContextsArrayType mContexts;
-
+
nsCOMPtr<nsIMemoryMultiReporter> mReporter;
static WebGLMemoryMultiReporterWrapper* UniqueInstance();
static ContextsArrayType & Contexts() { return UniqueInstance()->mContexts; }
+ friend class WebGLContext;
+
public:
static void AddWebGLContext(const WebGLContext* c) {
Contexts().AppendElement(c);
}
static void RemoveWebGLContext(const WebGLContext* c) {
ContextsArrayType & contexts = Contexts();
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -4499,16 +4499,17 @@ WebGLContext::name##_base(WebGLUniformLo
" (since this uniform is not an array uniform)," \
" got an array of length %d", \
#name, \
expectedElemSize, \
arrayLength); \
} \
\
uint32_t numElementsToUpload = NS_MIN(info.arraySize, arrayLength/expectedElemSize); \
+ MakeContextCurrent(); \
gl->f##name(location, numElementsToUpload, data); \
}
#define SIMPLE_MATRIX_METHOD_UNIFORM(name, dim) \
NS_IMETHODIMP \
WebGLContext::name(nsIWebGLUniformLocation* aLocation, bool aTranspose, \
const JS::Value& aValue, JSContext* aCx) \
{ \
--- a/content/events/src/nsDOMTouchEvent.cpp
+++ b/content/events/src/nsDOMTouchEvent.cpp
@@ -123,56 +123,52 @@ nsDOMTouch::Equals(nsIDOMTouch* aTouch)
aTouch->GetRadiusY(&radiusY);
return mRefPoint != aTouch->mRefPoint ||
(mForce != force) ||
(mRotationAngle != orientation) ||
(mRadius.x != radiusX) || (mRadius.y != radiusY);
}
// TouchList
+nsDOMTouchList::nsDOMTouchList(nsTArray<nsCOMPtr<nsIDOMTouch> > &aTouches)
+{
+ mPoints.AppendElements(aTouches);
+}
DOMCI_DATA(TouchList, nsDOMTouchList)
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMTouchList)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMTouchList)
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsIDOMTouchList)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TouchList)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMTouchList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_OF_NSCOMPTR(mPoints)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParent)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMTouchList)
- NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMTouchList)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mPoints)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParent)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMTouchList)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMTouchList)
NS_IMETHODIMP
nsDOMTouchList::GetLength(PRUint32* aLength)
{
*aLength = mPoints.Length();
return NS_OK;
}
NS_IMETHODIMP
nsDOMTouchList::Item(PRUint32 aIndex, nsIDOMTouch** aRetVal)
{
- NS_IF_ADDREF(*aRetVal = nsDOMTouchList::GetItemAt(aIndex));
+ NS_IF_ADDREF(*aRetVal = mPoints.SafeElementAt(aIndex, nsnull));
return NS_OK;
}
NS_IMETHODIMP
nsDOMTouchList::IdentifiedTouch(PRInt32 aIdentifier, nsIDOMTouch** aRetVal)
{
*aRetVal = nullptr;
for (PRUint32 i = 0; i < mPoints.Length(); ++i) {
@@ -182,22 +178,16 @@ nsDOMTouchList::IdentifiedTouch(PRInt32
aIdentifier == identifier) {
point.swap(*aRetVal);
break;
}
}
return NS_OK;
}
-nsIDOMTouch*
-nsDOMTouchList::GetItemAt(PRUint32 aIndex)
-{
- return mPoints.SafeElementAt(aIndex, nullptr);
-}
-
// TouchEvent
nsDOMTouchEvent::nsDOMTouchEvent(nsPresContext* aPresContext,
nsTouchEvent* aEvent)
: nsDOMUIEvent(aPresContext, aEvent ? aEvent :
new nsTouchEvent(false, 0, nullptr))
{
if (aEvent) {
@@ -292,21 +282,19 @@ nsDOMTouchEvent::GetTouches(nsIDOMTouchL
// for touchend events, remove any changed touches from the touches array
nsTArray<nsCOMPtr<nsIDOMTouch> > unchangedTouches;
nsTArray<nsCOMPtr<nsIDOMTouch> > touches = touchEvent->touches;
for (PRUint32 i = 0; i < touches.Length(); ++i) {
if (!touches[i]->mChanged) {
unchangedTouches.AppendElement(touches[i]);
}
}
- t = new nsDOMTouchList(static_cast<nsIDOMTouchEvent*>(this),
- unchangedTouches);
+ t = new nsDOMTouchList(unchangedTouches);
} else {
- t = new nsDOMTouchList(static_cast<nsIDOMTouchEvent*>(this),
- touchEvent->touches);
+ t = new nsDOMTouchList(touchEvent->touches);
}
mTouches = t;
return CallQueryInterface(mTouches, aTouches);
}
NS_IMETHODIMP
nsDOMTouchEvent::GetTargetTouches(nsIDOMTouchList** aTargetTouches)
{
@@ -326,18 +314,17 @@ nsDOMTouchEvent::GetTargetTouches(nsIDOM
if ((mEvent->message != NS_TOUCH_END &&
mEvent->message != NS_TOUCH_CANCEL) || !touches[i]->mChanged) {
nsIDOMEventTarget* targetPtr = touches[i]->GetTarget();
if (targetPtr == mEvent->target) {
targetTouches.AppendElement(touches[i]);
}
}
}
- mTargetTouches = new nsDOMTouchList(static_cast<nsIDOMTouchEvent*>(this),
- targetTouches);
+ mTargetTouches = new nsDOMTouchList(targetTouches);
return CallQueryInterface(mTargetTouches, aTargetTouches);
}
NS_IMETHODIMP
nsDOMTouchEvent::GetChangedTouches(nsIDOMTouchList** aChangedTouches)
{
NS_ENSURE_ARG_POINTER(aChangedTouches);
NS_ENSURE_STATE(mEvent);
@@ -349,18 +336,17 @@ nsDOMTouchEvent::GetChangedTouches(nsIDO
nsTArray<nsCOMPtr<nsIDOMTouch> > changedTouches;
nsTouchEvent* touchEvent = static_cast<nsTouchEvent*>(mEvent);
nsTArray<nsCOMPtr<nsIDOMTouch> > touches = touchEvent->touches;
for (PRUint32 i = 0; i < touches.Length(); ++i) {
if (touches[i]->mChanged) {
changedTouches.AppendElement(touches[i]);
}
}
- mChangedTouches = new nsDOMTouchList(static_cast<nsIDOMTouchEvent*>(this),
- changedTouches);
+ mChangedTouches = new nsDOMTouchList(changedTouches);
return CallQueryInterface(mChangedTouches, aChangedTouches);
}
NS_IMETHODIMP
nsDOMTouchEvent::GetAltKey(bool* aAltKey)
{
*aAltKey = static_cast<nsInputEvent*>(mEvent)->IsAlt();
return NS_OK;
--- a/content/events/src/nsDOMTouchEvent.h
+++ b/content/events/src/nsDOMTouchEvent.h
@@ -4,18 +4,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDOMTouchEvent_h_
#define nsDOMTouchEvent_h_
#include "nsDOMUIEvent.h"
#include "nsIDOMTouchEvent.h"
#include "nsString.h"
#include "nsTArray.h"
-#include "nsIDocument.h"
-#include "dombindings.h"
#include "mozilla/Attributes.h"
class nsDOMTouch MOZ_FINAL : public nsIDOMTouch
{
public:
nsDOMTouch(nsIDOMEventTarget* aTarget,
PRInt32 aIdentifier,
PRInt32 aPageX,
@@ -94,56 +92,38 @@ public:
nsIntPoint mScreenPoint;
nsIntPoint mRadius;
float mRotationAngle;
float mForce;
protected:
bool mPointsInitialized;
};
-class nsDOMTouchList MOZ_FINAL : public nsIDOMTouchList,
- public nsWrapperCache
+class nsDOMTouchList : public nsIDOMTouchList
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMTouchList)
+ NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMTouchList)
NS_DECL_NSIDOMTOUCHLIST
- nsDOMTouchList(nsISupports *aParent) : mParent(aParent)
- {
- SetIsDOMBinding();
- }
- nsDOMTouchList(nsISupports *aParent,
- nsTArray<nsCOMPtr<nsIDOMTouch> > &aTouches)
- : mPoints(aTouches),
- mParent(aParent)
- {
- SetIsDOMBinding();
- }
-
- virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
- bool *triedToWrap)
- {
- return mozilla::dom::binding::TouchList::create(cx, scope, this,
- triedToWrap);
- }
-
- nsISupports *GetParentObject()
- {
- return mParent;
- }
+ nsDOMTouchList() { }
+ nsDOMTouchList(nsTArray<nsCOMPtr<nsIDOMTouch> > &aTouches);
void Append(nsIDOMTouch* aPoint)
{
mPoints.AppendElement(aPoint);
}
+ nsIDOMTouch* GetItemAt(PRUint32 aIndex)
+ {
+ return mPoints.SafeElementAt(aIndex, nsnull);
+ }
+
protected:
nsTArray<nsCOMPtr<nsIDOMTouch> > mPoints;
- nsCOMPtr<nsISupports> mParent;
};
class nsDOMTouchEvent : public nsDOMUIEvent,
public nsIDOMTouchEvent
{
public:
nsDOMTouchEvent(nsPresContext* aPresContext, nsTouchEvent* aEvent);
virtual ~nsDOMTouchEvent();
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -483,26 +483,25 @@ nsGenericHTMLElement::GetOffsetRect(nsRe
Element* docElement = GetCurrentDoc()->GetRootElement();
nsIContent* content = frame->GetContent();
if (content && (content->IsHTML(nsGkAtoms::body) || content == docElement)) {
parent = frame;
}
else {
- const bool isPositioned = frame->GetStyleDisplay()->IsPositioned();
- const bool isAbsolutelyPositioned =
- frame->GetStyleDisplay()->IsAbsolutelyPositioned();
+ const bool isPositioned = frame->IsPositioned();
+ const bool isAbsolutelyPositioned = frame->IsAbsolutelyPositioned();
origin += frame->GetPositionIgnoringScrolling();
for ( ; parent ; parent = parent->GetParent()) {
content = parent->GetContent();
// Stop at the first ancestor that is positioned.
- if (parent->GetStyleDisplay()->IsPositioned()) {
+ if (parent->IsPositioned()) {
*aOffsetParent = content;
NS_IF_ADDREF(*aOffsetParent);
break;
}
// Add the parent's origin to our own to get to the
// right coordinate system.
const bool isOffsetParent = !isPositioned && IsOffsetParent(parent);
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -100,47 +100,53 @@ MOCHITEST_FILES = \
test_playback_errors.html \
test_seekable1.html \
test_preload_actions.html \
test_preload_attribute.html \
test_progress.html \
test_reactivate.html \
test_readyState.html \
test_replay_metadata.html \
- test_seek.html \
test_seek2.html \
- test_seek_out_of_range.html \
test_source.html \
test_source_write.html \
test_source_null.html \
test_standalone.html \
test_streams_element_capture.html \
test_streams_element_capture_reset.html \
- test_timeupdate_small_files.html \
test_too_many_elements.html \
test_volume.html \
test_video_to_canvas.html \
use_large_cache.js \
test_audiowrite.html \
test_mozHasAudio.html \
test_source_media.html \
- $(NULL)
-
-# Bug 759221
-MOCHITEST_FILES += \
test_autoplay_contentEditable.html \
test_buffered.html \
test_bug448534.html \
test_bug463162.xhtml \
test_decoder_disable.html \
test_media_selection.html \
test_playback.html \
test_seekLies.html \
$(NULL)
+# Tests disabled on Linux for frequent intermittent failures
+ifneq (Linux,$(OS_ARCH))
+MOCHITEST_FILES += \
+ test_seek.html \
+ test_seek_out_of_range.html \
+ test_timeupdate_small_files.html \
+ $(NULL)
+else
+$(warning test_seek.html is disabled on Linux for timeouts. Bug 620598)
+$(warning test_seek_out_of_range.html is disabled on Linux for timeouts. Bug 661076)
+$(warning test_timeupdate_small_files.html is disabled on Linux for timeouts. Bug 687972)
+endif
+
# Don't run in suite
ifndef MOZ_SUITE
MOCHITEST_FILES += test_play_twice.html
else
$(warning test_play_twice.html is disabled pending investigation. Bug 598252)
endif
# These tests are disabled until we figure out random failures.
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -2,29 +2,29 @@
// be ignored. To make sure tests respect that, we include a file of type
// "bogus/duh" in each list.
// These are small test files, good for just seeing if something loads. We
// really only need one test file per backend here.
var gSmallTests = [
{ name:"small-shot.ogg", type:"audio/ogg", duration:0.276 },
{ name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
- { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.233 },
+ { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.266 },
{ name:"seek.webm", type:"video/webm", width:320, height:240, duration:3.966 },
{ name:"detodos.opus", type:"audio/ogg; codecs=opus", duration:2.9135 },
{ name:"bogus.duh", type:"bogus/duh" }
];
// Used by test_progress to ensure we get the correct progress information
// during resource download.
var gProgressTests = [
{ name:"r11025_u8_c1.wav", type:"audio/x-wav", duration:1.0, size:11069 },
{ name:"big.wav", type:"audio/x-wav", duration:9.278981, size:102444 },
{ name:"seek.ogv", type:"video/ogg", duration:3.966, size:285310 },
- { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.233, size:28942 },
+ { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.266, size:28942 },
{ name:"seek.webm", type:"video/webm", duration:3.966, size:215529 },
{ name:"bogus.duh", type:"bogus/duh" }
];
// Used by test_played.html
var gPlayedTests = [
{ name:"big.wav", type:"audio/x-wav", duration:9.0 },
{ name:"sound.ogg", type:"audio/ogg", duration:4.0 },
@@ -35,17 +35,17 @@ var gPlayedTests = [
// Used by test_mozLoadFrom. Need one test file per decoder backend, plus
// anything for testing clone-specific bugs.
var cloneKey = Math.floor(Math.random()*100000000);
var gCloneTests = gSmallTests.concat([
// Actual duration is ~200ms, we have Content-Duration lie about it.
{ name:"bug520908.ogv", type:"video/ogg", duration:9000 },
// short-video is more like 1s, so if you load this twice you'll get an unexpected duration
{ name:"dynamic_resource.sjs?key=" + cloneKey + "&res1=320x240.ogv&res2=short-video.ogv",
- type:"video/ogg", duration:0.233 },
+ type:"video/ogg", duration:0.266 },
]);
// Used by test_play_twice. Need one test file per decoder backend, plus
// anything for testing bugs that occur when replying a played file.
var gReplayTests = gSmallTests.concat([
{ name:"bug533822.ogg", type:"audio/ogg" },
]);
@@ -54,17 +54,17 @@ var gReplayTests = gSmallTests.concat([
var gPausedAfterEndedTests = gSmallTests.concat([
{ name:"r11025_u8_c1.wav", type:"audio/x-wav", duration:1.0 },
{ name:"small-shot.ogg", type:"video/ogg", duration:0.276 }
]);
// Test the mozHasAudio property
var gMozHasAudioTests = [
{ name:"big.wav", type:"audio/x-wav", duration:9.278981, size:102444, hasAudio:undefined },
- { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.233, size:28942, hasAudio:false },
+ { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.266, size:28942, hasAudio:false },
{ name:"short-video.ogv", type:"video/ogg", duration:1.081, hasAudio:true },
{ name:"seek.webm", type:"video/webm", duration:3.966, size:215529, hasAudio:false },
{ name:"bogus.duh", type:"bogus/duh" }
];
// These are files that we want to make sure we can play through. We can
// also check metadata. Put files of the same type together in this list so if
// something crashes we have some idea of which backend is responsible.
@@ -123,17 +123,17 @@ var gPlayTests = [
{ name:"audio-overhang.ogg", type:"audio/ogg", duration:2.3 },
{ name:"video-overhang.ogg", type:"audio/ogg", duration:3.966 },
// bug461281.ogg with the middle second chopped out.
{ name:"audio-gaps.ogg", type:"audio/ogg", duration:2.208 },
// Test playback/metadata work after a redirect
{ name:"redirect.sjs?domain=mochi.test:8888&file=320x240.ogv",
- type:"video/ogg", duration:0.233 },
+ type:"video/ogg", duration:0.266 },
// Test playback of a webm file
{ name:"seek.webm", type:"video/webm", duration:3.966 },
// Test playback of a WebM file with non-zero start time.
{ name:"split.webm", type:"video/webm", duration:1.967 },
// Test playback of a raw file
@@ -253,17 +253,17 @@ var gErrorTests = [
{ name:"bogus.duh", type:"bogus/duh" }
];
// These are files that have nontrivial duration and are useful for seeking within.
var gSeekTests = [
{ name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
{ name:"audio.wav", type:"audio/x-wav", duration:0.031247 },
{ name:"seek.ogv", type:"video/ogg", duration:3.966 },
- { name:"320x240.ogv", type:"video/ogg", duration:0.233 },
+ { name:"320x240.ogv", type:"video/ogg", duration:0.266 },
{ name:"seek.webm", type:"video/webm", duration:3.966 },
{ name:"bug516323.indexed.ogv", type:"video/ogg", duration:4.208 },
{ name:"split.webm", type:"video/webm", duration:1.967 },
{ name:"detodos.opus", type:"audio/ogg; codecs=opus", duration:2.9135 },
{ name:"bogus.duh", type:"bogus/duh", duration:123 }
];
// These are files suitable for using with a "new Audio" constructor.
--- a/content/media/test/test_timeupdate_small_files.html
+++ b/content/media/test/test_timeupdate_small_files.html
@@ -49,17 +49,21 @@ function ended(e) {
500);
}
var eventsToLog = ["play", "canplay", "canplaythrough", "loadstart", "loadedmetadata",
"loadeddata", "playing", "progress", "timeupdate", "ended", "suspend", "error", "stalled", "emptied", "abort",
"waiting", "pause"];
function logEvent(event) {
if (event.target.gotEnded > (event.type == "ended" ? 1 : 0)) {
- ok(false, event.target.currentSrc + " got unexpected " + event.type + " after ended");
+ if (event.target.currentSrc.slice(-9) == "seek.webm" && event.type == "stalled") {
+ todo(false, event.target.currentSrc + " got unexpected stalled after ended (bug 760770)");
+ } else {
+ ok(false, event.target.currentSrc + " got unexpected " + event.type + " after ended");
+ }
} else {
info(event.target.currentSrc + " got " + event.type);
}
}
function startTest(test, token) {
var type = /^video/.test(test.type) ? "video" : "audio";
var v = document.createElement(type);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -187,16 +187,19 @@
#include "nsXULAppAPI.h"
#include "nsDOMNavigationTiming.h"
#include "nsITimedChannel.h"
#include "mozilla/StartupTimeline.h"
#include "nsIFrameMessageManager.h"
+#include "mozilla/Telemetry.h"
+#include "nsISecurityUITelemetry.h"
+
static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
#if defined(DEBUG_bryner) || defined(DEBUG_chb)
//#define DEBUG_DOCSHELL_FOCUS
#define DEBUG_PAGE_CACHE
#endif
@@ -4035,30 +4038,42 @@ nsDocShell::DisplayLoadError(nsresult aE
nsCOMPtr<nsIStrictTransportSecurityService> stss =
do_GetService(NS_STSSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
bool isStsHost = false;
rv = stss->IsStsURI(aURI, &isStsHost);
NS_ENSURE_SUCCESS(rv, rv);
- if (isStsHost)
+ PRUint32 bucketId;
+ if (isStsHost) {
cssClass.AssignLiteral("badStsCert");
+ //measuring STS separately allows us to measure click through
+ //rates easily
+ bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_STS;
+ } else {
+ bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT;
+ }
+
if (Preferences::GetBool(
"browser.xul.error_pages.expert_bad_cert", false)) {
cssClass.AssignLiteral("expertBadCert");
}
// See if an alternate cert error page is registered
nsAdoptingCString alternateErrorPage =
Preferences::GetCString(
"security.alternate_certificate_error_page");
if (alternateErrorPage)
errorPage.Assign(alternateErrorPage);
+
+ if (errorPage.EqualsIgnoreCase("certerror"))
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);
+
} else {
error.AssignLiteral("nssFailure2");
}
}
} else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError) {
nsCAutoString host;
aURI->GetHost(host);
CopyUTF8toUTF16(host, formatStrs[0]);
@@ -4066,20 +4081,29 @@ nsDocShell::DisplayLoadError(nsresult aE
// Malware and phishing detectors may want to use an alternate error
// page, but if the pref's not set, we'll fall back on the standard page
nsAdoptingCString alternateErrorPage =
Preferences::GetCString("urlclassifier.alternate_error_page");
if (alternateErrorPage)
errorPage.Assign(alternateErrorPage);
- if (NS_ERROR_PHISHING_URI == aError)
+ PRUint32 bucketId;
+ if (NS_ERROR_PHISHING_URI == aError) {
error.AssignLiteral("phishingBlocked");
- else
+ bucketId = nsISecurityUITelemetry::WARNING_PHISHING_PAGE;
+ } else {
error.AssignLiteral("malwareBlocked");
+ bucketId = nsISecurityUITelemetry::WARNING_MALWARE_PAGE;
+ }
+
+ if (errorPage.EqualsIgnoreCase("blocked"))
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI,
+ bucketId);
+
cssClass.AssignLiteral("blacklist");
}
else {
// Errors requiring simple formatting
switch (aError) {
case NS_ERROR_MALFORMED_URI:
// URI is malformed
error.AssignLiteral("malformedURI");
@@ -11376,17 +11400,17 @@ nsDocShell::GetIsContent(bool *aIsConten
NS_IMETHODIMP
nsDocShell::GetExtendedOrigin(nsIURI *aUri, nsACString &aResult)
{
bool isInBrowserElement;
GetIsInBrowserElement(&isInBrowserElement);
nsCOMPtr<nsIScriptSecurityManager> ssmgr =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
- NS_ENSURE_TRUE(ssmgr, false);
+ NS_ENSURE_TRUE(ssmgr, NS_ERROR_FAILURE);
return ssmgr->GetExtendedOrigin(aUri, mAppId, isInBrowserElement, aResult);
}
bool
nsDocShell::IsOKToLoadURI(nsIURI* aURI)
{
--- a/dom/alarm/AlarmDB.jsm
+++ b/dom/alarm/AlarmDB.jsm
@@ -74,52 +74,81 @@ AlarmDB.prototype = {
aSuccessCb,
aErrorCb
);
},
/**
* @param aId
* The ID of record to be removed.
+ * @param aManifestURL
+ * The manifest URL of the app that alarm belongs to.
+ * If null, directly remove the ID record; otherwise,
+ * need to check if the alarm belongs to this app.
* @param aSuccessCb
* Callback function to invoke with result.
* @param aErrorCb [optional]
* Callback function to invoke when there was an error.
*/
- remove: function remove(aId, aSuccessCb, aErrorCb) {
+ remove: function remove(aId, aManifestURL, aSuccessCb, aErrorCb) {
debug("remove()");
this.newTxn(
"readwrite",
function txnCb(aTxn, aStore) {
debug("Going to remove " + aId);
- aStore.delete(aId);
+
+ // Look up the existing record and compare the manifestURL
+ // to see if the alarm to be removed belongs to this app.
+ aStore.get(aId).onsuccess = function doRemove(aEvent) {
+ let alarm = aEvent.target.result;
+
+ if (!alarm) {
+ debug("Alarm doesn't exist. No need to remove it.");
+ return;
+ }
+
+ if (aManifestURL && aManifestURL != alarm.manifestURL) {
+ debug("Cannot remove the alarm added by other apps.");
+ return;
+ }
+
+ aStore.delete(aId);
+ };
},
aSuccessCb,
aErrorCb
);
},
/**
+ * @param aManifestURL
+ * The manifest URL of the app that alarms belong to.
+ * If null, directly return all alarms; otherwise,
+ * only return the alarms that belong to this app.
* @param aSuccessCb
* Callback function to invoke with result array.
* @param aErrorCb [optional]
* Callback function to invoke when there was an error.
*/
- getAll: function getAll(aSuccessCb, aErrorCb) {
+ getAll: function getAll(aManifestURL, aSuccessCb, aErrorCb) {
debug("getAll()");
this.newTxn(
"readonly",
function txnCb(aTxn, aStore) {
if (!aTxn.result)
- aTxn.result = {};
+ aTxn.result = [];
aStore.mozGetAll().onsuccess = function setTxnResult(aEvent) {
- aTxn.result = aEvent.target.result;
+ aEvent.target.result.forEach(function addAlarm(aAlarm) {
+ if (!aManifestURL || aManifestURL == aAlarm.manifestURL)
+ aTxn.result.push(aAlarm);
+ });
+
debug("Request successful. Record count: " + aTxn.result.length);
};
},
aSuccessCb,
aErrorCb
);
}
};
--- a/dom/alarm/AlarmService.jsm
+++ b/dom/alarm/AlarmService.jsm
@@ -75,16 +75,17 @@ let AlarmService = {
receiveMessage: function receiveMessage(aMessage) {
debug("receiveMessage(): " + aMessage.name);
let mm = aMessage.target.QueryInterface(Ci.nsIFrameMessageManager);
let json = aMessage.json;
switch (aMessage.name) {
case "AlarmsManager:GetAll":
this._db.getAll(
+ json.manifestURL,
function getAllSuccessCb(aAlarms) {
debug("Callback after getting alarms from database: " + JSON.stringify(aAlarms));
this._sendAsyncMessage(mm, "GetAll", true, json.requestId, aAlarms);
}.bind(this),
function getAllErrorCb(aErrorMsg) {
this._sendAsyncMessage(mm, "GetAll", false, json.requestId, aErrorMsg);
}.bind(this)
);
@@ -145,30 +146,34 @@ let AlarmService = {
this._sendAsyncMessage(mm, "Add", false, json.requestId, aErrorMsg);
}.bind(this)
);
break;
case "AlarmsManager:Remove":
this._removeAlarmFromDb(
json.id,
+ json.manifestURL,
function removeSuccessCb() {
debug("Callback after removing alarm from database.");
// if there is no alarm being set
if (!this._currentAlarm) {
this._debugCurrentAlarm();
return;
}
// check if the alarm to be removed is in the queue
+ // by ID and whether it belongs to the requesting app
let alarmQueue = this._alarmQueue;
- if (this._currentAlarm.id != json.id) {
+ if (this._currentAlarm.id != json.id ||
+ this._currentAlarm.manifestURL != json.manifestURL) {
for (let i = 0; i < alarmQueue.length; i++) {
- if (alarmQueue[i].id == json.id) {
+ if (alarmQueue[i].id == json.id &&
+ alarmQueue[i].manifestURL == json.manifestURL) {
alarmQueue.splice(i, 1);
break;
}
}
this._debugCurrentAlarm();
return;
}
@@ -219,29 +224,30 @@ let AlarmService = {
default:
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
break;
}
aMessageManager.sendAsyncMessage("AlarmsManager:" + aMessageName + ":Return:" + (aSuccess ? "OK" : "KO"), json);
},
- _removeAlarmFromDb: function _removeAlarmFromDb(aId, aRemoveSuccessCb) {
+ _removeAlarmFromDb: function _removeAlarmFromDb(aId, aManifestURL, aRemoveSuccessCb) {
debug("_removeAlarmFromDb()");
// If the aRemoveSuccessCb is undefined or null, set a
// dummy callback for it which is needed for _db.remove()
if (!aRemoveSuccessCb) {
aRemoveSuccessCb = function removeSuccessCb() {
debug("Remove alarm from DB successfully.");
};
}
this._db.remove(
aId,
+ aManifestURL,
aRemoveSuccessCb,
function removeErrorCb(aErrorMsg) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}
);
},
_fireSystemMessage: function _fireSystemMessage(aAlarm) {
@@ -250,31 +256,31 @@ let AlarmService = {
messenger.sendMessage("alarm", aAlarm, manifestURI);
},
_onAlarmFired: function _onAlarmFired() {
debug("_onAlarmFired()");
if (this._currentAlarm) {
this._fireSystemMessage(this._currentAlarm);
- this._removeAlarmFromDb(this._currentAlarm.id);
+ this._removeAlarmFromDb(this._currentAlarm.id, null);
this._currentAlarm = null;
}
// Reset the next alarm from the queue.
let alarmQueue = this._alarmQueue;
while (alarmQueue.length > 0) {
let nextAlarm = alarmQueue.shift();
let nextAlarmTime = this._getAlarmTime(nextAlarm);
// If the next alarm has been expired, directly
// fire system message for it instead of setting it.
if (nextAlarmTime <= Date.now()) {
this._fireSystemMessage(nextAlarm);
- this._removeAlarmFromDb(nextAlarm.id);
+ this._removeAlarmFromDb(nextAlarm.id, null);
} else {
this._currentAlarm = nextAlarm;
break;
}
}
this._debugCurrentAlarm();
},
@@ -284,16 +290,17 @@ let AlarmService = {
this._currentTimezoneOffset = aTimezoneOffset;
this._restoreAlarmsFromDb();
},
_restoreAlarmsFromDb: function _restoreAlarmsFromDb() {
debug("_restoreAlarmsFromDb()");
this._db.getAll(
+ null,
function getAllSuccessCb(aAlarms) {
debug("Callback after getting alarms from database: " + JSON.stringify(aAlarms));
// clear any alarms set or queued in the cache
let alarmQueue = this._alarmQueue;
alarmQueue.length = 0;
this._currentAlarm = null;
--- a/dom/alarm/AlarmsManager.js
+++ b/dom/alarm/AlarmsManager.js
@@ -73,27 +73,27 @@ AlarmsManager.prototype = {
return request;
},
remove: function remove(aId) {
debug("remove()");
return this._cpmm.sendSyncMessage(
"AlarmsManager:Remove",
- { id: aId }
+ { id: aId, manifestURL: this._manifestURL }
);
},
getAll: function getAll() {
debug("getAll()");
let request = this.createRequest();
this._cpmm.sendAsyncMessage(
"AlarmsManager:GetAll",
- { requestId: this.getRequestId(request) }
+ { requestId: this.getRequestId(request), manifestURL: this._manifestURL }
);
return request;
},
receiveMessage: function receiveMessage(aMessage) {
debug("receiveMessage(): " + aMessage.name);
let json = aMessage.json;
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -415,17 +415,16 @@
#include "nsIImageDocument.h"
// Storage includes
#include "nsDOMStorage.h"
// Device Storage
#include "nsIDOMDeviceStorage.h"
-#include "nsIDOMDeviceStorageChangeEvent.h"
#include "nsIDOMDeviceStorageCursor.h"
// Drag and drop
#include "nsIDOMDataTransfer.h"
// Geolocation
#include "nsIDOMGeoGeolocation.h"
#include "nsIDOMGeoPosition.h"
@@ -1435,19 +1434,16 @@ static nsDOMClassInfoData sClassInfoData
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MessageEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(DeviceStorage, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
- NS_DEFINE_CLASSINFO_DATA(DeviceStorageChangeEvent, nsDOMGenericSH,
- DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
NS_DEFINE_CLASSINFO_DATA(DeviceStorageCursor, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(GeoGeolocation, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(GeoPosition, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -1628,18 +1624,18 @@ static nsDOMClassInfoData sClassInfoData
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(IDBVersionChangeEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(IDBOpenDBRequest, IDBEventTargetSH,
IDBEVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(Touch, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
- NS_DEFINE_CLASSINFO_DATA(TouchList, nsDOMGenericSH,
- DOM_DEFAULT_SCRIPTABLE_FLAGS)
+ NS_DEFINE_CLASSINFO_DATA(TouchList, nsDOMTouchListSH,
+ ARRAY_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(TouchEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MozCSSKeyframeRule, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MozCSSKeyframesRule, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -4038,21 +4034,16 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(DeviceStorage, nsIDOMDeviceStorage)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceStorage)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_END
- DOM_CLASSINFO_MAP_BEGIN(DeviceStorageChangeEvent, nsIDOMDeviceStorageChangeEvent)
- DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceStorageChangeEvent)
- DOM_CLASSINFO_MAP_ENTRY(nsIDOMEvent)
- DOM_CLASSINFO_MAP_END
-
DOM_CLASSINFO_MAP_BEGIN(DeviceStorageCursor, nsIDOMDeviceStorageCursor)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceStorageCursor)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(GeoGeolocation, nsIDOMGeoGeolocation)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMGeoGeolocation)
@@ -4882,16 +4873,24 @@ nsDOMClassInfo::NewResolve(nsIXPConnectW
{
if (id == sConstructor_id && !(flags & JSRESOLVE_ASSIGNING)) {
return ResolveConstructor(cx, obj, objp);
}
return NS_OK;
}
+nsISupports*
+nsDOMTouchListSH::GetItemAt(nsISupports *aNative, PRUint32 aIndex,
+ nsWrapperCache **aCache, nsresult *aResult)
+{
+ nsDOMTouchList* list = static_cast<nsDOMTouchList*>(aNative);
+ return list->GetItemAt(aIndex);
+}
+
NS_IMETHODIMP
nsDOMClassInfo::Convert(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, PRUint32 type, jsval *vp,
bool *_retval)
{
NS_WARNING("nsDOMClassInfo::Convert Don't call me!");
return NS_ERROR_UNEXPECTED;
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -1211,16 +1211,37 @@ protected:
public:
static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
{
return new nsCSSRuleListSH(aData);
}
};
+class nsDOMTouchListSH : public nsArraySH
+{
+ protected:
+ nsDOMTouchListSH(nsDOMClassInfoData* aData) : nsArraySH(aData)
+ {
+ }
+
+ virtual ~nsDOMTouchListSH()
+ {
+ }
+
+ virtual nsISupports* GetItemAt(nsISupports *aNative, PRUint32 aIndex,
+ nsWrapperCache **aCache, nsresult *aResult);
+
+ public:
+ static nsIClassInfo* doCreate(nsDOMClassInfoData* aData)
+ {
+ return new nsDOMTouchListSH(aData);
+ }
+};
+
#ifdef MOZ_XUL
// TreeColumns helper
class nsTreeColumnsSH : public nsNamedArraySH
{
protected:
nsTreeColumnsSH(nsDOMClassInfoData* aData) : nsNamedArraySH(aData)
{
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -380,17 +380,16 @@ DOMCI_CLASS(ModalContentWindow)
// Data Events
DOMCI_CLASS(DataContainerEvent)
// event used for cross-domain message-passing and for server-sent events in
// HTML5
DOMCI_CLASS(MessageEvent)
DOMCI_CLASS(DeviceStorage)
-DOMCI_CLASS(DeviceStorageChangeEvent)
DOMCI_CLASS(DeviceStorageCursor)
// Geolocation
DOMCI_CLASS(GeoGeolocation)
DOMCI_CLASS(GeoPosition)
DOMCI_CLASS(GeoPositionCoords)
DOMCI_CLASS(GeoPositionError)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1241,16 +1241,22 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_
return tmp->IsBlackForCC();
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindow)
return tmp->IsBlackForCC();
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGlobalWindow)
+ if (NS_UNLIKELY(cb.WantDebugInfo())) {
+ char name[512];
+ PR_snprintf(name, sizeof(name), "nsGlobalWindow #%ld", tmp->mWindowID);
+ cb.DescribeRefCountedNode(tmp->mRefCnt.get(), sizeof(nsGlobalWindow), name);
+ }
+
if (!cb.WantAllTraces() && tmp->IsBlackForCC()) {
return NS_SUCCESS_INTERRUPTED_TRAVERSE;
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mControllers)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArguments)
--- a/dom/base/nsHistory.cpp
+++ b/dom/base/nsHistory.cpp
@@ -234,18 +234,18 @@ nsHistory::Go(PRInt32 aDelta)
NS_ENSURE_TRUE(session_history, NS_ERROR_FAILURE);
// QI SHistory to nsIWebNavigation
nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(session_history));
NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
PRInt32 curIndex=-1;
PRInt32 len = 0;
- nsresult rv = session_history->GetIndex(&curIndex);
- rv = session_history->GetCount(&len);
+ session_history->GetIndex(&curIndex);
+ session_history->GetCount(&len);
PRInt32 index = curIndex + aDelta;
if (index > -1 && index < len)
webnav->GotoIndex(index);
// We always want to return a NS_OK, since returning errors
// from GotoIndex() can lead to exceptions and a possible leak
// of history length
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -129,16 +129,17 @@ BrowserElementParentFactory.prototype =
};
function BrowserElementParent(frameLoader, hasRemoteFrame) {
debug("Creating new BrowserElementParent object for " + frameLoader);
this._domRequestCounter = 0;
this._pendingDOMRequests = {};
this._hasRemoteFrame = hasRemoteFrame;
+ this._frameLoader = frameLoader;
this._frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
if (!this._frameElement) {
debug("No frame element?");
return;
}
this._mm = frameLoader.messageManager;
@@ -173,16 +174,17 @@ function BrowserElementParent(frameLoade
addMessageListener('got-can-go-back', this._gotDOMRequestResult);
addMessageListener('got-can-go-forward', this._gotDOMRequestResult);
addMessageListener('fullscreen-origin-change', this._remoteFullscreenOriginChange);
addMessageListener('rollback-fullscreen', this._remoteFrameFullscreenReverted);
addMessageListener('exit-fullscreen', this._exitFullscreen);
let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
os.addObserver(this, 'ask-children-to-exit-fullscreen', /* ownsWeak = */ true);
+ os.addObserver(this, 'oop-frameloader-crashed', /* ownsWeak = */ true);
function defineMethod(name, fn) {
XPCNativeWrapper.unwrap(self._frameElement)[name] = function() {
if (self._isAlive()) {
return fn.apply(self, arguments);
}
};
}
@@ -469,18 +471,36 @@ BrowserElementParent.prototype = {
let origin = data.json;
this._windowUtils.remoteFrameFullscreenChanged(this._frameElement, origin);
},
_remoteFrameFullscreenReverted: function(data) {
this._windowUtils.remoteFrameFullscreenReverted();
},
+ _fireFatalError: function() {
+ let evt = this._createEvent('error', {type: 'fatal'},
+ /* cancelable = */ false);
+ this._frameElement.dispatchEvent(evt);
+ },
+
observe: function(subject, topic, data) {
- if (topic == 'ask-children-to-exit-fullscreen' &&
- this._isAlive() &&
- this._frameElement.ownerDocument == subject &&
- this._hasRemoteFrame)
- this._sendAsyncMsg('exit-fullscreen');
+ switch(topic) {
+ case 'oop-frameloader-crashed':
+ if (this._isAlive() && subject == this._frameLoader) {
+ this._fireFatalError();
+ }
+ break;
+ case 'ask-children-to-exit-fullscreen':
+ if (this._isAlive() &&
+ this._frameElement.ownerDocument == subject &&
+ this._hasRemoteFrame) {
+ this._sendAsyncMsg('exit-fullscreen');
+ }
+ break;
+ default:
+ debug('Unknown topic: ' + topic);
+ break;
+ };
},
};
var NSGetFactory = XPCOMUtils.generateNSGetFactory([BrowserElementParentFactory]);
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -28,16 +28,17 @@
#include "nsContentUtils.h"
#include "nsXULAppAPI.h"
#include "TabChild.h"
#include "DeviceStorageRequestChild.h"
#include "nsIDOMDeviceStorageChangeEvent.h"
#include "nsCRT.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
+#include "GeneratedEvents.h"
// Microsoft's API Name hackery sucks
#undef CreateEvent
#ifdef MOZ_WIDGET_GONK
#include "nsIVolumeService.h"
#endif
@@ -433,102 +434,16 @@ jsval StringToJsval(nsPIDOMWindow* aWind
jsval result = JSVAL_NULL;
if (!xpc::StringToJsval(cx, aString, &result)) {
return JSVAL_NULL;
}
return result;
}
-class nsDOMDeviceStorageChangeEvent : public nsDOMEvent, public nsIDOMDeviceStorageChangeEvent
-{
-public:
- NS_DECL_ISUPPORTS_INHERITED
- NS_FORWARD_NSIDOMEVENT(nsDOMEvent::)
- NS_DECL_NSIDOMDEVICESTORAGECHANGEEVENT
-
- nsDOMDeviceStorageChangeEvent();
-
- virtual nsresult InitFromCtor(const nsAString& aType,
- JSContext* aCx,
- jsval* aVal);
-
- NS_IMETHOD Init(const nsAString & aEventTypeArg,
- bool aCanBubbleArg,
- bool aCancelableArg,
- nsAString& aPath,
- nsAString& aReason);
-
-private:
- ~nsDOMDeviceStorageChangeEvent();
-
-protected:
- nsString mPath;
- nsString mReason;
-};
-
-DOMCI_DATA(DeviceStorageChangeEvent, nsDOMDeviceStorageChangeEvent)
-
-NS_INTERFACE_MAP_BEGIN(nsDOMDeviceStorageChangeEvent)
- NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceStorageChangeEvent)
- NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DeviceStorageChangeEvent)
-NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
-
-NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorageChangeEvent, nsDOMEvent)
-NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorageChangeEvent, nsDOMEvent)
-
-nsDOMDeviceStorageChangeEvent::nsDOMDeviceStorageChangeEvent()
- : nsDOMEvent(nullptr, nullptr)
-{
-}
-
-nsDOMDeviceStorageChangeEvent::~nsDOMDeviceStorageChangeEvent()
-{
-}
-
-NS_IMETHODIMP
-nsDOMDeviceStorageChangeEvent::Init(const nsAString & aEventTypeArg,
- bool aCanBubbleArg,
- bool aCancelableArg,
- nsAString& aPath,
- nsAString& aReason)
-{
- nsresult rv = nsDOMEvent::InitEvent(aEventTypeArg, aCanBubbleArg, aCancelableArg);
- NS_ENSURE_SUCCESS(rv, rv);
-
- mPath = aPath;
- mReason = aReason;
- return NS_OK;
-}
-
-nsresult
-nsDOMDeviceStorageChangeEvent::InitFromCtor(const nsAString& aType,
- JSContext* aCx,
- jsval* aVal)
-{
- mozilla::dom::DeviceStorageChangeEventInit d;
- nsresult rv = d.Init(aCx, aVal);
- NS_ENSURE_SUCCESS(rv, rv);
- return Init(aType, d.bubbles, d.cancelable, d.path, d.reason);
-}
-
-NS_IMETHODIMP
-nsDOMDeviceStorageChangeEvent::GetPath(nsAString & aPath)
-{
- aPath = mPath;
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMDeviceStorageChangeEvent::GetReason(nsAString & aReason)
-{
- aReason = mReason;
- return NS_OK;
-}
-
class DeviceStorageCursorRequest MOZ_FINAL
: public nsIContentPermissionRequest
, public PCOMContentPermissionRequestChild
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageCursorRequest, nsIContentPermissionRequest)
@@ -1663,26 +1578,28 @@ nsDOMDeviceStorage::Update(const char* a
if (!StringBeginsWith(fullpath, rootpath)) {
NS_WARNING("Observing a path outside of our root!");
return NS_OK;
}
nsAString::size_type len = rootpath.Length() + 1; // +1 for the trailing /
nsDependentSubstring newPath (fullpath, len, fullpath.Length() - len);
- nsRefPtr<nsDOMDeviceStorageChangeEvent> event = new nsDOMDeviceStorageChangeEvent();
+ nsCOMPtr<nsIDOMEvent> event;
+ NS_NewDOMDeviceStorageChangeEvent(getter_AddRefs(event), nullptr, nullptr);
+
+ nsCOMPtr<nsIDOMDeviceStorageChangeEvent> ce = do_QueryInterface(event);
+
nsString reason;
reason.AssignWithConversion(aReason);
- rv = event->Init(NS_LITERAL_STRING("change"), true, false, newPath, reason);
+ rv = ce->InitDeviceStorageChangeEvent(NS_LITERAL_STRING("change"), true, false, newPath, reason);
NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIDOMDeviceStorageChangeEvent> e = event.get();
-
bool ignore;
- DispatchEvent(e, &ignore);
+ DispatchEvent(ce, &ignore);
return NS_OK;
}
NS_IMETHODIMP
nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
nsIDOMEventListener *aListener,
bool aUseCapture,
bool aWantsUntrusted,
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -3,16 +3,17 @@
/* 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 "base/basictypes.h"
#include "IDBObjectStore.h"
+#include "mozilla/dom/ipc/nsIRemoteBlob.h"
#include "nsIJSContextStack.h"
#include "nsIOutputStream.h"
#include "jsfriendapi.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/StructuredCloneTags.h"
#include "mozilla/dom/ipc/Blob.h"
@@ -580,16 +581,57 @@ GetAddInfoCallback(JSContext* aCx, void*
if (!IDBObjectStore::SerializeValue(aCx, data->mCloneWriteInfo,
data->mValue)) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
return NS_OK;
}
+inline
+BlobChild*
+ActorFromRemoteBlob(nsIDOMBlob* aBlob)
+{
+ NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlob);
+ if (remoteBlob) {
+ BlobChild* actor =
+ static_cast<BlobChild*>(static_cast<PBlobChild*>(remoteBlob->GetPBlob()));
+ NS_ASSERTION(actor, "Null actor?!");
+ return actor;
+ }
+ return nullptr;
+}
+
+inline
+bool
+ResolveMysteryBlob(nsIDOMBlob* aBlob, const nsString& aName,
+ const nsString& aContentType, PRUint64 aSize)
+{
+ BlobChild* actor = ActorFromRemoteBlob(aBlob);
+ if (actor) {
+ return actor->SetMysteryBlobInfo(aName, aContentType, aSize);
+ }
+ return true;
+}
+
+inline
+bool
+ResolveMysteryBlob(nsIDOMBlob* aBlob, const nsString& aContentType,
+ PRUint64 aSize)
+{
+ BlobChild* actor = ActorFromRemoteBlob(aBlob);
+ if (actor) {
+ return actor->SetMysteryBlobInfo(aContentType, aSize);
+ }
+ return true;
+}
+
} // anonymous namespace
JSClass IDBObjectStore::sDummyPropJSClass = {
"dummy", 0,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub
@@ -1093,16 +1135,19 @@ IDBObjectStore::StructuredCloneReadCallb
NS_WARNING("Failed to get file!");
return nullptr;
}
}
if (aTag == SCTAG_DOM_BLOB) {
nsCOMPtr<nsIDOMBlob> domBlob;
if (file.mFile) {
+ if (!ResolveMysteryBlob(file.mFile, convType, size)) {
+ return nullptr;
+ }
domBlob = file.mFile;
}
else {
domBlob = new nsDOMFileFile(convType, size, nativeFile, fileInfo);
}
jsval wrappedBlob;
rv =
@@ -1121,16 +1166,19 @@ IDBObjectStore::StructuredCloneReadCallb
nsCString name;
if (!StructuredCloneReadString(aReader, name)) {
return nullptr;
}
NS_ConvertUTF8toUTF16 convName(name);
nsCOMPtr<nsIDOMFile> domFile;
if (file.mFile) {
+ if (!ResolveMysteryBlob(file.mFile, convName, convType, size)) {
+ return nullptr;
+ }
domFile = do_QueryInterface(file.mFile);
NS_ASSERTION(domFile, "This should never fail!");
}
else {
domFile = new nsDOMFileFile(convName, convType, size, nativeFile,
fileInfo);
}
--- a/dom/indexedDB/test/test_blob_simple.html
+++ b/dom/indexedDB/test/test_blob_simple.html
@@ -177,16 +177,15 @@
event = yield;
is(event.data, BLOB_DATA[0][1], "Correct text");
finishTest();
yield;
}
</script>
- <script type="text/javascript;version=1.7" src="file.js"></script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>
--- a/dom/interfaces/devicestorage/nsIDOMDeviceStorageChangeEvent.idl
+++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorageChangeEvent.idl
@@ -3,16 +3,23 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "domstubs.idl"
#include "nsIDOMEvent.idl"
[scriptable, uuid(468304d6-aab6-4e1e-8ab2-569d9e703431), builtinclass]
interface nsIDOMDeviceStorageChangeEvent : nsIDOMEvent
{
+
+ [noscript] void initDeviceStorageChangeEvent(in DOMString eventTypeArg,
+ in boolean canBubbleArg,
+ in boolean cancelableArg,
+ in DOMString path,
+ in DOMString reason);
+
readonly attribute DOMString path;
readonly attribute DOMString reason;
};
dictionary DeviceStorageChangeEventInit : EventInit
{
DOMString path;
DOMString reason;
--- a/dom/interfaces/events/nsIDOMTouchEvent.idl
+++ b/dom/interfaces/events/nsIDOMTouchEvent.idl
@@ -36,18 +36,17 @@ interface nsIDOMTouch : nsISupports {
bool mChanged;
PRUint32 mMessage;
%}
};
[scriptable, uuid(60706eb7-d50d-4379-b01c-e78e6af84213)]
interface nsIDOMTouchList : nsISupports {
readonly attribute unsigned long length;
- [getter,forward(getItemAt)] nsIDOMTouch item(in unsigned long index);
- [noscript,notxpcom,nostdcall] nsIDOMTouch getItemAt(in unsigned long index);
+ nsIDOMTouch item(in unsigned long index);
nsIDOMTouch identifiedTouch(in long identifier);
};
[scriptable, builtinclass, uuid(57809468-7c03-4e8a-b080-4e30f157db21)]
interface nsIDOMTouchEvent : nsIDOMUIEvent {
readonly attribute nsIDOMTouchList touches;
readonly attribute nsIDOMTouchList targetTouches;
readonly attribute nsIDOMTouchList changedTouches;
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -10,16 +10,17 @@
#include "nsIDOMFile.h"
#include "nsIInputStream.h"
#include "nsIRemoteBlob.h"
#include "nsISeekableStream.h"
#include "mozilla/Assertions.h"
#include "mozilla/Monitor.h"
+#include "mozilla/unused.h"
#include "mozilla/net/NeckoMessageUtils.h"
#include "nsDOMFile.h"
#include "nsThreadUtils.h"
#include "ContentChild.h"
#include "ContentParent.h"
using namespace mozilla::dom;
@@ -296,16 +297,26 @@ SetBlobOnParams(BlobChild* aActor, Slice
inline
void
SetBlobOnParams(BlobParent* aActor, SlicedBlobConstructorParams& aParams)
{
aParams.sourceParent() = aActor;
}
+inline
+nsDOMFileBase*
+ToConcreteBlob(nsIDOMBlob* aBlob)
+{
+ // XXX This is only safe so long as all blob implementations in our tree
+ // inherit nsDOMFileBase. If that ever changes then this will need to grow
+ // a real interface or something.
+ return static_cast<nsDOMFileBase*>(aBlob);
+}
+
} // anonymous namespace
namespace mozilla {
namespace dom {
namespace ipc {
template <ActorFlavorEnum ActorFlavor>
class RemoteBlob : public nsDOMFile,
@@ -525,23 +536,33 @@ private:
}
}
};
public:
NS_DECL_ISUPPORTS_INHERITED
RemoteBlob(const nsAString& aName, const nsAString& aContentType,
- PRUint64 aLength)
+ PRUint64 aLength)
: nsDOMFile(aName, aContentType, aLength), mActor(nullptr)
- { }
+ {
+ mImmutable = true;
+ }
RemoteBlob(const nsAString& aContentType, PRUint64 aLength)
: nsDOMFile(aContentType, aLength), mActor(nullptr)
- { }
+ {
+ mImmutable = true;
+ }
+
+ RemoteBlob()
+ : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX), mActor(nullptr)
+ {
+ mImmutable = true;
+ }
virtual ~RemoteBlob()
{
if (mActor) {
mActor->NoteDyingRemoteBlob();
}
}
@@ -589,68 +610,77 @@ public:
};
} // namespace ipc
} // namespace dom
} // namespace mozilla
template <ActorFlavorEnum ActorFlavor>
Blob<ActorFlavor>::Blob(nsIDOMBlob* aBlob)
-: mBlob(aBlob), mRemoteBlob(nullptr), mOwnsBlob(true)
+: mBlob(aBlob), mRemoteBlob(nullptr), mOwnsBlob(true), mBlobIsFile(false)
{
+ MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aBlob);
aBlob->AddRef();
+
+ nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
+ mBlobIsFile = !!file;
}
template <ActorFlavorEnum ActorFlavor>
Blob<ActorFlavor>::Blob(const BlobConstructorParams& aParams)
-: mBlob(nullptr), mRemoteBlob(nullptr), mOwnsBlob(true)
+: mBlob(nullptr), mRemoteBlob(nullptr), mOwnsBlob(false), mBlobIsFile(false)
{
+ MOZ_ASSERT(NS_IsMainThread());
+
nsRefPtr<RemoteBlobType> remoteBlob;
switch (aParams.type()) {
case BlobConstructorParams::TNormalBlobConstructorParams: {
const NormalBlobConstructorParams& params =
aParams.get_NormalBlobConstructorParams();
remoteBlob = new RemoteBlobType(params.contentType(), params.length());
break;
}
case BlobConstructorParams::TFileBlobConstructorParams: {
const FileBlobConstructorParams& params =
aParams.get_FileBlobConstructorParams();
remoteBlob =
new RemoteBlobType(params.name(), params.contentType(),
params.length());
+ mBlobIsFile = true;
+ break;
+ }
+
+ case BlobConstructorParams::TMysteryBlobConstructorParams: {
+ remoteBlob = new RemoteBlobType();
+ mBlobIsFile = true;
break;
}
default:
MOZ_NOT_REACHED("Unknown params!");
}
MOZ_ASSERT(remoteBlob);
- if (NS_FAILED(remoteBlob->SetMutable(false))) {
- MOZ_NOT_REACHED("Failed to make remote blob immutable!");
- }
-
- remoteBlob->SetActor(this);
- remoteBlob.forget(&mRemoteBlob);
-
- mBlob = mRemoteBlob;
+ SetRemoteBlob(remoteBlob);
}
template <ActorFlavorEnum ActorFlavor>
Blob<ActorFlavor>*
Blob<ActorFlavor>::Create(const BlobConstructorParams& aParams)
{
+ MOZ_ASSERT(NS_IsMainThread());
+
switch (aParams.type()) {
case BlobConstructorParams::TNormalBlobConstructorParams:
case BlobConstructorParams::TFileBlobConstructorParams:
+ case BlobConstructorParams::TMysteryBlobConstructorParams:
return new Blob<ActorFlavor>(aParams);
case BlobConstructorParams::TSlicedBlobConstructorParams: {
const SlicedBlobConstructorParams& params =
aParams.get_SlicedBlobConstructorParams();
nsCOMPtr<nsIDOMBlob> source = GetBlobFromParams<ActorFlavor>(params);
MOZ_ASSERT(source);
@@ -670,16 +700,17 @@ Blob<ActorFlavor>::Create(const BlobCons
return nullptr;
}
template <ActorFlavorEnum ActorFlavor>
already_AddRefed<nsIDOMBlob>
Blob<ActorFlavor>::GetBlob()
{
+ MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mBlob);
nsCOMPtr<nsIDOMBlob> blob;
// Remote blobs are held alive until the first call to GetBlob. Thereafter we
// only hold a weak reference. Normal blobs are held alive until the actor is
// destroyed.
if (mRemoteBlob && mOwnsBlob) {
@@ -691,81 +722,185 @@ Blob<ActorFlavor>::GetBlob()
}
MOZ_ASSERT(blob);
return blob.forget();
}
template <ActorFlavorEnum ActorFlavor>
+bool
+Blob<ActorFlavor>::SetMysteryBlobInfo(const nsString& aName,
+ const nsString& aContentType,
+ PRUint64 aLength)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mBlob);
+ MOZ_ASSERT(mRemoteBlob);
+ MOZ_ASSERT(aLength);
+
+ ToConcreteBlob(mBlob)->SetLazyData(aName, aContentType, aLength);
+
+ FileBlobConstructorParams params(aName, aContentType, aLength);
+ return BaseType::SendResolveMystery(params);
+}
+
+template <ActorFlavorEnum ActorFlavor>
+bool
+Blob<ActorFlavor>::SetMysteryBlobInfo(const nsString& aContentType,
+ PRUint64 aLength)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mBlob);
+ MOZ_ASSERT(mRemoteBlob);
+ MOZ_ASSERT(aLength);
+
+ nsString voidString;
+ voidString.SetIsVoid(true);
+
+ ToConcreteBlob(mBlob)->SetLazyData(voidString, aContentType, aLength);
+
+ NormalBlobConstructorParams params(aContentType, aLength);
+ return BaseType::SendResolveMystery(params);
+}
+
+template <ActorFlavorEnum ActorFlavor>
+void
+Blob<ActorFlavor>::SetRemoteBlob(nsRefPtr<RemoteBlobType>& aRemoteBlob)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mBlob);
+ MOZ_ASSERT(!mRemoteBlob);
+ MOZ_ASSERT(!mOwnsBlob);
+ MOZ_ASSERT(aRemoteBlob);
+
+ if (NS_FAILED(aRemoteBlob->SetMutable(false))) {
+ MOZ_NOT_REACHED("Failed to make remote blob immutable!");
+ }
+
+ aRemoteBlob->SetActor(this);
+ aRemoteBlob.forget(&mRemoteBlob);
+
+ mBlob = mRemoteBlob;
+ mOwnsBlob = true;
+}
+
+template <ActorFlavorEnum ActorFlavor>
void
Blob<ActorFlavor>::NoteDyingRemoteBlob()
{
MOZ_ASSERT(mBlob);
MOZ_ASSERT(mRemoteBlob);
MOZ_ASSERT(!mOwnsBlob);
+ // This may be called on any thread due to the fact that RemoteBlob is
+ // designed to be passed between threads. We must start the shutdown process
+ // on the main thread, so we proxy here if necessary.
if (!NS_IsMainThread()) {
nsCOMPtr<nsIRunnable> runnable =
NS_NewNonOwningRunnableMethod(this,
&Blob<ActorFlavor>::NoteDyingRemoteBlob);
if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
- MOZ_NOT_REACHED("Should never fail!");
+ MOZ_ASSERT(false, "Should never fail!");
}
return;
}
// Must do this before calling Send__delete__ or we'll crash there trying to
// access a dangling pointer.
mRemoteBlob = nullptr;
- BaseType::Send__delete__(this);
+ mozilla::unused << BaseType::Send__delete__(this);
}
template <ActorFlavorEnum ActorFlavor>
void
Blob<ActorFlavor>::ActorDestroy(ActorDestroyReason aWhy)
{
+ MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mBlob);
if (mRemoteBlob) {
mRemoteBlob->SetActor(nullptr);
}
if (mOwnsBlob) {
mBlob->Release();
}
}
template <ActorFlavorEnum ActorFlavor>
bool
+Blob<ActorFlavor>::RecvResolveMystery(const ResolveMysteryParams& aParams)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mBlob);
+ MOZ_ASSERT(!mRemoteBlob);
+ MOZ_ASSERT(mOwnsBlob);
+
+ if (!mBlobIsFile) {
+ MOZ_ASSERT(false, "Must always be a file!");
+ return false;
+ }
+
+ nsDOMFileBase* blob = ToConcreteBlob(mBlob);
+
+ switch (aParams.type()) {
+ case ResolveMysteryParams::TNormalBlobConstructorParams: {
+ const NormalBlobConstructorParams& params =
+ aParams.get_NormalBlobConstructorParams();
+ nsString voidString;
+ voidString.SetIsVoid(true);
+ blob->SetLazyData(voidString, params.contentType(), params.length());
+ break;
+ }
+
+ case ResolveMysteryParams::TFileBlobConstructorParams: {
+ const FileBlobConstructorParams& params =
+ aParams.get_FileBlobConstructorParams();
+ blob->SetLazyData(params.name(), params.contentType(), params.length());
+ break;
+ }
+
+ default:
+ MOZ_NOT_REACHED("Unknown params!");
+ }
+
+ return true;
+}
+
+template <ActorFlavorEnum ActorFlavor>
+bool
Blob<ActorFlavor>::RecvPBlobStreamConstructor(StreamType* aActor)
{
+ MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mBlob);
MOZ_ASSERT(!mRemoteBlob);
nsCOMPtr<nsIInputStream> stream;
nsresult rv = mBlob->GetInternalStream(getter_AddRefs(stream));
NS_ENSURE_SUCCESS(rv, false);
return aActor->Send__delete__(aActor, stream.get());
}
template <ActorFlavorEnum ActorFlavor>
typename Blob<ActorFlavor>::StreamType*
Blob<ActorFlavor>::AllocPBlobStream()
{
+ MOZ_ASSERT(NS_IsMainThread());
return new InputStreamActor<ActorFlavor>();
}
template <ActorFlavorEnum ActorFlavor>
bool
Blob<ActorFlavor>::DeallocPBlobStream(StreamType* aActor)
{
+ MOZ_ASSERT(NS_IsMainThread());
delete aActor;
return true;
}
template <ActorFlavorEnum ActorFlavor>
NS_IMPL_ADDREF_INHERITED(RemoteBlob<ActorFlavor>, nsDOMFile)
template <ActorFlavorEnum ActorFlavor>
--- a/dom/ipc/Blob.h
+++ b/dom/ipc/Blob.h
@@ -50,61 +50,82 @@ struct BlobTraits<Child>
};
template <ActorFlavorEnum>
class RemoteBlob;
template <ActorFlavorEnum ActorFlavor>
class Blob : public BlobTraits<ActorFlavor>::BaseType
{
+ friend class RemoteBlob<ActorFlavor>;
+
public:
typedef typename BlobTraits<ActorFlavor>::BaseType BaseType;
typedef typename BlobTraits<ActorFlavor>::StreamType StreamType;
typedef typename BlobTraits<ActorFlavor>::ManagerType ManagerType;
typedef RemoteBlob<ActorFlavor> RemoteBlobType;
typedef mozilla::ipc::IProtocolManager<
mozilla::ipc::RPCChannel::RPCListener>::ActorDestroyReason
ActorDestroyReason;
typedef mozilla::dom::BlobConstructorParams BlobConstructorParams;
protected:
nsIDOMBlob* mBlob;
RemoteBlobType* mRemoteBlob;
bool mOwnsBlob;
+ bool mBlobIsFile;
public:
// This create function is called on the sending side.
static Blob*
Create(nsIDOMBlob* aBlob)
{
return new Blob(aBlob);
}
// This create function is called on the receiving side.
static Blob*
Create(const BlobConstructorParams& aParams);
+ // Get the blob associated with this actor. This may always be called on the
+ // sending side. It may also be called on the receiving side unless this is a
+ // "mystery" blob that has not yet received a SetMysteryBlobInfo() call.
already_AddRefed<nsIDOMBlob>
GetBlob();
- void
- NoteDyingRemoteBlob();
+ // Use this for files.
+ bool
+ SetMysteryBlobInfo(const nsString& aName, const nsString& aContentType,
+ PRUint64 aLength);
+
+ // Use this for non-file blobs.
+ bool
+ SetMysteryBlobInfo(const nsString& aContentType, PRUint64 aLength);
private:
// This constructor is called on the sending side.
Blob(nsIDOMBlob* aBlob);
// This constructor is called on the receiving side.
Blob(const BlobConstructorParams& aParams);
+ void
+ SetRemoteBlob(nsRefPtr<RemoteBlobType>& aRemoteBlob);
+
+ void
+ NoteDyingRemoteBlob();
+
// These methods are only called by the IPDL message machinery.
virtual void
ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
virtual bool
+ RecvResolveMystery(const ResolveMysteryParams& aParams) MOZ_OVERRIDE;
+
+ virtual bool
RecvPBlobStreamConstructor(StreamType* aActor) MOZ_OVERRIDE;
virtual StreamType*
AllocPBlobStream() MOZ_OVERRIDE;
virtual bool
DeallocPBlobStream(StreamType* aActor) MOZ_OVERRIDE;
};
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -80,17 +80,17 @@
#ifdef ACCESSIBILITY
#include "nsIAccessibilityService.h"
#endif
#include "mozilla/dom/indexedDB/PIndexedDBChild.h"
#include "mozilla/dom/sms/SmsChild.h"
#include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
-#include "nsIDOMFile.h"
+#include "nsDOMFile.h"
#include "nsIRemoteBlob.h"
#include "StructuredCloneUtils.h"
using namespace mozilla::docshell;
using namespace mozilla::dom::devicestorage;
using namespace mozilla::dom::sms;
using namespace mozilla::dom::indexedDB;
using namespace mozilla::hal_sandbox;
@@ -428,53 +428,66 @@ ContentChild::DeallocPBlob(PBlobChild* a
{
delete aActor;
return true;
}
BlobChild*
ContentChild::GetOrCreateActorForBlob(nsIDOMBlob* aBlob)
{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aBlob, "Null pointer!");
nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlob);
if (remoteBlob) {
BlobChild* actor =
static_cast<BlobChild*>(static_cast<PBlobChild*>(remoteBlob->GetPBlob()));
NS_ASSERTION(actor, "Null actor?!");
return actor;
}
+ // XXX This is only safe so long as all blob implementations in our tree
+ // inherit nsDOMFileBase. If that ever changes then this will need to grow
+ // a real interface or something.
+ const nsDOMFileBase* blob = static_cast<nsDOMFileBase*>(aBlob);
+
BlobConstructorParams params;
- nsString contentType;
- nsresult rv = aBlob->GetType(contentType);
- NS_ENSURE_SUCCESS(rv, nullptr);
+ if (blob->IsSizeUnknown()) {
+ // We don't want to call GetSize yet since that may stat a file on the main
+ // thread here. Instead we'll learn the size lazily from the other process.
+ params = MysteryBlobConstructorParams();
+ }
+ else {
+ nsString contentType;
+ nsresult rv = aBlob->GetType(contentType);
+ NS_ENSURE_SUCCESS(rv, nullptr);
- PRUint64 length;
- rv = aBlob->GetSize(&length);
- NS_ENSURE_SUCCESS(rv, nullptr);
-
- nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
- if (file) {
- FileBlobConstructorParams fileParams;
-
- rv = file->GetName(fileParams.name());
+ PRUint64 length;
+ rv = aBlob->GetSize(&length);
NS_ENSURE_SUCCESS(rv, nullptr);
- fileParams.contentType() = contentType;
- fileParams.length() = length;
+ nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
+ if (file) {
+ FileBlobConstructorParams fileParams;
+
+ rv = file->GetName(fileParams.name());
+ NS_ENSURE_SUCCESS(rv, nullptr);
- params = fileParams;
- } else {
- NormalBlobConstructorParams blobParams;
- blobParams.contentType() = contentType;
- blobParams.length() = length;
- params = blobParams;
+ fileParams.contentType() = contentType;
+ fileParams.length() = length;
+
+ params = fileParams;
+ } else {
+ NormalBlobConstructorParams blobParams;
+ blobParams.contentType() = contentType;
+ blobParams.length() = length;
+ params = blobParams;
+ }
}
BlobChild* actor = BlobChild::Create(aBlob);
NS_ENSURE_TRUE(actor, nullptr);
if (!SendPBlobConstructor(actor, params)) {
return nullptr;
}
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -37,23 +37,23 @@
#include "nsAppRunner.h"
#include "nsAutoPtr.h"
#include "nsCExternalHandlerService.h"
#include "nsCOMPtr.h"
#include "nsChromeRegistryChrome.h"
#include "nsConsoleMessage.h"
#include "nsDebugImpl.h"
#include "nsDirectoryServiceDefs.h"
+#include "nsDOMFile.h"
#include "nsExternalHelperAppService.h"
#include "nsFrameMessageManager.h"
#include "nsHashPropertyBag.h"
#include "nsIAlertsService.h"
#include "nsIClipboard.h"
#include "nsIConsoleService.h"
-#include "nsIDOMFile.h"
#include "nsIDOMGeoGeolocation.h"
#include "nsIDOMWindow.h"
#include "nsIFilePicker.h"
#include "nsIMemoryReporter.h"
#include "nsIObserverService.h"
#include "nsIPresShell.h"
#include "nsIRemoteBlob.h"
#include "nsIScriptError.h"
@@ -886,54 +886,67 @@ ContentParent::DeallocPBlob(PBlobParent*
{
delete aActor;
return true;
}
BlobParent*
ContentParent::GetOrCreateActorForBlob(nsIDOMBlob* aBlob)
{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aBlob, "Null pointer!");
nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlob);
if (remoteBlob) {
BlobParent* actor =
static_cast<BlobParent*>(
static_cast<PBlobParent*>(remoteBlob->GetPBlob()));
NS_ASSERTION(actor, "Null actor?!");
return actor;
}
+ // XXX This is only safe so long as all blob implementations in our tree
+ // inherit nsDOMFileBase. If that ever changes then this will need to grow
+ // a real interface or something.
+ const nsDOMFileBase* blob = static_cast<nsDOMFileBase*>(aBlob);
+
BlobConstructorParams params;
- nsString contentType;
- nsresult rv = aBlob->GetType(contentType);
- NS_ENSURE_SUCCESS(rv, nullptr);
+ if (blob->IsSizeUnknown()) {
+ // We don't want to call GetSize yet since that may stat a file on the main
+ // thread here. Instead we'll learn the size lazily from the other process.
+ params = MysteryBlobConstructorParams();
+ }
+ else {
+ nsString contentType;
+ nsresult rv = aBlob->GetType(contentType);
+ NS_ENSURE_SUCCESS(rv, nullptr);
- PRUint64 length;
- rv = aBlob->GetSize(&length);
- NS_ENSURE_SUCCESS(rv, nullptr);
-
- nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
- if (file) {
- FileBlobConstructorParams fileParams;
-
- rv = file->GetName(fileParams.name());
+ PRUint64 length;
+ rv = aBlob->GetSize(&length);
NS_ENSURE_SUCCESS(rv, nullptr);
- fileParams.contentType() = contentType;
- fileParams.length() = length;
+ nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
+ if (file) {
+ FileBlobConstructorParams fileParams;
+
+ rv = file->GetName(fileParams.name());
+ NS_ENSURE_SUCCESS(rv, nullptr);
- params = fileParams;
- } else {
- NormalBlobConstructorParams blobParams;
- blobParams.contentType() = contentType;
- blobParams.length() = length;
- params = blobParams;
+ fileParams.contentType() = contentType;
+ fileParams.length() = length;
+
+ params = fileParams;
+ } else {
+ NormalBlobConstructorParams blobParams;
+ blobParams.contentType() = contentType;
+ blobParams.length() = length;
+ params = blobParams;
+ }
}
BlobParent* actor = BlobParent::Create(aBlob);
NS_ENSURE_TRUE(actor, nullptr);
if (!SendPBlobConstructor(actor, params)) {
return nullptr;
}
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -12,10 +12,23 @@ namespace mozilla {
namespace dom {
struct ClonedMessageData
{
SerializedStructuredCloneBuffer data;
PBlob[] blobs;
};
+struct NormalBlobConstructorParams
+{
+ nsString contentType;
+ uint64_t length;
+};
+
+struct FileBlobConstructorParams
+{
+ nsString name;
+ nsString contentType;
+ uint64_t length;
+};
+
} // namespace dom
} // namespace mozilla
--- a/dom/ipc/PBlob.ipdl
+++ b/dom/ipc/PBlob.ipdl
@@ -1,23 +1,33 @@
/* 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 protocol PBlobStream;
include protocol PContent;
+include DOMTypes;
+
namespace mozilla {
namespace dom {
+union ResolveMysteryParams
+{
+ NormalBlobConstructorParams;
+ FileBlobConstructorParams;
+};
+
protocol PBlob
{
manager PContent;
manages PBlobStream;
both:
__delete__();
PBlobStream();
+
+ ResolveMystery(ResolveMysteryParams params);
};
} // namespace dom
} // namespace mozilla
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -96,42 +96,35 @@ struct DeviceStorageEnumerationParams
union DeviceStorageParams
{
DeviceStorageAddParams;
DeviceStorageGetParams;
DeviceStorageDeleteParams;
DeviceStorageEnumerationParams;
};
-struct NormalBlobConstructorParams
-{
- nsString contentType;
- uint64_t length;
-};
-
-struct FileBlobConstructorParams
-{
- nsString name;
- nsString contentType;
- uint64_t length;
-};
-
struct SlicedBlobConstructorParams
{
PBlob source;
uint64_t begin;
uint64_t end;
nsString contentType;
};
+struct MysteryBlobConstructorParams
+{
+ // Nothing is known about this type of blob.
+};
+
union BlobConstructorParams
{
NormalBlobConstructorParams;
FileBlobConstructorParams;
SlicedBlobConstructorParams;
+ MysteryBlobConstructorParams;
};
rpc protocol PContent
{
parent opens PCompositor;
manages PAudio;
manages PBlob;
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -668,24 +668,27 @@ TabChild::RecvUpdateFrame(const nsIntRec
data += nsPrintfCString(", \"resolution\" : %f", aResolution.width);
data += nsPrintfCString(" }");
data += nsPrintfCString(", \"screenSize\" : ");
data += nsPrintfCString("{ \"width\" : %d", aScreenSize.width);
data += nsPrintfCString(", \"height\" : %d", aScreenSize.height);
data += nsPrintfCString(" }");
data += nsPrintfCString(" }");
+ JSAutoRequest ar(mCx);
jsval json = JSVAL_NULL;
StructuredCloneData cloneData;
JSAutoStructuredCloneBuffer buffer;
if (JS_ParseJSON(mCx,
static_cast<const jschar*>(NS_ConvertUTF8toUTF16(data).get()),
data.Length(),
&json)) {
WriteStructuredClone(mCx, json, buffer, cloneData.mClosure);
+ cloneData.mData = buffer.data();
+ cloneData.mDataLength = buffer.nbytes();
}
nsFrameScriptCx cx(static_cast<nsIWebBrowserChrome*>(this), this);
// Let the BrowserElementScrolling helper (if it exists) for this
// content manipulate the frame state.
nsRefPtr<nsFrameMessageManager> mm =
static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get());
mm->ReceiveMessage(static_cast<nsIDOMEventTarget*>(mTabChildGlobal),
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -115,16 +115,24 @@ TabParent::Recv__delete__()
void
TabParent::ActorDestroy(ActorDestroyReason why)
{
if (mIMETabParent == this)
mIMETabParent = nullptr;
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
if (frameLoader) {
frameLoader->DestroyChild();
+
+ if (why == AbnormalShutdown) {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, frameLoader),
+ "oop-frameloader-crashed", nullptr);
+ }
+ }
}
}
bool
TabParent::RecvMoveFocus(const bool& aForward)
{
nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
if (fm) {
--- a/dom/plugins/test/mochitest/Makefile.in
+++ b/dom/plugins/test/mochitest/Makefile.in
@@ -51,19 +51,16 @@ MOCHITEST_FILES = \
test_npn_timers.html \
test_npn_asynccall.html \
test_bug532208.html \
large-pic.jpg \
test_twostreams.html \
test_streamatclose.html \
neverending.sjs \
test_newstreamondestroy.html \
- test_crashing.html \
- $(warning test_crashing2.html disabled due to random orange; see bug 566049) \
- test_hanging.html \
crashing_subpage.html \
test_GCrace.html \
test_propertyAndMethod.html \
test_bug539565-1.html \
test_bug539565-2.html \
test_bug771202.html \
file_bug771202.html \
test_bug777098.html \
@@ -116,16 +113,22 @@ MOCHITEST_CHROME_FILES += \
test_xulbrowser_plugin_visibility.xul \
xulbrowser_plugin_visibility.xul \
plugin_visibility_loader.html \
$(NULL)
endif
endif
ifdef MOZ_CRASHREPORTER
+MOCHITEST_FILES += \
+ test_crashing.html \
+ $(warning test_crashing2.html disabled due to random orange; see bug 566049) \
+ test_hanging.html \
+ $(NULL)
+
MOCHITEST_CHROME_FILES += \
test_crash_notify.xul \
test_crash_notify_no_report.xul \
test_crash_submit.xul \
$(NULL)
ifeq ($(OS_ARCH),WINNT)
MOCHITEST_CHROME_FILES += \
test_idle_hang.xul \
--- a/dom/system/gonk/nsVolumeService.cpp
+++ b/dom/system/gonk/nsVolumeService.cpp
@@ -134,17 +134,17 @@ void nsVolumeService::UpdateVolume(const
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<nsVolume> vol = FindAddVolumeByName(aVolume->Name());
if (vol->Equals(aVolume)) {
// Nothing has really changed. Don't bother telling anybody.
return;
}
vol->Set(aVolume);
- nsRefPtr<nsIObserverService> obs = GetObserverService();
+ nsCOMPtr<nsIObserverService> obs = GetObserverService();
if (!obs) {
return;
}
nsString stateStr(NS_ConvertUTF8toUTF16(vol->StateStr()));
obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
}
/***************************************************************************
--- a/dom/wifi/DOMWifiManager.js
+++ b/dom/wifi/DOMWifiManager.js
@@ -285,17 +285,19 @@ DOMWifiManager.prototype = {
throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
return exposeReadOnly({ status: this._connectionStatus,
network: this._currentNetwork });
},
get connectionInformation() {
if (!this._hasPrivileges)
throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
- return exposeReadOnly(this._lastConnectionInfo);
+ return this._lastConnectionInfo
+ ? exposeReadOnly(this._lastConnectionInfo)
+ : null;
},
set onstatuschange(callback) {
if (!this._hasPrivileges)
throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
this._onStatusChange = callback;
},
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -940,17 +940,17 @@ GLContextEGL::CreateSharedHandle(Texture
}
return (SharedTextureHandle) new SurfaceTextureWrapper(reinterpret_cast<nsSurfaceTexture*>(aBuffer));
#endif
case SharedTextureBufferType::TextureID: {
if (!mShareWithEGLImage)
return 0;
- GLuint texture = (GLuint)aBuffer;
+ GLuint texture = (uintptr_t)aBuffer;
EGLTextureWrapper* tex = new EGLTextureWrapper();
if (!tex->CreateEGLImage(this, texture)) {
NS_ERROR("EGLImage creation for EGLTextureWrapper failed");
delete tex;
return 0;
}
return (SharedTextureHandle)tex;
--- a/gfx/layers/ImageLayers.h
+++ b/gfx/layers/ImageLayers.h
@@ -296,16 +296,33 @@ struct RemoteImageData {
};
/**
* A class that manages Images for an ImageLayer. The only reason
* we need a separate class here is that ImageLayers aren't threadsafe
* (because layers can only be used on the main thread) and we want to
* be able to set the current Image from any thread, to facilitate
* video playback without involving the main thread, for example.
+ *
+ * An ImageContainer can operate in one of three modes:
+ * 1) Normal. Triggered by constructing the ImageContainer with
+ * DISABLE_ASYNC or when compositing is happening on the main thread.
+ * SetCurrentImage changes ImageContainer state but nothing is sent to the
+ * compositor until the next layer transaction.
+ * 2) Asynchronous. Initiated by constructing the ImageContainer with
+ * ENABLE_ASYNC when compositing is happening on the main thread.
+ * SetCurrentImage sends a message through the ImageBridge to the compositor
+ * thread to update the image, without going through the main thread or
+ * a layer transaction.
+ * 3) Remote. Initiated by calling SetRemoteImageData on the ImageContainer
+ * before any other activity.
+ * The ImageContainer uses a shared memory block containing a cross-process mutex
+ * to communicate with the compositor thread. SetCurrentImage synchronously
+ * updates the shared state to point to the new image and the old image
+ * is immediately released (not true in Normal or Asynchronous modes).
*/
class THEBES_API ImageContainer {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
public:
enum { DISABLE_ASYNC = 0x0, ENABLE_ASYNC = 0x01 };
ImageContainer(int flag = 0);
--- a/js/jsd/jsd_text.c
+++ b/js/jsd/jsd_text.c
@@ -439,17 +439,17 @@ jsd_AppendUCSourceText(JSDContext* jsdc,
buf = malloc(UNICODE_TRUNCATE_BUF_SIZE);
if(!buf)
{
JSD_UNLOCK_SOURCE_TEXT(jsdc);
return NULL;
}
}
while(remaining && jsdsrc) {
- int bytes = JS_MIN(remaining, UNICODE_TRUNCATE_BUF_SIZE);
+ int bytes = (remaining < UNICODE_TRUNCATE_BUF_SIZE) ? remaining : UNICODE_TRUNCATE_BUF_SIZE;
int i;
for(i = 0; i < bytes; i++)
buf[i] = (const char) *(text++);
jsdsrc = jsd_AppendSourceText(jsdc,jsdsrc,
buf, bytes,
JSD_SOURCE_PARTIAL);
remaining -= bytes;
}
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -646,19 +646,19 @@ class OrderedHashSet
} // namespace js
/*** HashableValue *******************************************************************************/
bool
HashableValue::setValue(JSContext *cx, const Value &v)
{
- if (v.isString() && v.toString()->isRope()) {
- // Flatten this rope so that equals() is infallible.
- JSString *str = v.toString()->ensureLinear(cx);
+ if (v.isString()) {
+ // Atomize so that hash() and equals() are fast and infallible.
+ JSString *str = js_AtomizeString(cx, v.toString(), DoNotInternAtom);
if (!str)
return false;
value = StringValue(str);
} else if (v.isDouble()) {
double d = v.toDouble();
int32_t i;
if (MOZ_DOUBLE_IS_INT32(d, &i)) {
// Normalize int32-valued doubles to int32 for faster hashing and testing.
@@ -678,38 +678,25 @@ HashableValue::setValue(JSContext *cx, c
return true;
}
HashNumber
HashableValue::hash() const
{
// HashableValue::setValue normalizes values so that the SameValue relation
// on HashableValues is the same as the == relationship on
- // value.data.asBits, except for strings.
- if (value.isString()) {
- JSLinearString &s = value.toString()->asLinear();
- return HashChars(s.chars(), s.length());
- }
-
- // Having dispensed with strings, we can just hash asBits.
- uint64_t u = value.asRawBits();
- return HashNumber((u >> 3) ^
- (u >> (HashNumberSizeBits + 3)) ^
- (u << (HashNumberSizeBits - 3)));
+ // value.data.asBits.
+ return value.asRawBits();
}
bool
HashableValue::equals(const HashableValue &other) const
{
- // Two HashableValues are equal if they have equal bits or they're equal strings.
- bool b = (value.asRawBits() == other.value.asRawBits()) ||
- (value.isString() &&
- other.value.isString() &&
- EqualStrings(&value.toString()->asLinear(),
- &other.value.toString()->asLinear()));
+ // Two HashableValues are equal if they have equal bits.
+ bool b = (value.asRawBits() == other.value.asRawBits());
#ifdef DEBUG
bool same;
JS_ASSERT(SameValue(NULL, value, other.value, &same));
JS_ASSERT(same == b);
#endif
return b;
}
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -6726,17 +6726,17 @@ frontend::NewSrcNote(JSContext *cx, Byte
* Compute delta from the last annotated bytecode's offset. If it's too
* big to fit in sn, allocate one or more xdelta notes and reset sn.
*/
offset = bce->offset();
delta = offset - bce->lastNoteOffset();
bce->current->lastNoteOffset = offset;
if (delta >= SN_DELTA_LIMIT) {
do {
- xdelta = JS_MIN(delta, SN_XDELTA_MASK);
+ xdelta = Min(delta, SN_XDELTA_MASK);
SN_MAKE_XDELTA(sn, xdelta);
delta -= xdelta;
index = AllocSrcNote(cx, bce);
if (index < 0)
return -1;
sn = &bce->notes()[index];
} while (delta >= SN_DELTA_LIMIT);
}
@@ -6959,17 +6959,17 @@ frontend::FinishTakingSrcNotes(JSContext
if (offset < delta)
delta = offset;
for (;;) {
if (!AddToSrcNoteDelta(cx, bce, sn, delta))
return false;
offset -= delta;
if (offset == 0)
break;
- delta = JS_MIN(offset, SN_XDELTA_MASK);
+ delta = Min(offset, SN_XDELTA_MASK);
sn = bce->main.notes;
}
}
}
mainCount = bce->main.noteCount;
totalCount = prologCount + mainCount;
if (prologCount)
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -318,17 +318,17 @@ MapAlignedPages(size_t size, size_t alig
int flags = MAP_PRIVATE | MAP_ANON;
/* Special case: If we want page alignment, no further work is needed. */
if (alignment == PageSize) {
return mmap(NULL, size, prot, flags, -1, 0);
}
/* Overallocate and unmap the region's edges. */
- size_t reqSize = JS_MIN(size + 2 * alignment, 2 * size);
+ size_t reqSize = Min(size + 2 * alignment, 2 * size);
void *region = mmap(NULL, reqSize, prot, flags, -1, 0);
if (region == MAP_FAILED)
return NULL;
uintptr_t regionEnd = uintptr_t(region) + reqSize;
uintptr_t offset = uintptr_t(region) % alignment;
JS_ASSERT(offset < reqSize - size);
--- a/js/src/ion/IonMacroAssembler.cpp
+++ b/js/src/ion/IonMacroAssembler.cpp
@@ -397,17 +397,17 @@ MacroAssembler::initGCThing(const Regist
Address(obj, elementsOffset + ObjectElements::offsetOfInitializedLength()));
store32(Imm32(templateObject->getArrayLength()),
Address(obj, elementsOffset + ObjectElements::offsetOfLength()));
} else {
storePtr(ImmWord(emptyObjectElements), Address(obj, JSObject::offsetOfElements()));
// Fixed slots of non-array objects are required to be initialized.
// Use the values currently in the template object.
- size_t nslots = JS_MIN(templateObject->numFixedSlots(), templateObject->slotSpan());
+ size_t nslots = Min(templateObject->numFixedSlots(), templateObject->slotSpan());
for (unsigned i = 0; i < nslots; i++) {
storeValue(templateObject->getFixedSlot(i),
Address(obj, JSObject::getFixedSlotOffset(i)));
}
}
if (templateObject->hasPrivate()) {
uint32_t nfixed = templateObject->numFixedSlots();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/expression-autopsy.js
@@ -0,0 +1,96 @@
+function check_one(expected, f, err) {
+ var failed = true;
+ try {
+ f();
+ failed = false;
+ } catch (ex) {
+ var s = ex.toString();
+ assertEq(s.slice(0, 11), "TypeError: ");
+ assertEq(s.slice(-err.length), err);
+ assertEq(s.slice(11, -err.length), expected);
+ }
+ if (!failed)
+ throw new Error("didn't fail");
+}
+ieval = eval;
+function check(expr, expected=expr) {
+ var end, err;
+ for ([end, err] of [[".random_prop", " is undefined"], ["()", " is not a function"]]) {
+ var statement = "o = {};" + expr + end, f;
+ var cases = [
+ // Global scope
+ function () {
+ ieval("var o, undef;\n" + statement);
+ },
+ // Function scope
+ Function("o", "undef", statement),
+ // Function scope with variables
+ Function("var o, undef;\n" + statement),
+ // Function scope with some different arugments
+ Function("arg1", "arg2", "var o, undef;\n" + statement),
+ // Deoptimized function scope
+ Function("o", "undef", "with (Object) {}\n" + statement),
+ // Inside with
+ Function("with (Object) { " + statement + " }"),
+ // Closure
+ Function("o", "undef", "function myfunc() { return o + undef; }\n" + statement),
+ // Let definitions in a block
+ Function("{ let o, undef;\n" + statement + "}"),
+ // Let block
+ Function("let (o, undef) { " + statement + " }"),
+ // Let block with some other variables
+ Function("var v1, v2; let (o, undef) { " + statement + " }"),
+ // Shadowed let block
+ Function("o", "undef", "let (o, undef) { " + statement + " }"),
+ // Let in a switch
+ Function("var x = 4; switch (x) { case 4: let o, undef;" + statement + "\ncase 6: break;}"),
+ // Let in for-in
+ Function("var undef, o; for (let z in [1, 2]) { " + statement + " }"),
+ // The more lets the merrier
+ Function("let (x=4, y=5) { x + y; }\nlet (a, b, c) { a + b - c; }\nlet (o, undef) {" + statement + " }"),
+ // Let destructuring
+ Function("o", "undef", "let ([] = 4) {} let (o, undef) { " + statement + " }"),
+ // Try-catch blocks
+ Function("o", "undef", "try { let q = 4; try { let p = 4; } catch (e) {} } catch (e) {} let (o, undef) { " + statement + " }")
+ ];
+ for (var f of cases) {
+ check_one(expected, f, err);
+ }
+ }
+}
+
+check("undef");
+check("o.b");
+check("o.length");
+check("o[true]");
+check("o[false]");
+check("o[null]");
+check("o[0]");
+check("o[1]");
+check("o[3]");
+check("o[256]");
+check("o[65536]");
+check("o[268435455]");
+check("o['1.1']");
+check("o[4 + 'h']", "o['4h']");
+check("this.x");
+check("ieval(undef)", "ieval(...)");
+check("ieval.call()", "ieval.call(...)");
+
+for (let tok of ["|", "^", "&", "==", "!==", "===", "!==", "<", "<=", ">", ">=",
+ ">>", "<<", ">>>", "+", "-", "*", "/", "%"]) {
+ check("o[(undef " + tok + " 4)]");
+}
+
+check("o[!(o)]");
+check("o[~(o)]");
+check("o[+ (o)]");
+check("o[- (o)]");
+
+// A few one off tests
+check_one("6", (function () { 6() }), " is not a function");
+check_one("Array.prototype.reverse.call(...)", (function () { Array.prototype.reverse.call('123'); }), " is read-only");
+check_one("null", function () { var [{ x }] = [null, {}]; }, " has no properties");
+
+// Check fallback behavior
+check_one("undefined", (function () { for (let x of undefined) {} }), " has no properties");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/function-tosource-bug779694.js
@@ -0,0 +1,6 @@
+var x = "";
+for (var i=0; i<400; ++i) {
+ x += String.fromCharCode(i * 289);
+}
+var s = "'" + x + "'";
+assertEq(Function("evt", s).toString(), "function anonymous(evt) {\n" + s + "\n}");
--- a/js/src/jit-test/tests/basic/testBug566556.js
+++ b/js/src/jit-test/tests/basic/testBug566556.js
@@ -1,9 +1,9 @@
var msg = "";
try {
this.__defineSetter__('x', Object.create);
this.watch('x', function() {});
x = 3;
} catch (e) {
msg = e.toString();
}
-assertEq(msg, "TypeError: (void 0) is not an object or null");
+assertEq(msg, "TypeError: undefined is not an object or null");
--- a/js/src/jit-test/tests/basic/testBug579647.js
+++ b/js/src/jit-test/tests/basic/testBug579647.js
@@ -1,13 +1,13 @@
expected = "TypeError: NaN is not a function";
actual = "";
try {
a = ""
for each(x in [0, 0, 0, 0]) {
a %= x
- } ( - a)()
+ } ( let (a=-a) a)()
} catch (e) {
actual = '' + e;
}
assertEq(expected, actual);
--- a/js/src/jit-test/tests/basic/testInitSingletons.js
+++ b/js/src/jit-test/tests/basic/testInitSingletons.js
@@ -23,17 +23,17 @@ foo();
var q = {0x4fffffff: 0, 0x7fffffff: 1, 0xffffffff: 2};
assertEq(q[1342177279], 0);
assertEq(q[2147483647], 1);
assertEq(q[4294967295], 2);
try {
[1,2,3,{a:0,b:1}].foo.bar;
-} catch (e) { assertEq(e.message, "[1, 2, 3, {a:0, b:1}].foo is undefined"); }
+} catch (e) { assertEq(e.message.search("\.foo is undefined") != -1, true); }
var a = [1 + 1, 3 * 2, 6 - 5, 14 % 6, 15 / 5, 1 << 3,
8 >> 2, 5 | 2, 5 ^ 3, ~3, -3,"a" + "b", !true, !false];
assertEq(String(a), "2,6,1,2,3,8,2,7,6,-4,-3,ab,false,true");
assertEq(a.length, 14);
var b = {
a: 1 + 1,
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -545,21 +545,18 @@ JS_ValueToSource(JSContext *cx, jsval v)
CHECK_REQUEST(cx);
assertSameCompartment(cx, v);
return js_ValueToSource(cx, v);
}
JS_PUBLIC_API(JSBool)
JS_ValueToNumber(JSContext *cx, jsval v, double *dp)
{
- AssertHeapIsIdle(cx);
- CHECK_REQUEST(cx);
- assertSameCompartment(cx, v);
-
- return ToNumber(cx, v, dp);
+ RootedValue value(cx, v);
+ return JS::ToNumber(cx, value, dp);
}
JS_PUBLIC_API(JSBool)
JS_DoubleIsInt32(double d, int32_t *ip)
{
return MOZ_DOUBLE_IS_INT32(d, ip);
}
@@ -573,32 +570,25 @@ JS_PUBLIC_API(uint32_t)
JS_DoubleToUint32(double d)
{
return ToUint32(d);
}
JS_PUBLIC_API(JSBool)
JS_ValueToECMAInt32(JSContext *cx, jsval v, int32_t *ip)
{
- AssertHeapIsIdle(cx);
- CHECK_REQUEST(cx);
- assertSameCompartment(cx, v);
-
RootedValue value(cx, v);
- return ToInt32(cx, value, ip);
+ return JS::ToInt32(cx, value, ip);
}
JS_PUBLIC_API(JSBool)
JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32_t *ip)
{
- AssertHeapIsIdle(cx);
- CHECK_REQUEST(cx);
- assertSameCompartment(cx, v);
-
- return ToUint32(cx, v, (uint32_t *)ip);
+ RootedValue value(cx, v);
+ return JS::ToUint32(cx, value, ip);
}
JS_PUBLIC_API(JSBool)
JS_ValueToInt32(JSContext *cx, jsval v, int32_t *ip)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, v);
@@ -623,21 +613,17 @@ JS_ValueToInt32(JSContext *cx, jsval v,
*ip = (int32_t) floor(d + 0.5); /* Round to nearest */
return true;
}
JS_PUBLIC_API(JSBool)
JS_ValueToUint16(JSContext *cx, jsval v, uint16_t *ip)
{
- AssertHeapIsIdle(cx);
- CHECK_REQUEST(cx);
- assertSameCompartment(cx, v);
-
- return ValueToUint16(cx, v, (uint16_t *)ip);
+ return ToUint16(cx, v, ip);
}
JS_PUBLIC_API(JSBool)
JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, v);
@@ -2137,17 +2123,17 @@ SetIdArrayLength(JSContext *cx, JSIdArra
}
static JSIdArray *
AddNameToArray(JSContext *cx, PropertyName *name, JSIdArray *ida, int *ip)
{
int i = *ip;
int length = ida->length;
if (i >= length) {
- ida = SetIdArrayLength(cx, ida, JS_MAX(length * 2, 8));
+ ida = SetIdArrayLength(cx, ida, Max(length * 2, 8));
if (!ida)
return NULL;
JS_ASSERT(i < ida->length);
}
ida->vector[i].init(NameToId(name));
*ip = i + 1;
return ida;
}
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -993,18 +993,21 @@ class JS_PUBLIC_API(AutoCheckRequestDept
#else
# define CHECK_REQUEST(cx) \
((void) 0)
#endif /* JS_THREADSAFE && DEBUG */
#ifdef DEBUG
-/* Assert that we're not doing GC on cx, that we're in a request as
- needed, and that the compartments for cx and v are correct. */
+/*
+ * Assert that we're not doing GC on cx, that we're in a request as
+ * needed, and that the compartments for cx and v are correct.
+ * Also check that GC would be safe at this point.
+ */
JS_PUBLIC_API(void)
AssertArgumentsAreSane(JSContext *cx, const Value &v);
#else
inline void AssertArgumentsAreSane(JSContext *cx, const Value &v) {
/* Do nothing */
}
#endif /* DEBUG */
@@ -2696,20 +2699,23 @@ ToBooleanSlow(const JS::Value &v);
namespace JS {
/* ES5 9.3 ToNumber. */
JS_ALWAYS_INLINE bool
ToNumber(JSContext *cx, const Value &v, double *out)
{
AssertArgumentsAreSane(cx, v);
+ {
+ JS::SkipRoot root(cx, &v);
+ MaybeCheckStackRoots(cx);
+ }
if (v.isNumber()) {
*out = v.toNumber();
- MaybeCheckStackRoots(cx);
return true;
}
return js::ToNumberSlow(cx, v, out);
}
JS_ALWAYS_INLINE bool
ToBoolean(const Value &v)
{
@@ -2746,36 +2752,79 @@ JS_DoubleToUint32(double d);
* Convert a value to a number, then to an int32_t, according to the ECMA rules
* for ToInt32.
*/
extern JS_PUBLIC_API(JSBool)
JS_ValueToECMAInt32(JSContext *cx, jsval v, int32_t *ip);
#ifdef __cplusplus
namespace js {
-/*
- * DO NOT CALL THIS. Use JS::ToInt32
- */
+/* DO NOT CALL THIS. Use JS::ToInt16. */
+extern JS_PUBLIC_API(bool)
+ToUint16Slow(JSContext *cx, const JS::Value &v, uint16_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToInt32. */
extern JS_PUBLIC_API(bool)
ToInt32Slow(JSContext *cx, const JS::Value &v, int32_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToUint32. */
+extern JS_PUBLIC_API(bool)
+ToUint32Slow(JSContext *cx, const JS::Value &v, uint32_t *out);
} /* namespace js */
namespace JS {
JS_ALWAYS_INLINE bool
+ToUint16(JSContext *cx, const js::Value &v, uint16_t *out)
+{
+ AssertArgumentsAreSane(cx, v);
+ {
+ SkipRoot skip(cx, &v);
+ MaybeCheckStackRoots(cx);
+ }
+
+ if (v.isInt32()) {
+ *out = uint16_t(v.toInt32());
+ return true;
+ }
+ return js::ToUint16Slow(cx, v, out);
+}
+
+JS_ALWAYS_INLINE bool
ToInt32(JSContext *cx, const js::Value &v, int32_t *out)
{
AssertArgumentsAreSane(cx, v);
+ {
+ JS::SkipRoot root(cx, &v);
+ MaybeCheckStackRoots(cx);
+ }
+
if (v.isInt32()) {
*out = v.toInt32();
return true;
}
return js::ToInt32Slow(cx, v, out);
}
+JS_ALWAYS_INLINE bool
+ToUint32(JSContext *cx, const js::Value &v, uint32_t *out)
+{
+ AssertArgumentsAreSane(cx, v);
+ {
+ JS::SkipRoot root(cx, &v);
+ MaybeCheckStackRoots(cx);
+ }
+
+ if (v.isInt32()) {
+ *out = uint32_t(v.toInt32());
+ return true;
+ }
+ return js::ToUint32Slow(cx, v, out);
+}
+
} /* namespace JS */
#endif /* __cplusplus */
/*
* Convert a value to a number, then to a uint32_t, according to the ECMA rules
* for ToUint32.
*/
extern JS_PUBLIC_API(JSBool)
@@ -4961,17 +5010,17 @@ JS_CompileUCFunctionForPrincipalsVersion
JSVersion version);
#ifdef __cplusplus
JS_END_EXTERN_C
namespace JS {
/* Options for JavaScript compilation. */
-struct CompileOptions {
+struct JS_PUBLIC_API(CompileOptions) {
JSPrincipals *principals;
JSPrincipals *originPrincipals;
JSVersion version;
bool versionSet;
bool utf8;
const char *filename;
unsigned lineno;
bool compileAndGo;
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2648,27 +2648,27 @@ array_splice(JSContext *cx, unsigned arg
/* Step 5. */
double relativeStart;
if (!ToInteger(cx, argc >= 1 ? args[0] : UndefinedValue(), &relativeStart))
return false;
/* Step 6. */
uint32_t actualStart;
if (relativeStart < 0)
- actualStart = JS_MAX(len + relativeStart, 0);
+ actualStart = Max(len + relativeStart, 0.0);
else
- actualStart = JS_MIN(relativeStart, len);
+ actualStart = Min(relativeStart, double(len));
/* Step 7. */
uint32_t actualDeleteCount;
if (argc != 1) {
double deleteCountDouble;
if (!ToInteger(cx, argc >= 2 ? args[1] : Int32Value(0), &deleteCountDouble))
return false;
- actualDeleteCount = JS_MIN(JS_MAX(deleteCountDouble, 0), len - actualStart);
+ actualDeleteCount = Min(Max(deleteCountDouble, 0.0), double(len - actualStart));
} else {
/*
* Non-standard: if start was specified but deleteCount was omitted,
* delete to the end of the array. See bug 668024 for discussion.
*/
actualDeleteCount = len - actualStart;
}
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -374,17 +374,17 @@ JSStructuredCloneWriter::writeId(jsid id
inline void
JSStructuredCloneWriter::checkStack()
{
#ifdef DEBUG
/* To avoid making serialization O(n^2), limit stack-checking at 10. */
const size_t MAX = 10;
- size_t limit = JS_MIN(counts.length(), MAX);
+ size_t limit = Min(counts.length(), MAX);
JS_ASSERT(objs.length() == counts.length());
size_t total = 0;
for (size_t i = 0; i < limit; i++) {
JS_ASSERT(total + counts[i] >= total);
total += counts[i];
}
if (counts.length() <= MAX)
JS_ASSERT(total == ids.length());
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -541,16 +541,19 @@ struct JSRuntime : js::RuntimeFriendFiel
js::gcstats::Statistics gcStats;
/* Incremented on every GC slice. */
uint64_t gcNumber;
/* The gcNumber at the time of the most recent GC's first slice. */
uint64_t gcStartNumber;
+ /* Whether the currently running GC can finish in multiple slices. */
+ int gcIsIncremental;
+
/* Whether all compartments are being collected in first GC slice. */
bool gcIsFull;
/* The reason that an interrupt-triggered GC should be called. */
js::gcreason::Reason gcTriggerReason;
/*
* If this is true, all marked objects must belong to a compartment being
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -733,17 +733,17 @@ fractional(double *result, const jschar
* Succeed if exactly n digits are converted. Advance *i only
* on success.
*/
static JSBool
ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
{
size_t init = *i;
- if (digits(result, s, i, JS_MIN(limit, init+n)))
+ if (digits(result, s, i, Min(limit, init+n)))
return ((*i - init) == n);
*i = init;
return JS_FALSE;
}
static int
DaysInMonth(int year, int month)
--- a/js/src/jsdhash.cpp
+++ b/js/src/jsdhash.cpp
@@ -270,29 +270,29 @@ JS_DHashTableSetAlphaBounds(JSDHashTable
/*
* Ensure that at least one entry will always be free. If maxAlpha at
* minimum size leaves no entries free, reduce maxAlpha based on minimum
* size and the precision limit of maxAlphaFrac's fixed point format.
*/
JS_ASSERT(JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) >= 1);
if (JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) < 1) {
maxAlpha = (float)
- (JS_DHASH_MIN_SIZE - JS_MAX(JS_DHASH_MIN_SIZE / 256, 1))
+ (JS_DHASH_MIN_SIZE - Max(JS_DHASH_MIN_SIZE / 256, 1))
/ JS_DHASH_MIN_SIZE;
}
/*
* Ensure that minAlpha is strictly less than half maxAlpha. Take care
* not to truncate an entry's worth of alpha when storing in minAlphaFrac
* (8-bit fixed point format).
*/
JS_ASSERT(minAlpha < maxAlpha / 2);
if (minAlpha >= maxAlpha / 2) {
size = JS_DHASH_TABLE_SIZE(table);
- minAlpha = (size * maxAlpha - JS_MAX(size / 256, 1)) / (2 * size);
+ minAlpha = (size * maxAlpha - Max(size / 256, 1U)) / (2 * size);
}
table->maxAlphaFrac = (uint8_t)(maxAlpha * 256);
table->minAlphaFrac = (uint8_t)(minAlpha * 256);
}
/*
* Double hashing needs the second hash code to be relatively prime to table
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -775,16 +775,22 @@ SizeOfJSContext()
JS_FRIEND_API(GCSliceCallback)
SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback)
{
GCSliceCallback old = rt->gcSliceCallback;
rt->gcSliceCallback = callback;
return old;
}
+JS_FRIEND_API(bool)
+WasIncrementalGC(JSRuntime *rt)
+{
+ return rt->gcIsIncremental;
+}
+
jschar *
GCDescription::formatMessage(JSRuntime *rt) const
{
return rt->gcStats.formatMessage();
}
jschar *
GCDescription::formatJSON(JSRuntime *rt, uint64_t timestamp) const
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -740,16 +740,20 @@ struct JS_FRIEND_API(GCDescription) {
};
typedef void
(* GCSliceCallback)(JSRuntime *rt, GCProgress progress, const GCDescription &desc);
extern JS_FRIEND_API(GCSliceCallback)
SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback);
+/* Was the most recent GC run incrementally? */
+extern JS_FRIEND_API(bool)
+WasIncrementalGC(JSRuntime *rt);
+
/*
* Signals a good place to do an incremental slice, because the browser is
* drawing a frame.
*/
extern JS_FRIEND_API(void)
NotifyDidPaint(JSRuntime *rt);
extern JS_FRIEND_API(bool)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3238,28 +3238,28 @@ ShouldPreserveJITCode(JSCompartment *c,
return true;
}
c->lastCodeRelease = currentTime;
return false;
}
static void
-BeginMarkPhase(JSRuntime *rt, bool isIncremental)
+BeginMarkPhase(JSRuntime *rt)
{
int64_t currentTime = PRMJ_Now();
/*
* At the end of each incremental slice, we call prepareForIncrementalGC,
* which marks objects in all arenas that we're currently allocating
* into. This can cause leaks if unreachable objects are in these
* arenas. This purge call ensures that we only mark arenas that have had
* allocations after the incremental GC started.
*/
- if (isIncremental) {
+ if (rt->gcIsIncremental) {
for (GCCompartmentsIter c(rt); !c.done(); c.next())
c->arenas.purge();
}
rt->gcIsFull = true;
for (CompartmentsIter c(rt); !c.done(); c.next()) {
JS_ASSERT(!c->wasGCStarted());
for (unsigned i = 0 ; i < FINALIZE_LIMIT ; ++i)
@@ -3273,17 +3273,17 @@ BeginMarkPhase(JSRuntime *rt, bool isInc
c->setPreservingCode(ShouldPreserveJITCode(c, currentTime));
}
rt->gcMarker.start(rt);
JS_ASSERT(!rt->gcMarker.callback);
JS_ASSERT(IS_GC_MARKING_TRACER(&rt->gcMarker));
/* For non-incremental GC the following sweep discards the jit code. */
- if (isIncremental) {
+ if (rt->gcIsIncremental) {
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_MARK_DISCARD_CODE);
c->discardJitCode(rt->defaultFreeOp());
}
}
GCMarker *gcmarker = &rt->gcMarker;
@@ -3366,27 +3366,27 @@ MarkGrayAndWeak(JSRuntime *rt)
}
#ifdef DEBUG
static void
ValidateIncrementalMarking(JSRuntime *rt);
#endif
static void
-EndMarkPhase(JSRuntime *rt, bool isIncremental)
+EndMarkPhase(JSRuntime *rt)
{
{
gcstats::AutoPhase ap1(rt->gcStats, gcstats::PHASE_MARK);
MarkGrayAndWeak(rt);
}
JS_ASSERT(rt->gcMarker.isDrained());
#ifdef DEBUG
- if (isIncremental)
+ if (rt->gcIsIncremental)
ValidateIncrementalMarking(rt);
#endif
/*
* Having black->gray edges violates our promise to the cycle
* collector. This can happen if we're collecting a compartment and it has
* an edge to an uncollected compartment: it's possible that the source and
* destination of the cross-compartment edge should be gray, but the source
@@ -3937,42 +3937,46 @@ IncrementalCollectSlice(JSRuntime *rt,
AutoCopyFreeListToArenas copy(rt);
AutoGCSlice slice(rt);
gc::State initialState = rt->gcIncrementalState;
SliceBudget sliceBudget(budget);
int zeal = 0;
#ifdef JS_GC_ZEAL
- if (reason == gcreason::DEBUG_GC) {
+ if (reason == gcreason::DEBUG_GC && budget != SliceBudget::Unlimited) {
/*
- * Do the collection type specified by zeal mode only if the collection
- * was triggered by RunDebugGC().
+ * Do the incremental collection type specified by zeal mode if the
+ * collection was triggered by RunDebugGC() and incremental GC has not
+ * been cancelled by ResetIncrementalGC.
*/
zeal = rt->gcZeal();
- JS_ASSERT_IF(zeal == ZealIncrementalMarkAllThenFinish ||
- zeal == ZealIncrementalRootsThenFinish,
- budget == SliceBudget::Unlimited);
}
#endif
- bool isIncremental = rt->gcIncrementalState != NO_INCREMENTAL ||
- budget != SliceBudget::Unlimited ||
- zeal == ZealIncrementalRootsThenFinish ||
- zeal == ZealIncrementalMarkAllThenFinish;
+ rt->gcIsIncremental = rt->gcIncrementalState != NO_INCREMENTAL ||
+ budget != SliceBudget::Unlimited;
+
+ if (zeal == ZealIncrementalRootsThenFinish || zeal == ZealIncrementalMarkAllThenFinish) {
+ /*
+ * Yields between slices occurs at predetermined points in these
+ * modes. sliceBudget is not used.
+ */
+ sliceBudget.reset();
+ }
if (rt->gcIncrementalState == NO_INCREMENTAL) {
rt->gcIncrementalState = MARK_ROOTS;
rt->gcLastMarkSlice = false;
}
switch (rt->gcIncrementalState) {
case MARK_ROOTS:
- BeginMarkPhase(rt, isIncremental);
+ BeginMarkPhase(rt);
PushZealSelectedObjects(rt);
rt->gcIncrementalState = MARK;
if (zeal == ZealIncrementalRootsThenFinish)
break;
/* fall through */
@@ -3997,17 +4001,17 @@ IncrementalCollectSlice(JSRuntime *rt,
* Yield with the aim of starting the sweep in the next
* slice. We will need to mark anything new on the stack
* when we resume, so we stay in MARK state.
*/
rt->gcLastMarkSlice = true;
break;
}
- EndMarkPhase(rt, isIncremental);
+ EndMarkPhase(rt);
rt->gcIncrementalState = SWEEP;
/*
* This runs to completion, but we don't continue if the budget is
* now exhasted.
*/
BeginSweepPhase(rt);
@@ -4562,17 +4566,18 @@ RunDebugGC(JSContext *cx)
* completion.
*/
if (initialState == NO_INCREMENTAL)
rt->gcIncrementalLimit = rt->gcZealFrequency / 2;
else
rt->gcIncrementalLimit *= 2;
budget = SliceBudget::WorkBudget(rt->gcIncrementalLimit);
} else {
- budget = SliceBudget::Unlimited;
+ // This triggers incremental GC but is actually ignored by IncrementalMarkSlice.
+ budget = SliceBudget::WorkBudget(1);
}
Collect(rt, true, budget, GC_NORMAL, gcreason::DEBUG_GC);
/*
* For multi-slice zeal, reset the slice size when we get to the sweep
* phase.
*/
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2772,17 +2772,17 @@ BEGIN_CASE(JSOP_LOOKUPSWITCH)
end_lookup_switch:
len = GET_JUMP_OFFSET(pc2);
}
END_VARLEN_CASE
}
BEGIN_CASE(JSOP_ACTUALSFILLED)
{
- PUSH_INT32(JS_MAX(regs.fp()->numActualArgs(), GET_UINT16(regs.pc)));
+ PUSH_INT32(Max(regs.fp()->numActualArgs(), GET_UINT16(regs.pc)));
}
END_CASE(JSOP_ACTUALSFILLED)
BEGIN_CASE(JSOP_ARGUMENTS)
JS_ASSERT(!regs.fp()->fun()->hasRest());
if (script->needsArgsObj()) {
ArgumentsObject *obj = ArgumentsObject::createExpected(cx, regs.fp());
if (!obj)
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1410,33 +1410,33 @@ ToInt32Slow(JSContext *cx, const Value &
} else {
if (!ToNumberSlow(cx, v, &d))
return false;
}
*out = ToInt32(d);
return true;
}
-bool
+JS_PUBLIC_API(bool)
ToUint32Slow(JSContext *cx, const Value &v, uint32_t *out)
{
JS_ASSERT(!v.isInt32());
double d;
if (v.isDouble()) {
d = v.toDouble();
} else {
if (!ToNumberSlow(cx, v, &d))
return false;
}
*out = ToUint32(d);
return true;
}
-bool
-ValueToUint16Slow(JSContext *cx, const Value &v, uint16_t *out)
+JS_PUBLIC_API(bool)
+ToUint16Slow(JSContext *cx, const Value &v, uint16_t *out)
{
JS_ASSERT(!v.isInt32());
double d;
if (v.isDouble()) {
d = v.toDouble();
} else if (!ToNumberSlow(cx, v, &d)) {
return false;
}
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -139,63 +139,16 @@ ToNumber(JSContext *cx, Value *vp)
extern bool ToNumberSlow(JSContext *cx, js::Value v, double *dp);
if (!ToNumberSlow(cx, *vp, &d))
return false;
vp->setNumber(d);
return true;
}
-/*
- * Convert a value to a uint32_t, according to the ECMA rules for
- * ToUint32. Return converted value in *out on success, !ok on
- * failure.
- */
-
-JS_ALWAYS_INLINE bool
-ToUint32(JSContext *cx, const js::Value &v, uint32_t *out)
-{
-#ifdef DEBUG
- {
- SkipRoot skip(cx, &v);
- MaybeCheckStackRoots(cx);
- }
-#endif
-
- if (v.isInt32()) {
- *out = (uint32_t)v.toInt32();
- return true;
- }
- extern bool ToUint32Slow(JSContext *cx, const js::Value &v, uint32_t *ip);
- return ToUint32Slow(cx, v, out);
-}
-
-/*
- * Convert a value to a number, then to a uint16_t according to the ECMA rules
- * for ToUint16. Return converted value on success, !ok on failure. v must be a
- * copy of a rooted value.
- */
-JS_ALWAYS_INLINE bool
-ValueToUint16(JSContext *cx, const js::Value &v, uint16_t *out)
-{
-#ifdef DEBUG
- {
- SkipRoot skip(cx, &v);
- MaybeCheckStackRoots(cx);
- }
-#endif
-
- if (v.isInt32()) {
- *out = uint16_t(v.toInt32());
- return true;
- }
- extern bool ValueToUint16Slow(JSContext *cx, const js::Value &v, uint16_t *out);
- return ValueToUint16Slow(cx, v, out);
-}
-
JSBool
num_parseInt(JSContext *cx, unsigned argc, Value *vp);
} /* namespace js */
/*
* Similar to strtod except that it replaces overflows with infinities of the
* correct sign, and underflows with zeros of the correct sign. Guaranteed to
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3612,17 +3612,17 @@ JSObject::growElements(JSContext *cx, un
JS_ASSERT(oldcap <= newcap);
size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
uint32_t nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
? oldcap * 2
: oldcap + (oldcap >> 3);
- uint32_t actualCapacity = JS_MAX(newcap, nextsize);
+ uint32_t actualCapacity = Max(newcap, nextsize);
if (actualCapacity >= CAPACITY_CHUNK)
actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK);
else if (actualCapacity < SLOT_CAPACITY_MIN)
actualCapacity = SLOT_CAPACITY_MIN;
/* Don't let nelements get close to wrapping around uint32_t. */
if (actualCapacity >= NELEMENTS_LIMIT || actualCapacity < oldcap || actualCapacity < newcap) {
JS_ReportOutOfMemory(cx);
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -624,17 +624,17 @@ js_Stringify(JSContext *cx, MutableHandl
* a. Append item to the end of PropertyList.
* 7. Let i be i + 1.
*/
/* Step 4b(ii). */
uint32_t len;
JS_ALWAYS_TRUE(js_GetLengthProperty(cx, replacer, &len));
if (replacer->isDenseArray())
- len = JS_MIN(len, replacer->getDenseArrayCapacity());
+ len = Min(len, replacer->getDenseArrayCapacity());
HashSet<jsid> idSet(cx);
if (!idSet.init(len))
return false;
/* Step 4b(iii). */
uint32_t i = 0;
@@ -697,26 +697,26 @@ js_Stringify(JSContext *cx, MutableHandl
}
StringBuffer gap(cx);
if (space.isNumber()) {
/* Step 6. */
double d;
JS_ALWAYS_TRUE(ToInteger(cx, space, &d));
- d = JS_MIN(10, d);
+ d = Min(10.0, d);
if (d >= 1 && !gap.appendN(' ', uint32_t(d)))
return false;
} else if (space.isString()) {
/* Step 7. */
JSLinearString *str = space.toString()->ensureLinear(cx);
if (!str)
return false;
JS::Anchor<JSString *> anchor(str);
- size_t len = JS_MIN(10, space.toString()->length());
+ size_t len = Min(size_t(10), space.toString()->length());
if (!gap.append(str->chars(), len))
return false;
} else {
/* Step 8. */
JS_ASSERT(gap.empty());
}
/* Step 9. */
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1338,17 +1338,17 @@ CopyDecompiledTextForDecomposedOp(JSPrin
*
* This function cannot raise an exception or error. However, due to a risk of
* potential bugs when modeling the stack, the function returns -1 if it
* detects an inconsistency in the model. Such an inconsistency triggers an
* assert in a debug build.
*/
static int
ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
- jsbytecode **pcstack, jsbytecode **lastDecomposedPC);
+ jsbytecode **pcstack);
#define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
/*
* Decompile a part of expression up to the given pc. The function returns
* NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
* the decompiler fails due to a bug and/or unimplemented feature, or the
* decompiled string on success.
@@ -4704,17 +4704,17 @@ Decompile(SprintStack *ss, jsbytecode *p
* All allocation when decompiling is LIFO, using malloc or,
* more commonly, arena-allocating from cx->tempLifoAlloc
* Therefore after InitSprintStack succeeds, we must release
* to mark before returning.
*/
LifoAllocScope las(&cx->tempLifoAlloc());
outerLocalNames = jp->localNames;
if (!SetPrinterLocalNames(cx, fun->script(), jp))
- return NULL;
+ return NULL;
inner = fun->script();
if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) {
Foreground::delete_(jp->localNames);
jp->localNames = outerLocalNames;
return NULL;
}
ss2.inGenExp = JS_TRUE;
@@ -5496,16 +5496,17 @@ DecompileCode(JSPrinter *jp, JSScript *s
}
}
for (unsigned i = 0; i < initialStackDepth; i++) {
if (!PushStr(&ss, "", JSOP_NOP))
return false;
}
+ /* Call recursive subroutine to do the hard work. */
JSScript *oldscript = jp->script;
BindingVector *oldLocalNames = jp->localNames;
if (!SetPrinterLocalNames(cx, script, jp))
return false;
jp->script = script;
/* Call recursive subroutine to do the hard work. */
bool ok = Decompile(&ss, pc, len) != NULL;
@@ -5746,128 +5747,513 @@ js_DecompileFunction(JSPrinter *jp)
}
if (!jp->pretty && !jp->grouped && (fun->flags & JSFUN_LAMBDA))
js_puts(jp, ")");
return JS_TRUE;
}
+static JSObject *
+GetBlockChainAtPC(JSContext *cx, JSScript *script, jsbytecode *pc)
+{
+ jsbytecode *start = script->main();
+
+ JS_ASSERT(pc >= start && pc < script->code + script->length);
+
+ JSObject *blockChain = NULL;
+ for (jsbytecode *p = start; p < pc; p += GetBytecodeLength(p)) {
+ JSOp op = JSOp(*p);
+
+ switch (op) {
+ case JSOP_ENTERBLOCK:
+ case JSOP_ENTERLET0:
+ case JSOP_ENTERLET1: {
+ JSObject *child = script->getObject(p);
+ JS_ASSERT_IF(blockChain, child->asBlock().stackDepth() >= blockChain->asBlock().stackDepth());
+ blockChain = child;
+ break;
+ }
+ case JSOP_LEAVEBLOCK:
+ case JSOP_LEAVEBLOCKEXPR:
+ case JSOP_LEAVEFORLETIN: {
+ // Some LEAVEBLOCK instructions are due to early exits via
+ // return/break/etc. from block-scoped loops and functions. We
+ // should ignore these instructions, since they don't really signal
+ // the end of the block.
+ jssrcnote *sn = js_GetSrcNote(script, p);
+ if (!(sn && SN_TYPE(sn) == SRC_HIDDEN)) {
+ JS_ASSERT(blockChain);
+ blockChain = blockChain->asStaticBlock().enclosingBlock();
+ JS_ASSERT_IF(blockChain, blockChain->isBlock());
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return blockChain;
+}
+
+class PCStack
+{
+ JSContext *cx;
+ jsbytecode **stack;
+ int depth_;
+
+ public:
+ explicit PCStack(JSContext *cx)
+ : cx(cx),
+ stack(NULL),
+ depth_(0)
+ {}
+ ~PCStack();
+ bool init(JSContext *cx, JSScript *script, jsbytecode *pc);
+ int depth() const { return depth_; }
+ jsbytecode *operator[](int i) const;
+};
+
+PCStack::~PCStack()
+{
+ cx->free_(stack);
+}
+
+bool
+PCStack::init(JSContext *cx, JSScript *script, jsbytecode *pc)
+{
+ stack = static_cast<jsbytecode **>(cx->malloc_(StackDepth(script) * sizeof(*stack)));
+ if (!stack)
+ return false;
+ depth_ = ReconstructPCStack(cx, script, pc, stack);
+ JS_ASSERT(depth_ >= 0);
+ return true;
+}
+
+/* Indexes the pcstack. */
+jsbytecode *
+PCStack::operator[](int i) const
+{
+ if (i < 0) {
+ i += depth_;
+ JS_ASSERT(i >= 0);
+ }
+ JS_ASSERT(i < depth_);
+ return stack[i];
+}
+
+/*
+ * The expression decompiler is invoked by error handling code to produce a
+ * string representation of the erroring expression. As it's only a debugging
+ * tool, it only supports basic expressions. For anything complicated, it simply
+ * puts "(intermediate value)" into the error result.
+ *
+ * Here's the basic algorithm:
+ *
+ * 1. Find the stack location of the value whose expression we wish to
+ * decompile. The error handler can explicitly pass this as an
+ * argument. Otherwise, we search backwards down the stack for the offending
+ * value.
+ *
+ * 2. Call ReconstructPCStack with the current frame's pc. This creates a stack
+ * of pcs parallel to the interpreter stack; given an interpreter stack
+ * location, the corresponding pc stack location contains the opcode that pushed
+ * the value in the interpreter. Now, with the result of step 1, we have the
+ * opcode responsible for pushing the value we want to decompile.
+ *
+ * 3. Pass the opcode to decompilePC. decompilePC is the main decompiler
+ * routine, responsible for a string representation of the expression that
+ * generated a certain stack location. decompilePC looks at one opcode and
+ * returns the JS source equivalent of that opcode.
+ *
+ * 4. Expressions can, of course, contain subexpressions. For example, the
+ * literals "4" and "5" are subexpressions of the addition operator in "4 +
+ * 5". If we need to decompile a subexpression, we call decompilePC (step 2)
+ * recursively on the operands' pcs. The result is a depth-first traversal of
+ * the expression tree.
+ *
+ */
+struct ExpressionDecompiler
+{
+ JSContext *cx;
+ StackFrame *fp;
+ RootedScript script;
+ RootedFunction fun;
+ BindingVector *localNames;
+ Sprinter sprinter;
+
+ ExpressionDecompiler(JSContext *cx, JSScript *script, JSFunction *fun)
+ : cx(cx),
+ script(cx, script),
+ fun(cx, fun),
+ localNames(NULL),
+ sprinter(cx)
+ {}
+ ~ExpressionDecompiler();
+ bool init();
+ bool decompilePC(jsbytecode *pc);
+ JSAtom *getVar(unsigned slot);
+ JSAtom *getArg(unsigned slot);
+ JSAtom *findLetVar(jsbytecode *pc, unsigned depth);
+ JSAtom *loadAtom(jsbytecode *pc);
+ bool quote(JSString *s, uint32_t quote);
+ bool write(const char *s);
+ bool write(JSString *s);
+ bool getOutput(char **out);
+};
+
+bool
+ExpressionDecompiler::decompilePC(jsbytecode *pc)
+{
+ JS_ASSERT(script->code <= pc && pc < script->code + script->length);
+
+ PCStack pcstack(cx);
+ if (!pcstack.init(cx, script, pc))
+ return NULL;
+
+ JSOp op = (JSOp)*pc;
+
+ // None of these stack-writing ops generates novel values.
+ JS_ASSERT(op != JSOP_CASE && op != JSOP_DUP && op != JSOP_DUP2);
+
+ if (const char *token = CodeToken[op]) {
+ // Handle simple cases of binary and unary operators.
+ switch (js_CodeSpec[op].nuses) {
+ case 2: {
+ jssrcnote *sn = js_GetSrcNote(script, pc);
+ if (!sn || SN_TYPE(sn) != SRC_ASSIGNOP)
+ return write("(") &&
+ decompilePC(pcstack[-2]) &&
+ write(" ") &&
+ write(token) &&
+ write(" ") &&
+ decompilePC(pcstack[-1]) &&
+ write(")");
+ break;
+ }
+ case 1:
+ return write(token) &&
+ write("(") &&
+ decompilePC(pcstack[-1]) &&
+ write(")");
+ default:
+ break;
+ }
+ }
+
+ switch (op) {
+ case JSOP_GETGNAME:
+ case JSOP_CALLGNAME:
+ case JSOP_NAME:
+ case JSOP_CALLNAME:
+ return write(loadAtom(pc));
+ case JSOP_GETARG:
+ case JSOP_CALLARG: {
+ unsigned slot = GET_ARGNO(pc);
+ JSAtom *atom = getArg(slot);
+ if (!atom)
+ break; // Destructuring
+ return write(atom);
+ }
+ case JSOP_GETLOCAL:
+ case JSOP_CALLLOCAL: {
+ unsigned i = GET_SLOTNO(pc);
+ JSAtom *atom;
+ if (i >= script->nfixed) {
+ i -= script->nfixed;
+ JS_ASSERT(i < unsigned(pcstack.depth()));
+ atom = findLetVar(pc, i);
+ if (!atom)
+ return decompilePC(pcstack[i]); // Destructing temporary
+ } else {
+ atom = getVar(i);
+ }
+ JS_ASSERT(atom);
+ return write(atom);
+ }
+ case JSOP_CALLALIASEDVAR:
+ case JSOP_GETALIASEDVAR: {
+ JSAtom *atom = ScopeCoordinateName(cx->runtime, script, pc);
+ JS_ASSERT(atom);
+ return write(atom);
+ }
+ case JSOP_LENGTH:
+ case JSOP_GETPROP:
+ case JSOP_CALLPROP: {
+ JSAtom *prop = (op == JSOP_LENGTH) ? cx->runtime->atomState.lengthAtom : loadAtom(pc);
+ if (!decompilePC(pcstack[-1]))
+ return false;
+ if (IsIdentifier(prop))
+ return write(".") &&
+ quote(prop, '\0');
+ else
+ return write("[") &&
+ quote(prop, '\'') &&
+ write("]") >= 0;
+ return true;
+ }
+ case JSOP_GETELEM:
+ case JSOP_CALLELEM:
+ return decompilePC(pcstack[-2]) &&
+ write("[") &&
+ decompilePC(pcstack[-1]) &&
+ write("]");
+ case JSOP_NULL:
+ return write(js_null_str);
+ case JSOP_TRUE:
+ return write(js_true_str);
+ case JSOP_FALSE:
+ return write(js_false_str);
+ case JSOP_ZERO:
+ case JSOP_ONE:
+ case JSOP_INT8:
+ case JSOP_UINT16:
+ case JSOP_UINT24:
+ case JSOP_INT32: {
+ int32_t i;
+ switch (op) {
+ case JSOP_ZERO:
+ i = 0;
+ break;
+ case JSOP_ONE:
+ i = 1;
+ break;
+ case JSOP_INT8:
+ i = GET_INT8(pc);
+ break;
+ case JSOP_UINT16:
+ i = GET_UINT16(pc);
+ break;
+ case JSOP_UINT24:
+ i = GET_UINT24(pc);
+ break;
+ case JSOP_INT32:
+ i = GET_INT32(pc);
+ break;
+ default:
+ JS_NOT_REACHED("wat?");
+ }
+ return sprinter.printf("%d", i) >= 0;
+ }
+ case JSOP_STRING:
+ return quote(loadAtom(pc), '"');
+ case JSOP_UNDEFINED:
+ return write(js_undefined_str);
+ case JSOP_THIS:
+ // |this| could convert to a very long object initialiser, so cite it by
+ // its keyword name.
+ return write(js_this_str);
+ case JSOP_CALL:
+ case JSOP_FUNCALL:
+ return decompilePC(pcstack[-(GET_ARGC(pc) + 2)]) &&
+ write("(...)");
+ default:
+ break;
+ }
+ return write("(intermediate value)");
+}
+
+ExpressionDecompiler::~ExpressionDecompiler()
+{
+ cx->delete_<BindingVector>(localNames);
+}
+
+bool
+ExpressionDecompiler::init()
+{
+ if (!sprinter.init())
+ return false;
+
+ localNames = cx->new_<BindingVector>(cx);
+ if (!localNames)
+ return false;
+ if (!GetOrderedBindings(cx, script->bindings, localNames))
+ return false;
+
+ return true;
+}
+
+bool
+ExpressionDecompiler::write(const char *s)
+{
+ return sprinter.put(s) >= 0;
+}
+
+bool
+ExpressionDecompiler::write(JSString *s)
+{
+ return sprinter.putString(s) >= 0;
+}
+
+bool
+ExpressionDecompiler::quote(JSString *s, uint32_t quote)
+{
+ return QuoteString(&sprinter, s, quote) >= 0;
+}
+
+JSAtom *
+ExpressionDecompiler::loadAtom(jsbytecode *pc)
+{
+ return script->getAtom(GET_UINT32_INDEX(pc));
+}
+
+JSAtom *
+ExpressionDecompiler::findLetVar(jsbytecode *pc, unsigned depth)
+{
+ if (script->hasObjects()) {
+ JSObject *chain = GetBlockChainAtPC(cx, script, pc);
+ if (!chain)
+ return NULL;
+ JS_ASSERT(chain->isBlock());
+ do {
+ BlockObject &block = chain->asBlock();
+ uint32_t blockDepth = block.stackDepth();
+ uint32_t blockCount = block.slotCount();
+ if (uint32_t(depth - blockDepth) < uint32_t(blockCount)) {
+ for (Shape::Range r(block.lastProperty()); !r.empty(); r.popFront()) {
+ const Shape &shape = r.front();
+ if (shape.shortid() == int(depth - blockDepth)) {
+ // !JSID_IS_ATOM(shape.propid()) happens for empty
+ // destructuring variables in lets. They can be safely
+ // ignored.
+ if (JSID_IS_ATOM(shape.propid()))
+ return JSID_TO_ATOM(shape.propid());
+ }
+ }
+ }
+ chain = chain->enclosingScope();
+ } while (chain->isBlock());
+ }
+ return NULL;
+}
+
+JSAtom *
+ExpressionDecompiler::getArg(unsigned slot)
+{
+ JS_ASSERT(fun);
+ JS_ASSERT(slot < script->bindings.count());
+ return (*localNames)[slot].maybeName;
+}
+
+JSAtom *
+ExpressionDecompiler::getVar(unsigned slot)
+{
+ JS_ASSERT(fun);
+ slot += fun->nargs;
+ JS_ASSERT(slot < script->bindings.count());
+ return (*localNames)[slot].maybeName;
+}
+
+bool
+ExpressionDecompiler::getOutput(char **res)
+{
+ ptrdiff_t len = sprinter.stringEnd() - sprinter.stringAt(0);
+ *res = static_cast<char *>(cx->malloc_(len + 1));
+ if (!*res)
+ return false;
+ js_memcpy(*res, sprinter.stringAt(0), len);
+ (*res)[len] = 0;
+ return true;
+}
+
+static bool
+FindStartPC(JSContext *cx, ScriptFrameIter &iter, int spindex, Value v, jsbytecode **valuepc)
+{
+ jsbytecode *current = *valuepc;
+
+ if (spindex == JSDVG_IGNORE_STACK)
+ return true;
+
+ *valuepc = NULL;
+
+ PCStack pcstack(cx);
+ if (!pcstack.init(cx, iter.script(), current))
+ return false;
+
+ if (spindex == JSDVG_SEARCH_STACK) {
+ size_t index = iter.numFrameSlots();
+ Value s;
+
+ // We search from fp->sp to base to find the most recently calculated
+ // value matching v under assumption that it is it that caused
+ // exception.
+ do {
+ if (!index)
+ return true;
+ s = iter.frameSlotValue(--index);
+ } while (s != v);
+ if (index < size_t(pcstack.depth()))
+ *valuepc = pcstack[index];
+ } else {
+ *valuepc = pcstack[spindex];
+ }
+ return true;
+}
+
+static bool
+DecompileExpressionFromStack(JSContext *cx, int spindex, Value v, char **res)
+{
+ JS_ASSERT(spindex < 0 ||
+ spindex == JSDVG_IGNORE_STACK ||
+ spindex == JSDVG_SEARCH_STACK);
+
+ *res = NULL;
+
+ ScriptFrameIter frameIter(cx);
+
+ if (frameIter.done())
+ return true;
+
+ if (!cx->hasfp() || !cx->fp()->isScriptFrame())
+ return true;
+
+ JSScript *script = frameIter.script();
+ jsbytecode *valuepc = frameIter.pc();
+ JSFunction *fun = frameIter.isFunctionFrame()
+ ? frameIter.callee()
+ : NULL;
+
+ JS_ASSERT(script->code <= valuepc && valuepc < script->code + script->length);
+
+ // Give up if in prologue.
+ if (valuepc < script->main())
+ return true;
+
+ if (!FindStartPC(cx, frameIter, spindex, v, &valuepc))
+ return false;
+ if (!valuepc)
+ return true;
+
+ ExpressionDecompiler ea(cx, script, fun);
+ if (!ea.init())
+ return false;
+ if (!ea.decompilePC(valuepc))
+ return false;
+
+ return ea.getOutput(res);
+}
+
char *
js_DecompileValueGenerator(JSContext *cx, int spindex, jsval v,
JSString *fallback)
{
- JSScript *script;
- jsbytecode *pc;
- ScriptFrameIter frameIter(cx);
-
- JS_ASSERT(spindex < 0 ||
- spindex == JSDVG_IGNORE_STACK ||
- spindex == JSDVG_SEARCH_STACK);
-
-#ifdef JS_MORE_DETERMINISTIC
- /*
- * Always execute the fallback code if we need determistic behavior for
- * differential testing. IonMonkey does not use StackFrames and this ensures
- * we get the same error messages.
- */
- goto do_fallback;
-#endif
- if (frameIter.done())
- goto do_fallback;
-
- if (!cx->hasfp() || !cx->fp()->isScriptFrame())
- goto do_fallback;
-
- script = frameIter.script();
- pc = frameIter.pc();
-
- JS_ASSERT(script->code <= pc && pc < script->code + script->length);
-
- if (pc < script->main())
- goto do_fallback;
-
- if (spindex != JSDVG_IGNORE_STACK) {
- jsbytecode **pcstack;
-
- /*
- * Prepare computing pcstack containing pointers to opcodes that
- * populated interpreter's stack with its current content.
- */
- pcstack = (jsbytecode **)
- cx->malloc_(StackDepth(script) * sizeof *pcstack);
- if (!pcstack)
+ {
+ char *result;
+ if (!DecompileExpressionFromStack(cx, spindex, v, &result))
return NULL;
- jsbytecode *lastDecomposedPC = NULL;
- int pcdepth = ReconstructPCStack(cx, script, pc, pcstack, &lastDecomposedPC);
- if (pcdepth < 0)
- goto release_pcstack;
-
- if (spindex != JSDVG_SEARCH_STACK) {
- JS_ASSERT(spindex < 0);
- pcdepth += spindex;
- if (pcdepth < 0)
- goto release_pcstack;
- pc = pcstack[pcdepth];
- } else {
- size_t index = frameIter.numFrameSlots();
- Value s;
-
- /*
- * We search from fp->sp to base to find the most recently
- * calculated value matching v under assumption that it is it
- * that caused exception, see bug 328664.
- */
- do {
- if (!index) {
- pcdepth = -1;
- goto release_pcstack;
- }
- s = frameIter.frameSlotValue(--index);
- } while(s != v);
-
- /*
- * The value may have come from beyond stackBase + pcdepth, meaning
- * that it came from a temporary slot pushed by the interpreter or
- * arguments pushed for an Invoke call. Only update pc if beneath
- * stackBase + pcdepth. If above, the value may or may not be
- * produced by the current pc. Since it takes a fairly contrived
- * combination of calls to produce a situation where this is not
- * what we want, we just use the current pc.
- *
- * If we are in the middle of a decomposed opcode, use the outer
- * 'fat' opcode itself. Any source notes for the operation which
- * are needed during decompilation will be associated with the
- * outer opcode.
- */
- if (index < size_t(pcdepth)) {
- pc = pcstack[index];
- if (lastDecomposedPC) {
- size_t len = GetDecomposeLength(lastDecomposedPC,
- js_CodeSpec[*lastDecomposedPC].length);
- if (unsigned(pc - lastDecomposedPC) < len)
- pc = lastDecomposedPC;
- }
- }
+ if (result) {
+ if (strcmp(result, "(intermediate value)"))
+ return result;
+ cx->free_(result);
}
-
- release_pcstack:
- cx->free_(pcstack);
- if (pcdepth < 0)
- goto do_fallback;
}
-
- {
- char *name = DecompileExpression(cx, script, script->function(), pc);
- if (name != FAILED_EXPRESSION_DECOMPILER)
- return name;
- }
-
- do_fallback:
if (!fallback) {
+ if (v.isUndefined())
+ return JS_strdup(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
fallback = js_ValueToSource(cx, v);
if (!fallback)
return NULL;
}
size_t length = fallback->length();
const jschar *chars = fallback->getChars(cx);
if (!chars)
return NULL;
@@ -5952,17 +6338,17 @@ DecompileExpression(JSContext *cx, JSScr
Foreground::free_(pcstack);
}
} g;
g.pcstack = (jsbytecode **)OffTheBooks::malloc_(StackDepth(script) * sizeof *g.pcstack);
if (!g.pcstack)
return NULL;
- int pcdepth = ReconstructPCStack(cx, script, begin, g.pcstack, NULL);
+ int pcdepth = ReconstructPCStack(cx, script, begin, g.pcstack);
if (pcdepth < 0)
return FAILED_EXPRESSION_DECOMPILER;
g.printer = js_NewPrinter(cx, "js_DecompileValueGenerator", fun, 0, false, false, false);
if (!g.printer)
return NULL;
g.printer->dvgfence = end;
@@ -5971,17 +6357,17 @@ DecompileExpression(JSContext *cx, JSScr
return NULL;
return JS_strdup(cx, g.printer->sprinter.string());
}
unsigned
js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc)
{
- return ReconstructPCStack(cx, script, pc, NULL, NULL);
+ return ReconstructPCStack(cx, script, pc, NULL);
}
#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
static int
SimulateOp(JSContext *cx, JSScript *script, JSOp op, const JSCodeSpec *cs,
jsbytecode *pc, jsbytecode **pcstack, unsigned &pcdepth)
{
@@ -6033,17 +6419,17 @@ SimulateOp(JSContext *cx, JSScript *scri
break;
}
pcdepth += ndefs;
return pcdepth;
}
static int
ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target,
- jsbytecode **pcstack, jsbytecode **lastDecomposedPC)
+ jsbytecode **pcstack)
{
/*
* Walk forward from script->main and compute the stack depth and stack of
* operand-generating opcode PCs in pcstack.
*
* FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
* FIXME: Optimize to use last empty-stack sequence point.
*/
@@ -6051,25 +6437,20 @@ ReconstructPCStack(JSContext *cx, JSScri
LOCAL_ASSERT(script->code <= target && target < script->code + script->length);
jsbytecode *pc = script->code;
unsigned pcdepth = 0;
unsigned hpcdepth = unsigned(-1);
ptrdiff_t oplen;
for (; pc < target; pc += oplen) {
JSOp op = JSOp(*pc);
const JSCodeSpec *cs = &js_CodeSpec[op];
- oplen = cs->length;
- if (oplen < 0)
- oplen = js_GetVariableBytecodeLength(pc);
-
- if (cs->format & JOF_DECOMPOSE) {
- if (lastDecomposedPC)
- *lastDecomposedPC = pc;
+ oplen = GetBytecodeLength(pc);
+
+ if (cs->format & JOF_DECOMPOSE)
continue;
- }
if (op == JSOP_GOTO) {
ptrdiff_t jmpoff = GET_JUMP_OFFSET(pc);
if (0 < jmpoff && pc + jmpoff < target) {
pc += jmpoff;
oplen = 0;
continue;
}
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3027,30 +3027,30 @@ js_String(JSContext *cx, unsigned argc,
JSBool
js::str_fromCharCode(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JS_ASSERT(args.length() <= StackSpace::ARGS_LENGTH_MAX);
if (args.length() == 1) {
uint16_t code;
- if (!ValueToUint16(cx, args[0], &code))
+ if (!ToUint16(cx, args[0], &code))
return JS_FALSE;
if (StaticStrings::hasUnit(code)) {
args.rval().setString(cx->runtime->staticStrings.getUnit(code));
return JS_TRUE;
}
args[0].setInt32(code);
}
jschar *chars = (jschar *) cx->malloc_((args.length() + 1) * sizeof(jschar));
if (!chars)
return JS_FALSE;
for (unsigned i = 0; i < args.length(); i++) {
uint16_t code;
- if (!ValueToUint16(cx, args[i], &code)) {
+ if (!ToUint16(cx, args[i], &code)) {
cx->free_(chars);
return JS_FALSE;
}
chars[i] = (jschar)code;
}
chars[args.length()] = 0;
JSString *str = js_NewString(cx, chars, args.length());
if (!str) {
--- a/js/src/jsstrinlines.h
+++ b/js/src/jsstrinlines.h
@@ -105,17 +105,17 @@ SkipSpace(const jschar *s, const jschar
/*
* Return less than, equal to, or greater than zero depending on whether
* s1 is less than, equal to, or greater than s2.
*/
inline bool
CompareChars(const jschar *s1, size_t l1, const jschar *s2, size_t l2, int32_t *result)
{
- size_t n = JS_MIN(l1, l2);
+ size_t n = Min(l1, l2);
for (size_t i = 0; i < n; i++) {
if (int32_t cmp = s1[i] - s2[i]) {
*result = cmp;
return true;
}
}
*result = (int32_t)(l1 - l2);
--- a/js/src/jstypes.h
+++ b/js/src/jstypes.h
@@ -142,18 +142,16 @@
** JS_ROUNDUP
** JS_MIN
** JS_MAX
** DESCRIPTION:
** Commonly used macros for operations on compatible types.
***********************************************************************/
#define JS_HOWMANY(x,y) (((x)+(y)-1)/(y))
#define JS_ROUNDUP(x,y) (JS_HOWMANY(x,y)*(y))
-#define JS_MIN(x,y) ((x)<(y)?(x):(y))
-#define JS_MAX(x,y) ((x)>(y)?(x):(y))
#include "jscpucfg.h"
/*
* Define JS_64BIT iff we are building in an environment with 64-bit
* addresses.
*/
#ifdef _MSC_VER
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -62,17 +62,21 @@ Compressor::compressMore()
{
uInt left = inplen - (zs.next_in - inp);
bool done = left <= CHUNKSIZE;
if (done)
zs.avail_in = left;
else if (zs.avail_in == 0)
zs.avail_in = CHUNKSIZE;
int ret = deflate(&zs, done ? Z_FINISH : Z_NO_FLUSH);
- if (ret == Z_BUF_ERROR) {
+ if (ret == Z_MEM_ERROR) {
+ zs.avail_out = 0;
+ return false;
+ }
+ if (ret == Z_BUF_ERROR || (done && ret == Z_OK)) {
JS_ASSERT(zs.avail_out == 0);
return false;
}
JS_ASSERT_IF(!done, ret == Z_OK);
JS_ASSERT_IF(done, ret == Z_STREAM_END);
return !done;
}
@@ -168,17 +172,17 @@ ValToBin(unsigned logscale, uint32_t val
if (val <= 1)
return val;
bin = (logscale == 10)
? (unsigned) ceil(log10((double) val))
: (logscale == 2)
? (unsigned) JS_CEILING_LOG2W(val)
: val;
- return JS_MIN(bin, 10);
+ return Min(bin, 10U);
}
void
JS_BasicStatsAccum(JSBasicStats *bs, uint32_t val)
{
unsigned oldscale, newscale, bin;
double mean;
--- a/js/src/methodjit/BaseCompiler.h
+++ b/js/src/methodjit/BaseCompiler.h
@@ -69,18 +69,18 @@ class BaseCompiler : public MacroAssembl
#ifdef JS_CPU_X64
inline bool
VerifyRange(void *start1, size_t size1, void *start2, size_t size2)
{
uintptr_t end1 = uintptr_t(start1) + size1;
uintptr_t end2 = uintptr_t(start2) + size2;
- uintptr_t lowest = JS_MIN(uintptr_t(start1), uintptr_t(start2));
- uintptr_t highest = JS_MAX(end1, end2);
+ uintptr_t lowest = Min(uintptr_t(start1), uintptr_t(start2));
+ uintptr_t highest = Max(end1, end2);
return (highest - lowest < INT_MAX);
}
#endif
// This class wraps JSC::LinkBuffer for Mozilla-specific memory handling.
// Every return |false| guarantees an OOM that has been correctly propagated,
// and should continue to propagate.
--- a/js/src/prmjtime.cpp
+++ b/js/src/prmjtime.cpp
@@ -418,17 +418,17 @@ PRMJ_Now(void)
MUTEX_LOCK(&calibration.data_lock);
highresTime = calibration.offset + PRMJ_USEC_PER_SEC*
(highresTimerValue-calibration.timer_offset)/calibration.freq;
cachedOffset = calibration.offset;
/* On some dual processor/core systems, we might get an earlier time
so we cache the last time that we returned */
- calibration.last = JS_MAX(calibration.last, int64_t(highresTime));
+ calibration.last = js::Max(calibration.last, int64_t(highresTime));
returnedTime = calibration.last;
MUTEX_UNLOCK(&calibration.data_lock);
/* Rather than assume the NT kernel ticks every 15.6ms, ask it */
if (GetSystemTimeAdjustment(&timeAdjustment,
&timeIncrement,
&timeAdjustmentDisabled)) {
if (timeAdjustmentDisabled) {
@@ -687,17 +687,17 @@ DSTOffsetCache::getDSTOffsetMilliseconds
return oldOffsetMilliseconds;
}
oldOffsetMilliseconds = offsetMilliseconds;
oldRangeStartSeconds = rangeStartSeconds;
oldRangeEndSeconds = rangeEndSeconds;
if (rangeStartSeconds <= localTimeSeconds) {
- int64_t newEndSeconds = JS_MIN(rangeEndSeconds + RANGE_EXPANSION_AMOUNT, MAX_UNIX_TIMET);
+ int64_t newEndSeconds = js::Min(rangeEndSeconds + RANGE_EXPANSION_AMOUNT, MAX_UNIX_TIMET);
if (newEndSeconds >= localTimeSeconds) {
int64_t endOffsetMilliseconds = computeDSTOffsetMilliseconds(newEndSeconds);
if (endOffsetMilliseconds == offsetMilliseconds) {
rangeEndSeconds = newEndSeconds;
return offsetMilliseconds;
}
offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
@@ -710,17 +710,17 @@ DSTOffsetCache::getDSTOffsetMilliseconds
return offsetMilliseconds;
}
offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
rangeStartSeconds = rangeEndSeconds = localTimeSeconds;
return offsetMilliseconds;
}
- int64_t newStartSeconds = JS_MAX(rangeStartSeconds - RANGE_EXPANSION_AMOUNT, 0);
+ int64_t newStartSeconds = js::Max(rangeStartSeconds - RANGE_EXPANSION_AMOUNT, int64_t(0));
if (newStartSeconds <= localTimeSeconds) {
int64_t startOffsetMilliseconds = computeDSTOffsetMilliseconds(newStartSeconds);
if (startOffsetMilliseconds == offsetMilliseconds) {
rangeStartSeconds = newStartSeconds;
return offsetMilliseconds;
}
offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
--- a/js/src/shell/jsoptparse.cpp
+++ b/js/src/shell/jsoptparse.cpp
@@ -173,17 +173,17 @@ OptionParser::printHelp(const char *prog
if (!arguments.empty()) {
printf("Arguments:\n");
static const char fmt[] = " %s ";
size_t fmtChars = sizeof(fmt) - 2;
size_t lhsLen = 0;
for (Option **it = arguments.begin(), **end = arguments.end(); it != end; ++it)
- lhsLen = JS_MAX(lhsLen, strlen((*it)->longflag) + fmtChars);
+ lhsLen = Max(lhsLen, strlen((*it)->longflag) + fmtChars);
for (Option **it = arguments.begin(), **end = arguments.end(); it != end; ++it) {
Option *arg = *it;
size_t chars = printf(fmt, arg->longflag);
for (; chars < lhsLen; ++chars)
putchar(' ');
PrintParagraph(arg->help, lhsLen, helpWidth, false);
putchar('\n');
@@ -201,17 +201,17 @@ OptionParser::printHelp(const char *prog
size_t longflagLen = strlen(opt->longflag);
size_t fmtLen;
OptionFlagsToFormatInfo(opt->shortflag, opt->isValued(), &fmtLen);
size_t len = fmtLen + longflagLen;
if (opt->isValued())
len += strlen(opt->asValued()->metavar);
- lhsLen = JS_MAX(lhsLen, len);
+ lhsLen = Max(lhsLen, len);
}
/* Print option help text. */
for (Option **it = options.begin(), **end = options.end(); it != end; ++it) {
Option *opt = *it;
size_t fmtLen;
const char *fmt = OptionFlagsToFormatInfo(opt->shortflag, opt->isValued(), &fmtLen);
size_t chars;
--- a/js/src/tests/e4x/Regress/regress-355478.js
+++ b/js/src/tests/e4x/Regress/regress-355478.js
@@ -9,17 +9,17 @@
var BUGNUMBER = 355478;
var summary = 'Do not crash with hasOwnProperty';
var actual = '';
var expect = '';
printBugNumber(BUGNUMBER);
START(summary);
-expect = 'TypeError: <x/>.hasOwnProperty is not a constructor';
+expect = 'TypeError: (intermediate value).hasOwnProperty is not a constructor';
actual = '';
try
{
new <x/>.hasOwnProperty("y");
}
catch(ex)
{
--- a/js/src/tests/ecma_3/Object/8.6.1-01.js
+++ b/js/src/tests/ecma_3/Object/8.6.1-01.js
@@ -10,17 +10,17 @@ var summary = 'In strict mode, setting a
printBugNumber(BUGNUMBER);
printStatus (summary);
enterFunc (String (BUGNUMBER));
// should throw an error in strict mode
var actual = '';
-var expect = 's.length is read-only';
+var expect = '"length" is read-only';
var status = summary + ': Throw if STRICT and WERROR is enabled';
if (!options().match(/strict/))
{
options('strict');
}
if (!options().match(/werror/))
{
--- a/js/src/tests/js1_5/Regress/regress-372364.js
+++ b/js/src/tests/js1_5/Regress/regress-372364.js
@@ -16,28 +16,28 @@ test();
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
print('See Also bug 365891');
- expect = /TypeError: a\(1\) (has no properties|is null)/;
+ expect = /TypeError: a\(.+\) (has no properties|is null)/;
try
{
function a(){return null;} a(1)[0];
}
catch(ex)
{
actual = ex + '';
}
reportMatch(expect, actual, summary);
- expect = /TypeError: \/a\/\.exec\("b"\) (has no properties|is null)/;
+ expect = /TypeError: \(intermediate value\).exec\(.+\) (has no properties|is null)/;
try
{
/a/.exec("b")[0];
}
catch(ex)
{
actual = ex + '';
}
--- a/js/src/tests/js1_5/extensions/regress-50447-1.js
+++ b/js/src/tests/js1_5/extensions/regress-50447-1.js
@@ -94,24 +94,19 @@ function test2()
{
/* generate an error with only msg property */
enterFunc ("test2");
/* note this test incorporates the path to the
test file and assumes the path to the test case
is a subdirectory of the directory containing jsDriver.pl
*/
- var expectedLine = 114;
+ var expectedLine = 109;
var expectedFileName = 'js1_5/extensions/regress-50447-1.js';
- if (typeof document == "undefined")
- {
- expectedFileName = './' + expectedFileName;
- }
- else
- {
+ if (typeof document != "undefined") {
expectedFileName = document.location.href.
replace(/[^\/]*(\?.*)$/, '') +
expectedFileName;
}
var e = new InternalError ("msg");
reportCompare ("(new InternalError(\"msg\", \"" +
expectedFileName + "\", " + expectedLine + "))",
normalize(e.toSource()),
@@ -132,22 +127,17 @@ function test3()
/* note this test incorporates the path to the
test file and assumes the path to the test case
is a subdirectory of the directory containing jsDriver.pl
*/
enterFunc ("test3");
var expectedFileName = 'js1_5/extensions/regress-50447-1.js';
- if (typeof document == "undefined")
- {
- expectedFileName = './' + expectedFileName;
- }
- else
- {
+ if (typeof document != "undefined") {
expectedFileName = document.location.href.
replace(/[^\/]*(\?.*)$/, '') +
expectedFileName;
}
var e = new InternalError ("msg");
e.lineNumber = 10;
reportCompare ("(new InternalError(\"msg\", \"" +
@@ -163,17 +153,17 @@ function test3()
}
function test4()
{
/* generate an error with only msg and filename properties */
enterFunc ("test4");
- var expectedLine = 173;
+ var expectedLine = 163;
var e = new InternalError ("msg", "file");
reportCompare ("(new InternalError(\"msg\", \"file\", " + expectedLine + "))",
e.toSource(),
"toSource() returned unexpected result.");
reportCompare ("file", e.fileName,
"fileName property returned unexpected value.");
reportCompare (expectedLine, e.lineNumber,
--- a/js/src/tests/js1_6/Array/regress-304828.js
+++ b/js/src/tests/js1_6/Array/regress-304828.js
@@ -24,30 +24,30 @@ try
catch(e)
{
actual = e + '';
}
reportCompare(expect, actual, summary + ': join');
// reverse
value = '123';
-expect = 'TypeError: Array.prototype.reverse.call(value) is read-only';
+expect = 'TypeError: Array.prototype.reverse.call(...) is read-only';
try
{
actual = Array.prototype.reverse.call(value) + '';
}
catch(e)
{
actual = e + '';
}
reportCompare(expect, actual, summary + ': reverse');
// sort
value = 'cba';
-expect = 'TypeError: Array.prototype.sort.call(value) is read-only';
+expect = 'TypeError: Array.prototype.sort.call(...) is read-only';
try
{
actual = Array.prototype.sort.call(value) + '';
}
catch(e)
{
actual = e + '';
}
@@ -64,59 +64,59 @@ catch(e)
{
actual = e + '';
}
reportCompare(expect, actual, summary + ': push');
reportCompare('abc', value, summary + ': push');
// pop
value = 'abc';
-expect = "TypeError: property Array.prototype.pop.call(value) is non-configurable and can't be deleted";
+expect = "TypeError: property Array.prototype.pop.call(...) is non-configurable and can't be deleted";
try
{
actual = Array.prototype.pop.call(value);
}
catch(e)
{
actual = e + '';
}
reportCompare(expect, actual, summary + ': pop');
reportCompare('abc', value, summary + ': pop');
// unshift
value = 'def';
-expect = 'TypeError: Array.prototype.unshift.call(value, "a", "b", "c") is read-only';
+expect = 'TypeError: Array.prototype.unshift.call(...) is read-only';
try
{
actual = Array.prototype.unshift.call(value, 'a', 'b', 'c');
}
catch(e)
{
actual = e + '';
}
reportCompare(expect, actual, summary + ': unshift');
reportCompare('def', value, summary + ': unshift');
// shift
value = 'abc';
-expect = 'TypeError: Array.prototype.shift.call(value) is read-only';
+expect = 'TypeError: Array.prototype.shift.call(...) is read-only';
try
{
actual = Array.prototype.shift.call(value);
}
catch(e)
{
actual = e + '';
}
reportCompare(expect, actual, summary + ': shift');
reportCompare('abc', value, summary + ': shift');
// splice
value = 'abc';
-expect = 'TypeError: Array.prototype.splice.call(value, 1, 1) is read-only';
+expect = 'TypeError: Array.prototype.splice.call(...) is read-only';
try
{
actual = Array.prototype.splice.call(value, 1, 1) + '';
}
catch(e)
{
actual = e + '';
}
--- a/js/src/tests/js1_6/Regress/regress-350417.js
+++ b/js/src/tests/js1_6/Regress/regress-350417.js
@@ -15,17 +15,17 @@ test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
- expect = 'TypeError: y.a = [2 for each (p in [])] is not a function';
+ expect = 'TypeError: [] is not a function';
try
{
eval('y = {}; (y.a = [2 for each (p in [])])();');
}
catch(ex)
{
actual = ex + '';
}
--- a/js/src/tests/js1_7/block/regress-352616.js
+++ b/js/src/tests/js1_7/block/regress-352616.js
@@ -14,43 +14,43 @@ test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
- expect = /TypeError: (\(let \(b = 1\) 2\).c is not a function|Cannot find function c.)/;
+ expect = /TypeError: (.+\.c is not a function|Cannot find function c.)/;
actual = 'No Error';
try
{
for(a in (let (b=1) 2).c(3)) { };
}
catch(ex)
{
actual = ex + '';
}
reportMatch(expect, actual, summary + ': 1');
- expect = /TypeError: (\(let \(b = 1, d = 2\) 2\).c is not a function|Cannot find function c.)/;
+ expect = /TypeError: (.+\.c is not a function|Cannot find function c.)/;
actual = 'No Error';
try
{
for(a in (let (b=1,d=2) 2).c(3)) { };
}
catch(ex)
{
actual = ex + '';
}
reportMatch(expect, actual, summary + ': 2');
- expect = /TypeError: (\(let \(b = 1, d = 2\) 2\).c is not a function|Cannot find function c.)/;
+ expect = /TypeError: (.+\.c is not a function|Cannot find function c.)/;
actual = 'No Error';
try
{
for(a in (let (b=1,d=2) 2).c(3)) { };
}
catch(ex)
{
actual = ex + '';
--- a/js/src/tests/js1_7/extensions/regress-355052-01.js
+++ b/js/src/tests/js1_7/extensions/regress-355052-01.js
@@ -15,17 +15,17 @@ test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
- expect = /TypeError: NaN is not a function/;
+ expect = /TypeError: .+ is not a function/;
actual = 'No Error';
try
{
( {valueOf: gc} - [function(){}].__iterator__ )();
}
catch(ex)
{
actual = ex + '';
--- a/js/src/tests/js1_7/extensions/regress-355052-02.js
+++ b/js/src/tests/js1_7/extensions/regress-355052-02.js
@@ -15,17 +15,17 @@ test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
- expect = /TypeError: NaN is not a function/;
+ expect = /TypeError: .+ is not a function/;
actual = 'No Error';
try
{
( {valueOf: gc} - [].a )();
}
catch(ex)
{
actual = ex + '';
--- a/js/src/tests/js1_7/extensions/regress-355052-03.js
+++ b/js/src/tests/js1_7/extensions/regress-355052-03.js
@@ -15,17 +15,17 @@ test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
- expect = /TypeError: NaN is not a function/;
+ expect = /TypeError: .+ is not a function/;
actual = 'No Error';
try
{
var obj = {valueOf: gc };
function f() {
( obj * [].a )();
}
--- a/js/src/tests/js1_7/regress/regress-352870-02.js
+++ b/js/src/tests/js1_7/regress/regress-352870-02.js
@@ -16,17 +16,17 @@ test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
- expect = /TypeError: \[1, 2, 3, 4\].g (has no properties|is undefined)/;
+ expect = /TypeError: .+\.g (has no properties|is undefined)/;
actual = '';
try
{
switch(4) { case [(let (y = [].j(5)) ({}))
for (p in ([1,2,3,4].g).v({},((w).y(z, <x/>))))]: } }
catch(ex)
{
actual = ex + '';
--- a/js/src/tests/js1_8/extensions/regress-469625.js
+++ b/js/src/tests/js1_8/extensions/regress-469625.js
@@ -17,17 +17,17 @@ test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
- expect = 'TypeError: [].__proto__ is not a function';
+ expect = 'TypeError: (intermediate value).__proto__ is not a function';
jit(true);
Array.prototype.__proto__ = function () 3;
try
{
[].__proto__();
--- a/js/src/tests/js1_8_1/regress/regress-420399.js
+++ b/js/src/tests/js1_8_1/regress/regress-420399.js
@@ -15,22 +15,22 @@ test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
- expect = /TypeError: let \(a = undefined\) a (is undefined|has no properties)/;
+ expect = "TypeError: undefined has no properties";
try
{
(let (a=undefined) a).b = 3;
}
catch(ex)
{
actual = ex + '';
}
- reportMatch(expect, actual, summary);
+ reportCompare(expect, actual, summary);
exitFunc ('test');
}
--- a/js/src/tests/js1_8_5/regress/regress-469758.js
+++ b/js/src/tests/js1_8_5/regress/regress-469758.js
@@ -4,11 +4,11 @@
var err;
try {
{let i=1}
{let j=1; [][j][2]}
} catch (e) {
err = e;
}
assertEq(err instanceof TypeError, true);
-assertEq(err.message, "[][j] is undefined");
+assertEq(err.message, "(intermediate value)[j] is undefined");
reportCompare(0, 0, 'ok');
--- a/js/src/tests/lib/tests.py
+++ b/js/src/tests/lib/tests.py
@@ -81,18 +81,17 @@ class Test(object):
def get_command(self, js_cmd_prefix):
dirname, filename = os.path.split(self.path)
cmd = js_cmd_prefix
if self.allowXml:
cmd = cmd + [ '-e', 'options("allow_xml")' ]
cmd = cmd + Test.prefix_command(dirname)
if self.debugMode:
cmd += [ '-d' ]
- # There is a test that requires the path to start with './'.
- cmd += [ '-f', './' + self.path ]
+ cmd += [ '-f', self.path ]
return cmd
def run(self, js_cmd_prefix, timeout=30.0):
cmd = self.get_command(js_cmd_prefix)
out, err, rc, dt, timed_out = run_cmd(cmd, timeout)
return TestOutput(self, cmd, out, err, rc, dt, timed_out)
class TestCase(Test):
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -4097,23 +4097,23 @@ ApplyOrCall(JSContext *cx, unsigned argc
if (!args[1].isObject()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS,
js_apply_str);
return false;
}
RootedObject argsobj(cx, &args[1].toObject());
if (!js_GetLengthProperty(cx, argsobj, &callArgc))
return false;
- callArgc = unsigned(JS_MIN(callArgc, StackSpace::ARGS_LENGTH_MAX));
+ callArgc = unsigned(Min(callArgc, StackSpace::ARGS_LENGTH_MAX));
if (!argv.growBy(callArgc) || !GetElements(cx, argsobj, callArgc, argv.begin()))
return false;
callArgv = argv.begin();
}
} else {
- callArgc = argc > 0 ? unsigned(JS_MIN(argc - 1, StackSpace::ARGS_LENGTH_MAX)) : 0;
+ callArgc = argc > 0 ? unsigned(Min(argc - 1, StackSpace::ARGS_LENGTH_MAX)) : 0;
callArgv = args.array() + 1;
}
for (unsigned i = 0; i < callArgc; i++) {
if (!dbg->unwrapDebuggeeValue(cx, &callArgv[i]))
return false;
}
/*
--- a/js/src/yarr/wtfbridge.h
+++ b/js/src/yarr/wtfbridge.h
@@ -272,24 +272,24 @@ namespace std {
# undef min
# undef max
#endif
template<typename T>
inline T
min(T t1, T t2)
{
- return JS_MIN(t1, t2);
+ return js::Min(t1, t2);
}
template<typename T>
inline T
max(T t1, T t2)
{
- return JS_MAX(t1, t2);
+ return js::Max(t1, t2);
}
template<typename T>
inline void
swap(T &t1, T &t2)
{
T tmp = t1;
t1 = t2;
--- a/js/xpconnect/src/XPCInlines.h
+++ b/js/xpconnect/src/XPCInlines.h
@@ -477,17 +477,17 @@ XPCNativeSet::HasInterfaceWithAncestor(c
return false;
}
inline JSBool
XPCNativeSet::MatchesSetUpToInterface(const XPCNativeSet* other,
XPCNativeInterface* iface) const
{
- int count = JS_MIN((int)mInterfaceCount, (int)other->mInterfaceCount);
+ int count = js::Min(int(mInterfaceCount), int(other->mInterfaceCount));
XPCNativeInterface* const * pp1 = mInterfaces;
XPCNativeInterface* const * pp2 = other->mInterfaces;
for (int i = (int) count; i > 0; i--, pp1++, pp2++) {
XPCNativeInterface* cur = (*pp1);
if (cur != (*pp2))
return false;
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -17,16 +17,17 @@
#include "nsIMemoryReporter.h"
#include "nsPIDOMWindow.h"
#include "nsPrintfCString.h"
#include "mozilla/FunctionTimer.h"
#include "prsystem.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
+#include "nsLayoutStatics.h"
#include "nsContentUtils.h"
#include "nsCCUncollectableMarker.h"
#include "jsfriendapi.h"
#include "js/MemoryMetrics.h"
#include "mozilla/dom/DOMJSClass.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/Element.h"
#include "mozilla/Attributes.h"
@@ -559,16 +560,113 @@ DoDeferredRelease(nsTArray<T> &array)
break;
}
T wrapper = array[count-1];
array.RemoveElementAt(count-1);
NS_RELEASE(wrapper);
}
}
+class XPCIncrementalReleaseRunnable : public nsRunnable
+{
+ XPCJSRuntime *runtime;
+ nsTArray<nsISupports *> items;
+
+ static const PRTime SliceMillis = 10; /* ms */
+
+ public:
+ XPCIncrementalReleaseRunnable(XPCJSRuntime *rt, nsTArray<nsISupports *> &items);
+ virtual ~XPCIncrementalReleaseRunnable();
+
+ void ReleaseNow(bool limited);
+
+ NS_DECL_NSIRUNNABLE
+};
+
+XPCIncrementalReleaseRunnable::XPCIncrementalReleaseRunnable(XPCJSRuntime *rt,
+ nsTArray<nsISupports *> &items)
+ : runtime(rt)
+{
+ nsLayoutStatics::AddRef();
+ this->items.SwapElements(items);
+}
+
+XPCIncrementalReleaseRunnable::~XPCIncrementalReleaseRunnable()
+{
+ MOZ_ASSERT(this != runtime->mReleaseRunnable);
+ nsLayoutStatics::Release();
+}
+
+void
+XPCIncrementalReleaseRunnable::ReleaseNow(bool limited)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
+ TimeStamp started = TimeStamp::Now();
+ PRUint32 counter = 0;
+ while (1) {
+ PRUint32 count = items.Length();
+ if (!count)
+ break;
+
+ nsISupports *wrapper = items[count - 1];
+ items.RemoveElementAt(count - 1);
+ NS_RELEASE(wrapper);
+
+ if (limited) {
+ /* We don't want to read the clock too often. */
+ counter++;
+ if (counter == 100) {
+ counter = 0;
+ if (TimeStamp::Now() - started >= sliceTime)
+ break;
+ }
+ }
+ }
+
+ MOZ_ASSERT_IF(items.Length(), limited);
+
+ if (!items.Length()) {
+ MOZ_ASSERT(runtime->mReleaseRunnable == this);
+ runtime->mReleaseRunnable = nullptr;
+ }
+}
+
+NS_IMETHODIMP
+XPCIncrementalReleaseRunnable::Run()
+{
+ if (runtime->mReleaseRunnable != this) {
+ /* These items were already processed synchronously in JSGC_BEGIN. */
+ MOZ_ASSERT(!items.Length());
+ return NS_OK;
+ }
+
+ ReleaseNow(true);
+
+ if (items.Length()) {
+ nsresult rv = NS_DispatchToMainThread(this);
+ if (NS_FAILED(rv))
+ ReleaseNow(false);
+ }
+
+ return NS_OK;
+}
+
+void
+XPCJSRuntime::ReleaseIncrementally(nsTArray<nsISupports *> &array)
+{
+ MOZ_ASSERT(!mReleaseRunnable);
+ mReleaseRunnable = new XPCIncrementalReleaseRunnable(this, array);
+
+ nsresult rv = NS_DispatchToMainThread(mReleaseRunnable);
+ if (NS_FAILED(rv))
+ mReleaseRunnable->ReleaseNow(false);
+}
+
/* static */ void
XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
{
XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
if (!self)
return;
switch (status) {
@@ -580,25 +678,31 @@ XPCJSRuntime::GCCallback(JSRuntime *rt,
while (JSContext *acx = JS_ContextIterator(rt, &iter)) {
if (!js::HasUnrootedGlobal(acx))
JS_ToggleOptions(acx, JSOPTION_UNROOTED_GLOBAL);
}
break;
}
case JSGC_END:
{
+ /*
+ * If the previous GC created a runnable to release objects
+ * incrementally, and if it hasn't finished yet, finish it now. We
+ * don't want these to build up. We also don't want to allow any
+ * existing incremental release runnables to run after a
+ * non-incremental GC, since they are often used to detect leaks.
+ */
+ if (self->mReleaseRunnable)
+ self->mReleaseRunnable->ReleaseNow(false);
+
// Do any deferred releases of native objects.
-#ifdef XPC_TRACK_DEFERRED_RELEASES
- printf("XPC - Begin deferred Release of %d nsISupports pointers\n",
- self->mNativesToReleaseArray.Length());
-#endif
- DoDeferredRelease(self->mNativesToReleaseArray);
-#ifdef XPC_TRACK_DEFERRED_RELEASES
- printf("XPC - End deferred Releases\n");
-#endif
+ if (js::WasIncrementalGC(rt))
+ self->ReleaseIncrementally(self->mNativesToReleaseArray);
+ else
+ DoDeferredRelease(self->mNativesToReleaseArray);
self->GetXPConnect()->ClearGCBeforeCC();
break;
}
}
nsTArray<JSGCCallback> callbacks(self->extraGCCallbacks);
for (PRUint32 i = 0; i < callbacks.Length(); ++i)
@@ -959,16 +1063,18 @@ XPCJSRuntime::GetJSCycleCollectionContex
if (!mJSCycleCollectionContext)
return nullptr;
}
return mJSCycleCollectionContext;
}
XPCJSRuntime::~XPCJSRuntime()
{
+ MOZ_ASSERT(!mReleaseRunnable);
+
if (mWatchdogWakeup) {
// If the watchdog thread is running, tell it to terminate waking it
// up if necessary and wait until it signals that it finished. As we
// must release the lock before calling PR_DestroyCondVar, we use an
// extra block here.
{
AutoLockWatchdog lock(this);
if (mWatchdogThread) {
@@ -1995,16 +2101,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect*
mVariantRoots(nullptr),
mWrappedJSRoots(nullptr),
mObjectHolderRoots(nullptr),
mWatchdogLock(nullptr),
mWatchdogWakeup(nullptr),
mWatchdogThread(nullptr),
mWatchdogHibernating(false),
mLastActiveTime(-1),
+ mReleaseRunnable(nullptr),
mExceptionManagerNotAvailable(false)
{
#ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
DEBUG_WrappedNativeHashtable =
JS_NewDHashTable(JS_DHashGetStubOps(), nullptr,
sizeof(JSDHashEntryStub), 128);
#endif
NS_TIME_FUNCTION;
--- a/js/xpconnect/src/dictionary_helper_gen.conf
+++ b/js/xpconnect/src/dictionary_helper_gen.conf
@@ -12,17 +12,16 @@ dictionaries = [
[ 'BlobPropertyBag', 'nsIDOMFile.idl' ],
[ 'MutationObserverInit', 'nsIDOMMutationObserver.idl' ],
[ 'WifiConnectionInfoEventInit', 'nsIWifiEventInits.idl' ],
[ 'WifiStatusChangeEventInit', 'nsIWifiEventInits.idl' ],
[ 'GeoPositionOptions', 'nsIDOMGeoGeolocation.idl' ],
[ 'DOMFileMetadataParameters', 'nsIDOMLockedFile.idl' ],
[ 'XMLHttpRequestParameters', 'nsIXMLHttpRequest.idl' ],
[ 'DeviceStorageEnumerationParameters', 'nsIDOMDeviceStorage.idl' ],
- [ 'DeviceStorageChangeEventInit', 'nsIDOMDeviceStorageChangeEvent.idl' ],
[ 'CameraSize', 'nsIDOMCameraManager.idl' ],
[ 'CameraRegion', 'nsIDOMCameraManager.idl' ],
[ 'CameraPosition', 'nsIDOMCameraManager.idl' ],
[ 'CameraSelector', 'nsIDOMCameraManager.idl' ],
[ 'CameraPictureOptions', 'nsIDOMCameraManager.idl' ]
]
# include file names
--- a/js/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/xpconnect/src/dom_quickstubs.qsconf
@@ -181,16 +181,17 @@ members = [
'nsIDOMUIEvent.which',
'nsIDOMUIEvent.rangeParent',
'nsIDOMUIEvent.rangeOffset',
'nsIDOMUIEvent.pageX',
'nsIDOMUIEvent.pageY',
'nsIDOMUIEvent.isChar',
'nsIDOMTouch.*',
+ 'nsIDOMTouchList.*',
'nsIDOMTouchEvent.*',
# dom/interfaces/geolocation - None.
# dom/interfaces/html
'nsIDOMHTMLAnchorElement.href',
'nsIDOMHTMLAnchorElement.rel',
'nsIDOMHTMLAnchorElement.target',
--- a/js/xpconnect/src/dombindings.conf
+++ b/js/xpconnect/src/dombindings.conf
@@ -36,20 +36,16 @@ list_classes = [
'name': 'ClientRectList',
'nativeClass': 'nsClientRectList'
},
{
'name': 'PaintRequestList',
'nativeClass': 'nsPaintRequestList'
},
{
- 'name': 'TouchList',
- 'nativeClass': 'nsDOMTouchList'
- },
- {
'name': 'FileList',
'nativeClass': 'nsDOMFileList'
},
{
'name': 'SVGLengthList',
'nativeClass': 'mozilla::DOMSVGLengthList'
},
{
@@ -73,19 +69,16 @@ list_classes = [
prefableClasses = {
}
irregularFilenames = {
'nsHTMLOptionCollection': 'nsHTMLSelectElement',
'mozilla::dom::PropertyNodeList': 'HTMLPropertiesCollection',
'nsClientRectList': 'nsClientRect',
'nsPaintRequestList': 'nsPaintRequest',
- 'nsIDOMTouch': 'nsIDOMTouchEvent',
- 'nsIDOMTouchList': 'nsIDOMTouchEvent',
- 'nsDOMTouchList': 'nsDOMTouchEvent',
'nsDOMFileList': 'nsDOMFile',
}
customInheritance = {
'nsIDOMPropertyNodeList': 'nsIDOMNodeList',
'nsIDOMHTMLOptionsCollection': 'nsIDOMHTMLCollection',
'nsIDOMHTMLPropertiesCollection': 'nsIDOMHTMLCollection',
}
--- a/js/xpconnect/src/event_impl_gen.conf.in
+++ b/js/xpconnect/src/event_impl_gen.conf.in
@@ -15,17 +15,18 @@ simple_events = [
'CustomEvent',
'PageTransitionEvent',
'PopStateEvent',
'HashChangeEvent',
'CloseEvent',
'MozContactChangeEvent',
'DeviceOrientationEvent',
'DeviceLightEvent',
- 'MozApplicationEvent'
+ 'MozApplicationEvent',
+ 'DeviceStorageChangeEvent'
]
""" include file names """
special_includes = [
'DictionaryHelpers.h',
'nsContentUtils.h',
'nsIDOMApplicationRegistry.h'
]
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -633,16 +633,17 @@ private:
/***************************************************************************/
// In the current xpconnect system there can only be one XPCJSRuntime.
// So, xpconnect can only be used on one JSRuntime within the process.
// no virtuals. no refcounting.
class XPCJSContextStack;
+class XPCIncrementalReleaseRunnable;
class XPCJSRuntime
{
public:
static XPCJSRuntime* newXPCJSRuntime(nsXPConnect* aXPConnect);
static XPCJSRuntime* Get() { return nsXPConnect::GetXPConnect()->GetRuntime(); }
JSRuntime* GetJSRuntime() const {return mJSRuntime;}
nsXPConnect* GetXPConnect() const {return mXPConnect;}
@@ -861,16 +862,18 @@ private:
XPCJSRuntime(); // no implementation
XPCJSRuntime(nsXPConnect* aXPConnect);
// The caller must be holding the GC lock
void RescheduleWatchdog(XPCContext* ccx);
static void WatchdogMain(void *arg);
+ void ReleaseIncrementally(nsTArray<nsISupports *> &array);
+
static bool gNewDOMBindingsEnabled;
static bool gExperimentalBindingsEnabled;
static const char* mStrings[IDX_TOTAL_COUNT];
jsid mStrIDs[IDX_TOTAL_COUNT];
jsval mStrJSVals[IDX_TOTAL_COUNT];
nsXPConnect* mXPConnect;
@@ -901,22 +904,24 @@ private:
XPCRootSetElem *mObjectHolderRoots;
JSDHashTable mJSHolders;
PRLock *mWatchdogLock;
PRCondVar *mWatchdogWakeup;
PRThread *mWatchdogThread;
nsTArray<JSGCCallback> extraGCCallbacks;
bool mWatchdogHibernating;
PRTime mLastActiveTime; // -1 if active NOW
+ XPCIncrementalReleaseRunnable *mReleaseRunnable;
nsCOMPtr<nsIException> mPendingException;
nsCOMPtr<nsIExceptionManager> mExceptionManager;
bool mExceptionManagerNotAvailable;
friend class AutoLockWatchdog;
+ friend class XPCIncrementalReleaseRunnable;
};
/***************************************************************************/
/***************************************************************************/
// XPCContext is mostly a dumb class to hold JSContext specific data and
// maps that let us find wrappers created for the given JSContext.
// no virtuals
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -15,16 +15,17 @@
#include "nsBidiUtils.h"
#include "nsCSSFrameConstructor.h"
#include "nsContainerFrame.h"
#include "nsInlineFrame.h"
#include "nsPlaceholderFrame.h"
#include "nsFirstLetterFrame.h"
#include "nsUnicodeProperties.h"
#include "nsTextFrame.h"
+#include "nsStyleStructInlines.h"
#undef NOISY_BIDI
#undef REALLY_NOISY_BIDI
using namespace mozilla;
static const PRUnichar kSpace = 0x0020;
static const PRUnichar kZWSP = 0x200B;
@@ -512,17 +513,17 @@ CreateContinuation(nsIFrame* aFrame,
NS_ASSERTION(parent, "Couldn't get frame parent in nsBidiPresUtils::CreateContinuation");
nsresult rv = NS_OK;
// Have to special case floating first letter frames because the continuation
// doesn't go in the first letter frame. The continuation goes with the rest
// of the text that the first letter frame was made out of.
if (parent->GetType() == nsGkAtoms::letterFrame &&
- parent->GetStyleDisplay()->IsFloating()) {
+ parent->IsFloating()) {
nsFirstLetterFrame* letterFrame = do_QueryFrame(parent);
rv = letterFrame->CreateContinuationForFloatingParent(presContext, aFrame,
aNewFrame, aIsFluid);
return rv;
}
rv = presShell->FrameConstructor()->
CreateContinuingFrame(presContext, aFrame, parent, aNewFrame, aIsFluid);
@@ -1087,17 +1088,17 @@ nsBidiPresUtils::TraverseFrames(nsBlockF
} else {
// other frame type -- see the Unicode Bidi Algorithm:
// "...inline objects (such as graphics) are treated as if they are ...
// U+FFFC"
// <wbr>, however, is treated as U+200B ZERO WIDTH SPACE. See
// http://dev.w3.org/html5/spec/Overview.html#phrasing-content-1
aBpd->AppendUnichar(content->IsHTML(nsGkAtoms::wbr) ?
kZWSP : kObjectSubstitute);
- if (!frame->GetStyleContext()->GetStyleDisplay()->IsInlineOutside()) {
+ if (!frame->IsInlineOutside()) {
// if it is not inline, end the paragraph
ResolveParagraphWithinBlock(aBlockFrame, aBpd);
}
}
} else {
// For a non-leaf frame, recurse into TraverseFrames
nsIFrame* kid = frame->GetFirstPrincipalChild();
nsIFrame* overflowKid = frame->GetFirstChild(nsIFrame::kOverflowList);
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -83,16 +83,17 @@
#include "nsBox.h"
#include "nsTArray.h"
#include "nsGenericDOMDataNode.h"
#include "mozilla/dom/Element.h"
#include "FrameLayerBuilder.h"
#include "nsAutoLayoutPhase.h"
#include "nsCSSRenderingBorders.h"
#include "nsRenderingContext.h"
+#include "nsStyleStructInlines.h"
#ifdef MOZ_XUL
#include "nsIRootBox.h"
#include "nsIDOMXULCommandDispatcher.h"
#include "nsIDOMXULDocument.h"
#include "nsIXULDocument.h"
#endif
#ifdef MOZ_FLEXBOX
@@ -355,22 +356,16 @@ GetFieldSetBlockFrame(nsIFrame* aFieldse
#define FCDATA_DECL(_flags, _func) \
{ _flags, { (FrameCreationFunc)_func }, nullptr, nullptr }
#define FCDATA_WITH_WRAPPING_BLOCK(_flags, _func, _anon_box) \
{ _flags | FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS, \
{ (FrameCreationFunc)_func }, nullptr, &_anon_box }
//----------------------------------------------------------------------
-static bool
-IsInlineOutside(nsIFrame* aFrame)
-{
- return aFrame->GetStyleDisplay()->IsInlineOutside();
-}
-
/**
* True if aFrame is an actual inline frame in the sense of non-replaced
* display:inline CSS boxes. In other words, it can be affected by {ib}
* splitting and can contain first-letter frames. Basically, this is either an
* inline frame (positioned or otherwise) or an line frame (this last because
* it can contain first-letter and because inserting blocks in the middle of it
* needs to terminate it).
*/
@@ -469,17 +464,17 @@ static nsIFrame* GetSpecialPrevSibling(n
static nsIFrame*
GetLastSpecialSibling(nsIFrame* aFrame, bool aReturnEmptyTrailingInline)
{
for (nsIFrame *frame = aFrame, *next; ; frame = next) {
next = GetSpecialSibling(frame);
if (!next ||
(!aReturnEmptyTrailingInline && !next->GetFirstPrincipalChild() &&
!GetSpecialSibling(next))) {
- NS_ASSERTION(!next || !IsInlineOutside(frame),
+ NS_ASSERTION(!next || !frame->IsInlineOutside(),
"Should have a block here!");
return frame;
}
}
NS_NOTREACHED("unreachable code");
return nullptr;
}
@@ -556,31 +551,31 @@ GetIBContainingBlockFor(nsIFrame* aFrame
// After this function returns, aLink is pointing to the first link at or
// after its starting position for which the next frame is a block. If there
// is no such link, it points to the end of the list.
static void
FindFirstBlock(nsFrameList::FrameLinkEnumerator& aLink)
{
for ( ; !aLink.AtEnd(); aLink.Next()) {
- if (!IsInlineOutside(aLink.NextFrame())) {
+ if (!aLink.NextFrame()->IsInlineOutside()) {
return;
}
}
}
// This function returns a frame link enumerator pointing to the first link in
// the list for which the next frame is not block. If there is no such link,
// it points to the end of the list.
static nsFrameList::FrameLinkEnumerator
FindFirstNonBlock(const nsFrameList& aList)
{
nsFrameList::FrameLinkEnumerator link(aList);
for (; !link.AtEnd(); link.Next()) {
- if (IsInlineOutside(link.NextFrame())) {
+ if (link.NextFrame()->IsInlineOutside()) {
break;
}
}
return link;
}
inline void
SetInitialSingleChild(nsIFrame* aParent, nsIFrame* aFrame)
@@ -1063,18 +1058,22 @@ nsFrameConstructorState::GetGeometricPar
// elements. (1) has the usual problems when multiple frames share the same
// content (notice all the special cases in this file dealing with inner
// tables and outer tables which share the same content). (2) requires some
// work and possible factoring.
//
// XXXbz couldn't we just force position to "static" on roots and
// float to "none"? That's OK per CSS 2.1, as far as I can tell.
- if (aStyleDisplay->IsFloating() && mFloatedItems.containingBlock) {
- NS_ASSERTION(!aStyleDisplay->IsAbsolutelyPositioned(),
+ if (aContentParentFrame && aContentParentFrame->IsSVGText()) {
+ return aContentParentFrame;
+ }
+
+ if (aStyleDisplay->IsFloatingStyle() && mFloatedItems.containingBlock) {
+ NS_ASSERTION(!aStyleDisplay->IsAbsolutelyPositionedStyle(),
"Absolutely positioned _and_ floating?");
return mFloatedItems.containingBlock;
}
if (aStyleDisplay->mPosition == NS_STYLE_POSITION_ABSOLUTE &&
mAbsoluteItems.containingBlock) {
return mAbsoluteItems.containingBlock;
}
@@ -1115,17 +1114,17 @@ nsFrameConstructorState::AddChild(nsIFra
"Popup whose parent is not the popup containing block?");
NS_ASSERTION(mPopupItems.containingBlock, "Must have a popup set frame!");
needPlaceholder = true;
frameItems = &mPopupItems;
placeholderType = PLACEHOLDER_FOR_POPUP;
}
else
#endif // MOZ_XUL
- if (aCanBeFloated && disp->IsFloating() &&
+ if (aCanBeFloated && aNewFrame->IsFloating() &&
mFloatedItems.containingBlock) {
NS_ASSERTION(aNewFrame->GetParent() == mFloatedItems.containingBlock,
"Float whose parent is not the float containing block?");
needPlaceholder = true;
frameItems = &mFloatedItems;
placeholderType = PLACEHOLDER_FOR_FLOAT;
}
else if (aCanBePositioned) {
@@ -1924,17 +1923,17 @@ nsCSSFrameConstructor::ConstructTable(ns
nsFrameItems childItems;
// Process children
nsFrameConstructorSaveState absoluteSaveState;
const nsStyleDisplay* display = outerStyleContext->GetStyleDisplay();
// Mark the table frame as an absolute container if needed
- if (display->IsPositioned()) {
+ if (display->IsPositioned(aParentFrame)) {
aState.PushAbsoluteContainingBlock(newFrame, absoluteSaveState);
}
if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
rv = ConstructFramesFromItemList(aState, aItem.mChildItems,
innerFrame, childItems);
} else {
rv = ProcessChildren(aState, content, styleContext, innerFrame,
true, childItems, false, aItem.mPendingBinding);
@@ -2469,18 +2468,18 @@ nsCSSFrameConstructor::ConstructDocEleme
if (!contentFrame)
return NS_ERROR_OUT_OF_MEMORY;
nsFrameItems frameItems;
// Use a null PendingBinding, since our binding is not in fact pending.
rv = ConstructBlock(state, display, aDocElement,
state.GetGeometricParent(display,
mDocElementContainingBlock),
mDocElementContainingBlock, styleContext,
- &contentFrame, frameItems, display->IsPositioned(),
- nullptr);
+ &contentFrame, frameItems,
+ display->IsPositioned(contentFrame), nullptr);
if (NS_FAILED(rv) || frameItems.IsEmpty())
return rv;
*aNewFrame = frameItems.FirstChild();
NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
}
}
// set the primary frame
@@ -2968,19 +2967,19 @@ nsCSSFrameConstructor::ConstructSelectFr
// Notify the listbox that it is being used as a dropdown list.
nsIListControlFrame * listControlFrame = do_QueryFrame(listFrame);
if (listControlFrame) {
listControlFrame->SetComboboxFrame(comboboxFrame);
}
// Notify combobox that it should use the listbox as it's popup
comboBox->SetDropDown(listFrame);
- NS_ASSERTION(!listStyle->GetStyleDisplay()->IsPositioned(),
+ NS_ASSERTION(!listFrame->IsPositioned(),
"Ended up with positioned dropdown list somehow.");
- NS_ASSERTION(!listStyle->GetStyleDisplay()->IsFloating(),
+ NS_ASSERTION(!listFrame->IsFloating(),
"Ended up with floating dropdown list somehow.");
// Initialize the scroll frame positioned. Note that it is NOT
// initialized as absolutely positioned.
nsIFrame* scrolledFrame = NS_NewSelectsAreaFrame(mPresShell, styleContext, flags);
InitializeSelectFrame(aState, listFrame, scrolledFrame, content,
comboboxFrame, listStyle, true,
@@ -3132,17 +3131,17 @@ nsCSSFrameConstructor::ConstructFieldSet
if (NS_FAILED(rv)) {
return rv;
}
// Process children
nsFrameConstructorSaveState absoluteSaveState;
nsFrameItems childItems;
- if (aStyleDisplay->IsPositioned()) {
+ if (newFrame->IsPositioned()) {
aState.PushAbsoluteContainingBlock(newFrame, absoluteSaveState);
}
ProcessChildren(aState, content, styleContext, blockFrame, true,
childItems, true, aItem.mPendingBinding);
nsFrameItems fieldsetKids;
fieldsetKids.AddChild(blockFrame);
@@ -3351,18 +3350,18 @@ nsCSSFrameConstructor::FindHTMLData(Elem
"Unexpected parent for fieldset content anon box");
if (aTag == nsGkAtoms::legend &&
(!aParentFrame ||
(aParentFrame->GetType() != nsGkAtoms::fieldSetFrame &&
aParentFrame->GetStyleContext()->GetPseudo() !=
nsCSSAnonBoxes::fieldsetContent) ||
!aElement->GetParent() ||
!aElement->GetParent()->IsHTML(nsGkAtoms::fieldset) ||
- aStyleContext->GetStyleDisplay()->IsFloating() ||
- aStyleContext->GetStyleDisplay()->IsAbsolutelyPositioned())) {
+ aStyleContext->GetStyleDisplay()->IsFloatingStyle() ||
+ aStyleContext->GetStyleDisplay()->IsAbsolutelyPositionedStyle())) {
// <legend> is only special inside fieldset, check both the frame tree
// parent and content tree parent due to XBL issues. For floated or
// absolutely positioned legends we want to construct by display type and
// not do special legend stuff.
// XXXbz it would be nice if we could just decide this based on the parent
// tag, and hence just use a SIMPLE_TAG_CHAIN for legend below, but the
// fact that with XBL we could end up with this legend element in some
// totally weird insertion point makes that chancy, I think.
@@ -3674,17 +3673,17 @@ nsCSSFrameConstructor::ConstructFrameFro
}
SetInitialSingleChild(newFrame, blockFrame);
// Now figure out whether newFrame or blockFrame should be the
// absolute container. It should be the latter if it's
// positioned, otherwise the former.
const nsStyleDisplay* blockDisplay = blockContext->GetStyleDisplay();
- if (blockDisplay->IsPositioned()) {
+ if (blockDisplay->IsPositioned(blockFrame)) {
maybeAbsoluteContainingBlockDisplay = blockDisplay;
maybeAbsoluteContainingBlock = blockFrame;
}
// Our kids should go into the blockFrame
newFrame = blockFrame;
}
@@ -3709,17 +3708,18 @@ nsCSSFrameConstructor::ConstructFrameFro
// Process the child content if requested
nsFrameItems childItems;
nsFrameConstructorSaveState absoluteSaveState;
if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) {
aState.PushAbsoluteContainingBlock(nullptr, absoluteSaveState);
} else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH) &&
- maybeAbsoluteContainingBlockDisplay->IsPositioned()) {
+ maybeAbsoluteContainingBlockDisplay->IsPositioned
+ (maybeAbsoluteContainingBlock)) {
aState.PushAbsoluteContainingBlock(maybeAbsoluteContainingBlock,
absoluteSaveState);
}
if (bits & FCDATA_USE_CHILD_ITEMS) {
NS_ASSERTION(!ShouldSuppressFloatingOfDescendants(newFrame),
"uh oh -- this frame is supposed to _suppress_ floats, but "
"we're about to push it as a float containing block...");
@@ -4303,25 +4303,26 @@ nsCSSFrameConstructor::BuildScrollFrame(
FinishBuildingScrollFrame(aNewFrame, aScrolledFrame);
return NS_OK;
}
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay,
Element* aElement,
+ nsIFrame* aParentFrame,
nsStyleContext* aStyleContext)
{
PR_STATIC_ASSERT(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)));
// The style system ensures that floated and positioned frames are
// block-level.
- NS_ASSERTION(!(aDisplay->IsFloating() ||
- aDisplay->IsAbsolutelyPositioned()) ||
- aDisplay->IsBlockOutside(),
+ NS_ASSERTION(!(aDisplay->IsFloatingStyle() ||
+ aDisplay->IsAbsolutelyPositionedStyle()) ||
+ aDisplay->IsBlockOutsideStyle(),
"Style system did not apply CSS2.1 section 9.7 fixups");
// If this is "body", try propagating its scroll style to the viewport
// Note that we need to do this even if the body is NOT scrollable;
// it might have dynamically changed from scrollable to not scrollable,
// and that might need to be propagated.
// XXXbz is this the right place to do this? If this code moves,
// make this function static.
@@ -4335,38 +4336,40 @@ nsCSSFrameConstructor::FindDisplayData(c
!mPresShell->GetPresContext()->IsPaginated(),
"Shouldn't propagate scroll in paginated contexts");
// If the frame is a block-level frame and is scrollable, then wrap it in a
// scroll frame.
// XXX Ignore tables for the time being
// XXXbz it would be nice to combine this with the other block
// case... Think about how do do this?
- if (aDisplay->IsBlockInside() &&
+ if ((aParentFrame ? aDisplay->IsBlockInside(aParentFrame) :
+ aDisplay->IsBlockInsideStyle()) &&
aDisplay->IsScrollableOverflow() &&
!propagatedScrollToViewport) {
// Except we don't want to do that for paginated contexts for
// frames that are block-outside and aren't frames for native
// anonymous stuff.
if (mPresShell->GetPresContext()->IsPaginated() &&
- aDisplay->IsBlockOutside() &&
+ aDisplay->IsBlockOutsideStyle() &&
!aElement->IsInNativeAnonymousSubtree()) {
static const FrameConstructionData sForcedNonScrollableBlockData =
FULL_CTOR_FCDATA(FCDATA_FORCED_NON_SCROLLABLE_BLOCK,
&nsCSSFrameConstructor::ConstructNonScrollableBlock);
return &sForcedNonScrollableBlockData;
}
static const FrameConstructionData sScrollableBlockData =
FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructScrollableBlock);
return &sScrollableBlockData;
}
// Handle various non-scrollable blocks
- if (aDisplay->IsBlockInside()) {
+ if ((aParentFrame ? aDisplay->IsBlockInside(aParentFrame) :
+ aDisplay->IsBlockInsideStyle())) {
static const FrameConstructionData sNonScrollableBlockData =
FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructNonScrollableBlock);
return &sNonScrollableBlockData;
}
static const FrameConstructionDataByInt sDisplayData[] = {
// To keep the hash table small don't add inline frames (they're
// typically things like FONT and B), because we can quickly
@@ -4419,18 +4422,20 @@ nsCSSFrameConstructor::FindDisplayData(c
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
&nsCSSFrameConstructor::ConstructTableRow) },
{ NS_STYLE_DISPLAY_TABLE_CELL,
FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
&nsCSSFrameConstructor::ConstructTableCell) }
};
- return FindDataByInt(aDisplay->mDisplay, aElement, aStyleContext,
- sDisplayData, ArrayLength(sDisplayData));
+ return FindDataByInt((aParentFrame ? aDisplay->GetDisplay(aParentFrame) :
+ aDisplay->mDisplay),
+ aElement, aStyleContext, sDisplayData,
+ ArrayLength(sDisplayData));
}
nsresult
nsCSSFrameConstructor::ConstructScrollableBlock(nsFrameConstructorState& aState,
FrameConstructionItem& aItem,
nsIFrame* aParentFrame,
const nsStyleDisplay* aDisplay,
nsFrameItems& aFrameItems,
@@ -4450,17 +4455,18 @@ nsCSSFrameConstructor::ConstructScrollab
// pass a temporary stylecontext, the correct one will be set later
nsIFrame* scrolledFrame =
NS_NewBlockFormattingContext(mPresShell, styleContext);
nsFrameItems blockItem;
nsresult rv = ConstructBlock(aState,
scrolledContentStyle->GetStyleDisplay(), content,
*aNewFrame, *aNewFrame, scrolledContentStyle,
- &scrolledFrame, blockItem, aDisplay->IsPositioned(),
+ &scrolledFrame, blockItem,
+ aDisplay->IsPositioned(scrolledFrame),
aItem.mPendingBinding);
if (NS_UNLIKELY(NS_FAILED(rv))) {
// XXXbz any cleanup needed here?
return rv;
}
NS_ASSERTION(blockItem.FirstChild() == scrolledFrame,
"Scrollframe's frameItems should be exactly the scrolled frame");
@@ -4482,32 +4488,33 @@ nsCSSFrameConstructor::ConstructNonScrol
nsStyleContext* const styleContext = aItem.mStyleContext;
// We want a block formatting context root in paginated contexts for
// every block that would be scrollable in a non-paginated context.
// We mark our blocks with a bit here if this condition is true, so
// we can check it later in nsFrame::ApplyPaginatedOverflowClipping.
bool clipPaginatedOverflow =
(aItem.mFCData->mBits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) != 0;
- if (aDisplay->IsAbsolutelyPositioned() ||
- aDisplay->IsFloating() ||
- NS_STYLE_DISPLAY_INLINE_BLOCK == aDisplay->mDisplay ||
- clipPaginatedOverflow) {
+ if ((aDisplay->IsAbsolutelyPositionedStyle() ||
+ aDisplay->IsFloatingStyle() ||
+ NS_STYLE_DISPLAY_INLINE_BLOCK == aDisplay->mDisplay ||
+ clipPaginatedOverflow) &&
+ !aParentFrame->IsSVGText()) {
*aNewFrame = NS_NewBlockFormattingContext(mPresShell, styleContext);
if (clipPaginatedOverflow) {
(*aNewFrame)->AddStateBits(NS_BLOCK_CLIP_PAGINATED_OVERFLOW);
}
} else {
*aNewFrame = NS_NewBlockFrame(mPresShell, styleContext);
}
return ConstructBlock(aState, aDisplay, aItem.mContent,
aState.GetGeometricParent(aDisplay, aParentFrame),
aParentFrame, styleContext, aNewFrame,
- aFrameItems, aDisplay->IsPositioned(),
+ aFrameItems, aDisplay->IsPositioned(*aNewFrame),
aItem.mPendingBinding);
}
nsresult
nsCSSFrameConstructor::InitAndRestoreFrame(const nsFrameConstructorState& aState,
nsIContent* aContent,
nsIFrame* aParentFrame,
@@ -4654,17 +4661,17 @@ nsCSSFrameConstructor::FindMathMLData(El
if (aNameSpaceID != kNameSpaceID_MathML)
return nullptr;
// Handle <math> specially, because it sometimes produces inlines
if (aTag == nsGkAtoms::math) {
// This needs to match the test in EnsureBlockDisplay in
// nsRuleNode.cpp. Though the behavior here for the display:table
// case is pretty weird...
- if (aStyleContext->GetStyleDisplay()->IsBlockOutside()) {
+ if (aStyleContext->GetStyleDisplay()->IsBlockOutsideStyle()) {
static const FrameConstructionData sBlockMathData =
FCDATA_DECL(FCDATA_FORCE_NULL_ABSPOS_CONTAINER |
FCDATA_WRAP_KIDS_IN_BLOCKS,
NS_CreateNewMathMLmathBlockFrame);
return &sBlockMathData;
}
static const FrameConstructionData sInlineMathData =
@@ -5249,17 +5256,17 @@ nsCSSFrameConstructor::AddFrameConstruct
// Now check for XUL display types
if (!data) {
data = FindXULDisplayData(display, element, styleContext);
}
// And general display types
if (!data) {
- data = FindDisplayData(display, element, styleContext);
+ data = FindDisplayData(display, element, aParentFrame, styleContext);
}
NS_ASSERTION(data, "Should have frame construction data now");
if (data->mBits & FCDATA_SUPPRESS_FRAME) {
SetAsUndisplayedContent(aItems, element, styleContext, isGeneratedContent);
return;
}
@@ -5289,18 +5296,19 @@ nsCSSFrameConstructor::AddFrameConstruct
display->mDisplay != NS_STYLE_DISPLAY_TABLE_COLUMN)) {
SetAsUndisplayedContent(aItems, aContent, styleContext, isGeneratedContent);
return;
}
bool canHavePageBreak =
(aFlags & ITEM_ALLOW_PAGE_BREAK) &&
aState.mPresContext->IsPaginated() &&
- !display->IsAbsolutelyPositioned() &&
- !(bits & FCDATA_IS_TABLE_PART);
+ !display->IsAbsolutelyPositionedStyle() &&
+ !(bits & FCDATA_IS_TABLE_PART) &&
+ !(bits & FCDATA_IS_SVG_TEXT);
if (canHavePageBreak && display->mBreakBefore) {
AddPageBreakItem(aContent, aStyleContext, aItems);
}
FrameConstructionItem* item =
aItems.AppendItem(data, aContent, aTag, aNameSpaceID,
pendingBinding, styleContext.forget(),
@@ -5341,17 +5349,18 @@ nsCSSFrameConstructor::AddFrameConstruct
bool isInline =
// Table-internal things are inline-outside if and only if they're kids of
// inlines, since they'll trigger construction of inline-table
// pseudos.
((bits & FCDATA_IS_TABLE_PART) &&
(!aParentFrame || // No aParentFrame means inline
aParentFrame->GetStyleDisplay()->mDisplay == NS_STYLE_DISPLAY_INLINE)) ||
// Things that are inline-outside but aren't inline frames are inline
- display->IsInlineOutside() ||
+ (aParentFrame ? display->IsInlineOutside(aParentFrame) :
+ display->IsInlineOutsideStyle()) ||
// Popups that are certainly out of flow.
isPopup;
// Set mIsAllInline conservatively. It just might be that even an inline
// that has mIsAllInline false doesn't need an {ib} split. So this is just
// an optimization to keep from doing too much work in cases when we can
// show that mIsAllInline is true..
item->mIsAllInline = item->mHasInlineEnds = isInline ||
@@ -5371,18 +5380,20 @@ nsCSSFrameConstructor::AddFrameConstruct
(!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
aState.GetGeometricParent(display, nullptr));
// Set mIsBlock conservatively. It's OK to set it false for some real
// blocks, but not OK to set it true for things that aren't blocks. Since
// isOutOfFlow might be false even in cases when the frame will end up
// out-of-flow, we can't use it here. But we _can_ say that the frame will
// for sure end up in-flow if it's not floated or absolutely positioned.
- item->mIsBlock =
- !isInline && !display->IsAbsolutelyPositioned() && !display->IsFloating();
+ item->mIsBlock = !isInline &&
+ !display->IsAbsolutelyPositionedStyle() &&
+ !display->IsFloatingStyle() &&
+ !(bits & FCDATA_IS_SVG_TEXT);
}
if (item->mIsAllInline) {
aItems.InlineItemAdded();
} else if (item->mIsBlock) {
aItems.BlockItemAdded();
}
@@ -5591,18 +5602,17 @@ nsCSSFrameConstructor::GetAbsoluteContai
}
// If the frame is positioned, we will probably return it as the containing
// block (see the exceptions below). Otherwise, we'll start looking at the
// parent frame, unless we're dealing with a scrollframe.
// Scrollframes are special since they're not positioned, but their
// scrolledframe might be. So, we need to check this special case to return
// the correct containing block (the scrolledframe) in that case.
- const nsStyleDisplay* disp = frame->GetStyleDisplay();
- if (!disp->IsPositioned()) {
+ if (!frame->IsPositioned()) {
continue;
}
nsIFrame* absPosCBCandidate = nullptr;
if (frame->GetType() == nsGkAtoms::scrollFrame) {
nsIScrollableFrame* scrollFrame = do_QueryFrame(frame);
absPosCBCandidate = scrollFrame->GetScrolledFrame();
} else {
// Only first continuations can be containing blocks.
@@ -5768,17 +5778,17 @@ nsCSSFrameConstructor::AppendFramesToPar
if (!nextSibling && IsFrameSpecial(aParentFrame)) {
// When we get here, our frame list might start with a block. If it does
// so, and aParentFrame is an inline, and it and all its previous
// continuations have no siblings, then put the initial blocks from the
// frame list into the previous block of the {ib} split. Note that we
// didn't want to stop at the block part of the split when figuring out
// initial parent, because that could screw up float parenting; it's easier
// to do this little fixup here instead.
- if (aFrameList.NotEmpty() && !IsInlineOutside(aFrameList.FirstChild())) {
+ if (aFrameList.NotEmpty() && !aFrameList.FirstChild()->IsInlineOutside()) {
// See whether our trailing inline is empty
nsIFrame* firstContinuation = aParentFrame->GetFirstContinuation();
if (firstContinuation->PrincipalChildList().IsEmpty()) {
// Our trailing inline is empty. Collect our starting blocks from
// aFrameList, get the right parent frame for them, and put them in.
nsFrameList::FrameLinkEnumerator firstNonBlockEnumerator =
FindFirstNonBlock(aFrameList);
nsFrameList blockKids = aFrameList.ExtractHead(firstNonBlockEnumerator);
@@ -5799,18 +5809,19 @@ nsCSSFrameConstructor::AppendFramesToPar
nsFrameList inlineKids = aFrameList.ExtractHead(firstBlockEnumerator);
if (!inlineKids.IsEmpty()) {
AppendFrames(aParentFrame, kPrincipalList, inlineKids);
}
if (!aFrameList.IsEmpty()) {
const nsStyleDisplay* parentDisplay = aParentFrame->GetStyleDisplay();
bool positioned =
- parentDisplay->mPosition == NS_STYLE_POSITION_RELATIVE ||
- parentDisplay->HasTransform();
+ (parentDisplay->mPosition == NS_STYLE_POSITION_RELATIVE ||
+ parentDisplay->HasTransform()) &&
+ !aParentFrame->IsSVGText();
nsFrameItems ibSiblings;
CreateIBSiblings(aState, aParentFrame, positioned, aFrameList,
ibSiblings);
// Make sure to trigger reflow of the inline that used to be our
// last one and now isn't anymore, since its GetSkipSides() has
// changed.
mPresShell->FrameNeedsReflow(aParentFrame,
@@ -5846,18 +5857,18 @@ nsCSSFrameConstructor::IsValidSibling(ns
nsIAtom* grandparentType = nullptr;
if (parentFrame) {
parentType = parentFrame->GetType();
nsIFrame* grandparentFrame = parentFrame->GetParent();
if (grandparentFrame) {
grandparentType = grandparentFrame->GetType();
}
}
-
- PRUint8 siblingDisplay = aSibling->GetStyleDisplay()->mDisplay;
+
+ PRUint8 siblingDisplay = aSibling->GetDisplay();
if ((NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == siblingDisplay) ||
(NS_STYLE_DISPLAY_TABLE_COLUMN == siblingDisplay) ||
(NS_STYLE_DISPLAY_TABLE_CAPTION == siblingDisplay) ||
(NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == siblingDisplay) ||
(NS_STYLE_DISPLAY_TABLE_ROW_GROUP == siblingDisplay) ||
(NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == siblingDisplay) ||
nsGkAtoms::menuFrame == parentType) {
// if we haven't already, construct a style context to find the display type of aContent
@@ -6676,21 +6687,21 @@ nsCSSFrameConstructor::ContentAppended(n
LAYOUT_PHASE_TEMP_REENTER();
// If the parent is a block frame, and we're not in a special case
// where frames can be moved around, determine if the list is for the
// start or end of the block.
if (nsLayoutUtils::GetAsBlock(parentFrame) && !haveFirstLetterStyle &&
!haveFirstLineStyle && !parentSpecial) {
items.SetLineBoundaryAtStart(!prevSibling ||
- !prevSibling->GetStyleDisplay()->IsInlineOutside() ||
+ !prevSibling->IsInlineOutside() ||
prevSibling->GetType() == nsGkAtoms::brFrame);
// :after content can't be <br> so no need to check it
items.SetLineBoundaryAtEnd(!parentAfterFrame ||
- !parentAfterFrame->GetStyleDisplay()->IsInlineOutside());
+ !parentAfterFrame->IsInlineOutside());
}
// To suppress whitespace-only text frames, we have to verify that
// our container's DOM child list matches its flattened tree child list.
// This is guaranteed to be true if GetXBLChildNodesFor() returns null.
items.SetParentHasNoXBLChildren(haveNoXBLChildren);
nsFrameItems frameItems;
ConstructFramesFromItemList(state, items, parentFrame, frameItems);
@@ -7056,25 +7067,25 @@ nsCSSFrameConstructor::ContentRangeInser
nsIFrame* containingBlock = state.mFloatedItems.containingBlock;
bool haveFirstLetterStyle = false;
bool haveFirstLineStyle = false;
// In order to shave off some cycles, we only dig up the
// containing block haveFirst* flags if the parent frame where
// the insertion/append is occurring is an inline or block
// container. For other types of containers this isn't relevant.
- const nsStyleDisplay* parentDisplay = parentFrame->GetStyleDisplay();
+ PRUint8 parentDisplay = parentFrame->GetDisplay();
// Examine the parentFrame where the insertion is taking
// place. If it's a certain kind of container then some special
// processing is done.
- if ((NS_STYLE_DISPLAY_BLOCK == parentDisplay->mDisplay) ||
- (NS_STYLE_DISPLAY_LIST_ITEM == parentDisplay->mDisplay) ||
- (NS_STYLE_DISPLAY_INLINE == parentDisplay->mDisplay) ||
- (NS_STYLE_DISPLAY_INLINE_BLOCK == parentDisplay->mDisplay)) {
+ if ((NS_STYLE_DISPLAY_BLOCK == parentDisplay) ||
+ (NS_STYLE_DISPLAY_LIST_ITEM == parentDisplay) ||
+ (NS_STYLE_DISPLAY_INLINE == parentDisplay) ||
+ (NS_STYLE_DISPLAY_INLINE_BLOCK == parentDisplay)) {
// Recover the special style flags for the containing block
if (containingBlock) {
haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
haveFirstLineStyle =
ShouldHaveFirstLineStyle(containingBlock->GetContent(),
containingBlock->GetStyleContext());
}
@@ -9700,17 +9711,17 @@ nsCSSFrameConstructor::CreateNeededTable
true);
// Here we're cheating a tad... technically, table-internal items should be
// inline if aParentFrame is inline, but they'll get wrapped in an
// inline-table in the end, so it'll all work out. In any case, arguably
// we don't need to maintain this state at this point... but it's better
// to, I guess.
newItem->mIsAllInline = newItem->mHasInlineEnds =
- newItem->mStyleContext->GetStyleDisplay()->IsInlineOutside();
+ newItem->mStyleContext->GetStyleDisplay()->IsInlineOutsideStyle();
// Table pseudo frames always induce line boundaries around their
// contents.
newItem->mChildItems.SetLineBoundaryAtStart(true);
newItem->mChildItems.SetLineBoundaryAtEnd(true);
// The parent of the items in aItems is also the parent of the items
// in mChildItems
newItem->mChildItems.SetParentHasNoXBLChildren(
@@ -9985,17 +9996,17 @@ nsCSSFrameConstructor::WrapFramesInFirst
nsIFrame* aBlockFrame,
nsIFrame* aLineFrame,
nsFrameItems& aFrameItems)
{
nsresult rv = NS_OK;
// Find the part of aFrameItems that we want to put in the first-line
nsFrameList::FrameLinkEnumerator link(aFrameItems);
- while (!link.AtEnd() && IsInlineOutside(link.NextFrame())) {
+ while (!link.AtEnd() && link.NextFrame()->IsInlineOutside()) {
link.Next();
}
nsFrameList firstLineChildren = aFrameItems.ExtractHead(link);
if (firstLineChildren.IsEmpty()) {
// Nothing is supposed to go into the first-line; nothing to do
return NS_OK;
@@ -10422,17 +10433,17 @@ nsCSSFrameConstructor::CreateLetterFrame
NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame),
"Containing block is confused");
nsFrameConstructorState state(mPresShell, mFixedContainingBlock,
GetAbsoluteContainingBlock(aParentFrame),
aBlockContinuation);
// Create the right type of first-letter frame
const nsStyleDisplay* display = sc->GetStyleDisplay();
- if (display->IsFloating()) {
+ if (display->IsFloating(aParentFrame)) {
// Make a floating first-letter frame
CreateFloatingLetterFrame(state, aBlockFrame, aTextContent, textFrame,
blockContent, aParentFrame, sc, aResult);
}
else {
// Make an inflow first-letter frame
nsIFrame* letterFrame = NS_NewFirstLetterFrame(mPresShell, sc);
@@ -11017,17 +11028,18 @@ nsCSSFrameConstructor::ConstructInline(n
// Text("f")
nsIContent* const content = aItem.mContent;
nsStyleContext* const styleContext = aItem.mStyleContext;
bool positioned =
NS_STYLE_DISPLAY_INLINE == aDisplay->mDisplay &&
(NS_STYLE_POSITION_RELATIVE == aDisplay->mPosition ||
- aDisplay->HasTransform());
+ aDisplay->HasTransform()) &&
+ !aParentFrame->IsSVGText();
nsIFrame* newFrame = NS_NewInlineFrame(mPresShell, styleContext);
if (!newFrame) {
return NS_ERROR_OUT_OF_MEMORY;
}
// Initialize the frame
InitAndRestoreFrame(aState, content, aParentFrame, nullptr, newFrame);
@@ -11117,17 +11129,17 @@ nsCSSFrameConstructor::CreateIBSiblings(
nsCSSAnonBoxes::mozAnonymousBlock,
styleContext);
nsIFrame* lastNewInline = aInitialInline->GetFirstContinuation();
do {
// On entry to this loop aChildItems is not empty and the first frame in it
// is block-level.
NS_PRECONDITION(aChildItems.NotEmpty(), "Should have child items");
- NS_PRECONDITION(!IsInlineOutside(aChildItems.FirstChild()),
+ NS_PRECONDITION(!aChildItems.FirstChild()->IsInlineOutside(),
"Must have list starting with block");
// The initial run of blocks belongs to an anonymous block that we create
// right now. The anonymous block will be the parent of these block
// children of the inline.
nsIFrame* blockFrame;
blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
@@ -11568,17 +11580,17 @@ nsCSSFrameConstructor::WipeContainingBlo
// 3) Not an inline frame
// We're guaranteed to find one, since nsStyleContext::ApplyStyleFixups
// enforces that the root is display:none, display:table, or display:block.
// Note that walking up "too far" is OK in terms of correctness, even if it
// might be a little inefficient. This is why we walk out of all
// pseudo-frames -- telling which ones are or are not OK to walk out of is
// too hard (and I suspect that we do in fact need to walk out of all of
// them).
- while (IsFrameSpecial(aContainingBlock) || IsInlineOutside(aContainingBlock) ||
+ while (IsFrameSpecial(aContainingBlock) || aContainingBlock->IsInlineOutside() ||
aContainingBlock->GetStyleContext()->GetPseudo()) {
aContainingBlock = aContainingBlock->GetParent();
NS_ASSERTION(aContainingBlock,
"Must have non-inline, non-special, non-pseudo frame as root "
"(or child of root, for a table root)!");
}
// Tell parent of the containing block to reformulate the
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -690,16 +690,19 @@ private:
would have been scrollable but has been forced to be
non-scrollable due to being in a paginated context. */
#define FCDATA_FORCED_NON_SCROLLABLE_BLOCK 0x20000
/* If FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS is set, then create a
block formatting context wrapper around the kids of this frame
using the FrameConstructionData's mPseudoAtom for its anonymous
box type. */
#define FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS 0x40000
+ /* If FCDATA_IS_SVG_TEXT is set, then this text frame is a descendant of
+ an SVG text frame. */
+#define FCDATA_IS_SVG_TEXT 0x80000
/* Structure representing information about how a frame should be
constructed. */
struct FrameConstructionData {
// Flag bits that can modify the way the construction happens
PRUint32 mBits;
// We have exactly one of three types of functions, so use a union for
// better cache locality for the ones that aren't pointer-to-member. That
@@ -1368,17 +1371,17 @@ private:
PRInt32 aNameSpaceID,
nsIFrame* aParentFrame,
nsStyleContext* aStyleContext);
/* Not static because it does PropagateScrollToViewport. If this
changes, make this static */
const FrameConstructionData*
FindDisplayData(const nsStyleDisplay* aDisplay, Element* aElement,
- nsStyleContext* aStyleContext);
+ nsIFrame* aParentFrame, nsStyleContext* aStyleContext);
/**
* Construct a scrollable block frame
*/
nsresult ConstructScrollableBlock(nsFrameConstructorState& aState,
FrameConstructionItem& aItem,
nsIFrame* aParentFrame,
const nsStyleDisplay* aDisplay,
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -416,17 +416,17 @@ nsLayoutUtils::GetChildListNameFor(nsIFr
// XXX FIXME: Bug 350740
// Return here, because the postcondition for this function actually
// fails for this case, since the popups are not in a "real" frame list
// in the popup set.
return nsIFrame::kPopupList;
#endif // MOZ_XUL
} else {
- NS_ASSERTION(aChildFrame->GetStyleDisplay()->IsFloating(),
+ NS_ASSERTION(aChildFrame->IsFloating(),
"not a floated frame");
id = nsIFrame::kFloatList;
}
} else {
nsIAtom* childType = aChildFrame->GetType();
if (nsGkAtoms::menuPopupFrame == childType) {
nsIFrame* parent = aChildFrame->GetParent();
@@ -452,17 +452,17 @@ nsLayoutUtils::GetChildListNameFor(nsIFr
// corresponding overflow list.
nsIFrame* parent = aChildFrame->GetParent();
bool found = parent->GetChildList(id).ContainsFrame(aChildFrame);
if (!found) {
if (!(aChildFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
found = parent->GetChildList(nsIFrame::kOverflowList)
.ContainsFrame(aChildFrame);
}
- else if (aChildFrame->GetStyleDisplay()->IsFloating()) {
+ else if (aChildFrame->IsFloating()) {
found = parent->GetChildList(nsIFrame::kOverflowOutOfFlowList)
.ContainsFrame(aChildFrame);
}
// else it's positioned and should have been on the 'id' child list.
NS_POSTCONDITION(found, "not in child list");
}
#endif
@@ -530,17 +530,17 @@ nsLayoutUtils::GetStyleFrame(nsIFrame* a
nsIFrame*
nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) {
NS_ASSERTION(nsGkAtoms::placeholderFrame == aFrame->GetType(),
"Must have a placeholder here");
if (aFrame->GetStateBits() & PLACEHOLDER_FOR_FLOAT) {
nsIFrame *outOfFlowFrame =
nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
- NS_ASSERTION(outOfFlowFrame->GetStyleDisplay()->IsFloating(),
+ NS_ASSERTION(outOfFlowFrame->IsFloating(),
"How did that happen?");
return outOfFlowFrame;
}
return nullptr;
}
// static
@@ -1883,17 +1883,17 @@ nsLayoutUtils::PaintFrame(nsRenderingCon
// Flush the list so we don't trigger the IsEmpty-on-destruction assertion
list.DeleteAll();
return NS_OK;
}
PRInt32
nsLayoutUtils::GetZIndex(nsIFrame* aFrame) {
- if (!aFrame->GetStyleDisplay()->IsPositioned())
+ if (!aFrame->IsPositioned())
return 0;
const nsStylePosition* position =
aFrame->GetStylePosition();
if (position->mZIndex.GetUnit() == eStyleUnit_Integer)
return position->mZIndex.GetIntValue();
// sort the auto and 0 elements together
@@ -3440,17 +3440,17 @@ nsLayoutUtils::CalculateContentBottom(ns
return contentBottom;
}
/* static */ nsIFrame*
nsLayoutUtils::GetClosestLayer(nsIFrame* aFrame)
{
nsIFrame* layer;
for (layer = aFrame; layer; layer = layer->GetParent()) {
- if (layer->GetStyleDisplay()->IsPositioned() ||
+ if (layer->IsPositioned() ||
(layer->GetParent() &&
layer->GetParent()->GetType() == nsGkAtoms::scrollFrame))
break;
}
if (layer)
return layer;
return aFrame->PresContext()->PresShell()->FrameManager()->GetRootFrame();
}
@@ -4049,31 +4049,23 @@ nsLayoutUtils::GetDisplayRootFrame(nsIFr
return f;
nsIFrame* parent = GetCrossDocParentFrame(f);
if (!parent)
return f;
f = parent;
}
}
-static bool
-IsNonzeroCoord(const nsStyleCoord& aCoord)
-{
- if (eStyleUnit_Coord == aCoord.GetUnit())
- return aCoord.GetCoordValue() != 0;
- return false;
-}
-
/* static */ PRUint32
nsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext* aStyleContext,
- const nsStyleText* aStyleText,
- const nsStyleFont* aStyleFont)
+ const nsStyleFont* aStyleFont,
+ nscoord aLetterSpacing)
{
PRUint32 result = 0;
- if (IsNonzeroCoord(aStyleText->mLetterSpacing)) {
+ if (aLetterSpacing != 0) {
result |= gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES;
}
switch (aStyleContext->GetStyleSVG()->mTextRendering) {
case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED:
result |= gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
break;
case NS_STYLE_TEXT_RENDERING_AUTO:
if (aStyleFont->mFont.size <
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1306,18 +1306,18 @@ public:
/**
* Get textrun construction flags determined by a given style; in particular
* some combination of:
* -- TEXT_DISABLE_OPTIONAL_LIGATURES if letter-spacing is in use
* -- TEXT_OPTIMIZE_SPEED if the text-rendering CSS property and font size
* and prefs indicate we should be optimizing for speed over quality
*/
static PRUint32 GetTextRunFlagsForStyle(nsStyleContext* aStyleContext,
- const nsStyleText* aStyleText,
- const nsStyleFont* aStyleFont);
+ const nsStyleFont* aStyleFont,
+ nscoord aLetterSpacing);
/**
* Takes two rectangles whose origins must be the same, and computes
* the difference between their union and their intersection as two
* rectangles. (This difference is a superset of the difference
* between the two rectangles.)
*/
static void GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2,
--- a/layout/forms/nsLegendFrame.cpp
+++ b/layout/forms/nsLegendFrame.cpp
@@ -12,17 +12,17 @@
#include "nsStyleConsts.h"
#include "nsFormControlFrame.h"
nsIFrame*
NS_NewLegendFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
#ifdef DEBUG
const nsStyleDisplay* disp = aContext->GetStyleDisplay();
- NS_ASSERTION(!disp->IsAbsolutelyPositioned() && !disp->IsFloating(),
+ NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle() && !disp->IsFloatingStyle(),
"Legends should not be positioned and should not float");
#endif
nsIFrame* f = new (aPresShell) nsLegendFrame(aContext);
if (f) {
f->AddStateBits(NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT);
}
return f;
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -12,16 +12,17 @@
#include "nsContentUtils.h"
#include "nsGfxScrollFrame.h"
#include "nsIScrollableFrame.h"
#include "nsLayoutUtils.h"
#include "nsPresContext.h"
#include "nsRect.h"
#include "nsRenderingContext.h"
#include "nsTextFrame.h"
+#include "nsStyleStructInlines.h"
#include "mozilla/Util.h"
namespace mozilla {
namespace css {
static const PRUnichar kEllipsisChar[] = { 0x2026, 0x0 };
static const PRUnichar kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
@@ -48,17 +49,17 @@ GetSelfOrNearestBlock(nsIFrame* aFrame)
// Return true if the frame is an atomic inline-level element.
// It's not supposed to be called for block frames since we never
// process block descendants for text-overflow.
static bool
IsAtomicElement(nsIFrame* aFrame, const nsIAtom* aFrameType)
{
NS_PRECONDITION(!nsLayoutUtils::GetAsBlock(aFrame) ||
- !aFrame->GetStyleDisplay()->IsBlockOutside(),
+ !aFrame->IsBlockOutside(),
"unexpected block frame");
NS_PRECONDITION(aFrameType != nsGkAtoms::placeholderFrame,
"unexpected placeholder frame");
return !aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
}
static bool
IsFullyClipped(nsTextFrame* aFrame, nscoord aLeft, nscoord aRight,
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -48,16 +48,17 @@
#include "nsDisplayList.h"
#include "nsContentErrors.h"
#include "nsCSSAnonBoxes.h"
#include "nsCSSFrameConstructor.h"
#include "nsCSSRendering.h"
#include "FrameLayerBuilder.h"
#include "nsRenderingContext.h"
#include "TextOverflow.h"
+#include "nsStyleStructInlines.h"
#include "mozilla/Util.h" // for DebugOnly
#ifdef IBMBIDI
#include "nsBidiPresUtils.h"
#endif // IBMBIDI
#include "nsIDOMHTMLBodyElement.h"
#include "nsIDOMHTMLHtmlElement.h"
@@ -1570,21 +1571,24 @@ nsBlockFrame::MarkLineDirty(line_iterato
#endif
}
}
/**
* Test whether lines are certain to be aligned left so that we can make
* resizing optimizations
*/
-bool static inline IsAlignedLeft(const PRUint8 aAlignment,
- const PRUint8 aDirection,
- const PRUint8 aUnicodeBidi)
-{
- return (NS_STYLE_TEXT_ALIGN_LEFT == aAlignment ||
+static inline bool
+IsAlignedLeft(PRUint8 aAlignment,
+ PRUint8 aDirection,
+ PRUint8 aUnicodeBidi,
+ nsIFrame* aFrame)
+{
+ return (aFrame->IsSVGText() ||
+ NS_STYLE_TEXT_ALIGN_LEFT == aAlignment ||
((NS_STYLE_TEXT_ALIGN_DEFAULT == aAlignment &&
NS_STYLE_DIRECTION_LTR == aDirection) ||
(NS_STYLE_TEXT_ALIGN_END == aAlignment &&
NS_STYLE_DIRECTION_RTL == aDirection)) &&
!(NS_STYLE_UNICODE_BIDI_PLAINTEXT & aUnicodeBidi));
}
nsresult
@@ -1592,17 +1596,18 @@ nsBlockFrame::PrepareResizeReflow(nsBloc
{
const nsStyleText* styleText = GetStyleText();
const nsStyleTextReset* styleTextReset = GetStyleTextReset();
// See if we can try and avoid marking all the lines as dirty
bool tryAndSkipLines =
// The text must be left-aligned.
IsAlignedLeft(styleText->mTextAlign,
aState.mReflowState.mStyleVisibility->mDirection,
- styleTextReset->mUnicodeBidi) &&
+ styleTextReset->mUnicodeBidi,
+ this) &&
// The left content-edge must be a constant distance from the left
// border-edge.
!GetStylePadding()->mPadding.GetLeft().HasPercent();
#ifdef DEBUG
if (gDisableResizeOpt) {
tryAndSkipLines = false;
}
@@ -1631,17 +1636,18 @@ nsBlockFrame::PrepareResizeReflow(nsBloc
printf(": trying to avoid marking all lines dirty\n");
}
#endif
// The last line might not be aligned left even if the rest of the block is
bool skipLastLine = NS_STYLE_TEXT_ALIGN_AUTO == styleText->mTextAlignLast ||
IsAlignedLeft(styleText->mTextAlignLast,
aState.mReflowState.mStyleVisibility->mDirection,
- styleTextReset->mUnicodeBidi);
+ styleTextReset->mUnicodeBidi,
+ this);
for (line_iterator line = begin_lines(), line_end = end_lines();
line != line_end;
++line)
{
// We let child blocks make their own decisions the same
// way we are here.
bool isLastLine = line == mLines.back() && !GetNextInFlow();
@@ -2626,17 +2632,17 @@ nsBlockFrame::PullFrameFrom(nsBlockReflo
nsFrameList& aFromFrameList,
nsLineList::iterator aFromLine)
{
nsLineBox* fromLine = aFromLine;
NS_ABORT_IF_FALSE(fromLine, "bad line to pull from");
NS_ABORT_IF_FALSE(fromLine->GetChildCount(), "empty line");
NS_ABORT_IF_FALSE(aLine->GetChildCount(), "empty line");
- NS_ASSERTION(fromLine->IsBlock() == fromLine->mFirstChild->GetStyleDisplay()->IsBlockOutside(),
+ NS_ASSERTION(fromLine->IsBlock() == fromLine->mFirstChild->IsBlockOutside(),
"Disagreement about whether it's a block or not");
if (fromLine->IsBlock()) {
// If our line is not empty and the child in aFromLine is a block
// then we cannot pull up the frame into this line. In this case
// we stop pulling.
return nullptr;
}
@@ -4243,25 +4249,27 @@ nsBlockFrame::PlaceLine(nsBlockReflowSta
// Only block frames horizontally align their children because
// inline frames "shrink-wrap" around their children (therefore
// there is no extra horizontal space).
const nsStyleText* styleText = GetStyleText();
/**
* text-align-last defaults to the same value as text-align when
- * text-align-last is set to auto (unless when text-align is set to justify),
+ * text-align-last is set to auto (except when text-align is set to justify),
* so in that case we don't need to set isLastLine.
*
* In other words, isLastLine really means isLastLineAndWeCare.
*/
- bool isLastLine = ((NS_STYLE_TEXT_ALIGN_AUTO != styleText->mTextAlignLast ||
- NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
- (aLineLayout.GetLineEndsInBR() ||
- IsLastLine(aState, aLine)));
+ bool isLastLine =
+ !IsSVGText() &&
+ ((NS_STYLE_TEXT_ALIGN_AUTO != styleText->mTextAlignLast ||
+ NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
+ (aLineLayout.GetLineEndsInBR() ||
+ IsLastLine(aState, aLine)));
aLineLayout.HorizontalAlignFrames(aLine->mBounds, isLastLine);
// XXX: not only bidi: right alignment can be broken after
// RelativePositionFrames!!!
// XXXldb Is something here considering relatively positioned frames at
// other than their original positions?
#ifdef IBMBIDI
// XXXldb Why don't we do this earlier?
if (aState.mPresContext->BidiEnabled()) {
@@ -4943,21 +4951,21 @@ nsBlockFrame::AddFrames(nsFrameList& aFr
// Walk through the new frames being added and update the line data
// structures to fit.
for (nsFrameList::Enumerator e(newFrames); !e.AtEnd(); e.Next()) {
nsIFrame* newFrame = e.get();
NS_ASSERTION(!aPrevSibling || aPrevSibling->GetNextSibling() == newFrame,
"Unexpected aPrevSibling");
NS_ASSERTION(newFrame->GetType() != nsGkAtoms::placeholderFrame ||
- (!newFrame->GetStyleDisplay()->IsAbsolutelyPositioned() &&
- !newFrame->GetStyleDisplay()->IsFloating()),
+ (!newFrame->IsAbsolutelyPositioned() &&
+ !newFrame->IsFloating()),
"Placeholders should not float or be positioned");
- bool isBlock = newFrame->GetStyleDisplay()->IsBlockOutside();
+ bool isBlock = newFrame->IsBlockOutside();
// If the frame is a block frame, or if there is no previous line or if the
// previous line is a block line we need to make a new line. We also make
// a new line, as an optimization, in the two cases we know we'll need it:
// if the previous line ended with a <br>, or if it has significant whitespace
// and ended in a newline.
if (isBlock || prevSibLine == lineList->end() || prevSibLine->IsBlock() ||
(aPrevSibling && ShouldPutNextSiblingOnNewLine(aPrevSibling))) {
@@ -5120,18 +5128,17 @@ nsBlockFrame::RemoveFrame(ChildListID aL
void
nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame)
{
// The containing block is always the parent of aFrame.
nsBlockFrame* block = (nsBlockFrame*)aFrame->GetParent();
// Remove aFrame from the appropriate list.
- const nsStyleDisplay* display = aFrame->GetStyleDisplay();
- if (display->IsAbsolutelyPositioned()) {
+ if (aFrame->IsAbsolutelyPositioned()) {
// This also deletes the next-in-flows
block->GetAbsoluteContainingBlock()->RemoveFrame(block,
kAbsoluteList,
aFrame);
}
else {
// First remove aFrame's next-in-flows
nsIFrame* nif = aFrame->GetNextInFlow();
@@ -5631,17 +5638,17 @@ nsBlockFrame::DoRemoveFrame(nsIFrame* aD
nsresult
nsBlockFrame::StealFrame(nsPresContext* aPresContext,
nsIFrame* aChild,
bool aForceNormal)
{
NS_PRECONDITION(aPresContext && aChild, "null pointer");
if ((aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
- aChild->GetStyleDisplay()->IsFloating()) {
+ aChild->IsFloating()) {
bool removed = mFloats.RemoveFrameIfPresent(aChild);
if (!removed) {
nsFrameList* list = GetPushedFloats();
if (list) {
removed = list->RemoveFrameIfPresent(aChild);
}
}
return removed ? NS_OK : NS_ERROR_UNEXPECTED;
@@ -6467,17 +6474,17 @@ nsLineBox* nsBlockFrame::GetFirstLineCon
return cursor.get();
}
/* virtual */ void
nsBlockFrame::ChildIsDirty(nsIFrame* aChild)
{
// See if the child is absolutely positioned
if (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW &&
- aChild->GetStyleDisplay()->IsAbsolutelyPositioned()) {
+ aChild->IsAbsolutelyPositioned()) {
// do nothing
} else if (aChild == GetOutsideBullet()) {
// The bullet lives in the first line, unless the first line has
// height 0 and there is a second line, in which case it lives
// in the second line.
line_iterator bulletLine = begin_lines();
if (bulletLine != end_lines() && bulletLine->mBounds.height == 0 &&
bulletLine != mLines.back()) {
@@ -6559,17 +6566,18 @@ nsBlockFrame::SetInitialChildList(ChildL
// block.
nsIAtom *pseudo = GetStyleContext()->GetPseudo();
bool haveFirstLetterStyle =
(!pseudo ||
(pseudo == nsCSSAnonBoxes::cellContent &&
mParent->GetStyleContext()->GetPseudo() == nullptr) ||
pseudo == nsCSSAnonBoxes::fieldsetContent ||
pseudo == nsCSSAnonBoxes::scrolledContent ||
- pseudo == nsCSSAnonBoxes::columnContent) &&
+ pseudo == nsCSSAnonBoxes::columnContent ||
+ pseudo == nsCSSAnonBoxes::mozSVGText) &&
!IsFrameOfType(eMathML) &&
nsRefPtr<nsStyleContext>(GetFirstLetterStyle(presContext)) != nullptr;
NS_ASSERTION(haveFirstLetterStyle ==
((mState & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0),
"NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
#endif
rv = AddFrames(aChildList, nullptr);
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -769,17 +769,17 @@ nsContainerFrame::SyncFrameViewPropertie
// See if the view should be hidden or visible
vm->SetViewVisibility(aView,
aStyleContext->GetStyleVisibility()->IsVisible()
? nsViewVisibility_kShow : nsViewVisibility_kHide);
}
// See if the frame is being relatively positioned or absolutely
// positioned
- bool isPositioned = aStyleContext->GetStyleDisplay()->IsPositioned();
+ bool isPositioned = aFrame->IsPositioned();
PRInt32 zIndex = 0;
bool autoZIndex = false;
if (!isPositioned) {
autoZIndex = true;
} else {
// Make sure z-index is correct
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -510,17 +510,17 @@ protected:
* in nsAbsoluteContainingBlock and pretend the continuation is an overflow
* container. This isn't an ideal solution, but it lets us print the content
* at least. See bug 154892.
*/
#define IS_TRUE_OVERFLOW_CONTAINER(frame) \
( (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) \
&& !( (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && \
- frame->GetStyleDisplay()->IsAbsolutelyPositioned() ) )
+ frame->IsAbsolutelyPositioned() ) )
//XXXfr This check isn't quite correct, because it doesn't handle cases
// where the out-of-flow has overflow.. but that's rare.
// We'll need to revisit the way abspos continuations are handled later
// for various reasons, this detail is one of them. See bug 154892
/**
* Helper class for tracking overflow container continuations during reflow.
*
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -440,18 +440,18 @@ IsFontSizeInflationContainer(nsIFrame* a
*/
// The root frame should always be an inflation container.
if (!aFrame->GetParent()) {
return true;
}
nsIContent *content = aFrame->GetContent();
- bool isInline = (aStyleDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE ||
- (aStyleDisplay->IsFloating() &&
+ bool isInline = (aFrame->GetDisplay() == NS_STYLE_DISPLAY_INLINE ||
+ (aFrame->IsFloating() &&
aFrame->GetType() == nsGkAtoms::letterFrame) ||
// Given multiple frames for the same node, only the
// outer one should be considered a container.
// (Important, e.g., for nsSelectsAreaFrame.)
(aFrame->GetParent()->GetContent() == content) ||
(content && (content->IsHTML(nsGkAtoms::option) ||
content->IsHTML(nsGkAtoms::optgroup) ||
content->IsInNativeAnonymousSubtree()))) &&
@@ -496,17 +496,18 @@ nsFrame::Init(nsIContent* aContent,
NS_FRAME_MAY_BE_TRANSFORMED |
NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
}
if (mParent) {
nsFrameState state = mParent->GetStateBits();
// Make bits that are currently off (see constructor) the same:
mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
- NS_FRAME_GENERATED_CONTENT);
+ NS_FRAME_GENERATED_CONTENT |
+ NS_FRAME_IS_SVG_TEXT);
}
const nsStyleDisplay *disp = GetStyleDisplay();
if (disp->HasTransform()) {
// The frame gets reconstructed if we toggle the -moz-transform
// property, so we can set this bit here and then ignore it.
mState |= NS_FRAME_MAY_BE_TRANSFORMED;
}
@@ -516,17 +517,17 @@ nsFrame::Init(nsIContent* aContent,
// font size inflation is not enabled.
|| true
#endif
) {
if (IsFontSizeInflationContainer(this, disp)) {
AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER);
if (!GetParent() ||
// I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet.
- disp->IsFloating() || disp->IsAbsolutelyPositioned()) {
+ disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this)) {
AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
}
}
NS_ASSERTION(GetParent() ||
(GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER),
"root frame should always be a container");
}
@@ -806,17 +807,18 @@ const nsIFrame::ChildListID nsIFrame::kS
const nsIFrame::ChildListID nsIFrame::kNoReflowPrincipalList;
#endif
/* virtual */ nsMargin
nsIFrame::GetUsedMargin() const
{
nsMargin margin(0, 0, 0, 0);
if ((mState & NS_FRAME_FIRST_REFLOW) &&
- !(mState & NS_FRAME_IN_REFLOW))
+ !(mState & NS_FRAME_IN_REFLOW) ||
+ (mState & NS_FRAME_IS_SVG_TEXT))
return margin;
nsMargin *m = static_cast<nsMargin*>
(Properties().Get(UsedMarginProperty()));
if (m) {
margin = *m;
} else {
#ifdef DEBUG
@@ -828,17 +830,18 @@ nsIFrame::GetUsedMargin() const
return margin;
}
/* virtual */ nsMargin
nsIFrame::GetUsedBorder() const
{
nsMargin border(0, 0, 0, 0);
if ((mState & NS_FRAME_FIRST_REFLOW) &&
- !(mState & NS_FRAME_IN_REFLOW))
+ !(mState & NS_FRAME_IN_REFLOW) ||
+ (mState & NS_FRAME_IS_SVG_TEXT))
return border;
// Theme methods don't use const-ness.
nsIFrame *mutable_this = const_cast<nsIFrame*>(this);
const nsStyleDisplay *disp = GetStyleDisplay();
if (mutable_this->IsThemed(disp)) {
nsIntMargin result;
@@ -863,17 +866,18 @@ nsIFrame::GetUsedBorder() const
return border;
}
/* virtual */ nsMargin
nsIFrame::GetUsedPadding() const
{
nsMargin padding(0, 0, 0, 0);
if ((mState & NS_FRAME_FIRST_REFLOW) &&
- !(mState & NS_FRAME_IN_REFLOW))
+ !(mState & NS_FRAME_IN_REFLOW) ||
+ (mState & NS_FRAME_IS_SVG_TEXT))
return padding;
// Theme methods don't use const-ness.
nsIFrame *mutable_this = const_cast<nsIFrame*>(this);
const nsStyleDisplay *disp = GetStyleDisplay();
if (mutable_this->IsThemed(disp)) {
nsPresContext *presContext = PresContext();
@@ -1487,17 +1491,17 @@ inline static bool IsSVGContentWithCSSCl
bool
nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp, nsRect* aRect,
const nsSize& aSize) const
{
NS_PRECONDITION(aRect, "Must have aRect out parameter");
if (!(aDisp->mClipFlags & NS_STYLE_CLIP_RECT) ||
- !(aDisp->IsAbsolutelyPositioned() || IsSVGContentWithCSSClip(this))) {
+ !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) {
return false;
}
*aRect = aDisp->mClip;
if (NS_STYLE_CLIP_RIGHT_AUTO & aDisp->mClipFlags) {
aRect->width = aSize.width - aRect->x;
}
if (NS_STYLE_CLIP_BOTTOM_AUTO & aDisp->mClipFlags) {
@@ -2095,18 +2099,18 @@ nsIFrame::BuildDisplayListForChild(nsDis
// Child is composited if it's transformed, partially transparent, or has
// SVG effects.
const nsStyleDisplay* disp = child->GetStyleDisplay();
bool isVisuallyAtomic = child->HasOpacity()
|| child->IsTransformed()
|| nsSVGIntegrationUtils::UsingEffectsForFrame(child);
- bool isPositioned = !isSVG && disp->IsPositioned();
- if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating()) ||
+ bool isPositioned = !isSVG && disp->IsPositioned(child);
+ if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) ||
((disp->mClipFlags & NS_STYLE_CLIP_RECT) &&
IsSVGContentWithCSSClip(child)) ||
(aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
// If you change this, also change IsPseudoStackingContextFromStyle()
pseudoStackingContext = true;
}
// This controls later whether we build an nsDisplayWrapList or an
@@ -2237,17 +2241,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
nsDisplayList fixedPosDescendantList;
fixedPosDescendantList.AppendToTop(item);
aLists.PositionedDescendants()->AppendNewToTop(
new (aBuilder) nsDisplayFixedPosition(aBuilder, item->GetUnderlyingFrame(),
child, &fixedPosDescendantList));
}
}
}
- } else if (!isSVG && disp->IsFloating()) {
+ } else if (!isSVG && disp->IsFloating(child)) {
if (!list.IsEmpty()) {
rv = aLists.Floats()->AppendNewToTop(new (aBuilder)
nsDisplayWrapList(aBuilder, child, &list));
NS_ENSURE_SUCCESS(rv, rv);
}
} else {
aLists.Content()->AppendToTop(&list);
}
@@ -3464,17 +3468,17 @@ nsIFrame::ContentOffsets OffsetsForSingl
offsets.secondaryOffset = range.end;
offsets.associateWithNext = true;
return offsets;
}
// Figure out whether the offsets should be over, after, or before the frame
nsRect rect(nsPoint(0, 0), aFrame->GetSize());
- bool isBlock = (aFrame->GetStyleDisplay()->mDisplay != NS_STYLE_DISPLAY_INLINE);
+ bool isBlock = aFrame->GetDisplay() != NS_STYLE_DISPLAY_INLINE;
bool isRtl = (aFrame->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL);
if ((isBlock && rect.y < aPoint.y) ||
(!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) ||
(!isRtl && rect.x + rect.width / 2 < aPoint.x)))) {
offsets.offset = range.end;
if (rect.Contains(aPoint))
offsets.secondaryOffset = range.start;
else
@@ -5009,17 +5013,18 @@ ComputeOutlineAndEffectsRect(nsIFrame* a
}
return r;
}
nsPoint
nsIFrame::GetRelativeOffset(const nsStyleDisplay* aDisplay) const
{
- if (!aDisplay || NS_STYLE_POSITION_RELATIVE == aDisplay->mPosition) {
+ if (!aDisplay ||
+ aDisplay->IsRelativelyPositioned(this)) {
nsPoint *offsets = static_cast<nsPoint*>
(Properties().Get(ComputedOffsetProperty()));
if (offsets) {
return *offsets;
}
}
return nsPoint(0,0);
}
@@ -5285,17 +5290,17 @@ GetNearestBlockContainer(nsIFrame* frame
}
nsIFrame*
nsIFrame::GetContainingBlock() const
{
// MathML frames might have absolute positioning style, but they would
// still be in-flow. So we have to check to make sure that the frame
// is really out-of-flow too.
- if (GetStyleDisplay()->IsAbsolutelyPositioned() &&
+ if (IsAbsolutelyPositioned() &&
(GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
return GetParent(); // the parent is always the containing block
}
return GetNearestBlockContainer(GetParent());
}
#ifdef DEBUG
@@ -7404,16 +7409,62 @@ nsIFrame::IsFocusable(PRInt32 *aTabIndex
* should return false if this is not a text frame.
*/
bool
nsIFrame::HasTerminalNewline() const
{
return false;
}
+static PRUint8
+ConvertSVGDominantBaselineToVerticalAlign(PRUint8 aDominantBaseline)
+{
+ switch (aDominantBaseline) {
+ case NS_STYLE_DOMINANT_BASELINE_HANGING:
+ case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE:
+ return NS_STYLE_VERTICAL_ALIGN_TEXT_TOP;
+ case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE:
+ case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC:
+ return NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM;
+ case NS_STYLE_DOMINANT_BASELINE_CENTRAL:
+ case NS_STYLE_DOMINANT_BASELINE_MIDDLE:
+ return NS_STYLE_VERTICAL_ALIGN_MIDDLE;
+ case NS_STYLE_DOMINANT_BASELINE_AUTO:
+ case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC:
+ return NS_STYLE_VERTICAL_ALIGN_BASELINE;
+ default:
+ NS_NOTREACHED("unexpected aDominantBaseline value");
+ return NS_STYLE_VERTICAL_ALIGN_BASELINE;
+ }
+}
+
+PRUint8
+nsIFrame::VerticalAlignEnum() const
+{
+ if (mState & NS_FRAME_IS_SVG_TEXT) {
+ PRUint8 dominantBaseline;
+ for (const nsIFrame* frame = this; frame; frame = frame->GetParent()) {
+ dominantBaseline = frame->GetStyleSVGReset()->mDominantBaseline;
+ if (dominantBaseline != NS_STYLE_DOMINANT_BASELINE_AUTO ||
+ frame->GetType() == nsGkAtoms::svgTextFrame) {
+ break;
+ }
+ }
+ return ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline);
+ }
+
+ const nsStyleCoord& verticalAlign =
+ GetStyleContext()->GetStyleTextReset()->mVerticalAlign;
+ if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) {
+ return verticalAlign.GetIntValue();
+ }
+
+ return eInvalidVerticalAlign;
+}
+
/* static */
void nsFrame::FillCursorInformationFromStyle(const nsStyleUserInterface* ui,
nsIFrame::Cursor& aCursor)
{
aCursor.mCursor = ui->mCursor;
aCursor.mHaveHotspot = false;
aCursor.mHotspotX = aCursor.mHotspotY = 0.0f;
@@ -9308,19 +9359,19 @@ nsHTMLReflowState::DisplayInitFrameTypeE
printf("InitFrameType");
const nsStyleDisplay *disp = aState->mStyleDisplay;
if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
printf(" out-of-flow");
if (aFrame->GetPrevInFlow())
printf(" prev-in-flow");
- if (disp->IsAbsolutelyPositioned())
+ if (aFrame->IsAbsolutelyPositioned())
printf(" abspos");
- if (disp->IsFloating())
+ if (aFrame->IsFloating())
printf(" float");
// This array must exactly match the NS_STYLE_DISPLAY constants.
const char *const displayTypes[] = {
"none", "block", "inline", "inline-block", "list-item", "marker",
"run-in", "compact", "table", "inline-table", "table-row-group",
"table-column", "table-column-group", "table-header-group",
"table-footer-group", "table-row", "table-cell", "table-caption",
--- a/layout/generic/nsFrameSetFrame.cpp
+++ b/layout/generic/nsFrameSetFrame.cpp
@@ -1511,17 +1511,17 @@ nsHTMLFramesetFrame::EndMouseDrag(nsPres
gDragInProgress = false;
}
nsIFrame*
NS_NewHTMLFramesetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
#ifdef DEBUG
const nsStyleDisplay* disp = aContext->GetStyleDisplay();
- NS_ASSERTION(!disp->IsAbsolutelyPositioned() && !disp->IsFloating(),
+ NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle() && !disp->IsFloatingStyle(),
"Framesets should not be positioned and should not float");
#endif
return new (aPresShell) nsHTMLFramesetFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetFrame)
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2111,17 +2111,17 @@ void
nsGfxScrollFrameInner::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists,
bool& aCreateLayer,
bool aPositioned)
{
for (nsIFrame* kid = mOuter->GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) {
if (kid == mScrolledFrame ||
- (kid->GetStyleDisplay()->IsPositioned() != aPositioned))
+ (kid->IsPositioned() != aPositioned))
continue;
nsDisplayListCollection partList;
mOuter->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, partList,
nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
// Don't append textarea resizers to the positioned descendants because
// we don't want them to float on top of overlapping elements.
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -390,17 +390,17 @@ IsQuirkContainingBlockHeight(const nsHTM
if (nsGkAtoms::blockFrame == aFrameType ||
#ifdef MOZ_XUL
nsGkAtoms::XULLabelFrame == aFrameType ||
#endif
nsGkAtoms::scrollFrame == aFrameType) {
// Note: This next condition could change due to a style change,
// but that would cause a style reflow anyway, which means we're ok.
if (NS_AUTOHEIGHT == rs->ComputedHeight()) {
- if (!rs->frame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
+ if (!rs->frame->IsAbsolutelyPositioned()) {
return false;
}
}
}
return true;
}
@@ -636,39 +636,39 @@ nsHTMLReflowState::InitFrameType(nsIAtom
DISPLAY_INIT_TYPE(frame, this);
if (aFrameType == nsGkAtoms::tableFrame) {
mFrameType = NS_CSS_FRAME_TYPE_BLOCK;
return;
}
- NS_ASSERTION(frame->GetStyleDisplay()->IsAbsolutelyPositioned() ==
- disp->IsAbsolutelyPositioned(),
+ NS_ASSERTION(frame->GetStyleDisplay()->IsAbsolutelyPositionedStyle() ==
+ disp->IsAbsolutelyPositionedStyle(),
"Unexpected position style");