merge mozilla-inbound to mozilla-central. r=merge a=merge

MozReview-Commit-ID: 4W52wcX8xBm
This commit is contained in:
Sebastian Hengst 2017-09-26 11:54:34 +02:00
Родитель 57d322dc42 0b12597ec2
Коммит 3c0f8f3312
88 изменённых файлов: 1767 добавлений и 1486 удалений

Просмотреть файл

@ -2850,8 +2850,10 @@ var JawsScreenReaderVersionCheck = {
},
_checkVersionAndPrompt() {
// This executes a JAWS version check.
if (!Services.appinfo.shouldBlockIncompatJaws) {
// Make sure we only prompt for versions of JAWS we do not
// support and never prompt if e10s is disabled.
if (!Services.appinfo.shouldBlockIncompatJaws ||
!Services.appinfo.browserTabsRemoteAutostart) {
return;
}

Просмотреть файл

@ -124,48 +124,6 @@ menuitem.bookmark-item {
list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png);
}
/* Bookmark items */
.bookmark-item {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
}
.bookmark-item[container] {
list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
}
.bookmark-item[container][livemark] {
list-style-image: url("chrome://browser/skin/feeds/feedIcon16.png");
}
.bookmark-item[container][livemark] .bookmark-item {
list-style-image: url("chrome://browser/skin/places/livemark-item.png");
-moz-image-region: rect(0px, 16px, 16px, 0px);
}
.bookmark-item[container][livemark] .bookmark-item[visited] {
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
.bookmark-item[container][query] {
list-style-image: url("chrome://browser/skin/places/query.png");
}
.bookmark-item[query][tagContainer] {
list-style-image: url("chrome://browser/skin/places/tag.png");
}
.bookmark-item[query][dayContainer] {
list-style-image: url("chrome://browser/skin/places/calendar.png");
}
.bookmark-item[query][hostContainer] {
list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
}
.bookmark-item[query][hostContainer][open] {
list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
}
.bookmark-item[cutting] > .toolbarbutton-icon,
.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
opacity: 0.5;

Просмотреть файл

@ -41,14 +41,12 @@ browser.jar:
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)
skin/classic/browser/places/bookmarks-menu-arrow.png (places/bookmarks-menu-arrow.png)
skin/classic/browser/places/calendar.png (places/calendar.png)
* skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
skin/classic/browser/places/livemark-item.png (places/livemark-item.png)
skin/classic/browser/places/starred48.png (places/starred48.png)
* skin/classic/browser/places/places.css (places/places.css)
skin/classic/browser/places/organizer.css (places/organizer.css)
skin/classic/browser/places/organizer.xml (places/organizer.xml)
skin/classic/browser/places/query.png (places/query.png)
skin/classic/browser/places/tag.png (places/tag.png)
skin/classic/browser/places/toolbarDropMarker.png (places/toolbarDropMarker.png)
skin/classic/browser/places/unsortedBookmarks.png (places/unsortedBookmarks.png)

Двоичные данные
browser/themes/linux/places/calendar.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 670 B

Двоичные данные
browser/themes/linux/places/query.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 678 B

Просмотреть файл

@ -186,79 +186,6 @@
-moz-box-align: center;
}
/* ----- BOOKMARK BUTTONS ----- */
.bookmark-item[container] {
list-style-image: url("chrome://global/skin/tree/folder.png");
}
.bookmark-item[container][livemark] {
list-style-image: url("chrome://browser/skin/page-livemarks.png");
}
.bookmark-item[container][livemark] .bookmark-item {
list-style-image: url("chrome://browser/skin/places/livemark-item.png");
-moz-image-region: rect(0px, 16px, 16px, 0px);
}
.bookmark-item[container][livemark] .bookmark-item[visited] {
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
.bookmark-item[container][query] {
list-style-image: url("chrome://browser/skin/places/query.png");
}
.bookmark-item[query][tagContainer] {
list-style-image: url("chrome://browser/skin/places/tag.png");
}
.bookmark-item[query][dayContainer] {
list-style-image: url("chrome://browser/skin/places/history.png");
}
.bookmark-item[query][hostContainer] {
list-style-image: url("chrome://global/skin/tree/folder.png");
}
.bookmark-item[query][hostContainer][open] {
list-style-image: url("chrome://global/skin/tree/folder.png");
}
@media (min-resolution: 2dppx) {
.bookmark-item[container] {
list-style-image: url("chrome://global/skin/tree/folder@2x.png");
}
.bookmark-item[container][livemark] {
list-style-image: url("chrome://browser/skin/page-livemarks@2x.png");
}
.bookmark-item[container][livemark] .bookmark-item {
list-style-image: url("chrome://browser/skin/places/livemark-item.png");
}
.bookmark-item[container][query] {
list-style-image: url("chrome://browser/skin/places/query@2x.png");
}
.bookmark-item[query][tagContainer] {
list-style-image: url("chrome://browser/skin/places/tag@2x.png");
}
.bookmark-item[query][dayContainer] {
list-style-image: url("chrome://browser/skin/places/history@2x.png");
}
.bookmark-item[query][hostContainer] {
list-style-image: url("chrome://global/skin/tree/folder@2x.png");
}
.bookmark-item[query][hostContainer][open] {
list-style-image: url("chrome://global/skin/tree/folder@2x.png");
}
}
/* Workaround for native menubar inheritance */
.openintabs-menuitem,
.openlivemarksite-menuitem,
@ -829,10 +756,6 @@ html|span.ac-emphasize-text-url {
text-shadow: none;
}
.bookmark-item {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
}
.openintabs-menuitem {
list-style-image: none;
}

Просмотреть файл

@ -23,7 +23,6 @@ browser.jar:
skin/classic/browser/panel-expander-open@2x.png
skin/classic/browser/panel-plus-sign.png
skin/classic/browser/page-livemarks.png
skin/classic/browser/page-livemarks@2x.png
skin/classic/browser/pageInfo.css
skin/classic/browser/searchbar.css
skin/classic/browser/slowStartup-16.png
@ -46,13 +45,9 @@ browser.jar:
skin/classic/browser/places/allBookmarks.png (places/allBookmarks.png)
* skin/classic/browser/places/places.css (places/places.css)
skin/classic/browser/places/organizer.css (places/organizer.css)
skin/classic/browser/places/query.png (places/query.png)
skin/classic/browser/places/query@2x.png (places/query@2x.png)
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)
skin/classic/browser/places/bookmarksToolbar@2x.png (places/bookmarksToolbar@2x.png)
skin/classic/browser/places/history.png (places/history.png)
skin/classic/browser/places/history@2x.png (places/history@2x.png)
skin/classic/browser/places/toolbar.png (places/toolbar.png)
skin/classic/browser/places/toolbarDropMarker.png (places/toolbarDropMarker.png)
skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)

Двоичные данные
browser/themes/osx/page-livemarks@2x.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Двоичные данные
browser/themes/osx/places/history.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 843 B

Двоичные данные
browser/themes/osx/places/history@2x.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.8 KiB

Двоичные данные
browser/themes/osx/places/query.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 549 B

Двоичные данные
browser/themes/osx/places/query@2x.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.0 KiB

Просмотреть файл

@ -0,0 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M13 1H3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h11a2 2 0 0 0 2-2V4a3 3 0 0 0-3-3zm1 11a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1z"/>
<path d="M9.466 6.269l-.964-1.934-.185-.305-.662.028-1.101 2.211-2.039.364-.329.084-.184.613L5.67 9.123 5.365 11.3l-.023.351.552.356 2.116-1.102 1.844.96.319.138.525-.395-.347-2.485 1.462-1.573.214-.268-.227-.596-2.334-.417z"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 688 B

Просмотреть файл

@ -1,6 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="context-fill" fill-opacity="context-fill-opacity" d="M13 1H3a3.007 3.007 0 0 0-3 3v8a3.009 3.009 0 0 0 3 3h10a3.005 3.005 0 0 0 3-3V4a3.012 3.012 0 0 0-3-3zM3 3h10a1 1 0 0 1 1 1v1H2V4a1 1 0 0 1 1-1zm11 3v1H2V6zm-1 7H3a1 1 0 0 1-1-1V8h12v4a1 1 0 0 1-1 1z"/>
</svg>

До

Ширина:  |  Высота:  |  Размер: 574 B

Просмотреть файл

@ -170,7 +170,7 @@
skin/classic/browser/stop-to-reload.svg (../shared/icons/stop-to-reload.svg)
skin/classic/browser/sync.svg (../shared/icons/sync.svg)
skin/classic/browser/synced-tabs.svg (../shared/icons/synced-tabs.svg)
skin/classic/browser/toolbar.svg (../shared/icons/toolbar.svg)
skin/classic/browser/bookmarks-toolbar.svg (../shared/icons/bookmarks-toolbar.svg)
skin/classic/browser/webIDE.svg (../shared/icons/webIDE.svg)
skin/classic/browser/window.svg (../shared/icons/window.svg)
skin/classic/browser/zoom-in.svg (../shared/icons/zoom-in.svg)

Просмотреть файл

@ -97,7 +97,7 @@
}
#panelMenu_viewBookmarksToolbar {
list-style-image: url("chrome://browser/skin/toolbar.svg");
list-style-image: url("chrome://browser/skin/bookmarks-toolbar.svg");
}
#appMenu-library-bookmarks-button,

Просмотреть файл

