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

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

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

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

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

@ -124,48 +124,6 @@ menuitem.bookmark-item {
list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png); 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] > .toolbarbutton-icon,
.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon { .bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
opacity: 0.5; opacity: 0.5;

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

@ -41,14 +41,12 @@ browser.jar:
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png) skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.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/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/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
skin/classic/browser/places/livemark-item.png (places/livemark-item.png) skin/classic/browser/places/livemark-item.png (places/livemark-item.png)
skin/classic/browser/places/starred48.png (places/starred48.png) skin/classic/browser/places/starred48.png (places/starred48.png)
* skin/classic/browser/places/places.css (places/places.css) * skin/classic/browser/places/places.css (places/places.css)
skin/classic/browser/places/organizer.css (places/organizer.css) skin/classic/browser/places/organizer.css (places/organizer.css)
skin/classic/browser/places/organizer.xml (places/organizer.xml) 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/tag.png (places/tag.png)
skin/classic/browser/places/toolbarDropMarker.png (places/toolbarDropMarker.png) skin/classic/browser/places/toolbarDropMarker.png (places/toolbarDropMarker.png)
skin/classic/browser/places/unsortedBookmarks.png (places/unsortedBookmarks.png) skin/classic/browser/places/unsortedBookmarks.png (places/unsortedBookmarks.png)

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

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

До

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

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

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

До

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

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

@ -186,79 +186,6 @@
-moz-box-align: center; -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 */ /* Workaround for native menubar inheritance */
.openintabs-menuitem, .openintabs-menuitem,
.openlivemarksite-menuitem, .openlivemarksite-menuitem,
@ -829,10 +756,6 @@ html|span.ac-emphasize-text-url {
text-shadow: none; text-shadow: none;
} }
.bookmark-item {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
}
.openintabs-menuitem { .openintabs-menuitem {
list-style-image: none; list-style-image: none;
} }

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

@ -23,7 +23,6 @@ browser.jar:
skin/classic/browser/panel-expander-open@2x.png skin/classic/browser/panel-expander-open@2x.png
skin/classic/browser/panel-plus-sign.png skin/classic/browser/panel-plus-sign.png
skin/classic/browser/page-livemarks.png skin/classic/browser/page-livemarks.png
skin/classic/browser/page-livemarks@2x.png
skin/classic/browser/pageInfo.css skin/classic/browser/pageInfo.css
skin/classic/browser/searchbar.css skin/classic/browser/searchbar.css
skin/classic/browser/slowStartup-16.png 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/allBookmarks.png (places/allBookmarks.png)
* skin/classic/browser/places/places.css (places/places.css) * skin/classic/browser/places/places.css (places/places.css)
skin/classic/browser/places/organizer.css (places/organizer.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/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.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/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/toolbar.png (places/toolbar.png)
skin/classic/browser/places/toolbarDropMarker.png (places/toolbarDropMarker.png) skin/classic/browser/places/toolbarDropMarker.png (places/toolbarDropMarker.png)
skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css) skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)

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

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

До

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

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

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

До

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

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

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

До

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

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

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

До

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

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

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

До

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

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

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

После

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

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

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

До

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

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

@ -170,7 +170,7 @@
skin/classic/browser/stop-to-reload.svg (../shared/icons/stop-to-reload.svg) skin/classic/browser/stop-to-reload.svg (../shared/icons/stop-to-reload.svg)
skin/classic/browser/sync.svg (../shared/icons/sync.svg) skin/classic/browser/sync.svg (../shared/icons/sync.svg)
skin/classic/browser/synced-tabs.svg (../shared/icons/synced-tabs.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/webIDE.svg (../shared/icons/webIDE.svg)
skin/classic/browser/window.svg (../shared/icons/window.svg) skin/classic/browser/window.svg (../shared/icons/window.svg)
skin/classic/browser/zoom-in.svg (../shared/icons/zoom-in.svg) skin/classic/browser/zoom-in.svg (../shared/icons/zoom-in.svg)

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

@ -97,7 +97,7 @@
} }
#panelMenu_viewBookmarksToolbar { #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, #appMenu-library-bookmarks-button,

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

@ -186,7 +186,7 @@ toolbar[brighttext] {
#bookmarks-toolbar-button, #bookmarks-toolbar-button,
#bookmarks-toolbar-placeholder { #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 { #bookmarks-menu-button {
@ -521,3 +521,54 @@ toolbar[brighttext] {
animation-duration: 2s; animation-duration: 2s;
animation-timing-function: ease-out; 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; 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] > .toolbarbutton-icon,
.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon { .bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
opacity: 0.5; opacity: 0.5;

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

@ -10,7 +10,6 @@ browser.jar:
* skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css) * skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css)
* skin/classic/browser/browser.css * skin/classic/browser/browser.css
* skin/classic/browser/compacttheme.css * skin/classic/browser/compacttheme.css
skin/classic/browser/livemark-folder.png
skin/classic/browser/menuPanel-customize.png skin/classic/browser/menuPanel-customize.png
skin/classic/browser/menuPanel-customize@2x.png skin/classic/browser/menuPanel-customize@2x.png
skin/classic/browser/menuPanel-exit.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/notification-icons/geo.svg (notification-icons/geo.svg)
* skin/classic/browser/places/places.css (places/places.css) * skin/classic/browser/places/places.css (places/places.css)
* skin/classic/browser/places/organizer.css (places/organizer.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/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.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/toolbarDropMarker.png (places/toolbarDropMarker.png)
skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css) skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
skin/classic/browser/places/libraryToolbar.png (places/libraryToolbar.png) skin/classic/browser/places/libraryToolbar.png (places/libraryToolbar.png)

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

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

До

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

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

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

До

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

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

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

До

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

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

@ -308,7 +308,12 @@ exports.WebConsoleCommands = WebConsoleCommands;
* The result of calling document.querySelector(selector). * The result of calling document.querySelector(selector).
*/ */
WebConsoleCommands._registerOriginal("$", function (owner, selector) { WebConsoleCommands._registerOriginal("$", function (owner, selector) {
try {
return owner.window.document.querySelector(selector); 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). * Returns the result of document.querySelectorAll(selector).
*/ */
WebConsoleCommands._registerOriginal("$$", function (owner, 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 // Calling owner.window.Array.from() doesn't work without accessing the
// wrappedJSObject, so just loop through the results instead. // wrappedJSObject, so just loop through the results instead.

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

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

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

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

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

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

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

@ -3581,7 +3581,7 @@ Element::GetTransformToAncestor(Element& aAncestor)
// then the call to GetTransformToAncestor will return the transform // then the call to GetTransformToAncestor will return the transform
// all the way up through the parent chain. // all the way up through the parent chain.
transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame, transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame,
ancestorFrame, true); ancestorFrame, nsIFrame::IN_CSS_UNITS);
} }
DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform); DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
@ -3598,7 +3598,7 @@ Element::GetTransformToParent()
if (primaryFrame) { if (primaryFrame) {
nsIFrame* parentFrame = primaryFrame->GetParent(); nsIFrame* parentFrame = primaryFrame->GetParent();
transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame, transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame,
parentFrame, true); parentFrame, nsIFrame::IN_CSS_UNITS);
} }
DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform); DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
@ -3613,7 +3613,7 @@ Element::GetTransformToViewport()
Matrix4x4 transform; Matrix4x4 transform;
if (primaryFrame) { if (primaryFrame) {
transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame, transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame,
nsLayoutUtils::GetDisplayRootFrame(primaryFrame), true); nsLayoutUtils::GetDisplayRootFrame(primaryFrame), nsIFrame::IN_CSS_UNITS);
} }
DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform); DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
@ -4214,6 +4214,26 @@ Element::SetCustomElementData(CustomElementData* aData)
slots->mCustomElementData = 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_SIZE_OF(ServoElementMallocSizeOf)
MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoElementMallocEnclosingSizeOf) MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoElementMallocEnclosingSizeOf)

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

