From 6f362db5ba394d5fb8de394f714ebb98fac0e5ff Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Tue, 30 Aug 2016 12:34:22 +0200 Subject: [PATCH 01/65] Bug 1298225 - Format clipboard text of console stack traces into multiple lines r=bgrins MozReview-Commit-ID: HkCFEwjhCwx --HG-- extra : rebase_source : ecee3d3d1a5abd3fe63f63bc29649b6028c11ac3 --- devtools/client/shared/components/frame.js | 5 +- .../client/shared/components/stack-trace.js | 8 +- devtools/client/webconsole/console-output.js | 6 +- ...onsole_copy_entire_message_context_menu.js | 98 ++++++++++++------- .../client/webconsole/test/test-console.html | 7 ++ 5 files changed, 81 insertions(+), 43 deletions(-) diff --git a/devtools/client/shared/components/frame.js b/devtools/client/shared/components/frame.js index d673ccc053a9..7070994b61fe 100644 --- a/devtools/client/shared/components/frame.js +++ b/devtools/client/shared/components/frame.js @@ -176,7 +176,8 @@ module.exports = createClass({ if (functionDisplayName) { elements.push( dom.span({ className: "frame-link-function-display-name" }, - functionDisplayName) + functionDisplayName), + " " ); } } @@ -236,7 +237,7 @@ module.exports = createClass({ elements.push(sourceEl); if (showHost && host) { - elements.push(dom.span({ className: "frame-link-host" }, host)); + elements.push(" ", dom.span({ className: "frame-link-host" }, host)); } return dom.span(attributes, ...elements); diff --git a/devtools/client/shared/components/stack-trace.js b/devtools/client/shared/components/stack-trace.js index 9818d36d106e..511b4762618d 100644 --- a/devtools/client/shared/components/stack-trace.js +++ b/devtools/client/shared/components/stack-trace.js @@ -42,12 +42,12 @@ const StackTrace = createClass({ let frames = []; stacktrace.forEach(s => { if (s.asyncCause) { - frames.push(AsyncFrame({ + frames.push("\t", AsyncFrame({ asyncCause: s.asyncCause - })); + }), "\n"); } - frames.push(Frame({ + frames.push("\t", Frame({ frame: { functionDisplayName: s.functionName, source: s.filename.split(" -> ").pop(), @@ -58,7 +58,7 @@ const StackTrace = createClass({ showAnonymousFunctionName: true, showFullSourceUrl: true, onClick: onViewSourceInDebugger - })); + }), "\n"); }); return dom.div({ className: "stack-trace" }, frames); diff --git a/devtools/client/webconsole/console-output.js b/devtools/client/webconsole/console-output.js index 46bc29650bb1..56ef65f956dd 100644 --- a/devtools/client/webconsole/console-output.js +++ b/devtools/client/webconsole/console-output.js @@ -935,8 +935,6 @@ Messages.Simple.prototype = extend(Messages.BaseMessage.prototype, { this.element.appendChild(body); - this.element.appendChild(this.document.createTextNode("\n")); - this.element.clipboardText = this.element.textContent; if (this.private) { @@ -993,12 +991,16 @@ Messages.Simple.prototype = extend(Messages.BaseMessage.prototype, { let location = this._renderLocation(); if (repeatNode) { + bodyFlex.appendChild(this.document.createTextNode(" ")); bodyFlex.appendChild(repeatNode); } if (location) { + bodyFlex.appendChild(this.document.createTextNode(" ")); bodyFlex.appendChild(location); } + bodyFlex.appendChild(this.document.createTextNode("\n")); + if (this.stack) { this._attachment = new Widgets.Stacktrace(this, this.stack).render().element; } diff --git a/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js b/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js index 966f4308afff..bdd4f71793f2 100644 --- a/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js +++ b/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js @@ -3,67 +3,95 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* globals goDoCommand */ + "use strict"; // Test copying of the entire console message when right-clicked // with no other text selected. See Bug 1100562. -function test() { +add_task(function* () { let hud; let outputNode; let contextMenu; - const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + - "test/test-console.html"; + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test/test-console.html"; - Task.spawn(runner).then(finishTest); + const { tab, browser } = yield loadTab(TEST_URI); + hud = yield openConsole(tab); + outputNode = hud.outputNode; + contextMenu = hud.iframeWindow.document.getElementById("output-contextmenu"); - function* runner() { - const {tab} = yield loadTab(TEST_URI); - hud = yield openConsole(tab); - outputNode = hud.outputNode; - contextMenu = hud.iframeWindow.document.getElementById("output-contextmenu"); + registerCleanupFunction(() => { + hud = outputNode = contextMenu = null; + }); - registerCleanupFunction(() => { - hud = outputNode = contextMenu = null; - }); + hud.jsterm.clearOutput(); - hud.jsterm.clearOutput(); - content.console.log("bug 1100562"); + yield ContentTask.spawn(browser, {}, function* () { + let button = content.document.getElementById("testTrace"); + button.click(); + }); - let [results] = yield waitForMessages({ - webconsole: hud, - messages: [{ + let results = yield waitForMessages({ + webconsole: hud, + messages: [ + { text: "bug 1100562", category: CATEGORY_WEBDEV, severity: SEVERITY_LOG, - }] - }); + lines: 1, + }, + { + name: "console.trace output", + consoleTrace: true, + lines: 3, + }, + ] + }); - outputNode.focus(); - let message = [...results.matched][0]; + outputNode.focus(); - yield waitForContextMenu(contextMenu, message, copyFromPopup, - testContextMenuCopy); + for (let result of results) { + let message = [...result.matched][0]; - function copyFromPopup() { + yield waitForContextMenu(contextMenu, message, () => { let copyItem = contextMenu.querySelector("#cMenu_copy"); copyItem.doCommand(); let controller = top.document.commandDispatcher .getControllerForCommand("cmd_copy"); is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled"); - } + }); - function testContextMenuCopy() { - waitForClipboard((str) => { - return message.textContent.trim() == str.trim(); - }, () => { - goDoCommand("cmd_copy"); - }, () => {}, () => {} - ); - } + let clipboardText; - yield closeConsole(tab); + yield waitForClipboardPromise( + () => goDoCommand("cmd_copy"), + (str) => { + clipboardText = str; + return message.textContent == clipboardText; + } + ); + + ok(clipboardText, "Clipboard text was found and saved"); + + let lines = clipboardText.split("\n"); + ok(lines.length > 0, "There is at least one newline in the message"); + is(lines.pop(), "", "There is a newline at the end"); + is(lines.length, result.lines, `There are ${result.lines} lines in the message`); + + // Test the first line for "timestamp message repeat file:line" + let firstLine = lines.shift(); + ok(/^[\d:.]+ .+ \d+ .+:\d+$/.test(firstLine), + "The message's first line has the right format"); + + // Test the remaining lines (stack trace) for "TABfunctionName sourceURL:line:col" + for (let line of lines) { + ok(/^\t.+ .+:\d+:\d+$/.test(line), "The stack trace line has the right format"); + } } -} + + yield closeConsole(tab); + yield finishTest(); +}); diff --git a/devtools/client/webconsole/test/test-console.html b/devtools/client/webconsole/test/test-console.html index 9731c86a9fd5..b294a3ba1cfd 100644 --- a/devtools/client/webconsole/test/test-console.html +++ b/devtools/client/webconsole/test/test-console.html @@ -13,6 +13,12 @@ console.log(str); } } + + function testTrace() { + console.log("bug 1100562"); + console.trace(); + } + console.info("INLINE SCRIPT:"); test(); console.warn("I'm warning you, he will eat up all yr bacon."); @@ -22,6 +28,7 @@

Heads Up Display Demo

+
From b8ed2ee25c60ff42b59c9cd6bc046d6ab14f55a2 Mon Sep 17 00:00:00 2001 From: Andreas Pehrson Date: Wed, 31 Aug 2016 14:07:53 +0200 Subject: [PATCH 02/65] Bug 1299172 - Assert on MSG thread in DispatchToMainThreadAfterStreamStateUpdate. r=jesup MozReview-Commit-ID: 9ihJduZOSa --HG-- extra : rebase_source : 1088fc98f97456c933f146565ba91ce0358e6a4c --- dom/media/MediaStreamGraph.cpp | 19 +++++++++++++++++++ dom/media/MediaStreamGraph.h | 3 +++ dom/media/MediaStreamGraphImpl.h | 20 ++------------------ 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp index 5254a9730344..acc4273b96e2 100644 --- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -1077,6 +1077,25 @@ MediaStreamGraph::NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames, } } +void +MediaStreamGraph::AssertOnGraphThreadOrNotRunning() const +{ + // either we're on the right thread (and calling CurrentDriver() is safe), + // or we're going to assert anyways, so don't cross-check CurrentDriver +#ifdef DEBUG + MediaStreamGraphImpl const * graph = + static_cast(this); + // if all the safety checks fail, assert we own the monitor + if (!graph->mDriver->OnThread()) { + if (!(graph->mDetectedNotRunning && + graph->mLifecycleState > MediaStreamGraphImpl::LIFECYCLE_RUNNING && + NS_IsMainThread())) { + graph->mMonitor.AssertCurrentThreadOwns(); + } + } +#endif +} + bool MediaStreamGraphImpl::ShouldUpdateMainThread() { diff --git a/dom/media/MediaStreamGraph.h b/dom/media/MediaStreamGraph.h index a9f852be213f..e606091b3850 100644 --- a/dom/media/MediaStreamGraph.h +++ b/dom/media/MediaStreamGraph.h @@ -1353,6 +1353,7 @@ public: */ virtual void DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed aRunnable) { + AssertOnGraphThreadOrNotRunning(); *mPendingUpdateRunnables.AppendElement() = aRunnable; } @@ -1374,6 +1375,8 @@ public: void NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames, TrackRate aRate, uint32_t aChannels); + void AssertOnGraphThreadOrNotRunning() const; + protected: explicit MediaStreamGraph(TrackRate aSampleRate) : mSampleRate(aSampleRate) diff --git a/dom/media/MediaStreamGraphImpl.h b/dom/media/MediaStreamGraphImpl.h index 7349a5876c97..72ba363ab101 100644 --- a/dom/media/MediaStreamGraphImpl.h +++ b/dom/media/MediaStreamGraphImpl.h @@ -202,24 +202,8 @@ public: nsISupports* aData, const nsTArray& aAudioStreamSizes); - // The following methods run on the graph thread (or possibly the main thread if - // mLifecycleState > LIFECYCLE_RUNNING) - void AssertOnGraphThreadOrNotRunning() const - { - // either we're on the right thread (and calling CurrentDriver() is safe), - // or we're going to assert anyways, so don't cross-check CurrentDriver -#ifdef DEBUG - // if all the safety checks fail, assert we own the monitor - if (!mDriver->OnThread()) { - if (!(mDetectedNotRunning && - mLifecycleState > LIFECYCLE_RUNNING && - NS_IsMainThread())) { - mMonitor.AssertCurrentThreadOwns(); - } - } -#endif - } - + // The following methods run on the graph thread (or possibly the main thread + // if mLifecycleState > LIFECYCLE_RUNNING) void CollectSizesForMemoryReport( already_AddRefed aHandleReport, already_AddRefed aHandlerData); From 864bcdc3c45b95d2536228826e1c81b0e3cd1c1a Mon Sep 17 00:00:00 2001 From: Andreas Pehrson Date: Wed, 31 Aug 2016 14:31:28 +0200 Subject: [PATCH 03/65] Bug 1299172 - Fix media element StreamSizeListener calling DispatchToMainThreadAfterStreamStateUpdate off-MSG-thread. r=jesup MozReview-Commit-ID: Cc2tJbEog20 --HG-- extra : rebase_source : 83731e3fe29a7813a51f70eff8a0f43a21f92e1d --- dom/html/HTMLMediaElement.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 9048ad93334a..1649f4e6218a 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -310,10 +310,13 @@ public: if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0,0)) { mInitialSizeFound = true; nsCOMPtr event = - NewRunnableMethod( - this, &StreamSizeListener::ReceivedSize, - c->mFrame.GetIntrinsicSize()); - aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget()); + NewRunnableMethod(this, &StreamSizeListener::ReceivedSize, + c->mFrame.GetIntrinsicSize()); + // This is fine to dispatch straight to main thread (instead of via + // ...AfterStreamUpdate()) since it reflects state of the element, + // not the stream. Events reflecting stream or track state should be + // dispatched so their order is preserved. + NS_DispatchToMainThread(event.forget()); return; } } From a91bec92ad14ebb789e3e24a1c7d1a574755a383 Mon Sep 17 00:00:00 2001 From: Andreas Pehrson Date: Wed, 31 Aug 2016 14:32:13 +0200 Subject: [PATCH 04/65] Bug 1299172 - HTMLMediaElement::StreamSizeListener spring cleaning. r=jesup MozReview-Commit-ID: 25lt1j8t1Xh --HG-- extra : rebase_source : e972d008c5e80059dd10031df38cf400cc6c9650 --- dom/html/HTMLMediaElement.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 1649f4e6218a..547e9c3c62aa 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -279,7 +279,7 @@ public: /** * This listener observes the first video frame to arrive with a non-empty size, - * and calls HTMLMediaElement::ReceivedMediaStreamInitialSize() with that size. + * and calls HTMLMediaElement::UpdateInitialMediaSize() with that size. */ class HTMLMediaElement::StreamSizeListener : public DirectMediaStreamTrackListener { public: @@ -287,13 +287,17 @@ public: mElement(aElement), mInitialSizeFound(false) {} + void Forget() { mElement = nullptr; } void ReceivedSize(gfx::IntSize aSize) { + MOZ_ASSERT(NS_IsMainThread()); + if (!mElement) { return; } + RefPtr deathGrip = mElement; mElement->UpdateInitialMediaSize(aSize); } @@ -302,9 +306,15 @@ public: StreamTime aTrackOffset, const MediaSegment& aMedia) override { - if (mInitialSizeFound || aMedia.GetType() != MediaSegment::VIDEO) { + if (mInitialSizeFound) { return; } + + if (aMedia.GetType() != MediaSegment::VIDEO) { + MOZ_ASSERT(false, "Should only lock on to a video track"); + return; + } + const VideoSegment& video = static_cast(aMedia); for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) { if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0,0)) { @@ -326,7 +336,9 @@ private: // These fields may only be accessed on the main thread HTMLMediaElement* mElement; - // These fields may only be accessed on the MSG thread + // These fields may only be accessed on the MSG's appending thread. + // (this is a direct listener so we get called by whoever is producing + // this track's data) bool mInitialSizeFound; }; From 3e21f8275baf67d40be4a675ff6f2f720ebc7100 Mon Sep 17 00:00:00 2001 From: Andreas Pehrson Date: Wed, 31 Aug 2016 14:54:03 +0200 Subject: [PATCH 05/65] Bug 1299451 - Fix mozCaptureStream() happening after setting src but before having metadata. r=jesup MozReview-Commit-ID: CKC3n3Nt5IE --HG-- extra : rebase_source : 9c8348403cedb2ac7f68bce9fc5f36cfe2e08357 --- dom/html/HTMLMediaElement.cpp | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 547e9c3c62aa..6e915792ca96 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -2590,16 +2590,21 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded, mAudioCaptured = true; } + if (mDecoder) { + out->mCapturingDecoder = true; + mDecoder->AddOutputStream(out->mStream->GetInputStream()->AsProcessedStream(), + aFinishWhenEnded); + } else if (mSrcStream) { + out->mCapturingMediaStream = true; + } + if (mReadyState == HAVE_NOTHING) { - // Do not expose the tracks directly before we have metadata. + // Do not expose the tracks until we have metadata. RefPtr result = out->mStream; return result.forget(); } if (mDecoder) { - out->mCapturingDecoder = true; - mDecoder->AddOutputStream(out->mStream->GetInputStream()->AsProcessedStream(), - aFinishWhenEnded); if (HasAudio()) { TrackID audioTrackId = mMediaInfo.mAudio.mTrackId; RefPtr trackSource = @@ -2625,22 +2630,6 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded, } if (mSrcStream) { - out->mCapturingMediaStream = true; - MediaStream* inputStream = out->mStream->GetInputStream(); - if (!inputStream) { - NS_ERROR("No input stream"); - RefPtr result = out->mStream; - return result.forget(); - } - - ProcessedMediaStream* processedInputStream = - inputStream->AsProcessedStream(); - if (!processedInputStream) { - NS_ERROR("Input stream not a ProcessedMediaStream"); - RefPtr result = out->mStream; - return result.forget(); - } - for (size_t i = 0; i < AudioTracks()->Length(); ++i) { AudioTrack* t = (*AudioTracks())[i]; if (t->Enabled()) { From 2af3f87bb6f466c6557990835da27db3bcda3f82 Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Mon, 29 Aug 2016 16:51:43 -0400 Subject: [PATCH 06/65] Bug 1288686 - Avoid X11's |#define None 0L| intruding on other parts of the code. r=jrmuizel MozReview-Commit-ID: 9rD0KLTLg7l --HG-- extra : rebase_source : a537ba1913b6959e74fc4fcaf58f51f759cc5882 extra : source : 9278e13a7d664e787f763a7c21a32833700899e1 --- dom/plugins/base/npapi.h | 1 + dom/plugins/base/nsPluginInstanceOwner.cpp | 12 ++++----- dom/plugins/base/nsPluginNativeWindowGtk.cpp | 2 +- dom/plugins/ipc/PluginInstanceChild.cpp | 2 +- dom/plugins/test/testplugin/nptest_gtk2.cpp | 4 +-- gfx/2d/BorrowedContext.h | 5 ++-- gfx/2d/DrawTargetCairo.cpp | 6 ++--- gfx/gl/GLContextProviderGLX.cpp | 17 ++++++------ gfx/layers/ipc/ShadowLayerUtilsX11.cpp | 5 ++-- gfx/src/X11UndefineNone.h | 27 ++++++++++++++++++++ gfx/src/X11Util.cpp | 2 +- gfx/src/X11Util.h | 1 + gfx/src/moz.build | 1 + gfx/thebes/gfxXlibSurface.cpp | 14 +++++----- gfx/thebes/gfxXlibSurface.h | 7 ++--- widget/gtk/nsClipboard.cpp | 2 +- widget/gtk/nsWindow.cpp | 4 +-- 17 files changed, 73 insertions(+), 39 deletions(-) create mode 100644 gfx/src/X11UndefineNone.h diff --git a/dom/plugins/base/npapi.h b/dom/plugins/base/npapi.h index 798ee20aad6b..93600a80a1de 100644 --- a/dom/plugins/base/npapi.h +++ b/dom/plugins/base/npapi.h @@ -46,6 +46,7 @@ #if defined(MOZ_X11) #include #include +#include "X11UndefineNone.h" #endif #endif diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 270d25ace62a..36049f71573a 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -2610,7 +2610,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) #ifdef MOZ_WIDGET_GTK Window root = GDK_ROOT_WINDOW(); #else - Window root = None; // Could XQueryTree, but this is not important. + Window root = X11None; // Could XQueryTree, but this is not important. #endif switch (anEvent.mMessage) { @@ -2628,7 +2628,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) event.y_root = rootPoint.y; event.state = XInputEventState(mouseEvent); // information lost - event.subwindow = None; + event.subwindow = X11None; event.mode = -1; event.detail = NotifyDetailNone; event.same_screen = True; @@ -2647,7 +2647,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) event.y_root = rootPoint.y; event.state = XInputEventState(mouseEvent); // information lost - event.subwindow = None; + event.subwindow = X11None; event.is_hint = NotifyNormal; event.same_screen = True; } @@ -2678,7 +2678,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) break; } // information lost: - event.subwindow = None; + event.subwindow = X11None; event.same_screen = True; } break; @@ -2722,7 +2722,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) // Information that could be obtained from pluginEvent but we may not // want to promise to provide: - event.subwindow = None; + event.subwindow = X11None; event.x = 0; event.y = 0; event.x_root = -1; @@ -2764,7 +2764,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) XAnyEvent& event = pluginEvent.xany; event.display = widget ? static_cast(widget->GetNativeData(NS_NATIVE_DISPLAY)) : nullptr; - event.window = None; // not a real window + event.window = X11None; // not a real window // information lost: event.serial = 0; event.send_event = False; diff --git a/dom/plugins/base/nsPluginNativeWindowGtk.cpp b/dom/plugins/base/nsPluginNativeWindowGtk.cpp index 1aa66c6b8f96..bfb9510e07c0 100644 --- a/dom/plugins/base/nsPluginNativeWindowGtk.cpp +++ b/dom/plugins/base/nsPluginNativeWindowGtk.cpp @@ -217,7 +217,7 @@ nsresult nsPluginNativeWindowGtk::CreateXEmbedWindow(bool aEnableXtFocus) { GdkVisual* gdkVisual = gdk_drawable_get_visual(gdkWindow); mWsInfo.depth = gdkVisual->depth; #else - mWsInfo.colormap = None; + mWsInfo.colormap = X11None; GdkVisual* gdkVisual = gdk_window_get_visual(gdkWindow); mWsInfo.depth = gdk_visual_get_depth(gdkVisual); #endif diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp index 3dc717fac8ab..f51fbac8c7c7 100644 --- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -1282,7 +1282,7 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow) } } - if (aWindow.visualID != None + if (aWindow.visualID != X11None && gtk_check_version(2, 12, 10) != nullptr) { // older // Workaround for a bug in Gtk+ (prior to 2.12.10) where deleting // a foreign GdkColormap will also free the XColormap. diff --git a/dom/plugins/test/testplugin/nptest_gtk2.cpp b/dom/plugins/test/testplugin/nptest_gtk2.cpp index e1ce16e97728..772dfb1e40fe 100644 --- a/dom/plugins/test/testplugin/nptest_gtk2.cpp +++ b/dom/plugins/test/testplugin/nptest_gtk2.cpp @@ -81,7 +81,7 @@ pluginInstanceInit(InstanceData* instanceData) instanceData->platformData->display = nullptr; instanceData->platformData->visual = nullptr; - instanceData->platformData->colormap = None; + instanceData->platformData->colormap = X11None; instanceData->platformData->plug = nullptr; return NPERR_NO_ERROR; @@ -97,7 +97,7 @@ pluginInstanceShutdown(InstanceData* instanceData) if (instanceData->hasWidget) { Window window = reinterpret_cast(instanceData->window.window); - if (window != None) { + if (window != X11None) { // This window XID should still be valid. // See bug 429604 and bug 454756. XWindowAttributes attributes; diff --git a/gfx/2d/BorrowedContext.h b/gfx/2d/BorrowedContext.h index 73b624c27d87..ee50bf7c7315 100644 --- a/gfx/2d/BorrowedContext.h +++ b/gfx/2d/BorrowedContext.h @@ -11,6 +11,7 @@ #ifdef MOZ_X11 #include #include +#include "X11UndefineNone.h" #endif struct _cairo; @@ -87,7 +88,7 @@ public: BorrowedXlibDrawable() : mDT(nullptr), mDisplay(nullptr), - mDrawable(None), + mDrawable(X11None), mScreen(nullptr), mVisual(nullptr), mXRenderFormat(nullptr) @@ -96,7 +97,7 @@ public: explicit BorrowedXlibDrawable(DrawTarget *aDT) : mDT(nullptr), mDisplay(nullptr), - mDrawable(None), + mDrawable(X11None), mScreen(nullptr), mVisual(nullptr), mXRenderFormat(nullptr) diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp index bcd1f88ba732..7d2cc76b3b6c 100644 --- a/gfx/2d/DrawTargetCairo.cpp +++ b/gfx/2d/DrawTargetCairo.cpp @@ -2163,7 +2163,7 @@ DrawTargetCairo::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4 0, nullptr); XRenderComposite(display, PictOpSrc, - srcPict, None, dstPict, + srcPict, X11None, dstPict, 0, 0, 0, 0, 0, 0, xformBounds.width, xformBounds.height); @@ -2313,7 +2313,7 @@ BorrowedXlibDrawable::Init(DrawTarget* aDT) MOZ_ASSERT(aDT, "Caller should check for nullptr"); MOZ_ASSERT(!mDT, "Can't initialize twice!"); mDT = aDT; - mDrawable = None; + mDrawable = X11None; #ifdef CAIRO_HAS_XLIB_SURFACE if (aDT->GetBackendType() != BackendType::CAIRO || @@ -2356,7 +2356,7 @@ BorrowedXlibDrawable::Finish() cairo_surface_t* surf = cairo_get_group_target(cairoDT->mContext); cairo_surface_mark_dirty(surf); if (mDrawable) { - mDrawable = None; + mDrawable = X11None; } } #endif diff --git a/gfx/gl/GLContextProviderGLX.cpp b/gfx/gl/GLContextProviderGLX.cpp index 638e55aaedf8..916c4016d0a5 100644 --- a/gfx/gl/GLContextProviderGLX.cpp +++ b/gfx/gl/GLContextProviderGLX.cpp @@ -11,6 +11,7 @@ #include #include +#include "X11UndefineNone.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/StaticPtr.h" @@ -308,13 +309,13 @@ GLXPixmap GLXLibrary::CreatePixmap(gfxASurface* aSurface) { if (!SupportsTextureFromPixmap(aSurface)) { - return None; + return X11None; } gfxXlibSurface* xs = static_cast(aSurface); const XRenderPictFormat* format = xs->XRenderFormat(); if (!format || format->type != PictTypeDirect) { - return None; + return X11None; } const XRenderDirectFormat& direct = format->direct; int alphaSize = FloorLog2(direct.alphaMask + 1); @@ -327,7 +328,7 @@ GLXLibrary::CreatePixmap(gfxASurface* aSurface) (alphaSize ? LOCAL_GLX_BIND_TO_TEXTURE_RGBA_EXT : LOCAL_GLX_BIND_TO_TEXTURE_RGB_EXT), True, LOCAL_GLX_RENDER_TYPE, LOCAL_GLX_RGBA_BIT, - None }; + X11None }; int numConfigs = 0; Display* display = xs->XDisplay(); @@ -351,7 +352,7 @@ GLXLibrary::CreatePixmap(gfxASurface* aSurface) ~(redMask | greenMask | blueMask) != -1UL << format->depth; for (int i = 0; i < numConfigs; i++) { - int id = None; + int id = X11None; sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &id); Visual* visual; int depth; @@ -424,14 +425,14 @@ GLXLibrary::CreatePixmap(gfxASurface* aSurface) // caller should deal with this situation. NS_WARN_IF_FALSE(format->depth == 8, "[GLX] Couldn't find a FBConfig matching Pixmap format"); - return None; + return X11None; } int pixmapAttribs[] = { LOCAL_GLX_TEXTURE_TARGET_EXT, LOCAL_GLX_TEXTURE_2D_EXT, LOCAL_GLX_TEXTURE_FORMAT_EXT, (alphaSize ? LOCAL_GLX_TEXTURE_FORMAT_RGBA_EXT : LOCAL_GLX_TEXTURE_FORMAT_RGB_EXT), - None}; + X11None}; GLXPixmap glxpixmap = xCreatePixmap(display, cfgs[matchIndex], @@ -900,7 +901,7 @@ GLContextGLX::~GLContextGLX() #ifdef DEBUG bool success = #endif - mGLX->xMakeCurrent(mDisplay, None, nullptr); + mGLX->xMakeCurrent(mDisplay, X11None, nullptr); MOZ_ASSERT(success, "glXMakeCurrent failed to release GL context before we call " "glXDestroyContext!"); @@ -1242,7 +1243,7 @@ GLContextGLX::FindFBConfigForWindow(Display* display, int screen, Window window, #endif for (int i = 0; i < numConfigs; i++) { - int visid = None; + int visid = X11None; sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid); if (!visid) { continue; diff --git a/gfx/layers/ipc/ShadowLayerUtilsX11.cpp b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp index f4d9c8d5c674..6b966005452d 100644 --- a/gfx/layers/ipc/ShadowLayerUtilsX11.cpp +++ b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp @@ -11,6 +11,7 @@ #include // for XRenderPictFormat, etc #include // for PictFormat #include "cairo-xlib.h" +#include "X11UndefineNone.h" #include // for uint32_t #include "GLDefs.h" // for GLenum #include "gfxPlatform.h" // for gfxPlatform @@ -65,7 +66,7 @@ SurfaceDescriptorX11::SurfaceDescriptorX11(gfxXlibSurface* aSurf, bool aForwardGLX) : mId(aSurf->XDrawable()) , mSize(aSurf->GetSize()) - , mGLXPixmap(None) + , mGLXPixmap(X11None) { const XRenderPictFormat *pictFormat = aSurf->XRenderFormat(); if (pictFormat) { @@ -86,7 +87,7 @@ SurfaceDescriptorX11::SurfaceDescriptorX11(Drawable aDrawable, XID aFormatID, : mId(aDrawable) , mFormat(aFormatID) , mSize(aSize) - , mGLXPixmap(None) + , mGLXPixmap(X11None) { } already_AddRefed diff --git a/gfx/src/X11UndefineNone.h b/gfx/src/X11UndefineNone.h new file mode 100644 index 000000000000..cc0bf83255a5 --- /dev/null +++ b/gfx/src/X11UndefineNone.h @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_X11UNDEFINENONE_H_ +#define MOZILLA_GFX_X11UNDEFINENONE_H_ + +// The header defines "None" as a macro that expands to "0L". +// This is terrible because many enumerations have an enumerator named "None". +// To work around this, we undefine the macro "None", and define a replacement +// macro named "X11None". +// Include this header after including X11 headers, where necessary. +#ifdef None +# undef None +# define X11None 0L +// also defines "RevertToNone" as a macro that expands to "(int)None". +// Since we are undefining "None", that stops working. To keep it working, +// we undefine "RevertToNone" and redefine it in terms of "X11None". +# ifdef RevertToNone +# undef RevertToNone +# define RevertToNone (int)X11None +# endif +#endif + +#endif /* MOZILLA_GFX_X11UNDEFINENONE_H_ */ diff --git a/gfx/src/X11Util.cpp b/gfx/src/X11Util.cpp index dd1f025d4d96..41f2a95db4ea 100644 --- a/gfx/src/X11Util.cpp +++ b/gfx/src/X11Util.cpp @@ -29,7 +29,7 @@ FindVisualAndDepth(Display* aDisplay, VisualID aVisualID, } } - NS_ASSERTION(aVisualID == None, "VisualID not on Screen."); + NS_ASSERTION(aVisualID == X11None, "VisualID not on Screen."); *aVisual = nullptr; *aDepth = 0; return; diff --git a/gfx/src/X11Util.h b/gfx/src/X11Util.h index 9868d7ca593a..dff855ff23f9 100644 --- a/gfx/src/X11Util.h +++ b/gfx/src/X11Util.h @@ -13,6 +13,7 @@ #if defined(MOZ_WIDGET_GTK) # include # include +# include "X11UndefineNone.h" #else # error Unknown toolkit #endif diff --git a/gfx/src/moz.build b/gfx/src/moz.build index dd519c7a944c..3678eee7abbd 100644 --- a/gfx/src/moz.build +++ b/gfx/src/moz.build @@ -39,6 +39,7 @@ EXPORTS += [ 'nsTransform2D.h', 'PingPongRegion.h', 'RegionBuilder.h', + 'X11UndefineNone.h' ] EXPORTS.mozilla += [ diff --git a/gfx/thebes/gfxXlibSurface.cpp b/gfx/thebes/gfxXlibSurface.cpp index 99206b87210b..12891c4549d5 100644 --- a/gfx/thebes/gfxXlibSurface.cpp +++ b/gfx/thebes/gfxXlibSurface.cpp @@ -25,7 +25,7 @@ using namespace mozilla::gfx; gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual) : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable) #if defined(GL_PROVIDER_GLX) - , mGLXPixmap(None) + , mGLXPixmap(X11None) #endif { const gfx::IntSize size = DoSizeQuery(); @@ -36,7 +36,7 @@ gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual) gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual, const gfx::IntSize& size) : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable) #if defined(GL_PROVIDER_GLX) - , mGLXPixmap(None) + , mGLXPixmap(X11None) #endif { NS_ASSERTION(Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT), @@ -51,7 +51,7 @@ gfxXlibSurface::gfxXlibSurface(Screen *screen, Drawable drawable, XRenderPictFor : mPixmapTaken(false), mDisplay(DisplayOfScreen(screen)), mDrawable(drawable) #if defined(GL_PROVIDER_GLX) - , mGLXPixmap(None) + , mGLXPixmap(X11None) #endif { NS_ASSERTION(Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT), @@ -67,7 +67,7 @@ gfxXlibSurface::gfxXlibSurface(Screen *screen, Drawable drawable, XRenderPictFor gfxXlibSurface::gfxXlibSurface(cairo_surface_t *csurf) : mPixmapTaken(false) #if defined(GL_PROVIDER_GLX) - , mGLXPixmap(None) + , mGLXPixmap(X11None) #endif { NS_PRECONDITION(cairo_surface_status(csurf) == 0, @@ -97,9 +97,9 @@ CreatePixmap(Screen *screen, const gfx::IntSize& size, unsigned int depth, Drawable relatedDrawable) { if (!Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT)) - return None; + return X11None; - if (relatedDrawable == None) { + if (relatedDrawable == X11None) { relatedDrawable = RootWindowOfScreen(screen); } Display *dpy = DisplayOfScreen(screen); @@ -274,7 +274,7 @@ gfxXlibSurface::Finish() #if defined(GL_PROVIDER_GLX) if (mPixmapTaken && mGLXPixmap) { gl::sGLXLibrary.DestroyPixmap(mDisplay, mGLXPixmap); - mGLXPixmap = None; + mGLXPixmap = X11None; } #endif gfxASurface::Finish(); diff --git a/gfx/thebes/gfxXlibSurface.h b/gfx/thebes/gfxXlibSurface.h index 8c5e8f9a36d2..499bc5e966b7 100644 --- a/gfx/thebes/gfxXlibSurface.h +++ b/gfx/thebes/gfxXlibSurface.h @@ -10,6 +10,7 @@ #include #include +#include "X11UndefineNone.h" #if defined(GL_PROVIDER_GLX) #include "GLXLibrary.h" @@ -46,13 +47,13 @@ public: // |screen| (if specified). static already_AddRefed Create(Screen *screen, Visual *visual, const mozilla::gfx::IntSize& size, - Drawable relatedDrawable = None); + Drawable relatedDrawable = X11None); static cairo_surface_t * CreateCairoSurface(Screen *screen, Visual *visual, const mozilla::gfx::IntSize& size, - Drawable relatedDrawable = None); + Drawable relatedDrawable = X11None); static already_AddRefed Create(Screen* screen, XRenderPictFormat *format, const mozilla::gfx::IntSize& size, - Drawable relatedDrawable = None); + Drawable relatedDrawable = X11None); virtual ~gfxXlibSurface(); diff --git a/widget/gtk/nsClipboard.cpp b/widget/gtk/nsClipboard.cpp index 9db14808f93c..23d1b62b9246 100644 --- a/widget/gtk/nsClipboard.cpp +++ b/widget/gtk/nsClipboard.cpp @@ -1030,7 +1030,7 @@ selection_request_filter(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) { XEvent *xevent = static_cast(gdk_xevent); if (xevent->xany.type == SelectionRequest) { - if (xevent->xselectionrequest.requestor == None) + if (xevent->xselectionrequest.requestor == X11None) return GDK_FILTER_REMOVE; GdkDisplay *display = gdk_x11_lookup_xdisplay( diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index ff6ea552d4da..7b55314d674f 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -452,7 +452,7 @@ nsWindow::nsWindow() mOldFocusWindow = 0; mXDisplay = nullptr; - mXWindow = None; + mXWindow = X11None; mXVisual = nullptr; mXDepth = 0; #endif /* MOZ_X11 */ @@ -4587,7 +4587,7 @@ nsWindow::ClearTransparencyBitmap() Display* xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow); Window xWindow = gdk_x11_window_get_xid(mGdkWindow); - XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, None, ShapeSet); + XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, X11None, ShapeSet); #endif } From 2cb56a6d701d5cf0e933a969cfadf27e4ed29253 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Wed, 31 Aug 2016 13:07:21 -0700 Subject: [PATCH 07/65] Backed out changeset 2c986272197f (bug 1298225) for mochitest-c1 failures a=backout --HG-- extra : rebase_source : 0d2f61f743461d4f75f5c44552f9f1676cecb556 --- devtools/client/shared/components/frame.js | 5 +- .../client/shared/components/stack-trace.js | 8 +- devtools/client/webconsole/console-output.js | 6 +- ...onsole_copy_entire_message_context_menu.js | 100 +++++++----------- .../client/webconsole/test/test-console.html | 7 -- 5 files changed, 44 insertions(+), 82 deletions(-) diff --git a/devtools/client/shared/components/frame.js b/devtools/client/shared/components/frame.js index 7070994b61fe..d673ccc053a9 100644 --- a/devtools/client/shared/components/frame.js +++ b/devtools/client/shared/components/frame.js @@ -176,8 +176,7 @@ module.exports = createClass({ if (functionDisplayName) { elements.push( dom.span({ className: "frame-link-function-display-name" }, - functionDisplayName), - " " + functionDisplayName) ); } } @@ -237,7 +236,7 @@ module.exports = createClass({ elements.push(sourceEl); if (showHost && host) { - elements.push(" ", dom.span({ className: "frame-link-host" }, host)); + elements.push(dom.span({ className: "frame-link-host" }, host)); } return dom.span(attributes, ...elements); diff --git a/devtools/client/shared/components/stack-trace.js b/devtools/client/shared/components/stack-trace.js index 511b4762618d..9818d36d106e 100644 --- a/devtools/client/shared/components/stack-trace.js +++ b/devtools/client/shared/components/stack-trace.js @@ -42,12 +42,12 @@ const StackTrace = createClass({ let frames = []; stacktrace.forEach(s => { if (s.asyncCause) { - frames.push("\t", AsyncFrame({ + frames.push(AsyncFrame({ asyncCause: s.asyncCause - }), "\n"); + })); } - frames.push("\t", Frame({ + frames.push(Frame({ frame: { functionDisplayName: s.functionName, source: s.filename.split(" -> ").pop(), @@ -58,7 +58,7 @@ const StackTrace = createClass({ showAnonymousFunctionName: true, showFullSourceUrl: true, onClick: onViewSourceInDebugger - }), "\n"); + })); }); return dom.div({ className: "stack-trace" }, frames); diff --git a/devtools/client/webconsole/console-output.js b/devtools/client/webconsole/console-output.js index 56ef65f956dd..46bc29650bb1 100644 --- a/devtools/client/webconsole/console-output.js +++ b/devtools/client/webconsole/console-output.js @@ -935,6 +935,8 @@ Messages.Simple.prototype = extend(Messages.BaseMessage.prototype, { this.element.appendChild(body); + this.element.appendChild(this.document.createTextNode("\n")); + this.element.clipboardText = this.element.textContent; if (this.private) { @@ -991,16 +993,12 @@ Messages.Simple.prototype = extend(Messages.BaseMessage.prototype, { let location = this._renderLocation(); if (repeatNode) { - bodyFlex.appendChild(this.document.createTextNode(" ")); bodyFlex.appendChild(repeatNode); } if (location) { - bodyFlex.appendChild(this.document.createTextNode(" ")); bodyFlex.appendChild(location); } - bodyFlex.appendChild(this.document.createTextNode("\n")); - if (this.stack) { this._attachment = new Widgets.Stacktrace(this, this.stack).render().element; } diff --git a/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js b/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js index bdd4f71793f2..966f4308afff 100644 --- a/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js +++ b/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js @@ -3,95 +3,67 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* globals goDoCommand */ - "use strict"; // Test copying of the entire console message when right-clicked // with no other text selected. See Bug 1100562. -add_task(function* () { +function test() { let hud; let outputNode; let contextMenu; - const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test/test-console.html"; + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; - const { tab, browser } = yield loadTab(TEST_URI); - hud = yield openConsole(tab); - outputNode = hud.outputNode; - contextMenu = hud.iframeWindow.document.getElementById("output-contextmenu"); + Task.spawn(runner).then(finishTest); - registerCleanupFunction(() => { - hud = outputNode = contextMenu = null; - }); + function* runner() { + const {tab} = yield loadTab(TEST_URI); + hud = yield openConsole(tab); + outputNode = hud.outputNode; + contextMenu = hud.iframeWindow.document.getElementById("output-contextmenu"); - hud.jsterm.clearOutput(); + registerCleanupFunction(() => { + hud = outputNode = contextMenu = null; + }); - yield ContentTask.spawn(browser, {}, function* () { - let button = content.document.getElementById("testTrace"); - button.click(); - }); + hud.jsterm.clearOutput(); + content.console.log("bug 1100562"); - let results = yield waitForMessages({ - webconsole: hud, - messages: [ - { + let [results] = yield waitForMessages({ + webconsole: hud, + messages: [{ text: "bug 1100562", category: CATEGORY_WEBDEV, severity: SEVERITY_LOG, - lines: 1, - }, - { - name: "console.trace output", - consoleTrace: true, - lines: 3, - }, - ] - }); + }] + }); - outputNode.focus(); + outputNode.focus(); + let message = [...results.matched][0]; - for (let result of results) { - let message = [...result.matched][0]; + yield waitForContextMenu(contextMenu, message, copyFromPopup, + testContextMenuCopy); - yield waitForContextMenu(contextMenu, message, () => { + function copyFromPopup() { let copyItem = contextMenu.querySelector("#cMenu_copy"); copyItem.doCommand(); let controller = top.document.commandDispatcher .getControllerForCommand("cmd_copy"); is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled"); - }); - - let clipboardText; - - yield waitForClipboardPromise( - () => goDoCommand("cmd_copy"), - (str) => { - clipboardText = str; - return message.textContent == clipboardText; - } - ); - - ok(clipboardText, "Clipboard text was found and saved"); - - let lines = clipboardText.split("\n"); - ok(lines.length > 0, "There is at least one newline in the message"); - is(lines.pop(), "", "There is a newline at the end"); - is(lines.length, result.lines, `There are ${result.lines} lines in the message`); - - // Test the first line for "timestamp message repeat file:line" - let firstLine = lines.shift(); - ok(/^[\d:.]+ .+ \d+ .+:\d+$/.test(firstLine), - "The message's first line has the right format"); - - // Test the remaining lines (stack trace) for "TABfunctionName sourceURL:line:col" - for (let line of lines) { - ok(/^\t.+ .+:\d+:\d+$/.test(line), "The stack trace line has the right format"); } - } - yield closeConsole(tab); - yield finishTest(); -}); + function testContextMenuCopy() { + waitForClipboard((str) => { + return message.textContent.trim() == str.trim(); + }, () => { + goDoCommand("cmd_copy"); + }, () => {}, () => {} + ); + } + + yield closeConsole(tab); + } +} diff --git a/devtools/client/webconsole/test/test-console.html b/devtools/client/webconsole/test/test-console.html index b294a3ba1cfd..9731c86a9fd5 100644 --- a/devtools/client/webconsole/test/test-console.html +++ b/devtools/client/webconsole/test/test-console.html @@ -13,12 +13,6 @@ console.log(str); } } - - function testTrace() { - console.log("bug 1100562"); - console.trace(); - } - console.info("INLINE SCRIPT:"); test(); console.warn("I'm warning you, he will eat up all yr bacon."); @@ -28,7 +22,6 @@

Heads Up Display Demo

-
From ab7cc0932efddb7339b547d2fa4a37e0cc7d3ee3 Mon Sep 17 00:00:00 2001 From: Bryce Van Dyk Date: Tue, 30 Aug 2016 16:25:01 +1200 Subject: [PATCH 08/65] Bug 1213731 - Rework Youtube puppeteer to work with both youtube and embedded youtube. r=maja_zf Rework the Youtube puppeteer to look up player and video element based on class names, instead of ID. This means that the tests can work with embedded players. This has the benefit that we can use youtube embedded links (youtube.com/embedded/), which do not suffer from auto play related issues (auto play jumping to another video). MozReview-Commit-ID: 9UFyL7di6gH --HG-- extra : rebase_source : 69301bfa2b7ea9fd729742ae670ecb6e8209c4f9 --- .../media_utils/youtube_puppeteer.py | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py b/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py index ee6214c49587..a76cf815339c 100644 --- a/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py +++ b/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py @@ -15,9 +15,16 @@ from external_media_tests.utils import verbose_until class YouTubePuppeteer(VideoPuppeteer): """ - Wrapper around a YouTube #movie_player element. + Wrapper around a YouTube .html5-video-player element. - Partial reference: https://developers.google.com/youtube/js_api_reference. + Can be used with youtube videos or youtube videos at embedded URLS. E.g. + both https://www.youtube.com/watch?v=AbAACm1IQE0 and + https://www.youtube.com/embed/AbAACm1IQE0 should work. + + Using an embedded video has the advantage of not auto-playing more videos + while a test is running. + + Partial reference: https://developers.google.com/youtube/iframe_api_reference. This reference is useful for site-specific features such as interacting with ads, or accessing YouTube's debug data. """ @@ -37,14 +44,16 @@ class YouTubePuppeteer(VideoPuppeteer): self.player = None super(YouTubePuppeteer, self).__init__(marionette, url, - video_selector='#movie_player video', + video_selector='.html5-video-player video', **kwargs) wait = Wait(self.marionette, timeout=30) with self.marionette.using_context(Marionette.CONTEXT_CONTENT): verbose_until(wait, self, - expected.element_present(By.ID, 'movie_player')) - self.player = self.marionette.find_element(By.ID, 'movie_player') - self.marionette.execute_script("log('#movie_player " + expected.element_present(By.CLASS_NAME, + 'html5-video-player')) + self.player = self.marionette.find_element(By.CLASS_NAME, + 'html5-video-player') + self.marionette.execute_script("log('.html5-video-player " "element obtained');") # When an ad is playing, self.player_duration indicates the duration # of the spliced-in ad stream, not the duration of the main video, so @@ -117,7 +126,7 @@ class YouTubePuppeteer(VideoPuppeteer): def execute_yt_script(self, script): """ Execute JS script in content context with access to video element and - YouTube #movie_player element. + YouTube .html5-video-player element. :param script: script to be executed. @@ -131,7 +140,7 @@ class YouTubePuppeteer(VideoPuppeteer): @property def playback_quality(self): """ - Please see https://developers.google.com/youtube/js_api_reference#Playback_quality + Please see https://developers.google.com/youtube/iframe_api_reference#Playback_quality for valid values. :return: A string with a valid value returned via YouTube. @@ -176,7 +185,7 @@ class YouTubePuppeteer(VideoPuppeteer): """ :return: The YouTube state of the video. See - https://developers.google.com/youtube/js_api_reference#getPlayerState + https://developers.google.com/youtube/iframe_api_reference#getPlayerState for valid values. """ state = self.execute_yt_script('return arguments[1].' @@ -188,7 +197,7 @@ class YouTubePuppeteer(VideoPuppeteer): """ This and the following properties are based on the player.getPlayerState() call - (https://developers.google.com/youtube/js_api_reference#Playback_status) + (https://developers.google.com/youtube/iframe_api_reference#Playback_status) :return: True if the video has not yet started. """ @@ -240,7 +249,7 @@ class YouTubePuppeteer(VideoPuppeteer): Get state of current ad. :return: Returns one of the constants listed in - https://developers.google.com/youtube/js_api_reference#Playback_status + https://developers.google.com/youtube/iframe_api_reference#Playback_status for an ad. """ @@ -350,7 +359,7 @@ class YouTubePuppeteer(VideoPuppeteer): # no ad playing return False if self.ad_skippable: - selector = '#movie_player .videoAdUiSkipContainer' + selector = '.html5-video-player .videoAdUiSkipContainer' wait = Wait(self.marionette, timeout=30) try: with self.marionette.using_context(Marionette.CONTEXT_CONTENT): @@ -378,7 +387,7 @@ class YouTubePuppeteer(VideoPuppeteer): if (self.ad_playing and self.video_src.startswith('mediasource') and self.duration): return self.duration - selector = '#movie_player .videoAdUiAttribution' + selector = '.html5-media-player .videoAdUiAttribution' wait = Wait(self.marionette, timeout=5) try: with self.marionette.using_context(Marionette.CONTEXT_CONTENT): @@ -464,7 +473,7 @@ class YouTubePuppeteer(VideoPuppeteer): player_state = self._yt_player_state_name[self.player_state] ad_state = self._yt_player_state_name[self.ad_state] messages += [ - '#movie_player: {', + '.html5-media-player: {', '\tvideo id: {0},'.format(self.movie_id), '\tvideo_title: {0}'.format(self.movie_title), '\tcurrent_state: {0},'.format(player_state), @@ -475,7 +484,7 @@ class YouTubePuppeteer(VideoPuppeteer): '}' ] else: - messages += ['\t#movie_player: None'] + messages += ['\t.html5-media-player: None'] return '\n'.join(messages) From ae1691c6353de9e8b16fcb688ef5c9a4d43971fb Mon Sep 17 00:00:00 2001 From: Bryce Van Dyk Date: Tue, 30 Aug 2016 18:36:16 +1200 Subject: [PATCH 09/65] Bug 1213731 - Remove unused Youtube URLs, use embedded URLs where possible. r=maja_zf Many of the youtube URLs were not being used in tests. Many were are/also dead. Furthermore, non-embedded links are causing issues due to the next video auto play feature defaulting to on in youtube. This is a quick once over to remove unused links, prune some of the dead, and rewrite those that can be embedded to embedded URLs. In future I would like to see the embedded links and non embedded separated into their own files. However, theses changes are a halfway house that will not break compatibility downstream. MozReview-Commit-ID: 4aPMNjD3LC4 --HG-- rename : dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-720.ini => dom/media/test/external/external_media_tests/urls/youtube/long2-crashes-720.ini rename : dom/media/test/external/external_media_tests/urls/youtube/long4-crashes-900.ini => dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-900.ini rename : dom/media/test/external/external_media_tests/urls/youtube/short0-10.ini => dom/media/test/external/external_media_tests/urls/youtube/short1-10.ini rename : dom/media/test/external/external_media_tests/urls/youtube/short3-crashes-15.ini => dom/media/test/external/external_media_tests/urls/youtube/short2-crashes-15.ini extra : rebase_source : a5abcc8d7b1f1f1e3e2b6303f91b6183f4e4d9ee --- .../external_media_tests/urls/default.ini | 10 +++++----- .../urls/youtube/long1-720.ini | 19 +++++------------- .../urls/youtube/long2-720.ini | 9 --------- ...-crashes-720.ini => long2-crashes-720.ini} | 3 +++ ...-crashes-900.ini => long3-crashes-900.ini} | 3 +++ .../urls/youtube/massive-6000.ini | 15 -------------- .../urls/youtube/medium1-60.ini | 20 +++++++++---------- .../urls/youtube/medium2-60.ini | 6 ------ .../urls/youtube/medium3-120.ini | 11 ---------- .../youtube/{short0-10.ini => short1-10.ini} | 0 .../urls/youtube/short1-15.ini | 5 ----- .../urls/youtube/short2-15.ini | 5 ----- ...3-crashes-15.ini => short2-crashes-15.ini} | 3 +++ 13 files changed, 29 insertions(+), 80 deletions(-) delete mode 100644 dom/media/test/external/external_media_tests/urls/youtube/long2-720.ini rename dom/media/test/external/external_media_tests/urls/youtube/{long3-crashes-720.ini => long2-crashes-720.ini} (87%) rename dom/media/test/external/external_media_tests/urls/youtube/{long4-crashes-900.ini => long3-crashes-900.ini} (98%) delete mode 100644 dom/media/test/external/external_media_tests/urls/youtube/massive-6000.ini delete mode 100644 dom/media/test/external/external_media_tests/urls/youtube/medium2-60.ini delete mode 100644 dom/media/test/external/external_media_tests/urls/youtube/medium3-120.ini rename dom/media/test/external/external_media_tests/urls/youtube/{short0-10.ini => short1-10.ini} (100%) delete mode 100644 dom/media/test/external/external_media_tests/urls/youtube/short1-15.ini delete mode 100644 dom/media/test/external/external_media_tests/urls/youtube/short2-15.ini rename dom/media/test/external/external_media_tests/urls/youtube/{short3-crashes-15.ini => short2-crashes-15.ini} (74%) diff --git a/dom/media/test/external/external_media_tests/urls/default.ini b/dom/media/test/external/external_media_tests/urls/default.ini index ef284aba80ab..b1e26abbdd27 100644 --- a/dom/media/test/external/external_media_tests/urls/default.ini +++ b/dom/media/test/external/external_media_tests/urls/default.ini @@ -1,9 +1,9 @@ -# short videos; no ads; max 5 minutes +# short videos; no ads; embedded; max 5 minutes # 0:12 -[https://youtu.be/AbAACm1IQE0] +[https://youtube.com/embed/AbAACm1IQE0?autoplay=1] # 2:18 -[https://www.youtube.com/watch?v=yOQQCoxs8-k] +[https://youtube.com/embed/yOQQCoxs8-k?autoplay=1] # 0:08 -[https://www.youtube.com/watch?v=1visYpIREUM] +[https://youtube.com/embed/1visYpIREUM?autoplay=1] # 2:09 -[https://www.youtube.com/watch?v=rjmuKV9BTkE] +[https://youtube.com/embed/rjmuKV9BTkE?autoplay=1] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/long1-720.ini b/dom/media/test/external/external_media_tests/urls/youtube/long1-720.ini index 8676894796b7..cabb823b1b4b 100644 --- a/dom/media/test/external/external_media_tests/urls/youtube/long1-720.ini +++ b/dom/media/test/external/external_media_tests/urls/youtube/long1-720.ini @@ -1,14 +1,5 @@ -# all long videos; < 12 hours total -# 2:18:00 -[http://youtu.be/FLX64H5FYa8] -# 1:00:00 -[https://www.youtube.com/watch?v=AYYDshv8C4g] -# 1:10:00 -[https://www.youtube.com/watch?v=V0Vy4kYAPDk] -# 1:47:00 -[https://www.youtube.com/watch?v=bFtGE2C7Pxs] - - -# shutdownhang | WaitForSingleObjectEx | WaitForSingleObject | PR_Wait | nsThread::ProcessNextEvent(bool, bool*) | NS_ProcessNextEvent(nsIThread*, bool) | mozilla::MediaShutdownManager::Shutdown() -# 1:43:00 -[https://www.youtube.com/watch?v=BXMtXpmpXPU] +# a couple of very long videos, < 12 hours total +# 6:00:00 - can't embed due to copyright +[https://www.youtube.com/watch?v=5N8sUccRiTA] +# 2:09:00 +[https://www.youtube.com/embed/b6q5N16dje4?autoplay=1] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/long2-720.ini b/dom/media/test/external/external_media_tests/urls/youtube/long2-720.ini deleted file mode 100644 index d660ad427971..000000000000 --- a/dom/media/test/external/external_media_tests/urls/youtube/long2-720.ini +++ /dev/null @@ -1,9 +0,0 @@ -# a couple of very long videos, < 12 hours total -# 6:00:00 -[https://www.youtube.com/watch?v=5N8sUccRiTA] -# 2:27:00 -[https://www.youtube.com/watch?v=NAVrm3wjzq8] -# 58:50 -[https://www.youtube.com/watch?v=uP1BBw3IYco] -# 2:09:00 -[https://www.youtube.com/watch?v=b6q5N16dje4] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-720.ini b/dom/media/test/external/external_media_tests/urls/youtube/long2-crashes-720.ini similarity index 87% rename from dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-720.ini rename to dom/media/test/external/external_media_tests/urls/youtube/long2-crashes-720.ini index 0c1a5e2e5005..de449f88252e 100644 --- a/dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-720.ini +++ b/dom/media/test/external/external_media_tests/urls/youtube/long2-crashes-720.ini @@ -1,3 +1,6 @@ +# It appears these are not currently used by tests. They are left here as they +# reference failure scenarios. If tese are fixed that can be removed. + # videos from crashes, < 12 hours # hang | NtUserMessageCall | SendMessageW diff --git a/dom/media/test/external/external_media_tests/urls/youtube/long4-crashes-900.ini b/dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-900.ini similarity index 98% rename from dom/media/test/external/external_media_tests/urls/youtube/long4-crashes-900.ini rename to dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-900.ini index a10e21debaab..70081b986e57 100644 --- a/dom/media/test/external/external_media_tests/urls/youtube/long4-crashes-900.ini +++ b/dom/media/test/external/external_media_tests/urls/youtube/long3-crashes-900.ini @@ -1,3 +1,6 @@ +# It appears these are not currently used by tests. They are left here as they +# reference failure scenarios. If tese are fixed that can be removed. + # Total time: about 12-13 hours + unskippable ads #Request url: https://crash-stats.mozilla.com/api/SuperSearchUnredacted/?product=Firefox&url=%24https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D&url=%21~list&url=%21~index&_results_number=50&platform=Windows&version=37.0&date=%3E2015-03-26 diff --git a/dom/media/test/external/external_media_tests/urls/youtube/massive-6000.ini b/dom/media/test/external/external_media_tests/urls/youtube/massive-6000.ini deleted file mode 100644 index fad6dcc57bf1..000000000000 --- a/dom/media/test/external/external_media_tests/urls/youtube/massive-6000.ini +++ /dev/null @@ -1,15 +0,0 @@ -# very long test; 96-100 hours? -# 00:3:26 -[https://www.youtube.com/watch?v=7RMQksXpQSk] -# nyan cat 10 hours -[http://youtu.be/9bZkp7q19f0] -# 4:54:00 -[https://www.youtube.com/watch?v=jWlKjw3LBDk] -# 3:00:01 -[https://www.youtube.com/watch?v=ub9JUDS_6i8] -# 10 hours rick roll -[https://www.youtube.com/watch?v=BROWqjuTM0g] -# 24 hours -[https://www.youtube.com/watch?v=FvHiLLkPhQE] -# 2 hours -[https://www.youtube.com/watch?v=VmOuW5zTt9w diff --git a/dom/media/test/external/external_media_tests/urls/youtube/medium1-60.ini b/dom/media/test/external/external_media_tests/urls/youtube/medium1-60.ini index 7a2657eab7b5..65ccef11a045 100644 --- a/dom/media/test/external/external_media_tests/urls/youtube/medium1-60.ini +++ b/dom/media/test/external/external_media_tests/urls/youtube/medium1-60.ini @@ -1,18 +1,18 @@ # mix of shorter/longer videos with/without ads, < 60 min -# 4:59 -[http://youtu.be/pWI8RB2dmfU] +# 4:59 - can't embed +[https://www.youtube.com/watch?v=pWI8RB2dmfU] # 0:46 ad at start -[http://youtu.be/6SFp1z7uA6g] +[https://www.youtube.com/embed/6SFp1z7uA6g?autoplay=1] # 0:58 ad at start -[http://youtu.be/Aebs62bX0dA] +[https://www.youtube.com/embed/Aebs62bX0dA?autoplay=1] # 1:43 ad -[https://www.youtube.com/watch?v=l5ODwR6FPRQ] -# 8:00 ad +[https://www.youtube.com/embed/l5ODwR6FPRQ?autoplay=1] +# 8:00 ad - can't embed [https://www.youtube.com/watch?v=KlyXNRrsk4A] # video with ad in beginning and in the middle 20:00 # https://bugzilla.mozilla.org/show_bug.cgi?id=1176815 -[https://www.youtube.com/watch?v=cht9Xq9suGg] +[https://www.youtube.com/embed/cht9Xq9suGg?autoplay=1] # 1:35 ad -[https://www.youtube.com/watch?v=orybDrUj4vA] -# 3:02 - ad -[https://youtu.be/tDDVAErOI5U] +[https://www.youtube.com/embed/orybDrUj4vA?autoplay=1] +# 3:02 ad +[https://www.youtube.com/embed/tDDVAErOI5U?autoplay=1] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/medium2-60.ini b/dom/media/test/external/external_media_tests/urls/youtube/medium2-60.ini deleted file mode 100644 index e999d4a0dddd..000000000000 --- a/dom/media/test/external/external_media_tests/urls/youtube/medium2-60.ini +++ /dev/null @@ -1,6 +0,0 @@ -# a few longer videos, < 60 min total -# 0:30:00 no ad -[https://www.youtube.com/watch?v=-qXxNPvqHtQ] -# 0:20:00 -[http://youtu.be/Fu2DcHzokew] - diff --git a/dom/media/test/external/external_media_tests/urls/youtube/medium3-120.ini b/dom/media/test/external/external_media_tests/urls/youtube/medium3-120.ini deleted file mode 100644 index 112dd67c830b..000000000000 --- a/dom/media/test/external/external_media_tests/urls/youtube/medium3-120.ini +++ /dev/null @@ -1,11 +0,0 @@ -# a few longer videos, < 120 min total -# video with ad in the middle -# 21:00 -[https://www.youtube.com/watch?v=cht9Xq9suGg] -# 16:00 -[https://www.youtube.com/watch?v=6Lm9EHhbJAY] -# 20:00 -[https://www.youtube.com/watch?v=8XQ1onjXJK0] -# 59:06 -[https://www.youtube.com/watch?v=kmpiY5kssU4] - diff --git a/dom/media/test/external/external_media_tests/urls/youtube/short0-10.ini b/dom/media/test/external/external_media_tests/urls/youtube/short1-10.ini similarity index 100% rename from dom/media/test/external/external_media_tests/urls/youtube/short0-10.ini rename to dom/media/test/external/external_media_tests/urls/youtube/short1-10.ini diff --git a/dom/media/test/external/external_media_tests/urls/youtube/short1-15.ini b/dom/media/test/external/external_media_tests/urls/youtube/short1-15.ini deleted file mode 100644 index c6ddc0166104..000000000000 --- a/dom/media/test/external/external_media_tests/urls/youtube/short1-15.ini +++ /dev/null @@ -1,5 +0,0 @@ -# 00:12 -[https://youtu.be/AbAACm1IQE0] -# longer video with ads; < 15 min total -# 13:40 -[https://www.youtube.com/watch?v=87uo2TPrsl8] diff --git a/dom/media/test/external/external_media_tests/urls/youtube/short2-15.ini b/dom/media/test/external/external_media_tests/urls/youtube/short2-15.ini deleted file mode 100644 index 5933ceb41734..000000000000 --- a/dom/media/test/external/external_media_tests/urls/youtube/short2-15.ini +++ /dev/null @@ -1,5 +0,0 @@ -# 1-2 longer videos with ads; < 15 minutes total -[https://www.youtube.com/watch?v=v678Em6qyzk] -[https://www.youtube.com/watch?v=l8XOZJkozfI] - - diff --git a/dom/media/test/external/external_media_tests/urls/youtube/short3-crashes-15.ini b/dom/media/test/external/external_media_tests/urls/youtube/short2-crashes-15.ini similarity index 74% rename from dom/media/test/external/external_media_tests/urls/youtube/short3-crashes-15.ini rename to dom/media/test/external/external_media_tests/urls/youtube/short2-crashes-15.ini index ee0730407a9d..bfcba4101a15 100644 --- a/dom/media/test/external/external_media_tests/urls/youtube/short3-crashes-15.ini +++ b/dom/media/test/external/external_media_tests/urls/youtube/short2-crashes-15.ini @@ -1,3 +1,6 @@ +# It appears these are not currently used by tests. They are left here as they +# reference failure scenarios. If tese are fixed that can be removed. + # crash-data videos, < 15 minutes total # hang | NtUserMessageCall | SendMessageW From 64706576831a6225adc99c098788864865c28f38 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Mon, 29 Aug 2016 12:03:13 +0900 Subject: [PATCH 10/65] Bug 1161599 - Show "Reload Image" if the image has an error. r=florian MozReview-Commit-ID: 1hDVFySM6to --HG-- extra : rebase_source : 0b5133f2eea6d26ab4e190dc6bf060b6c580703a --- browser/base/content/nsContextMenu.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 40501af637b8..0e842d41c480 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -695,8 +695,11 @@ nsContextMenu.prototype = { this.target.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST); if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE)) this.onLoadedImage = true; - if (request && (request.imageStatus & request.STATUS_LOAD_COMPLETE)) + if (request && + (request.imageStatus & request.STATUS_LOAD_COMPLETE) && + !(request.imageStatus & request.STATUS_ERROR)) { this.onCompletedImage = true; + } this.mediaURL = this.target.currentURI.spec; From 31bcff5fe18e32351d7a677ee3dccb1d4c9c9e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 31 Aug 2016 14:33:46 -0700 Subject: [PATCH 11/65] Bug 1299396: Don't style content that is not an element or a text node in ServoStyleSet::StyleNewSubtree. r=bholley MozReview-Commit-ID: KmcYPFqt48W --HG-- extra : rebase_source : 0aff142b7522e853a8948820cd2c88d84afe5fb8 --- layout/style/ServoStyleSet.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index 5f0ab284292d..9564f617510d 100644 --- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -506,7 +506,9 @@ void ServoStyleSet::StyleNewSubtree(nsIContent* aContent) { MOZ_ASSERT(aContent->IsDirtyForServo()); - Servo_RestyleSubtree(aContent, mRawSet.get()); + if (aContent->IsElement() || aContent->IsNodeOfType(nsINode::eTEXT)) { + Servo_RestyleSubtree(aContent, mRawSet.get()); + } ClearDirtyBits(aContent); } From 411867285ab0d1fe181a8152c162c5a9f108fe8f Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 1 Sep 2016 07:43:38 +0900 Subject: [PATCH 12/65] Bug 1299661 - Use hg --template flag instead of -T. r=gps Versions of mercurial older than 3.0 don't support the -T shorthand for the --template option. While most people should be using something newer, it can still happen that some run an older version, and it's still trivial to support them by using the long option. --HG-- extra : rebase_source : 1507aea436779495045df48b044a58f4af1382be --- python/mozboot/mozboot/bootstrap.py | 2 +- python/mozlint/mozlint/vcs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/mozboot/mozboot/bootstrap.py b/python/mozboot/mozboot/bootstrap.py index 43f43eb6f924..40bb7cc86b81 100644 --- a/python/mozboot/mozboot/bootstrap.py +++ b/python/mozboot/mozboot/bootstrap.py @@ -417,7 +417,7 @@ def current_firefox_checkout(check_output, hg=None): if hg and os.path.exists(hg_dir): # Verify the hg repo is a Firefox repo by looking at rev 0. try: - node = check_output([hg, 'log', '-r', '0', '-T', '{node}'], cwd=path) + node = check_output([hg, 'log', '-r', '0', '--template', '{node}'], cwd=path) if node in HG_ROOT_REVISIONS: return 'hg' # Else the root revision is different. There could be nested diff --git a/python/mozlint/mozlint/vcs.py b/python/mozlint/mozlint/vcs.py index 1afa02a85713..44a76f0669d6 100644 --- a/python/mozlint/mozlint/vcs.py +++ b/python/mozlint/mozlint/vcs.py @@ -49,7 +49,7 @@ class VCSFiles(object): def by_rev(self, rev): if self.is_hg: - return self._run(['hg', 'log', '-T', '{files % "\\n{file}"}', '-r', rev]) + return self._run(['hg', 'log', '--template', '{files % "\\n{file}"}', '-r', rev]) elif self.is_git: return self._run(['git', 'diff', '--name-only', rev]) return [] From 8056e10c11610ed1aa09db78dc8cb30d8a2539a7 Mon Sep 17 00:00:00 2001 From: William Lachance Date: Wed, 31 Aug 2016 15:47:12 -0400 Subject: [PATCH 13/65] Bug 1263409 - In addon performance alert tests, poll to see if we get desired jank measurement r=Yoric MozReview-Commit-ID: 3k6AZO5QJTp --HG-- extra : rebase_source : dc4135589c173abd331f7ca33d1f8fdf7dce3573 --- .../tests/browser/browser_addonPerformanceAlerts.js | 2 +- .../tests/browser/browser_addonPerformanceAlerts_2.js | 2 +- .../tests/browser/browser_webpagePerformanceAlerts.js | 2 +- .../components/perfmonitoring/tests/browser/head.js | 11 +++++++---- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts.js b/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts.js index 0e2fea574ee9..af78f0074555 100644 --- a/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts.js +++ b/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts.js @@ -55,7 +55,7 @@ add_task(function* test_install_addon_then_watch_it() { Assert.ok(realListener.triggered, `1. The real listener was triggered ${topic}`); Assert.ok(universalListener.triggered, `1. The universal listener was triggered ${topic}`); Assert.ok(!fakeListener.triggered, `1. The fake listener was not triggered ${topic}`); - Assert.ok(realListener.result >= 200000, `1. jank is at least 300ms (${realListener.result/1000}ms) ${topic}`); + Assert.ok(realListener.result >= addon.jankThreshold, `1. jank is at least ${addon.jankThreshold/1000}ms (${realListener.result/1000}ms) ${topic}`); info(`Attempting to remove a performance listener incorrectly, check that this does not hurt our real listener ${topic}`); Assert.throws(() => PerformanceWatcher.removePerformanceListener({addonId: addon.addonId}, () => {})); diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts_2.js b/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts_2.js index 2ff47bb01136..d39c38b1f67c 100644 --- a/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts_2.js +++ b/toolkit/components/perfmonitoring/tests/browser/browser_addonPerformanceAlerts_2.js @@ -18,7 +18,7 @@ add_task(function* test_watch_addon_then_install_it() { let addon = new AddonBurner(addonId); Assert.ok((yield addon.run(topic, 10, realListener)), `5. The real listener was triggered ${topic}`); - Assert.ok(realListener.result >= 200000, `5. jank is at least 200ms (${realListener.result}µs) ${topic}`); + Assert.ok(realListener.result >= addon.jankThreshold, `5. jank is at least ${addon.jankThreshold/1000}ms (${realListener.result}µs) ${topic}`); realListener.unregister(); addon.dispose(); } diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_webpagePerformanceAlerts.js b/toolkit/components/perfmonitoring/tests/browser/browser_webpagePerformanceAlerts.js index aed730c57986..eb908c8dbf63 100644 --- a/toolkit/components/perfmonitoring/tests/browser/browser_webpagePerformanceAlerts.js +++ b/toolkit/components/perfmonitoring/tests/browser/browser_webpagePerformanceAlerts.js @@ -8,7 +8,7 @@ * Simulate a slow webpage. */ function WebpageBurner() { - CPUBurner.call(this, "http://example.com/browser/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html?test=" + Math.random()); + CPUBurner.call(this, "http://example.com/browser/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html?test=" + Math.random(), 300000); } WebpageBurner.prototype = Object.create(CPUBurner.prototype); WebpageBurner.prototype.promiseBurnContentCPU = function() { diff --git a/toolkit/components/perfmonitoring/tests/browser/head.js b/toolkit/components/perfmonitoring/tests/browser/head.js index 9245e8c29e94..92258fd1b9d6 100644 --- a/toolkit/components/perfmonitoring/tests/browser/head.js +++ b/toolkit/components/perfmonitoring/tests/browser/head.js @@ -8,14 +8,16 @@ Cu.import("resource://gre/modules/AddonManager.jsm", this); Cu.import("resource://gre/modules/AddonWatcher.jsm", this); Cu.import("resource://gre/modules/PerformanceWatcher.jsm", this); Cu.import("resource://gre/modules/Services.jsm", this); +Cu.import("resource://testing-common/ContentTaskUtils.jsm", this); /** * Base class for simulating slow addons/webpages. */ -function CPUBurner(url) { +function CPUBurner(url, jankThreshold) { info(`CPUBurner: Opening tab for ${url}\n`); this.url = url; this.tab = gBrowser.addTab(url); + this.jankThreshold = jankThreshold; let browser = this.tab.linkedBrowser; this._browser = browser; ContentTask.spawn(this._browser, null, CPUBurner.frameScript); @@ -26,7 +28,7 @@ CPUBurner.prototype = { return this._browser.outerWindowID; }, /** - * Burn CPU until it triggers a listener. + * Burn CPU until it triggers a listener with the specified jank threshold. */ run: Task.async(function*(burner, max, listener) { listener.reset(); @@ -37,7 +39,7 @@ CPUBurner.prototype = { } catch (ex) { return false; } - if (listener.triggered) { + if (listener.triggered && listener.result >= this.jankThreshold) { return true; } } @@ -161,7 +163,8 @@ AlertListener.prototype = { * Simulate a slow add-on. */ function AddonBurner(addonId = "fake add-on id: " + Math.random()) { - CPUBurner.call(this, `http://example.com/?uri=${addonId}`) + this.jankThreshold = 200000; + CPUBurner.call(this, `http://example.com/?uri=${addonId}`, this.jankThreshold); this._addonId = addonId; this._sandbox = Components.utils.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), { addonId: this._addonId }); this._CPOWBurner = null; From 02dde2e10dd64720eb7fe4c6b7690372ff4e0ff5 Mon Sep 17 00:00:00 2001 From: "Nils Ohlmeier [:drno]" Date: Mon, 29 Aug 2016 16:34:22 -0700 Subject: [PATCH 14/65] Bug 1298991: only create candidate pairs from STUN response when needed. r=bwc MozReview-Commit-ID: KpqNyi0FVb3 --HG-- extra : rebase_source : 8bde1830fa0a37205f647a4d594583e4641d041a --- media/mtransport/test/ice_unittest.cpp | 2 +- .../nICEr/src/ice/ice_candidate_pair.c | 44 +++++++++++-------- .../nICEr/src/ice/ice_media_stream.c | 18 ++++++++ .../nICEr/src/ice/ice_media_stream.h | 1 + 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/media/mtransport/test/ice_unittest.cpp b/media/mtransport/test/ice_unittest.cpp index 483260a0c0ba..a3dbdc7cfb0e 100644 --- a/media/mtransport/test/ice_unittest.cpp +++ b/media/mtransport/test/ice_unittest.cpp @@ -763,7 +763,7 @@ class IceTestPeer : public sigslot::has_slots<> { remote->GetCandidates(i); for (size_t j=0; jParseAttributes(candidates); ASSERT_TRUE(NS_SUCCEEDED(res)); diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c index a09b7bfd7a71..6c07ae2a6c3a 100644 --- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c +++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c @@ -181,7 +181,8 @@ int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg) { int r,_status; - nr_ice_cand_pair *pair=cb_arg,*orig_pair; + nr_ice_cand_pair *pair=cb_arg; + nr_ice_cand_pair *actual_pair=0; nr_ice_candidate *cand=0; nr_stun_message *sres; nr_transport_addr *request_src; @@ -256,14 +257,17 @@ static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg) cand=TAILQ_FIRST(&pair->local->component->candidates); while(cand){ - if(!nr_transport_addr_cmp(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + if(!nr_transport_addr_cmp(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): found pre-existing local candidate of type %d for mapped address %s", pair->pctx->label,cand->type,cand->addr.as_string); + assert(cand->type != HOST); break; + } cand=TAILQ_NEXT(cand,entry_comp); } - /* OK, nothing found, must be peer reflexive */ if(!cand) { + /* OK, nothing found, must be a new peer reflexive */ if (pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) { /* Any STUN response with a reflexive address in it is unwanted when we'll send on relay only. Bail since cand is used below. */ @@ -277,27 +281,31 @@ static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg) ABORT(r); cand->state=NR_ICE_CAND_STATE_INITIALIZED; TAILQ_INSERT_TAIL(&pair->local->component->candidates,cand,entry_comp); + } else { + /* Check if we have a pair for this candidate already. */ + if(r=nr_ice_media_stream_find_pair(pair->remote->stream, cand, pair->remote, &actual_pair)) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no pair exists for %s and %s", pair->pctx->label,cand->addr.as_string, pair->remote->addr.as_string); + } } - /* Note: we stomp the existing pair! */ - orig_pair=pair; - if(r=nr_ice_candidate_pair_create(pair->pctx,cand,pair->remote, - &pair)) - ABORT(r); + if(!actual_pair) { + if(r=nr_ice_candidate_pair_create(pair->pctx,cand,pair->remote, &actual_pair)) + ABORT(r); - nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_SUCCEEDED); + if(r=nr_ice_component_insert_pair(actual_pair->remote->component,actual_pair)) + ABORT(r); - if(r=nr_ice_component_insert_pair(pair->remote->component,pair)) - ABORT(r); + /* If the original pair was nominated, make us nominated too. */ + if(pair->peer_nominated) + actual_pair->peer_nominated=1; - /* If the original pair was nominated, make us nominated, - since we replace him*/ - if(orig_pair->peer_nominated) - pair->peer_nominated=1; + /* Now mark the orig pair failed */ + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); + } - - /* Now mark the orig pair failed */ - nr_ice_candidate_pair_set_state(orig_pair->pctx,orig_pair,NR_ICE_PAIR_STATE_FAILED); + assert(actual_pair); + nr_ice_candidate_pair_set_state(actual_pair->pctx,actual_pair,NR_ICE_PAIR_STATE_SUCCEEDED); + pair=actual_pair; } diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c index 6bdd99a78fce..55f6a3b3e31b 100644 --- a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c +++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c @@ -913,3 +913,21 @@ void nr_ice_media_stream_role_change(nr_ice_media_stream *stream) } } +int nr_ice_media_stream_find_pair(nr_ice_media_stream *str, nr_ice_candidate *lcand, nr_ice_candidate *rcand, nr_ice_cand_pair **pair) + { + nr_ice_cand_pair_head *head = &str->check_list; + nr_ice_cand_pair *c1; + + c1=TAILQ_FIRST(head); + while(c1){ + if(c1->local == lcand && + c1->remote == rcand) { + *pair=c1; + return(0); + } + + c1=TAILQ_NEXT(c1,check_queue_entry); + } + + return(R_NOT_FOUND); + } diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h index 8cb118a8245b..3d818d5300ec 100644 --- a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h +++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h @@ -92,6 +92,7 @@ int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int compone int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, UCHAR *data, int len); int nr_ice_media_stream_get_active(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_ice_candidate **local, nr_ice_candidate **remote); int nr_ice_media_stream_find_component(nr_ice_media_stream *str, int comp_id, nr_ice_component **compp); +int nr_ice_media_stream_find_pair(nr_ice_media_stream *str, nr_ice_candidate *local, nr_ice_candidate *remote, nr_ice_cand_pair **pair); int nr_ice_media_stream_addrs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_transport_addr *local, nr_transport_addr *remote); int nr_ice_peer_ctx_parse_media_stream_attribute(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *attr); From 20189a5b9573841153c79b736014e915bf5c5d75 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 30 Aug 2016 15:15:30 +1000 Subject: [PATCH 15/65] Bug 1297265: P1. Remove decode ahead logic. r=kamidphish The MediaFormatReader will no longer attempt to decode several frames in advance and ahead of the MDSM actually requesting it. The speed advantages were dubious at best, and as most MediaDataDecoders abused the use of InputExhausted callbacks we had to place artificial throttle that would often cause side effects. As such, it is now expected that the MediaDataDecoder will now always either return a decoded sample or call InputExhausted. Never both. MediaDataDecoder will continue to work as-is, even if they call InpuxExhausted too many times as the MediaFormatReader will only feed a single sample at a time. MozReview-Commit-ID: 9KUpNP9jozV --HG-- extra : rebase_source : ebb919fd3f1ce1adf5d08ed3f4292839b84c8321 --- dom/media/MediaFormatReader.cpp | 70 +++++++++++++-------------------- dom/media/MediaFormatReader.h | 28 ++++++------- 2 files changed, 38 insertions(+), 60 deletions(-) diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 6053a3cc3f2f..2d8ffc174c63 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -62,9 +62,9 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder, VideoFrameContainer* aVideoFrameContainer, layers::LayersBackend aLayersBackend) : MediaDecoderReader(aDecoder) - , mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2), + , mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-max-decode-error", 3)) - , mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2), + , mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-max-decode-error", 2)) , mDemuxer(aDemuxer) , mDemuxerInitDone(false) @@ -562,7 +562,7 @@ MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe, } RefPtr p = mVideo.EnsurePromise(__func__); - NotifyDecodingRequested(TrackInfo::kVideoTrack); + ScheduleUpdate(TrackInfo::kVideoTrack); return p; } @@ -606,7 +606,6 @@ MediaFormatReader::OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure void MediaFormatReader::DoDemuxVideo() { - // TODO Use DecodeAhead value rather than 1. mVideo.mDemuxRequest.Begin(mVideo.mTrackDemuxer->GetSamples(1) ->Then(OwnerThread(), __func__, this, &MediaFormatReader::OnVideoDemuxCompleted, @@ -657,7 +656,7 @@ MediaFormatReader::RequestAudioData() } RefPtr p = mAudio.EnsurePromise(__func__); - NotifyDecodingRequested(TrackInfo::kAudioTrack); + ScheduleUpdate(TrackInfo::kAudioTrack); return p; } @@ -665,7 +664,6 @@ MediaFormatReader::RequestAudioData() void MediaFormatReader::DoDemuxAudio() { - // TODO Use DecodeAhead value rather than 1. mAudio.mDemuxRequest.Begin(mAudio.mTrackDemuxer->GetSamples(1) ->Then(OwnerThread(), __func__, this, &MediaFormatReader::OnAudioDemuxCompleted, @@ -697,6 +695,7 @@ MediaFormatReader::NotifyNewOutput(TrackType aTrack, MediaData* aSample) decoder.mOutput.AppendElement(aSample); decoder.mNumSamplesOutput++; decoder.mNumOfConsecutiveError = 0; + decoder.mDecodePending = false; ScheduleUpdate(aTrack); } @@ -706,7 +705,7 @@ MediaFormatReader::NotifyInputExhausted(TrackType aTrack) MOZ_ASSERT(OnTaskQueue()); LOGV("Decoder has requested more %s data", TrackTypeToStr(aTrack)); auto& decoder = GetDecoderData(aTrack); - decoder.mInputExhausted = true; + decoder.mDecodePending = false; ScheduleUpdate(aTrack); } @@ -755,33 +754,21 @@ MediaFormatReader::NotifyEndOfStream(TrackType aTrack) ScheduleUpdate(aTrack); } -void -MediaFormatReader::NotifyDecodingRequested(TrackType aTrack) -{ - MOZ_ASSERT(OnTaskQueue()); - auto& decoder = GetDecoderData(aTrack); - decoder.mDecodingRequested = true; - ScheduleUpdate(aTrack); -} - bool MediaFormatReader::NeedInput(DecoderData& aDecoder) { - // We try to keep a few more compressed samples input than decoded samples - // have been output, provided the state machine has requested we send it a - // decoded sample. To account for H.264 streams which may require a longer - // run of input than we input, decoders fire an "input exhausted" callback, - // which overrides our "few more samples" threshold. + // To account for H.264 streams which may require a longer + // run of input than we input, decoders fire an "input exhausted" callback. + // The decoder will not be fed a new raw sample until either Output callback + // has been called, or InputExhausted was called. return + (aDecoder.HasPromise() || aDecoder.mTimeThreshold.isSome()) && !aDecoder.HasPendingDrain() && !aDecoder.HasFatalError() && - aDecoder.mDecodingRequested && !aDecoder.mDemuxRequest.Exists() && + !aDecoder.mOutput.Length() && !aDecoder.HasInternalSeekPending() && - aDecoder.mOutput.Length() <= aDecoder.mDecodeAhead && - (aDecoder.mInputExhausted || !aDecoder.mQueuedSamples.IsEmpty() || - aDecoder.mTimeThreshold.isSome() || - aDecoder.mNumSamplesInput - aDecoder.mNumSamplesOutput <= aDecoder.mDecodeAhead); + !aDecoder.mDecodePending; } void @@ -932,6 +919,7 @@ MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack, LOG("Unable to pass frame to decoder"); return false; } + decoder.mDecodePending = true; return true; } @@ -1007,7 +995,7 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack, decoder.ShutdownDecoder(); if (sample->mKeyframe) { decoder.mQueuedSamples.AppendElements(Move(samples)); - NotifyDecodingRequested(aTrack); + ScheduleUpdate(aTrack); } else { TimeInterval time = TimeInterval(TimeUnit::FromMicroseconds(sample->mTime), @@ -1045,9 +1033,6 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack, } samplesPending = true; } - - // We have serviced the decoder's request for more data. - decoder.mInputExhausted = false; } void @@ -1071,7 +1056,7 @@ MediaFormatReader::InternalSeek(TrackType aTrack, const InternalSeekTarget& aTar "Seek promise must be disconnected when timethreshold is reset"); decoder.mTimeThreshold.ref().mHasSeeked = true; self->SetVideoDecodeThreshold(); - self->NotifyDecodingRequested(aTrack); + self->ScheduleUpdate(aTrack); }, [self, aTrack] (DemuxerFailureReason aResult) { auto& decoder = self->GetDecoderData(aTrack); @@ -1275,6 +1260,7 @@ MediaFormatReader::Update(TrackType aTrack) if (decoder.mError && decoder.mError.ref() == MediaDataDecoderError::DECODE_ERROR) { + decoder.mDecodePending = false; decoder.mError.reset(); if (++decoder.mNumOfConsecutiveError > decoder.mMaxConsecutiveError) { NotifyError(aTrack); @@ -1292,11 +1278,11 @@ MediaFormatReader::Update(TrackType aTrack) bool needInput = NeedInput(decoder); - LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d ahead:%d sid:%u", - TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted, + LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d promise:%d sid:%u", + TrackTypeToStr(aTrack), needInput, needOutput, decoder.mDecodePending, decoder.mNumSamplesInput, decoder.mNumSamplesOutput, uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()), - decoder.mWaitingForData, !decoder.HasPromise(), decoder.mLastStreamSourceID); + decoder.mWaitingForData, decoder.HasPromise(), decoder.mLastStreamSourceID); if (decoder.mWaitingForData && (!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) { @@ -1577,7 +1563,7 @@ MediaFormatReader::OnVideoSkipCompleted(uint32_t aSkipped) VideoSkipReset(aSkipped); - NotifyDecodingRequested(TrackInfo::kVideoTrack); + ScheduleUpdate(TrackInfo::kVideoTrack); } void @@ -1595,7 +1581,7 @@ MediaFormatReader::OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailu DropDecodedSamples(TrackInfo::kVideoTrack); // We can't complete the skip operation, will just service a video frame // normally. - NotifyDecodingRequested(TrackInfo::kVideoTrack); + ScheduleUpdate(TrackInfo::kVideoTrack); break; case DemuxerFailureReason::CANCELED: MOZ_FALLTHROUGH; case DemuxerFailureReason::SHUTDOWN: @@ -2013,12 +1999,11 @@ MediaFormatReader::GetMozDebugReaderData(nsAString& aString) result += nsPrintfCString("audio frames decoded: %lld\n", mAudio.mNumSamplesOutputTotal); if (HasAudio()) { - result += nsPrintfCString("audio state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d decoder:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", + result += nsPrintfCString("audio state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", NeedInput(mAudio), mAudio.HasPromise(), - mAudio.mInputExhausted, + mAudio.mDecodePending, mAudio.mDemuxRequest.Exists(), int(mAudio.mQueuedSamples.Length()), - mAudio.mDecodingRequested, mAudio.mTimeThreshold ? mAudio.mTimeThreshold.ref().Time().ToSeconds() : -1.0, @@ -2037,12 +2022,11 @@ MediaFormatReader::GetMozDebugReaderData(nsAString& aString) mVideo.mNumSamplesOutputTotal, mVideo.mNumSamplesSkippedTotal); if (HasVideo()) { - result += nsPrintfCString("video state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d decoder:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", + result += nsPrintfCString("video state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", NeedInput(mVideo), mVideo.HasPromise(), - mVideo.mInputExhausted, + mVideo.mDecodePending, mVideo.mDemuxRequest.Exists(), int(mVideo.mQueuedSamples.Length()), - mVideo.mDecodingRequested, mVideo.mTimeThreshold ? mVideo.mTimeThreshold.ref().Time().ToSeconds() : -1.0, @@ -2080,7 +2064,7 @@ MediaFormatReader::SetBlankDecode(TrackType aTrack, bool aIsBlankDecode) decoder.mIsBlankDecode = aIsBlankDecode; decoder.Flush(); decoder.ShutdownDecoder(); - NotifyDecodingRequested(TrackInfo::kVideoTrack); // Calls ScheduleUpdate(). + ScheduleUpdate(TrackInfo::kVideoTrack); return; } diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index 1c61b4644437..f632a18b9272 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -169,7 +169,6 @@ private: void NotifyError(TrackType aTrack, MediaDataDecoderError aError = MediaDataDecoderError::FATAL_ERROR); void NotifyWaitingForData(TrackType aTrack); void NotifyEndOfStream(TrackType aTrack); - void NotifyDecodingRequested(TrackType aTrack); void ExtractCryptoInitData(nsTArray& aInitData); @@ -231,21 +230,18 @@ private: struct DecoderData { DecoderData(MediaFormatReader* aOwner, MediaData::Type aType, - uint32_t aDecodeAhead, uint32_t aNumOfMaxError) : mOwner(aOwner) , mType(aType) , mMonitor("DecoderData") , mDescription("shutdown") - , mDecodeAhead(aDecodeAhead) , mUpdateScheduled(false) , mDemuxEOS(false) , mWaitingForData(false) , mReceivedNewData(false) , mDecoderInitialized(false) - , mDecodingRequested(false) , mOutputRequested(false) - , mInputExhausted(false) + , mDecodePending(false) , mNeedDraining(false) , mDraining(false) , mDrainComplete(false) @@ -288,7 +284,6 @@ private: } // Only accessed from reader's task queue. - uint32_t mDecodeAhead; bool mUpdateScheduled; bool mDemuxEOS; bool mWaitingForData; @@ -312,11 +307,14 @@ private: MozPromiseRequestHolder mInitPromise; // False when decoder is created. True when decoder Init() promise is resolved. bool mDecoderInitialized; - // Set when decoding can proceed. It is reset when a decoding promise is - // rejected or prior a seek operation. - bool mDecodingRequested; bool mOutputRequested; - bool mInputExhausted; + // Set to true once the MediaDataDecoder has been fed a compressed sample. + // No more sample will be passed to the decoder while true. + // mDecodePending is reset when: + // 1- The decoder returns a sample + // 2- The decoder calls InputExhausted + // 3- The decoder is Flushed or Reset. + bool mDecodePending; bool mNeedDraining; bool mDraining; bool mDrainComplete; @@ -376,9 +374,8 @@ private: if (mDecoder) { mDecoder->Flush(); } - mDecodingRequested = false; mOutputRequested = false; - mInputExhausted = false; + mDecodePending = false; mOutput.Clear(); mNumSamplesInput = 0; mNumSamplesOutput = 0; @@ -397,10 +394,9 @@ private: mDemuxEOS = false; mWaitingForData = false; mQueuedSamples.Clear(); - mDecodingRequested = false; mOutputRequested = false; - mInputExhausted = false; mNeedDraining = false; + mDecodePending = false; mDraining = false; mDrainComplete = false; mTimeThreshold.reset(); @@ -441,9 +437,8 @@ private: public: DecoderDataWithPromise(MediaFormatReader* aOwner, MediaData::Type aType, - uint32_t aDecodeAhead, uint32_t aNumOfMaxError) - : DecoderData(aOwner, aType, aDecodeAhead, aNumOfMaxError) + : DecoderData(aOwner, aType, aNumOfMaxError) , mHasPromise(false) {} @@ -472,7 +467,6 @@ private: { MOZ_ASSERT(mOwner->OnTaskQueue()); mPromise.Reject(aReason, aMethodName); - mDecodingRequested = false; mHasPromise = false; } From 3734f817c6d27f95674299fcf6417f32615650e5 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 30 Aug 2016 15:17:47 +1000 Subject: [PATCH 16/65] Bug 1297265: P2. Amend MediaDataDecoder documentation to emphasize the new expected behavior. r=kamidphish MozReview-Commit-ID: EHFnCnc58qh --HG-- extra : rebase_source : 2a9e2c4a08103d10f77ceaee3f87300a1b7a56a2 --- dom/media/platforms/PlatformDecoderModule.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dom/media/platforms/PlatformDecoderModule.h b/dom/media/platforms/PlatformDecoderModule.h index 76577ab51eed..c63a71861129 100644 --- a/dom/media/platforms/PlatformDecoderModule.h +++ b/dom/media/platforms/PlatformDecoderModule.h @@ -175,6 +175,8 @@ public: // Denotes that the last input sample has been inserted into the decoder, // and no more output can be produced unless more input is sent. + // InputExhausted should only be called if no decoded samples were returned + // from the last input. virtual void InputExhausted() = 0; virtual void DrainComplete() = 0; From e88897a8a348bb25b964741e40823ac17d7be7ad Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 30 Aug 2016 15:18:25 +1000 Subject: [PATCH 17/65] Bug 1297265: P3. Rework Apple VT use of InputExhausted. r=me The only time we need to use InputExhausted is for the initial video decoding or when a frame is dropped. MozReview-Commit-ID: IrHqZXJwQe1 --HG-- extra : rebase_source : d9fec0f88d2c0a878723d75d79aa3ff63b5938cc --- dom/media/platforms/apple/AppleVTDecoder.cpp | 33 ++++---------------- dom/media/platforms/apple/AppleVTDecoder.h | 8 ----- 2 files changed, 6 insertions(+), 35 deletions(-) diff --git a/dom/media/platforms/apple/AppleVTDecoder.cpp b/dom/media/platforms/apple/AppleVTDecoder.cpp index b7364d9fb68a..61f1a27b5012 100644 --- a/dom/media/platforms/apple/AppleVTDecoder.cpp +++ b/dom/media/platforms/apple/AppleVTDecoder.cpp @@ -34,11 +34,9 @@ AppleVTDecoder::AppleVTDecoder(const VideoInfo& aConfig, , mPictureHeight(aConfig.mImage.height) , mDisplayWidth(aConfig.mDisplay.width) , mDisplayHeight(aConfig.mDisplay.height) - , mQueuedSamples(0) , mTaskQueue(aTaskQueue) , mMaxRefFrames(mp4_demuxer::H264::ComputeMaxRefFrames(aConfig.mExtraData)) , mImageContainer(aImageContainer) - , mInputIncoming(0) , mIsShutDown(false) #ifdef MOZ_WIDGET_UIKIT , mUseSoftwareImages(true) @@ -88,8 +86,6 @@ AppleVTDecoder::Input(MediaRawData* aSample) aSample->mKeyframe ? " keyframe" : "", aSample->Size()); - mInputIncoming++; - mTaskQueue->Dispatch(NewRunnableMethod>( this, &AppleVTDecoder::ProcessDecode, aSample)); return NS_OK; @@ -104,8 +100,6 @@ AppleVTDecoder::Flush() NewRunnableMethod(this, &AppleVTDecoder::ProcessFlush); SyncRunnable::DispatchToThread(mTaskQueue, runnable); mIsFlushing = false; - // All ProcessDecode() tasks should be done. - MOZ_ASSERT(mInputIncoming == 0); mSeekTargetThreshold.reset(); @@ -142,18 +136,11 @@ AppleVTDecoder::ProcessDecode(MediaRawData* aSample) { AssertOnTaskQueueThread(); - mInputIncoming--; - if (mIsFlushing) { return NS_OK; } auto rv = DoDecode(aSample); - // Ask for more data. - if (NS_SUCCEEDED(rv) && !mInputIncoming && mQueuedSamples <= mMaxRefFrames) { - LOG("%s task queue empty; requesting more data", GetDescriptionName()); - mCallback->InputExhausted(); - } return rv; } @@ -213,7 +200,6 @@ AppleVTDecoder::DrainReorderedFrames() while (!mReorderQueue.IsEmpty()) { mCallback->Output(mReorderQueue.Pop().get()); } - mQueuedSamples = 0; } void @@ -223,7 +209,6 @@ AppleVTDecoder::ClearReorderedFrames() while (!mReorderQueue.IsEmpty()) { mReorderQueue.Pop(); } - mQueuedSamples = 0; } void @@ -288,16 +273,10 @@ AppleVTDecoder::OutputFrame(CVPixelBufferRef aImage, aFrameRef.is_sync_point ? " keyframe" : "" ); - if (mQueuedSamples > mMaxRefFrames) { - // We had stopped requesting more input because we had received too much at - // the time. We can ask for more once again. - mCallback->InputExhausted(); - } - MOZ_ASSERT(mQueuedSamples); - mQueuedSamples--; - if (!aImage) { - // Image was dropped by decoder. + // Image was dropped by decoder or none return yet. + // We need more input to continue. + mCallback->InputExhausted(); return NS_OK; } @@ -410,8 +389,10 @@ AppleVTDecoder::OutputFrame(CVPixelBufferRef aImage, // in composition order. MonitorAutoLock mon(mMonitor); mReorderQueue.Push(data); - while (mReorderQueue.Length() > mMaxRefFrames) { + if (mReorderQueue.Length() > mMaxRefFrames) { mCallback->Output(mReorderQueue.Pop().get()); + } else { + mCallback->InputExhausted(); } LOG("%llu decoded frames queued", static_cast(mReorderQueue.Length())); @@ -480,8 +461,6 @@ AppleVTDecoder::DoDecode(MediaRawData* aSample) return NS_ERROR_FAILURE; } - mQueuedSamples++; - VTDecodeFrameFlags decodeFlags = kVTDecodeFrame_EnableAsynchronousDecompression; rv = VTDecompressionSessionDecodeFrame(mSession, diff --git a/dom/media/platforms/apple/AppleVTDecoder.h b/dom/media/platforms/apple/AppleVTDecoder.h index ce65d3ade804..5e0b45029864 100644 --- a/dom/media/platforms/apple/AppleVTDecoder.h +++ b/dom/media/platforms/apple/AppleVTDecoder.h @@ -90,11 +90,6 @@ private: const uint32_t mDisplayWidth; const uint32_t mDisplayHeight; - // Number of times a sample was queued via Input(). Will be decreased upon - // the decoder's callback being invoked. - // This is used to calculate how many frames has been buffered by the decoder. - Atomic mQueuedSamples; - // Method to set up the decompression session. nsresult InitializeSession(); nsresult WaitForAsynchronousFrames(); @@ -106,9 +101,6 @@ private: const RefPtr mTaskQueue; const uint32_t mMaxRefFrames; const RefPtr mImageContainer; - // Increased when Input is called, and decreased when ProcessFrame runs. - // Reaching 0 indicates that there's no pending Input. - Atomic mInputIncoming; Atomic mIsShutDown; const bool mUseSoftwareImages; From 97c82d5ef333455ddad6cc58ec032fb17bfadde4 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 30 Aug 2016 15:19:55 +1000 Subject: [PATCH 18/65] Bug 1297265: P4. Rework Apple AudioToolbox use of InputExhausted. r=kamidphish MozReview-Commit-ID: HznYxl9T7t5 --HG-- extra : rebase_source : aa1ebf557c56caf9572eab16df5ecaa4c1a440a6 --- dom/media/platforms/apple/AppleATDecoder.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dom/media/platforms/apple/AppleATDecoder.cpp b/dom/media/platforms/apple/AppleATDecoder.cpp index 7ec421658321..a97607326456 100644 --- a/dom/media/platforms/apple/AppleATDecoder.cpp +++ b/dom/media/platforms/apple/AppleATDecoder.cpp @@ -214,9 +214,7 @@ AppleATDecoder::SubmitSample(MediaRawData* aSample) } } mQueuedSamples.Clear(); - } - - if (mTaskQueue->IsEmpty()) { + } else { mCallback->InputExhausted(); } } @@ -282,6 +280,9 @@ AppleATDecoder::DecodeSample(MediaRawData* aSample) } while (true); if (outputData.IsEmpty()) { + // We aren't going to output anything, inform the reader that we need more + // data. + mCallback->InputExhausted(); return NS_OK; } From 3052030d0ed6ac2382554f6a40badc0f1e0dff95 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 30 Aug 2016 15:20:40 +1000 Subject: [PATCH 19/65] Bug 1297265: P5. Rework Blank Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: KEfWo1cdRkG --HG-- extra : rebase_source : 46adb7b4d81370ad4217a48e6855afcbfe71c2d6 --- dom/media/platforms/agnostic/BlankDecoderModule.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dom/media/platforms/agnostic/BlankDecoderModule.cpp b/dom/media/platforms/agnostic/BlankDecoderModule.cpp index c8252599d113..34c1b06474fe 100644 --- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp +++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp @@ -93,14 +93,13 @@ private: // Frames come out in DTS order but we need to output them in PTS order. mReorderQueue.Push(aData); - while (mReorderQueue.Length() > mMaxRefFrames) { - mCallback->Output(mReorderQueue.Pop().get()); - } - if (mReorderQueue.Length() <= mMaxRefFrames) { mCallback->InputExhausted(); + } else { + while (mReorderQueue.Length() > mMaxRefFrames) { + mCallback->Output(mReorderQueue.Pop().get()); + } } - } private: From 5aa7d037b4228de402789c8c65e5718fb793272d Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 30 Aug 2016 15:21:05 +1000 Subject: [PATCH 20/65] Bug 1297265: P6. Rework Opus Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: 4Usxx2BrQJt --HG-- extra : rebase_source : 5c74c41db2d11171d4a9c22e712fffb85168caf3 --- dom/media/platforms/agnostic/OpusDecoder.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dom/media/platforms/agnostic/OpusDecoder.cpp b/dom/media/platforms/agnostic/OpusDecoder.cpp index c068fe37b8f0..b90f5af67110 100644 --- a/dom/media/platforms/agnostic/OpusDecoder.cpp +++ b/dom/media/platforms/agnostic/OpusDecoder.cpp @@ -165,10 +165,6 @@ OpusDataDecoder::ProcessDecode(MediaRawData* aSample) case DecodeError::DECODE_SUCCESS: break; } - - if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); - } } OpusDataDecoder::DecodeError From 1c4550a35fbaa39a45a7963d5cf4da4f2cbe1889 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 30 Aug 2016 15:21:29 +1000 Subject: [PATCH 21/65] Bug 1297265: P7. Rework Theora Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: 1udj9o3r2YE --HG-- extra : rebase_source : ba19c9221e19a372c906231443aa2e17bb67dd67 --- dom/media/platforms/agnostic/TheoraDecoder.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/dom/media/platforms/agnostic/TheoraDecoder.cpp b/dom/media/platforms/agnostic/TheoraDecoder.cpp index 9a7e33dd4d91..b7f1c7474c6a 100644 --- a/dom/media/platforms/agnostic/TheoraDecoder.cpp +++ b/dom/media/platforms/agnostic/TheoraDecoder.cpp @@ -202,8 +202,6 @@ TheoraDecoder::ProcessDecode(MediaRawData* aSample) } if (DoDecode(aSample) == -1) { mCallback->Error(MediaDataDecoderError::DECODE_ERROR); - } else if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); } } From c3939b1976df0d48e5bac3eed3bf8a29c8fe58ed Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 30 Aug 2016 15:21:49 +1000 Subject: [PATCH 22/65] Bug 1297265: P8. Rework LibVPX Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: H7KnDpkcaeT --HG-- extra : rebase_source : 7b63bc3e0571cff0047f141f61797b6510399bf5 --- dom/media/platforms/agnostic/VPXDecoder.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dom/media/platforms/agnostic/VPXDecoder.cpp b/dom/media/platforms/agnostic/VPXDecoder.cpp index 1f6ed882a8b4..579765fe59d8 100644 --- a/dom/media/platforms/agnostic/VPXDecoder.cpp +++ b/dom/media/platforms/agnostic/VPXDecoder.cpp @@ -121,6 +121,7 @@ VPXDecoder::DoDecode(MediaRawData* aSample) vpx_codec_iter_t iter = nullptr; vpx_image_t *img; + bool didOutput = false; while ((img = vpx_codec_get_frame(&mVPX, &iter))) { NS_ASSERTION(img->fmt == VPX_IMG_FMT_I420 || @@ -179,6 +180,10 @@ VPXDecoder::DoDecode(MediaRawData* aSample) return -1; } mCallback->Output(v); + didOutput = true; + } + if (!didOutput) { + mCallback->InputExhausted(); } return 0; } @@ -192,8 +197,6 @@ VPXDecoder::ProcessDecode(MediaRawData* aSample) } if (DoDecode(aSample) == -1) { mCallback->Error(MediaDataDecoderError::DECODE_ERROR); - } else if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); } } From b31a3e29a2431b9547bc04286adf2a545c39d005 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 30 Aug 2016 15:22:14 +1000 Subject: [PATCH 23/65] Bug 1297265: P9. Rework Vorbis Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: 58KKxY1YcpK --HG-- extra : rebase_source : 7258069eb8ebf80b10453e9eba8d8e220647b9ec --- dom/media/platforms/agnostic/VorbisDecoder.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/dom/media/platforms/agnostic/VorbisDecoder.cpp b/dom/media/platforms/agnostic/VorbisDecoder.cpp index 7b240100d06f..31193132b875 100644 --- a/dom/media/platforms/agnostic/VorbisDecoder.cpp +++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp @@ -143,8 +143,6 @@ VorbisDataDecoder::ProcessDecode(MediaRawData* aSample) } if (DoDecode(aSample) == -1) { mCallback->Error(MediaDataDecoderError::DECODE_ERROR); - } else if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); } } From 02473df25a699a104edd8f8b7a122424a9b117ea Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 30 Aug 2016 15:22:36 +1000 Subject: [PATCH 24/65] Bug 1297265: P10. Rework FFmpeg Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: 8fndcHQELTo --HG-- extra : rebase_source : f54aea05056e674a76431a8a95225e13e8b6b57f --- dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp | 4 +++- dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp index 16574a805dff..508f25ffc91e 100644 --- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp @@ -133,6 +133,7 @@ FFmpegAudioDecoder::DoDecode(MediaRawData* aSample) int64_t samplePosition = aSample->mOffset; media::TimeUnit pts = media::TimeUnit::FromMicroseconds(aSample->mTime); + bool didOutput = false; while (packet.size > 0) { int decoded; @@ -181,6 +182,7 @@ FFmpegAudioDecoder::DoDecode(MediaRawData* aSample) numChannels, samplingRate); mCallback->Output(data); + didOutput = true; pts += duration; if (!pts.IsValid()) { NS_WARNING("Invalid count of accumulated audio samples"); @@ -192,7 +194,7 @@ FFmpegAudioDecoder::DoDecode(MediaRawData* aSample) samplePosition += bytesConsumed; } - return DecodeResult::DECODE_FRAME; + return didOutput ? DecodeResult::DECODE_FRAME : DecodeResult::DECODE_NO_FRAME; } void diff --git a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp index 5abcb26b082a..eeb39bc27a51 100644 --- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp @@ -117,10 +117,11 @@ FFmpegDataDecoder::ProcessDecode(MediaRawData* aSample) case DecodeResult::FATAL_ERROR: mCallback->Error(MediaDataDecoderError::FATAL_ERROR); break; + case DecodeResult::DECODE_NO_FRAME: + mCallback->InputExhausted(); + break; default: - if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); - } + break; } } From 223ee82386f3c31bc4a12b6b9b93addd8eb114a8 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Tue, 30 Aug 2016 15:22:54 +1000 Subject: [PATCH 25/65] Bug 1297265: P11. Rework WMF Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: GX1izQd55Di --HG-- extra : rebase_source : efc6198d4fa7914b13f808216bd6394ca5b53d43 --- dom/media/platforms/wmf/WMFMediaDataDecoder.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp index b763748e1828..e7138f7886c3 100644 --- a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp +++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp @@ -143,13 +143,15 @@ WMFMediaDataDecoder::ProcessOutput() { RefPtr output; HRESULT hr = S_OK; + bool didOutput = false; while (SUCCEEDED(hr = mMFTManager->Output(mLastStreamOffset, output)) && output) { mHasSuccessfulOutput = true; mCallback->Output(output); + didOutput = true; } if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { - if (mTaskQueue->IsEmpty()) { + if (!didOutput) { mCallback->InputExhausted(); } } else if (FAILED(hr)) { From 650d82dba8680dfd2a11bac33c03ed7f01d148d9 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Wed, 31 Aug 2016 17:16:22 +0900 Subject: [PATCH 26/65] Bug 1299400 - Remove WebActivities Android Backend. r=sebastian WebActivities API has removed by bug 1284455, so we should remove Android backend. MozReview-Commit-ID: IOYs1bM2u06 --HG-- extra : rebase_source : c6c701159817fe8595c20205c2fc3a2253ffd39f --- .../java/org/mozilla/gecko/IntentHelper.java | 9 - mobile/android/base/moz.build | 1 - .../components/AndroidActivitiesGlue.js | 31 --- .../components/MobileComponents.manifest | 4 - mobile/android/components/moz.build | 1 - .../mozilla/gecko/util/WebActivityMapper.java | 221 ------------------ mobile/android/installer/package-manifest.in | 9 - 7 files changed, 276 deletions(-) delete mode 100644 mobile/android/components/AndroidActivitiesGlue.js delete mode 100644 mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WebActivityMapper.java diff --git a/mobile/android/base/java/org/mozilla/gecko/IntentHelper.java b/mobile/android/base/java/org/mozilla/gecko/IntentHelper.java index 39b5cae8db15..debe24d30950 100644 --- a/mobile/android/base/java/org/mozilla/gecko/IntentHelper.java +++ b/mobile/android/base/java/org/mozilla/gecko/IntentHelper.java @@ -12,7 +12,6 @@ import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.JSONUtils; import org.mozilla.gecko.util.NativeEventListener; import org.mozilla.gecko.util.NativeJSObject; -import org.mozilla.gecko.util.WebActivityMapper; import org.mozilla.gecko.widget.ExternalIntentDuringPrivateBrowsingPromptFragment; import org.json.JSONArray; @@ -48,7 +47,6 @@ public final class IntentHelper implements GeckoEventListener, "Intent:GetHandlers", "Intent:Open", "Intent:OpenForResult", - "WebActivity:Open" }; private static final String[] NATIVE_EVENTS = { @@ -416,8 +414,6 @@ public final class IntentHelper implements GeckoEventListener, open(message); } else if (event.equals("Intent:OpenForResult")) { openForResult(message); - } else if (event.equals("WebActivity:Open")) { - openWebActivity(message); } } catch (JSONException e) { Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); @@ -572,11 +568,6 @@ public final class IntentHelper implements GeckoEventListener, return UNKNOWN_PROTOCOL_URI_PREFIX + encodedUri; } - private void openWebActivity(JSONObject message) throws JSONException { - final Intent intent = WebActivityMapper.getIntentForWebActivity(message.getJSONObject("activity")); - ActivityHandlerHelper.startIntentForActivity(activity, intent, new ResultHandler(message)); - } - private static class ResultHandler implements ActivityResultHandler { private final JSONObject message; diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index ccf953a588d6..a08d4bcb82bc 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -139,7 +139,6 @@ gujar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x for x in 'util/UIAsyncTask.java', 'util/UUIDUtil.java', 'util/WeakReferenceHandler.java', - 'util/WebActivityMapper.java', 'util/WindowUtils.java', ]] gujar.extra_jars = [ diff --git a/mobile/android/components/AndroidActivitiesGlue.js b/mobile/android/components/AndroidActivitiesGlue.js deleted file mode 100644 index 630b84c36751..000000000000 --- a/mobile/android/components/AndroidActivitiesGlue.js +++ /dev/null @@ -1,31 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict" - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Messaging.jsm"); - -function ActivitiesGlue() { } - -ActivitiesGlue.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIActivityUIGlue]), - classID: Components.ID("{e4deb5f6-d5e3-4fce-bc53-901dd9951c48}"), - - // Ignore aActivities results on Android, go straight to Android intents. - chooseActivity: function ap_chooseActivity(aOptions, aActivities, aCallback) { - Messaging.sendRequestForResult({ - type: "WebActivity:Open", - activity: { name: aOptions.name, data: aOptions.data } - }).then((result) => { - aCallback.handleEvent(Ci.nsIActivityUIGlueCallback.NATIVE_ACTIVITY, result); - }); - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ActivitiesGlue]); diff --git a/mobile/android/components/MobileComponents.manifest b/mobile/android/components/MobileComponents.manifest index 029b0400f361..ace3e7b16a09 100644 --- a/mobile/android/components/MobileComponents.manifest +++ b/mobile/android/components/MobileComponents.manifest @@ -117,10 +117,6 @@ category update-timer Snippets @mozilla.org/snippets;1,getService,snippets-updat component {430b987f-bb9f-46a3-99a5-241749220b29} ColorPicker.js contract @mozilla.org/colorpicker;1 {430b987f-bb9f-46a3-99a5-241749220b29} -# AndroidActivitiesGlue.js -component {e4deb5f6-d5e3-4fce-bc53-901dd9951c48} AndroidActivitiesGlue.js -contract @mozilla.org/dom/activities/ui-glue;1 {e4deb5f6-d5e3-4fce-bc53-901dd9951c48} - # PersistentNotificationHandler.js component {75390fe7-f8a3-423a-b3b1-258d7eabed40} PersistentNotificationHandler.js contract @mozilla.org/persistent-notification-handler;1 {75390fe7-f8a3-423a-b3b1-258d7eabed40} diff --git a/mobile/android/components/moz.build b/mobile/android/components/moz.build index b5b6dad86f51..239d3c90e304 100644 --- a/mobile/android/components/moz.build +++ b/mobile/android/components/moz.build @@ -13,7 +13,6 @@ XPIDL_MODULE = 'MobileComponents' EXTRA_COMPONENTS += [ 'AboutRedirector.js', 'AddonUpdateService.js', - 'AndroidActivitiesGlue.js', 'BlocklistPrompt.js', 'BrowserCLH.js', 'ColorPicker.js', diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WebActivityMapper.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WebActivityMapper.java deleted file mode 100644 index 4c317ecba62b..000000000000 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WebActivityMapper.java +++ /dev/null @@ -1,221 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.util; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import android.content.Intent; -import android.net.Uri; -import android.os.Environment; -import android.provider.MediaStore; -import android.text.TextUtils; -import android.util.Log; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -public final class WebActivityMapper { - private static final String LOGTAG = "Gecko"; - - private static final Map activityMap = new HashMap(); - static { - activityMap.put("dial", new DialMapping()); - activityMap.put("open", new OpenMapping()); - activityMap.put("pick", new PickMapping()); - activityMap.put("send", new SendMapping()); - activityMap.put("view", new ViewMapping()); - activityMap.put("record", new RecordMapping()); - }; - - private static abstract class WebActivityMapping { - protected JSONObject mData; - - public void setData(JSONObject data) { - mData = data; - } - - // Cannot return null - public abstract String getAction(); - - public String getMime() throws JSONException { - return null; - } - - public String getUri() throws JSONException { - return null; - } - - public void putExtras(Intent intent) throws JSONException {} - } - - /** - * Provides useful defaults for mime type and uri. - */ - private static abstract class BaseMapping extends WebActivityMapping { - /** - * If 'type' is present in data object, uses the value as the MIME type. - */ - @Override - public String getMime() throws JSONException { - return mData.optString("type", null); - } - - /** - * If 'uri' or 'url' is present in data object, uses the respective value as the Uri. - */ - @Override - public String getUri() throws JSONException { - // Will return uri or url if present. - String uri = mData.optString("uri", null); - return uri != null ? uri : mData.optString("url", null); - } - } - - public static Intent getIntentForWebActivity(JSONObject message) throws JSONException { - final String name = message.getString("name").toLowerCase(); - final JSONObject data = message.getJSONObject("data"); - - Log.w(LOGTAG, "Activity is: " + name); - final WebActivityMapping mapping = activityMap.get(name); - if (mapping == null) { - Log.w(LOGTAG, "No mapping found!"); - return null; - } - - mapping.setData(data); - - final Intent intent = new Intent(mapping.getAction()); - - final String mime = mapping.getMime(); - if (!TextUtils.isEmpty(mime)) { - intent.setType(mime); - } - - final String uri = mapping.getUri(); - if (!TextUtils.isEmpty(uri)) { - intent.setData(Uri.parse(uri)); - } - - mapping.putExtras(intent); - - return intent; - } - - private static class DialMapping extends WebActivityMapping { - @Override - public String getAction() { - return Intent.ACTION_DIAL; - } - - @Override - public String getUri() throws JSONException { - return "tel:" + mData.getString("number"); - } - } - - private static class OpenMapping extends BaseMapping { - @Override - public String getAction() { - return Intent.ACTION_VIEW; - } - } - - private static class PickMapping extends BaseMapping { - @Override - public String getAction() { - return Intent.ACTION_GET_CONTENT; - } - - @Override - public String getMime() throws JSONException { - // bug 1007112 - pick action needs a mimetype to work - String mime = mData.optString("type", null); - return !TextUtils.isEmpty(mime) ? mime : "*/*"; - } - } - - private static class SendMapping extends BaseMapping { - @Override - public String getAction() { - return Intent.ACTION_SEND; - } - - @Override - public void putExtras(Intent intent) throws JSONException { - optPutExtra("text", Intent.EXTRA_TEXT, intent); - optPutExtra("html_text", Intent.EXTRA_HTML_TEXT, intent); - optPutExtra("stream", Intent.EXTRA_STREAM, intent); - } - - private void optPutExtra(String key, String extraName, Intent intent) { - final String extraValue = mData.optString(key); - if (!TextUtils.isEmpty(extraValue)) { - intent.putExtra(extraName, extraValue); - } - } - } - - private static class ViewMapping extends BaseMapping { - @Override - public String getAction() { - return Intent.ACTION_VIEW; - } - - @Override - public String getMime() { - // MozActivity adds a type 'url' here, we don't want to set the MIME to 'url'. - String type = mData.optString("type", null); - if ("url".equals(type) || "uri".equals(type)) { - return null; - } else { - return type; - } - } - } - - private static class RecordMapping extends WebActivityMapping { - @Override - public String getAction() { - String type = mData.optString("type", null); - if ("photos".equals(type)) { - return "android.media.action.IMAGE_CAPTURE"; - } else if ("videos".equals(type)) { - return "android.media.action.VIDEO_CAPTURE"; - } - return null; - } - - // Add an extra to specify where to save the picture/video. - @Override - public void putExtras(Intent intent) { - final String action = getAction(); - - final String dirType = action == "android.media.action.IMAGE_CAPTURE" - ? Environment.DIRECTORY_PICTURES - : Environment.DIRECTORY_MOVIES; - - final String ext = action == "android.media.action.IMAGE_CAPTURE" - ? ".jpg" - : ".mp4"; - - File destDir = Environment.getExternalStoragePublicDirectory(dirType); - - try { - File dest = File.createTempFile( - "capture", /* prefix */ - ext, /* suffix */ - destDir /* directory */ - ); - intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(dest)); - } catch (Exception e) { - Log.w(LOGTAG, "Failed to add extra for " + action + " : " + e); - } - } - } -} diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index 13068b46622c..f82e9db04c87 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -114,7 +114,6 @@ @BINPATH@/components/directory.xpt @BINPATH@/components/docshell.xpt @BINPATH@/components/dom.xpt -@BINPATH@/components/dom_activities.xpt @BINPATH@/components/dom_apps.xpt @BINPATH@/components/dom_newapps.xpt @BINPATH@/components/dom_base.xpt @@ -124,7 +123,6 @@ @BINPATH@/components/dom_events.xpt @BINPATH@/components/dom_geolocation.xpt @BINPATH@/components/dom_media.xpt -@BINPATH@/components/dom_messages.xpt @BINPATH@/components/dom_network.xpt @BINPATH@/components/dom_notification.xpt @BINPATH@/components/dom_html.xpt @@ -372,13 +370,6 @@ @BINPATH@/components/htmlMenuBuilder.js @BINPATH@/components/htmlMenuBuilder.manifest -@BINPATH@/components/Activities.manifest -@BINPATH@/components/AndroidActivitiesGlue.js -@BINPATH@/components/ActivityProxy.js -@BINPATH@/components/ActivityRequestHandler.js -@BINPATH@/components/ActivityWrapper.js -@BINPATH@/components/ActivityMessageConfigurator.js - @BINPATH@/components/SystemMessageInternal.js @BINPATH@/components/SystemMessageManager.js @BINPATH@/components/SystemMessageCache.js From 7d54172fb429c488c91be8f8868451a47c577c55 Mon Sep 17 00:00:00 2001 From: Edouard Oger Date: Thu, 25 Aug 2016 16:25:58 -0700 Subject: [PATCH 27/65] Bug 1295348 - Send/Handle push messages for send tab to device on Fennec. r=Grisha,sebastian MozReview-Commit-ID: 1NSMPLQdoXv --HG-- extra : rebase_source : 4a350317a15d3649a966d8bc4bcd762259cc059a --- .../gecko/background/fxa/FxAccountClient.java | 5 +- .../background/fxa/FxAccountClient20.java | 55 ++++++++++- .../gecko/fxa/FxAccountDeviceRegistrator.java | 28 ++---- .../gecko/fxa/FxAccountPushHandler.java | 24 +++++ .../fxa/authenticator/AndroidFxAccount.java | 19 ++++ .../sync/stage/SyncClientsEngineStage.java | 96 +++++++++++++++++-- .../sync/net/test/TestClientsEngineStage.java | 8 +- .../gecko/fxa/login/MockFxAccountClient.java | 7 ++ 8 files changed, 204 insertions(+), 38 deletions(-) diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient.java index c347db6092a8..ed959ff0eef4 100644 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient.java @@ -5,12 +5,14 @@ package org.mozilla.gecko.background.fxa; import org.mozilla.gecko.background.fxa.FxAccountClient20.AccountStatusResponse; -import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate; import org.mozilla.gecko.background.fxa.FxAccountClient20.RecoveryEmailStatusResponse; +import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate; import org.mozilla.gecko.background.fxa.FxAccountClient20.TwoKeys; import org.mozilla.gecko.fxa.FxAccountDevice; import org.mozilla.gecko.sync.ExtendedJSONObject; +import java.util.List; + public interface FxAccountClient { public void accountStatus(String uid, RequestDelegate requestDelegate); public void recoveryEmailStatus(byte[] sessionToken, RequestDelegate requestDelegate); @@ -18,4 +20,5 @@ public interface FxAccountClient { public void sign(byte[] sessionToken, ExtendedJSONObject publicKey, long certificateDurationInMilliseconds, RequestDelegate requestDelegate); public void registerOrUpdateDevice(byte[] sessionToken, FxAccountDevice device, RequestDelegate requestDelegate); public void deviceList(byte[] sessionToken, RequestDelegate requestDelegate); + public void notifyDevices(byte[] sessionToken, List deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate requestDelegate); } diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java index 88095f95a6eb..596f4525e752 100644 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java @@ -4,6 +4,8 @@ package org.mozilla.gecko.background.fxa; +import android.support.annotation.NonNull; + import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.mozilla.gecko.background.common.log.Logger; @@ -33,6 +35,7 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; @@ -832,7 +835,6 @@ public class FxAccountClient20 implements FxAccountClient { } final BaseResource resource; - final ExtendedJSONObject body; try { resource = getBaseResource("account/devices"); } catch (URISyntaxException | UnsupportedEncodingException e) { @@ -858,4 +860,55 @@ public class FxAccountClient20 implements FxAccountClient { resource.get(); } + + @Override + public void notifyDevices(@NonNull byte[] sessionToken, @NonNull List deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate delegate) { + final byte[] tokenId = new byte[32]; + final byte[] reqHMACKey = new byte[32]; + final byte[] requestKey = new byte[32]; + try { + HKDF.deriveMany(sessionToken, new byte[0], FxAccountUtils.KW("sessionToken"), tokenId, reqHMACKey, requestKey); + } catch (Exception e) { + invokeHandleError(delegate, e); + return; + } + + final BaseResource resource; + final ExtendedJSONObject body = createNotifyDevicesBody(deviceIds, payload, TTL); + try { + resource = getBaseResource("account/devices/notify"); + } catch (URISyntaxException | UnsupportedEncodingException e) { + invokeHandleError(delegate, e); + return; + } + + resource.delegate = new ResourceDelegate(resource, delegate, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) { + @Override + public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) { + try { + delegate.handleSuccess(body); + } catch (Exception e) { + delegate.handleError(e); + } + } + }; + + post(resource, body); + } + + @NonNull + @SuppressWarnings("unchecked") + private ExtendedJSONObject createNotifyDevicesBody(@NonNull List deviceIds, ExtendedJSONObject payload, Long TTL) { + final ExtendedJSONObject body = new ExtendedJSONObject(); + final JSONArray to = new JSONArray(); + to.addAll(deviceIds); + body.put("to", to); + if (payload != null) { + body.put("payload", payload); + } + if (TTL != null) { + body.put("TTL", TTL); + } + return body; + } } diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java index 86cd8f6f0602..66a8ad8433c1 100644 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountDeviceRegistrator.java @@ -19,9 +19,8 @@ import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate; import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException; import org.mozilla.gecko.background.fxa.FxAccountRemoteError; import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; +import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount.InvalidFxAState; import org.mozilla.gecko.fxa.login.State; -import org.mozilla.gecko.fxa.login.State.StateLabel; -import org.mozilla.gecko.fxa.login.TokensAndKeysState; import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate; import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.EventCallback; @@ -112,7 +111,11 @@ public class FxAccountDeviceRegistrator implements BundleEventListener { String pushAuthKey = subscription.getString("pushAuthKey"); final AndroidFxAccount fxAccount = AndroidFxAccount.fromContext(context); - final byte[] sessionToken = getSessionToken(fxAccount); + if (fxAccount == null) { + Log.e(LOG_TAG, "AndroidFxAccount is null"); + return; + } + final byte[] sessionToken = fxAccount.getSessionToken(); final FxAccountDevice device; String deviceId = fxAccount.getDeviceId(); String clientName = getClientName(fxAccount, context); @@ -180,17 +183,6 @@ public class FxAccountDeviceRegistrator implements BundleEventListener { } } - @Nullable - private static byte[] getSessionToken(final AndroidFxAccount fxAccount) throws InvalidFxAState { - State state = fxAccount.getState(); - StateLabel stateLabel = state.getStateLabel(); - if (stateLabel == StateLabel.Cohabiting || stateLabel == StateLabel.Married) { - TokensAndKeysState tokensAndKeysState = (TokensAndKeysState) state; - return tokensAndKeysState.getSessionToken(); - } - throw new InvalidFxAState("Cannot get sessionToken: not in a TokensAndKeysState state"); - } - private static void handleTokenError(final FxAccountClientRemoteException error, final FxAccountClient fxAccountClient, final AndroidFxAccount fxAccount) { @@ -287,12 +279,4 @@ public class FxAccountDeviceRegistrator implements BundleEventListener { BundleEventListener.class, String[].class); registerBackgroundThreadListener.invoke(instance, this, new String[] { "FxAccountsPush:Subscribe:Response" }); } - - public static class InvalidFxAState extends Exception { - private static final long serialVersionUID = -8537626959811195978L; - - public InvalidFxAState(String message) { - super(message); - } - } } diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountPushHandler.java b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountPushHandler.java index f9d4ea2c6044..0117e6320231 100644 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountPushHandler.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/FxAccountPushHandler.java @@ -7,6 +7,7 @@ import android.os.Bundle; import android.text.TextUtils; import android.util.Log; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; @@ -15,6 +16,9 @@ public class FxAccountPushHandler { private static final String LOG_TAG = "FxAccountPush"; private static final String COMMAND_DEVICE_DISCONNECTED = "fxaccounts:device_disconnected"; + private static final String COMMAND_COLLECTION_CHANGED = "sync:collection_changed"; + + private static final String CLIENTS_COLLECTION = "clients"; // Forbid instantiation private FxAccountPushHandler() {} @@ -45,6 +49,9 @@ public class FxAccountPushHandler { case COMMAND_DEVICE_DISCONNECTED: handleDeviceDisconnection(context, data); break; + case COMMAND_COLLECTION_CHANGED: + handleCollectionChanged(context, data); + break; default: Log.d(LOG_TAG, "No handler defined for FxA Push command " + command); break; @@ -54,6 +61,23 @@ public class FxAccountPushHandler { } } + private static void handleCollectionChanged(Context context, JSONObject data) throws JSONException { + JSONArray collections = data.getJSONArray("collections"); + int len = collections.length(); + for (int i = 0; i < len; i++) { + if (collections.getString(i).equals(CLIENTS_COLLECTION)) { + final Account account = FirefoxAccounts.getFirefoxAccount(context); + if (account == null) { + Log.e(LOG_TAG, "The account does not exist anymore"); + return; + } + final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account); + fxAccount.requestImmediateSync(new String[] { CLIENTS_COLLECTION }, null); + return; + } + } + } + private static void handleDeviceDisconnection(Context context, JSONObject data) throws JSONException { final Account account = FirefoxAccounts.getFirefoxAccount(context); if (account == null) { diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java index 9ec91b9b57ff..d7ce7c47f77d 100644 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/authenticator/AndroidFxAccount.java @@ -30,6 +30,7 @@ import org.mozilla.gecko.fxa.FxAccountConstants; import org.mozilla.gecko.fxa.login.State; import org.mozilla.gecko.fxa.login.State.StateLabel; import org.mozilla.gecko.fxa.login.StateFactory; +import org.mozilla.gecko.fxa.login.TokensAndKeysState; import org.mozilla.gecko.fxa.sync.FxAccountProfileService; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.Utils; @@ -607,6 +608,24 @@ public class AndroidFxAccount { } } + public byte[] getSessionToken() throws InvalidFxAState { + State state = getState(); + StateLabel stateLabel = state.getStateLabel(); + if (stateLabel == StateLabel.Cohabiting || stateLabel == StateLabel.Married) { + TokensAndKeysState tokensAndKeysState = (TokensAndKeysState) state; + return tokensAndKeysState.getSessionToken(); + } + throw new InvalidFxAState("Cannot get sessionToken: not in a TokensAndKeysState state"); + } + + public static class InvalidFxAState extends Exception { + private static final long serialVersionUID = -8537626959811195978L; + + public InvalidFxAState(String message) { + super(message); + } + } + /** * For debugging only! */ diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/SyncClientsEngineStage.java b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/SyncClientsEngineStage.java index 9faa8b067547..04d3e7ce292b 100644 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/SyncClientsEngineStage.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/SyncClientsEngineStage.java @@ -6,19 +6,27 @@ package org.mozilla.gecko.sync.stage; import android.accounts.Account; import android.content.Context; +import android.support.annotation.NonNull; import android.text.TextUtils; +import android.util.Log; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.background.common.log.Logger; +import org.mozilla.gecko.background.fxa.FxAccountClient; +import org.mozilla.gecko.background.fxa.FxAccountClient20; +import org.mozilla.gecko.background.fxa.FxAccountClientException; import org.mozilla.gecko.fxa.FirefoxAccounts; import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; import org.mozilla.gecko.sync.CommandProcessor; @@ -54,6 +62,7 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage { public static final String STAGE_NAME = COLLECTION_NAME; public static final int CLIENTS_TTL_REFRESH = 604800000; // 7 days in milliseconds. public static final int MAX_UPLOAD_FAILURE_COUNT = 5; + public static final long NOTIFY_TAB_SENT_TTL_SECS = TimeUnit.SECONDS.convert(1L, TimeUnit.HOURS); // 1 hour protected final ClientRecordFactory factory = new ClientRecordFactory(); protected ClientUploadDelegate clientUploadDelegate; @@ -65,7 +74,7 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage { protected volatile boolean shouldWipe; protected volatile boolean shouldUploadLocalRecord; // Set if, e.g., we received commands or need to refresh our version. protected final AtomicInteger uploadAttemptsCount = new AtomicInteger(); - protected final List toUpload = new ArrayList(); + protected final List modifiedClientsToUpload = new ArrayList(); protected int getClientsCount() { return getClientsDatabaseAccessor().clientsCount(); @@ -151,13 +160,80 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage { // If we upload remote records, checkAndUpload() will be called upon // upload success in the delegate. Otherwise call checkAndUpload() now. - if (toUpload.size() > 0) { + if (modifiedClientsToUpload.size() > 0) { + // modifiedClientsToUpload is cleared in uploadRemoteRecords, save what we need here + final List devicesToNotify = new ArrayList<>(); + for (ClientRecord record : modifiedClientsToUpload) { + if (!TextUtils.isEmpty(record.fxaDeviceId)) { + devicesToNotify.add(record.fxaDeviceId); + } + } + + // This method is synchronous, there's no risk of notifying the clients + // before we actually uploaded the records uploadRemoteRecords(); + + // Notify the clients who got their record written + notifyClients(devicesToNotify); + return; } checkAndUpload(); } + private void notifyClients(final List devicesToNotify) { + final ExecutorService executor = Executors.newSingleThreadExecutor(); + final Context context = session.getContext(); + final Account account = FirefoxAccounts.getFirefoxAccount(context); + if (account == null) { + Log.e(LOG_TAG, "Can't notify other clients: no account"); + return; + } + final AndroidFxAccount fxAccount = new AndroidFxAccount(context, account); + final ExtendedJSONObject payload = createNotifyDevicesPayload(); + + final byte[] sessionToken; + try { + sessionToken = fxAccount.getSessionToken(); + } catch (AndroidFxAccount.InvalidFxAState invalidFxAState) { + Log.e(LOG_TAG, "Could not get session token", invalidFxAState); + return; + } + + // API doc : https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#post-v1accountdevicesnotify + final FxAccountClient fxAccountClient = new FxAccountClient20(fxAccount.getAccountServerURI(), executor); + fxAccountClient.notifyDevices(sessionToken, devicesToNotify, payload, NOTIFY_TAB_SENT_TTL_SECS, new FxAccountClient20.RequestDelegate() { + @Override + public void handleError(Exception e) { + Log.e(LOG_TAG, "Error while notifying devices", e); + } + + @Override + public void handleFailure(FxAccountClientException.FxAccountClientRemoteException e) { + Log.e(LOG_TAG, "Error while notifying devices", e); + } + + @Override + public void handleSuccess(ExtendedJSONObject result) { + Log.i(LOG_TAG, devicesToNotify.size() + " devices notified"); + } + }); + } + + @NonNull + @SuppressWarnings("unchecked") + private ExtendedJSONObject createNotifyDevicesPayload() { + final ExtendedJSONObject payload = new ExtendedJSONObject(); + payload.put("version", 1); + payload.put("command", "sync:collection_changed"); + final ExtendedJSONObject data = new ExtendedJSONObject(); + final JSONArray collections = new JSONArray(); + collections.add("clients"); + data.put("collections", collections); + payload.put("data", data); + return payload; + } + @Override public void handleRequestFailure(SyncStorageResponse response) { BaseResource.consumeEntity(response); // We don't need the response at all, and any exception handling shouldn't need the response body. @@ -290,7 +366,7 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage { Logger.debug(LOG_TAG, "Client upload failed. Aborting sync."); if (!currentlyUploadingLocalRecord) { - toUpload.clear(); // These will be redownloaded. + modifiedClientsToUpload.clear(); // These will be redownloaded. } BaseResource.consumeEntity(response); // The exception thrown should need the response body. session.abort(new HTTPFailureException(response), "Client upload failed."); @@ -474,19 +550,19 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage { } record.commands.add(jsonCommand); } - toUpload.add(record); + modifiedClientsToUpload.add(record); } @SuppressWarnings("unchecked") protected void uploadRemoteRecords() { - Logger.trace(LOG_TAG, "In uploadRemoteRecords. Uploading " + toUpload.size() + " records" ); + Logger.trace(LOG_TAG, "In uploadRemoteRecords. Uploading " + modifiedClientsToUpload.size() + " records" ); - for (ClientRecord r : toUpload) { + for (ClientRecord r : modifiedClientsToUpload) { Logger.trace(LOG_TAG, ">> Uploading record " + r.guid + ": " + r.name); } - if (toUpload.size() == 1) { - ClientRecord record = toUpload.get(0); + if (modifiedClientsToUpload.size() == 1) { + ClientRecord record = modifiedClientsToUpload.get(0); Logger.debug(LOG_TAG, "Only 1 remote record to upload."); Logger.debug(LOG_TAG, "Record last modified: " + record.lastModified); CryptoRecord cryptoRecord = encryptClientRecord(record); @@ -498,7 +574,7 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage { } JSONArray cryptoRecords = new JSONArray(); - for (ClientRecord record : toUpload) { + for (ClientRecord record : modifiedClientsToUpload) { Logger.trace(LOG_TAG, "Record " + record.guid + " is being uploaded" ); CryptoRecord cryptoRecord = encryptClientRecord(record); @@ -547,7 +623,7 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage { public void clearRecordsToUpload() { try { getClientsDatabaseAccessor().wipeCommandsTable(); - toUpload.clear(); + modifiedClientsToUpload.clear(); } finally { closeDataAccessor(); } diff --git a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestClientsEngineStage.java b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestClientsEngineStage.java index 2209f63b76fa..920cafb35976 100644 --- a/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestClientsEngineStage.java +++ b/mobile/android/tests/background/junit4/src/org/mozilla/android/sync/net/test/TestClientsEngineStage.java @@ -765,9 +765,9 @@ public class TestClientsEngineStage extends MockSyncClientsEngineStage { final String expectedGUID = remoteRecord.guid; this.addCommands(remoteRecord); - assertEquals(1, toUpload.size()); + assertEquals(1, modifiedClientsToUpload.size()); - final ClientRecord recordToUpload = toUpload.get(0); + final ClientRecord recordToUpload = modifiedClientsToUpload.get(0); assertEquals(4, recordToUpload.commands.size()); assertEquals(expectedGUID, recordToUpload.guid); assertEquals(null, recordToUpload.version); @@ -782,9 +782,9 @@ public class TestClientsEngineStage extends MockSyncClientsEngineStage { final String expectedGUID = remoteRecord.guid; this.addCommands(remoteRecord); - assertEquals(1, toUpload.size()); + assertEquals(1, modifiedClientsToUpload.size()); - final ClientRecord recordToUpload = toUpload.get(0); + final ClientRecord recordToUpload = modifiedClientsToUpload.get(0); assertEquals(4, recordToUpload.commands.size()); assertEquals(expectedGUID, recordToUpload.guid); assertEquals("12a1", recordToUpload.version); diff --git a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/fxa/login/MockFxAccountClient.java b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/fxa/login/MockFxAccountClient.java index b3553234ab99..868e90cd2fd3 100644 --- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/fxa/login/MockFxAccountClient.java +++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/fxa/login/MockFxAccountClient.java @@ -6,6 +6,7 @@ package org.mozilla.gecko.fxa.login; import android.text.TextUtils; import org.mozilla.gecko.background.fxa.FxAccountClient; +import org.mozilla.gecko.background.fxa.FxAccountClient20; import org.mozilla.gecko.background.fxa.FxAccountClient20.AccountStatusResponse; import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate; import org.mozilla.gecko.background.fxa.FxAccountClient20.RecoveryEmailStatusResponse; @@ -23,6 +24,7 @@ import org.mozilla.gecko.sync.Utils; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -216,4 +218,9 @@ public class MockFxAccountClient implements FxAccountClient { FxAccountDevice[] devicesArray = devices.toArray(new FxAccountDevice[devices.size()]); requestDelegate.handleSuccess(devicesArray); } + + @Override + public void notifyDevices(byte[] sessionToken, List deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate requestDelegate) { + requestDelegate.handleSuccess(new ExtendedJSONObject()); + } } From daac24680a7ba93ef9fc64dd4332b2c9e495b2af Mon Sep 17 00:00:00 2001 From: Jimmy Wang Date: Tue, 30 Aug 2016 21:30:45 -0400 Subject: [PATCH 28/65] Bug 1297539 - Add IPC message, PasteTransferable, to call PasteTransferable via a controller on the content process to handle the command content event, "pasteTransferable". New method nsContentUtils::IPCTransferableToTransferable since ContentParent::RecvSetClipboard and TabChild::RecvPasteTransferable both require the same setup to make a transferable. r=mrbkap MozReview-Commit-ID: 3I443eBOPEO --HG-- extra : rebase_source : 8cdd9d917685240c64155bd230405876c2c2258e --- dom/base/nsContentUtils.cpp | 72 +++++++++++++++++++++++++++++++++++++ dom/base/nsContentUtils.h | 8 +++++ dom/ipc/ContentParent.cpp | 60 +++---------------------------- dom/ipc/PBrowser.ipdl | 8 +++++ dom/ipc/TabChild.cpp | 34 ++++++++++++++++++ dom/ipc/TabChild.h | 5 +++ dom/ipc/TabParent.cpp | 10 ++++++ dom/ipc/TabParent.h | 4 +++ 8 files changed, 146 insertions(+), 55 deletions(-) diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 150bac63aac7..3bf65e3fc52d 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7416,6 +7416,78 @@ nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(nsPIDOMWindowOuter* aWindo (void *)&stateInfo); } +nsresult +nsContentUtils::IPCTransferableToTransferable(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + nsIPrincipal* aRequestingPrincipal, + nsITransferable* aTransferable, + mozilla::dom::nsIContentParent* aContentParent, + mozilla::dom::TabChild* aTabChild) +{ + nsresult rv; + + const nsTArray& items = aDataTransfer.items(); + for (const auto& item : items) { + aTransferable->AddDataFlavor(item.flavor().get()); + + if (item.data().type() == IPCDataTransferData::TnsString) { + nsCOMPtr dataWrapper = + do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + const nsString& text = item.data().get_nsString(); + rv = dataWrapper->SetData(text); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper, + text.Length() * sizeof(char16_t)); + + NS_ENSURE_SUCCESS(rv, rv); + } else if (item.data().type() == IPCDataTransferData::TShmem) { + if (nsContentUtils::IsFlavorImage(item.flavor())) { + nsCOMPtr imageContainer; + rv = nsContentUtils::DataTransferItemToImage(item, + getter_AddRefs(imageContainer)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr imgPtr = + do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID); + NS_ENSURE_TRUE(imgPtr, NS_ERROR_FAILURE); + + rv = imgPtr->SetData(imageContainer); + NS_ENSURE_SUCCESS(rv, rv); + + aTransferable->SetTransferData(item.flavor().get(), imgPtr, sizeof(nsISupports*)); + } else { + nsCOMPtr dataWrapper = + do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // The buffer contains the terminating null. + Shmem itemData = item.data().get_Shmem(); + const nsDependentCString text(itemData.get(), + itemData.Size()); + rv = dataWrapper->SetData(text); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper, text.Length()); + + NS_ENSURE_SUCCESS(rv, rv); + } + + if (aContentParent) { + Unused << aContentParent->DeallocShmem(item.data().get_Shmem()); + } else if (aTabChild) { + Unused << aTabChild->DeallocShmem(item.data().get_Shmem()); + } + } + } + + aTransferable->SetIsPrivateData(aIsPrivateData); + aTransferable->SetRequestingPrincipal(aRequestingPrincipal); + return NS_OK; +} + void nsContentUtils::TransferablesToIPCTransferables(nsISupportsArray* aTransferables, nsTArray& aIPC, diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 7ebb9a65f978..de7fae016d30 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -127,6 +127,7 @@ class IPCDataTransferItem; class NodeInfo; class nsIContentChild; class nsIContentParent; +class TabChild; class Selection; class TabParent; } // namespace dom @@ -2493,6 +2494,13 @@ public: */ static bool IsFlavorImage(const nsACString& aFlavor); + static nsresult IPCTransferableToTransferable(const mozilla::dom::IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + nsIPrincipal* aRequestingPrincipal, + nsITransferable* aTransferable, + mozilla::dom::nsIContentParent* aContentParent, + mozilla::dom::TabChild* aTabChild); + static void TransferablesToIPCTransferables(nsISupportsArray* aTransferables, nsTArray& aIPC, bool aInSyncMessage, diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 52088461f0cb..201c16adc164 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -2484,61 +2484,11 @@ ContentParent::RecvSetClipboard(const IPCDataTransfer& aDataTransfer, NS_ENSURE_SUCCESS(rv, true); trans->Init(nullptr); - const nsTArray& items = aDataTransfer.items(); - for (const auto& item : items) { - trans->AddDataFlavor(item.flavor().get()); - - if (item.data().type() == IPCDataTransferData::TnsString) { - nsCOMPtr dataWrapper = - do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, true); - - const nsString& text = item.data().get_nsString(); - rv = dataWrapper->SetData(text); - NS_ENSURE_SUCCESS(rv, true); - - rv = trans->SetTransferData(item.flavor().get(), dataWrapper, - text.Length() * sizeof(char16_t)); - - NS_ENSURE_SUCCESS(rv, true); - } else if (item.data().type() == IPCDataTransferData::TShmem) { - if (nsContentUtils::IsFlavorImage(item.flavor())) { - nsCOMPtr imageContainer; - rv = nsContentUtils::DataTransferItemToImage(item, - getter_AddRefs(imageContainer)); - NS_ENSURE_SUCCESS(rv, true); - - nsCOMPtr imgPtr = - do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID); - NS_ENSURE_TRUE(imgPtr, true); - - rv = imgPtr->SetData(imageContainer); - NS_ENSURE_SUCCESS(rv, true); - - trans->SetTransferData(item.flavor().get(), imgPtr, sizeof(nsISupports*)); - } else { - nsCOMPtr dataWrapper = - do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, true); - - // The buffer contains the terminating null. - Shmem itemData = item.data().get_Shmem(); - const nsDependentCString text(itemData.get(), - itemData.Size()); - rv = dataWrapper->SetData(text); - NS_ENSURE_SUCCESS(rv, true); - - rv = trans->SetTransferData(item.flavor().get(), dataWrapper, text.Length()); - - NS_ENSURE_SUCCESS(rv, true); - } - - Unused << DeallocShmem(item.data().get_Shmem()); - } - } - - trans->SetIsPrivateData(aIsPrivateData); - trans->SetRequestingPrincipal(aRequestingPrincipal); + rv = nsContentUtils::IPCTransferableToTransferable(aDataTransfer, + aIsPrivateData, + aRequestingPrincipal, + trans, this, nullptr); + NS_ENSURE_SUCCESS(rv, true); clipboard->SetData(trans, nullptr, aWhichClipboard); return true; diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 446a9a1b4165..3b4b78428c64 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -674,6 +674,14 @@ child: async SelectionEvent(WidgetSelectionEvent event); + /** + * Call PasteTransferable via a controller on the content process + * to handle the command content event, "pasteTransferable". + */ + async PasteTransferable(IPCDataTransfer aDataTransfer, + bool aIsPrivateData, + Principal aRequestingPrincipal); + /** * Activate event forwarding from client to parent. */ diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index f606c85750e2..d53a9b9432be 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -112,6 +112,7 @@ #include "nsSandboxFlags.h" #include "FrameLayerBuilder.h" #include "VRManagerChild.h" +#include "nsICommandParams.h" #ifdef NS_PRINTING #include "nsIPrintSession.h" @@ -2146,6 +2147,39 @@ TabChild::RecvSelectionEvent(const WidgetSelectionEvent& event) return true; } +bool +TabChild::RecvPasteTransferable(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + const IPC::Principal& aRequestingPrincipal) +{ + nsresult rv; + nsCOMPtr trans = + do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + trans->Init(nullptr); + + rv = nsContentUtils::IPCTransferableToTransferable(aDataTransfer, + aIsPrivateData, + aRequestingPrincipal, + trans, nullptr, this); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr ourDocShell = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourDocShell)) { + return true; + } + + nsCOMPtr params = do_CreateInstance("@mozilla.org/embedcomp/command-params;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + + rv = params->SetISupportsValue("transferable", trans); + NS_ENSURE_SUCCESS(rv, true); + + ourDocShell->DoCommandWithParams("cmd_pasteTransferable", params); + return true; +} + + a11y::PDocAccessibleChild* TabChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) { diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index aaa71b84bbab..7c97e30fb409 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -399,6 +399,11 @@ public: virtual bool RecvSelectionEvent(const mozilla::WidgetSelectionEvent& aEvent) override; + virtual bool + RecvPasteTransferable(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + const IPC::Principal& aRequestingPrincipal) override; + virtual bool RecvActivateFrameEvent(const nsString& aType, const bool& aCapture) override; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index bced3de1d0d5..d4ba56650015 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -2226,6 +2226,16 @@ TabParent::SendSelectionEvent(WidgetSelectionEvent& event) return true; } +bool +TabParent::SendPasteTransferable(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + const IPC::Principal& aRequestingPrincipal) +{ + return PBrowserParent::SendPasteTransferable(aDataTransfer, + aIsPrivateData, + aRequestingPrincipal); +} + /*static*/ TabParent* TabParent::GetFrom(nsFrameLoader* aFrameLoader) { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index acef93dbe463..950171cdc16b 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -540,6 +540,10 @@ public: bool SendSelectionEvent(mozilla::WidgetSelectionEvent& event); + bool SendPasteTransferable(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + const IPC::Principal& aRequestingPrincipal); + static TabParent* GetFrom(nsFrameLoader* aFrameLoader); static TabParent* GetFrom(nsIFrameLoader* aFrameLoader); From 407529424fb5ac76e9a86800533bd13e3d320a5d Mon Sep 17 00:00:00 2001 From: Jimmy Wang Date: Tue, 23 Aug 2016 20:00:27 -0400 Subject: [PATCH 29/65] Bug 1297539 - Send message PasteTransferable if the focused element is on the content process, otherwise pass the transferable to the controller directly. r=mrbkap MozReview-Commit-ID: Bj3mY23YUDT --HG-- extra : rebase_source : d55a51b0ac01f07fdc6a3cbcaca08bca8d0520fc --- dom/events/EventStateManager.cpp | 36 +++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 7d9055a27afc..f2853a4a2fd5 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -5209,16 +5209,38 @@ EventStateManager::DoContentCommandEvent(WidgetContentCommandEvent* aEvent) if (canDoIt && !aEvent->mOnlyEnabledCheck) { switch (aEvent->mMessage) { case eContentCommandPasteTransferable: { - nsCOMPtr commandController = do_QueryInterface(controller); - NS_ENSURE_STATE(commandController); + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + nsIContent* focusedContent = fm ? fm->GetFocusedContent() : nullptr; + RefPtr remote = TabParent::GetFrom(focusedContent); + if (remote) { + NS_ENSURE_TRUE(remote->Manager()->IsContentParent(), NS_ERROR_FAILURE); - nsCOMPtr params = do_CreateInstance("@mozilla.org/embedcomp/command-params;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr transferable = aEvent->mTransferable; + IPCDataTransfer ipcDataTransfer; + ContentParent* cp = remote->Manager()->AsContentParent(); + nsContentUtils::TransferableToIPCTransferable(transferable, + &ipcDataTransfer, + false, nullptr, + cp); + bool isPrivateData = false; + transferable->GetIsPrivateData(&isPrivateData); + nsCOMPtr requestingPrincipal; + transferable->GetRequestingPrincipal(getter_AddRefs(requestingPrincipal)); + remote->SendPasteTransferable(ipcDataTransfer, isPrivateData, + IPC::Principal(requestingPrincipal)); + rv = NS_OK; + } else { + nsCOMPtr commandController = do_QueryInterface(controller); + NS_ENSURE_STATE(commandController); - rv = params->SetISupportsValue("transferable", aEvent->mTransferable); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr params = do_CreateInstance("@mozilla.org/embedcomp/command-params;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); - rv = commandController->DoCommandWithParams(cmd, params); + rv = params->SetISupportsValue("transferable", aEvent->mTransferable); + NS_ENSURE_SUCCESS(rv, rv); + + rv = commandController->DoCommandWithParams(cmd, params); + } break; } From 64b87bb682aba7a7ede661cc7574c606262ce961 Mon Sep 17 00:00:00 2001 From: Jimmy Wang Date: Tue, 30 Aug 2016 22:02:56 -0400 Subject: [PATCH 30/65] Bug 1297539 - Write test for content event 'pasteTransferable' for the remote case. Ensures that EventStateManager dispatches the transferable correctly to TextEditor::PasteTransferable for plain text and HTMLEditor::PasteTransferable for html. r=mrbkap MozReview-Commit-ID: Hz36piNqX31 --HG-- extra : rebase_source : 39ebcb5647caa673c6729caac67ce020bd53d356 --- browser/base/content/test/general/browser.ini | 1 + .../test/general/browser_bug1297539.js | 114 ++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 browser/base/content/test/general/browser_bug1297539.js diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 04f49f6caff4..50e5e5f4642d 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -218,6 +218,7 @@ subsuite = clipboard [browser_bug1261299.js] subsuite = clipboard skip-if = toolkit != "cocoa" # Because of tests for supporting Service Menu of macOS, bug 1261299 +[browser_bug1297539.js] [browser_bug575561.js] [browser_bug575830.js] [browser_bug577121.js] diff --git a/browser/base/content/test/general/browser_bug1297539.js b/browser/base/content/test/general/browser_bug1297539.js new file mode 100644 index 000000000000..e75d8afe6b1c --- /dev/null +++ b/browser/base/content/test/general/browser_bug1297539.js @@ -0,0 +1,114 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test for Bug 1297539 + * Test that the content event "pasteTransferable" + * (mozilla::EventMessage::eContentCommandPasteTransferable) + * is handled correctly for plain text and html in the remote case. + * + * Original test test_bug525389.html for command content event + * "pasteTransferable" runs only in the content process. + * This doesn't test the remote case. + * + */ + +"use strict"; + +function getLoadContext() { + return window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsILoadContext); +} + +function getTransferableFromClipboard(asHTML) { + let trans = Cc["@mozilla.org/widget/transferable;1"]. + createInstance(Ci.nsITransferable); + trans.init(getLoadContext()); + if (asHTML) { + trans.addDataFlavor("text/html"); + } else { + trans.addDataFlavor("text/unicode"); + } + let clip = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard); + clip.getData(trans, Ci.nsIClipboard.kGlobalClipboard); + return trans; +} + +function* cutCurrentSelection(elementQueryString, property, browser) { + // Cut the current selection. + yield BrowserTestUtils.synthesizeKey("x", {accelKey: true}, browser); + + // The editor should be empty after cut. + yield ContentTask.spawn(browser, {elementQueryString, property}, + function* ({elementQueryString, property}) { + let element = content.document.querySelector(elementQueryString); + is(element[property], "", + `${elementQueryString} should be empty after cut (superkey + x)`); + }); +} + +// Test that you are able to pasteTransferable for plain text +// which is handled by TextEditor::PasteTransferable to paste into the editor. +add_task(function* test_paste_transferable_plain_text() +{ + let testPage = + 'data:text/html,' + + ''; + + yield BrowserTestUtils.withNewTab(testPage, function* (browser) { + // Select all the content in your editor element. + yield BrowserTestUtils.synthesizeMouse("#textarea", 0, 0, {}, browser); + yield BrowserTestUtils.synthesizeKey("a", {accelKey: true}, browser); + + yield* cutCurrentSelection("#textarea", "value", browser); + + let trans = getTransferableFromClipboard(false); + let DOMWindowUtils = EventUtils._getDOMWindowUtils(window); + DOMWindowUtils.sendContentCommandEvent("pasteTransferable", trans); + + yield ContentTask.spawn(browser, null, function* () { + let textArea = content.document.querySelector('#textarea'); + is(textArea.value, "Write something here", + "Send content command pasteTransferable successful"); + }); + }); +}); + +// Test that you are able to pasteTransferable for html +// which is handled by HTMLEditor::PasteTransferable to paste into the editor. +// +// On Linux, +// BrowserTestUtils.synthesizeKey("a", {accelKey: true}, browser); +// doesn't seem to trigger for contenteditable which is why we use +// Selection to select the contenteditable contents. +add_task(function* test_paste_transferable_html() +{ + let testPage = + 'data:text/html,' + + '
Bold Textitalics
'; + + yield BrowserTestUtils.withNewTab(testPage, function* (browser) { + // Select all the content in your editor element. + yield BrowserTestUtils.synthesizeMouse("div", 0, 0, {}, browser); + yield ContentTask.spawn(browser, {}, function* () { + let element = content.document.querySelector("div"); + let selection = content.window.getSelection(); + selection.selectAllChildren(element); + }); + + yield* cutCurrentSelection("div", "textContent", browser); + + let trans = getTransferableFromClipboard(true); + let DOMWindowUtils = EventUtils._getDOMWindowUtils(window); + DOMWindowUtils.sendContentCommandEvent("pasteTransferable", trans); + + yield ContentTask.spawn(browser, null, function* () { + let textArea = content.document.querySelector('div'); + is(textArea.innerHTML, "Bold Textitalics", + "Send content command pasteTransferable successful"); + }); + }); +}); \ No newline at end of file From 105b7c01d0655cfac90edc09ceb31f6b814862d1 Mon Sep 17 00:00:00 2001 From: Rob Wu Date: Sun, 28 Aug 2016 16:35:00 -0700 Subject: [PATCH 31/65] Bug 1298716 - Serialize extension in ExtensionTestUtils.loadExtension r=billm MozReview-Commit-ID: BUK2QnMmXp7 --HG-- extra : rebase_source : f98d167db8067a59666c1b260e2c031fb6fbbbae --- .../tests/SimpleTest/ExtensionTestUtils.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/testing/mochitest/tests/SimpleTest/ExtensionTestUtils.js b/testing/mochitest/tests/SimpleTest/ExtensionTestUtils.js index ae0b7a479087..ead62e27fcae 100644 --- a/testing/mochitest/tests/SimpleTest/ExtensionTestUtils.js +++ b/testing/mochitest/tests/SimpleTest/ExtensionTestUtils.js @@ -83,6 +83,23 @@ ExtensionTestUtils.loadExtension = function(ext) }, }; + // Mimic serialization of functions as done in `Extension.generateXPI` and + // `Extension.generateZipFile` because functions are dropped when `ext` object + // is sent to the main process via the message manager. + ext = Object.assign({}, ext); + if (ext.files) { + ext.files = Object.assign({}, ext.files); + for (let filename of Object.keys(ext.files)) { + let file = ext.files[filename]; + if (typeof file == "function") { + ext.files[filename] = `(${file})();` + } + } + } + if (typeof ext.background == "function") { + ext.background = `(${ext.background})();` + } + var extension = SpecialPowers.loadExtension(ext, handler); registerCleanup(() => { From 73811a0f67f994113bd16fb4e8870656f7170c57 Mon Sep 17 00:00:00 2001 From: Rob Wu Date: Sun, 28 Aug 2016 17:22:03 -0700 Subject: [PATCH 32/65] Bug 1298716 - Unify use of ExtensionTestUtils.loadExtension r=billm MozReview-Commit-ID: DT6rx6KYODl --HG-- extra : rebase_source : b37924fe255993006fc9a7dfa115d0527d6d0e10 --- .../test/mochitest/test_ext_pageAction.html | 4 ++-- .../mochitest/test_ext_pageAction_popup.html | 12 +++++----- ...st_chrome_ext_background_debug_global.html | 5 ++-- .../test_chrome_ext_background_page.html | 2 +- ...ontentscript_unrecognizedprop_warning.html | 6 ++--- .../test_chrome_ext_eventpage_warning.html | 4 ++-- .../test_chrome_ext_shutdown_cleanup.html | 2 +- .../test_chrome_ext_storage_cleanup.html | 6 ++--- .../test_chrome_ext_trustworthy_origin.html | 5 ++-- ...hrome_ext_webnavigation_resolved_urls.html | 4 ++-- .../test_ext_background_api_injection.html | 2 +- .../mochitest/test_ext_background_canvas.html | 2 +- .../test_ext_background_generated_url.html | 2 +- .../test_ext_background_teardown.html | 4 ++-- .../mochitest/test_ext_contentscript.html | 12 +++++----- .../test_ext_contentscript_api_injection.html | 4 ++-- .../test_ext_contentscript_context.html | 2 +- .../test_ext_contentscript_create_iframe.html | 8 +++---- ...t_ext_contentscript_devtools_metadata.html | 6 ++--- .../test_ext_contentscript_exporthelpers.html | 2 +- .../test_ext_contentscript_teardown.html | 6 ++--- .../test/mochitest/test_ext_cookies.html | 4 ++-- .../mochitest/test_ext_cookies_expiry.html | 2 +- .../test_ext_exclude_include_globs.html | 10 ++++---- .../test/mochitest/test_ext_generate.html | 4 ++-- .../test/mochitest/test_ext_geturl.html | 6 ++--- .../test/mochitest/test_ext_i18n.html | 10 ++++---- .../test/mochitest/test_ext_i18n_css.html | 2 +- .../test_ext_inIncognitoContext_window.html | 7 +++--- .../test/mochitest/test_ext_jsversion.html | 10 ++++---- .../mochitest/test_ext_notifications.html | 24 +++++++++---------- .../mochitest/test_ext_runtime_connect.html | 6 ++--- .../test_ext_runtime_connect_twoway.html | 6 ++--- .../test_ext_runtime_disconnect.html | 6 ++--- .../test/mochitest/test_ext_runtime_id.html | 4 ++-- .../test/mochitest/test_ext_sandbox_var.html | 6 ++--- .../test/mochitest/test_ext_schema.html | 4 ++-- .../test_ext_sendmessage_doublereply.html | 6 ++--- .../test_ext_sendmessage_no_receiver.html | 2 +- .../mochitest/test_ext_sendmessage_reply.html | 6 ++--- .../mochitest/test_ext_storage_content.html | 2 +- .../test/mochitest/test_ext_storage_tab.html | 6 ++--- .../test_ext_subframes_privileges.html | 22 ++++++++--------- .../test/mochitest/test_ext_tab_teardown.html | 14 +++++------ .../test_ext_web_accessible_resources.html | 24 +++++++++---------- .../mochitest/test_ext_webnavigation.html | 6 ++--- .../test_ext_webnavigation_filters.html | 8 +++---- 47 files changed, 152 insertions(+), 155 deletions(-) diff --git a/mobile/android/components/extensions/test/mochitest/test_ext_pageAction.html b/mobile/android/components/extensions/test/mochitest/test_ext_pageAction.html index 6cf575337de5..4acb041ceeb6 100644 --- a/mobile/android/components/extensions/test/mochitest/test_ext_pageAction.html +++ b/mobile/android/components/extensions/test/mochitest/test_ext_pageAction.html @@ -18,7 +18,7 @@ let dataURI = "iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAC4klEQVRYhdWXLWzbQ let image = atob(dataURI); const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0)).buffer; -function backgroundScript() { +function background() { browser.test.assertTrue("pageAction" in browser, "Namespace 'pageAction' exists in browser"); browser.test.assertTrue("show" in browser.pageAction, "API method 'show' exists in browser.pageAction"); @@ -46,7 +46,7 @@ function backgroundScript() { add_task(function* test_contentscript() { let extension = ExtensionTestUtils.loadExtension({ - background: "(" + backgroundScript.toString() + ")()", + background, manifest: { "name": "PageAction Extension", "page_action": { diff --git a/mobile/android/components/extensions/test/mochitest/test_ext_pageAction_popup.html b/mobile/android/components/extensions/test/mochitest/test_ext_pageAction_popup.html index ee439712f7c7..e0b6036bc9fb 100644 --- a/mobile/android/components/extensions/test/mochitest/test_ext_pageAction_popup.html +++ b/mobile/android/components/extensions/test/mochitest/test_ext_pageAction_popup.html @@ -21,7 +21,7 @@ let image = atob(dataURI); const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0)).buffer; add_task(function* test_contentscript() { - function backgroundScript() { + function background() { // TODO: Use the Tabs API to obtain the tab ids for showing pageActions. let tabId = 1; let onClickedListenerEnabled = false; @@ -71,7 +71,7 @@ add_task(function* test_contentscript() { } let extension = ExtensionTestUtils.loadExtension({ - background: `(${backgroundScript}())`, + background, manifest: { "name": "PageAction Extension", "page_action": { @@ -83,11 +83,11 @@ add_task(function* test_contentscript() { }, }, files: { - "default.html": ` diff --git a/devtools/client/webconsole/console-output.js b/devtools/client/webconsole/console-output.js index 46bc29650bb1..56ef65f956dd 100644 --- a/devtools/client/webconsole/console-output.js +++ b/devtools/client/webconsole/console-output.js @@ -935,8 +935,6 @@ Messages.Simple.prototype = extend(Messages.BaseMessage.prototype, { this.element.appendChild(body); - this.element.appendChild(this.document.createTextNode("\n")); - this.element.clipboardText = this.element.textContent; if (this.private) { @@ -993,12 +991,16 @@ Messages.Simple.prototype = extend(Messages.BaseMessage.prototype, { let location = this._renderLocation(); if (repeatNode) { + bodyFlex.appendChild(this.document.createTextNode(" ")); bodyFlex.appendChild(repeatNode); } if (location) { + bodyFlex.appendChild(this.document.createTextNode(" ")); bodyFlex.appendChild(location); } + bodyFlex.appendChild(this.document.createTextNode("\n")); + if (this.stack) { this._attachment = new Widgets.Stacktrace(this, this.stack).render().element; } diff --git a/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js b/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js index 966f4308afff..bdd4f71793f2 100644 --- a/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js +++ b/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js @@ -3,67 +3,95 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* globals goDoCommand */ + "use strict"; // Test copying of the entire console message when right-clicked // with no other text selected. See Bug 1100562. -function test() { +add_task(function* () { let hud; let outputNode; let contextMenu; - const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + - "test/test-console.html"; + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test/test-console.html"; - Task.spawn(runner).then(finishTest); + const { tab, browser } = yield loadTab(TEST_URI); + hud = yield openConsole(tab); + outputNode = hud.outputNode; + contextMenu = hud.iframeWindow.document.getElementById("output-contextmenu"); - function* runner() { - const {tab} = yield loadTab(TEST_URI); - hud = yield openConsole(tab); - outputNode = hud.outputNode; - contextMenu = hud.iframeWindow.document.getElementById("output-contextmenu"); + registerCleanupFunction(() => { + hud = outputNode = contextMenu = null; + }); - registerCleanupFunction(() => { - hud = outputNode = contextMenu = null; - }); + hud.jsterm.clearOutput(); - hud.jsterm.clearOutput(); - content.console.log("bug 1100562"); + yield ContentTask.spawn(browser, {}, function* () { + let button = content.document.getElementById("testTrace"); + button.click(); + }); - let [results] = yield waitForMessages({ - webconsole: hud, - messages: [{ + let results = yield waitForMessages({ + webconsole: hud, + messages: [ + { text: "bug 1100562", category: CATEGORY_WEBDEV, severity: SEVERITY_LOG, - }] - }); + lines: 1, + }, + { + name: "console.trace output", + consoleTrace: true, + lines: 3, + }, + ] + }); - outputNode.focus(); - let message = [...results.matched][0]; + outputNode.focus(); - yield waitForContextMenu(contextMenu, message, copyFromPopup, - testContextMenuCopy); + for (let result of results) { + let message = [...result.matched][0]; - function copyFromPopup() { + yield waitForContextMenu(contextMenu, message, () => { let copyItem = contextMenu.querySelector("#cMenu_copy"); copyItem.doCommand(); let controller = top.document.commandDispatcher .getControllerForCommand("cmd_copy"); is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled"); - } + }); - function testContextMenuCopy() { - waitForClipboard((str) => { - return message.textContent.trim() == str.trim(); - }, () => { - goDoCommand("cmd_copy"); - }, () => {}, () => {} - ); - } + let clipboardText; - yield closeConsole(tab); + yield waitForClipboardPromise( + () => goDoCommand("cmd_copy"), + (str) => { + clipboardText = str; + return message.textContent == clipboardText; + } + ); + + ok(clipboardText, "Clipboard text was found and saved"); + + let lines = clipboardText.split("\n"); + ok(lines.length > 0, "There is at least one newline in the message"); + is(lines.pop(), "", "There is a newline at the end"); + is(lines.length, result.lines, `There are ${result.lines} lines in the message`); + + // Test the first line for "timestamp message repeat file:line" + let firstLine = lines.shift(); + ok(/^[\d:.]+ .+ \d+ .+:\d+$/.test(firstLine), + "The message's first line has the right format"); + + // Test the remaining lines (stack trace) for "TABfunctionName sourceURL:line:col" + for (let line of lines) { + ok(/^\t.+ .+:\d+:\d+$/.test(line), "The stack trace line has the right format"); + } } -} + + yield closeConsole(tab); + yield finishTest(); +}); diff --git a/devtools/client/webconsole/test/test-console.html b/devtools/client/webconsole/test/test-console.html index 9731c86a9fd5..b294a3ba1cfd 100644 --- a/devtools/client/webconsole/test/test-console.html +++ b/devtools/client/webconsole/test/test-console.html @@ -13,6 +13,12 @@ console.log(str); } } + + function testTrace() { + console.log("bug 1100562"); + console.trace(); + } + console.info("INLINE SCRIPT:"); test(); console.warn("I'm warning you, he will eat up all yr bacon."); @@ -22,6 +28,7 @@

Heads Up Display Demo

+
From d6a56e1228700d00ea5716c52ff209ec2603b395 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 1 Sep 2016 19:25:54 +1000 Subject: [PATCH 52/65] Bug 1297265: P1. Remove decode ahead logic. r=kamidphish The MediaFormatReader will no longer attempt to decode several frames in advance and ahead of the MDSM actually requesting it. The speed advantages were dubious at best, and as most MediaDataDecoders abused the use of InputExhausted callbacks we had to place artificial throttle that would often cause side effects. As such, it is now expected that the MediaDataDecoder will now always call InputExhausted once Input has been called. InputExhausted indicates that the current decoding session has completed and the MediaDataDecoder is waiting for another input. MozReview-Commit-ID: 9KUpNP9jozV --HG-- extra : rebase_source : d261a5eb98de54d5bd29acb738c4205c56abca6b --- dom/media/MediaFormatReader.cpp | 69 +++++++++++++-------------------- dom/media/MediaFormatReader.h | 28 ++++++------- 2 files changed, 37 insertions(+), 60 deletions(-) diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 6053a3cc3f2f..3ff6356351a5 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -62,9 +62,9 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder, VideoFrameContainer* aVideoFrameContainer, layers::LayersBackend aLayersBackend) : MediaDecoderReader(aDecoder) - , mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-decode-ahead", 2), + , mAudio(this, MediaData::AUDIO_DATA, Preferences::GetUint("media.audio-max-decode-error", 3)) - , mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-decode-ahead", 2), + , mVideo(this, MediaData::VIDEO_DATA, Preferences::GetUint("media.video-max-decode-error", 2)) , mDemuxer(aDemuxer) , mDemuxerInitDone(false) @@ -562,7 +562,7 @@ MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe, } RefPtr p = mVideo.EnsurePromise(__func__); - NotifyDecodingRequested(TrackInfo::kVideoTrack); + ScheduleUpdate(TrackInfo::kVideoTrack); return p; } @@ -606,7 +606,6 @@ MediaFormatReader::OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure void MediaFormatReader::DoDemuxVideo() { - // TODO Use DecodeAhead value rather than 1. mVideo.mDemuxRequest.Begin(mVideo.mTrackDemuxer->GetSamples(1) ->Then(OwnerThread(), __func__, this, &MediaFormatReader::OnVideoDemuxCompleted, @@ -657,7 +656,7 @@ MediaFormatReader::RequestAudioData() } RefPtr p = mAudio.EnsurePromise(__func__); - NotifyDecodingRequested(TrackInfo::kAudioTrack); + ScheduleUpdate(TrackInfo::kAudioTrack); return p; } @@ -665,7 +664,6 @@ MediaFormatReader::RequestAudioData() void MediaFormatReader::DoDemuxAudio() { - // TODO Use DecodeAhead value rather than 1. mAudio.mDemuxRequest.Begin(mAudio.mTrackDemuxer->GetSamples(1) ->Then(OwnerThread(), __func__, this, &MediaFormatReader::OnAudioDemuxCompleted, @@ -706,7 +704,7 @@ MediaFormatReader::NotifyInputExhausted(TrackType aTrack) MOZ_ASSERT(OnTaskQueue()); LOGV("Decoder has requested more %s data", TrackTypeToStr(aTrack)); auto& decoder = GetDecoderData(aTrack); - decoder.mInputExhausted = true; + decoder.mDecodePending = false; ScheduleUpdate(aTrack); } @@ -755,33 +753,21 @@ MediaFormatReader::NotifyEndOfStream(TrackType aTrack) ScheduleUpdate(aTrack); } -void -MediaFormatReader::NotifyDecodingRequested(TrackType aTrack) -{ - MOZ_ASSERT(OnTaskQueue()); - auto& decoder = GetDecoderData(aTrack); - decoder.mDecodingRequested = true; - ScheduleUpdate(aTrack); -} - bool MediaFormatReader::NeedInput(DecoderData& aDecoder) { - // We try to keep a few more compressed samples input than decoded samples - // have been output, provided the state machine has requested we send it a - // decoded sample. To account for H.264 streams which may require a longer - // run of input than we input, decoders fire an "input exhausted" callback, - // which overrides our "few more samples" threshold. + // To account for H.264 streams which may require a longer + // run of input than we input, decoders fire an "input exhausted" callback. + // The decoder will not be fed a new raw sample until InputExhausted + // has been called. return + (aDecoder.HasPromise() || aDecoder.mTimeThreshold.isSome()) && !aDecoder.HasPendingDrain() && !aDecoder.HasFatalError() && - aDecoder.mDecodingRequested && !aDecoder.mDemuxRequest.Exists() && + !aDecoder.mOutput.Length() && !aDecoder.HasInternalSeekPending() && - aDecoder.mOutput.Length() <= aDecoder.mDecodeAhead && - (aDecoder.mInputExhausted || !aDecoder.mQueuedSamples.IsEmpty() || - aDecoder.mTimeThreshold.isSome() || - aDecoder.mNumSamplesInput - aDecoder.mNumSamplesOutput <= aDecoder.mDecodeAhead); + !aDecoder.mDecodePending; } void @@ -932,6 +918,7 @@ MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack, LOG("Unable to pass frame to decoder"); return false; } + decoder.mDecodePending = true; return true; } @@ -1007,7 +994,7 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack, decoder.ShutdownDecoder(); if (sample->mKeyframe) { decoder.mQueuedSamples.AppendElements(Move(samples)); - NotifyDecodingRequested(aTrack); + ScheduleUpdate(aTrack); } else { TimeInterval time = TimeInterval(TimeUnit::FromMicroseconds(sample->mTime), @@ -1045,9 +1032,6 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack, } samplesPending = true; } - - // We have serviced the decoder's request for more data. - decoder.mInputExhausted = false; } void @@ -1071,7 +1055,7 @@ MediaFormatReader::InternalSeek(TrackType aTrack, const InternalSeekTarget& aTar "Seek promise must be disconnected when timethreshold is reset"); decoder.mTimeThreshold.ref().mHasSeeked = true; self->SetVideoDecodeThreshold(); - self->NotifyDecodingRequested(aTrack); + self->ScheduleUpdate(aTrack); }, [self, aTrack] (DemuxerFailureReason aResult) { auto& decoder = self->GetDecoderData(aTrack); @@ -1275,6 +1259,7 @@ MediaFormatReader::Update(TrackType aTrack) if (decoder.mError && decoder.mError.ref() == MediaDataDecoderError::DECODE_ERROR) { + decoder.mDecodePending = false; decoder.mError.reset(); if (++decoder.mNumOfConsecutiveError > decoder.mMaxConsecutiveError) { NotifyError(aTrack); @@ -1292,11 +1277,11 @@ MediaFormatReader::Update(TrackType aTrack) bool needInput = NeedInput(decoder); - LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d ahead:%d sid:%u", - TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted, + LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d promise:%d sid:%u", + TrackTypeToStr(aTrack), needInput, needOutput, decoder.mDecodePending, decoder.mNumSamplesInput, decoder.mNumSamplesOutput, uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()), - decoder.mWaitingForData, !decoder.HasPromise(), decoder.mLastStreamSourceID); + decoder.mWaitingForData, decoder.HasPromise(), decoder.mLastStreamSourceID); if (decoder.mWaitingForData && (!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) { @@ -1577,7 +1562,7 @@ MediaFormatReader::OnVideoSkipCompleted(uint32_t aSkipped) VideoSkipReset(aSkipped); - NotifyDecodingRequested(TrackInfo::kVideoTrack); + ScheduleUpdate(TrackInfo::kVideoTrack); } void @@ -1595,7 +1580,7 @@ MediaFormatReader::OnVideoSkipFailed(MediaTrackDemuxer::SkipFailureHolder aFailu DropDecodedSamples(TrackInfo::kVideoTrack); // We can't complete the skip operation, will just service a video frame // normally. - NotifyDecodingRequested(TrackInfo::kVideoTrack); + ScheduleUpdate(TrackInfo::kVideoTrack); break; case DemuxerFailureReason::CANCELED: MOZ_FALLTHROUGH; case DemuxerFailureReason::SHUTDOWN: @@ -2013,12 +1998,11 @@ MediaFormatReader::GetMozDebugReaderData(nsAString& aString) result += nsPrintfCString("audio frames decoded: %lld\n", mAudio.mNumSamplesOutputTotal); if (HasAudio()) { - result += nsPrintfCString("audio state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d decoder:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", + result += nsPrintfCString("audio state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", NeedInput(mAudio), mAudio.HasPromise(), - mAudio.mInputExhausted, + mAudio.mDecodePending, mAudio.mDemuxRequest.Exists(), int(mAudio.mQueuedSamples.Length()), - mAudio.mDecodingRequested, mAudio.mTimeThreshold ? mAudio.mTimeThreshold.ref().Time().ToSeconds() : -1.0, @@ -2037,12 +2021,11 @@ MediaFormatReader::GetMozDebugReaderData(nsAString& aString) mVideo.mNumSamplesOutputTotal, mVideo.mNumSamplesSkippedTotal); if (HasVideo()) { - result += nsPrintfCString("video state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d decoder:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", + result += nsPrintfCString("video state: ni=%d no=%d ie=%d demuxr:%d demuxq:%d tt:%f tths:%d in:%llu out:%llu qs=%u pending:%u waiting:%d sid:%u\n", NeedInput(mVideo), mVideo.HasPromise(), - mVideo.mInputExhausted, + mVideo.mDecodePending, mVideo.mDemuxRequest.Exists(), int(mVideo.mQueuedSamples.Length()), - mVideo.mDecodingRequested, mVideo.mTimeThreshold ? mVideo.mTimeThreshold.ref().Time().ToSeconds() : -1.0, @@ -2080,7 +2063,7 @@ MediaFormatReader::SetBlankDecode(TrackType aTrack, bool aIsBlankDecode) decoder.mIsBlankDecode = aIsBlankDecode; decoder.Flush(); decoder.ShutdownDecoder(); - NotifyDecodingRequested(TrackInfo::kVideoTrack); // Calls ScheduleUpdate(). + ScheduleUpdate(TrackInfo::kVideoTrack); return; } diff --git a/dom/media/MediaFormatReader.h b/dom/media/MediaFormatReader.h index 1c61b4644437..f632a18b9272 100644 --- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -169,7 +169,6 @@ private: void NotifyError(TrackType aTrack, MediaDataDecoderError aError = MediaDataDecoderError::FATAL_ERROR); void NotifyWaitingForData(TrackType aTrack); void NotifyEndOfStream(TrackType aTrack); - void NotifyDecodingRequested(TrackType aTrack); void ExtractCryptoInitData(nsTArray& aInitData); @@ -231,21 +230,18 @@ private: struct DecoderData { DecoderData(MediaFormatReader* aOwner, MediaData::Type aType, - uint32_t aDecodeAhead, uint32_t aNumOfMaxError) : mOwner(aOwner) , mType(aType) , mMonitor("DecoderData") , mDescription("shutdown") - , mDecodeAhead(aDecodeAhead) , mUpdateScheduled(false) , mDemuxEOS(false) , mWaitingForData(false) , mReceivedNewData(false) , mDecoderInitialized(false) - , mDecodingRequested(false) , mOutputRequested(false) - , mInputExhausted(false) + , mDecodePending(false) , mNeedDraining(false) , mDraining(false) , mDrainComplete(false) @@ -288,7 +284,6 @@ private: } // Only accessed from reader's task queue. - uint32_t mDecodeAhead; bool mUpdateScheduled; bool mDemuxEOS; bool mWaitingForData; @@ -312,11 +307,14 @@ private: MozPromiseRequestHolder mInitPromise; // False when decoder is created. True when decoder Init() promise is resolved. bool mDecoderInitialized; - // Set when decoding can proceed. It is reset when a decoding promise is - // rejected or prior a seek operation. - bool mDecodingRequested; bool mOutputRequested; - bool mInputExhausted; + // Set to true once the MediaDataDecoder has been fed a compressed sample. + // No more sample will be passed to the decoder while true. + // mDecodePending is reset when: + // 1- The decoder returns a sample + // 2- The decoder calls InputExhausted + // 3- The decoder is Flushed or Reset. + bool mDecodePending; bool mNeedDraining; bool mDraining; bool mDrainComplete; @@ -376,9 +374,8 @@ private: if (mDecoder) { mDecoder->Flush(); } - mDecodingRequested = false; mOutputRequested = false; - mInputExhausted = false; + mDecodePending = false; mOutput.Clear(); mNumSamplesInput = 0; mNumSamplesOutput = 0; @@ -397,10 +394,9 @@ private: mDemuxEOS = false; mWaitingForData = false; mQueuedSamples.Clear(); - mDecodingRequested = false; mOutputRequested = false; - mInputExhausted = false; mNeedDraining = false; + mDecodePending = false; mDraining = false; mDrainComplete = false; mTimeThreshold.reset(); @@ -441,9 +437,8 @@ private: public: DecoderDataWithPromise(MediaFormatReader* aOwner, MediaData::Type aType, - uint32_t aDecodeAhead, uint32_t aNumOfMaxError) - : DecoderData(aOwner, aType, aDecodeAhead, aNumOfMaxError) + : DecoderData(aOwner, aType, aNumOfMaxError) , mHasPromise(false) {} @@ -472,7 +467,6 @@ private: { MOZ_ASSERT(mOwner->OnTaskQueue()); mPromise.Reject(aReason, aMethodName); - mDecodingRequested = false; mHasPromise = false; } From eeb0e7dd1d86ce5e1e5c2f5fc3a7908ad9abe99b Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 1 Sep 2016 19:28:24 +1000 Subject: [PATCH 53/65] Bug 1297265: P2. Amend MediaDataDecoder documentation to emphasize the new expected behavior. r=kamidphish MozReview-Commit-ID: EHFnCnc58qh --HG-- extra : rebase_source : 0163994bd82d6529d17fc50403956dd68df6cd62 --- dom/media/platforms/PlatformDecoderModule.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dom/media/platforms/PlatformDecoderModule.h b/dom/media/platforms/PlatformDecoderModule.h index 76577ab51eed..6f906fc2a4bd 100644 --- a/dom/media/platforms/PlatformDecoderModule.h +++ b/dom/media/platforms/PlatformDecoderModule.h @@ -175,6 +175,9 @@ public: // Denotes that the last input sample has been inserted into the decoder, // and no more output can be produced unless more input is sent. + // A frame decoding session is completed once InputExhausted has been called. + // MediaDataDecoder::Input will not be called again until InputExhausted has + // been called. virtual void InputExhausted() = 0; virtual void DrainComplete() = 0; From 92fc37ef9120ce0886aadc3fb86c6c9673c9e07e Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 1 Sep 2016 19:29:19 +1000 Subject: [PATCH 54/65] Bug 1297265: P3. Rework Apple VT use of InputExhausted. r=me The only time we need to use InputExhausted is for the initial video decoding or when a frame is dropped. MozReview-Commit-ID: IrHqZXJwQe1 --HG-- extra : rebase_source : eb7ff378adafe05458b79a6c3b6c7593c84d40a2 --- dom/media/platforms/apple/AppleVTDecoder.cpp | 32 +++----------------- dom/media/platforms/apple/AppleVTDecoder.h | 8 ----- 2 files changed, 5 insertions(+), 35 deletions(-) diff --git a/dom/media/platforms/apple/AppleVTDecoder.cpp b/dom/media/platforms/apple/AppleVTDecoder.cpp index b7364d9fb68a..05f4b7884a70 100644 --- a/dom/media/platforms/apple/AppleVTDecoder.cpp +++ b/dom/media/platforms/apple/AppleVTDecoder.cpp @@ -34,11 +34,9 @@ AppleVTDecoder::AppleVTDecoder(const VideoInfo& aConfig, , mPictureHeight(aConfig.mImage.height) , mDisplayWidth(aConfig.mDisplay.width) , mDisplayHeight(aConfig.mDisplay.height) - , mQueuedSamples(0) , mTaskQueue(aTaskQueue) , mMaxRefFrames(mp4_demuxer::H264::ComputeMaxRefFrames(aConfig.mExtraData)) , mImageContainer(aImageContainer) - , mInputIncoming(0) , mIsShutDown(false) #ifdef MOZ_WIDGET_UIKIT , mUseSoftwareImages(true) @@ -88,8 +86,6 @@ AppleVTDecoder::Input(MediaRawData* aSample) aSample->mKeyframe ? " keyframe" : "", aSample->Size()); - mInputIncoming++; - mTaskQueue->Dispatch(NewRunnableMethod>( this, &AppleVTDecoder::ProcessDecode, aSample)); return NS_OK; @@ -104,8 +100,6 @@ AppleVTDecoder::Flush() NewRunnableMethod(this, &AppleVTDecoder::ProcessFlush); SyncRunnable::DispatchToThread(mTaskQueue, runnable); mIsFlushing = false; - // All ProcessDecode() tasks should be done. - MOZ_ASSERT(mInputIncoming == 0); mSeekTargetThreshold.reset(); @@ -142,18 +136,11 @@ AppleVTDecoder::ProcessDecode(MediaRawData* aSample) { AssertOnTaskQueueThread(); - mInputIncoming--; - if (mIsFlushing) { return NS_OK; } auto rv = DoDecode(aSample); - // Ask for more data. - if (NS_SUCCEEDED(rv) && !mInputIncoming && mQueuedSamples <= mMaxRefFrames) { - LOG("%s task queue empty; requesting more data", GetDescriptionName()); - mCallback->InputExhausted(); - } return rv; } @@ -213,7 +200,6 @@ AppleVTDecoder::DrainReorderedFrames() while (!mReorderQueue.IsEmpty()) { mCallback->Output(mReorderQueue.Pop().get()); } - mQueuedSamples = 0; } void @@ -223,7 +209,6 @@ AppleVTDecoder::ClearReorderedFrames() while (!mReorderQueue.IsEmpty()) { mReorderQueue.Pop(); } - mQueuedSamples = 0; } void @@ -288,16 +273,10 @@ AppleVTDecoder::OutputFrame(CVPixelBufferRef aImage, aFrameRef.is_sync_point ? " keyframe" : "" ); - if (mQueuedSamples > mMaxRefFrames) { - // We had stopped requesting more input because we had received too much at - // the time. We can ask for more once again. - mCallback->InputExhausted(); - } - MOZ_ASSERT(mQueuedSamples); - mQueuedSamples--; - if (!aImage) { - // Image was dropped by decoder. + // Image was dropped by decoder or none return yet. + // We need more input to continue. + mCallback->InputExhausted(); return NS_OK; } @@ -410,9 +389,10 @@ AppleVTDecoder::OutputFrame(CVPixelBufferRef aImage, // in composition order. MonitorAutoLock mon(mMonitor); mReorderQueue.Push(data); - while (mReorderQueue.Length() > mMaxRefFrames) { + if (mReorderQueue.Length() > mMaxRefFrames) { mCallback->Output(mReorderQueue.Pop().get()); } + mCallback->InputExhausted(); LOG("%llu decoded frames queued", static_cast(mReorderQueue.Length())); @@ -480,8 +460,6 @@ AppleVTDecoder::DoDecode(MediaRawData* aSample) return NS_ERROR_FAILURE; } - mQueuedSamples++; - VTDecodeFrameFlags decodeFlags = kVTDecodeFrame_EnableAsynchronousDecompression; rv = VTDecompressionSessionDecodeFrame(mSession, diff --git a/dom/media/platforms/apple/AppleVTDecoder.h b/dom/media/platforms/apple/AppleVTDecoder.h index ce65d3ade804..5e0b45029864 100644 --- a/dom/media/platforms/apple/AppleVTDecoder.h +++ b/dom/media/platforms/apple/AppleVTDecoder.h @@ -90,11 +90,6 @@ private: const uint32_t mDisplayWidth; const uint32_t mDisplayHeight; - // Number of times a sample was queued via Input(). Will be decreased upon - // the decoder's callback being invoked. - // This is used to calculate how many frames has been buffered by the decoder. - Atomic mQueuedSamples; - // Method to set up the decompression session. nsresult InitializeSession(); nsresult WaitForAsynchronousFrames(); @@ -106,9 +101,6 @@ private: const RefPtr mTaskQueue; const uint32_t mMaxRefFrames; const RefPtr mImageContainer; - // Increased when Input is called, and decreased when ProcessFrame runs. - // Reaching 0 indicates that there's no pending Input. - Atomic mInputIncoming; Atomic mIsShutDown; const bool mUseSoftwareImages; From 4eaa12cdd89f7d478597a983bbf7180187eebe78 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 1 Sep 2016 19:38:37 +1000 Subject: [PATCH 55/65] Bug 1297265: P4. Rework Apple AudioToolbox use of InputExhausted. r=kamidphish MozReview-Commit-ID: HznYxl9T7t5 --HG-- extra : rebase_source : 5d338eee8fe9039a6887de7170046ba6f5f47609 --- dom/media/platforms/apple/AppleATDecoder.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dom/media/platforms/apple/AppleATDecoder.cpp b/dom/media/platforms/apple/AppleATDecoder.cpp index 7ec421658321..0b8a7a320524 100644 --- a/dom/media/platforms/apple/AppleATDecoder.cpp +++ b/dom/media/platforms/apple/AppleATDecoder.cpp @@ -215,10 +215,7 @@ AppleATDecoder::SubmitSample(MediaRawData* aSample) } mQueuedSamples.Clear(); } - - if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); - } + mCallback->InputExhausted(); } nsresult From 9b83d1635578603f4f0abbf6355a1f523fdc1635 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 1 Sep 2016 19:39:28 +1000 Subject: [PATCH 56/65] Bug 1297265: P5. Rework Blank Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: KEfWo1cdRkG --HG-- extra : rebase_source : fc8c7b9cfb56c3c3cc1dcd56217dc64623c4a8ee --- dom/media/platforms/agnostic/BlankDecoderModule.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/dom/media/platforms/agnostic/BlankDecoderModule.cpp b/dom/media/platforms/agnostic/BlankDecoderModule.cpp index c8252599d113..0e5024e08676 100644 --- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp +++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp @@ -96,11 +96,7 @@ private: while (mReorderQueue.Length() > mMaxRefFrames) { mCallback->Output(mReorderQueue.Pop().get()); } - - if (mReorderQueue.Length() <= mMaxRefFrames) { - mCallback->InputExhausted(); - } - + mCallback->InputExhausted(); } private: From 713efe3bc1a664ca484402418e3cb5b6bdb5a4f1 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 1 Sep 2016 19:40:15 +1000 Subject: [PATCH 57/65] Bug 1297265: P6. Rework Opus Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: 4Usxx2BrQJt --HG-- extra : rebase_source : c0094e8a3d8ae393bf2f20b8f7d2217978eb0e75 --- dom/media/platforms/agnostic/OpusDecoder.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dom/media/platforms/agnostic/OpusDecoder.cpp b/dom/media/platforms/agnostic/OpusDecoder.cpp index c068fe37b8f0..8b2427f0889a 100644 --- a/dom/media/platforms/agnostic/OpusDecoder.cpp +++ b/dom/media/platforms/agnostic/OpusDecoder.cpp @@ -163,12 +163,9 @@ OpusDataDecoder::ProcessDecode(MediaRawData* aSample) mCallback->Error(MediaDataDecoderError::DECODE_ERROR); break; case DecodeError::DECODE_SUCCESS: + mCallback->InputExhausted(); break; } - - if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); - } } OpusDataDecoder::DecodeError From b69bfcc9eba6f6b97f703028090e3fca8739adcd Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 1 Sep 2016 19:40:52 +1000 Subject: [PATCH 58/65] Bug 1297265: P7. Rework Theora Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: 1udj9o3r2YE --HG-- extra : rebase_source : fa0a51879750f2d42720c08a4b21251b179dee96 --- dom/media/platforms/agnostic/TheoraDecoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/media/platforms/agnostic/TheoraDecoder.cpp b/dom/media/platforms/agnostic/TheoraDecoder.cpp index 9a7e33dd4d91..58e0ed04c756 100644 --- a/dom/media/platforms/agnostic/TheoraDecoder.cpp +++ b/dom/media/platforms/agnostic/TheoraDecoder.cpp @@ -202,7 +202,7 @@ TheoraDecoder::ProcessDecode(MediaRawData* aSample) } if (DoDecode(aSample) == -1) { mCallback->Error(MediaDataDecoderError::DECODE_ERROR); - } else if (mTaskQueue->IsEmpty()) { + } else { mCallback->InputExhausted(); } } From fe5751fd3101fbaf7f0f06479622976346db0522 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 1 Sep 2016 19:41:28 +1000 Subject: [PATCH 59/65] Bug 1297265: P8. Rework LibVPX Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: H7KnDpkcaeT --HG-- extra : rebase_source : b2518d0e292a3e3ddb26048991216a228c09b2b3 --- dom/media/platforms/agnostic/VPXDecoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/media/platforms/agnostic/VPXDecoder.cpp b/dom/media/platforms/agnostic/VPXDecoder.cpp index 1f6ed882a8b4..4b6cfe60291d 100644 --- a/dom/media/platforms/agnostic/VPXDecoder.cpp +++ b/dom/media/platforms/agnostic/VPXDecoder.cpp @@ -192,7 +192,7 @@ VPXDecoder::ProcessDecode(MediaRawData* aSample) } if (DoDecode(aSample) == -1) { mCallback->Error(MediaDataDecoderError::DECODE_ERROR); - } else if (mTaskQueue->IsEmpty()) { + } else { mCallback->InputExhausted(); } } From b2f24696fad0263589ea6f6ec010a058e9f38b13 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 1 Sep 2016 19:41:53 +1000 Subject: [PATCH 60/65] Bug 1297265: P9. Rework Vorbis Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: 58KKxY1YcpK --HG-- extra : rebase_source : e4ebf699c7eba1c68b874d02ccde2fb8bb3c4439 --- dom/media/platforms/agnostic/VorbisDecoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/media/platforms/agnostic/VorbisDecoder.cpp b/dom/media/platforms/agnostic/VorbisDecoder.cpp index 7b240100d06f..051d0bf17a8c 100644 --- a/dom/media/platforms/agnostic/VorbisDecoder.cpp +++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp @@ -143,7 +143,7 @@ VorbisDataDecoder::ProcessDecode(MediaRawData* aSample) } if (DoDecode(aSample) == -1) { mCallback->Error(MediaDataDecoderError::DECODE_ERROR); - } else if (mTaskQueue->IsEmpty()) { + } else { mCallback->InputExhausted(); } } From 526bb76265af5df4f71add00c30ad7b84c898e11 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 1 Sep 2016 19:42:39 +1000 Subject: [PATCH 61/65] Bug 1297265: P10. Rework FFmpeg Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: 8fndcHQELTo --HG-- extra : rebase_source : ee4d208b67964fbce0b618150c31f80938bea8f5 --- dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp | 4 +++- dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp index 16574a805dff..508f25ffc91e 100644 --- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp @@ -133,6 +133,7 @@ FFmpegAudioDecoder::DoDecode(MediaRawData* aSample) int64_t samplePosition = aSample->mOffset; media::TimeUnit pts = media::TimeUnit::FromMicroseconds(aSample->mTime); + bool didOutput = false; while (packet.size > 0) { int decoded; @@ -181,6 +182,7 @@ FFmpegAudioDecoder::DoDecode(MediaRawData* aSample) numChannels, samplingRate); mCallback->Output(data); + didOutput = true; pts += duration; if (!pts.IsValid()) { NS_WARNING("Invalid count of accumulated audio samples"); @@ -192,7 +194,7 @@ FFmpegAudioDecoder::DoDecode(MediaRawData* aSample) samplePosition += bytesConsumed; } - return DecodeResult::DECODE_FRAME; + return didOutput ? DecodeResult::DECODE_FRAME : DecodeResult::DECODE_NO_FRAME; } void diff --git a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp index 5abcb26b082a..c09f31a4a1be 100644 --- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp @@ -117,10 +117,12 @@ FFmpegDataDecoder::ProcessDecode(MediaRawData* aSample) case DecodeResult::FATAL_ERROR: mCallback->Error(MediaDataDecoderError::FATAL_ERROR); break; + case DecodeResult::DECODE_NO_FRAME: + case DecodeResult::DECODE_FRAME: + mCallback->InputExhausted(); + break; default: - if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); - } + break; } } From 7eeb483897caa4e98098617f9d6986f297d67e67 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 1 Sep 2016 19:43:16 +1000 Subject: [PATCH 62/65] Bug 1297265: P11. Rework WMF Decoder use of InputExhausted. r=kamidphish MozReview-Commit-ID: GX1izQd55Di --HG-- extra : rebase_source : f3206a7d6e84d16f4e396819c19d306bd899b1ba --- dom/media/platforms/wmf/WMFMediaDataDecoder.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp index b763748e1828..5c561b72e7cb 100644 --- a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp +++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp @@ -149,9 +149,7 @@ WMFMediaDataDecoder::ProcessOutput() mCallback->Output(output); } if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { - if (mTaskQueue->IsEmpty()) { - mCallback->InputExhausted(); - } + mCallback->InputExhausted(); } else if (FAILED(hr)) { NS_WARNING("WMFMediaDataDecoder failed to output data"); mCallback->Error(MediaDataDecoderError::DECODE_ERROR); From 78ef9147a071b194919c744a5fffaa2d0d2a0ca9 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Thu, 1 Sep 2016 20:04:48 +1000 Subject: [PATCH 63/65] Bug 1297265: P12. Rework WAV Decoder use of InputExhausted. r=gerald MozReview-Commit-ID: A2BCy4hmL2 --HG-- extra : rebase_source : 4d6fee88561149c8c487ce915b0fa8e38fd8b7e4 --- dom/media/platforms/agnostic/WAVDecoder.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dom/media/platforms/agnostic/WAVDecoder.cpp b/dom/media/platforms/agnostic/WAVDecoder.cpp index be2b2727f805..7b26e8481d51 100644 --- a/dom/media/platforms/agnostic/WAVDecoder.cpp +++ b/dom/media/platforms/agnostic/WAVDecoder.cpp @@ -68,6 +68,8 @@ WaveDataDecoder::Input(MediaRawData* aSample) { if (!DoDecode(aSample)) { mCallback->Error(MediaDataDecoderError::DECODE_ERROR); + } else { + mCallback->InputExhausted(); } return NS_OK; } From 817237ca5722e3bae9fe7ec04bee76abb6b69074 Mon Sep 17 00:00:00 2001 From: Andrew Halberstadt Date: Fri, 26 Aug 2016 16:04:08 -0400 Subject: [PATCH 64/65] Bug 1292674 - Properly set up Firefox for Android dependencies on Fedora, r=gps This patch solves 3 problems on Fedora when trying to bootstrap Firefox for Android: 1) Installs java 2) Adds a call to android.ensure_android_packages() 3) Uses build-tools-23.0.1 which seems to be the latest on Fedora I'm not sure why the Android specific packages are only being installed on Fedora and not CentOS, but as I don't have CentOS distribution to play around with figured it was best to leave that for another bug. MozReview-Commit-ID: 19sD6tYj4V --HG-- extra : rebase_source : eaf17bc05d606d3010b11927f27a482680266992 --- python/mozboot/mozboot/centosfedora.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/python/mozboot/mozboot/centosfedora.py b/python/mozboot/mozboot/centosfedora.py index b1c4e8e95860..111e5eb90c8c 100644 --- a/python/mozboot/mozboot/centosfedora.py +++ b/python/mozboot/mozboot/centosfedora.py @@ -73,6 +73,7 @@ class CentOSFedoraBootstrapper(BaseBootstrapper): ] self.mobile_android_packages += [ + 'java-1.8.0-openjdk-devel', 'ncurses-devel.i686', 'libstdc++.i686', 'zlib-devel.i686', @@ -130,6 +131,15 @@ class CentOSFedoraBootstrapper(BaseBootstrapper): ndk_path=self.ndk_path, ndk_url=self.ndk_url, artifact_mode=artifact_mode) + # Most recent version of build-tools appears to be 23.0.1 on Fedora + packages = [p for p in android.ANDROID_PACKAGES if not p.startswith('build-tools')] + packages.append('build-tools-23.0.1') + + # 3. We expect the |android| tool to be at + # ~/.mozbuild/android-sdk-linux/tools/android. + android_tool = os.path.join(self.sdk_path, 'tools', 'android') + android.ensure_android_packages(android_tool=android_tool, packages=packages) + def suggest_mobile_android_mozconfig(self, artifact_mode=False): import android android.suggest_mozconfig(sdk_path=self.sdk_path, From 9d41cac3c37b6c9ad602f815ebee8f1efe094034 Mon Sep 17 00:00:00 2001 From: Andrew Halberstadt Date: Fri, 26 Aug 2016 17:03:02 -0400 Subject: [PATCH 65/65] Bug 1292674 - Also allow android build-tools v23.0.1 in configure, r=gps I recently discovered that build-tools 23.0.3 doesn't seem to exist on Fedora. Although I fixed mozboot to download 23.0.1 instead, the builds were still failing because of configure required 23.0.3. This seems like an artificial limitation, as building with 23.0.1 seems to work just fine. This patch will allow either 23.0.1 or 23.0.3. Note: It would probably better to check for some "minimum" version of build-tools I think GNU sort has a -V option we could use, but I don't know how cross-platform this is. MozReview-Commit-ID: 8W0P3yyAHu1 --HG-- extra : rebase_source : 8e2809327eabd49f681ef42eec9c2f0eb43f1eb8 --- build/autoconf/android.m4 | 27 ++++++++++++++++++--------- old-configure.in | 2 +- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/build/autoconf/android.m4 b/build/autoconf/android.m4 index dc64ce070cb4..06815119aa98 100644 --- a/build/autoconf/android.m4 +++ b/build/autoconf/android.m4 @@ -221,8 +221,8 @@ fi ]) dnl Configure an Android SDK. -dnl Arg 1: target SDK version, like 22. -dnl Arg 2: build tools version, like 22.0.1. +dnl Arg 1: target SDK version, like 23. +dnl Arg 2: list of build-tools versions, like "23.0.3 23.0.1". AC_DEFUN([MOZ_ANDROID_SDK], [ @@ -254,12 +254,21 @@ case "$target" in fi AC_MSG_RESULT([$android_sdk]) - android_build_tools="$android_sdk_root"/build-tools/$2 - AC_MSG_CHECKING([for Android build-tools version $2]) - if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then - AC_MSG_RESULT([$android_build_tools]) - else - AC_MSG_ERROR([You must install the Android build-tools version $2. Try |mach bootstrap|. (Looked for $android_build_tools)]) + AC_MSG_CHECKING([for Android build-tools]) + android_build_tools_base="$android_sdk_root"/build-tools + android_build_tools_version="" + versions=($2) + for version in $versions; do + android_build_tools="$android_build_tools_base"/$version + if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then + android_build_tools_version=$version + AC_MSG_RESULT([$android_build_tools]) + break + fi + done + if test "$android_build_tools_version" == ""; then + version=$(echo $versions | cut -d" " -f1) + AC_MSG_ERROR([You must install the Android build-tools version $version. Try |mach bootstrap|. (Looked for "$android_build_tools_base"/$version)]) fi MOZ_PATH_PROG(ZIPALIGN, zipalign, :, [$android_build_tools]) @@ -309,7 +318,7 @@ case "$target" in ANDROID_SDK="${android_sdk}" ANDROID_SDK_ROOT="${android_sdk_root}" ANDROID_TOOLS="${android_tools}" - ANDROID_BUILD_TOOLS_VERSION="$2" + ANDROID_BUILD_TOOLS_VERSION="$android_build_tools_version" AC_DEFINE_UNQUOTED(ANDROID_TARGET_SDK,$ANDROID_TARGET_SDK) AC_SUBST(ANDROID_TARGET_SDK) AC_SUBST(ANDROID_SDK_ROOT) diff --git a/old-configure.in b/old-configure.in index 8b5a7d26406b..9186c42a5779 100644 --- a/old-configure.in +++ b/old-configure.in @@ -2495,7 +2495,7 @@ dnl ======================================================== if test -z "$gonkdir" ; then case "$MOZ_BUILD_APP" in mobile/android) - MOZ_ANDROID_SDK(23, 23.0.3) + MOZ_ANDROID_SDK(23, "23.0.3 23.0.1") ;; esac fi