MozReview-Commit-ID: KhTVPUMvnOT
This commit is contained in:
Phil Ringnalda 2016-12-29 18:26:18 -08:00
Родитель 4854d1dba1 cf2b04cf9c
Коммит 2fcf8d4ba8
13 изменённых файлов: 631 добавлений и 122 удалений

Просмотреть файл

@ -11,3 +11,4 @@ skip-if = (os == "linux" && debug) # linux: bug 976544
[browser_devices_get_user_media_screen.js]
skip-if = (e10s && debug) || (os == "linux" && !debug) # bug 1320754 for e10s debug, and bug 1320994 for linux opt
[browser_devices_get_user_media_tear_off_tab.js]
[browser_webrtc_hooks.js]

Просмотреть файл

@ -0,0 +1,363 @@
/* 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/. */
Cu.import("resource:///modules/webrtcUI.jsm");
const ORIGIN = "https://example.com";
registerCleanupFunction(function() {
gBrowser.removeCurrentTab();
});
function* tryPeerConnection(browser, expectedError = null) {
let errtype = yield ContentTask.spawn(browser, null, function*() {
let pc = new content.RTCPeerConnection();
try {
yield pc.createOffer({offerToReceiveAudio: true});
return null;
} catch (err) {
return err.name;
}
});
let detail = expectedError ? `createOffer() threw a ${expectedError}`
: "createOffer() succeeded";
is(errtype, expectedError, detail);
}
// Helper for tests that use the peer-request-allowed and -blocked events.
// A test that expects some of those events does the following:
// - call Events.on() before the test to setup event handlers
// - call Events.expect(name) after a specific event is expected to have
// occured. This will fail if the event didn't occur, and will return
// the details passed to the handler for furhter checking.
// - call Events.off() at the end of the test to clean up. At this point, if
// any events were triggered that the test did not expect, the test fails.
const Events = {
events: ["peer-request-allowed", "peer-request-blocked"],
details: new Map(),
handlers: new Map(),
on() {
for (let event of this.events) {
let handler = data => {
if (this.details.has(event)) {
ok(false, `Got multiple ${event} events`);
}
this.details.set(event, data);
};
webrtcUI.on(event, handler);
this.handlers.set(event, handler);
}
},
expect(event) {
let result = this.details.get(event);
isnot(result, undefined, `${event} event was triggered`);
this.details.delete(event);
// All events should have a good origin
is(result.origin, ORIGIN, `${event} event has correct origin`);
return result;
},
off() {
for (let event of this.events) {
webrtcUI.off(event, this.handlers.get(event));
this.handlers.delete(event);
}
for (let [event, ] of this.details) {
ok(false, `Got unexpected event ${event}`);
}
},
};
var gTests = [
{
desc: "Basic peer-request-allowed event",
run: function* testPeerRequestEvent(browser) {
Events.on();
yield tryPeerConnection(browser);
let details = Events.expect("peer-request-allowed");
isnot(details.callID, undefined, "peer-request-allowed event includes callID");
isnot(details.windowID, undefined, "peer-request-allowed event includes windowID");
Events.off();
},
},
{
desc: "Immediate peer connection blocker can allow",
run: function* testBlocker(browser) {
Events.on();
let blockerCalled = false;
let blocker = params => {
is(params.origin, ORIGIN, "Peer connection blocker origin parameter is correct");
blockerCalled = true;
return "allow";
};
webrtcUI.addPeerConnectionBlocker(blocker);
yield tryPeerConnection(browser);
is(blockerCalled, true, "Blocker was called");
Events.expect("peer-request-allowed");
webrtcUI.removePeerConnectionBlocker(blocker);
Events.off();
},
},
{
desc: "Deferred peer connection blocker can allow",
run: function* testDeferredBlocker(browser) {
Events.on();
let blocker = params => Promise.resolve("allow");
webrtcUI.addPeerConnectionBlocker(blocker);
yield tryPeerConnection(browser);
Events.expect("peer-request-allowed");
webrtcUI.removePeerConnectionBlocker(blocker);
Events.off();
},
},
{
desc: "Immediate peer connection blocker can deny",
run: function* testBlockerDeny(browser) {
Events.on();
let blocker = params => "deny";
webrtcUI.addPeerConnectionBlocker(blocker);
yield tryPeerConnection(browser, "NotAllowedError");
Events.expect("peer-request-blocked");
webrtcUI.removePeerConnectionBlocker(blocker);
Events.off();
},
},
{
desc: "Multiple blockers work (both allow)",
run: function* testMultipleAllowBlockers(browser) {
Events.on();
let blocker1Called = false, blocker1 = params => {
blocker1Called = true;
return "allow";
};
webrtcUI.addPeerConnectionBlocker(blocker1);
let blocker2Called = false, blocker2 = params => {
blocker2Called = true;
return "allow";
};
webrtcUI.addPeerConnectionBlocker(blocker2);
yield tryPeerConnection(browser);
Events.expect("peer-request-allowed");
ok(blocker1Called, "First blocker was called");
ok(blocker2Called, "Second blocker was called");
webrtcUI.removePeerConnectionBlocker(blocker1);
webrtcUI.removePeerConnectionBlocker(blocker2);
Events.off();
},
},
{
desc: "Multiple blockers work (allow then deny)",
run: function* testAllowDenyBlockers(browser) {
Events.on();
let blocker1Called = false, blocker1 = params => {
blocker1Called = true;
return "allow";
};
webrtcUI.addPeerConnectionBlocker(blocker1);
let blocker2Called = false, blocker2 = params => {
blocker2Called = true;
return "deny";
};
webrtcUI.addPeerConnectionBlocker(blocker2);
yield tryPeerConnection(browser, "NotAllowedError");
Events.expect("peer-request-blocked");
ok(blocker1Called, "First blocker was called");
ok(blocker2Called, "Second blocker was called");
webrtcUI.removePeerConnectionBlocker(blocker1);
webrtcUI.removePeerConnectionBlocker(blocker2);
Events.off();
},
},
{
desc: "Multiple blockers work (deny first)",
run: function* testDenyAllowBlockers(browser) {
Events.on();
let blocker1Called = false, blocker1 = params => {
blocker1Called = true;
return "deny";
};
webrtcUI.addPeerConnectionBlocker(blocker1);
let blocker2Called = false, blocker2 = params => {
blocker2Called = true;
return "allow";
}
webrtcUI.addPeerConnectionBlocker(blocker2);
yield tryPeerConnection(browser, "NotAllowedError");
Events.expect("peer-request-blocked");
ok(blocker1Called, "First blocker was called");
ok(!blocker2Called, "Peer connection blocker after a deny is not invoked");
webrtcUI.removePeerConnectionBlocker(blocker1);
webrtcUI.removePeerConnectionBlocker(blocker2);
Events.off();
},
},
{
desc: "Blockers may be removed",
run: function* testRemoveBlocker(browser) {
Events.on();
let blocker1Called = false, blocker1 = params => {
blocker1Called = true;
return "allow";
};
webrtcUI.addPeerConnectionBlocker(blocker1);
let blocker2Called = false, blocker2 = params => {
blocker2Called = true;
return "allow";
};
webrtcUI.addPeerConnectionBlocker(blocker2);
webrtcUI.removePeerConnectionBlocker(blocker1);
yield tryPeerConnection(browser);
Events.expect("peer-request-allowed");
ok(!blocker1Called, "Removed peer connection blocker is not invoked");
ok(blocker2Called, "Second peer connection blocker was invoked");
webrtcUI.removePeerConnectionBlocker(blocker2);
Events.off();
},
},
{
desc: "Blocker that throws is ignored",
run: function* testBlockerThrows(browser) {
Events.on();
let blocker1Called = false, blocker1 = params => {
blocker1Called = true;
throw new Error("kaboom");
};
webrtcUI.addPeerConnectionBlocker(blocker1);
let blocker2Called = false, blocker2 = params => {
blocker2Called = true;
return "allow";
};
webrtcUI.addPeerConnectionBlocker(blocker2);
yield tryPeerConnection(browser);
Events.expect("peer-request-allowed");
ok(blocker1Called, "First blocker was invoked");
ok(blocker2Called, "Second blocker was invoked");
webrtcUI.removePeerConnectionBlocker(blocker1);
webrtcUI.removePeerConnectionBlocker(blocker2);
Events.off();
},
},
{
desc: "Cancel peer request",
run: function* testBlockerCancel(browser) {
let blocker, blockerPromise = new Promise(resolve => {
blocker = params => {
resolve();
// defer indefinitely
return new Promise(innerResolve => {});
};
});
webrtcUI.addPeerConnectionBlocker(blocker);
yield ContentTask.spawn(browser, null, function*() {
(new content.RTCPeerConnection()).createOffer({offerToReceiveAudio: true});
});
yield blockerPromise;
let eventPromise = new Promise(resolve => {
webrtcUI.on("peer-request-cancel", function listener(details) {
resolve(details);
webrtcUI.off("peer-request-cancel", listener);
});
});
yield ContentTask.spawn(browser, null, function*() {
content.location.reload();
});
let details = yield eventPromise;
isnot(details.callID, undefined, "peer-request-cancel event includes callID");
is(details.origin, ORIGIN, "peer-request-cancel event has correct origin");
webrtcUI.removePeerConnectionBlocker(blocker);
},
},
];
function test() {
waitForExplicitFinish();
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.addEventListener("load", function onload() {
browser.removeEventListener("load", onload, true);
is(PopupNotifications._currentNotifications.length, 0,
"should start the test without any prior popup notification");
ok(gIdentityHandler._identityPopup.hidden,
"should start the test with the control center hidden");
Task.spawn(function* () {
yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
for (let testCase of gTests) {
info(testCase.desc);
yield testCase.run(browser);
// Make sure the test cleaned up after itself.
is(webrtcUI.peerConnectionBlockers.size, 0, "Peer connection blockers list is empty");
}
}).then(finish, ex => {
Cu.reportError(ex);
ok(false, "Unexpected Exception: " + ex);
finish();
});
}, true);
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace("chrome://mochitests/content", ORIGIN);
content.location = rootDir + "get_user_media.html";
}