@ -556,6 +556,22 @@ public:
*/ */
void SetCustomElementData(CustomElementData* aData); 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: protected:
/** /**
* Method to get the _intrinsic_ content state of this element. This is the * 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); 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(); for (auto iter = mExtendedSlots->mRegisteredIntersectionObservers.Iter();
@ -848,7 +855,12 @@ FragmentOrElement::nsDOMSlots::Unlink()
mExtendedSlots->mContainingShadow = nullptr; mExtendedSlots->mContainingShadow = nullptr;
MOZ_ASSERT(!(mExtendedSlots->mXBLBinding)); MOZ_ASSERT(!(mExtendedSlots->mXBLBinding));
mExtendedSlots->mXBLInsertionParent = nullptr; mExtendedSlots->mXBLInsertionParent = nullptr;
if (mExtendedSlots->mCustomElementData) {
if (mExtendedSlots->mCustomElementData->mCustomElementDefinition) {
mExtendedSlots->mCustomElementData->mCustomElementDefinition = nullptr;
}
mExtendedSlots->mCustomElementData = nullptr; mExtendedSlots->mCustomElementData = nullptr;
}
mExtendedSlots->mRegisteredIntersectionObservers.Clear(); mExtendedSlots->mRegisteredIntersectionObservers.Clear();
nsCOMPtr<nsIFrameLoader> frameLoader = nsCOMPtr<nsIFrameLoader> frameLoader =
do_QueryInterface(mExtendedSlots->mFrameLoaderOrOpener); 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', 'CORSMode.h',
'FeedWriterEnabled.h', 'FeedWriterEnabled.h',
'FlushType.h', 'FlushType.h',
'RangeBoundary.h',
'TextInputProcessor.h', 'TextInputProcessor.h',
'UseCounter.h', 'UseCounter.h',
] ]

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

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

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

@ -3039,37 +3039,6 @@ nsDOMWindowUtils::RenderDocument(const nsRect& aRect,
return presShell->RenderDocument(aRect, aFlags, aBackgroundColor, aThebesContext); 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 NS_IMETHODIMP
nsDOMWindowUtils::GetDisplayDPI(float *aDPI) nsDOMWindowUtils::GetDisplayDPI(float *aDPI)
{ {

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

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

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

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

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

@ -23,6 +23,7 @@
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h" #include "mozilla/GuardObjects.h"
#include "mozilla/LinkedList.h" #include "mozilla/LinkedList.h"
#include "mozilla/RangeBoundary.h"
namespace mozilla { namespace mozilla {
class ErrorResult; class ErrorResult;
@ -44,6 +45,8 @@ class nsRange final : public nsIDOMRange,
typedef mozilla::ErrorResult ErrorResult; typedef mozilla::ErrorResult ErrorResult;
typedef mozilla::dom::DOMRect DOMRect; typedef mozilla::dom::DOMRect DOMRect;
typedef mozilla::dom::DOMRectList DOMRectList; typedef mozilla::dom::DOMRectList DOMRectList;
typedef mozilla::RangeBoundary RangeBoundary;
typedef mozilla::RawRangeBoundary RawRangeBoundary;
virtual ~nsRange(); virtual ~nsRange();
@ -82,11 +85,21 @@ public:
return mRoot; return mRoot;
} }
const RangeBoundary& StartRef() const
{
return mStart;
}
nsINode* GetStartContainer() const nsINode* GetStartContainer() const
{ {
return mStart.Container(); return mStart.Container();
} }
const RangeBoundary& EndRef() const
{
return mEnd;
}
nsINode* GetEndContainer() const nsINode* GetEndContainer() const
{ {
return mEnd.Container(); return mEnd.Container();
@ -412,215 +425,6 @@ public:
typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable; typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable;
protected: 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 RegisterCommonAncestor(nsINode* aNode);
void UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking); void UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking);
nsINode* IsValidBoundary(nsINode* aNode) const nsINode* IsValidBoundary(nsINode* aNode) const

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

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

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

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

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

@ -46,56 +46,62 @@ private:
class MOZ_STACK_CLASS RawRange final class MOZ_STACK_CLASS RawRange final
{ {
public: public:
RawRange() RawRange() {}
: mStartOffset(0)
, mEndOffset(0)
{
}
void Clear() void Clear()
{ {
mRoot = mStartContainer = mEndContainer = nullptr; mRoot = nullptr;
mStartOffset = mEndOffset = 0; mStart = RangeBoundary();
mEnd = RangeBoundary();
} }
bool IsPositioned() const bool IsPositioned() const
{ {
return mStartContainer && mEndContainer; return mStart.IsSet() && mEnd.IsSet();
} }
bool Collapsed() const bool Collapsed() const
{ {
return mStartContainer == mEndContainer && return mStart == mEnd && IsPositioned();
mStartOffset == mEndOffset &&
IsPositioned();
} }
nsINode* GetStartContainer() const { return mStartContainer; } nsINode* GetStartContainer() const { return mStart.Container(); }
nsINode* GetEndContainer() const { return mEndContainer; } nsINode* GetEndContainer() const { return mEnd.Container(); }
uint32_t StartOffset() const { return mStartOffset; } uint32_t StartOffset() const { return mStart.Offset(); }
uint32_t EndOffset() const { return mEndOffset; } 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 SetStart(const RawRangeBoundary& aStart);
nsresult SetEnd(nsINode* aEndContainer, uint32_t aEndOffset); 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); nsresult SetEndAfter(nsINode* aEndContainer);
void SetStartAndEnd(const nsRange* aRange); void SetStartAndEnd(const nsRange* aRange);
nsresult SetStartAndEnd(nsINode* aStartContainer, uint32_t aStartOffset, nsresult SetStartAndEnd(const RawRangeBoundary& aStart,
nsINode* aEndContainer, uint32_t aEndOffset); const RawRangeBoundary& aEnd);
nsresult SelectNodeContents(nsINode* aNodeToSelectContents); nsresult SelectNodeContents(nsINode* aNodeToSelectContents);
private: private:
bool IsValidOffset(nsINode* aContainer, uint32_t aOffset) const;
nsINode* IsValidBoundary(nsINode* aNode) const; nsINode* IsValidBoundary(nsINode* aNode) const;
inline void AssertStartIsBeforeOrEqualToEnd(); inline void AssertStartIsBeforeOrEqualToEnd();
nsCOMPtr<nsINode> mRoot; nsCOMPtr<nsINode> mRoot;
nsCOMPtr<nsINode> mStartContainer;
nsCOMPtr<nsINode> mEndContainer; RangeBoundary mStart;
uint32_t mStartOffset; RangeBoundary mEnd;
uint32_t mEndOffset;
}; };
public: public:
@ -163,76 +169,72 @@ public:
// When mNode is an element and mOffset is 0, the start position means after // When mNode is an element and mOffset is 0, the start position means after
// the open tag of mNode. // the open tag of mNode.
// This is useful to receive one or more sets of them instead of nsRange. // 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 // Only when mNode is an element node and mOffset is 0, mAfterOpenTag is
// referred. // referred.
bool mAfterOpenTag; bool mAfterOpenTag = true;
NodePosition() NodePosition()
: mOffset(-1) : RangeBoundary()
, mAfterOpenTag(true)
{ {
} }
NodePosition(nsINode* aNode, int32_t aOffset) NodePosition(nsINode* aContainer, int32_t aOffset)
: mNode(aNode) : RangeBoundary(aContainer, aOffset)
, mOffset(aOffset) {
, mAfterOpenTag(true) }
NodePosition(nsINode* aContainer, nsIContent* aRef)
: RangeBoundary(aContainer, aRef)
{ {
} }
explicit NodePosition(const nsIFrame::ContentOffsets& aContentOffsets) explicit NodePosition(const nsIFrame::ContentOffsets& aContentOffsets)
: mNode(aContentOffsets.content) : RangeBoundary(aContentOffsets.content, aContentOffsets.offset)
, mOffset(aContentOffsets.offset)
, mAfterOpenTag(true)
{
}
protected:
NodePosition(nsINode* aNode, int32_t aOffset, bool aAfterOpenTag)
: mNode(aNode)
, mOffset(aOffset)
, mAfterOpenTag(aAfterOpenTag)
{ {
} }
public: public:
bool operator==(const NodePosition& aOther) const bool operator==(const NodePosition& aOther) const
{ {
return mNode == aOther.mNode && return RangeBoundary::operator==(aOther) &&
mOffset == aOther.mOffset &&
mAfterOpenTag == aOther.mAfterOpenTag; 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 bool IsBeforeOpenTag() const
{ {
return IsValid() && mNode->IsElement() && !mOffset && !mAfterOpenTag; return IsSet() &&
Container()->IsElement() &&
!Ref() &&
!mAfterOpenTag;
} }
bool IsImmediatelyAfterOpenTag() const 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 // NodePositionBefore isn't good name if Container() isn't an element node nor
// mOffset is not 0, though, when mNode is an element node and mOffset is 0, // Offset() is not 0, though, when Container() is an element node and mOffset
// this is treated as before the open tag of mNode. // is 0, this is treated as before the open tag of Container().
struct NodePositionBefore final : public NodePosition struct NodePositionBefore final : public NodePosition
{ {
NodePositionBefore(nsINode* aNode, int32_t aOffset) NodePositionBefore(nsINode* aContainer, int32_t aOffset)
: NodePosition(aNode, aOffset, false) : 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"; 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 class WritingModeToString final : public nsAutoCString
{ {
public: public:
@ -123,8 +146,8 @@ public:
NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver) NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver)
// Note that we don't need to add mFirstAddedNodeContainer nor // Note that we don't need to add mFirstAddedContainer nor
// mLastAddedNodeContainer to cycle collection because they are non-null only // mLastAddedContainer to cycle collection because they are non-null only
// during short time and shouldn't be touched while they are non-null. // during short time and shouldn't be touched while they are non-null.
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
@ -174,9 +197,7 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver) NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
IMEContentObserver::IMEContentObserver() IMEContentObserver::IMEContentObserver()
: mFirstAddedNodeOffset(0) : mESM(nullptr)
, mLastAddedNodeOffset(0)
, mESM(nullptr)
, mIMENotificationRequests(nullptr) , mIMENotificationRequests(nullptr)
, mSuppressNotifications(0) , mSuppressNotifications(0)
, mPreCharacterDataChangeLength(-1) , mPreCharacterDataChangeLength(-1)
@ -1001,13 +1022,16 @@ IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
void void
IMEContentObserver::NotifyContentAdded(nsINode* aContainer, IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
int32_t aStartIndex, nsIContent* aFirstContent,
int32_t aEndIndex) nsIContent* aLastContent)
{ {
if (!NeedsTextChangeNotification()) { if (!NeedsTextChangeNotification()) {
return; return;
} }
MOZ_ASSERT_IF(aFirstContent, aFirstContent->GetParentNode() == aContainer);
MOZ_ASSERT_IF(aLastContent, aLastContent->GetParentNode() == aContainer);
mStartOfRemovingTextRangeCache.Clear(); mStartOfRemovingTextRangeCache.Clear();
// If it's in a document change, nodes are added consecutively. Therefore, // 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. // the last node in mEndOfAddedTextCache. Clear it.
mEndOfAddedTextCache.Clear(); mEndOfAddedTextCache.Clear();
if (!HasAddedNodesDuringDocumentChange()) { if (!HasAddedNodesDuringDocumentChange()) {
mFirstAddedNodeContainer = mLastAddedNodeContainer = aContainer; mFirstAddedContainer = mLastAddedContainer = aContainer;
mFirstAddedNodeOffset = aStartIndex; mFirstAddedContent = aFirstContent;
mLastAddedNodeOffset = aEndIndex; mLastAddedContent = aLastContent;
MOZ_LOG(sIMECOLog, LogLevel::Debug, MOZ_LOG(sIMECOLog, LogLevel::Debug,
("0x%p IMEContentObserver::NotifyContentAdded(), starts to store " ("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
"consecutive added nodes", this)); "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, // 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 // notify IME of the previous range first, then, restart to cache the
// range. // range.
if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aStartIndex))) { if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aFirstContent))) {
// Flush the old range first. // Flush the old range first.
MaybeNotifyIMEOfAddedTextDuringDocumentChange(); MaybeNotifyIMEOfAddedTextDuringDocumentChange();
mFirstAddedNodeContainer = aContainer; mFirstAddedContainer = aContainer;
mFirstAddedNodeOffset = aStartIndex; mFirstAddedContent = aFirstContent;
MOZ_LOG(sIMECOLog, LogLevel::Debug, MOZ_LOG(sIMECOLog, LogLevel::Debug,
("0x%p IMEContentObserver::NotifyContentAdded(), starts to store " ("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
"consecutive added nodes", this)); "consecutive added nodes", this));
} }
mLastAddedNodeContainer = aContainer; mLastAddedContainer = aContainer;
mLastAddedNodeOffset = aEndIndex; mLastAddedContent = aLastContent;
return; return;
} }
MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(), MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(),
@ -1048,11 +1072,14 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
uint32_t offset = 0; uint32_t offset = 0;
nsresult rv = NS_OK; nsresult rv = NS_OK;
if (!mEndOfAddedTextCache.Match(aContainer, aStartIndex)) { if (!mEndOfAddedTextCache.Match(aContainer,
aFirstContent->GetPreviousSibling())) {
mEndOfAddedTextCache.Clear(); mEndOfAddedTextCache.Clear();
rv = ContentEventHandler::GetFlatTextLengthInRange( rv = ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0), NodePosition(mRootContent, 0),
NodePositionBefore(aContainer, aStartIndex), NodePositionBefore(aContainer,
PointBefore(aContainer,
aFirstContent)),
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE); mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED((rv)))) { if (NS_WARN_IF(NS_FAILED((rv)))) {
return; return;
@ -1064,8 +1091,10 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
// get offset at the end of the last added node // get offset at the end of the last added node
uint32_t addingLength = 0; uint32_t addingLength = 0;
rv = ContentEventHandler::GetFlatTextLengthInRange( rv = ContentEventHandler::GetFlatTextLengthInRange(
NodePositionBefore(aContainer, aStartIndex), NodePositionBefore(aContainer,
NodePosition(aContainer, aEndIndex), PointBefore(aContainer,
aFirstContent)),
NodePosition(aContainer, aLastContent),
mRootContent, &addingLength, mRootContent, &addingLength,
LINE_BREAK_TYPE_NATIVE); LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED((rv)))) { 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 // NotifyContentAdded() is for adding next node. Therefore, caching the text
// length can skip to compute the text length before the adding node and // length can skip to compute the text length before the adding node and
// before of it. // before of it.
mEndOfAddedTextCache.Cache(aContainer, aEndIndex, offset + addingLength); mEndOfAddedTextCache.Cache(aContainer, aLastContent, offset + addingLength);
if (!addingLength) { if (!addingLength) {
return; return;
@ -1093,27 +1122,27 @@ void
IMEContentObserver::ContentAppended(nsIDocument* aDocument, IMEContentObserver::ContentAppended(nsIDocument* aDocument,
nsIContent* aContainer, nsIContent* aContainer,
nsIContent* aFirstNewContent, nsIContent* aFirstNewContent,
int32_t aNewIndexInContainer) int32_t /* unused */)
{ {
NotifyContentAdded(aContainer, aNewIndexInContainer, NotifyContentAdded(NODE_FROM(aContainer, aDocument),
aContainer->GetChildCount()); aFirstNewContent, aContainer->GetLastChild());
} }
void void
IMEContentObserver::ContentInserted(nsIDocument* aDocument, IMEContentObserver::ContentInserted(nsIDocument* aDocument,
nsIContent* aContainer, nsIContent* aContainer,
nsIContent* aChild, nsIContent* aChild,
int32_t aIndexInContainer) int32_t /* unused */)
{ {
NotifyContentAdded(NODE_FROM(aContainer, aDocument), NotifyContentAdded(NODE_FROM(aContainer, aDocument),
aIndexInContainer, aIndexInContainer + 1); aChild, aChild);
} }
void void
IMEContentObserver::ContentRemoved(nsIDocument* aDocument, IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
nsIContent* aContainer, nsIContent* aContainer,
nsIContent* aChild, nsIContent* aChild,
int32_t aIndexInContainer, int32_t /* unused */,
nsIContent* aPreviousSibling) nsIContent* aPreviousSibling)
{ {
if (!NeedsTextChangeNotification()) { if (!NeedsTextChangeNotification()) {
@ -1127,18 +1156,19 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
uint32_t offset = 0; uint32_t offset = 0;
nsresult rv = NS_OK; 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 // 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( rv = ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0), NodePosition(mRootContent, 0),
NodePosition(containerNode, aIndexInContainer), NodePosition(containerNode, aPreviousSibling),
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE); mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
mStartOfRemovingTextRangeCache.Clear(); mStartOfRemovingTextRangeCache.Clear();
return; return;
} }
mStartOfRemovingTextRangeCache.Cache(containerNode, aIndexInContainer, mStartOfRemovingTextRangeCache.Cache(containerNode, aPreviousSibling,
offset); offset);
} else { } else {
offset = mStartOfRemovingTextRangeCache.mFlatTextLength; offset = mStartOfRemovingTextRangeCache.mFlatTextLength;
@ -1230,8 +1260,8 @@ IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
void void
IMEContentObserver::ClearAddedNodesDuringDocumentChange() IMEContentObserver::ClearAddedNodesDuringDocumentChange()
{ {
mFirstAddedNodeContainer = mLastAddedNodeContainer = nullptr; mFirstAddedContainer = mLastAddedContainer = nullptr;
mFirstAddedNodeOffset = mLastAddedNodeOffset = 0; mFirstAddedContent = mLastAddedContent = nullptr;
MOZ_LOG(sIMECOLog, LogLevel::Debug, MOZ_LOG(sIMECOLog, LogLevel::Debug,
("0x%p IMEContentObserver::ClearAddedNodesDuringDocumentChange()" ("0x%p IMEContentObserver::ClearAddedNodesDuringDocumentChange()"
", finished storing consecutive nodes", this)); ", finished storing consecutive nodes", this));
@ -1256,66 +1286,46 @@ IMEContentObserver::GetChildNode(nsINode* aParent, int32_t aOffset)
bool bool
IMEContentObserver::IsNextNodeOfLastAddedNode(nsINode* aParent, IMEContentObserver::IsNextNodeOfLastAddedNode(nsINode* aParent,
int32_t aOffset) const nsIContent* aChild) const
{ {
MOZ_ASSERT(aParent); MOZ_ASSERT(aParent);
MOZ_ASSERT(aOffset >= 0 && MOZ_ASSERT(aChild && aChild->GetParentNode() == aParent);
aOffset <= static_cast<int32_t>(aParent->Length()));
MOZ_ASSERT(mRootContent); MOZ_ASSERT(mRootContent);
MOZ_ASSERT(HasAddedNodesDuringDocumentChange()); MOZ_ASSERT(HasAddedNodesDuringDocumentChange());
// If the parent node isn't changed, we can check it only with offset. // If the parent node isn't changed, we can check that mLastAddedContent has
if (aParent == mLastAddedNodeContainer) { // aChild as its next sibling.
if (NS_WARN_IF(mLastAddedNodeOffset != aOffset)) { if (aParent == mLastAddedContainer) {
if (NS_WARN_IF(mLastAddedContent->GetNextSibling() != aChild)) {
return false; return false;
} }
return true; return true;
} }
// If the parent node is changed, that means that given offset should be the // If the parent node is changed, that means that the recorded last added node
// last added node not having next sibling. // shouldn't have a sibling.
if (NS_WARN_IF(mLastAddedNodeOffset != if (NS_WARN_IF(mLastAddedContent->GetNextSibling())) {
static_cast<int32_t>(mLastAddedNodeContainer->Length()))) {
return false; return false;
} }
// If the node is aParent is a descendant of mLastAddedNodeContainer, // If the node is aParent is a descendant of mLastAddedContainer,
// aOffset should be 0. // aChild should be the first child in the new container.
if (mLastAddedNodeContainer == aParent->GetParent()) { if (mLastAddedContainer == aParent->GetParent()) {
if (NS_WARN_IF(aOffset)) { if (NS_WARN_IF(aChild->GetPreviousSibling())) {
return false; return false;
} }
return true; return true;
} }
// Otherwise, we need to check it even with slow path. // 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 = nsIContent* nextContentOfLastAddedContent =
lastAddedContent->GetNextNode(mRootContent->GetParentNode()); mLastAddedContent->GetNextNode(mRootContent->GetParentNode());
if (NS_WARN_IF(!nextContentOfLastAddedContent)) { if (NS_WARN_IF(!nextContentOfLastAddedContent)) {
return false; return false;
} }
if (NS_WARN_IF(nextContentOfLastAddedContent != aChild)) {
nsIContent* startContent = GetChildNode(aParent, aOffset);
if (NS_WARN_IF(!startContent) ||
NS_WARN_IF(nextContentOfLastAddedContent != startContent)) {
return false; 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; return true;
} }
@ -1338,8 +1348,9 @@ IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()
nsresult rv = nsresult rv =
ContentEventHandler::GetFlatTextLengthInRange( ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0), NodePosition(mRootContent, 0),
NodePosition(mFirstAddedNodeContainer, NodePosition(mFirstAddedContainer,
mFirstAddedNodeOffset), PointBefore(mFirstAddedContainer,
mFirstAddedContent)),
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE); mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
ClearAddedNodesDuringDocumentChange(); ClearAddedNodesDuringDocumentChange();
@ -1350,10 +1361,10 @@ IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()
uint32_t length; uint32_t length;
rv = rv =
ContentEventHandler::GetFlatTextLengthInRange( ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mFirstAddedNodeContainer, NodePosition(mFirstAddedContainer,
mFirstAddedNodeOffset), PointBefore(mFirstAddedContainer,
NodePosition(mLastAddedNodeContainer, mFirstAddedContent)),
mLastAddedNodeOffset), NodePosition(mLastAddedContainer, mLastAddedContent),
mRootContent, &length, LINE_BREAK_TYPE_NATIVE); mRootContent, &length, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
ClearAddedNodesDuringDocumentChange(); ClearAddedNodesDuringDocumentChange();

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

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

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

@ -80,12 +80,6 @@ interface nsIDOMWindowUtils : nsISupports {
*/ */
readonly attribute boolean docCharsetIsForced; 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 * Function to get metadata associated with the window's current document
* @param aName the name of the metadata. This should be all lowercase. * @param aName the name of the metadata. This should be all lowercase.

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

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

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

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

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

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

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

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

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

@ -394,6 +394,26 @@ public:
RectIterator RectIter() const { return RectIterator(*this); } 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: private:
pixman_region32_t mImpl; pixman_region32_t mImpl;
@ -431,26 +451,6 @@ private:
return *this; 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 pixman_region32_t* Impl() const
{ {
return const_cast<pixman_region32_t*>(&mImpl); return const_cast<pixman_region32_t*>(&mImpl);

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

@ -673,13 +673,17 @@ MessageChannel::CanSend() const
void void
MessageChannel::WillDestroyCurrentMessageLoop() MessageChannel::WillDestroyCurrentMessageLoop()
{ {
#if !defined(ANDROID) #if defined(DEBUG)
#if defined(MOZ_CRASHREPORTER) #if defined(MOZ_CRASHREPORTER)
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProtocolName"), CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProtocolName"),
nsDependentCString(mName)); nsDependentCString(mName));
#endif #endif
MOZ_CRASH("MessageLoop destroyed before MessageChannel that's bound to it"); MOZ_CRASH("MessageLoop destroyed before MessageChannel that's bound to it");
#endif #endif
// Clear mWorkerThread to avoid posting to it in the future.
MonitorAutoLock lock(*mMonitor);
mWorkerLoop = nullptr;
} }
void void
@ -1964,7 +1968,7 @@ MessageChannel::MessageTask::Post()
if (eventTarget) { if (eventTarget) {
eventTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL); eventTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL);
} else { } else if (mChannel->mWorkerLoop) {
mChannel->mWorkerLoop->PostTask(self.forget()); mChannel->mWorkerLoop->PostTask(self.forget());
} }
} }
@ -2406,7 +2410,9 @@ MessageChannel::OnChannelConnected(int32_t peer_id)
mPeerPidSet = true; mPeerPidSet = true;
mPeerPid = peer_id; mPeerPid = peer_id;
RefPtr<CancelableRunnable> task = mOnChannelConnectedTask; RefPtr<CancelableRunnable> task = mOnChannelConnectedTask;
if (mWorkerLoop) {
mWorkerLoop->PostTask(task.forget()); mWorkerLoop->PostTask(task.forget());
}
} }
void void
@ -2599,7 +2605,9 @@ MessageChannel::OnNotifyMaybeChannelError()
&MessageChannel::OnNotifyMaybeChannelError); &MessageChannel::OnNotifyMaybeChannelError);
RefPtr<Runnable> task = mChannelErrorTask; RefPtr<Runnable> task = mChannelErrorTask;
// 10 ms delay is completely arbitrary // 10 ms delay is completely arbitrary
if (mWorkerLoop) {
mWorkerLoop->PostDelayedTask(task.forget(), 10); mWorkerLoop->PostDelayedTask(task.forget(), 10);
}
return; return;
} }
@ -2611,7 +2619,7 @@ MessageChannel::PostErrorNotifyTask()
{ {
mMonitor->AssertCurrentThreadOwns(); mMonitor->AssertCurrentThreadOwns();
if (mChannelErrorTask) if (mChannelErrorTask || !mWorkerLoop)
return; return;
// This must be the last code that runs on this thread! // 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) != '~') if (variable.Name[1].charAt(0) != '~')
return false; 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; var constructExp = constructor.PEdgeCallInstance.Exp;
assert(constructExp.Kind == "Var"); assert(constructExp.Kind == "Var");
@ -55,7 +63,7 @@ function isMatchingDestructor(constructor, edge)
// treat each instance separately, such as when different regions of a function // treat each instance separately, such as when different regions of a function
// body were guarded by these constructors and you needed to do something // body were guarded by these constructors and you needed to do something
// different with each.) // different with each.)
function allRAIIGuardedCallPoints(bodies, body, isConstructor) function allRAIIGuardedCallPoints(typeInfo, bodies, body, isConstructor)
{ {
if (!("PEdge" in body)) if (!("PEdge" in body))
return []; return [];
@ -70,7 +78,7 @@ function allRAIIGuardedCallPoints(bodies, body, isConstructor)
continue; continue;
var variable = callee.Variable; var variable = callee.Variable;
assert(variable.Kind == "Func"); assert(variable.Kind == "Func");
if (!isConstructor(edge.Type, variable.Name)) if (!isConstructor(typeInfo, edge.Type, variable.Name))
continue; continue;
if (!("PEdgeCallInstance" in edge)) if (!("PEdgeCallInstance" in edge))
continue; continue;

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

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

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

@ -2,10 +2,6 @@
"use strict"; "use strict";
// RAII types within which we should assume GC is suppressed, eg
// AutoSuppressGC.
var GCSuppressionTypes = [];
// Ignore calls made through these function pointers // Ignore calls made through these function pointers
var ignoreIndirectCalls = { var ignoreIndirectCalls = {
"mallocSizeOf" : true, "mallocSizeOf" : true,
@ -43,11 +39,20 @@ function indirectCallCannotGC(fullCaller, fullVariable)
return true; return true;
// template method called during marking and hence cannot GC // 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; 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; return false;
} }
@ -218,6 +223,8 @@ var ignoreFunctions = {
"uint32 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true, "uint32 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true,
"void js::Nursery::freeMallocedBuffers()" : 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 // It would be cool to somehow annotate that nsTHashtable<T> will use
// nsTHashtable<T>::s_MatchEntry for its matchEntry function pointer, but // nsTHashtable<T>::s_MatchEntry for its matchEntry function pointer, but
// there is no mechanism for that. So we will just annotate a particularly // there is no mechanism for that. So we will just annotate a particularly
@ -287,11 +294,11 @@ function ignoreGCFunction(mangled)
return true; return true;
// Templatized function // Templatized function
if (fun.indexOf("void nsCOMPtr<T>::Assert_NoQueryNeeded()") >= 0) if (fun.includes("void nsCOMPtr<T>::Assert_NoQueryNeeded()"))
return true; return true;
// These call through an 'op' function pointer. // 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; return true;
// XXX modify refillFreeList<NoGC> to not need data flow analysis to understand it cannot GC. // XXX modify refillFreeList<NoGC> to not need data flow analysis to understand it cannot GC.
@ -307,9 +314,27 @@ function stripUCSAndNamespace(name)
return 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) function isRootedGCPointerTypeName(name)
@ -319,33 +344,16 @@ function isRootedGCPointerTypeName(name)
if (name.startsWith('MaybeRooted<')) if (name.startsWith('MaybeRooted<'))
return /\(js::AllowGC\)1u>::RootType/.test(name); 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'); return name.startsWith('Rooted') || name.startsWith('PersistentRooted');
} }
function isRootedTypeName(name)
{
return isRootedGCTypeName(name) || isRootedGCPointerTypeName(name);
}
function isUnsafeStorage(typeName) function isUnsafeStorage(typeName)
{ {
typeName = stripUCSAndNamespace(typeName); typeName = stripUCSAndNamespace(typeName);
return typeName.startsWith('UniquePtr<'); return typeName.startsWith('UniquePtr<');
} }
function isSuppressConstructor(edgeType, varName) function isSuppressConstructor(typeInfo, edgeType, varName)
{ {
// Check whether this could be a constructor // Check whether this could be a constructor
if (edgeType.Kind != 'Function') if (edgeType.Kind != 'Function')
@ -357,7 +365,7 @@ function isSuppressConstructor(edgeType, varName)
// Check whether the type is a known suppression type. // Check whether the type is a known suppression type.
var type = edgeType.TypeFunctionCSU.Type.Name; var type = edgeType.TypeFunctionCSU.Type.Name;
if (GCSuppressionTypes.indexOf(type) == -1) if (!(type in typeInfo.GCSuppressors))
return false; return false;
// And now make sure this is the constructor, not some other method on a // 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"); loadTypes("src_comp.xdb");
@ -155,7 +155,7 @@ function process(functionName, functionBodies)
body.suppressed = []; body.suppressed = [];
for (var body of functionBodies) { 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; pbody.suppressed[id] = true;
} }

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

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

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

@ -241,7 +241,10 @@ function str_edge(edge, env) {
} }
function str(unknown) { 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); return str_edge(unknown);
} else if ("Kind" in unknown) { } else if ("Kind" in unknown) {
if ("BlockId" in unknown) if ("BlockId" in unknown)

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

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

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

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

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

@ -33,14 +33,27 @@ nsAutoLayoutPhase::Enter()
case eLayoutPhase_Paint: case eLayoutPhase_Paint:
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0, MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0,
"recurring into paint"); "recurring into paint");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_DisplayListBuilding] == 0,
"recurring into paint from display list building");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0, MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
"painting in the middle of reflow"); "painting in the middle of reflow");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0, MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
"painting in the middle of frame construction"); "painting in the middle of frame construction");
break; 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: case eLayoutPhase_Reflow:
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0, MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0,
"reflowing in the middle of a paint"); "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, MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
"recurring into reflow"); "recurring into reflow");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0, MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
@ -49,6 +62,8 @@ nsAutoLayoutPhase::Enter()
case eLayoutPhase_FrameC: case eLayoutPhase_FrameC:
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0, MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0,
"constructing frames in the middle of a paint"); "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, MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
"constructing frames in the middle of reflow"); "constructing frames in the middle of reflow");
MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0, MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,

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

