MozReview-Commit-ID: AXRXwXgkOTv
This commit is contained in:
Kartikaya Gupta 2017-02-10 13:57:53 -05:00
Родитель 6b121b94c7 3ed98bee2a
Коммит 96c394d70c
586 изменённых файлов: 24873 добавлений и 22158 удалений

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

@ -11,8 +11,15 @@ jobs:
target-tasks-method: nightly_linux
run-on-projects:
- mozilla-central
- mozilla-aurora
- date
when: [] # never (temporary)
when:
by-project:
# Match buildbot starts for now
date: [{hour: 16, minute: 0}]
mozilla-central: [{hour: 11, minute: 0}]
mozilla-aurora: [{hour: 8, minute: 45}] # Buildbot uses minute 40
# No default
- name: nightly-android
job:
@ -22,8 +29,15 @@ jobs:
target-tasks-method: nightly_fennec
run-on-projects:
- mozilla-central
- mozilla-aurora
- date
when: [] # never (temporary)
when:
by-project:
# Match buildbot starts for now
date: [{hour: 16, minute: 0}]
mozilla-central: [{hour: 11, minute: 0}]
mozilla-aurora: [{hour: 8, minute: 45}] # Buildbot uses minute 40
# No default
- name: nightly-mochitest-valgrind
job:

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

