author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Fri, 11 Dec 2015 11:38:17 +0100 | |
changeset 310313 | 754b4805a65cab4f3aca99899227acc44ba4fb20 |
parent 310218 | 0dd42501bbe65904cf5a4f8b6e37408d60333045 (current diff) |
parent 310312 | 672a8b656e19b41617692bb1bc9b03a17beb4b3a (diff) |
child 310320 | 48aaaf45934f491490d0dca6c48617e097772bf5 |
child 310342 | e056ab8293136c0a904cba539053a5518691a254 |
child 310415 | d74d9d3c2598e72b206941e8653b1fa80b5b55b3 |
push id | 5513 |
push user | raliiev@mozilla.com |
push date | Mon, 25 Jan 2016 13:55:34 +0000 |
treeherder | mozilla-beta@5ee97dd05b5c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 45.0a1 |
first release with | nightly linux32
754b4805a65c
/
45.0a1
/
20151211030207
/
files
nightly linux64
754b4805a65c
/
45.0a1
/
20151211030207
/
files
nightly mac
754b4805a65c
/
45.0a1
/
20151211030207
/
files
nightly win32
754b4805a65c
/
45.0a1
/
20151211030207
/
files
nightly win64
754b4805a65c
/
45.0a1
/
20151211030207
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
45.0a1
/
20151211030207
/
pushlog to previous
nightly linux64
45.0a1
/
20151211030207
/
pushlog to previous
nightly mac
45.0a1
/
20151211030207
/
pushlog to previous
nightly win32
45.0a1
/
20151211030207
/
pushlog to previous
nightly win64
45.0a1
/
20151211030207
/
pushlog to previous
|
--- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -1002,56 +1002,26 @@ pref("dom.downloads.max_retention_days", // // To prevent SD card DoS attacks via downloads we disable background handling. // pref("security.exthelperapp.disable_background_handling", true); // Inactivity time in milliseconds after which we shut down the OS.File worker. pref("osfile.reset_worker_delay", 5000); -// APZC preferences. -#ifdef MOZ_WIDGET_GONK -pref("apz.allow_zooming", true); -#endif - -// Gaia relies heavily on scroll events for now, so lets fire them -// more often than the default value (100). -pref("apz.pan_repaint_interval", 16); - // APZ physics settings, tuned by UX designers +pref("apz.axis_lock.mode", 2); // Use "sticky" axis locking pref("apz.fling_curve_function_x1", "0.41"); pref("apz.fling_curve_function_y1", "0.0"); pref("apz.fling_curve_function_x2", "0.80"); pref("apz.fling_curve_function_y2", "1.0"); pref("apz.fling_curve_threshold_inches_per_ms", "0.01"); pref("apz.fling_friction", "0.0019"); pref("apz.max_velocity_inches_per_ms", "0.07"); -pref("apz.touch_start_tolerance", "0.1"); - -#ifdef MOZ_WIDGET_GONK -pref("apz.touch_move_tolerance", "0.03"); -#endif - -// Tweak default displayport values to reduce the risk of running out of -// memory when zooming in -pref("apz.x_skate_size_multiplier", "1.25"); -pref("apz.y_skate_size_multiplier", "1.5"); -pref("apz.x_stationary_size_multiplier", "1.5"); -pref("apz.y_stationary_size_multiplier", "1.8"); -pref("apz.enlarge_displayport_when_clipped", true); -// Use "sticky" axis locking -pref("apz.axis_lock.mode", 2); - -// Overscroll-related settings pref("apz.overscroll.enabled", true); -pref("apz.overscroll.stretch_factor", "0.35"); -pref("apz.overscroll.spring_stiffness", "0.0018"); -pref("apz.overscroll.spring_friction", "0.015"); -pref("apz.overscroll.stop_distance_threshold", "5.0"); -pref("apz.overscroll.stop_velocity_threshold", "0.01"); // For event-regions based hit-testing pref("layout.event-regions.enabled", true); // This preference allows FirefoxOS apps (and content, I think) to force // the use of software (instead of hardware accelerated) 2D canvases by // creating a context like this: //
--- a/b2g/components/AboutServiceWorkers.jsm +++ b/b2g/components/AboutServiceWorkers.jsm @@ -24,27 +24,22 @@ function debug(aMsg) { function serializeServiceWorkerInfo(aServiceWorkerInfo) { if (!aServiceWorkerInfo) { throw new Error("Invalid service worker information"); } let result = {}; - Object.keys(aServiceWorkerInfo).forEach(property => { - if (typeof aServiceWorkerInfo[property] == "function") { - return; - } - if (property === "principal") { - result.principal = { - origin: aServiceWorkerInfo.principal.originNoSuffix, - originAttributes: aServiceWorkerInfo.principal.originAttributes - }; - return; - } + result.principal = { + origin: aServiceWorkerInfo.principal.originNoSuffix, + originAttributes: aServiceWorkerInfo.principal.originAttributes + }; + + ["scope", "scriptSpec"].forEach(property => { result[property] = aServiceWorkerInfo[property]; }); return result; } this.AboutServiceWorkers = {
--- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -756,17 +756,17 @@ if (this.mBrowser.userTypedClear > 0 || ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) && aLocation.spec != "about:blank") || aFlags && Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) { this.mBrowser.userTypedValue = null; } // If the browser was playing audio, we should remove the playing state. - if (this.mTab.hasAttribute("soundplaying")) { + if (this.mTab.hasAttribute("soundplaying") && this.mBrowser.lastURI != aLocation) { this.mTab.removeAttribute("soundplaying"); this.mTabBrowser._tabAttrModified(this.mTab, ["soundplaying"]); } // If the browser was previously muted, we should restore the muted state. if (this.mTab.hasAttribute("muted")) { this.mTab.linkedBrowser.mute(); }
--- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -319,17 +319,16 @@ support-files = fxa_profile_handler.sjs skip-if = e10s # Bug 863514 - no gesture support. [browser_getshortcutoruri.js] [browser_hide_removing.js] [browser_homeDrop.js] skip-if = buildapp == 'mulet' [browser_identity_UI.js] [browser_insecureLoginForms.js] [browser_keywordBookmarklets.js] -skip-if = e10s # Bug 1102025 - different principals for the bookmarklet only in e10s mode (unclear if test or 'real' issue) [browser_keywordSearch.js] [browser_keywordSearch_postData.js] [browser_lastAccessedTab.js] skip-if = toolkit == "windows" # Disabled on Windows due to frequent failures (bug 969405) [browser_locationBarCommand.js] skip-if = os == "linux" # Linux: Intermittent failures, bug 917535 [browser_locationBarExternalLoad.js] [browser_menuButtonFitts.js]
--- a/browser/base/content/test/general/browser_keywordBookmarklets.js +++ b/browser/base/content/test/general/browser_keywordBookmarklets.js @@ -7,26 +7,46 @@ add_task(function* test_keyword_bookmark let tab = gBrowser.selectedTab = gBrowser.addTab(); registerCleanupFunction (function* () { gBrowser.removeTab(tab); yield PlacesUtils.bookmarks.remove(bm); }); yield promisePageShow(); let originalPrincipal = gBrowser.contentPrincipal; + function getPrincipalURI() { + return ContentTask.spawn(tab.linkedBrowser, null, function() { + return content.document.nodePrincipal.URI.spec; + }); + } + + let originalPrincipalURI = yield getPrincipalURI(); + yield PlacesUtils.keywords.insert({ keyword: "bm", url: "javascript:1;" }) // Enter bookmarklet keyword in the URL bar gURLBar.value = "bm"; gURLBar.focus(); EventUtils.synthesizeKey("VK_RETURN", {}); yield promisePageShow(); - ok(gBrowser.contentPrincipal.equals(originalPrincipal), "javascript bookmarklet should inherit principal"); + let newPrincipalURI = yield getPrincipalURI(); + is(newPrincipalURI, originalPrincipalURI, "content has the same principal"); + + // In e10s, null principals don't round-trip so the same null principal sent + // from the child will be a new null principal. Verify that this is the + // case. + if (tab.linkedBrowser.isRemoteBrowser) { + ok(originalPrincipal.isNullPrincipal && gBrowser.contentPrincipal.isNullPrincipal, + "both principals should be null principals in the parent"); + } else { + ok(gBrowser.contentPrincipal.equals(originalPrincipal), + "javascript bookmarklet should inherit principal"); + } }); function* promisePageShow() { return new Promise(resolve => { gBrowser.selectedBrowser.addEventListener("pageshow", function listen() { gBrowser.selectedBrowser.removeEventListener("pageshow", listen); resolve(); });
--- a/configure.in +++ b/configure.in @@ -4219,17 +4219,16 @@ cairo-uikit) AC_DEFINE(MOZ_WIDGET_UIKIT) LDFLAGS="$LDFLAGS -framework UIKit -lobjc" TK_CFLAGS="-DNO_X11" TK_LIBS='-Wl,-framework,Foundation -Wl,-framework,CoreFoundation -Wl,-framework,CoreGraphics -Wl,-framework,CoreText -Wl,-framework,AVFoundation -Wl,-framework,AudioToolbox -Wl,-framework,CoreMedia -Wl,-framework,CoreVideo -Wl,-framework,OpenGLES -Wl,-framework,QuartzCore' CFLAGS="$CFLAGS $TK_CFLAGS" CXXFLAGS="$CXXFLAGS $TK_CFLAGS" MOZ_USER_DIR="Mozilla" MOZ_FS_LAYOUT=bundle - AC_DEFINE(MOZ_SINGLE_PROCESS_APZ) ;; cairo-android) AC_DEFINE(MOZ_WIDGET_ANDROID) MOZ_WIDGET_TOOLKIT=android MOZ_PDF_PRINTING=1 MOZ_INSTRUMENT_EVENT_LOOP=1 ;; @@ -4793,21 +4792,18 @@ fi dnl ======================================================== dnl = Enable the C++ async pan/zoom code instead of the Java version dnl ======================================================== MOZ_ARG_ENABLE_BOOL(android-apz, [ --enable-android-apz Switch to C++ pan/zoom code], MOZ_ANDROID_APZ=1, MOZ_ANDROID_APZ=) if test -n "$MOZ_ANDROID_APZ"; then - dnl Do this if defined in confvars.sh - AC_DEFINE(MOZ_ANDROID_APZ) - if test -z "$MOZ_B2GDROID"; then - AC_DEFINE(MOZ_SINGLE_PROCESS_APZ) - fi + dnl Do this if defined in confvars.sh + AC_DEFINE(MOZ_ANDROID_APZ) fi dnl ======================================================== dnl = Disable WebSMS backend dnl ======================================================== MOZ_ARG_DISABLE_BOOL(websms-backend, [ --disable-websms-backend Disable WebSMS backend],
--- a/dom/apps/OfflineCacheInstaller.jsm +++ b/dom/apps/OfflineCacheInstaller.jsm @@ -224,17 +224,17 @@ function installCache(app) { return; let cacheManifest = cacheDir.clone(); cacheManifest.append('manifest.appcache'); if (!cacheManifest.exists()) return; let principal = - Services.scriptSecurityManager.createCodebasePrincipal(app.origin, {appId: aApp.localId}); + Services.scriptSecurityManager.createCodebasePrincipal(app.origin, {appId: app.localId}); // If the build has been correctly configured, this should not happen! // If we install the cache anyway, it won't be updateable. If we don't install // it, the application won't be useable offline. let metadataLoaded; if (!resourcesMetadata.exists()) { // Not debug, since this is something that should be logged always! dump("OfflineCacheInstaller: App " + app.appId + " does have an app cache" +
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7504,17 +7504,19 @@ nsContentUtils::TransferableToIPCTransfe // constructor before processing our response, which would crash. In // that case, hope that the caller is nsClipboardProxy::GetData, // called from editor and send over images as raw data. if (aInSyncMessage) { nsAutoCString type; if (IsFileImage(file, type)) { IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement(); item->flavor() = type; - SlurpFileToString(file, item->data()); + nsAutoCString data; + SlurpFileToString(file, data); + item->data() = data; } continue; } blobImpl = new BlobImplFile(file, false); ErrorResult rv; // Ensure that file data is cached no that the content process @@ -7664,21 +7666,20 @@ nsContentUtils::GetButtonsFlagForButton( } } LayoutDeviceIntPoint nsContentUtils::ToWidgetPoint(const CSSPoint& aPoint, const nsPoint& aOffset, nsPresContext* aPresContext) { - nsPoint point = CSSPoint::ToAppUnits(aPoint) + aOffset; -#if defined(MOZ_SINGLE_PROCESS_APZ) - point = point.ApplyResolution(aPresContext->PresShell()->GetCumulativeScaleResolution()); -#endif - return LayoutDeviceIntPoint::FromAppUnitsRounded(point, aPresContext->AppUnitsPerDevPixel()); + return LayoutDeviceIntPoint::FromAppUnitsRounded( + (CSSPoint::ToAppUnits(aPoint) + + aOffset).ApplyResolution(aPresContext->PresShell()->GetCumulativeNonRootScaleResolution()), + aPresContext->AppUnitsPerDevPixel()); } nsView* nsContentUtils::GetViewToDispatchEvent(nsPresContext* presContext, nsIPresShell** presShell) { if (presContext && presShell) { *presShell = presContext->PresShell();
--- a/dom/base/nsJSTimeoutHandler.cpp +++ b/dom/base/nsJSTimeoutHandler.cpp @@ -281,18 +281,17 @@ NS_CreateJSTimeoutHandler(JSContext *aCx new nsJSScriptTimeoutHandler(aCx, aWindow, aFunction, args, aError); return aError.Failed() ? nullptr : handler.forget(); } already_AddRefed<nsIScriptTimeoutHandler> NS_CreateJSTimeoutHandler(JSContext* aCx, nsGlobalWindow *aWindow, const nsAString& aExpression, ErrorResult& aError) { - ErrorResult rv; bool allowEval = false; RefPtr<nsJSScriptTimeoutHandler> handler = - new nsJSScriptTimeoutHandler(aCx, aWindow, aExpression, &allowEval, rv); - if (rv.Failed() || !allowEval) { + new nsJSScriptTimeoutHandler(aCx, aWindow, aExpression, &allowEval, aError); + if (aError.Failed() || !allowEval) { return nullptr; } return handler.forget(); }
--- a/dom/base/test/browser_bug593387.js +++ b/dom/base/test/browser_bug593387.js @@ -1,65 +1,70 @@ /* * Test for bug 593387 * Loads a chrome document in a content docshell and then inserts a * X-Frame-Options: DENY iframe into the document and verifies that the document * loads. The policy we are enforcing is outlined here: * https://bugzilla.mozilla.org/show_bug.cgi?id=593387#c17 */ -var newBrowser; -function test() { - waitForExplicitFinish(); +add_task(function* test() { + yield BrowserTestUtils.withNewTab({ gBrowser, + url: "chrome://global/content/mozilla.xhtml" }, + function* (newBrowser) { + // NB: We load the chrome:// page in the parent process. + yield testXFOFrameInChrome(newBrowser); - var newTab = gBrowser.addTab(); - gBrowser.selectedTab = newTab; - newBrowser = gBrowser.getBrowserForTab(newTab); - //alert(newBrowser.contentWindow); + // Run next test (try the same with a content top-level context) + yield BrowserTestUtils.loadURI(newBrowser, "http://example.com/"); + yield BrowserTestUtils.browserLoaded(newBrowser); - newBrowser.addEventListener("load", testXFOFrameInChrome, true); - newBrowser.contentWindow.location = "chrome://global/content/mozilla.xhtml"; -} + yield ContentTask.spawn(newBrowser, null, testXFOFrameInContent); + }); +}); -function testXFOFrameInChrome() { - newBrowser.removeEventListener("load", testXFOFrameInChrome, true); - +function testXFOFrameInChrome(newBrowser) { // Insert an iframe that specifies "X-Frame-Options: DENY" and verify // that it loads, since the top context is chrome + var deferred = {}; + deferred.promise = new Promise((resolve) => { + deferred.resolve = resolve; + }); + var frame = newBrowser.contentDocument.createElement("iframe"); frame.src = "http://mochi.test:8888/tests/dom/base/test/file_x-frame-options_page.sjs?testid=deny&xfo=deny"; - frame.addEventListener("load", function() { - frame.removeEventListener("load", arguments.callee, true); + frame.addEventListener("load", function loaded() { + frame.removeEventListener("load", loaded, true); // Test that the frame loaded var test = this.contentDocument.getElementById("test"); is(test.tagName, "H1", "wrong element type"); is(test.textContent, "deny", "wrong textContent"); - - // Run next test (try the same with a content top-level context) - newBrowser.addEventListener("load", testXFOFrameInContent, true); - newBrowser.contentWindow.location = "http://example.com/"; + deferred.resolve(); }, true); newBrowser.contentDocument.body.appendChild(frame); + return deferred.promise; } -function testXFOFrameInContent() { - newBrowser.removeEventListener("load", testXFOFrameInContent, true); - +function testXFOFrameInContent(newBrowser) { // Insert an iframe that specifies "X-Frame-Options: DENY" and verify that it // is blocked from loading since the top browsing context is another site - var frame = newBrowser.contentDocument.createElement("iframe"); + var deferred = {}; + deferred.promise = new Promise((resolve) => { + deferred.resolve = resolve; + }); + + var frame = content.document.createElement("iframe"); frame.src = "http://mochi.test:8888/tests/dom/base/test/file_x-frame-options_page.sjs?testid=deny&xfo=deny"; - frame.addEventListener("load", function() { - frame.removeEventListener("load", arguments.callee, true); + frame.addEventListener("load", function loaded() { + frame.removeEventListener("load", loaded, true); // Test that the frame DID NOT load var test = this.contentDocument.getElementById("test"); - is(test, undefined, "should be about:blank"); + is(test, null, "should be about:blank"); - // Finalize the test - gBrowser.removeCurrentTab(); - finish(); + deferred.resolve(); }, true); - newBrowser.contentDocument.body.appendChild(frame); + content.document.body.appendChild(frame); + return deferred.promise; }
--- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -2482,54 +2482,69 @@ CanvasRenderingContext2D::UpdateFilter() gfxRect(0, 0, mWidth, mHeight), CurrentState().filterAdditionalImages); } // // rects // -// bug 1074733 -// The canvas spec does not forbid rects with negative w or h, so given -// corners (x, y), (x+w, y), (x+w, y+h), and (x, y+h) we must generate -// the appropriate rect by flipping negative dimensions. This prevents -// draw targets from receiving "empty" rects later on. -static void -NormalizeRect(double& aX, double& aY, double& aWidth, double& aHeight) -{ +static bool +ValidateRect(double& aX, double& aY, double& aWidth, double& aHeight) +{ + + // bug 1018527 + // The values of canvas API input are in double precision, but Moz2D APIs are + // using float precision. Bypass canvas API calls when the input is out of + // float precision to avoid precision problem + if (!std::isfinite((float)aX) | !std::isfinite((float)aY) | + !std::isfinite((float)aWidth) | !std::isfinite((float)aHeight)) { + return false; + } + + // bug 1074733 + // The canvas spec does not forbid rects with negative w or h, so given + // corners (x, y), (x+w, y), (x+w, y+h), and (x, y+h) we must generate + // the appropriate rect by flipping negative dimensions. This prevents + // draw targets from receiving "empty" rects later on. if (aWidth < 0) { aWidth = -aWidth; aX -= aWidth; } if (aHeight < 0) { aHeight = -aHeight; aY -= aHeight; } + return true; } void CanvasRenderingContext2D::ClearRect(double x, double y, double w, double h) { - NormalizeRect(x, y, w, h); + if(!ValidateRect(x, y, w, h)) { + return; + } EnsureTarget(); mTarget->ClearRect(gfx::Rect(x, y, w, h)); RedrawUser(gfxRect(x, y, w, h)); } void CanvasRenderingContext2D::FillRect(double x, double y, double w, double h) { const ContextState &state = CurrentState(); - NormalizeRect(x, y, w, h); + if(!ValidateRect(x, y, w, h)) { + return; + } if (state.patternStyles[Style::FILL]) { CanvasPattern::RepeatMode repeat = state.patternStyles[Style::FILL]->mRepeat; // In the FillRect case repeat modes are easy to deal with. bool limitx = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATY; bool limity = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATX; @@ -2594,17 +2609,20 @@ CanvasRenderingContext2D::StrokeRect(dou { const ContextState &state = CurrentState(); gfx::Rect bounds; if (!w && !h) { return; } - NormalizeRect(x, y, w, h); + + if(!ValidateRect(x, y, w, h)) { + return; + } EnsureTarget(); if (!IsTargetValid()) { return; } if (NeedToCalculateBounds()) { bounds = gfx::Rect(x - state.lineWidth / 2.0f, y - state.lineWidth / 2.0f, @@ -4370,18 +4388,20 @@ CanvasRenderingContext2D::DrawImage(cons { if (mDrawObserver) { mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::DrawImage); } MOZ_ASSERT(optional_argc == 0 || optional_argc == 2 || optional_argc == 6); if (optional_argc == 6) { - NormalizeRect(sx, sy, sw, sh); - NormalizeRect(dx, dy, dw, dh); + if (!ValidateRect(sx, sy, sw, sh) || + !ValidateRect(dx, dy, dw, dh)) { + return; + } } RefPtr<SourceSurface> srcSurf; gfx::IntSize imgSize; Element* element = nullptr; EnsureTarget();
--- a/dom/canvas/test/test_canvas.html +++ b/dom/canvas/test/test_canvas.html @@ -21531,16 +21531,30 @@ ctx.fillStyle = '#0f0'; ctx.fillRect(0, 0, 100, 50); isPixel(ctx, 50,25, 0,255,0,255, 0); } </script> +<!-- [[[ test_2d.clearRect.testdoubleprecision.html ]]] --> + +<p>Canvas test: 2d.clearRect.testdoubleprecision</p> +<canvas id="c690" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas> +<script> + +function test_2d_clearRect_testdoubleprecision() { + var canvas = document.getElementById('c690'); + ctx = canvas.getContext('2d'); + ctx.setTransform(1, 1, 1, 1, 0, 0); + ctx.clearRect(-1.79e+308, 0, 1.79e+308, 8); +} +</script> + <script> function asyncTestsDone() { if (isDone_test_2d_drawImage_animated_apng && isDone_test_2d_pattern_animated_gif && isDone_test_2d_drawImage_animated_gif) { SimpleTest.finish(); } else { @@ -24830,17 +24844,23 @@ try { ok(false, "unexpected exception thrown in: test_2d_transformation_reset_transform"); } try { // run this test last since it replaces the getContext method test_type_replace(); } catch (e) { ok(false, "unexpected exception thrown in: test_type_replace"); } - + try { + test_2d_clearRect_testdoubleprecision(); + } catch(e) { + throw e; + ok(false, "unexpected exception thrown in: test_2d_clearRect_testdoubleprecision"); + } + //run the asynchronous tests try { test_2d_drawImage_animated_apng(); } catch (e) { ok(false, "unexpected exception thrown in: test_2d_drawImage_animated_apng"); } try { test_2d_drawImage_animated_gif();
--- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -930,21 +930,19 @@ Event::GetScreenCoords(nsPresContext* aP WidgetGUIEvent* guiEvent = aEvent->AsGUIEvent(); if (!aPresContext || !(guiEvent && guiEvent->widget)) { return CSSIntPoint(aPoint.x, aPoint.y); } nsPoint pt = LayoutDevicePixel::ToAppUnits(aPoint, aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom()); -#if defined(MOZ_SINGLE_PROCESS_APZ) if (aPresContext->PresShell()) { - pt = pt.RemoveResolution(aPresContext->PresShell()->GetCumulativeScaleResolution()); + pt = pt.RemoveResolution(aPresContext->PresShell()->GetCumulativeNonRootScaleResolution()); } -#endif pt += LayoutDevicePixel::ToAppUnits(guiEvent->widget->WidgetToScreenOffset(), aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom()); return CSSPixel::FromAppUnitsRounded(pt); } // static
--- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -755,25 +755,27 @@ EventStateManager::PreHandleEvent(nsPres case eBeforeKeyUp: case eKeyUp: case eAfterKeyUp: { nsIContent* content = GetFocusedContent(); if (content) mCurrentTargetContent = content; - // NOTE: Don't refer TextComposition::IsComposing() since DOM Level 3 - // Events defines that KeyboardEvent.isComposing is true when it's + // NOTE: Don't refer TextComposition::IsComposing() since UI Events + // defines that KeyboardEvent.isComposing is true when it's // dispatched after compositionstart and compositionend. // TextComposition::IsComposing() is false even before // compositionend if there is no composing string. - WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent(); + // And also don't expose other document's composition state. + // A native IME context is typically shared by multiple documents. + // So, don't use GetTextCompositionFor(nsIWidget*) here. RefPtr<TextComposition> composition = - IMEStateManager::GetTextCompositionFor(keyEvent); - keyEvent->mIsComposing = !!composition; + IMEStateManager::GetTextCompositionFor(aPresContext); + aEvent->AsKeyboardEvent()->mIsComposing = !!composition; } break; case eWheel: case eWheelOperationStart: case eWheelOperationEnd: { NS_ASSERTION(aEvent->mFlags.mIsTrusted, "Untrusted wheel event shouldn't be here");
--- a/dom/events/IMEStateManager.cpp +++ b/dom/events/IMEStateManager.cpp @@ -1139,37 +1139,47 @@ IMEStateManager::DispatchCompositionEven bool aIsSynthesized) { RefPtr<TabParent> tabParent = aEventTargetNode->IsContent() ? TabParent::GetFrom(aEventTargetNode->AsContent()) : nullptr; MOZ_LOG(sISMLog, LogLevel::Info, ("ISM: IMEStateManager::DispatchCompositionEvent(aNode=0x%p, " - "aPresContext=0x%p, aCompositionEvent={ message=%s, " + "aPresContext=0x%p, aCompositionEvent={ mMessage=%s, " + "mNativeIMEContext={ mRawNativeIMEContext=0x%X, " + "mOriginProcessID=0x%X }, widget(0x%p)={ " + "GetNativeIMEContext()={ mRawNativeIMEContext=0x%X, " + "mOriginProcessID=0x%X }, Destroyed()=%s }, " "mFlags={ mIsTrusted=%s, mPropagationStopped=%s } }, " "aIsSynthesized=%s), tabParent=%p", aEventTargetNode, aPresContext, ToChar(aCompositionEvent->mMessage), + aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext, + aCompositionEvent->mNativeIMEContext.mOriginProcessID, + aCompositionEvent->widget.get(), + aCompositionEvent->widget->GetNativeIMEContext().mRawNativeIMEContext, + aCompositionEvent->widget->GetNativeIMEContext().mOriginProcessID, + GetBoolName(aCompositionEvent->widget->Destroyed()), GetBoolName(aCompositionEvent->mFlags.mIsTrusted), GetBoolName(aCompositionEvent->mFlags.mPropagationStopped), GetBoolName(aIsSynthesized), tabParent.get())); if (!aCompositionEvent->mFlags.mIsTrusted || aCompositionEvent->mFlags.mPropagationStopped) { return; } MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionUpdate, "compositionupdate event shouldn't be dispatched manually"); EnsureTextCompositionArray(); RefPtr<TextComposition> composition = - sTextCompositions->GetCompositionFor(aCompositionEvent->widget); + sTextCompositions->GetCompositionFor(aCompositionEvent); if (!composition) { // If synthesized event comes after delayed native composition events // for request of commit or cancel, we should ignore it. if (NS_WARN_IF(aIsSynthesized)) { return; } MOZ_LOG(sISMLog, LogLevel::Debug, ("ISM: IMEStateManager::DispatchCompositionEvent(), " @@ -1273,18 +1283,28 @@ void IMEStateManager::OnCompositionEventDiscarded( WidgetCompositionEvent* aCompositionEvent) { // Note that this method is never called for synthesized events for emulating // commit or cancel composition. MOZ_LOG(sISMLog, LogLevel::Info, ("ISM: IMEStateManager::OnCompositionEventDiscarded(aCompositionEvent={ " - "mMessage=%s, mFlags={ mIsTrusted=%s } })", + "mMessage=%s, mNativeIMEContext={ mRawNativeIMEContext=0x%X, " + "mOriginProcessID=0x%X }, widget(0x%p)={ " + "GetNativeIMEContext()={ mRawNativeIMEContext=0x%X, " + "mOriginProcessID=0x%X }, Destroyed()=%s }, " + "mFlags={ mIsTrusted=%s } })", ToChar(aCompositionEvent->mMessage), + aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext, + aCompositionEvent->mNativeIMEContext.mOriginProcessID, + aCompositionEvent->widget.get(), + aCompositionEvent->widget->GetNativeIMEContext().mRawNativeIMEContext, + aCompositionEvent->widget->GetNativeIMEContext().mOriginProcessID, + GetBoolName(aCompositionEvent->widget->Destroyed()), GetBoolName(aCompositionEvent->mFlags.mIsTrusted))); if (!aCompositionEvent->mFlags.mIsTrusted) { return; } // Ignore compositionstart for now because sTextCompositions may not have // been created yet. @@ -1644,16 +1664,32 @@ IMEStateManager::GetTextCompositionFor(n } RefPtr<TextComposition> textComposition = sTextCompositions->GetCompositionFor(aWidget); return textComposition.forget(); } // static already_AddRefed<TextComposition> -IMEStateManager::GetTextCompositionFor(WidgetGUIEvent* aGUIEvent) +IMEStateManager::GetTextCompositionFor( + const WidgetCompositionEvent* aCompositionEvent) { - MOZ_ASSERT(aGUIEvent->AsCompositionEvent() || aGUIEvent->AsKeyboardEvent(), - "aGUIEvent has to be WidgetCompositionEvent or WidgetKeyboardEvent"); - return GetTextCompositionFor(aGUIEvent->widget); + if (!sTextCompositions) { + return nullptr; + } + RefPtr<TextComposition> textComposition = + sTextCompositions->GetCompositionFor(aCompositionEvent); + return textComposition.forget(); +} + +// static +already_AddRefed<TextComposition> +IMEStateManager::GetTextCompositionFor(nsPresContext* aPresContext) +{ + if (!sTextCompositions) { + return nullptr; + } + RefPtr<TextComposition> textComposition = + sTextCompositions->GetCompositionFor(aPresContext); + return textComposition.forget(); } } // namespace mozilla
--- a/dom/events/IMEStateManager.h +++ b/dom/events/IMEStateManager.h @@ -176,21 +176,27 @@ public: /** * Get TextComposition from widget. */ static already_AddRefed<TextComposition> GetTextCompositionFor(nsIWidget* aWidget); /** * Returns TextComposition instance for the event. - * - * @param aGUIEvent Should be a composition event which is being dispatched. */ static already_AddRefed<TextComposition> - GetTextCompositionFor(WidgetGUIEvent* aGUIEvent); + GetTextCompositionFor(const WidgetCompositionEvent* aCompositionEvent); + + /** + * Returns TextComposition instance for the pres context. + * Be aware, even if another pres context which shares native IME context with + * specified pres context has composition, this returns nullptr. + */ + static already_AddRefed<TextComposition> + GetTextCompositionFor(nsPresContext* aPresContext); /** * Send a notification to IME. It depends on the IME or platform spec what * will occur (or not occur). */ static nsresult NotifyIME(const IMENotification& aNotification, nsIWidget* aWidget, bool aOriginIsRemote = false);
--- a/dom/events/TextComposition.cpp +++ b/dom/events/TextComposition.cpp @@ -34,50 +34,44 @@ bool TextComposition::sHandlingSelection TextComposition::TextComposition(nsPresContext* aPresContext, nsINode* aNode, TabParent* aTabParent, WidgetCompositionEvent* aCompositionEvent) : mPresContext(aPresContext) , mNode(aNode) , mTabParent(aTabParent) - , mNativeContext( - aCompositionEvent->widget->GetInputContext().mNativeIMEContext) + , mNativeContext(aCompositionEvent->mNativeIMEContext) , mCompositionStartOffset(0) , mCompositionTargetOffset(0) , mIsSynthesizedForTests(aCompositionEvent->mFlags.mIsSynthesizedForTests) , mIsComposing(false) , mIsEditorHandlingEvent(false) , mIsRequestingCommit(false) , mIsRequestingCancel(false) , mRequestedToCommitOrCancel(false) , mWasNativeCompositionEndEventDiscarded(false) , mAllowControlCharacters( Preferences::GetBool("dom.compositionevent.allow_control_characters", false)) { + MOZ_ASSERT(aCompositionEvent->mNativeIMEContext.IsValid()); } void TextComposition::Destroy() { mPresContext = nullptr; mNode = nullptr; mTabParent = nullptr; // TODO: If the editor is still alive and this is held by it, we should tell // this being destroyed for cleaning up the stuff. } bool -TextComposition::MatchesNativeContext(nsIWidget* aWidget) const -{ - return mNativeContext == aWidget->GetInputContext().mNativeIMEContext; -} - -bool TextComposition::IsValidStateForComposition(nsIWidget* aWidget) const { return !Destroyed() && aWidget && !aWidget->Destroyed() && mPresContext->GetPresShell() && !mPresContext->GetPresShell()->IsDestroying(); } bool @@ -109,16 +103,17 @@ TextComposition::CloneAndDispatchAs( MOZ_ASSERT(IsValidStateForComposition(aCompositionEvent->widget), "Should be called only when it's safe to dispatch an event"); WidgetCompositionEvent compositionEvent(aCompositionEvent->mFlags.mIsTrusted, aMessage, aCompositionEvent->widget); compositionEvent.time = aCompositionEvent->time; compositionEvent.timeStamp = aCompositionEvent->timeStamp; compositionEvent.mData = aCompositionEvent->mData; + compositionEvent.mNativeIMEContext = aCompositionEvent->mNativeIMEContext; compositionEvent.mOriginalMessage = aCompositionEvent->mMessage; compositionEvent.mFlags.mIsSynthesizedForTests = aCompositionEvent->mFlags.mIsSynthesizedForTests; nsEventStatus dummyStatus = nsEventStatus_eConsumeNoDefault; nsEventStatus* status = aStatus ? aStatus : &dummyStatus; if (aMessage == eCompositionUpdate) { mLastData = compositionEvent.mData; @@ -608,32 +603,34 @@ TextComposition::CompositionEventDispatc return NS_OK; // cannot dispatch any events anymore } RefPtr<nsPresContext> presContext = mTextComposition->mPresContext; nsEventStatus status = nsEventStatus_eIgnore; switch (mEventMessage) { case eCompositionStart: { WidgetCompositionEvent compStart(true, eCompositionStart, widget); + compStart.mNativeIMEContext = mTextComposition->mNativeContext; WidgetQueryContentEvent selectedText(true, eQuerySelectedText, widget); ContentEventHandler handler(presContext); handler.OnQuerySelectedText(&selectedText); NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text"); compStart.mData = selectedText.mReply.mString; compStart.mFlags.mIsSynthesizedForTests = mTextComposition->IsSynthesizedForTests(); IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext, &compStart, &status, nullptr, mIsSynthesizedEvent); break; } case eCompositionChange: case eCompositionCommitAsIs: case eCompositionCommit: { WidgetCompositionEvent compEvent(true, mEventMessage, widget); + compEvent.mNativeIMEContext = mTextComposition->mNativeContext; if (mEventMessage != eCompositionCommitAsIs) { compEvent.mData = mData; } compEvent.mFlags.mIsSynthesizedForTests = mTextComposition->IsSynthesizedForTests(); IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext, &compEvent, &status, nullptr, mIsSynthesizedEvent); @@ -645,27 +642,36 @@ TextComposition::CompositionEventDispatc return NS_OK; } /****************************************************************************** * TextCompositionArray ******************************************************************************/ TextCompositionArray::index_type -TextCompositionArray::IndexOf(nsIWidget* aWidget) +TextCompositionArray::IndexOf(const NativeIMEContext& aNativeIMEContext) { + if (!aNativeIMEContext.IsValid()) { + return NoIndex; + } for (index_type i = Length(); i > 0; --i) { - if (ElementAt(i - 1)->MatchesNativeContext(aWidget)) { + if (ElementAt(i - 1)->GetNativeIMEContext() == aNativeIMEContext) { return i - 1; } } return NoIndex; } TextCompositionArray::index_type +TextCompositionArray::IndexOf(nsIWidget* aWidget) +{ + return IndexOf(aWidget->GetNativeIMEContext()); +} + +TextCompositionArray::index_type TextCompositionArray::IndexOf(nsPresContext* aPresContext) { for (index_type i = Length(); i > 0; --i) { if (ElementAt(i - 1)->GetPresContext() == aPresContext) { return i - 1; } } return NoIndex; @@ -689,16 +695,37 @@ TextCompositionArray::GetCompositionFor( index_type i = IndexOf(aWidget); if (i == NoIndex) { return nullptr; } return ElementAt(i); } TextComposition* +TextCompositionArray::GetCompositionFor( + const WidgetCompositionEvent* aCompositionEvent) +{ + index_type i = IndexOf(aCompositionEvent->mNativeIMEContext); + if (i == NoIndex) { + return nullptr; + } + return ElementAt(i); +} + +TextComposition* +TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext) +{ + index_type i = IndexOf(aPresContext); + if (i == NoIndex) { + return nullptr; + } + return ElementAt(i); +} + +TextComposition* TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext, nsINode* aNode) { index_type i = IndexOf(aPresContext, aNode); if (i == NoIndex) { return nullptr; } return ElementAt(i);
--- a/dom/events/TextComposition.h +++ b/dom/events/TextComposition.h @@ -71,17 +71,20 @@ public: nsIWidget* GetWidget() const { return mPresContext ? mPresContext->GetRootWidget() : nullptr; } // Returns true if the composition is started with synthesized event which // came from nsDOMWindowUtils. bool IsSynthesizedForTests() const { return mIsSynthesizedForTests; } - bool MatchesNativeContext(nsIWidget* aWidget) const; + const widget::NativeIMEContext& GetNativeIMEContext() const + { + return mNativeContext; + } /** * This is called when IMEStateManager stops managing the instance. */ void Destroy(); /** * Request to commit (or cancel) the composition to IME. This method should @@ -186,17 +189,17 @@ private: RefPtr<TabParent> mTabParent; // This is the clause and caret range information which is managed by // the focused editor. This may be null if there is no clauses or caret. RefPtr<TextRangeArray> mRanges; // mNativeContext stores a opaque pointer. This works as the "ID" for this // composition. Don't access the instance, it may not be available. - void* mNativeContext; + widget::NativeIMEContext mNativeContext; // mEditorWeak is a weak reference to the focused editor handling composition. nsWeakPtr mEditorWeak; // mLastData stores the data attribute of the latest composition event (except // the compositionstart event). nsString mLastData; @@ -395,21 +398,29 @@ private: * compositions in the process. If the instance is it, each TextComposition * in the array can be destroyed by calling some methods of itself. */ class TextCompositionArray final : public nsAutoTArray<RefPtr<TextComposition>, 2> { public: + // Looking for per native IME context. + index_type IndexOf(const widget::NativeIMEContext& aNativeIMEContext); index_type IndexOf(nsIWidget* aWidget); + + TextComposition* GetCompositionFor(nsIWidget* aWidget); + TextComposition* GetCompositionFor( + const WidgetCompositionEvent* aCompositionEvent); + + // Looking for per nsPresContext index_type IndexOf(nsPresContext* aPresContext); index_type IndexOf(nsPresContext* aPresContext, nsINode* aNode); - TextComposition* GetCompositionFor(nsIWidget* aWidget); + TextComposition* GetCompositionFor(nsPresContext* aPresContext); TextComposition* GetCompositionFor(nsPresContext* aPresContext, nsINode* aNode); TextComposition* GetCompositionInContent(nsPresContext* aPresContext, nsIContent* aContent); }; } // namespace mozilla
--- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -4975,32 +4975,32 @@ HTMLMediaElement::GetTopLevelPrincipal() } principal = doc->NodePrincipal(); return principal.forget(); } #endif // MOZ_EME NS_IMETHODIMP HTMLMediaElement::WindowAudioCaptureChanged() { - MOZ_ASSERT(mAudioChannelAgent); + MOZ_ASSERT(mAudioChannelAgent); if (!OwnerDoc()->GetInnerWindow()) { return NS_OK; } bool captured = OwnerDoc()->GetInnerWindow()->GetAudioCaptured(); if (captured != mAudioCapturedByWindow) { if (captured) { mAudioCapturedByWindow = true; nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(OwnerDoc()->GetParentObject()); uint64_t id = window->WindowID(); MediaStreamGraph* msg = MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER, - AudioChannel::Normal); + mAudioChannel); if (GetSrcMediaStream()) { mCaptureStreamPort = msg->ConnectToCaptureStream(id, GetSrcMediaStream()); } else { RefPtr<DOMMediaStream> stream = CaptureStreamInternal(false, msg); mCaptureStreamPort = msg->ConnectToCaptureStream(id, stream->GetPlaybackStream()); } } else {
--- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -230,30 +230,29 @@ parent: * Notifies chrome to position change * * contentCache Cache of content */ prio(urgent) async NotifyIMEPositionChange(ContentCache contentCache, IMENotification notification); /** - * Instructs chrome to end any pending composition + * Requests chrome to commit or cancel composition of IME. * - * cancel true if composition should be cancelled - * noCompositionEvent true if no composition event is fired by commit or - * cancel - * composition Text to commit before ending the composition + * cancel Set true if composition should be cancelled. * - * if cancel is true, - * widget should return empty string for composition - * if cancel is false, - * widget should return the current composition text + * isCommitted Returns true if the request causes composition + * being committed synchronously. + * committedString Returns committed string. The may be non-empty + * string even if cancel is true because IME may + * try to restore selected string which was + * replaced with the composition. */ - prio(urgent) sync EndIMEComposition(bool cancel) - returns (bool noCompositionEvent, nsString composition); + prio(urgent) sync RequestIMEToCommitComposition(bool cancel) + returns (bool isCommitted, nsString committedString); /** * OnEventNeedingAckHandled() is called after a child process dispatches a * composition event or a selection event which is sent from the parent * process. * * message The message value of the handled event. */ @@ -291,18 +290,17 @@ parent: * Indicate, based on the current state, that some commands are enabled and * some are disabled. */ EnableDisableCommands(nsString action, nsCString[] enabledCommands, nsCString[] disabledCommands); prio(urgent) sync GetInputContext() returns (int32_t IMEEnabled, - int32_t IMEOpen, - intptr_t NativeIMEContext); + int32_t IMEOpen); prio(urgent) async SetInputContext(int32_t IMEEnabled, int32_t IMEOpen, nsString type, nsString inputmode, nsString actionHint, int32_t cause, int32_t focusChange);
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -2316,26 +2316,29 @@ TabParent::GetTabIdFrom(nsIDocShell *doc RenderFrameParent* TabParent::GetRenderFrame() { PRenderFrameParent* p = LoneManagedOrNull(ManagedPRenderFrameParent()); return static_cast<RenderFrameParent*>(p); } bool -TabParent::RecvEndIMEComposition(const bool& aCancel, - bool* aNoCompositionEvent, - nsString* aComposition) +TabParent::RecvRequestIMEToCommitComposition(const bool& aCancel, + bool* aIsCommitted, + nsString* aCommittedString) { nsCOMPtr<nsIWidget> widget = GetWidget(); if (!widget) { + *aIsCommitted = false; return true; } - *aNoCompositionEvent = - !mContentCache.RequestToCommitComposition(widget, aCancel, *aComposition); + + *aIsCommitted = + mContentCache.RequestIMEToCommitComposition(widget, aCancel, + *aCommittedString); return true; } bool TabParent::RecvStartPluginIME(const WidgetKeyboardEvent& aKeyboardEvent, const int32_t& aPanelX, const int32_t& aPanelY, nsString* aCommitted) { @@ -2358,31 +2361,28 @@ TabParent::RecvSetPluginFocused(const bo return true; } widget->SetPluginFocused((bool&)aFocused); return true; } bool TabParent::RecvGetInputContext(int32_t* aIMEEnabled, - int32_t* aIMEOpen, - intptr_t* aNativeIMEContext) + int32_t* aIMEOpen) { nsCOMPtr<nsIWidget> widget = GetWidget(); if (!widget) { *aIMEEnabled = IMEState::DISABLED; *aIMEOpen = IMEState::OPEN_STATE_NOT_SUPPORTED; - *aNativeIMEContext = 0; return true; } InputContext context = widget->GetInputContext(); *aIMEEnabled = static_cast<int32_t>(context.mIMEState.mEnabled); *aIMEOpen = static_cast<int32_t>(context.mIMEState.mOpen); - *aNativeIMEContext = reinterpret_cast<intptr_t>(context.mNativeIMEContext); return true; } bool TabParent::RecvSetInputContext(const int32_t& aIMEEnabled, const int32_t& aIMEOpen, const nsString& aType, const nsString& aInputmode,
--- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -182,27 +182,26 @@ public: virtual bool RecvNotifyIMESelection(const ContentCache& aContentCache, const widget::IMENotification& aEventMessage) override; virtual bool RecvUpdateContentCache(const ContentCache& aContentCache) override; virtual bool RecvNotifyIMEMouseButtonEvent(const widget::IMENotification& aEventMessage, bool* aConsumedByIME) override; virtual bool RecvNotifyIMEPositionChange(const ContentCache& aContentCache, const widget::IMENotification& aEventMessage) override; virtual bool RecvOnEventNeedingAckHandled(const EventMessage& aMessage) override; - virtual bool RecvEndIMEComposition(const bool& aCancel, - bool* aNoCompositionEvent, - nsString* aComposition) override; + virtual bool RecvRequestIMEToCommitComposition(const bool& aCancel, + bool* aIsCommitted, + nsString* aCommittedString) override; virtual bool RecvStartPluginIME(const WidgetKeyboardEvent& aKeyboardEvent, const int32_t& aPanelX, const int32_t& aPanelY, nsString* aCommitted) override; virtual bool RecvSetPluginFocused(const bool& aFocused) override; virtual bool RecvGetInputContext(int32_t* aIMEEnabled, - int32_t* aIMEOpen, - intptr_t* aNativeIMEContext) override; + int32_t* aIMEOpen) override; virtual bool RecvSetInputContext(const int32_t& aIMEEnabled, const int32_t& aIMEOpen, const nsString& aType, const nsString& aInputmode, const nsString& aActionHint, const int32_t& aCause, const int32_t& aFocusChange) override; virtual bool RecvRequestFocus(const bool& aCanRaise) override;
--- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -2107,58 +2107,67 @@ MediaDecoderStateMachine::SeekCompleted( // seekTime is bounded in suitable duration. See Bug 1112438. int64_t videoStart = video ? video->mTime : seekTime; int64_t audioStart = audio ? audio->mTime : seekTime; newCurrentTime = std::min(audioStart, videoStart); } else { newCurrentTime = video ? video->mTime : seekTime; } - if (mDecodingFirstFrame) { - // We were resuming from dormant, or initiated a seek early. - // We can fire loadeddata now. - FinishDecodeFirstFrame(); - } - // Change state to DECODING or COMPLETED now. SeekingStopped will // call MediaDecoderStateMachine::Seek to reset our state to SEEKING // if we need to seek again. bool isLiveStream = mResource->IsLiveStream(); + State nextState; if (mPendingSeek.Exists()) { // A new seek target came in while we were processing the old one. No rest // for the seeking. DECODER_LOG("A new seek came along while we were finishing the old one - staying in SEEKING"); - SetState(DECODER_STATE_SEEKING); + nextState = DECODER_STATE_SEEKING; } else if (GetMediaTime() == Duration().ToMicroseconds() && !isLiveStream) { // Seeked to end of media, move to COMPLETED state. Note we don't do // this when playing a live stream, since the end of media will advance // once we download more data! DECODER_LOG("Changed state from SEEKING (to %lld) to COMPLETED", seekTime); // Explicitly set our state so we don't decode further, and so // we report playback ended to the media element. - SetState(DECODER_STATE_COMPLETED); - DispatchDecodeTasksIfNeeded(); + nextState = DECODER_STATE_COMPLETED; } else { DECODER_LOG("Changed state from SEEKING (to %lld) to DECODING", seekTime); + nextState = DECODER_STATE_DECODING; + } + + // We want to resolve the seek request prior finishing the first frame + // to ensure that the seeked event is fired prior loadeded. + mCurrentSeek.Resolve(nextState == DECODER_STATE_COMPLETED, __func__); + + if (mDecodingFirstFrame) { + // We were resuming from dormant, or initiated a seek early. + // We can fire loadeddata now. + FinishDecodeFirstFrame(); + } + + if (nextState == DECODER_STATE_DECODING) { StartDecoding(); + } else { + SetState(nextState); } // Ensure timestamps are up to date. UpdatePlaybackPositionInternal(newCurrentTime); // Try to decode another frame to detect if we're at the end... DECODER_LOG("Seek completed, mCurrentPosition=%lld", mCurrentPosition.Ref()); // Reset quick buffering status. This ensures that if we began the // seek while quick-buffering, we won't bypass quick buffering mode // if we need to buffer after the seek. mQuickBuffering = false; - mCurrentSeek.Resolve(mState == DECODER_STATE_COMPLETED, __func__); ScheduleStateMachine(); if (video) { mMediaSink->Redraw(); mOnPlaybackEvent.Notify(MediaEventType::Invalidate); } }
--- a/dom/media/mediasource/test/mochitest.ini +++ b/dom/media/mediasource/test/mochitest.ini @@ -76,16 +76,18 @@ skip-if = ((os == "win" && os_version == [test_SeekableBeforeEndOfStream.html] [test_SeekableBeforeEndOfStream_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ [test_SeekableBeforeEndOfStreamSplit.html] [test_SeekableBeforeEndOfStreamSplit_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ [test_SeekNoData_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +[test_SeekedEvent_mp4.html] +skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ [test_SeekTwice_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ [test_Sequence_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ [test_SetModeThrows.html] [test_SplitAppendDelay.html] [test_SplitAppendDelay_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
new file mode 100644 --- /dev/null +++ b/dom/media/mediasource/test/test_SeekedEvent_mp4.html @@ -0,0 +1,75 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>MSE: Check that seeked event is fired prior loadeddata</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="mediasource.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); + +runWithMSE(function(ms, el) { + el.controls = true; + el._seeked = false; + el._loadeddata = false; + el._playing = false; + el.addEventListener("seeked", function() { + ok(true, "got seeked event"); + is(el._loadeddata, false, "can't have received loadeddata prior seeked"); + is(el._playing, false, "can't be playing prior seeked"); + el._seeked = true; + }); + el.addEventListener("loadeddata", function() { + ok(true, "got loadeddata event"); + is(el._seeked, true, "must have received seeked prior loadeddata"); + is(el._playing, false, "can't be playing prior playing"); + el._loadeddata = true; + }); + el.addEventListener("playing", function() { + ok(true, "got playing"); + is(el._seeked, true, "must have received seeked prior playing"); + is(el._loadeddata, true, "must have received loadeddata prior playing"); + el._playing = true; + }); + once(ms, 'sourceopen').then(function() { + ok(true, "Receive a sourceopen event"); + var videosb = ms.addSourceBuffer("video/mp4"); + is(el.readyState, el.HAVE_NOTHING, "readyState is HAVE_NOTHING"); + fetchAndLoad(videosb, 'bipbop/bipbop_video', ['init'], '.mp4') + .then(once.bind(null, el, "loadedmetadata")) + .then(function() { + el.play(); + videosb.timestampOffset = 2; + is(el.readyState, el.HAVE_METADATA, "readyState is HAVE_METADATA"); + // Load [2, 3.606). + var promises = []; + promises.push(once(el, "play")); + promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', ['1'], '.m4s')); + return Promise.all(promises); + }) + .then(function() { + return fetchAndLoad(videosb, 'bipbop/bipbop_video', ['2'], '.m4s'); + }) + .then(function() { + is(el.readyState, el.HAVE_METADATA, "readyState is HAVE_METADATA"); + el.currentTime = 2; + var promises = []; + promises.push(once(el, "seeked")); + promises.push(once(el, "playing")); + return Promise.all(promises); + }) + .then(function() { + ok(true, "completed seek"); + SimpleTest.finish(); + }); + }); +}); + +</script> +</pre> +</body> +</html>
--- a/dom/media/ogg/OggReader.cpp +++ b/dom/media/ogg/OggReader.cpp @@ -17,16 +17,18 @@ extern "C" { #include "opus/opus_multistream.h" } #include "mozilla/TimeStamp.h" #include "VorbisUtils.h" #include "MediaMetadataManager.h" #include "nsISeekableStream.h" #include "gfx2DGlue.h" +#include "mozilla/Telemetry.h" +#include "nsPrintfCString.h" using namespace mozilla::gfx; using namespace mozilla::media; namespace mozilla { // On B2G estimate the buffered ranges rather than calculating them explicitly. // This prevents us doing I/O on the main thread, which is prohibited in B2G. @@ -143,16 +145,27 @@ OggReader::OggReader(AbstractMediaDecode MOZ_COUNT_CTOR(OggReader); memset(&mTheoraInfo, 0, sizeof(mTheoraInfo)); } OggReader::~OggReader() { ogg_sync_clear(&mOggState); MOZ_COUNT_DTOR(OggReader); + if (HasAudio() || HasVideo()) { + // If we were able to initialize our decoders, report whether we encountered + // a chained stream or not. + ReentrantMonitorAutoEnter mon(mMonitor); + bool isChained = mIsChained; + nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([=]() -> void { + LOG(LogLevel::Debug, (nsPrintfCString("Reporting telemetry MEDIA_OGG_LOADED_IS_CHAINED=%d", isChained).get())); + Telemetry::Accumulate(Telemetry::ID::MEDIA_OGG_LOADED_IS_CHAINED, isChained); + }); + AbstractThread::MainThread()->Dispatch(task.forget()); + } } nsresult OggReader::Init() { int ret = ogg_sync_init(&mOggState); NS_ENSURE_TRUE(ret == 0, NS_ERROR_FAILURE); return NS_OK; } @@ -699,20 +712,23 @@ bool OggReader::DecodeAudioData() // file while trying to decode, so inform the audio queue that there'll // be no more samples. return false; } return true; } -void OggReader::SetChained(bool aIsChained) { +void OggReader::SetChained() { { ReentrantMonitorAutoEnter mon(mMonitor); - mIsChained = aIsChained; + if (mIsChained) { + return; + } + mIsChained = true; } mOnMediaNotSeekable.Notify(); } bool OggReader::ReadOggChain() { bool chained = false; OpusState* newOpusState = nullptr; @@ -795,17 +811,17 @@ bool OggReader::ReadOggChain() mInfo.mAudio.mRate = newOpusState->mRate; mInfo.mAudio.mChannels = newOpusState->mChannels; chained = true; tags = newOpusState->GetTags(); } if (chained) { - SetChained(true); + SetChained(); { auto t = mDecodedAudioFrames * USECS_PER_S / mInfo.mAudio.mRate; mTimedMetadataEvent.Notify( TimedMetadata(media::TimeUnit::FromMicroseconds(t), Move(tags), nsAutoPtr<MediaInfo>(new MediaInfo(mInfo)))); } return true; @@ -1134,17 +1150,17 @@ int64_t OggReader::RangeEndTime(int64_t int serial = ogg_page_serialno(&page); OggCodecState* codecState = nullptr; codecState = mCodecStore.Get(serial); if (!codecState) { // This page is from a bitstream which we haven't encountered yet. // It's probably from a new "link" in a "chained" ogg. Don't // bother even trying to find a duration... - SetChained(true); + SetChained(); endTime = -1; break; } int64_t t = codecState->Time(granulepos); if (t != -1) { endTime = t; } @@ -1908,17 +1924,17 @@ media::TimeIntervals OggReader::GetBuffe startOffset += page.header_len + page.body_len; continue; } else { // Page is for a stream we don't know about (possibly a chained // ogg), return OK to abort the finding any further ranges. This // prevents us searching through the rest of the media when we // may not be able to extract timestamps from it. - SetChained(true); + SetChained(); return buffered; } } if (startTime != -1) { // We were able to find a start time for that range, see if we can // find an end time. int64_t endTime = RangeEndTime(startOffset, endOffset, true);
--- a/dom/media/ogg/OggReader.h +++ b/dom/media/ogg/OggReader.h @@ -247,17 +247,17 @@ private: // succeeds. bool ReadHeaders(OggCodecState* aState); // Reads the next link in the chain. bool ReadOggChain(); // Set this media as being a chain and notifies the state machine that the // media is no longer seekable. - void SetChained(bool aIsChained); + void SetChained(); // Returns the next Ogg packet for an bitstream/codec state. Returns a // pointer to an ogg_packet on success, or nullptr if the read failed. // The caller is responsible for deleting the packet and its |packet| field. ogg_packet* NextOggPacket(OggCodecState* aCodecState); // Fills aTracks with the serial numbers of each active stream, for use by // various SkeletonState functions.
--- a/dom/media/platforms/wmf/DXVA2Manager.cpp +++ b/dom/media/platforms/wmf/DXVA2Manager.cpp @@ -275,31 +275,33 @@ D3D9DXVA2Manager::Init(nsACString& aFail D3DDEVTYPE_HAL, (D3DFORMAT)MAKEFOURCC('N','V','1','2'), D3DFMT_X8R8G8B8); if (!SUCCEEDED(hr)) { aFailureReason = nsPrintfCString("CheckDeviceFormatConversion failed with error %X", hr); return hr; } - // Create D3D9DeviceEx. + // Create D3D9DeviceEx. We pass null HWNDs here even though the documentation + // suggests that one of them should not be. At this point in time Chromium + // does the same thing for video acceleration. D3DPRESENT_PARAMETERS params = {0}; params.BackBufferWidth = 1; params.BackBufferHeight = 1; params.BackBufferFormat = D3DFMT_UNKNOWN; params.BackBufferCount = 1; params.SwapEffect = D3DSWAPEFFECT_DISCARD; - params.hDeviceWindow = ::GetShellWindow(); + params.hDeviceWindow = nullptr; params.Windowed = TRUE; params.Flags = D3DPRESENTFLAG_VIDEO; RefPtr<IDirect3DDevice9Ex> device; hr = d3d9Ex->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, - ::GetShellWindow(), + nullptr, D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_MIXED_VERTEXPROCESSING, ¶ms, nullptr, getter_AddRefs(device)); if (!SUCCEEDED(hr)) { aFailureReason = nsPrintfCString("CreateDeviceEx failed with error %X", hr);
--- a/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm +++ b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm @@ -74,28 +74,38 @@ SpeechTaskCallback::OnCancel() } NS_IMETHODIMP SpeechTaskCallback::OnPause() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; [mSpeechSynthesizer pauseSpeakingAtBoundary:NSSpeechImmediateBoundary]; + if (!mTask) { + // When calling pause() on child porcess, it may not receive end event + // from chrome process yet. + return NS_ERROR_FAILURE; + } mTask->DispatchPause(GetTimeDurationFromStart(), mCurrentIndex); return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } NS_IMETHODIMP SpeechTaskCallback::OnResume() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; [mSpeechSynthesizer continueSpeaking]; + if (!mTask) { + // When calling resume() on child porcess, it may not receive end event + // from chrome process yet. + return NS_ERROR_FAILURE; + } mTask->DispatchResume(GetTimeDurationFromStart(), mCurrentIndex); return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } NS_IMETHODIMP SpeechTaskCallback::OnVolumeChanged(float aVolume) @@ -115,23 +125,29 @@ SpeechTaskCallback::GetTimeDurationFromS TimeDuration duration = TimeStamp::Now() - mStartingTime; return duration.ToMilliseconds(); } void SpeechTaskCallback::OnWillSpeakWord(uint32_t aIndex) { mCurrentIndex = aIndex; + if (!mTask) { + return; + } mTask->DispatchBoundary(NS_LITERAL_STRING("word"), GetTimeDurationFromStart(), mCurrentIndex); } void SpeechTaskCallback::OnError(uint32_t aIndex) { + if (!mTask) { + return; + } mTask->DispatchError(GetTimeDurationFromStart(), aIndex); } void SpeechTaskCallback::OnDidFinishSpeaking() { mTask->DispatchEnd(GetTimeDurationFromStart(), mCurrentIndex); // no longer needed
--- a/dom/media/webspeech/synth/windows/SapiService.cpp +++ b/dom/media/webspeech/synth/windows/SapiService.cpp @@ -71,26 +71,36 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(SapiCall NS_IMPL_CYCLE_COLLECTING_RELEASE(SapiCallback) NS_IMETHODIMP SapiCallback::OnPause() { if (FAILED(mSapiClient->Pause())) { return NS_ERROR_FAILURE; } + if (!mTask) { + // When calling pause() on child porcess, it may not receive end event + // from chrome process yet. + return NS_ERROR_FAILURE; + } mTask->DispatchPause(GetTickCount() - mStartingTime, mCurrentIndex); return NS_OK; } NS_IMETHODIMP SapiCallback::OnResume() { if (FAILED(mSapiClient->Resume())) { return NS_ERROR_FAILURE; } + if (!mTask) { + // When calling resume() on child porcess, it may not receive end event + // from chrome process yet. + return NS_ERROR_FAILURE; + } mTask->DispatchResume(GetTickCount() - mStartingTime, mCurrentIndex); return NS_OK; } NS_IMETHODIMP SapiCallback::OnCancel() { // After cancel, mCurrentIndex may be updated.
--- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -120,16 +120,17 @@ PluginInstanceParent::PluginInstancePare : mParent(parent) , mSurrogate(PluginAsyncSurrogate::Cast(npp)) , mUseSurrogate(true) , mNPP(npp) , mNPNIface(npniface) , mIsWhitelistedForShumway(false) , mWindowType(NPWindowTypeWindow) , mDrawingModel(kDefaultDrawingModel) + , mLastRecordedDrawingModel(-1) , mFrameID(0) #if defined(OS_WIN) , mPluginHWND(nullptr) , mChildPluginHWND(nullptr) , mChildPluginsParentHWND(nullptr) , mPluginWndProc(nullptr) , mNestedEventState(false) #endif // defined(XP_WIN) @@ -804,16 +805,18 @@ PluginInstanceParent::SetCurrentImage(Im { MOZ_ASSERT(IsUsingDirectDrawing()); ImageContainer::NonOwningImage holder(aImage); holder.mFrameID = ++mFrameID; nsAutoTArray<ImageContainer::NonOwningImage,1> imageList; imageList.AppendElement(holder); mImageContainer->SetCurrentImages(imageList); + + RecordDrawingModel(); } bool PluginInstanceParent::RecvShowDirectDXGISurface(const WindowsHandle& handle, const gfx::IntRect& dirty) { #if defined(XP_WIN) RefPtr<D3D11SurfaceHolder> surface; @@ -968,16 +971,17 @@ PluginInstanceParent::RecvShow(const NPR } mFrontSurface = surface; RecvNPN_InvalidateRect(updatedRect); PLUGIN_LOG_DEBUG((" (RecvShow invalidated for surface %p)", mFrontSurface.get())); + RecordDrawingModel(); return true; } nsresult PluginInstanceParent::AsyncSetWindow(NPWindow* aWindow) { NPRemoteWindow window; mWindowType = aWindow->type; @@ -1375,16 +1379,17 @@ PluginInstanceParent::NPP_SetWindow(cons window.visualID = ws_info->visual ? ws_info->visual->visualid : 0; window.colormap = ws_info->colormap; #endif if (!CallNPP_SetWindow(window)) { return NPERR_GENERIC_ERROR; } + RecordDrawingModel(); return NPERR_NO_ERROR; } NPError PluginInstanceParent::NPP_GetValue(NPPVariable aVariable, void* _retval) { switch (aVariable) { @@ -2358,8 +2363,33 @@ PluginInstanceParent::Cast(NPP aInstance if (aSurrogate) { *aSurrogate = resolver->GetAsyncSurrogate(); } return instancePtr; } +void +PluginInstanceParent::RecordDrawingModel() +{ + int mode = -1; + switch (mWindowType) { + case NPWindowTypeWindow: + // We use 0=windowed since there is no specific NPDrawingModel value. + mode = 0; + break; + case NPWindowTypeDrawable: + mode = mDrawingModel + 1; + break; + default: + MOZ_ASSERT_UNREACHABLE("bad window type"); + return; + } + + if (mode == mLastRecordedDrawingModel) { + return; + } + MOZ_ASSERT(mode >= 0); + + Telemetry::Accumulate(Telemetry::PLUGIN_DRAWING_MODEL, mode); + mLastRecordedDrawingModel = mode; +}
--- a/dom/plugins/ipc/PluginInstanceParent.h +++ b/dom/plugins/ipc/PluginInstanceParent.h @@ -371,27 +371,35 @@ private: bool InternalGetValueForNPObject(NPNVariable aVariable, PPluginScriptableObjectParent** aValue, NPError* aResult); nsPluginInstanceOwner* GetOwner(); void SetCurrentImage(layers::Image* aImage); + // Update Telemetry with the current drawing model. + void RecordDrawingModel(); + private: PluginModuleParent* mParent; RefPtr<PluginAsyncSurrogate> mSurrogate; bool mUseSurrogate; NPP mNPP; const NPNetscapeFuncs* mNPNIface; nsCString mSrcAttribute; bool mIsWhitelistedForShumway; NPWindowType mWindowType; int16_t mDrawingModel; + // Since plugins may request different drawing models to find a compatible + // one, we only record the drawing model after a SetWindow call and if the + // drawing model has changed. + int mLastRecordedDrawingModel; + nsDataHashtable<nsPtrHashKey<NPObject>, PluginScriptableObjectParent*> mScriptableObjects; // This is used to tell the compositor that it should invalidate the ImageLayer. uint32_t mFrameID; #if defined(XP_WIN) // Note: DXGI 1.1 surface handles are global across all processes, and are not // marshaled. As long as we haven't freed a texture its handle should be valid
--- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -149,16 +149,19 @@ partial interface Document { // Gecko extensions? attribute EventHandler onwheel; attribute EventHandler oncopy; attribute EventHandler oncut; attribute EventHandler onpaste; attribute EventHandler onbeforescriptexecute; attribute EventHandler onafterscriptexecute; + [Pref="dom.select_events.enabled"] + attribute EventHandler onselectionchange; + /** * True if this document is synthetic : stand alone image, video, audio file, * etc. */ [Func="IsChromeOrXBL"] readonly attribute boolean mozSyntheticDocument; /** * Returns the script element whose script is currently being processed. *
--- a/editor/libeditor/nsEditor.cpp +++ b/editor/libeditor/nsEditor.cpp @@ -1982,24 +1982,24 @@ nsEditor::RestorePreservedSelection(Sele void nsEditor::StopPreservingSelection() { mRangeUpdater.DropSelectionState(mSavedSel); mSavedSel.MakeEmpty(); } void -nsEditor::EnsureComposition(mozilla::WidgetGUIEvent* aEvent) +nsEditor::EnsureComposition(mozilla::WidgetCompositionEvent* aCompositionEvent) { if (mComposition) { return; } // The compositionstart event must cause creating new TextComposition // instance at being dispatched by IMEStateManager. - mComposition = IMEStateManager::GetTextCompositionFor(aEvent); + mComposition = IMEStateManager::GetTextCompositionFor(aCompositionEvent); if (!mComposition) { MOZ_CRASH("IMEStateManager doesn't return proper composition"); } mComposition->StartHandlingComposition(this); } nsresult nsEditor::BeginIMEComposition(WidgetCompositionEvent* aCompositionEvent)
--- a/editor/libeditor/nsEditor.h +++ b/editor/libeditor/nsEditor.h @@ -408,17 +408,17 @@ protected: // regardless of DOM. Also, check to see if spell check should be skipped or not. return !IsPasswordEditor() && !IsReadonly() && !IsDisabled() && !ShouldSkipSpellCheck(); } /** * EnsureComposition() should be called by composition event handlers. This * tries to get the composition for the event and set it to mComposition. */ - void EnsureComposition(mozilla::WidgetGUIEvent* aEvent); + void EnsureComposition(mozilla::WidgetCompositionEvent* aCompositionEvent); nsresult GetSelection(int16_t aSelectionType, nsISelection** aSelection); public: /** All editor operations which alter the doc should be prefaced * with a call to StartOperation, naming the action and direction */ NS_IMETHOD StartOperation(EditAction opID,
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -2214,17 +2214,17 @@ void AsyncPanZoomController::HandlePanni if (fabs(aPanDistance.x) > breakThreshold || fabs(aPanDistance.y) > breakThreshold) { if (mState == PANNING_LOCKED_X) { if (!IsCloseToHorizontal(angle, gfxPrefs::APZAxisBreakoutAngle())) { mY.SetAxisLocked(false); SetState(PANNING); } } else if (mState == PANNING_LOCKED_Y) { - if (!IsCloseToVertical(angle, gfxPrefs::APZAxisLockAngle())) { + if (!IsCloseToVertical(angle, gfxPrefs::APZAxisBreakoutAngle())) { mX.SetAxisLocked(false); SetState(PANNING); } } } } }
--- a/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp +++ b/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp @@ -543,21 +543,22 @@ Pan(const RefPtr<InputReceiver>& aTarget MockContentControllerDelayed* aMcc, const ScreenPoint& aTouchStart, const ScreenPoint& aTouchEnd, bool aKeepFingerDown = false, nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr, nsEventStatus (*aOutEventStatuses)[4] = nullptr, uint64_t* aOutInputBlockId = nullptr) { - // Reduce the touch start tolerance to a tiny value. + // Reduce the touch start and move tolerance to a tiny value. // We can't use a scoped pref because this value might be read at some later // time when the events are actually processed, rather than when we deliver // them. gfxPrefs::SetAPZTouchStartTolerance(1.0f / 1000.0f); + gfxPrefs::SetAPZTouchMoveTolerance(0.0f); const int OVERCOME_TOUCH_TOLERANCE = 1; const TimeDuration TIME_BETWEEN_TOUCH_EVENT = TimeDuration::FromMilliseconds(50); // Even if the caller doesn't care about the block id, we need it to set the // allowed touch behaviour below, so make sure aOutInputBlockId is non-null. uint64_t blockId; if (!aOutInputBlockId) {
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -410,68 +410,74 @@ APZCCallbackHelper::GetRootContentDocume } context = context->GetToplevelContentDocumentPresContext(); if (!context) { return nullptr; } return context->PresShell(); } +static nsIPresShell* +GetRootDocumentPresShell(nsIContent* aContent) +{ + nsIDocument* doc = aContent->GetComposedDoc(); + if (!doc) { + return nullptr; + } + nsIPresShell* shell = doc->GetShell(); + if (!shell) { + return nullptr; + } + nsPresContext* context = shell->GetPresContext(); + if (!context) { + return nullptr; + } + context = context->GetRootPresContext(); + if (!context) { + return nullptr; + } + return context->PresShell(); +} + CSSPoint APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput, const ScrollableLayerGuid& aGuid) { CSSPoint input = aInput; if (aGuid.mScrollId == FrameMetrics::NULL_SCROLL_ID) { return input; } - nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aGuid.mScrollId); if (!content) { return input; } -#if !defined(MOZ_SINGLE_PROCESS_APZ) // First, scale inversely by the root content document's pres shell // resolution to cancel the scale-to-resolution transform that the // compositor adds to the layer with the pres shell resolution. The points // sent to Gecko by APZ don't have this transform unapplied (unlike other // compositor-side transforms) because APZ doesn't know about it. - if (nsIPresShell* shell = GetRootContentDocumentPresShellForContent(content)) { + if (nsIPresShell* shell = GetRootDocumentPresShell(content)) { input = input / shell->GetResolution(); } -#endif - // Apply the callback-transform. + // Now apply the callback-transform. // XXX: technically we need to walk all the way up the layer tree from the layer // represented by |aGuid.mScrollId| up to the root of the layer tree and apply // the input transforms at each level in turn. However, it is quite difficult // to do this given that the structure of the layer tree may be different from // the structure of the content tree. Also it may be impossible to do correctly // at this point because there are other CSS transforms and such interleaved in // between so applying the inputTransforms all in a row at the end may leave // some things transformed improperly. In practice we should rarely hit scenarios // where any of this matters, so I'm skipping it for now and just doing the single // transform for the layer that the input hit. - void* property = content->GetProperty(nsGkAtoms::apzCallbackTransform); if (property) { CSSPoint delta = (*static_cast<CSSPoint*>(property)); - -#if defined(MOZ_SINGLE_PROCESS_APZ) - // The delta is in root content document coordinate space while the - // aInput point is in root document coordinate space so convert the - // delta to root document space before adding it to the aInput point. - float resolution = 1.0f; - if (nsIPresShell* shell = GetRootContentDocumentPresShellForContent(content)) { - resolution = shell->GetResolution(); - } - delta.x = delta.x * resolution; - delta.y = delta.y * resolution; -#endif // MOZ_SINGLE_PROCESS_APZ input += delta; } return input; } LayoutDeviceIntPoint APZCCallbackHelper::ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint, const ScrollableLayerGuid& aGuid,
--- a/gfx/layers/apz/util/ChromeProcessController.cpp +++ b/gfx/layers/apz/util/ChromeProcessController.cpp @@ -142,26 +142,24 @@ ChromeProcessController::HandleDoubleTap } nsCOMPtr<nsIDocument> document = GetRootContentDocument(aGuid.mScrollId); if (!document.get()) { return; } CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid); -#if defined(MOZ_SINGLE_PROCESS_APZ) // CalculateRectToZoomTo performs a hit test on the frame associated with the // Root Content Document. Unfortunately that frame does not know about the // resolution of the document and so we must remove it before calculating // the zoomToRect. nsIPresShell* presShell = document->GetShell(); const float resolution = presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f; point.x = point.x / resolution; point.y = point.y / resolution; -#endif // MOZ_SINGLE_PROCESS_APZ CSSRect zoomToRect = CalculateRectToZoomTo(document, point); uint32_t presShellId; FrameMetrics::ViewID viewId; if (APZCCallbackHelper::GetOrCreateScrollIdentifiers( document->GetDocumentElement(), &presShellId, &viewId)) { mAPZCTreeManager->ZoomToRect( ScrollableLayerGuid(aGuid.mLayersId, presShellId, viewId), zoomToRect);
--- a/ipc/glue/Transport_posix.cpp +++ b/ipc/glue/Transport_posix.cpp @@ -4,16 +4,18 @@ /* 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 <unistd.h> #include <string> +#include "base/eintr_wrapper.h" + #include "chrome/common/child_process_info.h" #include "mozilla/ipc/Transport.h" #include "mozilla/ipc/FileDescriptor.h" using namespace std; using base::ProcessHandle; @@ -36,16 +38,18 @@ CreateTransport(base::ProcessId aProcIdO return NS_ERROR_TRANSPORT_INIT; } // The Transport closes these fds when it goes out of scope, so we // dup them here fd1 = dup(fd1); fd2 = dup(fd2); if (fd1 < 0 || fd2 < 0) { + HANDLE_EINTR(close(fd1)); + HANDLE_EINTR(close(fd2)); return NS_ERROR_DUPLICATE_HANDLE; } aOne->mFd = base::FileDescriptor(fd1, true/*close after sending*/); aTwo->mFd = base::FileDescriptor(fd2, true/*close after sending*/); return NS_OK; }
--- a/js/src/asmjs/AsmJSLink.cpp +++ b/js/src/asmjs/AsmJSLink.cpp @@ -548,17 +548,16 @@ DynamicallyLinkModule(JSContext* cx, con if (!ValidateGlobalVariable(cx, module, global, importVal)) return false; break; case AsmJSModule::Global::FFI: if (!ValidateFFI(cx, global, importVal, &ffis)) return false; break; case AsmJSModule::Global::ArrayView: - case AsmJSModule::Global::SharedArrayView: case AsmJSModule::Global::ArrayViewCtor: if (!ValidateArrayView(cx, global, globalVal)) return false; break; case AsmJSModule::Global::ByteLength: if (!ValidateByteLength(cx, globalVal)) return false; break;
--- a/js/src/asmjs/AsmJSModule.h +++ b/js/src/asmjs/AsmJSModule.h @@ -95,17 +95,17 @@ enum AsmJSSimdOperation // // NB: this means that AsmJSModule must be GC-safe. class AsmJSModule { public: class Global { public: - enum Which { Variable, FFI, ArrayView, ArrayViewCtor, SharedArrayView, MathBuiltinFunction, + enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction, AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation, ByteLength }; enum VarInitKind { InitConstant, InitImport }; enum ConstantKind { GlobalConstant, MathConstant }; private: struct Pod { Which which_; union { @@ -184,27 +184,23 @@ class AsmJSModule MOZ_ASSERT(pod.which_ == FFI); return pod.u.ffiIndex_; } // When a view is created from an imported constructor: // var I32 = stdlib.Int32Array; // var i32 = new I32(buffer); // the second import has nothing to validate and thus has a null field. PropertyName* maybeViewName() const { - MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == SharedArrayView || pod.which_ == ArrayViewCtor); + MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor); return name_; } Scalar::Type viewType() const { - MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == SharedArrayView || pod.which_ == ArrayViewCtor); + MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor); return pod.u.viewType_; } - void makeViewShared() { - MOZ_ASSERT(pod.which_ == ArrayView); - pod.which_ = SharedArrayView; - } PropertyName* mathName() const { MOZ_ASSERT(pod.which_ == MathBuiltinFunction); return name_; } PropertyName* atomicsName() const { MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction); return name_; } @@ -905,30 +901,28 @@ class AsmJSModule bool addFFI(PropertyName* field, uint32_t* ffiIndex) { MOZ_ASSERT(!isFinished()); if (pod.numFFIs_ == UINT32_MAX) return false; Global g(Global::FFI, field); g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++; return globals_.append(g); } - bool addArrayView(Scalar::Type vt, PropertyName* maybeField, bool isSharedView) { + bool addArrayView(Scalar::Type vt, PropertyName* maybeField) { MOZ_ASSERT(!isFinished()); - MOZ_ASSERT(!pod.hasArrayView_ || (pod.isSharedView_ == isSharedView)); pod.hasArrayView_ = true; - pod.isSharedView_ = isSharedView; + pod.isSharedView_ = false; Global g(Global::ArrayView, maybeField); g.pod.u.viewType_ = vt; return globals_.append(g); } - bool addArrayViewCtor(Scalar::Type vt, PropertyName* field, bool isSharedView) { + bool addArrayViewCtor(Scalar::Type vt, PropertyName* field) { MOZ_ASSERT(!isFinished()); MOZ_ASSERT(field); - MOZ_ASSERT(!pod.isSharedView_ || isSharedView); - pod.isSharedView_ = isSharedView; + pod.isSharedView_ = false; Global g(Global::ArrayViewCtor, field); g.pod.u.viewType_ = vt; return globals_.append(g); } bool addByteLength() { MOZ_ASSERT(!isFinished()); Global g(Global::ByteLength, nullptr); return globals_.append(g); @@ -973,29 +967,19 @@ class AsmJSModule return globals_.append(g); } unsigned numGlobals() const { return globals_.length(); } Global& global(unsigned i) { return globals_[i]; } - bool isValidViewSharedness(bool shared) const { - if (pod.hasArrayView_) - return pod.isSharedView_ == shared; - return !pod.isSharedView_ || shared; - } void setViewsAreShared() { if (pod.hasArrayView_) pod.isSharedView_ = true; - for (size_t i=0 ; i < globals_.length() ; i++) { - Global& g = globals_[i]; - if (g.which() == Global::ArrayView) - g.makeViewShared(); - } } /*************************************************************************/ // These functions are called while parsing/compiling function bodies: bool hasArrayView() const { return pod.hasArrayView_; }
--- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -1009,17 +1009,16 @@ class MOZ_STACK_CLASS ModuleValidator uint32_t globalDataOffset_; NumLit literalValue_; } varOrConst; uint32_t funcIndex_; uint32_t funcPtrTableIndex_; uint32_t ffiIndex_; struct { Scalar::Type viewType_; - bool isSharedView_; } viewInfo; AsmJSMathBuiltinFunction mathBuiltinFunc_; AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_; AsmJSSimdType simdCtorType_; struct { AsmJSSimdType type_; AsmJSSimdOperation which_; } simdOp; @@ -1067,24 +1066,16 @@ class MOZ_STACK_CLASS ModuleValidator } bool isAnyArrayView() const { return which_ == ArrayView || which_ == ArrayViewCtor; } Scalar::Type viewType() const { MOZ_ASSERT(isAnyArrayView()); return u.viewInfo.viewType_; } - bool viewIsSharedView() const { - MOZ_ASSERT(isAnyArrayView()); - return u.viewInfo.isSharedView_; - } - void setViewIsSharedView() { - MOZ_ASSERT(isAnyArrayView()); - u.viewInfo.isSharedView_ = true; - } bool isMathFunction() const { return which_ == MathBuiltinFunction; } AsmJSMathBuiltinFunction mathBuiltinFunction() const { MOZ_ASSERT(which_ == MathBuiltinFunction); return u.mathBuiltinFunc_; } bool isAtomicsFunction() const { @@ -1397,28 +1388,26 @@ class MOZ_STACK_CLASS ModuleValidator Global::Which which = isConst ? Global::ConstantImport : Global::Variable; Global* global = validationLifo_.new_<Global>(which); if (!global) return false; global->u.varOrConst.globalDataOffset_ = globalDataOffset; global->u.varOrConst.type_ = Type::var(importType).which(); return globals_.putNew(varName, global); } - bool addArrayView(PropertyName* varName, Scalar::Type vt, PropertyName* maybeField, - bool isSharedView) + bool addArrayView(PropertyName* varName, Scalar::Type vt, PropertyName* maybeField) { if (!arrayViews_.append(ArrayView(varName, vt))) return false; Global* global = validationLifo_.new_<Global>(Global::ArrayView); if (!global) return false; - if (!module().addArrayView(vt, maybeField, isSharedView)) + if (!module().addArrayView(vt, maybeField)) return false; global->u.viewInfo.viewType_ = vt; - global->u.viewInfo.isSharedView_ = isSharedView; return globals_.putNew(varName, global); } bool addMathBuiltinFunction(PropertyName* varName, AsmJSMathBuiltinFunction func, PropertyName* fieldName) { if (!module().addMathBuiltinFunction(func, fieldName)) return false; Global* global = validationLifo_.new_<Global>(Global::MathBuiltinFunction); @@ -1492,24 +1481,23 @@ class MOZ_STACK_CLASS ModuleValidator module().addChangeHeap(mask, min, max); Global* global = validationLifo_.new_<Global>(Global::ChangeHeap); if (!global) return false; global->u.changeHeap.srcBegin_ = fn->pn_pos.begin; global->u.changeHeap.srcEnd_ = fn->pn_pos.end; return globals_.putNew(name, global); } - bool addArrayViewCtor(PropertyName* varName, Scalar::Type vt, PropertyName* fieldName, bool isSharedView) { + bool addArrayViewCtor(PropertyName* varName, Scalar::Type vt, PropertyName* fieldName) { Global* global = validationLifo_.new_<Global>(Global::ArrayViewCtor); if (!global) return false; - if (!module().addArrayViewCtor(vt, fieldName, isSharedView)) + if (!module().addArrayViewCtor(vt, fieldName)) return false; global->u.viewInfo.viewType_ = vt; - global->u.viewInfo.isSharedView_ = isSharedView; return globals_.putNew(varName, global); } bool addFFI(PropertyName* varName, PropertyName* field) { Global* global = validationLifo_.new_<Global>(Global::FFI); if (!global) return false; uint32_t index; if (!module().addFFI(field, &index)) @@ -1607,16 +1595,20 @@ class MOZ_STACK_CLASS ModuleValidator } bool tryRequireHeapLengthToBeAtLeast(uint32_t len) { return module().tryRequireHeapLengthToBeAtLeast(len); } uint32_t minHeapLength() const { return module().minHeapLength(); } + bool usesSharedMemory() const { + return atomicsPresent_; + } + // Error handling. bool hasAlreadyFailed() const { return !!errorString_; } bool failOffset(uint32_t offset, const char* str) { MOZ_ASSERT(!hasAlreadyFailed()); MOZ_ASSERT(errorOffset_ == UINT32_MAX); @@ -1735,24 +1727,18 @@ class MOZ_STACK_CLASS ModuleValidator if (SimdOperationNameMap::Ptr p = standardLibrarySimdOpNames_.lookup(name)) { *op = p->value(); return true; } return false; } void startFunctionBodies() { - if (atomicsPresent_) { - for (GlobalMap::Range r = globals_.all() ; !r.empty() ; r.popFront()) { - Global* g = r.front().value(); - if (g->isAnyArrayView()) - g->setViewIsSharedView(); - } + if (atomicsPresent_) module().setViewsAreShared(); - } } }; } // namespace /*****************************************************************************/ // Numeric literal utilities @@ -2467,20 +2453,19 @@ CheckGlobalVariableInitImport(ModuleVali ValType coerceTo; ParseNode* coercedExpr; if (!CheckTypeAnnotation(m, initNode, &coerceTo, &coercedExpr)) return false; return CheckGlobalVariableImportExpr(m, varName, coerceTo, coercedExpr, isConst); } static bool -IsArrayViewCtorName(ModuleValidator& m, PropertyName* name, Scalar::Type* type, bool* shared) +IsArrayViewCtorName(ModuleValidator& m, PropertyName* name, Scalar::Type* type) { JSAtomState& names = m.cx()->names(); - *shared = false; if (name == names.Int8Array) { *type = Scalar::Int8; } else if (name == names.Uint8Array) { *type = Scalar::Uint8; } else if (name == names.Int16Array) { *type = Scalar::Int16; } else if (name == names.Uint16Array) { *type = Scalar::Uint16; @@ -2521,50 +2506,45 @@ CheckNewArrayView(ModuleValidator& m, Pr PropertyName* bufferName = m.module().bufferArgumentName(); if (!bufferName) return m.fail(newExpr, "cannot create array view without an asm.js heap parameter"); ParseNode* ctorExpr = ListHead(newExpr); PropertyName* field; Scalar::Type type; - bool shared = false; if (ctorExpr->isKind(PNK_DOT)) { ParseNode* base = DotBase(ctorExpr); if (!IsUseOfName(base, globalName)) return m.failName(base, "expecting '%s.*Array", globalName); field = DotMember(ctorExpr); - if (!IsArrayViewCtorName(m, field, &type, &shared)) + if (!IsArrayViewCtorName(m, field, &type)) return m.fail(ctorExpr, "could not match typed array name"); } else { if (!ctorExpr->isKind(PNK_NAME)) return m.fail(ctorExpr, "expecting name of imported array view constructor"); PropertyName* globalName = ctorExpr->name(); const ModuleValidator::Global* global = m.lookupGlobal(globalName); if (!global) return m.failName(ctorExpr, "%s not found in module global scope", globalName); if (global->which() != ModuleValidator::Global::ArrayViewCtor) return m.failName(ctorExpr, "%s must be an imported array view constructor", globalName); field = nullptr; type = global->viewType(); - shared = global->viewIsSharedView(); } if (!CheckNewArrayViewArgs(m, ctorExpr, bufferName)) return false; - if (!m.module().isValidViewSharedness(shared)) - return m.failName(ctorExpr, "%s has different sharedness than previous view constructors", globalName); - - return m.addArrayView(varName, type, field, shared); + return m.addArrayView(varName, type, field); } static bool IsSimdTypeName(ModuleValidator& m, PropertyName* name, AsmJSSimdType* type) { if (name == m.cx()->names().int32x4) { *type = AsmJSSimdType_int32x4; return true; @@ -2690,22 +2670,18 @@ CheckGlobalDotImport(ModuleValidator& m, if (field == m.cx()->names().NaN) return m.addGlobalConstant(varName, GenericNaN(), field); if (field == m.cx()->names().Infinity) return m.addGlobalConstant(varName, PositiveInfinity<double>(), field); if (field == m.cx()->names().byteLength) return m.addByteLength(varName); Scalar::Type type; - bool shared = false; - if (IsArrayViewCtorName(m, field, &type, &shared)) { - if (!m.module().isValidViewSharedness(shared)) - return m.failName(initNode, "'%s' has different sharedness than previous view constructors", field); - return m.addArrayViewCtor(varName, type, field, shared); - } + if (IsArrayViewCtorName(m, field, &type)) + return m.addArrayViewCtor(varName, type, field); return m.failName(initNode, "'%s' is not a standard constant or typed array name", field); } if (base->name() == m.module().importArgumentName()) return m.addFFI(varName, field); const ModuleValidator::Global* global = m.lookupGlobal(base->name()); @@ -6782,16 +6758,22 @@ CheckModule(ExclusiveContext* cx, AsmJSP if (!CheckModuleProcessingDirectives(m)) return false; if (!CheckModuleGlobals(m)) return false; m.startFunctionBodies(); +#if !defined(ENABLE_SHARED_ARRAY_BUFFER) + if (m.usesSharedMemory()) + return m.failOffset(m.parser().tokenStream.currentToken().pos.begin, + "shared memory and atomics not supported by this build"); +#endif + if (!CheckFunctions(m)) return false; if (!CheckFuncPtrTables(m)) return false; if (!CheckModuleReturn(m)) return false;
--- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -99,17 +99,18 @@ class FunctionCompiler return false; for (ABIArgIter<LifoSig::ArgVector> i(args); !i.done(); i++) { MAsmJSParameter* ins = MAsmJSParameter::New(alloc(), *i, i.mirType()); curBlock_->add(ins); curBlock_->initSlot(info().localSlot(i.index()), ins); if (!mirGen_.ensureBallast()) return false; - localTypes_.append(args[i.index()]); + if (!localTypes_.append(args[i.index()])) + return false; } for (unsigned i = 0; i < func_.numVarInits(); i++) { Val v = func_.varInit(i); MInstruction* ins = nullptr; switch (v.type()) { case ValType::I32: ins = MConstant::NewAsmJS(alloc(), Int32Value(v.i32()), MIRType_Int32); @@ -129,17 +130,18 @@ class FunctionCompiler ins = MSimdConstant::New(alloc(), SimdConstant::CreateX4(v.f32x4()), MIRType_Float32x4); break; } curBlock_->add(ins); curBlock_->initSlot(info().localSlot(firstVarSlot + i), ins); if (!mirGen_.ensureBallast()) return false; - localTypes_.append(v.type()); + if (!localTypes_.append(v.type())) + return false; } return true; } void checkPostconditions() { MOZ_ASSERT(loopStack_.empty()); @@ -1124,22 +1126,30 @@ class FunctionCompiler } bool joinSwitch(MBasicBlock* switchBlock, const BlockVector& cases, MBasicBlock* defaultBlock) { size_t pos = breakableStack_.popCopy(); if (!switchBlock) return true; MTableSwitch* mir = switchBlock->lastIns()->toTableSwitch(); - size_t defaultIndex = mir->addDefault(defaultBlock); + size_t defaultIndex; + if (!mir->addDefault(defaultBlock, &defaultIndex)) + return false; for (unsigned i = 0; i < cases.length(); i++) { - if (!cases[i]) - mir->addCase(defaultIndex); - else - mir->addCase(mir->addSuccessor(cases[i])); + if (!cases[i]) { + if (!mir->addCase(defaultIndex)) + return false; + } else { + size_t caseIndex; + if (!mir->addSuccessor(cases[i], &caseIndex)) + return false; + if (!mir->addCase(caseIndex)) + return false; + } } if (curBlock_) { MBasicBlock* next; if (!newBlock(curBlock_, &next)) return false; curBlock_->end(MGoto::New(alloc(), next)); curBlock_ = next; }
--- a/js/src/builtin/Eval.cpp +++ b/js/src/builtin/Eval.cpp @@ -233,22 +233,16 @@ EvalKernel(JSContext* cx, const CallArgs AssertInnerizedScopeChain(cx, *scopeobj); Rooted<GlobalObject*> scopeObjGlobal(cx, &scopeobj->global()); if (!GlobalObject::isRuntimeCodeGenEnabled(cx, scopeObjGlobal)) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL); return false; } - if (evalType == DIRECT_EVAL && caller.script()->isDerivedClassConstructor()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DISABLED_DERIVED_CLASS, - "direct eval"); - return false; - } - // ES5 15.1.2.1 step 1. if (args.length() < 1) { args.rval().setUndefined(); return true; } if (!args[0].isString()) { args.rval().set(args[0]); return true;
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2778,69 +2778,73 @@ BytecodeEmitter::emitNameIncDec(ParseNod return false; if (post && !emit1(JSOP_POP)) // RESULT return false; return true; } bool -BytecodeEmitter::emitElemOperands(ParseNode* pn, JSOp op) +BytecodeEmitter::emitElemOperands(ParseNode* pn, EmitElemOption opts) { MOZ_ASSERT(pn->isArity(PN_BINARY)); if (!emitTree(pn->pn_left)) return false; - if (op == JSOP_CALLELEM && !emit1(JSOP_DUP)) - return false; + if (opts == EmitElemOption::IncDec) { + if (!emit1(JSOP_CHECKOBJCOERCIBLE)) + return false; + } else if (opts == EmitElemOption::Call) { + if (!emit1(JSOP_DUP)) + return false; + } if (!emitTree(pn->pn_right)) return false; - bool isSetElem = op == JSOP_SETELEM || op == JSOP_STRICTSETELEM; - if (isSetElem && !emit2(JSOP_PICK, 2)) - return false; - return true; -} - -bool -BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts) + if (opts == EmitElemOption::Set && !emit2(JSOP_PICK, 2)) + return false; + return true; +} + +bool +BytecodeEmitter::emitSuperElemOperands(ParseNode* pn, EmitElemOption opts) { MOZ_ASSERT(pn->isKind(PNK_ELEM) && pn->as<PropertyByValue>().isSuper()); // The ordering here is somewhat screwy. We need to evaluate the propval // first, by spec. Do a little dance to not emit more than one JSOP_THIS. // Since JSOP_THIS might throw in derived class constructors, we cannot // just push it earlier as the receiver. We have to swap it down instead. if (!emitTree(pn->pn_right)) return false; // We need to convert the key to an object id first, so that we do not do // it inside both the GETELEM and the SETELEM. - if (opts == SuperElem_IncDec && !emit1(JSOP_TOID)) + if (opts == EmitElemOption::IncDec && !emit1(JSOP_TOID)) return false; if (!emitGetThisForSuperBase(pn->pn_left)) return false; - if (opts == SuperElem_Call) { + if (opts == EmitElemOption::Call) { if (!emit1(JSOP_SWAP)) return false; // We need another |this| on top, also if (!emitDupAt(1)) return false; } if (!emit1(JSOP_SUPERBASE)) return false; - if (opts == SuperElem_Set && !emit2(JSOP_PICK, 3)) + if (opts == EmitElemOption::Set && !emit2(JSOP_PICK, 3)) return false; return true; } bool BytecodeEmitter::emitElemOpBase(JSOp op) { @@ -2849,27 +2853,33 @@ BytecodeEmitter::emitElemOpBase(JSOp op) checkTypeSet(op); return true; } bool BytecodeEmitter::emitElemOp(ParseNode* pn, JSOp op) { - return emitElemOperands(pn, op) && emitElemOpBase(op); + EmitElemOption opts = EmitElemOption::Get; + if (op == JSOP_CALLELEM) + opts = EmitElemOption::Call; + else if (op == JSOP_SETELEM || op == JSOP_STRICTSETELEM) + opts = EmitElemOption::Set; + + return emitElemOperands(pn, opts) && emitElemOpBase(op); } bool BytecodeEmitter::emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall) { - SuperElemOptions opts = SuperElem_Get; + EmitElemOption opts = EmitElemOption::Get; if (isCall) - opts = SuperElem_Call; + opts = EmitElemOption::Call; else if (op == JSOP_SETELEM_SUPER || op == JSOP_STRICTSETELEM_SUPER) - opts = SuperElem_Set; + opts = EmitElemOption::Set; if (!emitSuperElemOperands(pn, opts)) return false; if (!emitElemOpBase(op)) return false; if (isCall && !emit1(JSOP_SWAP)) return false; @@ -2880,20 +2890,20 @@ BytecodeEmitter::emitSuperElemOp(ParseNo bool BytecodeEmitter::emitElemIncDec(ParseNode* pn) { MOZ_ASSERT(pn->pn_kid->isKind(PNK_ELEM)); bool isSuper = pn->pn_kid->as<PropertyByValue>().isSuper(); if (isSuper) { - if (!emitSuperElemOperands(pn->pn_kid, SuperElem_IncDec)) + if (!emitSuperElemOperands(pn->pn_kid, EmitElemOption::IncDec)) return false; } else { - if (!emitElemOperands(pn->pn_kid, JSOP_GETELEM)) + if (!emitElemOperands(pn->pn_kid, EmitElemOption::IncDec)) return false; } bool post; JSOp binop = GetIncDecInfo(pn->getKind(), &post); JSOp getOp; if (isSuper) { @@ -3460,20 +3470,44 @@ BytecodeEmitter::emitSetThis(ParseNode* if (!emitTree(pn->pn_right)) return false; if (!bindNameToSlot(name)) return false; JSOp setOp = name->getOp(); + // Handle the eval case. Only accept the strict variant, as eval in a + // derived class constructor must be strict. + if (setOp == JSOP_STRICTSETNAME) { + if (!emitAtomOp(name, JSOP_GETNAME)) + return false; + if (!emit1(JSOP_CHECKTHISREINIT)) + return false; + if (!emit1(JSOP_POP)) + return false; + + if (!emitAtomOp(name, JSOP_BINDNAME)) + return false; + if (!emit1(JSOP_SWAP)) + return false; + + return emitAtomOp(name, setOp); + } + JSOp getOp; switch (setOp) { - case JSOP_SETLOCAL: getOp = JSOP_GETLOCAL; break; - case JSOP_SETALIASEDVAR: getOp = JSOP_GETALIASEDVAR; break; + case JSOP_SETLOCAL: + getOp = JSOP_GETLOCAL; + setOp = JSOP_INITLEXICAL; + break; + case JSOP_SETALIASEDVAR: + getOp = JSOP_GETALIASEDVAR; + setOp = JSOP_INITALIASEDLEXICAL; + break; default: MOZ_CRASH("Unexpected op"); } // First, get the original |this| and throw if we already initialized it. if (!emitVarOp(name, getOp)) return false; if (!emit1(JSOP_CHECKTHISREINIT)) return false; @@ -4374,23 +4408,35 @@ BytecodeEmitter::emitVariables(ParseNode } else if (binding->isKind(PNK_ASSIGN)) { /* * A destructuring initialiser assignment preceded by var will * never occur to the left of 'in' in a for-in loop. As with 'for * (var x = i in o)...', this will cause the entire 'var [a, b] = * i' to be hoisted out of the loop. */ MOZ_ASSERT(binding->isOp(JSOP_NOP)); - MOZ_ASSERT(emitOption != DefineVars && emitOption != AnnexB); - - /* - * To allow the front end to rewrite var f = x; as f = x; when a - * function f(){} precedes the var, detect simple name assignment - * here and initialize the name. - */ + MOZ_ASSERT(emitOption != DefineVars); + MOZ_ASSERT_IF(emitOption == AnnexB, binding->pn_left->isKind(PNK_NAME)); + + // To allow the front end to rewrite |var f = x;| as |f = x;| when a + // |function f(){}| precedes the var, detect simple name assignment + // here and initialize the name. + // + // There is a corner case where a function declaration synthesizes + // an Annex B declaration, which in turn gets rewritten later as a + // simple assignment due to hoisted function declaration of the + // same name. For example, + // + // { + // // Synthesizes an Annex B declaration because no 'f' binding + // // yet exists. This later gets rewritten as an assignment when + // // the outer function 'f' gets hoisted. + // function f() {} + // } + // function f() {} if (binding->pn_left->isKind(PNK_NAME)) { if (!emitSingleVariable(pn, binding->pn_left, binding->pn_right, emitOption)) return false; } else { ParseNode* initializer = binding->pn_left; if (!emitDestructuringDecls(pn->getOp(), initializer)) return false;
--- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -514,17 +514,18 @@ struct BytecodeEmitter bool emitPropOp(ParseNode* pn, JSOp op); bool emitPropIncDec(ParseNode* pn); bool emitComputedPropertyName(ParseNode* computedPropName); // Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM // opcode onto the stack in the right order. In the case of SETELEM, the // value to be assigned must already be pushed. - bool emitElemOperands(ParseNode* pn, JSOp op); + enum class EmitElemOption { Get, Set, Call, IncDec }; + bool emitElemOperands(ParseNode* pn, EmitElemOption opts); bool emitElemOpBase(JSOp op); bool emitElemOp(ParseNode* pn, JSOp op); bool emitElemIncDec(ParseNode* pn); bool emitCatch(ParseNode* pn); bool emitIf(ParseNode* pn); bool emitWith(ParseNode* pn); @@ -649,17 +650,16 @@ struct BytecodeEmitter // // Please refer the comment above emitSpread for additional information about // stack convention. bool emitForOf(StmtType type, ParseNode* pn); bool emitClass(ParseNode* pn); bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false); bool emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall = false); - enum SuperElemOptions { SuperElem_Get, SuperElem_Set, SuperElem_Call, SuperElem_IncDec }; - bool emitSuperElemOperands(ParseNode* pn, SuperElemOptions opts = SuperElem_Get); + bool emitSuperElemOperands(ParseNode* pn, EmitElemOption opts = EmitElemOption::Get); bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false); }; } /* namespace frontend */ } /* namespace js */ #endif /* frontend_BytecodeEmitter_h */
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -120,18 +120,22 @@ MarkUsesAsHoistedLexical(ParseNode* pn) void SharedContext::computeAllowSyntax(JSObject* staticScope) { for (StaticScopeIter<CanGC> it(context, staticScope); !it.done(); it++) { if (it.type() == StaticScopeIter<CanGC>::Function && !it.fun().isArrow()) { // Any function supports new.target. allowNewTarget_ = true; allowSuperProperty_ = it.fun().allowSuperProperty(); - if (it.maybeFunctionBox()) + if (it.maybeFunctionBox()) { superScopeAlreadyNeedsHomeObject_ = it.maybeFunctionBox()->needsHomeObject(); + allowSuperCall_ = it.maybeFunctionBox()->isDerivedClassConstructor(); + } else { + allowSuperCall_ = it.fun().isDerivedClassConstructor(); + } break; } } } void SharedContext::computeThisBinding(JSObject* staticScope) { @@ -7465,21 +7469,16 @@ Parser<ParseHandler>::assignExpr(InHandl tokenStream.seek(start); if (!abortIfSyntaxParser()) return null(); TokenKind ignored; if (!tokenStream.peekToken(&ignored, TokenStream::Operand)) return null(); - if (pc->sc->isFunctionBox() && pc->sc->asFunctionBox()->isDerivedClassConstructor()) { - report(ParseError, false, null(), JSMSG_DISABLED_DERIVED_CLASS, "arrow functions"); - return null(); - } - Node arrowFunc = functionDef(inHandling, yieldHandling, nullptr, Arrow, NotGenerator); if (!arrowFunc) return null(); if (isBlock) { // This arrow function could be a non-trailing member of a comma // expression or a semicolon terminating a full expression. If so, // the next token is that comma/semicolon, gotten with None: @@ -8885,17 +8884,17 @@ Parser<ParseHandler>::memberExpr(YieldHa nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end); if (!nextMember) return null(); } else if ((allowCallSyntax && tt == TOK_LP) || tt == TOK_TEMPLATE_HEAD || tt == TOK_NO_SUBS_TEMPLATE) { if (handler.isSuperBase(lhs)) { - if (!pc->sc->isFunctionBox() || !pc->sc->asFunctionBox()->isDerivedClassConstructor()) { + if (!pc->sc->allowSuperCall()) { report(ParseError, false, null(), JSMSG_BAD_SUPERCALL); return null(); } if (tt != TOK_LP) { report(ParseError, false, null(), JSMSG_BAD_SUPER); return null(); }
--- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -197,31 +197,33 @@ class SharedContext bool localStrict; bool extraWarnings; private: ThisBinding thisBinding_; bool allowNewTarget_; bool allowSuperProperty_; + bool allowSuperCall_; bool inWith_; bool needsThisTDZChecks_; bool superScopeAlreadyNeedsHomeObject_; public: SharedContext(ExclusiveContext* cx, Directives directives, bool extraWarnings) : context(cx), anyCxFlags(), strictScript(directives.strict()), localStrict(false), extraWarnings(extraWarnings), thisBinding_(ThisBinding::Global), allowNewTarget_(false), allowSuperProperty_(false), + allowSuperCall_(false), inWith_(false), needsThisTDZChecks_(false), superScopeAlreadyNeedsHomeObject_(false) { } // The unfortunate reason that staticScope() is a virtual is because // GlobalSharedContext and FunctionBox have different lifetimes. // GlobalSharedContexts are stack allocated and thus may use RootedObject @@ -239,16 +241,17 @@ class SharedContext bool isModuleBox() { return isObjectBox() && toObjectBox()->isModuleBox(); } inline ModuleBox* asModuleBox(); bool isGlobalContext() { return !toObjectBox(); } ThisBinding thisBinding() const { return thisBinding_; } bool allowNewTarget() const { return allowNewTarget_; } bool allowSuperProperty() const { return allowSuperProperty_; } + bool allowSuperCall() const { return allowSuperCall_; } bool inWith() const { return inWith_; } bool needsThisTDZChecks() const { return needsThisTDZChecks_; } void markSuperScopeNeedsHomeObject(); bool hasExplicitUseStrict() const { return anyCxFlags.hasExplicitUseStrict; } bool bindingsAccessedDynamically() const { return anyCxFlags.bindingsAccessedDynamically; } bool hasDebuggerStatement() const { return anyCxFlags.hasDebuggerStatement; }
--- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -872,23 +872,25 @@ TokenStream::getDirective(bool isMultili getChar(); // Debugging directives can occur in both single- and multi-line // comments. If we're currently inside a multi-line comment, we also // need to recognize multi-line comment terminators. if (isMultiline && c == '*' && peekChar() == '/') { ungetChar('*'); break; } - tokenbuf.append(c); + if (!tokenbuf.append(c)) + return false; } - if (tokenbuf.empty()) + if (tokenbuf.empty()) { // The directive's URL was missing, but this is not quite an // exception that we should stop and drop everything for. return true; + } size_t length = tokenbuf.length(); *destination = cx->make_pod_array<char16_t>(length + 1); if (!*destination) return false; PodCopy(destination->get(), tokenbuf.begin(), length);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/gating.js @@ -0,0 +1,33 @@ +// Check gating of shared memory features in asm.js (bug 1171540, +// bug 1231624). +// +// In asm.js, importing any atomic is a signal that shared memory is +// being used. If an atomic is imported, and if shared memory is +// disabled in the build or in the run then a type error should be +// signaled for the module at the end of the declaration section and +// the module should not be an asm.js module. + +if (!this.SharedArrayBuffer || !isAsmJSCompilationAvailable()) + quit(0); + +// This code is not run, we only care whether it compiles as asm.js. + +function module_a(stdlib, foreign, heap) { + "use asm"; + + var i32a = new stdlib.Int32Array(heap); + var ld = stdlib.Atomics.load; + + // There should be a type error around this line if shared memory + // is not enabled. + + function do_load() { + var v = 0; + v = ld(i32a, 0)|0; // It's not actually necessary to use the atomic op + return v|0; + } + + return { load: do_load }; +} + +assertEq(isAsmJSModule(module_a), !!this.SharedArrayBuffer);
--- a/js/src/jit-test/tests/basic/bug984766.js +++ b/js/src/jit-test/tests/basic/bug984766.js @@ -1,15 +1,17 @@ for (var i = 0; i < 10; i++) { x = new ArrayBuffer(4) x.f = (function() {}) new Uint16Array(x).set(JSON.parse) gcslice() } +if (!this.SharedArrayBuffer) + quit(0); for (var i = 0; i < 10; i++) { x = new SharedArrayBuffer(4) x.f = (function() {}) new Uint16Array(x).set(JSON.parse) gcslice() }
--- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -3583,49 +3583,71 @@ BaselineCompiler::emit_JSOP_RETRVAL() masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), &done); masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand); masm.bind(&done); } return emitReturn(); } -typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue, HandleValue, - MutableHandleValue); +typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue, MutableHandleValue); static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(js::ToIdOperation); bool BaselineCompiler::emit_JSOP_TOID() { // Load index in R0, but keep values on the stack for the decompiler. frame.syncStack(0); masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); // No-op if index is int32. Label done; masm.branchTestInt32(Assembler::Equal, R0, &done); prepareVMCall(); - masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R1); - pushArg(R0); - pushArg(R1); pushArg(ImmPtr(pc)); pushArg(ImmGCPtr(script)); if (!callVM(ToIdInfo)) return false; masm.bind(&done); frame.pop(); // Pop index. frame.push(R0); return true; } +typedef bool (*ThrowObjectCoercibleFn)(JSContext*, HandleValue); +static const VMFunction ThrowObjectCoercibleInfo = FunctionInfo<ThrowObjectCoercibleFn>(ThrowObjectCoercible); + +bool +BaselineCompiler::emit_JSOP_CHECKOBJCOERCIBLE() +{ + frame.syncStack(0); + masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); + + Label fail, done; + + masm.branchTestUndefined(Assembler::Equal, R0, &fail); + masm.branchTestNull(Assembler::NotEqual, R0, &done); + + masm.bind(&fail); + prepareVMCall(); + + pushArg(R0); + + if (!callVM(ThrowObjectCoercibleInfo)) + return false; + + masm.bind(&done); + return true; +} + typedef JSString* (*ToStringFn)(JSContext*, HandleValue); static const VMFunction ToStringInfo = FunctionInfo<ToStringFn>(ToStringSlow); bool BaselineCompiler::emit_JSOP_TOSTRING() { // Keep top stack value in R0. frame.popRegsAndSync(1);
--- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -214,17 +214,18 @@ namespace jit { _(JSOP_SUPERCALL) \ _(JSOP_SPREADSUPERCALL) \ _(JSOP_THROWSETCONST) \ _(JSOP_THROWSETALIASEDCONST) \ _(JSOP_INITHIDDENPROP_GETTER) \ _(JSOP_INITHIDDENPROP_SETTER) \ _(JSOP_INITHIDDENELEM) \ _(JSOP_INITHIDDENELEM_GETTER) \ - _(JSOP_INITHIDDENELEM_SETTER) + _(JSOP_INITHIDDENELEM_SETTER) \ + _(JSOP_CHECKOBJCOERCIBLE) class BaselineCompiler : public BaselineCompilerSpecific { FixedList<Label> labels_; NonAssertingLabel return_; NonAssertingLabel postBarrierSlot_; // Native code offset right before the scope chain is initialized.
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -2764,16 +2764,21 @@ DoSetElemFallback(JSContext* cx, Baselin } else if (op == JSOP_INITELEM_INC) { if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs)) return false; } else { if (!SetObjectElement(cx, obj, index, rhs, JSOp(*pc) == JSOP_STRICTSETELEM, script, pc)) return false; } + // Don't try to attach stubs that wish to be hidden. We don't know how to + // have different enumerability in the stubs for the moment. + if (op == JSOP_INITHIDDENELEM) + return true; + // Overwrite the object on the stack (pushed for the decompiler) with the rhs. MOZ_ASSERT(stack[2] == objv); stack[2] = rhs; // Check if debug mode toggling made the stub invalid. if (stub.invalid()) return true;
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -9056,32 +9056,31 @@ CodeGenerator::visitOutOfLineTypeOfV(Out masm.passABIArg(output); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::TypeOfObjectOperation)); masm.storeCallResult(output); restoreVolatile(output); masm.jump(ool->rejoin()); } -typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue, HandleValue, +typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue, MutableHandleValue); static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(ToIdOperation); void CodeGenerator::visitToIdV(LToIdV* lir) { Label notInt32; FloatRegister temp = ToFloatRegister(lir->tempFloat()); const ValueOperand out = ToOutValue(lir); ValueOperand index = ToValue(lir, LToIdV::Index); OutOfLineCode* ool = oolCallVM(ToIdInfo, lir, ArgList(ImmGCPtr(current->mir()->info().script()), ImmPtr(lir->mir()->resumePoint()->pc()), - ToValue(lir, LToIdV::Object), ToValue(lir, LToIdV::Index)), StoreValueTo(out)); Register tag = masm.splitTagForTest(index); masm.branchTestInt32(Assembler::NotEqual, tag, ¬Int32); masm.moveValue(index, out); masm.jump(ool->rejoin()); @@ -10364,16 +10363,32 @@ CodeGenerator::visitCheckReturn(LCheckRe Label bail, noChecks; masm.branchTestObject(Assembler::Equal, returnValue, &noChecks); masm.branchTestUndefined(Assembler::NotEqual, returnValue, &bail); masm.branchTestMagicValue(Assembler::Equal, thisValue, JS_UNINITIALIZED_LEXICAL, &bail); bailoutFrom(&bail, ins->snapshot()); masm.bind(&noChecks); } +typedef bool (*ThrowObjCoercibleFn)(JSContext*, HandleValue); +static const VMFunction ThrowObjectCoercibleInfo = FunctionInfo<ThrowObjCoercibleFn>(ThrowObjectCoercible); + +void +CodeGenerator::visitCheckObjCoercible(LCheckObjCoercible* ins) +{ + ValueOperand checkValue = ToValue(ins, LCheckObjCoercible::CheckValue); + Label fail, done; + masm.branchTestNull(Assembler::Equal, checkValue, &fail); + masm.branchTestUndefined(Assembler::NotEqual, checkValue, &done); + masm.bind(&fail); + pushArg(checkValue); + callVM(ThrowObjectCoercibleInfo, ins); + masm.bind(&done); +} + void CodeGenerator::visitRandom(LRandom* ins) { using mozilla::non_crypto::XorShift128PlusRNG; FloatRegister output = ToFloatRegister(ins->output()); Register tempReg = ToRegister(ins->temp0());
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -340,16 +340,17 @@ class CodeGenerator : public CodeGenerat void visitAsmJSVoidReturn(LAsmJSVoidReturn* ret); void visitLexicalCheck(LLexicalCheck* ins); void visitThrowRuntimeLexicalError(LThrowRuntimeLexicalError* ins); void visitGlobalNameConflictsCheck(LGlobalNameConflictsCheck* ins); void visitDebugger(LDebugger* ins); void visitNewTarget(LNewTarget* ins); void visitArrowNewTarget(LArrowNewTarget* ins); void visitCheckReturn(LCheckReturn* ins); + void visitCheckObjCoercible(LCheckObjCoercible* ins); void visitCheckOverRecursed(LCheckOverRecursed* lir); void visitCheckOverRecursedFailure(CheckOverRecursedFailure* ool); void visitInterruptCheckImplicit(LInterruptCheckImplicit* ins); void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit* ins); void visitUnboxFloatingPoint(LUnboxFloatingPoint* lir);
--- a/js/src/jit/ExecutableAllocator.h +++ b/js/src/jit/ExecutableAllocator.h @@ -349,19 +349,20 @@ class ExecutableAllocator // Create a new allocator ExecutablePool* pool = createPool(largeAllocSize); if (!pool) return nullptr; // At this point, local |pool| is the owner. if (m_smallPools.length() < maxSmallPools) { - // We haven't hit the maximum number of live pools; add the new pool. - m_smallPools.append(pool); - pool->addRef(); + // We haven't hit the maximum number of live pools; add the new pool. + // If append() OOMs, we just return an unshared allocator. + if (m_smallPools.append(pool)) + pool->addRef(); } else { // Find the pool with the least space. int iMin = 0; for (size_t i = 1; i < m_smallPools.length(); i++) { if (m_smallPools[i]->available() < m_smallPools[iMin]->available()) { iMin = i;
--- a/js/src/jit/IonAnalysis.h +++ b/js/src/jit/IonAnalysis.h @@ -129,17 +129,19 @@ class LinearSum constant_(0) { } LinearSum(const LinearSum& other) : terms_(other.terms_.allocPolicy()), constant_(other.constant_) { - terms_.appendAll(other.terms_); + AutoEnterOOMUnsafeRegion oomUnsafe; + if (!terms_.appendAll(other.terms_)) + oomUnsafe.crash("LinearSum::LinearSum"); } // These return false on an integer overflow, and afterwards the sum must // not be used. bool multiply(int32_t scale); bool add(const LinearSum& other, int32_t scale = 1); bool add(SimpleLinearSum other, int32_t scale = 1); bool add(MDefinition* term, int32_t scale);
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -610,17 +610,18 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl // Update the most recent header for this loop encountered, in case // new types flow to the phis and the loop is processed at least // three times. loopHeaders_[i].header = entry; return true; } } - loopHeaders_.append(LoopHeader(start, entry)); + if (!loopHeaders_.append(LoopHeader(start, entry))) + return false; jsbytecode* last = nullptr; jsbytecode* earlier = nullptr; for (jsbytecode* pc = start; pc != end; earlier = last, last = pc, pc += GetBytecodeLength(pc)) { uint32_t slot; if (*pc == JSOP_SETLOCAL) slot = info().localSlot(GET_LOCALNO(pc)); else if (*pc == JSOP_SETARG) @@ -2093,16 +2094,19 @@ IonBuilder::inspectOpcode(JSOp op) return pushConstant(UndefinedValue()); // Just fall through to the unsupported bytecode case. break; case JSOP_NEWTARGET: return jsop_newtarget(); + case JSOP_CHECKOBJCOERCIBLE: + return jsop_checkobjcoercible(); + #ifdef DEBUG case JSOP_PUSHBLOCKSCOPE: case JSOP_FRESHENBLOCKSCOPE: case JSOP_POPBLOCKSCOPE: // These opcodes are currently unhandled by Ion, but in principle // there's no reason they couldn't be. Whenever this happens, OSR will // have to consider that JSOP_FRESHENBLOCK mutates the scope chain -- // right now it caches the scope chain in MBasicBlock::scopeChain(). @@ -3414,18 +3418,22 @@ IonBuilder::tableSwitch(JSOp op, jssrcno // Create MIR instruction MTableSwitch* tableswitch = MTableSwitch::New(alloc(), ins, low, high); // Create default case MBasicBlock* defaultcase = newBlock(current, defaultpc); if (!defaultcase) return ControlStatus_Error; - tableswitch->addDefault(defaultcase); - tableswitch->addBlock(defaultcase); + + if (!tableswitch->addDefault(defaultcase)) + return ControlStatus_Error; + + if (!tableswitch->addBlock(defaultcase)) + return ControlStatus_Error; // Create cases jsbytecode* casepc = nullptr; for (int i = 0; i < high-low+1; i++) { casepc = pc + GET_JUMP_OFFSET(pc2); MOZ_ASSERT(casepc >= pc && casepc <= exitpc); MBasicBlock* caseblock; @@ -3443,24 +3451,32 @@ IonBuilder::tableSwitch(JSOp op, jssrcno return ControlStatus_Error; } else { // If this is an actual case (not filled gap), // add this block to the list that still needs to get processed. caseblock = newBlock(current, casepc); if (!caseblock) return ControlStatus_Error; - tableswitch->addBlock(caseblock); + if (!tableswitch->addBlock(caseblock)) + return ControlStatus_Error; + // Add constant to indicate which case this is for use by // processNextTableSwitchCase. MConstant* constant = MConstant::New(alloc(), Int32Value(i + low)); caseblock->add(constant); } - tableswitch->addCase(tableswitch->addSuccessor(caseblock)); + size_t caseIndex; + if (!tableswitch->addSuccessor(caseblock, &caseIndex)) + return ControlStatus_Error; + + if (!tableswitch->addCase(caseIndex)) + return ControlStatus_Error; + pc2 += JUMP_OFFSET_LEN; } // Move defaultcase to the end, to maintain RPO. graph().moveBlockToEnd(defaultcase); MOZ_ASSERT(tableswitch->numCases() == (uint32_t)(high - low + 1)); MOZ_ASSERT(tableswitch->numSuccessors() > 0); @@ -5384,17 +5400,17 @@ IonBuilder::selectInliningTargets(const if (totalSize > optimizationInfo().inlineMaxBytecodePerCallSite(offThread)) inlineable = false; } } else { // Non-function targets are not supported by polymorphic inlining. inlineable = false; } - choiceSet.append(inlineable); + choiceSet.infallibleAppend(inlineable); if (inlineable) *numInlineable += 1; } // If optimization tracking is turned on and one of the inlineable targets // is a native, track the type info of the call. Most native inlinings // depend on the types of the arguments and the return value. if (isOptimizationTrackingEnabled()) { @@ -5876,17 +5892,18 @@ IonBuilder::inlineCalls(CallInfo& callIn } // inlineSingleCall() changed |current| to the inline return block. MBasicBlock* inlineReturnBlock = current; setCurrent(dispatchBlock); // Connect the inline path to the returnBlock. ObjectGroup* funcGroup = target->isSingleton() ? nullptr : target->group(); - dispatch->addCase(target, funcGroup, inlineBlock); + if (!dispatch->addCase(target, funcGroup, inlineBlock)) + return false; MDefinition* retVal = inlineReturnBlock->peek(-1); retPhi->addInput(retVal); inlineReturnBlock->end(MGoto::New(alloc(), returnBlock)); if (!returnBlock->addPredecessorWithoutPhis(inlineReturnBlock)) return false; } @@ -10304,16 +10321,41 @@ IonBuilder::jsop_rest() // elements added. MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, index); current->add(initLength); current->push(array); return true; } +bool +IonBuilder::jsop_checkobjcoercible() +{ + MDefinition* toCheck = current->peek(-1); + + if (!toCheck->mightBeType(MIRType_Undefined) && + !toCheck->mightBeType(MIRType_Null)) + { + toCheck->setImplicitlyUsedUnchecked(); + return true; + } + + MOZ_ASSERT(toCheck->type() == MIRType_Value || + toCheck->type() == MIRType_Null || + toCheck->type() == MIRType_Undefined); + + // If we want to squeeze more perf here, we can throw without checking, + // if IsNullOrUndefined(toCheck->type()). Since this is a failure case, + // it should be OK. + MCheckObjCoercible* check = MCheckObjCoercible::New(alloc(), current->pop()); + current->add(check); + current->push(check); + return resumeAfter(check); +} + uint32_t IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed) { if (!types || types->unknownObject()) { trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); return UINT32_MAX; } @@ -13016,17 +13058,17 @@ IonBuilder::jsop_typeof() bool IonBuilder::jsop_toid() { // No-op if the index is an integer. if (current->peek(-1)->type() == MIRType_Int32) return true; MDefinition* index = current->pop(); - MToId* ins = MToId::New(alloc(), current->peek(-1), index); + MToId* ins = MToId::New(alloc(), index); current->add(ins); current->push(ins); return resumeAfter(ins); } bool
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -733,16 +733,17 @@ class IonBuilder bool jsop_iterend(); bool jsop_in(); bool jsop_in_dense(MDefinition* obj, MDefinition* id, JSValueType unboxedType); bool jsop_instanceof(); bool jsop_getaliasedvar(ScopeCoordinate sc); bool jsop_setaliasedvar(ScopeCoordinate sc); bool jsop_debugger(); bool jsop_newtarget(); + bool jsop_checkobjcoercible(); /* Inlining. */ enum InliningStatus { InliningStatus_Error, InliningStatus_NotInlined, InliningStatus_WarmUpCountTooLow,
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1092,18 +1092,17 @@ LIRGenerator::visitTypeOf(MTypeOf* ins) useBox(lir, LTypeOfV::Input, opd); define(lir, ins); } void LIRGenerator::visitToId(MToId* ins) { LToIdV* lir = new(alloc()) LToIdV(tempDouble()); - useBox(lir, LToIdV::Object, ins->lhs()); - useBox(lir, LToIdV::Index, ins->rhs()); + useBox(lir, LToIdV::Index, ins->input()); defineBox(lir, ins); assignSafepoint(lir, ins); } void LIRGenerator::visitBitNot(MBitNot* ins) { MDefinition* input = ins->getOperand(0); @@ -4326,16 +4325,29 @@ LIRGenerator::visitCheckReturn(MCheckRet LCheckReturn* lir = new(alloc()) LCheckReturn(); useBoxAtStart(lir, LCheckReturn::ReturnValue, retVal); useBoxAtStart(lir, LCheckReturn::ThisValue, thisVal); assignSnapshot(lir, Bailout_BadDerivedConstructorReturn); add(lir, ins); redefine(ins, retVal); } +void +LIRGenerator::visitCheckObjCoercible(MCheckObjCoercible* ins) +{ + MDefinition* checkVal = ins->checkValue(); + MOZ_ASSERT(checkVal->type() == MIRType_Value); + + LCheckObjCoercible* lir = new(alloc()) LCheckObjCoercible(); + useBoxAtStart(lir, LCheckObjCoercible::CheckValue, checkVal); + redefine(ins, checkVal); + add(lir, ins); + assignSafepoint(lir, ins); +} + static void SpewResumePoint(MBasicBlock* block, MInstruction* ins, MResumePoint* resumePoint) { Fprinter& out = JitSpewPrinter(); out.printf("Current resume point %p details:\n", (void*)resumePoint); out.printf(" frame count: %u\n", resumePoint->frameCount()); if (ins) {
--- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -305,14 +305,15 @@ class LIRGenerator : public LIRGenerator void visitThrowRuntimeLexicalError(MThrowRuntimeLexicalError* ins); void visitGlobalNameConflictsCheck(MGlobalNameConflictsCheck* ins); void visitDebugger(MDebugger* ins); void visitNewTarget(MNewTarget* ins); void visitArrowNewTarget(MArrowNewTarget* ins); void visitAtomicIsLockFree(MAtomicIsLockFree* ins); void visitGuardSharedTypedArray(MGuardSharedTypedArray* ins); void visitCheckReturn(MCheckReturn* ins); + void visitCheckObjCoercible(MCheckObjCoercible* ins); }; } // namespace jit } // namespace js #endif /* jit_Lowering_h */
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2531,21 +2531,21 @@ class MTableSwitch final public: INSTRUCTION_HEADER(TableSwitch) static MTableSwitch* New(TempAllocator& alloc, MDefinition* ins, int32_t low, int32_t high); size_t numSuccessors() const override { return successors_.length(); } - size_t addSuccessor(MBasicBlock* successor) { + bool addSuccessor(MBasicBlock* successor, size_t* index) { MOZ_ASSERT(successors_.length() < (size_t)(high_ - low_ + 2)); MOZ_ASSERT(!successors_.empty()); - successors_.append(successor); - return successors_.length() - 1; + *index = successors_.length(); + return successors_.append(successor); } MBasicBlock* getSuccessor(size_t i) const override { MOZ_ASSERT(i < numSuccessors()); return successors_[i]; } void replaceSuccessor(size_t i, MBasicBlock* successor) override { @@ -2576,33 +2576,34 @@ class MTableSwitch final MBasicBlock* getCase(size_t i) const { return getSuccessor(cases_[i]); } size_t numCases() const { return high() - low() + 1; } - size_t addDefault(MBasicBlock* block) { + bool addDefault(MBasicBlock* block, size_t* index = nullptr) { MOZ_ASSERT(successors_.empty()); - successors_.append(block); - return 0; - } - - void addCase(size_t successorIndex) { - cases_.append(successorIndex); + if (index) + *index = 0; + return successors_.append(block); + } + + bool addCase(size_t successorIndex) { + return cases_.append(successorIndex); } MBasicBlock* getBlock(size_t i) const { MOZ_ASSERT(i < numBlocks()); return blocks_[i]; } - void addBlock(MBasicBlock* block) { - blocks_.append(block); + bool addBlock(MBasicBlock* block) { + return blocks_.append(block); } MDefinition* getOperand(size_t index) const override { MOZ_ASSERT(index == 0); return operand_.producer(); } size_t numOperands() const override { @@ -5366,30 +5367,30 @@ class MTypeOf bool writeRecoverData(CompactBufferWriter& writer) const override; bool canRecoverOnBailout() const override { return true; } }; class MToId - : public MBinaryInstruction, + : public MUnaryInstruction, public BoxInputsPolicy::Data { - MToId(MDefinition* object, MDefinition* index) - : MBinaryInstruction(object, index) + explicit MToId(MDefinition* index) + : MUnaryInstruction(index) { setResultType(MIRType_Value); } public: INSTRUCTION_HEADER(ToId) - static MToId* New(TempAllocator& alloc, MDefinition* object, MDefinition* index) { - return new(alloc) MToId(object, index); + static MToId* New(TempAllocator& alloc, MDefinition* index) { + return new(alloc) MToId(index); } }; class MBinaryBitwiseInstruction : public MBinaryInstruction, public BitwisePolicy::Data { protected: @@ -10641,18 +10642,18 @@ class MDispatchInstruction MBasicBlock* getSuccessor(size_t i) const final override { MOZ_ASSERT(i < numSuccessors()); if (i == map_.length()) return fallback_; return map_[i].block; } public: - void addCase(JSFunction* func, ObjectGroup* funcGroup, MBasicBlock* block) { - map_.append(Entry(func, funcGroup, block)); + bool addCase(JSFunction* func, ObjectGroup* funcGroup, MBasicBlock* block) { + return map_.append(Entry(func, funcGroup, block)); } uint32_t numCases() const { return map_.length(); } JSFunction* getCase(uint32_t i) const { return map_[i].func; } ObjectGroup* getCaseObjectGroup(uint32_t i) const { @@ -13360,16 +13361,39 @@ class MDebugger : public MNullaryInstruc public: INSTRUCTION_HEADER(Debugger) static MDebugger* New(TempAllocator& alloc) { return new(alloc) MDebugger(); } }; +class MCheckObjCoercible + : public MUnaryInstruction, + public BoxInputsPolicy::Data +{ + explicit MCheckObjCoercible(MDefinition* toCheck) + : MUnaryInstruction(toCheck) + { + setGuard(); + setResultType(MIRType_Value); + setResultTypeSet(toCheck->resultTypeSet()); + } + + public: + INSTRUCTION_HEADER(CheckObjCoercible) + static MCheckObjCoercible* New(TempAllocator& alloc, MDefinition* toCheck) { + return new(alloc) MCheckObjCoercible(toCheck); + } + + MDefinition* checkValue() { + return getOperand(0); + } +}; + class MAsmJSNeg : public MUnaryInstruction, public NoTypePolicy::Data { MAsmJSNeg(MDefinition* op, MIRType type) : MUnaryInstruction(op) { setResultType(type);
--- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -274,17 +274,18 @@ namespace jit { _(AsmJSAtomicBinopHeap) \ _(UnknownValue) \ _(LexicalCheck) \ _(ThrowRuntimeLexicalError) \ _(GlobalNameConflictsCheck) \ _(Debugger) \ _(NewTarget) \ _(ArrowNewTarget) \ - _(CheckReturn) + _(CheckReturn) \ + _(CheckObjCoercible) // Forward declarations of MIR types. #define FORWARD_DECLARE(op) class M##op; MIR_OPCODE_LIST(FORWARD_DECLARE) #undef FORWARD_DECLARE class MDefinitionVisitor // interface i.e. pure abstract class {
--- a/js/src/jit/RegisterAllocator.h +++ b/js/src/jit/RegisterAllocator.h @@ -60,28 +60,34 @@ struct AllocationIntegrityState Vector<LDefinition, 0, SystemAllocPolicy> temps; Vector<LDefinition, 1, SystemAllocPolicy> outputs; InstructionInfo() { } InstructionInfo(const InstructionInfo& o) { - inputs.appendAll(o.inputs); - temps.appendAll(o.temps); - outputs.appendAll(o.outputs); + AutoEnterOOMUnsafeRegion oomUnsafe; + if (!inputs.appendAll(o.inputs) || + !temps.appendAll(o.temps) || + !outputs.appendAll(o.outputs)) + { + oomUnsafe.crash("InstructionInfo::InstructionInfo"); + } } }; Vector<InstructionInfo, 0, SystemAllocPolicy> instructions; struct BlockInfo { Vector<InstructionInfo, 5, SystemAllocPolicy> phis; BlockInfo() {} BlockInfo(const BlockInfo& o) { - phis.appendAll(o.phis); + AutoEnterOOMUnsafeRegion oomUnsafe; + if (!phis.appendAll(o.phis)) + oomUnsafe.crash("BlockInfo::BlockInfo"); } }; Vector<BlockInfo, 0, SystemAllocPolicy> blocks; Vector<LDefinition*, 20, SystemAllocPolicy> virtualRegisters; // Describes a correspondence that should hold at the end of a block. // The value which was written to vreg in the original LIR should be
--- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -1306,16 +1306,25 @@ ThrowBadDerivedReturn(JSContext* cx, Han } bool BaselineThrowUninitializedThis(JSContext* cx, BaselineFrame* frame) { return ThrowUninitializedThis(cx, frame); } + +bool +ThrowObjectCoercible(JSContext* cx, HandleValue v) +{ + MOZ_ASSERT(v.isUndefined() || v.isNull()); + MOZ_ALWAYS_FALSE(ToObjectSlow(cx, v, false)); + return false; +} + bool BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res) { return GetFunctionThis(cx, frame, res); } } // namespace jit } // namespace js
--- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -733,14 +733,16 @@ IonMarkFunction(MIRType type) } bool ObjectIsCallable(JSObject* obj); bool ThrowRuntimeLexicalError(JSContext* cx, unsigned errorNumber); bool BaselineThrowUninitializedThis(JSContext* cx, BaselineFrame* frame); bool ThrowBadDerivedReturn(JSContext* cx, HandleValue v); +bool ThrowObjectCoercible(JSContext* cx, HandleValue v); + bool BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res); } // namespace jit } // namespace js #endif /* jit_VMFunctions_h */
--- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -1271,28 +1271,27 @@ class LTypeOfV : public LInstructionHelp return getTemp(0); } MTypeOf* mir() const { return mir_->toTypeOf(); } }; -class LToIdV : public LInstructionHelper<BOX_PIECES, 2 * BOX_PIECES, 1> +class LToIdV : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1> { public: LIR_HEADER(ToIdV) explicit LToIdV(const LDefinition& temp) { setTemp(0, temp); } - static const size_t Object = 0; - static const size_t Index = BOX_PIECES; + static const size_t Index = 0; MToId* mir() const { return mir_->toToId(); } const LDefinition* tempFloat() { return getTemp(0); } @@ -7392,12 +7391,20 @@ class LCheckReturn : public LCallInstruc { public: static const size_t ReturnValue = 0; static const size_t ThisValue = BOX_PIECES; LIR_HEADER(CheckReturn) }; +class LCheckObjCoercible : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0> +{ + public: + static const size_t CheckValue = 0; + + LIR_HEADER(CheckObjCoercible) +}; + } // namespace jit } // namespace js #endif /* jit_shared_LIR_shared_h */
--- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -362,11 +362,12 @@ _(AssertResultV) \ _(AssertResultT) \ _(LexicalCheck) \ _(ThrowRuntimeLexicalError) \ _(GlobalNameConflictsCheck) \ _(Debugger) \ _(NewTarget) \ _(ArrowNewTarget) \ - _(CheckReturn) + _(CheckReturn) \ + _(CheckObjCoercible) #endif /* jit_shared_LOpcodes_shared_h */
--- a/js/src/js.msg +++ b/js/src/js.msg @@ -100,17 +100,16 @@ MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA, 2 MSG_DEF(JSMSG_NEXT_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "iterator.next() returned a non-object value") MSG_DEF(JSMSG_CANT_SET_PROTO, 0, JSEXN_TYPEERR, "can't set prototype of this object") MSG_DEF(JSMSG_CANT_SET_PROTO_OF, 1, JSEXN_TYPEERR, "can't set prototype of {0}") MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE, 0, JSEXN_TYPEERR, "can't set prototype: it would cause a prototype chain cycle") MSG_DEF(JSMSG_INVALID_ARG_TYPE, 3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}") MSG_DEF(JSMSG_TERMINATED, 1, JSEXN_ERR, "Script terminated by timeout at:\n{0}") MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0}.prototype is not an object or null") MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|") -MSG_DEF(JSMSG_DISABLED_DERIVED_CLASS, 1, JSEXN_INTERNALERR, "{0} temporarily disallowed in derived class constructors") MSG_DEF(JSMSG_UNINITIALIZED_THIS, 1, JSEXN_REFERENCEERR, "|this| used uninitialized in {0} class constructor") MSG_DEF(JSMSG_BAD_DERIVED_RETURN, 1, JSEXN_TYPEERR, "derived class constructor returned invalid value {0}") // JSON MSG_DEF(JSMSG_JSON_BAD_PARSE, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data") MSG_DEF(JSMSG_JSON_CYCLIC_VALUE, 1, JSEXN_TYPEERR, "cyclic {0} value") // Runtime errors
--- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -8,17 +8,16 @@ * JS math package. */ #include "jsmath.h" #include "mozilla/FloatingPoint.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/unused.h" #include <algorithm> // for std::max #include <fcntl.h> #ifdef XP_UNIX # include <unistd.h> #endif @@ -797,20 +796,25 @@ GenerateSeed(uint64_t* seedBuffer, size_ } #elif defined(XP_UNIX) int fd = open("/dev/urandom", O_RDONLY); MOZ_ASSERT(fd >= 0, "Can't open /dev/urandom?!"); if (fd >= 0) { ssize_t size = length * sizeof(seedBuffer[0]); ssize_t nread = read(fd, (char *) seedBuffer, size); + close(fd); MOZ_ASSERT(nread == size, "Can't read /dev/urandom?!"); - mozilla::Unused << nread; - close(fd); + if (nread == size) + return; } + + // Use PRMJ_Now() if we couldn't read from /dev/urandom. + for (size_t i = 0; i < length; i++) + seedBuffer[i] = PRMJ_Now(); #else # error "Platform needs to implement random_generateSeed()" #endif } void js::GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed) {
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2226,21 +2226,22 @@ js::LookupNameUnqualified(JSContext* cx, if (!LookupProperty(cx, scope, id, &pobj, &shape)) return false; if (shape) break; } // See note above RuntimeLexicalErrorObject. if (pobj == scope) { - if (IsUninitializedLexicalSlot(scope, shape)) { + if (name != cx->names().dotThis && IsUninitializedLexicalSlot(scope, shape)) { scope = RuntimeLexicalErrorObject::create(cx, scope, JSMSG_UNINITIALIZED_LEXICAL); if (!scope) return false; } else if (scope->is<ScopeObject>() && !scope->is<DeclEnvObject>() && !shape->writable()) { + MOZ_ASSERT(name != cx->names().dotThis); scope = RuntimeLexicalErrorObject::create(cx, scope, JSMSG_BAD_CONST_ASSIGN); if (!scope) return false; } } objp.set(scope); return true;
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalBinding.js @@ -0,0 +1,19 @@ +// Make sure it doesn't matter when we make the arrow function +var test = ` + +new class extends class { } { + constructor() { + let arrow = () => this; + assertThrowsInstanceOf(arrow, ReferenceError); + super(); + assertEq(arrow(), this); + } +}(); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK");
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalClosed.js @@ -0,0 +1,18 @@ +var test = ` + +new class extends class { } { + constructor() { + let a1 = () => this; + let a2 = (() => super()); + assertThrowsInstanceOf(a1, ReferenceError); + assertEq(a2(), a1()); + } +}(); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK");
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalEscape.js @@ -0,0 +1,20 @@ +var test = ` + +let arrow; + +class foo extends class { } { + constructor() { + arrow = () => this; + super(); + } +} + +assertEq(new foo(), arrow()); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK");
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalEscapeUninitialized.js @@ -0,0 +1,45 @@ +var test = ` + +let superArrow; +let thisArrow; + +let thisStash; + +class base { + constructor() { + // We run this constructor twice as part of the double init check + if (!thisStash) + thisStash = {prop:45}; + return thisStash; + } +} + +class foo extends base { + constructor() { + superArrow = (()=>super()); + thisArrow = ()=>this; + } +} + +// Populate the arrow function saves. Since we never invoke super(), we throw +assertThrowsInstanceOf(()=>new foo(), ReferenceError); + +// No |this| binding in the closure, yet +assertThrowsInstanceOf(thisArrow, ReferenceError); + +// call super() +superArrow(); + +// Can't call it twice +assertThrowsInstanceOf(superArrow, ReferenceError); + +// Oh look, |this| is populated, now. +assertEq(thisArrow(), thisStash); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK");
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalGetThis.js @@ -0,0 +1,17 @@ +var test = ` + +new class extends class { } { + constructor() { + super(); + assertEq(this, (()=>this)()); + assertEq(this, eval("this")); + } +}(); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK");
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalNestedSuperCall.js @@ -0,0 +1,41 @@ +var test = ` + +new class extends class { } { + constructor() { + (()=>eval("super()"))(); + assertEq(this, eval("this")); + assertEq(this, (()=>this)()); + } +}(); + +new class extends class { } { + constructor() { + (()=>(()=>super())())(); + assertEq(this, eval("this")); + assertEq(this, (()=>this)()); + } +}(); + +new class extends class { } { + constructor() { + eval("(()=>super())()"); + assertEq(this, eval("this")); + assertEq(this, (()=>this)()); + } +}(); + +new class extends class { } { + constructor() { + eval("eval('super()')"); + assertEq(this, eval("this")); + assertEq(this, (()=>this)()); + } +}(); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK");
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalSuperCall.js @@ -0,0 +1,25 @@ +var test = ` + +new class extends class { } { + constructor() { + assertEq(eval("super(); this"), this); + assertEq(this, eval("this")); + assertEq(this, (()=>this)()); + } +}(); + +new class extends class { } { + constructor() { + (()=>super())(); + assertEq(this, eval("this")); + assertEq(this, (()=>this)()); + } +}(); + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK");
deleted file mode 100644 --- a/js/src/tests/ecma_6/Class/derivedConstructorDisabled.js +++ /dev/null @@ -1,38 +0,0 @@ -// |reftest| skip-if(!xulRuntime.shell) - -var test = ` - -class base { - constructor() { - eval(''); - (()=>0)(); - } -} - -class derived extends base { - constructor() { - eval(''); - } -} - -// Make sure eval and arrows are still valid in non-derived constructors. -new base(); - - -// Eval throws in derived class constructors, in both class expressions and -// statements. -assertThrowsInstanceOf((() => new derived()), InternalError); -assertThrowsInstanceOf((() => new class extends base { constructor() { eval('') } }()), InternalError); - -var g = newGlobal(); -var dbg = Debugger(g); -dbg.onDebuggerStatement = function(frame) { assertThrowsInstanceOf(()=>frame.eval(''), InternalError); }; - -g.eval("new class foo extends null { constructor() { debugger; return {}; } }()"); -`; - -if (classesEnabled()) - eval(test); - -if (typeof reportCompare === 'function') - reportCompare(0,0,"OK");
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/Class/superPropIncDecElem.js @@ -0,0 +1,31 @@ +var test = ` + +// #1 +function base() { } + +base.prototype = { + test() { + --super[1]; + } +} + +var d = new base(); +d.test(); + +// #2 +class test2 { + test() { + super[1]++; + } +} + +var d = new test2(); +d.test() + +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK");
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-same-name.js @@ -0,0 +1,7 @@ +{ + function f() { return "inner"; } +} + +function f() { return "outer"; } + +reportCompare(f(), "inner");
--- a/js/src/tests/js1_8_5/reflect-parse/classes.js +++ b/js/src/tests/js1_8_5/reflect-parse/classes.js @@ -127,19 +127,16 @@ function testClasses() { assertClass("class NAME { constructor() { }; static [\"prototype\"]() { } }", [ctorPlaceholder, emptyCPNMethod("prototype", true)]); /* Constructor */ // Allow default constructors assertClass("class NAME { }", []); assertClass("class NAME extends null { }", [], lit(null)); - // For now, disallow arrow functions in derived class constructors - assertClassError("class NAME extends null { constructor() { (() => 0); }", InternalError); - // Derived class constructor must have curly brackets assertClassError("class NAME extends null { constructor() 1 }", SyntaxError); // It is an error to have two methods named constructor, but not other // names, regardless if one is an accessor or a generator or static. assertClassError("class NAME { constructor() { } constructor(a) { } }", SyntaxError); let methods = [["method() { }", simpleMethod("method", "method", false)], ["*method() { }", simpleMethod("method", "method", true)],
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -4368,18 +4368,18 @@ Debugger::setupTraceLogger(JSContext* cx args.rval().setBoolean(false); return true; } RootedValue v(cx); if (!GetProperty(cx, obj, obj, ids[i], &v)) return false; - textIds.append(textId); - values.append(ToBoolean(v)); + textIds.infallibleAppend(textId); + values.infallibleAppend(ToBoolean(v)); } MOZ_ASSERT(ids.length() == textIds.length()); MOZ_ASSERT(textIds.length() == values.length()); for (size_t i = 0; i < textIds.length(); i++) { if (values[i]) TraceLogEnableTextId(cx, textIds[i]); @@ -6719,23 +6719,16 @@ DebuggerGenericEval(JSContext* cx, const EvalBindings evalWithBindings, HandleValue bindings, HandleValue options, MutableHandleValue vp, Debugger* dbg, HandleObject scope, ScriptFrameIter* iter) { /* Either we're specifying the frame, or a global. */ MOZ_ASSERT_IF(iter, !scope); MOZ_ASSERT_IF(!iter, scope && IsGlobalLexicalScope(scope)); - if (iter && iter->script()->isDerivedClassConstructor()) { - MOZ_ASSERT(iter->isFunctionFrame() && iter->calleeTemplate()->isClassConstructor()); - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DISABLED_DERIVED_CLASS, - "debugger eval"); - return false; - } - /* Check the first argument, the eval code string. */ if (!code.isString()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, fullMethodName, "string", InformalValueTypeName(code)); return false; } RootedLinearString linear(cx, code.toString()->ensureLinear(cx)); if (!linear)
--- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -422,35 +422,40 @@ js::EnqueuePendingParseTasksAfterGC(JSRu GlobalHelperThreadState::ParseTaskVector newTasks; { AutoLockHelperThreadState lock; GlobalHelperThreadState::ParseTaskVector& waiting = HelperThreadState().parseWaitingOnGC(); for (size_t i = 0; i < waiting.length(); i++) { ParseTask* task = waiting[i]; if (task->runtimeMatches(rt)) { - newTasks.append(task); + AutoEnterOOMUnsafeRegion oomUnsafe; + if (!newTasks.append(task)) + oomUnsafe.crash("EnqueuePendingParseTasksAfterGC"); HelperThreadState().remove(waiting, &i); } } } if (newTasks.empty()) return; // This logic should mirror the contents of the !activeGCInAtomsZone() // branch in StartOffThreadParseScript: for (size_t i = 0; i < newTasks.length(); i++) newTasks[i]->activate(rt); AutoLockHelperThreadState lock; - for (size_t i = 0; i < newTasks.length(); i++) - HelperThreadState().parseWorklist().append(newTasks[i]); + { + AutoEnterOOMUnsafeRegion oomUnsafe; + if (!HelperThreadState().parseWorklist().appendAll(newTasks)) + oomUnsafe.crash("EnqueuePendingParseTasksAfterGC"); + } HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER); } static const uint32_t kDefaultHelperStackSize = 2048 * 1024; static const uint32_t kDefaultHelperStackQuota = 1800 * 1024; // TSan enforces a minimum stack size that's just slightly larger than our
--- a/js/src/vm/Interpreter-inl.h +++ b/js/src/vm/Interpreter-inl.h @@ -199,16 +199,20 @@ FetchName(JSContext* cx, HandleObject ob MOZ_ASSERT(shape->hasSlot()); vp.set(obj2->as<NativeObject>().getSlot(shape->slot())); } else { if (!NativeGetExistingProperty(cx, normalized, obj2.as<NativeObject>(), shape, vp)) return false; } } + // We do our own explicit checking for |this| + if (name == cx->names().dotThis) + return true; + // NAME operations are the slow paths already, so unconditionally check // for uninitialized lets. return CheckUninitializedLexical(cx, name, vp); } inline bool FetchNameNoGC(JSObject* pobj, Shape* shape, MutableHandleValue vp) { @@ -386,28 +390,24 @@ NegOperation(JSContext* cx, HandleScript return false; res.setNumber(-d); } return true; } static MOZ_ALWAYS_INLINE bool -ToIdOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue objval, - HandleValue idval, MutableHandleValue res) +ToIdOperation(JSContext* cx, HandleScript script, jsbytecode* pc, HandleValue idval, + MutableHandleValue res) { if (idval.isInt32()) { res.set(idval); return true; } - JSObject* obj = ToObjectFromStack(cx, objval); - if (!obj) - return false; - RootedId id(cx); if (!ToPropertyKey(cx, idval, &id)) return false; res.set(IdToValue(id)); return true; }
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1735,17 +1735,16 @@ CASE(EnableInterruptsPseudoOpcode) DISPATCH_TO(op); } /* Various 1-byte no-ops. */ CASE(JSOP_NOP) CASE(JSOP_UNUSED14) CASE(JSOP_UNUSED65) CASE(JSOP_BACKPATCH) -CASE(JSOP_UNUSED163) CASE(JSOP_UNUSED177) CASE(JSOP_UNUSED178) CASE(JSOP_UNUSED179) CASE(JSOP_UNUSED180) CASE(JSOP_UNUSED181) CASE(JSOP_UNUSED182) CASE(JSOP_UNUSED183) CASE(JSOP_UNUSED187) @@ -2417,20 +2416,19 @@ END_CASE(JSOP_DELELEM) CASE(JSOP_TOID) { /* * Increment or decrement requires use to lookup the same property twice, * but we need to avoid the observable stringification the second time. * There must be an object value below the id, which will not be popped. */ - ReservedRooted<Value> objval(&rootValue0, REGS.sp[-2]); ReservedRooted<Value> idval(&rootValue1, REGS.sp[-1]); MutableHandleValue res = REGS.stackHandleAt(-1); - if (!ToIdOperation(cx, script, REGS.pc, objval, idval, res)) + if (!ToIdOperation(cx, script, REGS.pc, idval, res)) goto error; } END_CASE(JSOP_TOID) CASE(JSOP_TYPEOFEXPR) CASE(JSOP_TYPEOF) { REGS.sp[-1].setString(TypeOfOperation(REGS.sp[-1], cx->runtime())); @@ -3227,20 +3225,17 @@ CASE(JSOP_GETLOCAL) assertSameCompartmentDebugOnly(cx, REGS.sp[-1]); } END_CASE(JSOP_GETLOCAL) CASE(JSOP_SETLOCAL) { uint32_t i = GET_LOCALNO(REGS.pc); - // Derived class constructors store the TDZ Value in the .this slot - // before a super() call. - MOZ_ASSERT_IF(!script->isDerivedClassConstructor(), - !IsUninitializedLexical(REGS.fp()->unaliasedLocal(i))); + MOZ_ASSERT(!IsUninitializedLexical(REGS.fp()->unaliasedLocal(i))); REGS.fp()->unaliasedLocal(i) = REGS.sp[-1]; } END_CASE(JSOP_SETLOCAL) CASE(JSOP_DEFVAR) { /* ES5 10.5 step 8 (with subsequent errata). */ @@ -3888,16 +3883,24 @@ CASE(JSOP_CLASSCONSTRUCTOR) JSFunction* constructor = MakeDefaultConstructor(cx, JSOp(*REGS.pc), script->getAtom(REGS.pc), nullptr); if (!constructor) goto error; PUSH_OBJECT(*constructor); } END_CASE(JSOP_CLASSCONSTRUCTOR) +CASE(JSOP_CHECKOBJCOERCIBLE) +{ + ReservedRooted<Value> checkVal(&rootValue0, REGS.sp[-1]); + if (checkVal.isNullOrUndefined() && !ToObjectFromStack(cx, checkVal)) + goto error; +} +END_CASE(JSOP_CHECKOBJCOERCIBLE) + DEFAULT() { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%d", *REGS.pc); JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_BYTECODE, numBuf); goto error; } @@ -4832,19 +4835,16 @@ js::ReportRuntimeRedeclaration(JSContext } } bool js::ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame) { RootedFunction fun(cx, frame.callee()); - MOZ_ASSERT(fun->isClassConstructor()); - MOZ_ASSERT(fun->nonLazyScript()->isDerivedClassConstructor()); - const char* name = "anonymous"; JSAutoByteString str; if (fun->atom()) { if (!AtomToPrintableString(cx, fun->atom(), &str)) return false; name = str.ptr(); }
--- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1663,17 +1663,24 @@ 1234567890123456789012345678901234567890 * Throws if a binding with the same name already exists on the scope, or * if a var binding with the same name exists on the global. * Category: Variables and Scopes * Type: Variables * Operands: uint32_t nameIndex * Stack: => */ \ macro(JSOP_DEFLET, 162,"deflet", NULL, 5, 0, 0, JOF_ATOM) \ - macro(JSOP_UNUSED163, 163,"unused163", NULL, 1, 0, 1, JOF_BYTE) \ + /* + * Throw if the value on the stack is not coerscible to an object (is |null| or |undefined|). + * Category: Literals + * Type: Object + * Operands: + * Stack: val => val + */ \ + macro(JSOP_CHECKOBJCOERCIBLE, 163, "checkobjcoercible", NULL, 1, 1, 1, JOF_BYTE) \ /* * Find the function to invoke with |super()| on the scope chain. * * Category: Variables and Scopes * Type: Super * Operands: * Stack: => superFun */ \ @@ -2117,25 +2124,22 @@ 1234567890123456789012345678901234567890 * Category: Variables and Scopes * Type: Arguments * Operands: * Stack: => rest */ \ macro(JSOP_REST, 224, "rest", NULL, 1, 0, 1, JOF_BYTE|JOF_TYPESET) \ \ /* - * First, throw a TypeError if baseValue is null or undefined. Then, - * replace the top-of-stack value propertyNameValue with - * ToPropertyKey(propertyNameValue). This opcode implements ES6 12.3.2.1 - * steps 7-10. It is also used to implement computed property names; in - * that case, baseValue is always an object, so the first step is a no-op. + * Replace the top-of-stack value propertyNameValue with + * ToPropertyKey(propertyNameValue). * Category: Literals * Type: Object * Operands: - * Stack: baseValue, propertyNameValue => baseValue, propertyKey + * Stack: propertyNameValue => propertyKey */ \ macro(JSOP_TOID, 225, "toid", NULL, 1, 1, 1, JOF_BYTE) \ \ /* * Pushes the implicit 'this' value for calls to the associated name onto * the stack. * Category: Variables and Scopes * Type: This
--- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -301,18 +301,20 @@ JSRuntime::init(uint32_t maxbytes, uint3 if (!atomsZone || !atomsZone->init(true)) return false; JS::CompartmentOptions options; ScopedJSDeletePtr<JSCompartment> atomsCompartment(new_<JSCompartment>(atomsZone.get(), options)); if (!atomsCompartment || !atomsCompartment->init(nullptr)) return false; - gc.zones.append(atomsZone.get()); - atomsZone->compartments.append(atomsCompartment.get()); + if (!gc.zones.append(atomsZone.get())) + return false; + if (!atomsZone->compartments.append(atomsCompartment.get())) + return false; atomsCompartment->setIsSystem(true); atomsZone.forget(); this->atomsCompartment_ = atomsCompartment.forget(); if (!symbolRegistry_.init()) return false;
--- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -948,17 +948,17 @@ class ClonedBlockObject : public BlockOb // (i.e. JSOP_SETNAME), we emit an accompanying, preceding JSOP_BINDNAME which // finds the right scope on which to set the name. Moreover, when the name on // the scope is an uninitialized lexical, we cannot throw eagerly, as the spec // demands that the error be thrown after evaluating the RHS of // assignments. Instead, this sentinel scope object is pushed on the stack. // Attempting to access anything on this scope throws the appropriate // ReferenceError. // -// ES6 'const' bindings induce a runtime assignment when assigned to outside +// ES6 'const' bindings induce a runtime error when assigned to outside // of initialization, regardless of strictness. class RuntimeLexicalErrorObject : public ScopeObject { static const unsigned ERROR_SLOT = 1; public: static const unsigned RESERVED_SLOTS = 2; static const Class class_;
--- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -3870,17 +3870,21 @@ TypeNewScript::rollbackPartiallyInitiali if (!initializerList) return false; bool found = false; RootedFunction function(cx, this->function()); Vector<uint32_t, 32> pcOffsets(cx); for (ScriptFrameIter iter(cx); !iter.done(); ++iter) { - pcOffsets.append(iter.script()->pcToOffset(iter.pc())); + { + AutoEnterOOMUnsafeRegion oomUnsafe; + if (!pcOffsets.append(iter.script()->pcToOffset(iter.pc()))) + oomUnsafe.crash("rollbackPartiallyInitializedObjects"); + } if (!iter.isConstructing() || !iter.matchCallee(cx, function)) continue; // Derived class constructors initialize their this-binding later and // we shouldn't run the definite properties analysis on them. MOZ_ASSERT(!iter.script()->isDerivedClassConstructor());
--- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -24,21 +24,21 @@ namespace js { * versions. If deserialization fails, the data should be invalidated if * possible. * * When you change this, run make_opcode_doc.py and copy the new output into * this wiki page: * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 327; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 329; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 425, +static_assert(JSErr_Limit == 424, "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or " "removed MSG_DEFs from js.msg, you should increment " "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's " "expected JSErr_Limit value."); class XDRBuffer { public: explicit XDRBuffer(JSContext* cx)
--- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -4570,23 +4570,19 @@ nsDisplayResolution::~nsDisplayResolutio #endif void nsDisplayResolution::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) { -#if defined(MOZ_SINGLE_PROCESS_APZ) nsIPresShell* presShell = mFrame->PresContext()->PresShell(); nsRect rect = aRect.RemoveResolution(presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f); mList.HitTest(aBuilder, rect, aState, aOutFrames); -#else - mList.HitTest(aBuilder, aRect, aState, aOutFrames); -#endif // MOZ_SINGLE_PROCESS_APZ } already_AddRefed<Layer> nsDisplayResolution::BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aContainerParameters) { nsIPresShell* presShell = mFrame->PresContext()->PresShell(); ContainerLayerParameters containerParameters(
--- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -1411,17 +1411,22 @@ public: * resolution bounds are sane, and the resolution of this was * actually updated. * * The resolution defaults to 1.0. */ virtual nsresult SetResolution(float aResolution) = 0; float GetResolution() { return mResolution.valueOr(1.0); } virtual float GetCumulativeResolution() = 0; - virtual float GetCumulativeScaleResolution() = 0; + + /** + * Calculate the cumulative scale resolution from this document up to + * but not including the root document. + */ + virtual float GetCumulativeNonRootScaleResolution() = 0; /** * Was the current resolution set by the user or just default initialized? */ bool IsResolutionSet() { return mResolution.isSome(); } /** * Similar to SetResolution() but also increases the scale of the content
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -2016,19 +2016,17 @@ nsLayoutUtils::GetEventCoordinatesRelati if (frameWidget && frameWidget == aWidget) { // Special case this cause it happens a lot. // This also fixes bug 664707, events in the extra-special case of select // dropdown popups that are transformed. nsPresContext* presContext = aFrame->PresContext(); nsPoint pt(presContext->DevPixelsToAppUnits(aPoint.x), presContext->DevPixelsToAppUnits(aPoint.y)); pt = pt - view->ViewToWidgetOffset(); -#if defined(MOZ_SINGLE_PROCESS_APZ) - pt = pt.RemoveResolution(presContext->PresShell()->GetCumulativeScaleResolution()); -#endif // MOZ_SINGLE_PROCESS_APZ + pt = pt.RemoveResolution(presContext->PresShell()->GetCumulativeNonRootScaleResolution()); return pt; } } /* If we walk up the frame tree and discover that any of the frames are * transformed, we need to do extra work to convert from the global * space to the local space. */ @@ -2054,22 +2052,20 @@ nsLayoutUtils::GetEventCoordinatesRelati return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); } // Convert from root document app units to app units of the document aFrame // is in. int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel(); int32_t localAPD = aFrame->PresContext()->AppUnitsPerDevPixel(); widgetToView = widgetToView.ScaleToOtherAppUnits(rootAPD, localAPD); -#if defined(MOZ_SINGLE_PROCESS_APZ) nsIPresShell* shell = aFrame->PresContext()->PresShell(); // XXX Bug 1224748 - Update nsLayoutUtils functions to correctly handle nsPresShell resolution - widgetToView = widgetToView.RemoveResolution(shell->GetCumulativeScaleResolution()); -#endif + widgetToView = widgetToView.RemoveResolution(shell->GetCumulativeNonRootScaleResolution()); /* If we encountered a transform, we can't do simple arithmetic to figure * out how to convert back to aFrame's coordinates and must use the CTM. */ if (transformFound || aFrame->IsSVGText()) { return TransformRootPointToFrame(aFrame, widgetToView); } @@ -2807,20 +2803,18 @@ nsLayoutUtils::TranslateViewToWidget(nsP nsIWidget* aWidget) { nsPoint viewOffset; nsIWidget* viewWidget = aView->GetNearestWidget(&viewOffset); if (!viewWidget) { return LayoutDeviceIntPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); } - nsPoint pt = aPt + viewOffset; -#if defined(MOZ_SINGLE_PROCESS_APZ) - pt = pt.ApplyResolution(aPresContext->PresShell()->GetCumulativeScaleResolution()); -#endif // MOZ_SINGLE_PROCESS_APZ + nsPoint pt = (aPt + + viewOffset).ApplyResolution(aPresContext->PresShell()->GetCumulativeNonRootScaleResolution()); LayoutDeviceIntPoint relativeToViewWidget(aPresContext->AppUnitsToDevPixels(pt.x), aPresContext->AppUnitsToDevPixels(pt.y)); return relativeToViewWidget + WidgetToWidgetOffset(viewWidget, aWidget); } // Combine aNewBreakType with aOrigBreakType, but limit the break types // to NS_STYLE_CLEAR_LEFT, RIGHT, BOTH. uint8_t
--- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5325,23 +5325,26 @@ float PresShell::GetCumulativeResolution float resolution = GetResolution(); nsPresContext* parentCtx = GetPresContext()->GetParentPresContext(); if (parentCtx) { resolution *= parentCtx->PresShell()->GetCumulativeResolution(); } return resolution; } -float PresShell::GetCumulativeScaleResolution() +float PresShell::GetCumulativeNonRootScaleResolution() { float resolution = 1.0; nsIPresShell* currentShell = this; while (currentShell) { - resolution *= currentShell->ScaleToResolution() ? currentShell->GetResolution() : 1.0f; - nsPresContext* parentCtx = currentShell->GetPresContext()->GetParentPresContext(); + nsPresContext* currentCtx = currentShell->GetPresContext(); + if (currentCtx != currentCtx->GetRootPresContext()) { + resolution *= currentShell->ScaleToResolution() ? currentShell->GetResolution() : 1.0f; + } + nsPresContext* parentCtx = currentCtx->GetParentPresContext(); if (parentCtx) { currentShell = parentCtx->PresShell(); } else { currentShell = nullptr; } } return resolution; }
--- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -213,17 +213,17 @@ public: virtual nsresult SetResolution(float aResolution) override { return SetResolutionImpl(aResolution, /* aScaleToResolution = */ false); } virtual nsresult SetResolutionAndScaleTo(float aResolution) override { return SetResolutionImpl(aResolution, /* aScaleToResolution = */ true); } virtual bool ScaleToResolution() const override; virtual float GetCumulativeResolution() override; - virtual float GetCumulativeScaleResolution() override; + virtual float GetCumulativeNonRootScaleResolution() override; //nsIViewObserver interface virtual void Paint(nsView* aViewToPaint, const nsRegion& aDirtyRegion, uint32_t aFlags) override; virtual nsresult HandleEvent(nsIFrame* aFrame, mozilla::WidgetGUIEvent* aEvent, bool aDontRetargetEvents,
--- a/layout/base/tests/chrome/chrome.ini +++ b/layout/base/tests/chrome/chrome.ini @@ -15,16 +15,17 @@ support-files = passpointerevents_dynamically_window.html printpreview_bug396024_helper.xul printpreview_bug482976_helper.xul printpreview_helper.xul transformed_scrolling_repaints_3_window.html file_bug1018265.xul [test_bug370436.html] +skip-if = buildapp == 'b2g' [test_bug396367-1.html] [test_bug396367-2.html] [test_bug420499.xul] skip-if = buildapp == 'b2g' [test_bug458898.html] skip-if = buildapp == 'b2g' [test_bug495648.xul] skip-if = buildapp == 'b2g'
--- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -437,22 +437,20 @@ nsSubDocumentFrame::BuildDisplayList(nsD dirty = rootScrollableFrame->ExpandRectToNearlyVisible(dirty); } } } aBuilder->EnterPresShell(subdocRootFrame, pointerEventsNone && !passPointerEventsToChildren); -#if defined(MOZ_SINGLE_PROCESS_APZ) if (!haveDisplayPort) { // Remove nsPresShell resolution dirty = dirty.RemoveResolution(presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f); } -#endif } else { dirty = aDirtyRect; } DisplayListClipState::AutoSaveRestore clipState(aBuilder); if (ShouldClipSubdocument()) { clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this); }
--- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -551,22 +551,30 @@ pref("editor.singleLine.pasteNewlines", // threshold where a tap becomes a drag, in 1/240" reference pixels // The names of the preferences are to be in sync with EventStateManager.cpp pref("ui.dragThresholdX", 25); pref("ui.dragThresholdY", 25); pref("layers.acceleration.disabled", false); pref("layers.offmainthreadcomposition.enabled", true); pref("layers.async-video.enabled", true); + #ifdef MOZ_ANDROID_APZ pref("layers.async-pan-zoom.enabled", true); -pref("apz.axis_lock.mode", 1); -pref("apz.fling_stop_on_tap_threshold", "0.08"); +// APZ physics settings, copied from B2G +pref("apz.axis_lock.mode", 2); // Use "sticky" axis locking +pref("apz.fling_curve_function_x1", "0.41"); +pref("apz.fling_curve_function_y1", "0.0"); +pref("apz.fling_curve_function_x2", "0.80"); +pref("apz.fling_curve_function_y2", "1.0"); +pref("apz.fling_curve_threshold_inches_per_ms", "0.01"); +pref("apz.fling_friction", "0.0019"); +pref("apz.max_velocity_inches_per_ms", "0.07"); #endif -pref("apz.allow_zooming", true); + pref("layers.progressive-paint", true); pref("layers.low-precision-buffer", true); pref("layers.low-precision-resolution", "0.25"); pref("layers.low-precision-opacity", "1.0"); // We want to limit layers for two reasons: // 1) We can't scroll smoothly if we have to many draw calls // 2) Pages that have too many layers consume too much memory and crash. // By limiting the number of layers on mobile we're making the main thread
--- a/mobile/android/b2gdroid/app/b2gdroid.js +++ b/mobile/android/b2gdroid/app/b2gdroid.js @@ -537,31 +537,30 @@ pref("editor.singleLine.pasteNewlines", // threshold where a tap becomes a drag, in 1/240" reference pixels // The names of the preferences are to be in sync with EventStateManager.cpp pref("ui.dragThresholdX", 25); pref("ui.dragThresholdY", 25); pref("layers.acceleration.disabled", false); pref("layers.offmainthreadcomposition.enabled", true); pref("layers.async-video.enabled", true); + #ifdef MOZ_ANDROID_APZ pref("layers.async-pan-zoom.enabled", true); -pref("apz.allow_zooming", true); -// Use "sticky" axis locking -pref("apz.axis_lock.mode", 2); // APZ physics settings, tuned by UX designers +pref("apz.axis_lock.mode", 2); // Use "sticky" axis locking pref("apz.fling_curve_function_x1", "0.41"); pref("apz.fling_curve_function_y1", "0.0"); pref("apz.fling_curve_function_x2", "0.80"); pref("apz.fling_curve_function_y2", "1.0"); pref("apz.fling_curve_threshold_inches_per_ms", "0.01"); pref("apz.fling_friction", "0.0019"); pref("apz.max_velocity_inches_per_ms", "0.07"); -pref("apz.touch_start_tolerance", "0.1"); #endif + pref("layers.progressive-paint", true); pref("layers.low-precision-buffer", true); pref("layers.low-precision-resolution", "0.25"); pref("layers.low-precision-opacity", "1.0"); // We want to limit layers for two reasons: // 1) We can't scroll smoothly if we have to many draw calls // 2) Pages that have too many layers consume too much memory and crash. // By limiting the number of layers on mobile we're making the main thread
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/LayerView.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/LayerView.java @@ -232,16 +232,21 @@ public class LayerView extends ScrollVie if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { requestFocus(); } event.offsetLocation(0, -mSurfaceTranslation); if (mToolbarAnimator != null && mToolbarAnimator.onInterceptTouchEvent(event)) { return true; } + if (AppConstants.MOZ_ANDROID_APZ && !mGeckoIsReady) { + // If gecko isn't loaded yet, don't try sending events to the + // native code because it's just going to crash + return true; + } if (mPanZoomController != null && mPanZoomController.onTouchEvent(event)) { return true; } return sendEventToGecko(event); } @Override public boolean onHoverEvent(MotionEvent event) { @@ -259,16 +264,21 @@ public class LayerView extends ScrollVie return sendEventToGecko(event); } @Override public boolean onGenericMotionEvent(MotionEvent event) { if (AndroidGamepadManager.handleMotionEvent(event)) { return true; } + if (AppConstants.MOZ_ANDROID_APZ && !mGeckoIsReady) { + // If gecko isn't loaded yet, don't try sending events to the + // native code because it's just going to crash + return true; + } if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) { return true; } return false; } @Override protected void onAttachedToWindow() { @@ -365,16 +375,21 @@ public class LayerView extends ScrollVie } public void setIsRTL(boolean aIsRTL) { mLayerClient.setIsRTL(aIsRTL); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { + if (AppConstants.MOZ_ANDROID_APZ && !mGeckoIsReady) { + // If gecko isn't loaded yet, don't try sending events to the + // native code because it's just going to crash + return true; + } if (mPanZoomController != null && mPanZoomController.onKeyEvent(event)) { return true; } return false; } public void requestRender() { if (mListener != null) {
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java @@ -87,20 +87,27 @@ class NativePanZoomController implements private native void init(); private native boolean handleTouchEvent(GeckoEvent event); private native void handleMotionEvent(GeckoEvent event); @Override public native void destroy(); @Override public native boolean getRedrawHint(); + @Override - public native void setOverScrollMode(int overscrollMode); + public void setOverScrollMode(int overscrollMode) { + // FIXME implement this + } + @Override - public native int getOverScrollMode(); + public int getOverScrollMode() { + // FIXME implement this + return 0; + } @WrapForJNI(allowMultithread = true, stubName = "RequestContentRepaintWrapper") private void requestContentRepaint(float x, float y, float width, float height, float resolution) { mTarget.forceRedraw(new DisplayPortMetrics(x, y, x + width, y + height, resolution)); } @Override public void setOverscrollHandler(final Overscroll listener) {
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -531,16 +531,20 @@ pref("media.video-queue.send-to-composit pref("media.video_stats.enabled", true); // Weather we allow AMD switchable graphics pref("layers.amd-switchable-gfx.enabled", true); // Whether to use async panning and zooming pref("layers.async-pan-zoom.enabled", false); +#ifdef MOZ_WIDGET_UIKIT +pref("layers.async-pan-zoom.enabled", true); +#endif + // Whether to enable event region building during painting pref("layout.event-regions.enabled", false); // APZ preferences. For documentation/details on what these prefs do, check // gfx/layers/apz/src/AsyncPanZoomController.cpp. pref("apz.allow_checkerboarding", true); pref("apz.allow_zooming", false); @@ -562,68 +566,64 @@ pref("apz.fling_accel_base_mult", "1.0") pref("apz.fling_accel_interval_ms", 500); pref("apz.fling_accel_supplemental_mult", "1.0"); pref("apz.fling_curve_function_x1", "0.0"); pref("apz.fling_curve_function_y1", "0.0"); pref("apz.fling_curve_function_x2", "1.0"); pref("apz.fling_curve_function_y2", "1.0"); pref("apz.fling_curve_threshold_inches_per_ms", "-1.0"); pref("apz.fling_friction", "0.002"); +pref("apz.fling_repaint_interval", 16); pref("apz.fling_stop_on_tap_threshold", "0.05"); pref("apz.fling_stopped_threshold", "0.01"); pref("apz.highlight_checkerboarded_areas", false); pref("apz.max_velocity_inches_per_ms", "-1.0"); pref("apz.max_velocity_queue_size", 5); pref("apz.min_skate_speed", "1.0"); pref("apz.minimap.enabled", false); pref("apz.num_paint_duration_samples", 3); pref("apz.overscroll.enabled", false); pref("apz.overscroll.min_pan_distance_ratio", "1.0"); -pref("apz.overscroll.stretch_factor", "0.5"); -pref("apz.overscroll.spring_stiffness", "0.001"); pref("apz.overscroll.spring_friction", "0.015"); +pref("apz.overscroll.spring_stiffness", "0.0018"); pref("apz.overscroll.stop_distance_threshold", "5.0"); pref("apz.overscroll.stop_velocity_threshold", "0.01"); +pref("apz.overscroll.stretch_factor", "0.35"); +pref("apz.pan_repaint_interval", 16); // Whether to print the APZC tree for debugging pref("apz.printtree", false); +pref("apz.smooth_scroll_repaint_interval", 16); pref("apz.test.logging_enabled", false); -pref("apz.touch_start_tolerance", "0.2222222"); // 0.2222222 came from 1.0/4.5 -pref("apz.touch_move_tolerance", "0.0"); +pref("apz.touch_start_tolerance", "0.1"); +pref("apz.touch_move_tolerance", "0.03"); pref("apz.use_paint_duration", true); pref("apz.velocity_bias", "1.0"); pref("apz.velocity_relevance_time_ms", 150); -pref("apz.x_stationary_size_multiplier", "3.0"); -pref("apz.y_stationary_size_multiplier", "3.5"); pref("apz.x_skate_highmem_adjust", "0.0"); pref("apz.y_skate_highmem_adjust", "0.0"); -pref("apz.zoom_animation_duration_ms", 250); - -#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) -// Desktop prefs -pref("apz.fling_repaint_interval", 16); -pref("apz.smooth_scroll_repaint_interval", 16); -pref("apz.pan_repaint_interval", 16); pref("apz.x_skate_size_multiplier", "2.5"); pref("apz.y_skate_size_multiplier", "3.5"); -#else +pref("apz.x_stationary_size_multiplier", "3.0"); +pref("apz.y_stationary_size_multiplier", "3.5"); +pref("apz.zoom_animation_duration_ms", 250); + +#if defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID) // Mobile prefs +pref("apz.allow_zooming", true); +pref("apz.enlarge_displayport_when_clipped", true); pref("apz.fling_repaint_interval", 75); pref("apz.smooth_scroll_repaint_interval", 75); -pref("apz.pan_repaint_interval", 250); pref("apz.x_skate_size_multiplier", "1.25"); pref("apz.y_skate_size_multiplier", "1.5"); pref("apz.x_stationary_size_multiplier", "1.5"); pref("apz.y_stationary_size_multiplier", "1.8"); #endif -// APZ testing (bug 961289) -pref("apz.test.logging_enabled", false); - #ifdef XP_MACOSX // Whether to run in native HiDPI mode on machines with "Retina"/HiDPI display; // <= 0 : hidpi mode disabled, display will just use pixel-based upscaling // == 1 : hidpi supported if all screens share the same backingScaleFactor // >= 2 : hidpi supported even with mixed backingScaleFactors (somewhat broken) pref("gfx.hidpi.enabled", 2); #endif @@ -2584,17 +2584,17 @@ pref("dom.ipc.plugins.unloadTimeoutSecs" // channels until some remaining bugs are resolved. #ifdef E10S_TESTING_ONLY pref("dom.ipc.plugins.asyncInit.enabled", false); #else pref("dom.ipc.plugins.asyncInit.enabled", true); #endif // Allow the AsyncDrawing mode to be used for plugins. -pref("dom.ipc.plugins.asyncdrawing.enabled", false); +pref("dom.ipc.plugins.asyncdrawing.enabled", true); pref("dom.ipc.processCount", 1); // Enable caching of Moz2D Path objects for SVG geometry elements pref("svg.path-caching.enabled", true); // Enable the use of display-lists for SVG hit-testing and painting. pref("svg.display-lists.hit-testing.enabled", true); @@ -3168,19 +3168,16 @@ pref("plugin.scan.Quicktime", "5.0"); // Locate and scan the Window Media Player installation directory for plugins with a minimum version pref("plugin.scan.WindowsMediaPlayer", "7.0"); // Locate plugins by the directories specified in the Windows registry for PLIDs // Which is currently HKLM\Software\MozillaPlugins\xxxPLIDxxx\Path pref("plugin.scan.plid.all", true); -// Allow the new AsyncDrawing mode to be used for plugins. -pref("plugin.allow.asyncdrawing", false); - // Help Windows NT, 2000, and XP dialup a RAS connection // when a network address is unreachable. pref("network.autodial-helper.enabled", true); // Switch the keyboard layout per window pref("intl.keyboard.per_window_layout", false); #ifdef NS_ENABLE_TSF @@ -4314,20 +4311,16 @@ pref("layers.tiles.adjust", true); // Set the default values, and then override per-platform as needed pref("layers.offmainthreadcomposition.enabled", true); // Compositor target frame rate. NOTE: If vsync is enabled the compositor // frame rate will still be capped. // -1 -> default (match layout.frame_rate or 60 FPS) // 0 -> full-tilt mode: Recomposite even if not transaction occured. pref("layers.offmainthreadcomposition.frame-rate", -1); -#ifdef MOZ_WIDGET_UIKIT -pref("layers.async-pan-zoom.enabled", true); -#endif - #ifdef XP_MACOSX pref("layers.enable-tiles", true); pref("layers.tile-width", 512); pref("layers.tile-height", 512); pref("layers.tiled-drawtarget.enabled", true); pref("layers.tiles.edge-padding", false); #endif
--- a/mozglue/android/jni-stubs.inc +++ b/mozglue/android/jni-stubs.inc @@ -737,54 +737,16 @@ Java_org_mozilla_gecko_gfx_NativePanZoom #endif #ifdef JNI_BINDINGS xul_dlsym("Java_org_mozilla_gecko_gfx_NativePanZoomController_getRedrawHint", &f_Java_org_mozilla_gecko_gfx_NativePanZoomController_getRedrawHint); #endif #ifdef JNI_STUBS -typedef void (*Java_org_mozilla_gecko_gfx_NativePanZoomController_setOverScrollMode_t)(JNIEnv *, jobject, jint); -static Java_org_mozilla_gecko_gfx_NativePanZoomController_setOverScrollMode_t f_Java_org_mozilla_gecko_gfx_NativePanZoomController_setOverScrollMode; -extern "C" NS_EXPORT void MOZ_JNICALL -Java_org_mozilla_gecko_gfx_NativePanZoomController_setOverScrollMode(JNIEnv * arg0, jobject arg1, jint arg2) { - if (!f_Java_org_mozilla_gecko_gfx_NativePanZoomController_setOverScrollMode) { - arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"), - "JNI Function called before it was loaded"); - return ; - } - f_Java_org_mozilla_gecko_gfx_NativePanZoomController_setOverScrollMode(arg0, arg1, arg2); -} -#endif - -#ifdef JNI_BINDINGS - xul_dlsym("Java_org_mozilla_gecko_gfx_NativePanZoomController_setOverScrollMode", &f_Java_org_mozilla_gecko_gfx_NativePanZoomController_setOverScrollMode); -#endif - -#ifdef JNI_STUBS - -typedef jint (*Java_org_mozilla_gecko_gfx_NativePanZoomController_getOverScrollMode_t)(JNIEnv *, jobject); -static Java_org_mozilla_gecko_gfx_NativePanZoomController_getOverScrollMode_t f_Java_org_mozilla_gecko_gfx_NativePanZoomController_getOverScrollMode; -extern "C" NS_EXPORT jint MOZ_JNICALL -Java_org_mozilla_gecko_gfx_NativePanZoomController_getOverScrollMode(JNIEnv * arg0, jobject arg1) { - if (!f_Java_org_mozilla_gecko_gfx_NativePanZoomController_getOverScrollMode) { - arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"), - "JNI Function called before it was loaded"); - return 0; - } - return f_Java_org_mozilla_gecko_gfx_NativePanZoomController_getOverScrollMode(arg0, arg1); -} -#endif - -#ifdef JNI_BINDINGS - xul_dlsym("Java_org_mozilla_gecko_gfx_NativePanZoomController_getOverScrollMode", &f_Java_org_mozilla_gecko_gfx_NativePanZoomController_getOverScrollMode); -#endif - -#ifdef JNI_STUBS - typedef void (*Java_org_mozilla_gecko_gfx_NativePanZoomController_setIsLongpressEnabled_t)(JNIEnv *, jobject, jboolean); static Java_org_mozilla_gecko_gfx_NativePanZoomController_setIsLongpressEnabled_t f_Java_org_mozilla_gecko_gfx_NativePanZoomController_setIsLongpressEnabled; extern "C" NS_EXPORT void MOZ_JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_setIsLongpressEnabled(JNIEnv * arg0, jobject arg1, jboolean arg2) { if (!f_Java_org_mozilla_gecko_gfx_NativePanZoomController_setIsLongpressEnabled) { arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"), "JNI Function called before it was loaded"); return ;
--- a/security/manager/ssl/tests/unit/test_cert_blocklist.js +++ b/security/manager/ssl/tests/unit/test_cert_blocklist.js @@ -55,25 +55,42 @@ var XULAppInfoFactory = { appInfo.QueryInterface(iid); if (outer != null) { throw Cr.NS_ERROR_NO_AGGREGATION; } return appInfo.QueryInterface(iid); } }; +// we need to ensure we setup revocation data before certDB, or we'll start with +// no revocation.txt in the profile +var profile = do_get_profile(); + +// Write out an empty blocklist.xml file to the profile to ensure nothing +// is blocklisted by default +var blockFile = profile.clone(); +blockFile.append("blocklist.xml"); +var stream = Cc["@mozilla.org/network/file-output-stream;1"] + .createInstance(Ci.nsIFileOutputStream); +stream.init(blockFile, + FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE, + FileUtils.PERMS_FILE, 0); + +var data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + + "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">\n" + + "</blocklist>\n"; +stream.write(data, data.length); +stream.close(); + var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}"); registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo", XULAPPINFO_CONTRACTID, XULAppInfoFactory); -// we need to ensure we setup revocation data before certDB, or we'll start with -// no revocation.txt in the profile -var profile = do_get_profile(); var revocations = profile.clone(); revocations.append("revocations.txt"); if (!revocations.exists()) { let existing = do_get_file("test_onecrl/sample_revocations.txt", false); existing.copyTo(profile,"revocations.txt"); } var certDB = Cc["@mozilla.org/security/x509certdb;1"] @@ -130,17 +147,17 @@ var blocklists = { "/updatedBlocklist/" : updatedBlocklist } function serveResponse(request, response) { do_print("Serving for path " + request.path + "\n"); response.write(blocklists[request.path]); } -for (path in blocklists) { +for (var path in blocklists) { testserver.registerPathHandler(path, serveResponse); } // start the test server testserver.start(-1); var port = testserver.identity.primaryPort; // Setup the addonManager @@ -215,17 +232,16 @@ function check_revocations_txt_contents( inputStream.QueryInterface(Ci.nsILineInputStream); let contents = ""; let hasmore = false; do { var line = {}; hasmore = inputStream.readLine(line); contents = contents + (contents.length == 0 ? "" : "\n") + line.value; } while (hasmore); - equal(contents, expected, "revocations.txt should be as expected"); } function run_test() { // import the certificates we need load_cert("test-ca", "CTu,CTu,CTu"); load_cert("test-int", ",,"); load_cert("other-test-ca", "CTu,CTu,CTu");
--- a/testing/marionette/transport/marionette_transport/transport.py +++ b/testing/marionette/transport/marionette_transport/transport.py @@ -123,25 +123,16 @@ class TcpTransport(object): self.protocol = 1 self.application_type = None self.last_id = 0 self.expected_responses = [] self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(self.socket_timeout) - def _recv_n_bytes(self, n): - data = "" - while len(data) < n: - chunk = self.sock.recv(n - len(data)) - if chunk == "": - break - data += chunk - return data - def _unmarshal(self, packet): msg = None # protocol 3 and above if self.protocol >= 3: typ = int(packet[1]) if typ == Command.TYPE: msg = Command.from_msg(packet)
--- a/testing/web-platform/meta/custom-elements/custom-element-lifecycle/types-of-callbacks/attached-callback-move-element-test.html.ini +++ b/testing/web-platform/meta/custom-elements/custom-element-lifecycle/types-of-callbacks/attached-callback-move-element-test.html.ini @@ -1,8 +1,9 @@ [attached-callback-move-element-test.html] type: testharness + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1231029 https://bugzilla.mozilla.org/show_bug.cgi?id=1231031 https://bugzilla.mozilla.org/show_bug.cgi?id=1183023 [Test attached callback if moving custom element from document without browsing context to document with browsing context] expected: FAIL [Test attached callback if moving custom element from document with browsing context to document with browsing context] expected: FAIL
--- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -6280,16 +6280,23 @@ "cpp_guard": "MOZ_RUST_MP4PARSE" }, "MEDIA_WMF_DECODE_ERROR": { "expires_in_version": "50", "kind": "enumerated", "n_values": 256, "description": "WMF media decoder error or success (0) codes." }, + "MEDIA_OGG_LOADED_IS_CHAINED": { + "alert_emails": ["cpearce@mozilla.com"], + "expires_in_version": "53", + "kind": "boolean", + "description": "Whether Ogg audio/video encountered are chained or not.", + "bug_numbers": [1230295] + }, "VIDEO_CAN_CREATE_AAC_DECODER": { "alert_emails": ["cpearce@mozilla.com"], "expires_in_version": "50", "kind": "boolean", "description": "Whether at startup we report we can playback MP4 (AAC) audio. This is single value is recorded at every startup.", "releaseChannelCollection": "opt-out" }, "VIDEO_CAN_CREATE_H264_DECODER": { @@ -10165,10 +10172,18 @@ "description": "The number of ICE connections which immediately failed (0) vs. reached at least checking state (1)." }, "YOUTUBE_REWRITABLE_EMBED_SEEN": { "alert_emails": ["cpeterson@mozilla.com"], "expires_in_version": "48", "kind": "flag", "bug_numbers": [1229971], "description": "Flag activated whenever a rewritable youtube flash embed is seen during a session." + }, + "PLUGIN_DRAWING_MODEL": { + "alert_emails": ["danderson@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "bug_numbers": [1229961], + "n_values": 12, + "description": "Plugin drawing model. 0 when windowed, otherwise NPDrawingModel + 1." } }
--- a/toolkit/mozapps/extensions/internal/PluginProvider.jsm +++ b/toolkit/mozapps/extensions/internal/PluginProvider.jsm @@ -444,17 +444,17 @@ PluginWrapper.prototype = { let libs = []; for (let tag of pluginFor(this).tags) libs.push(tag.filename); return libs; }, get pluginFullpath() { let paths = []; - for (let tag of aTags) + for (let tag of pluginFor(this).tags) paths.push(tag.fullpath); return paths; }, get pluginMimeTypes() { let types = []; for (let tag of pluginFor(this).tags) { let mimeTypes = tag.getMimeTypes({});
--- a/widget/ContentCache.cpp +++ b/widget/ContentCache.cpp @@ -443,21 +443,20 @@ ContentCacheInChild::SetSelection(nsIWid } /***************************************************************************** * mozilla::ContentCacheInParent *****************************************************************************/ ContentCacheInParent::ContentCacheInParent() : ContentCache() + , mCommitStringByRequest(nullptr) , mCompositionStart(UINT32_MAX) - , mCompositionEventsDuringRequest(0) , mPendingEventsNeedingAck(0) , mIsComposing(false) - , mRequestedToCommitOrCancelComposition(false) { } void ContentCacheInParent::AssignContent(const ContentCache& aOther, const IMENotification* aNotification) { mText = aOther.mText; @@ -822,58 +821,44 @@ ContentCacheInParent::GetCaretRect(uint3 bool ContentCacheInParent::OnCompositionEvent(const WidgetCompositionEvent& aEvent) { MOZ_LOG(sContentCacheLog, LogLevel::Info, ("ContentCacheInParent: 0x%p OnCompositionEvent(aEvent={ " "mMessage=%s, mData=\"%s\" (Length()=%u), mRanges->Length()=%u }), " "mPendingEventsNeedingAck=%u, mIsComposing=%s, " - "mRequestedToCommitOrCancelComposition=%s", + "mCommitStringByRequest=0x%p", this, ToChar(aEvent.mMessage), NS_ConvertUTF16toUTF8(aEvent.mData).get(), aEvent.mData.Length(), aEvent.mRanges ? aEvent.mRanges->Length() : 0, mPendingEventsNeedingAck, - GetBoolName(mIsComposing), - GetBoolName(mRequestedToCommitOrCancelComposition))); - - if (!aEvent.CausesDOMTextEvent()) { - MOZ_ASSERT(aEvent.mMessage == eCompositionStart); - mIsComposing = !aEvent.CausesDOMCompositionEndEvent(); - mCompositionStart = mSelection.StartOffset(); - // XXX What's this case?? - if (mRequestedToCommitOrCancelComposition) { - mCommitStringByRequest = aEvent.mData; - mCompositionEventsDuringRequest++; - return false; - } - mPendingEventsNeedingAck++; - return true; - } - - // XXX Why do we ignore following composition events here? - // TextComposition must handle following events correctly! - - // During REQUEST_TO_COMMIT_COMPOSITION or REQUEST_TO_CANCEL_COMPOSITION, - // widget usually sends a eCompositionChange event to finalize or - // clear the composition, respectively. - // Because the event will not reach content in time, we intercept it - // here and pass the text as the DidRequestToCommitOrCancelComposition() - // return value. - if (mRequestedToCommitOrCancelComposition) { - mCommitStringByRequest = aEvent.mData; - mCompositionEventsDuringRequest++; - return false; - } + GetBoolName(mIsComposing), mCommitStringByRequest)); // We must be able to simulate the selection because // we might not receive selection updates in time if (!mIsComposing) { mCompositionStart = mSelection.StartOffset(); } + mIsComposing = !aEvent.CausesDOMCompositionEndEvent(); + + // During REQUEST_TO_COMMIT_COMPOSITION or REQUEST_TO_CANCEL_COMPOSITION, + // widget usually sends a eCompositionChange and/or eCompositionCommit event + // to finalize or clear the composition, respectively. In this time, + // we need to intercept all composition events here and pass the commit + // string for returning to the remote process as a result of + // RequestIMEToCommitComposition(). Then, eCommitComposition event will + // be dispatched with the committed string in the remote process internally. + if (mCommitStringByRequest) { + MOZ_ASSERT(aEvent.mMessage == eCompositionChange || + aEvent.mMessage == eCompositionCommit); + *mCommitStringByRequest = aEvent.mData; + return false; + } + mPendingEventsNeedingAck++; return true; } void ContentCacheInParent::OnSelectionEvent( const WidgetSelectionEvent& aSelectionEvent) { @@ -907,39 +892,73 @@ ContentCacheInParent::OnEventNeedingAckH MOZ_RELEASE_ASSERT(mPendingEventsNeedingAck > 0); if (--mPendingEventsNeedingAck) { return; } FlushPendingNotifications(aWidget); } -uint32_t -ContentCacheInParent::RequestToCommitComposition(nsIWidget* aWidget, - bool aCancel, - nsAString& aLastString) +bool +ContentCacheInParent::RequestIMEToCommitComposition(nsIWidget* aWidget, + bool aCancel, + nsAString& aCommittedString) { MOZ_LOG(sContentCacheLog, LogLevel::Info, ("ContentCacheInParent: 0x%p RequestToCommitComposition(aWidget=%p, " - "aCancel=%s), mIsComposing=%s, mRequestedToCommitOrCancelComposition=%s, " - "mCompositionEventsDuringRequest=%u", + "aCancel=%s), mIsComposing=%s, mCommitStringByRequest=%p", this, aWidget, GetBoolName(aCancel), GetBoolName(mIsComposing), - GetBoolName(mRequestedToCommitOrCancelComposition), - mCompositionEventsDuringRequest)); + mCommitStringByRequest)); + + MOZ_ASSERT(!mCommitStringByRequest); - mRequestedToCommitOrCancelComposition = true; - mCompositionEventsDuringRequest = 0; + RefPtr<TextComposition> composition = + IMEStateManager::GetTextCompositionFor(aWidget); + if (NS_WARN_IF(!composition)) { + MOZ_LOG(sContentCacheLog, LogLevel::Warning, + (" ContentCacheInParent: 0x%p RequestToCommitComposition(), " + "does nothing due to no composition", this)); + return false; + } + + mCommitStringByRequest = &aCommittedString; aWidget->NotifyIME(IMENotification(aCancel ? REQUEST_TO_CANCEL_COMPOSITION : REQUEST_TO_COMMIT_COMPOSITION)); - mRequestedToCommitOrCancelComposition = false; - aLastString = mCommitStringByRequest; - mCommitStringByRequest.Truncate(0); - return mCompositionEventsDuringRequest; + mCommitStringByRequest = nullptr; + + MOZ_LOG(sContentCacheLog, LogLevel::Info, + (" ContentCacheInParent: 0x%p RequestToCommitComposition(), " + "mIsComposing=%s, the composition %s committed synchronously", + this, GetBoolName(mIsComposing), + composition->Destroyed() ? "WAS" : "has NOT been")); + + if (!composition->Destroyed()) { + // When the composition isn't committed synchronously, the remote process's + // TextComposition instance will synthesize commit events and wait to + // receive delayed composition events. When TextComposition instances both + // in this process and the remote process will be destroyed when delayed + // composition events received. TextComposition instance in the parent + // process will dispatch following composition events and be destroyed + // normally. On the other hand, TextComposition instance in the remote + // process won't dispatch following composition events and will be + // destroyed by IMEStateManager::DispatchCompositionEvent(). + return false; + } + + // When the composition is committed synchronously, the commit string will be + // returned to the remote process. Then, PuppetWidget will dispatch + // eCompositionCommit event with the returned commit string (i.e., the value + // is aCommittedString of this method). Finally, TextComposition instance in + // the remote process will be destroyed by + // IMEStateManager::DispatchCompositionEvent() at receiving the + // eCompositionCommit event (Note that TextComposition instance in this + // process was already destroyed). + return true; } void ContentCacheInParent::MaybeNotifyIME(nsIWidget* aWidget, const IMENotification& aNotification) { if (!mPendingEventsNeedingAck) { IMEStateManager::NotifyIME(aNotification, aWidget, true);
--- a/widget/ContentCache.h +++ b/widget/ContentCache.h @@ -320,61 +320,59 @@ public: * * WARNING: This may send notifications to IME. That might cause destroying * TabParent or aWidget. Therefore, the caller must not destroy * this instance during a call of this method. */ void OnEventNeedingAckHandled(nsIWidget* aWidget, EventMessage aMessage); /** - * RequestToCommitComposition() requests to commit or cancel composition to - * the widget. If it's handled synchronously, this returns the number of - * composition events after that. + * RequestIMEToCommitComposition() requests aWidget to commit or cancel + * composition. If it's handled synchronously, this returns true. * * @param aWidget The widget to be requested to commit or cancel * the composition. * @param aCancel When the caller tries to cancel the composition, true. * Otherwise, i.e., tries to commit the composition, false. - * @param aLastString The last composition string before requesting to - * commit or cancel composition. - * @return The count of composition events ignored after a call of - * WillRequestToCommitOrCancelComposition(). + * @param aCommittedString The committed string (i.e., the last data of + * dispatched composition events during requesting + * IME to commit composition. + * @return Whether the composition is actually committed + * synchronously. */ - uint32_t RequestToCommitComposition(nsIWidget* aWidget, - bool aCancel, - nsAString& aLastString); + bool RequestIMEToCommitComposition(nsIWidget* aWidget, + bool aCancel, + nsAString& aCommittedString); /** * MaybeNotifyIME() may notify IME of the notification. If child process * hasn't been handled all sending events yet, this stores the notification * and flush it later. */ void MaybeNotifyIME(nsIWidget* aWidget, const IMENotification& aNotification); private: IMENotification mPendingSelectionChange; IMENotification mPendingTextChange; IMENotification mPendingLayoutChange; IMENotification mPendingCompositionUpdate; - // This is commit string which is caused by our request. - nsString mCommitStringByRequest; + // This is not nullptr only while the instance is requesting IME to + // composition. Then, data value of dispatched composition events should + // be stored into the instance. + nsAString* mCommitStringByRequest; // Start offset of the composition string. uint32_t mCompositionStart; - // Count of composition events during requesting commit or cancel the - // composition. - uint32_t mCompositionEventsDuringRequest; // mPendingEventsNeedingAck is increased before sending a composition event or // a selection event and decreased after they are received in the child // process. uint32_t mPendingEventsNeedingAck; bool mIsComposing; - bool mRequestedToCommitOrCancelComposition; bool GetCaretRect(uint32_t aOffset, LayoutDeviceIntRect& aCaretRect) const; bool GetTextRect(uint32_t aOffset, LayoutDeviceIntRect& aTextRect) const; bool GetUnionTextRects(uint32_t aOffset, uint32_t aLength, LayoutDeviceIntRect& aUnionTextRect) const;
--- a/widget/IMEData.h +++ b/widget/IMEData.h @@ -5,16 +5,18 @@ #ifndef mozilla_widget_IMEData_h_ #define mozilla_widget_IMEData_h_ #include "nsPoint.h" #include "nsRect.h" #include "nsStringGlue.h" +class nsIWidget; + namespace mozilla { class WritingMode; } // namespace mozilla /** * Preference for receiving IME updates @@ -218,21 +220,68 @@ struct IMEState final // a plain text editor whose ime-mode is "disabled" or a windowless plugin // has focus. bool MaybeEditable() const { return IsEditable() || mEnabled == PLUGIN; } }; +// NS_ONLY_ONE_NATIVE_IME_CONTEXT is a special value of native IME context. +// If there can be only one IME composition in a process, this can be used. +#define NS_ONLY_ONE_NATIVE_IME_CONTEXT \ + (reinterpret_cast<void*>(static_cast<intptr_t>(-1))) + +struct NativeIMEContext final +{ + // Pointer to native IME context. Typically this is the result of + // nsIWidget::GetNativeData(NS_RAW_NATIVE_IME_CONTEXT) in the parent process. + // See also NS_ONLY_ONE_NATIVE_IME_CONTEXT. + uintptr_t mRawNativeIMEContext; + // Process ID of the origin of mNativeIMEContext. + uint64_t mOriginProcessID; + + NativeIMEContext() + { + Init(nullptr); + } + + explicit NativeIMEContext(nsIWidget* aWidget) + { + Init(aWidget); + } + + bool IsValid() const + { + return mRawNativeIMEContext && + mOriginProcessID != static_cast<uintptr_t>(-1); + } + + void Init(nsIWidget* aWidget); + void InitWithRawNativeIMEContext(const void* aRawNativeIMEContext) + { + InitWithRawNativeIMEContext(const_cast<void*>(aRawNativeIMEContext)); + } + void InitWithRawNativeIMEContext(void* aRawNativeIMEContext); + + bool operator==(const NativeIMEContext& aOther) const + { + return mRawNativeIMEContext == aOther.mRawNativeIMEContext && + mOriginProcessID == aOther.mOriginProcessID; + } + bool operator!=(const NativeIMEContext& aOther) const + { + return !(*this == aOther); + } +}; + struct InputContext final { InputContext() - : mNativeIMEContext(nullptr) - , mOrigin(XRE_IsParentProcess() ? ORIGIN_MAIN : ORIGIN_CONTENT) + : mOrigin(XRE_IsParentProcess() ? ORIGIN_MAIN : ORIGIN_CONTENT) , mMayBeIMEUnaware(false) { } bool IsPasswordEditor() const { return mHTMLInputType.LowerCaseEqualsLiteral("password"); } @@ -243,21 +292,16 @@ struct InputContext final nsString mHTMLInputType; /* The type of the inputmode */ nsString mHTMLInputInputmode; /* A hint for the action that is performed when the input is submitted */ nsString mActionHint; - /* Native IME context for the widget. This doesn't come from the argument of - SetInputContext(). If there is only one context in the process, this may - be nullptr. */ - void* mNativeIMEContext; - /** * mOrigin indicates whether this focus event refers to main or remote * content. */ enum Origin { // Adjusting focus of content on the main process ORIGIN_MAIN,
--- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -74,19 +74,19 @@ const size_t PuppetWidget::kMaxDimension NS_IMPL_ISUPPORTS_INHERITED0(PuppetWidget, nsBaseWidget) PuppetWidget::PuppetWidget(TabChild* aTabChild) : mTabChild(aTabChild) , mMemoryPressureObserver(nullptr) , mDPI(-1) , mDefaultScale(-1) - , mNativeKeyCommandsValid(false) , mCursorHotspotX(0) , mCursorHotspotY(0) + , mNativeKeyCommandsValid(false) { MOZ_COUNT_CTOR(PuppetWidget); mSingleLineCommands.SetCapacity(4); mMultiLineCommands.SetCapacity(4); mRichTextCommands.SetCapacity(4); } @@ -156,16 +156,21 @@ PuppetWidget::CreateChild(const LayoutDe NS_SUCCEEDED(widget->Create(isPopup ? nullptr: this, nullptr, aRect, aInitData))) ? widget.forget() : nullptr); } NS_IMETHODIMP PuppetWidget::Destroy() { + if (mOnDestroyCalled) { + return NS_OK; + } + mOnDestroyCalled = true; + Base::OnDestroy(); Base::Destroy(); mPaintTask.Revoke(); if (mMemoryPressureObserver) { mMemoryPressureObserver->Remove(); } mMemoryPressureObserver = nullptr; mChild = nullptr; @@ -314,16 +319,34 @@ PuppetWidget::DispatchEvent(WidgetGUIEve AutoCacheNativeKeyCommands autoCache(this); if (event->mFlags.mIsSynthesizedForTests && !mNativeKeyCommandsValid) { WidgetKeyboardEvent* keyEvent = event->AsKeyboardEvent(); if (keyEvent) { mTabChild->RequestNativeKeyBindings(&autoCache, keyEvent); } } + if (event->mClass == eCompositionEventClass) { + // Store the latest native IME context of parent process's widget or + // TextEventDispatcher if it's in this process. + WidgetCompositionEvent* compositionEvent = event->AsCompositionEvent(); +#ifdef DEBUG + if (mNativeIMEContext.IsValid() && + mNativeIMEContext != compositionEvent->mNativeIMEContext) { + RefPtr<TextComposition> composition = + IMEStateManager::GetTextCompositionFor(this); + MOZ_ASSERT(!composition, + "When there is composition caused by old native IME context, " + "composition events caused by different native IME context are not " + "allowed"); + } +#endif // #ifdef DEBUG + mNativeIMEContext = compositionEvent->mNativeIMEContext; + } + aStatus = nsEventStatus_eIgnore; if (GetCurrentWidgetListener()) { aStatus = GetCurrentWidgetListener()->HandleEvent(event, mUseAttachedEvents); } return NS_OK; } @@ -563,50 +586,72 @@ PuppetWidget::GetLayerManager(PLayerTran } if (aAllowRetaining) { *aAllowRetaining = true; } return mLayerManager; } nsresult -PuppetWidget::IMEEndComposition(bool aCancel) +PuppetWidget::RequestIMEToCommitComposition(bool aCancel) { -#ifndef MOZ_CROSS_PROCESS_IME - return NS_OK; -#endif - - nsEventStatus status; - bool noCompositionEvent = true; - WidgetCompositionEvent compositionCommitEvent(true, eCompositionCommit, this); - InitEvent(compositionCommitEvent, nullptr); - // SendEndIMEComposition is always called since ResetInputState - // should always be called even if we aren't composing something. - if (!mTabChild || - !mTabChild->SendEndIMEComposition(aCancel, &noCompositionEvent, - &compositionCommitEvent.mData)) { +#ifdef MOZ_CROSS_PROCESS_IME + if (!mTabChild) { return NS_ERROR_FAILURE; } - if (noCompositionEvent) { + MOZ_ASSERT(!Destroyed()); + + // There must not be composition which is caused by the PuppetWidget instance. + if (NS_WARN_IF(!mNativeIMEContext.IsValid())) { + return NS_OK; + } + + RefPtr<TextComposition> composition = + IMEStateManager::GetTextCompositionFor(this); + // This method shouldn't be called when there is no text composition instance. + if (NS_WARN_IF(!composition)) { return NS_OK; } + bool isCommitted = false; + nsAutoString committedString; + if (NS_WARN_IF(!mTabChild->SendRequestIMEToCommitComposition( + aCancel, &isCommitted, &committedString))) { + return NS_ERROR_FAILURE; + } + + // If the composition wasn't committed synchronously, we need to wait async + // composition events for destroying the TextComposition instance. + if (!isCommitted) { + return NS_OK; + } + + // Dispatch eCompositionCommit event. + WidgetCompositionEvent compositionCommitEvent(true, eCompositionCommit, this); + InitEvent(compositionCommitEvent, nullptr); + compositionCommitEvent.mData = committedString; + nsEventStatus status = nsEventStatus_eIgnore; DispatchEvent(&compositionCommitEvent, status); + + // NOTE: PuppetWidget might be destroyed already. + +#endif // #ifdef MOZ_CROSS_PROCESS_IME + return NS_OK; } nsresult PuppetWidget::NotifyIMEInternal(const IMENotification& aIMENotification) { switch (aIMENotification.mMessage) { case REQUEST_TO_COMMIT_COMPOSITION: - return IMEEndComposition(false); + return RequestIMEToCommitComposition(false); case REQUEST_TO_CANCEL_COMPOSITION: - return IMEEndComposition(true); + return RequestIMEToCommitComposition(true); case NOTIFY_IME_OF_FOCUS: case NOTIFY_IME_OF_BLUR: return NotifyIMEOfFocusChange(aIMENotification); case NOTIFY_IME_OF_SELECTION_CHANGE: return NotifyIMEOfSelectionChange(aIMENotification); case NOTIFY_IME_OF_TEXT_CHANGE: return NotifyIMEOfTextChange(aIMENotification); case NOTIFY_IME_OF_COMPOSITION_UPDATE: @@ -670,25 +715,30 @@ PuppetWidget::GetInputContext() { #ifndef MOZ_CROSS_PROCESS_IME return InputContext(); #endif InputContext context; if (mTabChild) { int32_t enabled, open; - intptr_t nativeIMEContext; - mTabChild->SendGetInputContext(&enabled, &open, &nativeIMEContext); + // TODO: This is too expensive. PuppetWidget should cache IMEState. + mTabChild->SendGetInputContext(&enabled, &open); context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(enabled); context.mIMEState.mOpen = static_cast<IMEState::Open>(open); - context.mNativeIMEContext = reinterpret_cast<void*>(nativeIMEContext); } return context; } +NS_IMETHODIMP_(NativeIMEContext) +PuppetWidget::GetNativeIMEContext() +{ + return mNativeIMEContext; +} + nsresult PuppetWidget::NotifyIMEOfFocusChange(const IMENotification& aIMENotification) { #ifndef MOZ_CROSS_PROCESS_IME return NS_OK; #endif if (!mTabChild) @@ -1116,16 +1166,18 @@ PuppetWidget::GetNativeData(uint32_t aDa mTabChild->SendGetWidgetNativeData(&nativeData); } return (void*)nativeData; } case NS_NATIVE_WIDGET: case NS_NATIVE_DISPLAY: // These types are ignored (see bug 1183828). break; + case NS_RAW_NATIVE_IME_CONTEXT: + MOZ_CRASH("You need to call GetNativeIMEContext() instead"); case NS_NATIVE_WINDOW: case NS_NATIVE_PLUGIN_PORT: case NS_NATIVE_GRAPHIC: case NS_NATIVE_SHELLWIDGET: default: NS_WARNING("nsWindow::GetNativeData called with bad value"); break; }
--- a/widget/PuppetWidget.h +++ b/widget/PuppetWidget.h @@ -174,16 +174,17 @@ public: GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr, LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE, LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT, bool* aAllowRetaining = nullptr) override; NS_IMETHOD_(void) SetInputContext(const InputContext& aContext, const InputContextAction& aAction) override; NS_IMETHOD_(InputContext) GetInputContext() override; + NS_IMETHOD_(NativeIMEContext) GetNativeIMEContext() override; virtual nsIMEUpdatePreference GetIMEUpdatePreference() override; NS_IMETHOD SetCursor(nsCursor aCursor) override; NS_IMETHOD SetCursor(imgIContainer* aCursor, uint32_t aHotspotX, uint32_t aHotspotY) override; virtual void ClearCachedCursor() override; @@ -249,28 +250,25 @@ public: virtual nsresult SynthesizeNativeTouchTap(ScreenIntPoint aPointerScreenPoint, bool aLongTap, nsIObserver* aObserver) override; virtual nsresult ClearNativeTouchSequence(nsIObserver* aObserver) override; virtual uint32_t GetMaxTouchPoints() const override; virtual void StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) override; protected: - bool mEnabled; - bool mVisible; - virtual nsresult NotifyIMEInternal( const IMENotification& aIMENotification) override; private: nsresult Paint(); void SetChild(PuppetWidget* aChild); - nsresult IMEEndComposition(bool aCancel); + nsresult RequestIMEToCommitComposition(bool aCancel); nsresult NotifyIMEOfFocusChange(const IMENotification& aIMENotification); nsresult NotifyIMEOfSelectionChange(const IMENotification& aIMENotification); nsresult NotifyIMEOfCompositionUpdate(const IMENotification& aIMENotification); nsresult NotifyIMEOfTextChange(const IMENotification& aIMENotification); nsresult NotifyIMEOfMouseButtonEvent(const IMENotification& aIMENotification); nsresult NotifyIMEOfPositionChange(const IMENotification& aIMENotification); bool CacheEditorRect(); @@ -317,31 +315,43 @@ private: nsRevocableEventPtr<PaintTask> mPaintTask; RefPtr<MemoryPressureObserver> mMemoryPressureObserver; // XXX/cjones: keeping this around until we teach LayerManager to do // retained-content-only transactions RefPtr<DrawTarget> mDrawTarget; // IME nsIMEUpdatePreference mIMEPreferenceOfParent; InputContext mInputContext; + // mNativeIMEContext is initialized when this dispatches every composition + // event both from parent process's widget and TextEventDispatcher in same + // process. If it hasn't been started composition yet, this isn't necessary + // for XP code since there is no TextComposition instance which is caused by + // the PuppetWidget instance. + NativeIMEContext mNativeIMEContext; ContentCacheInChild mContentCache; - bool mNeedIMEStateInit; // The DPI of the screen corresponding to this widget float mDPI; double mDefaultScale; // Precomputed answers for ExecuteNativeKeyBinding - bool mNativeKeyCommandsValid; InfallibleTArray<mozilla::CommandInt> mSingleLineCommands; InfallibleTArray<mozilla::CommandInt> mMultiLineCommands; InfallibleTArray<mozilla::CommandInt> mRichTextCommands; nsCOMPtr<imgIContainer> mCustomCursor; uint32_t mCursorHotspotX, mCursorHotspotY; + +protected: + bool mEnabled; + bool mVisible; + +private: + bool mNeedIMEStateInit; + bool mNativeKeyCommandsValid; }; struct AutoCacheNativeKeyCommands { explicit AutoCacheNativeKeyCommands(PuppetWidget* aWidget) : mWidget(aWidget) { mSavedValid = mWidget->mNativeKeyCommandsValid;
--- a/widget/TextEventDispatcher.cpp +++ b/widget/TextEventDispatcher.cpp @@ -129,16 +129,27 @@ TextEventDispatcher::GetState() const } void TextEventDispatcher::InitEvent(WidgetGUIEvent& aEvent) const { aEvent.time = PR_IntervalNow(); aEvent.refPoint = LayoutDeviceIntPoint(0, 0); aEvent.mFlags.mIsSynthesizedForTests = mForTests; + if (aEvent.mClass != eCompositionEventClass) { + return; + } + // Currently, we should set special native IME context when composition + // events are dispatched from PuppetWidget since PuppetWidget may have not + // known actual native IME context yet and it caches native IME context + // when it dispatches every WidgetCompositionEvent. + if (XRE_IsContentProcess()) { + aEvent.AsCompositionEvent()-> + mNativeIMEContext.InitWithRawNativeIMEContext(mWidget); + } } nsresult TextEventDispatcher::DispatchEvent(nsIWidget* aWidget, WidgetGUIEvent& aEvent, nsEventStatus& aStatus) { MOZ_ASSERT(!aEvent.AsInputEvent(), "Use DispatchInputEvent()");
--- a/widget/TextEvents.h +++ b/widget/TextEvents.h @@ -361,16 +361,17 @@ public: virtual WidgetCompositionEvent* AsCompositionEvent() override { return this; } WidgetCompositionEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget) : WidgetGUIEvent(aIsTrusted, aMessage, aWidget, eCompositionEventClass) + , mNativeIMEContext(aWidget) , mOriginalMessage(eVoidEvent) { // XXX compositionstart is cancelable in draft of DOM3 Events. // However, it doesn't make sense for us, we cannot cancel composition // when we send compositionstart event. mFlags.mCancelable = false; } @@ -388,16 +389,20 @@ public: // The composition string or the commit string. If the instance is a // compositionstart event, this is initialized with selected text by // TextComposition automatically. nsString mData; RefPtr<TextRangeArray> mRanges; + // mNativeIMEContext stores the native IME context which causes the + // composition event. + widget::NativeIMEContext mNativeIMEContext; + // If the instance is a clone of another event, mOriginalMessage stores // the another event's mMessage. EventMessage mOriginalMessage; void AssignCompositionEventData(const WidgetCompositionEvent& aEvent, bool aCopyTargets) { AssignGUIEventData(aEvent, aCopyTargets);
--- a/widget/android/AndroidJNI.cpp +++ b/widget/android/AndroidJNI.cpp @@ -398,28 +398,14 @@ Java_org_mozilla_gecko_gfx_NativePanZoom NS_EXPORT jboolean JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_getRedrawHint(JNIEnv* env, jobject instance) { // FIXME implement this return true; } NS_EXPORT void JNICALL -Java_org_mozilla_gecko_gfx_NativePanZoomController_setOverScrollMode(JNIEnv* env, jobject instance, jint overscrollMode) -{ - // FIXME implement this -} - -NS_EXPORT jint JNICALL -Java_org_mozilla_gecko_gfx_NativePanZoomController_getOverScrollMode(JNIEnv* env, jobject instance) -{ - // FIXME implement this - return 0; -} - - -NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_setIsLongpressEnabled(JNIEnv* env, jobject instance, jboolean isLongpressEnabled) { APZCTreeManager::SetLongTapEnabled(isLongpressEnabled); } }
--- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1258,16 +1258,21 @@ nsWindow::GetNativeData(uint32_t aDataTy { switch (aDataType) { // used by GLContextProviderEGL, nullptr is EGL_DEFAULT_DISPLAY case NS_NATIVE_DISPLAY: return nullptr; case NS_NATIVE_WIDGET: return (void *) this; + + case NS_RAW_NATIVE_IME_CONTEXT: + // We assume that there is only one context per process on Android + return NS_ONLY_ONE_NATIVE_IME_CONTEXT; + } return nullptr; } void nsWindow::OnMouseEvent(AndroidGeckoEvent *ae) { @@ -2239,18 +2244,16 @@ nsWindow::Natives::SetInputContext(const }); } InputContext nsWindow::Natives::GetInputContext() { InputContext context = mInputContext; context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED; - // We assume that there is only one context per process on Android - context.mNativeIMEContext = nullptr; return context; } void nsWindow::Natives::OnImeSynchronize() { if (!mIMEMaskEventsCount) { FlushIMEChanges();
--- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -679,16 +679,27 @@ void* nsChildView::GetNativeData(uint32_ case NS_NATIVE_OFFSETX: retVal = 0; break; case NS_NATIVE_OFFSETY: retVal = 0; break; + + case NS_RAW_NATIVE_IME_CONTEXT: + retVal = [mView inputContext]; + // If input context isn't available on this widget, we should set |this| + // instead of nullptr since if this returns nullptr, IMEStateManager + // cannot manage composition with TextComposition instance. Although, + // this case shouldn't occur. + if (NS_WARN_IF(!retVal)) { + retVal = this; + } + break; } return retVal; NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL; } #pragma mark - @@ -1764,23 +1775,16 @@ nsChildView::GetInputContext() break; } // If mTextInputHandler is null, set CLOSED instead... MOZ_FALLTHROUGH; default: mInputContext.mIMEState.mOpen = IMEState::CLOSED; break; } - mInputContext.mNativeIMEContext = [mView inputContext]; - // If input context isn't available on this widget, we should set |this| - // instead of nullptr since nullptr means that the platform has only one - // context per process. - if (!mInputContext.mNativeIMEContext) { - mInputContext.mNativeIMEContext = this; - } return mInputContext; } NS_IMETHODIMP nsChildView::AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) { NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE); return mTextInputHandler->AttachNativeKeyEvent(aEvent);
--- a/widget/cocoa/nsCocoaWindow.h +++ b/widget/cocoa/nsCocoaWindow.h @@ -360,26 +360,16 @@ public: void SetMenuBar(nsMenuBarX* aMenuBar); nsMenuBarX *GetMenuBar(); NS_IMETHOD_(void) SetInputContext( const InputContext& aContext, const InputContextAction& aAction) override; NS_IMETHOD_(InputContext) GetInputContext() override { - NSView* view = mWindow ? [mWindow contentView] : nil; - if (view) { - mInputContext.mNativeIMEContext = [view inputContext]; - } - // If inputContext isn't available on this window, returns this window's - // pointer since nullptr means the platform has only one context per - // process. - if (!mInputContext.mNativeIMEContext) { - mInputContext.mNativeIMEContext = this; - } return mInputContext; } NS_IMETHOD_(bool) ExecuteNativeKeyBinding( NativeKeyBindingsType aType, const mozilla::WidgetKeyboardEvent& aEvent, DoCommandCallback aCallback, void* aCallbackData) override;
--- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -587,16 +587,30 @@ void* nsCocoaWindow::GetNativeData(uint3 retVal = mWindow; break; case NS_NATIVE_GRAPHIC: // There isn't anything that makes sense to return here, // and it doesn't matter so just return nullptr. NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a top-level window!"); break; + case NS_RAW_NATIVE_IME_CONTEXT: { + NSView* view = mWindow ? [mWindow contentView] : nil; + if (view) { + retVal = [view inputContext]; + } + // If inputContext isn't available on this window, return this window's + // pointer instead of nullptr since if this returns nullptr, + // IMEStateManager cannot manage composition with TextComposition + // instance. Although, this case shouldn't occur. + if (NS_WARN_IF(!retVal)) { + retVal = this; + } + break; + } } return retVal; NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL; } bool nsCocoaWindow::IsVisible() const
--- a/widget/gonk/nsClipboard.cpp +++ b/widget/gonk/nsClipboard.cpp @@ -115,23 +115,22 @@ nsClipboard::SetData(nsITransferable *aT nsAutoString utf16string; wideString->GetData(utf16string); mClipboard->SetHTML(utf16string); } else if (!imageAdded && // image is added only once to the clipboard. (flavorStr.EqualsLiteral(kNativeImageMime) || flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || - flavorStr.EqualsLiteral(kJPGImageMime) || - flavorStr.EqualsLiteral(kGIFImageMime))) { - // image/[png|jpeg|jpg|gif] or application/x-moz-nativeimage + flavorStr.EqualsLiteral(kJPGImageMime))) { + // image/[png|jpeg|jpg] or application/x-moz-nativeimage // Look through our transfer data for the image. static const char* const imageMimeTypes[] = { - kNativeImageMime, kPNGImageMime, kJPEGImageMime, kJPGImageMime, kGIFImageMime }; + kNativeImageMime, kPNGImageMime, kJPEGImageMime, kJPGImageMime }; nsCOMPtr<nsISupportsInterfacePointer> imgPtr; for (uint32_t i = 0; !imgPtr && i < ArrayLength(imageMimeTypes); ++i) { aTransferable->GetTransferData(imageMimeTypes[i], getter_AddRefs(clip), &len); imgPtr = do_QueryInterface(clip); } if (!imgPtr) { continue; @@ -254,21 +253,20 @@ nsClipboard::GetData(nsITransferable *aT uint32_t len = mClipboard->GetHTML().Length() * sizeof(char16_t); rv = aTransferable->SetTransferData(flavorStr, genericDataWrapper, len); if (NS_WARN_IF(NS_FAILED(rv))) { continue; } break; } - // image/[png|jpeg|jpg|gif] + // image/[png|jpeg|jpg] if ((flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || - flavorStr.EqualsLiteral(kJPGImageMime) || - flavorStr.EqualsLiteral(kGIFImageMime)) && + flavorStr.EqualsLiteral(kJPGImageMime)) && mClipboard->HasImage() ) { // Get image buffer from clipboard. RefPtr<gfx::DataSourceSurface> image = mClipboard->GetImage(); // Encode according to MIME type. RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, image->GetSize()); nsCOMPtr<imgIContainer> imageContainer(image::ImageOps::CreateFromDrawable(drawable)); nsCOMPtr<imgITools> imgTool = do_GetService(NS_IMGTOOLS_CID); @@ -323,18 +321,17 @@ nsClipboard::HasDataMatchingFlavors(cons continue; } if (!strcmp(flavor, kUnicodeMime) && mClipboard->HasText()) { *aHasType = true; } else if (!strcmp(flavor, kHTMLMime) && mClipboard->HasHTML()) { *aHasType = true; } else if (!strcmp(flavor, kJPEGImageMime) || !strcmp(flavor, kJPGImageMime) || - !strcmp(flavor, kPNGImageMime) || - !strcmp(flavor, kGIFImageMime)) { + !strcmp(flavor, kPNGImageMime)) { // We will encode the image into any format you want, so we don't // need to check each specific format if (mClipboard->HasImage()) { *aHasType = true; } } } } else {
--- a/widget/gonk/nsWindow.cpp +++ b/widget/gonk/nsWindow.cpp @@ -534,16 +534,19 @@ void* nsWindow::GetNativeData(uint32_t aDataType) { switch (aDataType) { case NS_NATIVE_WINDOW: // Called before primary display's EGLSurface creation. return mScreen->GetNativeWindow(); case NS_NATIVE_OPENGL_CONTEXT: return mScreen->GetGLContext().take(); + case NS_RAW_NATIVE_IME_CONTEXT: + // There is only one IME context on Gonk. + return NS_ONLY_ONE_NATIVE_IME_CONTEXT; } return nullptr; } void nsWindow::SetNativeData(uint32_t aDataType, uintptr_t aVal) { @@ -577,18 +580,16 @@ nsWindow::SetInputContext(const InputCon const InputContextAction& aAction) { mInputContext = aContext; } NS_IMETHODIMP_(InputContext) nsWindow::GetInputContext() { - // There is only one IME context on Gonk. - mInputContext.mNativeIMEContext = nullptr; return mInputContext; } NS_IMETHODIMP nsWindow::ReparentNativeWidget(nsIWidget* aNewParent) { return NS_OK; }
--- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -1732,16 +1732,25 @@ nsWindow::GetNativeData(uint32_t aDataTy case NS_NATIVE_SHELLWIDGET: return GetToplevelWidget(); case NS_NATIVE_SHAREABLE_WINDOW: return (void *) GDK_WINDOW_XID(gdk_window_get_toplevel(mGdkWindow)); case NS_NATIVE_PLUGIN_OBJECT_PTR: return (void *) mPluginNativeWindow; + case NS_RAW_NATIVE_IME_CONTEXT: + // If IME context isn't available on this widget, we should set |this| + // instead of nullptr since if we return nullptr, IMEStateManager + // cannot manage composition with TextComposition instance. Although, + // this case shouldn't occur. + if (NS_WARN_IF(!mIMContext)) { + return this; + } + return mIMContext.get(); default: NS_WARNING("nsWindow::GetNativeData called with bad value"); return nullptr; } } void nsWindow::SetNativeData(uint32_t aDataType, uintptr_t aVal) @@ -6290,23 +6299,18 @@ nsWindow::SetInputContext(const InputCon NS_IMETHODIMP_(InputContext) nsWindow::GetInputContext() { InputContext context; if (!mIMContext) { context.mIMEState.mEnabled = IMEState::DISABLED; context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED; - // If IME context isn't available on this widget, we should set |this| - // instead of nullptr since nullptr means that the platform has only one - // context per process. - context.mNativeIMEContext = this; } else { context = mIMContext->GetInputContext(); - context.mNativeIMEContext = mIMContext; } return context; } nsIMEUpdatePreference nsWindow::GetIMEUpdatePreference() { if (!mIMContext) {
--- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -48,16 +48,17 @@ #include "mozilla/IMEStateManager.h" #include "mozilla/VsyncDispatcher.h" #include "mozilla/layers/APZCTreeManager.h" #include "mozilla/layers/APZEventState.h" #include "mozilla/layers/APZThreadUtils.h" #include "mozilla/layers/ChromeProcessController.h" #include "mozilla/layers/InputAPZContext.h" #include "mozilla/layers/APZCCallbackHelper.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/TabParent.h" #include "mozilla/Move.h" #include "mozilla/Services.h" #include "mozilla/Snprintf.h" #include "nsRefPtrHashtable.h" #include "TouchEvents.h" #include "WritingModes.h" #include "InputData.h" @@ -81,16 +82,17 @@ static int32_t gNumWidgets; #endif #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) static nsRefPtrHashtable<nsVoidPtrHashKey, nsIWidget>* sPluginWidgetList; #endif nsIRollupListener* nsBaseWidget::gRollupListener = nullptr; +using namespace mozilla::dom; using namespace mozilla::layers; using namespace mozilla::ipc; using namespace mozilla::widget; using namespace mozilla; using base::Thread; nsIContent* nsBaseWidget::mLastRollup = nullptr; // Global user preference for disabling native theme. Used @@ -2074,20 +2076,60 @@ nsIWidget::SnapshotWidgetOnScreen() gfx::Rect(gfx::Point(), gfx::Size(size)), gfx::Rect(gfx::Point(), gfx::Size(size)), gfx::DrawSurfaceOptions(gfx::Filter::POINT)); forwarder->DestroySharedSurface(&surface); return dt->Snapshot(); } +NS_IMETHODIMP_(nsIWidget::NativeIMEContext) +nsIWidget::GetNativeIMEContext() +{ + return NativeIMEContext(this); +} + namespace mozilla { namespace widget { void +NativeIMEContext::Init(nsIWidget* aWidget) +{ + if (!aWidget) { + mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr); + mOriginProcessID = static_cast<uint64_t>(-1); + return; + } + if (!XRE_IsContentProcess()) { + mRawNativeIMEContext = reinterpret_cast<uintptr_t>( + aWidget->GetNativeData(NS_RAW_NATIVE_IME_CONTEXT)); + mOriginProcessID = 0; + return; + } + // If this is created in a child process, aWidget is an instance of + // PuppetWidget which doesn't support NS_RAW_NATIVE_IME_CONTEXT. + // Instead of that PuppetWidget::GetNativeIMEContext() returns cached + // native IME context of the parent process. + *this = aWidget->GetNativeIMEContext(); +} + +void +NativeIMEContext::InitWithRawNativeIMEContext(void* aRawNativeIMEContext) +{ + if (NS_WARN_IF(!aRawNativeIMEContext)) { + mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr); + mOriginProcessID = static_cast<uint64_t>(-1); + return; + } + mRawNativeIMEContext = reinterpret_cast<uintptr_t>(aRawNativeIMEContext); + mOriginProcessID = + XRE_IsContentProcess() ? ContentChild::GetSingleton()->GetID() : 0; +} + +void IMENotification::TextChangeDataBase::MergeWith( const IMENotification::TextChangeDataBase& aOther) { MOZ_ASSERT(aOther.IsValid(), "Merging data must store valid data"); MOZ_ASSERT(aOther.mStartOffset <= aOther.mRemovedEndOffset, "end of removed text must be same or larger than start"); MOZ_ASSERT(aOther.mStartOffset <= aOther.mAddedEndOffset,
--- a/widget/nsGUIEventIPC.h +++ b/widget/nsGUIEventIPC.h @@ -539,29 +539,31 @@ template<> struct ParamTraits<mozilla::WidgetCompositionEvent> { typedef mozilla::WidgetCompositionEvent paramType; static void Write(Message* aMsg, const paramType& aParam) { WriteParam(aMsg, static_cast<mozilla::WidgetGUIEvent>(aParam)); WriteParam(aMsg, aParam.mData); + WriteParam(aMsg, aParam.mNativeIMEContext); bool hasRanges = !!aParam.mRanges; WriteParam(aMsg, hasRanges); if (hasRanges) { WriteParam(aMsg, *aParam.mRanges.get()); } } static bool Read(const Message* aMsg, void** aIter, paramType* aResult) { bool hasRanges; if (!ReadParam(aMsg, aIter, static_cast<mozilla::WidgetGUIEvent*>(aResult)) || !ReadParam(aMsg, aIter, &aResult->mData) || + !ReadParam(aMsg, aIter, &aResult->mNativeIMEContext) || !ReadParam(aMsg, aIter, &hasRanges)) { return false; } if (!hasRanges) { aResult->mRanges = nullptr; } else { aResult->mRanges = new mozilla::TextRangeArray(); @@ -677,16 +679,34 @@ struct ParamTraits<nsIMEUpdatePreference static bool Read(const Message* aMsg, void** aIter, paramType* aResult) { return ReadParam(aMsg, aIter, &aResult->mWantUpdates); } }; template<> +struct ParamTraits<mozilla::widget::NativeIMEContext> +{ + typedef mozilla::widget::NativeIMEContext paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mRawNativeIMEContext); + WriteParam(aMsg, aParam.mOriginProcessID); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->mRawNativeIMEContext) && + ReadParam(aMsg, aIter, &aResult->mOriginProcessID); + } +}; + +template<> struct ParamTraits<mozilla::widget::IMENotification::Point> { typedef mozilla::widget::IMENotification::Point paramType; static void Write(Message* aMsg, const paramType& aParam) { WriteParam(aMsg, aParam.mX); WriteParam(aMsg, aParam.mY);
--- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -99,17 +99,24 @@ typedef void* nsNativeWidget; #define NS_NATIVE_SCREEN 9 // The toplevel GtkWidget containing this nsIWidget: #define NS_NATIVE_SHELLWIDGET 10 // Has to match to NPNVnetscapeWindow, and shareable across processes // HWND on Windows and XID on X11 #define NS_NATIVE_SHAREABLE_WINDOW 11 #define NS_NATIVE_OPENGL_CONTEXT 12 // See RegisterPluginWindowForRemoteUpdates -#define NS_NATIVE_PLUGIN_ID 13 +#define NS_NATIVE_PLUGIN_ID 13 +// This is available only with GetNativeData() in parent process. Anybody +// shouldn't access this pointer as a valid pointer since the result may be +// special value like NS_ONLY_ONE_NATIVE_IME_CONTEXT. So, the result is just +// an identifier of distinguishing a text composition is caused by which native +// IME context. Note that the result is only valid in the process. So, +// XP code should use nsIWidget::GetNativeIMEContext() instead of using this. +#define NS_RAW_NATIVE_IME_CONTEXT 14 #ifdef XP_MACOSX #define NS_NATIVE_PLUGIN_PORT_QD 100 #define NS_NATIVE_PLUGIN_PORT_CG 101 #endif #ifdef XP_WIN #define NS_NATIVE_TSF_THREAD_MGR 100 #define NS_NATIVE_TSF_CATEGORY_MGR 101 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102 @@ -118,18 +125,18 @@ typedef void* nsNativeWidget; #define NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW 105 #endif #if defined(MOZ_WIDGET_GTK) // set/get nsPluginNativeWindowGtk, e10s specific #define NS_NATIVE_PLUGIN_OBJECT_PTR 104 #endif #define NS_IWIDGET_IID \ -{ 0xd953b7a1, 0x6981, 0x4ed7, \ - { 0xbc, 0xf0, 0xed, 0x96, 0x70, 0xee, 0x23, 0x28 } } +{ 0xaaa79c8d, 0xc99d, 0x4fe1, \ + { 0xa5, 0x11, 0xd3, 0xeb, 0xb1, 0x61, 0x9e, 0x26 } } /* * Window shadow styles * Also used for the -moz-window-shadow CSS property */ #define NS_STYLE_WINDOW_SHADOW_NONE 0 #define NS_STYLE_WINDOW_SHADOW_DEFAULT 1 @@ -324,16 +331,17 @@ class nsIWidget : public nsISupports { typedef mozilla::layers::LayersBackend LayersBackend; typedef mozilla::layers::PLayerTransactionChild PLayerTransactionChild; typedef mozilla::layers::ZoomConstraints ZoomConstraints; typedef mozilla::widget::IMEMessage IMEMessage; typedef mozilla::widget::IMENotification IMENotification; typedef mozilla::widget::IMEState IMEState; typedef mozilla::widget::InputContext InputContext; typedef mozilla::widget::InputContextAction InputContextAction; + typedef mozilla::widget::NativeIMEContext NativeIMEContext; typedef mozilla::widget::SizeConstraints SizeConstraints; typedef mozilla::widget::TextEventDispatcher TextEventDispatcher; typedef mozilla::CompositorVsyncDispatcher CompositorVsyncDispatcher; typedef mozilla::LayoutDeviceIntMargin LayoutDeviceIntMargin; typedef mozilla::LayoutDeviceIntPoint LayoutDeviceIntPoint; typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect; typedef mozilla::LayoutDeviceIntRegion LayoutDeviceIntRegion; typedef mozilla::LayoutDeviceIntSize LayoutDeviceIntSize; @@ -1786,16 +1794,23 @@ public: NS_IMETHOD_(void) SetInputContext(const InputContext& aContext, const InputContextAction& aAction) = 0; /* * Get current input context. */ NS_IMETHOD_(InputContext) GetInputContext() = 0; + /** + * Get native IME context. This is different from GetNativeData() with + * NS_RAW_NATIVE_IME_CONTEXT, the result is unique even if in a remote + * process. + */ + NS_IMETHOD_(NativeIMEContext) GetNativeIMEContext(); + /* * Given a WidgetKeyboardEvent, this method synthesizes a corresponding * native (OS-level) event for it. This method allows tests to simulate * keystrokes that trigger native key bindings (which require a native * event). */ NS_IMETHOD AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) = 0;
--- a/widget/qt/nsWindow.cpp +++ b/widget/qt/nsWindow.cpp @@ -659,16 +659,20 @@ nsWindow::GetNativeData(uint32_t aDataTy #endif break; } case NS_NATIVE_PLUGIN_PORT: case NS_NATIVE_GRAPHIC: case NS_NATIVE_SHELLWIDGET: { break; } + case NS_RAW_NATIVE_IME_CONTEXT: + // Our qt widget looks like using only one context per process. + // However, it's better to set the context's pointer. + return qApp->inputMethod(); default: NS_WARNING("nsWindow::GetNativeData called with bad value"); return nullptr; } LOG(("nsWindow::%s [%p] aDataType:%i\n", __FUNCTION__, (void *)this, aDataType)); return nullptr; } @@ -711,20 +715,16 @@ nsWindow::SetInputContext(const InputCon break; } } NS_IMETHODIMP_(InputContext) nsWindow::GetInputContext() { mInputContext.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED; - // Our qt widget looks like using only one context per process. - // However, it's better to set the context's pointer. - mInputContext.mNativeIMEContext = qApp->inputMethod(); - return mInputContext; } NS_IMETHODIMP nsWindow::ReparentNativeWidget(nsIWidget *aNewParent) { NS_PRECONDITION(aNewParent, "");
--- a/widget/uikit/nsWindow.mm +++ b/widget/uikit/nsWindow.mm @@ -831,17 +831,16 @@ nsWindow::SetInputContext(const InputCon { //TODO: actually show VKB mInputContext = aContext; } NS_IMETHODIMP_(mozilla::widget::InputContext) nsWindow::GetInputContext() { - mInputContext.mNativeIMEContext = nullptr; return mInputContext; } void nsWindow::SetBackgroundColor(const nscolor &aColor) { mNativeView.backgroundColor = [UIColor colorWithRed:NS_GET_R(aColor) green:NS_GET_G(aColor) @@ -874,16 +873,20 @@ void* nsWindow::GetNativeData(uint32_t a case NS_NATIVE_OFFSETY: retVal = 0; break; case NS_NATIVE_PLUGIN_PORT: // not implemented break; + + case NS_RAW_NATIVE_IME_CONTEXT: + retVal = NS_ONLY_ONE_NATIVE_IME_CONTEXT; + break; } return retVal; } CGFloat nsWindow::BackingScaleFactor() {
--- a/widget/windows/IMMHandler.cpp +++ b/widget/windows/IMMHandler.cpp @@ -181,16 +181,40 @@ IMEContext::IMEContext(HWND aWnd) } IMEContext::IMEContext(nsWindow* aWindow) : mWnd(aWindow->GetWindowHandle()) , mIMC(::ImmGetContext(aWindow->GetWindowHandle())) { } +void +IMEContext::Init(HWND aWnd) +{ + Clear(); + mWnd = aWnd; + mIMC = ::ImmGetContext(mWnd); +} + +void +IMEContext::Init(nsWindow* aWindow) +{ + Init(aWindow->GetWindowHandle()); +} + +void +IMEContext::Clear() +{ + if (mWnd && mIMC) { + ::ImmReleaseContext(mWnd, mIMC); + } + mWnd = nullptr; + mIMC = nullptr; +} + /****************************************************************************** * IMMHandler ******************************************************************************/ static UINT sWM_MSIME_MOUSE = 0; // mouse message for MSIME 98/2000 WritingMode IMMHandler::sWritingModeOfCompositionFont; nsString IMMHandler::sIMEName;
--- a/widget/windows/IMMHandler.h +++ b/widget/windows/IMMHandler.h @@ -21,32 +21,39 @@ class nsWindow; namespace mozilla { namespace widget { struct MSGResult; class IMEContext final { public: + IMEContext() + : mWnd(nullptr) + , mIMC(nullptr) + { + } + explicit IMEContext(HWND aWnd); explicit IMEContext(nsWindow* aWindow); ~IMEContext() { - if (mIMC) { - ::ImmReleaseContext(mWnd, mIMC); - mIMC = nullptr; - } + Clear(); } HIMC get() const { return mIMC; } + void Init(HWND aWnd); + void Init(nsWindow* aWindow); + void Clear(); + bool IsValid() const { return !!mIMC; } void SetOpenState(bool aOpen) const { if (!mIMC) { @@ -85,21 +92,16 @@ public: return false; } ::ImmReleaseContext(mWnd, mIMC); mIMC = nullptr; return true; } protected: - IMEContext() - { - MOZ_CRASH("Don't create IMEContext without window handle"); - } - IMEContext(const IMEContext& aOther) { MOZ_CRASH("Don't copy IMEContext"); } HWND mWnd; HIMC mIMC; };
--- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -89,18 +89,41 @@ IMEHandler::Terminate() } #endif // #ifdef NS_ENABLE_TSF IMMHandler::Terminate(); } // static void* -IMEHandler::GetNativeData(uint32_t aDataType) +IMEHandler::GetNativeData(nsWindow* aWindow, uint32_t aDataType) { + if (aDataType == NS_RAW_NATIVE_IME_CONTEXT) { +#ifdef NS_ENABLE_TSF + if (IsTSFAvailable()) { + return TSFTextStore::GetThreadManager(); + } +#endif // #ifdef NS_ENABLE_TSF + IMEContext context(aWindow); + if (context.IsValid()) { + return context.get(); + } + // If IMC isn't associated with the window, IME is disabled on the window + // now. In such case, we should return default IMC instead. + const IMEContext& defaultIMC = aWindow->DefaultIMC(); + if (defaultIMC.IsValid()) { + return defaultIMC.get(); + } + // If there is no default IMC, we should return the pointer to the window + // since if we return nullptr, IMEStateManager cannot manage composition + // with TextComposition instance. This is possible if no IME is installed, + // but composition may occur with dead key sequence. + return aWindow; + } + #ifdef NS_ENABLE_TSF void* result = TSFTextStore::GetNativeData(aDataType); if (!result || !(*(static_cast<void**>(result)))) { return nullptr; } // XXX During the TSF module test, sIsInTSFMode must be true. After that, // the value should be restored but currently, there is no way for that. // When the TSF test is enabled again, we need to fix this. Perhaps, @@ -379,24 +402,21 @@ IMEHandler::SetInputContext(nsWindow* aW } bool enable = WinUtils::IsIMEEnabled(aInputContext); bool adjustOpenState = (enable && aInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE); bool open = (adjustOpenState && aInputContext.mIMEState.mOpen == IMEState::OPEN); - aInputContext.mNativeIMEContext = nullptr; - #ifdef NS_ENABLE_TSF // Note that even while a plugin has focus, we need to notify TSF of that. if (sIsInTSFMode) { TSFTextStore::SetInputContext(aWindow, aInputContext, aAction); if (IsTSFAvailable()) { - aInputContext.mNativeIMEContext = TSFTextStore::GetThreadManager(); if (sIsIMMEnabled) { // Associate IME context for IMM-IMEs. AssociateIMEContext(aWindow, enable); } else if (oldInputContext.mIMEState.mEnabled == IMEState::PLUGIN) { // Disassociate the IME context from the window when plugin loses focus // in pure TSF mode. AssociateIMEContext(aWindow, false); } @@ -412,25 +432,16 @@ IMEHandler::SetInputContext(nsWindow* aW #endif // #ifdef NS_ENABLE_TSF AssociateIMEContext(aWindow, enable); IMEContext context(aWindow); if (adjustOpenState) { context.SetOpenState(open); } - - if (aInputContext.mNativeIMEContext) { - return; - } - - // The old InputContext must store the default IMC or old TextStore. - // When IME context is disassociated from the window, use it. - aInputContext.mNativeIMEContext = enable ? - static_cast<void*>(context.get()) : oldInputContext.mNativeIMEContext; } // static void IMEHandler::AssociateIMEContext(nsWindow* aWindow, bool aEnable) { IMEContext context(aWindow); if (aEnable) { @@ -451,35 +462,29 @@ IMEHandler::InitInputContext(nsWindow* a // For a11y, the default enabled state should be 'enabled'. aInputContext.mIMEState.mEnabled = IMEState::ENABLED; #ifdef NS_ENABLE_TSF if (sIsInTSFMode) { TSFTextStore::SetInputContext(aWindow, aInputContext, InputContextAction(InputContextAction::CAUSE_UNKNOWN, InputContextAction::GOT_FOCUS)); - aInputContext.mNativeIMEContext = TSFTextStore::GetThreadManager(); - MOZ_ASSERT(aInputContext.mNativeIMEContext); // IME context isn't necessary in pure TSF mode. if (!sIsIMMEnabled) { AssociateIMEContext(aWindow, false); } return; } #endif // #ifdef NS_ENABLE_TSF - // NOTE: mNativeIMEContext may be null if IMM module isn't installed. +#ifdef DEBUG + // NOTE: IMC may be null if IMM module isn't installed. IMEContext context(aWindow); - aInputContext.mNativeIMEContext = static_cast<void*>(context.get()); - MOZ_ASSERT(aInputContext.mNativeIMEContext || !CurrentKeyboardLayoutHasIME()); - // If no IME context is available, we should set the widget's pointer since - // nullptr indicates there is only one context per process on the platform. - if (!aInputContext.mNativeIMEContext) { - aInputContext.mNativeIMEContext = static_cast<void*>(aWindow); - } + MOZ_ASSERT(context.IsValid() || !CurrentKeyboardLayoutHasIME()); +#endif // #ifdef DEBUG } #ifdef DEBUG // static bool IMEHandler::CurrentKeyboardLayoutHasIME() { #ifdef NS_ENABLE_TSF
--- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -29,19 +29,19 @@ struct MSGResult; */ class IMEHandler final { public: static void Initialize(); static void Terminate(); /** - * Returns TSF related native data. + * Returns TSF related native data or native IME context. */ - static void* GetNativeData(uint32_t aDataType); + static void* GetNativeData(nsWindow* aWindow, uint32_t aDataType); /** * ProcessRawKeyMessage() message is called before calling TranslateMessage() * and DispatchMessage(). If this returns true, the message is consumed. * Then, caller must not perform TranslateMessage() nor DispatchMessage(). */ static bool ProcessRawKeyMessage(const MSG& aMsg);
--- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -651,16 +651,17 @@ nsWindow::Create(nsIWidget* aParent, // Starting with Windows XP, a process always runs within a terminal services // session. In order to play nicely with RDP, fast user switching, and the // lock screen, we should be handling WM_WTSSESSION_CHANGE. We must register // our HWND in order to receive this message. DebugOnly<BOOL> wtsRegistered = ::WTSRegisterSessionNotification(mWnd, NOTIFY_FOR_THIS_SESSION); NS_ASSERTION(wtsRegistered, "WTSRegisterSessionNotification failed!\n"); + mDefaultIMC.Init(this); IMEHandler::InitInputContext(this, mInputContext); // If the internal variable set by the config.trim_on_minimize pref has not // been initialized, and if this is the hidden window (conveniently created // before any visible windows, and after the profile has been initialized), // do some initialization work. if (sTrimOnMinimize == 2 && mWindowType == eWindowType_invisible) { // Our internal trim prevention logic is effective on 2K/XP at maintaining @@ -3144,20 +3145,21 @@ void* nsWindow::GetNativeData(uint32_t a // XXX: This is sleezy!! Remember to Release the DC after using it! #ifdef MOZ_XUL return (void*)(eTransparencyTransparent == mTransparencyMode) ? mMemoryDC : ::GetDC(mWnd); #else return (void*)::GetDC(mWnd); #endif + case NS_RAW_NATIVE_IME_CONTEXT: case NS_NATIVE_TSF_THREAD_MGR: case NS_NATIVE_TSF_CATEGORY_MGR: case NS_NATIVE_TSF_DISPLAY_ATTR_MGR: - return IMEHandler::GetNativeData(aDataType); + return IMEHandler::GetNativeData(this, aDataType); default: break; } return nullptr; }
--- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -39,16 +39,18 @@ #include "oleacc.h" #include "mozilla/a11y/Accessible.h" #endif #include "nsUXThemeData.h" #include "nsIDOMMouseEvent.h" #include "nsIIdleServiceInternal.h" +#include "IMMHandler.h" + /** * Forward class definitions */ class nsNativeDragTarget; class nsIRollupListener; class imgIContainer; @@ -66,16 +68,17 @@ struct MSGResult; class nsWindow : public nsWindowBase { typedef mozilla::TimeStamp TimeStamp; typedef mozilla::TimeDuration TimeDuration; typedef mozilla::widget::WindowHook WindowHook; typedef mozilla::widget::TaskbarWindowPreview TaskbarWindowPreview; typedef mozilla::widget::NativeKey NativeKey; typedef mozilla::widget::MSGResult MSGResult; + typedef mozilla::widget::IMEContext IMEContext; public: nsWindow(); NS_DECL_ISUPPORTS_INHERITED friend class nsWindowGfx; @@ -287,16 +290,18 @@ public: bool const DestroyCalled() { return mDestroyCalled; } bool IsPopup(); virtual bool ShouldUseOffMainThreadCompositing(); bool CaptureWidgetOnScreen(RefPtr<mozilla::gfx::DrawTarget> aDT); + const IMEContext& DefaultIMC() const { return mDefaultIMC; } + protected: virtual ~nsWindow(); virtual void WindowUsesOMTC() override; virtual void RegisterTouchWindow() override; virtual nsresult NotifyIMEInternal( const IMENotification& aIMENotification) override; @@ -467,16 +472,17 @@ protected: protected: nsCOMPtr<nsIWidget> mParent; nsIntSize mLastSize; nsIntPoint mLastPoint; HWND mWnd; HWND mTransitionWnd; WNDPROC mPrevWndProc; HBRUSH mBrush; + IMEContext mDefaultIMC; bool mIsTopWidgetWindow; bool mInDtor; bool mIsVisible; bool mUnicodeWidget; bool mPainting; bool mTouchWindow; bool mDisplayPanFeedback; bool mHideChrome;