author | Marco Bonardo <mbonardo@mozilla.com> |
Fri, 09 Mar 2012 13:33:59 +0100 | |
changeset 91886 | ead9016b4102e8bcadc7f02cb47f54e9a890e2c2 |
parent 91795 | 89d3250b701d293eba8f6bad3eed6802659155f3 (current diff) |
parent 91885 | 9059fb810f28f82bfeba318d6b9533c008f760d7 (diff) |
child 91906 | bfb1b7520ce9714dd7d089fb266fc40f004db923 |
child 93051 | c6dcca94622e4b66c69ef48a03c256b5fde85295 |
push id | 136 |
push user | lsblakk@mozilla.com |
push date | Fri, 01 Jun 2012 02:39:32 +0000 |
treeherder | mozilla-release@7ebf7352c959 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 13.0a1 |
first release with | nightly linux32
ead9016b4102
/
13.0a1
/
20120309062528
/
files
nightly linux64
ead9016b4102
/
13.0a1
/
20120309062528
/
files
nightly mac
ead9016b4102
/
13.0a1
/
20120309062528
/
files
nightly win32
ead9016b4102
/
13.0a1
/
20120309062528
/
files
nightly win64
ead9016b4102
/
13.0a1
/
20120309062528
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
13.0a1
/
20120309062528
/
pushlog to previous
nightly linux64
13.0a1
/
20120309062528
/
pushlog to previous
nightly mac
13.0a1
/
20120309062528
/
pushlog to previous
nightly win32
13.0a1
/
20120309062528
/
pushlog to previous
nightly win64
13.0a1
/
20120309062528
/
pushlog to previous
|
--- a/aclocal.m4 +++ b/aclocal.m4 @@ -13,16 +13,17 @@ builtin(include, build/autoconf/altoptio builtin(include, build/autoconf/mozprog.m4)dnl builtin(include, build/autoconf/mozheader.m4)dnl builtin(include, build/autoconf/mozcommonheader.m4)dnl builtin(include, build/autoconf/acwinpaths.m4)dnl builtin(include, build/autoconf/lto.m4)dnl builtin(include, build/autoconf/gcc-pr49911.m4)dnl builtin(include, build/autoconf/frameptr.m4)dnl builtin(include, build/autoconf/compiler-opts.m4)dnl +builtin(include, build/autoconf/expandlibs.m4)dnl MOZ_PROG_CHECKMSYS() # Read the user's .mozconfig script. We can't do this in # configure.in: autoconf puts the argument parsing code above anything # expanded from configure.in, and we need to get the configure options # from .mozconfig in place before that argument parsing code. MOZ_READ_MOZCONFIG(.)
--- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -845,17 +845,17 @@ nsContextMenu.prototype = { saveVideoFrameAsImage: function () { urlSecurityCheck(this.mediaURL, this.browser.contentPrincipal, Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT); let name = ""; try { let uri = makeURI(this.mediaURL); let url = uri.QueryInterface(Ci.nsIURL); if (url.fileBaseName) - name = url.fileBaseName + ".jpg"; + name = decodeURI(url.fileBaseName) + ".jpg"; } catch (e) { } if (!name) name = "snapshot.jpg"; var video = this.target; var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); canvas.width = video.videoWidth; canvas.height = video.videoHeight; var ctxDraw = canvas.getContext("2d");
--- a/browser/components/certerror/content/aboutCertError.css +++ b/browser/components/certerror/content/aboutCertError.css @@ -42,25 +42,11 @@ /* Logical CSS rules belong here, but presentation & theming rules should live in the CSS of the appropriate theme */ #technicalContentText { overflow: auto; white-space: pre-wrap; } -#technicalContent > h2, #expertContent > h2 { - cursor: pointer; - -moz-padding-start: 20px; - position: relative; - left: -20px; -} - -body[dir="rtl"] #technicalContent > h2, -body[dir="rtl"] #expertContent > h2 { - left: auto; - right: -20px; -} - -div[collapsed] > p, -div[collapsed] > div { +.expander[collapsed] + * { display: none; }
--- a/browser/components/certerror/content/aboutCertError.xhtml +++ b/browser/components/certerror/content/aboutCertError.xhtml @@ -255,28 +255,28 @@ <div id="whatShouldIDoContentText"> <p>&certerror.whatShouldIDo.content;</p> <button id='getMeOutOfHereButton'>&certerror.getMeOutOfHere.label;</button> </div> </div> <!-- The following sections can be unhidden by default by setting the "browser.xul.error_pages.expert_bad_cert" pref to true --> - <div id="technicalContent" collapsed="true"> - <h2 onclick="toggle('technicalContent');" id="technicalContentHeading">&certerror.technical.heading;</h2> - <p id="technicalContentText"/> - </div> + <h2 id="technicalContent" class="expander" collapsed="true"> + <button onclick="toggle('technicalContent');">&certerror.technical.heading;</button> + </h2> + <p id="technicalContentText"/> - <div id="expertContent" collapsed="true"> - <h2 onclick="toggle('expertContent');" id="expertContentHeading">&certerror.expert.heading;</h2> - <div> - <p>&certerror.expert.content;</p> - <p>&certerror.expert.contentPara2;</p> - <button id='exceptionDialogButton'>&certerror.addException.label;</button> - </div> + <h2 id="expertContent" class="expander" collapsed="true"> + <button onclick="toggle('expertContent');">&certerror.expert.heading;</button> + </h2> + <div> + <p>&certerror.expert.content;</p> + <p>&certerror.expert.contentPara2;</p> + <button id='exceptionDialogButton'>&certerror.addException.label;</button> </div> </div> </div> <!-- - Note: It is important to run the script this way, instead of using - an onload handler. This is because error pages are loaded as - LOAD_BACKGROUND, which means that onload handlers will not be executed.
--- a/browser/components/dirprovider/DirectoryProvider.cpp +++ b/browser/components/dirprovider/DirectoryProvider.cpp @@ -51,16 +51,17 @@ #include "nsCategoryManagerUtils.h" #include "nsComponentManagerUtils.h" #include "nsCOMArray.h" #include "nsDirectoryServiceUtils.h" #include "mozilla/ModuleUtils.h" #include "nsServiceManagerUtils.h" #include "nsStringAPI.h" #include "nsXULAppAPI.h" +#include "nsIPrefLocalizedString.h" namespace mozilla { namespace browser { NS_IMPL_ISUPPORTS2(DirectoryProvider, nsIDirectoryServiceProvider, nsIDirectoryServiceProvider2) @@ -195,17 +196,28 @@ AppendDistroSearchDirs(nsIProperties* aD nsCOMPtr<nsIFile> localePlugins; rv = searchPlugins->Clone(getter_AddRefs(localePlugins)); if (NS_FAILED(rv)) return; localePlugins->AppendNative(NS_LITERAL_CSTRING("locale")); nsCString locale; - rv = prefs->GetCharPref("general.useragent.locale", getter_Copies(locale)); + nsCOMPtr<nsIPrefLocalizedString> prefString; + rv = prefs->GetComplexValue("general.useragent.locale", + NS_GET_IID(nsIPrefLocalizedString), + getter_AddRefs(prefString)); + if (NS_SUCCEEDED(rv)) { + nsAutoString wLocale; + prefString->GetData(getter_Copies(wLocale)); + CopyUTF16toUTF8(wLocale, locale); + } else { + rv = prefs->GetCharPref("general.useragent.locale", getter_Copies(locale)); + } + if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsIFile> curLocalePlugins; rv = localePlugins->Clone(getter_AddRefs(curLocalePlugins)); if (NS_SUCCEEDED(rv)) { curLocalePlugins->AppendNative(locale); rv = curLocalePlugins->Exists(&exists);
--- a/browser/components/places/content/browserPlacesViews.js +++ b/browser/components/places/content/browserPlacesViews.js @@ -652,17 +652,19 @@ PlacesViewBase.prototype = { PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId }, (function (aStatus, aLivemark) { if (Components.isSuccessCode(aStatus)) { let shouldInvalidate = !aPlacesNode._feedURI; aPlacesNode._feedURI = aLivemark.feedURI; aPlacesNode._siteURI = aLivemark.siteURI; if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) { aLivemark.registerForUpdates(aPlacesNode, this); + // Prioritize the current livemark. aLivemark.reload(); + PlacesUtils.livemarks.reloadLivemarks(); if (shouldInvalidate) this.invalidateContainer(aPlacesNode); } else { aLivemark.unregisterForUpdates(aPlacesNode); } } }).bind(this) @@ -895,21 +897,16 @@ function PlacesToolbar(aPlace) { Services.telemetry.getHistogramById("FX_BOOKMARKS_TOOLBAR_INIT_MS") .add(Date.now() - startTime); } PlacesToolbar.prototype = { __proto__: PlacesViewBase.prototype, _cbEvents: ["dragstart", "dragover", "dragexit", "dragend", "drop", -#ifdef XP_UNIX -#ifndef XP_MACOSX - "mousedown", "mouseup", -#endif -#endif "mousemove", "mouseover", "mouseout"], QueryInterface: function PT_QueryInterface(aIID) { if (aIID.equals(Ci.nsIDOMEventListener) || aIID.equals(Ci.nsITimerCallback)) return this; return PlacesViewBase.prototype.QueryInterface.apply(this, arguments); @@ -1096,26 +1093,16 @@ PlacesToolbar.prototype = { this._onMouseOver(aEvent); break; case "mousemove": this._onMouseMove(aEvent); break; case "mouseout": this._onMouseOut(aEvent); break; -#ifdef XP_UNIX -#ifndef XP_MACOSX - case "mouseup": - this._onMouseUp(aEvent); - break; - case "mousedown": - this._onMouseDown(aEvent); - break; -#endif -#endif case "popupshowing": this._onPopupShowing(aEvent); break; case "popuphidden": this._onPopupHidden(aEvent); break; default: throw "Trying to handle unexpected event."; @@ -1533,24 +1520,16 @@ PlacesToolbar.prototype = { let draggedElt = aEvent.target; if (draggedElt.parentNode != this._rootElt || !draggedElt._placesNode) return; if (draggedElt.localName == "toolbarbutton" && draggedElt.getAttribute("type") == "menu") { // If the drag gesture on a container is toward down we open instead // of dragging. -#ifdef XP_UNIX -#ifndef XP_MACOSX - if (this._mouseDownTimer) { - this._mouseDownTimer.cancel(); - this._mouseDownTimer = null; - } -#endif -#endif let translateY = this._cachedMouseMoveEvent.clientY - aEvent.clientY; let translateX = this._cachedMouseMoveEvent.clientX - aEvent.clientX; if ((translateY) >= Math.abs(translateX/2)) { // Don't start the drag. aEvent.preventDefault(); // Open the menu. draggedElt.open = true; return; @@ -1713,57 +1692,16 @@ PlacesToolbar.prototype = { // Clear the dragover attribute if present, if we are dragging into a // folder in the hierachy of current opened popup we don't clear // this attribute on clearOverFolder. See Notify for closeTimer. if (parent.hasAttribute("dragover")) parent.removeAttribute("dragover"); } }, -#ifdef XP_UNIX -#ifndef XP_MACOSX - _onMouseDown: function PT__onMouseDown(aEvent) { - let target = aEvent.target; - if (aEvent.button == 0 && - target.localName == "toolbarbutton" && - target.getAttribute("type") == "menu") { - this._allowPopupShowing = false; - // On Linux we can open the popup only after a delay. - // Indeed as soon as the menupopup opens we are unable to start a - // drag aEvent. See bug 500081 for details. - this._mouseDownTimer = Cc["@mozilla.org/timer;1"]. - createInstance(Ci.nsITimer); - let callback = { - _self: this, - _target: target, - notify: function(timer) { - this._target.open = true; - this._mouseDownTimer = null; - } - }; - - this._mouseDownTimer.initWithCallback(callback, 300, - Ci.nsITimer.TYPE_ONE_SHOT); - } - }, - - _onMouseUp: function PT__onMouseUp(aEvent) { - if (aEvent.button != 0) - return; - - if (this._mouseDownTimer) { - // On a click (down/up), we should open the menu popup. - this._mouseDownTimer.cancel(); - this._mouseDownTimer = null; - aEvent.target.open = true; - } - }, -#endif -#endif - _onMouseMove: function PT__onMouseMove(aEvent) { // Used in dragStart to prevent dragging folders when dragging down. this._cachedMouseMoveEvent = aEvent; if (this._openedMenuButton == null || PlacesControllerDragHelper.getSession()) return;
--- a/browser/components/places/content/editBookmarkOverlay.xul +++ b/browser/components/places/content/editBookmarkOverlay.xul @@ -85,29 +85,27 @@ <row align="center" id="editBMPanel_feedLocationRow"> <label value="&editBookmarkOverlay.feedLocation.label;" class="editBMPanel_rowLabel" accesskey="&editBookmarkOverlay.feedLocation.accesskey;" control="editBMPanel_feedLocationField" observes="paneElementsBroadcaster"/> <textbox id="editBMPanel_feedLocationField" class="uri-element" - onblur="gEditItemOverlay.onFeedLocationFieldBlur();" observes="paneElementsBroadcaster"/> </row> <row align="center" id="editBMPanel_siteLocationRow"> <label value="&editBookmarkOverlay.siteLocation.label;" class="editBMPanel_rowLabel" accesskey="&editBookmarkOverlay.siteLocation.accesskey;" control="editBMPanel_siteLocationField" observes="paneElementsBroadcaster"/> <textbox id="editBMPanel_siteLocationField" class="uri-element" - onblur="gEditItemOverlay.onSiteLocationFieldBlur();" observes="paneElementsBroadcaster"/> </row> <row align="center" id="editBMPanel_folderRow"> <label value="&editBookmarkOverlay.folder.label;" class="editBMPanel_rowLabel" control="editBMPanel_folderMenuList" observes="paneElementsBroadcaster"/>
--- a/browser/components/places/content/treeView.js +++ b/browser/components/places/content/treeView.js @@ -891,17 +891,19 @@ PlacesTreeView.prototype = { PlacesUtils.livemarks.getLivemark({ id: aNode.itemId }, (function (aStatus, aLivemark) { if (Components.isSuccessCode(aStatus)) { let shouldInvalidate = !aNode._feedURI; aNode._feedURI = aLivemark.feedURI; if (aNewState == Components.interfaces.nsINavHistoryContainerResultNode.STATE_OPENED) { aLivemark.registerForUpdates(aNode, this); + // Prioritize the current livemark. aLivemark.reload(); + PlacesUtils.livemarks.reloadLivemarks(); if (shouldInvalidate) this.invalidateContainer(aNode); } else { aLivemark.unregisterForUpdates(aNode); } } }).bind(this)
--- a/browser/themes/gnomestripe/aboutCertError.css +++ b/browser/themes/gnomestripe/aboutCertError.css @@ -81,26 +81,29 @@ body[dir="rtl"] #errorPageContainer { #errorTitle { -moz-margin-start: 80px; } #errorLongContent { -moz-margin-start: 80px; } -#technicalContent > h2, #expertContent > h2 { - background : url("chrome://browser/skin/section_expanded.png") left 0 no-repeat; -} - -body[dir="rtl"] #technicalContent > h2, -body[dir="rtl"] #expertContent > h2 { - background-position: right 0; +.expander > button { + -moz-padding-start: 20px; + -moz-margin-start: -20px; + background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat; + border: none; + font: inherit; + color: inherit; + cursor: pointer; } -#technicalContent[collapsed] > h2, -#expertContent[collapsed] > h2{ - background-image: url("chrome://browser/skin/section_collapsed.png"); +body[dir="rtl"] .expander > button { + background-position: right center; } -body[dir="rtl"] #technicalContent[collapsed] > h2, -body[dir="rtl"] #expertContent[collapsed] > h2 { - background-image: url("chrome://browser/skin/section_collapsed-rtl.png"); +.expander[collapsed] > button { + background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png"); } + +body[dir="rtl"] .expander[collapsed] > button { + background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png"); +}
rename from browser/themes/gnomestripe/section_collapsed-rtl.png rename to browser/themes/gnomestripe/aboutCertError_sectionCollapsed-rtl.png
rename from browser/themes/gnomestripe/section_collapsed.png rename to browser/themes/gnomestripe/aboutCertError_sectionCollapsed.png
rename from browser/themes/gnomestripe/section_expanded.png rename to browser/themes/gnomestripe/aboutCertError_sectionExpanded.png
deleted file mode 100644 --- a/browser/themes/gnomestripe/fullscreen-video.css +++ /dev/null @@ -1,8 +0,0 @@ -#close { - position: absolute; - top: 0; - right: 0; - width: 32px; - height: 32px; - background: url(KUI-close.png) center center no-repeat; -}
--- a/browser/themes/gnomestripe/jar.mn +++ b/browser/themes/gnomestripe/jar.mn @@ -1,40 +1,39 @@ browser.jar: % skin browser classic/1.0 %skin/classic/browser/ % override chrome://global/skin/icons/warning-16.png moz-icon://stock/gtk-dialog-warning?size=menu skin/classic/browser/sanitizeDialog.css (sanitizeDialog.css) * skin/classic/browser/aboutPrivateBrowsing.css (aboutPrivateBrowsing.css) * skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css) skin/classic/browser/aboutSessionRestore-window-icon.png - skin/classic/browser/aboutCertError.css (aboutCertError.css) + skin/classic/browser/aboutCertError.css + skin/classic/browser/aboutCertError_sectionCollapsed.png + skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png + skin/classic/browser/aboutCertError_sectionExpanded.png #ifdef MOZ_SERVICES_SYNC skin/classic/browser/aboutSyncTabs.css #endif skin/classic/browser/actionicon-tab.png * skin/classic/browser/browser.css (browser.css) * skin/classic/browser/engineManager.css (engineManager.css) - skin/classic/browser/fullscreen-video.css skin/classic/browser/Geolocation-16.png skin/classic/browser/Geolocation-64.png skin/classic/browser/Go-arrow.png skin/classic/browser/identity.png skin/classic/browser/Info.png skin/classic/browser/KUI-close.png skin/classic/browser/monitor.png skin/classic/browser/monitor_16-10.png * skin/classic/browser/pageInfo.css skin/classic/browser/pageInfo.png skin/classic/browser/page-livemarks.png skin/classic/browser/Privacy-16.png skin/classic/browser/Privacy-48.png skin/classic/browser/searchbar.css (searchbar.css) - skin/classic/browser/section_collapsed.png - skin/classic/browser/section_collapsed-rtl.png - skin/classic/browser/section_expanded.png skin/classic/browser/Secure.png skin/classic/browser/Security-broken.png skin/classic/browser/setDesktopBackground.css skin/classic/browser/Toolbar.png skin/classic/browser/Toolbar-small.png skin/classic/browser/urlbar-arrow.png skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png) skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png)
--- a/browser/themes/pinstripe/aboutCertError.css +++ b/browser/themes/pinstripe/aboutCertError.css @@ -81,26 +81,29 @@ body[dir="rtl"] #errorPageContainer { #errorTitle { -moz-margin-start: 80px; } #errorLongContent { -moz-margin-start: 80px; } -#technicalContent > h2, #expertContent > h2 { - background : url("chrome://browser/skin/section_expanded.png") left 0 no-repeat; -} - -body[dir="rtl"] #technicalContent > h2, -body[dir="rtl"] #expertContent > h2 { - background-position: right 0; +.expander > button { + -moz-padding-start: 20px; + -moz-margin-start: -20px; + background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat; + border: none; + font: inherit; + color: inherit; + cursor: pointer; } -#technicalContent[collapsed] > h2, -#expertContent[collapsed] > h2{ - background-image: url("chrome://browser/skin/section_collapsed.png"); +body[dir="rtl"] .expander > button { + background-position: right center; } -body[dir="rtl"] #technicalContent[collapsed] > h2, -body[dir="rtl"] #expertContent[collapsed] > h2 { - background-image: url("chrome://browser/skin/section_collapsed-rtl.png"); +.expander[collapsed] > button { + background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png"); } + +body[dir="rtl"] .expander[collapsed] > button { + background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png"); +}
rename from browser/themes/pinstripe/section_collapsed-rtl.png rename to browser/themes/pinstripe/aboutCertError_sectionCollapsed-rtl.png
rename from browser/themes/pinstripe/section_collapsed.png rename to browser/themes/pinstripe/aboutCertError_sectionCollapsed.png
rename from browser/themes/pinstripe/section_expanded.png rename to browser/themes/pinstripe/aboutCertError_sectionExpanded.png
deleted file mode 100644 --- a/browser/themes/pinstripe/fullscreen-video.css +++ /dev/null @@ -1,8 +0,0 @@ -#close { - position: absolute; - top: 0; - left: 0; - width: 32px; - height: 32px; - background: url(KUI-close.png) center center no-repeat; -}
--- a/browser/themes/pinstripe/jar.mn +++ b/browser/themes/pinstripe/jar.mn @@ -1,22 +1,24 @@ browser.jar: % skin browser classic/1.0 %skin/classic/browser/ skin/classic/browser/sanitizeDialog.css (sanitizeDialog.css) * skin/classic/browser/aboutPrivateBrowsing.css (aboutPrivateBrowsing.css) * skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css) skin/classic/browser/aboutSessionRestore-window-icon.png - skin/classic/browser/aboutCertError.css (aboutCertError.css) + skin/classic/browser/aboutCertError.css + skin/classic/browser/aboutCertError_sectionCollapsed.png + skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png + skin/classic/browser/aboutCertError_sectionExpanded.png #ifdef MOZ_SERVICES_SYNC skin/classic/browser/aboutSyncTabs.css #endif skin/classic/browser/actionicon-tab.png * skin/classic/browser/browser.css (browser.css) * skin/classic/browser/engineManager.css (engineManager.css) - skin/classic/browser/fullscreen-video.css skin/classic/browser/Geolocation-16.png skin/classic/browser/Geolocation-64.png skin/classic/browser/home.png skin/classic/browser/hud-style-check-box-checked.png skin/classic/browser/hud-style-check-box-empty.png skin/classic/browser/hud-style-dropmarker-double-arrows.png skin/classic/browser/hud-style-expander-closed.png skin/classic/browser/hud-style-expander-open.png @@ -31,19 +33,16 @@ browser.jar: skin/classic/browser/page-livemarks.png skin/classic/browser/pageInfo.css skin/classic/browser/Privacy-16.png skin/classic/browser/Privacy-48.png skin/classic/browser/reload-stop-go.png skin/classic/browser/searchbar-dropmarker.png skin/classic/browser/searchbar.css skin/classic/browser/Search.png - skin/classic/browser/section_collapsed.png - skin/classic/browser/section_collapsed-rtl.png - skin/classic/browser/section_expanded.png skin/classic/browser/Secure-Glyph-White.png skin/classic/browser/keyhole-circle.png skin/classic/browser/Toolbar.png skin/classic/browser/toolbarbutton-dropmarker.png skin/classic/browser/urlbar-history-dropmarker.png skin/classic/browser/urlbar-arrow.png skin/classic/browser/urlbar-popup-blocked.png skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
--- a/browser/themes/winstripe/aboutCertError.css +++ b/browser/themes/winstripe/aboutCertError.css @@ -81,26 +81,29 @@ body[dir="rtl"] #errorPageContainer { #errorTitle { -moz-margin-start: 80px; } #errorLongContent { -moz-margin-start: 80px; } -#technicalContent > h2, #expertContent > h2 { - background : url("chrome://browser/skin/section_expanded.png") left center no-repeat; +.expander > button { + -moz-padding-start: 20px; + -moz-margin-start: -20px; + background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat; + border: none; + font: inherit; + color: inherit; + cursor: pointer; } -body[dir="rtl"] #technicalContent > h2, -body[dir="rtl"] #expertContent > h2 { +body[dir="rtl"] .expander > button { background-position: right center; } -#technicalContent[collapsed] > h2, -#expertContent[collapsed] > h2{ - background-image: url("chrome://browser/skin/section_collapsed.png"); +.expander[collapsed] > button { + background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png"); } -body[dir="rtl"] #technicalContent[collapsed] > h2, -body[dir="rtl"] #expertContent[collapsed] > h2 { - background-image: url("chrome://browser/skin/section_collapsed-rtl.png"); +body[dir="rtl"] .expander[collapsed] > button { + background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png"); }
rename from browser/themes/winstripe/section_collapsed-rtl.png rename to browser/themes/winstripe/aboutCertError_sectionCollapsed-rtl.png
rename from browser/themes/winstripe/section_collapsed.png rename to browser/themes/winstripe/aboutCertError_sectionCollapsed.png
rename from browser/themes/winstripe/section_expanded.png rename to browser/themes/winstripe/aboutCertError_sectionExpanded.png
deleted file mode 100644 --- a/browser/themes/winstripe/fullscreen-video.css +++ /dev/null @@ -1,8 +0,0 @@ -#close { - position: absolute; - top: 0; - right: 0; - width: 32px; - height: 32px; - background: url(KUI-close.png) center center no-repeat; -}
--- a/browser/themes/winstripe/jar.mn +++ b/browser/themes/winstripe/jar.mn @@ -2,26 +2,28 @@ browser.jar: % skin browser classic/1.0 %skin/classic/browser/ os=WINNT osversion<6 % skin browser classic/1.0 %skin/classic/browser/ os!=WINNT # NOTE: If you add a new file here, you'll need to add it to the aero # section at the bottom of this file skin/classic/browser/sanitizeDialog.css (sanitizeDialog.css) * skin/classic/browser/aboutPrivateBrowsing.css (aboutPrivateBrowsing.css) * skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css) skin/classic/browser/aboutSessionRestore-window-icon.png (preferences/application.png) - skin/classic/browser/aboutCertError.css (aboutCertError.css) + skin/classic/browser/aboutCertError.css + skin/classic/browser/aboutCertError_sectionCollapsed.png + skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png + skin/classic/browser/aboutCertError_sectionExpanded.png #ifdef MOZ_SERVICES_SYNC skin/classic/browser/aboutSyncTabs.css #endif skin/classic/browser/actionicon-tab.png skin/classic/browser/appmenu-icons.png skin/classic/browser/appmenu-dropmarker.png * skin/classic/browser/browser.css (browser.css) * skin/classic/browser/engineManager.css (engineManager.css) - skin/classic/browser/fullscreen-video.css skin/classic/browser/Geolocation-16.png skin/classic/browser/Geolocation-64.png skin/classic/browser/Info.png (Info.png) skin/classic/browser/identity.png (identity.png) skin/classic/browser/keyhole-forward-mask.svg skin/classic/browser/KUI-background.png skin/classic/browser/KUI-close.png skin/classic/browser/pageInfo.css @@ -33,19 +35,16 @@ browser.jar: skin/classic/browser/reload-stop-go.png skin/classic/browser/Secure24.png (Secure24.png) skin/classic/browser/Toolbar.png (Toolbar.png) skin/classic/browser/Toolbar-inverted.png skin/classic/browser/toolbarbutton-dropdown-arrow.png skin/classic/browser/toolbarbutton-dropdown-arrow-inverted.png * skin/classic/browser/searchbar.css (searchbar.css) skin/classic/browser/searchbar-dropdown-arrow.png - skin/classic/browser/section_collapsed.png - skin/classic/browser/section_collapsed-rtl.png - skin/classic/browser/section_expanded.png skin/classic/browser/setDesktopBackground.css skin/classic/browser/menu-back.png (menu-back.png) skin/classic/browser/menu-forward.png (menu-forward.png) skin/classic/browser/monitor.png skin/classic/browser/monitor_16-10.png skin/classic/browser/urlbar-arrow.png skin/classic/browser/urlbar-popup-blocked.png skin/classic/browser/urlbar-history-dropmarker.png @@ -172,26 +171,28 @@ browser.jar: #ifdef XP_WIN browser.jar: % skin browser classic/1.0 %skin/classic/aero/browser/ os=WINNT osversion>=6 skin/classic/aero/browser/sanitizeDialog.css (sanitizeDialog.css) * skin/classic/aero/browser/aboutPrivateBrowsing.css (aboutPrivateBrowsing.css) * skin/classic/aero/browser/aboutSessionRestore.css (aboutSessionRestore.css) skin/classic/aero/browser/aboutSessionRestore-window-icon.png (aboutSessionRestore-window-icon-aero.png) - skin/classic/aero/browser/aboutCertError.css (aboutCertError.css) + skin/classic/aero/browser/aboutCertError.css + skin/classic/aero/browser/aboutCertError_sectionCollapsed.png + skin/classic/aero/browser/aboutCertError_sectionCollapsed-rtl.png + skin/classic/aero/browser/aboutCertError_sectionExpanded.png #ifdef MOZ_SERVICES_SYNC skin/classic/aero/browser/aboutSyncTabs.css #endif skin/classic/aero/browser/actionicon-tab.png (actionicon-tab.png) skin/classic/aero/browser/appmenu-dropmarker.png skin/classic/aero/browser/appmenu-icons.png * skin/classic/aero/browser/browser.css (browser-aero.css) * skin/classic/aero/browser/engineManager.css (engineManager.css) - skin/classic/aero/browser/fullscreen-video.css skin/classic/aero/browser/Geolocation-16.png skin/classic/aero/browser/Geolocation-64.png skin/classic/aero/browser/Info.png (Info-aero.png) skin/classic/aero/browser/identity.png (identity-aero.png) skin/classic/aero/browser/keyhole-forward-mask.svg skin/classic/aero/browser/KUI-background.png skin/classic/aero/browser/KUI-close.png skin/classic/aero/browser/pageInfo.css @@ -203,19 +204,16 @@ browser.jar: skin/classic/aero/browser/reload-stop-go.png skin/classic/aero/browser/Secure24.png (Secure24-aero.png) skin/classic/aero/browser/Toolbar.png skin/classic/aero/browser/Toolbar-inverted.png skin/classic/aero/browser/toolbarbutton-dropdown-arrow.png skin/classic/aero/browser/toolbarbutton-dropdown-arrow-inverted.png * skin/classic/aero/browser/searchbar.css (searchbar.css) skin/classic/aero/browser/searchbar-dropdown-arrow.png (searchbar-dropdown-arrow-aero.png) - skin/classic/aero/browser/section_collapsed.png - skin/classic/aero/browser/section_collapsed-rtl.png - skin/classic/aero/browser/section_expanded.png skin/classic/aero/browser/setDesktopBackground.css skin/classic/aero/browser/menu-back.png (menu-back-aero.png) skin/classic/aero/browser/menu-forward.png (menu-forward-aero.png) skin/classic/aero/browser/monitor.png skin/classic/aero/browser/monitor_16-10.png skin/classic/aero/browser/urlbar-arrow.png skin/classic/aero/browser/urlbar-popup-blocked.png skin/classic/aero/browser/urlbar-history-dropmarker.png
--- a/build/autoconf/compiler-opts.m4 +++ b/build/autoconf/compiler-opts.m4 @@ -4,10 +4,81 @@ AC_DEFUN([MOZ_COMPILER_OPTS], [ if test "$CLANG_CXX"; then ## We disable return-type-c-linkage because jsval is defined as a C++ type but is ## returned by C functions. This is possible because we use knowledge about the ABI ## to typedef it to a C type with the same layout when the headers are included ## from C. _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage" fi + +if test "$GNU_CC"; then + CFLAGS="$CFLAGS -ffunction-sections -fdata-sections" + CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections" +fi + +dnl ======================================================== +dnl = Identical Code Folding +dnl ======================================================== + +MOZ_ARG_DISABLE_BOOL(icf, +[ --disable-icf Disable Identical Code Folding], + MOZ_DISABLE_ICF=1, + MOZ_DISABLE_ICF= ) + +if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF"; then + AC_CACHE_CHECK([whether the linker supports Identical Code Folding], + LD_SUPPORTS_ICF, + [echo 'int foo() {return 42;}' \ + 'int bar() {return 42;}' \ + 'int main() {return foo() - bar();}' > conftest.${ac_ext} + # If the linker supports ICF, foo and bar symbols will have + # the same address + if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) && + test -s conftest${ac_exeext} && + objdump -t conftest${ac_exeext} | awk changequote(<<, >>)'{a[<<$>>6] = <<$>>1} END {if (a["foo"] && (a["foo"] != a["bar"])) { exit 1 }}'changequote([, ]); then + LD_SUPPORTS_ICF=yes + else + LD_SUPPORTS_ICF=no + fi + rm -rf conftest*]) + if test "$LD_SUPPORTS_ICF" = yes; then + _SAVE_LDFLAGS="$LDFLAGS -Wl,--icf=safe" + LDFLAGS="$LDFLAGS -Wl,--icf=safe -Wl,--print-icf-sections" + AC_TRY_LINK([], [], + [LD_PRINT_ICF_SECTIONS=-Wl,--print-icf-sections], + [LD_PRINT_ICF_SECTIONS=]) + AC_SUBST([LD_PRINT_ICF_SECTIONS]) + LDFLAGS="$_SAVE_LDFLAGS" + fi +fi + +dnl ======================================================== +dnl = Automatically remove dead symbols +dnl ======================================================== + +if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -n "$MOZ_DEBUG_FLAGS"; then + dnl See bug 670659 + AC_CACHE_CHECK([whether removing dead symbols breaks debugging], + GC_SECTIONS_BREAKS_DEBUG_RANGES, + [echo 'int foo() {return 42;}' \ + 'int bar() {return 1;}' \ + 'int main() {return foo();}' > conftest.${ac_ext} + if AC_TRY_COMMAND([${CC-cc} -o conftest.${ac_objext} $CFLAGS $MOZ_DEBUG_FLAGS -c conftest.${ac_ext} 1>&2]) && + AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS $MOZ_DEBUG_FLAGS -Wl,--gc-sections conftest.${ac_objext} $LIBS 1>&2]) && + test -s conftest${ac_exeext} -a -s conftest.${ac_objext}; then + if test "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest.${ac_objext} conftest.${ac_ext}`" = \ + "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest${ac_exeext} conftest.${ac_ext}`"; then + GC_SECTIONS_BREAKS_DEBUG_RANGES=no + else + GC_SECTIONS_BREAKS_DEBUG_RANGES=yes + fi + else + dnl We really don't expect to get here, but just in case + GC_SECTIONS_BREAKS_DEBUG_RANGES="no, but it's broken in some other way" + fi + rm -rf conftest*]) + if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then + DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections" + fi +fi + ]) -
new file mode 100644 --- /dev/null +++ b/build/autoconf/expandlibs.m4 @@ -0,0 +1,56 @@ +AC_DEFUN([MOZ_EXPAND_LIBS], +[ +dnl ======================================================== +dnl = +dnl = Check what kind of list files are supported by the +dnl = linker +dnl = +dnl ======================================================== + +AC_CACHE_CHECK(what kind of list files are supported by the linker, + EXPAND_LIBS_LIST_STYLE, + [echo "int main() {return 0;}" > conftest.${ac_ext} + if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&5) && test -s conftest.${OBJ_SUFFIX}; then + echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list + if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then + EXPAND_LIBS_LIST_STYLE=linkerscript + else + echo "conftest.${OBJ_SUFFIX}" > conftest.list + if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then + EXPAND_LIBS_LIST_STYLE=list + else + EXPAND_LIBS_LIST_STYLE=none + fi + fi + else + dnl We really don't expect to get here, but just in case + AC_ERROR([couldn't compile a simple C file]) + fi + rm -rf conftest*]) + +LIBS_DESC_SUFFIX=desc +AC_SUBST(LIBS_DESC_SUFFIX) +AC_SUBST(EXPAND_LIBS_LIST_STYLE) + +if test "$GCC_USE_GNU_LD"; then + AC_CACHE_CHECK(what kind of ordering can be done with the linker, + EXPAND_LIBS_ORDER_STYLE, + [> conftest.order + _SAVE_LDFLAGS="$LDFLAGS" + LDFLAGS="${LDFLAGS} -Wl,--section-ordering-file,conftest.order" + AC_TRY_LINK([], [], + EXPAND_LIBS_ORDER_STYLE=section-ordering-file, + EXPAND_LIBS_ORDER_STYLE=) + LDFLAGS="$_SAVE_LDFLAGS" + if test -z "$EXPAND_LIBS_ORDER_STYLE"; then + if AC_TRY_COMMAND(${CC-cc} ${DSO_LDOPTS} ${LDFLAGS} -o ${DLL_PREFIX}conftest${DLL_SUFFIX} -Wl,--verbose 2> /dev/null | sed -n '/^===/,/^===/p' | grep '\.text'); then + EXPAND_LIBS_ORDER_STYLE=linkerscript + else + EXPAND_LIBS_ORDER_STYLE=none + fi + rm -f ${DLL_PREFIX}conftest${DLL_SUFFIX} + fi]) +fi +AC_SUBST(EXPAND_LIBS_ORDER_STYLE) + +])
--- a/config/config.mk +++ b/config/config.mk @@ -784,18 +784,22 @@ OPTIMIZE_JARS_CMD = $(PYTHON) $(call cor CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py) EXPAND_LIBS = $(PYTHON) -I$(DEPTH)/config $(topsrcdir)/config/expandlibs.py EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_exec.py EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_gen.py EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR) EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC) EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC) -EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(LD) -EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(MKSHLIB) +EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist -- $(LD) +EXPAND_MKSHLIB_ARGS = --uselist +ifdef SYMBOL_ORDER +EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER) +endif +EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) $(EXPAND_MKSHLIB_ARGS) -- $(MKSHLIB) ifdef STDCXX_COMPAT ifneq ($(OS_ARCH),Darwin) CHECK_STDCXX = objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' > /dev/null && echo "TEST-UNEXPECTED-FAIL | | We don't want these libstdc++ symbols to be used:" && objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && exit 1 || exit 0 endif endif # autoconf.mk sets OBJ_SUFFIX to an error to avoid use before including
--- a/config/expandlibs_config.py.in +++ b/config/expandlibs_config.py.in @@ -49,8 +49,10 @@ AR_EXTRACT = "@AR_EXTRACT@".replace('$(A DLL_PREFIX = "@DLL_PREFIX@" LIB_PREFIX = "@LIB_PREFIX@" OBJ_SUFFIX = normalize_suffix("@OBJ_SUFFIX@") LIB_SUFFIX = normalize_suffix("@LIB_SUFFIX@") DLL_SUFFIX = normalize_suffix("@DLL_SUFFIX@") IMPORT_LIB_SUFFIX = normalize_suffix("@IMPORT_LIB_SUFFIX@") LIBS_DESC_SUFFIX = normalize_suffix("@LIBS_DESC_SUFFIX@") EXPAND_LIBS_LIST_STYLE = "@EXPAND_LIBS_LIST_STYLE@" +EXPAND_LIBS_ORDER_STYLE = "@EXPAND_LIBS_ORDER_STYLE@" +LD_PRINT_ICF_SECTIONS = "@LD_PRINT_ICF_SECTIONS@"
--- a/config/expandlibs_exec.py +++ b/config/expandlibs_exec.py @@ -44,29 +44,41 @@ from static libraries (or use those list With the --uselist argument (useful for e.g. $(CC)), it replaces all object files with a list file. This can be used to avoid limitations in the length of a command line. The kind of list file format used depends on the EXPAND_LIBS_LIST_STYLE variable: 'list' for MSVC style lists (@file.list) or 'linkerscript' for GNU ld linker scripts. See https://bugzilla.mozilla.org/show_bug.cgi?id=584474#c59 for more details. -With the --reorder argument, followed by a file name, it will reorder the -object files from the command line according to the order given in the file. -Implies --extract. +With the --symbol-order argument, followed by a file name, it will add the +relevant linker options to change the order in which the linker puts the +symbols appear in the resulting binary. Only works for ELF targets. ''' from __future__ import with_statement import sys import os from expandlibs import ExpandArgs, relativize, isObject import expandlibs_config as conf from optparse import OptionParser import subprocess import tempfile import shutil +import subprocess +import re + +# The are the insert points for a GNU ld linker script, assuming a more +# or less "standard" default linker script. This is not a dict because +# order is important. +SECTION_INSERT_BEFORE = [ + ('.text', '.fini'), + ('.rodata', '.rodata1'), + ('.data.rel.ro', '.dynamic'), + ('.data', '.data1'), +] class ExpandArgsMore(ExpandArgs): ''' Meant to be used as 'with ExpandArgsMore(args) as ...: ''' def __enter__(self): self.tmp = [] return self def __exit__(self, type, value, tb): @@ -114,61 +126,203 @@ class ExpandArgsMore(ExpandArgs): fd, tmp = tempfile.mkstemp(suffix=".list",dir=os.curdir) if conf.EXPAND_LIBS_LIST_STYLE == "linkerscript": content = ["INPUT(%s)\n" % obj for obj in objs] ref = tmp elif conf.EXPAND_LIBS_LIST_STYLE == "list": content = ["%s\n" % obj for obj in objs] ref = "@" + tmp else: + os.close(fd) os.remove(tmp) return self.tmp.append(tmp) f = os.fdopen(fd, "w") f.writelines(content) f.close() idx = self.index(objs[0]) newlist = self[0:idx] + [ref] + [item for item in self[idx:] if item not in objs] self[0:] = newlist - def reorder(self, order_list): - '''Given a list of file names without OBJ_SUFFIX, rearrange self - so that the object file names it contains are ordered according to - that list. - ''' - objs = [o for o in self if isObject(o)] - if not objs: return - idx = self.index(objs[0]) - # Keep everything before the first object, then the ordered objects, - # then any other objects, then any non-objects after the first object - objnames = dict([(os.path.splitext(os.path.basename(o))[0], o) for o in objs]) - self[0:] = self[0:idx] + [objnames[o] for o in order_list if o in objnames] + \ - [o for o in objs if os.path.splitext(os.path.basename(o))[0] not in order_list] + \ - [x for x in self[idx:] if not isObject(x)] + def _getFoldedSections(self): + '''Returns a dict about folded sections. + When section A and B are folded into section C, the dict contains: + { 'A': 'C', + 'B': 'C', + 'C': ['A', 'B'] }''' + if not conf.LD_PRINT_ICF_SECTIONS: + return {} + + proc = subprocess.Popen(self + [conf.LD_PRINT_ICF_SECTIONS], stdout = subprocess.PIPE, stderr = subprocess.PIPE) + (stdout, stderr) = proc.communicate() + result = {} + # gold's --print-icf-sections output looks like the following: + # ld: ICF folding section '.section' in file 'file.o'into '.section' in file 'file.o' + # In terms of words, chances are this will change in the future, + # especially considering "into" is misplaced. Splitting on quotes + # seems safer. + for l in stderr.split('\n'): + quoted = l.split("'") + if len(quoted) > 5 and quoted[1] != quoted[5]: + result[quoted[1]] = quoted[5] + if quoted[5] in result: + result[quoted[5]].append(quoted[1]) + else: + result[quoted[5]] = [quoted[1]] + return result + + def _getOrderedSections(self, ordered_symbols): + '''Given an ordered list of symbols, returns the corresponding list + of sections following the order.''' + if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']: + raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE) + finder = SectionFinder([arg for arg in self if isObject(arg) or os.path.splitext(arg)[1] == conf.LIB_SUFFIX]) + folded = self._getFoldedSections() + sections = set() + ordered_sections = [] + for symbol in ordered_symbols: + symbol_sections = finder.getSections(symbol) + all_symbol_sections = [] + for section in symbol_sections: + if section in folded: + if isinstance(folded[section], str): + section = folded[section] + all_symbol_sections.append(section) + all_symbol_sections.extend(folded[section]) + else: + all_symbol_sections.append(section) + for section in all_symbol_sections: + if not section in sections: + ordered_sections.append(section) + sections.add(section) + return ordered_sections + + def orderSymbols(self, order): + '''Given a file containing a list of symbols, adds the appropriate + argument to make the linker put the symbols in that order.''' + with open(order) as file: + sections = self._getOrderedSections([l.strip() for l in file.readlines() if l.strip()]) + split_sections = {} + linked_sections = [s[0] for s in SECTION_INSERT_BEFORE] + for s in sections: + for linked_section in linked_sections: + if s.startswith(linked_section): + if linked_section in split_sections: + split_sections[linked_section].append(s) + else: + split_sections[linked_section] = [s] + break + content = [] + # Order is important + linked_sections = [s for s in linked_sections if s in split_sections] + if conf.EXPAND_LIBS_ORDER_STYLE == 'section-ordering-file': + option = '-Wl,--section-ordering-file,%s' + content = sections + for linked_section in linked_sections: + content.extend(split_sections[linked_section]) + content.append('%s.*' % linked_section) + content.append(linked_section) + + elif conf.EXPAND_LIBS_ORDER_STYLE == 'linkerscript': + option = '-Wl,-T,%s' + section_insert_before = dict(SECTION_INSERT_BEFORE) + for linked_section in linked_sections: + content.append('SECTIONS {') + content.append(' %s : {' % linked_section) + content.extend(' *(%s)' % s for s in split_sections[linked_section]) + content.append(' }') + content.append('}') + content.append('INSERT BEFORE %s' % section_insert_before[linked_section]) + else: + raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE) + + fd, tmp = tempfile.mkstemp(dir=os.curdir) + f = os.fdopen(fd, "w") + f.write('\n'.join(content)+'\n') + f.close() + self.tmp.append(tmp) + self.append(option % tmp) + +class SectionFinder(object): + '''Instances of this class allow to map symbol names to sections in + object files.''' + + def __init__(self, objs): + '''Creates an instance, given a list of object files.''' + if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']: + raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE) + self.mapping = {} + for obj in objs: + if not isObject(obj) and os.path.splitext(obj)[1] != conf.LIB_SUFFIX: + raise Exception('%s is not an object nor a static library' % obj) + for symbol, section in SectionFinder._getSymbols(obj): + sym = SectionFinder._normalize(symbol) + if sym in self.mapping: + if not section in self.mapping[sym]: + self.mapping[sym].append(section) + else: + self.mapping[sym] = [section] + + def getSections(self, symbol): + '''Given a symbol, returns a list of sections containing it or the + corresponding thunks. When the given symbol is a thunk, returns the + list of sections containing its corresponding normal symbol and the + other thunks for that symbol.''' + sym = SectionFinder._normalize(symbol) + if sym in self.mapping: + return self.mapping[sym] + return [] + + @staticmethod + def _normalize(symbol): + '''For normal symbols, return the given symbol. For thunks, return + the corresponding normal symbol.''' + if re.match('^_ZThn[0-9]+_', symbol): + return re.sub('^_ZThn[0-9]+_', '_Z', symbol) + return symbol + + @staticmethod + def _getSymbols(obj): + '''Returns a list of (symbol, section) contained in the given object + file.''' + proc = subprocess.Popen(['objdump', '-t', obj], stdout = subprocess.PIPE, stderr = subprocess.PIPE) + (stdout, stderr) = proc.communicate() + syms = [] + for line in stdout.splitlines(): + # Each line has the following format: + # <addr> [lgu!][w ][C ][W ][Ii ][dD ][FfO ] <section>\t<length> <symbol> + tmp = line.split(' ',1) + # This gives us ["<addr>", "[lgu!][w ][C ][W ][Ii ][dD ][FfO ] <section>\t<length> <symbol>"] + # We only need to consider cases where "<section>\t<length> <symbol>" is present, + # and where the [FfO] flag is either F (function) or O (object). + if len(tmp) > 1 and len(tmp[1]) > 6 and tmp[1][6] in ['O', 'F']: + tmp = tmp[1][8:].split() + # That gives us ["<section>","<length>", "<symbol>"] + syms.append((tmp[-1], tmp[0])) + return syms def main(): parser = OptionParser() parser.add_option("--extract", action="store_true", dest="extract", help="when a library has no descriptor file, extract it first, when possible") parser.add_option("--uselist", action="store_true", dest="uselist", help="use a list file for objects when executing a command") parser.add_option("--verbose", action="store_true", dest="verbose", help="display executed command and temporary files content") - parser.add_option("--reorder", dest="reorder", - help="reorder the objects according to the given list", metavar="FILE") + parser.add_option("--symbol-order", dest="symbol_order", metavar="FILE", + help="use the given list of symbols to order symbols in the resulting binary when using with a linker") (options, args) = parser.parse_args() with ExpandArgsMore(args) as args: - if options.extract or options.reorder: + if options.extract: args.extract() - if options.reorder: - with open(options.reorder) as file: - args.reorder([l.strip() for l in file.readlines()]) + if options.symbol_order: + args.orderSymbols(options.symbol_order) if options.uselist: args.makelist() if options.verbose: print >>sys.stderr, "Executing: " + " ".join(args) for tmp in [f for f in args.tmp if os.path.isfile(f)]: print >>sys.stderr, tmp + ":" with open(tmp) as file:
--- a/config/tests/unit-expandlibs.py +++ b/config/tests/unit-expandlibs.py @@ -33,17 +33,17 @@ config_unix = { 'LIBS_DESC_SUFFIX': '.desc', 'EXPAND_LIBS_LIST_STYLE': 'linkerscript', } config = sys.modules['expandlibs_config'] = imp.new_module('expandlibs_config') from expandlibs import LibDescriptor, ExpandArgs, relativize from expandlibs_gen import generate -from expandlibs_exec import ExpandArgsMore +from expandlibs_exec import ExpandArgsMore, SectionFinder def Lib(name): return config.LIB_PREFIX + name + config.LIB_SUFFIX def Obj(name): return name + config.OBJ_SUFFIX def Dll(name): @@ -262,33 +262,106 @@ class TestExpandArgsMore(TestExpandInit) tmp = args.tmp # Check that all temporary files are properly removed self.assertEqual(True, all([not os.path.exists(f) for f in tmp])) # Restore subprocess.call subprocess.call = subprocess_call - def test_reorder(self): - '''Test object reordering''' - # We don't care about AR_EXTRACT testing, which is done in test_extract - config.AR_EXTRACT = '' +class FakeProcess(object): + def __init__(self, out, err = ''): + self.out = out + self.err = err + + def communicate(self): + return (self.out, self.err) - # ExpandArgsMore does the same as ExpandArgs - with ExpandArgsMore(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))]) as args: - self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files) +OBJDUMPS = { +'foo.o': ''' +00000000 g F .text\t00000001 foo +00000000 g F .text._Z6foobarv\t00000001 _Z6foobarv +00000000 g F .text.hello\t00000001 hello +00000000 g F .text._ZThn4_6foobarv\t00000001 _ZThn4_6foobarv +''', +'bar.o': ''' +00000000 g F .text.hi\t00000001 hi +00000000 g F .text.hot._Z6barbazv\t00000001 .hidden _Z6barbazv +''', +} + +PRINT_ICF = ''' +ld: ICF folding section '.text.hello' in file 'foo.o'into '.text.hi' in file 'bar.o' +ld: ICF folding section '.foo' in file 'foo.o'into '.foo' in file 'bar.o' +''' + +class SubprocessPopen(object): + def __init__(self, test): + self.test = test + + def __call__(self, args, stdout = None, stderr = None): + self.test.assertEqual(stdout, subprocess.PIPE) + self.test.assertEqual(stderr, subprocess.PIPE) + if args[0] == 'objdump': + self.test.assertEqual(args[1], '-t') + self.test.assertTrue(args[2] in OBJDUMPS) + return FakeProcess(OBJDUMPS[args[2]]) + else: + return FakeProcess('', PRINT_ICF) - # Use an order containing object files from libraries - order_files = [self.libx_files[1], self.libx_files[0], self.liby_files[2], self.files[1]] - order = [os.path.splitext(os.path.basename(f))[0] for f in order_files] - args.reorder(order[:2] + ['unknown'] + order[2:]) +class TestSectionFinder(unittest.TestCase): + def test_getSections(self): + '''Test SectionFinder''' + # Divert subprocess.Popen + subprocess_popen = subprocess.Popen + subprocess.Popen = SubprocessPopen(self) + config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript' + config.OBJ_SUFFIX = '.o' + config.LIB_SUFFIX = '.a' + finder = SectionFinder(['foo.o', 'bar.o']) + self.assertEqual(finder.getSections('foobar'), []) + self.assertEqual(finder.getSections('_Z6barbazv'), ['.text.hot._Z6barbazv']) + self.assertEqual(finder.getSections('_Z6foobarv'), ['.text._Z6foobarv', '.text._ZThn4_6foobarv']) + self.assertEqual(finder.getSections('_ZThn4_6foobarv'), ['.text._Z6foobarv', '.text._ZThn4_6foobarv']) + subprocess.Popen = subprocess_popen - # self.files has objects at #1, #2, #4 - self.assertRelEqual(args[:3], ['foo', '-bar'] + self.files[:1]) - self.assertRelEqual(args[3:7], order_files) - self.assertRelEqual(args[7:9], [self.files[2], self.files[4]]) - self.assertRelEqual(args[9:11], self.liby_files[:2]) - self.assertRelEqual(args[11:12], [self.libx_files[2]]) - self.assertRelEqual(args[12:14], [self.files[3], self.files[5]]) - self.assertRelEqual(args[14:], [self.liby_files[3]]) +class TestSymbolOrder(unittest.TestCase): + def test_getOrderedSections(self): + '''Test ExpandMoreArgs' _getOrderedSections''' + # Divert subprocess.Popen + subprocess_popen = subprocess.Popen + subprocess.Popen = SubprocessPopen(self) + config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript' + config.OBJ_SUFFIX = '.o' + config.LIB_SUFFIX = '.a' + config.LD_PRINT_ICF_SECTIONS = '' + args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o']) + self.assertEqual(args._getOrderedSections(['_Z6foobarv', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hot._Z6barbazv']) + self.assertEqual(args._getOrderedSections(['_ZThn4_6foobarv', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hot._Z6barbazv']) + subprocess.Popen = subprocess_popen + + def test_getFoldedSections(self): + '''Test ExpandMoreArgs' _getFoldedSections''' + # Divert subprocess.Popen + subprocess_popen = subprocess.Popen + subprocess.Popen = SubprocessPopen(self) + config.LD_PRINT_ICF_SECTIONS = '-Wl,--print-icf-sections' + args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o']) + self.assertEqual(args._getFoldedSections(), {'.text.hello': '.text.hi', '.text.hi': ['.text.hello']}) + subprocess.Popen = subprocess_popen + + def test_getOrderedSectionsWithICF(self): + '''Test ExpandMoreArgs' _getOrderedSections, with ICF''' + # Divert subprocess.Popen + subprocess_popen = subprocess.Popen + subprocess.Popen = SubprocessPopen(self) + config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript' + config.OBJ_SUFFIX = '.o' + config.LIB_SUFFIX = '.a' + config.LD_PRINT_ICF_SECTIONS = '-Wl,--print-icf-sections' + args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o']) + self.assertEqual(args._getOrderedSections(['hello', '_Z6barbazv']), ['.text.hi', '.text.hello', '.text.hot._Z6barbazv']) + self.assertEqual(args._getOrderedSections(['_ZThn4_6foobarv', 'hi', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hi', '.text.hello', '.text.hot._Z6barbazv']) + subprocess.Popen = subprocess_popen + if __name__ == '__main__': unittest.main(testRunner=MozTestRunner())
--- a/configure.in +++ b/configure.in @@ -2519,16 +2519,18 @@ ia64*-hpux*) AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) ;; *-android*|*-linuxandroid*) AC_DEFINE(NO_PW_GECOS) no_x=yes if test -n "$gonkdir"; then _PLATFORM_DEFAULT_TOOLKIT=cairo-gonk + MOZ_B2G_RIL=1 + MOZ_B2G_BT=1 else _PLATFORM_DEFAULT_TOOLKIT=cairo-android MOZ_LINKER=1 fi TARGET_NSPR_MDCPUCFG='\"md/_linux.cfg\"' MOZ_GFX_OPTIMIZE_MOBILE=1 MOZ_OPTIMIZE_FLAGS="-Os -freorder-blocks -fno-reorder-functions" @@ -5033,17 +5035,16 @@ cairo-android) cairo-gonk) AC_DEFINE(MOZ_WIDGET_GONK) AC_DEFINE(MOZ_TOUCH) MOZ_WIDGET_TOOLKIT=gonk TK_CFLAGS='$(MOZ_CAIRO_CFLAGS)' TK_LIBS='$(MOZ_CAIRO_LIBS)' MOZ_WEBGL=1 MOZ_PDF_PRINTING=1 - MOZ_B2G_RIL=1 MOZ_TOUCH=1 ;; esac AC_SUBST(MOZ_OLD_LINKER) AC_SUBST(MOZ_PDF_PRINTING) if test "$MOZ_PDF_PRINTING"; then @@ -7175,88 +7176,16 @@ MOZ_ARG_ENABLE_STRING(debug-symbols, MOZ_DEBUG_SYMBOLS=1) if test -n "$MOZ_DEBUG" -o -n "$MOZ_DEBUG_SYMBOLS"; then AC_DEFINE(MOZ_DEBUG_SYMBOLS) export MOZ_DEBUG_SYMBOLS fi dnl ======================================================== -dnl = Identical Code Folding -dnl ======================================================== - -MOZ_ARG_DISABLE_BOOL(icf, -[ --disable-icf Disable Identical Code Folding], - MOZ_DISABLE_ICF=1, - MOZ_DISABLE_ICF= ) - -if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF"; then - AC_CACHE_CHECK([whether the linker supports Identical Code Folding], - LD_SUPPORTS_ICF, - [echo 'int foo() {return 42;}' \ - 'int bar() {return 42;}' \ - 'int main() {return foo() - bar();}' > conftest.${ac_ext} - # If the linker supports ICF, foo and bar symbols will have - # the same address - if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) && - test -s conftest${ac_exeext} && - objdump -t conftest${ac_exeext} | awk '{a[[$6]] = $1} END {if (a[["foo"]] && (a[["foo"]] != a[["bar"]])) { exit 1 }}'; then - LD_SUPPORTS_ICF=yes - else - LD_SUPPORTS_ICF=no - fi - rm -rf conftest*]) - if test "$LD_SUPPORTS_ICF" = yes; then - LDFLAGS="$LDFLAGS -Wl,--icf=safe" - CFLAGS="$CFLAGS -ffunction-sections" - CXXFLAGS="$CXXFLAGS -ffunction-sections" - fi -fi - -dnl ======================================================== -dnl = Automatically remove dead symbols -dnl ======================================================== - -if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -n "$MOZ_DEBUG_FLAGS"; then - dnl See bug 670659 - AC_CACHE_CHECK([whether removing dead symbols breaks debugging], - GC_SECTIONS_BREAKS_DEBUG_RANGES, - [echo 'int foo() {return 42;}' \ - 'int bar() {return 1;}' \ - 'int main() {return foo();}' > conftest.${ac_ext} - if AC_TRY_COMMAND([${CC-cc} -o conftest.${ac_objext} $CFLAGS $MOZ_DEBUG_FLAGS -ffunction-sections -c conftest.${ac_ext} 1>&2]) && - AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS $MOZ_DEBUG_FLAGS -Wl,--gc-sections conftest.${ac_objext} $LIBS 1>&2]) && - test -s conftest${ac_exeext} -a -s conftest.${ac_objext}; then - if test "`$PYTHON "$_topsrcdir"/build/unix/check_debug_ranges.py conftest.${ac_objext} conftest.${ac_ext}`" = \ - "`$PYTHON "$_topsrcdir"/build/unix/check_debug_ranges.py conftest${ac_exeext} conftest.${ac_ext}`"; then - GC_SECTIONS_BREAKS_DEBUG_RANGES=no - else - GC_SECTIONS_BREAKS_DEBUG_RANGES=yes - fi - else - dnl We really don't expect to get here, but just in case - GC_SECTIONS_BREAKS_DEBUG_RANGES="no, but it's broken in some other way" - fi - rm -rf conftest*]) - if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then - DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections" - case "$CFLAGS" in - *-ffunction-sections*) - CFLAGS="$CFLAGS -fdata-sections" - CXXFLAGS="$CXXFLAGS -fdata-sections" - ;; - *) - CFLAGS="$CFLAGS -ffunction-sections -fdata-sections" - CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections" - ;; - esac - fi -fi - -dnl ======================================================== dnl = Enable any treating of compile warnings as errors dnl ======================================================== MOZ_ARG_ENABLE_BOOL(warnings-as-errors, [ --enable-warnings-as-errors Enable treating of warnings as errors], MOZ_ENABLE_WARNINGS_AS_ERRORS=1, MOZ_ENABLE_WARNINGS_AS_ERRORS=) if test -z "$MOZ_ENABLE_WARNINGS_AS_ERRORS"; then @@ -8067,47 +7996,17 @@ CXXFLAGS="$_SAVE_CXXFLAGS" if test "$ac_nscap_nonconst_opeq_bug" = "yes" ; then AC_DEFINE(NSCAP_DONT_PROVIDE_NONCONST_OPEQ) fi fi # ! SKIP_COMPILER_CHECKS AC_DEFINE(CPP_THROW_NEW, [throw()]) AC_LANG_C -dnl ======================================================== -dnl = -dnl = Check what kind of list files are supported by the -dnl = linker -dnl = -dnl ======================================================== - -AC_CACHE_CHECK(what kind of list files are supported by the linker, - EXPAND_LIBS_LIST_STYLE, - [echo "int main() {return 0;}" > conftest.${ac_ext} - if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&2) && test -s conftest.${OBJ_SUFFIX}; then - echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list - if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&2) && test -s conftest${ac_exeext}; then - EXPAND_LIBS_LIST_STYLE=linkerscript - else - echo "conftest.${OBJ_SUFFIX}" > conftest.list - if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&2) && test -s conftest${ac_exeext}; then - EXPAND_LIBS_LIST_STYLE=list - else - EXPAND_LIBS_LIST_STYLE=none - fi - fi - else - dnl We really don't expect to get here, but just in case - AC_ERROR([couldn't compile a simple C file]) - fi - rm -rf conftest*]) - -LIBS_DESC_SUFFIX=desc -AC_SUBST(LIBS_DESC_SUFFIX) -AC_SUBST(EXPAND_LIBS_LIST_STYLE) +MOZ_EXPAND_LIBS dnl ======================================================== dnl = dnl = Build depencency options dnl = dnl ======================================================== MOZ_ARG_HEADER(Build dependencies)
--- a/content/base/public/Makefile.in +++ b/content/base/public/Makefile.in @@ -60,17 +60,16 @@ nsIMutationObserver.h \ nsIMutationObserver2.h \ nsINameSpaceManager.h \ nsINode.h \ nsINodeInfo.h \ nsINodeList.h \ nsIScriptElement.h \ nsIStyleSheetLinkingElement.h \ nsIContentSerializer.h \ -nsIHTMLToTextSink.h \ nsIXPathEvaluatorInternal.h \ mozISanitizingSerializer.h \ nsCaseTreatment.h \ nsContentCID.h \ nsCopySupport.h \ nsContentCreatorFunctions.h \ nsDOMFile.h \ nsLineBreaker.h \
--- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -437,16 +437,26 @@ public: * We define whitespace using the list in HTML5 and css3-selectors: * U+0009, U+000A, U+000C, U+000D, U+0020 * * HTML 4.01 also lists U+200B (zero-width space). */ static bool IsHTMLWhitespace(PRUnichar aChar); /** + * Is the HTML local name a block element? + */ + static bool IsHTMLBlock(nsIAtom* aLocalName); + + /** + * Is the HTML local name a void element? + */ + static bool IsHTMLVoid(nsIAtom* aLocalName); + + /** * Parse a margin string of format 'top, right, bottom, left' into * an nsIntMargin. * * @param aString the string to parse * @param aResult the resulting integer * @return whether the value could be parsed */ static bool ParseIntMarginValue(const nsAString& aString, nsIntMargin& aResult); @@ -833,21 +843,32 @@ public: static nsresult GetLocalizedString(PropertiesFile aFile, const char* aKey, nsXPIDLString& aResult); /** * Fill (with the parameters given) the localized string named |aKey| in * properties file |aFile|. */ +private: static nsresult FormatLocalizedString(PropertiesFile aFile, const char* aKey, - const PRUnichar **aParams, + const PRUnichar** aParams, PRUint32 aParamsLength, nsXPIDLString& aResult); + +public: + template<PRUint32 N> + static nsresult FormatLocalizedString(PropertiesFile aFile, + const char* aKey, + const PRUnichar* (&aParams)[N], + nsXPIDLString& aResult) + { + return FormatLocalizedString(aFile, aKey, aParams, N, aResult); + } /** * Returns true if aDocument is a chrome document */ static bool IsChromeDoc(nsIDocument *aDocument); /** * Returns true if aDocument is in a docshell whose parent is the same type
deleted file mode 100644 --- a/content/base/public/nsIHTMLToTextSink.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef _nsIPlainTextSink_h__ -#define _nsIPlainTextSink_h__ - -#include "nsISupports.h" -#include "nsStringGlue.h" - -#define NS_PLAINTEXTSINK_CONTRACTID "@mozilla.org/layout/plaintextsink;1" - -/* starting interface: nsIContentSerializer */ -#define NS_IHTMLTOTEXTSINK_IID_STR "b12b5643-07cb-401e-aabb-64b2dcd2717f" - -#define NS_IHTMLTOTEXTSINK_IID \ - {0xb12b5643, 0x07cb, 0x401e, \ - { 0xaa, 0xbb, 0x64, 0xb2, 0xdc, 0xd2, 0x71, 0x7f }} - - -class nsIHTMLToTextSink : public nsISupports { - public: - - NS_DECLARE_STATIC_IID_ACCESSOR(NS_IHTMLTOTEXTSINK_IID) - - NS_IMETHOD Initialize(nsAString* aOutString, - PRUint32 aFlags, PRUint32 aWrapCol) = 0; - // This function violates string ownership rules, see impl. -}; - -NS_DEFINE_STATIC_IID_ACCESSOR(nsIHTMLToTextSink, NS_IHTMLTOTEXTSINK_IID) - -#endif
--- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -1187,16 +1187,82 @@ nsContentUtils::IsHTMLWhitespace(PRUnich aChar == PRUnichar(0x000A) || aChar == PRUnichar(0x000C) || aChar == PRUnichar(0x000D) || aChar == PRUnichar(0x0020); } /* static */ bool +nsContentUtils::IsHTMLBlock(nsIAtom* aLocalName) +{ + return + (aLocalName == nsGkAtoms::address) || + (aLocalName == nsGkAtoms::article) || + (aLocalName == nsGkAtoms::aside) || + (aLocalName == nsGkAtoms::blockquote) || + (aLocalName == nsGkAtoms::center) || + (aLocalName == nsGkAtoms::dir) || + (aLocalName == nsGkAtoms::div) || + (aLocalName == nsGkAtoms::dl) || // XXX why not dt and dd? + (aLocalName == nsGkAtoms::fieldset) || + (aLocalName == nsGkAtoms::figure) || // XXX shouldn't figcaption be on this list + (aLocalName == nsGkAtoms::footer) || + (aLocalName == nsGkAtoms::form) || + (aLocalName == nsGkAtoms::h1) || + (aLocalName == nsGkAtoms::h2) || + (aLocalName == nsGkAtoms::h3) || + (aLocalName == nsGkAtoms::h4) || + (aLocalName == nsGkAtoms::h5) || + (aLocalName == nsGkAtoms::h6) || + (aLocalName == nsGkAtoms::header) || + (aLocalName == nsGkAtoms::hgroup) || + (aLocalName == nsGkAtoms::hr) || + (aLocalName == nsGkAtoms::li) || + (aLocalName == nsGkAtoms::listing) || + (aLocalName == nsGkAtoms::menu) || + (aLocalName == nsGkAtoms::multicol) || // XXX get rid of this one? + (aLocalName == nsGkAtoms::nav) || + (aLocalName == nsGkAtoms::ol) || + (aLocalName == nsGkAtoms::p) || + (aLocalName == nsGkAtoms::pre) || + (aLocalName == nsGkAtoms::section) || + (aLocalName == nsGkAtoms::table) || + (aLocalName == nsGkAtoms::ul) || + (aLocalName == nsGkAtoms::xmp); +} + +/* static */ +bool +nsContentUtils::IsHTMLVoid(nsIAtom* aLocalName) +{ + return + (aLocalName == nsGkAtoms::area) || + (aLocalName == nsGkAtoms::base) || + (aLocalName == nsGkAtoms::basefont) || + (aLocalName == nsGkAtoms::bgsound) || + (aLocalName == nsGkAtoms::br) || + (aLocalName == nsGkAtoms::col) || + (aLocalName == nsGkAtoms::command) || + (aLocalName == nsGkAtoms::embed) || + (aLocalName == nsGkAtoms::frame) || + (aLocalName == nsGkAtoms::hr) || + (aLocalName == nsGkAtoms::img) || + (aLocalName == nsGkAtoms::input) || + (aLocalName == nsGkAtoms::keygen) || + (aLocalName == nsGkAtoms::link) || + (aLocalName == nsGkAtoms::meta) || + (aLocalName == nsGkAtoms::param) || + (aLocalName == nsGkAtoms::source) || + (aLocalName == nsGkAtoms::track) || + (aLocalName == nsGkAtoms::wbr); +} + +/* static */ +bool nsContentUtils::ParseIntMarginValue(const nsAString& aString, nsIntMargin& result) { nsAutoString marginStr(aString); marginStr.CompressWhitespace(true, true); if (marginStr.IsEmpty()) { return false; }
--- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -2731,16 +2731,19 @@ nsGenericElement::SetAttribute(const nsA } nsresult nsGenericElement::RemoveAttribute(const nsAString& aName) { const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); if (!name) { + // If there is no canonical nsAttrName for this attribute name, then the + // attribute does not exist and we can't get its namespace ID and + // local name below, so we return early. return NS_OK; } // Hold a strong reference here so that the atom or nodeinfo doesn't go // away during UnsetAttr. If it did UnsetAttr would be left with a // dangling pointer as argument without knowing it. nsAttrName tmp(*name); @@ -2884,18 +2887,19 @@ nsresult nsGenericElement::RemoveAttributeNS(const nsAString& aNamespaceURI, const nsAString& aLocalName) { nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName); PRInt32 nsid = nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI); if (nsid == kNameSpaceID_Unknown) { - // Unknown namespace means no attr... - + // If the namespace ID is unknown, it means there can't possibly be an + // existing attribute. We would need a known namespace ID to pass into + // UnsetAttr, so we return early if we don't have one. return NS_OK; } UnsetAttr(nsid, name, true); return NS_OK; }
--- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -137,16 +137,17 @@ GK_ATOM(basefont, "basefont") GK_ATOM(baseline, "baseline") GK_ATOM(bdi, "bdi") GK_ATOM(bdo, "bdo") GK_ATOM(before, "before") GK_ATOM(before_end, "before_end") GK_ATOM(before_start, "before_start") GK_ATOM(below, "below") GK_ATOM(bgcolor, "bgcolor") +GK_ATOM(bgsound, "bgsound") GK_ATOM(big, "big") GK_ATOM(binding, "binding") GK_ATOM(bindings, "bindings") GK_ATOM(blankrow, "blankrow") GK_ATOM(block, "block") GK_ATOM(blockquote, "blockquote") GK_ATOM(blur, "blur") GK_ATOM(body, "body") @@ -488,16 +489,17 @@ GK_ATOM(itemref, "itemref") GK_ATOM(itemscope, "itemscope") GK_ATOM(itemtype, "itemtype") GK_ATOM(kbd, "kbd") GK_ATOM(noautofocus, "noautofocus") GK_ATOM(keepcurrentinview, "keepcurrentinview") GK_ATOM(key, "key") GK_ATOM(keycode, "keycode") GK_ATOM(keydown, "keydown") +GK_ATOM(keygen, "keygen") GK_ATOM(keypress, "keypress") GK_ATOM(keyset, "keyset") GK_ATOM(keytext, "keytext") GK_ATOM(keyup, "keyup") GK_ATOM(kind, "kind") GK_ATOM(label, "label") GK_ATOM(lang, "lang") GK_ATOM(language, "language")
--- a/content/base/src/nsGkAtoms.cpp +++ b/content/base/src/nsGkAtoms.cpp @@ -60,11 +60,11 @@ using namespace mozilla; static const nsStaticAtom GkAtoms_info[] = { #define GK_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &nsGkAtoms::name_), #include "nsGkAtomList.h" #undef GK_ATOM }; void nsGkAtoms::AddRefAtoms() { - NS_RegisterStaticAtoms(GkAtoms_info, ArrayLength(GkAtoms_info)); + NS_RegisterStaticAtoms(GkAtoms_info); }
--- a/content/base/src/nsPlainTextSerializer.cpp +++ b/content/base/src/nsPlainTextSerializer.cpp @@ -48,17 +48,16 @@ #include "nsIServiceManager.h" #include "nsGkAtoms.h" #include "nsINameSpaceManager.h" #include "nsTextFragment.h" #include "nsContentUtils.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "nsCRT.h" -#include "nsIParserService.h" #include "mozilla/dom/Element.h" #include "mozilla/Preferences.h" using namespace mozilla; using namespace mozilla::dom; #define PREF_STRUCTS "converter.html2txt.structs" #define PREF_HEADER_STRATEGY "converter.html2txt.header_strategy" @@ -74,17 +73,17 @@ static const PRInt32 kIndentIncrementHe indent h(x+1) this many columns more than h(x) */ static const PRInt32 kIndentSizeList = kTabSize; // Indention of non-first lines of ul and ol static const PRInt32 kIndentSizeDD = kTabSize; // Indention of <dd> static const PRUnichar kNBSP = 160; static const PRUnichar kSPACE = ' '; -static PRInt32 HeaderLevel(eHTMLTags aTag); +static PRInt32 HeaderLevel(nsIAtom* aTag); static PRInt32 GetUnicharWidth(PRUnichar ucs); static PRInt32 GetUnicharStringWidth(const PRUnichar* pwcs, PRInt32 n); // Someday may want to make this non-const: static const PRUint32 TagStackSize = 500; static const PRUint32 OLStackSize = 100; nsresult NS_NewPlainTextSerializer(nsIContentSerializer** aSerializer) @@ -121,17 +120,19 @@ nsPlainTextSerializer::nsPlainTextSerial // Flow mEmptyLines = 1; // The start of the document is an "empty line" in itself, mInWhitespace = false; mPreFormatted = false; mStartedOutput = false; // initialize the tag stack to zero: - mTagStack = new nsHTMLTag[TagStackSize]; + // The stack only ever contains pointers to static atoms, so they don't + // need refcounting. + mTagStack = new nsIAtom*[TagStackSize]; mTagStackIndex = 0; mIgnoreAboveIndex = (PRUint32)kNotFound; // initialize the OL stack, where numbers for ordered lists are kept mOLStack = new PRInt32[OLStackSize]; mOLStackIndex = 0; mULCount = 0; @@ -139,44 +140,39 @@ nsPlainTextSerializer::nsPlainTextSerial nsPlainTextSerializer::~nsPlainTextSerializer() { delete[] mTagStack; delete[] mOLStack; NS_WARN_IF_FALSE(mHeadLevel == 0, "Wrong head level!"); } -NS_IMPL_ISUPPORTS4(nsPlainTextSerializer, - nsIContentSerializer, - nsIContentSink, - nsIHTMLContentSink, - nsIHTMLToTextSink) +NS_IMPL_ISUPPORTS1(nsPlainTextSerializer, + nsIContentSerializer) NS_IMETHODIMP nsPlainTextSerializer::Init(PRUint32 aFlags, PRUint32 aWrapColumn, const char* aCharSet, bool aIsCopying, bool aIsWholeDocument) { #ifdef DEBUG // Check if the major control flags are set correctly. - if(aFlags & nsIDocumentEncoder::OutputFormatFlowed) { + if (aFlags & nsIDocumentEncoder::OutputFormatFlowed) { NS_ASSERTION(aFlags & nsIDocumentEncoder::OutputFormatted, "If you want format=flowed, you must combine it with " "nsIDocumentEncoder::OutputFormatted"); } - if(aFlags & nsIDocumentEncoder::OutputFormatted) { + if (aFlags & nsIDocumentEncoder::OutputFormatted) { NS_ASSERTION(!(aFlags & nsIDocumentEncoder::OutputPreformatted), "Can't do formatted and preformatted output at the same time!"); } #endif - NS_ENSURE_TRUE(nsContentUtils::GetParserService(), NS_ERROR_UNEXPECTED); - mFlags = aFlags; mWrapColumn = aWrapColumn; // Only create a linebreaker if we will handle wrapping. if (MayWrap()) { mLineBreaker = nsContentUtils::LineBreaker(); } @@ -265,31 +261,16 @@ nsPlainTextSerializer::PopBool(nsTArray< PRUint32 size = aStack.Length(); if (size > 0) { returnValue = aStack.ElementAt(size-1); aStack.RemoveElementAt(size-1); } return returnValue; } -NS_IMETHODIMP -nsPlainTextSerializer::Initialize(nsAString* aOutString, - PRUint32 aFlags, PRUint32 aWrapCol) -{ - nsresult rv = Init(aFlags, aWrapCol, nsnull, false, false); - NS_ENSURE_SUCCESS(rv, rv); - - // XXX This is wrong. It violates XPCOM string ownership rules. - // We're only getting away with this because instances of this - // class are restricted to single function scope. - mOutputString = aOutString; - - return NS_OK; -} - NS_IMETHODIMP nsPlainTextSerializer::AppendText(nsIContent* aText, PRInt32 aStartOffset, PRInt32 aEndOffset, nsAString& aStr) { if (mIgnoreAboveIndex != (PRUint32)kNotFound) { return NS_OK; @@ -330,41 +311,36 @@ nsPlainTextSerializer::AppendText(nsICon mOutputString = &aStr; // We have to split the string across newlines // to match parser behavior PRInt32 start = 0; PRInt32 offset = textstr.FindCharInSet("\n\r"); while (offset != kNotFound) { - if(offset>start) { + if (offset>start) { // Pass in the line - rv = DoAddLeaf(nsnull, - eHTMLTag_text, - Substring(textstr, start, offset-start)); - if (NS_FAILED(rv)) break; + DoAddText(false, + Substring(textstr, start, offset-start)); } // Pass in a newline - rv = DoAddLeaf(nsnull, eHTMLTag_newline, mLineBreak); - if (NS_FAILED(rv)) break; + DoAddText(true, mLineBreak); start = offset+1; offset = textstr.FindCharInSet("\n\r", start); } // Consume the last bit of the string if there's any left - if (NS_SUCCEEDED(rv) && start < length) { + if (start < length) { if (start) { - rv = DoAddLeaf(nsnull, - eHTMLTag_text, - Substring(textstr, start, length-start)); + DoAddText(false, Substring(textstr, start, length - start)); } else { - rv = DoAddLeaf(nsnull, eHTMLTag_text, textstr); + DoAddText(false, textstr); } } mOutputString = nsnull; return rv; } @@ -382,63 +358,63 @@ nsPlainTextSerializer::AppendElementStar Element* aOriginalElement, nsAString& aStr) { NS_ENSURE_ARG(aElement); mElement = aElement; nsresult rv; - PRInt32 id = GetIdForContent(mElement); + nsIAtom* id = GetIdForContent(mElement); - bool isContainer = IsContainer(id); + bool isContainer = !nsContentUtils::IsHTMLVoid(id); mOutputString = &aStr; if (isContainer) { - rv = DoOpenContainer(nsnull, id); + rv = DoOpenContainer(id); } else { - rv = DoAddLeaf(nsnull, id, EmptyString()); + rv = DoAddLeaf(id); } mElement = nsnull; mOutputString = nsnull; - if (id == eHTMLTag_head) { + if (id == nsGkAtoms::head) { ++mHeadLevel; } return rv; } NS_IMETHODIMP nsPlainTextSerializer::AppendElementEnd(Element* aElement, nsAString& aStr) { NS_ENSURE_ARG(aElement); mElement = aElement; nsresult rv; - PRInt32 id = GetIdForContent(mElement); + nsIAtom* id = GetIdForContent(mElement); - bool isContainer = IsContainer(id); + bool isContainer = !nsContentUtils::IsHTMLVoid(id); mOutputString = &aStr; rv = NS_OK; if (isContainer) { rv = DoCloseContainer(id); } mElement = nsnull; mOutputString = nsnull; - if (id == eHTMLTag_head) { + if (id == nsGkAtoms::head) { --mHeadLevel; NS_ASSERTION(mHeadLevel >= 0, "mHeadLevel < 0"); } return rv; } NS_IMETHODIMP @@ -452,159 +428,77 @@ nsPlainTextSerializer::Flush(nsAString& NS_IMETHODIMP nsPlainTextSerializer::AppendDocumentStart(nsIDocument *aDocument, nsAString& aStr) { return NS_OK; } -NS_IMETHODIMP -nsPlainTextSerializer::OpenContainer(const nsIParserNode& aNode) -{ - PRInt32 type = aNode.GetNodeType(); - - if (type == eHTMLTag_head) { - ++mHeadLevel; - return NS_OK; - } - - return DoOpenContainer(&aNode, type); -} - -NS_IMETHODIMP -nsPlainTextSerializer::CloseContainer(const nsHTMLTag aTag) -{ - if (aTag == eHTMLTag_head) { - --mHeadLevel; - NS_ASSERTION(mHeadLevel >= 0, "mHeadLevel < 0"); - return NS_OK; - } - - return DoCloseContainer(aTag); -} - -NS_IMETHODIMP -nsPlainTextSerializer::AddLeaf(const nsIParserNode& aNode) -{ - if (mIgnoreAboveIndex != (PRUint32)kNotFound) { - return NS_OK; - } - - eHTMLTags type = (eHTMLTags)aNode.GetNodeType(); - const nsAString& text = aNode.GetText(); - - if ((type == eHTMLTag_text) || - (type == eHTMLTag_whitespace) || - (type == eHTMLTag_newline)) { - // Copy the text out, stripping out CRs - nsAutoString str; - PRUint32 length; - str.SetCapacity(text.Length()); - nsReadingIterator<PRUnichar> srcStart, srcEnd; - length = nsContentUtils::CopyNewlineNormalizedUnicodeTo(text.BeginReading(srcStart), text.EndReading(srcEnd), str); - str.SetLength(length); - return DoAddLeaf(&aNode, type, str); - } - else { - return DoAddLeaf(&aNode, type, text); - } -} - -NS_IMETHODIMP -nsPlainTextSerializer::OpenHead() -{ - ++mHeadLevel; - return NS_OK; -} - -NS_IMETHODIMP -nsPlainTextSerializer::IsEnabled(PRInt32 aTag, bool* aReturn) -{ - nsHTMLTag theHTMLTag = nsHTMLTag(aTag); - - if (theHTMLTag == eHTMLTag_script) { - *aReturn = !(mFlags & nsIDocumentEncoder::OutputNoScriptContent); - } - else if (theHTMLTag == eHTMLTag_frameset) { - *aReturn = !(mFlags & nsIDocumentEncoder::OutputNoFramesContent); - } - else { - *aReturn = false; - } - - return NS_OK; -} - -/** - * aNode may be null when we're working with the DOM, but then mElement is - * useable instead. - */ nsresult -nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag) +nsPlainTextSerializer::DoOpenContainer(nsIAtom* aTag) { if (mFlags & nsIDocumentEncoder::OutputRaw) { // Raw means raw. Don't even think about doing anything fancy // here like indenting, adding line breaks or any other // characters such as list item bullets, quote characters // around <q>, etc. I mean it! Don't make me smack you! return NS_OK; } - eHTMLTags type = (eHTMLTags)aTag; - if (mTagStackIndex < TagStackSize) { - mTagStack[mTagStackIndex++] = type; + mTagStack[mTagStackIndex++] = aTag; } if (mIgnoreAboveIndex != (PRUint32)kNotFound) { return NS_OK; } // Reset this so that <blockquote type=cite> doesn't affect the whitespace // above random <pre>s below it. - mHasWrittenCiteBlockquote = mHasWrittenCiteBlockquote && aTag == eHTMLTag_pre; + mHasWrittenCiteBlockquote = mHasWrittenCiteBlockquote && + aTag == nsGkAtoms::pre; bool isInCiteBlockquote = false; // XXX special-case <blockquote type=cite> so that we don't add additional // newlines before the text. - if (aTag == eHTMLTag_blockquote) { + if (aTag == nsGkAtoms::blockquote) { nsAutoString value; - nsresult rv = GetAttributeValue(aNode, nsGkAtoms::type, value); + nsresult rv = GetAttributeValue(nsGkAtoms::type, value); isInCiteBlockquote = NS_SUCCEEDED(rv) && value.EqualsIgnoreCase("cite"); } if (mLineBreakDue && !isInCiteBlockquote) EnsureVerticalSpace(mFloatingLines); // Check if this tag's content that should not be output - if ((type == eHTMLTag_noscript && + if ((aTag == nsGkAtoms::noscript && !(mFlags & nsIDocumentEncoder::OutputNoScriptContent)) || - ((type == eHTMLTag_iframe || type == eHTMLTag_noframes) && + ((aTag == nsGkAtoms::iframe || aTag == nsGkAtoms::noframes) && !(mFlags & nsIDocumentEncoder::OutputNoFramesContent))) { // Ignore everything that follows the current tag in // question until a matching end tag is encountered. mIgnoreAboveIndex = mTagStackIndex - 1; return NS_OK; } - if (type == eHTMLTag_body) { + if (aTag == nsGkAtoms::body) { // Try to figure out here whether we have a // preformatted style attribute. // // Trigger on the presence of a "pre-wrap" in the // style attribute. That's a very simplistic way to do // it, but better than nothing. // Also set mWrapColumn to the value given there // (which arguably we should only do if told to do so). nsAutoString style; PRInt32 whitespace; - if(NS_SUCCEEDED(GetAttributeValue(aNode, nsGkAtoms::style, style)) && + if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::style, style)) && (kNotFound != (whitespace = style.Find("white-space:")))) { if (kNotFound != style.Find("pre-wrap", true, whitespace)) { #ifdef DEBUG_preformatted printf("Set mPreFormatted based on style pre-wrap\n"); #endif mPreFormatted = true; PRInt32 widthOffset = style.Find("width:"); @@ -647,32 +541,32 @@ nsPlainTextSerializer::DoOpenContainer(c return NS_OK; } // Keep this in sync with DoCloseContainer! if (!DoOutput()) { return NS_OK; } - if (type == eHTMLTag_p) + if (aTag == nsGkAtoms::p) EnsureVerticalSpace(1); - else if (type == eHTMLTag_pre) { + else if (aTag == nsGkAtoms::pre) { if (GetLastBool(mIsInCiteBlockquote)) EnsureVerticalSpace(0); else if (mHasWrittenCiteBlockquote) { EnsureVerticalSpace(0); mHasWrittenCiteBlockquote = false; } else EnsureVerticalSpace(1); } - else if (type == eHTMLTag_tr) { + else if (aTag == nsGkAtoms::tr) { PushBool(mHasWrittenCellsForRow, false); } - else if (type == eHTMLTag_td || type == eHTMLTag_th) { + else if (aTag == nsGkAtoms::td || aTag == nsGkAtoms::th) { // We must make sure that the content of two table cells get a // space between them. // To make the separation between cells most obvious and // importable, we use a TAB. if (GetLastBool(mHasWrittenCellsForRow)) { // Bypass |Write| so that the TAB isn't compressed away. AddToLine(NS_LITERAL_STRING("\t").get(), 1); @@ -682,49 +576,49 @@ nsPlainTextSerializer::DoOpenContainer(c // We don't always see a <tr> (nor a <table>) before the <td> if we're // copying part of a table PushBool(mHasWrittenCellsForRow, true); // will never be popped } else { SetLastBool(mHasWrittenCellsForRow, true); } } - else if (type == eHTMLTag_ul) { + else if (aTag == nsGkAtoms::ul) { // Indent here to support nested lists, which aren't included in li :-( EnsureVerticalSpace(mULCount + mOLStackIndex == 0 ? 1 : 0); // Must end the current line before we change indention mIndent += kIndentSizeList; mULCount++; } - else if (type == eHTMLTag_ol) { + else if (aTag == nsGkAtoms::ol) { EnsureVerticalSpace(mULCount + mOLStackIndex == 0 ? 1 : 0); if (mFlags & nsIDocumentEncoder::OutputFormatted) { // Must end the current line before we change indention if (mOLStackIndex < OLStackSize) { nsAutoString startAttr; PRInt32 startVal = 1; - if(NS_SUCCEEDED(GetAttributeValue(aNode, nsGkAtoms::start, startAttr))){ + if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::start, startAttr))) { PRInt32 rv = 0; startVal = startAttr.ToInteger(&rv); if (NS_FAILED(rv)) startVal = 1; } mOLStack[mOLStackIndex++] = startVal; } } else { mOLStackIndex++; } mIndent += kIndentSizeList; // see ul } - else if (type == eHTMLTag_li && + else if (aTag == nsGkAtoms::li && (mFlags & nsIDocumentEncoder::OutputFormatted)) { if (mTagStackIndex > 1 && IsInOL()) { if (mOLStackIndex > 0) { nsAutoString valueAttr; - if(NS_SUCCEEDED(GetAttributeValue(aNode, nsGkAtoms::value, valueAttr))){ + if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::value, valueAttr))) { PRInt32 rv = 0; PRInt32 valueAttrVal = valueAttr.ToInteger(&rv); if (NS_SUCCEEDED(rv)) mOLStack[mOLStackIndex-1] = valueAttrVal; } // This is what nsBulletFrame does for OLs: mInIndentString.AppendInt(mOLStack[mOLStackIndex-1]++, 10); } @@ -739,73 +633,72 @@ nsPlainTextSerializer::DoOpenContainer(c static char bulletCharArray[] = "*o+#"; PRUint32 index = mULCount > 0 ? (mULCount - 1) : 3; char bulletChar = bulletCharArray[index % 4]; mInIndentString.Append(PRUnichar(bulletChar)); } mInIndentString.Append(PRUnichar(' ')); } - else if (type == eHTMLTag_dl) { + else if (aTag == nsGkAtoms::dl) { EnsureVerticalSpace(1); } - else if (type == eHTMLTag_dt) { + else if (aTag == nsGkAtoms::dt) { EnsureVerticalSpace(0); } - else if (type == eHTMLTag_dd) { + else if (aTag == nsGkAtoms::dd) { EnsureVerticalSpace(0); mIndent += kIndentSizeDD; } - else if (type == eHTMLTag_span) { + else if (aTag == nsGkAtoms::span) { ++mSpanLevel; } - else if (type == eHTMLTag_blockquote) { + else if (aTag == nsGkAtoms::blockquote) { // Push PushBool(mIsInCiteBlockquote, isInCiteBlockquote); if (isInCiteBlockquote) { EnsureVerticalSpace(0); mCiteQuoteLevel++; } else { EnsureVerticalSpace(1); mIndent += kTabSize; // Check for some maximum value? } } - else if (type == eHTMLTag_q) { + else if (aTag == nsGkAtoms::q) { Write(NS_LITERAL_STRING("\"")); } // Else make sure we'll separate block level tags, // even if we're about to leave, before doing any other formatting. - else if (IsBlockLevel(aTag)) { + else if (nsContentUtils::IsHTMLBlock(aTag)) { EnsureVerticalSpace(0); } ////////////////////////////////////////////////////////////// if (!(mFlags & nsIDocumentEncoder::OutputFormatted)) { return NS_OK; } ////////////////////////////////////////////////////////////// // The rest of this routine is formatted output stuff, // which we should skip if we're not formatted: ////////////////////////////////////////////////////////////// // Push on stack - bool currentNodeIsConverted = IsCurrentNodeConverted(aNode); - PushBool(mCurrentNodeIsConverted, currentNodeIsConverted); + bool currentNodeIsConverted = IsCurrentNodeConverted(); - if (type == eHTMLTag_h1 || type == eHTMLTag_h2 || - type == eHTMLTag_h3 || type == eHTMLTag_h4 || - type == eHTMLTag_h5 || type == eHTMLTag_h6) + if (aTag == nsGkAtoms::h1 || aTag == nsGkAtoms::h2 || + aTag == nsGkAtoms::h3 || aTag == nsGkAtoms::h4 || + aTag == nsGkAtoms::h5 || aTag == nsGkAtoms::h6) { EnsureVerticalSpace(2); if (mHeaderStrategy == 2) { // numbered mIndent += kIndentSizeHeaders; // Caching - PRInt32 level = HeaderLevel(type); + PRInt32 level = HeaderLevel(aTag); // Increase counter for current level mHeaderCounter[level]++; // Reset all lower levels PRInt32 i; for (i = level + 1; i <= 6; i++) { mHeaderCounter[i] = 0; } @@ -816,62 +709,62 @@ nsPlainTextSerializer::DoOpenContainer(c leadup.AppendInt(mHeaderCounter[i]); leadup.Append(PRUnichar('.')); } leadup.Append(PRUnichar(' ')); Write(leadup); } else if (mHeaderStrategy == 1) { // indent increasingly mIndent += kIndentSizeHeaders; - for (PRInt32 i = HeaderLevel(type); i > 1; i--) { + for (PRInt32 i = HeaderLevel(aTag); i > 1; i--) { // for h(x), run x-1 times mIndent += kIndentIncrementHeaders; } } } - else if (type == eHTMLTag_a && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::a && !currentNodeIsConverted) { nsAutoString url; - if (NS_SUCCEEDED(GetAttributeValue(aNode, nsGkAtoms::href, url)) + if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::href, url)) && !url.IsEmpty()) { mURL = url; } } - else if (type == eHTMLTag_sup && mStructs && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::sup && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("^")); } - else if (type == eHTMLTag_sub && mStructs && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::sub && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("_")); } - else if (type == eHTMLTag_code && mStructs && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::code && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("|")); } - else if ((type == eHTMLTag_strong || type == eHTMLTag_b) + else if ((aTag == nsGkAtoms::strong || aTag == nsGkAtoms::b) && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("*")); } - else if ((type == eHTMLTag_em || type == eHTMLTag_i) + else if ((aTag == nsGkAtoms::em || aTag == nsGkAtoms::i) && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("/")); } - else if (type == eHTMLTag_u && mStructs && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::u && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("_")); } /* Container elements are always block elements, so we shouldn't output any whitespace immediately after the container tag even if there's extra whitespace there because the HTML is pretty-printed or something. To ensure that happens, tell the serializer we're already in whitespace so it won't output more. */ mInWhitespace = true; return NS_OK; } nsresult -nsPlainTextSerializer::DoCloseContainer(PRInt32 aTag) +nsPlainTextSerializer::DoCloseContainer(nsIAtom* aTag) { if (mFlags & nsIDocumentEncoder::OutputRaw) { // Raw means raw. Don't even think about doing anything fancy // here like indenting, adding line breaks or any other // characters such as list item bullets, quote characters // around <q>, etc. I mean it! Don't make me smack you! return NS_OK; @@ -886,19 +779,18 @@ nsPlainTextSerializer::DoCloseContainer( // We're dealing with the close tag whose matching // open tag had set the mIgnoreAboveIndex value. // Reset mIgnoreAboveIndex before discarding this tag. mIgnoreAboveIndex = (PRUint32)kNotFound; } return NS_OK; } - eHTMLTags type = (eHTMLTags)aTag; // End current line if we're ending a block level tag - if((type == eHTMLTag_body) || (type == eHTMLTag_html)) { + if ((aTag == nsGkAtoms::body) || (aTag == nsGkAtoms::html)) { // We want the output to end with a new line, // but in preformatted areas like text fields, // we can't emit newlines that weren't there. // So add the newline only in the case of formatted output. if (mFlags & nsIDocumentEncoder::OutputFormatted) { EnsureVerticalSpace(0); } else { @@ -909,71 +801,71 @@ nsPlainTextSerializer::DoCloseContainer( return NS_OK; } // Keep this in sync with DoOpenContainer! if (!DoOutput()) { return NS_OK; } - if (type == eHTMLTag_tr) { + if (aTag == nsGkAtoms::tr) { PopBool(mHasWrittenCellsForRow); // Should always end a line, but get no more whitespace if (mFloatingLines < 0) mFloatingLines = 0; mLineBreakDue = true; } - else if (((type == eHTMLTag_li) || - (type == eHTMLTag_dt)) && + else if (((aTag == nsGkAtoms::li) || + (aTag == nsGkAtoms::dt)) && (mFlags & nsIDocumentEncoder::OutputFormatted)) { // Items that should always end a line, but get no more whitespace if (mFloatingLines < 0) mFloatingLines = 0; mLineBreakDue = true; } - else if (type == eHTMLTag_pre) { + else if (aTag == nsGkAtoms::pre) { mFloatingLines = GetLastBool(mIsInCiteBlockquote) ? 0 : 1; mLineBreakDue = true; } - else if (type == eHTMLTag_ul) { + else if (aTag == nsGkAtoms::ul) { FlushLine(); mIndent -= kIndentSizeList; if (--mULCount + mOLStackIndex == 0) { mFloatingLines = 1; mLineBreakDue = true; } } - else if (type == eHTMLTag_ol) { + else if (aTag == nsGkAtoms::ol) { FlushLine(); // Doing this after decreasing OLStackIndex would be wrong. mIndent -= kIndentSizeList; NS_ASSERTION(mOLStackIndex, "Wrong OLStack level!"); mOLStackIndex--; if (mULCount + mOLStackIndex == 0) { mFloatingLines = 1; mLineBreakDue = true; } } - else if (type == eHTMLTag_dl) { + else if (aTag == nsGkAtoms::dl) { mFloatingLines = 1; mLineBreakDue = true; } - else if (type == eHTMLTag_dd) { + else if (aTag == nsGkAtoms::dd) { FlushLine(); mIndent -= kIndentSizeDD; } - else if (type == eHTMLTag_span) { + else if (aTag == nsGkAtoms::span) { NS_ASSERTION(mSpanLevel, "Span level will be negative!"); --mSpanLevel; } - else if (type == eHTMLTag_div) { + else if (aTag == nsGkAtoms::div) { if (mFloatingLines < 0) mFloatingLines = 0; mLineBreakDue = true; } - else if (type == eHTMLTag_blockquote) { + else if (aTag == nsGkAtoms::blockquote) { FlushLine(); // Is this needed? // Pop bool isInCiteBlockquote = PopBool(mIsInCiteBlockquote); if (isInCiteBlockquote) { NS_ASSERTION(mCiteQuoteLevel, "CiteQuote level will be negative!"); mCiteQuoteLevel--; @@ -981,23 +873,21 @@ nsPlainTextSerializer::DoCloseContainer( mHasWrittenCiteBlockquote = true; } else { mIndent -= kTabSize; mFloatingLines = 1; } mLineBreakDue = true; } - else if (type == eHTMLTag_q) { + else if (aTag == nsGkAtoms::q) { Write(NS_LITERAL_STRING("\"")); } - else if (IsBlockLevel(aTag) - && type != eHTMLTag_script - && type != eHTMLTag_doctypeDecl - && type != eHTMLTag_markupDecl) { + else if (nsContentUtils::IsHTMLBlock(aTag) + && aTag != nsGkAtoms::script) { // All other blocks get 1 vertical space after them // in formatted mode, otherwise 0. // This is hard. Sometimes 0 is a better number, but // how to know? if (mFlags & nsIDocumentEncoder::OutputFormatted) EnsureVerticalSpace(1); else { if (mFloatingLines < 0) @@ -1011,211 +901,210 @@ nsPlainTextSerializer::DoCloseContainer( return NS_OK; } ////////////////////////////////////////////////////////////// // The rest of this routine is formatted output stuff, // which we should skip if we're not formatted: ////////////////////////////////////////////////////////////// // Pop the currentConverted stack - bool currentNodeIsConverted = PopBool(mCurrentNodeIsConverted); + bool currentNodeIsConverted = IsCurrentNodeConverted(); - if (type == eHTMLTag_h1 || type == eHTMLTag_h2 || - type == eHTMLTag_h3 || type == eHTMLTag_h4 || - type == eHTMLTag_h5 || type == eHTMLTag_h6) { + if (aTag == nsGkAtoms::h1 || aTag == nsGkAtoms::h2 || + aTag == nsGkAtoms::h3 || aTag == nsGkAtoms::h4 || + aTag == nsGkAtoms::h5 || aTag == nsGkAtoms::h6) { if (mHeaderStrategy) { /*numbered or indent increasingly*/ mIndent -= kIndentSizeHeaders; } if (mHeaderStrategy == 1 /*indent increasingly*/ ) { - for (PRInt32 i = HeaderLevel(type); i > 1; i--) { + for (PRInt32 i = HeaderLevel(aTag); i > 1; i--) { // for h(x), run x-1 times mIndent -= kIndentIncrementHeaders; } } EnsureVerticalSpace(1); } - else if (type == eHTMLTag_a && !currentNodeIsConverted && !mURL.IsEmpty()) { + else if (aTag == nsGkAtoms::a && !currentNodeIsConverted && !mURL.IsEmpty()) { nsAutoString temp; temp.AssignLiteral(" <"); temp += mURL; temp.Append(PRUnichar('>')); Write(temp); mURL.Truncate(); } - else if ((type == eHTMLTag_sup || type == eHTMLTag_sub) + else if ((aTag == nsGkAtoms::sup || aTag == nsGkAtoms::sub) && mStructs && !currentNodeIsConverted) { Write(kSpace); } - else if (type == eHTMLTag_code && mStructs && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::code && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("|")); } - else if ((type == eHTMLTag_strong || type == eHTMLTag_b) + else if ((aTag == nsGkAtoms::strong || aTag == nsGkAtoms::b) && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("*")); } - else if ((type == eHTMLTag_em || type == eHTMLTag_i) + else if ((aTag == nsGkAtoms::em || aTag == nsGkAtoms::i) && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("/")); } - else if (type == eHTMLTag_u && mStructs && !currentNodeIsConverted) { + else if (aTag == nsGkAtoms::u && mStructs && !currentNodeIsConverted) { Write(NS_LITERAL_STRING("_")); } return NS_OK; } -/** - * aNode may be null when we're working with the DOM, but then mElement is - * useable instead. - */ +bool +nsPlainTextSerializer::MustSuppressLeaf() +{ + if ((mTagStackIndex > 1 && + mTagStack[mTagStackIndex-2] == nsGkAtoms::select) || + (mTagStackIndex > 0 && + mTagStack[mTagStackIndex-1] == nsGkAtoms::select)) { + // Don't output the contents of SELECT elements; + // Might be nice, eventually, to output just the selected element. + // Read more in bug 31994. + return true; + } + + if (mTagStackIndex > 0 && + (mTagStack[mTagStackIndex-1] == nsGkAtoms::script || + mTagStack[mTagStackIndex-1] == nsGkAtoms::style)) { + // Don't output the contents of <script> or <style> tags; + return true; + } + + return false; +} + +void +nsPlainTextSerializer::DoAddText(bool aIsLineBreak, const nsAString& aText) +{ + // If we don't want any output, just return + if (!DoOutput()) { + return; + } + + if (!aIsLineBreak) { + // Make sure to reset this, since it's no longer true. + mHasWrittenCiteBlockquote = false; + } + + if (mLineBreakDue) + EnsureVerticalSpace(mFloatingLines); + + if (MustSuppressLeaf()) { + return; + } + + if (aIsLineBreak) { + // The only times we want to pass along whitespace from the original + // html source are if we're forced into preformatted mode via flags, + // or if we're prettyprinting and we're inside a <pre>. + // Otherwise, either we're collapsing to minimal text, or we're + // prettyprinting to mimic the html format, and in neither case + // does the formatting of the html source help us. + if ((mFlags & nsIDocumentEncoder::OutputPreformatted) || + (mPreFormatted && !mWrapColumn) || + IsInPre()) { + EnsureVerticalSpace(mEmptyLines+1); + } + else if (!mInWhitespace) { + Write(kSpace); + mInWhitespace = true; + } + return; + } + + /* Check, if we are in a link (symbolized with mURL containing the URL) + and the text is equal to the URL. In that case we don't want to output + the URL twice so we scrap the text in mURL. */ + if (!mURL.IsEmpty() && mURL.Equals(aText)) { + mURL.Truncate(); + } + Write(aText); +} + nsresult -nsPlainTextSerializer::DoAddLeaf(const nsIParserNode *aNode, PRInt32 aTag, - const nsAString& aText) +nsPlainTextSerializer::DoAddLeaf(nsIAtom* aTag) { // If we don't want any output, just return if (!DoOutput()) { return NS_OK; } - if (aTag != eHTMLTag_whitespace && aTag != eHTMLTag_newline) { - // Make sure to reset this, since it's no longer true. - mHasWrittenCiteBlockquote = false; - } - if (mLineBreakDue) EnsureVerticalSpace(mFloatingLines); - eHTMLTags type = (eHTMLTags)aTag; - - if ((mTagStackIndex > 1 && - mTagStack[mTagStackIndex-2] == eHTMLTag_select) || - (mTagStackIndex > 0 && - mTagStack[mTagStackIndex-1] == eHTMLTag_select)) { - // Don't output the contents of SELECT elements; - // Might be nice, eventually, to output just the selected element. - // Read more in bug 31994. - return NS_OK; - } - else if (mTagStackIndex > 0 && - (mTagStack[mTagStackIndex-1] == eHTMLTag_script || - mTagStack[mTagStackIndex-1] == eHTMLTag_style)) { - // Don't output the contents of <script> or <style> tags; + if (MustSuppressLeaf()) { return NS_OK; } - else if (type == eHTMLTag_text) { - /* Check, if we are in a link (symbolized with mURL containing the URL) - and the text is equal to the URL. In that case we don't want to output - the URL twice so we scrap the text in mURL. */ - if (!mURL.IsEmpty() && mURL.Equals(aText)) { - mURL.Truncate(); - } - Write(aText); - } - else if (type == eHTMLTag_entity) { - nsIParserService* parserService = nsContentUtils::GetParserService(); - if (parserService) { - nsAutoString str(aText); - PRInt32 entity; - parserService->HTMLConvertEntityToUnicode(str, &entity); - if (entity == -1 && - !str.IsEmpty() && - str.First() == (PRUnichar) '#') { - PRInt32 err = 0; - entity = str.ToInteger(&err, kAutoDetect); // NCR - } - nsAutoString temp; - temp.Append(PRUnichar(entity)); - Write(temp); - } - } - else if (type == eHTMLTag_br) { + + if (aTag == nsGkAtoms::br) { // Another egregious editor workaround, see bug 38194: // ignore the bogus br tags that the editor sticks here and there. - nsAutoString typeAttr; - if (NS_FAILED(GetAttributeValue(aNode, nsGkAtoms::type, typeAttr)) - || !typeAttr.EqualsLiteral("_moz")) { + nsAutoString tagAttr; + if (NS_FAILED(GetAttributeValue(nsGkAtoms::type, tagAttr)) + || !tagAttr.EqualsLiteral("_moz")) { EnsureVerticalSpace(mEmptyLines+1); } } - else if (type == eHTMLTag_whitespace || type == eHTMLTag_newline) { - // The only times we want to pass along whitespace from the original - // html source are if we're forced into preformatted mode via flags, - // or if we're prettyprinting and we're inside a <pre>. - // Otherwise, either we're collapsing to minimal text, or we're - // prettyprinting to mimic the html format, and in neither case - // does the formatting of the html source help us. - if (mFlags & nsIDocumentEncoder::OutputPreformatted || - (mPreFormatted && !mWrapColumn) || - IsInPre()) { - if (type == eHTMLTag_newline) - EnsureVerticalSpace(mEmptyLines+1); - else - Write(aText); - } - else if(!mInWhitespace) { - Write(kSpace); - mInWhitespace = true; - } - } - else if (type == eHTMLTag_hr && + else if (aTag == nsGkAtoms::hr && (mFlags & nsIDocumentEncoder::OutputFormatted)) { EnsureVerticalSpace(0); // Make a line of dashes as wide as the wrap width // XXX honoring percentage would be nice nsAutoString line; PRUint32 width = (mWrapColumn > 0 ? mWrapColumn : 25); while (line.Length() < width) { line.Append(PRUnichar('-')); } Write(line); EnsureVerticalSpace(0); } - else if (type == eHTMLTag_img) { + else if (aTag == nsGkAtoms::img) { /* Output (in decreasing order of preference) alt, title or nothing */ // See <http://www.w3.org/TR/REC-html40/struct/objects.html#edef-IMG> nsAutoString imageDescription; - if (NS_SUCCEEDED(GetAttributeValue(aNode, - nsGkAtoms::alt, + if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::alt, imageDescription))) { // If the alt attribute has an empty value (|alt=""|), output nothing } - else if (NS_SUCCEEDED(GetAttributeValue(aNode, - nsGkAtoms::title, + else if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::title, imageDescription)) && !imageDescription.IsEmpty()) { imageDescription = NS_LITERAL_STRING(" [") + imageDescription + NS_LITERAL_STRING("] "); } Write(imageDescription); } - return NS_OK; } /** * Adds as many newline as necessary to get |noOfRows| empty lines * * noOfRows = -1 : Being in the middle of some line of text * noOfRows = 0 : Being at the start of a line * noOfRows = n>0 : Having n empty lines before the current line. */ void nsPlainTextSerializer::EnsureVerticalSpace(PRInt32 noOfRows) { // If we have something in the indent we probably want to output // it and it's not included in the count for empty lines so we don't // realize that we should start a new line. - if(noOfRows >= 0 && !mInIndentString.IsEmpty()) { + if (noOfRows >= 0 && !mInIndentString.IsEmpty()) { EndLine(false); mInWhitespace = true; } while(mEmptyLines < noOfRows) { EndLine(false); mInWhitespace = true; } @@ -1229,18 +1118,18 @@ nsPlainTextSerializer::EnsureVerticalSpa * this function destroys the cache information. * * It will also write indentation and quotes if we believe us to be * at the start of the line. */ void nsPlainTextSerializer::FlushLine() { - if(!mCurrentLine.IsEmpty()) { - if(mAtFirstColumn) { + if (!mCurrentLine.IsEmpty()) { + if (mAtFirstColumn) { OutputQuotesAndIndent(); // XXX: Should we always do this? Bug? } Output(mCurrentLine); mAtFirstColumn = mAtFirstColumn && mCurrentLine.IsEmpty(); mCurrentLine.Truncate(); mCurrentLineWidth = 0; } @@ -1288,60 +1177,60 @@ nsPlainTextSerializer::AddToLine(const P PRInt32 aLineFragmentLength) { PRUint32 prefixwidth = (mCiteQuoteLevel > 0 ? mCiteQuoteLevel + 1:0)+mIndent; if (mLineBreakDue) EnsureVerticalSpace(mFloatingLines); PRInt32 linelength = mCurrentLine.Length(); - if(0 == linelength) { - if(0 == aLineFragmentLength) { + if (0 == linelength) { + if (0 == aLineFragmentLength) { // Nothing at all. Are you kidding me? return; } - if(mFlags & nsIDocumentEncoder::OutputFormatFlowed) { - if(IsSpaceStuffable(aLineFragment) + if (mFlags & nsIDocumentEncoder::OutputFormatFlowed) { + if (IsSpaceStuffable(aLineFragment) && mCiteQuoteLevel == 0 // We space-stuff quoted lines anyway ) { // Space stuffing a la RFC 2646 (format=flowed). mCurrentLine.Append(PRUnichar(' ')); - if(MayWrap()) { + if (MayWrap()) { mCurrentLineWidth += GetUnicharWidth(' '); #ifdef DEBUG_wrapping NS_ASSERTION(GetUnicharStringWidth(mCurrentLine.get(), mCurrentLine.Length()) == (PRInt32)mCurrentLineWidth, "mCurrentLineWidth and reality out of sync!"); #endif } } } mEmptyLines=-1; } mCurrentLine.Append(aLineFragment, aLineFragmentLength); - if(MayWrap()) { + if (MayWrap()) { mCurrentLineWidth += GetUnicharStringWidth(aLineFragment, aLineFragmentLength); #ifdef DEBUG_wrapping NS_ASSERTION(GetUnicharstringWidth(mCurrentLine.get(), mCurrentLine.Length()) == (PRInt32)mCurrentLineWidth, "mCurrentLineWidth and reality out of sync!"); #endif } linelength = mCurrentLine.Length(); // Wrap? - if(MayWrap()) + if (MayWrap()) { #ifdef DEBUG_wrapping NS_ASSERTION(GetUnicharstringWidth(mCurrentLine.get(), mCurrentLine.Length()) == (PRInt32)mCurrentLineWidth, "mCurrentLineWidth and reality out of sync!"); #endif // Yes, wrap! @@ -1397,35 +1286,35 @@ nsPlainTextSerializer::AddToLine(const P goodSpace=(prefixwidth>mWrapColumn)?1:mWrapColumn-prefixwidth; while (goodSpace < linelength && !nsCRT::IsAsciiSpace(mCurrentLine.CharAt(goodSpace))) { goodSpace++; } } } - if((goodSpace < linelength) && (goodSpace > 0)) { + if ((goodSpace < linelength) && (goodSpace > 0)) { // Found a place to break // -1 (trim a char at the break position) // only if the line break was a space. if (nsCRT::IsAsciiSpace(mCurrentLine.CharAt(goodSpace))) { mCurrentLine.Right(restOfLine, linelength-goodSpace-1); } else { mCurrentLine.Right(restOfLine, linelength-goodSpace); } // if breaker was U+0020, it has to consider for delsp=yes support bool breakBySpace = mCurrentLine.CharAt(goodSpace) == ' '; mCurrentLine.Truncate(goodSpace); EndLine(true, breakBySpace); mCurrentLine.Truncate(); // Space stuff new line? - if(mFlags & nsIDocumentEncoder::OutputFormatFlowed) { - if(!restOfLine.IsEmpty() && IsSpaceStuffable(restOfLine.get()) + if (mFlags & nsIDocumentEncoder::OutputFormatFlowed) { + if (!restOfLine.IsEmpty() && IsSpaceStuffable(restOfLine.get()) && mCiteQuoteLevel == 0 // We space-stuff quoted lines anyway ) { // Space stuffing a la RFC 2646 (format=flowed). mCurrentLine.Append(PRUnichar(' ')); //XXX doesn't seem to work correctly for ' ' } } @@ -1453,66 +1342,66 @@ nsPlainTextSerializer::AddToLine(const P * one specified. Strips ending spaces from the line if it isn't * preformatted. */ void nsPlainTextSerializer::EndLine(bool aSoftlinebreak, bool aBreakBySpace) { PRUint32 currentlinelength = mCurrentLine.Length(); - if(aSoftlinebreak && 0 == currentlinelength) { + if (aSoftlinebreak && 0 == currentlinelength) { // No meaning return; } /* In non-preformatted mode, remove spaces from the end of the line for * format=flowed compatibility. Don't do this for these special cases: * "-- ", the signature separator (RFC 2646) shouldn't be touched and * "- -- ", the OpenPGP dash-escaped signature separator in inline * signed messages according to the OpenPGP standard (RFC 2440). */ - if(!(mFlags & nsIDocumentEncoder::OutputPreformatted) && + if (!(mFlags & nsIDocumentEncoder::OutputPreformatted) && (aSoftlinebreak || !(mCurrentLine.EqualsLiteral("-- ") || mCurrentLine.EqualsLiteral("- -- ")))) { // Remove spaces from the end of the line. while(currentlinelength > 0 && mCurrentLine[currentlinelength-1] == ' ') { --currentlinelength; } mCurrentLine.SetLength(currentlinelength); } - if(aSoftlinebreak && + if (aSoftlinebreak && (mFlags & nsIDocumentEncoder::OutputFormatFlowed) && (mIndent == 0)) { // Add the soft part of the soft linebreak (RFC 2646 4.1) // We only do this when there is no indentation since format=flowed // lines and indentation doesn't work well together. // If breaker character is ASCII space with RFC 3676 support (delsp=yes), // add twice space. - if (mFlags & nsIDocumentEncoder::OutputFormatDelSp && aBreakBySpace) + if ((mFlags & nsIDocumentEncoder::OutputFormatDelSp) && aBreakBySpace) mCurrentLine.Append(NS_LITERAL_STRING(" ")); else mCurrentLine.Append(PRUnichar(' ')); } - if(aSoftlinebreak) { + if (aSoftlinebreak) { mEmptyLines=0; } else { // Hard break - if(!mCurrentLine.IsEmpty() || !mInIndentString.IsEmpty()) { + if (!mCurrentLine.IsEmpty() || !mInIndentString.IsEmpty()) { mEmptyLines=-1; } mEmptyLines++; } - if(mAtFirstColumn) { + if (mAtFirstColumn) { // If we don't have anything "real" to output we have to // make sure the indent doesn't end in a space since that // would trick a format=flowed-aware receiver. bool stripTrailingSpaces = mCurrentLine.IsEmpty(); OutputQuotesAndIndent(stripTrailingSpaces); } mCurrentLine.Append(mLineBreak); @@ -1562,32 +1451,32 @@ nsPlainTextSerializer::OutputQuotesAndIn ) { nsAutoString spaces; for (int i=0; i < indentwidth; ++i) spaces.Append(PRUnichar(' ')); stringToOutput += spaces; mAtFirstColumn = false; } - if(!mInIndentString.IsEmpty()) { + if (!mInIndentString.IsEmpty()) { stringToOutput += mInIndentString; mAtFirstColumn = false; mInIndentString.Truncate(); } - if(stripTrailingSpaces) { + if (stripTrailingSpaces) { PRInt32 lineLength = stringToOutput.Length(); while(lineLength > 0 && ' ' == stringToOutput[lineLength-1]) { --lineLength; } stringToOutput.SetLength(lineLength); } - if(!stringToOutput.IsEmpty()) { + if (!stringToOutput.IsEmpty()) { Output(stringToOutput); } } /** * Write a string. This is the highlevel function to use to get text output. * By using AddToLine, Output, EndLine and other functions it handles quotation, @@ -1654,34 +1543,34 @@ nsPlainTextSerializer::Write(const nsASt // Find one of '\n' or '\r' using iterators since nsAString // doesn't have the old FindCharInSet function. nsAString::const_iterator iter; str.BeginReading(iter); nsAString::const_iterator done_searching; str.EndReading(done_searching); iter.advance(bol); PRInt32 new_newline = bol; newline = kNotFound; while(iter != done_searching) { - if('\n' == *iter || '\r' == *iter) { + if ('\n' == *iter || '\r' == *iter) { newline = new_newline; break; } - if(' ' != *iter) + if (' ' != *iter) spacesOnly = false; ++new_newline; ++iter; } // Done searching nsAutoString stringpart; - if(newline == kNotFound) { + if (newline == kNotFound) { // No new lines. stringpart.Assign(Substring(str, bol, totLen - bol)); - if(!stringpart.IsEmpty()) { + if (!stringpart.IsEmpty()) { PRUnichar lastchar = stringpart[stringpart.Length()-1]; - if((lastchar == '\t') || (lastchar == ' ') || + if ((lastchar == '\t') || (lastchar == ' ') || (lastchar == '\r') ||(lastchar == '\n')) { mInWhitespace = true; } else { mInWhitespace = false; } } mEmptyLines=-1; @@ -1691,17 +1580,17 @@ nsPlainTextSerializer::Write(const nsASt else { // There is a newline stringpart.Assign(Substring(str, bol, newline-bol)); mInWhitespace = true; outputLineBreak = true; mEmptyLines=0; atFirstColumn = true; bol = newline+1; - if('\r' == *iter && bol < totLen && '\n' == *++iter) { + if ('\r' == *iter && bol < totLen && '\n' == *++iter) { // There was a CRLF in the input. This used to be illegal and // stripped by the parser. Apparently not anymore. Let's skip // over the LF. bol++; } } mCurrentLine.AssignLiteral(""); @@ -1710,17 +1599,17 @@ nsPlainTextSerializer::Write(const nsASt !stringpart.EqualsLiteral("-- ") && !stringpart.EqualsLiteral("- -- ")) stringpart.Trim(" ", false, true, true); if (IsSpaceStuffable(stringpart.get()) && stringpart[0] != '>') mCurrentLine.Append(PRUnichar(' ')); } mCurrentLine.Append(stringpart); - if(outputQuotes) { + if (outputQuotes) { // Note: this call messes with mAtFirstColumn OutputQuotesAndIndent(); } Output(mCurrentLine); if (outputLineBreak) { Output(mLineBreak); } @@ -1750,17 +1639,17 @@ nsPlainTextSerializer::Write(const nsASt nsAutoString remaining; str.Right(remaining, totLen - bol); foo = ToNewCString(remaining); // printf("Next line: bol = %d, newlinepos = %d, totLen = %d, string = '%s'\n", // bol, nextpos, totLen, foo); nsMemory::Free(foo); #endif - if(nextpos == kNotFound) { + if (nextpos == kNotFound) { // The rest of the string offsetIntoBuffer = str.get() + bol; AddToLine(offsetIntoBuffer, totLen-bol); bol=totLen; mInWhitespace=false; } else { // There's still whitespace left in the string @@ -1777,29 +1666,29 @@ nsPlainTextSerializer::Write(const nsASt // If we're already in whitespace and not preformatted, just skip it: if (mInWhitespace && (nextpos == bol) && !mPreFormatted && !(mFlags & nsIDocumentEncoder::OutputPreformatted)) { // Skip whitespace bol++; continue; } - if(nextpos == bol) { + if (nextpos == bol) { // Note that we are in whitespace. mInWhitespace = true; offsetIntoBuffer = str.get() + nextpos; AddToLine(offsetIntoBuffer, 1); bol++; continue; } mInWhitespace = true; offsetIntoBuffer = str.get() + bol; - if(mPreFormatted || (mFlags & nsIDocumentEncoder::OutputPreformatted)) { + if (mPreFormatted || (mFlags & nsIDocumentEncoder::OutputPreformatted)) { // Preserve the real whitespace character nextpos++; AddToLine(offsetIntoBuffer, nextpos-bol); bol = nextpos; } else { // Replace the whitespace with a space AddToLine(offsetIntoBuffer, nextpos-bol); @@ -1811,118 +1700,70 @@ nsPlainTextSerializer::Write(const nsASt } /** * Gets the value of an attribute in a string. If the function returns * NS_ERROR_NOT_AVAILABLE, there was none such attribute specified. */ nsresult -nsPlainTextSerializer::GetAttributeValue(const nsIParserNode* aNode, - nsIAtom* aName, +nsPlainTextSerializer::GetAttributeValue(nsIAtom* aName, nsString& aValueRet) { if (mElement) { if (mElement->GetAttr(kNameSpaceID_None, aName, aValueRet)) { return NS_OK; } } - else if (aNode) { - nsDependentAtomString name(aName); - - PRInt32 count = aNode->GetAttributeCount(); - for (PRInt32 i=0;i<count;i++) { - const nsAString& key = aNode->GetKeyAt(i); - if (key.Equals(name, nsCaseInsensitiveStringComparator())) { - aValueRet = aNode->GetValueAt(i); - return NS_OK; - } - } - } return NS_ERROR_NOT_AVAILABLE; } /** * Returns true, if the element was inserted by Moz' TXT->HTML converter. * In this case, we should ignore it. */ bool -nsPlainTextSerializer::IsCurrentNodeConverted(const nsIParserNode* aNode) +nsPlainTextSerializer::IsCurrentNodeConverted() { nsAutoString value; - nsresult rv = GetAttributeValue(aNode, nsGkAtoms::_class, value); + nsresult rv = GetAttributeValue(nsGkAtoms::_class, value); return (NS_SUCCEEDED(rv) && (value.EqualsIgnoreCase("moz-txt", 7) || value.EqualsIgnoreCase("\"moz-txt", 8))); } // static -PRInt32 +nsIAtom* nsPlainTextSerializer::GetIdForContent(nsIContent* aContent) { if (!aContent->IsHTML()) { - return eHTMLTag_unknown; + return nsnull; } - nsIParserService* parserService = nsContentUtils::GetParserService(); - - return parserService ? parserService->HTMLAtomTagToId(aContent->Tag()) : - eHTMLTag_unknown; -} - -/** - * Returns true if the id represents an element of block type. - * Can be used to determine if a new paragraph should be started. - */ -bool -nsPlainTextSerializer::IsBlockLevel(PRInt32 aId) -{ - bool isBlock = false; - - nsIParserService* parserService = nsContentUtils::GetParserService(); - if (parserService) { - parserService->IsBlock(aId, isBlock); - } - - return isBlock; -} - -/** - * Returns true if the id represents a container. - */ -bool -nsPlainTextSerializer::IsContainer(PRInt32 aId) -{ - bool isContainer = false; - - nsIParserService* parserService = nsContentUtils::GetParserService(); - if (parserService) { - parserService->IsContainer(aId, isContainer); - } - - return isContainer; + nsIAtom* localName = aContent->Tag(); + return localName->IsStaticAtom() ? localName : nsnull; } /** * Returns true if we currently are inside a <pre>. The check is done * by traversing the tag stack looking for <pre> until we hit a block * level tag which is assumed to override any <pre>:s below it in * the stack. To do this correctly to a 100% would require access * to style which we don't support in this converter. */ bool nsPlainTextSerializer::IsInPre() { PRInt32 i = mTagStackIndex; while(i > 0) { - if(mTagStack[i-1] == eHTMLTag_pre) + if (mTagStack[i - 1] == nsGkAtoms::pre) return true; - if(IsBlockLevel(mTagStack[i-1])) { + if (nsContentUtils::IsHTMLBlock(mTagStack[i - 1])) { // We assume that every other block overrides a <pre> return false; } --i; } // Not a <pre> in the whole stack return false; @@ -1932,51 +1773,51 @@ nsPlainTextSerializer::IsInPre() * This method is required only to identify LI's inside OL. * Returns TRUE if we are inside an OL tag and FALSE otherwise. */ bool nsPlainTextSerializer::IsInOL() { PRInt32 i = mTagStackIndex; while(--i >= 0) { - if(mTagStack[i] == eHTMLTag_ol) + if (mTagStack[i] == nsGkAtoms::ol) return true; - if (mTagStack[i] == eHTMLTag_ul) { + if (mTagStack[i] == nsGkAtoms::ul) { // If a UL is reached first, LI belongs the UL nested in OL. return false; } } // We may reach here for orphan LI's. return false; } /* @return 0 = no header, 1 = h1, ..., 6 = h6 */ -PRInt32 HeaderLevel(eHTMLTags aTag) +PRInt32 HeaderLevel(nsIAtom* aTag) { - PRInt32 result; - switch (aTag) - { - case eHTMLTag_h1: - result = 1; break; - case eHTMLTag_h2: - result = 2; break; - case eHTMLTag_h3: - result = 3; break; - case eHTMLTag_h4: - result = 4; break; - case eHTMLTag_h5: - result = 5; break; - case eHTMLTag_h6: - result = 6; break; - default: - result = 0; break; + if (aTag == nsGkAtoms::h1) { + return 1; + } + if (aTag == nsGkAtoms::h2) { + return 2; + } + if (aTag == nsGkAtoms::h3) { + return 3; } - return result; + if (aTag == nsGkAtoms::h4) { + return 4; + } + if (aTag == nsGkAtoms::h5) { + return 5; + } + if (aTag == nsGkAtoms::h6) { + return 6; + } + return 0; } /* * This is an implementation of GetUnicharWidth() and * GetUnicharStringWidth() as defined in * "The Single UNIX Specification, Version 2, The Open Group, 1997" * <http://www.UNIX-systems.org/online.html>
--- a/content/base/src/nsPlainTextSerializer.h +++ b/content/base/src/nsPlainTextSerializer.h @@ -40,36 +40,31 @@ * nsIDocumentEncoder to convert a DOM into plaintext in a nice way * (eg for copy/paste as plaintext). */ #ifndef nsPlainTextSerializer_h__ #define nsPlainTextSerializer_h__ #include "nsIContentSerializer.h" -#include "nsIHTMLContentSink.h" -#include "nsHTMLTags.h" #include "nsCOMPtr.h" #include "nsString.h" #include "nsILineBreaker.h" #include "nsIContent.h" #include "nsIAtom.h" -#include "nsIHTMLToTextSink.h" #include "nsIDocumentEncoder.h" #include "nsTArray.h" namespace mozilla { namespace dom { class Element; } // namespace dom } // namespace mozilla -class nsPlainTextSerializer : public nsIContentSerializer, - public nsIHTMLContentSink, - public nsIHTMLToTextSink +class nsPlainTextSerializer : public nsIContentSerializer { public: nsPlainTextSerializer(); virtual ~nsPlainTextSerializer(); NS_DECL_ISUPPORTS // nsIContentSerializer @@ -95,63 +90,39 @@ public: nsAString& aStr); NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement, nsAString& aStr); NS_IMETHOD Flush(nsAString& aStr); NS_IMETHOD AppendDocumentStart(nsIDocument *aDocument, nsAString& aStr); - // nsIContentSink - NS_IMETHOD WillParse(void) { return NS_OK; } - NS_IMETHOD WillInterrupt(void) { return NS_OK; } - NS_IMETHOD WillResume(void) { return NS_OK; } - NS_IMETHOD SetParser(nsParserBase* aParser) { return NS_OK; } - NS_IMETHOD OpenContainer(const nsIParserNode& aNode); - NS_IMETHOD CloseContainer(const nsHTMLTag aTag); - NS_IMETHOD AddLeaf(const nsIParserNode& aNode); - virtual void FlushPendingNotifications(mozFlushType aType) { } - NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; } - virtual nsISupports *GetTarget() { return nsnull; } - - // nsIHTMLContentSink - NS_IMETHOD OpenHead(); - NS_IMETHOD IsEnabled(PRInt32 aTag, bool* aReturn); - NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; } - - NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; } - NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; } - NS_IMETHOD DidProcessTokens(void) { return NS_OK; } - NS_IMETHOD WillProcessAToken(void) { return NS_OK; } - NS_IMETHOD DidProcessAToken(void) { return NS_OK; } - - // nsIHTMLToTextSink - NS_IMETHOD Initialize(nsAString* aOutString, - PRUint32 aFlags, PRUint32 aWrapCol); - protected: - nsresult GetAttributeValue(const nsIParserNode* node, nsIAtom* aName, nsString& aValueRet); + nsresult GetAttributeValue(nsIAtom* aName, nsString& aValueRet); void AddToLine(const PRUnichar* aStringToAdd, PRInt32 aLength); void EndLine(bool softlinebreak, bool aBreakBySpace = false); void EnsureVerticalSpace(PRInt32 noOfRows); void FlushLine(); void OutputQuotesAndIndent(bool stripTrailingSpaces=false); void Output(nsString& aString); void Write(const nsAString& aString); - bool IsBlockLevel(PRInt32 aId); - bool IsContainer(PRInt32 aId); bool IsInPre(); bool IsInOL(); - bool IsCurrentNodeConverted(const nsIParserNode* aNode); - static PRInt32 GetIdForContent(nsIContent* aContent); - nsresult DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag); - nsresult DoCloseContainer(PRInt32 aTag); - nsresult DoAddLeaf(const nsIParserNode* aNode, - PRInt32 aTag, - const nsAString& aText); + bool IsCurrentNodeConverted(); + bool MustSuppressLeaf(); + + /** + * Returns the local name of the element as an atom if the element is an + * HTML element and the atom is a static atom. Otherwise, nsnull is returned. + */ + static nsIAtom* GetIdForContent(nsIContent* aContent); + nsresult DoOpenContainer(nsIAtom* aTag); + nsresult DoCloseContainer(nsIAtom* aTag); + nsresult DoAddLeaf(nsIAtom* aTag); + void DoAddText(bool aIsWhitespace, const nsAString& aText); // Inlined functions inline bool MayWrap() { return mWrapColumn && ((mFlags & nsIDocumentEncoder::OutputFormatted) || (mFlags & nsIDocumentEncoder::OutputWrap)); } @@ -235,24 +206,25 @@ protected: mHeaderCounter[1] for <h1> etc. */ nsRefPtr<mozilla::dom::Element> mElement; // For handling table rows nsAutoTArray<bool, 8> mHasWrittenCellsForRow; // Values gotten in OpenContainer that is (also) needed in CloseContainer - nsAutoTArray<bool, 8> mCurrentNodeIsConverted; nsAutoTArray<bool, 8> mIsInCiteBlockquote; // The output data nsAString* mOutputString; - // The tag stack: the stack of tags we're operating on, so we can nest: - nsHTMLTag *mTagStack; + // The tag stack: the stack of tags we're operating on, so we can nest. + // The stack only ever points to static atoms, so they don't need to be + // refcounted. + nsIAtom** mTagStack; PRUint32 mTagStackIndex; // Content in the stack above this index should be ignored: PRUint32 mIgnoreAboveIndex; // The stack for ordered lists PRInt32 *mOLStack; PRUint32 mOLStackIndex;
--- a/content/html/content/src/nsFormSubmission.cpp +++ b/content/html/content/src/nsFormSubmission.cpp @@ -264,17 +264,16 @@ HandleMailtoSubject(nsCString& aPath) { if (NS_FAILED(rv)) return; const PRUnichar *formatStrings[] = { brandName.get() }; nsXPIDLString subjectStr; rv = nsContentUtils::FormatLocalizedString( nsContentUtils::eFORMS_PROPERTIES, "DefaultFormSubject", formatStrings, - ArrayLength(formatStrings), subjectStr); if (NS_FAILED(rv)) return; aPath.AppendLiteral("subject="); nsCString subjectStrEscaped; aPath.Append(NS_EscapeURL(NS_ConvertUTF16toUTF8(subjectStr), esc_Query, subjectStrEscaped)); }
--- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -3825,17 +3825,17 @@ nsHTMLInputElement::GetValidationMessage GetTextLength(&textLength); strMaxLength.AppendInt(maxLength); strTextLength.AppendInt(textLength); const PRUnichar* params[] = { strMaxLength.get(), strTextLength.get() }; rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, "FormValidationTextTooLong", - params, 2, message); + params, message); aValidationMessage = message; break; } case VALIDITY_STATE_VALUE_MISSING: { nsXPIDLString message; nsCAutoString key; switch (mType) @@ -3884,17 +3884,17 @@ nsHTMLInputElement::GetValidationMessage message); } else { if (title.Length() > nsIConstraintValidation::sContentSpecifiedMaxLengthMessage) { title.Truncate(nsIConstraintValidation::sContentSpecifiedMaxLengthMessage); } const PRUnichar* params[] = { title.get() }; rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, "FormValidationPatternMismatchWithTitle", - params, 1, message); + params, message); } aValidationMessage = message; break; } default: rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType); }
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp +++ b/content/html/content/src/nsHTMLTextAreaElement.cpp @@ -1418,17 +1418,17 @@ nsHTMLTextAreaElement::GetValidationMess GetTextLength(&textLength); strMaxLength.AppendInt(maxLength); strTextLength.AppendInt(textLength); const PRUnichar* params[] = { strMaxLength.get(), strTextLength.get() }; rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, "FormValidationTextTooLong", - params, 2, message); + params, message); aValidationMessage = message; } break; case VALIDITY_STATE_VALUE_MISSING: { nsXPIDLString message; rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, "FormValidationValueMissing",
--- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -1461,20 +1461,24 @@ nsSVGElement::MaybeSerializeAttrBeforeRe { if (!aNotify || !nsContentUtils::HasMutationListeners(this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) { return; } + const nsAttrValue* attrValue = mAttrsAndChildren.GetAttr(aName); + if (!attrValue) + return; + nsAutoString serializedValue; - mAttrsAndChildren.GetAttr(aName)->ToString(serializedValue); - nsAttrValue attrValue(serializedValue); - mAttrsAndChildren.SetAndTakeAttr(aName, attrValue); + attrValue->ToString(serializedValue); + nsAttrValue oldAttrValue(serializedValue); + mAttrsAndChildren.SetAndTakeAttr(aName, oldAttrValue); } /* static */ nsIAtom* nsSVGElement::GetEventNameForAttr(nsIAtom* aAttr) { if (aAttr == nsGkAtoms::onload) return nsGkAtoms::onSVGLoad; if (aAttr == nsGkAtoms::onunload)
--- a/content/svg/content/test/test_SVGLengthList.xhtml +++ b/content/svg/content/test/test_SVGLengthList.xhtml @@ -56,16 +56,23 @@ function run_tests() eventChecker.expect("modify"); lengths[0].valueAsString = "10"; eventChecker.expect(""); lengths[0].value = 10; lengths[0].valueInSpecifiedUnits = 10; lengths[0].valueAsString = "10"; lengths[0].convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER); lengths[0].newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER, 10); + // -- Attribute removal + eventChecker.expect("remove"); + text.removeAttribute("x"); + // -- Non-existent attribute removal + eventChecker.expect(""); + text.removeAttribute("x"); + text.removeAttributeNS(null, "x"); eventChecker.finish(); SimpleTest.finish(); } window.addEventListener("load", run_tests, false); ]]>
--- a/content/svg/content/test/test_SVGNumberList.xhtml +++ b/content/svg/content/test/test_SVGNumberList.xhtml @@ -45,16 +45,23 @@ function run_tests() eventChecker.expect("modify modify"); numbers[0].value = 15; text.setAttribute("rotate", "17 20 30"); // -- Redundant changes eventChecker.expect(""); numbers[0].value = 17; numbers[1].value = 20; text.setAttribute("rotate", "17 20 30"); + // -- Attribute removal + eventChecker.expect("remove"); + text.removeAttribute("rotate"); + // -- Non-existent attribute removal + eventChecker.expect(""); + text.removeAttribute("rotate"); + text.removeAttributeNS(null, "rotate"); eventChecker.finish(); SimpleTest.finish(); } window.addEventListener("load", run_tests, false); ]]>
--- a/content/svg/content/test/test_SVGPathSegList.xhtml +++ b/content/svg/content/test/test_SVGPathSegList.xhtml @@ -100,16 +100,25 @@ function run_tests() list[0].y = 5; path.setAttribute("d", "M20,5 L12,34"); // -- Redundant changes eventChecker.expect(""); list[0].x = 20; list[1].y = 34; path.setAttribute("d", "M20,5 L12,34"); + + // -- Attribute removal + eventChecker.expect("remove"); + path.removeAttribute("d"); + + // -- Non-existent attribute removal + eventChecker.expect(""); + path.removeAttribute("d"); + path.removeAttributeNS(null, "d"); eventChecker.finish(); SimpleTest.finish(); } window.addEventListener("load", run_tests, false); ]]>
--- a/content/svg/content/test/test_SVGPointList.xhtml +++ b/content/svg/content/test/test_SVGPointList.xhtml @@ -45,16 +45,23 @@ function run_tests() eventChecker.expect("modify modify"); points[0].x = 40; polyline.setAttribute("points", "30,375 150,380"); // -- Redundant changes eventChecker.expect(""); points[0].x = 30; points[1].y = 380; polyline.setAttribute("points", "30,375 150,380"); + // -- Attribute removal + eventChecker.expect("remove"); + polyline.removeAttribute("points"); + // -- Non-existent attribute removal + eventChecker.expect(""); + polyline.removeAttribute("points"); + polyline.removeAttributeNS(null, "points"); eventChecker.finish(); SimpleTest.finish(); } window.addEventListener("load", run_tests, false); ]]>
--- a/content/svg/content/test/test_SVGTransformList.xhtml +++ b/content/svg/content/test/test_SVGTransformList.xhtml @@ -419,16 +419,26 @@ function testMutationEvents(g) list[0].matrix.e = 5; // setAttribute interaction eventChecker.expect("modify"); list[0].setMatrix(mx); eventChecker.expect(""); g.setAttribute("transform", "matrix(1, 0, 0, 1, 0, 0)"); list[0].setMatrix(mx); + + // Attribute removal + eventChecker.expect("remove"); + g.removeAttribute("transform"); + + // Non-existent attribute removal + eventChecker.expect(""); + g.removeAttribute("transform"); + g.removeAttributeNS(null, "transform"); + eventChecker.finish(); } window.addEventListener("load", main, false); ]]> </script> </pre>
--- a/content/svg/content/test/test_SVGxxxList.xhtml +++ b/content/svg/content/test/test_SVGxxxList.xhtml @@ -830,16 +830,17 @@ function run_baseVal_API_tests() 'an object of the wrong type.'); eventChecker.ignoreEvents(); } // Test removal and addition events eventChecker.expect("remove add"); t.element.removeAttribute(t.attr_name); + t.element.removeAttributeNS(null, t.attr_name); res = t.baseVal.appendItem(item); eventChecker.finish(); } } /** * This function tests the SVGXxxList API for the anim val list (see also the
--- a/content/svg/content/test/test_dataTypesModEvents.html +++ b/content/svg/content/test/test_dataTypesModEvents.html @@ -33,16 +33,17 @@ function runTests() // class attribute eventChecker.watchAttr(filter, "class"); eventChecker.expect("add modify remove add"); filter.setAttribute("class", "foo"); filter.className.baseVal = "bar"; filter.removeAttribute("class"); + filter.removeAttributeNS(null, "class"); filter.className.baseVal = "foo"; eventChecker.expect(""); filter.className.baseVal = "foo"; filter.setAttribute("class", "foo"); // length attribute @@ -52,16 +53,17 @@ function runTests() marker.markerWidth.baseVal.value = 8; marker.markerWidth.baseVal.valueInSpecifiedUnits = 9; marker.markerWidth.baseVal.valueAsString = "10"; marker.markerWidth.baseVal.convertToSpecifiedUnits( SVGLength.SVG_LENGTHTYPE_CM); marker.markerWidth.baseVal.newValueSpecifiedUnits( SVGLength.SVG_LENGTHTYPE_MM, 11); marker.removeAttribute("markerWidth"); + marker.removeAttributeNS(null, "markerWidth"); marker.markerWidth.baseVal.value = 8; eventChecker.expect("remove add modify"); marker.removeAttribute("markerWidth"); console.log(marker.getAttribute("markerWidth")); marker.markerWidth.baseVal.convertToSpecifiedUnits( SVGLength.SVG_LENGTHTYPE_NUMBER); console.log(marker.getAttribute("markerWidth")); @@ -79,43 +81,46 @@ function runTests() // number attribute eventChecker.watchAttr(convolve, "divisor"); eventChecker.expect("add modify remove add"); convolve.setAttribute("divisor", "12.5"); convolve.divisor.baseVal = 6; convolve.removeAttribute("divisor"); + convolve.removeAttributeNS(null, "divisor"); convolve.divisor.baseVal = 8; eventChecker.expect(""); convolve.divisor.baseVal = 8; convolve.setAttribute("divisor", "8"); // number-optional-number attribute eventChecker.watchAttr(blur, "stdDeviation"); eventChecker.expect("add modify remove add"); blur.setAttribute("stdDeviation", "5, 6"); blur.stdDeviationX.baseVal = 8; blur.removeAttribute("stdDeviation"); + blur.removeAttributeNS(null, "stdDeviation"); blur.setAttribute("stdDeviation", "2, 3"); eventChecker.expect(""); blur.stdDeviationX.baseVal = 2; blur.stdDeviationY.baseVal = 3; blur.setAttribute("stdDeviation", "2, 3"); // integer attribute eventChecker.watchAttr(convolve, "targetX"); eventChecker.expect("add modify remove add"); convolve.setAttribute("targetX", "12"); convolve.targetX.baseVal = 6; convolve.removeAttribute("targetX"); + convolve.removeAttributeNS(null, "targetX"); convolve.targetX.baseVal = 8; // Check redundant change when comparing typed value to attribute value eventChecker.expect(""); convolve.setAttribute("targetX", "8"); // Check redundant change when comparing attribute value to typed value eventChecker.expect("remove add"); convolve.removeAttribute("targetX"); @@ -124,16 +129,17 @@ function runTests() // integer-optional-integer attribute eventChecker.watchAttr(filter, "filterRes"); eventChecker.expect("add modify remove add"); filter.setAttribute("filterRes", "60, 70"); filter.filterResX.baseVal = 50; filter.removeAttribute("filterRes"); + filter.removeAttributeNS(null, "filterRes"); filter.setAttribute("filterRes", "50, 60"); eventChecker.expect(""); filter.filterResX.baseVal = 50; filter.setAttribute("filterRes", "50, 60"); filter.filterResY.baseVal = 60; // angle attribute @@ -144,16 +150,17 @@ function runTests() marker.orientAngle.baseVal.value = 12; marker.orientAngle.baseVal.valueInSpecifiedUnits = 23; marker.orientAngle.baseVal.valueAsString = "34"; marker.orientAngle.baseVal.newValueSpecifiedUnits( SVGAngle.SVG_ANGLETYPE_GRAD, 34); marker.orientAngle.baseVal.newValueSpecifiedUnits( SVGAngle.SVG_ANGLETYPE_GRAD, 45); marker.removeAttribute("orient"); + marker.removeAttributeNS(null, "orient"); marker.orientAngle.baseVal.value = 40; eventChecker.expect(""); marker.orientAngle.baseVal.value = 40; marker.orientAngle.baseVal.valueInSpecifiedUnits = 40; marker.orientAngle.baseVal.valueAsString = "40"; marker.orientAngle.baseVal.convertToSpecifiedUnits( SVGAngle.SVG_ANGLETYPE_UNSPECIFIED); @@ -162,73 +169,78 @@ function runTests() // boolean attribute eventChecker.watchAttr(convolve, "preserveAlpha"); eventChecker.expect("add modify remove add"); convolve.setAttribute("preserveAlpha", "true"); convolve.preserveAlpha.baseVal = false; convolve.removeAttribute("preserveAlpha"); + convolve.removeAttributeNS(null, "preserveAlpha"); convolve.preserveAlpha.baseVal = true; eventChecker.expect(""); convolve.preserveAlpha.baseVal = true; convolve.setAttribute("preserveAlpha", "true"); // enum attribute eventChecker.watchAttr(convolve, "edgeMode"); eventChecker.expect("add modify remove add"); convolve.setAttribute("edgeMode", "none"); convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP; convolve.removeAttribute("edgeMode"); + convolve.removeAttributeNS(null, "edgeMode"); convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE; eventChecker.expect(""); convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE; convolve.setAttribute("edgeMode", "none"); convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE; // string attribute eventChecker.watchAttr(convolve, "result"); eventChecker.expect("add modify remove add"); convolve.setAttribute("result", "bar"); convolve.result.baseVal = "foo"; convolve.removeAttribute("result"); + convolve.removeAttributeNS(null, "result"); convolve.result.baseVal = "bar"; eventChecker.expect(""); convolve.result.baseVal = "bar"; convolve.setAttribute("result", "bar"); convolve.result.baseVal = "bar"; // preserveAspectRatio attribute eventChecker.watchAttr(marker, "preserveAspectRatio"); eventChecker.expect("add modify remove add"); marker.setAttribute("preserveAspectRatio", "xMaxYMid slice"); marker.preserveAspectRatio.baseVal.align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMAX; marker.removeAttribute("preserveAspectRatio"); + marker.removeAttributeNS(null, "preserveAspectRatio"); marker.preserveAspectRatio.baseVal.align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN; eventChecker.expect(""); marker.preserveAspectRatio.baseVal.meetOrSlice = SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET; marker.setAttribute("preserveAspectRatio", "xMidYMin meet"); // viewBox attribute eventChecker.watchAttr(marker, "viewBox"); eventChecker.expect("add modify remove add"); marker.setAttribute("viewBox", "1 2 3 4"); marker.viewBox.baseVal.height = 5; marker.removeAttribute("viewBox"); + marker.removeAttributeNS(null, "viewBox"); marker.viewBox.baseVal.height = 4; eventChecker.ignoreEvents(); marker.setAttribute("viewBox", "1 2 3 4"); eventChecker.expect(""); marker.viewBox.baseVal.height = 4; marker.viewBox.baseVal.x = 1; marker.setAttribute("viewBox", "1 2 3 4");
--- a/content/xbl/test/test_bug389322.xhtml +++ b/content/xbl/test/test_bug389322.xhtml @@ -108,17 +108,17 @@ function report(testName, success) { let (x=1) (x); var success = true; } catch (e) { success = false; } report("HTML script tags with explicit version", success) ]]></script> <script type="text/javascript"><![CDATA[ try { - let (x=1) (x); + eval("let (x=1) (x)"); var success = false; } catch (e) { success = true; } is(success, true, "JS 1.7 should not work in versionless HTML script tags"); ]]></script> </pre> </body> </html>
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1928,22 +1928,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume happens, setting status isn't a big requirement, so don't. (Doesn't happen under normal circumstances, but bug 49615 describes a case.) */ nsContentUtils::AddScriptRunner( NS_NewRunnableMethod(this, &nsGlobalWindow::ClearStatus)); bool reUseInnerWindow = aForceReuseInnerWindow || wouldReuseInnerWindow; - // Remember the old document's principal. - nsIPrincipal *oldPrincipal = nsnull; - if (oldDoc) { - oldPrincipal = oldDoc->NodePrincipal(); - } - nsresult rv = NS_OK; // Set mDocument even if this is an outer window to avoid // having to *always* reach into the inner window to find the // document. mDocument = do_QueryInterface(aDocument); mDoc = aDocument; @@ -1951,16 +1945,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume mLastOpenedURI = aDocument->GetDocumentURI(); #endif mContext->WillInitializeContext(); nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); nsRefPtr<nsGlobalWindow> newInnerWindow; + bool createdInnerWindow = false; bool thisChrome = IsChromeWindow(); bool isChrome = false; nsCxPusher cxPusher; if (!cxPusher.Push(cx)) { return NS_ERROR_FAILURE; @@ -2029,16 +2024,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume rv = mContext->CreateNativeGlobalForInner(sgo, isChrome, aDocument->NodePrincipal(), &newInnerWindow->mJSObject, getter_AddRefs(holder)); NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerWindow->mJSObject && holder, "Failed to get script global and holder"); mCreatingInnerWindow = false; + createdInnerWindow = true; Thaw(); NS_ENSURE_SUCCESS(rv, rv); } if (currentInner && currentInner->mJSObject) { if (oldDoc == aDocument) { // Move the navigator from the old inner window to the new one since @@ -2109,16 +2105,29 @@ nsGlobalWindow::SetNewDocument(nsIDocume cx, newInnerWindow->mJSObject }; priv->waiverWrapperMap->Enumerate(ReparentWaiverWrappers, &closure); } } } + // If we created a new inner window above, we need to do the last little bit + // of initialization now that the dust has settled. + if (createdInnerWindow) { + nsIXPConnect *xpc = nsContentUtils::XPConnect(); + nsCOMPtr<nsIXPConnectWrappedNative> wrapper; + nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerWindow->mJSObject, + getter_AddRefs(wrapper)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ABORT_IF_FALSE(wrapper, "bad wrapper"); + rv = wrapper->FinishInitForWrappedGlobal(); + NS_ENSURE_SUCCESS(rv, rv); + } + JSAutoEnterCompartment ac; if (!ac.enter(cx, mJSObject)) { NS_ERROR("unable to enter a compartment"); return NS_ERROR_FAILURE; } // XXX Not sure if this is needed. if (aState) { @@ -4688,17 +4697,17 @@ nsGlobalWindow::MakeScriptDialogTitle(ns nsCAutoString prepath; fixedURI->GetPrePath(prepath); NS_ConvertUTF8toUTF16 ucsPrePath(prepath); const PRUnichar *formatStrings[] = { ucsPrePath.get() }; nsXPIDLString tempString; nsContentUtils::FormatLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDlgHeading", - formatStrings, ArrayLength(formatStrings), + formatStrings, tempString); aOutTitle = tempString; } } } } } else { // failed to get subject principal
--- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -142,31 +142,34 @@ static PRLogModuleInfo* gJSDiagnostics; // and doing the actual CC. #define NS_CC_DELAY 6000 // ms #define NS_CC_SKIPPABLE_DELAY 400 // ms // Force a CC after this long if there's anything in the purple buffer. #define NS_CC_FORCED (2 * 60 * PR_USEC_PER_SEC) // 2 min +// Don't allow an incremental GC to lock out the CC for too long. +#define NS_MAX_CC_LOCKEDOUT_TIME (5 * PR_USEC_PER_SEC) // 5 seconds + // Trigger a CC if the purple buffer exceeds this size when we check it. #define NS_CC_PURPLE_LIMIT 250 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT // if you add statics here, add them to the list in nsJSRuntime::Startup static nsITimer *sGCTimer; static nsITimer *sShrinkGCBuffersTimer; static nsITimer *sCCTimer; static PRTime sLastCCEndTime; -static bool sGCHasRun; static bool sCCLockedOut; +static PRTime sCCLockedOutTime; static js::GCSliceCallback sPrevGCSliceCallback; // The number of currently pending document loads. This count isn't // guaranteed to always reflect reality and can't easily as we don't // have an easy place to know when a load ends or is interrupted in // all cases. This counter also gets reset if we end up GC'ing while // we're waiting for a slow page to load. IOW, this count may be 0 @@ -807,17 +810,17 @@ nsJSContext::DOMOperationCallback(JSCont if (script) { const char *filename = ::JS_GetScriptFilename(cx, script); if (filename) { nsXPIDLString scriptLocation; NS_ConvertUTF8toUTF16 filenameUTF16(filename); const PRUnichar *formatParams[] = { filenameUTF16.get() }; rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, "KillScriptLocation", - formatParams, 1, + formatParams, scriptLocation); if (NS_SUCCEEDED(rv) && scriptLocation) { msg.AppendLiteral("\n\n"); msg.Append(scriptLocation); JSStackFrame *fp, *iterator = nsnull; fp = ::JS_FrameIterator(cx, &iterator); @@ -2309,29 +2312,21 @@ nsJSContext::CreateOuterObject(nsIScript return SetOuterObject(outer); } nsresult nsJSContext::SetOuterObject(JSObject* aOuterObject) { // Force our context's global object to be the outer. + // NB: JS_SetGlobalObject sets mContext->compartment. JS_SetGlobalObject(mContext, aOuterObject); - // NB: JS_SetGlobalObject sets mContext->compartment. + // Set up the prototype for the outer object. JSObject *inner = JS_GetParent(aOuterObject); - - nsIXPConnect *xpc = nsContentUtils::XPConnect(); - nsCOMPtr<nsIXPConnectWrappedNative> wrapper; - nsresult rv = xpc->GetWrappedNativeOfJSObject(mContext, inner, - getter_AddRefs(wrapper)); - NS_ENSURE_SUCCESS(rv, rv); - NS_ABORT_IF_FALSE(wrapper, "bad wrapper"); - - wrapper->FinishInitForWrappedGlobal(); JS_SetPrototype(mContext, aOuterObject, JS_GetPrototype(inner)); return NS_OK; } nsresult nsJSContext::InitOuterWindow() { @@ -3313,19 +3308,34 @@ TimerFireForgetSkippable(PRUint32 aSuspe sTotalForgetSkippableTime += delta; sRemovedPurples += (aSuspected - sPreviousSuspectedCount); ++sForgetSkippableBeforeCC; } static void CCTimerFired(nsITimer *aTimer, void *aClosure) { - if (sDidShutdown || sCCLockedOut) { + if (sDidShutdown) { return; } + + if (sCCLockedOut) { + PRTime now = PR_Now(); + if (sCCLockedOutTime == 0) { + sCCLockedOutTime = now; + return; + } + if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) { + return; + } + + // Finish the current incremental GC + nsJSContext::GarbageCollectNow(js::gcreason::CC_FORCED, nsGCNormal); + } + ++sCCTimerFireCount; // During early timer fires, we only run forgetSkippable. During the first // late timer fire, we decide if we are going to have a second and final // late timer fire, where we may run the CC. const PRUint32 numEarlyTimerFires = NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY - 2; bool isLateTimerFire = sCCTimerFireCount > numEarlyTimerFires; PRUint32 suspected = nsCycleCollector_suspectedCount(); @@ -3481,16 +3491,18 @@ nsJSContext::KillShrinkGCBuffersTimer() NS_RELEASE(sShrinkGCBuffersTimer); } } //static void nsJSContext::KillCCTimer() { + sCCLockedOutTime = 0; + if (sCCTimer) { sCCTimer->Cancel(); NS_RELEASE(sCCTimer); } } void @@ -3519,63 +3531,52 @@ DOMGCSliceCallback(JSRuntime *aRt, js::G double(delta) / PR_USEC_PER_SEC, aDesc.logMessage)); nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); if (cs) { cs->LogStringMessage(msg.get()); } } - // Prevent cycle collections during incremental GC. + // Prevent cycle collections and shrinking during incremental GC. if (aProgress == js::GC_CYCLE_BEGIN) { sCCLockedOut = true; + nsJSContext::KillShrinkGCBuffersTimer(); } else if (aProgress == js::GC_CYCLE_END) { sCCLockedOut = false; } // The GC has more work to do, so schedule another GC slice. if (aProgress == js::GC_SLICE_END) { nsJSContext::KillGCTimer(); - nsJSContext::KillCCTimer(); - nsJSContext::PokeGC(js::gcreason::INTER_SLICE_GC, NS_INTERSLICE_GC_DELAY); } if (aProgress == js::GC_CYCLE_END) { + // May need to kill the inter-slice GC timer + nsJSContext::KillGCTimer(); + sCCollectedWaitingForGC = 0; sCleanupSinceLastGC = false; - if (sGCTimer) { - // If we were waiting for a GC to happen, kill the timer. - nsJSContext::KillGCTimer(); - + if (aDesc.isCompartment) { // If this is a compartment GC, restart it. We still want // a full GC to happen. Compartment GCs usually happen as a - // result of last-ditch or MaybeGC. In both cases its + // result of last-ditch or MaybeGC. In both cases it is // probably a time of heavy activity and we want to delay // the full GC, but we do want it to happen eventually. - if (aDesc.isCompartment) { - nsJSContext::PokeGC(js::gcreason::POST_COMPARTMENT); - - // We poked the GC, so we can kill any pending CC here. - nsJSContext::KillCCTimer(); - } - } else { - // If this was a full GC, poke the CC to run soon. - if (!aDesc.isCompartment) { - sGCHasRun = true; - sNeedsFullCC = true; - nsJSContext::MaybePokeCC(); - } + nsJSContext::PokeGC(js::gcreason::POST_COMPARTMENT); } - // If we didn't end up scheduling a GC, make sure that we release GC buffers - // soon after canceling previous shrinking attempt. - nsJSContext::KillShrinkGCBuffersTimer(); - if (!sGCTimer) { + sNeedsFullCC = true; + nsJSContext::MaybePokeCC(); + + if (!aDesc.isCompartment) { + // Avoid shrinking during heavy activity, which is suggested by + // compartment GC. nsJSContext::PokeShrinkGCBuffers(); } } if (sPrevGCSliceCallback) (*sPrevGCSliceCallback)(aRt, aProgress, aDesc); } @@ -3664,18 +3665,18 @@ nsJSRuntime::ParseVersion(const nsString } //static void nsJSRuntime::Startup() { // initialize all our statics, so that we can restart XPCOM sGCTimer = sCCTimer = nsnull; - sGCHasRun = false; sCCLockedOut = false; + sCCLockedOutTime = 0; sLastCCEndTime = 0; sPendingLoadCount = 0; sLoadingInProgress = false; sCCollectedWaitingForGC = 0; sPostGCEventsToConsole = false; sNeedsFullCC = false; gNameSpaceManager = nsnull; sRuntimeService = nsnull;
--- a/dom/bluetooth/BluetoothAdapter.cpp +++ b/dom/bluetooth/BluetoothAdapter.cpp @@ -4,24 +4,70 @@ * 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 "nsDOMClassInfo.h" #include "nsDOMEvent.h" #include "nsThreadUtils.h" #include "nsXPCOMCIDInternal.h" #include "mozilla/LazyIdleThread.h" - +#include <dlfcn.h> #include "BluetoothAdapter.h" -#if defined(MOZ_WIDGET_GONK) -#include <bluedroid/bluetooth.h> -#endif +USING_BLUETOOTH_NAMESPACE + +static struct BluedroidFunctions { + bool initialized; + bool tried_initialization; + + BluedroidFunctions() : + initialized(false), + tried_initialization(false) + { + } + + int (* bt_enable)(); + int (* bt_disable)(); + int (* bt_is_enabled)(); +} sBluedroidFunctions; + +static bool EnsureBluetoothInit() { + if (sBluedroidFunctions.tried_initialization) + { + return sBluedroidFunctions.initialized; + } -USING_BLUETOOTH_NAMESPACE + sBluedroidFunctions.initialized = false; + sBluedroidFunctions.tried_initialization = true; + + void* handle = dlopen("libbluedroid.so", RTLD_LAZY); + + if(!handle) { + NS_ERROR("Failed to open libbluedroid.so, bluetooth cannot run"); + return false; + } + + sBluedroidFunctions.bt_enable = (int (*)())dlsym(handle, "bt_enable"); + if(sBluedroidFunctions.bt_enable == NULL) { + NS_ERROR("Failed to attach bt_enable function"); + return false; + } + sBluedroidFunctions.bt_disable = (int (*)())dlsym(handle, "bt_disable"); + if(sBluedroidFunctions.bt_disable == NULL) { + NS_ERROR("Failed to attach bt_disable function"); + return false; + } + sBluedroidFunctions.bt_is_enabled = (int (*)())dlsym(handle, "bt_is_enabled"); + if(sBluedroidFunctions.bt_is_enabled == NULL) { + NS_ERROR("Failed to attach bt_is_enabled function"); + return false; + } + sBluedroidFunctions.initialized = true; + return true; +} class ToggleBtResultTask : public nsRunnable { public: ToggleBtResultTask(nsRefPtr<BluetoothAdapter>& adapterPtr, bool result) : mResult(result) { MOZ_ASSERT(!NS_IsMainThread()); @@ -49,53 +95,55 @@ class ToggleBtResultTask : public nsRunn private: nsRefPtr<BluetoothAdapter> mAdapterPtr; bool mResult; }; class ToggleBtTask : public nsRunnable { public: - ToggleBtTask(bool onOff, BluetoothAdapter* adapterPtr) + ToggleBtTask(bool onOff, BluetoothAdapter* adapterPtr) : mOnOff(onOff), mAdapterPtr(adapterPtr) { MOZ_ASSERT(NS_IsMainThread()); } NS_IMETHOD Run() { MOZ_ASSERT(!NS_IsMainThread()); + if(!EnsureBluetoothInit()) { + NS_ERROR("Failed to load bluedroid library.\n"); + return NS_ERROR_FAILURE; + } + bool result; //Toggle BT here -#if defined(MOZ_WIDGET_GONK) + if (mOnOff) { - result = bt_enable(); + result = sBluedroidFunctions.bt_enable(); } else { - result = bt_disable(); + result = sBluedroidFunctions.bt_disable(); } -#else - result = true; -#endif // Create a result thread and pass it to Main Thread, nsCOMPtr<nsIRunnable> resultRunnable = new ToggleBtResultTask(mAdapterPtr, result); if (NS_FAILED(NS_DispatchToMainThread(resultRunnable))) { NS_WARNING("Failed to dispatch to main thread!"); } return NS_OK; } private: + bool mOnOff; nsRefPtr<BluetoothAdapter> mAdapterPtr; - bool mOnOff; }; DOMCI_DATA(BluetoothAdapter, BluetoothAdapter) NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothAdapter) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothAdapter, nsDOMEventTargetHelper) @@ -118,28 +166,47 @@ NS_IMPL_RELEASE_INHERITED(BluetoothAdapt BluetoothAdapter::BluetoothAdapter() : mPower(false) { } NS_IMETHODIMP BluetoothAdapter::GetPower(bool* aPower) { +#ifdef MOZ_WIDGET_GONK + if(!EnsureBluetoothInit()) { + NS_ERROR("Failed to load bluedroid library.\n"); + return NS_ERROR_FAILURE; + } + *aPower = sBluedroidFunctions.bt_is_enabled(); +#else *aPower = mPower; - +#endif return NS_OK; } NS_IMETHODIMP BluetoothAdapter::SetPower(bool aPower) { + +#ifdef MOZ_WIDGET_GONK + // Platform specific check for gonk until object is divided in + // different implementations per platform. Linux doesn't require + // bluetooth firmware loading, but code should work otherwise. + if(!EnsureBluetoothInit()) { + NS_ERROR("Failed to load bluedroid library.\n"); + return NS_ERROR_FAILURE; + } +#endif if (mPower != aPower) { mPower = aPower; +#ifdef MOZ_WIDGET_GONK return ToggleBluetoothAsync(); +#endif } return NS_OK; } nsresult BluetoothAdapter::ToggleBluetoothAsync() {
--- a/dom/tests/mochitest/general/file_frameElementWrapping.html +++ b/dom/tests/mochitest/general/file_frameElementWrapping.html @@ -1,26 +1,32 @@ <html> <script> - function check(elt, expectProxy, message) { - netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); - var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindowUtils); - var result = ((utils.getClassName(elt) === 'Proxy') === expectProxy) - ? "PASS" - : "FAIL"; + function check(elt, expectAccess, prop) { + var access = false; + try { + elt[prop]; + access = true; + } + catch (e) {} + return access === expectAccess; + } + function sendMessage(success, sameOrigin, prop) { + var result = success ? 'PASS' : 'FAIL'; + var message; + if (sameOrigin) + message = 'Can access |' + prop + '| if same origin'; + else + message = 'Cannot access |' + prop + '| if not same origin'; parent.postMessage(result + ',' + message, '*'); } - try { - // true if same origin, throws otherwise - var sameOrigin = parent.location.href !== ''; - } catch (e) { - sameOrigin = false; + var sameOrigin = location.host !== 'example.org'; + var pass = check(frameElement, sameOrigin, 'src'); + if (!pass) { + sendMessage(false, sameOrigin, 'src'); + } else { + pass = check(parent.location, sameOrigin, 'href'); + sendMessage(pass, sameOrigin, 'href'); } - - check(frameElement, !sameOrigin, - sameOrigin - ? 'no wrapper needed if same origin' - : 'wrapper needed if not same origin'); </script> </html>
--- a/dom/tests/mochitest/general/test_frameElementWrapping.html +++ b/dom/tests/mochitest/general/test_frameElementWrapping.html @@ -1,24 +1,32 @@ <!DOCTYPE HTML> <html> <head> - <title>Test for location object behaviors</title> + <title>Test for same-origin and cross-origin wrapping of frameElement</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <p id="display"></p> <div id="content" style="display: none"> </div> <iframe id="ifr" src="file_frameElementWrapping.html"></iframe> <pre id="test"> <script class="testbody" type="text/javascript"> +// +// This test has sort of morphed over time to become less and less useful. +// In the past, we had special security policy for frameElement, but that's +// more or less gone away with compartment/proxy wrapping. So we just go +// through the motions to make sure that, indeed, frameElement is subject +// to the same-origin policy. +// + SimpleTest.waitForExplicitFinish(); var count = 0; function runTest(result, message) { ok(result === 'PASS', message); if (++count === 2)
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2920,17 +2920,17 @@ WorkerPrivate::Dispatch(WorkerRunnable* } } if (!aQueue->Push(event)) { return false; } if (aQueue == &mControlQueue && mJSContext) { - JS_TriggerOperationCallback(mJSContext); + JS_TriggerOperationCallback(JS_GetRuntime(mJSContext)); } mCondVar.Notify(); } event.forget(); return true; }
--- a/editor/libeditor/html/nsEditProperty.cpp +++ b/editor/libeditor/html/nsEditProperty.cpp @@ -80,10 +80,10 @@ nsEditProperty::RegisterAtoms() { // inline tags static const nsStaticAtom property_atoms[] = { #define EDITOR_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &name_), #include "nsEditPropertyAtomList.h" #undef EDITOR_ATOM }; - NS_RegisterStaticAtoms(property_atoms, ArrayLength(property_atoms)); + NS_RegisterStaticAtoms(property_atoms); }
--- a/editor/txtsvc/src/nsTextServicesDocument.cpp +++ b/editor/txtsvc/src/nsTextServicesDocument.cpp @@ -129,17 +129,17 @@ void nsTextServicesDocument::RegisterAtoms() { static const nsStaticAtom ts_atoms[] = { #define TS_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &name_), #include "nsTSAtomList.h" #undef TS_ATOM }; - NS_RegisterStaticAtoms(ts_atoms, ArrayLength(ts_atoms)); + NS_RegisterStaticAtoms(ts_atoms); } NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextServicesDocument) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextServicesDocument) NS_INTERFACE_MAP_BEGIN(nsTextServicesDocument) NS_INTERFACE_MAP_ENTRY(nsITextServicesDocument) NS_INTERFACE_MAP_ENTRY(nsIEditActionListener)
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp +++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp @@ -1535,18 +1535,27 @@ PRUint32 nsWindowWatcher::CalculateChrom chromeFlags |= WinHasOption(aFeatures, "extrachrome", 0, nsnull) ? nsIWebBrowserChrome::CHROME_EXTRA : 0; chromeFlags |= WinHasOption(aFeatures, "centerscreen", 0, nsnull) ? nsIWebBrowserChrome::CHROME_CENTER_SCREEN : 0; chromeFlags |= WinHasOption(aFeatures, "dependent", 0, nsnull) ? nsIWebBrowserChrome::CHROME_DEPENDENT : 0; chromeFlags |= WinHasOption(aFeatures, "modal", 0, nsnull) ? (nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT) : 0; - chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nsnull) ? - nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0; + + /* On mobile we want to ignore the dialog window feature, since the mobile UI + does not provide any affordance for dialog windows. This does not interfere + with dialog windows created through openDialog. */ + bool disableDialogFeature = false; + nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs); + branch->GetBoolPref("dom.disable_window_open_dialog_feature", &disableDialogFeature); + if (!disableDialogFeature) { + chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nsnull) ? + nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0; + } /* and dialogs need to have the last word. assume dialogs are dialogs, and opened as chrome, unless explicitly told otherwise. */ if (aDialog) { if (!PL_strcasestr(aFeatures, "dialog")) chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG; if (!PL_strcasestr(aFeatures, "chrome")) chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
--- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -37,16 +37,19 @@ * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "mozilla/Preferences.h" #include "mozilla/Util.h" +#include "nsIScreen.h" +#include "nsIScreenManager.h" + #if defined(XP_UNIX) #ifdef MOZ_WIDGET_GTK2 #include <gdk/gdkx.h> // we're using default display for now #define GET_NATIVE_WINDOW(aWidget) (EGLNativeWindowType)GDK_WINDOW_XID((GdkWindow *) aWidget->GetNativeData(NS_NATIVE_WINDOW)) #elif defined(MOZ_WIDGET_QT) #include <QtOpenGL/QGLContext> @@ -2185,72 +2188,59 @@ static const EGLint kEGLConfigAttribsRGB LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT, LOCAL_EGL_RED_SIZE, 8, LOCAL_EGL_GREEN_SIZE, 8, LOCAL_EGL_BLUE_SIZE, 8, LOCAL_EGL_ALPHA_SIZE, 8, LOCAL_EGL_NONE }; -// This struct is used only by CreateConfig below, but ISO C++98 forbids -// instantiating a template dependent on a locally-defined type. Boo-urns! -struct EGLAttribs { - gfxASurface::gfxImageFormat mFormat; - const EGLint* mAttribs; -}; - // Return true if a suitable EGLConfig was found and pass it out // through aConfig. Return false otherwise. // // NB: It's entirely legal for the returned EGLConfig to be valid yet // have the value null. static bool CreateConfig(EGLConfig* aConfig) { - EGLAttribs attribsToTry[] = { -#ifdef MOZ_GFX_OPTIMIZE_MOBILE - // Prefer r5g6b5 for potential savings in memory bandwidth. - // This needs to be reevaluated for newer devices. - { gfxASurface::ImageFormatRGB16_565, kEGLConfigAttribsRGB16 }, -#endif - { gfxASurface::ImageFormatARGB32, kEGLConfigAttribsRGBA32 }, - }; + nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1"); + nsCOMPtr<nsIScreen> screen; + screenMgr->GetPrimaryScreen(getter_AddRefs(screen)); + PRInt32 depth = 24; + screen->GetColorDepth(&depth); EGLConfig configs[64]; - for (unsigned i = 0; i < ArrayLength(attribsToTry); ++i) { - const EGLAttribs& attribs = attribsToTry[i]; - EGLint ncfg = ArrayLength(configs); - - if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), attribs.mAttribs, - configs, ncfg, &ncfg) || - ncfg < 1) + gfxASurface::gfxImageFormat format; + const EGLint* attribs = depth == 16 ? kEGLConfigAttribsRGB16 : + kEGLConfigAttribsRGBA32; + EGLint ncfg = ArrayLength(configs); + + if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), attribs, + configs, ncfg, &ncfg) || + ncfg < 1) { + return false; + } + + for (int j = 0; j < ncfg; ++j) { + EGLConfig config = configs[j]; + EGLint r, g, b, a; + + if (sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, + LOCAL_EGL_RED_SIZE, &r) && + sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, + LOCAL_EGL_GREEN_SIZE, &g) && + sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, + LOCAL_EGL_BLUE_SIZE, &b) && + sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, + LOCAL_EGL_ALPHA_SIZE, &a) && + ((depth == 16 && r == 5 && g == 6 && b == 5) || + (depth == 24 && r == 8 && g == 8 && b == 8 && a == 8))) { - continue; - } - - for (int j = 0; j < ncfg; ++j) { - EGLConfig config = configs[j]; - EGLint r, g, b, a; - - if (sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, - LOCAL_EGL_RED_SIZE, &r) && - sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, - LOCAL_EGL_GREEN_SIZE, &g) && - sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, - LOCAL_EGL_BLUE_SIZE, &b) && - sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config, - LOCAL_EGL_ALPHA_SIZE, &a) && - ((gfxASurface::ImageFormatRGB16_565 == attribs.mFormat && - r == 5 && g == 6 && b == 5) || - (gfxASurface::ImageFormatARGB32 == attribs.mFormat && - r == 8 && g == 8 && b == 8 && a == 8))) - { - *aConfig = config; - return true; - } + *aConfig = config; + return true; } } return false; } static EGLSurface CreateSurfaceForWindow(nsIWidget *aWidget, EGLConfig config) {
--- a/gfx/thebes/gfxAtoms.cpp +++ b/gfx/thebes/gfxAtoms.cpp @@ -53,10 +53,10 @@ using namespace mozilla; static const nsStaticAtom atoms[] = { #define GFX_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &gfxAtoms::name_), #include "gfxAtomList.h" #undef GFX_ATOM }; void gfxAtoms::RegisterAtoms() { - NS_RegisterStaticAtoms(atoms, ArrayLength(atoms)); + NS_RegisterStaticAtoms(atoms); }
--- a/gfx/thebes/gfxDWriteFontList.cpp +++ b/gfx/thebes/gfxDWriteFontList.cpp @@ -63,16 +63,20 @@ using namespace mozilla; PR_LOG_DEBUG) #define LOG_FONTINIT(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \ PR_LOG_DEBUG, args) #define LOG_FONTINIT_ENABLED() PR_LOG_TEST( \ gfxPlatform::GetLog(eGfxLog_fontinit), \ PR_LOG_DEBUG) +#define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \ + gfxPlatform::GetLog(eGfxLog_cmapdata), \ + PR_LOG_DEBUG) + // font info loader constants // avoid doing this during startup even on slow machines but try to start // it soon enough so that system fallback doesn't happen first static const PRUint32 kDelayBeforeLoadingFonts = 120 * 1000; // 2 minutes after init static const PRUint32 kIntervalBetweenLoadingFonts = 2000; // every 2 seconds until complete static __inline void @@ -366,16 +370,26 @@ gfxDWriteFontEntry::ReadCMAP() if (GetFontTable(kCmapTag, buffer) != NS_OK) return NS_ERROR_FAILURE; PRUint8 *cmap = buffer.Elements(); bool unicodeFont = false, symbolFont = false; rv = gfxFontUtils::ReadCMAP(cmap, buffer.Length(), mCharacterMap, mUVSOffset, unicodeFont, symbolFont); +#ifdef PR_LOGGING + LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n", + NS_ConvertUTF16toUTF8(mName).get(), mCharacterMap.GetSize())); + if (LOG_CMAPDATA_ENABLED()) { + char prefix[256]; + sprintf(prefix, "(cmapdata) name: %.220s", + NS_ConvertUTF16toUTF8(mName).get()); + mCharacterMap.Dump(prefix, eGfxLog_cmapdata); + } +#endif mHasCmapTable = NS_SUCCEEDED(rv); return rv; } // loading using dwrite, don't use GetFontTable to avoid copy nsRefPtr<IDWriteFontFace> fontFace; rv = CreateFontFace(getter_AddRefs(fontFace)); @@ -406,16 +420,22 @@ gfxDWriteFontEntry::ReadCMAP() isUnicode, isSymbol); } fontFace->ReleaseFontTable(tableContext); #ifdef PR_LOGGING LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n", NS_ConvertUTF16toUTF8(mName).get(), mCharacterMap.GetSize())); + if (LOG_CMAPDATA_ENABLED()) { + char prefix[256]; + sprintf(prefix, "(cmapdata) name: %.220s", + NS_ConvertUTF16toUTF8(mName).get()); + mCharacterMap.Dump(prefix, eGfxLog_cmapdata); + } #endif mHasCmapTable = NS_SUCCEEDED(rv); return rv; } gfxFont * gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, @@ -1169,8 +1189,187 @@ gfxDWriteFontList::ResolveFontName(const } if (mNonExistingFonts.Contains(keyName)) { return false; } return gfxPlatformFontList::ResolveFontName(aFontName, aResolvedFontName); } + +static nsresult GetFamilyName(IDWriteFont *aFont, nsString& aFamilyName) +{ + HRESULT hr; + nsRefPtr<IDWriteFontFamily> family; + + // clean out previous value + aFamilyName.Truncate(); + + hr = aFont->GetFontFamily(getter_AddRefs(family)); + if (FAILED(hr)) { + return hr; + } + + nsRefPtr<IDWriteLocalizedStrings> familyNames; + + hr = family->GetFamilyNames(getter_AddRefs(familyNames)); + if (FAILED(hr)) { + return hr; + } + + UINT32 index = 0; + BOOL exists = false; + + hr = familyNames->FindLocaleName(L"en-us", &index, &exists); + if (FAILED(hr)) { + return hr; + } + + // If the specified locale doesn't exist, select the first on the list. + if (!exists) { + index = 0; + } + + nsAutoTArray<WCHAR, 32> name; + UINT32 length; + + hr = familyNames->GetStringLength(index, &length); + if (FAILED(hr)) { + return hr; + } + + if (!name.SetLength(length + 1)) { + return NS_ERROR_FAILURE; + } + hr = familyNames->GetString(index, name.Elements(), length + 1); + if (FAILED(hr)) { + return hr; + } + + aFamilyName.Assign(name.Elements()); + return NS_OK; +} + +// bug 705594 - the method below doesn't actually do any "drawing", it's only +// used to invoke the DirectWrite layout engine to determine the fallback font +// for a given character. + +IFACEMETHODIMP FontFallbackRenderer::DrawGlyphRun( + __maybenull void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_MEASURING_MODE measuringMode, + __in DWRITE_GLYPH_RUN const* glyphRun, + __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, + __maybenull IUnknown* clientDrawingEffect + ) +{ + if (!mSystemFonts) { + return E_FAIL; + } + + HRESULT hr = S_OK; + + nsRefPtr<IDWriteFont> font; + hr = mSystemFonts->GetFontFromFontFace(glyphRun->fontFace, + getter_AddRefs(font)); + if (FAILED(hr)) { + return hr; + } + + // copy the family name + hr = GetFamilyName(font, mFamilyName); + if (FAILED(hr)) { + return hr; + } + + // Arial is used as the default fallback font + // so if it matches ==> no font found + if (mFamilyName.EqualsLiteral("Arial")) { + mFamilyName.Truncate(); + return E_FAIL; + } + return hr; +} + +gfxFontEntry* +gfxDWriteFontList::GlobalFontFallback(const PRUint32 aCh, + PRInt32 aRunScript, + const gfxFontStyle* aMatchStyle, + PRUint32& aCmapCount) +{ + bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); + + if (useCmaps) { + return gfxPlatformFontList::GlobalFontFallback(aCh, + aRunScript, + aMatchStyle, + aCmapCount); + } + + HRESULT hr; + + nsRefPtr<IDWriteFactory> dwFactory = + gfxWindowsPlatform::GetPlatform()->GetDWriteFactory(); + if (!dwFactory) { + return nsnull; + } + + // initialize fallback renderer + if (!mFallbackRenderer) { + mFallbackRenderer = new FontFallbackRenderer(dwFactory); + } + + // initialize text format + if (!mFallbackFormat) { + hr = dwFactory->CreateTextFormat(L"Arial", NULL, + DWRITE_FONT_WEIGHT_REGULAR, + DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, + 72.0f, L"en-us", + getter_AddRefs(mFallbackFormat)); + if (FAILED(hr)) { + return nsnull; + } + } + + // set up string with fallback character + wchar_t str[16]; + PRUint32 strLen; + + if (IS_IN_BMP(aCh)) { + str[0] = static_cast<wchar_t> (aCh); + str[1] = 0; + strLen = 1; + } else { + str[0] = static_cast<wchar_t> (H_SURROGATE(aCh)); + str[1] = static_cast<wchar_t> (L_SURROGATE(aCh)); + str[2] = 0; + strLen = 2; + } + + // set up layout + nsRefPtr<IDWriteTextLayout> fallbackLayout; + + hr = dwFactory->CreateTextLayout(str, strLen, mFallbackFormat, + 200.0f, 200.0f, + getter_AddRefs(fallbackLayout)); + if (FAILED(hr)) { + return nsnull; + } + + // call the draw method to invoke the DirectWrite layout functions + // which determine the fallback font + hr = fallbackLayout->Draw(NULL, mFallbackRenderer, 50.0f, 50.0f); + if (FAILED(hr)) { + return nsnull; + } + + gfxFontEntry *fontEntry = nsnull; + bool needsBold; // ignored in the system fallback case + fontEntry = FindFontForFamily(mFallbackRenderer->FallbackFamilyName(), + aMatchStyle, needsBold); + if (fontEntry && !fontEntry->TestCharacterMap(aCh)) { + fontEntry = nsnull; + Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, true); + } + return fontEntry; +}
--- a/gfx/thebes/gfxDWriteFontList.h +++ b/gfx/thebes/gfxDWriteFontList.h @@ -199,16 +199,155 @@ protected: nsRefPtr<IDWriteFont> mFont; nsRefPtr<IDWriteFontFile> mFontFile; DWRITE_FONT_FACE_TYPE mFaceType; PRInt8 mIsCJK; bool mForceGDIClassic; }; +// custom text renderer used to determine the fallback font for a given char +class FontFallbackRenderer : public IDWriteTextRenderer +{ +public: + FontFallbackRenderer(IDWriteFactory *aFactory) + : mRefCount(0) + { + HRESULT hr = S_OK; + + hr = aFactory->GetSystemFontCollection(getter_AddRefs(mSystemFonts)); + NS_ASSERTION(SUCCEEDED(hr), "GetSystemFontCollection failed!"); + } + + ~FontFallbackRenderer() + {} + + // IDWriteTextRenderer methods + IFACEMETHOD(DrawGlyphRun)( + __maybenull void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_MEASURING_MODE measuringMode, + __in DWRITE_GLYPH_RUN const* glyphRun, + __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, + __maybenull IUnknown* clientDrawingEffect + ); + + IFACEMETHOD(DrawUnderline)( + __maybenull void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + __in DWRITE_UNDERLINE const* underline, + __maybenull IUnknown* clientDrawingEffect + ) + { + return E_NOTIMPL; + } + + + IFACEMETHOD(DrawStrikethrough)( + __maybenull void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + __in DWRITE_STRIKETHROUGH const* strikethrough, + __maybenull IUnknown* clientDrawingEffect + ) + { + return E_NOTIMPL; + } + + + IFACEMETHOD(DrawInlineObject)( + __maybenull void* clientDrawingContext, + FLOAT originX, + FLOAT originY, + IDWriteInlineObject* inlineObject, + BOOL isSideways, + BOOL isRightToLeft, + __maybenull IUnknown* clientDrawingEffect + ) + { + return E_NOTIMPL; + } + + // IDWritePixelSnapping methods + + IFACEMETHOD(IsPixelSnappingDisabled)( + __maybenull void* clientDrawingContext, + __out BOOL* isDisabled + ) + { + *isDisabled = FALSE; + return S_OK; + } + + IFACEMETHOD(GetCurrentTransform)( + __maybenull void* clientDrawingContext, + __out DWRITE_MATRIX* transform + ) + { + const DWRITE_MATRIX ident = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0}; + *transform = ident; + return S_OK; + } + + IFACEMETHOD(GetPixelsPerDip)( + __maybenull void* clientDrawingContext, + __out FLOAT* pixelsPerDip + ) + { + *pixelsPerDip = 1.0f; + return S_OK; + } + + // IUnknown methods + + IFACEMETHOD_(unsigned long, AddRef) () + { + return InterlockedIncrement(&mRefCount); + } + + IFACEMETHOD_(unsigned long, Release) () + { + unsigned long newCount = InterlockedDecrement(&mRefCount); + if (newCount == 0) + { + delete this; + return 0; + } + + return newCount; + } + + IFACEMETHOD(QueryInterface) (IID const& riid, void** ppvObject) + { + if (__uuidof(IDWriteTextRenderer) == riid) { + *ppvObject = this; + } else if (__uuidof(IDWritePixelSnapping) == riid) { + *ppvObject = this; + } else if (__uuidof(IUnknown) == riid) { + *ppvObject = this; + } else { + *ppvObject = NULL; + return E_FAIL; + } + + this->AddRef(); + return S_OK; + } + + const nsString& FallbackFamilyName() { return mFamilyName; } + +protected: + unsigned long mRefCount; + nsRefPtr<IDWriteFontCollection> mSystemFonts; + nsString mFamilyName; +}; + + class gfxDWriteFontList : public gfxPlatformFontList { public: gfxDWriteFontList(); static gfxDWriteFontList* PlatformFontList() { return static_cast<gfxDWriteFontList*>(sPlatformFontList); } @@ -243,16 +382,24 @@ public: private: friend class gfxDWriteFontFamily; nsresult GetFontSubstitutes(); void GetDirectWriteSubstitutes(); + // search fonts system-wide for a given character, null otherwise + virtual gfxFontEntry* GlobalFontFallback(const PRUint32 aCh, + PRInt32 aRunScript, + const gfxFontStyle* aMatchStyle, + PRUint32& aCmapCount); + + virtual bool UsesSystemFallback() { return true; } + /** * Fonts listed in the registry as substitutes but for which no actual * font family is found. */ nsTArray<nsString> mNonExistingFonts; typedef nsDataHashtable<nsStringHashKey, nsRefPtr<gfxFontFamily> > FontTable; @@ -265,12 +412,15 @@ private: bool mInitialized; virtual nsresult DelayedInitFontList(); gfxFloat mForceGDIClassicMaxFontSize; // whether to use GDI font table access routines bool mGDIFontTableAccess; nsRefPtr<IDWriteGdiInterop> mGDIInterop; + + nsRefPtr<FontFallbackRenderer> mFallbackRenderer; + nsRefPtr<IDWriteTextFormat> mFallbackFormat; }; #endif /* GFX_DWRITEFONTLIST_H */
--- a/gfx/thebes/gfxFT2Fonts.cpp +++ b/gfx/thebes/gfxFT2Fonts.cpp @@ -392,21 +392,22 @@ gfxFT2FontGroup::WhichPrefFontSupportsCh nsRefPtr<gfxFont> f = static_cast<gfxFont*>(selectedFont.get()); return f.forget(); } return nsnull; } already_AddRefed<gfxFont> -gfxFT2FontGroup::WhichSystemFontSupportsChar(PRUint32 aCh) +gfxFT2FontGroup::WhichSystemFontSupportsChar(PRUint32 aCh, PRInt32 aRunScript) { #if defined(XP_WIN) || defined(ANDROID) FontEntry *fe = static_cast<FontEntry*> - (gfxPlatformFontList::PlatformFontList()->FindFontForChar(aCh, GetFontAt(0))); + (gfxPlatformFontList::PlatformFontList()-> + SystemFindFontForChar(aCh, aRunScript, &mStyle)); if (fe) { nsRefPtr<gfxFT2Font> f = gfxFT2Font::GetOrMakeFont(fe, &mStyle); nsRefPtr<gfxFont> font = f.get(); return font.forget(); } #else nsRefPtr<gfxFont> selectedFont; nsRefPtr<gfxFont> refFont = GetFontAt(0);
--- a/gfx/thebes/gfxFT2Fonts.h +++ b/gfx/thebes/gfxFT2Fonts.h @@ -136,17 +136,18 @@ protected: // new functions nsTArray<nsRefPtr<gfxFontEntry> >& aFontEntryList); void GetCJKPrefFonts(nsTArray<nsRefPtr<gfxFontEntry> >& aFontEntryList); void FamilyListToArrayList(const nsString& aFamilies, nsIAtom *aLangGroup, nsTArray<nsRefPtr<gfxFontEntry> > *aFontEntryList); already_AddRefed<gfxFT2Font> WhichFontSupportsChar(const nsTArray<nsRefPtr<gfxFontEntry> >& aFontEntryList, PRUint32 aCh); already_AddRefed<gfxFont> WhichPrefFontSupportsChar(PRUint32 aCh); - already_AddRefed<gfxFont> WhichSystemFontSupportsChar(PRUint32 aCh); + already_AddRefed<gfxFont> + WhichSystemFontSupportsChar(PRUint32 aCh, PRInt32 aRunScript); nsTArray<gfxTextRange> mRanges; nsString mString; }; #endif // !ANDROID #endif /* GFX_FT2FONTS_H */
--- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -685,102 +685,125 @@ gfxFontFamily::FindWeightsForStyle(gfxFo void gfxFontFamily::LocalizedName(nsAString& aLocalizedName) { // just return the primary name; subclasses should override aLocalizedName = mName; } +// metric for how close a given font matches a style +static PRInt32 +CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle) +{ + PRInt32 rank = 0; + if (aStyle) { + // italics + bool wantItalic = + ((aStyle->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0); + if (aFontEntry->IsItalic() == wantItalic) { + rank += 10; + } + + // measure of closeness of weight to the desired value + rank += 9 - abs(aFontEntry->Weight() / 100 - aStyle->ComputeWeight()); + } else { + // if no font to match, prefer non-bold, non-italic fonts + if (!aFontEntry->IsItalic()) { + rank += 3; + } + if (!aFontEntry->IsBold()) { + rank += 2; + } + } + + return rank; +} + +#define RANK_MATCHED_CMAP 20 void -gfxFontFamily::FindFontForChar(FontSearch *aMatchData) +gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData) { - if (!mHasStyles) { - FindStyleVariations(); - } - - if (!TestCharacterMap(aMatchData->mCh)) { + if (mCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) { // none of the faces in the family support the required char, // so bail out immediately return; } - // iterate over fonts - PRUint32 numFonts = mAvailableFonts.Length(); - for (PRUint32 i = 0; i < numFonts; i++) { - gfxFontEntry *fe = mAvailableFonts[i]; - - // skip certain fonts during system fallback - if (!fe || fe->SkipDuringSystemFallback()) - continue; - + bool needsBold; + gfxFontStyle normal; + gfxFontEntry *fe = FindFontForStyle( + (aMatchData->mStyle == nsnull) ? *aMatchData->mStyle : normal, + needsBold); + + if (fe && !fe->SkipDuringSystemFallback()) { PRInt32 rank = 0; if (fe->TestCharacterMap(aMatchData->mCh)) { - rank += 20; + rank += RANK_MATCHED_CMAP; aMatchData->mCount++; #ifdef PR_LOGGING PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun); - + if (NS_UNLIKELY(log)) { PRUint32 charRange = gfxFontUtils::CharRangeBit(aMatchData->mCh); PRUint32 unicodeRange = FindCharUnicodeRange(aMatchData->mCh); PRUint32 script = GetScriptCode(aMatchData->mCh); PR_LOG(log, PR_LOG_DEBUG,\ ("(textrun-systemfallback-fonts) char: u+%6.6x " "char-range: %d unicode-range: %d script: %d match: [%s]\n", aMatchData->mCh, charRange, unicodeRange, script, NS_ConvertUTF16toUTF8(fe->Name()).get())); } #endif } - // if we didn't match any characters don't bother wasting more time with this face. - if (rank == 0) - continue; - - // omitting from original windows code -- family name, lang group, pitch - // not available in current FontEntry implementation - - if (aMatchData->mFontToMatch) { - const gfxFontStyle *style = aMatchData->mFontToMatch->GetStyle(); - - // matching italics takes precedence over weight - bool wantItalic = - ((style->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0); - if (fe->IsItalic() == wantItalic) { - rank += 10; - } - - // measure of closeness of weight to the desired value - rank += 9 - abs(fe->Weight() / 100 - style->ComputeWeight()); - } else { - // if no font to match, prefer non-bold, non-italic fonts - if (!fe->IsItalic()) { - rank += 3; - } - if (!fe->IsBold()) { - rank += 2; - } + aMatchData->mCmapsTested++; + if (rank == 0) { + return; } - + + // omitting from original windows code -- family name, lang group, pitch + // not available in current FontEntry implementation + rank += CalcStyleMatch(fe, aMatchData->mStyle); + // xxx - add whether AAT font with morphing info for specific lang groups - + if (rank > aMatchData->mMatchRank || (rank == aMatchData->mMatchRank && - Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) + Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) { aMatchData->mBestMatch = fe; aMatchData->mMatchRank = rank; } } } +void +gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData) +{ + PRUint32 i, numFonts = mAvailableFonts.Length(); + for (i = 0; i < numFonts; i++) { + gfxFontEntry *fe = mAvailableFonts[i]; + if (fe && fe->TestCharacterMap(aMatchData->mCh)) { + PRInt32 rank = RANK_MATCHED_CMAP; + rank += CalcStyleMatch(fe, aMatchData->mStyle); + if (rank > aMatchData->mMatchRank + || (rank == aMatchData->mMatchRank && + Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) + { + aMatchData->mBestMatch = fe; + aMatchData->mMatchRank = rank; + } + } + } +} + // returns true if other names were found, false otherwise bool gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList, FallibleTArray<PRUint8>& aNameTable, bool useFullName) { const PRUint8 *nameData = aNameTable.Elements(); PRUint32 dataLength = aNameTable.Length(); @@ -3148,17 +3171,17 @@ gfxFontGroup::InitTextRun(gfxContext *aC PRInt32 runScript = MOZ_SCRIPT_LATIN; while (scriptRuns.Next(runStart, runLimit, runScript)) { #ifdef PR_LOGGING if (NS_UNLIKELY(log)) { nsCAutoString lang; mStyle.language->ToUTF8String(lang); PRUint32 runLen = runLimit - runStart; - PR_LOG(log, PR_LOG_DEBUG,\ + PR_LOG(log, PR_LOG_WARNING,\ ("(%s) fontgroup: [%s] lang: %s script: %d len %d " "weight: %d width: %d style: %s " "TEXTRUN [%s] ENDTEXTRUN\n", (mStyle.systemFont ? "textrunui" : "textrun"), NS_ConvertUTF16toUTF8(mFamilies).get(), lang.get(), runScript, runLen, PRUint32(mStyle.weight), PRUint32(mStyle.stretch), (mStyle.style & FONT_STYLE_ITALIC ? "italic" : @@ -3354,21 +3377,22 @@ gfxFontGroup::FindFontForChar(PRUint32 a // 1. check fonts in the font group for (PRUint32 i = 0; i < FontListLength(); i++) { nsRefPtr<gfxFont> font = GetFontAt(i); if (font->HasCharacter(aCh)) { *aMatchType = gfxTextRange::kFontGroup; return font.forget(); } + // check other faces of the family gfxFontFamily *family = font->GetFontEntry()->Family(); if (family && family->TestCharacterMap(aCh)) { - FontSearch matchData(aCh, font); - family->FindFontForChar(&matchData); + GlobalFontMatch matchData(aCh, aRunScript, &mStyle); + family->SearchAllFontsForChar(&matchData); gfxFontEntry *fe = matchData.mBestMatch; if (fe) { bool needsBold = font->GetStyle()->weight >= 600 && !fe->IsBold(); selectedFont = fe->FindOrMakeFont(font->GetStyle(), needsBold); if (selectedFont) { return selectedFont.forget(); @@ -3390,29 +3414,34 @@ gfxFontGroup::FindFontForChar(PRUint32 a // 3. use fallback fonts // -- before searching for something else check the font used for the previous character if (!selectedFont && aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) { *aMatchType = gfxTextRange::kSystemFallback; selectedFont = aPrevMatchedFont; return selectedFont.forget(); } + // never fall back for characters from unknown scripts + if (aRunScript == HB_SCRIPT_UNKNOWN) { + return nsnull; + } + // for known "space" characters, don't do a full system-fallback search; // we'll synthesize appropriate-width spaces instead of missing-glyph boxes if (GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR && GetFontAt(0)->SynthesizeSpaceWidth(aCh) >= 0.0) { return nsnull; } // -- otherwise look for other stuff if (!selectedFont) { *aMatchType = gfxTextRange::kSystemFallback; - selectedFont = WhichSystemFontSupportsChar(aCh); + selectedFont = WhichSystemFontSupportsChar(aCh, aRunScript); return selectedFont.forget(); } return nsnull; } template<typename T> void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges, @@ -3547,20 +3576,16 @@ struct PrefFontCallbackData { } }; already_AddRefed<gfxFont> gfxFontGroup::WhichPrefFontSupportsChar(PRUint32 aCh) { gfxFont *font; - // FindCharUnicodeRange only supports BMP character points and there are no non-BMP fonts in prefs - if (aCh > 0xFFFF) - return nsnull; - // get the pref font list if it hasn't been set up already PRUint32 unicodeRange = FindCharUnicodeRange(aCh); eFontPrefLang charLang = gfxPlatform::GetPlatform()->GetFontPrefLangFor(unicodeRange); // if the last pref font was the first family in the pref list, no need to recheck through a list of families if (mLastPrefFont && charLang == mLastPrefLang && mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) { font = mLastPrefFont; @@ -3622,22 +3647,24 @@ gfxFontGroup::WhichPrefFontSupportsChar( } } return nsnull; } already_AddRefed<gfxFont> -gfxFontGroup::WhichSystemFontSupportsChar(PRUint32 aCh) +gfxFontGroup::WhichSystemFontSupportsChar(PRUint32 aCh, PRInt32 aRunScript) { gfxFontEntry *fe = - gfxPlatformFontList::PlatformFontList()->FindFontForChar(aCh, GetFontAt(0)); + gfxPlatformFontList::PlatformFontList()-> + SystemFindFontForChar(aCh, aRunScript, &mStyle); if (fe) { - nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, false); // ignore bolder considerations in system fallback case... + // ignore bolder considerations in system fallback case... + nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, false); return font.forget(); } return nsnull; } /*static*/ void gfxFontGroup::Shutdown()
--- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -482,26 +482,34 @@ private: nsTHashtable<FontTableHashEntry> mFontTableCache; gfxFontEntry(const gfxFontEntry&); gfxFontEntry& operator=(const gfxFontEntry&); }; -// used when picking fallback font -struct FontSearch { - FontSearch(const PRUint32 aCharacter, gfxFont *aFont) : - mCh(aCharacter), mFontToMatch(aFont), mMatchRank(0), mCount(0) { - } - const PRUint32 mCh; - gfxFont* mFontToMatch; - PRInt32 mMatchRank; - nsRefPtr<gfxFontEntry> mBestMatch; - PRUint32 mCount; +// used when iterating over all fonts looking for a match for a given character +struct GlobalFontMatch { + GlobalFontMatch(const PRUint32 aCharacter, + PRInt32 aRunScript, + const gfxFontStyle *aStyle) : + mCh(aCharacter), mRunScript(aRunScript), mStyle(aStyle), + mMatchRank(0), mCount(0), mCmapsTested(0) + { + + } + + const PRUint32 mCh; // codepoint to be matched + PRInt32 mRunScript; // Unicode script for the codepoint + const gfxFontStyle* mStyle; // style to match + PRInt32 mMatchRank; // metric indicating closest match + nsRefPtr<gfxFontEntry> mBestMatch; // current best match + PRUint32 mCount; // number of fonts matched + PRUint32 mCmapsTested; // number of cmaps tested }; class gfxFontFamily { public: NS_INLINE_DECL_REFCOUNTING(gfxFontFamily) gfxFontFamily(const nsAString& aName) : mName(aName), @@ -552,19 +560,22 @@ public: // choose a specific face to match a style using CSS font matching // rules (weight matching occurs here). may return a face that doesn't // precisely match (e.g. normal face when no italic face exists). // aNeedsSyntheticBold is set to true when synthetic bolding is // needed, false otherwise gfxFontEntry *FindFontForStyle(const gfxFontStyle& aFontStyle, bool& aNeedsSyntheticBold); - // iterates over faces looking for a match with a given characters + // checks for a matching font within the family // used as part of the font fallback process - void FindFontForChar(FontSearch *aMatchData); + void FindFontForChar(GlobalFontMatch *aMatchData); + + // checks all fonts for a matching font within the family + void SearchAllFontsForChar(GlobalFontMatch *aMatchData); // read in other family names, if any, and use functor to add each into cache virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList); // set when other family names have been read in void SetOtherFamilyNamesInitialized() { mOtherFamilyNamesInitialized = true; } @@ -577,33 +588,33 @@ public: // find faces belonging to this family (platform implementations override this; // should be made pure virtual once all subclasses have been updated) virtual void FindStyleVariations() { } // search for a specific face using the Postscript name gfxFontEntry* FindFont(const nsAString& aPostscriptName); // read in cmaps for all the faces - void ReadCMAP() { + void ReadAllCMAPs() { PRUint32 i, numFonts = mAvailableFonts.Length(); for (i = 0; i < numFonts; i++) { gfxFontEntry *fe = mAvailableFonts[i]; if (!fe) { continue; } fe->ReadCMAP(); mCharacterMap.Union(fe->mCharacterMap); } mCharacterMap.Compact(); mCharacterMapInitialized = true; } bool TestCharacterMap(PRUint32 aCh) { if (!mCharacterMapInitialized) { - ReadCMAP(); + ReadAllCMAPs(); } return mCharacterMap.test(aCh); } void ResetCharacterMap() { mCharacterMap.reset(); mCharacterMapInitialized = false; } @@ -2970,17 +2981,18 @@ public: virtual already_AddRefed<gfxFont> FindFontForChar(PRUint32 ch, PRUint32 prevCh, PRInt32 aRunScript, gfxFont *aPrevMatchedFont, PRUint8 *aMatchType); // search through pref fonts for a character, return nsnull if no matching pref font virtual already_AddRefed<gfxFont> WhichPrefFontSupportsChar(PRUint32 aCh); - virtual already_AddRefed<gfxFont> WhichSystemFontSupportsChar(PRUint32 aCh); + virtual already_AddRefed<gfxFont> + WhichSystemFontSupportsChar(PRUint32 aCh, PRInt32 aRunScript); template<typename T> void ComputeRanges(nsTArray<gfxTextRange>& mRanges, const T *aString, PRUint32 aLength, PRInt32 aRunScript); gfxUserFontSet* GetUserFontSet();
--- a/gfx/thebes/gfxFontUtils.cpp +++ b/gfx/thebes/gfxFontUtils.cpp @@ -32,37 +32,50 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ +#ifdef MOZ_LOGGING +#define FORCE_PR_LOG /* Allow logging in the release build */ +#endif +#include "prlog.h" + #include "mozilla/Util.h" #include "gfxFontUtils.h" #include "nsServiceManagerUtils.h" #include "mozilla/Preferences.h" #include "nsIStreamBufferAccess.h" #include "nsIUUIDGenerator.h" #include "nsMemory.h" #include "nsICharsetConverterManager.h" #include "plbase64.h" +#include "prlog.h" #include "woff.h" #ifdef XP_MACOSX #include <CoreFoundation/CoreFoundation.h> #endif +#ifdef PR_LOGGING + +#define LOG(log, args) PR_LOG(gfxPlatform::GetLog(log), \ + PR_LOG_DEBUG, args) + +#endif // PR_LOGGING + #define NO_RANGE_FOUND 126 // bit 126 in the font unicode ranges is required to be 0 #define UNICODE_BMP_LIMIT 0x10000 using namespace mozilla; /* Unicode subrange table * from: http://msdn.microsoft.com/en-us/library/dd374090 @@ -264,16 +277,47 @@ typedef struct { typedef struct { AutoSwap_PRUint32 startCharCode; AutoSwap_PRUint32 endCharCode; AutoSwap_PRUint32 startGlyphId; } Format12Group; #pragma pack() +#if PR_LOGGING +void +gfxSparseBitSet::Dump(const char* aPrefix, eGfxLog aWhichLog) const +{ + NS_ASSERTION(mBlocks.DebugGetHeader(), "mHdr is null, this is bad"); + PRUint32 b, numBlocks = mBlocks.Length(); + + for (b = 0; b < numBlocks; b++) { + Block *block = mBlocks[b]; + if (!block) continue; + char outStr[256]; + int index = 0; + index += sprintf(&outStr[index], "%s u+%6.6x [", aPrefix, (b << BLOCK_INDEX_SHIFT)); + for (int i = 0; i < 32; i += 4) { + for (int j = i; j < i + 4; j++) { + PRUint8 bits = block->mBits[j]; + PRUint8 flip1 = ((bits & 0xaa) >> 1) | ((bits & 0x55) << 1); + PRUint8 flip2 = ((flip1 & 0xcc) >> 2) | ((flip1 & 0x33) << 2); + PRUint8 flipped = ((flip2 & 0xf0) >> 4) | ((flip2 & 0x0f) << 4); + + index += sprintf(&outStr[index], "%2.2x", flipped); + } + if (i + 4 != 32) index += sprintf(&outStr[index], " "); + } + index += sprintf(&outStr[index], "]"); + LOG(aWhichLog, ("%s", outStr)); + } +} +#endif + + nsresult gfxFontUtils::ReadCMAPTableFormat12(const PRUint8 *aBuf, PRUint32 aLength, gfxSparseBitSet& aCharacterMap) { // Ensure table is large enough that we can safely read the header NS_ENSURE_TRUE(aLength >= sizeof(Format12CmapHeader), NS_ERROR_GFX_CMAP_MALFORMED);
--- a/gfx/thebes/gfxFontUtils.h +++ b/gfx/thebes/gfxFontUtils.h @@ -36,16 +36,17 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef GFX_FONT_UTILS_H #define GFX_FONT_UTILS_H #include "gfxTypes.h" +#include "gfxPlatform.h" #include "prtypes.h" #include "nsAlgorithm.h" #include "prcpucfg.h" #include "nsDataHashtable.h" #include "nsITimer.h" @@ -92,16 +93,21 @@ public: if (blockIndex >= mBlocks.Length()) return false; Block *block = mBlocks[blockIndex]; if (!block) return false; return ((block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)]) & (1 << (aIndex & 0x7))) != 0; } +#if PR_LOGGING + // dump out contents of bitmap + void Dump(const char* aPrefix, eGfxLog aWhichLog) const; +#endif + bool TestRange(PRUint32 aStart, PRUint32 aEnd) { PRUint32 startBlock, endBlock, blockLen; // start point is beyond the end of the block array? return false immediately startBlock = aStart >> BLOCK_INDEX_SHIFT; blockLen = mBlocks.Length(); if (startBlock >= blockLen) return false;
--- a/gfx/thebes/gfxGDIFontList.cpp +++ b/gfx/thebes/gfxGDIFontList.cpp @@ -78,16 +78,20 @@ using namespace mozilla; #ifdef PR_LOGGING #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \ PR_LOG_DEBUG, args) #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \ gfxPlatform::GetLog(eGfxLog_fontlist), \ PR_LOG_DEBUG) +#define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \ + gfxPlatform::GetLog(eGfxLog_cmapdata), \ + PR_LOG_DEBUG) + #endif // PR_LOGGING // font info loader constants // avoid doing this during startup even on slow machines but try to start // it soon enough so that system fallback doesn't happen first static const PRUint32 kDelayBeforeLoadingFonts = 120 * 1000; // 2 minutes after init static const PRUint32 kIntervalBetweenLoadingFonts = 2000; // every 2 seconds until complete @@ -222,16 +226,22 @@ GDIFontEntry::ReadCMAP() mCharacterMap, mUVSOffset, unicodeFont, symbolFont); mSymbolFont = symbolFont; mHasCmapTable = NS_SUCCEEDED(rv); #ifdef PR_LOGGING LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n", NS_ConvertUTF16toUTF8(mName).get(), mCharacterMap.GetSize())); + if (LOG_CMAPDATA_ENABLED()) { + char prefix[256]; + sprintf(prefix, "(cmapdata) name: %.220s", + NS_ConvertUTF16toUTF8(mName).get()); + mCharacterMap.Dump(prefix, eGfxLog_cmapdata); + } #endif return rv; } bool GDIFontEntry::IsSymbolFont() { // initialize cmap first
--- a/gfx/thebes/gfxMacPlatformFontList.h +++ b/gfx/thebes/gfxMacPlatformFontList.h @@ -161,32 +161,44 @@ public: static bool UseATSFontEntry() { return gfxPlatformMac::GetPlatform()->OSXVersion() < MAC_OS_X_VERSION_10_6_HEX; } private: friend class gfxPlatformMac; gfxMacPlatformFontList(); + virtual ~gfxMacPlatformFontList(); // initialize font lists virtual nsresult InitFontList(); // special case font faces treated as font families (set via prefs) void InitSingleFaceList(); gfxFontEntry* MakePlatformFontCG(const gfxProxyFontEntry *aProxyEntry, const PRUint8 *aFontData, PRUint32 aLength); gfxFontEntry* MakePlatformFontATS(const gfxProxyFontEntry *aProxyEntry, const PRUint8 *aFontData, PRUint32 aLength); static void ATSNotification(ATSFontNotificationInfoRef aInfo, void* aUserArg); + // search fonts system-wide for a given character, null otherwise + virtual gfxFontEntry* GlobalFontFallback(const PRUint32 aCh, + PRInt32 aRunScript, + const gfxFontStyle* aMatchStyle, + PRUint32& aCmapCount); + + virtual bool UsesSystemFallback() { return true; } + // keep track of ATS generation to prevent unneeded updates when loading downloaded fonts PRUint32 mATSGeneration; enum { kATSGenerationInitial = -1 }; + + // default font for use with system-wide font fallback + CTFontRef mDefaultFont; }; #endif /* gfxMacPlatformFontList_H_ */
--- a/gfx/thebes/gfxMacPlatformFontList.mm +++ b/gfx/thebes/gfxMacPlatformFontList.mm @@ -53,16 +53,17 @@ #include "gfxUserFontSet.h" #include "nsServiceManagerUtils.h" #include "nsTArray.h" #include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceDefs.h" #include "nsISimpleEnumerator.h" +#include "nsCharTraits.h" #include "mozilla/Telemetry.h" #include <unistd.h> #include <time.h> using namespace mozilla; @@ -131,16 +132,19 @@ static NSString* GetNSStringForString(co #ifdef PR_LOGGING #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \ PR_LOG_DEBUG, args) #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \ gfxPlatform::GetLog(eGfxLog_fontlist), \ PR_LOG_DEBUG) +#define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \ + gfxPlatform::GetLog(eGfxLog_cmapdata), \ + PR_LOG_DEBUG) #endif // PR_LOGGING /* MacOSFontEntry - abstract superclass for ATS and CG font entries */ #pragma mark- MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, PRInt32 aWeight, @@ -262,16 +266,22 @@ MacOSFontEntry::ReadCMAP() } } } #ifdef PR_LOGGING LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n", NS_ConvertUTF16toUTF8(mName).get(), mCharacterMap.GetSize())); + if (LOG_CMAPDATA_ENABLED()) { + char prefix[256]; + sprintf(prefix, "(cmapdata) name: %.220s", + NS_ConvertUTF16toUTF8(mName).get()); + mCharacterMap.Dump(prefix, eGfxLog_cmapdata); + } #endif return rv; } gfxFont* MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) { @@ -690,31 +700,39 @@ gfxSingleFaceMacFontFamily::ReadOtherFam mOtherFamilyNamesInitialized = true; } /* gfxMacPlatformFontList */ #pragma mark- gfxMacPlatformFontList::gfxMacPlatformFontList() : - gfxPlatformFontList(false), mATSGeneration(PRUint32(kATSGenerationInitial)) + gfxPlatformFontList(false), mATSGeneration(PRUint32(kATSGenerationInitial)), + mDefaultFont(nsnull) { ::ATSFontNotificationSubscribe(ATSNotification, kATSFontNotifyOptionDefault, (void*)this, nsnull); // this should always be available (though we won't actually fail if it's missing, // we'll just end up doing a search and then caching the new result instead) mReplacementCharFallbackFamily = NS_LITERAL_STRING("Lucida Grande"); // cache this in a static variable so that MacOSFontFamily objects // don't have to repeatedly look it up sFontManager = [NSFontManager sharedFontManager]; } +gfxMacPlatformFontList::~gfxMacPlatformFontList() +{ + if (mDefaultFont) { + ::CFRelease(mDefaultFont); + } +} + nsresult gfxMacPlatformFontList::InitFontList() { nsAutoreleasePool localPool; ATSGeneration currentGeneration = ::ATSGetGeneration(); // need to ignore notifications after adding each font @@ -862,16 +880,103 @@ gfxMacPlatformFontList::ATSNotification( void* aUserArg) { // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch gfxMacPlatformFontList *qfc = (gfxMacPlatformFontList*)aUserArg; qfc->UpdateFontList(); } gfxFontEntry* +gfxMacPlatformFontList::GlobalFontFallback(const PRUint32 aCh, + PRInt32 aRunScript, + const gfxFontStyle* aMatchStyle, + PRUint32& aCmapCount) +{ + bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); + + if (useCmaps) { + return gfxPlatformFontList::GlobalFontFallback(aCh, + aRunScript, + aMatchStyle, + aCmapCount); + } + + CFStringRef str; + UniChar ch[2]; + CFIndex len = 1; + + if (IS_IN_BMP(aCh)) { + ch[0] = aCh; + str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1, + kCFAllocatorNull); + } else { + ch[0] = H_SURROGATE(aCh); + ch[1] = L_SURROGATE(aCh); + str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2, + kCFAllocatorNull); + if (!str) { + return nsnull; + } + len = 2; + } + + // use CoreText to find the fallback family + + gfxFontEntry *fontEntry = nsnull; + CTFontRef fallback; + bool cantUseFallbackFont = false; + + if (!mDefaultFont) { + mDefaultFont = ::CTFontCreateWithName(CFSTR("Lucida Grande"), 12.f, + NULL); + } + + fallback = ::CTFontCreateForString(mDefaultFont, str, + ::CFRangeMake(0, len)); + + if (fallback) { + CFStringRef familyName = ::CTFontCopyFamilyName(fallback); + ::CFRelease(fallback); + + if (familyName && + ::CFStringCompare(familyName, CFSTR("LastResort"), + kCFCompareCaseInsensitive) != kCFCompareEqualTo) + { + nsAutoTArray<UniChar, 1024> buffer; + CFIndex len = ::CFStringGetLength(familyName); + buffer.SetLength(len+1); + ::CFStringGetCharacters(familyName, ::CFRangeMake(0, len), + buffer.Elements()); + buffer[len] = 0; + nsDependentString family(buffer.Elements(), len); + + bool needsBold; // ignored in the system fallback case + + fontEntry = FindFontForFamily(family, aMatchStyle, needsBold); + if (fontEntry && !fontEntry->TestCharacterMap(aCh)) { + fontEntry = nsnull; + cantUseFallbackFont = true; + } + } + + if (familyName) { + ::CFRelease(familyName); + } + } + + if (cantUseFallbackFont) { + Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont); + } + + ::CFRelease(str); + + return fontEntry; +} + +gfxFontEntry* gfxMacPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle, bool& aNeedsBold) { nsAutoreleasePool localPool; NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName]; nsAutoString familyName; GetStringForNSString(defaultFamily, familyName);
--- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -113,16 +113,17 @@ static void MigratePrefs(); using namespace mozilla::gfx; // logs shared across gfx #ifdef PR_LOGGING static PRLogModuleInfo *sFontlistLog = nsnull; static PRLogModuleInfo *sFontInitLog = nsnull; static PRLogModuleInfo *sTextrunLog = nsnull; static PRLogModuleInfo *sTextrunuiLog = nsnull; +static PRLogModuleInfo *sCmapDataLog = nsnull; #endif /* Class to listen for pref changes so that chrome code can dynamically force sRGB as an output profile. See Bug #452125. */ class SRGBOverrideObserver : public nsIObserver, public nsSupportsWeakReference { public: @@ -144,16 +145,17 @@ SRGBOverrideObserver::Observe(nsISupport return NS_OK; } #define GFX_DOWNLOADABLE_FONTS_ENABLED "gfx.downloadable_fonts.enabled" #define GFX_DOWNLOADABLE_FONTS_SANITIZE "gfx.downloadable_fonts.sanitize" #define GFX_PREF_HARFBUZZ_SCRIPTS "gfx.font_rendering.harfbuzz.scripts" #define HARFBUZZ_SCRIPTS_DEFAULT mozilla::unicode::SHAPING_DEFAULT +#define GFX_PREF_FALLBACK_USE_CMAPS "gfx.font_rendering.fallback.always_use_cmaps" #ifdef MOZ_GRAPHITE #define GFX_PREF_GRAPHITE_SHAPING "gfx.font_rendering.graphite.enabled" #endif #define BIDI_NUMERAL_PREF "bidi.numeral" static const char* kObservedPrefs[] = { @@ -227,16 +229,18 @@ static const char *gPrefLangNames[] = { }; gfxPlatform::gfxPlatform() : mAzureBackendCollector(this, &gfxPlatform::GetAzureBackendInfo) { mUseHarfBuzzScripts = UNINITIALIZED_VALUE; mAllowDownloadableFonts = UNINITIALIZED_VALUE; mDownloadableFontsSanitize = UNINITIALIZED_VALUE; + mFallbackUsesCmaps = UNINITIALIZED_VALUE; + #ifdef MOZ_GRAPHITE mGraphiteShapingEnabled = UNINITIALIZED_VALUE; #endif mBidiNumeralOption = UNINITIALIZED_VALUE; if (Preferences::GetBool("gfx.canvas.azure.prefer-skia", false)) { mPreferredDrawTargetBackend = BACKEND_SKIA; } else { @@ -263,16 +267,17 @@ gfxPlatform::Init() gfxAtoms::RegisterAtoms(); #ifdef PR_LOGGING sFontlistLog = PR_NewLogModule("fontlist");; sFontInitLog = PR_NewLogModule("fontinit");; sTextrunLog = PR_NewLogModule("textrun");; sTextrunuiLog = PR_NewLogModule("textrunui");; + sCmapDataLog = PR_NewLogModule("cmapdata");; #endif /* Initialize the GfxInfo service. * Note: we can't call functions on GfxInfo that depend * on gPlatform until after it has been initialized * below. GfxInfo initialization annotates our * crash reports so we want to do it before @@ -675,16 +680,27 @@ gfxPlatform::SanitizeDownloadedFonts() if (mDownloadableFontsSanitize == UNINITIALIZED_VALUE) { mDownloadableFontsSanitize = Preferences::GetBool(GFX_DOWNLOADABLE_FONTS_SANITIZE, true); } return mDownloadableFontsSanitize; } +bool +gfxPlatform::UseCmapsDuringSystemFallback() +{ + if (mFallbackUsesCmaps == UNINITIALIZED_VALUE) { + mFallbackUsesCmaps = + Preferences::GetBool(GFX_PREF_FALLBACK_USE_CMAPS, false); + } + + return mFallbackUsesCmaps; +} + #ifdef MOZ_GRAPHITE bool gfxPlatform::UseGraphiteShaping() { if (mGraphiteShapingEnabled == UNINITIALIZED_VALUE) { mGraphiteShapingEnabled = Preferences::GetBool(GFX_PREF_GRAPHITE_SHAPING, false); } @@ -1353,16 +1369,18 @@ gfxPlatform::GetBidiNumeralOption() void gfxPlatform::FontsPrefsChanged(const char *aPref) { NS_ASSERTION(aPref != nsnull, "null preference"); if (!strcmp(GFX_DOWNLOADABLE_FONTS_ENABLED, aPref)) { mAllowDownloadableFonts = UNINITIALIZED_VALUE; } else if (!strcmp(GFX_DOWNLOADABLE_FONTS_SANITIZE, aPref)) { mDownloadableFontsSanitize = UNINITIALIZED_VALUE; + } else if (!strcmp(GFX_PREF_FALLBACK_USE_CMAPS, aPref)) { + mFallbackUsesCmaps = UNINITIALIZED_VALUE; #ifdef MOZ_GRAPHITE } else if (!strcmp(GFX_PREF_GRAPHITE_SHAPING, aPref)) { mGraphiteShapingEnabled = UNINITIALIZED_VALUE; gfxFontCache *fontCache = gfxFontCache::GetCache(); if (fontCache) { fontCache->AgeAllGenerations(); fontCache->FlushShapedWordCaches(); } @@ -1392,16 +1410,19 @@ gfxPlatform::GetLog(eGfxLog aWhichLog) return sFontInitLog; break; case eGfxLog_textrun: return sTextrunLog; break; case eGfxLog_textrunui: return sTextrunuiLog; break; + case eGfxLog_cmapdata: + return sCmapDataLog; + break; default: break; } return nsnull; #else return nsnull; #endif
--- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -131,17 +131,19 @@ enum eCMSMode { enum eGfxLog { // all font enumerations, localized names, fullname/psnames, cmap loads eGfxLog_fontlist = 0, // timing info on font initialization eGfxLog_fontinit = 1, // dump text runs, font matching, system fallback for content eGfxLog_textrun = 2, // dump text runs, font matching, system fallback for chrome - eGfxLog_textrunui = 3 + eGfxLog_textrunui = 3, + // dump cmap coverage data as they are loaded + eGfxLog_cmapdata = 4 }; // when searching through pref langs, max number of pref langs const PRUint32 kMaxLenPrefLangList = 32; #define UNINITIALIZED_VALUE (-1) typedef gfxASurface::gfxImageFormat gfxImageFormat; @@ -312,16 +314,21 @@ public: * True when hinting should be enabled. This setting shouldn't * change per gecko process, while the process is live. If so the * results are not defined. * * NB: this bit is only honored by the FT2 backend, currently. */ virtual bool FontHintingEnabled() { return true; } + /** + * Whether to check all font cmaps during system font fallback + */ + bool UseCmapsDuringSystemFallback(); + #ifdef MOZ_GRAPHITE /** * Whether to use the SIL Graphite rendering engine * (for fonts that include Graphite tables) */ bool UseGraphiteShaping(); #endif @@ -364,16 +371,25 @@ public: static eFontPrefLang GetFontPrefLangFor(PRUint8 aUnicodeRange); // returns true if a pref lang is CJK static bool IsLangCJK(eFontPrefLang aLang); // helper method to add a pref lang to an array, if not already in array static void AppendPrefLang(eFontPrefLang aPrefLangs[], PRUint32& aLen, eFontPrefLang aAddLang); + // returns a list of commonly used fonts for a given character + // these are *possible* matches, no cmap-checking is done at this level + virtual void GetCommonFallbackFonts(const PRUint32 /*aCh*/, + PRInt32 /*aRunScript*/, + nsTArray<const char*>& /*aFontList*/) + { + // platform-specific override, by default do nothing + } + // helper method to indicate if we want to use Azure content drawing static bool UseAzureContentDrawing(); /** * Are we going to try color management? */ static eCMSMode GetCMSMode(); @@ -449,16 +465,20 @@ protected: PRInt8 mAllowDownloadableFonts; PRInt8 mDownloadableFontsSanitize; #ifdef MOZ_GRAPHITE PRInt8 mGraphiteShapingEnabled; #endif PRInt8 mBidiNumeralOption; + // whether to always search font cmaps globally + // when doing system font fallback + PRInt8 mFallbackUsesCmaps; + // which scripts should be shaped with harfbuzz PRInt32 mUseHarfBuzzScripts; // The preferred draw target backend to use mozilla::gfx::BackendType mPreferredDrawTargetBackend; private: virtual qcms_profile* GetPlatformCMSOutputProfile();
--- a/gfx/thebes/gfxPlatformFontList.cpp +++ b/gfx/thebes/gfxPlatformFontList.cpp @@ -15,16 +15,17 @@ * The Original Code is Mozilla Corporation code. * * The Initial Developer of the Original Code is Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2006-2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Jonathan Kew <jfkthame@gmail.com> + * John Daggett <jdaggett@mozilla.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -371,103 +372,163 @@ struct FontFamilyListData { void gfxPlatformFontList::GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray) { FontFamilyListData data(aFamilyArray); mFontFamilies.Enumerate(FontFamilyListData::AppendFamily, &data); } -gfxFontEntry* -gfxPlatformFontList::FindFontForChar(const PRUint32 aCh, gfxFont *aPrevFont) -{ +gfxFontEntry* +gfxPlatformFontList::SystemFindFontForChar(const PRUint32 aCh, + PRInt32 aRunScript, + const gfxFontStyle* aStyle) + { + gfxFontEntry* fontEntry = nsnull; + // is codepoint with no matching font? return null immediately if (mCodepointsWithNoFonts.test(aCh)) { return nsnull; } - // TODO: optimize fallback e.g. by caching lists of fonts to try for a given - // unicode range or script - - // try to short-circuit font fallback for U+FFFD, used to represent encoding errors: - // just use a platform-specific fallback system font that is guaranteed (or at least - // highly likely) to be around, or a cached family from last time U+FFFD was seen. - // this helps speed up pages with lots of encoding errors, binary-as-text, etc. + // try to short-circuit font fallback for U+FFFD, used to represent + // encoding errors: just use a platform-specific fallback system + // font that is guaranteed (or at least highly likely) to be around, + // or a cached family from last time U+FFFD was seen. this helps + // speed up pages with lots of encoding errors, binary-as-text, etc. if (aCh == 0xFFFD && mReplacementCharFallbackFamily.Length() > 0) { - gfxFontEntry* fontEntry = nsnull; bool needsBold; // ignored in the system fallback case - if (aPrevFont) { - fontEntry = FindFontForFamily(mReplacementCharFallbackFamily, aPrevFont->GetStyle(), needsBold); - } else { - gfxFontStyle normalStyle; - fontEntry = FindFontForFamily(mReplacementCharFallbackFamily, &normalStyle, needsBold); - } + fontEntry = FindFontForFamily(mReplacementCharFallbackFamily, + aStyle, needsBold); if (fontEntry && fontEntry->TestCharacterMap(aCh)) return fontEntry; } - static bool first = true; TimeStamp start = TimeStamp::Now(); - FontSearch data(aCh, aPrevFont); - - // iterate over all font families to find a font that support the character - mFontFamilies.Enumerate(gfxPlatformFontList::FindFontForCharProc, &data); - + // search commonly available fonts + bool common = true; + fontEntry = CommonFontFallback(aCh, aRunScript, aStyle); + + // if didn't find a font, do system-wide fallback (except for specials) + PRUint32 cmapCount = 0; + if (!fontEntry) { + common = false; + fontEntry = GlobalFontFallback(aCh, aRunScript, aStyle, cmapCount); + } TimeDuration elapsed = TimeStamp::Now() - start; #ifdef PR_LOGGING PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun); if (NS_UNLIKELY(log)) { PRUint32 charRange = gfxFontUtils::CharRangeBit(aCh); PRUint32 unicodeRange = FindCharUnicodeRange(aCh); PRInt32 script = mozilla::unicode::GetScriptCode(aCh); - PR_LOG(log, PR_LOG_DEBUG,\ - ("(textrun-systemfallback) char: u+%6.6x " - "char-range: %d unicode-range: %d script: %d match: [%s]" - " count: %d time: %dus\n", - aCh, - charRange, unicodeRange, script, - (data.mBestMatch ? - NS_ConvertUTF16toUTF8(data.mBestMatch->Name()).get() : - "<none>"), - data.mCount, - PRInt32(elapsed.ToMicroseconds()))); + PR_LOG(log, PR_LOG_WARNING,\ + ("(textrun-systemfallback-%s) char: u+%6.6x " + "char-range: %d unicode-range: %d script: %d match: [%s]" + " time: %dus cmaps: %d\n", + (common ? "common" : "global"), aCh, + charRange, unicodeRange, script, + (fontEntry ? NS_ConvertUTF16toUTF8(fontEntry->Name()).get() : + "<none>"), + PRInt32(elapsed.ToMicroseconds()), + cmapCount)); } #endif // no match? add to set of non-matching codepoints - if (!data.mBestMatch) { - mCodepointsWithNoFonts.set(aCh); - } else if (aCh == 0xFFFD) { - mReplacementCharFallbackFamily = data.mBestMatch->FamilyName(); - } - + if (!fontEntry) { + mCodepointsWithNoFonts.set(aCh); + } else if (aCh == 0xFFFD && fontEntry) { + mReplacementCharFallbackFamily = fontEntry->FamilyName(); + } + + // track system fallback time + static bool first = true; PRInt32 intElapsed = PRInt32(first ? elapsed.ToMilliseconds() : elapsed.ToMicroseconds()); Telemetry::Accumulate((first ? Telemetry::SYSTEM_FONT_FALLBACK_FIRST : Telemetry::SYSTEM_FONT_FALLBACK), intElapsed); first = false; - return data.mBestMatch; + // track the script for which fallback occurred (incremented one make it + // 1-based) + Telemetry::Accumulate(Telemetry::SYSTEM_FONT_FALLBACK_SCRIPT, aRunScript + 1); + + return fontEntry; } PLDHashOperator PR_CALLBACK gfxPlatformFontList::FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr<gfxFontFamily>& aFamilyEntry, void *userArg) { - FontSearch *data = static_cast<FontSearch*>(userArg); + GlobalFontMatch *data = static_cast<GlobalFontMatch*>(userArg); + + // evaluate all fonts in this family for a match + aFamilyEntry->FindFontForChar(data); + + return PL_DHASH_NEXT; +} + +#define NUM_FALLBACK_FONTS 8 + +gfxFontEntry* +gfxPlatformFontList::CommonFontFallback(const PRUint32 aCh, + PRInt32 aRunScript, + const gfxFontStyle* aMatchStyle) +{ + nsAutoTArray<const char*,NUM_FALLBACK_FONTS> defaultFallbacks; + PRUint32 i, numFallbacks; + + gfxPlatform::GetPlatform()->GetCommonFallbackFonts(aCh, aRunScript, + defaultFallbacks); + numFallbacks = defaultFallbacks.Length(); + for (i = 0; i < numFallbacks; i++) { + nsAutoString familyName; + const char *fallbackFamily = defaultFallbacks[i]; - // evaluate all fonts in this family for a match - aFamilyEntry->FindFontForChar(data); - return PL_DHASH_NEXT; + familyName.AppendASCII(fallbackFamily); + gfxFontFamily *fallback = + gfxPlatformFontList::PlatformFontList()->FindFamily(familyName); + if (!fallback) + continue; + + gfxFontEntry *fontEntry; + bool needsBold; // ignored in the system fallback case + + // use first font in list that supports a given character + fontEntry = fallback->FindFontForStyle(*aMatchStyle, needsBold); + if (fontEntry && fontEntry->TestCharacterMap(aCh)) { + return fontEntry; + } + } + + return nsnull; +} + +gfxFontEntry* +gfxPlatformFontList::GlobalFontFallback(const PRUint32 aCh, + PRInt32 aRunScript, + const gfxFontStyle* aMatchStyle, + PRUint32& aCmapCount) +{ + // otherwise, try to find it among local fonts + GlobalFontMatch data(aCh, aRunScript, aMatchStyle); + + // iterate over all font families to find a font that support the character + mFontFamilies.Enumerate(gfxPlatformFontList::FindFontForCharProc, &data); + + aCmapCount = data.mCmapsTested; + + return data.mBestMatch; } #ifdef XP_WIN #include <windows.h> // crude hack for using when monitoring process static void LogRegistryEvent(const wchar_t *msg) { @@ -604,37 +665,41 @@ gfxPlatformFontList::GetStandardFamilyNa void gfxPlatformFontList::InitLoader() { GetFontFamilyList(mFontFamiliesToLoad); mStartIndex = 0; mNumFamilies = mFontFamiliesToLoad.Length(); } -bool +bool gfxPlatformFontList::RunLoader() { PRUint32 i, endIndex = (mStartIndex + mIncrement < mNumFamilies ? mStartIndex + mIncrement : mNumFamilies); + bool loadCmaps = !UsesSystemFallback() || + gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); // for each font family, load in various font info for (i = mStartIndex; i < endIndex; i++) { gfxFontFamily* familyEntry = mFontFamiliesToLoad[i]; // find all faces that are members of this family familyEntry->FindStyleVariations(); if (familyEntry->GetFontList().Length() == 0) { // failed to load any faces for this family, so discard it nsAutoString key; GenerateFontListKey(familyEntry->Name(), key); mFontFamilies.Remove(key); continue; } - // load the cmaps - familyEntry->ReadCMAP(); + // load the cmaps if needed + if (loadCmaps) { + familyEntry->ReadAllCMAPs(); + } // read in face names familyEntry->ReadFaceNames(this, mNeedFullnamePostscriptNames); // check whether the family can be considered "simple" for style matching familyEntry->CheckForSimpleFamily(); }
--- a/gfx/thebes/gfxPlatformFontList.h +++ b/gfx/thebes/gfxPlatformFontList.h @@ -15,16 +15,17 @@ * The Original Code is Mozilla Corporation code. * * The Initial Developer of the Original Code is Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2008-2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Jonathan Kew <jfkthame@gmail.com> + * John Daggett <jdaggett@mozilla.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -92,17 +93,20 @@ public: nsAString& aResolvedFontName); void UpdateFontList() { InitFontList(); } void ClearPrefFonts() { mPrefFonts.Clear(); } virtual void GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray); - gfxFontEntry* FindFontForChar(const PRUint32 aCh, gfxFont *aPrevFont); + virtual gfxFontEntry* + SystemFindFontForChar(const PRUint32 aCh, + PRInt32 aRunScript, + const gfxFontStyle* aStyle); // TODO: make this virtual, for lazily adding to the font list virtual gfxFontFamily* FindFamily(const nsAString& aFamily); gfxFontEntry* FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold); bool GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<gfxFontFamily> > *array); void SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<gfxFontFamily> >& array); @@ -141,16 +145,31 @@ protected: gfxPlatformFontList(bool aNeedFullnamePostscriptNames = true); static gfxPlatformFontList *sPlatformFontList; static PLDHashOperator FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr<gfxFontFamily>& aFamilyEntry, void* userArg); + // returns default font for a given character, null otherwise + virtual gfxFontEntry* CommonFontFallback(const PRUint32 aCh, + PRInt32 aRunScript, + const gfxFontStyle* aMatchStyle); + + // search fonts system-wide for a given character, null otherwise + virtual gfxFontEntry* GlobalFontFallback(const PRUint32 aCh, + PRInt32 aRunScript, + const gfxFontStyle* aMatchStyle, + PRUint32& aCmapCount); + + // whether system-based font fallback is used or not + // if system fallback is used, no need to load all cmaps + virtual bool UsesSystemFallback() { return false; } + // separate initialization for reading in name tables, since this is expensive void InitOtherFamilyNames(); static PLDHashOperator InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey, nsRefPtr<gfxFontFamily>& aFamilyEntry, void* userArg); // read in all fullname/Postscript names for all font faces
--- a/gfx/thebes/gfxPlatformGtk.cpp +++ b/gfx/thebes/gfxPlatformGtk.cpp @@ -40,16 +40,17 @@ #ifdef MOZ_PANGO #define PANGO_ENABLE_BACKEND #define PANGO_ENABLE_ENGINE #endif #include "gfxPlatformGtk.h" #include "nsUnicharUtils.h" +#include "nsUnicodeProperties.h" #include "gfxFontconfigUtils.h" #ifdef MOZ_PANGO #include "gfxPangoFonts.h" #include "gfxContext.h" #include "gfxUserFontSet.h" #else #include <ft2build.h> #include FT_FREETYPE_H @@ -77,16 +78,17 @@ #include <fontconfig/fontconfig.h> #include "nsMathUtils.h" #define GDK_PIXMAP_SIZE_MAX 32767 using namespace mozilla; using namespace mozilla::gfx; +using namespace mozilla::unicode; gfxFontconfigUtils *gfxPlatformGtk::sFontconfigUtils = nsnull; #ifndef MOZ_PANGO typedef nsDataHashtable<nsStringHashKey, nsRefPtr<FontFamily> > FontTable; typedef nsDataHashtable<nsCStringHashKey, nsTArray<nsRefPtr<gfxFontEntry> > > PrefFontTable; static FontTable *gPlatformFonts = NULL; static FontTable *gPlatformFontAliases = NULL; @@ -663,33 +665,34 @@ gfxPlatformGtk::FindFontEntry(const nsAS return ff->FindFontEntry(aFontStyle); } static PLDHashOperator FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr<FontFamily>& aFontFamily, void* aUserArg) { - FontSearch *data = (FontSearch*)aUserArg; + GlobalFontMatch *data = (GlobalFontMatch*)aUserArg; aFontFamily->FindFontForChar(data); return PL_DHASH_NEXT; } already_AddRefed<gfxFont> gfxPlatformGtk::FindFontForChar(PRUint32 aCh, gfxFont *aFont) { if (!gPlatformFonts || !gCodepointsWithNoFonts) return nsnull; // is codepoint with no matching font? return null immediately if (gCodepointsWithNoFonts->test(aCh)) { return nsnull; } - FontSearch data(aCh, aFont); + GlobalFontMatch data(aCh, GetScriptCode(aCh), + (aFont ? aFont->GetStyle() : nsnull)); // find fonts that support the character gPlatformFonts->Enumerate(FindFontForCharProc, &data); if (data.mBestMatch) { nsRefPtr<gfxFT2Font> font = gfxFT2Font::GetOrMakeFont(static_cast<FontEntry*>(data.mBestMatch.get()), aFont->GetStyle());
--- a/gfx/thebes/gfxPlatformMac.cpp +++ b/gfx/thebes/gfxPlatformMac.cpp @@ -258,16 +258,118 @@ gfxPlatformMac::GetFontList(nsIAtom *aLa nsresult gfxPlatformMac::UpdateFontList() { gfxPlatformFontList::PlatformFontList()->UpdateFontList(); return NS_OK; } +static const char kFontArialUnicodeMS[] = "Arial Unicode MS"; +static const char kFontAppleBraille[] = "Apple Braille"; +static const char kFontAppleSymbols[] = "Apple Symbols"; +static const char kFontAppleMyungjo[] = "AppleMyungjo"; +static const char kFontGeneva[] = "Geneva"; +static const char kFontGeezaPro[] = "Geeza Pro"; +static const char kFontHiraginoKakuGothic[] = "Hiragino Kaku Gothic ProN"; +static const char kFontLucidaGrande[] = "Lucida Grande"; +static const char kFontMenlo[] = "Menlo"; +static const char kFontPlantagenetCherokee[] = "Plantagenet Cherokee"; +static const char kFontSTHeiti[] = "STHeiti"; + +void +gfxPlatformMac::GetCommonFallbackFonts(const PRUint32 aCh, + PRInt32 aRunScript, + nsTArray<const char*>& aFontList) +{ + aFontList.AppendElement(kFontLucidaGrande); + + if (!IS_IN_BMP(aCh)) { + PRUint32 p = aCh >> 16; + if (p == 1) { + aFontList.AppendElement(kFontAppleSymbols); + aFontList.AppendElement(kFontGeneva); + } + } else { + PRUint32 b = (aCh >> 8) & 0xff; + + switch (b) { + case 0x03: + case 0x05: + aFontList.AppendElement(kFontGeneva); + break; + case 0x07: + aFontList.AppendElement(kFontGeezaPro); + break; + case 0x10: + aFontList.AppendElement(kFontMenlo); + break; + case 0x13: // Cherokee + aFontList.AppendElement(kFontPlantagenetCherokee); + break; + case 0x18: // Mongolian + aFontList.AppendElement(kFontSTHeiti); + break; + case 0x1d: + case 0x1e: + aFontList.AppendElement(kFontGeneva); + break; + case 0x20: // Symbol ranges + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2e: + aFontList.AppendElement(kFontAppleSymbols); + aFontList.AppendElement(kFontMenlo); + aFontList.AppendElement(kFontGeneva); + aFontList.AppendElement(kFontHiraginoKakuGothic); + break; + case 0x2c: + case 0x2d: + aFontList.AppendElement(kFontGeneva); + break; + case 0x28: // Braille + aFontList.AppendElement(kFontAppleBraille); + break; + case 0x4d: + aFontList.AppendElement(kFontAppleSymbols); + break; + case 0xa0: // Yi + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + aFontList.AppendElement(kFontSTHeiti); + break; + case 0xa6: + case 0xa7: + aFontList.AppendElement(kFontGeneva); + aFontList.AppendElement(kFontAppleSymbols); + break; + case 0xfc: + case 0xff: + aFontList.AppendElement(kFontAppleSymbols); + break; + default: + break; + } + } + + // Arial Unicode MS has lots of glyphs for obscure, use it as a last resort + aFontList.AppendElement(kFontArialUnicodeMS); +} + + PRInt32 gfxPlatformMac::OSXVersion() { if (!mOSXVersion) { // minor version is not accurate, use gestaltSystemVersionMajor, gestaltSystemVersionMinor, gestaltSystemVersionBugFix for these OSErr err = ::Gestalt(gestaltSystemVersion, reinterpret_cast<SInt32*>(&mOSXVersion)); if (err != noErr) { //This should probably be changed when our minimum version changes
--- a/gfx/thebes/gfxPlatformMac.h +++ b/gfx/thebes/gfxPlatformMac.h @@ -45,16 +45,17 @@ #define MAC_OS_X_VERSION_10_4_HEX 0x00001040 #define MAC_OS_X_VERSION_10_5_HEX 0x00001050 #define MAC_OS_X_VERSION_10_6_HEX 0x00001060 #define MAC_OS_X_VERSION_10_7_HEX 0x00001070 #define MAC_OS_X_MAJOR_VERSION_MASK 0xFFFFFFF0U class gfxTextRun; +class gfxFontFamily; class mozilla::gfx::DrawTarget; class THEBES_API gfxPlatformMac : public gfxPlatform { public: gfxPlatformMac(); virtual ~gfxPlatformMac(); static gfxPlatformMac *GetPlatform() { @@ -93,16 +94,20 @@ public: bool IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags); nsresult GetFontList(nsIAtom *aLangGroup, const nsACString& aGenericFamily, nsTArray<nsString>& aListOfFonts); nsresult UpdateFontList(); + virtual void GetCommonFallbackFonts(const PRUint32 aCh, + PRInt32 aRunScript, + nsTArray<const char*>& aFontList); + // Returns the OS X version as returned from Gestalt(gestaltSystemVersion, ...) // Ex: Mac OS X 10.4.x ==> 0x104x PRInt32 OSXVersion(); // lower threshold on font anti-aliasing PRUint32 GetAntiAliasingThreshold() { return mFontAntiAliasingThreshold; } virtual already_AddRefed<gfxASurface>
--- a/gfx/thebes/gfxQtPlatform.cpp +++ b/gfx/thebes/gfxQtPlatform.cpp @@ -45,16 +45,17 @@ #include "gfxQtPlatform.h" #include "gfxFontconfigUtils.h" #include "cairo.h" #include "gfxImageSurface.h" #include "gfxQPainterSurface.h" +#include "nsUnicodeProperties.h" #ifdef MOZ_PANGO #include "gfxPangoFonts.h" #include "gfxContext.h" #include "gfxUserFontSet.h" #else #include "gfxFT2Fonts.h" #endif @@ -74,16 +75,17 @@ #ifndef MOZ_PANGO #include <ft2build.h> #include FT_FREETYPE_H #endif #include "mozilla/Preferences.h" using namespace mozilla; +using namespace mozilla::unicode; #define DEFAULT_RENDER_MODE RENDER_DIRECT static QPaintEngine::Type sDefaultQtPaintEngineType = QPaintEngine::Raster; gfxFontconfigUtils *gfxQtPlatform::sFontconfigUtils = nsnull; static cairo_user_data_key_t cairo_qt_pixmap_key; static void do_qt_pixmap_unref (void *data) { @@ -521,33 +523,34 @@ gfxQtPlatform::FindFontEntry(const nsASt return ff->FindFontEntry(aFontStyle); } static PLDHashOperator FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr<FontFamily>& aFontFamily, void* aUserArg) { - FontSearch *data = (FontSearch*)aUserArg; + GlobalFontMatch *data = (GlobalFontMatch*)aUserArg; aFontFamily->FindFontForChar(data); return PL_DHASH_NEXT; } already_AddRefed<gfxFont> gfxQtPlatform::FindFontForChar(PRUint32 aCh, gfxFont *aFont) { if (!gPlatformFonts || !gCodepointsWithNoFonts) return nsnull; // is codepoint with no matching font? return null immediately if (gCodepointsWithNoFonts->test(aCh)) { return nsnull; } - FontSearch data(aCh, aFont); + GlobalFontMatch data(aCh, GetScriptCode(aCh), + (aFont ? aFont->GetStyle() : nsnull)); // find fonts that support the character gPlatformFonts->Enumerate(FindFontForCharProc, &data); if (data.mBestMatch) { nsRefPtr<gfxFT2Font> font = gfxFT2Font::GetOrMakeFont(static_cast<FontEntry*>(data.mBestMatch.get()), aFont->GetStyle());
--- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -397,43 +397,44 @@ StoreUserFontData(gfxFontEntry* aFontEnt userFontData->mFormat = src.mFormatFlags; userFontData->mRealName = aOriginalName; if (aMetadata) { userFontData->mMetadata.SwapElements(*aMetadata); userFontData->mMetaOrigLen = aMetaOrigLen; } } +struct WOFFHeader { + AutoSwap_PRUint32 signature; + AutoSwap_PRUint32 flavor; + AutoSwap_PRUint32 length; + AutoSwap_PRUint16 numTables; + AutoSwap_PRUint16 reserved; + AutoSwap_PRUint32 totalSfntSize; + AutoSwap_PRUint16 majorVersion; + AutoSwap_PRUint16 minorVersion; + AutoSwap_PRUint32 metaOffset; + AutoSwap_PRUint32 metaCompLen; + AutoSwap_PRUint32 metaOrigLen; + AutoSwap_PRUint32 privOffset; + AutoSwap_PRUint32 privLen; +}; + void gfxUserFontSet::CopyWOFFMetadata(const PRUint8* aFontData, PRUint32 aLength, nsTArray<PRUint8>* aMetadata, PRUint32* aMetaOrigLen) { // This function may be called with arbitrary, unvalidated "font" data // from @font-face, so it needs to be careful to bounds-check, etc., // before trying to read anything. // This just saves a copy of the compressed data block; it does NOT check // that the block can be successfully decompressed, or that it contains // well-formed/valid XML metadata. - struct WOFFHeader { - AutoSwap_PRUint32 signature; - AutoSwap_PRUint32 flavor; - AutoSwap_PRUint32 length; - AutoSwap_PRUint16 numTables; - AutoSwap_PRUint16 reserved; - AutoSwap_PRUint32 totalSfntSize; - AutoSwap_PRUint16 majorVersion; - AutoSwap_PRUint16 minorVersion; - AutoSwap_PRUint32 metaOffset; - AutoSwap_PRUint32 metaCompLen; - AutoSwap_PRUint32 metaOrigLen; - AutoSwap_PRUint32 privOffset; - AutoSwap_PRUint32 privLen; - }; if (aLength < sizeof(WOFFHeader)) { return; } const WOFFHeader* woff = reinterpret_cast<const WOFFHeader*>(aFontData); PRUint32 metaOffset = woff->metaOffset; PRUint32 metaCompLen = woff->metaCompLen; if (!metaOffset || !metaCompLen || !woff->metaOrigLen) { return;
--- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -823,16 +823,185 @@ RemoveCharsetFromFontSubstitute(nsAStrin nsresult gfxWindowsPlatform::UpdateFontList() { gfxPlatformFontList::PlatformFontList()->UpdateFontList(); return NS_OK; } +static const char kFontArabicTypesetting[] = "Arabic Typesetting"; +static const char kFontArial[] = "Arial"; +static const char kFontArialUnicodeMS[] = "Arial Unicode MS"; +static const char kFontCambria[] = "Cambria"; +static const char kFontCambriaMath[] = "Cambria Math"; +static const char kFontEbrima[] = "Ebrima"; +static const char kFontEstrangeloEdessa[] = "Estrangelo Edessa"; +static const char kFontEuphemia[] = "Euphemia"; +static const char kFontGabriola[] = "Gabriola"; +static const char kFontKhmerUI[] = "Khmer UI"; +static const char kFontLaoUI[] = "Lao UI"; +static const char kFontMVBoli[] = "MV Boli"; +static const char kFontMalgunGothic[] = "Malgun Gothic"; +static const char kFontMicrosoftJhengHei[] = "Microsoft JhengHei"; +static const char kFontMicrosoftNewTaiLue[] = "Microsoft New Tai Lue"; +static const char kFontMicrosoftPhagsPa[] = "Microsoft PhagsPa"; +static const char kFontMicrosoftTaiLe[] = "Microsoft Tai Le"; +static const char kFontMicrosoftUighur[] = "Microsoft Uighur"; +static const char kFontMicrosoftYaHei[] = "Microsoft YaHei"; +static const char kFontMicrosoftYiBaiti[] = "Microsoft Yi Baiti"; +static const char kFontMeiryo[] = "Meiryo"; +static const char kFontMongolianBaiti[] = "Mongolian Baiti"; +static const char kFontNyala[] = "Nyala"; +static const char kFontPlantagenetCherokee[] = "Plantagenet Cherokee"; +static const char kFontSegoeUI[] = "Segoe UI"; +static const char kFontSegoeUISymbol[] = "Segoe UI Symbol"; +static const char kFontSylfaen[] = "Sylfaen"; +static const char kFontTraditionalArabic[] = "Traditional Arabic"; + +void +gfxWindowsPlatform::GetCommonFallbackFonts(const PRUint32 aCh, + PRInt32 aRunScript, + nsTArray<const char*>& aFontList) +{ + // Arial is used as the default fallback for system fallback + aFontList.AppendElement(kFontArial); + + if (!IS_IN_BMP(aCh)) { + PRUint32 p = aCh >> 16; + if (p == 1) { // SMP plane + aFontList.AppendElement(kFontCambriaMath); + aFontList.AppendElement(kFontSegoeUISymbol); + aFontList.AppendElement(kFontEbrima); + } + } else { + PRUint32 b = (aCh >> 8) & 0xff; + + switch (b) { + case 0x05: + aFontList.AppendElement(kFontEstrangeloEdessa); + aFontList.AppendElement(kFontCambria); + break; + case 0x06: + aFontList.AppendElement(kFontMicrosoftUighur); + break; + case 0x07: + aFontList.AppendElement(kFontEstrangeloEdessa); + aFontList.AppendElement(kFontMVBoli); + aFontList.AppendElement(kFontEbrima); + break; + case 0x0e: + aFontList.AppendElement(kFontLaoUI); + break; + case 0x12: + case 0x13: + aFontList.AppendElement(kFontNyala); + aFontList.AppendElement(kFontPlantagenetCherokee); + break; + case 0x14: + case 0x15: + case 0x16: + aFontList.AppendElement(kFontEuphemia); + aFontList.AppendElement(kFontSegoeUISymbol); + break; + case 0x17: + aFontList.AppendElement(kFontKhmerUI); + break; + case 0x18: // Mongolian + aFontList.AppendElement(kFontMongolianBaiti); + break; + case 0x19: + aFontList.AppendElement(kFontMicrosoftTaiLe); + aFontList.AppendElement(kFontMicrosoftNewTaiLue); + aFontList.AppendElement(kFontKhmerUI); + break; + break; + case 0x20: // Symbol ranges + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + aFontList.AppendElement(kFontSegoeUI); + aFontList.AppendElement(kFontSegoeUISymbol); + aFontList.AppendElement(kFontCambria); + aFontList.AppendElement(kFontCambriaMath); + aFontList.AppendElement(kFontMeiryo); + aFontList.AppendElement(kFontArial); + aFontList.AppendElement(kFontEbrima); + break; + case 0x2d: + case 0x2e: + case 0x2f: + aFontList.AppendElement(kFontEbrima); + aFontList.AppendElement(kFontNyala); + aFontList.AppendElement(kFontMeiryo); + break; + case 0x28: // Braille + aFontList.AppendElement(kFontSegoeUISymbol); + break; + case 0x30: + case 0x31: + aFontList.AppendElement(kFontMicrosoftYaHei); + break; + case 0x32: + aFontList.AppendElement(kFontMalgunGothic); + break; + case 0x4d: + aFontList.AppendElement(kFontSegoeUISymbol); + break; + case 0xa0: // Yi + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + aFontList.AppendElement(kFontMicrosoftYiBaiti); + break; + case 0xa5: + case 0xa6: + case 0xa7: + aFontList.AppendElement(kFontEbrima); + aFontList.AppendElement(kFontCambriaMath); + break; + case 0xa8: + aFontList.AppendElement(kFontMicrosoftPhagsPa); + break; + case 0xfb: + aFontList.AppendElement(kFontMicrosoftUighur); + aFontList.AppendElement(kFontGabriola); + aFontList.AppendElement(kFontSylfaen); + break; + case 0xfc: + case 0xfd: + aFontList.AppendElement(kFontTraditionalArabic); + aFontList.AppendElement(kFontArabicTypesetting); + break; + case 0xfe: + aFontList.AppendElement(kFontTraditionalArabic); + aFontList.AppendElement(kFontMicrosoftJhengHei); + break; + case 0xff: + aFontList.AppendElement(kFontMicrosoftJhengHei); + break; + default: + break; + } + } + + // Arial Unicode MS has lots of glyphs for obscure characters, + // use it as a last resort + aFontList.AppendElement(kFontArialUnicodeMS); +} + struct ResolveData { ResolveData(gfxPlatform::FontResolverCallback aCallback, gfxWindowsPlatform *aCaller, const nsAString *aFontName, void *aClosure) : mFoundCount(0), mCallback(aCallback), mCaller(aCaller), mFontName(aFontName), mClosure(aClosure) {} PRUint32 mFoundCount; gfxPlatform::FontResolverCallback mCallback;
--- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -174,16 +174,20 @@ public: HDC GetScreenDC() { return mScreenDC; } nsresult GetFontList(nsIAtom *aLangGroup, const nsACString& aGenericFamily, nsTArray<nsString>& aListOfFonts); nsresult UpdateFontList(); + virtual void GetCommonFallbackFonts(const PRUint32 aCh, + PRInt32 aRunScript, + nsTArray<const char*>& aFontList); + nsresult ResolveFontName(const nsAString& aFontName, FontResolverCallback aCallback, void *aClosure, bool& aAborted); nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName); gfxFontGroup *CreateFontGroup(const nsAString &aFamilies, const gfxFontStyle *aStyle,
--- a/gfx/thebes/nsUnicodeRange.cpp +++ b/gfx/thebes/nsUnicodeRange.cpp @@ -208,17 +208,17 @@ static nsIAtom **gUnicodeRangeToLangGrou * Misc - small form variants * fe50 - fe6f * Misc - Specials * fff0 - ffff *********************************************************************/ -#define NUM_OF_SUBTABLES 9 +#define NUM_OF_SUBTABLES 10 #define SUBTABLE_SIZE 16 static const PRUint8 gUnicodeSubrangeTable[NUM_OF_SUBTABLES][SUBTABLE_SIZE] = { { // table for X--- kRangeTableBase+1, //u0xxx kRangeTableBase+2, //u1xxx kRangeTableBase+3, //u2xxx @@ -247,17 +247,17 @@ static const PRUint8 gUnicodeSubrangeTab kRangeTertiaryTable, //u07xx kRangeUnassigned, //u08xx kRangeTertiaryTable, //u09xx kRangeTertiaryTable, //u0axx kRangeTertiaryTable, //u0bxx kRangeTertiaryTable, //u0cxx kRangeTertiaryTable, //u0dxx kRangeTertiaryTable, //u0exx - kRangeTibetan, //u0fxx + kRangeTibetan //u0fxx }, { //table for 1x-- kRangeTertiaryTable, //u10xx kRangeKorean, //u11xx kRangeEthiopic, //u12xx kRangeTertiaryTable, //u13xx kRangeCanadian, //u14xx kRangeCanadian, //u15xx @@ -265,17 +265,17 @@ static const PRUint8 gUnicodeSubrangeTab kRangeKhmer, //u17xx kRangeMongolian, //u18xx kRangeUnassigned, //u19xx kRangeUnassigned, //u1axx kRangeUnassigned, //u1bxx kRangeUnassigned, //u1cxx kRangeUnassigned, //u1dxx kRangeSetLatin, //u1exx - kRangeGreek, //u1fxx + kRangeGreek //u1fxx }, { //table for 2x-- kRangeSetLatin, //u20xx kRangeSetLatin, //u21xx kRangeMathOperators, //u22xx kRangeMiscTechnical, //u23xx kRangeControlOpticalEnclose, //u24xx kRangeBoxBlockGeometrics, //u25xx @@ -283,17 +283,17 @@ static const PRUint8 gUnicodeSubrangeTab kRangeDingbats, //u27xx kRangeBraillePattern, //u28xx kRangeUnassigned, //u29xx kRangeUnassigned, //u2axx kRangeUnassigned, //u2bxx kRangeUnassigned, //u2cxx kRangeUnassigned, //u2dxx kRangeSetCJK, //u2exx - kRangeSetCJK, //u2fxx + kRangeSetCJK //u2fxx }, { //table for ax-- kRangeYi, //ua0xx kRangeYi, //ua1xx kRangeYi, //ua2xx kRangeYi, //ua3xx kRangeYi, //ua4xx kRangeUnassigned, //ua5xx @@ -301,17 +301,17 @@ static const PRUint8 gUnicodeSubrangeTab kRangeUnassigned, //ua7xx kRangeUnassigned, //ua8xx kRangeUnassigned, //ua9xx kRangeUnassigned, //uaaxx kRangeUnassigned, //uabxx kRangeKorean, //uacxx kRangeKorean, //uadxx kRangeKorean, //uaexx - kRangeKorean, //uafxx + kRangeKorean //uafxx }, { //table for dx-- kRangeKorean, //ud0xx kRangeKorean, //ud1xx kRangeKorean, //ud2xx kRangeKorean, //ud3xx kRangeKorean, //ud4xx kRangeKorean, //ud5xx @@ -319,38 +319,35 @@ static const PRUint8 gUnicodeSubrangeTab kRangeKorean, //ud7xx kRangeSurrogate, //ud8xx kRangeSurrogate, //ud9xx kRangeSurrogate, //udaxx kRangeSurrogate, //udbxx kRangeSurrogate, //udcxx kRangeSurrogate, //uddxx kRangeSurrogate, //udexx - kRangeSurrogate, //udfxx + kRangeSurrogate //udfxx }, { // table for fx-- kRangePrivate, //uf0xx kRangePrivate, //uf1xx kRangePrivate, //uf2xx kRangePrivate, //uf3xx kRangePrivate, //uf4xx kRangePrivate, //uf5xx kRangePrivate, //uf6xx kRangePrivate, //uf7xx kRangePrivate, //uf8xx kRangeSetCJK, //uf9xx kRangeSetCJK, //ufaxx kRangeArabic, //ufbxx, includes alphabic presentation form kRangeArabic, //ufcxx kRangeArabic, //ufdxx - kRangeArabic, //ufexx, includes Combining half marks, - // CJK compatibility forms, - // CJK compatibility forms, - // small form variants - kRangeTableBase+8, //uffxx, halfwidth and fullwidth forms, includes Specials + kRangeTableBase+8, //ufexx + kRangeTableBase+9 //uffxx, halfwidth and fullwidth forms, includes Specials }, { //table for 0x0500 - 0x05ff kRangeCyrillic, //u050x kRangeCyrillic, //u051x kRangeCyrillic, //u052x kRangeArmenian, //u053x kRangeArmenian, //u054x kRangeArmenian, //u055x @@ -358,17 +355,35 @@ static const PRUint8 gUnicodeSubrangeTab kRangeArmenian, //u057x kRangeArmenian, //u058x kRangeHebrew, //u059x kRangeHebrew, //u05ax kRangeHebrew, //u05bx kRangeHebrew, //u05cx kRangeHebrew, //u05dx kRangeHebrew, //u05ex - kRangeHebrew, //u05fx + kRangeHebrew //u05fx + }, + { //table for 0xfe00 - 0xfeff + kRangeSetCJK, //ufe0x + kRangeSetCJK, //ufe1x + kRangeSetCJK, //ufe2x + kRangeSetCJK, //ufe3x + kRangeSetCJK, //ufe4x + kRangeSetCJK, //ufe5x + kRangeSetCJK, //ufe6x + kRangeArabic, //ufe7x + kRangeArabic, //ufe8x + kRangeArabic, //ufe9x + kRangeArabic, //ufeax + kRangeArabic, //ufebx + kRangeArabic, //ufecx + kRangeArabic, //ufedx + kRangeArabic, //ufeex + kRangeArabic //ufefx }, { //table for 0xff00 - 0xffff kRangeSetCJK, //uff0x, fullwidth latin kRangeSetCJK, //uff1x, fullwidth latin kRangeSetCJK, //uff2x, fullwidth latin kRangeSetCJK, //uff3x, fullwidth latin kRangeSetCJK, //uff4x, fullwidth latin kRangeSetCJK, //uff5x, fullwidth latin @@ -420,39 +435,52 @@ static const PRUint8 gUnicodeTertiaryRan kRangeEthiopic, //u128x place holder(resolved in the 2ndary tab.) kRangeEthiopic, //u130x kRangeCherokee, //u138x kRangeCanadian, //u140x place holder(resolved in the 2ndary tab.) kRangeCanadian, //u148x place holder(resolved in the 2ndary tab.) kRangeCanadian, //u150x place holder(resolved in the 2ndary tab.) kRangeCanadian, //u158x place holder(resolved in the 2ndary tab.) kRangeCanadian, //u160x - kRangeOghamRunic, //u168x this contains two scripts, Ogham & Runic + kRangeOghamRunic //u168x this contains two scripts, Ogham & Runic }; // A two level index is almost enough for locating a range, with the // exception of u03xx and u05xx. Since we don't really care about range for // combining diacritical marks in our font application, they are // not discriminated further. But future adoption of this module for other use // should be aware of this limitation. The implementation can be extended if // there is such a need. // For Indic, Southeast Asian scripts and some other scripts between // U+0700 and U+16FF, it's extended to the third level. -PRUint32 FindCharUnicodeRange(PRUnichar ch) +PRUint32 FindCharUnicodeRange(PRUint32 ch) { PRUint32 range; + + // aggregate ranges for non-BMP codepoints + if (ch > 0xFFFF) { + PRUint32 p = (ch >> 16); + if (p == 1) { + return kRangeSMP; + } else if (p == 2) { + return kRangeSetCJK; + } + return kRangeHigherPlanes; + } - //search the first table + // lookup explicit range for BMP codepoints + // first general range range = gUnicodeSubrangeTable[0][ch >> 12]; + // if general range is good enough, return that if (range < kRangeTableBase) // we try to get a specific range return range; - // otherwise, we have one more table to look at + // otherwise, use subrange tables range = gUnicodeSubrangeTable[range - kRangeTableBase][(ch & 0x0f00) >> 8]; if (range < kRangeTableBase) return range; if (range < kRangeTertiaryTable) return gUnicodeSubrangeTable[range - kRangeTableBase][(ch & 0x00f0) >> 4]; // Yet another table to look at : U+0700 - U+16FF : 128 code point blocks return gUnicodeTertiaryRangeTable[(ch - 0x0700) >> 7];
--- a/gfx/thebes/nsUnicodeRange.h +++ b/gfx/thebes/nsUnicodeRange.h @@ -103,17 +103,21 @@ const PRUint8 kRangeControlOpticalEncl const PRUint8 kRangeBoxBlockGeometrics = 46; const PRUint8 kRangeMiscSymbols = 47; const PRUint8 kRangeDingbats = 48; const PRUint8 kRangeBraillePattern = 49; const PRUint8 kRangeYi = 50; const PRUint8 kRangeCombiningDiacriticalMarks = 51; const PRUint8 kRangeSpecials = 52; +// aggregate ranges for non-BMP codepoints (u+2xxxx are all CJK) +const PRUint8 kRangeSMP = 53; // u+1xxxx +const PRUint8 kRangeHigherPlanes = 54; // u+3xxxx and above + const PRUint8 kRangeTableBase = 128; //values over 127 are reserved for internal use only const PRUint8 kRangeTertiaryTable = 145; // leave room for 16 subtable // indices (kRangeTableBase + 1 .. // kRangeTableBase + 16) -PRUint32 FindCharUnicodeRange(PRUnichar ch); +PRUint32 FindCharUnicodeRange(PRUint32 ch); nsIAtom* LangGroupFromUnicodeRange(PRUint8 unicodeRange);
--- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -2576,16 +2576,26 @@ RasterImage::Draw(gfxContext *aContext, if (mFrameDecodeFlags != DECODE_FLAGS_DEFAULT) { if (!CanForciblyDiscard()) return NS_ERROR_NOT_AVAILABLE; ForceDiscard(); mFrameDecodeFlags = DECODE_FLAGS_DEFAULT; } + // If this image is a candidate for discarding, reset its position in the + // discard tracker so we're less likely to discard it right away. + // + // (We don't normally draw unlocked images, so this conditition will usually + // be false. But we will draw unlocked images if image locking is globally + // disabled via the content.image.allow_locking pref.) + if (DiscardingActive()) { + DiscardTracker::Reset(&mDiscardTrackerNode); + } + // We use !mDecoded && mHasSourceData to mean discarded. if (!mDecoded && mHasSourceData) { mDrawStartTime = TimeStamp::Now(); // We're drawing this image, so indicate that we should decode it as soon // as possible. DecodeWorker::Singleton()->MarkAsASAP(this); }
--- a/image/test/reftest/reftest.list +++ b/image/test/reftest/reftest.list @@ -1,10 +1,10 @@ # Check for 24-bit color mode (test for bug 414720) -== colordepth.html about:blank +skip-if(Android) == colordepth.html about:blank # "PngSuite, the official set of PNG test images" # Images by Willem van Schaik # # http://www.schaik.com/pngsuite/pngsuite.html # http://www.libpng.org/pub/png/pngsuite.html include pngsuite-basic-n/reftest.list include pngsuite-basic-i/reftest.list
--- a/js/src/aclocal.m4 +++ b/js/src/aclocal.m4 @@ -10,10 +10,11 @@ builtin(include, build/autoconf/moznbyte builtin(include, build/autoconf/mozprog.m4)dnl builtin(include, build/autoconf/mozheader.m4)dnl builtin(include, build/autoconf/mozcommonheader.m4)dnl builtin(include, build/autoconf/acwinpaths.m4)dnl builtin(include, build/autoconf/lto.m4)dnl builtin(include, build/autoconf/gcc-pr49911.m4)dnl builtin(include, build/autoconf/frameptr.m4)dnl builtin(include, build/autoconf/compiler-opts.m4)dnl +builtin(include, build/autoconf/expandlibs.m4)dnl MOZ_PROG_CHECKMSYS()
--- a/js/src/build/autoconf/compiler-opts.m4 +++ b/js/src/build/autoconf/compiler-opts.m4 @@ -4,10 +4,81 @@ AC_DEFUN([MOZ_COMPILER_OPTS], [ if test "$CLANG_CXX"; then ## We disable return-type-c-linkage because jsval is defined as a C++ type but is ## returned by C functions. This is possible because we use knowledge about the ABI ## to typedef it to a C type with the same layout when the headers are included ## from C. _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage" fi + +if test "$GNU_CC"; then + CFLAGS="$CFLAGS -ffunction-sections -fdata-sections" + CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections" +fi + +dnl ======================================================== +dnl = Identical Code Folding +dnl ======================================================== + +MOZ_ARG_DISABLE_BOOL(icf, +[ --disable-icf Disable Identical Code Folding], + MOZ_DISABLE_ICF=1, + MOZ_DISABLE_ICF= ) + +if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF"; then + AC_CACHE_CHECK([whether the linker supports Identical Code Folding], + LD_SUPPORTS_ICF, + [echo 'int foo() {return 42;}' \ + 'int bar() {return 42;}' \ + 'int main() {return foo() - bar();}' > conftest.${ac_ext} + # If the linker supports ICF, foo and bar symbols will have + # the same address + if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) && + test -s conftest${ac_exeext} && + objdump -t conftest${ac_exeext} | awk changequote(<<, >>)'{a[<<$>>6] = <<$>>1} END {if (a["foo"] && (a["foo"] != a["bar"])) { exit 1 }}'changequote([, ]); then + LD_SUPPORTS_ICF=yes + else + LD_SUPPORTS_ICF=no + fi + rm -rf conftest*]) + if test "$LD_SUPPORTS_ICF" = yes; then + _SAVE_LDFLAGS="$LDFLAGS -Wl,--icf=safe" + LDFLAGS="$LDFLAGS -Wl,--icf=safe -Wl,--print-icf-sections" + AC_TRY_LINK([], [], + [LD_PRINT_ICF_SECTIONS=-Wl,--print-icf-sections], + [LD_PRINT_ICF_SECTIONS=]) + AC_SUBST([LD_PRINT_ICF_SECTIONS]) + LDFLAGS="$_SAVE_LDFLAGS" + fi +fi + +dnl ======================================================== +dnl = Automatically remove dead symbols +dnl ======================================================== + +if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -n "$MOZ_DEBUG_FLAGS"; then + dnl See bug 670659 + AC_CACHE_CHECK([whether removing dead symbols breaks debugging], + GC_SECTIONS_BREAKS_DEBUG_RANGES, + [echo 'int foo() {return 42;}' \ + 'int bar() {return 1;}' \ + 'int main() {return foo();}' > conftest.${ac_ext} + if AC_TRY_COMMAND([${CC-cc} -o conftest.${ac_objext} $CFLAGS $MOZ_DEBUG_FLAGS -c conftest.${ac_ext} 1>&2]) && + AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS $MOZ_DEBUG_FLAGS -Wl,--gc-sections conftest.${ac_objext} $LIBS 1>&2]) && + test -s conftest${ac_exeext} -a -s conftest.${ac_objext}; then + if test "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest.${ac_objext} conftest.${ac_ext}`" = \ + "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest${ac_exeext} conftest.${ac_ext}`"; then + GC_SECTIONS_BREAKS_DEBUG_RANGES=no + else + GC_SECTIONS_BREAKS_DEBUG_RANGES=yes + fi + else + dnl We really don't expect to get here, but just in case + GC_SECTIONS_BREAKS_DEBUG_RANGES="no, but it's broken in some other way" + fi + rm -rf conftest*]) + if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then + DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections" + fi +fi + ]) -
new file mode 100644 --- /dev/null +++ b/js/src/build/autoconf/expandlibs.m4 @@ -0,0 +1,56 @@ +AC_DEFUN([MOZ_EXPAND_LIBS], +[ +dnl ======================================================== +dnl = +dnl = Check what kind of list files are supported by the +dnl = linker +dnl = +dnl ======================================================== + +AC_CACHE_CHECK(what kind of list files are supported by the linker, + EXPAND_LIBS_LIST_STYLE, + [echo "int main() {return 0;}" > conftest.${ac_ext} + if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&5) && test -s conftest.${OBJ_SUFFIX}; then + echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list + if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then + EXPAND_LIBS_LIST_STYLE=linkerscript + else + echo "conftest.${OBJ_SUFFIX}" > conftest.list + if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then + EXPAND_LIBS_LIST_STYLE=list + else + EXPAND_LIBS_LIST_STYLE=none + fi + fi + else + dnl We really don't expect to get here, but just in case + AC_ERROR([couldn't compile a simple C file]) + fi + rm -rf conftest*]) + +LIBS_DESC_SUFFIX=desc +AC_SUBST(LIBS_DESC_SUFFIX) +AC_SUBST(EXPAND_LIBS_LIST_STYLE) + +if test "$GCC_USE_GNU_LD"; then + AC_CACHE_CHECK(what kind of ordering can be done with the linker, + EXPAND_LIBS_ORDER_STYLE, + [> conftest.order + _SAVE_LDFLAGS="$LDFLAGS" + LDFLAGS="${LDFLAGS} -Wl,--section-ordering-file,conftest.order" + AC_TRY_LINK([], [], + EXPAND_LIBS_ORDER_STYLE=section-ordering-file, + EXPAND_LIBS_ORDER_STYLE=) + LDFLAGS="$_SAVE_LDFLAGS" + if test -z "$EXPAND_LIBS_ORDER_STYLE"; then + if AC_TRY_COMMAND(${CC-cc} ${DSO_LDOPTS} ${LDFLAGS} -o ${DLL_PREFIX}conftest${DLL_SUFFIX} -Wl,--verbose 2> /dev/null | sed -n '/^===/,/^===/p' | grep '\.text'); then + EXPAND_LIBS_ORDER_STYLE=linkerscript + else + EXPAND_LIBS_ORDER_STYLE=none + fi + rm -f ${DLL_PREFIX}conftest${DLL_SUFFIX} + fi]) +fi +AC_SUBST(EXPAND_LIBS_ORDER_STYLE) + +])
--- a/js/src/config/config.mk +++ b/js/src/config/config.mk @@ -784,18 +784,22 @@ OPTIMIZE_JARS_CMD = $(PYTHON) $(call cor CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py) EXPAND_LIBS = $(PYTHON) -I$(DEPTH)/config $(topsrcdir)/config/expandlibs.py EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_exec.py EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_gen.py EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR) EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC) EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC) -EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(LD) -EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(MKSHLIB) +EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist -- $(LD) +EXPAND_MKSHLIB_ARGS = --uselist +ifdef SYMBOL_ORDER +EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER) +endif +EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) $(EXPAND_MKSHLIB_ARGS) -- $(MKSHLIB) ifdef STDCXX_COMPAT ifneq ($(OS_ARCH),Darwin) CHECK_STDCXX = objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' > /dev/null && echo "TEST-UNEXPECTED-FAIL | | We don't want these libstdc++ symbols to be used:" && objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && exit 1 || exit 0 endif endif # autoconf.mk sets OBJ_SUFFIX to an error to avoid use before including
--- a/js/src/config/expandlibs_config.py.in +++ b/js/src/config/expandlibs_config.py.in @@ -49,8 +49,10 @@ AR_EXTRACT = "@AR_EXTRACT@".replace('$(A DLL_PREFIX = "@DLL_PREFIX@" LIB_PREFIX = "@LIB_PREFIX@" OBJ_SUFFIX = normalize_suffix("@OBJ_SUFFIX@") LIB_SUFFIX = normalize_suffix("@LIB_SUFFIX@") DLL_SUFFIX = normalize_suffix("@DLL_SUFFIX@") IMPORT_LIB_SUFFIX = normalize_suffix("@IMPORT_LIB_SUFFIX@") LIBS_DESC_SUFFIX = normalize_suffix("@LIBS_DESC_SUFFIX@") EXPAND_LIBS_LIST_STYLE = "@EXPAND_LIBS_LIST_STYLE@" +EXPAND_LIBS_ORDER_STYLE = "@EXPAND_LIBS_ORDER_STYLE@" +LD_PRINT_ICF_SECTIONS = "@LD_PRINT_ICF_SECTIONS@"
--- a/js/src/config/expandlibs_exec.py +++ b/js/src/config/expandlibs_exec.py @@ -44,29 +44,41 @@ from static libraries (or use those list With the --uselist argument (useful for e.g. $(CC)), it replaces all object files with a list file. This can be used to avoid limitations in the length of a command line. The kind of list file format used depends on the EXPAND_LIBS_LIST_STYLE variable: 'list' for MSVC style lists (@file.list) or 'linkerscript' for GNU ld linker scripts. See https://bugzilla.mozilla.org/show_bug.cgi?id=584474#c59 for more details. -With the --reorder argument, followed by a file name, it will reorder the -object files from the command line according to the order given in the file. -Implies --extract. +With the --symbol-order argument, followed by a file name, it will add the +relevant linker options to change the order in which the linker puts the +symbols appear in the resulting binary. Only works for ELF targets. ''' from __future__ import with_statement import sys import os from expandlibs import ExpandArgs, relativize, isObject import expandlibs_config as conf from optparse import OptionParser import subprocess import tempfile import shutil +import subprocess +import re + +# The are the insert points for a GNU ld linker script, assuming a more +# or less "standard" default linker script. This is not a dict because +# order is important. +SECTION_INSERT_BEFORE = [ + ('.text', '.fini'), + ('.rodata', '.rodata1'), + ('.data.rel.ro', '.dynamic'), + ('.data', '.data1'), +] class ExpandArgsMore(ExpandArgs): ''' Meant to be used as 'with ExpandArgsMore(args) as ...: ''' def __enter__(self): self.tmp = [] return self def __exit__(self, type, value, tb): @@ -114,61 +126,203 @@ class ExpandArgsMore(ExpandArgs): fd, tmp = tempfile.mkstemp(suffix=".list",dir=os.curdir) if conf.EXPAND_LIBS_LIST_STYLE == "linkerscript": content = ["INPUT(%s)\n" % obj for obj in objs] ref = tmp elif conf.EXPAND_LIBS_LIST_STYLE == "list": content = ["%s\n" % obj for obj in objs] ref = "@" + tmp else: + os.close(fd) os.remove(tmp) return self.tmp.append(tmp) f = os.fdopen(fd, "w") f.writelines(content) f.close() idx = self.index(objs[0]) newlist = self[0:idx] + [ref] + [item for item in self[idx:] if item not in objs] self[0:] = newlist - def reorder(self, order_list): - '''Given a list of file names without OBJ_SUFFIX, rearrange self - so that the object file names it contains are ordered according to - that list. - ''' - objs = [o for o in self if isObject(o)] - if not objs: return - idx = self.index(objs[0]) - # Keep everything before the first object, then the ordered objects, - # then any other objects, then any non-objects after the first object - objnames = dict([(os.path.splitext(os.path.basename(o))[0], o) for o in objs]) - self[0:] = self[0:idx] + [objnames[o] for o in order_list if o in objnames] + \ - [o for o in objs if os.path.splitext(os.path.basename(o))[0] not in order_list] + \ - [x for x in self[idx:] if not isObject(x)] + def _getFoldedSections(self): + '''Returns a dict about folded sections. + When section A and B are folded into section C, the dict contains: + { 'A': 'C', + 'B': 'C', + 'C': ['A', 'B'] }''' + if not conf.LD_PRINT_ICF_SECTIONS: + return {} + + proc = subprocess.Popen(self + [conf.LD_PRINT_ICF_SECTIONS], stdout = subprocess.PIPE, stderr = subprocess.PIPE) + (stdout, stderr) = proc.communicate() + result = {} + # gold's --print-icf-sections output looks like the following: + # ld: ICF folding section '.section' in file 'file.o'into '.section' in file 'file.o' + # In terms of words, chances are this will change in the future, + # especially considering "into" is misplaced. Splitting on quotes + # seems safer. + for l in stderr.split('\n'): + quoted = l.split("'") + if len(quoted) > 5 and quoted[1] != quoted[5]: + result[quoted[1]] = quoted[5] + if quoted[5] in result: + result[quoted[5]].append(quoted[1]) + else: + result[quoted[5]] = [quoted[1]] + return result + + def _getOrderedSections(self, ordered_symbols): + '''Given an ordered list of symbols, returns the corresponding list + of sections following the order.''' + if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']: + raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE) + finder = SectionFinder([arg for arg in self if isObject(arg) or os.path.splitext(arg)[1] == conf.LIB_SUFFIX]) + folded = self._getFoldedSections() + sections = set() + ordered_sections = [] + for symbol in ordered_symbols: + symbol_sections = finder.getSections(symbol) + all_symbol_sections = [] + for section in symbol_sections: + if section in folded: + if isinstance(folded[section], str): + section = folded[section] + all_symbol_sections.append(section) + all_symbol_sections.extend(folded[section]) + else: + all_symbol_sections.append(section) + for section in all_symbol_sections: + if not section in sections: + ordered_sections.append(section) + sections.add(section) + return ordered_sections + + def orderSymbols(self, order): + '''Given a file containing a list of symbols, adds the appropriate + argument to make the linker put the symbols in that order.''' + with open(order) as file: + sections = self._getOrderedSections([l.strip() for l in file.readlines() if l.strip()]) + split_sections = {} + linked_sections = [s[0] for s in SECTION_INSERT_BEFORE] + for s in sections: + for linked_section in linked_sections: + if s.startswith(linked_section): + if linked_section in split_sections: + split_sections[linked_section].append(s) + else: + split_sections[linked_section] = [s] + break + content = [] + # Order is important + linked_sections = [s for s in linked_sections if s in split_sections] + if conf.EXPAND_LIBS_ORDER_STYLE == 'section-ordering-file': + option = '-Wl,--section-ordering-file,%s' + content = sections + for linked_section in linked_sections: + content.extend(split_sections[linked_section]) + content.append('%s.*' % linked_section) + content.append(linked_section) + + elif conf.EXPAND_LIBS_ORDER_STYLE == 'linkerscript': + option = '-Wl,-T,%s' + section_insert_before = dict(SECTION_INSERT_BEFORE) + for linked_section in linked_sections: + content.append('SECTIONS {') + content.append(' %s : {' % linked_section) + content.extend(' *(%s)' % s for s in split_sections[linked_section]) + content.append(' }') + content.append('}') + content.append('INSERT BEFORE %s' % section_insert_before[linked_section]) + else: + raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE) + + fd, tmp = tempfile.mkstemp(dir=os.curdir) + f = os.fdopen(fd, "w") + f.write('\n'.join(content)+'\n') + f.close() + self.tmp.append(tmp) + self.append(option % tmp) + +class SectionFinder(object): + '''Instances of this class allow to map symbol names to sections in + object files.''' + + def __init__(self, objs): + '''Creates an instance, given a list of object files.''' + if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']: + raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE) + self.mapping = {} + for obj in objs: + if not isObject(obj) and os.path.splitext(obj)[1] != conf.LIB_SUFFIX: + raise Exception('%s is not an object nor a static library' % obj) + for symbol, section in SectionFinder._getSymbols(obj): + sym = SectionFinder._normalize(symbol) + if sym in self.mapping: + if not section in self.mapping[sym]: + self.mapping[sym].append(section) + else: + self.mapping[sym] = [section] + + def getSections(self, symbol): + '''Given a symbol, returns a list of sections containing it or the + corresponding thunks. When the given symbol is a thunk, returns the + list of sections containing its corresponding normal symbol and the + other thunks for that symbol.''' + sym = SectionFinder._normalize(symbol) + if sym in self.mapping: + return self.mapping[sym] + return [] + + @staticmethod + def _normalize(symbol): + '''For normal symbols, return the given symbol. For thunks, return + the corresponding normal symbol.''' + if re.match('^_ZThn[0-9]+_', symbol): + return re.sub('^_ZThn[0-9]+_', '_Z', symbol) + return symbol + + @staticmethod + def _getSymbols(obj): + '''Returns a list of (symbol, section) contained in the given object + file.''' + proc = subprocess.Popen(['objdump', '-t', obj], stdout = subprocess.PIPE, stderr = subprocess.PIPE) + (stdout, stderr) = proc.communicate() + syms = [] + for line in stdout.splitlines(): + # Each line has the following format: + # <addr> [lgu!][w ][C ][W ][Ii ][dD ][FfO ] <section>\t<length> <symbol> + tmp = line.split(' ',1) + # This gives us ["<addr>", "[lgu!][w ][C ][W ][Ii ][dD ][FfO ] <section>\t<length> <symbol>"] + # We only need to consider cases where "<section>\t<length> <symbol>" is present, + # and where the [FfO] flag is either F (function) or O (object). + if len(tmp) > 1 and len(tmp[1]) > 6 and tmp[1][6] in ['O', 'F']: + tmp = tmp[1][8:].split() + # That gives us ["<section>","<length>", "<symbol>"] + syms.append((tmp[-1], tmp[0])) + return syms def main(): parser = OptionParser() parser.add_option("--extract", action="store_true", dest="extract", help="when a library has no descriptor file, extract it first, when possible") parser.add_option("--uselist", action="store_true", dest="uselist", help="use a list file for objects when executing a command") parser.add_option("--verbose", action="store_true", dest="verbose", help="display executed command and temporary files content") - parser.add_option("--reorder", dest="reorder", - help="reorder the objects according to the given list", metavar="FILE") + parser.add_option("--symbol-order", dest="symbol_order", metavar="FILE", + help="use the given list of symbols to order symbols in the resulting binary when using with a linker") (options, args) = parser.parse_args() with ExpandArgsMore(args) as args: - if options.extract or options.reorder: + if options.extract: args.extract() - if options.reorder: - with open(options.reorder) as file: - args.reorder([l.strip() for l in file.readlines()]) + if options.symbol_order: + args.orderSymbols(options.symbol_order) if options.uselist: args.makelist() if options.verbose: print >>sys.stderr, "Executing: " + " ".join(args) for tmp in [f for f in args.tmp if os.path.isfile(f)]: print >>sys.stderr, tmp + ":" with open(tmp) as file:
--- a/js/src/configure.in +++ b/js/src/configure.in @@ -4824,47 +4824,17 @@ else _MOZ_RTTI_FLAGS=$_MOZ_RTTI_FLAGS_OFF fi AC_SUBST(_MOZ_RTTI_FLAGS_ON) AC_DEFINE(CPP_THROW_NEW, [throw()]) AC_LANG_C -dnl ======================================================== -dnl = -dnl = Check what kind of list files are supported by the -dnl = linker -dnl = -dnl ======================================================== - -AC_CACHE_CHECK(what kind of list files are supported by the linker, - EXPAND_LIBS_LIST_STYLE, - [echo "int main() {return 0;}" > conftest.${ac_ext} - if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&2) && test -s conftest.${OBJ_SUFFIX}; then - echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list - if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&2) && test -s conftest${ac_exeext}; then - EXPAND_LIBS_LIST_STYLE=linkerscript - else - echo "conftest.${OBJ_SUFFIX}" > conftest.list - if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&2) && test -s conftest${ac_exeext}; then - EXPAND_LIBS_LIST_STYLE=list - else - EXPAND_LIBS_LIST_STYLE=none - fi - fi - else - dnl We really don't expect to get here, but just in case - AC_ERROR([couldn't compile a simple C file]) - fi - rm -rf conftest*]) - -LIBS_DESC_SUFFIX=desc -AC_SUBST(LIBS_DESC_SUFFIX) -AC_SUBST(EXPAND_LIBS_LIST_STYLE) +MOZ_EXPAND_LIBS dnl ======================================================== dnl = dnl = Build depencency options dnl = dnl ======================================================== MOZ_ARG_HEADER(Build dependencies)
--- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -1317,22 +1317,28 @@ TokenStream::checkForKeyword(const jscha *ttp = kw->tokentype; *topp = (JSOp) kw->op; return true; } return ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR, JSMSG_RESERVED_ID, kw->chars); } + /* The let keyword is reserved on <1.7 */ + if (kw->tokentype == TOK_LET) { + return ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR, + JSMSG_RESERVED_ID, kw->chars); + } + /* * The keyword is not in this version. Treat it as an identifier, - * unless it is let or yield which we treat as TOK_STRICT_RESERVED by - * falling through to the code below (ES5 forbids them in strict mode). + * unless it is yield which we treat as TOK_STRICT_RESERVED by + * falling through to the code below (ES5 forbids it in strict mode). */ - if (kw->tokentype != TOK_LET && kw->tokentype != TOK_YIELD) + if (kw->tokentype != TOK_YIELD) return true; } /* Strict reserved word. */ if (isStrictMode()) return ReportStrictModeError(cx, this, NULL, NULL, JSMSG_RESERVED_ID, kw->chars); return ReportCompileErrorNumber(cx, this, NULL, JSREPORT_STRICT | JSREPORT_WARNING, JSMSG_RESERVED_ID, kw->chars);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug734196.js @@ -0,0 +1,4 @@ +var x = new ArrayBuffer(2); +gczeal(4); +actual = [].concat(x).toString(); +var count2 = countHeap();
--- a/js/src/jit-test/tests/basic/regress-bug720680.js +++ b/js/src/jit-test/tests/basic/regress-bug720680.js @@ -4,12 +4,12 @@ eval("\ function TimeFromYear( y ) {}\ addTestCase( -2208988800000 );\ function addTestCase( t ) {\ var start = TimeFromYear((addTestCase(addTestCase << t, 0)));\ new TestCase( \ SECTION,\ '(new Date('+d+')).getUTCDay()',\ WeekDay((d)),\ - (new Date(let ({ stop } = 'properties.length' )('/ab[c\\\n]/'))).getUTCDay() \ + (new Date(foo ({ stop } = 'properties.length' )('/ab[c\\\n]/'))).getUTCDay() \ );\ }\ ");
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/bug732423.js @@ -0,0 +1,23 @@ +function testXML(x, y) { + for (var i = 0; i < 60; i++) { + x[y](); + x[y]; + } +} +testXML(<x/>, "elements"); + +var o = { + res: 0, + f: function() { this.res += 3; }, + __noSuchMethod__: function() { this.res += 5; } +}; + +function testNoSuchMethod(x, y) { + for (var i = 0; i < 60; i++) { + x[y](); + } +} + +testNoSuchMethod(o, "f"); +testNoSuchMethod(o, "g"); +assertEq(o.res, 480);
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -836,17 +836,17 @@ JSRuntime::init(uint32_t maxbytes) if (!(atomsCompartment = this->new_<JSCompartment>(this)) || !atomsCompartment->init(NULL) || !compartments.append(atomsCompartment)) { Foreground::delete_(atomsCompartment); return false; } atomsCompartment->isSystemCompartment = true; - atomsCompartment->setGCLastBytes(8192, GC_NORMAL); + atomsCompartment->setGCLastBytes(8192, 8192, GC_NORMAL); if (!js_InitAtomState(this)) return false; if (!InitRuntimeNumberState(this)) return false; dtoaState = js_NewDtoaState(); @@ -1009,18 +1009,16 @@ static void StartRequest(JSContext *cx) { JSRuntime *rt = cx->runtime; JS_ASSERT(rt->onOwnerThread()); if (rt->requestDepth) { rt->requestDepth++; } else { - AutoLockGC lock(rt); - /* Indicate that a request is running. */ rt->requestDepth = 1; if (rt->activityCallback) rt->activityCallback(rt->activityCallbackArg, true); } } @@ -1029,20 +1027,16 @@ StopRequest(JSContext *cx) { JSRuntime *rt = cx->runtime; JS_ASSERT(rt->onOwnerThread()); JS_ASSERT(rt->requestDepth != 0); if (rt->requestDepth != 1) { rt->requestDepth--; } else { rt->conservativeGC.updateForRequestEnd(rt->suspendCount); - - /* Lock before clearing to interlock with ClaimScope, in jslock.c. */ - AutoLockGC lock(rt); - rt->requestDepth = 0; if (rt->activityCallback) rt->activityCallback(rt->activityCallbackArg, false); } } #endif /* JS_THREADSAFE */ @@ -1302,24 +1296,22 @@ SetOptionsCommon(JSContext *cx, unsigned cx->setCompileOptions(newcopts); cx->updateJITEnabled(); return oldopts; } JS_PUBLIC_API(uint32_t) JS_SetOptions(JSContext *cx, uint32_t options) { - AutoLockGC lock(cx->runtime); return SetOptionsCommon(cx, options); } JS_PUBLIC_API(uint32_t) JS_ToggleOptions(JSContext *cx, uint32_t options) { - AutoLockGC lock(cx->runtime); unsigned oldopts = cx->allOptions(); unsigned newopts = oldopts ^ options; return SetOptionsCommon(cx, newopts); } JS_PUBLIC_API(void) JS_SetJitHardening(JSRuntime *rt, JSBool enabled) { @@ -2198,16 +2190,28 @@ JS_ComputeThis(JSContext *cx, jsval *vp) AssertNoGC(cx); assertSameCompartment(cx, JSValueArray(vp, 2)); CallReceiver call = CallReceiverFromVp(vp); if (!BoxNonStrictThis(cx, call)) return JSVAL_NULL; return call.thisv(); } +JS_PUBLIC_API(void) +JS_MallocInCompartment(JSCompartment *comp, size_t nbytes) +{ + comp->mallocInCompartment(nbytes); +} + +JS_PUBLIC_API(void) +JS_FreeInCompartment(JSCompartment *comp, size_t nbytes) +{ + comp->freeInCompartment(nbytes); +} + JS_PUBLIC_API(void *) JS_malloc(JSContext *cx, size_t nbytes) { AssertNoGC(cx); CHECK_REQUEST(cx); return cx->malloc_(nbytes); } @@ -2879,17 +2883,16 @@ JS_IsAboutToBeFinalized(void *thing) return IsAboutToBeFinalized(t); } JS_PUBLIC_API(void) JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value) { switch (key) { case JSGC_MAX_BYTES: { - AutoLockGC lock(rt); JS_ASSERT(value >= rt->gcBytes); rt->gcMaxBytes = value; break; } case JSGC_MAX_MALLOC_BYTES: rt->setGCMaxMallocBytes(value); break; case JSGC_SLICE_TIME_BUDGET: @@ -4263,17 +4266,17 @@ prop_iter_trace(JSTracer *trc, JSObject if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() < 0) { /* * Native case: just mark the next property to visit. We don't need a * barrier here because the pointer is updated via setPrivate, which * always takes a barrier. */ Shape *tmp = (Shape *)pdata; MarkShapeUnbarriered(trc, &tmp, "prop iter shape"); - JS_ASSERT(tmp == pdata); + obj->setPrivateUnbarriered(tmp); } else { /* Non-native case: mark each id in the JSIdArray private. */ JSIdArray *ida = (JSIdArray *) pdata; MarkIdRange(trc, ida->length, ida->vector, "prop iter"); } } static Class prop_iter_class = { @@ -5506,30 +5509,18 @@ JS_SetOperationCallback(JSContext *cx, J JS_PUBLIC_API(JSOperationCallback) JS_GetOperationCallback(JSContext *cx) { return cx->operationCallback; } JS_PUBLIC_API(void) -JS_TriggerOperationCallback(JSContext *cx) -{ -#ifdef JS_THREADSAFE - AutoLockGC lock(cx->runtime); -#endif - cx->runtime->triggerOperationCallback(); -} - -JS_PUBLIC_API(void) -JS_TriggerRuntimeOperationCallback(JSRuntime *rt) -{ -#ifdef JS_THREADSAFE - AutoLockGC lock(rt); -#endif +JS_TriggerOperationCallback(JSRuntime *rt) +{ rt->triggerOperationCallback(); } JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx) { StackFrame *fp = cx->maybefp(); while (fp && fp->isDummyFrame())
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2874,16 +2874,22 @@ JS_THIS(JSContext *cx, jsval *vp) * or vice versa. Either use the provided this value with this macro, or * compute the boxed this value using those. * * N.B. constructors must not use JS_THIS_VALUE, as no 'this' object has been * created. */ #define JS_THIS_VALUE(cx,vp) ((vp)[1]) +extern JS_PUBLIC_API(void) +JS_MallocInCompartment(JSCompartment *comp, size_t nbytes); + +extern JS_PUBLIC_API(void) +JS_FreeInCompartment(JSCompartment *comp, size_t nbytes); + extern JS_PUBLIC_API(void *) JS_malloc(JSContext *cx, size_t nbytes); extern JS_PUBLIC_API(void *) JS_realloc(JSContext *cx, void *p, size_t nbytes); extern JS_PUBLIC_API(void) JS_free(JSContext *cx, void *p); @@ -4493,42 +4499,35 @@ Call(JSContext *cx, jsval thisv, JSObjec } /* namespace JS */ JS_BEGIN_EXTERN_C #endif /* __cplusplus */ /* * These functions allow setting an operation callback that will be called - * from the thread the context is associated with some time after any thread - * triggered the callback using JS_TriggerOperationCallback(cx). + * from the JS thread some time after any thread triggered the callback using + * JS_TriggerOperationCallback(rt). * - * In a threadsafe build the engine internally triggers operation callbacks - * under certain circumstances (i.e. GC and title transfer) to force the - * context to yield its current request, which the engine always - * automatically does immediately prior to calling the callback function. - * The embedding should thus not rely on callbacks being triggered through - * the external API only. + * To schedule the GC and for other activities the engine internally triggers + * operation callbacks. The embedding should thus not rely on callbacks being + * triggered through the external API only. * * Important note: Additional callbacks can occur inside the callback handler * if it re-enters the JS engine. The embedding must ensure that the callback * is disconnected before attempting such re-entry. */ - extern JS_PUBLIC_API(JSOperationCallback) JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback); extern JS_PUBLIC_API(JSOperationCallback) JS_GetOperationCallback(JSContext *cx); extern JS_PUBLIC_API(void) -JS_TriggerOperationCallback(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_TriggerRuntimeOperationCallback(JSRuntime *rt); +JS_TriggerOperationCallback(JSRuntime *rt); extern JS_PUBLIC_API(JSBool) JS_IsRunning(JSContext *cx); /* * Saving and restoring frame chains. * * These two functions are used to set aside cx's call stack while that stack
--- a/js/src/jsbool.cpp +++ b/js/src/jsbool.cpp @@ -181,23 +181,16 @@ js_InitBooleanClass(JSContext *cx, JSObj } JSString * js_BooleanToString(JSContext *cx, JSBool b) { return cx->runtime->atomState.booleanAtoms[b ? 1 : 0]; } -/* This function implements E-262-3 section 9.8, toString. */ -bool -js::BooleanToStringBuffer(JSContext *cx, JSBool b, StringBuffer &sb) -{ - return b ? sb.append("true") : sb.append("false"); -} - namespace js { bool BooleanGetPrimitiveValueSlow(JSContext *cx, JSObject &obj, Value *vp) { JS_ASSERT(ObjectClassIs(obj, ESClass_Boolean, cx)); JS_ASSERT(obj.isProxy());
--- a/js/src/jsbool.h +++ b/js/src/jsbool.h @@ -49,19 +49,16 @@ extern JSObject * js_InitBooleanClass(JSContext *cx, JSObject *obj); extern JSString * js_BooleanToString(JSContext *cx, JSBool b); namespace js { -extern bool -BooleanToStringBuffer(JSContext *cx, JSBool b, StringBuffer &sb); - inline bool BooleanGetPrimitiveValue(JSContext *cx, JSObject &obj, Value *vp); } /* namespace js */ extern JSBool js_ValueToBoolean(const js::Value &v);
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -113,17 +113,17 @@ JSRuntime::sizeOfExcludingThis(JSMallocS if (stackCommitted) *stackCommitted = stackSpace.sizeOfCommitted(); if (gcMarkerSize) *gcMarkerSize = gcMarker.sizeOfExcludingThis(mallocSizeOf); } -JS_FRIEND_API(void) +void JSRuntime::triggerOperationCallback() { /* * Use JS_ATOMIC_SET in the hope that it ensures the write will become * immediately visible to other processors polling the flag. */ JS_ATOMIC_SET(&interrupt, 1); } @@ -245,59 +245,58 @@ js_DestroyContext(JSContext *cx, JSDestr * JSCONTEXT_DESTROY callback is not allowed to fail and must * return true. */ DebugOnly<JSBool> callbackStatus = cxCallback(cx, JSCONTEXT_DESTROY); JS_ASSERT(callbackStatus); } } - JS_LOCK_GC(rt); JS_REMOVE_LINK(&cx->link); bool last = !rt->hasContexts(); - if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC) { + if (last) { JS_ASSERT(!rt->gcRunning); #ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(); + { + AutoLockGC lock(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); + } #endif - JS_UNLOCK_GC(rt); - - if (last) { - /* - * Dump remaining type inference results first. This printing - * depends on atoms still existing. - */ - { - AutoLockGC lock(rt); - for (CompartmentsIter c(rt); !c.done(); c.next()) - c->types.print(cx, false); - } - - /* Unpin all common atoms before final GC. */ - js_FinishCommonAtoms(cx); + + /* + * Dump remaining type inference results first. This printing + * depends on atoms still existing. + */ + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->types.print(cx, false); - /* Clear debugging state to remove GC roots. */ - for (CompartmentsIter c(rt); !c.done(); c.next()) - c->clearTraps(cx); - JS_ClearAllWatchPoints(cx); - - GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT); + /* Unpin all common atoms before final GC. */ + js_FinishCommonAtoms(cx); + + /* Clear debugging state to remove GC roots. */ + for (CompartmentsIter c(rt); !c.done(); c.next()) + c->clearTraps(cx); + JS_ClearAllWatchPoints(cx); + + GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT); + } else if (mode == JSDCM_FORCE_GC) { + JS_ASSERT(!rt->gcRunning); + GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT); + } else if (mode == JSDCM_MAYBE_GC) { + JS_ASSERT(!rt->gcRunning); + JS_MaybeGC(cx); + } - } else if (mode == JSDCM_FORCE_GC) { - GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT); - } else if (mode == JSDCM_MAYBE_GC) { - JS_MaybeGC(cx); - } - JS_LOCK_GC(rt); +#ifdef JS_THREADSAFE + { + AutoLockGC lock(rt); + rt->gcHelperThread.waitBackgroundSweepEnd(); } -#ifdef JS_THREADSAFE - rt->gcHelperThread.waitBackgroundSweepEnd(); #endif - JS_UNLOCK_GC(rt); Foreground::delete_(cx); } namespace js { bool AutoResolving::alreadyStartedSlow() const {
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -681,17 +681,17 @@ struct JSRuntime : js::RuntimeFriendFiel * to recove some memory or to report an error. Failures in malloc and * calloc are signaled by p == null and p == reinterpret_cast<void *>(1). * Other values of p mean a realloc failure. * * The function must be called outside the GC lock. */ JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx); - JS_FRIEND_API(void) triggerOperationCallback(); + void triggerOperationCallback(); void setJitHardening(bool enabled); bool getJitHardening() const { return jitHardening; } void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary, size_t *regexpCode, size_t *stackCommitted, size_t *gcMarker); @@ -1245,16 +1245,50 @@ class AutoXMLRooter : private AutoGCRoot #ifdef JS_THREADSAFE # define JS_LOCK_GC(rt) PR_Lock((rt)->gcLock) # define JS_UNLOCK_GC(rt) PR_Unlock((rt)->gcLock) #else # define JS_LOCK_GC(rt) # define JS_UNLOCK_GC(rt) #endif +class AutoLockGC +{ + public: + explicit AutoLockGC(JSRuntime *rt = NULL + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : runtime(rt) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + if (rt) + JS_LOCK_GC(rt); + } + + ~AutoLockGC() + { + if (runtime) + JS_UNLOCK_GC(runtime); + } + + bool locked() const { + return !!runtime; + } + + void lock(JSRuntime *rt) { + JS_ASSERT(rt); + JS_ASSERT(!runtime); + runtime = rt; + JS_LOCK_GC(rt); + } + + private: + JSRuntime *runtime; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + class AutoUnlockGC { private: JSRuntime *rt; JS_DECL_USE_GUARD_OBJECT_NOTIFIER public: explicit AutoUnlockGC(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM)
--- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -70,27 +70,29 @@ using namespace js; using namespace js::gc; JSCompartment::JSCompartment(JSRuntime *rt) : rt(rt), principals(NULL), needsBarrier_(false), gcBytes(0), gcTriggerBytes(0), - gcLastBytes(0), hold(false), typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), data(NULL), active(false), #ifdef JS_METHODJIT jaegerCompartment_(NULL), #endif regExps(rt), propertyTree(thisForCtor()), emptyTypeObject(NULL), + gcMallocAndFreeBytes(0), + gcTriggerMallocAndFreeBytes(0), + gcMallocBytes(0), debugModeBits(rt->debugMode ? DebugFromC : 0), mathCache(NULL), watchpointMap(NULL) { PodArrayZero(evalCache); setGCMaxMallocBytes(rt->gcMaxMallocBytes * 0.9); }
--- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -200,17 +200,16 @@ struct JSCompartment js::GCMarker *barrierTracer() { JS_ASSERT(needsBarrier_); return &rt->gcMarker; } size_t gcBytes; size_t gcTriggerBytes; - size_t gcLastBytes; size_t gcMaxMallocBytes; bool hold; bool isSystemCompartment; /* * Pool for analysis and intermediate type information in this compartment. * Cleared on every GC, unless the GC happens during analysis (indicated @@ -262,24 +261,16 @@ struct JSCompartment size_t sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf); void sizeOfTypeInferenceData(JS::TypeInferenceSizes *stats, JSMallocSizeOfFun mallocSizeOf); /* * Shared scope property tree, and arena-pool for allocating its nodes. */ js::PropertyTree propertyTree; -#ifdef DEBUG - /* Property metering. */ - unsigned livePropTreeNodes; - unsigned totalPropTreeNodes; - unsigned propTreeKidsChunks; - unsigned liveDictModeNodes; -#endif - /* Set of all unowned base shapes in the compartment. */ js::BaseShapeSet baseShapes; void sweepBaseShapeTable(JSContext *cx); /* Set of initial shapes in the compartment. */ js::InitialShapeSet initialShapes; void sweepInitialShapeTable(JSContext *cx); @@ -293,26 +284,36 @@ struct JSCompartment /* Get the default 'new' type for objects with a NULL prototype. */ inline js::types::TypeObject *getEmptyType(JSContext *cx); js::types::TypeObject *getLazyType(JSContext *cx, JSObject *proto); /* Cache to speed up object creation. */ js::NewObjectCache newObjectCache; + /* + * Keeps track of the total number of malloc bytes connected to a + * compartment's GC things. This counter should be used in preference to + * gcMallocBytes. These counters affect collection in the same way as + * gcBytes and gcTriggerBytes. + */ + size_t gcMallocAndFreeBytes; + size_t gcTriggerMallocAndFreeBytes; + private: + /* + * Malloc counter to measure memory pressure for GC scheduling. It runs from + * gcMaxMallocBytes down to zero. This counter should be used only when it's + * not possible to know the size of a free. + */ + ptrdiff_t gcMallocBytes; + enum { DebugFromC = 1, DebugFromJS = 2 }; - unsigned debugModeBits; // see debugMode() below - - /* - * Malloc counter to measure memory pressure for GC scheduling. It runs - * from gcMaxMallocBytes down to zero. - */ - volatile ptrdiff_t gcMallocBytes; + unsigned debugModeBits; // see debugMode() below public: js::NativeIterCache nativeIterCache; typedef js::Maybe<js::ToSourceCache> LazyToSourceCache; LazyToSourceCache toSourceCache; js::ScriptFilenameTable scriptFilenameTable; @@ -335,31 +336,40 @@ struct JSCompartment bool wrap(JSContext *cx, js::PropertyDescriptor *desc); bool wrap(JSContext *cx, js::AutoIdVector &props); void markTypes(JSTracer *trc); void discardJitCode(JSContext *cx); void sweep(JSContext *cx, bool releaseTypes); void purge(); - void setGCLastBytes(size_t lastBytes, js::JSGCInvocationKind gckind); + void setGCLastBytes(size_t lastBytes, size_t lastMallocBytes, js::JSGCInvocationKind gckind); void reduceGCTriggerBytes(size_t amount); - + void resetGCMallocBytes(); void setGCMaxMallocBytes(size_t value); void updateMallocCounter(size_t nbytes) { ptrdiff_t oldCount = gcMallocBytes; ptrdiff_t newCount = oldCount - ptrdiff_t(nbytes); gcMallocBytes = newCount; if (JS_UNLIKELY(newCount <= 0 && oldCount > 0)) onTooMuchMalloc(); } - + void onTooMuchMalloc(); + void mallocInCompartment(size_t nbytes) { + gcMallocAndFreeBytes += nbytes; + } + + void freeInCompartment(size_t nbytes) { + JS_ASSERT(gcMallocAndFreeBytes >= nbytes); + gcMallocAndFreeBytes -= nbytes; + } + js::DtoaCache dtoaCache; private: js::MathCache *mathCache; js::MathCache *allocMathCache(JSContext *cx); /*
--- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -604,39 +604,16 @@ js::DumpHeapComplete(JSRuntime *rt, FILE dtrc.visited.finish(); fflush(dtrc.output); } #endif namespace js { -/* static */ void -AutoLockGC::LockGC(JSRuntime *rt) -{ - JS_ASSERT(rt); - JS_LOCK_GC(rt); -} - -/* static */ void -AutoLockGC::UnlockGC(JSRuntime *rt) -{ - JS_ASSERT(rt); - JS_UNLOCK_GC(rt); -} - -void -AutoLockGC::lock(JSRuntime *rt) -{ - JS_ASSERT(rt); - JS_ASSERT(!runtime); - runtime = rt; - JS_LOCK_GC(rt); -} - JS_FRIEND_API(const JSStructuredCloneCallbacks *) GetContextStructuredCloneCallbacks(JSContext *cx) { return cx->runtime->structuredCloneCallbacks; } JS_FRIEND_API(JSVersion) VersionSetXML(JSVersion version, bool enable) @@ -669,22 +646,16 @@ GetOwnerThread(const JSContext *cx) } JS_FRIEND_API(unsigned) GetContextOutstandingRequests(const JSContext *cx) { return cx->outstandingRequests; } -JS_FRIEND_API(PRLock *) -GetRuntimeGCLock(const JSRuntime *rt) -{ - return rt->gcLock; -} - AutoSkipConservativeScan::AutoSkipConservativeScan(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) : context(cx) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; JSRuntime *rt = context->runtime; JS_ASSERT(rt->requestDepth >= 1); @@ -721,22 +692,16 @@ SetActivityCallback(JSRuntime *rt, Activ } JS_FRIEND_API(bool) IsContextRunningJS(JSContext *cx) { return !cx->stack.empty(); } -JS_FRIEND_API(void) -TriggerOperationCallback(JSRuntime *rt) -{ - rt->triggerOperationCallback(); -} - JS_FRIEND_API(const CompartmentVector&) GetRuntimeCompartments(JSRuntime *rt) { return rt->compartments; } JS_FRIEND_API(size_t) SizeOfJSContext()
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -182,18 +182,16 @@ extern JS_FRIEND_API(bool) JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs); #endif JS_END_EXTERN_C #ifdef __cplusplus -struct PRLock; - namespace js { struct ContextFriendFields { JSRuntime *const runtime; ContextFriendFields(JSRuntime *rt) : runtime(rt) { } @@ -568,19 +566,16 @@ GetPCCountScriptContents(JSContext *cx, #ifdef JS_THREADSAFE JS_FRIEND_API(void *) GetOwnerThread(const JSContext *cx); JS_FRIEND_API(unsigned) GetContextOutstandingRequests(const JSContext *cx); -JS_FRIEND_API(PRLock *) -GetRuntimeGCLock(const JSRuntime *rt); - class JS_FRIEND_API(AutoSkipConservativeScan) { public: AutoSkipConservativeScan(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM); ~AutoSkipConservativeScan(); private: JSContext *context; @@ -595,72 +590,36 @@ JS_FRIEND_API(bool) HasUnrootedGlobal(const JSContext *cx); typedef void (* ActivityCallback)(void *arg, JSBool active); /* * Sets a callback that is run whenever the runtime goes idle - the * last active request ceases - and begins activity - when it was - * idle and a request begins. Note: The callback is called under the - * GC lock. + * idle and a request begins. */ JS_FRIEND_API(void) SetActivityCallback(JSRuntime *rt, ActivityCallback cb, void *arg); -class JS_FRIEND_API(AutoLockGC) -{ - public: - explicit AutoLockGC(JSRuntime *rt = NULL - MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : runtime(rt) - { - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - if (rt) - LockGC(rt); - } - - ~AutoLockGC() - { - if (runtime) - UnlockGC(runtime); - } - - bool locked() const { - return !!runtime; - } - void lock(JSRuntime *rt); - - private: - static void LockGC(JSRuntime *rt); - static void UnlockGC(JSRuntime *rt); - - JSRuntime *runtime; - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - extern JS_FRIEND_API(const JSStructuredCloneCallbacks *) GetContextStructuredCloneCallbacks(JSContext *cx); extern JS_FRIEND_API(JSVersion) VersionSetXML(JSVersion version, bool enable); extern JS_FRIEND_API(bool) CanCallContextDebugHandler(JSContext *cx); extern JS_FRIEND_API(JSTrapStatus) CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *rval); extern JS_FRIEND_API(bool) IsContextRunningJS(JSContext *cx); -/* Must be called with GC lock taken. */ -extern JS_FRIEND_API(void) -TriggerOperationCallback(JSRuntime *rt); - class SystemAllocPolicy; typedef Vector<JSCompartment*, 0, SystemAllocPolicy> CompartmentVector; extern JS_FRIEND_API(const CompartmentVector&) GetRuntimeCompartments(JSRuntime *rt); extern JS_FRIEND_API(size_t) SizeOfJSContext();
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1303,53 +1303,30 @@ js_AddGCThingRoot(JSContext *cx, void ** if (!ok) JS_ReportOutOfMemory(cx); return ok; } JS_FRIEND_API(JSBool) js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name) { - /* - * Due to the long-standing, but now removed, use of rt->gcLock across the - * bulk of js::GC, API users have come to depend on JS_AddRoot etc. locking - * properly with a racing GC, without calling JS_AddRoot from a request. - * We have to preserve API compatibility here, now that we avoid holding - * rt->gcLock across the mark phase (including the root hashtable mark). - */ - AutoLockGC lock(rt); - return !!rt->gcRootsHash.put((void *)vp, RootInfo(name, JS_GC_ROOT_VALUE_PTR)); } JS_FRIEND_API(JSBool) js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name) { - /* - * Due to the long-standing, but now removed, use of rt->gcLock across the - * bulk of js::GC, API users have come to depend on JS_AddRoot etc. locking - * properly with a racing GC, without calling JS_AddRoot from a request. - * We have to preserve API compatibility here, now that we avoid holding - * rt->gcLock across the mark phase (including the root hashtable mark). - */ - AutoLockGC lock(rt); - return !!rt->gcRootsHash.put((void *)rp, RootInfo(name, JS_GC_ROOT_GCTHING_PTR)); } JS_FRIEND_API(JSBool) js_RemoveRoot(JSRuntime *rt, void *rp) { - /* - * Due to the JS_RemoveRootRT API, we may be called outside of a request. - * Same synchronization drill as above in js_AddRoot. - */ - AutoLockGC lock(rt); rt->gcRootsHash.remove(rp); rt->gcPoke = JS_TRUE; return JS_TRUE; } typedef RootedValueMap::Range RootRange; typedef RootedValueMap::Entry RootEntry; typedef RootedValueMap::Enum RootEnum; @@ -1399,41 +1376,45 @@ js_DumpNamedRoots(JSRuntime *rt, } } #endif /* DEBUG */ uint32_t js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) { - AutoLockGC lock(rt); int ct = 0; for (RootEnum e(rt->gcRootsHash); !e.empty(); e.popFront()) { RootEntry &entry = e.front(); ct++; int mapflags = map(entry.key, entry.value.type, entry.value.name, data); if (mapflags & JS_MAP_GCROOT_REMOVE) e.removeFront(); if (mapflags & JS_MAP_GCROOT_STOP) break; } return ct; } -void -JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind) +static size_t +ComputeTriggerBytes(size_t lastBytes, size_t maxBytes, JSGCInvocationKind gckind) { - gcLastBytes = lastBytes; - size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD); float trigger = float(base) * GC_HEAP_GROWTH_FACTOR; - gcTriggerBytes = size_t(Min(float(rt->gcMaxBytes), trigger)); + return size_t(Min(float(maxBytes), trigger)); +} + +void +JSCompartment::setGCLastBytes(size_t lastBytes, size_t lastMallocBytes, JSGCInvocationKind gckind) +{ + gcTriggerBytes = ComputeTriggerBytes(lastBytes, rt->gcMaxBytes, gckind); + gcTriggerMallocAndFreeBytes = ComputeTriggerBytes(lastMallocBytes, SIZE_MAX, gckind); } void JSCompartment::reduceGCTriggerBytes(size_t amount) { JS_ASSERT(amount > 0); JS_ASSERT(gcTriggerBytes - amount >= 0); if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR) @@ -1785,35 +1766,31 @@ js_GetGCThingTraceKind(void *thing) } JSBool js_LockGCThingRT(JSRuntime *rt, void *thing) { if (!thing) return true; - AutoLockGC lock(rt); if (GCLocks::Ptr p = rt->gcLocksHash.lookupWithDefault(thing, 0)) { p->value++; return true; } return false; } void js_UnlockGCThingRT(JSRuntime *rt, void *thing) { if (!thing) return; - AutoLockGC lock(rt); - GCLocks::Ptr p = rt->gcLocksHash.lookup(thing); - - if (p) { + if (GCLocks::Ptr p = rt->gcLocksHash.lookup(thing)) { rt->gcPoke = true; if (--p->value == 0) rt->gcLocksHash.remove(p); } } namespace js { @@ -2407,16 +2384,17 @@ MarkRuntime(JSTracer *trc, bool useSaved gcmarker->startBufferingGrayRoots(); (*op)(trc, rt->gcGrayRootsData); gcmarker->endBufferingGrayRoots(); } else { (*op)(trc, rt->gcGrayRootsData); } } } + void TriggerGC(JSRuntime *rt, gcreason::Reason reason) { JS_ASSERT(rt->onOwnerThread()); if (rt->gcRunning || rt->gcIsNeeded) return; @@ -2453,17 +2431,17 @@ TriggerCompartmentGC(JSCompartment *comp /* * Trigger the GC when it is safe to call an operation callback on any * thread. */ rt->gcIsNeeded = true; rt->gcTriggerCompartment = comp; rt->gcTriggerReason = reason; - comp->rt->triggerOperationCallback(); + rt->triggerOperationCallback(); } void MaybeGC(JSContext *cx) { JSRuntime *rt = cx->runtime; JS_ASSERT(rt->onOwnerThread()); @@ -2482,16 +2460,21 @@ MaybeGC(JSContext *cx) if (comp->gcBytes > 8192 && comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4) && rt->gcIncrementalState == NO_INCREMENTAL) { GCSlice(cx, NULL, GC_NORMAL, gcreason::MAYBEGC); return; } + if (comp->gcMallocAndFreeBytes >= comp->gcTriggerMallocAndFreeBytes) { + GCSlice(cx, comp, GC_NORMAL, gcreason::MAYBEGC); + return; + } + /* * Access to the counters and, on 32 bit, setting gcNextFullGCTime below * is not atomic and a race condition could trigger or suppress the GC. We * tolerate this. */ int64_t now = PRMJ_Now(); if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) { if (rt->gcChunkAllocationSinceLastGC || @@ -3267,17 +3250,17 @@ SweepPhase(JSContext *cx, JSGCInvocation { gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_XPCONNECT); if (rt->gcFinalizeCallback) rt->gcFinalizeCallback(cx, JSFINALIZE_END); } for (CompartmentsIter c(rt); !c.done(); c.next()) - c->setGCLastBytes(c->gcBytes, gckind); + c->setGCLastBytes(c->gcBytes, c->gcMallocAndFreeBytes, gckind); } /* Perform mark-and-sweep GC. If comp is set, we perform a single-compartment GC. */ static void MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind) { JSRuntime *rt = cx->runtime; @@ -3920,17 +3903,17 @@ NewCompartment(JSContext *cx, JSPrincipa // Any compartment with the trusted principals -- and there can be // multiple -- is a system compartment. compartment->isSystemCompartment = principals && rt->trustedPrincipals() == principals; if (principals) { compartment->principals = principals; JSPRINCIPALS_HOLD(cx, principals); } - compartment->setGCLastBytes(8192, GC_NORMAL); + compartment->setGCLastBytes(8192, 8192, GC_NORMAL); /* * Before reporting the OOM condition, |lock| needs to be cleaned up, * hence the scoping. */ { AutoLockGC lock(rt); @@ -4501,34 +4484,32 @@ ReleaseScriptPCCounters(JSContext *cx) cx->delete_(rt->scriptPCCounters); rt->scriptPCCounters = NULL; } JS_FRIEND_API(void) StartPCCountProfiling(JSContext *cx) { JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); if (rt->profilingScripts) return; if (rt->scriptPCCounters) ReleaseScriptPCCounters(cx); ReleaseAllJITCode(cx); rt->profilingScripts = true; } JS_FRIEND_API(void) StopPCCountProfiling(JSContext *cx) { JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); if (!rt->profilingScripts) return; JS_ASSERT(!rt->scriptPCCounters); ReleaseAllJITCode(cx); ScriptOpcodeCountsVector *vec = cx->new_<ScriptOpcodeCountsVector>(SystemAllocPolicy()); @@ -4551,17 +4532,16 @@ StopPCCountProfiling(JSContext *cx) rt->profilingScripts = false; rt->scriptPCCounters = vec; } JS_FRIEND_API(void) PurgePCCounts(JSContext *cx) { JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); if (!rt->scriptPCCounters) return; JS_ASSERT(!rt->profilingScripts); ReleaseScriptPCCounters(cx); } @@ -4570,16 +4550,20 @@ PurgePCCounts(JSContext *cx) JS_PUBLIC_API(void) JS_IterateCompartments(JSRuntime *rt, void *data, JSIterateCompartmentCallback compartmentCallback) { JS_ASSERT(!rt->gcRunning); AutoLockGC lock(rt); AutoHeapSession session(rt); +#ifdef JS_THREADSAFE + rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); +#endif + AutoUnlockGC unlock(rt); for (CompartmentsIter c(rt); !c.done(); c.next()) (*compartmentCallback)(rt, data, c); } #if JS_HAS_XML_SUPPORT extern size_t sE4XObjectsCreated;
--- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2174,27 +2174,16 @@ TypeCompartment::nukeTypes(JSContext *cx * inconsistent state. */ JS_ASSERT(pendingNukeTypes); if (pendingRecompiles) { cx->free_(pendingRecompiles); pendingRecompiles = NULL; } - /* - * We may or may not be under the GC. In either case don't allocate, and - * acquire the GC lock so we can update inferenceEnabled for all contexts. - */ - -#ifdef JS_THREADSAFE - AutoLockGC maybeLock; - if (!cx->runtime->gcRunning) - maybeLock.lock(cx->runtime); -#endif - inferenceEnabled = false; /* Update the cached inferenceEnabled bit in all contexts. */ for (ContextIter acx(cx->runtime); !acx.done(); acx.next()) acx->setCompartment(acx->compartment); #ifdef JS_METHODJIT
--- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2556,55 +2556,27 @@ BEGIN_CASE(JSOP_SETMETHOD) goto error; regs.sp[-2] = regs.sp[-1]; regs.sp--; } END_CASE(JSOP_SETPROP) BEGIN_CASE(JSOP_GETELEM) +BEGIN_CASE(JSOP_CALLELEM) { Value &lref = regs.sp[-2]; Value &rref = regs.sp[-1]; - if (!GetElementOperation(cx, lref, rref, ®s.sp[-2])) + if (!GetElementOperation(cx, op, lref, rref, ®s.sp[-2])) goto error; TypeScript::Monitor(cx, script, regs.pc, regs.sp[-2]); regs.sp--; } END_CASE(JSOP_GETELEM) -BEGIN_CASE(JSOP_CALLELEM) -{ - /* Find the object on which to look for |this|'s properties. */ - Value thisv = regs.sp[-2]; - JSObject *thisObj = ValuePropertyBearer(cx, regs.fp(), thisv, -2); - if (!thisObj) - goto error; - - /* Fetch index and convert it to id suitable for use with obj. */ - jsid id; - FETCH_ELEMENT_ID(thisObj, -1, id); - - /* Get the method. */ - if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, ®s.sp[-2])) - goto error; - -#if JS_HAS_NO_SUCH_METHOD - if (JS_UNLIKELY(regs.sp[-2].isPrimitive()) && thisv.isObject()) { - if (!OnUnknownMethod(cx, &thisv.toObject(), regs.sp[-1], regs.sp - 2)) - goto error; - } -#endif - - regs.sp--; - - TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]); -} -END_CASE(JSOP_CALLELEM) - BEGIN_CASE(JSOP_SETELEM) { JSObject *obj; FETCH_OBJECT(cx, -3, obj); jsid id; FETCH_ELEMENT_ID(obj, -2, id); Value &value = regs.sp[-1]; if (!SetObjectElementOperation(cx, obj, id, value, script->strictModeCode))
--- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -697,18 +697,27 @@ ToIdOperation(JSContext *cx, const Value return false; if (!res->isInt32()) types::TypeScript::MonitorUnknown(cx); return true; } static JS_ALWAYS_INLINE bool -GetObjectElementOperation(JSContext *cx, JSObject *obj, const Value &rref, Value *res) +GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *obj, const Value &rref, Value *res) { +#if JS_HAS_XML_SUPPORT + if (op == JSOP_CALLELEM && JS_UNLIKELY(obj->isXML())) { + jsid id; + if (!FetchElementId(cx, obj, rref, id, res)) + return false; + return js_GetXMLMethod(cx, obj, id, res); + } +#endif + uint32_t index; if (IsDefinitelyIndex(rref, &index)) { do { if (obj->isDenseArray()) { if (index < obj->getDenseArrayInitializedLength()) { *res = obj->getDenseArrayElement(index); if (!res->isMagic()) break; @@ -748,18 +757,20 @@ GetObjectElementOperation(JSContext *cx, } } assertSameCompartment(cx, *res); return true; } static JS_ALWAYS_INLINE bool -GetElementOperation(JSContext *cx, const Value &lref, const Value &rref, Value *res) +GetElementOperation(JSContext *cx, JSOp op, const Value &lref, const Value &rref, Value *res) { + JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM); + if (lref.isString() && rref.isInt32()) { JSString *str = lref.toString(); int32_t i = rref.toInt32(); if (size_t(i) < str->length()) { str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(i)); if (!str) return false; res->setString(str); @@ -771,20 +782,30 @@ GetElementOperation(JSContext *cx, const if (rref.isInt32() && size_t(rref.toInt32()) < cx->regs().fp()->numActualArgs()) { *res = cx->regs().fp()->canonicalActualArg(rref.toInt32()); return true; } types::MarkArgumentsCreated(cx, cx->fp()->script()); JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS)); } + bool isObject = lref.isObject(); JSObject *obj = ValueToObject(cx, lref); if (!obj) return false; - return GetObjectElementOperation(cx, obj, rref, res); + if (!GetObjectElementOperation(cx, op, obj, rref, res)) + return false; + +#if JS_HAS_NO_SUCH_METHOD + if (op == JSOP_CALLELEM && JS_UNLIKELY(res->isPrimitive()) && isObject) { + if (!OnUnknownMethod(cx, obj, rref, res)) + return false; + } +#endif + return true; } static JS_ALWAYS_INLINE bool SetObjectElementOperation(JSContext *cx, JSObject *obj, jsid id, const Value &value, bool strict) { types::TypeScript::MonitorAssign(cx, obj, id); do {
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -769,16 +769,17 @@ struct JSObject : public js::ObjectImpl inline js::GlobalObject &global() const; /* Private data accessors. */ inline bool hasPrivate() const; inline void *getPrivate() const; inline void setPrivate(void *data); + inline void setPrivateUnbarriered(void *data); inline void initPrivate(void *data); /* Access private data for an object with a known number of fixed slots. */ inline void *getPrivate(size_t nfixed) const; /* N.B. Infallible: NULL means 'no principal', not an error. */ inline JSPrincipals *principals(JSContext *cx);
--- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -117,16 +117,23 @@ JSObject::setPrivate(void *data) void **pprivate = &privateRef(numFixedSlots()); privateWriteBarrierPre(pprivate); *pprivate = data; privateWriteBarrierPost(pprivate); } inline void +JSObject::setPrivateUnbarriered(void *data) +{ + void **pprivate = &privateRef(numFixedSlots()); + *pprivate = data; +} + +inline void JSObject::initPrivate(void *data) { privateRef(numFixedSlots()) = data; } inline bool JSObject::enumerate(JSContext *cx, JSIterateOp iterop, js::Value *statep, jsid *idp) {
--- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3260,36 +3260,16 @@ js::ToStringSlow(JSContext *cx, const Va } else if (v.isNull()) { str = cx->runtime->atomState.nullAtom; } else { str = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; } return str; } -/* This function implements E-262-3 section 9.8, toString. */ -bool -js::ValueToStringBufferSlow(JSContext *cx, const Value &arg, StringBuffer &sb) -{ - Value v = arg; - if (!ToPrimitive(cx, JSTYPE_STRING, &v)) - return false; - - if (v.isString()) - return sb.append(v.toString()); - if (v.isNumber()) - return NumberValueToStringBuffer(cx, v, sb); - if (v.isBoolean()) - return BooleanToStringBuffer(cx, v.toBoolean(), sb); - if (v.isNull()) - return sb.append(cx->runtime->atomState.nullAtom); - JS_ASSERT(v.isUndefined()); - return sb.append(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); -} - JS_FRIEND_API(JSString *) js_ValueToSource(JSContext *cx, const Value &v) { JS_CHECK_RECURSION(cx, return NULL); if (v.isUndefined()) return cx->runtime->atomState.void0Atom; if (v.isString())
--- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -327,17 +327,17 @@ ArrayBuffer::obj_trace(JSTracer *trc, JS { /* * If this object changes, it will get marked via the private data barrier, * so it's safe to leave it Unbarriered. */ JSObject *delegate = static_cast<JSObject*>(obj->getPrivate()); if (delegate) { MarkObjectUnbarriered(trc, &delegate, "arraybuffer.delegate"); - obj->setPrivate(delegate); + obj->setPrivateUnbarriered(delegate); } } static JSProperty * const PROPERTY_FOUND = reinterpret_cast<JSProperty *>(1); JSBool ArrayBuffer::obj_lookupGeneric(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp)
--- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -2655,16 +2655,26 @@ ic::GetElement(VMFrame &f, ic::GetElemen Value idval = f.regs.sp[-1]; RecompilationMonitor monitor(cx); JSObject *obj = ValueToObject(cx, f.regs.sp[-2]); if (!obj) THROW(); +#if JS_HAS_XML_SUPPORT + // Some XML properties behave differently when accessed in a call vs. normal + // context, so we fall back to stubs::GetElem. + if (obj->isXML()) { + ic->disable(f, "XML object"); + stubs::GetElem(f); + return; + } +#endif + jsid id; if (idval.isInt32() && INT_FITS_IN_JSID(idval.toInt32())) { id = INT_TO_JSID(idval.toInt32()); } else { if (!js_InternNonIntElementId(cx, obj, idval, &id)) THROW(); } @@ -2680,16 +2690,23 @@ ic::GetElement(VMFrame &f, ic::GetElemen // If the result can be cached, the value was already retrieved. JS_ASSERT(!f.regs.sp[-2].isMagic()); return; } } if (!obj->getGeneric(cx, id, &f.regs.sp[-2])) THROW(); + +#if JS_HAS_NO_SUCH_METHOD + if (*f.pc() == JSOP_CALLELEM && JS_UNLIKELY(f.regs.sp[-2].isPrimitive())) { + if (!OnUnknownMethod(cx, obj, idval, &f.regs.sp[-2])) + THROW(); + } +#endif } #define APPLY_STRICTNESS(f, s) \ (FunctionTemplateConditional(s, f<true>, f<false>)) LookupStatus SetElementIC::disable(VMFrame &f, const char *reason) {
--- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -129,89 +129,22 @@ stubs::Name(VMFrame &f) if (!NameOperation(f.cx, f.pc(), &rval)) THROW(); f.regs.sp[0] = rval; } void JS_FASTCALL stubs::GetElem(VMFrame &f) { - JSContext *cx = f.cx; - FrameRegs ®s = f.regs; - - Value &lref = regs.sp[-2]; - Value &rref = regs.sp[-1]; - Value &rval = regs.sp[-2]; - if (lref.isString() && rref.isInt32()) { - JSString *str = lref.toString(); - int32_t i = rref.toInt32(); - if ((size_t)i < str->length()) { - str = f.cx->runtime->staticStrings.getUnitStringForElement(cx, str, (size_t)i); - if (!str) - THROW(); - rval.setString(str); - return; - } - } - - if (lref.isMagic(JS_LAZY_ARGUMENTS)) { - if (rref.isInt32() && size_t(rref.toInt32()) < regs.fp()->numActualArgs()) { - rval = regs.fp()->canonicalActualArg(rref.toInt32()); - return; - } - MarkArgumentsCreated(cx, f.script()); - JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS)); - } - - bool isObject = lref.isObject(); - JSObject *obj = ValueToObject(cx, lref); - if (!obj) - THROW(); + Value &lref = f.regs.sp[-2]; + Value &rref = f.regs.sp[-1]; + Value &rval = f.regs.sp[-2]; - uint32_t index; - if (IsDefinitelyIndex(rref, &index)) { - if (obj->isDenseArray()) { - if (index < obj->getDenseArrayInitializedLength()) { - rval = obj->getDenseArrayElement(index); - if (!rval.isMagic()) - return; - } - } else if (obj->isArguments()) { - if (obj->asArguments().getElement(index, &rval)) - return; - } - - if (!obj->getElement(cx, index, &rval)) - THROW(); - } else { - SpecialId special; - if (ValueIsSpecial(obj, &rref, &special, cx)) { - if (!obj->getSpecial(cx, obj, special, &rval)) - THROW(); - } else { - JSAtom *name; - if (!js_ValueToAtom(cx, rref, &name)) - THROW(); - - if (name->isIndex(&index)) { - if (!obj->getElement(cx, index, &rval)) - THROW(); - } else { - if (!obj->getProperty(cx, name->asPropertyName(), &rval)) - THROW(); - } - } - } - -#if JS_HAS_NO_SUCH_METHOD - if (*f.pc() == JSOP_CALLELEM && JS_UNLIKELY(rval.isPrimitive()) && isObject) { - if (!OnUnknownMethod(cx, obj, rref, &rval)) - THROW(); - } -#endif + if (!GetElementOperation(f.cx, JSOp(*f.pc()), lref, rref, &rval)) + THROW(); } template<JSBool strict> void JS_FASTCALL stubs::SetElem(VMFrame &f) { JSContext *cx = f.cx; FrameRegs ®s = f.regs;
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3129,17 +3129,17 @@ CancelExecution(JSRuntime *rt) { gCanceled = true; if (gExitCode == 0) gExitCode = EXITCODE_TIMEOUT; #ifdef JS_THREADSAFE if (gWorkerThreadPool) js::workers::terminateAll(gWorkerThreadPool); #endif - JS_TriggerRuntimeOperationCallback(rt); + JS_TriggerOperationCallback(rt); static const char msg[] = "Script runs for too long, terminating.\n"; #if defined(XP_UNIX) && !defined(JS_THREADSAFE) /* It is not safe to call fputs from signals. */ /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */ ssize_t dummy = write(2, msg, sizeof(msg) - 1); (void)dummy; #else
--- a/js/src/shell/jsworkers.cpp +++ b/js/src/shell/jsworkers.cpp @@ -822,17 +822,17 @@ class Worker MOZ_FINAL : public WorkerPa return false; return events.push(event); } void setTerminateFlag() { AutoLock hold(lock); terminateFlag = true; if (current) - JS_TriggerOperationCallback(context); + JS_TriggerOperationCallback(runtime); } void notifyTerminating() { setTerminateFlag(); WorkerParent::notifyTerminating(); } void processOneEvent();
--- a/js/src/tests/ecma_5/misc/future-reserved-words.js +++ b/js/src/tests/ecma_5/misc/future-reserved-words.js @@ -16,24 +16,24 @@ print(BUGNUMBER + ": " + summary); var futureReservedWords = [ "class", // "const", // Mozilla extension enabled even for versionless code "enum", "export", "extends", "import", + "let", // Reserved even for versionless code, contrary to ES5 - see bug 730139 "super", ]; var strictFutureReservedWords = [ "implements", "interface", - "let", // enabled: this file doesn't execute as JS1.7 "package", "private", "protected", "public", "static", "yield", // enabled: this file doesn't execute as JS1.7 ];
--- a/js/src/tests/js1_5/LexicalConventions/jstests.list +++ b/js/src/tests/js1_5/LexicalConventions/jstests.list @@ -1,5 +1,6 @@ url-prefix ../../jsreftest.html?test=js1_5/LexicalConventions/ +script let.js script lexical-001.js script regress-177314.js script regress-343675.js script regress-469940.js
new file mode 100644 --- /dev/null +++ b/js/src/tests/js1_5/LexicalConventions/let.js @@ -0,0 +1,14 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var actual = 'No error'; +try { + eval("var let = true"); +} catch (exc) { + actual = exc + ''; +} + +reportCompare('SyntaxError: let is a reserved identifier', actual, 'ok'); +
--- a/js/src/tests/js1_5/Regress/regress-351515.js +++ b/js/src/tests/js1_5/Regress/regress-351515.js @@ -42,27 +42,25 @@ var actual = 'No Error'; var expect = 'No Error'; //----------------------------------------------------------------------------- test(); //----------------------------------------------------------------------------- yield = 1; -let = 1; function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); - function f(yield, let) { return yield+let; } + function f(yield) { return yield; } var yield = 1; - var let = 1; function yield() {} reportCompare(expect, actual, summary); exitFunc ('test'); }
--- a/js/src/tests/js1_6/extensions/regress-352392.js +++ b/js/src/tests/js1_6/extensions/regress-352392.js @@ -47,17 +47,17 @@ test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); - expect = 'SyntaxError: invalid for each loop'; + expect = 'SyntaxError: let is a reserved identifier'; try { var obj = { }; Object.defineProperty(obj, "y", { get: Array.prototype.map, enumerable: true, configurable: true }); eval('(function() { for each(let z in obj) { } })()'); } catch(ex) {
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -424,17 +424,16 @@ Breakpoint::nextInSite() Debugger::Debugger(JSContext *cx, JSObject *dbg) : object(dbg), uncaughtExceptionHook(NULL), enabled(true), frames(cx), scripts(cx), objects(cx), environments(cx) { assertSameCompartment(cx, dbg); JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); JS_APPEND_LINK(&link, &rt->debuggerList); JS_INIT_CLIST(&breakpoints); } Debugger::~Debugger() { JS_ASSERT(debuggees.empty()); @@ -3210,17 +3209,17 @@ DebuggerObject_trace(JSTracer *trc, JSOb { if (!trc->runtime->gcCurrentCompartment) { /* * There is a barrier on private pointers, so the Unbarriered marking * is okay. */ if (JSObject *referent = (JSObject *) obj->getPrivate()) { MarkObjectUnbarriered(trc, &referent, "Debugger.Object referent"); - obj->setPrivate(referent); + obj->setPrivateUnbarriered(referent); } } } Class DebuggerObject_class = { "Object", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT), @@ -3854,17 +3853,17 @@ DebuggerEnv_trace(JSTracer *trc, JSObjec { if (!trc->runtime->gcCurrentCompartment) { /* * There is a barrier on private pointers, so the Unbarriered marking * is okay. */ if (Env *referent = (JSObject *) obj->getPrivate()) { MarkObjectUnbarriered(trc, &referent, "Debugger.Environment referent"); - obj->setPrivate(referent); + obj->setPrivateUnbarriered(referent); } } } Class DebuggerEnv_class = { "Environment", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
--- a/js/src/vm/StringBuffer-inl.h +++ b/js/src/vm/StringBuffer-inl.h @@ -62,23 +62,31 @@ StringBuffer::append(const jschar *begin inline bool StringBuffer::appendN(const jschar c, size_t n) { if (!checkLength(cb.length() + n)) return false; return cb.appendN(c, n); } +/* ES5 9.8 ToString, appending the result to the string buffer. */ extern bool ValueToStringBufferSlow(JSContext *cx, const Value &v, StringBuffer &sb); inline bool ValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb) { if (v.isString()) return sb.append(v.toString()); return ValueToStringBufferSlow(cx, v, sb); } +/* ES5 9.8 ToString for booleans, appending the result to the string buffer. */ +inline bool +BooleanToStringBuffer(JSContext *cx, bool b, StringBuffer &sb) +{ + return b ? sb.append("true") : sb.append("false"); +} + } /* namespace js */ #endif /* StringBuffer_inl_h__ */
--- a/js/src/vm/StringBuffer.cpp +++ b/js/src/vm/StringBuffer.cpp @@ -1,16 +1,18 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * 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 "vm/StringBuffer.h" +#include "jsobjinlines.h" + #include "vm/String-inl.h" #include "vm/StringBuffer-inl.h" using namespace js; jschar * StringBuffer::extractWellSized() { @@ -40,18 +42,17 @@ StringBuffer::extractWellSized() JSFixedString * StringBuffer::finishString() { JSContext *cx = context(); if (cb.empty()) return cx->runtime->atomState.emptyAtom; size_t length = cb.length(); - if (!checkLength(length)) - return NULL; + JS_ASSERT(checkLength(length)); JS_STATIC_ASSERT(JSShortString::MAX_SHORT_LENGTH < CharBuffer::InlineLength); if (JSShortString::lengthFits(length)) return NewShortString(cx, cb.begin(), length); if (!cb.append('\0')) return NULL; @@ -73,8 +74,27 @@ StringBuffer::finishAtom() size_t length = cb.length(); if (length == 0) return cx->runtime->atomState.emptyAtom; JSAtom *atom = js_AtomizeChars(cx, cb.begin(), length); cb.clear(); return atom; } + +bool +js::ValueToStringBufferSlow(JSContext *cx, const Value &arg, StringBuffer &sb) +{ + Value v = arg; + if (!ToPrimitive(cx, JSTYPE_STRING, &v)) + return false; + + if (v.isString()) + return sb.append(v.toString()); + if (v.isNumber()) + return NumberValueToStringBuffer(cx, v, sb); + if (v.isBoolean()) + return BooleanToStringBuffer(cx, v.toBoolean(), sb); + if (v.isNull()) + return sb.append(cx->runtime->atomState.nullAtom); + JS_ASSERT(v.isUndefined()); + return sb.append(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); +}
--- a/js/src/vm/StringBuffer.h +++ b/js/src/vm/StringBuffer.h @@ -21,110 +21,96 @@ namespace js { * string length. * * Any operation which would exceed the maximum string length causes an * exception report on the context and results in a failed return value. * * Well-sized extractions (which waste no more than 1/4 of their char * buffer space) are guaranteed for strings built by this interface. * See |extractWellSized|. - * - * Note: over-allocation is not checked for when using the infallible - * |replaceRawBuffer|, so the implementation of |finishString| also must check - * for over-allocation. */ class StringBuffer { /* cb's buffer is taken by the new string so use ContextAllocPolicy. */ typedef Vector<jschar, 32, ContextAllocPolicy> CharBuffer; CharBuffer cb; - static inline bool checkLength(JSContext *cx, size_t length); inline bool checkLength(size_t length); JSContext *context() const { return cb.allocPolicy().context(); } jschar *extractWellSized(); StringBuffer(const StringBuffer &other) MOZ_DELETE; void operator=(const StringBuffer &other) MOZ_DELETE; public: - explicit inline StringBuffer(JSContext *cx); + explicit StringBuffer(JSContext *cx) : cb(cx) { } + inline bool reserve(size_t len); inline bool resize(size_t len); inline bool append(const jschar c); inline bool append(const jschar *chars, size_t len); inline bool append(const jschar *begin, const jschar *end); inline bool append(JSString *str); inline bool append(JSLinearString *str); inline bool appendN(const jschar c, size_t n); inline bool appendInflated(const char *cstr, size_t len); + template <size_t ArrayLength> + bool append(const char (&array)[ArrayLength]) { + return cb.append(array, array + ArrayLength - 1); /* No trailing '\0'. */ + } + /* Infallible variants usable when the corresponding space is reserved. */ void infallibleAppend(const jschar c) { cb.infallibleAppend(c); } void infallibleAppend(const jschar *chars, size_t len) { cb.infallibleAppend(chars, len); } void infallibleAppend(const jschar *begin, const jschar *end) { cb.infallibleAppend(begin, end); } void infallibleAppendN(const jschar c, size_t n) { cb.infallibleAppendN(c, n); } - JSAtom *atomize(unsigned flags = 0); - static JSAtom *atomize(JSContext *cx, const CharBuffer &cb, unsigned flags = 0); - static JSAtom *atomize(JSContext *cx, const jschar *begin, size_t length, unsigned flags = 0); - - void replaceRawBuffer(jschar *chars, size_t len) { cb.replaceRawBuffer(chars, len); } jschar *begin() { return cb.begin(); } jschar *end() { return cb.end(); } const jschar *begin() const { return cb.begin(); } const jschar *end() const { return cb.end(); } bool empty() const { return cb.empty(); } inline size_t length() const; /* * Creates a string from the characters in this buffer, then (regardless * whether string creation succeeded or failed) empties the buffer. */ JSFixedString *finishString(); /* Identical to finishString() except that an atom is created. */ JSAtom *finishAtom(); - - template <size_t ArrayLength> - bool append(const char (&array)[ArrayLength]) { - return cb.append(array, array + ArrayLength - 1); /* No trailing '\0'. */ - } }; -inline -StringBuffer::StringBuffer(JSContext *cx) - : cb(cx) -{} +inline bool +StringBuffer::append(JSLinearString *str) +{ + JS::Anchor<JSString *> anch(str); + return cb.append(str->chars(), str->length()); +} inline bool StringBuffer::append(JSString *str) { JSLinearString *linear = str->ensureLinear(context()); if (!linear) return false; return append(linear); } -inline bool -StringBuffer::append(JSLinearString *str) -{ - JS::Anchor<JSString *> anch(str); - return cb.append(str->chars(), str->length()); -} - inline size_t StringBuffer::length() const { JS_ASSERT(cb.length() <= JSString::MAX_LENGTH); return cb.length(); } inline bool
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -921,51 +921,64 @@ XPCJSRuntime::FinalizeCallback(JSContext xpc_NotifyAll(self->GetMapLock()); } break; } } } +class AutoLockWatchdog { + XPCJSRuntime* const mRuntime; + + public: + AutoLockWatchdog(XPCJSRuntime* aRuntime) + : mRuntime(aRuntime) { + PR_Lock(mRuntime->mWatchdogLock); + } + + ~AutoLockWatchdog() { + PR_Unlock(mRuntime->mWatchdogLock); + } +}; + //static void XPCJSRuntime::WatchdogMain(void *arg) { XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg); // Lock lasts until we return - js::AutoLockGC lock(self->mJSRuntime); + AutoLockWatchdog lock(self); PRIntervalTime sleepInterval; while (self->mWatchdogThread) { // Sleep only 1 second if recently (or currently) active; otherwise, hibernate if (self->mLastActiveTime == -1 || PR_Now() - self->mLastActiveTime <= PRTime(2*PR_USEC_PER_SEC)) sleepInterval = PR_TicksPerSecond(); else { sleepInterval = PR_INTERVAL_NO_TIMEOUT; self->mWatchdogHibernating = true; } -#ifdef DEBUG - PRStatus status = -#endif - PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval); - JS_ASSERT(status == PR_SUCCESS); - js::TriggerOperationCallback(self->mJSRuntime); + MOZ_ALWAYS_TRUE(PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval) == PR_SUCCESS); + JS_TriggerOperationCallback(self->mJSRuntime); } /* Wake up the main thread waiting for the watchdog to terminate. */ PR_NotifyCondVar(self->mWatchdogWakeup); } //static void XPCJSRuntime::ActivityCallback(void *arg, JSBool active) { XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg); + + AutoLockWatchdog lock(self); + if (active) { self->mLastActiveTime = -1; if (self->mWatchdogHibernating) { self->mWatchdogHibernating = false; PR_NotifyCondVar(self->mWatchdogWakeup); } } else { self->mLastActiveTime = PR_Now(); @@ -1053,24 +1066,25 @@ XPCJSRuntime::GetJSCycleCollectionContex XPCJSRuntime::~XPCJSRuntime() { if (mWatchdogWakeup) { // If the watchdog thread is running, tell it to terminate waking it // up if necessary and wait until it signals that it finished. As we // must release the lock before calling PR_DestroyCondVar, we use an // extra block here. { - js::AutoLockGC lock(mJSRuntime); + AutoLockWatchdog lock(this); if (mWatchdogThread) { mWatchdogThread = nsnull; PR_NotifyCondVar(mWatchdogWakeup); PR_WaitCondVar(mWatchdogWakeup, PR_INTERVAL_NO_TIMEOUT); } } PR_DestroyCondVar(mWatchdogWakeup); + PR_DestroyLock(mWatchdogLock); mWatchdogWakeup = nsnull; } if (mJSCycleCollectionContext) JS_DestroyContextNoGC(mJSCycleCollectionContext); #ifdef XPC_DUMP_AT_SHUTDOWN { @@ -2024,16 +2038,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* mMapLock(XPCAutoLock::NewLock("XPCJSRuntime::mMapLock")), mThreadRunningGC(nsnull), mWrappedJSToReleaseArray(), mNativesToReleaseArray(), mDoingFinalization(false), mVariantRoots(nsnull), mWrappedJSRoots(nsnull), mObjectHolderRoots(nsnull), + mWatchdogLock(nsnull), mWatchdogWakeup(nsnull), mWatchdogThread(nsnull), mWatchdogHibernating(false), mLastActiveTime(-1) { #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN DEBUG_WrappedNativeHashtable = JS_NewDHashTable(JS_DHashGetStubOps(), nsnull, @@ -2048,72 +2063,72 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* // these jsids filled in later when we have a JSContext to work with. mStrIDs[0] = JSID_VOID; mJSRuntime = JS_NewRuntime(32L * 1024L * 1024L); // pref ? if (!mJSRuntime) NS_RUNTIMEABORT("JS_NewRuntime failed."); - { - // Unconstrain the runtime's threshold on nominal heap size, to avoid - // triggering GC too often if operating continuously near an arbitrary - // finite threshold (0xffffffff is infinity for uint32_t parameters). - // This leaves the maximum-JS_malloc-bytes threshold still in effect - // to cause period, and we hope hygienic, last-ditch GCs from within - // the GC's allocator. - JS_SetGCParameter(mJSRuntime, JSGC_MAX_BYTES, 0xffffffff); + // Unconstrain the runtime's threshold on nominal heap size, to avoid + // triggering GC too often if operating continuously near an arbitrary + // finite threshold (0xffffffff is infinity for uint32_t parameters). + // This leaves the maximum-JS_malloc-bytes threshold still in effect + // to cause period, and we hope hygienic, last-ditch GCs from within + // the GC's allocator. + JS_SetGCParameter(mJSRuntime, JSGC_MAX_BYTES, 0xffffffff); #ifdef MOZ_ASAN - // ASan requires more stack space due to redzones - JS_SetNativeStackQuota(mJSRuntime, 2 * 128 * sizeof(size_t) * 1024); + // ASan requires more stack space due to redzones + JS_SetNativeStackQuota(mJSRuntime, 2 * 128 * sizeof(size_t) * 1024); #else - JS_SetNativeStackQuota(mJSRuntime, 128 * sizeof(size_t) * 1024); + JS_SetNativeStackQuota(mJSRuntime, 128 * sizeof(size_t) * 1024); #endif - JS_SetContextCallback(mJSRuntime, ContextCallback); - JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback); - JS_SetGCCallback(mJSRuntime, GCCallback); - JS_SetFinalizeCallback(mJSRuntime, FinalizeCallback); - JS_SetExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this); - JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);