mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b2g-inbound. a=merge
This commit is contained in:
commit
bf2a733859
@ -104,8 +104,8 @@ AccReorderEvent::IsShowHideEventTarget(const Accessible* aTarget) const
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccHideEvent::
|
||||
AccHideEvent(Accessible* aTarget, nsINode* aTargetNode, bool aNeedsShutdown) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget, aTargetNode),
|
||||
AccHideEvent(Accessible* aTarget, bool aNeedsShutdown) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget),
|
||||
mNeedsShutdown(aNeedsShutdown)
|
||||
{
|
||||
mNextSibling = mAccessible->NextSibling();
|
||||
@ -118,8 +118,8 @@ AccHideEvent::
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccShowEvent::
|
||||
AccShowEvent(Accessible* aTarget, nsINode* aTargetNode) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget, aTargetNode)
|
||||
AccShowEvent(Accessible* aTarget) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -212,8 +212,7 @@ private:
|
||||
class AccMutationEvent: public AccEvent
|
||||
{
|
||||
public:
|
||||
AccMutationEvent(uint32_t aEventType, Accessible* aTarget,
|
||||
nsINode* aTargetNode) :
|
||||
AccMutationEvent(uint32_t aEventType, Accessible* aTarget) :
|
||||
AccEvent(aEventType, aTarget, eAutoDetect, eCoalesceMutationTextChange)
|
||||
{
|
||||
// Don't coalesce these since they are coalesced by reorder event. Coalesce
|
||||
@ -250,8 +249,7 @@ protected:
|
||||
class AccHideEvent: public AccMutationEvent
|
||||
{
|
||||
public:
|
||||
AccHideEvent(Accessible* aTarget, nsINode* aTargetNode,
|
||||
bool aNeedsShutdown = true);
|
||||
explicit AccHideEvent(Accessible* aTarget, bool aNeedsShutdown = true);
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eHideEvent;
|
||||
@ -281,7 +279,7 @@ protected:
|
||||
class AccShowEvent: public AccMutationEvent
|
||||
{
|
||||
public:
|
||||
AccShowEvent(Accessible* aTarget, nsINode* aTargetNode);
|
||||
explicit AccShowEvent(Accessible* aTarget);
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eShowEvent;
|
||||
|
@ -1511,7 +1511,7 @@ DocAccessible::DoInitialUpdate()
|
||||
uint32_t childCount = ChildCount();
|
||||
for (uint32_t i = 0; i < childCount; i++) {
|
||||
Accessible* child = GetChildAt(i);
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(child, child->GetContent());
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(child);
|
||||
FireDelayedEvent(event);
|
||||
}
|
||||
}
|
||||
@ -1892,7 +1892,6 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
||||
// this node already then it will be suppressed by this one.
|
||||
Accessible* focusedAcc = nullptr;
|
||||
|
||||
nsINode* node = aChild->GetNode();
|
||||
if (aIsInsert) {
|
||||
// Create accessible tree for shown accessible.
|
||||
CacheChildrenInSubtree(aChild, &focusedAcc);
|
||||
@ -1914,9 +1913,9 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
||||
// Fire show/hide event.
|
||||
RefPtr<AccMutationEvent> event;
|
||||
if (aIsInsert)
|
||||
event = new AccShowEvent(aChild, node);
|
||||
event = new AccShowEvent(aChild);
|
||||
else
|
||||
event = new AccHideEvent(aChild, node);
|
||||
event = new AccHideEvent(aChild);
|
||||
|
||||
FireDelayedEvent(event);
|
||||
aReorderEvent->AddSubMutationEvent(event);
|
||||
@ -2089,8 +2088,7 @@ DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
||||
int32_t oldIdxInParent = aChild->IndexInParent();
|
||||
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(aChild, aChild->GetContent(), false);
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
{
|
||||
@ -2121,8 +2119,7 @@ DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
||||
FireDelayedEvent(reorderEvent);
|
||||
|
||||
reorderEvent = new AccReorderEvent(aNewParent);
|
||||
RefPtr<AccMutationEvent> showEvent =
|
||||
new AccShowEvent(aChild, aChild->GetContent());
|
||||
RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
|
||||
reorderEvent->AddSubMutationEvent(showEvent);
|
||||
|
||||
FireDelayedEvent(showEvent);
|
||||
@ -2140,8 +2137,7 @@ DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
|
||||
|
||||
Accessible* parent = aChild->Parent();
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(parent);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(aChild, aChild->GetContent(), false);
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
AutoTreeMutation mut(parent);
|
||||
@ -2152,8 +2148,7 @@ DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
|
||||
|
||||
FireDelayedEvent(hideEvent);
|
||||
|
||||
RefPtr<AccMutationEvent> showEvent =
|
||||
new AccShowEvent(aChild, aChild->GetContent());
|
||||
RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
|
||||
reorderEvent->AddSubMutationEvent(showEvent);
|
||||
FireDelayedEvent(showEvent);
|
||||
|
||||
@ -2177,8 +2172,7 @@ DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
|
||||
continue;
|
||||
}
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(owner);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(child, child->GetContent(), false);
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(child, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
{
|
||||
|
@ -121,7 +121,7 @@ HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
|
||||
}
|
||||
|
||||
if (aDoFireEvents) {
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(area, areaContent);
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(area);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
reorderEvent->AddSubMutationEvent(event);
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ HTMLLIAccessible::UpdateBullet(bool aHasBullet)
|
||||
document->BindToDocument(mBullet, nullptr);
|
||||
InsertChildAt(0, mBullet);
|
||||
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(mBullet, mBullet->GetContent());
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(mBullet);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
reorderEvent->AddSubMutationEvent(event);
|
||||
} else {
|
||||
|
@ -1014,8 +1014,6 @@ pref("browser.sessionstore.interval", 15000);
|
||||
// on which sites to save text data, POSTDATA and cookies
|
||||
// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
|
||||
pref("browser.sessionstore.privacy_level", 0);
|
||||
// the same as browser.sessionstore.privacy_level, but for saving deferred session data
|
||||
pref("browser.sessionstore.privacy_level_deferred", 1);
|
||||
// how many tabs can be reopened (per window)
|
||||
pref("browser.sessionstore.max_tabs_undo", 10);
|
||||
// how many windows can be reopened (per session) - on non-OS X platforms this
|
||||
@ -1405,8 +1403,8 @@ pref("security.insecure_password.ui.enabled", false);
|
||||
// 1 = allow MITM for certificate pinning checks.
|
||||
pref("security.cert_pinning.enforcement_level", 1);
|
||||
|
||||
// 2 = allow SHA-1 only before 2016-01-01
|
||||
pref("security.pki.sha1_enforcement_level", 2);
|
||||
// 0 = allow SHA-1
|
||||
pref("security.pki.sha1_enforcement_level", 0);
|
||||
|
||||
// Required blocklist freshness for OneCRL OCSP bypass
|
||||
// (default is 1.25x extensions.blocklist.interval, or 30 hours)
|
||||
@ -1502,7 +1500,8 @@ pref("media.eme.apiVisible", true);
|
||||
|
||||
// Decode using Gecko Media Plugins in <video>, if a system decoder is not
|
||||
// availble and the preferred GMP is available.
|
||||
pref("media.gmp.decoder.enabled", true);
|
||||
// NOTE: Disabled until Bug 1236756 is fixed by Adobe.
|
||||
pref("media.gmp.decoder.enabled", false);
|
||||
|
||||
// If decoding-via-GMP is turned on for <video>, use Adobe's GMP for decoding,
|
||||
// if it's available. Note: We won't fallback to another GMP if Adobe's is not
|
||||
|
@ -6132,6 +6132,16 @@
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="setUserContextId">
|
||||
<parameter name="aUserContextId"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.linkedBrowser.setAttribute("usercontextid", aUserContextId);
|
||||
this.setAttribute("usercontextid", aUserContextId);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
|
@ -80,8 +80,7 @@ add_task(function* test() {
|
||||
// check each item in the title and validate it meets expectatations
|
||||
for (let part of title) {
|
||||
let [storageMethodName, value] = part.split("=");
|
||||
let is_f = storageMethodName == "cookie" ? is : todo_is;
|
||||
is_f(value, expectedContext,
|
||||
is(value, expectedContext,
|
||||
"the title reflects the expected contextual identity of " +
|
||||
expectedContext + " for method " + storageMethodName + ": " + value);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ var menuBuilder = {
|
||||
let xulMenu = contextData.menu;
|
||||
xulMenu.addEventListener("popuphidden", this);
|
||||
this.xulMenu = xulMenu;
|
||||
for (let [extension, root] of rootItems) {
|
||||
for (let [, root] of rootItems) {
|
||||
let rootElement = this.buildElementWithChildren(root, contextData);
|
||||
if (!rootElement.childNodes.length) {
|
||||
// If the root has no visible children, there is no reason to show
|
||||
@ -52,7 +52,6 @@ var menuBuilder = {
|
||||
},
|
||||
|
||||
buildElementWithChildren(item, contextData) {
|
||||
let doc = contextData.menu.ownerDocument;
|
||||
let element = this.buildSingleElement(item, contextData);
|
||||
for (let child of item.children) {
|
||||
if (child.enabledForContext(contextData)) {
|
||||
@ -106,7 +105,7 @@ var menuBuilder = {
|
||||
customizeElement(element, item, contextData) {
|
||||
let label = item.title;
|
||||
if (label) {
|
||||
if (contextData.isTextSelected && label.indexOf('%s') > -1) {
|
||||
if (contextData.isTextSelected && label.indexOf("%s") > -1) {
|
||||
let selection = contextData.selectionText;
|
||||
// The rendering engine will truncate the title if it's longer than 64 characters.
|
||||
// But if it makes sense let's try truncate selection text only, to handle cases like
|
||||
@ -125,12 +124,12 @@ var menuBuilder = {
|
||||
element.setAttribute("disabled", true);
|
||||
}
|
||||
|
||||
element.addEventListener("command", event => {
|
||||
element.addEventListener("command", event => { // eslint-disable-line mozilla/balanced-listeners
|
||||
item.tabManager.addActiveTabPermission();
|
||||
if (item.onclick) {
|
||||
let clickData = item.getClickData(contextData, event);
|
||||
runSafe(item.extContext, item.onclick, clickData);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return element;
|
||||
@ -237,7 +236,7 @@ MenuItem.prototype = {
|
||||
type: "normal",
|
||||
checked: "false",
|
||||
contexts: ["all"],
|
||||
enabled: "true"
|
||||
enabled: "true",
|
||||
});
|
||||
},
|
||||
|
||||
@ -392,7 +391,7 @@ MenuItem.prototype = {
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var extCount = 0;
|
||||
|
@ -546,11 +546,11 @@ extensions.registerSchemaAPI("tabs", null, (extension, context) => {
|
||||
|
||||
let destinationWindow = null;
|
||||
if (moveProperties.windowId !== null) {
|
||||
destinationWindow = WindowManager.getWindow(moveProperties.windowId);
|
||||
// Ignore invalid window.
|
||||
if (!destinationWindow) {
|
||||
return;
|
||||
}
|
||||
destinationWindow = WindowManager.getWindow(moveProperties.windowId);
|
||||
// Ignore invalid window.
|
||||
if (!destinationWindow) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -587,11 +587,12 @@ extensions.registerSchemaAPI("tabs", null, (extension, context) => {
|
||||
if (WindowManager.getId(tab.ownerDocument.defaultView) !== windowId) {
|
||||
// If the window we are moving the tab in is different, then move the tab
|
||||
// to the new window.
|
||||
let newTab = gBrowser.addTab('about:blank');
|
||||
let newTab = gBrowser.addTab("about:blank");
|
||||
let newBrowser = gBrowser.getBrowserForTab(newTab);
|
||||
gBrowser.updateBrowserRemotenessByURL(newBrowser, tab.linkedBrowser.currentURI.spec);
|
||||
newBrowser.stop();
|
||||
newBrowser.docShell;
|
||||
// This is necessary for getter side-effects.
|
||||
void newBrowser.docShell;
|
||||
|
||||
if (tab.pinned) {
|
||||
gBrowser.pinTab(newTab);
|
||||
|
@ -1,39 +1,17 @@
|
||||
{
|
||||
"extends": "../../.eslintrc",
|
||||
"extends": "../../../../../testing/mochitest/browser.eslintrc",
|
||||
|
||||
"env": {
|
||||
"webextensions": true,
|
||||
},
|
||||
|
||||
"globals": {
|
||||
// DOM window globals
|
||||
"CustomEvent": false,
|
||||
"document": false,
|
||||
"ImageData": false,
|
||||
"MouseEvent": false,
|
||||
"setTimeout": false,
|
||||
"window": false,
|
||||
"XMLHttpRequest": false,
|
||||
|
||||
"gBrowser": false,
|
||||
|
||||
"sendAsyncMessage": false,
|
||||
|
||||
"NetUtil": true,
|
||||
"XPCOMUtils": true,
|
||||
"Task": true,
|
||||
|
||||
"browser": false,
|
||||
|
||||
// Test harness globals
|
||||
"add_task": false,
|
||||
"BrowserTestUtils": false,
|
||||
"ContentTask": false,
|
||||
"EventUtils": false,
|
||||
"ExtensionTestUtils": false,
|
||||
"info": false,
|
||||
"is": false,
|
||||
"ok": false,
|
||||
"registerCleanupFunction": false,
|
||||
"SimpleTest": false,
|
||||
"SpecialPowers": false,
|
||||
"waitForFocus": false,
|
||||
|
||||
"clickBrowserAction": true,
|
||||
"clickPageAction": true,
|
||||
|
@ -28,26 +28,26 @@ add_task(function* () {
|
||||
for (let i = 0; i < contexts.length; i++) {
|
||||
let context = contexts[i];
|
||||
let title = context;
|
||||
var id = browser.contextMenus.create({ title: title, contexts: [context], id: "ext-" + context,
|
||||
onclick: genericOnClick });
|
||||
browser.contextMenus.create({ title: title, contexts: [context], id: "ext-" + context,
|
||||
onclick: genericOnClick });
|
||||
if (context == "selection") {
|
||||
browser.contextMenus.update("ext-selection", {
|
||||
title: "selection is: '%s'",
|
||||
onclick: (info) => {
|
||||
browser.contextMenus.removeAll();
|
||||
genericOnClick(info);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var parent = browser.contextMenus.create({ title: "parent" });
|
||||
let parent = browser.contextMenus.create({ title: "parent" });
|
||||
browser.contextMenus.create(
|
||||
{ title: "child1", parentId: parent, onclick: genericOnClick });
|
||||
browser.contextMenus.create(
|
||||
let child2 = browser.contextMenus.create(
|
||||
{ title: "child2", parentId: parent, onclick: genericOnClick });
|
||||
|
||||
var parentToDel = browser.contextMenus.create({ title: "parentToDel" });
|
||||
let parentToDel = browser.contextMenus.create({ title: "parentToDel" });
|
||||
browser.contextMenus.create(
|
||||
{ title: "child1", parentId: parentToDel, onclick: genericOnClick });
|
||||
browser.contextMenus.create(
|
||||
@ -57,7 +57,7 @@ add_task(function* () {
|
||||
try {
|
||||
browser.contextMenus.update(parent, { parentId: child2 });
|
||||
browser.test.notifyFail();
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
browser.test.notifyPass();
|
||||
}
|
||||
},
|
||||
|
@ -6,26 +6,25 @@ add_task(function* () {
|
||||
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
|
||||
let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:config");
|
||||
|
||||
gBrowser.selectedTab = tab1
|
||||
gBrowser.selectedTab = tab1;
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["tabs"]
|
||||
"permissions": ["tabs"],
|
||||
},
|
||||
|
||||
background: function() {
|
||||
browser.tabs.query({
|
||||
lastFocusedWindow: true,
|
||||
}, function(tabs) {
|
||||
var tab = tabs[0];
|
||||
chrome.tabs.move(tab.id, {index: 0});
|
||||
browser.tabs.query({
|
||||
lastFocusedWindow: true,
|
||||
}, function(tabs) {
|
||||
let tab = tabs[0];
|
||||
browser.tabs.move(tab.id, {index: 0});
|
||||
browser.tabs.query(
|
||||
{ lastFocusedWindow: true },
|
||||
tabs => {
|
||||
browser.test.assertEq(tabs[0].url, tab.url, "should be first tab");
|
||||
browser.test.notifyPass("tabs.move.single");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -36,24 +35,24 @@ add_task(function* () {
|
||||
|
||||
extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["tabs"]
|
||||
"permissions": ["tabs"],
|
||||
},
|
||||
|
||||
background: function() {
|
||||
browser.tabs.query({
|
||||
lastFocusedWindow: true,
|
||||
}, function(tabs) {
|
||||
browser.tabs.query(
|
||||
{ lastFocusedWindow: true },
|
||||
tabs => {
|
||||
tabs.sort(function(a, b) { return a.url > b.url; });
|
||||
chrome.tabs.move(tabs.map(tab => tab.id), {index: 0});
|
||||
chrome.tabs.query({
|
||||
lastFocusedWindow: true,
|
||||
}, function(tabs) {
|
||||
browser.tabs.move(tabs.map(tab => tab.id), {index: 0});
|
||||
browser.tabs.query(
|
||||
{ lastFocusedWindow: true },
|
||||
tabs => {
|
||||
browser.test.assertEq(tabs[0].url, "about:blank", "should be first tab");
|
||||
browser.test.assertEq(tabs[1].url, "about:config", "should be second tab");
|
||||
browser.test.assertEq(tabs[2].url, "about:robots", "should be third tab");
|
||||
browser.test.notifyPass("tabs.move.multiple");
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -63,23 +62,23 @@ add_task(function* () {
|
||||
|
||||
extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["tabs"]
|
||||
"permissions": ["tabs"],
|
||||
},
|
||||
|
||||
background: function() {
|
||||
browser.tabs.query({
|
||||
lastFocusedWindow: true,
|
||||
}, function(tabs) {
|
||||
var tab = tabs[0];
|
||||
browser.tabs.query(
|
||||
{ lastFocusedWindow: true },
|
||||
tabs => {
|
||||
let tab = tabs[0];
|
||||
// Assuming that tab.id of 12345 does not exist.
|
||||
chrome.tabs.move([12345, tab.id], {index: 0});
|
||||
chrome.tabs.query({
|
||||
lastFocusedWindow: true,
|
||||
}, function(tabs) {
|
||||
browser.tabs.move([12345, tab.id], {index: 0});
|
||||
browser.tabs.query(
|
||||
{ lastFocusedWindow: true },
|
||||
tabs => {
|
||||
browser.test.assertEq(tabs[0].url, tab.url, "should be first tab");
|
||||
browser.test.notifyPass("tabs.move.invalid");
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -89,22 +88,22 @@ add_task(function* () {
|
||||
|
||||
extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["tabs"]
|
||||
"permissions": ["tabs"],
|
||||
},
|
||||
|
||||
background: function() {
|
||||
browser.tabs.query({
|
||||
lastFocusedWindow: true,
|
||||
}, function(tabs) {
|
||||
var tab = tabs[0];
|
||||
chrome.tabs.move(tab.id, {index: -1});
|
||||
chrome.tabs.query({
|
||||
lastFocusedWindow: true,
|
||||
}, function(tabs) {
|
||||
browser.tabs.query(
|
||||
{ lastFocusedWindow: true },
|
||||
tabs => {
|
||||
let tab = tabs[0];
|
||||
browser.tabs.move(tab.id, {index: -1});
|
||||
browser.tabs.query(
|
||||
{ lastFocusedWindow: true },
|
||||
tabs => {
|
||||
browser.test.assertEq(tabs[2].url, tab.url, "should be last tab");
|
||||
browser.test.notifyPass("tabs.move.last");
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -3,31 +3,30 @@
|
||||
"use strict";
|
||||
|
||||
add_task(function* () {
|
||||
let tab0 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
|
||||
yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
|
||||
let window1 = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
let tab1 = yield BrowserTestUtils.openNewForegroundTab(window1.gBrowser, "http://example.com/");
|
||||
yield BrowserTestUtils.openNewForegroundTab(window1.gBrowser, "http://example.com/");
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["tabs"]
|
||||
"permissions": ["tabs"],
|
||||
},
|
||||
|
||||
background: function() {
|
||||
chrome.tabs.query({
|
||||
url: '<all_urls>',
|
||||
browser.tabs.query({
|
||||
url: "<all_urls>",
|
||||
}, function(tabs) {
|
||||
let destination = tabs[0];
|
||||
let source = tabs[1]; // skip over about:blank in window1
|
||||
chrome.tabs.move(source.id, {windowId: destination.windowId, index: 0});
|
||||
|
||||
chrome.tabs.query({
|
||||
url: '<all_urls>',
|
||||
}, function(tabs) {
|
||||
browser.test.assertEq(tabs[0].url, "http://example.com/");
|
||||
browser.test.assertEq(tabs[0].windowId, destination.windowId);
|
||||
browser.test.notifyPass("tabs.move.window");
|
||||
});
|
||||
browser.tabs.move(source.id, {windowId: destination.windowId, index: 0});
|
||||
|
||||
browser.tabs.query(
|
||||
{ url: "<all_urls>" },
|
||||
tabs => {
|
||||
browser.test.assertEq(tabs[0].url, "http://example.com/");
|
||||
browser.test.assertEq(tabs[0].windowId, destination.windowId);
|
||||
browser.test.notifyPass("tabs.move.window");
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -43,31 +42,31 @@ add_task(function* () {
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
let tab0 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
|
||||
yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
|
||||
let window1 = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
let tab1 = yield BrowserTestUtils.openNewForegroundTab(window1.gBrowser, "http://example.com/");
|
||||
window1.gBrowser.pinTab(tab1);
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["tabs"]
|
||||
"permissions": ["tabs"],
|
||||
},
|
||||
|
||||
background: function() {
|
||||
chrome.tabs.query({
|
||||
url: '<all_urls>',
|
||||
}, function(tabs) {
|
||||
let destination = tabs[0];
|
||||
let source = tabs[1]; // remember, pinning moves it to the left.
|
||||
chrome.tabs.move(source.id, {windowId: destination.windowId, index: 0});
|
||||
browser.tabs.query(
|
||||
{ url: "<all_urls>" },
|
||||
tabs => {
|
||||
let destination = tabs[0];
|
||||
let source = tabs[1]; // remember, pinning moves it to the left.
|
||||
browser.tabs.move(source.id, {windowId: destination.windowId, index: 0});
|
||||
|
||||
chrome.tabs.query({
|
||||
url: '<all_urls>',
|
||||
}, function(tabs) {
|
||||
browser.test.assertEq(true, tabs[0].pinned);
|
||||
browser.test.notifyPass("tabs.move.pin");
|
||||
browser.tabs.query(
|
||||
{ url: "<all_urls>" },
|
||||
tabs => {
|
||||
browser.test.assertEq(true, tabs[0].pinned);
|
||||
browser.test.notifyPass("tabs.move.pin");
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -83,32 +82,31 @@ add_task(function* () {
|
||||
|
||||
add_task(function* () {
|
||||
let window1 = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
let tab0 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser, "http://example.net/");
|
||||
let tab1 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser, "http://example.com/");
|
||||
let tab2 = yield BrowserTestUtils.openNewForegroundTab(window1.gBrowser, "http://example.net/");
|
||||
let tab3 = yield BrowserTestUtils.openNewForegroundTab(window1.gBrowser, "http://example.com/");
|
||||
yield BrowserTestUtils.openNewForegroundTab(window.gBrowser, "http://example.net/");
|
||||
yield BrowserTestUtils.openNewForegroundTab(window.gBrowser, "http://example.com/");
|
||||
yield BrowserTestUtils.openNewForegroundTab(window1.gBrowser, "http://example.net/");
|
||||
yield BrowserTestUtils.openNewForegroundTab(window1.gBrowser, "http://example.com/");
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["tabs"]
|
||||
"permissions": ["tabs"],
|
||||
},
|
||||
|
||||
background: function() {
|
||||
chrome.tabs.query({
|
||||
url: '<all_urls>',
|
||||
}, function(tabs) {
|
||||
let move1 = tabs[1];
|
||||
let move3 = tabs[3];
|
||||
chrome.tabs.move([move1.id, move3.id], {index: 0});
|
||||
chrome.tabs.query({
|
||||
url: '<all_urls>',
|
||||
}, function(tabs) {
|
||||
browser.test.assertEq(tabs[0].url, move1.url);
|
||||
browser.test.assertEq(tabs[2].url, move3.url);
|
||||
browser.test.notifyPass("tabs.move.multiple");
|
||||
browser.tabs.query(
|
||||
{ url: "<all_urls>" },
|
||||
tabs => {
|
||||
let move1 = tabs[1];
|
||||
let move3 = tabs[3];
|
||||
browser.tabs.move([move1.id, move3.id], {index: 0});
|
||||
browser.tabs.query(
|
||||
{ url: "<all_urls>" },
|
||||
tabs => {
|
||||
browser.test.assertEq(tabs[0].url, move1.url);
|
||||
browser.test.assertEq(tabs[2].url, move3.url);
|
||||
browser.test.notifyPass("tabs.move.multiple");
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -18,12 +18,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
|
||||
* |url|.
|
||||
*
|
||||
* @param url The URL we want to save data for.
|
||||
* @param isPinned Whether the given |url| is contained in a pinned tab.
|
||||
* @return bool
|
||||
*/
|
||||
function checkPrivacyLevel(url, isPinned) {
|
||||
function checkPrivacyLevel(url) {
|
||||
let isHttps = url.startsWith("https:");
|
||||
return PrivacyLevel.canSave({isHttps: isHttps, isPinned: isPinned});
|
||||
return PrivacyLevel.canSave({isHttps});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,14 +36,13 @@ this.PrivacyFilter = Object.freeze({
|
||||
* we're allowed to store.
|
||||
*
|
||||
* @param data The session storage data as collected from a tab.
|
||||
* @param isPinned Whether the tab we collected from is pinned.
|
||||
* @return object
|
||||
*/
|
||||
filterSessionStorageData: function (data, isPinned) {
|
||||
filterSessionStorageData: function (data) {
|
||||
let retval = {};
|
||||
|
||||
for (let host of Object.keys(data)) {
|
||||
if (checkPrivacyLevel(host, isPinned)) {
|
||||
if (checkPrivacyLevel(host)) {
|
||||
retval[host] = data[host];
|
||||
}
|
||||
}
|
||||
@ -58,14 +56,13 @@ this.PrivacyFilter = Object.freeze({
|
||||
* allowed to store.
|
||||
*
|
||||
* @param data The form data as collected from a tab.
|
||||
* @param isPinned Whether the tab we collected from is pinned.
|
||||
* @return object
|
||||
*/
|
||||
filterFormData: function (data, isPinned) {
|
||||
filterFormData: function (data) {
|
||||
// If the given form data object has an associated URL that we are not
|
||||
// allowed to store data for, bail out. We explicitly discard data for any
|
||||
// children as well even if storing data for those frames would be allowed.
|
||||
if (data.url && !checkPrivacyLevel(data.url, isPinned)) {
|
||||
if (data.url && !checkPrivacyLevel(data.url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -73,7 +70,7 @@ this.PrivacyFilter = Object.freeze({
|
||||
|
||||
for (let key of Object.keys(data)) {
|
||||
if (key === "children") {
|
||||
let recurse = child => this.filterFormData(child, isPinned);
|
||||
let recurse = child => this.filterFormData(child);
|
||||
let children = data.children.map(recurse).filter(child => child);
|
||||
|
||||
if (children.length) {
|
||||
|
@ -14,8 +14,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
|
||||
"@mozilla.org/browser/sessionstartup;1", "nsISessionStartup");
|
||||
|
||||
const PREF_NORMAL = "browser.sessionstore.privacy_level";
|
||||
const PREF_DEFERRED = "browser.sessionstore.privacy_level_deferred";
|
||||
const PREF = "browser.sessionstore.privacy_level";
|
||||
|
||||
// The following constants represent the different possible privacy levels that
|
||||
// can be set by the user and that we need to consider when collecting text
|
||||
@ -28,25 +27,6 @@ const PRIVACY_ENCRYPTED = 1;
|
||||
// Collect no data.
|
||||
const PRIVACY_FULL = 2;
|
||||
|
||||
/**
|
||||
* Determines the current privacy level as set by the user.
|
||||
*
|
||||
* @param isPinned
|
||||
* Whether to return the privacy level for pinned tabs.
|
||||
* @return {int} The privacy level as read from the user's preferences.
|
||||
*/
|
||||
function getCurrentLevel(isPinned) {
|
||||
let pref = PREF_NORMAL;
|
||||
|
||||
// If we're in the process of quitting and we're not autoresuming the session
|
||||
// then we will use the deferred privacy level for non-pinned tabs.
|
||||
if (!isPinned && Services.startup.shuttingDown && !gSessionStartup.isAutomaticRestoreEnabled()) {
|
||||
pref = PREF_DEFERRED;
|
||||
}
|
||||
|
||||
return Services.prefs.getIntPref(pref);
|
||||
}
|
||||
|
||||
/**
|
||||
* The external API as exposed by this module.
|
||||
*/
|
||||
@ -54,14 +34,13 @@ var PrivacyLevel = Object.freeze({
|
||||
/**
|
||||
* Checks whether we're allowed to save data for a specific site.
|
||||
*
|
||||
* @param {isHttps: boolean, isPinned: boolean}
|
||||
* An object that must have two properties: 'isHttps' and 'isPinned'.
|
||||
* @param {isHttps: boolean}
|
||||
* An object that must have one property: 'isHttps'.
|
||||
* 'isHttps' tells whether the site us secure communication (HTTPS).
|
||||
* 'isPinned' tells whether the site is loaded in a pinned tab.
|
||||
* @return {bool} Whether we can save data for the specified site.
|
||||
*/
|
||||
canSave: function ({isHttps, isPinned}) {
|
||||
let level = getCurrentLevel(isPinned);
|
||||
canSave: function ({isHttps}) {
|
||||
let level = Services.prefs.getIntPref(PREF);
|
||||
|
||||
// Never save any data when full privacy is requested.
|
||||
if (level == PRIVACY_FULL) {
|
||||
|
@ -64,7 +64,7 @@ var SessionHistoryInternal = {
|
||||
* The docShell that owns the session history.
|
||||
*/
|
||||
collect: function (docShell) {
|
||||
let data = {entries: []};
|
||||
let data = {entries: [], userContextId: docShell.userContextId };
|
||||
let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let history = webNavigation.sessionHistory.QueryInterface(Ci.nsISHistoryInternal);
|
||||
|
||||
@ -253,6 +253,10 @@ var SessionHistoryInternal = {
|
||||
let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let history = webNavigation.sessionHistory;
|
||||
|
||||
if ("userContextId" in tabData) {
|
||||
docShell.userContextId = tabData.userContextId;
|
||||
}
|
||||
|
||||
if (history.count > 0) {
|
||||
history.PurgeHistory(history.count);
|
||||
}
|
||||
|
@ -3189,6 +3189,10 @@ var SessionStoreInternal = {
|
||||
tabbrowser.showTab(tab);
|
||||
}
|
||||
|
||||
if (tabData.userContextId) {
|
||||
tab.setUserContextId(tabData.userContextId);
|
||||
}
|
||||
|
||||
if (!!tabData.muted != browser.audioMuted) {
|
||||
tab.toggleMuteAudio();
|
||||
}
|
||||
|
@ -224,15 +224,19 @@ var TabStateInternal = {
|
||||
// Filter sensitive data according to the current privacy level.
|
||||
if (!includePrivateData) {
|
||||
if (key === "storage") {
|
||||
value = PrivacyFilter.filterSessionStorageData(value, isPinned);
|
||||
value = PrivacyFilter.filterSessionStorageData(value);
|
||||
} else if (key === "formdata") {
|
||||
value = PrivacyFilter.filterFormData(value, isPinned);
|
||||
value = PrivacyFilter.filterFormData(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (key === "history") {
|
||||
tabData.entries = value.entries;
|
||||
|
||||
if (value.hasOwnProperty("userContextId")) {
|
||||
tabData.userContextId = value.userContextId;
|
||||
}
|
||||
|
||||
if (value.hasOwnProperty("index")) {
|
||||
tabData.index = value.index;
|
||||
}
|
||||
|
@ -219,3 +219,4 @@ skip-if = os == "mac"
|
||||
run-if = e10s
|
||||
[browser_async_window_flushing.js]
|
||||
[browser_forget_async_closings.js]
|
||||
[browser_sessionStoreContainer.js]
|
||||
|
@ -0,0 +1,24 @@
|
||||
/* 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/. */
|
||||
|
||||
function retrieveUserContextId(browser) {
|
||||
return ContentTask.spawn(browser, null, function* () {
|
||||
return docShell.userContextId;
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function() {
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
let tab = gBrowser.addTab("about:blank");
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
yield promiseBrowserLoaded(browser);
|
||||
yield promiseTabState(tab, { userContextId: i, entries: [{ url: "http://example.com/" }] });
|
||||
|
||||
let userContextId = yield retrieveUserContextId(browser);
|
||||
is(userContextId, i, "The docShell has the correct userContextId");
|
||||
|
||||
yield promiseRemoveTab(tab);
|
||||
}
|
||||
});
|
@ -52,7 +52,8 @@ const SHARING_ROOM_URL = {
|
||||
COPY_FROM_PANEL: 0,
|
||||
COPY_FROM_CONVERSATION: 1,
|
||||
EMAIL_FROM_CALLFAILED: 2,
|
||||
EMAIL_FROM_CONVERSATION: 3
|
||||
EMAIL_FROM_CONVERSATION: 3,
|
||||
FACEBOOK_FROM_CONVERSATION: 4
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -85,6 +85,7 @@ loop.store = loop.store || {};
|
||||
"deleteRoom",
|
||||
"deleteRoomError",
|
||||
"emailRoomUrl",
|
||||
"facebookShareRoomUrl",
|
||||
"getAllRooms",
|
||||
"getAllRoomsError",
|
||||
"openRoom",
|
||||
@ -351,6 +352,29 @@ loop.store = loop.store || {};
|
||||
["TelemetryAddValue", "LOOP_SHARING_ROOM_URL", bucket]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Share a room url with Facebook
|
||||
*
|
||||
* @param {sharedActions.FacebookShareRoomUrl} actionData The action data.
|
||||
*/
|
||||
facebookShareRoomUrl: function(actionData) {
|
||||
var encodedRoom = encodeURIComponent(actionData.roomUrl);
|
||||
loop.request("GetLoopPref", "facebook.shareUrl")
|
||||
.then(shareUrl => {
|
||||
loop.request("OpenURL", shareUrl.replace("%ROOM_URL%", encodedRoom));
|
||||
}).then(() => {
|
||||
loop.request("NotifyUITour", "Loop:RoomURLShared");
|
||||
});
|
||||
|
||||
var from = actionData.from;
|
||||
var bucket = this._constants.SHARING_ROOM_URL["FACEBOOK_FROM_" + from.toUpperCase()];
|
||||
if (typeof bucket === "undefined") {
|
||||
console.error("No URL sharing type bucket found for '" + from + "'");
|
||||
return;
|
||||
}
|
||||
loop.request("TelemetryAddValue", "LOOP_SHARING_ROOM_URL", bucket);
|
||||
},
|
||||
|
||||
/**
|
||||
* Share a room url.
|
||||
*
|
||||
|
@ -280,6 +280,15 @@ loop.roomViews = (function(mozL10n) {
|
||||
}));
|
||||
},
|
||||
|
||||
handleFacebookButtonClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.FacebookShareRoomUrl({
|
||||
from: "conversation",
|
||||
roomUrl: this.props.roomData.roomUrl
|
||||
}));
|
||||
},
|
||||
|
||||
handleCopyButtonClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
@ -301,20 +310,6 @@ loop.roomViews = (function(mozL10n) {
|
||||
}
|
||||
},
|
||||
|
||||
handleShareButtonClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var providers = this.props.socialShareProviders;
|
||||
// If there are no providers available currently, save a click by dispatching
|
||||
// the 'AddSocialShareProvider' right away.
|
||||
if (!providers || !providers.length) {
|
||||
this.props.dispatcher.dispatch(new sharedActions.AddSocialShareProvider());
|
||||
return;
|
||||
}
|
||||
|
||||
this.toggleDropdownMenu();
|
||||
},
|
||||
|
||||
handleEditContextClose: function() {
|
||||
if (this.props.onEditContextClose) {
|
||||
this.props.onEditContextClose();
|
||||
@ -354,6 +349,12 @@ loop.roomViews = (function(mozL10n) {
|
||||
onMouseOver: this.resetTriggeredButtons},
|
||||
React.createElement("img", {src: "shared/img/glyph-email-16x16.svg"}),
|
||||
React.createElement("p", null, mozL10n.get("invite_email_link_button"))
|
||||
),
|
||||
React.createElement("div", {className: "btn-facebook invite-button",
|
||||
onClick: this.handleFacebookButtonClick,
|
||||
onMouseOver: this.resetTriggeredButtons},
|
||||
React.createElement("img", {src: "shared/img/glyph-facebook-16x16.svg"}),
|
||||
React.createElement("p", null, mozL10n.get("invite_facebook_button3"))
|
||||
)
|
||||
),
|
||||
React.createElement(SocialShareDropdown, {
|
||||
|
@ -280,6 +280,15 @@ loop.roomViews = (function(mozL10n) {
|
||||
}));
|
||||
},
|
||||
|
||||
handleFacebookButtonClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.FacebookShareRoomUrl({
|
||||
from: "conversation",
|
||||
roomUrl: this.props.roomData.roomUrl
|
||||
}));
|
||||
},
|
||||
|
||||
handleCopyButtonClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
@ -301,20 +310,6 @@ loop.roomViews = (function(mozL10n) {
|
||||
}
|
||||
},
|
||||
|
||||
handleShareButtonClick: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var providers = this.props.socialShareProviders;
|
||||
// If there are no providers available currently, save a click by dispatching
|
||||
// the 'AddSocialShareProvider' right away.
|
||||
if (!providers || !providers.length) {
|
||||
this.props.dispatcher.dispatch(new sharedActions.AddSocialShareProvider());
|
||||
return;
|
||||
}
|
||||
|
||||
this.toggleDropdownMenu();
|
||||
},
|
||||
|
||||
handleEditContextClose: function() {
|
||||
if (this.props.onEditContextClose) {
|
||||
this.props.onEditContextClose();
|
||||
@ -355,6 +350,12 @@ loop.roomViews = (function(mozL10n) {
|
||||
<img src="shared/img/glyph-email-16x16.svg" />
|
||||
<p>{mozL10n.get("invite_email_link_button")}</p>
|
||||
</div>
|
||||
<div className="btn-facebook invite-button"
|
||||
onClick={this.handleFacebookButtonClick}
|
||||
onMouseOver={this.resetTriggeredButtons}>
|
||||
<img src="shared/img/glyph-facebook-16x16.svg" />
|
||||
<p>{mozL10n.get("invite_facebook_button3")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<SocialShareDropdown
|
||||
dispatcher={this.props.dispatcher}
|
||||
|
@ -3,6 +3,7 @@ pref("loop.textChat.enabled", true);
|
||||
pref("loop.server", "https://loop.services.mozilla.com/v0");
|
||||
pref("loop.linkClicker.url", "https://hello.firefox.com/");
|
||||
pref("loop.gettingStarted.latestFTUVersion", 0);
|
||||
pref("loop.facebook.shareUrl", "https://www.facebook.com/sharer/sharer.php?u=%ROOM_URL%");
|
||||
pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start/");
|
||||
pref("loop.gettingStarted.resumeOnFirstJoin", false);
|
||||
pref("loop.learnMoreUrl", "https://www.firefox.com/hello/");
|
||||
|
@ -219,6 +219,7 @@ html[dir="rtl"] .conversation-toolbar-btn-box.btn-edit-entry {
|
||||
margin: .5rem 0 0;
|
||||
position: absolute;
|
||||
right: -10rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.call-action-group > .invite-button.triggered > p,
|
||||
@ -554,7 +555,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
||||
.room-invitation-content,
|
||||
.room-context-header {
|
||||
color: #333;
|
||||
font-size: 1.2rem;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
margin: 1rem auto;
|
||||
}
|
||||
|
@ -344,6 +344,9 @@ loop.shared.actions = (function() {
|
||||
/**
|
||||
* Copy a room url into the user's clipboard.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
* @from: where the invitation is shared from.
|
||||
* Possible values ['panel', 'conversation']
|
||||
* @roomUrl: the URL that is shared
|
||||
*/
|
||||
CopyRoomUrl: Action.define("copyRoomUrl", {
|
||||
from: String,
|
||||
@ -353,6 +356,9 @@ loop.shared.actions = (function() {
|
||||
/**
|
||||
* Email a room url.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
* @from: where the invitation is shared from.
|
||||
* Possible values ['panel', 'conversation']
|
||||
* @roomUrl: the URL that is shared
|
||||
*/
|
||||
EmailRoomUrl: Action.define("emailRoomUrl", {
|
||||
from: String,
|
||||
@ -360,9 +366,23 @@ loop.shared.actions = (function() {
|
||||
// roomDescription: String, Optional.
|
||||
}),
|
||||
|
||||
/**
|
||||
* Share a room url with Facebook.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
* @from: where the invitation is shared from.
|
||||
* Possible values ['panel', 'conversation']
|
||||
* @roomUrl: the URL that is shared
|
||||
*/
|
||||
FacebookShareRoomUrl: Action.define("facebookShareRoomUrl", {
|
||||
from: String,
|
||||
roomUrl: String
|
||||
}),
|
||||
|
||||
/**
|
||||
* Share a room url via the Social API.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
* @provider: one of the share-capable Social Providers included
|
||||
* @roomUrl: the URL that is shared
|
||||
*/
|
||||
ShareRoomUrl: Action.define("shareRoomUrl", {
|
||||
provider: Object,
|
||||
|
BIN
browser/extensions/loop/standalone/content/img/invitation.png
Normal file
BIN
browser/extensions/loop/standalone/content/img/invitation.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
@ -9,6 +9,14 @@
|
||||
<meta name="default_locale" content="en-US" />
|
||||
<meta name="referrer" content="origin" />
|
||||
|
||||
<meta property="og:image" content="https://hello.firefox.com/img/invitation.png" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:site_name" content="Firefox Hello" />
|
||||
<meta property="og:title" content="A friend has invited you to browse the Web together on Firefox Hello" />
|
||||
<meta property="og:description" content="Click to connect" />
|
||||
|
||||
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="shared/css/reset.css">
|
||||
|
@ -33,7 +33,8 @@ describe("loop.store.RoomStore", function() {
|
||||
COPY_FROM_PANEL: 0,
|
||||
COPY_FROM_CONVERSATION: 1,
|
||||
EMAIL_FROM_PANEL: 2,
|
||||
EMAIL_FROM_CONVERSATION: 3
|
||||
EMAIL_FROM_CONVERSATION: 3,
|
||||
FACEBOOK_FROM_CONVERSATION: 4
|
||||
},
|
||||
ROOM_CREATE: {
|
||||
CREATE_SUCCESS: 0,
|
||||
@ -56,6 +57,7 @@ describe("loop.store.RoomStore", function() {
|
||||
}
|
||||
},
|
||||
NotifyUITour: function() {},
|
||||
OpenURL: sinon.stub(),
|
||||
"Rooms:Create": sinon.stub().returns({ roomToken: "fakeToken" }),
|
||||
"Rooms:Delete": sinon.stub(),
|
||||
"Rooms:GetAll": sinon.stub(),
|
||||
@ -513,6 +515,43 @@ describe("loop.store.RoomStore", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#facebookShareRoomUrl", function() {
|
||||
var getLoopPrefStub;
|
||||
|
||||
beforeEach(function() {
|
||||
getLoopPrefStub = function() {
|
||||
return "https://shared.site/?u=%ROOM_URL%";
|
||||
};
|
||||
|
||||
LoopMochaUtils.stubLoopRequest({
|
||||
GetLoopPref: getLoopPrefStub
|
||||
});
|
||||
});
|
||||
|
||||
it("should open the facebook url with room URL", function() {
|
||||
|
||||
store.facebookShareRoomUrl(new sharedActions.FacebookShareRoomUrl({
|
||||
from: "conversation",
|
||||
roomUrl: "http://invalid"
|
||||
}));
|
||||
|
||||
sinon.assert.calledOnce(requestStubs.OpenURL);
|
||||
sinon.assert.calledWithExactly(requestStubs.OpenURL, "https://shared.site/?u=http%3A%2F%2Finvalid");
|
||||
});
|
||||
|
||||
it("should send a telemetry event for facebook share from conversation", function() {
|
||||
store.facebookShareRoomUrl(new sharedActions.FacebookShareRoomUrl({
|
||||
from: "conversation",
|
||||
roomUrl: "http://invalid"
|
||||
}));
|
||||
|
||||
sinon.assert.calledOnce(requestStubs.TelemetryAddValue);
|
||||
sinon.assert.calledWithExactly(requestStubs.TelemetryAddValue,
|
||||
"LOOP_SHARING_ROOM_URL", 4);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("#shareRoomUrl", function() {
|
||||
var socialShareRoomStub;
|
||||
|
||||
|
@ -246,6 +246,27 @@ describe("loop.roomViews", function() {
|
||||
}));
|
||||
});
|
||||
|
||||
it("should dispatch a FacebookShareRoomUrl action when the facebook button is clicked",
|
||||
function() {
|
||||
var url = "http://invalid";
|
||||
view = mountTestComponent({
|
||||
roomData: {
|
||||
roomUrl: url
|
||||
}
|
||||
});
|
||||
|
||||
var facebookBtn = view.getDOMNode().querySelector(".btn-facebook");
|
||||
|
||||
React.addons.TestUtils.Simulate.click(facebookBtn);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWith(dispatcher.dispatch,
|
||||
new sharedActions.FacebookShareRoomUrl({
|
||||
from: "conversation",
|
||||
roomUrl: url
|
||||
}));
|
||||
});
|
||||
|
||||
describe("Copy Button", function() {
|
||||
beforeEach(function() {
|
||||
view = mountTestComponent();
|
||||
|
@ -52,7 +52,7 @@ invite_header_text3=It takes two to use Firefox Hello, so send a friend a link t
|
||||
invite_copy_link_button=Copy Link
|
||||
invite_copied_link_button=Copied!
|
||||
invite_email_link_button=Email Link
|
||||
invite_facebook_button2=Share on Facebook
|
||||
invite_facebook_button3=Facebook
|
||||
invite_your_link=Your link:
|
||||
|
||||
# Status text
|
||||
|
@ -132,6 +132,7 @@ OriginAttributes::CreateSuffix(nsACString& aStr) const
|
||||
}
|
||||
|
||||
if (!mSignedPkg.IsEmpty()) {
|
||||
MOZ_RELEASE_ASSERT(mSignedPkg.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) == kNotFound);
|
||||
params->Set(NS_LITERAL_STRING("signedPkg"), mSignedPkg);
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
<link rel="stylesheet" href="chrome://global/skin/global.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/common.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/computedview.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/computed.css" type="text/css"/>
|
||||
|
||||
<script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"/>
|
||||
|
11
devtools/client/inspector/computed/moz.build
Normal file
11
devtools/client/inspector/computed/moz.build
Normal file
@ -0,0 +1,11 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'computed.js',
|
||||
)
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
// Extend from the shared list of defined globals for mochitests.
|
||||
"extends": "../../../.eslintrc.mochitests"
|
||||
"extends": "../../../../.eslintrc.mochitests"
|
||||
}
|
32
devtools/client/inspector/computed/test/browser.ini
Normal file
32
devtools/client/inspector/computed/test/browser.ini
Normal file
@ -0,0 +1,32 @@
|
||||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_matched_selectors.html
|
||||
doc_media_queries.html
|
||||
doc_pseudoelement.html
|
||||
doc_sourcemaps.css
|
||||
doc_sourcemaps.css.map
|
||||
doc_sourcemaps.html
|
||||
doc_sourcemaps.scss
|
||||
head.js
|
||||
|
||||
[browser_computed_browser-styles.js]
|
||||
[browser_computed_cycle_color.js]
|
||||
[browser_computed_getNodeInfo.js]
|
||||
[browser_computed_keybindings_01.js]
|
||||
[browser_computed_keybindings_02.js]
|
||||
[browser_computed_matched-selectors-toggle.js]
|
||||
[browser_computed_matched-selectors_01.js]
|
||||
[browser_computed_matched-selectors_02.js]
|
||||
[browser_computed_media-queries.js]
|
||||
[browser_computed_no-results-placeholder.js]
|
||||
[browser_computed_original-source-link.js]
|
||||
[browser_computed_pseudo-element_01.js]
|
||||
[browser_computed_refresh-on-style-change_01.js]
|
||||
[browser_computed_search-filter.js]
|
||||
[browser_computed_search-filter_clear.js]
|
||||
[browser_computed_search-filter_context-menu.js]
|
||||
[browser_computed_search-filter_escape-keypress.js]
|
||||
[browser_computed_select-and-copy-styles.js]
|
||||
[browser_computed_style-editor-link.js]
|
@ -6,7 +6,8 @@
|
||||
|
||||
// Checking selector counts, matched rules and titles in the computed-view.
|
||||
|
||||
const {PropertyView} = require("devtools/client/styleinspector/computed-view");
|
||||
const {PropertyView} =
|
||||
require("devtools/client/inspector/computed/computed");
|
||||
const TEST_URI = TEST_URL_ROOT + "doc_matched_selectors.html";
|
||||
|
||||
add_task(function*() {
|
@ -9,7 +9,7 @@
|
||||
|
||||
const TEST_URI = TEST_URL_ROOT + "doc_media_queries.html";
|
||||
|
||||
var {PropertyView} = require("devtools/client/styleinspector/computed-view");
|
||||
var {PropertyView} = require("devtools/client/inspector/computed/computed");
|
||||
var {CssLogic} = require("devtools/shared/styleinspector/css-logic");
|
||||
|
||||
add_task(function*() {
|
610
devtools/client/inspector/computed/test/head.js
Normal file
610
devtools/client/inspector/computed/test/head.js
Normal file
@ -0,0 +1,610 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
let Cu = Components.utils;
|
||||
let {gDevTools} = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {});
|
||||
let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
let {TargetFactory} = require("devtools/client/framework/target");
|
||||
let {CssComputedView} =
|
||||
require("devtools/client/inspector/computed/computed");
|
||||
let DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
let promise = require("promise");
|
||||
let {console} =
|
||||
Components.utils.import("resource://gre/modules/Console.jsm", {});
|
||||
|
||||
// All tests are asynchronous
|
||||
waitForExplicitFinish();
|
||||
|
||||
const TEST_URL_ROOT =
|
||||
"http://example.com/browser/devtools/client/inspector/computed/test/";
|
||||
const TEST_URL_ROOT_SSL =
|
||||
"https://example.com/browser/devtools/client/inspector/computed/test/";
|
||||
const ROOT_TEST_DIR = getRootDirectory(gTestPath);
|
||||
const FRAME_SCRIPT_URL = ROOT_TEST_DIR + "doc_frame_script.js";
|
||||
|
||||
// Auto clean-up when a test ends
|
||||
registerCleanupFunction(function*() {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
yield gDevTools.closeToolbox(target);
|
||||
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
});
|
||||
|
||||
// Uncomment this pref to dump all devtools emitted events to the console.
|
||||
// Services.prefs.setBoolPref("devtools.dump.emit", true);
|
||||
|
||||
// Set the testing flag on gDevTools and reset it when the test ends
|
||||
DevToolsUtils.testing = true;
|
||||
registerCleanupFunction(() => DevToolsUtils.testing = false);
|
||||
|
||||
// Clean-up all prefs that might have been changed during a test run
|
||||
// (safer here because if the test fails, then the pref is never reverted)
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
|
||||
Services.prefs.clearUserPref("devtools.dump.emit");
|
||||
Services.prefs.clearUserPref("devtools.defaultColorUnit");
|
||||
});
|
||||
|
||||
/**
|
||||
* The functions found below are here to ease test development and maintenance.
|
||||
* Most of these functions are stateless and will require some form of context
|
||||
* (the instance of the current toolbox, or inspector panel for instance).
|
||||
*
|
||||
* Most of these functions are async too and return promises.
|
||||
*
|
||||
* All tests should follow the following pattern:
|
||||
*
|
||||
* add_task(function*() {
|
||||
* yield addTab(TEST_URI);
|
||||
* let {toolbox, inspector, view} = yield openComputedView();
|
||||
*
|
||||
* yield selectNode("#test", inspector);
|
||||
* yield someAsyncTestFunction(view);
|
||||
* });
|
||||
*
|
||||
* add_task is the way to define the testcase in the test file. It accepts
|
||||
* a single generator-function argument.
|
||||
* The generator function should yield any async call.
|
||||
*
|
||||
* There is no need to clean tabs up at the end of a test as this is done
|
||||
* automatically.
|
||||
*
|
||||
* It is advised not to store any references on the global scope. There
|
||||
* shouldn't be a need to anyway. Thanks to add_task, test steps, even
|
||||
* though asynchronous, can be described in a nice flat way, and
|
||||
* if/for/while/... control flow can be used as in sync code, making it
|
||||
* possible to write the outline of the test case all in add_task, and delegate
|
||||
* actual processing and assertions to other functions.
|
||||
*/
|
||||
|
||||
/* *********************************************
|
||||
* UTILS
|
||||
* *********************************************
|
||||
* General test utilities.
|
||||
* Add new tabs, open the toolbox and switch to the various panels, select
|
||||
* nodes, get node references, ...
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a new test tab in the browser and load the given url.
|
||||
*
|
||||
* @param {String} url
|
||||
* The url to be loaded in the new tab
|
||||
* @return a promise that resolves to the tab object when the url is loaded
|
||||
*/
|
||||
function addTab(url) {
|
||||
info("Adding a new tab with URL: '" + url + "'");
|
||||
let def = promise.defer();
|
||||
|
||||
window.focus();
|
||||
|
||||
let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
info("Loading the helper frame script " + FRAME_SCRIPT_URL);
|
||||
browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
|
||||
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
info("URL '" + url + "' loading complete");
|
||||
|
||||
def.resolve(tab);
|
||||
}, true);
|
||||
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple DOM node accesor function that takes either a node or a string css
|
||||
* selector as argument and returns the corresponding node
|
||||
*
|
||||
* @param {String|DOMNode} nodeOrSelector
|
||||
* @return {DOMNode|CPOW} Note that in e10s mode a CPOW object is returned which
|
||||
* doesn't implement *all* of the DOMNode's properties
|
||||
*/
|
||||
function getNode(nodeOrSelector) {
|
||||
info("Getting the node for '" + nodeOrSelector + "'");
|
||||
return typeof nodeOrSelector === "string" ?
|
||||
content.document.querySelector(nodeOrSelector) :
|
||||
nodeOrSelector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the NodeFront for a given css selector, via the protocol
|
||||
*
|
||||
* @param {String} selector
|
||||
* @param {InspectorPanel} inspector
|
||||
* The instance of InspectorPanel currently loaded in the toolbox
|
||||
* @return {Promise} Resolves to the NodeFront instance
|
||||
*/
|
||||
function getNodeFront(selector, {walker}) {
|
||||
return walker.querySelector(walker.rootNode, selector);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the inspector's current selection to a node or to the first match of the
|
||||
* given css selector.
|
||||
*
|
||||
* @param {String|NodeFront} data
|
||||
* The node to select
|
||||
* @param {InspectorPanel} inspector
|
||||
* The instance of InspectorPanel currently loaded in the toolbox
|
||||
* @param {String} reason
|
||||
* Defaults to "test" which instructs the inspector not
|
||||
* to highlight the node upon selection
|
||||
* @return {Promise} Resolves when the inspector is updated with the new node
|
||||
*/
|
||||
var selectNode = Task.async(function*(data, inspector, reason="test") {
|
||||
info("Selecting the node for '" + data + "'");
|
||||
let nodeFront = data;
|
||||
if (!data._form) {
|
||||
nodeFront = yield getNodeFront(data, inspector);
|
||||
}
|
||||
let updated = inspector.once("inspector-updated");
|
||||
inspector.selection.setNodeFront(nodeFront, reason);
|
||||
yield updated;
|
||||
});
|
||||
|
||||
/**
|
||||
* Open the toolbox, with the inspector tool visible.
|
||||
*
|
||||
* @return a promise that resolves when the inspector is ready
|
||||
*/
|
||||
var openInspector = Task.async(function*() {
|
||||
info("Opening the inspector");
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
|
||||
let inspector, toolbox;
|
||||
|
||||
// Checking if the toolbox and the inspector are already loaded
|
||||
// The inspector-updated event should only be waited for if the inspector
|
||||
// isn't loaded yet
|
||||
toolbox = gDevTools.getToolbox(target);
|
||||
if (toolbox) {
|
||||
inspector = toolbox.getPanel("inspector");
|
||||
if (inspector) {
|
||||
info("Toolbox and inspector already open");
|
||||
return {
|
||||
toolbox: toolbox,
|
||||
inspector: inspector
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
info("Opening the toolbox");
|
||||
toolbox = yield gDevTools.showToolbox(target, "inspector");
|
||||
yield waitForToolboxFrameFocus(toolbox);
|
||||
inspector = toolbox.getPanel("inspector");
|
||||
|
||||
info("Waiting for the inspector to update");
|
||||
yield inspector.once("inspector-updated");
|
||||
|
||||
return {
|
||||
toolbox: toolbox,
|
||||
inspector: inspector
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Wait for the toolbox frame to receive focus after it loads
|
||||
*
|
||||
* @param {Toolbox} toolbox
|
||||
* @return a promise that resolves when focus has been received
|
||||
*/
|
||||
function waitForToolboxFrameFocus(toolbox) {
|
||||
info("Making sure that the toolbox's frame is focused");
|
||||
let def = promise.defer();
|
||||
let win = toolbox.frame.contentWindow;
|
||||
waitForFocus(def.resolve, win);
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the toolbox, with the inspector tool visible, and the sidebar that
|
||||
* corresponds to the given id selected
|
||||
*
|
||||
* @return a promise that resolves when the inspector is ready and the sidebar
|
||||
* view is visible and ready
|
||||
*/
|
||||
var openInspectorSideBar = Task.async(function*(id) {
|
||||
let {toolbox, inspector} = yield openInspector();
|
||||
|
||||
if (!hasSideBarTab(inspector, id)) {
|
||||
info("Waiting for the " + id + " sidebar to be ready");
|
||||
yield inspector.sidebar.once(id + "-ready");
|
||||
}
|
||||
|
||||
info("Selecting the " + id + " sidebar");
|
||||
inspector.sidebar.select(id);
|
||||
|
||||
return {
|
||||
toolbox: toolbox,
|
||||
inspector: inspector,
|
||||
view: inspector.sidebar.getWindowForTab(id)[id].view
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Open the toolbox, with the inspector tool visible, and the computed-view
|
||||
* sidebar tab selected.
|
||||
*
|
||||
* @return a promise that resolves when the inspector is ready and the computed
|
||||
* view is visible and ready
|
||||
*/
|
||||
function openComputedView() {
|
||||
return openInspectorSideBar("computedview");
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for eventName on target to be delivered a number of times.
|
||||
*
|
||||
* @param {Object} target
|
||||
* An observable object that either supports on/off or
|
||||
* addEventListener/removeEventListener
|
||||
* @param {String} eventName
|
||||
* @param {Number} numTimes
|
||||
* Number of deliveries to wait for.
|
||||
* @param {Boolean} useCapture
|
||||
* Optional, for addEventListener/removeEventListener
|
||||
* @return A promise that resolves when the event has been handled
|
||||
*/
|
||||
function waitForNEvents(target, eventName, numTimes, useCapture = false) {
|
||||
info("Waiting for event: '" + eventName + "' on " + target + ".");
|
||||
|
||||
let deferred = promise.defer();
|
||||
let count = 0;
|
||||
|
||||
for (let [add, remove] of [
|
||||
["addEventListener", "removeEventListener"],
|
||||
["addListener", "removeListener"],
|
||||
["on", "off"]
|
||||
]) {
|
||||
if ((add in target) && (remove in target)) {
|
||||
target[add](eventName, function onEvent(...aArgs) {
|
||||
if (++count == numTimes) {
|
||||
target[remove](eventName, onEvent, useCapture);
|
||||
deferred.resolve.apply(deferred, aArgs);
|
||||
}
|
||||
}, useCapture);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for eventName on target.
|
||||
*
|
||||
* @param {Object} target
|
||||
* An observable object that either supports on/off or
|
||||
* addEventListener/removeEventListener
|
||||
* @param {String} eventName
|
||||
* @param {Boolean} useCapture
|
||||
* Optional, for addEventListener/removeEventListener
|
||||
* @return A promise that resolves when the event has been handled
|
||||
*/
|
||||
function once(target, eventName, useCapture=false) {
|
||||
return waitForNEvents(target, eventName, 1, useCapture);
|
||||
}
|
||||
|
||||
/**
|
||||
* This shouldn't be used in the tests, but is useful when writing new tests or
|
||||
* debugging existing tests in order to introduce delays in the test steps
|
||||
*
|
||||
* @param {Number} ms
|
||||
* The time to wait
|
||||
* @return A promise that resolves when the time is passed
|
||||
*/
|
||||
function wait(ms) {
|
||||
let def = promise.defer();
|
||||
content.setTimeout(def.resolve, ms);
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for a new tab to open and return a promise that resolves when one
|
||||
* does and completes the load event.
|
||||
*
|
||||
* @return a promise that resolves to the tab object
|
||||
*/
|
||||
var waitForTab = Task.async(function*() {
|
||||
info("Waiting for a tab to open");
|
||||
yield once(gBrowser.tabContainer, "TabOpen");
|
||||
let tab = gBrowser.selectedTab;
|
||||
let browser = tab.linkedBrowser;
|
||||
yield once(browser, "load", true);
|
||||
info("The tab load completed");
|
||||
return tab;
|
||||
});
|
||||
|
||||
/**
|
||||
* @see SimpleTest.waitForClipboard
|
||||
*
|
||||
* @param {Function} setup
|
||||
* Function to execute before checking for the
|
||||
* clipboard content
|
||||
* @param {String|Boolean} expected
|
||||
* An expected string or validator function
|
||||
* @return a promise that resolves when the expected string has been found or
|
||||
* the validator function has returned true, rejects otherwise.
|
||||
*/
|
||||
function waitForClipboard(setup, expected) {
|
||||
let def = promise.defer();
|
||||
SimpleTest.waitForClipboard(expected, setup, def.resolve, def.reject);
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the copy event on the given element
|
||||
*/
|
||||
function fireCopyEvent(element) {
|
||||
let evt = element.ownerDocument.createEvent("Event");
|
||||
evt.initEvent("copy", true, true);
|
||||
element.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the inspector's sidebar corresponding to the given id already
|
||||
* exists
|
||||
*
|
||||
* @param {InspectorPanel}
|
||||
* @param {String}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function hasSideBarTab(inspector, id) {
|
||||
return !!inspector.sidebar.getWindowForTab(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate the key input for the given input in the window.
|
||||
*
|
||||
* @param {String} input
|
||||
* The string value to input
|
||||
* @param {Window} win
|
||||
* The window containing the panel
|
||||
*/
|
||||
function synthesizeKeys(input, win) {
|
||||
for (let key of input.split("")) {
|
||||
EventUtils.synthesizeKey(key, {}, win);
|
||||
}
|
||||
}
|
||||
|
||||
/* *********************************************
|
||||
* COMPUTED-VIEW
|
||||
* *********************************************
|
||||
* Computed-view related utility functions.
|
||||
* Allows to get properties, links, expand properties, ...
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get references to the name and value span nodes corresponding to a given
|
||||
* property name in the computed-view
|
||||
*
|
||||
* @param {CssComputedView} view
|
||||
* The instance of the computed view panel
|
||||
* @param {String} name
|
||||
* The name of the property to retrieve
|
||||
* @return an object {nameSpan, valueSpan}
|
||||
*/
|
||||
function getComputedViewProperty(view, name) {
|
||||
let prop;
|
||||
for (let property of view.styleDocument.querySelectorAll(".property-view")) {
|
||||
let nameSpan = property.querySelector(".property-name");
|
||||
let valueSpan = property.querySelector(".property-value");
|
||||
|
||||
if (nameSpan.textContent === name) {
|
||||
prop = {nameSpan: nameSpan, valueSpan: valueSpan};
|
||||
break;
|
||||
}
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of PropertyView from the computed-view.
|
||||
*
|
||||
* @param {CssComputedView} view
|
||||
* The instance of the computed view panel
|
||||
* @param {String} name
|
||||
* The name of the property to retrieve
|
||||
* @return {PropertyView}
|
||||
*/
|
||||
function getComputedViewPropertyView(view, name) {
|
||||
let propView;
|
||||
for (let propertyView of view.propertyViews) {
|
||||
if (propertyView._propertyInfo.name === name) {
|
||||
propView = propertyView;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return propView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to the property-content element for a given property name in
|
||||
* the computed-view.
|
||||
* A property-content element always follows (nextSibling) the property itself
|
||||
* and is only shown when the twisty icon is expanded on the property.
|
||||
* A property-content element contains matched rules, with selectors,
|
||||
* properties, values and stylesheet links
|
||||
*
|
||||
* @param {CssComputedView} view
|
||||
* The instance of the computed view panel
|
||||
* @param {String} name
|
||||
* The name of the property to retrieve
|
||||
* @return {Promise} A promise that resolves to the property matched rules
|
||||
* container
|
||||
*/
|
||||
var getComputedViewMatchedRules = Task.async(function*(view, name) {
|
||||
let expander;
|
||||
let propertyContent;
|
||||
for (let property of view.styleDocument.querySelectorAll(".property-view")) {
|
||||
let nameSpan = property.querySelector(".property-name");
|
||||
if (nameSpan.textContent === name) {
|
||||
expander = property.querySelector(".expandable");
|
||||
propertyContent = property.nextSibling;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!expander.hasAttribute("open")) {
|
||||
// Need to expand the property
|
||||
let onExpand = view.inspector.once("computed-view-property-expanded");
|
||||
expander.click();
|
||||
yield onExpand;
|
||||
}
|
||||
|
||||
return propertyContent;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the text value of the property corresponding to a given name in the
|
||||
* computed-view
|
||||
*
|
||||
* @param {CssComputedView} view
|
||||
* The instance of the computed view panel
|
||||
* @param {String} name
|
||||
* The name of the property to retrieve
|
||||
* @return {String} The property value
|
||||
*/
|
||||
function getComputedViewPropertyValue(view, name, propertyName) {
|
||||
return getComputedViewProperty(view, name, propertyName)
|
||||
.valueSpan.textContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a given property, given its index in the current property list of
|
||||
* the computed view
|
||||
*
|
||||
* @param {CssComputedView} view
|
||||
* The instance of the computed view panel
|
||||
* @param {Number} index
|
||||
* The index of the property to be expanded
|
||||
* @return a promise that resolves when the property has been expanded, or
|
||||
* rejects if the property was not found
|
||||
*/
|
||||
function expandComputedViewPropertyByIndex(view, index) {
|
||||
info("Expanding property " + index + " in the computed view");
|
||||
let expandos = view.styleDocument.querySelectorAll(".expandable");
|
||||
if (!expandos.length || !expandos[index]) {
|
||||
return promise.reject();
|
||||
}
|
||||
|
||||
let onExpand = view.inspector.once("computed-view-property-expanded");
|
||||
expandos[index].click();
|
||||
return onExpand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a rule-link from the computed-view given its index
|
||||
*
|
||||
* @param {CssComputedView} view
|
||||
* The instance of the computed view panel
|
||||
* @param {Number} index
|
||||
* The index of the link to be retrieved
|
||||
* @return {DOMNode} The link at the given index, if one exists, null otherwise
|
||||
*/
|
||||
function getComputedViewLinkByIndex(view, index) {
|
||||
let links = view.styleDocument.querySelectorAll(".rule-link .link");
|
||||
return links[index];
|
||||
}
|
||||
|
||||
/* *********************************************
|
||||
* STYLE-EDITOR
|
||||
* *********************************************
|
||||
* Style-editor related utility functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Wait for the toolbox to emit the styleeditor-selected event and when done
|
||||
* wait for the stylesheet identified by href to be loaded in the stylesheet
|
||||
* editor
|
||||
*
|
||||
* @param {Toolbox} toolbox
|
||||
* @param {String} href
|
||||
* Optional, if not provided, wait for the first editor to be ready
|
||||
* @return a promise that resolves to the editor when the stylesheet editor is
|
||||
* ready
|
||||
*/
|
||||
function waitForStyleEditor(toolbox, href) {
|
||||
let def = promise.defer();
|
||||
|
||||
info("Waiting for the toolbox to switch to the styleeditor");
|
||||
toolbox.once("styleeditor-selected").then(() => {
|
||||
let panel = toolbox.getCurrentPanel();
|
||||
ok(panel && panel.UI, "Styleeditor panel switched to front");
|
||||
|
||||
// A helper that resolves the promise once it receives an editor that
|
||||
// matches the expected href. Returns false if the editor was not correct.
|
||||
let gotEditor = (event, editor) => {
|
||||
let currentHref = editor.styleSheet.href;
|
||||
if (!href || (href && currentHref.endsWith(href))) {
|
||||
info("Stylesheet editor selected");
|
||||
panel.UI.off("editor-selected", gotEditor);
|
||||
|
||||
editor.getSourceEditor().then(sourceEditor => {
|
||||
info("Stylesheet editor fully loaded");
|
||||
def.resolve(sourceEditor);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
info("The editor was incorrect. Waiting for editor-selected event.");
|
||||
return false;
|
||||
};
|
||||
|
||||
// The expected editor may already be selected. Check the if the currently
|
||||
// selected editor is the expected one and if not wait for an
|
||||
// editor-selected event.
|
||||
if (!gotEditor("styleeditor-selected", panel.UI.selectedEditor)) {
|
||||
// The expected editor is not selected (yet). Wait for it.
|
||||
panel.UI.on("editor-selected", gotEditor);
|
||||
}
|
||||
});
|
||||
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the current page and wait for the inspector to be initialized after
|
||||
* the navigation
|
||||
*
|
||||
* @param {InspectorPanel} inspector
|
||||
* The instance of InspectorPanel currently loaded in the toolbox
|
||||
* @return a promise that resolves after page reload and inspector
|
||||
* initialization
|
||||
*/
|
||||
function reloadPage(inspector) {
|
||||
let onNewRoot = inspector.once("new-root");
|
||||
content.location.reload();
|
||||
return onNewRoot.then(() => {
|
||||
inspector.markup._waitForChildren();
|
||||
});
|
||||
}
|
@ -11,13 +11,13 @@
|
||||
<head>
|
||||
<title>&title;</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<link rel="stylesheet" href="font-inspector.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="fonts.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/common.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/font-inspector.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/fonts.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"/>
|
||||
</head>
|
||||
<body class="theme-sidebar devtools-monospace" role="application">
|
||||
<script type="application/javascript;version=1.8" src="font-inspector.js"></script>
|
||||
<script type="application/javascript;version=1.8" src="fonts.js"></script>
|
||||
<div>
|
||||
<div class="devtools-toolbar preview-input-toolbar">
|
||||
<div class="devtools-searchbox">
|
@ -4,5 +4,8 @@
|
||||
# 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/.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
DevToolsModules(
|
||||
'fonts.js',
|
||||
)
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
// Extend from the shared list of defined globals for mochitests.
|
||||
"extends": "../../../.eslintrc.mochitests"
|
||||
"extends": "../../../../.eslintrc.mochitests"
|
||||
}
|
@ -2,12 +2,13 @@
|
||||
/* 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";
|
||||
|
||||
// shared-head.js handles imports, constants, and utility functions
|
||||
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
|
||||
|
||||
const BASE_URI = "http://mochi.test:8888/browser/devtools/client/fontinspector/test/"
|
||||
const BASE_URI = "http://mochi.test:8888/browser/devtools/client/inspector/fonts/test/";
|
||||
|
||||
/**
|
||||
* Open the toolbox, with the inspector tool visible.
|
@ -353,21 +353,21 @@ InspectorPanel.prototype = {
|
||||
this.sidebar.on("select", this._setDefaultSidebar);
|
||||
|
||||
this.sidebar.addTab("ruleview",
|
||||
"chrome://devtools/content/styleinspector/cssruleview.xhtml",
|
||||
"chrome://devtools/content/inspector/rules/rules.xhtml",
|
||||
"ruleview" == defaultTab);
|
||||
|
||||
this.sidebar.addTab("computedview",
|
||||
"chrome://devtools/content/styleinspector/computedview.xhtml",
|
||||
"chrome://devtools/content/inspector/computed/computed.xhtml",
|
||||
"computedview" == defaultTab);
|
||||
|
||||
if (Services.prefs.getBoolPref("devtools.fontinspector.enabled") && this.canGetUsedFontFaces) {
|
||||
this.sidebar.addTab("fontinspector",
|
||||
"chrome://devtools/content/fontinspector/font-inspector.xhtml",
|
||||
"chrome://devtools/content/inspector/fonts/fonts.xhtml",
|
||||
"fontinspector" == defaultTab);
|
||||
}
|
||||
|
||||
this.sidebar.addTab("layoutview",
|
||||
"chrome://devtools/content/layoutview/view.xhtml",
|
||||
"chrome://devtools/content/inspector/layout/layout.xhtml",
|
||||
"layoutview" == defaultTab);
|
||||
|
||||
if (this.target.form.animationsActor) {
|
||||
|
@ -15,10 +15,10 @@
|
||||
<script type="application/javascript;version=1.8"
|
||||
src="chrome://devtools/content/shared/theme-switching.js"/>
|
||||
|
||||
<script type="application/javascript;version=1.8" src="view.js"></script>
|
||||
<script type="application/javascript;version=1.8" src="layout.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/common.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/layoutview.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/layout.css" type="text/css"/>
|
||||
|
||||
</head>
|
||||
<body class="theme-sidebar devtools-monospace">
|
@ -4,4 +4,8 @@
|
||||
# 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/.
|
||||
|
||||
JAR_MANIFESTS += ['../../../browser/extensions/shumway/jar.mn']
|
||||
DevToolsModules(
|
||||
'layout.js',
|
||||
)
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
4
devtools/client/inspector/layout/test/.eslintrc
Normal file
4
devtools/client/inspector/layout/test/.eslintrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
// Extend from the shared list of defined globals for mochitests.
|
||||
"extends": "../../../../.eslintrc.mochitests"
|
||||
}
|
22
devtools/client/inspector/layout/test/browser.ini
Normal file
22
devtools/client/inspector/layout/test/browser.ini
Normal file
@ -0,0 +1,22 @@
|
||||
[DEFAULT]
|
||||
tags = devtools
|
||||
skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_layout_iframe1.html
|
||||
doc_layout_iframe2.html
|
||||
head.js
|
||||
|
||||
[browser_layout.js]
|
||||
[browser_layout_editablemodel.js]
|
||||
# [browser_layout_editablemodel_allproperties.js]
|
||||
# Disabled for too many intermittent failures (bug 1009322)
|
||||
[browser_layout_editablemodel_border.js]
|
||||
[browser_layout_editablemodel_stylerules.js]
|
||||
[browser_layout_guides.js]
|
||||
[browser_layout_rotate-labels-on-sides.js]
|
||||
[browser_layout_tooltips.js]
|
||||
[browser_layout_update-after-navigation.js]
|
||||
[browser_layout_update-after-reload.js]
|
||||
# [browser_layout_update-in-iframes.js]
|
||||
# Bug 1020038 layout-view updates for iframe elements changes
|
@ -8,7 +8,7 @@
|
||||
// it also works after going back
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_layoutview_iframe1.html");
|
||||
yield addTab(TEST_URL_ROOT + "doc_layout_iframe1.html");
|
||||
let {toolbox, inspector, view} = yield openLayoutView();
|
||||
yield runTests(inspector, view);
|
||||
});
|
||||
@ -34,7 +34,7 @@ function*(inspector, view) {
|
||||
|
||||
addTest("Navigate to the second page",
|
||||
function*(inspector, view) {
|
||||
yield navigateTo(TEST_URL_ROOT + "doc_layoutview_iframe2.html");
|
||||
yield navigateTo(TEST_URL_ROOT + "doc_layout_iframe2.html");
|
||||
yield inspector.once("markuploaded");
|
||||
});
|
||||
|
@ -7,7 +7,7 @@
|
||||
// Test that the layout-view continues to work after the page is reloaded
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_layoutview_iframe1.html");
|
||||
yield addTab(TEST_URL_ROOT + "doc_layout_iframe1.html");
|
||||
let {toolbox, inspector, view} = yield openLayoutView();
|
||||
|
||||
info("Test that the layout-view works on the first page");
|
@ -8,7 +8,7 @@
|
||||
// change
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_layoutview_iframe1.html");
|
||||
yield addTab(TEST_URL_ROOT + "doc_layout_iframe1.html");
|
||||
let iframe2 = getNode("iframe").contentDocument.querySelector("iframe");
|
||||
|
||||
let {toolbox, inspector, view} = yield openLayoutView();
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user