Просмотреть файл

@ -10,6 +10,7 @@ const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
Cu.import("resource:///modules/syncedtabs/EventEmitter.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -17,12 +18,17 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
"resource://gre/modules/PluralForm.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
return Services.strings.createBundle("chrome://branding/locale/brand.properties");
});
this.webrtcUI = {
peerConnectionBlockers: new Set(),
emitter: new EventEmitter(),
init: function() {
Services.obs.addObserver(maybeAddMenuIndicator, "browser-delayed-startup-finished", false);
@ -165,49 +171,93 @@ this.webrtcUI = {
document.getElementById("webRTC-all-windows-shared").hidden = type != "Screen";
},
// Add-ons can override stock permission behavior by doing:
//
// webrtcUI.addPeerConnectionBlocker(function(aParams) {
// // new permission checking logic
// }));
//
// The blocking function receives an object with origin, callID, and windowID
// parameters. If it returns the string "deny" or a Promise that resolves
// to "deny", the connection is immediately blocked. With any other return
// value (though the string "allow" is suggested for consistency), control
// is passed to other registered blockers. If no registered blockers block
// the connection (or of course if there are no registered blockers), then
// the connection is allowed.
//
// Add-ons may also use webrtcUI.on/off to listen to events without
// blocking anything:
// peer-request-allowed is emitted when a new peer connection is
// established (and not blocked).
// peer-request-blocked is emitted when a peer connection request is
// blocked by some blocking connection handler.
// peer-request-cancel is emitted when a peer-request connection request
// is canceled. (This would typically be used in
// conjunction with a blocking handler to cancel
// a user prompt or other work done by the handler)
addPeerConnectionBlocker: function(aCallback) {
this.peerConnectionBlockers.add(aCallback);
},
removePeerConnectionBlocker: function(aCallback) {
this.peerConnectionBlockers.delete(aCallback);
},
on: function(...args) {
return this.emitter.on(...args);
},
off: function(...args) {
return this.emitter.off(...args);
},
receiveMessage: function(aMessage) {
switch (aMessage.name) {
// Add-ons can override stock permission behavior by doing:
//
// var stockReceiveMessage = webrtcUI.receiveMessage;
//
// webrtcUI.receiveMessage = function(aMessage) {
// switch (aMessage.name) {
// case "rtcpeer:Request": {
// // new code.
// break;
// ...
// default:
// return stockReceiveMessage.call(this, aMessage);
//
// Intercepting gUM and peerConnection requests should let an add-on
// limit PeerConnection activity with automatic rules and/or prompts
// in a sensible manner that avoids double-prompting in typical
// gUM+PeerConnection scenarios. For example:
//
// State Sample Action
// --------------------------------------------------------------
// No IP leaked yet + No gUM granted Warn user
// No IP leaked yet + gUM granted Avoid extra dialog
// No IP leaked yet + gUM request pending. Delay until gUM grant
// IP already leaked Too late to warn
case "rtcpeer:Request": {
// Always allow. This code-point exists for add-ons to override.
let { callID, windowID } = aMessage.data;
// Also available: isSecure, innerWindowID. For contentWindow:
//
// let contentWindow = Services.wm.getOuterWindowWithId(windowID);
let params = Object.freeze(Object.assign({
origin: aMessage.target.contentPrincipal.origin
}, aMessage.data));
let mm = aMessage.target.messageManager;
mm.sendAsyncMessage("rtcpeer:Allow",
{ callID: callID, windowID: windowID });
let blockers = Array.from(this.peerConnectionBlockers);
Task.spawn(function*() {
for (let blocker of blockers) {
try {
let result = yield blocker(params);
if (result == "deny") {
return false;
}
} catch (err) {
Cu.reportError(`error in PeerConnection blocker: ${err.message}`);
}
}
return true;
}).then(decision => {
let message;
if (decision) {
this.emitter.emit("peer-request-allowed", params);
message = "rtcpeer:Allow";
} else {
this.emitter.emit("peer-request-blocked", params);
message = "rtcpeer:Deny";
}
aMessage.target.messageManager.sendAsyncMessage(message, {
callID: params.callID,
windowID: params.windowID,
});
});
break;
}
case "rtcpeer:CancelRequest":
// No data to release. This code-point exists for add-ons to override.
case "rtcpeer:CancelRequest": {
let params = Object.freeze({
origin: aMessage.target.contentPrincipal.origin,
callID: aMessage.data
});
this.emitter.emit("peer-request-cancel", params);
break;
}
case "webrtc:Request":
prompt(aMessage.target, aMessage.data);
break;

Просмотреть файл

@ -50,13 +50,12 @@ NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
//
// nsMenuBarFrame cntr
//
nsMenuBarFrame::nsMenuBarFrame(nsStyleContext* aContext):
nsBoxFrame(aContext),
mStayActive(false),
mIsActive(false),
mActiveByKeyboard(false),
mCurrentMenu(nullptr),
mTarget(nullptr)
nsMenuBarFrame::nsMenuBarFrame(nsStyleContext* aContext)
: nsBoxFrame(aContext)
, mStayActive(false)
, mIsActive(false)
, mActiveByKeyboard(false)
, mCurrentMenu(nullptr)
{
} // cntr
@ -68,26 +67,7 @@ nsMenuBarFrame::Init(nsIContent* aContent,
nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
// Create the menu bar listener.
mMenuBarListener = new nsMenuBarListener(this);
// Hook up the menu bar as a key listener on the whole document. It will see every
// key press that occurs, but after everyone else does.
mTarget = aContent->GetComposedDoc();
// Also hook up the listener to the window listening for focus events. This is so we can keep proper
// state as the user alt-tabs through processes.
mTarget->AddSystemEventListener(NS_LITERAL_STRING("keypress"), mMenuBarListener, false);
mTarget->AddSystemEventListener(NS_LITERAL_STRING("keydown"), mMenuBarListener, false);
mTarget->AddSystemEventListener(NS_LITERAL_STRING("keyup"), mMenuBarListener, false);
mTarget->AddSystemEventListener(NS_LITERAL_STRING("mozaccesskeynotfound"), mMenuBarListener, false);
// mousedown event should be handled in all phase
mTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
mTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
mTarget->AddEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
mTarget->AddEventListener(NS_LITERAL_STRING("MozDOMFullscreen:Entered"), mMenuBarListener, false);
mMenuBarListener = new nsMenuBarListener(this, aContent);
}
NS_IMETHODIMP
@ -417,17 +397,6 @@ nsMenuBarFrame::DestroyFrom(nsIFrame* aDestructRoot)
if (pm)
pm->SetActiveMenuBar(this, false);
mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keypress"), mMenuBarListener, false);
mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), mMenuBarListener, false);
mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keyup"), mMenuBarListener, false);
mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mozaccesskeynotfound"), mMenuBarListener, false);
mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
mTarget->RemoveEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
mTarget->RemoveEventListener(NS_LITERAL_STRING("MozDOMFullscreen:Entered"), mMenuBarListener, false);
mMenuBarListener->OnDestroyMenuBarFrame();
mMenuBarListener = nullptr;

Просмотреть файл

@ -117,9 +117,6 @@ protected:
// The current menu that is active (highlighted), which may not be open. This will
// be null if no menu is active.
nsMenuFrame* mCurrentMenu;
mozilla::dom::EventTarget* mTarget;
}; // class nsMenuBarFrame
#endif

Просмотреть файл

@ -36,21 +36,80 @@ int32_t nsMenuBarListener::mAccessKey = -1;
Modifiers nsMenuBarListener::mAccessKeyMask = 0;
bool nsMenuBarListener::mAccessKeyFocuses = false;
nsMenuBarListener::nsMenuBarListener(nsMenuBarFrame* aMenuBar)
:mAccessKeyDown(false), mAccessKeyDownCanceled(false)
nsMenuBarListener::nsMenuBarListener(nsMenuBarFrame* aMenuBarFrame,
nsIContent* aMenuBarContent)
: mMenuBarFrame(aMenuBarFrame)
, mEventTarget(aMenuBarContent ? aMenuBarContent->GetComposedDoc() : nullptr)
, mTopWindowEventTarget(nullptr)
, mAccessKeyDown(false)
, mAccessKeyDownCanceled(false)
{
mMenuBarFrame = aMenuBar;
MOZ_ASSERT(mEventTarget);
// Hook up the menubar as a key listener on the whole document. This will
// see every keypress that occurs, but after everyone else does.
// Also hook up the listener to the window listening for focus events. This
// is so we can keep proper state as the user alt-tabs through processes.
mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("keypress"),
this, false);
mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("keydown"),
this, false);
mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("keyup"), this, false);
mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("mozaccesskeynotfound"),
this, false);
// mousedown event should be handled in all phase
mEventTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), this, true);
mEventTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), this, false);
mEventTarget->AddEventListener(NS_LITERAL_STRING("blur"), this, true);
mEventTarget->AddEventListener(
NS_LITERAL_STRING("MozDOMFullscreen:Entered"), this, false);
// Needs to listen to the deactivate event of the window.
RefPtr<EventTarget> topWindowEventTarget =
nsContentUtils::GetWindowRoot(aMenuBarContent->GetComposedDoc());
mTopWindowEventTarget = topWindowEventTarget.get();
mTopWindowEventTarget->AddSystemEventListener(NS_LITERAL_STRING("deactivate"),
this, true);
}
////////////////////////////////////////////////////////////////////////
nsMenuBarListener::~nsMenuBarListener()
{
MOZ_ASSERT(!mEventTarget,
"OnDestroyMenuBarFrame() should've alreay been called");
}
void
nsMenuBarListener::OnDestroyMenuBarFrame()
{
mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keypress"),
this, false);
mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"),
this, false);
mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keyup"),
this, false);
mEventTarget->RemoveSystemEventListener(
NS_LITERAL_STRING("mozaccesskeynotfound"), this, false);
mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, true);
mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"),
this, false);
mEventTarget->RemoveEventListener(NS_LITERAL_STRING("blur"), this, true);
mEventTarget->RemoveEventListener(
NS_LITERAL_STRING("MozDOMFullscreen:Entered"), this, false);
mTopWindowEventTarget->RemoveSystemEventListener(
NS_LITERAL_STRING("deactivate"), this, true);
mMenuBarFrame = nullptr;
mEventTarget = nullptr;
mTopWindowEventTarget = nullptr;
}
void
@ -369,14 +428,24 @@ nsMenuBarListener::Blur(nsIDOMEvent* aEvent)
{
if (!mMenuBarFrame->IsMenuOpen() && mMenuBarFrame->IsActive()) {
ToggleMenuActiveState();
mAccessKeyDown = false;
mAccessKeyDownCanceled = false;
}
return NS_OK; // means I am NOT consuming event
}
////////////////////////////////////////////////////////////////////////
nsresult
nsMenuBarListener::OnWindowDeactivated(nsIDOMEvent* aEvent)
{
// Reset the accesskey state because we cannot receive the keyup event for
// the pressing accesskey.
mAccessKeyDown = false;
mAccessKeyDownCanceled = false;
return NS_OK; // means I am NOT consuming event
}
////////////////////////////////////////////////////////////////////////
nsresult
nsMenuBarListener::MouseDown(nsIDOMEvent* aMouseEvent)
@ -442,6 +511,9 @@ nsMenuBarListener::HandleEvent(nsIDOMEvent* aEvent)
if (eventType.EqualsLiteral("blur")) {
return Blur(aEvent);
}
if (eventType.EqualsLiteral("deactivate")) {
return OnWindowDeactivated(aEvent);
}
if (eventType.EqualsLiteral("mousedown")) {
return MouseDown(aEvent);
}