@ -186,7 +186,7 @@ toolbar[brighttext] {
#bookmarks-toolbar-button,
#bookmarks-toolbar-placeholder {
list-style-image: url("chrome://browser/skin/bookmark-hollow.svg");
list-style-image: url("chrome://browser/skin/bookmarks-toolbar.svg");
}
#bookmarks-menu-button {
@ -521,3 +521,54 @@ toolbar[brighttext] {
animation-duration: 2s;
animation-timing-function: ease-out;
}
/* ----- BOOKMARK BUTTONS ----- */
.bookmark-item {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
-moz-context-properties: fill;
fill: currentColor;
}
.bookmark-item[container] {
list-style-image: url("chrome://browser/skin/places/folder.svg");
}
.bookmark-item[container][livemark] {
list-style-image: url("chrome://browser/skin/places/folder-live.svg");
}
.bookmark-item[container][livemark] .bookmark-item {
list-style-image: url("chrome://browser/skin/places/livemark-item.png");
-moz-image-region: rect(0px, 16px, 16px, 0px);
}
.bookmark-item[container][livemark] .bookmark-item[visited] {
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
.bookmark-item[container][query] {
list-style-image: url("chrome://browser/skin/places/folder-smart.svg");
}
.bookmark-item[query][tagContainer] {
list-style-image: url("chrome://browser/skin/places/tag.png");
-moz-image-region: auto;
}
.bookmark-item[query][dayContainer] {
list-style-image: url("chrome://browser/skin/places/history.svg");
-moz-image-region: auto;
}
.bookmark-item[query][hostContainer] {
list-style-image: url("chrome://browser/skin/places/folder.svg");
}
%ifdef XP_MACOSX
@media (min-resolution: 2dppx) {
.bookmark-item[query][tagContainer] {
list-style-image: url("chrome://browser/skin/places/tag@2x.png");
}
}
%endif

Просмотреть файл

@ -340,60 +340,6 @@ menuitem.bookmark-item {
padding-inline-start: 0px;
}
/* ::::: bookmark items ::::: */
.bookmark-item {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
}
.bookmark-item[container] {
list-style-image: url("chrome://global/skin/icons/folder-item.png");
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
.bookmark-item[container][open] {
-moz-image-region: rect(16px, 32px, 32px, 16px);
}
.bookmark-item[container][livemark] {
list-style-image: url("chrome://browser/skin/livemark-folder.png");
-moz-image-region: auto;
}
.bookmark-item[container][livemark] .bookmark-item {
list-style-image: url("chrome://browser/skin/places/livemark-item.png");
-moz-image-region: rect(0px, 16px, 16px, 0px);
}
.bookmark-item[container][livemark] .bookmark-item[visited] {
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
.bookmark-item[container][query] {
list-style-image: url("chrome://browser/skin/places/query.png");
-moz-image-region: auto;
}
.bookmark-item[query][tagContainer] {
list-style-image: url("chrome://browser/skin/places/tag.png");
-moz-image-region: auto;
}
.bookmark-item[query][dayContainer] {
list-style-image: url("chrome://browser/skin/places/calendar.png");
-moz-image-region: auto;
}
.bookmark-item[query][hostContainer] {
list-style-image: url("chrome://global/skin/icons/folder-item.png");
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
.bookmark-item[query][hostContainer][open] {
list-style-image: url("chrome://global/skin/icons/folder-item.png");
-moz-image-region: rect(16px, 32px, 32px, 16px);
}
.bookmark-item[cutting] > .toolbarbutton-icon,
.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
opacity: 0.5;

Просмотреть файл

@ -10,7 +10,6 @@ browser.jar:
* skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css)
* skin/classic/browser/browser.css
* skin/classic/browser/compacttheme.css
skin/classic/browser/livemark-folder.png
skin/classic/browser/menuPanel-customize.png
skin/classic/browser/menuPanel-customize@2x.png
skin/classic/browser/menuPanel-exit.png
@ -42,10 +41,8 @@ browser.jar:
skin/classic/browser/notification-icons/geo.svg (notification-icons/geo.svg)
* skin/classic/browser/places/places.css (places/places.css)
* skin/classic/browser/places/organizer.css (places/organizer.css)
skin/classic/browser/places/query.png (places/query.png)
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)
skin/classic/browser/places/calendar.png (places/calendar.png)
skin/classic/browser/places/toolbarDropMarker.png (places/toolbarDropMarker.png)
skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
skin/classic/browser/places/libraryToolbar.png (places/libraryToolbar.png)

Двоичные данные
browser/themes/windows/livemark-folder.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 619 B

Двоичные данные
browser/themes/windows/places/calendar.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 567 B

Двоичные данные
browser/themes/windows/places/query.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 601 B

Просмотреть файл

@ -308,7 +308,12 @@ exports.WebConsoleCommands = WebConsoleCommands;
* The result of calling document.querySelector(selector).
*/
WebConsoleCommands._registerOriginal("$", function (owner, selector) {
return owner.window.document.querySelector(selector);
try {
return owner.window.document.querySelector(selector);
} catch (err) {
// Throw an error like `err` but that belongs to `owner.window`.
throw new owner.window.DOMException(err.message, err.name);
}
});
/**
@ -320,7 +325,13 @@ WebConsoleCommands._registerOriginal("$", function (owner, selector) {
* Returns the result of document.querySelectorAll(selector).
*/
WebConsoleCommands._registerOriginal("$$", function (owner, selector) {
let nodes = owner.window.document.querySelectorAll(selector);
let nodes;
try {
nodes = owner.window.document.querySelectorAll(selector);
} catch (err) {
// Throw an error like `err` but that belongs to `owner.window`.
throw new owner.window.DOMException(err.message, err.name);
}
// Calling owner.window.Array.from() doesn't work without accessing the
// wrappedJSObject, so just loop through the results instead.

Просмотреть файл

@ -34,6 +34,7 @@ function startTest() {
checkQuerySelector,
checkQuerySelectorAll,
checkQuerySelectorAllNotExist,
checkQuerySelectorException,
checkQuerySelectorAllException
];
runTests(tests, testEnd);
@ -87,6 +88,25 @@ let checkQuerySelectorAllNotExist = Task.async(function*() {
nextTest();
});
let checkQuerySelectorException = Task.async(function*() {
info ("$ returns an exception if an invalid selector was provided");
let response = yield evaluateJS("$(':foo')");
checkObject(response, {
input: "$(':foo')",
exceptionMessage: "SyntaxError: ':foo' is not a valid selector",
exception: {
type: "object",
class: "DOMException",
preview: {
kind: "DOMException",
name: "SyntaxError",
message: "':foo' is not a valid selector"
}
}
});
nextTest();
});
let checkQuerySelectorAllException = Task.async(function*() {
info ("$$ returns an exception if an invalid selector was provided");
let response = yield evaluateJS("$$(':foo')");
@ -94,6 +114,8 @@ let checkQuerySelectorAllException = Task.async(function*() {
input: "$$(':foo')",
exceptionMessage: "SyntaxError: ':foo' is not a valid selector",
exception: {
type: "object",
class: "DOMException",
preview: {
kind: "DOMException",
name: "SyntaxError",

Просмотреть файл

@ -36,8 +36,20 @@ CustomElementCallback::Call()
// enqueue attached callback for ELEMENT.
nsIDocument* document = mThisObject->GetComposedDoc();
if (document && document->GetDocShell()) {
NodeInfo* ni = mThisObject->NodeInfo();
nsDependentAtomString extType(mOwnerData->mType);
// We need to do this because at this point, CustomElementDefinition is
// not set to CustomElementData yet, so EnqueueLifecycleCallback will
// fail to find the CE definition for this custom element.
// This will go away eventually since there is no created callback in v1.
CustomElementDefinition* definition =
nsContentUtils::LookupCustomElementDefinition(document,
ni->LocalName(), ni->NamespaceID(),
extType.IsEmpty() ? nullptr : &extType);
nsContentUtils::EnqueueLifecycleCallback(
document, nsIDocument::eAttached, mThisObject);
document, nsIDocument::eAttached, mThisObject, nullptr, definition);
}
static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
@ -158,46 +170,15 @@ private:
NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementRegistry)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementRegistry)
tmp->mCustomDefinitions.Clear();
tmp->mConstructors.clear();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomDefinitions)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWhenDefinedPromiseMap)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementRegistry)
for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
auto& callbacks = iter.UserData()->mCallbacks;
if (callbacks->mAttributeChangedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mCustomDefinitions->mCallbacks->mAttributeChangedCallback");
cb.NoteXPCOMChild(callbacks->mAttributeChangedCallback.Value());
}
if (callbacks->mCreatedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mCustomDefinitions->mCallbacks->mCreatedCallback");
cb.NoteXPCOMChild(callbacks->mCreatedCallback.Value());
}
if (callbacks->mAttachedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mCustomDefinitions->mCallbacks->mAttachedCallback");
cb.NoteXPCOMChild(callbacks->mAttachedCallback.Value());
}
if (callbacks->mDetachedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mCustomDefinitions->mCallbacks->mDetachedCallback");
cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value());
}
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mCustomDefinitions->mConstructor");
cb.NoteXPCOMChild(iter.UserData()->mConstructor);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomDefinitions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWhenDefinedPromiseMap)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@ -248,7 +229,7 @@ CustomElementRegistry::LookupCustomElementDefinition(const nsAString& aLocalName
nsCOMPtr<nsIAtom> localNameAtom = NS_Atomize(aLocalName);
nsCOMPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : localNameAtom;
CustomElementDefinition* data = mCustomDefinitions.Get(typeAtom);
CustomElementDefinition* data = mCustomDefinitions.GetWeak(typeAtom);
if (data && data->mLocalName == localNameAtom) {
return data;
}
@ -267,7 +248,7 @@ CustomElementRegistry::LookupCustomElementDefinition(JSContext* aCx,
return nullptr;
}
CustomElementDefinition* definition = mCustomDefinitions.Get(ptr->value());
CustomElementDefinition* definition = mCustomDefinitions.GetWeak(ptr->value());
MOZ_ASSERT(definition, "Definition must be found in mCustomDefinitions");
return definition;
@ -286,7 +267,7 @@ CustomElementRegistry::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTy
typeName = info->NameAtom();
}
if (mCustomDefinitions.Get(typeName)) {
if (mCustomDefinitions.GetWeak(typeName)) {
return;
}
@ -345,51 +326,35 @@ CustomElementRegistry::CreateCustomElementCallback(
nsIDocument::ElementCallbackType aType, Element* aCustomElement,
LifecycleCallbackArgs* aArgs, CustomElementDefinition* aDefinition)
{
MOZ_ASSERT(aDefinition, "CustomElementDefinition should not be null");
RefPtr<CustomElementData> elementData = aCustomElement->GetCustomElementData();
MOZ_ASSERT(elementData, "CustomElementData should exist");
// Let DEFINITION be ELEMENT's definition
CustomElementDefinition* definition = aDefinition;
if (!definition) {
mozilla::dom::NodeInfo* info = aCustomElement->NodeInfo();
// Make sure we get the correct definition in case the element
// is a extended custom element e.g. <button is="x-button">.
nsCOMPtr<nsIAtom> typeAtom = elementData ?
elementData->mType.get() : info->NameAtom();
definition = mCustomDefinitions.Get(typeAtom);
if (!definition || definition->mLocalName != info->NameAtom()) {
// Trying to enqueue a callback for an element that is not
// a custom element. We are done, nothing to do.
return nullptr;
}
}
// Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
CallbackFunction* func = nullptr;
switch (aType) {
case nsIDocument::eCreated:
if (definition->mCallbacks->mCreatedCallback.WasPassed()) {
func = definition->mCallbacks->mCreatedCallback.Value();
if (aDefinition->mCallbacks->mCreatedCallback.WasPassed()) {
func = aDefinition->mCallbacks->mCreatedCallback.Value();
}
break;
case nsIDocument::eAttached:
if (definition->mCallbacks->mAttachedCallback.WasPassed()) {
func = definition->mCallbacks->mAttachedCallback.Value();
if (aDefinition->mCallbacks->mAttachedCallback.WasPassed()) {
func = aDefinition->mCallbacks->mAttachedCallback.Value();
}
break;
case nsIDocument::eDetached:
if (definition->mCallbacks->mDetachedCallback.WasPassed()) {
func = definition->mCallbacks->mDetachedCallback.Value();
if (aDefinition->mCallbacks->mDetachedCallback.WasPassed()) {
func = aDefinition->mCallbacks->mDetachedCallback.Value();
}
break;
case nsIDocument::eAttributeChanged:
if (definition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
func = definition->mCallbacks->mAttributeChangedCallback.Value();
if (aDefinition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
func = aDefinition->mCallbacks->mAttributeChangedCallback.Value();
}
break;
}
@ -445,21 +410,11 @@ CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType
LifecycleCallbackArgs* aArgs,
CustomElementDefinition* aDefinition)
{
RefPtr<CustomElementData> elementData = aCustomElement->GetCustomElementData();
MOZ_ASSERT(elementData, "CustomElementData should exist");
// Let DEFINITION be ELEMENT's definition
CustomElementDefinition* definition = aDefinition;
if (!definition) {
mozilla::dom::NodeInfo* info = aCustomElement->NodeInfo();
// Make sure we get the correct definition in case the element
// is a extended custom element e.g. <button is="x-button">.
nsCOMPtr<nsIAtom> typeAtom = elementData ?
elementData->mType.get() : info->NameAtom();
definition = mCustomDefinitions.Get(typeAtom);
if (!definition || definition->mLocalName != info->NameAtom()) {
definition = aCustomElement->GetCustomElementDefinition();
if (!definition ||
definition->mLocalName != aCustomElement->NodeInfo()->NameAtom()) {
return;
}
}
@ -493,7 +448,8 @@ void
CustomElementRegistry::GetCustomPrototype(nsIAtom* aAtom,
JS::MutableHandle<JSObject*> aPrototype)
{
mozilla::dom::CustomElementDefinition* definition = mCustomDefinitions.Get(aAtom);
mozilla::dom::CustomElementDefinition* definition =
mCustomDefinitions.GetWeak(aAtom);
if (definition) {
aPrototype.set(definition->mPrototype);
} else {
@ -631,7 +587,7 @@ CustomElementRegistry::Define(const nsAString& aName,
* 3. If this CustomElementRegistry contains an entry with name name, then
* throw a "NotSupportedError" DOMException and abort these steps.
*/
if (mCustomDefinitions.Get(nameAtom)) {
if (mCustomDefinitions.GetWeak(nameAtom)) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
}
@ -642,7 +598,7 @@ CustomElementRegistry::Define(const nsAString& aName,
*/
const auto& ptr = mConstructors.lookup(constructorUnwrapped);
if (ptr) {
MOZ_ASSERT(mCustomDefinitions.Get(ptr->value()),
MOZ_ASSERT(mCustomDefinitions.GetWeak(ptr->value()),
"Definition must be found in mCustomDefinitions");
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
@ -834,7 +790,7 @@ CustomElementRegistry::Define(const nsAString& aName,
return;
}
CustomElementDefinition* definition =
RefPtr<CustomElementDefinition> definition =
new CustomElementDefinition(nameAtom,
localNameAtom,
&aFunctionConstructor,
@ -843,7 +799,8 @@ CustomElementRegistry::Define(const nsAString& aName,
callbacks,
0 /* TODO dependent on HTML imports. Bug 877072 */);
mCustomDefinitions.Put(nameAtom, definition);
CustomElementDefinition* def = definition.get();
mCustomDefinitions.Put(nameAtom, definition.forget());
MOZ_ASSERT(mCustomDefinitions.Count() == mConstructors.count(),
"Number of entries should be the same");
@ -851,7 +808,7 @@ CustomElementRegistry::Define(const nsAString& aName,
/**
* 13. 14. 15. Upgrade candidates
*/
UpgradeCandidates(nameAtom, definition, aRv);
UpgradeCandidates(nameAtom, def, aRv);
/**
* 16. If this CustomElementRegistry's when-defined promise map contains an
@ -874,7 +831,7 @@ CustomElementRegistry::Get(JSContext* aCx, const nsAString& aName,
JS::MutableHandle<JS::Value> aRetVal)
{
nsCOMPtr<nsIAtom> nameAtom(NS_Atomize(aName));
CustomElementDefinition* data = mCustomDefinitions.Get(nameAtom);
CustomElementDefinition* data = mCustomDefinitions.GetWeak(nameAtom);
if (!data) {
aRetVal.setUndefined();
@ -900,7 +857,7 @@ CustomElementRegistry::WhenDefined(const nsAString& aName, ErrorResult& aRv)
return promise.forget();
}
if (mCustomDefinitions.Get(nameAtom)) {
if (mCustomDefinitions.GetWeak(nameAtom)) {
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
}
@ -1004,6 +961,9 @@ CustomElementRegistry::Upgrade(Element* aElement,
// Step 8.
data->mState = CustomElementData::State::eCustom;
// Step 9.
aElement->SetCustomElementDefinition(aDefinition);
// This is for old spec.
nsContentUtils::EnqueueLifecycleCallback(aElement->OwnerDoc(),
nsIDocument::eCreated,
@ -1156,6 +1116,50 @@ CustomElementReactionsStack::InvokeReactions(ElementQueue* aElementQueue,
//-----------------------------------------------------
// CustomElementDefinition
NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementDefinition)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementDefinition)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConstructor)
tmp->mPrototype = nullptr;
tmp->mCallbacks = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementDefinition)
mozilla::dom::LifecycleCallbacks* callbacks = tmp->mCallbacks.get();
if (callbacks->mAttributeChangedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mCallbacks->mAttributeChangedCallback");
cb.NoteXPCOMChild(callbacks->mAttributeChangedCallback.Value());
}
if (callbacks->mCreatedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mCreatedCallback");
cb.NoteXPCOMChild(callbacks->mCreatedCallback.Value());
}
if (callbacks->mAttachedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mAttachedCallback");
cb.NoteXPCOMChild(callbacks->mAttachedCallback.Value());
}
if (callbacks->mDetachedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mDetachedCallback");
cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value());
}
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mConstructor");
cb.NoteXPCOMChild(tmp->mConstructor);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementDefinition)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPrototype)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CustomElementDefinition, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CustomElementDefinition, Release)
CustomElementDefinition::CustomElementDefinition(nsIAtom* aType,
nsIAtom* aLocalName,
Function* aConstructor,

Просмотреть файл

@ -14,7 +14,9 @@
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/WebComponentsBinding.h"
#include "nsCycleCollectionParticipant.h"
#include "nsGenericHTMLElement.h"
#include "nsWrapperCache.h"
#include "nsContentUtils.h"
@ -25,7 +27,6 @@ namespace dom {
struct CustomElementData;
struct ElementDefinitionOptions;
struct LifecycleCallbacks;
class CallbackFunction;
class CustomElementReaction;
class Function;
@ -117,6 +118,22 @@ struct CustomElementData
// e.g., create an element, insert a node.
AutoTArray<UniquePtr<CustomElementReaction>, 3> mReactionQueue;
RefPtr<CustomElementDefinition> mCustomElementDefinition;
void
SetCustomElementDefinition(CustomElementDefinition* aDefinition)
{
MOZ_ASSERT(!mCustomElementDefinition);
mCustomElementDefinition = aDefinition;
}
CustomElementDefinition*
GetCustomElementDefinition()
{
return mCustomElementDefinition;
}
private:
virtual ~CustomElementData() {}
};
@ -127,6 +144,9 @@ private:
// https://html.spec.whatwg.org/multipage/scripting.html#custom-element-definition
struct CustomElementDefinition
{
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CustomElementDefinition)
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CustomElementDefinition)
CustomElementDefinition(nsIAtom* aType,
nsIAtom* aLocalName,
Function* aConstructor,
@ -172,6 +192,9 @@ struct CustomElementDefinition
return mObservedAttributes.Contains(aName);
}
private:
~CustomElementDefinition() {}
};
class CustomElementReaction
@ -394,7 +417,7 @@ private:
CustomElementDefinition* aDefinition,
ErrorResult& aRv);
typedef nsClassHashtable<nsISupportsHashKey, CustomElementDefinition>
typedef nsRefPtrHashtable<nsISupportsHashKey, CustomElementDefinition>
DefinitionMap;
typedef nsClassHashtable<nsISupportsHashKey, nsTArray<nsWeakPtr>>
CandidateMap;

Просмотреть файл

@ -3581,7 +3581,7 @@ Element::GetTransformToAncestor(Element& aAncestor)
// then the call to GetTransformToAncestor will return the transform
// all the way up through the parent chain.
transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame,
ancestorFrame, true);
ancestorFrame, nsIFrame::IN_CSS_UNITS);
}
DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
@ -3598,7 +3598,7 @@ Element::GetTransformToParent()
if (primaryFrame) {
nsIFrame* parentFrame = primaryFrame->GetParent();
transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame,
parentFrame, true);
parentFrame, nsIFrame::IN_CSS_UNITS);
}
DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
@ -3613,7 +3613,7 @@ Element::GetTransformToViewport()
Matrix4x4 transform;
if (primaryFrame) {
transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame,
nsLayoutUtils::GetDisplayRootFrame(primaryFrame), true);
nsLayoutUtils::GetDisplayRootFrame(primaryFrame), nsIFrame::IN_CSS_UNITS);
}
DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
@ -4214,6 +4214,26 @@ Element::SetCustomElementData(CustomElementData* aData)
slots->mCustomElementData = aData;
}
CustomElementDefinition*
Element::GetCustomElementDefinition() const
{
CustomElementData* data = GetCustomElementData();
if (!data) {
return nullptr;
}
return data->GetCustomElementDefinition();
}
void
Element::SetCustomElementDefinition(CustomElementDefinition* aDefinition)
{
CustomElementData* data = GetCustomElementData();
MOZ_ASSERT(data);
data->SetCustomElementDefinition(aDefinition);
}
MOZ_DEFINE_MALLOC_SIZE_OF(ServoElementMallocSizeOf)
MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoElementMallocEnclosingSizeOf)

Просмотреть файл

@ -556,6 +556,22 @@ public:
*/
void SetCustomElementData(CustomElementData* aData);
/**
* Gets the custom element definition used by web components custom element.
*
* @return The custom element definition or null if element is not a custom
* element or custom element is not defined yet.
*/
CustomElementDefinition* GetCustomElementDefinition() const;
/**
* Sets the custom element definition, called when custom element is created
* or upgraded.
*
* @param aDefinition The custom element definition.
*/
void SetCustomElementDefinition(CustomElementDefinition* aDefinition);
protected:
/**
* Method to get the _intrinsic_ content state of this element. This is the

Просмотреть файл

@ -812,6 +812,13 @@ FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
mExtendedSlots->mCustomElementData->mReactionQueue[i]->Traverse(cb);
}
}
if (mExtendedSlots->mCustomElementData->mCustomElementDefinition) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mExtendedSlots->mCustomElementData->mCustomElementDefinition");
cb.NoteNativeChild(mExtendedSlots->mCustomElementData->mCustomElementDefinition,
NS_CYCLE_COLLECTION_PARTICIPANT(CustomElementDefinition));
}
}
for (auto iter = mExtendedSlots->mRegisteredIntersectionObservers.Iter();
@ -848,7 +855,12 @@ FragmentOrElement::nsDOMSlots::Unlink()
mExtendedSlots->mContainingShadow = nullptr;
MOZ_ASSERT(!(mExtendedSlots->mXBLBinding));
mExtendedSlots->mXBLInsertionParent = nullptr;
mExtendedSlots->mCustomElementData = nullptr;
if (mExtendedSlots->mCustomElementData) {
if (mExtendedSlots->mCustomElementData->mCustomElementDefinition) {
mExtendedSlots->mCustomElementData->mCustomElementDefinition = nullptr;
}
mExtendedSlots->mCustomElementData = nullptr;
}
mExtendedSlots->mRegisteredIntersectionObservers.Clear();
nsCOMPtr<nsIFrameLoader> frameLoader =
do_QueryInterface(mExtendedSlots->mFrameLoaderOrOpener);

256
dom/base/RangeBoundary.h Normal file
Просмотреть файл

@ -0,0 +1,256 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_RangeBoundary_h
#define mozilla_RangeBoundary_h
namespace mozilla {
// This class will maintain a reference to the child immediately
// before the boundary's offset. We try to avoid computing the
// offset as much as possible and just ensure mRef points to the
// correct child.
//
// mParent
// |
// [child0] [child1] [child2]
// / |
// mRef mOffset=2
//
// If mOffset == 0, mRef is null.
// For text nodes, mRef will always be null and the offset will
// be kept up-to-date.
template<typename ParentType, typename RefType>
class RangeBoundaryBase;
typedef RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>> RangeBoundary;
typedef RangeBoundaryBase<nsINode*, nsIContent*> RawRangeBoundary;
// This class has two specializations, one using reference counting
// pointers and one using raw pointers. This helps us avoid unnecessary
// AddRef/Release calls.
template<typename ParentType, typename RefType>
class RangeBoundaryBase
{
template<typename T, typename U>
friend class RangeBoundaryBase;
friend void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback&,
RangeBoundary&, const char*,
uint32_t);
friend void ImplCycleCollectionUnlink(RangeBoundary&);
public:
RangeBoundaryBase(nsINode* aContainer, nsIContent* aRef)
: mParent(aContainer)
, mRef(aRef)
{
if (!mRef) {
mOffset = mozilla::Some(0);
} else {
mOffset.reset();
}
}
RangeBoundaryBase(nsINode* aContainer, int32_t aOffset)
: mParent(aContainer)
, mRef(nullptr)
, mOffset(mozilla::Some(aOffset))
{
if (mParent && mParent->IsContainerNode()) {
// Find a reference node
if (aOffset == static_cast<int32_t>(aContainer->GetChildCount())) {
mRef = aContainer->GetLastChild();
} else if (aOffset != 0) {
mRef = mParent->GetChildAt(aOffset - 1);
MOZ_ASSERT(mRef);
}
MOZ_ASSERT_IF(!mRef, aOffset == 0);
}
MOZ_ASSERT_IF(mRef, mRef->GetParentNode() == mParent);
}
RangeBoundaryBase()
: mParent(nullptr)
, mRef(nullptr)
{
}
// Needed for initializing RawRangeBoundary from an existing RangeBoundary.
template<typename PT, typename RT>
explicit RangeBoundaryBase(const RangeBoundaryBase<PT, RT>& aOther)
: mParent(aOther.mParent)
, mRef(aOther.mRef)
, mOffset(aOther.mOffset)
{
}
nsIContent*
Ref() const
{
return mRef;
}
nsINode*
Container() const
{
return mParent;
}
nsIContent*
GetChildAtOffset() const
{
if (!mParent || !mParent->IsContainerNode()) {
return nullptr;
}
if (!mRef) {
MOZ_ASSERT(Offset() == 0);
return mParent->GetFirstChild();
}
MOZ_ASSERT(mParent->GetChildAt(Offset()) == mRef->GetNextSibling());
return mRef->GetNextSibling();
}
uint32_t
Offset() const
{
if (mOffset.isSome()) {
return mOffset.value();
}
if (!mParent) {
return 0;
}
MOZ_ASSERT(mRef);
MOZ_ASSERT(mRef->GetParentNode() == mParent);
mOffset = mozilla::Some(mParent->IndexOf(mRef) + 1);
return mOffset.value();
}
void
InvalidateOffset()
{
MOZ_ASSERT(mParent);
MOZ_ASSERT(mParent->IsContainerNode(), "Range is positioned on a text node!");
if (!mRef) {
MOZ_ASSERT(mOffset.isSome() && mOffset.value() == 0);
return;
}
mOffset.reset();
}
void
Set(nsINode* aContainer, int32_t aOffset)
{
mParent = aContainer;
if (mParent && mParent->IsContainerNode()) {
// Find a reference node
if (aOffset == static_cast<int32_t>(aContainer->GetChildCount())) {
mRef = aContainer->GetLastChild();
} else if (aOffset == 0) {
mRef = nullptr;
} else {
mRef = mParent->GetChildAt(aOffset - 1);
MOZ_ASSERT(mRef);
}
MOZ_ASSERT_IF(!mRef, aOffset == 0);
} else {
mRef = nullptr;
}
mOffset = mozilla::Some(aOffset);
MOZ_ASSERT_IF(mRef, mRef->GetParentNode() == mParent);
}
void
SetAfterRef(nsINode* aParent, nsIContent* aRef)
{
mParent = aParent;
mRef = aRef;
if (!mRef) {
mOffset = mozilla::Some(0);
} else {
mOffset.reset();
}
}
bool
IsSet() const
{
return mParent && (mRef || mOffset.isSome());
}
bool
IsSetAndValid() const
{
if (!IsSet()) {
return false;
}
if (Ref()) {
return Ref()->GetParentNode() == Container();
}
return Offset() <= Container()->Length();
}
// Convenience methods for switching between the two types
// of RangeBoundary.
RangeBoundaryBase<nsINode*, nsIContent*>
AsRaw() const
{
return RangeBoundaryBase<nsINode*, nsIContent*>(*this);
}
template<typename A, typename B>
RangeBoundaryBase& operator=(const RangeBoundaryBase<A,B>& aOther)
{
mParent = aOther.mParent;
mRef = aOther.mRef;
mOffset = aOther.mOffset;
return *this;
}
template<typename A, typename B>
bool operator==(const RangeBoundaryBase<A, B>& aOther) const
{
return mParent == aOther.mParent &&
(mRef ? mRef == aOther.mRef : mOffset == aOther.mOffset);
}
private:
ParentType mParent;
RefType mRef;
mutable mozilla::Maybe<uint32_t> mOffset;
};
inline void
ImplCycleCollectionUnlink(RangeBoundary& aField)
{
ImplCycleCollectionUnlink(aField.mParent);
ImplCycleCollectionUnlink(aField.mRef);
}
inline void
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
RangeBoundary& aField,
const char* aName,
uint32_t aFlags)
{
ImplCycleCollectionTraverse(aCallback, aField.mParent, "mParent", 0);
ImplCycleCollectionTraverse(aCallback, aField.mRef, "mRef", 0);
}
} // namespace mozilla
#endif // defined(mozilla_RangeBoundary_h)

Просмотреть файл

@ -140,6 +140,7 @@ EXPORTS.mozilla += [
'CORSMode.h',
'FeedWriterEnabled.h',
'FlushType.h',
'RangeBoundary.h',
'TextInputProcessor.h',
'UseCounter.h',
]

Просмотреть файл

@ -10122,13 +10122,8 @@ nsContentUtils::GetElementDefinitionIfObservingAttr(Element* aCustomElement,
nsIAtom* aExtensionType,
nsIAtom* aAttrName)
{
nsString extType = nsDependentAtomString(aExtensionType);
NodeInfo *ni = aCustomElement->NodeInfo();
CustomElementDefinition* definition =
LookupCustomElementDefinition(aCustomElement->OwnerDoc(), ni->LocalName(),
ni->NamespaceID(),
extType.IsEmpty() ? nullptr : &extType);
aCustomElement->GetCustomElementDefinition();
// Custom element not defined yet or attribute is not in the observed
// attribute list.

Просмотреть файл

@ -3039,37 +3039,6 @@ nsDOMWindowUtils::RenderDocument(const nsRect& aRect,
return presShell->RenderDocument(aRect, aFlags, aBackgroundColor, aThebesContext);
}
NS_IMETHODIMP
nsDOMWindowUtils::GetCursorType(int16_t *aCursor)
{
NS_ENSURE_ARG_POINTER(aCursor);
nsIDocument* doc = GetDocument();
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
bool isSameDoc = false;
do {
if (EventStateManager::sMouseOverDocument == doc) {
isSameDoc = true;
break;
}
} while ((doc = doc->GetParentDocument()));
if (!isSameDoc) {
*aCursor = eCursor_none;
return NS_OK;
}
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget)
return NS_ERROR_FAILURE;
// fetch cursor value from window's widget
*aCursor = widget->GetCursor();
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetDisplayDPI(float *aDPI)
{

Просмотреть файл

@ -6313,7 +6313,8 @@ nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value*
}
nsCOMPtr<nsIAtom> typeAtom(NS_Atomize(elemName));
CustomElementDefinition* definition = registry->mCustomDefinitions.Get(typeAtom);
CustomElementDefinition* definition =
registry->mCustomDefinitions.GetWeak(typeAtom);
if (!definition) {
return true;
}
@ -6383,6 +6384,8 @@ nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value*
new CustomElementData(definition->mType,
CustomElementData::State::eCustom));
element->SetCustomElementDefinition(definition);
// It'll be removed when we deprecate custom elements v0.
nsContentUtils::SyncInvokeReactions(nsIDocument::eCreated, element,
definition);

Просмотреть файл

@ -357,10 +357,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart.mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart.mRef)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd.mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd.mRef)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

Просмотреть файл

@ -23,6 +23,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/LinkedList.h"
#include "mozilla/RangeBoundary.h"
namespace mozilla {
class ErrorResult;
@ -44,6 +45,8 @@ class nsRange final : public nsIDOMRange,
typedef mozilla::ErrorResult ErrorResult;
typedef mozilla::dom::DOMRect DOMRect;
typedef mozilla::dom::DOMRectList DOMRectList;
typedef mozilla::RangeBoundary RangeBoundary;
typedef mozilla::RawRangeBoundary RawRangeBoundary;
virtual ~nsRange();
@ -82,11 +85,21 @@ public:
return mRoot;
}
const RangeBoundary& StartRef() const
{
return mStart;
}
nsINode* GetStartContainer() const
{
return mStart.Container();
}
const RangeBoundary& EndRef() const
{
return mEnd;
}
nsINode* GetEndContainer() const
{
return mEnd.Container();
@ -412,215 +425,6 @@ public:
typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable;
protected:
// This class has two specializations, one using reference counting
// pointers and one using raw pointers. This helps us avoid unnecessary
// AddRef/Release calls.
template<typename ParentType, typename RefType>
class RangeBoundaryBase
{
// This class will maintain a reference to the child immediately
// before the boundary's offset. We try to avoid computing the
// offset as much as possible and just ensure mRef points to the
// correct child.
//
// mParent
// |
// [child0] [child1] [child2]
// / |
// mRef mOffset=2
//
// If mOffset == 0, mRef is null.
// For text nodes, mRef will always be null and the offset will
// be kept up-to-date.
// for cycle collecting mParent and mRef;
friend class nsRange;
public:
RangeBoundaryBase(nsINode* aContainer, nsIContent* aRef)
: mParent(aContainer)
, mRef(aRef)
{
if (!mRef) {
mOffset = mozilla::Some(0);
} else {
mOffset.reset();
}
}
RangeBoundaryBase(nsINode* aContainer, int32_t aOffset)
: mParent(aContainer)
, mRef(nullptr)
, mOffset(mozilla::Some(aOffset))
{
if (mParent && mParent->IsContainerNode()) {
// Find a reference node
if (aOffset == static_cast<int32_t>(aContainer->GetChildCount())) {
mRef = aContainer->GetLastChild();
} else if (aOffset != 0) {
mRef = mParent->GetChildAt(aOffset - 1);
MOZ_ASSERT(mRef);
}
MOZ_ASSERT_IF(!mRef, aOffset == 0);
}
MOZ_ASSERT_IF(mRef, mRef->GetParentNode() == mParent);
}
RangeBoundaryBase()
: mParent(nullptr)
, mRef(nullptr)
{
}
// Needed for initializing RawRangeBoundary from an existing RangeBoundary.
explicit RangeBoundaryBase(const RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>>& aOther)
: mParent(aOther.mParent)
, mRef(aOther.mRef)
, mOffset(aOther.mOffset)
{
}
nsIContent*
Ref() const
{
return mRef;
}
nsINode*
Container() const
{
return mParent;
}
nsIContent*
GetChildAtOffset() const
{
if (!mParent || !mParent->IsContainerNode()) {
return nullptr;
}
if (!mRef) {
MOZ_ASSERT(Offset() == 0);
return mParent->GetFirstChild();
}
MOZ_ASSERT(mParent->GetChildAt(Offset()) == mRef->GetNextSibling());
return mRef->GetNextSibling();
}
uint32_t
Offset() const
{
if (mOffset.isSome()) {
return mOffset.value();
}
if (!mParent) {
return 0;
}
MOZ_ASSERT(mRef);
MOZ_ASSERT(mRef->GetParentNode() == mParent);
mOffset = mozilla::Some(mParent->IndexOf(mRef) + 1);
return mOffset.value();
}
void
InvalidateOffset()
{
MOZ_ASSERT(mParent);
MOZ_ASSERT(mParent->IsContainerNode(), "Range is positioned on a text node!");
if (!mRef) {
MOZ_ASSERT(mOffset.isSome() && mOffset.value() == 0);
return;
}
mOffset.reset();
}
void
AdjustOffset(int32_t aDelta)
{
MOZ_ASSERT(mRef);
mOffset = mozilla::Some(Offset() + aDelta);
}
void
Set(nsINode* aContainer, int32_t aOffset)
{
mParent = aContainer;
if (mParent && mParent->IsContainerNode()) {
// Find a reference node
if (aOffset == static_cast<int32_t>(aContainer->GetChildCount())) {
mRef = aContainer->GetLastChild();
} else if (aOffset == 0) {
mRef = nullptr;
} else {
mRef = mParent->GetChildAt(aOffset - 1);
MOZ_ASSERT(mRef);
}
MOZ_ASSERT_IF(!mRef, aOffset == 0);
} else {
mRef = nullptr;
}
mOffset = mozilla::Some(aOffset);
MOZ_ASSERT_IF(mRef, mRef->GetParentNode() == mParent);
}
void
SetAfterRef(nsINode* aParent, nsIContent* aRef)
{
mParent = aParent;
mRef = aRef;
if (!mRef) {
mOffset = mozilla::Some(0);
} else {
mOffset.reset();
}
}
bool
IsSet() const
{
return mParent && (mRef || mOffset.isSome());
}
// Convenience methods for switching between the two types
// of RangeBoundary.
RangeBoundaryBase<nsINode*, nsIContent*>
AsRaw() const
{
return RangeBoundaryBase<nsINode*, nsIContent*>(*this);
}
template<typename A, typename B>
RangeBoundaryBase& operator=(const RangeBoundaryBase<A,B>& aOther)
{
// Since the member variables may be nsCOMPtrs, better to try to avoid
// extra Release/AddRef calls.
if (mParent != aOther.mParent) {
mParent = aOther.mParent;
}
if (mRef != aOther.mRef) {
mRef = aOther.mRef;
}
mOffset = aOther.mOffset;
return *this;
}
private:
ParentType mParent;
RefType mRef;
mutable mozilla::Maybe<uint32_t> mOffset;
};
typedef RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>> RangeBoundary;
typedef RangeBoundaryBase<nsINode*, nsIContent*> RawRangeBoundary;
void RegisterCommonAncestor(nsINode* aNode);
void UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking);
nsINode* IsValidBoundary(nsINode* aNode) const

Просмотреть файл

@ -3682,6 +3682,8 @@ CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
newElement->SetCustomElementData(
new CustomElementData(definition->mType, CustomElementData::State::eCustom));
newElement->SetCustomElementDefinition(definition);
return newElement.forget();
}

Просмотреть файл

@ -47,69 +47,56 @@ void
ContentEventHandler::RawRange::AssertStartIsBeforeOrEqualToEnd()
{
MOZ_ASSERT(
nsContentUtils::ComparePoints(mStartContainer,
static_cast<int32_t>(mStartOffset),
mEndContainer,
static_cast<int32_t>(mEndOffset)) <= 0);
}
bool
ContentEventHandler::RawRange::IsValidOffset(nsINode* aContainer,
uint32_t aOffset) const
{
return aContainer && aOffset <= aContainer->Length();
nsContentUtils::ComparePoints(mStart.Container(),
static_cast<int32_t>(mStart.Offset()),
mEnd.Container(),
static_cast<int32_t>(mEnd.Offset())) <= 0);
}
nsresult
ContentEventHandler::RawRange::SetStart(nsINode* aStartContainer,
uint32_t aStartOffset)
ContentEventHandler::RawRange::SetStart(const RawRangeBoundary& aStart)
{
nsINode* newRoot = nsRange::ComputeRootNode(aStartContainer);
nsINode* newRoot = nsRange::ComputeRootNode(aStart.Container());
if (!newRoot) {
return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
}
if (!IsValidOffset(aStartContainer, aStartOffset)) {
if (!aStart.IsSetAndValid()) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
// Collapse if not positioned yet, or if positioned in another document.
if (!IsPositioned() || newRoot != mRoot) {
mRoot = newRoot;
mStartContainer = mEndContainer = aStartContainer;
mStartOffset = mEndOffset = aStartOffset;
mStart = mEnd = aStart;
return NS_OK;
}
mStartContainer = aStartContainer;
mStartOffset = aStartOffset;
mStart = aStart;
AssertStartIsBeforeOrEqualToEnd();
return NS_OK;
}
nsresult
ContentEventHandler::RawRange::SetEnd(nsINode* aEndContainer,
uint32_t aEndOffset)
ContentEventHandler::RawRange::SetEnd(const RawRangeBoundary& aEnd)
{
nsINode* newRoot = nsRange::ComputeRootNode(aEndContainer);
nsINode* newRoot = nsRange::ComputeRootNode(aEnd.Container());
if (!newRoot) {
return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
}
if (!IsValidOffset(aEndContainer, aEndOffset)) {
if (!aEnd.IsSetAndValid()) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
// Collapse if not positioned yet, or if positioned in another document.
if (!IsPositioned() || newRoot != mRoot) {
mRoot = newRoot;
mStartContainer = mEndContainer = aEndContainer;
mStartOffset = mEndOffset = aEndOffset;
mStart = mEnd = aEnd;
return NS_OK;
}
mEndContainer = aEndContainer;
mEndOffset = aEndOffset;
mEnd = aEnd;
AssertStartIsBeforeOrEqualToEnd();
return NS_OK;
}
@ -126,61 +113,53 @@ ContentEventHandler::RawRange::SetEndAfter(nsINode* aEndContainer)
void
ContentEventHandler::RawRange::SetStartAndEnd(const nsRange* aRange)
{
DebugOnly<nsresult> rv = SetStartAndEnd(aRange->GetStartContainer(),
aRange->StartOffset(),
aRange->GetEndContainer(),
aRange->EndOffset());
DebugOnly<nsresult> rv = SetStartAndEnd(aRange->StartRef().AsRaw(),
aRange->EndRef().AsRaw());
MOZ_ASSERT(!aRange->IsPositioned() || NS_SUCCEEDED(rv));
}
nsresult
ContentEventHandler::RawRange::SetStartAndEnd(nsINode* aStartContainer,
uint32_t aStartOffset,
nsINode* aEndContainer,
uint32_t aEndOffset)
ContentEventHandler::RawRange::SetStartAndEnd(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd)
{
nsINode* newStartRoot = nsRange::ComputeRootNode(aStartContainer);
nsINode* newStartRoot = nsRange::ComputeRootNode(aStart.Container());
if (!newStartRoot) {
return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
}
if (!IsValidOffset(aStartContainer, aStartOffset)) {
if (!aStart.IsSetAndValid()) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
if (aStartContainer == aEndContainer) {
if (!IsValidOffset(aEndContainer, aEndOffset)) {
if (aStart.Container() == aEnd.Container()) {
if (!aEnd.IsSetAndValid()) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
MOZ_ASSERT(aStartOffset <= aEndOffset);
MOZ_ASSERT(aStart.Offset() <= aEnd.Offset());
mRoot = newStartRoot;
mStartContainer = mEndContainer = aStartContainer;
mStartOffset = aStartOffset;
mEndOffset = aEndOffset;
mStart = aStart;
mEnd = aEnd;
return NS_OK;
}
nsINode* newEndRoot = nsRange::ComputeRootNode(aEndContainer);
nsINode* newEndRoot = nsRange::ComputeRootNode(aEnd.Container());
if (!newEndRoot) {
return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
}
if (!IsValidOffset(aEndContainer, aEndOffset)) {
if (!aEnd.IsSetAndValid()) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
// If they have different root, this should be collapsed at the end point.
if (newStartRoot != newEndRoot) {
mRoot = newEndRoot;
mStartContainer = mEndContainer = aEndContainer;
mStartOffset = mEndOffset = aEndOffset;
mStart = mEnd = aEnd;
return NS_OK;
}
// Otherwise, set the range as specified.
mRoot = newStartRoot;
mStartContainer = aStartContainer;
mStartOffset = aStartOffset;
mEndContainer = aEndContainer;
mEndOffset = aEndOffset;
mStart = aStart;
mEnd = aEnd;
AssertStartIsBeforeOrEqualToEnd();
return NS_OK;
}
@ -194,9 +173,9 @@ ContentEventHandler::RawRange::SelectNodeContents(
return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
}
mRoot = newRoot;
mStartContainer = mEndContainer = aNodeToSelectContents;
mStartOffset = 0;
mEndOffset = aNodeToSelectContents->Length();
mStart = RawRangeBoundary(aNodeToSelectContents, nullptr);
mEnd = RawRangeBoundary(aNodeToSelectContents,
aNodeToSelectContents->GetLastChild());
return NS_OK;
}
@ -416,7 +395,7 @@ ContentEventHandler::InitCommon(SelectionType aSelectionType)
// But otherwise, we need to assume that there is a selection range at the
// beginning of the root content if aSelectionType is eNormal.
rv = mFirstSelectedRawRange.CollapseTo(mRootContent, 0);
rv = mFirstSelectedRawRange.CollapseTo(RawRangeBoundary(mRootContent, 0));
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_UNEXPECTED;
}
@ -1160,7 +1139,7 @@ ContentEventHandler::SetRawRangeFromFlatTextOffset(
// Special case like <br contenteditable>
if (!mRootContent->HasChildren()) {
nsresult rv = aRawRange->CollapseTo(mRootContent, 0);
nsresult rv = aRawRange->CollapseTo(RawRangeBoundary(mRootContent, 0));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1615,8 +1594,8 @@ ContentEventHandler::NodePosition
ContentEventHandler::GetNodePositionHavingFlatText(
const NodePosition& aNodePosition)
{
return GetNodePositionHavingFlatText(aNodePosition.mNode,
aNodePosition.mOffset);
return GetNodePositionHavingFlatText(aNodePosition.Container(),
aNodePosition.Offset());
}
ContentEventHandler::NodePosition
@ -1644,10 +1623,11 @@ ContentEventHandler::GetNodePositionHavingFlatText(nsINode* aNode,
// child of it. For example, if a range is |<p>[<br>]</p>|, then, the
// end point is {<p>, 1}. In such case, callers need the <br> node.
if (aNodeOffset == childCount) {
NodePosition result;
result.mNode = aNode->GetChildAt(childCount - 1);
result.mOffset = result.mNode->IsNodeOfType(nsINode::eTEXT) ?
static_cast<int32_t>(result.mNode->AsContent()->TextLength()) : 1;
nsINode* node = aNode->GetChildAt(childCount - 1);
return NodePosition(node,
node->IsNodeOfType(nsINode::eTEXT)
? static_cast<int32_t>(node->AsContent()->TextLength())
: 1);
}
NS_WARNING("aNodeOffset is invalid value");
@ -1681,8 +1661,7 @@ ContentEventHandler::GetFirstFrameInRangeForTextRect(const RawRange& aRawRange)
int32_t offsetInNode =
node == aRawRange.GetStartContainer() ? aRawRange.StartOffset() : 0;
if (static_cast<uint32_t>(offsetInNode) < node->Length()) {
nodePosition.mNode = node;
nodePosition.mOffset = offsetInNode;
nodePosition.Set(node, offsetInNode);
break;
}
continue;
@ -1692,19 +1671,18 @@ ContentEventHandler::GetFirstFrameInRangeForTextRect(const RawRange& aRawRange)
// node causing text.
if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
IsMozBR(node->AsContent())) {
nodePosition.mNode = node;
nodePosition.mOffset = 0;
nodePosition.Set(node, 0);
}
}
if (!nodePosition.IsValid()) {
if (!nodePosition.IsSet()) {
return FrameAndNodeOffset();
}
nsIFrame* firstFrame = nullptr;
GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
GetFrameForTextRect(nodePosition.Container(), nodePosition.Offset(),
true, &firstFrame);
return FrameAndNodeOffset(firstFrame, nodePosition.mOffset);
return FrameAndNodeOffset(firstFrame, nodePosition.Offset());
}
ContentEventHandler::FrameAndNodeOffset
@ -1761,17 +1739,19 @@ ContentEventHandler::GetLastFrameInRangeForTextRect(const RawRange& aRawRange)
}
if (node->IsNodeOfType(nsINode::eTEXT)) {
nodePosition.mNode = node;
uint32_t offset;
if (node == aRawRange.GetEndContainer()) {
nodePosition.mOffset = aRawRange.EndOffset();
offset = aRawRange.EndOffset();
} else {
nodePosition.mOffset = node->Length();
offset = node->Length();
}
nodePosition.Set(node, offset);
// If the text node is empty or the last node of the range but the index
// is 0, we should store current position but continue looking for
// previous node (If there are no nodes before it, we should use current
// node position for returning its frame).
if (!nodePosition.mOffset) {
if (!nodePosition.Offset()) {
continue;
}
break;
@ -1779,18 +1759,18 @@ ContentEventHandler::GetLastFrameInRangeForTextRect(const RawRange& aRawRange)
if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
IsMozBR(node->AsContent())) {
nodePosition.mNode = node;
nodePosition.mOffset = 0;
nodePosition.Set(node, 0);
break;
}
}
if (!nodePosition.IsValid()) {
if (!nodePosition.IsSet()) {
return FrameAndNodeOffset();
}
nsIFrame* lastFrame = nullptr;
GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
GetFrameForTextRect(nodePosition.Container(),
nodePosition.Offset(),
true, &lastFrame);
if (!lastFrame) {
return FrameAndNodeOffset();
@ -1800,7 +1780,7 @@ ContentEventHandler::GetLastFrameInRangeForTextRect(const RawRange& aRawRange)
// includes at least one character in the range. Therefore, if it's not a
// text frame, we need to do nothing anymore.
if (!lastFrame->IsTextFrame()) {
return FrameAndNodeOffset(lastFrame, nodePosition.mOffset);
return FrameAndNodeOffset(lastFrame, nodePosition.Offset());
}
int32_t start, end;
@ -1812,15 +1792,16 @@ ContentEventHandler::GetLastFrameInRangeForTextRect(const RawRange& aRawRange)
// node and it's not 0, the frame shouldn't be added to the text rect. So,
// this should return previous text frame and its last offset if there is
// at least one text frame.
if (nodePosition.mOffset && nodePosition.mOffset == start) {
GetFrameForTextRect(nodePosition.mNode, --nodePosition.mOffset,
true, &lastFrame);
if (nodePosition.Offset() && nodePosition.Offset() == static_cast<uint32_t>(start)) {
nodePosition.Set(nodePosition.Container(), nodePosition.Offset() - 1);
GetFrameForTextRect(nodePosition.Container(), nodePosition.Offset(), true,
&lastFrame);
if (NS_WARN_IF(!lastFrame)) {
return FrameAndNodeOffset();
}
}
return FrameAndNodeOffset(lastFrame, nodePosition.mOffset);
return FrameAndNodeOffset(lastFrame, nodePosition.Offset());
}
ContentEventHandler::FrameRelativeRect
@ -2873,8 +2854,8 @@ ContentEventHandler::GetFlatTextLengthInRange(
LineBreakType aLineBreakType,
bool aIsRemovingNode /* = false */)
{
if (NS_WARN_IF(!aRootContent) || NS_WARN_IF(!aStartPosition.IsValid()) ||
NS_WARN_IF(!aEndPosition.IsValid()) || NS_WARN_IF(!aLength)) {
if (NS_WARN_IF(!aRootContent) || NS_WARN_IF(!aStartPosition.IsSet()) ||
NS_WARN_IF(!aEndPosition.IsSet()) || NS_WARN_IF(!aLength)) {
return NS_ERROR_INVALID_ARG;
}
@ -2896,49 +2877,49 @@ ContentEventHandler::GetFlatTextLengthInRange(
// be called after here. However, the node was already removed from the
// array of children of its parent. So, be careful to handle this case.
if (aIsRemovingNode) {
DebugOnly<nsIContent*> parent = aStartPosition.mNode->GetParent();
MOZ_ASSERT(parent && parent->IndexOf(aStartPosition.mNode) == -1,
DebugOnly<nsIContent*> parent = aStartPosition.Container()->GetParent();
MOZ_ASSERT(parent && parent->IndexOf(aStartPosition.Container()) == -1,
"At removing the node, the node shouldn't be in the array of children "
"of its parent");
MOZ_ASSERT(aStartPosition.mNode == endPosition.mNode,
MOZ_ASSERT(aStartPosition.Container() == endPosition.Container(),
"At removing the node, start and end node should be same");
MOZ_ASSERT(aStartPosition.mOffset == 0,
MOZ_ASSERT(aStartPosition.Offset() == 0,
"When the node is being removed, the start offset should be 0");
MOZ_ASSERT(static_cast<uint32_t>(endPosition.mOffset) ==
endPosition.mNode->GetChildCount(),
MOZ_ASSERT(static_cast<uint32_t>(endPosition.Offset()) ==
endPosition.Container()->GetChildCount(),
"When the node is being removed, the end offset should be child count");
iter = NS_NewPreContentIterator();
nsresult rv = iter->Init(aStartPosition.mNode);
nsresult rv = iter->Init(aStartPosition.Container());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
RawRange prevRawRange;
nsresult rv =
prevRawRange.SetStart(aStartPosition.mNode, aStartPosition.mOffset);
prevRawRange.SetStart(aStartPosition.AsRaw());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// When the end position is immediately after non-root element's open tag,
// we need to include a line break caused by the open tag.
if (endPosition.mNode != aRootContent &&
if (endPosition.Container() != aRootContent &&
endPosition.IsImmediatelyAfterOpenTag()) {
if (endPosition.mNode->HasChildren()) {
if (endPosition.Container()->HasChildren()) {
// When the end node has some children, move the end position to before
// the open tag of its first child.
nsINode* firstChild = endPosition.mNode->GetFirstChild();
nsINode* firstChild = endPosition.Container()->GetFirstChild();
if (NS_WARN_IF(!firstChild)) {
return NS_ERROR_FAILURE;
}
endPosition = NodePositionBefore(firstChild, 0);
} else {
// When the end node is empty, move the end position after the node.
nsIContent* parentContent = endPosition.mNode->GetParent();
nsIContent* parentContent = endPosition.Container()->GetParent();
if (NS_WARN_IF(!parentContent)) {
return NS_ERROR_FAILURE;
}
int32_t indexInParent = parentContent->IndexOf(endPosition.mNode);
int32_t indexInParent = parentContent->IndexOf(endPosition.Container());
if (NS_WARN_IF(indexInParent < 0)) {
return NS_ERROR_FAILURE;
}
@ -2946,9 +2927,9 @@ ContentEventHandler::GetFlatTextLengthInRange(
}
}
if (endPosition.OffsetIsValid()) {
if (endPosition.IsSetAndValid()) {
// Offset is within node's length; set end of range to that offset
rv = prevRawRange.SetEnd(endPosition.mNode, endPosition.mOffset);
rv = prevRawRange.SetEnd(endPosition.AsRaw());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -2959,9 +2940,9 @@ ContentEventHandler::GetFlatTextLengthInRange(
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else if (endPosition.mNode != aRootContent) {
} else if (endPosition.Container() != aRootContent) {
// Offset is past node's length; set end of range to end of node
rv = prevRawRange.SetEndAfter(endPosition.mNode);
rv = prevRawRange.SetEndAfter(endPosition.Container());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -2995,21 +2976,23 @@ ContentEventHandler::GetFlatTextLengthInRange(
if (node->IsNodeOfType(nsINode::eTEXT)) {
// Note: our range always starts from offset 0
if (node == endPosition.mNode) {
if (node == endPosition.Container()) {
// NOTE: We should have an offset here, as endPosition.Container() is a
// nsINode::eTEXT, which always has an offset.
*aLength += GetTextLength(content, aLineBreakType,
endPosition.mOffset);
endPosition.Offset());
} else {
*aLength += GetTextLength(content, aLineBreakType);
}
} else if (ShouldBreakLineBefore(content, aRootContent)) {
// If the start position is start of this node but doesn't include the
// open tag, don't append the line break length.
if (node == aStartPosition.mNode && !aStartPosition.IsBeforeOpenTag()) {
if (node == aStartPosition.Container() && !aStartPosition.IsBeforeOpenTag()) {
continue;
}
// If the end position is before the open tag, don't append the line
// break length.
if (node == endPosition.mNode && endPosition.IsBeforeOpenTag()) {
if (node == endPosition.Container() && endPosition.IsBeforeOpenTag()) {
continue;
}
*aLength += GetBRLength(aLineBreakType);
@ -3096,7 +3079,8 @@ ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(RawRange& aRawRange)
return NS_OK;
}
nsresult rv = aRawRange.CollapseTo(childNode, offsetInChildNode);
nsresult rv =
aRawRange.CollapseTo(RawRangeBoundary(childNode, offsetInChildNode));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

Просмотреть файл

@ -46,56 +46,62 @@ private:
class MOZ_STACK_CLASS RawRange final
{
public:
RawRange()
: mStartOffset(0)
, mEndOffset(0)
{
}
RawRange() {}
void Clear()
{
mRoot = mStartContainer = mEndContainer = nullptr;
mStartOffset = mEndOffset = 0;
mRoot = nullptr;
mStart = RangeBoundary();
mEnd = RangeBoundary();
}
bool IsPositioned() const
{
return mStartContainer && mEndContainer;
return mStart.IsSet() && mEnd.IsSet();
}
bool Collapsed() const
{
return mStartContainer == mEndContainer &&
mStartOffset == mEndOffset &&
IsPositioned();
return mStart == mEnd && IsPositioned();
}
nsINode* GetStartContainer() const { return mStartContainer; }
nsINode* GetEndContainer() const { return mEndContainer; }
uint32_t StartOffset() const { return mStartOffset; }
uint32_t EndOffset() const { return mEndOffset; }
nsINode* GetStartContainer() const { return mStart.Container(); }
nsINode* GetEndContainer() const { return mEnd.Container(); }
uint32_t StartOffset() const { return mStart.Offset(); }
uint32_t EndOffset() const { return mEnd.Offset(); }
nsIContent* StartRef() const { return mStart.Ref(); }
nsIContent* EndRef() const { return mEnd.Ref(); }
nsresult CollapseTo(nsINode* aContainer, uint32_t aOffset)
// XXX: Make these use RangeBoundaries...
nsresult CollapseTo(const RawRangeBoundary& aBoundary)
{
return SetStartAndEnd(aContainer, aOffset, aContainer, aOffset);
return SetStartAndEnd(aBoundary, aBoundary);
}
nsresult SetStart(nsINode* aStartContainer, uint32_t aStartOffset);
nsresult SetEnd(nsINode* aEndContainer, uint32_t aEndOffset);
nsresult SetStart(const RawRangeBoundary& aStart);
nsresult SetEnd(const RawRangeBoundary& aEnd);
// NOTE: These helpers can hide performance problems, as they perform a
// search to find aStartOffset in aStartContainer.
nsresult SetStart(nsINode* aStartContainer, uint32_t aStartOffset) {
return SetStart(RawRangeBoundary(aStartContainer, aStartOffset));
}
nsresult SetEnd(nsINode* aEndContainer, uint32_t aEndOffset) {
return SetEnd(RawRangeBoundary(aEndContainer, aEndOffset));
}
nsresult SetEndAfter(nsINode* aEndContainer);
void SetStartAndEnd(const nsRange* aRange);
nsresult SetStartAndEnd(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset);
nsresult SetStartAndEnd(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd);
nsresult SelectNodeContents(nsINode* aNodeToSelectContents);
private:
bool IsValidOffset(nsINode* aContainer, uint32_t aOffset) const;
nsINode* IsValidBoundary(nsINode* aNode) const;
inline void AssertStartIsBeforeOrEqualToEnd();
nsCOMPtr<nsINode> mRoot;
nsCOMPtr<nsINode> mStartContainer;
nsCOMPtr<nsINode> mEndContainer;
uint32_t mStartOffset;
uint32_t mEndOffset;
RangeBoundary mStart;
RangeBoundary mEnd;
};
public:
@ -163,76 +169,72 @@ public:
// When mNode is an element and mOffset is 0, the start position means after
// the open tag of mNode.
// This is useful to receive one or more sets of them instead of nsRange.
struct NodePosition
// This type is intended to be used for short-lived operations, and is thus
// marked MOZ_STACK_CLASS.
struct MOZ_STACK_CLASS NodePosition : public RangeBoundary
{
nsCOMPtr<nsINode> mNode;
int32_t mOffset;
// Only when mNode is an element node and mOffset is 0, mAfterOpenTag is
// referred.
bool mAfterOpenTag;
bool mAfterOpenTag = true;
NodePosition()
: mOffset(-1)
, mAfterOpenTag(true)
: RangeBoundary()
{
}
NodePosition(nsINode* aNode, int32_t aOffset)
: mNode(aNode)
, mOffset(aOffset)
, mAfterOpenTag(true)
NodePosition(nsINode* aContainer, int32_t aOffset)
: RangeBoundary(aContainer, aOffset)
{
}
NodePosition(nsINode* aContainer, nsIContent* aRef)
: RangeBoundary(aContainer, aRef)
{
}
explicit NodePosition(const nsIFrame::ContentOffsets& aContentOffsets)
: mNode(aContentOffsets.content)
, mOffset(aContentOffsets.offset)
, mAfterOpenTag(true)
{
}
protected:
NodePosition(nsINode* aNode, int32_t aOffset, bool aAfterOpenTag)
: mNode(aNode)
, mOffset(aOffset)
, mAfterOpenTag(aAfterOpenTag)
: RangeBoundary(aContentOffsets.content, aContentOffsets.offset)
{
}
public:
bool operator==(const NodePosition& aOther) const
{
return mNode == aOther.mNode &&
mOffset == aOther.mOffset &&
mAfterOpenTag == aOther.mAfterOpenTag;
return RangeBoundary::operator==(aOther) &&
mAfterOpenTag == aOther.mAfterOpenTag;
}
bool IsValid() const
{
return mNode && mOffset >= 0;
}
bool OffsetIsValid() const
{
return IsValid() && static_cast<uint32_t>(mOffset) <= mNode->Length();
}
bool IsBeforeOpenTag() const
{
return IsValid() && mNode->IsElement() && !mOffset && !mAfterOpenTag;
return IsSet() &&
Container()->IsElement() &&
!Ref() &&
!mAfterOpenTag;
}
bool IsImmediatelyAfterOpenTag() const
{
return IsValid() && mNode->IsElement() && !mOffset && mAfterOpenTag;
return IsSet() &&
Container()->IsElement() &&
!Ref() &&
mAfterOpenTag;
}
};
// NodePositionBefore isn't good name if mNode isn't an element node nor
// mOffset is not 0, though, when mNode is an element node and mOffset is 0,
// this is treated as before the open tag of mNode.
// NodePositionBefore isn't good name if Container() isn't an element node nor
// Offset() is not 0, though, when Container() is an element node and mOffset
// is 0, this is treated as before the open tag of Container().
struct NodePositionBefore final : public NodePosition
{
NodePositionBefore(nsINode* aNode, int32_t aOffset)
: NodePosition(aNode, aOffset, false)
NodePositionBefore(nsINode* aContainer, int32_t aOffset)
: NodePosition(aContainer, aOffset)
{
mAfterOpenTag = false;
}
NodePositionBefore(nsINode* aContainer, nsIContent* aRef)
: NodePosition(aContainer, aRef)
{
mAfterOpenTag = false;
}
};

Просмотреть файл

@ -48,6 +48,29 @@ ToChar(bool aBool)
return aBool ? "true" : "false";
}
// This method determines the node to use for the point before the current node.
// If you have the following aContent and aContainer, and want to represent the
// following point for `NodePosition` or `RangeBoundary`:
//
// <parent> {node} {node} | {node} </parent>
// ^ ^ ^
// aContainer point aContent
//
// This function will shift `aContent` to the left into the format which
// `NodePosition` and `RangeBoundary` use:
//
// <parent> {node} {node} | {node} </parent>
// ^ ^ ^
// aContainer result point
static nsIContent*
PointBefore(nsINode* aContainer, nsIContent* aContent)
{
if (aContent) {
return aContent->GetPreviousSibling();
}
return aContainer->GetLastChild();
}
class WritingModeToString final : public nsAutoCString
{
public:
@ -123,8 +146,8 @@ public:
NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver)
// Note that we don't need to add mFirstAddedNodeContainer nor
// mLastAddedNodeContainer to cycle collection because they are non-null only
// Note that we don't need to add mFirstAddedContainer nor
// mLastAddedContainer to cycle collection because they are non-null only
// during short time and shouldn't be touched while they are non-null.
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
@ -174,9 +197,7 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
IMEContentObserver::IMEContentObserver()
: mFirstAddedNodeOffset(0)
, mLastAddedNodeOffset(0)
, mESM(nullptr)
: mESM(nullptr)
, mIMENotificationRequests(nullptr)
, mSuppressNotifications(0)
, mPreCharacterDataChangeLength(-1)
@ -1001,13 +1022,16 @@ IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
void
IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
int32_t aStartIndex,
int32_t aEndIndex)
nsIContent* aFirstContent,
nsIContent* aLastContent)
{
if (!NeedsTextChangeNotification()) {
return;
}
MOZ_ASSERT_IF(aFirstContent, aFirstContent->GetParentNode() == aContainer);
MOZ_ASSERT_IF(aLastContent, aLastContent->GetParentNode() == aContainer);
mStartOfRemovingTextRangeCache.Clear();
// If it's in a document change, nodes are added consecutively. Therefore,
@ -1019,9 +1043,9 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
// the last node in mEndOfAddedTextCache. Clear it.
mEndOfAddedTextCache.Clear();
if (!HasAddedNodesDuringDocumentChange()) {
mFirstAddedNodeContainer = mLastAddedNodeContainer = aContainer;
mFirstAddedNodeOffset = aStartIndex;
mLastAddedNodeOffset = aEndIndex;
mFirstAddedContainer = mLastAddedContainer = aContainer;
mFirstAddedContent = aFirstContent;
mLastAddedContent = aLastContent;
MOZ_LOG(sIMECOLog, LogLevel::Debug,
("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
"consecutive added nodes", this));
@ -1030,17 +1054,17 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
// If first node being added is not next node of the last node,
// notify IME of the previous range first, then, restart to cache the
// range.
if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aStartIndex))) {
if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aFirstContent))) {
// Flush the old range first.
MaybeNotifyIMEOfAddedTextDuringDocumentChange();
mFirstAddedNodeContainer = aContainer;
mFirstAddedNodeOffset = aStartIndex;
mFirstAddedContainer = aContainer;
mFirstAddedContent = aFirstContent;
MOZ_LOG(sIMECOLog, LogLevel::Debug,
("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
"consecutive added nodes", this));
}
mLastAddedNodeContainer = aContainer;
mLastAddedNodeOffset = aEndIndex;
mLastAddedContainer = aContainer;
mLastAddedContent = aLastContent;
return;
}
MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(),
@ -1048,11 +1072,14 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
uint32_t offset = 0;
nsresult rv = NS_OK;
if (!mEndOfAddedTextCache.Match(aContainer, aStartIndex)) {
if (!mEndOfAddedTextCache.Match(aContainer,
aFirstContent->GetPreviousSibling())) {
mEndOfAddedTextCache.Clear();
rv = ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0),
NodePositionBefore(aContainer, aStartIndex),
NodePositionBefore(aContainer,
PointBefore(aContainer,
aFirstContent)),
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED((rv)))) {
return;
@ -1064,8 +1091,10 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
// get offset at the end of the last added node
uint32_t addingLength = 0;
rv = ContentEventHandler::GetFlatTextLengthInRange(
NodePositionBefore(aContainer, aStartIndex),
NodePosition(aContainer, aEndIndex),
NodePositionBefore(aContainer,
PointBefore(aContainer,
aFirstContent)),
NodePosition(aContainer, aLastContent),
mRootContent, &addingLength,
LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED((rv)))) {
@ -1077,7 +1106,7 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
// NotifyContentAdded() is for adding next node. Therefore, caching the text
// length can skip to compute the text length before the adding node and
// before of it.
mEndOfAddedTextCache.Cache(aContainer, aEndIndex, offset + addingLength);
mEndOfAddedTextCache.Cache(aContainer, aLastContent, offset + addingLength);
if (!addingLength) {
return;
@ -1093,27 +1122,27 @@ void
IMEContentObserver::ContentAppended(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aFirstNewContent,
int32_t aNewIndexInContainer)
int32_t /* unused */)
{
NotifyContentAdded(aContainer, aNewIndexInContainer,
aContainer->GetChildCount());
NotifyContentAdded(NODE_FROM(aContainer, aDocument),
aFirstNewContent, aContainer->GetLastChild());
}
void
IMEContentObserver::ContentInserted(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
int32_t aIndexInContainer)
int32_t /* unused */)
{
NotifyContentAdded(NODE_FROM(aContainer, aDocument),
aIndexInContainer, aIndexInContainer + 1);
aChild, aChild);
}
void
IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
int32_t aIndexInContainer,
int32_t /* unused */,
nsIContent* aPreviousSibling)
{
if (!NeedsTextChangeNotification()) {
@ -1127,18 +1156,19 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
uint32_t offset = 0;
nsresult rv = NS_OK;
if (!mStartOfRemovingTextRangeCache.Match(containerNode, aIndexInContainer)) {
if (!mStartOfRemovingTextRangeCache.Match(containerNode, aPreviousSibling)) {
// At removing a child node of aContainer, we need the line break caused
// by open tag of aContainer. Be careful when aIndexInContainer is 0.
// by open tag of aContainer. Be careful when aPreviousSibling is nullptr.
rv = ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0),
NodePosition(containerNode, aIndexInContainer),
NodePosition(containerNode, aPreviousSibling),
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) {
mStartOfRemovingTextRangeCache.Clear();
return;
}
mStartOfRemovingTextRangeCache.Cache(containerNode, aIndexInContainer,
mStartOfRemovingTextRangeCache.Cache(containerNode, aPreviousSibling,
offset);
} else {
offset = mStartOfRemovingTextRangeCache.mFlatTextLength;
@ -1230,8 +1260,8 @@ IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
void
IMEContentObserver::ClearAddedNodesDuringDocumentChange()
{
mFirstAddedNodeContainer = mLastAddedNodeContainer = nullptr;
mFirstAddedNodeOffset = mLastAddedNodeOffset = 0;
mFirstAddedContainer = mLastAddedContainer = nullptr;
mFirstAddedContent = mLastAddedContent = nullptr;
MOZ_LOG(sIMECOLog, LogLevel::Debug,
("0x%p IMEContentObserver::ClearAddedNodesDuringDocumentChange()"
", finished storing consecutive nodes", this));
@ -1256,66 +1286,46 @@ IMEContentObserver::GetChildNode(nsINode* aParent, int32_t aOffset)
bool
IMEContentObserver::IsNextNodeOfLastAddedNode(nsINode* aParent,
int32_t aOffset) const
nsIContent* aChild) const
{
MOZ_ASSERT(aParent);
MOZ_ASSERT(aOffset >= 0 &&
aOffset <= static_cast<int32_t>(aParent->Length()));
MOZ_ASSERT(aChild && aChild->GetParentNode() == aParent);
MOZ_ASSERT(mRootContent);
MOZ_ASSERT(HasAddedNodesDuringDocumentChange());
// If the parent node isn't changed, we can check it only with offset.
if (aParent == mLastAddedNodeContainer) {
if (NS_WARN_IF(mLastAddedNodeOffset != aOffset)) {
// If the parent node isn't changed, we can check that mLastAddedContent has
// aChild as its next sibling.
if (aParent == mLastAddedContainer) {
if (NS_WARN_IF(mLastAddedContent->GetNextSibling() != aChild)) {
return false;
}
return true;
}
// If the parent node is changed, that means that given offset should be the
// last added node not having next sibling.
if (NS_WARN_IF(mLastAddedNodeOffset !=
static_cast<int32_t>(mLastAddedNodeContainer->Length()))) {
// If the parent node is changed, that means that the recorded last added node
// shouldn't have a sibling.
if (NS_WARN_IF(mLastAddedContent->GetNextSibling())) {
return false;
}
// If the node is aParent is a descendant of mLastAddedNodeContainer,
// aOffset should be 0.
if (mLastAddedNodeContainer == aParent->GetParent()) {
if (NS_WARN_IF(aOffset)) {
// If the node is aParent is a descendant of mLastAddedContainer,
// aChild should be the first child in the new container.
if (mLastAddedContainer == aParent->GetParent()) {
if (NS_WARN_IF(aChild->GetPreviousSibling())) {
return false;
}
return true;
}
// Otherwise, we need to check it even with slow path.
nsIContent* lastAddedContent =
GetChildNode(mLastAddedNodeContainer, mLastAddedNodeOffset - 1);
if (NS_WARN_IF(!lastAddedContent)) {
return false;
}
nsIContent* nextContentOfLastAddedContent =
lastAddedContent->GetNextNode(mRootContent->GetParentNode());
mLastAddedContent->GetNextNode(mRootContent->GetParentNode());
if (NS_WARN_IF(!nextContentOfLastAddedContent)) {
return false;
}
nsIContent* startContent = GetChildNode(aParent, aOffset);
if (NS_WARN_IF(!startContent) ||
NS_WARN_IF(nextContentOfLastAddedContent != startContent)) {
if (NS_WARN_IF(nextContentOfLastAddedContent != aChild)) {
return false;
}
#ifdef DEBUG
NS_WARNING_ASSERTION(
!aOffset || aOffset == static_cast<int32_t>(aParent->Length() - 1),
"Used slow path for aParent");
NS_WARNING_ASSERTION(
!(mLastAddedNodeOffset - 1) ||
mLastAddedNodeOffset ==
static_cast<int32_t>(mLastAddedNodeContainer->Length()),
"Used slow path for mLastAddedNodeContainer");
#endif // #ifdef DEBUG
return true;
}
@ -1338,8 +1348,9 @@ IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()
nsresult rv =
ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0),
NodePosition(mFirstAddedNodeContainer,
mFirstAddedNodeOffset),
NodePosition(mFirstAddedContainer,
PointBefore(mFirstAddedContainer,
mFirstAddedContent)),
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) {
ClearAddedNodesDuringDocumentChange();
@ -1350,10 +1361,10 @@ IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()
uint32_t length;
rv =
ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mFirstAddedNodeContainer,
mFirstAddedNodeOffset),
NodePosition(mLastAddedNodeContainer,
mLastAddedNodeOffset),
NodePosition(mFirstAddedContainer,
PointBefore(mFirstAddedContainer,
mFirstAddedContent)),
NodePosition(mLastAddedContainer, mLastAddedContent),
mRootContent, &length, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) {
ClearAddedNodesDuringDocumentChange();

Просмотреть файл

@ -202,9 +202,9 @@ private:
/**
* MaybeNotifyIMEOfAddedTextDuringDocumentChange() may send text change
* notification caused by the nodes added between mFirstAddedNodeOffset in
* mFirstAddedNodeContainer and mLastAddedNodeOffset in
* mLastAddedNodeContainer and forgets the range.
* notification caused by the nodes added between mFirstAddedContent in
* mFirstAddedContainer and mLastAddedContent in
* mLastAddedContainer and forgets the range.
*/
void MaybeNotifyIMEOfAddedTextDuringDocumentChange();
@ -232,15 +232,14 @@ private:
*/
bool HasAddedNodesDuringDocumentChange() const
{
return mFirstAddedNodeContainer && mLastAddedNodeContainer;
return mFirstAddedContainer && mLastAddedContainer;
}
/**
* Returns true if the node at aOffset in aParent is next node of the node at
* mLastAddedNodeOffset in mLastAddedNodeContainer in pre-order tree
* traversal of the DOM.
* Returns true if the passed-in node in aParent is the next node of
* mLastAddedContent in pre-order tree traversal of the DOM.
*/
bool IsNextNodeOfLastAddedNode(nsINode* aParent, int32_t aOffset) const;
bool IsNextNodeOfLastAddedNode(nsINode* aParent, nsIContent* aChild) const;
void PostFocusSetNotification();
void MaybeNotifyIMEOfFocusSet();
@ -256,7 +255,9 @@ private:
void CancelNotifyingIMEOfPositionChange();
void PostCompositionEventHandledNotification();
void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
void NotifyContentAdded(nsINode* aContainer,
nsIContent* aFirstContent,
nsIContent* aLastContent);
void ObserveEditableNode();
/**
* NotifyIMEOfBlur() notifies IME of blur.
@ -431,42 +432,43 @@ private:
*/
struct FlatTextCache
{
// mContainerNode and mNodeOffset represent a point in DOM tree. E.g.,
// if mContainerNode is a div element, mNodeOffset is index of its child.
// mContainerNode and mNode represent a point in DOM tree. E.g.,
// if mContainerNode is a div element, mNode is a child.
nsCOMPtr<nsINode> mContainerNode;
int32_t mNodeOffset;
// mNode points to the last child which participates in the current
// mFlatTextLength. If mNode is null, then that means that the end point for
// mFlatTextLength is immediately before the first child of mContainerNode.
nsCOMPtr<nsINode> mNode;
// Length of flat text generated from contents between the start of content
// and a child node whose index is mNodeOffset of mContainerNode.
uint32_t mFlatTextLength;
FlatTextCache()
: mNodeOffset(0)
, mFlatTextLength(0)
: mFlatTextLength(0)
{
}
void Clear()
{
mContainerNode = nullptr;
mNodeOffset = 0;
mNode = nullptr;
mFlatTextLength = 0;
}
void Cache(nsINode* aContainer, int32_t aNodeOffset,
void Cache(nsINode* aContainer, nsINode* aNode,
uint32_t aFlatTextLength)
{
MOZ_ASSERT(aContainer, "aContainer must not be null");
MOZ_ASSERT(
aNodeOffset <= static_cast<int32_t>(aContainer->GetChildCount()),
"aNodeOffset must be same as or less than the count of children");
MOZ_ASSERT(!aNode || aNode->GetParentNode() == aContainer,
"aNode must be either null or a child of aContainer");
mContainerNode = aContainer;
mNodeOffset = aNodeOffset;
mNode = aNode;
mFlatTextLength = aFlatTextLength;
}
bool Match(nsINode* aContainer, int32_t aNodeOffset) const
bool Match(nsINode* aContainer, nsINode* aNode) const
{
return aContainer == mContainerNode && aNodeOffset == mNodeOffset;
return aContainer == mContainerNode && aNode == mNode;
}
};
// mEndOfAddedTextCache caches text length from the start of content to
@ -479,26 +481,25 @@ private:
// handled by the editor and no other mutation (e.g., adding node) occur.
FlatTextCache mStartOfRemovingTextRangeCache;
// mFirstAddedNodeContainer is parent node of first added node in current
// mFirstAddedContainer is parent node of first added node in current
// document change. So, this is not nullptr only when a node was added
// during a document change and the change has not been included into
// mTextChangeData yet.
// Note that this shouldn't be in cycle collection since this is not nullptr
// only during a document change.
nsCOMPtr<nsINode> mFirstAddedNodeContainer;
// mLastAddedNodeContainer is parent node of last added node in current
nsCOMPtr<nsINode> mFirstAddedContainer;
// mLastAddedContainer is parent node of last added node in current
// document change. So, this is not nullptr only when a node was added
// during a document change and the change has not been included into
// mTextChangeData yet.
// Note that this shouldn't be in cycle collection since this is not nullptr
// only during a document change.
nsCOMPtr<nsINode> mLastAddedNodeContainer;
// mFirstAddedNodeOffset is offset of first added node in
// mFirstAddedNodeContainer.
int32_t mFirstAddedNodeOffset;
// mLastAddedNodeOffset is offset of *after* last added node in
// mLastAddedNodeContainer. I.e., the index of last added node + 1.
int32_t mLastAddedNodeOffset;
nsCOMPtr<nsINode> mLastAddedContainer;
// mFirstAddedContent is the first node added in mFirstAddedContainer.
nsCOMPtr<nsIContent> mFirstAddedContent;
// mLastAddedContent is the last node added in mLastAddedContainer;
nsCOMPtr<nsIContent> mLastAddedContent;
TextChangeData mTextChangeData;

Просмотреть файл

@ -80,12 +80,6 @@ interface nsIDOMWindowUtils : nsISupports {
*/
readonly attribute boolean docCharsetIsForced;
/**
* Get current cursor type from this window
* @return the current value of nsCursor
*/
short getCursorType();
/**
* Function to get metadata associated with the window's current document
* @param aName the name of the metadata. This should be all lowercase.

Просмотреть файл

@ -126,6 +126,15 @@ ImageContainerListener::ClearImageContainer()
mImageContainer = nullptr;
}
void
ImageContainerListener::DropImageClient()
{
MutexAutoLock lock(mLock);
if (mImageContainer) {
mImageContainer->DropImageClient();
}
}
already_AddRefed<ImageClient>
ImageContainer::GetImageClient()
{
@ -162,12 +171,10 @@ ImageContainer::EnsureImageClient()
mImageClient = imageBridge->CreateImageClient(CompositableType::IMAGE, this);
if (mImageClient) {
mAsyncContainerHandle = mImageClient->GetAsyncHandle();
mNotifyCompositeListener = new ImageContainerListener(this);
} else {
// It's okay to drop the async container handle since the ImageBridgeChild
// is going to die anyway.
mAsyncContainerHandle = CompositableHandle();
mNotifyCompositeListener = nullptr;
}
}
}
@ -183,6 +190,7 @@ ImageContainer::ImageContainer(Mode flag)
mCurrentProducerID(-1)
{
if (flag == ASYNCHRONOUS) {
mNotifyCompositeListener = new ImageContainerListener(this);
EnsureImageClient();
}
}
@ -381,6 +389,7 @@ CompositableHandle ImageContainer::GetAsyncContainerHandle()
{
NS_ASSERTION(IsAsync(), "Shared image ID is only relevant to async ImageContainers");
NS_ASSERTION(mAsyncContainerHandle, "Should have a shared image ID");
RecursiveMutexAutoLock mon(mRecursiveMutex);
EnsureImageClient();
return mAsyncContainerHandle;
}

Просмотреть файл

@ -344,6 +344,7 @@ public:
void NotifyComposite(const ImageCompositeNotification& aNotification);
void ClearImageContainer();
void DropImageClient();
private:
typedef mozilla::Mutex Mutex;

Просмотреть файл

@ -249,7 +249,7 @@ ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
mCanSend = false;
{
MutexAutoLock lock(mContainerMapLock);
mImageContainers.Clear();
mImageContainerListeners.Clear();
}
}
@ -322,8 +322,8 @@ ImageBridgeChild::Connect(CompositableClient* aCompositable,
{
MutexAutoLock lock(mContainerMapLock);
MOZ_ASSERT(!mImageContainers.Contains(id));
mImageContainers.Put(id, aImageContainer);
MOZ_ASSERT(!mImageContainerListeners.Contains(id));
mImageContainerListeners.Put(id, aImageContainer->GetImageContainerListener());
}
CompositableHandle handle(id);
@ -335,7 +335,7 @@ void
ImageBridgeChild::ForgetImageContainer(const CompositableHandle& aHandle)
{
MutexAutoLock lock(mContainerMapLock);
mImageContainers.Remove(aHandle.Value());
mImageContainerListeners.Remove(aHandle.Value());
}
Thread* ImageBridgeChild::GetThread() const
@ -739,9 +739,16 @@ ImageBridgeChild::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier&
if (disablingWebRender) {
// ImageHost is incompatible between WebRender enabled and WebRender disabled.
// Then drop all ImageContainers' ImageClients during disabling WebRender.
MutexAutoLock lock(mContainerMapLock);
for (auto iter = mImageContainers.Iter(); !iter.Done(); iter.Next()) {
ImageContainer* container = iter.Data();
nsTArray<RefPtr<ImageContainerListener> > listeners;
{
MutexAutoLock lock(mContainerMapLock);
for (auto iter = mImageContainerListeners.Iter(); !iter.Done(); iter.Next()) {
RefPtr<ImageContainerListener>& listener = iter.Data();
listeners.AppendElement(listener);
}
}
// Drop ImageContainer's ImageClient whithout holding mContainerMapLock to avoid deadlock.
for (auto container : listeners) {
container->DropImageClient();
}
}
@ -1014,10 +1021,8 @@ ImageBridgeChild::RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&
RefPtr<ImageContainerListener> listener;
{
MutexAutoLock lock(mContainerMapLock);
ImageContainer* imageContainer;
imageContainer = mImageContainers.Get(n.compositable().Value());
if (imageContainer) {
listener = imageContainer->GetImageContainerListener();
if (auto entry = mImageContainerListeners.Lookup(n.compositable().Value())) {
listener = entry.Data();
}
}
if (listener) {
@ -1128,7 +1133,7 @@ ImageBridgeChild::ReleaseCompositable(const CompositableHandle& aHandle)
{
MutexAutoLock lock(mContainerMapLock);
mImageContainers.Remove(aHandle.Value());
mImageContainerListeners.Remove(aHandle.Value());
}
}

Просмотреть файл

@ -41,6 +41,7 @@ namespace layers {
class AsyncCanvasRenderer;
class ImageClient;
class ImageContainer;
class ImageContainerListener;
class ImageBridgeParent;
class CompositableClient;
struct CompositableTransaction;
@ -398,7 +399,7 @@ private:
* Mapping from async compositable IDs to image containers.
*/
Mutex mContainerMapLock;
nsDataHashtable<nsUint64HashKey, ImageContainer*> mImageContainers;
nsRefPtrHashtable<nsUint64HashKey, ImageContainerListener> mImageContainerListeners;
};
} // namespace layers

Просмотреть файл

@ -394,6 +394,26 @@ public:
RectIterator RectIter() const { return RectIterator(*this); }
static inline pixman_box32_t RectToBox(const nsRect &aRect)
{
pixman_box32_t box = { aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost() };
return box;
}
static inline pixman_box32_t RectToBox(const mozilla::gfx::IntRect &aRect)
{
pixman_box32_t box = { aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost() };
return box;
}
static inline nsRect BoxToRect(const pixman_box32_t &aBox)
{
return nsRect(aBox.x1, aBox.y1,
aBox.x2 - aBox.x1,
aBox.y2 - aBox.y1);
}
private:
pixman_region32_t mImpl;
@ -431,26 +451,6 @@ private:
return *this;
}
static inline pixman_box32_t RectToBox(const nsRect &aRect)
{
pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
return box;
}
static inline pixman_box32_t RectToBox(const mozilla::gfx::IntRect &aRect)
{
pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
return box;
}
static inline nsRect BoxToRect(const pixman_box32_t &aBox)
{
return nsRect(aBox.x1, aBox.y1,
aBox.x2 - aBox.x1,
aBox.y2 - aBox.y1);
}
pixman_region32_t* Impl() const
{
return const_cast<pixman_region32_t*>(&mImpl);

Просмотреть файл

@ -673,13 +673,17 @@ MessageChannel::CanSend() const
void
MessageChannel::WillDestroyCurrentMessageLoop()
{
#if !defined(ANDROID)
#if defined(DEBUG)
#if defined(MOZ_CRASHREPORTER)
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProtocolName"),
nsDependentCString(mName));
#endif
MOZ_CRASH("MessageLoop destroyed before MessageChannel that's bound to it");
#endif
// Clear mWorkerThread to avoid posting to it in the future.
MonitorAutoLock lock(*mMonitor);
mWorkerLoop = nullptr;
}
void
@ -1964,7 +1968,7 @@ MessageChannel::MessageTask::Post()
if (eventTarget) {
eventTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL);
} else {
} else if (mChannel->mWorkerLoop) {
mChannel->mWorkerLoop->PostTask(self.forget());
}
}
@ -2406,7 +2410,9 @@ MessageChannel::OnChannelConnected(int32_t peer_id)
mPeerPidSet = true;
mPeerPid = peer_id;
RefPtr<CancelableRunnable> task = mOnChannelConnectedTask;
mWorkerLoop->PostTask(task.forget());
if (mWorkerLoop) {
mWorkerLoop->PostTask(task.forget());
}
}
void
@ -2599,7 +2605,9 @@ MessageChannel::OnNotifyMaybeChannelError()
&MessageChannel::OnNotifyMaybeChannelError);
RefPtr<Runnable> task = mChannelErrorTask;
// 10 ms delay is completely arbitrary
mWorkerLoop->PostDelayedTask(task.forget(), 10);
if (mWorkerLoop) {
mWorkerLoop->PostDelayedTask(task.forget(), 10);
}
return;
}
@ -2611,7 +2619,7 @@ MessageChannel::PostErrorNotifyTask()
{
mMonitor->AssertCurrentThreadOwns();
if (mChannelErrorTask)
if (mChannelErrorTask || !mWorkerLoop)
return;
// This must be the last code that runs on this thread!

Просмотреть файл

@ -40,6 +40,14 @@ function isMatchingDestructor(constructor, edge)
if (variable.Name[1].charAt(0) != '~')
return false;
// Note that in some situations, a regular function can begin with '~', so
// we don't necessarily have a destructor in hand. This is probably a
// sixgill artifact, but in js::wasm::ModuleGenerator::~ModuleGenerator, a
// templatized static inline EraseIf is invoked, and it gets named ~EraseIf
// for some reason.
if (!("PEdgeCallInstance" in edge))
return false;
var constructExp = constructor.PEdgeCallInstance.Exp;
assert(constructExp.Kind == "Var");
@ -55,7 +63,7 @@ function isMatchingDestructor(constructor, edge)
// treat each instance separately, such as when different regions of a function
// body were guarded by these constructors and you needed to do something
// different with each.)
function allRAIIGuardedCallPoints(bodies, body, isConstructor)
function allRAIIGuardedCallPoints(typeInfo, bodies, body, isConstructor)
{
if (!("PEdge" in body))
return [];
@ -70,7 +78,7 @@ function allRAIIGuardedCallPoints(bodies, body, isConstructor)
continue;
var variable = callee.Variable;
assert(variable.Kind == "Func");
if (!isConstructor(edge.Type, variable.Name))
if (!isConstructor(typeInfo, edge.Type, variable.Name))
continue;
if (!("PEdgeCallInstance" in edge))
continue;

Просмотреть файл

@ -5,6 +5,7 @@
loadRelativeToScript('utility.js');
loadRelativeToScript('annotations.js');
loadRelativeToScript('CFG.js');
loadRelativeToScript('dumpCFG.js');
var sourceRoot = (os.getenv('SOURCE') || '') + '/'
@ -29,8 +30,6 @@ var batch = (scriptArgs[5]|0) || 1;
var numBatches = (scriptArgs[6]|0) || 1;
var tmpfile = scriptArgs[7] || "tmp.txt";
GCSuppressionTypes = loadTypeInfo(typeInfoFile)["Suppress GC"] || [];
var gcFunctions = {};
var text = snarf("gcFunctions.lst").split("\n");
assert(text.pop().length == 0);
@ -45,6 +44,8 @@ for (var line of text) {
}
text = null;
var typeInfo = loadTypeInfo(typeInfoFile);
var gcEdges = {};
text = snarf(gcEdgesFile).split('\n');
assert(text.pop().length == 0);
@ -749,7 +750,8 @@ function printEntryTrace(functionName, entry)
function isRootedType(type)
{
return type.Kind == "CSU" && isRootedTypeName(type.Name);
return type.Kind == "CSU" && ((type.Name in typeInfo.RootedPointers) ||
(type.Name in typeInfo.RootedGCThings));
}
function typeDesc(type)
@ -841,7 +843,7 @@ function process(name, json) {
for (var body of functionBodies)
body.suppressed = [];
for (var body of functionBodies) {
for (var [pbody, id] of allRAIIGuardedCallPoints(functionBodies, body, isSuppressConstructor))
for (var [pbody, id] of allRAIIGuardedCallPoints(typeInfo, functionBodies, body, isSuppressConstructor))
pbody.suppressed[id] = true;
}
processBodies(functionName);

Просмотреть файл

@ -2,10 +2,6 @@
"use strict";
// RAII types within which we should assume GC is suppressed, eg
// AutoSuppressGC.
var GCSuppressionTypes = [];
// Ignore calls made through these function pointers
var ignoreIndirectCalls = {
"mallocSizeOf" : true,
@ -43,11 +39,20 @@ function indirectCallCannotGC(fullCaller, fullVariable)
return true;
// template method called during marking and hence cannot GC
if (name == "op" && caller.indexOf("bool js::WeakMap<Key, Value, HashPolicy>::keyNeedsMark(JSObject*)") != -1)
if (name == "op" && caller.includes("bool js::WeakMap<Key, Value, HashPolicy>::keyNeedsMark(JSObject*)"))
{
return true;
}
// Call through a 'callback' function pointer, in a place where we're going
// to be throwing a JS exception.
if (name == "callback" && caller.includes("js::ErrorToException"))
return true;
// The math cache only gets called with non-GC math functions.
if (name == "f" && caller.includes("js::MathCache::lookup"))
return true;
return false;
}
@ -218,6 +223,8 @@ var ignoreFunctions = {
"uint32 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true,
"void js::Nursery::freeMallocedBuffers()" : true,
"void js::AutoEnterOOMUnsafeRegion::crash(uint64, int8*)" : true,
// It would be cool to somehow annotate that nsTHashtable<T> will use
// nsTHashtable<T>::s_MatchEntry for its matchEntry function pointer, but
// there is no mechanism for that. So we will just annotate a particularly
@ -287,11 +294,11 @@ function ignoreGCFunction(mangled)
return true;
// Templatized function
if (fun.indexOf("void nsCOMPtr<T>::Assert_NoQueryNeeded()") >= 0)
if (fun.includes("void nsCOMPtr<T>::Assert_NoQueryNeeded()"))
return true;
// These call through an 'op' function pointer.
if (fun.indexOf("js::WeakMap<Key, Value, HashPolicy>::getDelegate(") >= 0)
if (fun.includes("js::WeakMap<Key, Value, HashPolicy>::getDelegate("))
return true;
// XXX modify refillFreeList<NoGC> to not need data flow analysis to understand it cannot GC.
@ -307,9 +314,27 @@ function stripUCSAndNamespace(name)
return name;
}
function isRootedGCTypeName(name)
function extraRootedGCThings()
{
return (name == "JSAddonId");
return [ 'JSAddonId' ];
}
function extraRootedPointers()
{
return [
'ModuleValidator',
'JSErrorResult',
'WrappableJSErrorResult',
// These are not actually rooted, but are only used in the context of
// AutoKeepAtoms.
'js::frontend::TokenStream',
'js::frontend::TokenStream::Position',
'mozilla::ErrorResult',
'mozilla::IgnoredErrorResult',
'mozilla::dom::binding_detail::FastErrorResult',
];
}
function isRootedGCPointerTypeName(name)
@ -319,33 +344,16 @@ function isRootedGCPointerTypeName(name)
if (name.startsWith('MaybeRooted<'))
return /\(js::AllowGC\)1u>::RootType/.test(name);
if (name == "ErrorResult" ||
name == "JSErrorResult" ||
name == "WrappableJSErrorResult" ||
name == "binding_detail::FastErrorResult" ||
name == "IgnoredErrorResult" ||
name == "frontend::TokenStream" ||
name == "frontend::TokenStream::Position" ||
name == "ModuleValidator")
{
return true;
}
return name.startsWith('Rooted') || name.startsWith('PersistentRooted');
}
function isRootedTypeName(name)
{
return isRootedGCTypeName(name) || isRootedGCPointerTypeName(name);
}
function isUnsafeStorage(typeName)
{
typeName = stripUCSAndNamespace(typeName);
return typeName.startsWith('UniquePtr<');
}
function isSuppressConstructor(edgeType, varName)
function isSuppressConstructor(typeInfo, edgeType, varName)
{
// Check whether this could be a constructor
if (edgeType.Kind != 'Function')
@ -357,7 +365,7 @@ function isSuppressConstructor(edgeType, varName)
// Check whether the type is a known suppression type.
var type = edgeType.TypeFunctionCSU.Type.Name;
if (GCSuppressionTypes.indexOf(type) == -1)
if (!(type in typeInfo.GCSuppressors))
return false;
// And now make sure this is the constructor, not some other method on a

Просмотреть файл

@ -128,7 +128,7 @@ function processBody(functionName, body)
}
}
GCSuppressionTypes = loadTypeInfo(typeInfo_filename)["Suppress GC"] || [];
var typeInfo = loadTypeInfo(typeInfo_filename);
loadTypes("src_comp.xdb");
@ -155,7 +155,7 @@ function process(functionName, functionBodies)
body.suppressed = [];
for (var body of functionBodies) {
for (var [pbody, id] of allRAIIGuardedCallPoints(functionBodies, body, isSuppressConstructor))
for (var [pbody, id] of allRAIIGuardedCallPoints(typeInfo, functionBodies, body, isSuppressConstructor))
pbody.suppressed[id] = true;
}

Просмотреть файл

@ -8,12 +8,16 @@ loadRelativeToScript('annotations.js');
var gcTypes_filename = scriptArgs[0] || "gcTypes.txt";
var typeInfo_filename = scriptArgs[1] || "typeInfo.txt";
var annotations = {
var typeInfo = {
'GCPointers': [],
'GCThings': [],
'NonGCTypes': {}, // unused
'NonGCPointers': {},
'RootedGCThings': {},
'RootedPointers': {},
// RAII types within which we should assume GC is suppressed, eg
// AutoSuppressGC.
'GCSuppressors': {},
};
@ -22,6 +26,7 @@ var gDescriptors = new Map; // Map from descriptor string => Set of typeName
var structureParents = {}; // Map from field => list of <parent, fieldName>
var pointerParents = {}; // Map from field => list of <parent, fieldName>
var baseClasses = {}; // Map from struct name => list of base class name strings
var subClasses = {}; // Map from struct name => list of subclass name strings
var gcTypes = {}; // map from parent struct => Set of GC typed children
var gcPointers = {}; // map from parent struct => Set of GC typed children
@ -61,17 +66,17 @@ function processCSU(csu, body)
continue;
if (tag == 'GC Pointer')
annotations.GCPointers.push(csu);
typeInfo.GCPointers.push(csu);
else if (tag == 'Invalidated by GC')
annotations.GCPointers.push(csu);
typeInfo.GCPointers.push(csu);
else if (tag == 'GC Thing')
annotations.GCThings.push(csu);
typeInfo.GCThings.push(csu);
else if (tag == 'Suppressed GC Pointer')
annotations.NonGCPointers[csu] = true;
typeInfo.NonGCPointers[csu] = true;
else if (tag == 'Rooted Pointer')
annotations.RootedPointers[csu] = true;
typeInfo.RootedPointers[csu] = true;
else if (tag == 'Suppress GC')
annotations.GCSuppressors[csu] = true;
typeInfo.GCSuppressors[csu] = true;
}
}
@ -91,6 +96,9 @@ function addBaseClass(csu, base) {
if (!(csu in baseClasses))
baseClasses[csu] = [];
baseClasses[csu].push(base);
if (!(base in subClasses))
subClasses[base] = [];
subClasses[base].push(csu);
var k = baseClasses[csu].length;
addNestedStructure(csu, base, `<base-${k}>`);
}
@ -119,22 +127,24 @@ for (var csuIndex = minStream; csuIndex <= maxStream; csuIndex++) {
xdb.free_string(data);
}
for (const typename of extraRootedGCThings())
typeInfo.RootedGCThings[typename] = true;
for (const typename of extraRootedPointers())
typeInfo.RootedPointers[typename] = true;
// Now that we have the whole hierarchy set up, add all the types and propagate
// info.
for (let csu of annotations.GCThings)
for (const csu of typeInfo.GCThings)
addGCType(csu);
for (let csu of annotations.GCPointers)
for (const csu of typeInfo.GCPointers)
addGCPointer(csu);
function stars(n) { return n ? '*' + stars(n-1) : '' };
// "typeName is a (pointer to a)^'typePtrLevel' GC type because it contains a field
// named 'child' of type 'why' (or pointer to 'why' if fieldPtrLevel == 1), which is
// itself a GCThing or GCPointer."
function markGCType(typeName, child, why, typePtrLevel, fieldPtrLevel, indent)
{
//printErr(`${indent}${typeName}${stars(typePtrLevel)} may be a gctype/ptr because of its child '${child}' of type ${why}${stars(fieldPtrLevel)}`);
// Some types, like UniquePtr, do not mark/trace/relocate their contained
// pointers and so should not hold them live across a GC. UniquePtr in
// particular should be the only thing pointing to a structure containing a
@ -166,19 +176,22 @@ function markGCType(typeName, child, why, typePtrLevel, fieldPtrLevel, indent)
if (ptrLevel > 2)
return;
if (ptrLevel == 0 && isRootedGCTypeName(typeName))
if (isRootedGCPointerTypeName(typeName) && !(typeName in typeInfo.RootedPointers))
printErr("FIXME: use in-source annotation for " + typeName);
if (ptrLevel == 0 && (typeName in typeInfo.RootedGCThings))
return;
if (ptrLevel == 1 && isRootedGCPointerTypeName(typeName))
if (ptrLevel == 1 && (isRootedGCPointerTypeName(typeName) || (typeName in typeInfo.RootedPointers)))
return;
if (ptrLevel == 0) {
if (typeName in annotations.NonGCTypes)
if (typeName in typeInfo.NonGCTypes)
return;
if (!(typeName in gcTypes))
gcTypes[typeName] = new Set();
gcTypes[typeName].add(why);
} else if (ptrLevel == 1) {
if (typeName in annotations.NonGCPointers)
if (typeName in typeInfo.NonGCPointers)
return;
if (!(typeName in gcPointers))
gcPointers[typeName] = new Set();
@ -215,28 +228,34 @@ function addGCPointer(typeName)
markGCType(typeName, '<pointer-annotation>', '(annotation)', 1, 0, "");
}
// Add an arbitrary descriptor to a type, and apply it recursively to all base
// structs and structs that contain the given typeName as a field.
function addDescriptor(typeName, descriptor)
// Call a function for a type and every type that contains the type in a field
// or as a base class (which internally is pretty much the same thing --
// sublcasses are structs beginning with the base class and adding on their
// local fields.)
function foreachContainingStruct(typeName, func, seen = new Set())
{
if (!gDescriptors.has(descriptor))
gDescriptors.set(descriptor, new Set);
let descriptorTypes = gDescriptors.get(descriptor);
if (!descriptorTypes.has(typeName)) {
descriptorTypes.add(typeName);
if (typeName in structureParents) {
for (let [holder, field] of structureParents[typeName])
addDescriptor(holder, descriptor);
function recurse(container, typeName) {
if (seen.has(typeName))
return;
seen.add(typeName);
func(container, typeName);
if (typeName in subClasses) {
for (const sub of subClasses[typeName])
recurse("subclass of " + typeName, sub);
}
if (typeName in baseClasses) {
for (let base of baseClasses[typeName])
addDescriptor(base, descriptor);
if (typeName in structureParents) {
for (const [holder, field] of structureParents[typeName])
recurse(field + " : " + typeName, holder);
}
}
recurse('<annotation>', typeName);
}
for (var type of listNonGCPointers())
annotations.NonGCPointers[type] = true;
typeInfo.NonGCPointers[type] = true;
function explain(csu, indent, seen) {
if (!seen)
@ -288,12 +307,14 @@ for (var csu in gcPointers) {
// Redirect output to the typeInfo file and close the gcTypes file.
os.file.close(os.file.redirect(typeInfo_filename));
for (let csu in annotations.GCSuppressors)
addDescriptor(csu, 'Suppress GC');
// Compute the set of types that suppress GC within their RAII scopes (eg
// AutoSuppressGC, AutoSuppressGCForAnalysis).
var seen = new Set();
for (let csu in typeInfo.GCSuppressors)
foreachContainingStruct(csu,
(holder, typeName) => { typeInfo.GCSuppressors[typeName] = holder },
seen);
for (let [descriptor, types] of gDescriptors) {
for (let csu of types)
print(descriptor + "$$" + csu);
}
print(JSON.stringify(typeInfo, null, 4));
os.file.close(os.file.redirect(origOut));

Просмотреть файл

@ -241,7 +241,10 @@ function str_edge(edge, env) {
}
function str(unknown) {
if ("Index" in unknown) {
if ("Name" in unknown) {
return str_Variable(unknown);
} else if ("Index" in unknown) {
// Note: Variable also has .Index, with a different meaning.
return str_edge(unknown);
} else if ("Kind" in unknown) {
if ("BlockId" in unknown)

Просмотреть файл

@ -267,11 +267,5 @@ function addToKeyedList(collection, key, entry)
function loadTypeInfo(filename)
{
var info = {};
for (var line of readFileLines_gen(filename)) {
line = line.replace(/\n/, "");
let [property, name] = line.split("$$");
addToKeyedList(info, property, name);
}
return info;
return JSON.parse(os.file.readFile(filename));
}

Просмотреть файл

@ -37,6 +37,7 @@ EXPORTS += [
'FrameProperties.h',
'LayoutLogging.h',
'MobileViewportManager.h',
'nsAutoLayoutPhase.h',
'nsBidi.h',
'nsBidiPresUtils.h',
'nsCaret.h',

Просмотреть файл

@ -33,14 +33,27 @@ nsAutoLayoutPhase::Enter()
case eLayoutPhase_Paint:
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0,
"recurring into paint");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_DisplayListBuilding] == 0,
"recurring into paint from display list building");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
"painting in the middle of reflow");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
"painting in the middle of frame construction");
break;
case eLayoutPhase_DisplayListBuilding:
// It's fine and expected to be in a paint here.
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_DisplayListBuilding] == 0,
"recurring into display list building");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
"display list building in the middle of reflow");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
"display list building in the middle of frame construction");
break;
case eLayoutPhase_Reflow:
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0,
"reflowing in the middle of a paint");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_DisplayListBuilding] == 0,
"reflowing in the middle of a display list building");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
"recurring into reflow");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
@ -49,6 +62,8 @@ nsAutoLayoutPhase::Enter()
case eLayoutPhase_FrameC:
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0,
"constructing frames in the middle of a paint");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_DisplayListBuilding] == 0,
"constructing frames in the middle of a display list building");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
"constructing frames in the middle of reflow");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,

Просмотреть файл

@ -2709,22 +2709,46 @@ nsLayoutUtils::PostTranslate(Matrix4x4& aTransform, const nsPoint& aOrigin, floa
aTransform.PostTranslate(gfxOrigin);
}
// We want to this return true for the scroll frame, but not the
// scrolled frame (which has the same content).
bool
nsLayoutUtils::FrameHasDisplayPort(nsIFrame* aFrame, nsIFrame* aScrolledFrame)
{
if (!aFrame->GetContent() || !HasDisplayPort(aFrame->GetContent())) {
return false;
}
nsIScrollableFrame* sf = do_QueryFrame(aFrame);
if (sf) {
if (aScrolledFrame && aScrolledFrame != sf->GetScrolledFrame()) {
return false;
}
return true;
}
return false;
}
Matrix4x4
nsLayoutUtils::GetTransformToAncestor(nsIFrame *aFrame,
const nsIFrame *aAncestor,
bool aInCSSUnits)
uint32_t aFlags,
nsIFrame** aOutAncestor)
{
nsIFrame* parent;
Matrix4x4 ctm;
if (aFrame == aAncestor) {
return ctm;
}
ctm = aFrame->GetTransformMatrix(aAncestor, &parent, aInCSSUnits);
while (parent && parent != aAncestor) {
ctm = aFrame->GetTransformMatrix(aAncestor, &parent, aFlags);
while (parent && parent != aAncestor &&
(!(aFlags & nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) ||
(!parent->IsStackingContext() && !FrameHasDisplayPort(parent)))) {
if (!parent->Extend3DContext()) {
ctm.ProjectTo2D();
}
ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent, aInCSSUnits);
ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent, aFlags);
}
if (aOutAncestor) {
*aOutAncestor = parent;
}
return ctm;
}
@ -3029,7 +3053,9 @@ TransformGfxRectToAncestor(nsIFrame *aFrame,
const Rect &aRect,
const nsIFrame *aAncestor,
bool* aPreservesAxisAlignedRectangles = nullptr,
Maybe<Matrix4x4>* aMatrixCache = nullptr)
Maybe<Matrix4x4>* aMatrixCache = nullptr,
bool aStopAtStackingContextAndDisplayPort = false,
nsIFrame** aOutAncestor = nullptr)
{
Matrix4x4 ctm;
if (aMatrixCache && *aMatrixCache) {
@ -3037,7 +3063,11 @@ TransformGfxRectToAncestor(nsIFrame *aFrame,
ctm = aMatrixCache->value();
} else {
// Else, compute it
ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
uint32_t flags = 0;
if (aStopAtStackingContextAndDisplayPort) {
flags |= nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT;
}
ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor, flags, aOutAncestor);
if (aMatrixCache) {
// and put it in the cache, if provided
*aMatrixCache = Some(ctm);
@ -3098,7 +3128,9 @@ nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
const nsRect& aRect,
const nsIFrame* aAncestor,
bool* aPreservesAxisAlignedRectangles /* = nullptr */,
Maybe<Matrix4x4>* aMatrixCache /* = nullptr */)
Maybe<Matrix4x4>* aMatrixCache /* = nullptr */,
bool aStopAtStackingContextAndDisplayPort /* = false */,
nsIFrame** aOutAncestor /* = nullptr */)
{
SVGTextFrame* text = GetContainingSVGTextFrame(aFrame);
@ -3107,7 +3139,9 @@ nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
if (text) {
result = ToRect(text->TransformFrameRectFromTextChild(aRect, aFrame));
result = TransformGfxRectToAncestor(text, result, aAncestor, nullptr, aMatrixCache);
result = TransformGfxRectToAncestor(text, result, aAncestor,
nullptr, aMatrixCache,
aStopAtStackingContextAndDisplayPort, aOutAncestor);
// TransformFrameRectFromTextChild could involve any kind of transform, we
// could drill down into it to get an answer out of it but we don't yet.
if (aPreservesAxisAlignedRectangles)
@ -3117,7 +3151,9 @@ nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel));
result = TransformGfxRectToAncestor(aFrame, result, aAncestor, aPreservesAxisAlignedRectangles, aMatrixCache);
result = TransformGfxRectToAncestor(aFrame, result, aAncestor,
aPreservesAxisAlignedRectangles, aMatrixCache,
aStopAtStackingContextAndDisplayPort, aOutAncestor);
}
float destAppUnitsPerDevPixel = aAncestor->PresContext()->AppUnitsPerDevPixel();
@ -3492,6 +3528,46 @@ nsLayoutUtils::ExpireDisplayPortOnAsyncScrollableAncestor(nsIFrame* aFrame)
}
}
void
nsLayoutUtils::AddExtraBackgroundItems(nsDisplayListBuilder& aBuilder,
nsDisplayList& aList,
nsIFrame* aFrame,
const nsRect& aCanvasArea,
const nsRegion& aVisibleRegion,
nscolor aBackstop)
{
LayoutFrameType frameType = aFrame->Type();
nsPresContext* presContext = aFrame->PresContext();
nsIPresShell* presShell = presContext->PresShell();
// For the viewport frame in print preview/page layout we want to paint
// the grey background behind the page, not the canvas color.
if (frameType == LayoutFrameType::Viewport &&
nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
nsRect bounds = nsRect(aBuilder.ToReferenceFrame(aFrame),
aFrame->GetSize());
nsDisplayListBuilder::AutoBuildingDisplayList
buildingDisplayList(&aBuilder, aFrame, bounds, false);
presShell->AddPrintPreviewBackgroundItem(aBuilder, aList, aFrame, bounds);
} else if (frameType != LayoutFrameType::Page) {
// For printing, this function is first called on an nsPageFrame, which
// creates a display list with a PageContent item. The PageContent item's
// paint function calls this function on the nsPageFrame's child which is
// an nsPageContentFrame. We only want to add the canvas background color
// item once, for the nsPageContentFrame.
// Add the canvas background color to the bottom of the list. This
// happens after we've built the list so that AddCanvasBackgroundColorItem
// can monkey with the contents if necessary.
nsRect canvasArea = aVisibleRegion.GetBounds();
canvasArea.IntersectRect(aCanvasArea, canvasArea);
nsDisplayListBuilder::AutoBuildingDisplayList
buildingDisplayList(&aBuilder, aFrame, canvasArea, false);
presShell->AddCanvasBackgroundColorItem(
aBuilder, aList, aFrame, canvasArea, aBackstop);
}
}
nsresult
nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
const nsRegion& aDirtyRegion, nscolor aBackstop,
@ -3650,33 +3726,7 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
aFrame->BuildDisplayListForStackingContext(&builder, &list);
}
LayoutFrameType frameType = aFrame->Type();
// For the viewport frame in print preview/page layout we want to paint
// the grey background behind the page, not the canvas color.
if (frameType == LayoutFrameType::Viewport &&
nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
nsRect bounds = nsRect(builder.ToReferenceFrame(aFrame),
aFrame->GetSize());
nsDisplayListBuilder::AutoBuildingDisplayList
buildingDisplayList(&builder, aFrame, bounds, false);
presShell->AddPrintPreviewBackgroundItem(builder, list, aFrame, bounds);
} else if (frameType != LayoutFrameType::Page) {
// For printing, this function is first called on an nsPageFrame, which
// creates a display list with a PageContent item. The PageContent item's
// paint function calls this function on the nsPageFrame's child which is
// an nsPageContentFrame. We only want to add the canvas background color
// item once, for the nsPageContentFrame.
// Add the canvas background color to the bottom of the list. This
// happens after we've built the list so that AddCanvasBackgroundColorItem
// can monkey with the contents if necessary.
canvasArea.IntersectRect(canvasArea, visibleRegion.GetBounds());
nsDisplayListBuilder::AutoBuildingDisplayList
buildingDisplayList(&builder, aFrame, canvasArea, false);
presShell->AddCanvasBackgroundColorItem(
builder, list, aFrame, canvasArea, aBackstop);
}
AddExtraBackgroundItems(builder, list, aFrame, canvasArea, visibleRegion, aBackstop);
builder.LeavePresShell(aFrame, &list);

Просмотреть файл

@ -212,6 +212,14 @@ public:
*/
static bool HasDisplayPort(nsIContent* aContent);
/**
* Check whether the given frame has a displayport. It returns false
* for scrolled frames and true for the corresponding scroll frame.
* Optionally pass the child, and it only returns true if the child is the
* scrolled frame for the displayport.
*/
static bool FrameHasDisplayPort(nsIFrame* aFrame, nsIFrame* aScrolledFrame = nullptr);
/**
* Check if the given element has a margins based displayport but is missing a
* displayport base rect that it needs to properly compute a displayport rect.
@ -865,7 +873,9 @@ public:
const nsRect& aRect,
const nsIFrame* aAncestor,
bool* aPreservesAxisAlignedRectangles = nullptr,
mozilla::Maybe<Matrix4x4>* aMatrixCache = nullptr);
mozilla::Maybe<Matrix4x4>* aMatrixCache = nullptr,
bool aStopAtStackingContextAndDisplayPort = false,
nsIFrame** aOutAncestor = nullptr);
/**
@ -875,7 +885,8 @@ public:
*/
static Matrix4x4 GetTransformToAncestor(nsIFrame *aFrame,
const nsIFrame *aAncestor,
bool aInCSSUnits = false);
uint32_t aFlags = 0,
nsIFrame** aOutAncestor = nullptr);
/**
* Gets the scale factors of the transform for aFrame relative to the root
@ -2235,6 +2246,13 @@ public:
static nsIContent*
GetEditableRootContentByContentEditable(nsIDocument* aDocument);
static void AddExtraBackgroundItems(nsDisplayListBuilder& aBuilder,
nsDisplayList& aList,
nsIFrame* aFrame,
const nsRect& aCanvasArea,
const nsRegion& aVisibleRegion,
nscolor aBackstop);
/**
* Returns true if the passed in prescontext needs the dark grey background
* that goes behind the page of a print preview presentation.

Просмотреть файл

@ -108,6 +108,7 @@ struct nsAutoLayoutPhase;
enum nsLayoutPhase {
eLayoutPhase_Paint,
eLayoutPhase_DisplayListBuilding, // sometimes a subset of the paint phase
eLayoutPhase_Reflow,
eLayoutPhase_FrameC,
eLayoutPhase_COUNT

Просмотреть файл

@ -366,8 +366,6 @@ TextOverflow::TextOverflow(nsDisplayListBuilder* aBuilder,
LogicalPoint(mBlockWM,
mScrollableFrame->GetScrollPosition(),
nullContainerSize));
nsIFrame* scrollFrame = do_QueryFrame(mScrollableFrame);
scrollFrame->AddStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL);
}
uint8_t direction = aBlockFrame->StyleVisibility()->mDirection;
const nsStyleTextReset* style = aBlockFrame->StyleTextReset();

Просмотреть файл

@ -1326,11 +1326,18 @@ nsIFrame::GetMarginRectRelativeToSelf() const
bool
nsIFrame::IsTransformed(const nsStyleDisplay* aStyleDisplay,
EffectSet* aEffectSet) const
{
return IsCSSTransformed(aStyleDisplay, aEffectSet) ||
IsSVGTransformed();
}
bool
nsIFrame::IsCSSTransformed(const nsStyleDisplay* aStyleDisplay,
EffectSet* aEffectSet) const
{
MOZ_ASSERT(aStyleDisplay == StyleDisplay());
return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
(aStyleDisplay->HasTransform(this) ||
IsSVGTransformed() ||
HasAnimationOfTransform(aEffectSet)));
}
@ -1402,7 +1409,7 @@ nsIFrame::Combines3DTransformWithAncestors(const nsStyleDisplay* aStyleDisplay,
if (!parent || !parent->Extend3DContext()) {
return false;
}
return IsTransformed(aStyleDisplay,aEffectSet) ||
return IsCSSTransformed(aStyleDisplay, aEffectSet) ||
BackfaceIsHidden(aStyleDisplay);
}
@ -3108,21 +3115,10 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
const nsStyleDisplay* disp = child->StyleDisplay();
const nsStyleEffects* effects = child->StyleEffects();
const nsStylePosition* pos = child->StylePosition();
bool isVisuallyAtomic = child->HasOpacity(effectSet)
|| child->IsTransformed(disp, effectSet)
// strictly speaking, 'perspective' doesn't require visual atomicity,
// but the spec says it acts like the rest of these
|| disp->mChildPerspective.GetUnit() == eStyleUnit_Coord
|| effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL
|| nsSVGIntegrationUtils::UsingEffectsForFrame(child);
bool isVisuallyAtomic = child->IsVisuallyAtomic(effectSet, disp, effects);
bool isPositioned = disp->IsAbsPosContainingBlock(child);
bool isStackingContext =
(isPositioned && (disp->IsPositionForcingStackingContext() ||
pos->mZIndex.GetUnit() == eStyleUnit_Integer)) ||
(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) ||
disp->mIsolation != NS_STYLE_ISOLATION_AUTO ||
isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
bool isStackingContext = child->IsStackingContext(disp, pos, isPositioned, isVisuallyAtomic) ||
(aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) ||
((effects->mClipFlags & NS_STYLE_CLIP_RECT) &&
@ -6298,7 +6294,7 @@ nsIFrame::GetFlattenedTreeParentPrimaryFrame() const
Matrix4x4
nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
nsIFrame** aOutAncestor,
bool aInCSSUnits)
uint32_t aFlags)
{
NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!");
@ -6312,8 +6308,8 @@ nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
*/
NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this),
"Cannot transform the viewport frame!");
int32_t scaleFactor = (aInCSSUnits ? PresContext()->AppUnitsPerCSSPixel()
: PresContext()->AppUnitsPerDevPixel());
int32_t scaleFactor = ((aFlags & IN_CSS_UNITS) ? PresContext()->AppUnitsPerCSSPixel()
: PresContext()->AppUnitsPerDevPixel());
Matrix4x4 result = nsDisplayTransform::GetResultingTransformMatrix(this,
nsPoint(0,0), scaleFactor,
@ -6381,14 +6377,18 @@ nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
return Matrix4x4();
/* Keep iterating while the frame can't possibly be transformed. */
nsIFrame* current = this;
while (!(*aOutAncestor)->IsTransformed() &&
!nsLayoutUtils::IsPopup(*aOutAncestor) &&
*aOutAncestor != aStopAtAncestor) {
*aOutAncestor != aStopAtAncestor &&
(!(aFlags & STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) ||
(!(*aOutAncestor)->IsStackingContext() && !nsLayoutUtils::FrameHasDisplayPort(*aOutAncestor, current)))) {
/* If no parent, stop iterating. Otherwise, update the ancestor. */
nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor);
if (!parent)
break;
current = *aOutAncestor;
*aOutAncestor = parent;
}
@ -6398,8 +6398,8 @@ nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
* entire transform, so we're done.
*/
nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
int32_t scaleFactor = (aInCSSUnits ? PresContext()->AppUnitsPerCSSPixel()
: PresContext()->AppUnitsPerDevPixel());
int32_t scaleFactor = ((aFlags & IN_CSS_UNITS) ? PresContext()->AppUnitsPerCSSPixel()
: PresContext()->AppUnitsPerDevPixel());
return Matrix4x4::Translation(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
NSAppUnitsToFloatPixels(delta.y, scaleFactor),
0.0f);
@ -10506,6 +10506,41 @@ nsIFrame::IsPseudoStackingContextFromStyle() {
(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT);
}
bool
nsIFrame::IsVisuallyAtomic(EffectSet* aEffectSet,
const nsStyleDisplay* aStyleDisplay,
const nsStyleEffects* aStyleEffects) {
return HasOpacity(aEffectSet) ||
IsTransformed(aStyleDisplay) ||
// strictly speaking, 'perspective' doesn't require visual atomicity,
// but the spec says it acts like the rest of these
aStyleDisplay->mChildPerspective.GetUnit() == eStyleUnit_Coord ||
aStyleEffects->mMixBlendMode != NS_STYLE_BLEND_NORMAL ||
nsSVGIntegrationUtils::UsingEffectsForFrame(this);
}
bool
nsIFrame::IsStackingContext(const nsStyleDisplay* aStyleDisplay,
const nsStylePosition* aStylePosition,
bool aIsPositioned,
bool aIsVisuallyAtomic) {
return (aIsPositioned && (aStyleDisplay->IsPositionForcingStackingContext() ||
aStylePosition->mZIndex.GetUnit() == eStyleUnit_Integer)) ||
(aStyleDisplay->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) ||
aStyleDisplay->mIsolation != NS_STYLE_ISOLATION_AUTO ||
aIsVisuallyAtomic;
}
bool
nsIFrame::IsStackingContext()
{
const nsStyleDisplay* disp = StyleDisplay();
bool isPositioned = disp->IsAbsPosContainingBlock(this);
bool isVisuallyAtomic = IsVisuallyAtomic(EffectSet::GetEffectSet(this),
disp, StyleEffects());
return IsStackingContext(disp, StylePosition(), isPositioned, isVisuallyAtomic);
}
Element*
nsIFrame::GetPseudoElement(CSSPseudoElementType aType)
{

Просмотреть файл

@ -556,17 +556,6 @@ FRAME_STATE_BIT(Block, 62, BULLET_FRAME_HAS_FONT_INFLATION)
FRAME_STATE_BIT(Block, 63, BULLET_FRAME_IMAGE_LOADING)
// == Frame state bits that apply to scroll frames ============================
FRAME_STATE_GROUP(Scroll, nsIScrollableFrame)
// When set, the next scroll operation on the scrollframe will invalidate its
// entire contents. Useful for text-overflow.
// This bit is cleared after each time the scrollframe is scrolled. Whoever
// needs to set it should set it again on each paint.
FRAME_STATE_BIT(Scroll, 20, NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL)
// == Frame state bits that apply to image frames =============================
FRAME_STATE_GROUP(Image, nsImageFrame)

Просмотреть файл

@ -2421,12 +2421,6 @@ static void AdjustViews(nsIFrame* aFrame)
}
}
static bool
NeedToInvalidateOnScroll(nsIFrame* aFrame)
{
return (aFrame->GetStateBits() & NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL) != 0;
}
bool ScrollFrameHelper::IsIgnoringViewportClipping() const
{
if (!mIsRoot)
@ -2603,14 +2597,7 @@ void ScrollFrameHelper::ScrollVisual()
AdjustViews(mScrolledFrame);
// We need to call this after fixing up the view positions
// to be consistent with the frame hierarchy.
bool needToInvalidateOnScroll = NeedToInvalidateOnScroll(mOuter);
mOuter->RemoveStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL);
if (needToInvalidateOnScroll) {
MarkNotRecentlyScrolled();
} else {
MarkRecentlyScrolled();
}
MarkRecentlyScrolled();
}
/**
@ -3279,9 +3266,6 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
if (aBuilder->IsPaintingToWindow()) {
mScrollPosAtLastPaint = GetScrollPosition();
if (IsMaybeScrollingActive() && NeedToInvalidateOnScroll(mOuter)) {
MarkNotRecentlyScrolled();
}
if (IsMaybeScrollingActive()) {
if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) {
mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint;

Просмотреть файл

@ -1724,6 +1724,12 @@ public:
return IsTransformed(StyleDisplay(), aEffectSet);
}
/**
* Same as IsTransformed, except that it doesn't take SVG transforms
* into account.
*/
bool IsCSSTransformed(const nsStyleDisplay* aStyleDisplay, mozilla::EffectSet* aEffectSet = nullptr) const;
/**
* True if this frame has any animation of transform in effect.
*
@ -2789,9 +2795,13 @@ public:
* @return A Matrix4x4 that converts points in this frame's coordinate space
* into points in aOutAncestor's coordinate space.
*/
enum {
IN_CSS_UNITS = 1 << 0,
STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT = 1 << 1
};
Matrix4x4 GetTransformMatrix(const nsIFrame* aStopAtAncestor,
nsIFrame **aOutAncestor,
bool aInCSSUnits = false);
uint32_t aFlags = 0);
/**
* Bit-flags to pass to IsFrameOfType()
@ -3450,6 +3460,27 @@ public:
*/
bool IsPseudoStackingContextFromStyle();
/**
* Determines if this frame has a container effect that requires
* it to paint as a visually atomic unit.
*/
bool IsVisuallyAtomic(mozilla::EffectSet* aEffectSet,
const nsStyleDisplay* aStyleDisplay,
const nsStyleEffects* aStyleEffects);
/**
* Determines if this frame is a stacking context.
*
* @param aIsPositioned The precomputed result of IsAbsPosContainingBlock
* on the StyleDisplay().
* @param aIsVisuallyAtomic The precomputed result of IsVisuallyAtomic.
*/
bool IsStackingContext(const nsStyleDisplay* aStyleDisplay,
const nsStylePosition* aStylePosition,
bool aIsPositioned,
bool aIsVisuallyAtomic);
bool IsStackingContext();
virtual bool HonorPrintBackgroundSettings() { return true; }
/**

Просмотреть файл

@ -1218,6 +1218,10 @@ nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame,
state->mCaretFrame = nullptr;
state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
#ifdef DEBUG
state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(), eLayoutPhase_DisplayListBuilding);
#endif
state->mPresShell->UpdateCanvasBackground();
if (mIsPaintingToWindow) {

Просмотреть файл

@ -37,6 +37,7 @@
#include "mozilla/gfx/UserData.h"
#include "mozilla/layers/LayerAttributes.h"
#include "nsCSSRenderingBorders.h"
#include "nsAutoLayoutPhase.h"
#include "nsDisplayItemTypes.h"
#include <stdint.h>
@ -1474,6 +1475,9 @@ private:
struct PresShellState {
nsIPresShell* mPresShell;
#ifdef DEBUG
mozilla::Maybe<nsAutoLayoutPhase> mAutoLayoutPhase;
#endif
nsIFrame* mCaretFrame;
nsRect mCaretRect;
mozilla::Maybe<OutOfFlowDisplayData> mFixedBackgroundDisplayData;

Просмотреть файл

@ -0,0 +1,11 @@
<html>
<head>
</head>
<body style="width:100px">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="js" viewBox="0 0 200 200" style="transform: rotateX(180deg)">
<g>
<rect width="100" height="100" style="fill:rgb(0,0,255)"></rect>
</g>
</svg>
</body>
</html>

Просмотреть файл

@ -0,0 +1,17 @@
<html>
<head>
</head>
<body>
<div style="transform: rotatex(45deg); transform-style: preserve-3d; width: 100px;">
<div style="transform: rotatex(45deg); transform-style: preserve-3d; width: 100px;">
<div style="transform: rotatex(45deg); transform-style: preserve-3d; width: 100px;">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="js" viewBox="0 0 200 200" style="transform: rotateX(45deg)">
<g>
<rect width="100" height="100" style="fill:rgb(0,0,255)"></rect>
</g>
</svg>
</div>
</div>
</div>
</body>
</html>

Просмотреть файл

@ -677,6 +677,7 @@ public class BrowserApp extends GeckoApp
});
mProgressView = (AnimatedProgressBar) findViewById(R.id.page_progress);
mDynamicToolbar.setLayerView(mLayerView);
mProgressView.setDynamicToolbar(mDynamicToolbar);
mBrowserToolbar.setProgressBar(mProgressView);

Просмотреть файл

@ -55,7 +55,6 @@ EXPORTS += [
]
UNIFIED_SOURCES += [
'nsHtml5Atom.cpp',
'nsHtml5AtomTable.cpp',
'nsHtml5AttributeName.cpp',
'nsHtml5DependentUTF16Buffer.cpp',

Просмотреть файл

@ -1,63 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsHtml5Atom.h"
#include "nsAutoPtr.h"
#include "mozilla/Unused.h"
nsHtml5Atom::nsHtml5Atom(const nsAString& aString)
{
mLength = aString.Length();
SetKind(AtomKind::HTML5Atom);
RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
if (buf) {
mString = static_cast<char16_t*>(buf->Data());
} else {
const size_t size = (mLength + 1) * sizeof(char16_t);
buf = nsStringBuffer::Alloc(size);
if (MOZ_UNLIKELY(!buf)) {
// We OOM because atom allocations should be small and it's hard to
// handle them more gracefully in a constructor.
NS_ABORT_OOM(size);
}
mString = static_cast<char16_t*>(buf->Data());
CopyUnicodeTo(aString, 0, mString, mLength);
mString[mLength] = char16_t(0);
}
NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
NS_ASSERTION(buf && buf->StorageSize() >= (mLength+1) * sizeof(char16_t),
"enough storage");
NS_ASSERTION(Equals(aString), "correct data");
// Take ownership of buffer
mozilla::Unused << buf.forget();
}
nsHtml5Atom::~nsHtml5Atom()
{
nsStringBuffer::FromData(mString)->Release();
}
NS_IMETHODIMP
nsHtml5Atom::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
NS_NOTREACHED("Attempt to call QueryInterface an nsHtml5Atom.");
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
nsHtml5Atom::ToUTF8String(nsACString& aReturn)
{
NS_NOTREACHED("Should not attempt to convert to an UTF-8 string.");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP_(size_t)
nsHtml5Atom::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
{
NS_NOTREACHED("Should not call SizeOfIncludingThis.");
return 0;
}

Просмотреть файл

@ -1,28 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsHtml5Atom_h
#define nsHtml5Atom_h
#include "nsIAtom.h"
#include "mozilla/Attributes.h"
/**
* A dynamic atom implementation meant for use within the nsHtml5Tokenizer and
* nsHtml5TreeBuilder owned by one nsHtml5Parser or nsHtml5StreamParser
* instance.
*
* Usage is documented in nsHtml5AtomTable and nsIAtom.
*/
class nsHtml5Atom final : public nsIAtom
{
public:
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) final;
NS_DECL_NSIATOM
explicit nsHtml5Atom(const nsAString& aString);
~nsHtml5Atom();
};
#endif // nsHtml5Atom_h

Просмотреть файл

@ -3,12 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsHtml5AtomTable.h"
#include "nsHtml5Atom.h"
#include "nsThreadUtils.h"
nsHtml5AtomEntry::nsHtml5AtomEntry(KeyTypePointer aStr)
: nsStringHashKey(aStr)
, mAtom(new nsHtml5Atom(*aStr))
, mAtom(new nsAtom(nsAtom::AtomKind::HTML5Atom, *aStr, 0))
{
}
@ -21,6 +20,7 @@ nsHtml5AtomEntry::nsHtml5AtomEntry(const nsHtml5AtomEntry& aOther)
nsHtml5AtomEntry::~nsHtml5AtomEntry()
{
delete mAtom;
}
nsHtml5AtomTable::nsHtml5AtomTable()

Просмотреть файл

@ -7,26 +7,20 @@
#include "nsHashKeys.h"
#include "nsTHashtable.h"
#include "nsAutoPtr.h"
#include "nsIAtom.h"
#include "nsISerialEventTarget.h"
#define RECENTLY_USED_PARSER_ATOMS_SIZE 31
class nsHtml5Atom;
class nsHtml5AtomEntry : public nsStringHashKey
{
public:
explicit nsHtml5AtomEntry(KeyTypePointer aStr);
nsHtml5AtomEntry(const nsHtml5AtomEntry& aOther);
~nsHtml5AtomEntry();
inline nsHtml5Atom* GetAtom()
{
return mAtom;
}
inline nsAtom* GetAtom() { return mAtom; }
private:
nsAutoPtr<nsHtml5Atom> mAtom;
nsAtom* mAtom;
};
/**

Просмотреть файл

@ -4,6 +4,7 @@
#include "nsHtml5SpeculativeLoad.h"
#include "nsHtml5TreeOpExecutor.h"
#include "mozilla/Encoding.h"
nsHtml5SpeculativeLoad::nsHtml5SpeculativeLoad()
:

Просмотреть файл

@ -6,20 +6,8 @@
#ifndef nsParserCIID_h__
#define nsParserCIID_h__
#include "nsISupports.h"
#include "nsIFactory.h"
#include "nsIComponentManager.h"
// {2ce606b0-bee6-11d1-aad9-00805f8a3e14}
#define NS_PARSER_CID \
{ 0x2ce606b0, 0xbee6, 0x11d1, { 0xaa, 0xd9, 0x0, 0x80, 0x5f, 0x8a, 0x3e, 0x14 } }
// {a6cf9107-15b3-11d2-932e-00805f8add32}
#define NS_CNAVDTD_CID \
{ 0xa6cf9107, 0x15b3, 0x11d2, { 0x93, 0x2e, 0x0, 0x80, 0x5f, 0x8a, 0xdd, 0x32 } }
// {FFF4FBE9-528A-4b37-819D-FC18F3A401A7}
#define NS_EXPAT_DRIVER_CID \
{ 0xfff4fbe9, 0x528a, 0x4b37, { 0x81, 0x9d, 0xfc, 0x18, 0xf3, 0xa4, 0x1, 0xa7 } }
#endif

Просмотреть файл

@ -3,58 +3,27 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIAtom.h"
#include "nsString.h"
#include "nspr.h"
#include "nsCOMPtr.h"
#include "mozilla/ModuleUtils.h"
#include "nsParserCIID.h"
#include "nsParser.h"
#include "CNavDTD.h"
#include "nsHTMLTokenizer.h"
//#include "nsTextTokenizer.h"
#include "nsElementTable.h"
#include "nsSAXAttributes.h"
#include "nsSAXLocator.h"
#include "nsParserCIID.h"
#include "nsHTMLTags.h"
#include "nsSAXXMLReader.h"
#if defined(DEBUG)
#include "nsExpatDriver.h"
#endif
//----------------------------------------------------------------------
#if defined(DEBUG)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsExpatDriver)
#endif
NS_GENERIC_FACTORY_CONSTRUCTOR(nsParser)
NS_GENERIC_FACTORY_CONSTRUCTOR(CNavDTD)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSAXAttributes)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSAXXMLReader)
#if defined(DEBUG)
NS_DEFINE_NAMED_CID(NS_EXPAT_DRIVER_CID);
#endif
NS_DEFINE_NAMED_CID(NS_PARSER_CID);
NS_DEFINE_NAMED_CID(NS_CNAVDTD_CID);
NS_DEFINE_NAMED_CID(NS_SAXATTRIBUTES_CID);
NS_DEFINE_NAMED_CID(NS_SAXXMLREADER_CID);
static const mozilla::Module::CIDEntry kParserCIDs[] = {
#if defined(DEBUG)
{ &kNS_EXPAT_DRIVER_CID, false, nullptr, nsExpatDriverConstructor },
#endif
{ &kNS_PARSER_CID, false, nullptr, nsParserConstructor },
{ &kNS_CNAVDTD_CID, false, nullptr, CNavDTDConstructor },
{ &kNS_SAXATTRIBUTES_CID, false, nullptr, nsSAXAttributesConstructor },
{ &kNS_SAXXMLREADER_CID, false, nullptr, nsSAXXMLReaderConstructor },
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kParserContracts[] = {
{ NS_SAXATTRIBUTES_CONTRACTID, &kNS_SAXATTRIBUTES_CID },
{ NS_SAXXMLREADER_CONTRACTID, &kNS_SAXXMLREADER_CID },
{ nullptr }
};
@ -67,9 +36,6 @@ Initialize()
#ifdef DEBUG
CheckElementTable();
#endif
#ifdef DEBUG
nsHTMLTags::TestTagTable();
#endif

Просмотреть файл

@ -13,12 +13,6 @@
#include "nsString.h"
#include "mozilla/Attributes.h"
#define NS_SAXATTRIBUTES_CONTRACTID "@mozilla.org/saxparser/attributes;1"
#define NS_SAXATTRIBUTES_CID \
{/* {7bb40992-77eb-43db-9a4e-39d3bcc483ae}*/ \
0x7bb40992, 0x77eb, 0x43db, \
{ 0x9a, 0x4e, 0x39, 0xd3, 0xbc, 0xc3, 0x83, 0xae} }
struct SAXAttr
{
nsString uri;

Просмотреть файл

@ -10,12 +10,6 @@
#include "nsString.h"
#include "mozilla/Attributes.h"
#define NS_SAXLOCATOR_CONTRACTID "@mozilla.org/saxparser/locator;1"
#define NS_SAXLOCATOR_CID \
{/* {c1cd4045-846b-43bb-a95e-745a3d7b40e0}*/ \
0xc1cd4045, 0x846b, 0x43bb, \
{ 0xa9, 0x5e, 0x74, 0x5a, 0x3d, 0x7b, 0x40, 0xe0} }
class nsSAXLocator final : public nsISAXLocator
{
public:

Просмотреть файл

@ -106,7 +106,6 @@ config = {
},
},
},
},
"partials": {
"releases-dir": {
"product-name": "Firefox-%(version)s-Partial-%(prev_version)s",

Просмотреть файл

@ -697,10 +697,6 @@ nsBaseWidget::SetSizeMode(nsSizeMode aMode)
// Get this component cursor
//
//-------------------------------------------------------------------------
nsCursor nsBaseWidget::GetCursor()
{
return mCursor;
}
void
nsBaseWidget::SetCursor(nsCursor aCursor)

Просмотреть файл

@ -174,7 +174,6 @@ public:
return mIsFullyOccluded;
}
virtual nsCursor GetCursor() override;
virtual void SetCursor(nsCursor aCursor) override;
virtual nsresult SetCursor(imgIContainer* aCursor,
uint32_t aHotspotX, uint32_t aHotspotY) override;

Просмотреть файл

@ -960,14 +960,6 @@ class nsIWidget : public nsISupports
virtual void SetBackgroundColor(const nscolor &aColor) { }
/**
* Get the cursor for this widget.
*
* @return this widget's cursor.
*/
virtual nsCursor GetCursor(void) = 0;
/**
* Set the cursor for this widget
*

Просмотреть файл

@ -28,16 +28,16 @@
// There are two kinds of atoms handled by this module.
//
// - Dynamic: the Atom itself is heap allocated, as is the nsStringBuffer it
// points to. |gAtomTable| holds weak references to dynamic Atoms. When the
// refcount of a dynamic Atom drops to zero, we increment a static counter.
// When that counter reaches a certain threshold, we iterate over the Atom
// table, removing and deleting dynamic Atoms with refcount zero. This allows
// - Dynamic: the atom itself is heap allocated, as is the nsStringBuffer it
// points to. |gAtomTable| holds weak references to dynamic atoms. When the
// refcount of a dynamic atom drops to zero, we increment a static counter.
// When that counter reaches a certain threshold, we iterate over the atom
// table, removing and deleting dynamic atoms with refcount zero. This allows
// us to avoid acquiring the atom table lock during normal refcounting.
//
// - Static: the Atom itself is heap allocated, but it points to a static
// nsStringBuffer. |gAtomTable| effectively owns static Atoms, because such
// Atoms ignore all AddRef/Release calls, which ensures they stay alive until
// - Static: the atom itself is heap allocated, but it points to a static
// nsStringBuffer. |gAtomTable| effectively owns static atoms, because such
// atoms ignore all AddRef/Release calls, which ensures they stay alive until
// |gAtomTable| itself is destroyed whereupon they are explicitly deleted.
//
// Note that gAtomTable is used on multiple threads, and callers must acquire
@ -67,6 +67,32 @@ class CheckStaticAtomSizes
//----------------------------------------------------------------------
enum class GCKind {
RegularOperation,
Shutdown,
};
// This class encapsulates the functions that need access to nsAtom's private
// members.
class nsAtomFriend
{
public:
static void RegisterStaticAtoms(const nsStaticAtom* aAtoms,
uint32_t aAtomCount);
static void AtomTableClearEntry(PLDHashTable* aTable,
PLDHashEntryHdr* aEntry);
static void GCAtomTableLocked(const MutexAutoLock& aProofOfLock,
GCKind aKind);
static already_AddRefed<nsIAtom> Atomize(const nsACString& aUTF8String);
static already_AddRefed<nsIAtom> Atomize(const nsAString& aUTF16String);
static already_AddRefed<nsIAtom> AtomizeMainThread(const nsAString& aUTF16Str);
};
//----------------------------------------------------------------------
// gUnusedAtomCount is incremented when an atom loses its last reference
// (and thus turned into unused state), and decremented when an unused
// atom gets a reference again. The atom table relies on this value to
@ -109,131 +135,94 @@ private:
UniquePtr<nsTArray<FakeBufferRefcountHelper>> gFakeBuffers;
#endif
class Atom final : public nsIAtom
// This constructor is for dynamic atoms and HTML5 atoms.
nsAtom::nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash)
: mRefCnt(1)
{
public:
static already_AddRefed<Atom> CreateDynamic(const nsAString& aString,
uint32_t aHash)
{
// The refcount is appropriately initialized in the constructor.
return dont_AddRef(new Atom(aString, aHash));
}
static Atom* CreateStatic(nsStringBuffer* aStringBuffer, uint32_t aLength,
uint32_t aHash)
{
return new Atom(aStringBuffer, aLength, aHash);
}
static void GCAtomTable();
enum class GCKind {
RegularOperation,
Shutdown,
};
static void GCAtomTableLocked(const MutexAutoLock& aProofOfLock,
GCKind aKind);
private:
// This constructor is for dynamic Atoms.
Atom(const nsAString& aString, uint32_t aHash)
: mRefCnt(1)
{
mLength = aString.Length();
SetKind(AtomKind::DynamicAtom);
RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
if (buf) {
mString = static_cast<char16_t*>(buf->Data());
} else {
const size_t size = (mLength + 1) * sizeof(char16_t);
buf = nsStringBuffer::Alloc(size);
if (MOZ_UNLIKELY(!buf)) {
// We OOM because atom allocations should be small and it's hard to
// handle them more gracefully in a constructor.
NS_ABORT_OOM(size);
}
mString = static_cast<char16_t*>(buf->Data());
CopyUnicodeTo(aString, 0, mString, mLength);
mString[mLength] = char16_t(0);
mLength = aString.Length();
SetKind(aKind);
MOZ_ASSERT(IsDynamicAtom() || IsHTML5Atom());
RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
if (buf) {
mString = static_cast<char16_t*>(buf->Data());
} else {
const size_t size = (mLength + 1) * sizeof(char16_t);
buf = nsStringBuffer::Alloc(size);
if (MOZ_UNLIKELY(!buf)) {
// We OOM because atom allocations should be small and it's hard to
// handle them more gracefully in a constructor.
NS_ABORT_OOM(size);
}
mHash = aHash;
MOZ_ASSERT(mHash == HashString(mString, mLength));
NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
NS_ASSERTION(buf && buf->StorageSize() >= (mLength + 1) * sizeof(char16_t),
"enough storage");
NS_ASSERTION(Equals(aString), "correct data");
// Take ownership of buffer
mozilla::Unused << buf.forget();
mString = static_cast<char16_t*>(buf->Data());
CopyUnicodeTo(aString, 0, mString, mLength);
mString[mLength] = char16_t(0);
}
// This constructor is for static Atoms.
Atom(nsStringBuffer* aStringBuffer, uint32_t aLength, uint32_t aHash)
{
mLength = aLength;
SetKind(AtomKind::StaticAtom);
mString = static_cast<char16_t*>(aStringBuffer->Data());
mHash = aHash;
MOZ_ASSERT_IF(IsDynamicAtom(), mHash == HashString(mString, mLength));
NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
NS_ASSERTION(buf && buf->StorageSize() >= (mLength + 1) * sizeof(char16_t),
"enough storage");
NS_ASSERTION(Equals(aString), "correct data");
// Take ownership of buffer
mozilla::Unused << buf.forget();
}
// This constructor is for static atoms.
nsAtom::nsAtom(nsStringBuffer* aStringBuffer, uint32_t aLength, uint32_t aHash)
{
mLength = aLength;
SetKind(AtomKind::StaticAtom);
mString = static_cast<char16_t*>(aStringBuffer->Data());
#if defined(NS_BUILD_REFCNT_LOGGING)
MOZ_ASSERT(NS_IsMainThread());
if (!gFakeBuffers) {
gFakeBuffers = MakeUnique<nsTArray<FakeBufferRefcountHelper>>();
}
gFakeBuffers->AppendElement(aStringBuffer);
MOZ_ASSERT(NS_IsMainThread());
if (!gFakeBuffers) {
gFakeBuffers = MakeUnique<nsTArray<FakeBufferRefcountHelper>>();
}
gFakeBuffers->AppendElement(aStringBuffer);
#endif
// Technically we could currently avoid doing this addref by instead making
// the static atom buffers have an initial refcount of 2.
aStringBuffer->AddRef();
// Technically we could currently avoid doing this addref by instead making
// the static atom buffers have an initial refcount of 2.
aStringBuffer->AddRef();
mHash = aHash;
MOZ_ASSERT(mHash == HashString(mString, mLength));
mHash = aHash;
MOZ_ASSERT(mHash == HashString(mString, mLength));
MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
MOZ_ASSERT(aStringBuffer &&
aStringBuffer->StorageSize() == (mLength + 1) * sizeof(char16_t),
"correct storage");
MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
MOZ_ASSERT(aStringBuffer &&
aStringBuffer->StorageSize() == (mLength + 1) * sizeof(char16_t),
"correct storage");
}
// We don't need a virtual destructor because we always delete via an nsAtom*
// pointer (in AtomTableClearEntry() for static atoms, and in
// GCAtomTableLocked() for dynamic atoms), not an nsIAtom* pointer.
nsAtom::~nsAtom()
{
if (!IsStaticAtom()) {
MOZ_ASSERT(IsDynamicAtom() || IsHTML5Atom());
nsStringBuffer::FromData(mString)->Release();
}
}
public:
// We don't need a virtual destructor because we always delete via an Atom*
// pointer (in AtomTableClearEntry() for static Atoms, and in
// GCAtomTableLocked() for dynamic Atoms), not an nsIAtom* pointer.
~Atom() {
if (IsDynamicAtom()) {
nsStringBuffer::FromData(mString)->Release();
} else {
MOZ_ASSERT(IsStaticAtom());
}
}
NS_DECL_NSIATOM
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) final;
typedef mozilla::TrueType HasThreadSafeRefCnt;
MozExternalRefCountType DynamicAddRef();
MozExternalRefCountType DynamicRelease();
protected:
ThreadSafeAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
};
NS_IMPL_QUERY_INTERFACE(Atom, nsIAtom);
NS_IMPL_QUERY_INTERFACE(nsAtom, nsIAtom);
NS_IMETHODIMP
Atom::ToUTF8String(nsACString& aBuf)
nsAtom::ToUTF8String(nsACString& aBuf)
{
MOZ_ASSERT(!IsHTML5Atom(), "Called ToUTF8String() on an HTML5 atom");
CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf);
return NS_OK;
}
NS_IMETHODIMP_(size_t)
Atom::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
nsAtom::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
{
MOZ_ASSERT(!IsHTML5Atom(), "Called SizeOfIncludingThis() on an HTML5 atom");
size_t n = aMallocSizeOf(this);
// String buffers pointed to by static atoms are in static memory, and so
// are not measured here.
@ -251,23 +240,23 @@ Atom::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
NS_IMETHODIMP_(MozExternalRefCountType)
nsIAtom::AddRef()
{
MOZ_ASSERT(!IsHTML5Atom(), "Attempt to AddRef an nsHtml5Atom");
MOZ_ASSERT(!IsHTML5Atom(), "Attempt to AddRef an HTML5 atom");
if (!IsDynamicAtom()) {
MOZ_ASSERT(IsStaticAtom());
return 2;
}
return static_cast<Atom*>(this)->DynamicAddRef();
return static_cast<nsAtom*>(this)->DynamicAddRef();
}
NS_IMETHODIMP_(MozExternalRefCountType)
nsIAtom::Release()
{
MOZ_ASSERT(!IsHTML5Atom(), "Attempt to Release an nsHtml5Atom");
MOZ_ASSERT(!IsHTML5Atom(), "Attempt to Release an HTML5 atom");
if (!IsDynamicAtom()) {
MOZ_ASSERT(IsStaticAtom());
return 1;
}
return static_cast<Atom*>(this)->DynamicRelease();
return static_cast<nsAtom*>(this)->DynamicRelease();
}
//----------------------------------------------------------------------
@ -334,10 +323,10 @@ struct AtomTableKey
struct AtomTableEntry : public PLDHashEntryHdr
{
// These references are either to dynamic Atoms, in which case they are
// non-owning, or they are to static Atoms, which aren't really refcounted.
// These references are either to dynamic atoms, in which case they are
// non-owning, or they are to static atoms, which aren't really refcounted.
// See the comment at the top of this file for more details.
Atom* MOZ_NON_OWNING_REF mAtom;
nsAtom* MOZ_NON_OWNING_REF mAtom;
};
static PLDHashNumber
@ -363,14 +352,14 @@ AtomTableMatchKey(const PLDHashEntryHdr* aEntry, const void* aKey)
return he->mAtom->Equals(k->mUTF16String, k->mLength);
}
static void
AtomTableClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
void
nsAtomFriend::AtomTableClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
{
auto entry = static_cast<AtomTableEntry*>(aEntry);
Atom* atom = entry->mAtom;
nsAtom* atom = entry->mAtom;
if (atom->IsStaticAtom()) {
// This case -- when the entry being cleared holds a static Atom -- only
// occurs when gAtomTable is destroyed, whereupon all static Atoms within it
// This case -- when the entry being cleared holds a static atom -- only
// occurs when gAtomTable is destroyed, whereupon all static atoms within it
// must be explicitly deleted.
delete atom;
}
@ -386,27 +375,18 @@ static const PLDHashTableOps AtomTableOps = {
AtomTableGetHash,
AtomTableMatchKey,
PLDHashTable::MoveEntryStub,
AtomTableClearEntry,
nsAtomFriend::AtomTableClearEntry,
AtomTableInitEntry
};
//----------------------------------------------------------------------
#define RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE 31
static Atom*
static nsAtom*
sRecentlyUsedMainThreadAtoms[RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE] = {};
void
Atom::GCAtomTable()
{
if (NS_IsMainThread()) {
MutexAutoLock lock(*gAtomTableLock);
GCAtomTableLocked(lock, GCKind::RegularOperation);
}
}
void
Atom::GCAtomTableLocked(const MutexAutoLock& aProofOfLock, GCKind aKind)
nsAtomFriend::GCAtomTableLocked(const MutexAutoLock& aProofOfLock, GCKind aKind)
{
MOZ_ASSERT(NS_IsMainThread());
for (uint32_t i = 0; i < RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE; ++i) {
@ -422,7 +402,7 @@ Atom::GCAtomTableLocked(const MutexAutoLock& aProofOfLock, GCKind aKind)
continue;
}
Atom* atom = entry->mAtom;
nsAtom* atom = entry->mAtom;
if (atom->mRefCnt == 0) {
i.Remove();
delete atom;
@ -473,8 +453,17 @@ Atom::GCAtomTableLocked(const MutexAutoLock& aProofOfLock, GCKind aKind)
gUnusedAtomCount -= removedCount;
}
static void
GCAtomTable()
{
if (NS_IsMainThread()) {
MutexAutoLock lock(*gAtomTableLock);
nsAtomFriend::GCAtomTableLocked(lock, GCKind::RegularOperation);
}
}
MozExternalRefCountType
Atom::DynamicAddRef()
nsAtom::DynamicAddRef()
{
MOZ_ASSERT(IsDynamicAtom());
nsrefcnt count = ++mRefCnt;
@ -493,7 +482,7 @@ static const int32_t kAtomGCThreshold = 10000;
#endif
MozExternalRefCountType
Atom::DynamicRelease()
nsAtom::DynamicRelease()
{
MOZ_ASSERT(IsDynamicAtom());
MOZ_ASSERT(mRefCnt > 0);
@ -536,9 +525,9 @@ public:
enum { ALLOW_MEMMOVE = true };
// Static Atoms aren't really refcounted. Because these entries live in a
// Static atoms aren't really refcounted. Because these entries live in a
// global hashtable, this reference is essentially owning.
Atom* MOZ_OWNING_REF mAtom;
nsAtom* MOZ_OWNING_REF mAtom;
};
/**
@ -603,7 +592,7 @@ NS_ShutdownAtomTable()
// builds.
{
MutexAutoLock lock(*gAtomTableLock);
Atom::GCAtomTableLocked(lock, Atom::GCKind::Shutdown);
nsAtomFriend::GCAtomTableLocked(lock, GCKind::Shutdown);
}
#endif
@ -650,7 +639,8 @@ GetAtomHashEntry(const char16_t* aString, uint32_t aLength, uint32_t* aHashOut)
}
void
RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount)
nsAtomFriend::RegisterStaticAtoms(const nsStaticAtom* aAtoms,
uint32_t aAtomCount)
{
MutexAutoLock lock(*gAtomTableLock);
@ -674,7 +664,7 @@ RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount)
GetAtomHashEntry(static_cast<char16_t*>(stringBuffer->Data()),
stringLen, &hash);
Atom* atom = he->mAtom;
nsAtom* atom = he->mAtom;
if (atom) {
// Disallow creating a dynamic atom, and then later, while the
// dynamic atom is still alive, registering that same atom as a
@ -687,7 +677,7 @@ RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount)
"Static atom registration for %s should be pushed back", name.get());
}
} else {
atom = Atom::CreateStatic(stringBuffer, stringLen, hash);
atom = new nsAtom(stringBuffer, stringLen, hash);
he->mAtom = atom;
}
*atomp = atom;
@ -701,14 +691,20 @@ RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount)
}
}
already_AddRefed<nsIAtom>
NS_Atomize(const char* aUTF8String)
void
RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount)
{
return NS_Atomize(nsDependentCString(aUTF8String));
nsAtomFriend::RegisterStaticAtoms(aAtoms, aAtomCount);
}
already_AddRefed<nsIAtom>
NS_Atomize(const nsACString& aUTF8String)
NS_Atomize(const char* aUTF8String)
{
return nsAtomFriend::Atomize(nsDependentCString(aUTF8String));
}
already_AddRefed<nsIAtom>
nsAtomFriend::Atomize(const nsACString& aUTF8String)
{
MutexAutoLock lock(*gAtomTableLock);
uint32_t hash;
@ -727,7 +723,8 @@ NS_Atomize(const nsACString& aUTF8String)
// Actually, now there is, sort of: ForgetSharedBuffer.
nsString str;
CopyUTF8toUTF16(aUTF8String, str);
RefPtr<Atom> atom = Atom::CreateDynamic(str, hash);
RefPtr<nsAtom> atom =
dont_AddRef(new nsAtom(nsAtom::AtomKind::DynamicAtom, str, hash));
he->mAtom = atom;
@ -735,13 +732,19 @@ NS_Atomize(const nsACString& aUTF8String)
}
already_AddRefed<nsIAtom>
NS_Atomize(const char16_t* aUTF16String)
NS_Atomize(const nsACString& aUTF8String)
{
return NS_Atomize(nsDependentString(aUTF16String));
return nsAtomFriend::Atomize(aUTF8String);
}
already_AddRefed<nsIAtom>
NS_Atomize(const nsAString& aUTF16String)
NS_Atomize(const char16_t* aUTF16String)
{
return nsAtomFriend::Atomize(nsDependentString(aUTF16String));
}
already_AddRefed<nsIAtom>
nsAtomFriend::Atomize(const nsAString& aUTF16String)
{
MutexAutoLock lock(*gAtomTableLock);
uint32_t hash;
@ -755,21 +758,28 @@ NS_Atomize(const nsAString& aUTF16String)
return atom.forget();
}
RefPtr<Atom> atom = Atom::CreateDynamic(aUTF16String, hash);
RefPtr<nsAtom> atom =
dont_AddRef(new nsAtom(nsAtom::AtomKind::DynamicAtom, aUTF16String, hash));
he->mAtom = atom;
return atom.forget();
}
already_AddRefed<nsIAtom>
NS_AtomizeMainThread(const nsAString& aUTF16String)
NS_Atomize(const nsAString& aUTF16String)
{
return nsAtomFriend::Atomize(aUTF16String);
}
already_AddRefed<nsIAtom>
nsAtomFriend::AtomizeMainThread(const nsAString& aUTF16String)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIAtom> retVal;
uint32_t hash;
AtomTableKey key(aUTF16String.Data(), aUTF16String.Length(), &hash);
uint32_t index = hash % RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE;
Atom* atom = sRecentlyUsedMainThreadAtoms[index];
nsAtom* atom = sRecentlyUsedMainThreadAtoms[index];
if (atom) {
uint32_t length = atom->GetLength();
if (length == key.mLength &&
@ -786,7 +796,8 @@ NS_AtomizeMainThread(const nsAString& aUTF16String)
if (he->mAtom) {
retVal = he->mAtom;
} else {
RefPtr<Atom> newAtom = Atom::CreateDynamic(aUTF16String, hash);
RefPtr<nsAtom> newAtom = dont_AddRef(
new nsAtom(nsAtom::AtomKind::DynamicAtom, aUTF16String, hash));
he->mAtom = newAtom;
retVal = newAtom.forget();
}
@ -795,10 +806,16 @@ NS_AtomizeMainThread(const nsAString& aUTF16String)
return retVal.forget();
}
already_AddRefed<nsIAtom>
NS_AtomizeMainThread(const nsAString& aUTF16String)
{
return nsAtomFriend::AtomizeMainThread(aUTF16String);
}
nsrefcnt
NS_GetNumberOfAtoms(void)
{
Atom::GCAtomTable(); // Trigger a GC so that we return a deterministic result.
GCAtomTable(); // Trigger a GC so we return a deterministic result.
MutexAutoLock lock(*gAtomTableLock);
return gAtomTable->EntryCount();
}

Просмотреть файл

@ -79,7 +79,11 @@ public:
// A hashcode that is better distributed than the actual atom pointer, for
// use in situations that need a well-distributed hashcode.
uint32_t hash() const { return mHash; }
uint32_t hash() const
{
MOZ_ASSERT(!IsHTML5Atom());
return mHash;
}
protected:
uint32_t mLength: 30;
@ -97,6 +101,30 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIAtom, NS_IATOM_IID)
NS_IMETHOD ToUTF8String(nsACString& _retval) override; \
NS_IMETHOD_(size_t) SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) override;
class nsAtom final : public nsIAtom
{
public:
NS_DECL_NSIATOM
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) final;
typedef mozilla::TrueType HasThreadSafeRefCnt;
private:
friend class nsIAtom;
friend class nsAtomFriend;
friend class nsHtml5AtomEntry;
// Construction and destruction is done entirely by |friend|s.
nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash);
nsAtom(nsStringBuffer* aStringBuffer, uint32_t aLength, uint32_t aHash);
~nsAtom();
MozExternalRefCountType DynamicAddRef();
MozExternalRefCountType DynamicRelease();
mozilla::ThreadSafeAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
};
// The four forms of NS_Atomize (for use with |nsCOMPtr<nsIAtom>|) return the
// atom for the string given. At any given time there will always be one atom
// representing a given string. Atoms are intended to make string comparison

Просмотреть файл

@ -22,7 +22,7 @@ pub trait NsresultExt {
/// Get a printable name for the nsresult error code. This function returns
/// a nsCString<'static>, which implements `Display`.
fn error_name(self) -> nsCString<'static>;
fn error_name(self) -> nsCString;
}
impl NsresultExt for nsresult {
@ -42,7 +42,7 @@ impl NsresultExt for nsresult {
}
}
fn error_name(self) -> nsCString<'static> {
fn error_name(self) -> nsCString {
let mut cstr = nsCString::new();
unsafe {
Gecko_GetErrorName(self, &mut *cstr);

Просмотреть файл

@ -5,12 +5,18 @@
//! Use `&{mut,} nsA[C]String` for functions in rust which wish to take or
//! mutate XPCOM strings. The other string types `Deref` to this type.
//!
//! Use `ns[C]String<'a>` (`ns[C]String` in C++) for string struct members, and
//! as an intermediate between rust string data structures (such as `String`,
//! `Vec<u16>`, `&str`, and `&[u16]`) and `&{mut,} nsA[C]String` (using
//! `ns[C]String::from(value)`). These conversions, when possible, will not
//! perform any allocations. When using this type in structs shared with C++,
//! the correct lifetime argument is usually `'static`.
//! Use `ns[C]String` (`ns[C]String` in C++) for string struct members, and as
//! an intermediate between rust string data structures (such as `String` or
//! `Vec<u16>`) and `&{mut,} nsA[C]String` (using `ns[C]String::from(value)`).
//! These conversions will attempt to re-use the passed-in buffer, appending a
//! null.
//!
//! Use `ns[C]Str` (`nsDependent[C]String` in C++) as an intermediate between
//! borrowed rust data structures (such as `&str` and `&[u16]`) and `&{mut,}
//! nsA[C]String` (using `ns[C]Str::from(value)`). These conversions should not
//! perform any allocations. This type is not safe to share with `C++` as a
//! struct field, but passing the borrowed `&{mut,} nsA[C]String` over FFI is
//! safe.
//!
//! Use `nsFixed[C]String` or `ns_auto_[c]string!` for dynamic stack allocated
//! strings which are expected to hold short string values.
@ -48,7 +54,7 @@
//! with the methods `.append`, `.append_utf{8,16}`, and with the `write!`
//! macro, and can be assigned to with `.assign`.
//!
//! ## `ns[C]String<'a>`
//! ## `ns[C]Str<'a>`
//!
//! This type is an maybe-owned string type. It acts similarially to a
//! `Cow<[{u8,u16}]>`. This type provides `Deref` and `DerefMut` implementations
@ -57,20 +63,36 @@
//! storage. When modified this type may re-allocate in order to ensure that it
//! does not mutate its backing storage.
//!
//! `ns[C]String`s can be constructed either with `ns[C]String::new()`, which
//! creates an empty `ns[C]String<'static>`, or through one of the provided
//! `From` implementations. Both string types may be constructed `From<&'a
//! str>`, with `nsCString` having a `'a` lifetime, as the storage is shared
//! with the `str`, while `nsString` has a `'static` lifetime, as its storage
//! has to be transcoded.
//! `ns[C]Str`s can be constructed either with `ns[C]Str::new()`, which creates
//! an empty `ns[C]Str<'static>`, or through one of the provided `From`
//! implementations. Only `nsCStr` can be constructed `From<'a str>`, as
//! constructing a `nsStr` would require transcoding. Use `ns[C]String` instead.
//!
//! When passing this type by reference, prefer passing a `&nsA[C]String` or
//! `&mut nsA[C]String`. to passing this type.
//!
//! When passing this type across the language boundary, pass it as `*const
//! nsA[C]String` for an immutable reference, or `*mut nsA[C]String` for a
//! mutable reference. This struct may also be included in `#[repr(C)]`
//! structs shared with C++.
//! mutable reference.
//!
//! ## `ns[C]String`
//!
//! This type is an owned, null-terminated string type. This type provides
//! `Deref` and `DerefMut` implementations to `nsA[C]String`, which provides the
//! methods for manipulating this type.
//!
//! `ns[C]String`s can be constructed either with `ns[C]String::new()`, which
//! creates an empty `ns[C]String`, or through one of the provided `From`
//! implementations, which will try to avoid reallocating when possible,
//! although a terminating `null` will be added.
//!
//! When passing this type by reference, prefer passing a `&nsA[C]String` or
//! `&mut nsA[C]String`. to passing this type.
//!
//! When passing this type across the language boundary, pass it as `*const
//! nsA[C]String` for an immutable reference, or `*mut nsA[C]String` for a
//! mutable reference. This struct may also be included in `#[repr(C)]` structs
//! shared with C++.
//!
//! ## `nsFixed[C]String<'a>`
//!
@ -105,7 +127,7 @@
//! ## `ns[C]StringRepr`
//!
//! This crate also provides the type `ns[C]StringRepr` which acts conceptually
//! similar to an `ns[C]String<'static>`, however, it does not have a `Drop`
//! similar to an `ns[C]String`, however, it does not have a `Drop`
//! implementation.
//!
//! If this type is dropped in rust, it will not free its backing storage. This
@ -122,7 +144,6 @@ use std::ops::{Deref, DerefMut};
use std::marker::PhantomData;
use std::borrow;
use std::slice;
use std::ptr;
use std::mem;
use std::fmt;
use std::cmp;
@ -175,6 +196,7 @@ macro_rules! define_string_types {
AString = $AString: ident;
String = $String: ident;
Str = $Str: ident;
FixedString = $FixedString: ident;
StringLike = $StringLike: ident;
@ -207,6 +229,18 @@ macro_rules! define_string_types {
classflags: ClassFlags,
}
impl $StringRepr {
fn new(classflags: ClassFlags) -> $StringRepr {
static NUL: $char_t = 0;
$StringRepr {
data: &NUL,
length: 0,
dataflags: data_flags::TERMINATED | data_flags::LITERAL,
classflags: classflags,
}
}
}
impl Deref for $StringRepr {
type Target = $AString;
fn deref(&self) -> &$AString {
@ -385,8 +419,14 @@ macro_rules! define_string_types {
}
}
impl<'a> cmp::PartialEq<$String<'a>> for $AString {
fn eq(&self, other: &$String<'a>) -> bool {
impl cmp::PartialEq<$String> for $AString {
fn eq(&self, other: &$String) -> bool {
self.eq(&**other)
}
}
impl<'a> cmp::PartialEq<$Str<'a>> for $AString {
fn eq(&self, other: &$Str<'a>) -> bool {
self.eq(&**other)
}
}
@ -398,26 +438,21 @@ macro_rules! define_string_types {
}
#[repr(C)]
pub struct $String<'a> {
pub struct $Str<'a> {
hdr: $StringRepr,
_marker: PhantomData<&'a [$char_t]>,
}
impl $String<'static> {
pub fn new() -> $String<'static> {
$String {
hdr: $StringRepr {
data: ptr::null(),
length: 0,
dataflags: DataFlags::empty(),
classflags: class_flags::NULL_TERMINATED,
},
impl $Str<'static> {
pub fn new() -> $Str<'static> {
$Str {
hdr: $StringRepr::new(ClassFlags::empty()),
_marker: PhantomData,
}
}
}
impl<'a> Drop for $String<'a> {
impl<'a> Drop for $Str<'a> {
fn drop(&mut self) {
unsafe {
$drop(&mut **self);
@ -425,65 +460,184 @@ macro_rules! define_string_types {
}
}
impl<'a> Deref for $String<'a> {
impl<'a> Deref for $Str<'a> {
type Target = $AString;
fn deref(&self) -> &$AString {
&self.hdr
}
}
impl<'a> DerefMut for $String<'a> {
impl<'a> DerefMut for $Str<'a> {
fn deref_mut(&mut self) -> &mut $AString {
&mut self.hdr
}
}
impl<'a> AsRef<[$char_t]> for $String<'a> {
impl<'a> AsRef<[$char_t]> for $Str<'a> {
fn as_ref(&self) -> &[$char_t] {
&self
}
}
impl<'a> From<&'a String> for $String<'a> {
fn from(s: &'a String) -> $String<'a> {
$String::from(&s[..])
}
}
impl<'a> From<&'a Vec<$char_t>> for $String<'a> {
fn from(s: &'a Vec<$char_t>) -> $String<'a> {
$String::from(&s[..])
}
}
impl<'a> From<&'a [$char_t]> for $String<'a> {
fn from(s: &'a [$char_t]) -> $String<'a> {
impl<'a> From<&'a [$char_t]> for $Str<'a> {
fn from(s: &'a [$char_t]) -> $Str<'a> {
assert!(s.len() < (u32::MAX as usize));
$String {
if s.is_empty() {
return $Str::new();
}
$Str {
hdr: $StringRepr {
data: if s.is_empty() { ptr::null() } else { s.as_ptr() },
data: s.as_ptr(),
length: s.len() as u32,
dataflags: DataFlags::empty(),
classflags: class_flags::NULL_TERMINATED,
classflags: ClassFlags::empty(),
},
_marker: PhantomData,
}
}
}
impl From<Box<[$char_t]>> for $String<'static> {
fn from(s: Box<[$char_t]>) -> $String<'static> {
impl<'a> From<&'a Vec<$char_t>> for $Str<'a> {
fn from(s: &'a Vec<$char_t>) -> $Str<'a> {
$Str::from(&s[..])
}
}
impl<'a> From<&'a $AString> for $Str<'a> {
fn from(s: &'a $AString) -> $Str<'a> {
$Str::from(&s[..])
}
}
impl<'a> fmt::Write for $Str<'a> {
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
$AString::write_str(self, s)
}
}
impl<'a> fmt::Display for $Str<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
<$AString as fmt::Display>::fmt(self, f)
}
}
impl<'a> fmt::Debug for $Str<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
<$AString as fmt::Debug>::fmt(self, f)
}
}
impl<'a> cmp::PartialEq for $Str<'a> {
fn eq(&self, other: &$Str<'a>) -> bool {
$AString::eq(self, other)
}
}
impl<'a> cmp::PartialEq<[$char_t]> for $Str<'a> {
fn eq(&self, other: &[$char_t]) -> bool {
$AString::eq(self, other)
}
}
impl<'a, 'b> cmp::PartialEq<&'b [$char_t]> for $Str<'a> {
fn eq(&self, other: &&'b [$char_t]) -> bool {
$AString::eq(self, *other)
}
}
impl<'a> cmp::PartialEq<str> for $Str<'a> {
fn eq(&self, other: &str) -> bool {
$AString::eq(self, other)
}
}
impl<'a, 'b> cmp::PartialEq<&'b str> for $Str<'a> {
fn eq(&self, other: &&'b str) -> bool {
$AString::eq(self, *other)
}
}
#[repr(C)]
pub struct $String {
hdr: $StringRepr,
}
impl $String {
pub fn new() -> $String {
$String {
hdr: $StringRepr::new(class_flags::NULL_TERMINATED),
}
}
}
impl Drop for $String {
fn drop(&mut self) {
unsafe {
$drop(&mut **self);
}
}
}
impl Deref for $String {
type Target = $AString;
fn deref(&self) -> &$AString {
&self.hdr
}
}
impl DerefMut for $String {
fn deref_mut(&mut self) -> &mut $AString {
&mut self.hdr
}
}
impl AsRef<[$char_t]> for $String {
fn as_ref(&self) -> &[$char_t] {
&self
}
}
impl<'a> From<&'a [$char_t]> for $String {
fn from(s: &'a [$char_t]) -> $String {
let mut res = $String::new();
res.assign(&$Str::from(&s[..]));
res
}
}
impl<'a> From<&'a Vec<$char_t>> for $String {
fn from(s: &'a Vec<$char_t>) -> $String {
$String::from(&s[..])
}
}
impl<'a> From<&'a $AString> for $String {
fn from(s: &'a $AString) -> $String {
$String::from(&s[..])
}
}
impl From<Box<[$char_t]>> for $String {
fn from(s: Box<[$char_t]>) -> $String {
s.to_vec().into()
}
}
impl From<Vec<$char_t>> for $String {
fn from(mut s: Vec<$char_t>) -> $String {
assert!(s.len() < (u32::MAX as usize));
if s.is_empty() {
return $String::new();
}
let length = s.len() as u32;
s.push(0); // null terminator
// SAFETY NOTE: This method produces an data_flags::OWNED
// ns[C]String from a Box<[$char_t]>. this is only safe
// because in the Gecko tree, we use the same allocator for
// Rust code as for C++ code, meaning that our box can be
// legally freed with libc::free().
let length = s.len() as u32;
let ptr = s.as_ptr();
mem::forget(s);
unsafe {
@ -493,72 +647,57 @@ macro_rules! define_string_types {
hdr: $StringRepr {
data: ptr,
length: length,
dataflags: data_flags::OWNED,
dataflags: data_flags::OWNED | data_flags::TERMINATED,
classflags: class_flags::NULL_TERMINATED,
},
_marker: PhantomData,
}
}
}
}
impl From<Vec<$char_t>> for $String<'static> {
fn from(s: Vec<$char_t>) -> $String<'static> {
s.into_boxed_slice().into()
}
}
impl<'a> From<&'a $AString> for $String<'static> {
fn from(s: &'a $AString) -> $String<'static> {
let mut string = $String::new();
string.assign(s);
string
}
}
impl<'a> fmt::Write for $String<'a> {
impl fmt::Write for $String {
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
$AString::write_str(self, s)
}
}
impl<'a> fmt::Display for $String<'a> {
impl fmt::Display for $String {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
<$AString as fmt::Display>::fmt(self, f)
}
}
impl<'a> fmt::Debug for $String<'a> {
impl fmt::Debug for $String {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
<$AString as fmt::Debug>::fmt(self, f)
}
}
impl<'a> cmp::PartialEq for $String<'a> {
fn eq(&self, other: &$String<'a>) -> bool {
impl cmp::PartialEq for $String {
fn eq(&self, other: &$String) -> bool {
$AString::eq(self, other)
}
}
impl<'a> cmp::PartialEq<[$char_t]> for $String<'a> {
impl cmp::PartialEq<[$char_t]> for $String {
fn eq(&self, other: &[$char_t]) -> bool {
$AString::eq(self, other)
}
}
impl<'a, 'b> cmp::PartialEq<&'b [$char_t]> for $String<'a> {
fn eq(&self, other: &&'b [$char_t]) -> bool {
impl<'a> cmp::PartialEq<&'a [$char_t]> for $String {
fn eq(&self, other: &&'a [$char_t]) -> bool {
$AString::eq(self, *other)
}
}
impl<'a> cmp::PartialEq<str> for $String<'a> {
impl cmp::PartialEq<str> for $String {
fn eq(&self, other: &str) -> bool {
$AString::eq(self, other)
}
}
impl<'a, 'b> cmp::PartialEq<&'b str> for $String<'a> {
fn eq(&self, other: &&'b str) -> bool {
impl<'a> cmp::PartialEq<&'a str> for $String {
fn eq(&self, other: &&'a str) -> bool {
$AString::eq(self, *other)
}
}
@ -568,7 +707,7 @@ macro_rules! define_string_types {
/// buffer, rather than using heap allocations.
#[repr(C)]
pub struct $FixedString<'a> {
base: $String<'a>,
base: $String,
capacity: u32,
buffer: *mut $char_t,
_marker: PhantomData<&'a mut [$char_t]>,
@ -581,13 +720,7 @@ macro_rules! define_string_types {
let buf_ptr = buf.as_mut_ptr();
$FixedString {
base: $String {
hdr: $StringRepr {
data: ptr::null(),
length: 0,
dataflags: DataFlags::empty(),
classflags: class_flags::FIXED | class_flags::NULL_TERMINATED,
},
_marker: PhantomData,
hdr: $StringRepr::new(class_flags::FIXED | class_flags::NULL_TERMINATED),
},
capacity: len as u32,
buffer: buf_ptr,
@ -667,7 +800,7 @@ macro_rules! define_string_types {
/// &[$char_type], and &$AString to a function, while still performing
/// optimized operations when passed the $AString.
pub enum $StringAdapter<'a> {
Borrowed($String<'a>),
Borrowed($Str<'a>),
Abstract(&'a $AString),
}
@ -720,7 +853,13 @@ macro_rules! define_string_types {
}
}
impl<'a> $StringLike for $String<'a> {
impl<'a> $StringLike for $Str<'a> {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Abstract(self)
}
}
impl $StringLike for $String {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Abstract(self)
}
@ -734,19 +873,19 @@ macro_rules! define_string_types {
impl $StringLike for [$char_t] {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Borrowed($String::from(self))
$StringAdapter::Borrowed($Str::from(self))
}
}
impl $StringLike for Vec<$char_t> {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Borrowed($String::from(&self[..]))
$StringAdapter::Borrowed($Str::from(&self[..]))
}
}
impl $StringLike for Box<[$char_t]> {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Borrowed($String::from(&self[..]))
$StringAdapter::Borrowed($Str::from(&self[..]))
}
}
}
@ -761,6 +900,7 @@ define_string_types! {
AString = nsACString;
String = nsCString;
Str = nsCStr;
FixedString = nsFixedCString;
StringLike = nsCStringLike;
@ -805,20 +945,38 @@ impl nsACString {
}
}
impl<'a> From<&'a str> for nsCString<'a> {
fn from(s: &'a str) -> nsCString<'a> {
impl<'a> From<&'a str> for nsCStr<'a> {
fn from(s: &'a str) -> nsCStr<'a> {
s.as_bytes().into()
}
}
impl From<Box<str>> for nsCString<'static> {
fn from(s: Box<str>) -> nsCString<'static> {
impl<'a> From<&'a String> for nsCStr<'a> {
fn from(s: &'a String) -> nsCStr<'a> {
nsCStr::from(&s[..])
}
}
impl<'a> From<&'a str> for nsCString {
fn from(s: &'a str) -> nsCString {
s.as_bytes().into()
}
}
impl<'a> From<&'a String> for nsCString {
fn from(s: &'a String) -> nsCString {
nsCString::from(&s[..])
}
}
impl From<Box<str>> for nsCString {
fn from(s: Box<str>) -> nsCString {
s.into_string().into()
}
}
impl From<String> for nsCString<'static> {
fn from(s: String) -> nsCString<'static> {
impl From<String> for nsCString {
fn from(s: String) -> nsCString {
s.into_bytes().into()
}
}
@ -851,19 +1009,19 @@ impl cmp::PartialEq<str> for nsACString {
impl nsCStringLike for str {
fn adapt(&self) -> nsCStringAdapter {
nsCStringAdapter::Borrowed(nsCString::from(self))
nsCStringAdapter::Borrowed(nsCStr::from(self))
}
}
impl nsCStringLike for String {
fn adapt(&self) -> nsCStringAdapter {
nsCStringAdapter::Borrowed(nsCString::from(&self[..]))
nsCStringAdapter::Borrowed(nsCStr::from(&self[..]))
}
}
impl nsCStringLike for Box<str> {
fn adapt(&self) -> nsCStringAdapter {
nsCStringAdapter::Borrowed(nsCString::from(&self[..]))
nsCStringAdapter::Borrowed(nsCStr::from(&self[..]))
}
}
@ -884,6 +1042,7 @@ define_string_types! {
AString = nsAString;
String = nsString;
Str = nsStr;
FixedString = nsFixedString;
StringLike = nsStringLike;
@ -926,12 +1085,18 @@ impl nsAString {
// NOTE: The From impl for a string slice for nsString produces a <'static>
// lifetime, as it allocates.
impl<'a> From<&'a str> for nsString<'static> {
fn from(s: &'a str) -> nsString<'static> {
impl<'a> From<&'a str> for nsString {
fn from(s: &'a str) -> nsString {
s.encode_utf16().collect::<Vec<u16>>().into()
}
}
impl<'a> From<&'a String> for nsString {
fn from(s: &'a String) -> nsString {
nsString::from(&s[..])
}
}
// Support for the write!() macro for writing to nsStrings
impl fmt::Write for nsAString {
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
@ -1022,6 +1187,8 @@ pub mod test_helpers {
nsFixedString,
nsCString,
nsString,
nsCStr,
nsStr,
nsCStringRepr,
nsStringRepr,
data_flags,
@ -1042,7 +1209,7 @@ pub mod test_helpers {
}
}
};
($T:ty, $U:ty, $fname:ident) => {
($T:ty, $U:ty, $V:ty, $fname:ident) => {
#[no_mangle]
#[allow(non_snake_case)]
pub extern fn $fname(size: *mut usize, align: *mut usize) {
@ -1052,13 +1219,17 @@ pub mod test_helpers {
assert_eq!(*size, mem::size_of::<$U>());
assert_eq!(*align, mem::align_of::<$U>());
assert_eq!(*size, mem::size_of::<$V>());
assert_eq!(*align, mem::align_of::<$V>());
}
}
}
}
size_align_check!(nsStringRepr, nsString<'static>, Rust_Test_ReprSizeAlign_nsString);
size_align_check!(nsCStringRepr, nsCString<'static>, Rust_Test_ReprSizeAlign_nsCString);
size_align_check!(nsStringRepr, nsString, nsStr<'static>,
Rust_Test_ReprSizeAlign_nsString);
size_align_check!(nsCStringRepr, nsCString, nsCStr<'static>,
Rust_Test_ReprSizeAlign_nsCString);
size_align_check!(nsFixedString<'static>, Rust_Test_ReprSizeAlign_nsFixedString);
size_align_check!(nsFixedCString<'static>, Rust_Test_ReprSizeAlign_nsFixedCString);
@ -1088,7 +1259,7 @@ pub mod test_helpers {
}
}
};
($T:ty, $U:ty, $member:ident, $method:ident) => {
($T:ty, $U:ty, $V:ty, $member:ident, $method:ident) => {
#[no_mangle]
#[allow(non_snake_case)]
pub extern fn $method(size: *mut usize,
@ -1112,19 +1283,35 @@ pub mod test_helpers {
(&tmp.hdr.$member as *const _ as usize) -
(&tmp as *const _ as usize));
mem::forget(tmp);
let tmp: $V = mem::zeroed();
assert_eq!(*size, mem::size_of_val(&tmp.hdr.$member));
assert_eq!(*align, mem::align_of_val(&tmp.hdr.$member));
assert_eq!(*offset,
(&tmp.hdr.$member as *const _ as usize) -
(&tmp as *const _ as usize));
mem::forget(tmp);
}
}
}
}
member_check!(nsStringRepr, nsString<'static>, data, Rust_Test_Member_nsString_mData);
member_check!(nsStringRepr, nsString<'static>, length, Rust_Test_Member_nsString_mLength);
member_check!(nsStringRepr, nsString<'static>, dataflags, Rust_Test_Member_nsString_mDataFlags);
member_check!(nsStringRepr, nsString<'static>, classflags, Rust_Test_Member_nsString_mClassFlags);
member_check!(nsCStringRepr, nsCString<'static>, data, Rust_Test_Member_nsCString_mData);
member_check!(nsCStringRepr, nsCString<'static>, length, Rust_Test_Member_nsCString_mLength);
member_check!(nsCStringRepr, nsCString<'static>, dataflags, Rust_Test_Member_nsCString_mDataFlags);
member_check!(nsCStringRepr, nsCString<'static>, classflags, Rust_Test_Member_nsCString_mClassFlags);
member_check!(nsStringRepr, nsString, nsStr<'static>,
data, Rust_Test_Member_nsString_mData);
member_check!(nsStringRepr, nsString, nsStr<'static>,
length, Rust_Test_Member_nsString_mLength);
member_check!(nsStringRepr, nsString, nsStr<'static>,
dataflags, Rust_Test_Member_nsString_mDataFlags);
member_check!(nsStringRepr, nsString, nsStr<'static>,
classflags, Rust_Test_Member_nsString_mClassFlags);
member_check!(nsCStringRepr, nsCString, nsCStr<'static>,
data, Rust_Test_Member_nsCString_mData);
member_check!(nsCStringRepr, nsCString, nsCStr<'static>,
length, Rust_Test_Member_nsCString_mLength);
member_check!(nsCStringRepr, nsCString, nsCStr<'static>,
dataflags, Rust_Test_Member_nsCString_mDataFlags);
member_check!(nsCStringRepr, nsCString, nsCStr<'static>,
classflags, Rust_Test_Member_nsCString_mClassFlags);
member_check!(nsFixedString<'static>, capacity, Rust_Test_Member_nsFixedString_mFixedCapacity);
member_check!(nsFixedString<'static>, buffer, Rust_Test_Member_nsFixedString_mFixedBuf);
member_check!(nsFixedCString<'static>, capacity, Rust_Test_Member_nsFixedCString_mFixedCapacity);