@ -25,7 +25,6 @@ void
ProxyAccessibleBase<Derived>::Shutdown()
{
MOZ_DIAGNOSTIC_ASSERT(!IsDoc());
NS_ASSERTION(!mOuterDoc, "Why do we still have a child doc?");
xpcAccessibleDocument* xpcDoc =
GetAccService()->GetCachedXPCDocument(Document());
if (xpcDoc) {
@ -34,15 +33,16 @@ ProxyAccessibleBase<Derived>::Shutdown()
// XXX Ideally this wouldn't be necessary, but it seems OuterDoc accessibles
// can be destroyed before the doc they own.
uint32_t childCount = mChildren.Length();
if (!mOuterDoc) {
uint32_t childCount = mChildren.Length();
for (uint32_t idx = 0; idx < childCount; idx++)
mChildren[idx]->Shutdown();
} else {
if (mChildren.Length() != 1)
MOZ_CRASH("outer doc doesn't own adoc!");
mChildren[0]->AsDoc()->Unbind();
if (childCount > 1) {
MOZ_CRASH("outer doc has too many documents!");
} else if (childCount == 1) {
mChildren[0]->AsDoc()->Unbind();
}
}
mChildren.Clear();
@ -76,9 +76,7 @@ ProxyAccessibleBase<Derived>::ClearChildDoc(DocAccessibleParent* aChildDoc)
// in SetChildDoc(). This could result in two subsequent calls to
// ClearChildDoc() even though mChildren.Length() == 1.
MOZ_ASSERT(mChildren.Length() <= 1);
if (mChildren.RemoveElement(aChildDoc)) {
mOuterDoc = false;
}
mChildren.RemoveElement(aChildDoc);
}
template <class Derived>

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

@ -24,9 +24,9 @@
"size": 12072532
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 132388656,
"digest": "46c2c5d048b6d9e9bbb163277a1ae8a3c37727ad8aa8dd661c2b95e2b68e4e85b20ae4fa0eadf3a37a3c09801964fd7107151909ad07c3a7b2fbf805c7035d97",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 110076708,
"digest": "2c865f12279b103e8861071e05480cd8aeb9c4e4cd63eea1b8ca50fb92880583bebd27a3af6a86b3f12b9ee89e70839140f061ab829fcceca5e85dc8bc428ec3",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

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

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 132388656,
"digest": "46c2c5d048b6d9e9bbb163277a1ae8a3c37727ad8aa8dd661c2b95e2b68e4e85b20ae4fa0eadf3a37a3c09801964fd7107151909ad07c3a7b2fbf805c7035d97",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 110076708,
"digest": "2c865f12279b103e8861071e05480cd8aeb9c4e4cd63eea1b8ca50fb92880583bebd27a3af6a86b3f12b9ee89e70839140f061ab829fcceca5e85dc8bc428ec3",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

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

@ -2143,12 +2143,16 @@
<versionRange maxVersion="13.0.0.302" minVersion="13.0.0.302" severity="0" vulnerabilitystatus="1"/>
</pluginItem>
<pluginItem blockID="p1495" os="Linux">
<match exp="" name="name"/>
<match exp="libflashplayer\.so" name="filename"/>
<match exp="" name="description"/>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
<versionRange maxVersion="24.0.0.186" minVersion="23.0.0.207" severity="0" vulnerabilitystatus="1"/>
</pluginItem>
<pluginItem blockID="p1420">
<pluginItem blockID="p1420" os="">
<match exp="" name="name"/>
<match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
<match exp="" name="description"/>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
<versionRange maxVersion="23.0.0.205" minVersion="23.0.0.185" severity="0" vulnerabilitystatus="1"/>
</pluginItem>
@ -2202,7 +2206,9 @@
<versionRange maxVersion="*" minVersion="0"/>
</pluginItem>
<pluginItem blockID="p1419" os="Linux">
<match exp="" name="name"/>
<match exp="libflashplayer\.so" name="filename"/>
<match exp="" name="description"/>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
<versionRange maxVersion="11.2.202.643" minVersion="11.2.202.637" severity="0" vulnerabilitystatus="1"/>
</pluginItem>
@ -2459,8 +2465,10 @@
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
<versionRange maxVersion="18.0.0.232" minVersion="18.0.0.204" severity="0" vulnerabilitystatus="1"/>
</pluginItem>
<pluginItem blockID="p1274">
<pluginItem blockID="p1274" os="">
<match exp="" name="name"/>
<match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
<match exp="" name="description"/>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
<versionRange maxVersion="22.0.0.211" minVersion="22.0.0.192" severity="0" vulnerabilitystatus="1"/>
</pluginItem>
@ -2571,8 +2579,10 @@
</targetApplication>
</versionRange>
</pluginItem>
<pluginItem blockID="p1494">
<pluginItem blockID="p1494" os="">
<match exp="" name="name"/>
<match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
<match exp="" name="description"/>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
<versionRange maxVersion="24.0.0.186" minVersion="23.0.0.207" severity="0" vulnerabilitystatus="1"/>
</pluginItem>
@ -2640,8 +2650,10 @@
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
<versionRange maxVersion="18.0.0.352" minVersion="18.0.0.343" severity="0" vulnerabilitystatus="1"/>
</pluginItem>
<pluginItem blockID="p160">
<pluginItem blockID="p160" os="">
<match exp="" name="name"/>
<match exp="NPSWF32\.dll" name="filename"/>
<match exp="" name="description"/>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
<versionRange maxVersion="10.2.9999" minVersion="0" severity="0" vulnerabilitystatus="1">
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
@ -2810,7 +2822,9 @@
<match exp="NPFFAddOn.dll" name="filename"/>
</pluginItem>
<pluginItem blockID="p1421" os="Linux">
<match exp="" name="name"/>
<match exp="libflashplayer\.so" name="filename"/>
<match exp="" name="description"/>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
<versionRange maxVersion="23.0.0.207" minVersion="11.2.202.643" severity="0" vulnerabilitystatus="1"/>
</pluginItem>
@ -3006,8 +3020,10 @@
<infoURL>https://get.adobe.com/reader</infoURL>
<versionRange maxVersion="15.006.30174" minVersion="15.006.30174" severity="0" vulnerabilitystatus="1"/>
</pluginItem>
<pluginItem blockID="p1422">
<pluginItem blockID="p1422" os="">
<match exp="" name="name"/>
<match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
<match exp="" name="description"/>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
<versionRange maxVersion="23.0.0.207" minVersion="23.0.0.205" severity="0" vulnerabilitystatus="1"/>
</pluginItem>

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

@ -31,24 +31,6 @@ var SidebarUI = {
this.browser = document.getElementById("sidebar");
this._title = document.getElementById("sidebar-title");
this._splitter = document.getElementById("sidebar-splitter");
if (!this.adoptFromWindow(window.opener)) {
let commandID = this._box.getAttribute("sidebarcommand");
if (commandID) {
let command = document.getElementById(commandID);
if (command) {
this._delayedLoad = true;
this._box.hidden = false;
this._splitter.hidden = false;
command.setAttribute("checked", "true");
} else {
// Remove the |sidebarcommand| attribute, because the element it
// refers to no longer exists, so we should assume this sidebar
// panel has been uninstalled. (249883)
this._box.removeAttribute("sidebarcommand");
}
}
}
},
uninit() {
@ -69,14 +51,6 @@ var SidebarUI = {
* initialize the state itself.
*/
adoptFromWindow(sourceWindow) {
// No source window, or it being closed, or not chrome, or in a different
// private-browsing context means we can't adopt.
if (!sourceWindow || sourceWindow.closed ||
!sourceWindow.document.documentURIObject.schemeIs("chrome") ||
PrivateBrowsingUtils.isWindowPrivate(window) != PrivateBrowsingUtils.isWindowPrivate(sourceWindow)) {
return false;
}
// If the opener had a sidebar, open the same sidebar in our window.
// The opener can be the hidden window too, if we're coming from the state
// where no windows are open, and the hidden window has no sidebar box.
@ -108,23 +82,55 @@ var SidebarUI = {
// the <browser id="sidebar">. This lets us delay the actual load until
// delayedStartup().
this._box.setAttribute("src", sourceUI.browser.getAttribute("src"));
this._delayedLoad = true;
this._box.hidden = false;
this._splitter.hidden = false;
commandElem.setAttribute("checked", "true");
this.browser.setAttribute("src", this._box.getAttribute("src"));
return true;
},
windowPrivacyMatches(w1, w2) {
return PrivateBrowsingUtils.isWindowPrivate(w1) === PrivateBrowsingUtils.isWindowPrivate(w2);
},
/**
* If loading a sidebar was delayed on startup, start the load now.
*/
startDelayedLoad() {
if (!this._delayedLoad) {
let sourceWindow = window.opener;
// No source window means this is the initial window. If we're being
// opened from another window, check that it is one we might open a sidebar
// for.
if (sourceWindow) {
if (sourceWindow.closed || sourceWindow.location.protocol != "chrome:" ||
!this.windowPrivacyMatches(sourceWindow, window)) {
return;
}
// Try to adopt the sidebar state from the source window
if (this.adoptFromWindow(sourceWindow)) {
return;
}
}
// If we're not adopting settings from a parent window, set them now.
let commandID = this._box.getAttribute("sidebarcommand");
if (!commandID) {
return;
}
this.browser.setAttribute("src", this._box.getAttribute("src"));
let command = document.getElementById(commandID);
if (command) {
this._box.hidden = false;
this._splitter.hidden = false;
command.setAttribute("checked", "true");
this.browser.setAttribute("src", this._box.getAttribute("src"));
} else {
// Remove the |sidebarcommand| attribute, because the element it
// refers to no longer exists, so we should assume this sidebar
// panel has been uninstalled. (249883)
this._box.removeAttribute("sidebarcommand");
}
},
/**
@ -235,8 +241,7 @@ var SidebarUI = {
this._box.setAttribute("src", url);
if (this.browser.contentDocument.location.href != url) {
let onLoad = event => {
this.browser.removeEventListener("load", onLoad, true);
this.browser.addEventListener("load", event => {
// We're handling the 'load' event before it bubbles up to the usual
// (non-capturing) event handlers. Let it bubble up before firing the
@ -247,9 +252,7 @@ var SidebarUI = {
sidebarOnLoad(event);
resolve();
};
this.browser.addEventListener("load", onLoad, true);
}, {capture: true, once: true});
} else {
// Older code handled this case, so we do it too.
this._fireFocusedEvent();
@ -303,7 +306,7 @@ var SidebarUI = {
};
/**
* This exists for backards compatibility - it will be called once a sidebar is
* This exists for backwards compatibility - it will be called once a sidebar is
* ready, following any request to show it.
*
* @deprecated
@ -311,7 +314,7 @@ var SidebarUI = {
function fireSidebarFocusedEvent() {}
/**
* This exists for backards compatibility - it gets called when a sidebar has
* This exists for backwards compatibility - it gets called when a sidebar has
* been loaded.
*
* @deprecated
@ -319,9 +322,9 @@ function fireSidebarFocusedEvent() {}
function sidebarOnLoad(event) {}
/**
* This exists for backards compatibility, and is equivilent to
* This exists for backwards compatibility, and is equivilent to
* SidebarUI.toggle() without the forceOpen param. With forceOpen set to true,
* it is equalivent to SidebarUI.show().
* it is equivalent to SidebarUI.show().
*
* @deprecated
*/

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

@ -324,6 +324,10 @@ toolbarpaletteitem > toolbaritem[sdkstylewidget="true"][cui-areatype="toolbar"]
.webextension-page-action {
list-style-image: var(--webextension-urlbar-image);
}
.webextension-menuitem {
list-style-image: var(--webextension-menuitem-image);
}
}
@media (min-resolution: 1.1dppx) {
@ -339,6 +343,10 @@ toolbarpaletteitem > toolbaritem[sdkstylewidget="true"][cui-areatype="toolbar"]
.webextension-page-action {
list-style-image: var(--webextension-urlbar-image-2x);
}
.webextension-menuitem {
list-style-image: var(--webextension-menuitem-image-2x);
}
}
toolbarpaletteitem[removable="false"] {

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

@ -1285,8 +1285,6 @@ var gBrowserInit = {
PanelUI.init();
LightweightThemeListener.init();
SidebarUI.startDelayedLoad();
UpdateUrlbarSearchSplitterState();
if (!(isBlankPageURL(uriToLoad) || uriToLoad == "about:privatebrowsing") ||
@ -1460,6 +1458,7 @@ var gBrowserInit = {
// Enable the Restore Last Session command if needed
RestoreLastSessionObserver.init();
SidebarUI.startDelayedLoad();
SocialUI.init();
// Start monitoring slow add-ons

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

@ -1,9 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// This test checks that a <select> with an <optgroup> opens and can be navigated
// in a child process. This is different than single-process as a <menulist> is used
// to implement the dropdown list.
// This test tests <select> in a child process. This is different than
// single-process as a <menulist> is used to implement the dropdown list.
requestLongerTimeout(2);
@ -851,3 +850,35 @@ add_task(function* test_colors_applied_to_popup() {
yield hideSelectPopup(selectPopup, "escape");
yield BrowserTestUtils.removeTab(tab);
});
// This test checks that the popup is closed when the select element is blurred.
add_task(function* test_blur_hides_popup() {
const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
content.addEventListener("blur", function(event) {
event.preventDefault();
event.stopPropagation();
}, true);
content.document.getElementById("one").focus();
});
let menulist = document.getElementById("ContentSelectDropdown");
let selectPopup = menulist.menupopup;
yield openSelectPopup(selectPopup);
let popupHiddenPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
content.document.getElementById("one").blur();
});
yield popupHiddenPromise;
ok(true, "Blur closed popup");
yield BrowserTestUtils.removeTab(tab);
});

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

@ -0,0 +1,32 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* 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/. */
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionParent",
"resource://gre/modules/ExtensionParent.jsm");
function loadWebPanel() {
let sidebarURI = new URL(location);
let uri = sidebarURI.searchParams.get("panel");
let remote = sidebarURI.searchParams.get("remote");
let browser = document.getElementById("webext-panels-browser");
if (remote) {
let remoteType = E10SUtils.getRemoteTypeForURI(uri, true,
E10SUtils.EXTENSION_REMOTE_TYPE);
browser.setAttribute("remote", "true");
browser.setAttribute("remoteType", remoteType);
} else {
browser.removeAttribute("remote");
browser.removeAttribute("remoteType");
}
browser.loadURI(uri);
}
function load() {
let browser = document.getElementById("webext-panels-browser");
browser.messageManager.loadFrameScript("chrome://browser/content/content.js", true);
ExtensionParent.apiManager.emit("extension-browser-inserted", browser);
this.loadWebPanel();
}

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

@ -0,0 +1,73 @@
<?xml version="1.0"?>
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# 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/.
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
<!DOCTYPE page [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
<!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd">
%textcontextDTD;
]>
<page id="webextpanels-window"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="load()">
<script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
<script type="application/javascript" src="chrome://browser/content/browser.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-social.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-fxaccounts.js"/>
<script type="application/javascript" src="chrome://browser/content/nsContextMenu.js"/>
<script type="application/javascript" src="chrome://browser/content/webext-panels.js"/>
<stringbundleset id="stringbundleset">
<stringbundle id="bundle_browser" src="chrome://browser/locale/browser.properties"/>
</stringbundleset>
<broadcasterset id="mainBroadcasterSet">
<broadcaster id="isFrameImage"/>
</broadcasterset>
<commandset id="mainCommandset">
<command id="Browser:Back"
oncommand="getPanelBrowser().webNavigation.goBack();"
disabled="true"/>
<command id="Browser:Forward"
oncommand="getPanelBrowser().webNavigation.goForward();"
disabled="true"/>
<command id="Browser:Stop" oncommand="PanelBrowserStop();"/>
<command id="Browser:Reload" oncommand="PanelBrowserReload();"/>
</commandset>
<popupset id="mainPopupSet">
<tooltip id="aHTMLTooltip" page="true"/>
<menupopup id="contentAreaContextMenu" pagemenu="start"
onpopupshowing="if (event.target != this)
return true;
gContextMenu = new nsContextMenu(this, event.shiftKey);
if (gContextMenu.shouldDisplay)
document.popupNode = this.triggerNode;
return gContextMenu.shouldDisplay;"
onpopuphiding="if (event.target != this)
return;
gContextMenu.hiding();
gContextMenu = null;">
#include browser-context.inc
</menupopup>
</popupset>
<commandset id="editMenuCommands"/>
<browser id="webext-panels-browser"
type="content" flex="1"
webextension-view-type="sidebar"
context="contentAreaContextMenu" tooltip="aHTMLTooltip"
onclick="window.parent.contentAreaClick(event, true);"/>
</page>

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

@ -155,6 +155,8 @@ browser.jar:
content/browser/usercontext.svg (content/usercontext.svg)
content/browser/web-panels.js (content/web-panels.js)
* content/browser/web-panels.xul (content/web-panels.xul)
content/browser/webext-panels.js (content/webext-panels.js)
* content/browser/webext-panels.xul (content/webext-panels.xul)
* content/browser/baseMenuOverlay.xul (content/baseMenuOverlay.xul)
* content/browser/nsContextMenu.js (content/nsContextMenu.js)
# XXX: We should exclude this one as well (bug 71895)

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

@ -102,7 +102,7 @@ function updateCombinedWidgetStyle(aNode, aArea, aModifyCloseMenu) {
function fillSubviewFromMenuItems(aMenuItems, aSubview) {
let attrs = ["oncommand", "onclick", "label", "key", "disabled",
"command", "observes", "hidden", "class", "origin",
"image", "checked"];
"image", "checked", "style"];
let doc = aSubview.ownerDocument;
let fragment = doc.createDocumentFragment();

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

@ -12,6 +12,7 @@ module.exports = { // eslint-disable-line no-undef
"WindowEventManager": true,
"browserActionFor": true,
"getCookieStoreIdForTab": true,
"getDevToolsTargetForContext": true,
"makeWidgetId": true,
"pageActionFor": true,
"tabTracker": true,

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

@ -355,17 +355,13 @@ BrowserAction.prototype = {
}
}
// These URLs should already be properly escaped, but make doubly sure CSS
// string escape characters are escaped here, since they could lead to a
// sandbox break.
let escape = str => str.replace(/[\\\s"]/g, encodeURIComponent);
let getIcon = size => escape(IconDetails.getPreferredIcon(tabData.icon, this.extension, size).icon);
let getIcon = size => IconDetails.escapeUrl(
IconDetails.getPreferredIcon(tabData.icon, this.extension, size).icon);
node.setAttribute("style", `
--webextension-menupanel-image: url("${getIcon(32)}");
--webextension-menupanel-image-2x: url("${getIcon(64)}");
--webextension-toolbar-image: url("${escape(icon)}");
--webextension-toolbar-image: url("${IconDetails.escapeUrl(icon)}");
--webextension-toolbar-image-2x: url("${getIcon(baseSize * 2)}");
`);
},

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

@ -0,0 +1,35 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
const {
SingletonEventManager,
} = ExtensionUtils;
extensions.registerSchemaAPI("devtools.network", "devtools_parent", (context) => {
return {
devtools: {
network: {
onNavigated: new SingletonEventManager(context, "devtools.onNavigated", fire => {
let listener = (event, data) => {
fire.async(data.url);
};
let targetPromise = getDevToolsTargetForContext(context);
targetPromise.then(target => {
target.on("will-navigate", listener);
});
return () => {
targetPromise.then(target => {
target.off("will-navigate", listener);
});
};
}).api(),
},
},
};
});

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

@ -0,0 +1,349 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
let {
ExtensionError,
IconDetails,
} = ExtensionUtils;
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
// WeakMap[Extension -> SidebarAction]
let sidebarActionMap = new WeakMap();
const sidebarURL = "chrome://browser/content/webext-panels.xul";
/**
* Responsible for the sidebar_action section of the manifest as well
* as the associated sidebar browser.
*/
class SidebarAction {
constructor(options, extension) {
this.extension = extension;
// Add the extension to the sidebar menu. The sidebar widget will copy
// from that when it is viewed, so we shouldn't need to update that.
let widgetId = makeWidgetId(extension.id);
this.id = `${widgetId}-sidebar-action`;
this.menuId = `menu_${this.id}`;
this.defaults = {
enabled: true,
title: options.default_title || extension.name,
icon: IconDetails.normalize({path: options.default_icon}, extension),
panel: options.default_panel || "",
};
this.tabContext = new TabContext(tab => Object.create(this.defaults),
extension);
// We need to ensure our elements are available before session restore.
this.windowOpenListener = (window) => {
this.createMenuItem(window, this.defaults);
};
windowTracker.addOpenListener(this.windowOpenListener);
}
build() {
this.tabContext.on("tab-select", // eslint-disable-line mozilla/balanced-listeners
(evt, tab) => { this.updateWindow(tab.ownerGlobal); });
let install = this.extension.startupReason === "ADDON_INSTALL";
for (let window of windowTracker.browserWindows()) {
this.updateWindow(window);
if (install) {
let {SidebarUI} = window;
SidebarUI.show(this.id);
}
}
// Bug 1331507: UX review/analysis of sidebar-button injection.
if (AppConstants.RELEASE_OR_BETA) {
return;
}
if (install && !Services.prefs.prefHasUserValue("extensions.sidebar-button.shown")) {
Services.prefs.setBoolPref("extensions.sidebar-button.shown", true);
// If the sidebar button has never been moved to the toolbar, move it now
// so the user can see/access the sidebars.
let widget = CustomizableUI.getWidget("sidebar-button");
if (!widget.areaType) {
CustomizableUI.addWidgetToArea("sidebar-button", CustomizableUI.AREA_NAVBAR, 0);
}
}
}
sidebarUrl(panel) {
if (this.extension.remote) {
return `${sidebarURL}?remote=1&panel=${encodeURIComponent(panel)}`;
}
return `${sidebarURL}?&panel=${encodeURIComponent(panel)}`;
}
createMenuItem(window, details) {
let {document} = window;
// Use of the broadcaster allows browser-sidebar.js to properly manage the
// checkmarks in the menus.
let broadcaster = document.createElementNS(XUL_NS, "broadcaster");
broadcaster.setAttribute("id", this.id);
broadcaster.setAttribute("autoCheck", "false");
broadcaster.setAttribute("type", "checkbox");
broadcaster.setAttribute("group", "sidebar");
broadcaster.setAttribute("label", details.title);
broadcaster.setAttribute("sidebarurl", this.sidebarUrl(details.panel));
// oncommand gets attached to menuitem, so we use the observes attribute to
// get the command id we pass to SidebarUI.
broadcaster.setAttribute("oncommand", "SidebarUI.toggle(this.getAttribute('observes'))");
let menuitem = document.createElementNS(XUL_NS, "menuitem");
menuitem.setAttribute("id", this.menuId);
menuitem.setAttribute("observes", this.id);
menuitem.setAttribute("class", "menuitem-iconic webextension-menuitem");
document.getElementById("mainBroadcasterSet").appendChild(broadcaster);
document.getElementById("viewSidebarMenu").appendChild(menuitem);
return menuitem;
}
/**
* Update the broadcaster and menuitem `node` with the tab context data
* in `tabData`.
*
* @param {ChromeWindow} window
* Browser chrome window.
* @param {object} tabData
* Tab specific sidebar configuration.
*/
updateButton(window, tabData) {
let {document, SidebarUI} = window;
let title = tabData.title || this.extension.name;
let menu = document.getElementById(this.menuId);
if (!menu) {
menu = this.createMenuItem(window, tabData);
}
// Update the broadcaster first, it will update both menus.
let broadcaster = document.getElementById(this.id);
broadcaster.setAttribute("tooltiptext", title);
broadcaster.setAttribute("label", title);
let url = this.sidebarUrl(tabData.panel);
let urlChanged = url !== broadcaster.getAttribute("sidebarurl");
if (urlChanged) {
broadcaster.setAttribute("sidebarurl", url);
}
let getIcon = size => IconDetails.escapeUrl(
IconDetails.getPreferredIcon(tabData.icon, this.extension, size).icon);
menu.setAttribute("style", `
--webextension-menuitem-image: url("${getIcon(16)}");
--webextension-menuitem-image-2x: url("${getIcon(32)}");
`);
// Update the sidebar if this extension is the current sidebar.
if (SidebarUI.currentID === this.id) {
SidebarUI.title = title;
if (SidebarUI.isOpen && urlChanged) {
SidebarUI.show(this.id);
}
}
}
/**
* Update the broadcaster and menuitem for a given window.
*
* @param {ChromeWindow} window
* Browser chrome window.
*/
updateWindow(window) {
let nativeTab = window.gBrowser.selectedTab;
this.updateButton(window, this.tabContext.get(nativeTab));
}
/**
* Update the broadcaster and menuitem when the extension changes the icon,
* title, url, etc. If it only changes a parameter for a single
* tab, `tab` will be that tab. Otherwise it will be null.
*
* @param {XULElement|null} nativeTab
* Browser tab, may be null.
*/
updateOnChange(nativeTab) {
if (nativeTab) {
if (nativeTab.selected) {
this.updateWindow(nativeTab.ownerGlobal);
}
} else {
for (let window of windowTracker.browserWindows()) {
this.updateWindow(window);
}
}
}
/**
* Set a default or tab specific property.
*
* @param {XULElement|null} nativeTab
* Webextension tab object, may be null.
* @param {string} prop
* String property to retrieve ["icon", "title", or "panel"].
* @param {string} value
* Value for property.
*/
setProperty(nativeTab, prop, value) {
if (nativeTab === null) {
this.defaults[prop] = value;
} else if (value !== null) {
this.tabContext.get(nativeTab)[prop] = value;
} else {
delete this.tabContext.get(nativeTab)[prop];
}
this.updateOnChange(nativeTab);
}
/**
* Retrieve a property from the tab or defaults if tab is null.
*
* @param {XULElement|null} nativeTab
* Browser tab object, may be null.
* @param {string} prop
* String property to retrieve ["icon", "title", or "panel"]
* @returns {string} value
* Value for prop.
*/
getProperty(nativeTab, prop) {
if (nativeTab === null) {
return this.defaults[prop];
}
return this.tabContext.get(nativeTab)[prop];
}
shutdown() {
this.tabContext.shutdown();
for (let window of windowTracker.browserWindows()) {
let {document, SidebarUI} = window;
if (SidebarUI.currentID === this.id) {
SidebarUI.hide();
}
let menu = document.getElementById(this.menuId);
if (menu) {
menu.remove();
}
let broadcaster = document.getElementById(this.id);
if (broadcaster) {
broadcaster.remove();
}
}
windowTracker.removeOpenListener(this.windowOpenListener);
}
}
SidebarAction.for = (extension) => {
return sidebarActionMap.get(extension);
};
global.sidebarActionFor = SidebarAction.for;
/* eslint-disable mozilla/balanced-listeners */
extensions.on("manifest_sidebar_action", (type, directive, extension, manifest) => {
let sidebarAction = new SidebarAction(manifest.sidebar_action, extension);
sidebarActionMap.set(extension, sidebarAction);
});
extensions.on("ready", (type, extension) => {
// We build sidebars during ready to ensure the background scripts are ready.
if (sidebarActionMap.has(extension)) {
sidebarActionMap.get(extension).build();
}
});
extensions.on("shutdown", (type, extension) => {
if (sidebarActionMap.has(extension)) {
// Don't remove everything on app shutdown so session restore can handle
// restoring open sidebars.
if (extension.shutdownReason !== "APP_SHUTDOWN") {
sidebarActionMap.get(extension).shutdown();
}
sidebarActionMap.delete(extension);
}
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerSchemaAPI("sidebarAction", "addon_parent", context => {
let {extension} = context;
function getTab(tabId) {
if (tabId !== null) {
return tabTracker.getTab(tabId);
}
return null;
}
return {
sidebarAction: {
async setTitle(details) {
let nativeTab = getTab(details.tabId);
let title = details.title;
// Clear the tab-specific title when given a null string.
if (nativeTab && title === "") {
title = null;
}
SidebarAction.for(extension).setProperty(nativeTab, "title", title);
},
getTitle(details) {
let nativeTab = getTab(details.tabId);
let title = SidebarAction.for(extension).getProperty(nativeTab, "title");
return Promise.resolve(title);
},
async setIcon(details) {
let nativeTab = getTab(details.tabId);
let icon = IconDetails.normalize(details, extension, context);
SidebarAction.for(extension).setProperty(nativeTab, "icon", icon);
},
async setPanel(details) {
let nativeTab = getTab(details.tabId);
let url;
// Clear the tab-specific url when given a null string.
if (nativeTab && details.panel === "") {
url = null;
} else if (details.panel !== "") {
url = context.uri.resolve(details.panel);
} else {
throw new ExtensionError("Invalid url for sidebar panel.");
}
SidebarAction.for(extension).setProperty(nativeTab, "panel", url);
},
getPanel(details) {
let nativeTab = getTab(details.tabId);
let panel = SidebarAction.for(extension).getProperty(nativeTab, "panel");
return Promise.resolve(panel);
},
},
};
});

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

@ -7,10 +7,12 @@ category webextension-scripts contextMenus chrome://browser/content/ext-contextM
category webextension-scripts desktop-runtime chrome://browser/content/ext-desktop-runtime.js
category webextension-scripts devtools chrome://browser/content/ext-devtools.js
category webextension-scripts devtools-inspectedWindow chrome://browser/content/ext-devtools-inspectedWindow.js
category webextension-scripts devtools-network chrome://browser/content/ext-devtools-network.js
category webextension-scripts history chrome://browser/content/ext-history.js
category webextension-scripts omnibox chrome://browser/content/ext-omnibox.js
category webextension-scripts pageAction chrome://browser/content/ext-pageAction.js
category webextension-scripts sessions chrome://browser/content/ext-sessions.js
category webextension-scripts sidebarAction chrome://browser/content/ext-sidebarAction.js
category webextension-scripts tabs chrome://browser/content/ext-tabs.js
category webextension-scripts theme chrome://browser/content/ext-theme.js
category webextension-scripts url-overrides chrome://browser/content/ext-url-overrides.js
@ -34,10 +36,12 @@ category webextension-schemas context_menus chrome://browser/content/schemas/con
category webextension-schemas context_menus_internal chrome://browser/content/schemas/context_menus_internal.json
category webextension-schemas devtools chrome://browser/content/schemas/devtools.json
category webextension-schemas devtools_inspected_window chrome://browser/content/schemas/devtools_inspected_window.json
category webextension-schemas devtools_network chrome://browser/content/schemas/devtools_network.json
category webextension-schemas history chrome://browser/content/schemas/history.json
category webextension-schemas omnibox chrome://browser/content/schemas/omnibox.json
category webextension-schemas page_action chrome://browser/content/schemas/page_action.json
category webextension-schemas sessions chrome://browser/content/schemas/sessions.json
category webextension-schemas sidebar_action chrome://browser/content/schemas/sidebar_action.json
category webextension-schemas tabs chrome://browser/content/schemas/tabs.json
category webextension-schemas theme chrome://browser/content/schemas/theme.json
category webextension-schemas url_overrides chrome://browser/content/schemas/url_overrides.json

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

@ -20,10 +20,12 @@ browser.jar:
content/browser/ext-desktop-runtime.js
content/browser/ext-devtools.js
content/browser/ext-devtools-inspectedWindow.js
content/browser/ext-devtools-network.js
content/browser/ext-history.js
content/browser/ext-omnibox.js
content/browser/ext-pageAction.js
content/browser/ext-sessions.js
content/browser/ext-sidebarAction.js
content/browser/ext-tabs.js
content/browser/ext-theme.js
content/browser/ext-url-overrides.js

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

@ -0,0 +1,93 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
[
{
"namespace": "devtools.network",
"allowedContexts": ["devtools", "devtools_only"],
"defaultContexts": ["devtools", "devtools_only"],
"description": "Use the <code>chrome.devtools.network</code> API to retrieve the information about network requests displayed by the Developer Tools in the Network panel.",
"types": [
{
"id": "Request",
"type": "object",
"description": "Represents a network request for a document resource (script, image and so on). See HAR Specification for reference.",
"functions": [
{
"name": "getContent",
"type": "function",
"description": "Returns content of the response body.",
"async": "callback",
"parameters": [
{
"name": "callback",
"type": "function",
"description": "A function that receives the response body when the request completes.",
"parameters": [
{
"name": "content",
"type": "string",
"description": "Content of the response body (potentially encoded)."
},
{
"name": "encoding",
"type": "string",
"description": "Empty if content is not encoded, encoding name otherwise. Currently, only base64 is supported."
}
]
}
]
}
]
}
],
"functions": [
{
"name": "getHAR",
"unsupported": true,
"type": "function",
"description": "Returns HAR log that contains all known network requests.",
"async": "callback",
"parameters": [
{
"name": "callback",
"type": "function",
"description": "A function that receives the HAR log when the request completes.",
"parameters": [
{
"name": "harLog",
"type": "object",
"additionalProperties": {"type": "any"},
"description": "A HAR log. See HAR specification for details."
}
]
}
]
}
],
"events": [
{
"name": "onRequestFinished",
"unsupported": true,
"type": "function",
"description": "Fired when a network request is finished and all request data are available.",
"parameters": [
{ "name": "request", "$ref": "Request", "description": "Description of a network request in the form of a HAR entry. See HAR specification for details." }
]
},
{
"name": "onNavigated",
"type": "function",
"description": "Fired when the inspected window navigates to a new page.",
"parameters": [
{
"name": "url",
"type": "string",
"description": "URL of the new page."
}
]
}
]
}
]

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

@ -11,10 +11,12 @@ browser.jar:
content/browser/schemas/context_menus_internal.json
content/browser/schemas/devtools.json
content/browser/schemas/devtools_inspected_window.json
content/browser/schemas/devtools_network.json
content/browser/schemas/history.json
content/browser/schemas/omnibox.json
content/browser/schemas/page_action.json
content/browser/schemas/sessions.json
content/browser/schemas/sidebar_action.json
content/browser/schemas/tabs.json
content/browser/schemas/theme.json
content/browser/schemas/url_overrides.json

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

@ -0,0 +1,183 @@
/* 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/. */
[
{
"namespace": "manifest",
"types": [
{
"$extend": "WebExtensionManifest",
"properties": {
"sidebar_action": {
"type": "object",
"additionalProperties": { "$ref": "UnrecognizedProperty" },
"properties": {
"default_title": {
"type": "string",
"optional": true,
"preprocess": "localize"
},
"default_icon": {
"$ref": "IconPath",
"optional": true
},
"default_panel": {
"type": "string",
"format": "strictRelativeUrl",
"preprocess": "localize"
}
},
"optional": true
}
}
}
]
},
{
"namespace": "sidebarAction",
"description": "Use sidebar actions to add a sidebar to Firefox.",
"permissions": ["manifest:sidebar_action"],
"types": [
{
"id": "ImageDataType",
"type": "object",
"isInstanceOf": "ImageData",
"additionalProperties": { "type": "any" },
"postprocess": "convertImageDataToURL",
"description": "Pixel data for an image. Must be an ImageData object (for example, from a <code>canvas</code> element)."
}
],
"functions": [
{
"name": "setTitle",
"type": "function",
"description": "Sets the title of the sidebar action. This shows up in the tooltip.",
"async": true,
"parameters": [
{
"name": "details",
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "The string the sidebar action should display when moused over."
},
"tabId": {
"type": "integer",
"optional": true,
"description": "Sets the sidebar title for the tab specified by tabId. Automatically resets when the tab is closed."
}
}
}
]
},
{
"name": "getTitle",
"type": "function",
"description": "Gets the title of the sidebar action.",
"async": true,
"parameters": [
{
"name": "details",
"type": "object",
"properties": {
"tabId": {
"type": "integer",
"optional": true,
"description": "Specify the tab to get the title from. If no tab is specified, the non-tab-specific title is returned."
}
}
}
]
},
{
"name": "setIcon",
"type": "function",
"description": "Sets the icon for the sidebar action. The icon can be specified either as the path to an image file or as the pixel data from a canvas element, or as dictionary of either one of those. Either the <strong>path</strong> or the <strong>imageData</strong> property must be specified.",
"async": true,
"parameters": [
{
"name": "details",
"type": "object",
"properties": {
"imageData": {
"choices": [
{ "$ref": "ImageDataType" },
{
"type": "object",
"patternProperties": {
"^[1-9]\\d*$": { "$ref": "ImageDataType" }
},
"additionalProperties": false
}
],
"optional": true,
"description": "Either an ImageData object or a dictionary {size -> ImageData} representing icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * 19 will be selected. Initially only scales 1 and 2 will be supported. At least one image must be specified. Note that 'details.imageData = foo' is equivalent to 'details.imageData = {'19': foo}'"
},
"path": {
"choices": [
{ "type": "string" },
{
"type": "object",
"additionalProperties": {"type": "string"}
}
],
"optional": true,
"description": "Either a relative image path or a dictionary {size -> relative image path} pointing to icon to be set. If the icon is specified as a dictionary, the actual image to be used is chosen depending on screen's pixel density. If the number of image pixels that fit into one screen space unit equals <code>scale</code>, then image with size <code>scale</code> * 19 will be selected. Initially only scales 1 and 2 will be supported. At least one image must be specified. Note that 'details.path = foo' is equivalent to 'details.imageData = {'19': foo}'"
},
"tabId": {
"type": "integer",
"optional": true,
"description": "Sets the sidebar icon for the tab specified by tabId. Automatically resets when the tab is closed."
}
}
}
]
},
{
"name": "setPanel",
"type": "function",
"description": "Sets the url to the html document to be opened in the sidebar when the user clicks on the sidebar action's icon.",
"async": true,
"parameters": [
{
"name": "details",
"type": "object",
"properties": {
"tabId": {
"type": "integer",
"optional": true,
"minimum": 0,
"description": "Sets the sidebar url for the tab specified by tabId. Automatically resets when the tab is closed."
},
"panel": {
"type": "string",
"description": "The url to the html file to show in a sidebar. If set to the empty string (''), no sidebar is shown."
}
}
}
]
},
{
"name": "getPanel",
"type": "function",
"description": "Gets the url to the html document set as the panel for this sidebar action.",
"async": true,
"parameters": [
{
"name": "details",
"type": "object",
"properties": {
"tabId": {
"type": "integer",
"optional": true,
"description": "Specify the tab to get the sidebar from. If no tab is specified, the non-tab-specific sidebar is returned."
}
}
}
]
}
]
}
]

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

@ -52,6 +52,7 @@ support-files =
[browser_ext_currentWindow.js]
[browser_ext_devtools_inspectedWindow.js]
[browser_ext_devtools_inspectedWindow_reload.js]
[browser_ext_devtools_network.js]
[browser_ext_devtools_page.js]
[browser_ext_getViews.js]
[browser_ext_incognito_views.js]
@ -76,6 +77,8 @@ support-files =
[browser_ext_sessions_getRecentlyClosed_private.js]
[browser_ext_sessions_getRecentlyClosed_tabs.js]
[browser_ext_sessions_restore.js]
[browser_ext_sidebarAction.js]
[browser_ext_sidebarAction_context.js]
[browser_ext_simple.js]
[browser_ext_tab_runtimeConnect.js]
[browser_ext_tabs_audio.js]

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

@ -0,0 +1,94 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "devtools",
"resource://devtools/shared/Loader.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
"resource://devtools/client/framework/gDevTools.jsm");
add_task(async function test_devtools_network_on_navigated() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
function background() {
browser.test.onMessage.addListener(msg => {
let code;
if (msg === "navigate") {
code = "window.wrappedJSObject.location.href = 'http://example.com/';";
browser.tabs.executeScript({code});
} else if (msg === "reload") {
code = "window.wrappedJSObject.location.reload(true);";
browser.tabs.executeScript({code});
}
});
browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === "complete" && tab.url === "http://example.com/") {
browser.test.sendMessage("tabUpdated");
}
});
browser.test.sendMessage("ready");
}
function devtools_page() {
let eventCount = 0;
let listener = url => {
eventCount++;
browser.test.assertEq("http://example.com/", url, "onNavigated received the expected url.");
if (eventCount === 2) {
browser.devtools.network.onNavigated.removeListener(listener);
}
browser.test.sendMessage("onNavigatedFired", eventCount);
};
browser.devtools.network.onNavigated.addListener(listener);
}
let extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
permissions: ["tabs", "http://mochi.test/", "http://example.com/"],
devtools_page: "devtools_page.html",
},
files: {
"devtools_page.html": `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="devtools_page.js"></script>
</head>
<body>
</body>
</html>`,
"devtools_page.js": devtools_page,
},
});
await extension.startup();
await extension.awaitMessage("ready");
let target = devtools.TargetFactory.forTab(tab);
await gDevTools.showToolbox(target, "webconsole");
info("Developer toolbox opened.");
extension.sendMessage("navigate");
await extension.awaitMessage("tabUpdated");
let eventCount = await extension.awaitMessage("onNavigatedFired");
is(eventCount, 1, "The expected number of events were fired.");
extension.sendMessage("reload");
await extension.awaitMessage("tabUpdated");
eventCount = await extension.awaitMessage("onNavigatedFired");
is(eventCount, 2, "The expected number of events were fired.");
// do a reload after the listener has been removed, do not expect a message to be sent
extension.sendMessage("reload");
await extension.awaitMessage("tabUpdated");
await gDevTools.closeToolbox(target);
await target.destroy();
await extension.unload();
await BrowserTestUtils.removeTab(tab);
});

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