Просмотреть файл

@ -2,8 +2,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsMenuBarListener_h__
#define nsMenuBarListener_h__
#ifndef nsMenuBarListener_h
#define nsMenuBarListener_h
#include "mozilla/Attributes.h"
#include "mozilla/EventForwards.h"
@ -17,39 +17,58 @@
class nsMenuBarFrame;
class nsIDOMKeyEvent;
/** editor Implementation of the DragListener interface
namespace mozilla {
namespace dom {
class EventTarget;
} // namespace dom
} // namespace mozilla
/**
* EventListener implementation for menubar.
*/
class nsMenuBarListener final : public nsIDOMEventListener
{
public:
/** default constructor
explicit nsMenuBarListener(nsMenuBarFrame* aMenuBarFrame,
nsIContent* aMenuBarContent);
NS_DECL_ISUPPORTS
/**
* nsIDOMEventListener interface method.
*/
explicit nsMenuBarListener(nsMenuBarFrame* aMenuBar);
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override;
/**
* When mMenuBarFrame is being destroyed, this should be called.
*/
void OnDestroyMenuBarFrame();
static void InitializeStatics();
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override;
/**
* GetMenuAccessKey() returns keyCode value of a modifier key which is
* used for accesskey. Returns 0 if the platform doesn't support access key.
*/
static nsresult GetMenuAccessKey(int32_t* aAccessKey);
/**
* IsAccessKeyPressed() returns true if the modifier state of aEvent matches
* the modifier state of access key.
*/
static bool IsAccessKeyPressed(nsIDOMKeyEvent* aEvent);
protected:
virtual ~nsMenuBarListener();
nsresult KeyUp(nsIDOMEvent* aMouseEvent);
nsresult KeyDown(nsIDOMEvent* aMouseEvent);
nsresult KeyPress(nsIDOMEvent* aMouseEvent);
nsresult Blur(nsIDOMEvent* aEvent);
nsresult OnWindowDeactivated(nsIDOMEvent* aEvent);
nsresult MouseDown(nsIDOMEvent* aMouseEvent);
nsresult Fullscreen(nsIDOMEvent* aEvent);
static nsresult GetMenuAccessKey(int32_t* aAccessKey);
NS_DECL_ISUPPORTS
static bool IsAccessKeyPressed(nsIDOMKeyEvent* event);
void OnDestroyMenuBarFrame();
protected:
/** default destructor
*/
virtual ~nsMenuBarListener();
static void InitAccessKey();
static mozilla::Modifiers GetModifiersForAccessKey(nsIDOMKeyEvent* event);
@ -60,15 +79,25 @@ protected:
bool Destroyed() const { return !mMenuBarFrame; }
nsMenuBarFrame* mMenuBarFrame; // The menu bar object.
// The menu bar object.
nsMenuBarFrame* mMenuBarFrame;
// The event target to listen to the events.
// XXX Should this store this as strong reference? However,
// OnDestroyMenuBarFrame() should be called at destroying mMenuBarFrame.
// So, weak reference must be safe.
mozilla::dom::EventTarget* mEventTarget;
// The top window as EventTarget.
mozilla::dom::EventTarget* mTopWindowEventTarget;
// Whether or not the ALT key is currently down.
bool mAccessKeyDown;
// Whether or not the ALT key down is canceled by other action.
bool mAccessKeyDownCanceled;
static bool mAccessKeyFocuses; // Does the access key by itself focus the menubar?
static int32_t mAccessKey; // See nsIDOMKeyEvent.h for sample values
static mozilla::Modifiers mAccessKeyMask;// Modifier mask for the access key
// Does the access key by itself focus the menubar?
static bool mAccessKeyFocuses;
// See nsIDOMKeyEvent.h for sample values.
static int32_t mAccessKey;
// Modifier mask for the access key.
static mozilla::Modifiers mAccessKeyMask;
};
#endif
#endif // #ifndef nsMenuBarListener_h

