merge mozilla-inbound to mozilla-central. r=merge a=merge
MozReview-Commit-ID: 4W52wcX8xBm
@ -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)
|
||||
|
Before Width: | Height: | Size: 670 B |
Before Width: | Height: | Size: 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)
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 843 B |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 549 B |
Before Width: | Height: | Size: 1.0 KiB |
7
browser/themes/shared/icons/bookmarks-toolbar.svg
Normal file
@ -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>
|
After Width: | Height: | Size: 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>
|
Before Width: | Height: | Size: 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)
|
||||
|
Before Width: | Height: | Size: 619 B |
Before Width: | Height: | Size: 567 B |
Before Width: | Height: | Size: 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;
|
||||
|
11
layout/reftests/transform-3d/preserve3d-8-ref.html
Normal file
@ -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>
|
17
layout/reftests/transform-3d/preserve3d-8.html
Normal file
@ -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);
|
||||
|