@ -0,0 +1,122 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
let extData = {
manifest: {
"sidebar_action": {
"default_panel": "sidebar.html",
},
},
useAddonManager: "temporary",
files: {
"sidebar.html": `
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"/>
<script src="sidebar.js"></script>
</head>
<body>
A Test Sidebar
</body></html>
`,
"sidebar.js": function() {
window.onload = () => {
browser.test.sendMessage("sidebar");
};
},
},
background: function() {
browser.test.onMessage.addListener(msg => {
if (msg === "set-panel") {
browser.sidebarAction.setPanel({panel: ""}).then(() => {
browser.test.notifyFail("empty panel settable");
}).catch(() => {
browser.test.notifyPass("unable to set empty panel");
});
}
});
},
};
add_task(function* sidebar_initial_install() {
ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
// Test sidebar is opened on install
yield extension.awaitMessage("sidebar");
ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible");
// Test toolbar button is available
ok(document.getElementById("sidebar-button"), "sidebar button is in UI");
yield extension.unload();
// Test that the sidebar was closed on unload.
ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
// Move toolbar button back to customization.
CustomizableUI.removeWidgetFromArea("sidebar-button", CustomizableUI.AREA_NAVBAR);
ok(!document.getElementById("sidebar-button"), "sidebar button is not in UI");
});
add_task(function* sidebar_two_sidebar_addons() {
let extension2 = ExtensionTestUtils.loadExtension(extData);
yield extension2.startup();
// Test sidebar is opened on install
yield extension2.awaitMessage("sidebar");
ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible");
// Test toolbar button is NOT available after first install
ok(!document.getElementById("sidebar-button"), "sidebar button is not in UI");
// Test second sidebar install opens new sidebar
let extension3 = ExtensionTestUtils.loadExtension(extData);
yield extension3.startup();
// Test sidebar is opened on install
yield extension3.awaitMessage("sidebar");
ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible");
yield extension3.unload();
// We just close the sidebar on uninstall of the current sidebar.
ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
yield extension2.unload();
});
add_task(function* sidebar_windows() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
// Test sidebar is opened on install
yield extension.awaitMessage("sidebar");
ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible in first window");
let secondSidebar = extension.awaitMessage("sidebar");
// SidebarUI relies on window.opener being set, which is normal behavior when
// using menu or key commands to open a new browser window.
let win = yield BrowserTestUtils.openNewBrowserWindow({opener: window});
yield secondSidebar;
ok(!win.document.getElementById("sidebar-box").hidden, "sidebar box is visible in second window");
yield extension.unload();
yield BrowserTestUtils.closeWindow(win);
});
add_task(function* sidebar_empty_panel() {
let extension = ExtensionTestUtils.loadExtension(extData);
yield extension.startup();
// Test sidebar is opened on install
yield extension.awaitMessage("sidebar");
ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible in first window");
extension.sendMessage("set-panel");
yield extension.awaitFinish();
yield extension.unload();
});
add_task(function* cleanup() {
// This is set on initial sidebar install.
Services.prefs.clearUserPref("extensions.sidebar-button.shown");
});

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

@ -0,0 +1,381 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
SpecialPowers.pushPrefEnv({
// Ignore toolbarbutton stuff, other test covers it.
set: [["extensions.sidebar-button.shown", true]],
});
function* runTests(options) {
async function background(getTests) {
async function checkDetails(expecting, tabId) {
let title = await browser.sidebarAction.getTitle({tabId});
browser.test.assertEq(expecting.title, title,
"expected value from getTitle");
let panel = await browser.sidebarAction.getPanel({tabId});
browser.test.assertEq(expecting.panel, panel,
"expected value from getPanel");
}
let expectDefaults = expecting => {
return checkDetails(expecting);
};
let tabs = [];
let tests = getTests(tabs, expectDefaults);
{
let tabId = 0xdeadbeef;
let calls = [
() => browser.sidebarAction.setTitle({tabId, title: "foo"}),
() => browser.sidebarAction.setIcon({tabId, path: "foo.png"}),
() => browser.sidebarAction.setPanel({tabId, panel: "foo.html"}),
];
for (let call of calls) {
await browser.test.assertRejects(
new Promise(resolve => resolve(call())),
RegExp(`Invalid tab ID: ${tabId}`),
"Expected invalid tab ID error");
}
}
// Runs the next test in the `tests` array, checks the results,
// and passes control back to the outer test scope.
function nextTest() {
let test = tests.shift();
test(async expecting => {
// Check that the API returns the expected values, and then
// run the next test.
let tabs = await browser.tabs.query({active: true, currentWindow: true});
await checkDetails(expecting, tabs[0].id);
// Check that the actual icon has the expected values, then
// run the next test.
browser.test.sendMessage("nextTest", expecting, tests.length);
});
}
browser.test.onMessage.addListener((msg) => {
if (msg != "runNextTest") {
browser.test.fail("Expecting 'runNextTest' message");
}
nextTest();
});
browser.tabs.query({active: true, currentWindow: true}, resultTabs => {
tabs[0] = resultTabs[0].id;
});
}
let extension = ExtensionTestUtils.loadExtension({
manifest: options.manifest,
useAddonManager: "temporary",
files: options.files || {},
background: `(${background})(${options.getTests})`,
});
let sidebarActionId;
function checkDetails(details) {
if (!sidebarActionId) {
sidebarActionId = `${makeWidgetId(extension.id)}-sidebar-action`;
}
let command = document.getElementById(sidebarActionId);
ok(command, "command exists");
let menuId = `menu_${sidebarActionId}`;
let menu = document.getElementById(menuId);
ok(menu, "menu exists");
let title = details.title || options.manifest.name;
is(getListStyleImage(menu), details.icon, "icon URL is correct");
is(menu.getAttribute("label"), title, "image label is correct");
}
let awaitFinish = new Promise(resolve => {
extension.onMessage("nextTest", (expecting, testsRemaining) => {
checkDetails(expecting);
if (testsRemaining) {
extension.sendMessage("runNextTest");
} else {
resolve();
}
});
});
// Wait for initial sidebar load to start tests.
SidebarUI.browser.addEventListener("load", event => {
extension.sendMessage("runNextTest");
}, {capture: true, once: true});
yield extension.startup();
yield awaitFinish;
yield extension.unload();
}
let sidebar = `
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"/></head>
<body>
A Test Sidebar
</body></html>
`;
add_task(function* testTabSwitchContext() {
yield runTests({
manifest: {
"sidebar_action": {
"default_icon": "default.png",
"default_panel": "__MSG_panel__",
"default_title": "Default __MSG_title__",
},
"default_locale": "en",
"permissions": ["tabs"],
},
"files": {
"default.html": sidebar,
"default-2.html": sidebar,
"2.html": sidebar,
"_locales/en/messages.json": {
"panel": {
"message": "default.html",
"description": "Panel",
},
"title": {
"message": "Title",
"description": "Title",
},
},
"default.png": imageBuffer,
"default-2.png": imageBuffer,
"1.png": imageBuffer,
"2.png": imageBuffer,
},
getTests(tabs, expectDefaults) {
let details = [
{"icon": browser.runtime.getURL("default.png"),
"panel": browser.runtime.getURL("default.html"),
"title": "Default Title",
},
{"icon": browser.runtime.getURL("1.png"),
"panel": browser.runtime.getURL("default.html"),
"title": "Default Title",
},
{"icon": browser.runtime.getURL("2.png"),
"panel": browser.runtime.getURL("2.html"),
"title": "Title 2",
},
{"icon": browser.runtime.getURL("1.png"),
"panel": browser.runtime.getURL("default-2.html"),
"title": "Default Title 2",
},
{"icon": browser.runtime.getURL("1.png"),
"panel": browser.runtime.getURL("default-2.html"),
"title": "Default Title 2",
},
{"icon": browser.runtime.getURL("default-2.png"),
"panel": browser.runtime.getURL("default-2.html"),
"title": "Default Title 2",
},
{"icon": browser.runtime.getURL("1.png"),
"panel": browser.runtime.getURL("2.html"),
"title": "Default Title 2",
},
];
return [
async expect => {
browser.test.log("Initial state, expect default properties.");
await expectDefaults(details[0]);
expect(details[0]);
},
async expect => {
browser.test.log("Change the icon in the current tab. Expect default properties excluding the icon.");
await browser.sidebarAction.setIcon({tabId: tabs[0], path: "1.png"});
await expectDefaults(details[0]);
expect(details[1]);
},
async expect => {
browser.test.log("Create a new tab. Expect default properties.");
let tab = await browser.tabs.create({active: true, url: "about:blank?0"});
tabs.push(tab.id);
await expectDefaults(details[0]);
expect(details[0]);
},
async expect => {
browser.test.log("Change properties. Expect new properties.");
let tabId = tabs[1];
await Promise.all([
browser.sidebarAction.setIcon({tabId, path: "2.png"}),
browser.sidebarAction.setPanel({tabId, panel: "2.html"}),
browser.sidebarAction.setTitle({tabId, title: "Title 2"}),
]);
await expectDefaults(details[0]);
expect(details[2]);
},
expect => {
browser.test.log("Navigate to a new page. Expect no changes.");
// TODO: This listener should not be necessary, but the |tabs.update|
// callback currently fires too early in e10s windows.
browser.tabs.onUpdated.addListener(function listener(tabId, changed) {
if (tabId == tabs[1] && changed.url) {
browser.tabs.onUpdated.removeListener(listener);
expect(details[2]);
}
});
browser.tabs.update(tabs[1], {url: "about:blank?1"});
},
async expect => {
browser.test.log("Switch back to the first tab. Expect previously set properties.");
await browser.tabs.update(tabs[0], {active: true});
expect(details[1]);
},
async expect => {
browser.test.log("Change default values, expect those changes reflected.");
await Promise.all([
browser.sidebarAction.setIcon({path: "default-2.png"}),
browser.sidebarAction.setPanel({panel: "default-2.html"}),
browser.sidebarAction.setTitle({title: "Default Title 2"}),
]);
await expectDefaults(details[3]);
expect(details[3]);
},
async expect => {
browser.test.log("Switch back to tab 2. Expect former value, unaffected by changes to defaults in previous step.");
await browser.tabs.update(tabs[1], {active: true});
await expectDefaults(details[3]);
expect(details[2]);
},
async expect => {
browser.test.log("Delete tab, switch back to tab 1. Expect previous results again.");
await browser.tabs.remove(tabs[1]);
expect(details[4]);
},
async expect => {
browser.test.log("Create a new tab. Expect new default properties.");
let tab = await browser.tabs.create({active: true, url: "about:blank?2"});
tabs.push(tab.id);
expect(details[5]);
},
async expect => {
browser.test.log("Delete tab.");
await browser.tabs.remove(tabs[2]);
expect(details[4]);
},
async expect => {
browser.test.log("Change tab panel.");
let tabId = tabs[0];
await browser.sidebarAction.setPanel({tabId, panel: "2.html"});
expect(details[6]);
},
async expect => {
browser.test.log("Revert tab panel.");
let tabId = tabs[0];
await browser.sidebarAction.setPanel({tabId, panel: ""});
expect(details[4]);
},
];
},
});
});
add_task(function* testDefaultTitle() {
yield runTests({
manifest: {
"name": "Foo Extension",
"sidebar_action": {
"default_icon": "icon.png",
"default_panel": "sidebar.html",
},
"permissions": ["tabs"],
},
files: {
"sidebar.html": sidebar,
"icon.png": imageBuffer,
},
getTests(tabs, expectDefaults) {
let details = [
{"title": "Foo Extension",
"panel": browser.runtime.getURL("sidebar.html"),
"icon": browser.runtime.getURL("icon.png")},
{"title": "Foo Title",
"panel": browser.runtime.getURL("sidebar.html"),
"icon": browser.runtime.getURL("icon.png")},
{"title": "Bar Title",
"panel": browser.runtime.getURL("sidebar.html"),
"icon": browser.runtime.getURL("icon.png")},
{"title": "",
"panel": browser.runtime.getURL("sidebar.html"),
"icon": browser.runtime.getURL("icon.png")},
];
return [
async expect => {
browser.test.log("Initial state. Expect extension title as default title.");
await expectDefaults(details[0]);
expect(details[0]);
},
async expect => {
browser.test.log("Change the title. Expect new title.");
browser.sidebarAction.setTitle({tabId: tabs[0], title: "Foo Title"});
await expectDefaults(details[0]);
expect(details[1]);
},
async expect => {
browser.test.log("Change the default. Expect same properties.");
browser.sidebarAction.setTitle({title: "Bar Title"});
await expectDefaults(details[2]);
expect(details[1]);
},
async expect => {
browser.test.log("Clear the title. Expect new default title.");
browser.sidebarAction.setTitle({tabId: tabs[0], title: ""});
await expectDefaults(details[2]);
expect(details[2]);
},
async expect => {
browser.test.log("Set default title to null string. Expect null string from API, extension title in UI.");
browser.sidebarAction.setTitle({title: ""});
await expectDefaults(details[3]);
expect(details[3]);
},
];
},
});
});

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