Просмотреть файл

@ -146,7 +146,6 @@ macosx64-tests-talos:
- talos-dromaeojs
- talos-g1
- talos-g2
- talos-g3
- talos-g4
- talos-other
- talos-svgr

Просмотреть файл

@ -523,7 +523,7 @@ class TryOptionSyntax(object):
return False
return True
if attr('kind') in ('desktop-test', 'android-test'):
if attr('kind') == 'test':
return match_test(self.unittests, 'unittest_try_name') \
or match_test(self.talos, 'talos_try_name')
elif attr('kind') in JOB_KINDS:

Просмотреть файл

@ -135,7 +135,7 @@ class TestNavigate(WindowManagerMixin, MarionetteTestCase):
def test_error_when_exceeding_page_load_timeout(self):
with self.assertRaises(errors.TimeoutException):
self.marionette.timeout.page_load = 0
self.marionette.timeout.page_load = 0.1
self.marionette.navigate(self.marionette.absolute_url("slow"))
self.marionette.find_element(By.TAG_NAME, "p")
@ -168,6 +168,19 @@ class TestNavigate(WindowManagerMixin, MarionetteTestCase):
self.assertRaises(errors.InsecureCertificateException,
self.marionette.navigate, self.fixtures.where_is("/test.html", on="https"))
def test_html_document_to_image_document(self):
self.marionette.navigate(self.fixtures.where_is("test.html"))
self.marionette.navigate(self.fixtures.where_is("white.png"))
self.assertIn("white.png", self.marionette.title)
def test_image_document_to_image_document(self):
self.marionette.navigate(self.fixtures.where_is("test.html"))
self.marionette.navigate(self.fixtures.where_is("white.png"))
self.assertIn("white.png", self.marionette.title)
self.marionette.navigate(self.fixtures.where_is("black.png"))
self.assertIn("black.png", self.marionette.title)
class TestTLSNavigation(MarionetteTestCase):
insecure_tls = {"acceptInsecureCerts": True}
@ -191,6 +204,7 @@ class TestTLSNavigation(MarionetteTestCase):
try:
self.capabilities = self.marionette.start_session(
desired_capabilities=self.secure_tls)
self.assertFalse(self.capabilities["acceptInsecureCerts"])
yield self.marionette
finally:
self.marionette.delete_session()
@ -200,6 +214,7 @@ class TestTLSNavigation(MarionetteTestCase):
try:
self.capabilities = self.marionette.start_session(
desired_capabilities=self.insecure_tls)
self.assertTrue(self.capabilities["acceptInsecureCerts"])
yield self.marionette
finally:
self.marionette.delete_session()

Двоичные данные
testing/marionette/harness/marionette_harness/www/black.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 150 B

Двоичные данные
testing/marionette/harness/marionette_harness/www/white.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 150 B

Просмотреть файл

@ -988,24 +988,38 @@ function get(msg) {
return;
}
let isDocument = state & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
let isStart = state & Ci.nsIWebProgressListener.STATE_START;
let loadedURL = request.URI.spec;
// We have to look at the originalURL because for about: pages,
const isDocument = state & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
const loadedURL = request.URI.spec;
// We have to look at the originalURL because of about: pages,
// the loadedURL is what the about: page resolves to, and is
// not the one that was requested.
let originalURL = request.originalURI.spec;
let isRequestedURL = loadedURL == requestedURL ||
const originalURL = request.originalURI.spec;
const isRequestedURL = loadedURL == requestedURL ||
originalURL == requestedURL;
if (isDocument && isStart && isRequestedURL) {
// We started loading the requested document. This document
// might not be the one that ends up firing DOMContentLoaded
// (if it, for example, redirects), but because we've started
// loading this URL, we know that any future DOMContentLoaded's
// are fair game to tell the Marionette client about.
if (!isDocument || !isRequestedURL) {
return;
}
// We started loading the requested document. This document
// might not be the one that ends up firing DOMContentLoaded
// (if it, for example, redirects), but because we've started
// loading this URL, we know that any future DOMContentLoaded's
// are fair game to tell the Marionette client about.
if (state & Ci.nsIWebProgressListener.STATE_START) {
sawLoad = true;
}
// This indicates network stop or last request stop outside of
// loading the document. We hit this when DOMContentLoaded is
// not triggered, which is the case for image documents.
else if (state & Ci.nsIWebProgressListener.STATE_STOP &&
content.document instanceof content.ImageDocument) {
pollForReadyState(msg, start, () => {
removeEventListener("DOMContentLoaded", onDOMContentLoaded, false);
});
}
},
onLocationChange() {},