| author | Carsten "Tomcat" Book <cbook@mozilla.com> |
| Fri, 27 Nov 2015 11:08:41 +0100 | |
| changeset 274444 | 47b49b0d32360fab04b11ff9120970979c426911 |
| parent 274380 | ad985ade8a2e5e58123f0a774605d273834c3ba1 (current diff) |
| parent 274443 | 48139ac411625d1ee8b21c51770b917502dba1d9 (diff) |
| child 274445 | 7883e81f3c305078353ca27a6b1adb8c769d5904 |
| child 274448 | bf0d6a7b6fa1374e52560dce73111e9de835100d |
| child 274479 | bb512bf5a0669afa0d8158daf906a4223f4ba6ce |
| child 274530 | 2c0df58d9443dec00c684b00b111dfaf96613997 |
| push id | 29730 |
| push user | cbook@mozilla.com |
| push date | Fri, 27 Nov 2015 10:08:56 +0000 |
| treeherder | mozilla-central@47b49b0d3236 [default view] [failures only] |
| perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
| reviewers | merge |
| milestone | 45.0a1 |
| first release with | nightly linux32
47b49b0d3236
/
45.0a1
/
20151127030231
/
files
nightly linux64
47b49b0d3236
/
45.0a1
/
20151127030231
/
files
nightly mac
47b49b0d3236
/
45.0a1
/
20151127030231
/
files
nightly win32
47b49b0d3236
/
45.0a1
/
20151127030231
/
files
nightly win64
47b49b0d3236
/
45.0a1
/
20151127030231
/
files
|
| last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
| releases | nightly linux32
45.0a1
/
20151127030231
/
pushlog to previous
nightly linux64
45.0a1
/
20151127030231
/
pushlog to previous
nightly mac
45.0a1
/
20151127030231
/
pushlog to previous
nightly win32
45.0a1
/
20151127030231
/
pushlog to previous
nightly win64
45.0a1
/
20151127030231
/
pushlog to previous
|
--- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -2156,17 +2156,17 @@ DocAccessible::MoveChild(Accessible* aCh MaybeNotifyOfValueChange(parent); FireDelayedEvent(reorderEvent); } void DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren, uint32_t aStartIdx) { - nsTArray<Accessible*> containers; + nsTArray<RefPtr<Accessible> > containers; for (auto idx = aStartIdx; idx < aChildren->Length(); idx++) { Accessible* child = aChildren->ElementAt(idx); // If the child is in the tree then remove it from the owner. if (child->IsInDocument()) { Accessible* owner = child->Parent(); if (!owner) { NS_ERROR("Cannot put the child back. No parent, a broken tree."); @@ -2193,17 +2193,21 @@ DocAccessible::PutChildrenBack(nsTArray< containers.IndexOf(container) == nsTArray<Accessible*>::NoIndex) { containers.AppendElement(container); } } // And put it back where it belongs to. aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx); for (uint32_t idx = 0; idx < containers.Length(); idx++) { - UpdateTreeOnInsertion(containers[idx]); + NS_ASSERTION(containers[idx]->IsInDocument(), + "A container has been destroyed."); + if (containers[idx]->IsInDocument()) { + UpdateTreeOnInsertion(containers[idx]); + } } } void DocAccessible::CacheChildrenInSubtree(Accessible* aRoot, Accessible** aFocusedAcc) { // If the accessible is focused then report a focus event after all related
--- a/browser/base/content/browser-gestureSupport.js +++ b/browser/base/content/browser-gestureSupport.js @@ -641,38 +641,43 @@ var gHistorySwipeAnimation = { // from 0. if (this._direction == "horizontal" || this._lastSwipeDir != "") { gBrowser.stop(); this._lastSwipeDir = "RELOAD"; // just ensure that != "" this._canGoBack = this.canGoBack(); this._canGoForward = this.canGoForward(); this._handleFastSwiping(); } + this.updateAnimation(0); } else { - this._startingIndex = gBrowser.webNavigation.sessionHistory.index; - this._historyIndex = this._startingIndex; - this._canGoBack = this.canGoBack(); - this._canGoForward = this.canGoForward(); - if (this.active) { - this._addBoxes(); - this._takeSnapshot(); - this._installPrevAndNextSnapshots(); - this._lastSwipeDir = ""; + // Get the session history from SessionStore. + let updateSessionHistory = sessionHistory => { + this._startingIndex = sessionHistory.index; + this._historyIndex = this._startingIndex; + this._canGoBack = this.canGoBack(); + this._canGoForward = this.canGoForward(); + if (this.active) { + this._addBoxes(); + this._takeSnapshot(); + this._installPrevAndNextSnapshots(); + this._lastSwipeDir = ""; + } + this.updateAnimation(0); } + SessionStore.getSessionHistory(gBrowser.selectedTab, updateSessionHistory); } - this.updateAnimation(0); }, /** * Stops the swipe animation. */ stopAnimation: function HSA_stopAnimation() { gHistorySwipeAnimation._removeBoxes(); - this._historyIndex = gBrowser.webNavigation.sessionHistory.index; + this._historyIndex = this._getCurrentHistoryIndex(); }, /** * Updates the animation between two pages in history. * * @param aVal * A floating point value that represents the progress of the * swipe gesture. @@ -721,16 +726,20 @@ var gHistorySwipeAnimation = { this._positionBox(this._nextBox, offset + aVal); } else { this._prevBox.collapsed = true; this._positionBox(this._curBox, aVal / dampValue); } } }, + _getCurrentHistoryIndex: function() { + return SessionStore.getSessionHistory(gBrowser.selectedTab).index; + }, + /** * Event handler for events relevant to the history swipe animation. * * @param aEvent * An event to process. */ handleEvent: function HSA_handleEvent(aEvent) { let browser = gBrowser.selectedBrowser; @@ -816,32 +825,36 @@ var gHistorySwipeAnimation = { }, /** * Used to notify the history swipe animation that the OS sent a swipe end * event and that we should navigate to the page that the user swiped to, if * any. This will also result in the animation overlay to be torn down. */ swipeEndEventReceived: function HSA_swipeEndEventReceived() { - if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex) - this._navigateToHistoryIndex(); - else - this.stopAnimation(); + // Update the session history before continuing. + let updateSessionHistory = sessionHistory => { + if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex) + this._navigateToHistoryIndex(); + else + this.stopAnimation(); + } + SessionStore.getSessionHistory(gBrowser.selectedTab, updateSessionHistory); }, /** * Checks whether a particular index exists in the browser history or not. * * @param aIndex * The index to check for availability for in the history. * @return true if the index exists in the browser history, false otherwise. */ _doesIndexExistInHistory: function HSA__doesIndexExistInHistory(aIndex) { try { - gBrowser.webNavigation.sessionHistory.getEntryAtIndex(aIndex, false); + return SessionStore.getSessionHistory(gBrowser.selectedTab).entries[aIndex] != null; } catch(ex) { return false; } return true; }, /** @@ -954,21 +967,17 @@ var gHistorySwipeAnimation = { /** * Verifies that we're ready to take snapshots based on the global pref and * the current index in history. * * @return true if we're ready to take snapshots, false otherwise. */ _readyToTakeSnapshots: function HSA__readyToTakeSnapshots() { - if ((this._maxSnapshots < 1) || - (gBrowser.webNavigation.sessionHistory.index < 0)) { - return false; - } - return true; + return (this._maxSnapshots >= 1 && this._getCurrentHistoryIndex() >= 0); }, /** * Takes a snapshot of the page the browser is currently on. */ _takeSnapshot: function HSA__takeSnapshot() { if (!this._readyToTakeSnapshots()) { return; @@ -1021,17 +1030,17 @@ var gHistorySwipeAnimation = { * snapshot in the list. * * @param aCanvas * The snapshot to add to the list and compress. */ _assignSnapshotToCurrentBrowser: function HSA__assignSnapshotToCurrentBrowser(aCanvas) { let browser = gBrowser.selectedBrowser; - let currIndex = browser.webNavigation.sessionHistory.index; + let currIndex = this._getCurrentHistoryIndex(); this._removeTrackedSnapshot(currIndex, browser); this._addSnapshotRefToArray(currIndex, browser); if (!("snapshots" in browser)) browser.snapshots = []; let snapshots = browser.snapshots; // Temporarily store the canvas as the compressed snapshot. @@ -1054,17 +1063,17 @@ var gHistorySwipeAnimation = { // there's nothing to compress. return; } TelemetryStopwatch.start("FX_GESTURE_COMPRESS_SNAPSHOT_OF_PAGE"); try { let browser = gBrowser.selectedBrowser; let snapshots = browser.snapshots; - let currIndex = browser.webNavigation.sessionHistory.index; + let currIndex = _getCurrentHistoryIndex(); // Kick off snapshot compression. let canvas = snapshots[currIndex].image; canvas.toBlob(function(aBlob) { if (snapshots[currIndex]) { snapshots[currIndex].image = aBlob; } }, "image/png"
--- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -3372,27 +3372,26 @@ var E10SUINotification = { }, }; #else // E10S_TESTING_ONLY var E10SAccessibilityCheck = { init: function() { Services.obs.addObserver(this, "a11y-init-or-shutdown", true); - if (Services.appinfo.accessibilityIsBlacklistedForE10S) { + if (Services.appinfo.accessibilityEnabled) { this._showE10sAccessibilityWarning(); } }, QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]), observe: function(subject, topic, data) { if (topic == "a11y-init-or-shutdown" - && data == "1" && - Services.appinfo.accessibilityIsBlacklistedForE10S) { + && data == "1") { this._showE10sAccessibilityWarning(); } }, _warnedAboutAccessibility: false, _showE10sAccessibilityWarning: function() { try {
--- a/configure.in +++ b/configure.in @@ -319,27 +319,26 @@ if test -n "$gonkdir" ; then AC_DEFINE(HAVE_SYS_UIO_H) AC_DEFINE(HAVE_PTHREADS) MOZ_CHROME_FILE_FORMAT=omni direct_nspr_config=1 android_cxx_stl=mozstlport else if test "$COMPILE_ENVIRONMENT"; then MOZ_ANDROID_NDK - else - AC_DEFINE(ANDROID) fi # COMPILE_ENVIRONMENT case "$target" in *-android*|*-linuxandroid*) if test -z "$ANDROID_PACKAGE_NAME" ; then ANDROID_PACKAGE_NAME='org.mozilla.$(MOZ_APP_NAME)' fi MOZ_CHROME_FILE_FORMAT=omni ZLIB_DIR=yes + AC_DEFINE(ANDROID) ;; *-linux*) AC_PATH_PROG(OBJCOPY,objcopy) ;; esac fi case "$target" in @@ -4213,16 +4212,17 @@ cairo-uikit) AC_DEFINE(MOZ_WIDGET_UIKIT) LDFLAGS="$LDFLAGS -framework UIKit -lobjc" TK_CFLAGS="-DNO_X11" TK_LIBS='-Wl,-framework,Foundation -Wl,-framework,CoreFoundation -Wl,-framework,CoreGraphics -Wl,-framework,CoreText -Wl,-framework,AVFoundation -Wl,-framework,AudioToolbox -Wl,-framework,CoreMedia -Wl,-framework,CoreVideo -Wl,-framework,OpenGLES -Wl,-framework,QuartzCore' CFLAGS="$CFLAGS $TK_CFLAGS" CXXFLAGS="$CXXFLAGS $TK_CFLAGS" MOZ_USER_DIR="Mozilla" MOZ_FS_LAYOUT=bundle + AC_DEFINE(MOZ_SINGLE_PROCESS_APZ) ;; cairo-android) AC_DEFINE(MOZ_WIDGET_ANDROID) MOZ_WIDGET_TOOLKIT=android MOZ_PDF_PRINTING=1 MOZ_INSTRUMENT_EVENT_LOOP=1 ;; @@ -4796,18 +4796,21 @@ fi dnl ======================================================== dnl = Enable the C++ async pan/zoom code instead of the Java version dnl ======================================================== MOZ_ARG_ENABLE_BOOL(android-apz, [ --enable-android-apz Switch to C++ pan/zoom code], MOZ_ANDROID_APZ=1, MOZ_ANDROID_APZ=) if test -n "$MOZ_ANDROID_APZ"; then - dnl Do this if defined in confvars.sh - AC_DEFINE(MOZ_ANDROID_APZ) + dnl Do this if defined in confvars.sh + AC_DEFINE(MOZ_ANDROID_APZ) + if test -z "$MOZ_B2GDROID"; then + AC_DEFINE(MOZ_SINGLE_PROCESS_APZ) + fi fi dnl ======================================================== dnl = Disable WebSMS backend dnl ======================================================== MOZ_ARG_DISABLE_BOOL(websms-backend, [ --disable-websms-backend Disable WebSMS backend],
--- a/devtools/client/framework/gDevTools.jsm +++ b/devtools/client/framework/gDevTools.jsm @@ -824,17 +824,17 @@ var gDevToolsBrowser = { gDevToolsBrowser._trackedBrowserWindows.add(win); gDevToolsBrowser._addAllToolsToMenu(win.document); if (this._isFirebugInstalled()) { let broadcaster = win.document.getElementById("devtoolsMenuBroadcaster_DevToolbox"); broadcaster.removeAttribute("key"); } - let tabContainer = win.document.getElementById("tabbrowser-tabs"); + let tabContainer = win.gBrowser.tabContainer; tabContainer.addEventListener("TabSelect", this, false); tabContainer.addEventListener("TabOpen", this, false); tabContainer.addEventListener("TabClose", this, false); tabContainer.addEventListener("TabPinned", this, false); tabContainer.addEventListener("TabUnpinned", this, false); }, /** @@ -1019,30 +1019,27 @@ var gDevToolsBrowser = { ref = doc.getElementById("appmenu_devtools_separator"); } if (ref) { amp.insertBefore(elements.appmenuitem, ref); } } - let mp = doc.getElementById("menuWebDeveloperPopup"); - if (mp) { - let ref; + let ref; - if (prevDef != null) { - let menuitem = doc.getElementById("menuitem_" + prevDef.id); - ref = menuitem && menuitem.nextSibling ? menuitem.nextSibling : null; - } else { - ref = doc.getElementById("menu_devtools_separator"); - } + if (prevDef) { + let menuitem = doc.getElementById("menuitem_" + prevDef.id); + ref = menuitem && menuitem.nextSibling ? menuitem.nextSibling : null; + } else { + ref = doc.getElementById("menu_devtools_separator"); + } - if (ref) { - mp.insertBefore(elements.menuitem, ref); - } + if (ref) { + ref.parentNode.insertBefore(elements.menuitem, ref); } } if (toolDefinition.id === "jsdebugger") { gDevToolsBrowser.setSlowScriptDebugHandler(); } }, @@ -1082,25 +1079,25 @@ var gDevToolsBrowser = { let mcs = doc.getElementById("mainCommandSet"); mcs.appendChild(fragCommands); this.attachKeybindingsToBrowser(doc, fragKeys); let mbs = doc.getElementById("mainBroadcasterSet"); mbs.appendChild(fragBroadcasters); - let amp = doc.getElementById("appmenu_webDeveloper_popup"); - if (amp) { - let amps = doc.getElementById("appmenu_devtools_separator"); - amp.insertBefore(fragAppMenuItems, amps); + let amps = doc.getElementById("appmenu_devtools_separator"); + if (amps) { + amps.parentNode.insertBefore(fragAppMenuItems, amps); } - let mp = doc.getElementById("menuWebDeveloperPopup"); let mps = doc.getElementById("menu_devtools_separator"); - mp.insertBefore(fragMenuItems, mps); + if (mps) { + mps.parentNode.insertBefore(fragMenuItems, mps); + } }, /** * Add a menu entry for a tool definition * * @param {string} toolDefinition * Tool definition of the tool to add a menu entry. * @param {XULDocument} doc @@ -1251,17 +1248,17 @@ var gDevToolsBrowser = { // Destroy toolboxes for closed window for (let [target, toolbox] of gDevTools._toolboxes) { if (toolbox.frame && toolbox.frame.ownerDocument.defaultView == win) { toolbox.destroy(); } } - let tabContainer = win.document.getElementById("tabbrowser-tabs"); + let tabContainer = win.gBrowser.tabContainer; tabContainer.removeEventListener("TabSelect", this, false); tabContainer.removeEventListener("TabOpen", this, false); tabContainer.removeEventListener("TabClose", this, false); tabContainer.removeEventListener("TabPinned", this, false); tabContainer.removeEventListener("TabUnpinned", this, false); }, handleEvent: function(event) { @@ -1270,17 +1267,17 @@ var gDevToolsBrowser = { case "TabClose": case "TabPinned": case "TabUnpinned": let open = 0; let pinned = 0; for (let win of this._trackedBrowserWindows) { let tabContainer = win.gBrowser.tabContainer; - let numPinnedTabs = tabContainer.tabbrowser._numPinnedTabs; + let numPinnedTabs = win.gBrowser._numPinnedTabs || 0; let numTabs = tabContainer.itemCount - numPinnedTabs; open += numTabs; pinned += numPinnedTabs; } this._tabStats.histOpen.push(open); this._tabStats.histPinned.push(pinned);
--- a/devtools/server/actors/webbrowser.js +++ b/devtools/server/actors/webbrowser.js @@ -1084,16 +1084,20 @@ TabActor.prototype = { }, onListFrames: function BTA_onListFrames(aRequest) { let windows = this._docShellsToWindows(this.docShells); return { frames: windows }; }, onListWorkers: function BTA_onListWorkers(aRequest) { + if (!this.attached) { + return { error: "wrongState" }; + } + if (this._workerActorList === null) { this._workerActorList = new WorkerActorList({ type: Ci.nsIWorkerDebugger.TYPE_DEDICATED, window: this.window }); } return this._workerActorList.getList().then((actors) => {
--- a/devtools/server/actors/worker.js +++ b/devtools/server/actors/worker.js @@ -141,74 +141,82 @@ function WorkerActorList(options) { this._onListChanged = null; this._mustNotify = false; this.onRegister = this.onRegister.bind(this); this.onUnregister = this.onUnregister.bind(this); } WorkerActorList.prototype = { getList: function () { + // Create a set of debuggers. let dbgs = new Set(); let e = wdm.getWorkerDebuggerEnumerator(); while (e.hasMoreElements()) { let dbg = e.getNext().QueryInterface(Ci.nsIWorkerDebugger); if (matchWorkerDebugger(dbg, this._options)) { dbgs.add(dbg); } } + // Delete each actor for which we don't have a debugger. for (let [dbg, ] of this._actors) { if (!dbgs.has(dbg)) { this._actors.delete(dbg); } } + // Create an actor for each debugger for which we don't have one. for (let dbg of dbgs) { if (!this._actors.has(dbg)) { this._actors.set(dbg, new WorkerActor(dbg)); } } let actors = []; for (let [, actor] of this._actors) { actors.push(actor); } - this._mustNotify = true; - this._checkListening(); + if (!this._mustNotify) { + if (this._onListChanged !== null) { + wdm.addListener(this); + } + this._mustNotify = true; + } return Promise.resolve(actors); }, get onListChanged() { return this._onListChanged; }, set onListChanged(onListChanged) { if (typeof onListChanged !== "function" && onListChanged !== null) { throw new Error("onListChanged must be either a function or null."); } + if (this._mustNotify) { + if (this._onListChanged === null && onListChanged !== null) { + wdm.addListener(this); + } + if (this._onListChanged !== null && onListChanged === null) { + wdm.removeListener(this); + } + } this._onListChanged = onListChanged; - this._checkListening(); - }, - - _checkListening: function () { - if (this._onListChanged !== null && this._mustNotify) { - wdm.addListener(this); - } else { - wdm.removeListener(this); - } }, _notifyListChanged: function () { - if (this._mustNotify) { - this._onListChanged(); - this._mustNotify = false; - } + this._onListChanged(); + + if (this._onListChanged !== null) { + wdm.removeListener(this); + } + this._mustNotify = false; }, onRegister: function (dbg) { if (matchWorkerDebugger(dbg, this._options)) { this._notifyListChanged(); } },
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7653,19 +7653,21 @@ nsContentUtils::GetButtonsFlagForButton( } } LayoutDeviceIntPoint nsContentUtils::ToWidgetPoint(const CSSPoint& aPoint, const nsPoint& aOffset, nsPresContext* aPresContext) { - return LayoutDeviceIntPoint::FromAppUnitsRounded( - CSSPoint::ToAppUnits(aPoint) + aOffset, - aPresContext->AppUnitsPerDevPixel()); + nsPoint point = CSSPoint::ToAppUnits(aPoint) + aOffset; +#if defined(MOZ_SINGLE_PROCESS_APZ) + point = point.ApplyResolution(aPresContext->PresShell()->GetCumulativeScaleResolution()); +#endif + return LayoutDeviceIntPoint::FromAppUnitsRounded(point, aPresContext->AppUnitsPerDevPixel()); } nsView* nsContentUtils::GetViewToDispatchEvent(nsPresContext* presContext, nsIPresShell** presShell) { if (presContext && presShell) { *presShell = presContext->PresShell();
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -220,16 +220,17 @@ #include "mozilla/dom/PopupBlockedEvent.h" #include "mozilla/dom/PrimitiveConversions.h" #include "mozilla/dom/WindowBinding.h" #include "nsITabChild.h" #include "mozilla/dom/MediaQueryList.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/NavigatorBinding.h" #include "mozilla/dom/ImageBitmap.h" +#include "mozilla/dom/ServiceWorkerRegistration.h" #ifdef HAVE_SIDEBAR #include "mozilla/dom/ExternalBinding.h" #endif #ifdef MOZ_WEBSPEECH #include "mozilla/dom/SpeechSynthesis.h" #endif @@ -1571,16 +1572,18 @@ nsGlobalWindow::CleanUp() mAudioContexts.Clear(); if (mIdleTimer) { mIdleTimer->Cancel(); mIdleTimer = nullptr; } DisableTimeChangeNotifications(); + + mServiceWorkerRegistrationTable.Clear(); } void nsGlobalWindow::ClearControllers() { if (mControllers) { uint32_t count; mControllers->GetControllerCount(&count); @@ -1783,16 +1786,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerRegistrationTable) + #ifdef MOZ_WEBSPEECH NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis) #endif NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) @@ -1854,16 +1859,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers) NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDialogArguments) NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue) NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mServiceWorkerRegistrationTable) + #ifdef MOZ_WEBSPEECH NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis) #endif if (tmp->mOuterWindow) { static_cast<nsGlobalWindow*>(tmp->mOuterWindow.get())->MaybeClearInnerWindow(tmp); NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow) } @@ -10227,16 +10234,34 @@ nsGlobalWindow::GetCaches(ErrorResult& a storageBlocked, forceTrustedOrigin, aRv); } RefPtr<CacheStorage> ref = mCacheStorage; return ref.forget(); } +already_AddRefed<ServiceWorkerRegistrationMainThread> +nsPIDOMWindow::GetServiceWorkerRegistration(const nsAString& aScope) +{ + RefPtr<ServiceWorkerRegistrationMainThread> registration; + if (!mServiceWorkerRegistrationTable.Get(aScope, + getter_AddRefs(registration))) { + registration = new ServiceWorkerRegistrationMainThread(this, aScope); + mServiceWorkerRegistrationTable.Put(aScope, registration); + } + return registration.forget(); +} + +void +nsPIDOMWindow::InvalidateServiceWorkerRegistration(const nsAString& aScope) +{ + mServiceWorkerRegistrationTable.Remove(aScope); +} + void nsGlobalWindow::FireOfflineStatusEventIfChanged() { if (!IsCurrentInnerWindow()) return; bool isOffline = NS_IsOffline() || NS_IsAppOffline(GetPrincipal());
--- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -10,16 +10,17 @@ #include "nsIDOMWindow.h" #include "nsCOMPtr.h" #include "nsAutoPtr.h" #include "nsTArray.h" #include "mozilla/dom/EventTarget.h" #include "js/TypeDecls.h" +#include "nsRefPtrHashtable.h" #define DOM_WINDOW_DESTROYED_TOPIC "dom-window-destroyed" #define DOM_WINDOW_FROZEN_TOPIC "dom-window-frozen" #define DOM_WINDOW_THAWED_TOPIC "dom-window-thawed" class nsIArray; class nsIContent; class nsICSSDeclaration; @@ -32,16 +33,17 @@ class nsPerformance; class nsPIWindowRoot; class nsXBLPrototypeHandler; struct nsTimeout; namespace mozilla { namespace dom { class AudioContext; class Element; +class ServiceWorkerRegistrationMainThread; } // namespace dom namespace gfx { class VRHMDInfo; } // namespace gfx } // namespace mozilla // Popup control state enum. The values in this enum must go from most // permissive to least permissive so that it's safe to push state in @@ -213,16 +215,20 @@ public: } bool GetServiceWorkersTestingEnabled() { MOZ_ASSERT(IsOuterWindow()); return mServiceWorkersTestingEnabled; } + already_AddRefed<mozilla::dom::ServiceWorkerRegistrationMainThread> + GetServiceWorkerRegistration(const nsAString& aScope); + void InvalidateServiceWorkerRegistration(const nsAString& aScope); + protected: // Lazily instantiate an about:blank document if necessary, and if // we have what it takes to do so. void MaybeCreateDoc(); float GetAudioGlobalVolumeInternal(float aVolume); void RefreshMediaElements(); @@ -850,16 +856,21 @@ protected: // This reference is used by the subclass nsGlobalWindow, and cleared in it's // DetachFromDocShell() method. This method is called by nsDocShell::Destroy(), // which is called before the nsDocShell is destroyed. nsIDocShell* MOZ_NON_OWNING_REF mDocShell; // Weak Reference // mPerformance is only used on inner windows. RefPtr<nsPerformance> mPerformance; + typedef nsRefPtrHashtable<nsStringHashKey, + mozilla::dom::ServiceWorkerRegistrationMainThread> + ServiceWorkerRegistrationTable; + ServiceWorkerRegistrationTable mServiceWorkerRegistrationTable; + uint32_t mModalStateDepth; // These variables are only used on inner windows. nsTimeout *mRunningTimeout; uint32_t mMutationBits; bool mIsDocumentLoaded;
--- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -964,16 +964,21 @@ nsScriptLoader::ProcessRequest(nsScriptL mCurrentParserInsertedScript = aRequest->mElement; } FireScriptAvailable(NS_OK, aRequest); // The window may have gone away by this point, in which case there's no point // in trying to run the script. nsCOMPtr<nsIDocument> master = mDocument->MasterDocument(); + { + // Try to perform a microtask checkpoint + nsAutoMicroTask mt; + } + nsPIDOMWindow *pwin = master->GetInnerWindow(); bool runScript = !!pwin; if (runScript) { nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(), scriptElem, NS_LITERAL_STRING("beforescriptexecute"), true, true, &runScript); }
--- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -672,16 +672,17 @@ skip-if = os == "mac" # fails intermitte [test_bug737612.html] [test_bug738108.html] [test_bug744830.html] [test_bug749367.html] [test_bug753278.html] [test_bug761120.html] [test_bug782342.html] [test_bug787778.html] +[test_bug789315.html] [test_bug789856.html] [test_bug804395.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #bug 901343, specialpowers.wrap issue createsystemxhr [test_bug809003.html] [test_bug810494.html] [test_bug811701.html] [test_bug811701.xhtml] [test_bug813919.html]
new file mode 100644 --- /dev/null +++ b/dom/base/test/test_bug789315.html @@ -0,0 +1,49 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=789315 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 789315</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="text/javascript"> + (function() { + const observerConfig = { + childList: true, + }; + + var observer = new MutationObserver(onMutations); + observer.observe(document.head, observerConfig); + + function onMutations(mutations) { + for (var i in mutations) { + var mutation = mutations[i]; + for (var j in mutation.addedNodes) { + var addedNode = mutation.addedNodes[j]; + addedNode.mutationObserverHasNotified = true; + } + } + } + + })(); + </script> + + <link id="testnode" rel="localization" href="dummy"></link> + + <script type="text/javascript"> + var testNode = document.getElementById("testnode"); + ok(testNode.mutationObserverHasNotified); + </script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=789315">Mozilla Bug 789315</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html>
--- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -922,29 +922,32 @@ Event::GetScreenCoords(nsPresContext* aP aEvent->mClass != eTouchEventClass && aEvent->mClass != eDragEventClass && aEvent->mClass != eSimpleGestureEventClass)) { return CSSIntPoint(0, 0); } // Doing a straight conversion from LayoutDeviceIntPoint to CSSIntPoint // seem incorrect, but it is needed to maintain legacy functionality. - if (!aPresContext) { + WidgetGUIEvent* guiEvent = aEvent->AsGUIEvent(); + if (!aPresContext || !(guiEvent && guiEvent->widget)) { return CSSIntPoint(aPoint.x, aPoint.y); } - LayoutDeviceIntPoint offset = aPoint; + nsPoint pt = + LayoutDevicePixel::ToAppUnits(aPoint, aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom()); - WidgetGUIEvent* guiEvent = aEvent->AsGUIEvent(); - if (guiEvent && guiEvent->widget) { - offset += guiEvent->widget->WidgetToScreenOffset(); +#if defined(MOZ_SINGLE_PROCESS_APZ) + if (aPresContext->PresShell()) { + pt = pt.RemoveResolution(aPresContext->PresShell()->GetCumulativeScaleResolution()); } +#endif - nsPoint pt = - LayoutDevicePixel::ToAppUnits(offset, aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom()); + pt += LayoutDevicePixel::ToAppUnits(guiEvent->widget->WidgetToScreenOffset(), + aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom()); return CSSPixel::FromAppUnitsRounded(pt); } // static CSSIntPoint Event::GetPageCoords(nsPresContext* aPresContext, WidgetEvent* aEvent,
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl +++ b/dom/interfaces/base/nsIServiceWorkerManager.idl @@ -16,21 +16,29 @@ interface nsIURI; interface nsIServiceWorkerUnregisterCallback : nsISupports { // aState is true if the unregistration succeded. // It's false if this ServiceWorkerRegistration doesn't exist. void unregisterSucceeded(in bool aState); void unregisterFailed(); }; -[scriptable, builtinclass, uuid(1a1e71dd-0f78-4e2e-a2db-a946fe02cddf)] +interface nsIWorkerDebugger; + +[scriptable, builtinclass, uuid(76e357ed-208d-4e4c-9165-1c4059707879)] interface nsIServiceWorkerInfo : nsISupports { readonly attribute DOMString scriptSpec; readonly attribute DOMString cacheName; + + readonly attribute nsIWorkerDebugger debugger; + + void attachDebugger(); + + void detachDebugger(); }; [scriptable, uuid(87e63548-d440-4b8a-b158-65ad1de0211E)] interface nsIServiceWorkerRegistrationInfoListener : nsISupports { void onChange(); };
--- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -2802,32 +2802,30 @@ ContentChild::RecvOnAppThemeChanged() nsCOMPtr<nsIObserverService> os = services::GetObserverService(); if (os) { os->NotifyObservers(nullptr, "app-theme-changed", nullptr); } return true; } bool -ContentChild::RecvStartProfiler(const uint32_t& aEntries, - const double& aInterval, - nsTArray<nsCString>&& aFeatures, - nsTArray<nsCString>&& aThreadNameFilters) +ContentChild::RecvStartProfiler(const ProfilerInitParams& params) { nsTArray<const char*> featureArray; - for (size_t i = 0; i < aFeatures.Length(); ++i) { - featureArray.AppendElement(aFeatures[i].get()); + for (size_t i = 0; i < params.features().Length(); ++i) { + featureArray.AppendElement(params.features()[i].get()); } nsTArray<const char*> threadNameFilterArray; - for (size_t i = 0; i < aThreadNameFilters.Length(); ++i) { - threadNameFilterArray.AppendElement(aThreadNameFilters[i].get()); + for (size_t i = 0; i < params.threadFilters().Length(); ++i) { + threadNameFilterArray.AppendElement(params.threadFilters()[i].get()); } - profiler_start(aEntries, aInterval, featureArray.Elements(), featureArray.Length(), + profiler_start(params.entries(), params.interval(), + featureArray.Elements(), featureArray.Length(), threadNameFilterArray.Elements(), threadNameFilterArray.Length()); return true; } bool ContentChild::RecvStopProfiler() {
--- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -419,20 +419,17 @@ public: virtual bool RecvOnAppThemeChanged() override; virtual bool RecvAssociatePluginId(const uint32_t& aPluginId, const base::ProcessId& aProcessId) override; virtual bool RecvLoadPluginResult(const uint32_t& aPluginId, const bool& aResult) override; virtual bool RecvUpdateWindow(const uintptr_t& aChildId) override; - virtual bool RecvStartProfiler(const uint32_t& aEntries, - const double& aInterval, - nsTArray<nsCString>&& aFeatures, - nsTArray<nsCString>&& aThreadNameFilters) override; + virtual bool RecvStartProfiler(const ProfilerInitParams& params) override; virtual bool RecvPauseProfiler(const bool& aPause) override; virtual bool RecvStopProfiler() override; virtual bool RecvGatherProfile() override; virtual bool RecvDomainSetChanged(const uint32_t& aSetType, const uint32_t& aChangeType, const OptionalURIParams& aDomain) override; virtual bool RecvShutdown() override; virtual bool
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1552,16 +1552,31 @@ ContentParent::Init() #ifdef ACCESSIBILITY // If accessibility is running in chrome process then start it in content // process. if (nsIPresShell::IsAccessibilityActive()) { Unused << SendActivateA11y(); } #endif + +#ifdef MOZ_ENABLE_PROFILER_SPS + nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1")); + bool profilerActive = false; + DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + if (profilerActive) { + nsCOMPtr<nsIProfilerStartParams> currentProfilerParams; + rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + StartProfiler(currentProfilerParams); + } +#endif } void ContentParent::ForwardKnownInfo() { MOZ_ASSERT(mMetamorphosed); if (!mMetamorphosed) { return; @@ -3270,23 +3285,17 @@ ContentParent::Observe(nsISupports* aSub } #endif else if (!strcmp(aTopic, "app-theme-changed")) { Unused << SendOnAppThemeChanged(); } #ifdef MOZ_ENABLE_PROFILER_SPS else if (!strcmp(aTopic, "profiler-started")) { nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject)); - uint32_t entries; - double interval; - params->GetEntries(&entries); - params->GetInterval(&interval); - const nsTArray<nsCString>& features = params->GetFeatures(); - const nsTArray<nsCString>& threadFilterNames = params->GetThreadFilterNames(); - Unused << SendStartProfiler(entries, interval, features, threadFilterNames); + StartProfiler(params); } else if (!strcmp(aTopic, "profiler-stopped")) { Unused << SendStopProfiler(); } else if (!strcmp(aTopic, "profiler-paused")) { Unused << SendPauseProfiler(true); } else if (!strcmp(aTopic, "profiler-resumed")) { @@ -5704,16 +5713,34 @@ ContentParent::RecvGetAndroidSystemInfo( nsSystemInfo::GetAndroidSystemInfo(aInfo); return true; #else MOZ_CRASH("wrong platform!"); return false; #endif } +void +ContentParent::StartProfiler(nsIProfilerStartParams* aParams) +{ + if (NS_WARN_IF(!aParams)) { + return; + } + + ProfilerInitParams ipcParams; + + ipcParams.enabled() = true; + aParams->GetEntries(&ipcParams.entries()); + aParams->GetInterval(&ipcParams.interval()); + ipcParams.features() = aParams->GetFeatures(); + ipcParams.threadFilters() = aParams->GetThreadFilterNames(); + + Unused << SendStartProfiler(ipcParams); +} + } // namespace dom } // namespace mozilla NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver) NS_IMETHODIMP ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData) { mozilla::Unused << mParent->SendNotifyIdleObserver(mObserver,
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -930,16 +930,17 @@ private: const uint32_t& aDropEffect) override; virtual bool RecvGetBrowserConfiguration(const nsCString& aURI, BrowserConfiguration* aConfig) override; virtual bool RecvGamepadListenerAdded() override; virtual bool RecvGamepadListenerRemoved() override; virtual bool RecvProfile(const nsCString& aProfile) override; virtual bool RecvGetGraphicsDeviceInitData(DeviceInitData* aOut) override; + void StartProfiler(nsIProfilerStartParams* aParams); virtual bool RecvGetDeviceStorageLocation(const nsString& aType, nsString* aPath) override; virtual bool RecvGetAndroidSystemInfo(AndroidSystemInfo* aInfo) override; // If you add strong pointers to cycle collected objects here, be sure to // release these objects in ShutDownProcess. See the comment there for more
--- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -60,16 +60,17 @@ include InputStreamParams; include PTabContext; include URIParams; include PluginTypes; include ProtocolTypes; include PBackgroundSharedTypes; include PContentPermission; include BrowserConfiguration; include GraphicsMessages; +include ProfilerTypes; // Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp // are put into different UnifiedProtocolsXX.cpp files. // XXX Remove this once bug 1069073 is fixed include "mozilla/dom/PContentBridgeParent.h"; using GeoPosition from "nsGeoPositionIPCSerialiser.h"; @@ -646,18 +647,17 @@ child: * PluginModuleContentParent that the PluginModuleChromeParent's async * init has completed. */ async LoadPluginResult(uint32_t aPluginId, bool aResult); /** * Control the Gecko Profiler in the child process. */ - async StartProfiler(uint32_t aEntries, double aInterval, nsCString[] aFeatures, - nsCString[] aThreadNameFilters); + async StartProfiler(ProfilerInitParams params); async StopProfiler(); async PauseProfiler(bool aPause); async GatherProfile(); InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action); EndDragSession(bool aDoneDrag, bool aUserCancelled);
--- a/dom/media/eme/CDMProxy.cpp +++ b/dom/media/eme/CDMProxy.cpp @@ -13,16 +13,17 @@ #include "nsServiceManagerUtils.h" #include "MainThreadUtils.h" #include "mozilla/EMEUtils.h" #include "nsIConsoleService.h" #include "prenv.h" #include "mozilla/PodOperations.h" #include "mozilla/CDMCallbackProxy.h" #include "MediaData.h" +#include "nsPrintfCString.h" namespace mozilla { CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem) : mKeys(aKeys) , mKeySystem(aKeySystem) , mCDM(nullptr) , mDecryptionJobCount(0) @@ -36,16 +37,17 @@ CDMProxy::~CDMProxy() { MOZ_COUNT_DTOR(CDMProxy); } void CDMProxy::Init(PromiseId aPromiseId, const nsAString& aOrigin, const nsAString& aTopLevelOrigin, + const nsAString& aGMPName, bool aInPrivateBrowsing) { MOZ_ASSERT(NS_IsMainThread()); NS_ENSURE_TRUE_VOID(!mKeys.IsNull()); EME_LOG("CDMProxy::Init (%s, %s) %s", NS_ConvertUTF16toUTF8(aOrigin).get(), NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(), @@ -63,20 +65,27 @@ CDMProxy::Init(PromiseId aPromiseId, mps->GetThread(getter_AddRefs(mGMPThread)); if (!mGMPThread) { RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR, NS_LITERAL_CSTRING("Couldn't get GMP thread CDMProxy::Init")); return; } } + if (aGMPName.IsEmpty()) { + RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR, + nsPrintfCString("Unknown GMP for keysystem '%s'", NS_ConvertUTF16toUTF8(mKeySystem).get())); + return; + } + nsAutoPtr<InitData> data(new InitData()); data->mPromiseId = aPromiseId; data->mOrigin = aOrigin; data->mTopLevelOrigin = aTopLevelOrigin; + data->mGMPName = aGMPName; data->mInPrivateBrowsing = aInPrivateBrowsing; nsCOMPtr<nsIRunnable> task( NS_NewRunnableMethodWithArg<nsAutoPtr<InitData>>(this, &CDMProxy::gmp_Init, Move(data))); mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL); } @@ -171,16 +180,17 @@ CDMProxy::gmp_Init(nsAutoPtr<InitData>&& // Make a copy before we transfer ownership of aData to the // gmp_InitGetGMPDecryptorCallback. InitData data(*aData); UniquePtr<GetNodeIdCallback> callback( new gmp_InitGetGMPDecryptorCallback(this, Move(aData))); nsresult rv = mps->GetNodeId(data.mOrigin, data.mTopLevelOrigin, + data.mGMPName, data.mInPrivateBrowsing, Move(callback)); if (NS_FAILED(rv)) { RejectPromise(data.mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR, NS_LITERAL_CSTRING("Call to GetNodeId() failed early")); } }
--- a/dom/media/eme/CDMProxy.h +++ b/dom/media/eme/CDMProxy.h @@ -52,16 +52,17 @@ public: CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem); // Main thread only. // Loads the CDM corresponding to mKeySystem. // Calls MediaKeys::OnCDMCreated() when the CDM is created. void Init(PromiseId aPromiseId, const nsAString& aOrigin, const nsAString& aTopLevelOrigin, + const nsAString& aGMPName, bool aInPrivateBrowsing); // Main thread only. // Uses the CDM to create a key session. // Calls MediaKeys::OnSessionActivated() when session is created. // Assumes ownership of (Move()s) aInitData's contents. void CreateSession(uint32_t aCreateSessionToken, dom::SessionType aSessionType, @@ -181,16 +182,17 @@ public: private: friend class gmp_InitDoneCallback; friend class gmp_InitGetGMPDecryptorCallback; struct InitData { uint32_t mPromiseId; nsAutoString mOrigin; nsAutoString mTopLevelOrigin; + nsString mGMPName; bool mInPrivateBrowsing; }; // GMP thread only. void gmp_Init(nsAutoPtr<InitData>&& aData); void gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData); void gmp_InitGetGMPDecryptor(nsresult aResult, const nsACString& aNodeId,
--- a/dom/media/eme/EMEUtils.cpp +++ b/dom/media/eme/EMEUtils.cpp @@ -136,9 +136,22 @@ CopyArrayBufferViewOrArrayBufferData(con ArrayData data = GetArrayBufferViewOrArrayBufferData(aBufferOrView); aOutData.Clear(); if (!data.IsValid()) { return; } aOutData.AppendElements(data.mData, data.mLength); } +nsString +KeySystemToGMPName(const nsAString& aKeySystem) +{ + if (aKeySystem.EqualsLiteral("com.adobe.primetime")) { + return NS_LITERAL_STRING("gmp-eme-adobe"); + } + if (aKeySystem.EqualsLiteral("org.w3.clearkey")) { + return NS_LITERAL_STRING("gmp-clearkey"); + } + MOZ_ASSERT(false, "We should only call this for known GMPs"); + return EmptyString(); +} + } // namespace mozilla
--- a/dom/media/eme/EMEUtils.h +++ b/dom/media/eme/EMEUtils.h @@ -99,11 +99,14 @@ struct ArrayData { // while the ArrayData is live, as then all bets about the data not changing // are off! No calls into JS, no calls into JS-implemented WebIDL or XPIDL, // nothing. Beware! // // Only call this on a properly initialized ArrayBufferViewOrArrayBuffer. ArrayData GetArrayBufferViewOrArrayBufferData(const dom::ArrayBufferViewOrArrayBuffer& aBufferOrView); +nsString +KeySystemToGMPName(const nsAString& aKeySystem); + } // namespace mozilla #endif // EME_LOG_H_
--- a/dom/media/eme/MediaKeySystemAccess.cpp +++ b/dom/media/eme/MediaKeySystemAccess.cpp @@ -438,16 +438,19 @@ GetSupportedConfig(mozIGeckoMediaPluginS { MediaKeySystemConfiguration config; config.mLabel = aCandidate.mLabel; if (aCandidate.mInitDataTypes.WasPassed()) { nsTArray<nsString> initDataTypes; for (const nsString& candidate : aCandidate.mInitDataTypes.Value()) { if (candidate.EqualsLiteral("cenc")) { initDataTypes.AppendElement(candidate); + } else if (candidate.EqualsLiteral("keyids") && + aKeySystem.EqualsLiteral("org.w3.clearkey")) { + initDataTypes.AppendElement(candidate); } } if (initDataTypes.IsEmpty()) { return false; } config.mInitDataTypes.Construct(); config.mInitDataTypes.Value().Assign(initDataTypes); }
--- a/dom/media/eme/MediaKeys.cpp +++ b/dom/media/eme/MediaKeys.cpp @@ -363,16 +363,17 @@ MediaKeys::Init(ErrorResult& aRv) // here, and hold a self-reference until that promise is resolved or // rejected. MOZ_ASSERT(!mCreatePromiseId, "Should only be created once!"); mCreatePromiseId = StorePromise(promise); AddRef(); mProxy->Init(mCreatePromiseId, origin, topLevelOrigin, + KeySystemToGMPName(mKeySystem), inPrivateBrowsing); return promise.forget(); } void MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId, const uint32_t aPluginId) {
--- a/dom/media/gmp/GMPParent.cpp +++ b/dom/media/gmp/GMPParent.cpp @@ -1063,13 +1063,19 @@ GMPParent::Bridge(GMPServiceParent* aGMP { if (NS_FAILED(PGMPContent::Bridge(aGMPServiceParent, this))) { return false; } ++mGMPContentChildCount; return true; } +nsString +GMPParent::GetPluginBaseName() const +{ + return NS_LITERAL_STRING("gmp-") + mName; +} + } // namespace gmp } // namespace mozilla #undef LOG #undef LOGD
--- a/dom/media/gmp/GMPParent.h +++ b/dom/media/gmp/GMPParent.h @@ -119,16 +119,17 @@ public: // Specifies that a GMP can only work with the specified NodeIds. void SetNodeId(const nsACString& aNodeId); const nsACString& GetNodeId() const { return mNodeId; } const nsCString& GetDisplayName() const; const nsCString& GetVersion() const; const uint32_t GetPluginId() const; + nsString GetPluginBaseName() const; // Returns true if a plugin can be or is being used across multiple NodeIds. bool CanBeSharedCrossNodeIds() const; // A GMP can be used from a NodeId if it's already been set to work with // that NodeId, or if it's not been set to work with any NodeId and has // not yet been loaded (i.e. it's not shared across NodeIds). bool CanBeUsedFrom(const nsACString& aNodeId) const;
--- a/dom/media/gmp/GMPServiceChild.cpp +++ b/dom/media/gmp/GMPServiceChild.cpp @@ -132,56 +132,61 @@ GeckoMediaPluginServiceChild::GetPluginV aOutVersion = version; return ok ? NS_OK : NS_ERROR_FAILURE; } class GetNodeIdDone : public GetServiceChildCallback { public: GetNodeIdDone(const nsAString& aOrigin, const nsAString& aTopLevelOrigin, + const nsAString& aGMPName, bool aInPrivateBrowsing, UniquePtr<GetNodeIdCallback>&& aCallback) : mOrigin(aOrigin), mTopLevelOrigin(aTopLevelOrigin), + mGMPName(aGMPName), mInPrivateBrowsing(aInPrivateBrowsing), mCallback(Move(aCallback)) { } virtual void Done(GMPServiceChild* aGMPServiceChild) { if (!aGMPServiceChild) { mCallback->Done(NS_ERROR_FAILURE, EmptyCString()); return; } nsCString outId; if (!aGMPServiceChild->SendGetGMPNodeId(mOrigin, mTopLevelOrigin, + mGMPName, mInPrivateBrowsing, &outId)) { mCallback->Done(NS_ERROR_FAILURE, EmptyCString()); return; } mCallback->Done(NS_OK, outId); } private: nsString mOrigin; nsString mTopLevelOrigin; + nsString mGMPName; bool mInPrivateBrowsing; UniquePtr<GetNodeIdCallback> mCallback; }; NS_IMETHODIMP GeckoMediaPluginServiceChild::GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin, + const nsAString& aGMPName, bool aInPrivateBrowsing, UniquePtr<GetNodeIdCallback>&& aCallback) { UniquePtr<GetServiceChildCallback> callback( - new GetNodeIdDone(aOrigin, aTopLevelOrigin, aInPrivateBrowsing, Move(aCallback))); + new GetNodeIdDone(aOrigin, aTopLevelOrigin, aGMPName, aInPrivateBrowsing, Move(aCallback))); GetServiceChild(Move(callback)); return NS_OK; } NS_IMETHODIMP GeckoMediaPluginServiceChild::UpdateTrialCreateState(const nsAString& aKeySystem, uint32_t aState) {
--- a/dom/media/gmp/GMPServiceChild.h +++ b/dom/media/gmp/GMPServiceChild.h @@ -42,16 +42,17 @@ public: static already_AddRefed<GeckoMediaPluginServiceChild> GetSingleton(); NS_IMETHOD GetPluginVersionForAPI(const nsACString& aAPI, nsTArray<nsCString>* aTags, bool* aHasPlugin, nsACString& aOutVersion) override; NS_IMETHOD GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin, + const nsAString& aGMPName, bool aInPrivateBrowsingMode, UniquePtr<GetNodeIdCallback>&& aCallback) override; NS_IMETHOD UpdateTrialCreateState(const nsAString& aKeySystem, uint32_t aState) override; NS_DECL_NSIOBSERVER void SetServiceChild(UniquePtr<GMPServiceChild>&& aServiceChild);
--- a/dom/media/gmp/GMPServiceParent.cpp +++ b/dom/media/gmp/GMPServiceParent.cpp @@ -158,58 +158,79 @@ CloneAndAppend(nsIFile* aFile, const nsA rv = f->Append(aDir); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } return f.forget(); } static void -MoveAndOverwrite(nsIFile* aOldStorageDir, - nsIFile* aNewStorageDir, +MoveAndOverwrite(nsIFile* aOldParentDir, + nsIFile* aNewParentDir, const nsAString& aSubDir) { nsresult rv; - nsCOMPtr<nsIFile> srcDir(CloneAndAppend(aOldStorageDir, aSubDir)); + nsCOMPtr<nsIFile> srcDir(CloneAndAppend(aOldParentDir, aSubDir)); if (NS_WARN_IF(!srcDir)) { return; } if (!FileExists(srcDir)) { // No sub-directory to be migrated. return; } - nsCOMPtr<nsIFile> dstDir(CloneAndAppend(aNewStorageDir, aSubDir)); + // Ensure destination parent directory exists. + rv = aNewParentDir->Create(nsIFile::DIRECTORY_TYPE, 0700); + if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + nsCOMPtr<nsIFile> dstDir(CloneAndAppend(aNewParentDir, aSubDir)); if (FileExists(dstDir)) { // We must have migrated before already, and then ran an old version // of Gecko again which created storage at the old location. Overwrite // the previously migrated storage. rv = dstDir->Remove(true); if (NS_WARN_IF(NS_FAILED(rv))) { // MoveTo will fail. return; } } - rv = srcDir->MoveTo(aNewStorageDir, EmptyString()); + rv = srcDir->MoveTo(aNewParentDir, EmptyString()); if (NS_WARN_IF(NS_FAILED(rv))) { return; } } static void MigratePreGecko42StorageDir(nsIFile* aOldStorageDir, nsIFile* aNewStorageDir) { MoveAndOverwrite(aOldStorageDir, aNewStorageDir, NS_LITERAL_STRING("id")); MoveAndOverwrite(aOldStorageDir, aNewStorageDir, NS_LITERAL_STRING("storage")); } +static void +MigratePreGecko45StorageDir(nsIFile* aStorageDirBase) +{ + nsCOMPtr<nsIFile> adobeStorageDir(CloneAndAppend(aStorageDirBase, NS_LITERAL_STRING("gmp-eme-adobe"))); + if (NS_WARN_IF(!adobeStorageDir)) { + return; + } + + // The base storage dir in pre-45 contained "id" and "storage" subdirs. + // We assume all storage in the base storage dir that aren't known to GMP + // storage are records for the Adobe GMP. + MoveAndOverwrite(aStorageDirBase, adobeStorageDir, NS_LITERAL_STRING("id")); + MoveAndOverwrite(aStorageDirBase, adobeStorageDir, NS_LITERAL_STRING("storage")); +} + static nsresult GMPPlatformString(nsAString& aOutPlatform) { // Append the OS and arch so that we don't reuse the storage if the profile is // copied or used under a different bit-ness, or copied to another platform. nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1"); if (!runtime) { return NS_ERROR_FAILURE; @@ -286,21 +307,29 @@ GeckoMediaPluginServiceParent::InitStora return rv; } rv = mStorageBaseDir->Create(nsIFile::DIRECTORY_TYPE, 0700); if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) { return rv; } - // Prior to 42, GMP storage was stored in $profile/gmp/. After 42, it's - // stored in $profile/gmp/$platform/. So we must migrate any old records + // Prior to 42, GMP storage was stored in $profileDir/gmp/. After 42, it's + // stored in $profileDir/gmp/$platform/. So we must migrate any old records // from the old location to the new location, for forwards compatibility. MigratePreGecko42StorageDir(gmpDirWithoutPlatform, mStorageBaseDir); + // Prior to 45, GMP storage was not separated by plugin. In 45 and after, + // it's stored in $profile/gmp/$platform/$gmpName. So we must migrate old + // records from the old location to the new location, for forwards + // compatibility. We assume all directories in the base storage dir that + // aren't known to GMP storage are records for the Adobe GMP, since it + // was first. + MigratePreGecko45StorageDir(mStorageBaseDir); + return GeckoMediaPluginService::Init(); } NS_IMETHODIMP GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aSomeData) { @@ -727,17 +756,17 @@ GeckoMediaPluginServiceParent::PathRunna { if (mOperation == ADD) { mService->AddOnGMPThread(mPath); } else { mService->RemoveOnGMPThread(mPath, mOperation == REMOVE_AND_DELETE_FROM_DISK, mDefer); } -#ifndef MOZ_WIDGET_GONK // Bug 1214967: disabled on B2G due to inscrutable test failures. +#ifndef MOZ_WIDGET_GONK // Bug 1214967: disabled on B2G due to inscrutable test failures. // For e10s, we must fire a notification so that all ContentParents notify // their children to update the codecs that the GMPDecoderModule can use. NS_DispatchToMainThread(new NotifyObserversTask("gmp-changed"), NS_DISPATCH_NORMAL); // For non-e10s, and for decoding in the chrome process, must update GMP // PDM's codecs list directly. NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void { GMPDecoderModule::UpdateUsableCodecs(); })); @@ -1207,16 +1236,17 @@ GeckoMediaPluginServiceParent::IsPersist NS_ENSURE_ARG(aOutAllowed); *aOutAllowed = mPersistentStorageAllowed.Get(aNodeId); return NS_OK; } nsresult GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin, + const nsAString& aGMPName, bool aInPrivateBrowsing, nsACString& aOutId) { MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); LOGD(("%s::%s: (%s, %s), %s", __CLASS__, __FUNCTION__, NS_ConvertUTF16toUTF8(aOrigin).get(), NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(), (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"))); @@ -1239,58 +1269,70 @@ GeckoMediaPluginServiceParent::GetNodeId mPersistentStorageAllowed.Put(salt, false); return NS_OK; } const uint32_t hash = AddToHash(HashString(aOrigin), HashString(aTopLevelOrigin)); if (aInPrivateBrowsing) { - // For PB mode, we store the node id, indexed by the origin pair, - // so that if the same origin pair is opened in this session, it gets - // the same node id. + // For PB mode, we store the node id, indexed by the origin pair and GMP name, + // so that if the same origin pair is opened for the same GMP in this session, + // it gets the same node id. + const uint32_t pbHash = AddToHash(HashString(aGMPName), hash); nsCString* salt = nullptr; - if (!(salt = mTempNodeIds.Get(hash))) { + if (!(salt = mTempNodeIds.Get(pbHash))) { // No salt stored, generate and temporarily store some for this id. nsAutoCString newSalt; rv = GenerateRandomPathName(newSalt, NodeIdSaltLength); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } salt = new nsCString(newSalt); - mTempNodeIds.Put(hash, salt); + mTempNodeIds.Put(pbHash, salt); mPersistentStorageAllowed.Put(*salt, false); } aOutId = *salt; return NS_OK; } // Otherwise, try to see if we've previously generated and stored salt // for this origin pair. - nsCOMPtr<nsIFile> path; // $profileDir/gmp/ + nsCOMPtr<nsIFile> path; // $profileDir/gmp/$platform/ rv = GetStorageDir(getter_AddRefs(path)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + rv = path->Append(aGMPName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // $profileDir/gmp/$platform/$gmpName/ + rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700); + if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + rv = path->AppendNative(NS_LITERAL_CSTRING("id")); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - // $profileDir/gmp/id/ + // $profileDir/gmp/$platform/$gmpName/id/ rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700); if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsAutoCString hashStr; hashStr.AppendInt((int64_t)hash); - // $profileDir/gmp/id/$hash + // $profileDir/gmp/$platform/$gmpName/id/$hash rv = path->AppendNative(hashStr); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700); if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -1317,31 +1359,31 @@ GeckoMediaPluginServiceParent::GetNodeId // No stored salt for this origin. Generate salt, and store it and // the origin on disk. nsresult rv = GenerateRandomPathName(salt, NodeIdSaltLength); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } MOZ_ASSERT(salt.Length() == NodeIdSaltLength); - // $profileDir/gmp/id/$hash/salt + // $profileDir/gmp/$platform/$gmpName/id/$hash/salt rv = WriteToFile(path, NS_LITERAL_CSTRING("salt"), salt); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - // $profileDir/gmp/id/$hash/origin + // $profileDir/gmp/$platform/$gmpName/id/$hash/origin rv = WriteToFile(path, NS_LITERAL_CSTRING("origin"), NS_ConvertUTF16toUTF8(aOrigin)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - // $profileDir/gmp/id/$hash/topLevelOrigin + // $profileDir/gmp/$platform/$gmpName/id/$hash/topLevelOrigin rv = WriteToFile(path, NS_LITERAL_CSTRING("topLevelOrigin"), NS_ConvertUTF16toUTF8(aTopLevelOrigin)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } else { @@ -1355,21 +1397,22 @@ GeckoMediaPluginServiceParent::GetNodeId mPersistentStorageAllowed.Put(salt, true); return NS_OK; } NS_IMETHODIMP GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin, + const nsAString& aGMPName, bool aInPrivateBrowsing, UniquePtr<GetNodeIdCallback>&& aCallback) { nsCString nodeId; - nsresult rv = GetNodeId(aOrigin, aTopLevelOrigin, aInPrivateBrowsing, nodeId); + nsresult rv = GetNodeId(aOrigin, aTopLevelOrigin, aGMPName, aInPrivateBrowsing, nodeId); aCallback->Done(rv, nodeId); return rv; } NS_IMETHODIMP GeckoMediaPluginServiceParent::UpdateTrialCreateState(const nsAString& aKeySystem, uint32_t aState) { @@ -1476,99 +1519,80 @@ struct NodeFilter { } private: const nsTArray<nsCString>& mNodeIDs; }; void GeckoMediaPluginServiceParent::ClearNodeIdAndPlugin(DirectoryFilter& aFilter) { - nsresult rv; + // $profileDir/gmp/$platform/ nsCOMPtr<nsIFile> path; - - // $profileDir/gmp/ - rv = GetStorageDir(getter_AddRefs(path)); - if (NS_FAILED(rv)) { - return; - } - - // $profileDir/gmp/id/ - rv = path->AppendNative(NS_LITERAL_CSTRING("id")); - if (NS_FAILED(rv)) { - return; - } - - // Iterate all sub-folders of $profileDir/gmp/id/ - nsCOMPtr<nsISimpleEnumerator> iter; - rv = path->GetDirectoryEntries(getter_AddRefs(iter)); + nsresult rv = GetStorageDir(getter_AddRefs(path)); if (NS_FAILED(rv)) { return; } - bool hasMore = false; - nsTArray<nsCString> nodeIDsToClear; - while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) { - nsCOMPtr<nsISupports> supports; - rv = iter->GetNext(getter_AddRefs(supports)); - if (NS_FAILED(rv)) { - continue; - } + // Iterate all sub-folders of $profileDir/gmp/$platform/, i.e. the dirs in which + // specific GMPs store their data. + DirectoryEnumerator iter(path, DirectoryEnumerator::DirsOnly); + for (nsCOMPtr<nsIFile> pluginDir; (pluginDir = iter.Next()) != nullptr;) { + ClearNodeIdAndPlugin(pluginDir, aFilter); + } +} - // $profileDir/gmp/id/$hash - nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv)); - if (NS_FAILED(rv)) { - continue; - } +void +GeckoMediaPluginServiceParent::ClearNodeIdAndPlugin(nsIFile* aPluginStorageDir, + DirectoryFilter& aFilter) +{ + // $profileDir/gmp/$platform/$gmpName/id/ + nsCOMPtr<nsIFile> path = CloneAndAppend(aPluginStorageDir, NS_LITERAL_STRING("id")); + if (!path) { + return; + } - // Skip non-directory files. - bool isDirectory = false; - rv = dirEntry->IsDirectory(&isDirectory); - if (NS_FAILED(rv) || !isDirectory) { - continue; - } - + // Iterate all sub-folders of $profileDir/gmp/$platform/$gmpName/id/ + nsTArray<nsCString> nodeIDsToClear; + DirectoryEnumerator iter(path, DirectoryEnumerator::DirsOnly); + for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) { + // dirEntry is the hash of origins, i.e.: + // $profileDir/gmp/$platform/$gmpName/id/$originHash/ if (!aFilter(dirEntry)) { continue; } - nsAutoCString salt; if (NS_SUCCEEDED(ReadSalt(dirEntry, salt))) { // Keep node IDs to clear data/plugins associated with them later. nodeIDsToClear.AppendElement(salt); // Also remove node IDs from the table. mPersistentStorageAllowed.Remove(salt); } // Now we can remove the directory for the origin pair. if (NS_FAILED(dirEntry->Remove(true))) { NS_WARNING("Failed to delete the directory for the origin pair"); } } - // Kill plugins that have node IDs to be cleared. + // Kill plugin instances that have node IDs being cleared. KillPlugins(mPlugins, mMutex, NodeFilter(nodeIDsToClear)); - // Clear all matching $profileDir/gmp/storage/$nodeId/ - rv = GetStorageDir(getter_AddRefs(path)); - if (NS_FAILED(rv)) { + // Clear all storage in $profileDir/gmp/$platform/$gmpName/storage/$nodeId/ + path = CloneAndAppend(aPluginStorageDir, NS_LITERAL_STRING("storage")); + if (!path) { return; } - rv = path->AppendNative(NS_LITERAL_CSTRING("storage")); - if (NS_FAILED(rv)) { - return; - } - - for (size_t i = 0; i < nodeIDsToClear.Length(); i++) { + for (const nsCString& nodeId : nodeIDsToClear) { nsCOMPtr<nsIFile> dirEntry; - rv = path->Clone(getter_AddRefs(dirEntry)); + nsresult rv = path->Clone(getter_AddRefs(dirEntry)); if (NS_FAILED(rv)) { continue; } - rv = dirEntry->AppendNative(nodeIDsToClear[i]); + rv = dirEntry->AppendNative(nodeId); if (NS_FAILED(rv)) { continue; } if (NS_FAILED(DeleteDir(dirEntry))) { NS_WARNING("Failed to delete GMP storage directory for the node"); } } @@ -1593,91 +1617,68 @@ GeckoMediaPluginServiceParent::ForgetThi } void GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread(PRTime aSince) { MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); LOGD(("%s::%s: since=%lld", __CLASS__, __FUNCTION__, (int64_t)aSince)); - nsCOMPtr<nsIFile> storagePath; - nsCOMPtr<nsIFile> temp; - if (NS_SUCCEEDED(GetStorageDir(getter_AddRefs(temp))) && - NS_SUCCEEDED(temp->AppendNative(NS_LITERAL_CSTRING("storage")))) { - storagePath = temp.forget(); - } - struct MTimeFilter : public DirectoryFilter { - explicit MTimeFilter(PRTime aSince, already_AddRefed<nsIFile> aPath) - : mSince(aSince), mStoragePath(aPath) {} + explicit MTimeFilter(PRTime aSince) + : mSince(aSince) {} // Return true if any files under aPath is modified after |mSince|. bool IsModifiedAfter(nsIFile* aPath) { PRTime lastModified; nsresult rv = aPath->GetLastModifiedTime(&lastModified); if (NS_SUCCEEDED(rv) && lastModified >= mSince) { return true; } - // Check sub-directories recursively - nsCOMPtr<nsISimpleEnumerator> iter; - rv = aPath->GetDirectoryEntries(getter_AddRefs(iter)); - if (NS_FAILED(rv)) { - return false; - } - - bool hasMore = false; - while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) { - nsCOMPtr<nsISupports> supports; - rv = iter->GetNext(getter_AddRefs(supports)); - if (NS_FAILED(rv)) { - continue; - } - - nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv)); - if (NS_FAILED(rv)) { - continue; - } - - if (IsModifiedAfter(path)) { + DirectoryEnumerator iter(aPath, DirectoryEnumerator::FilesAndDirs); + for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) { + if (IsModifiedAfter(dirEntry)) { return true; } } return false; } - // |aPath| is $profileDir/gmp/id/$hash + // |aPath| is $profileDir/gmp/$platform/$gmpName/id/$originHash/ virtual bool operator()(nsIFile* aPath) { if (IsModifiedAfter(aPath)) { return true; } nsAutoCString salt; - nsresult rv = ReadSalt(aPath, salt); - if (NS_FAILED(rv)) { + if (NS_FAILED(ReadSalt(aPath, salt))) { return false; } - // $profileDir/gmp/storage/ - if (!mStoragePath) { + // $profileDir/gmp/$platform/$gmpName/id/ + nsCOMPtr<nsIFile> idDir; + if (NS_FAILED(aPath->GetParent(getter_AddRefs(idDir)))) { return false; } - // $profileDir/gmp/storage/$nodeId/ - nsCOMPtr<nsIFile> path; - rv = mStoragePath->Clone(getter_AddRefs(path)); - if (NS_FAILED(rv)) { + // $profileDir/gmp/$platform/$gmpName/ + nsCOMPtr<nsIFile> temp; + if (NS_FAILED(idDir->GetParent(getter_AddRefs(temp)))) { return false; } - rv = path->AppendNative(salt); - return NS_SUCCEEDED(rv) && IsModifiedAfter(path); + // $profileDir/gmp/$platform/$gmpName/storage/ + if (NS_FAILED(temp->Append(NS_LITERAL_STRING("storage")))) { + return false; + } + // $profileDir/gmp/$platform/$gmpName/storage/$originSalt + return NS_SUCCEEDED(temp->AppendNative(salt)) && IsModifiedAfter(temp); } private: const PRTime mSince; - const nsCOMPtr<nsIFile> mStoragePath; - } filter(aSince, storagePath.forget()); + } filter(aSince); ClearNodeIdAndPlugin(filter); NS_DispatchToMainThread(new NotifyObserversTask("gmp-clear-storage-complete"), NS_DISPATCH_NORMAL); } NS_IMETHODIMP GeckoMediaPluginServiceParent::ForgetThisSite(const nsAString& aSite) @@ -1696,17 +1697,17 @@ void GeckoMediaPluginServiceParent::ClearStorage() { MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); LOGD(("%s::%s", __CLASS__, __FUNCTION__)); // Kill plugins with valid nodeIDs. KillPlugins(mPlugins, mMutex, &IsNodeIdValid); - nsCOMPtr<nsIFile> path; // $profileDir/gmp/ + nsCOMPtr<nsIFile> path; // $profileDir/gmp/$platform/ nsresult rv = GetStorageDir(getter_AddRefs(path)); if (NS_WARN_IF(NS_FAILED(rv))) { return; } if (NS_FAILED(DeleteDir(path))) { NS_WARNING("Failed to delete GMP storage directory"); } @@ -1741,20 +1742,21 @@ GMPServiceParent::RecvLoadGMP(const nsCS *aPluginId = gmp->GetPluginId(); return aAlreadyBridgedTo.Contains(*aId) || gmp->Bridge(this); } bool GMPServiceParent::RecvGetGMPNodeId(const nsString& aOrigin, const nsString& aTopLevelOrigin, + const nsString& aGMPName, const bool& aInPrivateBrowsing, nsCString* aID) { - nsresult rv = mService->GetNodeId(aOrigin, aTopLevelOrigin, + nsresult rv = mService->GetNodeId(aOrigin, aTopLevelOrigin, aGMPName, aInPrivateBrowsing, *aID); return NS_SUCCEEDED(rv); } bool GMPServiceParent::RecvUpdateGMPTrialCreateState(const nsString& aKeySystem, const uint32_t& aState) {
--- a/dom/media/gmp/GMPServiceParent.h +++ b/dom/media/gmp/GMPServiceParent.h @@ -36,16 +36,17 @@ public: // mozIGeckoMediaPluginService NS_IMETHOD GetPluginVersionForAPI(const nsACString& aAPI, nsTArray<nsCString>* aTags, bool* aHasPlugin, nsACString& aOutVersion) override; NS_IMETHOD GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin, + const nsAString& aGMPName, bool aInPrivateBrowsingMode, UniquePtr<GetNodeIdCallback>&& aCallback) override; NS_IMETHOD UpdateTrialCreateState(const nsAString& aKeySystem, uint32_t aState) override; NS_DECL_MOZIGECKOMEDIAPLUGINCHROMESERVICE NS_DECL_NSIOBSERVER @@ -68,16 +69,17 @@ private: const nsCString& aAPI, const nsTArray<nsCString>& aTags); GMPParent* FindPluginForAPIFrom(size_t aSearchStartIndex, const nsCString& aAPI, const nsTArray<nsCString>& aTags, size_t* aOutPluginIndex); nsresult GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin, + const nsAString& aGMPName, bool aInPrivateBrowsing, nsACString& aOutId); void UnloadPlugins(); void CrashPlugins(); void NotifySyncShutdownComplete(); void NotifyAsyncShutdownComplete(); void LoadFromEnvironment(); @@ -90,17 +92,18 @@ private: nsresult SetAsyncShutdownTimeout(); struct DirectoryFilter { virtual bool operator()(nsIFile* aPath) = 0; ~DirectoryFilter() {} }; void ClearNodeIdAndPlugin(DirectoryFilter& aFilter); - + void ClearNodeIdAndPlugin(nsIFile* aPluginStorageDir, + DirectoryFilter& aFilter); void ForgetThisSiteOnGMPThread(const nsACString& aOrigin); void ClearRecentHistoryOnGMPThread(PRTime aSince); protected: friend class GMPParent; void ReAddOnGMPThread(const RefPtr<GMPParent>& aOld); void PluginTerminated(const RefPtr<GMPParent>& aOld); virtual void InitializePlugins() override; @@ -210,16 +213,17 @@ public: const nsCString& aApi, nsTArray<nsCString>&& aTags, nsTArray<ProcessId>&& aAlreadyBridgedTo, base::ProcessId* aID, nsCString* aDisplayName, uint32_t* aPluginId) override; virtual bool RecvGetGMPNodeId(const nsString& aOrigin, const nsString& aTopLevelOrigin, + const nsString& aGMPName, const bool& aInPrivateBrowsing, nsCString* aID) override; static bool RecvGetGMPPluginVersionForAPI(const nsCString& aAPI, nsTArray<nsCString>&& aTags, bool* aHasPlugin, nsCString* aVersion); virtual bool RecvUpdateGMPTrialCreateState(const nsString& aKeySystem, const uint32_t& aState) override;
--- a/dom/media/gmp/GMPStorageParent.cpp +++ b/dom/media/gmp/GMPStorageParent.cpp @@ -27,20 +27,22 @@ namespace mozilla { extern LogModule* GetGMPLog(); #define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg) #define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg) namespace gmp { -// We store the records in files in the profile dir. -// $profileDir/gmp/storage/$nodeId/ +// We store the records for a given GMP as files in the profile dir. +// $profileDir/gmp/$platform/$gmpName/storage/$nodeId/ static nsresult -GetGMPStorageDir(nsIFile** aTempDir, const nsCString& aNodeId) +GetGMPStorageDir(nsIFile** aTempDir, + const nsString& aGMPName, + const nsCString& aNodeId) { if (NS_WARN_IF(!aTempDir)) { return NS_ERROR_INVALID_ARG; } nsCOMPtr<mozIGeckoMediaPluginChromeService> mps = do_GetService("@mozilla.org/gecko-media-plugin-service;1"); if (NS_WARN_IF(!mps)) { @@ -48,16 +50,26 @@ GetGMPStorageDir(nsIFile** aTempDir, con } nsCOMPtr<nsIFile> tmpFile; nsresult rv = mps->GetStorageDir(getter_AddRefs(tmpFile)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + rv = tmpFile->Append(aGMPName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700); + if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + rv = tmpFile->AppendNative(NS_LITERAL_CSTRING("storage")); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700); if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -82,18 +94,20 @@ GetGMPStorageDir(nsIFile** aTempDir, con // the profile directory. The record name is a hash of the filename, // and we resolve hash collisions by just adding 1 to the hash code. // The format of records on disk is: // 4 byte, uint32_t $recordNameLength, in little-endian byte order, // record name (i.e. $recordNameLength bytes, no null terminator) // record bytes (entire remainder of file) class GMPDiskStorage : public GMPStorage { public: - explicit GMPDiskStorage(const nsCString& aNodeId) + explicit GMPDiskStorage(const nsCString& aNodeId, + const nsString& aGMPName) : mNodeId(aNodeId) + , mGMPName(aGMPName) { } ~GMPDiskStorage() { // Close all open file handles. for (auto iter = mRecords.ConstIter(); !iter.Done(); iter.Next()) { Record* record = iter.UserData(); if (record->mFileDesc) { @@ -101,39 +115,23 @@ public: record->mFileDesc = nullptr; } } } nsresult Init() { // Build our index of records on disk. nsCOMPtr<nsIFile> storageDir; - nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mNodeId); + nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mGMPName, mNodeId); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } - nsCOMPtr<nsISimpleEnumerator> iter; - rv = storageDir->GetDirectoryEntries(getter_AddRefs(iter)); - if (NS_FAILED(rv)) { - return NS_ERROR_FAILURE; - } - - bool hasMore; - while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) { - nsCOMPtr<nsISupports> supports; - rv = iter->GetNext(getter_AddRefs(supports)); - if (NS_FAILED(rv)) { - continue; - } - nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv)); - if (NS_FAILED(rv)) { - continue; - } - + DirectoryEnumerator iter(storageDir, DirectoryEnumerator::FilesAndDirs); + for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) { PRFileDesc* fd = nullptr; if (NS_FAILED(dirEntry->OpenNSPRFileDesc(PR_RDONLY, 0, &fd))) { continue; } int32_t recordLength = 0; nsCString recordName; nsresult err = ReadRecordMetadata(fd, recordLength, recordName); PR_Close(fd); @@ -329,17 +327,17 @@ private: // We store records in a file which is a hash of the record name. // If there is a hash collision, we just keep adding 1 to the hash // code, until we find a free slot. nsresult GetUnusedFilename(const nsACString& aRecordName, nsString& aOutFilename) { nsCOMPtr<nsIFile> storageDir; - nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mNodeId); + nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mGMPName, mNodeId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } uint64_t recordNameHash = HashString(PromiseFlatCString(aRecordName).get()); for (int i = 0; i < 1000000; i++) { nsCOMPtr<nsIFile> f; rv = storageDir->Clone(getter_AddRefs(f)); @@ -374,17 +372,17 @@ private: nsresult OpenStorageFile(const nsAString& aFileLeafName, const OpenFileMode aMode, PRFileDesc** aOutFD) { MOZ_ASSERT(aOutFD); nsCOMPtr<nsIFile> f; - nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mNodeId); + nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mGMPName, mNodeId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } f->Append(aFileLeafName); auto mode = PR_RDWR | PR_CREATE_FILE; if (aMode == Truncate) { mode |= PR_TRUNCATE; @@ -451,17 +449,17 @@ private: } return NS_OK; } nsresult RemoveStorageFile(const nsString& aFilename) { nsCOMPtr<nsIFile> f; - nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mNodeId); + nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mGMPName, mNodeId); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = f->Append(aFilename); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return f->Remove(/* bool recursive= */ false); @@ -480,16 +478,17 @@ private: nsString mFilename; nsCString mRecordName; PRFileDesc* mFileDesc; }; // Hash record name to record data. nsClassHashtable<nsCStringHashKey, Record> mRecords; const nsAutoCString mNodeId; + const nsString mGMPName; }; class GMPMemoryStorage : public GMPStorage { public: GMPErr Open(const nsCString& aRecordName) override { MOZ_ASSERT(!IsOpen(aRecordName)); @@ -587,17 +586,18 @@ GMPStorageParent::Init() return NS_ERROR_FAILURE; } bool persistent = false; if (NS_WARN_IF(NS_FAILED(mps->IsPersistentStorageAllowed(mNodeId, &persistent)))) { return NS_ERROR_FAILURE; } if (persistent) { - UniquePtr<GMPDiskStorage> storage = MakeUnique<GMPDiskStorage>(mNodeId); + UniquePtr<GMPDiskStorage> storage = + MakeUnique<GMPDiskStorage>(mNodeId, mPlugin->GetPluginBaseName()); if (NS_FAILED(storage->Init())) { NS_WARNING("Failed to initialize on disk GMP storage"); return NS_ERROR_FAILURE; } mStorage = Move(storage); } else { mStorage = MakeUnique<GMPMemoryStorage>(); }
--- a/dom/media/gmp/GMPUtils.cpp +++ b/dom/media/gmp/GMPUtils.cpp @@ -6,16 +6,17 @@ #include "GMPUtils.h" #include "nsDirectoryServiceDefs.h" #include "nsIFile.h" #include "nsCOMPtr.h" #include "nsLiteralString.h" #include "nsCRTGlue.h" #include "mozilla/Base64.h" +#include "nsISimpleEnumerator.h" namespace mozilla { bool GetEMEVoucherPath(nsIFile** aPath) { nsCOMPtr<nsIFile> path; NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(path)); @@ -66,9 +67,46 @@ ToBase64(const nsTArray<uint8_t>& aBytes bool FileExists(nsIFile* aFile) { bool exists = false; return aFile && NS_SUCCEEDED(aFile->Exists(&exists)) && exists; } +DirectoryEnumerator::DirectoryEnumerator(nsIFile* aPath, Mode aMode) + : mMode(aMode) +{ + aPath->GetDirectoryEntries(getter_AddRefs(mIter)); +} + +already_AddRefed<nsIFile> +DirectoryEnumerator::Next() +{ + if (!mIter) { + return nullptr; + } + bool hasMore = false; + while (NS_SUCCEEDED(mIter->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr<nsISupports> supports; + nsresult rv = mIter->GetNext(getter_AddRefs(supports)); + if (NS_FAILED(rv)) { + continue; + } + + nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv)); + if (NS_FAILED(rv)) { + continue; + } + + if (mMode == DirsOnly) { + bool isDirectory = false; + rv = path->IsDirectory(&isDirectory); + if (NS_FAILED(rv) || !isDirectory) { + continue; + } + } + return path.forget(); + } + return nullptr; +} + } // namespace mozilla
--- a/dom/media/gmp/GMPUtils.h +++ b/dom/media/gmp/GMPUtils.h @@ -3,19 +3,21 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef GMPUtils_h_ #define GMPUtils_h_ #include "mozilla/UniquePtr.h" #include "nsTArray.h" +#include "nsCOMPtr.h" class nsIFile; class nsCString; +class nsISimpleEnumerator; namespace mozilla { template<typename T> struct DestroyPolicy { void operator()(T* aGMPObject) const { aGMPObject->Destroy(); @@ -35,11 +37,29 @@ SplitAt(const char* aDelims, nsTArray<nsCString>& aOutTokens); nsCString ToBase64(const nsTArray<uint8_t>& aBytes); bool FileExists(nsIFile* aFile); +// Enumerate directory entries for a specified path. +class DirectoryEnumerator { +public: + + enum Mode { + DirsOnly, // Enumeration only includes directories. + FilesAndDirs // Enumeration includes directories and non-directory files. + }; + + DirectoryEnumerator(nsIFile* aPath, Mode aMode); + + already_AddRefed<nsIFile> Next(); + +private: + Mode mMode; + nsCOMPtr<nsISimpleEnumerator> mIter; +}; + } // namespace mozilla #endif
--- a/dom/media/gmp/PGMPService.ipdl +++ b/dom/media/gmp/PGMPService.ipdl @@ -14,16 +14,17 @@ sync protocol PGMPService { parent spawns PGMP as child; parent: sync LoadGMP(nsCString nodeId, nsCString api, nsCString[] tags, ProcessId[] alreadyBridgedTo) returns (ProcessId id, nsCString displayName, uint32_t pluginId); sync GetGMPNodeId(nsString origin, nsString topLevelOrigin, + nsString gmpName, bool inPrivateBrowsing) returns (nsCString id); async UpdateGMPTrialCreateState(nsString keySystem, uint32_t status); }; } // namespace gmp } // namespace mozilla
--- a/dom/media/gmp/mozIGeckoMediaPluginService.idl +++ b/dom/media/gmp/mozIGeckoMediaPluginService.idl @@ -47,17 +47,17 @@ public: [ptr] native TagArray(nsTArray<nsCString>); native GetGMPDecryptorCallback(mozilla::UniquePtr<GetGMPDecryptorCallback>&&); native GetGMPAudioDecoderCallback(mozilla::UniquePtr<GetGMPAudioDecoderCallback>&&); native GetGMPVideoDecoderCallback(mozilla::UniquePtr<GetGMPVideoDecoderCallback>&&); native GetGMPVideoEncoderCallback(mozilla::UniquePtr<GetGMPVideoEncoderCallback>&&); native GetNodeIdCallback(mozilla::UniquePtr<GetNodeIdCallback>&&); -[scriptable, uuid(661492d6-726b-4ba0-8e6e-14bfaf2b62e4)] +[scriptable, uuid(b5492915-2f0e-4973-9f91-a6fe61ac4749)] interface mozIGeckoMediaPluginService : nsISupports { /** * The GMP thread. Callable from any thread. */ readonly attribute nsIThread thread; @@ -140,16 +140,17 @@ interface mozIGeckoMediaPluginService : in GetGMPDecryptorCallback callback); /** * Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple. */ [noscript] void getNodeId(in AString origin, in AString topLevelOrigin, + in AString gmpName, in bool inPrivateBrowsingMode, in GetNodeIdCallback callback); /** * Stores the result of trying to create a decoder for the given keysystem. */ [noscript] void updateTrialCreateState(in AString keySystem, in uint32_t status);
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp +++ b/dom/media/gtest/TestGMPCrossOrigin.cpp @@ -268,34 +268,41 @@ EnumerateDir(nsIFile* aPath, T&& aDirIte } aDirIter(entry); } return NS_OK; } /** - * Enumerate files under $profileDir/gmp/$aDir/ (non-recursive). + * Enumerate files under $profileDir/gmp/$platform/gmp-fake/$aDir/ (non-recursive). */ template<typename T> static nsresult EnumerateGMPStorageDir(const nsACString& aDir, T&& aDirIter) { RefPtr<GeckoMediaPluginServiceParent> service = GeckoMediaPluginServiceParent::GetSingleton(); MOZ_ASSERT(service); - // $profileDir/gmp/ + // $profileDir/gmp/$platform/ nsCOMPtr<nsIFile> path; nsresult rv = service->GetStorageDir(getter_AddRefs(path)); if (NS_FAILED(rv)) { return rv; } - // $profileDir/gmp/$aDir/ + + // $profileDir/gmp/$platform/gmp-fake/ + rv = path->Append(NS_LITERAL_STRING("gmp-fake")); + if (NS_FAILED(rv)) { + return rv; + } + + // $profileDir/gmp/$platform/gmp-fake/$aDir/ rv = path->AppendNative(aDir); if (NS_FAILED(rv)) { return rv; } return EnumerateDir(path, aDirIter); } @@ -464,16 +471,17 @@ GetNodeId(const nsAString& aOrigin, nsCString nodeId; nsresult result; UniquePtr<GetNodeIdCallback> callback(new TestGetNodeIdCallback(nodeId, result)); // We rely on the fact that the GetNodeId implementation for // GeckoMediaPluginServiceParent is synchronous. nsresult rv = service->GetNodeId(aOrigin, aTopLevelOrigin, + NS_LITERAL_STRING("gmp-fake"), aInPBMode, Move(callback)); EXPECT_TRUE(NS_SUCCEEDED(rv) && NS_SUCCEEDED(result)); return nodeId; } static bool IsGMPStorageIsEmpty() @@ -815,20 +823,20 @@ class GMPStorageTest : public GMPDecrypt rv = EnumerateGMPStorageDir( NS_LITERAL_CSTRING("storage"), StorageVerifier(aSiteInfo)); EXPECT_TRUE(NS_SUCCEEDED(rv)); } /** * 1. Generate some storage data. - * 2. Find the max mtime |t| in $profileDir/gmp/id/. + * 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/id/. * 3. Pass |t| to clear recent history. - * 4. Check if all directories in $profileDir/gmp/id/ and - * $profileDir/gmp/storage are removed. + * 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and + * $profileDir/gmp/$platform/gmp-fake/storage are removed. */ void TestClearRecentHistory1() { AssertIsOnGMPThread(); EXPECT_TRUE(IsGMPStorageIsEmpty()); // Generate storage data for some site. nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod( this, &GMPStorageTest::TestClearRecentHistory1_Clear); @@ -837,20 +845,20 @@ class GMPStorageTest : public GMPDecrypt CreateDecryptor(NS_LITERAL_STRING("http://example1.com"), NS_LITERAL_STRING("http://example2.com"), false, NS_LITERAL_CSTRING("test-storage")); } /** * 1. Generate some storage data. - * 2. Find the max mtime |t| in $profileDir/gmp/storage/. + * 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/storage/. * 3. Pass |t| to clear recent history. - * 4. Check if all directories in $profileDir/gmp/id/ and - * $profileDir/gmp/storage are removed. + * 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and + * $profileDir/gmp/$platform/gmp-fake/storage are removed. */ void TestClearRecentHistory2() { AssertIsOnGMPThread(); EXPECT_TRUE(IsGMPStorageIsEmpty()); // Generate storage data for some site. nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod( this, &GMPStorageTest::TestClearRecentHistory2_Clear); @@ -859,20 +867,20 @@ class GMPStorageTest : public GMPDecrypt CreateDecryptor(NS_LITERAL_STRING("http://example1.com"), NS_LITERAL_STRING("http://example2.com"), false, NS_LITERAL_CSTRING("test-storage")); } /** * 1. Generate some storage data. - * 2. Find the max mtime |t| in $profileDir/gmp/storage/. + * 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/storage/. * 3. Pass |t+1| to clear recent history. - * 4. Check if all directories in $profileDir/gmp/id/ and - * $profileDir/gmp/storage remain unchanged. + * 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and + * $profileDir/gmp/$platform/gmp-fake/storage remain unchanged. */ void TestClearRecentHistory3() { AssertIsOnGMPThread(); EXPECT_TRUE(IsGMPStorageIsEmpty()); // Generate storage data for some site. nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod( this, &GMPStorageTest::TestClearRecentHistory3_Clear); @@ -943,39 +951,39 @@ class GMPStorageTest : public GMPDecrypt private: int mCount; }; void TestClearRecentHistory_CheckEmpty() { FileCounter c1; nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), c1); EXPECT_TRUE(NS_SUCCEEDED(rv)); - // There should be no files under $profileDir/gmp/id/ + // There should be no files under $profileDir/gmp/$platform/gmp-fake/id/ EXPECT_EQ(c1.GetCount(), 0); FileCounter c2; rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), c2); EXPECT_TRUE(NS_SUCCEEDED(rv)); - // There should be no files under $profileDir/gmp/storage/ + // There should be no files under $profileDir/gmp/$platform/gmp-fake/storage/ EXPECT_EQ(c2.GetCount(), 0); SetFinished(); } void TestClearRecentHistory_CheckNonEmpty() { FileCounter c1; nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), c1); EXPECT_TRUE(NS_SUCCEEDED(rv)); - // There should be one directory under $profileDir/gmp/id/ + // There should be one directory under $profileDir/gmp/$platform/gmp-fake/id/ EXPECT_EQ(c1.GetCount(), 1); FileCounter c2; rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), c2); EXPECT_TRUE(NS_SUCCEEDED(rv)); - // There should be one directory under $profileDir/gmp/storage/ + // There should be one directory under $profileDir/gmp/$platform/gmp-fake/storage/ EXPECT_EQ(c2.GetCount(), 1); SetFinished(); } void TestCrossOriginStorage() { EXPECT_TRUE(!mDecryptor);
--- a/dom/media/test/mochitest.ini +++ b/dom/media/test/mochitest.ini @@ -614,16 +614,18 @@ skip-if = (toolkit == 'android' && proce [test_defaultMuted.html] [test_delay_load.html] skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984 [test_dormant_playback.html] skip-if = (os == 'win' && os_version == '5.1') || (os != 'win' && toolkit != 'gonk') [test_eme_session_callable_value.html] [test_eme_canvas_blocked.html] skip-if = toolkit == 'android' # bug 1149374 +[test_eme_key_ids_initdata.html] +skip-if = toolkit == 'android' # bug 1149374 [test_eme_non_mse_fails.html] skip-if = toolkit == 'android' # bug 1149374 [test_eme_request_notifications.html] skip-if = toolkit == 'android' # bug 1149374 [test_eme_persistent_sessions.html] skip-if = toolkit == 'android' # bug 1149374 [test_eme_playback.html] skip-if = toolkit == 'android' || toolkit == 'gonk' # android: bug 1149374; gonk: bug 1193351
new file mode 100644 --- /dev/null +++ b/dom/media/test/test_eme_key_ids_initdata.html @@ -0,0 +1,111 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test Encrypted Media Extensions</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="text/javascript" src="manifest.js"></script> + <script type="text/javascript" src="eme.js"></script> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +var tests = [ + { + name: "One keyId", + initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"]}', + expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"],"type":"temporary"}', + sessionType: 'temporary', + expectPass: true, + }, + { + name: "Two keyIds", + initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A", "0DdtU9od-Bh5L3xbv0Xf_A"]}', + expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A","0DdtU9od-Bh5L3xbv0Xf_A"],"type":"temporary"}', + sessionType: 'temporary', + expectPass: true, + }, + { + name: "Two keyIds, temporary session", + initData: '{"type":"temporary", "kids":["LwVHf8JLtPrv2GUXFW2v_A", "0DdtU9od-Bh5L3xbv0Xf_A"]}', + expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A","0DdtU9od-Bh5L3xbv0Xf_A"],"type":"temporary"}', + sessionType: 'temporary', + expectPass: true, + }, + { + name: "Two keyIds, persistent session, type before kids", + initData: '{"type":"persistent", "kids":["LwVHf8JLtPrv2GUXFW2v_A", "0DdtU9od-Bh5L3xbv0Xf_A"]}', + expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A","0DdtU9od-Bh5L3xbv0Xf_A"],"type":"persistent"}', + sessionType: 'persistent', + expectPass: true, + }, + { + name: "Invalid keyId", + initData: '{"kids":["0"]}', + sessionType: 'temporary', + expectPass: false, + }, + { + name: "Empty keyId", + initData: '{"kids":[""]}', + sessionType: 'temporary', + expectPass: false, + }, + { + name: "SessionType in license doesn't match MediaKeySession's sessionType", + initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"]}', + sessionType: 'persistent', + expectPass: false, + }, + { + name: "One valid and one invalid kid", + initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A", "invalid"]}', + expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"],"type":"temporary"}', + sessionType: 'temporary', + expectPass: true, + }, + { + name: "Invalid initData", + initData: 'invalid initData', + sessionType: 'temporary', + expectPass: false, + }, +]; + +function Test(test) { + return new Promise(function(resolve, reject) { + navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{initDataTypes: ['keyids']}]).then( + (access) => access.createMediaKeys() + ).then( + (mediaKeys) => { + var session = mediaKeys.createSession(test.sessionType); + var initData = new TextEncoder().encode(test.initData); + session.addEventListener("message", function(event) { + is(event.messageType, "license-request", "'" + test.name + "' MediaKeyMessage type should be license-request."); + var text = new TextDecoder().decode(event.message); + is(text, test.expectedRequest, "'" + test.name + "' got expected response."); + is(text == test.expectedRequest, test.expectPass, + "'" + test.name + "' expected to " + (test.expectPass ? "pass" : "fail")); + resolve(); + }); + return session.generateRequest('keyids', initData); + } + ).catch((x) => { + ok(!test.expectPass, "'" + test.name + "' expected to fail."); + resolve(); + }); + }); +} + +function beginTest() { + Promise.all(tests.map(Test)).then(function() { SimpleTest.finish(); }); +} + +SimpleTest.waitForExplicitFinish(); +SetupEMEPref(beginTest); + +</script> +</pre> +</body> +</html>
--- a/dom/plugins/ipc/PPluginModule.ipdl +++ b/dom/plugins/ipc/PPluginModule.ipdl @@ -2,16 +2,17 @@ /* 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 protocol PPluginInstance; include protocol PPluginScriptableObject; include protocol PCrashReporter; include protocol PContent; +include ProfilerTypes; using NPError from "npapi.h"; using NPNVariable from "npapi.h"; using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h"; using class mac_plugin_interposing::NSCursorInfo from "mozilla/plugins/PluginMessageUtils.h"; using struct nsID from "nsID.h"; namespace mozilla { @@ -88,18 +89,17 @@ child: async SetParentHangTimeout(uint32_t seconds); intr PCrashReporter() returns (NativeThreadId tid, uint32_t processType); /** * Control the Gecko Profiler in the plugin process. */ - async StartProfiler(uint32_t aEntries, double aInterval, nsCString[] aFeatures, - nsCString[] aThreadNameFilters); + async StartProfiler(ProfilerInitParams params); async StopProfiler(); async GatherProfile(); async SettingChanged(PluginSettings settings); parent: async NP_InitializeResult(NPError aError);
--- a/dom/plugins/ipc/PluginModuleChild.cpp +++ b/dom/plugins/ipc/PluginModuleChild.cpp @@ -2519,32 +2519,30 @@ PluginModuleChild::RecvProcessNativeEven #ifdef MOZ_WIDGET_COCOA void PluginModuleChild::ProcessNativeEvents() { CallProcessSomeEvents(); } #endif bool -PluginModuleChild::RecvStartProfiler(const uint32_t& aEntries, - const double& aInterval, - nsTArray<nsCString>&& aFeatures, - nsTArray<nsCString>&& aThreadNameFilters) +PluginModuleChild::RecvStartProfiler(const ProfilerInitParams& params) { nsTArray<const char*> featureArray; - for (size_t i = 0; i < aFeatures.Length(); ++i) { - featureArray.AppendElement(aFeatures[i].get()); + for (size_t i = 0; i < params.features().Length(); ++i) { + featureArray.AppendElement(params.features()[i].get()); } nsTArray<const char*> threadNameFilterArray; - for (size_t i = 0; i < aThreadNameFilters.Length(); ++i) { - threadNameFilterArray.AppendElement(aThreadNameFilters[i].get()); + for (size_t i = 0; i < params.threadFilters().Length(); ++i) { + threadNameFilterArray.AppendElement(params.threadFilters()[i].get()); } - profiler_start(aEntries, aInterval, featureArray.Elements(), featureArray.Length(), + profiler_start(params.entries(), params.interval(), + featureArray.Elements(), featureArray.Length(), threadNameFilterArray.Elements(), threadNameFilterArray.Length()); return true; } bool PluginModuleChild::RecvStopProfiler() {
--- a/dom/plugins/ipc/PluginModuleChild.h +++ b/dom/plugins/ipc/PluginModuleChild.h @@ -142,20 +142,17 @@ protected: virtual void ActorDestroy(ActorDestroyReason why) override; MOZ_NORETURN void QuickExit(); virtual bool RecvProcessNativeEventsInInterruptCall() override; - virtual bool RecvStartProfiler(const uint32_t& aEntries, - const double& aInterval, - nsTArray<nsCString>&& aFeatures, - nsTArray<nsCString>&& aThreadNameFilters) override; + virtual bool RecvStartProfiler(const ProfilerInitParams& params) override; virtual bool RecvStopProfiler() override; virtual bool RecvGatherProfile() override; public: explicit PluginModuleChild(bool aIsChrome); virtual ~PluginModuleChild(); bool CommonInit(base::ProcessId aParentPid,
--- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -3145,17 +3145,25 @@ PluginProfilerObserver::Observe(nsISuppo if (!strcmp(aTopic, "profiler-started")) { nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject)); uint32_t entries; double interval; params->GetEntries(&entries); params->GetInterval(&interval); const nsTArray<nsCString>& features = params->GetFeatures(); const nsTArray<nsCString>& threadFilterNames = params->GetThreadFilterNames(); - Unused << mPmp->SendStartProfiler(entries, interval, features, threadFilterNames); + + ProfilerInitParams ipcParams; + ipcParams.enabled() = true; + ipcParams.entries() = entries; + ipcParams.interval() = interval; + ipcParams.features() = features; + ipcParams.threadFilters() = threadFilterNames; + + Unused << mPmp->SendStartProfiler(ipcParams); } else if (!strcmp(aTopic, "profiler-stopped")) { Unused << mPmp->SendStopProfiler(); } else if (!strcmp(aTopic, "profiler-subprocess-gather")) { RefPtr<ProfileGatherer> gatherer = static_cast<ProfileGatherer*>(aSubject); mPmp->GatherAsyncProfile(gatherer); } else if (!strcmp(aTopic, "profiler-subprocess")) { nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject); mPmp->GatheredAsyncProfile(pse);
--- a/dom/push/PushService.jsm +++ b/dom/push/PushService.jsm @@ -273,17 +273,17 @@ this.PushService = { case "perm-changed": this._onPermissionChange(aSubject, aData).catch(error => { console.error("onPermissionChange: Error updating registrations:", error); }) break; case "clear-origin-data": - this._clearOriginData(data).catch(error => { + this._clearOriginData(aData).catch(error => { console.error("clearOriginData: Error clearing origin data:", error); }); break; } }, _clearOriginData: function(data) { console.log("clearOriginData()");
--- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -128,128 +128,176 @@ struct ServiceWorkerManager::Registratio nsTArray<nsCString> mOrderedScopes; // Scope to registration. // The scope should be a fully qualified valid URL. nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mInfos; // Maps scopes to job queues. nsClassHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues; - - nsDataHashtable<nsCStringHashKey, bool> mSetOfScopesBeingUpdated; }; struct ServiceWorkerManager::PendingOperation final { nsCOMPtr<nsIRunnable> mRunnable; ServiceWorkerJobQueue* mQueue; RefPtr<ServiceWorkerJob> mJob; ServiceWorkerRegistrationData mRegistration; }; class ServiceWorkerJob : public nsISupports { + friend class ServiceWorkerJobQueue; + +public: + NS_DECL_ISUPPORTS + + enum Type + { + RegisterJob, + UpdateJob, + InstallJob, + UnregisterJob + }; + + virtual void Start() = 0; + + bool + IsRegisterOrInstallJob() const + { + return mJobType == RegisterJob || mJobType == UpdateJob || + mJobType == InstallJob; + } + protected: // The queue keeps the jobs alive, so they can hold a rawptr back to the // queue. ServiceWorkerJobQueue* mQueue; -public: - NS_DECL_ISUPPORTS - - virtual void Start() = 0; - - virtual bool - IsRegisterJob() const { return false; } - -protected: - explicit ServiceWorkerJob(ServiceWorkerJobQueue* aQueue) + Type mJobType; + + explicit ServiceWorkerJob(ServiceWorkerJobQueue* aQueue, Type aJobType) : mQueue(aQueue) + , mJobType(aJobType) {} virtual ~ServiceWorkerJob() {} void Done(nsresult aStatus); }; class ServiceWorkerJobQueue final { friend class ServiceWorkerJob; - nsTArray<RefPtr<ServiceWorkerJob>> mJobs; + struct QueueData final + { + QueueData() + : mPopping(false) + { } + + ~QueueData() + { + if (!mJobs.IsEmpty()) { + NS_WARNING("Pending/running jobs still around on shutdown!"); + } + } + + nsTArray<RefPtr<ServiceWorkerJob>> mJobs; + bool mPopping; + }; + const nsCString mOriginAttributesSuffix; - bool mPopping; + QueueData mRegistrationJobQueue; + QueueData mInstallationJobQueue; public: explicit ServiceWorkerJobQueue(const nsACString& aScopeKey) : mOriginAttributesSuffix(aScopeKey) - , mPopping(false) {} ~ServiceWorkerJobQueue() - { - if (!mJobs.IsEmpty()) { - NS_WARNING("Pending/running jobs still around on shutdown!"); - } - } + { } void Append(ServiceWorkerJob* aJob) { MOZ_ASSERT(aJob); - MOZ_ASSERT(!mJobs.Contains(aJob)); - bool wasEmpty = mJobs.IsEmpty(); - mJobs.AppendElement(aJob); + QueueData& queue = GetQueue(aJob->mJobType); + MOZ_ASSERT(!queue.mJobs.Contains(aJob)); + + bool wasEmpty = queue.mJobs.IsEmpty(); + queue.mJobs.AppendElement(aJob); if (wasEmpty) { aJob->Start(); } } void CancelJobs(); - // Only used by HandleError, keep it that way! - ServiceWorkerJob* - Peek() - { - if (mJobs.IsEmpty()) { - return nullptr; - } - return mJobs[0]; - } - private: void - Pop() + CancelJobs(QueueData& aQueue); + + // Internal helper function used to assign jobs to the correct queue. + QueueData& + GetQueue(ServiceWorkerJob::Type aType) { - MOZ_ASSERT(!mPopping, + switch (aType) { + case ServiceWorkerJob::Type::RegisterJob: + case ServiceWorkerJob::Type::UpdateJob: + case ServiceWorkerJob::Type::UnregisterJob: + return mRegistrationJobQueue; + case ServiceWorkerJob::Type::InstallJob: + return mInstallationJobQueue; + default: + MOZ_CRASH("Invalid job queue type."); + return mRegistrationJobQueue; + } + } + + bool + IsEmpty() + { + return mRegistrationJobQueue.mJobs.IsEmpty() && + mInstallationJobQueue.mJobs.IsEmpty(); + } + + void + Pop(QueueData& aQueue) + { + MOZ_ASSERT(!aQueue.mPopping, "Pop() called recursively, did you write a job which calls Done() synchronously from Start()?"); - AutoRestore<bool> savePopping(mPopping); - mPopping = true; - MOZ_ASSERT(!mJobs.IsEmpty()); - mJobs.RemoveElementAt(0); - if (!mJobs.IsEmpty()) { - mJobs[0]->Start(); - } else { + + AutoRestore<bool> savePopping(aQueue.mPopping); + aQueue.mPopping = true; + MOZ_ASSERT(!aQueue.mJobs.IsEmpty()); + aQueue.mJobs.RemoveElementAt(0); + if (!aQueue.mJobs.IsEmpty()) { + aQueue.mJobs[0]->Start(); + } else if (IsEmpty()) { RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); MOZ_ASSERT(swm); swm->MaybeRemoveRegistrationInfo(mOriginAttributesSuffix); } } void Done(ServiceWorkerJob* aJob) { - MOZ_ASSERT(!mJobs.IsEmpty()); - MOZ_ASSERT(mJobs[0] == aJob); - Pop(); + MOZ_ASSERT(aJob); + QueueData& queue = GetQueue(aJob->mJobType); + MOZ_ASSERT(!queue.mJobs.IsEmpty()); + MOZ_ASSERT(queue.mJobs[0] == aJob); + Pop(queue); } }; namespace { nsresult PopulateRegistrationData(nsIPrincipal* aPrincipal, const ServiceWorkerRegistrationInfo* aRegistration, @@ -370,16 +418,17 @@ ServiceWorkerRegistrationInfo::Clear() } ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope, nsIPrincipal* aPrincipal) : mControlledDocumentsCounter(0) , mScope(aScope) , mPrincipal(aPrincipal) , mLastUpdateCheckTime(0) + , mUpdating(false) , mPendingUninstall(false) {} ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo() { if (IsControllingDocuments()) { NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive."); } @@ -551,24 +600,24 @@ protected: {} public: virtual void ContinueAfterWorkerEvent(bool aSuccess) = 0; }; NS_IMPL_ISUPPORTS0(ContinueLifecycleTask); -class ServiceWorkerRegisterJob; +class ServiceWorkerInstallJob; class ContinueInstallTask final : public ContinueLifecycleTask { - RefPtr<ServiceWorkerRegisterJob> mJob; + RefPtr<ServiceWorkerInstallJob> mJob; public: - explicit ContinueInstallTask(ServiceWorkerRegisterJob* aJob) + explicit ContinueInstallTask(ServiceWorkerInstallJob* aJob) : mJob(aJob) {} void ContinueAfterWorkerEvent(bool aSuccess) override; }; class ContinueActivateTask final : public ContinueLifecycleTask { @@ -626,38 +675,45 @@ public: : mWindow(aWindow) , mPromise(aPromise) {} void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override { RefPtr<ServiceWorkerRegistrationMainThread> swr = - new ServiceWorkerRegistrationMainThread(mWindow, - NS_ConvertUTF8toUTF16(aInfo->mScope)); + mWindow->GetServiceWorkerRegistration(NS_ConvertUTF8toUTF16(aInfo->mScope)); mPromise->MaybeResolve(swr); } void UpdateFailed(ErrorResult& aStatus) override { mPromise->MaybeReject(aStatus); } }; -class ContinueUpdateRunnable final : public nsRunnable +class ContinueUpdateRunnable final : public LifeCycleEventCallback { nsMainThreadPtrHandle<nsISupports> mJob; + bool mScriptEvaluationResult; public: explicit ContinueUpdateRunnable(const nsMainThreadPtrHandle<nsISupports> aJob) : mJob(aJob) + , mScriptEvaluationResult(false) { AssertIsOnMainThread(); } + void + SetResult(bool aResult) + { + mScriptEvaluationResult = aResult; + } + NS_IMETHOD Run(); }; namespace { /** * The spec mandates slightly different behaviors for computing the scope * prefix string in case a Service-Worker-Allowed header is specified versus @@ -850,118 +906,323 @@ public: private: ~PropagateRemoveAllRunnable() {} }; } // namespace -class ServiceWorkerRegisterJob final : public ServiceWorkerJob, +class ServiceWorkerJobBase : public ServiceWorkerJob +{ +public: + ServiceWorkerJobBase(ServiceWorkerJobQueue* aQueue, + ServiceWorkerJob::Type aJobType, + ServiceWorkerUpdateFinishCallback* aCallback) + : ServiceWorkerJobBase(aQueue, aJobType, aCallback, nullptr, nullptr) + { } + + ServiceWorkerJobBase(ServiceWorkerJobQueue* aQueue, + ServiceWorkerJob::Type aJobType, + ServiceWorkerUpdateFinishCallback* aCallback, + ServiceWorkerRegistrationInfo* aRegistration, + ServiceWorkerInfo* aServiceWorkerInfo) + : ServiceWorkerJob(aQueue, aJobType) + , mCallback(aCallback) + , mCanceled(false) + , mRegistration(aRegistration) + , mUpdateAndInstallInfo(aServiceWorkerInfo) + { + AssertIsOnMainThread(); + } + + void + Cancel() + { + mQueue = nullptr; + mCanceled = true; + } + +protected: + RefPtr<ServiceWorkerUpdateFinishCallback> mCallback; + bool mCanceled; + RefPtr<ServiceWorkerRegistrationInfo> mRegistration; + RefPtr<ServiceWorkerInfo> mUpdateAndInstallInfo; + + ~ServiceWorkerJobBase() + { } + + // This MUST only be called when the job is still performing actions related + // to registration or update. After the spec resolves the update promise, use + // Done() with the failure code instead. + // Callers MUST hold a strong ref before calling this! + void + Fail(ErrorResult& aRv) + { + AssertIsOnMainThread(); + MOZ_ASSERT(mRegistration); + + // With cancellation support, we may only be running with one reference + // from another object like a stream loader or something. + RefPtr<ServiceWorkerJob> kungFuDeathGrip = this; + + // Save off the plain error code to pass to Done() where its logged to + // stderr as a warning. + nsresult origStatus = static_cast<nsresult>(aRv.ErrorCodeAsInt()); + + // Ensure that we only surface SecurityErr or TypeErr to script. + if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) && + !aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR)) { + + // Remove the old error code so we can replace it with a TypeError. + aRv.SuppressException(); + + NS_ConvertUTF8toUTF16 scriptSpec(mRegistration->mScriptSpec); + NS_ConvertUTF8toUTF16 scope(mRegistration->mScope); + + // Throw the type error with a generic error message. + aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope); + } + + if (mCallback) { + mCallback->UpdateFailed(aRv); + mCallback = nullptr; + } + // In case the callback does not consume the exception + aRv.SuppressException(); + + mUpdateAndInstallInfo = nullptr; + if (mRegistration->mInstallingWorker) { + nsresult rv = serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal, + mRegistration->mInstallingWorker->CacheName()); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to purge the installing worker cache."); + } + } + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + swm->MaybeRemoveRegistration(mRegistration); + // Ensures that the job can't do anything useful from this point on. + mRegistration = nullptr; + Done(origStatus); + } + + void + Fail(nsresult aRv) + { + ErrorResult rv(aRv); + Fail(rv); + } + + void + Succeed() + { + AssertIsOnMainThread(); + // We don't have a callback for soft updates. + if (mCallback) { + mCallback->UpdateSucceeded(mRegistration); + mCallback = nullptr; + } + } +}; + +class ServiceWorkerInstallJob final : public ServiceWorkerJobBase +{ + friend class ContinueInstallTask; + +public: + ServiceWorkerInstallJob(ServiceWorkerJobQueue* aQueue, + ServiceWorkerUpdateFinishCallback* aCallback, + ServiceWorkerRegistrationInfo* aRegistration, + ServiceWorkerInfo* aServiceWorkerInfo) + : ServiceWorkerJobBase(aQueue, Type::InstallJob, aCallback, + aRegistration, aServiceWorkerInfo) + { + MOZ_ASSERT(aRegistration); + } + + void + Start() + { + AssertIsOnMainThread(); + nsCOMPtr<nsIRunnable> r = + NS_NewRunnableMethod(this, &ServiceWorkerInstallJob::Install); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r))); + } + + void + Install() + { + RefPtr<ServiceWorkerJob> kungFuDeathGrip = this; + if (mCanceled) { + return Fail(NS_ERROR_DOM_ABORT_ERR); + } + MOZ_ASSERT(mRegistration); + + // Begin [[Install]] atomic step 3. + if (mRegistration->mInstallingWorker) { + mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant); + mRegistration->mInstallingWorker->WorkerPrivate()->TerminateWorker(); + } + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + swm->InvalidateServiceWorkerRegistrationWorker(mRegistration, + WhichServiceWorker::INSTALLING_WORKER); + + mRegistration->mInstallingWorker = mUpdateAndInstallInfo.forget(); + mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing); + mRegistration->NotifyListenersOnChange(); + + Succeed(); + + // The job should NOT call fail from this point on. + + // Step 8 "Queue a task..." for updatefound. + nsCOMPtr<nsIRunnable> upr = + NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>( + swm, + &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations, + mRegistration); + + NS_DispatchToMainThread(upr); + + // Call ContinueAfterInstallEvent(false) on main thread if the SW + // script fails to load. + nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs<bool> + (this, &ServiceWorkerInstallJob::ContinueAfterInstallEvent, false); + + nsMainThreadPtrHandle<ContinueLifecycleTask> installTask( + new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this))); + RefPtr<LifeCycleEventCallback> callback = new ContinueLifecycleRunnable(installTask); + + // This triggers Step 4.7 "Queue a task to run the following substeps..." + // which sends the install event to the worker. + ServiceWorkerPrivate* workerPrivate = + mRegistration->mInstallingWorker->WorkerPrivate(); + nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("install"), + callback, failRunnable); + + if (NS_WARN_IF(NS_FAILED(rv))) { + ContinueAfterInstallEvent(false /* aSuccess */); + } + } + + void + ContinueAfterInstallEvent(bool aInstallEventSuccess) + { + if (mCanceled) { + return Done(NS_ERROR_DOM_ABORT_ERR); + } + + if (!mRegistration->mInstallingWorker) { + NS_WARNING("mInstallingWorker was null."); + return Done(NS_ERROR_DOM_ABORT_ERR); + } + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + + // "If installFailed is true" + if (NS_WARN_IF(!aInstallEventSuccess)) { + mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant); + mRegistration->mInstallingWorker = nullptr; + swm->InvalidateServiceWorkerRegistrationWorker(mRegistration, + WhichServiceWorker::INSTALLING_WORKER); + swm->MaybeRemoveRegistration(mRegistration); + return Done(NS_ERROR_DOM_ABORT_ERR); + } + + // "If registration's waiting worker is not null" + if (mRegistration->mWaitingWorker) { + mRegistration->mWaitingWorker->WorkerPrivate()->TerminateWorker(); + mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant); + + nsresult rv = + serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal, + mRegistration->mWaitingWorker->CacheName()); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to purge the old waiting cache."); + } + } + + mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget(); + mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed); + mRegistration->NotifyListenersOnChange(); + swm->InvalidateServiceWorkerRegistrationWorker(mRegistration, + WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER); + + // "If registration's waiting worker's skip waiting flag is set" + if (mRegistration->mWaitingWorker->SkipWaitingFlag()) { + mRegistration->PurgeActiveWorker(); + } + + Done(NS_OK); + // Activate() is invoked out of band of atomic. + mRegistration->TryToActivate(); + } +}; + +class ServiceWorkerRegisterJob final : public ServiceWorkerJobBase, public serviceWorkerScriptCache::CompareCallback { - friend class ContinueInstallTask; + friend class ContinueUpdateRunnable; nsCString mScope; nsCString mScriptSpec; - RefPtr<ServiceWorkerRegistrationInfo> mRegistration; - nsTArray<RefPtr<ServiceWorkerUpdateFinishCallback>> mCallbacks; nsCOMPtr<nsIPrincipal> mPrincipal; - RefPtr<ServiceWorkerInfo> mUpdateAndInstallInfo; nsCOMPtr<nsILoadGroup> mLoadGroup; ~ServiceWorkerRegisterJob() - {} - - enum - { - REGISTER_JOB = 0, - UPDATE_JOB = 1, - } mJobType; - - bool mCanceled; + { } public: NS_DECL_ISUPPORTS_INHERITED // [[Register]] ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue, const nsCString& aScope, const nsCString& aScriptSpec, ServiceWorkerUpdateFinishCallback* aCallback, nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup) - : ServiceWorkerJob(aQueue) + : ServiceWorkerJobBase(aQueue, Type::RegisterJob, aCallback) , mScope(aScope) , mScriptSpec(aScriptSpec) , mPrincipal(aPrincipal) , mLoadGroup(aLoadGroup) - , mJobType(REGISTER_JOB) - , mCanceled(false) { AssertIsOnMainThread(); MOZ_ASSERT(mLoadGroup); MOZ_ASSERT(aCallback); - - mCallbacks.AppendElement(aCallback); } // [[Update]] ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue, ServiceWorkerRegistrationInfo* aRegistration, ServiceWorkerUpdateFinishCallback* aCallback) - : ServiceWorkerJob(aQueue) - , mRegistration(aRegistration) - , mJobType(UPDATE_JOB) - , mCanceled(false) + : ServiceWorkerJobBase(aQueue, Type::UpdateJob, aCallback, + aRegistration, nullptr) { AssertIsOnMainThread(); - MOZ_ASSERT(aCallback); - - mCallbacks.AppendElement(aCallback); - } - - bool - IsRegisterJob() const override - { - return true; - } - - void - AppendCallback(ServiceWorkerUpdateFinishCallback* aCallback) - { - AssertIsOnMainThread(); - MOZ_ASSERT(aCallback); - MOZ_ASSERT(!mCallbacks.Contains(aCallback)); - - mCallbacks.AppendElement(aCallback); - } - - void - Cancel() - { - mQueue = nullptr; - mCanceled = true; } void Start() override { AssertIsOnMainThread(); MOZ_ASSERT(!mCanceled); RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); if (!swm->HasBackgroundActor()) { nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethod(this, &ServiceWorkerRegisterJob::Start); swm->AppendPendingOperation(runnable); return; } - if (mJobType == REGISTER_JOB) { + if (mJobType == RegisterJob) { mRegistration = swm->GetRegistration(mPrincipal, mScope); if (mRegistration) { mRegistration->mPendingUninstall = false; RefPtr<ServiceWorkerInfo> newest = mRegistration->Newest(); if (newest && mScriptSpec.Equals(newest->ScriptSpec()) && mScriptSpec.Equals(mRegistration->mScriptSpec)) { swm->StoreRegistration(mPrincipal, mRegistration); @@ -980,21 +1241,17 @@ public: } else { mRegistration = swm->CreateNewRegistration(mScope, mPrincipal); } mRegistration->mScriptSpec = mScriptSpec; mRegistration->NotifyListenersOnChange(); swm->StoreRegistration(mPrincipal, mRegistration); } else { - MOZ_ASSERT(mJobType == UPDATE_JOB); - MOZ_ASSERT(mRegistration); - MOZ_ASSERT(mRegistration->mUpdateJob == nullptr); - - mRegistration->mUpdateJob = this; + MOZ_ASSERT(mJobType == UpdateJob); } Update(); } void ComparisonResult(nsresult aStatus, bool aInCacheAndEqual, const nsAString& aNewCacheName, @@ -1066,242 +1323,95 @@ public: return Fail(NS_ERROR_FAILURE); } ServiceWorkerManager::RegistrationDataPerPrincipal* data; if (!swm->mRegistrationInfos.Get(scopeKey, &data)) { return Fail(NS_ERROR_FAILURE); } - nsAutoString cacheName; - // We have to create a ServiceWorker here simply to ensure there are no - // errors. Ideally we should just pass this worker on to ContinueInstall. - MOZ_ASSERT(!data->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope)); - data->mSetOfScopesBeingUpdated.Put(mRegistration->mScope, true); - - // Call FailScopeUpdate on main thread if the SW script load fails below. - nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs - <StorensRefPtrPassByPtr<ServiceWorkerManager>, nsCString> - (this, &ServiceWorkerRegisterJob::FailScopeUpdate, swm, scopeKey); - MOZ_ASSERT(!mUpdateAndInstallInfo); mUpdateAndInstallInfo = new ServiceWorkerInfo(mRegistration, mRegistration->mScriptSpec, aNewCacheName); RefPtr<ServiceWorkerJob> upcasted = this; nsMainThreadPtrHandle<nsISupports> handle( new nsMainThreadPtrHolder<nsISupports>(upcasted)); - RefPtr<nsRunnable> callback = new ContinueUpdateRunnable(handle); + RefPtr<LifeCycleEventCallback> callback = new ContinueUpdateRunnable(handle); ServiceWorkerPrivate* workerPrivate = mUpdateAndInstallInfo->WorkerPrivate(); - rv = workerPrivate->ContinueOnSuccessfulScriptEvaluation(callback); + rv = workerPrivate->CheckScriptEvaluation(callback); if (NS_WARN_IF(NS_FAILED(rv))) { - return FailScopeUpdate(swm, scopeKey); + Fail(NS_ERROR_DOM_ABORT_ERR); } } +private: + // This will perform steps 27 and 28 from [[Update]] + // Remove the job from the registration queue and invoke [[Install]] void - FailScopeUpdate(ServiceWorkerManager* aSwm, const nsACString& aScopeKey) - { - AssertIsOnMainThread(); - MOZ_ASSERT(aSwm); - ServiceWorkerManager::RegistrationDataPerPrincipal* data; - if (aSwm->mRegistrationInfos.Get(aScopeKey, &data)) { - data->mSetOfScopesBeingUpdated.Remove(aScopeKey); - } - Fail(NS_ERROR_DOM_ABORT_ERR); - } - - // This MUST only be called when the job is still performing actions related - // to registration or update. After the spec resolves the update promise, use - // Done() with the failure code instead. - // Callers MUST hold a strong ref before calling this! - void - Fail(ErrorResult& aRv) + ContinueInstall(bool aScriptEvaluationResult) { AssertIsOnMainThread(); - MOZ_ASSERT(mCallbacks.Length()); - - // With cancellation support, we may only be running with one reference - // from another object like a stream loader or something. - RefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this; - - // Save off the plain error code to pass to Done() where its logged to - // stderr as a warning. - nsresult origStatus = static_cast<nsresult>(aRv.ErrorCodeAsInt()); - - // Ensure that we only surface SecurityErr or TypeErr to script. - if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) && - !aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR)) { - - // Remove the old error code so we can replace it with a TypeError. - aRv.SuppressException(); - - // Depending on how the job was created and where we are in the - // state machine the spec and scope may be stored in different ways. - // Extract the current scope and script spec. - nsString scriptSpec; - nsString scope; - if (mRegistration) { - CopyUTF8toUTF16(mRegistration->mScriptSpec, scriptSpec); - CopyUTF8toUTF16(mRegistration->mScope, scope); - } else { - CopyUTF8toUTF16(mScriptSpec, scriptSpec); - CopyUTF8toUTF16(mScope, scope); - } - - // Throw the type error with a generic error message. - aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope); - } - - for (uint32_t i = 1; i < mCallbacks.Length(); ++i) { - ErrorResult rv; - aRv.CloneTo(rv); - mCallbacks[i]->UpdateFailed(rv); - rv.SuppressException(); - } - - mCallbacks[0]->UpdateFailed(aRv); - - // In case the callback does not consume the exception - aRv.SuppressException(); - - mUpdateAndInstallInfo = nullptr; - if (mRegistration->mInstallingWorker) { - nsresult rv = serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal, - mRegistration->mInstallingWorker->CacheName()); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to purge the installing worker cache."); - } - } - - RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); - swm->MaybeRemoveRegistration(mRegistration); - // Ensures that the job can't do anything useful from this point on. - mRegistration->mUpdateJob = nullptr; - mRegistration = nullptr; - Done(origStatus); - } - - void - Fail(nsresult aRv) - { - ErrorResult rv(aRv); - Fail(rv); - } - - // Public so our error handling code can continue with a successful worker. - void - ContinueInstall() - { - // mRegistration will be null if we have already Fail()ed. - if (!mRegistration) { - return; - } - - // Even if we are canceled, ensure integrity of mSetOfScopesBeingUpdated - // first. - RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); - - nsAutoCString scopeKey; - nsresult rv = swm->PrincipalToScopeKey(mRegistration->mPrincipal, scopeKey); - if (NS_WARN_IF(NS_FAILED(rv))) { - return Fail(NS_ERROR_FAILURE); - } - - ServiceWorkerManager::RegistrationDataPerPrincipal* data; - if (!swm->mRegistrationInfos.Get(scopeKey, &data)) { - return Fail(NS_ERROR_FAILURE); - } - - MOZ_ASSERT(data->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope)); - data->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope); - // This is effectively the end of Step 4.3 of the [[Update]] algorithm. - // The invocation of [[Install]] is not part of the atomic block. + MOZ_ASSERT(mRegistration); + mRegistration->mUpdating = false; RefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this; if (mCanceled) { return Fail(NS_ERROR_DOM_ABORT_ERR); } - // Begin [[Install]] atomic step 4. - if (mRegistration->mInstallingWorker) { - mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant); - mRegistration->mInstallingWorker->WorkerPrivate()->TerminateWorker(); + if (NS_WARN_IF(!aScriptEvaluationResult)) { + ErrorResult error; + + NS_ConvertUTF8toUTF16 scriptSpec(mRegistration->mScriptSpec); + NS_ConvertUTF8toUTF16 scope(mRegistration->mScope); + error.ThrowTypeError<MSG_SW_SCRIPT_THREW>(scriptSpec, scope); + return Fail(error); } - swm->InvalidateServiceWorkerRegistrationWorker(mRegistration, - WhichServiceWorker::INSTALLING_WORKER); - - mRegistration->mInstallingWorker = mUpdateAndInstallInfo.forget(); - mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing); - mRegistration->NotifyListenersOnChange(); - - Succeed(); - // The job should NOT call fail from this point on. - - // Step 4.6 "Queue a task..." for updatefound. - nsCOMPtr<nsIRunnable> upr = - NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>( - swm, - &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations, - mRegistration); - - NS_DispatchToMainThread(upr); - - // Call ContinueAfterInstallEvent(false) on main thread if the SW - // script fails to load. - nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs<bool> - (this, &ServiceWorkerRegisterJob::ContinueAfterInstallEvent, false); - - nsMainThreadPtrHandle<ContinueLifecycleTask> installTask( - new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this))); - RefPtr<LifeCycleEventCallback> callback = new ContinueLifecycleRunnable(installTask); - - // This triggers Step 4.7 "Queue a task to run the following substeps..." - // which sends the install event to the worker. - ServiceWorkerPrivate* workerPrivate = - mRegistration->mInstallingWorker->WorkerPrivate(); - rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("install"), - callback, failRunnable); - - if (NS_WARN_IF(NS_FAILED(rv))) { - ContinueAfterInstallEvent(false /* aSuccess */); - } - } - -private: + RefPtr<ServiceWorkerInstallJob> job = + new ServiceWorkerInstallJob(mQueue, mCallback, + mRegistration, mUpdateAndInstallInfo); + mQueue->Append(job); + Done(NS_OK); + } + void Update() { + AssertIsOnMainThread(); + // Since Update() is called synchronously from Start(), we can assert this. MOZ_ASSERT(!mCanceled); MOZ_ASSERT(mRegistration); nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &ServiceWorkerRegisterJob::ContinueUpdate); NS_DispatchToMainThread(r); + + mRegistration->mUpdating = true; } // Aspects of (actually the whole algorithm) of [[Update]] after // "Run the following steps in parallel." void ContinueUpdate() { AssertIsOnMainThread(); RefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this; if (mCanceled) { return Fail(NS_ERROR_DOM_ABORT_ERR); } if (mRegistration->mInstallingWorker) { mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant); - // This will terminate the installing worker thread. + mRegistration->mInstallingWorker->WorkerPrivate()->TerminateWorker(); mRegistration->mInstallingWorker = nullptr; } RefPtr<ServiceWorkerInfo> workerInfo = mRegistration->Newest(); nsAutoString cacheName; // 9.2.20 If newestWorker is not null, and newestWorker's script url is // equal to registration's registering script url and response is a @@ -1315,123 +1425,67 @@ private: NS_ConvertUTF8toUTF16(mRegistration->mScriptSpec), this, mLoadGroup); if (NS_WARN_IF(NS_FAILED(rv))) { return Fail(rv); } } void - Succeed() + Done(nsresult aStatus) { AssertIsOnMainThread(); - MOZ_ASSERT(mCallbacks.Length()); - - for (uint32_t i = 0; i < mCallbacks.Length(); ++i) { - mCallbacks[i]->UpdateSucceeded(mRegistration); - } - mCallbacks.Clear(); - } - - void - ContinueAfterInstallEvent(bool aInstallEventSuccess) - { - if (mCanceled) { - return Done(NS_ERROR_DOM_ABORT_ERR); - } - - if (!mRegistration->mInstallingWorker) { - NS_WARNING("mInstallingWorker was null."); - return Done(NS_ERROR_DOM_ABORT_ERR); - } - - RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); - - // "If installFailed is true" - if (NS_WARN_IF(!aInstallEventSuccess)) { - mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant); - mRegistration->mInstallingWorker = nullptr; - swm->InvalidateServiceWorkerRegistrationWorker(mRegistration, - WhichServiceWorker::INSTALLING_WORKER); - swm->MaybeRemoveRegistration(mRegistration); - return Done(NS_ERROR_DOM_ABORT_ERR); + + if (mRegistration) { + mRegistration->mUpdating = false; } - // "If registration's waiting worker is not null" - if (mRegistration->mWaitingWorker) { - mRegistration->mWaitingWorker->WorkerPrivate()->TerminateWorker(); - mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant); - - nsresult rv = serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal, - mRegistration->mWaitingWorker->CacheName()); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to purge the old waiting cache."); - } - } - - mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget(); - mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed); - mRegistration->NotifyListenersOnChange(); - swm->InvalidateServiceWorkerRegistrationWorker(mRegistration, - WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER); - - // "If registration's waiting worker's skip waiting flag is set" - if (mRegistration->mWaitingWorker->SkipWaitingFlag()) { - mRegistration->PurgeActiveWorker(); - } - - Done(NS_OK); - // Activate() is invoked out of band of atomic. - mRegistration->TryToActivate(); - } - - void - Done(nsresult aStatus) - { ServiceWorkerJob::Done(aStatus); - - if (mJobType == UPDATE_JOB && mRegistration) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mRegistration->mUpdateJob); - mRegistration->mUpdateJob = nullptr; - } } }; NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerRegisterJob, ServiceWorkerJob); void ServiceWorkerJobQueue::CancelJobs() { - if (mJobs.IsEmpty()) { + // The order doesn't matter. Cancel() just sets a flag on these jobs. + CancelJobs(mRegistrationJobQueue); + CancelJobs(mInstallationJobQueue); +} + +void +ServiceWorkerJobQueue::CancelJobs(QueueData& aQueue) +{ + if (aQueue.mJobs.IsEmpty()) { return; } // We have to treat the first job specially. It is the running job and needs // to be notified correctly. - RefPtr<ServiceWorkerJob> runningJob = mJobs[0]; + RefPtr<ServiceWorkerJob> runningJob = aQueue.mJobs[0]; // We can just let an Unregister job run to completion. - if (runningJob->IsRegisterJob()) { - ServiceWorkerRegisterJob* job = static_cast<ServiceWorkerRegisterJob*>(runningJob.get()); + if (runningJob->IsRegisterOrInstallJob()) { + ServiceWorkerJobBase* job = static_cast<ServiceWorkerJobBase*>(runningJob.get()); job->Cancel(); } // Get rid of everything. Non-main thread objects may still be holding a ref // to the running register job. Since we called Cancel() on it, the job's // main thread functions will just exit. - mJobs.Clear(); + aQueue.mJobs.Clear(); } NS_IMETHODIMP ContinueUpdateRunnable::Run() { AssertIsOnMainThread(); RefPtr<ServiceWorkerJob> job = static_cast<ServiceWorkerJob*>(mJob.get()); RefPtr<ServiceWorkerRegisterJob> upjob = static_cast<ServiceWorkerRegisterJob*>(job.get()); - upjob->ContinueInstall(); + upjob->ContinueInstall(mScriptEvaluationResult); return NS_OK; } void ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess) { // This does not start the job immediately if there are other jobs in the // queue, which captures the "atomic" behaviour we want. @@ -1800,17 +1854,17 @@ public: rv = principal->CheckMayLoad(scopeURI, true /* report */, false /* allowIfInheritsPrincipal */); if (NS_WARN_IF(NS_FAILED(rv))) { continue; } RefPtr<ServiceWorkerRegistrationMainThread> swr = - new ServiceWorkerRegistrationMainThread(mWindow, scope); + mWindow->GetServiceWorkerRegistration(scope); array.AppendElement(swr); } mPromise->MaybeResolve(array); return NS_OK; } }; @@ -1907,17 +1961,17 @@ public: if (!registration) { mPromise->MaybeResolve(JS::UndefinedHandleValue); return NS_OK; } NS_ConvertUTF8toUTF16 scope(registration->mScope); RefPtr<ServiceWorkerRegistrationMainThread> swr = - new ServiceWorkerRegistrationMainThread(mWindow, scope); + mWindow->GetServiceWorkerRegistration(scope); mPromise->MaybeResolve(swr); return NS_OK; } }; // If we return an error code here, the ServiceWorkerContainer will // automatically reject the Promise. @@ -2169,17 +2223,17 @@ ServiceWorkerManager::CheckReadyPromise( MOZ_ASSERT(principal); RefPtr<ServiceWorkerRegistrationInfo> registration = GetServiceWorkerRegistrationInfo(principal, aURI); if (registration && registration->mActiveWorker) { NS_ConvertUTF8toUTF16 scope(registration->mScope); RefPtr<ServiceWorkerRegistrationMainThread> swr = - new ServiceWorkerRegistrationMainThread(aWindow, scope); + aWindow->GetServiceWorkerRegistration(scope); aPromise->MaybeResolve(swr); return true; } return false; } ServiceWorkerInfo* @@ -2227,17 +2281,17 @@ class ServiceWorkerUnregisterJob final : ~ServiceWorkerUnregisterJob() {} public: ServiceWorkerUnregisterJob(ServiceWorkerJobQueue* aQueue, const nsACString& aScope, nsIServiceWorkerUnregisterCallback* aCallback, nsIPrincipal* aPrincipal) - : ServiceWorkerJob(aQueue) + : ServiceWorkerJob(aQueue, Type::UnregisterJob) , mScope(aScope) , mCallback(aCallback) , mPrincipal(aPrincipal) { AssertIsOnMainThread(); } void @@ -2582,38 +2636,16 @@ ServiceWorkerManager::HandleError(JSCont return; } ServiceWorkerManager::RegistrationDataPerPrincipal* data; if (NS_WARN_IF(!mRegistrationInfos.Get(scopeKey, &data))) { return; } - // If this is a failure, we may need to cancel an in-progress registration. - if (!JSREPORT_IS_WARNING(aFlags) && - data->mSetOfScopesBeingUpdated.Contains(aScope)) { - - data->mSetOfScopesBeingUpdated.Remove(aScope); - - ServiceWorkerJobQueue* queue = data->mJobQueues.Get(aScope); - MOZ_ASSERT(queue); - - ServiceWorkerJob* job = queue->Peek(); - if (job) { - MOZ_ASSERT(job->IsRegisterJob()); - RefPtr<ServiceWorkerRegisterJob> regJob = - static_cast<ServiceWorkerRegisterJob*>(job); - - ErrorResult rv; - NS_ConvertUTF8toUTF16 scope(aScope); - rv.ThrowTypeError<MSG_SW_SCRIPT_THREW>(aWorkerURL, scope); - regJob->Fail(rv); - } - } - // Always report any uncaught exceptions or errors to the console of // each client. ReportToAllClients(aScope, aMessage, aFilename, aLine, aLineNumber, aColumnNumber, aFlags); } void ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess) @@ -2659,33 +2691,16 @@ void ServiceWorkerRegistrationInfo::NotifyListenersOnChange() { nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> listeners(mListeners); for (size_t index = 0; index < listeners.Length(); ++index) { listeners[index]->OnChange(); } } -bool -ServiceWorkerRegistrationInfo::IsUpdating() const -{ - MOZ_ASSERT(NS_IsMainThread()); - return mUpdateJob != nullptr; -} - -void -ServiceWorkerRegistrationInfo::AppendUpdateCallback(ServiceWorkerUpdateFinishCallback* aCallback) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aCallback); - MOZ_ASSERT(mUpdateJob); - - mUpdateJob->AppendCallback(aCallback); -} - void ServiceWorkerManager::LoadRegistration( const ServiceWorkerRegistrationData& aRegistration) { AssertIsOnMainThread(); nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(aRegistration.principal()); @@ -3010,16 +3025,17 @@ ServiceWorkerManager::RemoveScopeAndRegi RefPtr<ServiceWorkerRegistrationInfo> info; data->mInfos.Get(aRegistration->mScope, getter_AddRefs(info)); data->mInfos.Remove(aRegistration->mScope); data->mOrderedScopes.RemoveElement(aRegistration->mScope); swm->NotifyListenersOnUnregister(info); swm->MaybeRemoveRegistrationInfo(scopeKey); + swm->NotifyServiceWorkerRegistrationRemoved(aRegistration); } void ServiceWorkerManager::MaybeRemoveRegistrationInfo(const nsACString& aScopeKey) { RegistrationDataPerPrincipal* data; if (!mRegistrationInfos.Get(aScopeKey, &data)) { return; @@ -3522,65 +3538,44 @@ ServiceWorkerManager::InvalidateServiceW if (utf8Scope.Equals(aRegistration->mScope)) { target->InvalidateWorkers(aWhichOnes); } } } void -ServiceWorkerManager::SoftUpdate(nsIPrincipal* aPrincipal, - const nsACString& aScope, - ServiceWorkerUpdateFinishCallback* aCallback) +ServiceWorkerManager::NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration) { - MOZ_ASSERT(aPrincipal); - - nsAutoCString scopeKey; - nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - - SoftUpdate(scopeKey, aScope, aCallback); + AssertIsOnMainThread(); + nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners); + while (it.HasMore()) { + RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext(); + nsAutoString regScope; + target->GetScope(regScope); + MOZ_ASSERT(!regScope.IsEmpty()); + + NS_ConvertUTF16toUTF8 utf8Scope(regScope); + + if (utf8Scope.Equals(aRegistration->mScope)) { + target->RegistrationRemoved(); + } + } } void -ServiceWorkerManager::SoftUpdate(const PrincipalOriginAttributes& aOriginAttributes, - const nsACString& aScope, - ServiceWorkerUpdateFinishCallback* aCallback) +ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes, + const nsACString& aScope) { + AssertIsOnMainThread(); nsAutoCString scopeKey; aOriginAttributes.CreateSuffix(scopeKey); - SoftUpdate(scopeKey, aScope, aCallback); -} - -namespace { - -// Empty callback. Only use when you really want to ignore errors. -class EmptyUpdateFinishCallback final : public ServiceWorkerUpdateFinishCallback -{ -public: - void - UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override - {} - - void - UpdateFailed(ErrorResult& aStatus) override - {} -}; - -} // anonymous namespace - -void -ServiceWorkerManager::SoftUpdate(const nsACString& aScopeKey, - const nsACString& aScope, - ServiceWorkerUpdateFinishCallback* aCallback) -{ + RefPtr<ServiceWorkerRegistrationInfo> registration = - GetRegistration(aScopeKey, aScope); + GetRegistration(scopeKey, aScope); if (NS_WARN_IF(!registration)) { return; } // "If registration's uninstalling flag is set, abort these steps." if (registration->mPendingUninstall) { return; } @@ -3596,38 +3591,81 @@ ServiceWorkerManager::SoftUpdate(const n RefPtr<ServiceWorkerInfo> newest = registration->Newest(); if (!newest) { return; } // "Set registration's registering script url to newestWorker's script url." registration->mScriptSpec = newest->ScriptSpec(); + // "If the registration queue for registration is empty, invoke Update algorithm, + // or its equivalent, with client, registration as its argument." + // TODO(catalinb): We don't implement the force bypass cache flag. + // See: https://github.com/slightlyoff/ServiceWorker/issues/759 + if (!registration->mUpdating) { + ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(scopeKey, aScope); + MOZ_ASSERT(queue); + + RefPtr<ServiceWorkerRegisterJob> job = + new ServiceWorkerRegisterJob(queue, registration, nullptr); + queue->Append(job); + } +} + +void +ServiceWorkerManager::Update(nsIPrincipal* aPrincipal, + const nsACString& aScope, + ServiceWorkerUpdateFinishCallback* aCallback) +{ + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aCallback); + + nsAutoCString scopeKey; + nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + RefPtr<ServiceWorkerRegistrationInfo> registration = + GetRegistration(scopeKey, aScope); + if (NS_WARN_IF(!registration)) { + return; + } + + // "If registration's uninstalling flag is set, abort these steps." + if (registration->mPendingUninstall) { + return; + } + + // "Let newestWorker be the result of running Get Newest Worker algorithm + // passing registration as its argument. + // If newestWorker is null, return a promise rejected with "InvalidStateError" + RefPtr<ServiceWorkerInfo> newest = registration->Newest(); + if (!newest) { + ErrorResult error(NS_ERROR_DOM_INVALID_STATE_ERR); + aCallback->UpdateFailed(error); + + // In case the callback does not consume the exception + error.SuppressException(); + + return; + } + + // "Set registration's registering script url to newestWorker's script url." + registration->mScriptSpec = newest->ScriptSpec(); + ServiceWorkerJobQueue* queue = - GetOrCreateJobQueue(aScopeKey, aScope); + GetOrCreateJobQueue(scopeKey, aScope); MOZ_ASSERT(queue); - RefPtr<ServiceWorkerUpdateFinishCallback> cb(aCallback); - if (!cb) { - cb = new EmptyUpdateFinishCallback(); - } - // "Invoke Update algorithm, or its equivalent, with client, registration as // its argument." - if (registration->IsUpdating()) { - // This is used to reduce burst of update events. If there is an update - // job in queue when we try to create a new one, drop current one and - // merge the callback function to existing update job. - // See. https://github.com/slightlyoff/ServiceWorker/issues/759 - registration->AppendUpdateCallback(cb); - } else { - RefPtr<ServiceWorkerRegisterJob> job = - new ServiceWorkerRegisterJob(queue, registration, cb); - queue->Append(job); - } + RefPtr<ServiceWorkerRegisterJob> job = + new ServiceWorkerRegisterJob(queue, registration, aCallback); + queue->Append(job); } namespace { static void FireControllerChangeOnDocument(nsIDocument* aDocument) { AssertIsOnMainThread(); @@ -3753,30 +3791,31 @@ ServiceWorkerManager::ClaimClients(nsIPr nsresult ServiceWorkerManager::SetSkipWaitingFlag(nsIPrincipal* aPrincipal, const nsCString& aScope, uint64_t aServiceWorkerID) { RefPtr<ServiceWorkerRegistrationInfo> registration = GetRegistration(aPrincipal, aScope); - if (!registration) { + if (NS_WARN_IF(!registration)) { return NS_ERROR_FAILURE; } if (registration->mInstallingWorker && (registration->mInstallingWorker->ID() == aServiceWorkerID)) { registration->mInstallingWorker->SetSkipWaitingFlag(); } else if (registration->mWaitingWorker && (registration->mWaitingWorker->ID() == aServiceWorkerID)) { registration->mWaitingWorker->SetSkipWaitingFlag(); if (registration->mWaitingWorker->State() == ServiceWorkerState::Installed) { registration->TryToActivate(); } } else { + NS_WARNING("Failed to set skipWaiting flag, no matching worker."); return NS_ERROR_FAILURE; } return NS_OK; } void ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration) @@ -4528,16 +4567,38 @@ ServiceWorkerInfo::GetScriptSpec(nsAStri NS_IMETHODIMP ServiceWorkerInfo::GetCacheName(nsAString& aCacheName) { AssertIsOnMainThread(); aCacheName = mCacheName; return NS_OK; } +NS_IMETHODIMP +ServiceWorkerInfo::GetDebugger(nsIWorkerDebugger** aResult) +{ + if (NS_WARN_IF(!aResult)) { + return NS_ERROR_FAILURE; + } + + return mServiceWorkerPrivate->GetDebugger(aResult); +} + +NS_IMETHODIMP +ServiceWorkerInfo::AttachDebugger() +{ + return mServiceWorkerPrivate->AttachDebugger(); +} + +NS_IMETHODIMP +ServiceWorkerInfo::DetachDebugger() +{ + return mServiceWorkerPrivate->DetachDebugger(); +} + void ServiceWorkerInfo::AppendWorker(ServiceWorker* aWorker) { MOZ_ASSERT(aWorker); #ifdef DEBUG nsAutoString workerURL; aWorker->GetScriptURL(workerURL); MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec)));
--- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -42,21 +42,19 @@ namespace dom { class ServiceWorkerRegistrationListener; namespace workers { class ServiceWorker; class ServiceWorkerClientInfo; class ServiceWorkerInfo; class ServiceWorkerJob; -class ServiceWorkerRegisterJob; class ServiceWorkerJobQueue; class ServiceWorkerManagerChild; class ServiceWorkerPrivate; -class ServiceWorkerUpdateFinishCallback; class ServiceWorkerRegistrationInfo final : public nsIServiceWorkerRegistrationInfo { uint32_t mControlledDocumentsCounter; virtual ~ServiceWorkerRegistrationInfo(); @@ -74,17 +72,21 @@ public: RefPtr<ServiceWorkerInfo> mActiveWorker; RefPtr<ServiceWorkerInfo> mWaitingWorker; RefPtr<ServiceWorkerInfo> mInstallingWorker; nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> mListeners; uint64_t mLastUpdateCheckTime; - RefPtr<ServiceWorkerRegisterJob> mUpdateJob; + // According to the spec, Soft Update shouldn't queue an update job + // if the registration queue is not empty. Because our job queue + // works slightly different, we use a flag to determine if the registration + // is already updating. + bool mUpdating; // When unregister() is called on a registration, it is not immediately // removed since documents may be controlled. It is marked as // pendingUninstall and when all controlling documents go away, removed. bool mPendingUninstall; ServiceWorkerRegistrationInfo(const nsACString& aScope, nsIPrincipal* aPrincipal); @@ -144,22 +146,16 @@ public: void RefreshLastUpdateCheckTime(); bool IsLastUpdateCheckTimeOverOneDay() const; void NotifyListenersOnChange(); - - bool - IsUpdating() const; - - void - AppendUpdateCallback(ServiceWorkerUpdateFinishCallback* aCallback); }; class ServiceWorkerUpdateFinishCallback { protected: virtual ~ServiceWorkerUpdateFinishCallback() {} @@ -311,17 +307,19 @@ class ServiceWorkerManager final : public nsIServiceWorkerManager , public nsIIPCBackgroundChildCreateCallback , public nsIObserver { friend class GetReadyPromiseRunnable; friend class GetRegistrationsRunnable; friend class GetRegistrationRunnable; friend class ServiceWorkerJobQueue; + friend class ServiceWorkerInstallJob; friend class ServiceWorkerRegisterJob; + friend class ServiceWorkerJobBase; friend class ServiceWorkerRegistrationInfo; friend class ServiceWorkerUnregisterJob; public: NS_DECL_ISUPPORTS NS_DECL_NSISERVICEWORKERMANAGER NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK NS_DECL_NSIOBSERVER @@ -368,24 +366,23 @@ public: ErrorResult& aRv); void DispatchPreparedFetchEvent(nsIInterceptedChannel* aChannel, nsIRunnable* aPreparedRunnable, ErrorResult& aRv); void - SoftUpdate(nsIPrincipal* aPrincipal, - const nsACString& aScope, - ServiceWorkerUpdateFinishCallback* aCallback = nullptr); + Update(nsIPrincipal* aPrincipal, + const nsACString& aScope, + ServiceWorkerUpdateFinishCallback* aCallback); void - SoftUpdate(const PrincipalOriginAttributes& aOriginAttributes, - const nsACString& aScope, - ServiceWorkerUpdateFinishCallback* aCallback = nullptr); + SoftUpdate(const OriginAttributes& aOriginAttributes, + const nsACString& aScope); void PropagateSoftUpdate(const PrincipalOriginAttributes& aOriginAttributes, const nsAString& aScope); void PropagateRemove(const nsACString& aHost); @@ -484,21 +481,16 @@ private: ServiceWorkerJobQueue* GetOrCreateJobQueue(const nsACString& aOriginSuffix, const nsACString& aScope); void MaybeRemoveRegistrationInfo(const nsACString& aScopeKey); - void - SoftUpdate(const nsACString& aScopeKey, - const nsACString& aScope, - ServiceWorkerUpdateFinishCallback* aCallback = nullptr); - already_AddRefed<ServiceWorkerRegistrationInfo> GetRegistration(const nsACString& aScopeKey, const nsACString& aScope) const; void AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration); nsresult @@ -521,16 +513,19 @@ private: ServiceWorkerInfo* GetActiveWorkerInfoForDocument(nsIDocument* aDocument); void InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration, WhichServiceWorker aWhichOnes); void + NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration); + + void StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration, nsIDocument* aDoc); void StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration); already_AddRefed<ServiceWorkerRegistrationInfo> GetServiceWorkerRegistrationInfo(nsPIDOMWindow* aWindow);
--- a/dom/workers/ServiceWorkerManagerChild.cpp +++ b/dom/workers/ServiceWorkerManagerChild.cpp @@ -37,17 +37,17 @@ ServiceWorkerManagerChild::RecvNotifySof { if (mShuttingDown) { return true; } RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); MOZ_ASSERT(swm); - swm->SoftUpdate(aOriginAttributes, NS_ConvertUTF16toUTF8(aScope), nullptr); + swm->SoftUpdate(aOriginAttributes, NS_ConvertUTF16toUTF8(aScope)); return true; } bool ServiceWorkerManagerChild::RecvNotifyUnregister(const PrincipalInfo& aPrincipalInfo, const nsString& aScope) { if (mShuttingDown) {
--- a/dom/workers/ServiceWorkerPrivate.cpp +++ b/dom/workers/ServiceWorkerPrivate.cpp @@ -56,16 +56,17 @@ private: RefPtr<ServiceWorkerPrivate> mPrivate; }; NS_IMPL_ISUPPORTS0(KeepAliveToken) ServiceWorkerPrivate::ServiceWorkerPrivate(ServiceWorkerInfo* aInfo) : mInfo(aInfo) , mIsPushWorker(false) + , mDebuggerCount(0) , mTokenCount(0) { AssertIsOnMainThread(); MOZ_ASSERT(aInfo); mIdleWorkerTimer = do_CreateInstance(NS_TIMER_CONTRACTID); MOZ_ASSERT(mIdleWorkerTimer); } @@ -98,56 +99,74 @@ ServiceWorkerPrivate::SendMessageEvent(J return rv.StealNSResult(); } namespace { class CheckScriptEvaluationWithCallback final : public WorkerRunnable { nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken; - RefPtr<nsRunnable> mCallback; + RefPtr<LifeCycleEventCallback> mCallback; + DebugOnly<bool> mDone; public: CheckScriptEvaluationWithCallback(WorkerPrivate* aWorkerPrivate, KeepAliveToken* aKeepAliveToken, - nsRunnable* aCallback) + LifeCycleEventCallback* aCallback) : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) , mKeepAliveToken(new nsMainThreadPtrHolder<KeepAliveToken>(aKeepAliveToken)) , mCallback(aCallback) + , mDone(false) { AssertIsOnMainThread(); } + ~CheckScriptEvaluationWithCallback() + { + MOZ_ASSERT(mDone); + } + bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { aWorkerPrivate->AssertIsOnWorkerThread(); - if (aWorkerPrivate->WorkerScriptExecutedSuccessfully()) { - nsresult rv = NS_DispatchToMainThread(mCallback); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch CheckScriptEvaluation callback."); - } - } + Done(aWorkerPrivate->WorkerScriptExecutedSuccessfully()); return true; } + + NS_IMETHOD + Cancel() override + { + Done(false); + return WorkerRunnable::Cancel(); + } + +private: + void + Done(bool aResult) + { + mDone = true; + mCallback->SetResult(aResult); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(mCallback))); + } }; } // anonymous namespace nsresult -ServiceWorkerPrivate::ContinueOnSuccessfulScriptEvaluation(nsRunnable* aCallback) +ServiceWorkerPrivate::CheckScriptEvaluation(LifeCycleEventCallback* aCallback) { nsresult rv = SpawnWorkerIfNeeded(LifeCycleEvent, nullptr); NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(mKeepAliveToken); RefPtr<WorkerRunnable> r = new CheckScriptEvaluationWithCallback(mWorkerPrivate, - mKeepAliveToken, - aCallback); + mKeepAliveToken, + aCallback); AutoJSAPI jsapi; jsapi.Init(); if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) { return NS_ERROR_FAILURE; } return NS_OK; } @@ -347,78 +366,151 @@ public: bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { MOZ_ASSERT(aWorkerPrivate); return DispatchLifecycleEvent(aCx, aWorkerPrivate); } + NS_IMETHOD + Cancel() override + { + mCallback->SetResult(false); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(mCallback))); + + return WorkerRunnable::Cancel(); + } + private: bool DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate); }; /* - * Used to handle ExtendableEvent::waitUntil() and proceed with - * installation/activation. + * Used to handle ExtendableEvent::waitUntil() and catch abnormal worker + * termination during the execution of life cycle events. It is responsible + * with advancing the job queue for install/activate tasks. */ -class LifecycleEventPromiseHandler final : public PromiseNativeHandler +class LifeCycleEventWatcher final : public PromiseNativeHandler, + public WorkerFeature { + WorkerPrivate* mWorkerPrivate; RefPtr<LifeCycleEventCallback> mCallback; + bool mDone; - virtual - ~LifecycleEventPromiseHandler() - { } + ~LifeCycleEventWatcher() + { + if (mDone) { + return; + } + + MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate); + // XXXcatalinb: If all the promises passed to waitUntil go out of scope, + // the resulting Promise.all will be cycle collected and it will drop its + // native handlers (including this object). Instead of waiting for a timeout + // we report the failure now. + JSContext* cx = mWorkerPrivate->GetJSContext(); + ReportResult(cx, false); + } public: NS_DECL_ISUPPORTS - explicit LifecycleEventPromiseHandler(LifeCycleEventCallback* aCallback) - : mCallback(aCallback) + LifeCycleEventWatcher(WorkerPrivate* aWorkerPrivate, + LifeCycleEventCallback* aCallback) + : mWorkerPrivate(aWorkerPrivate) + , mCallback(aCallback) + , mDone(false) + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + } + + bool + Init() { - MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(mWorkerPrivate); + mWorkerPrivate->AssertIsOnWorkerThread(); + JSContext* cx = mWorkerPrivate->GetJSContext(); + + // We need to listen for worker termination in case the event handler + // never completes or never resolves the waitUntil promise. There are + // two possible scenarios: + // 1. The keepAlive token expires and the worker is terminated, in which + // case the registration/update promise will be rejected + // 2. A new service worker is registered which will terminate the current + // installing worker. + if (NS_WARN_IF(!mWorkerPrivate->AddFeature(cx, this))) { + NS_WARNING("LifeCycleEventWatcher failed to add feature."); + ReportResult(cx, false); + return false; + } + + return true; + } + + bool + Notify(JSContext* aCx, Status aStatus) override + { + if (aStatus < Terminating) { + return true; + } + + MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate); + ReportResult(aCx, false); + + return true; + } + + void + ReportResult(JSContext* aCx, bool aResult) + { + mWorkerPrivate->AssertIsOnWorkerThread(); + + if (mDone) { + return; + } + mDone = true; + + mCallback->SetResult(aResult); + nsresult rv = NS_DispatchToMainThread(mCallback); + if (NS_WARN_IF(NS_FAILED(rv))) { + NS_RUNTIMEABORT("Failed to dispatch life cycle event handler."); + } + + mWorkerPrivate->RemoveFeature(aCx, this); } void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override { - WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(workerPrivate); - workerPrivate->AssertIsOnWorkerThread(); + MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate); + mWorkerPrivate->AssertIsOnWorkerThread(); - mCallback->SetResult(true); - nsresult rv = NS_DispatchToMainThread(mCallback); - if (NS_WARN_IF(NS_FAILED(rv))) { - NS_RUNTIMEABORT("Failed to dispatch life cycle event handler."); - } + ReportResult(aCx, true); } void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override { - WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(workerPrivate); - workerPrivate->AssertIsOnWorkerThread(); + MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate); + mWorkerPrivate->AssertIsOnWorkerThread(); - mCallback->SetResult(false); - nsresult rv = NS_DispatchToMainThread(mCallback); - if (NS_WARN_IF(NS_FAILED(rv))) { - NS_RUNTIMEABORT("Failed to dispatch life cycle event handler."); - } + ReportResult(aCx, false); // Note, all WaitUntil() rejections are reported to client consoles // by the WaitUntilHandler in ServiceWorkerEvents. This ensures that // errors in non-lifecycle events like FetchEvent and PushEvent are // reported properly. } }; -NS_IMPL_ISUPPORTS0(LifecycleEventPromiseHandler) +NS_IMPL_ISUPPORTS0(LifeCycleEventWatcher) bool LifecycleEventWorkerRunnable::DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate) { aWorkerPrivate->AssertIsOnWorkerThread(); MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); @@ -431,26 +523,33 @@ LifecycleEventWorkerRunnable::DispatchLi init.mCancelable = false; event = ExtendableEvent::Constructor(target, mEventName, init); } else { MOZ_CRASH("Unexpected lifecycle event"); } event->SetTrusted(true); + // It is important to initialize the watcher before actually dispatching + // the event in order to catch worker termination while the event handler + // is still executing. This can happen with infinite loops, for example. + RefPtr<LifeCycleEventWatcher> watcher = + new LifeCycleEventWatcher(aWorkerPrivate, mCallback); + + if (!watcher->Init()) { + return true; + } + RefPtr<Promise> waitUntil; DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event, getter_AddRefs(waitUntil)); if (waitUntil) { - RefPtr<LifecycleEventPromiseHandler> handler = - new LifecycleEventPromiseHandler(mCallback); - waitUntil->AppendNativeHandler(handler); + waitUntil->AppendNativeHandler(watcher); } else { - mCallback->SetResult(false); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(mCallback))); + watcher->ReportResult(aCx, false); } return true; } } // anonymous namespace nsresult @@ -1326,17 +1425,17 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede // XXXcatalinb: We need to have a separate load group that's linked to // an existing tab child to pass security checks on b2g. // This should be fixed in bug 1125961, but for now we enforce updating // the overriden load group when intercepting a fetch. MOZ_ASSERT_IF(aWhy == FetchEvent, aLoadGroup); if (mWorkerPrivate) { mWorkerPrivate->UpdateOverridenLoadGroup(aLoadGroup); - ResetIdleTimeout(aWhy); + RenewKeepAliveToken(aWhy); return NS_OK; } // Sanity check: mSupportsArray should be empty if we're about to // spin up a new worker. MOZ_ASSERT(mSupportsArray.IsEmpty()); @@ -1406,17 +1505,17 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede scriptSpec, false, WorkerTypeService, mInfo->Scope(), &info, error); if (NS_WARN_IF(error.Failed())) { return error.StealNSResult(); } mIsPushWorker = false; - ResetIdleTimeout(aWhy); + RenewKeepAliveToken(aWhy); return NS_OK; } void ServiceWorkerPrivate::StoreISupports(nsISupports* aSupports) { AssertIsOnMainThread(); @@ -1472,17 +1571,17 @@ ServiceWorkerPrivate::NoteDeadServiceWor mInfo = nullptr; TerminateWorker(); } void ServiceWorkerPrivate::NoteStoppedControllingDocuments() { AssertIsOnMainThread(); - if (mIsPushWorker) { + if (mIsPushWorker || mDebuggerCount) { return; } TerminateWorker(); } void ServiceWorkerPrivate::Activated() @@ -1502,16 +1601,78 @@ ServiceWorkerPrivate::Activated() AutoJSAPI jsapi; jsapi.Init(); if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) { NS_WARNING("Failed to dispatch pending functional event!"); } } } +nsresult +ServiceWorkerPrivate::GetDebugger(nsIWorkerDebugger** aResult) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aResult); + + if (!mDebuggerCount) { + return NS_OK; + } + + MOZ_ASSERT(mWorkerPrivate); + + nsCOMPtr<nsIWorkerDebugger> debugger = do_QueryInterface(mWorkerPrivate->Debugger()); + debugger.forget(aResult); + + return NS_OK; +} + +nsresult +ServiceWorkerPrivate::AttachDebugger() +{ + AssertIsOnMainThread(); + + // When the first debugger attaches to a worker, we spawn a worker if needed, + // and cancel the idle timeout. The idle timeout should not be reset until + // the last debugger detached from the worker. + if (!mDebuggerCount) { + nsresult rv = SpawnWorkerIfNeeded(AttachEvent, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + mIdleWorkerTimer->Cancel(); + } + + ++mDebuggerCount; + + return NS_OK; +} + +nsresult +ServiceWorkerPrivate::DetachDebugger() +{ + AssertIsOnMainThread(); + + if (!mDebuggerCount) { + return NS_ERROR_UNEXPECTED; + } + + --mDebuggerCount; + + // When the last debugger detaches from a worker, we either reset the idle + // timeout, or terminate the worker if there are no more active tokens. + if (!mDebuggerCount) { + if (mTokenCount) { + ResetIdleTimeout(); + } else { + TerminateWorker(); + } + } + + return NS_OK; +} + /* static */ void ServiceWorkerPrivate::NoteIdleWorkerCallback(nsITimer* aTimer, void* aPrivate) { AssertIsOnMainThread(); MOZ_ASSERT(aPrivate); RefPtr<ServiceWorkerPrivate> swp = static_cast<ServiceWorkerPrivate*>(aPrivate); @@ -1545,34 +1706,46 @@ ServiceWorkerPrivate::TerminateWorkerCal MOZ_ASSERT(aTimer == serviceWorkerPrivate->mIdleWorkerTimer, "Invalid timer!"); serviceWorkerPrivate->TerminateWorker(); } void -ServiceWorkerPrivate::ResetIdleTimeout(WakeUpReason aWhy) +ServiceWorkerPrivate::RenewKeepAliveToken(WakeUpReason aWhy) { - // We should have an active worker if we're reseting the idle timeout + // We should have an active worker if we're renewing the keep alive token. MOZ_ASSERT(mWorkerPrivate); if (aWhy == PushEvent || aWhy == PushSubscriptionChangeEvent) { mIsPushWorker = true; } + // If there is at least one debugger attached to the worker, the idle worker + // timeout was canceled when the first debugger attached to the worker. It + // should not be reset until the last debugger detaches from the worker. + if (!mDebuggerCount) { + ResetIdleTimeout(); + } + + if (!mKeepAliveToken) { + mKeepAliveToken = new KeepAliveToken(this); + } +} + +void +ServiceWorkerPrivate::ResetIdleTimeout() +{ uint32_t timeout = Preferences::GetInt("dom.serviceWorkers.idle_timeout"); DebugOnly<nsresult> rv = mIdleWorkerTimer->InitWithFuncCallback(ServiceWorkerPrivate::NoteIdleWorkerCallback, this, timeout, nsITimer::TYPE_ONE_SHOT); MOZ_ASSERT(NS_SUCCEEDED(rv)); - if (!mKeepAliveToken) { - mKeepAliveToken = new KeepAliveToken(this); - } } void ServiceWorkerPrivate::AddToken() { AssertIsOnMainThread(); ++mTokenCount; }
--- a/dom/workers/ServiceWorkerPrivate.h +++ b/dom/workers/ServiceWorkerPrivate.h @@ -67,21 +67,19 @@ public: explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo); nsresult SendMessageEvent(JSContext* aCx, JS::Handle<JS::Value> aMessage, const Optional<Sequence<JS::Value>>& aTransferable, UniquePtr<ServiceWorkerClientInfo>&& aClientInfo); // This is used to validate the worker script and continue the installation - // process. Note that the callback is dispatched to the main thread - // ONLY if the evaluation was successful. Failure is handled by the JS - // exception handler which will call ServiceWorkerManager::HandleError. + // process. nsresult - ContinueOnSuccessfulScriptEvaluation(nsRunnable* aCallback); + CheckScriptEvaluation(LifeCycleEventCallback* aCallback); nsresult SendLifeCycleEvent(const nsAString& aEventType, LifeCycleEventCallback* aCallback, nsIRunnable* aLoadFailure); nsresult SendPushEvent(const Maybe<nsTArray<uint8_t>>& aData, @@ -132,35 +130,48 @@ public: GetWorkerPrivate() const { return mWorkerPrivate; } void Activated(); + nsresult + GetDebugger(nsIWorkerDebugger** aResult); + + nsresult + AttachDebugger(); + + nsresult + DetachDebugger(); + private: enum WakeUpReason { FetchEvent = 0, PushEvent, PushSubscriptionChangeEvent, MessageEvent, NotificationClickEvent, - LifeCycleEvent + LifeCycleEvent, + AttachEvent }; // Timer callbacks static void NoteIdleWorkerCallback(nsITimer* aTimer, void* aPrivate); static void TerminateWorkerCallback(nsITimer* aTimer, void *aPrivate); void - ResetIdleTimeout(WakeUpReason aWhy); + RenewKeepAliveToken(WakeUpReason aWhy); + + void + ResetIdleTimeout(); void AddToken(); void ReleaseToken(); // |aLoadFailedRunnable| is a runnable dispatched to the main thread @@ -188,16 +199,18 @@ private: // woken up. The flag is reset to false every time a new WorkerPrivate // is created. bool mIsPushWorker; // We keep a token for |dom.serviceWorkers.idle_timeout| seconds to give the // worker a grace period after each event. RefPtr<KeepAliveToken> mKeepAliveToken; + uint64_t mDebuggerCount; + uint64_t mTokenCount; // Meant for keeping objects alive while handling requests from the worker // on the main thread. Access to this array is provided through // |StoreISupports| and |RemoveISupports|. Note that the array is also // cleared whenever the worker is terminated. nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
--- a/dom/workers/ServiceWorkerRegistration.cpp +++ b/dom/workers/ServiceWorkerRegistration.cpp @@ -245,34 +245,46 @@ ServiceWorkerRegistrationMainThread::Inv if (aWhichOnes & WhichServiceWorker::WAITING_WORKER) { mWaitingWorker = nullptr; } if (aWhichOnes & WhichServiceWorker::ACTIVE_WORKER) { mActiveWorker = nullptr; } + +} + +void +ServiceWorkerRegistrationMainThread::RegistrationRemoved() +{ + // If the registration is being removed completely, remove it from the + // window registration hash table so that a new registration would get a new + // wrapper JS object. + nsCOMPtr<nsPIDOMWindow> window = GetOwner(); + if (window) { + window->InvalidateServiceWorkerRegistration(mScope); + } } namespace { void UpdateInternal(nsIPrincipal* aPrincipal, const nsAString& aScope, ServiceWorkerUpdateFinishCallback* aCallback) { AssertIsOnMainThread(); MOZ_ASSERT(aPrincipal); MOZ_ASSERT(aCallback); RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); MOZ_ASSERT(swm); - // The spec defines ServiceWorkerRegistration.update() exactly as Soft Update. - swm->SoftUpdate(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback); + swm->Update(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback); } class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback { RefPtr<Promise> mPromise; ~MainThreadUpdateCallback() { } @@ -388,24 +400,32 @@ public: {} NS_IMETHOD Run() override { AssertIsOnMainThread(); ErrorResult result; - MutexAutoLock lock(mPromiseProxy->Lock()); - if (mPromiseProxy->CleanedUp()) { - return NS_OK; + nsCOMPtr<nsIPrincipal> principal; + // UpdateInternal may try to reject the promise synchronously leading + // to a deadlock. + { + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { + return NS_OK; + } + + principal = mPromiseProxy->GetWorkerPrivate()->GetPrincipal(); } + MOZ_ASSERT(principal); RefPtr<WorkerThreadUpdateCallback> cb = new WorkerThreadUpdateCallback(mPromiseProxy); - UpdateInternal(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), mScope, cb); + UpdateInternal(principal, mScope, cb); return NS_OK; } private: ~UpdateRunnable() {} RefPtr<PromiseWorkerProxy> mPromiseProxy; @@ -853,16 +873,22 @@ public: void InvalidateWorkers(WhichServiceWorker aWhichOnes) override { AssertIsOnMainThread(); // FIXME(nsm); } void + RegistrationRemoved() override + { + AssertIsOnMainThread(); + } + + void GetScope(nsAString& aScope) const override { aScope = mScope; } ServiceWorkerRegistrationWorkerThread* GetRegistration() const {
--- a/dom/workers/ServiceWorkerRegistration.h +++ b/dom/workers/ServiceWorkerRegistration.h @@ -61,16 +61,19 @@ public: virtual void UpdateFound() = 0; virtual void InvalidateWorkers(WhichServiceWorker aWhichOnes) = 0; virtual void + RegistrationRemoved() = 0; + + virtual void GetScope(nsAString& aScope) const = 0; }; class ServiceWorkerRegistrationBase : public DOMEventTargetHelper { public: NS_DECL_ISUPPORTS_INHERITED @@ -101,19 +104,16 @@ protected: class ServiceWorkerRegistrationMainThread final : public ServiceWorkerRegistrationBase, public ServiceWorkerRegistrationListener { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationMainThread, ServiceWorkerRegistrationBase) - ServiceWorkerRegistrationMainThread(nsPIDOMWindow* aWindow, - const nsAString& aScope); - already_AddRefed<Promise> Update(ErrorResult& aRv); already_AddRefed<Promise> Unregister(ErrorResult& aRv); JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; @@ -150,22 +150,28 @@ public: // ServiceWorkerRegistrationListener void UpdateFound() override; void InvalidateWorkers(WhichServiceWorker aWhichOnes) override; void + RegistrationRemoved() override; + + void GetScope(nsAString& aScope) const override { aScope = mScope; } private: + friend nsPIDOMWindow; + ServiceWorkerRegistrationMainThread(nsPIDOMWindow* aWindow, + const nsAString& aScope); ~ServiceWorkerRegistrationMainThread(); already_AddRefed<workers::ServiceWorker> GetWorkerReference(WhichServiceWorker aWhichOne); void StartListeningForEvents();
--- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -588,17 +588,19 @@ public: swm->SetSkipWaitingFlag(workerPrivate->GetPrincipal(), mScope, workerPrivate->ServiceWorkerID()); RefPtr<SkipWaitingResultRunnable> runnable = new SkipWaitingResultRunnable(workerPrivate, mPromiseProxy); AutoJSAPI jsapi; jsapi.Init(); - runnable->Dispatch(jsapi.cx()); + if (!runnable->Dispatch(jsapi.cx())) { + NS_WARNING("Failed to dispatch SkipWaitingResultRunnable to the worker."); + } return NS_OK; } }; } // namespace already_AddRefed<Promise> ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv)
--- a/dom/workers/test/serviceworkers/chrome.ini +++ b/dom/workers/test/serviceworkers/chrome.ini @@ -1,17 +1,19 @@ [DEFAULT] skip-if = buildapp == 'b2g' || os == 'android' support-files = app/* app2/* chrome_helpers.js + serviceworkerinfo_iframe.html serviceworkermanager_iframe.html serviceworkerregistrationinfo_iframe.html worker.js worker2.js [test_aboutserviceworkers.html] skip-if = true #bug 1193319 [test_app_installation.html] [test_privateBrowsing.html] +[test_serviceworkerinfo.xul] [test_serviceworkermanager.xul] [test_serviceworkerregistrationinfo.xul]
--- a/dom/workers/test/serviceworkers/chrome_helpers.js +++ b/dom/workers/test/serviceworkers/chrome_helpers.js @@ -52,8 +52,23 @@ function waitForServiceWorkerRegistratio callback(); } resolve(callback ? callback() : undefined); } }; registration.addListener(listener); }); } + +function waitForServiceWorkerShutdown() { + return new Promise(function (resolve) { + let observer = { + observe: function (subject, topic, data) { + if (topic !== "service-worker-shutdown") { + return; + } + SpecialPowers.removeObserver(observer, "service-worker-shutdown"); + resolve(); + } + }; + SpecialPowers.addObserver(observer, "service-worker-shutdown", false); + }); +}
new file mode 100644 --- /dev/null +++ b/dom/workers/test/serviceworkers/serviceworkerinfo_iframe.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <script> + window.onmessage = function (event) { + if (event.data !== "register") { + return; + } + var promise = navigator.serviceWorker.register("worker.js"); + window.onmessage = function (event) { + if (event.data !== "unregister") { + return; + } + promise.then(function (registration) { + registration.unregister(); + }); + window.onmessage = null; + }; + }; + </script> + </head> + <body> + This is a test page. + </body> +<html>
--- a/dom/workers/test/serviceworkers/test_install_event.html +++ b/dom/workers/test/serviceworkers/test_install_event.html @@ -19,23 +19,36 @@ var p = navigator.serviceWorker.register("worker.js", { scope: "./install_event" }); return p; } function nextRegister(reg) { ok(reg instanceof ServiceWorkerRegistration, "reg should be a ServiceWorkerRegistration"); var p = navigator.serviceWorker.register("install_event_worker.js", { scope: "./install_event" }); return p.then(function(swr) { - ok(reg.scope === swr.scope, "Scope for registrations should match."); - return new Promise(function(resolve, reject) { + ok(reg === swr, "register should resolve to the same registration object"); + var update_found_promise = new Promise(function(resolve, reject) { swr.addEventListener('updatefound', function(e) { ok(true, "Received onupdatefound"); resolve(); }); }); + + var worker_activating = new Promise(function(res, reject) { + ok(swr.installing instanceof ServiceWorker, "There should be an installing worker if promise resolves."); + ok(swr.installing.state == "installing", "Installing worker's state should be 'installing'"); + swr.installing.onstatechange = function(e) { + if (e.target.state == "activating") { + e.target.onstatechange = null; + res(); + } + } + }); + + return Promise.all([update_found_promise, worker_activating]); }, function(e) { ok(false, "Unexpected Error in nextRegister! " + e); }); } function installError() { // Silence worker errors so they don't cause the test to fail. window.onerror = function(e) {}
new file mode 100644 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_serviceworkerinfo.xul @@ -0,0 +1,101 @@ +<?xml version="1.0"?> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<window title="Test for ServiceWorkerInfo" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="test();"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="chrome_helpers.js"/> + <script type="application/javascript"> + <![CDATA[ + + let IFRAME_URL = EXAMPLE_URL + "serviceworkerinfo_iframe.html"; + + function wait_for_active_worker(registration) { + ok(registration, "Registration is valid."); + return new Promise(function(res, rej) { + if (registration.activeWorker) { + res(registration); + return; + } + let listener = { + onChange: function() { + if (registration.activeWorker) { + registration.removeListener(listener); + res(registration); + } + } + } + registration.addListener(listener); + }); + } + + function test() { + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv({'set': [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.idle_extended_timeout", 1000000], + ["dom.serviceWorkers.idle_timeout", 0], + ["dom.serviceWorkers.testing.enabled", true], + ]}, function () { + Task.spawn(function *() { + let iframe = $("iframe"); + let promise = new Promise(function (resolve) { + iframe.onload = function () { + resolve(); + }; + }); + iframe.src = IFRAME_URL; + yield promise; + + info("Check that a service worker eventually shuts down."); + promise = Promise.all([ + waitForRegister(EXAMPLE_URL), + waitForServiceWorkerShutdown() + ]); + iframe.contentWindow.postMessage("register", "*"); + let [registration] = yield promise; + + // Make sure the worker is active. + registration = yield wait_for_active_worker(registration); + + let activeWorker = registration.activeWorker; + ok(activeWorker !== null, "Worker is not active!"); + ok(activeWorker.debugger === null); + + info("Attach a debugger to the service worker, and check that the " + + "service worker is restarted."); + activeWorker.attachDebugger(); + ok(activeWorker.debugger !== null); + + info("Detach the debugger from the service worker, and check that " + + "the service worker eventually shuts down again."); + promise = waitForServiceWorkerShutdown(); + activeWorker.detachDebugger(); + yield promise; + ok(activeWorker.debugger === null); + + promise = waitForUnregister(EXAMPLE_URL); + iframe.contentWindow.postMessage("unregister", "*"); + registration = yield promise; + + SimpleTest.finish(); + }); + }); + } + + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display:none;"></div> + <pre id="test"></pre> + <iframe id="iframe"></iframe> + </body> + <label id="test-result"/> +</window>
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -417,44 +417,60 @@ APZCCallbackHelper::GetRootContentDocume CSSPoint APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput, const ScrollableLayerGuid& aGuid) { CSSPoint input = aInput; if (aGuid.mScrollId == FrameMetrics::NULL_SCROLL_ID) { return input; } + nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aGuid.mScrollId); if (!content) { return input; } +#if !defined(MOZ_SINGLE_PROCESS_APZ) // First, scale inversely by the root content document's pres shell // resolution to cancel the scale-to-resolution transform that the // compositor adds to the layer with the pres shell resolution. The points // sent to Gecko by APZ don't have this transform unapplied (unlike other // compositor-side transforms) because APZ doesn't know about it. if (nsIPresShell* shell = GetRootContentDocumentPresShellForContent(content)) { input = input / shell->GetResolution(); } +#endif - // Now apply the callback-transform. + // Apply the callback-transform. // XXX: technically we need to walk all the way up the layer tree from the layer // represented by |aGuid.mScrollId| up to the root of the layer tree and apply // the input transforms at each level in turn. However, it is quite difficult // to do this given that the structure of the layer tree may be different from // the structure of the content tree. Also it may be impossible to do correctly // at this point because there are other CSS transforms and such interleaved in // between so applying the inputTransforms all in a row at the end may leave // some things transformed improperly. In practice we should rarely hit scenarios // where any of this matters, so I'm skipping it for now and just doing the single // transform for the layer that the input hit. + void* property = content->GetProperty(nsGkAtoms::apzCallbackTransform); if (property) { CSSPoint delta = (*static_cast<CSSPoint*>(property)); + +#if defined(MOZ_SINGLE_PROCESS_APZ) + // The delta is in root content document coordinate space while the + // aInput point is in root document coordinate space so convert the + // delta to root document space before adding it to the aInput point. + float resolution = 1.0f; + if (nsIPresShell* shell = GetRootContentDocumentPresShellForContent(content)) { + resolution = shell->GetResolution(); + } + delta.x = delta.x * resolution; + delta.y = delta.y * resolution; +#endif // MOZ_SINGLE_PROCESS_APZ input += delta; } return input; } LayoutDeviceIntPoint APZCCallbackHelper::ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint, const ScrollableLayerGuid& aGuid,
--- a/gfx/layers/apz/util/ChromeProcessController.cpp +++ b/gfx/layers/apz/util/ChromeProcessController.cpp @@ -142,16 +142,26 @@ ChromeProcessController::HandleDoubleTap } nsCOMPtr<nsIDocument> document = GetRootContentDocument(aGuid.mScrollId); if (!document.get()) { return; } CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid); +#if defined(MOZ_SINGLE_PROCESS_APZ) + // CalculateRectToZoomTo performs a hit test on the frame associated with the + // Root Content Document. Unfortunately that frame does not know about the + // resolution of the document and so we must remove it before calculating + // the zoomToRect. + nsIPresShell* presShell = document->GetShell(); + const float resolution = presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f; + point.x = point.x / resolution; + point.y = point.y / resolution; +#endif // MOZ_SINGLE_PROCESS_APZ CSSRect zoomToRect = CalculateRectToZoomTo(document, point); uint32_t presShellId; FrameMetrics::ViewID viewId; if (APZCCallbackHelper::GetOrCreateScrollIdentifiers( document->GetDocumentElement(), &presShellId, &viewId)) { mAPZCTreeManager->ZoomToRect( ScrollableLayerGuid(aGuid.mLayersId, presShellId, viewId), zoomToRect);
--- a/gfx/layers/basic/BasicLayerManager.cpp +++ b/gfx/layers/basic/BasicLayerManager.cpp @@ -123,16 +123,26 @@ BasicLayerManager::PushGroupForLayer(gfx group.mMaskSurface = GetMaskForLayer(aLayer, &group.mMaskTransform); return group; } } Matrix maskTransform; RefPtr<SourceSurface> maskSurf = GetMaskForLayer(aLayer, &maskTransform); + if (maskSurf) { + // The returned transform will transform the mask to device space on the + // destination. Since the User->Device space transform will be applied + // to the mask by PopGroupAndBlend we need to adjust the transform to + // transform the mask to user space. + Matrix currentTransform = ToMatrix(group.mFinalTarget->CurrentMatrix()); + currentTransform.Invert(); + maskTransform = maskTransform * currentTransform; + } + if (aLayer->CanUseOpaqueSurface() && ((didCompleteClip && aRegion.GetNumRects() == 1) || !aContext->CurrentMatrix().HasNonIntegerTranslation())) { // If the layer is opaque in its visible region we can push a gfxContentType::COLOR // group. We need to make sure that only pixels inside the layer's visible // region are copied back to the destination. Remember if we've already // clipped precisely to the visible region. group.mNeedsClipToVisibleRegion = !didCompleteClip || aRegion.GetNumRects() > 1; @@ -167,18 +177,22 @@ BasicLayerManager::PopGroupForLayer(Push DrawTarget* dt = group.mFinalTarget->GetDrawTarget(); RefPtr<DrawTarget> sourceDT = group.mGroupTarget->GetDrawTarget(); group.mGroupTarget = nullptr; RefPtr<SourceSurface> src = sourceDT->Snapshot(); if (group.mMaskSurface) { - dt->SetTransform(group.mMaskTransform * Matrix::Translation(-group.mFinalTarget->GetDeviceOffset())); - dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP, Matrix::Translation(group.mGroupOffset.x, group.mGroupOffset.y)), + Point finalOffset = group.mFinalTarget->GetDeviceOffset(); + dt->SetTransform(group.mMaskTransform * Matrix::Translation(-finalOffset)); + Matrix surfTransform = group.mMaskTransform; + surfTransform.Invert(); + dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP, surfTransform * + Matrix::Translation(group.mGroupOffset.x, group.mGroupOffset.y)), group.mMaskSurface, Point(0, 0), DrawOptions(group.mOpacity, group.mOperator)); } else { // For now this is required since our group offset is in device space of the final target, // context but that may still have its own device offset. Once PushGroup/PopGroup logic is // migrated to DrawTargets this can go as gfxContext::GetDeviceOffset will essentially // always become null. dt->SetTransform(Matrix::Translation(-group.mFinalTarget->GetDeviceOffset())); dt->DrawSurface(src, Rect(group.mGroupOffset.x, group.mGroupOffset.y, src->GetSize().width, src->GetSize().height),
--- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -1659,21 +1659,23 @@ CompositorParent::SetControllerForLayerT aLayersId, aController)); } /*static*/ APZCTreeManager* CompositorParent::GetAPZCTreeManager(uint64_t aLayersId) { EnsureLayerTreeMapReady(); - const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId); - if (state && state->mParent) { - return state->mParent->mApzcTreeManager; + MonitorAutoLock lock(*sIndirectLayerTreesLock); + LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aLayersId); + if (sIndirectLayerTrees.end() == cit) { + return nullptr; } - return nullptr; + LayerTreeState* lts = &cit->second; + return (lts->mParent ? lts->mParent->mApzcTreeManager.get() : nullptr); } float CompositorParent::ComputeRenderIntegrity() { if (mLayerManager) { return mLayerManager->ComputeRenderIntegrity(); }
--- a/gfx/src/nsPoint.h +++ b/gfx/src/nsPoint.h @@ -32,16 +32,21 @@ struct nsPoint : public mozilla::gfx::Ba /** * Return this point scaled to a different appunits per pixel (APP) ratio. * @param aFromAPP the APP to scale from * @param aToAPP the APP to scale to */ MOZ_WARN_UNUSED_RESULT inline nsPoint ScaleToOtherAppUnits(int32_t aFromAPP, int32_t aToAPP) const; + + MOZ_WARN_UNUSED_RESULT inline nsPoint + RemoveResolution(const float resolution) const; + MOZ_WARN_UNUSED_RESULT inline nsPoint + ApplyResolution(const float resolution) const; }; inline nsPoint ToAppUnits(const nsIntPoint& aPoint, nscoord aAppUnitsPerPixel); inline nsIntPoint nsPoint::ScaleToNearestPixels(float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const { @@ -63,16 +68,38 @@ nsPoint::ScaleToOtherAppUnits(int32_t aF nsPoint point; point.x = NSToCoordRound(NSCoordScale(x, aFromAPP, aToAPP)); point.y = NSToCoordRound(NSCoordScale(y, aFromAPP, aToAPP)); return point; } return *this; } +inline nsPoint +nsPoint::RemoveResolution(const float resolution) const { + if (resolution != 1.0f) { + nsPoint point; + point.x = NSToCoordRound(NSCoordToFloat(x) / resolution); + point.y = NSToCoordRound(NSCoordToFloat(y) / resolution); + return point; + } + return *this; +} + +inline nsPoint +nsPoint::ApplyResolution(const float resolution) const { + if (resolution != 1.0f) { + nsPoint point; + point.x = NSToCoordRound(NSCoordToFloat(x) * resolution); + point.y = NSToCoordRound(NSCoordToFloat(y) * resolution); + return point; + } + return *this; +} + // app units are integer multiples of pixels, so no rounding needed inline nsPoint ToAppUnits(const nsIntPoint& aPoint, nscoord aAppUnitsPerPixel) { return nsPoint(NSIntPixelsToAppUnits(aPoint.x, aAppUnitsPerPixel), NSIntPixelsToAppUnits(aPoint.y, aAppUnitsPerPixel)); }
--- a/gfx/src/nsRect.h +++ b/gfx/src/nsRect.h @@ -166,16 +166,18 @@ struct nsRect : MOZ_WARN_UNUSED_RESULT inline mozilla::gfx::IntRect ToInsidePixels(nscoord aAppUnitsPerPixel) const; // This is here only to keep IPDL-generated code happy. DO NOT USE. bool operator==(const nsRect& aRect) const { return IsEqualEdges(aRect); } + + MOZ_WARN_UNUSED_RESULT inline nsRect RemoveResolution(const float aResolution) const; }; /* * App Unit/Pixel conversions */ inline nsRect nsRect::ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP, int32_t aToAPP) const @@ -274,16 +276,35 @@ nsRect::ToOutsidePixels(nscoord aAppUnit } inline mozilla::gfx::IntRect nsRect::ToInsidePixels(nscoord aAppUnitsPerPixel) const { return ScaleToInsidePixels(1.0f, 1.0f, aAppUnitsPerPixel); } +inline nsRect +nsRect::RemoveResolution(const float aResolution) const +{ + MOZ_ASSERT(aResolution > 0.0f); + nsRect rect; + rect.x = NSToCoordRound(NSCoordToFloat(x) / aResolution); + rect.y = NSToCoordRound(NSCoordToFloat(y) / aResolution); + // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1 + // rect as well instead of possibly rounding the width or height to zero. + if (width == 1 && height == 1) { + rect.width = rect.height = 1; + } else { + rect.width = NSToCoordCeil(NSCoordToFloat(width) / aResolution); + rect.height = NSToCoordCeil(NSCoordToFloat(height) / aResolution); + } + + return rect; +} + const mozilla::gfx::IntRect& GetMaxSizedIntRect(); // app units are integer multiples of pixels, so no rounding needed nsRect ToAppUnits(const mozilla::gfx::IntRect& aRect, nscoord aAppUnitsPerPixel); #ifdef DEBUG // Diagnostics
--- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -11,16 +11,17 @@ #include "nsNetUtil.h" #include "nsIJARChannel.h" #include "nsIProtocolHandler.h" #include "nsIPrincipal.h" #include "nsIZipReader.h" #include "gfxFontConstants.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" +#include "mozilla/Telemetry.h" #include "mozilla/gfx/2D.h" #include "gfxPlatformFontList.h" #include "opentype-sanitiser.h" #include "ots-memory-stream.h" using namespace mozilla; @@ -422,16 +423,18 @@ gfxUserFontEntry::LoadNextSrc() fe->mFamilyName = mFamilyName; // For src:local(), we don't care whether the request is from // a private window as there's no issue of caching resources; // local fonts are just available all the time. StoreUserFontData(fe, false, nsString(), nullptr, 0, gfxUserFontData::kUnknownCompression); mPlatformFontEntry = fe; SetLoadState(STATUS_LOADED); + Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE, + currSrc.mSourceType + 1); return; } else { LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n", mFontSet, mSrcIndex, NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(), NS_ConvertUTF16toUTF8(mFamilyName).get())); } } @@ -452,16 +455,24 @@ gfxUserFontEntry::LoadNextSrc() gfxFontEntry* fe = gfxUserFontSet:: UserFontCache::GetFont(currSrc.mURI, principal, this, mFontSet->GetPrivateBrowsing()); if (fe) { mPlatformFontEntry = fe; SetLoadState(STATUS_LOADED); + if (LOG_ENABLED()) { + nsAutoCString fontURI; + currSrc.mURI->GetSpec(fontURI); + LOG(("userfonts (%p) [src %d] " + "loaded uri from cache: (%s) for (%s)\n", + mFontSet, mSrcIndex, fontURI.get(), + NS_ConvertUTF16toUTF8(mFamilyName).get())); + } return; } } // record the principal returned by CheckFontLoad, // for use when creating a channel // and when caching the loaded entry mPrincipal = principal; @@ -477,16 +488,18 @@ gfxUserFontEntry::LoadNextSrc() // sync load font immediately rv = mFontSet->SyncLoadFontData(this, &currSrc, buffer, bufferLength); if (NS_SUCCEEDED(rv) && LoadPlatformFont(buffer, bufferLength)) { SetLoadState(STATUS_LOADED); + Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE, + currSrc.mSourceType + 1); return; } else { mFontSet->LogMessage(this, "font load failed", nsIScriptError::errorFlag, rv); } @@ -531,16 +544,18 @@ gfxUserFontEntry::LoadNextSrc() uint32_t bufferLength = 0; // sync load font immediately currSrc.mBuffer->TakeBuffer(buffer, bufferLength); if (buffer && LoadPlatformFont(buffer, bufferLength)) { // LoadPlatformFont takes ownership of the buffer, so no need // to free it here. SetLoadState(STATUS_LOADED); + Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE, + currSrc.mSourceType + 1); return; } else { mFontSet->LogMessage(this, "font load failed", nsIScriptError::errorFlag); } } @@ -572,34 +587,47 @@ gfxUserFontEntry::LoadPlatformFont(const mUserFontLoadState == STATUS_LOADING) && mFontDataLoadingState < LOADING_FAILED, "attempting to load a font that has either completed or failed"); gfxFontEntry* fe = nullptr; gfxUserFontType fontType = gfxFontUtils::DetermineFontDataType(aFontData, aLength); + Telemetry::Accumulate(Telemetry::WEBFONT_FONTTYPE, uint32_t(fontType)); // Unwrap/decompress/sanitize or otherwise munge the downloaded data // to make a usable sfnt structure. // Because platform font activation code may replace the name table // in the font with a synthetic one, we save the original name so that // it can be reported via the nsIDOMFontFace API. nsAutoString originalFullName; // Call the OTS sanitizer; this will also decode WOFF to sfnt // if necessary. The original data in aFontData is left unchanged. uint32_t saneLen; + uint32_t fontCompressionRatio = 0; const uint8_t* saneData = SanitizeOpenTypeData(aFontData, aLength, saneLen, fontType); if (!saneData) { mFontSet->LogMessage(this, "rejected by sanitizer"); } if (saneData) { + if (saneLen) { + fontCompressionRatio = uint32_t(100.0 * aLength / saneLen + 0.5); + if (fontType == GFX_USERFONT_WOFF || + fontType == GFX_USERFONT_WOFF2) { + Telemetry::Accumulate(fontType == GFX_USERFONT_WOFF ? + Telemetry::WEBFONT_COMPRESSION_WOFF : + Telemetry::WEBFONT_COMPRESSION_WOFF2, + fontCompressionRatio); + } + } + // The sanitizer ensures that we have a valid sfnt and a usable // name table, so this should never fail unless we're out of // memory, and GetFullNameFromSFNT is not directly exposed to // arbitrary/malicious data from the web. gfxFontUtils::GetFullNameFromSFNT(saneData, saneLen, originalFullName); // Here ownership of saneData is passed to the platform, // which will delete it when no longer required @@ -636,21 +664,21 @@ gfxUserFontEntry::LoadPlatformFont(const fe->mFeatureSettings.AppendElements(mFeatureSettings); fe->mLanguageOverride = mLanguageOverride; fe->mFamilyName = mFamilyName; StoreUserFontData(fe, mFontSet->GetPrivateBrowsing(), originalFullName, &metadata, metaOrigLen, compression); if (LOG_ENABLED()) { nsAutoCString fontURI; mSrcList[mSrcIndex].mURI->GetSpec(fontURI); - LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) (%p) gen: %8.8x\n", + LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) " + "(%p) gen: %8.8x compress: %d%%\n", mFontSet, mSrcIndex, fontURI.get(), NS_ConvertUTF16toUTF8(mFamilyName).get(), - this, - uint32_t(mFontSet->mGeneration))); + this, uint32_t(mFontSet->mGeneration), fontCompressionRatio)); } mPlatformFontEntry = fe; SetLoadState(STATUS_LOADED); gfxUserFontSet::UserFontCache::CacheFont(fe); } else { if (LOG_ENABLED()) { nsAutoCString fontURI; mSrcList[mSrcIndex].mURI->GetSpec(fontURI); @@ -733,17 +761,20 @@ gfxUserFontEntry::FontDataDownloadComple void gfxUserFontEntry::GetUserFontSets(nsTArray<gfxUserFontSet*>& aResult) { aResult.Clear(); aResult.AppendElement(mFontSet); } gfxUserFontSet::gfxUserFontSet() - : mFontFamilies(4), mLocalRulesUsed(false) + : mFontFamilies(4), + mLocalRulesUsed(false), + mDownloadCount(0), + mDownloadSize(0) { IncrementGeneration(true); gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList(); if (fp) { fp->AddUserFontSet(this); } }
--- a/gfx/thebes/gfxUserFontSet.h +++ b/gfx/thebes/gfxUserFontSet.h @@ -463,16 +463,25 @@ public: }; void SetLocalRulesUsed() { mLocalRulesUsed = true; } static mozilla::LogModule* GetUserFontsLog(); + // record statistics about font completion + virtual void RecordFontLoadDone(uint32_t aFontSize, + mozilla::TimeStamp aDoneTime) {} + + void GetLoadStatistics(uint32_t& aLoadCount, uint64_t& aLoadSize) const { + aLoadCount = mDownloadCount; + aLoadSize = mDownloadSize; + } + protected: // Protected destructor, to discourage deletion outside of Release(): virtual ~gfxUserFontSet(); // Return whether the font set is associated with a private-browsing tab. virtual bool GetPrivateBrowsing() = 0; // parse data for a data URL @@ -508,16 +517,20 @@ protected: // font families defined by @font-face rules nsRefPtrHashtable<nsStringHashKey, gfxUserFontFamily> mFontFamilies; uint64_t mGeneration; // bumped on any font load change uint64_t mRebuildGeneration; // only bumped on rebuilds // true when local names have been looked up, false otherwise bool mLocalRulesUsed; + + // performance stats + uint32_t mDownloadCount; + uint64_t mDownloadSize; }; // acts a placeholder until the real font is downloaded class gfxUserFontEntry : public gfxFontEntry { friend class gfxUserFontSet; friend class nsUserFontSet; friend class nsFontFaceLoader;
--- a/intl/unicharutil/tools/genUnicodePropertyData.pl +++ b/intl/unicharutil/tools/genUnicodePropertyData.pl @@ -14,16 +14,17 @@ # (1) Download the current Unicode data files from # # http://www.unicode.org/Public/UNIDATA/ # # NB: not all the files are actually needed; currently, we require # - UnicodeData.txt # - Scripts.txt # - BidiMirroring.txt +# - BidiBrackets.txt # - HangulSyllableType.txt # - ReadMe.txt (to record version/date of the UCD) # - Unihan_Variants.txt (from Unihan.zip) # though this may change if we find a need for additional properties. # # The Unicode data files listed above should be together in one directory. # # We also require the file @@ -329,28 +330,30 @@ my %verticalOrientationCode = ( 'Tr' => 3 # Tr - Transformed typographically, with fallback to Rotated ); # initialize default properties my @script; my @category; my @combining; my @mirror; +my @pairedBracketType; my @hangul; my @casemap; my @xidmod; my @numericvalue; my @hanVariant; my @bidicategory; my @fullWidth; my @verticalOrientation; for (my $i = 0; $i < 0x110000; ++$i) { $script[$i] = $scriptCode{"UNKNOWN"}; $category[$i] = $catCode{"UNASSIGNED"}; $combining[$i] = 0; + $pairedBracketType[$i] = 0; $casemap[$i] = 0; $xidmod[$i] = $xidmodCode{"not-chars"}; $numericvalue[$i] = -1; $hanVariant[$i] = 0; $bidicategory[$i] = $bidicategoryCode{"L"}; $fullWidth[$i] = 0; $verticalOrientation[$i] = 1; # default for unlisted codepoints is 'R' } @@ -518,23 +521,48 @@ while (<FH>) { chomp; push @versionInfo, $_; last if /Date:/; } while (<FH>) { s/#.*//; if (m/([0-9A-F]{4,6});\s*([0-9A-F]{4,6})/) { my $mirrorOffset = hex("0x$2") - hex("0x$1"); - my $offsetIndex = first { $offsets[$_] eq $mirrorOffset } 0..$#offsets; - if ($offsetIndex == undef) { + my $offsetIndex = first { $offsets[$_] eq $mirrorOffset } 0..$#offsets; + if ($offsetIndex == undef) { die "too many offset codes\n" if scalar @offsets == 31; push @offsets, $mirrorOffset; - $offsetIndex = $#offsets; + $offsetIndex = $#offsets; } - $mirror[hex "0x$1"] = $offsetIndex; + $mirror[hex "0x$1"] = $offsetIndex; + } +} +close FH; + +# read BidiBrackets.txt +my %pairedBracketTypeCode = ( + 'N' => 0, + 'O' => 1, + 'C' => 2 +); +open FH, "< $ARGV[1]/BidiBrackets.txt" or die "can't open UCD file BidiBrackets.txt\n"; +push @versionInfo, ""; +while (<FH>) { + chomp; + push @versionInfo, $_; + last if /Date:/; +} +while (<FH>) { + s/#.*//; + if (m/([0-9A-F]{4,6});\s*([0-9A-F]{4,6});\s*(.)/) { + my $mirroredChar = $offsets[$mirror[hex "0x$1"]] + hex "0x$1"; + die "bidi bracket does not match mirrored char\n" unless $mirroredChar == hex "0x$2"; + my $pbt = uc($3); + warn "unknown Bidi Bracket type" unless exists $pairedBracketTypeCode{$pbt}; + $pairedBracketType[hex "0x$1"] = $pairedBracketTypeCode{$pbt}; } } close FH; # read HangulSyllableType.txt my %hangulType = ( 'L' => 0x01, 'V' => 0x02, @@ -731,37 +759,37 @@ my $type = q/ struct nsCharProps1 { unsigned char mMirrorOffsetIndex:5; unsigned char mHangulType:3; unsigned char mCombiningClass:8; }; /; print DATA_TABLES "#ifndef ENABLE_INTL_API\n"; &genTables("CharProp1", $type, "nsCharProps1", 11, 5, \&sprintCharProps1, 1, 2, 1); -+print DATA_TABLES "#endif\n\n"; +print DATA_TABLES "#endif\n\n"; sub sprintCharProps2 { my $usv = shift; return sprintf("{%d,%d,%d,%d,%d,%d,%d},", - $script[$usv], 0, $category[$usv], + $script[$usv], $pairedBracketType[$usv], $category[$usv], $bidicategory[$usv], $xidmod[$usv], $numericvalue[$usv], $verticalOrientation[$usv]); } -$type = q/ +$type = q| struct nsCharProps2 { unsigned char mScriptCode:8; - unsigned char mUnused:3; + unsigned char mPairedBracketType:3; // only 2 bits actually needed unsigned char mCategory:5; unsigned char mBidiCategory:5; unsigned char mXidmod:4; signed char mNumericValue:5; unsigned char mVertOrient:2; }; -/; +|; &genTables("CharProp2", $type, "nsCharProps2", 11, 5, \&sprintCharProps2, 16, 4, 1); print HEADER "#pragma pack()\n\n"; sub sprintHanVariants { my $baseUsv = shift; my $varShift = 0;
--- a/intl/unicharutil/util/nsUnicodeProperties.cpp +++ b/intl/unicharutil/util/nsUnicodeProperties.cpp @@ -165,16 +165,36 @@ GetScriptTagForCode(int32_t aScriptCode) { // this will safely return 0 for negative script codes, too :) if (uint32_t(aScriptCode) > ArrayLength(sScriptCodeToTag)) { return 0; } return sScriptCodeToTag[aScriptCode]; } +PairedBracketType GetPairedBracketType(uint32_t aCh) +{ +#if ENABLE_INTL_API + return PairedBracketType + (u_getIntPropertyValue(aCh, UCHAR_BIDI_PAIRED_BRACKET_TYPE)); +#else + return PairedBracketType(GetCharProps2(aCh).mPairedBracketType); +#endif +} + +uint32_t GetPairedBracket(uint32_t aCh) +{ +#if ENABLE_INTL_API + return u_getBidiPairedBracket(aCh); +#else + return GetPairedBracketType(aCh) != PAIRED_BRACKET_TYPE_NONE + ? GetMirroredChar(aCh) : aCh; +#endif +} + static inline uint32_t GetCaseMapValue(uint32_t aCh) { if (aCh < UNICODE_BMP_LIMIT) { return sCaseMapValues[sCaseMapPages[0][aCh >> kCaseMapCharBits]] [aCh & ((1 << kCaseMapCharBits) - 1)]; } if (aCh < (kCaseMapMaxPlane + 1) * 0x10000) {
--- a/intl/unicharutil/util/nsUnicodeProperties.h +++ b/intl/unicharutil/util/nsUnicodeProperties.h @@ -52,16 +52,26 @@ enum VerticalOrientation { VERTICAL_ORIENTATION_Tu = 2, VERTICAL_ORIENTATION_Tr = 3 }; inline VerticalOrientation GetVerticalOrientation(uint32_t aCh) { return VerticalOrientation(GetCharProps2(aCh).mVertOrient); } +/* This MUST match the values assigned by genUnicodePropertyData.pl! */ +enum PairedBracketType { + PAIRED_BRACKET_TYPE_NONE = 0, + PAIRED_BRACKET_TYPE_OPEN = 1, + PAIRED_BRACKET_TYPE_CLOSE = 2 +}; + +PairedBracketType GetPairedBracketType(uint32_t aCh); +uint32_t GetPairedBracket(uint32_t aCh); + enum XidmodType { XIDMOD_RECOMMENDED, XIDMOD_INCLUSION, XIDMOD_UNCOMMON_USE, XIDMOD_TECHNICAL, XIDMOD_OBSOLETE, XIDMOD_ASPIRATIONAL, XIDMOD_LIMITED_USE,
--- a/intl/unicharutil/util/nsUnicodePropertyData.cpp +++ b/intl/unicharutil/util/nsUnicodePropertyData.cpp @@ -6,17 +6,17 @@ /* * Derived from the Unicode Character Database by genUnicodePropertyData.pl * * For Unicode terms of use, see http://www.unicode.org/terms_of_use.html */ /* - * Created on Mon Sep 7 02:52:23 2015 from UCD data files with version info: + * Created on Tue Nov 17 07:34:16 2015 from UCD data files with version info: * # Date: 2015-06-16, 20:24:00 GMT [KW] # # Unicode Character Database # Copyright (c) 1991-2015 Unicode, Inc. # For terms of use, see http://www.unicode.org/terms_of_use.html # @@ -33,16 +33,19 @@ Standard. # Scripts-8.0.0.txt # Date: 2015-03-11, 22:29:42 GMT [MD] # BidiMirroring-8.0.0.txt # Date: 2015-01-20, 18:30:00 GMT [KW, LI] +# BidiBrackets-8.0.0.txt +# Date: 2015-01-20, 19:00:00 GMT [AG, LI, KW] + # HangulSyllableType-8.0.0.txt # Date: 2014-12-16, 23:07:45 GMT [MD] # File: xidmodifications.txt # Version: 8.0.0 # Generated: 2015-05-17, 03:09:04 GMT # @@ -406,19 +409,19 @@ static const uint16_t sCharProp2Pages[7] {320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,639}, {110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110}, {640,641,641,642,110,110,110,110,643,643,643,643,643,643,643,644,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110}, {377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,645} }; static const nsCharProps2 sCharProp2Values[646][32] = { {{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,8,8,-1,1},{0,0,0,7,8,-1,1},{0,0,0,8,8,-1,1},{0,0,0,9,8,-1,1},{0,0,0,7,8,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,7,12,-1,1},{0,0,0,7,12,-1,1},{0,0,0,7,12,-1,1},{0,0,0,8,12,-1,1}}, - {{0,0,29,9,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,21,4,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,1,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,25,3,8,-1,1},{0,0,21,6,8,-1,1},{0,0,17,3,1,-1,1},{0,0,21,6,1,-1,1},{0,0,21,6,8,-1,1},{0,0,13,2,0,0,1},{0,0,13,2,0,1,1},{0,0,13,2,0,2,1},{0,0,13,2,0,3,1},{0,0,13,2,0,4,1},{0,0,13,2,0,5,1},{0,0,13,2,0,6,1},{0,0,13,2,0,7,1},{0,0,13,2,0,8,1},{0,0,13,2,0,9,1},{0,0,21,6,1,-1,1},{0,0,21,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,21,10,8,-1,1}}, - {{0,0,21,10,8,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{0,0,22,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,24,10,8,-1,1},{0,0,16,10,0,-1,1}}, - {{0,0,24,10,8,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{0,0,22,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,0,18,12,-1,1}}, + {{0,0,29,9,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,21,4,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,1,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,25,3,8,-1,1},{0,0,21,6,8,-1,1},{0,0,17,3,1,-1,1},{0,0,21,6,1,-1,1},{0,0,21,6,8,-1,1},{0,0,13,2,0,0,1},{0,0,13,2,0,1,1},{0,0,13,2,0,2,1},{0,0,13,2,0,3,1},{0,0,13,2,0,4,1},{0,0,13,2,0,5,1},{0,0,13,2,0,6,1},{0,0,13,2,0,7,1},{0,0,13,2,0,8,1},{0,0,13,2,0,9,1},{0,0,21,6,1,-1,1},{0,0,21,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,21,10,8,-1,1}}, + {{0,0,21,10,8,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{0,1,22,10,8,-1,1},{0,0,21,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,24,10,8,-1,1},{0,0,16,10,0,-1,1}}, + {{0,0,24,10,8,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{0,1,22,10,8,-1,1},{0,0,25,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,0,18,12,-1,1}}, {{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,7,8,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1}}, {{0,0,29,6,9,-1,1},{0,0,21,10,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,26,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,24,10,9,-1,1},{0,0,26,10,8,-1,0},{25,0,7,0,9,-1,1},{0,0,20,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,1,18,10,-1,1},{0,0,26,10,8,-1,0},{0,0,24,10,9,-1,1},{0,0,26,4,8,-1,1},{0,0,25,4,8,-1,0},{0,0,15,2,9,2,1},{0,0,15,2,9,3,1},{0,0,24,10,9,-1,1},{0,0,5,0,9,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,1,-1,1},{0,0,24,10,9,-1,1},{0,0,15,2,9,1,1},{25,0,7,0,9,-1,1},{0,0,19,10,8,-1,1},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,21,10,8,-1,1}}, {{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{0,0,25,10,8,-1,0},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1}}, {{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{0,0,25,10,8,-1,0},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1}}, {{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1}}, {{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,9,-1,1},{25,0,5,0,9,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,9,-1,1}}, {{25,0,5,0,9,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,11,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1}}, {{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,9,-1,1}}, @@ -520,17 +523,17 @@ static const nsCharProps2 sCharProp2Valu {{61,0,2,0,12,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1}}, {{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,12,17,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,9,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{0,0,23,4,8,-1,1}}, {{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,6,0,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,21,0,8,-1,1},{38,0,13,0,0,0,1},{38,0,13,0,0,1,1},{38,0,13,0,0,2,1},{38,0,13,0,0,3,1},{38,0,13,0,0,4,1},{38,0,13,0,0,5,1},{38,0,13,0,0,6,1},{38,0,13,0,0,7,1},{38,0,13,0,0,8,1},{38,0,13,0,0,9,1},{38,0,21,0,8,-1,1},{38,0,21,0,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1}}, {{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,12,17,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,9,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{61,0,2,0,12,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,6,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{24,0,13,0,0,0,1},{24,0,13,0,0,1,1},{24,0,13,0,0,2,1},{24,0,13,0,0,3,1},{24,0,13,0,0,4,1},{24,0,13,0,0,5,1},{24,0,13,0,0,6,1},{24,0,13,0,0,7,1},{24,0,13,0,0,8,1},{24,0,13,0,0,9,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,9,-1,1},{24,0,7,0,9,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1}}, {{39,0,7,0,0,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,1,-1,1},{39,0,21,0,9,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,3,-1,1},{39,0,12,17,3,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1}}, - {{39,0,13,0,0,0,1},{39,0,13,0,0,1,1},{39,0,13,0,0,2,1},{39,0,13,0,0,3,1},{39,0,13,0,0,4,1},{39,0,13,0,0,5,1},{39,0,13,0,0,6,1},{39,0,13,0,0,7,1},{39,0,13,0,0,8,1},{39,0,13,0,0,9,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,0,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,0,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,2,-1,1},{39,0,22,10,8,-1,1},{39,0,18,10,8,-1,1},{39,0,22,10,8,-1,1},{39,0,18,10,8,-1,1},{39,0,10,0,0,-1,1},{39,0,10,0,0,-1,1}}, + {{39,0,13,0,0,0,1},{39,0,13,0,0,1,1},{39,0,13,0,0,2,1},{39,0,13,0,0,3,1},{39,0,13,0,0,4,1},{39,0,13,0,0,5,1},{39,0,13,0,0,6,1},{39,0,13,0,0,7,1},{39,0,13,0,0,8,1},{39,0,13,0,0,9,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,0,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,0,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,2,-1,1},{39,1,22,10,8,-1,1},{39,2,18,10,8,-1,1},{39,1,22,10,8,-1,1},{39,2,18,10,8,-1,1},{39,0,10,0,0,-1,1},{39,0,10,0,0,-1,1}}, {{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,9,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,9,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,9,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,9,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,9,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1}}, {{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,9,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,11,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,11,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,10,0,0,-1,1}}, {{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,21,0,8,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{61,0,2,0,12,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1}}, {{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{61,0,2,0,12,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1}}, {{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,0,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{61,0,2,0,12,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1}}, {{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,10,0,0,-1,1},{28,0,10,0,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,10,0,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,10,0,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,10,0,0,-1,1},{28,0,10,0,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,7,0,0,-1,1}}, {{28,0,13,0,0,0,1},{28,0,13,0,0,1,1},{28,0,13,0,0,2,1},{28,0,13,0,0,3,1},{28,0,13,0,0,4,1},{28,0,13,0,0,5,1},{28,0,13,0,0,6,1},{28,0,13,0,0,7,1},{28,0,13,0,0,8,1},{28,0,13,0,0,9,1},{28,0,21,0,8,-1,1},{28,0,21,0,8,-1,1},{28,0,21,0,8,-1,1},{28,0,21,0,8,-1,1},{28,0,21,0,8,-1,1},{28,0,21,0,8,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,10,0,0,-1,1},{28,0,10,0,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1}}, @@ -551,17 +554,17 @@ static const nsCharProps2 sCharProp2Valu {{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{11,0,12,17,0,-1,1},{11,0,12,17,0,-1,1},{11,0,12,17,0,-1,1}}, {{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,15,0,4,1,1},{11,0,15,0,4,2,1},{11,0,15,0,4,3,1},{11,0,15,0,4,4,1},{11,0,15,0,4,5,1},{11,0,15,0,4,6,1},{11,0,15,0,4,7,1},{11,0,15,0,4,8,1},{11,0,15,0,4,9,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1}}, {{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{6,0,5,0,6,-1,1},{6,0,5,0,6,-1,1},{6,0,5,0,6,-1,1},{6,0,5,0,6,-1,1},{6,0,5,0,6,-1,1},{6,0,5,0,6,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{40,0,17,10,8,-1,1},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0}}, {{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0}}, {{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,21,0,8,-1,0},{40,0,21,0,8,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0}}, - {{29,0,29,9,8,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,22,10,8,-1,1},{29,0,18,10,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, + {{29,0,29,9,8,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,1,22,10,8,-1,1},{29,2,18,10,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1}}, {{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{0,0,21,0,8,-1,1},{0,0,21,0,8,-1,1},{0,0,21,0,8,-1,1},{32,0,14,0,7,-1,1},{32,0,14,0,7,-1,1},{32,0,14,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,12,17,7,-1,1},{42,0,12,17,7,-1,1},{42,0,12,17,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,12,17,7,-1,1},{43,0,12,17,7,-1,1},{43,0,12,17,7,-1,1},{0,0,21,0,8,-1,1},{0,0,21,0,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,12,17,7,-1,1},{44,0,12,17,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{45,0,12,17,7,-1,1},{45,0,12,17,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1}}, {{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,11,-1,1},{23,0,7,0,11,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,4,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,12,17,10,-1,1},{23,0,12,17,10,-1,1},{23,0,10,0,0,-1,1},{23,0,12,17,0,-1,1},{23,0,12,17,0,-1,1},{23,0,12,17,0,-1,1},{23,0,12,17,0,-1,1},{23,0,12,17,0,-1,1},{23,0,12,17,0,-1,1},{23,0,12,17,0,-1,1},{23,0,10,0,0,-1,1},{23,0,10,0,0,-1,1}}, @@ -616,35 +619,35 @@ static const nsCharProps2 sCharProp2Valu {{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,9,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,9,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,9,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,9,0,0,-1,1}}, {{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1}}, {{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{14,0,8,0,0,-1,1},{14,0,24,10,9,-1,1},{14,0,5,0,9,-1,1},{14,0,24,10,9,-1,1}}, {{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{14,0,8,0,0,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{61,0,2,0,12,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1}}, {{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{14,0,9,0,0,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{14,0,8,0,0,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{61,0,2,0,12,-1,1}}, {{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,1,18,10,-1,1},{1,0,1,18,1,-1,1},{1,0,1,18,1,-1,1},{0,0,1,0,10,-1,1},{0,0,1,1,10,-1,1},{0,0,17,10,1,-1,1},{0,0,17,10,9,-1,1},{0,0,17,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,21,10,9,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,1,-1,1},{0,0,22,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,20,10,8,-1,1}}, {{0,0,21,10,8,-1,0},{0,0,21,10,8,-1,0},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,1,-1,1},{0,0,27,9,8,-1,1},{0,0,28,7,8,-1,1},{0,0,1,11,10,-1,1},{0,0,1,14,10,-1,1},{0,0,1,16,10,-1,1},{0,0,1,12,10,-1,1},{0,0,1,15,10,-1,1},{0,0,29,6,9,-1,1},{0,0,21,4,8,-1,0},{0,0,21,4,8,-1,0},{0,0,21,4,8,-1,1},{0,0,21,4,9,-1,1},{0,0,21,4,9,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,8,-1,1},{0,0,21,10,9,-1,1},{0,0,16,10,3,-1,1}}, - {{0,0,16,10,3,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,21,10,8,-1,1},{0,0,25,6,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,16,10,2,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,29,9,9,-1,1}}, - {{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{61,0,2,0,12,-1,0},{0,0,1,19,10,-1,1},{0,0,1,20,10,-1,1},{0,0,1,21,10,-1,1},{0,0,1,22,10,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,15,2,9,0,1},{25,0,6,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{0,0,15,2,9,4,1},{0,0,15,2,9,5,1},{0,0,15,2,9,6,1},{0,0,15,2,9,7,1},{0,0,15,2,9,8,1},{0,0,15,2,9,9,1},{0,0,25,3,9,-1,1},{0,0,25,3,9,-1,1},{0,0,25,10,9,-1,1},{0,0,22,10,9,-1,1},{0,0,18,10,9,-1,1},{25,0,6,0,9,-1,1}}, - {{0,0,15,2,9,0,1},{0,0,15,2,9,1,1},{0,0,15,2,9,2,1},{0,0,15,2,9,3,1},{0,0,15,2,9,4,1},{0,0,15,2,9,5,1},{0,0,15,2,9,6,1},{0,0,15,2,9,7,1},{0,0,15,2,9,8,1},{0,0,15,2,9,9,1},{0,0,25,3,9,-1,1},{0,0,25,3,9,-1,1},{0,0,25,10,9,-1,1},{0,0,22,10,9,-1,1},{0,0,18,10,9,-1,1},{61,0,2,0,12,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, + {{0,0,16,10,3,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,21,10,8,-1,1},{0,0,25,6,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,16,10,2,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,29,9,9,-1,1}}, + {{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{61,0,2,0,12,-1,0},{0,0,1,19,10,-1,1},{0,0,1,20,10,-1,1},{0,0,1,21,10,-1,1},{0,0,1,22,10,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,15,2,9,0,1},{25,0,6,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{0,0,15,2,9,4,1},{0,0,15,2,9,5,1},{0,0,15,2,9,6,1},{0,0,15,2,9,7,1},{0,0,15,2,9,8,1},{0,0,15,2,9,9,1},{0,0,25,3,9,-1,1},{0,0,25,3,9,-1,1},{0,0,25,10,9,-1,1},{0,1,22,10,9,-1,1},{0,2,18,10,9,-1,1},{25,0,6,0,9,-1,1}}, + {{0,0,15,2,9,0,1},{0,0,15,2,9,1,1},{0,0,15,2,9,2,1},{0,0,15,2,9,3,1},{0,0,15,2,9,4,1},{0,0,15,2,9,5,1},{0,0,15,2,9,6,1},{0,0,15,2,9,7,1},{0,0,15,2,9,8,1},{0,0,15,2,9,9,1},{0,0,25,3,9,-1,1},{0,0,25,3,9,-1,1},{0,0,25,10,9,-1,1},{0,1,22,10,9,-1,1},{0,2,18,10,9,-1,1},{61,0,2,0,12,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,9,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{61,0,2,4,12,-1,1}}, {{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,11,17,8,-1,0},{1,0,11,17,8,-1,0},{1,0,11,17,8,-1,0}}, {{1,0,11,17,8,-1,0},{1,0,12,17,7,-1,1},{1,0,11,17,8,-1,0},{1,0,11,17,8,-1,0},{1,0,11,17,8,-1,0},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{0,0,26,10,9,-1,0},{0,0,26,10,9,-1,0},{0,0,9,0,9,-1,1},{0,0,26,10,9,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,9,-1,0},{0,0,26,10,9,-1,0},{0,0,9,0,9,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,9,-1,0},{0,0,5,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,5,0,9,-1,1},{0,0,5,0,9,-1,0},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,5,0,9,-1,0},{0,0,26,10,8,-1,0},{0,0,9,0,9,-1,1},{0,0,26,10,9,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,3,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}}, {{0,0,26,10,9,-1,0},{0,0,26,10,9,-1,0},{0,0,26,10,9,-1,0},{0,0,26,10,8,-1,0},{0,0,9,0,9,-1,1},{0,0,26,10,8,-1,0},{14,0,9,0,9,-1,1},{0,0,26,10,8,-1,0},{0,0,9,0,9,-1,1},{0,0,26,10,8,-1,0},{25,0,9,0,9,-1,1},{25,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,26,4,3,-1,0},{0,0,5,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{25,0,9,0,4,-1,1},{0,0,9,0,9,-1,1},{0,0,5,0,9,-1,1},{0,0,7,0,9,-1,0},{0,0,7,0,9,-1,0},{0,0,7,0,9,-1,0},{0,0,7,0,9,-1,0},{0,0,5,0,9,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,9,-1,0},{0,0,5,0,9,-1,0},{0,0,5,0,9,-1,0},{0,0,9,0,9,-1,0},{0,0,9,0,9,-1,0}}, {{0,0,25,10,9,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,9,0,9,-1,0},{0,0,5,0,9,-1,0},{0,0,5,0,9,-1,0},{0,0,5,0,9,-1,0},{0,0,5,0,9,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{25,0,5,0,4,-1,1},{0,0,26,0,8,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0}}, {{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0}}, {{25,0,14,0,4,-1,0},{25,0,14,0,4,-1,0},{25,0,14,0,4,-1,0},{25,0,9,0,4,-1,0},{25,0,5,0,4,-1,0},{25,0,14,0,4,-1,0},{25,0,14,0,4,-1,0},{25,0,14,0,4,-1,0},{25,0,14,0,4,-1,0},{0,0,15,10,9,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}}, {{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}}, {{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}}, {{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,3,8,-1,1},{0,0,25,4,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,1}}, {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, - {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}}, - {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,22,10,11,-1,3},{0,0,18,10,11,-1,3},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1}}, + {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}}, + {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,1,22,10,11,-1,3},{0,2,18,10,11,-1,3},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1}}, {{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1}}, {{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}}, {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,0,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}}, {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0}}, {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}}, {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0}}, @@ -655,24 +658,24 @@ static const nsCharProps2 sCharProp2Valu {{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,15,10,9,0,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,1,0},{0,0,15,10,8,2,0},{0,0,15,10,8,3,0},{0,0,15,10,8,4,0},{0,0,15,10,8,5,0},{0,0,15,10,8,6,0},{0,0,15,10,8,7,0},{0,0,15,10,8,8,0},{0,0,15,10,8,9,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,0,0}}, {{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}}, {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}}, {{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}}, {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0}}, {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}}, {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}}, {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,0,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}}, - {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,15,10,8,1,0},{0,0,15,10,8,2,0},{0,0,15,10,8,3,0},{0,0,15,10,8,4,0},{0,0,15,10,8,5,0},{0,0,15,10,8,6,0},{0,0,15,10,8,7,0},{0,0,15,10,8,8,0},{0,0,15,10,8,9,0},{0,0,15,10,8,-1,0}}, + {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,15,10,8,1,0},{0,0,15,10,8,2,0},{0,0,15,10,8,3,0},{0,0,15,10,8,4,0},{0,0,15,10,8,5,0},{0,0,15,10,8,6,0},{0,0,15,10,8,7,0},{0,0,15,10,8,8,0},{0,0,15,10,8,9,0},{0,0,15,10,8,-1,0}}, {{0,0,15,10,8,1,0},{0,0,15,10,8,2,0},{0,0,15,10,8,3,0},{0,0,15,10,8,4,0},{0,0,15,10,8,5,0},{0,0,15,10,8,6,0},{0,0,15,10,8,7,0},{0,0,15,10,8,8,0},{0,0,15,10,8,9,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,1,0},{0,0,15,10,8,2,0},{0,0,15,10,8,3,0},{0,0,15,10,8,4,0},{0,0,15,10,8,5,0},{0,0,15,10,8,6,0},{0,0,15,10,8,7,0},{0,0,15,10,8,8,0},{0,0,15,10,8,9,0},{0,0,15,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}}, - {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, - {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, + {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, + {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, {{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1}}, - {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, - {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, - {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, + {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, + {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, + {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, {{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}}, {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}}, {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}}, {{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}}, {{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}}, @@ -688,25 +691,25 @@ static const nsCharProps2 sCharProp2Valu {{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1}}, {{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{61,0,2,0,12,-1,1},{12,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{12,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1}}, {{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1}}, {{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{57,0,6,0,9,-1,1},{57,0,21,0,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{57,0,12,17,5,-1,1}}, {{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{61,0,2,0,12,-1,1}}, {{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1}}, {{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1}}, - {{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,6,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1}}, + {{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,6,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1}}, {{0,0,17,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,22,10,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{61,0,2,0,12,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,9,-1,0}}, {{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0}}, {{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,9,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0}}, {{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0}}, {{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0}}, {{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0}}, - {{0,0,29,9,9,-1,0},{0,0,21,10,8,-1,2},{0,0,21,10,8,-1,2},{0,0,21,10,8,-1,0},{0,0,26,10,8,-1,0},{17,0,6,0,0,-1,0},{0,0,7,0,0,-1,0},{17,0,14,0,0,-1,0},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,17,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,18,10,8,-1,3}}, + {{0,0,29,9,9,-1,0},{0,0,21,10,8,-1,2},{0,0,21,10,8,-1,2},{0,0,21,10,8,-1,0},{0,0,26,10,8,-1,0},{17,0,6,0,0,-1,0},{0,0,7,0,0,-1,0},{17,0,14,0,0,-1,0},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,0,17,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,18,10,8,-1,3}}, {{0,0,26,10,8,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{1,0,12,17,3,-1,0},{1,0,12,17,3,-1,0},{1,0,12,17,3,-1,0},{1,0,12,17,3,-1,0},{18,0,10,0,4,-1,0},{18,0,10,0,4,-1,0},{0,0,17,10,8,-1,3},{0,0,6,0,3,-1,0},{0,0,6,0,3,-1,0},{0,0,6,0,3,-1,0},{0,0,6,0,3,-1,0},{0,0,6,0,3,-1,0},{0,0,26,10,9,-1,0},{0,0,26,10,8,-1,0},{17,0,14,0,9,-1,0},{17,0,14,0,9,-1,0},{17,0,14,0,9,-1,0},{17,0,6,0,3,-1,0},{0,0,7,0,3,-1,0},{0,0,21,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}}, {{61,0,2,0,12,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0}}, {{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0}}, {{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,2},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{1,0,12,17,0,-1,0},{1,0,12,17,0,-1,0},{0,0,24,10,9,-1,2},{0,0,24,10,9,-1,2},{20,0,6,0,0,-1,0},{20,0,6,0,0,-1,0},{20,0,7,0,9,-1,0}}, {{0,0,17,10,1,-1,3},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0}}, {{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0}}, {{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{0,0,21,10,1,-1,0},{0,0,6,0,0,-1,3},{22,0,6,0,0,-1,0},{22,0,6,0,0,-1,0},{22,0,7,0,9,-1,0}}, {{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0}}, @@ -801,23 +804,23 @@ static const nsCharProps2 sCharProp2Valu {{2,0,24,13,8,-1,1},{2,0,24,13,8,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1}}, {{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1}}, {{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1}}, {{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1}}, {{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,23,13,9,-1,1},{2,0,26,10,8,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1}}, {{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,21,10,9,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0}}, {{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{8,0,12,17,3,-1,1},{8,0,12,17,3,-1,1},{0,0,21,10,9,-1,0},{0,0,17,10,9,-1,0},{0,0,17,10,9,-1,0},{0,0,16,10,9,-1,0},{0,0,16,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0}}, - {{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,21,10,8,-1,0},{0,0,21,10,8,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,16,10,9,-1,1},{0,0,16,10,9,-1,1},{0,0,16,10,9,-1,1},{0,0,21,6,9,-1,2},{0,0,21,10,9,-1,2},{0,0,21,6,9,-1,2},{61,0,2,0,12,-1,0},{0,0,21,10,9,-1,0},{0,0,21,6,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,17,10,9,-1,1},{0,0,22,10,9,-1,3},{0,0,18,10,9,-1,3},{0,0,22,10,9,-1,3},{0,0,18,10,9,-1,3},{0,0,22,10,9,-1,3},{0,0,18,10,9,-1,3},{0,0,21,4,9,-1,0}}, + {{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,21,10,8,-1,0},{0,0,21,10,8,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,16,10,9,-1,1},{0,0,16,10,9,-1,1},{0,0,16,10,9,-1,1},{0,0,21,6,9,-1,2},{0,0,21,10,9,-1,2},{0,0,21,6,9,-1,2},{61,0,2,0,12,-1,0},{0,0,21,10,9,-1,0},{0,0,21,6,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,17,10,9,-1,1},{0,1,22,10,9,-1,3},{0,2,18,10,9,-1,3},{0,1,22,10,9,-1,3},{0,2,18,10,9,-1,3},{0,1,22,10,9,-1,3},{0,2,18,10,9,-1,3},{0,0,21,4,9,-1,0}}, {{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,25,3,9,-1,0},{0,0,17,3,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{61,0,2,0,12,-1,0},{0,0,21,10,9,-1,0},{0,0,23,4,9,-1,0},{0,0,21,4,9,-1,0},{0,0,21,10,9,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,3,-1,1},{2,0,7,13,9,-1,1},{61,0,2,13,12,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1}}, {{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{0,0,1,18,10,-1,1}}, - {{61,0,2,0,12,-1,1},{0,0,21,10,9,-1,2},{0,0,21,10,9,-1,0},{0,0,21,4,9,-1,0},{0,0,23,4,9,-1,0},{0,0,21,4,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,22,10,9,-1,3},{0,0,18,10,9,-1,3},{0,0,21,10,9,-1,0},{0,0,25,3,9,-1,0},{0,0,21,6,9,-1,2},{0,0,17,3,9,-1,1},{0,0,21,6,9,-1,2},{0,0,21,6,9,-1,0},{0,0,13,2,9,0,0},{0,0,13,2,9,1,0},{0,0,13,2,9,2,0},{0,0,13,2,9,3,0},{0,0,13,2,9,4,0},{0,0,13,2,9,5,0},{0,0,13,2,9,6,0},{0,0,13,2,9,7,0},{0,0,13,2,9,8,0},{0,0,13,2,9,9,0},{0,0,21,6,9,-1,3},{0,0,21,10,9,-1,3},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,21,10,9,-1,2}}, - {{0,0,21,10,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{0,0,22,10,9,-1,3},{0,0,21,10,9,-1,0},{0,0,18,10,9,-1,3},{0,0,24,10,9,-1,0},{0,0,16,10,9,-1,3}}, - {{0,0,24,10,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{0,0,22,10,9,-1,3},{0,0,25,10,9,-1,3},{0,0,18,10,9,-1,3},{0,0,25,10,9,-1,3},{0,0,22,10,9,-1,3}}, - {{0,0,18,10,9,-1,3},{0,0,21,10,9,-1,1},{0,0,22,10,9,-1,1},{0,0,18,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{0,0,6,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1}}, + {{61,0,2,0,12,-1,1},{0,0,21,10,9,-1,2},{0,0,21,10,9,-1,0},{0,0,21,4,9,-1,0},{0,0,23,4,9,-1,0},{0,0,21,4,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,1,22,10,9,-1,3},{0,2,18,10,9,-1,3},{0,0,21,10,9,-1,0},{0,0,25,3,9,-1,0},{0,0,21,6,9,-1,2},{0,0,17,3,9,-1,1},{0,0,21,6,9,-1,2},{0,0,21,6,9,-1,0},{0,0,13,2,9,0,0},{0,0,13,2,9,1,0},{0,0,13,2,9,2,0},{0,0,13,2,9,3,0},{0,0,13,2,9,4,0},{0,0,13,2,9,5,0},{0,0,13,2,9,6,0},{0,0,13,2,9,7,0},{0,0,13,2,9,8,0},{0,0,13,2,9,9,0},{0,0,21,6,9,-1,3},{0,0,21,10,9,-1,3},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,21,10,9,-1,2}}, + {{0,0,21,10,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{0,1,22,10,9,-1,3},{0,0,21,10,9,-1,0},{0,2,18,10,9,-1,3},{0,0,24,10,9,-1,0},{0,0,16,10,9,-1,3}}, + {{0,0,24,10,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{0,1,22,10,9,-1,3},{0,0,25,10,9,-1,3},{0,2,18,10,9,-1,3},{0,0,25,10,9,-1,3},{0,1,22,10,9,-1,3}}, + {{0,2,18,10,9,-1,3},{0,0,21,10,9,-1,1},{0,1,22,10,9,-1,1},{0,2,18,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{0,0,6,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1}}, {{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{0,0,6,0,9,-1,1},{0,0,6,0,9,-1,1}}, {{18,0,7,0,10,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{61,0,2,0,12,-1,1}}, {{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{0,0,23,4,9,-1,0},{0,0,23,4,9,-1,0},{0,0,25,10,9,-1,0},{0,0,24,10,9,-1,3},{0,0,26,10,9,-1,0},{0,0,23,4,9,-1,0},{0,0,23,4,9,-1,0},{61,0,2,0,12,-1,0},{0,0,26,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,26,10,9,-1,1},{0,0,26,10,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{0,0,1,10,8,-1,1},{0,0,1,10,8,-1,1},{0,0,1,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1}}, {{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{51,0,7,0,7,-1,1}}, {{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}}, {{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1}},
--- a/intl/unicharutil/util/nsUnicodeScriptCodes.h +++ b/intl/unicharutil/util/nsUnicodeScriptCodes.h @@ -6,17 +6,17 @@ /* * Derived from the Unicode Character Database by genUnicodePropertyData.pl * * For Unicode terms of use, see http://www.unicode.org/terms_of_use.html */ /* - * Created on Thu Sep 3 14:42:28 2015 from UCD data files with version info: + * Created on Tue Nov 17 07:34:16 2015 from UCD data files with version info: * # Date: 2015-06-16, 20:24:00 GMT [KW] # # Unicode Character Database # Copyright (c) 1991-2015 Unicode, Inc. # For terms of use, see http://www.unicode.org/terms_of_use.html # @@ -33,16 +33,19 @@ Standard. # Scripts-8.0.0.txt # Date: 2015-03-11, 22:29:42 GMT [MD] # BidiMirroring-8.0.0.txt # Date: 2015-01-20, 18:30:00 GMT [KW, LI] +# BidiBrackets-8.0.0.txt +# Date: 2015-01-20, 19:00:00 GMT [AG, LI, KW] + # HangulSyllableType-8.0.0.txt # Date: 2014-12-16, 23:07:45 GMT [MD] # File: xidmodifications.txt # Version: 8.0.0 # Generated: 2015-05-17, 03:09:04 GMT # @@ -67,17 +70,17 @@ struct nsCharProps1 { unsigned char mHangulType:3; unsigned char mCombiningClass:8; }; struct nsCharProps2 { unsigned char mScriptCode:8; - unsigned char mUnused:3; + unsigned char mPairedBracketType:3; // only 2 bits actually needed unsigned char mCategory:5; unsigned char mBidiCategory:5; unsigned char mXidmod:4; signed char mNumericValue:5; unsigned char mVertOrient:2; };
--- a/js/src/builtin/ModuleObject.h +++ b/js/src/builtin/ModuleObject.h @@ -172,16 +172,17 @@ class ModuleNamespaceObject : public Pro bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id, MutableHandleValue vp) const override; bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver, ObjectOpResult& result) const override; static const char family; }; + public: static const ProxyHandler proxyHandler; }; typedef Rooted<ModuleNamespaceObject*> RootedModuleNamespaceObject; typedef Handle<ModuleNamespaceObject*> HandleModuleNamespaceObject; struct FunctionDeclaration { @@ -311,9 +312,16 @@ class MOZ_STACK_CLASS ModuleBuilder }; JSObject* InitModuleClass(JSContext* cx, HandleObject obj); JSObject* InitImportEntryClass(JSContext* cx, HandleObject obj); JSObject* InitExportEntryClass(JSContext* cx, HandleObject obj); } // namespace js +template<> +inline bool +JSObject::is<js::ModuleNamespaceObject>() const +{ + return js::IsDerivedProxyObject(this, &js::ModuleNamespaceObject::proxyHandler); +} + #endif /* builtin_ModuleObject_h */
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/lib/dummyModuleResolveHook.js @@ -0,0 +1,14 @@ +/* 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/. */ + +// A dummy implementation of the module resolve hook used by module tests. This +// implements the bare minimum necessary to allow modules to refer to each +// other. + +let moduleRepo = {}; +setModuleResolveHook(function(module, specifier) { + if (specifier in moduleRepo) + return moduleRepo[specifier]; + throw "Module '" + specifier + "' not found"; +});
--- a/js/src/jit-test/tests/modules/ambiguous-star-export.js +++ b/js/src/jit-test/tests/modules/ambiguous-star-export.js @@ -1,38 +1,31 @@ // Test ambigious export * statements. "use strict"; load(libdir + "asserts.js"); +load(libdir + "dummyModuleResolveHook.js"); function checkModuleEval(source, result) { let m = parseModule(source); m.declarationInstantiation(); assertEq(m.evaluation(), result); } function checkModuleSyntaxError(source) { let m = parseModule(source); assertThrowsInstanceOf(() => m.declarationInstantiation(), SyntaxError); } -let moduleRepo = new Map(); -setModuleResolveHook(function(module, specifier) { - if (specifier in moduleRepo) - return moduleRepo[specifier]; - throw "Module " + specifier + " not found"; -}); - let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;"); let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;"); let c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';"); -let ms = [a, b, c]; -ms.map((m) => m.declarationInstantiation()); -ms.map((m) => m.evaluation(), moduleRepo.values()); +c.declarationInstantiation(); +c.evaluation(); // Check importing/exporting non-ambiguous name works. checkModuleEval("import { a } from 'c'; a;", 1); checkModuleEval("export { a } from 'c';", undefined); // Check importing/exporting ambiguous name is a syntax error. checkModuleSyntaxError("import { b } from 'c';"); checkModuleSyntaxError("export { b } from 'c';");
--- a/js/src/jit-test/tests/modules/bug1210391.js +++ b/js/src/jit-test/tests/modules/bug1210391.js @@ -1,11 +1,8 @@ -let moduleRepo = new Map(); -setModuleResolveHook(function(module, specifier) { - return moduleRepo[specifier]; -}); +load(libdir + "dummyModuleResolveHook.js"); let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;"); let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;"); let c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';"); let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;"); d.declarationInstantiation(); d.evaluation();
--- a/js/src/jit-test/tests/modules/debugger-frames.js +++ b/js/src/jit-test/tests/modules/debugger-frames.js @@ -52,21 +52,21 @@ dbg.onDebuggerStatement = function (fram // followed by the global. assertEq(env.parent.type, 'declarative'); assertEq(env.parent.parent.type, 'object'); assertEq(env.parent.parent.parent, null); }; f = g2.eval( ` - let moduleRepo = new Map(); + let moduleRepo = {}; setModuleResolveHook(function(module, specifier) { if (specifier in moduleRepo) return moduleRepo[specifier]; - throw "Module " + specifier + " not found"; + throw "Module '" + specifier + "' not found"; }); // Set up a module to import from. a = moduleRepo['a'] = parseModule( \` export var a = 1; export let b = 2; export const c = 3;
--- a/js/src/jit-test/tests/modules/import-namespace.js +++ b/js/src/jit-test/tests/modules/import-namespace.js @@ -1,21 +1,15 @@ // Test importing module namespaces "use strict"; load(libdir + "asserts.js"); load(libdir + "iteration.js"); - -let moduleRepo = new Map(); -setModuleResolveHook(function(module, specifier) { - if (specifier in moduleRepo) - return moduleRepo[specifier]; - throw "Module " + specifier + " not found"; -}); +load(libdir + "dummyModuleResolveHook.js"); function parseAndEvaluate(source) { let m = parseModule(source); m.declarationInstantiation(); return m.evaluation(); } function testHasNames(names, expected) {
--- a/js/src/jit-test/tests/modules/many-exports.js +++ b/js/src/jit-test/tests/modules/many-exports.js @@ -1,19 +1,14 @@ // Test many exports. +load(libdir + "dummyModuleResolveHook.js"); + const count = 1024; -let moduleRepo = {}; -setModuleResolveHook(function(module, specifier) { - if (specifier in moduleRepo) - return moduleRepo[specifier]; - throw "Module " + specifier + " not found"; -}); - let s = ""; for (let i = 0; i < count; i++) s += "export let e" + i + " = " + (i * i) + ";\n"; let a = moduleRepo['a'] = parseModule(s); let b = moduleRepo['b'] = parseModule("import * as ns from 'a'"); b.declarationInstantiation();
--- a/js/src/jit-test/tests/modules/many-imports.js +++ b/js/src/jit-test/tests/modules/many-imports.js @@ -1,19 +1,14 @@ // Test importing an import many times. +load(libdir + "dummyModuleResolveHook.js"); + const count = 1024; -let moduleRepo = {}; -setModuleResolveHook(function(module, specifier) { - if (specifier in moduleRepo) - return moduleRepo[specifier]; - throw "Module " + specifier + " not found"; -}); - let a = moduleRepo['a'] = parseModule("export let a = 1;"); let s = ""; for (let i = 0; i < count; i++) { s += "import { a as i" + i + " } from 'a';\n"; s += "assertEq(i" + i + ", 1);\n"; } let b = moduleRepo['b'] = parseModule(s);
--- a/js/src/jit-test/tests/modules/many-namespace-imports.js +++ b/js/src/jit-test/tests/modules/many-namespace-imports.js @@ -1,19 +1,14 @@ // Test importing a namespace many times. +load(libdir + "dummyModuleResolveHook.js"); + const count = 1024; -let moduleRepo = {}; -setModuleResolveHook(function(module, specifier) { - if (specifier in moduleRepo) - return moduleRepo[specifier]; - throw "Module " + specifier + " not found"; -}); - let a = moduleRepo['a'] = parseModule("export let a = 1;"); let s = ""; for (let i = 0; i < count; i++) { s += "import * as ns" + i + " from 'a';\n"; s += "assertEq(ns" + i + ".a, 1);\n"; } let b = moduleRepo['b'] = parseModule(s);
--- a/js/src/jit-test/tests/modules/module-declaration-instantiation.js +++ b/js/src/jit-test/tests/modules/module-declaration-instantiation.js @@ -1,30 +1,25 @@ // Exercise ModuleDeclarationInstantiation() operation. +load(libdir + "dummyModuleResolveHook.js"); + function testModuleEnvironment(module, expected) { var actual = getModuleEnvironmentNames(module).sort(); assertEq(actual.length, expected.length); for (var i = 0; i < actual.length; i++) { assertEq(actual[i], expected[i]); } } // Check the environment of an empty module. let m = parseModule(""); m.declarationInstantiation(); testModuleEnvironment(m, []); -let moduleRepo = new Map(); -setModuleResolveHook(function(module, specifier) { - if (specifier in moduleRepo) - return moduleRepo[specifier]; - throw "Module " + specifier + " not found"; -}); - let a = moduleRepo['a'] = parseModule("var x = 1; export { x };"); let b = moduleRepo['b'] = parseModule("import { x as y } from 'a';"); a.declarationInstantiation(); b.declarationInstantiation(); testModuleEnvironment(a, ['x']); testModuleEnvironment(b, ['y']);
--- a/js/src/jit-test/tests/modules/module-evaluation.js +++ b/js/src/jit-test/tests/modules/module-evaluation.js @@ -1,18 +1,12 @@ // Exercise ModuleEvaluation() concrete method. load(libdir + "asserts.js"); - -let moduleRepo = new Map(); -setModuleResolveHook(function(module, specifier) { - if (specifier in moduleRepo) - return moduleRepo[specifier]; - throw "Module " + specifier + " not found"; -}); +load(libdir + "dummyModuleResolveHook.js"); function parseAndEvaluate(source) { let m = parseModule(source); m.declarationInstantiation(); return m.evaluation(); } // Check the evaluation of an empty module succeeds.
--- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -83,17 +83,17 @@ real(Uint8Array, 23, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint8)) \ real(Int16Array, 24, InitViaClassSpec, TYPED_ARRAY_CLASP(Int16)) \ real(Uint16Array, 25, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint16)) \ real(Int32Array, 26, InitViaClassSpec, TYPED_ARRAY_CLASP(Int32)) \ real(Uint32Array, 27, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint32)) \ real(Float32Array, 28, InitViaClassSpec, TYPED_ARRAY_CLASP(Float32)) \ real(Float64Array, 29, InitViaClassSpec, TYPED_ARRAY_CLASP(Float64)) \ real(Uint8ClampedArray, 30, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint8Clamped)) \ - real(Proxy, 31, InitProxyClass, OCLASP(Proxy)) \ + real(Proxy, 31, InitProxyClass, js::ProxyClassPtr) \ real(WeakMap, 32, InitWeakMapClass, OCLASP(WeakMap)) \ real(Map, 33, InitMapClass, OCLASP(Map)) \ real(Set, 34, InitSetClass, OCLASP(Set)) \ real(DataView, 35, InitDataViewClass, OCLASP(DataView)) \ real(Symbol, 36, InitSymbolClass, OCLASP(Symbol)) \ IF_SAB(real,imaginary)(SharedArrayBuffer, 37, InitSharedArrayBufferClass, &js::SharedArrayBufferObject::protoClass) \ IF_INTL(real,imaginary) (Intl, 38, InitIntlClass, CLASP(Intl)) \ IF_BDATA(real,imaginary)(TypedObject, 39, InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \
--- a/js/src/proxy/DeadObjectProxy.cpp +++ b/js/src/proxy/DeadObjectProxy.cpp @@ -146,15 +146,13 @@ DeadObjectProxy::regexp_toShared(JSConte { ReportDead(cx); return false; } const char DeadObjectProxy::family = 0; const DeadObjectProxy DeadObjectProxy::singleton; - bool js::IsDeadProxyObject(JSObject* obj) { - return obj->is<ProxyObject>() && - obj->as<ProxyObject>().handler() == &DeadObjectProxy::singleton; + return IsDerivedProxyObject(obj, &DeadObjectProxy::singleton); }
--- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -712,20 +712,20 @@ js::proxy_GetElements(JSContext* cx, Han } JSString* js::proxy_FunToString(JSContext* cx, HandleObject proxy, unsigned indent) { return Proxy::fun_toString(cx, proxy, indent); } -const Class js::ProxyObject::class_ = +const Class js::ProxyObject::proxyClass = PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy)); -const Class* const js::ProxyClassPtr = &js::ProxyObject::class_; +const Class* const js::ProxyClassPtr = &js::ProxyObject::proxyClass; JS_FRIEND_API(JSObject*) js::NewProxyObject(JSContext* cx, const BaseProxyHandler* handler, HandleValue priv, JSObject* proto_, const ProxyOptions& options) { if (options.lazyProto()) { MOZ_ASSERT(!proto_); proto_ = TaggedProto::LazyProto; @@ -733,17 +733,17 @@ js::NewProxyObject(JSContext* cx, const return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), options); } void ProxyObject::renew(JSContext* cx, const BaseProxyHandler* handler, Value priv) { MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this)); - MOZ_ASSERT(getClass() == &ProxyObject::class_); + MOZ_ASSERT(getClass() == &ProxyObject::proxyClass); MOZ_ASSERT(!IsWindowProxy(this)); MOZ_ASSERT(hasLazyPrototype()); setHandler(handler); setCrossCompartmentPrivate(priv); setExtra(0, UndefinedValue()); setExtra(1, UndefinedValue()); }
--- a/js/src/vm/ProxyObject.h +++ b/js/src/vm/ProxyObject.h @@ -99,27 +99,35 @@ class ProxyObject : public JSObject static unsigned grayLinkExtraSlot(JSObject* obj); void renew(JSContext* cx, const BaseProxyHandler* handler, Value priv); static void trace(JSTracer* trc, JSObject* obj); void nuke(const BaseProxyHandler* handler); - static const Class class_; + // There is no class_ member to force specialization of JSObject::is<T>(). + // The implementation in JSObject is incorrect for proxies since it doesn't + // take account of the handler type. + static const Class proxyClass; }; +bool IsDerivedProxyObject(const JSObject* obj, const js::BaseProxyHandler* handler); + } // namespace js -// Note: the following |JSObject::is<T>| methods are implemented in terms of -// the Is*Proxy() friend API functions to ensure the implementations are tied -// together. The exception is |JSObject::is<js::OuterWindowProxyObject>() -// const|, which uses the standard template definition, because there is no -// IsOuterWindowProxy() function in the friend API. - template<> inline bool JSObject::is<js::ProxyObject>() const { + // Note: this method is implemented in terms of the IsProxy() friend API + // functions to ensure the implementations are tied together. + // Note 2: this specialization isn't used for subclasses of ProxyObject + // which must supply their own implementation. return js::IsProxy(const_cast<JSObject*>(this)); } +inline bool +js::IsDerivedProxyObject(const JSObject* obj, const js::BaseProxyHandler* handler) { + return obj->is<js::ProxyObject>() && obj->as<js::ProxyObject>().handler() == handler; +} + #endif /* vm_ProxyObject_h */
--- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -2239,16 +2239,23 @@ class DebugScopeProxy : public BaseProxy ObjectOpResult& result) const override { return result.fail(JSMSG_CANT_DELETE); } }; } /* anonymous namespace */ +template<> +bool +JSObject::is<js::DebugScopeObject>() const +{ + return IsDerivedProxyObject(this, &DebugScopeProxy::singleton); +} + const char DebugScopeProxy::family = 0; const DebugScopeProxy DebugScopeProxy::singleton; /* static */ DebugScopeObject* DebugScopeObject::create(JSContext* cx, ScopeObject& scope, HandleObject enclosing) { MOZ_ASSERT(scope.compartment() == cx->compartment()); MOZ_ASSERT(!enclosing->is<ScopeObject>()); @@ -2328,23 +2335,16 @@ DebugScopeObject::isOptimizedOut() const return !s.as<CallObject>().isForEval() && !s.as<CallObject>().callee().needsCallObject() && !maybeSnapshot(); } return false; } -bool -js::IsDebugScopeSlow(ProxyObject* proxy) -{ - MOZ_ASSERT(proxy->hasClass(&ProxyObject::class_)); - return proxy->handler() == &DebugScopeProxy::singleton; -} - /*****************************************************************************/ DebugScopes::DebugScopes(JSContext* cx) : proxiedScopes(cx), missingScopes(cx->runtime()), liveScopes(cx->runtime()) {}
--- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -1260,19 +1260,16 @@ class DebugScopes static void onPopCall(AbstractFramePtr frame, JSContext* cx); static void onPopBlock(JSContext* cx, const ScopeIter& si); static void onPopBlock(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc); static void onPopWith(AbstractFramePtr frame); static void onPopStrictEvalScope(AbstractFramePtr frame); static void onCompartmentUnsetIsDebuggee(JSCompartment* c); }; -extern bool -IsDebugScopeSlow(ProxyObject* proxy); - } /* namespace js */ template<> inline bool JSObject::is<js::NestedScopeObject>() const { return is<js::BlockObject>() || is<js::StaticWithObject>() || @@ -1294,23 +1291,18 @@ JSObject::is<js::ScopeObject>() const return is<js::LexicalScopeBase>() || is<js::DeclEnvObject>() || is<js::NestedScopeObject>() || is<js::RuntimeLexicalErrorObject>() || is<js::NonSyntacticVariablesObject>(); } template<> -inline bool -JSObject::is<js::DebugScopeObject>() const -{ - // Note: don't use is<ProxyObject>() here -- it also matches subclasses! - return hasClass(&js::ProxyObject::class_) && - IsDebugScopeSlow(&const_cast<JSObject*>(this)->as<js::ProxyObject>()); -} +bool +JSObject::is<js::DebugScopeObject>() const; template<> inline bool JSObject::is<js::ClonedBlockObject>() const { return is<js::BlockObject>() && !!getProto(); }
--- a/layout/base/nsBidi.cpp +++ b/layout/base/nsBidi.cpp @@ -36,28 +36,41 @@ enum { RLO = eCharType_RightToLeftOverride, PDF = eCharType_PopDirectionalFormat, NSM = eCharType_DirNonSpacingMark, BN = eCharType_BoundaryNeutral, LRI = eCharType_LeftToRightIsolate, RLI = eCharType_RightToLeftIsolate, FSI = eCharType_FirstStrongIsolate, PDI = eCharType_PopDirectionalIsolate, + ENL, /* EN after W7 */ /* 23 */ + ENR, /* EN not subject to W7 */ /* 24 */ dirPropCount }; +#define IS_STRONG_TYPE(dirProp) ((dirProp) <= R || (dirProp) == AL) + /* to avoid some conditional statements, use tiny constant arrays */ static Flags flagLR[2]={ DIRPROP_FLAG(L), DIRPROP_FLAG(R) }; static Flags flagE[2]={ DIRPROP_FLAG(LRE), DIRPROP_FLAG(RLE) }; static Flags flagO[2]={ DIRPROP_FLAG(LRO), DIRPROP_FLAG(RLO) }; #define DIRPROP_FLAG_LR(level) flagLR[(level)&1] #define DIRPROP_FLAG_E(level) flagE[(level)&1] #define DIRPROP_FLAG_O(level) flagO[(level)&1] +#define NO_OVERRIDE(level) ((level)&~NSBIDI_LEVEL_OVERRIDE) + +static inline uint8_t +DirFromStrong(uint8_t aDirProp) +{ + MOZ_ASSERT(IS_STRONG_TYPE(aDirProp)); + return aDirProp == L ? L : R; +} + /* * General implementation notes: * * Throughout the implementation, there are comments like (W2) that refer to * rules of the Bidi algorithm in its version 5, in this example to the second * rule of the resolution of weak types. * * For handling surrogate pairs, where two UChar's form one "abstract" (or UTF-32) @@ -133,19 +146,16 @@ static Flags flagO[2]={ DIRPROP_FLAG(LRO * the flags variable. * * If there are no White Space types in the paragraph, then * (L1) is not necessary in AdjustWSLevels(). */ nsBidi::nsBidi() { Init(); - - mMayAllocateText=true; - mMayAllocateRuns=true; } nsBidi::~nsBidi() { Free(); } void nsBidi::Init() @@ -169,57 +179,49 @@ void nsBidi::Init() mLevels=nullptr; mRuns=nullptr; mIsolates=nullptr; mDirPropsMemory=nullptr; mLevelsMemory=nullptr; mRunsMemory=nullptr; mIsolatesMemory=nullptr; - - mMayAllocateText=false; - mMayAllocateRuns=false; } /* - * We are allowed to allocate memory if aMemory==nullptr or - * aMayAllocate==true for each array that we need. + * We are allowed to allocate memory if aMemory==nullptr + * for each array that we need. * We also try to grow and shrink memory as needed if we * allocate it. * * Assume aSizeNeeded>0. * If *aMemory!=nullptr, then assume *aSize>0. * * ### this realloc() may unnecessarily copy the old data, * which we know we don't need any more; * is this the best way to do this?? */ -bool nsBidi::GetMemory(void **aMemory, size_t *aSize, bool aMayAllocate, size_t aSizeNeeded) +/*static*/ +bool +nsBidi::GetMemory(void **aMemory, size_t *aSize, size_t aSizeNeeded) { /* check for existing memory */ if(*aMemory==nullptr) { /* we need to allocate memory */ - if(!aMayAllocate) { - return false; + *aMemory=malloc(aSizeNeeded); + if (*aMemory!=nullptr) { + *aSize=aSizeNeeded; + return true; } else { - *aMemory=malloc(aSizeNeeded); - if (*aMemory!=nullptr) { - *aSize=aSizeNeeded; - return true; - } else { - *aSize=0; - return false; - } + *aSize=0; + return false; } } else { /* there is some memory, is it enough or too much? */ - if(aSizeNeeded>*aSize && !aMayAllocate) { - /* not enough memory, and we must not allocate */ - return false; - } else if(aSizeNeeded!=*aSize && aMayAllocate) { + if(aSizeNeeded!=*aSize) { /* we may try to grow or shrink */ void *memory=realloc(*aMemory, aSizeNeeded); if(memory!=nullptr) { *aMemory=memory; *aSize=aSizeNeeded; return true; } else { @@ -243,17 +245,17 @@ void nsBidi::Free() mRunsMemory = nullptr; free(mIsolatesMemory); mIsolatesMemory = nullptr; } /* SetPara ------------------------------------------------------------ */ nsresult nsBidi::SetPara(const char16_t *aText, int32_t aLength, - nsBidiLevel aParaLevel, nsBidiLevel *aEmbeddingLevels) + nsBidiLevel aParaLevel) { nsBidiDirection direction; /* check the argument values */ if(aText==nullptr || ((NSBIDI_MAX_EXPLICIT_LEVEL<aParaLevel) && !IS_DEFAULT_LEVEL(aParaLevel)) || aLength<-1 ) { @@ -297,42 +299,32 @@ nsresult nsBidi::SetPara(const char16_t */ if(GETDIRPROPSMEMORY(aLength)) { mDirProps=mDirPropsMemory; GetDirProps(aText); } else { return NS_ERROR_OUT_OF_MEMORY; } - /* are explicit levels specified? */ - if(aEmbeddingLevels==nullptr) { - /* no: determine explicit levels according to the (Xn) rules */\ - if(GETLEVELSMEMORY(aLength)) { - mLevels=mLevelsMemory; - ResolveExplicitLevels(&direction); - } else { - return NS_ERROR_OUT_OF_MEMORY; - } + /* determine explicit levels according to the (Xn) rules */ + if(GETLEVELSMEMORY(aLength)) { + mLevels=mLevelsMemory; + ResolveExplicitLevels(&direction, aText); } else { - /* set BN for all explicit codes, check that all levels are aParaLevel..NSBIDI_MAX_EXPLICIT_LEVEL */ - mLevels=aEmbeddingLevels; - nsresult rv = CheckExplicitLevels(&direction); - if(NS_FAILED(rv)) { - return rv; - } + return NS_ERROR_OUT_OF_MEMORY; } /* allocate isolate memory */ if (mIsolateCount <= SIMPLE_ISOLATES_SIZE) { mIsolates = mSimpleIsolates; } else { if (mIsolateCount * sizeof(Isolate) <= mIsolatesSize) { mIsolates = mIsolatesMemory; } else { - if (GETINITIALISOLATESMEMORY(mIsolateCount)) { + if (GETISOLATESMEMORY(mIsolateCount)) { mIsolates = mIsolatesMemory; } else { return NS_ERROR_OUT_OF_MEMORY; } } } mIsolateCount = -1; /* current isolates stack entry == none */ @@ -363,17 +355,17 @@ nsresult nsBidi::SetPara(const char16_t * then we can treat the entire paragraph as one run. * Otherwise, we need to perform the following rules on runs of * the text with the same embedding levels. (X10) * "Significant" explicit level codes are ones that actually * affect non-BN characters. * Examples for "insignificant" ones are empty embeddings * LRE-PDF, LRE-RLE-PDF-PDF, etc. */ - if(aEmbeddingLevels==nullptr && !(mFlags&DIRPROP_FLAG_MULTI_RUNS)) { + if(!(mFlags&DIRPROP_FLAG_MULTI_RUNS)) { ResolveImplicitLevels(0, aLength, GET_LR_FROM_LEVEL(mParaLevel), GET_LR_FROM_LEVEL(mParaLevel)); } else { /* sor, eor: start and end types of same-level-run */ nsBidiLevel *levels=mLevels; int32_t start, limit=0; nsBidiLevel level, nextLevel; @@ -580,16 +572,344 @@ void nsBidi::GetDirProps(const char16_t stackLast--; } flags|=DIRPROP_FLAG_LR(mParaLevel); mFlags = flags; } +/* Functions for handling paired brackets ----------------------------------- */ + +/* In the mIsoRuns array, the first entry is used for text outside of any + isolate sequence. Higher entries are used for each more deeply nested + isolate sequence. + mIsoRunLast is the index of the last used entry. + The mOpenings array is used to note the data of opening brackets not yet + matched by a closing bracket, or matched but still susceptible to change + level. + Each isoRun entry contains the index of the first and + one-after-last openings entries for pending opening brackets it + contains. The next mOpenings entry to use is the one-after-last of the + most deeply nested isoRun entry. + mIsoRuns entries also contain their current embedding level and the bidi + class of the last-encountered strong character, since these will be needed + to resolve the level of paired brackets. */ + +nsBidi::BracketData::BracketData(const nsBidi *aBidi) +{ + mIsoRunLast = 0; + mIsoRuns[0].start = 0; + mIsoRuns[0].limit = 0; + mIsoRuns[0].level = aBidi->mParaLevel; + mIsoRuns[0].lastStrong = mIsoRuns[0].lastBase = mIsoRuns[0].contextDir = + GET_LR_FROM_LEVEL(aBidi->mParaLevel); + mIsoRuns[0].contextPos = 0; + mOpenings = mSimpleOpenings; + mOpeningsCount = SIMPLE_OPENINGS_COUNT; + mOpeningsMemory = nullptr; +} + +nsBidi::BracketData::~BracketData() +{ + free(mOpeningsMemory); +} + +/* LRE, LRO, RLE, RLO, PDF */ +void +nsBidi::BracketData::ProcessBoundary(int32_t aLastDirControlCharPos, + nsBidiLevel aContextLevel, + nsBidiLevel aEmbeddingLevel, + const DirProp* aDirProps) +{ + IsoRun& lastIsoRun = mIsoRuns[mIsoRunLast]; + if (DIRPROP_FLAG(aDirProps[aLastDirControlCharPos]) & MASK_ISO) { /* after an isolate */ + return; + } + if (NO_OVERRIDE(aEmbeddingLevel) > NO_OVERRIDE(aContextLevel)) { /* not PDF */ + aContextLevel = aEmbeddingLevel; + } + lastIsoRun.limit = lastIsoRun.start; + lastIsoRun.level = aEmbeddingLevel; + lastIsoRun.lastStrong = lastIsoRun.lastBase = lastIsoRun.contextDir = + GET_LR_FROM_LEVEL(aContextLevel); + lastIsoRun.contextPos = aLastDirControlCharPos; +} + +/* LRI or RLI */ +void +nsBidi::BracketData::ProcessLRI_RLI(nsBidiLevel aLevel) +{ + MOZ_ASSERT(mIsoRunLast <= NSBIDI_MAX_EXPLICIT_LEVEL); + IsoRun& lastIsoRun = mIsoRuns[mIsoRunLast]; + lastIsoRun.lastBase = O_N; + IsoRun& currIsoRun = mIsoRuns[++mIsoRunLast]; + currIsoRun.start = currIsoRun.limit = lastIsoRun.limit; + currIsoRun.level = aLevel; + currIsoRun.lastStrong = currIsoRun.lastBase = currIsoRun.contextDir = + GET_LR_FROM_LEVEL(aLevel); + currIsoRun.contextPos = 0; +} + +/* PDI */ +void +nsBidi::BracketData::ProcessPDI() +{ + mIsoRuns[mIsoRunLast].lastBase = O_N; +} + +/* newly found opening bracket: create an openings entry */ +bool /* return true if success */ +nsBidi::BracketData::AddOpening(char16_t aMatch, int32_t aPosition) +{ + IsoRun& lastIsoRun = mIsoRuns[mIsoRunLast]; + if (lastIsoRun.limit >= mOpeningsCount) { /* no available new entry */ + if (!GETOPENINGSMEMORY(lastIsoRun.limit * 2)) { + return false; + } + if (mOpenings == mSimpleOpenings) { + memcpy(mOpeningsMemory, mSimpleOpenings, + SIMPLE_OPENINGS_COUNT * sizeof(Opening)); + } + mOpenings = mOpeningsMemory; /* may have changed */ + mOpeningsCount = mOpeningsSize / sizeof(Opening); + } + Opening& o = mOpenings[lastIsoRun.limit]; + o.position = aPosition; + o.match = aMatch; + o.contextDir = lastIsoRun.contextDir; + o.contextPos = lastIsoRun.contextPos; + o.flags = 0; + lastIsoRun.limit++; + return true; +} + +/* change N0c1 to N0c2 when a preceding bracket is assigned the embedding level */ +void +nsBidi::BracketData::FixN0c(int32_t aOpeningIndex, int32_t aNewPropPosition, + DirProp aNewProp, DirProp* aDirProps) +{ + /* This function calls itself recursively */ + IsoRun& lastIsoRun = mIsoRuns[mIsoRunLast]; + for (int32_t k = aOpeningIndex + 1; k < lastIsoRun.limit; k++) { + Opening& o = mOpenings[k]; + if (o.match >= 0) { /* not an N0c match */ + continue; + } + if (aNewPropPosition < o.contextPos) { + break; + } + int32_t openingPosition = o.position; + if (aNewPropPosition >= openingPosition) { + continue; + } + if (aNewProp == o.contextDir) { + break; + } + aDirProps[openingPosition] = aNewProp; + int32_t closingPosition = -(o.match); + aDirProps[closingPosition] = aNewProp; + o.match = 0; /* prevent further changes */ + FixN0c(k, openingPosition, aNewProp, aDirProps); + FixN0c(k, closingPosition, aNewProp, aDirProps); + } +} + +/* process closing bracket */ +DirProp /* return L or R if N0b or N0c, ON if N0d */ +nsBidi::BracketData::ProcessClosing(int32_t aOpenIdx, int32_t aPosition, + DirProp* aDirProps) +{ + IsoRun& lastIsoRun = mIsoRuns[mIsoRunLast]; + Opening& o = mOpenings[aOpenIdx]; + DirProp newProp; + DirProp direction = GET_LR_FROM_LEVEL(lastIsoRun.level); + bool stable = true; // assume stable until proved otherwise + + /* The stable flag is set when brackets are paired and their + level is resolved and cannot be changed by what will be + found later in the source string. + An unstable match can occur only when applying N0c, where + the resolved level depends on the preceding context, and + this context may be affected by text occurring later. + Example: RTL paragraph containing: abc[(latin) HEBREW] + When the closing parenthesis is encountered, it appears + that N0c1 must be applied since 'abc' sets an opposite + direction context and both parentheses receive level 2. + However, when the closing square bracket is processed, + N0b applies because of 'HEBREW' being included within the + brackets, thus the square brackets are treated like R and + receive level 1. However, this changes the preceding + context of the opening parenthesis, and it now appears + that N0c2 must be applied to the parentheses rather than + N0c1. */ + + if ((direction == 0 && o.flags & FOUND_L) || + (direction == 1 && o.flags & FOUND_R)) { /* N0b */ + newProp = direction; + } else if (o.flags & (FOUND_L|FOUND_R)) { /* N0c */ + /* it is stable if there is no containing pair or in + conditions too complicated and not worth checking */ + stable = (aOpenIdx == lastIsoRun.start); + if (direction != o.contextDir) { + newProp = o.contextDir; /* N0c1 */ + } else { + newProp = direction; /* N0c2 */ + } + } else { + /* forget this and any brackets nested within this pair */ + lastIsoRun.limit = aOpenIdx; + return O_N; /* N0d */ + } + aDirProps[o.position] = newProp; + aDirProps[aPosition] = newProp; + /* Update nested N0c pairs that may be affected */ + FixN0c(aOpenIdx, o.position, newProp, aDirProps); + if (stable) { + /* forget any brackets nested within this pair */ + lastIsoRun.limit = aOpenIdx; + } else { + int32_t k; + o.match = -aPosition; + /* neutralize any unmatched opening between the current pair */ + for (k = aOpenIdx + 1; k < lastIsoRun.limit; k++) { + Opening& oo = mOpenings[k]; + if (oo.position > aPosition) { + break; + } + if (oo.match > 0) { + oo.match = 0; + } + } + } + return newProp; +} + +static inline bool +IsMatchingCloseBracket(char16_t aCh1, char16_t aCh2) +{ + // U+232A RIGHT-POINTING ANGLE BRACKET and U+3009 RIGHT ANGLE BRACKET + // are canonical equivalents, so we special-case them here. + return (aCh1 == aCh2) || + (aCh1 == 0x232A && aCh2 == 0x3009) || + (aCh2 == 0x232A && aCh1 == 0x3009); +} + +/* Handle strong characters, digits and candidates for closing brackets. */ +/* Returns true if success. (The only failure mode is an OOM when trying + to allocate memory for the Openings array.) */ +bool +nsBidi::BracketData::ProcessChar(int32_t aPosition, char16_t aCh, + DirProp* aDirProps, nsBidiLevel* aLevels) +{ + IsoRun& lastIsoRun = mIsoRuns[mIsoRunLast]; + DirProp newProp; + DirProp dirProp = aDirProps[aPosition]; + nsBidiLevel level = aLevels[aPosition]; + if (dirProp == O_N) { + /* First see if it is a matching closing bracket. Hopefully, this is + more efficient than checking if it is a closing bracket at all */ + for (int32_t idx = lastIsoRun.limit - 1; idx >= lastIsoRun.start; idx--) { + if (!IsMatchingCloseBracket(aCh, mOpenings[idx].match)) { + continue; + } + /* We have a match */ + newProp = ProcessClosing(idx, aPosition, aDirProps); + if (newProp == O_N) { /* N0d */ + aCh = 0; /* prevent handling as an opening */ + break; + } + lastIsoRun.lastBase = O_N; + lastIsoRun.contextDir = newProp; + lastIsoRun.contextPos = aPosition; + if (level & NSBIDI_LEVEL_OVERRIDE) { /* X4, X5 */ + newProp = GET_LR_FROM_LEVEL(level); + lastIsoRun.lastStrong = newProp; + uint16_t flag = DIRPROP_FLAG(newProp); + for (int32_t i = lastIsoRun.start; i < idx; i++) { + mOpenings[i].flags |= flag; + } + /* matching brackets are not overridden by LRO/RLO */ + aLevels[aPosition] &= ~NSBIDI_LEVEL_OVERRIDE; + } + /* matching brackets are not overridden by LRO/RLO */ + aLevels[mOpenings[idx].position] &= ~NSBIDI_LEVEL_OVERRIDE; + return true; + } + /* We get here only if the ON character is not a matching closing + bracket or it is a case of N0d */ + /* Now see if it is an opening bracket */ + char16_t match = GetPairedBracket(aCh); + if (match != aCh && /* has a matching char */ + GetPairedBracketType(aCh) == PAIRED_BRACKET_TYPE_OPEN) { /* opening bracket */ + if (!AddOpening(match, aPosition)) { + return false; + } + } + } + if (level & NSBIDI_LEVEL_OVERRIDE) { /* X4, X5 */ + newProp = GET_LR_FROM_LEVEL(level); + if (dirProp != S && dirProp != WS && dirProp != O_N) { + aDirProps[aPosition] = newProp; + } + lastIsoRun.lastBase = newProp; + lastIsoRun.lastStrong = newProp; + lastIsoRun.contextDir = newProp; + lastIsoRun.contextPos = aPosition; + } else if (IS_STRONG_TYPE(dirProp)) { + newProp = DirFromStrong(dirProp); + lastIsoRun.lastBase = dirProp; + lastIsoRun.lastStrong = dirProp; + lastIsoRun.contextDir = newProp; + lastIsoRun.contextPos = aPosition; + } else if (dirProp == EN) { + lastIsoRun.lastBase = EN; + if (lastIsoRun.lastStrong == L) { + newProp = L; /* W7 */ + aDirProps[aPosition] = ENL; + lastIsoRun.contextDir = L; + lastIsoRun.contextPos = aPosition; + } else { + newProp = R; /* N0 */ + if (lastIsoRun.lastStrong == AL) { + aDirProps[aPosition] = AN; /* W2 */ + } else { + aDirProps[aPosition] = ENR; + } + lastIsoRun.contextDir = R; + lastIsoRun.contextPos = aPosition; + } + } else if (dirProp == AN) { + newProp = R; /* N0 */ + lastIsoRun.lastBase = AN; + lastIsoRun.contextDir = R; + lastIsoRun.contextPos = aPosition; + } else if (dirProp == NSM) { + /* if the last real char was ON, change NSM to ON so that it + will stay ON even if the last real char is a bracket which + may be changed to L or R */ + newProp = lastIsoRun.lastBase; + if (newProp == O_N) { + aDirProps[aPosition] = newProp; + } + } else { + newProp = dirProp; + lastIsoRun.lastBase = dirProp; + } + if (IS_STRONG_TYPE(newProp)) { + uint16_t flag = DIRPROP_FLAG(DirFromStrong(newProp)); + for (int32_t i = lastIsoRun.start; i < lastIsoRun.limit; i++) { + if (aPosition > mOpenings[i].position) { + mOpenings[i].flags |= flag; + } + } + } + return true; +} + /* perform (X1)..(X9) ------------------------------------------------------- */ /* * Resolve the explicit levels as specified by explicit embedding codes. * Recalculate the flags to have them reflect the real properties * after taking the explicit embeddings into account. * * The Bidi algorithm is designed to result in the same behavior whether embedding @@ -630,17 +950,17 @@ void nsBidi::GetDirProps(const char16_t * * In order to have a correct push-pop semantics even in the case of overflows, * overflow counters and a valid isolate counter are used as described in UAX#9 * section 3.3.2 "Explicit Levels and Direction". * * This implementation assumes that NSBIDI_MAX_EXPLICIT_LEVEL is odd. */ -void nsBidi::ResolveExplicitLevels(nsBidiDirection *aDirection) +void nsBidi::ResolveExplicitLevels(nsBidiDirection *aDirection, const char16_t *aText) { DirProp *dirProps=mDirProps; nsBidiLevel *levels=mLevels; int32_t i=0, length=mLength; Flags flags=mFlags; /* collect all directionalities in the text */ DirProp dirProp; nsBidiLevel level=mParaLevel; @@ -650,268 +970,237 @@ void nsBidi::ResolveExplicitLevels(nsBid /* determine if the text is mixed-directional or single-directional */ direction=DirectionFromFlags(flags); /* we may not need to resolve any explicit levels */ if(direction!=NSBIDI_MIXED) { /* not mixed directionality: levels don't matter - trailingWSStart will be 0 */ } else if(!(flags&(MASK_EXPLICIT|MASK_ISO))) { + BracketData bracketData(this); /* no embeddings, set all levels to the paragraph level */ for(i=0; i<length; ++i) { levels[i]=level; + if (dirProps[i] == BN) { + continue; + } + if (!bracketData.ProcessChar(i, aText[i], mDirProps, mLevels)) { + NS_WARNING("BracketData::ProcessChar failed, out of memory?"); + // Ran out of memory for deeply-nested openings; give up and + // return LTR. This could presumably result in incorrect display, + // but in practice it won't happen except in some artificially- + // constructed torture test -- which is just as likely to die + // altogether with an OOM failure. + *aDirection = NSBIDI_LTR; + return; + } } } else { /* continue to perform (Xn) */ /* (X1) level is set for all codes, embeddingLevel keeps track of the push/pop operations */ /* both variables may carry the NSBIDI_LEVEL_OVERRIDE flag to indicate the override status */ nsBidiLevel embeddingLevel = level, newLevel; nsBidiLevel previousLevel = level; /* previous level for regular (not CC) characters */ + int32_t lastDirControlCharPos = 0; /* index of last effective LRx,RLx, PDx */ uint16_t stack[NSBIDI_MAX_EXPLICIT_LEVEL + 2]; /* we never push anything >=NSBIDI_MAX_EXPLICIT_LEVEL but we need one more entry as base */ int32_t stackLast = 0; int32_t overflowIsolateCount = 0; int32_t overflowEmbeddingCount = 0; int32_t validIsolateCount = 0; + BracketData bracketData(this); + stack[0] = level; /* recalculate the flags */ flags=0; /* since we assume that this is a single paragraph, we ignore (X8) */ for(i=0; i<length; ++i) { dirProp=dirProps[i]; switch(dirProp) { case LRE: case RLE: case LRO: case RLO: /* (X2, X3, X4, X5) */ flags |= DIRPROP_FLAG(BN); + levels[i] = previousLevel; if (dirProp == LRE || dirProp == LRO) { newLevel = (embeddingLevel + 2) & ~(NSBIDI_LEVEL_OVERRIDE | 1); /* least greater even level */ } else { newLevel = ((embeddingLevel & ~NSBIDI_LEVEL_OVERRIDE) + 1) | 1; /* least greater odd level */ } if(newLevel <= NSBIDI_MAX_EXPLICIT_LEVEL && overflowIsolateCount == 0 && overflowEmbeddingCount == 0) { + lastDirControlCharPos = i; embeddingLevel = newLevel; if (dirProp == LRO || dirProp == RLO) { embeddingLevel |= NSBIDI_LEVEL_OVERRIDE; } stackLast++; stack[stackLast] = embeddingLevel; - /* we don't need to set UBIDI_LEVEL_OVERRIDE off for LRE and RLE + /* we don't need to set NSBIDI_LEVEL_OVERRIDE off for LRE and RLE since this has already been done for newLevel which is the source for embeddingLevel. */ } else { - dirProps[i] |= IGNORE_CC; if (overflowIsolateCount == 0) { overflowEmbeddingCount++; } } break; case PDF: /* (X7) */ flags |= DIRPROP_FLAG(BN); + levels[i] = previousLevel; /* handle all the overflow cases first */ if (overflowIsolateCount) { - dirProps[i] |= IGNORE_CC; break; } if (overflowEmbeddingCount) { - dirProps[i] |= IGNORE_CC; overflowEmbeddingCount--; break; } if (stackLast > 0 && stack[stackLast] < ISOLATE) { /* not an isolate entry */ + lastDirControlCharPos = i; stackLast--; embeddingLevel = stack[stackLast]; - } else { - dirProps[i] |= IGNORE_CC; } break; case LRI: case RLI: - if (embeddingLevel != previousLevel) { - previousLevel = embeddingLevel; + flags |= DIRPROP_FLAG(O_N) | DIRPROP_FLAG_LR(embeddingLevel); + levels[i] = NO_OVERRIDE(embeddingLevel); + if (NO_OVERRIDE(embeddingLevel) != NO_OVERRIDE(previousLevel)) { + bracketData.ProcessBoundary(lastDirControlCharPos, previousLevel, + embeddingLevel, mDirProps); + flags |= DIRPROP_FLAG_MULTI_RUNS; } + previousLevel = embeddingLevel; /* (X5a, X5b) */ - flags |= DIRPROP_FLAG(O_N) | DIRPROP_FLAG(BN) | DIRPROP_FLAG_LR(embeddingLevel); - level = embeddingLevel; if (dirProp == LRI) { newLevel = (embeddingLevel + 2) & ~(NSBIDI_LEVEL_OVERRIDE | 1); /* least greater even level */ } else { newLevel = ((embeddingLevel & ~NSBIDI_LEVEL_OVERRIDE) + 1) | 1; /* least greater odd level */ } if (newLevel <= NSBIDI_MAX_EXPLICIT_LEVEL && overflowIsolateCount == 0 && overflowEmbeddingCount == 0) { + flags |= DIRPROP_FLAG(dirProp); + lastDirControlCharPos = i; previousLevel = embeddingLevel; validIsolateCount++; if (validIsolateCount > mIsolateCount) { mIsolateCount = validIsolateCount; } embeddingLevel = newLevel; stackLast++; stack[stackLast] = embeddingLevel + ISOLATE; + bracketData.ProcessLRI_RLI(embeddingLevel); } else { - dirProps[i] |= IGNORE_CC; + /* make it so that it is handled by AdjustWSLevels() */ + dirProps[i] = WS; overflowIsolateCount++; } break; case PDI: + if (NO_OVERRIDE(embeddingLevel) != NO_OVERRIDE(previousLevel)) { + bracketData.ProcessBoundary(lastDirControlCharPos, previousLevel, + embeddingLevel, mDirProps); + flags |= DIRPROP_FLAG_MULTI_RUNS; + } /* (X6a) */ if (overflowIsolateCount) { - dirProps[i] |= IGNORE_CC; overflowIsolateCount--; + /* make it so that it is handled by AdjustWSLevels() */ + dirProps[i] = WS; } else if (validIsolateCount) { + flags |= DIRPROP_FLAG(PDI); + lastDirControlCharPos = i; overflowEmbeddingCount = 0; while (stack[stackLast] < ISOLATE) { /* pop embedding entries */ /* until the last isolate entry */ stackLast--; // Since validIsolateCount is true, there must be an isolate entry // on the stack, so the stack is guaranteed to not be empty. // Still, to eliminate a warning from coverity, we use an assertion. MOZ_ASSERT(stackLast > 0); } stackLast--; /* pop also the last isolate entry */ MOZ_ASSERT(stackLast >= 0); // For coverity validIsolateCount--; + bracketData.ProcessPDI(); } else { - dirProps[i] |= IGNORE_CC; + /* make it so that it is handled by AdjustWSLevels() */ + dirProps[i] = WS; } embeddingLevel = stack[stackLast] & ~ISOLATE; - previousLevel = level = embeddingLevel; - flags |= DIRPROP_FLAG(O_N) | DIRPROP_FLAG(BN) | DIRPROP_FLAG_LR(embeddingLevel); + flags |= DIRPROP_FLAG(O_N) | DIRPROP_FLAG_LR(embeddingLevel); + previousLevel = embeddingLevel; + levels[i] = NO_OVERRIDE(embeddingLevel); break; case B: /* * We do not expect to see a paragraph separator (B), */ NS_NOTREACHED("Unexpected paragraph separator"); break; case BN: /* BN, LRE, RLE, and PDF are supposed to be removed (X9) */ /* they will get their levels set correctly in AdjustWSLevels() */ - flags|=DIRPROP_FLAG(BN); + levels[i] = previousLevel; + flags |= DIRPROP_FLAG(BN); break; default: /* all other types get the "real" level */ - level = embeddingLevel; - if(embeddingLevel != previousLevel) { - previousLevel = embeddingLevel; + if (NO_OVERRIDE(embeddingLevel) != NO_OVERRIDE(previousLevel)) { + bracketData.ProcessBoundary(lastDirControlCharPos, previousLevel, + embeddingLevel, mDirProps); + flags |= DIRPROP_FLAG_MULTI_RUNS; + if (embeddingLevel & NSBIDI_LEVEL_OVERRIDE) { + flags |= DIRPROP_FLAG_O(embeddingLevel); + } else { + flags |= DIRPROP_FLAG_E(embeddingLevel); + } } - - if (level & NSBIDI_LEVEL_OVERRIDE) { - flags |= DIRPROP_FLAG_LR(level); - } else { - flags |= DIRPROP_FLAG(dirProp); + previousLevel = embeddingLevel; + levels[i] = embeddingLevel; + if (!bracketData.ProcessChar(i, aText[i], mDirProps, mLevels)) { + NS_WARNING("BracketData::ProcessChar failed, out of memory?"); + *aDirection = NSBIDI_LTR; + return; } + flags |= DIRPROP_FLAG(dirProps[i]); break; } - - /* - * We need to set reasonable levels even on BN codes and - * explicit codes because we will later look at same-level runs (X10). - */ - levels[i]=level; - if (i > 0 && levels[i - 1] != level) { - flags |= DIRPROP_FLAG_MULTI_RUNS; - if (level & NSBIDI_LEVEL_OVERRIDE) { - flags |= DIRPROP_FLAG_O(level); - } else { - flags |= DIRPROP_FLAG_E(level); - } - } - if (DIRPROP_FLAG(dirProp) & MASK_ISO) { - level = embeddingLevel; - } } if(flags&MASK_EMBEDDING) { flags|=DIRPROP_FLAG_LR(mParaLevel); } /* subsequently, ignore the explicit codes and BN (X9) */ /* again, determine if the text is mixed-directional or single-directional */ mFlags=flags; direction=DirectionFromFlags(flags); } *aDirection = direction; } -/* - * Use a pre-specified embedding levels array: - * - * Adjust the directional properties for overrides (->LEVEL_OVERRIDE), - * ignore all explicit codes (X9), - * and check all the preset levels. - * - * Recalculate the flags to have them reflect the real properties - * after taking the explicit embeddings into account. - */ -nsresult nsBidi::CheckExplicitLevels(nsBidiDirection *aDirection) -{ - const DirProp *dirProps=mDirProps; - DirProp dirProp; - nsBidiLevel *levels=mLevels; - int32_t isolateCount = 0; - - int32_t i, length=mLength; - Flags flags=0; /* collect all directionalities in the text */ - nsBidiLevel level, paraLevel=mParaLevel; - mIsolateCount = 0; - - for(i=0; i<length; ++i) { - level=levels[i]; - dirProp = dirProps[i]; - if (dirProp == LRI || dirProp == RLI) { - isolateCount++; - if (isolateCount > mIsolateCount) { - mIsolateCount = isolateCount; - } - } else if (dirProp == PDI) { - isolateCount--; - } - if(level&NSBIDI_LEVEL_OVERRIDE) { - /* keep the override flag in levels[i] but adjust the flags */ - level&=~NSBIDI_LEVEL_OVERRIDE; /* make the range check below simpler */ - flags|=DIRPROP_FLAG_O(level); - } else { - /* set the flags */ - flags|=DIRPROP_FLAG_E(level)|DIRPROP_FLAG(dirProp); - } - if(level<paraLevel || NSBIDI_MAX_EXPLICIT_LEVEL<level) { - /* level out of bounds */ - *aDirection = NSBIDI_LTR; - return NS_ERROR_INVALID_ARG; - } - } - if(flags&MASK_EMBEDDING) { - flags|=DIRPROP_FLAG_LR(mParaLevel); - } - - /* determine if the text is mixed-directional or single-directional */ - mFlags=flags; - *aDirection = DirectionFromFlags(flags); - return NS_OK; -} - /* determine if the text is mixed-directional or single-directional */ nsBidiDirection nsBidi::DirectionFromFlags(Flags aFlags) { /* if the text contains AN and neutrals, then some neutrals may become RTL */ if(!(aFlags&MASK_RTL || (aFlags&DIRPROP_FLAG(AN) && aFlags&MASK_POSSIBLE_N))) { return NSBIDI_LTR; } else if(!(aFlags&MASK_LTR)) { return NSBIDI_RTL; @@ -1191,31 +1480,31 @@ void nsBidi::ResolveImplicitLevels(int32 const DirProp *dirProps = mDirProps; DirProp dirProp; LevState levState; int32_t i, start1, start2; uint16_t oldStateImp, stateImp, actionImp; uint8_t gprop, resProp, cell; /* initialize for property and levels state tables */ - levState.startON = -1; levState.runStart = aStart; levState.runLevel = mLevels[aStart]; levState.pImpTab = impTab[levState.runLevel & 1]; levState.pImpAct = impAct0; /* The isolates[] entries contain enough information to resume the bidi algorithm in the same state as it was when it was interrupted by an isolate sequence. */ - if (dirProps[aStart] == PDI) { + if (dirProps[aStart] == PDI && mIsolateCount >= 0) { start1 = mIsolates[mIsolateCount].start1; stateImp = mIsolates[mIsolateCount].stateImp; levState.state = mIsolates[mIsolateCount].state; mIsolateCount--; } else { + levState.startON = -1; start1 = aStart; if (dirProps[aStart] == NSM) { stateImp = 1 + aSOR; } else { stateImp = 0; } levState.state = 0; ProcessPropertySeq(&levState, aSOR, aStart, aStart); @@ -1228,17 +1517,17 @@ void nsBidi::ResolveImplicitLevels(int32 dirProp = mDirProps[aLimit - 1]; if (dirProp == LRI || dirProp == RLI) { break; /* no forced closing for sequence ending with LRI/RLI */ } } gprop = aEOR; } else { DirProp prop; - prop = PURE_DIRPROP(dirProps[i]); + prop = dirProps[i]; gprop = groupProp[prop]; } oldStateImp = stateImp; cell = impTabProps[oldStateImp][gprop]; stateImp = GET_STATEPROPS(cell); /* isolate the new state */ actionImp = GET_ACTIONPROPS(cell); /* isolate the action */ if ((i == aLimit) && (actionImp == 0)) { /* there is an unprocessed sequence if its property == eor */ @@ -1299,24 +1588,24 @@ void nsBidi::AdjustWSLevels() if(mFlags&MASK_WS) { nsBidiLevel paraLevel=mParaLevel; Flags flag; i=mTrailingWSStart; while(i>0) { /* reset a sequence of WS/BN before eop and B/S to the paragraph paraLevel */ - while (i > 0 && DIRPROP_FLAG(PURE_DIRPROP(dirProps[--i])) & MASK_WS) { + while (i > 0 && DIRPROP_FLAG(dirProps[--i]) & MASK_WS) { levels[i]=paraLevel; } /* reset BN to the next character's paraLevel until B/S, which restarts above loop */ /* here, i+1 is guaranteed to be <length */ while(i>0) { - flag = DIRPROP_FLAG(PURE_DIRPROP(dirProps[--i])); + flag = DIRPROP_FLAG(dirProps[--i]); if(flag&MASK_BN_EXPLICIT) { levels[i]=levels[i+1]; } else if(flag&MASK_B_S) { levels[i]=paraLevel; break; } } } @@ -1329,262 +1618,16 @@ nsresult nsBidi::GetDirection(nsBidiDire return NS_OK; } nsresult nsBidi::GetParaLevel(nsBidiLevel* aParaLevel) { *aParaLevel = mParaLevel; return NS_OK; } -#ifdef FULL_BIDI_ENGINE - -/* -------------------------------------------------------------------------- */ - -nsresult nsBidi::GetLength(int32_t* aLength) -{ - *aLength = mLength; - return NS_OK; -} - -/* - * General remarks about the functions in this section: - * - * These functions deal with the aspects of potentially mixed-directional - * text in a single paragraph or in a line of a single paragraph - * which has already been processed according to - * the Unicode 6.3 Bidi algorithm as defined in - * http://www.unicode.org/unicode/reports/tr9/ , version 28, - * also described in The Unicode Standard, Version 6.3.0 . - * - * This means that there is a nsBidi object with a levels - * and a dirProps array. - * paraLevel and direction are also set. - * Only if the length of the text is zero, then levels==dirProps==nullptr. - * - * The overall directionality of the paragraph - * or line is used to bypass the reordering steps if possible. - * Even purely RTL text does not need reordering there because - * the getLogical/VisualIndex() functions can compute the - * index on the fly in such a case. - * - * The implementation of the access to same-level-runs and of the reordering - * do attempt to provide better performance and less memory usage compared to - * a direct implementation of especially rule (L2) with an array of - * one (32-bit) integer per text character. - * - * Here, the levels array is scanned as soon as necessary, and a vector of - * same-level-runs is created. Reordering then is done on this vector. - * For each run of text positions that were resolved to the same level, - * only 8 bytes are stored: the first text position of the run and the visual - * position behind the run after reordering. - * One sign bit is used to hold the directionality of the run. - * This is inefficient if there are many very short runs. If the average run - * length is <2, then this uses more memory. - * - * In a further attempt to save memory, the levels array is never changed - * after all the resolution rules (Xn, Wn, Nn, In). - * Many functions have to consider the field trailingWSStart: - * if it is less than length, then there is an implicit trailing run - * at the paraLevel, - * which is not reflected in the levels array. - * This allows a line nsBidi object to use the same levels array as - * its paragraph parent object. - * - * When a nsBidi object is created for a line of a paragraph, then the - * paragraph's levels and dirProps arrays are reused by way of setting - * a pointer into them, not by copying. This again saves memory and forbids to - * change the now shared levels for (L1). - */ -nsresult nsBidi::SetLine(const nsBidi* aParaBidi, int32_t aStart, int32_t aLimit) -{ - nsBidi* pParent = (nsBidi*)aParaBidi; - int32_t length; - - /* check the argument values */ - if(pParent==nullptr) { - return NS_ERROR_INVALID_POINTER; - } else if(aStart < 0 || aStart >= aLimit || aLimit > pParent->mLength) { - return NS_ERROR_INVALID_ARG; - } - - /* set members from our aParaBidi parent */ - length = mLength = aLimit - aStart; - mParaLevel=pParent->mParaLevel; - - mRuns=nullptr; - mFlags=0; - - mDirProps=pParent->mDirProps+aStart; - mLevels=pParent->mLevels+aStart; - mRunCount=-1; - - if(pParent->mDirection!=NSBIDI_MIXED) { - /* the parent is already trivial */ - mDirection=pParent->mDirection; - - /* - * The parent's levels are all either - * implicitly or explicitly ==paraLevel; - * do the same here. - */ - if(pParent->mTrailingWSStart<=aStart) { - mTrailingWSStart=0; - } else if(pParent->mTrailingWSStart<aLimit) { - mTrailingWSStart=pParent->mTrailingWSStart-aStart; - } else { - mTrailingWSStart=length; - } - } else { - const nsBidiLevel *levels=mLevels; - int32_t i, trailingWSStart; - nsBidiLevel level; - - SetTrailingWSStart(); - trailingWSStart=mTrailingWSStart; - - /* recalculate pLineBidi->direction */ - if(trailingWSStart==0) { - /* all levels are at paraLevel */ - mDirection=(nsBidiDirection)(mParaLevel&1); - } else { - /* get the level of the first character */ - level=levels[0]&1; - - /* if there is anything of a different level, then the line is mixed */ - if(trailingWSStart<length && (mParaLevel&1)!=level) { - /* the trailing WS is at paraLevel, which differs from levels[0] */ - mDirection=NSBIDI_MIXED; - } else { - /* see if levels[1..trailingWSStart-1] have the same direction as levels[0] and paraLevel */ - i=1; - for(;;) { - if(i==trailingWSStart) { - /* the direction values match those in level */ - mDirection=(nsBidiDirection)level; - break; - } else if((levels[i]&1)!=level) { - mDirection=NSBIDI_MIXED; - break; - } - ++i; - } - } - } - - switch(mDirection) { - case NSBIDI_LTR: - /* make sure paraLevel is even */ - mParaLevel=(mParaLevel+1)&~1; - - /* all levels are implicitly at paraLevel (important for GetLevels()) */ - mTrailingWSStart=0; - break; - case NSBIDI_RTL: - /* make sure paraLevel is odd */ - mParaLevel|=1; - - /* all levels are implicitly at paraLevel (important for GetLevels()) */ - mTrailingWSStart=0; - break; - default: - break; - } - } - return NS_OK; -} - -/* handle trailing WS (L1) -------------------------------------------------- */ - -/* - * SetTrailingWSStart() sets the start index for a trailing - * run of WS in the line. This is necessary because we do not modify - * the paragraph's levels array that we just point into. - * Using trailingWSStart is another form of performing (L1). - * - * To make subsequent operations easier, we also include the run - * before the WS if it is at the paraLevel - we merge the two here. - */ -void nsBidi::SetTrailingWSStart() { - /* mDirection!=NSBIDI_MIXED */ - - const DirProp *dirProps=mDirProps; - nsBidiLevel *levels=mLevels; - int32_t start=mLength; - nsBidiLevel paraLevel=mParaLevel; - - /* go backwards across all WS, BN, explicit codes */ - while(start>0 && DIRPROP_FLAG(dirProps[start-1])&MASK_WS) { - --start; - } - - /* if the WS run can be merged with the previous run then do so here */ - while(start>0 && levels[start-1]==paraLevel) { - --start; - } - - mTrailingWSStart=start; -} - -nsresult nsBidi::GetLevelAt(int32_t aCharIndex, nsBidiLevel* aLevel) -{ - /* return paraLevel if in the trailing WS run, otherwise the real level */ - if(aCharIndex<0 || mLength<=aCharIndex) { - *aLevel = 0; - } else if(mDirection!=NSBIDI_MIXED || aCharIndex>=mTrailingWSStart) { - *aLevel = mParaLevel; - } else { - *aLevel = mLevels[aCharIndex]; - } - return NS_OK; -} - -nsresult nsBidi::GetLevels(nsBidiLevel** aLevels) -{ - int32_t start, length; - - length = mLength; - if(length<=0) { - *aLevels = nullptr; - return NS_ERROR_INVALID_ARG; - } - - start = mTrailingWSStart; - if(start==length) { - /* the current levels array reflects the WS run */ - *aLevels = mLevels; - return NS_OK; - } - - /* - * After the previous if(), we know that the levels array - * has an implicit trailing WS run and therefore does not fully - * reflect itself all the levels. - * This must be a nsBidi object for a line, and - * we need to create a new levels array. - */ - - if(GETLEVELSMEMORY(length)) { - nsBidiLevel *levels=mLevelsMemory; - - if(start>0 && levels!=mLevels) { - memcpy(levels, mLevels, start); - } - memset(levels+start, mParaLevel, length-start); - - /* this new levels array is set for the line and reflects the WS run */ - mTrailingWSStart=length; - *aLevels=mLevels=levels; - return NS_OK; - } else { - /* out of memory */ - *aLevels = nullptr; - return NS_ERROR_OUT_OF_MEMORY; - } -} -#endif // FULL_BIDI_ENGINE nsresult nsBidi::GetCharTypeAt(int32_t aCharIndex, nsCharType* pType) { if(aCharIndex<0 || mLength<=aCharIndex) { return NS_ERROR_INVALID_ARG; } *pType = (nsCharType)mDirProps[aCharIndex]; return NS_OK; @@ -2033,459 +2076,8 @@ bool nsBidi::PrepareReorder(const nsBidi /* initialize the index map */ for(start=aLength; start>0;) { --start; aIndexMap[start]=start; } return true; } - -#ifdef FULL_BIDI_ENGINE -/* API functions for logical<->visual mapping ------------------------------- */ - -nsresult nsBidi::GetVisualIndex(int32_t aLogicalIndex, int32_t* aVisualIndex) { - int32_t visualIndex = NSBIDI_MAP_NOWHERE; - - if(aLogicalIndex<0 || mLength<=aLogicalIndex) { - return NS_ERROR_INVALID_ARG; - } else { - /* we can do the trivial cases without the runs array */ - switch(mDirection) { - case NSBIDI_LTR: - *aVisualIndex = aLogicalIndex; - return NS_OK; - case NSBIDI_RTL: - *aVisualIndex = mLength-aLogicalIndex-1; - return NS_OK; - default: - if(mRunCount<0 && !GetRuns()) { - return NS_ERROR_OUT_OF_MEMORY; - } else { - Run *runs=mRuns; - int32_t i, visualStart=0, offset, length; - - /* linear search for the run, search on the visual runs */ - for (i = 0; i < mRunCount; ++i) { - length=runs[i].visualLimit-visualStart; - offset=aLogicalIndex-GET_INDEX(runs[i].logicalStart); - if(offset>=0 && offset<length) { - if(IS_EVEN_RUN(runs[i].logicalStart)) { - /* LTR */ - visualIndex = visualStart + offset; - } else { - /* RTL */ - visualIndex = visualStart + length - offset - 1; - } - break; - } - visualStart+=length; - } - if (i >= mRunCount) { - *aVisualIndex = NSBIDI_MAP_NOWHERE; - return NS_OK; - } - } - } - } - - *aVisualIndex = visualIndex; - return NS_OK; -} - -nsresult nsBidi::GetLogicalIndex(int32_t aVisualIndex, int32_t *aLogicalIndex) -{ - if(aVisualIndex<0 || mLength<=aVisualIndex) { - return NS_ERROR_INVALID_ARG; - } - - /* we can do the trivial cases without the runs array */ - if (mDirection == NSBIDI_LTR) { - *aLogicalIndex = aVisualIndex; - return NS_OK; - } else if (mDirection == NSBIDI_RTL) { - *aLogicalIndex = mLength - aVisualIndex - 1; - return NS_OK; - } - - if(mRunCount<0 && !GetRuns()) { - return NS_ERROR_OUT_OF_MEMORY; - } - - Run *runs=mRuns; - int32_t i, runCount=mRunCount, start; - - if(runCount<=10) { - /* linear search for the run */ - for(i=0; aVisualIndex>=runs[i].visualLimit; ++i) {} - } else { - /* binary search for the run */ - int32_t start=0, limit=runCount; - - /* the middle if() will guaranteed find the run, we don't need a loop limit */ - for(;;) { - i=(start+limit)/2; - if(aVisualIndex>=runs[i].visualLimit) { - start=i+1; - } else if(i==0 || aVisualIndex>=runs[i-1].visualLimit) { - break; - } else { - limit=i; - } - } - } - - start=runs[i].logicalStart; - if(IS_EVEN_RUN(start)) { - /* LTR */ - /* the offset in runs[i] is aVisualIndex-runs[i-1].visualLimit */ - if(i>0) { - aVisualIndex-=runs[i-1].visualLimit; - } - *aLogicalIndex = GET_INDEX(start)+aVisualIndex; - return NS_OK; - } else { - /* RTL */ - *aLogicalIndex = GET_INDEX(start)+runs[i].visualLimit-aVisualIndex-1; - return NS_OK; - } -} - -nsresult nsBidi::GetLogicalMap(int32_t *aIndexMap) -{ - nsresult rv; - - /* CountRuns() checks for VALID_PARA_OR_LINE */ - rv = CountRuns(nullptr); - if(NS_FAILED(rv)) { - return rv; - } else if(aIndexMap==nullptr) { - return NS_ERROR_INVALID_ARG; - } else { - /* fill a logical-to-visual index map using the runs[] */ - int32_t visualStart, visualLimit, j; - int32_t logicalStart; - Run *runs = mRuns; - if (mLength <= 0) { - return NS_OK; - } - - visualStart = 0; - for (j = 0; j < mRunCount; ++j) { - logicalStart = GET_INDEX(runs[j].logicalStart); - visualLimit = runs[j].visualLimit; - if (IS_EVEN_RUN(runs[j].logicalStart)) { - do { /* LTR */ - aIndexMap[logicalStart++] = visualStart++; - } while (visualStart < visualLimit); - } else { - logicalStart += visualLimit-visualStart; /* logicalLimit */ - do { /* RTL */ - aIndexMap[--logicalStart] = visualStart++; - } while (visualStart < visualLimit); - } - /* visualStart==visualLimit; */ - } - } - return NS_OK; -} - -nsresult nsBidi::GetVisualMap(int32_t *aIndexMap) -{ - int32_t* runCount=nullptr; - nsresult rv; - - if(aIndexMap==nullptr) { - return NS_ERROR_INVALID_ARG; - } - - /* CountRuns() checks all of its and our arguments */ - rv = CountRuns(runCount); - if(NS_FAILED(rv)) { - return rv; - } else { - /* fill a visual-to-logical index map using the runs[] */ - Run *runs=mRuns, *runsLimit=runs+mRunCount; - int32_t logicalStart, visualStart, visualLimit; - - visualStart=0; - for(; runs<runsLimit; ++runs) { - logicalStart=runs->logicalStart; - visualLimit=runs->visualLimit; - if(IS_EVEN_RUN(logicalStart)) { - do { /* LTR */ - *aIndexMap++ = logicalStart++; - } while(++visualStart<visualLimit); - } else { - REMOVE_ODD_BIT(logicalStart); - logicalStart+=visualLimit-visualStart; /* logicalLimit */ - do { /* RTL */ - *aIndexMap++ = --logicalStart; - } while(++visualStart<visualLimit); - } - /* visualStart==visualLimit; */ - } - return NS_OK; - } -} - -/* reorder a line based on a levels array (L2) ------------------------------ */ - -nsresult nsBidi::ReorderLogical(const nsBidiLevel *aLevels, int32_t aLength, int32_t *aIndexMap) -{ - int32_t start, limit, sumOfSosEos; - nsBidiLevel minLevel, maxLevel; - - if(aIndexMap==nullptr || - !PrepareReorder(aLevels, aLength, aIndexMap, &minLevel, &maxLevel)) { - return NS_OK; - } - - /* nothing to do? */ - if(minLevel==maxLevel && (minLevel&1)==0) { - return NS_OK; - } - - /* reorder only down to the lowest odd level */ - minLevel|=1; - - /* loop maxLevel..minLevel */ - do { - start=0; - - /* loop for all sequences of levels to reorder at the current maxLevel */ - for(;;) { - /* look for a sequence of levels that are all at >=maxLevel */ - /* look for the first index of such a sequence */ - while(start<aLength && aLevels[start]<maxLevel) { - ++start; - } - if(start>=aLength) { - break; /* no more such sequences */ - } - - /* look for the limit of such a sequence (the index behind it) */ - for(limit=start; ++limit<aLength && aLevels[limit]>=maxLevel;) {} - - /* - * sos=start of sequence, eos=end of sequence - * - * The closed (inclusive) interval from sos to eos includes all the logical - * and visual indexes within this sequence. They are logically and - * visually contiguous and in the same range. - * - * For each run, the new visual index=sos+eos-old visual index; - * we pre-add sos+eos into sumOfSosEos -> - * new visual index=sumOfSosEos-old visual index; - */ - sumOfSosEos=start+limit-1; - - /* reorder each index in the sequence */ - do { - aIndexMap[start]=sumOfSosEos-aIndexMap[start]; - } while(++start<limit); - - /* start==limit */ - if(limit==aLength) { - break; /* no more such sequences */ - } else { - start=limit+1; - } - } - } while(--maxLevel>=minLevel); - - return NS_OK; -} - -nsresult nsBidi::InvertMap(const int32_t *aSrcMap, int32_t *aDestMap, int32_t aLength) -{ - if(aSrcMap!=nullptr && aDestMap!=nullptr && aLength > 0) { - const int32_t *pi; - int32_t destLength = -1, count = 0; - /* find highest value and count positive indexes in srcMap */ - pi = aSrcMap + aLength; - while (pi > aSrcMap) { - if (*--pi > destLength) { - destLength = *pi; - } - if (*pi >= 0) { - count++; - } - } - destLength++; /* add 1 for origin 0 */ - if (count < destLength) { - /* we must fill unmatched destMap entries with -1 */ - memset(aDestMap, 0xFF, destLength * sizeof(int32_t)); - } - pi = aSrcMap + aLength; - while (aLength > 0) { - if (*--pi >= 0) { - aDestMap[*pi] = --aLength; - } else { - --aLength; - } - } - } - return NS_OK; -} - -int32_t nsBidi::doWriteReverse(const char16_t *src, int32_t srcLength, - char16_t *dest, uint16_t options) { - /* - * RTL run - - * - * RTL runs need to be copied to the destination in reverse order - * of code points, not code units, to keep Unicode characters intact. - * - * The general strategy for this is to read the source text - * in backward order, collect all code units for a code point - * (and optionally following combining characters, see below), - * and copy all these code units in ascending order - * to the destination for this run. - * - * Several options request whether combining characters - * should be kept after their base characters, - * whether Bidi control characters should be removed, and - * whether characters should be replaced by their mirror-image - * equivalent Unicode characters. - */ - int32_t i, j, destSize; - uint32_t c; - - /* optimize for several combinations of options */ - switch(options&(NSBIDI_REMOVE_BIDI_CONTROLS|NSBIDI_DO_MIRRORING|NSBIDI_KEEP_BASE_COMBINING)) { - case 0: - /* - * With none of the "complicated" options set, the destination - * run will have the same length as the source run, - * and there is no mirroring and no keeping combining characters - * with their base characters. - */ - destSize=srcLength; - - /* preserve character integrity */ - do { - /* i is always after the last code unit known to need to be kept in this segment */ - i=srcLength; - - /* collect code units for one base character */ - UTF_BACK_1(src, 0, srcLength); - - /* copy this base character */ - j=srcLength; - do { - *dest++=src[j++]; - } while(j<i); - } while(srcLength>0); - break; - case NSBIDI_KEEP_BASE_COMBINING: - /* - * Here, too, the destination - * run will have the same length as the source run, - * and there is no mirroring. - * We do need to keep combining characters with their base characters. - */ - destSize=srcLength; - - /* preserve character integrity */ - do { - /* i is always after the last code unit known to need to be kept in this segment */ - i=srcLength; - - /* collect code units and modifier letters for one base character */ - do { - UTF_PREV_CHAR(src, 0, srcLength, c); - } while(srcLength>0 && GetBidiCat(c) == eCharType_DirNonSpacingMark); - - /* copy this "user character" */ - j=srcLength; - do { - *dest++=src[j++]; - } while(j<i); - } while(srcLength>0); - break; - default: - /* - * With several "complicated" options set, this is the most - * general and the slowest copying of an RTL run. - * We will do mirroring, remove Bidi controls, and - * keep combining characters with their base characters - * as requested. - */ - if(!(options&NSBIDI_REMOVE_BIDI_CONTROLS)) { - i=srcLength; - } else { - /* we need to find out the destination length of the run, - which will not include the Bidi control characters */ - int32_t length=srcLength; - char16_t ch; - - i=0; - do { - ch=*src++; - if (!IsBidiControl((uint32_t)ch)) { - ++i; - } - } while(--length>0); - src-=srcLength; - } - destSize=i; - - /* preserve character integrity */ - do { - /* i is always after the last code unit known to need to be kept in this segment */ - i=srcLength; - - /* collect code units for one base character */ - UTF_PREV_CHAR(src, 0, srcLength, c); - if(options&NSBIDI_KEEP_BASE_COMBINING) { - /* collect modifier letters for this base character */ - while(srcLength>0 && GetBidiCat(c) == eCharType_DirNonSpacingMark) { - UTF_PREV_CHAR(src, 0, srcLength, c); - } - } - - if(options&NSBIDI_REMOVE_BIDI_CONTROLS && IsBidiControl(c)) { - /* do not copy this Bidi control character */ - continue; - } - - /* copy this "user character" */ - j=srcLength; - if(options&NSBIDI_DO_MIRRORING) { - /* mirror only the base character */ - c = GetMirroredChar(c); - - int32_t k=0; - UTF_APPEND_CHAR_UNSAFE(dest, k, c); - dest+=k; - j+=k; - } - while(j<i) { - *dest++=src[j++]; - } - } while(srcLength>0); - break; - } /* end of switch */ - return destSize; -} - -nsresult nsBidi::WriteReverse(const char16_t *aSrc, int32_t aSrcLength, char16_t *aDest, uint16_t aOptions, int32_t *aDestSize) -{ - if( aSrc==nullptr || aSrcLength<0 || - aDest==nullptr - ) { - return NS_ERROR_INVALID_ARG; - } - - /* do input and output overlap? */ - if( aSrc>=aDest && aSrc<aDest+aSrcLength || - aDest>=aSrc && aDest<aSrc+aSrcLength - ) { - return NS_ERROR_INVALID_ARG; - } - - if(aSrcLength>0) { - *aDestSize = doWriteReverse(aSrc, aSrcLength, aDest, aOptions); - } - return NS_OK; -} -#endif // FULL_BIDI_ENGINE
--- a/layout/base/nsBidi.h +++ b/layout/base/nsBidi.h @@ -44,24 +44,16 @@ */ /** * nsBidiLevel is the type of the level values in this * Bidi implementation. * It holds an embedding level and indicates the visual direction * by its bit 0 (even/odd value).<p> * - * It can also hold non-level values for the - * <code>aParaLevel</code> and <code>aEmbeddingLevels</code> - * arguments of <code>SetPara</code>; there: - * <ul> - * <li>bit 7 of an <code>aEmbeddingLevels[]</code> - * value indicates whether the using application is - * specifying the level of a character to <i>override</i> whatever the - * Bidi implementation would resolve it to.</li> * <li><code>aParaLevel</code> can be set to the * pseudo-level values <code>NSBIDI_DEFAULT_LTR</code> * and <code>NSBIDI_DEFAULT_RTL</code>.</li></ul> * * @see nsBidi::SetPara * * <p>The related constants are not real, valid level values. * <code>NSBIDI_DEFAULT_XXX</code> can be used to specify @@ -122,74 +114,38 @@ enum nsBidiDirection { /** All left-to-right text This is a 0 value. */ NSBIDI_LTR, /** All right-to-left text This is a 1 value. */ NSBIDI_RTL, /** Mixed-directional text. */ NSBIDI_MIXED }; -typedef enum nsBidiDirection nsBidiDirection; - /* miscellaneous definitions ------------------------------------------------ */ -/** option flags for WriteReverse() */ -/** - * option bit for WriteReverse(): - * keep combining characters after their base characters in RTL runs - * - * @see WriteReverse - */ -#define NSBIDI_KEEP_BASE_COMBINING 1 - -/** - * option bit for WriteReverse(): - * replace characters with the "mirrored" property in RTL runs - * by their mirror-image mappings - * - * @see WriteReverse - */ -#define NSBIDI_DO_MIRRORING 2 - -/** - * option bit for WriteReverse(): - * remove Bidi control characters - * - * @see WriteReverse - */ -#define NSBIDI_REMOVE_BIDI_CONTROLS 8 /* helper macros for each allocated array member */ -#define GETDIRPROPSMEMORY(length) \ - GetMemory((void **)&mDirPropsMemory, &mDirPropsSize, \ - mMayAllocateText, (length)) +#define GETDIRPROPSMEMORY(length) nsBidi::GetMemory((void **)&mDirPropsMemory, \ + &mDirPropsSize, \ + (length)) -#define GETLEVELSMEMORY(length) \ - GetMemory((void **)&mLevelsMemory, &mLevelsSize, \ - mMayAllocateText, (length)) - -#define GETRUNSMEMORY(length) \ - GetMemory((void **)&mRunsMemory, &mRunsSize, \ - mMayAllocateRuns, (length)*sizeof(Run)) +#define GETLEVELSMEMORY(length) nsBidi::GetMemory((void **)&mLevelsMemory, \ + &mLevelsSize, \ + (length)) -/* additional macros used by constructor - always allow allocation */ -#define GETINITIALDIRPROPSMEMORY(length) \ - GetMemory((void **)&mDirPropsMemory, &mDirPropsSize, \ - true, (length)) +#define GETRUNSMEMORY(length) nsBidi::GetMemory((void **)&mRunsMemory, \ + &mRunsSize, \ + (length)*sizeof(Run)) -#define GETINITIALLEVELSMEMORY(length) \ - GetMemory((void **)&mLevelsMemory, &mLevelsSize, \ - true, (length)) +#define GETISOLATESMEMORY(length) nsBidi::GetMemory((void **)&mIsolatesMemory, \ + &mIsolatesSize, \ + (length)*sizeof(Isolate)) -#define GETINITIALRUNSMEMORY(length) \ - GetMemory((void **)&mRunsMemory, &mRunsSize, \ - true, (length)*sizeof(Run)) - -#define GETINITIALISOLATESMEMORY(length) \ - GetMemory((void **)&mIsolatesMemory, &mIsolatesSize, \ - true, (length)*sizeof(Isolate)) +#define GETOPENINGSMEMORY(length) nsBidi::GetMemory((void **)&mOpeningsMemory, \ + &mOpeningsSize, \ + (length)*sizeof(Opening)) /* * Sometimes, bit values are more appropriate * to deal with directionality properties. * Abbreviations in these macro names refer to names * used in the Bidi algorithm. */ typedef uint8_t DirProp; @@ -229,33 +185,27 @@ typedef uint8_t DirProp; #define MASK_EMBEDDING (DIRPROP_FLAG(NSM)|MASK_POSSIBLE_N) /* the dirProp's L and R are defined to 0 and 1 values in nsCharType */ #define GET_LR_FROM_LEVEL(level) ((DirProp)((level)&1)) #define IS_DEFAULT_LEVEL(level) (((level)&0xfe)==0xfe) /* - * The following bit is ORed to the property of directional control - * characters which are ignored: unmatched PDF or PDI; LRx, RLx or FSI - * which would exceed the maximum explicit bidi level. - */ -#define IGNORE_CC 0x40 - -#define PURE_DIRPROP(prop) ((prop)&~IGNORE_CC) - -/* * The following bit is used for the directional isolate status. * Stack entries corresponding to isolate sequences are greater than ISOLATE. */ #define ISOLATE 0x0100 /* number of isolate entries allocated initially without malloc */ #define SIMPLE_ISOLATES_SIZE 5 +/* number of isolate run entries for paired brackets allocated initially without malloc */ +#define SIMPLE_OPENINGS_COUNT 8 + /* handle surrogate pairs --------------------------------------------------- */ #define IS_FIRST_SURROGATE(uchar) (((uchar)&0xfc00)==0xd800) #define IS_SECOND_SURROGATE(uchar) (((uchar)&0xfc00)==0xdc00) /* get the UTF-32 value directly from the surrogate pseudo-characters */ #define SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000) #define GET_UTF_32(first, second) (((first)<<10UL)+(second)-SURROGATE_OFFSET) @@ -385,16 +335,42 @@ typedef uint8_t DirProp; #define UTF_APPEND_CHAR(s, i, length, c) UTF_APPEND_CHAR_SAFE(s, i, length, c) struct Isolate { int32_t start1; int16_t stateImp; int16_t state; }; +// For bracket matching + +#define FOUND_L DIRPROP_FLAG(L) +#define FOUND_R DIRPROP_FLAG(R) + +struct Opening { + int32_t position; /* position of opening bracket */ + int32_t match; /* matching char or -position of closing bracket */ + int32_t contextPos; /* position of last strong char found before opening */ + uint16_t flags; /* bits for L or R/AL found within the pair */ + DirProp contextDir; /* L or R according to last strong char before opening */ + uint8_t filler; /* to complete a nice multiple of 4 chars */ +}; + +struct IsoRun { + int32_t contextPos; /* position of char determining context */ + uint16_t start; /* index of first opening entry for this run */ + uint16_t limit; /* index after last opening entry for this run */ + nsBidiLevel level; /* level of this run */ + DirProp lastStrong; /* bidi class of last strong char found in this run */ + DirProp lastBase; /* bidi class of last base char found in this run */ + DirProp contextDir; /* L or R to use as context for following openings */ +}; + +class nsBidi; + /* Run structure for reordering --------------------------------------------- */ typedef struct Run { int32_t logicalStart; /* first character of the run; b31 indicates even/odd level */ int32_t visualLimit; /* last visual position of the run +1 */ } Run; /* in a Run, logicalStart will get this bit set if the run level is odd */ @@ -500,38 +476,18 @@ public: * If the function shall determine the paragraph level from the text, * then <code>aParaLevel</code> can be set to * either <code>NSBIDI_DEFAULT_LTR</code> * or <code>NSBIDI_DEFAULT_RTL</code>; * if there is no strongly typed character, then * the desired default is used (0 for LTR or 1 for RTL). * Any other value between 0 and <code>NSBIDI_MAX_EXPLICIT_LEVEL</code> is also valid, * with odd levels indicating RTL. - * - * @param aEmbeddingLevels (in) may be used to preset the embedding and override levels, - * ignoring characters like LRE and PDF in the text. - * A level overrides the directional property of its corresponding - * (same index) character if the level has the - * <code>NSBIDI_LEVEL_OVERRIDE</code> bit set.<p> - * Except for that bit, it must be - * <code>aParaLevel<=aEmbeddingLevels[]<=NSBIDI_MAX_EXPLICIT_LEVEL</code>.<p> - * <strong>Caution: </strong>A copy of this pointer, not of the levels, - * will be stored in the <code>nsBidi</code> object; - * the <code>aEmbeddingLevels</code> array must not be - * deallocated before the <code>nsBidi</code> object is destroyed or reused, - * and the <code>aEmbeddingLevels</code> - * should not be modified to avoid unexpected results on subsequent Bidi operations. - * However, the <code>SetPara</code> and - * <code>SetLine</code> functions may modify some or all of the levels.<p> - * After the <code>nsBidi</code> object is reused or destroyed, the caller - * must take care of the deallocation of the <code>aEmbeddingLevels</code> array.<p> - * <strong>The <code>aEmbeddingLevels</code> array must be - * at least <code>aLength</code> long.</strong> */ - nsresult SetPara(const char16_t *aText, int32_t aLength, nsBidiLevel aParaLevel, nsBidiLevel *aEmbeddingLevels); + nsresult SetPara(const char16_t *aText, int32_t aLength, nsBidiLevel aParaLevel); /** * Get the directionality of the text. * * @param aDirection receives a <code>NSBIDI_XXX</code> value that indicates if the entire text * represented by this object is unidirectional, * and which direction, or if it is mixed-directional. * @@ -543,83 +499,16 @@ public: * Get the paragraph level of the text. * * @param aParaLevel receives a <code>NSBIDI_XXX</code> value indicating the paragraph level * * @see nsBidiLevel */ nsresult GetParaLevel(nsBidiLevel* aParaLevel); -#ifdef FULL_BIDI_ENGINE - /** - * <code>SetLine</code> sets an <code>nsBidi</code> to - * contain the reordering information, especially the resolved levels, - * for all the characters in a line of text. This line of text is - * specified by referring to an <code>nsBidi</code> object representing - * this information for a paragraph of text, and by specifying - * a range of indexes in this paragraph.<p> - * In the new line object, the indexes will range from 0 to <code>aLimit-aStart</code>.<p> - * - * This is used after calling <code>SetPara</code> - * for a paragraph, and after line-breaking on that paragraph. - * It is not necessary if the paragraph is treated as a single line.<p> - * - * After line-breaking, rules (L1) and (L2) for the treatment of - * trailing WS and for reordering are performed on - * an <code>nsBidi</code> object that represents a line.<p> - * - * <strong>Important:</strong> the line <code>nsBidi</code> object shares data with - * <code>aParaBidi</code>. - * You must destroy or reuse this object before <code>aParaBidi</code>. - * In other words, you must destroy or reuse the <code>nsBidi</code> object for a line - * before the object for its parent paragraph. - * - * @param aParaBidi is the parent paragraph object. - * - * @param aStart is the line's first index into the paragraph text. - * - * @param aLimit is just behind the line's last index into the paragraph text - * (its last index +1).<br> - * It must be <code>0<=aStart<=aLimit<=</code>paragraph length. - * - * @see SetPara - */ - nsresult SetLine(const nsBidi* aParaBidi, int32_t aStart, int32_t aLimit); - - /** - * Get the length of the text. - * - * @param aLength receives the length of the text that the nsBidi object was created for. - */ - nsresult GetLength(int32_t* aLength); - - /** - * Get the level for one character. - * - * @param aCharIndex the index of a character. - * - * @param aLevel receives the level for the character at aCharIndex. - * - * @see nsBidiLevel - */ - nsresult GetLevelAt(int32_t aCharIndex, nsBidiLevel* aLevel); - - /** - * Get an array of levels for each character.<p> - * - * Note that this function may allocate memory under some - * circumstances, unlike <code>GetLevelAt</code>. - * - * @param aLevels receives a pointer to the levels array for the text, - * or <code>nullptr</code> if an error occurs. - * - * @see nsBidiLevel - */ - nsresult GetLevels(nsBidiLevel** aLevels); -#endif // FULL_BIDI_ENGINE /** * Get the bidirectional type for one character. * * @param aCharIndex the index of a character. * * @param aType receives the bidirectional type of the character at aCharIndex. */ nsresult GetCharTypeAt(int32_t aCharIndex, nsCharType* aType); @@ -702,102 +591,16 @@ public: * @endcode * * Note that in right-to-left runs, code like this places * modifier letters before base characters and second surrogates * before first ones. */ nsresult GetVisualRun(int32_t aRunIndex, int32_t* aLogicalStart, int32_t* aLength, nsBidiDirection* aDirection); -#ifdef FULL_BIDI_ENGINE - /** - * Get the visual position from a logical text position. - * If such a mapping is used many times on the same - * <code>nsBidi</code> object, then calling - * <code>GetLogicalMap</code> is more efficient.<p> - * - * Note that in right-to-left runs, this mapping places - * modifier letters before base characters and second surrogates - * before first ones. - * - * @param aLogicalIndex is the index of a character in the text. - * - * @param aVisualIndex will receive the visual position of this character. - * - * @see GetLogicalMap - * @see GetLogicalIndex - */ - nsresult GetVisualIndex(int32_t aLogicalIndex, int32_t* aVisualIndex); - - /** - * Get the logical text position from a visual position. - * If such a mapping is used many times on the same - * <code>nsBidi</code> object, then calling - * <code>GetVisualMap</code> is more efficient.<p> - * - * This is the inverse function to <code>GetVisualIndex</code>. - * - * @param aVisualIndex is the visual position of a character. - * - * @param aLogicalIndex will receive the index of this character in the text. - * - * @see GetVisualMap - * @see GetVisualIndex - */ - nsresult GetLogicalIndex(int32_t aVisualIndex, int32_t* aLogicalIndex); - - /** - * Get a logical-to-visual index map (array) for the characters in the nsBidi - * (paragraph or line) object. - * - * @param aIndexMap is a pointer to an array of <code>GetLength</code> - * indexes which will reflect the reordering of the characters. - * The array does not need to be initialized.<p> - * The index map will result in <code>aIndexMap[aLogicalIndex]==aVisualIndex</code>.<p> - * - * @see GetVisualMap - * @see GetVisualIndex - */ - nsresult GetLogicalMap(int32_t *aIndexMap); - - /** - * Get a visual-to-logical index map (array) for the characters in the nsBidi - * (paragraph or line) object. - * - * @param aIndexMap is a pointer to an array of <code>GetLength</code> - * indexes which will reflect the reordering of the characters. - * The array does not need to be initialized.<p> - * The index map will result in <code>aIndexMap[aVisualIndex]==aLogicalIndex</code>.<p> - * - * @see GetLogicalMap - * @see GetLogicalIndex - */ - nsresult GetVisualMap(int32_t *aIndexMap); - - /** - * This is a convenience function that does not use a nsBidi object. - * It is intended to be used for when an application has determined the levels - * of objects (character sequences) and just needs to have them reordered (L2). - * This is equivalent to using <code>GetLogicalMap</code> on a - * <code>nsBidi</code> object. - * - * @param aLevels is an array with <code>aLength</code> levels that have been determined by - * the application. - * - * @param aLength is the number of levels in the array, or, semantically, - * the number of objects to be reordered. - * It must be <code>aLength>0</code>. - * - * @param aIndexMap is a pointer to an array of <code>aLength</code> - * indexes which will reflect the reordering of the characters. - * The array does not need to be initialized.<p> - * The index map will result in <code>aIndexMap[aLogicalIndex]==aVisualIndex</code>. - */ - static nsresult ReorderLogical(const nsBidiLevel *aLevels, int32_t aLength, int32_t *aIndexMap); -#endif // FULL_BIDI_ENGINE /** * This is a convenience function that does not use a nsBidi object. * It is intended to be used for when an application has determined the levels * of objects (character sequences) and just needs to have them reordered (L2). * This is equivalent to using <code>GetVisualMap</code> on a * <code>nsBidi</code> object. * * @param aLevels is an array with <code>aLength</code> levels that have been determined by @@ -809,32 +612,16 @@ public: * * @param aIndexMap is a pointer to an array of <code>aLength</code> * indexes which will reflect the reordering of the characters. * The array does not need to be initialized.<p> * The index map will result in <code>aIndexMap[aVisualIndex]==aLogicalIndex</code>. */ static nsresult ReorderVisual(const nsBidiLevel *aLevels, int32_t aLength, int32_t *aIndexMap); -#ifdef FULL_BIDI_ENGINE - /** - * Invert an index map. - * The one-to-one index mapping of the first map is inverted and written to - * the second one. - * - * @param aSrcMap is an array with <code>aLength</code> indexes - * which define the original mapping. - * - * @param aDestMap is an array with <code>aLength</code> indexes - * which will be filled with the inverse mapping. - * - * @param aLength is the length of each array. - */ - nsresult InvertMap(const int32_t *aSrcMap, int32_t *aDestMap, int32_t aLength); -#endif // FULL_BIDI_ENGINE /** * Reverse a Right-To-Left run of Unicode text. * * This function preserves the integrity of characters with multiple * code units and (optionally) modifier letters. * Characters can be replaced by mirror-image characters * in the destination buffer. Note that "real" mirroring has * to be done in a rendering engine by glyph selection @@ -865,32 +652,68 @@ public: * * @param aDestSize will receive the number of characters that were written to <code>aDest</code>. */ nsresult WriteReverse(const char16_t *aSrc, int32_t aSrcLength, char16_t *aDest, uint16_t aOptions, int32_t *aDestSize); protected: friend class nsBidiPresUtils; + class BracketData { + public: + explicit BracketData(const nsBidi* aBidi); + ~BracketData(); + + void ProcessBoundary(int32_t aLastDirControlCharPos, + nsBidiLevel aContextLevel, + nsBidiLevel aEmbeddingLevel, + const DirProp* aDirProps); + void ProcessLRI_RLI(nsBidiLevel aLevel); + void ProcessPDI(); + bool AddOpening(char16_t aMatch, int32_t aPosition); + void FixN0c(int32_t aOpeningIndex, int32_t aNewPropPosition, + DirProp aNewProp, DirProp* aDirProps); + DirProp ProcessClosing(int32_t aOpenIdx, int32_t aPosition, + DirProp* aDirProps); + bool ProcessChar(int32_t aPosition, char16_t aCh, DirProp* aDirProps, + nsBidiLevel* aLevels); + + private: + // array of opening entries which should be enough in most cases; + // no malloc() needed + Opening mSimpleOpenings[SIMPLE_OPENINGS_COUNT]; + Opening* mOpenings; // pointer to current array of entries, + // either mSimpleOpenings or malloced array + + Opening* mOpeningsMemory; + size_t mOpeningsSize; + + // array of nested isolated sequence entries; can never exceed + // UBIDI_MAX_EXPLICIT_LEVEL + // + 1 for index 0 + // + 1 for before the first isolated sequence + IsoRun mIsoRuns[NSBIDI_MAX_EXPLICIT_LEVEL+2]; + int32_t mIsoRunLast; // index of last used entry in mIsoRuns + + int32_t mOpeningsCount; // number of allocated entries in mOpenings + }; + /** length of the current text */ int32_t mLength; /** memory sizes in bytes */ size_t mDirPropsSize, mLevelsSize, mRunsSize; size_t mIsolatesSize; /** allocated memory */ DirProp* mDirPropsMemory; nsBidiLevel* mLevelsMemory; Run* mRunsMemory; Isolate* mIsolatesMemory; - /** indicators for whether memory may be allocated after construction */ - bool mMayAllocateText, mMayAllocateRuns; - DirProp* mDirProps; nsBidiLevel* mLevels; /** the paragraph level */ nsBidiLevel mParaLevel; /** flags is a bit set for which directional properties are in the text */ Flags mFlags; @@ -919,25 +742,23 @@ protected: /** for simple text, have a small stack (no malloc()) */ Isolate mSimpleIsolates[SIMPLE_ISOLATES_SIZE]; private: void Init(); - bool GetMemory(void **aMemory, size_t* aSize, bool aMayAllocate, size_t aSizeNeeded); + static bool GetMemory(void **aMemory, size_t* aSize, size_t aSizeNeeded); void Free(); void GetDirProps(const char16_t *aText); - void ResolveExplicitLevels(nsBidiDirection *aDirection); - - nsresult CheckExplicitLevels(nsBidiDirection *aDirection); + void ResolveExplicitLevels(nsBidiDirection *aDirection, const char16_t *aText); nsBidiDirection DirectionFromFlags(Flags aFlags); void ProcessPropertySeq(LevState *pLevState, uint8_t _prop, int32_t start, int32_t limit); void ResolveImplicitLevels(int32_t aStart, int32_t aLimit, DirProp aSOR, DirProp aEOR); void AdjustWSLevels(); @@ -946,15 +767,11 @@ private: bool GetRuns(); void GetSingleRun(nsBidiLevel aLevel); void ReorderLine(nsBidiLevel aMinLevel, nsBidiLevel aMaxLevel); static bool PrepareReorder(const nsBidiLevel *aLevels, int32_t aLength, int32_t *aIndexMap, nsBidiLevel *aMinLevel, nsBidiLevel *aMaxLevel); - - int32_t doWriteReverse(const char16_t *src, int32_t srcLength, - char16_t *dest, uint16_t options); - }; #endif // _nsBidi_h_
--- a/layout/base/nsBidiPresUtils.cpp +++ b/layout/base/nsBidiPresUtils.cpp @@ -203,17 +203,17 @@ struct BidiParagraphData { void EmptyBuffer() { mBuffer.SetLength(0); } nsresult SetPara() { return mBidiEngine->SetPara(mBuffer.get(), BufferLength(), - mParaLevel, nullptr); + mParaLevel); } /** * mParaLevel can be NSBIDI_DEFAULT_LTR as well as NSBIDI_LTR or NSBIDI_RTL. * GetParaLevel() returns the actual (resolved) paragraph level which is * always either NSBIDI_LTR or NSBIDI_RTL */ nsBidiLevel GetParaLevel() @@ -2007,17 +2007,17 @@ nsresult nsBidiPresUtils::ProcessText(co nsBidi* aBidiEngine) { NS_ASSERTION((aPosResolve == nullptr) != (aPosResolveCount > 0), "Incorrect aPosResolve / aPosResolveCount arguments"); int32_t runCount; nsAutoString textBuffer(aText, aLength); - nsresult rv = aBidiEngine->SetPara(aText, aLength, aBaseLevel, nullptr); + nsresult rv = aBidiEngine->SetPara(aText, aLength, aBaseLevel); if (NS_FAILED(rv)) return rv; rv = aBidiEngine->CountRuns(&runCount); if (NS_FAILED(rv)) return rv; nscoord xOffset = 0;
--- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -693,17 +693,17 @@ bool nsDisplayListBuilder::NeedToForceTr AnimatedGeometryRoot* nsDisplayListBuilder::WrapAGRForFrame(nsIFrame* aAnimatedGeometryRoot, AnimatedGeometryRoot* aParent /* = nullptr */) { MOZ_ASSERT(IsAnimatedGeometryRoot(aAnimatedGeometryRoot)); AnimatedGeometryRoot* result = nullptr; if (!mFrameToAnimatedGeometryRootMap.Get(aAnimatedGeometryRoot, &result)) { - MOZ_ASSERT(aAnimatedGeometryRoot != RootReferenceFrame()); + MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(RootReferenceFrame(), aAnimatedGeometryRoot)); AnimatedGeometryRoot* parent = aParent; if (!parent) { nsIFrame* parentFrame = nsLayoutUtils::GetCrossDocParentFrame(aAnimatedGeometryRoot); if (parentFrame) { nsIFrame* parentAGRFrame = FindAnimatedGeometryRootFrameFor(parentFrame); parent = WrapAGRForFrame(parentAGRFrame); } } @@ -712,17 +712,17 @@ nsDisplayListBuilder::WrapAGRForFrame(ns } MOZ_ASSERT(!aParent || result->mParentAGR == aParent); return result; } AnimatedGeometryRoot* nsDisplayListBuilder::FindAnimatedGeometryRootFor(nsIFrame* aFrame) { - if (!IsForPainting()) { + if (!IsPaintingToWindow()) { return &mRootAGR; } if (aFrame == mCurrentFrame) { return mCurrentAGR; } AnimatedGeometryRoot* result = nullptr; if (mFrameToAnimatedGeometryRootMap.Get(aFrame, &result)) { return result; @@ -1086,18 +1086,26 @@ IsStickyFrameActive(nsDisplayListBuilder nsIScrollableFrame* sf = do_QueryFrame(parent); return sf->IsScrollingActive(aBuilder) && sf->GetScrolledFrame() == cursor; } bool nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParent) { - if (aFrame == mReferenceFrame) + if (aFrame == mReferenceFrame) { return true; + } + if (!IsPaintingToWindow()) { + if (aParent) { + *aParent = nsLayoutUtils::GetCrossDocParentFrame(aFrame); + } + return false; + } + if (nsLayoutUtils::IsPopup(aFrame)) return true; if (ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(aFrame)) return true; if (!aFrame->GetParent() && nsLayoutUtils::ViewportHasDisplayPort(aFrame->PresContext())) { // Viewport frames in a display port need to be animated geometry roots // for background-attachment:fixed elements. @@ -1140,16 +1148,17 @@ nsDisplayListBuilder::IsAnimatedGeometry *aParent = parent; } return false; } nsIFrame* nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame) { + MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(RootReferenceFrame(), aFrame)); nsIFrame* cursor = aFrame; while (cursor != RootReferenceFrame()) { nsIFrame* next; if (IsAnimatedGeometryRoot(cursor, &next)) return cursor; cursor = next; } return cursor; @@ -4552,16 +4561,31 @@ nsDisplayResolution::nsDisplayResolution } #ifdef NS_BUILD_REFCNT_LOGGING nsDisplayResolution::~nsDisplayResolution() { MOZ_COUNT_DTOR(nsDisplayResolution); } #endif +void +nsDisplayResolution::HitTest(nsDisplayListBuilder* aBuilder, + const nsRect& aRect, + HitTestState* aState, + nsTArray<nsIFrame*> *aOutFrames) +{ +#if defined(MOZ_SINGLE_PROCESS_APZ) + nsIPresShell* presShell = mFrame->PresContext()->PresShell(); + nsRect rect = aRect.RemoveResolution(presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f); + mList.HitTest(aBuilder, rect, aState, aOutFrames); +#else + mList.HitTest(aBuilder, aRect, aState, aOutFrames); +#endif // MOZ_SINGLE_PROCESS_APZ +} + already_AddRefed<Layer> nsDisplayResolution::BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) { nsIPresShell* presShell = mFrame->PresContext()->PresShell(); ContainerLayerParameters containerParameters( presShell->GetResolution(), presShell->GetResolution(), nsIntPoint(), aContainerParameters); @@ -4878,32 +4902,32 @@ nsDisplayTransform::nsDisplayTransform(n MOZ_COUNT_CTOR(nsDisplayTransform); MOZ_ASSERT(aFrame, "Must have a frame!"); Init(aBuilder); } void nsDisplayTransform::SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder) { + mAnimatedGeometryRootForChildren = mAnimatedGeometryRoot; if (mFrame == aBuilder->RootReferenceFrame()) { return; } nsIFrame *outerFrame = nsLayoutUtils::GetCrossDocParentFrame(mFrame); mReferenceFrame = aBuilder->FindReferenceFrameFor(outerFrame); mToReferenceFrame = mFrame->GetOffsetToCrossDoc(mReferenceFrame); - mAnimatedGeometryRootForChildren = mAnimatedGeometryRoot; if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(mFrame)) { // This is an odd special case. If we are both IsFixedPosFrameInDisplayPort // and transformed that we are our own AGR parent. // We want our frame to be our AGR because FrameLayerBuilder uses our AGR to // determine if we are inside a fixed pos subtree. If we use the outer AGR // from outside the fixed pos subtree FLB can't tell that we are fixed pos. mAnimatedGeometryRoot = mAnimatedGeometryRootForChildren; - } else { + } else if (mAnimatedGeometryRoot->mParentAGR) { mAnimatedGeometryRoot = mAnimatedGeometryRoot->mParentAGR; } mVisibleRect = aBuilder->GetDirtyRect() + mToReferenceFrame; } void nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder) {
--- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -3503,17 +3503,20 @@ protected: */ class nsDisplayResolution : public nsDisplaySubDocument { public: nsDisplayResolution(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, uint32_t aFlags); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayResolution(); #endif - + virtual void HitTest(nsDisplayListBuilder* aBuilder, + const nsRect& aRect, + HitTestState* aState, + nsTArray<nsIFrame*> *aOutFrames) override; virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) override; NS_DISPLAY_DECL_NAME("Resolution", TYPE_RESOLUTION) }; /** * A display item used to represent sticky position elements. The contents
--- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -135,20 +135,20 @@ typedef struct CapturingContentInfo { // capture should only be allowed during a mousedown event bool mAllowed; bool mPointerLock; bool mRetargetToElement; bool mPreventDrag; mozilla::StaticRefPtr<nsIContent> mContent; } CapturingContentInfo; -// 327d78a0-0680-4709-b209-1cf9578720e6 +// 5023beaa-0e54-4fc7-b9dc-0344dc4fb8be #define NS_IPRESSHELL_IID \ -{ 0x327d78a0, 0x0680, 0x4709, \ - { 0xb2, 0x09, 0x1c, 0xf9, 0x57, 0x87, 0x20, 0xe6 } } +{ 0x5023beaa, 0x0e54, 0x4fc7, \ + { 0xb9, 0xdc, 0x03, 0x44, 0xdc, 0x4f, 0xb8, 0xbe } } // debug VerifyReflow flags #define VERIFY_REFLOW_ON 0x01 #define VERIFY_REFLOW_NOISY 0x02 #define VERIFY_REFLOW_ALL 0x04 #define VERIFY_REFLOW_DUMP_COMMANDS 0x08 #define VERIFY_REFLOW_NOISY_RC 0x10 #define VERIFY_REFLOW_REALLY_NOISY_RC 0x20 @@ -1411,16 +1411,17 @@ public: * resolution bounds are sane, and the resolution of this was * actually updated. * * The resolution defaults to 1.0. */ virtual nsresult SetResolution(float aResolution) = 0; float GetResolution() { return mResolution.valueOr(1.0); } virtual float GetCumulativeResolution() = 0; + virtual float GetCumulativeScaleResolution() = 0; /** * Was the current resolution set by the user or just default initialized? */ bool IsResolutionSet() { return mResolution.isSome(); } /** * Similar to SetResolution() but also increases the scale of the content
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -2106,17 +2106,21 @@ nsLayoutUtils::GetEventCoordinatesRelati nsIWidget* frameWidget = view->GetWidget(); if (frameWidget && frameWidget == aWidget) { // Special case this cause it happens a lot. // This also fixes bug 664707, events in the extra-special case of select // dropdown popups that are transformed. nsPresContext* presContext = aFrame->PresContext(); nsPoint pt(presContext->DevPixelsToAppUnits(aPoint.x), presContext->DevPixelsToAppUnits(aPoint.y)); - return pt - view->ViewToWidgetOffset(); + pt = pt - view->ViewToWidgetOffset(); +#if defined(MOZ_SINGLE_PROCESS_APZ) + pt = pt.RemoveResolution(presContext->PresShell()->GetCumulativeScaleResolution()); +#endif // MOZ_SINGLE_PROCESS_APZ + return pt; } } /* If we walk up the frame tree and discover that any of the frames are * transformed, we need to do extra work to convert from the global * space to the local space. */ nsIFrame* rootFrame = aFrame; @@ -2141,16 +2145,22 @@ nsLayoutUtils::GetEventCoordinatesRelati return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); } // Convert from root document app units to app units of the document aFrame // is in. int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel(); int32_t localAPD = aFrame->PresContext()->AppUnitsPerDevPixel(); widgetToView = widgetToView.ScaleToOtherAppUnits(rootAPD, localAPD); +#if defined(MOZ_SINGLE_PROCESS_APZ) + nsIPresShell* shell = aFrame->PresContext()->PresShell(); + + // XXX Bug 1224748 - Update nsLayoutUtils functions to correctly handle nsPresShell resolution + widgetToView = widgetToView.RemoveResolution(shell->GetCumulativeScaleResolution()); +#endif /* If we encountered a transform, we can't do simple arithmetic to figure * out how to convert back to aFrame's coordinates and must use the CTM. */ if (transformFound || aFrame->IsSVGText()) { return TransformRootPointToFrame(aFrame, widgetToView); } @@ -2888,18 +2898,22 @@ nsLayoutUtils::TranslateViewToWidget(nsP nsIWidget* aWidget) { nsPoint viewOffset; nsIWidget* viewWidget = aView->GetNearestWidget(&viewOffset); if (!viewWidget) { return LayoutDeviceIntPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); } - LayoutDeviceIntPoint relativeToViewWidget(aPresContext->AppUnitsToDevPixels(aPt.x + viewOffset.x), - aPresContext->AppUnitsToDevPixels(aPt.y + viewOffset.y)); + nsPoint pt = aPt + viewOffset; +#if defined(MOZ_SINGLE_PROCESS_APZ) + pt = pt.ApplyResolution(aPresContext->PresShell()->GetCumulativeScaleResolution()); +#endif // MOZ_SINGLE_PROCESS_APZ + LayoutDeviceIntPoint relativeToViewWidget(aPresContext->AppUnitsToDevPixels(pt.x), + aPresContext->AppUnitsToDevPixels(pt.y)); return relativeToViewWidget + WidgetToWidgetOffset(viewWidget, aWidget); } // Combine aNewBreakType with aOrigBreakType, but limit the break types // to NS_STYLE_CLEAR_LEFT, RIGHT, BOTH. uint8_t nsLayoutUtils::CombineBreakType(uint8_t aOrigBreakType, uint8_t aNewBreakType)
--- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1079,16 +1079,30 @@ PresShell::Destroy() // dump out cumulative text perf metrics gfxTextPerfMetrics* tp; if (mPresContext && (tp = mPresContext->GetTextPerfMetrics())) { tp->Accumulate(); if (tp->cumulative.numChars > 0) { LogTextPerfStats(tp, this, tp->cumulative, 0.0, eLog_totals, nullptr); } } + if (mPresContext) { + gfxUserFontSet* fs = mPresContext->GetUserFontSet(); + if (fs) { + uint32_t fontCount; + uint64_t fontSize; + fs->GetLoadStatistics(fontCount, fontSize); + Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, fontCount); + Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE, + uint32_t(fontSize/1024)); + } else { + Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, 0); + Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE, 0); + } + } #ifdef MOZ_REFLOW_PERF DumpReflows(); if (mReflowCountMgr) { delete mReflowCountMgr; mReflowCountMgr = nullptr; } #endif @@ -5332,16 +5346,32 @@ float PresShell::GetCumulativeResolution float resolution = GetResolution(); nsPresContext* parentCtx = GetPresContext()->GetParentPresContext(); if (parentCtx) { resolution *= parentCtx->PresShell()->GetCumulativeResolution(); } return resolution; } +float PresShell::GetCumulativeScaleResolution() +{ + float resolution = 1.0; + nsIPresShell* currentShell = this; + while (currentShell) { + resolution *= currentShell->ScaleToResolution() ? currentShell->GetResolution() : 1.0f; + nsPresContext* parentCtx = currentShell->GetPresContext()->GetParentPresContext(); + if (parentCtx) { + currentShell = parentCtx->PresShell(); + } else { + currentShell = nullptr; + } + } + return resolution; +} + void PresShell::SetRenderingState(const RenderingState& aState) { if (mRenderFlags != aState.mRenderFlags) { // Rendering state changed in a way that forces us to flush any // retained layers we might already have. LayerManager* manager = GetLayerManager(); if (manager) { FrameLayerBuilder::InvalidateAllLayers(manager); @@ -7078,18 +7108,19 @@ PresShell::HandleEvent(nsIFrame* aFrame, retargetEventDoc = window->GetExtantDoc(); if (!retargetEventDoc) return NS_OK; } else if (capturingContent) { // if the mouse is being captured then retarget the mouse event at the // document that is being captured. retargetEventDoc = capturingContent->GetCrossShadowCurrentDoc(); #ifdef ANDROID - } else if (aEvent->mClass == eTouchEventClass || - (aEvent->AsMouseEvent() && aEvent->AsMouseEvent()->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH)) { + } else if ((aEvent->mClass == eTouchEventClass) || + (aEvent->mClass == eMouseEventClass) || + (aEvent->mClass == eWheelEventClass)) { retargetEventDoc = GetTouchEventTargetDocument(); #endif } if (retargetEventDoc) { nsCOMPtr<nsIPresShell> presShell = retargetEventDoc->GetShell(); if (!presShell) return NS_OK;
--- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -213,16 +213,17 @@ public: virtual nsresult SetResolution(float aResolution) override { return SetResolutionImpl(aResolution, /* aScaleToResolution = */ false); } virtual nsresult SetResolutionAndScaleTo(float aResolution) override { return SetResolutionImpl(aResolution, /* aScaleToResolution = */ true); } virtual bool ScaleToResolution() const override; virtual float GetCumulativeResolution() override; + virtual float GetCumulativeScaleResolution() override; //nsIViewObserver interface virtual void Paint(nsView* aViewToPaint, const nsRegion& aDirtyRegion, uint32_t aFlags) override; virtual nsresult HandleEvent(nsIFrame* aFrame, mozilla::WidgetGUIEvent* aEvent, bool aDontRetargetEvents,
--- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -5354,31 +5354,35 @@ ScrollFrameHelper::SaveState() const // that ScrollToRestoredPosition uses). This ensures if a reframe occurs // while we're in the process of loading content to scroll to a restored // position, we'll keep trying after the reframe. nsPoint pt = GetLogicalScrollPosition(); if (mRestorePos.y != -1 && pt == mLastPos) { pt = mRestorePos; } state->SetScrollState(pt); - nsIPresShell* shell = mOuter->PresContext()->PresShell(); - state->SetResolution(shell->GetResolution()); - state->SetScaleToResolution(shell->ScaleToResolution()); + if (mIsRoot) { + // Only save resolution properties for root scroll frames + nsIPresShell* shell = mOuter->PresContext()->PresShell(); + state->SetResolution(shell->GetResolution()); + state->SetScaleToResolution(shell->ScaleToResolution()); + } return state; } void ScrollFrameHelper::RestoreState(nsPresState* aState) { mRestorePos = aState->GetScrollState(); mDidHistoryRestore = true; mLastPos = mScrolledFrame ? GetLogicalScrollPosition() : nsPoint(0,0); - // Scaling-to-resolution should only be used on root scroll frames. - MOZ_ASSERT(mIsRoot || !aState->GetScaleToResolution()); + // Resolution properties should only exist on root scroll frames. + MOZ_ASSERT(mIsRoot || (!aState->GetScaleToResolution() && + aState->GetResolution() == 1.0)); if (mIsRoot) { nsIPresShell* presShell = mOuter->PresContext()->PresShell(); if (aState->GetScaleToResolution()) { presShell->SetResolutionAndScaleTo(aState->GetResolution()); } else { presShell->SetResolution(aState->GetResolution()); }
--- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -436,16 +436,23 @@ nsSubDocumentFrame::BuildDisplayList(nsD nsIScrollableFrame* rootScrollableFrame = do_QueryFrame(rootScrollFrame); dirty = rootScrollableFrame->ExpandRectToNearlyVisible(dirty); } } } aBuilder->EnterPresShell(subdocRootFrame, pointerEventsNone && !passPointerEventsToChildren); + +#if defined(MOZ_SINGLE_PROCESS_APZ) + if (!haveDisplayPort) { + // Remove nsPresShell resolution + dirty = dirty.RemoveResolution(presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f); + } +#endif } else { dirty = aDirtyRect; } DisplayListClipState::AutoSaveRestore clipState(aBuilder); if (ShouldClipSubdocument()) { clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this); }
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-1a-ltr-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 1: + AB ( CD [ & ef ] ! ) gh +--> +<div>בא(דג[&ef]!)gh</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-1a-ltr.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 1: + AB ( CD [ & ef ] ! ) gh +--> +<!-- LTR --> +<div>אב(גד[&ef]!)gh</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-1a-rtl-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 1: + AB ( CD [ & ef ] ! ) gh +--> +<div>gh(![ef&]דג)בא</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-1a-rtl.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 1: + AB ( CD [ & ef ] ! ) gh +--> +<!-- RTL --> +<div dir=rtl>אב(גד[&ef]!)gh</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-1b-ltr-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 1, AB and gh swapped: + gh ( CD [ & ef ] ! ) AB +--> +<div>gh(דג[&ef]!)בא</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-1b-ltr.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 1, AB and gh swapped: + gh ( CD [ & ef ] ! ) AB +--> +<!-- LTR --> +<div>gh(גד[&ef]!)אב</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-1b-rtl-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 1, AB and gh swapped: + gh ( CD [ & ef ] ! ) AB +--> +<div>בא(![ef&]דג)gh</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-1b-rtl.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 1, AB and gh swapped: + gh ( CD [ & ef ] ! ) AB +--> +<!-- RTL --> +<div dir=rtl>gh(גד[&ef]!)אב</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-1c-ltr-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 1: + AB ( ef [ & CD ] ! ) gh +--> +<div>בא(ef[&דג]!)gh</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-1c-ltr.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 1, CD and ef swapped: + AB ( ef [ & CD ] ! ) gh +--> +<!-- LTR --> +<div>אב(ef[&גד]!)gh</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-1c-rtl-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 1: + AB ( ef [ & CD ] ! ) gh +--> +<div>gh(![דג&]ef)בא</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-1c-rtl.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 1, CD and ef swapped: + AB ( ef [ & CD ] ! ) gh +--> +<!-- RTL --> +<div dir=rtl>אב(ef[&גד]!)gh</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-2a-ltr-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 2: + smith (fabrikam ARABIC) HEBREW +--> +<div>smith (fabrikam يبرعلا) תירבע</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-2a-ltr.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 2: + smith (fabrikam ARABIC) HEBREW +--> +<!-- LTR --> +<div>smith (fabrikam العربي) עברית</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-2a-rtl-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 2: + smith (fabrikam ARABIC) HEBREW +--> +<div>תירבע (يبرعلا fabrikam) smith</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-2a-rtl.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 2: + smith (fabrikam ARABIC) HEBREW +--> +<!-- RTL --> +<div dir=rtl>smith (fabrikam العربي) עברית</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-2b-ltr-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 2 with smith and HEBREW exchanged: + HEBREW (fabrikam ARABIC) smith +--> +<div>תירבע (fabrikam يبرعلا) smith</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-2b-ltr.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 2 with smith and HEBREW exchanged: + HEBREW (fabrikam ARABIC) smith +--> +<!-- LTR --> +<div>עברית (fabrikam العربي) smith</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-2b-rtl-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 2 with smith and HEBREW exchanged: + HEBREW (fabrikam ARABIC) smith +--> +<div>smith (يبرعلا fabrikam) תירבע</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-2b-rtl.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 2 with smith and HEBREW exchanged: + HEBREW (fabrikam ARABIC) smith +--> +<!-- RTL --> +<div dir=rtl>עברית (fabrikam العربي) smith</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-2c-ltr-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 2 with fabrikam and ARABIC exchanged: + smith (ARABIC fabrikam) HEBREW +--> +<div>smith (يبرعلا fabrikam) תירבע</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-2c-ltr.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 2 with fabrikam and ARABIC exchanged: + smith (ARABIC fabrikam) HEBREW +--> +<!-- LTR --> +<div>smith (العربي fabrikam) עברית</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-2c-rtl-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 2 with fabrikam and ARABIC exchanged: + smith (ARABIC fabrikam) HEBREW +--> +<div>תירבע (fabrikam يبرعلا) smith</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-2c-rtl.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 2 with fabrikam and ARABIC exchanged: + smith (ARABIC fabrikam) HEBREW +--> +<!-- RTL --> +<div dir=rtl>smith (العربي fabrikam) עברית</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-3a-ltr-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 3: + ARABIC book(s) +--> +<div>يبرعلا book(s)</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-3a-ltr.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 3: + ARABIC book(s) +--> +<!-- LTR --> +<div>العربي book(s)</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-3a-rtl-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; unicode-bidi: bidi-override; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 3: + ARABIC book(s) +--> +<div>book(s) يبرعلا</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-3a-rtl.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 3: + ARABIC book(s) +--> +<!-- RTL --> +<div dir=rtl>العربي book(s)</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-3b-ltr-ref.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +span { unicode-bidi: -moz-isolate; unicode-bidi: isolate; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 3, LTR and RTL exchanged: + arabic BOOK(S) +--> +<div>arabic (<span dir=rtl>كُتُب</span>)<span dir=rtl>كِتَاب</span></div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-3b-ltr.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 3, LTR and RTL exchanged: + arabic BOOK(S) +--> +<!-- LTR --> +<div>arabic كِتَاب(كُتُب)</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-3b-rtl-ref.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +span { unicode-bidi: -moz-isolate; unicode-bidi: isolate; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 3, LTR and RTL exchanged: + arabic BOOK(S) +--> +<div>(<span dir=rtl>كُتُب</span>)<span dir=rtl>كِتَاب</span> arabic</div>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bidi/brackets-3b-rtl.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<style> +body { font: 32px monospace; } +div { text-align: center; } +</style> + +<body> + +<!-- + http://unicode.org/reports/tr9/#N0, Example 3, LTR and RTL exchanged: + arabic BOOK(S) +--> +<!-- RTL --> +<div dir=rtl>arabic كِتَاب(كُتُب)</div>
--- a/layout/reftests/bidi/reftest.list +++ b/layout/reftests/bidi/reftest.list @@ -149,10 +149,26 @@ skip-if((B2G&&browserIsRemote)||Mulet) f == 922550-1.html 922550-1-ref.html == 1067268-1.html 1067268-1-ref.html == 1069941-inline-bidi-border-1.html 1069941-inline-bidi-border-1-ref.html == 1069941-inline-bidi-margin-1.html 1069941-inline-bidi-margin-1-ref.html skip-if(B2G||Mulet) != 1155359-1.xul 1155359-1-ref.xul == 1157726-1.html 1157726-1-ref.html == 1161752.html 1161752-ref.html == 1161752-5-embed.html 1161752-5-embed-ref.html +== brackets-1a-ltr.html brackets-1a-ltr-ref.html +== brackets-1a-rtl.html brackets-1a-rtl-ref.html +== brackets-1b-ltr.html brackets-1b-ltr-ref.html +== brackets-1b-rtl.html brackets-1b-rtl-ref.html +== brackets-1c-ltr.html brackets-1c-ltr-ref.html +== brackets-1c-rtl.html brackets-1c-rtl-ref.html +== brackets-2a-ltr.html brackets-2a-ltr-ref.html +fuzzy-if(Android,254,557) == brackets-2a-rtl.html brackets-2a-rtl-ref.html +== brackets-2b-ltr.html brackets-2b-ltr-ref.html +== brackets-2b-rtl.html brackets-2b-rtl-ref.html +== brackets-2c-ltr.html brackets-2c-ltr-ref.html +fuzzy-if(Android,254,231) == brackets-2c-rtl.html brackets-2c-rtl-ref.html +== brackets-3a-ltr.html brackets-3a-ltr-ref.html +== brackets-3a-rtl.html brackets-3a-rtl-ref.html +== brackets-3b-ltr.html brackets-3b-ltr-ref.html +== brackets-3b-rtl.html brackets-3b-rtl-ref.html == 1217833-1.html 1217833-1-ref.html == 1217833-2.html 1217833-2-ref.html
--- a/layout/style/FontFaceSet.cpp +++ b/layout/style/FontFaceSet.cpp @@ -13,16 +13,17 @@ #include "mozilla/dom/FontFaceSetIterator.h" #include "mozilla/dom/FontFaceSetLoadEvent.h" #include "mozilla/dom/FontFaceSetLoadEventBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/Logging.h" #include "mozilla/Preferences.h" #include "mozilla/Snprintf.h" +#include "mozilla/Telemetry.h" #include "nsCORSListenerProxy.h" #include "nsCSSParser.h" #include "nsDeviceContext.h" #include "nsFontFaceLoader.h" #include "nsIConsoleService.h" #include "nsIContentPolicy.h" #include "nsIContentSecurityPolicy.h" #include "nsIDocShell.h" @@ -34,16 +35,17 @@ #include "nsIWebNavigation.h" #include "nsNetUtil.h" #include "nsIProtocolHandler.h" #include "nsIInputStream.h" #include "nsPresContext.h" #include "nsPrintfCString.h" #include "nsStyleSet.h" #include "nsUTF8Utils.h" +#include "nsDOMNavigationTiming.h" using namespace mozilla; using namespace mozilla::css; using namespace mozilla::dom; #define LOG(args) MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args) #define LOG_ENABLED() MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), \ LogLevel::Debug) @@ -300,16 +302,27 @@ FontFaceSet::FindMatchingFontFaces(const FontFace* f = record.mFontFace; if (matchingFaces.Contains(f)) { aFontFaces.AppendElement(f); } } } } +TimeStamp +FontFaceSet::GetNavigationStartTimeStamp() +{ + TimeStamp navStart; + RefPtr<nsDOMNavigationTiming> timing(mDocument->GetNavigationTiming()); + if (timing) { + navStart = timing->GetNavigationStartTimeStamp(); + } + return navStart; +} + already_AddRefed<Promise> FontFaceSet::Load(JSContext* aCx, const nsAString& aFont, const nsAString& aText, ErrorResult& aRv) { FlushUserFontSet(); @@ -329,16 +342,17 @@ FontFaceSet::Load(JSContext* aCx, if (!promises.AppendElement(promise, fallible)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } } nsIGlobalObject* globalObject = GetParentObject(); if (!globalObject) { + aRv.Throw(NS_ERROR_FAILURE); return nullptr; } JS::Rooted<JSObject*> jsGlobal(aCx, globalObject->GetGlobalJSObject()); GlobalObject global(aCx, jsGlobal); RefPtr<Promise> result = Promise::All(global, promises, aRv); return result.forget(); @@ -1717,16 +1731,36 @@ FontFaceSet::UserFontSet::StartLoad(gfxU const gfxFontFaceSrc* aFontFaceSrc) { if (!mFontFaceSet) { return NS_ERROR_FAILURE; } return mFontFaceSet->StartLoad(aUserFontEntry, aFontFaceSrc); } +void +FontFaceSet::UserFontSet::RecordFontLoadDone(uint32_t aFontSize, + TimeStamp aDoneTime) +{ + mDownloadCount++; + mDownloadSize += aFontSize; + Telemetry::Accumulate(Telemetry::WEBFONT_SIZE, aFontSize / 1024); + + if (!mFontFaceSet) { + return; + } + + TimeStamp navStart = mFontFaceSet->GetNavigationStartTimeStamp(); + TimeStamp zero; + if (navStart != zero) { + Telemetry::AccumulateTimeDelta(Telemetry::WEBFONT_DOWNLOAD_TIME_AFTER_START, + navStart, aDoneTime); + } +} + /* virtual */ nsresult FontFaceSet::UserFontSet::LogMessage(gfxUserFontEntry* aUserFontEntry, const char* aMessage, uint32_t aFlags, nsresult aStatus) { if (!mFontFaceSet) { return NS_ERROR_FAILURE;
--- a/layout/style/FontFaceSet.h +++ b/layout/style/FontFaceSet.h @@ -63,16 +63,19 @@ public: FontFaceSet* GetFontFaceSet() { return mFontFaceSet; } virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc, nsIPrincipal** aPrincipal, bool* aBypassCache) override; virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry, const gfxFontFaceSrc* aFontFaceSrc) override; + void RecordFontLoadDone(uint32_t aFontSize, + mozilla::TimeStamp aDoneTime) override; + protected: virtual bool GetPrivateBrowsing() override; virtual nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad, const gfxFontFaceSrc* aFontFaceSrc, uint8_t*& aBuffer, uint32_t& aBufferLength) override; virtual nsresult LogMessage(gfxUserFontEntry* aUserFontEntry, const char* aMessage, @@ -288,16 +291,18 @@ private: int32_t& aStretch, uint8_t& aStyle, ErrorResult& aRv); void FindMatchingFontFaces(const nsAString& aFont, const nsAString& aText, nsTArray<FontFace*>& aFontFaces, mozilla::ErrorResult& aRv); + TimeStamp GetNavigationStartTimeStamp(); + RefPtr<UserFontSet> mUserFontSet; // The document this is a FontFaceSet for. nsCOMPtr<nsIDocument> mDocument; // A Promise that is fulfilled once all of the FontFace objects // in mRuleFaces and mNonRuleFaces that started or were loading at the // time the Promise was created have finished loading. It is rejected if
new file mode 100644 --- /dev/null +++ b/layout/style/crashtests/1226400-1.html @@ -0,0 +1,55 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>FontFaceSet::Load crasher</title> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + +<style type="text/css"> + +body { + margin: 50px; +} + +p { + margin: 0; + font-size: 300%; +} + +</style> + +</head> +<body> + +<p>This may crash on load...</p> + +<script> +var scriptText = ` + var fontFaceSet = document.fonts; + var link = document.createElement("link"); + link.onerror = link.onload = function() { + fontFaceSet.load("12px foo"); + } + link.rel = "stylesheet"; + link.href = "data:text/css,"; + document.body.appendChild(link); +`; + +var styleText = ` + @font-face { + font-family: foo; + src: url("data:text/ttf,"); + } +`; + +var ifr = document.createElement("iframe"); +document.body.appendChild(ifr); +var style = ifr.contentDocument.createElement("style"); +style.textContent = styleText; +ifr.contentDocument.body.appendChild(style); +var script = ifr.contentDocument.createElement("script"); +script.textContent = scriptText; +ifr.contentDocument.body.appendChild(script); +ifr.remove(); +</script> +</body> +</html>
--- a/layout/style/crashtests/crashtests.list +++ b/layout/style/crashtests/crashtests.list @@ -125,13 +125,14 @@ load 1161366-1.html load 1163446-1.html load 1164813-1.html load 1167782-1.html load 1186768-1.xhtml load 1200568-1.html load 1206105-1.html load 1223688-1.html load 1223694-1.html +load 1226400-1.html load 1227501-1.html load border-image-visited-link.html load font-face-truncated-src.html load large_border_image_width.html load long-url-list-stack-overflow.html
--- a/layout/style/nsFontFaceLoader.cpp +++ b/layout/style/nsFontFaceLoader.cpp @@ -8,16 +8,17 @@ #include "mozilla/Logging.h" #include "nsFontFaceLoader.h" #include "nsError.h" #include "nsContentUtils.h" #include "mozilla/Preferences.h" +#include "mozilla/Telemetry.h" #include "FontFaceSet.h" #include "nsPresContext.h" #include "nsIPrincipal.h" #include "nsIScriptSecurityManager.h" #include "nsIHttpChannel.h" #include "nsIContentPolicy.h" #include "nsContentPolicyUtils.h" @@ -34,16 +35,17 @@ nsFontFaceLoader::nsFontFaceLoader(gfxUs nsIURI* aFontURI, FontFaceSet* aFontFaceSet, nsIChannel* aChannel) : mUserFontEntry(aUserFontEntry), mFontURI(aFontURI), mFontFaceSet(aFontFaceSet), mChannel(aChannel) { + mStartTime = TimeStamp::Now(); } nsFontFaceLoader::~nsFontFaceLoader() { if (mUserFontEntry) { mUserFontEntry->mLoader = nullptr; } if (mLoadTimer) { @@ -143,22 +145,27 @@ nsFontFaceLoader::OnStreamComplete(nsISt { if (!mFontFaceSet) { // We've been canceled return aStatus; } mFontFaceSet->RemoveLoader(this); + TimeStamp doneTime = TimeStamp::Now(); + TimeDuration downloadTime = doneTime - mStartTime; + uint32_t downloadTimeMS = uint32_t(downloadTime.ToMilliseconds()); + Telemetry::Accumulate(Telemetry::WEBFONT_DOWNLOAD_TIME, downloadTimeMS); + if (LOG_ENABLED()) { nsAutoCString fontURI; mFontURI->GetSpec(fontURI); if (NS_SUCCEEDED(aStatus)) { - LOG(("userfonts (%p) download completed - font uri: (%s)\n", - this, fontURI.get())); + LOG(("userfonts (%p) download completed - font uri: (%s) time: %d ms\n", + this, fontURI.get(), downloadTimeMS)); } else { LOG(("userfonts (%p) download failed - font uri: (%s) error: %8.8x\n", this, fontURI.get(), aStatus)); } } if (NS_SUCCEEDED(aStatus)) { // for HTTP requests, check whether the request _actually_ succeeded; @@ -183,16 +190,18 @@ nsFontFaceLoader::OnStreamComplete(nsISt // (aString) when finished with it; the pointer is no longer valid // after FontDataDownloadComplete returns. // This is called even in the case of a failed download (HTTP 404, etc), // as there may still be data to be freed (e.g. an error page), // and we need to load the next source. bool fontUpdate = mUserFontEntry->FontDataDownloadComplete(aString, aStringLen, aStatus); + mFontFaceSet->GetUserFontSet()->RecordFontLoadDone(aStringLen, doneTime); + // when new font loaded, need to reflow if (fontUpdate) { nsTArray<gfxUserFontSet*> fontSets; mUserFontEntry->GetUserFontSets(fontSets); for (gfxUserFontSet* fontSet : fontSets) { nsPresContext* ctx = FontFaceSet::GetPresContextFor(fontSet); if (ctx) { // Update layout for the presence of the new font. Since this is
--- a/layout/style/nsFontFaceLoader.h +++ b/layout/style/nsFontFaceLoader.h @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* code for loading in @font-face defined font data */ #ifndef nsFontFaceLoader_h_ #define nsFontFaceLoader_h_ #include "mozilla/Attributes.h" +#include "mozilla/TimeStamp.h" #include "nsCOMPtr.h" #include "nsIStreamLoader.h" #include "nsIChannel.h" #include "gfxUserFontSet.h" #include "nsHashKeys.h" #include "nsTHashtable.h" #include "nsCSSRules.h" @@ -50,13 +51,13 @@ protected: virtual ~nsFontFaceLoader(); private: RefPtr<gfxUserFontEntry> mUserFontEntry; nsCOMPtr<nsIURI> mFontURI; RefPtr<mozilla::dom::FontFaceSet> mFontFaceSet; nsCOMPtr<nsIChannel> mChannel; nsCOMPtr<nsITimer> mLoadTimer; - + TimeStamp mStartTime; nsIStreamLoader* mStreamLoader; }; #endif /* !defined(nsFontFaceLoader_h_) */
--- a/layout/xul/crashtests/crashtests.list +++ b/layout/xul/crashtests/crashtests.list @@ -72,17 +72,17 @@ load 432058-1.xul load 432068-1.xul load 432068-2.xul load 433296-1.xul load 433429.xul load 434458-1.xul load 452185.html load 460900-1.xul load 464149-1.xul -asserts-if(winWidget,1) asserts-if(Android&&asyncPan,1) load 464407-1.xhtml # Bug 450974 on win, Bug 1217984 on android +asserts-if(winWidget,1) load 464407-1.xhtml # Bug 450974 on win load 467080.xul load 467481-1.xul load 470063-1.html load 470272.html load 472189.xul load 475133.html load 488210-1.xhtml load 495728-1.xul
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp +++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp @@ -117,17 +117,17 @@ ClearKeyDecryptionManager::ExpectKeyId(K } mDecryptors[aKeyId]->AddRef(); } void ClearKeyDecryptionManager::ReleaseKeyId(KeyId aKeyId) { CK_LOGD("ClearKeyDecryptionManager::ReleaseKeyId"); - assert(HasKeyForKeyId(aKeyId)); + assert(HasSeenKeyId(aKeyId)); ClearKeyDecryptor* decryptor = mDecryptors[aKeyId]; if (!decryptor->Release()) { mDecryptors.erase(aKeyId); } } GMPErr
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp +++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp @@ -108,63 +108,68 @@ ClearKeyPersistence::GetNewSessionId(GMP } class CreateSessionTask : public GMPTask { public: CreateSessionTask(ClearKeySessionManager* aTarget, uint32_t aCreateSessionToken, uint32_t aPromiseId, + const string& aInitDataType, const uint8_t* aInitData, uint32_t aInitDataSize, GMPSessionType aSessionType) : mTarget(aTarget) , mCreateSessionToken(aCreateSessionToken) , mPromiseId(aPromiseId) + , mInitDataType(aInitDataType) , mSessionType(aSessionType) { mInitData.insert(mInitData.end(), aInitData, aInitData + aInitDataSize); } virtual void Run() override { mTarget->CreateSession(mCreateSessionToken, mPromiseId, - "cenc", - strlen("cenc"), + mInitDataType.c_str(), + mInitDataType.size(), &mInitData.front(), mInitData.size(), mSessionType); } virtual void Destroy() override { delete this; } private: RefPtr<ClearKeySessionManager> mTarget; uint32_t mCreateSessionToken; uint32_t mPromiseId; + const string mInitDataType; vector<uint8_t> mInitData; GMPSessionType mSessionType; }; /* static */ bool ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance, uint32_t aCreateSessionToken, uint32_t aPromiseId, + const string& aInitDataType, const uint8_t* aInitData, uint32_t aInitDataSize, GMPSessionType aSessionType) { if (sPersistentKeyState >= LOADED) { return false; } GMPTask* t = new CreateSessionTask(aInstance, aCreateSessionToken, aPromiseId, + aInitDataType, aInitData, aInitDataSize, aSessionType); sTasksBlockedOnSessionIdLoad.push_back(t); return true; } class LoadSessionTask : public GMPTask {
--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.h +++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h @@ -26,16 +26,17 @@ class ClearKeyPersistence { public: static void EnsureInitialized(); static std::string GetNewSessionId(GMPSessionType aSessionType); static bool DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance, uint32_t aCreateSessionToken, uint32_t aPromiseId, + const std::string& aInitDataType, const uint8_t* aInitData, uint32_t aInitDataSize, GMPSessionType aSessionType); static bool DeferLoadSessionIfNotReady(ClearKeySessionManager* aInstance, uint32_t aPromiseId, const char* aSessionId, uint32_t aSessionIdLength);
--- a/media/gmp-clearkey/0.1/ClearKeySession.cpp +++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp @@ -37,35 +37,47 @@ ClearKeySession::ClearKeySession(const s } ClearKeySession::~ClearKeySession() { CK_LOGD("ClearKeySession dtor %p", this); auto& keyIds = GetKeyIds(); for (auto it = keyIds.begin(); it != keyIds.end(); it++) { - assert(ClearKeyDecryptionManager::Get()->HasKeyForKeyId(*it)); + assert(ClearKeyDecryptionManager::Get()->HasSeenKeyId(*it)); ClearKeyDecryptionManager::Get()->ReleaseKeyId(*it); mCallback->KeyStatusChanged(&mSessionId[0], mSessionId.size(), &(*it)[0], it->size(), kGMPUnknown); } } void ClearKeySession::Init(uint32_t aCreateSessionToken, uint32_t aPromiseId, + const std::string& aInitDataType, const uint8_t* aInitData, uint32_t aInitDataSize) { CK_LOGD("ClearKeySession::Init"); - ClearKeyUtils::ParseInitData(aInitData, aInitDataSize, mKeyIds); + if (aInitDataType == "cenc") { + ClearKeyUtils::ParseCENCInitData(aInitData, aInitDataSize, mKeyIds); + } else if (aInitDataType == "keyids") { + std::string sessionType; + ClearKeyUtils::ParseKeyIdsInitData(aInitData, aInitDataSize, mKeyIds, sessionType); + if (sessionType != ClearKeyUtils::SessionTypeToString(mSessionType)) { + const char message[] = "Session type specified in keyids init data doesn't match session type."; + mCallback->RejectPromise(aPromiseId, kGMPAbortError, message, strlen(message)); + return; + } + } + if (!mKeyIds.size()) { - const char message[] = "Couldn't parse cenc key init data"; + const char message[] = "Couldn't parse init data"; mCallback->RejectPromise(aPromiseId, kGMPAbortError, message, strlen(message)); return; } mCallback->SetSessionId(aCreateSessionToken, &mSessionId[0], mSessionId.length()); mCallback->ResolvePromise(aPromiseId); }
--- a/media/gmp-clearkey/0.1/ClearKeySession.h +++ b/media/gmp-clearkey/0.1/ClearKeySession.h @@ -33,16 +33,17 @@ public: GMPSessionType aSessionType); ~ClearKeySession(); const std::vector<KeyId>& GetKeyIds() const { return mKeyIds; } void Init(uint32_t aCreateSessionToken, uint32_t aPromiseId, + const string& aInitDataType, const uint8_t* aInitData, uint32_t aInitDataSize); GMPSessionType Type() const; void AddKeyId(const KeyId& aKeyId); const std::string& Id() const { return mSessionId; }
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp +++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp @@ -96,37 +96,39 @@ ClearKeySessionManager::CreateSession(ui const char* aInitDataType, uint32_t aInitDataTypeSize, const uint8_t* aInitData, uint32_t aInitDataSize, GMPSessionType aSessionType) { CK_LOGD("ClearKeySessionManager::CreateSession type:%s", aInitDataType); - // initDataType must be "cenc". - if (strcmp("cenc", aInitDataType)) { + string initDataType(aInitDataType, aInitDataType + aInitDataTypeSize); + // initDataType must be "cenc" or "keyids". + if (initDataType != "cenc" && initDataType != "keyids") { mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError, nullptr /* message */, 0 /* messageLen */); return; } if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this, aCreateSessionToken, aPromiseId, + initDataType, aInitData, aInitDataSize, aSessionType)) { return; } string sessionId = ClearKeyPersistence::GetNewSessionId(aSessionType); assert(mSessions.find(sessionId) == mSessions.end()); ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType); - session->Init(aCreateSessionToken, aPromiseId, aInitData, aInitDataSize); + session->Init(aCreateSessionToken, aPromiseId, initDataType, aInitData, aInitDataSize); mSessions[sessionId] = session; const vector<KeyId>& sessionKeys = session->GetKeyIds(); vector<KeyId> neededKeys; for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) { // Need to request this key ID from the client. We always send a key // request, whether or not another session has sent a request with the same // key ID. Otherwise a script can end up waiting for another script to
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp +++ b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp @@ -127,18 +127,19 @@ EncodeBase64Web(vector<uint8_t> aBinary, assert(idx < MOZ_ARRAY_LENGTH(sAlphabet)); // out of bounds index for 'sAlphabet' out[i] = sAlphabet[idx]; } return true; } /* static */ void -ClearKeyUtils::ParseInitData(const uint8_t* aInitData, uint32_t aInitDataSize, - vector<KeyId>& aOutKeys) +ClearKeyUtils::ParseCENCInitData(const uint8_t* aInitData, + uint32_t aInitDataSize, + vector<KeyId>& aOutKeyIds) { using mozilla::BigEndian; uint32_t size = 0; for (uint32_t offset = 0; offset + sizeof(uint32_t) < aInitDataSize; offset += size) { const uint8_t* data = aInitData + offset; size = BigEndian::readUint32(data); data += sizeof(uint32_t); @@ -177,43 +178,43 @@ ClearKeyUtils::ParseInitData(const uint8 uint32_t kidCount = BigEndian::readUint32(data); data += sizeof(uint32_t); if (data + kidCount * CLEARKEY_KEY_LEN > aInitData + aInitDataSize) { CK_LOGE("pssh key IDs overflow init data buffer"); return; } for (uint32_t i = 0; i < kidCount; i++) { - aOutKeys.push_back(KeyId(data, data + CLEARKEY_KEY_LEN)); + aOutKeyIds.push_back(KeyId(data, data + CLEARKEY_KEY_LEN)); data += CLEARKEY_KEY_LEN; } } } /* static */ void ClearKeyUtils::MakeKeyRequest(const vector<KeyId>& aKeyIDs, string& aOutRequest, GMPSessionType aSessionType) { assert(aKeyIDs.size() && aOutRequest.empty()); - aOutRequest.append("{ \"kids\":["); + aOutRequest.append("{\"kids\":["); for (size_t i = 0; i < aKeyIDs.size(); i++) { if (i) { aOutRequest.append(","); } aOutRequest.append("\""); string base64key; EncodeBase64Web(aKeyIDs[i], base64key); aOutRequest.append(base64key); aOutRequest.append("\""); } - aOutRequest.append("], \"type\":"); + aOutRequest.append("],\"type\":"); aOutRequest.append("\""); aOutRequest.append(SessionTypeToString(aSessionType)); aOutRequest.append("\"}"); } #define EXPECT_SYMBOL(CTX, X) do { \ if (GetNextSymbol(CTX) != (X)) { \ @@ -503,16 +504,90 @@ ClearKeyUtils::ParseJWK(const uint8_t* a } // Consume '}' from end of object. EXPECT_SYMBOL(ctx, '}'); return true; } +static bool +ParseKeyIds(ParserContext& aCtx, vector<KeyId>& aOutKeyIds) +{ + // Consume start of array. + EXPECT_SYMBOL(aCtx, '['); + + while (true) { + string label; + vector<uint8_t> keyId; + if (!GetNextLabel(aCtx, label) || + !DecodeBase64KeyOrId(label, keyId)) { + return false; + } + assert(!keyId.empty()); + aOutKeyIds.push_back(keyId); + + uint8_t sym = PeekSymbol(aCtx); + if (!sym || sym == ']') { + break; + } + + EXPECT_SYMBOL(aCtx, ','); + } + + return GetNextSymbol(aCtx) == ']'; +} + + +/* static */ bool +ClearKeyUtils::ParseKeyIdsInitData(const uint8_t* aInitData, + uint32_t aInitDataSize, + vector<KeyId>& aOutKeyIds, + string& aOutSessionType) +{ + aOutSessionType = "temporary"; + + ParserContext ctx; + ctx.mIter = aInitData; + ctx.mEnd = aInitData + aInitDataSize; + + // Consume '{' from start of object. + EXPECT_SYMBOL(ctx, '{'); + + while (true) { + string label; + // Consume member kids. + if (!GetNextLabel(ctx, label)) return false; + EXPECT_SYMBOL(ctx, ':'); + + if (label == "kids") { + // Parse "kids" array. + if (!ParseKeyIds(ctx, aOutKeyIds)) return false; + } else if (label == "type") { + // Consume type string. + if (!GetNextLabel(ctx, aOutSessionType)) return false; + } else { + SkipToken(ctx); + } + + // Check for end of object. + if (PeekSymbol(ctx) == '}') { + break; + } + + // Consume ',' between object members. + EXPECT_SYMBOL(ctx, ','); + } + + // Consume '}' from end of object. + EXPECT_SYMBOL(ctx, '}'); + + return true; +} + /* static */ const char* ClearKeyUtils::SessionTypeToString(GMPSessionType aSessionType) { switch (aSessionType) { case kGMPTemporySession: return "temporary"; case kGMPPersistentSession: return "persistent"; default: { assert(false); // Should not reach here.
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.h +++ b/media/gmp-clearkey/0.1/ClearKeyUtils.h @@ -49,18 +49,24 @@ struct KeyIdPair }; class ClearKeyUtils { public: static void DecryptAES(const std::vector<uint8_t>& aKey, std::vector<uint8_t>& aData, std::vector<uint8_t>& aIV); - static void ParseInitData(const uint8_t* aInitData, uint32_t aInitDataSize, - std::vector<Key>& aOutKeys); + static void ParseCENCInitData(const uint8_t* aInitData, + uint32_t aInitDataSize, + std::vector<Key>& aOutKeyIds); + + static bool ParseKeyIdsInitData(const uint8_t* aInitData, + uint32_t aInitDataSize, + std::vector<KeyId>& aOutKeyIds, + std::string& aOutSessionType); static void MakeKeyRequest(const std::vector<KeyId>& aKeyIds, std::string& aOutRequest, GMPSessionType aSessionType); static bool ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize, std::vector<KeyIdPair>& aOutKeys, GMPSessionType aSessionType);
--- a/security/manager/ssl/CertBlocklist.cpp +++ b/security/manager/ssl/CertBlocklist.cpp @@ -25,22 +25,24 @@ #include "prtime.h" NS_IMPL_ISUPPORTS(CertBlocklist, nsICertBlocklist) using namespace mozilla; using namespace mozilla::pkix; #define PREF_BACKGROUND_UPDATE_TIMER "app.update.lastUpdateTime.blocklist-background-update-timer" +#define PREF_KINTO_ONECRL_CHECKED "services.kinto.onecrl.checked" #define PREF_MAX_STALENESS_IN_SECONDS "security.onecrl.maximum_staleness_in_seconds" #define PREF_ONECRL_VIA_AMO "security.onecrl.via.amo" static PRLogModuleInfo* gCertBlockPRLog; uint32_t CertBlocklist::sLastBlocklistUpdate = 0U; +uint32_t CertBlocklist::sLastKintoUpdate = 0U; uint32_t CertBlocklist::sMaxStaleness = 0U; bool CertBlocklist::sUseAMO = true; CertBlocklistItem::CertBlocklistItem(const uint8_t* DNData, size_t DNLength, const uint8_t* otherData, size_t otherLength, CertBlocklistItemMechanism itemMechanism) @@ -139,16 +141,19 @@ CertBlocklist::~CertBlocklist() PREF_BACKGROUND_UPDATE_TIMER, this); Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged, PREF_MAX_STALENESS_IN_SECONDS, this); Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged, PREF_ONECRL_VIA_AMO, this); + Preferences::UnregisterCallback(CertBlocklist::PreferenceChanged, + PREF_KINTO_ONECRL_CHECKED, + this); } nsresult CertBlocklist::Init() { MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, ("CertBlocklist::Init")); // Init must be on main thread for getting the profile directory @@ -173,16 +178,22 @@ CertBlocklist::Init() return rv; } rv = Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged, PREF_ONECRL_VIA_AMO, this); if (NS_FAILED(rv)) { return rv; } + rv = Preferences::RegisterCallbackAndCall(CertBlocklist::PreferenceChanged, + PREF_KINTO_ONECRL_CHECKED, + this); + if (NS_FAILED(rv)) { + return rv; + } // Get the profile directory rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mBackingFile)); if (NS_FAILED(rv) || !mBackingFile) { MOZ_LOG(gCertBlockPRLog, LogLevel::Debug, ("CertBlocklist::Init - couldn't get profile dir")); // Since we're returning NS_OK here, set mBackingFile to a safe value. @@ -612,26 +623,25 @@ CertBlocklist::IsCertRevoked(const uint8 return NS_OK; } NS_IMETHODIMP CertBlocklist::IsBlocklistFresh(bool* _retval) { MutexAutoLock lock(mMutex); *_retval = false; - if (!sUseAMO) { - // for the time being, if we're not using AMO data, assume the blocklist is - // not fresh (in particular, prevent OneCRL OCSP bypass). - return NS_OK; - } uint32_t now = uint32_t(PR_Now() / PR_USEC_PER_SEC); + uint32_t lastUpdate = sUseAMO ? sLastBlocklistUpdate : sLastKintoUpdate; + MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, + ("CertBlocklist::IsBlocklistFresh using AMO? %i lastUpdate is %i", + sUseAMO, lastUpdate)); - if (now > sLastBlocklistUpdate) { - int64_t interval = now - sLastBlocklistUpdate; + if (now > lastUpdate) { + int64_t interval = now - lastUpdate; MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, ("CertBlocklist::IsBlocklistFresh we're after the last BlocklistUpdate " "interval is %i, staleness %u", interval, sMaxStaleness)); *_retval = sMaxStaleness > interval; } MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, ("CertBlocklist::IsBlocklistFresh ? %s", *_retval ? "true" : "false")); return NS_OK; @@ -646,15 +656,18 @@ CertBlocklist::PreferenceChanged(const c CertBlocklist* blocklist = reinterpret_cast<CertBlocklist*>(aClosure); MutexAutoLock lock(blocklist->mMutex); MOZ_LOG(gCertBlockPRLog, LogLevel::Warning, ("CertBlocklist::PreferenceChanged %s changed", aPref)); if (strcmp(aPref, PREF_BACKGROUND_UPDATE_TIMER) == 0) { sLastBlocklistUpdate = Preferences::GetUint(PREF_BACKGROUND_UPDATE_TIMER, uint32_t(0)); + } else if (strcmp(aPref, PREF_KINTO_ONECRL_CHECKED) == 0) { + sLastKintoUpdate = Preferences::GetUint(PREF_KINTO_ONECRL_CHECKED, + uint32_t(0)); } else if (strcmp(aPref, PREF_MAX_STALENESS_IN_SECONDS) == 0) { sMaxStaleness = Preferences::GetUint(PREF_MAX_STALENESS_IN_SECONDS, uint32_t(0)); } else if (strcmp(aPref, PREF_ONECRL_VIA_AMO) == 0) { sUseAMO = Preferences::GetBool(PREF_ONECRL_VIA_AMO, true); } }
--- a/security/manager/ssl/CertBlocklist.h +++ b/security/manager/ssl/CertBlocklist.h @@ -75,14 +75,15 @@ private: // call EnsureBackingFileInitialized before operations that read or // modify CertBlocklist data nsresult EnsureBackingFileInitialized(mozilla::MutexAutoLock& lock); nsCOMPtr<nsIFile> mBackingFile; protected: static void PreferenceChanged(const char* aPref, void* aClosure); static uint32_t sLastBlocklistUpdate; + static uint32_t sLastKintoUpdate; static uint32_t sMaxStaleness; static bool sUseAMO; virtual ~CertBlocklist(); }; #endif // CertBlocklist_h
--- a/security/manager/ssl/tests/unit/test_ev_certs.js +++ b/security/manager/ssl/tests/unit/test_ev_certs.js @@ -183,31 +183,55 @@ function run_test() { gEVExpected ? ["int-ev-valid", "ev-valid"] : ["ev-valid"]); check_ee_for_ev("ev-valid", gEVExpected); Services.prefs.clearUserPref("security.onecrl.maximum_staleness_in_seconds"); ocspResponder.stop(run_next_test); }); add_test(function () { - // test that setting "security.onecrl.via.amo" to false will prevent - // OCSP skipping + // test that setting "security.onecrl.via.amo" results in the correct + // OCSP behavior when services.kinto.onecrl.checked is in the distant past + // and blacklist-background-update-timer is recent Services.prefs.setBoolPref("security.onecrl.via.amo", false); // enable OneCRL OCSP skipping - allow staleness of up to 30 hours Services.prefs.setIntPref("security.onecrl.maximum_staleness_in_seconds", 108000); // set the blocklist-background-update-timer value to the recent past + // (services.kinto.onecrl.checked defaults to 0) Services.prefs.setIntPref("app.update.lastUpdateTime.blocklist-background-update-timer", Math.floor(Date.now() / 1000) - 1); clearOCSPCache(); // the intermediate should have an associated OCSP request let ocspResponder = start_ocsp_responder( gEVExpected ? ["int-ev-valid", "ev-valid"] : ["ev-valid"]); check_ee_for_ev("ev-valid", gEVExpected); - Services.prefs.clearUserPref("security.onecrl.maximum_staleness_in_seconds"); + ocspResponder.stop(run_next_test); + }); + + add_test(function () { + // test that setting "security.onecrl.via.amo" results in the correct + // OCSP behavior when services.kinto.onecrl.checked is recent + Services.prefs.setBoolPref("security.onecrl.via.amo", false); + + // enable OneCRL OCSP skipping - allow staleness of up to 30 hours + Services.prefs.setIntPref("security.onecrl.maximum_staleness_in_seconds", 108000); + + // now set services.kinto.onecrl.checked to a recent value + Services.prefs.setIntPref("services.kinto.onecrl.checked", + Math.floor(Date.now() / 1000) - 1); + + clearOCSPCache(); + // the intermediate should not have an associated OCSP request + let ocspResponder = start_ocsp_responder(["ev-valid"]); + check_ee_for_ev("ev-valid", gEVExpected); + // The tests following this assume no OCSP bypass + Services.prefs.setIntPref("security.onecrl.maximum_staleness_in_seconds", 0); + Services.prefs.clearUserPref("security.onecrl.via.amo"); + Services.prefs.clearUserPref("services.kinto.onecrl.checked"); ocspResponder.stop(run_next_test); }); // Test the EV continues to work with flags after successful EV verification add_test(function () { clearOCSPCache(); let ocspResponder = start_ocsp_responder( gEVExpected ? ["int-ev-valid", "ev-valid"]
--- a/testing/marionette/actions.js +++ b/testing/marionette/actions.js @@ -69,17 +69,17 @@ ActionChain.prototype.dispatchActions = ctrlKey: false, altKey: false, metaKey: false }; try { this.actions(commandArray, touchId, 0, keyModifiers); } catch (e) { - this.onError(e); + callbacks.onError(e); this.resetValues(); } }; /** * This function emit mouse event. * * @param {Document} doc @@ -151,17 +151,17 @@ ActionChain.prototype.resetValues = func * Function to emit touch events for each finger. e.g. * finger=[['press', id], ['wait', 5], ['release']] touchId represents * the finger id, i keeps track of the current action of the chain * keyModifiers is an object keeping track keyDown/keyUp pairs through * an action chain. */ ActionChain.prototype.actions = function(chain, touchId, i, keyModifiers) { if (i == chain.length) { - this.onSuccess({value: touchId || null}); + this.onSuccess(touchId || null); this.resetValues(); return; } let pack = chain[i]; let command = pack[0]; let el; let c;
--- a/testing/marionette/client/marionette/tests/unit/test_multi_finger.py +++ b/testing/marionette/client/marionette/tests/unit/test_multi_finger.py @@ -1,14 +1,14 @@ # 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/. from marionette import MarionetteTestCase -from marionette.marionette import MultiActions, Actions +from marionette_driver.marionette import MultiActions, Actions class testMultiFinger(MarionetteTestCase): def test_move_element(self): testAction = self.marionette.absolute_url("testAction.html") self.marionette.navigate(testAction) start = self.marionette.find_element("id", "button1") drop = self.marionette.find_element("id", "button2") ele = self.marionette.find_element("id", "button3")
new file mode 100644 --- /dev/null +++ b/testing/marionette/cookies.js @@ -0,0 +1,131 @@ +/* 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/. */ + +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +Cu.import("resource://gre/modules/Log.jsm"); +Cu.import("chrome://marionette/content/error.js"); + +const logger = Log.repository.getLogger("Marionette"); + +this.EXPORTED_SYMBOLS = ["Cookies"]; + +const IPV4_PORT_EXPR = /:\d+$/; + +/** + * Interface for manipulating cookies from content space. + */ +this.Cookies = class { + + /** + * @param {function(): Document} documentFn + * Closure that returns the current content document. + * @param {Proxy(SyncChromeSender)} chromeProxy + * A synchronous proxy interface to chrome space. + */ + constructor(documentFn, chromeProxy) { + this.documentFn_ = documentFn; + this.chrome = chromeProxy; + } + + get document() { + return this.documentFn_(); + } + + [Symbol.iterator]() { + let path = this.document.location.pathname || "/"; + let cs = this.chrome.getVisibleCookies(path, this.document.location.hostname)[0]; + return cs[Symbol.iterator](); + } + + /** + * Add a new cookie to a content document. + * + * @param {string} name + * Cookie key. + * @param {string} value + * Cookie value. + * @param {Object.<string, ?>} opts + * An object with the optional fields {@code domain}, {@code path}, + * {@code secure}, {@code httpOnly}, and {@code expiry}. + * + * @return {Object.<string, ?>} + * A serialisation of the cookie that was added. + * + * @throws UnableToSetCookieError + * If the document's content type isn't HTML, the current document's + * domain is a mismatch to the cookie's provided domain, or there + * otherwise was issues with the input data. + */ + add(name, value, opts={}) { + if (typeof this.document == "undefined" || !this.document.contentType.match(/html/i)) { + throw new UnableToSetCookieError( + "You may only set cookies on HTML documents: " + this.document.contentType); + } + + if (!opts.expiry) { + // date twenty years into future, in seconds + let date = new Date(); + let now = new Date(Date.now()); + date.setYear(now.getFullYear() + 20); + opts.expiry = date.getTime() / 1000; + } + + if (!opts.domain) { + opts.domain = this.document.location.host; + } else if (this.document.location.host.indexOf(opts.domain) < 0) { + throw new InvalidCookieDomainError( + "You may only set cookies for the current domain"); + } + + // remove port from domain, if present. + // unfortunately this catches IPv6 addresses by mistake + // TODO: Bug 814416 + opts.domain = opts.domain.replace(IPV4_PORT_EXPR, ""); + + let cookie = { + domain: opts.domain, + path: opts.path, + name: name, + value: value, + secure: opts.secure, + httpOnly: opts.httpOnly, + session: false, + expiry: opts.expiry, + }; + if (!this.chrome.addCookie(cookie)) { + throw new UnableToSetCookieError(); + } + + return cookie; + } + + /** + * Delete cookie by reference or by name. + * + * @param {(string|Object.<string, ?>)} cookie + * Name of cookie or cookie object. + * + * @throws {UnknownError} + * If unable to delete the cookie. + */ + delete(cookie) { + let name; + if (cookie.hasOwnProperty("name")) { + name = cookie.name; + } else { + name = cookie; + } + + for (let candidate of this) { + if (candidate.name == name) { + if (!this.chrome.deleteCookie(candidate)) { + throw new UnknownError("Unable to delete cookie by name: " + name); + } + } + } + } +};
--- a/testing/marionette/driver.js +++ b/testing/marionette/driver.js @@ -1852,22 +1852,22 @@ GeckoDriver.prototype.actionChain = func } let cbs = {}; cbs.onSuccess = val => resp.body.value = val; cbs.onError = err => { throw err; }; let win = this.getCurrentWindow(); let elm = this.curBrowser.elementManager; - this.actions.dispatchActions(chain, nextId, { frame: win }, elm, cbs); + this.actions.dispatchActions(chain, nextId, {frame: win}, elm, cbs); break; case Context.CONTENT: this.addFrameCloseListener("action chain"); - resp.body.value = yield this.listener.actionChain({chain: chain, nextId: nextId}); + resp.body.value = yield this.listener.actionChain(chain, nextId); break; } }; /** * A multi-action chain. * * @param {Object} value @@ -1877,18 +1877,17 @@ GeckoDriver.prototype.actionChain = func */ GeckoDriver.prototype.multiAction = function(cmd, resp) { switch (this.context) { case Context.CHROME: throw new WebDriverError("Command 'multiAction' is not available in chrome context"); case Context.CONTENT: this.addFrameCloseListener("multi action chain"); - yield this.listener.multiAction( - {value: cmd.parameters.value, maxlen: cmd.parameters.max_len}); + yield this.listener.multiAction( cmd.parameters.value, cmd.parameters.max_len); break; } }; /** * Find an element using the indicated search strategy. * * @param {string} using @@ -2286,37 +2285,74 @@ GeckoDriver.prototype.clearElement = fun GeckoDriver.prototype.switchToShadowRoot = function(cmd, resp) { let id; if (cmd.parameters) { id = cmd.parameters.id; } yield this.listener.switchToShadowRoot(id); }; /** Add a cookie to the document. */ GeckoDriver.prototype.addCookie = function(cmd, resp) { - yield this.listener.addCookie({cookie: cmd.parameters.cookie}); + let cb = msg => { + this.mm.removeMessageListener("Marionette:addCookie", cb); + let cookie = msg.json; + Services.cookies.add( + cookie.domain, + cookie.path, + cookie.name, + cookie.value, + cookie.secure, + cookie.httpOnly, + cookie.session, + cookie.expiry); + return true; + }; + this.mm.addMessageListener("Marionette:addCookie", cb); + yield this.listener.addCookie(cmd.parameters.cookie); }; /** * Get all the cookies for the current domain. * * This is the equivalent of calling {@code document.cookie} and parsing * the result. */ GeckoDriver.prototype.getCookies = function(cmd, resp) { resp.body = yield this.listener.getCookies(); }; /** Delete all cookies that are visible to a document. */ GeckoDriver.prototype.deleteAllCookies = function(cmd, resp) { + let cb = msg => { + let cookie = msg.json; + cookieManager.remove( + cookie.host, + cookie.name, + cookie.path, + false); + return true; + }; + this.mm.addMessageListener("Marionette:deleteCookie", cb); yield this.listener.deleteAllCookies(); + this.mm.removeMessageListener("Marionette:deleteCookie", cb); }; /** Delete a cookie by name. */ GeckoDriver.prototype.deleteCookie = function(cmd, resp) { - yield this.listener.deleteCookie({name: cmd.parameters.name}); + let cb = msg => { + this.mm.removeMessageListener("Marionette:deleteCookie", cb); + let cookie = msg.json; + cookieManager.remove( + cookie.host, + cookie.name, + cookie.path, + false); + return true; + }; + this.mm.addMessageListener("Marionette:deleteCookie", cb); + yield this.listener.deleteCookie(cmd.parameters.name); }; /** * Close the current window, ending the session if it's the last * window currently open. * * On B2G this method is a noop and will return immediately. */ @@ -2820,17 +2856,17 @@ GeckoDriver.prototype.receiveMessage = f if (message.json.storePrevious) { this.previousFrameElement = this.currentFrameElement; } this.currentFrameElement = message.json.frameValue; } break; case "Marionette:getVisibleCookies": - let [currentPath, host] = message.json.value; + let [currentPath, host] = message.json; let isForCurrentPath = path => currentPath.indexOf(path) != -1; let results = []; let en = cookieManager.getCookiesFromHost(host); while (en.hasMoreElements()) { let cookie = en.getNext().QueryInterface(Ci.nsICookie2); // take the hostname and progressively shorten let hostname = host; @@ -2848,38 +2884,16 @@ GeckoDriver.prototype.receiveMessage = f }); break; } hostname = hostname.replace(/^.*?\./, ""); } while (hostname.indexOf(".") != -1); } return results; - case "Marionette:addCookie": - let cookieToAdd = message.json.value; - Services.cookies.add( - cookieToAdd.domain, - cookieToAdd.path, - cookieToAdd.name, - cookieToAdd.value, - cookieToAdd.secure, - cookieToAdd.httpOnly, - false, - cookieToAdd.expiry); - return true; - - case "Marionette:deleteCookie": - let cookieToDelete = message.json.value; - cookieManager.remove( - cookieToDelete.host, - cookieToDelete.name, - cookieToDelete.path, - false); - return true; - case "Marionette:getFiles": // Generates file objects to send back to the content script // for handling file uploads. let val = message.json.value; let command_id = message.json.command_id; Cu.importGlobalProperties(["File"]); try { let file = new File(val);
--- a/testing/marionette/error.js +++ b/testing/marionette/error.js @@ -49,21 +49,16 @@ const XPCOM_EXCEPTIONS = []; for (let prop in Cr) { XPCOM_EXCEPTIONS.push(Cr[prop]); } } this.error = {}; /** - * Determines if the given status is successful. - */ -error.isSuccess = status => status === "success"; - -/** * Checks if obj is an instance of the Error prototype in a safe manner. * Prefer using this over using instanceof since the Error prototype * isn't unique across browsers, and XPCOM exceptions are special * snowflakes. * * @param {*} val * Any value that should be undergo the test for errorness. * @return {boolean}
--- a/testing/marionette/frame-manager.js +++ b/testing/marionette/frame-manager.js @@ -185,19 +185,17 @@ FrameManager.prototype = { mm.addWeakMessageListener("Marionette:error", this.server); mm.addWeakMessageListener("Marionette:emitTouchEvent", this.server); mm.addWeakMessageListener("Marionette:log", this.server); mm.addWeakMessageListener("Marionette:runEmulatorCmd", this.server); mm.addWeakMessageListener("Marionette:runEmulatorShell", this.server); mm.addWeakMessageListener("Marionette:shareData", this.server); mm.addWeakMessageListener("Marionette:switchToModalOrigin", this.server); mm.addWeakMessageListener("Marionette:switchedToFrame", this.server); - mm.addWeakMessageListener("Marionette:addCookie", this.server); mm.addWeakMessageListener("Marionette:getVisibleCookies", this.server); - mm.addWeakMessageListener("Marionette:deleteCookie", this.server); mm.addWeakMessageListener("Marionette:register", this.server); mm.addWeakMessageListener("Marionette:listenersAttached", this.server); mm.addWeakMessageListener("Marionette:getFiles", this.server); mm.addWeakMessageListener("MarionetteFrame:handleModal", this); mm.addWeakMessageListener("MarionetteFrame:getCurrentFrameId", this); mm.addWeakMessageListener("MarionetteFrame:getInterruptedState", this); }, @@ -217,18 +215,16 @@ FrameManager.prototype = { mm.removeWeakMessageListener("Marionette:ok", this.server); mm.removeWeakMessageListener("Marionette:done", this.server); mm.removeWeakMessageListener("Marionette:error", this.server); mm.removeWeakMessageListener("Marionette:log", this.server); mm.removeWeakMessageListener("Marionette:shareData", this.server); mm.removeWeakMessageListener("Marionette:runEmulatorCmd", this.server); mm.removeWeakMessageListener("Marionette:runEmulatorShell", this.server); mm.removeWeakMessageListener("Marionette:switchedToFrame", this.server); - mm.removeWeakMessageListener("Marionette:addCookie", this.server); mm.removeWeakMessageListener("Marionette:getVisibleCookies", this.server); - mm.removeWeakMessageListener("Marionette:deleteCookie", this.server); mm.removeWeakMessageListener("Marionette:listenersAttached", this.server); mm.removeWeakMessageListener("Marionette:register", this.server); mm.removeWeakMessageListener("Marionette:getFiles", this.server); mm.removeWeakMessageListener("MarionetteFrame:handleModal", this); mm.removeWeakMessageListener("MarionetteFrame:getCurrentFrameId", this); } };
--- a/testing/marionette/jar.mn +++ b/testing/marionette/jar.mn @@ -17,16 +17,17 @@ marionette.jar: content/ChromeUtils.js (ChromeUtils.js) content/error.js (error.js) content/command.js (command.js) content/dispatcher.js (dispatcher.js) content/emulator.js (emulator.js) content/modal.js (modal.js) content/proxy.js (proxy.js) content/capture.js (capture.js) + content/cookies.js (cookies.js) #ifdef ENABLE_TESTS content/test.xul (client/marionette/chrome/test.xul) content/test2.xul (client/marionette/chrome/test2.xul) content/test_nested_iframe.xul (client/marionette/chrome/test_nested_iframe.xul) content/test_anonymous_content.xul (client/marionette/chrome/test_anonymous_content.xul) #endif % content specialpowers %content/
--- a/testing/marionette/listener.js +++ b/testing/marionette/listener.js @@ -9,18 +9,21 @@ var uuidGen = Cc["@mozilla.org/uuid-gene var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] .getService(Ci.mozIJSSubScriptLoader); loader.loadSubScript("chrome://marionette/content/simpletest.js"); loader.loadSubScript("chrome://marionette/content/common.js"); loader.loadSubScript("chrome://marionette/content/actions.js"); Cu.import("chrome://marionette/content/capture.js"); +Cu.import("chrome://marionette/content/cookies.js"); Cu.import("chrome://marionette/content/elements.js"); Cu.import("chrome://marionette/content/error.js"); +Cu.import("chrome://marionette/content/proxy.js"); + Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); var utils = {}; utils.window = content; // Load Event/ChromeUtils for use with JS scripts: loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils); @@ -72,16 +75,19 @@ var readyStateTimer = Cc["@mozilla.org/t // timer for navigation commands. var navTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); var onDOMContentLoaded; // Send move events about this often var EVENT_INTERVAL = 30; // milliseconds // last touch for each fingerId var multiLast = {}; +var chrome = proxy.toChrome(sendSyncMessage.bind(this)); +var cookies = new Cookies(() => curContainer.frame.document, chrome); + Cu.import("resource://gre/modules/Log.jsm"); var logger = Log.repository.getLogger("Marionette"); logger.info("loaded listener.js"); var modalHandler = function() { // This gets called on the system app only since it receives the mozbrowserprompt event sendSyncMessage("Marionette:switchedToFrame", { frameValue: null, storePrevious: true }); let isLocal = sendSyncMessage("MarionetteFrame:handleModal", {})[0].value; if (isLocal) { @@ -212,29 +218,34 @@ var findElementsContentFn = dispatch(fin var isElementSelectedFn = dispatch(isElementSelected); var clearElementFn = dispatch(clearElement); var isElementDisplayedFn = dispatch(isElementDisplayed); var getElementValueOfCssPropertyFn = dispatch(getElementValueOfCssProperty); var switchToShadowRootFn = dispatch(switchToShadowRoot); var getCookiesFn = dispatch(getCookies); var singleTapFn = dispatch(singleTap); var takeScreenshotFn = dispatch(takeScreenshot); +var actionChainFn = dispatch(actionChain); +var multiActionFn = dispatch(multiAction); +var addCookieFn = dispatch(addCookie); +var deleteCookieFn = dispatch(deleteCookie); +var deleteAllCookiesFn = dispatch(deleteAllCookies); /** * Start all message listeners */ function startListeners() { addMessageListenerId("Marionette:receiveFiles", receiveFiles); addMessageListenerId("Marionette:newSession", newSession); addMessageListenerId("Marionette:executeScript", executeScript); addMessageListenerId("Marionette:executeAsyncScript", executeAsyncScript); addMessageListenerId("Marionette:executeJSScript", executeJSScript); addMessageListenerId("Marionette:singleTap", singleTapFn); - addMessageListenerId("Marionette:actionChain", actionChain); - addMessageListenerId("Marionette:multiAction", multiAction); + addMessageListenerId("Marionette:actionChain", actionChainFn); + addMessageListenerId("Marionette:multiAction", multiActionFn); addMessageListenerId("Marionette:get", get); addMessageListenerId("Marionette:pollForReadyState", pollForReadyState); addMessageListenerId("Marionette:cancelRequest", cancelRequest); addMessageListenerId("Marionette:getCurrentUrl", getCurrentUrlFn); addMessageListenerId("Marionette:getTitle", getTitleFn); addMessageListenerId("Marionette:getPageSource", getPageSourceFn); addMessageListenerId("Marionette:goBack", goBackFn); addMessageListenerId("Marionette:goForward", goForward); @@ -258,20 +269,20 @@ function startListeners() { addMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn); addMessageListenerId("Marionette:deleteSession", deleteSession); addMessageListenerId("Marionette:sleepSession", sleepSession); addMessageListenerId("Marionette:emulatorCmdResult", emulatorCmdResult); addMessageListenerId("Marionette:importScript", importScript); addMessageListenerId("Marionette:getAppCacheStatus", getAppCacheStatus); addMessageListenerId("Marionette:setTestName", setTestName); addMessageListenerId("Marionette:takeScreenshot", takeScreenshotFn); - addMessageListenerId("Marionette:addCookie", addCookie); + addMessageListenerId("Marionette:addCookie", addCookieFn); addMessageListenerId("Marionette:getCookies", getCookiesFn); - addMessageListenerId("Marionette:deleteAllCookies", deleteAllCookies); - addMessageListenerId("Marionette:deleteCookie", deleteCookie); + addMessageListenerId("Marionette:deleteAllCookies", deleteAllCookiesFn); + addMessageListenerId("Marionette:deleteCookie", deleteCookieFn); } /** * Used during newSession and restart, called to set up the modal dialog listener in b2g */ function waitForReady() { if (content.document.readyState == 'complete') { readyStateTimer.cancel(); @@ -328,18 +339,18 @@ function restart(msg) { */ function deleteSession(msg) { removeMessageListenerId("Marionette:receiveFiles", receiveFiles); removeMessageListenerId("Marionette:newSession", newSession); removeMessageListenerId("Marionette:executeScript", executeScript); removeMessageListenerId("Marionette:executeAsyncScript", executeAsyncScript); removeMessageListenerId("Marionette:executeJSScript", executeJSScript); removeMessageListenerId("Marionette:singleTap", singleTapFn); - removeMessageListenerId("Marionette:actionChain", actionChain); - removeMessageListenerId("Marionette:multiAction", multiAction); + removeMessageListenerId("Marionette:actionChain", actionChainFn); + removeMessageListenerId("Marionette:multiAction", multiActionFn); removeMessageListenerId("Marionette:get", get); removeMessageListenerId("Marionette:pollForReadyState", pollForReadyState); removeMessageListenerId("Marionette:cancelRequest", cancelRequest); removeMessageListenerId("Marionette:getTitle", getTitleFn); removeMessageListenerId("Marionette:getPageSource", getPageSourceFn); removeMessageListenerId("Marionette:getCurrentUrl", getCurrentUrlFn); removeMessageListenerId("Marionette:goBack", goBackFn); removeMessageListenerId("Marionette:goForward", goForward); @@ -363,20 +374,20 @@ function deleteSession(msg) { removeMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn); removeMessageListenerId("Marionette:deleteSession", deleteSession); removeMessageListenerId("Marionette:sleepSession", sleepSession); removeMessageListenerId("Marionette:emulatorCmdResult", emulatorCmdResult); removeMessageListenerId("Marionette:importScript", importScript); removeMessageListenerId("Marionette:getAppCacheStatus", getAppCacheStatus); removeMessageListenerId("Marionette:setTestName", setTestName); removeMessageListenerId("Marionette:takeScreenshot", takeScreenshotFn); - removeMessageListenerId("Marionette:addCookie", addCookie); + removeMessageListenerId("Marionette:addCookie", addCookieFn); removeMessageListenerId("Marionette:getCookies", getCookiesFn); - removeMessageListenerId("Marionette:deleteAllCookies", deleteAllCookies); - removeMessageListenerId("Marionette:deleteCookie", deleteCookie); + removeMessageListenerId("Marionette:deleteAllCookies", deleteAllCookiesFn); + removeMessageListenerId("Marionette:deleteCookie", deleteCookieFn); if (isB2G) { content.removeEventListener("mozbrowsershowmodalprompt", modalHandler, false); } elementManager.reset(); // reset container frame to the top-most frame curContainer = { frame: content, shadowRoot: null }; curContainer.frame.focus(); actions.touchIds = {}; @@ -1055,42 +1066,32 @@ function createATouch(el, corx, cory, to let win = doc.defaultView; let [clientX, clientY, pageX, pageY, screenX, screenY] = actions.getCoordinateInfo(el, corx, cory); let atouch = doc.createTouch(win, el, touchId, pageX, pageY, screenX, screenY, clientX, clientY); return atouch; } /** - * Function to start action chain on one finger + * Start action chain on one finger. */ -function actionChain(msg) { - let command_id = msg.json.command_id; - let args = msg.json.chain; - let touchId = msg.json.nextId; - - let callbacks = {}; - callbacks.onSuccess = value => sendResponse(value, command_id); - callbacks.onError = err => sendError(err, command_id); - +function actionChain(chain, touchId) { let touchProvider = {}; touchProvider.createATouch = createATouch; touchProvider.emitTouchEvent = emitTouchEvent; - try { + return new Promise((resolve, reject) => { actions.dispatchActions( - args, + chain, touchId, curContainer, elementManager, - callbacks, + {onSuccess: resolve, onError: reject}, touchProvider); - } catch (e) { - sendError(e, command_id); - } + }); } /** * Function to emit touch events which allow multi touch on the screen * @param type represents the type of event, touch represents the current touch,touches are all pending touches */ function emitMultiEvents(type, touch, touches) { let target = touch.target; @@ -1119,26 +1120,23 @@ function emitMultiEvents(type, touch, to changedTouches); target.dispatchEvent(event); } /** * Function to dispatch one set of actions * @param touches represents all pending touches, batchIndex represents the batch we are dispatching right now */ -function setDispatch(batches, touches, command_id, batchIndex) { - if (typeof batchIndex === "undefined") { - batchIndex = 0; - } +function setDispatch(batches, touches, batchIndex=0) { // check if all the sets have been fired if (batchIndex >= batches.length) { multiLast = {}; - sendOk(command_id); return; } + // a set of actions need to be done let batch = batches[batchIndex]; // each action for some finger let pack; // the touch id for the finger (pack) let touchId; // command for the finger let command; @@ -1147,116 +1145,120 @@ function setDispatch(batches, touches, c let corx; let cory; let touch; let lastTouch; let touchIndex; let waitTime = 0; let maxTime = 0; let c; + + // loop through the batch batchIndex++; - // loop through the batch for (let i = 0; i < batch.length; i++) { pack = batch[i]; touchId = pack[0]; command = pack[1]; + switch (command) { - case 'press': + case "press": el = elementManager.getKnownElement(pack[2], curContainer); c = coordinates(el, pack[3], pack[4]); touch = createATouch(el, c.x, c.y, touchId); multiLast[touchId] = touch; touches.push(touch); - emitMultiEvents('touchstart', touch, touches); + emitMultiEvents("touchstart", touch, touches); break; - case 'release': + + case "release": touch = multiLast[touchId]; // the index of the previous touch for the finger may change in the touches array touchIndex = touches.indexOf(touch); touches.splice(touchIndex, 1); - emitMultiEvents('touchend', touch, touches); + emitMultiEvents("touchend", touch, touches); break; - case 'move': + + case "move": el = elementManager.getKnownElement(pack[2], curContainer); c = coordinates(el); touch = createATouch(multiLast[touchId].target, c.x, c.y, touchId); touchIndex = touches.indexOf(lastTouch); touches[touchIndex] = touch; multiLast[touchId] = touch; - emitMultiEvents('touchmove', touch, touches); + emitMultiEvents("touchmove", touch, touches); break; - case 'moveByOffset': + + case "moveByOffset": el = multiLast[touchId].target; lastTouch = multiLast[touchId]; touchIndex = touches.indexOf(lastTouch); let doc = el.ownerDocument; let win = doc.defaultView; // since x and y are relative to the last touch, therefore, it's relative to the position of the last touch let clientX = lastTouch.clientX + pack[2], clientY = lastTouch.clientY + pack[3]; let pageX = clientX + win.pageXOffset, pageY = clientY + win.pageYOffset; let screenX = clientX + win.mozInnerScreenX, screenY = clientY + win.mozInnerScreenY; touch = doc.createTouch(win, el, touchId, pageX, pageY, screenX, screenY, clientX, clientY); touches[touchIndex] = touch; multiLast[touchId] = touch; - emitMultiEvents('touchmove', touch, touches); + emitMultiEvents("touchmove", touch, touches); break; - case 'wait': - if (pack[2] != undefined ) { - waitTime = pack[2]*1000; + + case "wait": + if (typeof pack[2] != "undefined") { + waitTime = pack[2] * 1000; if (waitTime > maxTime) { maxTime = waitTime; } } break; - }//end of switch block - }//end of for loop + } + } + if (maxTime != 0) { - checkTimer.initWithCallback(function(){setDispatch(batches, touches, command_id, batchIndex);}, maxTime, Ci.nsITimer.TYPE_ONE_SHOT); - } - else { - setDispatch(batches, touches, command_id, batchIndex); + checkTimer.initWithCallback(function() { + setDispatch(batches, touches, batchIndex); + }, maxTime, Ci.nsITimer.TYPE_ONE_SHOT); + } else { + setDispatch(batches, touches, batchIndex); } } /** - * Function to start multi-action + * Start multi-action. + * + * @param {Number} maxLen + * Longest action chain for one finger. */ -function multiAction(msg) { - let command_id = msg.json.command_id; - let args = msg.json.value; - // maxlen is the longest action chain for one finger - let maxlen = msg.json.maxlen; - try { - // unwrap the original nested array - let commandArray = elementManager.convertWrappedArguments(args, curContainer); - let concurrentEvent = []; - let temp; - for (let i = 0; i < maxlen; i++) { - let row = []; - for (let j = 0; j < commandArray.length; j++) { - if (commandArray[j][i] != undefined) { - // add finger id to the front of each action, i.e. [finger_id, action, element] - temp = commandArray[j][i]; - temp.unshift(j); - row.push(temp); - } +function multiAction(args, maxLen) { + // unwrap the original nested array + let commandArray = elementManager.convertWrappedArguments(args, curContainer); + let concurrentEvent = []; + let temp; + for (let i = 0; i < maxLen; i++) { + let row = []; + for (let j = 0; j < commandArray.length; j++) { + if (typeof commandArray[j][i] != "undefined") { + // add finger id to the front of each action, i.e. [finger_id, action, element] + temp = commandArray[j][i]; + temp.unshift(j); + row.push(temp); } - concurrentEvent.push(row); } - // now concurrent event is made of sets where each set contain a list of actions that need to be fired. - // note: each action belongs to a different finger - // pendingTouches keeps track of current touches that's on the screen - let pendingTouches = []; - setDispatch(concurrentEvent, pendingTouches, command_id); - } catch (e) { - sendError(e, command_id); + concurrentEvent.push(row); } + + // now concurrent event is made of sets where each set contain a list of actions that need to be fired. + // note: each action belongs to a different finger + // pendingTouches keeps track of current touches that's on the screen + let pendingTouches = []; + setDispatch(concurrentEvent, pendingTouches); } /* * This implements the latter part of a get request (for the case we need to resume one * when a remoteness update happens in the middle of a navigate request). This is most of * of the work of a navigate request, but doesn't assume DOMContentLoaded is yet to fire. */ function pollForReadyState(msg, start, callback) { @@ -1662,17 +1664,25 @@ function clearElement(id) { * @param {WebElement} id * Reference to web element. */ function switchToShadowRoot(id) { if (!id) { // If no host element is passed, attempt to find a parent shadow root or, if // none found, unset the current shadow root if (curContainer.shadowRoot) { - let parent = curContainer.shadowRoot.host; + let parent; + try { + parent = curContainer.shadowRoot.host; + } catch (e) { + // There is a chance that host element is dead and we are trying to + // access a dead object. + curContainer.shadowRoot = null; + return; + } while (parent && !(parent instanceof curContainer.frame.ShadowRoot)) { parent = parent.parentNode; } curContainer.shadowRoot = parent; } return; } @@ -1832,65 +1842,26 @@ function switchToFrame(msg) { curContainer.frame = curContainer.frame.contentWindow; if (msg.json.focus) curContainer.frame.focus(); checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); } sendResponse({value: rv}, command_id); } - /** - * Add a cookie to the document - */ -function addCookie(msg) { - let cookie = msg.json.cookie; - if (!cookie.expiry) { - var date = new Date(); - var thePresent = new Date(Date.now()); - date.setYear(thePresent.getFullYear() + 20); - cookie.expiry = date.getTime() / 1000; // Stored in seconds. - } - if (!cookie.domain) { - var location = curContainer.frame.document.location; - cookie.domain = location.hostname; - } else { - var currLocation = curContainer.frame.location; - var currDomain = currLocation.host; - if (currDomain.indexOf(cookie.domain) == -1) { - sendError(new InvalidCookieDomainError("You may only set cookies for the current domain"), msg.json.command_id); - } - } - - // The cookie's domain may include a port. Which is bad. Remove it - // We'll catch ip6 addresses by mistake. Since no-one uses those - // this will be okay for now. See Bug 814416 - if (cookie.domain.match(/:\d+$/)) { - cookie.domain = cookie.domain.replace(/:\d+$/, ''); - } - - var document = curContainer.frame.document; - if (!document || !document.contentType.match(/html/i)) { - sendError(new UnableToSetCookieError("You may only set cookies on html documents"), msg.json.command_id); - } - - let added = sendSyncMessage("Marionette:addCookie", {value: cookie}); - if (added[0] !== true) { - sendError(new UnableToSetCookieError(), msg.json.command_id); - return; - } - sendOk(msg.json.command_id); +function addCookie(cookie) { + cookies.add(cookie.name, cookie.value, cookie); } /** * Get all cookies for the current domain. */ function getCookies() { let rv = []; - let cookies = getVisibleCookies(curContainer.frame.location); for (let cookie of cookies) { let expires = cookie.expires; // session cookie, don't return an expiry if (expires == 0) { expires = null; // date before epoch time, cap to epoch } else if (expires == 1) { @@ -1906,57 +1877,29 @@ function getCookies() { 'expiry': expires }); } return rv; } /** - * Delete a cookie by name + * Delete a cookie by name. */ -function deleteCookie(msg) { - let toDelete = msg.json.name; - let cookies = getVisibleCookies(curContainer.frame.location); - for (let cookie of cookies) { - if (cookie.name == toDelete) { - let deleted = sendSyncMessage("Marionette:deleteCookie", {value: cookie}); - if (deleted[0] !== true) { - sendError(new UnknownError("Could not delete cookie: " + msg.json.name), msg.json.command_id); - return; - } - } - } - - sendOk(msg.json.command_id); +function deleteCookie(name) { + cookies.delete(name); } /** - * Delete all the visibile cookies on a page + * Delete all the visibile cookies on a page. */ -function deleteAllCookies(msg) { - let cookies = getVisibleCookies(curContainer.frame.location); +function deleteAllCookies() { for (let cookie of cookies) { - let deleted = sendSyncMessage("Marionette:deleteCookie", {value: cookie}); - if (!deleted[0]) { - sendError(new UnknownError("Could not delete cookie: " + JSON.stringify(cookie)), msg.json.command_id); - return; - } + cookies.delete(cookie); } - sendOk(msg.json.command_id); -} - -/** - * Get all the visible cookies from a location - */ -function getVisibleCookies(location) { - let currentPath = location.pathname || '/'; - let result = sendSyncMessage("Marionette:getVisibleCookies", - {value: [currentPath, location.hostname]}); - return result[0]; } function getAppCacheStatus(msg) { sendResponse({ value: curContainer.frame.applicationCache.status }, msg.json.command_id); } // emulator callbacks
--- a/testing/marionette/proxy.js +++ b/testing/marionette/proxy.js @@ -1,164 +1,202 @@ /* 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/. */ "use strict"; -var {classes: Cc, interfaces: Ci, utils: Cu} = Components; +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/Log.jsm"); Cu.import("chrome://marionette/content/modal.js"); this.EXPORTED_SYMBOLS = ["proxy"]; const MARIONETTE_OK = "Marionette:ok"; const MARIONETTE_DONE = "Marionette:done"; const MARIONETTE_ERROR = "Marionette:error"; const logger = Log.repository.getLogger("Marionette"); const uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); +// Proxy handler that traps requests to get a property. Will prioritise +// properties that exist on the object's own prototype. +var ownPriorityGetterTrap = { + get: (obj, prop) => { + if (obj.hasOwnProperty(prop)) { + return obj[prop]; + } + return (...args) => obj.send(prop, args); + } +}; + this.proxy = {}; /** * Creates a transparent interface between the chrome- and content - * processes. + * contexts. * * Calls to this object will be proxied via the message manager to the active * browsing context (content) and responses will be provided back as * promises. * * The argument sequence is serialised and passed as an array, unless it * consists of a single object type that isn't null, in which case it's * passed literally. The latter specialisation is temporary to achieve * backwards compatibility with listener.js. * * @param {function(): (nsIMessageSender|nsIMessageBroadcaster)} mmFn * Function returning the current message manager. * @param {function(string, Object, number)} sendAsyncFn * Callback for sending async messages to the current listener. */ proxy.toListener = function(mmFn, sendAsyncFn) { - let sender = new ContentSender(mmFn, sendAsyncFn); - let handler = { - get: (obj, prop) => { - if (obj.hasOwnProperty(prop)) { - return obj[prop]; - } - return (...args) => obj.send(prop, args); - } - }; - return new Proxy(sender, handler); + let sender = new AsyncContentSender(mmFn, sendAsyncFn); + return new Proxy(sender, ownPriorityGetterTrap); }; /** - * The ContentSender allows one to make synchronous calls to the + * The AsyncContentSender allows one to make synchronous calls to the * message listener of the content frame of the current browsing context. * * Presumptions about the responses from content space are made so we * can provide a nicer API on top of the message listener primitives that * make calls from chrome- to content space seem synchronous by leveraging * promises. * * The promise is guaranteed not to resolve until the execution of the * command in content space is complete. - * - * @param {function(): (nsIMessageSender|nsIMessageBroadcaster)} mmFn - * Function returning the current message manager. - * @param {function(string, Object, number)} sendAsyncFn - * Callback for sending async messages to the current listener. */ -var ContentSender = function(mmFn, sendAsyncFn) { - this.curId = null; - this.sendAsync = sendAsyncFn; - this.mmFn_ = mmFn; - this._listeners = []; -}; - -Object.defineProperty(ContentSender.prototype, "mm", { - get: function() { return this.mmFn_(); } -}); - -ContentSender.prototype.removeListeners = function () { - this._listeners.map(l => this.mm.removeMessageListener(l[0], l[1])); - this._listeners = []; -} +this.AsyncContentSender = class { + constructor(mmFn, sendAsyncFn) { + this.curId = null; + this.sendAsync = sendAsyncFn; + this.mmFn_ = mmFn; + this._listeners = []; + } -/** - * Call registered function in the frame script environment of the - * current browsing context's content frame. - * - * @param {string} name - * Function to call in the listener, e.g. for "Marionette:foo8", - * use "foo". - * @param {Array}Â args - * Argument list to pass the function. If args has a single entry - * that is an object, we assume it's an old style dispatch, and - * the object will passed literally. - * - * @return {Promise} - * A promise that resolves to the result of the command. - */ -ContentSender.prototype.send = function(name, args) { - if (this._listeners[0]) { - // A prior (probably timed-out) request has left listeners behind. - // Remove them before proceeding. - logger.warn("A previous failed command left content listeners behind!"); - this.removeListeners(); + get mm() { + return this.mmFn_(); + } + + removeListeners() { + this._listeners.map(l => this.mm.removeMessageListener(l[0], l[1])); + this._listeners = []; } - this.curId = uuidgen.generateUUID().toString(); + /** + * Call registered function in the frame script environment of the + * current browsing context's content frame. + * + * @param {string} name + * Function to call in the listener, e.g. for "Marionette:foo8", + * use "foo". + * @param {Array}Â args + * Argument list to pass the function. If args has a single entry + * that is an object, we assume it's an old style dispatch, and + * the object will passed literally. + * + * @return {Promise} + * A promise that resolves to the result of the command. + */ + send(name, args) { + if (this._listeners[0]) { + // A prior (probably timed-out) request has left listeners behind. + // Remove them before proceeding. + logger.warn("A previous failed command left content listeners behind!"); + this.removeListeners(); + } + + this.curId = uuidgen.generateUUID().toString(); - let proxy = new Promise((resolve, reject) => { - let removeListeners = (n, fn) => { - let rmFn = msg => { - if (this.curId !== msg.json.command_id) { - logger.warn("Skipping out-of-sync response from listener: " + - `Expected response to ${name} with ID ${this.curId}, ` + - "but got: " + msg.name + msg.json.toSource()); - return; - } + let proxy = new Promise((resolve, reject) => { + let removeListeners = (n, fn) => { + let rmFn = msg => { + if (this.curId !== msg.json.command_id) { + logger.warn("Skipping out-of-sync response from listener: " + + `Expected response to ${name} with ID ${this.curId}, ` + + "but got: " + msg.name + msg.json.toSource()); + return; + } + + this.removeListeners(); + modal.removeHandler(handleDialog); - this.removeListeners(); - modal.removeHandler(handleDialog); + fn(msg); + this.curId = null; + }; + + this._listeners.push([n, rmFn]); + return rmFn; + }; - fn(msg); - this.curId = null; + let okListener = () => resolve(); + let valListener = msg => resolve(msg.json.value); + let errListener = msg => reject(msg.objects.error); + + let handleDialog = (subject, topic) => { + this.removeListeners() + modal.removeHandler(handleDialog); + this.sendAsync("cancelRequest"); + resolve(); }; - this._listeners.push([n, rmFn]); - return rmFn; - }; - - let okListener = () => resolve(); - let valListener = msg => resolve(msg.json.value); - let errListener = msg => reject(msg.objects.error); - - let handleDialog = (subject, topic) => { - this.removeListeners() - modal.removeHandler(handleDialog); - this.sendAsync("cancelRequest"); - resolve(); - }; + // start content process listeners, and install observers for global- + // and tab modal dialogues + this.mm.addMessageListener(MARIONETTE_OK, removeListeners(MARIONETTE_OK, okListener)); + this.mm.addMessageListener(MARIONETTE_DONE, removeListeners(MARIONETTE_DONE, valListener)); + this.mm.addMessageListener(MARIONETTE_ERROR, removeListeners(MARIONETTE_ERROR, errListener)); + modal.addHandler(handleDialog); - // start content process listeners, and install observers for global- - // and tab modal dialogues - this.mm.addMessageListener(MARIONETTE_OK, removeListeners(MARIONETTE_OK, okListener)); - this.mm.addMessageListener(MARIONETTE_DONE, removeListeners(MARIONETTE_DONE, valListener)); - this.mm.addMessageListener(MARIONETTE_ERROR, removeListeners(MARIONETTE_ERROR, errListener)); - modal.addHandler(handleDialog); + this.sendAsync(name, marshal(args), this.curId); + }); - // new style dispatches are arrays of arguments, old style dispatches - // are key-value objects - let msg = args; - if (args.length == 1 && typeof args[0] == "object") { - msg = args[0]; - } - - this.sendAsync(name, msg, this.curId); - }); - - return proxy; + return proxy; + } }; -proxy.ContentSender = ContentSender; +/** + * Creates a transparent interface from the content- to the chrome context. + * + * Calls to this object will be proxied via the frame's sendSyncMessage + * (nsISyncMessageSender) function. Since the message is synchronous, + * the return value is presented as a return value. + * + * Example on how to use from a frame content script: + * + * let chrome = proxy.toChrome(sendSyncMessage.bind(this)); + * let cookie = chrome.getCookie("foo"); + * + * @param {nsISyncMessageSender} sendSyncMessageFn + * The frame message manager's sendSyncMessage function. + */ +proxy.toChrome = function(sendSyncMessageFn) { + let sender = new SyncChromeSender(sendSyncMessageFn); + return new Proxy(sender, ownPriorityGetterTrap); +}; + +/** + * The SyncChromeSender sends synchronous RPC messages to the chrome + * context, using a frame's sendSyncMessage (nsISyncMessageSender) function. + * + * Example on how to use from a frame content script: + * + * let sender = new SyncChromeSender(sendSyncMessage.bind(this)); + * let res = sender.send("addCookie", cookie); + */ +this.SyncChromeSender = class { + constructor(sendSyncMessage) { + this.sendSyncMessage_ = sendSyncMessage; + } + + send(func, args) { + let name = "Marionette:" + func; + return this.sendSyncMessage_(name, marshal(args)); + } +}; + +var marshal = function(args) { + if (args.length == 1 && typeof args[0] == "object") { + return args[0]; + } + return args; +};
--- a/testing/mozharness/mozharness/mozilla/testing/firefox_ui_tests.py +++ b/testing/mozharness/mozharness/mozilla/testing/firefox_ui_tests.py @@ -18,16 +18,26 @@ from mozharness.base.log import FATAL from mozharness.base.python import PostScriptRun, PreScriptAction from mozharness.mozilla.structuredlog import StructuredOutputParser from mozharness.mozilla.testing.testbase import ( TestingMixin, testing_config_options, ) from mozharness.mozilla.vcstools import VCSToolsScript +# Command line arguments for firefox ui tests +firefox_ui_tests_harness_config_options = [ + [["--e10s"], { + 'dest': 'e10s', + 'action': 'store_true', + 'default': False, + 'help': 'Enable multi-process (e10s) mode when running tests.', + }], +] + # General command line arguments for Firefox ui tests firefox_ui_tests_config_options = [ [['--dry-run'], { 'dest': 'dry_run', 'default': False, 'help': 'Only show what was going to be tested.', }], [['--firefox-ui-branch'], { @@ -39,17 +49,18 @@ firefox_ui_tests_config_options = [ 'default': 'https://github.com/mozilla/firefox-ui-tests.git', 'help': 'which firefox_ui_tests repo to use', }], [['--symbols-path=SYMBOLS_PATH'], { 'dest': 'symbols_path', 'help': 'absolute path to directory containing breakpad ' 'symbols, or the url of a zip file containing symbols.', }], -] + copy.deepcopy(testing_config_options) +] + firefox_ui_tests_harness_config_options \ + + copy.deepcopy(testing_config_options) # Command line arguments for update tests firefox_ui_update_harness_config_options = [ [['--update-allow-mar-channel'], { 'dest': 'update_allow_mar_channel', 'help': 'Additional MAR channel to be allowed for updates, e.g. ' '"firefox-mozilla-beta" for updating a release build to ' 'the latest beta build.', @@ -200,22 +211,36 @@ class FirefoxUITests(TestingMixin, VCSTo abs_dirs.update({ 'abs_reports_dir': os.path.join(abs_dirs['base_work_dir'], 'reports'), 'fx_ui_dir': os.path.join(abs_dirs['abs_work_dir'], 'firefox_ui_tests'), }) self.abs_dirs = abs_dirs return self.abs_dirs - def query_extra_cmd_args(self): + def query_harness_args(self, extra_harness_config_options): """Collects specific update test related command line arguments. Sub classes should override this method for their own specific arguments. """ - return [] + extra_harness_config_options = extra_harness_config_options or [] + config_options = firefox_ui_tests_harness_config_options + extra_harness_config_options + + args = [] + for option in config_options: + dest = option[1]['dest'] + name = self.config.get(dest) + + if name: + if type(name) is bool: + args.append(option[0][0]) + else: + args.extend([option[0][0], self.config[dest]]) + + return args def query_minidump_stackwalk(self): """We don't have an extracted test package available to get the manifest file. So we have to explicitely download the latest version of the manifest from the mozilla-central repository and feed it into the query_minidump_stackwalk() method. We can remove this whole method once our tests are part of the tree. @@ -260,17 +285,17 @@ class FirefoxUITests(TestingMixin, VCSTo '--log-raw=-', # structured log for output parser redirected to stdout # additional reports helpful for Jenkins and inpection via Treeherder '--log-html', os.path.join(dirs["abs_reports_dir"], self.reports['html']), '--log-xunit', os.path.join(dirs["abs_reports_dir"], self.reports['xunit']), ] # Collect all pass-through harness options to the script - cmd.extend(self.query_extra_cmd_args()) + cmd.extend(self.query_harness_args()) # Set further environment settings env = env or self.query_env() if self.symbols_url: cmd.extend(['--symbols-path', self.symbols_url]) if self.query_minidump_stackwalk(): @@ -310,23 +335,12 @@ class FirefoxUIUpdateTests(FirefoxUITest cli_script = 'cli_update.py' def __init__(self, config_options=None, *args, **kwargs): config_options = config_options or firefox_ui_update_config_options FirefoxUITests.__init__(self, config_options=config_options, *args, **kwargs) - def query_extra_cmd_args(self): + def query_harness_args(self): """Collects specific update test related command line arguments.""" - args = [] - - for option in firefox_ui_update_harness_config_options: - dest = option[1]['dest'] - name = self.config.get(dest) - - if name: - if type(name) is bool: - args.append(option[0][0]) - else: - args.extend([option[0][0], self.config[dest]]) - - return args + return FirefoxUITests.query_harness_args(self, + firefox_ui_update_harness_config_options)
deleted file mode 100644 --- a/testing/web-platform/meta/dom/nodes/MutationObserver-document.html.ini +++ /dev/null @@ -1,11 +0,0 @@ -[MutationObserver-document.html] - type: testharness - [parser insertion mutations] - expected: FAIL - - [parser script insertion mutation] - expected: FAIL - - [removal of parent during parsing] - expected: FAIL -
deleted file mode 100644 --- a/testing/web-platform/mozilla/meta/service-workers/service-worker/register-wait-forever-in-install-worker.https.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[register-wait-forever-in-install-worker.https.html] - type: testharness - expected: TIMEOUT - [register worker that calls waitUntil with a promise that never resolves in oninstall] - expected: TIMEOUT -
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/getregistration.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/getregistration.https.html @@ -19,36 +19,38 @@ async_test(function(t) { var registration; service_worker_unregister_and_register(t, 'resources/empty-worker.js', scope) .then(function(r) { registration = r; return navigator.serviceWorker.getRegistration(scope); }) .then(function(value) { - assert_equals(value.scope, registration.scope, - 'getRegistration should resolve with registration'); + assert_equals( + value, registration, + 'getRegistration should resolve to the same registration object'); service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); }, 'Register then getRegistration'); async_test(function(t) { var scope = 'resources/scope/getregistration/url-with-fragment'; var documentURL = scope + '#ref'; var registration; service_worker_unregister_and_register(t, 'resources/empty-worker.js', scope) .then(function(r) { registration = r; return navigator.serviceWorker.getRegistration(documentURL); }) .then(function(value) { - assert_equals(value.scope, registration.scope, - 'getRegistration should resolve with registration'); + assert_equals( + value, registration, + 'getRegistration should resolve to the same registration object'); service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); }, 'Register then getRegistration with a URL having a fragment'); async_test(function(t) { var documentURL = 'http://example.com/'; navigator.serviceWorker.getRegistration(documentURL)
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/getregistrations.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/getregistrations.https.html @@ -31,45 +31,45 @@ promise_test(function(t) { }, 'getRegistrations'); promise_test(function(t) { var scope = 'resources/scope/getregistrations/normal'; var script = 'resources/empty-worker.js'; var registrations = []; return service_worker_unregister_and_register(t, script, scope) .then(function(r) { - registrations.push(r.scope); + registrations.push(r); return navigator.serviceWorker.getRegistrations(); }) .then(function(value) { assert_array_equals( - value.map((r) => r.scope), + value, registrations, 'getRegistrations should resolve with array of registrations.'); return service_worker_unregister(t, scope); }); }, 'Register then getRegistrations'); promise_test(function(t) { var scope1 = 'resources/scope/getregistrations/scope1'; var scope2 = 'resources/scope/getregistrations/scope2'; var script = 'resources/empty-worker.js'; var registrations = []; return service_worker_unregister_and_register(t, script, scope1) .then(function(r) { - registrations.push(r.scope); + registrations.push(r); return service_worker_unregister_and_register(t, script, scope2); }) .then(function(r) { - registrations.push(r.scope); + registrations.push(r); return navigator.serviceWorker.getRegistrations(); }) .then(function(value) { assert_array_equals( - value.map((r) => r.scope), + value, registrations, 'getRegistrations should resolve with array of registrations.'); return service_worker_unregister(t, scope1); }) .then(function() { return service_worker_unregister(t, scope2); }); }, 'Register multiple times then getRegistrations'); @@ -124,22 +124,22 @@ promise_test(function(t) { // test. So we have to wait until the cross origin register() is done, and not // just until the frame loads. return with_iframe_ready(frame_url) .then(function(f) { frame = f; return service_worker_unregister_and_register(t, script, scope); }) .then(function(r) { - registrations.push(r.scope); + registrations.push(r); return navigator.serviceWorker.getRegistrations(); }) .then(function(value) { assert_array_equals( - value.map((r) => r.scope), + value, registrations, 'getRegistrations should only return same origin registrations.'); var channel = new MessageChannel(); var resolve; var p = new Promise(function(r) { resolve = r; }); channel.port1.onmessage = function(e) {
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/multiple-register.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/multiple-register.https.html @@ -14,24 +14,20 @@ async_test(function(t) { .then(function(r) { registration = r; return wait_for_state(t, r.installing, 'activated'); }) .then(function() { return navigator.serviceWorker.register(worker_url, { scope: scope }); }) .then(function(new_registration) { - // FIXME: Bug 1201127 will fix scope vs object comparisons. - assert_not_equals( - registration, new_registration, - 'register should resolve to the different registration'); - assert_equals(new_registration.scope, registration.scope, - 'registrations should have the same scope'); - assert_equals(new_registration.active.scriptURL, registration.active.scriptURL, - 'active workers should have same scriptURL'); + assert_equals(new_registration, registration, + 'register should resolve to the same registration'); + assert_equals(new_registration.active, registration.active, + 'register should resolve to the same worker'); assert_equals(new_registration.active.state, 'activated', 'the worker should be in state "activated"'); return registration.unregister(); }) .then(function() { t.done(); }) .catch(unreached_rejection(t)); }, 'Subsequent registrations resolve to the same registration object'); @@ -47,20 +43,19 @@ async_test(function(t) { }) .then(function() { return with_iframe('resources/404.py'); }) .then(function(f) { frame = f; return frame.contentWindow.navigator.serviceWorker.register( worker_url, { scope: scope }); }) .then(function(new_registration) { - // FIXME: Bug 1201127 will fix scope vs object comparisons. assert_not_equals( registration, new_registration, - 'register should resolve to the different registration'); + 'register should resolve to a different registration'); assert_equals( registration.scope, new_registration.scope, 'registrations should have the same scope'); assert_equals( registration.installing, null, 'installing worker should be null'); assert_equals( @@ -103,19 +98,18 @@ async_test(function(t) { for (var i = 0; i < 10; ++i) { promises.push(navigator.serviceWorker.register(worker_url, { scope: scope })); } return Promise.all(promises); }) .then(function(registrations) { registrations.forEach(function(registration) { - // FIXME: Bug 1201127 will fix scope vs object comparisons. - assert_equals(registration.scope, registrations[0].scope, - 'register should resolve to registrations with the same scope'); + assert_equals(registration, registrations[0], + 'register should resolve to the same registration'); }); return registrations[0].unregister(); }) .then(function() { t.done(); }) .catch(unreached_rejection(t)); }, 'Concurrent registrations resolve to the same registration object'); </script>
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/skip-waiting-worker.js +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/skip-waiting-worker.js @@ -1,12 +1,16 @@ importScripts('worker-testharness.js'); promise_test(function() { - return self.skipWaiting() + // wait for the worker to reach "installing" state, otherwise skipWaiting() + // will fail. Bug 1228277 + return new Promise(function(res, rej) { + oninstall = res; + }).then(() => skipWaiting()) .then(function(result) { assert_equals(result, undefined, 'Promise should be resolved with undefined'); }) .then(function() { var promises = []; for (var i = 0; i < 8; ++i) promises.push(self.skipWaiting());
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting.https.html @@ -41,14 +41,13 @@ promise_test(function(t) { }) .then(function() { assert_equals(activated_worker.state, 'redundant', 'Worker with url1 should be redundant'); assert_equals(waiting_worker.state, 'redundant', 'Worker with url2 should be redundant'); assert_equals(sw_registration.active.scriptURL, normalizeURL(url3), 'Worker with url3 should be activated'); - frame.remove(); return service_worker_unregister_and_done(t, scope); }); }, 'Test skipWaiting with both active and waiting workers'); </script>
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/unregister-then-register.https.html +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/unregister-then-register.https.html @@ -43,18 +43,18 @@ async_test(function(t) { }) .then(function(frame) { return registration.unregister(); }) .then(function() { return navigator.serviceWorker.register(worker_url, { scope: scope }); }) .then(function(new_registration) { - assert_equals(registration.scope, new_registration.scope, - 'new registration should resolve to the same scope'); + assert_equals(registration, new_registration, + 'new registration should resolve to the same registration'); service_worker_unregister_and_done(t, scope); }) .catch(unreached_rejection(t)); }, 'Unregister then register resolves to the original value if the ' + 'registration is in use.'); async_test(function(t) { var scope = 'resources/scope/re-register-does-not-affect-existing-controllee';
--- a/testing/web-platform/tests/dom/nodes/MutationObserver-document.html +++ b/testing/web-platform/tests/dom/nodes/MutationObserver-document.html @@ -44,29 +44,29 @@ }}]); }); } else if (testCounter == 2) { insertionTest2.step( function () { checkRecords(document, sequence, [{type: "childList", addedNodes: function () { + return [ document.getElementById("inserted_script") ]; + }, + target: function () { + return document.getElementById("n00"); + }}, + {type: "childList", + addedNodes: function () { return [ document.getElementById("inserted_element") ]; }, previousSibling: function () { return document.getElementById("s002"); }, - target: document.body}, - {type: "childList", - addedNodes: function () { - return [ document.getElementById("inserted_script") ]; - }, - target: function () { - return document.getElementById("n00"); - }} + target: document.body} ]); }); } } var document_observer; var newElement; setupTest.step(function() { document_observer = new MutationObserver(masterMO);
--- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -10071,16 +10071,86 @@ "description": "Counts number of times a certain plugin has been activated." }, "YOUTUBE_EMBED_SEEN": { "alert_emails": ["cpeterson@mozilla.com"], "expires_in_version": "48", "kind": "flag", "description": "Flag activated whenever a youtube flash embed is seen during a session." }, + "WEBFONT_DOWNLOAD_TIME": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Time to download a webfont (ms)" + }, + "WEBFONT_DOWNLOAD_TIME_AFTER_START": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Time after navigationStart webfont download completed (ms)" + }, + "WEBFONT_FONTTYPE": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 10, + "description": "Font format type (woff/woff2/ttf/...)" + }, + "WEBFONT_SRCTYPE": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 5, + "description": "Font src type loaded (1 = local, 2 = url, 3 = data)" + }, + "WEBFONT_PER_PAGE": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "count", + "description": "Number of fonts loaded at page load" + }, + "WEBFONT_SIZE_PER_PAGE": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "exponential", + "high": "5000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Size of all fonts loaded at page load (kb)" + }, + "WEBFONT_SIZE": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "exponential", + "high": "5000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Size of font loaded (kb)" + }, + "WEBFONT_COMPRESSION_WOFF": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 50, + "description": "Compression ratio of WOFF data (%)" + }, + "WEBFONT_COMPRESSION_WOFF2": { + "alert_emails": ["jdaggett@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 50, + "description": "Compression ratio of WOFF2 data (%)" + }, "WEBRTC_ICE_CHECKING_RATE": { "alert_emails": ["webrtc-ice-telemetry-alerts@mozilla.com"], "expires_in_version": "53", "kind": "boolean", "bug_numbers": [1188391], "description": "The number of ICE connections which immediately failed (0) vs. reached at least checking state (1)." } }
--- a/tools/profiler/core/GeckoSampler.cpp +++ b/tools/profiler/core/GeckoSampler.cpp @@ -215,16 +215,22 @@ GeckoSampler::GeckoSampler(double aInter #endif // Deep copy aThreadNameFilters MOZ_ALWAYS_TRUE(mThreadNameFilters.resize(aFilterCount)); for (uint32_t i = 0; i < aFilterCount; ++i) { mThreadNameFilters[i] = aThreadNameFilters[i]; } + // Deep copy aFeatures + MOZ_ALWAYS_TRUE(mFeatures.resize(aFeatureCount)); + for (uint32_t i = 0; i < aFeatureCount; ++i) { + mFeatures[i] = aFeatures[i]; + } + bool ignore; sStartTime = mozilla::TimeStamp::ProcessCreation(ignore); { ::MutexAutoLock lock(*sRegisteredThreadsMutex); // Create ThreadProfile for each registered thread for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
--- a/tools/profiler/core/GeckoSampler.h +++ b/tools/profiler/core/GeckoSampler.h @@ -18,16 +18,17 @@ #include "GeckoTaskTracer.h" #endif namespace mozilla { class ProfileGatherer; } // namespace mozilla typedef mozilla::Vector<std::string> ThreadNameFilterList; +typedef mozilla::Vector<std::string> FeatureList; static bool threadSelected(ThreadInfo* aInfo, const ThreadNameFilterList &aThreadNameFilters) { if (aThreadNameFilters.empty()) { return true; } for (uint32_t i = 0; i < aThreadNameFilters.length(); ++i) { @@ -116,16 +117,18 @@ class GeckoSampler: public Sampler { bool ProfileThreads() const override { return mProfileThreads; } bool InPrivacyMode() const { return mPrivacyMode; } bool AddMainThreadIO() const { return mAddMainThreadIO; } bool ProfileMemory() const { return mProfileMemory; } bool TaskTracer() const { return mTaskTracer; } bool LayersDump() const { return mLayersDump; } bool DisplayListDump() const { return mDisplayListDump; } bool ProfileRestyle() const { return mProfileRestyle; } + const ThreadNameFilterList& ThreadNameFilters() { return mThreadNameFilters; } + const FeatureList& Features() { return mFeatures; } void GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration); void ProfileGathered(); protected: // Called within a signal. This function must be reentrant virtual void InplaceTick(TickSample* sample); @@ -148,16 +151,17 @@ protected: bool mProfilePower; bool mLayersDump; bool mDisplayListDump; bool mProfileRestyle; // Keep the thread filter to check against new thread that // are started while profiling ThreadNameFilterList mThreadNameFilters; + FeatureList mFeatures; bool mPrivacyMode; bool mAddMainThreadIO; bool mProfileMemory; bool mTaskTracer; #if defined(XP_WIN) IntelPowerGadget* mIntelPowerGadget; #endif
--- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -605,16 +605,48 @@ void mozilla_sampler_get_profile_data_as { GeckoSampler *t = tlsTicker.get(); if (NS_WARN_IF(!t)) { return; } t->ToJSObjectAsync(aSinceTime, aPromise); } + +void mozilla_sampler_get_profiler_start_params(int* aEntrySize, + double* aInterval, + mozilla::Vector<const char*>* aFilters, + mozilla::Vector<const char*>* aFeatures) +{ + if (NS_WARN_IF(!aEntrySize) || NS_WARN_IF(!aInterval) || + NS_WARN_IF(!aFilters) || NS_WARN_IF(!aFeatures)) { + return; + } + + GeckoSampler *t = tlsTicker.get(); + if (NS_WARN_IF(!t)) { + return; + } + + *aEntrySize = t->EntrySize(); + *aInterval = t->interval(); + + const ThreadNameFilterList& threadNameFilterList = t->ThreadNameFilters(); + MOZ_ALWAYS_TRUE(aFilters->resize(threadNameFilterList.length())); + for (uint32_t i = 0; i < threadNameFilterList.length(); ++i) { + (*aFilters)[i] = threadNameFilterList[i].c_str(); + } + + const FeatureList& featureList = t->Features(); + MOZ_ALWAYS_TRUE(aFeatures->resize(featureList.length())); + for (size_t i = 0; i < featureList.length(); ++i) { + (*aFeatures)[i] = featureList[i].c_str(); + } +} + #endif void mozilla_sampler_save_profile_to_file(const char* aFilename) { GeckoSampler *t = tlsTicker.get(); if (!t) { return; }
new file mode 100644 --- /dev/null +++ b/tools/profiler/gecko/ProfilerTypes.ipdlh @@ -0,0 +1,16 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* 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/. */ + +namespace mozilla { + +struct ProfilerInitParams { + bool enabled; + uint32_t entries; + double interval; + nsCString[] threadFilters; + nsCString[] features; +}; + +} // namespace mozilla \ No newline at end of file
--- a/tools/profiler/gecko/nsIProfiler.idl +++ b/tools/profiler/gecko/nsIProfiler.idl @@ -7,17 +7,32 @@ %{C++ template<class T> class nsTArray; class nsCString; %} [ref] native StringArrayRef(const nsTArray<nsCString>); -[scriptable, uuid(ff398a14-df1c-4966-9ab2-772ea6a6da6c)] +/** + * Start-up parameters for subprocesses are passed through nsIObserverService, + * which, unfortunately, means we need to implement nsISupports in order to + * go through it. + */ +[uuid(0a175ba7-8fcf-4ce9-9c4b-ccc6272f4425)] +interface nsIProfilerStartParams : nsISupports +{ + attribute uint32_t entries; + attribute double interval; + + [noscript, notxpcom, nostdcall] StringArrayRef getFeatures(); + [noscript, notxpcom, nostdcall] StringArrayRef getThreadFilterNames(); +}; + +[scriptable, uuid(b373b360-c997-448a-b60d-4985b70dc810)] interface nsIProfiler : nsISupports { boolean CanProfile(); void StartProfiler(in uint32_t aEntries, in double aInterval, [array, size_is(aFeatureCount)] in string aFeatures, in uint32_t aFeatureCount, [array, size_is(aFilterCount), optional] in string aThreadNameFilters, [optional] in uint32_t aFilterCount); @@ -40,16 +55,22 @@ interface nsIProfiler : nsISupports jsval getProfileData([optional] in double aSinceTime); [implicit_jscontext] nsISupports getProfileDataAsync([optional] in double aSinceTime); boolean IsActive(); void GetFeatures(out uint32_t aCount, [retval, array, size_is(aCount)] out string aFeatures); + /** + * The starting parameters that were sent to the profiler for sampling. + * If the profiler is not currently sampling, this will return null. + */ + readonly attribute nsIProfilerStartParams startParams; + void GetBufferInfo(out uint32_t aCurrentPosition, out uint32_t aTotalSize, out uint32_t aGeneration); /** * Returns the elapsed time, in milliseconds, since the profiler's epoch. * The epoch is guaranteed to be constant for the duration of the * process, but is otherwise arbitrary. */ @@ -67,23 +88,8 @@ interface nsIProfiler : nsISupports */ AString getSharedLibraryInformation(); /** * Dump the collected profile to a file. */ void dumpProfileToFile(in string aFilename); }; - -/** - * Start-up parameters for subprocesses are passed through nsIObserverService, - * which, unfortunately, means we need to implement nsISupports in order to - * go through it. - */ -[uuid(0a175ba7-8fcf-4ce9-9c4b-ccc6272f4425)] -interface nsIProfilerStartParams : nsISupports -{ - attribute uint32_t entries; - attribute double interval; - - [noscript, notxpcom, nostdcall] StringArrayRef getFeatures(); - [noscript, notxpcom, nostdcall] StringArrayRef getThreadFilterNames(); -};
--- a/tools/profiler/gecko/nsProfiler.cpp +++ b/tools/profiler/gecko/nsProfiler.cpp @@ -2,16 +2,17 @@ /* 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 <string> #include <sstream> #include "GeckoProfiler.h" #include "nsProfiler.h" +#include "nsProfilerStartParams.h" #include "nsMemory.h" #include "nsString.h" #include "mozilla/Services.h" #include "nsIObserverService.h" #include "nsIInterfaceRequestor.h" #include "nsILoadContext.h" #include "nsIWebNavigation.h" #include "nsIInterfaceRequestorUtils.h" @@ -243,16 +244,47 @@ nsProfiler::GetFeatures(uint32_t *aCount } *aFeatures = featureList; *aCount = len; return NS_OK; } NS_IMETHODIMP +nsProfiler::GetStartParams(nsIProfilerStartParams** aRetVal) +{ + if (!profiler_is_active()) { + *aRetVal = nullptr; + } else { + int entrySize = 0; + double interval = 0; + mozilla::Vector<const char*> filters; + mozilla::Vector<const char*> features; + profiler_get_start_params(&entrySize, &interval, &filters, &features); + + nsTArray<nsCString> filtersArray; + for (uint32_t i = 0; i < filters.length(); ++i) { + filtersArray.AppendElement(filters[i]); + } + + nsTArray<nsCString> featuresArray; + for (size_t i = 0; i < features.length(); ++i) { + featuresArray.AppendElement(features[i]); + } + + nsCOMPtr<nsIProfilerStartParams> startParams = + new nsProfilerStartParams(entrySize, interval, featuresArray, + filtersArray); + + startParams.forget(aRetVal); + } + return NS_OK; +} + +NS_IMETHODIMP nsProfiler::GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration) { MOZ_ASSERT(aCurrentPosition); MOZ_ASSERT(aTotalSize); MOZ_ASSERT(aGeneration); profiler_get_buffer_info(aCurrentPosition, aTotalSize, aGeneration); return NS_OK; }
--- a/tools/profiler/moz.build +++ b/tools/profiler/moz.build @@ -104,16 +104,22 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']: if CONFIG['ENABLE_TESTS']: DIRS += ['tests/gtest'] if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and (CONFIG['ANDROID_VERSION'] <= '17' or CONFIG['ANDROID_VERSION'] >= '21'): DEFINES['ELFSIZE'] = 32 FINAL_LIBRARY = 'xul' +IPDL_SOURCES += [ + 'gecko/ProfilerTypes.ipdlh', +] + +include('/ipc/chromium/chromium-config.mozbuild') + EXPORTS += [ 'public/GeckoProfiler.h', ] if CONFIG['MOZ_TASK_TRACER']: EXPORTS += [ 'public/GeckoTaskTracer.h', 'public/GeckoTaskTracerImpl.h',
--- a/tools/profiler/public/GeckoProfiler.h +++ b/tools/profiler/public/GeckoProfiler.h @@ -48,26 +48,31 @@ #ifndef SAMPLER_H #define SAMPLER_H #ifndef SPS_STANDALONE #include "js/TypeDecls.h" #endif #include "mozilla/UniquePtr.h" +#include "mozilla/Vector.h" namespace mozilla { class TimeStamp; namespace dom { class Promise; } // namespace dom } // namespace mozilla +#ifndef SPS_STANDALONE +class nsIProfilerStartParams; +#endif + enum TracingMetadata { TRACING_DEFAULT, TRACING_INTERVAL_START, TRACING_INTERVAL_END, TRACING_EVENT, TRACING_EVENT_BACKTRACE, TRACING_TIMESTAMP }; @@ -177,16 +182,20 @@ static inline JSObject* profiler_get_pro double aSinceTime = 0) { return nullptr; } #ifndef SPS_STANDALONE // Get the profile encoded as a JSON object. static inline void profiler_get_profile_jsobject_async(double aSinceTime = 0, mozilla::dom::Promise* = 0) {} +static inline void profiler_get_start_params(int* aEntrySize, + double* aInterval, + mozilla::Vector<const char*>* aFilters, + mozilla::Vector<const char*>* aFeatures) {} #endif // Get the profile and write it into a file static inline void profiler_save_profile_to_file(char* aFilename) { } // Get the features supported by the profiler that are accepted by profiler_init. // Returns a null terminated char* array. static inline char** profiler_get_features() { return nullptr; }
--- a/tools/profiler/public/GeckoProfilerFunc.h +++ b/tools/profiler/public/GeckoProfilerFunc.h @@ -6,16 +6,17 @@ #ifndef PROFILER_FUNCS_H #define PROFILER_FUNCS_H #ifndef SPS_STANDALONE #include "js/TypeDecls.h" #endif #include "js/ProfilingStack.h" #include "mozilla/UniquePtr.h" +#include "mozilla/Vector.h" #include <stdint.h> namespace mozilla { class TimeStamp; namespace dom { class Promise; } // namespace dom @@ -63,16 +64,20 @@ const double* mozilla_sampler_get_respon void mozilla_sampler_save(); mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime); #ifndef SPS_STANDALONE JSObject *mozilla_sampler_get_profile_data(JSContext* aCx, double aSinceTime); void mozilla_sampler_get_profile_data_async(double aSinceTime, mozilla::dom::Promise* aPromise); +void mozilla_sampler_get_profiler_start_params(int* aEntrySize, + double* aInterval, + mozilla::Vector<const char*>* aFilters, + mozilla::Vector<const char*>* aFeatures); #endif // Make this function easily callable from a debugger in a build without // debugging information (work around http://llvm.org/bugs/show_bug.cgi?id=22211) extern "C" { void mozilla_sampler_save_profile_to_file(const char* aFilename); }
--- a/tools/profiler/public/GeckoProfilerImpl.h +++ b/tools/profiler/public/GeckoProfilerImpl.h @@ -165,16 +165,25 @@ JSObject* profiler_get_profile_jsobject( } static inline void profiler_get_profile_jsobject_async(double aSinceTime = 0, mozilla::dom::Promise* aPromise = 0) { mozilla_sampler_get_profile_data_async(aSinceTime, aPromise); } + +static inline +void profiler_get_start_params(int* aEntrySize, + double* aInterval, + mozilla::Vector<const char*>* aFilters, + mozilla::Vector<const char*>* aFeatures) +{ + mozilla_sampler_get_profiler_start_params(aEntrySize, aInterval, aFilters, aFeatures); +} #endif static inline void profiler_save_profile_to_file(const char* aFilename) { return mozilla_sampler_save_profile_to_file(aFilename); }