@ -514,7 +514,7 @@ PlacesTreeView.prototype = {
const locale = Cc["@mozilla.org/chrome/chrome-registry;1"]
.getService(Ci.nsIXULChromeRegistry)
.getSelectedLocale("global", true);
const dtOptions = { year: "2-digit", month: "numeric", day: "numeric",
const dtOptions = { year: "numeric", month: "numeric", day: "numeric",
hour: "numeric", minute: "numeric" };
this.__dateFormatter = new Intl.DateTimeFormat(locale, dtOptions);
}

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

@ -127,7 +127,7 @@
case "date":
let timeObj = new Date(node.time / 1000);
// Default is short date format.
let dtOptions = { year: '2-digit', month: 'numeric', day: 'numeric',
let dtOptions = { year: 'numeric', month: 'numeric', day: 'numeric',
hour: 'numeric', minute: 'numeric' };
// For today's visits we don't show date portion.
if (node.uri == "http://at.midnight.com/" ||

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

@ -10,32 +10,9 @@
function test() {
waitForExplicitFinish();
let { utils: Cu } = Components;
let { Promise: { defer } } = Cu.import("resource://gre/modules/Promise.jsm", {});
// opens a sidebar
function openSidebar(win) {
let { promise, resolve } = defer();
let doc = win.document;
let sidebarID = 'viewBookmarksSidebar';
let sidebar = doc.getElementById('sidebar');
let sidebarurl = doc.getElementById(sidebarID).getAttribute('sidebarurl');
sidebar.addEventListener('load', function onSidebarLoad() {
if (sidebar.contentWindow.location.href != sidebarurl)
return;
sidebar.removeEventListener('load', onSidebarLoad, true);
resolve(win);
}, true);
win.SidebarUI.show(sidebarID);
return promise;
return win.SidebarUI.show("viewBookmarksSidebar").then(() => win);
}
let windowCache = [];

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

@ -16,17 +16,10 @@ function whenNewWindowLoaded(aOptions, aCallback) {
return win;
}
function openWindow(aParent, aOptions, a3) {
let { Promise: { defer } } = Components.utils.import("resource://gre/modules/Promise.jsm", {});
let { promise, resolve } = defer();
function openWindow(aParent, aOptions) {
let win = aParent.OpenBrowserWindow(aOptions);
win.addEventListener("load", function() {
resolve(win);
}, {once: true});
return promise;
return TestUtils.topicObserved("browser-delayed-startup-finished",
subject => subject == win).then(() => win);
}
function newDirectory() {

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

@ -67,6 +67,8 @@
#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
#define APP_REG_NAME_BASE L"Firefox-"
using mozilla::IsWin8OrLater;
using namespace mozilla;
using namespace mozilla::gfx;
@ -91,152 +93,6 @@ OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey)
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
// Default Browser Registry Settings
//
// The setting of these values are made by an external binary since writing
// these values may require elevation.
//
// To allow multiple installations to coexist, identifiers written to the
// registry include a hash of the installation path. This is referred to as
// <PathHash> in the tables below.
//
// - File Extension Mappings
// -----------------------
// The following file extensions:
// .htm .html .shtml .xht .xhtml
// are mapped like so:
//
// HKCU\SOFTWARE\Classes\.<ext>\ (default) REG_SZ FirefoxHTML-<PathHash>
//
// as aliases to the class:
//
// HKCU\SOFTWARE\Classes\FirefoxHTML-<PathHash>\
// DefaultIcon (default) REG_SZ <apppath>,1
// shell\open\command (default) REG_SZ <apppath> -osint -url "%1"
// shell\open\ddeexec (default) REG_SZ <empty string>
//
// - Windows Vista and above Protocol Handler
//
// HKCU\SOFTWARE\Classes\FirefoxURL-<PathHash>\
// (default) REG_SZ <appname> URL
// EditFlags REG_DWORD 2
// FriendlyTypeName REG_SZ <appname> URL
// DefaultIcon (default) REG_SZ <apppath>,1
// shell\open\command (default) REG_SZ <apppath> -osint -url "%1"
// shell\open\ddeexec (default) REG_SZ <empty string>
//
// - Protocol Mappings
// -----------------
// The following protocols:
// HTTP, HTTPS, FTP
// are mapped like so:
//
// HKCU\SOFTWARE\Classes\<protocol>\
// DefaultIcon (default) REG_SZ <apppath>,1
// shell\open\command (default) REG_SZ <apppath> -osint -url "%1"
// shell\open\ddeexec (default) REG_SZ <empty string>
//
// - Windows Start Menu (XP SP1 and newer)
// -------------------------------------------------
// The following keys are set to make Firefox appear in the Start Menu as the
// browser:
//
// HKCU\SOFTWARE\Clients\StartMenuInternet\<appname>-<PathHash>\
// (default) REG_SZ <appname>
// DefaultIcon (default) REG_SZ <apppath>,0
// InstallInfo HideIconsCommand REG_SZ <uninstpath> /HideShortcuts
// InstallInfo IconsVisible REG_DWORD 1
// InstallInfo ReinstallCommand REG_SZ <uninstpath> /SetAsDefaultAppGlobal
// InstallInfo ShowIconsCommand REG_SZ <uninstpath> /ShowShortcuts
// shell\open\command (default) REG_SZ <apppath>
// shell\properties (default) REG_SZ <appname> &Options
// shell\properties\command (default) REG_SZ <apppath> -preferences
// shell\safemode (default) REG_SZ <appname> &Safe Mode
// shell\safemode\command (default) REG_SZ <apppath> -safe-mode
//
// - RegisteredApplications
// -------------------------------------------------
// This entry creates the listing in Default Apps for Windows 8 and up and in
// Set Program Access and Defaults (SPAD) on older versions.
//
// HKCU\Software\RegisteredApplications\
// Firefox-<PathHash> REG_SZ "Software\Clients\StartMenuInternet\<appname>-<PathHash>\Capabilities"
// HKCU\Software\Clients\StartMenuInternet\<appname>-<PathHash>\Capabilities\
// ApplicationDescription REG_SZ <branding description>
// ApplicationIcon REG_SZ <apppath>,0
// ApplicationName REG_SZ <appname>
// FileAssociations .htm REG_SZ FirefoxHTML-<PathHash>
// FileAssociations .html REG_SZ FirefoxHTML-<PathHash>
// FileAssociations .shtml REG_SZ FirefoxHTML-<PathHash>
// FileAssociations .xht REG_SZ FirefoxHTML-<PathHash>
// FileAssociations .xhtml REG_SZ FirefoxHTML-<PathHash>
// StartMenu StartMenuInternet REG_SZ <appname>-<PathHash>
// URLAssociations ftp REG_SZ FirefoxURL-<PathHash>
// URLAssociations http REG_SZ FirefoxURL-<PathHash>
// URLAssociations https REG_SZ FirefoxURL-<PathHash>
// The values checked are all default values so the value name is not needed.
typedef struct {
const char* keyName;
const char* valueData;
const char* oldValueData;
} SETTING;
#define APP_REG_NAME_BASE L"Firefox-"
#define VAL_FILE_ICON "%APPPATH%,1"
#define VAL_OPEN "\"%APPPATH%\" -osint -url \"%1\""
#define OLD_VAL_OPEN "\"%APPPATH%\" -requestPending -osint -url \"%1\""
#define DI "\\DefaultIcon"
#define SOC "\\shell\\open\\command"
#define SOD "\\shell\\open\\ddeexec"
// Used for updating the FTP protocol handler's shell open command under HKCU.
#define FTP_SOC L"Software\\Classes\\ftp\\shell\\open\\command"
#define MAKE_KEY_NAME1(PREFIX, MID) \
PREFIX MID
// The DefaultIcon registry key value should never be used when checking if
// Firefox is the default browser for file handlers since other applications
// (e.g. MS Office) may modify the DefaultIcon registry key value to add Icon
// Handlers. see http://msdn2.microsoft.com/en-us/library/aa969357.aspx for
// more info. The FTP protocol is not checked so advanced users can set the FTP
// handler to another application and still have Firefox check if it is the
// default HTTP and HTTPS handler.
// *** Do not add additional checks here unless you skip them when aForAllTypes
// is false below***.
static SETTING gSettings[] = {
// File Handler Class
// ***keep this as the first entry because when aForAllTypes is not set below
// it will skip over this check.***
{ MAKE_KEY_NAME1("FirefoxHTML", SOC), VAL_OPEN, OLD_VAL_OPEN },
// Protocol Handler Class - for Vista and above
{ MAKE_KEY_NAME1("FirefoxURL", SOC), VAL_OPEN, OLD_VAL_OPEN },
// Protocol Handlers
{ MAKE_KEY_NAME1("HTTP", DI), VAL_FILE_ICON },
{ MAKE_KEY_NAME1("HTTP", SOC), VAL_OPEN, OLD_VAL_OPEN },
{ MAKE_KEY_NAME1("HTTPS", DI), VAL_FILE_ICON },
{ MAKE_KEY_NAME1("HTTPS", SOC), VAL_OPEN, OLD_VAL_OPEN }
};
// The settings to disable DDE are separate from the default browser settings
// since they are only checked when Firefox is the default browser and if they
// are incorrect they are fixed without notifying the user.
static SETTING gDDESettings[] = {
// File Handler Class
{ MAKE_KEY_NAME1("Software\\Classes\\FirefoxHTML", SOD) },
// Protocol Handler Class - for Vista and above
{ MAKE_KEY_NAME1("Software\\Classes\\FirefoxURL", SOD) },
// Protocol Handlers
{ MAKE_KEY_NAME1("Software\\Classes\\FTP", SOD) },
{ MAKE_KEY_NAME1("Software\\Classes\\HTTP", SOD) },
{ MAKE_KEY_NAME1("Software\\Classes\\HTTPS", SOD) }
};
nsresult
GetHelperPath(nsAutoString& aPath)
{
@ -373,29 +229,6 @@ IsAARDefault(const RefPtr<IApplicationAssociationRegistration>& pAAR,
return isDefault;
}
static void
IsDefaultBrowserWin8(bool aCheckAllTypes, bool* aIsDefaultBrowser)
{
RefPtr<IApplicationAssociationRegistration> pAAR;
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
nullptr,
CLSCTX_INPROC,
IID_IApplicationAssociationRegistration,
getter_AddRefs(pAAR));
if (FAILED(hr)) {
return;
}
bool res = IsAARDefault(pAAR, L"http");
if (*aIsDefaultBrowser) {
*aIsDefaultBrowser = res;
}
res = IsAARDefault(pAAR, L".html");
if (*aIsDefaultBrowser && aCheckAllTypes) {
*aIsDefaultBrowser = res;
}
}
static nsresult
GetAppRegName(nsAutoString &aAppRegName)
{
@ -414,279 +247,45 @@ GetAppRegName(nsAutoString &aAppRegName)
rv = exeFile->GetParent(getter_AddRefs(appDir));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString path;
rv = appDir->GetPath(path);
nsAutoString appDirStr;
rv = appDir->GetPath(appDirStr);
NS_ENSURE_SUCCESS(rv, rv);
uint64_t hash = CityHash64(static_cast<const char *>(path.get()),
path.Length() * sizeof(nsAutoString::char_type));
aAppRegName = APP_REG_NAME_BASE;
aAppRegName.AppendInt((int)hash, 16);
uint64_t hash = CityHash64(static_cast<const char *>(appDirStr.get()),
appDirStr.Length() * sizeof(nsAutoString::char_type));
aAppRegName.AppendInt((int)(hash >> 32), 16);
aAppRegName.AppendInt((int)hash, 16);
return rv;
}
/*
* Query's the AAR for the default status.
* This only checks for FirefoxURL and if aCheckAllTypes is set, then
* it also checks for FirefoxHTML. Note that those ProgIDs are shared
* by all Firefox browsers.
*/
bool
nsWindowsShellService::IsDefaultBrowserVista(bool aCheckAllTypes,
bool* aIsDefaultBrowser)
{
RefPtr<IApplicationAssociationRegistration> pAAR;
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
nullptr,
CLSCTX_INPROC,
IID_IApplicationAssociationRegistration,
getter_AddRefs(pAAR));
if (FAILED(hr)) {
return false;
}
if (aCheckAllTypes) {
BOOL res;
nsAutoString appRegName;
GetAppRegName(appRegName);
hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE,
appRegName.get(),
&res);
*aIsDefaultBrowser = res;
} else if (!IsWin8OrLater()) {
*aIsDefaultBrowser = IsAARDefault(pAAR, L"http");
}
return true;
}
NS_IMETHODIMP
nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
bool aForAllTypes,
bool* aIsDefaultBrowser)
{
// Assume we're the default unless one of the several checks below tell us
// otherwise.
*aIsDefaultBrowser = true;
mozilla::Unused << aStartupCheck;
wchar_t exePath[MAX_BUF];
if (!::GetModuleFileNameW(0, exePath, MAX_BUF))
return NS_ERROR_FAILURE;
*aIsDefaultBrowser = false;
// Convert the path to a long path since GetModuleFileNameW returns the path
// that was used to launch Firefox which is not necessarily a long path.
if (!::GetLongPathNameW(exePath, exePath, MAX_BUF))
return NS_ERROR_FAILURE;
nsAutoString appLongPath(exePath);
HKEY theKey;
DWORD res;
nsresult rv;
wchar_t currValue[MAX_BUF];
SETTING* settings = gSettings;
if (!aForAllTypes && IsWin8OrLater()) {
// Skip over the file handler check
settings++;
RefPtr<IApplicationAssociationRegistration> pAAR;
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
nullptr,
CLSCTX_INPROC,
IID_IApplicationAssociationRegistration,
getter_AddRefs(pAAR));
if (FAILED(hr)) {
return NS_OK;
}
SETTING* end = gSettings + sizeof(gSettings) / sizeof(SETTING);
for (; settings < end; ++settings) {
NS_ConvertUTF8toUTF16 keyName(settings->keyName);
NS_ConvertUTF8toUTF16 valueData(settings->valueData);
int32_t offset = valueData.Find("%APPPATH%");
valueData.Replace(offset, 9, appLongPath);
rv = OpenKeyForReading(HKEY_CLASSES_ROOT, keyName, &theKey);
if (NS_FAILED(rv)) {
*aIsDefaultBrowser = false;
return NS_OK;
}
::ZeroMemory(currValue, sizeof(currValue));
DWORD len = sizeof currValue;
res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
(LPBYTE)currValue, &len);
// Close the key that was opened.
::RegCloseKey(theKey);
if (REG_FAILED(res) ||
_wcsicmp(valueData.get(), currValue)) {
// Key wasn't set or was set to something other than our registry entry.
NS_ConvertUTF8toUTF16 oldValueData(settings->oldValueData);
offset = oldValueData.Find("%APPPATH%");
oldValueData.Replace(offset, 9, appLongPath);
// The current registry value doesn't match the current or the old format.
if (_wcsicmp(oldValueData.get(), currValue)) {
*aIsDefaultBrowser = false;
return NS_OK;
}
res = ::RegOpenKeyExW(HKEY_CLASSES_ROOT, keyName.get(),
0, KEY_SET_VALUE, &theKey);
if (REG_FAILED(res)) {
// If updating the open command fails try to update it using the helper
// application when setting Firefox as the default browser.
*aIsDefaultBrowser = false;
return NS_OK;
}
res = ::RegSetValueExW(theKey, L"", 0, REG_SZ,
(const BYTE *) valueData.get(),
(valueData.Length() + 1) * sizeof(char16_t));
// Close the key that was created.
::RegCloseKey(theKey);
if (REG_FAILED(res)) {
// If updating the open command fails try to update it using the helper
// application when setting Firefox as the default browser.
*aIsDefaultBrowser = false;
return NS_OK;
}
}
}
// Only check if Firefox is the default browser on Vista and above if the
// previous checks show that Firefox is the default browser.
if (*aIsDefaultBrowser) {
IsDefaultBrowserVista(aForAllTypes, aIsDefaultBrowser);
if (IsWin8OrLater()) {
IsDefaultBrowserWin8(aForAllTypes, aIsDefaultBrowser);
}
}
// To handle the case where DDE isn't disabled due for a user because there
// account didn't perform a Firefox update this will check if Firefox is the
// default browser and if dde is disabled for each handler
// and if it isn't disable it. When Firefox is not the default browser the
// helper application will disable dde for each handler.
*aIsDefaultBrowser = IsAARDefault(pAAR, L"http");
if (*aIsDefaultBrowser && aForAllTypes) {
// Check ftp settings
end = gDDESettings + sizeof(gDDESettings) / sizeof(SETTING);
for (settings = gDDESettings; settings < end; ++settings) {
NS_ConvertUTF8toUTF16 keyName(settings->keyName);
rv = OpenKeyForReading(HKEY_CURRENT_USER, keyName, &theKey);
if (NS_FAILED(rv)) {
::RegCloseKey(theKey);
// If disabling DDE fails try to disable it using the helper
// application when setting Firefox as the default browser.
*aIsDefaultBrowser = false;
return NS_OK;
}
::ZeroMemory(currValue, sizeof(currValue));
DWORD len = sizeof currValue;
res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
(LPBYTE)currValue, &len);
// Close the key that was opened.
::RegCloseKey(theKey);
if (REG_FAILED(res) || char16_t('\0') != *currValue) {
// Key wasn't set or was set to something other than our registry entry.
// Delete the key along with all of its childrean and then recreate it.
::SHDeleteKeyW(HKEY_CURRENT_USER, keyName.get());
res = ::RegCreateKeyExW(HKEY_CURRENT_USER, keyName.get(), 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,
nullptr, &theKey, nullptr);
if (REG_FAILED(res)) {
// If disabling DDE fails try to disable it using the helper
// application when setting Firefox as the default browser.
*aIsDefaultBrowser = false;
return NS_OK;
}
res = ::RegSetValueExW(theKey, L"", 0, REG_SZ, (const BYTE *) L"",
sizeof(char16_t));
// Close the key that was created.
::RegCloseKey(theKey);
if (REG_FAILED(res)) {
// If disabling DDE fails try to disable it using the helper
// application when setting Firefox as the default browser.
*aIsDefaultBrowser = false;
return NS_OK;
}
}
}
// Update the FTP protocol handler's shell open command if it is the old
// format.
res = ::RegOpenKeyExW(HKEY_CURRENT_USER, FTP_SOC, 0, KEY_ALL_ACCESS,
&theKey);
// Don't update the FTP protocol handler's shell open command when opening
// its registry key fails under HKCU since it most likely doesn't exist.
if (NS_FAILED(rv)) {
return NS_OK;
}
NS_ConvertUTF8toUTF16 oldValueOpen(OLD_VAL_OPEN);
int32_t offset = oldValueOpen.Find("%APPPATH%");
oldValueOpen.Replace(offset, 9, appLongPath);
::ZeroMemory(currValue, sizeof(currValue));
DWORD len = sizeof currValue;
res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, (LPBYTE)currValue,
&len);
// Don't update the FTP protocol handler's shell open command when the
// current registry value doesn't exist or matches the old format.
if (REG_FAILED(res) ||
_wcsicmp(oldValueOpen.get(), currValue)) {
::RegCloseKey(theKey);
return NS_OK;
}
NS_ConvertUTF8toUTF16 valueData(VAL_OPEN);
valueData.Replace(offset, 9, appLongPath);
res = ::RegSetValueExW(theKey, L"", 0, REG_SZ,
(const BYTE *) valueData.get(),
(valueData.Length() + 1) * sizeof(char16_t));
// Close the key that was created.
::RegCloseKey(theKey);
// If updating the FTP protocol handlers shell open command fails try to
// update it using the helper application when setting Firefox as the
// default browser.
if (REG_FAILED(res)) {
*aIsDefaultBrowser = false;
}
*aIsDefaultBrowser = IsAARDefault(pAAR, L".html");
}
return NS_OK;
}
static nsresult
DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
{
// shell32.dll is in the knownDLLs list so will always be loaded from the
// system32 directory.
static const wchar_t kSehllLibraryName[] = L"shell32.dll";
HMODULE shellDLL = ::LoadLibraryW(kSehllLibraryName);
if (!shellDLL) {
return NS_ERROR_FAILURE;
}
decltype(SHOpenWithDialog)* SHOpenWithDialogFn =
(decltype(SHOpenWithDialog)*) GetProcAddress(shellDLL, "SHOpenWithDialog");
if (!SHOpenWithDialogFn) {
return NS_ERROR_FAILURE;
}
nsresult rv;
HRESULT hr = SHOpenWithDialogFn(hwndParent, poainfo);
if (SUCCEEDED(hr) || (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))) {
rv = NS_OK;
} else {
rv = NS_ERROR_FAILURE;
}
FreeLibrary(shellDLL);
return rv;
}
nsresult
nsWindowsShellService::LaunchControlPanelDefaultsSelectionUI()
{
@ -863,7 +462,12 @@ nsWindowsShellService::LaunchHTTPHandlerPane()
info.oaifInFlags = OAIF_FORCE_REGISTRATION |
OAIF_URL_PROTOCOL |
OAIF_REGISTER_EXT;
return DynSHOpenWithDialog(nullptr, &info);
HRESULT hr = SHOpenWithDialog(nullptr, &info);
if (SUCCEEDED(hr) || (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))) {
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP

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

@ -26,7 +26,6 @@ public:
NS_DECL_NSIWINDOWSSHELLSERVICE
protected:
bool IsDefaultBrowserVista(bool aCheckAllTypes, bool* aIsDefaultBrowser);
nsresult LaunchControlPanelDefaultsSelectionUI();
nsresult LaunchControlPanelDefaultPrograms();
nsresult LaunchModernSettingsDialogDefaultApps();

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

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 132388656,
"digest": "46c2c5d048b6d9e9bbb163277a1ae8a3c37727ad8aa8dd661c2b95e2b68e4e85b20ae4fa0eadf3a37a3c09801964fd7107151909ad07c3a7b2fbf805c7035d97",
"version": "rustc 1.15.1-dev (38014d1b6 2017-02-08) gecko build with -fPIC",
"size": 75614428,
"digest": "1bfd06db51c4aeaf43e43d8080069bba4b3d590a4863366ceb9c9a9915fbc528111fa067a13fc701ee00cbeda21a9f5de23d20d2479eab35fa2d7f729c660159",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

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

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 132388656,
"digest": "46c2c5d048b6d9e9bbb163277a1ae8a3c37727ad8aa8dd661c2b95e2b68e4e85b20ae4fa0eadf3a37a3c09801964fd7107151909ad07c3a7b2fbf805c7035d97",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 110076708,
"digest": "2c865f12279b103e8861071e05480cd8aeb9c4e4cd63eea1b8ca50fb92880583bebd27a3af6a86b3f12b9ee89e70839140f061ab829fcceca5e85dc8bc428ec3",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

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

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 132388656,
"digest": "46c2c5d048b6d9e9bbb163277a1ae8a3c37727ad8aa8dd661c2b95e2b68e4e85b20ae4fa0eadf3a37a3c09801964fd7107151909ad07c3a7b2fbf805c7035d97",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 110076708,
"digest": "2c865f12279b103e8861071e05480cd8aeb9c4e4cd63eea1b8ca50fb92880583bebd27a3af6a86b3f12b9ee89e70839140f061ab829fcceca5e85dc8bc428ec3",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

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

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 132388656,
"digest": "46c2c5d048b6d9e9bbb163277a1ae8a3c37727ad8aa8dd661c2b95e2b68e4e85b20ae4fa0eadf3a37a3c09801964fd7107151909ad07c3a7b2fbf805c7035d97",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 110076708,
"digest": "2c865f12279b103e8861071e05480cd8aeb9c4e4cd63eea1b8ca50fb92880583bebd27a3af6a86b3f12b9ee89e70839140f061ab829fcceca5e85dc8bc428ec3",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

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

@ -24,9 +24,9 @@
"size": 12072532
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 132388656,
"digest": "46c2c5d048b6d9e9bbb163277a1ae8a3c37727ad8aa8dd661c2b95e2b68e4e85b20ae4fa0eadf3a37a3c09801964fd7107151909ad07c3a7b2fbf805c7035d97",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 110076708,
"digest": "2c865f12279b103e8861071e05480cd8aeb9c4e4cd63eea1b8ca50fb92880583bebd27a3af6a86b3f12b9ee89e70839140f061ab829fcceca5e85dc8bc428ec3",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

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

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 132388656,
"digest": "46c2c5d048b6d9e9bbb163277a1ae8a3c37727ad8aa8dd661c2b95e2b68e4e85b20ae4fa0eadf3a37a3c09801964fd7107151909ad07c3a7b2fbf805c7035d97",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 110076708,
"digest": "2c865f12279b103e8861071e05480cd8aeb9c4e4cd63eea1b8ca50fb92880583bebd27a3af6a86b3f12b9ee89e70839140f061ab829fcceca5e85dc8bc428ec3",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

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

@ -16,9 +16,9 @@
"unpack": true
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 132388656,
"digest": "46c2c5d048b6d9e9bbb163277a1ae8a3c37727ad8aa8dd661c2b95e2b68e4e85b20ae4fa0eadf3a37a3c09801964fd7107151909ad07c3a7b2fbf805c7035d97",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 110076708,
"digest": "2c865f12279b103e8861071e05480cd8aeb9c4e4cd63eea1b8ca50fb92880583bebd27a3af6a86b3f12b9ee89e70839140f061ab829fcceca5e85dc8bc428ec3",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

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

@ -8,9 +8,9 @@
"unpack": true
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 118864812,
"digest": "649900813154b21c51c129050fde791e9974083936c9ef002dc18e1c6f8b9ad9ecfbdb5dd580e87743fc2bebed9b64ef9906461e60ec2a1e5277f3c213f417c9",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 122688750,
"digest": "3216889f778b7df63e1d90f788322beb13f4120b36f43bc92108468443b7fdadc0249ef82dfe04ad6a8224d2b65b8d5bfdc803244809f4f65fd469d769039573",
"algorithm": "sha512",
"filename": "rustc.tar.bz2",
"unpack": true

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

@ -33,17 +33,18 @@
"size": 1020700
},
{
"size": 57060,
"version": "https://github.com/andreas56/libdmg-hfsplus rev 81dd75fd1549b24bf8af9736ac25518b367e6b63",
"size": 62032,
"visibility": "public",
"digest": "9649ca595f4cf088d118da26201f92cc94cda7af49c7c48112ee31cd13c83b2935b3e145de9dd78060cff2480b4c2e7ff5fb24235876956fed13c87852071998",
"digest": "9073c41034784eb8823ec817aed42bbc65c8da63ad3fac572726fa48b36320ee302ca8f51b23576e7fdbeec6ab300610d0c58bbd9c52024577dfdb13d95aa2ec",
"algorithm": "sha512",
"unpack": true,
"filename": "dmg.tar.xz"
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 152573516,
"digest": "eef2f10bf57005d11c34b9b49eb76d0f09d026295055039fea89952a3be51580abdab29366278ed4bfa393b33c5cee4d51d3af4221e9e7d7d833d0fc1347597c",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 140312816,
"digest": "9b464eae8e8ae0020609b019ce10407eabb65580b2bf97fd1719dee15d6211fa61cb8bf7671efb415515256eaa0c5e48270f74d322e0b9105ee56b61a0de4935",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

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

@ -8,9 +8,9 @@
"unpack": true
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 118864812,
"digest": "649900813154b21c51c129050fde791e9974083936c9ef002dc18e1c6f8b9ad9ecfbdb5dd580e87743fc2bebed9b64ef9906461e60ec2a1e5277f3c213f417c9",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 122688750,
"digest": "3216889f778b7df63e1d90f788322beb13f4120b36f43bc92108468443b7fdadc0249ef82dfe04ad6a8224d2b65b8d5bfdc803244809f4f65fd469d769039573",
"algorithm": "sha512",
"filename": "rustc.tar.bz2",
"unpack": true

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

@ -6,9 +6,9 @@
"filename": "mozmake.exe"
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 63983591,
"digest": "7bd57c81e57a984a83de75dce214e344cee870bf7e1955ae3831f5a3d638fd4d2d1dca6d434a3264f98bc3b3f00dedb55d0b6d28afd9015d231e8a8f3075dea0",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 67183225,
"digest": "83e3f621587598ed46a3cde20c440f372a69db60d9bcb287bca6578d689a48a4caf2c0d3e490968856aaf5f4fad7314078936b7cc92a3344093541c3e6f2553f",
"algorithm": "sha512",
"filename": "rustc.tar.bz2",
"unpack": true

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

@ -6,9 +6,9 @@
"filename": "mozmake.exe"
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 63983591,
"digest": "7bd57c81e57a984a83de75dce214e344cee870bf7e1955ae3831f5a3d638fd4d2d1dca6d434a3264f98bc3b3f00dedb55d0b6d28afd9015d231e8a8f3075dea0",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 67183225,
"digest": "83e3f621587598ed46a3cde20c440f372a69db60d9bcb287bca6578d689a48a4caf2c0d3e490968856aaf5f4fad7314078936b7cc92a3344093541c3e6f2553f",
"algorithm": "sha512",
"filename": "rustc.tar.bz2",
"unpack": true

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

@ -6,9 +6,9 @@
"filename": "mozmake.exe"
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 63983591,
"digest": "7bd57c81e57a984a83de75dce214e344cee870bf7e1955ae3831f5a3d638fd4d2d1dca6d434a3264f98bc3b3f00dedb55d0b6d28afd9015d231e8a8f3075dea0",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 67183225,
"digest": "83e3f621587598ed46a3cde20c440f372a69db60d9bcb287bca6578d689a48a4caf2c0d3e490968856aaf5f4fad7314078936b7cc92a3344093541c3e6f2553f",
"algorithm": "sha512",
"filename": "rustc.tar.bz2",
"unpack": true

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

@ -6,9 +6,9 @@
"filename": "mozmake.exe"
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 69760089,
"digest": "1ad9b88c90c70a11b76526a437f0f4df9590cd3dced50229a306b4b49fbf22b45871461cff9e33c8aabd00ef7734732f09dae131f1805587bac864e63f24f68f",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 72765977,
"digest": "eb17864d7221b3bff46a33b483691c5bbd3d8ced66343ec4c789d84c87d931fafc4da499066b586966a4a5c10232d9b1fd424b6d00ad795dddbe8b12903442f9",
"algorithm": "sha512",
"visibility": "public",
"filename": "rustc.tar.bz2",

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

@ -6,9 +6,9 @@
"filename": "mozmake.exe"
},
{
"version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
"size": 69760089,
"digest": "1ad9b88c90c70a11b76526a437f0f4df9590cd3dced50229a306b4b49fbf22b45871461cff9e33c8aabd00ef7734732f09dae131f1805587bac864e63f24f68f",
"version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
"size": 72765977,
"digest": "eb17864d7221b3bff46a33b483691c5bbd3d8ced66343ec4c789d84c87d931fafc4da499066b586966a4a5c10232d9b1fd424b6d00ad795dddbe8b12903442f9",
"algorithm": "sha512",
"visibility": "public",
"filename": "rustc.tar.bz2",

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

@ -17,8 +17,8 @@ module.exports = { // eslint-disable-line no-undef
// Rules from the mozilla plugin
"mozilla/balanced-listeners": "error",
"mozilla/no-aArgs": "warn",
"mozilla/no-cpows-in-tests": "warn",
"mozilla/var-only-at-top-level": "warn",
"mozilla/no-cpows-in-tests": "error",
"mozilla/var-only-at-top-level": "error",
"valid-jsdoc": ["error", {
"prefer": {
@ -38,12 +38,6 @@ module.exports = { // eslint-disable-line no-undef
// Braces only needed for multi-line arrow function blocks
// "arrow-body-style": ["error", "as-needed"],
// Require spacing around =>
"arrow-spacing": "error",
// Always require spacing around a single line block
"block-spacing": "warn",
// Forbid spaces inside the square brackets of array literals.
"array-bracket-spacing": ["error", "never"],
@ -57,24 +51,12 @@ module.exports = { // eslint-disable-line no-undef
// start and end braces on the same line.
"brace-style": ["error", "1tbs", {"allowSingleLine": true}],
// No space before always a space after a comma
"comma-spacing": ["error", {"before": false, "after": true}],
// Commas at the end of the line not the start
"comma-style": "error",
// Don't require spaces around computed properties
"computed-property-spacing": ["warn", "never"],
// Functions are not required to consistently return something or nothing
"consistent-return": "off",
// Require braces around blocks that start a new line
"curly": ["error", "all"],
// Always require a trailing EOL
"eol-last": "error",
// Require function* name()
"generator-star-spacing": ["error", {"before": false, "after": true}],
@ -84,27 +66,12 @@ module.exports = { // eslint-disable-line no-undef
// Space after colon not before in property declarations
"key-spacing": ["error", {"beforeColon": false, "afterColon": true, "mode": "minimum"}],
// Require spaces before and after finally, catch, etc.
"keyword-spacing": "error",
// Unix linebreaks
"linebreak-style": ["error", "unix"],
// Always require parenthesis for new calls
"new-parens": "error",
// Use [] instead of Array()
"no-array-constructor": "error",
// No duplicate arguments in function declarations
"no-dupe-args": "error",
// No duplicate keys in object declarations
"no-dupe-keys": "error",
// No duplicate cases in switch statements
"no-duplicate-case": "error",
// If an if block ends with a return no need for an else block
// "no-else-return": "error",
@ -115,74 +82,9 @@ module.exports = { // eslint-disable-line no-undef
// which is a valid use case.
"no-empty": "error",
// No empty character classes in regex
"no-empty-character-class": "error",
// Disallow empty destructuring
"no-empty-pattern": "error",
// No assiging to exception variable
"no-ex-assign": "error",
// No using !! where casting to boolean is already happening
"no-extra-boolean-cast": "warn",
// No double semicolon
"no-extra-semi": "error",
// No overwriting defined functions
"no-func-assign": "error",
// No invalid regular expresions
"no-invalid-regexp": "error",
// No odd whitespace characters
"no-irregular-whitespace": "error",
// No single if block inside an else block
"no-lonely-if": "warn",
// No mixing spaces and tabs in indent
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
// Disallow use of multiple spaces (sometimes used to align const values,
// array or object items, etc.). It's hard to maintain and doesn't add that
// much benefit.
"no-multi-spaces": "warn",
// No reassigning native JS objects
"no-native-reassign": "error",
// Nested ternary statements are confusing
"no-nested-ternary": "error",
// Use {} instead of new Object()
"no-new-object": "error",
// No Math() or JSON()
"no-obj-calls": "error",
// No octal literals
"no-octal": "error",
// No redeclaring variables
"no-redeclare": "error",
// No unnecessary comparisons
"no-self-compare": "error",
// No spaces between function name and parentheses
"no-spaced-func": "warn",
// No trailing whitespace
"no-trailing-spaces": "error",
// Error on newline where a semicolon is needed
"no-unexpected-multiline": "error",
// No unreachable statements
"no-unreachable": "error",
// No expressions where a statement is expected
"no-unused-expressions": "error",
@ -192,30 +94,12 @@ module.exports = { // eslint-disable-line no-undef
// No using variables before defined
"no-use-before-define": "error",
// No using with
"no-with": "error",
// Always require semicolon at end of statement
"semi": ["error", "always"],
// Require space before blocks
"space-before-blocks": "error",
// Never use spaces before function parentheses
"space-before-function-paren": ["error", {"anonymous": "never", "named": "never"}],
// Require spaces around operators, except for a|"off".
"space-infix-ops": ["error", {"int32Hint": true}],
// ++ and -- should not need spacing
"space-unary-ops": ["warn", {"nonwords": false}],
// No comparisons to NaN
"use-isnan": "error",
// Only check typeof against valid results
"valid-typeof": "error",
// Disallow using variables outside the blocks they are defined (especially
// since only let and const are used, see "no-var").
"block-scoped-var": "error",
@ -231,228 +115,70 @@ module.exports = { // eslint-disable-line no-undef
// with auto-binding fat arrow functions).
// "consistent-this": ["error", "self"],
// Don't require a default case in switch statements. Avoid being forced to
// add a bogus default when you know all possible cases are handled.
"default-case": "off",
// Enforce dots on the next line with property name.
"dot-location": ["error", "property"],
// Encourage the use of dot notation whenever possible.
"dot-notation": "error",
// Allow using == instead of ===, in the interest of landing something since
// the devtools codebase is split on convention here.
"eqeqeq": "off",
// Don't require function expressions to have a name.
// This makes the code more verbose and hard to read. Our engine already
// does a fantastic job assigning a name to the function, which includes
// the enclosing function name, and worst case you have a line number that
// you can just look up.
"func-names": "off",
// Allow use of function declarations and expressions.
"func-style": "off",
// Don't enforce the maximum depth that blocks can be nested. The complexity
// rule is a better rule to check this.
"max-depth": "off",
// Maximum length of a line.
// Disabled because we exceed this in too many places.
"max-len": ["off", 80],
// This should be 100 but too many lines were longer than that so set a
// conservative upper bound for now.
"max-len": ["error", 140],
// Maximum depth callbacks can be nested.
"max-nested-callbacks": ["error", 4],
// Don't limit the number of parameters that can be used in a function.
"max-params": "off",
// Don't limit the maximum number of statement allowed in a function. We
// already have the complexity rule that's a better measurement.
"max-statements": "off",
// Don't require a capital letter for constructors, only check if all new
// operators are followed by a capital letter. Don't warn when capitalized
// functions are used without the new operator.
"new-cap": ["off", {"capIsNew": false}],
// Allow use of bitwise operators.
"no-bitwise": "off",
// Disallow use of arguments.caller or arguments.callee.
"no-caller": "error",
// Disallow the catch clause parameter name being the same as a variable in
// the outer scope, to avoid confusion.
"no-catch-shadow": "off",
// Disallow assignment in conditional expressions.
"no-cond-assign": "error",
// Disallow using the console API.
"no-console": "error",
// Allow using constant expressions in conditions like while (true)
"no-constant-condition": "off",
// Allow use of the continue statement.
"no-continue": "off",
// Disallow control characters in regular expressions.
"no-control-regex": "error",
// Disallow use of debugger.
"no-debugger": "error",
// Disallow deletion of variables (deleting properties is fine).
"no-delete-var": "error",
// Allow division operators explicitly at beginning of regular expression.
"no-div-regex": "off",
// Disallow use of eval(). We have other APIs to evaluate code in content.
"no-eval": "error",
// Disallow adding to native types
"no-extend-native": "error",
// Disallow unnecessary function binding.
"no-extra-bind": "error",
// Allow unnecessary parentheses, as they may make the code more readable.
"no-extra-parens": "off",
// Disallow fallthrough of case statements, except if there is a comment.
"no-fallthrough": "error",
// Allow the use of leading or trailing decimal points in numeric literals.
"no-floating-decimal": "off",
// Allow comments inline after code.
"no-inline-comments": "off",
// Disallow use of labels for anything other then loops and switches.
"no-labels": ["error", {"allowLoop": true}],
// Disallow use of multiline strings (use template strings instead).
"no-multi-str": "warn",
// Disallow multiple empty lines.
"no-multiple-empty-lines": ["warn", {"max": 2}],
// Allow reassignment of function parameters.
"no-param-reassign": "off",
// Allow string concatenation with __dirname and __filename (not a node env).
"no-path-concat": "off",
// Allow use of unary operators, ++ and --.
"no-plusplus": "off",
// Allow using process.env (not a node environment).
"no-process-env": "off",
// Allow using process.exit (not a node environment).
"no-process-exit": "off",
// Disallow usage of __proto__ property.
"no-proto": "error",
// Disallow multiple spaces in a regular expression literal.
"no-regex-spaces": "error",
// Allow reserved words being used as object literal keys.
"no-reserved-keys": "off",
// Don't restrict usage of specified node modules (not a node environment).
"no-restricted-modules": "off",
// Disallow use of assignment in return statement. It is preferable for a
// single line of code to have only one easily predictable effect.
"no-return-assign": "error",
// Don't warn about declaration of variables already declared in the outer scope.
"no-shadow": "off",
// Disallow shadowing of names such as arguments.
"no-shadow-restricted-names": "error",
// Allow use of synchronous methods (not a node environment).
"no-sync": "off",
// Allow the use of ternary operators.
"no-ternary": "off",
// Disallow throwing literals (eg. throw "error" instead of
// throw new Error("error")).
"no-throw-literal": "error",
// Disallow use of undeclared variables unless mentioned in a /* global */
// block. Note that globals from head.js are automatically imported in tests
// by the import-headjs-globals rule form the mozilla eslint plugin.
"no-undef": "error",
// Allow dangling underscores in identifiers (for privates).
"no-underscore-dangle": "off",
// Allow use of undefined variable.
"no-undefined": "off",
// Disallow the use of Boolean literals in conditional expressions.
"no-unneeded-ternary": "error",
// We use var-only-at-top-level instead of no-var as we allow top level
// vars.
"no-var": "off",
// Allow using TODO/FIXME comments.
"no-warning-comments": "off",
// Don't require method and property shorthand syntax for object literals.
// We use this in the code a lot, but not consistently, and this seems more
// like something to check at code review time.
"object-shorthand": "off",
// Allow more than one variable declaration per function.
"one-var": "off",
// Disallow padding within blocks.
"padded-blocks": ["warn", "never"],
// Don't require quotes around object literal property names.
"quote-props": "off",
// Double quotes should be used.
"quotes": ["warn", "double", {"avoidEscape": true, "allowTemplateLiterals": true}],
// Require use of the second argument for parseInt().
"radix": "error",
// Enforce spacing after semicolons.
"semi-spacing": ["error", {"before": false, "after": true}],
// Don't require to sort variables within the same declaration block.
// Anyway, one-var is disabled.
"sort-vars": "off",
// Require a space immediately following the // in a line comment.
"spaced-comment": ["error", "always"],
// Require "use strict" to be defined globally in the script.
"strict": ["error", "global"],
// Allow vars to be declared anywhere in the scope.
"vars-on-top": "off",
// Don't require immediate function invocation to be wrapped in parentheses.
"wrap-iife": "off",
// Don't require regex literals to be wrapped in parentheses (which
// supposedly prevent them from being mistaken for division operators).
"wrap-regex": "off",
// Disallow Yoda conditions (where literal value comes first).
"yoda": "error",
@ -462,9 +188,6 @@ module.exports = { // eslint-disable-line no-undef
// Disallow function or variable declarations in nested blocks
"no-inner-declarations": "error",
// Disallow usage of __iterator__ property
"no-iterator": "error",
// Disallow labels that share a name with a variable
"no-label-var": "error",

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

@ -46,7 +46,7 @@ let FormAutofillParent = {
/**
* Initializes ProfileStorage and registers the message handler.
*/
init: function() {
init() {
let storePath =
OS.Path.join(OS.Constants.Path.profileDir, PROFILE_JSON_FILE_NAME);
@ -66,7 +66,7 @@ let FormAutofillParent = {
* @param {object} message.data The data of the message.
* @param {nsIFrameMessageManager} message.target Caller's message manager.
*/
receiveMessage: function({name, data, target}) {
receiveMessage({name, data, target}) {
switch (name) {
case "FormAutofill:PopulateFieldValues":
this._populateFieldValues(data, target);
@ -84,7 +84,7 @@ let FormAutofillParent = {
*
* @returns {ProfileStorage}
*/
getProfileStore: function() {
getProfileStore() {
return this._profileStore;
},
@ -93,7 +93,7 @@ let FormAutofillParent = {
*
* @private
*/
_uninit: function() {
_uninit() {
if (this._profileStore) {
this._profileStore._saveImmediately();
this._profileStore = null;

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

@ -1,5 +0,0 @@
{
"extends": [
"../../../../../testing/xpcshell/xpcshell.eslintrc.js"
],
}

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

@ -0,0 +1,7 @@
"use strict";
module.exports = { // eslint-disable-line no-undef
"extends": [
"../../../../../testing/xpcshell/xpcshell.eslintrc.js",
],
};

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

@ -39,7 +39,7 @@ let gFileCounter = Math.floor(Math.random() * 1000000);
function loadFormAutofillContent() {
let facGlobal = {
addEventListener: function() {},
addEventListener() {},
};
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);

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

@ -2,8 +2,6 @@
* Test for populating field values in Form Autofill Parent.
*/
/* global FormAutofillParent */
"use strict";
Cu.import("resource://formautofill/FormAutofillParent.jsm");
@ -64,7 +62,7 @@ add_task(function* test_populateFieldValues() {
fields: TEST_FIELDS,
},
target: {
sendAsyncMessage: function(name, data) {
sendAsyncMessage(name, data) {
do_check_eq(name, "FormAutofill:fillForm");
let fields = data.fields;

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

@ -16,7 +16,7 @@ let matchingProfiles = [{
let testCases = [{
options: {},
matchingProfiles: matchingProfiles,
matchingProfiles,
searchString: "",
fieldName: "",
expected: {

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

@ -2,8 +2,6 @@
* Tests ProfileStorage object.
*/
/* global ProfileStorage */
"use strict";
Cu.import("resource://gre/modules/Task.jsm");

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

@ -457,7 +457,13 @@ class Viewport {
type: 'viewport',
xOffset: this._runtimePosition.x,
yOffset: this._runtimePosition.y,
zoom: this._zoom
zoom: this._zoom,
// FIXME Since Chromium improves pinch-zoom for PDF. PostMessage of type
// viewport takes an addition parameter pinchPhase. We workaround
// here by adding a pinchPhase of value 0 to make sure that viewing
// pdf works normally. More details about pinch-zoom please refer
// to chromium revision: 6e1abbfb2450eedddb1ab128be1b31cc93104e41
pinchPhase: 0
});
let newPage = this._getMostVisiblePage();

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

@ -17,15 +17,12 @@ if test -z $TMPDIR; then
TMPDIR=/tmp/
fi
# Install clang first
yum install -y clang
# Set an md5 check file to validate input
echo "${md5sum} *${TMPDIR}/${filename}" > $TMPDIR/hfsplus.MD5
# Most-upstream is https://opensource.apple.com/source/diskdev_cmds/
# Download the source of the specified version of binutils
# Download the source of the specified version of hfsplus
wget -c -P $TMPDIR http://pkgs.fedoraproject.org/repo/pkgs/hfsplus-tools/${filename}/${md5sum}/${filename} || exit 1
md5sum -c $TMPDIR/hfsplus.MD5 || exit 1
mkdir hfsplus-source

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

@ -730,7 +730,7 @@ BasePrincipal::CloneStrippingUserContextIdAndFirstPartyDomain()
}
bool
BasePrincipal::AddonAllowsLoad(nsIURI* aURI)
BasePrincipal::AddonAllowsLoad(nsIURI* aURI, bool aExplicit /* = false */)
{
if (mOriginAttributes.mAddonId.IsEmpty()) {
return false;
@ -740,7 +740,7 @@ BasePrincipal::AddonAllowsLoad(nsIURI* aURI)
NS_ENSURE_TRUE(aps, false);
bool allowed = false;
nsresult rv = aps->AddonMayLoadURI(mOriginAttributes.mAddonId, aURI, &allowed);
nsresult rv = aps->AddonMayLoadURI(mOriginAttributes.mAddonId, aURI, aExplicit, &allowed);
return NS_SUCCEEDED(rv) && allowed;
}

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

@ -264,6 +264,11 @@ public:
already_AddRefed<BasePrincipal> CloneStrippingUserContextIdAndFirstPartyDomain();
// Helper to check whether this principal is associated with an addon that
// allows unprivileged code to load aURI. aExplicit == true will prevent
// use of all_urls permission, requiring the domain in its permissions.
bool AddonAllowsLoad(nsIURI* aURI, bool aExplicit = false);
protected:
virtual ~BasePrincipal();
@ -278,10 +283,6 @@ protected:
virtual bool MayLoadInternal(nsIURI* aURI) = 0;
friend class ::nsExpandedPrincipal;
// Helper to check whether this principal is associated with an addon that
// allows unprivileged code to load aURI.
bool AddonAllowsLoad(nsIURI* aURI);
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
nsCOMPtr<nsIContentSecurityPolicy> mPreloadCSP;
OriginAttributes mOriginAttributes;

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

@ -47,9 +47,10 @@ interface nsIAddonPolicyService : nsISupports
/**
* Returns true if unprivileged code associated with the given addon may load
* data from |aURI|.
* data from |aURI|. If |aExplicit| is true, the <all_urls> permission and
* permissive host globs are ignored when checking for a match.
*/
boolean addonMayLoadURI(in AString aAddonId, in nsIURI aURI);
boolean addonMayLoadURI(in AString aAddonId, in nsIURI aURI, [optional] in boolean aExplicit);
/**
* Returns true if a given extension:// URI is web-accessible.

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

@ -299,9 +299,9 @@ AnimationTimeBlock.prototype = {
text += "\n";
}
// Adding the easing.
if (state.easing) {
text += L10N.getStr("player.animationEasingLabel") + " ";
// Adding the easing if it is not "linear".
if (state.easing && state.easing !== "linear") {
text += L10N.getStr("player.animationOverallEasingLabel") + " ";
text += state.easing;
text += "\n";
}

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

@ -37,8 +37,11 @@ add_task(function* () {
} else {
ok(!title.match(/Repeats: /), "The tooltip doesn't show the iterations");
}
if (state.easing) {
ok(title.match(/Easing: /), "The tooltip shows the easing");
if (state.easing && state.easing !== "linear") {
ok(title.match(/Overall easing: /), "The tooltip shows the easing");
} else {
ok(!title.match(/Overall easing: /),
"The tooltip doesn't show the easing if it is 'linear'");
}
if (state.fill) {
ok(title.match(/Fill: /), "The tooltip shows the fill");

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

@ -70,11 +70,12 @@ player.infiniteIterationCountText=∞
# %2$S will be replaced by the actual time of iteration start
player.animationIterationStartLabel=Iteration start: %1$S (%2$Ss)
# LOCALIZATION NOTE (player.animationEasingLabel):
# LOCALIZATION NOTE (player.animationOverallEasingLabel):
# This string is displayed in a tooltip that appears when hovering over
# animations in the timeline. It is the label displayed before the animation
# easing value.
player.animationEasingLabel=Easing:
# animations in the timeline. It is the label displayed before the easing
# that applies to a whole iteration of an animation as opposed to the
# easing that applies between animation keyframes.
player.animationOverallEasingLabel=Overall easing:
# LOCALIZATION NOTE (player.animationFillLabel):
# This string is displayed in a tooltip that appears when hovering over

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

@ -2,15 +2,19 @@
* 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/. */
/* globals NetMonitorController */
"use strict";
const {
ADD_REQUEST,
UPDATE_REQUEST,
CLEAR_REQUESTS,
CLONE_SELECTED_REQUEST,
REMOVE_SELECTED_CUSTOM_REQUEST,
CLEAR_REQUESTS,
SEND_CUSTOM_REQUEST,
UPDATE_REQUEST,
} = require("../constants");
const { getSelectedRequest } = require("../selectors/index");
function addRequest(id, data, batch) {
return {
@ -40,6 +44,43 @@ function cloneSelectedRequest() {
};
}
/**
* Send a new HTTP request using the data in the custom request form.
*/
function sendCustomRequest() {
if (!NetMonitorController.supportsCustomRequest) {
return cloneSelectedRequest();
}
return (dispatch, getState) => {
const selected = getSelectedRequest(getState());
if (!selected) {
return;
}
// Send a new HTTP request using the data in the custom request form
let data = {
url: selected.url,
method: selected.method,
httpVersion: selected.httpVersion,
};
if (selected.requestHeaders) {
data.headers = selected.requestHeaders.headers;
}
if (selected.requestPostData) {
data.body = selected.requestPostData.postData.text;
}
NetMonitorController.webConsoleClient.sendHTTPRequest(data, (response) => {
return dispatch({
type: SEND_CUSTOM_REQUEST,
id: response.eventActor.actor,
});
});
};
}
/**
* Remove a request from the list. Supports removing only cloned requests with a
* "isCustom" attribute. Other requests never need to be removed.
@ -58,8 +99,9 @@ function clearRequests() {
module.exports = {
addRequest,
updateRequest,
clearRequests,
cloneSelectedRequest,
removeSelectedCustomRequest,
clearRequests,
sendCustomRequest,
updateRequest,
};

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

@ -5,19 +5,7 @@
"use strict";
const { getDisplayedRequests } = require("../selectors/index");
const { SELECT_REQUEST, PRESELECT_REQUEST } = require("../constants");
/**
* When a new request with a given id is added in future, select it immediately.
* Used by the "Edit and Resend" feature, where we know in advance the ID of the
* request, at a time when it wasn't sent yet.
*/
function preselectRequest(id) {
return {
type: PRESELECT_REQUEST,
id
};
}
const { SELECT_REQUEST } = require("../constants");
/**
* Select request with a given id.
@ -61,7 +49,6 @@ function selectDelta(delta) {
}
module.exports = {
preselectRequest,
selectRequest,
selectDelta,
};

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

@ -5,20 +5,20 @@
"use strict";
const {
OPEN_SIDEBAR,
OPEN_NETWORK_DETAILS,
OPEN_STATISTICS,
SELECT_DETAILS_PANEL_TAB,
WATERFALL_RESIZE,
} = require("../constants");
/**
* Change sidebar open state.
* Change network details panel.
*
* @param {boolean} open - open state
* @param {boolean} open - expected network details panel open state
*/
function openSidebar(open) {
function openNetworkDetails(open) {
return {
type: OPEN_SIDEBAR,
type: OPEN_NETWORK_DETAILS,
open,
};
}
@ -26,7 +26,7 @@ function openSidebar(open) {
/**
* Change performance statistics view open state.
*
* @param {boolean} visible - expected sidebar open state
* @param {boolean} visible - expected performance statistics open state
*/
function openStatistics(open) {
return {
@ -46,7 +46,7 @@ function resizeWaterfall(width) {
}
/**
* Change the selected tab for details panel.
* Change the selected tab for network details panel.
*
* @param {string} id - tab id to be selected
*/
@ -58,10 +58,11 @@ function selectDetailsPanelTab(id) {
}
/**
* Toggle sidebar open state.
* Toggle network details panel.
*/
function toggleSidebar() {
return (dispatch, getState) => dispatch(openSidebar(!getState().ui.sidebarOpen));
function toggleNetworkDetails() {
return (dispatch, getState) =>
dispatch(openNetworkDetails(!getState().ui.networkDetailsOpen));
}
/**
@ -72,10 +73,10 @@ function toggleStatistics() {
}
module.exports = {
openSidebar,
openNetworkDetails,
openStatistics,
resizeWaterfall,
selectDetailsPanelTab,
toggleSidebar,
toggleNetworkDetails,
toggleStatistics,
};

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

@ -1,15 +1,22 @@
/* 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/. */
/* globals NetMonitorView */
/* globals NetMonitorController */
"use strict";
const { createClass, PropTypes, DOM } = require("devtools/client/shared/vendor/react");
const { L10N } = require("../l10n");
const { div, span, button } = DOM;
const {
createClass,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const Actions = require("../actions/index");
const { ACTIVITY_TYPE } = require("../constants");
const { L10N } = require("../l10n");
const { button, div, span } = DOM;
/**
* UI displayed when the request list is empty. Contains instructions on reloading
@ -61,6 +68,7 @@ module.exports = connect(
undefined,
dispatch => ({
onPerfClick: e => dispatch(Actions.openStatistics(true)),
onReloadClick: e => NetMonitorView.reloadPage(),
onReloadClick: e =>
NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
})
)(RequestListEmptyNotice);

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

@ -23,7 +23,7 @@ function propertiesEqual(props, item1, item2) {
* Used by shouldComponentUpdate: compare two items, and compare only properties
* relevant for rendering the RequestListItem. Other properties (like request and
* response headers, cookies, bodies) are ignored. These are very useful for the
* sidebar details, but not here.
* network details, but not here.
*/
const UPDATED_REQ_ITEM_PROPS = [
"mimeType",

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

@ -15,7 +15,7 @@ const Actions = require("../actions/index");
const { L10N } = require("../l10n");
const {
getDisplayedRequestsSummary,
isSidebarToggleButtonDisabled,
isNetworkDetailsToggleButtonDisabled,
} = require("../selectors/index");
const {
getSizeWithDecimals,
@ -43,14 +43,17 @@ function Toolbar({
openStatistics,
requestFilterTypes,
setRequestFilterText,
sidebarToggleDisabled,
sidebarOpen,
networkDetailsToggleDisabled,
networkDetailsOpen,
summary,
toggleNetworkDetails,
toggleRequestFilterType,
toggleSidebar,
}) {
let toggleButtonClassName = ["devtools-button"];
if (!sidebarOpen) {
let toggleButtonClassName = [
"network-details-panel-toggle",
"devtools-button",
];
if (!networkDetailsOpen) {
toggleButtonClassName.push("pane-collapsed");
}
@ -109,12 +112,11 @@ function Toolbar({
onChange: setRequestFilterText,
}),
button({
id: "details-pane-toggle",
className: toggleButtonClassName.join(" "),
title: sidebarOpen ? COLLPASE_DETAILS_PANE : EXPAND_DETAILS_PANE,
disabled: sidebarToggleDisabled,
title: networkDetailsOpen ? COLLPASE_DETAILS_PANE : EXPAND_DETAILS_PANE,
disabled: networkDetailsToggleDisabled,
tabIndex: "0",
onMouseDown: toggleSidebar,
onMouseDown: toggleNetworkDetails,
}),
)
)
@ -128,17 +130,17 @@ Toolbar.propTypes = {
openStatistics: PropTypes.func.isRequired,
requestFilterTypes: PropTypes.object.isRequired,
setRequestFilterText: PropTypes.func.isRequired,
sidebarToggleDisabled: PropTypes.bool.isRequired,
sidebarOpen: PropTypes.bool.isRequired,
networkDetailsToggleDisabled: PropTypes.bool.isRequired,
networkDetailsOpen: PropTypes.bool.isRequired,
summary: PropTypes.object.isRequired,
toggleNetworkDetails: PropTypes.func.isRequired,
toggleRequestFilterType: PropTypes.func.isRequired,
toggleSidebar: PropTypes.func.isRequired,
};
module.exports = connect(
(state) => ({
sidebarToggleDisabled: isSidebarToggleButtonDisabled(state),
sidebarOpen: state.ui.sidebarOpen,
networkDetailsToggleDisabled: isNetworkDetailsToggleButtonDisabled(state),
networkDetailsOpen: state.ui.networkDetailsOpen,
requestFilterTypes: state.filters.requestFilterTypes,
summary: getDisplayedRequestsSummary(state),
}),
@ -152,6 +154,6 @@ module.exports = connect(
}
dispatch(Actions.toggleRequestFilterType(evt.target.dataset.key));
},
toggleSidebar: () => dispatch(Actions.toggleSidebar()),
toggleNetworkDetails: () => dispatch(Actions.toggleNetworkDetails()),
})
)(Toolbar);

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

@ -19,12 +19,12 @@ const actionTypes = {
CLEAR_TIMING_MARKERS: "CLEAR_TIMING_MARKERS",
CLONE_SELECTED_REQUEST: "CLONE_SELECTED_REQUEST",
ENABLE_REQUEST_FILTER_TYPE_ONLY: "ENABLE_REQUEST_FILTER_TYPE_ONLY",
OPEN_SIDEBAR: "OPEN_SIDEBAR",
OPEN_NETWORK_DETAILS: "OPEN_NETWORK_DETAILS",
OPEN_STATISTICS: "OPEN_STATISTICS",
PRESELECT_REQUEST: "PRESELECT_REQUEST",
REMOVE_SELECTED_CUSTOM_REQUEST: "REMOVE_SELECTED_CUSTOM_REQUEST",
SELECT_REQUEST: "SELECT_REQUEST",
SELECT_DETAILS_PANEL_TAB: "SELECT_DETAILS_PANEL_TAB",
SEND_CUSTOM_REQUEST: "SEND_CUSTOM_REQUEST",
SET_REQUEST_FILTER_TEXT: "SET_REQUEST_FILTER_TEXT",
SORT_BY: "SORT_BY",
TOGGLE_REQUEST_FILTER_TYPE: "TOGGLE_REQUEST_FILTER_TYPE",

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

@ -1,222 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* globals window, dumpn, gNetwork, $, EVENTS, NetMonitorView */
"use strict";
const { Task } = require("devtools/shared/task");
const { writeHeaderText,
getKeyWithEvent,
getUrlQuery,
parseQueryString } = require("./request-utils");
const Actions = require("./actions/index");
/**
* Functions handling the custom request view.
*/
function CustomRequestView() {
dumpn("CustomRequestView was instantiated");
}
CustomRequestView.prototype = {
/**
* Initialization function, called when the network monitor is started.
*/
initialize: function () {
dumpn("Initializing the CustomRequestView");
this.updateCustomRequestEvent = getKeyWithEvent(this.onUpdate.bind(this));
$("#custom-pane").addEventListener("input",
this.updateCustomRequestEvent);
},
/**
* Destruction function, called when the network monitor is closed.
*/
destroy: function () {
dumpn("Destroying the CustomRequestView");
$("#custom-pane").removeEventListener("input",
this.updateCustomRequestEvent);
},
/**
* Populates this view with the specified data.
*
* @param object data
* The data source (this should be the attachment of a request item).
* @return object
* Returns a promise that resolves upon population the view.
*/
populate: Task.async(function* (data) {
$("#custom-url-value").value = data.url;
$("#custom-method-value").value = data.method;
this.updateCustomQuery(data.url);
if (data.requestHeaders) {
let headers = data.requestHeaders.headers;
$("#custom-headers-value").value = writeHeaderText(headers);
}
if (data.requestPostData) {
let postData = data.requestPostData.postData.text;
$("#custom-postdata-value").value = yield gNetwork.getString(postData);
}
window.emit(EVENTS.CUSTOMREQUESTVIEW_POPULATED);
}),
/**
* Handle user input in the custom request form.
*
* @param object field
* the field that the user updated.
*/
onUpdate: function (field) {
let selectedItem = NetMonitorView.RequestsMenu.selectedItem;
let store = NetMonitorView.RequestsMenu.store;
let value;
switch (field) {
case "method":
value = $("#custom-method-value").value.trim();
store.dispatch(Actions.updateRequest(selectedItem.id, { method: value }));
break;
case "url":
value = $("#custom-url-value").value;
this.updateCustomQuery(value);
store.dispatch(Actions.updateRequest(selectedItem.id, { url: value }));
break;
case "query":
let query = $("#custom-query-value").value;
this.updateCustomUrl(query);
value = $("#custom-url-value").value;
store.dispatch(Actions.updateRequest(selectedItem.id, { url: value }));
break;
case "body":
value = $("#custom-postdata-value").value;
store.dispatch(Actions.updateRequest(selectedItem.id, {
requestPostData: {
postData: { text: value }
}
}));
break;
case "headers":
let headersText = $("#custom-headers-value").value;
value = parseHeadersText(headersText);
store.dispatch(Actions.updateRequest(selectedItem.id, {
requestHeaders: { headers: value }
}));
break;
}
},
/**
* Update the query string field based on the url.
*
* @param object url
* The URL to extract query string from.
*/
updateCustomQuery: function (url) {
const paramsArray = parseQueryString(getUrlQuery(url));
if (!paramsArray) {
$("#custom-query").hidden = true;
return;
}
$("#custom-query").hidden = false;
$("#custom-query-value").value = writeQueryText(paramsArray);
},
/**
* Update the url based on the query string field.
*
* @param object queryText
* The contents of the query string field.
*/
updateCustomUrl: function (queryText) {
let params = parseQueryText(queryText);
let queryString = writeQueryString(params);
let url = $("#custom-url-value").value;
let oldQuery = getUrlQuery(url);
let path = url.replace(oldQuery, queryString);
$("#custom-url-value").value = path;
}
};
/**
* Parse text representation of multiple HTTP headers.
*
* @param string text
* Text of headers
* @return array
* Array of headers info {name, value}
*/
function parseHeadersText(text) {
return parseRequestText(text, "\\S+?", ":");
}
/**
* Parse readable text list of a query string.
*
* @param string text
* Text of query string representation
* @return array
* Array of query params {name, value}
*/
function parseQueryText(text) {
return parseRequestText(text, ".+?", "=");
}
/**
* Parse a text representation of a name[divider]value list with
* the given name regex and divider character.
*
* @param string text
* Text of list
* @return array
* Array of headers info {name, value}
*/
function parseRequestText(text, namereg, divider) {
let regex = new RegExp("(" + namereg + ")\\" + divider + "\\s*(.+)");
let pairs = [];
for (let line of text.split("\n")) {
let matches;
if (matches = regex.exec(line)) { // eslint-disable-line
let [, name, value] = matches;
pairs.push({name: name, value: value});
}
}
return pairs;
}
/**
* Write out a list of query params into a chunk of text
*
* @param array params
* Array of query params {name, value}
* @return string
* List of query params in text format
*/
function writeQueryText(params) {
return params.map(({name, value}) => name + "=" + value).join("\n");
}
/**
* Write out a list of query params into a query string
*
* @param array params
* Array of query params {name, value}
* @return string
* Query string that can be appended to a url.
*/
function writeQueryString(params) {
return params.map(({name, value}) => name + "=" + value).join("&");
}
exports.CustomRequestView = CustomRequestView;

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

@ -59,12 +59,6 @@ const EVENTS = {
RESPONSE_IMAGE_THUMBNAIL_DISPLAYED:
"NetMonitor:ResponseImageThumbnailAvailable",
// Fired when Sidebar has finished being populated.
SIDEBAR_POPULATED: "NetMonitor:SidebarPopulated",
// Fired when CustomRequestView has finished being populated.
CUSTOMREQUESTVIEW_POPULATED: "NetMonitor:CustomRequestViewPopulated",
// Fired when charts have been displayed in the PerformanceStatisticsView.
PLACEHOLDER_CHARTS_DISPLAYED: "NetMonitor:PlaceholderChartsDisplayed",
PRIMED_CACHE_CHART_DISPLAYED: "NetMonitor:PrimedChartsDisplayed",

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

@ -15,7 +15,6 @@ DIRS += [
DevToolsModules(
'constants.js',
'custom-request-view.js',
'events.js',
'filter-predicates.js',
'l10n.js',
@ -26,7 +25,6 @@ DevToolsModules(
'request-list-context-menu.js',
'request-utils.js',
'requests-menu-view.js',
'sidebar-view.js',
'sort-predicates.js',
'store.js',
'waterfall-background.js',

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

@ -2,24 +2,19 @@
* 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/. */
/* globals $, gStore, NetMonitorController, dumpn */
/* eslint-disable mozilla/reject-some-requires */
/* globals $, gStore, NetMonitorController */
"use strict";
const { Task } = require("devtools/shared/task");
const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");
const { RequestsMenuView } = require("./requests-menu-view");
const { CustomRequestView } = require("./custom-request-view");
const { SidebarView } = require("./sidebar-view");
const { ACTIVITY_TYPE } = require("./constants");
const { Prefs } = require("./prefs");
const { createFactory } = require("devtools/client/shared/vendor/react");
const Actions = require("./actions/index");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
// Components
const DetailsPanel = createFactory(require("./shared/components/details-panel"));
const NetworkDetailsPanel = createFactory(require("./shared/components/network-details-panel"));
const StatisticsPanel = createFactory(require("./components/statistics-panel"));
const Toolbar = createFactory(require("./components/toolbar"));
@ -31,16 +26,16 @@ var NetMonitorView = {
* Initializes the network monitor view.
*/
initialize: function () {
this._initializePanes();
this._body = $("#body");
this.detailsPanel = $("#react-details-panel-hook");
this.networkDetailsPanel = $("#react-network-details-panel-hook");
ReactDOM.render(Provider(
{ store: gStore },
DetailsPanel({ toolbox: NetMonitorController._toolbox }),
), this.detailsPanel);
NetworkDetailsPanel({ toolbox: NetMonitorController._toolbox }),
), this.networkDetailsPanel);
this.statisticsPanel = $("#statistics-panel");
this.statisticsPanel = $("#react-statistics-panel-hook");
ReactDOM.render(Provider(
{ store: gStore },
@ -55,7 +50,6 @@ var NetMonitorView = {
), this.toolbar);
this.RequestsMenu.initialize(gStore);
this.CustomRequest.initialize();
// Store watcher here is for observing the statisticsOpen state change.
// It should be removed once we migrate to react and apply react/redex binding.
@ -70,111 +64,21 @@ var NetMonitorView = {
* Destroys the network monitor view.
*/
destroy: function () {
this._isDestroyed = true;
this.RequestsMenu.destroy();
this.CustomRequest.destroy();
ReactDOM.unmountComponentAtNode(this.detailsPanel);
ReactDOM.unmountComponentAtNode(this.networkDetailsPanel);
ReactDOM.unmountComponentAtNode(this.statisticsPanel);
ReactDOM.unmountComponentAtNode(this.toolbar);
this.unsubscribeStore();
this._destroyPanes();
},
/**
* Initializes the UI for all the displayed panes.
*/
_initializePanes: function () {
dumpn("Initializing the NetMonitorView panes");
this._body = $("#body");
this._detailsPane = $("#details-pane");
this._detailsPane.setAttribute("width", Prefs.networkDetailsWidth);
this._detailsPane.setAttribute("height", Prefs.networkDetailsHeight);
this.toggleDetailsPane({ visible: false });
},
/**
* Destroys the UI for all the displayed panes.
*/
_destroyPanes: Task.async(function* () {
dumpn("Destroying the NetMonitorView panes");
Prefs.networkDetailsWidth = this._detailsPane.getAttribute("width");
Prefs.networkDetailsHeight = this._detailsPane.getAttribute("height");
this._detailsPane = null;
}),
/**
* Gets the visibility state of the network details pane.
* @return boolean
*/
get detailsPaneHidden() {
return this._detailsPane.classList.contains("pane-collapsed");
},
/**
* Sets the network details pane hidden or visible.
*
* @param object flags
* An object containing some of the following properties:
* - visible: true if the pane should be shown, false to hide
* - animated: true to display an animation on toggle
* - delayed: true to wait a few cycles before toggle
* - callback: a function to invoke when the toggle finishes
* @param number tabIndex [optional]
* The index of the intended selected tab in the details pane.
*/
toggleDetailsPane: function (flags, tabIndex) {
ViewHelpers.togglePane(flags, this._detailsPane);
if (flags.visible) {
this._body.classList.remove("pane-collapsed");
gStore.dispatch(Actions.openSidebar(true));
} else {
this._body.classList.add("pane-collapsed");
gStore.dispatch(Actions.openSidebar(false));
}
if (tabIndex !== undefined) {
$("#event-details-pane").selectedIndex = tabIndex;
}
},
get currentFrontendMode() {
// The getter may be called from a timeout after the panel is destroyed.
if (!this._body.selectedPanel) {
return null;
}
return this._body.selectedPanel.id;
},
toggleFrontendMode: function () {
if (gStore.getState().ui.statisticsOpen) {
this.showNetworkStatisticsView();
this._body.selectedPanel = $("#react-statistics-panel-hook");
NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
} else {
this.showNetworkInspectorView();
this._body.selectedPanel = $("#inspector-panel");
}
},
showNetworkInspectorView: function () {
this._body.selectedPanel = $("#inspector-panel");
},
showNetworkStatisticsView: function () {
this._body.selectedPanel = $("#statistics-panel");
NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
},
reloadPage: function () {
NetMonitorController.triggerActivity(
ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT);
},
_body: null,
_detailsPane: null,
};
// A smart store watcher to notify store changes as necessary
@ -193,8 +97,6 @@ function storeWatcher(initialValue, reduceValue, onChange) {
/**
* Preliminary setup for the NetMonitorView object.
*/
NetMonitorView.Sidebar = new SidebarView();
NetMonitorView.RequestsMenu = new RequestsMenuView();
NetMonitorView.CustomRequest = new CustomRequestView();
exports.NetMonitorView = NetMonitorView;

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

@ -33,76 +33,18 @@
<splitter id="network-inspector-view-splitter"
class="devtools-side-splitter"/>
<deck id="details-pane"
hidden="true">
<vbox id="custom-pane"
class="tabpanel-content">
<hbox align="baseline">
<label data-localization="content=netmonitor.custom.newRequest"
class="plain tabpanel-summary-label
custom-header"/>
<hbox flex="1" pack="end"
class="devtools-toolbarbutton-group">
<button id="custom-request-send-button"
class="devtools-toolbarbutton"
data-localization="label=netmonitor.custom.send"/>
<button id="custom-request-close-button"
class="devtools-toolbarbutton"
data-localization="label=netmonitor.custom.cancel"/>
</hbox>
</hbox>
<hbox id="custom-method-and-url"
class="tabpanel-summary-container"
align="center">
<textbox id="custom-method-value"
data-key="method"/>
<textbox id="custom-url-value"
flex="1"
data-key="url"/>
</hbox>
<vbox id="custom-query"
class="tabpanel-summary-container custom-section">
<label class="plain tabpanel-summary-label"
data-localization="content=netmonitor.custom.query"/>
<textbox id="custom-query-value"
class="tabpanel-summary-input"
multiline="true"
rows="4"
wrap="off"
data-key="query"/>
</vbox>
<vbox id="custom-headers"
class="tabpanel-summary-container custom-section">
<label class="plain tabpanel-summary-label"
data-localization="content=netmonitor.custom.headers"/>
<textbox id="custom-headers-value"
class="tabpanel-summary-input"
multiline="true"
rows="8"
wrap="off"
data-key="headers"/>
</vbox>
<vbox id="custom-postdata"
class="tabpanel-summary-container custom-section">
<label class="plain tabpanel-summary-label"
data-localization="content=netmonitor.custom.postData"/>
<textbox id="custom-postdata-value"
class="tabpanel-summary-input"
multiline="true"
rows="6"
wrap="off"
data-key="body"/>
</vbox>
</vbox>
<box id="splitter-adjustable-box"
hidden="true">
<html:div xmlns="http://www.w3.org/1999/xhtml"
id="react-details-panel-hook"/>
</deck>
id="react-network-details-panel-hook">
</html:div>
</box>
</hbox>
</vbox>
<html:div xmlns="http://www.w3.org/1999/xhtml"
id="statistics-panel">
id="react-statistics-panel-hook">
</html:div>
</deck>

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

@ -8,13 +8,13 @@ const I = require("devtools/client/shared/vendor/immutable");
const { getUrlDetails } = require("../request-utils");
const {
ADD_REQUEST,
UPDATE_REQUEST,
CLEAR_REQUESTS,
SELECT_REQUEST,
PRESELECT_REQUEST,
CLONE_SELECTED_REQUEST,
OPEN_NETWORK_DETAILS,
REMOVE_SELECTED_CUSTOM_REQUEST,
OPEN_SIDEBAR,
SELECT_REQUEST,
SEND_CUSTOM_REQUEST,
UPDATE_REQUEST,
} = require("../constants");
const Request = I.Record({
@ -43,6 +43,9 @@ const Request = I.Record({
totalTime: undefined,
eventTimings: undefined,
headersSize: undefined,
// Text value is used for storing custom request query
// which only appears when user edit the custom requst form
customQueryValue: undefined,
requestHeaders: undefined,
requestHeadersFromUploadStream: undefined,
requestCookies: undefined,
@ -81,6 +84,7 @@ const UPDATE_PROPS = [
"totalTime",
"eventTimings",
"headersSize",
"customQueryValue",
"requestHeaders",
"requestHeadersFromUploadStream",
"requestCookies",
@ -92,6 +96,29 @@ const UPDATE_PROPS = [
"formDataSections",
];
/**
* Remove the currently selected custom request.
*/
function closeCustomRequest(state) {
let { requests, selectedId } = state;
if (!selectedId) {
return state;
}
let removedRequest = requests.get(selectedId);
// Only custom requests can be removed
if (!removedRequest || !removedRequest.isCustom) {
return state;
}
return state.withMutations(st => {
st.requests = st.requests.delete(selectedId);
st.selectedId = null;
});
}
function requestsReducer(state = new Requests(), action) {
switch (action.type) {
case ADD_REQUEST: {
@ -119,7 +146,6 @@ function requestsReducer(state = new Requests(), action) {
}
});
}
case UPDATE_REQUEST: {
let { requests, lastEndedMillis } = state;
@ -166,9 +192,6 @@ function requestsReducer(state = new Requests(), action) {
case SELECT_REQUEST: {
return state.set("selectedId", action.id);
}
case PRESELECT_REQUEST: {
return state.set("preselectedId", action.id);
}
case CLONE_SELECTED_REQUEST: {
let { requests, selectedId } = state;
@ -197,24 +220,15 @@ function requestsReducer(state = new Requests(), action) {
});
}
case REMOVE_SELECTED_CUSTOM_REQUEST: {
let { requests, selectedId } = state;
if (!selectedId) {
return state;
}
// Only custom requests can be removed
let removedRequest = requests.get(selectedId);
if (!removedRequest || !removedRequest.isCustom) {
return state;
}
return state.withMutations(st => {
st.requests = requests.delete(selectedId);
st.selectedId = null;
});
return closeCustomRequest(state);
}
case OPEN_SIDEBAR: {
case SEND_CUSTOM_REQUEST: {
// When a new request with a given id is added in future, select it immediately.
// where we know in advance the ID of the request, at a time when it
// wasn't sent yet.
return closeCustomRequest(state.set("preselectedId", action.id));
}
case OPEN_NETWORK_DETAILS: {
if (!action.open) {
return state.set("selectedId", null);
}

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

@ -6,7 +6,7 @@
const I = require("devtools/client/shared/vendor/immutable");
const {
OPEN_SIDEBAR,
OPEN_NETWORK_DETAILS,
OPEN_STATISTICS,
SELECT_DETAILS_PANEL_TAB,
WATERFALL_RESIZE,
@ -14,7 +14,7 @@ const {
const UI = I.Record({
detailsPanelSelectedTab: "headers",
sidebarOpen: false,
networkDetailsOpen: false,
statisticsOpen: false,
waterfallWidth: null,
});
@ -26,8 +26,8 @@ function resizeWaterfall(state, action) {
return state.set("waterfallWidth", action.width - REQUESTS_WATERFALL_SAFE_BOUNDS);
}
function openSidebar(state, action) {
return state.set("sidebarOpen", action.open);
function openNetworkDetails(state, action) {
return state.set("networkDetailsOpen", action.open);
}
function openStatistics(state, action) {
@ -40,8 +40,8 @@ function setDetailsPanelTab(state, action) {
function ui(state = new UI(), action) {
switch (action.type) {
case OPEN_SIDEBAR:
return openSidebar(state, action);
case OPEN_NETWORK_DETAILS:
return openNetworkDetails(state, action);
case OPEN_STATISTICS:
return openStatistics(state, action);
case SELECT_DETAILS_PANEL_TAB:

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

@ -6,35 +6,8 @@
"use strict";
const { KeyCodes } = require("devtools/client/shared/keycodes");
const { Task } = require("devtools/shared/task");
/**
* Helper method to get a wrapped function which can be bound to as
* an event listener directly and is executed only when data-key is
* present in event.target.
*
* @param {function} callback - function to execute execute when data-key
* is present in event.target.
* @param {bool} onlySpaceOrReturn - flag to indicate if callback should only
* be called when the space or return button
* is pressed
* @return {function} wrapped function with the target data-key as the first argument
* and the event as the second argument.
*/
function getKeyWithEvent(callback, onlySpaceOrReturn) {
return function (event) {
let key = event.target.getAttribute("data-key");
let filterKeyboardEvent = !onlySpaceOrReturn ||
event.keyCode === KeyCodes.DOM_VK_SPACE ||
event.keyCode === KeyCodes.DOM_VK_RETURN;
if (key && filterKeyboardEvent) {
callback(key);
}
};
}
/**
* Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
* POST request.
@ -254,7 +227,6 @@ function parseQueryString(query) {
}
module.exports = {
getKeyWithEvent,
getFormDataSections,
fetchHeaders,
formDataURI,

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

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* globals window, dumpn, $, gNetwork, NetMonitorController, NetMonitorView */
/* globals window, dumpn, $, gNetwork, NetMonitorController */
"use strict";
@ -81,10 +81,10 @@ RequestsMenuView.prototype = {
(newSelected, oldSelected) => this.onSelectionUpdate(newSelected, oldSelected)
));
// Watch the sidebar status and resize the waterfall column on change
// Watch the network details panel toggle and resize the waterfall column on change
this.store.subscribe(storeWatcher(
false,
() => this.store.getState().ui.sidebarOpen,
() => this.store.getState().ui.networkDetailsOpen,
() => this.onResize()
));
@ -139,9 +139,6 @@ RequestsMenuView.prototype = {
},
));
this.sendCustomRequestEvent = this.sendCustomRequest.bind(this);
this.closeCustomRequestEvent = this.closeCustomRequest.bind(this);
this._summary = $("#requests-menu-network-summary-button");
this._summary.setAttribute("label", L10N.getStr("networkMenu.empty"));
@ -157,17 +154,6 @@ RequestsMenuView.prototype = {
{ store: this.store },
RequestList()
), this.mountPoint);
window.once("connected", this._onConnect.bind(this));
},
_onConnect() {
if (NetMonitorController.supportsCustomRequest) {
$("#custom-request-send-button")
.addEventListener("click", this.sendCustomRequestEvent);
$("#custom-request-close-button")
.addEventListener("click", this.closeCustomRequestEvent);
}
},
/**
@ -178,13 +164,6 @@ RequestsMenuView.prototype = {
Prefs.filters = getActiveFilters(this.store.getState());
// this.flushRequestsTask.disarm();
$("#custom-request-send-button")
.removeEventListener("click", this.sendCustomRequestEvent);
$("#custom-request-close-button")
.removeEventListener("click", this.closeCustomRequestEvent);
this._splitter.removeEventListener("mouseup", this.onResize);
window.removeEventListener("resize", this.onResize);
@ -202,7 +181,7 @@ RequestsMenuView.prototype = {
},
/**
* Removes all network requests and closes the sidebar if open.
* Removes all network requests and closes the network details panel if open.
*/
clear() {
this.store.dispatch(Actions.clearRequests());
@ -401,16 +380,15 @@ RequestsMenuView.prototype = {
},
/**
* Updates the sidebar status when something about the selection changes
* Updates the network details panel state when something about the selection changes
*/
onSelectionUpdate(newSelected, oldSelected) {
if (newSelected) {
// Another item just got selected
NetMonitorView.Sidebar.populate(newSelected);
NetMonitorView.Sidebar.toggle(true);
this.store.dispatch(Actions.openNetworkDetails(true));
} else {
// Selection just got empty
NetMonitorView.Sidebar.toggle(false);
this.store.dispatch(Actions.openNetworkDetails(false));
}
},
@ -426,48 +404,7 @@ RequestsMenuView.prototype = {
this.store.dispatch(Actions.resizeWaterfall(width));
}
});
},
/**
* Create a new custom request form populated with the data from
* the currently selected request.
*/
cloneSelectedRequest() {
this.store.dispatch(Actions.cloneSelectedRequest());
},
/**
* Send a new HTTP request using the data in the custom request form.
*/
sendCustomRequest: function () {
let selected = getSelectedRequest(this.store.getState());
let data = {
url: selected.url,
method: selected.method,
httpVersion: selected.httpVersion,
};
if (selected.requestHeaders) {
data.headers = selected.requestHeaders.headers;
}
if (selected.requestPostData) {
data.body = selected.requestPostData.postData.text;
}
NetMonitorController.webConsoleClient.sendHTTPRequest(data, response => {
let id = response.eventActor.actor;
this.store.dispatch(Actions.preselectRequest(id));
});
this.closeCustomRequest();
},
/**
* Remove the currently selected custom request.
*/
closeCustomRequest() {
this.store.dispatch(Actions.removeSelectedCustomRequest());
},
}
};
exports.RequestsMenuView = RequestsMenuView;

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

@ -94,7 +94,7 @@ const getDisplayedRequestsSummary = createSelector(
const getSelectedRequest = createSelector(
state => state.requests,
({ selectedId, requests }) => selectedId ? requests.get(selectedId) : null
({ selectedId, requests }) => selectedId ? requests.get(selectedId) : undefined
);
function getRequestById(state, id) {

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

@ -6,7 +6,7 @@
const { getDisplayedRequests } = require("./requests");
function isSidebarToggleButtonDisabled(state) {
function isNetworkDetailsToggleButtonDisabled(state) {
return getDisplayedRequests(state).isEmpty();
}
@ -31,6 +31,6 @@ function getWaterfallScale(state) {
}
module.exports = {
isSidebarToggleButtonDisabled,
isNetworkDetailsToggleButtonDisabled,
getWaterfallScale,
};

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

@ -0,0 +1,257 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { L10N } = require("../../l10n");
const Actions = require("../../actions/index");
const { getSelectedRequest } = require("../../selectors/index");
const {
getUrlQuery,
parseQueryString,
writeHeaderText,
} = require("../../request-utils");
const {
button,
div,
input,
textarea,
} = DOM;
const CUSTOM_CANCEL = L10N.getStr("netmonitor.custom.cancel");
const CUSTOM_HEADERS = L10N.getStr("netmonitor.custom.headers");
const CUSTOM_NEW_REQUEST = L10N.getStr("netmonitor.custom.newRequest");
const CUSTOM_POSTDATA = L10N.getStr("netmonitor.custom.postData");
const CUSTOM_QUERY = L10N.getStr("netmonitor.custom.query");
const CUSTOM_SEND = L10N.getStr("netmonitor.custom.send");
function CustomRequestPanel({
removeSelectedCustomRequest,
request = {},
sendCustomRequest,
updateRequest,
}) {
let {
method,
customQueryValue,
requestHeaders,
requestPostData,
url,
} = request;
let headers = "";
if (requestHeaders) {
headers = requestHeaders.customHeadersValue ?
requestHeaders.customHeadersValue : writeHeaderText(requestHeaders.headers);
}
let queryArray = url ? parseQueryString(getUrlQuery(url)) : [];
let params = customQueryValue;
if (!params) {
params = queryArray ?
queryArray.map(({ name, value }) => name + "=" + value).join("\n") : "";
}
let postData = requestPostData && requestPostData.postData.text ?
requestPostData.postData.text : "";
return (
div({ className: "custom-request-panel" },
div({ className: "tabpanel-summary-container custom-request" },
div({ className: "custom-request-label custom-header" },
CUSTOM_NEW_REQUEST
),
button({
className: "devtools-button",
id: "custom-request-send-button",
onClick: sendCustomRequest,
},
CUSTOM_SEND
),
button({
className: "devtools-button",
id: "custom-request-close-button",
onClick: removeSelectedCustomRequest,
},
CUSTOM_CANCEL
),
),
div({
className: "tabpanel-summary-container custom-method-and-url",
id: "custom-method-and-url",
},
input({
className: "custom-method-value",
id: "custom-method-value",
onChange: (evt) => updateCustomRequestFields(evt, request, updateRequest),
value: method || "GET",
}),
input({
className: "custom-url-value",
id: "custom-url-value",
onChange: (evt) => updateCustomRequestFields(evt, request, updateRequest),
value: url || "http://",
}),
),
// Hide query field when there is no params
params ? div({
className: "tabpanel-summary-container custom-section",
id: "custom-query",
},
div({ className: "custom-request-label" }, CUSTOM_QUERY),
textarea({
className: "tabpanel-summary-input",
id: "custom-query-value",
onChange: (evt) => updateCustomRequestFields(evt, request, updateRequest),
rows: 4,
value: params,
wrap: "off",
})
) : null,
div({
id: "custom-headers",
className: "tabpanel-summary-container custom-section",
},
div({ className: "custom-request-label" }, CUSTOM_HEADERS),
textarea({
className: "tabpanel-summary-input",
id: "custom-headers-value",
onChange: (evt) => updateCustomRequestFields(evt, request, updateRequest),
rows: 8,
value: headers,
wrap: "off",
})
),
div({
id: "custom-postdata",
className: "tabpanel-summary-container custom-section",
},
div({ className: "custom-request-label" }, CUSTOM_POSTDATA),
textarea({
className: "tabpanel-summary-input",
id: "custom-postdata-value",
onChange: (evt) => updateCustomRequestFields(evt, request, updateRequest),
rows: 6,
value: postData,
wrap: "off",
})
),
)
);
}
CustomRequestPanel.displayName = "CustomRequestPanel";
CustomRequestPanel.propTypes = {
removeSelectedCustomRequest: PropTypes.func.isRequired,
request: PropTypes.object,
sendCustomRequest: PropTypes.func.isRequired,
updateRequest: PropTypes.func.isRequired,
};
/**
* Parse a text representation of a name[divider]value list with
* the given name regex and divider character.
*
* @param {string} text - Text of list
* @return {array} array of headers info {name, value}
*/
function parseRequestText(text, namereg, divider) {
let regex = new RegExp(`(${namereg})\\${divider}\\s*(.+)`);
let pairs = [];
for (let line of text.split("\n")) {
let matches = regex.exec(line);
if (matches) {
let [, name, value] = matches;
pairs.push({ name, value });
}
}
return pairs;
}
/**
* Update Custom Request Fields
*
* @param {Object} evt click event
* @param {Object} request current request
* @param {updateRequest} updateRequest action
*/
function updateCustomRequestFields(evt, request, updateRequest) {
const val = evt.target.value;
let data;
switch (evt.target.id) {
case "custom-headers-value":
let customHeadersValue = val || "";
// Parse text representation of multiple HTTP headers
let headersArray = parseRequestText(customHeadersValue, "\\S+?", ":");
// Remove temp customHeadersValue while query string is parsable
if (customHeadersValue === "" ||
headersArray.length === customHeadersValue.split("\n").length) {
customHeadersValue = null;
}
data = {
requestHeaders: {
customHeadersValue,
headers: headersArray,
},
};
break;
case "custom-method-value":
data = { method: val.trim() };
break;
case "custom-postdata-value":
data = {
requestPostData: {
postData: { text: val },
}
};
break;
case "custom-query-value":
let customQueryValue = val || "";
// Parse readable text list of a query string
let queryArray = customQueryValue ?
parseRequestText(customQueryValue, ".+?", "=") : [];
// Write out a list of query params into a query string
let queryString = queryArray.map(
({ name, value }) => name + "=" + value).join("&");
let url = queryString ? [request.url.split("?")[0], queryString].join("?") :
request.url.split("?")[0];
// Remove temp customQueryValue while query string is parsable
if (customQueryValue === "" ||
queryArray.length === customQueryValue.split("\n").length) {
customQueryValue = null;
}
data = {
customQueryValue,
url,
};
break;
case "custom-url-value":
data = {
customQueryValue: null,
url: val
};
break;
default:
break;
}
if (data) {
// All updateRequest batch mode should be disabled to make UI editing in sync
updateRequest(request.id, data, false);
}
}
module.exports = connect(
(state) => ({ request: getSelectedRequest(state) }),
(dispatch) => ({
removeSelectedCustomRequest: () => dispatch(Actions.removeSelectedCustomRequest()),
sendCustomRequest: () => dispatch(Actions.sendCustomRequest()),
updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)),
})
)(CustomRequestPanel);

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

@ -4,14 +4,16 @@
DevToolsModules(
'cookies-panel.js',
'details-panel.js',
'custom-request-panel.js',
'editor.js',
'headers-mdn.js',
'headers-panel.js',
'network-details-panel.js',
'params-panel.js',
'preview-panel.js',
'properties-view.js',
'response-panel.js',
'security-panel.js',
'tabbox-panel.js',
'timings-panel.js',
)

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

@ -0,0 +1,102 @@
/* 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/. */
/* globals document */
"use strict";
const {
createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const Actions = require("../../actions/index");
const { getSelectedRequest } = require("../../selectors/index");
const { Prefs } = require("../../prefs");
// Components
const CustomRequestPanel = createFactory(require("./custom-request-panel"));
const TabboxPanel = createFactory(require("./tabbox-panel"));
const { div } = DOM;
/*
* Network details panel component
*/
const NetworkDetailsPanel = createClass({
displayName: "NetworkDetailsPanel",
propTypes: {
activeTabId: PropTypes.string,
cloneSelectedRequest: PropTypes.func.isRequired,
open: PropTypes.bool,
request: PropTypes.object,
selectTab: PropTypes.func.isRequired,
toolbox: PropTypes.object.isRequired,
},
componentDidMount() {
// FIXME: Workaround should be removed in bug 1309183
document.getElementById("splitter-adjustable-box")
.setAttribute("width", Prefs.networkDetailsWidth);
document.getElementById("splitter-adjustable-box")
.setAttribute("height", Prefs.networkDetailsHeight);
},
componentWillUnmount() {
// FIXME: Workaround should be removed in bug 1309183
Prefs.networkDetailsWidth =
document.getElementById("splitter-adjustable-box").getAttribute("width");
Prefs.networkDetailsHeight =
document.getElementById("splitter-adjustable-box").getAttribute("height");
},
render() {
let {
activeTabId,
cloneSelectedRequest,
open,
request,
selectTab,
toolbox,
} = this.props;
if (!open || !request) {
// FIXME: Workaround should be removed in bug 1309183
document.getElementById("splitter-adjustable-box").setAttribute("hidden", true);
return null;
}
// FIXME: Workaround should be removed in bug 1309183
document.getElementById("splitter-adjustable-box").removeAttribute("hidden");
return (
div({ className: "network-details-panel" },
!request.isCustom ?
TabboxPanel({
activeTabId,
request,
selectTab,
toolbox,
}) :
CustomRequestPanel({
cloneSelectedRequest,
request,
})
)
);
}
});
module.exports = connect(
(state) => ({
activeTabId: state.ui.detailsPanelSelectedTab,
open: state.ui.networkDetailsOpen,
request: getSelectedRequest(state),
}),
(dispatch) => ({
cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
selectTab: (tabId) => dispatch(Actions.selectDetailsPanelTab(tabId)),
}),
)(NetworkDetailsPanel);

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

@ -34,10 +34,10 @@ const SECURITY_TITLE = L10N.getStr("netmonitor.tab.security");
const PREVIEW_TITLE = L10N.getStr("netmonitor.tab.preview");
/*
* Details panel component
* Tabbox panel component
* Display the network request details
*/
function DetailsPanel({
function TabboxPanel({
activeTabId,
cloneSelectedRequest,
request,
@ -104,13 +104,13 @@ function DetailsPanel({
);
}
DetailsPanel.displayName = "DetailsPanel";
TabboxPanel.displayName = "TabboxPanel";
DetailsPanel.propTypes = {
TabboxPanel.propTypes = {
activeTabId: PropTypes.string,
cloneSelectedRequest: PropTypes.func.isRequired,
request: PropTypes.object,
setTabIndex: PropTypes.func.isRequired,
selectedTab: PropTypes.number.isRequired,
selectTab: PropTypes.func.isRequired,
toolbox: PropTypes.object.isRequired,
};
@ -123,4 +123,4 @@ module.exports = connect(
cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
selectTab: (tabId) => dispatch(Actions.selectDetailsPanelTab(tabId)),
}),
)(DetailsPanel);
)(TabboxPanel);

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

@ -1,52 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* globals window, dumpn, $, NetMonitorView */
"use strict";
const { Task } = require("devtools/shared/task");
const { EVENTS } = require("./events");
/**
* Functions handling the sidebar details view.
*/
function SidebarView() {
dumpn("SidebarView was instantiated");
}
SidebarView.prototype = {
/**
* Sets this view hidden or visible. It's visible by default.
*
* @param boolean visibleFlag
* Specifies the intended visibility.
*/
toggle: function (visibleFlag) {
NetMonitorView.toggleDetailsPane({ visible: visibleFlag });
},
/**
* Populates this view with the specified data.
*
* @param object data
* The data source (this should be the attachment of a request item).
* @return object
* Returns a promise that resolves upon population of the subview.
*/
populate: Task.async(function* (data) {
let isCustom = data.isCustom;
if (isCustom) {
yield NetMonitorView.CustomRequest.populate(data);
}
$("#details-pane").selectedIndex = isCustom ? 0 : 1;
window.emit(EVENTS.SIDEBAR_POPULATED);
})
};
exports.SidebarView = SidebarView;

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

@ -128,12 +128,14 @@ skip-if = (os == 'linux' && debug && bits == 32) # Bug 1303439
[browser_net_post-data-04.js]
[browser_net_prefs-and-l10n.js]
[browser_net_prefs-reload.js]
skip-if = true # bug 1309183, it should be fixed by SplitBox support
[browser_net_raw_headers.js]
[browser_net_reload-button.js]
[browser_net_reload-markers.js]
[browser_net_req-resp-bodies.js]
[browser_net_resend_cors.js]
[browser_net_resend_headers.js]
[browser_net_resend.js]
[browser_net_security-details.js]
[browser_net_security-error.js]
[browser_net_security-icon-click.js]

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

@ -14,7 +14,7 @@ add_task(function* () {
// It seems that this test may be slow on Ubuntu builds running on ec2.
requestLongerTimeout(2);
let { NetMonitorView, gStore, windowRequire } = monitor.panelWin;
let { document, NetMonitorView, gStore, windowRequire } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
@ -22,13 +22,13 @@ add_task(function* () {
let Actions = windowRequire("devtools/client/netmonitor/actions/index");
let count = 0;
function check(selectedIndex, paneVisibility) {
function check(selectedIndex, panelVisibility) {
info("Performing check " + (count++) + ".");
is(RequestsMenu.selectedIndex, selectedIndex,
"The selected item in the requests menu was incorrect.");
is(NetMonitorView.detailsPaneHidden, !paneVisibility,
"The network requests details pane visibility state was incorrect.");
is(!!document.querySelector(".network-details-panel"), panelVisibility,
"The network details panel should render correctly.");
}
let wait = waitForNetworkEvents(monitor, 2);

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

@ -14,19 +14,19 @@ add_task(function* () {
// It seems that this test may be slow on Ubuntu builds running on ec2.
requestLongerTimeout(2);
let { window, $, NetMonitorView } = monitor.panelWin;
let { window, document, NetMonitorView } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
let count = 0;
function check(selectedIndex, paneVisibility) {
function check(selectedIndex, panelVisibility) {
info("Performing check " + (count++) + ".");
is(RequestsMenu.selectedIndex, selectedIndex,
"The selected item in the requests menu was incorrect.");
is(NetMonitorView.detailsPaneHidden, !paneVisibility,
"The network requests details pane visibility state was incorrect.");
is(!!document.querySelector(".network-details-panel"), panelVisibility,
"The network details panel should render correctly.");
}
let wait = waitForNetworkEvents(monitor, 2);
@ -35,7 +35,7 @@ add_task(function* () {
});
yield wait;
$(".requests-menu-contents").focus();
document.querySelector(".requests-menu-contents").focus();
check(-1, false);
@ -122,7 +122,8 @@ add_task(function* () {
EventUtils.sendKey("DOWN", window);
check(19, true);
EventUtils.sendMouseEvent({ type: "mousedown" }, $(".request-list-item"));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelector(".request-list-item"));
check(0, true);
yield teardown(monitor);

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

@ -40,7 +40,7 @@ add_task(function* () {
wait = waitForDOM(document, "#response-panel .editor-mount iframe");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.getElementById("details-pane-toggle"));
document.querySelector(".network-details-panel-toggle"));
document.querySelector("#response-tab").click();
let [editorFrame] = yield wait;

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

@ -11,16 +11,15 @@ add_task(function* () {
let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
info("Starting test... ");
let { $, NetMonitorView } = monitor.panelWin;
let { document, NetMonitorView } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
let detailsPane = $("#details-pane");
let detailsPaneToggleButton = $("#details-pane-toggle");
let clearButton = $("#requests-menu-clear-button");
let detailsPanelToggleButton = document.querySelector(".network-details-panel-toggle");
let clearButton = document.querySelector("#requests-menu-clear-button");
RequestsMenu.lazyUpdate = false;
// Make sure we start in a sane state
assertNoRequestState(RequestsMenu, detailsPaneToggleButton);
assertNoRequestState(RequestsMenu, detailsPanelToggleButton);
// Load one request and assert it shows up in the list
let networkEvent = monitor.panelWin.once(monitor.panelWin.EVENTS.NETWORK_EVENT);
@ -40,17 +39,18 @@ add_task(function* () {
assertSingleRequestState();
// Make sure we can now open the details pane
NetMonitorView.toggleDetailsPane({ visible: true, animated: false });
ok(!detailsPane.classList.contains("pane-collapsed") &&
!detailsPaneToggleButton.classList.contains("pane-collapsed"),
// Make sure we can now open the network details panel
EventUtils.sendMouseEvent({ type: "mousedown" }, detailsPanelToggleButton);
ok(document.querySelector(".network-details-panel") &&
!detailsPanelToggleButton.classList.contains("pane-collapsed"),
"The details pane should be visible after clicking the toggle button.");
// Click clear and make sure the details pane closes
EventUtils.sendMouseEvent({ type: "click" }, clearButton);
assertNoRequestState();
ok(detailsPane.classList.contains("pane-collapsed") &&
detailsPaneToggleButton.classList.contains("pane-collapsed"),
ok(!document.querySelector(".network-details-panel") &&
detailsPanelToggleButton.classList.contains("pane-collapsed"),
"The details pane should not be visible clicking 'clear'.");
return teardown(monitor);
@ -61,7 +61,7 @@ add_task(function* () {
function assertSingleRequestState() {
is(RequestsMenu.itemCount, 1,
"The request menu should have one item at this point.");
is(detailsPaneToggleButton.hasAttribute("disabled"), false,
is(detailsPanelToggleButton.hasAttribute("disabled"), false,
"The pane toggle button should be enabled after a request is made.");
}
@ -71,7 +71,7 @@ add_task(function* () {
function assertNoRequestState() {
is(RequestsMenu.itemCount, 0,
"The request menu should be empty at this point.");
is(detailsPaneToggleButton.hasAttribute("disabled"), true,
is(detailsPanelToggleButton.hasAttribute("disabled"), true,
"The pane toggle button should be disabled when the request menu is cleared.");
}
});

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

@ -27,7 +27,7 @@ add_task(function* () {
wait = waitForDOM(document, "#params-panel .tree-section", 2);
EventUtils.sendMouseEvent({ type: "mousedown" },
document.getElementById("details-pane-toggle"));
document.querySelector(".network-details-panel-toggle"));
document.querySelector("#params-tab").click();
yield wait;
testParamsTab1("a", '""', '{ "foo": "bar" }', '""');
@ -61,9 +61,7 @@ add_task(function* () {
yield waitForDOM(editorFrames[0].contentDocument, ".CodeMirror-code");
testParamsTab2("a", '"b"', "?foo=bar", "text");
wait = waitForDOM(document, "#params-panel .empty-notice");
RequestsMenu.selectedIndex = 6;
yield wait;
testParamsTab3();
yield teardown(monitor);

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

@ -30,7 +30,7 @@ add_task(function* () {
wait = waitForDOM(document, "#response-panel .editor-mount iframe");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.getElementById("details-pane-toggle"));
document.querySelector(".network-details-panel-toggle"));
document.querySelector("#response-tab").click();
let [editor] = yield wait;
yield once(editor, "DOMContentLoaded");

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

@ -29,7 +29,7 @@ add_task(function* () {
wait = waitForDOM(document, "#response-panel .editor-mount iframe");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.getElementById("details-pane-toggle"));
document.querySelector(".network-details-panel-toggle"));
document.querySelector("#response-tab").click();
let [editor] = yield wait;
yield once(editor, "DOMContentLoaded");

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

@ -140,7 +140,7 @@ add_task(function* () {
info("Starting test... ");
let { $, NetMonitorView } = monitor.panelWin;
let { document, NetMonitorView } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
@ -150,85 +150,108 @@ add_task(function* () {
yield performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS);
yield wait;
EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle"));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelector(".network-details-panel-toggle"));
isnot(RequestsMenu.selectedItem, null,
"There should be a selected item in the requests menu.");
is(RequestsMenu.selectedIndex, 0,
"The first item should be selected in the requests menu.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should not be hidden after toggle button was pressed.");
is(!!document.querySelector(".network-details-panel"), true,
"The network details panel should render correctly.");
// First test with single filters...
testFilterButtons(monitor, "all");
testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-html-button"));
testFilterButtons(monitor, "html");
testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]);
// Reset filters
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-css-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-css-button"));
testFilterButtons(monitor, "css");
testContents([0, 1, 0, 0, 0, 0, 0, 0, 0]);
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-js-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-js-button"));
testFilterButtons(monitor, "js");
testContents([0, 0, 1, 0, 0, 0, 0, 0, 0]);
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-xhr-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-xhr-button"));
testFilterButtons(monitor, "xhr");
testContents([1, 1, 1, 1, 1, 1, 1, 1, 0]);
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-fonts-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-fonts-button"));
testFilterButtons(monitor, "fonts");
testContents([0, 0, 0, 1, 0, 0, 0, 0, 0]);
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-images-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-images-button"));
testFilterButtons(monitor, "images");
testContents([0, 0, 0, 0, 1, 0, 0, 0, 0]);
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-media-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-media-button"));
testFilterButtons(monitor, "media");
testContents([0, 0, 0, 0, 0, 1, 1, 0, 0]);
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-flash-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-flash-button"));
testFilterButtons(monitor, "flash");
testContents([0, 0, 0, 0, 0, 0, 0, 1, 0]);
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-ws-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-ws-button"));
testFilterButtons(monitor, "ws");
testContents([0, 0, 0, 0, 0, 0, 0, 0, 1]);
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
testFilterButtons(monitor, "all");
testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
// Text in filter box that matches nothing should hide all.
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
setFreetextFilter("foobar");
testContents([0, 0, 0, 0, 0, 0, 0, 0, 0]);
// Text in filter box that matches should filter out everything else.
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
setFreetextFilter("sample");
testContents([1, 1, 1, 0, 0, 0, 0, 0, 0]);
// Text in filter box that matches should filter out everything else.
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
setFreetextFilter("SAMPLE");
testContents([1, 1, 1, 0, 0, 0, 0, 0, 0]);
// Test negative filtering (only show unmatched items)
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
setFreetextFilter("-sample");
testContents([0, 0, 0, 1, 1, 1, 1, 1, 1]);
@ -236,8 +259,10 @@ add_task(function* () {
// Enable filtering for html and css; should show request of both type.
setFreetextFilter("");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-css-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-html-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-css-button"));
testFilterButtonsCustom(monitor, [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
testContents([1, 1, 0, 0, 0, 0, 0, 0, 0]);
@ -246,28 +271,36 @@ add_task(function* () {
setFreetextFilter("sample");
testContents([1, 1, 0, 0, 0, 0, 0, 0, 0]);
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-flash-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-flash-button"));
setFreetextFilter("");
testFilterButtonsCustom(monitor, [0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0]);
testContents([1, 1, 0, 0, 0, 0, 0, 1, 0]);
// Disable some filters. Only one left active.
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-css-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-flash-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-css-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-flash-button"));
testFilterButtons(monitor, "html");
testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]);
// Disable last active filter. Should toggle to all.
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-html-button"));
testFilterButtons(monitor, "all");
testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
// Enable few filters and click on all. Only "all" should be checked.
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-css-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-ws-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-html-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-css-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-ws-button"));
testFilterButtonsCustom(monitor, [0, 1, 1, 0, 0, 0, 0, 0, 0, 1]);
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
testFilterButtons(monitor, "all");
testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
@ -278,8 +311,8 @@ add_task(function* () {
"There should still be a selected item after filtering.");
is(RequestsMenu.selectedIndex, 0,
"The first item should be still selected after filtering.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should still be visible after filtering.");
is(!!document.querySelector(".network-details-panel"), true,
"The network details panel should render correctly.");
const items = RequestsMenu.items;
const visibleItems = RequestsMenu.visibleItems;

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

@ -136,7 +136,7 @@ add_task(function* () {
// It seems that this test may be slow on Ubuntu builds running on ec2.
requestLongerTimeout(2);
let { $, NetMonitorView } = monitor.panelWin;
let { document, NetMonitorView } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
@ -146,20 +146,22 @@ add_task(function* () {
yield performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS);
yield wait;
EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle"));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelector(".network-details-panel-toggle"));
isnot(RequestsMenu.selectedItem, null,
"There should be a selected item in the requests menu.");
is(RequestsMenu.selectedIndex, 0,
"The first item should be selected in the requests menu.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should not be hidden after toggle button was pressed.");
is(!!document.querySelector(".network-details-panel"), true,
"The network details panel should be visible after toggle button was pressed.");
testFilterButtons(monitor, "all");
testContents([1, 1, 1, 1, 1, 1, 1, 1, 1]);
info("Testing html filtering.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-html-button"));
testFilterButtons(monitor, "html");
testContents([1, 0, 0, 0, 0, 0, 0, 0, 0]);
@ -183,7 +185,8 @@ add_task(function* () {
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]);
info("Resetting filters.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-all-button"));
testFilterButtons(monitor, "all");
testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
@ -195,8 +198,8 @@ add_task(function* () {
"There should still be a selected item after filtering.");
is(RequestsMenu.selectedIndex, 0,
"The first item should be still selected after filtering.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should still be visible after filtering.");
is(!!document.querySelector(".network-details-panel"), true,
"The network details panel should still be visible after filtering.");
const items = RequestsMenu.items;
const visibleItems = RequestsMenu.visibleItems;

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

@ -27,7 +27,7 @@ add_task(function* () {
// It seems that this test may be slow on Ubuntu builds running on ec2.
requestLongerTimeout(2);
let { $, NetMonitorView } = monitor.panelWin;
let { document, NetMonitorView } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
@ -44,25 +44,28 @@ add_task(function* () {
yield performRequestsInContent(requests);
yield wait;
EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle"));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelector(".network-details-panel-toggle"));
isnot(RequestsMenu.selectedItem, null,
"There should be a selected item in the requests menu.");
is(RequestsMenu.selectedIndex, 0,
"The first item should be selected in the requests menu.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should not be hidden after toggle button was pressed.");
is(!!document.querySelector(".network-details-panel"), true,
"The network details panel should be visible after toggle button was pressed.");
testFilterButtons(monitor, "all");
testContents([0, 1, 2, 3, 4, 5, 6], 7, 0);
info("Sorting by size, ascending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-size-button"));
testFilterButtons(monitor, "all");
testContents([6, 4, 5, 0, 1, 2, 3], 7, 6);
info("Testing html filtering.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-filter-html-button"));
testFilterButtons(monitor, "html");
testContents([6, 4, 5, 0, 1, 2, 3], 1, 6);
@ -89,8 +92,10 @@ add_task(function* () {
yield teardown(monitor);
function resetSorting() {
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-waterfall-button"));
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-waterfall-button"));
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#requests-menu-size-button"));
}
function testContents(order, visible, selection) {
@ -98,8 +103,8 @@ add_task(function* () {
"There should still be a selected item after filtering.");
is(RequestsMenu.selectedIndex, selection,
"The first item should be still selected after filtering.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should still be visible after filtering.");
is(!!document.querySelector(".network-details-panel"), true,
"The network details panel should still be visible after filtering.");
is(RequestsMenu.items.length, order.length,
"There should be a specific amount of items in the requests menu.");

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

@ -23,7 +23,7 @@ add_task(function* () {
yield wait;
EventUtils.sendMouseEvent({ type: "mousedown" },
document.getElementById("details-pane-toggle"));
document.querySelector(".network-details-panel-toggle"));
ok(document.querySelector("#headers-tab[aria-selected=true]"),
"The headers tab in the details panel should be selected.");
@ -32,11 +32,8 @@ add_task(function* () {
ok(!document.querySelector("#preview-panel"),
"The preview panel is hidden for non html responses.");
wait = waitForDOM(document, ".tabs");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[4]);
yield wait;
document.querySelector("#preview-tab").click();
ok(document.querySelector("#preview-tab[aria-selected=true]"),
@ -45,7 +42,9 @@ add_task(function* () {
"The preview panel should be visible now.");
let iframe = document.querySelector("#preview-panel iframe");
console.log(123)
yield once(iframe, "DOMContentLoaded");
console.log(123)
ok(iframe,
"There should be a response preview iframe available.");

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

@ -25,7 +25,7 @@ add_task(function* () {
wait = waitForDOM(document, "#response-panel");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.getElementById("details-pane-toggle"));
document.querySelector(".network-details-panel-toggle"));
document.querySelector("#response-tab").click();
yield wait;

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

@ -41,7 +41,7 @@ add_task(function* () {
wait = waitForDOM(document, "#response-panel");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.getElementById("details-pane-toggle"));
document.querySelector(".network-details-panel-toggle"));
document.querySelector("#response-tab").click();
yield wait;

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше