mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Merge jsdbg2 to mozilla-inbound. See bug 672829.
This commit is contained in:
commit
ee1e3d9a60
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1311096050000">
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1311891123000">
|
||||
<emItems>
|
||||
<emItem blockID="i41" id="{99079a25-328f-4bd4-be04-00955acaa0a7}">
|
||||
<versionRange minVersion="0.1" maxVersion="4.3.0.00" severity="1">
|
||||
<versionRange minVersion="0.1" maxVersion="4.3.1.00" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i8" id="{B13721C7-F507-4982-B2E5-502A71474FED}">
|
||||
@ -30,7 +30,7 @@
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i39" id="{c2d64ff7-0ab8-4263-89c9-ea3b0f8f050c}">
|
||||
<versionRange minVersion="0.1" maxVersion="4.3.0.00" severity="1">
|
||||
<versionRange minVersion="0.1" maxVersion="4.3.1.00" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i10" id="{8CE11043-9A15-4207-A565-0C94C42D590D}">
|
||||
@ -61,7 +61,7 @@
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i40" id="{28387537-e3f9-4ed7-860c-11e69af4a8a0}">
|
||||
<versionRange minVersion="0.1" maxVersion="4.3.0.00" severity="1">
|
||||
<versionRange minVersion="0.1" maxVersion="4.3.1.00" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i23" id="firefox@bandoo.com">
|
||||
|
@ -74,6 +74,11 @@ pref("extensions.blocklist.itemURL", "https://addons.mozilla.org/%LOCALE%/%APP%/
|
||||
|
||||
pref("extensions.update.autoUpdateDefault", true);
|
||||
|
||||
// Disable add-ons installed into the shared user and shared system areas by
|
||||
// default. This does not include the application directory. See the SCOPE
|
||||
// constants in AddonManager.jsm for values to use here
|
||||
pref("extensions.autoDisableScopes", 10);
|
||||
|
||||
// Dictionary download preference
|
||||
pref("browser.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/firefox/dictionaries/");
|
||||
|
||||
|
@ -588,6 +588,7 @@ function startTest() {
|
||||
iframe = subwindow.document.getElementById("test-iframe");
|
||||
textarea = subwindow.document.getElementById("test-textarea");
|
||||
contenteditable = subwindow.document.getElementById("test-contenteditable");
|
||||
contenteditable.focus(); // content editable needs to be focused to enable spellcheck
|
||||
inputspell = subwindow.document.getElementById("test-input-spellcheck");
|
||||
pagemenu = subwindow.document.getElementById("test-pagemenu");
|
||||
|
||||
|
@ -51,6 +51,7 @@ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
@ -399,6 +400,21 @@ BrowserGlue.prototype = {
|
||||
// been warned about them yet, open the plugins update page.
|
||||
if (Services.prefs.getBoolPref(PREF_PLUGINS_NOTIFYUSER))
|
||||
this._showPluginUpdatePage();
|
||||
|
||||
// For any add-ons that were installed disabled and can be enabled offer
|
||||
// them to the user
|
||||
var win = this.getMostRecentBrowserWindow();
|
||||
var browser = win.gBrowser;
|
||||
var changedIDs = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED);
|
||||
AddonManager.getAddonsByIDs(changedIDs, function(aAddons) {
|
||||
aAddons.forEach(function(aAddon) {
|
||||
// If the add-on isn't user disabled or can't be enabled then skip it
|
||||
if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE))
|
||||
return;
|
||||
|
||||
browser.selectedTab = browser.addTab("about:newaddon?id=" + aAddon.id);
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
_onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
|
||||
|
@ -741,7 +741,6 @@ toolbar[mode="icons"] #zoom-out-button {
|
||||
toolbar[mode="icons"] #zoom-in-button {
|
||||
-moz-border-start: none;
|
||||
-moz-margin-start: 0;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
#zoom-out-button:-moz-locale-dir(ltr),
|
||||
|
@ -1081,6 +1081,7 @@ endif
|
||||
|
||||
ifdef DTRACE_PROBE_OBJ
|
||||
EXTRA_DEPS += $(DTRACE_PROBE_OBJ)
|
||||
OBJS += $(DTRACE_PROBE_OBJ)
|
||||
endif
|
||||
|
||||
$(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
|
||||
@ -1121,8 +1122,8 @@ ifdef HAVE_DTRACE
|
||||
ifndef XP_MACOSX
|
||||
ifdef DTRACE_PROBE_OBJ
|
||||
ifndef DTRACE_LIB_DEPENDENT
|
||||
$(DTRACE_PROBE_OBJ): $(OBJS)
|
||||
dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ) $(OBJS)
|
||||
$(DTRACE_PROBE_OBJ):
|
||||
dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -1144,7 +1145,7 @@ endif
|
||||
$(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(DTRACE_PROBE_OBJ) $(MOZILLA_PROBE_LIBS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE)
|
||||
@$(RM) $(DTRACE_PROBE_OBJ)
|
||||
else # ! DTRACE_LIB_DEPENDENT
|
||||
$(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(DTRACE_PROBE_OBJ) $(LOBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE)
|
||||
$(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE)
|
||||
endif # DTRACE_LIB_DEPENDENT
|
||||
@$(call CHECK_STDCXX,$@)
|
||||
|
||||
|
12
configure.in
12
configure.in
@ -7593,6 +7593,18 @@ if test -n "$JS_GC_ZEAL"; then
|
||||
AC_DEFINE(JS_GC_ZEAL)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl JS opt-mode assertions and minidump instrumentation
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(js-diagnostics,
|
||||
[ --enable-js-diagnostics
|
||||
Enable JS diagnostic assertions and breakpad data],
|
||||
JS_CRASH_DIAGNOSTICS=1,
|
||||
JS_CRASH_DIAGNOSTICS= )
|
||||
if test -n "$JS_CRASH_DIAGNOSTICS"; then
|
||||
AC_DEFINE(JS_CRASH_DIAGNOSTICS)
|
||||
fi
|
||||
|
||||
dnl ======================================================
|
||||
dnl = Enable compiling with ccache
|
||||
dnl ======================================================
|
||||
|
@ -944,6 +944,31 @@ public:
|
||||
*/
|
||||
nsIContent* GetEditingHost();
|
||||
|
||||
/**
|
||||
* Determing language. Look at the nearest ancestor element that has a lang
|
||||
* attribute in the XML namespace or is an HTML element and has a lang in
|
||||
* no namespace attribute.
|
||||
*/
|
||||
void GetLang(nsAString& aResult) const {
|
||||
for (const nsIContent* content = this; content; content = content->GetParent()) {
|
||||
if (content->GetAttrCount() > 0) {
|
||||
// xml:lang has precedence over lang on HTML elements (see
|
||||
// XHTML1 section C.7).
|
||||
PRBool hasAttr = content->GetAttr(kNameSpaceID_XML, nsGkAtoms::lang,
|
||||
aResult);
|
||||
if (!hasAttr && content->IsHTML()) {
|
||||
hasAttr = content->GetAttr(kNameSpaceID_None, nsGkAtoms::lang,
|
||||
aResult);
|
||||
}
|
||||
NS_ASSERTION(hasAttr || aResult.IsEmpty(),
|
||||
"GetAttr that returns false should not make string non-empty");
|
||||
if (hasAttr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Overloaded from nsINode
|
||||
virtual already_AddRefed<nsIURI> GetBaseURI() const;
|
||||
|
||||
|
@ -90,6 +90,8 @@ static RedirEntry kRedirMap[] = {
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
{ "addons", "chrome://mozapps/content/extensions/extensions.xul",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
{ "newaddon", "chrome://mozapps/content/extensions/newaddon.xul",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
{ "support", "chrome://global/content/aboutSupport.xhtml",
|
||||
nsIAboutModule::ALLOW_SCRIPT }
|
||||
};
|
||||
|
@ -212,6 +212,7 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "memory", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "addons", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
|
||||
{ NS_URI_LOADER_CONTRACTID, &kNS_URI_LOADER_CID },
|
||||
{ NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &kNS_DOCUMENTLOADER_SERVICE_CID },
|
||||
|
@ -88,6 +88,11 @@ EXPORTS = \
|
||||
nsDOMMemoryReporter.h \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS_NAMESPACES = mozilla/dom
|
||||
EXPORTS_mozilla/dom = \
|
||||
StructuredCloneTags.h \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
nsBarProps.cpp \
|
||||
nsDOMException.cpp \
|
||||
|
55
dom/base/StructuredCloneTags.h
Normal file
55
dom/base/StructuredCloneTags.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Structured Clone Code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Kyle Huey <me@kylehuey.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef StructuredCloneTags_h__
|
||||
#define StructuredCloneTags_h__
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
enum StructuredCloneTags {
|
||||
SCTAG_BASE = JS_SCTAG_USER_MIN,
|
||||
SCTAG_DOM_BLOB,
|
||||
SCTAG_DOM_FILELIST,
|
||||
SCTAG_DOM_MAX
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // StructuredCloneTags_h__
|
@ -179,6 +179,7 @@
|
||||
#include "nsCSSProps.h"
|
||||
#include "nsFileDataProtocolHandler.h"
|
||||
#include "nsIDOMFile.h"
|
||||
#include "nsIDOMFileList.h"
|
||||
#include "nsIURIFixup.h"
|
||||
#include "mozilla/FunctionTimer.h"
|
||||
#include "nsCDefaultURIFixup.h"
|
||||
@ -240,6 +241,8 @@
|
||||
#include "mozilla/dom/indexedDB/IDBFactory.h"
|
||||
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
|
||||
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
|
||||
#include "nsRefreshDriver.h"
|
||||
#include "mozAutoDocUpdate.h"
|
||||
|
||||
@ -5911,7 +5914,6 @@ nsGlobalWindow::CallerInnerWindow()
|
||||
return static_cast<nsGlobalWindow*>(win.get());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class used to represent events generated by calls to Window.postMessage,
|
||||
* which asynchronously creates and dispatches events.
|
||||
@ -5949,6 +5951,12 @@ class PostMessageEvent : public nsRunnable
|
||||
aBuffer.steal(&mMessage, &mMessageLen);
|
||||
}
|
||||
|
||||
bool StoreISupports(nsISupports* aSupports)
|
||||
{
|
||||
mSupportsArray.AppendElement(aSupports);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<nsGlobalWindow> mSource;
|
||||
nsString mCallerOrigin;
|
||||
@ -5957,8 +5965,102 @@ class PostMessageEvent : public nsRunnable
|
||||
nsRefPtr<nsGlobalWindow> mTargetWindow;
|
||||
nsCOMPtr<nsIURI> mProvidedOrigin;
|
||||
PRBool mTrustedCaller;
|
||||
nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
struct StructuredCloneInfo {
|
||||
PostMessageEvent* event;
|
||||
PRBool subsumes;
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
PostMessageReadStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32 tag,
|
||||
uint32 data,
|
||||
void* closure)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) {
|
||||
NS_ASSERTION(!data, "Data should be empty");
|
||||
|
||||
nsISupports* supports;
|
||||
if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
|
||||
JSObject* global = JS_GetGlobalForObject(cx, JS_GetScopeChain(cx));
|
||||
if (global) {
|
||||
jsval val;
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
|
||||
if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, global, supports,
|
||||
&val,
|
||||
getter_AddRefs(wrapper)))) {
|
||||
return JSVAL_TO_OBJECT(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||
cx->runtime->structuredCloneCallbacks;
|
||||
|
||||
if (runtimeCallbacks) {
|
||||
return runtimeCallbacks->read(cx, reader, tag, data, nsnull);
|
||||
}
|
||||
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
PostMessageWriteStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneWriter* writer,
|
||||
JSObject* obj,
|
||||
void *closure)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
|
||||
nsContentUtils::XPConnect()->
|
||||
GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
|
||||
if (wrappedNative) {
|
||||
PRUint32 scTag = 0;
|
||||
nsISupports* supports = wrappedNative->Native();
|
||||
|
||||
nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
|
||||
if (blob && scInfo->subsumes)
|
||||
scTag = SCTAG_DOM_BLOB;
|
||||
|
||||
nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
|
||||
if (list && scInfo->subsumes)
|
||||
scTag = SCTAG_DOM_FILELIST;
|
||||
|
||||
if (scTag)
|
||||
return JS_WriteUint32Pair(writer, scTag, 0) &&
|
||||
JS_WriteBytes(writer, &supports, sizeof(supports)) &&
|
||||
scInfo->event->StoreISupports(supports);
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||
cx->runtime->structuredCloneCallbacks;
|
||||
|
||||
if (runtimeCallbacks) {
|
||||
return runtimeCallbacks->write(cx, writer, obj, nsnull);
|
||||
}
|
||||
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSStructuredCloneCallbacks kPostMessageCallbacks = {
|
||||
PostMessageReadStructuredClone,
|
||||
PostMessageWriteStructuredClone,
|
||||
nsnull
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMETHODIMP
|
||||
PostMessageEvent::Run()
|
||||
{
|
||||
@ -6046,8 +6148,10 @@ PostMessageEvent::Run()
|
||||
jsval messageData;
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
StructuredCloneInfo scInfo;
|
||||
scInfo.event = this;
|
||||
|
||||
if (!buffer.read(cx, &messageData, nsnull))
|
||||
if (!buffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo))
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
@ -6173,8 +6277,14 @@ nsGlobalWindow::PostMessageMoz(const jsval& aMessage,
|
||||
// We *must* clone the data here, or the jsval could be modified
|
||||
// by script
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
StructuredCloneInfo scInfo;
|
||||
scInfo.event = event;
|
||||
|
||||
if (!buffer.write(aCx, aMessage, nsnull, nsnull))
|
||||
nsIPrincipal* principal = GetPrincipal();
|
||||
if (NS_FAILED(callerPrin->Subsumes(principal, &scInfo.subsumes)))
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
|
||||
if (!buffer.write(aCx, aMessage, &kPostMessageCallbacks, &scInfo))
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
|
||||
event->SetJSData(buffer);
|
||||
|
@ -42,11 +42,15 @@
|
||||
|
||||
#include "nsEditorSpellCheck.h"
|
||||
|
||||
#include "nsStyleUtil.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsITextServicesDocument.h"
|
||||
#include "nsISpellChecker.h"
|
||||
#include "nsISelection.h"
|
||||
#include "nsIDOMRange.h"
|
||||
#include "nsIEditor.h"
|
||||
#include "nsIHTMLEditor.h"
|
||||
|
||||
#include "nsIComponentManager.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
@ -54,6 +58,7 @@
|
||||
#include "nsString.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsITextServicesFilter.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
@ -183,61 +188,9 @@ nsEditorSpellCheck::InitSpellChecker(nsIEditor* aEditor, PRBool aEnableSelection
|
||||
rv = mSpellChecker->SetDocument(tsDoc, PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Tell the spellchecker what dictionary to use:
|
||||
|
||||
nsAdoptingString dictName =
|
||||
Preferences::GetLocalizedString("spellchecker.dictionary");
|
||||
|
||||
if (dictName.IsEmpty())
|
||||
{
|
||||
// Prefs didn't give us a dictionary name, so just get the current
|
||||
// locale and use that as the default dictionary name!
|
||||
|
||||
nsCOMPtr<nsIXULChromeRegistry> packageRegistry =
|
||||
mozilla::services::GetXULChromeRegistryService();
|
||||
|
||||
if (packageRegistry) {
|
||||
nsCAutoString utf8DictName;
|
||||
rv = packageRegistry->GetSelectedLocale(NS_LITERAL_CSTRING("global"),
|
||||
utf8DictName);
|
||||
AppendUTF8toUTF16(utf8DictName, dictName);
|
||||
}
|
||||
}
|
||||
|
||||
PRBool setDictionary = PR_FALSE;
|
||||
if (NS_SUCCEEDED(rv) && !dictName.IsEmpty()) {
|
||||
rv = SetCurrentDictionary(dictName.get());
|
||||
|
||||
// fall back to "en-US" if the current locale doesn't have a dictionary.
|
||||
if (NS_FAILED(rv)) {
|
||||
rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US").get());
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
setDictionary = PR_TRUE;
|
||||
}
|
||||
|
||||
// If there was no dictionary specified by spellchecker.dictionary and setting it to the
|
||||
// locale dictionary didn't work, try to use the first dictionary we find. This helps when
|
||||
// the first dictionary is installed
|
||||
if (! setDictionary) {
|
||||
nsTArray<nsString> dictList;
|
||||
rv = mSpellChecker->GetDictionaryList(&dictList);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (dictList.Length() > 0) {
|
||||
rv = SetCurrentDictionary(dictList[0].get());
|
||||
if (NS_SUCCEEDED(rv))
|
||||
SaveDefaultDictionary();
|
||||
}
|
||||
}
|
||||
|
||||
// If an error was thrown while checking the dictionary pref, just
|
||||
// fail silently so that the spellchecker dialog is allowed to come
|
||||
// up. The user can manually reset the language to their choice on
|
||||
// the dialog if it is wrong.
|
||||
|
||||
DeleteSuggestedWordList();
|
||||
|
||||
// do not fail if UpdateCurrentDictionary fails because this method may
|
||||
// succeed later.
|
||||
UpdateCurrentDictionary(aEditor);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -439,14 +392,6 @@ nsEditorSpellCheck::UninitSpellChecker()
|
||||
{
|
||||
NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// we preserve the last selected language, but ignore errors so we continue
|
||||
// to uninitialize
|
||||
#ifdef DEBUG
|
||||
nsresult rv =
|
||||
#endif
|
||||
SaveDefaultDictionary();
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to set default dictionary");
|
||||
|
||||
// Cleanup - kill the spell checker
|
||||
DeleteSuggestedWordList();
|
||||
mDictionaryList.Clear();
|
||||
@ -489,3 +434,124 @@ nsEditorSpellCheck::DeleteSuggestedWordList()
|
||||
mSuggestedWordIndex = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditor* aEditor)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Tell the spellchecker what dictionary to use:
|
||||
nsAutoString dictName;
|
||||
|
||||
// First, try to get language with html5 algorithm
|
||||
nsAutoString editorLang;
|
||||
|
||||
nsCOMPtr<nsIContent> rootContent;
|
||||
|
||||
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(aEditor);
|
||||
if (htmlEditor) {
|
||||
rootContent = htmlEditor->GetActiveEditingHost();
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMElement> rootElement;
|
||||
rv = aEditor->GetRootElement(getter_AddRefs(rootElement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rootContent = do_QueryInterface(rootElement);
|
||||
}
|
||||
NS_ENSURE_TRUE(rootContent, NS_ERROR_FAILURE);
|
||||
|
||||
rootContent->GetLang(editorLang);
|
||||
|
||||
if (editorLang.IsEmpty()) {
|
||||
nsCOMPtr<nsIDocument> doc = rootContent->GetCurrentDoc();
|
||||
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
||||
doc->GetContentLanguage(editorLang);
|
||||
}
|
||||
|
||||
if (!editorLang.IsEmpty()) {
|
||||
dictName.Assign(editorLang);
|
||||
}
|
||||
|
||||
// otherwise, get language from preferences
|
||||
if (dictName.IsEmpty()) {
|
||||
dictName.Assign(Preferences::GetLocalizedString("spellchecker.dictionary"));
|
||||
}
|
||||
|
||||
if (dictName.IsEmpty())
|
||||
{
|
||||
// Prefs didn't give us a dictionary name, so just get the current
|
||||
// locale and use that as the default dictionary name!
|
||||
|
||||
nsCOMPtr<nsIXULChromeRegistry> packageRegistry =
|
||||
mozilla::services::GetXULChromeRegistryService();
|
||||
|
||||
if (packageRegistry) {
|
||||
nsCAutoString utf8DictName;
|
||||
rv = packageRegistry->GetSelectedLocale(NS_LITERAL_CSTRING("global"),
|
||||
utf8DictName);
|
||||
AppendUTF8toUTF16(utf8DictName, dictName);
|
||||
}
|
||||
}
|
||||
|
||||
SetCurrentDictionary(NS_LITERAL_STRING("").get());
|
||||
|
||||
if (NS_SUCCEEDED(rv) && !dictName.IsEmpty()) {
|
||||
rv = SetCurrentDictionary(dictName.get());
|
||||
if (NS_FAILED(rv)) {
|
||||
// required dictionary was not available. Try to get a dictionary
|
||||
// matching at least language part of dictName: If required dictionary is
|
||||
// "aa-bb", we try "aa", then we try any available dictionary aa-XX
|
||||
nsAutoString langCode;
|
||||
PRInt32 dashIdx = dictName.FindChar('-');
|
||||
if (dashIdx != -1) {
|
||||
langCode.Assign(Substring(dictName, 0, dashIdx));
|
||||
// try to use langCode
|
||||
rv = SetCurrentDictionary(langCode.get());
|
||||
} else {
|
||||
langCode.Assign(dictName);
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
// loop over avaible dictionaries; if we find one with required
|
||||
// language, use it
|
||||
nsTArray<nsString> dictList;
|
||||
rv = mSpellChecker->GetDictionaryList(&dictList);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsDefaultStringComparator comparator;
|
||||
PRInt32 i, count = dictList.Length();
|
||||
for (i = 0; i < count; i++) {
|
||||
nsAutoString dictStr(dictList.ElementAt(i));
|
||||
if (nsStyleUtil::DashMatchCompare(dictStr, langCode, comparator) &&
|
||||
NS_SUCCEEDED(SetCurrentDictionary(dictStr.get()))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have not set dictionary, and the editable element doesn't have a
|
||||
// lang attribute, we try to get a dictionary. First try, en-US. If it does
|
||||
// not work, pick the first one.
|
||||
if (editorLang.IsEmpty()) {
|
||||
nsAutoString currentDictonary;
|
||||
rv = mSpellChecker->GetCurrentDictionary(currentDictonary);
|
||||
if (NS_FAILED(rv) || currentDictonary.IsEmpty()) {
|
||||
rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US").get());
|
||||
if (NS_FAILED(rv)) {
|
||||
nsTArray<nsString> dictList;
|
||||
rv = mSpellChecker->GetDictionaryList(&dictList);
|
||||
if (NS_SUCCEEDED(rv) && dictList.Length() > 0) {
|
||||
SetCurrentDictionary(dictList[0].get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If an error was thrown while setting the dictionary, just
|
||||
// fail silently so that the spellchecker dialog is allowed to come
|
||||
// up. The user can manually reset the language to their choice on
|
||||
// the dialog if it is wrong.
|
||||
|
||||
DeleteSuggestedWordList();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@
|
||||
interface nsIEditor;
|
||||
interface nsITextServicesFilter;
|
||||
|
||||
[scriptable, uuid(90c93610-c116-44ab-9793-62dccb9f43ce)]
|
||||
[scriptable, uuid(803ff0dd-07f2-4438-b3a6-ab9c2fe4e1dd)]
|
||||
interface nsIEditorSpellCheck : nsISupports
|
||||
{
|
||||
|
||||
@ -188,4 +188,10 @@ interface nsIEditorSpellCheck : nsISupports
|
||||
*/
|
||||
boolean CheckCurrentWordNoSuggest(in wstring suggestedWord);
|
||||
|
||||
/**
|
||||
* Update the dictionary in use to be sure it corresponds to what the editor
|
||||
* needs.
|
||||
*/
|
||||
void UpdateCurrentDictionary(in nsIEditor editor);
|
||||
|
||||
};
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "domstubs.idl"
|
||||
|
||||
interface nsIAtom;
|
||||
interface nsIContent;
|
||||
interface nsISupportsArray;
|
||||
interface nsISelection;
|
||||
interface nsIContentFilter;
|
||||
@ -51,7 +52,7 @@ interface nsIContentFilter;
|
||||
NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_EDITOR, 1)
|
||||
|
||||
%}
|
||||
[scriptable, uuid(c964b8b0-e9e8-11df-9492-0800200c9a66)]
|
||||
[scriptable, uuid(d58f35a7-c269-4292-b9aa-a79e200a7c99)]
|
||||
|
||||
interface nsIHTMLEditor : nsISupports
|
||||
{
|
||||
@ -613,5 +614,11 @@ interface nsIHTMLEditor : nsISupports
|
||||
* Checks whether a BR node is visible to the user.
|
||||
*/
|
||||
boolean breakIsVisible(in nsIDOMNode aNode);
|
||||
|
||||
/**
|
||||
* Get an active editor's editing host in DOM window. If this editor isn't
|
||||
* active in the DOM window, this returns NULL.
|
||||
*/
|
||||
[noscript, notxpcom] nsIContent GetActiveEditingHost();
|
||||
};
|
||||
|
||||
|
@ -5311,3 +5311,12 @@ nsEditor::BeginKeypressHandling(nsIDOMNSEvent* aEvent)
|
||||
mLastKeypressEventWasTrusted = isTrusted ? eTriTrue : eTriFalse;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsEditor::OnFocus(nsIDOMEventTarget* aFocusEventTarget)
|
||||
{
|
||||
InitializeSelection(aFocusEventTarget);
|
||||
if (mInlineSpellChecker) {
|
||||
mInlineSpellChecker->UpdateCurrentDictionary();
|
||||
}
|
||||
}
|
||||
|
@ -719,6 +719,11 @@ public:
|
||||
// nothing.
|
||||
nsresult InitializeSelection(nsIDOMEventTarget* aFocusEventTarget);
|
||||
|
||||
// This method has to be called by nsEditorEventListener::Focus.
|
||||
// All actions that have to be done when the editor is focused needs to be
|
||||
// added here.
|
||||
void OnFocus(nsIDOMEventTarget* aFocusEventTarget);
|
||||
|
||||
protected:
|
||||
|
||||
PRUint32 mModCount; // number of modifications (for undo/redo stack)
|
||||
|
@ -818,7 +818,7 @@ nsEditorEventListener::Focus(nsIDOMEvent* aEvent)
|
||||
}
|
||||
}
|
||||
|
||||
mEditor->InitializeSelection(target);
|
||||
mEditor->OnFocus(target);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -185,27 +185,26 @@ mDocChangeRange(nsnull)
|
||||
,mUtilRange(nsnull)
|
||||
,mJoinOffset(0)
|
||||
{
|
||||
nsString emptyString;
|
||||
// populate mCachedStyles
|
||||
mCachedStyles[0] = StyleCache(nsEditProperty::b, emptyString, emptyString);
|
||||
mCachedStyles[1] = StyleCache(nsEditProperty::i, emptyString, emptyString);
|
||||
mCachedStyles[2] = StyleCache(nsEditProperty::u, emptyString, emptyString);
|
||||
mCachedStyles[3] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("face"), emptyString);
|
||||
mCachedStyles[4] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("size"), emptyString);
|
||||
mCachedStyles[5] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("color"), emptyString);
|
||||
mCachedStyles[6] = StyleCache(nsEditProperty::tt, emptyString, emptyString);
|
||||
mCachedStyles[7] = StyleCache(nsEditProperty::em, emptyString, emptyString);
|
||||
mCachedStyles[8] = StyleCache(nsEditProperty::strong, emptyString, emptyString);
|
||||
mCachedStyles[9] = StyleCache(nsEditProperty::dfn, emptyString, emptyString);
|
||||
mCachedStyles[10] = StyleCache(nsEditProperty::code, emptyString, emptyString);
|
||||
mCachedStyles[11] = StyleCache(nsEditProperty::samp, emptyString, emptyString);
|
||||
mCachedStyles[12] = StyleCache(nsEditProperty::var, emptyString, emptyString);
|
||||
mCachedStyles[13] = StyleCache(nsEditProperty::cite, emptyString, emptyString);
|
||||
mCachedStyles[14] = StyleCache(nsEditProperty::abbr, emptyString, emptyString);
|
||||
mCachedStyles[15] = StyleCache(nsEditProperty::acronym, emptyString, emptyString);
|
||||
mCachedStyles[16] = StyleCache(nsEditProperty::cssBackgroundColor, emptyString, emptyString);
|
||||
mCachedStyles[17] = StyleCache(nsEditProperty::sub, emptyString, emptyString);
|
||||
mCachedStyles[18] = StyleCache(nsEditProperty::sup, emptyString, emptyString);
|
||||
mCachedStyles[0] = StyleCache(nsEditProperty::b, EmptyString(), EmptyString());
|
||||
mCachedStyles[1] = StyleCache(nsEditProperty::i, EmptyString(), EmptyString());
|
||||
mCachedStyles[2] = StyleCache(nsEditProperty::u, EmptyString(), EmptyString());
|
||||
mCachedStyles[3] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("face"), EmptyString());
|
||||
mCachedStyles[4] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("size"), EmptyString());
|
||||
mCachedStyles[5] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("color"), EmptyString());
|
||||
mCachedStyles[6] = StyleCache(nsEditProperty::tt, EmptyString(), EmptyString());
|
||||
mCachedStyles[7] = StyleCache(nsEditProperty::em, EmptyString(), EmptyString());
|
||||
mCachedStyles[8] = StyleCache(nsEditProperty::strong, EmptyString(), EmptyString());
|
||||
mCachedStyles[9] = StyleCache(nsEditProperty::dfn, EmptyString(), EmptyString());
|
||||
mCachedStyles[10] = StyleCache(nsEditProperty::code, EmptyString(), EmptyString());
|
||||
mCachedStyles[11] = StyleCache(nsEditProperty::samp, EmptyString(), EmptyString());
|
||||
mCachedStyles[12] = StyleCache(nsEditProperty::var, EmptyString(), EmptyString());
|
||||
mCachedStyles[13] = StyleCache(nsEditProperty::cite, EmptyString(), EmptyString());
|
||||
mCachedStyles[14] = StyleCache(nsEditProperty::abbr, EmptyString(), EmptyString());
|
||||
mCachedStyles[15] = StyleCache(nsEditProperty::acronym, EmptyString(), EmptyString());
|
||||
mCachedStyles[16] = StyleCache(nsEditProperty::cssBackgroundColor, EmptyString(), EmptyString());
|
||||
mCachedStyles[17] = StyleCache(nsEditProperty::sub, EmptyString(), EmptyString());
|
||||
mCachedStyles[18] = StyleCache(nsEditProperty::sup, EmptyString(), EmptyString());
|
||||
}
|
||||
|
||||
nsHTMLEditRules::~nsHTMLEditRules()
|
||||
|
@ -458,9 +458,6 @@ protected:
|
||||
// @return If the editor has focus, this returns the focused node.
|
||||
// Otherwise, returns null.
|
||||
already_AddRefed<nsINode> GetFocusedNode();
|
||||
// Get an active editor's editing host in DOM window. If this editor isn't
|
||||
// active in the DOM window, this returns NULL.
|
||||
nsIContent* GetActiveEditingHost();
|
||||
|
||||
// Return TRUE if aElement is a table-related elemet and caret was set
|
||||
PRBool SetCaretInTableCell(nsIDOMElement* aElement);
|
||||
|
@ -45,7 +45,6 @@ function append(str) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
var edit = document.getElementById("edit");
|
||||
edit.focus();
|
||||
var editor = getEditor();
|
||||
var sel = editor.selection;
|
||||
sel.selectAllChildren(edit);
|
||||
@ -58,16 +57,19 @@ function append(str) {
|
||||
|
||||
function runTest() {
|
||||
gMisspeltWords = ["haz", "cheezburger"];
|
||||
is(isSpellingCheckOk(), true, "All misspellings before editing are accounted for.");
|
||||
|
||||
var edit = document.getElementById("edit");
|
||||
append(" becaz I'm a lolcat!");
|
||||
edit.focus();
|
||||
SimpleTest.executeSoon(function() {
|
||||
gMisspeltWords.push("becaz");
|
||||
gMisspeltWords.push("lolcat");
|
||||
is(isSpellingCheckOk(), true, "All misspellings after typing are accounted for.");
|
||||
is(isSpellingCheckOk(), true, "All misspellings before editing are accounted for.");
|
||||
|
||||
SimpleTest.finish();
|
||||
append(" becaz I'm a lolcat!");
|
||||
SimpleTest.executeSoon(function() {
|
||||
gMisspeltWords.push("becaz");
|
||||
gMisspeltWords.push("lolcat");
|
||||
is(isSpellingCheckOk(), true, "All misspellings after typing are accounted for.");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -221,11 +221,16 @@ nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
|
||||
nsnull, 0, nsnull, 0);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// if only trailing <br> remaining remove it
|
||||
res = RemoveRedundantTrailingBR();
|
||||
if (NS_FAILED(res))
|
||||
return res;
|
||||
|
||||
// detect empty doc
|
||||
res = CreateBogusNodeIfNeeded(selection);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// insure trailing br node
|
||||
// ensure trailing br node
|
||||
res = CreateTrailingBRIfNeeded();
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
@ -1045,6 +1050,66 @@ nsTextEditRules::DidOutputText(nsISelection *aSelection, nsresult aResult)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTextEditRules::RemoveRedundantTrailingBR()
|
||||
{
|
||||
// If the bogus node exists, we have no work to do
|
||||
if (mBogusNode)
|
||||
return NS_OK;
|
||||
|
||||
// Likewise, nothing to be done if we could never have inserted a trailing br
|
||||
if (IsSingleLineEditor())
|
||||
return NS_OK;
|
||||
|
||||
nsIDOMNode* body = mEditor->GetRoot();
|
||||
if (!body)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
PRBool hasChildren;
|
||||
nsresult res = body->HasChildNodes(&hasChildren);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
if (hasChildren) {
|
||||
nsCOMPtr<nsIDOMNodeList> childList;
|
||||
res = body->GetChildNodes(getter_AddRefs(childList));
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
if (!childList)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
PRUint32 childCount;
|
||||
res = childList->GetLength(&childCount);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// The trailing br is redundant if it is the only remaining child node
|
||||
if (childCount != 1)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> child;
|
||||
res = body->GetFirstChild(getter_AddRefs(child));
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
if (nsTextEditUtils::IsMozBR(child)) {
|
||||
// Rather than deleting this node from the DOM tree we should instead
|
||||
// morph this br into the bogus node
|
||||
nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(child);
|
||||
if (elem) {
|
||||
elem->RemoveAttribute(NS_LITERAL_STRING("type"));
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// set mBogusNode to be this <br>
|
||||
mBogusNode = elem;
|
||||
|
||||
// give it the bogus node attribute
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(elem);
|
||||
content->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
|
||||
kMOZEditorBogusNodeValue, PR_FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTextEditRules::CreateTrailingBRIfNeeded()
|
||||
{
|
||||
|
@ -205,6 +205,9 @@ protected:
|
||||
|
||||
|
||||
// helper functions
|
||||
|
||||
/** check for and replace a redundant trailing break */
|
||||
nsresult RemoveRedundantTrailingBR();
|
||||
|
||||
/** creates a trailing break in the text doc if there is not one already */
|
||||
nsresult CreateTrailingBRIfNeeded();
|
||||
|
@ -73,6 +73,7 @@ _TEST_FILES += \
|
||||
endif
|
||||
|
||||
_CHROME_TEST_FILES = \
|
||||
test_bug483651.html \
|
||||
test_bug636465.xul \
|
||||
$(NULL)
|
||||
|
||||
|
92
editor/libeditor/text/tests/test_bug483651.html
Normal file
92
editor/libeditor/text/tests/test_bug483651.html
Normal file
@ -0,0 +1,92 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!-- ***** BEGIN LICENSE BLOCK *****
|
||||
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
-
|
||||
- The contents of this file are subject to the Mozilla Public License Version
|
||||
- 1.1 (the "License"); you may not use this file except in compliance with
|
||||
- the License. You may obtain a copy of the License at
|
||||
- http://www.mozilla.org/MPL/
|
||||
-
|
||||
- Software distributed under the License is distributed on an "AS IS" basis,
|
||||
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
- for the specific language governing rights and limitations under the
|
||||
- License.
|
||||
-
|
||||
- The Original Code is Plaintext Editor Test code
|
||||
-
|
||||
- The Initial Developer of the Original Code is
|
||||
- Graeme McCutcheon <graememcc_firefox@graeme-online.co.uk>.
|
||||
- Portions created by the Initial Developer are Copyright (C) 2009
|
||||
- the Initial Developer. All Rights Reserved.
|
||||
-
|
||||
- Contributor(s):
|
||||
-
|
||||
-
|
||||
- Alternatively, the contents of this file may be used under the terms of
|
||||
- either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
- in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
- of those above. If you wish to allow use of your version of this file only
|
||||
- under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
- use your version of this file under the terms of the MPL, indicate your
|
||||
- decision by deleting the provisions above and replace them with the notice
|
||||
- and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
- the provisions above, a recipient may use your version of this file under
|
||||
- the terms of any one of the MPL, the GPL or the LGPL.
|
||||
-
|
||||
- ***** END LICENSE BLOCK ***** -->
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=483651
|
||||
-->
|
||||
|
||||
<head>
|
||||
<title>Test for Bug 483651</title>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
|
||||
<body onload="doTest();">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=483651">Mozilla Bug 483651</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 483651 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function doTest() {
|
||||
var t1 = $("t1");
|
||||
var editor = null;
|
||||
|
||||
if (t1 instanceof Components.interfaces.nsIDOMNSEditableElement)
|
||||
editor = t1.editor;
|
||||
|
||||
ok(editor, "able to get editor for the element");
|
||||
t1.focus();
|
||||
synthesizeKey("A", {});
|
||||
synthesizeKey("VK_BACK_SPACE", {});
|
||||
|
||||
try {
|
||||
// Was the trailing br removed?
|
||||
is(editor.documentIsEmpty, true, "trailing <br> correctly removed");
|
||||
} catch (e) {
|
||||
ok(false, "test failed with error "+e);
|
||||
}
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
<textarea id="t1" rows="2" columns="80"></textarea>
|
||||
</body>
|
||||
</html>
|
@ -43,7 +43,7 @@ interface nsISelection;
|
||||
interface nsIEditor;
|
||||
interface nsIEditorSpellCheck;
|
||||
|
||||
[scriptable, uuid(07be036a-2355-4a92-b150-5c9b7e9fdf2f)]
|
||||
[scriptable, uuid(f456dda1-965d-470c-8c55-e51b38e45212)]
|
||||
|
||||
interface nsIInlineSpellChecker : nsISupports
|
||||
{
|
||||
@ -71,6 +71,7 @@ interface nsIInlineSpellChecker : nsISupports
|
||||
|
||||
void ignoreWord(in AString aWord);
|
||||
void ignoreWords([array, size_is(aCount)] in wstring aWordsToIgnore, in unsigned long aCount);
|
||||
void updateCurrentDictionary();
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
@ -142,7 +142,8 @@ public:
|
||||
/**
|
||||
* Tells the spellchecker to use a specific dictionary.
|
||||
* @param aDictionary a string that is in the list returned
|
||||
* by GetDictionaryList().
|
||||
* by GetDictionaryList() or an empty string. If aDictionary is
|
||||
* empty string, spellchecker will be disabled.
|
||||
*/
|
||||
NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary) = 0;
|
||||
};
|
||||
|
@ -1384,6 +1384,9 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||
PRBool isMisspelled;
|
||||
aWordUtil.NormalizeWord(wordText);
|
||||
rv = mSpellCheck->CheckCurrentWordNoSuggest(wordText.get(), &isMisspelled);
|
||||
if (NS_FAILED(rv))
|
||||
continue;
|
||||
|
||||
if (isMisspelled) {
|
||||
// misspelled words count extra toward the max
|
||||
wordsSinceTimeCheck += MISSPELLED_WORD_COUNT_PENALTY;
|
||||
@ -1441,6 +1444,23 @@ mozInlineSpellChecker::ResumeCheck(mozInlineSpellStatus* aStatus)
|
||||
nsCOMPtr<nsISelection> spellCheckSelection;
|
||||
rv = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUnichar *currentDictionary = nsnull;
|
||||
rv = mSpellCheck->GetCurrentDictionary(¤tDictionary);
|
||||
if (NS_FAILED(rv)) {
|
||||
// no active dictionary
|
||||
PRInt32 count;
|
||||
spellCheckSelection->GetRangeCount(&count);
|
||||
for (PRInt32 index = count - 1; index >= 0; index--) {
|
||||
nsCOMPtr<nsIDOMRange> checkRange;
|
||||
spellCheckSelection->GetRangeAt(index, getter_AddRefs(checkRange));
|
||||
if (checkRange) {
|
||||
RemoveRange(spellCheckSelection, checkRange);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
CleanupRangesInSelection(spellCheckSelection);
|
||||
|
||||
rv = aStatus->FinishInitOnEvent(wordUtil);
|
||||
@ -1733,3 +1753,38 @@ nsresult mozInlineSpellChecker::KeyPress(nsIDOMEvent* aKeyEvent)
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP mozInlineSpellChecker::UpdateCurrentDictionary()
|
||||
{
|
||||
if (!mSpellCheck) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRUnichar *previousDictionary = nsnull;
|
||||
nsDependentString previousDictionaryStr;
|
||||
if (NS_SUCCEEDED(mSpellCheck->GetCurrentDictionary(&previousDictionary))) {
|
||||
previousDictionaryStr.Assign(previousDictionary);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
|
||||
nsresult rv = mSpellCheck->UpdateCurrentDictionary(editor);
|
||||
|
||||
PRUnichar *currentDictionary = nsnull;
|
||||
nsDependentString currentDictionaryStr;
|
||||
if (NS_SUCCEEDED(mSpellCheck->GetCurrentDictionary(¤tDictionary))) {
|
||||
currentDictionaryStr.Assign(currentDictionary);
|
||||
}
|
||||
|
||||
if (!previousDictionary || !currentDictionary || !previousDictionaryStr.Equals(currentDictionaryStr)) {
|
||||
rv = SpellCheckRange(nsnull);
|
||||
}
|
||||
|
||||
if (previousDictionary) {
|
||||
nsMemory::Free(previousDictionary);
|
||||
}
|
||||
if (currentDictionary) {
|
||||
nsMemory::Free(currentDictionary);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -369,6 +369,12 @@ mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary)
|
||||
nsresult rv;
|
||||
nsCString *contractId;
|
||||
|
||||
if (aDictionary.IsEmpty()) {
|
||||
mCurrentEngineContractId = nsnull;
|
||||
mSpellCheckingEngine = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mDictionariesMap.Get(aDictionary, &contractId)){
|
||||
NS_WARNING("Dictionary not found");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
@ -44,7 +44,7 @@ struct {
|
||||
{ HB_TAG('c','u','r','s'), DEFAULT_PRIORITY },
|
||||
{ HB_TAG('k','e','r','n'), DEFAULT_PRIORITY },
|
||||
{ HB_TAG('l','i','g','a'), DEFAULT_PRIORITY },
|
||||
{ HB_TAG('l','o','c','l'), DEFAULT_PRIORITY },
|
||||
{ HB_TAG('l','o','c','l'), FIRST_PRIORITY },
|
||||
{ HB_TAG('m','a','r','k'), DEFAULT_PRIORITY },
|
||||
{ HB_TAG('m','k','m','k'), DEFAULT_PRIORITY },
|
||||
{ HB_TAG('r','l','i','g'), DEFAULT_PRIORITY }
|
||||
|
@ -94,7 +94,7 @@ inline nscoord NSToCoordRound(float aValue)
|
||||
#if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__)
|
||||
return NS_lroundup30(aValue);
|
||||
#else
|
||||
return nscoord(NS_floorf(aValue + 0.5f));
|
||||
return nscoord(floorf(aValue + 0.5f));
|
||||
#endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
|
||||
}
|
||||
|
||||
@ -103,7 +103,7 @@ inline nscoord NSToCoordRound(double aValue)
|
||||
#if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__)
|
||||
return NS_lroundup30((float)aValue);
|
||||
#else
|
||||
return nscoord(NS_floor(aValue + 0.5f));
|
||||
return nscoord(floor(aValue + 0.5f));
|
||||
#endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */
|
||||
}
|
||||
|
||||
@ -360,12 +360,12 @@ inline float NSCoordToFloat(nscoord aCoord) {
|
||||
*/
|
||||
inline nscoord NSToCoordFloor(float aValue)
|
||||
{
|
||||
return nscoord(NS_floorf(aValue));
|
||||
return nscoord(floorf(aValue));
|
||||
}
|
||||
|
||||
inline nscoord NSToCoordFloor(double aValue)
|
||||
{
|
||||
return nscoord(NS_floor(aValue));
|
||||
return nscoord(floor(aValue));
|
||||
}
|
||||
|
||||
inline nscoord NSToCoordFloorClamped(float aValue)
|
||||
@ -388,12 +388,12 @@ inline nscoord NSToCoordFloorClamped(float aValue)
|
||||
|
||||
inline nscoord NSToCoordCeil(float aValue)
|
||||
{
|
||||
return nscoord(NS_ceilf(aValue));
|
||||
return nscoord(ceilf(aValue));
|
||||
}
|
||||
|
||||
inline nscoord NSToCoordCeil(double aValue)
|
||||
{
|
||||
return nscoord(NS_ceil(aValue));
|
||||
return nscoord(ceil(aValue));
|
||||
}
|
||||
|
||||
inline nscoord NSToCoordCeilClamped(float aValue)
|
||||
@ -437,12 +437,12 @@ inline nscoord NSToCoordCeilClamped(double aValue)
|
||||
*/
|
||||
inline PRInt32 NSToIntFloor(float aValue)
|
||||
{
|
||||
return PRInt32(NS_floorf(aValue));
|
||||
return PRInt32(floorf(aValue));
|
||||
}
|
||||
|
||||
inline PRInt32 NSToIntCeil(float aValue)
|
||||
{
|
||||
return PRInt32(NS_ceilf(aValue));
|
||||
return PRInt32(ceilf(aValue));
|
||||
}
|
||||
|
||||
inline PRInt32 NSToIntRound(float aValue)
|
||||
@ -457,12 +457,12 @@ inline PRInt32 NSToIntRound(double aValue)
|
||||
|
||||
inline PRInt32 NSToIntRoundUp(float aValue)
|
||||
{
|
||||
return PRInt32(NS_floorf(aValue + 0.5f));
|
||||
return PRInt32(floorf(aValue + 0.5f));
|
||||
}
|
||||
|
||||
inline PRInt32 NSToIntRoundUp(double aValue)
|
||||
{
|
||||
return PRInt32(NS_floor(aValue + 0.5));
|
||||
return PRInt32(floor(aValue + 0.5));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -147,7 +147,7 @@ nsFontMetrics::Destroy()
|
||||
|
||||
// XXXTODO get rid of this macro
|
||||
#define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5)
|
||||
#define CEIL_TO_TWIPS(x) (nscoord)NS_ceil((x) * mP2A)
|
||||
#define CEIL_TO_TWIPS(x) (nscoord)ceil((x) * mP2A)
|
||||
|
||||
const gfxFont::Metrics& nsFontMetrics::GetMetrics() const
|
||||
{
|
||||
@ -194,15 +194,15 @@ nsFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize)
|
||||
static gfxFloat ComputeMaxDescent(const gfxFont::Metrics& aMetrics,
|
||||
gfxFontGroup* aFontGroup)
|
||||
{
|
||||
gfxFloat offset = NS_floor(-aFontGroup->GetUnderlineOffset() + 0.5);
|
||||
gfxFloat offset = floor(-aFontGroup->GetUnderlineOffset() + 0.5);
|
||||
gfxFloat size = NS_round(aMetrics.underlineSize);
|
||||
gfxFloat minDescent = NS_floor(offset + size + 0.5);
|
||||
gfxFloat minDescent = floor(offset + size + 0.5);
|
||||
return NS_MAX(minDescent, aMetrics.maxDescent);
|
||||
}
|
||||
|
||||
static gfxFloat ComputeMaxAscent(const gfxFont::Metrics& aMetrics)
|
||||
{
|
||||
return NS_floor(aMetrics.maxAscent + 0.5);
|
||||
return floor(aMetrics.maxAscent + 0.5);
|
||||
}
|
||||
|
||||
nscoord
|
||||
|
@ -114,7 +114,7 @@ gfxCachedTempSurface::Get(gfxASurface::gfxContentType aContentType,
|
||||
|
||||
PRBool cleared = PR_FALSE;
|
||||
if (!mSurface) {
|
||||
mSize = gfxIntSize(PRInt32(NS_ceil(aRect.width)), PRInt32(NS_ceil(aRect.height)));
|
||||
mSize = gfxIntSize(PRInt32(ceil(aRect.width)), PRInt32(ceil(aRect.height)));
|
||||
mSurface = aSimilarTo->CreateSimilarSurface(aContentType, mSize);
|
||||
if (!mSurface)
|
||||
return nsnull;
|
||||
|
@ -70,11 +70,11 @@ ScaleRoundDesignUnits(FT_Short aDesignMetric, FT_Fixed aScale)
|
||||
static void
|
||||
SnapLineToPixels(gfxFloat& aOffset, gfxFloat& aSize)
|
||||
{
|
||||
gfxFloat snappedSize = NS_MAX(NS_floor(aSize + 0.5), 1.0);
|
||||
gfxFloat snappedSize = NS_MAX(floor(aSize + 0.5), 1.0);
|
||||
// Correct offset for change in size
|
||||
gfxFloat offset = aOffset - 0.5 * (aSize - snappedSize);
|
||||
// Snap offset
|
||||
aOffset = NS_floor(offset + 0.5);
|
||||
aOffset = floor(offset + 0.5);
|
||||
aSize = snappedSize;
|
||||
}
|
||||
|
||||
@ -281,16 +281,16 @@ gfxFT2LockedFace::GetMetrics(gfxFont::Metrics* aMetrics,
|
||||
// internalLeading + externalLeading, but first each of these is rounded
|
||||
// to layout units. To ensure that the result is an integer number of
|
||||
// pixels, round each of the components to pixels.
|
||||
aMetrics->emHeight = NS_floor(emHeight + 0.5);
|
||||
aMetrics->emHeight = floor(emHeight + 0.5);
|
||||
|
||||
// maxHeight will normally be an integer, but round anyway in case
|
||||
// FreeType is configured differently.
|
||||
aMetrics->internalLeading =
|
||||
NS_floor(aMetrics->maxHeight - aMetrics->emHeight + 0.5);
|
||||
floor(aMetrics->maxHeight - aMetrics->emHeight + 0.5);
|
||||
|
||||
// Text input boxes currently don't work well with lineHeight
|
||||
// significantly less than maxHeight (with Verdana, for example).
|
||||
lineHeight = NS_floor(NS_MAX(lineHeight, aMetrics->maxHeight) + 0.5);
|
||||
lineHeight = floor(NS_MAX(lineHeight, aMetrics->maxHeight) + 0.5);
|
||||
aMetrics->externalLeading =
|
||||
lineHeight - aMetrics->internalLeading - aMetrics->emHeight;
|
||||
|
||||
|
@ -651,15 +651,12 @@ void gfxFontFamily::LocalizedName(nsAString& aLocalizedName)
|
||||
void
|
||||
gfxFontFamily::FindFontForChar(FontSearch *aMatchData)
|
||||
{
|
||||
if (!mHasStyles) {
|
||||
if (!mHasStyles)
|
||||
FindStyleVariations();
|
||||
}
|
||||
|
||||
if (!TestCharacterMap(aMatchData->mCh)) {
|
||||
// none of the faces in the family support the required char,
|
||||
// so bail out immediately
|
||||
return;
|
||||
}
|
||||
// xxx - optimization point - keep a bit vector with the union of supported unicode ranges
|
||||
// by all fonts for this family and bail immediately if the character is not in any of
|
||||
// this family's cmaps
|
||||
|
||||
// iterate over fonts
|
||||
PRUint32 numFonts = mAvailableFonts.Length();
|
||||
@ -1594,7 +1591,7 @@ gfxFont::SetupGlyphExtents(gfxContext *aContext, PRUint32 aGlyphID, PRBool aNeed
|
||||
extents.y_bearing >= -fontMetrics.maxAscent &&
|
||||
extents.height + extents.y_bearing <= fontMetrics.maxDescent) {
|
||||
PRUint32 appUnitsWidth =
|
||||
PRUint32(NS_ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit));
|
||||
PRUint32(ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit));
|
||||
if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
|
||||
aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, PRUint16(appUnitsWidth));
|
||||
return;
|
||||
@ -1713,9 +1710,9 @@ RoundToNearestMultiple(double aValue, double aFraction)
|
||||
void gfxFont::CalculateDerivedMetrics(Metrics& aMetrics)
|
||||
{
|
||||
aMetrics.maxAscent =
|
||||
NS_ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1/1024.0));
|
||||
ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1/1024.0));
|
||||
aMetrics.maxDescent =
|
||||
NS_ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1/1024.0));
|
||||
ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1/1024.0));
|
||||
|
||||
if (aMetrics.xHeight <= 0) {
|
||||
// only happens if we couldn't find either font metrics
|
||||
@ -1828,13 +1825,13 @@ gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, PRBool aIsBadUnderlineFont)
|
||||
// If strikeout line is overflowed from the ascent, the line should be resized and moved for
|
||||
// that being in the ascent space.
|
||||
// Note that the strikeoutOffset is *middle* of the strikeout line position.
|
||||
gfxFloat halfOfStrikeoutSize = NS_floor(aMetrics->strikeoutSize / 2.0 + 0.5);
|
||||
gfxFloat halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
|
||||
if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) {
|
||||
if (aMetrics->strikeoutSize > aMetrics->maxAscent) {
|
||||
aMetrics->strikeoutSize = NS_MAX(aMetrics->maxAscent, 1.0);
|
||||
halfOfStrikeoutSize = NS_floor(aMetrics->strikeoutSize / 2.0 + 0.5);
|
||||
halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
|
||||
}
|
||||
gfxFloat ascent = NS_floor(aMetrics->maxAscent + 0.5);
|
||||
gfxFloat ascent = floor(aMetrics->maxAscent + 0.5);
|
||||
aMetrics->strikeoutOffset = NS_MAX(halfOfStrikeoutSize, ascent / 2.0);
|
||||
}
|
||||
|
||||
@ -2544,7 +2541,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
|
||||
gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
|
||||
if (wid >= 0.0) {
|
||||
nscoord advance =
|
||||
aTextRun->GetAppUnitsPerDevUnit() * NS_floor(wid + 0.5);
|
||||
aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
|
||||
gfxTextRun::CompressedGlyph g;
|
||||
if (gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance)) {
|
||||
aTextRun->SetSimpleGlyph(index,
|
||||
@ -2614,8 +2611,11 @@ gfxFontGroup::FindFontForChar(PRUint32 aCh, PRUint32 aPrevCh,
|
||||
return font.forget();
|
||||
}
|
||||
// check other faces of the family
|
||||
// XXX optimization point: give the family a charmap that is the union
|
||||
// of the char maps of all its faces, so we can quickly test whether
|
||||
// it's worth doing this search
|
||||
gfxFontFamily *family = font->GetFontEntry()->Family();
|
||||
if (family && family->TestCharacterMap(aCh)) {
|
||||
if (family) {
|
||||
FontSearch matchData(aCh, font);
|
||||
family->FindFontForChar(&matchData);
|
||||
gfxFontEntry *fe = matchData.mBestMatch;
|
||||
|
@ -482,8 +482,7 @@ public:
|
||||
mFaceNamesInitialized(PR_FALSE),
|
||||
mHasStyles(PR_FALSE),
|
||||
mIsSimpleFamily(PR_FALSE),
|
||||
mIsBadUnderlineFamily(PR_FALSE),
|
||||
mCharacterMapInitialized(PR_FALSE)
|
||||
mIsBadUnderlineFamily(PR_FALSE)
|
||||
{ }
|
||||
|
||||
virtual ~gfxFontFamily() { }
|
||||
@ -545,28 +544,10 @@ public:
|
||||
// read in cmaps for all the faces
|
||||
void ReadCMAP() {
|
||||
PRUint32 i, numFonts = mAvailableFonts.Length();
|
||||
for (i = 0; i < numFonts; i++) {
|
||||
gfxFontEntry *fe = mAvailableFonts[i];
|
||||
if (!fe) {
|
||||
continue;
|
||||
}
|
||||
fe->ReadCMAP();
|
||||
mCharacterMap.Union(fe->mCharacterMap);
|
||||
}
|
||||
mCharacterMap.Compact();
|
||||
mCharacterMapInitialized = PR_TRUE;
|
||||
}
|
||||
|
||||
PRBool TestCharacterMap(PRUint32 aCh) {
|
||||
if (!mCharacterMapInitialized) {
|
||||
ReadCMAP();
|
||||
}
|
||||
return mCharacterMap.test(aCh);
|
||||
}
|
||||
|
||||
void ResetCharacterMap() {
|
||||
mCharacterMap.reset();
|
||||
mCharacterMapInitialized = PR_FALSE;
|
||||
// called from RunLoader BEFORE CheckForSimpleFamily so that there cannot
|
||||
// be any NULL entries in mAvailableFonts
|
||||
for (i = 0; i < numFonts; i++)
|
||||
mAvailableFonts[i]->ReadCMAP();
|
||||
}
|
||||
|
||||
// mark this family as being in the "bad" underline offset blacklist
|
||||
@ -609,14 +590,12 @@ protected:
|
||||
|
||||
nsString mName;
|
||||
nsTArray<nsRefPtr<gfxFontEntry> > mAvailableFonts;
|
||||
gfxSparseBitSet mCharacterMap;
|
||||
PRPackedBool mOtherFamilyNamesInitialized;
|
||||
PRPackedBool mHasOtherFamilyNames;
|
||||
PRPackedBool mFaceNamesInitialized;
|
||||
PRPackedBool mHasStyles;
|
||||
PRPackedBool mIsSimpleFamily;
|
||||
PRPackedBool mIsBadUnderlineFamily;
|
||||
PRPackedBool mCharacterMapInitialized;
|
||||
|
||||
enum {
|
||||
// for "simple" families, the faces are stored in mAvailableFonts
|
||||
|
@ -313,7 +313,7 @@ gfxFontUtils::ReadCMAPTableFormat12(const PRUint8 *aBuf, PRUint32 aLength,
|
||||
prevEndCharCode = endCharCode;
|
||||
}
|
||||
|
||||
aCharacterMap.Compact();
|
||||
aCharacterMap.mBlocks.Compact();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -391,7 +391,7 @@ gfxFontUtils::ReadCMAPTableFormat4(const PRUint8 *aBuf, PRUint32 aLength,
|
||||
}
|
||||
}
|
||||
|
||||
aCharacterMap.Compact();
|
||||
aCharacterMap.mBlocks.Compact();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -165,6 +165,8 @@ public:
|
||||
Block *block = mBlocks[blockIndex];
|
||||
if (!block) {
|
||||
block = new Block;
|
||||
if (NS_UNLIKELY(!block)) // OOM
|
||||
return;
|
||||
mBlocks[blockIndex] = block;
|
||||
}
|
||||
block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7);
|
||||
@ -199,6 +201,9 @@ public:
|
||||
fullBlock = PR_TRUE;
|
||||
|
||||
block = new Block(fullBlock ? 0xFF : 0);
|
||||
|
||||
if (NS_UNLIKELY(!block)) // OOM
|
||||
return;
|
||||
mBlocks[i] = block;
|
||||
|
||||
if (fullBlock)
|
||||
@ -274,44 +279,7 @@ public:
|
||||
for (i = 0; i < mBlocks.Length(); i++)
|
||||
mBlocks[i] = nsnull;
|
||||
}
|
||||
|
||||
// set this bitset to the union of its current contents and another
|
||||
void Union(const gfxSparseBitSet& aBitset) {
|
||||
// ensure mBlocks is large enough
|
||||
PRUint32 blockCount = aBitset.mBlocks.Length();
|
||||
if (blockCount > mBlocks.Length()) {
|
||||
PRUint32 needed = blockCount - mBlocks.Length();
|
||||
nsAutoPtr<Block> *blocks = mBlocks.AppendElements(needed);
|
||||
if (NS_UNLIKELY(!blocks)) { // OOM
|
||||
return;
|
||||
}
|
||||
}
|
||||
// for each block that may be present in aBitset...
|
||||
for (PRUint32 i = 0; i < blockCount; ++i) {
|
||||
// if it is missing (implicitly empty), just skip
|
||||
if (!aBitset.mBlocks[i]) {
|
||||
continue;
|
||||
}
|
||||
// if the block is missing in this set, just copy the other
|
||||
if (!mBlocks[i]) {
|
||||
mBlocks[i] = new Block(*aBitset.mBlocks[i]);
|
||||
continue;
|
||||
}
|
||||
// else set existing block to the union of both
|
||||
PRUint32 *dst = reinterpret_cast<PRUint32*>(mBlocks[i]->mBits);
|
||||
const PRUint32 *src =
|
||||
reinterpret_cast<const PRUint32*>(aBitset.mBlocks[i]->mBits);
|
||||
for (PRUint32 j = 0; j < BLOCK_SIZE / 4; ++j) {
|
||||
dst[j] |= src[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Compact() {
|
||||
mBlocks.Compact();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
nsTArray< nsAutoPtr<Block> > mBlocks;
|
||||
};
|
||||
|
||||
|
@ -1128,7 +1128,7 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
|
||||
hb_position_t x_advance = posInfo[glyphStart].x_advance;
|
||||
nscoord advance =
|
||||
roundX ? dev2appUnits * FixedToIntRound(x_advance)
|
||||
: NS_floor(hb2appUnits * x_advance + 0.5);
|
||||
: floor(hb2appUnits * x_advance + 0.5);
|
||||
|
||||
if (glyphsInClump == 1 &&
|
||||
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
|
||||
@ -1158,18 +1158,18 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
|
||||
hb_position_t x_offset = posInfo[glyphStart].x_offset;
|
||||
details->mXOffset =
|
||||
roundX ? dev2appUnits * FixedToIntRound(x_offset)
|
||||
: NS_floor(hb2appUnits * x_offset + 0.5);
|
||||
: floor(hb2appUnits * x_offset + 0.5);
|
||||
hb_position_t y_offset = posInfo[glyphStart].y_offset;
|
||||
details->mYOffset = yPos -
|
||||
(roundY ? dev2appUnits * FixedToIntRound(y_offset)
|
||||
: NS_floor(hb2appUnits * y_offset + 0.5));
|
||||
: floor(hb2appUnits * y_offset + 0.5));
|
||||
|
||||
details->mAdvance = advance;
|
||||
hb_position_t y_advance = posInfo[glyphStart].y_advance;
|
||||
if (y_advance != 0) {
|
||||
yPos -=
|
||||
roundY ? dev2appUnits * FixedToIntRound(y_advance)
|
||||
: NS_floor(hb2appUnits * y_advance + 0.5);
|
||||
: floor(hb2appUnits * y_advance + 0.5);
|
||||
}
|
||||
if (++glyphStart >= glyphEnd) {
|
||||
break;
|
||||
@ -1177,7 +1177,7 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
|
||||
x_advance = posInfo[glyphStart].x_advance;
|
||||
advance =
|
||||
roundX ? dev2appUnits * FixedToIntRound(x_advance)
|
||||
: NS_floor(hb2appUnits * x_advance + 0.5);
|
||||
: floor(hb2appUnits * x_advance + 0.5);
|
||||
}
|
||||
|
||||
gfxTextRun::CompressedGlyph g;
|
||||
|
@ -194,8 +194,8 @@ public:
|
||||
*/
|
||||
PRBool HasNonIntegerTranslation() const {
|
||||
return HasNonTranslation() ||
|
||||
!FuzzyEqual(x0, NS_floor(x0 + 0.5)) ||
|
||||
!FuzzyEqual(y0, NS_floor(y0 + 0.5));
|
||||
!FuzzyEqual(x0, floor(x0 + 0.5)) ||
|
||||
!FuzzyEqual(y0, floor(y0 + 0.5));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -291,8 +291,8 @@ public:
|
||||
* Returns true if the matrix has non-integer scale
|
||||
*/
|
||||
PRBool HasNonIntegerScale() const {
|
||||
return !FuzzyEqual(xx, NS_floor(xx + 0.5)) ||
|
||||
!FuzzyEqual(yy, NS_floor(yy + 0.5));
|
||||
return !FuzzyEqual(xx, floor(xx + 0.5)) ||
|
||||
!FuzzyEqual(yy, floor(yy + 0.5));
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -127,11 +127,11 @@ static void FillMetricsDefaults(gfxFont::Metrics *aMetrics)
|
||||
// line as close to the original position as possible.
|
||||
static void SnapLineToPixels(gfxFloat& aOffset, gfxFloat& aSize)
|
||||
{
|
||||
gfxFloat snappedSize = NS_MAX(NS_floor(aSize + 0.5), 1.0);
|
||||
gfxFloat snappedSize = NS_MAX(floor(aSize + 0.5), 1.0);
|
||||
// Correct offset for change in size
|
||||
gfxFloat offset = aOffset - 0.5 * (aSize - snappedSize);
|
||||
// Snap offset
|
||||
aOffset = NS_floor(offset + 0.5);
|
||||
aOffset = floor(offset + 0.5);
|
||||
aSize = snappedSize;
|
||||
}
|
||||
|
||||
@ -155,7 +155,7 @@ const gfxFont::Metrics& gfxOS2Font::GetMetrics()
|
||||
|
||||
// round size to integer pixels, this is to get full pixels for layout
|
||||
// together with internal/external leading (see below)
|
||||
mMetrics->emHeight = NS_floor(GetStyle()->size + 0.5);
|
||||
mMetrics->emHeight = floor(GetStyle()->size + 0.5);
|
||||
|
||||
cairo_scaled_font_t* scaledFont = CairoScaledFont();
|
||||
if (!scaledFont) {
|
||||
@ -274,9 +274,9 @@ const gfxFont::Metrics& gfxOS2Font::GetMetrics()
|
||||
// leadings are not available directly (only for WinFNTs);
|
||||
// better compute them on our own, to get integer values and make
|
||||
// layout happy (see // LockedFTFace::GetMetrics in gfxPangoFonts.cpp)
|
||||
mMetrics->internalLeading = NS_floor(mMetrics->maxHeight
|
||||
mMetrics->internalLeading = floor(mMetrics->maxHeight
|
||||
- mMetrics->emHeight + 0.5);
|
||||
gfxFloat lineHeight = NS_floor(mMetrics->maxHeight + 0.5);
|
||||
gfxFloat lineHeight = floor(mMetrics->maxHeight + 0.5);
|
||||
mMetrics->externalLeading = lineHeight
|
||||
- mMetrics->internalLeading - mMetrics->emHeight;
|
||||
|
||||
|
@ -69,8 +69,8 @@ struct THEBES_API gfxPoint : public mozilla::gfx::BasePoint<gfxFloat, gfxPoint>
|
||||
// And if you need similar method which is using NS_round(), you should
|
||||
// create new |RoundAwayFromZero()| method.
|
||||
gfxPoint& Round() {
|
||||
x = NS_floor(x + 0.5);
|
||||
y = NS_floor(y + 0.5);
|
||||
x = floor(x + 0.5);
|
||||
y = floor(y + 0.5);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
@ -59,10 +59,10 @@ void
|
||||
gfxRect::Round()
|
||||
{
|
||||
// Note that don't use NS_round here. See the comment for this method in gfxRect.h
|
||||
gfxFloat x0 = NS_floor(X() + 0.5);
|
||||
gfxFloat y0 = NS_floor(Y() + 0.5);
|
||||
gfxFloat x1 = NS_floor(XMost() + 0.5);
|
||||
gfxFloat y1 = NS_floor(YMost() + 0.5);
|
||||
gfxFloat x0 = floor(X() + 0.5);
|
||||
gfxFloat y0 = floor(Y() + 0.5);
|
||||
gfxFloat x1 = floor(XMost() + 0.5);
|
||||
gfxFloat y1 = floor(YMost() + 0.5);
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
@ -74,10 +74,10 @@ gfxRect::Round()
|
||||
void
|
||||
gfxRect::RoundIn()
|
||||
{
|
||||
gfxFloat x0 = NS_ceil(X());
|
||||
gfxFloat y0 = NS_ceil(Y());
|
||||
gfxFloat x1 = NS_floor(XMost());
|
||||
gfxFloat y1 = NS_floor(YMost());
|
||||
gfxFloat x0 = ceil(X());
|
||||
gfxFloat y0 = ceil(Y());
|
||||
gfxFloat x1 = floor(XMost());
|
||||
gfxFloat y1 = floor(YMost());
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
@ -89,10 +89,10 @@ gfxRect::RoundIn()
|
||||
void
|
||||
gfxRect::RoundOut()
|
||||
{
|
||||
gfxFloat x0 = NS_floor(X());
|
||||
gfxFloat y0 = NS_floor(Y());
|
||||
gfxFloat x1 = NS_ceil(XMost());
|
||||
gfxFloat y1 = NS_ceil(YMost());
|
||||
gfxFloat x0 = floor(X());
|
||||
gfxFloat y0 = floor(Y());
|
||||
gfxFloat x1 = ceil(XMost());
|
||||
gfxFloat y1 = ceil(YMost());
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
|
@ -106,12 +106,10 @@ public:
|
||||
|
||||
virtual ~gfxMixedFontFamily() { }
|
||||
|
||||
void AddFontEntry(gfxFontEntry *aFontEntry)
|
||||
{
|
||||
void AddFontEntry(gfxFontEntry *aFontEntry) {
|
||||
nsRefPtr<gfxFontEntry> fe = aFontEntry;
|
||||
mAvailableFonts.AppendElement(fe);
|
||||
aFontEntry->SetFamily(this);
|
||||
ResetCharacterMap();
|
||||
}
|
||||
|
||||
void ReplaceFontEntry(gfxFontEntry *aOldFontEntry, gfxFontEntry *aNewFontEntry)
|
||||
@ -125,10 +123,9 @@ public:
|
||||
// other reference to it except from its family
|
||||
mAvailableFonts[i] = aNewFontEntry;
|
||||
aNewFontEntry->SetFamily(this);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
ResetCharacterMap();
|
||||
}
|
||||
|
||||
void RemoveFontEntry(gfxFontEntry *aFontEntry)
|
||||
@ -139,10 +136,9 @@ public:
|
||||
if (fe == aFontEntry) {
|
||||
aFontEntry->SetFamily(nsnull);
|
||||
mAvailableFonts.RemoveElementAt(i);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
ResetCharacterMap();
|
||||
}
|
||||
|
||||
// temp method to determine if all proxies are loaded
|
||||
|
@ -489,7 +489,7 @@ gfxUtils::ClampToScaleFactor(gfxFloat aVal)
|
||||
if (fabs(power - NS_round(power)) < 1e-6) {
|
||||
power = NS_round(power);
|
||||
} else {
|
||||
power = NS_ceil(power);
|
||||
power = ceil(power);
|
||||
}
|
||||
|
||||
return pow(kScaleResolution, power);
|
||||
|
@ -138,8 +138,8 @@ gfxWindowsNativeDrawing::BeginNativeDrawing()
|
||||
// There's probably a better fix, but I haven't figured out
|
||||
// the root cause of the problem.
|
||||
mTempSurfaceSize =
|
||||
gfxIntSize((PRInt32) NS_ceil(mNativeRect.Width() + 1),
|
||||
(PRInt32) NS_ceil(mNativeRect.Height() + 1));
|
||||
gfxIntSize((PRInt32) ceil(mNativeRect.Width() + 1),
|
||||
(PRInt32) ceil(mNativeRect.Height() + 1));
|
||||
} else {
|
||||
// figure out the scale factors
|
||||
mScale = m.ScaleFactors(PR_TRUE);
|
||||
@ -153,8 +153,8 @@ gfxWindowsNativeDrawing::BeginNativeDrawing()
|
||||
|
||||
// See comment above about "+1"
|
||||
mTempSurfaceSize =
|
||||
gfxIntSize((PRInt32) NS_ceil(mNativeRect.Width() * mScale.width + 1),
|
||||
(PRInt32) NS_ceil(mNativeRect.Height() * mScale.height + 1));
|
||||
gfxIntSize((PRInt32) ceil(mNativeRect.Width() * mScale.width + 1),
|
||||
(PRInt32) ceil(mNativeRect.Height() * mScale.height + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -677,7 +677,7 @@ check-malloc-function-usage: $(filter-out %jsalloc.h %jscntxt.h %jsutil.h, $(ALL
|
||||
# We desire these numbers to go down, not up. See "User guide to memory
|
||||
# management within SpiderMonkey" in jsutil.h.
|
||||
$(srcdir)/config/check_source_count.py OffTheBooks:: 59 \
|
||||
"in Makefile.in" "{cx,rt}->{new_,new_array,malloc_,calloc_,realloc_}" $^
|
||||
"in Makefile.in" "{cx,rt}->{new_,array_new,malloc_,calloc_,realloc_}" $^
|
||||
# This should go to zero, if possible.
|
||||
$(srcdir)/config/check_source_count.py UnwantedForeground:: 31 \
|
||||
"in Makefile.in" "{cx,rt}->{free_,delete_,array_delete}" $^
|
||||
|
@ -1081,6 +1081,7 @@ endif
|
||||
|
||||
ifdef DTRACE_PROBE_OBJ
|
||||
EXTRA_DEPS += $(DTRACE_PROBE_OBJ)
|
||||
OBJS += $(DTRACE_PROBE_OBJ)
|
||||
endif
|
||||
|
||||
$(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
|
||||
@ -1121,8 +1122,8 @@ ifdef HAVE_DTRACE
|
||||
ifndef XP_MACOSX
|
||||
ifdef DTRACE_PROBE_OBJ
|
||||
ifndef DTRACE_LIB_DEPENDENT
|
||||
$(DTRACE_PROBE_OBJ): $(OBJS)
|
||||
dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ) $(OBJS)
|
||||
$(DTRACE_PROBE_OBJ):
|
||||
dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@ -1144,7 +1145,7 @@ endif
|
||||
$(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(DTRACE_PROBE_OBJ) $(MOZILLA_PROBE_LIBS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE)
|
||||
@$(RM) $(DTRACE_PROBE_OBJ)
|
||||
else # ! DTRACE_LIB_DEPENDENT
|
||||
$(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(DTRACE_PROBE_OBJ) $(LOBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE)
|
||||
$(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE)
|
||||
endif # DTRACE_LIB_DEPENDENT
|
||||
@$(call CHECK_STDCXX,$@)
|
||||
|
||||
|
@ -5022,6 +5022,18 @@ if test -n "$JS_GC_ZEAL"; then
|
||||
AC_DEFINE(JS_GC_ZEAL)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl JS opt-mode assertions and minidump instrumentation
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(js-diagnostics,
|
||||
[ --enable-js-diagnostics
|
||||
Enable JS diagnostic assertions and breakpad data],
|
||||
JS_CRASH_DIAGNOSTICS=1,
|
||||
JS_CRASH_DIAGNOSTICS= )
|
||||
if test -n "$JS_CRASH_DIAGNOSTICS"; then
|
||||
AC_DEFINE(JS_CRASH_DIAGNOSTICS)
|
||||
fi
|
||||
|
||||
dnl ======================================================
|
||||
dnl = Enable compiling with ccache
|
||||
dnl ======================================================
|
||||
|
2
js/src/ctypes/libffi/configure
vendored
2
js/src/ctypes/libffi/configure
vendored
@ -11277,7 +11277,7 @@ case "$host" in
|
||||
powerpc-*-aix* | rs6000-*-aix*)
|
||||
TARGET=POWERPC_AIX; TARGETDIR=powerpc
|
||||
;;
|
||||
powerpc-*-freebsd*)
|
||||
powerpc-*-freebsd* | powerpc-*-openbsd*)
|
||||
TARGET=POWERPC_FREEBSD; TARGETDIR=powerpc
|
||||
;;
|
||||
powerpc*-*-rtems*)
|
||||
|
3
js/src/jit-test/tests/basic/bug678211.js
Normal file
3
js/src/jit-test/tests/basic/bug678211.js
Normal file
@ -0,0 +1,3 @@
|
||||
var g = newGlobal('new-compartment');
|
||||
g.eval("function f(n) { for (var i = 0; i < n; i++) f(0); }");
|
||||
g.f(RUNLOOP + 1);
|
@ -51,15 +51,18 @@ CPPSRCS = \
|
||||
selfTest.cpp \
|
||||
testArgumentsObject.cpp \
|
||||
testBug604087.cpp \
|
||||
testChromeBuffer.cpp \
|
||||
testClassGetter.cpp \
|
||||
testCloneScript.cpp \
|
||||
testConservativeGC.cpp \
|
||||
testContexts.cpp \
|
||||
testCustomIterator.cpp \
|
||||
testDebugger.cpp \
|
||||
testDeepFreeze.cpp \
|
||||
testDefineGetterSetterNonEnumerable.cpp \
|
||||
testDefineProperty.cpp \
|
||||
testExtendedEq.cpp \
|
||||
testExternalStrings.cpp \
|
||||
testFuncCallback.cpp \
|
||||
testFunctionProperties.cpp \
|
||||
testGCChunkAlloc.cpp \
|
||||
@ -84,9 +87,6 @@ CPPSRCS = \
|
||||
testUTF8.cpp \
|
||||
testVersion.cpp \
|
||||
testXDR.cpp \
|
||||
testCustomIterator.cpp \
|
||||
testExternalStrings.cpp \
|
||||
testChromeBuffer.cpp \
|
||||
$(NULL)
|
||||
|
||||
# Disabled: an entirely unrelated test seems to cause this to fail. Moreover,
|
||||
|
@ -7,43 +7,50 @@
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsnum.h"
|
||||
#include "jsstr.h"
|
||||
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
template<size_t N> JSFlatString *
|
||||
NewString(JSContext *cx, const jschar (&chars)[N])
|
||||
{
|
||||
return js_NewStringCopyN(cx, chars, N);
|
||||
}
|
||||
|
||||
static const struct TestPair {
|
||||
uint32 num;
|
||||
const char *expected;
|
||||
} tests[] = {
|
||||
{ 0, "0" },
|
||||
{ 1, "1" },
|
||||
{ 2, "2" },
|
||||
{ 9, "9" },
|
||||
{ 10, "10" },
|
||||
{ 15, "15" },
|
||||
{ 16, "16" },
|
||||
{ 17, "17" },
|
||||
{ 99, "99" },
|
||||
{ 100, "100" },
|
||||
{ 255, "255" },
|
||||
{ 256, "256" },
|
||||
{ 257, "257" },
|
||||
{ 999, "999" },
|
||||
{ 1000, "1000" },
|
||||
{ 4095, "4095" },
|
||||
{ 4096, "4096" },
|
||||
{ 9999, "9999" },
|
||||
{ 1073741823, "1073741823" },
|
||||
{ 1073741824, "1073741824" },
|
||||
{ 1073741825, "1073741825" },
|
||||
{ 2147483647, "2147483647" },
|
||||
{ 2147483648, "2147483648" },
|
||||
{ 2147483649, "2147483649" },
|
||||
{ 4294967294, "4294967294" },
|
||||
{ 4294967295, "4294967295" },
|
||||
};
|
||||
|
||||
BEGIN_TEST(testIndexToString)
|
||||
{
|
||||
const struct TestPair {
|
||||
uint32 num;
|
||||
const char *expected;
|
||||
} tests[] = {
|
||||
{ 0, "0" },
|
||||
{ 1, "1" },
|
||||
{ 2, "2" },
|
||||
{ 9, "9" },
|
||||
{ 10, "10" },
|
||||
{ 15, "15" },
|
||||
{ 16, "16" },
|
||||
{ 17, "17" },
|
||||
{ 99, "99" },
|
||||
{ 100, "100" },
|
||||
{ 255, "255" },
|
||||
{ 256, "256" },
|
||||
{ 257, "257" },
|
||||
{ 999, "999" },
|
||||
{ 1000, "1000" },
|
||||
{ 4095, "4095" },
|
||||
{ 4096, "4096" },
|
||||
{ 9999, "9999" },
|
||||
{ 1073741823, "1073741823" },
|
||||
{ 1073741824, "1073741824" },
|
||||
{ 1073741825, "1073741825" },
|
||||
{ 2147483647, "2147483647" },
|
||||
{ 2147483648, "2147483648" },
|
||||
{ 2147483649, "2147483649" },
|
||||
{ 4294967294, "4294967294" },
|
||||
{ 4294967295, "4294967295" },
|
||||
};
|
||||
|
||||
for (size_t i = 0, sz = JS_ARRAY_LENGTH(tests); i < sz; i++) {
|
||||
uint32 u = tests[i].num;
|
||||
JSString *str = js::IndexToString(cx, u);
|
||||
@ -60,3 +67,45 @@ BEGIN_TEST(testIndexToString)
|
||||
return true;
|
||||
}
|
||||
END_TEST(testIndexToString)
|
||||
|
||||
BEGIN_TEST(testStringIsElement)
|
||||
{
|
||||
for (size_t i = 0, sz = JS_ARRAY_LENGTH(tests); i < sz; i++) {
|
||||
uint32 u = tests[i].num;
|
||||
JSFlatString *str = js::IndexToString(cx, u);
|
||||
CHECK(str);
|
||||
|
||||
uint32 n;
|
||||
CHECK(str->isElement(&n));
|
||||
CHECK(u == n);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testStringIsElement)
|
||||
|
||||
BEGIN_TEST(testStringToPropertyName)
|
||||
{
|
||||
uint32 index;
|
||||
|
||||
static const jschar hiChars[] = { 'h', 'i' };
|
||||
JSFlatString *hiStr = NewString(cx, hiChars);
|
||||
CHECK(hiStr);
|
||||
CHECK(!hiStr->isElement(&index));
|
||||
CHECK(hiStr->toPropertyName(cx) != NULL);
|
||||
|
||||
static const jschar maxChars[] = { '4', '2', '9', '4', '9', '6', '7', '2', '9', '5' };
|
||||
JSFlatString *maxStr = NewString(cx, maxChars);
|
||||
CHECK(maxStr);
|
||||
CHECK(maxStr->isElement(&index));
|
||||
CHECK(index == UINT32_MAX);
|
||||
|
||||
static const jschar maxPlusOneChars[] = { '4', '2', '9', '4', '9', '6', '7', '2', '9', '6' };
|
||||
JSFlatString *maxPlusOneStr = NewString(cx, maxPlusOneChars);
|
||||
CHECK(maxPlusOneStr);
|
||||
CHECK(!maxPlusOneStr->isElement(&index));
|
||||
CHECK(maxPlusOneStr->toPropertyName(cx) != NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testStringToPropertyName)
|
||||
|
@ -2712,9 +2712,9 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key)
|
||||
case JSGC_MODE:
|
||||
return uint32(rt->gcMode);
|
||||
case JSGC_UNUSED_CHUNKS:
|
||||
return uint32(rt->gcChunksWaitingToExpire);
|
||||
return uint32(rt->gcEmptyChunkCount);
|
||||
case JSGC_TOTAL_CHUNKS:
|
||||
return uint32(rt->gcUserChunkSet.count() + rt->gcSystemChunkSet.count());
|
||||
return uint32(rt->gcChunkSet.count() + rt->gcEmptyChunkCount);
|
||||
default:
|
||||
JS_ASSERT(key == JSGC_NUMBER);
|
||||
return rt->gcNumber;
|
||||
|
@ -169,13 +169,6 @@ struct JSAtomMap {
|
||||
|
||||
namespace js {
|
||||
|
||||
/* N.B. must correspond to boolean tagging behavior. */
|
||||
enum InternBehavior
|
||||
{
|
||||
DoNotInternAtom = false,
|
||||
InternAtom = true
|
||||
};
|
||||
|
||||
typedef TaggedPointerEntry<JSAtom> AtomStateEntry;
|
||||
|
||||
struct AtomHasher
|
||||
@ -507,13 +500,6 @@ js_InitCommonAtoms(JSContext *cx);
|
||||
extern void
|
||||
js_FinishCommonAtoms(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Find or create the atom for a string. Return null on failure to allocate
|
||||
* memory.
|
||||
*/
|
||||
extern JSAtom *
|
||||
js_AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom);
|
||||
|
||||
extern JSAtom *
|
||||
js_Atomize(JSContext *cx, const char *bytes, size_t length,
|
||||
js::InternBehavior ib = js::DoNotInternAtom,
|
||||
|
@ -135,12 +135,10 @@ js_Int32ToId(JSContext* cx, int32 index, jsid* id)
|
||||
|
||||
namespace js {
|
||||
|
||||
static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1;
|
||||
|
||||
/*
|
||||
* Write out character representing |index| to the memory just before |end|.
|
||||
* Thus |*end| is not touched, but |end[-1]| and earlier are modified as
|
||||
* appropriate. There must be at least UINT32_CHAR_BUFFER_LENGTH elements
|
||||
* appropriate. There must be at least js::UINT32_CHAR_BUFFER_LENGTH elements
|
||||
* before |end| to avoid buffer underflow. The start of the characters written
|
||||
* is returned and is necessarily before |end|.
|
||||
*/
|
||||
|
@ -138,7 +138,7 @@ struct GSNCache {
|
||||
|
||||
void purge();
|
||||
};
|
||||
|
||||
|
||||
inline GSNCache *
|
||||
GetGSNCache(JSContext *cx);
|
||||
|
||||
@ -212,7 +212,7 @@ struct ThreadData {
|
||||
~ThreadData();
|
||||
|
||||
bool init();
|
||||
|
||||
|
||||
void mark(JSTracer *trc) {
|
||||
stackSpace.mark(trc);
|
||||
}
|
||||
@ -263,7 +263,7 @@ struct JSThread {
|
||||
suspendCount(0)
|
||||
# ifdef DEBUG
|
||||
, checkRequestDepth(0)
|
||||
# endif
|
||||
# endif
|
||||
{
|
||||
JS_INIT_CLIST(&contextList);
|
||||
}
|
||||
@ -390,8 +390,31 @@ struct JSRuntime
|
||||
uint32 protoHazardShape;
|
||||
|
||||
/* Garbage collector state, used by jsgc.c. */
|
||||
js::GCChunkSet gcUserChunkSet;
|
||||
js::GCChunkSet gcSystemChunkSet;
|
||||
|
||||
/*
|
||||
* Set of all GC chunks with at least one allocated thing. The
|
||||
* conservative GC uses it to quickly check if a possible GC thing points
|
||||
* into an allocated chunk.
|
||||
*/
|
||||
js::GCChunkSet gcChunkSet;
|
||||
|
||||
/*
|
||||
* Doubly-linked lists of chunks from user and system compartments. The GC
|
||||
* allocates its arenas from the corresponding list and when all arenas
|
||||
* in the list head are taken, then the chunk is removed from the list.
|
||||
* During the GC when all arenas in a chunk become free, that chunk is
|
||||
* removed from the list and scheduled for release.
|
||||
*/
|
||||
js::gc::Chunk *gcSystemAvailableChunkListHead;
|
||||
js::gc::Chunk *gcUserAvailableChunkListHead;
|
||||
|
||||
/*
|
||||
* Singly-linked list of empty chunks and its length. We use the list not
|
||||
* to release empty chunks immediately so they can be used for future
|
||||
* allocations. This avoids very high overhead of chunk release/allocation.
|
||||
*/
|
||||
js::gc::Chunk *gcEmptyChunkListHead;
|
||||
size_t gcEmptyChunkCount;
|
||||
|
||||
js::RootedValueMap gcRootsHash;
|
||||
js::GCLocks gcLocksHash;
|
||||
@ -401,7 +424,6 @@ struct JSRuntime
|
||||
size_t gcLastBytes;
|
||||
size_t gcMaxBytes;
|
||||
size_t gcMaxMallocBytes;
|
||||
size_t gcChunksWaitingToExpire;
|
||||
uint32 gcEmptyArenaPoolLifespan;
|
||||
uint32 gcNumber;
|
||||
js::GCMarker *gcMarkingTracer;
|
||||
@ -673,7 +695,7 @@ struct JSRuntime
|
||||
return infoEnabled;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Circular buffer with GC data.
|
||||
* count may grow >= INFO_LIMIT, which would indicate data loss.
|
||||
*/
|
||||
|
@ -130,7 +130,6 @@ JSCompartment::~JSCompartment()
|
||||
bool
|
||||
JSCompartment::init()
|
||||
{
|
||||
chunk = NULL;
|
||||
for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
|
||||
arenas[i].init();
|
||||
freeLists.init();
|
||||
@ -470,8 +469,6 @@ JSCompartment::markCrossCompartmentWrappers(JSTracer *trc)
|
||||
void
|
||||
JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
|
||||
{
|
||||
chunk = NULL;
|
||||
|
||||
/* Remove dead wrappers from the table. */
|
||||
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
|
||||
JS_ASSERT_IF(IsAboutToBeFinalized(cx, e.front().key.toGCThing()) &&
|
||||
@ -569,8 +566,6 @@ JSCompartment::purge(JSContext *cx)
|
||||
#endif
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
js::CheckCompartmentScripts(this);
|
||||
|
||||
for (JSScript *script = (JSScript *)scripts.next;
|
||||
&script->links != &scripts;
|
||||
script = (JSScript *)script->links.next) {
|
||||
|
@ -391,7 +391,6 @@ typedef HashSet<ScriptFilenameEntry *,
|
||||
struct JS_FRIEND_API(JSCompartment) {
|
||||
JSRuntime *rt;
|
||||
JSPrincipals *principals;
|
||||
js::gc::Chunk *chunk;
|
||||
|
||||
js::gc::ArenaList arenas[js::gc::FINALIZE_LIMIT];
|
||||
js::gc::FreeLists freeLists;
|
||||
|
@ -245,37 +245,37 @@ static Stack gGCStack(JS_CRASH_STACK_GC);
|
||||
static Stack gErrorStack(JS_CRASH_STACK_ERROR);
|
||||
static Ring gRingBuffer(JS_CRASH_RING);
|
||||
|
||||
void
|
||||
SnapshotGCStack()
|
||||
{
|
||||
if (gInitialized)
|
||||
gGCStack.snapshot();
|
||||
}
|
||||
|
||||
void
|
||||
SnapshotErrorStack()
|
||||
{
|
||||
if (gInitialized)
|
||||
gErrorStack.snapshot();
|
||||
}
|
||||
|
||||
void
|
||||
SaveCrashData(uint64 tag, void *ptr, size_t size)
|
||||
{
|
||||
if (gInitialized)
|
||||
gRingBuffer.push(tag, ptr, size);
|
||||
}
|
||||
|
||||
} /* namespace crash */
|
||||
} /* namespace js */
|
||||
|
||||
using namespace js;
|
||||
using namespace js::crash;
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_SnapshotGCStack()
|
||||
{
|
||||
if (gInitialized)
|
||||
gGCStack.snapshot();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_SnapshotErrorStack()
|
||||
{
|
||||
if (gInitialized)
|
||||
gErrorStack.snapshot();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_SaveCrashData(uint64 tag, void *ptr, size_t size)
|
||||
{
|
||||
if (gInitialized)
|
||||
gRingBuffer.push(tag, ptr, size);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback)
|
||||
{
|
||||
#if 1
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
if (!gInitialized) {
|
||||
gInitialized = true;
|
||||
(*callback)(&gGCStack, sizeof(gGCStack));
|
||||
|
@ -42,18 +42,46 @@
|
||||
#define jscrashreport_h___
|
||||
|
||||
#include "jstypes.h"
|
||||
#include "jsutil.h"
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
namespace js {
|
||||
namespace crash {
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_SnapshotGCStack();
|
||||
void
|
||||
SnapshotGCStack();
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_SnapshotErrorStack();
|
||||
void
|
||||
SnapshotErrorStack();
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_SaveCrashData(uint64 tag, void *ptr, size_t size);
|
||||
void
|
||||
SaveCrashData(uint64 tag, void *ptr, size_t size);
|
||||
|
||||
JS_END_EXTERN_C
|
||||
template<size_t size, char marker>
|
||||
class StackBuffer {
|
||||
private:
|
||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
volatile char buffer[size + 4];
|
||||
|
||||
public:
|
||||
StackBuffer(void *data JS_GUARD_OBJECT_NOTIFIER_PARAM) {
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
|
||||
buffer[0] = marker;
|
||||
buffer[1] = '[';
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (data)
|
||||
buffer[i + 2] = ((char *)data)[i];
|
||||
else
|
||||
buffer[i + 2] = 0;
|
||||
}
|
||||
|
||||
buffer[size - 2] = ']';
|
||||
buffer[size - 1] = marker;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace crash */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jscrashreport_h___ */
|
||||
|
235
js/src/jsgc.cpp
235
js/src/jsgc.cpp
@ -242,7 +242,7 @@ Arena::finalize(JSContext *cx)
|
||||
if (!newFreeSpanStart)
|
||||
newFreeSpanStart = thing;
|
||||
t->finalize(cx);
|
||||
memset(t, JS_FREE_PATTERN, sizeof(T));
|
||||
JS_POISON(t, JS_FREE_PATTERN, sizeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -338,27 +338,49 @@ Chunk::init(JSRuntime *rt)
|
||||
|
||||
for (size_t i = 0; i != JS_ARRAY_LENGTH(markingDelay); ++i)
|
||||
markingDelay[i].init();
|
||||
|
||||
/*
|
||||
* The rest of info fields is initailzied in PickChunk. We do not clear
|
||||
* the mark bitmap as that is done at the start of the next GC.
|
||||
*/
|
||||
}
|
||||
|
||||
bool
|
||||
Chunk::unused()
|
||||
inline Chunk **
|
||||
GetAvailableChunkList(JSCompartment *comp)
|
||||
{
|
||||
return info.numFree == ArenasPerChunk;
|
||||
JSRuntime *rt = comp->rt;
|
||||
return comp->isSystemCompartment
|
||||
? &rt->gcSystemAvailableChunkListHead
|
||||
: &rt->gcUserAvailableChunkListHead;
|
||||
}
|
||||
|
||||
bool
|
||||
Chunk::hasAvailableArenas()
|
||||
inline void
|
||||
Chunk::addToAvailableList(JSCompartment *comp)
|
||||
{
|
||||
return info.numFree > 0;
|
||||
Chunk **listHeadp = GetAvailableChunkList(comp);
|
||||
JS_ASSERT(!info.prevp);
|
||||
JS_ASSERT(!info.next);
|
||||
info.prevp = listHeadp;
|
||||
Chunk *head = *listHeadp;
|
||||
if (head) {
|
||||
JS_ASSERT(head->info.prevp == listHeadp);
|
||||
head->info.prevp = &info.next;
|
||||
}
|
||||
info.next = head;
|
||||
*listHeadp = this;
|
||||
}
|
||||
|
||||
bool
|
||||
Chunk::withinArenasRange(Cell *cell)
|
||||
inline void
|
||||
Chunk::removeFromAvailableList()
|
||||
{
|
||||
uintptr_t addr = uintptr_t(cell);
|
||||
if (addr >= uintptr_t(&arenas[0]) && addr < uintptr_t(&arenas[ArenasPerChunk]))
|
||||
return true;
|
||||
return false;
|
||||
JS_ASSERT(info.prevp);
|
||||
*info.prevp = info.next;
|
||||
if (info.next) {
|
||||
JS_ASSERT(info.next->info.prevp == &info.next);
|
||||
info.next->info.prevp = info.prevp;
|
||||
}
|
||||
info.prevp = NULL;
|
||||
info.next = NULL;
|
||||
}
|
||||
|
||||
template <size_t thingSize>
|
||||
@ -372,6 +394,9 @@ Chunk::allocateArena(JSContext *cx, unsigned thingKind)
|
||||
aheader->init(comp, thingKind, thingSize);
|
||||
--info.numFree;
|
||||
|
||||
if (!hasAvailableArenas())
|
||||
removeFromAvailableList();
|
||||
|
||||
JSRuntime *rt = info.runtime;
|
||||
Probes::resizeHeap(comp, rt->gcBytes, rt->gcBytes + ArenaSize);
|
||||
JS_ATOMIC_ADD(&rt->gcBytes, ArenaSize);
|
||||
@ -410,8 +435,27 @@ Chunk::releaseArena(ArenaHeader *aheader)
|
||||
aheader->next = info.emptyArenaListHead;
|
||||
info.emptyArenaListHead = aheader;
|
||||
++info.numFree;
|
||||
if (unused())
|
||||
if (info.numFree == 1) {
|
||||
JS_ASSERT(!info.prevp);
|
||||
JS_ASSERT(!info.next);
|
||||
addToAvailableList(aheader->compartment);
|
||||
} else if (!unused()) {
|
||||
JS_ASSERT(info.prevp);
|
||||
} else {
|
||||
rt->gcChunkSet.remove(this);
|
||||
removeFromAvailableList();
|
||||
|
||||
/*
|
||||
* We keep empty chunks until we are done with finalization to allow
|
||||
* calling IsAboutToBeFinalized/Cell::isMarked for finalized GC things
|
||||
* in empty chunks. So we add the chunk to the empty set even during
|
||||
* GC_SHRINK.
|
||||
*/
|
||||
info.age = 0;
|
||||
info.next = rt->gcEmptyChunkListHead;
|
||||
rt->gcEmptyChunkListHead = this;
|
||||
rt->gcEmptyChunkCount++;
|
||||
}
|
||||
}
|
||||
|
||||
inline Chunk *
|
||||
@ -438,100 +482,71 @@ ReleaseGCChunk(JSRuntime *rt, Chunk *p)
|
||||
inline Chunk *
|
||||
PickChunk(JSContext *cx)
|
||||
{
|
||||
Chunk *chunk = cx->compartment->chunk;
|
||||
if (chunk && chunk->hasAvailableArenas())
|
||||
JSCompartment *comp = cx->compartment;
|
||||
JSRuntime *rt = comp->rt;
|
||||
Chunk **listHeadp = GetAvailableChunkList(comp);
|
||||
Chunk *chunk = *listHeadp;
|
||||
if (chunk)
|
||||
return chunk;
|
||||
|
||||
JSRuntime *rt = cx->runtime;
|
||||
bool isSystemCompartment = cx->compartment->isSystemCompartment;
|
||||
|
||||
/*
|
||||
* The chunk used for the last allocation is full, search all chunks for
|
||||
* free arenas.
|
||||
* We do not have available chunks, either get one from the empty set or
|
||||
* allocate one.
|
||||
*/
|
||||
GCChunkSet::Range
|
||||
r(isSystemCompartment ? rt->gcSystemChunkSet.all() : rt->gcUserChunkSet.all());
|
||||
for (; !r.empty(); r.popFront()) {
|
||||
chunk = r.front();
|
||||
if (chunk->hasAvailableArenas()) {
|
||||
cx->compartment->chunk = chunk;
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
|
||||
chunk = AllocateGCChunk(rt);
|
||||
if (!chunk) {
|
||||
/* Our last chance is to find an empty chunk in the other chunk set. */
|
||||
GCChunkSet::Enum e(isSystemCompartment ? rt->gcUserChunkSet : rt->gcSystemChunkSet);
|
||||
for (; !e.empty(); e.popFront()) {
|
||||
if (e.front()->info.numFree == ArenasPerChunk) {
|
||||
chunk = e.front();
|
||||
e.removeFront();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
chunk = rt->gcEmptyChunkListHead;
|
||||
if (chunk) {
|
||||
JS_ASSERT(chunk->unused());
|
||||
JS_ASSERT(!rt->gcChunkSet.has(chunk));
|
||||
JS_ASSERT(rt->gcEmptyChunkCount >= 1);
|
||||
rt->gcEmptyChunkListHead = chunk->info.next;
|
||||
rt->gcEmptyChunkCount--;
|
||||
} else {
|
||||
chunk = AllocateGCChunk(rt);
|
||||
if (!chunk)
|
||||
return NULL;
|
||||
|
||||
chunk->init(rt);
|
||||
rt->gcChunkAllocationSinceLastGC = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME bug 583732 - chunk is newly allocated and cannot be present in
|
||||
* the table so using ordinary lookupForAdd is suboptimal here.
|
||||
*/
|
||||
GCChunkSet::AddPtr p = isSystemCompartment ?
|
||||
rt->gcSystemChunkSet.lookupForAdd(chunk) :
|
||||
rt->gcUserChunkSet.lookupForAdd(chunk);
|
||||
GCChunkSet::AddPtr p = rt->gcChunkSet.lookupForAdd(chunk);
|
||||
JS_ASSERT(!p);
|
||||
if (isSystemCompartment) {
|
||||
if (!rt->gcSystemChunkSet.add(p, chunk)) {
|
||||
ReleaseGCChunk(rt, chunk);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (!rt->gcUserChunkSet.add(p, chunk)) {
|
||||
ReleaseGCChunk(rt, chunk);
|
||||
return NULL;
|
||||
}
|
||||
if (!rt->gcChunkSet.add(p, chunk)) {
|
||||
ReleaseGCChunk(rt, chunk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chunk->init(rt);
|
||||
cx->compartment->chunk = chunk;
|
||||
rt->gcChunkAllocationSinceLastGC = true;
|
||||
chunk->info.prevp = NULL;
|
||||
chunk->info.next = NULL;
|
||||
chunk->addToAvailableList(comp);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
static void
|
||||
ExpireGCChunks(JSRuntime *rt, JSGCInvocationKind gckind)
|
||||
{
|
||||
static const size_t MaxAge = 3;
|
||||
|
||||
/* Remove unused chunks. */
|
||||
AutoLockGC lock(rt);
|
||||
|
||||
rt->gcChunksWaitingToExpire = 0;
|
||||
for (GCChunkSet::Enum e(rt->gcUserChunkSet); !e.empty(); e.popFront()) {
|
||||
Chunk *chunk = e.front();
|
||||
JS_ASSERT(chunk->info.runtime == rt);
|
||||
if (chunk->unused()) {
|
||||
if (gckind == GC_SHRINK || chunk->info.age++ > MaxAge) {
|
||||
e.removeFront();
|
||||
ReleaseGCChunk(rt, chunk);
|
||||
continue;
|
||||
}
|
||||
rt->gcChunksWaitingToExpire++;
|
||||
}
|
||||
}
|
||||
for (GCChunkSet::Enum e(rt->gcSystemChunkSet); !e.empty(); e.popFront()) {
|
||||
Chunk *chunk = e.front();
|
||||
JS_ASSERT(chunk->info.runtime == rt);
|
||||
if (chunk->unused()) {
|
||||
if (gckind == GC_SHRINK || chunk->info.age++ > MaxAge) {
|
||||
e.removeFront();
|
||||
ReleaseGCChunk(rt, chunk);
|
||||
continue;
|
||||
}
|
||||
rt->gcChunksWaitingToExpire++;
|
||||
/* Return old empty chunks to the system. */
|
||||
for (Chunk **chunkp = &rt->gcEmptyChunkListHead; *chunkp; ) {
|
||||
JS_ASSERT(rt->gcEmptyChunkCount);
|
||||
Chunk *chunk = *chunkp;
|
||||
JS_ASSERT(chunk->unused());
|
||||
JS_ASSERT(!rt->gcChunkSet.has(chunk));
|
||||
JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE);
|
||||
if (gckind == GC_SHRINK || chunk->info.age == MAX_EMPTY_CHUNK_AGE) {
|
||||
*chunkp = chunk->info.next;
|
||||
--rt->gcEmptyChunkCount;
|
||||
ReleaseGCChunk(rt, chunk);
|
||||
} else {
|
||||
/* Keep the chunk but increase its age. */
|
||||
++chunk->info.age;
|
||||
chunkp = &chunk->info.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -571,14 +586,7 @@ static const int64 JIT_SCRIPT_EIGHTH_LIFETIME = 120 * 1000 * 1000;
|
||||
JSBool
|
||||
js_InitGC(JSRuntime *rt, uint32 maxbytes)
|
||||
{
|
||||
/*
|
||||
* Make room for at least 16 chunks so the table would not grow before
|
||||
* the browser starts up.
|
||||
*/
|
||||
if (!rt->gcUserChunkSet.init(16))
|
||||
return false;
|
||||
|
||||
if (!rt->gcSystemChunkSet.init(16))
|
||||
if (!rt->gcChunkSet.init(INITIAL_CHUNK_CAPACITY))
|
||||
return false;
|
||||
|
||||
if (!rt->gcRootsHash.init(256))
|
||||
@ -720,8 +728,7 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w)
|
||||
|
||||
Chunk *chunk = Chunk::fromAddress(addr);
|
||||
|
||||
if (!trc->context->runtime->gcUserChunkSet.has(chunk) &&
|
||||
!trc->context->runtime->gcSystemChunkSet.has(chunk))
|
||||
if (!trc->context->runtime->gcChunkSet.has(chunk))
|
||||
return CGCT_NOTCHUNK;
|
||||
|
||||
/*
|
||||
@ -928,12 +935,18 @@ js_FinishGC(JSRuntime *rt)
|
||||
rt->compartments.clear();
|
||||
rt->atomsCompartment = NULL;
|
||||
|
||||
for (GCChunkSet::Range r(rt->gcUserChunkSet.all()); !r.empty(); r.popFront())
|
||||
rt->gcSystemAvailableChunkListHead = NULL;
|
||||
rt->gcUserAvailableChunkListHead = NULL;
|
||||
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
|
||||
ReleaseGCChunk(rt, r.front());
|
||||
for (GCChunkSet::Range r(rt->gcSystemChunkSet.all()); !r.empty(); r.popFront())
|
||||
ReleaseGCChunk(rt, r.front());
|
||||
rt->gcUserChunkSet.clear();
|
||||
rt->gcSystemChunkSet.clear();
|
||||
rt->gcChunkSet.clear();
|
||||
for (Chunk *chunk = rt->gcEmptyChunkListHead; chunk; ) {
|
||||
Chunk *next = chunk->info.next;
|
||||
ReleaseGCChunk(rt, chunk);
|
||||
chunk = next;
|
||||
}
|
||||
rt->gcEmptyChunkListHead = NULL;
|
||||
rt->gcEmptyChunkCount = 0;
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
rt->gcHelperThread.finish(rt);
|
||||
@ -1089,7 +1102,7 @@ JSRuntime::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
|
||||
{
|
||||
gcLastBytes = lastBytes;
|
||||
|
||||
size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER);
|
||||
size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD);
|
||||
float trigger = float(base) * GC_HEAP_GROWTH_FACTOR;
|
||||
gcTriggerBytes = size_t(Min(float(gcMaxBytes), trigger));
|
||||
}
|
||||
@ -1098,7 +1111,7 @@ void
|
||||
JSRuntime::reduceGCTriggerBytes(uint32 amount) {
|
||||
JS_ASSERT(amount > 0);
|
||||
JS_ASSERT(gcTriggerBytes - amount >= 0);
|
||||
if (gcTriggerBytes - amount < GC_ARENA_ALLOCATION_TRIGGER * GC_HEAP_GROWTH_FACTOR)
|
||||
if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR)
|
||||
return;
|
||||
gcTriggerBytes -= amount;
|
||||
}
|
||||
@ -1108,7 +1121,7 @@ JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
|
||||
{
|
||||
gcLastBytes = lastBytes;
|
||||
|
||||
size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER);
|
||||
size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD);
|
||||
float trigger = float(base) * GC_HEAP_GROWTH_FACTOR;
|
||||
gcTriggerBytes = size_t(Min(float(rt->gcMaxBytes), trigger));
|
||||
}
|
||||
@ -1117,7 +1130,7 @@ void
|
||||
JSCompartment::reduceGCTriggerBytes(uint32 amount) {
|
||||
JS_ASSERT(amount > 0);
|
||||
JS_ASSERT(gcTriggerBytes - amount >= 0);
|
||||
if (gcTriggerBytes - amount < GC_ARENA_ALLOCATION_TRIGGER * GC_HEAP_GROWTH_FACTOR)
|
||||
if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR)
|
||||
return;
|
||||
gcTriggerBytes -= amount;
|
||||
}
|
||||
@ -1932,7 +1945,7 @@ MaybeGC(JSContext *cx)
|
||||
*/
|
||||
int64 now = PRMJ_Now();
|
||||
if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) {
|
||||
if (rt->gcChunkAllocationSinceLastGC || rt->gcChunksWaitingToExpire) {
|
||||
if (rt->gcChunkAllocationSinceLastGC || rt->gcEmptyChunkListHead) {
|
||||
GCREASON(MAYBEGC);
|
||||
js_GC(cx, NULL, GC_SHRINK);
|
||||
} else {
|
||||
@ -2258,10 +2271,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
|
||||
JS_ASSERT(gcmarker.getMarkColor() == BLACK);
|
||||
rt->gcMarkingTracer = &gcmarker;
|
||||
|
||||
for (GCChunkSet::Range r(rt->gcUserChunkSet.all()); !r.empty(); r.popFront())
|
||||
r.front()->bitmap.clear();
|
||||
|
||||
for (GCChunkSet::Range r(rt->gcSystemChunkSet.all()); !r.empty(); r.popFront())
|
||||
for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
|
||||
r.front()->bitmap.clear();
|
||||
|
||||
if (comp) {
|
||||
@ -2402,9 +2412,6 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
|
||||
printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++)
|
||||
js::CheckCompartmentScripts(*c);
|
||||
}
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
@ -2696,7 +2703,7 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
||||
GCCrashData crashData;
|
||||
crashData.isRegen = rt->shapeGen & SHAPE_OVERFLOW_BIT;
|
||||
crashData.isCompartment = !!comp;
|
||||
js_SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
|
||||
crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
|
||||
|
||||
GCTIMER_BEGIN(rt, comp);
|
||||
|
||||
@ -2747,7 +2754,7 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
|
||||
rt->gcChunkAllocationSinceLastGC = false;
|
||||
GCTIMER_END(gckind == GC_LAST_CONTEXT);
|
||||
|
||||
js_SnapshotGCStack();
|
||||
crash::SnapshotGCStack();
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
@ -514,8 +514,9 @@ struct MarkingDelay {
|
||||
|
||||
/* The chunk header (located at the end of the chunk to preserve arena alignment). */
|
||||
struct ChunkInfo {
|
||||
Chunk *link;
|
||||
JSRuntime *runtime;
|
||||
Chunk *next;
|
||||
Chunk **prevp;
|
||||
ArenaHeader *emptyArenaListHead;
|
||||
size_t age;
|
||||
size_t numFree;
|
||||
@ -613,10 +614,24 @@ struct Chunk {
|
||||
return (addr & GC_CHUNK_MASK) >> ArenaShift;
|
||||
}
|
||||
|
||||
uintptr_t address() const {
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(this);
|
||||
JS_ASSERT(!(addr & GC_CHUNK_MASK));
|
||||
return addr;
|
||||
}
|
||||
|
||||
void init(JSRuntime *rt);
|
||||
bool unused();
|
||||
bool hasAvailableArenas();
|
||||
bool withinArenasRange(Cell *cell);
|
||||
|
||||
bool unused() const {
|
||||
return info.numFree == ArenasPerChunk;
|
||||
}
|
||||
|
||||
bool hasAvailableArenas() const {
|
||||
return info.numFree > 0;
|
||||
}
|
||||
|
||||
inline void addToAvailableList(JSCompartment *compartment);
|
||||
inline void removeFromAvailableList();
|
||||
|
||||
template <size_t thingSize>
|
||||
ArenaHeader *allocateArena(JSContext *cx, unsigned thingKind);
|
||||
@ -750,13 +765,12 @@ Cell::compartment() const
|
||||
/*
|
||||
* Lower limit after which we limit the heap growth
|
||||
*/
|
||||
const size_t GC_ARENA_ALLOCATION_TRIGGER = 30 * js::GC_CHUNK_SIZE;
|
||||
const size_t GC_ALLOCATION_THRESHOLD = 30 * 1024 * 1024;
|
||||
|
||||
/*
|
||||
* A GC is triggered once the number of newly allocated arenas
|
||||
* is GC_HEAP_GROWTH_FACTOR times the number of live arenas after
|
||||
* the last GC starting after the lower limit of
|
||||
* GC_ARENA_ALLOCATION_TRIGGER.
|
||||
* A GC is triggered once the number of newly allocated arenas is
|
||||
* GC_HEAP_GROWTH_FACTOR times the number of live arenas after the last GC
|
||||
* starting after the lower limit of GC_ALLOCATION_THRESHOLD.
|
||||
*/
|
||||
const float GC_HEAP_GROWTH_FACTOR = 3.0f;
|
||||
|
||||
@ -998,9 +1012,17 @@ struct FreeLists {
|
||||
extern void *
|
||||
RefillFinalizableFreeList(JSContext *cx, unsigned thingKind);
|
||||
|
||||
} /* namespace gc */
|
||||
/*
|
||||
* Initial allocation size for data structures holding chunks is set to hold
|
||||
* chunks with total capacity of 16MB to avoid buffer resizes during browser
|
||||
* startup.
|
||||
*/
|
||||
const size_t INITIAL_CHUNK_CAPACITY = 16 * 1024 * 1024 / GC_CHUNK_SIZE;
|
||||
|
||||
typedef Vector<gc::Chunk *, 32, SystemAllocPolicy> GCChunks;
|
||||
/* The number of GC cycles an empty chunk can survive before been released. */
|
||||
const size_t MAX_EMPTY_CHUNK_AGE = 4;
|
||||
|
||||
} /* namespace gc */
|
||||
|
||||
struct GCPtrHasher
|
||||
{
|
||||
|
@ -100,13 +100,6 @@ PushMarkStack(GCMarker *gcmarker, JSShortString *thing);
|
||||
static inline void
|
||||
PushMarkStack(GCMarker *gcmarker, JSString *thing);
|
||||
|
||||
static void
|
||||
volatile_memcpy(volatile unsigned char *dst, const void *src, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
dst[i] = ((char *)src)[i];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
Mark(JSTracer *trc, T *thing)
|
||||
@ -122,15 +115,9 @@ Mark(JSTracer *trc, T *thing)
|
||||
JS_ASSERT(thing->compartment());
|
||||
JS_ASSERT(thing->compartment()->rt == rt);
|
||||
|
||||
if (rt->gcCheckCompartment && thing->compartment() != rt->gcCheckCompartment &&
|
||||
thing->compartment() != rt->atomsCompartment)
|
||||
{
|
||||
volatile unsigned char dbg[sizeof(T) + 2];
|
||||
dbg[0] = 0xab;
|
||||
dbg[1] = 0xcd;
|
||||
volatile_memcpy(dbg + 2, thing, sizeof(T));
|
||||
JS_Assert("compartment mismatch in GC", __FILE__, __LINE__);
|
||||
}
|
||||
JS_OPT_ASSERT_IF(rt->gcCheckCompartment,
|
||||
thing->compartment() == rt->gcCheckCompartment ||
|
||||
thing->compartment() == rt->atomsCompartment);
|
||||
|
||||
/*
|
||||
* Don't mark things outside a compartment if we are in a per-compartment
|
||||
|
@ -156,6 +156,19 @@ js::GetBlockChain(JSContext *cx, StackFrame *fp)
|
||||
|
||||
JSScript *script = fp->script();
|
||||
jsbytecode *start = script->code;
|
||||
|
||||
/*
|
||||
* If the debugger asks for the scope chain at a pc where we are about to
|
||||
* fix it up, advance target past the fixup. See bug 672804.
|
||||
*/
|
||||
JSOp op = js_GetOpcode(cx, script, target);
|
||||
while (op == JSOP_NOP || op == JSOP_INDEXBASE || op == JSOP_INDEXBASE1 ||
|
||||
op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3 ||
|
||||
op == JSOP_BLOCKCHAIN || op == JSOP_NULLBLOCKCHAIN)
|
||||
{
|
||||
target += js_CodeSpec[op].length;
|
||||
op = js_GetOpcode(cx, script, target);
|
||||
}
|
||||
JS_ASSERT(target >= start && target < start + script->length);
|
||||
|
||||
JSObject *blockChain = NULL;
|
||||
|
@ -3494,24 +3494,6 @@ CopySlots(JSContext *cx, JSObject *from, JSObject *to)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
CheckProxy(JSObject *obj)
|
||||
{
|
||||
if (!obj->isProxy())
|
||||
return;
|
||||
|
||||
JSProxyHandler *handler = obj->getProxyHandler();
|
||||
if (handler->isCrossCompartment())
|
||||
return;
|
||||
|
||||
Value priv = obj->getProxyPrivate();
|
||||
if (!priv.isObject())
|
||||
return;
|
||||
|
||||
if (priv.toObject().compartment() != obj->compartment())
|
||||
JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent)
|
||||
{
|
||||
@ -3549,8 +3531,6 @@ JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CheckProxy(clone);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@ -3663,11 +3643,6 @@ JSObject::swap(JSContext *cx, JSObject *other)
|
||||
TradeGuts(this, otherClone);
|
||||
TradeGuts(other, thisClone);
|
||||
|
||||
CheckProxy(this);
|
||||
CheckProxy(other);
|
||||
CheckProxy(thisClone);
|
||||
CheckProxy(otherClone);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -350,14 +350,14 @@ inline uint32
|
||||
JSObject::getArrayLength() const
|
||||
{
|
||||
JS_ASSERT(isArray());
|
||||
return (uint32)(size_t) getPrivate();
|
||||
return (uint32)(uintptr_t) getPrivate();
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::setArrayLength(uint32 length)
|
||||
{
|
||||
JS_ASSERT(isArray());
|
||||
setPrivate((void*)(size_t)length);
|
||||
setPrivate((void*)(uintptr_t) length);
|
||||
}
|
||||
|
||||
inline uint32
|
||||
|
@ -4965,14 +4965,16 @@ CloneLeftHandSide(JSParseNode *opn, JSTreeContext *tc)
|
||||
|
||||
pn->pn_link = dn->dn_uses;
|
||||
dn->dn_uses = pn;
|
||||
} else if (opn->pn_defn) {
|
||||
/* We copied some definition-specific state into pn. Clear it out. */
|
||||
} else {
|
||||
pn->pn_expr = NULL;
|
||||
pn->pn_cookie.makeFree();
|
||||
pn->pn_dflags &= ~PND_BOUND;
|
||||
pn->pn_defn = false;
|
||||
if (opn->pn_defn) {
|
||||
/* We copied some definition-specific state into pn. Clear it out. */
|
||||
pn->pn_cookie.makeFree();
|
||||
pn->pn_dflags &= ~PND_BOUND;
|
||||
pn->pn_defn = false;
|
||||
|
||||
LinkUseToDef(pn, (JSDefinition *) opn, tc);
|
||||
LinkUseToDef(pn, (JSDefinition *) opn, tc);
|
||||
}
|
||||
}
|
||||
return pn;
|
||||
}
|
||||
|
@ -1171,11 +1171,6 @@ NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObje
|
||||
else
|
||||
clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass;
|
||||
|
||||
if (!handler->isCrossCompartment() && priv.isObject()) {
|
||||
if (priv.toObject().compartment() != cx->compartment)
|
||||
JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
JSObject *obj = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent);
|
||||
if (!obj || !obj->ensureInstanceReservedSlots(cx, 0))
|
||||
return NULL;
|
||||
|
@ -92,10 +92,6 @@ class JS_FRIEND_API(JSProxyHandler) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool isCrossCompartment() {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void *family() {
|
||||
return mFamily;
|
||||
}
|
||||
|
@ -166,6 +166,7 @@ typedef struct JSCrossCompartmentCall JSCrossCompartmentCall;
|
||||
typedef struct JSStructuredCloneWriter JSStructuredCloneWriter;
|
||||
typedef struct JSStructuredCloneReader JSStructuredCloneReader;
|
||||
typedef struct JSStructuredCloneCallbacks JSStructuredCloneCallbacks;
|
||||
typedef struct JSPropertyName JSPropertyName;
|
||||
|
||||
#ifdef __cplusplus
|
||||
typedef class JSWrapper JSWrapper;
|
||||
|
@ -2131,8 +2131,6 @@ TokenStream::getTokenInternal()
|
||||
return tt;
|
||||
|
||||
error:
|
||||
JS_ASSERT(cx->isExceptionPending());
|
||||
|
||||
/*
|
||||
* For erroneous multi-line tokens we won't have changed end.lineno (it'll
|
||||
* still be equal to begin.lineno) so we revert end.index to be equal to
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "jstypes.h"
|
||||
#include "jsstdint.h"
|
||||
#include "jsutil.h"
|
||||
#include "jscrashreport.h"
|
||||
#include "jsprf.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsatom.h"
|
||||
@ -288,44 +289,26 @@ Bindings::trace(JSTracer *trc)
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
static void
|
||||
volatile_memcpy(volatile char *dst, void *src, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
dst[i] = ((char *)src)[i];
|
||||
}
|
||||
|
||||
static void
|
||||
CheckScript(JSScript *script, JSScript *prev)
|
||||
{
|
||||
volatile char dbg1[sizeof(JSScript)], dbg2[sizeof(JSScript)];
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
if (script->cookie1 != JS_SCRIPT_COOKIE || script->cookie2 != JS_SCRIPT_COOKIE) {
|
||||
volatile_memcpy(dbg1, script, sizeof(JSScript));
|
||||
if (prev)
|
||||
volatile_memcpy(dbg2, prev, sizeof(JSScript));
|
||||
crash::StackBuffer<sizeof(JSScript), 0x87> buf1(script);
|
||||
crash::StackBuffer<sizeof(JSScript), 0x88> buf2(prev);
|
||||
JS_OPT_ASSERT(false);
|
||||
}
|
||||
JS_OPT_ASSERT(script->cookie1 == JS_SCRIPT_COOKIE && script->cookie2 == JS_SCRIPT_COOKIE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
CheckScriptOwner(JSScript *script, JSObject *owner)
|
||||
{
|
||||
if (script->ownerObject != owner) {
|
||||
volatile char scriptData[sizeof(JSScript)];
|
||||
volatile char owner1Data[sizeof(JSObject)], owner2Data[sizeof(JSObject)];
|
||||
volatile char savedOwner[sizeof(JSObject *)];
|
||||
|
||||
volatile_memcpy(scriptData, script, sizeof(JSScript));
|
||||
volatile_memcpy(savedOwner, &owner, sizeof(JSObject *));
|
||||
if (script->ownerObject != JS_NEW_SCRIPT && script->ownerObject != JS_CACHED_SCRIPT)
|
||||
volatile_memcpy(owner1Data, script->ownerObject, sizeof(JSObject));
|
||||
if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT)
|
||||
volatile_memcpy(owner2Data, owner, sizeof(JSObject));
|
||||
}
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
JS_OPT_ASSERT(script->ownerObject == owner);
|
||||
|
||||
if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT)
|
||||
JS_OPT_ASSERT(script->compartment == owner->compartment());
|
||||
#endif
|
||||
}
|
||||
|
||||
#if JS_HAS_XDR
|
||||
@ -982,8 +965,10 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
|
||||
return NULL;
|
||||
|
||||
PodZero(script);
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
script->cookie1 = script->cookie2 = JS_SCRIPT_COOKIE;
|
||||
script->ownerObject = JS_NEW_SCRIPT;
|
||||
#endif
|
||||
script->length = length;
|
||||
script->version = version;
|
||||
new (&script->bindings) Bindings(cx, emptyCallShape);
|
||||
@ -1306,8 +1291,10 @@ JSScript::totalSize()
|
||||
void
|
||||
JSScript::setOwnerObject(JSObject *owner)
|
||||
{
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
CheckScriptOwner(this, JS_NEW_SCRIPT);
|
||||
ownerObject = owner;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1349,22 +1336,6 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
|
||||
JS_ClearScriptTraps(cx, script);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
void
|
||||
CheckCompartmentScripts(JSCompartment *comp)
|
||||
{
|
||||
JSScript *prev = NULL;
|
||||
for (JSScript *script = (JSScript *)comp->scripts.next;
|
||||
&script->links != &comp->scripts;
|
||||
prev = script, script = (JSScript *)script->links.next)
|
||||
{
|
||||
CheckScript(script, prev);
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
static void
|
||||
DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller)
|
||||
{
|
||||
@ -1429,7 +1400,7 @@ DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller)
|
||||
if (script->sourceMap)
|
||||
cx->free_(script->sourceMap);
|
||||
|
||||
memset(script, 0xdb, script->totalSize());
|
||||
JS_POISON(script, 0xdb, sizeof(JSScript));
|
||||
*(uint32 *)script = caller;
|
||||
cx->free_(script);
|
||||
}
|
||||
@ -1464,9 +1435,10 @@ js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner)
|
||||
if (owner)
|
||||
CheckScriptOwner(script, owner);
|
||||
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
JSRuntime *rt = trc->context->runtime;
|
||||
if (rt->gcCheckCompartment && script->compartment != rt->gcCheckCompartment)
|
||||
JS_Assert("compartment mismatch in GC", __FILE__, __LINE__);
|
||||
JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment == rt->gcCheckCompartment);
|
||||
#endif
|
||||
|
||||
JSAtomMap *map = &script->atomMap;
|
||||
MarkAtomRange(trc, map->length, map->vector, "atomMap");
|
||||
|
@ -449,7 +449,9 @@ struct JSScript {
|
||||
jsbytecode *code; /* bytecodes and their immediate operands */
|
||||
uint32 length; /* length of code vector */
|
||||
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
uint32 cookie1;
|
||||
#endif
|
||||
|
||||
private:
|
||||
uint16 version; /* JS version under which script was compiled */
|
||||
@ -507,7 +509,9 @@ struct JSScript {
|
||||
JSPrincipals *principals;/* principals for this script */
|
||||
jschar *sourceMap; /* source map file or null */
|
||||
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
JSObject *ownerObject;
|
||||
#endif
|
||||
|
||||
void setOwnerObject(JSObject *owner);
|
||||
|
||||
@ -535,7 +539,9 @@ struct JSScript {
|
||||
/* array of execution counters for every JSOp in the script, by runmode */
|
||||
JSPCCounters pcCounters;
|
||||
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
uint32 cookie2;
|
||||
#endif
|
||||
|
||||
public:
|
||||
#ifdef JS_METHODJIT
|
||||
@ -742,19 +748,6 @@ js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner);
|
||||
extern void
|
||||
js_DestroyCachedScript(JSContext *cx, JSScript *script);
|
||||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* This diagnostic function checks that a compartment's list of scripts
|
||||
* contains only valid scripts. It also searches for the target script
|
||||
* in the list. If expected is true, it asserts that the target script
|
||||
* is found. If expected is false, it asserts that it's not found.
|
||||
*/
|
||||
void
|
||||
CheckCompartmentScripts(JSCompartment *comp);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
extern void
|
||||
js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner);
|
||||
|
||||
|
@ -1535,11 +1535,8 @@ MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
|
||||
}
|
||||
|
||||
Value v;
|
||||
if (!res->createLastMatch(cx, &v))
|
||||
return false;
|
||||
|
||||
JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
|
||||
return !!arrayobj->setProperty(cx, INT_TO_JSID(count), &v, false);
|
||||
return res->createLastMatch(cx, &v) &&
|
||||
arrayobj->defineProperty(cx, INT_TO_JSID(count), v);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
@ -10467,7 +10467,10 @@ TraceRecorder::record_EnterFrame()
|
||||
|
||||
/* Try inlining one level in case this recursion doesn't go too deep. */
|
||||
if (fp->script() == fp->prev()->script() &&
|
||||
fp->prev()->prev() && fp->prev()->prev()->script() == fp->script()) {
|
||||
fp->prev()->prev() &&
|
||||
fp->prev()->prev()->isScriptFrame() &&
|
||||
fp->prev()->prev()->script() == fp->script())
|
||||
{
|
||||
RETURN_STOP_A("recursion started inlining");
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,6 @@
|
||||
#define jsutil_h___
|
||||
|
||||
#include "jstypes.h"
|
||||
#include "jscrashreport.h"
|
||||
#include "mozilla/Util.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -62,12 +61,24 @@ JS_BEGIN_EXTERN_C
|
||||
|
||||
#define JS_FREE_PATTERN 0xDA
|
||||
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
|
||||
#define JS_POISON(p, val, size) memset((p), (val), (size))
|
||||
|
||||
#define JS_OPT_ASSERT(expr) \
|
||||
((expr) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__))
|
||||
|
||||
#define JS_OPT_ASSERT_IF(cond, expr) \
|
||||
((!(cond) || (expr)) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__))
|
||||
|
||||
#else
|
||||
|
||||
#define JS_POISON(p, val, size) ((void) 0)
|
||||
#define JS_OPT_ASSERT(expr) ((void) 0)
|
||||
#define JS_OPT_ASSERT_IF(cond, expr) ((void) 0)
|
||||
|
||||
#endif /* JS_CRASH_DIAGNOSTICS */
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define JS_ASSERT(expr) \
|
||||
@ -227,12 +238,6 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_counter; /* data race, who cares. */
|
||||
#define JS_OOM_POSSIBLY_FAIL() do {} while(0)
|
||||
#endif
|
||||
|
||||
static JS_INLINE void *js_record_oom(void *p) {
|
||||
if (!p)
|
||||
js_SnapshotErrorStack();
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* SpiderMonkey code should not be calling these allocation functions directly.
|
||||
* Instead, all calls should go through JSRuntime, JSContext or OffTheBooks.
|
||||
@ -240,17 +245,17 @@ static JS_INLINE void *js_record_oom(void *p) {
|
||||
*/
|
||||
static JS_INLINE void* js_malloc(size_t bytes) {
|
||||
JS_OOM_POSSIBLY_FAIL();
|
||||
return js_record_oom(malloc(bytes));
|
||||
return malloc(bytes);
|
||||
}
|
||||
|
||||
static JS_INLINE void* js_calloc(size_t bytes) {
|
||||
JS_OOM_POSSIBLY_FAIL();
|
||||
return js_record_oom(calloc(bytes, 1));
|
||||
return calloc(bytes, 1);
|
||||
}
|
||||
|
||||
static JS_INLINE void* js_realloc(void* p, size_t bytes) {
|
||||
JS_OOM_POSSIBLY_FAIL();
|
||||
return js_record_oom(realloc(p, bytes));
|
||||
return realloc(p, bytes);
|
||||
}
|
||||
|
||||
static JS_INLINE void js_free(void* p) {
|
||||
@ -271,13 +276,13 @@ JS_END_EXTERN_C
|
||||
*
|
||||
* Allocation:
|
||||
* - Prefer to allocate using JSContext:
|
||||
* cx->{malloc_,realloc_,calloc_,new_,new_array}
|
||||
* cx->{malloc_,realloc_,calloc_,new_,array_new}
|
||||
*
|
||||
* - If no JSContext is available, use a JSRuntime:
|
||||
* rt->{malloc_,realloc_,calloc_,new_,new_array}
|
||||
* rt->{malloc_,realloc_,calloc_,new_,array_new}
|
||||
*
|
||||
* - As a last resort, use unaccounted allocation ("OffTheBooks"):
|
||||
* js::OffTheBooks::{malloc_,realloc_,calloc_,new_,new_array}
|
||||
* js::OffTheBooks::{malloc_,realloc_,calloc_,new_,array_new}
|
||||
*
|
||||
* Deallocation:
|
||||
* - When the deallocation occurs on a slow path, use:
|
||||
|
@ -956,6 +956,8 @@ typedef void
|
||||
|
||||
class AutoIdVector;
|
||||
|
||||
class PropertyName;
|
||||
|
||||
/*
|
||||
* Prepare to make |obj| non-extensible; in particular, fully resolve its properties.
|
||||
* On error, return false.
|
||||
@ -983,6 +985,9 @@ static inline JSCheckAccessOp Jsvalify(CheckAccessOp f) { return (JSChec
|
||||
static inline EqualityOp Valueify(JSEqualityOp f); /* Same type as JSHasInstanceOp */
|
||||
static inline JSEqualityOp Jsvalify(EqualityOp f); /* Same type as HasInstanceOp */
|
||||
|
||||
static inline PropertyName *Valueify(JSPropertyName *n) { return (PropertyName *)n; }
|
||||
static inline JSPropertyName *Jsvalify(PropertyName *n) { return (JSPropertyName *)n; }
|
||||
|
||||
static const PropertyOp PropertyStub = (PropertyOp)JS_PropertyStub;
|
||||
static const StrictPropertyOp StrictPropertyStub = (StrictPropertyOp)JS_StrictPropertyStub;
|
||||
static const JSEnumerateOp EnumerateStub = JS_EnumerateStub;
|
||||
|
@ -413,7 +413,7 @@ ForceFrame::enter()
|
||||
JSObject *scopeChain = target->getGlobal();
|
||||
JS_ASSERT(scopeChain->isNative());
|
||||
|
||||
return context->stack.pushDummyFrame(context, *scopeChain, frame);
|
||||
return context->stack.pushDummyFrame(context, REPORT_ERROR, *scopeChain, frame);
|
||||
}
|
||||
|
||||
AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
|
||||
@ -442,8 +442,21 @@ AutoCompartment::enter()
|
||||
JS_ASSERT(scopeChain->isNative());
|
||||
|
||||
frame.construct();
|
||||
if (!context->stack.pushDummyFrame(context, *scopeChain, &frame.ref()))
|
||||
|
||||
/*
|
||||
* Set the compartment eagerly so that pushDummyFrame associates the
|
||||
* resource allocation request with 'destination' instead of 'origin'.
|
||||
* (This is important when content has overflowed the stack and chrome
|
||||
* is preparing to run JS to throw up a slow script dialog.) However,
|
||||
* if an exception is thrown, we need it to be in origin's compartment
|
||||
* so be careful to only report after restoring.
|
||||
*/
|
||||
context->compartment = destination;
|
||||
if (!context->stack.pushDummyFrame(context, DONT_REPORT_ERROR, *scopeChain, &frame.ref())) {
|
||||
context->compartment = origin;
|
||||
js_ReportOverRecursed(context);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context->isExceptionPending())
|
||||
context->wrapPendingException();
|
||||
|
@ -155,10 +155,6 @@ class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper {
|
||||
|
||||
virtual void trace(JSTracer *trc, JSObject *wrapper);
|
||||
|
||||
virtual bool isCrossCompartment() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSCrossCompartmentWrapper singleton;
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@ script 15.5.4.2.js
|
||||
script 15.5.4.7.js
|
||||
script 15.5.4.11-01.js
|
||||
script defaultvalue.js
|
||||
script match-defines-match-elements.js
|
||||
script split-01.js
|
||||
script split-undefined-separator.js
|
||||
script split-xregexp.js
|
||||
|
47
js/src/tests/ecma_5/String/match-defines-match-elements.js
Normal file
47
js/src/tests/ecma_5/String/match-defines-match-elements.js
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
var BUGNUMBER = 677820;
|
||||
var summary =
|
||||
"String.prototype.match must define matches on the returned array, not set " +
|
||||
"them";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
var called = false;
|
||||
function setterFunction(v) { called = true; }
|
||||
function getterFunction(v) { return "getter"; }
|
||||
|
||||
Object.defineProperty(Array.prototype, 1,
|
||||
{ get: getterFunction, set: setterFunction });
|
||||
|
||||
assertEq(called, false);
|
||||
var matches = "abcdef".match(/./g);
|
||||
assertEq(called, false);
|
||||
assertEq(matches.length, 6);
|
||||
assertEq(matches[0], "a");
|
||||
assertEq(matches[1], "b");
|
||||
assertEq(matches[2], "c");
|
||||
assertEq(matches[3], "d");
|
||||
assertEq(matches[4], "e");
|
||||
assertEq(matches[5], "f");
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(Array.prototype, 1);
|
||||
assertEq(desc.get, getterFunction);
|
||||
assertEq(desc.set, setterFunction);
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.configurable, false);
|
||||
assertEq([][1], "getter");
|
||||
|
||||
assertEq(called, false);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
||||
print("Tests complete");
|
@ -53,5 +53,8 @@ script weakmap.js
|
||||
script regress-645160.js
|
||||
script regress-650753.js
|
||||
script regress-668438.js
|
||||
require-or(debugMode,skip) script regress-672804-1.js
|
||||
require-or(debugMode,skip) script regress-672804-2.js
|
||||
require-or(debugMode,skip) script regress-672804-3.js
|
||||
skip-if(!xulRuntime.shell) script regress-677589.js
|
||||
script regress-677924.js
|
||||
|
12
js/src/tests/js1_8_5/extensions/regress-672804-1.js
Normal file
12
js/src/tests/js1_8_5/extensions/regress-672804-1.js
Normal file
@ -0,0 +1,12 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
var a = 0;
|
||||
function f() {
|
||||
let (a = let (x = 1) x) {}
|
||||
}
|
||||
|
||||
trap(f, 3, 'assertEq(evalInFrame(1, "a"), 0)');
|
||||
f();
|
||||
|
||||
reportCompare(0, 0, 'ok');
|
12
js/src/tests/js1_8_5/extensions/regress-672804-2.js
Normal file
12
js/src/tests/js1_8_5/extensions/regress-672804-2.js
Normal file
@ -0,0 +1,12 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
var a = 0;
|
||||
function f() {
|
||||
let (a = let (x = 1) x) {}
|
||||
}
|
||||
|
||||
trap(f, 4, 'assertEq(evalInFrame(1, "a"), 0)');
|
||||
f();
|
||||
|
||||
reportCompare(0, 0, 'ok');
|
11
js/src/tests/js1_8_5/extensions/regress-672804-3.js
Normal file
11
js/src/tests/js1_8_5/extensions/regress-672804-3.js
Normal file
@ -0,0 +1,11 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
var e = [], x = {b: []};
|
||||
function f() {
|
||||
let (a = [[] for (x in e)], {b: []} = x) {}
|
||||
}
|
||||
trap(f, 4, '');
|
||||
f();
|
||||
|
||||
reportCompare(0, 0, 'ok');
|
@ -109,6 +109,7 @@ script regress-646820-3.js
|
||||
script regress-665355.js
|
||||
script regress-666599.js
|
||||
script regress-667047.js
|
||||
script regress-672892.js
|
||||
script regress-673070-1.js
|
||||
script regress-673070-2.js
|
||||
script regress-673070-3.js
|
||||
|
8
js/src/tests/js1_8_5/regress/regress-672892.js
Normal file
8
js/src/tests/js1_8_5/regress/regress-672892.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
with (0)
|
||||
for (var b = 0 in 0) // don't assert in parser
|
||||
;
|
||||
|
||||
reportCompare(0, 0, 'ok');
|
@ -388,8 +388,7 @@ StackSpace::ensureEnoughSpaceToEnterTrace(JSContext *cx)
|
||||
|
||||
STATIC_POSTCONDITION(!return || ubound(from) >= nvals)
|
||||
JS_ALWAYS_INLINE bool
|
||||
StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
|
||||
JSCompartment *dest) const
|
||||
StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
|
||||
{
|
||||
assertInvariants();
|
||||
JS_ASSERT(from >= firstUnused());
|
||||
@ -397,16 +396,10 @@ StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptr
|
||||
JS_ASSERT(from <= commitEnd_);
|
||||
#endif
|
||||
if (JS_UNLIKELY(conservativeEnd_ - from < nvals))
|
||||
return ensureSpaceSlow(cx, report, from, nvals, dest);
|
||||
return ensureSpaceSlow(cx, report, from, nvals);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
|
||||
{
|
||||
return ensureSpace(cx, report, from, nvals, cx->compartment);
|
||||
}
|
||||
|
||||
inline Value *
|
||||
StackSpace::getStackLimit(JSContext *cx, MaybeReportError report)
|
||||
{
|
||||
|
@ -414,13 +414,13 @@ StackSpace::mark(JSTracer *trc)
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
|
||||
JSCompartment *dest) const
|
||||
StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report,
|
||||
Value *from, ptrdiff_t nvals) const
|
||||
{
|
||||
assertInvariants();
|
||||
|
||||
JS_ASSERT_IF(dest, cx);
|
||||
bool trusted = !dest || dest->principals == cx->runtime->trustedPrincipals();
|
||||
bool trusted = !cx->compartment ||
|
||||
cx->compartment->principals == cx->runtime->trustedPrincipals();
|
||||
Value *end = trusted ? trustedEnd_ : defaultEnd_;
|
||||
|
||||
/*
|
||||
@ -548,17 +548,17 @@ ContextStack::containsSlow(const StackFrame *target) const
|
||||
*/
|
||||
Value *
|
||||
ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
|
||||
MaybeExtend extend, bool *pushedSeg, JSCompartment *dest)
|
||||
MaybeExtend extend, bool *pushedSeg)
|
||||
{
|
||||
Value *firstUnused = space().firstUnused();
|
||||
|
||||
if (onTop() && extend) {
|
||||
if (!space().ensureSpace(cx, report, firstUnused, nvars, dest))
|
||||
if (!space().ensureSpace(cx, report, firstUnused, nvars))
|
||||
return NULL;
|
||||
return firstUnused;
|
||||
}
|
||||
|
||||
if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars, dest))
|
||||
if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars))
|
||||
return NULL;
|
||||
|
||||
FrameRegs *regs;
|
||||
@ -577,13 +577,6 @@ ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
|
||||
return seg_->slotsBegin();
|
||||
}
|
||||
|
||||
Value *
|
||||
ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
|
||||
MaybeExtend extend, bool *pushedSeg)
|
||||
{
|
||||
return ensureOnTop(cx, report, nvars, extend, pushedSeg, cx->compartment);
|
||||
}
|
||||
|
||||
void
|
||||
ContextStack::popSegment()
|
||||
{
|
||||
@ -703,12 +696,11 @@ ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thi
|
||||
}
|
||||
|
||||
bool
|
||||
ContextStack::pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg)
|
||||
ContextStack::pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain,
|
||||
DummyFrameGuard *dfg)
|
||||
{
|
||||
JSCompartment *dest = scopeChain.compartment();
|
||||
|
||||
uintN nvars = VALUES_PER_STACK_FRAME;
|
||||
Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &dfg->pushedSeg_, dest);
|
||||
Value *firstUnused = ensureOnTop(cx, report, nvars, CAN_EXTEND, &dfg->pushedSeg_);
|
||||
if (!firstUnused)
|
||||
return NULL;
|
||||
|
||||
@ -716,7 +708,6 @@ ContextStack::pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuar
|
||||
fp->initDummyFrame(cx, scopeChain);
|
||||
dfg->regs_.initDummyFrame(*fp);
|
||||
|
||||
cx->compartment = dest;
|
||||
dfg->prevRegs_ = seg_->pushRegs(dfg->regs_);
|
||||
JS_ASSERT(space().firstUnused() == dfg->regs_.sp);
|
||||
dfg->setPushed(*this);
|
||||
@ -799,11 +790,24 @@ ContextStack::popGeneratorFrame(const GeneratorFrameGuard &gfg)
|
||||
bool
|
||||
ContextStack::saveFrameChain()
|
||||
{
|
||||
JSCompartment *dest = NULL;
|
||||
/*
|
||||
* The StackSpace uses the context's current compartment to determine
|
||||
* whether to allow access to the privileged end-of-stack buffer.
|
||||
* However, we always want saveFrameChain to have access to this privileged
|
||||
* buffer since it gets used to prepare calling trusted JS. To force this,
|
||||
* we clear the current compartment (which is interpreted by ensureSpace as
|
||||
* 'trusted') and either restore it on OOM or let resetCompartment()
|
||||
* clobber it.
|
||||
*/
|
||||
JSCompartment *original = cx_->compartment;
|
||||
cx_->compartment = NULL;
|
||||
|
||||
bool pushedSeg;
|
||||
if (!ensureOnTop(cx_, REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg, dest))
|
||||
if (!ensureOnTop(cx_, DONT_REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg)) {
|
||||
cx_->compartment = original;
|
||||
js_ReportOverRecursed(cx_);
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_ASSERT(pushedSeg);
|
||||
JS_ASSERT(!hasfp());
|
||||
|
@ -44,7 +44,6 @@
|
||||
#include "jsfun.h"
|
||||
|
||||
struct JSContext;
|
||||
struct JSCompartment;
|
||||
|
||||
namespace js {
|
||||
|
||||
@ -1330,15 +1329,10 @@ class StackSpace
|
||||
friend class ContextStack;
|
||||
friend class StackFrame;
|
||||
|
||||
inline bool ensureSpace(JSContext *cx, MaybeReportError report,
|
||||
Value *from, ptrdiff_t nvals,
|
||||
JSCompartment *dest) const;
|
||||
inline bool ensureSpace(JSContext *cx, MaybeReportError report,
|
||||
Value *from, ptrdiff_t nvals) const;
|
||||
JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report,
|
||||
Value *from, ptrdiff_t nvals,
|
||||
JSCompartment *dest) const;
|
||||
|
||||
Value *from, ptrdiff_t nvals) const;
|
||||
StackSegment &findContainingSegment(const StackFrame *target) const;
|
||||
|
||||
public:
|
||||
@ -1426,9 +1420,6 @@ class ContextStack
|
||||
/* Implementation details of push* public interface. */
|
||||
StackSegment *pushSegment(JSContext *cx);
|
||||
enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
|
||||
Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
|
||||
MaybeExtend extend, bool *pushedSeg,
|
||||
JSCompartment *dest);
|
||||
Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
|
||||
MaybeExtend extend, bool *pushedSeg);
|
||||
|
||||
@ -1511,16 +1502,9 @@ class ContextStack
|
||||
*/
|
||||
bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
|
||||
|
||||
/*
|
||||
* When changing the compartment of a cx, it is necessary to immediately
|
||||
* change the scope chain to a global in the right compartment since any
|
||||
* amount of general VM code can run before the first scripted frame is
|
||||
* pushed (if at all). This is currently and hackily accomplished by
|
||||
* pushing a "dummy frame" with the correct scope chain. On success, this
|
||||
* function will change the compartment to 'scopeChain.compartment()' and
|
||||
* push a dummy frame for 'scopeChain'. On failure, nothing is changed.
|
||||
*/
|
||||
bool pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg);
|
||||
/* Pushes a "dummy" frame; should be removed one day. */
|
||||
bool pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain,
|
||||
DummyFrameGuard *dfg);
|
||||
|
||||
/*
|
||||
* An "inline frame" may only be pushed from within the top, active
|
||||
|
@ -89,6 +89,21 @@ JSDependentString::new_(JSContext *cx, JSLinearString *base, const jschar *chars
|
||||
return str;
|
||||
}
|
||||
|
||||
inline js::PropertyName *
|
||||
JSFlatString::toPropertyName(JSContext *cx)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
uint32 dummy;
|
||||
JS_ASSERT(!isElement(&dummy));
|
||||
#endif
|
||||
if (isAtom())
|
||||
return asAtom().asPropertyName();
|
||||
JSAtom *atom = js_AtomizeString(cx, this);
|
||||
if (!atom)
|
||||
return NULL;
|
||||
return atom->asPropertyName();
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE void
|
||||
JSFixedString::init(const jschar *chars, size_t length)
|
||||
{
|
||||
|
@ -38,9 +38,12 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "mozilla/RangedPtr.h"
|
||||
|
||||
#include "String.h"
|
||||
#include "String-inl.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace js;
|
||||
|
||||
bool
|
||||
@ -317,3 +320,52 @@ JSDependentString::undepend(JSContext *cx)
|
||||
JSStringFinalizeOp JSExternalString::str_finalizers[JSExternalString::TYPE_LIMIT] = {
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
bool
|
||||
JSFlatString::isElement(uint32 *indexp) const
|
||||
{
|
||||
const jschar *s = charsZ();
|
||||
jschar ch = *s;
|
||||
|
||||
if (!JS7_ISDEC(ch))
|
||||
return false;
|
||||
|
||||
size_t n = length();
|
||||
if (n > UINT32_CHAR_BUFFER_LENGTH)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Make sure to account for the '\0' at the end of characters, dereferenced
|
||||
* in the loop below.
|
||||
*/
|
||||
RangedPtr<const jschar> cp(s, n + 1);
|
||||
const RangedPtr<const jschar> end(s + n, s, n + 1);
|
||||
|
||||
uint32 index = JS7_UNDEC(*cp++);
|
||||
uint32 oldIndex = 0;
|
||||
uint32 c = 0;
|
||||
|
||||
if (index != 0) {
|
||||
while (JS7_ISDEC(*cp)) {
|
||||
oldIndex = index;
|
||||
c = JS7_UNDEC(*cp);
|
||||
index = 10 * index + c;
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
|
||||
/* It's not an element if there are characters after the number. */
|
||||
if (cp != end)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Look out for "4294967296" and larger-number strings that fit in
|
||||
* UINT32_CHAR_BUFFER_LENGTH: only unsigned 32-bit integers shall pass.
|
||||
*/
|
||||
if (oldIndex < UINT32_MAX / 10 || (oldIndex == UINT32_MAX / 10 && c <= (UINT32_MAX % 10))) {
|
||||
*indexp = index;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -43,6 +43,7 @@
|
||||
|
||||
#include "jscell.h"
|
||||
|
||||
class JSString;
|
||||
class JSDependentString;
|
||||
class JSExtensibleString;
|
||||
class JSExternalString;
|
||||
@ -52,6 +53,29 @@ class JSStaticAtom;
|
||||
class JSRope;
|
||||
class JSAtom;
|
||||
|
||||
namespace js {
|
||||
|
||||
class PropertyName;
|
||||
|
||||
/* The buffer length required to contain any unsigned 32-bit integer. */
|
||||
static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1;
|
||||
|
||||
/* N.B. must correspond to boolean tagging behavior. */
|
||||
enum InternBehavior
|
||||
{
|
||||
DoNotInternAtom = false,
|
||||
InternAtom = true
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/*
|
||||
* Find or create the atom for a string. Return null on failure to allocate
|
||||
* memory.
|
||||
*/
|
||||
extern JSAtom *
|
||||
js_AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom);
|
||||
|
||||
/*
|
||||
* JavaScript strings
|
||||
*
|
||||
@ -474,6 +498,21 @@ class JSFlatString : public JSLinearString
|
||||
return chars();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this string's characters store an unsigned 32-bit
|
||||
* integer value, initializing *indexp to that value if so. (Thus if
|
||||
* calling isElement returns true, js::IndexToString(cx, *indexp) will be a
|
||||
* string equal to this string.)
|
||||
*/
|
||||
bool isElement(uint32 *indexp) const;
|
||||
|
||||
/*
|
||||
* Returns a property name represented by this string, or null on failure.
|
||||
* You must verify that this is not an element per isElement before calling
|
||||
* this method.
|
||||
*/
|
||||
inline js::PropertyName *toPropertyName(JSContext *cx);
|
||||
|
||||
/* Only called by the GC for strings with the FINALIZE_STRING kind. */
|
||||
|
||||
inline void finalize(JSRuntime *rt);
|
||||
@ -678,6 +717,9 @@ class JSAtom : public JSFixedString
|
||||
/* Return null if no static atom exists for the given (chars, length). */
|
||||
static inline JSStaticAtom *lookupStatic(const jschar *chars, size_t length);
|
||||
|
||||
/* Returns the PropertyName for this. isElement() must be false. */
|
||||
inline js::PropertyName *asPropertyName();
|
||||
|
||||
inline void finalize(JSRuntime *rt);
|
||||
};
|
||||
|
||||
@ -708,7 +750,21 @@ class JSStaticAtom : public JSAtom
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(JSStaticAtom) == sizeof(JSString));
|
||||
|
||||
/* Avoid requring vm/String-inl.h just to call getChars. */
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* Represents an atomized string which does not contain an unsigned 32-bit
|
||||
* value. That is, it is never the case that for a PropertyName propname,
|
||||
* ToString(ToUint32(propname)) is equal to propname.
|
||||
*/
|
||||
class PropertyName : public JSAtom
|
||||
{};
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(PropertyName) == sizeof(JSString));
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/* Avoid requiring vm/String-inl.h just to call getChars. */
|
||||
|
||||
JS_ALWAYS_INLINE const jschar *
|
||||
JSString::getChars(JSContext *cx)
|
||||
@ -758,4 +814,14 @@ JSString::ensureFixed(JSContext *cx)
|
||||
return &asFixed();
|
||||
}
|
||||
|
||||
inline js::PropertyName *
|
||||
JSAtom::asPropertyName()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
uint32 dummy;
|
||||
JS_ASSERT(!isElement(&dummy));
|
||||
#endif
|
||||
return static_cast<js::PropertyName *>(this);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -4646,8 +4646,8 @@ nsCSSFrameConstructor::FindMathMLData(Element* aElement,
|
||||
SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmsupFrame),
|
||||
SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmsubFrame),
|
||||
SIMPLE_MATHML_CREATE(msubsup_, NS_NewMathMLmsubsupFrame),
|
||||
SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderFrame),
|
||||
SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmoverFrame),
|
||||
SIMPLE_MATHML_CREATE(munder_, NS_NewMathMLmunderoverFrame),
|
||||
SIMPLE_MATHML_CREATE(mover_, NS_NewMathMLmunderoverFrame),
|
||||
SIMPLE_MATHML_CREATE(munderover_, NS_NewMathMLmunderoverFrame),
|
||||
SIMPLE_MATHML_CREATE(mphantom_, NS_NewMathMLmphantomFrame),
|
||||
SIMPLE_MATHML_CREATE(mpadded_, NS_NewMathMLmpaddedFrame),
|
||||
|
@ -1943,7 +1943,7 @@ static nscoord
|
||||
FindTileStart(nscoord aDirtyCoord, nscoord aTilePos, nscoord aTileDim)
|
||||
{
|
||||
NS_ASSERTION(aTileDim > 0, "Non-positive tile dimension");
|
||||
double multiples = NS_floor(double(aDirtyCoord - aTilePos)/aTileDim);
|
||||
double multiples = floor(double(aDirtyCoord - aTilePos)/aTileDim);
|
||||
return NSToCoordRound(multiples*aTileDim + aTilePos);
|
||||
}
|
||||
|
||||
@ -2050,7 +2050,7 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
|
||||
// try to do anything in that case. We certainly need to avoid
|
||||
// dividing by zero.
|
||||
if (stopDelta >= 1e-6) {
|
||||
double instanceCount = NS_ceil(-firstStop/stopDelta);
|
||||
double instanceCount = ceil(-firstStop/stopDelta);
|
||||
// Advance stops by instanceCount multiples of the period of the
|
||||
// repeating gradient.
|
||||
double offset = instanceCount*stopDelta;
|
||||
@ -3607,15 +3607,15 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
|
||||
|
||||
PRBool canLiftUnderline = aDescentLimit >= 0.0;
|
||||
|
||||
const gfxFloat left = NS_floor(aPt.x + 0.5),
|
||||
right = NS_floor(aPt.x + aLineSize.width + 0.5);
|
||||
const gfxFloat left = floor(aPt.x + 0.5),
|
||||
right = floor(aPt.x + aLineSize.width + 0.5);
|
||||
gfxRect r(left, 0, right - left, 0);
|
||||
|
||||
gfxFloat lineHeight = NS_round(aLineSize.height);
|
||||
lineHeight = NS_MAX(lineHeight, 1.0);
|
||||
|
||||
gfxFloat ascent = NS_round(aAscent);
|
||||
gfxFloat descentLimit = NS_floor(aDescentLimit);
|
||||
gfxFloat descentLimit = floor(aDescentLimit);
|
||||
|
||||
gfxFloat suggestedMaxRectHeight = NS_MAX(NS_MIN(ascent, descentLimit), 1.0);
|
||||
r.height = lineHeight;
|
||||
@ -3671,7 +3671,7 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
|
||||
}
|
||||
}
|
||||
|
||||
gfxFloat baseline = NS_floor(aPt.y + aAscent + 0.5);
|
||||
gfxFloat baseline = floor(aPt.y + aAscent + 0.5);
|
||||
gfxFloat offset = 0.0;
|
||||
switch (aDecoration) {
|
||||
case NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE:
|
||||
@ -3692,7 +3692,7 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
|
||||
offset = aOffset - lineHeight + r.Height();
|
||||
break;
|
||||
case NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH: {
|
||||
gfxFloat extra = NS_floor(r.Height() / 2.0 + 0.5);
|
||||
gfxFloat extra = floor(r.Height() / 2.0 + 0.5);
|
||||
extra = NS_MAX(extra, lineHeight);
|
||||
offset = aOffset - lineHeight + extra;
|
||||
break;
|
||||
@ -3700,7 +3700,7 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
|
||||
default:
|
||||
NS_ERROR("Invalid decoration value!");
|
||||
}
|
||||
r.y = baseline - NS_floor(offset + 0.5);
|
||||
r.y = baseline - floor(offset + 0.5);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -2766,7 +2766,7 @@ AdvanceToNextTab(gfxFloat aX, nsIFrame* aFrame,
|
||||
// Advance aX to the next multiple of *aCachedTabWidth. We must advance
|
||||
// by at least 1 appunit.
|
||||
// XXX should we make this 1 CSS pixel?
|
||||
return NS_ceil((aX + 1)/(*aCachedTabWidth))*(*aCachedTabWidth);
|
||||
return ceil((aX + 1)/(*aCachedTabWidth))*(*aCachedTabWidth);
|
||||
}
|
||||
|
||||
void
|
||||
@ -4517,7 +4517,7 @@ ComputeSelectionUnderlineHeight(nsPresContext* aPresContext,
|
||||
gfxFloat fontSize = NS_MIN(gfxFloat(defaultFontSize),
|
||||
aFontMetrics.emHeight);
|
||||
fontSize = NS_MAX(fontSize, 1.0);
|
||||
return NS_ceil(fontSize / 20);
|
||||
return ceil(fontSize / 20);
|
||||
}
|
||||
default:
|
||||
NS_WARNING("Requested underline style is not valid");
|
||||
|
@ -76,8 +76,6 @@ CPPSRCS = nsMathMLChar.cpp \
|
||||
nsMathMLmsubsupFrame.cpp \
|
||||
nsMathMLmmultiscriptsFrame.cpp \
|
||||
nsMathMLmtableFrame.cpp \
|
||||
nsMathMLmunderFrame.cpp \
|
||||
nsMathMLmoverFrame.cpp \
|
||||
nsMathMLmunderoverFrame.cpp \
|
||||
nsMathMLmpaddedFrame.cpp \
|
||||
nsMathMLmspaceFrame.cpp \
|
||||
|
@ -56,8 +56,6 @@ nsIFrame* NS_NewMathMLmfracFrame(nsIPresShell* aPresShell, nsStyleContext* aCont
|
||||
nsIFrame* NS_NewMathMLmsubFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
nsIFrame* NS_NewMathMLmsupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
nsIFrame* NS_NewMathMLmsubsupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
nsIFrame* NS_NewMathMLmunderFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
nsIFrame* NS_NewMathMLmoverFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
nsIFrame* NS_NewMathMLmunderoverFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
nsIFrame* NS_NewMathMLmmultiscriptsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
nsIFrame* NS_NewMathMLmstyleFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user