author | Ed Morley <emorley@mozilla.com> |
Fri, 10 Jan 2014 15:25:50 +0000 | |
changeset 162959 | 3f4308d223cf7c350c240f96f67c0680ae829799 |
parent 162817 | 30f3710477c2f3cf6d939146a6950801341d6d09 (current diff) |
parent 162958 | d3e7f639267094b2ea372e7094a3a6c806a653a2 (diff) |
child 162960 | cbe9f7791348460899a877bfe8068ef6d5799b4c |
push id | 25975 |
push user | ryanvm@gmail.com |
push date | Fri, 10 Jan 2014 19:46:47 +0000 |
treeherder | autoland@e89afc241513 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 29.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
intl/uconv/util/ubase.h | file | annotate | diff | comparison | revisions |
--- a/browser/base/content/test/general/browser_save_link-perwindowpb.js +++ b/browser/base/content/test/general/browser_save_link-perwindowpb.js @@ -11,20 +11,23 @@ let NetUtil = tempScope.NetUtil; // Trigger a save of a link in public mode, then trigger an identical save // in private mode and ensure that the second request is differentiated from // the first by checking that cookies set by the first response are not sent // during the second request. function triggerSave(aWindow, aCallback) { var fileName; let testBrowser = aWindow.gBrowser.selectedBrowser; // This page sets a cookie if and only if a cookie does not exist yet - testBrowser.loadURI("http://mochi.test:8888/browser/browser/base/content/test/general/bug792517-2.html"); + let testURI = "http://mochi.test:8888/browser/browser/base/content/test/general/bug792517-2.html"; + testBrowser.loadURI(testURI); testBrowser.addEventListener("pageshow", function pageShown(event) { - if (event.target.location == "about:blank") + if (event.target.location != testURI) { + testBrowser.loadURI(testURI); return; + } testBrowser.removeEventListener("pageshow", pageShown, false); executeSoon(function () { aWindow.document.addEventListener("popupshown", function(e) contextMenuOpened(aWindow, e), false); var link = testBrowser.contentDocument.getElementById("fff"); EventUtils.synthesizeMouseAtCenter(link, { type: "contextmenu", button: 2 },
--- a/build/automation.py.in +++ b/build/automation.py.in @@ -843,17 +843,17 @@ class Automation(object): def checkForCrashes(self, minidumpDir, symbolsPath): return mozcrash.check_for_crashes(minidumpDir, symbolsPath, test_name=self.lastTestSeen) def runApp(self, testURL, env, app, profileDir, extraArgs, runSSLTunnel = False, utilityPath = None, xrePath = None, certPath = None, debuggerInfo = None, symbolsPath = None, timeout = -1, maxTime = None, onLaunch = None, - webapprtChrome = False): + webapprtChrome = False, hide_subtests=None): """ Run the app, log the duration it took to execute, return the status code. Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds. """ if utilityPath == None: utilityPath = self.DIST_BIN if xrePath == None:
--- a/config/rules.mk +++ b/config/rules.mk @@ -119,19 +119,16 @@ SIMPLE_PROGRAMS += $(CPP_UNIT_TEST_BINS) INCLUDES += -I$(DIST)/include/testing LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) ifndef MOZ_PROFILE_GENERATE libs:: $(CPP_UNIT_TEST_BINS) $(call mkdir_deps,$(DIST)/cppunittests) $(NSINSTALL) $(CPP_UNIT_TEST_BINS) $(DIST)/cppunittests endif -check:: - @$(PYTHON) $(topsrcdir)/testing/runcppunittests.py --xre-path=$(DIST)/bin --symbols-path=$(DIST)/crashreporter-symbols $(subst .cpp,$(BIN_SUFFIX),$(CPP_UNIT_TESTS)) - cppunittests-remote: DM_TRANS?=adb cppunittests-remote: @if [ '${TEST_DEVICE}' != '' -o '$(DM_TRANS)' = 'adb' ]; then \ $(PYTHON) -u $(topsrcdir)/testing/remotecppunittests.py \ --xre-path=$(DEPTH)/dist/bin \ --localLib=$(DEPTH)/dist/$(MOZ_APP_NAME) \ --dm_trans=$(DM_TRANS) \ --deviceIP=${TEST_DEVICE} \
--- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -8813,16 +8813,20 @@ nsDocument::SetScrollToRef(nsIURI *aDocu mScrollToRef = Substring(start, end); } } void nsDocument::ScrollToRef() { if (mScrolledToRefAlready) { + nsCOMPtr<nsIPresShell> shell = GetShell(); + if (shell) { + shell->ScrollToAnchor(); + } return; } if (mScrollToRef.IsEmpty()) { return; } char* tmpstr = ToNewCString(mScrollToRef);
--- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -1894,17 +1894,21 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ mErrorLoad = mErrorLoad || NS_FAILED(status); if (mUpload && !mUploadComplete && !mErrorLoad && (mState & XML_HTTP_REQUEST_ASYNC)) { if (mProgressTimerIsActive) { mProgressTimerIsActive = false; mProgressNotifier->Cancel(); } - MaybeDispatchProgressEvents(true); + if (mUploadTransferred < mUploadTotal) { + mUploadTransferred = mUploadTotal; + mProgressSinceLastProgressEvent = true; + MaybeDispatchProgressEvents(true); + } mUploadComplete = true; DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOAD_STR), true, mUploadTotal, mUploadTotal); } mContext = ctxt; mState |= XML_HTTP_REQUEST_PARSEBODY; ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED); @@ -3405,19 +3409,16 @@ nsXMLHttpRequest::MaybeDispatchProgressE if (!aFinalProgress) { StartProgressEventTimer(); } // We're uploading if our state is XML_HTTP_REQUEST_OPENED or // XML_HTTP_REQUEST_SENT if ((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState) { - if (aFinalProgress) { - mUploadTotal = mUploadTransferred; - } if (mUpload && !mUploadComplete) { DispatchProgressEvent(mUpload, NS_LITERAL_STRING(PROGRESS_STR), mUploadLengthComputable, mUploadTransferred, mUploadTotal); } } else { if (aFinalProgress) { mLoadTotal = mLoadTransferred;
--- a/content/base/test/test_bug435425.html +++ b/content/base/test/test_bug435425.html @@ -18,25 +18,33 @@ https://bugzilla.mozilla.org/show_bug.cg <script class="testbody" type="text/javascript"> /** Test for Bug 435425 **/ var xhr = null; var upload = null; var currentEvents = null; var expectedResponseText = null; +var uploadTotal = 0; function logEvent(evt) { var i = 0; while ((currentEvents.length != i) && currentEvents[i].optional && ((currentEvents[i].type != evt.type) || !(evt.target instanceof currentEvents[i].target))) { ++i; } + if (evt.target instanceof XMLHttpRequestUpload) { + if (evt.type == "loadstart") { + uploadTotal = evt.total + } else { + is(evt.total, uploadTotal, "event(" + evt.type + ").total should not change during upload."); + } + } ok(i != currentEvents.length, "Extra or wrong event?"); is(evt.type, currentEvents[i].type, "Wrong event!") ok(evt.target instanceof currentEvents[i].target, "Wrong event target [" + evt.target + "," + evt.type + "]!"); // If we handled non-optional event, remove all optional events before the // handled event and then the non-optional event from the list. if (!currentEvents[i].optional) { for (;i != -1; --i) {
--- a/content/canvas/src/CanvasRenderingContext2D.cpp +++ b/content/canvas/src/CanvasRenderingContext2D.cpp @@ -1785,33 +1785,16 @@ bool CanvasRenderingContext2D::DrawCusto } nsIFocusManager* fm = nsFocusManager::GetFocusManager(); if (fm) { // check that the element i focused nsCOMPtr<nsIDOMElement> focusedElement; fm->GetFocusedElement(getter_AddRefs(focusedElement)); if (SameCOMIdentity(aElement.AsDOMNode(), focusedElement)) { - // get the bounds of the current path - mgfx::Rect bounds; - bounds = mPath->GetBounds(mTarget->GetTransform()); - - // and set them as the accessible area - nsRect rect(canvas->ClientLeft() + bounds.x, canvas->ClientTop() + bounds.y, - bounds.width, bounds.height); - rect.x *= AppUnitsPerCSSPixel(); - rect.y *= AppUnitsPerCSSPixel(); - rect.width *= AppUnitsPerCSSPixel(); - rect.height *= AppUnitsPerCSSPixel(); - - nsIFrame* frame = aElement.GetPrimaryFrame(); - if(frame) { - frame->SetRect(rect); - } - return true; } } return false; } void
--- a/content/canvas/src/WebGLContext.cpp +++ b/content/canvas/src/WebGLContext.cpp @@ -151,16 +151,21 @@ WebGLContext::WebGLContext() mStencilClearValue = 0; mStencilRefFront = 0; mStencilRefBack = 0; mStencilValueMaskFront = 0xffffffff; mStencilValueMaskBack = 0xffffffff; mStencilWriteMaskFront = 0xffffffff; mStencilWriteMaskBack = 0xffffffff; + mViewportX = 0; + mViewportY = 0; + mViewportWidth = 0; + mViewportHeight = 0; + mScissorTestEnabled = 0; mDitherEnabled = 1; mRasterizerDiscardEnabled = 0; // OpenGL ES 3.0 spec p244 // initialize some GL values: we're going to get them from the GL and use them as the sizes of arrays, // so in case glGetIntegerv leaves them uninitialized because of a GL bug, we would have very weird crashes. mGLMaxVertexAttribs = 0; mGLMaxTextureUnits = 0; @@ -188,16 +193,17 @@ WebGLContext::WebGLContext() mContextRestorer = do_CreateInstance("@mozilla.org/timer;1"); mContextStatus = ContextNotLost; mContextLostErrorSet = false; mLoseContextOnHeapMinimize = false; mCanLoseContextInForeground = true; mAlreadyGeneratedWarnings = 0; mAlreadyWarnedAboutFakeVertexAttrib0 = false; + mAlreadyWarnedAboutViewportLargerThanDest = false; mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32); if (mMaxWarnings < -1) { GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)"); mMaxWarnings = 0; } mLastUseIndex = 0; @@ -568,16 +574,18 @@ WebGLContext::SetDimensions(int32_t widt #ifdef DEBUG if (gl->DebugMode()) { printf_stderr("--- WebGL context created: %p\n", gl.get()); } #endif mWidth = width; mHeight = height; + mViewportWidth = width; + mViewportHeight = height; mResetLayer = true; mOptionsFrozen = true; mHasRobustness = gl->HasRobustness(); // increment the generation number ++mGeneration;
--- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -1131,16 +1131,22 @@ protected: GLuint mStencilValueMaskFront, mStencilValueMaskBack, mStencilWriteMaskFront, mStencilWriteMaskBack; realGLboolean mColorWriteMask[4]; realGLboolean mDepthWriteMask; GLfloat mColorClearValue[4]; GLint mStencilClearValue; GLfloat mDepthClearValue; + GLint mViewportX; + GLint mViewportY; + GLsizei mViewportWidth; + GLsizei mViewportHeight; + bool mAlreadyWarnedAboutViewportLargerThanDest; + nsCOMPtr<nsITimer> mContextRestorer; bool mAllowRestore; bool mContextLossTimerRunning; bool mDrawSinceContextLossTimerSet; ContextStatus mContextStatus; bool mContextLostErrorSet; // Used for some hardware (particularly Tegra 2 and 4) that likes to
--- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -48,17 +48,17 @@ using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::gl; using namespace mozilla::gfx; static bool BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize); static GLenum InternalFormatForFormatAndType(GLenum format, GLenum type, bool isGLES2); -inline const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const { +const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const { return mBoundFramebuffer ? mBoundFramebuffer->RectangleObject() : static_cast<const WebGLRectangleObject*>(this); } WebGLContext::FakeBlackTexture::FakeBlackTexture(GLContext *gl, GLenum target, GLenum format) : mGL(gl) , mGLName(0) { @@ -3062,16 +3062,21 @@ WebGLContext::Viewport(GLint x, GLint y, if (IsContextLost()) return; if (width < 0 || height < 0) return ErrorInvalidValue("viewport: negative size"); MakeContextCurrent(); gl->fViewport(x, y, width, height); + + mViewportX = x; + mViewportY = y; + mViewportWidth = width; + mViewportHeight = height; } void WebGLContext::CompileShader(WebGLShader *shader) { if (IsContextLost()) return;
--- a/content/canvas/src/WebGLContextValidate.cpp +++ b/content/canvas/src/WebGLContextValidate.cpp @@ -485,19 +485,21 @@ uint32_t WebGLContext::GetBitsPerTexel(G int multiplier = type == LOCAL_GL_FLOAT ? 32 : 8; switch (format) { case LOCAL_GL_ALPHA: case LOCAL_GL_LUMINANCE: return 1 * multiplier; case LOCAL_GL_LUMINANCE_ALPHA: return 2 * multiplier; case LOCAL_GL_RGB: + case LOCAL_GL_RGB32F: case LOCAL_GL_SRGB_EXT: return 3 * multiplier; case LOCAL_GL_RGBA: + case LOCAL_GL_RGBA32F: case LOCAL_GL_SRGB_ALPHA_EXT: return 4 * multiplier; case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: return 2; case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case LOCAL_GL_ATC_RGB: @@ -514,17 +516,17 @@ uint32_t WebGLContext::GetBitsPerTexel(G } } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 || type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 || type == LOCAL_GL_UNSIGNED_SHORT_5_6_5) { return 16; } - MOZ_ASSERT(false); + MOZ_ASSERT(false, "Unhandled format+type combo."); return 0; } bool WebGLContext::ValidateTexFormatAndType(GLenum format, GLenum type, int jsArrayType, uint32_t *texelSize, const char *info) { if (IsExtensionEnabled(WEBGL_depth_texture)) { if (format == LOCAL_GL_DEPTH_COMPONENT) {
--- a/content/canvas/src/WebGLContextVertices.cpp +++ b/content/canvas/src/WebGLContextVertices.cpp @@ -728,16 +728,30 @@ void WebGLContext::Draw_cleanup() mDrawCallsSinceLastFlush++; if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) { gl->fFlush(); mDrawCallsSinceLastFlush = 0; } } } + + // Let's check the viewport + const WebGLRectangleObject* rect = FramebufferRectangleObject(); + if (rect) { + if (mViewportWidth > rect->Width() || + mViewportHeight > rect->Height()) + { + if (!mAlreadyWarnedAboutViewportLargerThanDest) { + GenerateWarning("Drawing to a destination rect smaller than the viewport rect. " + "(This warning will only be given once)"); + mAlreadyWarnedAboutViewportLargerThanDest = true; + } + } + } } /* * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount) * that will be legal to be read from bound VBOs. */ bool
--- a/content/html/content/src/HTMLMediaElement.cpp +++ b/content/html/content/src/HTMLMediaElement.cpp @@ -3672,22 +3672,22 @@ void HTMLMediaElement::FireTimeUpdate(bo } if (mFragmentEnd >= 0.0 && time >= mFragmentEnd) { Pause(); mFragmentEnd = -1.0; mFragmentStart = -1.0; mDecoder->SetFragmentEndTime(mFragmentEnd); } - // Update visible text tracks. + // Update the cues displaying on the video. // Here mTextTrackManager can be null if the cycle collector has unlinked // us before our parent. In that case UnbindFromTree will call us // when our parent is unlinked. if (mTextTrackManager) { - mTextTrackManager->Update(time); + mTextTrackManager->UpdateCueDisplay(); } } void HTMLMediaElement::GetCurrentSpec(nsCString& aString) { if (mLoadingSrc) { mLoadingSrc->GetSpec(aString); } else {
--- a/content/html/content/src/TextTrackManager.cpp +++ b/content/html/content/src/TextTrackManager.cpp @@ -3,33 +3,50 @@ /* 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 "mozilla/dom/TextTrackManager.h" #include "mozilla/dom/HTMLMediaElement.h" #include "mozilla/dom/HTMLTrackElement.h" +#include "mozilla/dom/TextTrack.h" +#include "mozilla/dom/TextTrackCue.h" +#include "mozilla/ClearOnShutdown.h" +#include "nsComponentManagerUtils.h" +#include "nsVideoFrame.h" +#include "nsIFrame.h" +#include "nsTArrayHelpers.h" +#include "nsIWebVTTParserWrapper.h" namespace mozilla { namespace dom { NS_IMPL_CYCLE_COLLECTION_3(TextTrackManager, mTextTracks, mPendingTextTracks, mNewCues) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(TextTrackManager, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(TextTrackManager, Release) +StaticRefPtr<nsIWebVTTParserWrapper> TextTrackManager::sParserWrapper; + TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement) : mMediaElement(aMediaElement) { MOZ_COUNT_CTOR(TextTrackManager); mNewCues = new TextTrackCueList(mMediaElement->OwnerDoc()->GetParentObject()); mTextTracks = new TextTrackList(mMediaElement->OwnerDoc()->GetParentObject()); mPendingTextTracks = new TextTrackList(mMediaElement->OwnerDoc()->GetParentObject()); + + if (!sParserWrapper) { + nsCOMPtr<nsIWebVTTParserWrapper> parserWrapper = + do_CreateInstance(NS_WEBVTTPARSERWRAPPER_CONTRACTID); + sParserWrapper = parserWrapper; + ClearOnShutdown(&sParserWrapper); + } } TextTrackManager::~TextTrackManager() { MOZ_COUNT_DTOR(TextTrackManager); } TextTrackList* @@ -81,19 +98,52 @@ TextTrackManager::RemoveTextTrack(TextTr void TextTrackManager::DidSeek() { mTextTracks->DidSeek(); } void -TextTrackManager::Update(double aTime) +TextTrackManager::UpdateCueDisplay() { - mTextTracks->Update(aTime); + nsIFrame* frame = mMediaElement->GetPrimaryFrame(); + if (!frame) { + return; + } + + nsVideoFrame* videoFrame = do_QueryFrame(frame); + if (!videoFrame) { + return; + } + + nsCOMPtr<nsIContent> overlay = videoFrame->GetCaptionOverlay(); + if (!overlay) { + return; + } + + nsTArray<nsRefPtr<TextTrackCue> > activeCues; + mTextTracks->GetAllActiveCues(activeCues); + + if (activeCues.Length() > 0) { + nsCOMPtr<nsIWritableVariant> jsCues = + do_CreateInstance("@mozilla.org/variant;1"); + + jsCues->SetAsArray(nsIDataType::VTYPE_INTERFACE, + &NS_GET_IID(nsIDOMEventTarget), + activeCues.Length(), + static_cast<void*>(activeCues.Elements())); + + nsPIDOMWindow* window = mMediaElement->OwnerDoc()->GetWindow(); + if (window) { + sParserWrapper->ProcessCues(window, jsCues, overlay); + } + } else if (overlay->nsINode::Length() > 0) { + nsContentUtils::SetNodeTextContent(overlay, EmptyString(), true); + } } void TextTrackManager::AddCue(TextTrackCue& aCue) { mNewCues->AddCue(aCue); }
--- a/content/html/content/src/TextTrackManager.h +++ b/content/html/content/src/TextTrackManager.h @@ -6,21 +6,26 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_TextTrackManager_h #define mozilla_dom_TextTrackManager_h #include "mozilla/dom/TextTrack.h" #include "mozilla/dom/TextTrackList.h" #include "mozilla/dom/TextTrackCueList.h" +#include "mozilla/StaticPtr.h" + +class nsIWebVTTParserWrapper; namespace mozilla { namespace dom { class HTMLMediaElement; +class TextTrack; +class TextTrackCue; class TextTrackManager { public: NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(TextTrackManager) NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(TextTrackManager); TextTrackManager(HTMLMediaElement *aMediaElement); @@ -32,32 +37,60 @@ public: const nsAString& aLanguage); void AddTextTrack(TextTrack* aTextTrack); void RemoveTextTrack(TextTrack* aTextTrack, bool aPendingListOnly); void DidSeek(); void AddCue(TextTrackCue& aCue); void AddCues(TextTrack* aTextTrack); - // Update the display of cues on the video as per the current play back time - // of aTime. - void Update(double aTime); + /** + * Overview of WebVTT cuetext and anonymous content setup. + * + * WebVTT nodes are the parsed version of WebVTT cuetext. WebVTT cuetext is + * the portion of a WebVTT cue that specifies what the caption will actually + * show up as on screen. + * + * WebVTT cuetext can contain markup that loosely relates to HTML markup. It + * can contain tags like <b>, <u>, <i>, <c>, <v>, <ruby>, <rt>, <lang>, + * including timestamp tags. + * + * When the caption is ready to be displayed the WebVTT nodes are converted + * over to anonymous DOM content. <i>, <u>, <b>, <ruby>, and <rt> all become + * HTMLElements of their corresponding HTML markup tags. <c> and <v> are + * converted to <span> tags. Timestamp tags are converted to XML processing + * instructions. Additionally, all cuetext tags support specifying of classes. + * This takes the form of <foo.class.subclass>. These classes are then parsed + * and set as the anonymous content's class attribute. + * + * Rules on constructing DOM objects from WebVTT nodes can be found here + * http://dev.w3.org/html5/webvtt/#webvtt-cue-text-dom-construction-rules. + * Current rules are taken from revision on April 15, 2013. + */ + + /** + * Converts the TextTrackCue's cuetext into a tree of DOM objects and attaches + * it to a div on it's owning TrackElement's MediaElement's caption overlay. + */ + void UpdateCueDisplay(); void PopulatePendingList(); private: // The HTMLMediaElement that this TextTrackManager manages the TextTracks of. // This is a weak reference as the life time of TextTrackManager is dependent // on the HTMLMediaElement, so it should not be trying to hold the // HTMLMediaElement alive. HTMLMediaElement* mMediaElement; // List of the TextTrackManager's owning HTMLMediaElement's TextTracks. nsRefPtr<TextTrackList> mTextTracks; // List of text track objects awaiting loading. nsRefPtr<TextTrackList> mPendingTextTracks; // List of newly introduced Text Track cues. nsRefPtr<TextTrackCueList> mNewCues; + + static StaticRefPtr<nsIWebVTTParserWrapper> sParserWrapper; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_TextTrackManager_h
--- a/content/media/TextTrack.cpp +++ b/content/media/TextTrack.cpp @@ -67,24 +67,16 @@ TextTrack::SetDefaultSettings() mCueList = new TextTrackCueList(mParent); mActiveCueList = new TextTrackCueList(mParent); mRegionList = new TextTrackRegionList(mParent); mCuePos = 0; mDirty = false; mReadyState = HTMLTrackElement::NONE; } -void -TextTrack::Update(double aTime) -{ - if (mCueList) { - mCueList->Update(aTime); - } -} - JSObject* TextTrack::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) { return TextTrackBinding::Wrap(aCx, aScope, this); } void TextTrack::SetMode(TextTrackMode aValue) @@ -137,21 +129,21 @@ TextTrack::RemoveRegion(const TextTrackR if (!mRegionList->GetRegionById(aRegion.Id())) { aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR); return; } mRegionList->RemoveTextTrackRegion(aRegion); } -TextTrackCueList* -TextTrack::GetActiveCues() +void +TextTrack::UpdateActiveCueList() { if (mMode == TextTrackMode::Disabled || !mMediaElement) { - return nullptr; + return; } // If we are dirty, i.e. an event happened that may cause the sorted mCueList // to have changed like a seek or an insert for a cue, than we need to rebuild // the active cue list from scratch. if (mDirty) { mCuePos = 0; mDirty = false; @@ -171,19 +163,31 @@ TextTrack::GetActiveCues() // We can stop iterating safely once we encounter a cue that does not have // a valid start time as the cue list is sorted. for (; mCuePos < mCueList->Length() && (*mCueList)[mCuePos]->StartTime() <= playbackTime; mCuePos++) { if ((*mCueList)[mCuePos]->EndTime() >= playbackTime) { mActiveCueList->AddCue(*(*mCueList)[mCuePos]); } } +} + +TextTrackCueList* +TextTrack::GetActiveCues() { + UpdateActiveCueList(); return mActiveCueList; } +void +TextTrack::GetActiveCueArray(nsTArray<nsRefPtr<TextTrackCue> >& aCues) +{ + UpdateActiveCueList(); + mActiveCueList->GetArray(aCues); +} + uint16_t TextTrack::ReadyState() const { return mReadyState; } void TextTrack::SetReadyState(uint16_t aState)
--- a/content/media/TextTrack.h +++ b/content/media/TextTrack.h @@ -78,42 +78,42 @@ public: { if (mMode == TextTrackMode::Disabled) { return nullptr; } return mCueList; } TextTrackCueList* GetActiveCues(); + void GetActiveCueArray(nsTArray<nsRefPtr<TextTrackCue> >& aCues); TextTrackRegionList* GetRegions() const { if (mMode != TextTrackMode::Disabled) { return mRegionList; } return nullptr; } uint16_t ReadyState() const; void SetReadyState(uint16_t aState); void AddRegion(TextTrackRegion& aRegion); void RemoveRegion(const TextTrackRegion& aRegion, ErrorResult& aRv); - // Time is in seconds. - void Update(double aTime); - void AddCue(TextTrackCue& aCue); void RemoveCue(TextTrackCue& aCue, ErrorResult& aRv); void CueChanged(TextTrackCue& aCue); void SetDirty() { mDirty = true; } IMPL_EVENT_HANDLER(cuechange) private: + void UpdateActiveCueList(); + nsCOMPtr<nsISupports> mParent; nsRefPtr<HTMLMediaElement> mMediaElement; TextTrackKind mKind; nsString mLabel; nsString mLanguage; nsString mType; nsString mId;
--- a/content/media/TextTrackCue.cpp +++ b/content/media/TextTrackCue.cpp @@ -1,17 +1,15 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 "mozilla/dom/HTMLTrackElement.h" #include "mozilla/dom/TextTrackCue.h" -#include "nsIFrame.h" -#include "nsVideoFrame.h" #include "nsComponentManagerUtils.h" #include "mozilla/ClearOnShutdown.h" // Alternate value for the 'auto' keyword. #define WEBVTT_AUTO -1 namespace mozilla { namespace dom { @@ -31,16 +29,17 @@ NS_INTERFACE_MAP_END_INHERITING(nsDOMEve StaticRefPtr<nsIWebVTTParserWrapper> TextTrackCue::sParserWrapper; // Set cue setting defaults based on step 19 & seq. // in http://dev.w3.org/html5/webvtt/#parsing void TextTrackCue::SetDefaultCueSettings() { mPosition = 50; + mPositionAlign = AlignSetting::Middle; mSize = 100; mPauseOnExit = false; mSnapToLines = true; mLine = WEBVTT_AUTO; mAlign = AlignSetting::Middle; mLineAlign = AlignSetting::Start; mVertical = DirectionSetting::_empty; } @@ -95,67 +94,16 @@ TextTrackCue::StashDocument(nsISupports* } mDocument = window->GetDoc(); if (!mDocument) { return NS_ERROR_NOT_AVAILABLE; } return NS_OK; } -void -TextTrackCue::CreateCueOverlay() -{ - mDocument->CreateElem(NS_LITERAL_STRING("div"), nullptr, - kNameSpaceID_XHTML, - getter_AddRefs(mDisplayState)); - nsGenericHTMLElement* cueDiv = - static_cast<nsGenericHTMLElement*>(mDisplayState.get()); - cueDiv->SetClassName(NS_LITERAL_STRING("caption-text")); -} - -void -TextTrackCue::RenderCue() -{ - nsRefPtr<DocumentFragment> frag = GetCueAsHTML(); - if (!frag || !mTrackElement) { - return; - } - - if (!mDisplayState) { - CreateCueOverlay(); - } - - HTMLMediaElement* parent = mTrackElement->mMediaParent; - if (!parent) { - return; - } - - nsIFrame* frame = parent->GetPrimaryFrame(); - if (!frame) { - return; - } - - nsVideoFrame* videoFrame = do_QueryFrame(frame); - if (!videoFrame) { - return; - } - - nsIContent* overlay = videoFrame->GetCaptionOverlay(); - if (!overlay) { - return; - } - - ErrorResult rv; - nsContentUtils::SetNodeTextContent(overlay, EmptyString(), true); - nsContentUtils::SetNodeTextContent(mDisplayState, EmptyString(), true); - - mDisplayState->AppendChild(*frag, rv); - overlay->AppendChild(*mDisplayState, rv); -} - already_AddRefed<DocumentFragment> TextTrackCue::GetCueAsHTML() { MOZ_ASSERT(mDocument); if (!sParserWrapper) { nsresult rv; nsCOMPtr<nsIWebVTTParserWrapper> parserWrapper =
--- a/content/media/TextTrackCue.h +++ b/content/media/TextTrackCue.h @@ -9,16 +9,17 @@ #include "mozilla/dom/DocumentFragment.h" #include "mozilla/dom/VTTCueBinding.h" #include "nsCycleCollectionParticipant.h" #include "nsDOMEventTargetHelper.h" #include "nsIWebVTTParserWrapper.h" #include "mozilla/StaticPtr.h" #include "nsIDocument.h" +#include "mozilla/dom/HTMLDivElement.h" namespace mozilla { namespace dom { class HTMLTrackElement; class TextTrack; class TextTrackCue MOZ_FINAL : public nsDOMEventTargetHelper @@ -215,16 +216,36 @@ public: return; } mReset = true; mPosition = aPosition; CueChanged(); } + AlignSetting PositionAlign() const + { + return mPositionAlign; + } + + void SetPositionAlign(AlignSetting aPositionAlign, ErrorResult& aRv) + { + if (mPositionAlign == aPositionAlign) + return; + + if (aPositionAlign == AlignSetting::Left || + aPositionAlign == AlignSetting::Right) { + return aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + } + + mReset = true; + mPositionAlign = aPositionAlign; + CueChanged(); + } + int32_t Size() const { return mSize; } void SetSize(int32_t aSize, ErrorResult& aRv) { if (mSize == aSize) { @@ -271,96 +292,81 @@ public: mReset = true; mText = aText; CueChanged(); } IMPL_EVENT_HANDLER(enter) IMPL_EVENT_HANDLER(exit) + HTMLDivElement* GetDisplayState() + { + return static_cast<HTMLDivElement*>(mDisplayState.get()); + } + + void SetDisplayState(HTMLDivElement* aDisplayState) + { + mDisplayState = aDisplayState; + } + + bool HasBeenReset() + { + return mReset; + } + // Helper functions for implementation. bool operator==(const TextTrackCue& rhs) const { return mId.Equals(rhs.mId); } const nsAString& Id() const { return mId; } /** - * Overview of WebVTT cuetext and anonymous content setup. - * - * WebVTT nodes are the parsed version of WebVTT cuetext. WebVTT cuetext is - * the portion of a WebVTT cue that specifies what the caption will actually - * show up as on screen. - * - * WebVTT cuetext can contain markup that loosely relates to HTML markup. It - * can contain tags like <b>, <u>, <i>, <c>, <v>, <ruby>, <rt>, <lang>, - * including timestamp tags. - * - * When the caption is ready to be displayed the WebVTT nodes are converted - * over to anonymous DOM content. <i>, <u>, <b>, <ruby>, and <rt> all become - * HTMLElements of their corresponding HTML markup tags. <c> and <v> are - * converted to <span> tags. Timestamp tags are converted to XML processing - * instructions. Additionally, all cuetext tags support specifying of classes. - * This takes the form of <foo.class.subclass>. These classes are then parsed - * and set as the anonymous content's class attribute. - * - * Rules on constructing DOM objects from WebVTT nodes can be found here - * http://dev.w3.org/html5/webvtt/#webvtt-cue-text-dom-construction-rules. - * Current rules are taken from revision on April 15, 2013. - */ - - /** - * Converts the TextTrackCue's cuetext into a tree of DOM objects and attaches - * it to a div on it's owning TrackElement's MediaElement's caption overlay. - */ - void RenderCue(); - - /** * Produces a tree of anonymous content based on the tree of the processed * cue text. * * Returns a DocumentFragment that is the head of the tree of anonymous * content. */ already_AddRefed<DocumentFragment> GetCueAsHTML(); void SetTrackElement(HTMLTrackElement* aTrackElement); private: void CueChanged(); void SetDefaultCueSettings(); - void CreateCueOverlay(); nsresult StashDocument(nsISupports* aGlobal); nsRefPtr<nsIDocument> mDocument; nsString mText; double mStartTime; double mEndTime; nsRefPtr<TextTrack> mTrack; nsRefPtr<HTMLTrackElement> mTrackElement; nsString mId; int32_t mPosition; + AlignSetting mPositionAlign; int32_t mSize; bool mPauseOnExit; bool mSnapToLines; nsString mRegionId; DirectionSetting mVertical; int mLine; AlignSetting mAlign; AlignSetting mLineAlign; // Holds the computed DOM elements that represent the parsed cue text. // http://www.whatwg.org/specs/web-apps/current-work/#text-track-cue-display-state - nsCOMPtr<nsIContent> mDisplayState; + nsRefPtr<nsGenericHTMLElement> mDisplayState; // Tells whether or not we need to recompute mDisplayState. This is set // anytime a property that relates to the display of the TextTrackCue is // changed. bool mReset; static StaticRefPtr<nsIWebVTTParserWrapper> sParserWrapper; };
--- a/content/media/TextTrackCueList.cpp +++ b/content/media/TextTrackCueList.cpp @@ -33,27 +33,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION( NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END TextTrackCueList::TextTrackCueList(nsISupports* aParent) : mParent(aParent) { SetIsDOMBinding(); } -void -TextTrackCueList::Update(double aTime) -{ - const uint32_t length = mList.Length(); - for (uint32_t i = 0; i < length; i++) { - if (aTime > mList[i]->StartTime() && aTime < mList[i]->EndTime()) { - mList[i]->RenderCue(); - } - } -} - JSObject* TextTrackCueList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) { return TextTrackCueListBinding::Wrap(aCx, aScope, this); } TextTrackCue* TextTrackCueList::IndexedGetter(uint32_t aIndex, bool& aFound) @@ -111,10 +100,17 @@ TextTrackCueList::RemoveCueAt(uint32_t a } void TextTrackCueList::RemoveAll() { mList.Clear(); } +void +TextTrackCueList::GetArray(nsTArray<nsRefPtr<TextTrackCue> >& aCues) +{ + aCues = nsTArray<nsRefPtr<TextTrackCue> >(mList); +} + + } // namespace dom } // namespace mozilla
--- a/content/media/TextTrackCueList.h +++ b/content/media/TextTrackCueList.h @@ -36,31 +36,29 @@ public: return mParent; } uint32_t Length() const { return mList.Length(); } - // Time is in seconds. - void Update(double aTime); - TextTrackCue* IndexedGetter(uint32_t aIndex, bool& aFound); TextTrackCue* operator[](uint32_t aIndex); TextTrackCue* GetCueById(const nsAString& aId); // Adds a cue to mList by performing an insertion sort on mList. // We expect most files to already be sorted, so an insertion sort starting // from the end of the current array should be more efficient than a general // sort step after all cues are loaded. void AddCue(TextTrackCue& aCue); void RemoveCue(TextTrackCue& aCue, ErrorResult& aRv); void RemoveCueAt(uint32_t aIndex); void RemoveAll(); + void GetArray(nsTArray<nsRefPtr<TextTrackCue> >& aCues); private: nsCOMPtr<nsISupports> mParent; // A sorted list of TextTrackCues sorted by earliest start time. If the start // times are equal then it will be sorted by end time, earliest first. nsTArray< nsRefPtr<TextTrackCue> > mList; };
--- a/content/media/TextTrackList.cpp +++ b/content/media/TextTrackList.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 "mozilla/dom/TextTrackList.h" #include "mozilla/dom/TextTrackListBinding.h" #include "mozilla/dom/TrackEvent.h" #include "nsThreadUtils.h" +#include "mozilla/dom/TextTrackCue.h" namespace mozilla { namespace dom { NS_IMPL_CYCLE_COLLECTION_INHERITED_2(TextTrackList, nsDOMEventTargetHelper, mGlobal, mTextTracks) @@ -22,21 +23,24 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_ NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper) TextTrackList::TextTrackList(nsISupports* aGlobal) : mGlobal(aGlobal) { SetIsDOMBinding(); } void -TextTrackList::Update(double aTime) +TextTrackList::GetAllActiveCues(nsTArray<nsRefPtr<TextTrackCue> >& aCues) { - uint32_t length = Length(), i; - for (i = 0; i < length; i++) { - mTextTracks[i]->Update(aTime); + nsTArray< nsRefPtr<TextTrackCue> > cues; + for (uint32_t i = 0; i < Length(); i++) { + if (mTextTracks[i]->Mode() != TextTrackMode::Disabled) { + mTextTracks[i]->GetActiveCueArray(cues); + aCues.AppendElements(cues); + } } } JSObject* TextTrackList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) { return TextTrackListBinding::Wrap(aCx, aScope, this); }
--- a/content/media/TextTrackList.h +++ b/content/media/TextTrackList.h @@ -33,18 +33,18 @@ public: return mGlobal; } uint32_t Length() const { return mTextTracks.Length(); } - // Time is in seconds. - void Update(double aTime); + // Get all the current active cues. + void GetAllActiveCues(nsTArray<nsRefPtr<TextTrackCue> >& aCues); TextTrack* IndexedGetter(uint32_t aIndex, bool& aFound); already_AddRefed<TextTrack> AddTextTrack(HTMLMediaElement* aMediaElement, TextTrackKind aKind, const nsAString& aLabel, const nsAString& aLanguage); TextTrack* GetTrackById(const nsAString& aId);
--- a/content/media/test/mochitest.ini +++ b/content/media/test/mochitest.ini @@ -234,16 +234,17 @@ support-files = [test_source.html] [test_source_write.html] [test_source_null.html] [test_standalone.html] [test_volume.html] [test_video_to_canvas.html] [test_audiowrite.html] [test_mediarecorder_creation.html] +[test_mediarecorder_creation_fail.html] [test_mediarecorder_avoid_recursion.html] [test_mediarecorder_record_timeslice.html] [test_mediarecorder_record_audiocontext.html] [test_mediarecorder_record_4ch_audiocontext.html] [test_mediarecorder_record_stopms.html] [test_mediarecorder_record_nosrc.html] [test_mozHasAudio.html] [test_source_media.html]
new file mode 100644 --- /dev/null +++ b/content/media/test/test_mediarecorder_creation_fail.html @@ -0,0 +1,74 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test MediaRecorder Record with media.ogg.enabled = false</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> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +function startTest() { + var element = document.createElement('audio'); + + element.src = 'detodos.opus'; + element.stream = element.mozCaptureStream(); + // the expect sequence should be + // 1. onerror + // 2. ondataavailable + // 3. onstop + var callbackStep = 0; + var mediaRecorder = new MediaRecorder(element.stream); + + mediaRecorder.onerror = function (e) { + is(callbackStep, 0, 'should fired onstop callback'); + is(e.name, 'GenericError', 'error name should be GenericError'); + is(mediaRecorder.mimeType, '', 'mimetype should be empty'); + is(mediaRecorder.state, 'recording', 'state is recording'); + info('onerror callback fired'); + SpecialPowers.setBoolPref('media.ogg.enabled', true); + callbackStep = 1; + }; + + mediaRecorder.onwarning = function () { + ok(false, 'Unexpected onwarning callback fired'); + }; + + mediaRecorder.onstop = function () { + info('onstop callback fired'); + is(mediaRecorder.state, 'inactive', 'state should be inactive'); + is(callbackStep, 2, 'should fired onstop callback'); + SimpleTest.finish(); + }; + + // This handler fires every 250ms to generate a blob. + mediaRecorder.ondataavailable = function (evt) { + info('ondataavailable callback fired'); + is(callbackStep, 1, 'should fired ondataavailable callback'); + is(evt.data.size, 0, 'data size should be zero'); + ok(evt instanceof BlobEvent, + 'Events fired from ondataavailable should be BlobEvent'); + is(evt.data.type, '', 'encoder start fail, blob miemType should be empty'); + callbackStep = 2; + }; + + // Start recording once canplaythrough fires + element.oncanplaythrough = function() { + SpecialPowers.setBoolPref("media.ogg.enabled", false); + mediaRecorder.start(250); + is(mediaRecorder.state, 'recording', 'Media recorder should be recording'); + is(mediaRecorder.stream, element.stream, + 'Media recorder stream = element stream at the start of recording'); + }; + + element.play(); +} + +startTest(); +SimpleTest.waitForExplicitFinish(); +</script> +</pre> +</body> +</html>
--- a/content/media/test/test_texttrackcue.html +++ b/content/media/test/test_texttrackcue.html @@ -99,16 +99,42 @@ SpecialPowers.pushPrefEnv({"set": [["med cue.lineAlign = "middle"; is(cue.lineAlign, "middle", "Cue's line align should be middle."); cue.lineAlign = "START"; is(cue.lineAlign, "middle", "Cue's line align should be middle."); cue.lineAlign = "end"; is(cue.lineAlign, "end", "Cue's line align should be end."); + // Check that cue position align works properly + is(cue.positionAlign, "middle", "Cue's default position alignment should be middle."); + + var exceptionHappened = false; + try { + cue.positionAlign = "left"; + } catch(e) { + exceptionHappened = true; + is(e.name, "SyntaxError", "Should have thrown SyntaxError."); + } + ok(exceptionHappened, "Exception should have happened."); + + exceptionHappened = false; + try { + cue.positionAlign = "right"; + } catch(e) { + exceptionHappened = true; + is(e.name, "SyntaxError", "Should have thrown SyntaxError."); + } + ok(exceptionHappened, "Exception should have happened."); + + cue.positionAlign = "start"; + is(cue.positionAlign, "start", "Cue's position align should be start."); + cue.positionAlign = "end"; + is(cue.positionAlign, "end", "Cue's position align should be end."); + // Check that we can create and add new VTTCues var vttCue = new VTTCue(3.999, 4, "foo"); trackElement.track.addCue(vttCue); is(cueList.length, 7, "Cue list length should now be 7."); // Check that new VTTCue was added correctly cue = cueList[6]; is(cue.startTime, 3.999, "Cue's start time should be 3.999.");
--- a/content/media/webaudio/OscillatorNode.cpp +++ b/content/media/webaudio/OscillatorNode.cpp @@ -18,17 +18,18 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED_3(Osc mPeriodicWave, mFrequency, mDetune) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OscillatorNode) NS_INTERFACE_MAP_END_INHERITING(AudioNode) NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioNode) NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioNode) -static const float sLeak = 0.995f; +static const float sLeakTriangle = 0.995f; +static const float sLeak = 0.999f; class DCBlocker { public: // These are sane defauts when the initial mPhase is zero DCBlocker(float aLastInput = 0.0f, float aLastOutput = 0.0f, float aPole = 0.995) @@ -315,34 +316,34 @@ public: } void ComputeSquare(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd) { for (uint32_t i = aStart; i < aEnd; ++i) { UpdateParametersIfNeeded(ticks, i); // Integration to get us a square. It turns out we can have a // pure integrator here. - mSquare += BipolarBLIT(); + mSquare = mSquare * sLeak + BipolarBLIT(); aOutput[i] = mSquare; // maybe we want to apply a gain, the wg has not decided yet aOutput[i] *= 1.5; IncrementPhase(); } } void ComputeSawtooth(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd) { float dcoffset; for (uint32_t i = aStart; i < aEnd; ++i) { UpdateParametersIfNeeded(ticks, i); // DC offset so the Saw does not ramp up to infinity when integrating. dcoffset = mFinalFrequency / mSource->SampleRate(); // Integrate and offset so we get mAmplitudeAtZero sawtooth. We have a // very low frequency component somewhere here, but I'm not sure where. - mSaw += UnipolarBLIT() - dcoffset; + mSaw = mSaw * sLeak + (UnipolarBLIT() - dcoffset); // reverse the saw so we are spec compliant aOutput[i] = -mSaw * 1.5; IncrementPhase(); } } void ComputeTriangle(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd) @@ -351,17 +352,17 @@ public: UpdateParametersIfNeeded(ticks, i); // Integrate to get a square mSquare += BipolarBLIT(); // Leaky integrate to get a triangle. We get too much dc offset if we don't // leaky integrate here. // C6 = k0 / period // (period is samplingrate / frequency, k0 = (PI/2)/(2*PI)) = 0.25 float C6 = 0.25 / (mSource->SampleRate() / mFinalFrequency); - mTriangle = mTriangle * sLeak + mSquare + C6; + mTriangle = mTriangle * sLeakTriangle + mSquare + C6; // DC Block, and scale back to [-1.0; 1.0] aOutput[i] = mDCBlocker.Process(mTriangle) / (mSignalPeriod/2) * 1.5; IncrementPhase(); } } void ComputeCustom(float* aOutput,
--- a/content/media/webvtt/WebVTTParserWrapper.js +++ b/content/media/webvtt/WebVTTParserWrapper.js @@ -45,16 +45,21 @@ WebVTTParserWrapper.prototype = this.parser.onregion = callback.onRegion; }, convertCueToDOMTree: function(window, cue) { return WebVTTParser.convertCueToDOMTree(window, cue.text); }, + processCues: function(window, cues, overlay) + { + WebVTTParser.processCues(window, cues, null, overlay); + }, + classDescription: "Wrapper for the JS WebVTTParser (vtt.js)", classID: Components.ID(WEBVTTPARSERWRAPPER_CID), QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebVTTParserWrapper]), classInfo: XPCOMUtils.generateCI({ classID: WEBVTTPARSERWRAPPER_CID, contractID: WEBVTTPARSERWRAPPER_CONTRACTID, interfaces: [Ci.nsIWebVTTParserWrapper] })
--- a/content/media/webvtt/nsIWebVTTParserWrapper.idl +++ b/content/media/webvtt/nsIWebVTTParserWrapper.idl @@ -2,16 +2,17 @@ * 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 "nsISupports.idl" interface nsIDOMHTMLElement; interface nsIWebVTTListener; interface nsIDOMWindow; +interface nsIVariant; /** * Interface for a wrapper of a JS WebVTT parser (vtt.js). */ [scriptable, uuid(1604a67f-3b72-4027-bcba-6dddd5be6b10)] interface nsIWebVTTParserWrapper : nsISupports { /** @@ -56,13 +57,31 @@ interface nsIWebVTTParserWrapper : nsISu * * @param window A window object with which the document fragment will be * created. * @param cue The cue whose content will be converted to a document * fragment. */ nsIDOMHTMLElement convertCueToDOMTree(in nsIDOMWindow window, in nsISupports cue); + + + /** + * Compute the display state of the VTTCues in cues along with any VTTRegions + * that they might be in. First, it computes the positioning and styling of + * the cues and regions passed and converts them into a DOM tree rooted at + * a containing HTMLDivElement. It then adjusts those computed divs for + * overlap avoidance using the dimensions of 'overlay'. Finally, it adds the + * computed divs to the VTTCues display state property for use later. + * + * @param window A window object with which it will create the DOM tree + * and containing div element. + * @param cues An array of VTTCues who need there display state to be + * computed. + * @param overlay The HTMLElement that the cues will be displayed within. + */ + void processCues(in nsIDOMWindow window, in nsIVariant cues, + in nsISupports overlay); }; %{C++ #define NS_WEBVTTPARSERWRAPPER_CONTRACTID "@mozilla.org/webvttParserWrapper;1" %}
--- a/content/media/webvtt/vtt.jsm +++ b/content/media/webvtt/vtt.jsm @@ -3,31 +3,38 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ this.EXPORTED_SYMBOLS = ["WebVTTParser"]; /** * Code below is vtt.js the JS WebVTTParser. * Current source code can be found at http://github.com/andreasgal/vtt.js * - * Code taken from commit 355f375b6cf04763dcb1843d5683a7c489846425 + * Code taken from commit 33a837b1ceef138a61a3b2f6fac90d5c70bd90d9 */ +(function(global) { -(function(global) { + function ParsingError(message) { + this.name = "ParsingError"; + this.message = message || ""; + } + ParsingError.prototype = Object.create(Error.prototype); + ParsingError.prototype.constructor = ParsingError; // Try to parse input as a time stamp. function parseTimeStamp(input) { function computeSeconds(h, m, s, f) { return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000; } var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/); - if (!m) + if (!m) { return null; + } if (m[3]) { // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds] return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]); } else if (m[1] > 59) { // Timestamp takes the form of [hours]:[minutes].[milliseconds] // First position is hours as it's over 59. return computeSeconds(m[1], m[2], 0, m[4]); @@ -41,46 +48,49 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"] // assignment to a specific key. function Settings() { this.values = Object.create(null); } Settings.prototype = { // Only accept the first assignment to any key. set: function(k, v) { - if (!this.get(k) && v !== "") + if (!this.get(k) && v !== "") { this.values[k] = v; + } }, // Return the value for a key, or a default value. - get: function(k, dflt) { + // If 'defaultKey' is passed then 'dflt' is assumed to be an object with + // a number of possible default values as properties where 'defaultKey' is + // the key of the property that will be chosen; otherwise it's assumed to be + // a single value. + get: function(k, dflt, defaultKey) { + if (defaultKey) { + return this.has(k) ? this.values[k] : dflt[defaultKey]; + } return this.has(k) ? this.values[k] : dflt; }, // Check whether we have a value for a key. has: function(k) { return k in this.values; }, // Accept a setting if its one of the given alternatives. alt: function(k, v, a) { for (var n = 0; n < a.length; ++n) { if (v === a[n]) { this.set(k, v); break; } } }, - // Accept a region if it doesn't have the special string '-->' - region: function(k, v) { - if (!v.match(/-->/)) { - this.set(k, v); - } - }, // Accept a setting if its a valid (signed) integer. integer: function(k, v) { - if (/^-?\d+$/.test(v)) // integer + if (/^-?\d+$/.test(v)) { // integer this.set(k, parseInt(v, 10)); + } }, // Accept a setting if its a valid percentage. percent: function(k, v, frac) { var m; if ((m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/))) { v = v.replace("%", ""); if (!m[2] || (m[2] && frac)) { v = parseFloat(v); @@ -94,83 +104,114 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"] } }; // Helper function to parse input into groups separated by 'groupDelim', and // interprete each group as a key/value pair separated by 'keyValueDelim'. function parseOptions(input, callback, keyValueDelim, groupDelim) { var groups = groupDelim ? input.split(groupDelim) : [input]; for (var i in groups) { + if (typeof groups[i] !== "string") { + continue; + } var kv = groups[i].split(keyValueDelim); - if (kv.length !== 2) + if (kv.length !== 2) { continue; + } var k = kv[0]; var v = kv[1]; callback(k, v); } } function parseCue(input, cue) { // 4.1 WebVTT timestamp function consumeTimeStamp() { var ts = parseTimeStamp(input); - if (ts === null) - throw "error"; + if (ts === null) { + throw new ParsingError("Malformed time stamp."); + } // Remove time stamp from input. input = input.replace(/^[^\s-]+/, ""); return ts; } // 4.4.2 WebVTT cue settings function consumeCueSettings(input, cue) { var settings = new Settings(); parseOptions(input, function (k, v) { switch (k) { case "region": - settings.region(k, v); + settings.set(k, v); break; case "vertical": settings.alt(k, v, ["rl", "lr"]); break; case "line": - settings.integer(k, v); - settings.percent(k, v) ? settings.set("snapToLines", false) : null; - settings.alt(k, v, ["auto"]); + var vals = v.split(","), + vals0 = vals[0]; + settings.integer(k, vals0); + settings.percent(k, vals0) ? settings.set("snapToLines", false) : null; + settings.alt(k, vals0, ["auto"]); + if (vals.length === 2) { + settings.alt("lineAlign", vals[1], ["start", "middle", "end"]); + } break; case "position": + vals = v.split(","); + settings.percent(k, vals[0]); + if (vals.length === 2) { + settings.alt("positionAlign", vals[1], ["start", "middle", "end"]); + } + break; case "size": settings.percent(k, v); break; case "align": settings.alt(k, v, ["start", "middle", "end", "left", "right"]); break; } }, /:/, /\s/); // Apply default values for any missing fields. cue.regionId = settings.get("region", ""); cue.vertical = settings.get("vertical", ""); cue.line = settings.get("line", "auto"); + cue.lineAlign = settings.get("lineAlign", "start"); cue.snapToLines = settings.get("snapToLines", true); - cue.position = settings.get("position", 50); cue.size = settings.get("size", 100); cue.align = settings.get("align", "middle"); + cue.position = settings.get("position", { + start: 0, + left: 0, + middle: 50, + end: 100, + right: 100 + }, cue.align); + cue.positionAlign = settings.get("positionAlign", { + start: "start", + left: "start", + middle: "middle", + end: "end", + right: "end" + }, cue.align); } function skipWhitespace() { input = input.replace(/^\s+/, ""); } // 4.1 WebVTT cue timings. skipWhitespace(); cue.startTime = consumeTimeStamp(); // (1) collect cue start time skipWhitespace(); - if (input.substr(0, 3) !== "-->") // (3) next characters must match "-->" - throw "error"; + if (input.substr(0, 3) !== "-->") { // (3) next characters must match "-->" + throw new ParsingError("Malformed time stamp (time stamps must be separated by '-->')."); + } input = input.substr(3); skipWhitespace(); cue.endTime = consumeTimeStamp(); // (5) collect cue end time // 4.1 WebVTT cue settings list. skipWhitespace(); consumeCueSettings(input, cue); } @@ -203,18 +244,19 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"] const NEEDS_PARENT = { rt: "ruby" }; // Parse content into a document fragment. function parseContent(window, input) { function nextToken() { // Check for end-of-string. - if (!input) + if (!input) { return null; + } // Consume 'n' characters from the input. function consume(result) { input = input.substr(result.length); return result; } var m = input.match(/^([^<]*)(<[^>]+>?)?/); @@ -223,36 +265,39 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"] return consume(m[1] ? m[1] : m[2]); } // Unescape a string 's'. function unescape1(e) { return ESCAPE[e]; } function unescape(s) { - while ((m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/))) + while ((m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/))) { s = s.replace(m[0], unescape1); + } return s; } function shouldAdd(current, element) { return !NEEDS_PARENT[element.localName] || NEEDS_PARENT[element.localName] === current.localName; } // Create an element for this tag. function createElement(type, annotation) { var tagName = TAG_NAME[type]; - if (!tagName) + if (!tagName) { return null; + } var element = window.document.createElement(tagName); element.localName = tagName; var name = TAG_ANNOTATION[type]; - if (name && annotation) + if (name && annotation) { element[name] = annotation.trim(); + } return element; } var rootDiv = window.document.createElement("div"), current = rootDiv, t, tagStack = []; @@ -267,261 +312,667 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"] } // Otherwise just ignore the end tag. continue; } var ts = parseTimeStamp(t.substr(1, t.length - 2)); var node; if (ts) { // Timestamps are lead nodes as well. - node = window.ProcessingInstruction(); - node.target = "timestamp"; - node.data = ts; + node = window.document.createProcessingInstruction("timestamp", ts); current.appendChild(node); continue; } var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/); // If we can't parse the tag, skip to the next tag. - if (!m) + if (!m) { continue; + } // Try to construct an element, and ignore the tag if we couldn't. node = createElement(m[1], m[3]); - if (!node) + if (!node) { continue; + } // Determine if the tag should be added based on the context of where it // is placed in the cuetext. - if (!shouldAdd(current, node)) + if (!shouldAdd(current, node)) { continue; + } // Set the class list (as a list of classes, separated by space). - if (m[2]) + if (m[2]) { node.className = m[2].substr(1).replace('.', ' '); + } // Append the node to the current node, and enter the scope of the new // node. tagStack.push(m[1]); current.appendChild(node); current = node; continue; } // Text nodes are leaf nodes. current.appendChild(window.document.createTextNode(unescape(t))); } return rootDiv; } + // This is a list of all the Unicode characters that have a strong + // right-to-left category. What this means is that these characters are + // written right-to-left for sure. It was generated by pulling all the strong + // right-to-left characters out of the Unicode data table. That table can + // found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt + var strongRTLChars = [0x05BE, 0x05C0, 0x05C3, 0x05C6, 0x05D0, 0x05D1, + 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, + 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3, + 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x05F0, 0x05F1, + 0x05F2, 0x05F3, 0x05F4, 0x0608, 0x060B, 0x060D, 0x061B, 0x061E, 0x061F, + 0x0620, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, + 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, + 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, + 0x063B, 0x063C, 0x063D, 0x063E, 0x063F, 0x0640, 0x0641, 0x0642, 0x0643, + 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x066D, 0x066E, + 0x066F, 0x0671, 0x0672, 0x0673, 0x0674, 0x0675, 0x0676, 0x0677, 0x0678, + 0x0679, 0x067A, 0x067B, 0x067C, 0x067D, 0x067E, 0x067F, 0x0680, 0x0681, + 0x0682, 0x0683, 0x0684, 0x0685, 0x0686, 0x0687, 0x0688, 0x0689, 0x068A, + 0x068B, 0x068C, 0x068D, 0x068E, 0x068F, 0x0690, 0x0691, 0x0692, 0x0693, + 0x0694, 0x0695, 0x0696, 0x0697, 0x0698, 0x0699, 0x069A, 0x069B, 0x069C, + 0x069D, 0x069E, 0x069F, 0x06A0, 0x06A1, 0x06A2, 0x06A3, 0x06A4, 0x06A5, + 0x06A6, 0x06A7, 0x06A8, 0x06A9, 0x06AA, 0x06AB, 0x06AC, 0x06AD, 0x06AE, + 0x06AF, 0x06B0, 0x06B1, 0x06B2, 0x06B3, 0x06B4, 0x06B5, 0x06B6, 0x06B7, + 0x06B8, 0x06B9, 0x06BA, 0x06BB, 0x06BC, 0x06BD, 0x06BE, 0x06BF, 0x06C0, + 0x06C1, 0x06C2, 0x06C3, 0x06C4, 0x06C5, 0x06C6, 0x06C7, 0x06C8, 0x06C9, + 0x06CA, 0x06CB, 0x06CC, 0x06CD, 0x06CE, 0x06CF, 0x06D0, 0x06D1, 0x06D2, + 0x06D3, 0x06D4, 0x06D5, 0x06E5, 0x06E6, 0x06EE, 0x06EF, 0x06FA, 0x06FB, + 0x06FC, 0x06FD, 0x06FE, 0x06FF, 0x0700, 0x0701, 0x0702, 0x0703, 0x0704, + 0x0705, 0x0706, 0x0707, 0x0708, 0x0709, 0x070A, 0x070B, 0x070C, 0x070D, + 0x070F, 0x0710, 0x0712, 0x0713, 0x0714, 0x0715, 0x0716, 0x0717, 0x0718, + 0x0719, 0x071A, 0x071B, 0x071C, 0x071D, 0x071E, 0x071F, 0x0720, 0x0721, + 0x0722, 0x0723, 0x0724, 0x0725, 0x0726, 0x0727, 0x0728, 0x0729, 0x072A, + 0x072B, 0x072C, 0x072D, 0x072E, 0x072F, 0x074D, 0x074E, 0x074F, 0x0750, + 0x0751, 0x0752, 0x0753, 0x0754, 0x0755, 0x0756, 0x0757, 0x0758, 0x0759, + 0x075A, 0x075B, 0x075C, 0x075D, 0x075E, 0x075F, 0x0760, 0x0761, 0x0762, + 0x0763, 0x0764, 0x0765, 0x0766, 0x0767, 0x0768, 0x0769, 0x076A, 0x076B, + 0x076C, 0x076D, 0x076E, 0x076F, 0x0770, 0x0771, 0x0772, 0x0773, 0x0774, + 0x0775, 0x0776, 0x0777, 0x0778, 0x0779, 0x077A, 0x077B, 0x077C, 0x077D, + 0x077E, 0x077F, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0786, + 0x0787, 0x0788, 0x0789, 0x078A, 0x078B, 0x078C, 0x078D, 0x078E, 0x078F, + 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0796, 0x0797, 0x0798, + 0x0799, 0x079A, 0x079B, 0x079C, 0x079D, 0x079E, 0x079F, 0x07A0, 0x07A1, + 0x07A2, 0x07A3, 0x07A4, 0x07A5, 0x07B1, 0x07C0, 0x07C1, 0x07C2, 0x07C3, + 0x07C4, 0x07C5, 0x07C6, 0x07C7, 0x07C8, 0x07C9, 0x07CA, 0x07CB, 0x07CC, + 0x07CD, 0x07CE, 0x07CF, 0x07D0, 0x07D1, 0x07D2, 0x07D3, 0x07D4, 0x07D5, + 0x07D6, 0x07D7, 0x07D8, 0x07D9, 0x07DA, 0x07DB, 0x07DC, 0x07DD, 0x07DE, + 0x07DF, 0x07E0, 0x07E1, 0x07E2, 0x07E3, 0x07E4, 0x07E5, 0x07E6, 0x07E7, + 0x07E8, 0x07E9, 0x07EA, 0x07F4, 0x07F5, 0x07FA, 0x0800, 0x0801, 0x0802, + 0x0803, 0x0804, 0x0805, 0x0806, 0x0807, 0x0808, 0x0809, 0x080A, 0x080B, + 0x080C, 0x080D, 0x080E, 0x080F, 0x0810, 0x0811, 0x0812, 0x0813, 0x0814, + 0x0815, 0x081A, 0x0824, 0x0828, 0x0830, 0x0831, 0x0832, 0x0833, 0x0834, + 0x0835, 0x0836, 0x0837, 0x0838, 0x0839, 0x083A, 0x083B, 0x083C, 0x083D, + 0x083E, 0x0840, 0x0841, 0x0842, 0x0843, 0x0844, 0x0845, 0x0846, 0x0847, + 0x0848, 0x0849, 0x084A, 0x084B, 0x084C, 0x084D, 0x084E, 0x084F, 0x0850, + 0x0851, 0x0852, 0x0853, 0x0854, 0x0855, 0x0856, 0x0857, 0x0858, 0x085E, + 0x08A0, 0x08A2, 0x08A3, 0x08A4, 0x08A5, 0x08A6, 0x08A7, 0x08A8, 0x08A9, + 0x08AA, 0x08AB, 0x08AC, 0x200F, 0xFB1D, 0xFB1F, 0xFB20, 0xFB21, 0xFB22, + 0xFB23, 0xFB24, 0xFB25, 0xFB26, 0xFB27, 0xFB28, 0xFB2A, 0xFB2B, 0xFB2C, + 0xFB2D, 0xFB2E, 0xFB2F, 0xFB30, 0xFB31, 0xFB32, 0xFB33, 0xFB34, 0xFB35, + 0xFB36, 0xFB38, 0xFB39, 0xFB3A, 0xFB3B, 0xFB3C, 0xFB3E, 0xFB40, 0xFB41, + 0xFB43, 0xFB44, 0xFB46, 0xFB47, 0xFB48, 0xFB49, 0xFB4A, 0xFB4B, 0xFB4C, + 0xFB4D, 0xFB4E, 0xFB4F, 0xFB50, 0xFB51, 0xFB52, 0xFB53, 0xFB54, 0xFB55, + 0xFB56, 0xFB57, 0xFB58, 0xFB59, 0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D, 0xFB5E, + 0xFB5F, 0xFB60, 0xFB61, 0xFB62, 0xFB63, 0xFB64, 0xFB65, 0xFB66, 0xFB67, + 0xFB68, 0xFB69, 0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D, 0xFB6E, 0xFB6F, 0xFB70, + 0xFB71, 0xFB72, 0xFB73, 0xFB74, 0xFB75, 0xFB76, 0xFB77, 0xFB78, 0xFB79, + 0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D, 0xFB7E, 0xFB7F, 0xFB80, 0xFB81, 0xFB82, + 0xFB83, 0xFB84, 0xFB85, 0xFB86, 0xFB87, 0xFB88, 0xFB89, 0xFB8A, 0xFB8B, + 0xFB8C, 0xFB8D, 0xFB8E, 0xFB8F, 0xFB90, 0xFB91, 0xFB92, 0xFB93, 0xFB94, + 0xFB95, 0xFB96, 0xFB97, 0xFB98, 0xFB99, 0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D, + 0xFB9E, 0xFB9F, 0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3, 0xFBA4, 0xFBA5, 0xFBA6, + 0xFBA7, 0xFBA8, 0xFBA9, 0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD, 0xFBAE, 0xFBAF, + 0xFBB0, 0xFBB1, 0xFBB2, 0xFBB3, 0xFBB4, 0xFBB5, 0xFBB6, 0xFBB7, 0xFBB8, + 0xFBB9, 0xFBBA, 0xFBBB, 0xFBBC, 0xFBBD, 0xFBBE, 0xFBBF, 0xFBC0, 0xFBC1, + 0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6, 0xFBD7, 0xFBD8, 0xFBD9, 0xFBDA, 0xFBDB, + 0xFBDC, 0xFBDD, 0xFBDE, 0xFBDF, 0xFBE0, 0xFBE1, 0xFBE2, 0xFBE3, 0xFBE4, + 0xFBE5, 0xFBE6, 0xFBE7, 0xFBE8, 0xFBE9, 0xFBEA, 0xFBEB, 0xFBEC, 0xFBED, + 0xFBEE, 0xFBEF, 0xFBF0, 0xFBF1, 0xFBF2, 0xFBF3, 0xFBF4, 0xFBF5, 0xFBF6, + 0xFBF7, 0xFBF8, 0xFBF9, 0xFBFA, 0xFBFB, 0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF, + 0xFC00, 0xFC01, 0xFC02, 0xFC03, 0xFC04, 0xFC05, 0xFC06, 0xFC07, 0xFC08, + 0xFC09, 0xFC0A, 0xFC0B, 0xFC0C, 0xFC0D, 0xFC0E, 0xFC0F, 0xFC10, 0xFC11, + 0xFC12, 0xFC13, 0xFC14, 0xFC15, 0xFC16, 0xFC17, 0xFC18, 0xFC19, 0xFC1A, + 0xFC1B, 0xFC1C, 0xFC1D, 0xFC1E, 0xFC1F, 0xFC20, 0xFC21, 0xFC22, 0xFC23, + 0xFC24, 0xFC25, 0xFC26, 0xFC27, 0xFC28, 0xFC29, 0xFC2A, 0xFC2B, 0xFC2C, + 0xFC2D, 0xFC2E, 0xFC2F, 0xFC30, 0xFC31, 0xFC32, 0xFC33, 0xFC34, 0xFC35, + 0xFC36, 0xFC37, 0xFC38, 0xFC39, 0xFC3A, 0xFC3B, 0xFC3C, 0xFC3D, 0xFC3E, + 0xFC3F, 0xFC40, 0xFC41, 0xFC42, 0xFC43, 0xFC44, 0xFC45, 0xFC46, 0xFC47, + 0xFC48, 0xFC49, 0xFC4A, 0xFC4B, 0xFC4C, 0xFC4D, 0xFC4E, 0xFC4F, 0xFC50, + 0xFC51, 0xFC52, 0xFC53, 0xFC54, 0xFC55, 0xFC56, 0xFC57, 0xFC58, 0xFC59, + 0xFC5A, 0xFC5B, 0xFC5C, 0xFC5D, 0xFC5E, 0xFC5F, 0xFC60, 0xFC61, 0xFC62, + 0xFC63, 0xFC64, 0xFC65, 0xFC66, 0xFC67, 0xFC68, 0xFC69, 0xFC6A, 0xFC6B, + 0xFC6C, 0xFC6D, 0xFC6E, 0xFC6F, 0xFC70, 0xFC71, 0xFC72, 0xFC73, 0xFC74, + 0xFC75, 0xFC76, 0xFC77, 0xFC78, 0xFC79, 0xFC7A, 0xFC7B, 0xFC7C, 0xFC7D, + 0xFC7E, 0xFC7F, 0xFC80, 0xFC81, 0xFC82, 0xFC83, 0xFC84, 0xFC85, 0xFC86, + 0xFC87, 0xFC88, 0xFC89, 0xFC8A, 0xFC8B, 0xFC8C, 0xFC8D, 0xFC8E, 0xFC8F, + 0xFC90, 0xFC91, 0xFC92, 0xFC93, 0xFC94, 0xFC95, 0xFC96, 0xFC97, 0xFC98, + 0xFC99, 0xFC9A, 0xFC9B, 0xFC9C, 0xFC9D, 0xFC9E, 0xFC9F, 0xFCA0, 0xFCA1, + 0xFCA2, 0xFCA3, 0xFCA4, 0xFCA5, 0xFCA6, 0xFCA7, 0xFCA8, 0xFCA9, 0xFCAA, + 0xFCAB, 0xFCAC, 0xFCAD, 0xFCAE, 0xFCAF, 0xFCB0, 0xFCB1, 0xFCB2, 0xFCB3, + 0xFCB4, 0xFCB5, 0xFCB6, 0xFCB7, 0xFCB8, 0xFCB9, 0xFCBA, 0xFCBB, 0xFCBC, + 0xFCBD, 0xFCBE, 0xFCBF, 0xFCC0, 0xFCC1, 0xFCC2, 0xFCC3, 0xFCC4, 0xFCC5, + 0xFCC6, 0xFCC7, 0xFCC8, 0xFCC9, 0xFCCA, 0xFCCB, 0xFCCC, 0xFCCD, 0xFCCE, + 0xFCCF, 0xFCD0, 0xFCD1, 0xFCD2, 0xFCD3, 0xFCD4, 0xFCD5, 0xFCD6, 0xFCD7, + 0xFCD8, 0xFCD9, 0xFCDA, 0xFCDB, 0xFCDC, 0xFCDD, 0xFCDE, 0xFCDF, 0xFCE0, + 0xFCE1, 0xFCE2, 0xFCE3, 0xFCE4, 0xFCE5, 0xFCE6, 0xFCE7, 0xFCE8, 0xFCE9, + 0xFCEA, 0xFCEB, 0xFCEC, 0xFCED, 0xFCEE, 0xFCEF, 0xFCF0, 0xFCF1, 0xFCF2, + 0xFCF3, 0xFCF4, 0xFCF5, 0xFCF6, 0xFCF7, 0xFCF8, 0xFCF9, 0xFCFA, 0xFCFB, + 0xFCFC, 0xFCFD, 0xFCFE, 0xFCFF, 0xFD00, 0xFD01, 0xFD02, 0xFD03, 0xFD04, + 0xFD05, 0xFD06, 0xFD07, 0xFD08, 0xFD09, 0xFD0A, 0xFD0B, 0xFD0C, 0xFD0D, + 0xFD0E, 0xFD0F, 0xFD10, 0xFD11, 0xFD12, 0xFD13, 0xFD14, 0xFD15, 0xFD16, + 0xFD17, 0xFD18, 0xFD19, 0xFD1A, 0xFD1B, 0xFD1C, 0xFD1D, 0xFD1E, 0xFD1F, + 0xFD20, 0xFD21, 0xFD22, 0xFD23, 0xFD24, 0xFD25, 0xFD26, 0xFD27, 0xFD28, + 0xFD29, 0xFD2A, 0xFD2B, 0xFD2C, 0xFD2D, 0xFD2E, 0xFD2F, 0xFD30, 0xFD31, + 0xFD32, 0xFD33, 0xFD34, 0xFD35, 0xFD36, 0xFD37, 0xFD38, 0xFD39, 0xFD3A, + 0xFD3B, 0xFD3C, 0xFD3D, 0xFD50, 0xFD51, 0xFD52, 0xFD53, 0xFD54, 0xFD55, + 0xFD56, 0xFD57, 0xFD58, 0xFD59, 0xFD5A, 0xFD5B, 0xFD5C, 0xFD5D, 0xFD5E, + 0xFD5F, 0xFD60, 0xFD61, 0xFD62, 0xFD63, 0xFD64, 0xFD65, 0xFD66, 0xFD67, + 0xFD68, 0xFD69, 0xFD6A, 0xFD6B, 0xFD6C, 0xFD6D, 0xFD6E, 0xFD6F, 0xFD70, + 0xFD71, 0xFD72, 0xFD73, 0xFD74, 0xFD75, 0xFD76, 0xFD77, 0xFD78, 0xFD79, + 0xFD7A, 0xFD7B, 0xFD7C, 0xFD7D, 0xFD7E, 0xFD7F, 0xFD80, 0xFD81, 0xFD82, + 0xFD83, 0xFD84, 0xFD85, 0xFD86, 0xFD87, 0xFD88, 0xFD89, 0xFD8A, 0xFD8B, + 0xFD8C, 0xFD8D, 0xFD8E, 0xFD8F, 0xFD92, 0xFD93, 0xFD94, 0xFD95, 0xFD96, + 0xFD97, 0xFD98, 0xFD99, 0xFD9A, 0xFD9B, 0xFD9C, 0xFD9D, 0xFD9E, 0xFD9F, + 0xFDA0, 0xFDA1, 0xFDA2, 0xFDA3, 0xFDA4, 0xFDA5, 0xFDA6, 0xFDA7, 0xFDA8, + 0xFDA9, 0xFDAA, 0xFDAB, 0xFDAC, 0xFDAD, 0xFDAE, 0xFDAF, 0xFDB0, 0xFDB1, + 0xFDB2, 0xFDB3, 0xFDB4, 0xFDB5, 0xFDB6, 0xFDB7, 0xFDB8, 0xFDB9, 0xFDBA, + 0xFDBB, 0xFDBC, 0xFDBD, 0xFDBE, 0xFDBF, 0xFDC0, 0xFDC1, 0xFDC2, 0xFDC3, + 0xFDC4, 0xFDC5, 0xFDC6, 0xFDC7, 0xFDF0, 0xFDF1, 0xFDF2, 0xFDF3, 0xFDF4, + 0xFDF5, 0xFDF6, 0xFDF7, 0xFDF8, 0xFDF9, 0xFDFA, 0xFDFB, 0xFDFC, 0xFE70, + 0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE76, 0xFE77, 0xFE78, 0xFE79, 0xFE7A, + 0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F, 0xFE80, 0xFE81, 0xFE82, 0xFE83, + 0xFE84, 0xFE85, 0xFE86, 0xFE87, 0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C, + 0xFE8D, 0xFE8E, 0xFE8F, 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95, + 0xFE96, 0xFE97, 0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E, + 0xFE9F, 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7, + 0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF, 0xFEB0, + 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8, 0xFEB9, + 0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0, 0xFEC1, 0xFEC2, + 0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8, 0xFEC9, 0xFECA, 0xFECB, + 0xFECC, 0xFECD, 0xFECE, 0xFECF, 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4, + 0xFED5, 0xFED6, 0xFED7, 0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD, + 0xFEDE, 0xFEDF, 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6, + 0xFEE7, 0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF, + 0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7, 0xFEF8, + 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0x10800, 0x10801, 0x10802, 0x10803, + 0x10804, 0x10805, 0x10808, 0x1080A, 0x1080B, 0x1080C, 0x1080D, 0x1080E, + 0x1080F, 0x10810, 0x10811, 0x10812, 0x10813, 0x10814, 0x10815, 0x10816, + 0x10817, 0x10818, 0x10819, 0x1081A, 0x1081B, 0x1081C, 0x1081D, 0x1081E, + 0x1081F, 0x10820, 0x10821, 0x10822, 0x10823, 0x10824, 0x10825, 0x10826, + 0x10827, 0x10828, 0x10829, 0x1082A, 0x1082B, 0x1082C, 0x1082D, 0x1082E, + 0x1082F, 0x10830, 0x10831, 0x10832, 0x10833, 0x10834, 0x10835, 0x10837, + 0x10838, 0x1083C, 0x1083F, 0x10840, 0x10841, 0x10842, 0x10843, 0x10844, + 0x10845, 0x10846, 0x10847, 0x10848, 0x10849, 0x1084A, 0x1084B, 0x1084C, + 0x1084D, 0x1084E, 0x1084F, 0x10850, 0x10851, 0x10852, 0x10853, 0x10854, + 0x10855, 0x10857, 0x10858, 0x10859, 0x1085A, 0x1085B, 0x1085C, 0x1085D, + 0x1085E, 0x1085F, 0x10900, 0x10901, 0x10902, 0x10903, 0x10904, 0x10905, + 0x10906, 0x10907, 0x10908, 0x10909, 0x1090A, 0x1090B, 0x1090C, 0x1090D, + 0x1090E, 0x1090F, 0x10910, 0x10911, 0x10912, 0x10913, 0x10914, 0x10915, + 0x10916, 0x10917, 0x10918, 0x10919, 0x1091A, 0x1091B, 0x10920, 0x10921, + 0x10922, 0x10923, 0x10924, 0x10925, 0x10926, 0x10927, 0x10928, 0x10929, + 0x1092A, 0x1092B, 0x1092C, 0x1092D, 0x1092E, 0x1092F, 0x10930, 0x10931, + 0x10932, 0x10933, 0x10934, 0x10935, 0x10936, 0x10937, 0x10938, 0x10939, + 0x1093F, 0x10980, 0x10981, 0x10982, 0x10983, 0x10984, 0x10985, 0x10986, + 0x10987, 0x10988, 0x10989, 0x1098A, 0x1098B, 0x1098C, 0x1098D, 0x1098E, + 0x1098F, 0x10990, 0x10991, 0x10992, 0x10993, 0x10994, 0x10995, 0x10996, + 0x10997, 0x10998, 0x10999, 0x1099A, 0x1099B, 0x1099C, 0x1099D, 0x1099E, + 0x1099F, 0x109A0, 0x109A1, 0x109A2, 0x109A3, 0x109A4, 0x109A5, 0x109A6, + 0x109A7, 0x109A8, 0x109A9, 0x109AA, 0x109AB, 0x109AC, 0x109AD, 0x109AE, + 0x109AF, 0x109B0, 0x109B1, 0x109B2, 0x109B3, 0x109B4, 0x109B5, 0x109B6, + 0x109B7, 0x109BE, 0x109BF, 0x10A00, 0x10A10, 0x10A11, 0x10A12, 0x10A13, + 0x10A15, 0x10A16, 0x10A17, 0x10A19, 0x10A1A, 0x10A1B, 0x10A1C, 0x10A1D, + 0x10A1E, 0x10A1F, 0x10A20, 0x10A21, 0x10A22, 0x10A23, 0x10A24, 0x10A25, + 0x10A26, 0x10A27, 0x10A28, 0x10A29, 0x10A2A, 0x10A2B, 0x10A2C, 0x10A2D, + 0x10A2E, 0x10A2F, 0x10A30, 0x10A31, 0x10A32, 0x10A33, 0x10A40, 0x10A41, + 0x10A42, 0x10A43, 0x10A44, 0x10A45, 0x10A46, 0x10A47, 0x10A50, 0x10A51, + 0x10A52, 0x10A53, 0x10A54, 0x10A55, 0x10A56, 0x10A57, 0x10A58, 0x10A60, + 0x10A61, 0x10A62, 0x10A63, 0x10A64, 0x10A65, 0x10A66, 0x10A67, 0x10A68, + 0x10A69, 0x10A6A, 0x10A6B, 0x10A6C, 0x10A6D, 0x10A6E, 0x10A6F, 0x10A70, + 0x10A71, 0x10A72, 0x10A73, 0x10A74, 0x10A75, 0x10A76, 0x10A77, 0x10A78, + 0x10A79, 0x10A7A, 0x10A7B, 0x10A7C, 0x10A7D, 0x10A7E, 0x10A7F, 0x10B00, + 0x10B01, 0x10B02, 0x10B03, 0x10B04, 0x10B05, 0x10B06, 0x10B07, 0x10B08, + 0x10B09, 0x10B0A, 0x10B0B, 0x10B0C, 0x10B0D, 0x10B0E, 0x10B0F, 0x10B10, + 0x10B11, 0x10B12, 0x10B13, 0x10B14, 0x10B15, 0x10B16, 0x10B17, 0x10B18, + 0x10B19, 0x10B1A, 0x10B1B, 0x10B1C, 0x10B1D, 0x10B1E, 0x10B1F, 0x10B20, + 0x10B21, 0x10B22, 0x10B23, 0x10B24, 0x10B25, 0x10B26, 0x10B27, 0x10B28, + 0x10B29, 0x10B2A, 0x10B2B, 0x10B2C, 0x10B2D, 0x10B2E, 0x10B2F, 0x10B30, + 0x10B31, 0x10B32, 0x10B33, 0x10B34, 0x10B35, 0x10B40, 0x10B41, 0x10B42, + 0x10B43, 0x10B44, 0x10B45, 0x10B46, 0x10B47, 0x10B48, 0x10B49, 0x10B4A, + 0x10B4B, 0x10B4C, 0x10B4D, 0x10B4E, 0x10B4F, 0x10B50, 0x10B51, 0x10B52, + 0x10B53, 0x10B54, 0x10B55, 0x10B58, 0x10B59, 0x10B5A, 0x10B5B, 0x10B5C, + 0x10B5D, 0x10B5E, 0x10B5F, 0x10B60, 0x10B61, 0x10B62, 0x10B63, 0x10B64, + 0x10B65, 0x10B66, 0x10B67, 0x10B68, 0x10B69, 0x10B6A, 0x10B6B, 0x10B6C, + 0x10B6D, 0x10B6E, 0x10B6F, 0x10B70, 0x10B71, 0x10B72, 0x10B78, 0x10B79, + 0x10B7A, 0x10B7B, 0x10B7C, 0x10B7D, 0x10B7E, 0x10B7F, 0x10C00, 0x10C01, + 0x10C02, 0x10C03, 0x10C04, 0x10C05, 0x10C06, 0x10C07, 0x10C08, 0x10C09, + 0x10C0A, 0x10C0B, 0x10C0C, 0x10C0D, 0x10C0E, 0x10C0F, 0x10C10, 0x10C11, + 0x10C12, 0x10C13, 0x10C14, 0x10C15, 0x10C16, 0x10C17, 0x10C18, 0x10C19, + 0x10C1A, 0x10C1B, 0x10C1C, 0x10C1D, 0x10C1E, 0x10C1F, 0x10C20, 0x10C21, + 0x10C22, 0x10C23, 0x10C24, 0x10C25, 0x10C26, 0x10C27, 0x10C28, 0x10C29, + 0x10C2A, 0x10C2B, 0x10C2C, 0x10C2D, 0x10C2E, 0x10C2F, 0x10C30, 0x10C31, + 0x10C32, 0x10C33, 0x10C34, 0x10C35, 0x10C36, 0x10C37, 0x10C38, 0x10C39, + 0x10C3A, 0x10C3B, 0x10C3C, 0x10C3D, 0x10C3E, 0x10C3F, 0x10C40, 0x10C41, + 0x10C42, 0x10C43, 0x10C44, 0x10C45, 0x10C46, 0x10C47, 0x10C48, 0x1EE00, + 0x1EE01, 0x1EE02, 0x1EE03, 0x1EE05, 0x1EE06, 0x1EE07, 0x1EE08, 0x1EE09, + 0x1EE0A, 0x1EE0B, 0x1EE0C, 0x1EE0D, 0x1EE0E, 0x1EE0F, 0x1EE10, 0x1EE11, + 0x1EE12, 0x1EE13, 0x1EE14, 0x1EE15, 0x1EE16, 0x1EE17, 0x1EE18, 0x1EE19, + 0x1EE1A, 0x1EE1B, 0x1EE1C, 0x1EE1D, 0x1EE1E, 0x1EE1F, 0x1EE21, 0x1EE22, + 0x1EE24, 0x1EE27, 0x1EE29, 0x1EE2A, 0x1EE2B, 0x1EE2C, 0x1EE2D, 0x1EE2E, + 0x1EE2F, 0x1EE30, 0x1EE31, 0x1EE32, 0x1EE34, 0x1EE35, 0x1EE36, 0x1EE37, + 0x1EE39, 0x1EE3B, 0x1EE42, 0x1EE47, 0x1EE49, 0x1EE4B, 0x1EE4D, 0x1EE4E, + 0x1EE4F, 0x1EE51, 0x1EE52, 0x1EE54, 0x1EE57, 0x1EE59, 0x1EE5B, 0x1EE5D, + 0x1EE5F, 0x1EE61, 0x1EE62, 0x1EE64, 0x1EE67, 0x1EE68, 0x1EE69, 0x1EE6A, + 0x1EE6C, 0x1EE6D, 0x1EE6E, 0x1EE6F, 0x1EE70, 0x1EE71, 0x1EE72, 0x1EE74, + 0x1EE75, 0x1EE76, 0x1EE77, 0x1EE79, 0x1EE7A, 0x1EE7B, 0x1EE7C, 0x1EE7E, + 0x1EE80, 0x1EE81, 0x1EE82, 0x1EE83, 0x1EE84, 0x1EE85, 0x1EE86, 0x1EE87, + 0x1EE88, 0x1EE89, 0x1EE8B, 0x1EE8C, 0x1EE8D, 0x1EE8E, 0x1EE8F, 0x1EE90, + 0x1EE91, 0x1EE92, 0x1EE93, 0x1EE94, 0x1EE95, 0x1EE96, 0x1EE97, 0x1EE98, + 0x1EE99, 0x1EE9A, 0x1EE9B, 0x1EEA1, 0x1EEA2, 0x1EEA3, 0x1EEA5, 0x1EEA6, + 0x1EEA7, 0x1EEA8, 0x1EEA9, 0x1EEAB, 0x1EEAC, 0x1EEAD, 0x1EEAE, 0x1EEAF, + 0x1EEB0, 0x1EEB1, 0x1EEB2, 0x1EEB3, 0x1EEB4, 0x1EEB5, 0x1EEB6, 0x1EEB7, + 0x1EEB8, 0x1EEB9, 0x1EEBA, 0x1EEBB, 0x10FFFD]; + + function determineBidi(cueDiv) { + var nodeStack = [], + text = ""; + + if (!cueDiv || !cueDiv.childNodes) { + return "ltr"; + } + + function pushNodes(nodeStack, node) { + for (var i = node.childNodes.length - 1; i >= 0; i--) { + nodeStack.push(node.childNodes[i]); + } + } + + function nextTextNode(nodeStack) { + if (!nodeStack || !nodeStack.length) { + return null; + } + + var node = nodeStack.pop(); + if (node.textContent) { + // TODO: This should match all unicode type B characters (paragraph + // separator characters). See issue #115. + var m = node.textContent.match(/^.*(\n|\r)/); + if (m) { + nodeStack.length = 0; + return m[0]; + } + return node.textContent; + } + if (node.tagName === "ruby") { + return nextTextNode(nodeStack); + } + if (node.childNodes) { + pushNodes(nodeStack, node); + return nextTextNode(nodeStack); + } + } + + pushNodes(nodeStack, cueDiv); + while ((text = nextTextNode(nodeStack))) { + for (var i = 0; i < text.length; i++) { + if (strongRTLChars.indexOf(text.charCodeAt(i)) !== -1) { + return "rtl"; + } + } + } + return "ltr"; + } + function computeLinePos(cue) { if (typeof cue.line === "number" && - (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) + (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) { return cue.line; - if (!cue.track) + } + if (!cue.track) { return -1; + } // TODO: Have to figure out a way to determine what the position of the // Track is in the Media element's list of TextTracks and return that + 1, // negated. return 100; } - function CueBoundingBox(cue) { - // TODO: Apply unicode bidi algorithm and assign the result to 'direction' - this.direction = "ltr"; + function BoundingBox() { + } + + BoundingBox.prototype.applyStyles = function(styles) { + var div = this.div; + Object.keys(styles).forEach(function(style) { + div.style[style] = styles[style]; + }); + }; + + BoundingBox.prototype.formatStyle = function(val, unit) { + return val === 0 ? 0 : val + unit; + }; + + function BasicBoundingBox(window, cue) { + BoundingBox.call(this); + + // Parse our cue's text into a DOM tree rooted at 'div'. + this.div = parseContent(window, cue.text); - var boxLen = (function(direction){ - var maxLen = 0; - if ((cue.vertical === "" && - (cue.align === "left" || - (cue.align === "start" && direction === "ltr") || - (cue.align === "end" && direction === "rtl"))) || - ((cue.vertical === "rl" || cue.vertical === "lr") && - (cue.align === "start" || cue.align === "left"))) - maxLen = 100 - cue.position; - else if ((cue.vertical === "" && - (cue.align === "right" || - (cue.align === "end" && direction === "ltr") || - (cue.align === "start" && direction === "rtl"))) || - ((cue.vertical === "rl" || cue.vertical === "lr") && - (cue.align === "end" || cue.align === "right"))) - maxLen = cue.position; - else if (cue.align === "middle") { - if (cue.position <= 50) - maxLen = cue.position * 2; - else - maxLen = (100 - cue.position) * 2; - } - return cue.size < maxLen ? cue.size : maxLen; - }(this.direction)); + // Calculate the distance from the reference edge of the viewport to the text + // position of the cue box. The reference edge will be resolved later when + // the box orientation styles are applied. + var textPos = 0; + switch (cue.positionAlign) { + case "start": + textPos = cue.position; + break; + case "middle": + textPos = cue.position - (cue.size / 2); + break; + case "end": + textPos = cue.position - cue.size; + break; + } + + // Horizontal box orientation; textPos is the distance from the left edge of the + // area to the left edge of the box and cue.size is the distance extending to + // the right from there. + if (cue.vertical === "") { + this.applyStyles({ + left: this.formatStyle(textPos, "%"), + width: this.formatStyle(cue.size, "%"), + }); + // Vertical box orientation; textPos is the distance from the top edge of the + // area to the top edge of the box and cue.size is the height extending + // downwards from there. + } else { + this.applyStyles({ + top: this.formatStyle(textPos, "%"), + height: this.formatStyle(cue.size, "%") + }); + } - this.left = (function(direction) { - if (cue.vertical === "") { - if (direction === "ltr") { - if (cue.align === "start" || cue.align === "left") - return cue.position; - else if (cue.align === "end" || cue.align === "right") - return cue.position - boxLen; - else if (cue.align === "middle") - return cue.position - (boxLen / 2); - } else if (direction === "rtl") { - if (cue.align === "end" || cue.align === "left") - return 100 - cue.position; - else if (cue.align === "start" || cue.align === "right") - return 100 - cue.position - boxLen; - else if (cue.align === "middle") - return 100 - cue.position - (boxLen / 2); - } - } - return cue.snapToLines ? 0 : computeLinePos(cue); - }(this.direction)); + // All WebVTT cue-setting alignments are equivalent to the CSS mirrors of + // them except "middle" which is "center" in CSS. + this.applyStyles({ + "textAlign": cue.align === "middle" ? "center" : cue.align + }); + } + BasicBoundingBox.prototype = Object.create(BoundingBox.prototype); + BasicBoundingBox.prototype.constructor = BasicBoundingBox; + + const CUE_FONT_SIZE = 2.5; + const SCROLL_DURATION = 0.433; + const LINE_HEIGHT = 0.0533; + const REGION_FONT_SIZE = 1.3; - this.top = (function() { - if (cue.vertical === "rl" || cue.vertical === "lr") { - if (cue.align === "start" || cue.align === "left") - return cue.position; - else if (cue.align === "end" || cue.align === "right") - return cue.position - boxLen; - else if (cue.align === "middle") - return cue.position - (boxLen / 2); - } - return cue.snapToLines ? 0 : computeLinePos(cue); - }()); + // Constructs the computed display state of the cue (a div). Places the div + // into the overlay which should be a block level element (usually a div). + function CueBoundingBox(window, cue, overlay) { + BasicBoundingBox.call(this, window, cue); + this.applyStyles({ + direction: determineBidi(this.div), + writingMode: cue.vertical === "" ? "horizontal-tb" + : cue.vertical === "lr" ? "vertical-lr" + : "vertical-rl", + position: "absolute", + unicodeBidi: "plaintext", + fontSize: CUE_FONT_SIZE + "vh", + fontFamily: "sans-serif", + color: "rgba(255, 255, 255, 1)", + backgroundColor: "rgba(0, 0, 0, 0.8)", + whiteSpace: "pre-line" + }); + + // Append the div to the overlay so we can get the computed styles of the + // element in order to position for overlap avoidance. + overlay.appendChild(this.div); - // Apply a margin to the edges of the bounding box. The margin is user agent - // defined and is expressed as a percentage of the containing box's width. - var edgeMargin = 10; - if (cue.snapToLines) { - if (cue.vertical === "") { - if (this.left < edgeMargin && this.left + boxLen > edgeMargin) { - this.left += edgeMargin; - boxLen -= edgeMargin; - } - var rightMargin = 100 - edgeMargin; - if (this.left < rightMargin && this.left + boxLen > rightMargin) - boxLen -= edgeMargin; - } else if (cue.vertical === "lr" || cue.vertical === "rl") { - if (this.top < edgeMargin && this.top + boxLen > edgeMargin) { - this.top += edgeMargin; - boxLen -= edgeMargin; - } - var bottomMargin = 100 - edgeMargin; - if (this.top < bottomMargin && this.top + boxLen > bottomMargin) - boxLen -= edgeMargin; + // Calculate the distance from the reference edge of the viewport to the line + // position of the cue box. The reference edge will be resolved later when + // the box orientation styles are applied. Default if snapToLines is not set + // is 85. + var linePos = 85; + if (!cue.snapToLines) { + var computedLinePos = computeLinePos(cue), + boxComputedStyle = window.getComputedStyle(this.div), + size = cue.vertical === "" ? boxComputedStyle.getPropertyValue("height") : + boxComputedStyle.getPropertyValue("width"), + // Get the percentage value of the computed height as getPropertyValue + // returns pixels. + overlayHeight = window.getComputedStyle(overlay).getPropertyValue("height"), + calculatedPercentage = (size.replace("px", "") / overlayHeight.replace("px", "")) * 100; + + switch (cue.lineAlign) { + case "start": + linePos = computedLinePos; + break; + case "middle": + linePos = computedLinePos - (calculatedPercentage / 2); + break; + case "end": + linePos = computedLinePos - calculatedPercentage; + break; } } - this.height = cue.vertical === "" ? "auto" : boxLen; - this.width = cue.vertical === "" ? boxLen : "auto"; + switch (cue.vertical) { + case "": + this.applyStyles({ + top: this.formatStyle(linePos, "%") + }); + break; + case "rl": + this.applyStyles({ + left: this.formatStyle(linePos, "%") + }); + break; + case "lr": + this.applyStyles({ + right: this.formatStyle(linePos, "%") + }); + break; + } + + cue.displayState = this.div; + } + CueBoundingBox.prototype = Object.create(BasicBoundingBox.prototype); + CueBoundingBox.prototype.constuctor = CueBoundingBox; + + function RegionBoundingBox(window, region) { + BoundingBox.call(this); + this.div = window.document.createElement("div"); + + var left = region.viewportAnchorX - + region.regionAnchorX * region.width / 100, + top = region.viewportAnchorY - + region.regionAnchorY * region.lines * LINE_HEIGHT / 100; - this.writingMode = cue.vertical === "" ? - "horizontal-tb" : - cue.vertical === "lr" ? "vertical-lr" : "vertical-rl"; - this.position = "absolute"; - this.unicodeBidi = "plaintext"; - this.textAlign = cue.align === "middle" ? "center" : cue.align; - this.font = "5vh sans-serif"; - this.color = "rgba(255,255,255,1)"; - this.whiteSpace = "pre-line"; + this.applyStyles({ + position: "absolute", + writingMode: "horizontal-tb", + backgroundColor: "rgba(0, 0, 0, 0.8)", + wordWrap: "break-word", + overflowWrap: "break-word", + font: REGION_FONT_SIZE + "vh/" + LINE_HEIGHT + "vh sans-serif", + lineHeight: LINE_HEIGHT + "vh", + color: "rgba(255, 255, 255, 1)", + overflow: "hidden", + width: this.formatStyle(region.width, "%"), + minHeight: "0", + // TODO: This value is undefined in the spec, but I am assuming that they + // refer to lines * line height to get the max height See issue #107. + maxHeight: this.formatStyle(region.lines * LINE_HEIGHT, "px"), + left: this.formatStyle(left, "%"), + top: this.formatStyle(top, "%"), + display: "inline-flex", + flexFlow: "column", + justifyContent: "flex-end" + }); + + this.maybeAddCue = function(cue) { + if (region.id !== cue.regionId) { + return false; + } + + var basicBox = new BasicBoundingBox(window, cue); + basicBox.applyStyles({ + position: "relative", + unicodeBidi: "plaintext", + width: "auto" + }); + + if (this.div.childNodes.length === 1 && region.scroll === "up") { + this.applyStyles({ + transitionProperty: "top", + transitionDuration: SCROLL_DURATION + "s" + }); + } + + this.div.appendChild(basicBox.div); + return true; + }; } - - const WEBVTT = "WEBVTT"; + RegionBoundingBox.prototype = Object.create(BoundingBox.prototype); + RegionBoundingBox.prototype.constructor = RegionBoundingBox; function WebVTTParser(window, decoder) { this.window = window; this.state = "INITIAL"; this.buffer = ""; - this.decoder = decoder || TextDecoder("utf8"); + this.decoder = decoder || new TextDecoder("utf8"); } // Helper to allow strings to be decoded instead of the default binary utf8 data. WebVTTParser.StringDecoder = function() { return { decode: function(data) { - if (!data) return ""; - if (typeof data !== "string") throw "[StringDecoder] Error - expected string data"; - + if (!data) { + return ""; + } + if (typeof data !== "string") { + throw new Error("Error - expected string data."); + } return decodeURIComponent(escape(data)); } }; }; WebVTTParser.convertCueToDOMTree = function(window, cuetext) { - if (!window || !cuetext) + if (!window || !cuetext) { return null; + } return parseContent(window, cuetext); }; - WebVTTParser.processCues = function(window, cues) { - if (!window || !cues) + // Runs the processing model over the cues and regions passed to it. + // @param overlay A block level element (usually a div) that the computed cues + // and regions will be placed into. + WebVTTParser.processCues = function(window, cues, regions, overlay) { + if (!window || !cues || !overlay) { return null; + } + + // Remove all previous children. + while (overlay.firstChild) { + overlay.removeChild(overlay.firstChild); + } + + var regionBoxes = regions ? regions.map(function(region) { + return new RegionBoundingBox(window, region); + }) : null; - return cues.map(function(cue) { - var div = parseContent(window, cue.text); - div.style = new CueBoundingBox(cue); - // TODO: Adjust divs based on other cues already processed. - // TODO: Account for regions. - return div; - }); + function mapCueToRegion(cue) { + for (var i = 0; i < regionBoxes.length; i++) { + if (regionBoxes[i].maybeAddCue(cue)) { + return true; + } + } + return false; + } + + for (var i = 0; i < cues.length; i++) { + // Check to see if this cue is contained within a VTTRegion. + if (regionBoxes && mapCueToRegion(cues[i])) { + continue; + } + // Check to see if we can just reuse the last computed styles of the cue. + if (cues[i].hasBeenReset !== true && cues[i].displayState) { + overlay.appendChild(cues[i].displayState); + continue; + } + // Compute the position of the cue box on the cue overlay. + var cueBox = new CueBoundingBox(window, cues[i], overlay); + } }; WebVTTParser.prototype = { parse: function (data) { var self = this; // If there is no data then we won't decode it, but will just try to parse // whatever is in buffer already. This may occur in circumstances, for // example when flush() is called. if (data) { // Try to decode the data that we received. self.buffer += self.decoder.decode(data, {stream: true}); } - // Advance tells whether or not to remove the collected line from the buffer - // after it is read. - function collectNextLine(advance) { + function collectNextLine() { var buffer = self.buffer; var pos = 0; - advance = typeof advance === "undefined" ? true : advance; - while (pos < buffer.length && buffer[pos] != '\r' && buffer[pos] != '\n') + while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') { ++pos; + } var line = buffer.substr(0, pos); // Advance the buffer early in case we fail below. - if (buffer[pos] === '\r') + if (buffer[pos] === '\r') { ++pos; - if (buffer[pos] === '\n') + } + if (buffer[pos] === '\n') { ++pos; - if (advance) - self.buffer = buffer.substr(pos); + } + self.buffer = buffer.substr(pos); return line; } // 3.4 WebVTT region and WebVTT region settings syntax function parseRegion(input) { var settings = new Settings(); parseOptions(input, function (k, v) { switch (k) { case "id": - settings.region(k, v); + settings.set(k, v); break; case "width": settings.percent(k, v, true); break; case "lines": settings.integer(k, v); break; case "regionanchor": case "viewportanchor": var xy = v.split(','); - if (xy.length !== 2) + if (xy.length !== 2) { break; + } // We have to make sure both x and y parse, so use a temporary // settings object here. var anchor = new Settings(); anchor.percent("x", xy[0], true); anchor.percent("y", xy[1], true); - if (!anchor.has("x") || !anchor.has("y")) + if (!anchor.has("x") || !anchor.has("y")) { break; + } settings.set(k + "X", anchor.get("x")); settings.set(k + "Y", anchor.get("y")); break; case "scroll": settings.alt(k, v, ["up"]); break; } }, /=/, /\s/); @@ -532,17 +983,17 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"] var region = new self.window.VTTRegion(); region.id = settings.get("id"); region.width = settings.get("width", 100); region.lines = settings.get("lines", 3); region.regionAnchorX = settings.get("regionanchorX", 0); region.regionAnchorY = settings.get("regionanchorY", 100); region.viewportAnchorX = settings.get("viewportanchorX", 0); region.viewportAnchorY = settings.get("viewportanchorY", 100); - region.scroll = settings.get("scroll", "none"); + region.scroll = settings.get("scroll", ""); self.onregion(region); } } // 3.2 WebVTT metadata header syntax function parseHeader(input) { parseOptions(input, function (k, v) { switch (k) { @@ -553,43 +1004,34 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"] } }, /:/); } // 5.1 WebVTT file parsing. try { var line; if (self.state === "INITIAL") { - // Wait until we have enough data to parse the header. - if (self.buffer.length <= WEBVTT.length) + // We can't start parsing until we have the first line. + if (!/\r\n|\n/.test(self.buffer)) { return this; + } - // Collect the next line, but do not remove the collected line from the - // buffer as we may not have the full WEBVTT signature yet when - // incrementally parsing. - line = collectNextLine(false); - // (4-12) - Check for the "WEBVTT" identifier followed by an optional space or tab, - // and ignore the rest of the line. - if (line.substr(0, WEBVTT.length) !== WEBVTT || - line.length > WEBVTT.length && !/[ \t]/.test(line[WEBVTT.length])) { - throw "error"; + line = collectNextLine(); + + var m = line.match(/^WEBVTT([ \t].*)?$/); + if (!m || !m[0]) { + throw new ParsingError("Malformed WebVTT signature."); } - // Now that we've read the WEBVTT signature we can remove it from - // the buffer. - collectNextLine(true); + self.state = "HEADER"; } while (self.buffer) { // We can't parse a line until we have the full line. - if (!/[\r\n]/.test(self.buffer)) { - // If we are in the midst of parsing a cue, report it early. We will report it - // again when updates come in. - if (self.state === "CUETEXT" && self.cue && self.onpartialcue) - self.onpartialcue(self.cue); + if (!/\r\n|\n/.test(self.buffer)) { return this; } line = collectNextLine(); switch (self.state) { case "HEADER": // 13-18 - Allow a header (metadata) under the WEBVTT line. @@ -597,79 +1039,90 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"] parseHeader(line); } else if (!line) { // An empty line terminates the header and starts the body (cues). self.state = "ID"; } continue; case "NOTE": // Ignore NOTE blocks. - if (!line) + if (!line) { self.state = "ID"; + } continue; case "ID": // Check for the start of NOTE blocks. if (/^NOTE($|[ \t])/.test(line)) { self.state = "NOTE"; break; } // 19-29 - Allow any number of line terminators, then initialize new cue values. - if (!line) + if (!line) { continue; + } self.cue = new self.window.VTTCue(0, 0, ""); self.state = "CUE"; // 30-39 - Check if self line contains an optional identifier or timing data. - if (line.indexOf("-->") == -1) { + if (line.indexOf("-->") === -1) { self.cue.id = line; continue; } // Process line as start of a cue. /*falls through*/ case "CUE": // 40 - Collect cue timings and settings. try { parseCue(line, self.cue); } catch (e) { + // If it's not a parsing error then throw it to the consumer. + if (!(e instanceof ParsingError)) { + throw e; + } // In case of an error ignore rest of the cue. self.cue = null; self.state = "BADCUE"; continue; } self.state = "CUETEXT"; continue; case "CUETEXT": // 41-53 - Collect the cue text, create a cue, and add it to the output. if (!line) { // We are done parsing self cue. self.oncue && self.oncue(self.cue); self.cue = null; self.state = "ID"; continue; } - if (self.cue.text) + if (self.cue.text) { self.cue.text += "\n"; + } self.cue.text += line; continue; - default: // BADCUE + case "BADCUE": // BADCUE // 54-62 - Collect and discard the remaining cue. if (!line) { self.state = "ID"; } continue; } } } catch (e) { + // If it's not a parsing error then throw it to the consumer. + if (!(e instanceof ParsingError)) { + throw e; + } // If we are currently parsing a cue, report what we have, and then the error. - if (self.state === "CUETEXT" && self.cue && self.oncue) + if (self.state === "CUETEXT" && self.cue && self.oncue) { self.oncue(self.cue); + } self.cue = null; - // Report the error and enter the BADCUE state, except if we haven't even made - // it through the header yet. - if (self.state !== "INITIAL") - self.state = "BADCUE"; + // Enter BADWEBVTT state if header was not parsed correctly otherwise + // another exception occurred so enter BADCUE state. + self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE"; } return this; }, flush: function () { var self = this; // Finish decoding the stream. self.buffer += self.decoder.decode(); // Synthesize the end of the current cue or region.
--- a/dom/system/gonk/OpenFileFinder.cpp +++ b/dom/system/gonk/OpenFileFinder.cpp @@ -5,16 +5,29 @@ #include "OpenFileFinder.h" #include "mozilla/FileUtils.h" #include "nsPrintfCString.h" #include <sys/stat.h> #include <errno.h> +#define USE_DEBUG 0 + +#undef LOG +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "OpenFileFinder", ## args) +#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "OpenFileFinder", ## args) +#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "OpenFileFinder", ## args) + +#if USE_DEBUG +#define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "OpenFileFinder" , ## args) +#else +#define DBG(args...) +#endif + namespace mozilla { namespace system { OpenFileFinder::OpenFileFinder(const nsACString& aPath, bool aCheckIsB2gOrDescendant /* = true */) : mPath(aPath), mProcDir(nullptr), mFdDir(nullptr),
--- a/dom/system/gonk/OpenFileFinder.h +++ b/dom/system/gonk/OpenFileFinder.h @@ -4,29 +4,16 @@ #ifndef mozilla_system_openfilefinder_h__ #define mozilla_system_openfilefinder_h__ #include "nsString.h" #include <dirent.h> -#define USE_DEBUG 0 - -#undef LOG -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "OpenFileFinder", ## args) -#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "OpenFileFinder", ## args) -#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "OpenFileFinder", ## args) - -#if USE_DEBUG -#define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "OpenFileFinder" , ## args) -#else -#define DBG(args...) -#endif - namespace mozilla { namespace system { class OpenFileFinder { public: enum State {
--- a/dom/webidl/VTTCue.webidl +++ b/dom/webidl/VTTCue.webidl @@ -37,17 +37,27 @@ interface VTTCue : EventTarget { attribute boolean snapToLines; // XXXhumph: https://www.w3.org/Bugs/Public/show_bug.cgi?id=20651 // attribute (long or AutoKeyword) line; [SetterThrows] attribute AlignSetting lineAlign; [SetterThrows] attribute long position; [SetterThrows] + attribute AlignSetting positionAlign; + [SetterThrows] attribute long size; attribute AlignSetting align; attribute DOMString text; DocumentFragment getCueAsHTML(); attribute EventHandler onenter; attribute EventHandler onexit; }; + +// Mozilla extensions. +partial interface VTTCue { + [ChromeOnly] + attribute HTMLDivElement? displayState; + [ChromeOnly] + readonly attribute boolean hasBeenReset; +};
--- a/gfx/gl/GLReadTexImageHelper.cpp +++ b/gfx/gl/GLReadTexImageHelper.cpp @@ -8,16 +8,18 @@ #include "GLContext.h" #include "OGLShaderProgram.h" #include "gfxTypes.h" #include "gfxContext.h" #include "ScopedGLHelpers.h" #include "mozilla/gfx/2D.h" #include "gfx2DGlue.h" +using namespace mozilla::gfx; + namespace mozilla { namespace gl { GLReadTexImageHelper::GLReadTexImageHelper(GLContext* gl) : mGL(gl) { mPrograms[0] = 0; mPrograms[1] = 0; @@ -199,24 +201,24 @@ GetActualReadFormats(GLContext* gl, GLen return false; } else { readFormat = destFormat; readType = destType; return true; } } -static void SwapRAndBComponents(gfxImageSurface* surf) +static void SwapRAndBComponents(DataSourceSurface* surf) { - uint8_t *row = surf->Data(); + uint8_t *row = surf->GetData(); - size_t rowBytes = surf->Width()*4; + size_t rowBytes = surf->GetSize().width*4; size_t rowHole = surf->Stride() - rowBytes; - size_t rows = surf->Height(); + size_t rows = surf->GetSize().height; while (rows) { const uint8_t *rowEnd = row + rowBytes; while (row != rowEnd) { row[0] ^= row[2]; row[2] ^= row[0]; @@ -277,29 +279,29 @@ ReadPixelsIntoImageSurface(GLContext* gl nsAutoPtr<gfxImageSurface> tempSurf; gfxImageSurface* readSurf = nullptr; int readPixelSize = 0; if (needsTempSurf) { if (gl->DebugMode()) { NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!"); } - gfx::SurfaceFormat readFormatGFX; + SurfaceFormat readFormatGFX; switch (readFormat) { case LOCAL_GL_RGBA: case LOCAL_GL_BGRA: { - readFormatGFX = hasAlpha ? gfx::FORMAT_B8G8R8A8 - : gfx::FORMAT_B8G8R8X8; + readFormatGFX = hasAlpha ? FORMAT_B8G8R8A8 + : FORMAT_B8G8R8X8; break; } case LOCAL_GL_RGB: { MOZ_ASSERT(readPixelSize == 2); MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV); - readFormatGFX = gfx::FORMAT_R5G6B5; + readFormatGFX = FORMAT_R5G6B5; break; } default: { MOZ_CRASH("Bad read format."); } } switch (readType) { @@ -353,17 +355,22 @@ ReadPixelsIntoImageSurface(GLContext* gl gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment); if (readSurf != dest) { MOZ_ASSERT(readFormat == LOCAL_GL_RGBA); MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE); // So we just copied in RGBA in big endian, or le: 0xAABBGGRR. // We want 0xAARRGGBB, so swap R and B: dest->Flush(); - SwapRAndBComponents(readSurf); + RefPtr<DataSourceSurface> readDSurf = + Factory::CreateWrappingDataSourceSurface(readSurf->Data(), + readSurf->Stride(), + ToIntSize(readSurf->GetSize()), + ImageFormatToSurfaceFormat(readSurf->Format())); + SwapRAndBComponents(readDSurf); dest->MarkDirty(); gfxContext ctx(dest); ctx.SetOperator(gfxContext::OPERATOR_SOURCE); ctx.SetSource(readSurf); ctx.Paint(); } @@ -392,76 +399,80 @@ ReadPixelsIntoImageSurface(GLContext* gl } } dest->MarkDirty(); } } #endif } -static already_AddRefed<gfxImageSurface> YInvertImageSurface(gfxImageSurface* aSurf) +static TemporaryRef<DataSourceSurface> YInvertImageSurface(DataSourceSurface* aSurf) { - gfxIntSize size = aSurf->GetSize(); - nsRefPtr<gfxImageSurface> temp = new gfxImageSurface(size, aSurf->Format()); - nsRefPtr<gfxContext> ctx = new gfxContext(temp); + RefPtr<DataSourceSurface> temp = + Factory::CreateDataSourceSurfaceWithStride(aSurf->GetSize(), + aSurf->GetFormat(), + aSurf->Stride()); + RefPtr<DrawTarget> dt = + Factory::CreateDrawTargetForData(BACKEND_CAIRO, + temp->GetData(), + temp->GetSize(), + temp->Stride(), + temp->GetFormat()); + nsRefPtr<gfxContext> ctx = new gfxContext(dt); ctx->SetOperator(gfxContext::OPERATOR_SOURCE); ctx->Scale(1.0, -1.0); - ctx->Translate(-gfxPoint(0.0, size.height)); - ctx->SetSource(aSurf); + ctx->Translate(-gfxPoint(0.0, aSurf->GetSize().height)); + + nsRefPtr<gfxImageSurface> thebesSurf = + new gfxImageSurface(aSurf->GetData(), + ThebesIntSize(aSurf->GetSize()), + aSurf->Stride(), + SurfaceFormatToImageFormat(aSurf->GetFormat())); + ctx->SetSource(thebesSurf); ctx->Paint(); return temp.forget(); } -already_AddRefed<gfxImageSurface> -GetTexImage(GLContext* gl, GLuint aTexture, bool aYInvert, gfx::SurfaceFormat aFormat) +TemporaryRef<DataSourceSurface> +ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, SurfaceFormat aFormat) { gl->MakeCurrent(); gl->GuaranteeResolve(); gl->fActiveTexture(LOCAL_GL_TEXTURE0); gl->fBindTexture(LOCAL_GL_TEXTURE_2D, aTexture); - gfxIntSize size; + IntSize size; gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_WIDTH, &size.width); gl->fGetTexLevelParameteriv(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_TEXTURE_HEIGHT, &size.height); - nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(size, gfxImageFormatARGB32); - if (!surf || surf->CairoStatus()) { + RefPtr<DataSourceSurface> surf = + Factory::CreateDataSourceSurfaceWithStride(size, FORMAT_B8G8R8A8, + GetAlignedStride<4>(size.width * BytesPerPixel(FORMAT_B8G8R8A8))); + + if (!surf) { return nullptr; } uint32_t currentPackAlignment = 0; gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*)¤tPackAlignment); if (currentPackAlignment != 4) { gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4); } - gl->fGetTexImage(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, surf->Data()); + gl->fGetTexImage(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, surf->GetData()); if (currentPackAlignment != 4) { gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment); } - if (aFormat == gfx::FORMAT_R8G8B8A8 || aFormat == gfx::FORMAT_R8G8B8X8) { + if (aFormat == FORMAT_R8G8B8A8 || aFormat == FORMAT_R8G8B8X8) { SwapRAndBComponents(surf); } if (aYInvert) { surf = YInvertImageSurface(surf); } - return surf.forget(); -} - -TemporaryRef<gfx::DataSourceSurface> -ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, gfx::SurfaceFormat aFormat) -{ - nsRefPtr<gfxImageSurface> image = GetTexImage(gl, aTexture, aYInvert, aFormat); - RefPtr<gfx::DataSourceSurface> surf = - gfx::Factory::CreateDataSourceSurface(gfx::ToIntSize(image->GetSize()), aFormat); - - if (!image->CopyTo(surf)) { - return nullptr; - } return surf.forget(); } void ReadScreenIntoImageSurface(GLContext* gl, gfxImageSurface* dest) { ScopedBindFramebuffer autoFB(gl, 0);
--- a/gfx/gl/GLReadTexImageHelper.h +++ b/gfx/gl/GLReadTexImageHelper.h @@ -22,19 +22,16 @@ namespace gfx { class DataSourceSurface; } namespace gl { void ReadPixelsIntoImageSurface(GLContext* aGL, gfxImageSurface* aSurface); void ReadScreenIntoImageSurface(GLContext* aGL, gfxImageSurface* aSurface); -already_AddRefed<gfxImageSurface> -GetTexImage(GLContext* gl, GLuint aTexture, bool aYInvert, gfx::SurfaceFormat aFormat); - TemporaryRef<gfx::DataSourceSurface> ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, gfx::SurfaceFormat aFormat); class GLReadTexImageHelper MOZ_FINAL { // The GLContext is the sole owner of the GLBlitHelper. GLContext* mGL;
--- a/gfx/gl/SharedSurfaceGralloc.cpp +++ b/gfx/gl/SharedSurfaceGralloc.cpp @@ -6,22 +6,24 @@ #include "mozilla/Preferences.h" #include "SharedSurfaceGralloc.h" #include "GLContext.h" #include "SharedSurfaceGL.h" #include "SurfaceFactory.h" #include "GLLibraryEGL.h" +#include "mozilla/layers/GrallocTextureClient.h" #include "mozilla/layers/ShadowLayers.h" #include "ui/GraphicBuffer.h" #include "../layers/ipc/ShadowLayers.h" #include "ScopedGLHelpers.h" +#include "gfxPlatform.h" #include "gfx2DGlue.h" #define DEBUG_GRALLOC #ifdef DEBUG_GRALLOC #define DEBUG_PRINT(...) do { printf_stderr(__VA_ARGS__); } while (0) #else #define DEBUG_PRINT(...) do { } while (0) #endif @@ -67,44 +69,45 @@ SharedSurface_Gralloc::Create(GLContext* GLLibraryEGL* egl = &sEGLLibrary; MOZ_ASSERT(egl); DEBUG_PRINT("SharedSurface_Gralloc::Create -------\n"); if (!HasExtensions(egl, prodGL)) return nullptr; - SurfaceDescriptor baseDesc; - SurfaceDescriptorGralloc desc; + gfxContentType type = hasAlpha ? GFX_CONTENT_COLOR_ALPHA + : GFX_CONTENT_COLOR; + + gfxImageFormat format + = gfxPlatform::GetPlatform()->OptimalFormatForContent(type); - gfxContentType type = hasAlpha ? GFX_CONTENT_COLOR_ALPHA - : GFX_CONTENT_COLOR; - if (!allocator->AllocSurfaceDescriptorWithCaps(size, type, USING_GL_RENDERING_ONLY, &baseDesc)) - return false; + GrallocTextureClientOGL* grallocTC = + new GrallocTextureClientOGL( + allocator, + gfx::ImageFormatToSurfaceFormat(format), + TEXTURE_FLAGS_DEFAULT); - if (baseDesc.type() != SurfaceDescriptor::TSurfaceDescriptorGralloc) { - allocator->DestroySharedSurface(&baseDesc); - return false; + if (!grallocTC->AllocateForGLRendering(size)) { + return nullptr; } - desc = baseDesc.get_SurfaceDescriptorGralloc(); - - sp<GraphicBuffer> buffer = GrallocBufferActor::GetFrom(desc); + sp<GraphicBuffer> buffer = grallocTC->GetGraphicBuffer(); EGLDisplay display = egl->Display(); EGLClientBuffer clientBuffer = buffer->getNativeBuffer(); EGLint attrs[] = { LOCAL_EGL_NONE, LOCAL_EGL_NONE }; EGLImage image = egl->fCreateImage(display, EGL_NO_CONTEXT, LOCAL_EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); if (!image) { - allocator->DestroySharedSurface(&baseDesc); + grallocTC->DropTextureData()->DeallocateSharedData(allocator); return nullptr; } prodGL->MakeCurrent(); GLuint prodTex = 0; prodGL->fGenTextures(1, &prodTex); ScopedBindTexture autoTex(prodGL, prodTex); @@ -112,17 +115,17 @@ SharedSurface_Gralloc::Create(GLContext* prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR); prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); prodGL->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, image); egl->fDestroyImage(display, image); - SharedSurface_Gralloc *surf = new SharedSurface_Gralloc(prodGL, size, hasAlpha, egl, allocator, desc, prodTex); + SharedSurface_Gralloc *surf = new SharedSurface_Gralloc(prodGL, size, hasAlpha, egl, allocator, grallocTC, prodTex); DEBUG_PRINT("SharedSurface_Gralloc::Create: success -- surface %p, GraphicBuffer %p.\n", surf, buffer.get()); return surf; } bool @@ -134,22 +137,16 @@ SharedSurface_Gralloc::HasExtensions(GLL SharedSurface_Gralloc::~SharedSurface_Gralloc() { DEBUG_PRINT("[SharedSurface_Gralloc %p] destroyed\n", this); mGL->MakeCurrent(); mGL->fDeleteTextures(1, (GLuint*)&mProdTex); - - SurfaceDescriptor desc(mDesc); - - if (mAllocator) { - mAllocator->DestroySharedSurface(&desc); - } } void SharedSurface_Gralloc::Fence() { // We should be able to rely on genlock write locks/read locks. // But they're broken on some configs, and even a glFinish doesn't // work. glReadPixels seems to, though.
--- a/gfx/gl/SharedSurfaceGralloc.h +++ b/gfx/gl/SharedSurfaceGralloc.h @@ -2,23 +2,23 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef SHARED_SURFACE_GRALLOC_H_ #define SHARED_SURFACE_GRALLOC_H_ #include "SharedSurfaceGL.h" +#include "mozilla/layers/ISurfaceAllocator.h" #include "mozilla/layers/LayersSurfaces.h" -#include "mozilla/layers/ISurfaceAllocator.h" +#include "mozilla/layers/TextureClient.h" namespace mozilla { namespace layers { class ISurfaceAllocator; -class SurfaceDescriptorGralloc; } namespace gl { class GLContext; class GLLibraryEGL; class SharedSurface_Gralloc : public SharedSurface_GL @@ -34,43 +34,34 @@ public: MOZ_ASSERT(surf->Type() == SharedSurfaceType::Gralloc); return (SharedSurface_Gralloc*)surf; } protected: GLLibraryEGL* const mEGL; RefPtr<layers::ISurfaceAllocator> mAllocator; - // We keep the SurfaceDescriptor around, because we'll end up - // using it often and it's handy to do so. The actual - // GraphicBuffer is kept alive by the sp<GraphicBuffer> in - // GrallocBufferActor; the actor will stay alive until we - // explicitly destroy this descriptor (and thus deallocate the - // actor) it in the destructor of this class. This is okay to do - // on the client, but is very bad to do on the server (because on - // the client, the actor has no chance of going away unless the - // whole app died). - layers::SurfaceDescriptorGralloc mDesc; + RefPtr<layers::TextureClient> mTextureClient; const GLuint mProdTex; SharedSurface_Gralloc(GLContext* prodGL, const gfx::IntSize& size, bool hasAlpha, GLLibraryEGL* egl, layers::ISurfaceAllocator* allocator, - layers::SurfaceDescriptorGralloc& desc, + layers::TextureClient* textureClient, GLuint prodTex) : SharedSurface_GL(SharedSurfaceType::Gralloc, AttachmentType::GLTexture, prodGL, size, hasAlpha) , mEGL(egl) , mAllocator(allocator) - , mDesc(desc) + , mTextureClient(textureClient) , mProdTex(prodTex) {} static bool HasExtensions(GLLibraryEGL* egl, GLContext* gl); public: virtual ~SharedSurface_Gralloc(); @@ -79,18 +70,18 @@ public: virtual void LockProdImpl(); virtual void UnlockProdImpl(); virtual GLuint Texture() const { return mProdTex; } - layers::SurfaceDescriptorGralloc& GetDescriptor() { - return mDesc; + layers::TextureClient* GetTextureClient() { + return mTextureClient; } }; class SurfaceFactory_Gralloc : public SurfaceFactory_GL { protected: RefPtr<layers::ISurfaceAllocator> mAllocator;
--- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -1110,44 +1110,50 @@ void WriteSnapshotLinkToDumpFile(T* aObj } nsCString string(aObj->Name()); string.Append("-"); string.AppendInt((uint64_t)aObj); fprintf_stderr(aFile, "href=\"javascript:ViewImage('%s')\"", string.BeginReading()); } template <typename T> -void WriteSnapshotToDumpFile_internal(T* aObj, gfxASurface* aSurf) +void WriteSnapshotToDumpFile_internal(T* aObj, DataSourceSurface* aSurf) { + nsRefPtr<gfxImageSurface> deprecatedSurf = + new gfxImageSurface(aSurf->GetData(), + ThebesIntSize(aSurf->GetSize()), + aSurf->Stride(), + SurfaceFormatToImageFormat(aSurf->GetFormat())); nsCString string(aObj->Name()); string.Append("-"); string.AppendInt((uint64_t)aObj); if (gfxUtils::sDumpPaintFile) { fprintf_stderr(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading()); } - aSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile); + deprecatedSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile); if (gfxUtils::sDumpPaintFile) { fprintf_stderr(gfxUtils::sDumpPaintFile, "\";"); } } -void WriteSnapshotToDumpFile(Layer* aLayer, gfxASurface* aSurf) +void WriteSnapshotToDumpFile(Layer* aLayer, DataSourceSurface* aSurf) { WriteSnapshotToDumpFile_internal(aLayer, aSurf); } -void WriteSnapshotToDumpFile(LayerManager* aManager, gfxASurface* aSurf) +void WriteSnapshotToDumpFile(LayerManager* aManager, DataSourceSurface* aSurf) { WriteSnapshotToDumpFile_internal(aManager, aSurf); } void WriteSnapshotToDumpFile(Compositor* aCompositor, DrawTarget* aTarget) { - nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aTarget); - WriteSnapshotToDumpFile_internal(aCompositor, surf); + RefPtr<SourceSurface> surf = aTarget->Snapshot(); + RefPtr<DataSourceSurface> dSurf = surf->GetDataSurface(); + WriteSnapshotToDumpFile_internal(aCompositor, dSurf); } #endif void Layer::Dump(FILE* aFile, const char* aPrefix, bool aDumpHtml) { if (aDumpHtml) { fprintf_stderr(aFile, "<li><a id=\"%p\" ", this);
--- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -2041,17 +2041,17 @@ protected: // 0 is a special value that means "no ID". uint64_t mId; }; void SetAntialiasingFlags(Layer* aLayer, gfxContext* aTarget); void SetAntialiasingFlags(Layer* aLayer, gfx::DrawTarget* aTarget); #ifdef MOZ_DUMP_PAINTING -void WriteSnapshotToDumpFile(Layer* aLayer, gfxASurface* aSurf); -void WriteSnapshotToDumpFile(LayerManager* aManager, gfxASurface* aSurf); +void WriteSnapshotToDumpFile(Layer* aLayer, gfx::DataSourceSurface* aSurf); +void WriteSnapshotToDumpFile(LayerManager* aManager, gfx::DataSourceSurface* aSurf); void WriteSnapshotToDumpFile(Compositor* aCompositor, gfx::DrawTarget* aTarget); #endif } } #endif /* GFX_LAYERS_H */
--- a/gfx/layers/client/CanvasClient.cpp +++ b/gfx/layers/client/CanvasClient.cpp @@ -10,46 +10,42 @@ #include "Layers.h" // for Layer, etc #include "SurfaceStream.h" // for SurfaceStream #include "SurfaceTypes.h" // for SurfaceStreamHandle #include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat #include "gfxASurface.h" // for gfxASurface, etc #include "gfxPlatform.h" // for gfxPlatform #include "mozilla/gfx/BaseSize.h" // for BaseSize #include "mozilla/layers/CompositableForwarder.h" +#include "mozilla/layers/GrallocTextureClient.h" #include "mozilla/layers/LayersTypes.h" #include "mozilla/layers/TextureClient.h" // for TextureClient, etc +#include "mozilla/layers/TextureClientOGL.h" #include "nsAutoPtr.h" // for nsRefPtr #include "nsDebug.h" // for printf_stderr, NS_ASSERTION #include "nsXULAppAPI.h" // for XRE_GetProcessType, etc #ifdef MOZ_WIDGET_GONK #include "SharedSurfaceGralloc.h" #endif using namespace mozilla::gfx; using namespace mozilla::gl; namespace mozilla { -namespace gfx { -class SharedSurface; -} -} - -namespace mozilla { namespace layers { /* static */ TemporaryRef<CanvasClient> CanvasClient::CreateCanvasClient(CanvasClientType aType, CompositableForwarder* aForwarder, TextureFlags aFlags) { if (aType == CanvasClientGLContext && aForwarder->GetCompositorBackendType() == LAYERS_OPENGL) { aFlags |= TEXTURE_DEALLOCATE_CLIENT; - return new DeprecatedCanvasClientSurfaceStream(aForwarder, aFlags); + return new CanvasClientSurfaceStream(aForwarder, aFlags); } if (gfxPlatform::GetPlatform()->UseDeprecatedTextures()) { aFlags |= TEXTURE_DEALLOCATE_CLIENT; return new DeprecatedCanvasClient2D(aForwarder, aFlags); } return new CanvasClient2D(aForwarder, aFlags); } @@ -101,16 +97,83 @@ CanvasClient2D::Update(gfx::IntSize aSiz TemporaryRef<BufferTextureClient> CanvasClient2D::CreateBufferTextureClient(gfx::SurfaceFormat aFormat, TextureFlags aFlags) { return CompositableClient::CreateBufferTextureClient(aFormat, mTextureInfo.mTextureFlags | aFlags); } +CanvasClientSurfaceStream::CanvasClientSurfaceStream(CompositableForwarder* aLayerForwarder, + TextureFlags aFlags) + : CanvasClient(aLayerForwarder, aFlags) +{ +} + +void +CanvasClientSurfaceStream::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) +{ + GLScreenBuffer* screen = aLayer->mGLContext->Screen(); + SurfaceStream* stream = screen->Stream(); + + bool isCrossProcess = !(XRE_GetProcessType() == GeckoProcessType_Default); + bool bufferCreated = false; + if (isCrossProcess) { +#ifdef MOZ_WIDGET_GONK + SharedSurface* surf = stream->SwapConsumer(); + if (!surf) { + printf_stderr("surf is null post-SwapConsumer!\n"); + return; + } + + if (surf->Type() != SharedSurfaceType::Gralloc) { + printf_stderr("Unexpected non-Gralloc SharedSurface in IPC path!"); + MOZ_ASSERT(false); + return; + } + + SharedSurface_Gralloc* grallocSurf = SharedSurface_Gralloc::Cast(surf); + + GrallocTextureClientOGL* grallocTextureClient = + static_cast<GrallocTextureClientOGL*>(grallocSurf->GetTextureClient()); + + // If IPDLActor is null means this TextureClient didn't AddTextureClient yet + if (!grallocTextureClient->GetIPDLActor()) { + grallocTextureClient->SetTextureFlags(mTextureInfo.mTextureFlags); + AddTextureClient(grallocTextureClient); + } + + if (grallocTextureClient->GetIPDLActor()) { + GetForwarder()->UseTexture(this, grallocTextureClient); + } +#else + printf_stderr("isCrossProcess, but not MOZ_WIDGET_GONK! Someone needs to write some code!"); + MOZ_ASSERT(false); +#endif + } else { + if (!mBuffer) { + StreamTextureClientOGL* textureClient = + new StreamTextureClientOGL(mTextureInfo.mTextureFlags); + textureClient->InitWith(stream); + mBuffer = textureClient; + bufferCreated = true; + } + + if (bufferCreated && !AddTextureClient(mBuffer)) { + mBuffer = nullptr; + } + + if (mBuffer) { + GetForwarder()->UseTexture(this, mBuffer); + } + } + + aLayer->Painted(); +} + void DeprecatedCanvasClient2D::Updated() { mForwarder->UpdateTexture(this, 1, mDeprecatedTextureClient->LockSurfaceDescriptor()); } DeprecatedCanvasClient2D::DeprecatedCanvasClient2D(CompositableForwarder* aFwd, @@ -201,17 +264,18 @@ DeprecatedCanvasClientSurfaceStream::Upd #ifdef MOZ_WIDGET_GONK if (surf->Type() != SharedSurfaceType::Gralloc) { printf_stderr("Unexpected non-Gralloc SharedSurface in IPC path!"); return; } SharedSurface_Gralloc* grallocSurf = SharedSurface_Gralloc::Cast(surf); - mDeprecatedTextureClient->SetDescriptor(grallocSurf->GetDescriptor()); + //XXX todo + //mDeprecatedTextureClient->SetDescriptor(grallocSurf->GetDescriptor()); #else printf_stderr("isCrossProcess, but not MOZ_WIDGET_GONK! Someone needs to write some code!"); MOZ_ASSERT(false); #endif } else { SurfaceStreamHandle handle = stream->GetShareHandle(); mDeprecatedTextureClient->SetDescriptor(SurfaceStreamDescriptor(handle, false));
--- a/gfx/layers/client/CanvasClient.h +++ b/gfx/layers/client/CanvasClient.h @@ -14,16 +14,22 @@ #include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor #include "mozilla/layers/TextureClient.h" // for TextureClient, etc #include "mozilla/mozalloc.h" // for operator delete #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/gfx/Types.h" // for SurfaceFormat namespace mozilla { +namespace gfx { +class SharedSurface; +} +} + +namespace mozilla { namespace layers { class ClientCanvasLayer; class CompositableForwarder; /** * Compositable client for 2d and webgl canvas. */ @@ -90,16 +96,39 @@ public: { mBuffer = nullptr; } private: RefPtr<TextureClient> mBuffer; }; +// Used for GL canvases where we don't need to do any readback, i.e., with a +// GL backend. +class CanvasClientSurfaceStream : public CanvasClient +{ +public: + CanvasClientSurfaceStream(CompositableForwarder* aLayerForwarder, TextureFlags aFlags); + + TextureInfo GetTextureInfo() const + { + return TextureInfo(COMPOSITABLE_IMAGE); + } + + virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) MOZ_OVERRIDE; + + virtual void OnDetach() MOZ_OVERRIDE + { + mBuffer = nullptr; + } + +private: + RefPtr<TextureClient> mBuffer; +}; + class DeprecatedCanvasClient2D : public CanvasClient { public: DeprecatedCanvasClient2D(CompositableForwarder* aLayerForwarder, TextureFlags aFlags); TextureInfo GetTextureInfo() const MOZ_OVERRIDE {
--- a/gfx/layers/client/ClientCanvasLayer.h +++ b/gfx/layers/client/ClientCanvasLayer.h @@ -46,55 +46,56 @@ public: } virtual void SetVisibleRegion(const nsIntRegion& aRegion) { NS_ASSERTION(ClientManager()->InConstruction(), "Can only set properties in construction phase"); CanvasLayer::SetVisibleRegion(aRegion); } - + virtual void Initialize(const Data& aData); virtual void RenderLayer(); - + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { aAttrs = CanvasLayerAttributes(mFilter, mBounds); } virtual Layer* AsLayer() { return this; } virtual ShadowableLayer* AsShadowableLayer() { return this; } - + virtual void Disconnect() { mCanvasClient = nullptr; ClientLayer::Disconnect(); } virtual CompositableClient* GetCompositableClient() MOZ_OVERRIDE { return mCanvasClient; } protected: ClientLayerManager* ClientManager() { return static_cast<ClientLayerManager*>(mManager); } - + CanvasClientType GetCanvasClientType() { if (mGLContext) { return CanvasClient::CanvasClientGLContext; } return CanvasClient::CanvasClientSurface; } RefPtr<CanvasClient> mCanvasClient; friend class DeprecatedCanvasClient2D; friend class CanvasClient2D; friend class DeprecatedCanvasClientSurfaceStream; + friend class CanvasClientSurfaceStream; }; } } #endif
--- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -517,18 +517,18 @@ BufferTextureClient::AllocateForSurface( mSize = aSize; return true; } TemporaryRef<gfx::DrawTarget> BufferTextureClient::GetAsDrawTarget() { MOZ_ASSERT(IsValid()); - // XXX - uncomment when ContentClient's locking is fixed - // MOZ_ASSERT(mLocked); + // XXX - Turn this into a fatal assertion as soon as Bug 952507 is fixed + NS_WARN_IF_FALSE(mLocked, "GetAsDrawTarget should be called on locked textures only"); if (mDrawTarget) { return mDrawTarget; } ImageDataSerializer serializer(GetBuffer()); if (!serializer.IsValid()) { return nullptr; @@ -555,26 +555,28 @@ BufferTextureClient::GetAsDrawTarget() mDrawTarget->CopySurface(surface, rect, IntPoint(0,0)); } return mDrawTarget; } bool BufferTextureClient::Lock(OpenMode aMode) { - MOZ_ASSERT(!mLocked); + // XXX - Turn this into a fatal assertion as soon as Bug 952507 is fixed + NS_WARN_IF_FALSE(!mLocked, "The TextureClient is already Locked!"); mOpenMode = aMode; mLocked = true; return true; } void BufferTextureClient::Unlock() { - MOZ_ASSERT(mLocked); + // XXX - Turn this into a fatal assertion as soon as Bug 952507 is fixed + NS_WARN_IF_FALSE(mLocked, "The TextureClient is already Unlocked!"); mLocked = false; if (!mDrawTarget) { mUsingFallbackDrawTarget = false; return; } mDrawTarget->Flush(); if (mUsingFallbackDrawTarget && (mOpenMode & OPEN_WRITE)) {
--- a/gfx/layers/composite/CanvasLayerComposite.cpp +++ b/gfx/layers/composite/CanvasLayerComposite.cpp @@ -65,23 +65,17 @@ CanvasLayerComposite::RenderLayer(const if (!mImageHost || !mImageHost->IsAttached()) { return; } mCompositor->MakeCurrent(); #ifdef MOZ_DUMP_PAINTING if (gfxUtils::sDumpPainting) { - RefPtr<gfx::DataSourceSurface> dSurf = mImageHost->GetAsSurface(); - gfxPlatform *platform = gfxPlatform::GetPlatform(); - RefPtr<gfx::DrawTarget> dt = platform->CreateDrawTargetForData(dSurf->GetData(), - dSurf->GetSize(), - dSurf->Stride(), - dSurf->GetFormat()); - nsRefPtr<gfxASurface> surf = platform->GetThebesSurfaceForDrawTarget(dt); + RefPtr<gfx::DataSourceSurface> surf = mImageHost->GetAsSurface(); WriteSnapshotToDumpFile(this, surf); } #endif GraphicsFilter filter = mFilter; #ifdef ANDROID // Bug 691354 // Using the LINEAR filter we get unexplained artifacts.
--- a/gfx/layers/composite/ContainerLayerComposite.cpp +++ b/gfx/layers/composite/ContainerLayerComposite.cpp @@ -328,17 +328,17 @@ ContainerRender(ContainerT* aContainer, // invariant: our GL context should be current here, I don't think we can // assert it though } if (needsSurface) { // Unbind the current surface and rebind the previous one. #ifdef MOZ_DUMP_PAINTING if (gfxUtils::sDumpPainting) { - nsRefPtr<gfxImageSurface> surf = surface->Dump(aManager->GetCompositor()); + RefPtr<gfx::DataSourceSurface> surf = surface->Dump(aManager->GetCompositor()); WriteSnapshotToDumpFile(aContainer, surf); } #endif compositor->SetRenderTarget(previousTarget); EffectChain effectChain; LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(aContainer->GetMaskLayer(), effectChain,
--- a/gfx/layers/composite/ImageLayerComposite.cpp +++ b/gfx/layers/composite/ImageLayerComposite.cpp @@ -79,23 +79,17 @@ void ImageLayerComposite::RenderLayer(const nsIntRect& aClipRect) { if (!mImageHost || !mImageHost->IsAttached()) { return; } #ifdef MOZ_DUMP_PAINTING if (gfxUtils::sDumpPainting) { - RefPtr<gfx::DataSourceSurface> dSurf = mImageHost->GetAsSurface(); - gfxPlatform *platform = gfxPlatform::GetPlatform(); - RefPtr<gfx::DrawTarget> dt = platform->CreateDrawTargetForData(dSurf->GetData(), - dSurf->GetSize(), - dSurf->Stride(), - dSurf->GetFormat()); - nsRefPtr<gfxASurface> surf = platform->GetThebesSurfaceForDrawTarget(dt); + RefPtr<gfx::DataSourceSurface> surf = mImageHost->GetAsSurface(); WriteSnapshotToDumpFile(this, surf); } #endif mCompositor->MakeCurrent(); EffectChain effectChain; LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(mMaskLayer, effectChain);
--- a/gfx/layers/composite/TextureHost.h +++ b/gfx/layers/composite/TextureHost.h @@ -838,17 +838,17 @@ class CompositingRenderTarget : public T { public: CompositingRenderTarget(const gfx::IntPoint& aOrigin) : mOrigin(aOrigin) {} virtual ~CompositingRenderTarget() {} #ifdef MOZ_DUMP_PAINTING - virtual already_AddRefed<gfxImageSurface> Dump(Compositor* aCompositor) { return nullptr; } + virtual TemporaryRef<gfx::DataSourceSurface> Dump(Compositor* aCompositor) { return nullptr; } #endif const gfx::IntPoint& GetOrigin() { return mOrigin; } private: gfx::IntPoint mOrigin; };
--- a/gfx/layers/composite/ThebesLayerComposite.cpp +++ b/gfx/layers/composite/ThebesLayerComposite.cpp @@ -106,24 +106,18 @@ ThebesLayerComposite::RenderLayer(const "buffer is corrupted"); gfx::Matrix4x4 transform; ToMatrix4x4(GetEffectiveTransform(), transform); gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height); #ifdef MOZ_DUMP_PAINTING if (gfxUtils::sDumpPainting) { - RefPtr<gfx::DataSourceSurface> dSurf = mBuffer->GetAsSurface(); - if (dSurf) { - gfxPlatform *platform = gfxPlatform::GetPlatform(); - RefPtr<gfx::DrawTarget> dt = platform->CreateDrawTargetForData(dSurf->GetData(), - dSurf->GetSize(), - dSurf->Stride(), - dSurf->GetFormat()); - nsRefPtr<gfxASurface> surf = platform->GetThebesSurfaceForDrawTarget(dt); + RefPtr<gfx::DataSourceSurface> surf = mBuffer->GetAsSurface(); + if (surf) { WriteSnapshotToDumpFile(this, surf); } } #endif EffectChain effectChain; LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(mMaskLayer, effectChain);
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -128,17 +128,17 @@ namespace mozilla { namespace layers { /** * Constant describing the tolerance in distance we use, multiplied by the * device DPI, before we start panning the screen. This is to prevent us from * accidentally processing taps as touch moves, and from very short/accidental * touches moving the screen. */ -static float gTouchStartTolerance = 1.0f/16.0f; +static float gTouchStartTolerance = 1.0f/2.0f; /** * Angle from axis within which we stay axis-locked */ static const double AXIS_LOCK_ANGLE = M_PI / 6.0; // 30 degrees /** * The distance in inches the user must pan before axis lock can be broken
--- a/gfx/layers/opengl/CompositingRenderTargetOGL.cpp +++ b/gfx/layers/opengl/CompositingRenderTargetOGL.cpp @@ -1,18 +1,21 @@ /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * 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 "CompositingRenderTargetOGL.h" #include "GLContext.h" #include "GLReadTexImageHelper.h" +#include "mozilla/gfx/2D.h" using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::gl; using namespace mozilla::layers; CompositingRenderTargetOGL::~CompositingRenderTargetOGL() { mGL->fDeleteTextures(1, &mTextureHandle); mGL->fDeleteFramebuffers(1, &mFBO); } @@ -52,22 +55,22 @@ CompositingRenderTargetOGL::BindRenderTa } } mCompositor->PrepareViewport(mInitParams.mSize, mTransform); } } #ifdef MOZ_DUMP_PAINTING -already_AddRefed<gfxImageSurface> +TemporaryRef<DataSourceSurface> CompositingRenderTargetOGL::Dump(Compositor* aCompositor) { MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED); CompositorOGL* compositorOGL = static_cast<CompositorOGL*>(aCompositor); - return GetTexImage(mGL, mTextureHandle, true, compositorOGL->GetFBOFormat()); + return ReadBackSurface(mGL, mTextureHandle, true, compositorOGL->GetFBOFormat()); } #endif void CompositingRenderTargetOGL::InitializeImpl() { MOZ_ASSERT(mInitParams.mStatus == InitParams::READY);
--- a/gfx/layers/opengl/CompositingRenderTargetOGL.h +++ b/gfx/layers/opengl/CompositingRenderTargetOGL.h @@ -24,16 +24,19 @@ #include "nsString.h" // for nsAutoCString class gfxImageSurface; namespace mozilla { namespace gl { class BindableTexture; } +namespace gfx { + class DataSourceSurface; +} namespace layers { class TextureSource; class CompositingRenderTargetOGL : public CompositingRenderTarget { typedef mozilla::gl::GLContext GLContext; @@ -147,17 +150,17 @@ public: return gfx::FORMAT_UNKNOWN; } const gfxMatrix& GetTransform() { return mTransform; } #ifdef MOZ_DUMP_PAINTING - virtual already_AddRefed<gfxImageSurface> Dump(Compositor* aCompositor); + virtual TemporaryRef<gfx::DataSourceSurface> Dump(Compositor* aCompositor); #endif private: /** * Actually do the initialisation. Note that we leave our FBO bound, and so * calling this method is only suitable when about to use this render target. */ void InitializeImpl();
--- a/gfx/layers/opengl/GrallocTextureClient.cpp +++ b/gfx/layers/opengl/GrallocTextureClient.cpp @@ -88,49 +88,63 @@ GrallocTextureClientOGL::DropTextureData return result; } } GrallocTextureClientOGL::GrallocTextureClientOGL(GrallocBufferActor* aActor, gfx::IntSize aSize, TextureFlags aFlags) : BufferTextureClient(nullptr, gfx::FORMAT_UNKNOWN, aFlags) +, mAllocator(nullptr) , mGrallocFlags(android::GraphicBuffer::USAGE_SW_READ_OFTEN) , mMappedBuffer(nullptr) { InitWith(aActor, aSize); MOZ_COUNT_CTOR(GrallocTextureClientOGL); } GrallocTextureClientOGL::GrallocTextureClientOGL(CompositableClient* aCompositable, gfx::SurfaceFormat aFormat, TextureFlags aFlags) : BufferTextureClient(aCompositable, aFormat, aFlags) +, mAllocator(nullptr) +, mGrallocFlags(android::GraphicBuffer::USAGE_SW_READ_OFTEN) +, mMappedBuffer(nullptr) +{ + MOZ_COUNT_CTOR(GrallocTextureClientOGL); +} + +GrallocTextureClientOGL::GrallocTextureClientOGL(ISurfaceAllocator* aAllocator, + gfx::SurfaceFormat aFormat, + TextureFlags aFlags) +: BufferTextureClient(nullptr, aFormat, aFlags) +, mAllocator(aAllocator) , mGrallocFlags(android::GraphicBuffer::USAGE_SW_READ_OFTEN) , mMappedBuffer(nullptr) { MOZ_COUNT_CTOR(GrallocTextureClientOGL); } GrallocTextureClientOGL::~GrallocTextureClientOGL() { MOZ_COUNT_DTOR(GrallocTextureClientOGL); if (ShouldDeallocateInDestructor()) { // If the buffer has never been shared we must deallocate it or it would // leak. if (mBufferLocked) { mBufferLocked->Unlock(); } else { - MOZ_ASSERT(mCompositable); // We just need to wrap the actor in a SurfaceDescriptor because that's what // ISurfaceAllocator uses as input, we don't care about the other parameters. SurfaceDescriptor sd = SurfaceDescriptorGralloc(nullptr, mGrallocActor, IntSize(0, 0), false, false); - mCompositable->GetForwarder()->DestroySharedSurface(&sd); + + ISurfaceAllocator* allocator = GetAllocator(); + allocator->DestroySharedSurface(&sd); } } } void GrallocTextureClientOGL::InitWith(GrallocBufferActor* aActor, gfx::IntSize aSize) { MOZ_ASSERT(aActor); @@ -190,18 +204,16 @@ GrallocTextureClientOGL::GetBuffer() con return mMappedBuffer; } bool GrallocTextureClientOGL::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags) { MOZ_ASSERT(IsValid()); - MOZ_ASSERT(mCompositable); - ISurfaceAllocator* allocator = mCompositable->GetForwarder(); uint32_t format; uint32_t usage = android::GraphicBuffer::USAGE_SW_READ_OFTEN; bool swapRB = GetFlags() & TEXTURE_RB_SWAPPED; switch (mFormat) { case gfx::FORMAT_R8G8B8A8: format = swapRB ? android::PIXEL_FORMAT_BGRA_8888 : android::PIXEL_FORMAT_RGBA_8888; @@ -233,23 +245,55 @@ GrallocTextureClientOGL::AllocateForYCbC { MOZ_ASSERT(IsValid()); return AllocateGralloc(aYSize, HAL_PIXEL_FORMAT_YV12, android::GraphicBuffer::USAGE_SW_READ_OFTEN); } bool +GrallocTextureClientOGL::AllocateForGLRendering(gfx::IntSize aSize) +{ + MOZ_ASSERT(IsValid()); + + uint32_t format; + uint32_t usage = android::GraphicBuffer::USAGE_HW_RENDER | + android::GraphicBuffer::USAGE_HW_TEXTURE; + + switch (mFormat) { + case gfx::FORMAT_R8G8B8A8: + case gfx::FORMAT_B8G8R8A8: + format = android::PIXEL_FORMAT_RGBA_8888; + break; + case gfx::FORMAT_R8G8B8X8: + case gfx::FORMAT_B8G8R8X8: + // there is no android BGRX format? + format = android::PIXEL_FORMAT_RGBX_8888; + break; + case gfx::FORMAT_R5G6B5: + format = android::PIXEL_FORMAT_RGB_565; + break; + case gfx::FORMAT_A8: + format = android::PIXEL_FORMAT_A_8; + break; + default: + NS_WARNING("Unsupported surface format"); + return false; + } + + return AllocateGralloc(aSize, format, usage); +} + +bool GrallocTextureClientOGL::AllocateGralloc(gfx::IntSize aSize, uint32_t aAndroidFormat, uint32_t aUsage) { MOZ_ASSERT(IsValid()); - MOZ_ASSERT(mCompositable); - ISurfaceAllocator* allocator = mCompositable->GetForwarder(); + ISurfaceAllocator* allocator = GetAllocator(); MaybeMagicGrallocBufferHandle handle; PGrallocBufferChild* actor = allocator->AllocGrallocBuffer(aSize, aAndroidFormat, aUsage, &handle); if (!actor) { @@ -290,12 +334,21 @@ GrallocTextureClientOGL::Allocate(uint32 size_t GrallocTextureClientOGL::GetBufferSize() const { // see Bug 908196 MOZ_CRASH("This method should never be called."); return 0; } +ISurfaceAllocator* +GrallocTextureClientOGL::GetAllocator() +{ + MOZ_ASSERT(mCompositable || mAllocator); + return mCompositable ? + mCompositable->GetForwarder() : + mAllocator; +} + } // namesapace layers } // namesapace mozilla #endif // MOZ_WIDGET_GONK
--- a/gfx/layers/opengl/GrallocTextureClient.h +++ b/gfx/layers/opengl/GrallocTextureClient.h @@ -34,16 +34,19 @@ class GrallocTextureClientOGL : public B { public: GrallocTextureClientOGL(GrallocBufferActor* aActor, gfx::IntSize aSize, TextureFlags aFlags = TEXTURE_FLAGS_DEFAULT); GrallocTextureClientOGL(CompositableClient* aCompositable, gfx::SurfaceFormat aFormat, TextureFlags aFlags = TEXTURE_FLAGS_DEFAULT); + GrallocTextureClientOGL(ISurfaceAllocator* aAllocator, + gfx::SurfaceFormat aFormat, + TextureFlags aFlags = TEXTURE_FLAGS_DEFAULT); ~GrallocTextureClientOGL(); virtual bool Lock(OpenMode aMode) MOZ_OVERRIDE; virtual void Unlock() MOZ_OVERRIDE; virtual bool ImplementsLocking() const MOZ_OVERRIDE { return true; } @@ -51,16 +54,18 @@ public: virtual bool IsAllocated() const MOZ_OVERRIDE; virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE; virtual TextureClientData* DropTextureData() MOZ_OVERRIDE; void InitWith(GrallocBufferActor* aActor, gfx::IntSize aSize); + void SetTextureFlags(TextureFlags aFlags) { AddFlags(aFlags); } + gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; } android::GraphicBuffer* GetGraphicBuffer() { return mGraphicBuffer.get(); } android::PixelFormat GetPixelFormat() @@ -82,35 +87,40 @@ public: virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags = ALLOC_DEFAULT) MOZ_OVERRIDE; virtual bool AllocateForYCbCr(gfx::IntSize aYSize, gfx::IntSize aCbCrSize, StereoMode aStereoMode) MOZ_OVERRIDE; + bool AllocateForGLRendering(gfx::IntSize aSize); + bool AllocateGralloc(gfx::IntSize aYSize, uint32_t aAndroidFormat, uint32_t aUsage); virtual bool Allocate(uint32_t aSize) MOZ_OVERRIDE; virtual size_t GetBufferSize() const MOZ_OVERRIDE; void SetGraphicBufferLocked(GraphicBufferLocked* aBufferLocked); protected: + ISurfaceAllocator* GetAllocator(); /** * Unfortunately, until bug 879681 is fixed we need to use a GrallocBufferActor. */ GrallocBufferActor* mGrallocActor; RefPtr<GraphicBufferLocked> mBufferLocked; android::sp<android::GraphicBuffer> mGraphicBuffer; + RefPtr<ISurfaceAllocator> mAllocator; + /** * Flags that are used when locking the gralloc buffer */ uint32_t mGrallocFlags; /** * Points to a mapped gralloc buffer between calls to lock and unlock. * Should be null outside of the lock-unlock pair. */
--- a/gfx/layers/opengl/TextureClientOGL.cpp +++ b/gfx/layers/opengl/TextureClientOGL.cpp @@ -1,17 +1,18 @@ /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * 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 "mozilla/layers/TextureClientOGL.h" #include "GLContext.h" // for GLContext, etc +#include "SurfaceStream.h" #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/layers/ISurfaceAllocator.h" +#include "mozilla/layers/TextureClientOGL.h" #include "nsSize.h" // for nsIntSize using namespace mozilla::gl; namespace mozilla { namespace layers { class CompositableForwarder; @@ -60,16 +61,52 @@ SharedTextureClientOGL::InitWith(gl::Sha } bool SharedTextureClientOGL::IsAllocated() const { return mHandle != 0; } +StreamTextureClientOGL::StreamTextureClientOGL(TextureFlags aFlags) + : TextureClient(aFlags) + , mStream(0) +{ +} + +StreamTextureClientOGL::~StreamTextureClientOGL() +{ + // the data is owned externally. +} + +bool +StreamTextureClientOGL::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) +{ + if (!IsAllocated()) { + return false; + } + + gfx::SurfaceStreamHandle handle = mStream->GetShareHandle(); + aOutDescriptor = SurfaceStreamDescriptor(handle, false); + return true; +} + +void +StreamTextureClientOGL::InitWith(gfx::SurfaceStream* aStream) +{ + MOZ_ASSERT(!IsAllocated()); + mStream = aStream; +} + +bool +StreamTextureClientOGL::IsAllocated() const +{ + return mStream != 0; +} + DeprecatedTextureClientSharedOGL::DeprecatedTextureClientSharedOGL(CompositableForwarder* aForwarder, const TextureInfo& aTextureInfo) : DeprecatedTextureClient(aForwarder, aTextureInfo) , mGL(nullptr) { } void
--- a/gfx/layers/opengl/TextureClientOGL.h +++ b/gfx/layers/opengl/TextureClientOGL.h @@ -10,16 +10,22 @@ #include "gfxTypes.h" #include "mozilla/Attributes.h" // for MOZ_OVERRIDE #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/layers/CompositorTypes.h" #include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor #include "mozilla/layers/TextureClient.h" // for DeprecatedTextureClient, etc namespace mozilla { +namespace gfx { +class SurfaceStream; +} +} + +namespace mozilla { namespace layers { class CompositableForwarder; /** * A TextureClient implementation to share TextureMemory that is already * on the GPU, for the OpenGL backend. */ @@ -52,16 +58,40 @@ public: protected: gl::SharedTextureHandle mHandle; gfx::IntSize mSize; gl::SharedTextureShareType mShareType; bool mInverted; }; +/** + * A TextureClient implementation to share SurfaceStream. + */ +class StreamTextureClientOGL : public TextureClient +{ +public: + StreamTextureClientOGL(TextureFlags aFlags); + + ~StreamTextureClientOGL(); + + virtual bool IsAllocated() const MOZ_OVERRIDE; + + virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE; + + virtual TextureClientData* DropTextureData() MOZ_OVERRIDE { return nullptr; } + + void InitWith(gfx::SurfaceStream* aStream); + + virtual gfx::IntSize GetSize() const { return gfx::IntSize(); } + +protected: + gfx::SurfaceStream* mStream; +}; + class DeprecatedTextureClientSharedOGL : public DeprecatedTextureClient { public: DeprecatedTextureClientSharedOGL(CompositableForwarder* aForwarder, const TextureInfo& aTextureInfo); ~DeprecatedTextureClientSharedOGL() { ReleaseResources(); } virtual bool SupportsType(DeprecatedTextureClientType aType) MOZ_OVERRIDE { return aType == TEXTURE_SHARED_GL; } virtual bool EnsureAllocated(gfx::IntSize aSize, gfxContentType aType);
--- a/gfx/layers/opengl/TextureHostOGL.cpp +++ b/gfx/layers/opengl/TextureHostOGL.cpp @@ -103,16 +103,21 @@ CreateTextureHostOGL(const SurfaceDescri const SharedTextureDescriptor& desc = aDesc.get_SharedTextureDescriptor(); result = new SharedTextureHostOGL(aFlags, desc.shareType(), desc.handle(), desc.size(), desc.inverted()); break; } + case SurfaceDescriptor::TSurfaceStreamDescriptor: { + const SurfaceStreamDescriptor& desc = aDesc.get_SurfaceStreamDescriptor(); + result = new StreamTextureHostOGL(aFlags, desc); + break; + } #ifdef XP_MACOSX case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: { const SurfaceDescriptorMacIOSurface& desc = aDesc.get_SurfaceDescriptorMacIOSurface(); result = new MacIOSurfaceTextureHostOGL(aFlags, desc); break; } #endif @@ -458,16 +463,194 @@ SharedTextureHostOGL::SetCompositor(Comp gfx::SurfaceFormat SharedTextureHostOGL::GetFormat() const { MOZ_ASSERT(mTextureSource); return mTextureSource->GetFormat(); } +void +StreamTextureSourceOGL::BindTexture(GLenum activetex) +{ + MOZ_ASSERT(gl()); + gl()->fActiveTexture(activetex); + gl()->fBindTexture(mTextureTarget, mTextureHandle); +} + +bool +StreamTextureSourceOGL::RetrieveTextureFromStream() +{ + gl()->MakeCurrent(); + + SharedSurface* sharedSurf = mStream->SwapConsumer(); + if (!sharedSurf) { + // We don't have a valid surf to show yet. + return false; + } + + gl()->MakeCurrent(); + + mSize = IntSize(sharedSurf->Size().width, sharedSurf->Size().height); + + DataSourceSurface* toUpload = nullptr; + switch (sharedSurf->Type()) { + case SharedSurfaceType::GLTextureShare: { + SharedSurface_GLTexture* glTexSurf = SharedSurface_GLTexture::Cast(sharedSurf); + glTexSurf->SetConsumerGL(gl()); + mTextureHandle = glTexSurf->Texture(); + mTextureTarget = glTexSurf->TextureTarget(); + MOZ_ASSERT(mTextureHandle); + mFormat = sharedSurf->HasAlpha() ? FORMAT_R8G8B8A8 + : FORMAT_R8G8B8X8; + break; + } + case SharedSurfaceType::EGLImageShare: { + SharedSurface_EGLImage* eglImageSurf = + SharedSurface_EGLImage::Cast(sharedSurf); + + mTextureHandle = eglImageSurf->AcquireConsumerTexture(gl()); + mTextureTarget = eglImageSurf->TextureTarget(); + if (!mTextureHandle) { + toUpload = eglImageSurf->GetPixels(); + MOZ_ASSERT(toUpload); + } else { + mFormat = sharedSurf->HasAlpha() ? FORMAT_R8G8B8A8 + : FORMAT_R8G8B8X8; + } + break; + } +#ifdef XP_MACOSX + case SharedSurfaceType::IOSurface: { + SharedSurface_IOSurface* glTexSurf = SharedSurface_IOSurface::Cast(sharedSurf); + mTextureHandle = glTexSurf->Texture(); + mTextureTarget = glTexSurf->TextureTarget(); + MOZ_ASSERT(mTextureHandle); + mFormat = sharedSurf->HasAlpha() ? FORMAT_R8G8B8A8 + : FORMAT_R8G8B8X8; + break; + } +#endif + case SharedSurfaceType::Basic: { + toUpload = SharedSurface_Basic::Cast(sharedSurf)->GetData(); + MOZ_ASSERT(toUpload); + break; + } + default: + MOZ_CRASH("Invalid SharedSurface type."); + } + + if (toUpload) { + // mBounds seems to end up as (0,0,0,0) a lot, so don't use it? + nsIntSize size(ThebesIntSize(toUpload->GetSize())); + nsIntRect rect(nsIntPoint(0,0), size); + nsIntRegion bounds(rect); + mFormat = UploadSurfaceToTexture(gl(), + toUpload, + bounds, + mUploadTexture, + true); + mTextureHandle = mUploadTexture; + mTextureTarget = LOCAL_GL_TEXTURE_2D; + } + + MOZ_ASSERT(mTextureHandle); + gl()->fBindTexture(mTextureTarget, mTextureHandle); + gl()->fTexParameteri(mTextureTarget, + LOCAL_GL_TEXTURE_WRAP_S, + LOCAL_GL_CLAMP_TO_EDGE); + gl()->fTexParameteri(mTextureTarget, + LOCAL_GL_TEXTURE_WRAP_T, + LOCAL_GL_CLAMP_TO_EDGE); + + return true; +} + +void +StreamTextureSourceOGL::DeallocateDeviceData() +{ + if (mUploadTexture) { + MOZ_ASSERT(gl()); + gl()->MakeCurrent(); + gl()->fDeleteTextures(1, &mUploadTexture); + mUploadTexture = 0; + mTextureHandle = 0; + } +} + +gl::GLContext* +StreamTextureSourceOGL::gl() const +{ + return mCompositor ? mCompositor->gl() : nullptr; +} + +void +StreamTextureSourceOGL::SetCompositor(Compositor* aCompositor) +{ + mCompositor = static_cast<CompositorOGL*>(aCompositor); +} + +StreamTextureHostOGL::StreamTextureHostOGL(TextureFlags aFlags, + const SurfaceStreamDescriptor& aDesc) + : TextureHost(aFlags) +{ + mStream = SurfaceStream::FromHandle(aDesc.handle()); + MOZ_ASSERT(mStream); +} + +StreamTextureHostOGL::~StreamTextureHostOGL() +{ + // If need to deallocate textures, call DeallocateSharedData() before + // the destructor +} + +bool +StreamTextureHostOGL::Lock() +{ + if (!mCompositor) { + return false; + } + + if (!mTextureSource) { + mTextureSource = new StreamTextureSourceOGL(mCompositor, + mStream); + } + + return mTextureSource->RetrieveTextureFromStream(); +} + +void +StreamTextureHostOGL::Unlock() +{ +} + +void +StreamTextureHostOGL::SetCompositor(Compositor* aCompositor) +{ + CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor); + mCompositor = glCompositor; + if (mTextureSource) { + mTextureSource->SetCompositor(glCompositor); + } +} + +gfx::SurfaceFormat +StreamTextureHostOGL::GetFormat() const +{ + MOZ_ASSERT(mTextureSource); + return mTextureSource->GetFormat(); +} + +gfx::IntSize +StreamTextureHostOGL::GetSize() const +{ + MOZ_ASSERT(mTextureSource); + return mTextureSource->GetSize(); +} + TextureImageDeprecatedTextureHostOGL::~TextureImageDeprecatedTextureHostOGL() { MOZ_COUNT_DTOR(TextureImageDeprecatedTextureHostOGL); if (mTexture && mTexture->InUpdate()) { mTexture->EndUpdate(); } }
--- a/gfx/layers/opengl/TextureHostOGL.h +++ b/gfx/layers/opengl/TextureHostOGL.h @@ -331,16 +331,118 @@ protected: CompositorOGL* mCompositor; gl::SharedTextureHandle mSharedHandle; gl::SharedTextureShareType mShareType; RefPtr<SharedTextureSourceOGL> mTextureSource; }; /** + * A texture source meant for use with StreamTextureHostOGL. + * + * It does not own any texture, we get texture from SurfaceStream. + */ +class StreamTextureSourceOGL : public NewTextureSource + , public TextureSourceOGL +{ +public: + StreamTextureSourceOGL(CompositorOGL* aCompositor, + gfx::SurfaceStream* aStream) + : mCompositor(aCompositor) + , mStream(aStream) + , mTextureHandle(0) + , mTextureTarget(LOCAL_GL_TEXTURE_2D) + , mUploadTexture(0) + , mFormat(gfx::FORMAT_UNKNOWN) + { + MOZ_COUNT_CTOR(StreamTextureSourceOGL); + } + + ~StreamTextureSourceOGL() + { + MOZ_COUNT_DTOR(StreamTextureSourceOGL); + } + + virtual TextureSourceOGL* AsSourceOGL() { return this; } + + virtual void BindTexture(GLenum activetex) MOZ_OVERRIDE; + + virtual bool IsValid() const MOZ_OVERRIDE { return !!gl(); } + + virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; } + + virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; } + + virtual GLenum GetTextureTarget() const { return mTextureTarget; } + + virtual GLenum GetWrapMode() const MOZ_OVERRIDE { return LOCAL_GL_CLAMP_TO_EDGE; } + + virtual void DeallocateDeviceData(); + + bool RetrieveTextureFromStream(); + + virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE; + +protected: + gl::GLContext* gl() const; + + CompositorOGL* mCompositor; + gfx::SurfaceStream* mStream; + GLuint mTextureHandle; + GLenum mTextureTarget; + GLuint mUploadTexture; + gfx::IntSize mSize; + gfx::SurfaceFormat mFormat; +}; + +/** + * A TextureHost for shared SurfaceStream + */ +class StreamTextureHostOGL : public TextureHost +{ +public: + StreamTextureHostOGL(TextureFlags aFlags, + const SurfaceStreamDescriptor& aDesc); + + virtual ~StreamTextureHostOGL(); + + // SharedTextureHostOGL doesn't own any GL texture + virtual void DeallocateDeviceData() MOZ_OVERRIDE {} + + virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE; + + virtual bool Lock() MOZ_OVERRIDE; + + virtual void Unlock() MOZ_OVERRIDE; + + virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE; + + virtual NewTextureSource* GetTextureSources() MOZ_OVERRIDE + { + return mTextureSource; + } + + virtual TemporaryRef<gfx::DataSourceSurface> GetAsSurface() MOZ_OVERRIDE + { + return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING) + } + + virtual gfx::IntSize GetSize() const MOZ_OVERRIDE; + +#ifdef MOZ_LAYERS_HAVE_LOG + virtual const char* Name() { return "StreamTextureHostOGL"; } +#endif + +protected: + CompositorOGL* mCompositor; + gfx::SurfaceStream* mStream; + RefPtr<StreamTextureSourceOGL> mTextureSource; +}; + +/** * DeprecatedTextureHost implementation using a TextureImage as the underlying texture. */ class TextureImageDeprecatedTextureHostOGL : public DeprecatedTextureHost , public TextureSourceOGL , public TileIterator { public: TextureImageDeprecatedTextureHostOGL(gl::TextureImage* aTexImage = nullptr)
new file mode 100644 --- /dev/null +++ b/gfx/tests/gtest/TestSkipChars.cpp @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "gtest/gtest.h" + +#include "gfxSkipChars.h" +#include "mozilla/ArrayUtils.h" + +static bool +TestConstructor() +{ + gfxSkipChars skipChars; + + EXPECT_TRUE(skipChars.GetOriginalCharCount() == 0) << + "[1] Make sure the gfxSkipChars was properly initialized with constructor"; + + return true; +} + +static bool +TestLength() +{ + gfxSkipChars skipChars; + + skipChars.KeepChars(100); + + EXPECT_TRUE(skipChars.GetOriginalCharCount() == 100) << + "[1] Check length after keeping chars"; + + skipChars.SkipChars(50); + + EXPECT_TRUE(skipChars.GetOriginalCharCount() == 150) << + "[2] Check length after skipping chars"; + + skipChars.SkipChars(50); + + EXPECT_TRUE(skipChars.GetOriginalCharCount() == 200) << + "[3] Check length after skipping more chars"; + + skipChars.KeepChar(); + + EXPECT_TRUE(skipChars.GetOriginalCharCount() == 201) << + "[4] Check length after keeping a final char"; + + return true; +} + +static bool +TestIterator() +{ + // Test a gfxSkipChars that starts with kept chars + gfxSkipChars skipChars1; + + skipChars1.KeepChars(9); + skipChars1.SkipChar(); + skipChars1.KeepChars(9); + skipChars1.SkipChar(); + skipChars1.KeepChars(9); + + EXPECT_TRUE(skipChars1.GetOriginalCharCount() == 29) << + "[1] Check length"; + + gfxSkipCharsIterator iter1(skipChars1); + + EXPECT_TRUE(iter1.GetOriginalOffset() == 0) << + "[2] Check initial original offset"; + EXPECT_TRUE(iter1.GetSkippedOffset() == 0) << + "[3] Check initial skipped offset"; + + uint32_t expectSkipped1[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 }; + + for (uint32_t i = 0; i < mozilla::ArrayLength(expectSkipped1); i++) { + EXPECT_TRUE(iter1.ConvertOriginalToSkipped(i) == expectSkipped1[i]) << + "[4] Check mapping of original to skipped for " << i; + } + + uint32_t expectOriginal1[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, + 10, 11, 12, 13, 14, 15, 16, 17, 18, + 20, 21, 22, 23, 24, 25, 26, 27, 28 }; + + for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal1); i++) { + EXPECT_TRUE(iter1.ConvertSkippedToOriginal(i) == expectOriginal1[i]) << + "[5] Check mapping of skipped to original for " << i; + } + + // Test a gfxSkipChars that starts with skipped chars + gfxSkipChars skipChars2; + + skipChars2.SkipChars(9); + skipChars2.KeepChar(); + skipChars2.SkipChars(9); + skipChars2.KeepChar(); + skipChars2.SkipChars(9); + + EXPECT_TRUE(skipChars2.GetOriginalCharCount() == 29) << + "[6] Check length"; + + gfxSkipCharsIterator iter2(skipChars2); + + EXPECT_TRUE(iter2.GetOriginalOffset() == 0) << + "[7] Check initial original offset"; + EXPECT_TRUE(iter2.GetSkippedOffset() == 0) << + "[8] Check initial skipped offset"; + + uint32_t expectSkipped2[] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + + for (uint32_t i = 0; i < mozilla::ArrayLength(expectSkipped2); i++) { + EXPECT_TRUE(iter2.ConvertOriginalToSkipped(i) == expectSkipped2[i]) << + "[9] Check mapping of original to skipped for " << i; + } + + uint32_t expectOriginal2[] = { 9, 19, 29 }; + + for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal2); i++) { + EXPECT_TRUE(iter2.ConvertSkippedToOriginal(i) == expectOriginal2[i]) << + "[10] Check mapping of skipped to original for " << i; + } + + return true; +} + +TEST(Gfx, gfxSkipChars) { + TestConstructor(); + TestLength(); + TestIterator(); +}
--- a/gfx/tests/gtest/moz.build +++ b/gfx/tests/gtest/moz.build @@ -10,16 +10,17 @@ UNIFIED_SOURCES += [ 'gfxSurfaceRefCountTest.cpp', # Disabled on suspicion of causing bug 904227 #'gfxWordCacheTest.cpp', 'TestAsyncPanZoomController.cpp', 'TestBufferRotation.cpp', 'TestColorNames.cpp', 'TestLayers.cpp', 'TestRegion.cpp', + 'TestSkipChars.cpp', # Hangs on linux in ApplyGdkScreenFontOptions #'gfxFontSelectionTest.cpp', 'TestTextures.cpp', # Test works but it doesn't assert anything #'gfxTextRunPerfTest.cpp', 'TestTiledLayerBuffer.cpp', ]
--- a/gfx/thebes/gfxPattern.cpp +++ b/gfx/thebes/gfxPattern.cpp @@ -174,18 +174,21 @@ Pattern* gfxPattern::GetPattern(DrawTarget *aTarget, Matrix *aPatternTransform) { if (mGfxPattern) { mGfxPattern->~Pattern(); mGfxPattern = nullptr; } if (!mPattern) { + Matrix adjustedMatrix = mTransform; + if (aPatternTransform) + AdjustTransformForPattern(adjustedMatrix, aTarget->GetTransform(), aPatternTransform); mGfxPattern = new (mSurfacePattern.addr()) - SurfacePattern(mSourceSurface, ToExtendMode(mExtend), mTransform, mFilter); + SurfacePattern(mSourceSurface, ToExtendMode(mExtend), adjustedMatrix, mFilter); return mGfxPattern; } GraphicsExtend extend = (GraphicsExtend)cairo_pattern_get_extend(mPattern); switch (cairo_pattern_get_type(mPattern)) { case CAIRO_PATTERN_TYPE_SOLID: {
--- a/gfx/thebes/gfxSkipChars.cpp +++ b/gfx/thebes/gfxSkipChars.cpp @@ -1,231 +1,140 @@ /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "gfxSkipChars.h" -#include <stdlib.h> -#include <algorithm> - -#define SHORTCUT_FREQUENCY 256 - -// Even numbered list entries are "keep" entries -static bool -IsKeepEntry(uint32_t aEntry) +void +gfxSkipCharsIterator::SetOriginalOffset(int32_t aOffset) { - return !(aEntry & 1); -} + aOffset += mOriginalStringToSkipCharsOffset; + NS_ASSERTION(uint32_t(aOffset) <= mSkipChars->mCharCount, + "Invalid offset"); -void -gfxSkipChars::BuildShortcuts() -{ - if (!mList || mCharCount < SHORTCUT_FREQUENCY) - return; - - mShortcuts = new Shortcut[mCharCount/SHORTCUT_FREQUENCY]; - if (!mShortcuts) + mOriginalStringOffset = aOffset; + + uint32_t rangeCount = mSkipChars->mRanges.Length(); + if (rangeCount == 0) { + mSkippedStringOffset = aOffset; return; - - uint32_t i; - uint32_t nextShortcutIndex = 0; - uint32_t originalCharOffset = 0; - uint32_t skippedCharOffset = 0; - for (i = 0; i < mListLength; ++i) { - uint8_t len = mList[i]; - - // We use >= here to ensure that when mCharCount is a multiple of - // SHORTCUT_FREQUENCY, we fill in the final shortcut with a reference - // to the last element of mList. This means that in general when a list - // element ends on an offset that's a multiple of SHORTCUT_FREQUENCY, - // that list element is the shortcut for that offset, which is - // slightly suboptimal (the *next* element is the one we really want), - // but it's all correct and simpler this way. - while (originalCharOffset + len >= (nextShortcutIndex + 1)*SHORTCUT_FREQUENCY) { - mShortcuts[nextShortcutIndex] = - Shortcut(i, originalCharOffset, skippedCharOffset); - ++nextShortcutIndex; - } - - originalCharOffset += len; - if (IsKeepEntry(i)) { - skippedCharOffset += len; + } + + // at start of string? + if (aOffset == 0) { + mSkippedStringOffset = 0; + mCurrentRangeIndex = + rangeCount && mSkipChars->mRanges[0].Start() == 0 ? 0 : -1; + return; + } + + // find the range that includes or precedes aOffset + uint32_t lo = 0, hi = rangeCount; + const gfxSkipChars::SkippedRange* ranges = mSkipChars->mRanges.Elements(); + while (lo < hi) { + uint32_t mid = (lo + hi) / 2; + if (uint32_t(aOffset) < ranges[mid].Start()) { + hi = mid; + } else { + lo = mid + 1; } } + + if (lo == rangeCount) { + mCurrentRangeIndex = rangeCount - 1; + } else if (uint32_t(aOffset) < ranges[lo].Start()) { + mCurrentRangeIndex = lo - 1; + if (mCurrentRangeIndex == -1) { + mSkippedStringOffset = aOffset; + return; + } + } else { + mCurrentRangeIndex = lo; + } + + const gfxSkipChars::SkippedRange& r = ranges[mCurrentRangeIndex]; + if (uint32_t(aOffset) < r.End()) { + mSkippedStringOffset = r.SkippedOffset(); + return; + } + + mSkippedStringOffset = aOffset - r.NextDelta(); } void -gfxSkipCharsIterator::SetOffsets(uint32_t aOffset, bool aInOriginalString) +gfxSkipCharsIterator::SetSkippedOffset(uint32_t aOffset) { - NS_ASSERTION(aOffset <= mSkipChars->mCharCount, - "Invalid offset"); + NS_ASSERTION((mSkipChars->mRanges.IsEmpty() && + aOffset <= mSkipChars->mCharCount) || + (aOffset <= mSkipChars->LastRange().SkippedOffset() + + mSkipChars->mCharCount - + mSkipChars->LastRange().End()), + "Invalid skipped offset"); + mSkippedStringOffset = aOffset; - if (mSkipChars->mListLength == 0) { - mOriginalStringOffset = mSkippedStringOffset = aOffset; + uint32_t rangeCount = mSkipChars->mRanges.Length(); + if (rangeCount == 0) { + mOriginalStringOffset = aOffset; return; } - - if (aOffset == 0) { - // Start from the beginning of the string. - mSkippedStringOffset = 0; - mOriginalStringOffset = 0; - mListPrefixLength = 0; - mListPrefixKeepCharCount = 0; - mListPrefixCharCount = 0; - if (aInOriginalString) { - // Nothing more to do! - return; - } - } - - if (aInOriginalString && mSkipChars->mShortcuts && - abs(int32_t(aOffset) - int32_t(mListPrefixCharCount)) > SHORTCUT_FREQUENCY) { - // Take a shortcut. This makes SetOffsets(..., true) O(1) by bounding - // the iterations in the loop below to at most SHORTCUT_FREQUENCY iterations - uint32_t shortcutIndex = aOffset/SHORTCUT_FREQUENCY; - if (shortcutIndex == 0) { - mListPrefixLength = 0; - mListPrefixKeepCharCount = 0; - mListPrefixCharCount = 0; + + uint32_t lo = 0, hi = rangeCount; + const gfxSkipChars::SkippedRange* ranges = mSkipChars->mRanges.Elements(); + while (lo < hi) { + uint32_t mid = (lo + hi) / 2; + if (aOffset < ranges[mid].SkippedOffset()) { + hi = mid; } else { - const gfxSkipChars::Shortcut& shortcut = mSkipChars->mShortcuts[shortcutIndex - 1]; - mListPrefixLength = shortcut.mListPrefixLength; - mListPrefixKeepCharCount = shortcut.mListPrefixKeepCharCount; - mListPrefixCharCount = shortcut.mListPrefixCharCount; + lo = mid + 1; } } - - int32_t currentRunLength = mSkipChars->mList[mListPrefixLength]; - for (;;) { - // See if aOffset is in the string segment described by - // mSkipChars->mList[mListPrefixLength] - uint32_t segmentOffset = aInOriginalString ? mListPrefixCharCount : mListPrefixKeepCharCount; - if ((aInOriginalString || IsKeepEntry(mListPrefixLength)) && - aOffset >= segmentOffset && aOffset < segmentOffset + currentRunLength) { - int32_t offsetInSegment = aOffset - segmentOffset; - mOriginalStringOffset = mListPrefixCharCount + offsetInSegment; - mSkippedStringOffset = mListPrefixKeepCharCount; - if (IsKeepEntry(mListPrefixLength)) { - mSkippedStringOffset += offsetInSegment; - } + + if (lo == rangeCount) { + mCurrentRangeIndex = rangeCount - 1; + } else if (aOffset < ranges[lo].SkippedOffset()) { + mCurrentRangeIndex = lo - 1; + if (mCurrentRangeIndex == -1) { + mOriginalStringOffset = aOffset; return; } - - if (aOffset < segmentOffset) { - // We need to move backwards - if (mListPrefixLength <= 0) { - // nowhere to go backwards - mOriginalStringOffset = mSkippedStringOffset = 0; - return; - } - // Go backwards one segment and restore invariants - --mListPrefixLength; - currentRunLength = mSkipChars->mList[mListPrefixLength]; - mListPrefixCharCount -= currentRunLength; - if (IsKeepEntry(mListPrefixLength)) { - mListPrefixKeepCharCount -= currentRunLength; - } - } else { - // We need to move forwards - if (mListPrefixLength >= mSkipChars->mListLength - 1) { - // nowhere to go forwards - mOriginalStringOffset = mListPrefixCharCount + currentRunLength; - mSkippedStringOffset = mListPrefixKeepCharCount; - if (IsKeepEntry(mListPrefixLength)) { - mSkippedStringOffset += currentRunLength; - } - return; - } - // Go forwards one segment and restore invariants - mListPrefixCharCount += currentRunLength; - if (IsKeepEntry(mListPrefixLength)) { - mListPrefixKeepCharCount += currentRunLength; - } - ++mListPrefixLength; - currentRunLength = mSkipChars->mList[mListPrefixLength]; - } + } else { + mCurrentRangeIndex = lo; } + + const gfxSkipChars::SkippedRange& r = ranges[mCurrentRangeIndex]; + mOriginalStringOffset = r.End() + aOffset - r.SkippedOffset(); } bool gfxSkipCharsIterator::IsOriginalCharSkipped(int32_t* aRunLength) const { - if (mSkipChars->mListLength == 0) { + if (mCurrentRangeIndex == -1) { + // we're before the first skipped range (if any) if (aRunLength) { - *aRunLength = mSkipChars->mCharCount - mOriginalStringOffset; + uint32_t end = mSkipChars->mRanges.IsEmpty() ? + mSkipChars->mCharCount : mSkipChars->mRanges[0].Start(); + *aRunLength = end - mOriginalStringOffset; } return mSkipChars->mCharCount == uint32_t(mOriginalStringOffset); } - - uint32_t listPrefixLength = mListPrefixLength; - // figure out which segment we're in - uint32_t currentRunLength = mSkipChars->mList[listPrefixLength]; - // Zero-length list entries are possible. Advance until mListPrefixLength - // is pointing to a run with real characters (or we're at the end of the - // string). - while (currentRunLength == 0 && listPrefixLength < mSkipChars->mListLength - 1) { - ++listPrefixLength; - // This does not break the iterator's invariant because no skipped - // or kept characters are being added - currentRunLength = mSkipChars->mList[listPrefixLength]; - } - NS_ASSERTION(uint32_t(mOriginalStringOffset) >= mListPrefixCharCount, - "Invariant violation"); - uint32_t offsetIntoCurrentRun = - uint32_t(mOriginalStringOffset) - mListPrefixCharCount; - if (listPrefixLength >= mSkipChars->mListLength - 1 && - offsetIntoCurrentRun >= currentRunLength) { - NS_ASSERTION(listPrefixLength == mSkipChars->mListLength - 1 && - offsetIntoCurrentRun == currentRunLength, - "Overran end of string"); - // We're at the end of the string + + const gfxSkipChars::SkippedRange& range = + mSkipChars->mRanges[mCurrentRangeIndex]; + + if (uint32_t(mOriginalStringOffset) < range.End()) { if (aRunLength) { - *aRunLength = 0; + *aRunLength = range.End() - mOriginalStringOffset; } return true; } - - bool isSkipped = !IsKeepEntry(listPrefixLength); - if (aRunLength) { - // Long runs of all-skipped or all-kept characters will be encoded as - // sequences of 255, 0, 255, 0 etc. Compute the maximum run length by skipping - // over zero entries. - uint32_t runLength = currentRunLength - offsetIntoCurrentRun; - for (uint32_t i = listPrefixLength + 2; i < mSkipChars->mListLength; i += 2) { - if (mSkipChars->mList[i - 1] != 0) - break; - runLength += mSkipChars->mList[i]; - } - *aRunLength = runLength; - } - return isSkipped; -} -void -gfxSkipCharsBuilder::FlushRun() -{ - NS_ASSERTION((mBuffer.Length() & 1) == mRunSkipped, - "out of sync?"); - // Fill in buffer entries starting at mBufferLength, as many as necessary - uint32_t charCount = mRunCharCount; - for (;;) { - uint32_t chars = std::min<uint32_t>(255, charCount); - if (!mBuffer.AppendElement(chars)) { - mInErrorState = true; - return; - } - charCount -= chars; - if (charCount == 0) - break; - if (!mBuffer.AppendElement(0)) { - mInErrorState = true; - return; - } + if (aRunLength) { + uint32_t end = + uint32_t(mCurrentRangeIndex) + 1 < mSkipChars->mRanges.Length() ? + mSkipChars->mRanges[mCurrentRangeIndex + 1].Start() : + mSkipChars->mCharCount; + *aRunLength = end - mOriginalStringOffset; } - - NS_ASSERTION(mCharCount + mRunCharCount >= mCharCount, - "String length overflow"); - mCharCount += mRunCharCount; - mRunCharCount = 0; - mRunSkipped = !mRunSkipped; + + return mSkipChars->mCharCount == uint32_t(mOriginalStringOffset); }
--- a/gfx/thebes/gfxSkipChars.h +++ b/gfx/thebes/gfxSkipChars.h @@ -1,316 +1,302 @@ /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef GFX_SKIP_CHARS_H #define GFX_SKIP_CHARS_H -#include "nsAutoPtr.h" #include "nsTArray.h" /* * gfxSkipChars is a data structure representing a list of characters that * have been skipped. The initial string is called the "original string" * and after skipping some characters, the result is called the "skipped string". * gfxSkipChars provides efficient ways to translate between offsets in the * original string and the skipped string. It is used by textrun code to keep * track of offsets before and after text transformations such as whitespace * compression and control code deletion. */ /** - * gfxSkipCharsBuilder is a helper class that accumulates a list of (skip, keep) - * commands and can eventually be used to construct a real gfxSkipChars. - * gfxSkipCharsBuilder objects are quite large so don't keep these around. - * On the positive side, the Skip/KeepChar(s) methods are very efficient, - * especially when you have runs of all-kept or all-skipped characters. - * - * mBuffer is an array of bytes; even numbered bytes represent characters kept, - * odd numbered bytes represent characters skipped. After those characters - * are accounted for, we have mRunCharCount characters which are kept or - * skipped depending on the value of mRunSkipped. - * - * mCharCount is the sum of counts of all skipped and kept characters, i.e., - * the length of the original string. + * The gfxSkipChars is represented as a sorted array of skipped ranges. + * + * A freshly-created gfxSkipChars means "all chars kept". */ -class gfxSkipCharsBuilder { +class gfxSkipChars +{ +private: + class SkippedRange + { + public: + SkippedRange(uint32_t aOffset, uint32_t aLength, uint32_t aDelta) + : mOffset(aOffset), mLength(aLength), mDelta(aDelta) + { } + + uint32_t Start() const + { + return mOffset; + } + + uint32_t End() const + { + return mOffset + mLength; + } + + uint32_t Length() const + { + return mLength; + } + + uint32_t SkippedOffset() const + { + return mOffset - mDelta; + } + + uint32_t Delta() const + { + return mDelta; + } + + uint32_t NextDelta() const + { + return mDelta + mLength; + } + + void Extend(uint32_t aChars) + { + mLength += aChars; + } + + private: + uint32_t mOffset; // original-string offset at which we want to skip + uint32_t mLength; // number of skipped chars at this offset + uint32_t mDelta; // sum of lengths of preceding skipped-ranges + }; + public: - gfxSkipCharsBuilder() : - mCharCount(0), mRunCharCount(0), mRunSkipped(false), mInErrorState(false) - {} - - void SkipChars(uint32_t aChars) { - DoChars(aChars, true); - } - void KeepChars(uint32_t aChars) { - DoChars(aChars, false); - } - void SkipChar() { - SkipChars(1); - } - void KeepChar() { - KeepChars(1); - } - void DoChars(uint32_t aChars, bool aSkipped) { - if (aSkipped != mRunSkipped && aChars > 0) { - FlushRun(); + gfxSkipChars() + : mCharCount(0) + { } + + void SkipChars(uint32_t aChars) + { + NS_ASSERTION(mCharCount + aChars > mCharCount, + "Character count overflow"); + uint32_t rangeCount = mRanges.Length(); + uint32_t delta = 0; + if (rangeCount > 0) { + SkippedRange& lastRange = mRanges[rangeCount - 1]; + if (lastRange.End() == mCharCount) { + lastRange.Extend(aChars); + mCharCount += aChars; + return; + } + delta = lastRange.NextDelta(); } - NS_ASSERTION(mRunCharCount + aChars > mRunCharCount, - "Character count overflow"); - mRunCharCount += aChars; + mRanges.AppendElement(SkippedRange(mCharCount, aChars, delta)); + mCharCount += aChars; } - bool IsOK() { return !mInErrorState; } - - uint32_t GetCharCount() { return mCharCount + mRunCharCount; } - bool GetAllCharsKept() { return mBuffer.Length() == 0; } - - friend class gfxSkipChars; - -private: - typedef AutoFallibleTArray<uint8_t,256> Buffer; + void KeepChars(uint32_t aChars) + { + NS_ASSERTION(mCharCount + aChars > mCharCount, + "Character count overflow"); + mCharCount += aChars; + } - /** - * Moves mRunCharCount/mRunSkipped to the buffer (updating mCharCount), - * sets mRunCharCount to zero and toggles mRunSkipped. - */ - void FlushRun(); - - Buffer mBuffer; - uint32_t mCharCount; - uint32_t mRunCharCount; - bool mRunSkipped; // == mBuffer.Length()&1 - bool mInErrorState; -}; + void SkipChar() + { + SkipChars(1); + } -/** - * The gfxSkipChars list is represented as a list of bytes of the form - * [chars to keep, chars to skip, chars to keep, chars to skip, ...] - * In the special case where all chars are to be kept, the list is length - * zero. - * - * A freshly-created gfxSkipChars means "all chars kept". - */ -class gfxSkipChars { -public: - gfxSkipChars() : mListLength(0), mCharCount(0) {} - - void TakeFrom(gfxSkipChars* aSkipChars) { - mList = aSkipChars->mList.forget(); - mListLength = aSkipChars->mListLength; + void KeepChar() + { + KeepChars(1); + } + + void TakeFrom(gfxSkipChars* aSkipChars) + { + mRanges.SwapElements(aSkipChars->mRanges); mCharCount = aSkipChars->mCharCount; aSkipChars->mCharCount = 0; - aSkipChars->mListLength = 0; - BuildShortcuts(); + } + + int32_t GetOriginalCharCount() const + { + return mCharCount; } - - void TakeFrom(gfxSkipCharsBuilder* aSkipCharsBuilder) { - if (!aSkipCharsBuilder->mBuffer.Length()) { - NS_ASSERTION(!aSkipCharsBuilder->mRunSkipped, "out of sync"); - // all characters kept - mCharCount = aSkipCharsBuilder->mRunCharCount; - mList = nullptr; - mListLength = 0; - } else { - aSkipCharsBuilder->FlushRun(); - mCharCount = aSkipCharsBuilder->mCharCount; - mList = new uint8_t[aSkipCharsBuilder->mBuffer.Length()]; - if (!mList) { - mListLength = 0; - } else { - mListLength = aSkipCharsBuilder->mBuffer.Length(); - memcpy(mList, aSkipCharsBuilder->mBuffer.Elements(), mListLength); - } - } - aSkipCharsBuilder->mBuffer.Clear(); - aSkipCharsBuilder->mCharCount = 0; - aSkipCharsBuilder->mRunCharCount = 0; - aSkipCharsBuilder->mRunSkipped = false; - BuildShortcuts(); + + const SkippedRange& LastRange() const + { + // this is only valid if mRanges is non-empty; no assertion here + // because nsTArray will already assert if we abuse it + return mRanges[mRanges.Length() - 1]; } - - void SetAllKeep(uint32_t aLength) { - mCharCount = aLength; - mList = nullptr; - mListLength = 0; - } - - int32_t GetOriginalCharCount() const { return mCharCount; } friend class gfxSkipCharsIterator; private: - struct Shortcut { - uint32_t mListPrefixLength; - uint32_t mListPrefixCharCount; - uint32_t mListPrefixKeepCharCount; - - Shortcut() {} - Shortcut(uint32_t aListPrefixLength, uint32_t aListPrefixCharCount, - uint32_t aListPrefixKeepCharCount) : - mListPrefixLength(aListPrefixLength), - mListPrefixCharCount(aListPrefixCharCount), - mListPrefixKeepCharCount(aListPrefixKeepCharCount) {} - }; - - void BuildShortcuts(); - - nsAutoArrayPtr<uint8_t> mList; - nsAutoArrayPtr<Shortcut> mShortcuts; - uint32_t mListLength; - uint32_t mCharCount; + nsTArray<SkippedRange> mRanges; + uint32_t mCharCount; }; /** * A gfxSkipCharsIterator represents a position in the original string. It lets you * map efficiently to and from positions in the string after skipped characters * have been removed. You can also specify an offset that is added to all * incoming original string offsets and subtracted from all outgoing original * string offsets --- useful when the gfxSkipChars corresponds to something * offset from the original DOM coordinates, which it often does for gfxTextRuns. - * + * * The current positions (in both the original and skipped strings) are * always constrained to be >= 0 and <= the string length. When the position * is equal to the string length, it is at the end of the string. The current * positions do not include any aOriginalStringToSkipCharsOffset. - * + * * When the position in the original string corresponds to a skipped character, * the skipped-characters offset is the offset of the next unskipped character, * or the skipped-characters string length if there is no next unskipped character. */ -class gfxSkipCharsIterator { +class gfxSkipCharsIterator +{ public: /** * @param aOriginalStringToSkipCharsOffset add this to all incoming and * outgoing original string offsets */ gfxSkipCharsIterator(const gfxSkipChars& aSkipChars, int32_t aOriginalStringToSkipCharsOffset, int32_t aOriginalStringOffset) : mSkipChars(&aSkipChars), - mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset), - mListPrefixLength(0), mListPrefixCharCount(0), mListPrefixKeepCharCount(0) { + mOriginalStringOffset(0), + mSkippedStringOffset(0), + mCurrentRangeIndex(-1), + mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset) + { SetOriginalOffset(aOriginalStringOffset); } gfxSkipCharsIterator(const gfxSkipChars& aSkipChars, int32_t aOriginalStringToSkipCharsOffset = 0) : mSkipChars(&aSkipChars), - mOriginalStringOffset(0), mSkippedStringOffset(0), - mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset), - mListPrefixLength(0), mListPrefixCharCount(0), mListPrefixKeepCharCount(0) { - } + mOriginalStringOffset(0), + mSkippedStringOffset(0), + mCurrentRangeIndex(-1), + mOriginalStringToSkipCharsOffset(aOriginalStringToSkipCharsOffset) + { } gfxSkipCharsIterator(const gfxSkipCharsIterator& aIterator) : mSkipChars(aIterator.mSkipChars), mOriginalStringOffset(aIterator.mOriginalStringOffset), mSkippedStringOffset(aIterator.mSkippedStringOffset), - mOriginalStringToSkipCharsOffset(aIterator.mOriginalStringToSkipCharsOffset), - mListPrefixLength(aIterator.mListPrefixLength), - mListPrefixCharCount(aIterator.mListPrefixCharCount), - mListPrefixKeepCharCount(aIterator.mListPrefixKeepCharCount) - {} - + mCurrentRangeIndex(aIterator.mCurrentRangeIndex), + mOriginalStringToSkipCharsOffset(aIterator.mOriginalStringToSkipCharsOffset) + { } + /** * The empty constructor creates an object that is useless until it is assigned. */ - gfxSkipCharsIterator() : mSkipChars(nullptr) {} + gfxSkipCharsIterator() + : mSkipChars(nullptr) + { } /** * Return true if this iterator is properly initialized and usable. - */ - bool IsInitialized() { return mSkipChars != nullptr; } + */ + bool IsInitialized() + { + return mSkipChars != nullptr; + } /** * Set the iterator to aOriginalStringOffset in the original string. * This can efficiently move forward or backward from the current position. * aOriginalStringOffset is clamped to [0,originalStringLength]. */ - void SetOriginalOffset(int32_t aOriginalStringOffset) { - SetOffsets(aOriginalStringOffset + mOriginalStringToSkipCharsOffset, true); - } - + void SetOriginalOffset(int32_t aOriginalStringOffset); + /** * Set the iterator to aSkippedStringOffset in the skipped string. * This can efficiently move forward or backward from the current position. * aSkippedStringOffset is clamped to [0,skippedStringLength]. */ - void SetSkippedOffset(uint32_t aSkippedStringOffset) { - SetOffsets(aSkippedStringOffset, false); - } - - uint32_t ConvertOriginalToSkipped(int32_t aOriginalStringOffset) { + void SetSkippedOffset(uint32_t aSkippedStringOffset); + + uint32_t ConvertOriginalToSkipped(int32_t aOriginalStringOffset) + { SetOriginalOffset(aOriginalStringOffset); return GetSkippedOffset(); } - uint32_t ConvertSkippedToOriginal(int32_t aSkippedStringOffset) { + + uint32_t ConvertSkippedToOriginal(int32_t aSkippedStringOffset) + { SetSkippedOffset(aSkippedStringOffset); return GetOriginalOffset(); } - + /** * Test if the character at the current position in the original string * is skipped or not. If aRunLength is non-null, then *aRunLength is set * to a number of characters all of which are either skipped or not, starting * at this character. When the current position is at the end of the original * string, we return true and *aRunLength is set to zero. */ bool IsOriginalCharSkipped(int32_t* aRunLength = nullptr) const; - - void AdvanceOriginal(int32_t aDelta) { - SetOffsets(mOriginalStringOffset + aDelta, true); + + void AdvanceOriginal(int32_t aDelta) + { + SetOriginalOffset(GetOriginalOffset() + aDelta); } - void AdvanceSkipped(int32_t aDelta) { - SetOffsets(mSkippedStringOffset + aDelta, false); + + void AdvanceSkipped(int32_t aDelta) + { + SetSkippedOffset(GetSkippedOffset() + aDelta); } - + /** * @return the offset within the original string */ - int32_t GetOriginalOffset() const { + int32_t GetOriginalOffset() const + { return mOriginalStringOffset - mOriginalStringToSkipCharsOffset; } + /** * @return the offset within the skipped string corresponding to the * current position in the original string. If the current position * in the original string is a character that is skipped, then we return * the position corresponding to the first non-skipped character in the * original string after the current position, or the length of the skipped * string if there is no such character. */ - uint32_t GetSkippedOffset() const { return mSkippedStringOffset; } + uint32_t GetSkippedOffset() const + { + return mSkippedStringOffset; + } - int32_t GetOriginalEnd() const { + int32_t GetOriginalEnd() const + { return mSkipChars->GetOriginalCharCount() - mOriginalStringToSkipCharsOffset; } private: - void SetOffsets(uint32_t aOffset, bool aInOriginalString); - const gfxSkipChars* mSkipChars; + + // Current position int32_t mOriginalStringOffset; uint32_t mSkippedStringOffset; + // Index of the last skippedRange that precedes or contains the current + // position in the original string. + // If index == -1 then we are before the first skipped char. + int32_t mCurrentRangeIndex; + // This offset is added to map from "skipped+unskipped characters in // the original DOM string" character space to "skipped+unskipped // characters in the textrun's gfxSkipChars" character space int32_t mOriginalStringToSkipCharsOffset; - - /* - * This is used to speed up cursor-style traversal. The invariant is that - * the first mListPrefixLength bytes of mSkipChars.mList sum to - * mListPrefixCharCount, and the even-indexed bytes in that prefix sum to - * mListPrefixKeepCharCount. - * Also, 0 <= mListPrefixLength < mSkipChars.mListLength, or else - * mSkipChars.mListLength is zero. - * Also, mListPrefixCharCount <= mOriginalStringOffset (and therefore - * mListPrefixKeepCharCount < mSkippedStringOffset). - */ - uint32_t mListPrefixLength; - uint32_t mListPrefixCharCount; - uint32_t mListPrefixKeepCharCount; }; #endif /*GFX_SKIP_CHARS_H*/
deleted file mode 100644 --- a/intl/uconv/util/ubase.h +++ /dev/null @@ -1,14 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef ubase_h__ -#define ubase_h__ - -#include "prtypes.h" -#include <stdint.h> - -#define PRIVATE -#define MODULE_PRIVATE - -#endif
--- a/intl/uconv/util/ugen.c +++ b/intl/uconv/util/ugen.c @@ -1,176 +1,176 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 "unicpriv.h" /*================================================================================= =================================================================================*/ -typedef PRBool (*uSubGeneratorFunc) (uint16_t in, unsigned char* out); +typedef int (*uSubGeneratorFunc) (uint16_t in, unsigned char* out); /*================================================================================= =================================================================================*/ -typedef PRBool (*uGeneratorFunc) ( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); +typedef int (*uGeneratorFunc) ( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); -MODULE_PRIVATE PRBool uGenerate( - uScanClassID scanClass, - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); +int uGenerate( + uScanClassID scanClass, + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); #define uSubGenerator(sub,in,out) (* m_subgenerator[sub])((in),(out)) -PRIVATE PRBool uCheckAndGenAlways1Byte( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); -PRIVATE PRBool uCheckAndGenAlways2Byte( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); -PRIVATE PRBool uCheckAndGenAlways2ByteShiftGR( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); -MODULE_PRIVATE PRBool uGenerateShift( - uShiftOutTable *shift, - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8F( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8EA2( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); +int uCheckAndGenAlways1Byte( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); +int uCheckAndGenAlways2Byte( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); +int uCheckAndGenAlways2ByteShiftGR( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); +int uGenerateShift( + uShiftOutTable *shift, + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); +int uCheckAndGen2ByteGRPrefix8F( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); +int uCheckAndGen2ByteGRPrefix8EA2( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8EA3( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); +int uCheckAndGen2ByteGRPrefix8EA3( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8EA4( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); +int uCheckAndGen2ByteGRPrefix8EA4( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8EA5( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); +int uCheckAndGen2ByteGRPrefix8EA5( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8EA6( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); +int uCheckAndGen2ByteGRPrefix8EA6( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8EA7( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); -PRIVATE PRBool uCnGAlways8BytesDecomposedHangul( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); +int uCheckAndGen2ByteGRPrefix8EA7( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); +int uCnGAlways8BytesDecomposedHangul( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); -PRIVATE PRBool uCheckAndGenJohabHangul( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); +int uCheckAndGenJohabHangul( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); -PRIVATE PRBool uCheckAndGenJohabSymbol( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); +int uCheckAndGenJohabSymbol( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); -PRIVATE PRBool uCheckAndGen4BytesGB18030( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ); +int uCheckAndGen4BytesGB18030( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ); -PRIVATE PRBool uGenAlways2Byte( - uint16_t in, - unsigned char* out - ); -PRIVATE PRBool uGenAlways2ByteShiftGR( - uint16_t in, - unsigned char* out - ); -PRIVATE PRBool uGenAlways1Byte( - uint16_t in, - unsigned char* out - ); -PRIVATE PRBool uGenAlways1BytePrefix8E( - uint16_t in, - unsigned char* out - ); - /*================================================================================= - +int uGenAlways2Byte( + uint16_t in, + unsigned char* out + ); +int uGenAlways2ByteShiftGR( + uint16_t in, + unsigned char* out + ); +int uGenAlways1Byte( + uint16_t in, + unsigned char* out + ); +int uGenAlways1BytePrefix8E( + uint16_t in, + unsigned char* out + ); +/*================================================================================= + =================================================================================*/ -PRIVATE const uGeneratorFunc m_generator[uNumOfCharsetType] = +const uGeneratorFunc m_generator[uNumOfCharsetType] = { uCheckAndGenAlways1Byte, uCheckAndGenAlways2Byte, uCheckAndGenAlways2ByteShiftGR, uCheckAndGen2ByteGRPrefix8F, uCheckAndGen2ByteGRPrefix8EA2, uCheckAndGen2ByteGRPrefix8EA3, uCheckAndGen2ByteGRPrefix8EA4, @@ -183,378 +183,377 @@ PRIVATE const uGeneratorFunc m_generator uCheckAndGen4BytesGB18030, uCheckAndGenAlways2Byte /* place-holder for GR128 */ }; /*================================================================================= =================================================================================*/ -PRIVATE const uSubGeneratorFunc m_subgenerator[uNumOfCharType] = +const uSubGeneratorFunc m_subgenerator[uNumOfCharType] = { uGenAlways1Byte, uGenAlways2Byte, uGenAlways2ByteShiftGR, uGenAlways1BytePrefix8E - }; /*================================================================================= =================================================================================*/ -MODULE_PRIVATE PRBool uGenerate( - uScanClassID scanClass, - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uGenerate( + uScanClassID scanClass, + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { return (* m_generator[scanClass]) (state,in,out,outbuflen,outlen); } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uGenAlways1Byte( - uint16_t in, - unsigned char* out - ) +int uGenAlways1Byte( + uint16_t in, + unsigned char* out + ) { out[0] = (unsigned char)in; - return PR_TRUE; + return 1; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uGenAlways2Byte( - uint16_t in, - unsigned char* out - ) +int uGenAlways2Byte( + uint16_t in, + unsigned char* out + ) { out[0] = (unsigned char)((in >> 8) & 0xff); out[1] = (unsigned char)(in & 0xff); - return PR_TRUE; + return 1; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uGenAlways2ByteShiftGR( - uint16_t in, - unsigned char* out - ) +int uGenAlways2ByteShiftGR( + uint16_t in, + unsigned char* out + ) { out[0] = (unsigned char)(((in >> 8) & 0xff) | 0x80); out[1] = (unsigned char)((in & 0xff) | 0x80); - return PR_TRUE; + return 1; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uGenAlways1BytePrefix8E( - uint16_t in, - unsigned char* out - ) +int uGenAlways1BytePrefix8E( + uint16_t in, + unsigned char* out + ) { out[0] = 0x8E; out[1] = (unsigned char)(in & 0xff); - return PR_TRUE; + return 1; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndGenAlways1Byte( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGenAlways1Byte( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { /* Don't check inlen. The caller should ensure it is larger than 0 */ /* Oops, I don't agree. Code changed to check every time. [CATA] */ if(outbuflen < 1) - return PR_FALSE; + return 0; else { *outlen = 1; out[0] = in & 0xff; - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndGenAlways2Byte( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGenAlways2Byte( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { if(outbuflen < 2) - return PR_FALSE; + return 0; else { *outlen = 2; out[0] = ((in >> 8 ) & 0xff); out[1] = in & 0xff; - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndGenAlways2ByteShiftGR( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGenAlways2ByteShiftGR( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { if(outbuflen < 2) - return PR_FALSE; + return 0; else { *outlen = 2; out[0] = ((in >> 8 ) & 0xff) | 0x80; out[1] = (in & 0xff) | 0x80; - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ -MODULE_PRIVATE PRBool uGenerateShift( - uShiftOutTable *shift, - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uGenerateShift( + uShiftOutTable *shift, + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { int16_t i; const uShiftOutCell* cell = &(shift->shiftcell[0]); int16_t itemnum = shift->numOfItem; unsigned char inH, inL; inH = (in >> 8) & 0xff; inL = (in & 0xff ); for(i=0;i<itemnum;i++) { if( ( inL >= cell[i].shiftout_MinLB) && ( inL <= cell[i].shiftout_MaxLB) && ( inH >= cell[i].shiftout_MinHB) && ( inH <= cell[i].shiftout_MaxHB) ) { if(outbuflen < cell[i].reserveLen) { - return PR_FALSE; + return 0; } else { *outlen = cell[i].reserveLen; return (uSubGenerator(cell[i].classID,in,out)); } } } - return PR_FALSE; + return 0; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8F( int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGen2ByteGRPrefix8F(int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { if(outbuflen < 3) - return PR_FALSE; + return 0; else { *outlen = 3; out[0] = 0x8F; out[1] = ((in >> 8 ) & 0xff) | 0x80; out[2] = (in & 0xff) | 0x80; - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8EA2( int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGen2ByteGRPrefix8EA2(int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { if(outbuflen < 4) - return PR_FALSE; + return 0; else { *outlen = 4; out[0] = 0x8E; out[1] = 0xA2; out[2] = ((in >> 8 ) & 0xff) | 0x80; out[3] = (in & 0xff) | 0x80; - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8EA3( int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGen2ByteGRPrefix8EA3(int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { if(outbuflen < 4) - return PR_FALSE; + return 0; else { *outlen = 4; out[0] = 0x8E; out[1] = 0xA3; out[2] = ((in >> 8 ) & 0xff) | 0x80; out[3] = (in & 0xff) | 0x80; - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8EA4( int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGen2ByteGRPrefix8EA4(int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { if(outbuflen < 4) - return PR_FALSE; + return 0; else { *outlen = 4; out[0] = 0x8E; out[1] = 0xA4; out[2] = ((in >> 8 ) & 0xff) | 0x80; out[3] = (in & 0xff) | 0x80; - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8EA5( int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGen2ByteGRPrefix8EA5(int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { if(outbuflen < 4) - return PR_FALSE; + return 0; else { *outlen = 4; out[0] = 0x8E; out[1] = 0xA5; out[2] = ((in >> 8 ) & 0xff) | 0x80; out[3] = (in & 0xff) | 0x80; - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8EA6( int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGen2ByteGRPrefix8EA6(int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { if(outbuflen < 4) - return PR_FALSE; + return 0; else { *outlen = 4; out[0] = 0x8E; out[1] = 0xA6; out[2] = ((in >> 8 ) & 0xff) | 0x80; out[3] = (in & 0xff) | 0x80; - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndGen2ByteGRPrefix8EA7( int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGen2ByteGRPrefix8EA7(int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { if(outbuflen < 4) - return PR_FALSE; + return 0; else { *outlen = 4; out[0] = 0x8E; out[1] = 0xA7; out[2] = ((in >> 8 ) & 0xff) | 0x80; out[3] = (in & 0xff) | 0x80; - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ #define SBase 0xAC00 #define LCount 19 #define VCount 21 #define TCount 28 #define NCount (VCount * TCount) /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCnGAlways8BytesDecomposedHangul( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCnGAlways8BytesDecomposedHangul( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { static const uint8_t lMap[LCount] = { 0xa1, 0xa2, 0xa4, 0xa7, 0xa8, 0xa9, 0xb1, 0xb2, 0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe }; static const uint8_t tMap[TCount] = { 0xd4, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xba, 0xbb, 0xbc, 0xbd, 0xbe }; uint16_t SIndex, LIndex, VIndex, TIndex; if(outbuflen < 8) - return PR_FALSE; + return 0; /* the following line are copy from Unicode 2.0 page 3-13 */ /* item 1 of Hangul Syllabel Decomposition */ SIndex = in - SBase; /* the following lines are copy from Unicode 2.0 page 3-14 */ /* item 2 of Hangul Syllabel Decomposition w/ modification */ LIndex = SIndex / NCount; @@ -570,29 +569,29 @@ PRIVATE PRBool uCnGAlways8BytesDecompose */ *outlen = 8; out[0] = out[2] = out[4] = out[6] = 0xa4; out[1] = 0xd4; out[3] = lMap[LIndex] ; out[5] = (VIndex + 0xbf); out[7] = tMap[TIndex]; - return PR_TRUE; + return 1; } -PRIVATE PRBool uCheckAndGenJohabHangul( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGenJohabHangul( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { if(outbuflen < 2) - return PR_FALSE; + return 0; else { /* See Table 4-45 (page 183) of CJKV Information Processing for detail explanation of the following table. */ /* static const uint8_t lMap[LCount] = { @@ -628,29 +627,29 @@ PRIVATE PRBool uCheckAndGenJohabHangul( ((LIndex+2)<<10) | (vMap[VIndex]<<5)| tMap[TIndex]; out[0] = (ch >> 8); out[1] = ch & 0x00FF; #if 0 printf("Johab Hangul %x %x in=%x L=%d V=%d T=%d\n", out[0], out[1], in, LIndex, VIndex, TIndex); #endif - return PR_TRUE; + return 1; } } -PRIVATE PRBool uCheckAndGenJohabSymbol( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGenJohabSymbol( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { if(outbuflen < 2) - return PR_FALSE; + return 0; else { /* The following code are based on the Perl code listed under * "ISO-2022-KR or EUC-KR to Johab Conversion" (page 1013) * in the book "CJKV Information Processing" by * Ken Lunde <lunde@adobe.com> * * sub convert2johab($) { # Convert ISO-2022-KR or EUC-KR to Johab @@ -684,30 +683,30 @@ PRIVATE PRBool uCheckAndGenJohabSymbol( } *outlen = 2; out[0] = ((hi+hi_off) >> 1) + ((hi<74) ? 200 : 187 ) - fe_off; out[1] = lo + (((hi+lo_off) & 1) ? ((lo > 110) ? 34 : 16) : 128); #if 0 printf("Johab Symbol %x %x in=%x\n", out[0], out[1], in); #endif - return PR_TRUE; + return 1; } } -PRIVATE PRBool uCheckAndGen4BytesGB18030( - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen - ) +int uCheckAndGen4BytesGB18030( + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen + ) { if(outbuflen < 4) - return PR_FALSE; + return 0; out[0] = (in / (10*126*10)) + 0x81; in %= (10*126*10); out[1] = (in / (10*126)) + 0x30; in %= (10*126); out[2] = (in / (10)) + 0x81; out[3] = (in % 10) + 0x30; *outlen = 4; - return PR_TRUE; + return 1; }
--- a/intl/uconv/util/umap.c +++ b/intl/uconv/util/umap.c @@ -2,140 +2,140 @@ /* 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 "PRIntlpriv.h" */ #include "unicpriv.h" typedef uint16_t (* MapFormatFunc)(uint16_t in,const uTable *uT,const uMapCell *cell); -typedef PRBool (* HitFormateFunc)(uint16_t in,const uMapCell *cell); +typedef int (* HitFormateFunc)(uint16_t in,const uMapCell *cell); typedef void (* FillInfoFormateFunc)(const uTable *uT, const uMapCell *cell, uint32_t* info); -PRIVATE PRBool uHitFormate0(uint16_t in,const uMapCell *cell); -PRIVATE PRBool uHitFormate2(uint16_t in,const uMapCell *cell); -PRIVATE uint16_t uMapFormate0(uint16_t in,const uTable *uT,const uMapCell *cell); -PRIVATE uint16_t uMapFormate1(uint16_t in,const uTable *uT,const uMapCell *cell); -PRIVATE uint16_t uMapFormate2(uint16_t in,const uTable *uT,const uMapCell *cell); -PRIVATE void uFillInfoFormate0(const uTable *uT,const uMapCell *cell,uint32_t* aInfo); -PRIVATE void uFillInfoFormate1(const uTable *uT,const uMapCell *cell,uint32_t* aInfo); -PRIVATE void uFillInfoFormate2(const uTable *uT,const uMapCell *cell,uint32_t* aInfo); +int uHitFormate0(uint16_t in,const uMapCell *cell); +int uHitFormate2(uint16_t in,const uMapCell *cell); +uint16_t uMapFormate0(uint16_t in,const uTable *uT,const uMapCell *cell); +uint16_t uMapFormate1(uint16_t in,const uTable *uT,const uMapCell *cell); +uint16_t uMapFormate2(uint16_t in,const uTable *uT,const uMapCell *cell); +void uFillInfoFormate0(const uTable *uT,const uMapCell *cell,uint32_t* aInfo); +void uFillInfoFormate1(const uTable *uT,const uMapCell *cell,uint32_t* aInfo); +void uFillInfoFormate2(const uTable *uT,const uMapCell *cell,uint32_t* aInfo); -PRIVATE const uMapCell *uGetMapCell(const uTable *uT, int16_t item); -PRIVATE char uGetFormat(const uTable *uT, int16_t item); +const uMapCell *uGetMapCell(const uTable *uT, int16_t item); +char uGetFormat(const uTable *uT, int16_t item); /*================================================================================= =================================================================================*/ -PRIVATE const MapFormatFunc m_map[uNumFormatTag] = +const MapFormatFunc m_map[uNumFormatTag] = { uMapFormate0, uMapFormate1, uMapFormate2, }; /*================================================================================= =================================================================================*/ -PRIVATE const FillInfoFormateFunc m_fillinfo[uNumFormatTag] = +const FillInfoFormateFunc m_fillinfo[uNumFormatTag] = { uFillInfoFormate0, uFillInfoFormate1, uFillInfoFormate2, }; /*================================================================================= =================================================================================*/ -PRIVATE const HitFormateFunc m_hit[uNumFormatTag] = +const HitFormateFunc m_hit[uNumFormatTag] = { uHitFormate0, uHitFormate0, uHitFormate2, }; #define uHit(format,in,cell) (* m_hit[(format)])((in),(cell)) #define uMap(format,in,uT,cell) (* m_map[(format)])((in),(uT),(cell)) #define uGetMapCell(uT, item) ((uMapCell *)(((uint16_t *)uT) + (uT)->offsetToMapCellArray + (item)*(UMAPCELL_SIZE/sizeof(uint16_t)))) #define uGetFormat(uT, item) (((((uint16_t *)uT) + (uT)->offsetToFormatArray)[(item)>> 2 ] >> (((item)% 4 ) << 2)) & 0x0f) /*================================================================================= =================================================================================*/ -MODULE_PRIVATE PRBool uMapCode(const uTable *uT, uint16_t in, uint16_t* out) +int uMapCode(const uTable *uT, uint16_t in, uint16_t* out) { - PRBool done = PR_FALSE; + int done = 0; uint16_t itemOfList = uT->itemOfList; uint16_t i; *out = NOMAPPING; for(i=0;i<itemOfList;i++) { const uMapCell* uCell; int8_t format = uGetFormat(uT,i); uCell = uGetMapCell(uT,i); if(uHit(format, in, uCell)) { *out = uMap(format, in, uT,uCell); - done = PR_TRUE; + done = 1; break; } } return ( done && (*out != NOMAPPING)); } /* member function */ /*================================================================================= =================================================================================*/ -PRIVATE PRBool uHitFormate0(uint16_t in,const uMapCell *cell) +int uHitFormate0(uint16_t in,const uMapCell *cell) { return ( (in >= cell->fmt.format0.srcBegin) && (in <= cell->fmt.format0.srcEnd) ) ; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uHitFormate2(uint16_t in,const uMapCell *cell) +int uHitFormate2(uint16_t in,const uMapCell *cell) { return (in == cell->fmt.format2.srcBegin); } /*================================================================================= =================================================================================*/ -PRIVATE uint16_t uMapFormate0(uint16_t in,const uTable *uT,const uMapCell *cell) +uint16_t uMapFormate0(uint16_t in,const uTable *uT,const uMapCell *cell) { return ((in - cell->fmt.format0.srcBegin) + cell->fmt.format0.destBegin); } /*================================================================================= =================================================================================*/ -PRIVATE uint16_t uMapFormate1(uint16_t in,const uTable *uT,const uMapCell *cell) +uint16_t uMapFormate1(uint16_t in,const uTable *uT,const uMapCell *cell) { return (*(((uint16_t *)uT) + uT->offsetToMappingTable + cell->fmt.format1.mappingOffset + in - cell->fmt.format1.srcBegin)); } /*================================================================================= =================================================================================*/ -PRIVATE uint16_t uMapFormate2(uint16_t in,const uTable *uT,const uMapCell *cell) +uint16_t uMapFormate2(uint16_t in,const uTable *uT,const uMapCell *cell) { return (cell->fmt.format2.destBegin); } #define SET_REPRESENTABLE(info, c) (info)[(c) >> 5] |= (1L << ((c) & 0x1f)) /*================================================================================= =================================================================================*/ -PRIVATE void uFillInfoFormate0(const uTable *uT,const uMapCell *cell,uint32_t* info) +void uFillInfoFormate0(const uTable *uT,const uMapCell *cell,uint32_t* info) { uint16_t begin, end, i; begin = cell->fmt.format0.srcBegin; end = cell->fmt.format0.srcEnd; if( (begin >> 5) == (end >> 5)) /* High 17 bits are the same */ { for(i = begin; i <= end; i++) SET_REPRESENTABLE(info, i); @@ -147,29 +147,29 @@ PRIVATE void uFillInfoFormate0(const uTa info[ e ] |= (0xFFFFFFFFL >> (31 - ((end) & 0x1f))); for(b++ ; b < e ; b++) info[b] |= 0xFFFFFFFFL; } } /*================================================================================= =================================================================================*/ -PRIVATE void uFillInfoFormate1(const uTable *uT,const uMapCell *cell,uint32_t* info) +void uFillInfoFormate1(const uTable *uT,const uMapCell *cell,uint32_t* info) { uint16_t begin, end, i; uint16_t *base; begin = cell->fmt.format0.srcBegin; end = cell->fmt.format0.srcEnd; base = (((uint16_t *)uT) + uT->offsetToMappingTable + cell->fmt.format1.mappingOffset); for(i = begin; i <= end; i++) { if(0xFFFD != base[i - begin]) /* check every item */ SET_REPRESENTABLE(info, i); } } /*================================================================================= =================================================================================*/ -PRIVATE void uFillInfoFormate2(const uTable *uT,const uMapCell *cell,uint32_t* info) +void uFillInfoFormate2(const uTable *uT,const uMapCell *cell,uint32_t* info) { SET_REPRESENTABLE(info, cell->fmt.format2.srcBegin); }
--- a/intl/uconv/util/unicpriv.h +++ b/intl/uconv/util/unicpriv.h @@ -1,52 +1,52 @@ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef __UNIPRIV__ #define __UNIPRIV__ -#include "ubase.h" +#include <stdint.h> #include "umap.h" #include "uconvutil.h" #ifdef __cplusplus extern "C" { #endif -PRBool uMapCode(const uTable *uT, - uint16_t in, - uint16_t* out); +int uMapCode(const uTable *uT, + uint16_t in, + uint16_t* out); -PRBool uGenerate(uScanClassID scanClass, - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen); +int uGenerate(uScanClassID scanClass, + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen); -PRBool uScan(uScanClassID scanClass, - int32_t *state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen); +int uScan(uScanClassID scanClass, + int32_t *state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen); -PRBool uGenerateShift(uShiftOutTable *shift, - int32_t* state, - uint16_t in, - unsigned char* out, - uint32_t outbuflen, - uint32_t* outlen); +int uGenerateShift(uShiftOutTable *shift, + int32_t* state, + uint16_t in, + unsigned char* out, + uint32_t outbuflen, + uint32_t* outlen); -PRBool uScanShift(uShiftInTable *shift, - int32_t *state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen); +int uScanShift(uShiftInTable *shift, + int32_t *state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen); #ifdef __cplusplus } #endif #endif /* __UNIPRIV__ */
--- a/intl/uconv/util/uscan.c +++ b/intl/uconv/util/uscan.c @@ -3,176 +3,176 @@ * 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 "unicpriv.h" #define CHK_GR94(b) ( (uint8_t) 0xa0 < (uint8_t) (b) && (uint8_t) (b) < (uint8_t) 0xff ) #define CHK_GR94_2Byte(b1,b2) (CHK_GR94(b1) && CHK_GR94(b2)) /*================================================================================= =================================================================================*/ -typedef PRBool (*uSubScannerFunc) (unsigned char* in, uint16_t* out); +typedef int (*uSubScannerFunc) (unsigned char* in, uint16_t* out); /*================================================================================= =================================================================================*/ -typedef PRBool (*uScannerFunc) ( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); +typedef int (*uScannerFunc) ( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); -MODULE_PRIVATE PRBool uScan( - uScanClassID scanClass, +int uScan( + uScanClassID scanClass, + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); + +#define uSubScanner(sub,in,out) (* m_subscanner[sub])((in),(out)) + +int uCheckAndScanAlways1Byte( int32_t* state, unsigned char *in, uint16_t *out, uint32_t inbuflen, uint32_t* inscanlen ); - -#define uSubScanner(sub,in,out) (* m_subscanner[sub])((in),(out)) +int uCheckAndScanAlways2Byte( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); +int uCheckAndScanAlways2ByteShiftGR( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); +int uCheckAndScanAlways2ByteGR128( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); +int uScanShift( + uShiftInTable *shift, + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); -PRIVATE PRBool uCheckAndScanAlways1Byte( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -PRIVATE PRBool uCheckAndScanAlways2Byte( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -PRIVATE PRBool uCheckAndScanAlways2ByteShiftGR( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -PRIVATE PRBool uCheckAndScanAlways2ByteGR128( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -MODULE_PRIVATE PRBool uScanShift( - uShiftInTable *shift, +int uCheckAndScan2ByteGRPrefix8F( int32_t* state, unsigned char *in, uint16_t *out, uint32_t inbuflen, uint32_t* inscanlen ); +int uCheckAndScan2ByteGRPrefix8EA2( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); +int uCheckAndScan2ByteGRPrefix8EA3( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); +int uCheckAndScan2ByteGRPrefix8EA4( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); +int uCheckAndScan2ByteGRPrefix8EA5( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); +int uCheckAndScan2ByteGRPrefix8EA6( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); +int uCheckAndScan2ByteGRPrefix8EA7( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); +int uCnSAlways8BytesDecomposedHangul( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); +int uCheckAndScanJohabHangul( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); +int uCheckAndScanJohabSymbol( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8F( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8EA2( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8EA3( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8EA4( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8EA5( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8EA6( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8EA7( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -PRIVATE PRBool uCnSAlways8BytesDecomposedHangul( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -PRIVATE PRBool uCheckAndScanJohabHangul( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); -PRIVATE PRBool uCheckAndScanJohabSymbol( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); +int uCheckAndScan4BytesGB18030( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ); -PRIVATE PRBool uCheckAndScan4BytesGB18030( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ); +int uScanAlways2Byte( + unsigned char* in, + uint16_t* out + ); +int uScanAlways2ByteShiftGR( + unsigned char* in, + uint16_t* out + ); +int uScanAlways1Byte( + unsigned char* in, + uint16_t* out + ); +int uScanAlways1BytePrefix8E( + unsigned char* in, + uint16_t* out + ); +/*================================================================================= -PRIVATE PRBool uScanAlways2Byte( - unsigned char* in, - uint16_t* out - ); -PRIVATE PRBool uScanAlways2ByteShiftGR( - unsigned char* in, - uint16_t* out - ); -PRIVATE PRBool uScanAlways1Byte( - unsigned char* in, - uint16_t* out - ); -PRIVATE PRBool uScanAlways1BytePrefix8E( - unsigned char* in, - uint16_t* out - ); - /*================================================================================= - =================================================================================*/ -PRIVATE const uScannerFunc m_scanner[uNumOfCharsetType] = +const uScannerFunc m_scanner[uNumOfCharsetType] = { uCheckAndScanAlways1Byte, uCheckAndScanAlways2Byte, uCheckAndScanAlways2ByteShiftGR, uCheckAndScan2ByteGRPrefix8F, uCheckAndScan2ByteGRPrefix8EA2, uCheckAndScan2ByteGRPrefix8EA3, uCheckAndScan2ByteGRPrefix8EA4, @@ -185,460 +185,460 @@ PRIVATE const uScannerFunc m_scanner[uNu uCheckAndScan4BytesGB18030, uCheckAndScanAlways2ByteGR128 }; /*================================================================================= =================================================================================*/ -PRIVATE const uSubScannerFunc m_subscanner[uNumOfCharType] = +const uSubScannerFunc m_subscanner[uNumOfCharType] = { uScanAlways1Byte, uScanAlways2Byte, uScanAlways2ByteShiftGR, uScanAlways1BytePrefix8E }; /*================================================================================= =================================================================================*/ -MODULE_PRIVATE PRBool uScan( - uScanClassID scanClass, - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uScan( + uScanClassID scanClass, + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { return (* m_scanner[scanClass]) (state,in,out,inbuflen,inscanlen); } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uScanAlways1Byte( - unsigned char* in, - uint16_t* out - ) +int uScanAlways1Byte( + unsigned char* in, + uint16_t* out + ) { *out = (uint16_t) in[0]; - return PR_TRUE; + return 1; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uScanAlways2Byte( - unsigned char* in, - uint16_t* out - ) +int uScanAlways2Byte( + unsigned char* in, + uint16_t* out + ) { *out = (uint16_t) (( in[0] << 8) | (in[1])); - return PR_TRUE; + return 1; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uScanAlways2ByteShiftGR( - unsigned char* in, - uint16_t* out - ) +int uScanAlways2ByteShiftGR( + unsigned char* in, + uint16_t* out + ) { *out = (uint16_t) ((( in[0] << 8) | (in[1])) & 0x7F7F); - return PR_TRUE; + return 1; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uScanAlways1BytePrefix8E( - unsigned char* in, - uint16_t* out - ) +int uScanAlways1BytePrefix8E( + unsigned char* in, + uint16_t* out + ) { *out = (uint16_t) in[1]; - return PR_TRUE; + return 1; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndScanAlways1Byte( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScanAlways1Byte( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { /* Don't check inlen. The caller should ensure it is larger than 0 */ *inscanlen = 1; *out = (uint16_t) in[0]; - return PR_TRUE; + return 1; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndScanAlways2Byte( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScanAlways2Byte( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { if(inbuflen < 2) - return PR_FALSE; + return 0; else { *inscanlen = 2; *out = ((in[0] << 8) | ( in[1])) ; - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndScanAlways2ByteShiftGR( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScanAlways2ByteShiftGR( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { /* * Both bytes should be in the range of [0xa1,0xfe] for 94x94 character sets * invoked on GR. No encoding implemented in Mozilla uses 96x96 char. sets. * Only 2nd byte range needs to be checked because * 1st byte is checked before calling this in nsUnicodeDecoerHelper.cpp */ if(inbuflen < 2) /* will lead to NS_OK_UDEC_MOREINPUT */ - return PR_FALSE; + return 0; else if (! CHK_GR94(in[1])) { *inscanlen = 2; *out = 0xFF; /* for 2-byte table, uMap() is guaranteed to fail for 0xFF. */ - return PR_TRUE; + return 1; } else { *inscanlen = 2; *out = (((in[0] << 8) | ( in[1])) & 0x7F7F); - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndScanAlways2ByteGR128( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScanAlways2ByteGR128( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { /* * The first byte should be in [0xa1,0xfe] * and the second byte in [0x41,0xfe] * Used by CP949 -> Unicode converter. * Only 2nd byte range needs to be checked because * 1st byte is checked before calling this in nsUnicodeDecoderHelper.cpp */ if(inbuflen < 2) /* will lead to NS_OK_UDEC_MOREINPUT */ - return PR_FALSE; + return 0; else if (in[1] < 0x41) /* 2nd byte range check */ { *inscanlen = 2; *out = 0xFF; /* for 2-byte table, uMap() is guaranteed to fail for 0xFF. */ - return PR_TRUE; + return 1; } else { *inscanlen = 2; *out = (in[0] << 8) | in[1]; - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uScanShift( - uShiftInTable *shift, - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uScanShift( + uShiftInTable *shift, + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { int16_t i; const uShiftInCell* cell = &(shift->shiftcell[0]); int16_t itemnum = shift->numOfItem; for(i=0;i<itemnum;i++) { if( ( in[0] >= cell[i].shiftin_Min) && ( in[0] <= cell[i].shiftin_Max)) { if(inbuflen < cell[i].reserveLen) - return PR_FALSE; + return 0; else { *inscanlen = cell[i].reserveLen; return (uSubScanner(cell[i].classID,in,out)); } } } - return PR_FALSE; + return 0; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8F( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScan2ByteGRPrefix8F( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { if((inbuflen < 3) ||(in[0] != 0x8F)) - return PR_FALSE; + return 0; else if (! CHK_GR94(in[1])) /* 2nd byte range check */ { *inscanlen = 2; *out = 0xFF; /* for 2-byte table, uMap() is guaranteed to fail for 0xFF. */ - return PR_TRUE; + return 1; } else if (! CHK_GR94(in[2])) /* 3rd byte range check */ { *inscanlen = 3; *out = 0xFF; /* for 2-byte table, uMap() is guaranteed to fail for 0xFF. */ - return PR_TRUE; + return 1; } else { *inscanlen = 3; *out = (((in[1] << 8) | ( in[2])) & 0x7F7F); - return PR_TRUE; + return 1; } } /*================================================================================= =================================================================================*/ /* Macro definition to use for uCheckAndScan2ByteGRPrefix8EAX() * where X is 2,3,4,5,6,7 */ #define CNS_8EAX_4BYTE(PREFIX) \ if((inbuflen < 4) || (in[0] != 0x8E)) \ - return PR_FALSE; \ + return 0; \ else if((in[1] != (PREFIX))) \ { \ *inscanlen = 2; \ *out = 0xFF; \ - return PR_TRUE; \ + return 1; \ } \ else if(! CHK_GR94(in[2])) \ { \ *inscanlen = 3; \ *out = 0xFF; \ - return PR_TRUE; \ + return 1; \ } \ else if(! CHK_GR94(in[3])) \ { \ *inscanlen = 4; \ *out = 0xFF; \ - return PR_TRUE; \ + return 1; \ } \ else \ { \ *inscanlen = 4; \ *out = (((in[2] << 8) | ( in[3])) & 0x7F7F); \ - return PR_TRUE; \ + return 1; \ } -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8EA2( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScan2ByteGRPrefix8EA2( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { CNS_8EAX_4BYTE(0xA2) } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8EA3( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScan2ByteGRPrefix8EA3( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { CNS_8EAX_4BYTE(0xA3) } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8EA4( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScan2ByteGRPrefix8EA4( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { CNS_8EAX_4BYTE(0xA4) } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8EA5( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScan2ByteGRPrefix8EA5( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { CNS_8EAX_4BYTE(0xA5) } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8EA6( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScan2ByteGRPrefix8EA6( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { CNS_8EAX_4BYTE(0xA6) } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndScan2ByteGRPrefix8EA7( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScan2ByteGRPrefix8EA7( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { CNS_8EAX_4BYTE(0xA7) } /*================================================================================= =================================================================================*/ #define SBase 0xAC00 #define SCount 11172 #define LCount 19 #define VCount 21 #define TCount 28 #define NCount (VCount * TCount) -PRIVATE PRBool uCnSAlways8BytesDecomposedHangul( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCnSAlways8BytesDecomposedHangul( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { uint16_t LIndex, VIndex, TIndex; /* no 8 bytes, not in a4 range, or the first 2 byte are not a4d4 */ if((inbuflen < 8) || (0xa4 != in[0]) || (0xd4 != in[1]) || (0xa4 != in[2] ) || (0xa4 != in[4]) || (0xa4 != in[6])) - return PR_FALSE; + return 0; /* Compute LIndex */ if((in[3] < 0xa1) || (in[3] > 0xbe)) { /* illegal leading consonant */ - return PR_FALSE; + return 0; } else { static const uint8_t lMap[] = { /* A1 A2 A3 A4 A5 A6 A7 */ 0, 1,0xff, 2,0xff,0xff, 3, /* A8 A9 AA AB AC AD AE AF */ 4, 5,0xff,0xff,0xff,0xff,0xff,0xff, /* B0 B1 B2 B3 B4 B5 B6 B7 */ 0xff, 6, 7, 8,0xff, 9, 10, 11, /* B8 B9 BA BB BC BD BE */ 12, 13, 14, 15, 16, 17, 18 }; LIndex = lMap[in[3] - 0xa1]; if(0xff == (0xff & LIndex)) - return PR_FALSE; + return 0; } /* Compute VIndex */ if((in[5] < 0xbf) || (in[5] > 0xd3)) { /* illegal medial vowel */ - return PR_FALSE; + return 0; } else { VIndex = in[5] - 0xbf; } /* Compute TIndex */ if(0xd4 == in[7]) { TIndex = 0; } else if((in[7] < 0xa1) || (in[7] > 0xbe)) {/* illegal trailing consonant */ - return PR_FALSE; + return 0; } else { static const uint8_t tMap[] = { /* A1 A2 A3 A4 A5 A6 A7 */ 1, 2, 3, 4, 5, 6, 7, /* A8 A9 AA AB AC AD AE AF */ 0xff, 8, 9, 10, 11, 12, 13, 14, /* B0 B1 B2 B3 B4 B5 B6 B7 */ 15, 16, 17,0xff, 18, 19, 20, 21, /* B8 B9 BA BB BC BD BE */ 22,0xff, 23, 24, 25, 26, 27 }; TIndex = tMap[in[7] - 0xa1]; if(0xff == (0xff & TIndex)) - return PR_FALSE; + return 0; } *inscanlen = 8; /* the following line is from Unicode 2.0 page 3-13 item 5 */ *out = ( LIndex * VCount + VIndex) * TCount + TIndex + SBase; - return PR_TRUE; + return 1; } /*================================================================================= =================================================================================*/ -PRIVATE PRBool uCheckAndScanJohabHangul( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScanJohabHangul( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { /* since we don't have code to convert Johab to Unicode right now * * make this part of code #if 0 to save space until we fully test it */ if(inbuflen < 2) - return PR_FALSE; + return 0; else { /* * See Table 4-45 Johab Encoding's Five-Bit Binary Patterns in page 183 * of "CJKV Information Processing" for details */ static const uint8_t lMap[32]={ /* totaly 19 */ 0xff,0xff,0, 1, 2, 3, 4, 5, /* 0-7 */ 6, 7, 8, 9, 10, 11, 12, 13, /* 8-15 */ @@ -655,40 +655,40 @@ PRIVATE PRBool uCheckAndScanJohabHangul( 0xff,0, 1, 2, 3, 4, 5, 6, /* 0-7 */ 7, 8, 9, 10, 11, 12, 13, 14, /* 8-15 */ 15, 16, 0xff,17, 18, 19, 20, 21, /* 16-23 */ 22, 23, 24, 25, 26, 27, 0xff,0xff /* 24-31 */ }; uint16_t ch = (in[0] << 8) | in[1]; uint16_t LIndex, VIndex, TIndex; if(0 == (0x8000 & ch)) - return PR_FALSE; + return 0; LIndex=lMap[(ch>>10)& 0x1F]; VIndex=vMap[(ch>>5) & 0x1F]; TIndex=tMap[(ch>>0) & 0x1F]; if((0xff==(LIndex)) || (0xff==(VIndex)) || (0xff==(TIndex))) - return PR_FALSE; + return 0; /* the following line is from Unicode 2.0 page 3-13 item 5 */ *out = ( LIndex * VCount + VIndex) * TCount + TIndex + SBase; *inscanlen = 2; - return PR_TRUE; + return 1; } } -PRIVATE PRBool uCheckAndScanJohabSymbol( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScanJohabSymbol( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { if(inbuflen < 2) - return PR_FALSE; + return 0; else { /* * The following code are based on the Perl code lised under * "Johab to ISO-2022-KR or EUC-KR Conversion" in page 1014 of * "CJKV Information Processing" by Ken Lunde <lunde@adobe.com> * * sub johab2ks ($) { # Convert Johab to ISO-2022-KR * my @johab = unpack("C*", $_[0]); @@ -721,39 +721,39 @@ PRIVATE PRBool uCheckAndScanJohabSymbol( d8_off = 42; } *out = (((((hi - ((hi < 223) ? 200 : 187)) << 1) - (lo < 161 ? 1 : 0) + offset) + d8_off) << 8 ) | (lo - ((lo < 161) ? ((lo > 126) ? 34 : 16) : 128)); *inscanlen = 2; - return PR_TRUE; + return 1; } } -PRIVATE PRBool uCheckAndScan4BytesGB18030( - int32_t* state, - unsigned char *in, - uint16_t *out, - uint32_t inbuflen, - uint32_t* inscanlen - ) +int uCheckAndScan4BytesGB18030( + int32_t* state, + unsigned char *in, + uint16_t *out, + uint32_t inbuflen, + uint32_t* inscanlen + ) { uint32_t data; if(inbuflen < 4) - return PR_FALSE; + return 0; if((in[0] < 0x81 ) || (0xfe < in[0])) - return PR_FALSE; + return 0; if((in[1] < 0x30 ) || (0x39 < in[1])) - return PR_FALSE; + return 0; if((in[2] < 0x81 ) || (0xfe < in[2])) - return PR_FALSE; + return 0; if((in[3] < 0x30 ) || (0x39 < in[3])) - return PR_FALSE; + return 0; data = (((((in[0] - 0x81) * 10 + (in[1] - 0x30)) * 126) + (in[2] - 0x81)) * 10 ) + (in[3] - 0x30); *inscanlen = 4; *out = (data < 0x00010000) ? data : 0xFFFD; - return PR_TRUE; + return 1; }
--- a/intl/unicharutil/util/nsSpecialCasingData.h +++ b/intl/unicharutil/util/nsSpecialCasingData.h @@ -1,13 +1,12 @@ /* 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 "prtypes.h" #include <stdint.h> namespace mozilla { namespace unicode { // Multi-character mappings (from SpecialCasing.txt) map a single Unicode // value to a sequence of 2 or 3 Unicode characters. There are currently none // defined outside the BMP, so we can use char16_t here. Unused trailing
--- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -1280,16 +1280,19 @@ MessageChannel::ReportMessageRouteError( { PrintErrorMessage(mSide, channelName, "Need a route"); mListener->OnProcessingError(MsgRouteError); } void MessageChannel::ReportConnectionError(const char* aChannelName) const { + AssertWorkerThread(); + mMonitor->AssertCurrentThreadOwns(); + const char* errorMsg = nullptr; switch (mChannelState) { case ChannelClosed: errorMsg = "Closed channel: cannot send/recv"; break; case ChannelOpening: errorMsg = "Opening channel: not yet ready for send/recv"; break; @@ -1303,16 +1306,18 @@ MessageChannel::ReportConnectionError(co errorMsg = "Channel error: cannot send/recv"; break; default: NS_RUNTIMEABORT("unreached"); } PrintErrorMessage(mSide, aChannelName, errorMsg); + + MonitorAutoUnlock unlock(*mMonitor); mListener->OnProcessingError(MsgDropped); } bool MessageChannel::MaybeHandleError(Result code, const char* channelName) { if (MsgProcessed == code) return true;
--- a/js/jsd/jsd_scpt.cpp +++ b/js/jsd/jsd_scpt.cpp @@ -355,17 +355,17 @@ JSScript * jsd_GetJSScript (JSDContext *jsdc, JSDScript *script) { return script->script; } JSFunction * jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script) { - AutoSafeJSContext cx; // NB: Actually unused. + AutoSafeJSContext cx; return JS_GetScriptFunction(cx, script->script); } JSDScript* jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp) { JSDScript *jsdscript = *iterp;
--- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -603,16 +603,32 @@ function ArrayKeys() { */ function ComputeNumChunks(length) { var chunks = length >>> CHUNK_SHIFT; if (chunks << CHUNK_SHIFT === length) return chunks; return chunks + 1; } +#define SLICES_PER_WORKER 8 + +/** + * Compute the number of slices given an array length and the number of + * chunks. Used in tandem with the workstealing scheduler. + */ +function ComputeNumSlices(workers, length, chunks) { + if (length !== 0) { + var slices = workers * SLICES_PER_WORKER; + if (chunks < slices) + return workers; + return slices; + } + return workers; +} + /** * Computes the bounds for slice |sliceIndex| of |numItems| items, * assuming |numSlices| total slices. If numItems is not evenly * divisible by numSlices, then the final thread may have a bit of * extra work. */ function ComputeSliceBounds(numItems, sliceIndex, numSlices) { var sliceWidth = (numItems / numSlices) | 0; @@ -671,32 +687,33 @@ function ArrayMapPar(func, mode) { // - Breaking out of named blocks does not currently work (bug 684384); // - Unreachable Code Elim. can't properly handle if (a && b) (bug 669796) if (ShouldForceSequential()) break parallel; if (!TRY_PARALLEL(mode)) break parallel; var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); + var numWorkers = ForkJoinNumWorkers(); + var numSlices = ComputeNumSlices(numWorkers, length, chunks); var info = ComputeAllSliceBounds(chunks, numSlices); - ForkJoin(mapSlice, ForkJoinMode(mode)); + ForkJoin(mapSlice, ForkJoinMode(mode), numSlices); return buffer; } // Sequential fallback: ASSERT_SEQUENTIAL_IS_OK(mode); for (var i = 0; i < length; i++) { // Note: Unlike JS arrays, parallel arrays cannot have holes. var v = func(self[i], i, self); UnsafePutElements(buffer, i, v); } return buffer; - function mapSlice(sliceId, numSlices, warmup) { + function mapSlice(sliceId, warmup) { var chunkPos = info[SLICE_POS(sliceId)]; var chunkEnd = info[SLICE_END(sliceId)]; if (warmup && chunkEnd > chunkPos + 1) chunkEnd = chunkPos + 1; while (chunkPos < chunkEnd) { var indexStart = chunkPos << CHUNK_SHIFT; @@ -730,37 +747,38 @@ function ArrayReducePar(func, mode) { parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc if (ShouldForceSequential()) break parallel; if (!TRY_PARALLEL(mode)) break parallel; var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - if (chunks < numSlices) + var numWorkers = ForkJoinNumWorkers(); + if (chunks < numWorkers) break parallel; + var numSlices = ComputeNumSlices(numWorkers, length, chunks); var info = ComputeAllSliceBounds(chunks, numSlices); var subreductions = NewDenseArray(numSlices); - ForkJoin(reduceSlice, ForkJoinMode(mode)); + ForkJoin(reduceSlice, ForkJoinMode(mode), numSlices); var accumulator = subreductions[0]; for (var i = 1; i < numSlices; i++) accumulator = func(accumulator, subreductions[i]); return accumulator; } // Sequential fallback: ASSERT_SEQUENTIAL_IS_OK(mode); var accumulator = self[0]; for (var i = 1; i < length; i++) accumulator = func(accumulator, self[i]); return accumulator; - function reduceSlice(sliceId, numSlices, warmup) { + function reduceSlice(sliceId, warmup) { var chunkStart = info[SLICE_START(sliceId)]; var chunkPos = info[SLICE_POS(sliceId)]; var chunkEnd = info[SLICE_END(sliceId)]; // (*) This function is carefully designed so that the warmup // (which executes with chunkStart === chunkPos) will execute all // potential loads and stores. In particular, the warmup run // processes two chunks rather than one. Moreover, it stores @@ -819,23 +837,25 @@ function ArrayScanPar(func, mode) { parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc if (ShouldForceSequential()) break parallel; if (!TRY_PARALLEL(mode)) break parallel; var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - if (chunks < numSlices) + var numWorkers = ForkJoinNumWorkers(); + if (chunks < numWorkers) break parallel; + + var numSlices = ComputeNumSlices(numWorkers, length, chunks); var info = ComputeAllSliceBounds(chunks, numSlices); // Scan slices individually (see comment on phase1()). - ForkJoin(phase1, ForkJoinMode(mode)); + ForkJoin(phase1, ForkJoinMode(mode), numSlices); // Compute intermediates array (see comment on phase2()). var intermediates = []; var accumulator = buffer[finalElement(0)]; ARRAY_PUSH(intermediates, accumulator); for (var i = 1; i < numSlices - 1; i++) { accumulator = func(accumulator, buffer[finalElement(i)]); ARRAY_PUSH(intermediates, accumulator); @@ -845,17 +865,17 @@ function ArrayScanPar(func, mode) { // convert from chunks to indices (see comment on phase2()). for (var i = 0; i < numSlices; i++) { info[SLICE_POS(i)] = info[SLICE_START(i)] << CHUNK_SHIFT; info[SLICE_END(i)] = info[SLICE_END(i)] << CHUNK_SHIFT; } info[SLICE_END(numSlices - 1)] = std_Math_min(info[SLICE_END(numSlices - 1)], length); // Complete each slice using intermediates array (see comment on phase2()). - ForkJoin(phase2, ForkJoinMode(mode)); + ForkJoin(phase2, ForkJoinMode(mode), numSlices); return buffer; } // Sequential fallback: ASSERT_SEQUENTIAL_IS_OK(mode); scan(self[0], 0, length); return buffer; @@ -879,17 +899,17 @@ function ArrayScanPar(func, mode) { * result array like: * * [A, A+B, A+B+C, D, D+E, D+E+F, G, G+H, G+H+I] * ^~~~~~~~~~~~^ ^~~~~~~~~~~~^ ^~~~~~~~~~~~~^ * Slice 0 Slice 1 Slice 2 * * Read on in phase2 to see what we do next! */ - function phase1(sliceId, numSlices, warmup) { + function phase1(sliceId, warmup) { var chunkStart = info[SLICE_START(sliceId)]; var chunkPos = info[SLICE_POS(sliceId)]; var chunkEnd = info[SLICE_END(sliceId)]; if (warmup && chunkEnd > chunkPos + 2) chunkEnd = chunkPos + 2; if (chunkPos === chunkStart) { @@ -962,17 +982,17 @@ function ArrayScanPar(func, mode) { * SUBTLE: Because we are mutating |buffer| in place, we have to * be very careful about bailouts! We cannot checkpoint a chunk * at a time as we do elsewhere because that assumes it is safe to * replay the portion of a chunk which was already processed. * Therefore, in this phase, we track the current position at an * index granularity, although this requires two memory writes per * index. */ - function phase2(sliceId, numSlices, warmup) { + function phase2(sliceId, warmup) { if (sliceId === 0) return true; // No work to do for the 0th slice. var indexPos = info[SLICE_POS(sliceId)]; var indexEnd = info[SLICE_END(sliceId)]; if (warmup) indexEnd = std_Math_min(indexEnd, indexPos + CHUNK_SIZE); @@ -1097,33 +1117,33 @@ function ArrayScatterPar(targets, defaul ThrowError(JSMSG_PAR_ARRAY_SCATTER_CONFLICT); return conflictFunc(elem1, elem2); } function parDivideOutputRange() { var chunks = ComputeNumChunks(targetsLength); - var numSlices = ForkJoinSlices(); + var numSlices = ComputeNumSlices(ForkJoinNumWorkers(), length, chunks); var checkpoints = NewDenseArray(numSlices); for (var i = 0; i < numSlices; i++) UnsafePutElements(checkpoints, i, 0); var buffer = NewDenseArray(length); var conflicts = NewDenseArray(length); for (var i = 0; i < length; i++) { UnsafePutElements(buffer, i, defaultValue); UnsafePutElements(conflicts, i, false); } - ForkJoin(fill, ForkJoinMode(mode)); + ForkJoin(fill, ForkJoinMode(mode), numSlices); return buffer; - function fill(sliceId, numSlices, warmup) { + function fill(sliceId, warmup) { var indexPos = checkpoints[sliceId]; var indexEnd = targetsLength; if (warmup) indexEnd = std_Math_min(indexEnd, indexPos + CHUNK_SIZE); // Range in the output for which we are responsible: var [outputStart, outputEnd] = ComputeSliceBounds(length, sliceId, numSlices); @@ -1144,17 +1164,17 @@ function ArrayScatterPar(targets, defaul } function parDivideScatterVector() { // Subtle: because we will be mutating the localBuffers and // conflict arrays in place, we can never replay an entry in the // target array for fear of inducing a conflict where none existed // before. Therefore, we must proceed not by chunks but rather by // individual indices. - var numSlices = ForkJoinSlices(); + var numSlices = ComputeNumSlices(ForkJoinNumWorkers(), length, ComputeNumChunks(length)); var info = ComputeAllSliceBounds(targetsLength, numSlices); // FIXME(bug 844890): Use typed arrays here. var localBuffers = NewDenseArray(numSlices); for (var i = 0; i < numSlices; i++) UnsafePutElements(localBuffers, i, NewDenseArray(length)); var localConflicts = NewDenseArray(numSlices); for (var i = 0; i < numSlices; i++) { @@ -1167,21 +1187,21 @@ function ArrayScatterPar(targets, defaul // Initialize the 0th buffer, which will become the output. For // the other buffers, we track which parts have been written to // using the conflict buffer so they do not need to be // initialized. var outputBuffer = localBuffers[0]; for (var i = 0; i < length; i++) UnsafePutElements(outputBuffer, i, defaultValue); - ForkJoin(fill, ForkJoinMode(mode)); + ForkJoin(fill, ForkJoinMode(mode), numSlices); mergeBuffers(); return outputBuffer; - function fill(sliceId, numSlices, warmup) { + function fill(sliceId, warmup) { var indexPos = info[SLICE_POS(sliceId)]; var indexEnd = info[SLICE_END(sliceId)]; if (warmup) indexEnd = std_Math_min(indexEnd, indexPos + CHUNK_SIZE); var localbuffer = localBuffers[sliceId]; var conflicts = localConflicts[sliceId]; while (indexPos < indexEnd) { @@ -1270,43 +1290,44 @@ function ArrayFilterPar(func, mode) { parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc if (ShouldForceSequential()) break parallel; if (!TRY_PARALLEL(mode)) break parallel; var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); - if (chunks < numSlices * 2) + var numWorkers = ForkJoinNumWorkers(); + if (chunks < numWorkers * 2) break parallel; + var numSlices = ComputeNumSlices(numWorkers, length, chunks); var info = ComputeAllSliceBounds(chunks, numSlices); // Step 1. Compute which items from each slice of the result // buffer should be preserved. When we're done, we have an array // |survivors| containing a bitset for each chunk, indicating // which members of the chunk survived. We also keep an array // |counts| containing the total number of items that are being // preserved from within one slice. // // FIXME(bug 844890): Use typed arrays here. var counts = NewDenseArray(numSlices); for (var i = 0; i < numSlices; i++) UnsafePutElements(counts, i, 0); var survivors = NewDenseArray(chunks); - ForkJoin(findSurvivorsInSlice, ForkJoinMode(mode)); + ForkJoin(findSurvivorsInSlice, ForkJoinMode(mode), numSlices); // Step 2. Compress the slices into one contiguous set. var count = 0; for (var i = 0; i < numSlices; i++) count += counts[i]; var buffer = NewDenseArray(count); if (count > 0) - ForkJoin(copySurvivorsInSlice, ForkJoinMode(mode)); + ForkJoin(copySurvivorsInSlice, ForkJoinMode(mode), numSlices); return buffer; } // Sequential fallback: ASSERT_SEQUENTIAL_IS_OK(mode); var buffer = []; for (var i = 0; i < length; i++) { @@ -1317,17 +1338,17 @@ function ArrayFilterPar(func, mode) { return buffer; /** * As described above, our goal is to determine which items we * will preserve from a given slice. We do this one chunk at a * time. When we finish a chunk, we record our current count and * the next chunk sliceId, lest we should bail. */ - function findSurvivorsInSlice(sliceId, numSlices, warmup) { + function findSurvivorsInSlice(sliceId, warmup) { var chunkPos = info[SLICE_POS(sliceId)]; var chunkEnd = info[SLICE_END(sliceId)]; if (warmup && chunkEnd > chunkPos) chunkEnd = chunkPos + 1; var count = counts[sliceId]; while (chunkPos < chunkEnd) { @@ -1344,17 +1365,17 @@ function ArrayFilterPar(func, mode) { UnsafePutElements(survivors, chunkPos, chunkBits, counts, sliceId, count, info, SLICE_POS(sliceId), ++chunkPos); } return chunkEnd === info[SLICE_END(sliceId)]; } - function copySurvivorsInSlice(sliceId, numSlices, warmup) { + function copySurvivorsInSlice(sliceId, warmup) { // Copies the survivors from this slice into the correct position. // Note that this is an idempotent operation that does not invoke // user code. Therefore, we don't expect bailouts and make an // effort to proceed chunk by chunk or avoid duplicating work. // Total up the items preserved by previous slices. var count = 0; if (sliceId > 0) { // FIXME(#819219)---work around a bug in Ion's range checks @@ -1427,28 +1448,29 @@ function ArrayStaticBuildPar(length, fun parallel: for (;;) { if (ShouldForceSequential()) break parallel; if (!TRY_PARALLEL(mode)) break parallel; var chunks = ComputeNumChunks(length); - var numSlices = ForkJoinSlices(); + var numWorkers = ForkJoinNumWorkers(); + var numSlices = ComputeNumSlices(numWorkers, length, chunks); var info = ComputeAllSliceBounds(chunks, numSlices); - ForkJoin(constructSlice, ForkJoinMode(mode)); + ForkJoin(constructSlice, ForkJoinMode(mode), numSlices); return buffer; } // Sequential fallback: ASSERT_SEQUENTIAL_IS_OK(mode); fill(0, length); return buffer; - function constructSlice(sliceId, numSlices, warmup) { + function constructSlice(sliceId, warmup) { var chunkPos = info[SLICE_POS(sliceId)]; var chunkEnd = info[SLICE_END(sliceId)]; if (warmup && chunkEnd > chunkPos) chunkEnd = chunkPos + 1; while (chunkPos < chunkEnd) { var indexStart = chunkPos << CHUNK_SHIFT;
--- a/js/src/config/rules.mk +++ b/js/src/config/rules.mk @@ -119,19 +119,16 @@ SIMPLE_PROGRAMS += $(CPP_UNIT_TEST_BINS) INCLUDES += -I$(DIST)/include/testing LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) ifndef MOZ_PROFILE_GENERATE libs:: $(CPP_UNIT_TEST_BINS) $(call mkdir_deps,$(DIST)/cppunittests) $(NSINSTALL) $(CPP_UNIT_TEST_BINS) $(DIST)/cppunittests endif -check:: - @$(PYTHON) $(topsrcdir)/testing/runcppunittests.py --xre-path=$(DIST)/bin --symbols-path=$(DIST)/crashreporter-symbols $(subst .cpp,$(BIN_SUFFIX),$(CPP_UNIT_TESTS)) - cppunittests-remote: DM_TRANS?=adb cppunittests-remote: @if [ '${TEST_DEVICE}' != '' -o '$(DM_TRANS)' = 'adb' ]; then \ $(PYTHON) -u $(topsrcdir)/testing/remotecppunittests.py \ --xre-path=$(DEPTH)/dist/bin \ --localLib=$(DEPTH)/dist/$(MOZ_APP_NAME) \ --dm_trans=$(DM_TRANS) \ --deviceIP=${TEST_DEVICE} \
--- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -67,17 +67,19 @@ CheckArgumentsWithinEval(JSContext *cx, // It's an error to use |arguments| in a function that has a rest // parameter. parser.report(ParseError, false, nullptr, JSMSG_ARGUMENTS_AND_REST); return false; } // Force construction of arguments objects for functions that use // |arguments| within an eval. - RootedScript script(cx, fun->nonLazyScript()); + RootedScript script(cx, fun->getOrCreateScript(cx)); + if (!script) + return false; if (script->argumentsHasVarBinding()) { if (!JSScript::argumentsOptimizationFailed(cx, script)) return false; } // It's an error to use |arguments| in a legacy generator expression. if (script->isGeneratorExp() && script->isLegacyGenerator()) { parser.report(ParseError, false, nullptr, JSMSG_BAD_GENEXP_BODY, js_arguments_str); @@ -118,17 +120,19 @@ MaybeCheckEvalFreeVariables(ExclusiveCon // If the eval'ed script contains any debugger statement, force construction // of arguments objects for the caller script and any other scripts it is // transitively nested inside. The debugger can access any variable on the // scope chain. if (pc.sc->hasDebuggerStatement()) { RootedObject scope(cx, scopeChain); while (scope->is<ScopeObject>() || scope->is<DebugScopeObject>()) { if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) { - RootedScript script(cx, scope->as<CallObject>().callee().nonLazyScript()); + RootedScript script(cx, scope->as<CallObject>().callee().getOrCreateScript(cx)); + if (!script) + return false; if (script->argumentsHasVarBinding()) { if (!JSScript::argumentsOptimizationFailed(cx, script)) return false; } } scope = scope->enclosingScope(); } } @@ -230,20 +234,18 @@ frontend::CompileScript(ExclusiveContext Parser<FullParseHandler> parser(cx, alloc, options, chars, length, /* foldConstants = */ true, canLazilyParse ? &syntaxParser.ref() : nullptr, nullptr); parser.sct = sct; parser.ss = ss; Directives directives(options.strictOption); GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption); - bool savedCallerFun = - options.compileAndGo && - evalCaller && - (evalCaller->function() || evalCaller->savedCallerFun()); + bool savedCallerFun = options.compileAndGo && + evalCaller && evalCaller->functionOrCallerFunction(); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, sourceObject, 0, length)); if (!script) return nullptr; // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). InternalHandle<Bindings*> bindings(script, &script->bindings); @@ -399,17 +401,17 @@ frontend::CompileScript(ExclusiveContext return nullptr; return script; } bool frontend::CompileLazyFunction(JSContext *cx, Handle<LazyScript*> lazy, const jschar *chars, size_t length) { - JS_ASSERT(cx->compartment() == lazy->function()->compartment()); + JS_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment()); CompileOptions options(cx, lazy->version()); options.setPrincipals(cx->compartment()->principals) .setOriginPrincipals(lazy->originPrincipals()) .setFileAndLine(lazy->source()->filename(), lazy->lineno()) .setColumn(lazy->column()) .setCompileAndGo(true) .setNoScriptRval(false) @@ -422,17 +424,17 @@ frontend::CompileLazyFunction(JSContext options); #endif Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length, /* foldConstants = */ true, nullptr, lazy); uint32_t staticLevel = lazy->staticLevel(cx); - Rooted<JSFunction*> fun(cx, lazy->function()); + Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying()); JS_ASSERT(!lazy->isLegacyGenerator()); ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict(), lazy->generatorKind()); if (!pn) return false; if (!NameFunctions(cx, pn)) return false;
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1280,17 +1280,17 @@ TryConvertFreeName(BytecodeEmitter *bce, // Use generic ops if a catch block is encountered. return false; } if (ssi.hasDynamicScopeObject()) hops++; continue; } RootedScript script(bce->sc->context, ssi.funScript()); - if (script->function()->atom() == pn->pn_atom) + if (script->functionNonDelazifying()->atom() == pn->pn_atom) return false; if (ssi.hasDynamicScopeObject()) { uint16_t slot; if (LookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot)) { JSOp op; switch (pn->getOp()) { case JSOP_NAME: op = JSOP_GETALIASEDVAR; break; case JSOP_SETNAME: op = JSOP_SETALIASEDVAR; break; @@ -1862,17 +1862,17 @@ BytecodeEmitter::needsImplicitThis() void BytecodeEmitter::tellDebuggerAboutCompiledScript(ExclusiveContext *cx) { // Note: when parsing off thread the resulting scripts need to be handed to // the debugger after rejoining to the main thread. if (!cx->isJSContext()) return; - RootedFunction function(cx, script->function()); + RootedFunction function(cx, script->functionNonDelazifying()); CallNewScriptHook(cx->asJSContext(), script, function); // Lazy scripts are never top level (despite always being invoked with a // nullptr parent), and so the hook should never be fired. if (emitterMode != LazyFunction && !parent) { GlobalObject *compileAndGoGlobal = nullptr; if (script->compileAndGo()) compileAndGoGlobal = &script->global(); Debugger::onNewScript(cx->asJSContext(), script, compileAndGoGlobal); @@ -2790,17 +2790,17 @@ frontend::EmitFunctionScript(ExclusiveCo * initializers created within it may be given more precise types. */ if (runOnce) { bce->script->setTreatAsRunOnce(); JS_ASSERT(!bce->script->hasRunOnce()); } /* Initialize fun->script() so that the debugger has a valid fun->script(). */ - RootedFunction fun(cx, bce->script->function()); + RootedFunction fun(cx, bce->script->functionNonDelazifying()); JS_ASSERT(fun->isInterpreted()); if (fun->isInterpretedLazy()) { AutoLockForCompilation lock(cx); fun->setUnlazifiedScript(bce->script); } else { fun->setScript(bce->script); }
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/regexp-match-limit.js @@ -0,0 +1,9 @@ +// See bug 953013 + +load(libdir + "asserts.js"); + +function test() { + var input = Array(999999+1).join('a'); + var result = /^([\w])+$/.test(input); +} +assertThrowsInstanceOf(test, InternalError);
--- a/js/src/jit-test/tests/basic/string-endswith.js +++ b/js/src/jit-test/tests/basic/string-endswith.js @@ -1,39 +1,262 @@ -assertEq("abc".endsWith("abc"), true); -assertEq("abcd".endsWith("bcd"), true); -assertEq("abc".endsWith("c"), true); -assertEq("abc".endsWith("abcd"), false); -assertEq("abc".endsWith("bbc"), false); -assertEq("abc".endsWith("b"), false); -assertEq("abc".endsWith("abc", 3), true); -assertEq("abc".endsWith("bc", 3), true); -assertEq("abc".endsWith("a", 3), false); -assertEq("abc".endsWith("bc", 3), true); -assertEq("abc".endsWith("a", 1), true); -assertEq("abc".endsWith("abc", 1), false); -assertEq("abc".endsWith("b", 2), true); -assertEq("abc".endsWith("d", 2), false); -assertEq("abc".endsWith("dcd", 2), false); -assertEq("abc".endsWith("a", 42), false); -assertEq("abc".endsWith("bc", Infinity), true); -assertEq("abc".endsWith("a", Infinity), false); -assertEq("abc".endsWith("bc", undefined), true); -assertEq("abc".endsWith("bc", -43), false); -assertEq("abc".endsWith("bc", -Infinity), false); -assertEq("abc".endsWith("bc", NaN), false); -var myobj = {toString : (function () "abc"), endsWith : String.prototype.endsWith}; -assertEq(myobj.endsWith("abc"), true); -assertEq(myobj.endsWith("ab"), false); -var gotStr = false, gotPos = false; -myobj = {toString : (function () { - assertEq(gotPos, false); - gotStr = true; - return "xyz"; -}), -endsWith : String.prototype.endsWith}; -var idx = {valueOf : (function () { - assertEq(gotStr, true); - gotPos = true; - return 42; -})}; -myobj.endsWith("elephant", idx); -assertEq(gotPos, true); +/* +* Copyright (c) 2013 Mathias Bynens. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +function assertThrows(fun, errorType) { + try { + fun(); + assertEq(true, false, "Expected error, but none was thrown"); + } catch (e) { + assertEq(e instanceof errorType, true, "Wrong error type thrown"); + } +} +assertEq(String.prototype.endsWith.length, 1); +assertEq(String.prototype.propertyIsEnumerable('endsWith'), false); + +assertEq('undefined'.endsWith(), true); +assertEq('undefined'.endsWith(undefined), true); +assertEq('undefined'.endsWith(null), false); +assertEq('null'.endsWith(), false); +assertEq('null'.endsWith(undefined), false); +assertEq('null'.endsWith(null), true); + +assertEq('abc'.endsWith(), false); +assertEq('abc'.endsWith(''), true); +assertEq('abc'.endsWith('\0'), false); +assertEq('abc'.endsWith('c'), true); +assertEq('abc'.endsWith('b'), false); +assertEq('abc'.endsWith('a'), false); +assertEq('abc'.endsWith('ab'), false); +assertEq('abc'.endsWith('bc'), true); +assertEq('abc'.endsWith('abc'), true); +assertEq('abc'.endsWith('bcd'), false); +assertEq('abc'.endsWith('abcd'), false); +assertEq('abc'.endsWith('bcde'), false); + +assertEq('abc'.endsWith('', NaN), true); +assertEq('abc'.endsWith('\0', NaN), false); +assertEq('abc'.endsWith('c', NaN), false); +assertEq('abc'.endsWith('b', NaN), false); +assertEq('abc'.endsWith('a', NaN), false); +assertEq('abc'.endsWith('ab', NaN), false); +assertEq('abc'.endsWith('bc', NaN), false); +assertEq('abc'.endsWith('abc', NaN), false); +assertEq('abc'.endsWith('bcd', NaN), false); +assertEq('abc'.endsWith('abcd', NaN), false); +assertEq('abc'.endsWith('bcde', NaN), false); + +assertEq('abc'.endsWith('', false), true); +assertEq('abc'.endsWith('\0', false), false); +assertEq('abc'.endsWith('c', false), false); +assertEq('abc'.endsWith('b', false), false); +assertEq('abc'.endsWith('a', false), false); +assertEq('abc'.endsWith('ab', false), false); +assertEq('abc'.endsWith('bc', false), false); +assertEq('abc'.endsWith('abc', false), false); +assertEq('abc'.endsWith('bcd', false), false); +assertEq('abc'.endsWith('abcd', false), false); +assertEq('abc'.endsWith('bcde', false), false); + +assertEq('abc'.endsWith('', undefined), true); +assertEq('abc'.endsWith('\0', undefined), false); +assertEq('abc'.endsWith('c', undefined), true); +assertEq('abc'.endsWith('b', undefined), false); +assertEq('abc'.endsWith('a', undefined), false); +assertEq('abc'.endsWith('ab', undefined), false); +assertEq('abc'.endsWith('bc', undefined), true); +assertEq('abc'.endsWith('abc', undefined), true); +assertEq('abc'.endsWith('bcd', undefined), false); +assertEq('abc'.endsWith('abcd', undefined), false); +assertEq('abc'.endsWith('bcde', undefined), false); + +assertEq('abc'.endsWith('', null), true); +assertEq('abc'.endsWith('\0', null), false); +assertEq('abc'.endsWith('c', null), false); +assertEq('abc'.endsWith('b', null), false); +assertEq('abc'.endsWith('a', null), false); +assertEq('abc'.endsWith('ab', null), false); +assertEq('abc'.endsWith('bc', null), false); +assertEq('abc'.endsWith('abc', null), false); +assertEq('abc'.endsWith('bcd', null), false); +assertEq('abc'.endsWith('abcd', null), false); +assertEq('abc'.endsWith('bcde', null), false); + +assertEq('abc'.endsWith('', -Infinity), true); +assertEq('abc'.endsWith('\0', -Infinity), false); +assertEq('abc'.endsWith('c', -Infinity), false); +assertEq('abc'.endsWith('b', -Infinity), false); +assertEq('abc'.endsWith('a', -Infinity), false); +assertEq('abc'.endsWith('ab', -Infinity), false); +assertEq('abc'.endsWith('bc', -Infinity), false); +assertEq('abc'.endsWith('abc', -Infinity), false); +assertEq('abc'.endsWith('bcd', -Infinity), false); +assertEq('abc'.endsWith('abcd', -Infinity), false); +assertEq('abc'.endsWith('bcde', -Infinity), false); + +assertEq('abc'.endsWith('', -1), true); +assertEq('abc'.endsWith('\0', -1), false); +assertEq('abc'.endsWith('c', -1), false); +assertEq('abc'.endsWith('b', -1), false); +assertEq('abc'.endsWith('a', -1), false); +assertEq('abc'.endsWith('ab', -1), false); +assertEq('abc'.endsWith('bc', -1), false); +assertEq('abc'.endsWith('abc', -1), false); +assertEq('abc'.endsWith('bcd', -1), false); +assertEq('abc'.endsWith('abcd', -1), false); +assertEq('abc'.endsWith('bcde', -1), false); + +assertEq('abc'.endsWith('', -0), true); +assertEq('abc'.endsWith('\0', -0), false); +assertEq('abc'.endsWith('c', -0), false); +assertEq('abc'.endsWith('b', -0), false); +assertEq('abc'.endsWith('a', -0), false); +assertEq('abc'.endsWith('ab', -0), false); +assertEq('abc'.endsWith('bc', -0), false); +assertEq('abc'.endsWith('abc', -0), false); +assertEq('abc'.endsWith('bcd', -0), false); +assertEq('abc'.endsWith('abcd', -0), false); +assertEq('abc'.endsWith('bcde', -0), false); + +assertEq('abc'.endsWith('', +0), true); +assertEq('abc'.endsWith('\0', +0), false); +assertEq('abc'.endsWith('c', +0), false); +assertEq('abc'.endsWith('b', +0), false); +assertEq('abc'.endsWith('a', +0), false); +assertEq('abc'.endsWith('ab', +0), false); +assertEq('abc'.endsWith('bc', +0), false); +assertEq('abc'.endsWith('abc', +0), false); +assertEq('abc'.endsWith('bcd', +0), false); +assertEq('abc'.endsWith('abcd', +0), false); +assertEq('abc'.endsWith('bcde', +0), false); + +assertEq('abc'.endsWith('', 1), true); +assertEq('abc'.endsWith('\0', 1), false); +assertEq('abc'.endsWith('c', 1), false); +assertEq('abc'.endsWith('b', 1), false); +assertEq('abc'.endsWith('ab', 1), false); +assertEq('abc'.endsWith('bc', 1), false); +assertEq('abc'.endsWith('abc', 1), false); +assertEq('abc'.endsWith('bcd', 1), false); +assertEq('abc'.endsWith('abcd', 1), false); +assertEq('abc'.endsWith('bcde', 1), false); + +assertEq('abc'.endsWith('', 2), true); +assertEq('abc'.endsWith('\0', 2), false); +assertEq('abc'.endsWith('c', 2), false); +assertEq('abc'.endsWith('b', 2), true); +assertEq('abc'.endsWith('ab', 2), true); +assertEq('abc'.endsWith('bc', 2), false); +assertEq('abc'.endsWith('abc', 2), false); +assertEq('abc'.endsWith('bcd', 2), false); +assertEq('abc'.endsWith('abcd', 2), false); +assertEq('abc'.endsWith('bcde', 2), false); + +assertEq('abc'.endsWith('', +Infinity), true); +assertEq('abc'.endsWith('\0', +Infinity), false); +assertEq('abc'.endsWith('c', +Infinity), true); +assertEq('abc'.endsWith('b', +Infinity), false); +assertEq('abc'.endsWith('a', +Infinity), false); +assertEq('abc'.endsWith('ab', +Infinity), false); +assertEq('abc'.endsWith('bc', +Infinity), true); +assertEq('abc'.endsWith('abc', +Infinity), true); +assertEq('abc'.endsWith('bcd', +Infinity), false); +assertEq('abc'.endsWith('abcd', +Infinity), false); +assertEq('abc'.endsWith('bcde', +Infinity), false); + +assertEq('abc'.endsWith('', true), true); +assertEq('abc'.endsWith('\0', true), false); +assertEq('abc'.endsWith('c', true), false); +assertEq('abc'.endsWith('b', true), false); +assertEq('abc'.endsWith('ab', true), false); +assertEq('abc'.endsWith('bc', true), false); +assertEq('abc'.endsWith('abc', true), false); +assertEq('abc'.endsWith('bcd', true), false); +assertEq('abc'.endsWith('abcd', true), false); +assertEq('abc'.endsWith('bcde', true), false); + +assertEq('abc'.endsWith('', 'x'), true); +assertEq('abc'.endsWith('\0', 'x'), false); +assertEq('abc'.endsWith('c', 'x'), false); +assertEq('abc'.endsWith('b', 'x'), false); +assertEq('abc'.endsWith('a', 'x'), false); +assertEq('abc'.endsWith('ab', 'x'), false); +assertEq('abc'.endsWith('bc', 'x'), false); +assertEq('abc'.endsWith('abc', 'x'), false); +assertEq('abc'.endsWith('bcd', 'x'), false); +assertEq('abc'.endsWith('abcd', 'x'), false); +assertEq('abc'.endsWith('bcde', 'x'), false); + +assertEq('[a-z]+(bar)?'.endsWith('(bar)?'), true); +assertThrows(function() { '[a-z]+(bar)?'.endsWith(/(bar)?/); }, TypeError); +assertEq('[a-z]+(bar)?'.endsWith('[a-z]+', 6), true); +assertThrows(function() { '[a-z]+(bar)?'.endsWith(/(bar)?/); }, TypeError); +assertThrows(function() { '[a-z]+/(bar)?/'.endsWith(/(bar)?/); }, TypeError); +var global = newGlobal(); +global.eval('this.re = /(bar)?/'); +assertThrows(function() { '[a-z]+/(bar)?/'.endsWith(global.re); }, TypeError); + +// http://mathiasbynens.be/notes/javascript-unicode#poo-test +var string = 'I\xF1t\xEBrn\xE2ti\xF4n\xE0liz\xE6ti\xF8n\u2603\uD83D\uDCA9'; +assertEq(string.endsWith(''), true); +assertEq(string.endsWith('\xF1t\xEBr'), false); +assertEq(string.endsWith('\xF1t\xEBr', 5), true); +assertEq(string.endsWith('\xE0liz\xE6'), false); +assertEq(string.endsWith('\xE0liz\xE6', 16), true); +assertEq(string.endsWith('\xF8n\u2603\uD83D\uDCA9'), true); +assertEq(string.endsWith('\xF8n\u2603\uD83D\uDCA9', 23), true); +assertEq(string.endsWith('\u2603'), false); +assertEq(string.endsWith('\u2603', 21), true); +assertEq(string.endsWith('\uD83D\uDCA9'), true); +assertEq(string.endsWith('\uD83D\uDCA9', 23), true); + +assertThrows(function() { String.prototype.endsWith.call(undefined); }, TypeError); +assertThrows(function() { String.prototype.endsWith.call(undefined, 'b'); }, TypeError); +assertThrows(function() { String.prototype.endsWith.call(undefined, 'b', 4); }, TypeError); +assertThrows(function() { String.prototype.endsWith.call(null); }, TypeError); +assertThrows(function() { String.prototype.endsWith.call(null, 'b'); }, TypeError); +assertThrows(function() { String.prototype.endsWith.call(null, 'b', 4); }, TypeError); +assertEq(String.prototype.endsWith.call(42, '2'), true); +assertEq(String.prototype.endsWith.call(42, '4'), false); +assertEq(String.prototype.endsWith.call(42, 'b', 4), false); +assertEq(String.prototype.endsWith.call(42, '2', 1), false); +assertEq(String.prototype.endsWith.call(42, '2', 4), true); +assertEq(String.prototype.endsWith.call({ 'toString': function() { return 'abc'; } }, 'b', 0), false); +assertEq(String.prototype.endsWith.call({ 'toString': function() { return 'abc'; } }, 'b', 1), false); +assertEq(String.prototype.endsWith.call({ 'toString': function() { return 'abc'; } }, 'b', 2), true); +assertThrows(function() { String.prototype.endsWith.call({ 'toString': function() { throw RangeError(); } }, /./); }, RangeError); +assertThrows(function() { String.prototype.endsWith.call({ 'toString': function() { return 'abc' } }, /./); }, TypeError); + +assertThrows(function() { String.prototype.endsWith.apply(undefined); }, TypeError); +assertThrows(function() { String.prototype.endsWith.apply(undefined, ['b']); }, TypeError); +assertThrows(function() { String.prototype.endsWith.apply(undefined, ['b', 4]); }, TypeError); +assertThrows(function() { String.prototype.endsWith.apply(null); }, TypeError); +assertThrows(function() { String.prototype.endsWith.apply(null, ['b']); }, TypeError); +assertThrows(function() { String.prototype.endsWith.apply(null, ['b', 4]); }, TypeError); +assertEq(String.prototype.endsWith.apply(42, ['2']), true); +assertEq(String.prototype.endsWith.apply(42, ['4']), false); +assertEq(String.prototype.endsWith.apply(42, ['b', 4]), false); +assertEq(String.prototype.endsWith.apply(42, ['2', 1]), false); +assertEq(String.prototype.endsWith.apply(42, ['2', 4]), true); +assertEq(String.prototype.endsWith.apply({ 'toString': function() { return 'abc'; } }, ['b', 0]), false); +assertEq(String.prototype.endsWith.apply({ 'toString': function() { return 'abc'; } }, ['b', 1]), false); +assertEq(String.prototype.endsWith.apply({ 'toString': function() { return 'abc'; } }, ['b', 2]), true); +assertThrows(function() { String.prototype.endsWith.apply({ 'toString': function() { throw RangeError(); } }, [/./]); }, RangeError); +assertThrows(function() { String.prototype.endsWith.apply({ 'toString': function() { return 'abc' } }, [/./]); }, TypeError);
--- a/js/src/jit-test/tests/basic/string-startswith.js +++ b/js/src/jit-test/tests/basic/string-startswith.js @@ -1,39 +1,243 @@ -assertEq("abc".startsWith("abc"), true); -assertEq("abcd".startsWith("abc"), true); -assertEq("abc".startsWith("a"), true); -assertEq("abc".startsWith("abcd"), false); -assertEq("abc".startsWith("bcde"), false); -assertEq("abc".startsWith("b"), false); -assertEq("abc".startsWith("abc", 0), true); -assertEq("abc".startsWith("bc", 0), false); -assertEq("abc".startsWith("bc", 1), true); -assertEq("abc".startsWith("c", 1), false); -assertEq("abc".startsWith("abc", 1), false); -assertEq("abc".startsWith("c", 2), true); -assertEq("abc".startsWith("d", 2), false); -assertEq("abc".startsWith("dcd", 2), false); -assertEq("abc".startsWith("a", 42), false); -assertEq("abc".startsWith("a", Infinity), false); -assertEq("abc".startsWith("a", NaN), true); -assertEq("abc".startsWith("b", NaN), false); -assertEq("abc".startsWith("ab", -43), true); -assertEq("abc".startsWith("ab", -Infinity), true); -assertEq("abc".startsWith("bc", -42), false); -assertEq("abc".startsWith("bc", -Infinity), false); -var myobj = {toString : (function () "abc"), startsWith : String.prototype.startsWith}; -assertEq(myobj.startsWith("abc"), true); -assertEq(myobj.startsWith("bc"), false); -var gotStr = false, gotPos = false; -myobj = {toString : (function () { - assertEq(gotPos, false); - gotStr = true; - return "xyz"; -}), -startsWith : String.prototype.startsWith}; -var idx = {valueOf : (function () { - assertEq(gotStr, true); - gotPos = true; - return 42; -})}; -myobj.startsWith("elephant", idx); -assertEq(gotPos, true); +/* +* Copyright (c) 2013 Mathias Bynens. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +function assertThrows(fun, errorType) { + try { + fun(); + assertEq(true, false, "Expected error, but none was thrown"); + } catch (e) { + assertEq(e instanceof errorType, true, "Wrong error type thrown"); + } +} + +Object.prototype[1] = 2; // try to break `arguments[1]` + +assertEq(String.prototype.startsWith.length, 1); +assertEq(String.prototype.propertyIsEnumerable('startsWith'), false); + +assertEq('undefined'.startsWith(), true); +assertEq('undefined'.startsWith(undefined), true); +assertEq('undefined'.startsWith(null), false); +assertEq('null'.startsWith(), false); +assertEq('null'.startsWith(undefined), false); +assertEq('null'.startsWith(null), true); + +assertEq('abc'.startsWith(), false); +assertEq('abc'.startsWith(''), true); +assertEq('abc'.startsWith('\0'), false); +assertEq('abc'.startsWith('a'), true); +assertEq('abc'.startsWith('b'), false); +assertEq('abc'.startsWith('ab'), true); +assertEq('abc'.startsWith('bc'), false); +assertEq('abc'.startsWith('abc'), true); +assertEq('abc'.startsWith('bcd'), false); +assertEq('abc'.startsWith('abcd'), false); +assertEq('abc'.startsWith('bcde'), false); + +assertEq('abc'.startsWith('', NaN), true); +assertEq('abc'.startsWith('\0', NaN), false); +assertEq('abc'.startsWith('a', NaN), true); +assertEq('abc'.startsWith('b', NaN), false); +assertEq('abc'.startsWith('ab', NaN), true); +assertEq('abc'.startsWith('bc', NaN), false); +assertEq('abc'.startsWith('abc', NaN), true); +assertEq('abc'.startsWith('bcd', NaN), false); +assertEq('abc'.startsWith('abcd', NaN), false); +assertEq('abc'.startsWith('bcde', NaN), false); + +assertEq('abc'.startsWith('', false), true); +assertEq('abc'.startsWith('\0', false), false); +assertEq('abc'.startsWith('a', false), true); +assertEq('abc'.startsWith('b', false), false); +assertEq('abc'.startsWith('ab', false), true); +assertEq('abc'.startsWith('bc', false), false); +assertEq('abc'.startsWith('abc', false), true); +assertEq('abc'.startsWith('bcd', false), false); +assertEq('abc'.startsWith('abcd', false), false); +assertEq('abc'.startsWith('bcde', false), false); + +assertEq('abc'.startsWith('', undefined), true); +assertEq('abc'.startsWith('\0', undefined), false); +assertEq('abc'.startsWith('a', undefined), true); +assertEq('abc'.startsWith('b', undefined), false); +assertEq('abc'.startsWith('ab', undefined), true); +assertEq('abc'.startsWith('bc', undefined), false); +assertEq('abc'.startsWith('abc', undefined), true); +assertEq('abc'.startsWith('bcd', undefined), false); +assertEq('abc'.startsWith('abcd', undefined), false); +assertEq('abc'.startsWith('bcde', undefined), false); + +assertEq('abc'.startsWith('', null), true); +assertEq('abc'.startsWith('\0', null), false); +assertEq('abc'.startsWith('a', null), true); +assertEq('abc'.startsWith('b', null), false); +assertEq('abc'.startsWith('ab', null), true); +assertEq('abc'.startsWith('bc', null), false); +assertEq('abc'.startsWith('abc', null), true); +assertEq('abc'.startsWith('bcd', null), false); +assertEq('abc'.startsWith('abcd', null), false); +assertEq('abc'.startsWith('bcde', null), false); + +assertEq('abc'.startsWith('', -Infinity), true); +assertEq('abc'.startsWith('\0', -Infinity), false); +assertEq('abc'.startsWith('a', -Infinity), true); +assertEq('abc'.startsWith('b', -Infinity), false); +assertEq('abc'.startsWith('ab', -Infinity), true); +assertEq('abc'.startsWith('bc', -Infinity), false); +assertEq('abc'.startsWith('abc', -Infinity), true); +assertEq('abc'.startsWith('bcd', -Infinity), false); +assertEq('abc'.startsWith('abcd', -Infinity), false); +assertEq('abc'.startsWith('bcde', -Infinity), false); + +assertEq('abc'.startsWith('', -1), true); +assertEq('abc'.startsWith('\0', -1), false); +assertEq('abc'.startsWith('a', -1), true); +assertEq('abc'.startsWith('b', -1), false); +assertEq('abc'.startsWith('ab', -1), true); +assertEq('abc'.startsWith('bc', -1), false); +assertEq('abc'.startsWith('abc', -1), true); +assertEq('abc'.startsWith('bcd', -1), false); +assertEq('abc'.startsWith('abcd', -1), false); +assertEq('abc'.startsWith('bcde', -1), false); + +assertEq('abc'.startsWith('', -0), true); +assertEq('abc'.startsWith('\0', -0), false); +assertEq('abc'.startsWith('a', -0), true); +assertEq('abc'.startsWith('b', -0), false); +assertEq('abc'.startsWith('ab', -0), true); +assertEq('abc'.startsWith('bc', -0), false); +assertEq('abc'.startsWith('abc', -0), true); +assertEq('abc'.startsWith('bcd', -0), false); +assertEq('abc'.startsWith('abcd', -0), false); +assertEq('abc'.startsWith('bcde', -0), false); + +assertEq('abc'.startsWith('', +0), true); +assertEq('abc'.startsWith('\0', +0), false); +assertEq('abc'.startsWith('a', +0), true); +assertEq('abc'.startsWith('b', +0), false); +assertEq('abc'.startsWith('ab', +0), true); +assertEq('abc'.startsWith('bc', +0), false); +assertEq('abc'.startsWith('abc', +0), true); +assertEq('abc'.startsWith('bcd', +0), false); +assertEq('abc'.startsWith('abcd', +0), false); +assertEq('abc'.startsWith('bcde', +0), false); + +assertEq('abc'.startsWith('', 1), true); +assertEq('abc'.startsWith('\0', 1), false); +assertEq('abc'.startsWith('a', 1), false); +assertEq('abc'.startsWith('b', 1), true); +assertEq('abc'.startsWith('ab', 1), false); +assertEq('abc'.startsWith('bc', 1), true); +assertEq('abc'.startsWith('abc', 1), false); +assertEq('abc'.startsWith('bcd', 1), false); +assertEq('abc'.startsWith('abcd', 1), false); +assertEq('abc'.startsWith('bcde', 1), false); + +assertEq('abc'.startsWith('', +Infinity), true); +assertEq('abc'.startsWith('\0', +Infinity), false); +assertEq('abc'.startsWith('a', +Infinity), false); +assertEq('abc'.startsWith('b', +Infinity), false); +assertEq('abc'.startsWith('ab', +Infinity), false); +assertEq('abc'.startsWith('bc', +Infinity), false); +assertEq('abc'.startsWith('abc', +Infinity), false); +assertEq('abc'.startsWith('bcd', +Infinity), false); +assertEq('abc'.startsWith('abcd', +Infinity), false); +assertEq('abc'.startsWith('bcde', +Infinity), false); + +assertEq('abc'.startsWith('', true), true); +assertEq('abc'.startsWith('\0', true), false); +assertEq('abc'.startsWith('a', true), false); +assertEq('abc'.startsWith('b', true), true); +assertEq('abc'.startsWith('ab', true), false); +assertEq('abc'.startsWith('bc', true), true); +assertEq('abc'.startsWith('abc', true), false); +assertEq('abc'.startsWith('bcd', true), false); +assertEq('abc'.startsWith('abcd', true), false); +assertEq('abc'.startsWith('bcde', true), false); + +assertEq('abc'.startsWith('', 'x'), true); +assertEq('abc'.startsWith('\0', 'x'), false); +assertEq('abc'.startsWith('a', 'x'), true); +assertEq('abc'.startsWith('b', 'x'), false); +assertEq('abc'.startsWith('ab', 'x'), true); +assertEq('abc'.startsWith('bc', 'x'), false); +assertEq('abc'.startsWith('abc', 'x'), true); +assertEq('abc'.startsWith('bcd', 'x'), false); +assertEq('abc'.startsWith('abcd', 'x'), false); +assertEq('abc'.startsWith('bcde', 'x'), false); + +assertEq('[a-z]+(bar)?'.startsWith('[a-z]+'), true); +assertThrows(function() { '[a-z]+(bar)?'.startsWith(/[a-z]+/); }, TypeError); +assertEq('[a-z]+(bar)?'.startsWith('(bar)?', 6), true); +assertThrows(function() { '[a-z]+(bar)?'.startsWith(/(bar)?/); }, TypeError); +assertThrows(function() { '[a-z]+/(bar)?/'.startsWith(/(bar)?/); }, TypeError); +var global = newGlobal(); +global.eval('this.re = /(bar)?/'); +assertThrows(function() { '[a-z]+/(bar)?/'.startsWith(global.re); }, TypeError); + +// http://mathiasbynens.be/notes/javascript-unicode#poo-test +var string = 'I\xF1t\xEBrn\xE2ti\xF4n\xE0liz\xE6ti\xF8n\u2603\uD83D\uDCA9'; +assertEq(string.startsWith(''), true); +assertEq(string.startsWith('\xF1t\xEBr'), false); +assertEq(string.startsWith('\xF1t\xEBr', 1), true); +assertEq(string.startsWith('\xE0liz\xE6'), false); +assertEq(string.startsWith('\xE0liz\xE6', 11), true); +assertEq(string.startsWith('\xF8n\u2603\uD83D\uDCA9'), false); +assertEq(string.startsWith('\xF8n\u2603\uD83D\uDCA9', 18), true); +assertEq(string.startsWith('\u2603'), false); +assertEq(string.startsWith('\u2603', 20), true); +assertEq(string.startsWith('\uD83D\uDCA9'), false); +assertEq(string.startsWith('\uD83D\uDCA9', 21), true); + +assertThrows(function() { String.prototype.startsWith.call(undefined); }, TypeError); +assertThrows(function() { String.prototype.startsWith.call(undefined, 'b'); }, TypeError); +assertThrows(function() { String.prototype.startsWith.call(undefined, 'b', 4); }, TypeError); +assertThrows(function() { String.prototype.startsWith.call(null); }, TypeError); +assertThrows(function() { String.prototype.startsWith.call(null, 'b'); }, TypeError); +assertThrows(function() { String.prototype.startsWith.call(null, 'b', 4); }, TypeError); +assertEq(String.prototype.startsWith.call(42, '2'), false); +assertEq(String.prototype.startsWith.call(42, '4'), true); +assertEq(String.prototype.startsWith.call(42, 'b', 4), false); +assertEq(String.prototype.startsWith.call(42, '2', 1), true); +assertEq(String.prototype.startsWith.call(42, '2', 4), false); +assertEq(String.prototype.startsWith.call({ 'toString': function() { return 'abc'; } }, 'b', 0), false); +assertEq(String.prototype.startsWith.call({ 'toString': function() { return 'abc'; } }, 'b', 1), true); +assertEq(String.prototype.startsWith.call({ 'toString': function() { return 'abc'; } }, 'b', 2), false); +assertThrows(function() { String.prototype.startsWith.call({ 'toString': function() { throw RangeError(); } }, /./); }, RangeError); +assertThrows(function() { String.prototype.startsWith.call({ 'toString': function() { return 'abc'; } }, /./); }, TypeError); + +assertThrows(function() { String.prototype.startsWith.apply(undefined); }, TypeError); +assertThrows(function() { String.prototype.startsWith.apply(undefined, ['b']); }, TypeError); +assertThrows(function() { String.prototype.startsWith.apply(undefined, ['b', 4]); }, TypeError); +assertThrows(function() { String.prototype.startsWith.apply(null); }, TypeError); +assertThrows(function() { String.prototype.startsWith.apply(null, ['b']); }, TypeError); +assertThrows(function() { String.prototype.startsWith.apply(null, ['b', 4]); }, TypeError); +assertEq(String.prototype.startsWith.apply(42, ['2']), false); +assertEq(String.prototype.startsWith.apply(42, ['4']), true); +assertEq(String.prototype.startsWith.apply(42, ['b', 4]), false); +assertEq(String.prototype.startsWith.apply(42, ['2', 1]), true); +assertEq(String.prototype.startsWith.apply(42, ['2', 4]), false); +assertEq(String.prototype.startsWith.apply({ 'toString': function() { return 'abc'; } }, ['b', 0]), false); +assertEq(String.prototype.startsWith.apply({ 'toString': function() { return 'abc'; } }, ['b', 1]), true); +assertEq(String.prototype.startsWith.apply({ 'toString': function() { return 'abc'; } }, ['b', 2]), false); +assertThrows(function() { String.prototype.startsWith.apply({ 'toString': function() { throw RangeError(); } }, [/./]); }, RangeError); +assertThrows(function() { String.prototype.startsWith.apply({ 'toString': function() { return 'abc'; } }, [/./]); }, TypeError);
--- a/js/src/jit-test/tests/parallel/Array-reducePar-bail.js +++ b/js/src/jit-test/tests/parallel/Array-reducePar-bail.js @@ -10,17 +10,18 @@ function testReduce() { var aCounter = 0; function sum(a, b) { var r = a + b; if (r == 234) // occurs once per slice aCounter++; return r; } - var array = build(4096, function() { return 1; }); + // We use a big array, to make sure that the test runs with 64 slices. + var array = build(8 * 4096, function() { return 1; }); var seqResult = array.reduce(sum); var seqCounter = aCounter; aCounter = 0; var parResult = array.reducePar(sum); var parCounter = aCounter; assertEq(true, parCounter >= seqCounter);
--- a/js/src/jit-test/tests/parallel/bailout-executed.js +++ b/js/src/jit-test/tests/parallel/bailout-executed.js @@ -1,14 +1,14 @@ load(libdir + "parallelarray-helpers.js"); function makeObject(e, i, c) { var v = {element: e, index: i, collection: c}; - if (e == 512) // note: happens once + if (e == 0) // note: happens once delete v.index; return v; } function test() { var array = range(0, 768); var array1 = array.map(makeObject);
--- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -108,18 +108,18 @@ BaselineCompiler::compile() return Method_Error; Linker linker(masm); JitCode *code = linker.newCode<CanGC>(cx, JSC::BASELINE_CODE); if (!code) return Method_Error; JSObject *templateScope = nullptr; - if (script->function()) { - RootedFunction fun(cx, script->function()); + if (script->functionNonDelazifying()) { + RootedFunction fun(cx, script->functionNonDelazifying()); if (fun->isHeavyweight()) { templateScope = CallObject::createTemplateObject(cx, script, gc::TenuredHeap); if (!templateScope) return Method_Error; if (fun->isNamedLambda()) { RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap)); if (!declEnvObject)
--- a/js/src/jit/BaselineFrame.h +++ b/js/src/jit/BaselineFrame.h @@ -177,17 +177,17 @@ class BaselineFrame } unsigned numActualArgs() const { return *(size_t *)(reinterpret_cast<const uint8_t *>(this) + BaselineFrame::Size() + offsetOfNumActualArgs()); } unsigned numFormalArgs() const { - return script()->function()->nargs(); + return script()->functionNonDelazifying()->nargs(); } Value &thisValue() const { return *(Value *)(reinterpret_cast<const uint8_t *>(this) + BaselineFrame::Size() + offsetOfThis()); } Value *argv() const { return (Value *)(reinterpret_cast<const uint8_t *>(this) +
--- a/js/src/jit/BaselineFrameInfo.h +++ b/js/src/jit/BaselineFrameInfo.h @@ -177,17 +177,17 @@ class FrameInfo { } bool init(TempAllocator &alloc); uint32_t nlocals() const { return script->nfixed(); } uint32_t nargs() const { - return script->function()->nargs(); + return script->functionNonDelazifying()->nargs(); } private: inline StackValue *rawPush() { StackValue *val = &stack[spIndex++]; val->reset(); return val; }
--- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -215,16 +215,18 @@ jit::EnterBaselineAtBranch(JSContext *cx MethodStatus jit::BaselineCompile(JSContext *cx, HandleScript script) { JS_ASSERT(!script->hasBaselineScript()); JS_ASSERT(script->canBaselineCompile()); LifoAlloc alloc(BASELINE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); + script->ensureNonLazyCanonicalFunction(cx); + TempAllocator *temp = alloc.new_<TempAllocator>(&alloc); if (!temp) return Method_Error; IonContext ictx(cx, temp); BaselineCompiler compiler(cx, *temp, script); if (!compiler.init())
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3396,17 +3396,18 @@ bool CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir) { Register obj = ToRegister(lir->output()); JSObject *templateObj = lir->mir()->templateObj(); CompileInfo &info = lir->mir()->block()->info(); // If we have a template object, we can inline call object creation. OutOfLineCode *ool = oolCallVM(NewDeclEnvObjectInfo, lir, - (ArgList(), ImmGCPtr(info.fun()), Imm32(gc::DefaultHeap)), + (ArgList(), ImmGCPtr(info.funMaybeLazy()), + Imm32(gc::DefaultHeap)), StoreRegisterTo(obj)); if (!ool) return false; masm.newGCThing(obj, templateObj, ool->entry(), gc::DefaultHeap); masm.initGCThing(obj, templateObj); masm.bind(ool->rejoin()); return true;
--- a/js/src/jit/CompileInfo.h +++ b/js/src/jit/CompileInfo.h @@ -46,20 +46,22 @@ class CompileInfo public: CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing, ExecutionMode executionMode, bool scriptNeedsArgsObj) : script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing), executionMode_(executionMode), scriptNeedsArgsObj_(scriptNeedsArgsObj) { JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY); - // The function here can flow in from anywhere so look up the canonical function to ensure that - // we do not try to embed a nursery pointer in jit-code. + // The function here can flow in from anywhere so look up the canonical + // function to ensure that we do not try to embed a nursery pointer in + // jit-code. Precisely because it can flow in from anywhere, it's not + // guaranteed to be non-lazy. Hence, don't access its script! if (fun_) { - fun_ = fun_->nonLazyScript()->function(); + fun_ = fun_->nonLazyScript()->functionNonDelazifying(); JS_ASSERT(fun_->isTenured()); } nimplicit_ = StartArgSlot(script) /* scope chain and argument obj */ + (fun ? 1 : 0); /* this */ nargs_ = fun ? fun->nargs() : 0; nlocals_ = script->nfixed(); nstack_ = script->nslots() - script->nfixed(); @@ -75,17 +77,17 @@ class CompileInfo nlocals_ = nlocals; nstack_ = 1; /* For FunctionCompiler::pushPhiInput/popPhiOutput */ nslots_ = nlocals_ + nstack_; } JSScript *script() const { return script_; } - JSFunction *fun() const { + JSFunction *funMaybeLazy() const { return fun_; } bool constructing() const { return constructing_; } jsbytecode *osrPc() { return osrPc_; } @@ -169,17 +171,17 @@ class CompileInfo JS_ASSERT(script()); return 1; } uint32_t argsObjSlot() const { JS_ASSERT(hasArguments()); return 2; } uint32_t thisSlot() const { - JS_ASSERT(fun()); + JS_ASSERT(funMaybeLazy()); JS_ASSERT(nimplicit_ > 0); return nimplicit_ - 1; } uint32_t firstArgSlot() const { return nimplicit_; } uint32_t argSlotUnchecked(uint32_t i) const { // During initialization, some routines need to get at arg @@ -208,26 +210,26 @@ class CompileInfo } uint32_t startArgSlot() const { JS_ASSERT(script()); return StartArgSlot(script()); } uint32_t endArgSlot() const { JS_ASSERT(script()); - return CountArgSlots(script(), fun()); + return CountArgSlots(script(), funMaybeLazy()); } uint32_t totalSlots() const { - JS_ASSERT(script() && fun()); + JS_ASSERT(script() && funMaybeLazy()); return nimplicit() + nargs() + nlocals(); } bool isSlotAliased(uint32_t index) const { - if (fun() && index == thisSlot()) + if (funMaybeLazy() && index == thisSlot()) return false; uint32_t arg = index - firstArgSlot(); if (arg < nargs()) { if (script()->formalIsAliased(arg)) return true; return false; }
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -1617,17 +1617,19 @@ TrackAllProperties(JSContext *cx, JSObje static void TrackPropertiesForSingletonScopes(JSContext *cx, JSScript *script, BaselineFrame *baselineFrame) { // Ensure that all properties of singleton call objects which the script // could access are tracked. These are generally accessed through // ALIASEDVAR operations in baseline and will not be tracked even if they // have been accessed in baseline code. - JSObject *environment = script->function() ? script->function()->environment() : nullptr; + JSObject *environment = script->functionNonDelazifying() + ? script->functionNonDelazifying()->environment() + : nullptr; while (environment && !environment->is<GlobalObject>()) { if (environment->is<CallObject>() && environment->hasSingletonType()) TrackAllProperties(cx, environment); environment = environment->enclosingScope(); } if (baselineFrame) { @@ -1646,16 +1648,20 @@ IonCompile(JSContext *cx, JSScript *scri #if JS_TRACE_LOGGING AutoTraceLog logger(TraceLogging::defaultLogger(), TraceLogging::ION_COMPILE_START, TraceLogging::ION_COMPILE_STOP, script); #endif JS_ASSERT(optimizationLevel > Optimization_DontCompile); + // Make sure the script's canonical function isn't lazy. We can't de-lazify + // it in a worker thread. + script->ensureNonLazyCanonicalFunction(cx); + TrackPropertiesForSingletonScopes(cx, script, baselineFrame); LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); if (!alloc) return AbortReason_Alloc; ScopedJSDeletePtr<LifoAlloc> autoDelete(alloc); @@ -1672,18 +1678,19 @@ IonCompile(JSContext *cx, JSScript *scri if (!cx->compartment()->jitCompartment()->ensureIonStubsExist(cx)) return AbortReason_Alloc; MIRGraph *graph = alloc->new_<MIRGraph>(temp); if (!graph) return AbortReason_Alloc; - CompileInfo *info = alloc->new_<CompileInfo>(script, script->function(), osrPc, constructing, - executionMode, script->needsArgsObj()); + CompileInfo *info = alloc->new_<CompileInfo>(script, script->functionNonDelazifying(), osrPc, + constructing, executionMode, + script->needsArgsObj()); if (!info) return AbortReason_Alloc; BaselineInspector *inspector = alloc->new_<BaselineInspector>(script); if (!inspector) return AbortReason_Alloc; BaselineFrameInspector *baselineFrameInspector = nullptr; @@ -2213,17 +2220,17 @@ jit::CanEnterUsingFastInvoke(JSContext * JS_ASSERT(jit::IsIonEnabled(cx)); // Skip if the code is expected to result in a bailout. if (!script->hasIonScript() || script->ionScript()->bailoutExpected()) return Method_Skipped; // Don't handle arguments underflow, to make this work we would have to pad // missing arguments with |undefined|. - if (numActualArgs < script->function()->nargs()) + if (numActualArgs < script->functionNonDelazifying()->nargs()) return Method_Skipped; if (!cx->compartment()->ensureJitCompartmentExists(cx)) return Method_Error; // This can GC, so afterward, script->ion is not guaranteed to be valid. if (!cx->runtime()->jitRuntime()->enterIon()) return Method_Error; @@ -2274,17 +2281,17 @@ EnterIon(JSContext *cx, EnterJitData &da bool jit::SetEnterJitData(JSContext *cx, EnterJitData &data, RunState &state, AutoValueVector &vals) { data.osrFrame = nullptr; if (state.isInvoke()) { CallArgs &args = state.asInvoke()->args(); - unsigned numFormals = state.script()->function()->nargs(); + unsigned numFormals = state.script()->functionNonDelazifying()->nargs(); data.constructing = state.asInvoke()->constructing(); data.numActualArgs = args.length(); data.maxArgc = Max(args.length(), numFormals) + 1; data.scopeChain = nullptr; data.calleeToken = CalleeToToken(&args.callee().as<JSFunction>()); if (data.numActualArgs >= numFormals) { data.maxArgv = args.base() + 1;
--- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -211,17 +211,17 @@ IsPhiObservable(MPhi *phi, Observability !iter->consumer()->toDefinition()->isPhi()) return true; } break; } uint32_t slot = phi->slot(); CompileInfo &info = phi->block()->info(); - JSFunction *fun = info.fun(); + JSFunction *fun = info.funMaybeLazy(); // If the Phi is of the |this| value, it must always be observable. if (fun && slot == info.thisSlot()) return true; // If the function may need an arguments object, then make sure to preserve // the scope chain, because it may be needed to construct the arguments // object during bailout. @@ -2138,21 +2138,20 @@ jit::AnalyzeNewScriptProperties(JSContex Vector<types::TypeNewScript::Initializer> *initializerList) { JS_ASSERT(cx->compartment()->activeAnalysis); // When invoking 'new' on the specified script, try to find some properties // which will definitely be added to the created object before it has a // chance to escape and be accessed elsewhere. - if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx)) + RootedScript script(cx, fun->getOrCreateScript(cx)); + if (!script) return false; - RootedScript script(cx, fun->nonLazyScript()); - if (!script->compileAndGo() || !script->canBaselineCompile()) return true; Vector<PropertyName *> accessedProperties(cx); LifoAlloc alloc(types::TypeZone::TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); TempAllocator temp(&alloc);
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -70,17 +70,17 @@ jit::NewBaselineFrameInspector(TempAlloc inspector->thisType = types::GetValueType(frame->thisValue()); if (frame->scopeChain()->hasSingletonType()) inspector->singletonScopeChain = frame->scopeChain(); JSScript *script = frame->script(); - if (script->function()) { + if (script->functionNonDelazifying()) { if (!inspector->argTypes.reserve(frame->numFormalArgs())) return nullptr; for (size_t i = 0; i < frame->numFormalArgs(); i++) { if (script->formalIsAliased(i)) inspector->argTypes.infallibleAppend(types::Type::UndefinedType()); else if (!script->argsObjAliasesFormals()) inspector->argTypes.infallibleAppend(types::GetValueType(frame->unaliasedFormal(i))); else if (frame->hasArgsObj()) @@ -344,20 +344,20 @@ IonBuilder::canInlineTarget(JSFunction * return InliningDecision_DontInline; if (!target->isInterpreted()) return DontInline(nullptr, "Non-interpreted target"); // Allow constructing lazy scripts when performing the definite properties // analysis, as baseline has not been used to warm the caller up yet. if (target->isInterpreted() && info().executionMode() == DefinitePropertiesAnalysis) { - if (!target->getOrCreateScript(analysisContext)) + RootedScript script(analysisContext, target->getOrCreateScript(analysisContext)); + if (!script) return InliningDecision_Error; - RootedScript script(analysisContext, target->nonLazyScript()); if (!script->hasBaselineScript() && script->canBaselineCompile()) { MethodStatus status = BaselineCompile(analysisContext, script); if (status == Method_Error) return InliningDecision_Error; if (status != Method_Compiled) return InliningDecision_DontInline; } } @@ -682,17 +682,17 @@ IonBuilder::build() // It's safe to start emitting actual IR, so now build the scope chain. if (!initScopeChain()) return false; if (info().needsArgsObj() && !initArgumentsObject()) return false; // Prevent |this| from being DCE'd: necessary for constructors. - if (info().fun()) + if (info().funMaybeLazy()) current->getSlot(info().thisSlot())->setGuard(); // The type analysis phase attempts to insert unbox operations near // definitions of values. It also attempts to replace uses in resume points // with the narrower, unboxed variants. However, we must prevent this // replacement from happening on values in the entry snapshot. Otherwise we // could get this: // @@ -914,29 +914,29 @@ IonBuilder::rewriteParameter(uint32_t sl // Apply Type Inference information to parameters early on, unboxing them if // they have a definitive type. The actual guards will be emitted by the code // generator, explicitly, as part of the function prologue. void IonBuilder::rewriteParameters() { JS_ASSERT(info().scopeChainSlot() == 0); - if (!info().fun()) + if (!info().funMaybeLazy()) return; for (uint32_t i = info().startArgSlot(); i < info().endArgSlot(); i++) { MDefinition *param = current->getSlot(i); rewriteParameter(i, param, param->toParameter()->index()); } } bool IonBuilder::initParameters() { - if (!info().fun()) + if (!info().funMaybeLazy()) return true; // If we are doing OSR on a frame which initially executed in the // interpreter and didn't accumulate type information, try to use that OSR // frame to determine possible initial types for 'this' and parameters. // For unknownProperties() tests under addType. lock(); @@ -985,17 +985,17 @@ IonBuilder::initScopeChain(MDefinition * // will try to access the scope. For other scripts, the scope instructions // will be held live by resume points and code will still be generated for // them, so just use a constant undefined value. if (!script()->compileAndGo()) return abort("non-CNG global scripts are not supported"); lock(); - if (JSFunction *fun = info().fun()) { + if (JSFunction *fun = info().funMaybeLazy()) { if (!callee) { MCallee *calleeIns = MCallee::New(alloc()); current->add(calleeIns); callee = calleeIns; } scope = MFunctionEnvironment::New(alloc(), callee); current->add(scope); @@ -3825,17 +3825,17 @@ class AutoAccumulateReturns ~AutoAccumulateReturns() { graph_.setReturnAccumulator(prev_); } }; bool IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target) { - JS_ASSERT(target->isInterpreted()); + JS_ASSERT(target->hasScript()); JS_ASSERT(IsIonInlinablePC(pc)); callInfo.setImplicitlyUsedUnchecked(); // Ensure sufficient space in the slots: needed for inlining from FUNAPPLY. uint32_t depth = current->stackDepth() + callInfo.numFormals(); if (depth > current->nslots()) { if (!current->increaseSlots(depth - current->nslots())) @@ -5293,17 +5293,17 @@ IonBuilder::jsop_eval(uint32_t argc) JSFunction *singleton = getSingleCallTarget(calleeTypes); if (!singleton) return abort("No singleton callee for eval()"); if (script()->global().valueIsEval(ObjectValue(*singleton))) { if (argc != 1) return abort("Direct eval with more than one argument"); - if (!info().fun()) + if (!info().funMaybeLazy()) return abort("Direct eval in global code"); // The 'this' value for the outer and eval scripts must be the // same. This is not guaranteed if a primitive string/number/etc. // is passed through to the eval invoke as the primitive may be // boxed into different objects if accessed via 'this'. JSValueType type = thisTypes->getKnownTypeTag(); if (type != JSVAL_TYPE_OBJECT && type != JSVAL_TYPE_NULL && type != JSVAL_TYPE_UNDEFINED) @@ -5715,17 +5715,17 @@ IonBuilder::newOsrPreheader(MBasicBlock if (needsArgsObj) argsObj = MOsrArgumentsObject::New(alloc(), entry); else argsObj = MConstant::New(alloc(), UndefinedValue()); osrBlock->add(argsObj); osrBlock->initSlot(info().argsObjSlot(), argsObj); } - if (info().fun()) { + if (info().funMaybeLazy()) { // Initialize |this| parameter. MParameter *thisv = MParameter::New(alloc(), MParameter::THIS_SLOT, nullptr); osrBlock->add(thisv); osrBlock->initSlot(info().thisSlot(), thisv); // Initialize arguments. for (uint32_t i = 0; i < info().nargs(); i++) { uint32_t slot = needsArgsObj ? info().argSlotUnchecked(i) : info().argSlot(i); @@ -5820,17 +5820,17 @@ IonBuilder::newOsrPreheader(MBasicBlock // Finish the osrBlock. osrBlock->end(MGoto::New(alloc(), preheader)); preheader->addPredecessor(alloc(), osrBlock); graph().setOsrBlock(osrBlock); // Wrap |this| with a guaranteed use, to prevent instruction elimination. // Prevent |this| from being DCE'd: necessary for constructors. - if (info().fun()) + if (info().funMaybeLazy()) preheader->getSlot(info().thisSlot())->setGuard(); return preheader; } MBasicBlock * IonBuilder::newPendingLoopHeader(MBasicBlock *predecessor, jsbytecode *pc, bool osr) { @@ -5860,17 +5860,17 @@ IonBuilder::newPendingLoopHeader(MBasicB continue; MPhi *phi = block->getSlot(i)->toPhi(); // Get the type from the baseline frame. types::Type existingType = types::Type::UndefinedType(); uint32_t arg = i - info().firstArgSlot(); uint32_t var = i - info().firstLocalSlot(); - if (info().fun() && i == info().thisSlot()) + if (info().funMaybeLazy() && i == info().thisSlot()) existingType = baselineFrame_->thisType; else if (arg < info().nargs()) existingType = baselineFrame_->argTypes[arg]; else existingType = baselineFrame_->varTypes[var]; // Extract typeset from value. types::TemporaryTypeSet *typeSet = @@ -9235,20 +9235,20 @@ IonBuilder::jsop_deffun(uint32_t index) current->add(deffun); return resumeAfter(deffun); } bool IonBuilder::jsop_this() { - if (!info().fun()) + if (!info().funMaybeLazy()) return abort("JSOP_THIS outside of a JSFunction."); - if (script()->strict() || info().fun()->isSelfHostedBuiltin()) { + if (script()->strict() || info().funMaybeLazy()->isSelfHostedBuiltin()) { // No need to wrap primitive |this| in strict mode or self-hosted code. current->pushSlot(info().thisSlot()); return true; } if (thisTypes->getKnownTypeTag() == JSVAL_TYPE_OBJECT || (thisTypes->empty() && baselineFrame_ && baselineFrame_->thisType.isSomeObject())) { @@ -9394,33 +9394,34 @@ IonBuilder::walkScopeChain(unsigned hops bool IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall) { JSScript *outerScript = ScopeCoordinateFunctionScript(script(), pc); if (!outerScript || !outerScript->treatAsRunOnce()) return false; - types::TypeObjectKey *funType = types::TypeObjectKey::get(outerScript->function()); + types::TypeObjectKey *funType = + types::TypeObjectKey::get(outerScript->functionNonDelazifying()); if (funType->hasFlags(constraints(), types::OBJECT_FLAG_RUNONCE_INVALIDATED)) return false; // The script this aliased var operation is accessing will run only once, // so there will be only one call object and the aliased var access can be // compiled in the same manner as a global access. We still need to find // the call object though. // Look for the call object on the current script's function's scope chain. // If the current script is inner to the outer script and the function has // singleton type then it should show up here. MDefinition *scope = current->getSlot(info().scopeChainSlot()); scope->setImplicitlyUsedUnchecked(); - JSObject *environment = script()->function()->environment(); + JSObject *environment = script()->functionNonDelazifying()->environment(); while (environment && !environment->is<GlobalObject>()) { if (environment->is<CallObject>() && !environment->as<CallObject>().isForEval() && environment->as<CallObject>().callee().nonLazyScript() == outerScript) { JS_ASSERT(environment->hasSingletonType()); *pcall = environment; return true;
--- a/js/src/jit/IonFrames.cpp +++ b/js/src/jit/IonFrames.cpp @@ -614,17 +614,17 @@ HandleException(ResumeFromException *rfe bool popSPSFrame = cx->runtime()->spsProfiler.enabled(); if (invalidated) popSPSFrame = ionScript->hasSPSInstrumentation(); // When profiling, each frame popped needs a notification that // the function has exited, so invoke the probe that a function // is exiting. JSScript *script = frames.script(); - probes::ExitScript(cx, script, script->function(), popSPSFrame); + probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); if (!frames.more()) break; ++frames; } if (invalidated) ionScript->decref(cx->runtime()->defaultFreeOp()); @@ -633,17 +633,17 @@ HandleException(ResumeFromException *rfe bool calledDebugEpilogue = false; HandleExceptionBaseline(cx, iter, rfe, &calledDebugEpilogue); if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME) return; // Unwind profiler pseudo-stack JSScript *script = iter.script(); - probes::ExitScript(cx, script, script->function(), + probes::ExitScript(cx, script, script->functionNonDelazifying(), iter.baselineFrame()->hasPushedSPSFrame()); // After this point, any pushed SPS frame would have been popped if it needed // to be. Unset the flag here so that if we call DebugEpilogue below, // it doesn't try to pop the SPS frame again. iter.baselineFrame()->unsetPushedSPSFrame(); if (cx->compartment()->debugMode() && !calledDebugEpilogue) { // If DebugEpilogue returns |true|, we have to perform a forced
--- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -511,23 +511,23 @@ JSObject * NewStringObject(JSContext *cx, HandleString str) { return StringObject::create(cx, str); } bool SPSEnter(JSContext *cx, HandleScript script) { - return cx->runtime()->spsProfiler.enter(cx, script, script->function()); + return cx->runtime()->spsProfiler.enter(cx, script, script->functionNonDelazifying()); } bool SPSExit(JSContext *cx, HandleScript script) { - cx->runtime()->spsProfiler.exit(cx, script, script->function()); + cx->runtime()->spsProfiler.exit(cx, script, script->functionNonDelazifying()); return true; } bool OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, bool *out) { RootedId id(cx); if (!ValueToId<CanGC>(cx, key, &id))
--- a/js/src/jit/shared/BaselineCompiler-shared.h +++ b/js/src/jit/shared/BaselineCompiler-shared.h @@ -91,17 +91,19 @@ class BaselineCompilerShared JS_ASSERT(!icEntries_.empty()); ICLoadLabel loadLabel; loadLabel.label = label; loadLabel.icEntry = icEntries_.length() - 1; return icLoadLabels_.append(loadLabel); } JSFunction *function() const { - return script->function(); + // Not delazifying here is ok as the function is guaranteed to have + // been delazified before compilation started. + return script->functionNonDelazifying(); } PCMappingSlotInfo getStackTopSlotInfo() { JS_ASSERT(frame.numUnsyncedSlots() <= 2); switch (frame.numUnsyncedSlots()) { case 0: return PCMappingSlotInfo::MakeSlotInfo(); case 1:
--- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -256,17 +256,17 @@ CodeGeneratorShared::encode(LSnapshot *s uint32_t startIndex = 0; for (MResumePoint **it = mirOperandIter.begin(), **end = mirOperandIter.end(); it != end; ++it) { MResumePoint *mir = *it; MBasicBlock *block = mir->block(); - JSFunction *fun = block->info().fun(); + JSFunction *fun = block->info().funMaybeLazy(); JSScript *script = block->info().script(); jsbytecode *pc = mir->pc(); uint32_t exprStack = mir->stackDepth() - block->info().ninvoke(); snapshots_.startFrame(fun, script, pc, exprStack); // Ensure that all snapshot which are encoded can safely be used for // bailouts. DebugOnly<jsbytecode *> bailPC = pc;
--- a/js/src/js.msg +++ b/js/src/js.msg @@ -350,17 +350,17 @@ MSG_DEF(JSMSG_NO_REST_NAME, 29 MSG_DEF(JSMSG_ARGUMENTS_AND_REST, 297, 0, JSEXN_SYNTAXERR, "'arguments' object may not be used in conjunction with a rest parameter") MSG_DEF(JSMSG_FUNCTION_ARGUMENTS_AND_REST, 298, 0, JSEXN_ERR, "the 'arguments' property of a function with a rest parameter may not be used") MSG_DEF(JSMSG_REST_WITH_DEFAULT, 299, 0, JSEXN_SYNTAXERR, "rest parameter may not have a default") MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 300, 0, JSEXN_SYNTAXERR, "parameter(s) with default followed by parameter without default") MSG_DEF(JSMSG_YIELD_IN_DEFAULT, 301, 0, JSEXN_SYNTAXERR, "yield in default expression") MSG_DEF(JSMSG_INTRINSIC_NOT_DEFINED, 302, 1, JSEXN_REFERENCEERR, "no intrinsic function {0}") MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA, 303, 2, JSEXN_ERR, "{0} is being assigned a {1}, but already has one") MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG, 304, 0, JSEXN_RANGEERR, "invalid parallel method argument") -MSG_DEF(JSMSG_UNUSED305, 305, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_REGEXP_RUNTIME_ERROR, 305, 0, JSEXN_INTERNALERR, "an error occurred while executing regular expression") MSG_DEF(JSMSG_UNUSED306, 306, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_UNUSED307, 307, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 308, 0, JSEXN_ERR, "no conflict resolution function provided") MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS, 309, 0, JSEXN_ERR, "index in scatter vector out of bounds") MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE, 310, 0, JSEXN_TYPEERR, "proxy can't report a non-configurable own property as non-existent") MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE, 311, 0, JSEXN_TYPEERR, "proxy can't report an existing own property as non-existent on a non-extensible object") MSG_DEF(JSMSG_CANT_REPORT_NEW, 312, 0, JSEXN_TYPEERR, "proxy can't report a new property on a non-extensible object") MSG_DEF(JSMSG_CANT_REPORT_INVALID, 313, 0, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor") @@ -429,8 +429,9 @@ MSG_DEF(JSMSG_MODULE_SPEC_AFTER_FROM, MSG_DEF(JSMSG_MODULES_NOT_IMPLEMENTED, 375, 0, JSEXN_SYNTAXERR, "modules are not implemented yet") MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL, 376, 0, JSEXN_SYNTAXERR, "export declarations may only appear at top level") MSG_DEF(JSMSG_RC_AFTER_EXPORT_SPEC_LIST, 377, 0, JSEXN_SYNTAXERR, "missing '}' after export specifier list") MSG_DEF(JSMSG_NO_EXPORT_NAME, 378, 0, JSEXN_SYNTAXERR, "missing export name") MSG_DEF(JSMSG_DECLARATION_AFTER_EXPORT, 379, 0, JSEXN_SYNTAXERR, "missing declaration after 'export' keyword") MSG_DEF(JSMSG_INVALID_PROTOTYPE, 380, 0, JSEXN_TYPEERR, "prototype field is not an object") MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_TO_UNSIZED, 381, 0, JSEXN_TYPEERR, "cannot create a handle to an unsized type") MSG_DEF(JSMSG_SETPROTOTYPEOF_FAIL, 382, 0, JSEXN_TYPEERR, "[[SetPrototypeOf]] failed") +MSG_DEF(JSMSG_INVALID_ARG_TYPE, 383, 3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
--- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -220,17 +220,18 @@ FollowBranch(JSContext *cx, JSScript *sc /* Common representation of slots throughout analyses and the compiler. */ static inline uint32_t ThisSlot() { return 0; } static inline uint32_t ArgSlot(uint32_t arg) { return 1 + arg; } static inline uint32_t LocalSlot(JSScript *script, uint32_t local) { - return 1 + (script->function() ? script->function()->nargs() : 0) + local; + return 1 + local + + (script->functionNonDelazifying() ? script->functionNonDelazifying()->nargs() : 0); } static inline uint32_t TotalSlots(JSScript *script) { return LocalSlot(script, 0) + script->nfixed(); } static inline uint32_t StackSlot(JSScript *script, uint32_t index) { return TotalSlots(script) + index; }
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3979,26 +3979,26 @@ JS_CloneFunctionObject(JSContext *cx, JS parent = cx->global(); if (!funobj->is<JSFunction>()) { AutoCompartment ac(cx, funobj); ReportIsNotFunction(cx, ObjectValue(*funobj)); return nullptr; } - /* - * If a function was compiled to be lexically nested inside some other - * script, we cannot clone it without breaking the compiler's assumptions. - */ RootedFunction fun(cx, &funobj->as<JSFunction>()); if (fun->isInterpretedLazy()) { AutoCompartment ac(cx, funobj); if (!fun->getOrCreateScript(cx)) return nullptr; } + /* + * If a function was compiled to be lexically nested inside some other + * script, we cannot clone it without breaking the compiler's assumptions. + */ if (fun->isInterpreted() && (fun->nonLazyScript()->enclosingStaticScope() || (fun->nonLazyScript()->compileAndGo() && !parent->is<GlobalObject>()))) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_CLONE_FUNOBJ_SCOPE); return nullptr; } if (fun->isBoundFunction()) { @@ -4686,17 +4686,18 @@ JS_CompileFunction(JSContext *cx, JS::Ha JS_PUBLIC_API(JSString *) JS_DecompileScript(JSContext *cx, JSScript *scriptArg, const char *name, unsigned indent) { JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); AssertHeapIsIdle(cx); CHECK_REQUEST(cx); RootedScript script(cx, scriptArg); - RootedFunction fun(cx, script->function()); + script->ensureNonLazyCanonicalFunction(cx); + RootedFunction fun(cx, script->functionNonDelazifying()); if (fun) return JS_DecompileFunction(cx, fun, indent); bool haveSource = script->scriptSource()->hasSourceData(); if (!haveSource && !JSScript::loadSource(cx, script->scriptSource(), &haveSource)) return nullptr; return haveSource ? script->sourceData(cx) : js_NewStringCopyZ<CanGC>(cx, "[no source]"); }
--- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -38,16 +38,17 @@ using namespace js::gc; using mozilla::DebugOnly; JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options = JS::CompartmentOptions()) : options_(options), zone_(zone), runtime_(zone->runtimeFromMainThread()), principals(nullptr), isSystem(false), + isSelfHosting(false), marked(true), #ifdef DEBUG firedOnNewGlobalObject(false), #endif global_(nullptr), enterCompartmentDepth(0), data(nullptr), objectMetadataCallback(nullptr), @@ -719,17 +720,17 @@ CreateLazyScriptsForCompartment(JSContex AutoObjectVector lazyFunctions(cx); // Find all live lazy scripts in the compartment, and via them all root // lazy functions in the compartment: those which have not been compiled // and which have a source object, indicating that their parent has been // compiled. for (gc::CellIter i(cx->zone(), gc::FINALIZE_LAZY_SCRIPT); !i.done(); i.next()) { LazyScript *lazy = i.get<LazyScript>(); - JSFunction *fun = lazy->function(); + JSFunction *fun = lazy->functionNonDelazifying(); if (fun->compartment() == cx->compartment() && lazy->sourceObject() && !lazy->maybeScript()) { MOZ_ASSERT(fun->isInterpretedLazy()); MOZ_ASSERT(lazy == fun->lazyScriptOrNull()); if (!lazyFunctions.append(fun)) return false; }
--- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -122,16 +122,17 @@ struct JSCompartment private: JS::Zone *zone_; JSRuntime *runtime_; public: JSPrincipals *principals; bool isSystem; + bool isSelfHosting; bool marked; #ifdef DEBUG bool firedOnNewGlobalObject; #endif void mark() { marked = true; }
--- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -450,17 +450,19 @@ js::CloneFunctionAndScript(JSContext *cx } RootedFunction clone(cx, NewFunctionWithProto(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED, NullPtr(), NullPtr(), cloneProto, JSFunction::FinalizeKind, TenuredObject)); if (!clone) return nullptr; - RootedScript srcScript(cx, srcFun->nonLazyScript()); + RootedScript srcScript(cx, srcFun->getOrCreateScript(cx)); + if (!srcScript) + return nullptr; RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, clone, srcScript)); if (!clonedScript) return nullptr; clone->setArgCount(srcFun->nargs()); clone->setFlags(srcFun->flags()); clone->initAtom(srcFun->displayAtom()); clone->initScript(clonedScript); @@ -517,21 +519,40 @@ JSFunction::trace(JSTracer *trc) } if (atom_) MarkString(trc, &atom_, "atom"); if (isInterpreted()) { // Functions can be be marked as interpreted despite having no script // yet at some points when parsing, and can be lazy with no lazy script - // for self hosted code. - if (hasScript() && u.i.s.script_) - MarkScriptUnbarriered(trc, &u.i.s.script_, "script"); - else if (isInterpretedLazy() && u.i.s.lazy_) + // for self-hosted code. + if (hasScript() && u.i.s.script_) { + // Functions can be relazified under the following conditions: + // - their compartment isn't currently executing scripts or being + // debugged + // - they are not in the self-hosting compartment + // - they aren't generators + // - they don't have JIT code attached + // - they don't have child functions + // - they have information for un-lazifying them again later + // This information can either be a LazyScript, or the name of a + // self-hosted function which can be cloned over again. The latter + // is stored in the first extended slot. + if (IS_GC_MARKING_TRACER(trc) && !compartment()->hasBeenEntered() && + !compartment()->debugMode() && !compartment()->isSelfHosting && + u.i.s.script_->isRelazifiable() && (!isSelfHostedBuiltin() || isExtended())) + { + relazify(trc); + } else { + MarkScriptUnbarriered(trc, &u.i.s.script_, "script"); + } + } else if (isInterpretedLazy() && u.i.s.lazy_) { MarkLazyScriptUnbarriered(trc, &u.i.s.lazy_, "lazyScript"); + } if (u.i.env_) MarkObjectUnbarriered(trc, &u.i.env_, "fun_callscope"); } } static void fun_trace(JSTracer *trc, JSObject *obj) { @@ -1121,21 +1142,28 @@ JSFunction::createScriptForLazilyInterpr // THING_ROOT_LAZY_SCRIPT). AutoSuppressGC suppressGC(cx); RootedScript script(cx, lazy->maybeScript()); if (script) { AutoLockForCompilation lock(cx); fun->setUnlazifiedScript(script); + // Remember the lazy script on the compiled script, so it can be + // stored on the function again in case of re-lazification. + // Only functions without inner functions are re-lazified. + if (!lazy->numInnerFunctions()) + script->setLazyScript(lazy); return true; } - if (fun != lazy->function()) { - script = lazy->function()->getOrCreateScript(cx); + if (fun != lazy->functionNonDelazifying()) { + if (!lazy->functionDelazifying(cx)) + return false; + script = lazy->functionNonDelazifying()->nonLazyScript(); if (!script) return false; AutoLockForCompilation lock(cx); fun->setUnlazifiedScript(script); return true; } @@ -1155,27 +1183,28 @@ JSFunction::createScriptForLazilyInterpr if (script) { RootedObject enclosingScope(cx, lazy->enclosingScope()); RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, fun, script)); if (!clonedScript) return false; clonedScript->setSourceObject(lazy->sourceObject()); - fun->initAtom(script->function()->displayAtom()); + fun->initAtom(script->functionNonDelazifying()->displayAtom()); clonedScript->setFunction(fun); { AutoLockForCompilation lock(cx); fun->setUnlazifiedScript(clonedScript); } CallNewScriptHook(cx, clonedScript, fun); - lazy->initScript(clonedScript); + if (!lazy->maybeScript()) + lazy->initScript(clonedScript); return true; } JS_ASSERT(lazy->source()->hasSourceData()); // Parse and compile the script from source. SourceDataCache::AutoSuppressPurge asp(cx); const jschar *chars = lazy->source()->chars(cx, asp); @@ -1185,41 +1214,87 @@ JSFunction::createScriptForLazilyInterpr const jschar *lazyStart = chars + lazy->begin(); size_t lazyLength = lazy->end() - lazy->begin(); if (!frontend::CompileLazyFunction(cx, lazy, lazyStart, lazyLength)) return false; script = fun->nonLazyScript(); + // Remember the compiled script on the lazy script itself, in case + // there are clones of the function still pointing to the lazy script. + if (!lazy->maybeScript()) + lazy->initScript(script); + // Try to insert the newly compiled script into the lazy script cache. if (!lazy->numInnerFunctions()) { // A script's starting column isn't set by the bytecode emitter, so // specify this from the lazy script so that if an identical lazy // script is encountered later a match can be determined. script->setColumn(lazy->column()); LazyScriptCache::Lookup lookup(cx, lazy); cx->runtime()->lazyScriptCache.insert(lookup, script); + + // Remember the lazy script on the compiled script, so it can be + // stored on the function again in case of re-lazification. + // Only functions without inner functions are re-lazified. + script->setLazyScript(lazy); } - - // Remember the compiled script on the lazy script itself, in case - // there are clones of the function still pointing to the lazy script. - lazy->initScript(script); return true; } - /* Lazily cloned self hosted script. */ + /* Lazily cloned self-hosted script. */ + JS_ASSERT(fun->isSelfHostedBuiltin()); RootedAtom funAtom(cx, &fun->getExtendedSlot(0).toString()->asAtom()); if (!funAtom) return false; Rooted<PropertyName *> funName(cx, funAtom->asPropertyName()); return cx->runtime()->cloneSelfHostedFunctionScript(cx, funName, fun); } +void +JSFunction::relazify(JSTracer *trc) +{ + JSScript *script = nonLazyScript(); + JS_ASSERT(script->isRelazifiable()); + JS_ASSERT(!compartment()->hasBeenEntered()); + JS_ASSERT(!compartment()->debugMode()); + + // If the script's canonical function isn't lazy, we have to mark the + // script. Otherwise, the following scenario would leave it unmarked + // and cause it to be swept while a function is still expecting it to be + // valid: + // 1. an incremental GC slice causes the canonical function to relazify + // 2. a clone is used and delazifies the canonical function + // 3. another GC slice causes the clone to relazify + // The result is that no function marks the script, but the canonical + // function expects it to be valid. + if (script->functionNonDelazifying()->hasScript()) + MarkScriptUnbarriered(trc, &u.i.s.script_, "script"); + + flags_ &= ~INTERPRETED; + flags_ |= INTERPRETED_LAZY; + LazyScript *lazy = script->maybeLazyScript(); + u.i.s.lazy_ = lazy; + if (lazy) { + JS_ASSERT(!isSelfHostedBuiltin()); + // If this is the script stored in the lazy script to be cloned + // for un-lazifying other functions, reset it so the script can + // be freed. + if (lazy->maybeScript() == script) + lazy->resetScript(); + MarkLazyScriptUnbarriered(trc, &u.i.s.lazy_, "lazyScript"); + } else { + JS_ASSERT(isSelfHostedBuiltin()); + JS_ASSERT(isExtended()); + JS_ASSERT(getExtendedSlot(0).toString()->isAtom()); + } +} + /* ES5 15.3.4.5.1 and 15.3.4.5.2. */ bool js::CallOrConstructBoundFunction(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedFunction fun(cx, &args.callee().as<JSFunction>()); JS_ASSERT(fun->isBoundFunction());
--- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -274,16 +274,17 @@ class JSFunction : public JSObject } static inline size_t offsetOfNargs() { return offsetof(JSFunction, nargs_); } static inline size_t offsetOfFlags() { return offsetof(JSFunction, flags_); } static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); } static inline size_t offsetOfAtom() { return offsetof(JSFunction, atom_); } static bool createScriptForLazilyInterpretedFunction(JSContext *cx, js::HandleFunction fun); + void relazify(JSTracer *trc); // Function Scripts // // Interpreted functions may either have an explicit JSScript (hasScript()) // or be lazy with sufficient information to construct the JSScript if // necessary (isInterpretedLazy()). // // A lazy function will have a LazyScript if the function came from parsed @@ -303,45 +304,43 @@ class JSFunction : public JSObject JSScript *getOrCreateScript(JSContext *cx) { JS_ASSERT(isInterpreted()); JS_ASSERT(cx); if (isInterpretedLazy()) { JS::RootedFunction self(cx, this); if (!createScriptForLazilyInterpretedFunction(cx, self)) return nullptr; - JS_ASSERT(self->hasScript()); - return self->u.i.s.script_; + return self->nonLazyScript(); } - JS_ASSERT(hasScript()); - return u.i.s.script_; + return nonLazyScript(); } JSScript *existingScript() { JS_ASSERT(isInterpreted()); if (isInterpretedLazy()) { js::LazyScript *lazy = lazyScript(); JSScript *script = lazy->maybeScript(); JS_ASSERT(script); if (shadowZone()->needsBarrier()) js::LazyScript::writeBarrierPre(lazy); flags_ &= ~INTERPRETED_LAZY; flags_ |= INTERPRETED; initScript(script); } - JS_ASSERT(hasScript()); - return u.i.s.script_; + return nonLazyScript(); } JSScript *nonLazyScript() const { js::AutoThreadSafeAccess ts(this); + JS_ASSERT(js::CurrentThreadCanReadCompilationData()); JS_ASSERT(hasScript()); - JS_ASSERT(js::CurrentThreadCanReadCompilationData()); + JS_ASSERT(u.i.s.script_); return u.i.s.script_; }