From d0bb2f8df74f68f4d36c2559ed0c4543b4ad3ead Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Wed, 17 Apr 2013 15:02:24 -0500 Subject: [PATCH 01/75] Bug 862025 - Ignore monocle vertical drag orientation for single line text inputs. r=bbondy --- browser/metro/base/content/Util.js | 4 ++++ .../metro/base/content/contenthandlers/SelectionHandler.js | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/browser/metro/base/content/Util.js b/browser/metro/base/content/Util.js index 9968f524502f..eef44e22d19c 100644 --- a/browser/metro/base/content/Util.js +++ b/browser/metro/base/content/Util.js @@ -185,6 +185,10 @@ let Util = { aElement instanceof Ci.nsIDOMHTMLTextAreaElement); }, + isMultilineInput: function isMultilineInput(aElement) { + return (aElement instanceof Ci.nsIDOMHTMLTextAreaElement); + }, + isLink: function isLink(aElement) { return ((aElement instanceof Ci.nsIDOMHTMLAnchorElement && aElement.href) || (aElement instanceof Ci.nsIDOMHTMLAreaElement && aElement.href) || diff --git a/browser/metro/base/content/contenthandlers/SelectionHandler.js b/browser/metro/base/content/contenthandlers/SelectionHandler.js index 89a007ed3af9..1b65daf533d3 100644 --- a/browser/metro/base/content/contenthandlers/SelectionHandler.js +++ b/browser/metro/base/content/contenthandlers/SelectionHandler.js @@ -842,13 +842,16 @@ var SelectionHandler = { let orientation = this._pointOrientationToRect(aClientPoint); let result = { speed: 1, trigger: false, start: false, end: false }; + let ml = Util.isMultilineInput(this._targetElement); - if (orientation.left || orientation.top) { + // This could be improved such that we only select to the beginning of + // the line when dragging left but not up. + if (orientation.left || (ml && orientation.top)) { this._addEditSelection(kSelectionNodeAnchor); result.speed = orientation.left + orientation.top; result.trigger = true; result.end = true; - } else if (orientation.right || orientation.bottom) { + } else if (orientation.right || (ml && orientation.bottom)) { this._addEditSelection(kSelectionNodeFocus); result.speed = orientation.right + orientation.bottom; result.trigger = true; From 35d113c9cdf75fa214ba42b2bc0b3b3b94b2b971 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Wed, 17 Apr 2013 15:02:24 -0500 Subject: [PATCH 02/75] Bug 854070 - Add a bit of directional hysteresis when converting a caret monocle drag to selection. r=bbondy --- .../content/helperui/SelectionHelperUI.js | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/browser/metro/base/content/helperui/SelectionHelperUI.js b/browser/metro/base/content/helperui/SelectionHelperUI.js index 86180fbe6554..10d282e05ed3 100644 --- a/browser/metro/base/content/helperui/SelectionHelperUI.js +++ b/browser/metro/base/content/helperui/SelectionHelperUI.js @@ -16,6 +16,9 @@ // Y axis scroll distance that will disable this module and cancel selection const kDisableOnScrollDistance = 25; +// Drag hysteresis programmed into monocle drag moves +const kDragHysteresisDistance = 10; + /* * Markers */ @@ -99,6 +102,8 @@ Marker.prototype = { _selectionHelperUI: null, _xPos: 0, _yPos: 0, + _xDrag: 0, + _yDrag: 0, _tag: "", _hPlane: 0, _vPlane: 0, @@ -174,6 +179,8 @@ Marker.prototype = { }, dragStart: function dragStart(aX, aY) { + this._xDrag = 0; + this._yDrag = 0; this._selectionHelperUI.markerDragStart(this); }, @@ -184,7 +191,15 @@ Marker.prototype = { moveBy: function moveBy(aDx, aDy, aClientX, aClientY) { this._xPos -= aDx; this._yPos -= aDy; - let direction = (aDx >= 0 && aDy >= 0 ? "start" : "end"); + this._xDrag -= aDx; + this._yDrag -= aDy; + // Add a bit of hysteresis to our directional detection so "big fingers" + // are detected accurately. + let direction = "tbd"; + if (Math.abs(this._xDrag) > kDragHysteresisDistance || + Math.abs(this._yDrag) > kDragHysteresisDistance) { + direction = (this._xDrag <= 0 && this._yDrag <= 0 ? "start" : "end"); + } // We may swap markers in markerDragMove. If markerDragMove // returns true keep processing, otherwise get out of here. if (this._selectionHelperUI.markerDragMove(this, direction)) { @@ -973,11 +988,16 @@ var SelectionHelperUI = { markerDragMove: function markerDragMove(aMarker, aDirection) { if (aMarker.tag == "caret") { - // We are going to transition from caret browsing mode to selection - // mode on drag. So swap the caret monocle for a start or end monocle - // depending on the direction of the drag, and start selecting text. - this._transitionFromCaretToSelection(aDirection); - return false; + // If direction is "tbd" the drag monocle hasn't determined which + // direction the user is dragging. + if (aDirection != "tbd") { + // We are going to transition from caret browsing mode to selection + // mode on drag. So swap the caret monocle for a start or end monocle + // depending on the direction of the drag, and start selecting text. + this._transitionFromCaretToSelection(aDirection); + return false; + } + return true; } let json = this._getMarkerBaseMessage(); json.change = aMarker.tag; From f32710dcdf82d0eeeafe867d6d0958d1943cf854 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 17 Apr 2013 16:08:11 -0400 Subject: [PATCH 03/75] Bug 847898 - Put Android Beam support behind a confvar variable. r=mfinkle --- configure.in | 9 +++++++++ mobile/android/base/AndroidManifest.xml.in | 4 ++++ mobile/android/base/AppConstants.java.in | 7 +++++++ mobile/android/base/BrowserApp.java | 6 +++--- mobile/android/confvars.sh | 3 +++ 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/configure.in b/configure.in index af24709aeb45..2e0cea550412 100644 --- a/configure.in +++ b/configure.in @@ -4263,6 +4263,7 @@ MOZ_USE_NATIVE_POPUP_WINDOWS= MOZ_ANDROID_HISTORY= MOZ_WEBSMS_BACKEND= MOZ_ANDROID_WALLPAPER= +MOZ_ANDROID_BEAM= ACCESSIBILITY=1 MOZ_SYS_MSG= MOZ_TIME_MANAGER= @@ -5146,6 +5147,13 @@ if test -n "$MOZ_ANDROID_WALLPAPER"; then AC_DEFINE(MOZ_ANDROID_WALLPAPER) fi +dnl ======================================================== +dnl = Enable NFC permission on Android +dnl ======================================================== +if test -n "$MOZ_ANDROID_BEAM"; then + AC_DEFINE(MOZ_ANDROID_BEAM) +fi + dnl ======================================================== dnl = Build Personal Security Manager dnl ======================================================== @@ -8594,6 +8602,7 @@ AC_SUBST(MOZ_METRO) AC_SUBST(MOZ_ANDROID_HISTORY) AC_SUBST(MOZ_WEBSMS_BACKEND) AC_SUBST(MOZ_ANDROID_WALLPAPER) +AC_SUBST(MOZ_ANDROID_BEAM) AC_SUBST(ENABLE_STRIP) AC_SUBST(PKG_SKIP_STRIP) AC_SUBST(STRIP_FLAGS) diff --git a/mobile/android/base/AndroidManifest.xml.in b/mobile/android/base/AndroidManifest.xml.in index 8b1cd0c4e8c9..19fce07c2840 100644 --- a/mobile/android/base/AndroidManifest.xml.in +++ b/mobile/android/base/AndroidManifest.xml.in @@ -44,9 +44,11 @@ +#ifdef MOZ_ANDROID_BEAM +#endif #ifdef MOZ_WEBRTC @@ -125,12 +127,14 @@ +#ifdef MOZ_ANDROID_BEAM +#endif diff --git a/mobile/android/base/AppConstants.java.in b/mobile/android/base/AppConstants.java.in index 33825d6d7a49..605b2d6cc057 100644 --- a/mobile/android/base/AppConstants.java.in +++ b/mobile/android/base/AppConstants.java.in @@ -86,4 +86,11 @@ public class AppConstants { #else false; #endif + + public static final boolean MOZ_ANDROID_BEAM = +#ifdef MOZ_ANDROID_BEAM + true; +#else + false; +#endif } diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 891143c12052..c5f0d5b0052e 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -536,7 +536,7 @@ abstract public class BrowserApp extends GeckoApp Distribution.init(this, getPackageResourcePath()); JavaAddonManager.getInstance().init(getApplicationContext()); - if (Build.VERSION.SDK_INT >= 14) { + if (AppConstants.MOZ_ANDROID_BEAM && Build.VERSION.SDK_INT >= 14) { NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this); if (nfc != null) { nfc.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() { @@ -632,7 +632,7 @@ abstract public class BrowserApp extends GeckoApp unregisterEventListener("Feedback:MaybeLater"); unregisterEventListener("Telemetry:Gather"); - if (Build.VERSION.SDK_INT >= 14) { + if (AppConstants.MOZ_ANDROID_BEAM && Build.VERSION.SDK_INT >= 14) { NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this); if (nfc != null) { // null this out even though the docs say it's not needed, @@ -1664,7 +1664,7 @@ abstract public class BrowserApp extends GeckoApp String action = intent.getAction(); - if (Build.VERSION.SDK_INT >= 10 && NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) { + if (AppConstants.MOZ_ANDROID_BEAM && Build.VERSION.SDK_INT >= 10 && NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) { String uri = intent.getDataString(); GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(uri)); } diff --git a/mobile/android/confvars.sh b/mobile/android/confvars.sh index 57d72fda5531..5d9c60031028 100644 --- a/mobile/android/confvars.sh +++ b/mobile/android/confvars.sh @@ -22,6 +22,9 @@ MOZ_MEDIA_NAVIGATOR=1 # Enable SET_WALLPAPER permission MOZ_ANDROID_WALLPAPER=1 +# Enable NFC permission +MOZ_ANDROID_BEAM=1 + if test "$LIBXUL_SDK"; then MOZ_XULRUNNER=1 else From 8ee9ad14d69a16ac5291bb4cc7f2a484cc952078 Mon Sep 17 00:00:00 2001 From: Shane Caraveo Date: Wed, 17 Apr 2013 12:43:30 -0700 Subject: [PATCH 04/75] bug 851653 add services to addonprovider for fhr, r=gps --- services/healthreport/providers.jsm | 28 ++++++++++++++++++- .../tests/xpcshell/test_provider_addons.js | 23 ++++++++++++--- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/services/healthreport/providers.jsm b/services/healthreport/providers.jsm index 2815c1f63140..09c47c1dde7f 100644 --- a/services/healthreport/providers.jsm +++ b/services/healthreport/providers.jsm @@ -681,6 +681,29 @@ function AddonCountsMeasurement() { AddonCountsMeasurement.prototype = Object.freeze({ __proto__: Metrics.Measurement.prototype, + name: "counts", + version: 2, + + fields: { + theme: DAILY_LAST_NUMERIC_FIELD, + lwtheme: DAILY_LAST_NUMERIC_FIELD, + plugin: DAILY_LAST_NUMERIC_FIELD, + extension: DAILY_LAST_NUMERIC_FIELD, + service: DAILY_LAST_NUMERIC_FIELD, + }, +}); + + +/** + * Legacy version of addons counts before services was added. + */ +function AddonCountsMeasurement1() { + Metrics.Measurement.call(this); +} + +AddonCountsMeasurement1.prototype = Object.freeze({ + __proto__: Metrics.Measurement.prototype, + name: "counts", version: 1, @@ -719,12 +742,14 @@ AddonsProvider.prototype = Object.freeze({ FULL_DETAIL_TYPES: [ "plugin", "extension", + "service", ], name: "org.mozilla.addons", measurementTypes: [ ActiveAddonsMeasurement, + AddonCountsMeasurement1, AddonCountsMeasurement, ], @@ -770,7 +795,8 @@ AddonsProvider.prototype = Object.freeze({ let now = new Date(); let active = this.getMeasurement("active", 1); - let counts = this.getMeasurement("counts", 1); + let counts = this.getMeasurement(AddonCountsMeasurement.prototype.name, + AddonCountsMeasurement.prototype.version); this.enqueueStorageOperation(function storageAddons() { for (let type in data.counts) { diff --git a/services/healthreport/tests/xpcshell/test_provider_addons.js b/services/healthreport/tests/xpcshell/test_provider_addons.js index b2d8e482b982..ecaaa8d86260 100644 --- a/services/healthreport/tests/xpcshell/test_provider_addons.js +++ b/services/healthreport/tests/xpcshell/test_provider_addons.js @@ -88,6 +88,18 @@ add_task(function test_collect() { installDate: now, updateDate: now, }, + { + id: "addon3", + userDisabled: false, + appDisabled: false, + version: "4", + type: "service", + scope: 1, + foreignInstall: false, + hasBinaryComponents: false, + installDate: now, + updateDate: now, + }, ]; monkeypatchAddons(provider, addons); @@ -104,29 +116,32 @@ add_task(function test_collect() { let json = data.singular.get("addons")[1]; let value = JSON.parse(json); do_check_eq(typeof(value), "object"); - do_check_eq(Object.keys(value).length, 2); + do_check_eq(Object.keys(value).length, 3); do_check_true("addon0" in value); do_check_true("addon1" in value); + do_check_true("addon3" in value); let serializer = active.serializer(active.SERIALIZE_JSON); let serialized = serializer.singular(data.singular); do_check_eq(typeof(serialized), "object"); - do_check_eq(Object.keys(serialized).length, 3); // Our two keys, plus _v. + do_check_eq(Object.keys(serialized).length, 4); // Our three keys, plus _v. do_check_true("addon0" in serialized); do_check_true("addon1" in serialized); + do_check_true("addon3" in serialized); do_check_eq(serialized._v, 1); - let counts = provider.getMeasurement("counts", 1); + let counts = provider.getMeasurement("counts", 2); data = yield counts.getValues(); do_check_eq(data.days.size, 1); do_check_eq(data.singular.size, 0); do_check_true(data.days.hasDay(now)); value = data.days.getDay(now); - do_check_eq(value.size, 3); + do_check_eq(value.size, 4); do_check_eq(value.get("extension"), 1); do_check_eq(value.get("plugin"), 1); do_check_eq(value.get("theme"), 1); + do_check_eq(value.get("service"), 1); yield provider.shutdown(); yield storage.close(); From 783b2ce156c09391b2f7343503963d783cdd9368 Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Wed, 17 Apr 2013 15:48:35 -0400 Subject: [PATCH 05/75] Bug 861958: Support pre-negotiated streams greater than the currently-allocated number r=tuexen --- netwerk/sctp/datachannel/DataChannel.cpp | 141 ++++++++++++++++------- 1 file changed, 99 insertions(+), 42 deletions(-) diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp index c79aa2972737..11f52a0745dc 100644 --- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -1842,7 +1842,8 @@ DataChannelConnection::Open(const nsACString& label, const nsACString& protocol, return nullptr; } - if (aStream != INVALID_STREAM && mStreams[aStream]) { + // Don't look past currently-negotiated streams + if (aStream != INVALID_STREAM && aStream < mStreams.Length() && mStreams[aStream]) { LOG(("ERROR: external negotiation of already-open channel %u", aStream)); // XXX How do we indicate this up to the application? Probably the // caller's job, but we may need to return an error code. @@ -1873,55 +1874,96 @@ DataChannelConnection::OpenFinish(already_AddRefed aChannel) // Normally 1 reference if called from ::Open(), or 2 if called from // ProcessQueuedOpens() unless the DOMDataChannel was gc'd uint16_t stream = channel->mStream; + bool queue = false; mLock.AssertCurrentThreadOwns(); - if (stream == INVALID_STREAM || mState != OPEN) { - if (mState == OPEN) { // implies INVALID_STREAM - // Don't try to find a stream if not open - mAllocateEven isn't set yet + // Cases we care about: + // Pre-negotiated: + // Not Open: + // Doesn't fit: + // -> change initial ask or renegotiate after open + // -> queue open + // Open: + // Doesn't fit: + // -> RequestMoreStreams && queue + // Does fit: + // -> open + // Not negotiated: + // Not Open: + // -> queue open + // Open: + // -> Try to get a stream + // Doesn't fit: + // -> RequestMoreStreams && queue + // Does fit: + // -> open + // So the Open cases are basically the same + // Not Open cases are simply queue for non-negotiated, and + // either change the initial ask or possibly renegotiate after open. + + if (mState == OPEN) { + if (stream == INVALID_STREAM) { stream = FindFreeStream(); // may be INVALID_STREAM if we need more - if (stream == INVALID_STREAM) { - if (!RequestMoreStreams()) { - channel->mState = CLOSED; - if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) { - // We already returned the channel to the app. - NS_ERROR("Failed to request more streams"); - NS_DispatchToMainThread(new DataChannelOnMessageAvailable( - DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this, - channel)); - return channel.forget(); - } - // we'll be destroying the channel, but it never really got set up - // Alternative would be to RUN_ON_THREAD(channel.forget(),::Destroy,...) and - // Dispatch it to ourselves - return nullptr; - } + } + if (stream == INVALID_STREAM || stream >= mStreams.Length()) { + // RequestMoreStreams() limits to MAX_NUM_STREAMS -- allocate extra streams + // to avoid going back immediately for more if the ask to N, N+1, etc + int32_t more_needed = (stream == INVALID_STREAM) ? 16 : + (stream-((int32_t)mStreams.Length())) + 16; + if (!RequestMoreStreams(more_needed)) { + // Something bad happened... we're done + goto request_error_cleanup; } - // if INVALID here, we need to queue + queue = true; } - if (stream != INVALID_STREAM) { - // just allocated (& OPEN), or externally negotiated - mStreams[stream] = channel; // holds a reference - channel->mStream = stream; - } - - LOG(("Finishing open: channel %p, stream = %u", channel.get(), stream)); - - if (stream == INVALID_STREAM || mState != OPEN) { - // we're going to queue - - LOG(("Queuing channel %p (%u) to finish open", channel.get(), stream)); - // Also serves to mark we told the app - channel->mFlags |= DATA_CHANNEL_FLAGS_FINISH_OPEN; - channel->AddRef(); // we need a ref for the nsDeQue and one to return - mPending.Push(channel); - return channel.forget(); - } // else OPEN and we selected a stream } else { - // OPEN and externally negotiated stream - mStreams[stream] = channel; - mStreams[stream] = channel; // holds a reference + // not OPEN + if (stream != INVALID_STREAM && stream >= mStreams.Length() && + mState == CLOSED) { + // Update number of streams for init message + struct sctp_initmsg initmsg; + socklen_t len = sizeof(initmsg); + int32_t total_needed = stream+16; + + memset(&initmsg, 0, sizeof(initmsg)); + if (usrsctp_getsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, &len) < 0) { + LOG(("*** failed getsockopt SCTP_INITMSG")); + goto request_error_cleanup; + } + LOG(("Setting number of SCTP streams to %u, was %u/%u", total_needed, + initmsg.sinit_num_ostreams, initmsg.sinit_max_instreams)); + initmsg.sinit_num_ostreams = total_needed; + initmsg.sinit_max_instreams = MAX_NUM_STREAMS; + if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, + (socklen_t)sizeof(initmsg)) < 0) { + LOG(("*** failed setsockopt SCTP_INITMSG, errno %d", errno)); + goto request_error_cleanup; + } + + int32_t old_len = mStreams.Length(); + mStreams.AppendElements(total_needed - old_len); + for (int32_t i = old_len; i < total_needed; ++i) { + mStreams[i] = nullptr; + } + } + // else if state is CONNECTING, we'll just re-negotiate when OpenFinish + // is called, if needed + queue = true; } + if (queue) { + LOG(("Queuing channel %p (%u) to finish open", channel.get(), stream)); + // Also serves to mark we told the app + channel->mFlags |= DATA_CHANNEL_FLAGS_FINISH_OPEN; + channel->AddRef(); // we need a ref for the nsDeQue and one to return + mPending.Push(channel); + return channel.forget(); + } + + MOZ_ASSERT(stream != INVALID_STREAM); + // just allocated (& OPEN), or externally negotiated + mStreams[stream] = channel; // holds a reference + channel->mStream = stream; #ifdef TEST_QUEUED_DATA // It's painful to write a test for this... @@ -1970,6 +2012,21 @@ DataChannelConnection::OpenFinish(already_AddRefed aChannel) channel)); return channel.forget(); + +request_error_cleanup: + channel->mState = CLOSED; + if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) { + // We already returned the channel to the app. + NS_ERROR("Failed to request more streams"); + NS_DispatchToMainThread(new DataChannelOnMessageAvailable( + DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this, + channel)); + return channel.forget(); + } + // we'll be destroying the channel, but it never really got set up + // Alternative would be to RUN_ON_THREAD(channel.forget(),::Destroy,...) and + // Dispatch it to ourselves + return nullptr; } int32_t From 857009063fcdb286d5cc44ee7bebc31acda95230 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Wed, 17 Apr 2013 22:16:14 +0200 Subject: [PATCH 06/75] Bug 844549 - Backout the test for bug 395609 since it refers to external resources (http://google.com/). r=test-only --- layout/xul/base/src/crashtests/395609.xul | 16 ---------------- layout/xul/base/src/crashtests/crashtests.list | 1 - 2 files changed, 17 deletions(-) delete mode 100644 layout/xul/base/src/crashtests/395609.xul diff --git a/layout/xul/base/src/crashtests/395609.xul b/layout/xul/base/src/crashtests/395609.xul deleted file mode 100644 index 65988102ecd3..000000000000 --- a/layout/xul/base/src/crashtests/395609.xul +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/layout/xul/base/src/crashtests/crashtests.list b/layout/xul/base/src/crashtests/crashtests.list index 1a38a75491ee..375b95abe44b 100644 --- a/layout/xul/base/src/crashtests/crashtests.list +++ b/layout/xul/base/src/crashtests/crashtests.list @@ -47,7 +47,6 @@ load 387033-1.xhtml load 387080-1.xul load 391974-1.html load 394120-1.xhtml -skip-if(B2G||browserIsRemote) load 395609.xul # bug 844549 load 397304-1.html load 398326-1.xhtml load 399013.xul From e46f28dbdf6efa07d5eeb356269a7c9b4f7c704d Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Wed, 17 Apr 2013 22:16:14 +0200 Subject: [PATCH 07/75] Bug 701504 - Null out all BidiParagraphData::mPrevFrame when starting a new block. r=smontagu --- layout/base/crashtests/701504.html | 24 ++++++++++++++++++++++++ layout/base/crashtests/crashtests.list | 1 + layout/base/nsBidiPresUtils.cpp | 16 ++++++++++++++-- 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 layout/base/crashtests/701504.html diff --git a/layout/base/crashtests/701504.html b/layout/base/crashtests/701504.html new file mode 100644 index 000000000000..011331077840 --- /dev/null +++ b/layout/base/crashtests/701504.html @@ -0,0 +1,24 @@ + + + + + + + +
+x
+ + + diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index 392fac8a084b..e242e027ce4f 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -368,6 +368,7 @@ load 691118-1.html load 695861.html load 698335.html needs-focus pref(accessibility.browsewithcaret,true) load 699353-1.html +load 701504.html load 707098.html load 722137.html load 725535.html diff --git a/layout/base/nsBidiPresUtils.cpp b/layout/base/nsBidiPresUtils.cpp index 91f03ca4e6b8..aad1966b70f4 100644 --- a/layout/base/nsBidiPresUtils.cpp +++ b/layout/base/nsBidiPresUtils.cpp @@ -199,6 +199,13 @@ struct BidiParagraphData { } } + void ResetForNewBlock() + { + for (BidiParagraphData* bpd = this; bpd; bpd = bpd->mSubParagraph) { + bpd->mPrevFrame = nullptr; + } + } + void AppendFrame(nsIFrame* aFrame, nsBlockInFlowLineIterator* aLineIter, nsIContent* aContent = nullptr) @@ -603,8 +610,7 @@ nsBidiPresUtils::Resolve(nsBlockFrame* aBlockFrame) block = static_cast(block->GetNextContinuation())) { block->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION); nsBlockInFlowLineIterator lineIter(block, block->begin_lines()); - bpd.mPrevFrame = nullptr; - bpd.GetSubParagraph()->mPrevFrame = nullptr; + bpd.ResetForNewBlock(); TraverseFrames(aBlockFrame, &lineIter, block->GetFirstPrincipalChild(), &bpd); // XXX what about overflow lines? } @@ -920,6 +926,10 @@ nsBidiPresUtils::TraverseFrames(nsBlockFrame* aBlockFrame, if (!aCurrentFrame) return; +#ifdef DEBUG + nsBlockFrame* initialLineContainer = aLineIter->GetContainer(); +#endif + nsIFrame* childFrame = aCurrentFrame; do { /* @@ -1165,6 +1175,8 @@ nsBidiPresUtils::TraverseFrames(nsBlockFrame* aBlockFrame, } childFrame = nextSibling; } while (childFrame); + + MOZ_ASSERT(initialLineContainer == aLineIter->GetContainer()); } void From db4a38d7ac473a20be054e92181d7bd366332147 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Wed, 17 Apr 2013 22:16:14 +0200 Subject: [PATCH 08/75] Bug 485149 - Using box-shadow on a
with does not follow box shape. r=roc --- layout/base/nsCSSRendering.cpp | 3 +- layout/base/nsDisplayList.cpp | 2 +- layout/forms/nsFieldSetFrame.cpp | 36 ++++---- layout/generic/nsIFrame.h | 8 ++ layout/reftests/box-shadow/fieldset-ref.html | 95 ++++++++++++++++++++ layout/reftests/box-shadow/fieldset.html | 90 +++++++++++++++++++ layout/reftests/box-shadow/reftest.list | 1 + 7 files changed, 218 insertions(+), 17 deletions(-) create mode 100644 layout/reftests/box-shadow/fieldset-ref.html create mode 100644 layout/reftests/box-shadow/fieldset.html diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index c0c63b5bf9ea..076c232755bb 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -1214,7 +1214,8 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext, } else { nativeTheme = false; nscoord twipsRadii[8]; - NS_ASSERTION(aFrameArea.Size() == aForFrame->GetSize(), "unexpected size"); + NS_ASSERTION(aFrameArea.Size() == aForFrame->VisualBorderRectRelativeToSelf().Size(), + "unexpected size"); hasBorderRadius = aForFrame->GetBorderRadii(twipsRadii); if (hasBorderRadius) { ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii); diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index a87409917665..0969a050c33a 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -2385,7 +2385,7 @@ void nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) { nsPoint offset = ToReferenceFrame(); - nsRect borderRect = nsRect(offset, mFrame->GetSize()); + nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset; nsPresContext* presContext = mFrame->PresContext(); nsAutoTArray rects; ComputeDisjointRectangles(mVisibleRegion, &rects); diff --git a/layout/forms/nsFieldSetFrame.cpp b/layout/forms/nsFieldSetFrame.cpp index 74f253ae8b59..6ba523262aa1 100644 --- a/layout/forms/nsFieldSetFrame.cpp +++ b/layout/forms/nsFieldSetFrame.cpp @@ -3,9 +3,6 @@ * 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/. */ -// YY need to pass isMultiple before create called - -//#include "nsFormControlFrame.h" #include "nsContainerFrame.h" #include "nsLegendFrame.h" #include "nsIDOMNode.h" @@ -13,7 +10,6 @@ #include "nsIDOMHTMLLegendElement.h" #include "nsCSSRendering.h" #include -//#include "nsIDOMHTMLCollection.h" #include "nsIContent.h" #include "nsIFrame.h" #include "nsISupports.h" @@ -35,7 +31,7 @@ using namespace mozilla::layout; class nsLegendFrame; -class nsFieldSetFrame : public nsContainerFrame { +class nsFieldSetFrame MOZ_FINAL : public nsContainerFrame { public: NS_DECL_FRAMEARENA_HELPERS @@ -55,6 +51,21 @@ public: uint32_t aFlags) MOZ_OVERRIDE; virtual nscoord GetBaseline() const; + /** + * The area to paint box-shadows around. It's the border rect except + * when there's a we offset the y-position to the center of it. + */ + virtual nsRect VisualBorderRectRelativeToSelf() const MOZ_OVERRIDE { + nscoord topBorder = StyleBorder()->GetComputedBorderWidth(NS_SIDE_TOP); + nsRect r(nsPoint(0,0), GetSize()); + if (topBorder < mLegendRect.height) { + nscoord yoff = (mLegendRect.height - topBorder) / 2; + r.y += yoff; + r.height -= yoff; + } + return r; + } + NS_IMETHOD Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, @@ -229,20 +240,14 @@ void nsFieldSetFrame::PaintBorderBackground(nsRenderingContext& aRenderingContext, nsPoint aPt, const nsRect& aDirtyRect, uint32_t aBGFlags) { - const nsStyleBorder* borderStyle = StyleBorder(); - - nscoord topBorder = borderStyle->GetComputedBorderWidth(NS_SIDE_TOP); - nscoord yoff = 0; - nsPresContext* presContext = PresContext(); - // if the border is smaller than the legend. Move the border down // to be centered on the legend. // FIXME: This means border-radius clamping is incorrect; we should // override nsIFrame::GetBorderRadii. - if (topBorder < mLegendRect.height) - yoff = (mLegendRect.height - topBorder)/2; - - nsRect rect(aPt.x, aPt.y + yoff, mRect.width, mRect.height - yoff); + nsRect rect = VisualBorderRectRelativeToSelf(); + nscoord yoff = rect.y; + rect += aPt; + nsPresContext* presContext = PresContext(); nsCSSRendering::PaintBackground(presContext, aRenderingContext, this, aDirtyRect, rect, aBGFlags); @@ -251,6 +256,7 @@ nsFieldSetFrame::PaintBorderBackground(nsRenderingContext& aRenderingContext, this, rect, aDirtyRect); if (mLegendFrame) { + nscoord topBorder = StyleBorder()->GetComputedBorderWidth(NS_SIDE_TOP); // Use the rect of the legend frame, not mLegendRect, so we draw our // border under the legend's left and right margins. diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 9358fc0c3c1a..7a60a913e2d4 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1008,6 +1008,14 @@ public: nsRect GetContentRect() const; nsRect GetContentRectRelativeToSelf() const; + /** + * The area to paint box-shadows around. The default is the border rect. + * (nsFieldSetFrame overrides this). + */ + virtual nsRect VisualBorderRectRelativeToSelf() const { + return nsRect(0, 0, mRect.width, mRect.height); + } + /** * Get the size, in app units, of the border radii. It returns FALSE iff all * returned radii == 0 (so no border radii), TRUE otherwise. diff --git a/layout/reftests/box-shadow/fieldset-ref.html b/layout/reftests/box-shadow/fieldset-ref.html new file mode 100644 index 000000000000..9196d5063604 --- /dev/null +++ b/layout/reftests/box-shadow/fieldset-ref.html @@ -0,0 +1,95 @@ + + + + Testcase for bug 485149 + + + + + +1 + + +

+ +2 + + +

+ +3 + + +

+ +4 + + +
+
+
+ + + diff --git a/layout/reftests/box-shadow/fieldset.html b/layout/reftests/box-shadow/fieldset.html new file mode 100644 index 000000000000..581633f54b11 --- /dev/null +++ b/layout/reftests/box-shadow/fieldset.html @@ -0,0 +1,90 @@ + + + + Testcase for bug 485149 + + + + +
Legend +1 +
+ +

+
Legend +2 +
+ +

+
+3 +
+ +

+
+4 +
+ +
+
+
+ + + diff --git a/layout/reftests/box-shadow/reftest.list b/layout/reftests/box-shadow/reftest.list index 5289bf29cd54..fdee43264f69 100644 --- a/layout/reftests/box-shadow/reftest.list +++ b/layout/reftests/box-shadow/reftest.list @@ -25,3 +25,4 @@ random-if(d2d) == boxshadow-threecorners.html boxshadow-threecorners-ref.html == overflow-not-scrollable-2.html overflow-not-scrollable-2-ref.html fails-if(Android) == 611574-1.html 611574-1-ref.html fails-if(Android) == 611574-2.html 611574-2-ref.html +fuzzy-if(winWidget,5,30) == fieldset.html fieldset-ref.html # minor anti-aliasing problem on Windows From fece0273648838a05bef4e59b07896336ad99d9a Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Wed, 17 Apr 2013 22:16:14 +0200 Subject: [PATCH 09/75] Bug 859088 - Left/Right Arrow key after selection should move to the beginning/end of selection respectively (on all platforms). r=ehsan --- layout/generic/nsSelection.cpp | 8 +++----- modules/libpref/src/init/all.js | 7 ++++--- toolkit/content/tests/chrome/bug451540_window.xul | 15 +++++++++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index 1f28f6d13ea6..15c6004e8436 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -784,13 +784,11 @@ nsFrameSelection::MoveCaret(uint32_t aKeycode, SetDesiredX(desiredX); } - int32_t caretStyle = - Preferences::GetInt("layout.selection.caret_style", 0); -#ifdef XP_MACOSX + int32_t caretStyle = Preferences::GetInt("layout.selection.caret_style", 0); if (caretStyle == 0) { - caretStyle = 2; // put caret at the selection edge in the |aKeycode| direction + // Put caret at the selection edge in the |aKeycode| direction. + caretStyle = 2; } -#endif if (!isCollapsed && !aContinueSelection && caretStyle == 2) { switch (aKeycode){ diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 6f5b9fa4d0fd..bdd1883f5821 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1683,11 +1683,12 @@ pref("layout.word_select.stop_at_punctuation", true); // controls caret style and word-delete during text selection // 0 = use platform default // 1 = caret moves and blinks as when there is no selection; word -// delete deselects the selection and then deletes word (Windows default) +// delete deselects the selection and then deletes word // 2 = caret moves to selection edge and is not visible during selection; -// word delete deletes the selection (Mac default) +// word delete deletes the selection (Mac and Linux default) // 3 = caret moves and blinks as when there is no selection; word delete -// deletes the selection (Unix default) +// deletes the selection +// Windows default is 1 for word delete behavior, the rest as for 2. pref("layout.selection.caret_style", 0); // pref to control whether or not to replace backslashes with Yen signs diff --git a/toolkit/content/tests/chrome/bug451540_window.xul b/toolkit/content/tests/chrome/bug451540_window.xul index 936412e97962..c1b5f0aefd5c 100644 --- a/toolkit/content/tests/chrome/bug451540_window.xul +++ b/toolkit/content/tests/chrome/bug451540_window.xul @@ -48,6 +48,16 @@ gBrowser.loadURI(data); } + function goToStartOfLine(aElement) { + if (navigator.platform.indexOf("Mac") >= 0) { + synthesizeKey(aElement, true, KeyEvent.DOM_VK_LEFT, + false, false, true); + } else { + synthesizeKey(aElement, true, KeyEvent.DOM_VK_HOME, + false, false, false); + } + } + function resetForNextTest(aElement, aText) { if (!aText) aText = SEARCH_TEXT; @@ -67,10 +77,7 @@ // Move caret to start of element aElement.focus(); - synthesizeKey(aElement, true, KeyEvent.DOM_VK_UP, - sendCtrl, false, sendMeta); - synthesizeKey(aElement, true, KeyEvent.DOM_VK_LEFT, - sendCtrl, false, sendMeta); + goToStartOfLine(aElement); } function synthesizeKey(target, isKeyCode, key, ctlKey, shiftKey, metaKey) { From 7cc2021f5c875151e5a6db39a042d2c5090a288a Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Wed, 17 Apr 2013 22:16:14 +0200 Subject: [PATCH 10/75] Bug 862185 - Make nsFieldSetFrame reflow and paint overflow container children. r=roc --- layout/forms/nsFieldSetFrame.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/layout/forms/nsFieldSetFrame.cpp b/layout/forms/nsFieldSetFrame.cpp index 6ba523262aa1..5bf47fac326e 100644 --- a/layout/forms/nsFieldSetFrame.cpp +++ b/layout/forms/nsFieldSetFrame.cpp @@ -87,6 +87,11 @@ public: nsIFrame* aOldFrame); virtual nsIAtom* GetType() const; + virtual bool IsFrameOfType(uint32_t aFlags) const + { + return nsContainerFrame::IsFrameOfType(aFlags & + ~nsIFrame::eCanContainOverflowContainers); + } #ifdef ACCESSIBILITY virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE; @@ -196,7 +201,8 @@ nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, // REVIEW: We don't really need to check frame emptiness here; if it's empty, // the background/border display item won't do anything, and if it isn't empty, // we need to paint the outline - if (IsVisibleForPainting(aBuilder)) { + if (!(GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) && + IsVisibleForPainting(aBuilder)) { if (StyleBorder()->mBoxShadow) { aLists.BorderBackground()->AppendNewToTop(new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, this)); @@ -212,6 +218,10 @@ nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, DO_GLOBAL_REFLOW_COUNT_DSP("nsFieldSetFrame"); } + if (GetPrevInFlow()) { + DisplayOverflowContainers(aBuilder, aDirtyRect, aLists); + } + nsDisplayListCollection contentDisplayItems; if (mContentFrame) { // Collect mContentFrame's display items into their own collection. We need @@ -392,6 +402,13 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext, // Initialize OUT parameter aStatus = NS_FRAME_COMPLETE; + nsOverflowAreas ocBounds; + nsReflowStatus ocStatus = NS_FRAME_COMPLETE; + if (GetPrevInFlow()) { + ReflowOverflowContainerChildren(aPresContext, aReflowState, ocBounds, 0, + ocStatus); + } + //------------ Handle Incremental Reflow ----------------- bool reflowContent; bool reflowLegend; @@ -571,6 +588,11 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext, ConsiderChildOverflow(aDesiredSize.mOverflowAreas, mLegendFrame); if (mContentFrame) ConsiderChildOverflow(aDesiredSize.mOverflowAreas, mContentFrame); + + // Merge overflow container bounds and status. + aDesiredSize.mOverflowAreas.UnionWith(ocBounds); + NS_MergeReflowStatusInto(&aStatus, ocStatus); + FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); InvalidateFrame(); From 1c5df355669ec20eac2b9016de179dfc3d0bfd19 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Wed, 17 Apr 2013 22:16:14 +0200 Subject: [PATCH 11/75] Bug 862185 - Remove child frame members which may become stale and add accessors to get them from the child list instead. r=roc --- layout/forms/nsFieldSetFrame.cpp | 201 +++++++++++++++++-------------- 1 file changed, 111 insertions(+), 90 deletions(-) diff --git a/layout/forms/nsFieldSetFrame.cpp b/layout/forms/nsFieldSetFrame.cpp index 5bf47fac326e..79e4f9d8d910 100644 --- a/layout/forms/nsFieldSetFrame.cpp +++ b/layout/forms/nsFieldSetFrame.cpp @@ -3,6 +3,7 @@ * 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 "nsCSSAnonBoxes.h" #include "nsContainerFrame.h" #include "nsLegendFrame.h" #include "nsIDOMNode.h" @@ -37,9 +38,6 @@ public: nsFieldSetFrame(nsStyleContext* aContext); - NS_IMETHOD SetInitialChildList(ChildListID aListID, - nsFrameList& aChildList); - NS_HIDDEN_(nscoord) GetIntrinsicWidth(nsRenderingContext* aRenderingContext, nsLayoutUtils::IntrinsicWidthType); @@ -98,6 +96,9 @@ public: #endif #ifdef DEBUG + NS_IMETHOD SetInitialChildList(ChildListID aListID, + nsFrameList& aChildList); + NS_IMETHOD GetFrameName(nsAString& aResult) const { return MakeFrameName(NS_LITERAL_STRING("FieldSet"), aResult); } @@ -107,10 +108,40 @@ protected: void ReparentFrameList(const nsFrameList& aFrameList); - // mLegendFrame is a nsLegendFrame or a nsHTMLScrollFrame with the - // nsLegendFrame as the scrolled frame (aka content insertion frame). - nsIFrame* mLegendFrame; - nsIFrame* mContentFrame; + /** + * Return the anonymous frame that contains all descendants except + * the legend frame. This is currently always a block frame with + * pseudo nsCSSAnonBoxes::fieldsetContent -- this may change in the + * future when we add support for CSS overflow for
. + */ + nsIFrame* GetInner() const + { + nsIFrame* last = mFrames.LastChild(); + if (last && + last->StyleContext()->GetPseudo() == nsCSSAnonBoxes::fieldsetContent) { + return last; + } + MOZ_ASSERT(mFrames.LastChild() == mFrames.FirstChild()); + return nullptr; + } + + /** + * Return the frame that represents the legend if any. This may be + * a nsLegendFrame or a nsHTMLScrollFrame with the nsLegendFrame as the + * scrolled frame (aka content insertion frame). + */ + nsIFrame* GetLegend() const + { + if (mFrames.FirstChild() == GetInner()) { + MOZ_ASSERT(mFrames.LastChild() == mFrames.FirstChild()); + return nullptr; + } + MOZ_ASSERT(mFrames.FirstChild() && + mFrames.FirstChild()->GetContentInsertionFrame()->GetType() == + nsGkAtoms::legendFrame); + return mFrames.FirstChild(); + } + nsRect mLegendRect; nscoord mLegendSpace; }; @@ -126,8 +157,6 @@ NS_IMPL_FRAMEARENA_HELPERS(nsFieldSetFrame) nsFieldSetFrame::nsFieldSetFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) { - mContentFrame = nullptr; - mLegendFrame = nullptr; mLegendSpace = 0; } @@ -137,23 +166,16 @@ nsFieldSetFrame::GetType() const return nsGkAtoms::fieldSetFrame; } +#ifdef DEBUG NS_IMETHODIMP nsFieldSetFrame::SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) { - // Get the content and legend frames. - if (!aChildList.OnlyChild()) { - NS_ASSERTION(aChildList.GetLength() == 2, "Unexpected child list"); - mContentFrame = aChildList.LastChild(); - mLegendFrame = aChildList.FirstChild(); - } else { - mContentFrame = aChildList.FirstChild(); - mLegendFrame = nullptr; - } - - // Queue up the frames for the content frame - return nsContainerFrame::SetInitialChildList(kPrincipalList, aChildList); + nsresult rv = nsContainerFrame::SetInitialChildList(kPrincipalList, aChildList); + MOZ_ASSERT(GetInner()); + return rv; } +#endif class nsDisplayFieldSetBorderBackground : public nsDisplayItem { public: @@ -223,26 +245,25 @@ nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, } nsDisplayListCollection contentDisplayItems; - if (mContentFrame) { - // Collect mContentFrame's display items into their own collection. We need - // to be calling BuildDisplayList on mContentFrame before mLegendFrame in - // case it contains out-of-flow frames whose placeholders are under - // mLegendFrame. However, we want mContentFrame's display items to be - // after mLegendFrame's display items in z-order, so we need to save them + if (nsIFrame* inner = GetInner()) { + // Collect the inner frame's display items into their own collection. + // We need to be calling BuildDisplayList on it before the legend in + // case it contains out-of-flow frames whose placeholders are in the + // legend. However, we want the inner frame's display items to be + // after the legend's display items in z-order, so we need to save them // and append them later. - BuildDisplayListForChild(aBuilder, mContentFrame, aDirtyRect, - contentDisplayItems); + BuildDisplayListForChild(aBuilder, inner, aDirtyRect, contentDisplayItems); } - if (mLegendFrame) { + if (nsIFrame* legend = GetLegend()) { // The legend's background goes on our BlockBorderBackgrounds list because // it's a block child. nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds()); - BuildDisplayListForChild(aBuilder, mLegendFrame, aDirtyRect, set); + BuildDisplayListForChild(aBuilder, legend, aDirtyRect, set); } - // Put mContentFrame's display items on the master list. Note that - // this moves mContentFrame's border/background display items to our - // BorderBackground() list, which isn't really correct, but it's OK because - // mContentFrame is anonymous and can't have its own border and background. + // Put the inner frame's display items on the master list. Note that this + // moves its border/background display items to our BorderBackground() list, + // which isn't really correct, but it's OK because the inner frame is + // anonymous and can't have its own border and background. contentDisplayItems.MoveTo(aLists); } @@ -265,12 +286,12 @@ nsFieldSetFrame::PaintBorderBackground(nsRenderingContext& aRenderingContext, nsCSSRendering::PaintBoxShadowInner(presContext, aRenderingContext, this, rect, aDirtyRect); - if (mLegendFrame) { + if (nsIFrame* legend = GetLegend()) { nscoord topBorder = StyleBorder()->GetComputedBorderWidth(NS_SIDE_TOP); // Use the rect of the legend frame, not mLegendRect, so we draw our // border under the legend's left and right margins. - nsRect legendRect = mLegendFrame->GetRect() + aPt; + nsRect legendRect = legend->GetRect() + aPt; // we should probably use PaintBorderEdges to do this but for now just use clipping // to achieve the same effect. @@ -328,16 +349,14 @@ nsFieldSetFrame::GetIntrinsicWidth(nsRenderingContext* aRenderingContext, { nscoord legendWidth = 0; nscoord contentWidth = 0; - if (mLegendFrame) { + if (nsIFrame* legend = GetLegend()) { legendWidth = - nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mLegendFrame, - aType); + nsLayoutUtils::IntrinsicForContainer(aRenderingContext, legend, aType); } - if (mContentFrame) { + if (nsIFrame* inner = GetInner()) { contentWidth = - nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mContentFrame, - aType); + nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, aType); } return std::max(legendWidth, contentWidth); @@ -410,29 +429,30 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext, } //------------ Handle Incremental Reflow ----------------- - bool reflowContent; + bool reflowInner; bool reflowLegend; - + nsIFrame* legend = GetLegend(); + nsIFrame* inner = GetInner(); if (aReflowState.ShouldReflowAllKids()) { - reflowContent = mContentFrame != nullptr; - reflowLegend = mLegendFrame != nullptr; + reflowInner = inner != nullptr; + reflowLegend = legend != nullptr; } else { - reflowContent = mContentFrame && NS_SUBTREE_DIRTY(mContentFrame); - reflowLegend = mLegendFrame && NS_SUBTREE_DIRTY(mLegendFrame); + reflowInner = inner && NS_SUBTREE_DIRTY(inner); + reflowLegend = legend && NS_SUBTREE_DIRTY(legend); } // We don't allow fieldsets to break vertically. If we did, we'd // need logic here to push and pull overflow frames. nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE); - NS_ASSERTION(!mContentFrame || + NS_ASSERTION(!inner || nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext, - mContentFrame, + inner, nsLayoutUtils::MIN_WIDTH) <= availSize.width, "Bogus availSize.width; should be bigger"); - NS_ASSERTION(!mLegendFrame || + NS_ASSERTION(!legend || nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext, - mLegendFrame, + legend, nsLayoutUtils::MIN_WIDTH) <= availSize.width, "Bogus availSize.width; should be bigger"); @@ -447,17 +467,17 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext, // reflow the legend only if needed if (reflowLegend) { nsHTMLReflowState legendReflowState(aPresContext, aReflowState, - mLegendFrame, availSize); + legend, availSize); nsHTMLReflowMetrics legendDesiredSize; - ReflowChild(mLegendFrame, aPresContext, legendDesiredSize, legendReflowState, + ReflowChild(legend, aPresContext, legendDesiredSize, legendReflowState, 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus); #ifdef NOISY_REFLOW printf(" returned (%d, %d)\n", legendDesiredSize.width, legendDesiredSize.height); #endif // figure out the legend's rectangle - legendMargin = mLegendFrame->GetUsedMargin(); + legendMargin = legend->GetUsedMargin(); mLegendRect.width = legendDesiredSize.width + legendMargin.left + legendMargin.right; mLegendRect.height = legendDesiredSize.height + legendMargin.top + legendMargin.bottom; mLegendRect.x = borderPadding.left; @@ -474,24 +494,24 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext, // if the legend space changes then we need to reflow the // content area as well. - if (mLegendSpace != oldSpace && mContentFrame) { - reflowContent = true; + if (mLegendSpace != oldSpace && inner) { + reflowInner = true; } - FinishReflowChild(mLegendFrame, aPresContext, &legendReflowState, + FinishReflowChild(legend, aPresContext, &legendReflowState, legendDesiredSize, 0, 0, NS_FRAME_NO_MOVE_FRAME); - } else if (!mLegendFrame) { + } else if (!legend) { mLegendRect.SetEmpty(); mLegendSpace = 0; } else { // mLegendSpace and mLegendRect haven't changed, but we need // the used margin when placing the legend. - legendMargin = mLegendFrame->GetUsedMargin(); + legendMargin = legend->GetUsedMargin(); } // reflow the content frame only if needed - if (reflowContent) { - nsHTMLReflowState kidReflowState(aPresContext, aReflowState, mContentFrame, + if (reflowInner) { + nsHTMLReflowState kidReflowState(aPresContext, aReflowState, inner, availSize); // Our child is "height:100%" but we actually want its height to be reduced // by the amount of content-height the legend is eating up, unless our @@ -513,19 +533,19 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext, NS_ASSERTION(kidReflowState.mComputedMargin == nsMargin(0,0,0,0), "Margins on anonymous fieldset child not supported!"); nsPoint pt(borderPadding.left, borderPadding.top + mLegendSpace); - ReflowChild(mContentFrame, aPresContext, kidDesiredSize, kidReflowState, + ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowState, pt.x, pt.y, 0, aStatus); - FinishReflowChild(mContentFrame, aPresContext, &kidReflowState, + FinishReflowChild(inner, aPresContext, &kidReflowState, kidDesiredSize, pt.x, pt.y, 0); NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus); } nsRect contentRect(0,0,0,0); - if (mContentFrame) { - // We don't support margins on mContentFrame, so our "content rect" is just + if (inner) { + // We don't support margins on inner, so our "content rect" is just // its rect. - contentRect = mContentFrame->GetRect(); + contentRect = inner->GetRect(); } // use the computed width if the inner content does not fill it @@ -533,11 +553,11 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext, contentRect.width = aReflowState.ComputedWidth(); } - if (mLegendFrame) { + if (legend) { // if the content rect is larger then the legend we can align the legend if (contentRect.width > mLegendRect.width) { int32_t align = static_cast - (mLegendFrame->GetContentInsertionFrame())->GetAlign(); + (legend->GetContentInsertionFrame())->GetAlign(); switch(align) { case NS_STYLE_TEXT_ALIGN_RIGHT: @@ -557,16 +577,16 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext, nsRect actualLegendRect(mLegendRect); actualLegendRect.Deflate(legendMargin); - nsPoint curOrigin = mLegendFrame->GetPosition(); + nsPoint curOrigin = legend->GetPosition(); // only if the origin changed if ((curOrigin.x != mLegendRect.x) || (curOrigin.y != mLegendRect.y)) { - mLegendFrame->SetPosition(nsPoint(actualLegendRect.x , actualLegendRect.y)); - nsContainerFrame::PositionFrameView(mLegendFrame); + legend->SetPosition(nsPoint(actualLegendRect.x , actualLegendRect.y)); + nsContainerFrame::PositionFrameView(legend); // We need to recursively process the legend frame's // children since we're moving the frame after Reflow. - nsContainerFrame::PositionChildViews(mLegendFrame); + nsContainerFrame::PositionChildViews(legend); } } @@ -584,10 +604,10 @@ nsFieldSetFrame::Reflow(nsPresContext* aPresContext, } aDesiredSize.width = contentRect.width + borderPadding.LeftRight(); aDesiredSize.SetOverflowAreasToDesiredBounds(); - if (mLegendFrame) - ConsiderChildOverflow(aDesiredSize.mOverflowAreas, mLegendFrame); - if (mContentFrame) - ConsiderChildOverflow(aDesiredSize.mOverflowAreas, mContentFrame); + if (legend) + ConsiderChildOverflow(aDesiredSize.mOverflowAreas, legend); + if (inner) + ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inner); // Merge overflow container bounds and status. aDesiredSize.mOverflowAreas.UnionWith(ocBounds); @@ -607,7 +627,7 @@ nsFieldSetFrame::AppendFrames(ChildListID aListID, { // aFrameList is not allowed to contain "the legend" for this fieldset ReparentFrameList(aFrameList); - return mContentFrame->AppendFrames(aListID, aFrameList); + return GetInner()->AppendFrames(aListID, aFrameList); } NS_IMETHODIMP @@ -616,15 +636,15 @@ nsFieldSetFrame::InsertFrames(ChildListID aListID, nsFrameList& aFrameList) { NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this || - aPrevFrame->GetParent() == mContentFrame, + aPrevFrame->GetParent() == GetInner(), "inserting after sibling frame with different parent"); // aFrameList is not allowed to contain "the legend" for this fieldset ReparentFrameList(aFrameList); - if (MOZ_UNLIKELY(aPrevFrame == mLegendFrame)) { + if (MOZ_UNLIKELY(aPrevFrame == GetLegend())) { aPrevFrame = nullptr; } - return mContentFrame->InsertFrames(aListID, aPrevFrame, aFrameList); + return GetInner()->InsertFrames(aListID, aPrevFrame, aFrameList); } NS_IMETHODIMP @@ -632,8 +652,8 @@ nsFieldSetFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) { // For reference, see bug 70648, bug 276104 and bug 236071. - NS_ASSERTION(aOldFrame != mLegendFrame, "Cannot remove mLegendFrame here"); - return mContentFrame->RemoveFrame(aListID, aOldFrame); + NS_ASSERTION(aOldFrame != GetLegend(), "Cannot remove legend here"); + return GetInner()->RemoveFrame(aListID, aOldFrame); } #ifdef ACCESSIBILITY @@ -648,10 +668,11 @@ void nsFieldSetFrame::ReparentFrameList(const nsFrameList& aFrameList) { nsFrameManager* frameManager = PresContext()->FrameManager(); + nsIFrame* inner = GetInner(); for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { - NS_ASSERTION(mLegendFrame || e.get()->GetType() != nsGkAtoms::legendFrame, + NS_ASSERTION(GetLegend() || e.get()->GetType() != nsGkAtoms::legendFrame, "The fieldset's legend is not allowed in this list"); - e.get()->SetParent(mContentFrame); + e.get()->SetParent(inner); frameManager->ReparentStyleContext(e.get()); } } @@ -659,9 +680,9 @@ nsFieldSetFrame::ReparentFrameList(const nsFrameList& aFrameList) nscoord nsFieldSetFrame::GetBaseline() const { - // We know mContentFrame is a block, so calling GetBaseline() on it will do + // We know inner is a block, so calling GetBaseline() on it will do // the right thing (that being to return the baseline of the last line). - NS_ASSERTION(nsLayoutUtils::GetAsBlock(mContentFrame), - "Unexpected mContentFrame"); - return mContentFrame->GetPosition().y + mContentFrame->GetBaseline(); + nsIFrame* inner = GetInner(); + NS_ASSERTION(nsLayoutUtils::GetAsBlock(inner), "Unexpected inner"); + return inner->GetPosition().y + inner->GetBaseline(); } From 6bda3b5219b74d05117a23784a4f6e40e08dd498 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Wed, 17 Apr 2013 22:16:15 +0200 Subject: [PATCH 12/75] Bug 762332 - Make the "EnsureTextRun should have set font size inflation" assertion non-fatal for now so it doesn't block fuzzing. r=sjohnson --- layout/generic/crashtests/crashtests.list | 1 + layout/generic/crashtests/font-inflation-762332.html | 2 ++ layout/generic/nsTextFrameThebes.cpp | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 layout/generic/crashtests/font-inflation-762332.html diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 642a81656bbe..ed4908107a5b 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -433,6 +433,7 @@ load 734777.html test-pref(layout.css.flexbox.enabled,true) load 737313-1.html test-pref(layout.css.flexbox.enabled,true) load 737313-2.html test-pref(layout.css.flexbox.enabled,true) load 737313-3.html +test-pref(font.size.inflation.emPerLine,15) asserts(1-100) load font-inflation-762332.html # bug 762332 load 762902.html load 762764-1.html load 786740-1.html diff --git a/layout/generic/crashtests/font-inflation-762332.html b/layout/generic/crashtests/font-inflation-762332.html new file mode 100644 index 000000000000..e733a56d1f34 --- /dev/null +++ b/layout/generic/crashtests/font-inflation-762332.html @@ -0,0 +1,2 @@ + +
xxxxxxxxxxxxxx x xxxxxxx x xxxxxxxxxxxxxxxxxx x xxxxxxx x
diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index e6d1d04ba27a..cf7b8702d128 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -7659,8 +7659,8 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth, EnsureTextRun(nsTextFrame::eInflated, ctx, lineContainer, aLineLayout.GetLine(), &flowEndInTextRun); - NS_ABORT_IF_FALSE(IsCurrentFontInflation(fontSizeInflation), - "EnsureTextRun should have set font size inflation"); + NS_ASSERTION(IsCurrentFontInflation(fontSizeInflation), + "EnsureTextRun should have set font size inflation"); if (mTextRun && iter.GetOriginalEnd() < offset + length) { // The textrun does not map enough text for this frame. This can happen From 590c7a70a03373b0006b88cca7313a8209e9e4a6 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Wed, 17 Apr 2013 13:18:06 -0700 Subject: [PATCH 13/75] Bug 862988 - Uplift Addon SDK to Firefox --- .../doc/module-source/sdk/lang/functional.md | 292 ++++++++++++++++++ .../source/doc/module-source/sdk/panel.md | 84 ++++- .../doc/module-source/sdk/platform/xpcom.md | 2 +- .../doc/module-source/sdk/window/utils.md | 16 + addon-sdk/source/lib/sdk/content/worker.js | 5 +- addon-sdk/source/lib/sdk/core/promise.js | 20 +- .../source/lib/sdk/deprecated/api-utils.js | 20 +- addon-sdk/source/lib/sdk/lang/type.js | 15 + addon-sdk/source/lib/sdk/panel.js | 51 ++- addon-sdk/source/lib/sdk/panel/utils.js | 95 ++++-- addon-sdk/source/lib/sdk/view/core.js | 3 + addon-sdk/source/lib/sdk/widget.js | 2 +- addon-sdk/source/lib/sdk/window/helpers.js | 19 +- addon-sdk/source/lib/sdk/window/utils.js | 19 ++ .../test-global-private-browsing.js | 2 +- .../source/test/private-browsing/helper.js | 19 +- .../source/test/tabs/test-firefox-tabs.js | 64 ++-- addon-sdk/source/test/test-panel.js | 13 +- ...st-window-utils-global-private-browsing.js | 2 +- addon-sdk/source/test/test-window-utils2.js | 17 +- addon-sdk/source/test/test-windows-common.js | 2 +- .../test/windows/test-firefox-windows.js | 2 - 22 files changed, 651 insertions(+), 113 deletions(-) create mode 100644 addon-sdk/source/doc/module-source/sdk/lang/functional.md diff --git a/addon-sdk/source/doc/module-source/sdk/lang/functional.md b/addon-sdk/source/doc/module-source/sdk/lang/functional.md new file mode 100644 index 000000000000..9b63948aab44 --- /dev/null +++ b/addon-sdk/source/doc/module-source/sdk/lang/functional.md @@ -0,0 +1,292 @@ + + +The `lang/functional` module provides functional helper methods. A lot of these +functions implement APIs from Jeremy Ashkenas's [underscore.js](http://underscorejs.org) +and all credits go to him and his contributors. + + +@function +Takes a function and returns a method associated with an object. +When the method is invoked on an instance of the object, the original +function is called. It is passed the object instance (i.e. `this`) as +the first parameter, followed by any parameters passed into the method. + + let { method } = require("sdk/lang/functional"); + let myNumber = { + times: method(times), + add: method(add), + number: 0 + }; + + function times (target, x) { return target.number *= x; } + function add (target, x) { return target.number += x; } + + console.log(myNumber.number); // 0 + myNumber.add(10); // 10 + myNumber.times(2); // 20 + myNumber.add(3); // 23 + +@param lambda {function} + The function to be wrapped and returned. + +@returns {function} + The wrapped `lambda`. + + + +@function +Takes a function and returns a wrapped version of the function. Calling the +wrapped version will call the original function during the next event loop. +This is similar to calling [setTimeout](modules/sdk/timers.html#setTimeout(callback%2C ms)) with no +wait (i.e. `setTimeout(function () { ... }, 0)`), except that the wrapped function +may be reused and does not need to be repeated each time. This also enables you +to use these functions as event listeners. + + let { defer } = require("sdk/lang/functional"); + let fn = defer(function myEvent (event, value) { + console.log(event + " : " + value); + }); + + fn("click", "#home"); + console.log("done"); + + // This will print 'done' before 'click : #home' since + // we deferred the execution of the wrapped `myEvent` + // function, making it non-blocking and executing on the + // next event loop + +@param fn {function} + The function to be deferred. + +@returns {function} + The new, deferred function. + + + +@function +An alias for [defer](modules/sdk/lang/functional.html#defer(fn)). + + + +@function +Invokes `callee`, passing `params` as an argument and `self` as `this`. +Returns the value that is returned by `callee`. + + let { invoke } = require("sdk/lang/functional"); + + invoke(sum, [1,2,3,4,5], null); // 15 + + function sum () { + return Array.slice(arguments).reduce(function (a, b) { + return a + b; + }); + } + +@param callee {function} + Function to invoke. +@param params {Array} + Parameters to be passed into `callee`. +@param self {mixed} + Object to be passed as the `this` context to `callee`. +@returns {mixed} + Returns the return value of `callee`. + + + +@function +[Curries](http://en.wikipedia.org/wiki/Currying) the given function with the arguments given. + + let { curry } = require("sdk/lang/functional"); + let add = function add (x, y) { return x + y; } + let addOne = curry(add, 1); + + addOne(5); // 6 + addOne(10); // 11 + curry(add, addOne(20))(2); // 23 + +@param fn {function} + Function to be curried. + +@param arguments... {mixed} + Additional arguments + +@returns {function} + The curried function. + + + +@function +Returns the [composition](http://en.wikipedia.org/wiki/Function_composition_(computer_science)) of a list of functions, where each function consumes the +return value of the function that follows. In math terms, composing the functions +`f()`, `g()`, and `h()` produces `f(g(h()))`. + + let { compose } = require("sdk/lang/functional"); + + let welcome = compose(exclaim, greet); + + welcome('moe'); // "hi: moe!"; + + function greet (name) { return "hi: " + name; } + function exclaim (statement) { return statement + "!"; } + +@param fn... {function} + Takes a variable number of functions as arguments and composes them from right to left. + +@returns {function} + The composed function. + + + +@function +Returns the first function passed as an argument to the second, +allowing you to adjust arguments, run code before and after, and +conditionally execute the original function. + + let { wrap } = require("sdk/lang/functional"); + + let wrappedHello = wrap(hello, function (fn, name) { + return "before, " + fn(name) + "after"; + }); + + wrappedHello("moe"); // "before, hello: moe, after" + + function hello (name) { return "hello: " + name; } + +@param fn {function} + The function to be passed into the `wrapper` function. + +@param wrapper {function} + The function that is called when the return function is executed, + taking the wrapped `fn` as the first parameter. + +@returns {function} + A function which, when called, executes `wrapper` with `fn` as the first parameter, + and passes in any additional parameters to the `wrapper` function. + + + +@function +Returns the same value that is used as the argument. In math: f(x) = x. + + let { identity } = require("sdk/lang/functional"); + let x = 5; + identity(x); // 5 + +@param value {mixed} + The value to be returned. + +@returns {mixed} + The value that was originally passed in. + + + +@function +[Memoizes](http://en.wikipedia.org/wiki/Memoization) a given function by caching +the computed result. Useful for speeding up slow-running computations. If +passed an optional `hashFunction`, it will be used to compute the hash key for +storing the result, based on the arguments to the original function. The +default `hashFunction` just uses the first argument to the memoized function as +the key. + + let { memoize } = require("sdk/lang/functional"); + + let memoizedFn = memoize(primeFactorization); + + memoizedFn(50); // Returns [2, 5, 5], had to compute + memoizedFn(100); // Returns [2, 2, 5, 5], had to compute + memoizedFn(50); // Returns [2, 5, 5] again, but pulled from cache + + function primeFactorization (x) { + // Some tricky stuff + } + + // We can also use a hash function to compute a different + // hash value. In this example, we'll fabricate a function + // that takes a string of first and last names that + // somehow computes the lineage of that name. Our hash + // function will just parse the last name, as our naive + // implementation assumes that they will share the same lineage + + let getLineage = memoize(function (name) { + // computes lineage + return data; + }, hasher); + + // Hashing function takes a string of first and last name + // and returns the last name. + function hasher (input) { + return input.split(" ")[1]; + } + + getLineage("homer simpson"); // Computes and returns information for "simpson" + getLineage("lisa simpson"); // Returns cached for "simpson" + +@param fn {function} + The function that becomes memoized. + +@param hasher {function} + An optional function that takes the memoized function's parameter and returns + a hash key for storing the result. + +@returns {function} + The memoized version of `fn`. + + + +@function +Much like `setTimeout`, `delay` invokes a function after waiting a set number of +milliseconds. If you pass additional, optional, arguments, they will be forwarded +on to the function when it is invoked. + + let { delay } = require("sdk/lang/functional"); + + delay(printAdd, 2000, 5, 10); + + // Prints "5+10=15" in two seconds (2000ms) + function printAdd (a, b) { console.log(a + "+" + b + "=" + (a+b)); } + +@param fn {function} + A function to be delayed. + +@param ms {number} + Number of milliseconds to delay the execution of `fn`. + +@param arguments {mixed} + Additional arguments to pass to `fn` upon execution + + + +@function +Creates a version of the input function that can only be called one time. +Repeated calls to the modified function will have no effect, returning +the value from the original call. Useful for initialization functions, instead +of having to set a boolean flag and checking it later. + + let { once } = require("sdk/lang/functional"); + let setup = once(function (env) { + // initializing important things + console.log("successfully initialized " + env); + return 1; // Assume success and return 1 + }); + + setup('dev'); // returns 1 + // prints "successfully initialized dev" + + // Future attempts to call this function just return the cached + // value that was returned previously + setup('production'); // Returns 1 + // No print message is displayed since the function isn't executed + +@param fn {function} + The function that will be executed only once inside the once wrapper. + +@returns {function} + The wrapped `fn` that can only be executed once. + + + +@function +An alias for [once](modules/sdk/lang/functional.html#once(fn)). + diff --git a/addon-sdk/source/doc/module-source/sdk/panel.md b/addon-sdk/source/doc/module-source/sdk/panel.md index c4fecb2f82f7..ed410703eb02 100644 --- a/addon-sdk/source/doc/module-source/sdk/panel.md +++ b/addon-sdk/source/doc/module-source/sdk/panel.md @@ -421,7 +421,73 @@ Creates a panel. The width of the panel in pixels. Optional. @prop [height] {number} The height of the panel in pixels. Optional. - @prop [focus] {boolean} + @prop [position] {object} + The position of the panel. + Ignored if the panel is opened by a widget. + + You can set as value an object that has one or more of the following + properites: `top`, `right`, `bottom` and `left`. Their values are expressed + in pixels. Any other properties will be ignored. + + The default alignment is centered, so for example panel can be displayed in + the center of the bottom corner by leaving off vertical axis: + + // Show the panel to the centered horizontally and aligned to the bottom + // of the content area + require("sdk/panel").Panel({ + position: { + bottom: 0 + } + }).show(); + + // Show the panel to the centered vertically and aligned to the left o + // the content area + require("sdk/panel").Panel({ + position: { + left: 0 + } + }).show(); + + // Centered panel, default behavior + require("sdk/panel").Panel({}).show(); + + In the same way of their CSS counterpart, setting both `top` and `bottom`, + or `left` and `right`, will results in calculated the `height` and `width`: + + // Show the panel centered horizontally, that is distant 40px + // from the top and 100px from the bottom. + require("sdk/panel").Panel({ + position: { + top: 40, + bottom: 100 + } + }).show(); + + Set implicitly `height` in this example, will makes the panel ignore the + `bottom` property, as the CSS homonym properties does: + + // Show the panel centered horizontally, that is distant 40px from the top + // and has 400px as height + require("sdk/panel").Panel({ + position: { + top: 40, + bottom: 100, + }, + height: 400 + }).show(); + + // This is equivalent to: + + require("panel").Panel({ + position { + top: 40 + }, + height: 400 + }).show(); + + The same principle is applied for `width`, `left` and `right`. + + @prop [focus=true] {boolean} Set to `false` to prevent taking the focus away when the panel is shown. Only turn this off if necessary, to prevent accessibility issue. Optional, default to `true`. @@ -575,6 +641,22 @@ The message to send. Must be stringifiable to JSON. @method Displays the panel. + +If the `options` argument is given, it will be shallow merged with the options +provided in the constructor: the `options` passed in the `show` method takes +the precedence. +It's useful for temporary changes, without touching the default values. + +@param options {object} + Showing options for the panel, with the following keys: + @prop [width] {number} + The width of the panel in pixels. Optional. + @prop [height] {number} + The height of the panel in pixels. Optional. + @prop [position] {object} + The position of the panel. Optional. See [Panel's options](./modules/sdk/panel.html#Panel%28options%29) for further details. + @prop [focus=true] {boolean} + Set to `false` to prevent taking the focus away when the panel is shown. diff --git a/addon-sdk/source/doc/module-source/sdk/platform/xpcom.md b/addon-sdk/source/doc/module-source/sdk/platform/xpcom.md index 96797cb11287..fa0a414e2f7e 100644 --- a/addon-sdk/source/doc/module-source/sdk/platform/xpcom.md +++ b/addon-sdk/source/doc/module-source/sdk/platform/xpcom.md @@ -48,7 +48,7 @@ interface to listen for and log all topic notifications: observerService.addObserver(this, this.topic, false); }, unregister: function() { - addObserver.removeObserver(this, this.topic); + observerService.removeObserver(this, this.topic); }, observe: function observe(subject, topic, data) { console.log('star observer:', subject, topic, data); diff --git a/addon-sdk/source/doc/module-source/sdk/window/utils.md b/addon-sdk/source/doc/module-source/sdk/window/utils.md index d0def80b23ff..22135da28b5e 100644 --- a/addon-sdk/source/doc/module-source/sdk/window/utils.md +++ b/addon-sdk/source/doc/module-source/sdk/window/utils.md @@ -79,6 +79,22 @@ to support private browsing, refer to the @returns {nsIBaseWindow} + + @function + Returns the toplevel + [`nsIDOMWindow`](https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIDOMWindow) + for the given child [`nsIDOMWindow`](https://developer.mozilla.org/en/nsIDOMWindow): + + var { Ci } = require('chrome'); + var utils = require('sdk/window/utils'); + var browserWindow = utils.getMostRecentBrowserWindow(); + var window = browserWindow.content; // `window` object for the current webpage + utils.getToplevelWindw(window) == browserWindow // => true + + @param window {nsIDOMWindow} + @returns {nsIDOMWindow} + + @function Returns the diff --git a/addon-sdk/source/lib/sdk/content/worker.js b/addon-sdk/source/lib/sdk/content/worker.js index ef1cd4fe955d..51d79fbc9055 100644 --- a/addon-sdk/source/lib/sdk/content/worker.js +++ b/addon-sdk/source/lib/sdk/content/worker.js @@ -130,7 +130,7 @@ const WorkerSandbox = EventEmitter.compose({ // Instantiate trusted code in another Sandbox in order to prevent content // script from messing with standard classes used by proxy and API code. - let apiSandbox = sandbox(window, { wantXrays: true }); + let apiSandbox = sandbox(window, { wantXrays: true, sameZoneAs: window }); apiSandbox.console = console; // Build content proxies only if the document has a non-system principal @@ -148,7 +148,8 @@ const WorkerSandbox = EventEmitter.compose({ // have access to all standard globals (window, document, ...) let content = this._sandbox = sandbox(window, { sandboxPrototype: proto, - wantXrays: true + wantXrays: true, + sameZoneAs: window }); // We have to ensure that window.top and window.parent are the exact same // object than window object, i.e. the sandbox global object. But not diff --git a/addon-sdk/source/lib/sdk/core/promise.js b/addon-sdk/source/lib/sdk/core/promise.js index 808127af015c..599b62654342 100644 --- a/addon-sdk/source/lib/sdk/core/promise.js +++ b/addon-sdk/source/lib/sdk/core/promise.js @@ -105,7 +105,7 @@ function defer(prototype) { // `null` promise is not resolved yet. var result = null; - prototype = (prototype || prototype === null) ? prototype : Object.prototype + prototype = (prototype || prototype === null) ? prototype : Object.prototype; // Create an object implementing promise API. var promise = Object.create(prototype, { @@ -137,7 +137,7 @@ function defer(prototype) { } catch(error) { if (exports._reportErrors && typeof(console) === 'object') - console.error(error) + console.error(error); deferred.resolve(rejected(error)); } } @@ -244,8 +244,8 @@ var promised = (function() { // slower property accesses and unnecessary closure creations on each // call of this popular function. - var call = Function.call - var concat = Array.prototype.concat + var call = Function.call; + var concat = Array.prototype.concat; // Utility function that does following: // execute([ f, self, args...]) => f.apply(self, args) @@ -256,9 +256,9 @@ var promised = (function() { function promisedConcat(promises, unknown) { return promises.then(function(values) { return resolve(unknown).then(function(value) { - return values.concat([ value ]) - }) - }) + return values.concat([ value ]); + }); + }); } return function promised(f, prototype) { @@ -280,10 +280,10 @@ var promised = (function() { // reduce it via `promisedConcat` to get promised array of fulfillments reduce(promisedConcat, resolve([], prototype)). // finally map that to promise of `f.apply(this, args...)` - then(execute) - } + then(execute); + }; } -})() +})(); exports.promised = promised; var all = promised(Array); diff --git a/addon-sdk/source/lib/sdk/deprecated/api-utils.js b/addon-sdk/source/lib/sdk/deprecated/api-utils.js index 521ad6ea5010..0b59dc56c6cb 100644 --- a/addon-sdk/source/lib/sdk/deprecated/api-utils.js +++ b/addon-sdk/source/lib/sdk/deprecated/api-utils.js @@ -96,6 +96,9 @@ exports.validateOptions = function validateOptions(options, requirements) { optsVal = req.map(optsVal); } catch (err) { + if (err instanceof RequirementError) + throw err; + mapThrew = true; } } @@ -108,12 +111,12 @@ exports.validateOptions = function validateOptions(options, requirements) { } }); if (req.is.indexOf(getTypeOf(optsVal)) < 0) - throw requirementError(key, req); + throw new RequirementError(key, req); } if (req.ok && !req.ok(optsVal)) - throw requirementError(key, req); + throw new RequirementError(key, req); - if (keyInOpts || (req.map && !mapThrew)) + if (keyInOpts || (req.map && !mapThrew && optsVal !== undefined)) validatedOptions[key] = optsVal; } @@ -145,8 +148,11 @@ let getTypeOf = exports.getTypeOf = function getTypeOf(val) { return typ; } -// Returns a new Error with a nice message. -function requirementError(key, requirement) { +function RequirementError(key, requirement) { + Error.call(this); + + this.name = "RequirementError"; + let msg = requirement.msg; if (!msg) { msg = 'The option "' + key + '" '; @@ -154,5 +160,7 @@ function requirementError(key, requirement) { "must be one of the following types: " + requirement.is.join(", ") : "is invalid."; } - return new Error(msg); + + this.message = msg; } +RequirementError.prototype = Object.create(Error.prototype); diff --git a/addon-sdk/source/lib/sdk/lang/type.js b/addon-sdk/source/lib/sdk/lang/type.js index a184edf9b75e..4f221ed099d8 100644 --- a/addon-sdk/source/lib/sdk/lang/type.js +++ b/addon-sdk/source/lib/sdk/lang/type.js @@ -31,6 +31,21 @@ function isNull(value) { } exports.isNull = isNull; +/** + * Returns `true` if value is `null` or `undefined`. + * It's equivalent to `== null`, but resolve the ambiguity of the writer + * intention, makes clear that he's clearly checking both `null` and `undefined` + * values, and it's not a typo for `=== null`. + */ +function isNil(value) { + return value === null || value === undefined; +} +exports.isNil = isNil; + +function isBoolean(value) { + return typeof value === "boolean"; +} +exports.isBoolean = isBoolean; /** * Returns `true` if value is a string. * @examples diff --git a/addon-sdk/source/lib/sdk/panel.js b/addon-sdk/source/lib/sdk/panel.js index 1eb3ebb71ba7..c60bbce95e2f 100644 --- a/addon-sdk/source/lib/sdk/panel.js +++ b/addon-sdk/source/lib/sdk/panel.js @@ -29,7 +29,8 @@ const domPanel = require("./panel/utils"); const { events } = require("./panel/events"); const systemEvents = require("./system/events"); const { filter, pipe } = require("./event/utils"); -const { getNodeView } = require("./view/core"); +const { getNodeView, getActiveView } = require("./view/core"); +const { isNil, isObject } = require("./lang/type"); if (isPrivateBrowsingSupported && isWindowPBSupported) throw Error('The panel module cannot be used with per-window private browsing at the moment, see Bug 816257'); @@ -65,11 +66,26 @@ function getAttachEventType(model) { let number = { is: ['number', 'undefined', 'null'] }; let boolean = { is: ['boolean', 'undefined', 'null'] }; -let panelContract = contract(merge({ +let rectContract = contract({ + top: number, + right: number, + bottom: number, + left: number +}); + +let rect = { + is: ['object', 'undefined', 'null'], + map: function(v) isNil(v) || !isObject(v) ? v : rectContract(v) +} + +let displayContract = contract({ width: number, height: number, focus: boolean, -}, loaderContract.rules)); + position: rect +}); + +let panelContract = contract(merge({}, displayContract.rules, loaderContract.rules)); function isDisposed(panel) !views.has(panel); @@ -80,12 +96,12 @@ let views = new WeakMap(); let workers = new WeakMap(); function viewFor(panel) views.get(panel) -exports.viewFor = viewFor; - function modelFor(panel) models.get(panel) function panelFor(view) panels.get(view) function workerFor(panel) workers.get(panel) +getActiveView.define(Panel, viewFor); + // Utility function takes `panel` instance and makes sure it will be // automatically hidden as soon as other panel is shown. let setupAutoHide = new function() { @@ -124,9 +140,10 @@ const Panel = Class({ extends: WorkerHost(workerFor), setup: function setup(options) { let model = merge({ - width: 320, - height: 240, + defaultWidth: 320, + defaultHeight: 240, focus: true, + position: Object.freeze({}), }, panelContract(options)); models.set(this, model); @@ -172,6 +189,9 @@ const Panel = Class({ /* Public API: Panel.focus */ get focus() modelFor(this).focus, + /* Public API: Panel.position */ + get position() modelFor(this).position, + get contentURL() modelFor(this).contentURL, set contentURL(value) { let model = modelFor(this); @@ -183,13 +203,22 @@ const Panel = Class({ get isShowing() !isDisposed(this) && domPanel.isOpen(viewFor(this)), /* Public API: Panel.show */ - show: function show(anchor) { + show: function show(options, anchor) { let model = modelFor(this); let view = viewFor(this); let anchorView = getNodeView(anchor); + options = merge({ + position: model.position, + width: model.width, + height: model.height, + defaultWidth: model.defaultWidth, + defaultHeight: model.defaultHeight, + focus: model.focus + }, displayContract(options)); + if (!isDisposed(this)) - domPanel.show(view, model.width, model.height, model.focus, anchorView); + domPanel.show(view, options, anchorView); return this; }, @@ -207,8 +236,8 @@ const Panel = Class({ let model = modelFor(this); let view = viewFor(this); let change = panelContract({ - width: width || model.width, - height: height || model.height + width: width || model.width || model.defaultWidth, + height: height || model.height || model.defaultHeight }); model.width = change.width diff --git a/addon-sdk/source/lib/sdk/panel/utils.js b/addon-sdk/source/lib/sdk/panel/utils.js index 63029cce38d4..827559f21cfb 100644 --- a/addon-sdk/source/lib/sdk/panel/utils.js +++ b/addon-sdk/source/lib/sdk/panel/utils.js @@ -12,18 +12,67 @@ const { Cc, Ci } = require("chrome"); const { setTimeout } = require("../timers"); const { platform } = require("../system"); const { getMostRecentBrowserWindow, getOwnerBrowserWindow, - getHiddenWindow } = require("../window/utils"); + getHiddenWindow, getScreenPixelsPerCSSPixel } = require("../window/utils"); + const { create: createFrame, swapFrameLoaders } = require("../frame/utils"); const { window: addonWindow } = require("../addon/window"); +const { isNil } = require("../lang/type"); const events = require("../system/events"); const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; -function open(panel, width, height, anchor) { +function calculateRegion({ position, width, height, defaultWidth, defaultHeight }, rect) { + let x, y; + + let hasTop = !isNil(position.top); + let hasRight = !isNil(position.right); + let hasBottom = !isNil(position.bottom); + let hasLeft = !isNil(position.left); + let hasWidth = !isNil(width); + let hasHeight = !isNil(height); + + // if width is not specified by constructor or show's options, then get + // the default width + if (!hasWidth) + width = defaultWidth; + + // if height is not specified by constructor or show's options, then get + // the default height + if (!hasHeight) + height = defaultHeight; + + // default position is centered + x = (rect.right - width) / 2; + y = (rect.top + rect.bottom - height) / 2; + + if (hasTop) { + y = rect.top + position.top; + + if (hasBottom && !hasHeight) + height = rect.bottom - position.bottom - y; + } + else if (hasBottom) { + y = rect.bottom - position.bottom - height; + } + + if (hasLeft) { + x = position.left; + + if (hasRight && !hasWidth) + width = rect.right - position.right - x; + } + else if (hasRight) { + x = rect.right - width - position.right; + } + + return {x: x, y: y, width: width, height: height}; +} + +function open(panel, options, anchor) { // Wait for the XBL binding to be constructed - if (!panel.openPopup) setTimeout(open, 50, panel, width, height, anchor); - else display(panel, width, height, anchor); + if (!panel.openPopup) setTimeout(open, 50, panel, options, anchor); + else display(panel, options, anchor); } exports.open = open; @@ -52,29 +101,37 @@ function resize(panel, width, height) { } exports.resize = resize -function display(panel, width, height, anchor) { +function display(panel, options, anchor) { let document = panel.ownerDocument; - let x = null; - let y = null; - let position = null; + + let x, y; + let { width, height, defaultWidth, defaultHeight } = options; + + let popupPosition = null; // Panel XBL has some SDK incompatible styling decisions. We shim panel // instances until proper fix for Bug 859504 is shipped. shimDefaultStyle(panel); if (!anchor) { - // Open the popup in the middle of the window. - x = document.documentElement.clientWidth / 2 - width / 2; - y = document.documentElement.clientHeight / 2 - height / 2; - position = null; + // The XUL Panel doesn't have an arrow, so the margin needs to be reset + // in order to, be positioned properly + panel.style.margin = "0"; + + let viewportRect = document.defaultView.gBrowser.getBoundingClientRect(); + + ({x, y, width, height}) = calculateRegion(options, viewportRect); } else { + width = width || defaultWidth; + height = height || defaultHeight; + // Open the popup by the anchor. let rect = anchor.getBoundingClientRect(); let window = anchor.ownerDocument.defaultView; - let zoom = window.mozScreenPixelsPerCSSPixel; + let zoom = getScreenPixelsPerCSSPixel(window); let screenX = rect.left + window.mozInnerScreenX * zoom; let screenY = rect.top + window.mozInnerScreenY * zoom; @@ -92,7 +149,7 @@ function display(panel, width, height, anchor) { horizontal = "right"; let verticalInverse = vertical == "top" ? "bottom" : "top"; - position = vertical + "center " + verticalInverse + horizontal; + popupPosition = vertical + "center " + verticalInverse + horizontal; // Allow panel to flip itself if the panel can't be displayed at the // specified position (useful if we compute a bad position or if the @@ -105,7 +162,7 @@ function display(panel, width, height, anchor) { panel.firstChild.style.width = width + "px"; panel.firstChild.style.height = height + "px"; - panel.openPopup(anchor, position, x, y); + panel.openPopup(anchor, popupPosition, x, y); } exports.display = display; @@ -124,16 +181,16 @@ function shimDefaultStyle(panel) { }); } -function show(panel, width, height, focus, anchor) { +function show(panel, options, anchor) { // Prevent the panel from getting focus when showing up // if focus is set to false - panel.setAttribute("noautofocus", !focus); - + panel.setAttribute("noautofocus", !options.focus); let window = anchor && getOwnerBrowserWindow(anchor); let { document } = window ? window : getMostRecentBrowserWindow(); attach(panel, document); - open(panel, width, height, anchor); + + open(panel, options, anchor); } exports.show = show diff --git a/addon-sdk/source/lib/sdk/view/core.js b/addon-sdk/source/lib/sdk/view/core.js index f5cb109134aa..76738cba526c 100644 --- a/addon-sdk/source/lib/sdk/view/core.js +++ b/addon-sdk/source/lib/sdk/view/core.js @@ -24,3 +24,6 @@ getNodeView.define(function(value) { }); exports.getNodeView = getNodeView; + +let getActiveView = method("getActiveView"); +exports.getActiveView = getActiveView; diff --git a/addon-sdk/source/lib/sdk/widget.js b/addon-sdk/source/lib/sdk/widget.js index e872f45ac756..1011dd0822f6 100644 --- a/addon-sdk/source/lib/sdk/widget.js +++ b/addon-sdk/source/lib/sdk/widget.js @@ -435,7 +435,7 @@ const WidgetViewTrait = LightTrait.compose(EventEmitterTrait, LightTrait({ // This kind of ugly workaround, instead we should implement // `getNodeView` for the `Widget` class itself, but that's kind of // hard without cleaning things up. - this.panel.show(getNodeView.implement({}, function() domNode)); + this.panel.show(null, getNodeView.implement({}, function() domNode)); }, _isInWindow: function WidgetView__isInWindow(window) { diff --git a/addon-sdk/source/lib/sdk/window/helpers.js b/addon-sdk/source/lib/sdk/window/helpers.js index ae95efd4927f..43a11b14b406 100644 --- a/addon-sdk/source/lib/sdk/window/helpers.js +++ b/addon-sdk/source/lib/sdk/window/helpers.js @@ -4,7 +4,8 @@ 'use strict'; const { defer } = require('../core/promise'); -const { open: openWindow, onFocus } = require('./utils'); +const events = require('../system/events'); +const { open: openWindow, onFocus, getToplevelWindow } = require('./utils'); function open(uri, options) { return promise(openWindow.apply(null, arguments), 'load'); @@ -12,11 +13,19 @@ function open(uri, options) { exports.open = open; function close(window) { - // unload event could happen so fast that it is not resolved - // if we listen to unload after calling close() - let p = promise(window, 'unload'); + // We shouldn't wait for unload, as it is dispatched + // before the window is actually closed. + // `domwindowclosed` is a better match. + let deferred = defer(); + let toplevelWindow = getToplevelWindow(window); + events.on("domwindowclosed", function onclose({subject}) { + if (subject == toplevelWindow) { + events.off("domwindowclosed", onclose); + deferred.resolve(window); + } + }, true); window.close(); - return p; + return deferred.promise; } exports.close = close; diff --git a/addon-sdk/source/lib/sdk/window/utils.js b/addon-sdk/source/lib/sdk/window/utils.js index 8eb383f5fcd4..0375ca4527be 100644 --- a/addon-sdk/source/lib/sdk/window/utils.js +++ b/addon-sdk/source/lib/sdk/window/utils.js @@ -113,6 +113,19 @@ function getBaseWindow(window) { } exports.getBaseWindow = getBaseWindow; +/** + * Returns the `nsIDOMWindow` toplevel window for any child/inner window + */ +function getToplevelWindow(window) { + return window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); +} +exports.getToplevelWindow = getToplevelWindow; + function getWindowDocShell(window) window.gBrowser.docShell; exports.getWindowDocShell = getWindowDocShell; @@ -341,6 +354,12 @@ function getFrames(window) { } exports.getFrames = getFrames; +function getScreenPixelsPerCSSPixel(window) { + return window.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils).screenPixelsPerCSSPixel; +} +exports.getScreenPixelsPerCSSPixel = getScreenPixelsPerCSSPixel; + function getOwnerBrowserWindow(node) { /** Takes DOM node and returns browser window that contains it. diff --git a/addon-sdk/source/test/addons/private-browsing-supported/test-global-private-browsing.js b/addon-sdk/source/test/addons/private-browsing-supported/test-global-private-browsing.js index 86bf9dad9ba1..dc1b33899f8c 100644 --- a/addon-sdk/source/test/addons/private-browsing-supported/test-global-private-browsing.js +++ b/addon-sdk/source/test/addons/private-browsing-supported/test-global-private-browsing.js @@ -70,7 +70,7 @@ exports.testShowPanelAndWidgetOnPrivateWindow = function(assert, done) { } }); } - }).show(window.gBrowser); + }).show(null, window.gBrowser); }, onUntrack: function(window) { if (window === myPrivateWindow) { diff --git a/addon-sdk/source/test/private-browsing/helper.js b/addon-sdk/source/test/private-browsing/helper.js index efac72df41ef..71f0d30d103e 100644 --- a/addon-sdk/source/test/private-browsing/helper.js +++ b/addon-sdk/source/test/private-browsing/helper.js @@ -14,6 +14,7 @@ const { openDialog, getMostRecentBrowserWindow } = require('sdk/window/utils'); const { openTab, getTabContentWindow, getActiveTab, setTabURL, closeTab } = require('sdk/tabs/utils'); const promise = require("sdk/core/promise"); const windowHelpers = require('sdk/window/helpers'); +const events = require("sdk/system/events"); function LoaderWithHookedConsole(module) { let globals = {}; @@ -71,13 +72,19 @@ exports.openWebpage = function openWebpage(url, enablePrivate) { private: enablePrivate }); let deferred = promise.defer(); - win.addEventListener("load", function onLoad() { - win.removeEventListener("load", onLoad, false); - let rawTab = getActiveTab(win); - setTabURL(rawTab, url); - deferred.resolve(getTabContentWindow(rawTab)); - }); + // Wait for delayed startup code to be executed, in order to ensure + // that the window is really ready + events.on("browser-delayed-startup-finished", function onReady({subject}) { + if (subject == win) { + events.off("browser-delayed-startup-finished", onReady, true); + + let rawTab = getActiveTab(win); + setTabURL(rawTab, url); + deferred.resolve(getTabContentWindow(rawTab)); + } + }, true); + return { ready: deferred.promise, close: function () { diff --git a/addon-sdk/source/test/tabs/test-firefox-tabs.js b/addon-sdk/source/test/tabs/test-firefox-tabs.js index 191a68184f73..607952399ac1 100644 --- a/addon-sdk/source/test/tabs/test-firefox-tabs.js +++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js @@ -937,49 +937,37 @@ exports['test ready event on new window tab'] = function(test) { }; exports['test unique tab ids'] = function(test) { - test.waitUntilDone(); + var windows = require('sdk/windows').browserWindows; + var { all, defer } = require('sdk/core/promise'); - var windows = require('sdk/windows').browserWindows, - tabIds = {}, win1, win2; + function openWindow() { + // console.log('in openWindow'); + let deferred = defer(); + let win = windows.open({ + url: "data:text/html;charset=utf-8,foo", + }); - let steps = [ - function (index) { - win1 = windows.open({ - url: "data:text/html;charset=utf-8,foo", - onOpen: function(window) { - tabIds['tab1'] = window.tabs.activeTab.id; - next(index); - } + win.on('open', function(window) { + test.assert(window.tabs.length); + test.assert(window.tabs.activeTab); + test.assert(window.tabs.activeTab.id); + deferred.resolve({ + id: window.tabs.activeTab.id, + win: win }); - }, - function (index) { - win2 = windows.open({ - url: "data:text/html;charset=utf-8,foo", - onOpen: function(window) { - tabIds['tab2'] = window.tabs.activeTab.id; - next(index); - } - }); - }, - function (index) { - test.assertNotEqual(tabIds.tab1, tabIds.tab2, "Tab ids should be unique."); - win1.close(); - win2.close(); - test.done(); - } - ]; - - function next(index) { - if (index === steps.length) { - return; - } - let fn = steps[index]; - index++ - fn(index); + }); + + return deferred.promise; } - // run! - next(0); + test.waitUntilDone(); + var one = openWindow(), two = openWindow(); + all([one, two]).then(function(results) { + test.assertNotEqual(results[0].id, results[1].id, "tab Ids should not be equal."); + results[0].win.close(); + results[1].win.close(); + test.done(); + }); } // related to Bug 671305 diff --git a/addon-sdk/source/test/test-panel.js b/addon-sdk/source/test/test-panel.js index 80e0d26d4937..3809e1cc48f7 100644 --- a/addon-sdk/source/test/test-panel.js +++ b/addon-sdk/source/test/test-panel.js @@ -318,7 +318,7 @@ exports["test Anchor And Arrow"] = function(assert, done) { return; } let { panel, anchor } = queue.shift(); - panel.show(anchor); + panel.show(null, anchor); } let tabs= require("sdk/tabs"); @@ -462,6 +462,7 @@ exports["test Change Content URL"] = function(assert, done) { contentURL: "about:blank", contentScript: "self.port.emit('ready', document.location.href);" }); + let count = 0; panel.port.on("ready", function (location) { count++; @@ -651,7 +652,7 @@ if (isWindowPBSupported) { showTries++; panel.show(); showTries++; - panel.show(browserWindow.gBrowser); + panel.show(null, browserWindow.gBrowser); return promise; }). @@ -703,9 +704,9 @@ if (isWindowPBSupported) { } }); showTries++; - panel.show(window.gBrowser); + panel.show(null, window.gBrowser); showTries++; - panel.show(browserWindow.gBrowser); + panel.show(null, browserWindow.gBrowser); return promise; }). @@ -753,7 +754,7 @@ exports['test Style Applied Only Once'] = function (assert, done) { 'self.port.on("check",function() { self.port.emit("count", document.getElementsByTagName("style").length); });' + 'self.port.on("ping", function (count) { self.port.emit("pong", count); });' }); - + panel.port.on('count', function (styleCount) { assert.equal(styleCount, 1, 'should only have one style'); done(); @@ -836,7 +837,7 @@ else if (isGlobalPBSupported) { assert.ok(isPrivate(window), 'window is private'); assert.equal(getWindow(window.gBrowser), window, 'private window elements returns window'); assert.equal(getWindow(activeWindow.gBrowser), activeWindow, 'active window elements returns window'); - + pb.once('stop', done); pb.deactivate(); }) diff --git a/addon-sdk/source/test/test-window-utils-global-private-browsing.js b/addon-sdk/source/test/test-window-utils-global-private-browsing.js index 39d7aa61ddb2..26cdfabf8779 100644 --- a/addon-sdk/source/test/test-window-utils-global-private-browsing.js +++ b/addon-sdk/source/test/test-window-utils-global-private-browsing.js @@ -61,7 +61,7 @@ exports.testShowPanelAndWidgetOnPrivateWindow = function(assert, done) { } }); } - }).show(window.gBrowser); + }).show(null, window.gBrowser); }, onUntrack: function(window) { if (window === myPrivateWindow) { diff --git a/addon-sdk/source/test/test-window-utils2.js b/addon-sdk/source/test/test-window-utils2.js index 080ad072c626..758120393f27 100644 --- a/addon-sdk/source/test/test-window-utils2.js +++ b/addon-sdk/source/test/test-window-utils2.js @@ -5,7 +5,7 @@ const { Ci } = require('chrome'); const { open, backgroundify, windows, isBrowser, - getXULWindow, getBaseWindow, getMostRecentWindow, + getXULWindow, getBaseWindow, getToplevelWindow, getMostRecentWindow, getMostRecentBrowserWindow } = require('sdk/window/utils'); const { close } = require('sdk/window/helpers'); const windowUtils = require('sdk/deprecated/window-utils'); @@ -28,6 +28,16 @@ exports['test get nsIXULWindow from nsIDomWindow'] = function(assert) { 'base returns nsIXULWindow'); }; +exports['test getToplevelWindow'] = function(assert) { + let active = windowUtils.activeBrowserWindow; + assert.equal(getToplevelWindow(active), active, + 'getToplevelWindow of toplevel window returns the same window'); + assert.equal(getToplevelWindow(active.content), active, + 'getToplevelWindow of tab window returns the browser window'); + assert.ok(getToplevelWindow(active) instanceof Ci.nsIDOMWindow, + 'getToplevelWindow returns nsIDOMWindow'); +}; + exports['test top window creation'] = function(assert, done) { let window = open('data:text/html;charset=utf-8,Hello top window'); assert.ok(~windows().indexOf(window), 'window was opened'); @@ -61,7 +71,10 @@ exports.testBackgroundify = function(assert, done) { 'backgroundifyied window is in the list of windows'); // Wait for the window unload before ending test - close(window).then(done); + // backgroundified windows doesn't dispatch domwindowclosed event + // so that we have to manually wait for unload event + window.onunload = done; + window.close(); }; exports.testIsBrowser = function(assert) { diff --git a/addon-sdk/source/test/test-windows-common.js b/addon-sdk/source/test/test-windows-common.js index 05cd8bf5dcce..b77229403f2f 100644 --- a/addon-sdk/source/test/test-windows-common.js +++ b/addon-sdk/source/test/test-windows-common.js @@ -39,7 +39,7 @@ exports.testWindowTabsObject_alt = function(test) { test.assertNotEqual(window.tabs.activeTab, tab, "Correct active tab"); // end test - tab.close(test.done()); + tab.close(test.done.bind(test)); } }); }; diff --git a/addon-sdk/source/test/windows/test-firefox-windows.js b/addon-sdk/source/test/windows/test-firefox-windows.js index 551ec4865566..e6ab606952a0 100644 --- a/addon-sdk/source/test/windows/test-firefox-windows.js +++ b/addon-sdk/source/test/windows/test-firefox-windows.js @@ -356,8 +356,6 @@ exports.testWindowOpenPrivateDefault = function(test) { url: 'about:mozilla', isPrivate: true, onOpen: function(window) { - test.assertEqual(); - let tab = window.tabs[0]; tab.once('ready', function() { test.assertEqual(tab.url, 'about:mozilla', 'opened correct tab'); From 026959e36f838bdf33f59f4799cd74943c52610a Mon Sep 17 00:00:00 2001 From: Ralph Giles Date: Tue, 16 Apr 2013 15:53:00 -0700 Subject: [PATCH 14/75] Bug 862586 - Update webvtt parser to v0.4. r=cpearce Update media/webvtt to the v0.4 tag of https://github.com/mozilla/webvtt.git master --- media/webvtt/README_MOZILLA | 2 +- media/webvtt/alloc.c | 2 + media/webvtt/cue.c | 8 + media/webvtt/cue_internal.h | 5 +- media/webvtt/cuetext.c | 216 +++-- media/webvtt/include/webvtt/cue.h | 1 + media/webvtt/include/webvtt/node.h | 19 +- media/webvtt/include/webvtt/string.h | 9 +- media/webvtt/lexer.c | 110 ++- media/webvtt/node.c | 3 +- media/webvtt/parser.c | 1221 +++++++++++++------------- media/webvtt/parser_internal.h | 58 +- media/webvtt/string.c | 44 +- media/webvtt/string_internal.h | 54 +- 14 files changed, 905 insertions(+), 847 deletions(-) diff --git a/media/webvtt/README_MOZILLA b/media/webvtt/README_MOZILLA index 492341477ab3..257f36aec2e1 100644 --- a/media/webvtt/README_MOZILLA +++ b/media/webvtt/README_MOZILLA @@ -1,5 +1,5 @@ These files are from the WebVTT library, and are extracted from rev -6b637c9f30911433e6d5a5be5d8512317a0d8a14 of the git repository at +c93accafb2087df7c678748e25bca21f1173979f of the git repository at https://github.com/mozilla/webvtt. The following CPPFLAGS are used in order to build and link in Mozilla diff --git a/media/webvtt/alloc.c b/media/webvtt/alloc.c index 41e1879d24da..a8d111c320ad 100644 --- a/media/webvtt/alloc.c +++ b/media/webvtt/alloc.c @@ -46,12 +46,14 @@ struct { static void *WEBVTT_CALLBACK default_alloc( void *unused, webvtt_uint nb ) { + (void)unused; return malloc( nb ); } static void WEBVTT_CALLBACK default_free( void *unused, void *ptr ) { + (void)unused; free( ptr ); } diff --git a/media/webvtt/cue.c b/media/webvtt/cue.c index ec37883858c2..03d356048e8e 100644 --- a/media/webvtt/cue.c +++ b/media/webvtt/cue.c @@ -54,6 +54,7 @@ webvtt_create_cue( webvtt_cue **pcue ) */ webvtt_ref( &cue->refs ); webvtt_init_string( &cue->id ); + webvtt_init_string( &cue->body ); cue->from = 0xFFFFFFFFFFFFFFFF; cue->until = 0xFFFFFFFFFFFFFFFF; cue->snap_to_lines = 1; @@ -83,6 +84,7 @@ webvtt_release_cue( webvtt_cue **pcue ) *pcue = 0; if( webvtt_deref( &cue->refs ) == 0 ) { webvtt_release_string( &cue->id ); + webvtt_release_string( &cue->body ); webvtt_release_node( &cue->node_head ); webvtt_free( cue ); } @@ -115,3 +117,9 @@ webvtt_validate_cue( webvtt_cue *cue ) error: return 0; } + +WEBVTT_INTERN webvtt_bool +cue_is_incomplete( const webvtt_cue *cue ) { + return !cue || ( cue->flags & CUE_HEADER_MASK ) == CUE_HAVE_ID; +} + diff --git a/media/webvtt/cue_internal.h b/media/webvtt/cue_internal.h index 7f634eba0468..142af0867371 100644 --- a/media/webvtt/cue_internal.h +++ b/media/webvtt/cue_internal.h @@ -47,9 +47,6 @@ enum { CUE_HEADER_MASK = CUE_HAVE_CUEPARAMS|CUE_HAVE_ID, }; -static webvtt_bool -cue_is_incomplete( const webvtt_cue *cue ) { - return !cue || ( cue->flags & CUE_HEADER_MASK ) == CUE_HAVE_ID; -} +WEBVTT_INTERN webvtt_bool cue_is_incomplete( const webvtt_cue *cue ); #endif diff --git a/media/webvtt/cuetext.c b/media/webvtt/cuetext.c index 705fb66f69b4..c1f11f24b710 100644 --- a/media/webvtt/cuetext.c +++ b/media/webvtt/cuetext.c @@ -29,11 +29,10 @@ #include #include "parser_internal.h" #include "cuetext_internal.h" +#include "node_internal.h" #include "cue_internal.h" #include "string_internal.h" -static void webvtt_skipwhite( webvtt_byte **position ); - #ifdef min # undef min #endif @@ -67,19 +66,6 @@ do \ goto dealloc; \ } \ -/** - * This will only work on null-terminated strings, remember that! - */ -static void -webvtt_skipwhite( webvtt_byte **position ) -{ - webvtt_byte *p = *position; - while( *p && webvtt_iswhite(*p) ) { - ++p; - } - *position = p; -} - WEBVTT_INTERN webvtt_status webvtt_create_token( webvtt_cuetext_token **token, webvtt_token_type token_type ) { @@ -175,19 +161,15 @@ webvtt_delete_token( webvtt_cuetext_token **token ) * Note that time stamp tokens do not need to free any internal data because * they do not allocate anything. */ - switch( t->token_type ) { - case START_TOKEN: - data = t->start_token_data; - webvtt_release_stringlist( &data.css_classes ); - webvtt_release_string( &data.annotations ); - webvtt_release_string( &t->tag_name ); - break; - case END_TOKEN: - webvtt_release_string( &t->tag_name ); - break; - case TEXT_TOKEN: - webvtt_release_string( &t->text ); - break; + if( t->token_type == START_TOKEN ) { + data = t->start_token_data; + webvtt_release_stringlist( &data.css_classes ); + webvtt_release_string( &data.annotations ); + webvtt_release_string( &t->tag_name ); + } else if( t->token_type == END_TOKEN ) { + webvtt_release_string( &t->tag_name ); + } else if( t->token_type == TEXT_TOKEN ) { + webvtt_release_string( &t->text ); } webvtt_free( t ); *token = 0; @@ -196,7 +178,7 @@ webvtt_delete_token( webvtt_cuetext_token **token ) WEBVTT_INTERN int tag_accepts_annotation( webvtt_string *tag_name ) { - return webvtt_string_is_equal( tag_name, "v", 1 ); + return webvtt_string_is_equal( tag_name, ( webvtt_byte * )"v", 1 ); } WEBVTT_INTERN webvtt_status @@ -208,25 +190,25 @@ webvtt_node_kind_from_tag_name( webvtt_string *tag_name, webvtt_node_kind *kind if( webvtt_string_length(tag_name) == 1 ) { switch( webvtt_string_text(tag_name)[0] ) { - case( UTF8_B ): + case( 'b' ): *kind = WEBVTT_BOLD; break; - case( UTF8_I ): + case( 'i' ): *kind = WEBVTT_ITALIC; break; - case( UTF8_U ): + case( 'u' ): *kind = WEBVTT_UNDERLINE; break; - case( UTF8_C ): + case( 'c' ): *kind = WEBVTT_CLASS; break; - case( UTF8_V ): + case( 'v' ): *kind = WEBVTT_VOICE; break; } - } else if( webvtt_string_is_equal( tag_name, "ruby", 4 ) ) { + } else if( webvtt_string_is_equal( tag_name, ( webvtt_byte * )"ruby", 4 ) ) { *kind = WEBVTT_RUBY; - } else if( webvtt_string_is_equal( tag_name, "rt", 2 ) ) { + } else if( webvtt_string_is_equal( tag_name, ( webvtt_byte * )"rt", 2 ) ) { *kind = WEBVTT_RUBY_TEXT; } else { return WEBVTT_INVALID_TAG_NAME; @@ -279,17 +261,17 @@ webvtt_data_state( webvtt_byte **position, webvtt_token_state *token_state, { for ( ; *token_state == DATA; (*position)++ ) { switch( **position ) { - case UTF8_AMPERSAND: + case '&': *token_state = ESCAPE; break; - case UTF8_LESS_THAN: + case '<': if( webvtt_string_length(result) == 0 ) { *token_state = TAG; } else { return WEBVTT_SUCCESS; } break; - case UTF8_NULL_BYTE: + case '\0': return WEBVTT_SUCCESS; break; default: @@ -328,14 +310,14 @@ webvtt_escape_state( webvtt_byte **position, webvtt_token_state *token_state, * Append ampersand here because the algorithm is not able to add it to the * buffer when it reads it in the DATA state tokenizer. */ - CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( &buffer, UTF8_AMPERSAND ) ); + CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( &buffer, '&' ) ); for( ; *token_state == ESCAPE; (*position)++ ) { /** * We have encountered a token termination point. * Append buffer to result and return success. */ - if( **position == UTF8_NULL_BYTE || **position == UTF8_LESS_THAN ) { + if( **position == '\0' || **position == '<' ) { CHECK_MEMORY_OP_JUMP( status, webvtt_string_append_string( result, &buffer ) ); goto dealloc; } @@ -344,7 +326,7 @@ webvtt_escape_state( webvtt_byte **position, webvtt_token_state *token_state, * This means that we need to add that malformed text to the result and * recreate the buffer to prepare for a new escape sequence. */ - else if( **position == UTF8_AMPERSAND ) { + else if( **position == '&' ) { CHECK_MEMORY_OP_JUMP( status, webvtt_string_append_string( result, &buffer ) ); webvtt_release_string( &buffer ); CHECK_MEMORY_OP_JUMP( status, webvtt_create_string( 1, &buffer ) ); @@ -355,18 +337,18 @@ webvtt_escape_state( webvtt_byte **position, webvtt_token_state *token_state, * Check if buffer contains a valid escape sequence and if it does append * the interpretation to result and change the state to DATA. */ - else if( **position == UTF8_SEMI_COLON ) { - if( webvtt_string_is_equal( &buffer, "&", 4 ) ) { + else if( **position == ';' ) { + if( webvtt_string_is_equal( &buffer, ( webvtt_byte * )"&", 4 ) ) { CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( result, '&' ) ); - } else if( webvtt_string_is_equal( &buffer, "<", 3 ) ) { + } else if( webvtt_string_is_equal( &buffer, ( webvtt_byte * )"<", 3 ) ) { CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( result, '<' ) ); - } else if( webvtt_string_is_equal( &buffer, ">", 3 ) ) { + } else if( webvtt_string_is_equal( &buffer, ( webvtt_byte * )">", 3 ) ) { CHECK_MEMORY_OP_JUMP( status, webvtt_string_putc( result, '>' ) ); - } else if( webvtt_string_is_equal( &buffer, "&rlm", 4 ) ) { + } else if( webvtt_string_is_equal( &buffer, ( webvtt_byte * )"&rlm", 4 ) ) { CHECK_MEMORY_OP_JUMP( status, webvtt_string_append( result, rlm_replace, RLM_REPLACE_LENGTH ) ); - } else if( webvtt_string_is_equal( &buffer, "&lrm", 4 ) ) { + } else if( webvtt_string_is_equal( &buffer, ( webvtt_byte * )"&lrm", 4 ) ) { CHECK_MEMORY_OP_JUMP( status, webvtt_string_append( result, lrm_replace, LRM_REPLACE_LENGTH ) ); - } else if( webvtt_string_is_equal( &buffer, " ", 5 ) ) { + } else if( webvtt_string_is_equal( &buffer, ( webvtt_byte * )" ", 5 ) ) { CHECK_MEMORY_OP_JUMP( status, webvtt_string_append( result, nbsp_replace, NBSP_REPLACE_LENGTH ) ); } else { CHECK_MEMORY_OP_JUMP( status, webvtt_string_append_string( result, &buffer ) ); @@ -407,25 +389,25 @@ webvtt_tag_state( webvtt_byte **position, webvtt_token_state *token_state, webvtt_string *result ) { for( ; *token_state == TAG; (*position)++ ) { - if( **position == UTF8_TAB || **position == UTF8_LINE_FEED || - **position == UTF8_CARRIAGE_RETURN || **position == UTF8_FORM_FEED || - **position == UTF8_SPACE ) { + if( **position == '\t' || **position == '\n' || + **position == '\r' || **position == '\f' || + **position == ' ' ) { *token_state = START_TAG_ANNOTATION; } else if( webvtt_isdigit( **position ) ) { CHECK_MEMORY_OP( webvtt_string_putc( result, **position ) ); *token_state = TIME_STAMP_TAG; } else { switch( **position ) { - case UTF8_FULL_STOP: + case '.': *token_state = START_TAG_CLASS; break; - case UTF8_SOLIDUS: + case '/': *token_state = END_TAG; break; - case UTF8_GREATER_THAN: + case '>': return WEBVTT_SUCCESS; break; - case UTF8_NULL_BYTE: + case '\0': return WEBVTT_SUCCESS; break; default: @@ -443,19 +425,19 @@ webvtt_start_tag_state( webvtt_byte **position, webvtt_token_state *token_state, webvtt_string *result ) { for( ; *token_state == START_TAG; (*position)++ ) { - if( **position == UTF8_TAB || **position == UTF8_FORM_FEED || - **position == UTF8_SPACE || **position == UTF8_LINE_FEED || - **position == UTF8_CARRIAGE_RETURN ) { + if( **position == '\t' || **position == '\f' || + **position == ' ' || **position == '\n' || + **position == '\r' ) { *token_state = START_TAG_ANNOTATION; } else { switch( **position ) { - case UTF8_TAB: + case '\t': *token_state = START_TAG_ANNOTATION; break; - case UTF8_FULL_STOP: + case '.': *token_state = START_TAG_CLASS; break; - case UTF8_GREATER_THAN: + case '>': return WEBVTT_SUCCESS; break; default: @@ -478,17 +460,17 @@ webvtt_class_state( webvtt_byte **position, webvtt_token_state *token_state, CHECK_MEMORY_OP( webvtt_create_string( 1, &buffer ) ); for( ; *token_state == START_TAG_CLASS; (*position)++ ) { - if( **position == UTF8_TAB || **position == UTF8_FORM_FEED || - **position == UTF8_SPACE || **position == UTF8_LINE_FEED || - **position == UTF8_CARRIAGE_RETURN) { + if( **position == '\t' || **position == '\f' || + **position == ' ' || **position == '\n' || + **position == '\r') { CHECK_MEMORY_OP_JUMP( status, webvtt_stringlist_push( css_classes, &buffer ) ); *token_state = START_TAG_ANNOTATION; return WEBVTT_SUCCESS; - } else if( **position == UTF8_GREATER_THAN || **position == UTF8_NULL_BYTE ) { + } else if( **position == '>' || **position == '\0' ) { CHECK_MEMORY_OP_JUMP( status, webvtt_stringlist_push( css_classes, &buffer ) ); webvtt_release_string( &buffer ); return WEBVTT_SUCCESS; - } else if( **position == UTF8_FULL_STOP ) { + } else if( **position == '.' ) { CHECK_MEMORY_OP_JUMP( status, webvtt_stringlist_push( css_classes, &buffer ) ); webvtt_release_string( &buffer ); CHECK_MEMORY_OP( webvtt_create_string( 1, &buffer ) ); @@ -508,7 +490,7 @@ webvtt_annotation_state( webvtt_byte **position, webvtt_token_state *token_state webvtt_string *annotation ) { for( ; *token_state == START_TAG_ANNOTATION; (*position)++ ) { - if( **position == UTF8_NULL_BYTE || **position == UTF8_GREATER_THAN ) { + if( **position == '\0' || **position == '>' ) { return WEBVTT_SUCCESS; } CHECK_MEMORY_OP( webvtt_string_putc( annotation, **position ) ); @@ -522,7 +504,7 @@ webvtt_end_tag_state( webvtt_byte **position, webvtt_token_state *token_state, webvtt_string *result ) { for( ; *token_state == END_TAG; (*position)++ ) { - if( **position == UTF8_GREATER_THAN || **position == UTF8_NULL_BYTE ) { + if( **position == '>' || **position == '\0' ) { return WEBVTT_SUCCESS; } CHECK_MEMORY_OP( webvtt_string_putc( result, **position ) ); @@ -536,7 +518,7 @@ webvtt_timestamp_state( webvtt_byte **position, webvtt_token_state *token_state, webvtt_string *result ) { for( ; *token_state == TIME_STAMP_TAG; (*position)++ ) { - if( **position == UTF8_GREATER_THAN || **position == UTF8_NULL_BYTE ) { + if( **position == '>' || **position == '\0' ) { return WEBVTT_SUCCESS; } CHECK_MEMORY_OP( webvtt_string_putc( result, **position ) ); @@ -599,13 +581,9 @@ webvtt_cuetext_tokenizer( webvtt_byte **position, webvtt_cuetext_token **token ) status = webvtt_timestamp_state( position, &token_state, &result ); break; } - - if( token_state == START_TAG_ANNOTATION ) { - webvtt_skipwhite( position ); - } } - if( **position == UTF8_GREATER_THAN ) + if( **position == '>' ) { (*position)++; } if( status == WEBVTT_SUCCESS ) { @@ -661,6 +639,16 @@ webvtt_parse_cuetext( webvtt_parser self, webvtt_cue *cue, webvtt_string *payloa webvtt_cuetext_token *token; webvtt_node_kind kind; + /** + * TODO: Use these parameters! 'finished' isn't really important + * here, but 'self' certainly is as it lets us report syntax errors. + * + * However, for the time being we can trick the compiler into not + * warning us about unused variables by doing this. + */ + ( void )self; + ( void )finished; + if( !cue ) { return WEBVTT_INVALID_PARAM; } @@ -685,74 +673,68 @@ webvtt_parse_cuetext( webvtt_parser self, webvtt_cue *cue, webvtt_string *payloa * Routine taken from the W3C specification * http://dev.w3.org/html5/webvtt/#webvtt-cue-text-parsing-rules */ - while( *position != UTF8_NULL_BYTE ) { - + while( *position != '\0' ) { + webvtt_status status = WEBVTT_SUCCESS; webvtt_delete_token( &token ); /* Step 7. */ - switch( webvtt_cuetext_tokenizer( &position, &token ) ) { - case( WEBVTT_UNFINISHED ): - /* Error here. */ - break; - /* Step 8. */ - case( WEBVTT_SUCCESS ): - + if( WEBVTT_FAILED( status = webvtt_cuetext_tokenizer( &position, + &token ) ) ) { + /* Error here. */ + } else { + /* Succeeded... Process token */ + if( token->token_type == END_TOKEN ) { /** * If we've found an end token which has a valid end token tag name and * a tag name that is equal to the current node then set current to the * parent of current. */ - if( token->token_type == END_TOKEN ) { + if( current_node->kind == WEBVTT_HEAD_NODE ) { /** * We have encountered an end token but we are at the top of the list * and thus have not encountered any start tokens yet, throw away the * token. */ - if( current_node->kind == WEBVTT_HEAD_NODE ) { - continue; - } + continue; + } + if( webvtt_node_kind_from_tag_name( &token->tag_name, &kind ) == WEBVTT_INVALID_TAG_NAME ) { /** * We have encountered an end token but it is not in a format that is * supported, throw away the token. */ - if( webvtt_node_kind_from_tag_name( &token->tag_name, &kind ) == WEBVTT_INVALID_TAG_NAME ) { - continue; - } + continue; + } + if( current_node->kind == kind ) { /** * We have encountered an end token and it matches the start token of * the node that we are currently on. Move back up the list of nodes * and continue parsing. */ - if( current_node->kind == kind ) { - current_node = current_node->parent; - } - } else { - - /** - * Attempt to create a valid node from the token. - * If successful then attach the node to the current nodes list and - * also set current to the newly created node if it is an internal - * node type. - */ - if( webvtt_create_node_from_token( token, &temp_node, current_node ) != WEBVTT_SUCCESS ) { - /* Do something here? */ - } - else { - webvtt_attach_node( current_node, temp_node ); - - if( WEBVTT_IS_VALID_INTERNAL_NODE( temp_node->kind ) ) { - current_node = temp_node; - } - - /* Release the node as attach internal node increases the count. */ - webvtt_release_node( &temp_node ); - } + current_node = current_node->parent; } - break; + } else { + /** + * Attempt to create a valid node from the token. + * If successful then attach the node to the current nodes list and + * also set current to the newly created node if it is an internal + * node type. + */ + if( webvtt_create_node_from_token( token, &temp_node, current_node ) != WEBVTT_SUCCESS ) { + /* Do something here? */ + } else { + webvtt_attach_node( current_node, temp_node ); + + if( WEBVTT_IS_VALID_INTERNAL_NODE( temp_node->kind ) ) { + current_node = temp_node; + } + + /* Release the node as attach internal node increases the count. */ + webvtt_release_node( &temp_node ); + } + } } - webvtt_skipwhite( &position ); } webvtt_delete_token( &token ); diff --git a/media/webvtt/include/webvtt/cue.h b/media/webvtt/include/webvtt/cue.h index edcfbbd7fdc1..bd7dae3be3b4 100644 --- a/media/webvtt/include/webvtt/cue.h +++ b/media/webvtt/include/webvtt/cue.h @@ -81,6 +81,7 @@ webvtt_cue_t { webvtt_cue_settings settings; webvtt_bool snap_to_lines; webvtt_string id; + webvtt_string body; /** * Parsed cue-text (NULL if has not been parsed) diff --git a/media/webvtt/include/webvtt/node.h b/media/webvtt/include/webvtt/node.h index 250e8f26eee7..70bc9431ba78 100644 --- a/media/webvtt/include/webvtt/node.h +++ b/media/webvtt/include/webvtt/node.h @@ -71,11 +71,20 @@ webvtt_node_kind_t { WEBVTT_EMPTY_NODE = 258 } webvtt_node_kind; -#define WEBVTT_IS_LEAF(Kind) ( ((Kind) & WEBVTT_NODE_LEAF) != 0 ) -#define WEBVTT_NODE_INDEX(Kind) ( (Kind) & ~WEBVTT_NODE_LEAF ) -#define WEBVTT_IS_VALID_LEAF_NODE(Kind) ( WEBVTT_IS_LEAF(Kind) && (WEBVTT_NODE_INDEX(Kind) >= WEBVTT_NODE_LEAF_START && WEBVTT_NODE_INDEX(Kind) <= WEBVTT_NODE_LEAF_END ) ) -#define WEBVTT_IS_VALID_INTERNAL_NODE(Kind) ( (!WEBVTT_IS_LEAF(Kind)) && (WEBVTT_NODE_INDEX(Kind) >= WEBVTT_NODE_INTERNAL_START && WEBVTT_NODE_INDEX(Kind) <= WEBVTT_NODE_INTERNAL_END) ) -#define WEBVTT_IS_VALID_NODE_KIND(Kind) ( WEBVTT_IS_VALID_INTERNAL_NODE(Kind) || WEBVTT_IS_VALID_LEAF_NODE(Kind) ) +#define WEBVTT_IS_LEAF( Kind ) ( ( ( Kind ) & WEBVTT_NODE_LEAF) != 0 ) +#define WEBVTT_NODE_INDEX( Kind ) ( ( Kind ) & ~WEBVTT_NODE_LEAF ) + +#define WEBVTT_IS_VALID_LEAF_NODE( Kind ) \ + ( WEBVTT_IS_LEAF( Kind ) && \ + ( WEBVTT_NODE_INDEX( Kind ) >= WEBVTT_NODE_LEAF_START && \ + WEBVTT_NODE_INDEX( Kind ) <= WEBVTT_NODE_LEAF_END ) ) + +#define WEBVTT_IS_VALID_INTERNAL_NODE( Kind ) \ + ( ( !WEBVTT_IS_LEAF( Kind ) ) && \ + ( WEBVTT_NODE_INDEX( Kind ) <= WEBVTT_NODE_INTERNAL_END ) ) + +#define WEBVTT_IS_VALID_NODE_KIND( Kind ) \ + ( WEBVTT_IS_VALID_INTERNAL_NODE( Kind ) || WEBVTT_IS_VALID_LEAF_NODE( Kind ) ) struct webvtt_internal_node_data_t; diff --git a/media/webvtt/include/webvtt/string.h b/media/webvtt/include/webvtt/string.h index 96e1f2f6fb2e..560ad6dd47b9 100644 --- a/media/webvtt/include/webvtt/string.h +++ b/media/webvtt/include/webvtt/string.h @@ -126,14 +126,14 @@ WEBVTT_EXPORT const webvtt_byte *webvtt_string_text( const webvtt_string *str ); * * return the length of a strings text */ -WEBVTT_EXPORT const webvtt_uint32 webvtt_string_length( const webvtt_string *str ); +WEBVTT_EXPORT webvtt_uint32 webvtt_string_length( const webvtt_string *str ); /** * webvtt_string_capacity * * return the current capacity of a string */ -WEBVTT_EXPORT const webvtt_uint32 webvtt_string_capacity( const webvtt_string *str ); +WEBVTT_EXPORT webvtt_uint32 webvtt_string_capacity( const webvtt_string *str ); /** * webvtt_string_getline @@ -142,7 +142,7 @@ WEBVTT_EXPORT const webvtt_uint32 webvtt_string_capacity( const webvtt_string *s * including the terminating character(s) */ WEBVTT_EXPORT int webvtt_string_getline( webvtt_string *str, const webvtt_byte *buffer, - webvtt_uint *pos, webvtt_uint len, int *truncate, webvtt_bool finish, webvtt_bool retain_new_line ); + webvtt_uint *pos, int len, int *truncate, webvtt_bool finish ); /** * webvtt_string_putc @@ -158,7 +158,8 @@ WEBVTT_EXPORT webvtt_status webvtt_string_putc( webvtt_string *str, webvtt_byte * compare a string's text to a byte array * */ -WEBVTT_EXPORT webvtt_bool webvtt_string_is_equal( webvtt_string *str, webvtt_byte *to_compare, webvtt_uint len ); +WEBVTT_EXPORT webvtt_bool webvtt_string_is_equal( const webvtt_string *str, + const webvtt_byte *to_compare, int len ); /** * webvtt_string_append diff --git a/media/webvtt/lexer.c b/media/webvtt/lexer.c index 5105686cd47d..e48d1eefcefd 100644 --- a/media/webvtt/lexer.c +++ b/media/webvtt/lexer.c @@ -125,7 +125,7 @@ #define OR #define AND -#define OVERFLOW(X) \ +#define IF_OVERFLOW(X) \ if( self->token_pos >= (sizeof(self->token) - 1 ) ) \ { \ RETURN(X) \ @@ -156,7 +156,6 @@ webvtt_lex_word( webvtt_parser self, webvtt_string *str, const webvtt_byte *buff { webvtt_status status = WEBVTT_SUCCESS; webvtt_uint pos = *ppos; - int d = 0; if( !str ) { return WEBVTT_INVALID_PARAM; } @@ -193,6 +192,83 @@ _finished: return status; } +/** + * webvtt_lex_newline + * + * Get newline sequence in re-entrant fashion. self->tstate must be + * L_START or L_NEWLINE0 for this function to behave correctly. + */ +WEBVTT_INTERN webvtt_token +webvtt_lex_newline( webvtt_parser self, const + webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint length, + webvtt_bool finish ) +{ + webvtt_uint p = *pos; + + /* Ensure that we've got a valid token-state for this use-case. */ + DIE_IF( self->tstate != L_START && self->tstate != L_NEWLINE0 ); + + while( p < length ) { + webvtt_byte c = buffer[ p++ ]; + self->token[ self->token_pos++ ] = c; + self->token[ self->token_pos ] = 0; + self->bytes++; + + switch( self->tstate ) { + case L_START: + if( c == '\n' ) { + *pos = p; + return NEWLINE; + } else if( c == '\r' ) { + self->tstate = L_NEWLINE0; + } else { + goto backup; + } + break; + case L_NEWLINE0: + if( c == '\n' ) { + *pos = p; + self->tstate = L_START; + return NEWLINE; + } else { + goto backup; + } + break; + + default: + /** + * This should never happen if the function is called correctly + * (EG immediately following a successful call to webvtt_string_getline) + */ + goto backup; + } + } + + *pos = p; + if( finish && ( p >= length ) ) { + /* If pos >= length, it's and 'finish' is set, it's an automatic EOL */ + self->tstate = L_START; + return NEWLINE; + } + + if( self->tstate == L_NEWLINE0 ) { + return UNFINISHED; + } else { + /* This branch should never occur, if the function is used properly. */ + *pos = --p; + return BADTOKEN; + } +backup: + self->token[ --self->token_pos ] = 0; + --self->bytes; + *pos = --p; + if( self->tstate == L_NEWLINE0 ) { + self->tstate = L_START; + return NEWLINE; + } + return BADTOKEN; +} + WEBVTT_INTERN webvtt_token webvtt_lex( webvtt_parser self, const webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint length, webvtt_bool finish ) { @@ -269,10 +345,17 @@ webvtt_lex( webvtt_parser self, const webvtt_byte *buffer, webvtt_uint *pos, web BEGIN_STATE(L_DIGIT0) U_DIGIT { - OVERFLOW(INTEGER) + IF_OVERFLOW(INTEGER) SET_STATE(L_DIGIT0) } - U_COLON { SET_STATE(L_TIMESTAMP1) } + U_COLON { + /* Don't return a TIMESTAMP if we start with '-' */ + if( self->token[0] == '-' ) { + RETURN(INTEGER); + } else { + SET_STATE(L_TIMESTAMP1) + } + } U_PERCENT { RETURN(PERCENTAGE) } DEFAULT { BACKUP AND RETURN(INTEGER) } END_STATE_EX @@ -283,7 +366,7 @@ webvtt_lex( webvtt_parser self, const webvtt_byte *buffer, webvtt_uint *pos, web END_STATE_EX BEGIN_STATE(L_WHITESPACE) - U_SPACE OR U_TAB { OVERFLOW(WHITESPACE) SET_STATE(L_WHITESPACE) } + U_SPACE OR U_TAB { IF_OVERFLOW(WHITESPACE) SET_STATE(L_WHITESPACE) } DEFAULT { BACKUP RETURN(WHITESPACE) } END_STATE_EX @@ -453,37 +536,42 @@ webvtt_lex( webvtt_parser self, const webvtt_byte *buffer, webvtt_uint *pos, web BEGIN_STATE(L_TIMESTAMP1) U_DIGIT { - OVERFLOW(BADTOKEN) + IF_OVERFLOW(BADTOKEN) SET_STATE(L_TIMESTAMP1) } U_COLON { - OVERFLOW(BADTOKEN) + IF_OVERFLOW(BADTOKEN) SET_STATE(L_TIMESTAMP2) } U_PERIOD { - OVERFLOW(BADTOKEN) + IF_OVERFLOW(BADTOKEN) SET_STATE(L_TIMESTAMP3) } END_STATE BEGIN_STATE(L_TIMESTAMP2) U_DIGIT { - OVERFLOW(BADTOKEN) + IF_OVERFLOW(BADTOKEN) SET_STATE(L_TIMESTAMP2) } U_PERIOD { - OVERFLOW(BADTOKEN) + IF_OVERFLOW(BADTOKEN) SET_STATE(L_TIMESTAMP3) } END_STATE BEGIN_STATE(L_TIMESTAMP3) U_DIGIT { - OVERFLOW(TIMESTAMP) + IF_OVERFLOW(TIMESTAMP) BREAK } DEFAULT { BACKUP + /* Don't return a TIMESTAMP if we don't have at least one + millisecond */ + if( !webvtt_isdigit( self->token[ self->token_pos - 1 ] ) ) { + RETURN(BADTOKEN); + } RETURN(TIMESTAMP) BREAK } diff --git a/media/webvtt/node.c b/media/webvtt/node.c index bae4d263fb92..659a58642b15 100644 --- a/media/webvtt/node.c +++ b/media/webvtt/node.c @@ -32,7 +32,8 @@ static webvtt_node empty_node = { { 1 }, /* init ref count */ 0, /* parent */ - WEBVTT_EMPTY_NODE /* node kind */ + WEBVTT_EMPTY_NODE, /* node kind */ + { { 0 } } /* value */ }; WEBVTT_EXPORT void diff --git a/media/webvtt/parser.c b/media/webvtt/parser.c index 17282f7b0d28..5af6e5e65ac6 100644 --- a/media/webvtt/parser.c +++ b/media/webvtt/parser.c @@ -33,7 +33,7 @@ #define _ERROR(X) do { if( skip_error == 0 ) { ERROR(X); } } while(0) static const webvtt_byte separator[] = { - UTF8_HYPHEN_MINUS, UTF8_HYPHEN_MINUS, UTF8_GREATER_THAN + '-', '-', '>' }; #define MSECS_PER_HOUR (3600000) @@ -43,7 +43,6 @@ static const webvtt_byte separator[] = { #define MALFORMED_TIME ((webvtt_timestamp_t)-1.0) static webvtt_status find_bytes( const webvtt_byte *buffer, webvtt_uint len, const webvtt_byte *sbytes, webvtt_uint slen ); -static webvtt_status webvtt_skipwhite( const webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint len ); static webvtt_int64 parse_int( const webvtt_byte **pb, int *pdigits ); static void skip_spacetab( const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len, webvtt_uint *column ); @@ -129,6 +128,8 @@ cleanup_stack( webvtt_parser self ) * Eventually the cuetext parser will probably be making use * of this stack, and will need to manage it well also. */ + default: + break; } st->type = V_NONE; st->line = st->column = st->token = 0; @@ -155,34 +156,63 @@ cleanup_stack( webvtt_parser self ) /** * */ -WEBVTT_EXPORT webvtt_status -webvtt_finish_parsing( webvtt_parser self ) +WEBVTT_EXPORT webvtt_status +webvtt_finish_parsing( webvtt_parser self ) { webvtt_status status = WEBVTT_SUCCESS; - + const webvtt_byte buffer[] = "\0"; + const webvtt_uint len = 0; + webvtt_uint pos = 0; + if( !self->finished ) { self->finished = 1; - + +retry: switch( self->mode ) { /** * We've left off parsing cue settings and are not in the empty state, * return WEBVTT_CUE_INCOMPLETE. */ case M_WEBVTT: - if( self->top->type != V_NONE ) { - ERROR( WEBVTT_CUE_INCOMPLETE ); + if( self->top->state == T_CUEREAD ) { + SAFE_ASSERT( self->top != self->stack ); + --self->top; + self->popped = 1; + } + + if( self->top->state == T_CUE ) { + webvtt_string text; + webvtt_cue *cue; + if( self->top->type == V_NONE ) { + webvtt_create_cue( &self->top->v.cue ); + self->top->type = V_CUE; + } + cue = self->top->v.cue; + SAFE_ASSERT( self->popped && (self->top+1)->state == T_CUEREAD ); + SAFE_ASSERT( cue != 0 ); + text.d = (self->top+1)->v.text.d; + (self->top+1)->v.text.d = 0; + (self->top+1)->type = V_NONE; + (self->top+1)->state = 0; + self->column = 1; + status = webvtt_proc_cueline( self, cue, &text ); + if( cue_is_incomplete( cue ) ) { + ERROR( WEBVTT_CUE_INCOMPLETE ); + } + ++self->line; + self->column = 1; + if( self->mode == M_CUETEXT ) { + goto retry; + } } break; - /** + /** * We've left off on trying to read in a cue text. - * Parse the partial cue text read and pass the cue back to the + * Parse the partial cue text read and pass the cue back to the * application if possible. */ case M_CUETEXT: - status = webvtt_parse_cuetext( self, self->top->v.cue, - &self->line_buffer, self->finished ); - webvtt_release_string( &self->line_buffer ); - finish_cue( self, &self->top->v.cue ); + status = webvtt_proc_cuetext( self, buffer, &pos, len, self->finished ); break; case M_SKIP_CUE: /* Nothing to do here. */ @@ -193,7 +223,7 @@ webvtt_finish_parsing( webvtt_parser self ) } cleanup_stack( self ); } - + return status; } @@ -224,7 +254,7 @@ static int find_newline( const webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint len ) { while( *pos < len ) { - if( buffer[ *pos ] == UTF8_CARRIAGE_RETURN || buffer[ *pos ] == UTF8_LINE_FEED ) { + if( buffer[ *pos ] == '\r' || buffer[ *pos ] == '\n' ) { return 1; } else { ( *pos )++; @@ -243,7 +273,7 @@ skip_spacetab( const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len, } while( *pos < len ) { webvtt_byte ch = text[ *pos ]; - if( ch == 0x20 || ch == 0x09 ) { + if( ch == ' ' || ch == '\t' ) { ++( *pos ); ++( *column ); } else { @@ -262,7 +292,7 @@ skip_until_white( const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len, } while( *pos < len ) { webvtt_byte ch = text[ *pos ]; - if( ch == 0x20 || ch == 0x09 || ch == 0x0A || ch == 0x0D ) { + if( ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' ) { break; } else { int length = webvtt_utf8_length( text + *pos ); @@ -272,33 +302,6 @@ skip_until_white( const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len, } } -static webvtt_status -webvtt_skipwhite( const webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint len ) -{ - if( !buffer || !pos ) { - return WEBVTT_INVALID_PARAM; - } - - for( ; *pos < len && webvtt_iswhite( buffer[ *pos ] ); (*pos)++ ); - - return WEBVTT_SUCCESS; -} - -static void -find_next_whitespace( const webvtt_byte *buffer, webvtt_uint *ppos, webvtt_uint len ) -{ - webvtt_uint pos = *ppos; - while( pos < len ) { - webvtt_byte c = buffer[pos]; - if( c == UTF8_CARRIAGE_RETURN || c == UTF8_LINE_FEED || c == UTF8_SPACE || c == UTF8_TAB ) { - break; - } - - ++pos; - } - *ppos = pos; -} - /** * basic strnstr-ish routine */ @@ -337,7 +340,7 @@ find_bytes( const webvtt_byte *buffer, webvtt_uint len, /** * More state stack helpers */ -static webvtt_status +WEBVTT_INTERN webvtt_status do_push( webvtt_parser self, webvtt_uint token, webvtt_uint back, webvtt_uint state, void *data, webvtt_state_value_type type, webvtt_uint line, webvtt_uint column ) { if( STACK_SIZE + 1 >= self->stack_alloc ) { @@ -356,6 +359,7 @@ do_push( webvtt_parser self, webvtt_uint token, webvtt_uint back, webvtt_uint st } ++self->top; self->top->state = state; + self->top->flags = 0; self->top->type = type; self->top->token = ( webvtt_token )token; self->top->line = line; @@ -443,13 +447,13 @@ webvtt_parse_cuesetting( webvtt_parser self, const webvtt_byte *text, if( *pos < len ) { webvtt_uint column = last_column; webvtt_byte ch = text[ *pos ]; - if( ch != 0x3A ) { + if( ch != ':' ) { webvtt_error e = WEBVTT_INVALID_CUESETTING; - if( ch == 0x20 || ch == 0x09 ) { + if( ch == ' ' || ch == '\t' ) { column = self->column; e = WEBVTT_UNEXPECTED_WHITESPACE; skip_spacetab( text, pos, len, &self->column ); - if( text[ *pos ] == 0x3A ) { + if( text[ *pos ] == ':' ) { skip_until_white( text, pos, len, &self->column ); } } else { @@ -461,7 +465,7 @@ webvtt_parse_cuesetting( webvtt_parser self, const webvtt_byte *text, keyword_column = last_column; } } else { - ERROR_AT_COLUMN( WEBVTT_INVALID_CUESETTING, last_column ); + ERROR_AT_COLUMN( WEBVTT_INVALID_CUESETTING, last_column ); } break; case WHITESPACE: @@ -473,10 +477,8 @@ webvtt_parse_cuesetting( webvtt_parser self, const webvtt_byte *text, ERROR_AT( WEBVTT_INVALID_CUESETTING, last_line, last_column ); *pos = *pos + tp + 1; - skip_param: - while( *pos < len && text[ *pos ] != 0x20 - && text[ *pos ] != 0x09 ) { - if( text[ *pos ] == 0x0A || text[ *pos ] == 0x0D ) { + while( *pos < len && text[ *pos ] != ' ' && text[ *pos ] != '\t' ) { + if( text[ *pos ] == '\n' || text[ *pos ] == '\r' ) { return WEBVTT_SUCCESS; } ++( *pos ); @@ -518,8 +520,7 @@ get_value: *value_column = last_column; if( *pos < len ) { webvtt_byte ch = text[ *pos ]; - if( ch != 0x20 && ch != 0x09 - && ch != 0x0D && ch != 0x0A ) { + if( ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n' ) { goto bad_value; } } @@ -528,21 +529,23 @@ get_value: case PERCENTAGE: if( ( flags & TF_SIGN_MASK ) != TF_SIGN_MASK ) { const webvtt_byte p = self->token[ 0 ]; - if( ( ( flags & TF_NEGATIVE ) && p != UTF8_HYPHEN_MINUS ) - || ( ( flags & TF_POSITIVE ) && p == UTF8_HYPHEN_MINUS -) ) { + if( ( ( flags & TF_NEGATIVE ) && p != '-' ) + || ( ( flags & TF_POSITIVE ) && p == '-' ) ) { goto bad_value; } } + break; + + default: /* Currently, other types don't need these checks */ + break; } return i + 1; } else { bad_value: ERROR_AT( bv, last_line, last_column ); bad_value_eol: - while( *pos < len && text[ *pos ] != 0x20 - && text[ *pos ] != 0x09 ) { - if( text[ *pos ] == 0x0A || text[ *pos ] == 0x0D ) { + while( *pos < len && text[ *pos ] != ' ' && text[ *pos ] != '\t' ) { + if( text[ *pos ] == '\n' || text[ *pos ] == '\r' ) { return WEBVTT_SUCCESS; } ++( *pos ); @@ -563,36 +566,32 @@ bad_value_eol: } WEBVTT_INTERN webvtt_status -webvtt_parse_align( webvtt_parser self, webvtt_cue *cue, const -webvtt_byte *text, - webvtt_uint *pos, webvtt_uint len ) +webvtt_parse_align( webvtt_parser self, webvtt_cue *cue, + const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len ) { webvtt_uint last_line = self->line; webvtt_uint last_column = self->column; webvtt_status v; webvtt_uint vc; - webvtt_token values[] = { START, MIDDLE, END, LEFT, RIGHT, 0 }; + webvtt_token tokens[] = { START, MIDDLE, END, LEFT, RIGHT, 0 }; + webvtt_align_type values[] = { + WEBVTT_ALIGN_START, WEBVTT_ALIGN_MIDDLE, WEBVTT_ALIGN_END, + WEBVTT_ALIGN_LEFT, WEBVTT_ALIGN_RIGHT + }; if( ( v = webvtt_parse_cuesetting( self, text, pos, len, - WEBVTT_ALIGN_BAD_VALUE, ALIGN, values, &vc ) ) > 0 ) { + WEBVTT_ALIGN_BAD_VALUE, ALIGN, tokens, &vc ) ) > 0 ) { if( cue->flags & CUE_HAVE_ALIGN ) { ERROR_AT( WEBVTT_ALIGN_ALREADY_SET, last_line, last_column ); } cue->flags |= CUE_HAVE_ALIGN; - switch( values[ v - 1 ] ) { - case START: cue->settings.align = WEBVTT_ALIGN_START; break; - case MIDDLE: cue->settings.align = WEBVTT_ALIGN_MIDDLE; break; - case END: cue->settings.align = WEBVTT_ALIGN_END; break; - case LEFT: cue->settings.align = WEBVTT_ALIGN_LEFT; break; - case RIGHT: cue->settings.align = WEBVTT_ALIGN_RIGHT; break; - } + cue->settings.align = values[ v - 1 ]; } return v >= 0 ? WEBVTT_SUCCESS : v; } WEBVTT_INTERN webvtt_status -webvtt_parse_line( webvtt_parser self, webvtt_cue *cue, const -webvtt_byte *text, - webvtt_uint *pos, webvtt_uint len ) +webvtt_parse_line( webvtt_parser self, webvtt_cue *cue, const webvtt_byte *text, + webvtt_uint *pos, webvtt_uint len ) { webvtt_uint last_line = self->line; webvtt_uint last_column = self->column; @@ -602,7 +601,7 @@ webvtt_byte *text, webvtt_token values[] = { INTEGER, PERCENTAGE|TF_POSITIVE, 0 }; if( ( v = webvtt_parse_cuesetting( self, text, pos, len, WEBVTT_LINE_BAD_VALUE, LINE, values, &vc ) ) > 0 ) { - webvtt_uint digits; + int digits; webvtt_int64 value; const webvtt_byte *t = self->token; if( cue->flags & CUE_HAVE_LINE ) { @@ -635,180 +634,329 @@ webvtt_byte *text, return v >= 0 ? WEBVTT_SUCCESS : v; } +WEBVTT_INTERN webvtt_status +webvtt_parse_position( webvtt_parser self, webvtt_cue *cue, + const webvtt_byte *text, webvtt_uint *pos, + webvtt_uint len ) +{ + webvtt_uint last_line = self->line; + webvtt_uint last_column = self->column; + webvtt_status v; + webvtt_uint vc; + webvtt_bool first_flag = 0; + webvtt_token values[] = { PERCENTAGE|TF_POSITIVE, 0 }; + if( ( v = webvtt_parse_cuesetting( self, text, pos, len, + WEBVTT_POSITION_BAD_VALUE, POSITION, values, &vc ) ) > 0 ) { + int digits; + webvtt_int64 value; + const webvtt_byte *t = self->token; + if( cue->flags & CUE_HAVE_LINE ) { + ERROR_AT( WEBVTT_POSITION_ALREADY_SET, last_line, last_column ); + } else { + first_flag = 1; + } + cue->flags |= CUE_HAVE_POSITION; + value = parse_int( &t, &digits ); + if( value < 0 || value > 100 ) { + if( first_flag ) { + cue->flags &= ~CUE_HAVE_POSITION; + } + ERROR_AT_COLUMN( WEBVTT_POSITION_BAD_VALUE, vc ); + return WEBVTT_SUCCESS; + } + cue->settings.position = ( int )value; + } + return v >= 0 ? WEBVTT_SUCCESS : v; +} + +WEBVTT_INTERN webvtt_status +webvtt_parse_size( webvtt_parser self, webvtt_cue *cue, const webvtt_byte *text, + webvtt_uint *pos, webvtt_uint len ) +{ + webvtt_uint last_line = self->line; + webvtt_uint last_column = self->column; + webvtt_status v; + webvtt_uint vc; + webvtt_token tokens[] = { PERCENTAGE|TF_POSITIVE, 0 }; + if( ( v = webvtt_parse_cuesetting( self, text, pos, len, + WEBVTT_SIZE_BAD_VALUE, SIZE, tokens, &vc ) ) > 0 ) { + if( cue->flags & CUE_HAVE_SIZE ) { + ERROR_AT( WEBVTT_SIZE_ALREADY_SET, last_line, last_column ); + } + cue->flags |= CUE_HAVE_SIZE; + if( tokens[ v - 1 ] ) { + int digits; + const webvtt_byte *t = self->token; + webvtt_int64 value; + self->token_pos = 0; + value = parse_int( &t, &digits ); + if( value < 0 || value > 100 ) { + ERROR_AT_COLUMN( WEBVTT_SIZE_BAD_VALUE, vc ); + } else { + cue->settings.size = ( int )value; + } + } + } + return v >= 0 ? WEBVTT_SUCCESS : v; +} + +WEBVTT_INTERN webvtt_status +webvtt_parse_vertical( webvtt_parser self, webvtt_cue *cue, + const webvtt_byte *text, webvtt_uint *pos, + webvtt_uint len ) +{ + webvtt_uint last_line = self->line; + webvtt_uint last_column = self->column; + webvtt_status v; + webvtt_uint vc; + webvtt_token tokens[] = { RL, LR, 0 }; + webvtt_vertical_type values[] = { WEBVTT_VERTICAL_RL, WEBVTT_VERTICAL_LR }; + if( ( v = webvtt_parse_cuesetting( self, text, pos, len, + WEBVTT_VERTICAL_BAD_VALUE, VERTICAL, tokens, &vc ) ) > 0 ) { + if( cue->flags & CUE_HAVE_VERTICAL ) { + ERROR_AT( WEBVTT_VERTICAL_ALREADY_SET, last_line, last_column ); + } + cue->flags |= CUE_HAVE_VERTICAL; + cue->settings.vertical = values[ v - 1 ]; + } + return v >= 0 ? WEBVTT_SUCCESS : v; +} +/** + * Read a timestamp into 'result' field, following the rules of the cue-times + * section of the draft: + * http://dev.w3.org/html5/webvtt/#collect-webvtt-cue-timings-and-settings + * + * - Ignore whitespace + * - Return immediately after having parsed a timestamp + * - Return error if timestamp invalid. + */ +WEBVTT_INTERN webvtt_status +webvtt_get_timestamp( webvtt_parser self, webvtt_timestamp *result, + const webvtt_byte *text, webvtt_uint *pos, + webvtt_uint len, const char *accepted ) +{ + webvtt_uint last_column = self->column; + webvtt_uint last_line = self->line; + webvtt_token token; + while( *pos < len ) { + last_column = self->column; + last_line = self->line; + token = webvtt_lex( self, text, pos, len, 1 ); + self->token_pos = 0; + + if( token == TIMESTAMP ) { + if( *pos < len && text[ *pos ] != '\r' && text[ *pos ] != '\n' && + text[ *pos ] != ' ' && text[ *pos ] != '\t' ) { + if( accepted == 0 || !strchr( accepted, text[ *pos ] ) ) { + ERROR_AT( WEBVTT_EXPECTED_TIMESTAMP, last_line, last_column ); + return WEBVTT_PARSE_ERROR; + } + } + + if( !parse_timestamp( self->token, result ) ) { + /* Read a bad timestamp, throw away and abort cue */ + ERROR_AT( WEBVTT_MALFORMED_TIMESTAMP, last_line, last_column ); + if( BAD_TIMESTAMP( *result ) ) { + return WEBVTT_PARSE_ERROR; + } + } + return WEBVTT_SUCCESS; + } + else if( token == WHITESPACE ) { + /* Ignore whitespace */ + } else { + /* Not a timestamp, this is an error. */ + ERROR_AT( WEBVTT_EXPECTED_TIMESTAMP, last_line, last_column ); + return WEBVTT_PARSE_ERROR; + } + } + + /* If we finish entire line without reading a timestamp, + this is not a proper cue header and should be discarded. */ + ERROR_AT( WEBVTT_EXPECTED_TIMESTAMP, last_line, last_column ); + return WEBVTT_PARSE_ERROR; +} + +WEBVTT_INTERN webvtt_status +webvtt_proc_cueline( webvtt_parser self, webvtt_cue *cue, + webvtt_string *line ) +{ + const webvtt_byte *text; + webvtt_uint length; + DIE_IF( line == NULL ); + length = webvtt_string_length( line ); + text = webvtt_string_text( line ); + /* backup the column */ + self->column = 1; + if( find_bytes( text, length, separator, sizeof( separator ) ) + == WEBVTT_SUCCESS) { + /* It's not a cue id, we found '-->'. It can't be a second + cueparams line, because if we had it, we would be in + a different state. */ + int v; + self->cuetext_line = self->line + 1; + if( ( v = parse_cueparams( self, text, length, cue ) ) < 0 ) { + if( v == WEBVTT_PARSE_ERROR ) { + return WEBVTT_PARSE_ERROR; + } + self->mode = M_SKIP_CUE; + } else { + cue->flags |= CUE_HAVE_CUEPARAMS; + self->mode = M_CUETEXT; + } + } else { + /* It is a cue-id */ + if( cue && cue->flags & CUE_HAVE_ID ) { + /** + * This isn't actually a cue-id, because we already + * have one. It seems to be cuetext, which is occurring + * before cue-params + */ + webvtt_release_string( line ); + ERROR( WEBVTT_CUE_INCOMPLETE ); + self->mode = M_SKIP_CUE; + return WEBVTT_SUCCESS; + } else { + webvtt_uint last_column = self->column; + webvtt_uint last_line = self->line; + webvtt_token token = UNFINISHED; + self->column += length; + self->cuetext_line = self->line; + if( WEBVTT_FAILED( webvtt_string_append( &cue->id, text, + length ) ) ) { + webvtt_release_string( line ); + ERROR( WEBVTT_ALLOCATION_FAILED ); + return WEBVTT_OUT_OF_MEMORY; + } + cue->flags |= CUE_HAVE_ID; + + /* Read cue-params line */ + PUSH0( T_CUEREAD, 0, V_NONE ); + webvtt_init_string( &SP->v.text ); + SP->type = V_TEXT; + } + } + + webvtt_release_string( line ); + return WEBVTT_SUCCESS; +} + WEBVTT_INTERN int parse_cueparams( webvtt_parser self, const webvtt_byte *buffer, webvtt_uint len, webvtt_cue *cue ) { - int digits; - int have_ws = 0; - int unexpected_whitespace = 0; - webvtt_uint baddelim = 0; webvtt_uint pos = 0; - webvtt_token last_token = 0; - webvtt_uint last_line = self->line; enum cp_state { - CP_T1, CP_T2, CP_T3, CP_T4, CP_T5, /* 'start' cuetime, whitespace1, - 'separator', whitespace2, 'end' cuetime */ - CP_CS0, /* pre-cuesetting */ + CP_STARTTIME = 0, /* Start timestamp */ + CP_SEPARATOR, /* --> */ + CP_ENDTIME, /* End timestamp */ - CP_SD, /* cuesettings delimiter here */ - - CP_V1, /* 'vertical' cuesetting */ - CP_P1, /* 'position' cuesetting */ - CP_A1, /* 'align' cuesetting */ - CP_S1, /* 'size' cuesetting */ - CP_L1, /* 'line' cuesetting */ - - CP_SV, /* cuesettings value here */ - - CP_V2, - CP_P2, - CP_A2, - CP_S2, - CP_L2, + CP_SETTING, /* Cuesetting */ }; - enum cp_state last_state = CP_T1; - enum cp_state state = CP_T1; + enum cp_state state = CP_STARTTIME; -#define SETST(X) do { baddelim = 0; last_state = state; state = (X); } while( 0 ) +#define SETST(X) do { baddelim = 0; state = (X); } while( 0 ) self->token_pos = 0; while( pos < len ) { - webvtt_uint last_column = self->column; - webvtt_token token = webvtt_lex( self, buffer, &pos, len, 1 ); - webvtt_uint tlen = self->token_pos; - self->token_pos = 0; -_recheck: + webvtt_uint last_column; + webvtt_uint last_line; + webvtt_token token; + webvtt_uint token_len; + switch( state ) { - /* start timestamp */ - case CP_T1: - if( token == WHITESPACE && !unexpected_whitespace ) { - ERROR_AT_COLUMN( WEBVTT_UNEXPECTED_WHITESPACE, self->column ); - unexpected_whitespace = 1; - } else if( token == TIMESTAMP ) - if( !parse_timestamp( self->token, &cue->from ) ) { - ERROR_AT_COLUMN( - ( BAD_TIMESTAMP( cue->from ) - ? WEBVTT_EXPECTED_TIMESTAMP - : WEBVTT_MALFORMED_TIMESTAMP ), last_column ); - if( self->token_pos && !webvtt_isdigit( self->token[self->token_pos - 1] ) ) { - while( pos < len && buffer[pos] != 0x09 && buffer[pos] != 0x20 ) { ++pos; } - } - if( BAD_TIMESTAMP( cue->from ) ) - { return -1; } - SETST( CP_T2 ); - } else { - SETST( CP_T2 ); - } - else { - ERROR_AT_COLUMN( WEBVTT_EXPECTED_TIMESTAMP, last_column ); + /* start timestamp */ + case CP_STARTTIME: + if( WEBVTT_FAILED( webvtt_get_timestamp( self, &cue->from, buffer, &pos, + len, "-" ) ) ) { + /* abort cue */ return -1; } + + state = CP_SEPARATOR; break; - /* end timestamp */ - case CP_T5: - if( token == WHITESPACE ) { - /* no problem, just ignore it and continue */ - } else if( token == TIMESTAMP ) - if( !parse_timestamp( self->token, &cue->until ) ) { - ERROR_AT_COLUMN( - ( BAD_TIMESTAMP( cue->until ) - ? WEBVTT_EXPECTED_TIMESTAMP - : WEBVTT_MALFORMED_TIMESTAMP ), last_column ); - if( !webvtt_isdigit( self->token[self->token_pos - 1] ) ) { - while( pos < len && buffer[pos] != 0x09 && buffer[pos] != 0x20 ) { ++pos; } - } - if( BAD_TIMESTAMP( cue->until ) ) - { return -1; } - SETST( CP_CS0 ); - } else { - SETST( CP_CS0 ); - } - else { - ERROR_AT_COLUMN( WEBVTT_EXPECTED_TIMESTAMP, last_column ); + + /* '-->' separator */ + case CP_SEPARATOR: + last_column = self->column; + last_line = self->line; + token = webvtt_lex( self, buffer, &pos, len, 1 ); + self->token_pos = 0; + if( token == SEPARATOR ) { + /* Expect end timestamp */ + state = CP_ENDTIME; + } else if( token == WHITESPACE ) { + /* ignore whitespace */ + } else { + /* Unexpected token. Abort cue. */ + ERROR_AT( WEBVTT_EXPECTED_CUETIME_SEPARATOR, last_line, last_column ); return -1; } break; - /* whitespace 1 */ - case CP_T2: - switch( token ) { - case SEPARATOR: - ERROR_AT_COLUMN( WEBVTT_EXPECTED_WHITESPACE, last_column ); - SETST( CP_T4 ); - break; - case WHITESPACE: - SETST( CP_T3 ); - break; + /* end timestamp */ + case CP_ENDTIME: + if( WEBVTT_FAILED( webvtt_get_timestamp( self, &cue->until, buffer, + &pos, len, 0 ) ) ) { + /* abort cue */ + return -1; } + + /* Expect cuesetting */ + state = CP_SETTING; break; - case CP_T3: - switch( token ) { - case WHITESPACE: /* ignore this whitespace */ - break; - case SEPARATOR: - SETST( CP_T4 ); - break; - - case TIMESTAMP: - ERROR( WEBVTT_MISSING_CUETIME_SEPARATOR ); - SETST( CP_T5 ); - goto _recheck; - - default: /* some garbage */ - ERROR_AT_COLUMN( WEBVTT_EXPECTED_CUETIME_SEPARATOR, last_column ); - return -1; - } - break; - case CP_T4: - switch( token ) { - case WHITESPACE: - SETST( CP_T5 ); - break; - case TIMESTAMP: - ERROR_AT_COLUMN( WEBVTT_EXPECTED_WHITESPACE, last_column ); - goto _recheck; - default: - ERROR_AT_COLUMN( WEBVTT_EXPECTED_WHITESPACE, last_column ); - goto _recheck; - } - break; -#define CHKDELIM \ -if( baddelim ) \ - ERROR_AT_COLUMN(WEBVTT_INVALID_CUESETTING_DELIMITER,baddelim); \ -else if( !have_ws ) \ - ERROR_AT_COLUMN(WEBVTT_EXPECTED_WHITESPACE,last_column); + default: /** - * This section is "pre-cuesetting". We are expecting whitespace, - * followed by a cuesetting keyword + * Most of these branches need to read a token for the time + * being, so they're grouped together here. * - * If we don't see a keyword, but have our whitespace, it is considered - * a bad keyword (invalid cuesetting) - * - * Otherwise, if we don't have whitespace and have a bad token, it's an - * invalid delimiter + * TODO: Remove need to call lexer at all here. */ - case CP_CS0: + last_column = self->column; + last_line = self->line; + token = webvtt_lex( self, buffer, &pos, len, 1 ); + token_len = self->token_pos; + self->token_pos = 0; + switch( token ) { + case NEWLINE: + return 0; case WHITESPACE: - have_ws = last_column; - break; + continue; + case VERTICAL: - CHKDELIM have_ws = 0; - SETST( CP_V1 ); - break; + { + webvtt_status status; + pos -= token_len; /* Required for parse_vertical() */ + self->column = last_column; /* Reset for parse_vertical() */ + status = webvtt_parse_vertical( self, cue, buffer, &pos, len ); + if( status == WEBVTT_PARSE_ERROR ) { + return WEBVTT_PARSE_ERROR; + } + } + break; + + case POSITION: - CHKDELIM have_ws = 0; - SETST( CP_P1 ); - break; + { + webvtt_status status; + pos -= token_len; /* Required for parse_position() */ + self->column = last_column; /* Reset for parse_position() */ + status = webvtt_parse_position( self, cue, buffer, &pos, len ); + if( status == WEBVTT_PARSE_ERROR ) { + return WEBVTT_PARSE_ERROR; + } + } + break; + case ALIGN: { webvtt_status status; - pos -= tlen; /* Required for parse_align() */ + pos -= token_len; /* Required for parse_align() */ self->column = last_column; /* Reset for parse_align() */ status = webvtt_parse_align( self, cue, buffer, &pos, len ); if( status == WEBVTT_PARSE_ERROR ) { @@ -818,14 +966,22 @@ else if( !have_ws ) \ break; case SIZE: - CHKDELIM have_ws = 0; - SETST( CP_S1 ); - break; + { + webvtt_status status; + pos -= token_len; /* Required for parse_size() */ + self->column = last_column; /* Reset for parse_size() */ + status = webvtt_parse_size( self, cue, buffer, &pos, len ); + if( status == WEBVTT_PARSE_ERROR ) { + return WEBVTT_PARSE_ERROR; + } + } + break; + case LINE: { webvtt_status status; - pos -= tlen; /* Required for parse_align() */ - self->column = last_column; /* Reset for parse_align() */ + pos -= token_len; /* Required for parse_line() */ + self->column = last_column; /* Reset for parse_line() */ status = webvtt_parse_line( self, cue, buffer, &pos, len ); if( status == WEBVTT_PARSE_ERROR ) { return WEBVTT_PARSE_ERROR; @@ -833,267 +989,28 @@ else if( !have_ws ) \ } break; default: - if( have_ws ) { - ERROR_AT_COLUMN( WEBVTT_INVALID_CUESETTING, last_column ); - } else if( token == BADTOKEN ) { - /* it was a bad delimiter... */ - if( !baddelim ) { - baddelim = last_column; - } - ++pos; - } - while( pos < len && buffer[pos] != 0x09 && buffer[pos] != 0x20 ) { + ERROR_AT_COLUMN( WEBVTT_INVALID_CUESETTING, last_column ); + while( pos < len && buffer[pos] != '\t' && buffer[pos] != ' ' ) { ++pos; } } break; -#define CS1(S) \ - if( token == COLON ) \ - { if(have_ws) { ERROR_AT_COLUMN(WEBVTT_UNEXPECTED_WHITESPACE,have_ws); } SETST((S)); have_ws = 0; } \ - else if( token == WHITESPACE && !have_ws ) \ - { \ - have_ws = last_column; \ - } \ - else \ - { \ - switch(token) \ - { \ - case LR: case RL: case INTEGER: case PERCENTAGE: case START: case MIDDLE: case END: case LEFT: case RIGHT: \ - ERROR_AT_COLUMN(WEBVTT_MISSING_CUESETTING_DELIMITER,have_ws ? have_ws : last_column); break; \ - default: \ - ERROR_AT_COLUMN(WEBVTT_INVALID_CUESETTING_DELIMITER,last_column); \ - while( pos < len && buffer[pos] != 0x20 && buffer[pos] != 0x09 ) ++pos; \ - break; \ - } \ - have_ws = 0; \ - } - - /** - * If we get a COLON, we advance to the next state. - * If we encounter whitespace first, fire an "unexpected whitespace" - * error and continue. If we encounter a cue-setting value, fire a - * "missing cuesetting delimiter" error otherwise (eg vertical;rl), fire - * "invalid cuesetting delimiter" error - * - * this logic is performed by the CS1 macro, defined above - */ - case CP_V1: - CS1( CP_V2 ); - break; - case CP_P1: - CS1( CP_P2 ); - break; - case CP_A1: - CS1( CP_A2 ); - break; - case CP_S1: - CS1( CP_S2 ); - break; - case CP_L1: - CS1( CP_L2 ); - break; -#undef CS1 - -/* BV: emit the BAD_VALUE error for the appropriate setting, when required */ -#define BV(T) \ -ERROR_AT_COLUMN(WEBVTT_##T##_BAD_VALUE,last_column); \ -while( pos < len && buffer[pos] != 0x20 && buffer[pos] != 0x09 ) ++pos; \ -SETST(CP_CS0); - -/* HV: emit the ALREADY_SET (have value) error for the appropriate setting, when required */ -#define HV(T) \ -if( cue->flags & CUE_HAVE_##T ) \ -{ \ - ERROR_AT_COLUMN(WEBVTT_##T##_ALREADY_SET,last_column); \ -} -/* WS: emit the WEBVTT_UNEXPECTED_WHITESPACE error when required. */ -#define WS \ -case WHITESPACE: \ - if( !have_ws ) \ - { \ - ERROR_AT_COLUMN(WEBVTT_UNEXPECTED_WHITESPACE,last_column); \ - have_ws = last_column; \ - } \ -break - -/* set that the cue already has a value for this */ -#define SV(T) cue->flags |= CUE_HAVE_##T - case CP_V2: - HV( VERTICAL ); - switch( token ) { - WS; - case LR: - cue->settings.vertical = WEBVTT_VERTICAL_LR; - have_ws = 0; - SETST( CP_CS0 ); - SV( VERTICAL ); - break; - case RL: - cue->settings.vertical = WEBVTT_VERTICAL_RL; - have_ws = 0; - SETST( CP_CS0 ); - SV( VERTICAL ); - break; - default: - BV( VERTICAL ); - } - break; - - case CP_P2: - HV( POSITION ); - switch( token ) { - WS; - case PERCENTAGE: { - int digits; - const webvtt_byte *t = self->token; - webvtt_int64 v = parse_int( &t, &digits ); - if( v < 0 ) { - BV( POSITION ); - } - cue->settings.position = ( webvtt_uint )v; - SETST( CP_CS0 ); - SV( POSITION ); - } - break; - default: - BV( POSITION ); - break; - } - break; - - case CP_A2: - HV( ALIGN ); - switch( token ) { - WS; - case START: - cue->settings.align = WEBVTT_ALIGN_START; - have_ws = 0; - SETST( CP_CS0 ); - SV( ALIGN ); - break; - case MIDDLE: - cue->settings.align = WEBVTT_ALIGN_MIDDLE; - have_ws = 0; - SETST( CP_CS0 ); - SV( ALIGN ); - break; - case END: - cue->settings.align = WEBVTT_ALIGN_END; - have_ws = 0; - SETST( CP_CS0 ); - SV( ALIGN ); - break; - case LEFT: - cue->settings.align = WEBVTT_ALIGN_LEFT; - have_ws = 0; - SETST( CP_CS0 ); - SV( ALIGN ); - break; - case RIGHT: - cue->settings.align = WEBVTT_ALIGN_RIGHT; - have_ws = 0; - SETST( CP_CS0 ); - SV( ALIGN ); - break; - default: - BV( ALIGN ); - break; - } - break; - - case CP_S2: - HV( SIZE ); - switch( token ) { - WS; - case PERCENTAGE: { - int digits; - const webvtt_byte *t = self->token; - webvtt_int64 v = parse_int( &t, &digits ); - if( v < 0 ) { - BV( SIZE ); - } - cue->settings.size = ( webvtt_uint )v; - SETST( CP_CS0 ); - SV( SIZE ); - } - break; - default: - BV( SIZE ); - break; - } - break; - - case CP_L2: - HV( LINE ); - switch( token ) { - WS; - case INTEGER: { - const webvtt_byte *t = self->token; - webvtt_int64 v = parse_int( &t, &digits ); - cue->snap_to_lines = 1; - cue->settings.line = ( int )v; - SETST( CP_CS0 ); - SV( LINE ); - } - break; - case PERCENTAGE: { - const webvtt_byte *t = self->token; - webvtt_int64 v = parse_int( &t, &digits ); - if( v < 0 ) { - BV( POSITION ); - } - cue->snap_to_lines = 0; - cue->settings.line = ( int )v; - SETST( CP_CS0 ); - SV( LINE ); - } - break; - default: - BV( LINE ); - break; - } -#undef BV -#undef HV -#undef SV -#undef WS } self->token_pos = 0; - last_token = token; } /** * If we didn't finish in a good state... */ - if( state != CP_CS0 ) { + if( state != CP_SETTING ) { /* if we never made it to the cuesettings, we didn't finish the cuetimes */ - if( state < CP_CS0 ) { + if( state < CP_SETTING ) { ERROR( WEBVTT_UNFINISHED_CUETIMES ); return -1; } else { /* if we did, we should report an error but continue parsing. */ webvtt_error e = WEBVTT_INVALID_CUESETTING; - switch( state ) { - case CP_V2: - e = WEBVTT_VERTICAL_BAD_VALUE; - break; - case CP_P2: - e = WEBVTT_POSITION_BAD_VALUE; - break; - case CP_A2: - e = WEBVTT_ALIGN_BAD_VALUE; - break; - case CP_S2: - e = WEBVTT_SIZE_BAD_VALUE; - break; - case CP_L2: - e = WEBVTT_LINE_BAD_VALUE; - break; - } ERROR( e ); } - } else { - if( baddelim ) { - ERROR_AT_COLUMN( WEBVTT_INVALID_CUESETTING_DELIMITER, baddelim ); - } } #undef SETST return 0; @@ -1101,42 +1018,47 @@ break static webvtt_status parse_webvtt( webvtt_parser self, const webvtt_byte *buffer, webvtt_uint *ppos, - webvtt_uint len, webvtt_parse_mode *mode, int finish ) + webvtt_uint len, int finish ) { webvtt_status status = WEBVTT_SUCCESS; - webvtt_token token; + webvtt_token token = 0; webvtt_uint pos = *ppos; - int settings_delimiter = 0; int skip_error = 0; - int settings_whitespace = 0; while( pos < len ) { - webvtt_uint last_column, last_line, last_pos; + webvtt_uint last_column, last_line; skip_error = 0; -_next: last_column = self->column; last_line = self->line; - last_pos = pos; /** * If we're in certain states, we don't want to get a token and just * want to read text instead. */ if( SP->state == T_CUEREAD ) { - int v; - webvtt_uint old_pos = pos; - if( v = webvtt_string_getline( &SP->v.text, buffer, &pos, - len, 0, finish, 0 ) ) { - if( v < 0 ) { - webvtt_release_string( &SP->v.text ); - SP->type = V_NONE; - POP(); - ERROR( WEBVTT_ALLOCATION_FAILED ); - status = WEBVTT_OUT_OF_MEMORY; - goto _finish; + DIE_IF( SP->type != V_TEXT ); + if( SP->flags == 0 ) { + int v; + if( ( v = webvtt_string_getline( &SP->v.text, buffer, &pos, len, 0, + finish ) ) ) { + if( v < 0 ) { + webvtt_release_string( &SP->v.text ); + SP->type = V_NONE; + POP(); + ERROR( WEBVTT_ALLOCATION_FAILED ); + status = WEBVTT_OUT_OF_MEMORY; + goto _finish; + } + SP->flags = 1; + } + } + if( SP->flags ) { + webvtt_token token = webvtt_lex_newline( self, buffer, &pos, len, + self->finished ); + if( token == NEWLINE ) { + POP(); + continue; } - /* POP the stack and let the previous frame deal with it */ - POP(); } } @@ -1164,6 +1086,10 @@ _next: } _recheck: switch( SP->state ) { + default: + /* Should never happen */ + break; + case T_INITIAL: /** * In the initial state: @@ -1180,15 +1106,10 @@ _recheck: if( token == WEBVTT ) { PUSH0( T_TAG, 0, V_NONE ); break; - } else { - if( pos != len ) { - if( !skip_error ) { - ERROR_AT_COLUMN( WEBVTT_MALFORMED_TAG, last_column ); - skip_error = 1; - } - status = WEBVTT_PARSE_ERROR; - goto _finish; - } + } else if( token != UNFINISHED ) { + ERROR_AT( WEBVTT_MALFORMED_TAG, 1, 1 ); + status = WEBVTT_PARSE_ERROR; + goto _finish; } break; @@ -1256,6 +1177,9 @@ _recheck: case NEWLINE: SP->state = T_FROM; break; + + default: + break; } /** @@ -1307,94 +1231,37 @@ _recheck: case T_CUE: - if( self->popped && FRAMEUP( 1 )->state == T_CUEREAD ) { - /** - * We're expecting either cue-id (contains '-->') or cue - * params - */ - webvtt_cue *cue = SP->v.cue; - webvtt_state *st = FRAMEUP( 1 ); - webvtt_string text = st->v.text; + { + webvtt_cue *cue; + webvtt_state *st; + webvtt_string text; + SAFE_ASSERT( self->popped && FRAMEUP( 1 )->state == T_CUEREAD ); + /** + * We're expecting either cue-id (contains '-->') or cue + * params + */ + cue = SP->v.cue; + st = FRAMEUP( 1 ); + text.d = st->v.text.d; - /* FIXME: guard inconsistent state */ - if (!cue) { - ERROR( WEBVTT_PARSE_ERROR ); - status = WEBVTT_PARSE_ERROR; - goto _finish; - } + st->type = V_NONE; + st->v.cue = NULL; - st->type = V_NONE; - st->v.cue = NULL; - - /** - * The type should be V_TEXT. If it's not, somethings wrong. - * - * TODO: Add debug assertion - */ - if( find_bytes( webvtt_string_text( &text ), webvtt_string_length( &text ), separator, - sizeof( separator ) ) == WEBVTT_SUCCESS) { - /* It's not a cue id, we found '-->'. It can't be a second - cueparams line, because if we had it, we would be in - a different state. */ - int v; - /* backup the column */ - self->column = 1; - if( ( v = parse_cueparams( self, webvtt_string_text( &text ), - webvtt_string_length( &text ), cue ) ) < 0 ) { - if( v == WEBVTT_PARSE_ERROR ) { - status = WEBVTT_PARSE_ERROR; - goto _finish; - } - webvtt_release_string( &text ); - *mode = M_SKIP_CUE; - goto _finish; - } else { - webvtt_release_string( &text ); - cue->flags |= CUE_HAVE_CUEPARAMS; - *mode = M_CUETEXT; - goto _finish; - } - } else { - /* It is a cue-id */ - if( cue && cue->flags & CUE_HAVE_ID ) { - /** - * This isn't actually a cue-id, because we already - * have one. It seems to be cuetext, which is occurring - * before cue-params - */ - webvtt_release_string( &text ); - ERROR( WEBVTT_CUE_INCOMPLETE ); - *mode = M_SKIP_CUE; - goto _finish; - } else { - self->column += webvtt_string_length( &text ); - if( WEBVTT_FAILED( status = webvtt_string_append( - &cue->id, webvtt_string_text( &text ), webvtt_string_length( &text ) ) ) ) { - webvtt_release_string( &text ); - ERROR( WEBVTT_ALLOCATION_FAILED ); - } - - cue->flags |= CUE_HAVE_ID; - } - } - webvtt_release_string( &text ); - self->popped = 0; - } else { - webvtt_cue *cue = SP->v.cue; - /* If we have a newline, it might be the end of the cue. */ - if( token == NEWLINE ) { - if( cue->flags & CUE_HAVE_CUEPARAMS ) { - *mode = M_CUETEXT; - } else if( cue->flags & CUE_HAVE_ID ) { - PUSH0( T_CUEREAD, 0, V_NONE ); - } else { - /* I don't think this should ever happen? */ - POPBACK(); - } - } + /* FIXME: guard inconsistent state */ + if (!cue) { + ERROR( WEBVTT_PARSE_ERROR ); + status = WEBVTT_PARSE_ERROR; + goto _finish; } - break; + status = webvtt_proc_cueline( self, cue, &text ); + ++self->line; + if( self->mode != M_WEBVTT ) { + goto _finish; + } + self->popped = 0; + } + break; } /** @@ -1412,46 +1279,158 @@ _finish: return status; } -static webvtt_status -read_cuetext( webvtt_parser self, const webvtt_byte *b, webvtt_uint -*ppos, webvtt_uint len, webvtt_parse_mode *mode, webvtt_bool finish ) +WEBVTT_INTERN webvtt_status +webvtt_read_cuetext( webvtt_parser self, const webvtt_byte *b, + webvtt_uint *ppos, webvtt_uint len, webvtt_bool finish ) { webvtt_status status = WEBVTT_SUCCESS; webvtt_uint pos = *ppos; int finished = 0; - do { - int v; - if( ( v = webvtt_string_getline( &self->line_buffer, b, &pos, len, &self->truncate, finish, 1 ) ) ) { - if( v < 0 ) { - status = WEBVTT_OUT_OF_MEMORY; - goto _finish; - } + int flags = 0; + webvtt_cue *cue; - /** - * We've encountered a line without any cuetext on it, i.e. there is no - * newline character and len is 0 or there is and len is 1, therefore, - * the cue text is finished. - */ - if( self->line_buffer.d->length <= 1 ) { - /** - * finished + /* Ensure that we have a cue to work with */ + SAFE_ASSERT( self->top->type = V_CUE ); + cue = self->top->v.cue; + + /** + * Hack to support lines spanning multiple buffers. + * + * TODO: Do this some better way. This is not good! + */ + if( self->line_buffer.d != 0 && self->line_buffer.d->text[ + self->line_buffer.d->length - 1 ] == '\n' ) { + flags = 1; + } + + do { + if( !flags ) { + int v; + if( ( v = webvtt_string_getline( &self->line_buffer, b, &pos, len, + &self->truncate, finish ) ) ) { + if( v < 0 || WEBVTT_FAILED( webvtt_string_putc( &self->line_buffer, + '\n' ) ) ) { + status = WEBVTT_OUT_OF_MEMORY; + goto _finish; + } + flags = 1; + } + } + if( flags ) { + webvtt_token token = webvtt_lex_newline( self, b, &pos, len, finish ); + if( token == NEWLINE ) { + self->token_pos = 0; + self->line++; + + /* Remove the '\n' that we appended to determine that we're in state 1 */ - finished = 1; + self->line_buffer.d->text[ --self->line_buffer.d->length ] = 0; + /** + * We've encountered a line without any cuetext on it, i.e. there is no + * newline character and len is 0 or there is and len is 1, therefore, + * the cue text is finished. + */ + if( self->line_buffer.d->length == 0 ) { + webvtt_release_string( &self->line_buffer ); + finished = 1; + } else if( find_bytes( webvtt_string_text( &self->line_buffer ), + webvtt_string_length( &self->line_buffer ), separator, + sizeof( separator ) ) == WEBVTT_SUCCESS ) { + /** + * Line contains cue-times separator, and thus we treat it as a + * separate cue. Trick program into thinking that T_CUEREAD had read + * this line. + */ + do_push( self, 0, 0, T_CUEREAD, 0, V_NONE, self->line, self->column ); + webvtt_copy_string( &SP->v.text, &self->line_buffer ); + webvtt_release_string( &self->line_buffer ); + SP->type = V_TEXT; + POP(); + finished = 1; + } else { + /** + * If it's not the end of a cue, simply append it to the cue's payload + * text. + */ + if( webvtt_string_length( &cue->body ) && + WEBVTT_FAILED( webvtt_string_putc( &cue->body, '\n' ) ) ) { + status = WEBVTT_OUT_OF_MEMORY; + goto _finish; + } + webvtt_string_append_string( &cue->body, &self->line_buffer ); + webvtt_release_string( &self->line_buffer ); + flags = 0; + } } } } while( pos < len && !finished ); _finish: *ppos = pos; + if( finish ) { + finished = 1; + } + /** * If we didn't encounter 2 successive EOLs, and it's not the final buffer in * the file, notify the caller. */ - if( pos >= len && !WEBVTT_FAILED( status ) && !finished ) { + if( !finish && pos >= len && !WEBVTT_FAILED( status ) && !finished ) { status = WEBVTT_UNFINISHED; } return status; } +WEBVTT_INTERN webvtt_status +webvtt_proc_cuetext( webvtt_parser self, const webvtt_byte *b, + webvtt_uint *ppos, webvtt_uint len, webvtt_bool finish ) +{ + webvtt_status status; + webvtt_cue *cue; + SAFE_ASSERT( ( self->mode == M_CUETEXT || self->mode == M_SKIP_CUE ) + && self->top->type == V_CUE ); + cue = self->top->v.cue; + SAFE_ASSERT( cue != 0 ); + status = webvtt_read_cuetext( self, b, ppos, len, finish ); + + if( status == WEBVTT_SUCCESS ) { + if( self->mode != M_SKIP_CUE ) { + /** + * Once we've successfully read the cuetext into line_buffer, call the + * cuetext parser from cuetext.c + */ + status = webvtt_parse_cuetext( self, cue, &cue->body, + self->finished ); + + /** + * return the cue to the user, if possible. + */ + finish_cue( self, &cue ); + } else { + webvtt_release_cue( &cue ); + } + + self->top->type = V_NONE; + self->top->state = 0; + self->top->v.cue = 0; + + if( (self->top+1)->type == V_NONE ) { + (self->top+1)->state = 0; + /* Pop from T_CUE state */ + POP(); + } else { + /** + * If we found '-->', we need to create another cue and remain + * in T_CUE state + */ + webvtt_create_cue( &self->top->v.cue ); + self->top->type = V_CUE; + self->top->state = T_CUE; + } + self->mode = M_WEBVTT; + } + return status; +} + WEBVTT_EXPORT webvtt_status webvtt_parse_chunk( webvtt_parser self, const void *buffer, webvtt_uint len ) { @@ -1462,7 +1441,7 @@ webvtt_parse_chunk( webvtt_parser self, const void *buffer, webvtt_uint len ) while( pos < len ) { switch( self->mode ) { case M_WEBVTT: - if( WEBVTT_FAILED( status = parse_webvtt( self, b, &pos, len, &self->mode, self->finished ) ) ) { + if( WEBVTT_FAILED( status = parse_webvtt( self, b, &pos, len, self->finished ) ) ) { return status; } break; @@ -1471,30 +1450,14 @@ webvtt_parse_chunk( webvtt_parser self, const void *buffer, webvtt_uint len ) /** * read in cuetext */ - if( WEBVTT_FAILED( status = read_cuetext( self, b, &pos, len, &self->mode, self->finished ) ) ) { + if( WEBVTT_FAILED( status = webvtt_proc_cuetext( self, b, &pos, len, + self->finished ) ) ) { if( status == WEBVTT_UNFINISHED ) { /* Make an exception here, because this isn't really a failure. */ return WEBVTT_SUCCESS; } return status; } - /** - * Once we've successfully read the cuetext into line_buffer, call the - * cuetext parser from cuetext.c - */ - status = webvtt_parse_cuetext( self, SP->v.cue, &self->line_buffer, self->finished ); - - /** - * return the cue to the user, if possible. - */ - finish_cue( self, &SP->v.cue ); - - /** - * return to our typical parsing mode now. - */ - SP->type = V_NONE; - webvtt_release_string( &self->line_buffer ); - self->mode = M_WEBVTT; /* If we failed to parse cuetext, return the error */ if( WEBVTT_FAILED( status ) ) { @@ -1503,11 +1466,10 @@ webvtt_parse_chunk( webvtt_parser self, const void *buffer, webvtt_uint len ) break; case M_SKIP_CUE: - if( WEBVTT_FAILED( status = read_cuetext( self, b, &pos, len, &self->mode, self->finished ) ) ) { + if( WEBVTT_FAILED( status = webvtt_proc_cuetext( self, b, &pos, len, + self->finished ) ) ) { return status; } - webvtt_release_string( &self->line_buffer ); - self->mode = M_WEBVTT; break; case M_READ_LINE: { @@ -1516,7 +1478,9 @@ webvtt_parse_chunk( webvtt_parser self, const void *buffer, webvtt_uint len ) * we will and depending on our state, do something with it. */ int ret; - if( ( ret = webvtt_string_getline( &self->line_buffer, b, &pos, len, &self->truncate, self->finished, 0 ) ) ) { + if( ( ret = webvtt_string_getline( &self->line_buffer, b, &pos, len, + &self->truncate, + self->finished ) ) ) { if( ret < 0 ) { ERROR( WEBVTT_ALLOCATION_FAILED ); return WEBVTT_OUT_OF_MEMORY; @@ -1526,9 +1490,6 @@ webvtt_parse_chunk( webvtt_parser self, const void *buffer, webvtt_uint len ) break; } } - if( WEBVTT_FAILED( status = webvtt_skipwhite( b, &pos, len ) ) ) { - return status; - } } return WEBVTT_SUCCESS; @@ -1558,9 +1519,9 @@ parse_int( const webvtt_byte **pb, int *pdigits ) /** * Digit character, carry on */ - result = result * 10 + ( ch - UTF8_DIGIT_ZERO ); + result = result * 10 + ( ch - '0' ); ++digits; - } else if( mul == 1 && digits == 0 && ch == UTF8_HYPHEN_MINUS ) { + } else if( mul == 1 && digits == 0 && ch == '-' ) { mul = -1; } else { break; @@ -1602,7 +1563,7 @@ parse_timestamp( const webvtt_byte *b, webvtt_timestamp *result ) } /* fail if missing colon ':' character */ - if ( !*b || *b++ != UTF8_COLON ) { + if ( !*b || *b++ != ':' ) { malformed = 1; } @@ -1617,10 +1578,10 @@ parse_timestamp( const webvtt_byte *b, webvtt_timestamp *result ) malformed = 1; } - /* if we already know there's an hour component, or if the next byte is a + /* if we already know there's an hour component, or if the next byte is a colon ':', read the next value */ - if ( have_hours || ( *b == UTF8_COLON ) ) { - if( *b++ != UTF8_COLON ) { + if ( have_hours || ( *b == ':' ) ) { + if( *b++ != ':' ) { goto _malformed; } if( !*b || !webvtt_isdigit( *b ) ) { @@ -1639,7 +1600,7 @@ parse_timestamp( const webvtt_byte *b, webvtt_timestamp *result ) /* collect the manditory seconds-frac component. fail if there is no FULL_STOP '.' or if there is no ascii digit following it */ - if( *b++ != UTF8_FULL_STOP || !webvtt_isdigit( *b ) ) { + if( *b++ != '.' || !webvtt_isdigit( *b ) ) { goto _malformed; } v[3] = parse_int( &b, &digits ); diff --git a/media/webvtt/parser_internal.h b/media/webvtt/parser_internal.h index 4675601e8ddd..0085d2c8c6d9 100644 --- a/media/webvtt/parser_internal.h +++ b/media/webvtt/parser_internal.h @@ -144,18 +144,21 @@ webvtt_parse_state_t { */ typedef enum webvtt_lexer_state_t { - L_START = 0, L_BOM0, L_BOM1, L_WEBVTT0, L_WEBVTT1, L_WEBVTT2, L_WEBVTT3, L_WEBVTT4, L_WEBVTT5, L_DASH0, L_SEP1, - L_DIGIT0, L_NEWLINE0, L_WHITESPACE, L_POSITION0, L_POSITION1, L_POSITION2, L_POSITION3, L_POSITION4, L_POSITION5, - L_POSITION6, L_ALIGN0, L_ALIGN1, L_ALIGN2, L_ALIGN3, L_L0, L_LINE1, L_LINE2, L_LINE3, - L_VERTICAL0, L_VERTICAL1, L_VERTICAL2, L_VERTICAL3, L_VERTICAL4, L_VERTICAL5, L_VERTICAL6, L_RL0, - L_S0, L_SIZE1, L_SIZE2, L_START1, L_START2, L_START3, L_MIDDLE0, L_MIDDLE1, L_MIDDLE2, L_MIDDLE3, - L_MIDDLE4, L_END0, L_END1, L_TIMESTAMP1, L_TIMESTAMP2, L_TIMESTAMP3, L_RIGHT1, L_RIGHT2, - L_RIGHT3, L_NOTE1, L_NOTE2, L_NOTE3, L_LEFT1, L_LEFT2, + L_START = 0, L_BOM0, L_BOM1, L_WEBVTT0, L_WEBVTT1, L_WEBVTT2, L_WEBVTT3, + L_WEBVTT4, L_DASH0, L_SEP1, L_DIGIT0, L_NEWLINE0, L_WHITESPACE, L_POSITION0, + L_POSITION1, L_POSITION2, L_POSITION3, L_POSITION4, L_POSITION5, L_POSITION6, + L_ALIGN0, L_ALIGN1, L_ALIGN2, L_ALIGN3, L_L0, L_LINE1, L_LINE2, L_VERTICAL0, + L_VERTICAL1, L_VERTICAL2, L_VERTICAL3, L_VERTICAL4, L_VERTICAL5, L_VERTICAL6, + L_RL0, L_S0, L_SIZE1, L_SIZE2, L_START1, L_START2, L_START3, L_MIDDLE0, + L_MIDDLE1, L_MIDDLE2, L_MIDDLE3, L_MIDDLE4, L_END0, L_END1, L_TIMESTAMP1, + L_TIMESTAMP2, L_TIMESTAMP3, L_RIGHT1, L_RIGHT2, L_RIGHT3, L_NOTE1, L_NOTE2, + L_NOTE3, L_LEFT1, L_LEFT2, } webvtt_lexer_state; typedef struct webvtt_state { webvtt_parse_state state; + webvtt_uint flags; /* Defaults to 0 when pushed */ webvtt_token token; webvtt_state_value_type type; webvtt_uint back; @@ -199,6 +202,8 @@ webvtt_parser_t { void *userdata; webvtt_bool finished; + webvtt_uint cuetext_line; /* start line of cuetext */ + /** * 'mode' can have several states, it is not boolean. */ @@ -227,8 +232,47 @@ webvtt_parser_t { WEBVTT_INTERN webvtt_token webvtt_lex( webvtt_parser self, const webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint length, webvtt_bool finish ); WEBVTT_INTERN webvtt_status webvtt_lex_word( webvtt_parser self, webvtt_string *pba, const webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint length, webvtt_bool finish ); + +/* Tokenize newline sequence, without incrementing 'self->line'. Returns + * BAD_TOKEN when a newline sequence is not found. */ +WEBVTT_INTERN webvtt_token webvtt_lex_newline( webvtt_parser self, const + webvtt_byte *buffer, webvtt_uint *pos, webvtt_uint length, webvtt_bool finish ); + +WEBVTT_INTERN webvtt_status webvtt_proc_cueline( webvtt_parser self, + webvtt_cue *cue, webvtt_string *line ); + +WEBVTT_INTERN webvtt_status webvtt_parse_align( webvtt_parser self, + webvtt_cue *cue, const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len ); + +WEBVTT_INTERN webvtt_status webvtt_parse_line( webvtt_parser self, + webvtt_cue *cue, const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len ); + +WEBVTT_INTERN webvtt_status webvtt_parse_position( webvtt_parser self, + webvtt_cue *cue, const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len ); + +WEBVTT_INTERN webvtt_status webvtt_parse_size( webvtt_parser self, + webvtt_cue *cue, const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len ); + +WEBVTT_INTERN webvtt_status webvtt_parse_vertical( webvtt_parser self, + webvtt_cue *cue, const webvtt_byte *text, webvtt_uint *pos, webvtt_uint len ); + WEBVTT_INTERN int parse_timestamp( const webvtt_byte *b, webvtt_timestamp *result ); +WEBVTT_INTERN webvtt_status do_push( webvtt_parser self, webvtt_uint token, + webvtt_uint back, webvtt_uint state, void *data, webvtt_state_value_type type, + webvtt_uint line, webvtt_uint column ); + +WEBVTT_INTERN webvtt_status webvtt_read_cuetext( webvtt_parser self, + const webvtt_byte *b, webvtt_uint *ppos, webvtt_uint len, + webvtt_bool finish ); + +WEBVTT_INTERN webvtt_status webvtt_proc_cuetext( webvtt_parser self, + const webvtt_byte *b, webvtt_uint *ppos, webvtt_uint len, + webvtt_bool finish ); + +WEBVTT_INTERN int parse_cueparams( webvtt_parser self, const webvtt_byte *text, + webvtt_uint len, webvtt_cue *cue ); + /** * Flags which can apply additional meaning to a token. find_token() will * test for only the actual token and ignore the additional flags. diff --git a/media/webvtt/string.c b/media/webvtt/string.c index 708107b91ec4..1a4f7105d7c1 100644 --- a/media/webvtt/string.c +++ b/media/webvtt/string.c @@ -84,8 +84,6 @@ webvtt_create_string( webvtt_uint32 alloc, webvtt_string *result ) WEBVTT_EXPORT webvtt_status webvtt_create_string_with_text( webvtt_string *result, const webvtt_byte *init_text, int len ) { - webvtt_uint pos = 0; - if( !result ) { return WEBVTT_INVALID_PARAM; } @@ -200,7 +198,7 @@ webvtt_string_text(const webvtt_string *str) return str->d->text; } -WEBVTT_EXPORT const webvtt_uint32 +WEBVTT_EXPORT webvtt_uint32 webvtt_string_length(const webvtt_string *str) { if( !str || !str->d ) @@ -211,7 +209,7 @@ webvtt_string_length(const webvtt_string *str) return str->d->length; } -WEBVTT_EXPORT const webvtt_uint32 +WEBVTT_EXPORT webvtt_uint32 webvtt_string_capacity(const webvtt_string *str) { if( !str || !str->d ) @@ -287,14 +285,15 @@ grow( webvtt_string *str, webvtt_uint need ) WEBVTT_EXPORT int webvtt_string_getline( webvtt_string *src, const webvtt_byte *buffer, - webvtt_uint *pos, webvtt_uint len, int *truncate, webvtt_bool finish, webvtt_bool retain_new_line ) + webvtt_uint *pos, int len, int *truncate, + webvtt_bool finish ) { int ret = 0; webvtt_string *str = src; webvtt_string_data *d = 0; const webvtt_byte *s = buffer + *pos; const webvtt_byte *p = s; - const webvtt_byte *n = buffer + len; + const webvtt_byte *n; /** *if this is public now, maybe we should return webvtt_status so we can @@ -312,13 +311,13 @@ webvtt_string_getline( webvtt_string *src, const webvtt_byte *buffer, } d = str->d; } - - while( p < n && *p != UTF8_CARRIAGE_RETURN && *p != UTF8_LINE_FEED ) { - ++p; + if( len < 0 ) { + len = strlen( (const char *)buffer ); } - /* Retain the new line character. */ - if( p < n && retain_new_line ) { - p++; + n = buffer + len; + + while( p < n && *p != '\r' && *p != '\n' ) { + ++p; } if( p < n || finish ) { @@ -371,11 +370,21 @@ webvtt_string_putc( webvtt_string *str, webvtt_byte to_append ) } WEBVTT_EXPORT webvtt_bool -webvtt_string_is_equal( webvtt_string *str, webvtt_byte *to_compare, webvtt_uint len ) +webvtt_string_is_equal( const webvtt_string *str, const webvtt_byte *to_compare, + int len ) { - if( !str || !to_compare || webvtt_string_length( str ) != len ) { + if( !str || !to_compare ) { return 0; } + + if( len < 0 ) { + len = strlen( (const char *)to_compare ); + } + + if( str->d->length != (unsigned)len ) { + return 0; + } + return memcmp( webvtt_string_text( str ), to_compare, len ) == 0; } @@ -584,8 +593,8 @@ WEBVTT_EXPORT webvtt_uint16 webvtt_utf8_to_utf16( const webvtt_byte *utf8, const webvtt_byte *end, webvtt_uint16 *high_surrogate ) { - int need = 0, min = 0; - webvtt_uint32 uc = 0; + int need = 0; + webvtt_uint32 uc = 0, min = 0; /* We're missing our pointers */ if( !utf8 ) { @@ -625,7 +634,8 @@ webvtt_utf8_to_utf16( const webvtt_byte *utf8, const webvtt_byte *end, *high_surrogate = UTF_HIGH_SURROGATE( uc ); } return UTF_LOW_SURROGATE( uc ); - } else if ( ( uc < min ) || ( uc >= 0xD800 && uc <= 0xDFFF ) || nc || uc >= 0x110000) { + } else if ( ( uc < min ) || ( uc >= 0xD800 && uc <= 0xDFFF ) || nc + || uc >= 0x110000) { /* Non-character, overlong sequence, or utf16 surrogate */ return 0xFFFD; } else { diff --git a/media/webvtt/string_internal.h b/media/webvtt/string_internal.h index d699a14540b1..0dd649f03162 100644 --- a/media/webvtt/string_internal.h +++ b/media/webvtt/string_internal.h @@ -29,10 +29,6 @@ # define __INTERN_STRING_H__ # include -# define UTF8_AMPERSAND (0x26) -# define UTF8_LESS_THAN (0x3C) -# define UTF8_GREATER_THAN (0x3E) -# define UTF8_HYPHEN_MINUS (0x2D) # define UTF8_LEFT_TO_RIGHT_1 (0xE2) # define UTF8_LEFT_TO_RIGHT_2 (0x80) # define UTF8_LEFT_TO_RIGHT_3 (0x8E) @@ -41,48 +37,6 @@ # define UTF8_RIGHT_TO_LEFT_3 (0x8F) # define UTF8_NO_BREAK_SPACE_1 (0xC2) # define UTF8_NO_BREAK_SPACE_2 (0xA0) -# define UTF8_NULL_BYTE (0x00) -# define UTF8_COLON (0x3A) -# define UTF8_SEMI_COLON (0x3B) -# define UTF8_TAB (0x09) -# define UTF8_FORM_FEED (0x0C) -# define UTF8_LINE_FEED (0x0A) -# define UTF8_CARRIAGE_RETURN (0x0D) -# define UTF8_FULL_STOP (0x2E) -# define UTF8_SOLIDUS (0x2F) -# define UTF8_SPACE (0x20) -# define UTF8_DIGIT_ZERO (0x30) -# define UTF8_DIGIT_NINE (0x39) - -# define UTF8_CAPITAL_A (0x41) -# define UTF8_CAPITAL_Z (0x5A) - -# define UTF8_A (0x61) -# define UTF8_B (0x62) -# define UTF8_C (0x63) -# define UTF8_D (0x64) -# define UTF8_E (0x65) -# define UTF8_F (0x66) -# define UTF8_G (0x67) -# define UTF8_H (0x68) -# define UTF8_I (0x69) -# define UTF8_J (0x6A) -# define UTF8_K (0x6B) -# define UTF8_L (0x6C) -# define UTF8_M (0x6D) -# define UTF8_N (0x6E) -# define UTF8_O (0x6F) -# define UTF8_P (0x70) -# define UTF8_Q (0x71) -# define UTF8_R (0x72) -# define UTF8_S (0x73) -# define UTF8_T (0x74) -# define UTF8_U (0x75) -# define UTF8_V (0x76) -# define UTF8_W (0x77) -# define UTF8_X (0x78) -# define UTF8_Y (0x79) -# define UTF8_Z (0x7A) /** * Taken from ICU @@ -118,12 +72,12 @@ webvtt_string_data_t { static __WEBVTT_STRING_INLINE int webvtt_isalpha( webvtt_byte ch ) { - return ( ( ( ch >= UTF8_CAPITAL_A ) && ( ch <= UTF8_CAPITAL_Z ) ) || ( ( ch >= UTF8_A ) && ( ch <= UTF8_Z ) ) ); + return ( ( ( ch >= 'A' ) && ( ch <= 'Z' ) ) || ( ( ch >= 'a' ) && ( ch <= 'z' ) ) ); } static __WEBVTT_STRING_INLINE int webvtt_isdigit( webvtt_byte ch ) { - return ( ( ch >= UTF8_DIGIT_ZERO ) && ( ch <= UTF8_DIGIT_NINE ) ); + return ( ( ch >= '0' ) && ( ch <= '9' ) ); } static __WEBVTT_STRING_INLINE int @@ -135,8 +89,8 @@ webvtt_isalphanum( webvtt_byte ch ) static __WEBVTT_STRING_INLINE int webvtt_iswhite( webvtt_byte ch ) { - return ( ( ch == UTF8_CARRIAGE_RETURN ) || ( ch == UTF8_LINE_FEED ) || ( ch == UTF8_FORM_FEED ) - || ( ch == UTF8_TAB ) || ( ch == UTF8_SPACE ) ) ; + return ( ( ch == '\r' ) || ( ch == '\n' ) || ( ch == '\f' ) + || ( ch == '\t' ) || ( ch == ' ' ) ) ; } # undef __WEBVTT_STRING_INLINE From df55253a6e54fec86e476c41d97d0e6be1306766 Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Wed, 17 Apr 2013 21:28:59 +0100 Subject: [PATCH 15/75] Bug 862817 - Fix animateMotion on foreignObject elements. r=dholbert --- layout/reftests/svg/smil/motion/animateMotion-by-1.svg | 6 +++++- layout/svg/nsSVGForeignObjectFrame.cpp | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/layout/reftests/svg/smil/motion/animateMotion-by-1.svg b/layout/reftests/svg/smil/motion/animateMotion-by-1.svg index fdb31efef607..8340603f4066 100644 --- a/layout/reftests/svg/smil/motion/animateMotion-by-1.svg +++ b/layout/reftests/svg/smil/motion/animateMotion-by-1.svg @@ -16,9 +16,13 @@ - + + +
+ + diff --git a/layout/svg/nsSVGForeignObjectFrame.cpp b/layout/svg/nsSVGForeignObjectFrame.cpp index bf247c46880b..5d3572eb6b47 100644 --- a/layout/svg/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/nsSVGForeignObjectFrame.cpp @@ -179,7 +179,7 @@ nsSVGForeignObjectFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, bool nsSVGForeignObjectFrame::IsSVGTransformed(gfxMatrix *aOwnTransform, - gfxMatrix *aFromParentTransform) const + gfxMatrix *aFromParentTransform) const { bool foundTransform = false; @@ -194,7 +194,8 @@ nsSVGForeignObjectFrame::IsSVGTransformed(gfxMatrix *aOwnTransform, nsSVGElement *content = static_cast(mContent); nsSVGAnimatedTransformList* transformList = content->GetAnimatedTransformList(); - if (transformList && transformList->HasTransform()) { + if ((transformList && transformList->HasTransform()) || + content->GetAnimateMotionTransform()) { if (aOwnTransform) { *aOwnTransform = content->PrependLocalTransformsTo(gfxMatrix(), nsSVGElement::eUserSpaceToParent); From a3cc058a5a5e7c473b74019d87513824c4def9ef Mon Sep 17 00:00:00 2001 From: Allison Naaktgeboren Date: Wed, 17 Apr 2013 13:34:42 -0700 Subject: [PATCH 16/75] Bug 830505 - Re theme the autocomplete screen in snapped view.r=sfoster --- .../base/content/bindings/autocomplete.xml | 4 ++-- browser/metro/base/content/browser-ui.js | 5 +++++ browser/metro/base/content/browser.xul | 2 +- browser/metro/theme/browser.css | 19 +++++++++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/browser/metro/base/content/bindings/autocomplete.xml b/browser/metro/base/content/bindings/autocomplete.xml index dbe2dfe6e7a8..ce731e78719f 100644 --- a/browser/metro/base/content/bindings/autocomplete.xml +++ b/browser/metro/base/content/bindings/autocomplete.xml @@ -68,12 +68,12 @@ - + - + diff --git a/browser/metro/base/content/browser-ui.js b/browser/metro/base/content/browser-ui.js index 7614892947e8..caa15188ae1d 100644 --- a/browser/metro/base/content/browser-ui.js +++ b/browser/metro/base/content/browser-ui.js @@ -569,11 +569,16 @@ var BrowserUI = { break; case "metro_viewstate_changed": this._adjustDOMforViewState(); + let autocomplete = document.getElementById("start-autocomplete"); if (aData == "snapped") { FlyoutPanelsUI.hide(); // Order matters (need grids to get dimensions, etc), now // let snapped grid know to refresh/redraw Services.obs.notifyObservers(null, "metro_viewstate_dom_snapped", null); + autocomplete.setAttribute("orient", "vertical"); + } + else { + autocomplete.setAttribute("orient", "horizontal"); } break; } diff --git a/browser/metro/base/content/browser.xul b/browser/metro/base/content/browser.xul index 25fedd3611f9..5449a42cdb3a 100644 --- a/browser/metro/base/content/browser.xul +++ b/browser/metro/base/content/browser.xul @@ -294,7 +294,7 @@ - + diff --git a/browser/metro/theme/browser.css b/browser/metro/theme/browser.css index 5797334552b1..b563d4c87d0e 100644 --- a/browser/metro/theme/browser.css +++ b/browser/metro/theme/browser.css @@ -875,6 +875,25 @@ setting[type="radio"] > vbox { visibility: collapse; } +/*Formatting for the limited horizontal space of snapped*/ +#start-autocomplete[viewstate="snapped"] .richgrid-item-content { + -moz-box-orient: horizontal; +} + +#start-autocomplete[viewstate="snapped"] { + padding-left: 0px; + padding-right: 0px; +} + +#start-container[viewstate="snapped"] { + padding-left: 0px; + padding-right: 0px; +} + +#start-container[viewstate="snapped"] .meta-section { + margin: 0px; +} + /* Browser Content Areas ----------------------------------------------------- */ /* Hide the browser while the start UI is visible */ From a4a21f27beecc92be7f60ff12a9a823ee0627935 Mon Sep 17 00:00:00 2001 From: Jeff Hammel Date: Wed, 17 Apr 2013 11:08:02 -0700 Subject: [PATCH 17/75] Bug 860091 - mirror test.py and mozdevice and mozprofile to m-c;r=jgriffin --HG-- extra : rebase_source : 16b0b4bb8b49b0a70e0a2160c3ca6370737b0fab --- .../mozdevice/mozdevice/devicemanager.py | 39 +- .../mozdevice/mozdevice/devicemanagerADB.py | 23 +- .../mozdevice/mozdevice/devicemanagerSUT.py | 108 +++--- testing/mozbase/mozdevice/mozdevice/dmcli.py | 342 +++++++++--------- testing/mozbase/mozdevice/mozdevice/droid.py | 12 +- testing/mozbase/mozdevice/setup.py | 4 +- .../mozdevice/tests/droidsut_launch.py | 35 ++ testing/mozbase/mozdevice/tests/manifest.ini | 4 + testing/mozbase/mozdevice/tests/sut_basic.py | 13 +- testing/mozbase/mozdevice/tests/sut_mkdir.py | 9 +- testing/mozbase/mozdevice/tests/sut_pull.py | 8 +- testing/mozbase/mozdevice/tests/sut_push.py | 5 +- .../mozbase/mozprofile/mozprofile/addons.py | 14 +- .../mozprofile/mozprofile/permissions.py | 61 ++-- .../mozbase/mozprofile/mozprofile/prefs.py | 24 +- .../mozbase/mozprofile/mozprofile/profile.py | 21 +- testing/mozbase/mozprofile/setup.py | 2 +- testing/mozbase/mozprofile/tests/addonid.py | 15 +- .../tests/{ => addons}/empty/install.rdf | 2 +- testing/mozbase/mozprofile/tests/bug758250.py | 4 +- testing/mozbase/mozprofile/tests/bug785146.py | 4 +- .../mozbase/mozprofile/tests/permissions.py | 77 +++- .../mozprofile/tests/server_locations.py | 2 +- testing/mozbase/test.py | 23 +- 24 files changed, 517 insertions(+), 334 deletions(-) create mode 100644 testing/mozbase/mozdevice/tests/droidsut_launch.py rename testing/mozbase/mozprofile/tests/{ => addons}/empty/install.rdf (97%) diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanager.py b/testing/mozbase/mozdevice/mozdevice/devicemanager.py index 4231dd673c9a..4aad2c3fb70b 100644 --- a/testing/mozbase/mozdevice/mozdevice/devicemanager.py +++ b/testing/mozbase/mozdevice/mozdevice/devicemanager.py @@ -3,6 +3,7 @@ # You can obtain one at http://mozilla.org/MPL/2.0/. import hashlib +import mozlog import socket import os import re @@ -46,6 +47,35 @@ class DeviceManager(object): _logcatNeedsRoot = True + def __init__(self, logLevel=mozlog.ERROR): + self._logger = mozlog.getLogger("DeviceManager") + self._logLevel = logLevel + self._logger.setLevel(logLevel) + + @property + def logLevel(self): + return self._logLevel + + @logLevel.setter + def logLevel_setter(self, newLogLevel): + self._logLevel = newLogLevel + self._logger.setLevel(self._logLevel) + + @property + def debug(self): + self._logger.warn("dm.debug is deprecated. Use logLevel.") + levels = {mozlog.DEBUG: 5, mozlog.INFO: 3, mozlog.WARNING: 2, + mozlog.ERROR: 1, mozlog.CRITICAL: 0} + return levels[self.logLevel] + + @debug.setter + def debug_setter(self, newDebug): + self._logger.warn("dm.debug is deprecated. Use logLevel.") + newDebug = 5 if newDebug > 5 else newDebug # truncate >=5 to 5 + levels = {5: mozlog.DEBUG, 3: mozlog.INFO, 2: mozlog.WARNING, + 1: mozlog.ERROR, 0: mozlog.CRITICAL} + self.logLevel = levels[newDebug] + @abstractmethod def getInfo(self, directive=None): """ @@ -178,8 +208,7 @@ class DeviceManager(object): Returns True if remoteDirname on device is same as localDirname on host. """ - if (self.debug >= 2): - print "validating directory: " + localDirname + " to " + remoteDirname + self._logger.info("validating directory: %s to %s" % (localDirname, remoteDirname)) for root, dirs, files in os.walk(localDirname): parts = root.split(localDirname) for f in files: @@ -566,11 +595,11 @@ class NetworkTools: break except: if seed > maxportnum: - print "Automation Error: Could not find open port after checking 5000 ports" + self._logger.error("Automation Error: Could not find open port after checking 5000 ports") raise seed += 1 except: - print "Automation Error: Socket error trying to find open port" + self._logger.error("Automation Error: Socket error trying to find open port") return seed @@ -629,7 +658,7 @@ class ZeroconfListener(object): return ip = m.group(1).replace("_", ".") - + if self.hwid == hwid: self.ip = ip self.evt.set() diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py index c870d03a607b..f1821e93d76d 100644 --- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py +++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py @@ -2,6 +2,7 @@ # 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/. +import mozlog import subprocess from devicemanager import DeviceManager, DMError, _pop_last_line import re @@ -29,7 +30,9 @@ class DeviceManagerADB(DeviceManager): default_timeout = 300 def __init__(self, host=None, port=5555, retryLimit=5, packageName='fennec', - adbPath='adb', deviceSerial=None, deviceRoot=None, **kwargs): + adbPath='adb', deviceSerial=None, deviceRoot=None, + logLevel=mozlog.ERROR, **kwargs): + DeviceManager.__init__(self, logLevel) self.host = host self.port = port self.retryLimit = retryLimit @@ -206,7 +209,7 @@ class DeviceManagerADB(DeviceManager): if re.search("unzip: exiting", data) or re.search("Operation not permitted", data): raise Exception("unzip failed, or permissions error") except: - print "zip/unzip failure: falling back to normal push" + self._logger.info("zip/unzip failure: falling back to normal push") self._useZip = False self.pushDir(localDir, remoteDir, retryLimit=retryLimit) else: @@ -335,7 +338,7 @@ class DeviceManagerADB(DeviceManager): if uri != "": acmd.append("-d") acmd.append(''.join(['\'',uri, '\''])); - print acmd + self._logger.info(acmd) self._checkCmd(acmd) return outputFile @@ -429,7 +432,7 @@ class DeviceManagerADB(DeviceManager): try: self.mkDir(self.deviceRoot) except: - print "Unable to create device root %s" % self.deviceRoot + self._logger.error("Unable to create device root %s" % self.deviceRoot) raise return @@ -512,7 +515,7 @@ class DeviceManagerADB(DeviceManager): ret["process"] = self._runCmd(["shell", "ps"]).stdout.read() if (directive == "systime" or directive == "all"): ret["systime"] = self._runCmd(["shell", "date"]).stdout.read() - print ret + self._logger.info(ret) return ret def uninstallApp(self, appName, installPath=None): @@ -619,12 +622,12 @@ class DeviceManagerADB(DeviceManager): self.chmodDir(remoteEntry) else: self._checkCmdAs(["shell", "chmod", mask, remoteEntry]) - print "chmod " + remoteEntry + self._logger.info("chmod %s" % remoteEntry) self._checkCmdAs(["shell", "chmod", mask, remoteDir]) - print "chmod " + remoteDir + self._logger.info("chmod %s" % remoteDir) else: self._checkCmdAs(["shell", "chmod", mask, remoteDir.strip()]) - print "chmod " + remoteDir.strip() + self._logger.info("chmod %s" % remoteDir.strip()) def _verifyADB(self): """ @@ -690,7 +693,7 @@ class DeviceManagerADB(DeviceManager): self._checkCmd(["push", tmpfile.name, tmpDir + "/tmpfile"]) self._checkCmd(["shell", "run-as", self._packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"]) if (self.fileExists(devroot + "/sanity/tmpfile")): - print "will execute commands via run-as " + self._packageName + self._logger.info("will execute commands via run-as %s" % self._packageName) self._useRunAs = True self._checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"]) self._checkCmd(["shell", "run-as", self._packageName, "rm", "-r", devroot + "/sanity"]) @@ -743,7 +746,7 @@ class DeviceManagerADB(DeviceManager): # optimization for large directories. self._useZip = False if (self._isUnzipAvailable() and self._isLocalZipAvailable()): - print "will use zip to push directories" + self._logger.info("will use zip to push directories") self._useZip = True else: raise DMError("zip not available") diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py b/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py index e2cc8d4326c9..a683c70767fc 100644 --- a/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py +++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py @@ -2,6 +2,7 @@ # 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/. +import mozlog import select import socket import time @@ -22,7 +23,6 @@ class DeviceManagerSUT(DeviceManager): app must be present and listening for connections for this to work. """ - debug = 2 _base_prompt = '$>' _base_prompt_re = '\$\>' _prompt_sep = '\x00' @@ -33,7 +33,9 @@ class DeviceManagerSUT(DeviceManager): reboot_timeout = 600 reboot_settling_time = 60 - def __init__(self, host, port = 20701, retryLimit = 5, deviceRoot = None, **kwargs): + def __init__(self, host, port = 20701, retryLimit = 5, + deviceRoot = None, logLevel = mozlog.ERROR, **kwargs): + DeviceManager.__init__(self, logLevel) self.host = host self.port = port self.retryLimit = retryLimit @@ -130,13 +132,12 @@ class DeviceManagerSUT(DeviceManager): # couldn't execute it). retry otherwise if err.fatal: raise err - if self.debug >= 4: - print err + self._logger.debug(err) retries += 1 # if we lost the connection or failed to establish one, wait a bit if retries < retryLimit and not self._sock: sleep_time = 5 * retries - print 'Could not connect; sleeping for %d seconds.' % sleep_time + self._logger.info('Could not connect; sleeping for %d seconds.' % sleep_time) time.sleep(sleep_time) raise DMError("Remote Device Error: unable to connect to %s after %s attempts" % (self.host, retryLimit)) @@ -162,8 +163,8 @@ class DeviceManagerSUT(DeviceManager): if not self._sock: try: - if self.debug >= 1 and self._everConnected: - print "reconnecting socket" + if self._everConnected: + self._logger.info("reconnecting socket") self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error, msg: self._sock = None @@ -202,13 +203,12 @@ class DeviceManagerSUT(DeviceManager): raise DMError("Remote Device Error: we had %s bytes of data to send, but " "only sent %s" % (len(cmd['data']), sent)) - if self.debug >= 4: - print "sent cmd: " + str(cmd['cmd']) + self._logger.debug("sent cmd: %s" % cmd['cmd']) except socket.error, msg: self._sock.close() self._sock = None - if self.debug >= 1: - print "Remote Device Error: Error sending data to socket. cmd="+str(cmd['cmd'])+"; err="+str(msg) + self._logger.error("Remote Device Error: Error sending data"\ + " to socket. cmd=%s; err=%s" % (cmd['cmd'], msg)) return False # Check if the command should close the socket @@ -226,16 +226,14 @@ class DeviceManagerSUT(DeviceManager): socketClosed = False errStr = '' temp = '' - if self.debug >= 4: - print "recv'ing..." + self._logger.debug("recv'ing...") # Get our response try: # Wait up to a second for socket to become ready for reading... if select.select([self._sock], [], [], select_timeout)[0]: temp = self._sock.recv(1024) - if self.debug >= 4: - print "response: " + str(temp) + self._logger.debug("response: %s" % temp) timer = 0 if not temp: socketClosed = True @@ -352,8 +350,7 @@ class DeviceManagerSUT(DeviceManager): except OSError: raise DMError("DeviceManager: Error reading file to push") - if (self.debug >= 3): - print "push returned: %s" % remoteHash + self._logger.debug("push returned: %s" % remoteHash) localHash = self._getLocalHash(localname) @@ -367,8 +364,7 @@ class DeviceManagerSUT(DeviceManager): def pushDir(self, localDir, remoteDir, retryLimit = None): retryLimit = retryLimit or self.retryLimit - if (self.debug >= 2): - print "pushing directory: %s to %s" % (localDir, remoteDir) + self._logger.info("pushing directory: %s to %s" % (localDir, remoteDir)) existentDirectories = [] for root, dirs, files in os.walk(localDir, followlinks=True): @@ -418,8 +414,7 @@ class DeviceManagerSUT(DeviceManager): return files def removeFile(self, filename): - if (self.debug>= 2): - print "removing file: " + filename + self._logger.info("removing file: " + filename) if self.fileExists(filename): self._runCmds([{ 'cmd': 'rm ' + filename }]) @@ -444,8 +439,8 @@ class DeviceManagerSUT(DeviceManager): # unexpected format raise ValueError except ValueError: - print "ERROR: Unable to parse process list (bug 805969)" - print "Line: %s\nFull output of process list:\n%s" % (line, data) + self._logger.error("Unable to parse process list (bug 805969)") + self._logger.error("Line: %s\nFull output of process list:\n%s" % (line, data)) raise DMError("Invalid process line: %s" % line) return processTuples @@ -461,11 +456,10 @@ class DeviceManagerSUT(DeviceManager): if not appname: raise DMError("Automation Error: fireProcess called with no command to run") - if (self.debug >= 2): - print "FIRE PROC: '" + appname + "'" + self._logger.info("FIRE PROC: '%s'" % appname) if (self.processExist(appname) != None): - print "WARNING: process %s appears to be running already\n" % appname + self._logger.warn("process %s appears to be running already\n" % appname) if (failIfRunning): raise DMError("Automation Error: Process is already running") @@ -484,8 +478,7 @@ class DeviceManagerSUT(DeviceManager): time.sleep(1) waited += 1 - if (self.debug >= 4): - print "got pid: %s for process: %s" % (pid, appname) + self._logger.debug("got pid: %s for process: %s" % (pid, appname)) return pid def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False): @@ -500,8 +493,7 @@ class DeviceManagerSUT(DeviceManager): DEPRECATED: Use shell() or launchApplication() for new code """ if not cmd: - if (self.debug >= 1): - print "WARNING: launchProcess called without command to run" + self._logger.warn("launchProcess called without command to run") return None cmdline = subprocess.list2cmdline(cmd) @@ -521,7 +513,7 @@ class DeviceManagerSUT(DeviceManager): def killProcess(self, appname, forceKill=False): if forceKill: - print "WARNING: killProcess(): forceKill parameter unsupported on SUT" + self._logger.warn("killProcess(): forceKill parameter unsupported on SUT") retries = 0 while retries < self.retryLimit: try: @@ -530,10 +522,9 @@ class DeviceManagerSUT(DeviceManager): return except DMError, err: retries +=1 - print ("WARNING: try %d of %d failed to kill %s" % + self._logger.warn("try %d of %d failed to kill %s" % (retries, self.retryLimit, appname)) - if self.debug >= 4: - print err + self._logger.debug(err) if retries >= self.retryLimit: raise err @@ -549,7 +540,7 @@ class DeviceManagerSUT(DeviceManager): def err(error_msg): err_str = 'DeviceManager: pull unsuccessful: %s' % error_msg - print err_str + self._logger.error(err_str) self._sock = None raise DMError(err_str) @@ -608,8 +599,7 @@ class DeviceManagerSUT(DeviceManager): metadata, sep, buf = read_until_char('\n', buf, 'could not find metadata') if not metadata: return None - if self.debug >= 3: - print 'metadata: %s' % metadata + self._logger.debug('metadata: %s' % metadata) filename, sep, filesizestr = metadata.partition(',') if sep == '': @@ -648,15 +638,13 @@ class DeviceManagerSUT(DeviceManager): remoteFile) def getDirectory(self, remoteDir, localDir, checkDir=True): - if (self.debug >= 2): - print "getting files in '" + remoteDir + "'" + self._logger.info("getting files in '%s'" % remoteDir) if checkDir and not self.dirExists(remoteDir): raise DMError("Automation Error: Error getting directory: %s not a directory" % remoteDir) filelist = self.listFiles(remoteDir) - if (self.debug >= 3): - print filelist + self._logger.debug(filelist) if not os.path.exists(localDir): os.makedirs(localDir) @@ -684,8 +672,7 @@ class DeviceManagerSUT(DeviceManager): def _getRemoteHash(self, filename): data = self._runCmds([{ 'cmd': 'hash ' + filename }]).strip() - if self.debug >= 3: - print "remote hash returned: '%s'" % data + self._logger.debug("remote hash returned: '%s'" % data) return data def getDeviceRoot(self): @@ -723,8 +710,7 @@ class DeviceManagerSUT(DeviceManager): self._runCmds([{ 'cmd': 'unzp %s %s' % (filePath, destDir)}]) def _wait_for_reboot(self, host, port): - if self.debug >= 3: - print 'Creating server with %s:%d' % (host, port) + self._logger.debug('Creating server with %s:%d' % (host, port)) timeout_expires = time.time() + self.reboot_timeout conn = None data = '' @@ -752,15 +738,14 @@ class DeviceManagerSUT(DeviceManager): # also up. time.sleep(self.reboot_settling_time) else: - print 'Automation Error: Timed out waiting for reboot callback.' + self._logger.error('Timed out waiting for reboot callback.') s.close() return data def reboot(self, ipAddr=None, port=30000): cmd = 'rebt' - if self.debug > 3: - print "INFO: sending rebt command" + self._logger.info("sending rebt command") if ipAddr is not None: # The update.info command tells the SUTAgent to send a TCP message @@ -778,8 +763,7 @@ class DeviceManagerSUT(DeviceManager): if ipAddr is not None: status = self._wait_for_reboot(ipAddr, port) - if self.debug > 3: - print "INFO: rebt- got status back: " + str(status) + self._logger.info("rebt- got status back: %s" % status) def getInfo(self, directive=None): data = None @@ -810,8 +794,7 @@ class DeviceManagerSUT(DeviceManager): proclist.append(l.split('\t')) result['process'] = proclist - if (self.debug >= 3): - print "results: " + str(result) + self._logger.debug("results: %s" % result) return result def installApp(self, appBundlePath, destPath=None): @@ -833,8 +816,7 @@ class DeviceManagerSUT(DeviceManager): data = self._runCmds([{ 'cmd': cmd }]) status = data.split('\n')[0].strip() - if self.debug > 3: - print "uninstallApp: '%s'" % status + self._logger.debug("uninstallApp: '%s'" % status) if status == 'Success': return raise DMError("Remote Device Error: uninstall failed for %s" % appName) @@ -845,8 +827,7 @@ class DeviceManagerSUT(DeviceManager): cmd += ' ' + installPath data = self._runCmds([{ 'cmd': cmd }]) - if (self.debug > 3): - print "uninstallAppAndReboot: " + str(data) + self._logger.debug("uninstallAppAndReboot: %s" % data) return def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=30000): @@ -865,16 +846,14 @@ class DeviceManagerSUT(DeviceManager): ip, port = self._getCallbackIpAndPort(ipAddr, port) cmd += " %s %s" % (ip, port) - if self.debug >= 3: - print "INFO: updateApp using command: " + str(cmd) + self._logger.debug("updateApp using command: " % cmd) status = self._runCmds([{'cmd': cmd}]) if ipAddr is not None: status = self._wait_for_reboot(ip, port) - if self.debug >= 3: - print "INFO: updateApp: got status back: %s" + str(status) + self._logger.debug("updateApp: got status back: %s" % status) def getCurrentTime(self): return self._runCmds([{ 'cmd': 'clok' }]).strip() @@ -922,14 +901,12 @@ class DeviceManagerSUT(DeviceManager): supported resolutions: 640x480, 800x600, 1024x768, 1152x864, 1200x1024, 1440x900, 1680x1050, 1920x1080 """ if self.getInfo('os')['os'][0].split()[0] != 'harmony-eng': - if (self.debug >= 2): - print "WARNING: unable to adjust screen resolution on non Tegra device" + self._logger.warn("unable to adjust screen resolution on non Tegra device") return False results = self.getInfo('screen') parts = results['screen'][0].split(':') - if (self.debug >= 3): - print "INFO: we have a current resolution of %s, %s" % (parts[1].split()[0], parts[2].split()[0]) + self._logger.debug("we have a current resolution of %s, %s" % (parts[1].split()[0], parts[2].split()[0])) #verify screen type is valid, and set it to the proper value (https://bugzilla.mozilla.org/show_bug.cgi?id=632895#c4) screentype = -1 @@ -950,8 +927,7 @@ class DeviceManagerSUT(DeviceManager): if (height < 100 or height > 9999): return False - if (self.debug >= 3): - print "INFO: adjusting screen resolution to %s, %s and rebooting" % (width, height) + self._logger.debug("adjusting screen resolution to %s, %s and rebooting" % (width, height)) self._runCmds([{ 'cmd': "exec setprop persist.tegra.dpy%s.mode.width %s" % (screentype, width) }]) self._runCmds([{ 'cmd': "exec setprop persist.tegra.dpy%s.mode.height %s" % (screentype, height) }]) diff --git a/testing/mozbase/mozdevice/mozdevice/dmcli.py b/testing/mozbase/mozdevice/mozdevice/dmcli.py index 980ef36fbee6..ada6e418e647 100644 --- a/testing/mozbase/mozdevice/mozdevice/dmcli.py +++ b/testing/mozbase/mozdevice/mozdevice/dmcli.py @@ -11,211 +11,191 @@ import os import posixpath import StringIO import sys -import textwrap import mozdevice -from optparse import OptionParser +import mozlog +import argparse class DMCli(object): def __init__(self): - # a value of None for 'max_args' means there is no limit to the number - # of arguments. 'min_args' should always have an integer value >= 0. self.commands = { 'install': { 'function': self.install, - 'min_args': 1, - 'max_args': 1, - 'help_args': '', + 'args': [ { 'name': 'file', 'nargs': None } ], 'help': 'push this package file to the device and install it' }, - 'uninstall': { 'function': lambda a: self.dm.uninstallApp(a), - 'min_args': 1, - 'max_args': 1, - 'help_args': '', + 'uninstall': { 'function': self.uninstall, + 'args': [ { 'name': 'packagename', 'nargs': None } ], 'help': 'uninstall the named app from the device' }, - 'killapp': { 'function': self.killapp, - 'min_args': 1, - 'max_args': 1, - 'help_args': '', - 'help': 'kills any processes with a particular name on device' }, + 'killapp': { 'function': self.kill, + 'args': [ { 'name': 'process_name', 'nargs': '*' } ], + 'help': 'kills any processes with name(s) on device' }, 'launchapp': { 'function': self.launchapp, - 'min_args': 4, - 'max_args': 4, - 'help_args': ' ', - 'help': 'launches application on device' }, + 'args': [ { 'name': 'appname', 'nargs': None }, + { 'name': 'activity_name', + 'nargs': None }, + { 'name': '--intent', + 'action': 'store', + 'default': 'android.intent.action.VIEW' }, + { 'name': '--url', + 'action': 'store' } + ], + 'help': 'launches application on device' }, 'push': { 'function': self.push, - 'min_args': 2, - 'max_args': 2, - 'help_args': ' ', + 'args': [ { 'name': 'local_file', 'nargs': None }, + { 'name': 'remote_file', 'nargs': None } + ], 'help': 'copy file/dir to device' }, 'pull': { 'function': self.pull, - 'min_args': 1, - 'max_args': 2, - 'help_args': ' [remote]', + 'args': [ { 'name': 'local_file', 'nargs': None }, + { 'name': 'remote_file', 'nargs': '?' } ], 'help': 'copy file/dir from device' }, 'shell': { 'function': self.shell, - 'min_args': 1, - 'max_args': None, - 'help_args': '', - 'help': 'run shell command on device' }, + 'args': [ { 'name': 'command', 'nargs': argparse.REMAINDER } ], + 'help': 'run shell command on device' }, 'info': { 'function': self.getinfo, - 'min_args': 0, - 'max_args': 1, - 'help_args': '[os|id|uptime|systime|screen|memory|processes]', - 'help': 'get information on a specified ' + 'args': [ { 'name': 'directive', 'nargs': '?' } ], + 'help': 'get information on specified ' 'aspect of the device (if no argument ' 'given, print all available information)' }, 'ps': { 'function': self.processlist, - 'min_args': 0, - 'max_args': 0, - 'help_args': '', - 'help': 'get information on running processes on device' + 'help': 'get information on running processes on device' }, 'logcat' : { 'function': self.logcat, - 'min_args': 0, - 'max_args': 0, - 'help_args': '', 'help': 'get logcat from device' }, 'ls': { 'function': self.listfiles, - 'min_args': 1, - 'max_args': 1, - 'help_args': '', + 'args': [ { 'name': 'remote_dir', 'nargs': None } ], 'help': 'list files on device' }, - 'rm': { 'function': lambda f: self.dm.removeFile(f), - 'min_args': 1, - 'max_args': 1, - 'help_args': '', - 'help': 'remove file from device' + 'rm': { 'function': self.removefile, + 'args': [ { 'name': 'remote_file', 'nargs': None } ], + 'help': 'remove file from device' }, 'isdir': { 'function': self.isdir, - 'min_args': 1, - 'max_args': 1, - 'help_args': '', + 'args': [ { 'name': 'remote_dir', 'nargs': None } ], 'help': 'print if remote file is a directory' }, - 'mkdir': { 'function': lambda d: self.dm.mkDir(d), - 'min_args': 1, - 'max_args': 1, - 'help_args': '', + 'mkdir': { 'function': self.mkdir, + 'args': [ { 'name': 'remote_dir', 'nargs': None } ], 'help': 'makes a directory on device' }, - 'rmdir': { 'function': lambda d: self.dm.removeDir(d), - 'min_args': 1, - 'max_args': 1, - 'help_args': '', - 'help': 'recursively remove directory from device' + 'rmdir': { 'function': self.rmdir, + 'args': [ { 'name': 'remote_dir', 'nargs': None } ], + 'help': 'recursively remove directory from device' }, - 'screencap': { 'function': lambda f: self.dm.saveScreenshot(f), - 'min_args': 1, - 'max_args': 1, - 'help_args': '', - 'help': 'capture screenshot of device in action' - }, + 'screencap': { 'function': self.screencap, + 'args': [ { 'name': 'png_file', 'nargs': None } ], + 'help': 'capture screenshot of device in action' + }, 'sutver': { 'function': self.sutver, - 'min_args': 0, - 'max_args': 0, - 'help_args': '', 'help': 'SUTAgent\'s product name and version (SUT only)' }, - + 'clearlogcat': { 'function': self.clearlogcat, + 'help': 'clear the logcat' + }, + 'reboot': { 'function': self.reboot, + 'help': 'reboot the device' + }, + 'isfile': { 'function': self.isfile, + 'args': [ { 'name': 'remote_file', 'nargs': None } ], + 'help': 'check whether a file exists on the device' + }, + 'launchfennec': { 'function': self.launchfennec, + 'args': [ { 'name': 'appname', 'nargs': None }, + { 'name': '--intent', 'action': 'store', + 'default': 'android.intent.action.VIEW' }, + { 'name': '--url', 'action': 'store' }, + { 'name': '--extra-args', 'action': 'store' }, + { 'name': '--mozenv', 'action': 'store' } ], + 'help': 'launch fennec' + }, + 'getip': { 'function': self.getip, + 'args': [ { 'name': 'interface', 'nargs': '*' } ], + 'help': 'get the ip address of the device' + } } - usage = "usage: %prog [options] []\n\ndevice commands:\n" - usage += "\n".join([textwrap.fill("%s %s - %s" % - (cmdname, cmd['help_args'], - cmd['help']), - initial_indent=" ", - subsequent_indent=" ") - for (cmdname, cmd) in - sorted(self.commands.iteritems())]) - - self.parser = OptionParser(usage) + self.parser = argparse.ArgumentParser() self.add_options(self.parser) - + self.add_commands(self.parser) def run(self, args=sys.argv[1:]): - (self.options, self.args) = self.parser.parse_args(args) + args = self.parser.parse_args() - if len(self.args) < 1: - self.parser.error("must specify command") - - if self.options.dmtype == "sut" and not self.options.host and \ - not self.options.hwid: + if args.dmtype == "sut" and not args.host and not args.hwid: self.parser.error("Must specify device ip in TEST_DEVICE or " "with --host option with SUT") - (command_name, command_args) = (self.args[0], self.args[1:]) - if command_name not in self.commands: - self.parser.error("Invalid command. Valid commands: %s" % - " ".join(self.commands.keys())) + self.dm = self.getDevice(dmtype=args.dmtype, hwid=args.hwid, + host=args.host, port=args.port, + verbose=args.verbose) - command = self.commands[command_name] - if (len(command_args) < command['min_args'] or - (command['max_args'] is not None and len(command_args) > - command['max_args'])): - self.parser.error("Wrong number of arguments") - - self.dm = self.getDevice(dmtype=self.options.dmtype, - hwid=self.options.hwid, - host=self.options.host, - port=self.options.port) - ret = command['function'](*command_args) + ret = args.func(args) if ret is None: ret = 0 sys.exit(ret) def add_options(self, parser): - parser.add_option("-v", "--verbose", action="store_true", - dest="verbose", - help="Verbose output from DeviceManager", - default=False) - parser.add_option("--host", action="store", - type="string", dest="host", - help="Device hostname (only if using TCP/IP)", - default=os.environ.get('TEST_DEVICE')) - parser.add_option("-p", "--port", action="store", - type="int", dest="port", - help="Custom device port (if using SUTAgent or " - "adb-over-tcp)", default=None) - parser.add_option("-m", "--dmtype", action="store", - type="string", dest="dmtype", - help="DeviceManager type (adb or sut, defaults " \ - "to adb)", default=os.environ.get('DM_TRANS', - 'adb')) - parser.add_option("-d", "--hwid", action="store", - type="string", dest="hwid", - help="HWID", default=None) - parser.add_option("--package-name", action="store", - type="string", dest="packagename", - help="Packagename (if using DeviceManagerADB)", - default=None) + parser.add_argument("-v", "--verbose", action="store_true", + help="Verbose output from DeviceManager", + default=False) + parser.add_argument("--host", action="store", + help="Device hostname (only if using TCP/IP)", + default=os.environ.get('TEST_DEVICE')) + parser.add_argument("-p", "--port", action="store", + type=int, + help="Custom device port (if using SUTAgent or " + "adb-over-tcp)", default=None) + parser.add_argument("-m", "--dmtype", action="store", + help="DeviceManager type (adb or sut, defaults " \ + "to adb)", default=os.environ.get('DM_TRANS', + 'adb')) + parser.add_argument("-d", "--hwid", action="store", + help="HWID", default=None) + parser.add_argument("--package-name", action="store", + help="Packagename (if using DeviceManagerADB)", + default=None) - def getDevice(self, dmtype="adb", hwid=None, host=None, port=None): + def add_commands(self, parser): + subparsers = parser.add_subparsers(title="Commands", metavar="") + for (commandname, commandprops) in sorted(self.commands.iteritems()): + subparser = subparsers.add_parser(commandname, help=commandprops['help']) + if commandprops.get('args'): + for arg in commandprops['args']: + subparser.add_argument(arg['name'], nargs=arg.get('nargs'), + action=arg.get('action')) + subparser.set_defaults(func=commandprops['function']) + + def getDevice(self, dmtype="adb", hwid=None, host=None, port=None, + packagename=None, verbose=False): ''' Returns a device with the specified parameters ''' - if self.options.verbose: - mozdevice.DroidSUT.debug = 4 + logLevel = mozlog.ERROR + if verbose: + logLevel = mozlog.DEBUG if hwid: - return mozdevice.DroidConnectByHWID(hwid) + return mozdevice.DroidConnectByHWID(hwid, logLevel=logLevel) if dmtype == "adb": if host and not port: port = 5555 - return mozdevice.DroidADB(packageName=self.options.packagename, - host=host, port=port) + return mozdevice.DroidADB(packageName=packagename, + host=host, port=port, + logLevel=logLevel) elif dmtype == "sut": if not host: self.parser.error("Must specify host with SUT!") if not port: port = 20701 - return mozdevice.DroidSUT(host=host, port=port) + return mozdevice.DroidSUT(host=host, port=port, + logLevel=logLevel) else: self.parser.error("Unknown device manager type: %s" % type) - def push(self, src, dest): + def push(self, args): + (src, dest) = (args.local_file, args.remote_file) if os.path.isdir(src): self.dm.pushDir(src, dest) else: @@ -225,7 +205,8 @@ class DMCli(object): dest = posixpath.join(dest, os.path.basename(src)) self.dm.pushFile(src, dest) - def pull(self, src, dest=None): + def pull(self, args): + (src, dest) = (args.local_file, args.remote_file) if not self.dm.fileExists(src): print 'No such file or directory' return @@ -236,69 +217,106 @@ class DMCli(object): else: self.dm.getFile(src, dest) - def install(self, apkfile): - basename = os.path.basename(apkfile) + def install(self, args): + basename = os.path.basename(args.file) app_path_on_device = posixpath.join(self.dm.getDeviceRoot(), basename) - self.dm.pushFile(apkfile, app_path_on_device) + self.dm.pushFile(args.file, app_path_on_device) self.dm.installApp(app_path_on_device) - def launchapp(self, appname, activity, intent, url): - self.dm.launchApplication(appname, activity, intent, url) + def uninstall(self, args): + self.dm.uninstallApp(args.packagename) - def killapp(self, *args): - for appname in args: - self.dm.killProcess(appname) + def launchapp(self, args): + self.dm.launchApplication(args.appname, args.activity_name, + args.intent, args.url) - def shell(self, *args): + def kill(self, args): + for name in args.process_name: + self.dm.killProcess(name) + + def shell(self, args, root=False): buf = StringIO.StringIO() - self.dm.shell(args, buf) + self.dm.shell(args.command, buf, root=root) print str(buf.getvalue()[0:-1]).rstrip() - def getinfo(self, *args): - directive=None - if args: - directive=args[0] - info = self.dm.getInfo(directive=directive) + def getinfo(self, args): + info = self.dm.getInfo(directive=args.directive) for (infokey, infoitem) in sorted(info.iteritems()): if infokey == "process": pass # skip process list: get that through ps - elif not directive and not infoitem: + elif not args.directive and not infoitem: print "%s:" % infokey.upper() - elif not directive: + elif not args.directive: for line in infoitem: print "%s: %s" % (infokey.upper(), line) else: print "%s" % "\n".join(infoitem) - def logcat(self): + def logcat(self, args): print ''.join(self.dm.getLogcat()) - def processlist(self): + def clearlogcat(self, args): + self.dm.recordLogcat() + + def reboot(self, args): + self.dm.reboot() + + def processlist(self, args): pslist = self.dm.getProcessList() for ps in pslist: print " ".join(str(i) for i in ps) - def listfiles(self, dir): - filelist = self.dm.listFiles(dir) + def listfiles(self, args): + filelist = self.dm.listFiles(args.remote_dir) for file in filelist: print file - def isdir(self, file): - if self.dm.dirExists(file): + def removefile(self, args): + self.dm.removeFile(args.remote_file) + + def isdir(self, args): + if self.dm.dirExists(args.remote_dir): print "TRUE" - return 0 + return print "FALSE" return errno.ENOTDIR - def sutver(self): - if self.options.dmtype == 'sut': + def mkdir(self, args): + self.dm.mkDir(args.remote_dir) + + def rmdir(self, args): + self.dm.removeDir(args.remote_dir) + + def screencap(self, args): + self.dm.saveScreenshot(args.png_file) + + def sutver(self, args): + if args.dmtype == 'sut': print '%s Version %s' % (self.dm.agentProductName, self.dm.agentVersion) else: print 'Must use SUT transport to get SUT version.' + def isfile(self, args): + if self.dm.fileExists(args.remote_file): + print "TRUE" + return + print "FALSE" + return errno.ENOENT + + def launchfennec(self, args): + self.dm.launchFennec(args.appname, intent=args.intent, + mozEnv=args.mozenv, + extraArgs=args.extra_args, url=args.url) + + def getip(self, args): + if args.interface: + print(self.dm.getIP(args.interface)) + else: + print(self.dm.getIP()) + def cli(args=sys.argv[1:]): # process the command line cli = DMCli() diff --git a/testing/mozbase/mozdevice/mozdevice/droid.py b/testing/mozbase/mozdevice/mozdevice/droid.py index 4dee62f7c56c..a1b86c23b003 100644 --- a/testing/mozbase/mozdevice/mozdevice/droid.py +++ b/testing/mozbase/mozdevice/mozdevice/droid.py @@ -100,7 +100,7 @@ class DroidSUT(DeviceManagerSUT, DroidMixin): # a different process than the one that started the app. In this case, # we need to get back the original user serial number and then pass # that to the 'am start' command line - if not hasattr(self, 'userSerial'): + if not hasattr(self, '_userSerial'): infoDict = self.getInfo(directive="sutuserinfo") if infoDict.get('sutuserinfo') and \ len(infoDict['sutuserinfo']) > 0: @@ -108,12 +108,14 @@ class DroidSUT(DeviceManagerSUT, DroidMixin): # user serial always an integer, see: http://developer.android.com/reference/android/os/UserManager.html#getSerialNumberForUser%28android.os.UserHandle%29 m = re.match('User Serial:([0-9]+)', userSerialString) if m: - self.userSerial = m.group(1) + self._userSerial = m.group(1) else: - self.userSerial = None + self._userSerial = None + else: + self._userSerial = None - if self.userSerial is not None: - return [ "--user", self.userSerial ] + if self._userSerial is not None: + return [ "--user", self._userSerial ] return [] diff --git a/testing/mozbase/mozdevice/setup.py b/testing/mozbase/mozdevice/setup.py index 6006d25e2005..4462b2ee0917 100644 --- a/testing/mozbase/mozdevice/setup.py +++ b/testing/mozbase/mozdevice/setup.py @@ -4,7 +4,7 @@ from setuptools import setup -PACKAGE_VERSION = '0.21' +PACKAGE_VERSION = '0.22' setup(name='mozdevice', version=PACKAGE_VERSION, @@ -19,7 +19,7 @@ setup(name='mozdevice', packages=['mozdevice'], include_package_data=True, zip_safe=False, - install_requires=[], + install_requires=['mozlog'], entry_points=""" # -*- Entry points: -*- [console_scripts] diff --git a/testing/mozbase/mozdevice/tests/droidsut_launch.py b/testing/mozbase/mozdevice/tests/droidsut_launch.py new file mode 100644 index 000000000000..522c2fafa570 --- /dev/null +++ b/testing/mozbase/mozdevice/tests/droidsut_launch.py @@ -0,0 +1,35 @@ +from sut import MockAgent +import mozdevice +import mozlog +import unittest + +class LaunchTest(unittest.TestCase): + + def test_nouserserial(self): + a = MockAgent(self, commands = [("ps", + "10029 549 com.android.launcher\n" + "10066 1198 com.twitter.android"), + ("info sutuserinfo", ""), + ("exec am start -W -n " + "org.mozilla.fennec/.App -a " + "android.intent.action.VIEW", + "OK\nreturn code [0]")]) + d = mozdevice.DroidSUT("127.0.0.1", port=a.port, logLevel=mozlog.DEBUG) + d.launchFennec("org.mozilla.fennec") + a.wait() + + def test_userserial(self): + a = MockAgent(self, commands = [("ps", + "10029 549 com.android.launcher\n" + "10066 1198 com.twitter.android"), + ("info sutuserinfo", "User Serial:0"), + ("exec am start --user 0 -W -n " + "org.mozilla.fennec/.App -a " + "android.intent.action.VIEW", + "OK\nreturn code [0]")]) + d = mozdevice.DroidSUT("127.0.0.1", port=a.port, logLevel=mozlog.DEBUG) + d.launchFennec("org.mozilla.fennec") + a.wait() + +if __name__ == '__main__': + unittest.main() diff --git a/testing/mozbase/mozdevice/tests/manifest.ini b/testing/mozbase/mozdevice/tests/manifest.ini index 69486ce8320b..d9682256b9cf 100644 --- a/testing/mozbase/mozdevice/tests/manifest.ini +++ b/testing/mozbase/mozdevice/tests/manifest.ini @@ -1,5 +1,9 @@ +[DEFAULT] +skip-if = os == 'win' + [sut_basic.py] [sut_mkdir.py] [sut_push.py] [sut_pull.py] [sut_ps.py] +[droidsut_launch.py] \ No newline at end of file diff --git a/testing/mozbase/mozdevice/tests/sut_basic.py b/testing/mozbase/mozdevice/tests/sut_basic.py index 3cec0af48ec7..5b03cd0a0b70 100644 --- a/testing/mozbase/mozdevice/tests/sut_basic.py +++ b/testing/mozbase/mozdevice/tests/sut_basic.py @@ -1,5 +1,6 @@ from sut import MockAgent import mozdevice +import mozlog import unittest class BasicTest(unittest.TestCase): @@ -8,8 +9,7 @@ class BasicTest(unittest.TestCase): """Tests DeviceManager initialization.""" a = MockAgent(self) - mozdevice.DroidSUT.debug = 4 - d = mozdevice.DroidSUT("127.0.0.1", port=a.port) + d = mozdevice.DroidSUT("127.0.0.1", port=a.port, logLevel=mozlog.DEBUG) # all testing done in device's constructor a.wait() @@ -21,8 +21,7 @@ class BasicTest(unittest.TestCase): ("mkdr /mnt/sdcard/tests", "/mnt/sdcard/tests successfully created"), ("ver", "SUTAgentAndroid Version 1.14")] a = MockAgent(self, start_commands = cmds) - mozdevice.DroidSUT.debug = 4 - dm = mozdevice.DroidSUT("127.0.0.1", port=a.port) + dm = mozdevice.DroidSUT("127.0.0.1", port=a.port, logLevel=mozlog.DEBUG) a.wait() def test_timeout_normal(self): @@ -32,8 +31,7 @@ class BasicTest(unittest.TestCase): ("ls", "test.txt"), ("rm /mnt/sdcard/tests/test.txt", "Removed the file")]) - mozdevice.DroidSUT.debug = 4 - d = mozdevice.DroidSUT("127.0.0.1", port=a.port) + d = mozdevice.DroidSUT("127.0.0.1", port=a.port, logLevel=mozlog.DEBUG) ret = d.removeFile('/mnt/sdcard/tests/test.txt') self.assertEqual(ret, None) # if we didn't throw an exception, we're ok a.wait() @@ -44,8 +42,7 @@ class BasicTest(unittest.TestCase): ("cd /mnt/sdcard/tests", ""), ("ls", "test.txt"), ("rm /mnt/sdcard/tests/test.txt", 0)]) - mozdevice.DroidSUT.debug = 4 - d = mozdevice.DroidSUT("127.0.0.1", port=a.port) + d = mozdevice.DroidSUT("127.0.0.1", port=a.port, logLevel=mozlog.DEBUG) d.default_timeout = 1 exceptionThrown = False try: diff --git a/testing/mozbase/mozdevice/tests/sut_mkdir.py b/testing/mozbase/mozdevice/tests/sut_mkdir.py index 188746a4390c..14cc658474f9 100644 --- a/testing/mozbase/mozdevice/tests/sut_mkdir.py +++ b/testing/mozbase/mozdevice/tests/sut_mkdir.py @@ -2,6 +2,7 @@ # http://creativecommons.org/publicdomain/zero/1.0/ import mozdevice +import mozlog import unittest from sut import MockAgent @@ -31,8 +32,8 @@ class MkDirsTest(unittest.TestCase): exceptionThrown = False try: - mozdevice.DroidSUT.debug = 4 - d = mozdevice.DroidSUT('127.0.0.1', port=a.port) + d = mozdevice.DroidSUT('127.0.0.1', port=a.port, + logLevel=mozlog.DEBUG) d.mkDirs('/mnt/sdcard/baz/boop/bip') except mozdevice.DMError: exceptionThrown = True @@ -53,8 +54,8 @@ class MkDirsTest(unittest.TestCase): ('mkdr /mnt/sdcard/foo', '/mnt/sdcard/foo successfully created')] a = MockAgent(self, commands=cmds) - mozdevice.DroidSUT.debug = 4 - d = mozdevice.DroidSUT('127.0.0.1', port=a.port) + d = mozdevice.DroidSUT('127.0.0.1', port=a.port, + logLevel=mozlog.DEBUG) d.mkDirs('/mnt/sdcard/foo/foo') a.wait() diff --git a/testing/mozbase/mozdevice/tests/sut_pull.py b/testing/mozbase/mozdevice/tests/sut_pull.py index 6e8e98f2df75..6eaeddfcd3ef 100644 --- a/testing/mozbase/mozdevice/tests/sut_pull.py +++ b/testing/mozbase/mozdevice/tests/sut_pull.py @@ -1,5 +1,6 @@ from sut import MockAgent import mozdevice +import mozlog import unittest import hashlib import tempfile @@ -15,14 +16,14 @@ class PullTest(unittest.TestCase): # pull file is kind of gross, make sure we can still execute commands after it's done remoteName = "/mnt/sdcard/cheeseburgers" - mozdevice.DroidSUT.debug = 4 a = MockAgent(self, commands = [("pull %s" % remoteName, "%s,%s\n%s" % (remoteName, len(cheeseburgers), cheeseburgers)), ("isdir /mnt/sdcard", "TRUE")]) - d = mozdevice.DroidSUT("127.0.0.1", port=a.port) + d = mozdevice.DroidSUT("127.0.0.1", port=a.port, + logLevel=mozlog.DEBUG) pulledData = d.pullFile("/mnt/sdcard/cheeseburgers") self.assertEqual(pulledData, cheeseburgers) d.dirExists('/mnt/sdcard') @@ -35,7 +36,8 @@ class PullTest(unittest.TestCase): a = MockAgent(self, commands = [("pull %s" % remoteName, "%s,15\n%s" % (remoteName, "cheeseburgh"))]) - d = mozdevice.DroidSUT("127.0.0.1", port=a.port) + d = mozdevice.DroidSUT("127.0.0.1", port=a.port, + logLevel=mozlog.DEBUG) exceptionThrown = False try: d.pullFile("/mnt/sdcard/cheeseburgers") diff --git a/testing/mozbase/mozdevice/tests/sut_push.py b/testing/mozbase/mozdevice/tests/sut_push.py index 3d02a3a9f5a2..37d51f7daf74 100644 --- a/testing/mozbase/mozdevice/tests/sut_push.py +++ b/testing/mozbase/mozdevice/tests/sut_push.py @@ -1,5 +1,6 @@ from sut import MockAgent import mozdevice +import mozlog import unittest import hashlib import tempfile @@ -68,8 +69,8 @@ class PushTest(unittest.TestCase): exceptionThrown = False try: - mozdevice.DroidSUT.debug = 4 - d = mozdevice.DroidSUT("127.0.0.1", port=a.port) + d = mozdevice.DroidSUT("127.0.0.1", port=a.port, + logLevel=mozlog.DEBUG) d.pushDir(tempdir, "/mnt/sdcard") except mozdevice.DMError, e: exceptionThrown = True diff --git a/testing/mozbase/mozprofile/mozprofile/addons.py b/testing/mozbase/mozprofile/mozprofile/addons.py index 14f44acd6aa3..dcd5df33e70e 100644 --- a/testing/mozbase/mozprofile/mozprofile/addons.py +++ b/testing/mozbase/mozprofile/mozprofile/addons.py @@ -103,14 +103,14 @@ class AddonManager(object): """ Returns a dictionary of details about the addon. - :param addon_path: path to the addon directory + :param addon_path: path to the add-on directory or XPI Returns:: {'id': u'rainbow@colors.org', # id of the addon 'version': u'1.4', # version of the addon 'name': u'Rainbow', # name of the addon - 'unpack': False } # whether to unpack the addon + 'unpack': False } # whether to unpack the addon """ # TODO: We don't use the unpack variable yet, but we should: bug 662683 @@ -140,7 +140,15 @@ class AddonManager(object): rc.append(node.data) return ''.join(rc).strip() - doc = minidom.parse(os.path.join(addon_path, 'install.rdf')) + if zipfile.is_zipfile(addon_path): + compressed_file = zipfile.ZipFile(addon_path, 'r') + try: + parseable = compressed_file.read('install.rdf') + doc = minidom.parseString(parseable) + finally: + compressed_file.close() + else: + doc = minidom.parse(os.path.join(addon_path, 'install.rdf')) # Get the namespaces abbreviations em = get_namespace_id(doc, "http://www.mozilla.org/2004/em-rdf#") diff --git a/testing/mozbase/mozprofile/mozprofile/permissions.py b/testing/mozbase/mozprofile/mozprofile/permissions.py index e031ee505e15..31b241d3c2b4 100644 --- a/testing/mozbase/mozprofile/mozprofile/permissions.py +++ b/testing/mozbase/mozprofile/mozprofile/permissions.py @@ -21,6 +21,11 @@ except ImportError: from pysqlite2 import dbapi2 as sqlite3 import urlparse +# http://hg.mozilla.org/mozilla-central/file/b871dfb2186f/build/automation.py.in#l28 +_DEFAULT_PORTS = { 'http': '8888', + 'https': '4443', + 'ws': '9988', + 'wss': '4443' } class LocationError(Exception): """Signifies an improperly formed location.""" @@ -58,7 +63,7 @@ class BadPortLocationError(LocationError): def __init__(self, given_port): LocationError.__init__(self, "bad value for port: %s" % given_port) - + class LocationsSyntaxError(Exception): """Signifies a syntax error on a particular line in server-locations.txt.""" @@ -182,11 +187,7 @@ class ServerLocations(object): host, port = netloc.rsplit(':', 1) except ValueError: host = netloc - default_ports = {'http': '80', - 'https': '443', - 'ws': '443', - 'wss': '443'} - port = default_ports.get(scheme, '80') + port = _DEFAULT_PORTS.get(scheme, '80') try: location = Location(scheme, host, port, options) @@ -232,7 +233,7 @@ class Permissions(object): # Open database and create table permDB = sqlite3.connect(os.path.join(self._profileDir, "permissions.sqlite")) cursor = permDB.cursor(); - cursor.execute("PRAGMA schema_version = 3;") + # SQL copied from # http://mxr.mozilla.org/mozilla-central/source/extensions/cookie/nsPermissionManager.cpp cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( @@ -252,15 +253,27 @@ class Permissions(object): permission_type = 1 else: permission_type = 2 - cursor.execute("INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0)", + + rows = cursor.execute("PRAGMA table_info(moz_hosts)") + count = len(rows.fetchall()) + + # if the db contains 8 columns, we're using user_version 3 + if count == 8: + statement = "INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0, 0, 0)" + cursor.execute("PRAGMA user_version=3;") + else: + statement = "INSERT INTO moz_hosts values(?, ?, ?, ?, 0, 0)" + cursor.execute("PRAGMA user_version=2;") + + cursor.execute(statement, (self._num_permissions, location.host, perm, - permission_type)) + permission_type)) # Commit and close permDB.commit() cursor.close() - def network_prefs(self, proxy=False): + def network_prefs(self, proxy=None): """ take known locations and generate preferences to handle permissions and proxy returns a tuple of prefs, user_prefs @@ -277,30 +290,33 @@ class Permissions(object): prefs.append(("capability.principal.codebase.p%s.subjectName" % i, "")) if proxy: - user_prefs = self.pac_prefs() + user_prefs = self.pac_prefs(proxy) else: user_prefs = [] return prefs, user_prefs - def pac_prefs(self): + def pac_prefs(self, user_proxy=None): """ return preferences for Proxy Auto Config. originally taken from http://mxr.mozilla.org/mozilla-central/source/build/automation.py.in """ - - prefs = [] + proxy = _DEFAULT_PORTS # We need to proxy every server but the primary one. origins = ["'%s'" % l.url() for l in self._locations] - origins = ", ".join(origins) + proxy["origins"] = origins for l in self._locations: if "primary" in l.options: - webServer = l.host - port = l.port + proxy["remote"] = l.host + proxy[l.scheme] = l.port + + # overwrite defaults with user specified proxy + if isinstance(user_proxy, dict): + proxy.update(user_proxy) # TODO: this should live in a template! # TODO: So changing the 5th line of the regex below from (\\\\\\\\d+) @@ -335,14 +351,15 @@ function FindProxyForURL(url, host) var origin = matches[1] + '://' + matches[2] + ':' + matches[3]; if (origins.indexOf(origin) < 0) return 'DIRECT'; - if (isHttp || isHttps || isWebSocket || isWebSocketSSL) - return 'PROXY %(remote)s:%(port)s'; + if (isHttp) return 'PROXY %(remote)s:%(http)s'; + if (isHttps) return 'PROXY %(remote)s:%(https)s'; + if (isWebSocket) return 'PROXY %(remote)s:%(ws)s'; + if (isWebSocketSSL) return 'PROXY %(remote)s:%(wss)s'; return 'DIRECT'; -}""" % { "origins": origins, - "remote": webServer, - "port": port } +}""" % proxy pacURL = "".join(pacURL.splitlines()) + prefs = [] prefs.append(("network.proxy.type", 2)) prefs.append(("network.proxy.autoconfig_url", pacURL)) diff --git a/testing/mozbase/mozprofile/mozprofile/prefs.py b/testing/mozbase/mozprofile/mozprofile/prefs.py index b701afbd6238..81cd3b41d7da 100644 --- a/testing/mozbase/mozprofile/mozprofile/prefs.py +++ b/testing/mozbase/mozprofile/mozprofile/prefs.py @@ -45,7 +45,7 @@ class Preferences(object): def add_file(self, path): """a preferences from a file - + :param path: """ self.add(self.read(path)) @@ -203,26 +203,26 @@ class Preferences(object): return retval @classmethod - def write(cls, _file, prefs, pref_string='user_pref("%s", %s);'): + def write(cls, _file, prefs, pref_string='user_pref(%s, %s);'): """write preferences to a file""" if isinstance(_file, basestring): - f = file(_file, 'w') + f = file(_file, 'a') else: f = _file if isinstance(prefs, dict): + # order doesn't matter prefs = prefs.items() - for key, value in prefs: - if value is True: - print >> f, pref_string % (key, 'true') - elif value is False: - print >> f, pref_string % (key, 'false') - elif isinstance(value, basestring): - print >> f, pref_string % (key, repr(str(value))) - else: - print >> f, pref_string % (key, value) # should be numeric! + # serialize -> JSON + _prefs = [(json.dumps(k), json.dumps(v) ) + for k, v in prefs] + # write the preferences + for _pref in _prefs: + print >> f, pref_string % _pref + + # close the file if opened internally if isinstance(_file, basestring): f.close() diff --git a/testing/mozbase/mozprofile/mozprofile/profile.py b/testing/mozbase/mozprofile/mozprofile/profile.py index 26e7f828e451..95f0aaa9f222 100644 --- a/testing/mozbase/mozprofile/mozprofile/profile.py +++ b/testing/mozbase/mozprofile/mozprofile/profile.py @@ -11,6 +11,7 @@ import types import uuid from addons import AddonManager from permissions import Permissions +from prefs import Preferences from shutil import copytree, rmtree from webapps import WebappCollection @@ -134,9 +135,8 @@ class Profile(object): return c def create_new_profile(self): - """Create a new clean profile in tmp which is a simple empty folder""" - profile = tempfile.mkdtemp(suffix='.mozrunner') - return profile + """Create a new clean temporary profile which is a simple empty folder""" + return tempfile.mkdtemp(suffix='.mozrunner') ### methods for preferences @@ -153,18 +153,15 @@ class Profile(object): # note what files we've touched self.written_prefs.add(filename) - - if isinstance(preferences, dict): - # order doesn't matter - preferences = preferences.items() + # opening delimeter + f.write('\n%s\n' % self.delimeters[0]) # write the preferences - f.write('\n%s\n' % self.delimeters[0]) - _prefs = [(json.dumps(k), json.dumps(v) ) - for k, v in preferences] - for _pref in _prefs: - f.write('user_pref(%s, %s);\n' % _pref) + Preferences.write(f, preferences) + + # closing delimeter f.write('%s\n' % self.delimeters[1]) + f.close() def pop_preferences(self, filename): diff --git a/testing/mozbase/mozprofile/setup.py b/testing/mozbase/mozprofile/setup.py index a0b68f3cad49..01e37fd10ba2 100644 --- a/testing/mozbase/mozprofile/setup.py +++ b/testing/mozbase/mozprofile/setup.py @@ -5,7 +5,7 @@ import sys from setuptools import setup -PACKAGE_VERSION = '0.5' +PACKAGE_VERSION = '0.7' # we only support python 2 right now assert sys.version_info[0] == 2 diff --git a/testing/mozbase/mozprofile/tests/addonid.py b/testing/mozbase/mozprofile/tests/addonid.py index 6f71d92f2501..8b463f207a10 100755 --- a/testing/mozbase/mozprofile/tests/addonid.py +++ b/testing/mozbase/mozprofile/tests/addonid.py @@ -6,16 +6,20 @@ import unittest import shutil from mozprofile import addons + +here = os.path.dirname(os.path.abspath(__file__)) + + class AddonIDTest(unittest.TestCase): """ Test finding the addon id in a variety of install.rdf styles """ - + def make_install_rdf(self, filecontents): path = tempfile.mkdtemp() f = open(os.path.join(path, "install.rdf"), "w") f.write(filecontents) f.close() return path - + def test_addonID(self): testlist = self.get_test_list() for t in testlist: @@ -23,10 +27,15 @@ class AddonIDTest(unittest.TestCase): p = self.make_install_rdf(t) a = addons.AddonManager(os.path.join(p, "profile")) addon_id = a.addon_details(p)['id'] - self.assertTrue(addon_id == "winning", "We got the addon id") + self.assertEqual(addon_id, "winning", "We got the addon id") finally: shutil.rmtree(p) + def test_addonID_xpi(self): + a = addons.AddonManager("profile") + addon = a.addon_details(os.path.join(here, "addons", "empty.xpi")) + self.assertEqual(addon['id'], "test-empty@quality.mozilla.org", "We got the addon id") + def get_test_list(self): """ This just returns a hardcoded list of install.rdf snippets for testing. When adding snippets for testing, remember that the id we're looking for diff --git a/testing/mozbase/mozprofile/tests/empty/install.rdf b/testing/mozbase/mozprofile/tests/addons/empty/install.rdf similarity index 97% rename from testing/mozbase/mozprofile/tests/empty/install.rdf rename to testing/mozbase/mozprofile/tests/addons/empty/install.rdf index 4531a3820b83..70b9e13e44bf 100644 --- a/testing/mozbase/mozprofile/tests/empty/install.rdf +++ b/testing/mozbase/mozprofile/tests/addons/empty/install.rdf @@ -16,5 +16,5 @@ * - + diff --git a/testing/mozbase/mozprofile/tests/bug758250.py b/testing/mozbase/mozprofile/tests/bug758250.py index 7f385bcea85c..61bc85fae555 100755 --- a/testing/mozbase/mozprofile/tests/bug758250.py +++ b/testing/mozbase/mozprofile/tests/bug758250.py @@ -6,8 +6,10 @@ import shutil import tempfile import unittest + here = os.path.dirname(os.path.abspath(__file__)) + class Bug758250(unittest.TestCase): """ use of --profile in mozrunner just blows away addon sources: @@ -17,7 +19,7 @@ class Bug758250(unittest.TestCase): def test_profile_addon_cleanup(self): # sanity check: the empty addon should be here - empty = os.path.join(here, 'empty') + empty = os.path.join(here, 'addons', 'empty') self.assertTrue(os.path.exists(empty)) self.assertTrue(os.path.isdir(empty)) self.assertTrue(os.path.exists(os.path.join(empty, 'install.rdf'))) diff --git a/testing/mozbase/mozprofile/tests/bug785146.py b/testing/mozbase/mozprofile/tests/bug785146.py index c0762e3791cf..29376d8d17fd 100644 --- a/testing/mozbase/mozprofile/tests/bug785146.py +++ b/testing/mozbase/mozprofile/tests/bug785146.py @@ -41,7 +41,7 @@ http://127.0.0.1:8888 privileged perms_db_filename = os.path.join(self.profile_dir, 'permissions.sqlite') perms.write_db(self.locations_file) - stmt = 'PRAGMA schema_version;' + stmt = 'PRAGMA user_version;' con = sqlite3.connect(perms_db_filename) cur = con.cursor() @@ -49,7 +49,7 @@ http://127.0.0.1:8888 privileged entries = cur.fetchall() schema_version = entries[0][0] - self.assertEqual(schema_version, 3) + self.assertEqual(schema_version, 2) if __name__ == '__main__': unittest.main() diff --git a/testing/mozbase/mozprofile/tests/permissions.py b/testing/mozbase/mozprofile/tests/permissions.py index 2bb904531625..5f2798c036f9 100644 --- a/testing/mozbase/mozprofile/tests/permissions.py +++ b/testing/mozbase/mozprofile/tests/permissions.py @@ -36,7 +36,37 @@ http://127.0.0.1:8888 privileged if self.locations_file: self.locations_file.close() - def test_permissions_db(self): + def write_perm_db(self, version=3): + permDB = sqlite3.connect(os.path.join(self.profile_dir, "permissions.sqlite")) + cursor = permDB.cursor() + + cursor.execute("PRAGMA user_version=%d;" % version) + + if version == 3: + cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( + id INTEGER PRIMARY KEY, + host TEXT, + type TEXT, + permission INTEGER, + expireType INTEGER, + expireTime INTEGER, + appId INTEGER, + isInBrowserElement INTEGER)""") + elif version == 2: + cursor.execute("""CREATE TABLE IF NOT EXISTS moz_hosts ( + id INTEGER PRIMARY KEY, + host TEXT, + type TEXT, + permission INTEGER, + expireType INTEGER, + expireTime INTEGER)""") + else: + raise Exception("version must be 2 or 3") + + permDB.commit() + cursor.close() + + def test_create_permissions_db(self): perms = Permissions(self.profile_dir, self.locations_file.name) perms_db_filename = os.path.join(self.profile_dir, 'permissions.sqlite') @@ -48,7 +78,7 @@ http://127.0.0.1:8888 privileged entries = cur.fetchall() self.assertEqual(len(entries), 3) - + self.assertEqual(entries[0][0], 'mochi.test') self.assertEqual(entries[0][1], 'allowXULXBL') self.assertEqual(entries[0][2], 1) @@ -71,12 +101,17 @@ http://127.0.0.1:8888 privileged self.assertEqual(entries[3][1], 'allowXULXBL') self.assertEqual(entries[3][2], 2) + # when creating a DB we should default to user_version==2 + cur.execute('PRAGMA user_version') + entries = cur.fetchall() + self.assertEqual(entries[0][0], 2) + perms.clean_db() # table should be removed cur.execute("select * from sqlite_master where type='table'") entries = cur.fetchall() self.assertEqual(len(entries), 0) - + def test_nw_prefs(self): perms = Permissions(self.profile_dir, self.locations_file.name) @@ -97,7 +132,6 @@ http://127.0.0.1:8888 privileged 'http://127.0.0.1:8888')) self.assertEqual(prefs[5], ('capability.principal.codebase.p2.subjectName', '')) - prefs, user_prefs = perms.network_prefs(True) self.assertEqual(len(user_prefs), 2) self.assertEqual(user_prefs[0], ('network.proxy.type', 2)) @@ -106,8 +140,39 @@ http://127.0.0.1:8888 privileged origins_decl = "var origins = ['http://mochi.test:8888', 'http://127.0.0.1:80', 'http://127.0.0.1:8888'];" self.assertTrue(origins_decl in user_prefs[1][1]) - proxy_check = "if (isHttp || isHttps || isWebSocket || isWebSocketSSL) return 'PROXY mochi.test:8888';" - self.assertTrue(proxy_check in user_prefs[1][1]) + proxy_check = ("if (isHttp) return 'PROXY mochi.test:8888';", + "if (isHttps) return 'PROXY mochi.test:4443';", + "if (isWebSocket) return 'PROXY mochi.test:9988';", + "if (isWebSocketSSL) return 'PROXY mochi.test:4443';") + self.assertTrue(all(c in user_prefs[1][1] for c in proxy_check)) + + def verify_user_version(self, version): + """Verifies that we call INSERT statements using the correct number + of columns for existing databases. + """ + self.write_perm_db(version=version) + Permissions(self.profile_dir, self.locations_file.name) + perms_db_filename = os.path.join(self.profile_dir, 'permissions.sqlite') + + select_stmt = 'select * from moz_hosts' + + con = sqlite3.connect(perms_db_filename) + cur = con.cursor() + cur.execute(select_stmt) + entries = cur.fetchall() + + self.assertEqual(len(entries), 3) + + columns = 8 if version == 3 else 6 + self.assertEqual(len(entries[0]), columns) + for x in range(4, columns): + self.assertEqual(entries[0][x], 0) + + def test_existing_permissions_db_v2(self): + self.verify_user_version(2) + + def test_existing_permissions_db_v3(self): + self.verify_user_version(3) if __name__ == '__main__': diff --git a/testing/mozbase/mozprofile/tests/server_locations.py b/testing/mozbase/mozprofile/tests/server_locations.py index 2f3dc8bc600d..0c860bd79524 100644 --- a/testing/mozbase/mozprofile/tests/server_locations.py +++ b/testing/mozbase/mozprofile/tests/server_locations.py @@ -70,7 +70,7 @@ http://example.org:80 privileged self.compare_location(i.next(), 'https', 'test', '80', ['privileged']) self.compare_location(i.next(), 'http', 'example.org', '80', ['privileged']) - self.compare_location(i.next(), 'http', 'test1.example.org', '80', + self.compare_location(i.next(), 'http', 'test1.example.org', '8888', ['privileged']) locations.add_host('mozilla.org') diff --git a/testing/mozbase/test.py b/testing/mozbase/test.py index 97f35613721a..c0f62b1beb09 100755 --- a/testing/mozbase/test.py +++ b/testing/mozbase/test.py @@ -11,6 +11,8 @@ by default https://github.com/mozilla/mozbase/blob/master/test-manifest.ini import imp import manifestparser +import mozinfo +import optparse import os import sys import unittest @@ -38,6 +40,14 @@ def unittests(path): def main(args=sys.argv[1:]): + # parse command line options + usage = '%prog [options] manifest.ini <...>' + parser = optparse.OptionParser(usage=usage, description=__doc__) + parser.add_option('--list', dest='list_tests', + action='store_true', default=False, + help="list paths of tests to be run") + options, args = parser.parse_args(args) + # read the manifest if args: manifests = args @@ -48,14 +58,21 @@ def main(args=sys.argv[1:]): # ensure manifests exist if not os.path.exists(manifest): missing.append(manifest) - assert not missing, 'manifest%s not found: %s' % ((len(manifests) == 1 and '' or 's'), ', '.join(missing)) + assert not missing, 'manifest(s) not found: %s' % ', '.join(missing) manifest = manifestparser.TestManifest(manifests=manifests) # gather the tests - tests = manifest.active_tests() + tests = manifest.active_tests(disabled=False, **mozinfo.info) + tests = [test['path'] for test in tests] + if options.list_tests: + # print test paths + print '\n'.join(tests) + sys.exit(0) + + # create unittests unittestlist = [] for test in tests: - unittestlist.extend(unittests(test['path'])) + unittestlist.extend(unittests(test)) # run the tests suite = unittest.TestSuite(unittestlist) From 906276315aa83bfc48b97ae77918d13a82872dc0 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Wed, 17 Apr 2013 16:56:03 -0400 Subject: [PATCH 18/75] Bug 860441 - Camera preview update is not smooth on gonk - r=nsilva --- gfx/gl/GLContext.h | 1 + gfx/gl/GLContextProviderEGL.cpp | 22 +++++++++++++++++ gfx/layers/client/ImageClient.cpp | 15 +++++++++++ gfx/layers/composite/ImageHost.cpp | 1 - gfx/layers/opengl/TextureHostOGL.cpp | 37 ++++++++++++++++------------ gfx/layers/opengl/TextureHostOGL.h | 6 +++++ 6 files changed, 65 insertions(+), 17 deletions(-) diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h index 44a27192c9be..de6f0af14df8 100644 --- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -359,6 +359,7 @@ public: #ifdef MOZ_WIDGET_GONK virtual EGLImage CreateEGLImageForNativeBuffer(void* buffer) = 0; virtual void DestroyEGLImage(EGLImage image) = 0; + virtual EGLImage GetNullEGLImage() = 0; #endif virtual already_AddRefed diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp index eeb3725b927f..7866bb57b698 100644 --- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -441,6 +441,26 @@ public: { sEGLLibrary.fDestroyImage(EGL_DISPLAY(), image); } + + EGLImage GetNullEGLImage() MOZ_OVERRIDE + { + if (!mNullGraphicBuffer.get()) { + mNullGraphicBuffer + = new android::GraphicBuffer( + 1, 1, + PIXEL_FORMAT_RGB_565, + GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER); + EGLint attrs[] = { + LOCAL_EGL_NONE, LOCAL_EGL_NONE + }; + mNullEGLImage = sEGLLibrary.fCreateImage(EGL_DISPLAY(), + EGL_NO_CONTEXT, + LOCAL_EGL_NATIVE_BUFFER_ANDROID, + mNullGraphicBuffer->getNativeBuffer(), + attrs); + } + return mNullEGLImage; + } #endif @@ -695,6 +715,8 @@ protected: bool mShareWithEGLImage; #ifdef MOZ_WIDGET_GONK nsRefPtr mHwc; + EGLImage mNullEGLImage; + android::sp mNullGraphicBuffer; #endif // A dummy texture ID that can be used when we need a texture object whose diff --git a/gfx/layers/client/ImageClient.cpp b/gfx/layers/client/ImageClient.cpp index e0d661d0edf1..02f907e9f3be 100644 --- a/gfx/layers/client/ImageClient.cpp +++ b/gfx/layers/client/ImageClient.cpp @@ -159,6 +159,21 @@ ImageClientSingle::UpdateImage(ImageContainer* aContainer, return false; } mTextureClient->SetDescriptor(desc); + } else if (image->GetFormat() == GONK_IO_SURFACE) { + EnsureTextureClient(TEXTURE_SHARED_GL_EXTERNAL); + + nsIntRect rect(0, 0, + image->GetSize().width, + image->GetSize().height); + UpdatePictureRect(rect); + + AutoLockTextureClient lock(mTextureClient); + + SurfaceDescriptor desc = static_cast(image)->GetSurfaceDescriptor(); + if (!IsSurfaceDescriptorValid(desc)) { + return false; + } + mTextureClient->SetDescriptor(desc); } else { nsRefPtr surface = image->GetAsSurface(); MOZ_ASSERT(surface); diff --git a/gfx/layers/composite/ImageHost.cpp b/gfx/layers/composite/ImageHost.cpp index 93fd20667afd..a0de015afb51 100644 --- a/gfx/layers/composite/ImageHost.cpp +++ b/gfx/layers/composite/ImageHost.cpp @@ -161,7 +161,6 @@ ImageHostBuffered::EnsureTextureHost(TextureIdentifier aTextureId, aTextureInfo); if (result) { mTextureHost->SetBuffer(new SurfaceDescriptor(null_t()), aAllocator); - mPictureRect = nsIntRect(0, 0, -1, -1); } return result; diff --git a/gfx/layers/opengl/TextureHostOGL.cpp b/gfx/layers/opengl/TextureHostOGL.cpp index d4b70734fc3f..a1bae77d39f6 100644 --- a/gfx/layers/opengl/TextureHostOGL.cpp +++ b/gfx/layers/opengl/TextureHostOGL.cpp @@ -593,9 +593,22 @@ SurfaceFormatForAndroidPixelFormat(android::PixelFormat aFormat) return FORMAT_R5G6B5; case android::PIXEL_FORMAT_A_8: return FORMAT_A8; + case 17: // NV21 YUV format, see http://developer.android.com/reference/android/graphics/ImageFormat.html#NV21 + return FORMAT_B8G8R8A8; // yup, use FORMAT_B8G8R8A8 even though it's a YUV texture. This is an external texture. default: MOZ_NOT_REACHED("Unknown Android pixel format"); - return FORMAT_B8G8R8A8; + return FORMAT_UNKNOWN; + } +} + +static GLenum +TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat) +{ + switch (aFormat) { + case 17: // NV21 YUV format, see http://developer.android.com/reference/android/graphics/ImageFormat.html#NV21 + return LOCAL_GL_TEXTURE_EXTERNAL; + default: + return LOCAL_GL_TEXTURE_2D; } } @@ -615,7 +628,7 @@ GrallocTextureHostOGL::DeleteTextures() mGL->MakeCurrent(); if (mGLTexture) { mGL->fDeleteTextures(1, &mGLTexture); - mGLTexture= 0; + mGLTexture = 0; } if (mEGLImage) { mGL->DestroyEGLImage(mEGLImage); @@ -641,6 +654,7 @@ GrallocTextureHostOGL::SwapTexturesImpl(const SurfaceDescriptor& aImage, const SurfaceDescriptorGralloc& desc = aImage.get_SurfaceDescriptorGralloc(); mGraphicBuffer = GrallocBufferActor::GetFrom(desc); mFormat = SurfaceFormatForAndroidPixelFormat(mGraphicBuffer->getPixelFormat()); + mTextureTarget = TextureTargetForAndroidPixelFormat(mGraphicBuffer->getPixelFormat()); DeleteTextures(); } @@ -651,7 +665,7 @@ void GrallocTextureHostOGL::BindTexture(GLenum aTextureUnit) mGL->MakeCurrent(); mGL->fActiveTexture(aTextureUnit); - mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mGLTexture); + mGL->fBindTexture(mTextureTarget, mGLTexture); mGL->fActiveTexture(LOCAL_GL_TEXTURE0); } @@ -688,11 +702,11 @@ GrallocTextureHostOGL::Lock() mGL->fGenTextures(1, &mGLTexture); } mGL->fActiveTexture(LOCAL_GL_TEXTURE0); - mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mGLTexture); + mGL->fBindTexture(mTextureTarget, mGLTexture); if (!mEGLImage) { mEGLImage = mGL->CreateEGLImageForNativeBuffer(mGraphicBuffer->getNativeBuffer()); } - mGL->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, mEGLImage); + mGL->fEGLImageTargetTexture2D(mTextureTarget, mEGLImage); return true; } @@ -707,20 +721,11 @@ GrallocTextureHostOGL::Unlock() * the GL may place read locks on it. We must ensure that we release them early enough, * i.e. before the next time that we will try to acquire a write lock on the same buffer, * because read and write locks on gralloc buffers are mutually exclusive. - * - * Unfortunately there does not seem to exist an EGL function to dissociate a gralloc - * buffer from a texture that it was tied to. Failing that, we achieve the same result - * by uploading a 1x1 dummy texture image to the same texture, replacing the existing - * gralloc buffer attachment. */ mGL->MakeCurrent(); mGL->fActiveTexture(LOCAL_GL_TEXTURE0); - mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mGLTexture); - mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, - LOCAL_GL_RGBA, - 1, 1, 0, - LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, - nullptr); + mGL->fBindTexture(mTextureTarget, mGLTexture); + mGL->fEGLImageTargetTexture2D(mTextureTarget, mGL->GetNullEGLImage()); } gfx::SurfaceFormat diff --git a/gfx/layers/opengl/TextureHostOGL.h b/gfx/layers/opengl/TextureHostOGL.h index ee7c19840ce8..cbcb2cc7309f 100644 --- a/gfx/layers/opengl/TextureHostOGL.h +++ b/gfx/layers/opengl/TextureHostOGL.h @@ -547,6 +547,7 @@ class GrallocTextureHostOGL public: GrallocTextureHostOGL() : mGL(nullptr) + , mTextureTarget(0) , mGLTexture(0) , mEGLImage(0) { @@ -575,6 +576,10 @@ public: gl::ShaderProgramType GetShaderProgram() const MOZ_OVERRIDE { + if (mTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL) { + return gl::RGBAExternalLayerProgramType; + } + MOZ_ASSERT(mTextureTarget == LOCAL_GL_TEXTURE_2D); return mFormat == gfx::FORMAT_B8G8R8A8 || mFormat == gfx::FORMAT_B8G8R8X8 ? gl::BGRALayerProgramType : gl::RGBALayerProgramType; @@ -605,6 +610,7 @@ private: RefPtr mGL; android::sp mGraphicBuffer; + GLenum mTextureTarget; GLuint mGLTexture; EGLImage mEGLImage; }; From c5a96730b5b47896cf2fc64c3bed146c94ede616 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Wed, 17 Apr 2013 16:56:05 -0400 Subject: [PATCH 19/75] Bug 862523 - Android: blocklist the STAGEFRIGHT feature on the following Hardware's: antares, endeavoru, harmony, picasso, picasso_e, ventana - r=joe,doublec --- widget/android/GfxInfo.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/widget/android/GfxInfo.cpp b/widget/android/GfxInfo.cpp index 503b16454858..7bcbd4a754fe 100644 --- a/widget/android/GfxInfo.cpp +++ b/widget/android/GfxInfo.cpp @@ -348,6 +348,19 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, if (aFeature == FEATURE_STAGEFRIGHT) { NS_LossyConvertUTF16toASCII cManufacturer(mManufacturer); NS_LossyConvertUTF16toASCII cModel(mModel); + NS_LossyConvertUTF16toASCII cHardware(mHardware); + + if (cHardware.Equals("antares") || + cHardware.Equals("endeavoru") || + cHardware.Equals("harmony") || + cHardware.Equals("picasso") || + cHardware.Equals("picasso_e") || + cHardware.Equals("ventana")) + { + *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE; + return NS_OK; + } + if (CompareVersions(mOSVersion.get(), "2.2.0") >= 0 && CompareVersions(mOSVersion.get(), "2.3.0") < 0) { From 0249969fdc0820a838a560876d9eb5657ef49e04 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Wed, 17 Apr 2013 23:17:44 +0300 Subject: [PATCH 20/75] Bug 862987 - Include nsAutoPtr.h in BindingDeclarations.h, r=bz --HG-- extra : rebase_source : b3059811b16fa5a2bec070176a276ece18178baf --- dom/bindings/BindingDeclarations.h | 1 + 1 file changed, 1 insertion(+) diff --git a/dom/bindings/BindingDeclarations.h b/dom/bindings/BindingDeclarations.h index 0acaef30597e..212b878a9b22 100644 --- a/dom/bindings/BindingDeclarations.h +++ b/dom/bindings/BindingDeclarations.h @@ -20,6 +20,7 @@ #include "nsDOMString.h" #include "nsStringBuffer.h" #include "nsTArray.h" +#include "nsAutoPtr.h" // for nsRefPtr member variables class nsWrapperCache; From baa392919d402f273d39252f48b3d7451249d787 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Wed, 17 Apr 2013 23:29:09 +0300 Subject: [PATCH 21/75] Bug 862983 - HeaderFile annotation for WebIDL interfaces, r=bz --HG-- extra : rebase_source : f096c032342b71aeb42999f80241697c79037b7c --- dom/bindings/Bindings.conf | 4 ---- dom/bindings/Configuration.py | 2 ++ dom/bindings/parser/WebIDL.py | 3 ++- dom/webidl/DesktopNotification.webidl | 1 + 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 447ae15f5418..30d6c3b07dad 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -228,10 +228,6 @@ DOMInterfaces = { 'resultNotAddRefed': [ 'delayTime' ], }], -'DesktopNotificationCenter': { - 'headerFile': 'mozilla/dom/DesktopNotification.h', -}, - 'DeviceMotionEvent': { 'nativeType': 'nsDOMDeviceMotionEvent', }, diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index 1d5d03f95868..4ecd958dc986 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -226,6 +226,8 @@ class Descriptor(DescriptorProvider): else: if self.workers: headerDefault = "mozilla/dom/workers/bindings/%s.h" % ifaceName + elif not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"): + headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0] else: headerDefault = self.nativeType headerDefault = headerDefault.replace("::", "/") + ".h" diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 3450ee83f19e..902e4bce8041 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -897,7 +897,8 @@ class IDLInterface(IDLObjectWithScope): elif (identifier == "PrefControlled" or identifier == "Pref" or identifier == "NeedNewResolve" or - identifier == "JSImplementation"): + identifier == "JSImplementation" or + identifier == "HeaderFile"): # Known attributes that we don't need to do anything with here pass else: diff --git a/dom/webidl/DesktopNotification.webidl b/dom/webidl/DesktopNotification.webidl index ebe4554023d5..9763adc5fd81 100644 --- a/dom/webidl/DesktopNotification.webidl +++ b/dom/webidl/DesktopNotification.webidl @@ -6,6 +6,7 @@ interface MozObserver; +[HeaderFile="mozilla/dom/DesktopNotification.h"] interface DesktopNotificationCenter { [Creator] From 238dddc9f62a4677a9b7383e430adf60a275b90c Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Wed, 17 Apr 2013 23:40:54 +0300 Subject: [PATCH 22/75] Bug 862991 - If dictionary member needs cx, so should dictionary itself, r=bz --HG-- extra : rebase_source : a495d79df021e896d3b7695a7d11da8d52559c28 --- dom/bindings/Codegen.py | 6 ++++++ dom/bindings/test/TestBindingHeader.h | 12 ++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index aca253ae9aea..245fbce9431c 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -3720,12 +3720,18 @@ def typeNeedsCx(type, descriptorProvider, retVal=False): if type.isUnion(): return any(typeNeedsCx(t, descriptorProvider) for t in type.unroll().flatMemberTypes) + if type.isDictionary(): + return dictionaryNeedsCx(type.inner, descriptorProvider) if retVal and type.isSpiderMonkeyInterface(): return True if type.isCallback(): return descriptorProvider.workers return type.isAny() or type.isObject() +def dictionaryNeedsCx(dictionary, descriptorProvider): + return (any(typeNeedsCx(m.type, descriptorProvider) for m in dictionary.members) or + (dictionary.parent and dictionaryNeedsCx(dictionary.parent, descriptorProvider))) + # Returns a tuple consisting of a CGThing containing the type of the return # value, or None if there is no need for a return value, and a boolean signaling # whether the return value is passed in an out parameter. diff --git a/dom/bindings/test/TestBindingHeader.h b/dom/bindings/test/TestBindingHeader.h index 9252661562fb..fcfb7a04e50a 100644 --- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -442,13 +442,13 @@ public: void SetAttributeRenamedTo(int8_t); // Dictionary tests - void PassDictionary(const Dict&); - void ReceiveDictionary(Dict&); + void PassDictionary(JSContext*, const Dict&); + void ReceiveDictionary(JSContext*, Dict&); void PassOtherDictionary(const GrandparentDict&); - void PassSequenceOfDictionaries(const Sequence&); - void PassDictionaryOrLong(const Dict&); + void PassSequenceOfDictionaries(JSContext*, const Sequence&); + void PassDictionaryOrLong(JSContext*, const Dict&); void PassDictionaryOrLong(int32_t); - void PassDictContainingDict(const DictContainingDict&); + void PassDictContainingDict(JSContext*, const DictContainingDict&); void PassDictContainingSequence(const DictContainingSequence&); void ReceiveDictContainingSequence(DictContainingSequence&); @@ -468,7 +468,7 @@ public: bool Overload1(TestInterface&); TestInterface* Overload1(const nsAString&, TestInterface&); void Overload2(TestInterface&); - void Overload2(const Dict&); + void Overload2(JSContext*, const Dict&); void Overload2(const nsAString&); void Overload3(TestInterface&); void Overload3(const TestCallback&); From 3919709af46eb0025f62cc49045b9138b19cca8a Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Wed, 17 Apr 2013 17:18:15 -0400 Subject: [PATCH 23/75] Bug 860441 - trivial compile fix on non-gonk - no review, CLOSED TREE --- gfx/layers/client/ImageClient.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gfx/layers/client/ImageClient.cpp b/gfx/layers/client/ImageClient.cpp index 02f907e9f3be..f905097b40bf 100644 --- a/gfx/layers/client/ImageClient.cpp +++ b/gfx/layers/client/ImageClient.cpp @@ -159,6 +159,7 @@ ImageClientSingle::UpdateImage(ImageContainer* aContainer, return false; } mTextureClient->SetDescriptor(desc); +#ifdef MOZ_WIDGET_GONK } else if (image->GetFormat() == GONK_IO_SURFACE) { EnsureTextureClient(TEXTURE_SHARED_GL_EXTERNAL); @@ -174,6 +175,7 @@ ImageClientSingle::UpdateImage(ImageContainer* aContainer, return false; } mTextureClient->SetDescriptor(desc); +#endif } else { nsRefPtr surface = image->GetAsSurface(); MOZ_ASSERT(surface); From eaa5b3fc8455ca400518afaff1875ca25f00f5a3 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 17 Apr 2013 17:39:10 -0400 Subject: [PATCH 24/75] Bug 859951 - Refactor the code to convert MOTION_EVENT GeckoEvents to nsTouchEvent instances. r=wesj --- mobile/android/base/GeckoEvent.java | 2 +- widget/android/AndroidJavaWrappers.cpp | 61 +++++++++++++++++++ widget/android/AndroidJavaWrappers.h | 2 + widget/android/nsWindow.cpp | 84 ++++---------------------- widget/android/nsWindow.h | 2 - 5 files changed, 76 insertions(+), 75 deletions(-) diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index 490b6d363110..1a4e53e2423a 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -374,7 +374,7 @@ public class GeckoEvent { } } - public void addMotionPoint(int index, int eventIndex, MotionEvent event) { + private void addMotionPoint(int index, int eventIndex, MotionEvent event) { try { PointF geckoPoint = new PointF(event.getX(eventIndex), event.getY(eventIndex)); geckoPoint = GeckoApp.mAppContext.getLayerView().convertViewPointToLayerPoint(geckoPoint); diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index 773010db8dbf..ae8a599ab040 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -690,6 +690,67 @@ AndroidGeckoEvent::Init(AndroidGeckoEvent *aResizeEvent) mPoints = aResizeEvent->mPoints; // x,y coordinates } +nsTouchEvent +AndroidGeckoEvent::MakeTouchEvent(nsIWidget* widget) +{ + int type = NS_EVENT_NULL; + int startIndex = 0; + int endIndex = Count(); + + int action = Action() & AndroidMotionEvent::ACTION_MASK; + switch (action) { + case AndroidMotionEvent::ACTION_DOWN: + case AndroidMotionEvent::ACTION_POINTER_DOWN: { + type = NS_TOUCH_START; + break; + } + case AndroidMotionEvent::ACTION_MOVE: { + type = NS_TOUCH_MOVE; + break; + } + case AndroidMotionEvent::ACTION_UP: + case AndroidMotionEvent::ACTION_POINTER_UP: { + type = NS_TOUCH_END; + // for pointer-up events we only want the data from + // the one pointer that went up + startIndex = PointerIndex(); + endIndex = startIndex + 1; + break; + } + case AndroidMotionEvent::ACTION_OUTSIDE: + case AndroidMotionEvent::ACTION_CANCEL: { + type = NS_TOUCH_CANCEL; + break; + } + } + + nsTouchEvent event(true, type, widget); + if (type == NS_EVENT_NULL) { + // An event we don't know about + return event; + } + + event.modifiers = 0; + event.time = Time(); + event.InitBasicModifiers(IsCtrlPressed(), + IsAltPressed(), + IsShiftPressed(), + IsMetaPressed()); + + const nsIntPoint& offset = widget->WidgetToScreenOffset(); + event.touches.SetCapacity(endIndex - startIndex); + for (int i = startIndex; i < endIndex; i++) { + nsCOMPtr t(new dom::Touch(PointIndicies()[i], + Points()[i] - offset, + PointRadii()[i], + Orientations()[i], + Pressures()[i])); + event.touches.AppendElement(t); + } + + return event; +} + void AndroidPoint::Init(JNIEnv *jenv, jobject jobj) { diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h index 24353be3d692..a94b28df27ca 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -15,6 +15,7 @@ #include "nsString.h" #include "nsTArray.h" #include "mozilla/gfx/Rect.h" +#include "mozilla/dom/Touch.h" //#define FORCE_ALOG 1 @@ -660,6 +661,7 @@ public: RefCountedJavaObject* ByteBuffer() { return mByteBuffer; } int Width() { return mWidth; } int Height() { return mHeight; } + nsTouchEvent MakeTouchEvent(nsIWidget* widget); protected: int mAction; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 08e374db93a2..9fda5dcdb34f 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -28,7 +28,6 @@ using mozilla::unused; #include "nsRenderingContext.h" #include "nsIDOMSimpleGestureEvent.h" -#include "mozilla/dom/Touch.h" #include "nsGkAtoms.h" #include "nsWidgetsCID.h" @@ -1202,31 +1201,18 @@ bool nsWindow::OnMultitouchEvent(AndroidGeckoEvent *ae) bool preventDefaultActions = false; bool isDownEvent = false; - switch (ae->Action() & AndroidMotionEvent::ACTION_MASK) { - case AndroidMotionEvent::ACTION_DOWN: - case AndroidMotionEvent::ACTION_POINTER_DOWN: { - nsTouchEvent event(true, NS_TOUCH_START, this); - preventDefaultActions = DispatchMultitouchEvent(event, ae); - isDownEvent = true; - break; - } - case AndroidMotionEvent::ACTION_MOVE: { - nsTouchEvent event(true, NS_TOUCH_MOVE, this); - preventDefaultActions = DispatchMultitouchEvent(event, ae); - break; - } - case AndroidMotionEvent::ACTION_UP: - case AndroidMotionEvent::ACTION_POINTER_UP: { - nsTouchEvent event(true, NS_TOUCH_END, this); - preventDefaultActions = DispatchMultitouchEvent(event, ae); - break; - } - case AndroidMotionEvent::ACTION_OUTSIDE: - case AndroidMotionEvent::ACTION_CANCEL: { - nsTouchEvent event(true, NS_TOUCH_CANCEL, this); - preventDefaultActions = DispatchMultitouchEvent(event, ae); - break; - } + + nsTouchEvent event = ae->MakeTouchEvent(this); + if (event.message != NS_EVENT_NULL) { + nsEventStatus status; + DispatchEvent(&event, status); + // We check mMultipleActionsPrevented because that's what + // sets when someone starts dragging the thumb. It doesn't set the status + // because it doesn't want to prevent the code that gives the input focus + // from running. + preventDefaultActions = (status == nsEventStatus_eConsumeNoDefault || + event.mFlags.mMultipleActionsPrevented); + isDownEvent = (event.message == NS_TOUCH_START); } // if the last event we got was a down event, then by now we know for sure whether @@ -1256,52 +1242,6 @@ bool nsWindow::OnMultitouchEvent(AndroidGeckoEvent *ae) return preventDefaultActions; } -bool -nsWindow::DispatchMultitouchEvent(nsTouchEvent &event, AndroidGeckoEvent *ae) -{ - nsIntPoint offset = WidgetToScreenOffset(); - - event.modifiers = 0; - event.time = ae->Time(); - event.InitBasicModifiers(ae->IsCtrlPressed(), - ae->IsAltPressed(), - ae->IsShiftPressed(), - ae->IsMetaPressed()); - - int action = ae->Action() & AndroidMotionEvent::ACTION_MASK; - if (action == AndroidMotionEvent::ACTION_UP || - action == AndroidMotionEvent::ACTION_POINTER_UP) { - event.touches.SetCapacity(1); - int pointerIndex = ae->PointerIndex(); - nsCOMPtr t(new Touch(ae->PointIndicies()[pointerIndex], - ae->Points()[pointerIndex] - offset, - ae->PointRadii()[pointerIndex], - ae->Orientations()[pointerIndex], - ae->Pressures()[pointerIndex])); - event.touches.AppendElement(t); - } else { - int count = ae->Count(); - event.touches.SetCapacity(count); - for (int i = 0; i < count; i++) { - nsCOMPtr t(new Touch(ae->PointIndicies()[i], - ae->Points()[i] - offset, - ae->PointRadii()[i], - ae->Orientations()[i], - ae->Pressures()[i])); - event.touches.AppendElement(t); - } - } - - nsEventStatus status; - DispatchEvent(&event, status); - // We check mMultipleActionsPrevented because that's what - // sets when someone starts dragging the thumb. It doesn't set the status - // because it doesn't want to prevent the code that gives the input focus - // from running. - return (status == nsEventStatus_eConsumeNoDefault || - event.mFlags.mMultipleActionsPrevented); -} - void nsWindow::OnNativeGestureEvent(AndroidGeckoEvent *ae) { diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index 35fe96ddc221..32559dd252d7 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -215,8 +215,6 @@ protected: private: void InitKeyEvent(nsKeyEvent& event, mozilla::AndroidGeckoEvent& key, ANPEvent* pluginEvent); - bool DispatchMultitouchEvent(nsTouchEvent &event, - mozilla::AndroidGeckoEvent *ae); void DispatchMotionEvent(nsInputEvent &event, mozilla::AndroidGeckoEvent *ae, const nsIntPoint &refPoint); From 332865f4e6e29f575bf6028deee387a04f60cb12 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 17 Apr 2013 17:39:11 -0400 Subject: [PATCH 25/75] Bug 859951 - Split MotionEvent's action into mask and index in Java rather than in JNI code. r=wesj --- mobile/android/base/GeckoEvent.java | 6 +++--- widget/android/AndroidJavaWrappers.cpp | 3 +-- widget/android/AndroidJavaWrappers.h | 3 --- widget/android/nsWindow.cpp | 4 ++-- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index 1a4e53e2423a..4dff62ec1906 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -336,11 +336,11 @@ public class GeckoEvent { } private void initMotionEvent(MotionEvent m) { - mAction = m.getAction(); + mAction = m.getActionMasked(); mTime = (System.currentTimeMillis() - SystemClock.elapsedRealtime()) + m.getEventTime(); mMetaState = m.getMetaState(); - switch (mAction & MotionEvent.ACTION_MASK) { + switch (mAction) { case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: @@ -356,7 +356,7 @@ public class GeckoEvent { mOrientations = new float[mCount]; mPressures = new float[mCount]; mPointRadii = new Point[mCount]; - mPointerIndex = (mAction & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + mPointerIndex = m.getActionIndex(); for (int i = 0; i < mCount; i++) { addMotionPoint(i, i, m); } diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index ae8a599ab040..380be2781554 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -697,8 +697,7 @@ AndroidGeckoEvent::MakeTouchEvent(nsIWidget* widget) int startIndex = 0; int endIndex = Count(); - int action = Action() & AndroidMotionEvent::ACTION_MASK; - switch (action) { + switch (Action()) { case AndroidMotionEvent::ACTION_DOWN: case AndroidMotionEvent::ACTION_POINTER_DOWN: { type = NS_TOUCH_START; diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h index a94b28df27ca..f7251bfc3436 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -546,7 +546,6 @@ class AndroidMotionEvent { public: enum { - ACTION_MASK = 0xff, ACTION_DOWN = 0, ACTION_UP = 1, ACTION_MOVE = 2, @@ -560,8 +559,6 @@ public: ACTION_MAGNIFY_START = 11, ACTION_MAGNIFY = 12, ACTION_MAGNIFY_END = 13, - ACTION_POINTER_ID_MASK = 0xff00, - ACTION_POINTER_ID_SHIFT = 8, EDGE_TOP = 0x00000001, EDGE_BOTTOM = 0x00000002, EDGE_LEFT = 0x00000004, diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 9fda5dcdb34f..c99200c4dc1f 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1128,7 +1128,7 @@ void nsWindow::OnMouseEvent(AndroidGeckoEvent *ae) { uint32_t msg; - switch (ae->Action() & AndroidMotionEvent::ACTION_MASK) { + switch (ae->Action()) { #ifndef MOZ_ONLY_TOUCH_EVENTS case AndroidMotionEvent::ACTION_DOWN: msg = NS_MOUSE_BUTTON_DOWN; @@ -1250,7 +1250,7 @@ nsWindow::OnNativeGestureEvent(AndroidGeckoEvent *ae) double delta = ae->X(); int msg = 0; - switch (ae->Action() & AndroidMotionEvent::ACTION_MASK) { + switch (ae->Action()) { case AndroidMotionEvent::ACTION_MAGNIFY_START: msg = NS_SIMPLE_GESTURE_MAGNIFY_START; mStartDist = delta; From 6294aa50b5db5e1d0b1412df7c5658c72cb306fc Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 17 Apr 2013 17:39:11 -0400 Subject: [PATCH 26/75] Bug 859962 - Guard against LayerView.getPanZoomController() returning null in BrowserApp. r=Cwiiis --- mobile/android/base/BrowserApp.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index c5f0d5b0052e..447f3823d329 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -10,6 +10,7 @@ import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.gfx.BitmapUtils; import org.mozilla.gecko.gfx.ImmutableViewportMetrics; import org.mozilla.gecko.gfx.LayerView; +import org.mozilla.gecko.gfx.PanZoomController; import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.GamepadUtils; import org.mozilla.gecko.util.HardwareUtils; @@ -338,9 +339,10 @@ abstract public class BrowserApp extends GeckoApp action == MotionEvent.ACTION_CANCEL) { // Animate the toolbar to fully on or off, depending on how much // of it is hidden and the current swipe velocity. + PanZoomController pzc = mLayerView.getPanZoomController(); + float yVelocity = (pzc == null ? 0.0f : pzc.getVelocityVector().y); mBrowserToolbar.animateVisibilityWithVelocityBias( - toolbarY > toolbarHeight / 2 ? false : true, - mLayerView.getPanZoomController().getVelocityVector().y); + toolbarY > toolbarHeight / 2 ? false : true, yVelocity); } // Update the last recorded position. From 418439dffede9e54771dc5267844c1c317199f7f Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 17 Apr 2013 17:39:12 -0400 Subject: [PATCH 27/75] Bug 860940 - Add static factory-style methods to create AndroidGeckoEvent instances in widget code. r=cpeterson --- widget/android/AndroidJNI.cpp | 2 +- widget/android/AndroidJavaWrappers.cpp | 14 +------ widget/android/AndroidJavaWrappers.h | 57 +++++++++++++++++--------- widget/android/nsAppShell.cpp | 8 ++-- widget/android/nsWindow.cpp | 9 ++-- 5 files changed, 49 insertions(+), 41 deletions(-) diff --git a/widget/android/AndroidJNI.cpp b/widget/android/AndroidJNI.cpp index b73dc5a8dc7e..cf239d3e9ef2 100644 --- a/widget/android/AndroidJNI.cpp +++ b/widget/android/AndroidJNI.cpp @@ -61,7 +61,7 @@ Java_org_mozilla_gecko_GeckoAppShell_notifyGeckoOfEvent(JNIEnv *jenv, jclass jc, { // poke the appshell if (nsAppShell::gAppShell) - nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(jenv, event)); + nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeFromJavaObject(jenv, event)); } NS_EXPORT void JNICALL diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index 380be2781554..3bbb46c00e37 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -498,11 +498,9 @@ AndroidGeckoEvent::ReadCharactersExtraField(JNIEnv *jenv) } void -AndroidGeckoEvent::Init(int aType, nsIntRect const& aRect) +AndroidGeckoEvent::UnionRect(nsIntRect const& aRect) { - mType = aType; - mAckNeeded = false; - mRect = aRect; + mRect = aRect.Union(mRect); } uint32_t @@ -671,14 +669,6 @@ AndroidGeckoEvent::Init(int aType) mAckNeeded = false; } -void -AndroidGeckoEvent::Init(int aType, int aAction) -{ - mType = aType; - mAckNeeded = false; - mAction = aAction; -} - void AndroidGeckoEvent::Init(AndroidGeckoEvent *aResizeEvent) { diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h index f7251bfc3436..8548ea8738c9 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -589,31 +589,49 @@ public: class AndroidGeckoEvent : public WrappedJavaObject { -public: - static void InitGeckoEventClass(JNIEnv *jEnv); - - AndroidGeckoEvent(int aType) { - Init(aType); - } - AndroidGeckoEvent(int aType, int aAction) { - Init(aType, aAction); - } - AndroidGeckoEvent(int aType, const nsIntRect &aRect) { - Init(aType, aRect); - } - AndroidGeckoEvent(JNIEnv *jenv, jobject jobj) { - Init(jenv, jobj); - } - AndroidGeckoEvent(AndroidGeckoEvent *aResizeEvent) { - Init(aResizeEvent); +private: + AndroidGeckoEvent() { } void Init(JNIEnv *jenv, jobject jobj); void Init(int aType); - void Init(int aType, int aAction); - void Init(int aType, const nsIntRect &aRect); void Init(AndroidGeckoEvent *aResizeEvent); +public: + static void InitGeckoEventClass(JNIEnv *jEnv); + + static AndroidGeckoEvent* MakeNativePoke() { + AndroidGeckoEvent *event = new AndroidGeckoEvent(); + event->Init(NATIVE_POKE); + return event; + } + + static AndroidGeckoEvent* MakeIMEEvent(int aAction) { + AndroidGeckoEvent *event = new AndroidGeckoEvent(); + event->Init(IME_EVENT); + event->mAction = aAction; + return event; + } + + static AndroidGeckoEvent* MakeDrawEvent(const nsIntRect& aRect) { + AndroidGeckoEvent *event = new AndroidGeckoEvent(); + event->Init(DRAW); + event->mRect = aRect; + return event; + } + + static AndroidGeckoEvent* MakeFromJavaObject(JNIEnv *jenv, jobject jobj) { + AndroidGeckoEvent *event = new AndroidGeckoEvent(); + event->Init(jenv, jobj); + return event; + } + + static AndroidGeckoEvent* CopyResizeEvent(AndroidGeckoEvent *aResizeEvent) { + AndroidGeckoEvent *event = new AndroidGeckoEvent(); + event->Init(aResizeEvent); + return event; + } + int Action() { return mAction; } int Type() { return mType; } bool AckNeeded() { return mAckNeeded; } @@ -659,6 +677,7 @@ public: int Width() { return mWidth; } int Height() { return mHeight; } nsTouchEvent MakeTouchEvent(nsIWidget* widget); + void UnionRect(nsIntRect const& aRect); protected: int mAction; diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index d0a27ee857a8..eac90d841673 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -269,7 +269,7 @@ nsAppShell::ScheduleNativeEventCallback() EVLOG("nsAppShell::ScheduleNativeEventCallback pth: %p thread: %p main: %d", (void*) pthread_self(), (void*) NS_GetCurrentThread(), NS_IsMainThread()); // this is valid to be called from any thread, so do so. - PostEvent(new AndroidGeckoEvent(AndroidGeckoEvent::NATIVE_POKE)); + PostEvent(AndroidGeckoEvent::MakeNativePoke()); } bool @@ -467,7 +467,7 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) case AndroidGeckoEvent::SIZE_CHANGED: { // store the last resize event to dispatch it to new windows with a FORCED_RESIZE event if (curEvent != gLastSizeChange) { - gLastSizeChange = new AndroidGeckoEvent(curEvent); + gLastSizeChange = AndroidGeckoEvent::CopyResizeEvent(curEvent); } nsWindow::OnGlobalAndroidEvent(curEvent); break; @@ -603,12 +603,12 @@ nsAppShell::PostEvent(AndroidGeckoEvent *ae) case AndroidGeckoEvent::DRAW: if (mQueuedDrawEvent) { +#if defined(DEBUG) || defined(FORCE_ALOG) // coalesce this new draw event with the one already in the queue const nsIntRect& oldRect = mQueuedDrawEvent->Rect(); const nsIntRect& newRect = ae->Rect(); nsIntRect combinedRect = oldRect.Union(newRect); -#if defined(DEBUG) || defined(FORCE_ALOG) // XXX We may want to consider using regions instead of rectangles. // Print an error if we're upload a lot more than we would // if we handled this as two separate events. @@ -623,7 +623,7 @@ nsAppShell::PostEvent(AndroidGeckoEvent *ae) // coalesce into the new draw event rather than the queued one because // it is not always safe to move draws earlier in the queue; there may // be events between the two draws that affect scroll position or something. - ae->Init(AndroidGeckoEvent::DRAW, combinedRect); + ae->UnionRect(mQueuedDrawEvent->Rect()); EVLOG("nsAppShell: Coalescing previous DRAW event at %p into new DRAW event %p", mQueuedDrawEvent, ae); mEventQueue.RemoveElement(mQueuedDrawEvent); diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index c99200c4dc1f..b5a25e282df6 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -514,7 +514,7 @@ nsWindow::IsEnabled() const NS_IMETHODIMP nsWindow::Invalidate(const nsIntRect &aRect) { - AndroidGeckoEvent *event = new AndroidGeckoEvent(AndroidGeckoEvent::DRAW, aRect); + AndroidGeckoEvent *event = AndroidGeckoEvent::MakeDrawEvent(aRect); nsAppShell::gAppShell->PostEvent(event); return NS_OK; } @@ -2038,8 +2038,7 @@ nsWindow::SetInputContext(const InputContext& aContext, if (mIMEUpdatingContext) { return; } - AndroidGeckoEvent *event = new AndroidGeckoEvent( - AndroidGeckoEvent::IME_EVENT, + AndroidGeckoEvent *event = AndroidGeckoEvent::MakeIMEEvent( AndroidGeckoEvent::IME_UPDATE_CONTEXT); nsAppShell::gAppShell->PostEvent(event); mIMEUpdatingContext = true; @@ -2068,8 +2067,8 @@ nsWindow::PostFlushIMEChanges() // Already posted return; } - AndroidGeckoEvent *event = new AndroidGeckoEvent( - AndroidGeckoEvent::IME_EVENT, AndroidGeckoEvent::IME_FLUSH_CHANGES); + AndroidGeckoEvent *event = AndroidGeckoEvent::MakeIMEEvent( + AndroidGeckoEvent::IME_FLUSH_CHANGES); nsAppShell::gAppShell->PostEvent(event); } From 07c6f3a6d841f3243222da2e3bfd3fee9183cc85 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 17 Apr 2013 17:39:12 -0400 Subject: [PATCH 28/75] Bug 860613 - Remove CompositorParent::GetDefaultPanZoomController (patch from romaxa/tatiana). r=BenWa --- gfx/layers/ipc/CompositorParent.cpp | 3 --- gfx/layers/ipc/CompositorParent.h | 4 ---- 2 files changed, 7 deletions(-) diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index d77fabf682e1..56cbc87cd546 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -859,9 +859,6 @@ CompositorParent::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame, // Check if an AsyncPanZoomController is attached to this layer. if (LayerUserData* data = aLayer->GetUserData(&sPanZoomUserDataKey)) { controller = static_cast(data)->mController; - } else { - // Check if a derived implementation provides a default AsyncPanZoomController. - controller = GetDefaultPanZoomController(); } if (controller) { diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index 28935e0d600c..373da54b3544 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -181,10 +181,6 @@ protected: nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY, gfx::Margin& aFixedLayerMargins); void SetEGLSurfaceSize(int width, int height); - // If SetPanZoomControllerForLayerTree is not set, Compositor will use - // derived class AsyncPanZoomController transformations. - // Compositor will not own AsyncPanZoomController here. - virtual AsyncPanZoomController* GetDefaultPanZoomController() { return nullptr; } private: void PauseComposition(); From df9cf426fc58c2c7b6d829fde958c4bf8426e8d2 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 17 Apr 2013 17:39:13 -0400 Subject: [PATCH 29/75] Bug 860613 - Add an API to bind an APZC instance to a Layer. r=BenWa --- gfx/layers/Layers.cpp | 32 +++++++++++++++++++++++++++++ gfx/layers/Layers.h | 6 ++++++ gfx/layers/ipc/CompositorParent.cpp | 25 +++------------------- 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index 7a2e0bac0b61..73da2b330dad 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -398,6 +398,38 @@ Layer::SetAnimations(const AnimationArray& aAnimations) Mutated(); } +static uint8_t sPanZoomUserDataKey; +struct PanZoomUserData : public LayerUserData { + PanZoomUserData(AsyncPanZoomController* aController) + : mController(aController) + { } + + // We don't keep a strong ref here because PanZoomUserData is only + // set transiently, and APZC is thread-safe refcounted so + // AddRef/Release is expensive. + AsyncPanZoomController* mController; +}; + +void +Layer::SetAsyncPanZoomController(AsyncPanZoomController *controller) +{ + if (controller) { + SetUserData(&sPanZoomUserDataKey, new PanZoomUserData(controller)); + } else { + RemoveUserData(&sPanZoomUserDataKey); + } +} + +AsyncPanZoomController* +Layer::GetAsyncPanZoomController() +{ + LayerUserData* data = GetUserData(&sPanZoomUserDataKey); + if (!data) { + return nullptr; + } + return static_cast(data)->mController; +} + void Layer::ApplyPendingUpdatesToSubtree() { diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 4d8d589acb39..0d3cd5178ff9 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -48,6 +48,7 @@ namespace layers { class Animation; class AnimationData; +class AsyncPanZoomController; class CommonLayerAttributes; class Layer; class ThebesLayer; @@ -896,6 +897,11 @@ public: const gfx::Margin& GetFixedPositionMargins() { return mMargins; } Layer* GetMaskLayer() { return mMaskLayer; } + // These functions allow attaching an AsyncPanZoomController to this layer, + // and can be used anytime. + void SetAsyncPanZoomController(AsyncPanZoomController *controller); + AsyncPanZoomController* GetAsyncPanZoomController(); + // Note that all lengths in animation data are either in CSS pixels or app // units and must be converted to device pixels by the compositor. AnimationArray& GetAnimations() { return mAnimations; } diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index 56cbc87cd546..b18c383bac1e 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -67,18 +67,6 @@ struct LayerTreeState { TargetConfig mTargetConfig; }; -static uint8_t sPanZoomUserDataKey; -struct PanZoomUserData : public LayerUserData { - PanZoomUserData(AsyncPanZoomController* aController) - : mController(aController) - { } - - // We don't keep a strong ref here because PanZoomUserData is only - // set transiently, and APZC is thread-safe refcounted so - // AddRef/Release is expensive. - AsyncPanZoomController* mController; -}; - /** * Lookup the indirect shadow tree for |aId| and return it if it * exists. Otherwise null is returned. This must only be called on @@ -504,12 +492,11 @@ private: if (OP == Resolve) { ref->ConnectReferentLayer(referent); if (AsyncPanZoomController* apzc = state->mController) { - referent->SetUserData(&sPanZoomUserDataKey, - new PanZoomUserData(apzc)); + referent->SetAsyncPanZoomController(apzc); } } else { ref->DetachReferentLayer(referent); - referent->RemoveUserData(&sPanZoomUserDataKey); + referent->SetAsyncPanZoomController(nullptr); } } } @@ -855,13 +842,7 @@ CompositorParent::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame, return appliedTransform; } - AsyncPanZoomController* controller = nullptr; - // Check if an AsyncPanZoomController is attached to this layer. - if (LayerUserData* data = aLayer->GetUserData(&sPanZoomUserDataKey)) { - controller = static_cast(data)->mController; - } - - if (controller) { + if (AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController()) { ShadowLayer* shadow = aLayer->AsShadowLayer(); ViewTransform treeTransform; From ff617de66bb6c0f3cd89399e88ca42f41d001cef Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 17 Apr 2013 17:39:13 -0400 Subject: [PATCH 30/75] Bug 860613 - Create an android-specific subclass of CompositorParent to be able to pick the target layer for APZC. r=BenWa --- widget/android/nsWindow.cpp | 52 +++++++++++++++++++++++++++++++ widget/android/nsWindow.h | 6 ++++ widget/xpwidgets/nsBaseWidget.cpp | 13 ++++---- widget/xpwidgets/nsBaseWidget.h | 1 + 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index b5a25e282df6..bf6f0878c423 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -62,8 +62,10 @@ NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget) static gfxIntSize gAndroidBounds = gfxIntSize(0, 0); static gfxIntSize gAndroidScreenBounds; +#include "mozilla/layers/AsyncPanZoomController.h" #include "mozilla/layers/CompositorChild.h" #include "mozilla/layers/CompositorParent.h" +#include "mozilla/layers/ShadowLayersParent.h" #include "mozilla/Mutex.h" #include "nsThreadUtils.h" @@ -2241,6 +2243,7 @@ nsRefPtr nsWindow::sLayerManager = 0; nsRefPtr nsWindow::sCompositorParent = 0; nsRefPtr nsWindow::sCompositorChild = 0; bool nsWindow::sCompositorPaused = true; +nsRefPtr nsWindow::sApzc = 0; void nsWindow::SetCompositor(mozilla::layers::LayerManager* aLayerManager, @@ -2315,4 +2318,53 @@ nsWindow::NeedsPaint() return nsIWidget::NeedsPaint(); } +class AndroidCompositorParent : public mozilla::layers::CompositorParent { +public: + AndroidCompositorParent(nsIWidget* aWidget, bool aRenderToEGLSurface, + int aSurfaceWidth, int aSurfaceHeight) + : CompositorParent(aWidget, aRenderToEGLSurface, aSurfaceHeight, aSurfaceHeight) + { + if (nsWindow::GetPanZoomController()) { + nsWindow::GetPanZoomController()->SetCompositorParent(this); + } + } + + virtual void ShadowLayersUpdated(mozilla::layers::ShadowLayersParent* aLayerTree, + const mozilla::layers::TargetConfig& aTargetConfig, + bool isFirstPaint) MOZ_OVERRIDE + { + CompositorParent::ShadowLayersUpdated(aLayerTree, aTargetConfig, isFirstPaint); + mozilla::layers::Layer* targetLayer = GetLayerManager()->GetPrimaryScrollableLayer(); + mozilla::layers::AsyncPanZoomController* controller = nsWindow::GetPanZoomController(); + if (targetLayer && targetLayer->AsContainerLayer() && controller) { + targetLayer->SetAsyncPanZoomController(controller); + controller->NotifyLayersUpdated(targetLayer->AsContainerLayer()->GetFrameMetrics(), isFirstPaint); + } + } +}; + +mozilla::layers::CompositorParent* +nsWindow::NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight) +{ + return new AndroidCompositorParent(this, true, aSurfaceWidth, aSurfaceHeight); +} + +void +nsWindow::SetPanZoomController(mozilla::layers::AsyncPanZoomController* apzc) +{ + if (sApzc) { + sApzc->SetCompositorParent(nullptr); + sApzc = nullptr; + } + if (apzc) { + sApzc = apzc; + sApzc->SetCompositorParent(sCompositorParent); + } +} + +mozilla::layers::AsyncPanZoomController* +nsWindow::GetPanZoomController() +{ + return sApzc; +} diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index 32559dd252d7..f379cc5043ef 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -24,6 +24,7 @@ namespace mozilla { class CompositorParent; class CompositorChild; class LayerManager; + class AsyncPanZoomController; } } @@ -145,6 +146,8 @@ public: virtual void DrawWindowUnderlay(LayerManager* aManager, nsIntRect aRect); virtual void DrawWindowOverlay(LayerManager* aManager, nsIntRect aRect); + virtual mozilla::layers::CompositorParent* NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight) MOZ_OVERRIDE; + static void SetCompositor(mozilla::layers::LayerManager* aLayerManager, mozilla::layers::CompositorParent* aCompositorParent, mozilla::layers::CompositorChild* aCompositorChild); @@ -152,6 +155,8 @@ public: static void ScheduleResumeComposition(int width, int height); static void ForceIsFirstPaint(); static float ComputeRenderIntegrity(); + static void SetPanZoomController(mozilla::layers::AsyncPanZoomController* apzc); + static mozilla::layers::AsyncPanZoomController* GetPanZoomController(); virtual bool WidgetPaintsBackground(); @@ -230,6 +235,7 @@ private: static nsRefPtr sCompositorParent; static nsRefPtr sCompositorChild; static bool sCompositorPaused; + static nsRefPtr sApzc; }; #endif /* NSWINDOW_H_ */ diff --git a/widget/xpwidgets/nsBaseWidget.cpp b/widget/xpwidgets/nsBaseWidget.cpp index c300373e7a5f..043aaab81ea4 100644 --- a/widget/xpwidgets/nsBaseWidget.cpp +++ b/widget/xpwidgets/nsBaseWidget.cpp @@ -872,6 +872,12 @@ nsBaseWidget::ComputeShouldAccelerate(bool aDefault) return aDefault; } +CompositorParent* nsBaseWidget::NewCompositorParent(int aSurfaceWidth, + int aSurfaceHeight) +{ + return new CompositorParent(this, false, aSurfaceWidth, aSurfaceHeight); +} + void nsBaseWidget::CreateCompositor() { nsIntRect rect; @@ -881,12 +887,7 @@ void nsBaseWidget::CreateCompositor() void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) { - bool renderToEGLSurface = false; -#ifdef MOZ_ANDROID_OMTC - renderToEGLSurface = true; -#endif - mCompositorParent = - new CompositorParent(this, renderToEGLSurface, aWidth, aHeight); + mCompositorParent = NewCompositorParent(aWidth, aHeight); AsyncChannel *parentChannel = mCompositorParent->GetIPCChannel(); LayerManager* lm = CreateBasicLayerManager(); MessageLoop *childMessageLoop = CompositorParent::CompositorLoop(); diff --git a/widget/xpwidgets/nsBaseWidget.h b/widget/xpwidgets/nsBaseWidget.h index 9e11c7bcda83..b99440dd6c9a 100644 --- a/widget/xpwidgets/nsBaseWidget.h +++ b/widget/xpwidgets/nsBaseWidget.h @@ -110,6 +110,7 @@ public: LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT, bool* aAllowRetaining = nullptr); + virtual CompositorParent* NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight); virtual void CreateCompositor(); virtual void CreateCompositor(int aWidth, int aHeight); virtual void DrawWindowUnderlay(LayerManager* aManager, nsIntRect aRect) {} From 980a377da58a1d9e18d6beadb5aab47f6020d2e6 Mon Sep 17 00:00:00 2001 From: Panos Astithas Date: Thu, 18 Apr 2013 00:27:24 +0300 Subject: [PATCH 31/75] Try to fix intermittent test_dbgsocket.js (bug 775924); r=me --- toolkit/devtools/debugger/tests/unit/test_dbgsocket.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/devtools/debugger/tests/unit/test_dbgsocket.js b/toolkit/devtools/debugger/tests/unit/test_dbgsocket.js index 0c67efdf2937..2ba8c5365da4 100644 --- a/toolkit/devtools/debugger/tests/unit/test_dbgsocket.js +++ b/toolkit/devtools/debugger/tests/unit/test_dbgsocket.js @@ -77,7 +77,7 @@ function test_socket_shutdown() }, onClosed: function(aStatus) { - do_check_eq(aStatus, Cr.NS_ERROR_CONNECTION_REFUSED); + do_check_eq(aStatus, Components.results.NS_ERROR_CONNECTION_REFUSED); run_next_test(); } }; From aa5209f6943a6fad43f31e95a82a4632ff19785d Mon Sep 17 00:00:00 2001 From: Brian Nicholson Date: Wed, 17 Apr 2013 14:51:53 -0700 Subject: [PATCH 32/75] Bug 862049 - Run setAccessibilityEnabled() on the UI thread. r=Cwiiis --- mobile/android/base/GeckoAccessibility.java | 25 ++++++++++++--------- mobile/android/base/GeckoApp.java | 4 ++-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/mobile/android/base/GeckoAccessibility.java b/mobile/android/base/GeckoAccessibility.java index 5e4685b3893b..92d51f487aa4 100644 --- a/mobile/android/base/GeckoAccessibility.java +++ b/mobile/android/base/GeckoAccessibility.java @@ -7,6 +7,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.util.ThreadUtils; +import org.mozilla.gecko.util.UiAsyncTask; import org.json.JSONArray; import org.json.JSONObject; @@ -47,17 +48,17 @@ public class GeckoAccessibility { "es.codefactory.android.app.ma.MAAccessibilityService" // Codefactory Mobile Accessibility screen reader })); - public static void updateAccessibilitySettings () { - ThreadUtils.postToBackgroundThread(new Runnable() { + public static void updateAccessibilitySettings (final GeckoApp app) { + new UiAsyncTask(ThreadUtils.getBackgroundHandler()) { @Override - public void run() { + public Void doInBackground(Void... args) { JSONObject ret = new JSONObject(); sEnabled = false; AccessibilityManager accessibilityManager = - (AccessibilityManager) GeckoApp.mAppContext.getSystemService(Context.ACCESSIBILITY_SERVICE); + (AccessibilityManager) app.getSystemService(Context.ACCESSIBILITY_SERVICE); if (accessibilityManager.isEnabled()) { ActivityManager activityManager = - (ActivityManager) GeckoApp.mAppContext.getSystemService(Context.ACTIVITY_SERVICE); + (ActivityManager) app.getSystemService(Context.ACTIVITY_SERVICE); List runningServices = activityManager.getRunningServices(Integer.MAX_VALUE); for (RunningServiceInfo runningServiceInfo : runningServices) { @@ -67,10 +68,6 @@ public class GeckoAccessibility { } } - // Disable the dynamic toolbar when enabling accessibility. - // These features tend not to interact well. - GeckoApp.mAppContext.setAccessibilityEnabled(sEnabled); - try { ret.put("enabled", sEnabled); } catch (Exception ex) { @@ -79,8 +76,16 @@ public class GeckoAccessibility { GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:Settings", ret.toString())); + return null; } - }); + + @Override + public void onPostExecute(Void args) { + // Disable the dynamic toolbar when enabling accessibility. + // These features tend not to interact well. + app.setAccessibilityEnabled(sEnabled); + } + }.execute(); } private static void populateEventFromJSON (AccessibilityEvent event, JSONObject message) { diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index aa3669279d1c..73be8862be55 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -791,7 +791,7 @@ abstract public class GeckoApp } else if (event.equals("Accessibility:Event")) { GeckoAccessibility.sendAccessibilityEvent(message); } else if (event.equals("Accessibility:Ready")) { - GeckoAccessibility.updateAccessibilitySettings(); + GeckoAccessibility.updateAccessibilitySettings(this); } else if (event.equals("Shortcut:Remove")) { final String url = message.getString("url"); final String origin = message.getString("origin"); @@ -1903,7 +1903,7 @@ abstract public class GeckoApp GeckoScreenOrientationListener.getInstance().start(); // User may have enabled/disabled accessibility. - GeckoAccessibility.updateAccessibilitySettings(); + GeckoAccessibility.updateAccessibilitySettings(this); ThreadUtils.postToBackgroundThread(new Runnable() { @Override From 94262c1579a2241cb97cbc710b270ac3b2799601 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Wed, 17 Apr 2013 18:00:14 -0400 Subject: [PATCH 33/75] Bug 840012 - Fix test name. r=luke --HG-- rename : js/src/jit-test/tests/basic/testBug0012.js => js/src/jit-test/tests/basic/testBug840012.js --- js/src/jit-test/tests/basic/{testBug0012.js => testBug840012.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename js/src/jit-test/tests/basic/{testBug0012.js => testBug840012.js} (100%) diff --git a/js/src/jit-test/tests/basic/testBug0012.js b/js/src/jit-test/tests/basic/testBug840012.js similarity index 100% rename from js/src/jit-test/tests/basic/testBug0012.js rename to js/src/jit-test/tests/basic/testBug840012.js From 2c6dd08daf62efb15716b807e038cbb4300ea34b Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Wed, 17 Apr 2013 18:00:14 -0400 Subject: [PATCH 34/75] Bug 847279 - Fix incorrect relativesrcdir in other mochitest makefiles. r=mbrubeck --- dom/imptests/Makefile.in | 2 +- dom/permission/tests/Makefile.in | 2 +- dom/plugins/test/mochitest/Makefile.in | 2 +- dom/tests/mochitest/crypto/Makefile.in | 2 +- layout/base/tests/chrome/Makefile.in | 2 +- security/manager/ssl/tests/mochitest/bugs/Makefile.in | 2 +- security/manager/ssl/tests/mochitest/mixedcontent/Makefile.in | 2 +- .../ssl/tests/mochitest/stricttransportsecurity/Makefile.in | 2 +- .../components/places/tests/mochitest/bug_411966/Makefile.in | 2 +- .../components/places/tests/mochitest/bug_461710/Makefile.in | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dom/imptests/Makefile.in b/dom/imptests/Makefile.in index ee1693503333..332c8967f760 100644 --- a/dom/imptests/Makefile.in +++ b/dom/imptests/Makefile.in @@ -6,7 +6,7 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ -relativesrcdir = dom/imported-tests +relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk diff --git a/dom/permission/tests/Makefile.in b/dom/permission/tests/Makefile.in index 9f8a8bdec7d9..af97961cd4de 100644 --- a/dom/permission/tests/Makefile.in +++ b/dom/permission/tests/Makefile.in @@ -7,7 +7,7 @@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ -relativesrcdir = dom/permission/tests +relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk diff --git a/dom/plugins/test/mochitest/Makefile.in b/dom/plugins/test/mochitest/Makefile.in index 51e95374ae26..b9c4d65cb0cb 100644 --- a/dom/plugins/test/mochitest/Makefile.in +++ b/dom/plugins/test/mochitest/Makefile.in @@ -7,7 +7,7 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ -relativesrcdir = dom/plugins/test +relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk diff --git a/dom/tests/mochitest/crypto/Makefile.in b/dom/tests/mochitest/crypto/Makefile.in index 494c85ab3805..ce2e344a677d 100644 --- a/dom/tests/mochitest/crypto/Makefile.in +++ b/dom/tests/mochitest/crypto/Makefile.in @@ -6,7 +6,7 @@ DEPTH = ../../../.. topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ -relativesrcdir = dom/tests/mochitest/crypto +relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk diff --git a/layout/base/tests/chrome/Makefile.in b/layout/base/tests/chrome/Makefile.in index 591358eef435..b7f155c51d7c 100644 --- a/layout/base/tests/chrome/Makefile.in +++ b/layout/base/tests/chrome/Makefile.in @@ -6,7 +6,7 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ -relativesrcdir = layout/base/test/chrome +relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk diff --git a/security/manager/ssl/tests/mochitest/bugs/Makefile.in b/security/manager/ssl/tests/mochitest/bugs/Makefile.in index 8981ea47a8d9..d08b4551b44e 100644 --- a/security/manager/ssl/tests/mochitest/bugs/Makefile.in +++ b/security/manager/ssl/tests/mochitest/bugs/Makefile.in @@ -7,7 +7,7 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ -relativesrcdir = security/ssl/bugs +relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/Makefile.in b/security/manager/ssl/tests/mochitest/mixedcontent/Makefile.in index 6c89bba737a6..582ff18ba911 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/Makefile.in +++ b/security/manager/ssl/tests/mochitest/mixedcontent/Makefile.in @@ -7,7 +7,7 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ -relativesrcdir = security/ssl/mixedcontent +relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/Makefile.in b/security/manager/ssl/tests/mochitest/stricttransportsecurity/Makefile.in index e431550c2bc7..1dde9f22d25f 100644 --- a/security/manager/ssl/tests/mochitest/stricttransportsecurity/Makefile.in +++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/Makefile.in @@ -6,7 +6,7 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ -relativesrcdir = security/ssl/stricttransportsecurity +relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk diff --git a/toolkit/components/places/tests/mochitest/bug_411966/Makefile.in b/toolkit/components/places/tests/mochitest/bug_411966/Makefile.in index 8d4991e3e04f..69bdc35452f3 100644 --- a/toolkit/components/places/tests/mochitest/bug_411966/Makefile.in +++ b/toolkit/components/places/tests/mochitest/bug_411966/Makefile.in @@ -7,7 +7,7 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ -relativesrcdir = toolkit/components/places/tests/bug_411966 +relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk diff --git a/toolkit/components/places/tests/mochitest/bug_461710/Makefile.in b/toolkit/components/places/tests/mochitest/bug_461710/Makefile.in index 090b499b7228..aac587d78a81 100644 --- a/toolkit/components/places/tests/mochitest/bug_461710/Makefile.in +++ b/toolkit/components/places/tests/mochitest/bug_461710/Makefile.in @@ -7,7 +7,7 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ -relativesrcdir = toolkit/components/places/tests/bug_461710 +relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk From 1f13fb9ca7c50a129cbdd72946e986b5cae03ce4 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Wed, 17 Apr 2013 18:00:14 -0400 Subject: [PATCH 35/75] Bug 847279 - Fix hardcoded path names in mochitests to match relativesrcdir. r=mbrubeck --- dom/plugins/test/mochitest/307-xo-redirect.sjs | 2 +- dom/plugins/test/mochitest/test_busy_hang.xul | 2 +- dom/plugins/test/mochitest/test_idle_hang.xul | 2 +- .../test/mochitest/test_privatemode_perwindowpb.xul | 4 +++- dom/plugins/test/mochitest/test_redirect_handling.html | 2 +- .../test/mochitest/xulbrowser_plugin_visibility.xul | 2 +- layout/base/tests/chrome/test_bug551434.html | 2 +- .../ssl/tests/mochitest/mixedcontent/bug383369step2.html | 2 +- .../manager/ssl/tests/mochitest/mixedcontent/iframe2.html | 2 +- .../tests/mochitest/mixedcontent/iframeMetaRedirect.html | 2 +- .../tests/mochitest/mixedcontent/iframesecredirect.sjs | 2 +- .../tests/mochitest/mixedcontent/iframeunsecredirect.sjs | 2 +- .../ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs | 2 +- .../ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs | 2 +- .../ssl/tests/mochitest/mixedcontent/mixedContentTest.js | 4 ++-- .../tests/mochitest/mixedcontent/redirecttoemptyimage.sjs | 2 +- .../ssl/tests/mochitest/mixedcontent/test_bug329869.html | 2 +- .../ssl/tests/mochitest/mixedcontent/test_bug455367.html | 2 +- .../ssl/tests/mochitest/mixedcontent/test_bug472986.html | 4 ++-- .../ssl/tests/mochitest/mixedcontent/test_bug521461.html | 2 +- .../ssl/tests/mochitest/mixedcontent/test_cssBefore1.html | 2 +- .../tests/mochitest/mixedcontent/test_cssContent1.html | 2 +- .../tests/mochitest/mixedcontent/test_cssContent2.html | 2 +- .../tests/mochitest/mixedcontent/test_documentWrite1.html | 2 +- .../tests/mochitest/mixedcontent/test_documentWrite2.html | 2 +- .../mixedcontent/test_dynDelayedUnsecurePicture.html | 6 +++--- .../mixedcontent/test_dynDelayedUnsecureXHR.html | 2 +- .../mixedcontent/test_dynUnsecureBackground.html | 6 +++--- .../mixedcontent/test_dynUnsecureIframeRedirect.html | 4 ++-- .../mochitest/mixedcontent/test_dynUnsecurePicture.html | 6 +++--- .../mixedcontent/test_dynUnsecurePicturePreload.html | 2 +- .../mochitest/mixedcontent/test_dynUnsecureRedirect.html | 6 +++--- .../test_innerHtmlDelayedUnsecurePicture.html | 2 +- .../mixedcontent/test_innerHtmlUnsecurePicture.html | 2 +- .../ssl/tests/mochitest/mixedcontent/test_secureAll.html | 8 ++++---- .../tests/mochitest/mixedcontent/test_securePicture.html | 2 +- .../mochitest/mixedcontent/test_unsecureBackground.html | 2 +- .../tests/mochitest/mixedcontent/test_unsecureCSS.html | 2 +- .../tests/mochitest/mixedcontent/test_unsecureIframe.html | 2 +- .../mochitest/mixedcontent/test_unsecureIframe2.html | 2 +- .../mixedcontent/test_unsecureIframeMetaRedirect.html | 2 +- .../mixedcontent/test_unsecureIframeRedirect.html | 2 +- .../mochitest/mixedcontent/test_unsecurePicture.html | 2 +- .../mixedcontent/test_unsecurePictureInIframe.html | 2 +- .../mochitest/mixedcontent/test_unsecureRedirect.html | 2 +- .../ssl/tests/mochitest/mixedcontent/unsecureIframe.html | 2 +- .../tests/mochitest/mixedcontent/unsecurePictureDup.html | 2 +- .../test_stricttransportsecurity.html | 2 +- .../test_sts_privatebrowsing_perwindowpb.html | 4 ++-- .../places/tests/mochitest/bug_411966/redirect.js | 8 ++++---- .../places/tests/mochitest/test_bug_411966.html | 2 +- .../tests/mochitest/test_bug_461710_perwindowpb.html | 4 ++-- 52 files changed, 73 insertions(+), 71 deletions(-) diff --git a/dom/plugins/test/mochitest/307-xo-redirect.sjs b/dom/plugins/test/mochitest/307-xo-redirect.sjs index 639623c140c5..b880978cea80 100644 --- a/dom/plugins/test/mochitest/307-xo-redirect.sjs +++ b/dom/plugins/test/mochitest/307-xo-redirect.sjs @@ -1,6 +1,6 @@ function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 307, "Moved temporarily"); - response.setHeader("Location", "http://example.org/tests/dom/plugins/test/loremipsum.txt"); + response.setHeader("Location", "http://example.org/tests/dom/plugins/test/mochitest/loremipsum.txt"); response.setHeader("Content-Type", "text/html"); } diff --git a/dom/plugins/test/mochitest/test_busy_hang.xul b/dom/plugins/test/mochitest/test_busy_hang.xul index c93623810524..f4842c07e921 100644 --- a/dom/plugins/test/mochitest/test_busy_hang.xul +++ b/dom/plugins/test/mochitest/test_busy_hang.xul @@ -9,7 +9,7 @@ - + diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html b/security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html index ccc3bddef131..d033f5b00be0 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html @@ -1,6 +1,6 @@ + Content="0; URL=http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html"> Redirecting by meta tag... diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs index ab58786d9240..ea93b80b7d58 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs +++ b/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs @@ -1,5 +1,5 @@ function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 307, "Moved temporarly"); - response.setHeader("Location", "https://example.com/tests/security/ssl/mixedcontent/iframe.html"); + response.setHeader("Location", "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html"); } diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs index 5cb51671e1a4..937e29954f3f 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs +++ b/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs @@ -1,5 +1,5 @@ function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 307, "Moved temporarly"); - response.setHeader("Location", "http://example.com/tests/security/ssl/mixedcontent/iframe.html"); + response.setHeader("Location", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html"); } diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs index 1a5bcc62d1b6..7af6116e1018 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs +++ b/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs @@ -1,5 +1,5 @@ function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 307, "Moved temporarly"); - response.setHeader("Location", "https://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg"); + response.setHeader("Location", "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg"); } diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs index ba15359c1bad..a8eb26642ae8 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs +++ b/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs @@ -1,5 +1,5 @@ function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 307, "Moved temporarly"); - response.setHeader("Location", "http://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg"); + response.setHeader("Location", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg"); } diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js b/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js index 73c3495a0c55..26142541affe 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js +++ b/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js @@ -128,8 +128,8 @@ function finish() window.setTimeout(function() { window.location.assign(navigateToInsecure ? - "http://example.com/tests/security/ssl/mixedcontent/backward.html" : - "https://example.com/tests/security/ssl/mixedcontent/backward.html"); + "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/backward.html" : + "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/backward.html"); }, 0); } else diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs index 1f9a520f5162..4d85e1658171 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs +++ b/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs @@ -1,5 +1,5 @@ function handleRequest(request, response) { response.setStatusLine(request.httpVersion, 307, "Moved temporarly"); - response.setHeader("Location", "http://example.com/tests/security/ssl/mixedcontent/emptyimage.sjs"); + response.setHeader("Location", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs"); } diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug329869.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug329869.html index 889b84c130e1..49d5c41c633d 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug329869.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug329869.html @@ -16,7 +16,7 @@ window.setTimeout(function() { var newElement = document.createElement("script"); - newElement.src= "http://example.org/tests/security/ssl/mixedcontent/bug329869.js"; + newElement.src= "http://example.org/tests/security/manager/ssl/tests/mochitest/mixedcontent/bug329869.js"; document.body.appendChild(newElement); }, 0); } diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html index 3b57e92a8e80..c0b6e3ebbe60 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html @@ -28,6 +28,6 @@ - + diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html index 176c4725af0b..427e0fd70f42 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html @@ -21,7 +21,7 @@ { var img1 = document.getElementById("img1"); img1.addEventListener("load", onLoadFunction, false); - img1.src = "https://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg"; + img1.src = "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg"; } function runTest() @@ -40,6 +40,6 @@ - + diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug521461.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug521461.html index 9b06e08e3f52..9d9d4de09fe6 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug521461.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug521461.html @@ -16,7 +16,7 @@ function runTest() { - window.location = "https://example.com/tests/security/ssl/mixedcontent/nocontent.sjs"; + window.location = "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/nocontent.sjs"; window.setTimeout(function() { isSecurityState("insecure", "location.href doesn't effect the security state"); is(document.body.innerHTML, "This is an unsecure page!", "Document has not changed content"); diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssBefore1.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssBefore1.html index a4a24167877a..59d2fa7a9dbb 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssBefore1.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssBefore1.html @@ -12,7 +12,7 @@ diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent1.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent1.html index 1b347d435c9c..347382000a0d 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent1.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent1.html @@ -13,7 +13,7 @@ diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent2.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent2.html index aa5e813bc451..dfcd7dd76199 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent2.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent2.html @@ -20,7 +20,7 @@ { isSecurityState("secure"); document.getElementById("para").style.content = - "url('http://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg')"; + "url('http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg')"; waitForSecurityState("broken", function() { diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite1.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite1.html index 7e2356b009f1..b95a5caca349 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite1.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite1.html @@ -30,7 +30,7 @@ diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite2.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite2.html index 61fec12dcef1..a92288b7acdd 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite2.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite2.html @@ -32,7 +32,7 @@ diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecurePicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecurePicture.html index a441b1045fb2..c0684718faae 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecurePicture.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecurePicture.html @@ -18,7 +18,7 @@ window.setTimeout(function() { // Don't do this synchronously from onload handler document.getElementById("image1").src = - "http://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg"; + "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg"; }, 0); waitForSecurityState("broken", function() @@ -31,7 +31,7 @@ function afterNavigationTest() { is(document.getElementById("image1").src, - "https://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg", + "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg", "img.src secure again"); isSecurityState("secure", "security full after navigation"); finish(); @@ -41,6 +41,6 @@ - + diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecureXHR.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecureXHR.html index c77053b6a8b5..d566a35947f7 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecureXHR.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecureXHR.html @@ -22,7 +22,7 @@ try { var req = new XMLHttpRequest(); - req.open("GET", "http://example.com/tests/security/ssl/mixedcontent/alloworigin.sjs", false); + req.open("GET", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/alloworigin.sjs", false); req.send(null); // Change should be immediate, the request was sent synchronously diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html index 4ab5cc3df776..352ae52efc66 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html @@ -18,7 +18,7 @@ { isSecurityState("secure"); document.body.background = - "http://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg"; + "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg"; waitForSecurityState("broken", function() { @@ -30,7 +30,7 @@ function afterNavigationTest() { is(document.body.background, - "https://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg", + "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg", "document backround secure again"); isSecurityState("secure", "secure after re-navigation"); finish(); @@ -39,6 +39,6 @@ - + diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html index 9dfb31a2244c..a9967228b4c5 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html @@ -25,7 +25,7 @@ } iframe.src = - "https://example.com/tests/security/ssl/mixedcontent/iframeunsecredirect.sjs"; + "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs"; } function afterNavigationTest() @@ -38,6 +38,6 @@ - + diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicture.html index b11189693910..aca7be9d1195 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicture.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicture.html @@ -18,7 +18,7 @@ { isSecurityState("secure"); document.getElementById("image1").src = - "http://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg"; + "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg"; window.setTimeout(function() { isSecurityState("broken", "src='http://...' changed to broken"); @@ -29,7 +29,7 @@ function afterNavigationTest() { is(document.getElementById("image1").src, - "https://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg", + "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg", "img.src secure again"); isSecurityState("secure", "security full after navigation"); finish(); @@ -39,6 +39,6 @@ - + diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicturePreload.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicturePreload.html index e379730faacc..916245c303bd 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicturePreload.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicturePreload.html @@ -13,7 +13,7 @@ - + diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureCSS.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureCSS.html index d868ce34cb40..8985fc7d85d2 100644 --- a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureCSS.html +++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureCSS.html @@ -11,7 +11,7 @@ + href="http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/somestyle.css" /> - + diff --git a/toolkit/components/places/tests/mochitest/test_bug_461710_perwindowpb.html b/toolkit/components/places/tests/mochitest/test_bug_461710_perwindowpb.html index 48f5b9cc31c0..58d2b215d122 100644 --- a/toolkit/components/places/tests/mochitest/test_bug_461710_perwindowpb.html +++ b/toolkit/components/places/tests/mochitest/test_bug_461710_perwindowpb.html @@ -60,7 +60,7 @@ function waitForTrue(fn, onWaitComplete) { const kRed = "rgb(255, 0, 0)"; const kBlue = "rgb(0, 0, 255)"; -var testpath = "/tests/toolkit/components/places/tests/mochitest/../bug_461710/"; +var testpath = "/tests/toolkit/components/places/tests/mochitest/bug_461710/"; var prefix = "http://mochi.test:8888" + testpath; var subtests = [ "visited_page.html", // 1 @@ -163,7 +163,7 @@ var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor) .rootTreeItem .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindow); -var contentPage = "http://mochi.test:8888/tests/toolkit/components/places/tests/mochitest/../bug_461710/iframe.html"; +var contentPage = "http://mochi.test:8888/tests/toolkit/components/places/tests/mochitest/bug_461710/iframe.html"; function testOnWindow(aIsPrivate, aCallback) { var win = mainWindow.OpenBrowserWindow({private: aIsPrivate}); From 05b690b216a86a209773cf8626af7b282722624f Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Wed, 17 Apr 2013 15:34:26 -0700 Subject: [PATCH 36/75] Bug 863043: Move CompositorOGL::Initialize's helper-nsRunnable from function-scope to class-scope to fix build warning about ignored visibility attribute. r=Bas --- gfx/layers/opengl/CompositorOGL.cpp | 20 +++++++++++--------- gfx/layers/opengl/CompositorOGL.h | 6 ++++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp index bbccca900d6f..082001c4944c 100644 --- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -311,6 +311,16 @@ CompositorOGL::CleanupResources() mGLContext = nullptr; } +// Impl of a a helper-runnable's "Run" method, used in Initialize() +NS_IMETHODIMP +CompositorOGL::ReadDrawFPSPref::Run() +{ + // NOTE: This must match the code in Initialize()'s NS_IsMainThread check. + Preferences::AddBoolVarCache(&sDrawFPS, "layers.acceleration.draw-fps"); + Preferences::AddBoolVarCache(&sFrameCounter, "layers.acceleration.frame-counter"); + return NS_OK; +} + bool CompositorOGL::Initialize() { @@ -481,19 +491,11 @@ CompositorOGL::Initialize() } if (NS_IsMainThread()) { + // NOTE: This must match the code in ReadDrawFPSPref::Run(). Preferences::AddBoolVarCache(&sDrawFPS, "layers.acceleration.draw-fps"); Preferences::AddBoolVarCache(&sFrameCounter, "layers.acceleration.frame-counter"); } else { // We have to dispatch an event to the main thread to read the pref. - class ReadDrawFPSPref : public nsRunnable { - public: - NS_IMETHOD Run() - { - Preferences::AddBoolVarCache(&sDrawFPS, "layers.acceleration.draw-fps"); - Preferences::AddBoolVarCache(&sFrameCounter, "layers.acceleration.frame-counter"); - return NS_OK; - } - }; NS_DispatchToMainThread(new ReadDrawFPSPref()); } diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h index 43c965d15289..5959d57354f4 100644 --- a/gfx/layers/opengl/CompositorOGL.h +++ b/gfx/layers/opengl/CompositorOGL.h @@ -141,6 +141,12 @@ private: /** The size of the surface we are rendering to */ nsIntSize mSurfaceSize; + /** Helper-class used by Initialize **/ + class ReadDrawFPSPref MOZ_FINAL : public nsRunnable { + public: + NS_IMETHOD Run() MOZ_OVERRIDE; + }; + already_AddRefed CreateContext(); /** Shader Programs */ From 69fb03370e9b3c5ae0ff458140691f1fa0d06b2e Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Wed, 10 Apr 2013 15:03:50 +1200 Subject: [PATCH 37/75] Bug 846122 - Handle split multi-track Cues in nestegg. r=padenot --- media/libnestegg/README_MOZILLA | 2 +- media/libnestegg/src/nestegg.c | 71 ++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/media/libnestegg/README_MOZILLA b/media/libnestegg/README_MOZILLA index 49ac6a10434d..d58c4d8c06ba 100644 --- a/media/libnestegg/README_MOZILLA +++ b/media/libnestegg/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The nestegg git repository is: git://github.com/kinetiknz/nestegg.git -The git commit ID used was ebdbb688fb13dd315fc9d16e6897adb5ee42b7bb. +The git commit ID used was 7d0f6d22b5a332ce47b95bc19dc259f204d339e1. diff --git a/media/libnestegg/src/nestegg.c b/media/libnestegg/src/nestegg.c index 59a1a4734c77..c6d685f2db97 100644 --- a/media/libnestegg/src/nestegg.c +++ b/media/libnestegg/src/nestegg.c @@ -1406,8 +1406,34 @@ ne_find_seek_for_id(struct ebml_list_node * seek_head, uint64_t id) return NULL; } +static struct cue_track_positions * +ne_find_cue_position_for_track(nestegg * ctx, struct ebml_list_node * node, unsigned int track) +{ + struct cue_track_positions * pos = NULL; + uint64_t track_number; + unsigned int t; + + while (node) { + assert(node->id == ID_CUE_TRACK_POSITIONS); + pos = node->data; + if (ne_get_uint(pos->track, &track_number) != 0) + return NULL; + + if (ne_map_track_number_to_index(ctx, track_number, &t) != 0) + return NULL; + + if (t == track) { + return pos; + } + + node = node->next; + } + + return NULL; +} + static struct cue_point * -ne_find_cue_point_for_tstamp(struct ebml_list_node * cue_point, uint64_t scale, uint64_t tstamp) +ne_find_cue_point_for_tstamp(nestegg * ctx, struct ebml_list_node * cue_point, unsigned int track, uint64_t scale, uint64_t tstamp) { uint64_t time; struct cue_point * c, * prev = NULL; @@ -1422,7 +1448,9 @@ ne_find_cue_point_for_tstamp(struct ebml_list_node * cue_point, uint64_t scale, if (ne_get_uint(c->time, &time) == 0 && time * scale > tstamp) break; - prev = cue_point->data; + if (ne_find_cue_position_for_track(ctx, c->cue_track_positions.head, track) != NULL) + prev = c; + cue_point = cue_point->next; } @@ -1506,7 +1534,6 @@ ne_init_cue_points(nestegg * ctx, int64_t max_offset) return 0; } - /* Three functions that implement the nestegg_io interface, operating on a * sniff_buffer. */ struct sniff_buffer { @@ -1831,49 +1858,29 @@ nestegg_track_seek(nestegg * ctx, unsigned int track, uint64_t tstamp) int r; struct cue_point * cue_point; struct cue_track_positions * pos; - uint64_t seek_pos, tc_scale, track_number; - unsigned int t; - struct ebml_list_node * node = ctx->segment.cues.cue_point.head; + uint64_t seek_pos, tc_scale; /* If there are no cues loaded, check for cues element in the seek head and load it. */ - if (!node) { + if (!ctx->segment.cues.cue_point.head) { r = ne_init_cue_points(ctx, -1); if (r != 0) return -1; - - /* Check cues were loaded. */ - node = ctx->segment.cues.cue_point.head; - if (!node) - return -1; } tc_scale = ne_get_timecode_scale(ctx); - cue_point = ne_find_cue_point_for_tstamp(ctx->segment.cues.cue_point.head, tc_scale, tstamp); + cue_point = ne_find_cue_point_for_tstamp(ctx, ctx->segment.cues.cue_point.head, + track, tc_scale, tstamp); if (!cue_point) return -1; - node = cue_point->cue_track_positions.head; + pos = ne_find_cue_position_for_track(ctx, cue_point->cue_track_positions.head, track); + if (pos == NULL) + return -1; - seek_pos = 0; - - while (node) { - assert(node->id == ID_CUE_TRACK_POSITIONS); - pos = node->data; - if (ne_get_uint(pos->track, &track_number) != 0) - return -1; - - if (ne_map_track_number_to_index(ctx, track_number, &t) != 0) - return -1; - - if (t == track) { - if (ne_get_uint(pos->cluster_position, &seek_pos) != 0) - return -1; - break; - } - node = node->next; - } + if (ne_get_uint(pos->cluster_position, &seek_pos) != 0) + return -1; /* Seek and set up parser state for segment-level element (Cluster). */ r = nestegg_offset_seek(ctx, ctx->segment_offset + seek_pos); From ece74ec63fa41d52114a526ecf63b2486850596a Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 18 Apr 2013 09:15:09 +1000 Subject: [PATCH 38/75] Bug 862673 - ensure assertions in test_mousecapture_area are attributed to that test rather than the following test. r=gavin --- toolkit/content/tests/widgets/test_mousecapture_area.html | 6 +++++- toolkit/content/tests/widgets/test_videocontrols.html | 2 -- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/toolkit/content/tests/widgets/test_mousecapture_area.html b/toolkit/content/tests/widgets/test_mousecapture_area.html index b91748329240..660cbb2bcfae 100644 --- a/toolkit/content/tests/widgets/test_mousecapture_area.html +++ b/toolkit/content/tests/widgets/test_mousecapture_area.html @@ -52,7 +52,7 @@
 
 
-  
+  
 
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html b/security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html
index d033f5b00be0..ccc3bddef131 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html
@@ -1,6 +1,6 @@
 
 
+      Content="0; URL=http://example.com/tests/security/ssl/mixedcontent/iframe.html">
 
   
     Redirecting by meta tag...
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs
index ea93b80b7d58..ab58786d9240 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs
@@ -1,5 +1,5 @@
 function handleRequest(request, response)
 {
   response.setStatusLine(request.httpVersion, 307, "Moved temporarly");
-  response.setHeader("Location", "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html");
+  response.setHeader("Location", "https://example.com/tests/security/ssl/mixedcontent/iframe.html");
 }
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs
index 937e29954f3f..5cb51671e1a4 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs
@@ -1,5 +1,5 @@
 function handleRequest(request, response)
 {
   response.setStatusLine(request.httpVersion, 307, "Moved temporarly");
-  response.setHeader("Location", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html");
+  response.setHeader("Location", "http://example.com/tests/security/ssl/mixedcontent/iframe.html");
 }
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs
index 7af6116e1018..1a5bcc62d1b6 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs
@@ -1,5 +1,5 @@
 function handleRequest(request, response)
 {
   response.setStatusLine(request.httpVersion, 307, "Moved temporarly");
-  response.setHeader("Location", "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg");
+  response.setHeader("Location", "https://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg");
 }
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs
index a8eb26642ae8..ba15359c1bad 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs
@@ -1,5 +1,5 @@
 function handleRequest(request, response)
 {
   response.setStatusLine(request.httpVersion, 307, "Moved temporarly");
-  response.setHeader("Location", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg");
+  response.setHeader("Location", "http://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg");
 }
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js b/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
index 26142541affe..73c3495a0c55 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
@@ -128,8 +128,8 @@ function finish()
     window.setTimeout(function()
     {
       window.location.assign(navigateToInsecure ?
-        "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/backward.html" :
-        "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/backward.html");
+        "http://example.com/tests/security/ssl/mixedcontent/backward.html" :
+        "https://example.com/tests/security/ssl/mixedcontent/backward.html");
     }, 0);
   }
   else
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs
index 4d85e1658171..1f9a520f5162 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs
@@ -1,5 +1,5 @@
 function handleRequest(request, response)
 {
   response.setStatusLine(request.httpVersion, 307, "Moved temporarly");
-  response.setHeader("Location", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs");
+  response.setHeader("Location", "http://example.com/tests/security/ssl/mixedcontent/emptyimage.sjs");
 }
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug329869.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug329869.html
index 49d5c41c633d..889b84c130e1 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug329869.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug329869.html
@@ -16,7 +16,7 @@
     window.setTimeout(function() 
     {
       var newElement = document.createElement("script");
-      newElement.src= "http://example.org/tests/security/manager/ssl/tests/mochitest/mixedcontent/bug329869.js";
+      newElement.src= "http://example.org/tests/security/ssl/mixedcontent/bug329869.js";
       document.body.appendChild(newElement);
     }, 0);
   }
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html
index c0b6e3ebbe60..3b57e92a8e80 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html
@@ -28,6 +28,6 @@
 
 
 
-  
+  
 
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html
index 427e0fd70f42..176c4725af0b 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html
@@ -21,7 +21,7 @@
   {
     var img1 = document.getElementById("img1");
     img1.addEventListener("load", onLoadFunction, false);
-    img1.src = "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg";
+    img1.src = "https://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg";
   }
 
   function runTest()
@@ -40,6 +40,6 @@
 
 
 
-  
+  
 
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug521461.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug521461.html
index 9d9d4de09fe6..9b06e08e3f52 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug521461.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug521461.html
@@ -16,7 +16,7 @@
 
   function runTest()
   {
-    window.location = "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/nocontent.sjs";
+    window.location = "https://example.com/tests/security/ssl/mixedcontent/nocontent.sjs";
     window.setTimeout(function() {
       isSecurityState("insecure", "location.href doesn't effect the security state");
       is(document.body.innerHTML, "This is an unsecure page!", "Document has not changed content");
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssBefore1.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssBefore1.html
index 59d2fa7a9dbb..a4a24167877a 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssBefore1.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssBefore1.html
@@ -12,7 +12,7 @@
   
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent1.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent1.html
index 347382000a0d..1b347d435c9c 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent1.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent1.html
@@ -13,7 +13,7 @@
   
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent2.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent2.html
index dfcd7dd76199..aa5e813bc451 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent2.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent2.html
@@ -20,7 +20,7 @@
   {
     isSecurityState("secure");
     document.getElementById("para").style.content =
-      "url('http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg')";
+      "url('http://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg')";
 
     waitForSecurityState("broken", function()
     {
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite1.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite1.html
index b95a5caca349..7e2356b009f1 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite1.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite1.html
@@ -30,7 +30,7 @@
 
   
 
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite2.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite2.html
index a92288b7acdd..61fec12dcef1 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite2.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite2.html
@@ -32,7 +32,7 @@
 
   
 
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecurePicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecurePicture.html
index c0684718faae..a441b1045fb2 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecurePicture.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecurePicture.html
@@ -18,7 +18,7 @@
     window.setTimeout(function() {
       // Don't do this synchronously from onload handler
       document.getElementById("image1").src =
-        "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg";
+        "http://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg";
     }, 0);
 
     waitForSecurityState("broken", function()
@@ -31,7 +31,7 @@
   function afterNavigationTest()
   {
     is(document.getElementById("image1").src,
-      "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg",
+      "https://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg",
       "img.src secure again");
     isSecurityState("secure", "security full after navigation");
     finish();
@@ -41,6 +41,6 @@
 
 
 
-  
+  
 
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecureXHR.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecureXHR.html
index d566a35947f7..c77053b6a8b5 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecureXHR.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecureXHR.html
@@ -22,7 +22,7 @@
       try
       {
         var req = new XMLHttpRequest();
-        req.open("GET", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/alloworigin.sjs", false);
+        req.open("GET", "http://example.com/tests/security/ssl/mixedcontent/alloworigin.sjs", false);
         req.send(null);
 
         // Change should be immediate, the request was sent synchronously
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html
index 352ae52efc66..4ab5cc3df776 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html
@@ -18,7 +18,7 @@
   {
     isSecurityState("secure");
     document.body.background =
-      "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg";
+      "http://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg";
 
     waitForSecurityState("broken", function() 
     {
@@ -30,7 +30,7 @@
   function afterNavigationTest()
   {
     is(document.body.background,
-      "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg",
+      "https://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg",
       "document backround secure again");
     isSecurityState("secure", "secure after re-navigation");
     finish();
@@ -39,6 +39,6 @@
   
 
 
-
+
 
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html
index a9967228b4c5..9dfb31a2244c 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html
@@ -25,7 +25,7 @@
     }
     
     iframe.src =
-      "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs";
+      "https://example.com/tests/security/ssl/mixedcontent/iframeunsecredirect.sjs";
   }
 
   function afterNavigationTest()
@@ -38,6 +38,6 @@
 
 
 
-  
+  
 
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicture.html
index aca7be9d1195..b11189693910 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicture.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicture.html
@@ -18,7 +18,7 @@
   {
     isSecurityState("secure");
     document.getElementById("image1").src =
-      "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg";
+      "http://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg";
 
     window.setTimeout(function() {
       isSecurityState("broken", "src='http://...' changed to broken");
@@ -29,7 +29,7 @@
   function afterNavigationTest()
   {
     is(document.getElementById("image1").src,
-      "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg",
+      "https://example.com/tests/security/ssl/mixedcontent/moonsurface.jpg",
       "img.src secure again");
     isSecurityState("secure", "security full after navigation");
     finish();
@@ -39,6 +39,6 @@
 
 
 
-  
+  
 
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicturePreload.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicturePreload.html
index 916245c303bd..e379730faacc 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicturePreload.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicturePreload.html
@@ -13,7 +13,7 @@
   
 
 
-
+
 
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureCSS.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureCSS.html
index 8985fc7d85d2..d868ce34cb40 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureCSS.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureCSS.html
@@ -11,7 +11,7 @@
   
   
   
+    href="http://example.com/tests/security/ssl/mixedcontent/somestyle.css" />
 
   
-    
+    
     
   
   
diff --git a/toolkit/components/places/tests/mochitest/test_bug_461710_perwindowpb.html b/toolkit/components/places/tests/mochitest/test_bug_461710_perwindowpb.html
index 58d2b215d122..48f5b9cc31c0 100644
--- a/toolkit/components/places/tests/mochitest/test_bug_461710_perwindowpb.html
+++ b/toolkit/components/places/tests/mochitest/test_bug_461710_perwindowpb.html
@@ -60,7 +60,7 @@ function waitForTrue(fn, onWaitComplete) {
 const kRed = "rgb(255, 0, 0)";
 const kBlue = "rgb(0, 0, 255)";
 
-var testpath = "/tests/toolkit/components/places/tests/mochitest/bug_461710/";
+var testpath = "/tests/toolkit/components/places/tests/mochitest/../bug_461710/";
 var prefix = "http://mochi.test:8888" + testpath;
 var subtests = [
                    "visited_page.html",   // 1
@@ -163,7 +163,7 @@ var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
                     .rootTreeItem
                     .QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindow);
-var contentPage = "http://mochi.test:8888/tests/toolkit/components/places/tests/mochitest/bug_461710/iframe.html";
+var contentPage = "http://mochi.test:8888/tests/toolkit/components/places/tests/mochitest/../bug_461710/iframe.html";
 
 function testOnWindow(aIsPrivate, aCallback) {
   var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});

From d5c07891687283a34476dbac0376d1923de1e566 Mon Sep 17 00:00:00 2001
From: Daniel Holbert 
Date: Wed, 17 Apr 2013 17:13:02 -0700
Subject: [PATCH 44/75] backout 8e0af273404e (Bug 847279) for android M-8
 orange

---
 dom/imptests/Makefile.in                                        | 2 +-
 dom/permission/tests/Makefile.in                                | 2 +-
 dom/plugins/test/mochitest/Makefile.in                          | 2 +-
 dom/tests/mochitest/crypto/Makefile.in                          | 2 +-
 layout/base/tests/chrome/Makefile.in                            | 2 +-
 security/manager/ssl/tests/mochitest/bugs/Makefile.in           | 2 +-
 security/manager/ssl/tests/mochitest/mixedcontent/Makefile.in   | 2 +-
 .../ssl/tests/mochitest/stricttransportsecurity/Makefile.in     | 2 +-
 .../components/places/tests/mochitest/bug_411966/Makefile.in    | 2 +-
 .../components/places/tests/mochitest/bug_461710/Makefile.in    | 2 +-
 10 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/dom/imptests/Makefile.in b/dom/imptests/Makefile.in
index 332c8967f760..ee1693503333 100644
--- a/dom/imptests/Makefile.in
+++ b/dom/imptests/Makefile.in
@@ -6,7 +6,7 @@ DEPTH = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
-relativesrcdir = @relativesrcdir@
+relativesrcdir = dom/imported-tests
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
diff --git a/dom/permission/tests/Makefile.in b/dom/permission/tests/Makefile.in
index af97961cd4de..9f8a8bdec7d9 100644
--- a/dom/permission/tests/Makefile.in
+++ b/dom/permission/tests/Makefile.in
@@ -7,7 +7,7 @@ topsrcdir        = @top_srcdir@
 srcdir           = @srcdir@
 VPATH            = @srcdir@
 
-relativesrcdir   = @relativesrcdir@
+relativesrcdir   = dom/permission/tests
 
 include $(DEPTH)/config/autoconf.mk
 
diff --git a/dom/plugins/test/mochitest/Makefile.in b/dom/plugins/test/mochitest/Makefile.in
index b9c4d65cb0cb..51e95374ae26 100644
--- a/dom/plugins/test/mochitest/Makefile.in
+++ b/dom/plugins/test/mochitest/Makefile.in
@@ -7,7 +7,7 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
-relativesrcdir  = @relativesrcdir@
+relativesrcdir  = dom/plugins/test
 
 include $(DEPTH)/config/autoconf.mk
 
diff --git a/dom/tests/mochitest/crypto/Makefile.in b/dom/tests/mochitest/crypto/Makefile.in
index ce2e344a677d..494c85ab3805 100644
--- a/dom/tests/mochitest/crypto/Makefile.in
+++ b/dom/tests/mochitest/crypto/Makefile.in
@@ -6,7 +6,7 @@ DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
-relativesrcdir	= @relativesrcdir@
+relativesrcdir	= dom/tests/mochitest/crypto
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
diff --git a/layout/base/tests/chrome/Makefile.in b/layout/base/tests/chrome/Makefile.in
index b7f155c51d7c..591358eef435 100644
--- a/layout/base/tests/chrome/Makefile.in
+++ b/layout/base/tests/chrome/Makefile.in
@@ -6,7 +6,7 @@ DEPTH     = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
-relativesrcdir  = @relativesrcdir@
+relativesrcdir  = layout/base/test/chrome
 
 include $(DEPTH)/config/autoconf.mk
 
diff --git a/security/manager/ssl/tests/mochitest/bugs/Makefile.in b/security/manager/ssl/tests/mochitest/bugs/Makefile.in
index d08b4551b44e..8981ea47a8d9 100644
--- a/security/manager/ssl/tests/mochitest/bugs/Makefile.in
+++ b/security/manager/ssl/tests/mochitest/bugs/Makefile.in
@@ -7,7 +7,7 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
-relativesrcdir	= @relativesrcdir@
+relativesrcdir	= security/ssl/bugs
 
 include $(DEPTH)/config/autoconf.mk
 
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/Makefile.in b/security/manager/ssl/tests/mochitest/mixedcontent/Makefile.in
index 582ff18ba911..6c89bba737a6 100644
--- a/security/manager/ssl/tests/mochitest/mixedcontent/Makefile.in
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/Makefile.in
@@ -7,7 +7,7 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
-relativesrcdir	= @relativesrcdir@
+relativesrcdir	= security/ssl/mixedcontent
 
 include $(DEPTH)/config/autoconf.mk
 
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/Makefile.in b/security/manager/ssl/tests/mochitest/stricttransportsecurity/Makefile.in
index 1dde9f22d25f..e431550c2bc7 100644
--- a/security/manager/ssl/tests/mochitest/stricttransportsecurity/Makefile.in
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/Makefile.in
@@ -6,7 +6,7 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
-relativesrcdir	= @relativesrcdir@
+relativesrcdir	= security/ssl/stricttransportsecurity
 
 include $(DEPTH)/config/autoconf.mk
 
diff --git a/toolkit/components/places/tests/mochitest/bug_411966/Makefile.in b/toolkit/components/places/tests/mochitest/bug_411966/Makefile.in
index 69bdc35452f3..8d4991e3e04f 100644
--- a/toolkit/components/places/tests/mochitest/bug_411966/Makefile.in
+++ b/toolkit/components/places/tests/mochitest/bug_411966/Makefile.in
@@ -7,7 +7,7 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
-relativesrcdir = @relativesrcdir@
+relativesrcdir = toolkit/components/places/tests/bug_411966
 
 include $(DEPTH)/config/autoconf.mk
 
diff --git a/toolkit/components/places/tests/mochitest/bug_461710/Makefile.in b/toolkit/components/places/tests/mochitest/bug_461710/Makefile.in
index aac587d78a81..090b499b7228 100644
--- a/toolkit/components/places/tests/mochitest/bug_461710/Makefile.in
+++ b/toolkit/components/places/tests/mochitest/bug_461710/Makefile.in
@@ -7,7 +7,7 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
-relativesrcdir = @relativesrcdir@
+relativesrcdir = toolkit/components/places/tests/bug_461710
 
 include $(DEPTH)/config/autoconf.mk
 

From e0b70017ca397ff061bb66b370256f6dab23a1ea Mon Sep 17 00:00:00 2001
From: Sriram Ramasubramanian 
Date: Wed, 17 Apr 2013 17:18:03 -0700
Subject: [PATCH 45/75] Bug 861658 - Make Android Sync styles use Gecko themes.
 r=nalexander

This avoids using "@android:style/TextAppearance" or
"@android:style/{TextView,Button,EditText}" and avoids a crash on
pre-Gingerbread devices.
---
 mobile/android/base/resources/values/sync_styles.xml | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/mobile/android/base/resources/values/sync_styles.xml b/mobile/android/base/resources/values/sync_styles.xml
index 3f08a2bd77cf..db41525ad68e 100644
--- a/mobile/android/base/resources/values/sync_styles.xml
+++ b/mobile/android/base/resources/values/sync_styles.xml
@@ -16,14 +16,14 @@
   
 
   
-  
-  
   
-  
 
-  
 
   
-