@ -2709,22 +2709,46 @@ nsLayoutUtils::PostTranslate(Matrix4x4& aTransform, const nsPoint& aOrigin, floa
aTransform.PostTranslate(gfxOrigin); 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 Matrix4x4
nsLayoutUtils::GetTransformToAncestor(nsIFrame *aFrame, nsLayoutUtils::GetTransformToAncestor(nsIFrame *aFrame,
const nsIFrame *aAncestor, const nsIFrame *aAncestor,
bool aInCSSUnits) uint32_t aFlags,
nsIFrame** aOutAncestor)
{ {
nsIFrame* parent; nsIFrame* parent;
Matrix4x4 ctm; Matrix4x4 ctm;
if (aFrame == aAncestor) { if (aFrame == aAncestor) {
return ctm; return ctm;
} }
ctm = aFrame->GetTransformMatrix(aAncestor, &parent, aInCSSUnits); ctm = aFrame->GetTransformMatrix(aAncestor, &parent, aFlags);
while (parent && parent != aAncestor) { while (parent && parent != aAncestor &&
(!(aFlags & nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) ||
(!parent->IsStackingContext() && !FrameHasDisplayPort(parent)))) {
if (!parent->Extend3DContext()) { if (!parent->Extend3DContext()) {
ctm.ProjectTo2D(); ctm.ProjectTo2D();
} }
ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent, aInCSSUnits); ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent, aFlags);
}
if (aOutAncestor) {
*aOutAncestor = parent;
} }
return ctm; return ctm;
} }
@ -3029,7 +3053,9 @@ TransformGfxRectToAncestor(nsIFrame *aFrame,
const Rect &aRect, const Rect &aRect,
const nsIFrame *aAncestor, const nsIFrame *aAncestor,
bool* aPreservesAxisAlignedRectangles = nullptr, bool* aPreservesAxisAlignedRectangles = nullptr,
Maybe<Matrix4x4>* aMatrixCache = nullptr) Maybe<Matrix4x4>* aMatrixCache = nullptr,
bool aStopAtStackingContextAndDisplayPort = false,
nsIFrame** aOutAncestor = nullptr)
{ {
Matrix4x4 ctm; Matrix4x4 ctm;
if (aMatrixCache && *aMatrixCache) { if (aMatrixCache && *aMatrixCache) {
@ -3037,7 +3063,11 @@ TransformGfxRectToAncestor(nsIFrame *aFrame,
ctm = aMatrixCache->value(); ctm = aMatrixCache->value();
} else { } else {
// Else, compute it // 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) { if (aMatrixCache) {
// and put it in the cache, if provided // and put it in the cache, if provided
*aMatrixCache = Some(ctm); *aMatrixCache = Some(ctm);
@ -3098,7 +3128,9 @@ nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
const nsRect& aRect, const nsRect& aRect,
const nsIFrame* aAncestor, const nsIFrame* aAncestor,
bool* aPreservesAxisAlignedRectangles /* = nullptr */, bool* aPreservesAxisAlignedRectangles /* = nullptr */,
Maybe<Matrix4x4>* aMatrixCache /* = nullptr */) Maybe<Matrix4x4>* aMatrixCache /* = nullptr */,
bool aStopAtStackingContextAndDisplayPort /* = false */,
nsIFrame** aOutAncestor /* = nullptr */)
{ {
SVGTextFrame* text = GetContainingSVGTextFrame(aFrame); SVGTextFrame* text = GetContainingSVGTextFrame(aFrame);
@ -3107,7 +3139,9 @@ nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
if (text) { if (text) {
result = ToRect(text->TransformFrameRectFromTextChild(aRect, aFrame)); 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 // 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. // could drill down into it to get an answer out of it but we don't yet.
if (aPreservesAxisAlignedRectangles) if (aPreservesAxisAlignedRectangles)
@ -3117,7 +3151,9 @@ nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel), NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel), NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aRect.height, 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(); 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 nsresult
nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame, nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
const nsRegion& aDirtyRegion, nscolor aBackstop, const nsRegion& aDirtyRegion, nscolor aBackstop,
@ -3650,33 +3726,7 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
aFrame->BuildDisplayListForStackingContext(&builder, &list); aFrame->BuildDisplayListForStackingContext(&builder, &list);
} }
LayoutFrameType frameType = aFrame->Type(); AddExtraBackgroundItems(builder, list, aFrame, canvasArea, visibleRegion, aBackstop);
// 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);
}
builder.LeavePresShell(aFrame, &list); builder.LeavePresShell(aFrame, &list);

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

@ -212,6 +212,14 @@ public:
*/ */
static bool HasDisplayPort(nsIContent* aContent); 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 * 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. * displayport base rect that it needs to properly compute a displayport rect.
@ -865,7 +873,9 @@ public:
const nsRect& aRect, const nsRect& aRect,
const nsIFrame* aAncestor, const nsIFrame* aAncestor,
bool* aPreservesAxisAlignedRectangles = nullptr, 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, static Matrix4x4 GetTransformToAncestor(nsIFrame *aFrame,
const nsIFrame *aAncestor, 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 * Gets the scale factors of the transform for aFrame relative to the root
@ -2235,6 +2246,13 @@ public:
static nsIContent* static nsIContent*
GetEditableRootContentByContentEditable(nsIDocument* aDocument); 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 * Returns true if the passed in prescontext needs the dark grey background
* that goes behind the page of a print preview presentation. * that goes behind the page of a print preview presentation.

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

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

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

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

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

@ -1326,11 +1326,18 @@ nsIFrame::GetMarginRectRelativeToSelf() const
bool bool
nsIFrame::IsTransformed(const nsStyleDisplay* aStyleDisplay, nsIFrame::IsTransformed(const nsStyleDisplay* aStyleDisplay,
EffectSet* aEffectSet) const EffectSet* aEffectSet) const
{
return IsCSSTransformed(aStyleDisplay, aEffectSet) ||
IsSVGTransformed();
}
bool
nsIFrame::IsCSSTransformed(const nsStyleDisplay* aStyleDisplay,
EffectSet* aEffectSet) const
{ {
MOZ_ASSERT(aStyleDisplay == StyleDisplay()); MOZ_ASSERT(aStyleDisplay == StyleDisplay());
return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
(aStyleDisplay->HasTransform(this) || (aStyleDisplay->HasTransform(this) ||
IsSVGTransformed() ||
HasAnimationOfTransform(aEffectSet))); HasAnimationOfTransform(aEffectSet)));
} }
@ -1402,7 +1409,7 @@ nsIFrame::Combines3DTransformWithAncestors(const nsStyleDisplay* aStyleDisplay,
if (!parent || !parent->Extend3DContext()) { if (!parent || !parent->Extend3DContext()) {
return false; return false;
} }
return IsTransformed(aStyleDisplay,aEffectSet) || return IsCSSTransformed(aStyleDisplay, aEffectSet) ||
BackfaceIsHidden(aStyleDisplay); BackfaceIsHidden(aStyleDisplay);
} }
@ -3108,21 +3115,10 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
const nsStyleDisplay* disp = child->StyleDisplay(); const nsStyleDisplay* disp = child->StyleDisplay();
const nsStyleEffects* effects = child->StyleEffects(); const nsStyleEffects* effects = child->StyleEffects();
const nsStylePosition* pos = child->StylePosition(); const nsStylePosition* pos = child->StylePosition();
bool isVisuallyAtomic = child->HasOpacity(effectSet) bool isVisuallyAtomic = child->IsVisuallyAtomic(effectSet, disp, effects);
|| 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 isPositioned = disp->IsAbsPosContainingBlock(child); bool isPositioned = disp->IsAbsPosContainingBlock(child);
bool isStackingContext = bool isStackingContext = child->IsStackingContext(disp, pos, isPositioned, isVisuallyAtomic) ||
(isPositioned && (disp->IsPositionForcingStackingContext() || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
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);
if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) || if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) ||
((effects->mClipFlags & NS_STYLE_CLIP_RECT) && ((effects->mClipFlags & NS_STYLE_CLIP_RECT) &&
@ -6298,7 +6294,7 @@ nsIFrame::GetFlattenedTreeParentPrimaryFrame() const
Matrix4x4 Matrix4x4
nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor, nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
nsIFrame** aOutAncestor, nsIFrame** aOutAncestor,
bool aInCSSUnits) uint32_t aFlags)
{ {
NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!"); NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!");
@ -6312,7 +6308,7 @@ nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
*/ */
NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this), NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this),
"Cannot transform the viewport frame!"); "Cannot transform the viewport frame!");
int32_t scaleFactor = (aInCSSUnits ? PresContext()->AppUnitsPerCSSPixel() int32_t scaleFactor = ((aFlags & IN_CSS_UNITS) ? PresContext()->AppUnitsPerCSSPixel()
: PresContext()->AppUnitsPerDevPixel()); : PresContext()->AppUnitsPerDevPixel());
Matrix4x4 result = nsDisplayTransform::GetResultingTransformMatrix(this, Matrix4x4 result = nsDisplayTransform::GetResultingTransformMatrix(this,
@ -6381,14 +6377,18 @@ nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
return Matrix4x4(); return Matrix4x4();
/* Keep iterating while the frame can't possibly be transformed. */ /* Keep iterating while the frame can't possibly be transformed. */
nsIFrame* current = this;
while (!(*aOutAncestor)->IsTransformed() && while (!(*aOutAncestor)->IsTransformed() &&
!nsLayoutUtils::IsPopup(*aOutAncestor) && !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. */ /* If no parent, stop iterating. Otherwise, update the ancestor. */
nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor); nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor);
if (!parent) if (!parent)
break; break;
current = *aOutAncestor;
*aOutAncestor = parent; *aOutAncestor = parent;
} }
@ -6398,7 +6398,7 @@ nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
* entire transform, so we're done. * entire transform, so we're done.
*/ */
nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor); nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
int32_t scaleFactor = (aInCSSUnits ? PresContext()->AppUnitsPerCSSPixel() int32_t scaleFactor = ((aFlags & IN_CSS_UNITS) ? PresContext()->AppUnitsPerCSSPixel()
: PresContext()->AppUnitsPerDevPixel()); : PresContext()->AppUnitsPerDevPixel());
return Matrix4x4::Translation(NSAppUnitsToFloatPixels(delta.x, scaleFactor), return Matrix4x4::Translation(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
NSAppUnitsToFloatPixels(delta.y, scaleFactor), NSAppUnitsToFloatPixels(delta.y, scaleFactor),
@ -10506,6 +10506,41 @@ nsIFrame::IsPseudoStackingContextFromStyle() {
(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT); (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* Element*
nsIFrame::GetPseudoElement(CSSPseudoElementType aType) 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_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 bits that apply to image frames =============================
FRAME_STATE_GROUP(Image, nsImageFrame) 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 bool ScrollFrameHelper::IsIgnoringViewportClipping() const
{ {
if (!mIsRoot) if (!mIsRoot)
@ -2603,14 +2597,7 @@ void ScrollFrameHelper::ScrollVisual()
AdjustViews(mScrolledFrame); AdjustViews(mScrolledFrame);
// We need to call this after fixing up the view positions // We need to call this after fixing up the view positions
// to be consistent with the frame hierarchy. // 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()) { if (aBuilder->IsPaintingToWindow()) {
mScrollPosAtLastPaint = GetScrollPosition(); mScrollPosAtLastPaint = GetScrollPosition();
if (IsMaybeScrollingActive() && NeedToInvalidateOnScroll(mOuter)) {
MarkNotRecentlyScrolled();
}
if (IsMaybeScrollingActive()) { if (IsMaybeScrollingActive()) {
if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) { if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) {
mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint; mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint;

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

@ -1724,6 +1724,12 @@ public:
return IsTransformed(StyleDisplay(), aEffectSet); 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. * 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 * @return A Matrix4x4 that converts points in this frame's coordinate space
* into points in aOutAncestor'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, Matrix4x4 GetTransformMatrix(const nsIFrame* aStopAtAncestor,
nsIFrame **aOutAncestor, nsIFrame **aOutAncestor,
bool aInCSSUnits = false); uint32_t aFlags = 0);
/** /**
* Bit-flags to pass to IsFrameOfType() * Bit-flags to pass to IsFrameOfType()
@ -3450,6 +3460,27 @@ public:
*/ */
bool IsPseudoStackingContextFromStyle(); 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; } virtual bool HonorPrintBackgroundSettings() { return true; }
/** /**

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

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

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

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

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

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

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

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

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

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

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

@ -55,7 +55,6 @@ EXPORTS += [
] ]
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [
'nsHtml5Atom.cpp',
'nsHtml5AtomTable.cpp', 'nsHtml5AtomTable.cpp',
'nsHtml5AttributeName.cpp', 'nsHtml5AttributeName.cpp',
'nsHtml5DependentUTF16Buffer.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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsHtml5AtomTable.h" #include "nsHtml5AtomTable.h"
#include "nsHtml5Atom.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
nsHtml5AtomEntry::nsHtml5AtomEntry(KeyTypePointer aStr) nsHtml5AtomEntry::nsHtml5AtomEntry(KeyTypePointer aStr)
: nsStringHashKey(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() nsHtml5AtomEntry::~nsHtml5AtomEntry()
{ {
delete mAtom;
} }
nsHtml5AtomTable::nsHtml5AtomTable() nsHtml5AtomTable::nsHtml5AtomTable()

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

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

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

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

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

@ -6,20 +6,8 @@
#ifndef nsParserCIID_h__ #ifndef nsParserCIID_h__
#define nsParserCIID_h__ #define nsParserCIID_h__
#include "nsISupports.h"
#include "nsIFactory.h"
#include "nsIComponentManager.h"
// {2ce606b0-bee6-11d1-aad9-00805f8a3e14} // {2ce606b0-bee6-11d1-aad9-00805f8a3e14}
#define NS_PARSER_CID \ #define NS_PARSER_CID \
{ 0x2ce606b0, 0xbee6, 0x11d1, { 0xaa, 0xd9, 0x0, 0x80, 0x5f, 0x8a, 0x3e, 0x14 } } { 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 #endif

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

@ -3,58 +3,27 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * 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 "mozilla/ModuleUtils.h"
#include "nsParserCIID.h"
#include "nsParser.h" #include "nsParser.h"
#include "CNavDTD.h" #include "nsParserCIID.h"
#include "nsHTMLTokenizer.h" #include "nsHTMLTags.h"
//#include "nsTextTokenizer.h"
#include "nsElementTable.h"
#include "nsSAXAttributes.h"
#include "nsSAXLocator.h"
#include "nsSAXXMLReader.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(nsParser)
NS_GENERIC_FACTORY_CONSTRUCTOR(CNavDTD)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSAXAttributes)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSAXXMLReader) 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_PARSER_CID);
NS_DEFINE_NAMED_CID(NS_CNAVDTD_CID);
NS_DEFINE_NAMED_CID(NS_SAXATTRIBUTES_CID);
NS_DEFINE_NAMED_CID(NS_SAXXMLREADER_CID); NS_DEFINE_NAMED_CID(NS_SAXXMLREADER_CID);
static const mozilla::Module::CIDEntry kParserCIDs[] = { static const mozilla::Module::CIDEntry kParserCIDs[] = {
#if defined(DEBUG)
{ &kNS_EXPAT_DRIVER_CID, false, nullptr, nsExpatDriverConstructor },
#endif
{ &kNS_PARSER_CID, false, nullptr, nsParserConstructor }, { &kNS_PARSER_CID, false, nullptr, nsParserConstructor },
{ &kNS_CNAVDTD_CID, false, nullptr, CNavDTDConstructor },
{ &kNS_SAXATTRIBUTES_CID, false, nullptr, nsSAXAttributesConstructor },
{ &kNS_SAXXMLREADER_CID, false, nullptr, nsSAXXMLReaderConstructor }, { &kNS_SAXXMLREADER_CID, false, nullptr, nsSAXXMLReaderConstructor },
{ nullptr } { nullptr }
}; };
static const mozilla::Module::ContractIDEntry kParserContracts[] = { static const mozilla::Module::ContractIDEntry kParserContracts[] = {
{ NS_SAXATTRIBUTES_CONTRACTID, &kNS_SAXATTRIBUTES_CID },
{ NS_SAXXMLREADER_CONTRACTID, &kNS_SAXXMLREADER_CID }, { NS_SAXXMLREADER_CONTRACTID, &kNS_SAXXMLREADER_CID },
{ nullptr } { nullptr }
}; };
@ -67,9 +36,6 @@ Initialize()
#ifdef DEBUG #ifdef DEBUG
CheckElementTable(); CheckElementTable();
#endif
#ifdef DEBUG
nsHTMLTags::TestTagTable(); nsHTMLTags::TestTagTable();
#endif #endif

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

@ -13,12 +13,6 @@
#include "nsString.h" #include "nsString.h"
#include "mozilla/Attributes.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 struct SAXAttr
{ {
nsString uri; nsString uri;

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

@ -10,12 +10,6 @@
#include "nsString.h" #include "nsString.h"
#include "mozilla/Attributes.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 class nsSAXLocator final : public nsISAXLocator
{ {
public: public:

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

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

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

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

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

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

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

@ -960,14 +960,6 @@ class nsIWidget : public nsISupports
virtual void SetBackgroundColor(const nscolor &aColor) { } 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 * Set the cursor for this widget
* *

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

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

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

@ -79,7 +79,11 @@ public:
// A hashcode that is better distributed than the actual atom pointer, for // A hashcode that is better distributed than the actual atom pointer, for
// use in situations that need a well-distributed hashcode. // 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: protected:
uint32_t mLength: 30; 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 ToUTF8String(nsACString& _retval) override; \
NS_IMETHOD_(size_t) SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) 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 // 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 // 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 // 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 /// Get a printable name for the nsresult error code. This function returns
/// a nsCString<'static>, which implements `Display`. /// a nsCString<'static>, which implements `Display`.
fn error_name(self) -> nsCString<'static>; fn error_name(self) -> nsCString;
} }
impl NsresultExt for nsresult { 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(); let mut cstr = nsCString::new();
unsafe { unsafe {
Gecko_GetErrorName(self, &mut *cstr); Gecko_GetErrorName(self, &mut *cstr);

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

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